@calimero-network/mero-js 23.9.0 → 24.1.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.
@@ -181,7 +181,9 @@ export function createAccountAdmin(input, deps = {}) {
181
181
  return { namespaceId, haEnabled, ...(haError !== undefined ? { haError } : {}) };
182
182
  },
183
183
  async createGroupInNamespace(namespaceId, req = {}) {
184
- const { op } = await subgroupCreation({ parentId: namespaceId, restricted: req.visibility !== 'open', admin: s.account });
184
+ // Absent ⇒ Open, as on a node: a subgroup created Restricted and flipped
185
+ // Open strands every namespace member outside it.
186
+ const { op } = await subgroupCreation({ parentId: namespaceId, restricted: req.visibility === 'restricted', admin: s.account });
185
187
  const { groupId } = await root(s, namespaceId, op);
186
188
  rememberGroupNamespace(s.account, groupId, namespaceId);
187
189
  if (req.groupName)
@@ -668,7 +668,12 @@ export interface JoinNamespaceResponseData {
668
668
  }
669
669
  export interface CreateGroupInNamespaceRequest {
670
670
  groupName?: string;
671
- /** Subgroup visibility at birth. Absent means `'restricted'`. */
671
+ /**
672
+ * Subgroup visibility at birth. Absent means `'open'` (core 0.11 and later;
673
+ * older nodes default to `'restricted'`). Pass `'restricted'` for a private
674
+ * subgroup, and choose here rather than flipping it afterwards: a subgroup
675
+ * created Restricted and flipped Open strands namespace members outside it.
676
+ */
672
677
  visibility?: 'open' | 'restricted';
673
678
  }
674
679
  export interface CreateGroupInNamespaceResponseData {
@@ -155,6 +155,54 @@ export interface CloudRelay {
155
155
  canExecute: boolean;
156
156
  lastSeenAt?: string | null;
157
157
  confirmedAt?: string | null;
158
+ /**
159
+ * How far along this relay's admission is, as the cloud judges it:
160
+ * `'confirmed'` (in), `'pending'` (trying), `'refused'` (alive and turned
161
+ * away), `'absent'` (stopped talking before getting in). `null` from a cloud
162
+ * that predates the field. A string, not a closed union, so a verdict the
163
+ * cloud adds later is passed through rather than dropped.
164
+ */
165
+ admission: string | null;
166
+ /** The cloud's one-line explanation of `admission`, for a person; `null` when none. */
167
+ admissionHint: string | null;
168
+ /**
169
+ * What the relay itself reported about its latest attempt to join, reasons
170
+ * included — or `null` when it has reported nothing (an older fleet node or
171
+ * cloud, or a relay assigned since its last report).
172
+ */
173
+ join: CloudRelayJoin | null;
174
+ }
175
+ /**
176
+ * A fleet node's own account of its latest attempt to join a namespace.
177
+ *
178
+ * A refusal happens peer to peer, between the node and the namespace's
179
+ * admitters, so this is the only place a client learns WHY a relay is stuck:
180
+ * the node forwards what each admitter it asked directly answered.
181
+ */
182
+ export interface CloudRelayJoin {
183
+ /**
184
+ * `'admitted'` (in, or an admitter said yes and the key is on its way),
185
+ * `'refused'` (every admitter asked declined; see `refusals`), `'waiting'`
186
+ * (nobody answered directly yet), or `'error'` (the attempt itself failed;
187
+ * see `error`). Passed through as sent.
188
+ */
189
+ state: string;
190
+ /** Consecutive attempts without admission, as the node counts them. */
191
+ attempts: number;
192
+ /**
193
+ * Each directly-asked admitter that declined: its libp2p peer id and its
194
+ * reason, verbatim. Text to show a person, never to parse — the most useful
195
+ * one is "was removed from group … and cannot rejoin; an admin must re-add
196
+ * them", which the namespace owner can fix by re-adding the node.
197
+ */
198
+ refusals: Array<{
199
+ peer: string;
200
+ reason: string;
201
+ }>;
202
+ /** Why the attempt itself failed, when `state` is `'error'`. */
203
+ error: string | null;
204
+ /** When the node reported this, ISO 8601 (UTC, no offset), or `null`. */
205
+ reportedAt: string | null;
158
206
  }
159
207
  /**
160
208
  * One node serving a namespace, as a caller with no cloud account sees it.
@@ -66,6 +66,27 @@ function teeRoleOf(row) {
66
66
  export function pickExecutingRelay(relays) {
67
67
  return (relays.find((r) => r.canExecute && r.relayUrl !== null && r.executorAccount !== null) ?? null);
68
68
  }
69
+ /** `join` from a relay row, or `null` when absent or not an object. */
70
+ function joinOf(row) {
71
+ const raw = row.join;
72
+ if (!raw || typeof raw !== 'object' || Array.isArray(raw))
73
+ return null;
74
+ const join = raw;
75
+ if (typeof join.state !== 'string')
76
+ return null;
77
+ const refusals = Array.isArray(join.refusals)
78
+ ? join.refusals
79
+ .filter((r) => !!r && typeof r === 'object')
80
+ .map((r) => ({ peer: String(r.peer ?? ''), reason: String(r.reason ?? '') }))
81
+ : [];
82
+ return {
83
+ state: join.state,
84
+ attempts: typeof join.attempts === 'number' ? join.attempts : 0,
85
+ refusals,
86
+ error: typeof join.error === 'string' ? join.error : null,
87
+ reportedAt: typeof join.reported_at === 'string' ? join.reported_at : null,
88
+ };
89
+ }
69
90
  const ACCOUNT_HEX = /^[0-9a-f]{64}$/;
70
91
  function accountHexOrNull(value) {
71
92
  return typeof value === 'string' && ACCOUNT_HEX.test(value) ? value : null;
@@ -349,6 +370,9 @@ export class CloudClient {
349
370
  canExecute: canExecuteOf(row, authorshipReady && relayUrl !== null && executorAccount !== null),
350
371
  lastSeenAt: row.last_seen_at ?? null,
351
372
  confirmedAt: row.confirmed_at ?? null,
373
+ admission: typeof row.admission === 'string' ? row.admission : null,
374
+ admissionHint: typeof row.admission_hint === 'string' ? row.admission_hint : null,
375
+ join: joinOf(row),
352
376
  };
353
377
  });
354
378
  }
@@ -1,5 +1,5 @@
1
1
  export { CloudClient, admitterAddrsFromNetworkStatus, AccountHaRefusedError, AccountNotLinkedError, AccountLinkedToSeveralUsersError, HaRequestPendingError, UnknownRelayError, RelayNotDialableError, } from './cloud-client.js';
2
- export type { CloudClientConfig, CloudSession, CloudNamespace, CloudRelay, CloudAccountRelay, CloudNamespaceNode, CloudNamespaceRouting, CloudMachine, CloudMachineNamespace, CloudLinkedAccount, CloudLinkedAccounts, CloudAccountLink, AccountLinkChallenge, AccountLinkProof, AccountLinkHandoff, AccountLinkCallback, AccountLoginChallenge, AccountLoginProof, EnableHAOptions, DisableHAOptions, EnableHaAsAccountOptions, } from './cloud-client.js';
2
+ export type { CloudClientConfig, CloudSession, CloudNamespace, CloudRelay, CloudRelayJoin, CloudAccountRelay, CloudNamespaceNode, CloudNamespaceRouting, CloudMachine, CloudMachineNamespace, CloudLinkedAccount, CloudLinkedAccounts, CloudAccountLink, AccountLinkChallenge, AccountLinkProof, AccountLinkHandoff, AccountLinkCallback, AccountLoginChallenge, AccountLoginProof, EnableHAOptions, DisableHAOptions, EnableHaAsAccountOptions, } from './cloud-client.js';
3
3
  export { connectCloud, connectCloudWithAccount } from './connect.js';
4
4
  export type { ConnectCloudOptions, ConnectCloudWithAccountOptions, CloudConnection, } from './connect.js';
5
5
  export { signRoutingChallenge, routingProofHeaders } from './routing-proof.js';