@xtr-dev/rondevu-client 0.13.0 → 0.17.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/dist/api.js CHANGED
@@ -1,322 +1,279 @@
1
1
  /**
2
- * Rondevu API Client - Single class for all API endpoints
2
+ * Rondevu API Client - RPC interface
3
3
  */
4
- import * as ed25519 from '@noble/ed25519';
5
- // Set SHA-512 hash function for ed25519 (required in @noble/ed25519 v3+)
6
- ed25519.hashes.sha512Async = async (message) => {
7
- return new Uint8Array(await crypto.subtle.digest('SHA-512', message));
8
- };
4
+ import { WebCryptoAdapter } from './web-crypto-adapter.js';
5
+ import { RpcBatcher } from './rpc-batcher.js';
9
6
  /**
10
- * Helper: Convert Uint8Array to base64 string
11
- */
12
- function bytesToBase64(bytes) {
13
- const binString = Array.from(bytes, byte => String.fromCodePoint(byte)).join('');
14
- return btoa(binString);
15
- }
16
- /**
17
- * Helper: Convert base64 string to Uint8Array
18
- */
19
- function base64ToBytes(base64) {
20
- const binString = atob(base64);
21
- return Uint8Array.from(binString, char => char.codePointAt(0));
22
- }
23
- /**
24
- * RondevuAPI - Complete API client for Rondevu signaling server
7
+ * RondevuAPI - RPC-based API client for Rondevu signaling server
25
8
  */
26
9
  export class RondevuAPI {
27
- constructor(baseUrl, credentials) {
10
+ constructor(baseUrl, username, keypair, cryptoAdapter, batcherOptions) {
28
11
  this.baseUrl = baseUrl;
29
- this.credentials = credentials;
30
- }
31
- /**
32
- * Set credentials for authentication
33
- */
34
- setCredentials(credentials) {
35
- this.credentials = credentials;
36
- }
37
- /**
38
- * Authentication header
39
- */
40
- getAuthHeader() {
41
- if (!this.credentials) {
42
- return {};
12
+ this.username = username;
13
+ this.keypair = keypair;
14
+ this.batcher = null;
15
+ // Use WebCryptoAdapter by default (browser environment)
16
+ this.crypto = cryptoAdapter || new WebCryptoAdapter();
17
+ // Create batcher if not explicitly disabled
18
+ if (batcherOptions !== false) {
19
+ this.batcher = new RpcBatcher((requests) => this.rpcBatchDirect(requests), batcherOptions);
43
20
  }
44
- return {
45
- Authorization: `Bearer ${this.credentials.peerId}:${this.credentials.secret}`,
46
- };
47
- }
48
- // ============================================
49
- // Ed25519 Cryptography Helpers
50
- // ============================================
51
- /**
52
- * Generate an Ed25519 keypair for username claiming and service publishing
53
- */
54
- static async generateKeypair() {
55
- const privateKey = ed25519.utils.randomSecretKey();
56
- const publicKey = await ed25519.getPublicKeyAsync(privateKey);
57
- return {
58
- publicKey: bytesToBase64(publicKey),
59
- privateKey: bytesToBase64(privateKey),
60
- };
61
21
  }
62
22
  /**
63
- * Sign a message with an Ed25519 private key
23
+ * Generate authentication parameters for RPC calls
64
24
  */
65
- static async signMessage(message, privateKeyBase64) {
66
- const privateKey = base64ToBytes(privateKeyBase64);
67
- const encoder = new TextEncoder();
68
- const messageBytes = encoder.encode(message);
69
- const signature = await ed25519.signAsync(messageBytes, privateKey);
70
- return bytesToBase64(signature);
25
+ async generateAuth(method, params = '') {
26
+ const timestamp = Date.now();
27
+ const message = params
28
+ ? `${method}:${this.username}:${params}:${timestamp}`
29
+ : `${method}:${this.username}:${timestamp}`;
30
+ const signature = await this.crypto.signMessage(message, this.keypair.privateKey);
31
+ return { message, signature };
71
32
  }
72
33
  /**
73
- * Verify a signature
34
+ * Execute RPC call with optional batching
74
35
  */
75
- static async verifySignature(message, signatureBase64, publicKeyBase64) {
76
- const publicKey = base64ToBytes(publicKeyBase64);
77
- const signature = base64ToBytes(signatureBase64);
78
- const encoder = new TextEncoder();
79
- const messageBytes = encoder.encode(message);
80
- return await ed25519.verifyAsync(signature, messageBytes, publicKey);
36
+ async rpc(request) {
37
+ // Use batcher if enabled
38
+ if (this.batcher) {
39
+ return await this.batcher.add(request);
40
+ }
41
+ // Direct call without batching
42
+ return await this.rpcDirect(request);
81
43
  }
82
- // ============================================
83
- // Authentication
84
- // ============================================
85
44
  /**
86
- * Register a new peer and get credentials
45
+ * Execute single RPC call directly (bypasses batcher)
87
46
  */
88
- async register() {
89
- const response = await fetch(`${this.baseUrl}/register`, {
47
+ async rpcDirect(request) {
48
+ const response = await fetch(`${this.baseUrl}/rpc`, {
90
49
  method: 'POST',
91
50
  headers: { 'Content-Type': 'application/json' },
51
+ body: JSON.stringify(request),
92
52
  });
93
53
  if (!response.ok) {
94
- const error = await response.json().catch(() => ({ error: 'Unknown error' }));
95
- throw new Error(`Registration failed: ${error.error || response.statusText}`);
54
+ throw new Error(`HTTP ${response.status}: ${response.statusText}`);
55
+ }
56
+ const result = await response.json();
57
+ if (!result.success) {
58
+ throw new Error(result.error || 'RPC call failed');
96
59
  }
97
- return await response.json();
60
+ return result.result;
98
61
  }
99
- // ============================================
100
- // Offers
101
- // ============================================
102
62
  /**
103
- * Create one or more offers
63
+ * Execute batch RPC calls directly (bypasses batcher)
104
64
  */
105
- async createOffers(offers) {
106
- const response = await fetch(`${this.baseUrl}/offers`, {
65
+ async rpcBatchDirect(requests) {
66
+ const response = await fetch(`${this.baseUrl}/rpc`, {
107
67
  method: 'POST',
108
- headers: {
109
- 'Content-Type': 'application/json',
110
- ...this.getAuthHeader(),
111
- },
112
- body: JSON.stringify({ offers }),
68
+ headers: { 'Content-Type': 'application/json' },
69
+ body: JSON.stringify(requests),
113
70
  });
114
71
  if (!response.ok) {
115
- const error = await response.json().catch(() => ({ error: 'Unknown error' }));
116
- throw new Error(`Failed to create offers: ${error.error || response.statusText}`);
72
+ throw new Error(`HTTP ${response.status}: ${response.statusText}`);
117
73
  }
118
- return await response.json();
119
- }
120
- /**
121
- * Get offer by ID
122
- */
123
- async getOffer(offerId) {
124
- const response = await fetch(`${this.baseUrl}/offers/${offerId}`, {
125
- headers: this.getAuthHeader(),
126
- });
127
- if (!response.ok) {
128
- const error = await response.json().catch(() => ({ error: 'Unknown error' }));
129
- throw new Error(`Failed to get offer: ${error.error || response.statusText}`);
74
+ const results = await response.json();
75
+ // Validate response is an array
76
+ if (!Array.isArray(results)) {
77
+ console.error('Invalid RPC batch response:', results);
78
+ throw new Error('Server returned invalid batch response (not an array)');
130
79
  }
131
- return await response.json();
80
+ // Check response length matches request length
81
+ if (results.length !== requests.length) {
82
+ console.error(`Response length mismatch: expected ${requests.length}, got ${results.length}`);
83
+ }
84
+ return results.map((result, i) => {
85
+ if (!result || typeof result !== 'object') {
86
+ throw new Error(`Invalid response at index ${i}`);
87
+ }
88
+ if (!result.success) {
89
+ throw new Error(result.error || `RPC call ${i} failed`);
90
+ }
91
+ return result.result;
92
+ });
132
93
  }
94
+ // ============================================
95
+ // Ed25519 Cryptography Helpers
96
+ // ============================================
133
97
  /**
134
- * Answer a specific offer from a service
98
+ * Generate an Ed25519 keypair for username claiming and service publishing
99
+ * @param cryptoAdapter - Optional crypto adapter (defaults to WebCryptoAdapter)
135
100
  */
136
- async postOfferAnswer(serviceFqn, offerId, sdp) {
137
- const response = await fetch(`${this.baseUrl}/services/${encodeURIComponent(serviceFqn)}/offers/${offerId}/answer`, {
138
- method: 'POST',
139
- headers: {
140
- 'Content-Type': 'application/json',
141
- ...this.getAuthHeader(),
142
- },
143
- body: JSON.stringify({ sdp }),
144
- });
145
- if (!response.ok) {
146
- const error = await response.json().catch(() => ({ error: 'Unknown error' }));
147
- throw new Error(`Failed to answer offer: ${error.error || response.statusText}`);
148
- }
149
- return await response.json();
101
+ static async generateKeypair(cryptoAdapter) {
102
+ const adapter = cryptoAdapter || new WebCryptoAdapter();
103
+ return await adapter.generateKeypair();
150
104
  }
151
105
  /**
152
- * Get answer for a specific offer (offerer polls this)
106
+ * Sign a message with an Ed25519 private key
107
+ * @param cryptoAdapter - Optional crypto adapter (defaults to WebCryptoAdapter)
153
108
  */
154
- async getOfferAnswer(serviceFqn, offerId) {
155
- const response = await fetch(`${this.baseUrl}/services/${encodeURIComponent(serviceFqn)}/offers/${offerId}/answer`, {
156
- headers: this.getAuthHeader(),
157
- });
158
- if (!response.ok) {
159
- // 404 means not yet answered
160
- if (response.status === 404) {
161
- return null;
162
- }
163
- const error = await response.json().catch(() => ({ error: 'Unknown error' }));
164
- throw new Error(`Failed to get answer: ${error.error || response.statusText}`);
165
- }
166
- return await response.json();
109
+ static async signMessage(message, privateKeyBase64, cryptoAdapter) {
110
+ const adapter = cryptoAdapter || new WebCryptoAdapter();
111
+ return await adapter.signMessage(message, privateKeyBase64);
167
112
  }
168
113
  /**
169
- * Search offers by topic
114
+ * Verify an Ed25519 signature
115
+ * @param cryptoAdapter - Optional crypto adapter (defaults to WebCryptoAdapter)
170
116
  */
171
- async searchOffers(topic) {
172
- const response = await fetch(`${this.baseUrl}/offers?topic=${encodeURIComponent(topic)}`, {
173
- headers: this.getAuthHeader(),
174
- });
175
- if (!response.ok) {
176
- const error = await response.json().catch(() => ({ error: 'Unknown error' }));
177
- throw new Error(`Failed to search offers: ${error.error || response.statusText}`);
178
- }
179
- return await response.json();
117
+ static async verifySignature(message, signatureBase64, publicKeyBase64, cryptoAdapter) {
118
+ const adapter = cryptoAdapter || new WebCryptoAdapter();
119
+ return await adapter.verifySignature(message, signatureBase64, publicKeyBase64);
180
120
  }
181
121
  // ============================================
182
- // ICE Candidates
122
+ // Username Management
183
123
  // ============================================
184
124
  /**
185
- * Add ICE candidates to a specific offer
125
+ * Check if a username is available
186
126
  */
187
- async addOfferIceCandidates(serviceFqn, offerId, candidates) {
188
- const response = await fetch(`${this.baseUrl}/services/${encodeURIComponent(serviceFqn)}/offers/${offerId}/ice-candidates`, {
189
- method: 'POST',
190
- headers: {
191
- 'Content-Type': 'application/json',
192
- ...this.getAuthHeader(),
193
- },
194
- body: JSON.stringify({ candidates }),
127
+ async isUsernameAvailable(username) {
128
+ const auth = await this.generateAuth('getUser', username);
129
+ const result = await this.rpc({
130
+ method: 'getUser',
131
+ message: auth.message,
132
+ signature: auth.signature,
133
+ params: { username },
195
134
  });
196
- if (!response.ok) {
197
- const error = await response.json().catch(() => ({ error: 'Unknown error' }));
198
- throw new Error(`Failed to add ICE candidates: ${error.error || response.statusText}`);
199
- }
200
- return await response.json();
135
+ return result.available;
201
136
  }
202
137
  /**
203
- * Get ICE candidates for a specific offer (with polling support)
138
+ * Check if current username is claimed
204
139
  */
205
- async getOfferIceCandidates(serviceFqn, offerId, since = 0) {
206
- const url = new URL(`${this.baseUrl}/services/${encodeURIComponent(serviceFqn)}/offers/${offerId}/ice-candidates`);
207
- url.searchParams.set('since', since.toString());
208
- const response = await fetch(url.toString(), { headers: this.getAuthHeader() });
209
- if (!response.ok) {
210
- const error = await response.json().catch(() => ({ error: 'Unknown error' }));
211
- throw new Error(`Failed to get ICE candidates: ${error.error || response.statusText}`);
212
- }
213
- const data = await response.json();
214
- return {
215
- candidates: data.candidates || [],
216
- offerId: data.offerId
217
- };
140
+ async isUsernameClaimed() {
141
+ const auth = await this.generateAuth('getUser', this.username);
142
+ const result = await this.rpc({
143
+ method: 'getUser',
144
+ message: auth.message,
145
+ signature: auth.signature,
146
+ params: { username: this.username },
147
+ });
148
+ return !result.available;
218
149
  }
219
150
  // ============================================
220
- // Services
151
+ // Service Management
221
152
  // ============================================
222
153
  /**
223
154
  * Publish a service
224
- * Service FQN must include username: service:version@username
225
155
  */
226
156
  async publishService(service) {
227
- const response = await fetch(`${this.baseUrl}/services`, {
228
- method: 'POST',
229
- headers: {
230
- 'Content-Type': 'application/json',
231
- ...this.getAuthHeader(),
157
+ const auth = await this.generateAuth('publishService', service.serviceFqn);
158
+ return await this.rpc({
159
+ method: 'publishService',
160
+ message: auth.message,
161
+ signature: auth.signature,
162
+ publicKey: this.keypair.publicKey,
163
+ params: {
164
+ serviceFqn: service.serviceFqn,
165
+ offers: service.offers,
166
+ ttl: service.ttl,
232
167
  },
233
- body: JSON.stringify(service),
234
168
  });
235
- if (!response.ok) {
236
- const error = await response.json().catch(() => ({ error: 'Unknown error' }));
237
- throw new Error(`Failed to publish service: ${error.error || response.statusText}`);
238
- }
239
- return await response.json();
240
169
  }
241
170
  /**
242
- * Get service by FQN (with username) - Direct lookup
243
- * Example: chat:1.0.0@alice
171
+ * Get service by FQN (direct lookup, random, or paginated)
244
172
  */
245
- async getService(serviceFqn) {
246
- const response = await fetch(`${this.baseUrl}/services/${encodeURIComponent(serviceFqn)}`, {
247
- headers: this.getAuthHeader(),
173
+ async getService(serviceFqn, options) {
174
+ const auth = await this.generateAuth('getService', serviceFqn);
175
+ return await this.rpc({
176
+ method: 'getService',
177
+ message: auth.message,
178
+ signature: auth.signature,
179
+ publicKey: this.keypair.publicKey,
180
+ params: {
181
+ serviceFqn,
182
+ ...options,
183
+ },
248
184
  });
249
- if (!response.ok) {
250
- const error = await response.json().catch(() => ({ error: 'Unknown error' }));
251
- throw new Error(`Failed to get service: ${error.error || response.statusText}`);
252
- }
253
- return await response.json();
254
185
  }
255
186
  /**
256
- * Discover a random available service without knowing the username
257
- * Example: chat:1.0.0 (without @username)
187
+ * Delete a service
258
188
  */
259
- async discoverService(serviceVersion) {
260
- const response = await fetch(`${this.baseUrl}/services/${encodeURIComponent(serviceVersion)}`, {
261
- headers: this.getAuthHeader(),
189
+ async deleteService(serviceFqn) {
190
+ const auth = await this.generateAuth('deleteService', serviceFqn);
191
+ await this.rpc({
192
+ method: 'deleteService',
193
+ message: auth.message,
194
+ signature: auth.signature,
195
+ publicKey: this.keypair.publicKey,
196
+ params: { serviceFqn },
262
197
  });
263
- if (!response.ok) {
264
- const error = await response.json().catch(() => ({ error: 'Unknown error' }));
265
- throw new Error(`Failed to discover service: ${error.error || response.statusText}`);
266
- }
267
- return await response.json();
268
198
  }
199
+ // ============================================
200
+ // WebRTC Signaling
201
+ // ============================================
269
202
  /**
270
- * Discover multiple available services with pagination
271
- * Example: chat:1.0.0 (without @username)
203
+ * Answer an offer
272
204
  */
273
- async discoverServices(serviceVersion, limit = 10, offset = 0) {
274
- const url = new URL(`${this.baseUrl}/services/${encodeURIComponent(serviceVersion)}`);
275
- url.searchParams.set('limit', limit.toString());
276
- url.searchParams.set('offset', offset.toString());
277
- const response = await fetch(url.toString(), {
278
- headers: this.getAuthHeader(),
205
+ async answerOffer(serviceFqn, offerId, sdp) {
206
+ const auth = await this.generateAuth('answerOffer', offerId);
207
+ await this.rpc({
208
+ method: 'answerOffer',
209
+ message: auth.message,
210
+ signature: auth.signature,
211
+ publicKey: this.keypair.publicKey,
212
+ params: { serviceFqn, offerId, sdp },
279
213
  });
280
- if (!response.ok) {
281
- const error = await response.json().catch(() => ({ error: 'Unknown error' }));
282
- throw new Error(`Failed to discover services: ${error.error || response.statusText}`);
283
- }
284
- return await response.json();
285
214
  }
286
- // ============================================
287
- // Usernames
288
- // ============================================
289
215
  /**
290
- * Check if username is available
216
+ * Get answer for a specific offer (offerer polls this)
291
217
  */
292
- async checkUsername(username) {
293
- const response = await fetch(`${this.baseUrl}/users/${encodeURIComponent(username)}`);
294
- if (!response.ok) {
295
- const error = await response.json().catch(() => ({ error: 'Unknown error' }));
296
- throw new Error(`Failed to check username: ${error.error || response.statusText}`);
218
+ async getOfferAnswer(serviceFqn, offerId) {
219
+ try {
220
+ const auth = await this.generateAuth('getOfferAnswer', offerId);
221
+ return await this.rpc({
222
+ method: 'getOfferAnswer',
223
+ message: auth.message,
224
+ signature: auth.signature,
225
+ publicKey: this.keypair.publicKey,
226
+ params: { serviceFqn, offerId },
227
+ });
228
+ }
229
+ catch (err) {
230
+ if (err.message.includes('not yet answered')) {
231
+ return null;
232
+ }
233
+ throw err;
297
234
  }
298
- return await response.json();
299
235
  }
300
236
  /**
301
- * Claim a username (requires Ed25519 signature)
237
+ * Combined polling for answers and ICE candidates
302
238
  */
303
- async claimUsername(username, publicKey, signature, message) {
304
- const response = await fetch(`${this.baseUrl}/users/${encodeURIComponent(username)}`, {
305
- method: 'POST',
306
- headers: {
307
- 'Content-Type': 'application/json',
308
- ...this.getAuthHeader(),
309
- },
310
- body: JSON.stringify({
311
- publicKey,
312
- signature,
313
- message,
314
- }),
239
+ async poll(since) {
240
+ const auth = await this.generateAuth('poll');
241
+ return await this.rpc({
242
+ method: 'poll',
243
+ message: auth.message,
244
+ signature: auth.signature,
245
+ publicKey: this.keypair.publicKey,
246
+ params: { since },
315
247
  });
316
- if (!response.ok) {
317
- const error = await response.json().catch(() => ({ error: 'Unknown error' }));
318
- throw new Error(`Failed to claim username: ${error.error || response.statusText}`);
319
- }
320
- return await response.json();
248
+ }
249
+ /**
250
+ * Add ICE candidates to a specific offer
251
+ */
252
+ async addOfferIceCandidates(serviceFqn, offerId, candidates) {
253
+ const auth = await this.generateAuth('addIceCandidates', offerId);
254
+ return await this.rpc({
255
+ method: 'addIceCandidates',
256
+ message: auth.message,
257
+ signature: auth.signature,
258
+ publicKey: this.keypair.publicKey,
259
+ params: { serviceFqn, offerId, candidates },
260
+ });
261
+ }
262
+ /**
263
+ * Get ICE candidates for a specific offer
264
+ */
265
+ async getOfferIceCandidates(serviceFqn, offerId, since = 0) {
266
+ const auth = await this.generateAuth('getIceCandidates', `${offerId}:${since}`);
267
+ const result = await this.rpc({
268
+ method: 'getIceCandidates',
269
+ message: auth.message,
270
+ signature: auth.signature,
271
+ publicKey: this.keypair.publicKey,
272
+ params: { serviceFqn, offerId, since },
273
+ });
274
+ return {
275
+ candidates: result.candidates || [],
276
+ offerId: result.offerId,
277
+ };
321
278
  }
322
279
  }
@@ -0,0 +1,37 @@
1
+ /**
2
+ * Crypto adapter interface for platform-independent cryptographic operations
3
+ */
4
+ export interface Keypair {
5
+ publicKey: string;
6
+ privateKey: string;
7
+ }
8
+ /**
9
+ * Platform-independent crypto adapter interface
10
+ * Implementations provide platform-specific crypto operations
11
+ */
12
+ export interface CryptoAdapter {
13
+ /**
14
+ * Generate an Ed25519 keypair
15
+ */
16
+ generateKeypair(): Promise<Keypair>;
17
+ /**
18
+ * Sign a message with an Ed25519 private key
19
+ */
20
+ signMessage(message: string, privateKeyBase64: string): Promise<string>;
21
+ /**
22
+ * Verify an Ed25519 signature
23
+ */
24
+ verifySignature(message: string, signatureBase64: string, publicKeyBase64: string): Promise<boolean>;
25
+ /**
26
+ * Convert Uint8Array to base64 string
27
+ */
28
+ bytesToBase64(bytes: Uint8Array): string;
29
+ /**
30
+ * Convert base64 string to Uint8Array
31
+ */
32
+ base64ToBytes(base64: string): Uint8Array;
33
+ /**
34
+ * Generate random bytes
35
+ */
36
+ randomBytes(length: number): Uint8Array;
37
+ }
@@ -0,0 +1,4 @@
1
+ /**
2
+ * Crypto adapter interface for platform-independent cryptographic operations
3
+ */
4
+ export {};
package/dist/index.d.ts CHANGED
@@ -4,8 +4,10 @@
4
4
  */
5
5
  export { Rondevu } from './rondevu.js';
6
6
  export { RondevuAPI } from './api.js';
7
- export { RondevuSignaler } from './rondevu-signaler.js';
7
+ export { RpcBatcher } from './rpc-batcher.js';
8
+ export { WebCryptoAdapter } from './web-crypto-adapter.js';
9
+ export { NodeCryptoAdapter } from './node-crypto-adapter.js';
8
10
  export type { Signaler, Binnable, } from './types.js';
9
- export type { Credentials, Keypair, OfferRequest, Offer, ServiceRequest, Service, IceCandidate, } from './api.js';
10
- export type { RondevuOptions, PublishServiceOptions } from './rondevu.js';
11
- export type { PollingConfig } from './rondevu-signaler.js';
11
+ export type { Keypair, OfferRequest, ServiceRequest, Service, ServiceOffer, IceCandidate, } from './api.js';
12
+ export type { RondevuOptions, PublishServiceOptions, ConnectToServiceOptions, ConnectionContext, OfferContext, OfferFactory } from './rondevu.js';
13
+ export type { CryptoAdapter } from './crypto-adapter.js';
package/dist/index.js CHANGED
@@ -4,4 +4,7 @@
4
4
  */
5
5
  export { Rondevu } from './rondevu.js';
6
6
  export { RondevuAPI } from './api.js';
7
- export { RondevuSignaler } from './rondevu-signaler.js';
7
+ export { RpcBatcher } from './rpc-batcher.js';
8
+ // Export crypto adapters
9
+ export { WebCryptoAdapter } from './web-crypto-adapter.js';
10
+ export { NodeCryptoAdapter } from './node-crypto-adapter.js';
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Node.js Crypto adapter for Node.js environments
3
+ * Requires Node.js 19+ or Node.js 18 with --experimental-global-webcrypto flag
4
+ */
5
+ import { CryptoAdapter, Keypair } from './crypto-adapter.js';
6
+ /**
7
+ * Node.js Crypto implementation using Node.js built-in APIs
8
+ * Uses Buffer for base64 encoding and crypto.randomBytes for random generation
9
+ *
10
+ * Requirements:
11
+ * - Node.js 19+ (crypto.subtle available globally)
12
+ * - OR Node.js 18 with --experimental-global-webcrypto flag
13
+ *
14
+ * @example
15
+ * ```typescript
16
+ * import { RondevuAPI } from '@xtr-dev/rondevu-client'
17
+ * import { NodeCryptoAdapter } from '@xtr-dev/rondevu-client/node'
18
+ *
19
+ * const api = new RondevuAPI(
20
+ * 'https://signal.example.com',
21
+ * 'alice',
22
+ * keypair,
23
+ * new NodeCryptoAdapter()
24
+ * )
25
+ * ```
26
+ */
27
+ export declare class NodeCryptoAdapter implements CryptoAdapter {
28
+ constructor();
29
+ generateKeypair(): Promise<Keypair>;
30
+ signMessage(message: string, privateKeyBase64: string): Promise<string>;
31
+ verifySignature(message: string, signatureBase64: string, publicKeyBase64: string): Promise<boolean>;
32
+ bytesToBase64(bytes: Uint8Array): string;
33
+ base64ToBytes(base64: string): Uint8Array;
34
+ randomBytes(length: number): Uint8Array;
35
+ }