@xtr-dev/rondevu-client 0.12.4 → 0.17.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 +100 -381
- package/dist/api.d.ts +75 -96
- package/dist/api.js +202 -243
- package/dist/crypto-adapter.d.ts +37 -0
- package/dist/crypto-adapter.js +4 -0
- package/dist/index.d.ts +8 -15
- package/dist/index.js +5 -8
- package/dist/node-crypto-adapter.d.ts +35 -0
- package/dist/node-crypto-adapter.js +80 -0
- package/dist/rondevu-signaler.d.ts +14 -12
- package/dist/rondevu-signaler.js +111 -95
- package/dist/rondevu.d.ts +329 -0
- package/dist/rondevu.js +648 -0
- package/dist/rpc-batcher.d.ts +61 -0
- package/dist/rpc-batcher.js +111 -0
- package/dist/types.d.ts +8 -21
- package/dist/types.js +4 -6
- package/dist/web-crypto-adapter.d.ts +16 -0
- package/dist/web-crypto-adapter.js +52 -0
- package/package.json +1 -1
- package/dist/bin.d.ts +0 -35
- package/dist/bin.js +0 -35
- package/dist/connection-manager.d.ts +0 -104
- package/dist/connection-manager.js +0 -324
- package/dist/connection.d.ts +0 -112
- package/dist/connection.js +0 -194
- package/dist/durable-connection.d.ts +0 -120
- package/dist/durable-connection.js +0 -244
- package/dist/event-bus.d.ts +0 -52
- package/dist/event-bus.js +0 -84
- package/dist/noop-signaler.d.ts +0 -14
- package/dist/noop-signaler.js +0 -27
- package/dist/quick-start.d.ts +0 -29
- package/dist/quick-start.js +0 -44
- package/dist/rondevu-context.d.ts +0 -10
- package/dist/rondevu-context.js +0 -20
- package/dist/rondevu-service.d.ts +0 -87
- package/dist/rondevu-service.js +0 -170
- package/dist/service-client.d.ts +0 -77
- package/dist/service-client.js +0 -158
- package/dist/service-host.d.ts +0 -67
- package/dist/service-host.js +0 -120
- package/dist/signaler.d.ts +0 -25
- package/dist/signaler.js +0 -89
- package/dist/webrtc-context.d.ts +0 -5
- package/dist/webrtc-context.js +0 -35
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* RPC Batcher - Throttles and batches RPC requests to reduce HTTP overhead
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Batches and throttles RPC requests to optimize network usage
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```typescript
|
|
9
|
+
* const batcher = new RpcBatcher(
|
|
10
|
+
* (requests) => api.rpcBatch(requests),
|
|
11
|
+
* { maxBatchSize: 10, maxWaitTime: 50 }
|
|
12
|
+
* )
|
|
13
|
+
*
|
|
14
|
+
* // These will be batched together if called within maxWaitTime
|
|
15
|
+
* const result1 = await batcher.add(request1)
|
|
16
|
+
* const result2 = await batcher.add(request2)
|
|
17
|
+
* const result3 = await batcher.add(request3)
|
|
18
|
+
* ```
|
|
19
|
+
*/
|
|
20
|
+
export class RpcBatcher {
|
|
21
|
+
constructor(sendBatch, options) {
|
|
22
|
+
this.queue = [];
|
|
23
|
+
this.batchTimeout = null;
|
|
24
|
+
this.lastBatchTime = 0;
|
|
25
|
+
this.sendBatch = sendBatch;
|
|
26
|
+
this.options = {
|
|
27
|
+
maxBatchSize: options?.maxBatchSize ?? 10,
|
|
28
|
+
maxWaitTime: options?.maxWaitTime ?? 50,
|
|
29
|
+
throttleInterval: options?.throttleInterval ?? 10,
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Add an RPC request to the batch queue
|
|
34
|
+
* Returns a promise that resolves when the request completes
|
|
35
|
+
*/
|
|
36
|
+
async add(request) {
|
|
37
|
+
return new Promise((resolve, reject) => {
|
|
38
|
+
this.queue.push({ request, resolve, reject });
|
|
39
|
+
// Send immediately if batch is full
|
|
40
|
+
if (this.queue.length >= this.options.maxBatchSize) {
|
|
41
|
+
this.flush();
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
// Schedule batch if not already scheduled
|
|
45
|
+
if (!this.batchTimeout) {
|
|
46
|
+
this.batchTimeout = setTimeout(() => {
|
|
47
|
+
this.flush();
|
|
48
|
+
}, this.options.maxWaitTime);
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Flush the queue immediately
|
|
54
|
+
*/
|
|
55
|
+
async flush() {
|
|
56
|
+
// Clear timeout if set
|
|
57
|
+
if (this.batchTimeout) {
|
|
58
|
+
clearTimeout(this.batchTimeout);
|
|
59
|
+
this.batchTimeout = null;
|
|
60
|
+
}
|
|
61
|
+
// Nothing to flush
|
|
62
|
+
if (this.queue.length === 0) {
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
// Throttle: wait if we sent a batch too recently
|
|
66
|
+
const now = Date.now();
|
|
67
|
+
const timeSinceLastBatch = now - this.lastBatchTime;
|
|
68
|
+
if (timeSinceLastBatch < this.options.throttleInterval) {
|
|
69
|
+
const waitTime = this.options.throttleInterval - timeSinceLastBatch;
|
|
70
|
+
await new Promise(resolve => setTimeout(resolve, waitTime));
|
|
71
|
+
}
|
|
72
|
+
// Extract requests from queue
|
|
73
|
+
const batch = this.queue.splice(0, this.options.maxBatchSize);
|
|
74
|
+
const requests = batch.map(item => item.request);
|
|
75
|
+
this.lastBatchTime = Date.now();
|
|
76
|
+
try {
|
|
77
|
+
// Send batch request
|
|
78
|
+
const results = await this.sendBatch(requests);
|
|
79
|
+
// Resolve individual promises
|
|
80
|
+
for (let i = 0; i < batch.length; i++) {
|
|
81
|
+
batch[i].resolve(results[i]);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
catch (error) {
|
|
85
|
+
// Reject all promises in batch
|
|
86
|
+
for (const item of batch) {
|
|
87
|
+
item.reject(error);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Get current queue size
|
|
93
|
+
*/
|
|
94
|
+
getQueueSize() {
|
|
95
|
+
return this.queue.length;
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Clear the queue without sending
|
|
99
|
+
*/
|
|
100
|
+
clear() {
|
|
101
|
+
if (this.batchTimeout) {
|
|
102
|
+
clearTimeout(this.batchTimeout);
|
|
103
|
+
this.batchTimeout = null;
|
|
104
|
+
}
|
|
105
|
+
// Reject all pending requests
|
|
106
|
+
for (const item of this.queue) {
|
|
107
|
+
item.reject(new Error('Batch queue cleared'));
|
|
108
|
+
}
|
|
109
|
+
this.queue = [];
|
|
110
|
+
}
|
|
111
|
+
}
|
package/dist/types.d.ts
CHANGED
|
@@ -1,26 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Core
|
|
2
|
+
* Core signaling types
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Cleanup function returned by listener methods
|
|
6
|
+
*/
|
|
7
|
+
export type Binnable = () => void;
|
|
8
|
+
/**
|
|
9
|
+
* Signaler interface for WebRTC offer/answer/ICE exchange
|
|
3
10
|
*/
|
|
4
|
-
import { EventBus } from './event-bus.js';
|
|
5
|
-
import { Binnable } from './bin.js';
|
|
6
|
-
export type Message = string | ArrayBuffer;
|
|
7
|
-
export interface QueueMessageOptions {
|
|
8
|
-
expiresAt?: number;
|
|
9
|
-
}
|
|
10
|
-
export interface ConnectionEvents {
|
|
11
|
-
'state-change': ConnectionInterface['state'];
|
|
12
|
-
message: Message;
|
|
13
|
-
}
|
|
14
|
-
export declare const ConnectionStates: readonly ["connected", "disconnected", "connecting"];
|
|
15
|
-
export declare const isConnectionState: (state: string) => state is (typeof ConnectionStates)[number];
|
|
16
|
-
export interface ConnectionInterface {
|
|
17
|
-
state: (typeof ConnectionStates)[number];
|
|
18
|
-
lastActive: number;
|
|
19
|
-
expiresAt?: number;
|
|
20
|
-
events: EventBus<ConnectionEvents>;
|
|
21
|
-
queueMessage(message: Message, options?: QueueMessageOptions): Promise<void>;
|
|
22
|
-
sendMessage(message: Message): Promise<boolean>;
|
|
23
|
-
}
|
|
24
11
|
export interface Signaler {
|
|
25
12
|
addIceCandidate(candidate: RTCIceCandidate): Promise<void>;
|
|
26
13
|
addListener(callback: (candidate: RTCIceCandidate) => void): Binnable;
|
package/dist/types.js
CHANGED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Web Crypto adapter for browser environments
|
|
3
|
+
*/
|
|
4
|
+
import { CryptoAdapter, Keypair } from './crypto-adapter.js';
|
|
5
|
+
/**
|
|
6
|
+
* Web Crypto implementation using browser APIs
|
|
7
|
+
* Uses btoa/atob for base64 encoding and crypto.getRandomValues for random bytes
|
|
8
|
+
*/
|
|
9
|
+
export declare class WebCryptoAdapter implements CryptoAdapter {
|
|
10
|
+
generateKeypair(): Promise<Keypair>;
|
|
11
|
+
signMessage(message: string, privateKeyBase64: string): Promise<string>;
|
|
12
|
+
verifySignature(message: string, signatureBase64: string, publicKeyBase64: string): Promise<boolean>;
|
|
13
|
+
bytesToBase64(bytes: Uint8Array): string;
|
|
14
|
+
base64ToBytes(base64: string): Uint8Array;
|
|
15
|
+
randomBytes(length: number): Uint8Array;
|
|
16
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Web Crypto adapter for browser environments
|
|
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
|
+
};
|
|
9
|
+
/**
|
|
10
|
+
* Web Crypto implementation using browser APIs
|
|
11
|
+
* Uses btoa/atob for base64 encoding and crypto.getRandomValues for random bytes
|
|
12
|
+
*/
|
|
13
|
+
export class WebCryptoAdapter {
|
|
14
|
+
async generateKeypair() {
|
|
15
|
+
const privateKey = ed25519.utils.randomSecretKey();
|
|
16
|
+
const publicKey = await ed25519.getPublicKeyAsync(privateKey);
|
|
17
|
+
return {
|
|
18
|
+
publicKey: this.bytesToBase64(publicKey),
|
|
19
|
+
privateKey: this.bytesToBase64(privateKey),
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
async signMessage(message, privateKeyBase64) {
|
|
23
|
+
const privateKey = this.base64ToBytes(privateKeyBase64);
|
|
24
|
+
const encoder = new TextEncoder();
|
|
25
|
+
const messageBytes = encoder.encode(message);
|
|
26
|
+
const signature = await ed25519.signAsync(messageBytes, privateKey);
|
|
27
|
+
return this.bytesToBase64(signature);
|
|
28
|
+
}
|
|
29
|
+
async verifySignature(message, signatureBase64, publicKeyBase64) {
|
|
30
|
+
try {
|
|
31
|
+
const signature = this.base64ToBytes(signatureBase64);
|
|
32
|
+
const publicKey = this.base64ToBytes(publicKeyBase64);
|
|
33
|
+
const encoder = new TextEncoder();
|
|
34
|
+
const messageBytes = encoder.encode(message);
|
|
35
|
+
return await ed25519.verifyAsync(signature, messageBytes, publicKey);
|
|
36
|
+
}
|
|
37
|
+
catch {
|
|
38
|
+
return false;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
bytesToBase64(bytes) {
|
|
42
|
+
const binString = Array.from(bytes, byte => String.fromCodePoint(byte)).join('');
|
|
43
|
+
return btoa(binString);
|
|
44
|
+
}
|
|
45
|
+
base64ToBytes(base64) {
|
|
46
|
+
const binString = atob(base64);
|
|
47
|
+
return Uint8Array.from(binString, char => char.codePointAt(0));
|
|
48
|
+
}
|
|
49
|
+
randomBytes(length) {
|
|
50
|
+
return crypto.getRandomValues(new Uint8Array(length));
|
|
51
|
+
}
|
|
52
|
+
}
|
package/package.json
CHANGED
package/dist/bin.d.ts
DELETED
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Binnable - A cleanup function that can be synchronous or asynchronous
|
|
3
|
-
*
|
|
4
|
-
* Used to unsubscribe from events, close connections, or perform other cleanup operations.
|
|
5
|
-
*/
|
|
6
|
-
export type Binnable = () => void | Promise<void>;
|
|
7
|
-
/**
|
|
8
|
-
* Create a cleanup function collector (garbage bin)
|
|
9
|
-
*
|
|
10
|
-
* Collects cleanup functions and provides a single `clean()` method to execute all of them.
|
|
11
|
-
* Useful for managing multiple cleanup operations in a single place.
|
|
12
|
-
*
|
|
13
|
-
* @returns A function that accepts cleanup functions and has a `clean()` method
|
|
14
|
-
*
|
|
15
|
-
* @example
|
|
16
|
-
* ```typescript
|
|
17
|
-
* const bin = createBin();
|
|
18
|
-
*
|
|
19
|
-
* // Add cleanup functions
|
|
20
|
-
* bin(
|
|
21
|
-
* () => console.log('Cleanup 1'),
|
|
22
|
-
* () => connection.close(),
|
|
23
|
-
* () => clearInterval(timer)
|
|
24
|
-
* );
|
|
25
|
-
*
|
|
26
|
-
* // Later, clean everything
|
|
27
|
-
* bin.clean(); // Executes all cleanup functions
|
|
28
|
-
* ```
|
|
29
|
-
*/
|
|
30
|
-
export declare const createBin: () => ((...rubbish: Binnable[]) => number) & {
|
|
31
|
-
/**
|
|
32
|
-
* Execute all cleanup functions and clear the bin
|
|
33
|
-
*/
|
|
34
|
-
clean: () => void;
|
|
35
|
-
};
|
package/dist/bin.js
DELETED
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Create a cleanup function collector (garbage bin)
|
|
3
|
-
*
|
|
4
|
-
* Collects cleanup functions and provides a single `clean()` method to execute all of them.
|
|
5
|
-
* Useful for managing multiple cleanup operations in a single place.
|
|
6
|
-
*
|
|
7
|
-
* @returns A function that accepts cleanup functions and has a `clean()` method
|
|
8
|
-
*
|
|
9
|
-
* @example
|
|
10
|
-
* ```typescript
|
|
11
|
-
* const bin = createBin();
|
|
12
|
-
*
|
|
13
|
-
* // Add cleanup functions
|
|
14
|
-
* bin(
|
|
15
|
-
* () => console.log('Cleanup 1'),
|
|
16
|
-
* () => connection.close(),
|
|
17
|
-
* () => clearInterval(timer)
|
|
18
|
-
* );
|
|
19
|
-
*
|
|
20
|
-
* // Later, clean everything
|
|
21
|
-
* bin.clean(); // Executes all cleanup functions
|
|
22
|
-
* ```
|
|
23
|
-
*/
|
|
24
|
-
export const createBin = () => {
|
|
25
|
-
const bin = [];
|
|
26
|
-
return Object.assign((...rubbish) => bin.push(...rubbish), {
|
|
27
|
-
/**
|
|
28
|
-
* Execute all cleanup functions and clear the bin
|
|
29
|
-
*/
|
|
30
|
-
clean: () => {
|
|
31
|
-
bin.forEach(binnable => binnable());
|
|
32
|
-
bin.length = 0;
|
|
33
|
-
},
|
|
34
|
-
});
|
|
35
|
-
};
|
|
@@ -1,104 +0,0 @@
|
|
|
1
|
-
import { WebRTCRondevuConnection } from './connection.js';
|
|
2
|
-
import { Credentials } from './api.js';
|
|
3
|
-
import { ConnectionInterface } from './types.js';
|
|
4
|
-
export interface ConnectionManagerOptions {
|
|
5
|
-
apiUrl: string;
|
|
6
|
-
username: string;
|
|
7
|
-
credentials?: Credentials;
|
|
8
|
-
autoReconnect?: boolean;
|
|
9
|
-
reconnectDelay?: number;
|
|
10
|
-
maxReconnectAttempts?: number;
|
|
11
|
-
}
|
|
12
|
-
export interface HostServiceOptions {
|
|
13
|
-
service: string;
|
|
14
|
-
ttl?: number;
|
|
15
|
-
onConnection?: (connection: ConnectionInterface) => void;
|
|
16
|
-
}
|
|
17
|
-
export interface ConnectToServiceOptions {
|
|
18
|
-
username: string;
|
|
19
|
-
service: string;
|
|
20
|
-
onConnection?: (connection: ConnectionInterface) => void;
|
|
21
|
-
}
|
|
22
|
-
/**
|
|
23
|
-
* ConnectionManager - High-level connection management
|
|
24
|
-
*
|
|
25
|
-
* @example
|
|
26
|
-
* // Host a service
|
|
27
|
-
* const manager = new ConnectionManager({
|
|
28
|
-
* apiUrl: 'https://api.ronde.vu',
|
|
29
|
-
* credentials: await api.register()
|
|
30
|
-
* })
|
|
31
|
-
*
|
|
32
|
-
* await manager.hostService({
|
|
33
|
-
* service: 'chat.app@1.0.0',
|
|
34
|
-
* onConnection: (conn) => {
|
|
35
|
-
* conn.events.on('message', msg => console.log('Received:', msg))
|
|
36
|
-
* }
|
|
37
|
-
* })
|
|
38
|
-
*
|
|
39
|
-
* @example
|
|
40
|
-
* // Connect to a service
|
|
41
|
-
* const connection = await manager.connectToService({
|
|
42
|
-
* username: 'alice',
|
|
43
|
-
* service: 'chat.app@1.0.0'
|
|
44
|
-
* })
|
|
45
|
-
*
|
|
46
|
-
* await connection.sendMessage('Hello!')
|
|
47
|
-
*/
|
|
48
|
-
export declare class ConnectionManager {
|
|
49
|
-
private readonly api;
|
|
50
|
-
private readonly username;
|
|
51
|
-
private readonly connections;
|
|
52
|
-
private readonly autoReconnect;
|
|
53
|
-
private readonly reconnectDelay;
|
|
54
|
-
private readonly maxReconnectAttempts;
|
|
55
|
-
private readonly bin;
|
|
56
|
-
constructor(options: ConnectionManagerOptions);
|
|
57
|
-
/**
|
|
58
|
-
* Host a service - Creates an offer and publishes it to the signaling server
|
|
59
|
-
*
|
|
60
|
-
* The service will automatically accept incoming connections and manage them.
|
|
61
|
-
* Each new connection triggers the onConnection callback.
|
|
62
|
-
*
|
|
63
|
-
* @param options - Service hosting options
|
|
64
|
-
* @returns Promise that resolves when the service is published
|
|
65
|
-
*/
|
|
66
|
-
hostService(options: HostServiceOptions): Promise<void>;
|
|
67
|
-
/**
|
|
68
|
-
* Connect to a hosted service
|
|
69
|
-
*
|
|
70
|
-
* Searches for the service, retrieves the offer, and creates an answering connection.
|
|
71
|
-
*
|
|
72
|
-
* @param options - Connection options
|
|
73
|
-
* @returns The established connection
|
|
74
|
-
*/
|
|
75
|
-
connectToService(options: ConnectToServiceOptions): Promise<WebRTCRondevuConnection>;
|
|
76
|
-
/**
|
|
77
|
-
* Get a connection by ID
|
|
78
|
-
*/
|
|
79
|
-
getConnection(id: string): WebRTCRondevuConnection | undefined;
|
|
80
|
-
/**
|
|
81
|
-
* Get all managed connections
|
|
82
|
-
*/
|
|
83
|
-
getAllConnections(): WebRTCRondevuConnection[];
|
|
84
|
-
/**
|
|
85
|
-
* Remove a connection
|
|
86
|
-
*/
|
|
87
|
-
private removeConnection;
|
|
88
|
-
/**
|
|
89
|
-
* Schedule reconnection for a failed connection
|
|
90
|
-
*/
|
|
91
|
-
private scheduleReconnect;
|
|
92
|
-
/**
|
|
93
|
-
* Attempt to reconnect a failed connection
|
|
94
|
-
*/
|
|
95
|
-
private attemptReconnect;
|
|
96
|
-
/**
|
|
97
|
-
* Create a temporary signaler (used during initial offer creation)
|
|
98
|
-
*/
|
|
99
|
-
private createTempSignaler;
|
|
100
|
-
/**
|
|
101
|
-
* Clean up all connections and resources
|
|
102
|
-
*/
|
|
103
|
-
destroy(): void;
|
|
104
|
-
}
|