@calimero-network/mero-js 29.14.0 → 29.15.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
@@ -111,7 +111,7 @@ Pinned measurements go through `policy.verifyQuote` (`createQuoteVerifier`).
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`, `PasskeyUnknownHere`, `WrongNode`, `NotFound`, `ConfirmRequired`, `ConfirmCancelled`, `ConfirmPromptBlocked`,
114
- `ArgsTooLarge`, `IdempotencyMismatch`, `ReleaseDeadlineError`, `ConsentStale`, `AppError`, `TrapError`,
114
+ `ArgsTooLarge`, `IdempotencyMismatch`, `ReleaseDeadlineError`, `RuntimeTooOld`, `ConsentStale`, `AppError`, `TrapError`,
115
115
  `UnavailableError`, `OriginNotRegistered`, `BadOrigin`, `OriginTaken`, `QuotaExceeded`),
116
116
  all extending `CalimeroError` with the node's `status` and `code`. A `401` once
117
117
  the node that issued the session is replaced or out of reach is answered by one
@@ -82,7 +82,28 @@ export interface ReportedReplica {
82
82
  release: string | null;
83
83
  approved: boolean;
84
84
  }
85
- /** One app in a report. `name`, `team` and `availableVersions` are in members' and accounts' reports only. */
85
+ /** A replica's node whose runtime is older than a release needs, and what it runs: the version, or `{ before }` for a node on a release from before nodes named their runtime. */
86
+ export interface RuntimeShortNode {
87
+ node: number;
88
+ runtime: string | {
89
+ before: string;
90
+ } | null;
91
+ }
92
+ /**
93
+ * Why an app cannot move to the newest release it may see (core's
94
+ * `latestBlocked`): `runtime`, a replica's node runs an older runtime than
95
+ * the release's `minRuntimeVersion` (the upgrade would be refused `409
96
+ * RUNTIME_TOO_OLD`); `authority`, the app's owner upgrades it and the
97
+ * release would give its upgrades to the publisher.
98
+ */
99
+ export interface LatestBlocked {
100
+ version: string;
101
+ reason: 'runtime' | 'authority';
102
+ message: string;
103
+ minRuntimeVersion?: string;
104
+ nodes?: RuntimeShortNode[];
105
+ }
106
+ /** One app in a report. `name`, `team`, `availableVersions`, `latestReachable` and `latestBlocked` are in members' and accounts' reports only. */
86
107
  export interface ReportedApp {
87
108
  app: string;
88
109
  group: number;
@@ -103,6 +124,14 @@ export interface ReportedApp {
103
124
  team?: string;
104
125
  /** Newer releases of its package it may be upgraded to, oldest first (at most 16, the newest). */
105
126
  availableVersions?: string[];
127
+ /**
128
+ * The newest of every newer release it may see that an upgrade would take
129
+ * now, which may be older than all of `availableVersions`; `null` when none
130
+ * would. Absent from a node before core 1.0.0-rc.16.
131
+ */
132
+ latestReachable?: string | null;
133
+ /** Present when the newest release it may see is not `latestReachable`: which, and why. */
134
+ latestBlocked?: LatestBlocked;
106
135
  }
107
136
  /** A mero-tee release the cluster's nodes run, with how many and whether the scope approved it. */
108
137
  export interface ClusterRelease {
@@ -308,8 +337,16 @@ export interface PendingUpgrade {
308
337
  name: string | null;
309
338
  package: string;
310
339
  from: string;
311
- /** The newest it may move to. */
340
+ /**
341
+ * The newest it can move to now (the report's `latestReachable`); when
342
+ * none can be (`reachable: false`), or from a node that does not say, the
343
+ * newest it may see.
344
+ */
312
345
  to: string;
346
+ /** Whether an upgrade to `to` would be taken now; `false`: `blocked` says why none is. */
347
+ reachable: boolean;
348
+ /** Why the newest release it may see is not reachable, when it is not. */
349
+ blocked: LatestBlocked | null;
313
350
  /** Every newer release it may move to, oldest first. */
314
351
  available: string[];
315
352
  /** Whether owners hold app upgrades on this cluster; the node checks the package's own `upgrades` when asked. */
@@ -140,12 +140,16 @@ export function pendingApprovalsFrom(report, rules, now = Date.now() / 1000) {
140
140
  const available = a.availableVersions ?? [];
141
141
  if (a.package === null || available.length === 0)
142
142
  continue;
143
+ const latest = available[available.length - 1];
144
+ const reachable = a.latestReachable === undefined ? latest : a.latestReachable;
143
145
  upgrades.push({
144
146
  app: a.app,
145
147
  name: a.name ?? null,
146
148
  package: a.package.package,
147
149
  from: a.package.version,
148
- to: available[available.length - 1],
150
+ to: reachable ?? latest,
151
+ reachable: reachable !== null,
152
+ blocked: a.latestBlocked ?? null,
149
153
  available,
150
154
  actionable: upgradesActionable,
151
155
  });
@@ -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, BadOrigin, CalimeroError, ConfirmCancelled, ConfirmPromptBlocked, ConfirmRequired, IdempotencyMismatch, LoginRequired, ConsentStale, NotFound, OriginNotRegistered, OriginTaken, PasskeyUnknownHere, QuotaExceeded, RecoveryNotReady, ReleaseDeadlineError, SignUpRefused, TrapError, UnavailableError, WrongNode, WrongRecoveryCode, errorFrom, } from './errors.js';
15
+ export { AppError, ArgsTooLarge, BadOrigin, CalimeroError, ConfirmCancelled, ConfirmPromptBlocked, ConfirmRequired, IdempotencyMismatch, LoginRequired, ConsentStale, NotFound, OriginNotRegistered, OriginTaken, PasskeyUnknownHere, QuotaExceeded, RecoveryNotReady, ReleaseDeadlineError, RuntimeTooOld, SignUpRefused, TrapError, UnavailableError, WrongNode, WrongRecoveryCode, errorFrom, } from './errors.js';
16
16
  export type { Committed, SignUpRefusal } from './errors.js';
17
17
  export interface CalimeroSession {
18
18
  accountId: string;
@@ -1140,8 +1140,10 @@ export declare class CalimeroClient {
1140
1140
  }, passkey?: PasskeyProvider): Promise<MoveAppResult>;
1141
1141
  /**
1142
1142
  * Move an app to another release of its package (`{ version }`), another
1143
- * package, or a bundle hash, running its migration. Answers the upgrade's
1144
- * entry; a failed migration rejects with code `UPGRADE_FAILED` (422).
1143
+ * package, or a bundle hash. An upgrade is code only: the app moves to any
1144
+ * newer release in one step. Answers the upgrade's entry; one the app's
1145
+ * group refuses rejects with code `UPGRADE_FAILED` (422), and one a
1146
+ * replica's node is too old to run with {@link RuntimeTooOld} (409).
1145
1147
  * Where owners hold app upgrades, the node asks for the approval of
1146
1148
  * whoever the package's manifest names (`412 CONFIRM_REQUIRED`), which
1147
1149
  * `passkey` gives. Refused (`403`) to a session opened on an app's
@@ -16,7 +16,7 @@ import { CalimeroError, ConfirmCancelled, ConfirmPromptBlocked, ConfirmRequired,
16
16
  import { b64url, fromB64url } from './passkey.js';
17
17
  import { signRecovery } from './recovery.js';
18
18
  import { confirmationCode, pageQuery, pendingApprovalsFrom, } from './approvals.js';
19
- export { AppError, ArgsTooLarge, BadOrigin, CalimeroError, ConfirmCancelled, ConfirmPromptBlocked, ConfirmRequired, IdempotencyMismatch, LoginRequired, ConsentStale, NotFound, OriginNotRegistered, OriginTaken, PasskeyUnknownHere, QuotaExceeded, RecoveryNotReady, ReleaseDeadlineError, SignUpRefused, TrapError, UnavailableError, WrongNode, WrongRecoveryCode, errorFrom, } from './errors.js';
19
+ export { AppError, ArgsTooLarge, BadOrigin, CalimeroError, ConfirmCancelled, ConfirmPromptBlocked, ConfirmRequired, IdempotencyMismatch, LoginRequired, ConsentStale, NotFound, OriginNotRegistered, OriginTaken, PasskeyUnknownHere, QuotaExceeded, RecoveryNotReady, ReleaseDeadlineError, RuntimeTooOld, SignUpRefused, TrapError, UnavailableError, WrongNode, WrongRecoveryCode, errorFrom, } from './errors.js';
20
20
  /** A deployer's key, as `GET /deployers` lists it and `DELETE /deployers/{key}` takes it. */
21
21
  export function deployerKey(deployer) {
22
22
  return 'account' in deployer ? `account:${deployer.account}` : `org:${deployer.org}`;
@@ -958,8 +958,10 @@ export class CalimeroClient {
958
958
  }
959
959
  /**
960
960
  * Move an app to another release of its package (`{ version }`), another
961
- * package, or a bundle hash, running its migration. Answers the upgrade's
962
- * entry; a failed migration rejects with code `UPGRADE_FAILED` (422).
961
+ * package, or a bundle hash. An upgrade is code only: the app moves to any
962
+ * newer release in one step. Answers the upgrade's entry; one the app's
963
+ * group refuses rejects with code `UPGRADE_FAILED` (422), and one a
964
+ * replica's node is too old to run with {@link RuntimeTooOld} (409).
963
965
  * Where owners hold app upgrades, the node asks for the approval of
964
966
  * whoever the package's manifest names (`412 CONFIRM_REQUIRED`), which
965
967
  * `passkey` gives. Refused (`403`) to a session opened on an app's
@@ -120,6 +120,24 @@ export declare class ReleaseDeadlineError extends CalimeroError {
120
120
  readonly after: string;
121
121
  constructor(message: string, details?: Record<string, unknown>);
122
122
  }
123
+ /**
124
+ * `409 RUNTIME_TOO_OLD`: a node that would run the app (one of its replicas
125
+ * for an upgrade, a node it would be placed on or moved to) runs an older
126
+ * runtime than the release's signed `minRuntimeVersion`. Nothing ran. The
127
+ * app's report names the newest release it can reach (`latestReachable`).
128
+ */
129
+ export declare class RuntimeTooOld extends CalimeroError {
130
+ /** The runtime the release needs. */
131
+ readonly minRuntimeVersion: string;
132
+ /** Each node that falls short, and what it runs (`{ before }`: older than that). */
133
+ readonly nodes: {
134
+ node: number;
135
+ runtime: string | {
136
+ before: string;
137
+ } | null;
138
+ }[];
139
+ constructor(message: string, details?: Record<string, unknown>);
140
+ }
123
141
  /**
124
142
  * `409 CONSENT_STALE`: what a consent page showed (the app's bundle, the
125
143
  * target release, the release's measurements) changed before the person
@@ -151,6 +151,20 @@ export class ReleaseDeadlineError extends CalimeroError {
151
151
  this.after = typeof details.after === 'string' ? details.after : '';
152
152
  }
153
153
  }
154
+ /**
155
+ * `409 RUNTIME_TOO_OLD`: a node that would run the app (one of its replicas
156
+ * for an upgrade, a node it would be placed on or moved to) runs an older
157
+ * runtime than the release's signed `minRuntimeVersion`. Nothing ran. The
158
+ * app's report names the newest release it can reach (`latestReachable`).
159
+ */
160
+ export class RuntimeTooOld extends CalimeroError {
161
+ constructor(message, details = {}) {
162
+ super(409, 'RUNTIME_TOO_OLD', message, details);
163
+ this.name = 'RuntimeTooOld';
164
+ this.minRuntimeVersion = typeof details.minRuntimeVersion === 'string' ? details.minRuntimeVersion : '';
165
+ this.nodes = Array.isArray(details.nodes) ? details.nodes : [];
166
+ }
167
+ }
154
168
  /**
155
169
  * `409 CONSENT_STALE`: what a consent page showed (the app's bundle, the
156
170
  * target release, the release's measurements) changed before the person
@@ -383,6 +397,8 @@ export function errorFrom(status, body, retryAfterHeader) {
383
397
  return new IdempotencyMismatch(message, details);
384
398
  case '409 RELEASE_DEADLINE':
385
399
  return new ReleaseDeadlineError(message, details);
400
+ case '409 RUNTIME_TOO_OLD':
401
+ return new RuntimeTooOld(message, details);
386
402
  case '409 CONSENT_STALE':
387
403
  return new ConsentStale(message, details);
388
404
  case '403 ORIGIN_NOT_REGISTERED':
@@ -1,7 +1,7 @@
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, ClusterNotUpgraded, ConfirmCancelled, ConfirmPromptBlocked, ConfirmRequired, IdempotencyMismatch, LoginRequired, ConsentStale, NotFound, NotServing, OriginNotRegistered, OriginTaken, PasskeyUnknownHere, QuotaExceeded, RecoveryNotReady, ReleaseDeadlineError, SignUpRefused, TrapError, UnavailableError, WrongNode, WrongRecoveryCode, errorFrom, } from './errors.js';
4
+ export { AppError, ArgsTooLarge, BadOrigin, CalimeroError, ClusterNotUpgraded, ConfirmCancelled, ConfirmPromptBlocked, ConfirmRequired, IdempotencyMismatch, LoginRequired, ConsentStale, NotFound, NotServing, OriginNotRegistered, OriginTaken, PasskeyUnknownHere, QuotaExceeded, RecoveryNotReady, ReleaseDeadlineError, RuntimeTooOld, SignUpRefused, TrapError, UnavailableError, WrongNode, WrongRecoveryCode, errorFrom, } from './errors.js';
5
5
  export type { Committed, SignUpRefusal } from './errors.js';
6
6
  export type { AppInfo, AppTeam, AppTeams, AppXcalls, BlobContent, BlobInfo, DeadLetter, Deployer, DeployerGrant, DeployersInfo, DeploymentInfo, DeploymentOffer, DeploymentOrgs, OrgMemberSecurity, FrontendManifest, MeInfo, MeOrg, MePasskey, MoveAppResult, AccountSession, AccountActivity, PackageOrigin, PackageOriginAdded, PackageOriginRemoved, PackageOrigins, ServedOrigin, ServedOrigins, SignInOptions, AccountRecovery, SignUpOptions, SignUpPolicy, PublicDeployment, CallResult, CalimeroClientOptions, CalimeroConnectOptions, CloudEvent, CalimeroSession, PackageInfo, PackageInstalled, CloudRetryOptions, OrgInfo, OrgInvite, OrgMode, OrgRecovery, RecoveryTimes, OrgUpgradeTarget, PresenceEntry, ScopeApp, ScopeAppsOptions, ScopeAppsResult, ScopedAppTeam, TeamInfo, TeamMemberInfo, UpgradeRun, ViewResult, } from './client.js';
7
7
  export { claimedRelease, fetchAttestedKey, freshCollateral, MOCK_QUOTE_HEADER } from './attestation.js';
@@ -10,6 +10,6 @@ export { browserPasskey, b64url, fromB64url } from './passkey.js';
10
10
  export { MAX_RECOVERY_NONCE_BYTES, RECOVERY_DOMAIN, newRecoverySeed, parseRecoverySeed, recoveryMessage, recoveryPublicKey, recoverySeedText, signRecovery, } from './recovery.js';
11
11
  export type { AssertionJSON, PasskeyCreateOptions, PasskeyGetOptions, PasskeyProvider, PasskeyUserVerification, RegistrationJSON, } from './passkey.js';
12
12
  export { MAX_REPORT_PAGE, RELEASE_VERIFIER, compareVersions, confirmationCode, consentReturn, pageQuery, pendingApprovalsFrom, publisherBindingBytes, publisherBindingMessage, releaseConsent, releaseNotesUrl, stalledBy, upgradeConsent, verifyReleaseUrl, withdrawConsent, } from './approvals.js';
13
- export type { ActorKind, AppsReport, Authority, ClusterRelease, ConsentReleaseModel, ConsentRequest, ConsentResult, ConsentScope, ConsentStarted, ConsentStatus, OpenConsentOptions, Governance, LastUpgrade, OpsAppsReport, OpsOrgsReport, PageOptions, PendingApprovals, PendingRelease, PendingUpgrade, PublisherBindingInfo, PublisherProof, ReleaseDeadline, ReleaseRules, ReportScope, ReportedApp, ReportedBundle, ReportedRelease, ReportedReplica, UpgradeAuthority, } from './approvals.js';
13
+ export type { ActorKind, AppsReport, Authority, ClusterRelease, ConsentReleaseModel, ConsentRequest, ConsentResult, ConsentScope, ConsentStarted, ConsentStatus, OpenConsentOptions, Governance, LastUpgrade, LatestBlocked, OpsAppsReport, OpsOrgsReport, PageOptions, PendingApprovals, PendingRelease, PendingUpgrade, PublisherBindingInfo, PublisherProof, ReleaseDeadline, ReleaseRules, ReportScope, ReportedApp, ReportedBundle, ReportedRelease, ReportedReplica, RuntimeShortNode, UpgradeAuthority, } from './approvals.js';
14
14
  export { DEFAULT_ORG_CAPABILITIES, ORG_CAPABILITIES, orgCapabilities } from './capabilities.js';
15
15
  export type { OrgCapability } from './capabilities.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, ClusterNotUpgraded, ConfirmCancelled, ConfirmPromptBlocked, ConfirmRequired, IdempotencyMismatch, LoginRequired, ConsentStale, NotFound, NotServing, OriginNotRegistered, OriginTaken, PasskeyUnknownHere, QuotaExceeded, RecoveryNotReady, ReleaseDeadlineError, SignUpRefused, TrapError, UnavailableError, WrongNode, WrongRecoveryCode, errorFrom, } from './errors.js';
3
+ export { AppError, ArgsTooLarge, BadOrigin, CalimeroError, ClusterNotUpgraded, ConfirmCancelled, ConfirmPromptBlocked, ConfirmRequired, IdempotencyMismatch, LoginRequired, ConsentStale, NotFound, NotServing, OriginNotRegistered, OriginTaken, PasskeyUnknownHere, QuotaExceeded, RecoveryNotReady, ReleaseDeadlineError, RuntimeTooOld, 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';