@calimero-network/mero-js 29.5.0 → 29.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/README.md CHANGED
@@ -99,7 +99,7 @@ Pinned measurements go through `policy.verifyQuote` (`createQuoteVerifier`).
99
99
 
100
100
  - **Credentials**: `signUp` (with an `invite`, on a cluster that takes new accounts by invite; `signUpPolicy` reads which), `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
101
  - **Account recovery** (core #4844): `createRecoveryCode`, `removeRecoveryCode` (passkey-approved; the code is shown once), then signed out `startAccountRecovery(code, passkey)` and, 72 hours later, `finishAccountRecovery(passkey, accountId?)`; a passkey the account still has cancels in between (`cancelAccountRecovery`; `me()` shows `recoveryCode` and `recoveryPending`).
102
- - **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`.
102
+ - **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`. `packageOrigins.add(pkg, origin, passkey, { served: true })` registers a custom domain: the cluster also serves the package's frontend there from its signed bundle (`https://host`, no port; the passkey's consent names `served: true`); `list` answers `served` on each origin, and the extra refusals are `NotServing` (the cluster serves no frontends), `ClusterNotUpgraded` (a node is on an older release), `QuotaExceeded` (4 served per package, 64 per cluster) and `OriginTaken` (held with the other `served`).
103
103
  - **Packages**: `installPackage`, `uploadPackage`, `packages`, `package`, `uninstallPackage`; `uploadBundle` (raw WASM, development clusters only).
104
104
  - **Apps**: `createApp`, `listApps`, `getApp`, `deleteApp`, `setAccess`, `setAppMetadata`, `appStorage`, `appAbi`, `upgradeApp`, `transferApp`.
105
105
  - **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.
@@ -107,7 +107,7 @@ Pinned measurements go through `policy.verifyQuote` (`createQuoteVerifier`).
107
107
  - **Organizations**: `createOrg`, `getOrg`, `deleteOrg`, `setOrgMetadata`, `setMember`, `removeMember`, `setMemberMetadata`, `setCapabilities` (`ORG_CAPABILITIES`), teams, `invite`, `redeemInvite`, recovery (`setRecoveryKey`, `startRecovery`, `cancelRecovery`, `finishRecovery`; `newRecoverySeed`, `recoveryPublicKey`). Setting or removing a member or team member, capabilities and invites are approved with the caller's passkey; redeeming an invite is not.
108
108
  - **Owner decisions** (passkey approval, never from an app's frontend): `upgradeOrg`, `upgradeRun`, `retryUpgradeRun`, `approveRelease`, `approveOutbound`, `bindPublisher`, `upgradePublished`.
109
109
  - **Approvals from an app**: `appsReport`, `releaseRules`, `pendingApprovals`, then `requestConsent` / `openConsentPage` to send the user to the node's consent page, and `consentResult`. Show the answer's `code` to the user before sending them to `url` (`openConsentPage`'s `confirm` hook); they should see the same code on the page (`confirmationCode(requestId)` checks it). See [Ask your users to approve node images and app upgrades](https://calimero-network.github.io/mero-js/guides/user-approvals/).
110
- - **Operator** (ops token): `opsApps`, `opsOrgs`, `setReleaseDeadline`, `liftReleaseDeadline`.
110
+ - **Operator** (ops token): `opsApps`, `opsOrgs`, `opsServedOrigins` (every custom domain the log holds, by package label), `setReleaseDeadline`, `liftReleaseDeadline`.
111
111
  - **Any route**: `get`, `post`, `put`, `delete`; `postConfirmed` and `deleteConfirmed` answer a `412 CONFIRM_REQUIRED` with a passkey.
112
112
 
113
113
  Refusals are typed (`LoginRequired`, `WrongNode`, `NotFound`, `ConfirmRequired`, `ConfirmCancelled`,
@@ -331,6 +331,12 @@ export interface PackageOrigin {
331
331
  /** The account that registered it. */
332
332
  addedBy: string;
333
333
  addedAt: string | number;
334
+ /**
335
+ * The cluster serves the package's frontend on this origin too (a custom
336
+ * domain, from its signed bundle). Absent (`false`) from a node that
337
+ * predates served origins.
338
+ */
339
+ served?: boolean;
334
340
  }
335
341
  /** What registering an origin answers. */
336
342
  export interface PackageOriginAdded extends PackageOrigin {
@@ -356,9 +362,21 @@ export interface PackageOrigins {
356
362
  * caller as {@link ConfirmRequired}). `rpId` defaults to the node's choice
357
363
  * for the origin (its host). Refusals: {@link BadOrigin} (`BAD_ORIGIN`,
358
364
  * `BAD_RP_ID`), {@link OriginTaken}, {@link QuotaExceeded}.
365
+ *
366
+ * `served: true` makes it a custom domain: the cluster also serves the
367
+ * package's frontend on it (`https://host`, no port; never a name under the
368
+ * apps domain or the cluster's rp-id), and the consent the passkey signs
369
+ * names it (`{ origin, rpId, served: true }`). Further refusals:
370
+ * {@link NotServing} (the cluster serves no frontends),
371
+ * {@link ClusterNotUpgraded} (a node of the cluster is on an older release),
372
+ * {@link QuotaExceeded} for the served limits (4 per package, 64 per
373
+ * cluster), {@link OriginTaken} when the origin is held with the other
374
+ * `served` (remove it first). Serving it to browsers also needs the
375
+ * certificate and route Calimero Cloud sets up for the domain.
359
376
  */
360
377
  add(pkg: string, origin: string, passkey?: PasskeyProvider, options?: {
361
378
  rpId?: string;
379
+ served?: boolean;
362
380
  }): Promise<PackageOriginAdded>;
363
381
  /**
364
382
  * Remove `origin` from `pkg`, approved with `passkey` (`__remove_origin`).
@@ -366,6 +384,22 @@ export interface PackageOrigins {
366
384
  */
367
385
  remove(pkg: string, origin: string, passkey?: PasskeyProvider): Promise<PackageOriginRemoved>;
368
386
  }
387
+ /** One served origin (custom domain) of the cluster, as `GET /ops/served-origins` lists it. */
388
+ export interface ServedOrigin {
389
+ /** `https://host`. */
390
+ origin: string;
391
+ /** The origin's host, the name a certificate and a route are for. */
392
+ host: string;
393
+ /** The package's app-host label (`{label}.{apps domain}`); the listing names no package. */
394
+ label: string;
395
+ addedAt: string;
396
+ }
397
+ /** `GET /ops/served-origins`: every served origin the cluster's log holds. Operator token. */
398
+ export interface ServedOrigins {
399
+ /** This node had caught up with the system leader. */
400
+ fresh: boolean;
401
+ origins: ServedOrigin[];
402
+ }
369
403
  /**
370
404
  * Where a sign-up or sign-in says it runs. By default the page's origin
371
405
  * (`window.location.origin`) when it is not the origin of a node the client
@@ -1035,6 +1069,12 @@ export declare class CalimeroClient {
1035
1069
  opsApps(page?: PageOptions): Promise<OpsAppsReport>;
1036
1070
  /** `GET /ops/orgs`: each organization's approved releases. Operator token. */
1037
1071
  opsOrgs(page?: PageOptions): Promise<OpsOrgsReport>;
1072
+ /**
1073
+ * `GET /ops/served-origins`: every served origin (custom domain) the
1074
+ * cluster's log holds, with its package's label and no package name or
1075
+ * account. Operator token.
1076
+ */
1077
+ opsServedOrigins(): Promise<ServedOrigins>;
1038
1078
  /**
1039
1079
  * Announce when work on images older than `version` ends: from `after` on,
1040
1080
  * a scope that has not approved it (or newer) starts no new work. Operator
@@ -200,7 +200,14 @@ export class CalimeroClient {
200
200
  const { origins } = await this.get(`package-origins/${seg(pkg)}`);
201
201
  return origins ?? [];
202
202
  },
203
- add: (pkg, origin, passkey, options = {}) => this.confirmed('POST', `package-origins/${seg(pkg)}`, options.rpId === undefined ? { origin } : { origin, rpId: options.rpId }, passkey),
203
+ add: (pkg, origin, passkey, options = {}) => this.confirmed('POST', `package-origins/${seg(pkg)}`,
204
+ // Only the fields set: the node refuses unknown ones, and one that
205
+ // predates served origins takes a plain add unchanged.
206
+ {
207
+ origin,
208
+ ...(options.rpId === undefined ? {} : { rpId: options.rpId }),
209
+ ...(options.served ? { served: true } : {}),
210
+ }, passkey),
204
211
  remove: (pkg, origin, passkey) => this.confirmed('DELETE', `package-origins/${seg(pkg)}`, { origin }, passkey),
205
212
  };
206
213
  const trim = (url) => url.replace(/\/+$/, '');
@@ -1241,6 +1248,14 @@ export class CalimeroClient {
1241
1248
  opsOrgs(page = {}) {
1242
1249
  return this.get(`ops/orgs${pageQuery(page)}`);
1243
1250
  }
1251
+ /**
1252
+ * `GET /ops/served-origins`: every served origin (custom domain) the
1253
+ * cluster's log holds, with its package's label and no package name or
1254
+ * account. Operator token.
1255
+ */
1256
+ opsServedOrigins() {
1257
+ return this.get('ops/served-origins');
1258
+ }
1244
1259
  /**
1245
1260
  * Announce when work on images older than `version` ends: from `after` on,
1246
1261
  * a scope that has not approved it (or newer) starts no new work. Operator
@@ -117,15 +117,40 @@ export declare class OriginNotRegistered extends CalimeroError {
117
117
  * `400 BAD_ORIGIN` or `400 BAD_RP_ID`: the node does not accept the origin a
118
118
  * package's manager tried to register (it must be a bare web origin,
119
119
  * `scheme://host[:port]`), or the relying-party id given for it (which must
120
- * fit the origin's host). `code` says which.
120
+ * fit the origin's host). `code` says which. A served origin (`served: true`)
121
+ * must also be `https://host` with no port, and not a name under the
122
+ * cluster's apps domain or its rp-id.
121
123
  */
122
124
  export declare class BadOrigin extends CalimeroError {
123
125
  constructor(code: 'BAD_ORIGIN' | 'BAD_RP_ID', message: string, details?: Record<string, unknown>);
124
126
  }
125
- /** `409 ORIGIN_TAKEN`: the origin is registered already (for this package or another). */
127
+ /**
128
+ * `409 ORIGIN_TAKEN`: the origin is registered already (for another package,
129
+ * or for this one with the other `served`: remove it first), or it is one of
130
+ * the cluster's own (a node's API host, a cluster origin, a frontend it
131
+ * serves).
132
+ */
126
133
  export declare class OriginTaken extends CalimeroError {
127
134
  constructor(message: string, details?: Record<string, unknown>);
128
135
  }
136
+ /**
137
+ * `400 NOT_SERVING`: a served origin (a custom domain, `served: true`) was
138
+ * asked of a cluster that serves no frontends (merod without
139
+ * `--apps-domain`). Register the origin unserved, or use a cluster that
140
+ * serves them.
141
+ */
142
+ export declare class NotServing extends CalimeroError {
143
+ constructor(message: string, details?: Record<string, unknown>);
144
+ }
145
+ /**
146
+ * `409 CLUSTER_NOT_UPGRADED`: the change needs every node of the cluster
147
+ * (replicas, voters and learners) on the node's release or later, and one
148
+ * is not yet; for instance adding a served origin. Retry once the cluster is
149
+ * upgraded.
150
+ */
151
+ export declare class ClusterNotUpgraded extends CalimeroError {
152
+ constructor(message: string, details?: Record<string, unknown>);
153
+ }
129
154
  /**
130
155
  * `409 RECOVERY_NOT_READY`: a recovery (an account's or an organization's)
131
156
  * may finish only once its 72-hour delay has passed. `readyAt` is when it may
@@ -164,7 +189,10 @@ export declare class SignUpRefused extends CalimeroError {
164
189
  readonly reason: SignUpRefusal;
165
190
  constructor(message: string, details?: Record<string, unknown>);
166
191
  }
167
- /** `429 QUOTA_EXCEEDED`: the caller holds as many of something (origins, blobs, ...) as it may. */
192
+ /**
193
+ * `429 QUOTA_EXCEEDED`: the caller holds as many of something (origins,
194
+ * served origins per package or per cluster, blobs, ...) as it may.
195
+ */
168
196
  export declare class QuotaExceeded extends CalimeroError {
169
197
  constructor(message: string, details?: Record<string, unknown>);
170
198
  }
@@ -150,7 +150,9 @@ export class OriginNotRegistered extends CalimeroError {
150
150
  * `400 BAD_ORIGIN` or `400 BAD_RP_ID`: the node does not accept the origin a
151
151
  * package's manager tried to register (it must be a bare web origin,
152
152
  * `scheme://host[:port]`), or the relying-party id given for it (which must
153
- * fit the origin's host). `code` says which.
153
+ * fit the origin's host). `code` says which. A served origin (`served: true`)
154
+ * must also be `https://host` with no port, and not a name under the
155
+ * cluster's apps domain or its rp-id.
154
156
  */
155
157
  export class BadOrigin extends CalimeroError {
156
158
  constructor(code, message, details = {}) {
@@ -158,13 +160,42 @@ export class BadOrigin extends CalimeroError {
158
160
  this.name = 'BadOrigin';
159
161
  }
160
162
  }
161
- /** `409 ORIGIN_TAKEN`: the origin is registered already (for this package or another). */
163
+ /**
164
+ * `409 ORIGIN_TAKEN`: the origin is registered already (for another package,
165
+ * or for this one with the other `served`: remove it first), or it is one of
166
+ * the cluster's own (a node's API host, a cluster origin, a frontend it
167
+ * serves).
168
+ */
162
169
  export class OriginTaken extends CalimeroError {
163
170
  constructor(message, details = {}) {
164
171
  super(409, 'ORIGIN_TAKEN', message, details);
165
172
  this.name = 'OriginTaken';
166
173
  }
167
174
  }
175
+ /**
176
+ * `400 NOT_SERVING`: a served origin (a custom domain, `served: true`) was
177
+ * asked of a cluster that serves no frontends (merod without
178
+ * `--apps-domain`). Register the origin unserved, or use a cluster that
179
+ * serves them.
180
+ */
181
+ export class NotServing extends CalimeroError {
182
+ constructor(message, details = {}) {
183
+ super(400, 'NOT_SERVING', message, details);
184
+ this.name = 'NotServing';
185
+ }
186
+ }
187
+ /**
188
+ * `409 CLUSTER_NOT_UPGRADED`: the change needs every node of the cluster
189
+ * (replicas, voters and learners) on the node's release or later, and one
190
+ * is not yet; for instance adding a served origin. Retry once the cluster is
191
+ * upgraded.
192
+ */
193
+ export class ClusterNotUpgraded extends CalimeroError {
194
+ constructor(message, details = {}) {
195
+ super(409, 'CLUSTER_NOT_UPGRADED', message, details);
196
+ this.name = 'ClusterNotUpgraded';
197
+ }
198
+ }
168
199
  /**
169
200
  * `409 RECOVERY_NOT_READY`: a recovery (an account's or an organization's)
170
201
  * may finish only once its 72-hour delay has passed. `readyAt` is when it may
@@ -223,7 +254,10 @@ function signUpRefusal(message) {
223
254
  return 'org-gone';
224
255
  return 'other';
225
256
  }
226
- /** `429 QUOTA_EXCEEDED`: the caller holds as many of something (origins, blobs, ...) as it may. */
257
+ /**
258
+ * `429 QUOTA_EXCEEDED`: the caller holds as many of something (origins,
259
+ * served origins per package or per cluster, blobs, ...) as it may.
260
+ */
227
261
  export class QuotaExceeded extends CalimeroError {
228
262
  constructor(message, details = {}) {
229
263
  super(429, 'QUOTA_EXCEEDED', message, details);
@@ -321,6 +355,10 @@ export function errorFrom(status, body, retryAfterHeader) {
321
355
  return new BadOrigin(code, message, details);
322
356
  case '409 ORIGIN_TAKEN':
323
357
  return new OriginTaken(message, details);
358
+ case '400 NOT_SERVING':
359
+ return new NotServing(message, details);
360
+ case '409 CLUSTER_NOT_UPGRADED':
361
+ return new ClusterNotUpgraded(message, details);
324
362
  case '429 QUOTA_EXCEEDED':
325
363
  return new QuotaExceeded(message, details);
326
364
  case '409 RECOVERY_NOT_READY':
@@ -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, BadOrigin, CalimeroError, ConfirmCancelled, ConfirmRequired, IdempotencyMismatch, LoginRequired, ConsentStale, NotFound, OriginNotRegistered, OriginTaken, QuotaExceeded, RecoveryNotReady, ReleaseDeadlineError, SignUpRefused, TrapError, UnavailableError, WrongNode, WrongRecoveryCode, errorFrom, } from './errors.js';
4
+ export { AppError, ArgsTooLarge, BadOrigin, CalimeroError, ClusterNotUpgraded, ConfirmCancelled, ConfirmRequired, IdempotencyMismatch, LoginRequired, ConsentStale, NotFound, NotServing, OriginNotRegistered, OriginTaken, QuotaExceeded, RecoveryNotReady, ReleaseDeadlineError, SignUpRefused, TrapError, UnavailableError, WrongNode, WrongRecoveryCode, errorFrom, } from './errors.js';
5
5
  export type { Committed, SignUpRefusal } from './errors.js';
6
- export type { AppInfo, AppXcalls, BlobContent, BlobInfo, DeadLetter, Deployer, DeployerGrant, DeployersInfo, DeploymentInfo, DeploymentOffer, DeploymentOrgs, FrontendManifest, MeInfo, PackageOrigin, PackageOriginAdded, PackageOriginRemoved, PackageOrigins, SignInOptions, AccountRecovery, SignUpOptions, SignUpPolicy, PublicDeployment, CallResult, CalimeroClientOptions, CalimeroConnectOptions, CloudEvent, CalimeroSession, PackageInfo, PackageInstalled, CloudRetryOptions, OrgInfo, OrgRecovery, RecoveryTimes, 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, ServedOrigin, ServedOrigins, SignInOptions, AccountRecovery, SignUpOptions, SignUpPolicy, PublicDeployment, CallResult, CalimeroClientOptions, CalimeroConnectOptions, CloudEvent, CalimeroSession, PackageInfo, PackageInstalled, CloudRetryOptions, OrgInfo, OrgRecovery, RecoveryTimes, 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, BadOrigin, CalimeroError, ConfirmCancelled, ConfirmRequired, IdempotencyMismatch, LoginRequired, ConsentStale, NotFound, OriginNotRegistered, OriginTaken, QuotaExceeded, RecoveryNotReady, ReleaseDeadlineError, SignUpRefused, TrapError, UnavailableError, WrongNode, WrongRecoveryCode, errorFrom, } from './errors.js';
3
+ export { AppError, ArgsTooLarge, BadOrigin, CalimeroError, ClusterNotUpgraded, ConfirmCancelled, ConfirmRequired, IdempotencyMismatch, LoginRequired, ConsentStale, NotFound, NotServing, OriginNotRegistered, OriginTaken, QuotaExceeded, RecoveryNotReady, ReleaseDeadlineError, SignUpRefused, TrapError, UnavailableError, WrongNode, WrongRecoveryCode, 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_RECOVERY_NONCE_BYTES, RECOVERY_DOMAIN, newRecoverySeed, parseRecoverySeed, recoveryMessage, recoveryPublicKey, recoverySeedText, signRecovery, } from './recovery.js';