@calimero-network/mero-js 26.10.0 → 27.0.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 +73 -288
- package/dist/cloud-v2/attestation.d.ts +91 -0
- package/dist/cloud-v2/attestation.js +135 -0
- package/dist/cloud-v2/capabilities.d.ts +30 -0
- package/dist/cloud-v2/capabilities.js +31 -0
- package/dist/cloud-v2/client.d.ts +426 -0
- package/dist/cloud-v2/client.js +646 -0
- package/dist/cloud-v2/errors.d.ts +98 -0
- package/dist/cloud-v2/errors.js +165 -0
- package/dist/cloud-v2/index.d.ts +10 -0
- package/dist/cloud-v2/index.js +5 -0
- package/dist/cloud-v2/passkey.d.ts +27 -0
- package/dist/cloud-v2/passkey.js +73 -0
- package/dist/crypto/internal.d.ts +3 -37
- package/dist/crypto/internal.js +3 -72
- package/dist/index.browser.mjs +4 -2
- package/dist/index.browser.mjs.map +4 -4
- package/dist/index.cjs +1750 -12767
- package/dist/index.cjs.map +4 -4
- package/dist/index.d.ts +3 -56
- package/dist/index.js +14 -75
- package/dist/index.mjs +1750 -12767
- package/dist/index.mjs.map +4 -4
- package/dist/sealed/index.d.ts +4 -4
- package/dist/sealed/index.js +2 -2
- package/dist/sealed/release.d.ts +21 -76
- package/dist/sealed/release.js +24 -108
- package/dist/sealed/sealed.d.ts +8 -56
- package/dist/sealed/sealed.js +11 -89
- package/dist/sealed/verify.d.ts +7 -13
- package/dist/sealed/verify.js +3 -7
- package/package.json +8 -13
- package/dist/account/account-admin.d.ts +0 -80
- package/dist/account/account-admin.js +0 -346
- package/dist/account/account.d.ts +0 -196
- package/dist/account/account.js +0 -269
- package/dist/account/application-id.d.ts +0 -50
- package/dist/account/application-id.js +0 -83
- package/dist/account/bootstrap-from-invitation.d.ts +0 -110
- package/dist/account/bootstrap-from-invitation.js +0 -80
- package/dist/account/create-context.d.ts +0 -103
- package/dist/account/create-context.js +0 -267
- package/dist/account/govern.d.ts +0 -24
- package/dist/account/govern.js +0 -50
- package/dist/account/index.d.ts +0 -26
- package/dist/account/index.js +0 -15
- package/dist/account/join-as-account.d.ts +0 -23
- package/dist/account/join-as-account.js +0 -34
- package/dist/account/join-with-node.d.ts +0 -115
- package/dist/account/join-with-node.js +0 -161
- package/dist/account/node-admin.d.ts +0 -20
- package/dist/account/node-admin.js +0 -33
- package/dist/account/relay-from-invitation.d.ts +0 -137
- package/dist/account/relay-from-invitation.js +0 -180
- package/dist/account/relay-login-error.d.ts +0 -32
- package/dist/account/relay-login-error.js +0 -34
- package/dist/account/session.d.ts +0 -266
- package/dist/account/session.js +0 -915
- package/dist/account/wordlist.d.ts +0 -16
- package/dist/account/wordlist.js +0 -273
- package/dist/admin-api/admin-client.d.ts +0 -686
- package/dist/admin-api/admin-client.js +0 -1225
- package/dist/admin-api/admin-factory.d.ts +0 -14
- package/dist/admin-api/admin-factory.js +0 -22
- package/dist/admin-api/admin-types.d.ts +0 -2041
- package/dist/admin-api/admin-types.js +0 -12
- package/dist/admin-api/index.d.ts +0 -4
- package/dist/admin-api/index.js +0 -5
- package/dist/admin-api/redeem-invitation.d.ts +0 -101
- package/dist/admin-api/redeem-invitation.js +0 -200
- package/dist/auth/index.d.ts +0 -34
- package/dist/auth/index.js +0 -67
- package/dist/auth-api/auth-client.d.ts +0 -31
- package/dist/auth-api/auth-client.js +0 -117
- package/dist/auth-api/auth-factory.d.ts +0 -7
- package/dist/auth-api/auth-factory.js +0 -41
- package/dist/auth-api/auth-types.d.ts +0 -130
- package/dist/auth-api/auth-types.js +0 -2
- package/dist/auth-api/index.d.ts +0 -3
- package/dist/auth-api/index.js +0 -4
- package/dist/capabilities.d.ts +0 -84
- package/dist/capabilities.js +0 -117
- package/dist/cloud/account-ownership.d.ts +0 -63
- package/dist/cloud/account-ownership.js +0 -87
- package/dist/cloud/cloud-client.d.ts +0 -949
- package/dist/cloud/cloud-client.js +0 -1068
- package/dist/cloud/connect.d.ts +0 -213
- package/dist/cloud/connect.js +0 -260
- package/dist/cloud/enrol-redirect.d.ts +0 -181
- package/dist/cloud/enrol-redirect.js +0 -244
- package/dist/cloud/index.d.ts +0 -12
- package/dist/cloud/index.js +0 -10
- package/dist/cloud/link-redirect.d.ts +0 -95
- package/dist/cloud/link-redirect.js +0 -121
- package/dist/cloud/link-state.d.ts +0 -36
- package/dist/cloud/link-state.js +0 -104
- package/dist/cloud/routing-proof.d.ts +0 -113
- package/dist/cloud/routing-proof.js +0 -96
- package/dist/device-cert/device-cert.d.ts +0 -189
- package/dist/device-cert/device-cert.js +0 -239
- package/dist/device-cert/device-scope.d.ts +0 -58
- package/dist/device-cert/device-scope.js +0 -76
- package/dist/device-cert/index.d.ts +0 -4
- package/dist/device-cert/index.js +0 -3
- package/dist/ephemeral/codec.d.ts +0 -4
- package/dist/ephemeral/codec.js +0 -12
- package/dist/ephemeral/index.d.ts +0 -57
- package/dist/ephemeral/index.js +0 -101
- package/dist/ephemeral/types.d.ts +0 -27
- package/dist/ephemeral/types.js +0 -1
- package/dist/errors/classify.d.ts +0 -65
- package/dist/errors/classify.js +0 -117
- package/dist/errors/index.d.ts +0 -2
- package/dist/errors/index.js +0 -1
- package/dist/events/group.d.ts +0 -95
- package/dist/events/group.js +0 -10
- package/dist/events/index.d.ts +0 -5
- package/dist/events/index.js +0 -2
- package/dist/events/sse.d.ts +0 -127
- package/dist/events/sse.js +0 -469
- package/dist/events/ws.d.ts +0 -57
- package/dist/events/ws.js +0 -212
- package/dist/http-client/api-response.d.ts +0 -15
- package/dist/http-client/api-response.js +0 -1
- package/dist/http-client/http-factory.d.ts +0 -71
- package/dist/http-client/http-factory.js +0 -68
- package/dist/http-client/http-types.d.ts +0 -87
- package/dist/http-client/http-types.js +0 -1
- package/dist/http-client/index.d.ts +0 -7
- package/dist/http-client/index.js +0 -11
- package/dist/http-client/path-segment.d.ts +0 -1
- package/dist/http-client/path-segment.js +0 -8
- package/dist/http-client/retry.d.ts +0 -5
- package/dist/http-client/retry.js +0 -82
- package/dist/http-client/signal-utils.d.ts +0 -2
- package/dist/http-client/signal-utils.js +0 -41
- package/dist/http-client/web-client.d.ts +0 -80
- package/dist/http-client/web-client.js +0 -557
- package/dist/invitation/index.d.ts +0 -2
- package/dist/invitation/index.js +0 -2
- package/dist/invitation/invitation.d.ts +0 -106
- package/dist/invitation/invitation.js +0 -104
- package/dist/login/index.d.ts +0 -4
- package/dist/login/index.js +0 -3
- package/dist/login/login.d.ts +0 -111
- package/dist/login/login.js +0 -110
- package/dist/login/session.d.ts +0 -124
- package/dist/login/session.js +0 -141
- package/dist/mero-js.d.ts +0 -231
- package/dist/mero-js.js +0 -469
- package/dist/namespace-op/index.d.ts +0 -2
- package/dist/namespace-op/index.js +0 -3
- package/dist/namespace-op/namespace-op.d.ts +0 -150
- package/dist/namespace-op/namespace-op.js +0 -191
- package/dist/nodeDiscovery.d.ts +0 -47
- package/dist/nodeDiscovery.js +0 -101
- package/dist/owner-op/index.d.ts +0 -2
- package/dist/owner-op/index.js +0 -3
- package/dist/owner-op/owner-op.d.ts +0 -119
- package/dist/owner-op/owner-op.js +0 -214
- package/dist/presence/relay-presence.d.ts +0 -56
- package/dist/presence/relay-presence.js +0 -107
- package/dist/presence/statement.d.ts +0 -12
- package/dist/presence/statement.js +0 -17
- package/dist/relay/index.d.ts +0 -4
- package/dist/relay/index.js +0 -2
- package/dist/relay/nonce-source.d.ts +0 -72
- package/dist/relay/nonce-source.js +0 -111
- package/dist/relay/relay-client.d.ts +0 -472
- package/dist/relay/relay-client.js +0 -518
- package/dist/relay-attestation/attest-node-key.d.ts +0 -66
- package/dist/relay-attestation/attest-node-key.js +0 -110
- package/dist/relay-attestation/index.d.ts +0 -1
- package/dist/relay-attestation/index.js +0 -1
- package/dist/request/caller-proof.d.ts +0 -43
- package/dist/request/caller-proof.js +0 -36
- package/dist/request/index.d.ts +0 -3
- package/dist/request/index.js +0 -3
- package/dist/request/proof-signer.d.ts +0 -49
- package/dist/request/proof-signer.js +0 -59
- package/dist/request/request-sig.d.ts +0 -99
- package/dist/request/request-sig.js +0 -95
- package/dist/request-proof/authorizer.d.ts +0 -41
- package/dist/request-proof/authorizer.js +0 -60
- package/dist/request-proof/index.d.ts +0 -4
- package/dist/request-proof/index.js +0 -2
- package/dist/request-proof/request-proof.d.ts +0 -53
- package/dist/request-proof/request-proof.js +0 -95
- package/dist/rpc/index.d.ts +0 -64
- package/dist/rpc/index.js +0 -78
- package/dist/signer/index.d.ts +0 -2
- package/dist/signer/index.js +0 -3
- package/dist/signer/signer.d.ts +0 -92
- package/dist/signer/signer.js +0 -147
- package/dist/token-store/index.d.ts +0 -27
- package/dist/token-store/index.js +0 -62
- package/dist/transport/index.d.ts +0 -6
- package/dist/transport/index.js +0 -3
- package/dist/transport/mero-client.d.ts +0 -294
- package/dist/transport/mero-client.js +0 -386
- package/dist/transport/relay-observer.d.ts +0 -152
- package/dist/transport/relay-observer.js +0 -173
- package/dist/transport/relay-transport.d.ts +0 -73
- package/dist/transport/relay-transport.js +0 -94
- package/dist/transport/types.d.ts +0 -100
- package/dist/transport/types.js +0 -32
- package/dist/types/api-response.d.ts +0 -15
- package/dist/types/api-response.js +0 -1
- package/dist/types/index.d.ts +0 -1
- package/dist/types/index.js +0 -1
- package/dist/warrant/creation-warrant.d.ts +0 -163
- package/dist/warrant/creation-warrant.js +0 -194
- package/dist/warrant/governance-op.d.ts +0 -202
- package/dist/warrant/governance-op.js +0 -355
- package/dist/warrant/governance-warrant.d.ts +0 -124
- package/dist/warrant/governance-warrant.js +0 -164
- package/dist/warrant/index.d.ts +0 -8
- package/dist/warrant/index.js +0 -5
- package/dist/warrant/warrant.d.ts +0 -188
- package/dist/warrant/warrant.js +0 -202
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What the cloud node's refusals mean to a caller (spec D64, D70).
|
|
3
|
+
*
|
|
4
|
+
* The node answers an error as `{ error: CODE, message, details }` with an HTTP
|
|
5
|
+
* status. {@link errorFrom} is the one place the client turns that into an
|
|
6
|
+
* error a caller can switch on; every call, view and event stream goes through
|
|
7
|
+
* it. It matches the status and the code together, never the status alone: the
|
|
8
|
+
* node sends other 409s (`CONFLICT`, `NOT_INSTALLED`) that are not an
|
|
9
|
+
* idempotency mismatch, and other 413s (`TOO_LARGE` from the sealed transport)
|
|
10
|
+
* that are not oversized call arguments. Anything it does not recognise stays a
|
|
11
|
+
* plain {@link CalimeroError} carrying both.
|
|
12
|
+
*
|
|
13
|
+
* Every typed error extends {@link CalimeroError}, so `instanceof CalimeroError`
|
|
14
|
+
* and its `status` and `code` (the node's code) keep working for all of them.
|
|
15
|
+
*/
|
|
16
|
+
export declare class CalimeroError extends Error {
|
|
17
|
+
readonly status: number;
|
|
18
|
+
readonly code: string;
|
|
19
|
+
readonly details: Record<string, unknown>;
|
|
20
|
+
constructor(status: number, code: string, message: string, details?: Record<string, unknown>);
|
|
21
|
+
}
|
|
22
|
+
/** `401 UNAUTHENTICATED`: sign in (again) and repeat the request. */
|
|
23
|
+
export declare class LoginRequired extends CalimeroError {
|
|
24
|
+
constructor(message: string, details?: Record<string, unknown>);
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* `404 NOT_FOUND`: what the request names does not exist for this caller. For
|
|
28
|
+
* an app this does not say whether it exists: an account the app does not
|
|
29
|
+
* admit gets the same answer as a missing app.
|
|
30
|
+
*/
|
|
31
|
+
export declare class NotFound extends CalimeroError {
|
|
32
|
+
constructor(message: string, details?: Record<string, unknown>);
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* `412 CONFIRM_REQUIRED`: the call needs the account holder's passkey over a
|
|
36
|
+
* challenge in `details`. The `*Confirmed` methods answer it themselves; it
|
|
37
|
+
* reaches a caller only from a method that was given no passkey.
|
|
38
|
+
*/
|
|
39
|
+
export declare class ConfirmRequired extends CalimeroError {
|
|
40
|
+
constructor(message: string, details?: Record<string, unknown>);
|
|
41
|
+
}
|
|
42
|
+
/** The user declined the passkey prompt a confirmation asked for. Nothing was sent. */
|
|
43
|
+
export declare class ConfirmCancelled extends Error {
|
|
44
|
+
readonly cause?: unknown;
|
|
45
|
+
constructor(cause?: unknown);
|
|
46
|
+
}
|
|
47
|
+
/** `413 ARGS_TOO_LARGE`: the call's arguments are over the node's limit; it did not run. */
|
|
48
|
+
export declare class ArgsTooLarge extends CalimeroError {
|
|
49
|
+
constructor(message: string, details?: Record<string, unknown>);
|
|
50
|
+
}
|
|
51
|
+
/** `409 IDEMPOTENCY_MISMATCH`: the idempotency key was used before with different arguments. */
|
|
52
|
+
export declare class IdempotencyMismatch extends CalimeroError {
|
|
53
|
+
constructor(message: string, details?: Record<string, unknown>);
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* `422 APP_ERROR`: the app refused the call. `appCode` is the app's declared
|
|
57
|
+
* error (`Taken`, `Forbidden`, ...) and `fields` its named fields. The call was
|
|
58
|
+
* logged, so its idempotency key is spent.
|
|
59
|
+
*/
|
|
60
|
+
export declare class AppError extends CalimeroError {
|
|
61
|
+
readonly appCode: string;
|
|
62
|
+
readonly fields: Record<string, unknown>;
|
|
63
|
+
constructor(message: string, details?: Record<string, unknown>);
|
|
64
|
+
}
|
|
65
|
+
/** `422 TRAP`: the run trapped (`OutOfFuel`, `OutOfMemory`, ...). The call was logged. */
|
|
66
|
+
export declare class TrapError extends CalimeroError {
|
|
67
|
+
readonly kind: string;
|
|
68
|
+
constructor(message: string, details?: Record<string, unknown>);
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Whether a call the node answered 503 may have committed: `false` when the
|
|
72
|
+
* node says it did not (`TIMEOUT`, `MIGRATING`), `"unknown"` otherwise (a call
|
|
73
|
+
* that raced a leader change). Never a plain boolean: `"unknown"` must not read
|
|
74
|
+
* as "no".
|
|
75
|
+
*/
|
|
76
|
+
export type Committed = false | 'unknown';
|
|
77
|
+
/**
|
|
78
|
+
* Any `503`, or a connection that failed, after the client's retries with the
|
|
79
|
+
* same idempotency key ran out. `reason` is the node's code in lower case
|
|
80
|
+
* (`timeout`, `migrating`, `unavailable`, ...), or `network` when the node gave
|
|
81
|
+
* no answer at all (code `NETWORK`, `cause` the `fetch` failure); such a call
|
|
82
|
+
* may have committed, so its `committed` is `'unknown'`.
|
|
83
|
+
*/
|
|
84
|
+
export declare class UnavailableError extends CalimeroError {
|
|
85
|
+
readonly reason: string;
|
|
86
|
+
readonly committed: Committed;
|
|
87
|
+
/** Seconds the node asked to wait, when it said. */
|
|
88
|
+
readonly retryAfter: number | undefined;
|
|
89
|
+
/** What failed, when the node gave no answer. */
|
|
90
|
+
readonly cause: unknown;
|
|
91
|
+
constructor(code: string, message: string, details?: Record<string, unknown>, retryAfter?: number, cause?: unknown);
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* The error for a refusal the node answered with `status` and `body` (its JSON
|
|
95
|
+
* `{ error, message, details }`, or whatever it sent). The one place statuses
|
|
96
|
+
* become typed errors.
|
|
97
|
+
*/
|
|
98
|
+
export declare function errorFrom(status: number, body: unknown, retryAfterHeader?: string | null): CalimeroError;
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What the cloud node's refusals mean to a caller (spec D64, D70).
|
|
3
|
+
*
|
|
4
|
+
* The node answers an error as `{ error: CODE, message, details }` with an HTTP
|
|
5
|
+
* status. {@link errorFrom} is the one place the client turns that into an
|
|
6
|
+
* error a caller can switch on; every call, view and event stream goes through
|
|
7
|
+
* it. It matches the status and the code together, never the status alone: the
|
|
8
|
+
* node sends other 409s (`CONFLICT`, `NOT_INSTALLED`) that are not an
|
|
9
|
+
* idempotency mismatch, and other 413s (`TOO_LARGE` from the sealed transport)
|
|
10
|
+
* that are not oversized call arguments. Anything it does not recognise stays a
|
|
11
|
+
* plain {@link CalimeroError} carrying both.
|
|
12
|
+
*
|
|
13
|
+
* Every typed error extends {@link CalimeroError}, so `instanceof CalimeroError`
|
|
14
|
+
* and its `status` and `code` (the node's code) keep working for all of them.
|
|
15
|
+
*/
|
|
16
|
+
export class CalimeroError extends Error {
|
|
17
|
+
constructor(status, code, message, details = {}) {
|
|
18
|
+
super(`${status} ${code}: ${message}`);
|
|
19
|
+
this.status = status;
|
|
20
|
+
this.code = code;
|
|
21
|
+
this.details = details;
|
|
22
|
+
this.name = 'CalimeroError';
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
/** `401 UNAUTHENTICATED`: sign in (again) and repeat the request. */
|
|
26
|
+
export class LoginRequired extends CalimeroError {
|
|
27
|
+
constructor(message, details = {}) {
|
|
28
|
+
super(401, 'UNAUTHENTICATED', message, details);
|
|
29
|
+
this.name = 'LoginRequired';
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* `404 NOT_FOUND`: what the request names does not exist for this caller. For
|
|
34
|
+
* an app this does not say whether it exists: an account the app does not
|
|
35
|
+
* admit gets the same answer as a missing app.
|
|
36
|
+
*/
|
|
37
|
+
export class NotFound extends CalimeroError {
|
|
38
|
+
constructor(message, details = {}) {
|
|
39
|
+
super(404, 'NOT_FOUND', message, details);
|
|
40
|
+
this.name = 'NotFound';
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* `412 CONFIRM_REQUIRED`: the call needs the account holder's passkey over a
|
|
45
|
+
* challenge in `details`. The `*Confirmed` methods answer it themselves; it
|
|
46
|
+
* reaches a caller only from a method that was given no passkey.
|
|
47
|
+
*/
|
|
48
|
+
export class ConfirmRequired extends CalimeroError {
|
|
49
|
+
constructor(message, details = {}) {
|
|
50
|
+
super(412, 'CONFIRM_REQUIRED', message, details);
|
|
51
|
+
this.name = 'ConfirmRequired';
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
/** The user declined the passkey prompt a confirmation asked for. Nothing was sent. */
|
|
55
|
+
export class ConfirmCancelled extends Error {
|
|
56
|
+
constructor(cause) {
|
|
57
|
+
super('the passkey confirmation was cancelled');
|
|
58
|
+
this.cause = cause;
|
|
59
|
+
this.name = 'ConfirmCancelled';
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
/** `413 ARGS_TOO_LARGE`: the call's arguments are over the node's limit; it did not run. */
|
|
63
|
+
export class ArgsTooLarge extends CalimeroError {
|
|
64
|
+
constructor(message, details = {}) {
|
|
65
|
+
super(413, 'ARGS_TOO_LARGE', message, details);
|
|
66
|
+
this.name = 'ArgsTooLarge';
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
/** `409 IDEMPOTENCY_MISMATCH`: the idempotency key was used before with different arguments. */
|
|
70
|
+
export class IdempotencyMismatch extends CalimeroError {
|
|
71
|
+
constructor(message, details = {}) {
|
|
72
|
+
super(409, 'IDEMPOTENCY_MISMATCH', message, details);
|
|
73
|
+
this.name = 'IdempotencyMismatch';
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* `422 APP_ERROR`: the app refused the call. `appCode` is the app's declared
|
|
78
|
+
* error (`Taken`, `Forbidden`, ...) and `fields` its named fields. The call was
|
|
79
|
+
* logged, so its idempotency key is spent.
|
|
80
|
+
*/
|
|
81
|
+
export class AppError extends CalimeroError {
|
|
82
|
+
constructor(message, details = {}) {
|
|
83
|
+
super(422, 'APP_ERROR', message, details);
|
|
84
|
+
this.name = 'AppError';
|
|
85
|
+
const { appCode, fields } = appErrorOf(details);
|
|
86
|
+
this.appCode = appCode;
|
|
87
|
+
this.fields = fields;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
/** `422 TRAP`: the run trapped (`OutOfFuel`, `OutOfMemory`, ...). The call was logged. */
|
|
91
|
+
export class TrapError extends CalimeroError {
|
|
92
|
+
constructor(message, details = {}) {
|
|
93
|
+
super(422, 'TRAP', message, details);
|
|
94
|
+
this.name = 'TrapError';
|
|
95
|
+
this.kind = typeof details.kind === 'string' ? details.kind : '';
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Any `503`, or a connection that failed, after the client's retries with the
|
|
100
|
+
* same idempotency key ran out. `reason` is the node's code in lower case
|
|
101
|
+
* (`timeout`, `migrating`, `unavailable`, ...), or `network` when the node gave
|
|
102
|
+
* no answer at all (code `NETWORK`, `cause` the `fetch` failure); such a call
|
|
103
|
+
* may have committed, so its `committed` is `'unknown'`.
|
|
104
|
+
*/
|
|
105
|
+
export class UnavailableError extends CalimeroError {
|
|
106
|
+
constructor(code, message, details = {}, retryAfter, cause) {
|
|
107
|
+
super(503, code, message, details);
|
|
108
|
+
this.name = 'UnavailableError';
|
|
109
|
+
this.reason = code.toLowerCase();
|
|
110
|
+
this.committed = code === 'TIMEOUT' || code === 'MIGRATING' ? false : 'unknown';
|
|
111
|
+
this.retryAfter = retryAfter;
|
|
112
|
+
this.cause = cause;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
function record(value) {
|
|
116
|
+
return value && typeof value === 'object' && !Array.isArray(value) ? value : {};
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* The app's code and named fields. The node puts them in `details` as
|
|
120
|
+
* `{ code, fields }`; an older node nests the app's error under `details.error`.
|
|
121
|
+
* Positional payloads (`"0"`, `"1"`) are never read as fields (D63).
|
|
122
|
+
*/
|
|
123
|
+
function appErrorOf(details) {
|
|
124
|
+
const source = typeof details.code === 'string' ? details : record(details.error);
|
|
125
|
+
const appCode = typeof source.code === 'string' ? source.code : '';
|
|
126
|
+
return { appCode, fields: { ...record(source.fields) } };
|
|
127
|
+
}
|
|
128
|
+
function retryAfterOf(details, header) {
|
|
129
|
+
for (const candidate of [details.retryAfter, header == null ? undefined : Number(header)]) {
|
|
130
|
+
if (typeof candidate === 'number' && Number.isFinite(candidate) && candidate >= 0)
|
|
131
|
+
return candidate;
|
|
132
|
+
}
|
|
133
|
+
return undefined;
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* The error for a refusal the node answered with `status` and `body` (its JSON
|
|
137
|
+
* `{ error, message, details }`, or whatever it sent). The one place statuses
|
|
138
|
+
* become typed errors.
|
|
139
|
+
*/
|
|
140
|
+
export function errorFrom(status, body, retryAfterHeader) {
|
|
141
|
+
const value = record(body);
|
|
142
|
+
const code = typeof value.error === 'string' ? value.error : 'ERROR';
|
|
143
|
+
const message = typeof value.message === 'string' ? value.message : '';
|
|
144
|
+
const details = record(value.details);
|
|
145
|
+
if (status === 503)
|
|
146
|
+
return new UnavailableError(code, message, details, retryAfterOf(details, retryAfterHeader));
|
|
147
|
+
switch (`${status} ${code}`) {
|
|
148
|
+
case '401 UNAUTHENTICATED':
|
|
149
|
+
return new LoginRequired(message, details);
|
|
150
|
+
case '404 NOT_FOUND':
|
|
151
|
+
return new NotFound(message, details);
|
|
152
|
+
case '409 IDEMPOTENCY_MISMATCH':
|
|
153
|
+
return new IdempotencyMismatch(message, details);
|
|
154
|
+
case '412 CONFIRM_REQUIRED':
|
|
155
|
+
return new ConfirmRequired(message, details);
|
|
156
|
+
case '413 ARGS_TOO_LARGE':
|
|
157
|
+
return new ArgsTooLarge(message, details);
|
|
158
|
+
case '422 APP_ERROR':
|
|
159
|
+
return new AppError(message, details);
|
|
160
|
+
case '422 TRAP':
|
|
161
|
+
return new TrapError(message, details);
|
|
162
|
+
default:
|
|
163
|
+
return new CalimeroError(status, code, message, details);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export { CalimeroClient } from './client.js';
|
|
2
|
+
export { AppError, ArgsTooLarge, CalimeroError, ConfirmCancelled, ConfirmRequired, IdempotencyMismatch, LoginRequired, NotFound, TrapError, UnavailableError, errorFrom, } from './errors.js';
|
|
3
|
+
export type { Committed } from './errors.js';
|
|
4
|
+
export type { AppInfo, BlobInfo, CallResult, CalimeroClientOptions, CloudEvent, CalimeroSession, PackageInfo, PackageInstalled, CloudRetryOptions, OrgUpgradeTarget, PresenceEntry, UpgradeRun, ViewResult, } from './client.js';
|
|
5
|
+
export { claimedRelease, fetchAttestedKey, MOCK_QUOTE_HEADER } from './attestation.js';
|
|
6
|
+
export type { AttestationDoc, CloudPolicy, CloudReleasePolicy } from './attestation.js';
|
|
7
|
+
export { browserPasskey, b64url, fromB64url } from './passkey.js';
|
|
8
|
+
export type { AssertionJSON, PasskeyProvider, RegistrationJSON } from './passkey.js';
|
|
9
|
+
export { DEFAULT_ORG_CAPABILITIES, ORG_CAPABILITIES, orgCapabilities } from './capabilities.js';
|
|
10
|
+
export type { OrgCapability } from './capabilities.js';
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { CalimeroClient } from './client.js';
|
|
2
|
+
export { AppError, ArgsTooLarge, CalimeroError, ConfirmCancelled, ConfirmRequired, IdempotencyMismatch, LoginRequired, NotFound, TrapError, UnavailableError, errorFrom, } from './errors.js';
|
|
3
|
+
export { claimedRelease, fetchAttestedKey, MOCK_QUOTE_HEADER } from './attestation.js';
|
|
4
|
+
export { browserPasskey, b64url, fromB64url } from './passkey.js';
|
|
5
|
+
export { DEFAULT_ORG_CAPABILITIES, ORG_CAPABILITIES, orgCapabilities } from './capabilities.js';
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Passkeys: the browser's WebAuthn API, or anything that answers like it.
|
|
3
|
+
*
|
|
4
|
+
* Every binary field crosses the wire as base64url, matching
|
|
5
|
+
* `calimero-identity`'s `Registration` and `Assertion`.
|
|
6
|
+
*/
|
|
7
|
+
export interface RegistrationJSON {
|
|
8
|
+
attestationObject: string;
|
|
9
|
+
clientDataJson: string;
|
|
10
|
+
}
|
|
11
|
+
export interface AssertionJSON {
|
|
12
|
+
credentialId: string;
|
|
13
|
+
authenticatorData: string;
|
|
14
|
+
clientDataJson: string;
|
|
15
|
+
signature: string;
|
|
16
|
+
}
|
|
17
|
+
/** Something that holds a passkey. */
|
|
18
|
+
export interface PasskeyProvider {
|
|
19
|
+
/** Answer `navigator.credentials.create` for the node's options. */
|
|
20
|
+
create(options: Record<string, unknown>): Promise<RegistrationJSON>;
|
|
21
|
+
/** Sign `challenge` (an assertion), optionally limited to `allowCredentials`. */
|
|
22
|
+
get(challenge: Uint8Array, rpId: string, allowCredentials?: string[]): Promise<AssertionJSON>;
|
|
23
|
+
}
|
|
24
|
+
export declare function b64url(bytes: Uint8Array): string;
|
|
25
|
+
export declare function fromB64url(s: string): Uint8Array;
|
|
26
|
+
/** The platform authenticator (Touch ID, Face ID, Windows Hello, a security key, a synced passkey). */
|
|
27
|
+
export declare function browserPasskey(): PasskeyProvider;
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Passkeys: the browser's WebAuthn API, or anything that answers like it.
|
|
3
|
+
*
|
|
4
|
+
* Every binary field crosses the wire as base64url, matching
|
|
5
|
+
* `calimero-identity`'s `Registration` and `Assertion`.
|
|
6
|
+
*/
|
|
7
|
+
export function b64url(bytes) {
|
|
8
|
+
let bin = '';
|
|
9
|
+
for (const b of bytes)
|
|
10
|
+
bin += String.fromCharCode(b);
|
|
11
|
+
return btoa(bin).replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
|
|
12
|
+
}
|
|
13
|
+
export function fromB64url(s) {
|
|
14
|
+
const pad = s.replace(/-/g, '+').replace(/_/g, '/') + '==='.slice((s.length + 3) % 4);
|
|
15
|
+
const bin = atob(pad);
|
|
16
|
+
const out = new Uint8Array(bin.length);
|
|
17
|
+
for (let i = 0; i < bin.length; i++)
|
|
18
|
+
out[i] = bin.charCodeAt(i);
|
|
19
|
+
return out;
|
|
20
|
+
}
|
|
21
|
+
const buf = (b) => b.buffer.slice(b.byteOffset, b.byteOffset + b.byteLength);
|
|
22
|
+
/** A dismissed prompt, named as WebAuthn names one (`NotAllowedError`), so a confirmation reads it as declined. */
|
|
23
|
+
function cancelled(message) {
|
|
24
|
+
const error = new Error(message);
|
|
25
|
+
error.name = 'NotAllowedError';
|
|
26
|
+
return error;
|
|
27
|
+
}
|
|
28
|
+
/** The platform authenticator (Touch ID, Face ID, Windows Hello, a security key, a synced passkey). */
|
|
29
|
+
export function browserPasskey() {
|
|
30
|
+
return {
|
|
31
|
+
async create(options) {
|
|
32
|
+
const o = options;
|
|
33
|
+
const cred = (await navigator.credentials.create({
|
|
34
|
+
publicKey: {
|
|
35
|
+
challenge: buf(fromB64url(o.challenge)),
|
|
36
|
+
rp: o.rp,
|
|
37
|
+
user: { ...o.user, id: buf(fromB64url(o.user.id)) },
|
|
38
|
+
pubKeyCredParams: o.pubKeyCredParams,
|
|
39
|
+
excludeCredentials: (o.excludeCredentials ?? []).map((c) => ({ type: c.type, id: buf(fromB64url(c.id)) })),
|
|
40
|
+
authenticatorSelection: o.authenticatorSelection,
|
|
41
|
+
attestation: 'none',
|
|
42
|
+
timeout: o.timeout,
|
|
43
|
+
},
|
|
44
|
+
}));
|
|
45
|
+
if (!cred)
|
|
46
|
+
throw cancelled('passkey creation was cancelled');
|
|
47
|
+
const r = cred.response;
|
|
48
|
+
return {
|
|
49
|
+
attestationObject: b64url(new Uint8Array(r.attestationObject)),
|
|
50
|
+
clientDataJson: b64url(new Uint8Array(r.clientDataJSON)),
|
|
51
|
+
};
|
|
52
|
+
},
|
|
53
|
+
async get(challenge, rpId, allowCredentials) {
|
|
54
|
+
const cred = (await navigator.credentials.get({
|
|
55
|
+
publicKey: {
|
|
56
|
+
challenge: buf(challenge),
|
|
57
|
+
rpId,
|
|
58
|
+
userVerification: 'preferred',
|
|
59
|
+
allowCredentials: (allowCredentials ?? []).map((id) => ({ type: 'public-key', id: buf(fromB64url(id)) })),
|
|
60
|
+
},
|
|
61
|
+
}));
|
|
62
|
+
if (!cred)
|
|
63
|
+
throw cancelled('passkey sign-in was cancelled');
|
|
64
|
+
const r = cred.response;
|
|
65
|
+
return {
|
|
66
|
+
credentialId: b64url(new Uint8Array(cred.rawId)),
|
|
67
|
+
authenticatorData: b64url(new Uint8Array(r.authenticatorData)),
|
|
68
|
+
clientDataJson: b64url(new Uint8Array(r.clientDataJSON)),
|
|
69
|
+
signature: b64url(new Uint8Array(r.signature)),
|
|
70
|
+
};
|
|
71
|
+
},
|
|
72
|
+
};
|
|
73
|
+
}
|
|
@@ -1,11 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Byte-level primitives shared by
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
* needed the same `domainHash`, hex handling and ed25519 import. Keeping one
|
|
6
|
-
* copy is the point: these have to stay byte-identical with core, and two
|
|
7
|
-
* copies drifting apart is exactly the failure the byte-parity e2e exists to
|
|
8
|
-
* catch. Internal — not exported from the package root.
|
|
2
|
+
* Byte-level primitives shared by the sealed transport, the attestation check
|
|
3
|
+
* and the agent gate: hex handling, fixed-width integers and concatenation.
|
|
4
|
+
* Internal — not exported from the package root.
|
|
9
5
|
*/
|
|
10
6
|
/** Little-endian u32, for borsh fixed-width fields and epoch counters. */
|
|
11
7
|
export declare function u32le(value: number): Uint8Array;
|
|
@@ -24,33 +20,3 @@ export declare function fromHex(value: string, label: string, expectedBytes: num
|
|
|
24
20
|
export declare function fromHexUnsized(value: string, label: string): Uint8Array;
|
|
25
21
|
export declare function u64le(value: number | bigint): Uint8Array;
|
|
26
22
|
export declare function concat(...parts: Uint8Array[]): Uint8Array;
|
|
27
|
-
/**
|
|
28
|
-
* core's `domain_hash`: SHA-256 over the length-prefixed domain, then each
|
|
29
|
-
* length-prefixed part. The lengths are what stop two different field splits from
|
|
30
|
-
* hashing alike.
|
|
31
|
-
*/
|
|
32
|
-
export declare function domainHash(domain: Uint8Array, parts: Uint8Array[]): Promise<Uint8Array>;
|
|
33
|
-
export declare function importSigningKey(secretHex: string): Promise<CryptoKey>;
|
|
34
|
-
/**
|
|
35
|
-
* Check an ed25519 signature against a public key, both raw.
|
|
36
|
-
*
|
|
37
|
-
* The one primitive here that consumes rather than produces a signature: a page
|
|
38
|
-
* handed a credential by another origin has to check it before it acts on it,
|
|
39
|
-
* and "the node will reject it later" is not the same safeguard — by then the
|
|
40
|
-
* page has already stored an account it does not control.
|
|
41
|
-
*
|
|
42
|
-
* `raw` import is the public-key counterpart of the PKCS#8 import above and is
|
|
43
|
-
* available on exactly the runtimes that can sign, so a runtime that got this
|
|
44
|
-
* far will not fail here for lack of support.
|
|
45
|
-
*
|
|
46
|
-
* @param publicKey 64 hex, the key the signature must verify against
|
|
47
|
-
*/
|
|
48
|
-
export declare function verifySignature(publicKey: string, signature: Uint8Array, payload: Uint8Array): Promise<boolean>;
|
|
49
|
-
/**
|
|
50
|
-
* The public half of an ed25519 seed.
|
|
51
|
-
*
|
|
52
|
-
* Derived rather than taken as an argument, for the reason core does the same: a
|
|
53
|
-
* caller able to name a key it does not hold could produce a warrant it cannot
|
|
54
|
-
* sign, and the field would stop meaning "who authorised this".
|
|
55
|
-
*/
|
|
56
|
-
export declare function derivePublicKey(secretHex: string): Promise<Uint8Array>;
|
package/dist/crypto/internal.js
CHANGED
|
@@ -1,16 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Byte-level primitives shared by
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
* needed the same `domainHash`, hex handling and ed25519 import. Keeping one
|
|
6
|
-
* copy is the point: these have to stay byte-identical with core, and two
|
|
7
|
-
* copies drifting apart is exactly the failure the byte-parity e2e exists to
|
|
8
|
-
* catch. Internal — not exported from the package root.
|
|
2
|
+
* Byte-level primitives shared by the sealed transport, the attestation check
|
|
3
|
+
* and the agent gate: hex handling, fixed-width integers and concatenation.
|
|
4
|
+
* Internal — not exported from the package root.
|
|
9
5
|
*/
|
|
10
|
-
const PKCS8_ED25519_PREFIX = new Uint8Array([
|
|
11
|
-
0x30, 0x2e, 0x02, 0x01, 0x00, 0x30, 0x05, 0x06, 0x03, 0x2b, 0x65, 0x70, 0x04,
|
|
12
|
-
0x22, 0x04, 0x20,
|
|
13
|
-
]);
|
|
14
6
|
/** Little-endian u32, for borsh fixed-width fields and epoch counters. */
|
|
15
7
|
export function u32le(value) {
|
|
16
8
|
const out = new Uint8Array(4);
|
|
@@ -69,64 +61,3 @@ export function concat(...parts) {
|
|
|
69
61
|
}
|
|
70
62
|
return out;
|
|
71
63
|
}
|
|
72
|
-
/**
|
|
73
|
-
* core's `domain_hash`: SHA-256 over the length-prefixed domain, then each
|
|
74
|
-
* length-prefixed part. The lengths are what stop two different field splits from
|
|
75
|
-
* hashing alike.
|
|
76
|
-
*/
|
|
77
|
-
export async function domainHash(domain, parts) {
|
|
78
|
-
const chunks = [u64le(domain.length), domain];
|
|
79
|
-
for (const part of parts) {
|
|
80
|
-
chunks.push(u64le(part.length), part);
|
|
81
|
-
}
|
|
82
|
-
return new Uint8Array(await crypto.subtle.digest('SHA-256', concat(...chunks)));
|
|
83
|
-
}
|
|
84
|
-
export async function importSigningKey(secretHex) {
|
|
85
|
-
const seed = fromHex(secretHex, 'deviceSecret', 32);
|
|
86
|
-
const pkcs8 = concat(PKCS8_ED25519_PREFIX, seed);
|
|
87
|
-
try {
|
|
88
|
-
return await crypto.subtle.importKey('pkcs8', pkcs8, { name: 'Ed25519' }, false, ['sign']);
|
|
89
|
-
}
|
|
90
|
-
catch (cause) {
|
|
91
|
-
// The original message is folded in rather than passed as `cause`: this
|
|
92
|
-
// package targets ES2020, where `Error` has no `cause` option.
|
|
93
|
-
const detail = cause instanceof Error ? `: ${cause.message}` : '';
|
|
94
|
-
throw new Error('this runtime has no WebCrypto Ed25519, so a warrant cannot be signed ' +
|
|
95
|
-
'here. Node 18.4+, Safari 17+, Firefox 129+ and Chrome 137+ have it; ' +
|
|
96
|
-
'otherwise mint the warrant with `merod account warrant`' +
|
|
97
|
-
detail);
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
/**
|
|
101
|
-
* Check an ed25519 signature against a public key, both raw.
|
|
102
|
-
*
|
|
103
|
-
* The one primitive here that consumes rather than produces a signature: a page
|
|
104
|
-
* handed a credential by another origin has to check it before it acts on it,
|
|
105
|
-
* and "the node will reject it later" is not the same safeguard — by then the
|
|
106
|
-
* page has already stored an account it does not control.
|
|
107
|
-
*
|
|
108
|
-
* `raw` import is the public-key counterpart of the PKCS#8 import above and is
|
|
109
|
-
* available on exactly the runtimes that can sign, so a runtime that got this
|
|
110
|
-
* far will not fail here for lack of support.
|
|
111
|
-
*
|
|
112
|
-
* @param publicKey 64 hex, the key the signature must verify against
|
|
113
|
-
*/
|
|
114
|
-
export async function verifySignature(publicKey, signature, payload) {
|
|
115
|
-
const key = await crypto.subtle.importKey('raw', fromHex(publicKey, 'publicKey', 32), { name: 'Ed25519' }, false, ['verify']);
|
|
116
|
-
return crypto.subtle.verify({ name: 'Ed25519' }, key, signature, payload);
|
|
117
|
-
}
|
|
118
|
-
/**
|
|
119
|
-
* The public half of an ed25519 seed.
|
|
120
|
-
*
|
|
121
|
-
* Derived rather than taken as an argument, for the reason core does the same: a
|
|
122
|
-
* caller able to name a key it does not hold could produce a warrant it cannot
|
|
123
|
-
* sign, and the field would stop meaning "who authorised this".
|
|
124
|
-
*/
|
|
125
|
-
export async function derivePublicKey(secretHex) {
|
|
126
|
-
const seed = fromHex(secretHex, 'deviceSecret', 32);
|
|
127
|
-
const pkcs8 = concat(PKCS8_ED25519_PREFIX, seed);
|
|
128
|
-
const key = await crypto.subtle.importKey('pkcs8', pkcs8, { name: 'Ed25519' }, true, ['sign']);
|
|
129
|
-
const jwk = await crypto.subtle.exportKey('jwk', key);
|
|
130
|
-
const raw = jwk.x;
|
|
131
|
-
return Uint8Array.from(atob(raw.replace(/-/g, '+').replace(/_/g, '/')), (c) => c.charCodeAt(0));
|
|
132
|
-
}
|