ugly-app 0.1.233 → 0.1.234
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/cli/version.d.ts +1 -1
- package/dist/cli/version.js +1 -1
- package/dist/collab/client/CollabProvider.d.ts +36 -0
- package/dist/collab/client/CollabProvider.d.ts.map +1 -0
- package/dist/collab/client/CollabProvider.js +152 -0
- package/dist/collab/client/CollabProvider.js.map +1 -0
- package/dist/collab/client/collabUtils.d.ts +3 -0
- package/dist/collab/client/collabUtils.d.ts.map +1 -0
- package/dist/collab/client/collabUtils.js +17 -0
- package/dist/collab/client/collabUtils.js.map +1 -0
- package/dist/collab/client/index.d.ts +5 -0
- package/dist/collab/client/index.d.ts.map +1 -0
- package/dist/collab/client/index.js +4 -0
- package/dist/collab/client/index.js.map +1 -0
- package/dist/collab/client/useCollab.d.ts +14 -0
- package/dist/collab/client/useCollab.d.ts.map +1 -0
- package/dist/collab/client/useCollab.js +38 -0
- package/dist/collab/client/useCollab.js.map +1 -0
- package/dist/collab/server/CollabServer.d.ts +64 -0
- package/dist/collab/server/CollabServer.d.ts.map +1 -0
- package/dist/collab/server/CollabServer.js +381 -0
- package/dist/collab/server/CollabServer.js.map +1 -0
- package/dist/collab/server/index.d.ts +20 -0
- package/dist/collab/server/index.d.ts.map +1 -0
- package/dist/collab/server/index.js +21 -0
- package/dist/collab/server/index.js.map +1 -0
- package/dist/collab/shared/CollabTypes.d.ts +31 -0
- package/dist/collab/shared/CollabTypes.d.ts.map +1 -0
- package/dist/collab/shared/CollabTypes.js +3 -0
- package/dist/collab/shared/CollabTypes.js.map +1 -0
- package/dist/server/Email.d.ts +4 -7
- package/dist/server/Email.d.ts.map +1 -1
- package/dist/server/Email.js +26 -37
- package/dist/server/Email.js.map +1 -1
- package/package.json +10 -3
- package/src/cli/version.ts +1 -1
- package/src/collab/client/CollabProvider.ts +187 -0
- package/src/collab/client/collabUtils.ts +18 -0
- package/src/collab/client/index.ts +12 -0
- package/src/collab/client/useCollab.ts +56 -0
- package/src/collab/server/CollabServer.ts +530 -0
- package/src/collab/server/index.ts +36 -0
- package/src/collab/shared/CollabTypes.ts +44 -0
- package/src/server/Email.ts +28 -43
package/dist/cli/version.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export declare const CLI_VERSION = "0.1.
|
|
1
|
+
export declare const CLI_VERSION = "0.1.234";
|
|
2
2
|
//# sourceMappingURL=version.d.ts.map
|
package/dist/cli/version.js
CHANGED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import * as Y from 'yjs';
|
|
2
|
+
/**
|
|
3
|
+
* Transport interface for sending collab messages over a WebSocket.
|
|
4
|
+
* The consuming app wires this to its own socket implementation.
|
|
5
|
+
*/
|
|
6
|
+
export interface CollabTransport {
|
|
7
|
+
/** RPC — sends a message and waits for a response. */
|
|
8
|
+
send(type: string, data: object): Promise<unknown>;
|
|
9
|
+
/** Fire-and-forget — sends a message with no response expected. */
|
|
10
|
+
emit(type: string, data: object): void;
|
|
11
|
+
}
|
|
12
|
+
type PeerIdsCallback = (peerIds: string[]) => void;
|
|
13
|
+
export declare class CollabClient {
|
|
14
|
+
readonly ydoc: Y.Doc;
|
|
15
|
+
readonly docId: string;
|
|
16
|
+
private connected;
|
|
17
|
+
private destroyed;
|
|
18
|
+
private pendingUpdates;
|
|
19
|
+
private onPeersChange;
|
|
20
|
+
private transport;
|
|
21
|
+
private updateHandler;
|
|
22
|
+
private awarenessHandler;
|
|
23
|
+
private peersHandler;
|
|
24
|
+
private reconnectHandler;
|
|
25
|
+
constructor(docId: string, transport: CollabTransport | null);
|
|
26
|
+
setTransport(transport: CollabTransport | null): void;
|
|
27
|
+
setPeersCallback(cb: PeerIdsCallback): void;
|
|
28
|
+
connect(): Promise<void>;
|
|
29
|
+
private handleLocalUpdate;
|
|
30
|
+
private sendUpdate;
|
|
31
|
+
disconnect(): void;
|
|
32
|
+
destroy(): void;
|
|
33
|
+
isConnected(): boolean;
|
|
34
|
+
}
|
|
35
|
+
export {};
|
|
36
|
+
//# sourceMappingURL=CollabProvider.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CollabProvider.d.ts","sourceRoot":"","sources":["../../../src/collab/client/CollabProvider.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,CAAC,MAAM,KAAK,CAAC;AAUzB;;;GAGG;AACH,MAAM,WAAW,eAAe;IAC9B,sDAAsD;IACtD,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IACnD,mEAAmE;IACnE,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;CACxC;AAED,KAAK,eAAe,GAAG,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,IAAI,CAAC;AAEnD,qBAAa,YAAY;IACvB,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC;IACrB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,cAAc,CAAoB;IAC1C,OAAO,CAAC,aAAa,CAAgC;IACrD,OAAO,CAAC,SAAS,CAAyB;IAG1C,OAAO,CAAC,aAAa,CAA+C;IACpE,OAAO,CAAC,gBAAgB,CAA+C;IACvE,OAAO,CAAC,YAAY,CAA+C;IACnE,OAAO,CAAC,gBAAgB,CAA+C;gBAE3D,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,eAAe,GAAG,IAAI;IAoD5D,YAAY,CAAC,SAAS,EAAE,eAAe,GAAG,IAAI,GAAG,IAAI;IAIrD,gBAAgB,CAAC,EAAE,EAAE,eAAe,GAAG,IAAI;IAIrC,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAmC9B,OAAO,CAAC,iBAAiB;IAazB,OAAO,CAAC,UAAU;IAQlB,UAAU,IAAI,IAAI;IAQlB,OAAO,IAAI,IAAI;IAqBf,WAAW,IAAI,OAAO;CAGvB"}
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import * as Y from 'yjs';
|
|
2
|
+
import { base64Decode, base64Encode } from './collabUtils.js';
|
|
3
|
+
export class CollabClient {
|
|
4
|
+
ydoc;
|
|
5
|
+
docId;
|
|
6
|
+
connected = false;
|
|
7
|
+
destroyed = false;
|
|
8
|
+
pendingUpdates = [];
|
|
9
|
+
onPeersChange = null;
|
|
10
|
+
transport;
|
|
11
|
+
// Event listener cleanup references
|
|
12
|
+
updateHandler = null;
|
|
13
|
+
awarenessHandler = null;
|
|
14
|
+
peersHandler = null;
|
|
15
|
+
reconnectHandler = null;
|
|
16
|
+
constructor(docId, transport) {
|
|
17
|
+
this.docId = docId;
|
|
18
|
+
this.transport = transport;
|
|
19
|
+
this.ydoc = new Y.Doc();
|
|
20
|
+
// Listen for local Yjs updates
|
|
21
|
+
this.ydoc.on('update', (update, origin) => {
|
|
22
|
+
if (origin === 'remote')
|
|
23
|
+
return;
|
|
24
|
+
this.handleLocalUpdate(update);
|
|
25
|
+
});
|
|
26
|
+
// Listen for remote events from WebSocket (dispatched as CustomEvents on window)
|
|
27
|
+
this.updateHandler = (event) => {
|
|
28
|
+
const msg = event.detail;
|
|
29
|
+
if (msg.docId !== this.docId)
|
|
30
|
+
return;
|
|
31
|
+
try {
|
|
32
|
+
const update = base64Decode(msg.update);
|
|
33
|
+
Y.applyUpdate(this.ydoc, update, 'remote');
|
|
34
|
+
}
|
|
35
|
+
catch (error) {
|
|
36
|
+
console.error('[Collab] Failed to apply remote update', error);
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
this.awarenessHandler = (event) => {
|
|
40
|
+
const msg = event.detail;
|
|
41
|
+
if (msg.docId !== this.docId)
|
|
42
|
+
return;
|
|
43
|
+
// Awareness is handled at a higher level if needed
|
|
44
|
+
};
|
|
45
|
+
this.peersHandler = (event) => {
|
|
46
|
+
const msg = event.detail;
|
|
47
|
+
if (msg.docId !== this.docId)
|
|
48
|
+
return;
|
|
49
|
+
this.onPeersChange?.(msg.peerIds);
|
|
50
|
+
};
|
|
51
|
+
this.reconnectHandler = (event) => {
|
|
52
|
+
const detail = event.detail;
|
|
53
|
+
if (detail.state === 'connected' && detail.wasReconnect) {
|
|
54
|
+
this.connected = false;
|
|
55
|
+
void this.connect();
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
window.addEventListener('collab:update', this.updateHandler);
|
|
59
|
+
window.addEventListener('collab:awareness', this.awarenessHandler);
|
|
60
|
+
window.addEventListener('collab:peers', this.peersHandler);
|
|
61
|
+
window.addEventListener('ws-connection-state', this.reconnectHandler);
|
|
62
|
+
}
|
|
63
|
+
setTransport(transport) {
|
|
64
|
+
this.transport = transport;
|
|
65
|
+
}
|
|
66
|
+
setPeersCallback(cb) {
|
|
67
|
+
this.onPeersChange = cb;
|
|
68
|
+
}
|
|
69
|
+
async connect() {
|
|
70
|
+
if (this.destroyed || this.connected || !this.transport)
|
|
71
|
+
return;
|
|
72
|
+
try {
|
|
73
|
+
const stateVector = Y.encodeStateVector(this.ydoc);
|
|
74
|
+
const input = {
|
|
75
|
+
docId: this.docId,
|
|
76
|
+
stateVector: base64Encode(stateVector),
|
|
77
|
+
};
|
|
78
|
+
const response = (await this.transport.send('collab:join', input));
|
|
79
|
+
if (this.destroyed)
|
|
80
|
+
return;
|
|
81
|
+
if (response.update) {
|
|
82
|
+
const update = base64Decode(response.update);
|
|
83
|
+
Y.applyUpdate(this.ydoc, update, 'remote');
|
|
84
|
+
}
|
|
85
|
+
this.connected = true;
|
|
86
|
+
if (response.peerIds) {
|
|
87
|
+
this.onPeersChange?.(response.peerIds);
|
|
88
|
+
}
|
|
89
|
+
// Flush pending updates
|
|
90
|
+
for (const update of this.pendingUpdates) {
|
|
91
|
+
this.sendUpdate(update);
|
|
92
|
+
}
|
|
93
|
+
this.pendingUpdates = [];
|
|
94
|
+
}
|
|
95
|
+
catch (error) {
|
|
96
|
+
console.error('[Collab] Failed to join', error);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
handleLocalUpdate(update) {
|
|
100
|
+
if (this.destroyed)
|
|
101
|
+
return;
|
|
102
|
+
if (this.connected) {
|
|
103
|
+
this.sendUpdate(update);
|
|
104
|
+
}
|
|
105
|
+
else {
|
|
106
|
+
this.pendingUpdates.push(update);
|
|
107
|
+
if (this.pendingUpdates.length > 1000) {
|
|
108
|
+
const merged = Y.mergeUpdatesV2(this.pendingUpdates);
|
|
109
|
+
this.pendingUpdates = [merged];
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
sendUpdate(update) {
|
|
114
|
+
if (!this.transport)
|
|
115
|
+
return;
|
|
116
|
+
this.transport.emit('collab:update', {
|
|
117
|
+
docId: this.docId,
|
|
118
|
+
update: base64Encode(update),
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
disconnect() {
|
|
122
|
+
if (!this.connected)
|
|
123
|
+
return;
|
|
124
|
+
if (this.transport) {
|
|
125
|
+
this.transport.emit('collab:leave', { docId: this.docId });
|
|
126
|
+
}
|
|
127
|
+
this.connected = false;
|
|
128
|
+
}
|
|
129
|
+
destroy() {
|
|
130
|
+
if (this.destroyed)
|
|
131
|
+
return;
|
|
132
|
+
this.destroyed = true;
|
|
133
|
+
this.disconnect();
|
|
134
|
+
if (this.updateHandler) {
|
|
135
|
+
window.removeEventListener('collab:update', this.updateHandler);
|
|
136
|
+
}
|
|
137
|
+
if (this.awarenessHandler) {
|
|
138
|
+
window.removeEventListener('collab:awareness', this.awarenessHandler);
|
|
139
|
+
}
|
|
140
|
+
if (this.peersHandler) {
|
|
141
|
+
window.removeEventListener('collab:peers', this.peersHandler);
|
|
142
|
+
}
|
|
143
|
+
if (this.reconnectHandler) {
|
|
144
|
+
window.removeEventListener('ws-connection-state', this.reconnectHandler);
|
|
145
|
+
}
|
|
146
|
+
this.ydoc.destroy();
|
|
147
|
+
}
|
|
148
|
+
isConnected() {
|
|
149
|
+
return this.connected;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
//# sourceMappingURL=CollabProvider.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CollabProvider.js","sourceRoot":"","sources":["../../../src/collab/client/CollabProvider.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"collabUtils.d.ts","sourceRoot":"","sources":["../../../src/collab/client/collabUtils.ts"],"names":[],"mappings":"AAEA,wBAAgB,YAAY,CAAC,IAAI,EAAE,UAAU,GAAG,MAAM,CAMrD;AAED,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,UAAU,CAOvD"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
// Base64 encode/decode for Yjs binary data (browser-safe)
|
|
2
|
+
export function base64Encode(data) {
|
|
3
|
+
let binary = '';
|
|
4
|
+
for (let i = 0; i < data.length; i++) {
|
|
5
|
+
binary += String.fromCharCode(data[i]);
|
|
6
|
+
}
|
|
7
|
+
return btoa(binary);
|
|
8
|
+
}
|
|
9
|
+
export function base64Decode(base64) {
|
|
10
|
+
const binary = atob(base64);
|
|
11
|
+
const data = new Uint8Array(binary.length);
|
|
12
|
+
for (let i = 0; i < binary.length; i++) {
|
|
13
|
+
data[i] = binary.charCodeAt(i);
|
|
14
|
+
}
|
|
15
|
+
return data;
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=collabUtils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"collabUtils.js","sourceRoot":"","sources":["../../../src/collab/client/collabUtils.ts"],"names":[],"mappings":"AAAA,0DAA0D;AAE1D,MAAM,UAAU,YAAY,CAAC,IAAgB;IAC3C,IAAI,MAAM,GAAG,EAAE,CAAC;IAChB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACrC,MAAM,IAAI,MAAM,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC,CAAE,CAAC,CAAC;IAC1C,CAAC;IACD,OAAO,IAAI,CAAC,MAAM,CAAC,CAAC;AACtB,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,MAAc;IACzC,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC;IAC5B,MAAM,IAAI,GAAG,IAAI,UAAU,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IAC3C,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACvC,IAAI,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;IACjC,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { CollabClient, type CollabTransport } from './CollabProvider.js';
|
|
2
|
+
export { useCollab, type UseCollabOptions, type UseCollabResult } from './useCollab.js';
|
|
3
|
+
export { base64Encode, base64Decode } from './collabUtils.js';
|
|
4
|
+
export type { CollabJoinInput, CollabSyncOutput, CollabUpdateMessage, CollabAwarenessMessage, CollabLeaveInput, CollabPeersMessage, CollabDocState, } from '../shared/CollabTypes.js';
|
|
5
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/collab/client/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,KAAK,eAAe,EAAE,MAAM,qBAAqB,CAAC;AACzE,OAAO,EAAE,SAAS,EAAE,KAAK,gBAAgB,EAAE,KAAK,eAAe,EAAE,MAAM,gBAAgB,CAAC;AACxF,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAC9D,YAAY,EACV,eAAe,EACf,gBAAgB,EAChB,mBAAmB,EACnB,sBAAsB,EACtB,gBAAgB,EAChB,kBAAkB,EAClB,cAAc,GACf,MAAM,0BAA0B,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/collab/client/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAwB,MAAM,qBAAqB,CAAC;AACzE,OAAO,EAAE,SAAS,EAA+C,MAAM,gBAAgB,CAAC;AACxF,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { CollabClient, type CollabTransport } from './CollabProvider.js';
|
|
2
|
+
export interface UseCollabOptions {
|
|
3
|
+
docId: string;
|
|
4
|
+
enabled: boolean;
|
|
5
|
+
transport: CollabTransport | null;
|
|
6
|
+
}
|
|
7
|
+
export interface UseCollabResult {
|
|
8
|
+
provider: CollabClient | null;
|
|
9
|
+
ydoc: CollabClient['ydoc'] | null;
|
|
10
|
+
peerIds: string[];
|
|
11
|
+
connected: boolean;
|
|
12
|
+
}
|
|
13
|
+
export declare function useCollab(options: UseCollabOptions): UseCollabResult;
|
|
14
|
+
//# sourceMappingURL=useCollab.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useCollab.d.ts","sourceRoot":"","sources":["../../../src/collab/client/useCollab.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,KAAK,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAEzE,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,OAAO,CAAC;IACjB,SAAS,EAAE,eAAe,GAAG,IAAI,CAAC;CACnC;AAED,MAAM,WAAW,eAAe;IAC9B,QAAQ,EAAE,YAAY,GAAG,IAAI,CAAC;IAC9B,IAAI,EAAE,YAAY,CAAC,MAAM,CAAC,GAAG,IAAI,CAAC;IAClC,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,SAAS,EAAE,OAAO,CAAC;CACpB;AAED,wBAAgB,SAAS,CAAC,OAAO,EAAE,gBAAgB,GAAG,eAAe,CAuCpE"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { useEffect, useRef, useState } from 'react';
|
|
2
|
+
import { CollabClient } from './CollabProvider.js';
|
|
3
|
+
export function useCollab(options) {
|
|
4
|
+
const { docId, enabled, transport } = options;
|
|
5
|
+
const [provider, setProvider] = useState(null);
|
|
6
|
+
const [peerIds, setPeerIds] = useState([]);
|
|
7
|
+
const [connected, setConnected] = useState(false);
|
|
8
|
+
const providerRef = useRef(null);
|
|
9
|
+
useEffect(() => {
|
|
10
|
+
if (!enabled || !docId)
|
|
11
|
+
return;
|
|
12
|
+
const client = new CollabClient(docId, transport);
|
|
13
|
+
providerRef.current = client;
|
|
14
|
+
client.setPeersCallback((newPeerIds) => {
|
|
15
|
+
setPeerIds(newPeerIds);
|
|
16
|
+
});
|
|
17
|
+
void client.connect().then(() => {
|
|
18
|
+
if (client.isConnected()) {
|
|
19
|
+
setProvider(client);
|
|
20
|
+
setConnected(true);
|
|
21
|
+
}
|
|
22
|
+
});
|
|
23
|
+
return () => {
|
|
24
|
+
client.destroy();
|
|
25
|
+
providerRef.current = null;
|
|
26
|
+
setProvider(null);
|
|
27
|
+
setPeerIds([]);
|
|
28
|
+
setConnected(false);
|
|
29
|
+
};
|
|
30
|
+
}, [docId, enabled, transport]);
|
|
31
|
+
return {
|
|
32
|
+
provider,
|
|
33
|
+
ydoc: provider?.ydoc ?? null,
|
|
34
|
+
peerIds,
|
|
35
|
+
connected,
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
//# sourceMappingURL=useCollab.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useCollab.js","sourceRoot":"","sources":["../../../src/collab/client/useCollab.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AACpD,OAAO,EAAE,YAAY,EAAwB,MAAM,qBAAqB,CAAC;AAezE,MAAM,UAAU,SAAS,CAAC,OAAyB;IACjD,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,SAAS,EAAE,GAAG,OAAO,CAAC;IAC9C,MAAM,CAAC,QAAQ,EAAE,WAAW,CAAC,GAAG,QAAQ,CAAsB,IAAI,CAAC,CAAC;IACpE,MAAM,CAAC,OAAO,EAAE,UAAU,CAAC,GAAG,QAAQ,CAAW,EAAE,CAAC,CAAC;IACrD,MAAM,CAAC,SAAS,EAAE,YAAY,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC;IAClD,MAAM,WAAW,GAAG,MAAM,CAAsB,IAAI,CAAC,CAAC;IAEtD,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,CAAC,OAAO,IAAI,CAAC,KAAK;YAAE,OAAO;QAE/B,MAAM,MAAM,GAAG,IAAI,YAAY,CAAC,KAAK,EAAE,SAAS,CAAC,CAAC;QAClD,WAAW,CAAC,OAAO,GAAG,MAAM,CAAC;QAE7B,MAAM,CAAC,gBAAgB,CAAC,CAAC,UAAU,EAAE,EAAE;YACrC,UAAU,CAAC,UAAU,CAAC,CAAC;QACzB,CAAC,CAAC,CAAC;QAEH,KAAK,MAAM,CAAC,OAAO,EAAE,CAAC,IAAI,CAAC,GAAG,EAAE;YAC9B,IAAI,MAAM,CAAC,WAAW,EAAE,EAAE,CAAC;gBACzB,WAAW,CAAC,MAAM,CAAC,CAAC;gBACpB,YAAY,CAAC,IAAI,CAAC,CAAC;YACrB,CAAC;QACH,CAAC,CAAC,CAAC;QAEH,OAAO,GAAG,EAAE;YACV,MAAM,CAAC,OAAO,EAAE,CAAC;YACjB,WAAW,CAAC,OAAO,GAAG,IAAI,CAAC;YAC3B,WAAW,CAAC,IAAI,CAAC,CAAC;YAClB,UAAU,CAAC,EAAE,CAAC,CAAC;YACf,YAAY,CAAC,KAAK,CAAC,CAAC;QACtB,CAAC,CAAC;IACJ,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,EAAE,SAAS,CAAC,CAAC,CAAC;IAEhC,OAAO;QACL,QAAQ;QACR,IAAI,EAAE,QAAQ,EAAE,IAAI,IAAI,IAAI;QAC5B,OAAO;QACP,SAAS;KACV,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import type WebSocket from 'ws';
|
|
2
|
+
import * as Y from 'yjs';
|
|
3
|
+
import type { CollabDocState } from '../shared/CollabTypes.js';
|
|
4
|
+
export interface CollabConfig {
|
|
5
|
+
/**
|
|
6
|
+
* Load the current Yjs state for a document.
|
|
7
|
+
* Return the base64-encoded yjsState string, or null if no state exists.
|
|
8
|
+
*/
|
|
9
|
+
loadState(docId: string): Promise<string | null>;
|
|
10
|
+
/**
|
|
11
|
+
* Persist the Yjs state for a document.
|
|
12
|
+
* Called with the full base64-encoded state and a serialized content string
|
|
13
|
+
* (if a serialize callback is provided).
|
|
14
|
+
*/
|
|
15
|
+
saveState(docId: string, collabState: CollabDocState, serialized: string | null): Promise<void>;
|
|
16
|
+
/**
|
|
17
|
+
* Optional: Initialize a Yjs doc from existing content when no collab state
|
|
18
|
+
* exists yet. Called with an empty Y.Doc — the implementation should populate it.
|
|
19
|
+
*/
|
|
20
|
+
initializeDoc?(ydoc: Y.Doc, docId: string): Promise<void>;
|
|
21
|
+
/**
|
|
22
|
+
* Optional: Serialize a Yjs doc to a string for persistence alongside the
|
|
23
|
+
* binary Yjs state. Useful for maintaining a searchable/readable representation.
|
|
24
|
+
*/
|
|
25
|
+
serialize?(ydoc: Y.Doc): string;
|
|
26
|
+
/**
|
|
27
|
+
* Optional: Called when an error occurs. Defaults to console.error.
|
|
28
|
+
*/
|
|
29
|
+
onError?(message: string, error: unknown, context?: Record<string, unknown>): void;
|
|
30
|
+
/** Debounce interval for persistence in ms. Default: 3000 */
|
|
31
|
+
persistDebounceMs?: number;
|
|
32
|
+
/** Max wait before forced persistence in ms. Default: 30000 */
|
|
33
|
+
persistMaxWaitMs?: number;
|
|
34
|
+
/** Idle timeout before unloading a document in ms. Default: 300000 (5 min) */
|
|
35
|
+
idleCleanupMs?: number;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Manages collaborative editing for multiple documents using Yjs CRDTs.
|
|
39
|
+
* Cross-server sync via NATS, debounced persistence, idle cleanup.
|
|
40
|
+
*/
|
|
41
|
+
export declare class CollabServer {
|
|
42
|
+
private readonly config;
|
|
43
|
+
private readonly activeDocuments;
|
|
44
|
+
private readonly socketDocs;
|
|
45
|
+
constructor(config: CollabConfig);
|
|
46
|
+
private logError;
|
|
47
|
+
private loadDocument;
|
|
48
|
+
private unloadDocument;
|
|
49
|
+
private schedulePersist;
|
|
50
|
+
private persistDocument;
|
|
51
|
+
private broadcastToLocalPeers;
|
|
52
|
+
private broadcastPeerList;
|
|
53
|
+
join(ws: WebSocket, docId: string, userId: string, stateVector: string, messageId: string | undefined): Promise<void>;
|
|
54
|
+
handleUpdate(docId: string, userId: string, update: string): void;
|
|
55
|
+
handleAwareness(docId: string, userId: string, states: string): void;
|
|
56
|
+
leave(ws: WebSocket, docId: string, userId: string): void;
|
|
57
|
+
cleanupSocket(ws: WebSocket, userId: string): void;
|
|
58
|
+
/**
|
|
59
|
+
* Returns a socket message handler suitable for use with
|
|
60
|
+
* `configurator.addSocketMessageHandler()`.
|
|
61
|
+
*/
|
|
62
|
+
createSocketHandler(): (ws: WebSocket, userId: string, msg: unknown) => boolean;
|
|
63
|
+
}
|
|
64
|
+
//# sourceMappingURL=CollabServer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CollabServer.d.ts","sourceRoot":"","sources":["../../../src/collab/server/CollabServer.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,SAAS,MAAM,IAAI,CAAC;AAChC,OAAO,KAAK,CAAC,MAAM,KAAK,CAAC;AAIzB,OAAO,KAAK,EAEV,cAAc,EAIf,MAAM,0BAA0B,CAAC;AAIlC,MAAM,WAAW,YAAY;IAC3B;;;OAGG;IACH,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IAEjD;;;;OAIG;IACH,SAAS,CACP,KAAK,EAAE,MAAM,EACb,WAAW,EAAE,cAAc,EAC3B,UAAU,EAAE,MAAM,GAAG,IAAI,GACxB,OAAO,CAAC,IAAI,CAAC,CAAC;IAEjB;;;OAGG;IACH,aAAa,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,GAAG,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAE1D;;;OAGG;IACH,SAAS,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,GAAG,GAAG,MAAM,CAAC;IAEhC;;OAEG;IACH,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IAEnF,6DAA6D;IAC7D,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,+DAA+D;IAC/D,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,8EAA8E;IAC9E,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAwDD;;;GAGG;AACH,qBAAa,YAAY;IACvB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAGR;IAEf,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAqC;IACrE,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAqC;gBAEpD,MAAM,EAAE,YAAY;IAShC,OAAO,CAAC,QAAQ;YAUF,YAAY;YA6GZ,cAAc;IAoB5B,OAAO,CAAC,eAAe;YAeT,eAAe;IA0C7B,OAAO,CAAC,qBAAqB;IAc7B,OAAO,CAAC,iBAAiB;IAQnB,IAAI,CACR,EAAE,EAAE,SAAS,EACb,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM,EACd,WAAW,EAAE,MAAM,EACnB,SAAS,EAAE,MAAM,GAAG,SAAS,GAC5B,OAAO,CAAC,IAAI,CAAC;IA2ChB,YAAY,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI;IAyBjE,eAAe,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI;IAgBpE,KAAK,CAAC,EAAE,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI;IAoCzD,aAAa,CAAC,EAAE,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI;IAUlD;;;OAGG;IACH,mBAAmB,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,OAAO,KAAK,OAAO;CAoChF"}
|