@calimero-network/mero-js 29.15.0 → 29.16.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 +35 -0
- package/dist/cloud-v2/client.d.ts +20 -129
- package/dist/cloud-v2/client.js +43 -452
- package/dist/index.browser.mjs +5 -5
- package/dist/index.browser.mjs.map +4 -4
- package/dist/index.cjs +503 -383
- package/dist/index.cjs.map +4 -4
- package/dist/index.d.ts +2 -0
- package/dist/index.js +4 -0
- package/dist/index.mjs +503 -383
- package/dist/index.mjs.map +4 -4
- package/dist/runtime/index.d.ts +10 -0
- package/dist/runtime/index.js +21 -0
- package/dist/runtime/transport.d.ts +311 -0
- package/dist/runtime/transport.js +576 -0
- package/dist/runtime.browser.mjs +67 -0
- package/dist/runtime.cjs +67 -0
- package/dist/runtime.mjs +67 -0
- package/package.json +9 -2
package/README.md
CHANGED
|
@@ -125,6 +125,41 @@ The pieces it is built from are exported too: the sealed transport
|
|
|
125
125
|
the client for mero-tee's private agent gate (`attestAgent`, `provisionAgent`,
|
|
126
126
|
`releaseAgent`).
|
|
127
127
|
|
|
128
|
+
## The runtime and the route layer
|
|
129
|
+
|
|
130
|
+
The client is two layers. The **runtime** (`@calimero-network/mero-js/runtime`,
|
|
131
|
+
`src/runtime/`) is everything that is not a route: the node's attestation, the
|
|
132
|
+
sealed channel, the credential and the session's refresh on a `401`, moving on
|
|
133
|
+
`421 WRONG_NODE`, D70 retries and failover, the `412 CONFIRM_REQUIRED` passkey
|
|
134
|
+
step-up, error mapping and server-sent events. Its whole surface toward routes is
|
|
135
|
+
one interface:
|
|
136
|
+
|
|
137
|
+
```ts
|
|
138
|
+
interface Transport {
|
|
139
|
+
request(method: HttpMethod, path: string, options?: RequestOptions): Promise<Response>; // 2xx, body unread
|
|
140
|
+
json<T>(method: HttpMethod, path: string, options?: RequestOptions): Promise<T>;
|
|
141
|
+
events(path: string, options?: { query?; signal? }): AsyncGenerator<CloudEvent>;
|
|
142
|
+
}
|
|
143
|
+
// RequestOptions: { query?, body? (JSON) | bytes? + contentType?, passkey? (answers a 412),
|
|
144
|
+
// userVerification?, retry?: true | 'once' (D70), signal?, promptSignal? }
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
A refusal is thrown as its typed `CalimeroError`. `NodeTransport` implements it;
|
|
148
|
+
`routePath('apps/{app}/blobs/{id}', { app, id })` fills a path with encoded
|
|
149
|
+
segments. The **route layer**, `CalimeroClient`, is the hand-written methods on
|
|
150
|
+
top; `client.transport` is its runtime, so a route layer of your own (one
|
|
151
|
+
generated from the node's OpenAPI document, say) shares the client's session:
|
|
152
|
+
|
|
153
|
+
```ts
|
|
154
|
+
import { routePath } from '@calimero-network/mero-js/runtime';
|
|
155
|
+
|
|
156
|
+
const app = await client.transport.json('GET', routePath('apps/{app}', { app: appId }));
|
|
157
|
+
await client.transport.json('DELETE', routePath('orgs/{org}', { org }), { body: {}, passkey });
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
The `/runtime` entry re-exports the package's own bundle, so its error classes are
|
|
161
|
+
the ones `CalimeroClient` throws (`instanceof` holds across both imports).
|
|
162
|
+
|
|
128
163
|
## Development
|
|
129
164
|
|
|
130
165
|
```bash
|
|
@@ -9,17 +9,13 @@
|
|
|
9
9
|
* const { result } = await cloud.call(appId, 'set', { key: 'a', value: '1' });
|
|
10
10
|
* ```
|
|
11
11
|
*/
|
|
12
|
+
import { DEFAULT_RETRY, NodeTransport, retryDelayMs, type CalimeroSession, type CloudEvent, type CloudRetryOptions } from '../runtime/transport.js';
|
|
12
13
|
import { type AttestationDoc, type CloudPolicy } from './attestation.js';
|
|
13
14
|
import { type PasskeyProvider } from './passkey.js';
|
|
14
15
|
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
16
|
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
17
|
export type { Committed, SignUpRefusal } from './errors.js';
|
|
17
|
-
export
|
|
18
|
-
accountId: string;
|
|
19
|
-
sessionToken: string;
|
|
20
|
-
expiresAt: number;
|
|
21
|
-
refreshToken: string;
|
|
22
|
-
}
|
|
18
|
+
export type { CalimeroSession, CloudEvent };
|
|
23
19
|
export interface CallResult {
|
|
24
20
|
index: number;
|
|
25
21
|
result: unknown;
|
|
@@ -32,11 +28,6 @@ export interface ViewResult {
|
|
|
32
28
|
index: number;
|
|
33
29
|
result: unknown;
|
|
34
30
|
}
|
|
35
|
-
export interface CloudEvent {
|
|
36
|
-
id: string;
|
|
37
|
-
kind: string;
|
|
38
|
-
data: unknown;
|
|
39
|
-
}
|
|
40
31
|
/** What installing a package release answers. */
|
|
41
32
|
export interface PackageInstalled {
|
|
42
33
|
package: string;
|
|
@@ -703,31 +694,8 @@ export interface SignUpPolicy {
|
|
|
703
694
|
/** Whether a sign-up without an invite is refused. */
|
|
704
695
|
inviteRequired: boolean;
|
|
705
696
|
}
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
* the same key, after a backoff that starts at `baseDelayMs` and doubles up to
|
|
709
|
-
* `maxDelayMs`, no jitter, at most `retries` times. The defaults are the Rust
|
|
710
|
-
* client's: 6 retries, 200 ms doubling, at most 2 s (about 9 s in all, past a
|
|
711
|
-
* leader election).
|
|
712
|
-
*/
|
|
713
|
-
export interface CloudRetryOptions {
|
|
714
|
-
/** Retries after the first attempt. Default 6. */
|
|
715
|
-
retries?: number;
|
|
716
|
-
/** The first backoff; each retry doubles it. Default 200 ms. */
|
|
717
|
-
baseDelayMs?: number;
|
|
718
|
-
/** The longest backoff. Default 2 s. */
|
|
719
|
-
maxDelayMs?: number;
|
|
720
|
-
}
|
|
721
|
-
export declare const DEFAULT_RETRY: {
|
|
722
|
-
readonly retries: 6;
|
|
723
|
-
readonly baseDelayMs: 200;
|
|
724
|
-
readonly maxDelayMs: 2000;
|
|
725
|
-
};
|
|
726
|
-
/** The backoff before retry `retry` (0 for the first): `baseDelayMs` doubling, at most `maxDelayMs`. */
|
|
727
|
-
export declare function retryDelayMs(retry: number, options: {
|
|
728
|
-
baseDelayMs: number;
|
|
729
|
-
maxDelayMs: number;
|
|
730
|
-
}): number;
|
|
697
|
+
export { DEFAULT_RETRY, retryDelayMs };
|
|
698
|
+
export type { CloudRetryOptions };
|
|
731
699
|
export interface CalimeroClientOptions {
|
|
732
700
|
/** One node of the cluster: the one the client talks to first. */
|
|
733
701
|
baseUrl: string;
|
|
@@ -763,24 +731,15 @@ export interface CalimeroConnectOptions {
|
|
|
763
731
|
random?: () => number;
|
|
764
732
|
}
|
|
765
733
|
export declare class CalimeroClient {
|
|
766
|
-
/**
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
private auth;
|
|
776
|
-
private current;
|
|
777
|
-
/** The refresh under way, which every request that needs one shares. */
|
|
778
|
-
private refreshing;
|
|
779
|
-
private readonly retry;
|
|
780
|
-
/** Rejections of the underlying `fetch` itself: the node could not be reached or dropped the connection. */
|
|
781
|
-
private readonly networkFailures;
|
|
782
|
-
/** The directory the node list came from, for a client made by {@link connect}. */
|
|
783
|
-
private source;
|
|
734
|
+
/**
|
|
735
|
+
* The runtime every route of this client is sent through
|
|
736
|
+
* (`@calimero-network/mero-js/runtime`): the attested, sealed channel to each
|
|
737
|
+
* node, the credential and its refresh, the `412 CONFIRM_REQUIRED` step-up,
|
|
738
|
+
* D70 retries and error mapping. A route layer of its own (one generated from
|
|
739
|
+
* the node's OpenAPI document, say) sends through it and shares this
|
|
740
|
+
* client's session.
|
|
741
|
+
*/
|
|
742
|
+
readonly transport: NodeTransport;
|
|
784
743
|
/**
|
|
785
744
|
* A client for the Calimero Cloud cluster named `cluster`: its node list read
|
|
786
745
|
* from the public node directory, those in `zone` first, each group in
|
|
@@ -792,87 +751,18 @@ export declare class CalimeroClient {
|
|
|
792
751
|
* first request once the directory's `refreshAfterSeconds` has passed.
|
|
793
752
|
*/
|
|
794
753
|
static connect(options: CalimeroConnectOptions): Promise<CalimeroClient>;
|
|
795
|
-
|
|
754
|
+
/** A client of the nodes `options` names, or one sending through an existing `transport`. */
|
|
755
|
+
constructor(options: CalimeroClientOptions | NodeTransport);
|
|
796
756
|
/** The node requests go to now: `baseUrl`, until a `421 WRONG_NODE` or a failover moved the client on. */
|
|
797
757
|
get nodeUrl(): string;
|
|
798
758
|
/** Every node this client may use, in the order it moves through them. */
|
|
799
759
|
get nodes(): string[];
|
|
800
|
-
/** The sealed channel to `url`, attested under the client's policy before its first request. */
|
|
801
|
-
private channel;
|
|
802
|
-
/**
|
|
803
|
-
* Move on from node `from` (the one that answered `421 WRONG_NODE`) to the
|
|
804
|
-
* next one. False when there is no other. A request that saw the same node
|
|
805
|
-
* refuse after another request moved on already does not move again.
|
|
806
|
-
*/
|
|
807
|
-
private moveOn;
|
|
808
|
-
/**
|
|
809
|
-
* Fail over from node `from`, where a request's retries ran out, to the next
|
|
810
|
-
* node it has not `tried`. False when it has tried them all. A request whose
|
|
811
|
-
* node another request moved away from already goes to that node, if untried.
|
|
812
|
-
*/
|
|
813
|
-
private failOver;
|
|
814
|
-
/**
|
|
815
|
-
* Read the node list again from the directory, in the background, for a
|
|
816
|
-
* client made by {@link connect}: the node in use stays first, then the
|
|
817
|
-
* directory's order; a node no longer listed is dropped. A failed read is
|
|
818
|
-
* ignored. One read at a time.
|
|
819
|
-
*/
|
|
820
|
-
private reread;
|
|
821
760
|
/** Authenticate every request with an API key. */
|
|
822
761
|
useApiKey(key: string): void;
|
|
823
762
|
useSession(session: CalimeroSession): void;
|
|
824
763
|
get session(): CalimeroSession | undefined;
|
|
825
|
-
/**
|
|
826
|
-
* Send one request to the node in use, with the credential held now, and
|
|
827
|
-
* answer the response, whatever its status. {@link send} judges it.
|
|
828
|
-
*/
|
|
829
|
-
private request;
|
|
830
|
-
/**
|
|
831
|
-
* {@link request}, answering what a lost issuing node or a node on the
|
|
832
|
-
* wrong release does to a session: a successful response, or the refusal as a
|
|
833
|
-
* typed error ({@link errorFrom}).
|
|
834
|
-
*
|
|
835
|
-
* - `401 UNAUTHENTICATED` to a request that carried a session: the node that
|
|
836
|
-
* signed the session may be gone, replaced, or out of reach of the node
|
|
837
|
-
* asked, which any node of the cluster answers with a refresh. The client
|
|
838
|
-
* refreshes once, shared by every request that needs it at the same time
|
|
839
|
-
* ({@link refresh}), and repeats the request once. A refresh that is
|
|
840
|
-
* refused surfaces as {@link LoginRequired}.
|
|
841
|
-
* - `421 WRONG_NODE`: this node's release may not serve the account. The
|
|
842
|
-
* client moves to the next node it knows and repeats the request once; with
|
|
843
|
-
* none, the {@link WrongNode} reaches the caller.
|
|
844
|
-
*
|
|
845
|
-
* Repeating is safe even for a request that is not idempotent: the node
|
|
846
|
-
* answers `401` and `421` when it judges the credential, before it reads what
|
|
847
|
-
* the request asks, so the first attempt ran nothing. The exceptions are the
|
|
848
|
-
* ceremonies that mint a session ({@link ceremony}), never repeated here.
|
|
849
|
-
*/
|
|
850
|
-
private send;
|
|
851
|
-
/** Whether a `401` to a request sent with `token` is one a refresh may answer. */
|
|
852
|
-
private mayRefresh;
|
|
853
|
-
/**
|
|
854
|
-
* A fresh session in place of the one `token` belonged to: by the refresh
|
|
855
|
-
* another request already made, or by one this request starts.
|
|
856
|
-
*/
|
|
857
|
-
private renew;
|
|
764
|
+
/** A JSON request, or a raw one when `raw` is given (as `contentType`, the transport's default when absent). */
|
|
858
765
|
private json;
|
|
859
|
-
/**
|
|
860
|
-
* {@link json} for a call or a view (D70): while the node answers 503 or
|
|
861
|
-
* cannot be reached, the same request, with the same idempotency key, is sent
|
|
862
|
-
* again after {@link retryDelayMs} (or the pause the node asked for, if
|
|
863
|
-
* longer), at most `retries` times. Every other answer is final.
|
|
864
|
-
*
|
|
865
|
-
* When a node's retries run out, the request fails over to the next node the
|
|
866
|
-
* client knows and its retries start again there, with the same key: the
|
|
867
|
-
* key is kept in the app's replicated log, so the cluster answers it once
|
|
868
|
-
* whichever node it reaches. Each node is tried once per request. The last
|
|
869
|
-
* failure surfaces as {@link UnavailableError}: the node's 503, or reason
|
|
870
|
-
* `network` for a connection that failed, whose `committed` is `'unknown'`.
|
|
871
|
-
*
|
|
872
|
-
* `once` sends the request exactly once, to the node in use (the confirmed
|
|
873
|
-
* attempt of {@link callConfirmed}, whose challenge is single-use).
|
|
874
|
-
*/
|
|
875
|
-
private retried;
|
|
876
766
|
post<T = Record<string, unknown>>(path: string, body?: unknown): Promise<T>;
|
|
877
767
|
get<T = Record<string, unknown>>(path: string): Promise<T>;
|
|
878
768
|
put<T = Record<string, unknown>>(path: string, body?: unknown): Promise<T>;
|
|
@@ -917,7 +807,6 @@ export declare class CalimeroClient {
|
|
|
917
807
|
* same token again (which would revoke the session).
|
|
918
808
|
*/
|
|
919
809
|
refresh(): Promise<CalimeroSession>;
|
|
920
|
-
private exchangeRefresh;
|
|
921
810
|
/**
|
|
922
811
|
* End this session. The client is signed out whether or not the node
|
|
923
812
|
* answers: the revoke (`POST /auth/logout`) is attempted, and its failure
|
|
@@ -1177,6 +1066,10 @@ export declare class CalimeroClient {
|
|
|
1177
1066
|
* confirmed retry carries `confirm` beside it.
|
|
1178
1067
|
*/
|
|
1179
1068
|
deleteConfirmed<T>(path: string, passkey: PasskeyProvider, body?: Record<string, unknown>): Promise<T>;
|
|
1069
|
+
/**
|
|
1070
|
+
* `body` to `path`, answering a `412 CONFIRM_REQUIRED` with `passkey` when one
|
|
1071
|
+
* is given; `signal` aborts only the passkey prompt.
|
|
1072
|
+
*/
|
|
1180
1073
|
private confirmed;
|
|
1181
1074
|
/**
|
|
1182
1075
|
* Create an app instance from a bundle hash, or from a package release
|
|
@@ -1219,8 +1112,6 @@ export declare class CalimeroClient {
|
|
|
1219
1112
|
}): Promise<ViewResult>;
|
|
1220
1113
|
/** Committed events from index `from`, then live, until `signal` aborts. */
|
|
1221
1114
|
events(app: string, from?: number, signal?: AbortSignal): AsyncGenerator<CloudEvent>;
|
|
1222
|
-
/** A server-sent event stream on `path`, sealed like every other answer. */
|
|
1223
|
-
private stream;
|
|
1224
1115
|
/**
|
|
1225
1116
|
* Publish this device's presence in `app`: `state` (at most 4 KiB) or `null`
|
|
1226
1117
|
* to retract it. The newest `seq` wins; send the same `seq` and state again
|