@calimero-network/mero-js 1.0.2 → 2.0.0-beta.1
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 +341 -319
- package/dist/api/admin/aliases.d.ts +54 -0
- package/dist/api/admin/aliases.d.ts.map +1 -0
- package/dist/api/admin/aliases.js +43 -0
- package/dist/api/admin/aliases.js.map +1 -0
- package/dist/api/admin/applications.d.ts +52 -0
- package/dist/api/admin/applications.d.ts.map +1 -0
- package/dist/api/admin/applications.js +31 -0
- package/dist/api/admin/applications.js.map +1 -0
- package/dist/api/admin/blobs.d.ts +24 -0
- package/dist/api/admin/blobs.d.ts.map +1 -0
- package/dist/api/admin/blobs.js +58 -0
- package/dist/api/admin/blobs.js.map +1 -0
- package/dist/api/admin/capabilities.d.ts +26 -0
- package/dist/api/admin/capabilities.d.ts.map +1 -0
- package/dist/api/admin/capabilities.js +13 -0
- package/dist/api/admin/capabilities.js.map +1 -0
- package/dist/api/admin/client.d.ts +63 -0
- package/dist/api/admin/client.d.ts.map +1 -0
- package/dist/api/admin/client.js +103 -0
- package/dist/api/admin/client.js.map +1 -0
- package/dist/api/admin/contexts.d.ts +110 -0
- package/dist/api/admin/contexts.d.ts.map +1 -0
- package/dist/api/admin/contexts.js +61 -0
- package/dist/api/admin/contexts.js.map +1 -0
- package/dist/api/admin/factory.d.ts +4 -0
- package/dist/api/admin/factory.d.ts.map +1 -0
- package/dist/api/admin/factory.js +5 -0
- package/dist/api/admin/factory.js.map +1 -0
- package/dist/api/admin/identity.d.ts +10 -0
- package/dist/api/admin/identity.d.ts.map +1 -0
- package/dist/api/admin/identity.js +10 -0
- package/dist/api/admin/identity.js.map +1 -0
- package/dist/api/admin/index.d.ts +23 -0
- package/dist/api/admin/index.d.ts.map +1 -0
- package/dist/api/admin/index.js +26 -0
- package/dist/api/admin/index.js.map +1 -0
- package/dist/api/admin/network.d.ts +10 -0
- package/dist/api/admin/network.d.ts.map +1 -0
- package/dist/api/admin/network.js +9 -0
- package/dist/api/admin/network.js.map +1 -0
- package/dist/api/admin/proposals.d.ts +49 -0
- package/dist/api/admin/proposals.d.ts.map +1 -0
- package/dist/api/admin/proposals.js +34 -0
- package/dist/api/admin/proposals.js.map +1 -0
- package/dist/api/admin/public.d.ts +15 -0
- package/dist/api/admin/public.d.ts.map +1 -0
- package/dist/api/admin/public.js +18 -0
- package/dist/api/admin/public.js.map +1 -0
- package/dist/api/admin/tee.d.ts +74 -0
- package/dist/api/admin/tee.d.ts.map +1 -0
- package/dist/api/admin/tee.js +16 -0
- package/dist/api/admin/tee.js.map +1 -0
- package/dist/api/auth/client.d.ts +55 -0
- package/dist/api/auth/client.d.ts.map +1 -0
- package/dist/api/auth/client.js +127 -0
- package/dist/api/auth/client.js.map +1 -0
- package/dist/api/auth/factory.d.ts +4 -0
- package/dist/api/auth/factory.d.ts.map +1 -0
- package/dist/api/auth/factory.js +5 -0
- package/dist/api/auth/factory.js.map +1 -0
- package/dist/api/auth/index.d.ts +4 -0
- package/dist/api/auth/index.d.ts.map +1 -0
- package/dist/api/auth/index.js +4 -0
- package/dist/api/auth/index.js.map +1 -0
- package/dist/api/auth/types.d.ts +94 -0
- package/dist/api/auth/types.d.ts.map +1 -0
- package/dist/api/auth/types.js +4 -0
- package/dist/api/auth/types.js.map +1 -0
- package/dist/api/index.d.ts +15 -0
- package/dist/api/index.d.ts.map +1 -0
- package/dist/api/index.js +18 -0
- package/dist/api/index.js.map +1 -0
- package/dist/api/rpc/client.d.ts +76 -0
- package/dist/api/rpc/client.d.ts.map +1 -0
- package/dist/api/rpc/client.js +126 -0
- package/dist/api/rpc/client.js.map +1 -0
- package/dist/api/rpc/index.d.ts +3 -0
- package/dist/api/rpc/index.d.ts.map +1 -0
- package/dist/api/rpc/index.js +2 -0
- package/dist/api/rpc/index.js.map +1 -0
- package/dist/api/rpc/types.d.ts +74 -0
- package/dist/api/rpc/types.d.ts.map +1 -0
- package/dist/api/rpc/types.js +6 -0
- package/dist/api/rpc/types.js.map +1 -0
- package/dist/api/sse/client.d.ts +76 -0
- package/dist/api/sse/client.d.ts.map +1 -0
- package/dist/api/sse/client.js +203 -0
- package/dist/api/sse/client.js.map +1 -0
- package/dist/api/sse/index.d.ts +4 -0
- package/dist/api/sse/index.d.ts.map +1 -0
- package/dist/api/sse/index.js +2 -0
- package/dist/api/sse/index.js.map +1 -0
- package/dist/api/sse/types.d.ts +35 -0
- package/dist/api/sse/types.d.ts.map +1 -0
- package/dist/api/sse/types.js +6 -0
- package/dist/api/sse/types.js.map +1 -0
- package/dist/api/utils.d.ts +68 -0
- package/dist/api/utils.d.ts.map +1 -0
- package/dist/api/utils.js +83 -0
- package/dist/api/utils.js.map +1 -0
- package/dist/api/ws/client.d.ts +72 -0
- package/dist/api/ws/client.d.ts.map +1 -0
- package/dist/api/ws/client.js +202 -0
- package/dist/api/ws/client.js.map +1 -0
- package/dist/api/ws/index.d.ts +4 -0
- package/dist/api/ws/index.d.ts.map +1 -0
- package/dist/api/ws/index.js +2 -0
- package/dist/api/ws/index.js.map +1 -0
- package/dist/api/ws/types.d.ts +32 -0
- package/dist/api/ws/types.d.ts.map +1 -0
- package/dist/api/ws/types.js +6 -0
- package/dist/api/ws/types.js.map +1 -0
- package/dist/http-client/http-factory.d.ts +18 -0
- package/dist/http-client/http-factory.d.ts.map +1 -1
- package/dist/http-client/http-factory.js +2 -0
- package/dist/http-client/http-factory.js.map +1 -1
- package/dist/http-client/http-types.d.ts +6 -0
- package/dist/http-client/http-types.d.ts.map +1 -1
- package/dist/http-client/index.d.ts +1 -1
- package/dist/http-client/index.d.ts.map +1 -1
- package/dist/http-client/index.js +2 -1
- package/dist/http-client/index.js.map +1 -1
- package/dist/http-client/web-client.d.ts +5 -3
- package/dist/http-client/web-client.d.ts.map +1 -1
- package/dist/http-client/web-client.js +142 -59
- package/dist/http-client/web-client.js.map +1 -1
- package/dist/index.browser.mjs +1 -1
- package/dist/index.browser.mjs.map +4 -4
- package/dist/index.cjs +1669 -341
- package/dist/index.cjs.map +4 -4
- package/dist/index.d.ts +8 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -5
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1675 -341
- package/dist/index.mjs.map +4 -4
- package/dist/mero-js.d.ts +198 -7
- package/dist/mero-js.d.ts.map +1 -1
- package/dist/mero-js.js +329 -38
- package/dist/mero-js.js.map +1 -1
- package/package.json +42 -7
- package/dist/admin-api/admin-client.d.ts +0 -38
- package/dist/admin-api/admin-client.d.ts.map +0 -1
- package/dist/admin-api/admin-client.js +0 -104
- package/dist/admin-api/admin-client.js.map +0 -1
- package/dist/admin-api/admin-factory.d.ts +0 -8
- package/dist/admin-api/admin-factory.d.ts.map +0 -1
- package/dist/admin-api/admin-factory.js +0 -42
- package/dist/admin-api/admin-factory.js.map +0 -1
- package/dist/admin-api/admin-types.d.ts +0 -213
- package/dist/admin-api/admin-types.d.ts.map +0 -1
- package/dist/admin-api/admin-types.js +0 -3
- package/dist/admin-api/admin-types.js.map +0 -1
- package/dist/admin-api/index.d.ts +0 -4
- package/dist/admin-api/index.d.ts.map +0 -1
- package/dist/admin-api/index.js +0 -5
- package/dist/admin-api/index.js.map +0 -1
- package/dist/auth-api/auth-client.d.ts +0 -34
- package/dist/auth-api/auth-client.d.ts.map +0 -1
- package/dist/auth-api/auth-client.js +0 -112
- package/dist/auth-api/auth-client.js.map +0 -1
- package/dist/auth-api/auth-factory.d.ts +0 -8
- package/dist/auth-api/auth-factory.d.ts.map +0 -1
- package/dist/auth-api/auth-factory.js +0 -42
- package/dist/auth-api/auth-factory.js.map +0 -1
- package/dist/auth-api/auth-types.d.ts +0 -127
- package/dist/auth-api/auth-types.d.ts.map +0 -1
- package/dist/auth-api/auth-types.js +0 -3
- package/dist/auth-api/auth-types.js.map +0 -1
- package/dist/auth-api/index.d.ts +0 -4
- package/dist/auth-api/index.d.ts.map +0 -1
- package/dist/auth-api/index.js +0 -5
- package/dist/auth-api/index.js.map +0 -1
- package/dist/http-client/api-response.d.ts +0 -16
- package/dist/http-client/api-response.d.ts.map +0 -1
- package/dist/http-client/api-response.js +0 -2
- package/dist/http-client/api-response.js.map +0 -1
package/README.md
CHANGED
|
@@ -1,442 +1,464 @@
|
|
|
1
|
-
# Mero.js
|
|
1
|
+
# Mero.js v2 — Pure JavaScript SDK for Calimero
|
|
2
2
|
|
|
3
|
-
A lightweight, universal JavaScript SDK for Calimero
|
|
4
|
-
|
|
5
|
-
> **🚨 Breaking Change (v1.0.0)**: This version removes all legacy Axios-based code. The package now uses only Web Standards (`fetch`, `AbortController`) with zero external dependencies.
|
|
3
|
+
A lightweight, universal JavaScript SDK for interacting with Calimero nodes. Works in browsers, Node.js, React Native, and Tauri.
|
|
6
4
|
|
|
7
5
|
## Features
|
|
8
6
|
|
|
9
|
-
- 🌐 **
|
|
10
|
-
-
|
|
11
|
-
-
|
|
12
|
-
-
|
|
13
|
-
-
|
|
14
|
-
-
|
|
15
|
-
-
|
|
16
|
-
-
|
|
17
|
-
|
|
18
|
-
## Behavior
|
|
19
|
-
|
|
20
|
-
### Error Model (Throwing)
|
|
7
|
+
- 🌐 **Universal** — Browser, Node.js, React Native, Tauri
|
|
8
|
+
- 📦 **Zero Dependencies** — Built on Web Standards (`fetch`, `AbortController`)
|
|
9
|
+
- 🔐 **Smart Token Management** — Automatic refresh on 401, preemptive refresh before expiry
|
|
10
|
+
- 💾 **Pluggable Storage** — Bring your own `TokenStorage` (localStorage, AsyncStorage, Keychain)
|
|
11
|
+
- 🔄 **Real-time** — WebSocket and SSE clients for event subscriptions
|
|
12
|
+
- ⚡ **JSON-RPC** — Execute queries and mutations on contexts
|
|
13
|
+
- 🛡️ **Type Safe** — Full TypeScript definitions
|
|
14
|
+
- ✅ **Tested** — 236 unit tests, E2E with Merobox
|
|
21
15
|
|
|
22
|
-
|
|
16
|
+
## Installation
|
|
23
17
|
|
|
24
|
-
```
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
} catch (error) {
|
|
29
|
-
if (error instanceof HTTPError) {
|
|
30
|
-
console.log(`HTTP ${error.status}: ${error.statusText}`);
|
|
31
|
-
console.log('URL:', error.url);
|
|
32
|
-
console.log('Headers:', error.headers);
|
|
33
|
-
console.log('Body:', error.bodyText); // Up to 64KB
|
|
34
|
-
} else if (error.name === 'TimeoutError') {
|
|
35
|
-
// Request timed out
|
|
36
|
-
} else if (error.name === 'AbortError') {
|
|
37
|
-
// Request was cancelled
|
|
38
|
-
}
|
|
39
|
-
}
|
|
18
|
+
```bash
|
|
19
|
+
npm install @calimero-network/mero-js
|
|
20
|
+
# or
|
|
21
|
+
pnpm add @calimero-network/mero-js
|
|
40
22
|
```
|
|
41
23
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
Each method accepts optional `{ parse?: 'json'|'text'|'blob'|'arrayBuffer'|'response' }`:
|
|
45
|
-
|
|
46
|
-
**Default parsing rules:**
|
|
47
|
-
|
|
48
|
-
- `Content-Type: application/json` → parse as JSON
|
|
49
|
-
- `Content-Type: text/*` → parse as text
|
|
50
|
-
- Other types → parse as `arrayBuffer`
|
|
51
|
-
|
|
52
|
-
**Override:**
|
|
24
|
+
## Quick Start
|
|
53
25
|
|
|
54
26
|
```typescript
|
|
55
|
-
|
|
56
|
-
const response = await httpClient.get('/api/data', { parse: 'response' });
|
|
57
|
-
```
|
|
58
|
-
|
|
59
|
-
### Retry Policy (`withRetry`)
|
|
60
|
-
|
|
61
|
-
The `withRetry` helper retries on:
|
|
27
|
+
import { MeroJs } from '@calimero-network/mero-js';
|
|
62
28
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
29
|
+
const mero = new MeroJs({
|
|
30
|
+
baseUrl: 'http://localhost:2428',
|
|
31
|
+
credentials: {
|
|
32
|
+
username: 'admin',
|
|
33
|
+
password: 'your-password',
|
|
34
|
+
},
|
|
35
|
+
});
|
|
66
36
|
|
|
67
|
-
|
|
37
|
+
// Authenticate
|
|
38
|
+
await mero.authenticate();
|
|
68
39
|
|
|
69
|
-
|
|
40
|
+
// Use the Admin API
|
|
41
|
+
const apps = await mero.admin.applications.listApplications();
|
|
42
|
+
const contexts = await mero.admin.contexts.listContexts();
|
|
70
43
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
44
|
+
// Execute JSON-RPC calls
|
|
45
|
+
const result = await mero.rpc.query(
|
|
46
|
+
'context-id',
|
|
47
|
+
'get',
|
|
48
|
+
{ key: 'myKey' },
|
|
49
|
+
'ed25519:executor-public-key'
|
|
77
50
|
);
|
|
78
51
|
```
|
|
79
52
|
|
|
80
|
-
|
|
53
|
+
## Token Persistence
|
|
54
|
+
|
|
55
|
+
By default, tokens are stored in-memory. Provide a `TokenStorage` to persist across sessions:
|
|
81
56
|
|
|
82
|
-
|
|
57
|
+
### Browser (localStorage)
|
|
83
58
|
|
|
84
59
|
```typescript
|
|
85
|
-
const
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
60
|
+
const mero = new MeroJs({
|
|
61
|
+
baseUrl: 'http://localhost:2428',
|
|
62
|
+
credentials: { username: 'admin', password: 'password' },
|
|
63
|
+
tokenStorage: {
|
|
64
|
+
async get() {
|
|
65
|
+
const data = localStorage.getItem('mero-token');
|
|
66
|
+
return data ? JSON.parse(data) : null;
|
|
67
|
+
},
|
|
68
|
+
async set(token) {
|
|
69
|
+
localStorage.setItem('mero-token', JSON.stringify(token));
|
|
70
|
+
},
|
|
71
|
+
async clear() {
|
|
72
|
+
localStorage.removeItem('mero-token');
|
|
73
|
+
},
|
|
74
|
+
},
|
|
89
75
|
});
|
|
90
|
-
```
|
|
91
|
-
|
|
92
|
-
### Header Precedence & Authorization
|
|
93
76
|
|
|
94
|
-
|
|
95
|
-
|
|
77
|
+
// Load stored tokens on startup
|
|
78
|
+
await mero.init();
|
|
96
79
|
|
|
97
|
-
|
|
80
|
+
if (!mero.isAuthenticated()) {
|
|
81
|
+
await mero.authenticate();
|
|
82
|
+
}
|
|
83
|
+
```
|
|
98
84
|
|
|
99
|
-
|
|
85
|
+
### React Native (AsyncStorage)
|
|
100
86
|
|
|
101
87
|
```typescript
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
88
|
+
import AsyncStorage from '@react-native-async-storage/async-storage';
|
|
89
|
+
|
|
90
|
+
const mero = new MeroJs({
|
|
91
|
+
baseUrl: 'http://localhost:2428',
|
|
92
|
+
credentials: { username: 'admin', password: 'password' },
|
|
93
|
+
tokenStorage: {
|
|
94
|
+
async get() {
|
|
95
|
+
const data = await AsyncStorage.getItem('mero-token');
|
|
96
|
+
return data ? JSON.parse(data) : null;
|
|
97
|
+
},
|
|
98
|
+
async set(token) {
|
|
99
|
+
await AsyncStorage.setItem('mero-token', JSON.stringify(token));
|
|
100
|
+
},
|
|
101
|
+
async clear() {
|
|
102
|
+
await AsyncStorage.removeItem('mero-token');
|
|
103
|
+
},
|
|
104
|
+
},
|
|
105
|
+
});
|
|
106
106
|
```
|
|
107
107
|
|
|
108
|
-
###
|
|
109
|
-
|
|
110
|
-
No implicit `same-origin` default. Credentials are only set if explicitly provided:
|
|
108
|
+
### Tauri (Secure Store)
|
|
111
109
|
|
|
112
110
|
```typescript
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
111
|
+
import { Store } from '@tauri-apps/plugin-store';
|
|
112
|
+
|
|
113
|
+
const store = new Store('.tokens.dat');
|
|
114
|
+
|
|
115
|
+
const mero = new MeroJs({
|
|
116
|
+
baseUrl: 'http://localhost:2428',
|
|
117
|
+
requestCredentials: 'omit', // Required for Tauri
|
|
118
|
+
tokenStorage: {
|
|
119
|
+
async get() {
|
|
120
|
+
return await store.get('mero-token');
|
|
121
|
+
},
|
|
122
|
+
async set(token) {
|
|
123
|
+
await store.set('mero-token', token);
|
|
124
|
+
await store.save();
|
|
125
|
+
},
|
|
126
|
+
async clear() {
|
|
127
|
+
await store.delete('mero-token');
|
|
128
|
+
await store.save();
|
|
129
|
+
},
|
|
130
|
+
},
|
|
116
131
|
});
|
|
117
132
|
```
|
|
118
133
|
|
|
119
|
-
##
|
|
134
|
+
## Token Refresh
|
|
120
135
|
|
|
121
|
-
|
|
122
|
-
|
|
136
|
+
The SDK automatically handles token refresh in two ways:
|
|
137
|
+
|
|
138
|
+
1. **Preemptive** — Refreshes 5 minutes before expiry
|
|
139
|
+
2. **Reactive** — On 401 with `x-auth-error: token_expired`, automatically refreshes and retries
|
|
140
|
+
|
|
141
|
+
```
|
|
142
|
+
┌─────────────────────────────────────────────────┐
|
|
143
|
+
│ Token Lifecycle │
|
|
144
|
+
├─────────────────────────────────────────────────┤
|
|
145
|
+
│ authenticate() ──> Token stored │
|
|
146
|
+
│ │ │
|
|
147
|
+
│ API Request ───────┼──> Token valid? ──yes──> │
|
|
148
|
+
│ │ │ │
|
|
149
|
+
│ │ no (within 5min) │
|
|
150
|
+
│ │ │ │
|
|
151
|
+
│ │ ▼ │
|
|
152
|
+
│ │ Refresh token │
|
|
153
|
+
│ │ │ │
|
|
154
|
+
│ 401 + expired ─────┼─────────┘ │
|
|
155
|
+
│ │ │
|
|
156
|
+
│ clearToken() ──────┼──> Token cleared │
|
|
157
|
+
└─────────────────────────────────────────────────┘
|
|
123
158
|
```
|
|
124
159
|
|
|
125
|
-
##
|
|
160
|
+
## API Reference
|
|
126
161
|
|
|
127
|
-
###
|
|
162
|
+
### MeroJs Class
|
|
128
163
|
|
|
129
164
|
```typescript
|
|
130
|
-
|
|
165
|
+
interface MeroJsConfig {
|
|
166
|
+
baseUrl: string; // Node URL
|
|
167
|
+
authBaseUrl?: string; // Auth service URL (if separate)
|
|
168
|
+
credentials?: { username: string; password: string };
|
|
169
|
+
timeoutMs?: number; // Default: 10000
|
|
170
|
+
requestCredentials?: RequestCredentials;
|
|
171
|
+
tokenStorage?: TokenStorage; // For persistence
|
|
172
|
+
}
|
|
131
173
|
|
|
132
|
-
const
|
|
133
|
-
baseUrl: 'https://api.calimero.network',
|
|
134
|
-
getAuthToken: async () => localStorage.getItem('access_token'),
|
|
135
|
-
onTokenRefresh: async (newToken) =>
|
|
136
|
-
localStorage.setItem('access_token', newToken),
|
|
137
|
-
});
|
|
174
|
+
const mero = new MeroJs(config);
|
|
138
175
|
|
|
139
|
-
//
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
176
|
+
// Lifecycle
|
|
177
|
+
await mero.init(); // Load tokens from storage
|
|
178
|
+
await mero.authenticate(); // Get new tokens
|
|
179
|
+
await mero.clearToken(); // Clear tokens
|
|
180
|
+
mero.isAuthenticated(); // Check auth status
|
|
181
|
+
mero.getTokenData(); // Get current tokens
|
|
182
|
+
|
|
183
|
+
// API Clients
|
|
184
|
+
mero.auth // Auth API
|
|
185
|
+
mero.admin // Admin API
|
|
186
|
+
mero.rpc // JSON-RPC client
|
|
187
|
+
|
|
188
|
+
// Real-time
|
|
189
|
+
mero.createWebSocket(); // WebSocket client
|
|
190
|
+
mero.createSse(); // SSE client
|
|
146
191
|
```
|
|
147
192
|
|
|
148
|
-
###
|
|
193
|
+
### Admin API
|
|
149
194
|
|
|
150
195
|
```typescript
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
});
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
196
|
+
// Applications
|
|
197
|
+
await mero.admin.applications.listApplications();
|
|
198
|
+
await mero.admin.applications.getApplication(appId);
|
|
199
|
+
await mero.admin.applications.installApplication({ url, metadata });
|
|
200
|
+
await mero.admin.applications.uninstallApplication(appId);
|
|
201
|
+
|
|
202
|
+
// Contexts
|
|
203
|
+
await mero.admin.contexts.listContexts();
|
|
204
|
+
await mero.admin.contexts.createContext({ applicationId, contextSeed });
|
|
205
|
+
await mero.admin.contexts.getContext(contextId);
|
|
206
|
+
await mero.admin.contexts.deleteContext(contextId);
|
|
207
|
+
await mero.admin.contexts.joinContext({ contextId, invitationPayload });
|
|
208
|
+
|
|
209
|
+
// Blobs
|
|
210
|
+
await mero.admin.blobs.listBlobs();
|
|
211
|
+
await mero.admin.blobs.uploadBlob(data);
|
|
212
|
+
await mero.admin.blobs.getBlob(blobId);
|
|
213
|
+
await mero.admin.blobs.deleteBlob(blobId);
|
|
214
|
+
|
|
215
|
+
// Identity
|
|
216
|
+
await mero.admin.identity.generateContextIdentity();
|
|
217
|
+
|
|
218
|
+
// Network
|
|
219
|
+
await mero.admin.network.getPeersCount();
|
|
220
|
+
|
|
221
|
+
// Public (no auth required)
|
|
222
|
+
await mero.admin.public.health();
|
|
223
|
+
await mero.admin.public.isAuthed();
|
|
163
224
|
```
|
|
164
225
|
|
|
165
|
-
###
|
|
226
|
+
### Auth API
|
|
166
227
|
|
|
167
228
|
```typescript
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
229
|
+
// Health & Info
|
|
230
|
+
await mero.auth.getHealth();
|
|
231
|
+
await mero.auth.getIdentity();
|
|
232
|
+
await mero.auth.getProviders();
|
|
233
|
+
|
|
234
|
+
// Tokens
|
|
235
|
+
await mero.auth.getToken(request);
|
|
236
|
+
await mero.auth.refreshToken({ refresh_token });
|
|
237
|
+
await mero.auth.validateToken({ token });
|
|
238
|
+
|
|
239
|
+
// Keys
|
|
240
|
+
await mero.auth.listRootKeys();
|
|
241
|
+
await mero.auth.createRootKey(request);
|
|
242
|
+
await mero.auth.deleteRootKey(publicKey);
|
|
243
|
+
await mero.auth.listClientKeys();
|
|
244
|
+
await mero.auth.generateClientKey(request);
|
|
245
|
+
await mero.auth.deleteClientKey(publicKey);
|
|
178
246
|
```
|
|
179
247
|
|
|
180
|
-
|
|
248
|
+
### JSON-RPC
|
|
181
249
|
|
|
182
|
-
|
|
250
|
+
```typescript
|
|
251
|
+
// Query (read-only)
|
|
252
|
+
const result = await mero.rpc.query(
|
|
253
|
+
'context-id',
|
|
254
|
+
'get',
|
|
255
|
+
{ key: 'myKey' },
|
|
256
|
+
'ed25519:executor-public-key'
|
|
257
|
+
);
|
|
183
258
|
|
|
184
|
-
|
|
259
|
+
// Mutate (write)
|
|
260
|
+
const result = await mero.rpc.mutate(
|
|
261
|
+
'context-id',
|
|
262
|
+
'set',
|
|
263
|
+
{ key: 'myKey', value: 'myValue' },
|
|
264
|
+
'ed25519:executor-public-key'
|
|
265
|
+
);
|
|
185
266
|
|
|
186
|
-
|
|
267
|
+
// Generic execute
|
|
268
|
+
const result = await mero.rpc.execute({
|
|
269
|
+
contextId: 'context-id',
|
|
270
|
+
method: 'myMethod',
|
|
271
|
+
args: { foo: 'bar' },
|
|
272
|
+
executorPublicKey: 'ed25519:...',
|
|
273
|
+
});
|
|
274
|
+
```
|
|
187
275
|
|
|
188
|
-
|
|
276
|
+
### WebSocket (Real-time Events)
|
|
189
277
|
|
|
190
|
-
|
|
278
|
+
```typescript
|
|
279
|
+
const ws = mero.createWebSocket();
|
|
191
280
|
|
|
192
|
-
|
|
281
|
+
await ws.connect();
|
|
193
282
|
|
|
194
|
-
|
|
283
|
+
ws.onEvent((event) => {
|
|
284
|
+
console.log('Event:', event);
|
|
285
|
+
});
|
|
195
286
|
|
|
196
|
-
|
|
287
|
+
ws.onError((error) => {
|
|
288
|
+
console.error('Error:', error);
|
|
289
|
+
});
|
|
197
290
|
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
onTokenRefresh?: (token: string) => Promise<void>; // Token refresh callback
|
|
204
|
-
defaultHeaders?: Record<string, string>; // Default headers
|
|
205
|
-
timeoutMs?: number; // Request timeout (default: 30000)
|
|
206
|
-
credentials?: RequestCredentials; // CORS credentials (default: 'same-origin' for browser)
|
|
207
|
-
defaultAbortSignal?: AbortSignal; // Default abort signal for all requests
|
|
208
|
-
}
|
|
291
|
+
await ws.subscribe(['context-id-1', 'context-id-2']);
|
|
292
|
+
|
|
293
|
+
// Later...
|
|
294
|
+
await ws.unsubscribe(['context-id-1']);
|
|
295
|
+
ws.disconnect();
|
|
209
296
|
```
|
|
210
297
|
|
|
211
|
-
###
|
|
298
|
+
### SSE (Server-Sent Events)
|
|
212
299
|
|
|
213
300
|
```typescript
|
|
214
|
-
|
|
215
|
-
const response = await httpClient.get<T>('/api/endpoint', init?);
|
|
301
|
+
const sse = mero.createSse();
|
|
216
302
|
|
|
217
|
-
|
|
218
|
-
const response = await httpClient.post<T>('/api/endpoint', body?, init?);
|
|
303
|
+
const sessionId = await sse.connect();
|
|
219
304
|
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
// DELETE request
|
|
224
|
-
const response = await httpClient.delete<T>('/api/endpoint', init?);
|
|
305
|
+
sse.onEvent((event) => {
|
|
306
|
+
console.log('Event:', event);
|
|
307
|
+
});
|
|
225
308
|
|
|
226
|
-
|
|
227
|
-
const response = await httpClient.patch<T>('/api/endpoint', body?, init?);
|
|
309
|
+
await sse.subscribe(['context-id-1']);
|
|
228
310
|
|
|
229
|
-
//
|
|
230
|
-
const
|
|
311
|
+
// Get session info
|
|
312
|
+
const session = await sse.getSession();
|
|
231
313
|
|
|
232
|
-
//
|
|
233
|
-
|
|
314
|
+
// Later...
|
|
315
|
+
sse.disconnect();
|
|
234
316
|
```
|
|
235
317
|
|
|
236
|
-
|
|
318
|
+
## Deep Imports
|
|
319
|
+
|
|
320
|
+
For tree-shaking or direct access:
|
|
237
321
|
|
|
238
322
|
```typescript
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
323
|
+
// Main SDK
|
|
324
|
+
import { MeroJs } from '@calimero-network/mero-js';
|
|
325
|
+
|
|
326
|
+
// HTTP Client only
|
|
327
|
+
import { createBrowserHttpClient } from '@calimero-network/mero-js/http-client';
|
|
328
|
+
|
|
329
|
+
// Specific API clients
|
|
330
|
+
import { AdminApiClient } from '@calimero-network/mero-js/api/admin';
|
|
331
|
+
import { AuthApiClient } from '@calimero-network/mero-js/api/auth';
|
|
332
|
+
import { RpcClient } from '@calimero-network/mero-js/api/rpc';
|
|
333
|
+
import { WebSocketClient } from '@calimero-network/mero-js/api/ws';
|
|
334
|
+
import { SseClient } from '@calimero-network/mero-js/api/sse';
|
|
243
335
|
```
|
|
244
336
|
|
|
245
|
-
|
|
337
|
+
## HTTP Client (Low-Level)
|
|
246
338
|
|
|
247
|
-
|
|
339
|
+
For custom use cases, use the HTTP client directly:
|
|
248
340
|
|
|
249
341
|
```typescript
|
|
250
|
-
|
|
251
|
-
|
|
342
|
+
import { createBrowserHttpClient, HTTPError } from '@calimero-network/mero-js';
|
|
343
|
+
|
|
344
|
+
const http = createBrowserHttpClient({
|
|
345
|
+
baseUrl: 'https://api.example.com',
|
|
346
|
+
getAuthToken: async () => myTokenStore.getToken(),
|
|
347
|
+
refreshToken: async () => {
|
|
348
|
+
const newToken = await myRefreshLogic();
|
|
349
|
+
return newToken;
|
|
350
|
+
},
|
|
351
|
+
onTokenRefresh: async (token) => {
|
|
352
|
+
await myTokenStore.setToken(token);
|
|
353
|
+
},
|
|
354
|
+
timeoutMs: 10000,
|
|
355
|
+
});
|
|
252
356
|
|
|
253
|
-
// Error - throws HTTPError or other errors
|
|
254
357
|
try {
|
|
255
|
-
const data = await
|
|
358
|
+
const data = await http.get<MyType>('/api/endpoint');
|
|
256
359
|
} catch (error) {
|
|
257
360
|
if (error instanceof HTTPError) {
|
|
258
|
-
console.log(`HTTP ${error.status}: ${error.
|
|
259
|
-
console.log('URL:', error.url);
|
|
260
|
-
console.log('Headers:', error.headers);
|
|
261
|
-
console.log('Body:', error.bodyText);
|
|
361
|
+
console.log(`HTTP ${error.status}: ${error.bodyText}`);
|
|
262
362
|
}
|
|
263
363
|
}
|
|
264
364
|
```
|
|
265
365
|
|
|
266
|
-
##
|
|
267
|
-
|
|
268
|
-
### Custom Transport
|
|
366
|
+
## Error Handling
|
|
269
367
|
|
|
270
368
|
```typescript
|
|
271
|
-
import {
|
|
272
|
-
|
|
273
|
-
const transport: Transport = {
|
|
274
|
-
fetch: customFetch,
|
|
275
|
-
baseUrl: 'https://api.example.com',
|
|
276
|
-
getAuthToken: async () => 'your-token',
|
|
277
|
-
timeoutMs: 5000,
|
|
278
|
-
};
|
|
279
|
-
|
|
280
|
-
const httpClient = createHttpClient(transport);
|
|
281
|
-
```
|
|
369
|
+
import { HTTPError, ApiResponseError } from '@calimero-network/mero-js';
|
|
282
370
|
|
|
283
|
-
### Error Handling
|
|
284
|
-
|
|
285
|
-
```typescript
|
|
286
371
|
try {
|
|
287
|
-
|
|
288
|
-
// Success - data is the parsed response
|
|
289
|
-
console.log(data);
|
|
372
|
+
await mero.admin.contexts.getContext('invalid-id');
|
|
290
373
|
} catch (error) {
|
|
291
374
|
if (error instanceof HTTPError) {
|
|
292
|
-
// HTTP error (4xx, 5xx)
|
|
293
|
-
console.
|
|
294
|
-
console.
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
console.
|
|
375
|
+
// HTTP-level error (4xx, 5xx)
|
|
376
|
+
console.log(`HTTP ${error.status}: ${error.statusText}`);
|
|
377
|
+
console.log('Body:', error.bodyText);
|
|
378
|
+
} else if (error instanceof ApiResponseError) {
|
|
379
|
+
// API-level error (in response body)
|
|
380
|
+
console.log(`API Error: ${error.message}`);
|
|
381
|
+
console.log('Type:', error.type);
|
|
382
|
+
console.log('Code:', error.code);
|
|
299
383
|
} else if (error.name === 'AbortError') {
|
|
300
384
|
// Request was cancelled
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
// Network or other errors
|
|
304
|
-
console.error('Request failed:', error);
|
|
385
|
+
} else if (error.name === 'TimeoutError') {
|
|
386
|
+
// Request timed out
|
|
305
387
|
}
|
|
306
388
|
}
|
|
307
389
|
```
|
|
308
390
|
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
```typescript
|
|
312
|
-
const data = await httpClient.post('/api/data', body, {
|
|
313
|
-
headers: {
|
|
314
|
-
'Content-Type': 'application/json',
|
|
315
|
-
'X-Custom-Header': 'value',
|
|
316
|
-
},
|
|
317
|
-
});
|
|
318
|
-
```
|
|
319
|
-
|
|
320
|
-
### Request Cancellation
|
|
321
|
-
|
|
322
|
-
```typescript
|
|
323
|
-
const abortController = new AbortController();
|
|
324
|
-
|
|
325
|
-
// Cancel request after 5 seconds
|
|
326
|
-
setTimeout(() => abortController.abort(), 5000);
|
|
327
|
-
|
|
328
|
-
const data = await httpClient.get('/api/slow-endpoint', {
|
|
329
|
-
signal: abortController.signal,
|
|
330
|
-
});
|
|
331
|
-
```
|
|
332
|
-
|
|
333
|
-
### FormData Support
|
|
334
|
-
|
|
335
|
-
```typescript
|
|
336
|
-
const formData = new FormData();
|
|
337
|
-
formData.append('file', fileInput.files[0]);
|
|
338
|
-
formData.append('name', 'John Doe');
|
|
339
|
-
|
|
340
|
-
const data = await httpClient.post('/api/upload', formData);
|
|
341
|
-
// Content-Type is set by the browser/undici with proper boundary
|
|
342
|
-
```
|
|
343
|
-
|
|
344
|
-
### Response Parsing
|
|
345
|
-
|
|
346
|
-
```typescript
|
|
347
|
-
// Explicit parsing
|
|
348
|
-
const jsonData = await httpClient.get('/api/data', { parse: 'json' });
|
|
349
|
-
const textData = await httpClient.get('/api/text', { parse: 'text' });
|
|
350
|
-
const blobData = await httpClient.get('/api/file', { parse: 'blob' });
|
|
351
|
-
|
|
352
|
-
// Auto-detection based on Content-Type (default)
|
|
353
|
-
const autoData = await httpClient.get('/api/data');
|
|
354
|
-
```
|
|
391
|
+
## Testing
|
|
355
392
|
|
|
356
|
-
|
|
393
|
+
```bash
|
|
394
|
+
# Unit tests (236 tests)
|
|
395
|
+
pnpm test
|
|
357
396
|
|
|
358
|
-
|
|
359
|
-
|
|
397
|
+
# E2E tests with Merobox
|
|
398
|
+
pnpm test:e2e
|
|
360
399
|
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
{ attempts: 3 }, // Default: 3 attempts
|
|
364
|
-
);
|
|
400
|
+
# Full test suite
|
|
401
|
+
pnpm test:all
|
|
365
402
|
```
|
|
366
403
|
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
```typescript
|
|
370
|
-
import { combineSignals, createTimeoutSignal } from '@calimero-network/mero-js';
|
|
404
|
+
## Development
|
|
371
405
|
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
const combinedSignal = combineSignals([userSignal, timeoutSignal]);
|
|
406
|
+
```bash
|
|
407
|
+
# Install dependencies
|
|
408
|
+
pnpm install
|
|
376
409
|
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
});
|
|
380
|
-
```
|
|
410
|
+
# Build (with strict mode)
|
|
411
|
+
pnpm build
|
|
381
412
|
|
|
382
|
-
|
|
413
|
+
# Lint
|
|
414
|
+
pnpm lint
|
|
383
415
|
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
baseUrl: 'https://api.example.com',
|
|
387
|
-
credentials: 'include', // Include cookies in CORS requests
|
|
388
|
-
});
|
|
416
|
+
# Type check
|
|
417
|
+
pnpm typecheck
|
|
389
418
|
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
credentials: 'include',
|
|
393
|
-
});
|
|
419
|
+
# Generate MSW handlers from OpenAPI
|
|
420
|
+
pnpm generate:msw
|
|
394
421
|
```
|
|
395
422
|
|
|
396
|
-
##
|
|
397
|
-
|
|
398
|
-
If you're migrating from an Axios-based client:
|
|
423
|
+
## Bundle Sizes
|
|
399
424
|
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
425
|
+
| Format | Size | Gzipped |
|
|
426
|
+
|--------|------|---------|
|
|
427
|
+
| ESM | ~57kb | ~15kb |
|
|
428
|
+
| CJS | ~59kb | ~16kb |
|
|
429
|
+
| Browser (minified) | ~29kb | ~9kb |
|
|
404
430
|
|
|
405
431
|
## Browser Support
|
|
406
432
|
|
|
407
|
-
- Modern browsers with `fetch`
|
|
408
|
-
- Node.js 18+ (native fetch) or
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
- **ESM**: ~9.4kb (gzipped: ~3.2kb)
|
|
413
|
-
- **CJS**: ~10.5kb (gzipped: ~3.6kb)
|
|
433
|
+
- Modern browsers with `fetch` (Chrome 42+, Firefox 39+, Safari 10.1+)
|
|
434
|
+
- Node.js 18+ (native fetch) or 16+ with `undici`
|
|
435
|
+
- React Native with `fetch` polyfill
|
|
436
|
+
- Tauri (set `requestCredentials: 'omit'`)
|
|
414
437
|
|
|
415
|
-
##
|
|
438
|
+
## Migration from v1.x
|
|
416
439
|
|
|
417
|
-
|
|
440
|
+
### Breaking Changes
|
|
418
441
|
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
442
|
+
1. **`clearToken()` is now async**
|
|
443
|
+
```typescript
|
|
444
|
+
// Before
|
|
445
|
+
mero.clearToken();
|
|
446
|
+
|
|
447
|
+
// After
|
|
448
|
+
await mero.clearToken();
|
|
449
|
+
```
|
|
422
450
|
|
|
423
|
-
**
|
|
451
|
+
2. **HTTP client wiring** — Token refresh callbacks are now wired automatically
|
|
424
452
|
|
|
425
|
-
|
|
453
|
+
### New Features
|
|
426
454
|
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
# Test
|
|
435
|
-
pnpm test
|
|
436
|
-
|
|
437
|
-
# Lint
|
|
438
|
-
pnpm lint
|
|
439
|
-
```
|
|
455
|
+
- `TokenStorage` interface for persistence
|
|
456
|
+
- `init()` method to load stored tokens
|
|
457
|
+
- Automatic 401 token refresh with retry
|
|
458
|
+
- WebSocket and SSE clients
|
|
459
|
+
- JSON-RPC client
|
|
460
|
+
- Lazy-loaded Admin API sub-clients
|
|
461
|
+
- Enhanced `unwrap()` with RPC error detection
|
|
440
462
|
|
|
441
463
|
## License
|
|
442
464
|
|