@calimero-network/mero-js 23.4.3 → 23.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.
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,
@@ -175,6 +180,7 @@ __export(index_exports, {
175
180
  rootGuardedOpBytes: () => rootGuardedOpBytes,
176
181
  routingProofHeaders: () => routingProofHeaders,
177
182
  shouldRetain: () => shouldRetain,
183
+ signAccountHaClaim: () => signAccountHaClaim,
178
184
  signAccountLink: () => signAccountLink,
179
185
  signAccountLogin: () => signAccountLogin,
180
186
  signAccountOwnershipClaim: () => signAccountOwnershipClaim,
@@ -3686,6 +3692,54 @@ async function resolveSigner(secret, signer, label2) {
3686
3692
  throw new Error(`${label2} or signer is required`);
3687
3693
  }
3688
3694
 
3695
+ // src/cloud/account-ownership.ts
3696
+ var DOMAIN = new TextEncoder().encode("calimero.mdma.account-ownership-claim.v1\0");
3697
+ var DEFAULT_TTL_MS = 6e4;
3698
+ var MAX_TTL_MS = 5 * 6e4;
3699
+ var ACCOUNT_OWNERSHIP_AUDIENCE = "mdma:enable-ha-namespace";
3700
+ var ACCOUNT_HA_AUDIENCE = "mdma:enable-ha-namespace-as-account";
3701
+ function base64(bytes) {
3702
+ let binary = "";
3703
+ for (const b of bytes) binary += String.fromCharCode(b);
3704
+ return btoa(binary);
3705
+ }
3706
+ var hex32 = (value, what) => {
3707
+ if (!/^[0-9a-fA-F]{64}$/.test(value)) throw new Error(`${what} must be 64 hex characters`);
3708
+ return value.toLowerCase();
3709
+ };
3710
+ async function signClaim(input, audience, subject) {
3711
+ const ttl = input.ttlMs ?? DEFAULT_TTL_MS;
3712
+ if (!(ttl > 0 && ttl <= MAX_TTL_MS)) throw new Error(`ttlMs must be in (0, ${MAX_TTL_MS}]`);
3713
+ const groupId = hex32(input.namespaceId, "namespaceId");
3714
+ const accountId = hex32(input.accountId, "accountId");
3715
+ const salt = hex32(input.salt, "salt");
3716
+ const signer = await resolveSigner(input.deviceSecret, input.signer, "deviceSecret");
3717
+ const issued = (input.now ?? Date.now)();
3718
+ const nonce2 = Array.from(crypto.getRandomValues(new Uint8Array(16)), (b) => b.toString(16).padStart(2, "0")).join("");
3719
+ const payload = new TextEncoder().encode(
3720
+ JSON.stringify({
3721
+ v: 1,
3722
+ audience,
3723
+ group_id: groupId,
3724
+ account_id: accountId,
3725
+ salt,
3726
+ ...subject === void 0 ? {} : { subject },
3727
+ nonce: nonce2,
3728
+ issued_at_ms: issued,
3729
+ expires_at_ms: issued + ttl
3730
+ })
3731
+ );
3732
+ const signature = await signer.sign(concat(DOMAIN, payload));
3733
+ return { kind: "account", credential: input.credential, signed_payload: base64(payload), signature: base64(signature) };
3734
+ }
3735
+ async function signAccountOwnershipClaim(input) {
3736
+ if (!input.subject) throw new Error("subject must be the cloud login email");
3737
+ return signClaim(input, ACCOUNT_OWNERSHIP_AUDIENCE, input.subject);
3738
+ }
3739
+ async function signAccountHaClaim(input) {
3740
+ return signClaim(input, ACCOUNT_HA_AUDIENCE, void 0);
3741
+ }
3742
+
3689
3743
  // src/device-cert/device-cert.ts
3690
3744
  var CERT_DOMAIN = new TextEncoder().encode("calimero.device.cert.v1");
3691
3745
  var ACCOUNT_ID_DOMAIN = new TextEncoder().encode("calimero.account.genesis.v1");
@@ -5977,7 +6031,7 @@ async function signAccountLink(input) {
5977
6031
  const signature = await signer.sign(
5978
6032
  concat(ACCOUNT_LINK_DOMAIN, new TextEncoder().encode(input.nonce))
5979
6033
  );
5980
- return base64(signature);
6034
+ return base642(signature);
5981
6035
  }
5982
6036
  async function signAccountLogin(input) {
5983
6037
  if (!input.nonce) throw new Error("nonce is required to sign an account login");
@@ -5985,7 +6039,7 @@ async function signAccountLogin(input) {
5985
6039
  const signature = await signer.sign(
5986
6040
  concat(ACCOUNT_LOGIN_DOMAIN, new TextEncoder().encode(input.nonce))
5987
6041
  );
5988
- return base64(signature);
6042
+ return base642(signature);
5989
6043
  }
5990
6044
  async function withPhrase(secret, phrase) {
5991
6045
  return { ...await accountRootFromSecret(secret), phrase };
@@ -6038,7 +6092,7 @@ function bitsOf(bytes) {
6038
6092
  for (const byte of bytes) bits += byte.toString(2).padStart(8, "0");
6039
6093
  return bits;
6040
6094
  }
6041
- function base64(bytes) {
6095
+ function base642(bytes) {
6042
6096
  let binary = "";
6043
6097
  for (const byte of bytes) binary += String.fromCharCode(byte);
6044
6098
  return btoa(binary);
@@ -6058,7 +6112,7 @@ async function signRoutingChallenge(nonce2, deviceSecret) {
6058
6112
  const signature = new Uint8Array(
6059
6113
  await crypto.subtle.sign({ name: "Ed25519" }, key, message)
6060
6114
  );
6061
- return base642(signature);
6115
+ return base643(signature);
6062
6116
  }
6063
6117
  async function routingProofHeaders(challenge, credential) {
6064
6118
  return {
@@ -6070,7 +6124,7 @@ async function routingProofHeaders(challenge, credential) {
6070
6124
  )
6071
6125
  };
6072
6126
  }
6073
- function base642(bytes) {
6127
+ function base643(bytes) {
6074
6128
  let binary = "";
6075
6129
  for (const byte of bytes) binary += String.fromCharCode(byte);
6076
6130
  return btoa(binary);
@@ -6087,6 +6141,39 @@ function teeRoleOf(row) {
6087
6141
  function pickExecutingRelay(relays) {
6088
6142
  return relays.find((r) => r.canExecute && r.relayUrl !== null && r.executorAccount !== null) ?? null;
6089
6143
  }
6144
+ var AccountHaRefusedError = class extends HTTPError {
6145
+ constructor(code, status, statusText, url, headers, bodyText) {
6146
+ super(status, statusText, url, headers, bodyText);
6147
+ this.code = code;
6148
+ this.name = "AccountHaRefusedError";
6149
+ }
6150
+ };
6151
+ var AccountNotLinkedError = class extends AccountHaRefusedError {
6152
+ constructor() {
6153
+ super(...arguments);
6154
+ this.name = "AccountNotLinkedError";
6155
+ }
6156
+ };
6157
+ AccountNotLinkedError.CODE = "account_not_linked";
6158
+ var AccountLinkedToSeveralUsersError = class extends AccountHaRefusedError {
6159
+ constructor() {
6160
+ super(...arguments);
6161
+ this.name = "AccountLinkedToSeveralUsersError";
6162
+ }
6163
+ };
6164
+ AccountLinkedToSeveralUsersError.CODE = "account_linked_to_several_users";
6165
+ function conflictCode(bodyText) {
6166
+ if (!bodyText) return void 0;
6167
+ try {
6168
+ const body = JSON.parse(bodyText);
6169
+ if (typeof body?.error === "string") return body.error;
6170
+ if (typeof body?.detail === "string") return body.detail;
6171
+ const nested = body?.detail?.error;
6172
+ if (typeof nested === "string") return nested;
6173
+ } catch {
6174
+ }
6175
+ return void 0;
6176
+ }
6090
6177
  var REFRESH_HEADER = "x-mdma-session-refresh";
6091
6178
  var REFRESH_EXPIRES_HEADER = "x-mdma-session-expires";
6092
6179
  var CloudClient = class {
@@ -6547,6 +6634,52 @@ var CloudClient = class {
6547
6634
  { body: { groups: [], ownership_proof: ownershipProof } }
6548
6635
  );
6549
6636
  }
6637
+ /**
6638
+ * Enable HA for a namespace this account founded, with **no cloud session**.
6639
+ *
6640
+ * The anonymous sibling of {@link enableFoundedNamespaceHa}: it signs the
6641
+ * founder's claim itself ({@link signAccountHaClaim} — no `subject`, its own
6642
+ * audience) and posts it to
6643
+ * `/api/cloud/accounts/{accountId}/namespaces/{namespaceId}/enable-ha`. The
6644
+ * cloud bills the user the account is linked to through the wallet, so the
6645
+ * account must be linked to exactly one:
6646
+ *
6647
+ * - not linked at all → {@link AccountNotLinkedError};
6648
+ * - linked to several → {@link AccountLinkedToSeveralUsersError}.
6649
+ *
6650
+ * A claim that does not verify is a `403` `HTTPError`; quota and the rest
6651
+ * answer as the session route does. Returns the cloud's response body, the
6652
+ * same shape the session route's enable-ha returns.
6653
+ */
6654
+ async enableHaAsAccount(options) {
6655
+ const proof = await signAccountHaClaim({
6656
+ namespaceId: options.namespaceId,
6657
+ accountId: options.accountId,
6658
+ salt: options.salt,
6659
+ credential: options.credential,
6660
+ deviceSecret: options.deviceSecret,
6661
+ signer: options.signer,
6662
+ ttlMs: options.ttlMs
6663
+ });
6664
+ const account = options.accountId.toLowerCase();
6665
+ const namespace = options.namespaceId.toLowerCase();
6666
+ try {
6667
+ return await this.request(
6668
+ "POST",
6669
+ `/api/cloud/accounts/${encodeURIComponent(account)}/namespaces/${encodeURIComponent(namespace)}/enable-ha`,
6670
+ { body: { ownership_proof: proof }, anonymous: true }
6671
+ );
6672
+ } catch (err) {
6673
+ if (err instanceof HTTPError && err.status === 409) {
6674
+ const code = conflictCode(err.bodyText);
6675
+ const Typed = code === AccountNotLinkedError.CODE ? AccountNotLinkedError : code === AccountLinkedToSeveralUsersError.CODE ? AccountLinkedToSeveralUsersError : void 0;
6676
+ if (Typed && code) {
6677
+ throw new Typed(code, err.status, err.statusText, err.url, err.headers, err.bodyText);
6678
+ }
6679
+ }
6680
+ throw err;
6681
+ }
6682
+ }
6550
6683
  /**
6551
6684
  * Stop hosting a claimed namespace.
6552
6685
  *
@@ -10055,43 +10188,6 @@ function assertSafeReturnTo(returnTo) {
10055
10188
  );
10056
10189
  }
10057
10190
 
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
10191
  // src/sealed/verify.ts
10096
10192
  var MEASUREMENT_HEX = /^[0-9a-f]{96}$/;
10097
10193
  var REGISTERS = ["mrtd", "rtmr0", "rtmr1", "rtmr2", "rtmr3"];