@calimero-network/mero-js 2.0.0-beta.1 → 2.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.
Files changed (204) hide show
  1. package/README.md +358 -341
  2. package/dist/admin-api/admin-client.d.ts +90 -0
  3. package/dist/admin-api/admin-client.d.ts.map +1 -0
  4. package/dist/admin-api/admin-client.js +289 -0
  5. package/dist/admin-api/admin-client.js.map +1 -0
  6. package/dist/admin-api/admin-factory.d.ts +8 -0
  7. package/dist/admin-api/admin-factory.d.ts.map +1 -0
  8. package/dist/admin-api/admin-factory.js +42 -0
  9. package/dist/admin-api/admin-factory.js.map +1 -0
  10. package/dist/admin-api/admin-types.d.ts +490 -0
  11. package/dist/admin-api/admin-types.d.ts.map +1 -0
  12. package/dist/admin-api/admin-types.js +4 -0
  13. package/dist/admin-api/admin-types.js.map +1 -0
  14. package/dist/admin-api/index.d.ts +4 -0
  15. package/dist/admin-api/index.d.ts.map +1 -0
  16. package/dist/admin-api/index.js +5 -0
  17. package/dist/admin-api/index.js.map +1 -0
  18. package/dist/auth/index.d.ts +26 -0
  19. package/dist/auth/index.d.ts.map +1 -0
  20. package/dist/auth/index.js +51 -0
  21. package/dist/auth/index.js.map +1 -0
  22. package/dist/auth-api/auth-client.d.ts +34 -0
  23. package/dist/auth-api/auth-client.d.ts.map +1 -0
  24. package/dist/auth-api/auth-client.js +112 -0
  25. package/dist/auth-api/auth-client.js.map +1 -0
  26. package/dist/auth-api/auth-factory.d.ts +8 -0
  27. package/dist/auth-api/auth-factory.d.ts.map +1 -0
  28. package/dist/auth-api/auth-factory.js +42 -0
  29. package/dist/auth-api/auth-factory.js.map +1 -0
  30. package/dist/auth-api/auth-types.d.ts +127 -0
  31. package/dist/auth-api/auth-types.d.ts.map +1 -0
  32. package/dist/auth-api/auth-types.js +3 -0
  33. package/dist/auth-api/auth-types.js.map +1 -0
  34. package/dist/auth-api/index.d.ts +4 -0
  35. package/dist/auth-api/index.d.ts.map +1 -0
  36. package/dist/auth-api/index.js +5 -0
  37. package/dist/auth-api/index.js.map +1 -0
  38. package/dist/cloud/cloud-client.d.ts +20 -0
  39. package/dist/cloud/cloud-client.d.ts.map +1 -0
  40. package/dist/cloud/cloud-client.js +26 -0
  41. package/dist/cloud/cloud-client.js.map +1 -0
  42. package/dist/cloud/index.d.ts +3 -0
  43. package/dist/cloud/index.d.ts.map +1 -0
  44. package/dist/cloud/index.js +2 -0
  45. package/dist/cloud/index.js.map +1 -0
  46. package/dist/events/index.d.ts +5 -0
  47. package/dist/events/index.d.ts.map +1 -0
  48. package/dist/events/index.js +3 -0
  49. package/dist/events/index.js.map +1 -0
  50. package/dist/events/sse.d.ts +41 -0
  51. package/dist/events/sse.d.ts.map +1 -0
  52. package/dist/events/sse.js +237 -0
  53. package/dist/events/sse.js.map +1 -0
  54. package/dist/events/ws.d.ts +42 -0
  55. package/dist/events/ws.d.ts.map +1 -0
  56. package/dist/events/ws.js +178 -0
  57. package/dist/events/ws.js.map +1 -0
  58. package/dist/http-client/api-response.d.ts +16 -0
  59. package/dist/http-client/api-response.d.ts.map +1 -0
  60. package/dist/http-client/api-response.js +2 -0
  61. package/dist/http-client/api-response.js.map +1 -0
  62. package/dist/http-client/index.d.ts +1 -1
  63. package/dist/http-client/index.d.ts.map +1 -1
  64. package/dist/http-client/index.js +1 -2
  65. package/dist/http-client/index.js.map +1 -1
  66. package/dist/http-client/web-client.d.ts +3 -3
  67. package/dist/http-client/web-client.d.ts.map +1 -1
  68. package/dist/http-client/web-client.js +6 -18
  69. package/dist/http-client/web-client.js.map +1 -1
  70. package/dist/index.browser.mjs +2 -1
  71. package/dist/index.browser.mjs.map +4 -4
  72. package/dist/index.cjs +1076 -1467
  73. package/dist/index.cjs.map +4 -4
  74. package/dist/index.d.ts +12 -8
  75. package/dist/index.d.ts.map +1 -1
  76. package/dist/index.js +15 -7
  77. package/dist/index.js.map +1 -1
  78. package/dist/index.mjs +1024 -1421
  79. package/dist/index.mjs.map +4 -4
  80. package/dist/mero-js.d.ts +44 -189
  81. package/dist/mero-js.d.ts.map +1 -1
  82. package/dist/mero-js.js +139 -314
  83. package/dist/mero-js.js.map +1 -1
  84. package/dist/rpc/index.d.ts +22 -0
  85. package/dist/rpc/index.d.ts.map +1 -0
  86. package/dist/rpc/index.js +38 -0
  87. package/dist/rpc/index.js.map +1 -0
  88. package/dist/token-store/index.d.ts +20 -0
  89. package/dist/token-store/index.d.ts.map +1 -0
  90. package/dist/token-store/index.js +62 -0
  91. package/dist/token-store/index.js.map +1 -0
  92. package/package.json +7 -42
  93. package/dist/api/admin/aliases.d.ts +0 -54
  94. package/dist/api/admin/aliases.d.ts.map +0 -1
  95. package/dist/api/admin/aliases.js +0 -43
  96. package/dist/api/admin/aliases.js.map +0 -1
  97. package/dist/api/admin/applications.d.ts +0 -52
  98. package/dist/api/admin/applications.d.ts.map +0 -1
  99. package/dist/api/admin/applications.js +0 -31
  100. package/dist/api/admin/applications.js.map +0 -1
  101. package/dist/api/admin/blobs.d.ts +0 -24
  102. package/dist/api/admin/blobs.d.ts.map +0 -1
  103. package/dist/api/admin/blobs.js +0 -58
  104. package/dist/api/admin/blobs.js.map +0 -1
  105. package/dist/api/admin/capabilities.d.ts +0 -26
  106. package/dist/api/admin/capabilities.d.ts.map +0 -1
  107. package/dist/api/admin/capabilities.js +0 -13
  108. package/dist/api/admin/capabilities.js.map +0 -1
  109. package/dist/api/admin/client.d.ts +0 -63
  110. package/dist/api/admin/client.d.ts.map +0 -1
  111. package/dist/api/admin/client.js +0 -103
  112. package/dist/api/admin/client.js.map +0 -1
  113. package/dist/api/admin/contexts.d.ts +0 -110
  114. package/dist/api/admin/contexts.d.ts.map +0 -1
  115. package/dist/api/admin/contexts.js +0 -61
  116. package/dist/api/admin/contexts.js.map +0 -1
  117. package/dist/api/admin/factory.d.ts +0 -4
  118. package/dist/api/admin/factory.d.ts.map +0 -1
  119. package/dist/api/admin/factory.js +0 -5
  120. package/dist/api/admin/factory.js.map +0 -1
  121. package/dist/api/admin/identity.d.ts +0 -10
  122. package/dist/api/admin/identity.d.ts.map +0 -1
  123. package/dist/api/admin/identity.js +0 -10
  124. package/dist/api/admin/identity.js.map +0 -1
  125. package/dist/api/admin/index.d.ts +0 -23
  126. package/dist/api/admin/index.d.ts.map +0 -1
  127. package/dist/api/admin/index.js +0 -26
  128. package/dist/api/admin/index.js.map +0 -1
  129. package/dist/api/admin/network.d.ts +0 -10
  130. package/dist/api/admin/network.d.ts.map +0 -1
  131. package/dist/api/admin/network.js +0 -9
  132. package/dist/api/admin/network.js.map +0 -1
  133. package/dist/api/admin/proposals.d.ts +0 -49
  134. package/dist/api/admin/proposals.d.ts.map +0 -1
  135. package/dist/api/admin/proposals.js +0 -34
  136. package/dist/api/admin/proposals.js.map +0 -1
  137. package/dist/api/admin/public.d.ts +0 -15
  138. package/dist/api/admin/public.d.ts.map +0 -1
  139. package/dist/api/admin/public.js +0 -18
  140. package/dist/api/admin/public.js.map +0 -1
  141. package/dist/api/admin/tee.d.ts +0 -74
  142. package/dist/api/admin/tee.d.ts.map +0 -1
  143. package/dist/api/admin/tee.js +0 -16
  144. package/dist/api/admin/tee.js.map +0 -1
  145. package/dist/api/auth/client.d.ts +0 -55
  146. package/dist/api/auth/client.d.ts.map +0 -1
  147. package/dist/api/auth/client.js +0 -127
  148. package/dist/api/auth/client.js.map +0 -1
  149. package/dist/api/auth/factory.d.ts +0 -4
  150. package/dist/api/auth/factory.d.ts.map +0 -1
  151. package/dist/api/auth/factory.js +0 -5
  152. package/dist/api/auth/factory.js.map +0 -1
  153. package/dist/api/auth/index.d.ts +0 -4
  154. package/dist/api/auth/index.d.ts.map +0 -1
  155. package/dist/api/auth/index.js +0 -4
  156. package/dist/api/auth/index.js.map +0 -1
  157. package/dist/api/auth/types.d.ts +0 -94
  158. package/dist/api/auth/types.d.ts.map +0 -1
  159. package/dist/api/auth/types.js +0 -4
  160. package/dist/api/auth/types.js.map +0 -1
  161. package/dist/api/index.d.ts +0 -15
  162. package/dist/api/index.d.ts.map +0 -1
  163. package/dist/api/index.js +0 -18
  164. package/dist/api/index.js.map +0 -1
  165. package/dist/api/rpc/client.d.ts +0 -76
  166. package/dist/api/rpc/client.d.ts.map +0 -1
  167. package/dist/api/rpc/client.js +0 -126
  168. package/dist/api/rpc/client.js.map +0 -1
  169. package/dist/api/rpc/index.d.ts +0 -3
  170. package/dist/api/rpc/index.d.ts.map +0 -1
  171. package/dist/api/rpc/index.js +0 -2
  172. package/dist/api/rpc/index.js.map +0 -1
  173. package/dist/api/rpc/types.d.ts +0 -74
  174. package/dist/api/rpc/types.d.ts.map +0 -1
  175. package/dist/api/rpc/types.js +0 -6
  176. package/dist/api/rpc/types.js.map +0 -1
  177. package/dist/api/sse/client.d.ts +0 -76
  178. package/dist/api/sse/client.d.ts.map +0 -1
  179. package/dist/api/sse/client.js +0 -203
  180. package/dist/api/sse/client.js.map +0 -1
  181. package/dist/api/sse/index.d.ts +0 -4
  182. package/dist/api/sse/index.d.ts.map +0 -1
  183. package/dist/api/sse/index.js +0 -2
  184. package/dist/api/sse/index.js.map +0 -1
  185. package/dist/api/sse/types.d.ts +0 -35
  186. package/dist/api/sse/types.d.ts.map +0 -1
  187. package/dist/api/sse/types.js +0 -6
  188. package/dist/api/sse/types.js.map +0 -1
  189. package/dist/api/utils.d.ts +0 -68
  190. package/dist/api/utils.d.ts.map +0 -1
  191. package/dist/api/utils.js +0 -83
  192. package/dist/api/utils.js.map +0 -1
  193. package/dist/api/ws/client.d.ts +0 -72
  194. package/dist/api/ws/client.d.ts.map +0 -1
  195. package/dist/api/ws/client.js +0 -202
  196. package/dist/api/ws/client.js.map +0 -1
  197. package/dist/api/ws/index.d.ts +0 -4
  198. package/dist/api/ws/index.d.ts.map +0 -1
  199. package/dist/api/ws/index.js +0 -2
  200. package/dist/api/ws/index.js.map +0 -1
  201. package/dist/api/ws/types.d.ts +0 -32
  202. package/dist/api/ws/types.d.ts.map +0 -1
  203. package/dist/api/ws/types.js +0 -6
  204. package/dist/api/ws/types.js.map +0 -1
package/README.md CHANGED
@@ -1,464 +1,481 @@
1
- # Mero.js v2 Pure JavaScript SDK for Calimero
1
+ # Mero.js - Pure JavaScript SDK for Calimero
2
2
 
3
- A lightweight, universal JavaScript SDK for interacting with Calimero nodes. Works in browsers, Node.js, React Native, and Tauri.
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.
4
6
 
5
7
  ## Features
6
8
 
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
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
15
17
 
16
- ## Installation
18
+ ## Behavior
17
19
 
18
- ```bash
19
- npm install @calimero-network/mero-js
20
- # or
21
- pnpm add @calimero-network/mero-js
22
- ```
20
+ ### Error Model (Throwing)
23
21
 
24
- ## Quick Start
22
+ The client **throws** `HTTPError` on any `!response.ok` status. Network/timeout/abort errors are re-thrown as-is:
25
23
 
26
24
  ```typescript
27
- import { MeroJs } from '@calimero-network/mero-js';
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
+ }
40
+ ```
28
41
 
29
- const mero = new MeroJs({
30
- baseUrl: 'http://localhost:2428',
31
- credentials: {
32
- username: 'admin',
33
- password: 'your-password',
34
- },
35
- });
42
+ ### Parsing Defaults & Override
36
43
 
37
- // Authenticate
38
- await mero.authenticate();
44
+ Each method accepts optional `{ parse?: 'json'|'text'|'blob'|'arrayBuffer'|'response' }`:
39
45
 
40
- // Use the Admin API
41
- const apps = await mero.admin.applications.listApplications();
42
- const contexts = await mero.admin.contexts.listContexts();
46
+ **Default parsing rules:**
43
47
 
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'
50
- );
51
- ```
48
+ - `Content-Type: application/json` → parse as JSON
49
+ - `Content-Type: text/*` parse as text
50
+ - Other types → parse as `arrayBuffer`
52
51
 
53
- ## Token Persistence
52
+ **Override:**
54
53
 
55
- By default, tokens are stored in-memory. Provide a `TokenStorage` to persist across sessions:
54
+ ```typescript
55
+ // Get raw Response object
56
+ const response = await httpClient.get('/api/data', { parse: 'response' });
57
+ ```
56
58
 
57
- ### Browser (localStorage)
59
+ ### Retry Policy (`withRetry`)
58
60
 
59
- ```typescript
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
- },
75
- });
61
+ The `withRetry` helper retries on:
76
62
 
77
- // Load stored tokens on startup
78
- await mero.init();
63
+ - `HTTPError` with status `429` or `>= 500`
64
+ - `TimeoutError` (internal timeouts)
65
+ - `TypeError` (network failures)
79
66
 
80
- if (!mero.isAuthenticated()) {
81
- await mero.authenticate();
82
- }
83
- ```
67
+ **Never retries:**
84
68
 
85
- ### React Native (AsyncStorage)
69
+ - `AbortError` (user/caller aborts)
86
70
 
87
71
  ```typescript
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
- });
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
77
+ );
106
78
  ```
107
79
 
108
- ### Tauri (Secure Store)
80
+ ### Signal Composition
81
+
82
+ The client combines caller signals with internal timeouts:
109
83
 
110
84
  ```typescript
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
- },
85
+ const userSignal = new AbortController().signal;
86
+ const data = await httpClient.get('/api/data', {
87
+ signal: userSignal,
88
+ timeoutMs: 5000,
131
89
  });
132
90
  ```
133
91
 
134
- ## Token Refresh
92
+ ### Header Precedence & Authorization
135
93
 
136
- The SDK automatically handles token refresh in two ways:
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)
137
96
 
138
- 1. **Preemptive** — Refreshes 5 minutes before expiry
139
- 2. **Reactive** — On 401 with `x-auth-error: token_expired`, automatically refreshes and retries
97
+ ### FormData Handling
140
98
 
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
- └─────────────────────────────────────────────────┘
99
+ For `FormData` bodies, the client does **not** set `content-type` (lets browser/undici add the boundary):
100
+
101
+ ```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
158
106
  ```
159
107
 
160
- ## API Reference
108
+ ### Credentials
161
109
 
162
- ### MeroJs Class
110
+ No implicit `same-origin` default. Credentials are only set if explicitly provided:
163
111
 
164
112
  ```typescript
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
- }
173
-
174
- const mero = new MeroJs(config);
175
-
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
113
+ const client = createBrowserHttpClient({
114
+ baseUrl: 'https://api.example.com',
115
+ credentials: 'include', // Only if you want to include credentials
116
+ });
117
+ ```
182
118
 
183
- // API Clients
184
- mero.auth // Auth API
185
- mero.admin // Admin API
186
- mero.rpc // JSON-RPC client
119
+ ## Installation
187
120
 
188
- // Real-time
189
- mero.createWebSocket(); // WebSocket client
190
- mero.createSse(); // SSE client
121
+ ```bash
122
+ npm install @calimero-network/mero-js
191
123
  ```
192
124
 
193
- ### Admin API
125
+ ## Quick Start
126
+
127
+ ### Browser Usage
194
128
 
195
129
  ```typescript
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();
224
- ```
130
+ import { createBrowserHttpClient } from '@calimero-network/mero-js';
225
131
 
226
- ### Auth API
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
+ });
227
138
 
228
- ```typescript
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);
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
+ }
246
146
  ```
247
147
 
248
- ### JSON-RPC
148
+ ### Node.js Usage
249
149
 
250
150
  ```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
- );
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
+ });
258
160
 
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
- );
161
+ const data = await httpClient.get<{ message: string }>('/api/hello');
162
+ console.log(data.message);
163
+ ```
266
164
 
267
- // Generic execute
268
- const result = await mero.rpc.execute({
269
- contextId: 'context-id',
270
- method: 'myMethod',
271
- args: { foo: 'bar' },
272
- executorPublicKey: 'ed25519:...',
165
+ ### Universal Usage
166
+
167
+ ```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
+ },
273
177
  });
274
178
  ```
275
179
 
276
- ### WebSocket (Real-time Events)
180
+ ## API Reference
277
181
 
278
- ```typescript
279
- const ws = mero.createWebSocket();
182
+ ### Factory Functions
280
183
 
281
- await ws.connect();
184
+ #### `createBrowserHttpClient(options)`
282
185
 
283
- ws.onEvent((event) => {
284
- console.log('Event:', event);
285
- });
186
+ Creates an HTTP client optimized for browser environments.
286
187
 
287
- ws.onError((error) => {
288
- console.error('Error:', error);
289
- });
188
+ #### `createNodeHttpClient(options)`
189
+
190
+ Creates an HTTP client for Node.js environments.
290
191
 
291
- await ws.subscribe(['context-id-1', 'context-id-2']);
192
+ #### `createUniversalHttpClient(options)`
292
193
 
293
- // Later...
294
- await ws.unsubscribe(['context-id-1']);
295
- ws.disconnect();
194
+ Creates an HTTP client that works in both browser and Node.js.
195
+
196
+ ### Options
197
+
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
+ }
296
209
  ```
297
210
 
298
- ### SSE (Server-Sent Events)
211
+ ### HTTP Methods
299
212
 
300
213
  ```typescript
301
- const sse = mero.createSse();
214
+ // GET request
215
+ const response = await httpClient.get<T>('/api/endpoint', init?);
302
216
 
303
- const sessionId = await sse.connect();
217
+ // POST request
218
+ const response = await httpClient.post<T>('/api/endpoint', body?, init?);
304
219
 
305
- sse.onEvent((event) => {
306
- console.log('Event:', event);
307
- });
220
+ // PUT request
221
+ const response = await httpClient.put<T>('/api/endpoint', body?, init?);
308
222
 
309
- await sse.subscribe(['context-id-1']);
223
+ // DELETE request
224
+ const response = await httpClient.delete<T>('/api/endpoint', init?);
310
225
 
311
- // Get session info
312
- const session = await sse.getSession();
226
+ // PATCH request
227
+ const response = await httpClient.patch<T>('/api/endpoint', body?, init?);
313
228
 
314
- // Later...
315
- sse.disconnect();
316
- ```
229
+ // HEAD request
230
+ const response = await httpClient.head<T>('/api/endpoint', init?);
317
231
 
318
- ## Deep Imports
232
+ // Generic request
233
+ const response = await httpClient.request<T>('/api/endpoint', init?);
234
+ ```
319
235
 
320
- For tree-shaking or direct access:
236
+ ### Request Options
321
237
 
322
238
  ```typescript
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';
239
+ interface RequestOptions extends RequestInit {
240
+ parse?: 'json' | 'text' | 'blob' | 'arrayBuffer' | 'response';
241
+ timeoutMs?: number;
242
+ }
335
243
  ```
336
244
 
337
- ## HTTP Client (Low-Level)
245
+ ### Response Format
338
246
 
339
- For custom use cases, use the HTTP client directly:
247
+ All methods return the parsed data directly or throw errors:
340
248
 
341
249
  ```typescript
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
- });
250
+ // Success - returns parsed data
251
+ const data: T = await httpClient.get<T>('/api/data');
356
252
 
253
+ // Error - throws HTTPError or other errors
357
254
  try {
358
- const data = await http.get<MyType>('/api/endpoint');
255
+ const data = await httpClient.get('/api/data');
359
256
  } catch (error) {
360
257
  if (error instanceof HTTPError) {
361
- console.log(`HTTP ${error.status}: ${error.bodyText}`);
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);
362
262
  }
363
263
  }
364
264
  ```
365
265
 
366
- ## Error Handling
266
+ ## Advanced Usage
267
+
268
+ ### Custom Transport
367
269
 
368
270
  ```typescript
369
- import { HTTPError, ApiResponseError } from '@calimero-network/mero-js';
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
+ };
370
279
 
280
+ const httpClient = createHttpClient(transport);
281
+ ```
282
+
283
+ ### Error Handling
284
+
285
+ ```typescript
371
286
  try {
372
- await mero.admin.contexts.getContext('invalid-id');
287
+ const data = await httpClient.get('/api/data');
288
+ // Success - data is the parsed response
289
+ console.log(data);
373
290
  } catch (error) {
374
291
  if (error instanceof HTTPError) {
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);
383
- } else if (error.name === 'AbortError') {
384
- // Request was cancelled
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);
385
296
  } else if (error.name === 'TimeoutError') {
386
297
  // Request timed out
298
+ console.error('Request timed out');
299
+ } else if (error.name === 'AbortError') {
300
+ // Request was cancelled
301
+ console.error('Request was cancelled');
302
+ } else {
303
+ // Network or other errors
304
+ console.error('Request failed:', error);
387
305
  }
388
306
  }
389
307
  ```
390
308
 
391
- ## Testing
309
+ ### Custom Headers
392
310
 
393
- ```bash
394
- # Unit tests (236 tests)
395
- pnpm test
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
+ ```
396
319
 
397
- # E2E tests with Merobox
398
- pnpm test:e2e
320
+ ### Request Cancellation
399
321
 
400
- # Full test suite
401
- pnpm test:all
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
+ });
402
331
  ```
403
332
 
404
- ## Development
333
+ ### FormData Support
405
334
 
406
- ```bash
407
- # Install dependencies
408
- pnpm install
335
+ ```typescript
336
+ const formData = new FormData();
337
+ formData.append('file', fileInput.files[0]);
338
+ formData.append('name', 'John Doe');
409
339
 
410
- # Build (with strict mode)
411
- pnpm build
340
+ const data = await httpClient.post('/api/upload', formData);
341
+ // Content-Type is set by the browser/undici with proper boundary
342
+ ```
412
343
 
413
- # Lint
414
- pnpm lint
344
+ ### Response Parsing
415
345
 
416
- # Type check
417
- pnpm typecheck
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' });
418
351
 
419
- # Generate MSW handlers from OpenAPI
420
- pnpm generate:msw
352
+ // Auto-detection based on Content-Type (default)
353
+ const autoData = await httpClient.get('/api/data');
421
354
  ```
422
355
 
423
- ## Bundle Sizes
356
+ ### Retry with Exponential Backoff
357
+
358
+ ```typescript
359
+ import { withRetry } from '@calimero-network/mero-js';
360
+
361
+ const data = await withRetry(
362
+ (attempt) => httpClient.get('/api/unreliable-endpoint'),
363
+ { attempts: 3 }, // Default: 3 attempts
364
+ );
365
+ ```
366
+
367
+ ### Signal Combination
368
+
369
+ ```typescript
370
+ import { combineSignals, createTimeoutSignal } from '@calimero-network/mero-js';
371
+
372
+ // Combine multiple abort signals
373
+ const userSignal = new AbortController().signal;
374
+ const timeoutSignal = createTimeoutSignal(5000);
375
+ const combinedSignal = combineSignals([userSignal, timeoutSignal]);
376
+
377
+ const data = await httpClient.get('/api/endpoint', {
378
+ signal: combinedSignal,
379
+ });
380
+ ```
381
+
382
+ ### CORS and Credentials
383
+
384
+ ```typescript
385
+ const httpClient = createBrowserHttpClient({
386
+ baseUrl: 'https://api.example.com',
387
+ credentials: 'include', // Include cookies in CORS requests
388
+ });
389
+
390
+ // For APIs that require credentials
391
+ const data = await httpClient.get('/api/protected', {
392
+ credentials: 'include',
393
+ });
394
+ ```
395
+
396
+ ## Migration from Axios
397
+
398
+ If you're migrating from an Axios-based client:
424
399
 
425
- | Format | Size | Gzipped |
426
- |--------|------|---------|
427
- | ESM | ~57kb | ~15kb |
428
- | CJS | ~59kb | ~16kb |
429
- | Browser (minified) | ~29kb | ~9kb |
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
430
404
 
431
405
  ## Browser Support
432
406
 
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'`)
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)
414
+
415
+ ## Examples
416
+
417
+ See the `examples/` directory for complete usage examples:
418
+
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)
422
+
423
+ **Note**: Run `npm run build` before running the examples, as they import from the built library.
424
+
425
+ ## Admin API
426
+
427
+ The `AdminApiClient` provides methods for managing Calimero node resources:
428
+
429
+ ### Namespaces (application instances)
430
+ ```typescript
431
+ const namespaces = await admin.listNamespaces();
432
+ const identity = await admin.getNamespaceIdentity(namespaceId);
433
+ const appNamespaces = await admin.listNamespacesForApplication(appId);
434
+ ```
437
435
 
438
- ## Migration from v1.x
436
+ ### Groups (governance boundaries)
437
+ ```typescript
438
+ const groups = await admin.listGroups();
439
+ const group = await admin.createGroup({ applicationId: '...' });
440
+ const info = await admin.getGroupInfo(groupId);
441
+ const members = await admin.listGroupMembers(groupId);
442
+ const contexts = await admin.listGroupContexts(groupId);
443
+ ```
439
444
 
440
- ### Breaking Changes
445
+ ### Contexts (WASM execution instances)
446
+ ```typescript
447
+ // Single-service app
448
+ const ctx = await admin.createContext({ applicationId: '...' });
441
449
 
442
- 1. **`clearToken()` is now async**
443
- ```typescript
444
- // Before
445
- mero.clearToken();
446
-
447
- // After
448
- await mero.clearToken();
449
- ```
450
+ // Multi-service app -- specify which service to run
451
+ const ctx = await admin.createContext({
452
+ applicationId: '...',
453
+ serviceName: 'chat',
454
+ });
455
+ ```
450
456
 
451
- 2. **HTTP client wiring** — Token refresh callbacks are now wired automatically
457
+ ### Cloud (TEE High Availability)
458
+ ```typescript
459
+ import { CloudClient } from '@calimero-network/mero-js';
460
+ const cloud = new CloudClient();
461
+ cloud.enableHA({ groupId, contextId, redirectUrl });
462
+ ```
452
463
 
453
- ### New Features
464
+ ## Development
454
465
 
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
466
+ ```bash
467
+ # Install dependencies
468
+ pnpm install
469
+
470
+ # Build
471
+ pnpm build
472
+
473
+ # Test
474
+ pnpm test
475
+
476
+ # Lint
477
+ pnpm lint
478
+ ```
462
479
 
463
480
  ## License
464
481