@calimero-network/mero-js 29.4.0 → 29.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
@@ -62,6 +62,7 @@ __export(index_exports, {
62
62
  QuotaExceeded: () => QuotaExceeded,
63
63
  RECOVERY_DOMAIN: () => RECOVERY_DOMAIN,
64
64
  RELEASE_VERIFIER: () => RELEASE_VERIFIER,
65
+ RecoveryNotReady: () => RecoveryNotReady,
65
66
  ReleaseDeadlineError: () => ReleaseDeadlineError,
66
67
  SEALED_CONTENT_TYPE: () => SEALED_CONTENT_TYPE,
67
68
  SEALED_PATH: () => SEALED_PATH,
@@ -71,6 +72,7 @@ __export(index_exports, {
71
72
  TrapError: () => TrapError,
72
73
  UnavailableError: () => UnavailableError,
73
74
  WrongNode: () => WrongNode,
75
+ WrongRecoveryCode: () => WrongRecoveryCode,
74
76
  agentKeyBinding: () => agentKeyBinding,
75
77
  agentQuoteVerifier: () => agentQuoteVerifier,
76
78
  attestAgent: () => attestAgent,
@@ -1545,6 +1547,19 @@ var OriginTaken = class extends CalimeroError {
1545
1547
  this.name = "OriginTaken";
1546
1548
  }
1547
1549
  };
1550
+ var RecoveryNotReady = class extends CalimeroError {
1551
+ constructor(message, details = {}) {
1552
+ super(409, "RECOVERY_NOT_READY", message, details);
1553
+ this.name = "RecoveryNotReady";
1554
+ this.readyAt = typeof details.readyAt === "number" ? details.readyAt : void 0;
1555
+ }
1556
+ };
1557
+ var WrongRecoveryCode = class extends CalimeroError {
1558
+ constructor(message, details = {}) {
1559
+ super(401, "UNAUTHENTICATED", message || "unknown or used recovery code", details);
1560
+ this.name = "WrongRecoveryCode";
1561
+ }
1562
+ };
1548
1563
  var SignUpRefused = class extends CalimeroError {
1549
1564
  constructor(message, details = {}) {
1550
1565
  super(403, "SIGN_UP_REFUSED", message, details);
@@ -1634,6 +1649,8 @@ function errorFrom(status, body, retryAfterHeader) {
1634
1649
  return new OriginTaken(message, details);
1635
1650
  case "429 QUOTA_EXCEEDED":
1636
1651
  return new QuotaExceeded(message, details);
1652
+ case "409 RECOVERY_NOT_READY":
1653
+ return new RecoveryNotReady(message, details);
1637
1654
  case "403 SIGN_UP_REFUSED":
1638
1655
  return new SignUpRefused(message, details);
1639
1656
  case "412 CONFIRM_REQUIRED":
@@ -1980,8 +1997,9 @@ function retryDelayMs(retry, options) {
1980
1997
  return Math.min(options.baseDelayMs * 2 ** retry, options.maxDelayMs);
1981
1998
  }
1982
1999
  function ceremony(path) {
1983
- return path === "auth/refresh" || path.startsWith("auth/passkey/");
2000
+ return path === "auth/refresh" || path.startsWith("auth/passkey/") || RECOVERY_CEREMONIES.has(path);
1984
2001
  }
2002
+ var RECOVERY_CEREMONIES = /* @__PURE__ */ new Set(["auth/recovery/begin", "auth/recovery/start", "auth/recovery/finish"]);
1985
2003
  var CalimeroClient = class _CalimeroClient {
1986
2004
  constructor(options) {
1987
2005
  /** The one in use: an index into {@link nodeUrls}. */
@@ -2416,6 +2434,71 @@ var CalimeroClient = class _CalimeroClient {
2416
2434
  async labelPasskey(credentialId, label) {
2417
2435
  await this.post(`auth/passkeys/${seg(credentialId)}`, { label });
2418
2436
  }
2437
+ // ---------------------------------------------------------------- account recovery
2438
+ //
2439
+ // core #4844 (`docs/specs/account-recovery.md`): a recovery code, made with
2440
+ // a passkey and shown once, lets someone who lost every passkey register a
2441
+ // new one; it is added, and the account signed in, 72 hours later unless a
2442
+ // passkey the account still has cancels it first.
2443
+ /**
2444
+ * Make the account's recovery code (replacing any it had), approved with
2445
+ * `passkey` (`412 CONFIRM_REQUIRED`). Answers the code, once: show it to
2446
+ * the person to keep offline; the node keeps only its public key. A passkey
2447
+ * session on the cluster's own origin only (never an API key or a
2448
+ * package-bound session).
2449
+ */
2450
+ async createRecoveryCode(passkey) {
2451
+ const { code } = await this.postConfirmed("auth/recovery", {}, passkey);
2452
+ return code;
2453
+ }
2454
+ /** Remove the account's recovery code, approved with `passkey`. */
2455
+ async removeRecoveryCode(passkey) {
2456
+ await this.deleteConfirmed("auth/recovery", passkey);
2457
+ }
2458
+ /**
2459
+ * Signed out: start recovering the account `code` names. `passkey` makes a
2460
+ * new passkey for it (the browser's create prompt, user verified); the
2461
+ * node spends the code and keeps the passkey as pending. Nothing is added
2462
+ * and no session opens: finish with {@link finishAccountRecovery} at
2463
+ * `readyAt`. A code the cluster does not hold (mistyped, replaced or
2464
+ * spent) is {@link WrongRecoveryCode}; an account with a recovery already
2465
+ * under way `409 RECOVERY_PENDING`.
2466
+ */
2467
+ async startAccountRecovery(code, passkey) {
2468
+ try {
2469
+ const begin = await this.post("auth/recovery/begin", { code });
2470
+ const registration = await passkey.create(begin.options);
2471
+ return await this.post("auth/recovery/start", { challengeId: begin.challengeId, code, registration });
2472
+ } catch (error) {
2473
+ if (error instanceof LoginRequired) throw new WrongRecoveryCode(error.message.replace(/^401 UNAUTHENTICATED: /, ""), error.details);
2474
+ throw error;
2475
+ }
2476
+ }
2477
+ /**
2478
+ * Cancel the recovery of the signed-in account under way, approved with
2479
+ * `passkey` (one the account already has). The code stays spent: make a
2480
+ * new one.
2481
+ */
2482
+ async cancelAccountRecovery(passkey) {
2483
+ await this.postConfirmed("auth/recovery/cancel", {}, passkey);
2484
+ }
2485
+ /**
2486
+ * Signed out, once the delay passed: finish the recovery with the passkey
2487
+ * it registered (`passkey` signs the node's sign-in challenge). The passkey
2488
+ * joins the account, every other session of the account ends, and this
2489
+ * client is signed in. `accountId` (from the start) lets the node refuse
2490
+ * early and offer only that passkey; before `readyAt` it is
2491
+ * {@link RecoveryNotReady}, with no recovery under way {@link NotFound}.
2492
+ */
2493
+ async finishAccountRecovery(passkey, accountId) {
2494
+ const session = await this.postConfirmed(
2495
+ "auth/recovery/finish",
2496
+ accountId ? { accountId } : {},
2497
+ passkey
2498
+ );
2499
+ this.useSession(session);
2500
+ return session;
2501
+ }
2419
2502
  /** What this account uses (apps, bundle bytes, API keys, owned orgs), with the cluster's quotas. */
2420
2503
  usage() {
2421
2504
  return this.get("auth/usage");