@calimero-network/mero-react 1.0.0-beta.1 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,220 +1,296 @@
1
1
  # @calimero-network/mero-react
2
2
 
3
- React bindings for [MeroJs](https://github.com/calimero-network/mero-js) - the official Calimero Network SDK.
3
+ React bindings for [@calimero-network/mero-js](../mero-js) the Calimero Network SDK.
4
4
 
5
- ## Features
6
-
7
- - **MeroProvider** - React Context provider that manages MeroJs instance
8
- - **useMero** - Hook to access MeroJs and authentication state
9
- - **ConnectButton** - Ready-to-use connection button component
10
- - **LoginModal** - Modal for node selection (local/remote)
11
- - **localStorage TokenStorage** - Built-in token persistence
5
+ No UI components. No styled-components. No axios. Just a provider, hooks, and storage helpers.
12
6
 
13
7
  ## Installation
14
8
 
15
9
  ```bash
16
- npm install @calimero-network/mero-react @calimero-network/mero-js
17
- # or
18
10
  pnpm add @calimero-network/mero-react @calimero-network/mero-js
19
11
  ```
20
12
 
21
- ## Quick Start
13
+ Peer dependencies: `react` ^18 || ^19, `react-dom` ^18 || ^19.
14
+
15
+ ## Quick start
22
16
 
23
17
  ```tsx
24
- import { MeroProvider, ConnectButton, useMero, AppMode } from '@calimero-network/mero-react';
18
+ import { MeroProvider, useMero, useExecute, useSubscription, AppMode } from '@calimero-network/mero-react';
25
19
 
26
20
  function App() {
27
21
  return (
28
- <MeroProvider
29
- mode={AppMode.SingleContext}
30
- packageName="my-app"
31
- >
22
+ <MeroProvider mode={AppMode.SingleContext} packageName="com.calimero.my-app">
32
23
  <MyApp />
33
24
  </MeroProvider>
34
25
  );
35
26
  }
36
27
 
37
28
  function MyApp() {
38
- const { mero, isAuthenticated, isLoading } = useMero();
29
+ const { isAuthenticated, connectToNode, logout, contextId, contextIdentity } = useMero();
39
30
 
40
- if (isLoading) {
41
- return <div>Loading...</div>;
31
+ if (!isAuthenticated) {
32
+ return <button onClick={() => connectToNode('http://localhost:4001')}>Connect</button>;
42
33
  }
43
34
 
44
- return (
45
- <div>
46
- <ConnectButton />
47
-
48
- {isAuthenticated && mero && (
49
- <Dashboard mero={mero} />
50
- )}
51
- </div>
52
- );
35
+ return <Dashboard />;
53
36
  }
54
37
 
55
- function Dashboard({ mero }) {
56
- const [contexts, setContexts] = useState([]);
38
+ function Dashboard() {
39
+ const { contextId, contextIdentity } = useMero();
40
+ const { execute, loading } = useExecute(contextId, contextIdentity);
41
+ const [items, setItems] = useState([]);
57
42
 
58
- useEffect(() => {
59
- // Access MeroJs APIs through the mero instance
60
- mero.admin.contexts.listContexts()
61
- .then(response => setContexts(response.contexts));
62
- }, [mero]);
43
+ // Real-time updates via SSE
44
+ useSubscription(
45
+ contextId ? [contextId] : [],
46
+ () => fetchItems(),
47
+ );
48
+
49
+ const fetchItems = async () => {
50
+ const data = await execute('list');
51
+ if (data) setItems(data);
52
+ };
53
+
54
+ const addItem = async (title: string) => {
55
+ await execute('add', { title });
56
+ await fetchItems();
57
+ };
63
58
 
64
59
  return (
65
- <ul>
66
- {contexts.map(ctx => (
67
- <li key={ctx.contextId}>{ctx.contextId}</li>
68
- ))}
69
- </ul>
60
+ <div>
61
+ {loading && <p>Loading...</p>}
62
+ {items.map(item => <div key={item.id}>{item.title}</div>)}
63
+ <button onClick={() => addItem('New item')}>Add</button>
64
+ </div>
70
65
  );
71
66
  }
72
67
  ```
73
68
 
74
- ## Provider Configuration
69
+ ## API reference
70
+
71
+ ### `<MeroProvider>`
72
+
73
+ Wraps your app with a MeroJs instance, auth state, and SSE connectivity.
75
74
 
76
75
  ```tsx
77
76
  <MeroProvider
78
- // Required: Application mode
79
- mode={AppMode.SingleContext | AppMode.MultiContext | AppMode.Admin}
80
-
81
- // Package-based (recommended)
82
- packageName="@my-org/my-app"
83
- packageVersion="1.0.0" // optional, defaults to latest
84
- registryUrl="https://registry.calimero.network" // optional
85
-
86
- // OR Legacy: Application ID
87
- applicationId="app-hash-id"
88
- applicationPath="/my-app"
89
-
90
- // Optional
91
- eventStreamMode={EventStreamMode.WebSocket | EventStreamMode.SSE}
92
- timeoutMs={30000}
77
+ mode={AppMode.SingleContext} // required
78
+ packageName="com.calimero.my-app" // for package-based apps
79
+ timeoutMs={30000} // optional, default 30s
93
80
  >
94
81
  {children}
95
82
  </MeroProvider>
96
83
  ```
97
84
 
98
- ## Application Modes
85
+ Props (`MeroProviderConfig & { children }`):
86
+
87
+ | Prop | Type | Required | Description |
88
+ |------|------|----------|-------------|
89
+ | `mode` | `AppMode` | Yes | `SingleContext`, `MultiContext`, or `Admin` |
90
+ | `packageName` | `string` | No | Package name for registry/node lookup |
91
+ | `packageVersion` | `string` | No | Specific version (defaults to latest) |
92
+ | `registryUrl` | `string` | No | Registry URL override |
93
+ | `timeoutMs` | `number` | No | HTTP request timeout (default 30000) |
94
+
95
+ Modes and their permissions:
99
96
 
100
- | Mode | Permissions | Use Case |
97
+ | Mode | Permissions | Use case |
101
98
  |------|-------------|----------|
102
99
  | `SingleContext` | `context:execute` | Apps that work with one context |
103
100
  | `MultiContext` | `context:create`, `context:list`, `context:execute` | Apps managing multiple contexts |
104
101
  | `Admin` | `admin` | Admin dashboards, dev tools |
105
102
 
106
- ## useMero Hook
103
+ Auth flow: when `connectToNode(url)` is called, the provider redirects to the node's auth page. After login, the node redirects back with tokens in the URL hash. The provider processes these once (StrictMode-safe via ref) and sets `isAuthenticated = true`.
104
+
105
+ Online detection: the provider opens an SSE connection to the node after auth. `isOnline` reflects the SSE connection state — no polling.
106
+
107
+ ### `useMero()`
108
+
109
+ Access the MeroJs instance, auth state, and actions.
107
110
 
108
111
  ```tsx
109
112
  const {
110
- mero, // MeroJs instance (null if not connected)
111
- isAuthenticated, // Whether user is logged in
112
- isOnline, // Whether connection is healthy
113
- isLoading, // Initial loading state
114
- nodeUrl, // Current node URL
115
- applicationId, // Resolved application ID
116
- connectToNode, // Connect to a node URL and start auth
117
- logout, // Clear session
113
+ mero, // MeroJs | null the SDK instance
114
+ isAuthenticated, // boolean
115
+ isOnline, // boolean — SSE connection state
116
+ isLoading, // boolean initial session restore
117
+ nodeUrl, // string | null
118
+ applicationId, // string | null — resolved from auth callback
119
+ contextId, // string | null from auth callback
120
+ contextIdentity, // string | null — executor public key from auth callback
121
+ connectToNode, // (url: string) => void — starts auth redirect
122
+ logout, // () => void — clears tokens and state
118
123
  } = useMero();
119
124
  ```
120
125
 
121
- ## Connect Button
126
+ Through `mero` you access the full MeroJs API:
122
127
 
123
128
  ```tsx
124
- import { ConnectButton, ConnectionType } from '@calimero-network/mero-react';
129
+ // Admin API (flat methods, NOT nested)
130
+ await mero.admin.healthCheck();
131
+ await mero.admin.getContexts();
132
+ await mero.admin.getContext(contextId);
133
+ await mero.admin.getContextIdentitiesOwned(contextId);
134
+ await mero.admin.listApplications();
135
+ await mero.admin.getApplication(appId);
136
+ await mero.admin.installApplication(request);
137
+ await mero.admin.createContext(request);
138
+ await mero.admin.uploadBlob(request);
139
+ await mero.admin.getPeersCount();
140
+
141
+ // Auth API
142
+ await mero.auth.getProviders();
143
+ await mero.auth.generateTokens(request);
144
+ await mero.auth.refreshToken(request);
125
145
 
126
- // Default: shows local/remote options
127
- <ConnectButton />
128
-
129
- // Only remote
130
- <ConnectButton connectionType={ConnectionType.Remote} />
146
+ // RPC
147
+ await mero.rpc.execute({ contextId, method, argsJson, executorPublicKey });
131
148
 
132
- // Only local
133
- <ConnectButton connectionType={ConnectionType.Local} />
149
+ // SSE events
150
+ mero.events.connect();
151
+ mero.events.subscribe(contextIds);
152
+ mero.events.on('event', handler);
134
153
 
135
- // Custom URL (skip modal)
136
- <ConnectButton connectionType={{ type: ConnectionType.Custom, url: 'https://my-node.com' }} />
154
+ // Tokens
155
+ mero.getTokenData(); // { access_token, refresh_token, expires_at } | null
156
+ mero.isAuthenticated(); // boolean
137
157
  ```
138
158
 
139
- ## Styling
159
+ ### `useExecute(contextId, executorId)`
140
160
 
141
- The components come with default styles. You can override them by:
161
+ Wraps `mero.rpc.execute()` with loading/error state. Unmount-safe.
142
162
 
143
- 1. **CSS Variables**:
144
- ```css
145
- :root {
146
- --mero-bg: #1a1a2e;
147
- --mero-text: #eaeaea;
148
- --mero-accent: #7b68ee;
149
- --mero-success: #a8e640;
150
- --mero-error: #ff4d4d;
151
- /* ... see styles.css for all variables */
152
- }
163
+ ```tsx
164
+ const { execute, loading, error } = useExecute(contextId, contextIdentity);
165
+
166
+ // Generic typed
167
+ const todos = await execute<Todo[]>('list');
168
+ await execute('add', { title: 'Buy milk' });
169
+ await execute('toggle', { id: '1' });
153
170
  ```
154
171
 
155
- 2. **Custom Classes**:
172
+ | Return | Type | Description |
173
+ |--------|------|-------------|
174
+ | `execute` | `<T>(method, params?) => Promise<T \| null>` | Call a contract method |
175
+ | `loading` | `boolean` | Request in flight |
176
+ | `error` | `Error \| null` | Last error |
177
+
178
+ ### `useSubscription(contextIds, callback)`
179
+
180
+ Manages SSE event subscription lifecycle. StrictMode-safe — connects once per MeroJs instance, cleans up on unmount.
181
+
156
182
  ```tsx
157
- <ConnectButton className="my-custom-button" />
183
+ useSubscription(
184
+ contextId ? [contextId] : [],
185
+ (event) => {
186
+ console.log('Context event:', event.contextId, event.data);
187
+ refreshData();
188
+ },
189
+ );
158
190
  ```
159
191
 
160
- 3. **Replace Components**: Create your own using `useMero()` hook.
192
+ | Param | Type | Description |
193
+ |-------|------|-------------|
194
+ | `contextIds` | `string[]` | Context IDs to subscribe to (empty array = no subscription) |
195
+ | `callback` | `(event: SseEventData) => void` | Called on each context event |
161
196
 
162
- ## Storage
197
+ The SSE connection is shared — multiple `useSubscription` hooks reuse the same connection. The first one to mount calls `connect()`, subsequent ones just add handlers.
163
198
 
164
- Built-in localStorage utilities:
199
+ ### `useContexts(applicationId?)`
200
+
201
+ Fetches contexts from the node, optionally filtered by application ID.
202
+
203
+ ```tsx
204
+ const { contexts, loading, error, refetch } = useContexts(applicationId);
205
+
206
+ // contexts: Array<{ contextId: string; applicationId: string }>
207
+ ```
208
+
209
+ ### Storage helpers
210
+
211
+ Persist/read node URL, application ID, context ID, and context identity in localStorage.
165
212
 
166
213
  ```tsx
167
214
  import {
168
- localStorageTokenStorage,
169
- getNodeUrl,
170
- setNodeUrl,
171
- getApplicationId,
215
+ getNodeUrl, setNodeUrl, clearNodeUrl,
216
+ getApplicationId, setApplicationId, clearApplicationId,
172
217
  clearAllStorage,
173
218
  } from '@calimero-network/mero-react';
174
-
175
- // localStorageTokenStorage implements MeroJs TokenStorage interface
176
- // It's automatically used by MeroProvider
177
219
  ```
178
220
 
179
- ## Accessing MeroJs APIs
221
+ These are used internally by `MeroProvider` but exported for apps that need direct access.
180
222
 
181
- All MeroJs APIs are available through the `mero` instance:
223
+ ### Re-exports from mero-js
224
+
225
+ mero-react re-exports everything from mero-js via `export * from '@calimero-network/mero-js'`. Any new API added to mero-js is automatically available from mero-react — no manual sync needed.
182
226
 
183
227
  ```tsx
184
- const { mero } = useMero();
228
+ // All of these work from a single import
229
+ import {
230
+ MeroProvider, useMero, useExecute, useSubscription, // react
231
+ MeroJs, RpcClient, SseClient, WsClient, // core
232
+ parseAuthCallback, buildAuthLoginUrl, // auth helpers
233
+ LocalStorageTokenStore, MemoryTokenStore, // token stores
234
+ } from '@calimero-network/mero-react';
235
+ ```
185
236
 
186
- // Admin APIs
187
- await mero.admin.applications.listApplications();
188
- await mero.admin.contexts.createContext({ applicationId, ... });
189
- await mero.admin.blobs.uploadBlob(file);
237
+ ## Enums
190
238
 
191
- // Auth APIs
192
- await mero.auth.getHealth();
193
- await mero.auth.refreshToken();
239
+ ```tsx
194
240
 
195
- // RPC
196
- await mero.rpc.execute({ contextId, method, args, executorPublicKey });
241
+ AppMode.SingleContext // 'single-context'
242
+ AppMode.MultiContext // 'multi-context'
243
+ AppMode.Admin // 'admin'
197
244
 
198
- // WebSocket subscriptions
199
- mero.ws.subscribe(contextId, onEvent);
245
+ ConnectionType.Custom // 'custom'
246
+ ConnectionType.Local // 'local'
247
+ ConnectionType.Remote // 'remote'
200
248
 
201
- // SSE subscriptions
202
- mero.sse.subscribe(contextId, onEvent);
203
249
  ```
204
250
 
205
- ## TypeScript
206
-
207
- Full TypeScript support:
251
+ ## Types
208
252
 
209
253
  ```tsx
210
254
  import type {
211
- MeroContextValue,
212
- MeroProviderConfig,
213
- AppContext,
214
- ExecutionResult,
255
+ MeroContextValue, // useMero() return type
256
+ MeroProviderConfig, // MeroProvider props (without children)
257
+ MeroProviderProps, // MeroProvider props (with children)
258
+ CustomConnectionConfig,// { type: ConnectionType.Custom, url: string }
259
+ AppContext, // { contextId, executorId, applicationId }
260
+ ExecutionResult, // { success, result?, error? }
215
261
  } from '@calimero-network/mero-react';
216
262
  ```
217
263
 
264
+ ## Full exports list
265
+
266
+ ```
267
+ // Provider & hooks (mero-react)
268
+ MeroProvider, useMero, MeroContext
269
+ useExecute, useSubscription, useContexts
270
+
271
+ // Enums (mero-react)
272
+
273
+ // Types (mero-react)
274
+ MeroContextValue, MeroProviderConfig, MeroProviderProps
275
+ CustomConnectionConfig, AppContext, ExecutionResult
276
+
277
+ // Storage (mero-react)
278
+ localStorageTokenStorage
279
+ getNodeUrl, setNodeUrl, clearNodeUrl
280
+ getApplicationId, setApplicationId, clearApplicationId
281
+ clearAllStorage
282
+
283
+ // Everything from @calimero-network/mero-js (auto re-exported)
284
+ MeroJs, createMeroJs, MeroJsConfig, TokenData
285
+ RpcClient, RpcError, ExecuteParams
286
+ SseClient, SseEventData, WsClient, WsEventData
287
+ AuthApiClient, AdminApiClient
288
+ LocalStorageTokenStore, MemoryTokenStore, TokenStore
289
+ parseAuthCallback, buildAuthLoginUrl, AuthCallbackResult, AuthLoginOptions
290
+ WebHttpClient, HttpClient, HTTPError
291
+ // ...and all other mero-js exports
292
+ ```
293
+
218
294
  ## License
219
295
 
220
296
  MIT