@calimero-network/mero-js 29.7.6 → 29.8.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
@@ -1725,11 +1725,18 @@ function cancelled(message) {
1725
1725
  error.name = "NotAllowedError";
1726
1726
  return error;
1727
1727
  }
1728
- async function ask(method, options) {
1728
+ async function ask(method, options, signal, what) {
1729
1729
  const container = navigator.credentials;
1730
1730
  const own = globalThis.CredentialsContainer?.prototype?.[method];
1731
1731
  const call = own ?? container[method];
1732
- return call.call(container, options);
1732
+ try {
1733
+ return await call.call(container, signal ? { ...options, signal } : options);
1734
+ } catch (error) {
1735
+ if (signal?.aborted || error?.name === "AbortError") {
1736
+ throw Object.assign(cancelled(`${what} was cancelled`), { cause: error });
1737
+ }
1738
+ throw error;
1739
+ }
1733
1740
  }
1734
1741
  function es256First(params) {
1735
1742
  const es256 = params.filter((p) => p.alg === -7);
@@ -1737,7 +1744,7 @@ function es256First(params) {
1737
1744
  }
1738
1745
  function browserPasskey() {
1739
1746
  return {
1740
- async create(options) {
1747
+ async create(options, createOptions) {
1741
1748
  const o = options;
1742
1749
  const cred = await ask("create", {
1743
1750
  publicKey: {
@@ -1753,7 +1760,7 @@ function browserPasskey() {
1753
1760
  attestation: "none",
1754
1761
  timeout: o.timeout
1755
1762
  }
1756
- });
1763
+ }, createOptions?.signal, "passkey creation");
1757
1764
  if (!cred) throw cancelled("passkey creation was cancelled");
1758
1765
  const r = cred.response;
1759
1766
  return {
@@ -1772,7 +1779,7 @@ function browserPasskey() {
1772
1779
  id: buf(fromB64url(id))
1773
1780
  }))
1774
1781
  }
1775
- });
1782
+ }, options?.signal, "passkey sign-in");
1776
1783
  if (!cred) throw cancelled("passkey sign-in was cancelled");
1777
1784
  const r = cred.response;
1778
1785
  return {
@@ -2377,14 +2384,16 @@ var CalimeroClient = class _CalimeroClient {
2377
2384
  origin === void 0 ? { name, ...invite } : { name, origin, ...invite },
2378
2385
  origin
2379
2386
  );
2380
- const registration = await passkey.create(begin.options);
2387
+ const signal = options.signal;
2388
+ const registration = await (signal ? passkey.create(begin.options, { signal }) : passkey.create(begin.options));
2381
2389
  const session = await this.confirmed(
2382
2390
  "POST",
2383
2391
  "auth/passkey/register/finish",
2384
2392
  { challengeId: begin.challengeId, registration, ...invite },
2385
2393
  passkey,
2386
2394
  void 0,
2387
- "preferred"
2395
+ "preferred",
2396
+ signal
2388
2397
  );
2389
2398
  this.useSession(session);
2390
2399
  return session;
@@ -2405,7 +2414,8 @@ var CalimeroClient = class _CalimeroClient {
2405
2414
  const origin = this.pageOrigin(options);
2406
2415
  const begin = await this.begin("auth/passkey/login/begin", origin === void 0 ? void 0 : { origin }, origin);
2407
2416
  const rpId = begin.options.rpId ?? begin.options.rp?.id ?? "";
2408
- const assertion = await passkey.get(fromB64url(begin.options.challenge), rpId);
2417
+ const challenge = fromB64url(begin.options.challenge);
2418
+ const assertion = await (options.signal ? passkey.get(challenge, rpId, void 0, { signal: options.signal }) : passkey.get(challenge, rpId));
2409
2419
  const session = await this.post("auth/passkey/login/finish", {
2410
2420
  challengeId: begin.challengeId,
2411
2421
  assertion
@@ -2474,16 +2484,31 @@ var CalimeroClient = class _CalimeroClient {
2474
2484
  if (this.current === current) this.useSession(session);
2475
2485
  return session;
2476
2486
  }
2487
+ /**
2488
+ * End this session. The client is signed out whether or not the node
2489
+ * answers: the revoke (`POST /auth/logout`) is attempted, and its failure
2490
+ * (the node unreachable, say) is thrown after the session is dropped, so a
2491
+ * caller can say the server may still hold it, but no request after this
2492
+ * one carries its token.
2493
+ */
2477
2494
  async signOut() {
2478
- await this.post("auth/logout");
2479
- this.auth = void 0;
2480
- this.current = void 0;
2495
+ await this.signingOut("auth/logout");
2481
2496
  }
2482
- /** End every session of the signed-in account, on every device, this one too. */
2497
+ /**
2498
+ * End every session of the signed-in account, on every device, this one
2499
+ * too. Like {@link signOut}, this client is signed out even when the
2500
+ * request fails, and the failure is thrown.
2501
+ */
2483
2502
  async signOutEverywhere() {
2484
- await this.post("auth/logout-all");
2485
- this.auth = void 0;
2486
- this.current = void 0;
2503
+ await this.signingOut("auth/logout-all");
2504
+ }
2505
+ async signingOut(path) {
2506
+ try {
2507
+ await this.post(path);
2508
+ } finally {
2509
+ this.auth = void 0;
2510
+ this.current = void 0;
2511
+ }
2487
2512
  }
2488
2513
  /** Who the credential is: the account, how it signed in, and the package (`app`) and `origin` a session is bound to. */
2489
2514
  me() {
@@ -2775,7 +2800,7 @@ var CalimeroClient = class _CalimeroClient {
2775
2800
  deleteConfirmed(path, passkey, body = {}) {
2776
2801
  return this.confirmed("DELETE", path, body, passkey);
2777
2802
  }
2778
- async confirmed(method, path, body, passkey, send = (b) => this.json(method, path, b), userVerification = "required") {
2803
+ async confirmed(method, path, body, passkey, send = (b) => this.json(method, path, b), userVerification = "required", signal) {
2779
2804
  try {
2780
2805
  return await send(body);
2781
2806
  } catch (error) {
@@ -2784,7 +2809,8 @@ var CalimeroClient = class _CalimeroClient {
2784
2809
  let assertion;
2785
2810
  try {
2786
2811
  assertion = await passkey.get(fromB64url(d.challenge), d.rpId, d.allowCredentials, {
2787
- userVerification: userVerificationOf(d.userVerification, userVerification)
2812
+ userVerification: userVerificationOf(d.userVerification, userVerification),
2813
+ ...signal ? { signal } : {}
2788
2814
  });
2789
2815
  } catch (declined) {
2790
2816
  throw promptFailure(declined);
@@ -3171,16 +3197,33 @@ var CalimeroClient = class _CalimeroClient {
3171
3197
  }
3172
3198
  /**
3173
3199
  * Set capability bits ({@link ORG_CAPABILITIES}): for a member (`account`),
3174
- * for a member of one team (`account` and `team`), or the organization's
3175
- * default (neither). Approved with the caller's `passkey`.
3200
+ * for a member of one team (`account` and `team`), a team's default
3201
+ * (`team` alone, {@link setTeamDefaultCapabilities}), or the
3202
+ * organization's default (neither). Approved with the caller's `passkey`.
3176
3203
  */
3177
3204
  async setCapabilities(org, capabilities, passkey, at = {}) {
3178
3205
  await this.postConfirmed(`orgs/${seg(org)}/capabilities`, { capabilities, ...at }, passkey);
3179
3206
  }
3207
+ /**
3208
+ * Set the capability bits everyone in `team` holds there on top of their
3209
+ * own, unless their row sets its own (`POST /orgs/{org}/capabilities` with
3210
+ * `team` and no `account`): `ORG_CAPABILITIES.INVITE_MEMBERS` is "members
3211
+ * may invite others to this app". The team's managers, or whoever holds
3212
+ * `MANAGE_MEMBERS` on it; approved with the caller's `passkey`
3213
+ * (`__set_default_capabilities { team, capabilities }`).
3214
+ */
3215
+ async setTeamDefaultCapabilities(org, team, capabilities, passkey) {
3216
+ await this.postConfirmed(`orgs/${seg(org)}/capabilities`, { team, capabilities }, passkey);
3217
+ }
3180
3218
  async createTeam(org, name, join, parent) {
3181
3219
  const { teamId } = await this.post(`orgs/${seg(org)}/teams`, { name, join, parent });
3182
3220
  return teamId;
3183
3221
  }
3222
+ /**
3223
+ * A team the caller sees: its rows (each with its effective capabilities),
3224
+ * children, apps, the package it is the app team of and its default
3225
+ * capabilities.
3226
+ */
3184
3227
  getTeam(org, team) {
3185
3228
  return this.get(`orgs/${seg(org)}/teams/${seg(team)}`);
3186
3229
  }
@@ -3222,6 +3265,48 @@ var CalimeroClient = class _CalimeroClient {
3222
3265
  redeemInvite(token) {
3223
3266
  return this.post(`invites/${seg(token)}/redeem`);
3224
3267
  }
3268
+ /**
3269
+ * The organization's pending invites the caller may see (never a token):
3270
+ * every one for an owner or admin, their own, and those naming only teams
3271
+ * they manage. Expired ones are listed (`expired: true`) until revoked. A
3272
+ * caller outside the organization: {@link NotFound}.
3273
+ */
3274
+ async listInvites(org) {
3275
+ const { invites } = await this.get(`orgs/${seg(org)}/invites`);
3276
+ return invites;
3277
+ }
3278
+ /**
3279
+ * Withdraw an invite by its key ({@link listInvites}), approved with the
3280
+ * caller's `passkey` (`__revoke_invite { inviteKey }`). One the caller may
3281
+ * not see: {@link NotFound}.
3282
+ */
3283
+ async revokeInvite(org, inviteKey, passkey) {
3284
+ await this.deleteConfirmed(`orgs/${seg(org)}/invites/${seg(inviteKey)}`, passkey);
3285
+ }
3286
+ // ---------------------------------------------------------------- app teams
3287
+ /**
3288
+ * The organization's mode and its app teams (one per package), for any
3289
+ * member; a guest sees only those they are in. A caller outside the
3290
+ * organization: {@link NotFound}.
3291
+ */
3292
+ listAppTeams(org) {
3293
+ return this.get(`orgs/${seg(org)}/app-teams`);
3294
+ }
3295
+ /**
3296
+ * Give apps their own access lists in one step (owner or admin): find or
3297
+ * make each package's app team, add everyone who reaches its instances now,
3298
+ * and move those instances onto it; with `shared`, close every app team.
3299
+ * Idempotent per package. Approved with the caller's `passkey`
3300
+ * (`__scope_apps { apps, shared }`); a refusal is `400 REJECTED` with the
3301
+ * node's reason. For a fresh standalone install pass
3302
+ * `{ apps: [{ package, name, instances: [] }] }`.
3303
+ */
3304
+ scopeApps(org, passkey, options = {}) {
3305
+ const body = {};
3306
+ if (options.apps !== void 0) body.apps = options.apps;
3307
+ if (options.shared !== void 0) body.shared = options.shared;
3308
+ return this.postConfirmed(`orgs/${seg(org)}/app-teams`, body, passkey);
3309
+ }
3225
3310
  // ---------------------------------------------------------------- deployers
3226
3311
  /**
3227
3312
  * Let an account or organization install apps where the cluster's