y-partyserver 2.1.0 → 2.1.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/server/index.d.ts +13 -41
- package/dist/server/index.js +168 -164
- package/dist/server/index.js.map +1 -1
- package/package.json +2 -2
package/dist/server/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Connection,
|
|
1
|
+
import { Connection, Server, WSMessage } from "partyserver";
|
|
2
2
|
import * as awarenessProtocol from "y-protocols/awareness";
|
|
3
3
|
import { Doc } from "yjs";
|
|
4
4
|
|
|
@@ -19,58 +19,30 @@ interface CallbackOptions {
|
|
|
19
19
|
debounceMaxWait?: number;
|
|
20
20
|
timeout?: number;
|
|
21
21
|
}
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
> extends Server<Env> {
|
|
25
|
-
#private;
|
|
26
|
-
static callbackOptions: CallbackOptions;
|
|
22
|
+
type ServerClass = new (...args: any[]) => Server;
|
|
23
|
+
interface YjsInstance {
|
|
27
24
|
readonly document: WSSharedDoc;
|
|
28
25
|
onLoad(): Promise<Doc | void>;
|
|
29
26
|
onSave(): Promise<void>;
|
|
30
|
-
/**
|
|
31
|
-
* Replaces the document with a different state using Yjs UndoManager key remapping.
|
|
32
|
-
*
|
|
33
|
-
* @param snapshotUpdate - The snapshot update to replace the document with.
|
|
34
|
-
* @param getMetadata (optional) - A function that returns the type of the root for a given key.
|
|
35
|
-
*/
|
|
36
27
|
unstable_replaceDocument(
|
|
37
28
|
snapshotUpdate: Uint8Array,
|
|
38
29
|
getMetadata?: (key: string) => YjsRootType
|
|
39
30
|
): void;
|
|
40
|
-
onStart(): Promise<void>;
|
|
41
31
|
isReadOnly(connection: Connection): boolean;
|
|
42
|
-
/**
|
|
43
|
-
* Handle custom string messages from the client.
|
|
44
|
-
* Override this method to implement custom message handling.
|
|
45
|
-
* @param connection - The connection that sent the message
|
|
46
|
-
* @param message - The custom message string (without the __YPS: prefix)
|
|
47
|
-
*/
|
|
48
32
|
onCustomMessage(connection: Connection, message: string): void;
|
|
49
|
-
/**
|
|
50
|
-
* Send a custom string message to a specific connection.
|
|
51
|
-
* @param connection - The connection to send the message to
|
|
52
|
-
* @param message - The custom message string to send
|
|
53
|
-
*/
|
|
54
33
|
sendCustomMessage(connection: Connection, message: string): void;
|
|
55
|
-
/**
|
|
56
|
-
* Broadcast a custom string message to all connected clients.
|
|
57
|
-
* @param message - The custom message string to broadcast
|
|
58
|
-
* @param excludeConnection - Optional connection to exclude from the broadcast
|
|
59
|
-
*/
|
|
60
34
|
broadcastCustomMessage(message: string, excludeConnection?: Connection): void;
|
|
61
35
|
handleMessage(connection: Connection, message: WSMessage): void;
|
|
62
|
-
onMessage(conn: Connection, message: WSMessage): void;
|
|
63
|
-
onClose(
|
|
64
|
-
connection: Connection<unknown>,
|
|
65
|
-
_code: number,
|
|
66
|
-
_reason: string,
|
|
67
|
-
_wasClean: boolean
|
|
68
|
-
): void | Promise<void>;
|
|
69
|
-
onConnect(
|
|
70
|
-
conn: Connection<unknown>,
|
|
71
|
-
_ctx: ConnectionContext
|
|
72
|
-
): void | Promise<void>;
|
|
73
36
|
}
|
|
37
|
+
interface YjsStatic {
|
|
38
|
+
callbackOptions: CallbackOptions;
|
|
39
|
+
}
|
|
40
|
+
declare function withYjs<TBase extends ServerClass>(
|
|
41
|
+
Base: TBase
|
|
42
|
+
): TBase & YjsStatic & (new (...args: any[]) => YjsInstance);
|
|
43
|
+
declare const YServer: typeof Server &
|
|
44
|
+
YjsStatic &
|
|
45
|
+
(new (...args: any[]) => YjsInstance);
|
|
74
46
|
//#endregion
|
|
75
|
-
export { CallbackOptions, YServer };
|
|
47
|
+
export { CallbackOptions, YServer, YjsInstance, YjsStatic, withYjs };
|
|
76
48
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/server/index.js
CHANGED
|
@@ -69,187 +69,191 @@ function send(conn, m) {
|
|
|
69
69
|
conn.send(m);
|
|
70
70
|
} catch {}
|
|
71
71
|
}
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
}
|
|
109
|
-
async onStart() {
|
|
110
|
-
const src = await this.onLoad();
|
|
111
|
-
if (src != null) {
|
|
112
|
-
const state = encodeStateAsUpdate(src);
|
|
113
|
-
applyUpdate(this.document, state);
|
|
72
|
+
function withYjs(Base) {
|
|
73
|
+
class YjsMixin extends Base {
|
|
74
|
+
static callbackOptions = {};
|
|
75
|
+
document = new WSSharedDoc();
|
|
76
|
+
async onLoad() {}
|
|
77
|
+
async onSave() {}
|
|
78
|
+
/**
|
|
79
|
+
* Replaces the document with a different state using Yjs UndoManager key remapping.
|
|
80
|
+
*
|
|
81
|
+
* @param snapshotUpdate - The snapshot update to replace the document with.
|
|
82
|
+
* @param getMetadata (optional) - A function that returns the type of the root for a given key.
|
|
83
|
+
*/
|
|
84
|
+
unstable_replaceDocument(snapshotUpdate, getMetadata = () => "Map") {
|
|
85
|
+
try {
|
|
86
|
+
const doc = this.document;
|
|
87
|
+
const snapshotDoc = new Doc();
|
|
88
|
+
applyUpdate(snapshotDoc, snapshotUpdate, snapshotOrigin);
|
|
89
|
+
const currentStateVector = encodeStateVector(doc);
|
|
90
|
+
const changesSinceSnapshotUpdate = encodeStateAsUpdate(doc, encodeStateVector(snapshotDoc));
|
|
91
|
+
const undoManager = new UndoManager([...snapshotDoc.share.keys()].map((key) => {
|
|
92
|
+
const type = getMetadata(key);
|
|
93
|
+
if (type === "Text") return snapshotDoc.getText(key);
|
|
94
|
+
else if (type === "Map") return snapshotDoc.getMap(key);
|
|
95
|
+
else if (type === "Array") return snapshotDoc.getArray(key);
|
|
96
|
+
else if (type === "XmlText") return snapshotDoc.get(key, XmlText);
|
|
97
|
+
else if (type === "XmlElement") return snapshotDoc.get(key, XmlElement);
|
|
98
|
+
else if (type === "XmlFragment") return snapshotDoc.get(key, XmlFragment);
|
|
99
|
+
throw new Error(`Unknown root type: ${type} for key: ${key}`);
|
|
100
|
+
}), { trackedOrigins: new Set([snapshotOrigin]) });
|
|
101
|
+
applyUpdate(snapshotDoc, changesSinceSnapshotUpdate, snapshotOrigin);
|
|
102
|
+
undoManager.undo();
|
|
103
|
+
const documentChangesSinceSnapshotUpdate = encodeStateAsUpdate(snapshotDoc, currentStateVector);
|
|
104
|
+
applyUpdate(this.document, documentChangesSinceSnapshotUpdate);
|
|
105
|
+
} catch (error) {
|
|
106
|
+
throw new Error(`Failed to replace document: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
107
|
+
}
|
|
114
108
|
}
|
|
115
|
-
|
|
116
|
-
const
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
for (const conn of this.getConnections()) send(conn, message);
|
|
121
|
-
});
|
|
122
|
-
this.document.awareness.on("update", ({ added, updated, removed }, conn) => {
|
|
123
|
-
if (conn !== null) try {
|
|
124
|
-
const currentIds = new Set(getAwarenessIds(conn));
|
|
125
|
-
for (const clientID of added) currentIds.add(clientID);
|
|
126
|
-
for (const clientID of removed) currentIds.delete(clientID);
|
|
127
|
-
setAwarenessIds(conn, [...currentIds]);
|
|
128
|
-
} catch (_e) {}
|
|
129
|
-
else {
|
|
130
|
-
const changedClients = added.concat(updated, removed);
|
|
131
|
-
const encoder = encoding.createEncoder();
|
|
132
|
-
encoding.writeVarUint(encoder, messageAwareness);
|
|
133
|
-
encoding.writeVarUint8Array(encoder, awarenessProtocol.encodeAwarenessUpdate(this.document.awareness, changedClients));
|
|
134
|
-
const buff = encoding.toUint8Array(encoder);
|
|
135
|
-
for (const c of this.getConnections()) send(c, buff);
|
|
109
|
+
async onStart() {
|
|
110
|
+
const src = await this.onLoad();
|
|
111
|
+
if (src != null) {
|
|
112
|
+
const state = encodeStateAsUpdate(src);
|
|
113
|
+
applyUpdate(this.document, state);
|
|
136
114
|
}
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
115
|
+
this.document.on("update", (update) => {
|
|
116
|
+
const encoder = encoding.createEncoder();
|
|
117
|
+
encoding.writeVarUint(encoder, messageSync);
|
|
118
|
+
syncProtocol.writeUpdate(encoder, update);
|
|
119
|
+
const message = encoding.toUint8Array(encoder);
|
|
120
|
+
for (const conn of this.getConnections()) send(conn, message);
|
|
121
|
+
});
|
|
122
|
+
this.document.awareness.on("update", ({ added, updated, removed }, conn) => {
|
|
123
|
+
if (conn !== null) try {
|
|
124
|
+
const currentIds = new Set(getAwarenessIds(conn));
|
|
125
|
+
for (const clientID of added) currentIds.add(clientID);
|
|
126
|
+
for (const clientID of removed) currentIds.delete(clientID);
|
|
127
|
+
setAwarenessIds(conn, [...currentIds]);
|
|
128
|
+
} catch (_e) {}
|
|
129
|
+
else {
|
|
130
|
+
const changedClients = added.concat(updated, removed);
|
|
131
|
+
const encoder = encoding.createEncoder();
|
|
132
|
+
encoding.writeVarUint(encoder, messageAwareness);
|
|
133
|
+
encoding.writeVarUint8Array(encoder, awarenessProtocol.encodeAwarenessUpdate(this.document.awareness, changedClients));
|
|
134
|
+
const buff = encoding.toUint8Array(encoder);
|
|
135
|
+
for (const c of this.getConnections()) send(c, buff);
|
|
136
|
+
}
|
|
137
|
+
});
|
|
138
|
+
const ctor = this.constructor;
|
|
139
|
+
this.document.on("update", debounce((_update, _origin, _doc) => {
|
|
140
|
+
try {
|
|
141
|
+
this.onSave().catch((err) => {
|
|
142
|
+
console.error("failed to persist:", err);
|
|
143
|
+
});
|
|
144
|
+
} catch (err) {
|
|
141
145
|
console.error("failed to persist:", err);
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
syncProtocol.writeSyncStep1(syncEncoder, this.document);
|
|
150
|
-
const syncMessage = encoding.toUint8Array(syncEncoder);
|
|
151
|
-
for (const conn of this.getConnections()) send(conn, syncMessage);
|
|
152
|
-
}
|
|
153
|
-
isReadOnly(connection) {
|
|
154
|
-
return false;
|
|
155
|
-
}
|
|
156
|
-
/**
|
|
157
|
-
* Handle custom string messages from the client.
|
|
158
|
-
* Override this method to implement custom message handling.
|
|
159
|
-
* @param connection - The connection that sent the message
|
|
160
|
-
* @param message - The custom message string (without the __YPS: prefix)
|
|
161
|
-
*/
|
|
162
|
-
onCustomMessage(connection, message) {
|
|
163
|
-
console.warn(`Received custom message but onCustomMessage is not implemented in ${this.#ParentClass.name}:`, message);
|
|
164
|
-
}
|
|
165
|
-
/**
|
|
166
|
-
* Send a custom string message to a specific connection.
|
|
167
|
-
* @param connection - The connection to send the message to
|
|
168
|
-
* @param message - The custom message string to send
|
|
169
|
-
*/
|
|
170
|
-
sendCustomMessage(connection, message) {
|
|
171
|
-
if (connection.readyState !== void 0 && connection.readyState !== wsReadyStateConnecting && connection.readyState !== wsReadyStateOpen) return;
|
|
172
|
-
try {
|
|
173
|
-
connection.send(`__YPS:${message}`);
|
|
174
|
-
} catch (e) {
|
|
175
|
-
console.warn("Failed to send custom message", e);
|
|
146
|
+
}
|
|
147
|
+
}, ctor.callbackOptions.debounceWait || CALLBACK_DEFAULTS.debounceWait, { maxWait: ctor.callbackOptions.debounceMaxWait || CALLBACK_DEFAULTS.debounceMaxWait }));
|
|
148
|
+
const syncEncoder = encoding.createEncoder();
|
|
149
|
+
encoding.writeVarUint(syncEncoder, messageSync);
|
|
150
|
+
syncProtocol.writeSyncStep1(syncEncoder, this.document);
|
|
151
|
+
const syncMessage = encoding.toUint8Array(syncEncoder);
|
|
152
|
+
for (const conn of this.getConnections()) send(conn, syncMessage);
|
|
176
153
|
}
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
154
|
+
isReadOnly(connection) {
|
|
155
|
+
return false;
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* Handle custom string messages from the client.
|
|
159
|
+
* Override this method to implement custom message handling.
|
|
160
|
+
* @param connection - The connection that sent the message
|
|
161
|
+
* @param message - The custom message string (without the __YPS: prefix)
|
|
162
|
+
*/
|
|
163
|
+
onCustomMessage(connection, message) {
|
|
164
|
+
console.warn(`Received custom message but onCustomMessage is not implemented in ${this.constructor.name}:`, message);
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* Send a custom string message to a specific connection.
|
|
168
|
+
* @param connection - The connection to send the message to
|
|
169
|
+
* @param message - The custom message string to send
|
|
170
|
+
*/
|
|
171
|
+
sendCustomMessage(connection, message) {
|
|
172
|
+
if (connection.readyState !== void 0 && connection.readyState !== wsReadyStateConnecting && connection.readyState !== wsReadyStateOpen) return;
|
|
188
173
|
try {
|
|
189
|
-
|
|
174
|
+
connection.send(`__YPS:${message}`);
|
|
190
175
|
} catch (e) {
|
|
191
|
-
console.warn("Failed to
|
|
176
|
+
console.warn("Failed to send custom message", e);
|
|
192
177
|
}
|
|
193
178
|
}
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
179
|
+
/**
|
|
180
|
+
* Broadcast a custom string message to all connected clients.
|
|
181
|
+
* @param message - The custom message string to broadcast
|
|
182
|
+
* @param excludeConnection - Optional connection to exclude from the broadcast
|
|
183
|
+
*/
|
|
184
|
+
broadcastCustomMessage(message, excludeConnection) {
|
|
185
|
+
const formattedMessage = `__YPS:${message}`;
|
|
186
|
+
for (const conn of this.getConnections()) {
|
|
187
|
+
if (excludeConnection && conn === excludeConnection) continue;
|
|
188
|
+
if (conn.readyState !== void 0 && conn.readyState !== wsReadyStateConnecting && conn.readyState !== wsReadyStateOpen) continue;
|
|
189
|
+
try {
|
|
190
|
+
conn.send(formattedMessage);
|
|
191
|
+
} catch (e) {
|
|
192
|
+
console.warn("Failed to broadcast custom message", e);
|
|
193
|
+
}
|
|
201
194
|
}
|
|
202
|
-
console.warn(`Received non-prefixed string message. Custom messages should be sent using sendMessage() on the provider.`);
|
|
203
|
-
return;
|
|
204
195
|
}
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
196
|
+
handleMessage(connection, message) {
|
|
197
|
+
if (typeof message === "string") {
|
|
198
|
+
if (message.startsWith("__YPS:")) {
|
|
199
|
+
const customMessage = message.slice(6);
|
|
200
|
+
this.onCustomMessage(connection, customMessage);
|
|
201
|
+
return;
|
|
202
|
+
}
|
|
203
|
+
console.warn(`Received non-prefixed string message. Custom messages should be sent using sendMessage() on the provider.`);
|
|
204
|
+
return;
|
|
205
|
+
}
|
|
206
|
+
try {
|
|
207
|
+
const encoder = encoding.createEncoder();
|
|
208
|
+
const uint8Array = message instanceof Uint8Array ? message : message instanceof ArrayBuffer ? new Uint8Array(message) : new Uint8Array(message.buffer, message.byteOffset, message.byteLength);
|
|
209
|
+
const decoder = decoding.createDecoder(uint8Array);
|
|
210
|
+
switch (decoding.readVarUint(decoder)) {
|
|
211
|
+
case messageSync:
|
|
212
|
+
encoding.writeVarUint(encoder, messageSync);
|
|
213
|
+
readSyncMessage(decoder, encoder, this.document, connection, this.isReadOnly(connection));
|
|
214
|
+
if (encoding.length(encoder) > 1) send(connection, encoding.toUint8Array(encoder));
|
|
215
|
+
break;
|
|
216
|
+
case messageAwareness: {
|
|
217
|
+
const awarenessData = decoding.readVarUint8Array(decoder);
|
|
218
|
+
awarenessProtocol.applyAwarenessUpdate(this.document.awareness, awarenessData, connection);
|
|
219
|
+
const awarenessEncoder = encoding.createEncoder();
|
|
220
|
+
encoding.writeVarUint(awarenessEncoder, messageAwareness);
|
|
221
|
+
encoding.writeVarUint8Array(awarenessEncoder, awarenessData);
|
|
222
|
+
const awarenessBuff = encoding.toUint8Array(awarenessEncoder);
|
|
223
|
+
for (const c of this.getConnections()) send(c, awarenessBuff);
|
|
224
|
+
break;
|
|
225
|
+
}
|
|
224
226
|
}
|
|
227
|
+
} catch (err) {
|
|
228
|
+
console.error(err);
|
|
229
|
+
this.document.emit("error", [err]);
|
|
225
230
|
}
|
|
226
|
-
} catch (err) {
|
|
227
|
-
console.error(err);
|
|
228
|
-
this.document.emit("error", [err]);
|
|
229
231
|
}
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
onConnect(conn, _ctx) {
|
|
239
|
-
const encoder = encoding.createEncoder();
|
|
240
|
-
encoding.writeVarUint(encoder, messageSync);
|
|
241
|
-
syncProtocol.writeSyncStep1(encoder, this.document);
|
|
242
|
-
send(conn, encoding.toUint8Array(encoder));
|
|
243
|
-
const awarenessStates = this.document.awareness.getStates();
|
|
244
|
-
if (awarenessStates.size > 0) {
|
|
232
|
+
onMessage(conn, message) {
|
|
233
|
+
this.handleMessage(conn, message);
|
|
234
|
+
}
|
|
235
|
+
onClose(connection, _code, _reason, _wasClean) {
|
|
236
|
+
const controlledIds = getAwarenessIds(connection);
|
|
237
|
+
if (controlledIds.length > 0) awarenessProtocol.removeAwarenessStates(this.document.awareness, controlledIds, null);
|
|
238
|
+
}
|
|
239
|
+
onConnect(conn, _ctx) {
|
|
245
240
|
const encoder = encoding.createEncoder();
|
|
246
|
-
encoding.writeVarUint(encoder,
|
|
247
|
-
|
|
241
|
+
encoding.writeVarUint(encoder, messageSync);
|
|
242
|
+
syncProtocol.writeSyncStep1(encoder, this.document);
|
|
248
243
|
send(conn, encoding.toUint8Array(encoder));
|
|
244
|
+
const awarenessStates = this.document.awareness.getStates();
|
|
245
|
+
if (awarenessStates.size > 0) {
|
|
246
|
+
const encoder = encoding.createEncoder();
|
|
247
|
+
encoding.writeVarUint(encoder, messageAwareness);
|
|
248
|
+
encoding.writeVarUint8Array(encoder, awarenessProtocol.encodeAwarenessUpdate(this.document.awareness, Array.from(awarenessStates.keys())));
|
|
249
|
+
send(conn, encoding.toUint8Array(encoder));
|
|
250
|
+
}
|
|
249
251
|
}
|
|
250
252
|
}
|
|
251
|
-
|
|
253
|
+
return YjsMixin;
|
|
254
|
+
}
|
|
255
|
+
const YServer = withYjs(Server);
|
|
252
256
|
|
|
253
257
|
//#endregion
|
|
254
|
-
export { YServer };
|
|
258
|
+
export { YServer, withYjs };
|
|
255
259
|
//# sourceMappingURL=index.js.map
|
package/dist/server/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":["YDoc","#ParentClass"],"sources":["../../src/server/index.ts"],"sourcesContent":["import * as decoding from \"lib0/decoding\";\nimport * as encoding from \"lib0/encoding\";\nimport debounce from \"lodash.debounce\";\nimport type { Connection, ConnectionContext, WSMessage } from \"partyserver\";\nimport { Server } from \"partyserver\";\nimport * as awarenessProtocol from \"y-protocols/awareness\";\nimport * as syncProtocol from \"y-protocols/sync\";\nimport {\n applyUpdate,\n Doc as YDoc,\n encodeStateAsUpdate,\n encodeStateVector,\n UndoManager,\n XmlText,\n XmlElement,\n XmlFragment\n} from \"yjs\";\n\nconst snapshotOrigin = Symbol(\"snapshot-origin\");\ntype YjsRootType =\n | \"Text\"\n | \"Map\"\n | \"Array\"\n | \"XmlText\"\n | \"XmlElement\"\n | \"XmlFragment\";\n\nconst wsReadyStateConnecting = 0;\nconst wsReadyStateOpen = 1;\n// oxlint-disable-next-line no-unused-vars\nconst wsReadyStateClosing = 2;\n// oxlint-disable-next-line no-unused-vars\nconst wsReadyStateClosed = 3;\n\nconst messageSync = 0;\nconst messageAwareness = 1;\n// oxlint-disable-next-line no-unused-vars\nconst messageAuth = 2;\n\n/**\n * Internal key used in connection.setState() to track which awareness\n * client IDs are controlled by each connection. This survives hibernation\n * because connection state is persisted to WebSocket attachments.\n */\nconst AWARENESS_IDS_KEY = \"__ypsAwarenessIds\";\n\ntype YServerConnectionState = {\n [AWARENESS_IDS_KEY]?: number[];\n [key: string]: unknown;\n};\n\nfunction getAwarenessIds(conn: Connection): number[] {\n try {\n const state = conn.state as YServerConnectionState | null;\n return state?.[AWARENESS_IDS_KEY] ?? [];\n } catch {\n return [];\n }\n}\n\nfunction setAwarenessIds(conn: Connection, ids: number[]): void {\n try {\n conn.setState((prev: YServerConnectionState | null) => ({\n ...prev,\n [AWARENESS_IDS_KEY]: ids\n }));\n } catch {\n // ignore — may fail if connection is already closed\n }\n}\n\nclass WSSharedDoc extends YDoc {\n awareness: awarenessProtocol.Awareness;\n\n constructor() {\n super({ gc: true });\n this.awareness = new awarenessProtocol.Awareness(this);\n this.awareness.setLocalState(null);\n\n // Disable the awareness protocol's built-in check interval.\n // It renews the local clock every 15s and removes peers after 30s,\n // but we handle peer cleanup via onClose instead. Clearing it here\n // prevents it from defeating Durable Object hibernation.\n clearInterval(\n (\n this.awareness as unknown as {\n _checkInterval: ReturnType<typeof setInterval>;\n }\n )._checkInterval\n );\n }\n}\n\nconst CALLBACK_DEFAULTS = {\n debounceWait: 2000,\n debounceMaxWait: 10000,\n timeout: 5000\n};\n\nfunction readSyncMessage(\n decoder: decoding.Decoder,\n encoder: encoding.Encoder,\n doc: YDoc,\n transactionOrigin: Connection,\n readOnly = false\n) {\n const messageType = decoding.readVarUint(decoder);\n switch (messageType) {\n case syncProtocol.messageYjsSyncStep1:\n syncProtocol.readSyncStep1(decoder, encoder, doc);\n break;\n case syncProtocol.messageYjsSyncStep2:\n if (!readOnly)\n syncProtocol.readSyncStep2(decoder, doc, transactionOrigin);\n break;\n case syncProtocol.messageYjsUpdate:\n if (!readOnly) syncProtocol.readUpdate(decoder, doc, transactionOrigin);\n break;\n default:\n throw new Error(\"Unknown message type\");\n }\n return messageType;\n}\n\nfunction send(conn: Connection, m: Uint8Array): void {\n if (\n conn.readyState !== undefined &&\n conn.readyState !== wsReadyStateConnecting &&\n conn.readyState !== wsReadyStateOpen\n ) {\n return;\n }\n try {\n conn.send(m);\n } catch {\n // connection is broken, ignore\n }\n}\n\nexport interface CallbackOptions {\n debounceWait?: number;\n debounceMaxWait?: number;\n timeout?: number;\n}\n\nexport class YServer<\n Env extends Cloudflare.Env = Cloudflare.Env\n> extends Server<Env> {\n static callbackOptions: CallbackOptions = {};\n\n #ParentClass: typeof YServer = Object.getPrototypeOf(this).constructor;\n readonly document: WSSharedDoc = new WSSharedDoc();\n\n async onLoad(): Promise<YDoc | void> {\n // to be implemented by the user\n return;\n }\n\n async onSave(): Promise<void> {\n // to be implemented by the user\n }\n\n /**\n * Replaces the document with a different state using Yjs UndoManager key remapping.\n *\n * @param snapshotUpdate - The snapshot update to replace the document with.\n * @param getMetadata (optional) - A function that returns the type of the root for a given key.\n */\n unstable_replaceDocument(\n snapshotUpdate: Uint8Array,\n getMetadata: (key: string) => YjsRootType = () => \"Map\"\n ): void {\n try {\n const doc = this.document;\n const snapshotDoc = new YDoc();\n applyUpdate(snapshotDoc, snapshotUpdate, snapshotOrigin);\n\n const currentStateVector = encodeStateVector(doc);\n const snapshotStateVector = encodeStateVector(snapshotDoc);\n\n const changesSinceSnapshotUpdate = encodeStateAsUpdate(\n doc,\n snapshotStateVector\n );\n\n const undoManager = new UndoManager(\n [...snapshotDoc.share.keys()].map((key) => {\n const type = getMetadata(key);\n if (type === \"Text\") {\n return snapshotDoc.getText(key);\n } else if (type === \"Map\") {\n return snapshotDoc.getMap(key);\n } else if (type === \"Array\") {\n return snapshotDoc.getArray(key);\n } else if (type === \"XmlText\") {\n return snapshotDoc.get(key, XmlText);\n } else if (type === \"XmlElement\") {\n return snapshotDoc.get(key, XmlElement);\n } else if (type === \"XmlFragment\") {\n return snapshotDoc.get(key, XmlFragment);\n }\n throw new Error(`Unknown root type: ${type} for key: ${key}`);\n }),\n {\n trackedOrigins: new Set([snapshotOrigin])\n }\n );\n\n applyUpdate(snapshotDoc, changesSinceSnapshotUpdate, snapshotOrigin);\n undoManager.undo();\n\n const documentChangesSinceSnapshotUpdate = encodeStateAsUpdate(\n snapshotDoc,\n currentStateVector\n );\n\n applyUpdate(this.document, documentChangesSinceSnapshotUpdate);\n } catch (error) {\n throw new Error(\n `Failed to replace document: ${error instanceof Error ? error.message : \"Unknown error\"}`\n );\n }\n }\n\n async onStart(): Promise<void> {\n const src = await this.onLoad();\n if (src != null) {\n const state = encodeStateAsUpdate(src);\n applyUpdate(this.document, state);\n }\n\n // Broadcast doc updates to all connections.\n // Uses this.getConnections() which works for both hibernate and non-hibernate\n // modes and survives DO hibernation (unlike an in-memory Map).\n this.document.on(\"update\", (update: Uint8Array) => {\n const encoder = encoding.createEncoder();\n encoding.writeVarUint(encoder, messageSync);\n syncProtocol.writeUpdate(encoder, update);\n const message = encoding.toUint8Array(encoder);\n for (const conn of this.getConnections()) {\n send(conn, message);\n }\n });\n\n // Track which awareness clientIDs each connection controls.\n // Stored in connection.setState() so it survives hibernation.\n // When conn is null (internal changes like removeAwarenessStates on close),\n // broadcast the update to remaining connections.\n // When conn is non-null (client message), handleMessage broadcasts directly.\n this.document.awareness.on(\n \"update\",\n (\n {\n added,\n updated,\n removed\n }: {\n added: Array<number>;\n updated: Array<number>;\n removed: Array<number>;\n },\n conn: Connection | null\n ) => {\n if (conn !== null) {\n // Track which clientIDs this connection controls\n try {\n const currentIds = new Set(getAwarenessIds(conn));\n for (const clientID of added) currentIds.add(clientID);\n for (const clientID of removed) currentIds.delete(clientID);\n setAwarenessIds(conn, [...currentIds]);\n } catch (_e) {\n // ignore — best-effort tracking\n }\n } else {\n // Internal awareness change (e.g. removeAwarenessStates on close)\n // — broadcast to all remaining connections\n const changedClients = added.concat(updated, removed);\n const encoder = encoding.createEncoder();\n encoding.writeVarUint(encoder, messageAwareness);\n encoding.writeVarUint8Array(\n encoder,\n awarenessProtocol.encodeAwarenessUpdate(\n this.document.awareness,\n changedClients\n )\n );\n const buff = encoding.toUint8Array(encoder);\n for (const c of this.getConnections()) {\n send(c, buff);\n }\n }\n }\n );\n\n // Debounced persistence handler\n this.document.on(\n \"update\",\n debounce(\n (_update: Uint8Array, _origin: Connection, _doc: YDoc) => {\n try {\n this.onSave().catch((err) => {\n console.error(\"failed to persist:\", err);\n });\n } catch (err) {\n console.error(\"failed to persist:\", err);\n }\n },\n this.#ParentClass.callbackOptions.debounceWait ||\n CALLBACK_DEFAULTS.debounceWait,\n {\n maxWait:\n this.#ParentClass.callbackOptions.debounceMaxWait ||\n CALLBACK_DEFAULTS.debounceMaxWait\n }\n )\n );\n\n // After hibernation wake-up, the doc is empty but existing connections\n // survive. Re-sync by sending sync step 1 to all connections — they'll\n // respond with sync step 2 containing their full state.\n // On first start there are no connections, so this is a no-op.\n const syncEncoder = encoding.createEncoder();\n encoding.writeVarUint(syncEncoder, messageSync);\n syncProtocol.writeSyncStep1(syncEncoder, this.document);\n const syncMessage = encoding.toUint8Array(syncEncoder);\n for (const conn of this.getConnections()) {\n send(conn, syncMessage);\n }\n }\n\n // oxlint-disable-next-line no-unused-vars\n isReadOnly(connection: Connection): boolean {\n // to be implemented by the user\n return false;\n }\n\n /**\n * Handle custom string messages from the client.\n * Override this method to implement custom message handling.\n * @param connection - The connection that sent the message\n * @param message - The custom message string (without the __YPS: prefix)\n */\n // oxlint-disable-next-line no-unused-vars\n onCustomMessage(connection: Connection, message: string): void {\n // to be implemented by the user\n console.warn(\n `Received custom message but onCustomMessage is not implemented in ${this.#ParentClass.name}:`,\n message\n );\n }\n\n /**\n * Send a custom string message to a specific connection.\n * @param connection - The connection to send the message to\n * @param message - The custom message string to send\n */\n sendCustomMessage(connection: Connection, message: string): void {\n if (\n connection.readyState !== undefined &&\n connection.readyState !== wsReadyStateConnecting &&\n connection.readyState !== wsReadyStateOpen\n ) {\n return;\n }\n try {\n connection.send(`__YPS:${message}`);\n } catch (e) {\n console.warn(\"Failed to send custom message\", e);\n }\n }\n\n /**\n * Broadcast a custom string message to all connected clients.\n * @param message - The custom message string to broadcast\n * @param excludeConnection - Optional connection to exclude from the broadcast\n */\n broadcastCustomMessage(\n message: string,\n excludeConnection?: Connection\n ): void {\n const formattedMessage = `__YPS:${message}`;\n for (const conn of this.getConnections()) {\n if (excludeConnection && conn === excludeConnection) {\n continue;\n }\n if (\n conn.readyState !== undefined &&\n conn.readyState !== wsReadyStateConnecting &&\n conn.readyState !== wsReadyStateOpen\n ) {\n continue;\n }\n try {\n conn.send(formattedMessage);\n } catch (e) {\n console.warn(\"Failed to broadcast custom message\", e);\n }\n }\n }\n\n handleMessage(connection: Connection, message: WSMessage) {\n if (typeof message === \"string\") {\n // Handle custom messages with __YPS: prefix\n if (message.startsWith(\"__YPS:\")) {\n const customMessage = message.slice(6); // Remove __YPS: prefix\n this.onCustomMessage(connection, customMessage);\n return;\n }\n console.warn(\n `Received non-prefixed string message. Custom messages should be sent using sendMessage() on the provider.`\n );\n return;\n }\n try {\n const encoder = encoding.createEncoder();\n // Convert ArrayBuffer to Uint8Array if needed (ArrayBufferView like Uint8Array can be used directly)\n const uint8Array =\n message instanceof Uint8Array\n ? message\n : message instanceof ArrayBuffer\n ? new Uint8Array(message)\n : new Uint8Array(\n message.buffer,\n message.byteOffset,\n message.byteLength\n );\n const decoder = decoding.createDecoder(uint8Array);\n const messageType = decoding.readVarUint(decoder);\n switch (messageType) {\n case messageSync:\n encoding.writeVarUint(encoder, messageSync);\n readSyncMessage(\n decoder,\n encoder,\n this.document,\n connection,\n this.isReadOnly(connection)\n );\n\n // If the `encoder` only contains the type of reply message and no\n // message, there is no need to send the message. When `encoder` only\n // contains the type of reply, its length is 1.\n if (encoding.length(encoder) > 1) {\n send(connection, encoding.toUint8Array(encoder));\n }\n break;\n case messageAwareness: {\n const awarenessData = decoding.readVarUint8Array(decoder);\n awarenessProtocol.applyAwarenessUpdate(\n this.document.awareness,\n awarenessData,\n connection\n );\n // Forward raw awareness bytes to all connections\n const awarenessEncoder = encoding.createEncoder();\n encoding.writeVarUint(awarenessEncoder, messageAwareness);\n encoding.writeVarUint8Array(awarenessEncoder, awarenessData);\n const awarenessBuff = encoding.toUint8Array(awarenessEncoder);\n for (const c of this.getConnections()) {\n send(c, awarenessBuff);\n }\n break;\n }\n }\n } catch (err) {\n console.error(err);\n // @ts-expect-error - TODO: fix this\n this.document.emit(\"error\", [err]);\n }\n }\n\n onMessage(conn: Connection, message: WSMessage) {\n this.handleMessage(conn, message);\n }\n\n onClose(\n connection: Connection<unknown>,\n _code: number,\n _reason: string,\n _wasClean: boolean\n ): void | Promise<void> {\n // Read controlled awareness clientIDs from connection state\n // (survives hibernation unlike an in-memory Map)\n const controlledIds = getAwarenessIds(connection);\n if (controlledIds.length > 0) {\n awarenessProtocol.removeAwarenessStates(\n this.document.awareness,\n controlledIds,\n null\n );\n }\n }\n\n // TODO: explore why onError gets triggered when a connection closes\n\n onConnect(\n conn: Connection<unknown>,\n _ctx: ConnectionContext\n ): void | Promise<void> {\n // Note: awareness IDs are lazily initialized when the first awareness\n // message is received — no need to call setAwarenessIds(conn, []) here\n\n // send sync step 1\n const encoder = encoding.createEncoder();\n encoding.writeVarUint(encoder, messageSync);\n syncProtocol.writeSyncStep1(encoder, this.document);\n send(conn, encoding.toUint8Array(encoder));\n const awarenessStates = this.document.awareness.getStates();\n if (awarenessStates.size > 0) {\n const encoder = encoding.createEncoder();\n encoding.writeVarUint(encoder, messageAwareness);\n encoding.writeVarUint8Array(\n encoder,\n awarenessProtocol.encodeAwarenessUpdate(\n this.document.awareness,\n Array.from(awarenessStates.keys())\n )\n );\n send(conn, encoding.toUint8Array(encoder));\n }\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":["YDoc"],"sources":["../../src/server/index.ts"],"sourcesContent":["import * as decoding from \"lib0/decoding\";\nimport * as encoding from \"lib0/encoding\";\nimport debounce from \"lodash.debounce\";\nimport type { Connection, ConnectionContext, WSMessage } from \"partyserver\";\nimport { Server } from \"partyserver\";\nimport * as awarenessProtocol from \"y-protocols/awareness\";\nimport * as syncProtocol from \"y-protocols/sync\";\nimport {\n applyUpdate,\n Doc as YDoc,\n encodeStateAsUpdate,\n encodeStateVector,\n UndoManager,\n XmlText,\n XmlElement,\n XmlFragment\n} from \"yjs\";\n\nconst snapshotOrigin = Symbol(\"snapshot-origin\");\ntype YjsRootType =\n | \"Text\"\n | \"Map\"\n | \"Array\"\n | \"XmlText\"\n | \"XmlElement\"\n | \"XmlFragment\";\n\nconst wsReadyStateConnecting = 0;\nconst wsReadyStateOpen = 1;\n// oxlint-disable-next-line no-unused-vars\nconst wsReadyStateClosing = 2;\n// oxlint-disable-next-line no-unused-vars\nconst wsReadyStateClosed = 3;\n\nconst messageSync = 0;\nconst messageAwareness = 1;\n// oxlint-disable-next-line no-unused-vars\nconst messageAuth = 2;\n\n/**\n * Internal key used in connection.setState() to track which awareness\n * client IDs are controlled by each connection. This survives hibernation\n * because connection state is persisted to WebSocket attachments.\n */\nconst AWARENESS_IDS_KEY = \"__ypsAwarenessIds\";\n\ntype YServerConnectionState = {\n [AWARENESS_IDS_KEY]?: number[];\n [key: string]: unknown;\n};\n\nfunction getAwarenessIds(conn: Connection): number[] {\n try {\n const state = conn.state as YServerConnectionState | null;\n return state?.[AWARENESS_IDS_KEY] ?? [];\n } catch {\n return [];\n }\n}\n\nfunction setAwarenessIds(conn: Connection, ids: number[]): void {\n try {\n conn.setState((prev: YServerConnectionState | null) => ({\n ...prev,\n [AWARENESS_IDS_KEY]: ids\n }));\n } catch {\n // ignore — may fail if connection is already closed\n }\n}\n\nclass WSSharedDoc extends YDoc {\n awareness: awarenessProtocol.Awareness;\n\n constructor() {\n super({ gc: true });\n this.awareness = new awarenessProtocol.Awareness(this);\n this.awareness.setLocalState(null);\n\n // Disable the awareness protocol's built-in check interval.\n // It renews the local clock every 15s and removes peers after 30s,\n // but we handle peer cleanup via onClose instead. Clearing it here\n // prevents it from defeating Durable Object hibernation.\n clearInterval(\n (\n this.awareness as unknown as {\n _checkInterval: ReturnType<typeof setInterval>;\n }\n )._checkInterval\n );\n }\n}\n\nconst CALLBACK_DEFAULTS = {\n debounceWait: 2000,\n debounceMaxWait: 10000,\n timeout: 5000\n};\n\nfunction readSyncMessage(\n decoder: decoding.Decoder,\n encoder: encoding.Encoder,\n doc: YDoc,\n transactionOrigin: Connection,\n readOnly = false\n) {\n const messageType = decoding.readVarUint(decoder);\n switch (messageType) {\n case syncProtocol.messageYjsSyncStep1:\n syncProtocol.readSyncStep1(decoder, encoder, doc);\n break;\n case syncProtocol.messageYjsSyncStep2:\n if (!readOnly)\n syncProtocol.readSyncStep2(decoder, doc, transactionOrigin);\n break;\n case syncProtocol.messageYjsUpdate:\n if (!readOnly) syncProtocol.readUpdate(decoder, doc, transactionOrigin);\n break;\n default:\n throw new Error(\"Unknown message type\");\n }\n return messageType;\n}\n\nfunction send(conn: Connection, m: Uint8Array): void {\n if (\n conn.readyState !== undefined &&\n conn.readyState !== wsReadyStateConnecting &&\n conn.readyState !== wsReadyStateOpen\n ) {\n return;\n }\n try {\n conn.send(m);\n } catch {\n // connection is broken, ignore\n }\n}\n\nexport interface CallbackOptions {\n debounceWait?: number;\n debounceMaxWait?: number;\n timeout?: number;\n}\n\ntype ServerClass = new (...args: any[]) => Server;\n\nexport interface YjsInstance {\n readonly document: WSSharedDoc;\n onLoad(): Promise<YDoc | void>;\n onSave(): Promise<void>;\n unstable_replaceDocument(\n snapshotUpdate: Uint8Array,\n getMetadata?: (key: string) => YjsRootType\n ): void;\n isReadOnly(connection: Connection): boolean;\n onCustomMessage(connection: Connection, message: string): void;\n sendCustomMessage(connection: Connection, message: string): void;\n broadcastCustomMessage(message: string, excludeConnection?: Connection): void;\n handleMessage(connection: Connection, message: WSMessage): void;\n}\n\nexport interface YjsStatic {\n callbackOptions: CallbackOptions;\n}\n\nexport function withYjs<TBase extends ServerClass>(\n Base: TBase\n): TBase & YjsStatic & (new (...args: any[]) => YjsInstance) {\n class YjsMixin extends Base {\n static callbackOptions: CallbackOptions = {};\n\n readonly document: WSSharedDoc = new WSSharedDoc();\n\n async onLoad(): Promise<YDoc | void> {\n // to be implemented by the user\n return;\n }\n\n async onSave(): Promise<void> {\n // to be implemented by the user\n }\n\n /**\n * Replaces the document with a different state using Yjs UndoManager key remapping.\n *\n * @param snapshotUpdate - The snapshot update to replace the document with.\n * @param getMetadata (optional) - A function that returns the type of the root for a given key.\n */\n unstable_replaceDocument(\n snapshotUpdate: Uint8Array,\n getMetadata: (key: string) => YjsRootType = () => \"Map\"\n ): void {\n try {\n const doc = this.document;\n const snapshotDoc = new YDoc();\n applyUpdate(snapshotDoc, snapshotUpdate, snapshotOrigin);\n\n const currentStateVector = encodeStateVector(doc);\n const snapshotStateVector = encodeStateVector(snapshotDoc);\n\n const changesSinceSnapshotUpdate = encodeStateAsUpdate(\n doc,\n snapshotStateVector\n );\n\n const undoManager = new UndoManager(\n [...snapshotDoc.share.keys()].map((key) => {\n const type = getMetadata(key);\n if (type === \"Text\") {\n return snapshotDoc.getText(key);\n } else if (type === \"Map\") {\n return snapshotDoc.getMap(key);\n } else if (type === \"Array\") {\n return snapshotDoc.getArray(key);\n } else if (type === \"XmlText\") {\n return snapshotDoc.get(key, XmlText);\n } else if (type === \"XmlElement\") {\n return snapshotDoc.get(key, XmlElement);\n } else if (type === \"XmlFragment\") {\n return snapshotDoc.get(key, XmlFragment);\n }\n throw new Error(`Unknown root type: ${type} for key: ${key}`);\n }),\n {\n trackedOrigins: new Set([snapshotOrigin])\n }\n );\n\n applyUpdate(snapshotDoc, changesSinceSnapshotUpdate, snapshotOrigin);\n undoManager.undo();\n\n const documentChangesSinceSnapshotUpdate = encodeStateAsUpdate(\n snapshotDoc,\n currentStateVector\n );\n\n applyUpdate(this.document, documentChangesSinceSnapshotUpdate);\n } catch (error) {\n throw new Error(\n `Failed to replace document: ${error instanceof Error ? error.message : \"Unknown error\"}`\n );\n }\n }\n\n async onStart(): Promise<void> {\n const src = await this.onLoad();\n if (src != null) {\n const state = encodeStateAsUpdate(src);\n applyUpdate(this.document, state);\n }\n\n // Broadcast doc updates to all connections.\n // Uses this.getConnections() which works for both hibernate and non-hibernate\n // modes and survives DO hibernation (unlike an in-memory Map).\n this.document.on(\"update\", (update: Uint8Array) => {\n const encoder = encoding.createEncoder();\n encoding.writeVarUint(encoder, messageSync);\n syncProtocol.writeUpdate(encoder, update);\n const message = encoding.toUint8Array(encoder);\n for (const conn of this.getConnections()) {\n send(conn, message);\n }\n });\n\n // Track which awareness clientIDs each connection controls.\n // Stored in connection.setState() so it survives hibernation.\n // When conn is null (internal changes like removeAwarenessStates on close),\n // broadcast the update to remaining connections.\n // When conn is non-null (client message), handleMessage broadcasts directly.\n this.document.awareness.on(\n \"update\",\n (\n {\n added,\n updated,\n removed\n }: {\n added: Array<number>;\n updated: Array<number>;\n removed: Array<number>;\n },\n conn: Connection | null\n ) => {\n if (conn !== null) {\n // Track which clientIDs this connection controls\n try {\n const currentIds = new Set(getAwarenessIds(conn));\n for (const clientID of added) currentIds.add(clientID);\n for (const clientID of removed) currentIds.delete(clientID);\n setAwarenessIds(conn, [...currentIds]);\n } catch (_e) {\n // ignore — best-effort tracking\n }\n } else {\n // Internal awareness change (e.g. removeAwarenessStates on close)\n // — broadcast to all remaining connections\n const changedClients = added.concat(updated, removed);\n const encoder = encoding.createEncoder();\n encoding.writeVarUint(encoder, messageAwareness);\n encoding.writeVarUint8Array(\n encoder,\n awarenessProtocol.encodeAwarenessUpdate(\n this.document.awareness,\n changedClients\n )\n );\n const buff = encoding.toUint8Array(encoder);\n for (const c of this.getConnections()) {\n send(c, buff);\n }\n }\n }\n );\n\n // Debounced persistence handler\n const ctor = this.constructor as typeof YjsMixin;\n this.document.on(\n \"update\",\n debounce(\n (_update: Uint8Array, _origin: Connection, _doc: YDoc) => {\n try {\n this.onSave().catch((err) => {\n console.error(\"failed to persist:\", err);\n });\n } catch (err) {\n console.error(\"failed to persist:\", err);\n }\n },\n ctor.callbackOptions.debounceWait || CALLBACK_DEFAULTS.debounceWait,\n {\n maxWait:\n ctor.callbackOptions.debounceMaxWait ||\n CALLBACK_DEFAULTS.debounceMaxWait\n }\n )\n );\n\n // After hibernation wake-up, the doc is empty but existing connections\n // survive. Re-sync by sending sync step 1 to all connections — they'll\n // respond with sync step 2 containing their full state.\n // On first start there are no connections, so this is a no-op.\n const syncEncoder = encoding.createEncoder();\n encoding.writeVarUint(syncEncoder, messageSync);\n syncProtocol.writeSyncStep1(syncEncoder, this.document);\n const syncMessage = encoding.toUint8Array(syncEncoder);\n for (const conn of this.getConnections()) {\n send(conn, syncMessage);\n }\n }\n\n // oxlint-disable-next-line no-unused-vars\n isReadOnly(connection: Connection): boolean {\n // to be implemented by the user\n return false;\n }\n\n /**\n * Handle custom string messages from the client.\n * Override this method to implement custom message handling.\n * @param connection - The connection that sent the message\n * @param message - The custom message string (without the __YPS: prefix)\n */\n // oxlint-disable-next-line no-unused-vars\n onCustomMessage(connection: Connection, message: string): void {\n // to be implemented by the user\n console.warn(\n `Received custom message but onCustomMessage is not implemented in ${this.constructor.name}:`,\n message\n );\n }\n\n /**\n * Send a custom string message to a specific connection.\n * @param connection - The connection to send the message to\n * @param message - The custom message string to send\n */\n sendCustomMessage(connection: Connection, message: string): void {\n if (\n connection.readyState !== undefined &&\n connection.readyState !== wsReadyStateConnecting &&\n connection.readyState !== wsReadyStateOpen\n ) {\n return;\n }\n try {\n connection.send(`__YPS:${message}`);\n } catch (e) {\n console.warn(\"Failed to send custom message\", e);\n }\n }\n\n /**\n * Broadcast a custom string message to all connected clients.\n * @param message - The custom message string to broadcast\n * @param excludeConnection - Optional connection to exclude from the broadcast\n */\n broadcastCustomMessage(\n message: string,\n excludeConnection?: Connection\n ): void {\n const formattedMessage = `__YPS:${message}`;\n for (const conn of this.getConnections()) {\n if (excludeConnection && conn === excludeConnection) {\n continue;\n }\n if (\n conn.readyState !== undefined &&\n conn.readyState !== wsReadyStateConnecting &&\n conn.readyState !== wsReadyStateOpen\n ) {\n continue;\n }\n try {\n conn.send(formattedMessage);\n } catch (e) {\n console.warn(\"Failed to broadcast custom message\", e);\n }\n }\n }\n\n handleMessage(connection: Connection, message: WSMessage) {\n if (typeof message === \"string\") {\n // Handle custom messages with __YPS: prefix\n if (message.startsWith(\"__YPS:\")) {\n const customMessage = message.slice(6); // Remove __YPS: prefix\n this.onCustomMessage(connection, customMessage);\n return;\n }\n console.warn(\n `Received non-prefixed string message. Custom messages should be sent using sendMessage() on the provider.`\n );\n return;\n }\n try {\n const encoder = encoding.createEncoder();\n // Convert ArrayBuffer to Uint8Array if needed (ArrayBufferView like Uint8Array can be used directly)\n const uint8Array =\n message instanceof Uint8Array\n ? message\n : message instanceof ArrayBuffer\n ? new Uint8Array(message)\n : new Uint8Array(\n message.buffer,\n message.byteOffset,\n message.byteLength\n );\n const decoder = decoding.createDecoder(uint8Array);\n const messageType = decoding.readVarUint(decoder);\n switch (messageType) {\n case messageSync:\n encoding.writeVarUint(encoder, messageSync);\n readSyncMessage(\n decoder,\n encoder,\n this.document,\n connection,\n this.isReadOnly(connection)\n );\n\n // If the `encoder` only contains the type of reply message and no\n // message, there is no need to send the message. When `encoder` only\n // contains the type of reply, its length is 1.\n if (encoding.length(encoder) > 1) {\n send(connection, encoding.toUint8Array(encoder));\n }\n break;\n case messageAwareness: {\n const awarenessData = decoding.readVarUint8Array(decoder);\n awarenessProtocol.applyAwarenessUpdate(\n this.document.awareness,\n awarenessData,\n connection\n );\n // Forward raw awareness bytes to all connections\n const awarenessEncoder = encoding.createEncoder();\n encoding.writeVarUint(awarenessEncoder, messageAwareness);\n encoding.writeVarUint8Array(awarenessEncoder, awarenessData);\n const awarenessBuff = encoding.toUint8Array(awarenessEncoder);\n for (const c of this.getConnections()) {\n send(c, awarenessBuff);\n }\n break;\n }\n }\n } catch (err) {\n console.error(err);\n // @ts-expect-error - TODO: fix this\n this.document.emit(\"error\", [err]);\n }\n }\n\n onMessage(conn: Connection, message: WSMessage) {\n this.handleMessage(conn, message);\n }\n\n onClose(\n connection: Connection<unknown>,\n _code: number,\n _reason: string,\n _wasClean: boolean\n ): void | Promise<void> {\n // Read controlled awareness clientIDs from connection state\n // (survives hibernation unlike an in-memory Map)\n const controlledIds = getAwarenessIds(connection);\n if (controlledIds.length > 0) {\n awarenessProtocol.removeAwarenessStates(\n this.document.awareness,\n controlledIds,\n null\n );\n }\n }\n\n // TODO: explore why onError gets triggered when a connection closes\n\n onConnect(\n conn: Connection<unknown>,\n _ctx: ConnectionContext\n ): void | Promise<void> {\n // Note: awareness IDs are lazily initialized when the first awareness\n // message is received — no need to call setAwarenessIds(conn, []) here\n\n // send sync step 1\n const encoder = encoding.createEncoder();\n encoding.writeVarUint(encoder, messageSync);\n syncProtocol.writeSyncStep1(encoder, this.document);\n send(conn, encoding.toUint8Array(encoder));\n const awarenessStates = this.document.awareness.getStates();\n if (awarenessStates.size > 0) {\n const encoder = encoding.createEncoder();\n encoding.writeVarUint(encoder, messageAwareness);\n encoding.writeVarUint8Array(\n encoder,\n awarenessProtocol.encodeAwarenessUpdate(\n this.document.awareness,\n Array.from(awarenessStates.keys())\n )\n );\n send(conn, encoding.toUint8Array(encoder));\n }\n }\n }\n\n return YjsMixin as unknown as TBase &\n YjsStatic &\n (new (...args: any[]) => YjsInstance);\n}\n\nexport const YServer = withYjs(Server);\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "y-partyserver",
|
|
3
|
-
"version": "2.1.
|
|
3
|
+
"version": "2.1.1",
|
|
4
4
|
"description": "",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"collaboration",
|
|
@@ -52,7 +52,7 @@
|
|
|
52
52
|
"devDependencies": {
|
|
53
53
|
"@cloudflare/workers-types": "^4.20260303.0",
|
|
54
54
|
"@types/lodash.debounce": "^4.0.9",
|
|
55
|
-
"partyserver": "
|
|
55
|
+
"partyserver": "^0.3.1",
|
|
56
56
|
"ws": "^8.19.0",
|
|
57
57
|
"yjs": "^13.6.29"
|
|
58
58
|
},
|