@calimero-network/mero-js 29.0.0 → 29.2.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/README.md CHANGED
@@ -98,6 +98,7 @@ Pinned measurements go through `policy.verifyQuote` (`createQuoteVerifier`).
98
98
  ## The API
99
99
 
100
100
  - **Credentials**: `signUp`, `signIn`, `refresh`, `signOut`, `signOutEverywhere`, `useSession`, `useApiKey`, `me`, `usage`, `addPasskey`, `labelPasskey`, `removePasskey`, `createApiKey`, `listApiKeys`, `revokeApiKey`. Adding or removing a passkey and creating an API key are approved with a passkey the account already has.
101
+ - **Package origins**: `packageOrigins.list`, `packageOrigins.add`, `packageOrigins.remove`. A package's managers register the web origins of a frontend they host themselves (approved with a passkey); `signUp` and `signIn` on such a page send its origin, sign for that origin's relying-party id, and open a session bound to the package (`me()` answers its `app` and `origin`). An unregistered origin is refused with `OriginNotRegistered`.
101
102
  - **Packages**: `installPackage`, `uploadPackage`, `packages`, `package`, `uninstallPackage`; `uploadBundle` (raw WASM, development clusters only).
102
103
  - **Apps**: `createApp`, `listApps`, `getApp`, `deleteApp`, `setAccess`, `setAppMetadata`, `appStorage`, `appAbi`, `upgradeApp`, `transferApp`.
103
104
  - **Calls**: `call`, `callConfirmed`, `view`, `events`. A call always carries an idempotency key, and is sent again with it while the node answers `503`, then to the next node.
@@ -110,7 +111,7 @@ Pinned measurements go through `policy.verifyQuote` (`createQuoteVerifier`).
110
111
 
111
112
  Refusals are typed (`LoginRequired`, `WrongNode`, `NotFound`, `ConfirmRequired`, `ConfirmCancelled`,
112
113
  `ArgsTooLarge`, `IdempotencyMismatch`, `ReleaseDeadlineError`, `ConsentStale`, `AppError`, `TrapError`,
113
- `UnavailableError`),
114
+ `UnavailableError`, `OriginNotRegistered`, `BadOrigin`, `OriginTaken`, `QuotaExceeded`),
114
115
  all extending `CalimeroError` with the node's `status` and `code`. A `401` once
115
116
  the node that issued the session is replaced or out of reach is answered by one
116
117
  refresh and a repeat; a `421 WRONG_NODE` by moving to the next of the `nodes` the client was
@@ -12,7 +12,7 @@
12
12
  import { type AttestationDoc, type CloudPolicy } from './attestation.js';
13
13
  import { type PasskeyProvider } from './passkey.js';
14
14
  import { type AppsReport, type ConsentRequest, type ConsentResult, type ConsentStarted, type OpenConsentOptions, type OpsAppsReport, type OpsOrgsReport, type PageOptions, type PendingApprovals, type PublisherBindingInfo, type PublisherProof, type ReleaseRules } from './approvals.js';
15
- export { AppError, ArgsTooLarge, CalimeroError, ConfirmCancelled, ConfirmRequired, IdempotencyMismatch, LoginRequired, ConsentStale, NotFound, ReleaseDeadlineError, TrapError, UnavailableError, WrongNode, errorFrom, } from './errors.js';
15
+ export { AppError, ArgsTooLarge, BadOrigin, CalimeroError, ConfirmCancelled, ConfirmRequired, IdempotencyMismatch, LoginRequired, ConsentStale, NotFound, OriginNotRegistered, OriginTaken, QuotaExceeded, ReleaseDeadlineError, TrapError, UnavailableError, WrongNode, errorFrom, } from './errors.js';
16
16
  export type { Committed } from './errors.js';
17
17
  export interface CalimeroSession {
18
18
  accountId: string;
@@ -271,6 +271,73 @@ export declare function frontendManifest(origin?: string, fetcher?: typeof fetch
271
271
  * not opened through a deployment. `href` defaults to `window.location.href`.
272
272
  */
273
273
  export declare function pageDeployment(href?: string): string | null;
274
+ /** `GET /auth/me`: who the credential is. */
275
+ export interface MeInfo extends Record<string, unknown> {
276
+ accountId?: string;
277
+ /** `session` or `apikey`. */
278
+ via?: string;
279
+ /**
280
+ * The package the session is bound to: opened on an app's frontend or on
281
+ * an origin its manager registered ({@link CalimeroClient.packageOrigins}).
282
+ * `null` (or absent, on an older node) for an unbound session.
283
+ */
284
+ app?: string | null;
285
+ /** The web origin the session was opened on, when the node recorded one. */
286
+ origin?: string | null;
287
+ }
288
+ /** One web origin registered for a package (`GET /package-origins/{package}`). */
289
+ export interface PackageOrigin {
290
+ /** `scheme://host[:port]`. */
291
+ origin: string;
292
+ /** The WebAuthn relying-party id passkeys made there are bound to. */
293
+ rpId: string;
294
+ /** The account that registered it. */
295
+ addedBy: string;
296
+ addedAt: string | number;
297
+ }
298
+ /** What registering an origin answers. */
299
+ export interface PackageOriginAdded extends PackageOrigin {
300
+ package: string;
301
+ }
302
+ /** What removing an origin answers. */
303
+ export interface PackageOriginRemoved {
304
+ package: string;
305
+ origin: string;
306
+ removed: true;
307
+ }
308
+ /**
309
+ * The web origins a package's frontend may sign users in from besides the
310
+ * cluster's own, so a developer can host it themselves. A session opened on
311
+ * one is bound to the package. For the package's managers.
312
+ */
313
+ export interface PackageOrigins {
314
+ /** The origins registered for `pkg`. `NotFound` for an unknown package; `403` to anyone not managing it. */
315
+ list(pkg: string): Promise<PackageOrigin[]>;
316
+ /**
317
+ * Register `origin` for `pkg`, approved with `passkey` (`412
318
+ * CONFIRM_REQUIRED` over `__add_origin`; without a passkey it reaches the
319
+ * caller as {@link ConfirmRequired}). `rpId` defaults to the node's choice
320
+ * for the origin (its host). Refusals: {@link BadOrigin} (`BAD_ORIGIN`,
321
+ * `BAD_RP_ID`), {@link OriginTaken}, {@link QuotaExceeded}.
322
+ */
323
+ add(pkg: string, origin: string, passkey?: PasskeyProvider, options?: {
324
+ rpId?: string;
325
+ }): Promise<PackageOriginAdded>;
326
+ /**
327
+ * Remove `origin` from `pkg`, approved with `passkey` (`__remove_origin`).
328
+ * Sessions opened there stop working (`401`). `NotFound` if it was not registered.
329
+ */
330
+ remove(pkg: string, origin: string, passkey?: PasskeyProvider): Promise<PackageOriginRemoved>;
331
+ }
332
+ /**
333
+ * Where a sign-up or sign-in says it runs. By default the page's origin
334
+ * (`window.location.origin`) when it is not the origin of a node the client
335
+ * talks to; nothing outside a browser. A string sends that origin; `null`
336
+ * sends none (the cluster's own origins).
337
+ */
338
+ export interface SignInOptions {
339
+ origin?: string | null;
340
+ }
274
341
  /**
275
342
  * How a call or a view is sent again while the node is unavailable (D70): with
276
343
  * the same key, after a backoff that starts at `baseDelayMs` and doubles up to
@@ -447,12 +514,29 @@ export declare class CalimeroClient {
447
514
  delete<T = Record<string, unknown>>(path: string, body?: unknown): Promise<T>;
448
515
  /**
449
516
  * Create an account with a new passkey, and sign in. The node then asks the
450
- * new passkey to sign the account it creates (`412 CONFIRM_REQUIRED`, core
451
- * #proofs-at-apply-rest), so a sign-up prompts twice: create, then sign.
517
+ * new passkey to sign the account it creates (`412 CONFIRM_REQUIRED`), so a
518
+ * sign-up prompts twice: create, then sign. On a page whose origin is not
519
+ * the cluster's ({@link SignInOptions}), the node makes the passkey for that
520
+ * origin's relying party (the confirmation's `rpId` is that origin's too)
521
+ * and binds the session to the package the origin is registered for; an
522
+ * unregistered origin is refused with {@link OriginNotRegistered}.
523
+ */
524
+ signUp(passkey: PasskeyProvider, name: string, options?: SignInOptions): Promise<CalimeroSession>;
525
+ /**
526
+ * Sign in with an existing passkey. On a page whose origin is not the
527
+ * cluster's ({@link SignInOptions}), the assertion is made for that
528
+ * origin's relying-party id, as the node answers it, and the session is
529
+ * bound to the origin's package; an unregistered origin is refused with
530
+ * {@link OriginNotRegistered}.
452
531
  */
453
- signUp(passkey: PasskeyProvider, name: string): Promise<CalimeroSession>;
454
- /** Sign in with an existing passkey. */
455
- signIn(passkey: PasskeyProvider): Promise<CalimeroSession>;
532
+ signIn(passkey: PasskeyProvider, options?: SignInOptions): Promise<CalimeroSession>;
533
+ /**
534
+ * The origin a sign-up or sign-in sends: the one given, or the page's own
535
+ * when it is not a node's origin; `undefined` for none.
536
+ */
537
+ private pageOrigin;
538
+ /** POST a ceremony's `begin`, naming the page's `origin` in an {@link OriginNotRegistered} the node leaves it out of. */
539
+ private begin;
456
540
  /**
457
541
  * Swap the refresh token for a new session, on whichever node the client
458
542
  * uses: any node of the cluster takes it. A refresh token is used once, so
@@ -464,7 +548,8 @@ export declare class CalimeroClient {
464
548
  signOut(): Promise<void>;
465
549
  /** End every session of the signed-in account, on every device, this one too. */
466
550
  signOutEverywhere(): Promise<void>;
467
- me(): Promise<Record<string, unknown>>;
551
+ /** Who the credential is: the account, how it signed in, and the package (`app`) and `origin` a session is bound to. */
552
+ me(): Promise<MeInfo>;
468
553
  /**
469
554
  * Add another passkey (another device, `passkey`) to the signed-in account;
470
555
  * answers its credential id. The node asks for the approval of a passkey
@@ -491,6 +576,12 @@ export declare class CalimeroClient {
491
576
  * Create an API key for an agent or server, approved with the account's
492
577
  * `passkey` (`412 CONFIRM_REQUIRED`); needs a passkey session (an API key
493
578
  * is refused `403`). The key is returned once.
579
+ *
580
+ * `upgrades: { app, package? }` makes an upgrade key, for CI: it upgrades
581
+ * that one app to signed releases of its package (default: the one the app
582
+ * runs, by that package's pinned signer) with {@link upgradeApp} and no
583
+ * passkey, and every other request with it is refused
584
+ * `403 UPGRADE_KEY_ONLY`. It takes no other scope field.
494
585
  */
495
586
  createApiKey(options: {
496
587
  label?: string;
@@ -499,6 +590,10 @@ export declare class CalimeroClient {
499
590
  readOnly?: boolean;
500
591
  allowConfirm?: boolean;
501
592
  ttlSecs?: number;
593
+ upgrades?: {
594
+ app: string;
595
+ package?: string;
596
+ };
502
597
  }, passkey: PasskeyProvider): Promise<{
503
598
  keyId: string;
504
599
  key: string;
@@ -588,6 +683,8 @@ export declare class CalimeroClient {
588
683
  * `passkey` gives. Refused (`403`) to a session opened on an app's
589
684
  * frontend: an app asks its user with {@link requestConsent} instead. Past
590
685
  * a release deadline its approver has not met: {@link ReleaseDeadlineError}.
686
+ * With an upgrade key ({@link createApiKey} `upgrades`) pass no passkey and
687
+ * a `version` of the key's package: the key's own proof stands in for it.
591
688
  */
592
689
  upgradeApp(app: string, target: {
593
690
  version?: string;
@@ -861,6 +958,12 @@ export declare class CalimeroClient {
861
958
  uses?: number;
862
959
  }): Promise<string>;
863
960
  redeemInvite(token: string): Promise<Record<string, unknown>>;
961
+ /**
962
+ * The web origins `{package}`'s managers registered so a frontend they host
963
+ * themselves can sign users in with passkeys ({@link PackageOrigins}).
964
+ * Adding and removing one is approved with a passkey.
965
+ */
966
+ readonly packageOrigins: PackageOrigins;
864
967
  /**
865
968
  * Let an account or organization install apps where the cluster's
866
969
  * deployments are `granted`, until `until` (RFC 3339 or a `Date`; absent,
@@ -12,10 +12,10 @@
12
12
  import { SealedTransportError, createSealedFetch } from '../sealed/sealed.js';
13
13
  import { fetchAttestedKey } from './attestation.js';
14
14
  import { DEFAULT_DIRECTORY, orderNodes, readDirectory } from './directory.js';
15
- import { CalimeroError, ConfirmCancelled, ConfirmRequired, LoginRequired, UnavailableError, WrongNode, errorFrom, } from './errors.js';
15
+ import { CalimeroError, ConfirmCancelled, ConfirmRequired, LoginRequired, OriginNotRegistered, UnavailableError, WrongNode, errorFrom, } from './errors.js';
16
16
  import { b64url, fromB64url } from './passkey.js';
17
17
  import { confirmationCode, pageQuery, pendingApprovalsFrom, } from './approvals.js';
18
- export { AppError, ArgsTooLarge, CalimeroError, ConfirmCancelled, ConfirmRequired, IdempotencyMismatch, LoginRequired, ConsentStale, NotFound, ReleaseDeadlineError, TrapError, UnavailableError, WrongNode, errorFrom, } from './errors.js';
18
+ export { AppError, ArgsTooLarge, BadOrigin, CalimeroError, ConfirmCancelled, ConfirmRequired, IdempotencyMismatch, LoginRequired, ConsentStale, NotFound, OriginNotRegistered, OriginTaken, QuotaExceeded, ReleaseDeadlineError, TrapError, UnavailableError, WrongNode, errorFrom, } from './errors.js';
19
19
  /** A deployer's key, as `GET /deployers` lists it and `DELETE /deployers/{key}` takes it. */
20
20
  export function deployerKey(deployer) {
21
21
  return 'account' in deployer ? `account:${deployer.account}` : `org:${deployer.org}`;
@@ -186,6 +186,20 @@ export class CalimeroClient {
186
186
  this.channels = new Map();
187
187
  /** Rejections of the underlying `fetch` itself: the node could not be reached or dropped the connection. */
188
188
  this.networkFailures = new WeakSet();
189
+ // ---------------------------------------------------------------- package origins
190
+ /**
191
+ * The web origins `{package}`'s managers registered so a frontend they host
192
+ * themselves can sign users in with passkeys ({@link PackageOrigins}).
193
+ * Adding and removing one is approved with a passkey.
194
+ */
195
+ this.packageOrigins = {
196
+ list: async (pkg) => {
197
+ const { origins } = await this.get(`package-origins/${seg(pkg)}`);
198
+ return origins ?? [];
199
+ },
200
+ add: (pkg, origin, passkey, options = {}) => this.confirmed('POST', `package-origins/${seg(pkg)}`, options.rpId === undefined ? { origin } : { origin, rpId: options.rpId }, passkey),
201
+ remove: (pkg, origin, passkey) => this.confirmed('DELETE', `package-origins/${seg(pkg)}`, { origin }, passkey),
202
+ };
189
203
  const trim = (url) => url.replace(/\/+$/, '');
190
204
  this.nodeUrls = [...new Set([options.baseUrl, ...(options.nodes ?? [])].map(trim))];
191
205
  this.policy = options.policy;
@@ -442,20 +456,33 @@ export class CalimeroClient {
442
456
  // ---------------------------------------------------------------- identity
443
457
  /**
444
458
  * Create an account with a new passkey, and sign in. The node then asks the
445
- * new passkey to sign the account it creates (`412 CONFIRM_REQUIRED`, core
446
- * #proofs-at-apply-rest), so a sign-up prompts twice: create, then sign.
459
+ * new passkey to sign the account it creates (`412 CONFIRM_REQUIRED`), so a
460
+ * sign-up prompts twice: create, then sign. On a page whose origin is not
461
+ * the cluster's ({@link SignInOptions}), the node makes the passkey for that
462
+ * origin's relying party (the confirmation's `rpId` is that origin's too)
463
+ * and binds the session to the package the origin is registered for; an
464
+ * unregistered origin is refused with {@link OriginNotRegistered}.
447
465
  */
448
- async signUp(passkey, name) {
449
- const begin = await this.post('auth/passkey/register/begin', { name });
466
+ async signUp(passkey, name, options = {}) {
467
+ const origin = this.pageOrigin(options);
468
+ const begin = await this.begin('auth/passkey/register/begin', origin === undefined ? { name } : { name, origin }, origin);
450
469
  const registration = await passkey.create(begin.options);
451
470
  const session = await this.postConfirmed('auth/passkey/register/finish', { challengeId: begin.challengeId, registration }, passkey);
452
471
  this.useSession(session);
453
472
  return session;
454
473
  }
455
- /** Sign in with an existing passkey. */
456
- async signIn(passkey) {
457
- const begin = await this.post('auth/passkey/login/begin');
458
- const assertion = await passkey.get(fromB64url(begin.options.challenge), begin.options.rpId);
474
+ /**
475
+ * Sign in with an existing passkey. On a page whose origin is not the
476
+ * cluster's ({@link SignInOptions}), the assertion is made for that
477
+ * origin's relying-party id, as the node answers it, and the session is
478
+ * bound to the origin's package; an unregistered origin is refused with
479
+ * {@link OriginNotRegistered}.
480
+ */
481
+ async signIn(passkey, options = {}) {
482
+ const origin = this.pageOrigin(options);
483
+ const begin = await this.begin('auth/passkey/login/begin', origin === undefined ? undefined : { origin }, origin);
484
+ const rpId = begin.options.rpId ?? begin.options.rp?.id ?? '';
485
+ const assertion = await passkey.get(fromB64url(begin.options.challenge), rpId);
459
486
  const session = await this.post('auth/passkey/login/finish', {
460
487
  challengeId: begin.challengeId,
461
488
  assertion,
@@ -463,6 +490,38 @@ export class CalimeroClient {
463
490
  this.useSession(session);
464
491
  return session;
465
492
  }
493
+ /**
494
+ * The origin a sign-up or sign-in sends: the one given, or the page's own
495
+ * when it is not a node's origin; `undefined` for none.
496
+ */
497
+ pageOrigin(options) {
498
+ if (options.origin !== undefined)
499
+ return options.origin ?? undefined;
500
+ const page = globalThis.location?.origin;
501
+ if (!page || page === 'null')
502
+ return undefined;
503
+ const nodes = new Set(this.nodeUrls.map((url) => {
504
+ try {
505
+ return new URL(url).origin;
506
+ }
507
+ catch {
508
+ return url;
509
+ }
510
+ }));
511
+ return nodes.has(page) ? undefined : page;
512
+ }
513
+ /** POST a ceremony's `begin`, naming the page's `origin` in an {@link OriginNotRegistered} the node leaves it out of. */
514
+ async begin(path, body, origin) {
515
+ try {
516
+ return await this.post(path, body);
517
+ }
518
+ catch (error) {
519
+ if (error instanceof OriginNotRegistered && !error.origin && origin !== undefined) {
520
+ throw new OriginNotRegistered(error.nodeMessage, { ...error.details, origin });
521
+ }
522
+ throw error;
523
+ }
524
+ }
466
525
  /**
467
526
  * Swap the refresh token for a new session, on whichever node the client
468
527
  * uses: any node of the cluster takes it. A refresh token is used once, so
@@ -514,6 +573,7 @@ export class CalimeroClient {
514
573
  this.auth = undefined;
515
574
  this.current = undefined;
516
575
  }
576
+ /** Who the credential is: the account, how it signed in, and the package (`app`) and `origin` a session is bound to. */
517
577
  me() {
518
578
  return this.get('auth/me');
519
579
  }
@@ -565,6 +625,12 @@ export class CalimeroClient {
565
625
  * Create an API key for an agent or server, approved with the account's
566
626
  * `passkey` (`412 CONFIRM_REQUIRED`); needs a passkey session (an API key
567
627
  * is refused `403`). The key is returned once.
628
+ *
629
+ * `upgrades: { app, package? }` makes an upgrade key, for CI: it upgrades
630
+ * that one app to signed releases of its package (default: the one the app
631
+ * runs, by that package's pinned signer) with {@link upgradeApp} and no
632
+ * passkey, and every other request with it is refused
633
+ * `403 UPGRADE_KEY_ONLY`. It takes no other scope field.
568
634
  */
569
635
  createApiKey(options, passkey) {
570
636
  return this.postConfirmed('auth/apikeys', { ...options }, passkey);
@@ -706,6 +772,8 @@ export class CalimeroClient {
706
772
  * `passkey` gives. Refused (`403`) to a session opened on an app's
707
773
  * frontend: an app asks its user with {@link requestConsent} instead. Past
708
774
  * a release deadline its approver has not met: {@link ReleaseDeadlineError}.
775
+ * With an upgrade key ({@link createApiKey} `upgrades`) pass no passkey and
776
+ * a `version` of the key's package: the key's own proof stands in for it.
709
777
  */
710
778
  upgradeApp(app, target, passkey) {
711
779
  const path = `apps/${seg(app)}/upgrade`;
@@ -100,6 +100,36 @@ export declare class ReleaseDeadlineError extends CalimeroError {
100
100
  export declare class ConsentStale extends CalimeroError {
101
101
  constructor(message: string, details?: Record<string, unknown>);
102
102
  }
103
+ /**
104
+ * `403 ORIGIN_NOT_REGISTERED`: a sign-up or sign-in started on a web origin
105
+ * the cluster does not know: neither the cluster's own nor one a package's
106
+ * manager registered (`packageOrigins.add`). `origin` is the page's origin
107
+ * and `package` the package it was expected to be registered for, when known.
108
+ */
109
+ export declare class OriginNotRegistered extends CalimeroError {
110
+ readonly origin: string;
111
+ readonly package: string;
112
+ /** What the node said, if anything. */
113
+ readonly nodeMessage: string;
114
+ constructor(message: string, details?: Record<string, unknown>);
115
+ }
116
+ /**
117
+ * `400 BAD_ORIGIN` or `400 BAD_RP_ID`: the node does not accept the origin a
118
+ * package's manager tried to register (it must be a bare web origin,
119
+ * `scheme://host[:port]`), or the relying-party id given for it (which must
120
+ * fit the origin's host). `code` says which.
121
+ */
122
+ export declare class BadOrigin extends CalimeroError {
123
+ constructor(code: 'BAD_ORIGIN' | 'BAD_RP_ID', message: string, details?: Record<string, unknown>);
124
+ }
125
+ /** `409 ORIGIN_TAKEN`: the origin is registered already (for this package or another). */
126
+ export declare class OriginTaken extends CalimeroError {
127
+ constructor(message: string, details?: Record<string, unknown>);
128
+ }
129
+ /** `429 QUOTA_EXCEEDED`: the caller holds as many of something (origins, blobs, ...) as it may. */
130
+ export declare class QuotaExceeded extends CalimeroError {
131
+ constructor(message: string, details?: Record<string, unknown>);
132
+ }
103
133
  /**
104
134
  * `422 APP_ERROR`: the app refused the call. `appCode` is the app's declared
105
135
  * error (`Taken`, `Forbidden`, ...) and `fields` its named fields. The call was
@@ -128,6 +128,50 @@ export class ConsentStale extends CalimeroError {
128
128
  this.name = 'ConsentStale';
129
129
  }
130
130
  }
131
+ /**
132
+ * `403 ORIGIN_NOT_REGISTERED`: a sign-up or sign-in started on a web origin
133
+ * the cluster does not know: neither the cluster's own nor one a package's
134
+ * manager registered (`packageOrigins.add`). `origin` is the page's origin
135
+ * and `package` the package it was expected to be registered for, when known.
136
+ */
137
+ export class OriginNotRegistered extends CalimeroError {
138
+ constructor(message, details = {}) {
139
+ const origin = typeof details.origin === 'string' ? details.origin : '';
140
+ const pkg = typeof details.package === 'string' ? details.package : '';
141
+ const site = origin ? `this site (${origin})` : 'this site';
142
+ super(403, 'ORIGIN_NOT_REGISTERED', `${site} is not registered for ${pkg || 'any package'}${message ? ` (${message})` : ''}`, details);
143
+ this.name = 'OriginNotRegistered';
144
+ this.origin = origin;
145
+ this.package = pkg;
146
+ this.nodeMessage = message;
147
+ }
148
+ }
149
+ /**
150
+ * `400 BAD_ORIGIN` or `400 BAD_RP_ID`: the node does not accept the origin a
151
+ * package's manager tried to register (it must be a bare web origin,
152
+ * `scheme://host[:port]`), or the relying-party id given for it (which must
153
+ * fit the origin's host). `code` says which.
154
+ */
155
+ export class BadOrigin extends CalimeroError {
156
+ constructor(code, message, details = {}) {
157
+ super(400, code, message, details);
158
+ this.name = 'BadOrigin';
159
+ }
160
+ }
161
+ /** `409 ORIGIN_TAKEN`: the origin is registered already (for this package or another). */
162
+ export class OriginTaken extends CalimeroError {
163
+ constructor(message, details = {}) {
164
+ super(409, 'ORIGIN_TAKEN', message, details);
165
+ this.name = 'OriginTaken';
166
+ }
167
+ }
168
+ /** `429 QUOTA_EXCEEDED`: the caller holds as many of something (origins, blobs, ...) as it may. */
169
+ export class QuotaExceeded extends CalimeroError {
170
+ constructor(message, details = {}) {
171
+ super(429, 'QUOTA_EXCEEDED', message, details);
172
+ this.name = 'QuotaExceeded';
173
+ }
174
+ }
131
175
  /**
132
176
  * `422 APP_ERROR`: the app refused the call. `appCode` is the app's declared
133
177
  * error (`Taken`, `Forbidden`, ...) and `fields` its named fields. The call was
@@ -212,6 +256,15 @@ export function errorFrom(status, body, retryAfterHeader) {
212
256
  return new ReleaseDeadlineError(message, details);
213
257
  case '409 CONSENT_STALE':
214
258
  return new ConsentStale(message, details);
259
+ case '403 ORIGIN_NOT_REGISTERED':
260
+ return new OriginNotRegistered(message, details);
261
+ case '400 BAD_ORIGIN':
262
+ case '400 BAD_RP_ID':
263
+ return new BadOrigin(code, message, details);
264
+ case '409 ORIGIN_TAKEN':
265
+ return new OriginTaken(message, details);
266
+ case '429 QUOTA_EXCEEDED':
267
+ return new QuotaExceeded(message, details);
215
268
  case '412 CONFIRM_REQUIRED':
216
269
  return new ConfirmRequired(message, details);
217
270
  case '413 ARGS_TOO_LARGE':
@@ -1,9 +1,9 @@
1
1
  export { CalimeroClient, deployerKey, frontendManifest, pageDeployment } from './client.js';
2
2
  export { DEFAULT_DIRECTORY, DirectoryError } from './directory.js';
3
3
  export type { DirectoryFailure } from './directory.js';
4
- export { AppError, ArgsTooLarge, CalimeroError, ConfirmCancelled, ConfirmRequired, IdempotencyMismatch, LoginRequired, ConsentStale, NotFound, ReleaseDeadlineError, TrapError, UnavailableError, WrongNode, errorFrom, } from './errors.js';
4
+ export { AppError, ArgsTooLarge, BadOrigin, CalimeroError, ConfirmCancelled, ConfirmRequired, IdempotencyMismatch, LoginRequired, ConsentStale, NotFound, OriginNotRegistered, OriginTaken, QuotaExceeded, ReleaseDeadlineError, TrapError, UnavailableError, WrongNode, errorFrom, } from './errors.js';
5
5
  export type { Committed } from './errors.js';
6
- export type { AppInfo, AppXcalls, BlobContent, BlobInfo, DeadLetter, Deployer, DeployerGrant, DeployersInfo, DeploymentInfo, DeploymentOffer, DeploymentOrgs, FrontendManifest, PublicDeployment, CallResult, CalimeroClientOptions, CalimeroConnectOptions, CloudEvent, CalimeroSession, PackageInfo, PackageInstalled, CloudRetryOptions, OrgInfo, OrgUpgradeTarget, PresenceEntry, UpgradeRun, ViewResult, } from './client.js';
6
+ export type { AppInfo, AppXcalls, BlobContent, BlobInfo, DeadLetter, Deployer, DeployerGrant, DeployersInfo, DeploymentInfo, DeploymentOffer, DeploymentOrgs, FrontendManifest, MeInfo, PackageOrigin, PackageOriginAdded, PackageOriginRemoved, PackageOrigins, SignInOptions, PublicDeployment, CallResult, CalimeroClientOptions, CalimeroConnectOptions, CloudEvent, CalimeroSession, PackageInfo, PackageInstalled, CloudRetryOptions, OrgInfo, OrgUpgradeTarget, PresenceEntry, UpgradeRun, ViewResult, } from './client.js';
7
7
  export { claimedRelease, fetchAttestedKey, freshCollateral, MOCK_QUOTE_HEADER } from './attestation.js';
8
8
  export type { AttestationDoc, CloudPolicy, CloudReleasePolicy } from './attestation.js';
9
9
  export { browserPasskey, b64url, fromB64url } from './passkey.js';
@@ -1,6 +1,6 @@
1
1
  export { CalimeroClient, deployerKey, frontendManifest, pageDeployment } from './client.js';
2
2
  export { DEFAULT_DIRECTORY, DirectoryError } from './directory.js';
3
- export { AppError, ArgsTooLarge, CalimeroError, ConfirmCancelled, ConfirmRequired, IdempotencyMismatch, LoginRequired, ConsentStale, NotFound, ReleaseDeadlineError, TrapError, UnavailableError, WrongNode, errorFrom, } from './errors.js';
3
+ export { AppError, ArgsTooLarge, BadOrigin, CalimeroError, ConfirmCancelled, ConfirmRequired, IdempotencyMismatch, LoginRequired, ConsentStale, NotFound, OriginNotRegistered, OriginTaken, QuotaExceeded, ReleaseDeadlineError, TrapError, UnavailableError, WrongNode, errorFrom, } from './errors.js';
4
4
  export { claimedRelease, fetchAttestedKey, freshCollateral, MOCK_QUOTE_HEADER } from './attestation.js';
5
5
  export { browserPasskey, b64url, fromB64url } from './passkey.js';
6
6
  export { MAX_REPORT_PAGE, RELEASE_VERIFIER, compareVersions, confirmationCode, consentReturn, pageQuery, pendingApprovalsFrom, publisherBindingBytes, publisherBindingMessage, releaseConsent, releaseNotesUrl, stalledBy, upgradeConsent, verifyReleaseUrl, withdrawConsent, } from './approvals.js';