@dabble/patches 0.8.21 → 0.9.2
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 +1 -1
- package/dist/net/PatchesSync.d.ts +36 -0
- package/dist/net/PatchesSync.js +53 -1
- package/dist/net/error.d.ts +9 -0
- package/dist/net/error.js +10 -1
- package/dist/net/index.d.ts +5 -2
- package/dist/net/index.js +2 -1
- package/dist/net/invite.d.ts +55 -0
- package/dist/net/invite.js +0 -0
- package/dist/net/protocol/types.d.ts +16 -2
- package/dist/net/rest/PatchesREST.d.ts +46 -0
- package/dist/net/rest/PatchesREST.js +92 -0
- package/dist/net/rest/PatchesRESTSignalingTransport.d.ts +42 -0
- package/dist/net/rest/PatchesRESTSignalingTransport.js +34 -0
- package/dist/net/rest/SSEServer.d.ts +15 -0
- package/dist/net/rest/SSEServer.js +25 -0
- package/dist/net/rest/SSESignalingService.d.ts +60 -0
- package/dist/net/rest/SSESignalingService.js +27 -0
- package/dist/net/rest/index.d.ts +4 -0
- package/dist/net/rest/index.js +2 -0
- package/dist/net/signaling/SignalingService.d.ts +74 -0
- package/dist/net/signaling/SignalingService.js +135 -0
- package/dist/net/signaling/index.d.ts +7 -0
- package/dist/net/signaling/index.js +1 -0
- package/dist/net/webrtc/WebRTCAwareness.d.ts +0 -2
- package/dist/net/webrtc/WebRTCTransport.d.ts +9 -8
- package/dist/net/webrtc/WebRTCTransport.js +3 -2
- package/dist/net/webrtc/index.d.ts +0 -2
- package/dist/net/websocket/SignalingService.d.ts +2 -66
- package/dist/net/websocket/SignalingService.js +1 -135
- package/dist/server/LWWBranchManager.d.ts +2 -2
- package/dist/server/LWWServer.d.ts +2 -2
- package/dist/server/types.d.ts +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { SignalingService, JsonRpcMessage } from '../signaling/SignalingService.js';
|
|
2
|
+
import { SSEServer } from './SSEServer.js';
|
|
3
|
+
import '../protocol/types.js';
|
|
4
|
+
import 'easy-signal';
|
|
5
|
+
import '../../types.js';
|
|
6
|
+
import '../../json-patch/JSONPatch.js';
|
|
7
|
+
import '@dabble/delta';
|
|
8
|
+
import '../../json-patch/types.js';
|
|
9
|
+
import '../websocket/AuthorizationProvider.js';
|
|
10
|
+
import '../../server/types.js';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* {@link SignalingService} that delivers WebRTC signaling frames over an
|
|
14
|
+
* {@link SSEServer} stream as the multiplexed `signal` event.
|
|
15
|
+
*
|
|
16
|
+
* Wire it up in your routes alongside the existing SSE doc-sync handlers.
|
|
17
|
+
*
|
|
18
|
+
* **Security:** the `fromId` passed to {@link handleClientMessage} MUST come
|
|
19
|
+
* from your authenticated session, not from the request URL. If you trust the
|
|
20
|
+
* URL `:clientId`, client A can POST to `/signal/B` and impersonate B in the
|
|
21
|
+
* WebRTC mesh, redirecting peer connections. Treat the URL parameter as
|
|
22
|
+
* untrusted input and bind sender identity to auth.
|
|
23
|
+
*
|
|
24
|
+
* ```typescript
|
|
25
|
+
* const sse = new SSEServer();
|
|
26
|
+
* const signaling = new SSESignalingService(sse);
|
|
27
|
+
*
|
|
28
|
+
* // GET /events/:clientId — after creating the SSE stream:
|
|
29
|
+
* const clientId = req.auth.clientId; // authenticated, not URL-derived
|
|
30
|
+
* await signaling.onClientConnected(clientId);
|
|
31
|
+
*
|
|
32
|
+
* // POST /signal/:clientId
|
|
33
|
+
* app.post('/signal/:clientId', async (req, res) => {
|
|
34
|
+
* if (req.auth.clientId !== req.params.clientId) return res.status(403).end();
|
|
35
|
+
* const body = await readBody(req);
|
|
36
|
+
* await signaling.handleClientMessage(req.auth.clientId, body);
|
|
37
|
+
* res.status(204).end();
|
|
38
|
+
* });
|
|
39
|
+
*
|
|
40
|
+
* // On SSE stream close:
|
|
41
|
+
* await signaling.onClientDisconnected(clientId);
|
|
42
|
+
* ```
|
|
43
|
+
*/
|
|
44
|
+
declare class SSESignalingService extends SignalingService {
|
|
45
|
+
private sse;
|
|
46
|
+
constructor(sse: SSEServer);
|
|
47
|
+
/**
|
|
48
|
+
* Derived from the live SSE connection set rather than tracked separately,
|
|
49
|
+
* so peer-routing decisions can't drift from actual writer liveness. If a
|
|
50
|
+
* client's SSE stream silently dies, `getConnectionIds()` excludes it
|
|
51
|
+
* immediately and `handleClientMessage` will respond with "Target not
|
|
52
|
+
* connected" instead of relaying into the void.
|
|
53
|
+
*/
|
|
54
|
+
getClients(): Promise<Set<string>>;
|
|
55
|
+
/** No-op: the SSEServer's connection set is the source of truth. */
|
|
56
|
+
setClients(_clients: Set<string>): Promise<void>;
|
|
57
|
+
send(id: string, message: JsonRpcMessage): void;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export { SSESignalingService };
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import "../../chunk-IZ2YBCUP.js";
|
|
2
|
+
import { SignalingService } from "../signaling/SignalingService.js";
|
|
3
|
+
class SSESignalingService extends SignalingService {
|
|
4
|
+
constructor(sse) {
|
|
5
|
+
super();
|
|
6
|
+
this.sse = sse;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Derived from the live SSE connection set rather than tracked separately,
|
|
10
|
+
* so peer-routing decisions can't drift from actual writer liveness. If a
|
|
11
|
+
* client's SSE stream silently dies, `getConnectionIds()` excludes it
|
|
12
|
+
* immediately and `handleClientMessage` will respond with "Target not
|
|
13
|
+
* connected" instead of relaying into the void.
|
|
14
|
+
*/
|
|
15
|
+
async getClients() {
|
|
16
|
+
return new Set(this.sse.getConnectionIds());
|
|
17
|
+
}
|
|
18
|
+
/** No-op: the SSEServer's connection set is the source of truth. */
|
|
19
|
+
async setClients(_clients) {
|
|
20
|
+
}
|
|
21
|
+
send(id, message) {
|
|
22
|
+
this.sse.sendToClient(id, "signal", JSON.stringify(message));
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
export {
|
|
26
|
+
SSESignalingService
|
|
27
|
+
};
|
package/dist/net/rest/index.d.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
export { PatchesREST, PatchesRESTOptions } from './PatchesREST.js';
|
|
2
|
+
export { PatchesRESTSignalingTransport } from './PatchesRESTSignalingTransport.js';
|
|
2
3
|
export { BufferedEvent, SSEServer, SSEServerOptions } from './SSEServer.js';
|
|
4
|
+
export { SSESignalingService } from './SSESignalingService.js';
|
|
3
5
|
export { normalizeIds } from './utils.js';
|
|
4
6
|
import 'easy-signal';
|
|
5
7
|
import '../../types.js';
|
|
@@ -8,5 +10,7 @@ import '@dabble/delta';
|
|
|
8
10
|
import '../../json-patch/types.js';
|
|
9
11
|
import '../PatchesConnection.js';
|
|
10
12
|
import '../protocol/types.js';
|
|
13
|
+
import '../invite.js';
|
|
11
14
|
import '../websocket/AuthorizationProvider.js';
|
|
12
15
|
import '../../server/types.js';
|
|
16
|
+
import '../signaling/SignalingService.js';
|
package/dist/net/rest/index.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import "../../chunk-IZ2YBCUP.js";
|
|
2
2
|
export * from "./PatchesREST.js";
|
|
3
|
+
export * from "./PatchesRESTSignalingTransport.js";
|
|
3
4
|
export * from "./SSEServer.js";
|
|
5
|
+
export * from "./SSESignalingService.js";
|
|
4
6
|
import { normalizeIds } from "./utils.js";
|
|
5
7
|
export {
|
|
6
8
|
normalizeIds
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { JsonRpcRequest, JsonRpcResponse } from '../protocol/types.js';
|
|
2
|
+
import 'easy-signal';
|
|
3
|
+
import '../../types.js';
|
|
4
|
+
import '../../json-patch/JSONPatch.js';
|
|
5
|
+
import '@dabble/delta';
|
|
6
|
+
import '../../json-patch/types.js';
|
|
7
|
+
|
|
8
|
+
/** Union type for all possible JSON-RPC message types */
|
|
9
|
+
type JsonRpcMessage = JsonRpcRequest | JsonRpcResponse;
|
|
10
|
+
/**
|
|
11
|
+
* Service that facilitates WebRTC connection establishment by relaying signaling messages.
|
|
12
|
+
* Acts as a central hub for WebRTC peers to exchange connection information.
|
|
13
|
+
*
|
|
14
|
+
* Transport-agnostic: concrete subclasses implement {@link send} over WebSocket,
|
|
15
|
+
* SSE, or any other channel.
|
|
16
|
+
*/
|
|
17
|
+
declare abstract class SignalingService {
|
|
18
|
+
protected clients: Set<string>;
|
|
19
|
+
abstract send(id: string, message: JsonRpcMessage): void | Promise<void>;
|
|
20
|
+
/**
|
|
21
|
+
* Returns the list of all connected client IDs.
|
|
22
|
+
* @returns Array of client IDs
|
|
23
|
+
*/
|
|
24
|
+
getClients(): Promise<Set<string>>;
|
|
25
|
+
/**
|
|
26
|
+
* Sets the list of all connected client IDs.
|
|
27
|
+
* @param clients - Set of client IDs
|
|
28
|
+
*/
|
|
29
|
+
setClients(clients: Set<string>): Promise<void>;
|
|
30
|
+
/**
|
|
31
|
+
* Registers a new client connection with the signaling service.
|
|
32
|
+
* Assigns a unique ID to the client and informs them of other connected peers.
|
|
33
|
+
*
|
|
34
|
+
* @param id - Optional client ID (generated if not provided)
|
|
35
|
+
* @returns The client's assigned ID
|
|
36
|
+
*/
|
|
37
|
+
onClientConnected(id?: string): Promise<string>;
|
|
38
|
+
/**
|
|
39
|
+
* Handles a client disconnection by removing them from the registry
|
|
40
|
+
* and notifying all other connected clients.
|
|
41
|
+
*
|
|
42
|
+
* @param id - ID of the disconnected client
|
|
43
|
+
*/
|
|
44
|
+
onClientDisconnected(id: string): Promise<void>;
|
|
45
|
+
/**
|
|
46
|
+
* Handles a signaling message from a client, relaying WebRTC session data
|
|
47
|
+
* between peers to facilitate connection establishment.
|
|
48
|
+
*
|
|
49
|
+
* @param fromId - ID of the client sending the message
|
|
50
|
+
* @param message - The JSON-RPC message or its string representation
|
|
51
|
+
* @returns True if the message was a valid signaling message and was handled, false otherwise
|
|
52
|
+
*/
|
|
53
|
+
handleClientMessage(fromId: string, message: string | JsonRpcRequest): Promise<boolean>;
|
|
54
|
+
/**
|
|
55
|
+
* Sends a successful JSON-RPC response to a client.
|
|
56
|
+
*
|
|
57
|
+
* @protected
|
|
58
|
+
* @param toId - ID of the client to send the response to
|
|
59
|
+
* @param id - Request ID to match in the response
|
|
60
|
+
* @param result - Result data to include in the response
|
|
61
|
+
*/
|
|
62
|
+
protected respond(toId: string, id: number, result: any): Promise<void>;
|
|
63
|
+
/**
|
|
64
|
+
* Sends an error JSON-RPC response to a client.
|
|
65
|
+
*
|
|
66
|
+
* @protected
|
|
67
|
+
* @param toId - ID of the client to send the error response to
|
|
68
|
+
* @param id - Request ID to match in the response, or undefined for notifications
|
|
69
|
+
* @param message - Error message to include
|
|
70
|
+
*/
|
|
71
|
+
protected respondError(toId: string, id: number | undefined, message: string): Promise<void>;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export { type JsonRpcMessage, SignalingService };
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import "../../chunk-IZ2YBCUP.js";
|
|
2
|
+
import { createId } from "crypto-id";
|
|
3
|
+
class SignalingService {
|
|
4
|
+
clients = /* @__PURE__ */ new Set();
|
|
5
|
+
/**
|
|
6
|
+
* Returns the list of all connected client IDs.
|
|
7
|
+
* @returns Array of client IDs
|
|
8
|
+
*/
|
|
9
|
+
async getClients() {
|
|
10
|
+
return new Set(this.clients);
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Sets the list of all connected client IDs.
|
|
14
|
+
* @param clients - Set of client IDs
|
|
15
|
+
*/
|
|
16
|
+
async setClients(clients) {
|
|
17
|
+
this.clients = clients;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Registers a new client connection with the signaling service.
|
|
21
|
+
* Assigns a unique ID to the client and informs them of other connected peers.
|
|
22
|
+
*
|
|
23
|
+
* @param id - Optional client ID (generated if not provided)
|
|
24
|
+
* @returns The client's assigned ID
|
|
25
|
+
*/
|
|
26
|
+
async onClientConnected(id = createId(14)) {
|
|
27
|
+
const clients = await this.getClients();
|
|
28
|
+
clients.add(id);
|
|
29
|
+
await this.setClients(clients);
|
|
30
|
+
const welcome = {
|
|
31
|
+
jsonrpc: "2.0",
|
|
32
|
+
method: "peer-welcome",
|
|
33
|
+
params: {
|
|
34
|
+
id,
|
|
35
|
+
peers: Array.from(clients).filter((pid) => pid !== id)
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
await this.send(id, welcome);
|
|
39
|
+
return id;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Handles a client disconnection by removing them from the registry
|
|
43
|
+
* and notifying all other connected clients.
|
|
44
|
+
*
|
|
45
|
+
* @param id - ID of the disconnected client
|
|
46
|
+
*/
|
|
47
|
+
async onClientDisconnected(id) {
|
|
48
|
+
const clients = await this.getClients();
|
|
49
|
+
clients.delete(id);
|
|
50
|
+
await this.setClients(clients);
|
|
51
|
+
const message = {
|
|
52
|
+
jsonrpc: "2.0",
|
|
53
|
+
method: "peer-disconnected",
|
|
54
|
+
params: { id }
|
|
55
|
+
};
|
|
56
|
+
await Promise.all(Array.from(clients).map((clientId) => this.send(clientId, message)));
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Handles a signaling message from a client, relaying WebRTC session data
|
|
60
|
+
* between peers to facilitate connection establishment.
|
|
61
|
+
*
|
|
62
|
+
* @param fromId - ID of the client sending the message
|
|
63
|
+
* @param message - The JSON-RPC message or its string representation
|
|
64
|
+
* @returns True if the message was a valid signaling message and was handled, false otherwise
|
|
65
|
+
*/
|
|
66
|
+
async handleClientMessage(fromId, message) {
|
|
67
|
+
let parsed;
|
|
68
|
+
try {
|
|
69
|
+
parsed = typeof message === "string" ? JSON.parse(message) : message;
|
|
70
|
+
} catch {
|
|
71
|
+
return false;
|
|
72
|
+
}
|
|
73
|
+
if (parsed.jsonrpc !== "2.0" || parsed.method !== "peer-signal" || !parsed.params?.to) return false;
|
|
74
|
+
const { params, id } = parsed;
|
|
75
|
+
const { to, data } = params;
|
|
76
|
+
const clients = await this.getClients();
|
|
77
|
+
if (!clients.has(to)) {
|
|
78
|
+
this.respondError(fromId, id, "Target not connected");
|
|
79
|
+
return true;
|
|
80
|
+
}
|
|
81
|
+
const outbound = {
|
|
82
|
+
jsonrpc: "2.0",
|
|
83
|
+
method: "signal",
|
|
84
|
+
params: {
|
|
85
|
+
from: fromId,
|
|
86
|
+
data
|
|
87
|
+
}
|
|
88
|
+
};
|
|
89
|
+
await this.send(to, outbound);
|
|
90
|
+
if (id !== void 0) {
|
|
91
|
+
await this.respond(fromId, id, "ok");
|
|
92
|
+
}
|
|
93
|
+
return true;
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Sends a successful JSON-RPC response to a client.
|
|
97
|
+
*
|
|
98
|
+
* @protected
|
|
99
|
+
* @param toId - ID of the client to send the response to
|
|
100
|
+
* @param id - Request ID to match in the response
|
|
101
|
+
* @param result - Result data to include in the response
|
|
102
|
+
*/
|
|
103
|
+
async respond(toId, id, result) {
|
|
104
|
+
const clients = await this.getClients();
|
|
105
|
+
if (!clients.has(toId)) return;
|
|
106
|
+
const response = {
|
|
107
|
+
jsonrpc: "2.0",
|
|
108
|
+
result,
|
|
109
|
+
id
|
|
110
|
+
};
|
|
111
|
+
await this.send(toId, response);
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Sends an error JSON-RPC response to a client.
|
|
115
|
+
*
|
|
116
|
+
* @protected
|
|
117
|
+
* @param toId - ID of the client to send the error response to
|
|
118
|
+
* @param id - Request ID to match in the response, or undefined for notifications
|
|
119
|
+
* @param message - Error message to include
|
|
120
|
+
*/
|
|
121
|
+
async respondError(toId, id, message) {
|
|
122
|
+
if (id === void 0) return;
|
|
123
|
+
const clients = await this.getClients();
|
|
124
|
+
if (!clients.has(toId)) return;
|
|
125
|
+
const response = {
|
|
126
|
+
jsonrpc: "2.0",
|
|
127
|
+
error: { code: -32e3, message },
|
|
128
|
+
id
|
|
129
|
+
};
|
|
130
|
+
await this.send(toId, response);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
export {
|
|
134
|
+
SignalingService
|
|
135
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./SignalingService.js";
|
|
@@ -6,8 +6,6 @@ import '../../json-patch/JSONPatch.js';
|
|
|
6
6
|
import '@dabble/delta';
|
|
7
7
|
import '../../json-patch/types.js';
|
|
8
8
|
import 'simple-peer';
|
|
9
|
-
import '../websocket/WebSocketTransport.js';
|
|
10
|
-
import '../../utils/deferred.js';
|
|
11
9
|
|
|
12
10
|
/**
|
|
13
11
|
* Base type for awareness states, representing arbitrary structured data
|
|
@@ -1,17 +1,17 @@
|
|
|
1
|
-
import { ClientTransport, ConnectionState } from '../protocol/types.js';
|
|
1
|
+
import { ClientTransport, ConnectionState, SignalingTransport } from '../protocol/types.js';
|
|
2
2
|
import * as easy_signal from 'easy-signal';
|
|
3
3
|
import Peer from 'simple-peer';
|
|
4
|
-
import { WebSocketTransport } from '../websocket/WebSocketTransport.js';
|
|
5
4
|
import '../../types.js';
|
|
6
5
|
import '../../json-patch/JSONPatch.js';
|
|
7
6
|
import '@dabble/delta';
|
|
8
7
|
import '../../json-patch/types.js';
|
|
9
|
-
import '../../utils/deferred.js';
|
|
10
8
|
|
|
11
9
|
/**
|
|
12
10
|
* WebRTC-based transport implementation that enables direct peer-to-peer communication.
|
|
13
|
-
* Uses a
|
|
14
|
-
*
|
|
11
|
+
* Uses a {@link SignalingTransport} (e.g. `WebSocketTransport`,
|
|
12
|
+
* `PatchesRESTSignalingTransport`) as a signaling channel to establish WebRTC
|
|
13
|
+
* connections. Once connections are established, data flows directly between peers
|
|
14
|
+
* without going through a server.
|
|
15
15
|
*/
|
|
16
16
|
declare class WebRTCTransport implements ClientTransport {
|
|
17
17
|
private transport;
|
|
@@ -36,14 +36,15 @@ declare class WebRTCTransport implements ClientTransport {
|
|
|
36
36
|
readonly onPeerDisconnect: easy_signal.Signal<(peerId: string, peer: Peer.Instance) => void>;
|
|
37
37
|
/**
|
|
38
38
|
* Signal that emits when the underlying signaling transport's state changes.
|
|
39
|
-
* This is delegated directly from the
|
|
39
|
+
* This is delegated directly from the signaling transport.
|
|
40
40
|
*/
|
|
41
41
|
get onStateChange(): easy_signal.Signal<(state: ConnectionState) => void>;
|
|
42
42
|
/**
|
|
43
43
|
* Creates a new WebRTC transport instance.
|
|
44
|
-
* @param transport -
|
|
44
|
+
* @param transport - A signaling-capable transport (e.g. `WebSocketTransport`
|
|
45
|
+
* or `PatchesRESTSignalingTransport`) used to relay WebRTC handshake messages.
|
|
45
46
|
*/
|
|
46
|
-
constructor(transport:
|
|
47
|
+
constructor(transport: SignalingTransport);
|
|
47
48
|
/**
|
|
48
49
|
* Gets the unique ID assigned to this peer by the signaling server.
|
|
49
50
|
* @returns The peer ID, or undefined if not yet connected
|
|
@@ -6,7 +6,8 @@ import { rpcError } from "../protocol/utils.js";
|
|
|
6
6
|
class WebRTCTransport {
|
|
7
7
|
/**
|
|
8
8
|
* Creates a new WebRTC transport instance.
|
|
9
|
-
* @param transport -
|
|
9
|
+
* @param transport - A signaling-capable transport (e.g. `WebSocketTransport`
|
|
10
|
+
* or `PatchesRESTSignalingTransport`) used to relay WebRTC handshake messages.
|
|
10
11
|
*/
|
|
11
12
|
constructor(transport) {
|
|
12
13
|
this.transport = transport;
|
|
@@ -48,7 +49,7 @@ class WebRTCTransport {
|
|
|
48
49
|
onPeerDisconnect = signal();
|
|
49
50
|
/**
|
|
50
51
|
* Signal that emits when the underlying signaling transport's state changes.
|
|
51
|
-
* This is delegated directly from the
|
|
52
|
+
* This is delegated directly from the signaling transport.
|
|
52
53
|
*/
|
|
53
54
|
get onStateChange() {
|
|
54
55
|
return this.transport.onStateChange;
|
|
@@ -1,71 +1,7 @@
|
|
|
1
|
-
|
|
1
|
+
export { JsonRpcMessage, SignalingService } from '../signaling/SignalingService.js';
|
|
2
|
+
import '../protocol/types.js';
|
|
2
3
|
import 'easy-signal';
|
|
3
4
|
import '../../types.js';
|
|
4
5
|
import '../../json-patch/JSONPatch.js';
|
|
5
6
|
import '@dabble/delta';
|
|
6
7
|
import '../../json-patch/types.js';
|
|
7
|
-
|
|
8
|
-
/** Union type for all possible JSON-RPC message types */
|
|
9
|
-
type JsonRpcMessage = JsonRpcRequest | JsonRpcResponse;
|
|
10
|
-
/**
|
|
11
|
-
* Service that facilitates WebRTC connection establishment by relaying signaling messages.
|
|
12
|
-
* Acts as a central hub for WebRTC peers to exchange connection information.
|
|
13
|
-
*/
|
|
14
|
-
declare abstract class SignalingService {
|
|
15
|
-
protected clients: Set<string>;
|
|
16
|
-
abstract send(id: string, message: JsonRpcMessage): void | Promise<void>;
|
|
17
|
-
/**
|
|
18
|
-
* Returns the list of all connected client IDs.
|
|
19
|
-
* @returns Array of client IDs
|
|
20
|
-
*/
|
|
21
|
-
getClients(): Promise<Set<string>>;
|
|
22
|
-
/**
|
|
23
|
-
* Sets the list of all connected client IDs.
|
|
24
|
-
* @param clients - Set of client IDs
|
|
25
|
-
*/
|
|
26
|
-
setClients(clients: Set<string>): Promise<void>;
|
|
27
|
-
/**
|
|
28
|
-
* Registers a new client connection with the signaling service.
|
|
29
|
-
* Assigns a unique ID to the client and informs them of other connected peers.
|
|
30
|
-
*
|
|
31
|
-
* @param id - Optional client ID (generated if not provided)
|
|
32
|
-
* @returns The client's assigned ID
|
|
33
|
-
*/
|
|
34
|
-
onClientConnected(id?: string): Promise<string>;
|
|
35
|
-
/**
|
|
36
|
-
* Handles a client disconnection by removing them from the registry
|
|
37
|
-
* and notifying all other connected clients.
|
|
38
|
-
*
|
|
39
|
-
* @param id - ID of the disconnected client
|
|
40
|
-
*/
|
|
41
|
-
onClientDisconnected(id: string): Promise<void>;
|
|
42
|
-
/**
|
|
43
|
-
* Handles a signaling message from a client, relaying WebRTC session data
|
|
44
|
-
* between peers to facilitate connection establishment.
|
|
45
|
-
*
|
|
46
|
-
* @param fromId - ID of the client sending the message
|
|
47
|
-
* @param message - The JSON-RPC message or its string representation
|
|
48
|
-
* @returns True if the message was a valid signaling message and was handled, false otherwise
|
|
49
|
-
*/
|
|
50
|
-
handleClientMessage(fromId: string, message: string | JsonRpcRequest): Promise<boolean>;
|
|
51
|
-
/**
|
|
52
|
-
* Sends a successful JSON-RPC response to a client.
|
|
53
|
-
*
|
|
54
|
-
* @protected
|
|
55
|
-
* @param toId - ID of the client to send the response to
|
|
56
|
-
* @param id - Request ID to match in the response
|
|
57
|
-
* @param result - Result data to include in the response
|
|
58
|
-
*/
|
|
59
|
-
protected respond(toId: string, id: number, result: any): Promise<void>;
|
|
60
|
-
/**
|
|
61
|
-
* Sends an error JSON-RPC response to a client.
|
|
62
|
-
*
|
|
63
|
-
* @protected
|
|
64
|
-
* @param toId - ID of the client to send the error response to
|
|
65
|
-
* @param id - Request ID to match in the response, or undefined for notifications
|
|
66
|
-
* @param message - Error message to include
|
|
67
|
-
*/
|
|
68
|
-
protected respondError(toId: string, id: number | undefined, message: string): Promise<void>;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
export { type JsonRpcMessage, SignalingService };
|
|
@@ -1,135 +1 @@
|
|
|
1
|
-
|
|
2
|
-
import { createId } from "crypto-id";
|
|
3
|
-
class SignalingService {
|
|
4
|
-
clients = /* @__PURE__ */ new Set();
|
|
5
|
-
/**
|
|
6
|
-
* Returns the list of all connected client IDs.
|
|
7
|
-
* @returns Array of client IDs
|
|
8
|
-
*/
|
|
9
|
-
async getClients() {
|
|
10
|
-
return new Set(this.clients);
|
|
11
|
-
}
|
|
12
|
-
/**
|
|
13
|
-
* Sets the list of all connected client IDs.
|
|
14
|
-
* @param clients - Set of client IDs
|
|
15
|
-
*/
|
|
16
|
-
async setClients(clients) {
|
|
17
|
-
this.clients = clients;
|
|
18
|
-
}
|
|
19
|
-
/**
|
|
20
|
-
* Registers a new client connection with the signaling service.
|
|
21
|
-
* Assigns a unique ID to the client and informs them of other connected peers.
|
|
22
|
-
*
|
|
23
|
-
* @param id - Optional client ID (generated if not provided)
|
|
24
|
-
* @returns The client's assigned ID
|
|
25
|
-
*/
|
|
26
|
-
async onClientConnected(id = createId(14)) {
|
|
27
|
-
const clients = await this.getClients();
|
|
28
|
-
clients.add(id);
|
|
29
|
-
await this.setClients(clients);
|
|
30
|
-
const welcome = {
|
|
31
|
-
jsonrpc: "2.0",
|
|
32
|
-
method: "peer-welcome",
|
|
33
|
-
params: {
|
|
34
|
-
id,
|
|
35
|
-
peers: Array.from(this.clients).filter((pid) => pid !== id)
|
|
36
|
-
}
|
|
37
|
-
};
|
|
38
|
-
this.send(id, welcome);
|
|
39
|
-
return id;
|
|
40
|
-
}
|
|
41
|
-
/**
|
|
42
|
-
* Handles a client disconnection by removing them from the registry
|
|
43
|
-
* and notifying all other connected clients.
|
|
44
|
-
*
|
|
45
|
-
* @param id - ID of the disconnected client
|
|
46
|
-
*/
|
|
47
|
-
async onClientDisconnected(id) {
|
|
48
|
-
const clients = await this.getClients();
|
|
49
|
-
clients.delete(id);
|
|
50
|
-
await this.setClients(clients);
|
|
51
|
-
const message = {
|
|
52
|
-
jsonrpc: "2.0",
|
|
53
|
-
method: "peer-disconnected",
|
|
54
|
-
params: { id }
|
|
55
|
-
};
|
|
56
|
-
await Promise.all(Array.from(clients).map((clientId) => this.send(clientId, message)));
|
|
57
|
-
}
|
|
58
|
-
/**
|
|
59
|
-
* Handles a signaling message from a client, relaying WebRTC session data
|
|
60
|
-
* between peers to facilitate connection establishment.
|
|
61
|
-
*
|
|
62
|
-
* @param fromId - ID of the client sending the message
|
|
63
|
-
* @param message - The JSON-RPC message or its string representation
|
|
64
|
-
* @returns True if the message was a valid signaling message and was handled, false otherwise
|
|
65
|
-
*/
|
|
66
|
-
async handleClientMessage(fromId, message) {
|
|
67
|
-
let parsed;
|
|
68
|
-
try {
|
|
69
|
-
parsed = typeof message === "string" ? JSON.parse(message) : message;
|
|
70
|
-
} catch {
|
|
71
|
-
return false;
|
|
72
|
-
}
|
|
73
|
-
if (parsed.jsonrpc !== "2.0" || parsed.method !== "peer-signal" || !parsed.params?.to) return false;
|
|
74
|
-
const { params, id } = parsed;
|
|
75
|
-
const { to, data } = params;
|
|
76
|
-
const clients = await this.getClients();
|
|
77
|
-
if (!clients.has(to)) {
|
|
78
|
-
this.respondError(fromId, id, "Target not connected");
|
|
79
|
-
return true;
|
|
80
|
-
}
|
|
81
|
-
const outbound = {
|
|
82
|
-
jsonrpc: "2.0",
|
|
83
|
-
method: "signal",
|
|
84
|
-
params: {
|
|
85
|
-
from: fromId,
|
|
86
|
-
data
|
|
87
|
-
}
|
|
88
|
-
};
|
|
89
|
-
await this.send(to, outbound);
|
|
90
|
-
if (id !== void 0) {
|
|
91
|
-
await this.respond(fromId, id, "ok");
|
|
92
|
-
}
|
|
93
|
-
return true;
|
|
94
|
-
}
|
|
95
|
-
/**
|
|
96
|
-
* Sends a successful JSON-RPC response to a client.
|
|
97
|
-
*
|
|
98
|
-
* @protected
|
|
99
|
-
* @param toId - ID of the client to send the response to
|
|
100
|
-
* @param id - Request ID to match in the response
|
|
101
|
-
* @param result - Result data to include in the response
|
|
102
|
-
*/
|
|
103
|
-
async respond(toId, id, result) {
|
|
104
|
-
const clients = await this.getClients();
|
|
105
|
-
if (!clients.has(toId)) return;
|
|
106
|
-
const response = {
|
|
107
|
-
jsonrpc: "2.0",
|
|
108
|
-
result,
|
|
109
|
-
id
|
|
110
|
-
};
|
|
111
|
-
await this.send(toId, response);
|
|
112
|
-
}
|
|
113
|
-
/**
|
|
114
|
-
* Sends an error JSON-RPC response to a client.
|
|
115
|
-
*
|
|
116
|
-
* @protected
|
|
117
|
-
* @param toId - ID of the client to send the error response to
|
|
118
|
-
* @param id - Request ID to match in the response, or undefined for notifications
|
|
119
|
-
* @param message - Error message to include
|
|
120
|
-
*/
|
|
121
|
-
async respondError(toId, id, message) {
|
|
122
|
-
if (id === void 0) return;
|
|
123
|
-
const clients = await this.getClients();
|
|
124
|
-
if (!clients.has(toId)) return;
|
|
125
|
-
const response = {
|
|
126
|
-
jsonrpc: "2.0",
|
|
127
|
-
error: { code: -32e3, message },
|
|
128
|
-
id
|
|
129
|
-
};
|
|
130
|
-
await this.send(toId, response);
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
export {
|
|
134
|
-
SignalingService
|
|
135
|
-
};
|
|
1
|
+
export * from "../signaling/SignalingService.js";
|
|
@@ -5,10 +5,10 @@ import { LWWServer } from './LWWServer.js';
|
|
|
5
5
|
import { LWWStoreBackend, BranchingStoreBackend } from './types.js';
|
|
6
6
|
import 'easy-signal';
|
|
7
7
|
import '../net/websocket/AuthorizationProvider.js';
|
|
8
|
-
import '../
|
|
8
|
+
import '../json-patch/types.js';
|
|
9
9
|
import '../json-patch/JSONPatch.js';
|
|
10
10
|
import '@dabble/delta';
|
|
11
|
-
import '../
|
|
11
|
+
import '../net/protocol/types.js';
|
|
12
12
|
import './PatchesServer.js';
|
|
13
13
|
|
|
14
14
|
/**
|
|
@@ -4,10 +4,10 @@ import { Change, CommitChangesOptions, DeleteDocOptions, ChangeInput, ChangeMuta
|
|
|
4
4
|
import { PatchesServer } from './PatchesServer.js';
|
|
5
5
|
import { LWWStoreBackend } from './types.js';
|
|
6
6
|
import '../net/websocket/AuthorizationProvider.js';
|
|
7
|
-
import '../
|
|
7
|
+
import '../json-patch/types.js';
|
|
8
8
|
import '../json-patch/JSONPatch.js';
|
|
9
9
|
import '@dabble/delta';
|
|
10
|
-
import '../
|
|
10
|
+
import '../net/protocol/types.js';
|
|
11
11
|
|
|
12
12
|
/**
|
|
13
13
|
* Configuration options for LWWServer.
|
package/dist/server/types.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { JSONPatchOp } from '../json-patch/types.js';
|
|
2
|
-
import { VersionMetadata, Change, ListVersionsOptions, EditableVersionMetadata, ListChangesOptions,
|
|
2
|
+
import { DocumentTombstone, VersionMetadata, Change, ListVersionsOptions, EditableVersionMetadata, ListChangesOptions, ListBranchesOptions, Branch } from '../types.js';
|
|
3
3
|
import '../json-patch/JSONPatch.js';
|
|
4
4
|
import '@dabble/delta';
|
|
5
5
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dabble/patches",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.2",
|
|
4
4
|
"description": "Immutable JSON Patch implementation based on RFC 6902 supporting operational transformation and last-writer-wins",
|
|
5
5
|
"author": "Jacob Wright <jacwright@gmail.com>",
|
|
6
6
|
"bugs": {
|