@calimero-network/mero-js 23.4.3 → 23.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
@@ -19,6 +19,11 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
19
19
  // src/index.ts
20
20
  var index_exports = {};
21
21
  __export(index_exports, {
22
+ ACCOUNT_HA_AUDIENCE: () => ACCOUNT_HA_AUDIENCE,
23
+ ACCOUNT_OWNERSHIP_AUDIENCE: () => ACCOUNT_OWNERSHIP_AUDIENCE,
24
+ AccountHaRefusedError: () => AccountHaRefusedError,
25
+ AccountLinkedToSeveralUsersError: () => AccountLinkedToSeveralUsersError,
26
+ AccountNotLinkedError: () => AccountNotLinkedError,
22
27
  AdminApiClient: () => AdminApiClient,
23
28
  AuthApiClient: () => AuthApiClient,
24
29
  AuthRevokedError: () => AuthRevokedError,
@@ -30,6 +35,7 @@ __export(index_exports, {
30
35
  EphemeralClient: () => EphemeralClient,
31
36
  HANDSHAKE_PATH: () => HANDSHAKE_PATH,
32
37
  HTTPError: () => HTTPError,
38
+ HaRequestPendingError: () => HaRequestPendingError,
33
39
  INVITED_ROLE: () => INVITED_ROLE,
34
40
  IntentRefusedError: () => IntentRefusedError,
35
41
  LocalStorageTokenStore: () => LocalStorageTokenStore,
@@ -42,6 +48,7 @@ __export(index_exports, {
42
48
  PRESENCE_DOMAIN: () => PRESENCE_DOMAIN,
43
49
  REQUEST_PROOF_HEADER: () => REQUEST_PROOF_HEADER,
44
50
  RelayClient: () => RelayClient,
51
+ RelayNotDialableError: () => RelayNotDialableError,
45
52
  RelayObserver: () => RelayObserver,
46
53
  RelayPresenceClient: () => RelayPresenceClient,
47
54
  RelayTransport: () => RelayTransport,
@@ -53,6 +60,7 @@ __export(index_exports, {
53
60
  SealedTransportError: () => SealedTransportError,
54
61
  SseClient: () => SseClient,
55
62
  StaleTransportKeyError: () => StaleTransportKeyError,
63
+ UnknownRelayError: () => UnknownRelayError,
56
64
  WarrantNonceExhaustedError: () => WarrantNonceExhaustedError,
57
65
  WarrantNonceRouteUnavailableError: () => WarrantNonceRouteUnavailableError,
58
66
  WebHttpClient: () => WebHttpClient,
@@ -175,6 +183,7 @@ __export(index_exports, {
175
183
  rootGuardedOpBytes: () => rootGuardedOpBytes,
176
184
  routingProofHeaders: () => routingProofHeaders,
177
185
  shouldRetain: () => shouldRetain,
186
+ signAccountHaClaim: () => signAccountHaClaim,
178
187
  signAccountLink: () => signAccountLink,
179
188
  signAccountLogin: () => signAccountLogin,
180
189
  signAccountOwnershipClaim: () => signAccountOwnershipClaim,
@@ -3686,6 +3695,59 @@ async function resolveSigner(secret, signer, label2) {
3686
3695
  throw new Error(`${label2} or signer is required`);
3687
3696
  }
3688
3697
 
3698
+ // src/cloud/account-ownership.ts
3699
+ var DOMAIN = new TextEncoder().encode("calimero.mdma.account-ownership-claim.v1\0");
3700
+ var DEFAULT_TTL_MS = 6e4;
3701
+ var MAX_TTL_MS = 5 * 6e4;
3702
+ var MAX_RELAY_URL = 1024;
3703
+ var ACCOUNT_OWNERSHIP_AUDIENCE = "mdma:enable-ha-namespace";
3704
+ var ACCOUNT_HA_AUDIENCE = "mdma:enable-ha-namespace-as-account";
3705
+ function base64(bytes) {
3706
+ let binary = "";
3707
+ for (const b of bytes) binary += String.fromCharCode(b);
3708
+ return btoa(binary);
3709
+ }
3710
+ var hex32 = (value, what) => {
3711
+ if (!/^[0-9a-fA-F]{64}$/.test(value)) throw new Error(`${what} must be 64 hex characters`);
3712
+ return value.toLowerCase();
3713
+ };
3714
+ async function signClaim(input, audience, subject, relayUrl) {
3715
+ if (relayUrl !== void 0 && !(relayUrl.length > 0 && relayUrl.length <= MAX_RELAY_URL)) {
3716
+ throw new Error(`relayUrl must be a non-empty string of at most ${MAX_RELAY_URL} characters`);
3717
+ }
3718
+ const ttl = input.ttlMs ?? DEFAULT_TTL_MS;
3719
+ if (!(ttl > 0 && ttl <= MAX_TTL_MS)) throw new Error(`ttlMs must be in (0, ${MAX_TTL_MS}]`);
3720
+ const groupId = hex32(input.namespaceId, "namespaceId");
3721
+ const accountId = hex32(input.accountId, "accountId");
3722
+ const salt = hex32(input.salt, "salt");
3723
+ const signer = await resolveSigner(input.deviceSecret, input.signer, "deviceSecret");
3724
+ const issued = (input.now ?? Date.now)();
3725
+ const nonce2 = Array.from(crypto.getRandomValues(new Uint8Array(16)), (b) => b.toString(16).padStart(2, "0")).join("");
3726
+ const payload = new TextEncoder().encode(
3727
+ JSON.stringify({
3728
+ v: 1,
3729
+ audience,
3730
+ group_id: groupId,
3731
+ account_id: accountId,
3732
+ salt,
3733
+ ...subject === void 0 ? {} : { subject },
3734
+ nonce: nonce2,
3735
+ issued_at_ms: issued,
3736
+ expires_at_ms: issued + ttl,
3737
+ ...relayUrl === void 0 ? {} : { relay_url: relayUrl }
3738
+ })
3739
+ );
3740
+ const signature = await signer.sign(concat(DOMAIN, payload));
3741
+ return { kind: "account", credential: input.credential, signed_payload: base64(payload), signature: base64(signature) };
3742
+ }
3743
+ async function signAccountOwnershipClaim(input) {
3744
+ if (!input.subject) throw new Error("subject must be the cloud login email");
3745
+ return signClaim(input, ACCOUNT_OWNERSHIP_AUDIENCE, input.subject);
3746
+ }
3747
+ async function signAccountHaClaim(input) {
3748
+ return signClaim(input, ACCOUNT_HA_AUDIENCE, void 0, input.relayUrl);
3749
+ }
3750
+
3689
3751
  // src/device-cert/device-cert.ts
3690
3752
  var CERT_DOMAIN = new TextEncoder().encode("calimero.device.cert.v1");
3691
3753
  var ACCOUNT_ID_DOMAIN = new TextEncoder().encode("calimero.account.genesis.v1");
@@ -5977,7 +6039,7 @@ async function signAccountLink(input) {
5977
6039
  const signature = await signer.sign(
5978
6040
  concat(ACCOUNT_LINK_DOMAIN, new TextEncoder().encode(input.nonce))
5979
6041
  );
5980
- return base64(signature);
6042
+ return base642(signature);
5981
6043
  }
5982
6044
  async function signAccountLogin(input) {
5983
6045
  if (!input.nonce) throw new Error("nonce is required to sign an account login");
@@ -5985,7 +6047,7 @@ async function signAccountLogin(input) {
5985
6047
  const signature = await signer.sign(
5986
6048
  concat(ACCOUNT_LOGIN_DOMAIN, new TextEncoder().encode(input.nonce))
5987
6049
  );
5988
- return base64(signature);
6050
+ return base642(signature);
5989
6051
  }
5990
6052
  async function withPhrase(secret, phrase) {
5991
6053
  return { ...await accountRootFromSecret(secret), phrase };
@@ -6038,7 +6100,7 @@ function bitsOf(bytes) {
6038
6100
  for (const byte of bytes) bits += byte.toString(2).padStart(8, "0");
6039
6101
  return bits;
6040
6102
  }
6041
- function base64(bytes) {
6103
+ function base642(bytes) {
6042
6104
  let binary = "";
6043
6105
  for (const byte of bytes) binary += String.fromCharCode(byte);
6044
6106
  return btoa(binary);
@@ -6058,7 +6120,7 @@ async function signRoutingChallenge(nonce2, deviceSecret) {
6058
6120
  const signature = new Uint8Array(
6059
6121
  await crypto.subtle.sign({ name: "Ed25519" }, key, message)
6060
6122
  );
6061
- return base642(signature);
6123
+ return base643(signature);
6062
6124
  }
6063
6125
  async function routingProofHeaders(challenge, credential) {
6064
6126
  return {
@@ -6070,7 +6132,7 @@ async function routingProofHeaders(challenge, credential) {
6070
6132
  )
6071
6133
  };
6072
6134
  }
6073
- function base642(bytes) {
6135
+ function base643(bytes) {
6074
6136
  let binary = "";
6075
6137
  for (const byte of bytes) binary += String.fromCharCode(byte);
6076
6138
  return btoa(binary);
@@ -6087,6 +6149,64 @@ function teeRoleOf(row) {
6087
6149
  function pickExecutingRelay(relays) {
6088
6150
  return relays.find((r) => r.canExecute && r.relayUrl !== null && r.executorAccount !== null) ?? null;
6089
6151
  }
6152
+ var AccountHaRefusedError = class extends HTTPError {
6153
+ constructor(code, status, statusText, url, headers, bodyText) {
6154
+ super(status, statusText, url, headers, bodyText);
6155
+ this.code = code;
6156
+ this.name = "AccountHaRefusedError";
6157
+ }
6158
+ };
6159
+ var AccountNotLinkedError = class extends AccountHaRefusedError {
6160
+ constructor() {
6161
+ super(...arguments);
6162
+ this.name = "AccountNotLinkedError";
6163
+ }
6164
+ };
6165
+ AccountNotLinkedError.CODE = "account_not_linked";
6166
+ var AccountLinkedToSeveralUsersError = class extends AccountHaRefusedError {
6167
+ constructor() {
6168
+ super(...arguments);
6169
+ this.name = "AccountLinkedToSeveralUsersError";
6170
+ }
6171
+ };
6172
+ AccountLinkedToSeveralUsersError.CODE = "account_linked_to_several_users";
6173
+ var HaRequestPendingError = class extends AccountHaRefusedError {
6174
+ constructor() {
6175
+ super(...arguments);
6176
+ this.name = "HaRequestPendingError";
6177
+ }
6178
+ };
6179
+ HaRequestPendingError.CODE = "ha_request_pending";
6180
+ var UnknownRelayError = class extends AccountHaRefusedError {
6181
+ constructor() {
6182
+ super(...arguments);
6183
+ this.name = "UnknownRelayError";
6184
+ }
6185
+ };
6186
+ UnknownRelayError.CODE = "unknown_relay";
6187
+ var RelayNotDialableError = class extends AccountHaRefusedError {
6188
+ constructor() {
6189
+ super(...arguments);
6190
+ this.name = "RelayNotDialableError";
6191
+ }
6192
+ };
6193
+ RelayNotDialableError.CODE = "relay_not_dialable";
6194
+ var ACCOUNT_HA_REFUSALS = {
6195
+ 409: [AccountNotLinkedError, AccountLinkedToSeveralUsersError, HaRequestPendingError],
6196
+ 422: [UnknownRelayError, RelayNotDialableError]
6197
+ };
6198
+ function conflictCode(bodyText) {
6199
+ if (!bodyText) return void 0;
6200
+ try {
6201
+ const body = JSON.parse(bodyText);
6202
+ if (typeof body?.error === "string") return body.error;
6203
+ if (typeof body?.detail === "string") return body.detail;
6204
+ const nested = body?.detail?.error;
6205
+ if (typeof nested === "string") return nested;
6206
+ } catch {
6207
+ }
6208
+ return void 0;
6209
+ }
6090
6210
  var REFRESH_HEADER = "x-mdma-session-refresh";
6091
6211
  var REFRESH_EXPIRES_HEADER = "x-mdma-session-expires";
6092
6212
  var CloudClient = class {
@@ -6547,6 +6667,53 @@ var CloudClient = class {
6547
6667
  { body: { groups: [], ownership_proof: ownershipProof } }
6548
6668
  );
6549
6669
  }
6670
+ /**
6671
+ * Enable HA for a namespace this account founded, with **no cloud session**.
6672
+ *
6673
+ * The anonymous sibling of {@link enableFoundedNamespaceHa}: it signs the
6674
+ * founder's claim itself ({@link signAccountHaClaim} — no `subject`, its own
6675
+ * audience) and posts it to
6676
+ * `/api/cloud/accounts/{accountId}/namespaces/{namespaceId}/enable-ha`. The
6677
+ * cloud bills the user the account is linked to through the wallet, so the
6678
+ * account must be linked to exactly one:
6679
+ *
6680
+ * - not linked at all → {@link AccountNotLinkedError};
6681
+ * - linked to several → {@link AccountLinkedToSeveralUsersError}.
6682
+ *
6683
+ * A claim that does not verify is a `403` `HTTPError`; quota and the rest
6684
+ * answer as the session route does. Returns the cloud's response body, the
6685
+ * same shape the session route's enable-ha returns.
6686
+ */
6687
+ async enableHaAsAccount(options) {
6688
+ const proof = await signAccountHaClaim({
6689
+ namespaceId: options.namespaceId,
6690
+ accountId: options.accountId,
6691
+ salt: options.salt,
6692
+ credential: options.credential,
6693
+ deviceSecret: options.deviceSecret,
6694
+ signer: options.signer,
6695
+ ttlMs: options.ttlMs,
6696
+ relayUrl: options.relayUrl
6697
+ });
6698
+ const account = options.accountId.toLowerCase();
6699
+ const namespace = options.namespaceId.toLowerCase();
6700
+ try {
6701
+ return await this.request(
6702
+ "POST",
6703
+ `/api/cloud/accounts/${encodeURIComponent(account)}/namespaces/${encodeURIComponent(namespace)}/enable-ha`,
6704
+ { body: { ownership_proof: proof }, anonymous: true }
6705
+ );
6706
+ } catch (err) {
6707
+ if (err instanceof HTTPError && ACCOUNT_HA_REFUSALS[err.status]) {
6708
+ const code = conflictCode(err.bodyText);
6709
+ const Typed = ACCOUNT_HA_REFUSALS[err.status].find((t) => t.CODE === code);
6710
+ if (Typed && code) {
6711
+ throw new Typed(code, err.status, err.statusText, err.url, err.headers, err.bodyText);
6712
+ }
6713
+ }
6714
+ throw err;
6715
+ }
6716
+ }
6550
6717
  /**
6551
6718
  * Stop hosting a claimed namespace.
6552
6719
  *
@@ -10055,43 +10222,6 @@ function assertSafeReturnTo(returnTo) {
10055
10222
  );
10056
10223
  }
10057
10224
 
10058
- // src/cloud/account-ownership.ts
10059
- var DOMAIN = new TextEncoder().encode("calimero.mdma.account-ownership-claim.v1\0");
10060
- var DEFAULT_TTL_MS = 6e4;
10061
- var MAX_TTL_MS = 5 * 6e4;
10062
- function base643(bytes) {
10063
- let binary = "";
10064
- for (const b of bytes) binary += String.fromCharCode(b);
10065
- return btoa(binary);
10066
- }
10067
- var hex32 = (value, what) => {
10068
- if (!/^[0-9a-fA-F]{64}$/.test(value)) throw new Error(`${what} must be 64 hex characters`);
10069
- return value.toLowerCase();
10070
- };
10071
- async function signAccountOwnershipClaim(input) {
10072
- const ttl = input.ttlMs ?? DEFAULT_TTL_MS;
10073
- if (!(ttl > 0 && ttl <= MAX_TTL_MS)) throw new Error(`ttlMs must be in (0, ${MAX_TTL_MS}]`);
10074
- if (!input.subject) throw new Error("subject must be the cloud login email");
10075
- const signer = await resolveSigner(input.deviceSecret, input.signer, "deviceSecret");
10076
- const issued = (input.now ?? Date.now)();
10077
- const nonce2 = Array.from(crypto.getRandomValues(new Uint8Array(16)), (b) => b.toString(16).padStart(2, "0")).join("");
10078
- const payload = new TextEncoder().encode(
10079
- JSON.stringify({
10080
- v: 1,
10081
- audience: "mdma:enable-ha-namespace",
10082
- group_id: hex32(input.namespaceId, "namespaceId"),
10083
- account_id: hex32(input.accountId, "accountId"),
10084
- salt: hex32(input.salt, "salt"),
10085
- subject: input.subject,
10086
- nonce: nonce2,
10087
- issued_at_ms: issued,
10088
- expires_at_ms: issued + ttl
10089
- })
10090
- );
10091
- const signature = await signer.sign(concat(DOMAIN, payload));
10092
- return { kind: "account", credential: input.credential, signed_payload: base643(payload), signature: base643(signature) };
10093
- }
10094
-
10095
10225
  // src/sealed/verify.ts
10096
10226
  var MEASUREMENT_HEX = /^[0-9a-f]{96}$/;
10097
10227
  var REGISTERS = ["mrtd", "rtmr0", "rtmr1", "rtmr2", "rtmr3"];