@abraca/dabra 1.0.3 → 1.0.5
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/abracadabra-provider.cjs +561 -37
- package/dist/abracadabra-provider.cjs.map +1 -1
- package/dist/abracadabra-provider.esm.js +558 -38
- package/dist/abracadabra-provider.esm.js.map +1 -1
- package/dist/index.d.ts +165 -2
- package/package.json +1 -1
- package/src/AbracadabraProvider.ts +11 -8
- package/src/AbracadabraWS.ts +1 -1
- package/src/index.ts +1 -0
- package/src/sync/BroadcastChannelSync.ts +235 -0
- package/src/types.ts +2 -0
- package/src/webrtc/AbracadabraWebRTC.ts +68 -1
- package/src/webrtc/DataChannelRouter.ts +73 -5
- package/src/webrtc/E2EEChannel.ts +195 -0
- package/src/webrtc/ManualSignaling.ts +197 -0
- package/src/webrtc/YjsDataChannel.ts +37 -30
- package/src/webrtc/index.ts +5 -1
- package/src/webrtc/types.ts +18 -0
|
@@ -23,11 +23,26 @@ export class YjsDataChannel {
|
|
|
23
23
|
private channelOpenHandler: ((data: { name: string; channel: RTCDataChannel }) => void) | null = null;
|
|
24
24
|
private channelMessageHandler: ((data: { name: string; data: any }) => void) | null = null;
|
|
25
25
|
|
|
26
|
+
/** Channel names used for sync and awareness (supports namespaced subdoc channels). */
|
|
27
|
+
private readonly syncChannelName: string;
|
|
28
|
+
private readonly awarenessChannelName: string;
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* @param document - The Y.Doc to sync
|
|
32
|
+
* @param awareness - Optional Awareness instance
|
|
33
|
+
* @param router - DataChannelRouter for the peer connection
|
|
34
|
+
* @param channelPrefix - Optional prefix for subdocument channels (e.g. `"{childId}:"`)
|
|
35
|
+
*/
|
|
26
36
|
constructor(
|
|
27
37
|
private readonly document: Y.Doc,
|
|
28
38
|
private readonly awareness: Awareness | null,
|
|
29
39
|
private readonly router: DataChannelRouter,
|
|
30
|
-
|
|
40
|
+
channelPrefix?: string,
|
|
41
|
+
) {
|
|
42
|
+
const prefix = channelPrefix ?? "";
|
|
43
|
+
this.syncChannelName = `${prefix}${CHANNEL_NAMES.YJS_SYNC}`;
|
|
44
|
+
this.awarenessChannelName = `${prefix}${CHANNEL_NAMES.AWARENESS}`;
|
|
45
|
+
}
|
|
31
46
|
|
|
32
47
|
/** Start listening for Y.js updates and data channel messages. */
|
|
33
48
|
attach(): void {
|
|
@@ -36,13 +51,12 @@ export class YjsDataChannel {
|
|
|
36
51
|
// Don't echo updates we received from this data channel.
|
|
37
52
|
if (origin === this) return;
|
|
38
53
|
|
|
39
|
-
|
|
40
|
-
if (!channel || channel.readyState !== "open") return;
|
|
54
|
+
if (!this.router.isOpen(this.syncChannelName)) return;
|
|
41
55
|
|
|
42
56
|
const encoder = encoding.createEncoder();
|
|
43
57
|
encoding.writeVarUint(encoder, YJS_MSG.UPDATE);
|
|
44
58
|
encoding.writeVarUint8Array(encoder, update);
|
|
45
|
-
|
|
59
|
+
this.router.send(this.syncChannelName, encoding.toUint8Array(encoder));
|
|
46
60
|
};
|
|
47
61
|
this.document.on("update", this.docUpdateHandler);
|
|
48
62
|
|
|
@@ -52,21 +66,20 @@ export class YjsDataChannel {
|
|
|
52
66
|
{ added, updated, removed }: { added: number[]; updated: number[]; removed: number[] },
|
|
53
67
|
_origin: any,
|
|
54
68
|
) => {
|
|
55
|
-
|
|
56
|
-
if (!channel || channel.readyState !== "open") return;
|
|
69
|
+
if (!this.router.isOpen(this.awarenessChannelName)) return;
|
|
57
70
|
|
|
58
71
|
const changedClients = added.concat(updated).concat(removed);
|
|
59
72
|
const update = encodeAwarenessUpdate(this.awareness!, changedClients);
|
|
60
|
-
|
|
73
|
+
this.router.send(this.awarenessChannelName, update);
|
|
61
74
|
};
|
|
62
75
|
this.awareness.on("update", this.awarenessUpdateHandler);
|
|
63
76
|
}
|
|
64
77
|
|
|
65
78
|
// Handle incoming data channel messages.
|
|
66
79
|
this.channelMessageHandler = ({ name, data }: { name: string; data: any }) => {
|
|
67
|
-
if (name ===
|
|
80
|
+
if (name === this.syncChannelName) {
|
|
68
81
|
this.handleSyncMessage(data);
|
|
69
|
-
} else if (name ===
|
|
82
|
+
} else if (name === this.awarenessChannelName) {
|
|
70
83
|
this.handleAwarenessMessage(data);
|
|
71
84
|
}
|
|
72
85
|
};
|
|
@@ -74,37 +87,33 @@ export class YjsDataChannel {
|
|
|
74
87
|
|
|
75
88
|
// When sync channel opens, initiate sync handshake.
|
|
76
89
|
this.channelOpenHandler = ({ name }: { name: string; channel: RTCDataChannel }) => {
|
|
77
|
-
if (name ===
|
|
90
|
+
if (name === this.syncChannelName) {
|
|
78
91
|
this.sendSyncStep1();
|
|
79
|
-
} else if (name ===
|
|
92
|
+
} else if (name === this.awarenessChannelName && this.awareness) {
|
|
80
93
|
// Send full awareness state on channel open.
|
|
81
|
-
|
|
82
|
-
if (channel?.readyState === "open") {
|
|
94
|
+
if (this.router.isOpen(this.awarenessChannelName)) {
|
|
83
95
|
const update = encodeAwarenessUpdate(
|
|
84
96
|
this.awareness,
|
|
85
97
|
Array.from(this.awareness.getStates().keys()),
|
|
86
98
|
);
|
|
87
|
-
|
|
99
|
+
this.router.send(this.awarenessChannelName, update);
|
|
88
100
|
}
|
|
89
101
|
}
|
|
90
102
|
};
|
|
91
103
|
this.router.on("channelOpen", this.channelOpenHandler);
|
|
92
104
|
|
|
93
105
|
// If sync channel is already open, start sync immediately.
|
|
94
|
-
if (this.router.isOpen(
|
|
106
|
+
if (this.router.isOpen(this.syncChannelName)) {
|
|
95
107
|
this.sendSyncStep1();
|
|
96
108
|
}
|
|
97
109
|
|
|
98
110
|
// If awareness channel is already open, send state immediately.
|
|
99
|
-
if (this.awareness && this.router.isOpen(
|
|
100
|
-
const
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
);
|
|
106
|
-
channel.send(update);
|
|
107
|
-
}
|
|
111
|
+
if (this.awareness && this.router.isOpen(this.awarenessChannelName)) {
|
|
112
|
+
const update = encodeAwarenessUpdate(
|
|
113
|
+
this.awareness,
|
|
114
|
+
Array.from(this.awareness.getStates().keys()),
|
|
115
|
+
);
|
|
116
|
+
this.router.send(this.awarenessChannelName, update);
|
|
108
117
|
}
|
|
109
118
|
}
|
|
110
119
|
|
|
@@ -138,13 +147,12 @@ export class YjsDataChannel {
|
|
|
138
147
|
}
|
|
139
148
|
|
|
140
149
|
private sendSyncStep1(): void {
|
|
141
|
-
|
|
142
|
-
if (!channel || channel.readyState !== "open") return;
|
|
150
|
+
if (!this.router.isOpen(this.syncChannelName)) return;
|
|
143
151
|
|
|
144
152
|
const encoder = encoding.createEncoder();
|
|
145
153
|
encoding.writeVarUint(encoder, YJS_MSG.SYNC);
|
|
146
154
|
syncProtocol.writeSyncStep1(encoder, this.document);
|
|
147
|
-
|
|
155
|
+
this.router.send(this.syncChannelName, encoding.toUint8Array(encoder));
|
|
148
156
|
}
|
|
149
157
|
|
|
150
158
|
private handleSyncMessage(data: ArrayBuffer | Uint8Array): void {
|
|
@@ -170,9 +178,8 @@ export class YjsDataChannel {
|
|
|
170
178
|
responseEncoder,
|
|
171
179
|
encoding.toUint8Array(encoder),
|
|
172
180
|
);
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
channel.send(encoding.toUint8Array(responseEncoder));
|
|
181
|
+
if (this.router.isOpen(this.syncChannelName)) {
|
|
182
|
+
this.router.send(this.syncChannelName, encoding.toUint8Array(responseEncoder));
|
|
176
183
|
}
|
|
177
184
|
}
|
|
178
185
|
|
package/src/webrtc/index.ts
CHANGED
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
export { AbracadabraWebRTC } from "./AbracadabraWebRTC.ts";
|
|
2
2
|
export { SignalingSocket } from "./SignalingSocket.ts";
|
|
3
3
|
export { PeerConnection } from "./PeerConnection.ts";
|
|
4
|
-
export { DataChannelRouter } from "./DataChannelRouter.ts";
|
|
4
|
+
export { DataChannelRouter, KEY_EXCHANGE_CHANNEL } from "./DataChannelRouter.ts";
|
|
5
5
|
export { YjsDataChannel } from "./YjsDataChannel.ts";
|
|
6
6
|
export { FileTransferChannel, FileTransferHandle } from "./FileTransferChannel.ts";
|
|
7
|
+
export { E2EEChannel } from "./E2EEChannel.ts";
|
|
8
|
+
export type { E2EEIdentity } from "./E2EEChannel.ts";
|
|
9
|
+
export { ManualSignaling } from "./ManualSignaling.ts";
|
|
10
|
+
export type { ManualSignalingBlob } from "./ManualSignaling.ts";
|
|
7
11
|
export type {
|
|
8
12
|
AbracadabraWebRTCConfiguration,
|
|
9
13
|
PeerInfo,
|
package/src/webrtc/types.ts
CHANGED
|
@@ -98,6 +98,10 @@ export const CHANNEL_NAMES = {
|
|
|
98
98
|
CUSTOM: "custom",
|
|
99
99
|
} as const;
|
|
100
100
|
|
|
101
|
+
// ── E2EE ────────────────────────────────────────────────────────────────────
|
|
102
|
+
|
|
103
|
+
export type { E2EEIdentity } from "./E2EEChannel.ts";
|
|
104
|
+
|
|
101
105
|
// ── Configuration ───────────────────────────────────────────────────────────
|
|
102
106
|
|
|
103
107
|
export interface AbracadabraWebRTCConfiguration {
|
|
@@ -107,6 +111,13 @@ export interface AbracadabraWebRTCConfiguration {
|
|
|
107
111
|
/** Server base URL (http/https). Signaling URL derived automatically. */
|
|
108
112
|
url: string;
|
|
109
113
|
|
|
114
|
+
/**
|
|
115
|
+
* Override the signaling WebSocket URL. When set, signaling connects to
|
|
116
|
+
* this server instead of deriving from `url`. Useful for local spaces that
|
|
117
|
+
* piggyback a remote server's signaling endpoint.
|
|
118
|
+
*/
|
|
119
|
+
signalingUrl?: string;
|
|
120
|
+
|
|
110
121
|
/** JWT token or async token factory. */
|
|
111
122
|
token: string | (() => string) | (() => Promise<string>);
|
|
112
123
|
|
|
@@ -142,6 +153,13 @@ export interface AbracadabraWebRTCConfiguration {
|
|
|
142
153
|
/** Auto-connect on construction. Default: true. */
|
|
143
154
|
autoConnect?: boolean;
|
|
144
155
|
|
|
156
|
+
/**
|
|
157
|
+
* E2EE identity for application-level encryption on data channels.
|
|
158
|
+
* When provided, all data channel messages (except key-exchange) are
|
|
159
|
+
* encrypted with AES-256-GCM using X25519 ECDH-derived session keys.
|
|
160
|
+
*/
|
|
161
|
+
e2ee?: import("./E2EEChannel.ts").E2EEIdentity;
|
|
162
|
+
|
|
145
163
|
/** WebSocket polyfill for signaling (e.g. for Node.js). */
|
|
146
164
|
WebSocketPolyfill?: any;
|
|
147
165
|
}
|