@calimero-network/mero-js 1.1.0 → 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.
Files changed (172) hide show
  1. package/README.md +341 -319
  2. package/dist/api/admin/aliases.d.ts +54 -0
  3. package/dist/api/admin/aliases.d.ts.map +1 -0
  4. package/dist/api/admin/aliases.js +43 -0
  5. package/dist/api/admin/aliases.js.map +1 -0
  6. package/dist/api/admin/applications.d.ts +52 -0
  7. package/dist/api/admin/applications.d.ts.map +1 -0
  8. package/dist/api/admin/applications.js +31 -0
  9. package/dist/api/admin/applications.js.map +1 -0
  10. package/dist/api/admin/blobs.d.ts +24 -0
  11. package/dist/api/admin/blobs.d.ts.map +1 -0
  12. package/dist/api/admin/blobs.js +58 -0
  13. package/dist/api/admin/blobs.js.map +1 -0
  14. package/dist/api/admin/capabilities.d.ts +26 -0
  15. package/dist/api/admin/capabilities.d.ts.map +1 -0
  16. package/dist/api/admin/capabilities.js +13 -0
  17. package/dist/api/admin/capabilities.js.map +1 -0
  18. package/dist/api/admin/client.d.ts +63 -0
  19. package/dist/api/admin/client.d.ts.map +1 -0
  20. package/dist/api/admin/client.js +103 -0
  21. package/dist/api/admin/client.js.map +1 -0
  22. package/dist/api/admin/contexts.d.ts +110 -0
  23. package/dist/api/admin/contexts.d.ts.map +1 -0
  24. package/dist/api/admin/contexts.js +61 -0
  25. package/dist/api/admin/contexts.js.map +1 -0
  26. package/dist/api/admin/factory.d.ts +4 -0
  27. package/dist/api/admin/factory.d.ts.map +1 -0
  28. package/dist/api/admin/factory.js +5 -0
  29. package/dist/api/admin/factory.js.map +1 -0
  30. package/dist/api/admin/identity.d.ts +10 -0
  31. package/dist/api/admin/identity.d.ts.map +1 -0
  32. package/dist/api/admin/identity.js +10 -0
  33. package/dist/api/admin/identity.js.map +1 -0
  34. package/dist/api/admin/index.d.ts +23 -0
  35. package/dist/api/admin/index.d.ts.map +1 -0
  36. package/dist/api/admin/index.js +26 -0
  37. package/dist/api/admin/index.js.map +1 -0
  38. package/dist/api/admin/network.d.ts +10 -0
  39. package/dist/api/admin/network.d.ts.map +1 -0
  40. package/dist/api/admin/network.js +9 -0
  41. package/dist/api/admin/network.js.map +1 -0
  42. package/dist/api/admin/proposals.d.ts +49 -0
  43. package/dist/api/admin/proposals.d.ts.map +1 -0
  44. package/dist/api/admin/proposals.js +34 -0
  45. package/dist/api/admin/proposals.js.map +1 -0
  46. package/dist/api/admin/public.d.ts +15 -0
  47. package/dist/api/admin/public.d.ts.map +1 -0
  48. package/dist/api/admin/public.js +18 -0
  49. package/dist/api/admin/public.js.map +1 -0
  50. package/dist/api/admin/tee.d.ts +74 -0
  51. package/dist/api/admin/tee.d.ts.map +1 -0
  52. package/dist/api/admin/tee.js +16 -0
  53. package/dist/api/admin/tee.js.map +1 -0
  54. package/dist/api/auth/client.d.ts +55 -0
  55. package/dist/api/auth/client.d.ts.map +1 -0
  56. package/dist/api/auth/client.js +127 -0
  57. package/dist/api/auth/client.js.map +1 -0
  58. package/dist/api/auth/factory.d.ts +4 -0
  59. package/dist/api/auth/factory.d.ts.map +1 -0
  60. package/dist/api/auth/factory.js +5 -0
  61. package/dist/api/auth/factory.js.map +1 -0
  62. package/dist/api/auth/index.d.ts +4 -0
  63. package/dist/api/auth/index.d.ts.map +1 -0
  64. package/dist/api/auth/index.js +4 -0
  65. package/dist/api/auth/index.js.map +1 -0
  66. package/dist/api/auth/types.d.ts +94 -0
  67. package/dist/api/auth/types.d.ts.map +1 -0
  68. package/dist/api/auth/types.js +4 -0
  69. package/dist/api/auth/types.js.map +1 -0
  70. package/dist/api/index.d.ts +15 -0
  71. package/dist/api/index.d.ts.map +1 -0
  72. package/dist/api/index.js +18 -0
  73. package/dist/api/index.js.map +1 -0
  74. package/dist/api/rpc/client.d.ts +76 -0
  75. package/dist/api/rpc/client.d.ts.map +1 -0
  76. package/dist/api/rpc/client.js +126 -0
  77. package/dist/api/rpc/client.js.map +1 -0
  78. package/dist/api/rpc/index.d.ts +3 -0
  79. package/dist/api/rpc/index.d.ts.map +1 -0
  80. package/dist/api/rpc/index.js +2 -0
  81. package/dist/api/rpc/index.js.map +1 -0
  82. package/dist/api/rpc/types.d.ts +74 -0
  83. package/dist/api/rpc/types.d.ts.map +1 -0
  84. package/dist/api/rpc/types.js +6 -0
  85. package/dist/api/rpc/types.js.map +1 -0
  86. package/dist/api/sse/client.d.ts +76 -0
  87. package/dist/api/sse/client.d.ts.map +1 -0
  88. package/dist/api/sse/client.js +203 -0
  89. package/dist/api/sse/client.js.map +1 -0
  90. package/dist/api/sse/index.d.ts +4 -0
  91. package/dist/api/sse/index.d.ts.map +1 -0
  92. package/dist/api/sse/index.js +2 -0
  93. package/dist/api/sse/index.js.map +1 -0
  94. package/dist/api/sse/types.d.ts +35 -0
  95. package/dist/api/sse/types.d.ts.map +1 -0
  96. package/dist/api/sse/types.js +6 -0
  97. package/dist/api/sse/types.js.map +1 -0
  98. package/dist/api/utils.d.ts +68 -0
  99. package/dist/api/utils.d.ts.map +1 -0
  100. package/dist/api/utils.js +83 -0
  101. package/dist/api/utils.js.map +1 -0
  102. package/dist/api/ws/client.d.ts +72 -0
  103. package/dist/api/ws/client.d.ts.map +1 -0
  104. package/dist/api/ws/client.js +202 -0
  105. package/dist/api/ws/client.js.map +1 -0
  106. package/dist/api/ws/index.d.ts +4 -0
  107. package/dist/api/ws/index.d.ts.map +1 -0
  108. package/dist/api/ws/index.js +2 -0
  109. package/dist/api/ws/index.js.map +1 -0
  110. package/dist/api/ws/types.d.ts +32 -0
  111. package/dist/api/ws/types.d.ts.map +1 -0
  112. package/dist/api/ws/types.js +6 -0
  113. package/dist/api/ws/types.js.map +1 -0
  114. package/dist/http-client/index.d.ts +1 -1
  115. package/dist/http-client/index.d.ts.map +1 -1
  116. package/dist/http-client/index.js +2 -1
  117. package/dist/http-client/index.js.map +1 -1
  118. package/dist/http-client/web-client.d.ts +3 -3
  119. package/dist/http-client/web-client.d.ts.map +1 -1
  120. package/dist/http-client/web-client.js +16 -6
  121. package/dist/http-client/web-client.js.map +1 -1
  122. package/dist/index.browser.mjs +1 -1
  123. package/dist/index.browser.mjs.map +4 -4
  124. package/dist/index.cjs +1590 -281
  125. package/dist/index.cjs.map +4 -4
  126. package/dist/index.d.ts +8 -3
  127. package/dist/index.d.ts.map +1 -1
  128. package/dist/index.js +7 -5
  129. package/dist/index.js.map +1 -1
  130. package/dist/index.mjs +1596 -281
  131. package/dist/index.mjs.map +4 -4
  132. package/dist/mero-js.d.ts +198 -7
  133. package/dist/mero-js.d.ts.map +1 -1
  134. package/dist/mero-js.js +329 -38
  135. package/dist/mero-js.js.map +1 -1
  136. package/package.json +42 -7
  137. package/dist/admin-api/admin-client.d.ts +0 -38
  138. package/dist/admin-api/admin-client.d.ts.map +0 -1
  139. package/dist/admin-api/admin-client.js +0 -104
  140. package/dist/admin-api/admin-client.js.map +0 -1
  141. package/dist/admin-api/admin-factory.d.ts +0 -8
  142. package/dist/admin-api/admin-factory.d.ts.map +0 -1
  143. package/dist/admin-api/admin-factory.js +0 -42
  144. package/dist/admin-api/admin-factory.js.map +0 -1
  145. package/dist/admin-api/admin-types.d.ts +0 -213
  146. package/dist/admin-api/admin-types.d.ts.map +0 -1
  147. package/dist/admin-api/admin-types.js +0 -3
  148. package/dist/admin-api/admin-types.js.map +0 -1
  149. package/dist/admin-api/index.d.ts +0 -4
  150. package/dist/admin-api/index.d.ts.map +0 -1
  151. package/dist/admin-api/index.js +0 -5
  152. package/dist/admin-api/index.js.map +0 -1
  153. package/dist/auth-api/auth-client.d.ts +0 -34
  154. package/dist/auth-api/auth-client.d.ts.map +0 -1
  155. package/dist/auth-api/auth-client.js +0 -112
  156. package/dist/auth-api/auth-client.js.map +0 -1
  157. package/dist/auth-api/auth-factory.d.ts +0 -8
  158. package/dist/auth-api/auth-factory.d.ts.map +0 -1
  159. package/dist/auth-api/auth-factory.js +0 -42
  160. package/dist/auth-api/auth-factory.js.map +0 -1
  161. package/dist/auth-api/auth-types.d.ts +0 -127
  162. package/dist/auth-api/auth-types.d.ts.map +0 -1
  163. package/dist/auth-api/auth-types.js +0 -3
  164. package/dist/auth-api/auth-types.js.map +0 -1
  165. package/dist/auth-api/index.d.ts +0 -4
  166. package/dist/auth-api/index.d.ts.map +0 -1
  167. package/dist/auth-api/index.js +0 -5
  168. package/dist/auth-api/index.js.map +0 -1
  169. package/dist/http-client/api-response.d.ts +0 -16
  170. package/dist/http-client/api-response.d.ts.map +0 -1
  171. package/dist/http-client/api-response.js +0 -2
  172. package/dist/http-client/api-response.js.map +0 -1
package/README.md CHANGED
@@ -1,442 +1,464 @@
1
- # Mero.js - Pure JavaScript SDK for Calimero
1
+ # Mero.js v2 Pure JavaScript SDK for Calimero
2
2
 
3
- A lightweight, universal JavaScript SDK for Calimero that works in both browser and Node.js environments using Web Standards.
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
- - 🌐 **Web Standards First**: Built on `fetch`, `AbortController`, and other Web APIs
10
- - 🔄 **Universal**: Works in browsers, Node.js, and edge runtimes
11
- - 📦 **Zero Dependencies**: No external dependencies, uses native Web APIs
12
- - 🔧 **Dependency Injection**: Flexible and testable architecture
13
- - **Modern**: ES2020+ with TypeScript support
14
- - 🛡️ **Type Safe**: Full TypeScript definitions
15
- - 🔄 **Smart Retry**: Distinguishes user aborts vs timeouts for intelligent retry logic
16
- - 🎯 **Advanced Cancellation**: Signal combination and proper timeout handling
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
- The client **throws** `HTTPError` on any `!response.ok` status. Network/timeout/abort errors are re-thrown as-is:
16
+ ## Installation
23
17
 
24
- ```typescript
25
- try {
26
- const data = await httpClient.get('/api/data');
27
- // Success - data is the parsed response
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
- ### Parsing Defaults & Override
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
- // Get raw Response object
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
- - `HTTPError` with status `429` or `>= 500`
64
- - `TimeoutError` (internal timeouts)
65
- - `TypeError` (network failures)
29
+ const mero = new MeroJs({
30
+ baseUrl: 'http://localhost:2428',
31
+ credentials: {
32
+ username: 'admin',
33
+ password: 'your-password',
34
+ },
35
+ });
66
36
 
67
- **Never retries:**
37
+ // Authenticate
38
+ await mero.authenticate();
68
39
 
69
- - `AbortError` (user/caller aborts)
40
+ // Use the Admin API
41
+ const apps = await mero.admin.applications.listApplications();
42
+ const contexts = await mero.admin.contexts.listContexts();
70
43
 
71
- ```typescript
72
- import { withRetry } from '@calimero-network/mero-js';
73
-
74
- const data = await withRetry(
75
- (attempt) => httpClient.get('/api/data'),
76
- { attempts: 3 }, // Default: 3 attempts
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
- ### Signal Composition
53
+ ## Token Persistence
54
+
55
+ By default, tokens are stored in-memory. Provide a `TokenStorage` to persist across sessions:
81
56
 
82
- The client combines caller signals with internal timeouts:
57
+ ### Browser (localStorage)
83
58
 
84
59
  ```typescript
85
- const userSignal = new AbortController().signal;
86
- const data = await httpClient.get('/api/data', {
87
- signal: userSignal,
88
- timeoutMs: 5000,
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
- - **Caller headers win**: Request-level headers override client defaults
95
- - **Authorization rules**: Only set `Authorization: Bearer ${token}` if caller didn't provide any `authorization` header (case-insensitive)
77
+ // Load stored tokens on startup
78
+ await mero.init();
96
79
 
97
- ### FormData Handling
80
+ if (!mero.isAuthenticated()) {
81
+ await mero.authenticate();
82
+ }
83
+ ```
98
84
 
99
- For `FormData` bodies, the client does **not** set `content-type` (lets browser/undici add the boundary):
85
+ ### React Native (AsyncStorage)
100
86
 
101
87
  ```typescript
102
- const formData = new FormData();
103
- formData.append('file', file);
104
- await httpClient.post('/api/upload', formData);
105
- // No content-type header is set by the client
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
- ### Credentials
109
-
110
- No implicit `same-origin` default. Credentials are only set if explicitly provided:
108
+ ### Tauri (Secure Store)
111
109
 
112
110
  ```typescript
113
- const client = createBrowserHttpClient({
114
- baseUrl: 'https://api.example.com',
115
- credentials: 'include', // Only if you want to include credentials
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
- ## Installation
134
+ ## Token Refresh
120
135
 
121
- ```bash
122
- npm install @calimero-network/mero-js
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
- ## Quick Start
160
+ ## API Reference
126
161
 
127
- ### Browser Usage
162
+ ### MeroJs Class
128
163
 
129
164
  ```typescript
130
- import { createBrowserHttpClient } from '@calimero-network/mero-js';
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 httpClient = createBrowserHttpClient({
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
- // Make requests
140
- try {
141
- const data = await httpClient.get<{ message: string }>('/api/hello');
142
- console.log(data.message);
143
- } catch (error) {
144
- console.error('Request failed:', error);
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
- ### Node.js Usage
193
+ ### Admin API
149
194
 
150
195
  ```typescript
151
- import { createNodeHttpClient } from '@calimero-network/mero-js';
152
- // For Node.js < 18, install undici: npm install undici
153
- import { fetch as undiciFetch } from 'undici';
154
-
155
- const httpClient = createNodeHttpClient({
156
- baseUrl: 'https://api.calimero.network',
157
- fetch: undiciFetch, // Required for Node.js < 18
158
- getAuthToken: async () => process.env.ACCESS_TOKEN,
159
- });
160
-
161
- const data = await httpClient.get<{ message: string }>('/api/hello');
162
- console.log(data.message);
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
- ### Universal Usage
226
+ ### Auth API
166
227
 
167
228
  ```typescript
168
- import { createUniversalHttpClient } from '@calimero-network/mero-js';
169
-
170
- const httpClient = createUniversalHttpClient({
171
- baseUrl: 'https://api.calimero.network',
172
- getAuthToken: async () => {
173
- return typeof window !== 'undefined'
174
- ? localStorage.getItem('access_token')
175
- : process.env.ACCESS_TOKEN;
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
- ## API Reference
248
+ ### JSON-RPC
181
249
 
182
- ### Factory Functions
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
- #### `createBrowserHttpClient(options)`
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
- Creates an HTTP client optimized for browser environments.
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
- #### `createNodeHttpClient(options)`
276
+ ### WebSocket (Real-time Events)
189
277
 
190
- Creates an HTTP client for Node.js environments.
278
+ ```typescript
279
+ const ws = mero.createWebSocket();
191
280
 
192
- #### `createUniversalHttpClient(options)`
281
+ await ws.connect();
193
282
 
194
- Creates an HTTP client that works in both browser and Node.js.
283
+ ws.onEvent((event) => {
284
+ console.log('Event:', event);
285
+ });
195
286
 
196
- ### Options
287
+ ws.onError((error) => {
288
+ console.error('Error:', error);
289
+ });
197
290
 
198
- ```typescript
199
- interface HttpClientOptions {
200
- baseUrl: string; // Base URL for all requests
201
- fetch?: typeof fetch; // Custom fetch implementation (Node.js)
202
- getAuthToken?: () => Promise<string | undefined>; // Token getter
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
- ### HTTP Methods
298
+ ### SSE (Server-Sent Events)
212
299
 
213
300
  ```typescript
214
- // GET request
215
- const response = await httpClient.get<T>('/api/endpoint', init?);
301
+ const sse = mero.createSse();
216
302
 
217
- // POST request
218
- const response = await httpClient.post<T>('/api/endpoint', body?, init?);
303
+ const sessionId = await sse.connect();
219
304
 
220
- // PUT request
221
- const response = await httpClient.put<T>('/api/endpoint', body?, init?);
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
- // PATCH request
227
- const response = await httpClient.patch<T>('/api/endpoint', body?, init?);
309
+ await sse.subscribe(['context-id-1']);
228
310
 
229
- // HEAD request
230
- const response = await httpClient.head<T>('/api/endpoint', init?);
311
+ // Get session info
312
+ const session = await sse.getSession();
231
313
 
232
- // Generic request
233
- const response = await httpClient.request<T>('/api/endpoint', init?);
314
+ // Later...
315
+ sse.disconnect();
234
316
  ```
235
317
 
236
- ### Request Options
318
+ ## Deep Imports
319
+
320
+ For tree-shaking or direct access:
237
321
 
238
322
  ```typescript
239
- interface RequestOptions extends RequestInit {
240
- parse?: 'json' | 'text' | 'blob' | 'arrayBuffer' | 'response';
241
- timeoutMs?: number;
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
- ### Response Format
337
+ ## HTTP Client (Low-Level)
246
338
 
247
- All methods return the parsed data directly or throw errors:
339
+ For custom use cases, use the HTTP client directly:
248
340
 
249
341
  ```typescript
250
- // Success - returns parsed data
251
- const data: T = await httpClient.get<T>('/api/data');
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 httpClient.get('/api/data');
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.statusText}`);
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
- ## Advanced Usage
267
-
268
- ### Custom Transport
366
+ ## Error Handling
269
367
 
270
368
  ```typescript
271
- import { createHttpClient, Transport } from '@calimero-network/mero-js';
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
- const data = await httpClient.get('/api/data');
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.error(`HTTP ${error.status}: ${error.statusText}`);
294
- console.error('URL:', error.url);
295
- console.error('Body:', error.bodyText);
296
- } else if (error.name === 'TimeoutError') {
297
- // Request timed out
298
- console.error('Request timed out');
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
- console.error('Request was cancelled');
302
- } else {
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
- ### Custom Headers
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
- ### Retry with Exponential Backoff
393
+ ```bash
394
+ # Unit tests (236 tests)
395
+ pnpm test
357
396
 
358
- ```typescript
359
- import { withRetry } from '@calimero-network/mero-js';
397
+ # E2E tests with Merobox
398
+ pnpm test:e2e
360
399
 
361
- const data = await withRetry(
362
- (attempt) => httpClient.get('/api/unreliable-endpoint'),
363
- { attempts: 3 }, // Default: 3 attempts
364
- );
400
+ # Full test suite
401
+ pnpm test:all
365
402
  ```
366
403
 
367
- ### Signal Combination
368
-
369
- ```typescript
370
- import { combineSignals, createTimeoutSignal } from '@calimero-network/mero-js';
404
+ ## Development
371
405
 
372
- // Combine multiple abort signals
373
- const userSignal = new AbortController().signal;
374
- const timeoutSignal = createTimeoutSignal(5000);
375
- const combinedSignal = combineSignals([userSignal, timeoutSignal]);
406
+ ```bash
407
+ # Install dependencies
408
+ pnpm install
376
409
 
377
- const data = await httpClient.get('/api/endpoint', {
378
- signal: combinedSignal,
379
- });
380
- ```
410
+ # Build (with strict mode)
411
+ pnpm build
381
412
 
382
- ### CORS and Credentials
413
+ # Lint
414
+ pnpm lint
383
415
 
384
- ```typescript
385
- const httpClient = createBrowserHttpClient({
386
- baseUrl: 'https://api.example.com',
387
- credentials: 'include', // Include cookies in CORS requests
388
- });
416
+ # Type check
417
+ pnpm typecheck
389
418
 
390
- // For APIs that require credentials
391
- const data = await httpClient.get('/api/protected', {
392
- credentials: 'include',
393
- });
419
+ # Generate MSW handlers from OpenAPI
420
+ pnpm generate:msw
394
421
  ```
395
422
 
396
- ## Migration from Axios
397
-
398
- If you're migrating from an Axios-based client:
423
+ ## Bundle Sizes
399
424
 
400
- 1. **Replace imports**: Use the factory functions instead of direct class instantiation
401
- 2. **Update method signatures**: Methods now use `RequestInit` instead of custom options
402
- 3. **Handle responses**: Methods now return parsed data directly or throw errors (no more `ResponseData<T>`)
403
- 4. **Token management**: Use the `getAuthToken` and `onTokenRefresh` callbacks
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` support (Chrome 42+, Firefox 39+, Safari 10.1+)
408
- - Node.js 18+ (native fetch) or Node.js 16+ with `undici`
409
-
410
- ## Bundle Sizes
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
- ## Examples
438
+ ## Migration from v1.x
416
439
 
417
- See the `examples/` directory for complete usage examples:
440
+ ### Breaking Changes
418
441
 
419
- - `browser-example.ts` - Browser-specific usage (designed for browser environments)
420
- - `node-example.ts` - Node.js-specific usage (designed for Node.js environments)
421
- - `universal-example.ts` - Universal usage (works in both browser and Node.js)
442
+ 1. **`clearToken()` is now async**
443
+ ```typescript
444
+ // Before
445
+ mero.clearToken();
446
+
447
+ // After
448
+ await mero.clearToken();
449
+ ```
422
450
 
423
- **Note**: Run `npm run build` before running the examples, as they import from the built library.
451
+ 2. **HTTP client wiring** Token refresh callbacks are now wired automatically
424
452
 
425
- ## Development
453
+ ### New Features
426
454
 
427
- ```bash
428
- # Install dependencies
429
- pnpm install
430
-
431
- # Build
432
- pnpm build
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