y-partyserver 2.0.0 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/provider/index.d.ts +0 -1
- package/dist/provider/index.js +9 -17
- package/dist/provider/index.js.map +1 -1
- package/dist/server/index.d.ts +2 -3
- package/dist/server/index.js +74 -556
- package/dist/server/index.js.map +1 -1
- package/package.json +28 -24
package/dist/provider/index.d.ts
CHANGED
|
@@ -59,7 +59,6 @@ declare class WebsocketProvider extends Observable<string> {
|
|
|
59
59
|
_updateHandler: (update: Uint8Array, origin: unknown) => void;
|
|
60
60
|
_awarenessUpdateHandler: (update: AwarenessUpdate, origin: unknown) => void;
|
|
61
61
|
_unloadHandler: () => void;
|
|
62
|
-
_checkInterval: ReturnType<typeof setInterval> | number;
|
|
63
62
|
constructor(
|
|
64
63
|
serverUrl: string,
|
|
65
64
|
roomname: string,
|
package/dist/provider/index.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import * as decoding from "lib0/decoding";
|
|
2
2
|
import * as encoding from "lib0/encoding";
|
|
3
|
-
import { nanoid } from "nanoid";
|
|
4
3
|
import * as awarenessProtocol from "y-protocols/awareness";
|
|
5
4
|
import * as syncProtocol from "y-protocols/sync";
|
|
6
5
|
import { Doc } from "yjs";
|
|
@@ -9,6 +8,7 @@ import * as math from "lib0/math";
|
|
|
9
8
|
import { Observable } from "lib0/observable";
|
|
10
9
|
import * as time from "lib0/time";
|
|
11
10
|
import * as url from "lib0/url";
|
|
11
|
+
import { nanoid } from "nanoid";
|
|
12
12
|
import * as authProtocol from "y-protocols/auth";
|
|
13
13
|
|
|
14
14
|
//#region src/provider/index.ts
|
|
@@ -17,9 +17,6 @@ const messageQueryAwareness = 3;
|
|
|
17
17
|
const messageAwareness = 1;
|
|
18
18
|
const messageAuth = 2;
|
|
19
19
|
const DEFAULT_DISABLE_BC = typeof window === "undefined";
|
|
20
|
-
function assert(condition, message) {
|
|
21
|
-
if (!condition) throw new Error(message);
|
|
22
|
-
}
|
|
23
20
|
const messageHandlers = [];
|
|
24
21
|
messageHandlers[messageSync] = (encoder, decoder, provider, emitSynced, _messageType) => {
|
|
25
22
|
encoding.writeVarUint(encoder, messageSync);
|
|
@@ -36,7 +33,6 @@ messageHandlers[messageAwareness] = (_encoder, decoder, provider, _emitSynced, _
|
|
|
36
33
|
messageHandlers[messageAuth] = (_encoder, decoder, provider, _emitSynced, _messageType) => {
|
|
37
34
|
authProtocol.readAuthMessage(decoder, provider.doc, (_ydoc, reason) => permissionDeniedHandler(provider, reason));
|
|
38
35
|
};
|
|
39
|
-
const messageReconnectTimeout = 3e4;
|
|
40
36
|
function permissionDeniedHandler(provider, reason) {
|
|
41
37
|
console.warn(`Permission denied to access ${provider.url}.\n${reason}`);
|
|
42
38
|
}
|
|
@@ -80,7 +76,9 @@ function setupWS(provider) {
|
|
|
80
76
|
if (provider.wsconnected) {
|
|
81
77
|
provider.wsconnected = false;
|
|
82
78
|
provider.synced = false;
|
|
83
|
-
|
|
79
|
+
const removedClients = Array.from(provider.awareness.getStates().keys()).filter((client) => client !== provider.doc.clientID);
|
|
80
|
+
awarenessProtocol.removeAwarenessStates(provider.awareness, removedClients, provider);
|
|
81
|
+
for (const clientID of removedClients) provider.awareness.meta.delete(clientID);
|
|
84
82
|
provider.emit("status", [{ status: "disconnected" }]);
|
|
85
83
|
} else provider.wsUnsuccessfulReconnects++;
|
|
86
84
|
setTimeout(setupWS, math.min(math.pow(2, provider.wsUnsuccessfulReconnects) * 100, provider.maxBackoffTime), provider);
|
|
@@ -96,6 +94,7 @@ function setupWS(provider) {
|
|
|
96
94
|
syncProtocol.writeSyncStep1(encoder, provider.doc);
|
|
97
95
|
websocket.send(encoding.toUint8Array(encoder));
|
|
98
96
|
if (provider.awareness.getLocalState() !== null) {
|
|
97
|
+
provider.awareness.setLocalState(provider.awareness.getLocalState());
|
|
99
98
|
const encoderAwarenessState = encoding.createEncoder();
|
|
100
99
|
encoding.writeVarUint(encoderAwarenessState, messageAwareness);
|
|
101
100
|
encoding.writeVarUint8Array(encoderAwarenessState, awarenessProtocol.encodeAwarenessUpdate(provider.awareness, [provider.doc.clientID]));
|
|
@@ -147,7 +146,6 @@ var WebsocketProvider = class extends Observable {
|
|
|
147
146
|
_updateHandler;
|
|
148
147
|
_awarenessUpdateHandler;
|
|
149
148
|
_unloadHandler;
|
|
150
|
-
_checkInterval;
|
|
151
149
|
constructor(serverUrl, roomname, doc, { connect = true, awareness = new awarenessProtocol.Awareness(doc), params = {}, isPrefixedUrl = false, WebSocketPolyfill = DefaultWebSocket, resyncInterval = -1, maxBackoffTime = 2500, disableBc = DEFAULT_DISABLE_BC } = {}) {
|
|
152
150
|
super();
|
|
153
151
|
while (serverUrl[serverUrl.length - 1] === "/") serverUrl = serverUrl.slice(0, serverUrl.length - 1);
|
|
@@ -208,13 +206,8 @@ var WebsocketProvider = class extends Observable {
|
|
|
208
206
|
};
|
|
209
207
|
if (typeof window !== "undefined") window.addEventListener("unload", this._unloadHandler);
|
|
210
208
|
else if (typeof process !== "undefined" && typeof process.on === "function") process.on("exit", this._unloadHandler);
|
|
211
|
-
awareness.on("
|
|
212
|
-
|
|
213
|
-
if (this.wsconnected && messageReconnectTimeout < time.getUnixTime() - this.wsLastMessageReceived) {
|
|
214
|
-
assert(this.ws !== null, "ws must not be null");
|
|
215
|
-
this.ws.close();
|
|
216
|
-
}
|
|
217
|
-
}, messageReconnectTimeout / 10);
|
|
209
|
+
awareness.on("change", this._awarenessUpdateHandler);
|
|
210
|
+
clearInterval(awareness._checkInterval);
|
|
218
211
|
if (connect) this.connect();
|
|
219
212
|
}
|
|
220
213
|
/**
|
|
@@ -232,11 +225,10 @@ var WebsocketProvider = class extends Observable {
|
|
|
232
225
|
}
|
|
233
226
|
destroy() {
|
|
234
227
|
if (this._resyncInterval !== 0) clearInterval(this._resyncInterval);
|
|
235
|
-
clearInterval(this._checkInterval);
|
|
236
228
|
this.disconnect();
|
|
237
229
|
if (typeof window !== "undefined") window.removeEventListener("unload", this._unloadHandler);
|
|
238
230
|
else if (typeof process !== "undefined" && typeof process.off === "function") process.off("exit", this._unloadHandler);
|
|
239
|
-
this.awareness.off("
|
|
231
|
+
this.awareness.off("change", this._awarenessUpdateHandler);
|
|
240
232
|
this.doc.off("update", this._updateHandler);
|
|
241
233
|
super.destroy();
|
|
242
234
|
}
|
|
@@ -295,7 +287,7 @@ var YProvider = class extends WebsocketProvider {
|
|
|
295
287
|
assertType(host, "host", "string");
|
|
296
288
|
assertType(room, "room", "string");
|
|
297
289
|
host = host.replace(/^(http|https|ws|wss):\/\//, "");
|
|
298
|
-
if (host.endsWith("/")) host.slice(0, -1);
|
|
290
|
+
if (host.endsWith("/")) host = host.slice(0, -1);
|
|
299
291
|
const serverUrl = `${options.protocol || (host.startsWith("localhost:") || host.startsWith("127.0.0.1:") || host.startsWith("192.168.") || host.startsWith("10.") || host.startsWith("172.") && host.split(".")[1] >= "16" && host.split(".")[1] <= "31" ? "ws" : "wss")}://${host}${options.prefix || `/parties/${options.party || "main"}`}`;
|
|
300
292
|
const id = options.connectionId ?? nanoid(10);
|
|
301
293
|
const { params, connect = true, ...rest } = options;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":["messageHandlers: Array<\n (\n encoder: encoding.Encoder,\n decoder: decoding.Decoder,\n provider: WebsocketProvider,\n emitSynced: boolean,\n messageType: number\n ) => void\n>","YDoc","#params"],"sources":["../../src/provider/index.ts"],"sourcesContent":["import * as bc from \"lib0/broadcastchannel\";\nimport * as decoding from \"lib0/decoding\";\nimport * as encoding from \"lib0/encoding\";\nimport * as math from \"lib0/math\";\nimport { Observable } from \"lib0/observable\";\nimport * as time from \"lib0/time\";\nimport * as url from \"lib0/url\";\nimport { nanoid } from \"nanoid\";\nimport * as authProtocol from \"y-protocols/auth\";\nimport * as awarenessProtocol from \"y-protocols/awareness\";\nimport * as syncProtocol from \"y-protocols/sync\";\nimport { Doc as YDoc } from \"yjs\";\n\nexport const messageSync = 0;\nexport const messageQueryAwareness = 3;\nexport const messageAwareness = 1;\nexport const messageAuth = 2;\n\n// Disable BroadcastChannel by default in Cloudflare Workers / Node\nconst DEFAULT_DISABLE_BC = typeof window === \"undefined\";\n\nfunction assert(condition: unknown, message: string): asserts condition {\n if (!condition) {\n throw new Error(message);\n }\n}\n\nconst messageHandlers: Array<\n (\n encoder: encoding.Encoder,\n decoder: decoding.Decoder,\n provider: WebsocketProvider,\n emitSynced: boolean,\n messageType: number\n ) => void\n> = [];\n\nmessageHandlers[messageSync] = (\n encoder,\n decoder,\n provider,\n emitSynced,\n _messageType\n) => {\n encoding.writeVarUint(encoder, messageSync);\n const syncMessageType = syncProtocol.readSyncMessage(\n decoder,\n encoder,\n provider.doc,\n provider\n );\n if (\n emitSynced &&\n syncMessageType === syncProtocol.messageYjsSyncStep2 &&\n !provider.synced\n ) {\n provider.synced = true;\n }\n};\n\nmessageHandlers[messageQueryAwareness] = (\n encoder,\n _decoder,\n provider,\n _emitSynced,\n _messageType\n) => {\n encoding.writeVarUint(encoder, messageAwareness);\n encoding.writeVarUint8Array(\n encoder,\n awarenessProtocol.encodeAwarenessUpdate(\n provider.awareness,\n Array.from(provider.awareness.getStates().keys())\n )\n );\n};\n\nmessageHandlers[messageAwareness] = (\n _encoder,\n decoder,\n provider,\n _emitSynced,\n _messageType\n) => {\n awarenessProtocol.applyAwarenessUpdate(\n provider.awareness,\n decoding.readVarUint8Array(decoder),\n provider\n );\n};\n\nmessageHandlers[messageAuth] = (\n _encoder,\n decoder,\n provider,\n _emitSynced,\n _messageType\n) => {\n authProtocol.readAuthMessage(decoder, provider.doc, (_ydoc, reason) =>\n permissionDeniedHandler(provider, reason)\n );\n};\n\n// @todo - this should depend on awareness.outdatedTime\nconst messageReconnectTimeout = 30000;\n\nfunction permissionDeniedHandler(provider: WebsocketProvider, reason: string) {\n console.warn(`Permission denied to access ${provider.url}.\\n${reason}`);\n}\n\nfunction readMessage(\n provider: WebsocketProvider,\n buf: Uint8Array,\n emitSynced: boolean\n): encoding.Encoder {\n const decoder = decoding.createDecoder(buf);\n const encoder = encoding.createEncoder();\n const messageType = decoding.readVarUint(decoder);\n const messageHandler = provider.messageHandlers[messageType];\n if (/** @type {any} */ messageHandler) {\n messageHandler(encoder, decoder, provider, emitSynced, messageType);\n } else {\n console.error(\"Unable to compute message\");\n }\n return encoder;\n}\n\nfunction setupWS(provider: WebsocketProvider) {\n if (provider.shouldConnect && provider.ws === null) {\n if (!provider._WS) {\n throw new Error(\n \"No WebSocket implementation available, did you forget to pass options.WebSocketPolyfill?\"\n );\n }\n const websocket = new provider._WS(provider.url);\n websocket.binaryType = \"arraybuffer\";\n provider.ws = websocket;\n provider.wsconnecting = true;\n provider.wsconnected = false;\n provider.synced = false;\n\n websocket.addEventListener(\"message\", (event) => {\n if (typeof event.data === \"string\") {\n // Handle custom messages with __YPS: prefix\n if (event.data.startsWith(\"__YPS:\")) {\n const customMessage = event.data.slice(6); // Remove __YPS: prefix\n provider.emit(\"custom-message\", [customMessage]);\n }\n return;\n }\n provider.wsLastMessageReceived = time.getUnixTime();\n const encoder = readMessage(provider, new Uint8Array(event.data), true);\n if (encoding.length(encoder) > 1) {\n websocket.send(encoding.toUint8Array(encoder));\n }\n });\n websocket.addEventListener(\"error\", (event) => {\n provider.emit(\"connection-error\", [event, provider]);\n });\n websocket.addEventListener(\"close\", (event) => {\n provider.emit(\"connection-close\", [event, provider]);\n provider.ws = null;\n provider.wsconnecting = false;\n if (provider.wsconnected) {\n provider.wsconnected = false;\n provider.synced = false;\n // update awareness (all users except local left)\n awarenessProtocol.removeAwarenessStates(\n provider.awareness,\n Array.from(provider.awareness.getStates().keys()).filter(\n (client) => client !== provider.doc.clientID\n ),\n provider\n );\n provider.emit(\"status\", [\n {\n status: \"disconnected\"\n }\n ]);\n } else {\n provider.wsUnsuccessfulReconnects++;\n }\n // Start with no reconnect timeout and increase timeout by\n // using exponential backoff starting with 100ms\n setTimeout(\n setupWS,\n math.min(\n math.pow(2, provider.wsUnsuccessfulReconnects) * 100,\n provider.maxBackoffTime\n ),\n provider\n );\n });\n websocket.addEventListener(\"open\", () => {\n provider.wsLastMessageReceived = time.getUnixTime();\n provider.wsconnecting = false;\n provider.wsconnected = true;\n provider.wsUnsuccessfulReconnects = 0;\n provider.emit(\"status\", [\n {\n status: \"connected\"\n }\n ]);\n // always send sync step 1 when connected\n const encoder = encoding.createEncoder();\n encoding.writeVarUint(encoder, messageSync);\n syncProtocol.writeSyncStep1(encoder, provider.doc);\n websocket.send(encoding.toUint8Array(encoder));\n // broadcast local awareness state\n if (provider.awareness.getLocalState() !== null) {\n const encoderAwarenessState = encoding.createEncoder();\n encoding.writeVarUint(encoderAwarenessState, messageAwareness);\n encoding.writeVarUint8Array(\n encoderAwarenessState,\n awarenessProtocol.encodeAwarenessUpdate(provider.awareness, [\n provider.doc.clientID\n ])\n );\n websocket.send(encoding.toUint8Array(encoderAwarenessState));\n }\n });\n provider.emit(\"status\", [\n {\n status: \"connecting\"\n }\n ]);\n }\n}\n\nfunction broadcastMessage(provider: WebsocketProvider, buf: Uint8Array) {\n const ws = provider.ws;\n if (provider.wsconnected && ws && ws.readyState === ws.OPEN) {\n ws.send(buf);\n }\n if (provider.bcconnected) {\n bc.publish(provider.bcChannel, buf, provider);\n }\n}\n\ntype AwarenessUpdate = {\n added: number[];\n updated: number[];\n removed: number[];\n};\n\nconst DefaultWebSocket = typeof WebSocket === \"undefined\" ? null : WebSocket;\n\n/**\n * Websocket Provider for Yjs. Creates a websocket connection to sync the shared document.\n * The document name is attached to the provided url. I.e. the following example\n * creates a websocket connection to http://localhost:1234/my-document-name\n *\n * @example\n * import * as Y from 'yjs'\n * import { WebsocketProvider } from 'y-websocket'\n * const doc = new Y.Doc()\n * const provider = new WebsocketProvider('http://localhost:1234', 'my-document-name', doc)\n *\n * @extends {Observable<string>}\n */\nexport class WebsocketProvider extends Observable<string> {\n maxBackoffTime: number;\n bcChannel: string;\n url: string;\n roomname: string;\n doc: YDoc;\n _WS: typeof WebSocket;\n awareness: awarenessProtocol.Awareness;\n wsconnected: boolean;\n wsconnecting: boolean;\n bcconnected: boolean;\n disableBc: boolean;\n wsUnsuccessfulReconnects: number;\n messageHandlers: typeof messageHandlers;\n _synced: boolean;\n ws: WebSocket | null;\n wsLastMessageReceived: number;\n shouldConnect: boolean; // Whether to connect to other peers or not\n _resyncInterval: ReturnType<typeof setInterval> | number;\n _bcSubscriber: (message: Uint8Array, origin: unknown) => void;\n _updateHandler: (update: Uint8Array, origin: unknown) => void;\n _awarenessUpdateHandler: (update: AwarenessUpdate, origin: unknown) => void;\n _unloadHandler: () => void;\n _checkInterval: ReturnType<typeof setInterval> | number;\n\n constructor(\n serverUrl: string,\n roomname: string,\n doc: YDoc,\n {\n connect = true,\n awareness = new awarenessProtocol.Awareness(doc),\n params = {},\n isPrefixedUrl = false,\n WebSocketPolyfill = DefaultWebSocket, // Optionally provide a WebSocket polyfill\n resyncInterval = -1, // Request server state every `resyncInterval` milliseconds\n maxBackoffTime = 2500, // Maximum amount of time to wait before trying to reconnect (we try to reconnect using exponential backoff)\n disableBc = DEFAULT_DISABLE_BC // Disable cross-tab BroadcastChannel communication\n }: {\n connect?: boolean;\n awareness?: awarenessProtocol.Awareness;\n params?: { [s: string]: string };\n isPrefixedUrl?: boolean;\n WebSocketPolyfill?: typeof WebSocket | null;\n resyncInterval?: number;\n maxBackoffTime?: number;\n disableBc?: boolean;\n } = {}\n ) {\n super();\n // ensure that url is always ends with /\n while (serverUrl[serverUrl.length - 1] === \"/\") {\n serverUrl = serverUrl.slice(0, serverUrl.length - 1);\n }\n const encodedParams = url.encodeQueryParams(params);\n this.maxBackoffTime = maxBackoffTime;\n this.bcChannel = `${serverUrl}/${roomname}`;\n this.url = isPrefixedUrl\n ? serverUrl\n : `${serverUrl}/${roomname}${encodedParams.length === 0 ? \"\" : `?${encodedParams}`}`;\n this.roomname = roomname;\n this.doc = doc;\n this._WS = WebSocketPolyfill!;\n this.awareness = awareness;\n this.wsconnected = false;\n this.wsconnecting = false;\n this.bcconnected = false;\n this.disableBc = disableBc;\n this.wsUnsuccessfulReconnects = 0;\n this.messageHandlers = messageHandlers.slice();\n\n this._synced = false;\n\n this.ws = null;\n this.wsLastMessageReceived = 0;\n\n this.shouldConnect = connect;\n\n this._resyncInterval = 0;\n if (resyncInterval > 0) {\n this._resyncInterval = /** @type {any} */ setInterval(() => {\n if (this.ws && this.ws.readyState === WebSocket.OPEN) {\n // resend sync step 1\n const encoder = encoding.createEncoder();\n encoding.writeVarUint(encoder, messageSync);\n syncProtocol.writeSyncStep1(encoder, doc);\n this.ws.send(encoding.toUint8Array(encoder));\n }\n }, resyncInterval);\n }\n\n this._bcSubscriber = (data: Uint8Array, origin: unknown) => {\n if (origin !== this) {\n const encoder = readMessage(this, new Uint8Array(data), false);\n if (encoding.length(encoder) > 1) {\n bc.publish(this.bcChannel, encoding.toUint8Array(encoder), this);\n }\n }\n };\n /**\n * Listens to Yjs updates and sends them to remote peers (ws and broadcastchannel)\n */\n this._updateHandler = (update: Uint8Array, origin: unknown) => {\n if (origin !== this) {\n const encoder = encoding.createEncoder();\n encoding.writeVarUint(encoder, messageSync);\n syncProtocol.writeUpdate(encoder, update);\n broadcastMessage(this, encoding.toUint8Array(encoder));\n }\n };\n this.doc.on(\"update\", this._updateHandler);\n\n this._awarenessUpdateHandler = (\n { added, updated, removed }: AwarenessUpdate,\n _origin: unknown\n ) => {\n const changedClients = added.concat(updated).concat(removed);\n const encoder = encoding.createEncoder();\n encoding.writeVarUint(encoder, messageAwareness);\n encoding.writeVarUint8Array(\n encoder,\n awarenessProtocol.encodeAwarenessUpdate(awareness, changedClients)\n );\n broadcastMessage(this, encoding.toUint8Array(encoder));\n };\n this._unloadHandler = () => {\n awarenessProtocol.removeAwarenessStates(\n this.awareness,\n [doc.clientID],\n \"window unload\"\n );\n };\n if (typeof window !== \"undefined\") {\n window.addEventListener(\"unload\", this._unloadHandler);\n } else if (\n typeof process !== \"undefined\" &&\n typeof process.on === \"function\"\n ) {\n process.on(\"exit\", this._unloadHandler);\n }\n awareness.on(\"update\", this._awarenessUpdateHandler);\n this._checkInterval = /** @type {any} */ setInterval(() => {\n if (\n this.wsconnected &&\n messageReconnectTimeout <\n time.getUnixTime() - this.wsLastMessageReceived\n ) {\n assert(this.ws !== null, \"ws must not be null\");\n // no message received in a long time - not even your own awareness\n // updates (which are updated every 15 seconds)\n this.ws.close();\n }\n }, messageReconnectTimeout / 10);\n if (connect) {\n this.connect();\n }\n }\n\n /**\n * @type {boolean}\n */\n get synced() {\n return this._synced;\n }\n\n set synced(state) {\n if (this._synced !== state) {\n this._synced = state;\n this.emit(\"synced\", [state]);\n this.emit(\"sync\", [state]);\n }\n }\n\n destroy() {\n if (this._resyncInterval !== 0) {\n clearInterval(this._resyncInterval);\n }\n clearInterval(this._checkInterval);\n this.disconnect();\n if (typeof window !== \"undefined\") {\n window.removeEventListener(\"unload\", this._unloadHandler);\n } else if (\n typeof process !== \"undefined\" &&\n typeof process.off === \"function\"\n ) {\n process.off(\"exit\", this._unloadHandler);\n }\n this.awareness.off(\"update\", this._awarenessUpdateHandler);\n this.doc.off(\"update\", this._updateHandler);\n super.destroy();\n }\n\n connectBc() {\n if (this.disableBc) {\n return;\n }\n if (!this.bcconnected) {\n bc.subscribe(this.bcChannel, this._bcSubscriber);\n this.bcconnected = true;\n }\n // send sync step1 to bc\n // write sync step 1\n const encoderSync = encoding.createEncoder();\n encoding.writeVarUint(encoderSync, messageSync);\n syncProtocol.writeSyncStep1(encoderSync, this.doc);\n bc.publish(this.bcChannel, encoding.toUint8Array(encoderSync), this);\n // broadcast local state\n const encoderState = encoding.createEncoder();\n encoding.writeVarUint(encoderState, messageSync);\n syncProtocol.writeSyncStep2(encoderState, this.doc);\n bc.publish(this.bcChannel, encoding.toUint8Array(encoderState), this);\n // write queryAwareness\n const encoderAwarenessQuery = encoding.createEncoder();\n encoding.writeVarUint(encoderAwarenessQuery, messageQueryAwareness);\n bc.publish(\n this.bcChannel,\n encoding.toUint8Array(encoderAwarenessQuery),\n this\n );\n // broadcast local awareness state\n const encoderAwarenessState = encoding.createEncoder();\n encoding.writeVarUint(encoderAwarenessState, messageAwareness);\n encoding.writeVarUint8Array(\n encoderAwarenessState,\n awarenessProtocol.encodeAwarenessUpdate(this.awareness, [\n this.doc.clientID\n ])\n );\n bc.publish(\n this.bcChannel,\n encoding.toUint8Array(encoderAwarenessState),\n this\n );\n }\n\n disconnectBc() {\n // broadcast message with local awareness state set to null (indicating disconnect)\n const encoder = encoding.createEncoder();\n encoding.writeVarUint(encoder, messageAwareness);\n encoding.writeVarUint8Array(\n encoder,\n awarenessProtocol.encodeAwarenessUpdate(\n this.awareness,\n [this.doc.clientID],\n new Map()\n )\n );\n broadcastMessage(this, encoding.toUint8Array(encoder));\n if (this.bcconnected) {\n bc.unsubscribe(this.bcChannel, this._bcSubscriber);\n this.bcconnected = false;\n }\n }\n\n disconnect() {\n this.shouldConnect = false;\n this.disconnectBc();\n if (this.ws !== null) {\n this.ws.close();\n }\n }\n\n connect() {\n this.shouldConnect = true;\n if (!this.wsconnected && this.ws === null) {\n setupWS(this);\n this.connectBc();\n }\n }\n}\n\nfunction assertType(value: unknown, label: string, type: string) {\n if (typeof value !== type) {\n throw new Error(\n `Invalid \"${label}\" parameter provided to YProvider. Expected: ${type}, received: ${value as string}`\n );\n }\n}\n\ntype Params = Record<string, string | null | undefined>;\ntype ParamsProvider = Params | (() => Params | Promise<Params>);\ntype BaseProviderOptions = ConstructorParameters<typeof WebsocketProvider>[3];\n\ntype YProviderOptions = Omit<NonNullable<BaseProviderOptions>, \"params\"> & {\n connectionId?: string;\n party?: string;\n prefix?: string;\n params?: ParamsProvider;\n protocol?: \"ws\" | \"wss\";\n};\n\nexport default class YProvider extends WebsocketProvider {\n id: string;\n #params?: ParamsProvider;\n\n constructor(\n host: string,\n room: string,\n doc?: YDoc,\n options: YProviderOptions = {}\n ) {\n assertType(host, \"host\", \"string\");\n assertType(room, \"room\", \"string\");\n\n // strip the protocol from the beginning of `host` if any\n host = host.replace(/^(http|https|ws|wss):\\/\\//, \"\");\n\n // strip trailing slash from host if any\n if (host.endsWith(\"/\")) {\n host.slice(0, -1);\n }\n\n const serverUrl = `${\n options.protocol ||\n (host.startsWith(\"localhost:\") ||\n host.startsWith(\"127.0.0.1:\") ||\n host.startsWith(\"192.168.\") ||\n host.startsWith(\"10.\") ||\n (host.startsWith(\"172.\") &&\n host.split(\".\")[1] >= \"16\" &&\n host.split(\".\")[1] <= \"31\")\n ? \"ws\"\n : \"wss\")\n }://${host}${options.prefix || `/parties/${options.party || \"main\"}`}`;\n\n // use provided id, or generate a random one\n const id = options.connectionId ?? nanoid(10);\n\n // don't pass params to WebsocketProvider, we override them in connect()\n const { params, connect = true, ...rest } = options;\n\n // don't connect until we've updated the url parameters\n const baseOptions = {\n ...rest,\n isPrefixedUrl: !!options.prefix,\n connect: false\n };\n\n super(serverUrl, room, doc ?? new YDoc(), baseOptions);\n\n this.id = id;\n this.#params = params;\n\n if (connect) {\n void this.connect();\n }\n }\n\n async connect() {\n try {\n // get updated url parameters\n const nextParams =\n typeof this.#params === \"function\"\n ? await this.#params()\n : this.#params;\n // override current url parameters before connecting\n const urlParams = new URLSearchParams([[\"_pk\", this.id]]);\n if (nextParams) {\n for (const [key, value] of Object.entries(nextParams)) {\n // filter out null/undefined values\n if (value !== null && value !== undefined) {\n urlParams.append(key, value);\n }\n }\n }\n\n const nextUrl = new URL(this.url);\n nextUrl.search = urlParams.toString();\n this.url = nextUrl.toString();\n\n // finally, connect\n super.connect();\n } catch (err) {\n console.error(\"Failed to open connecton to PartyServer\", err);\n throw err;\n }\n }\n\n sendMessage(message: string) {\n this.ws?.send(`__YPS:${message}`);\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":["YDoc","#params"],"sources":["../../src/provider/index.ts"],"sourcesContent":["import * as bc from \"lib0/broadcastchannel\";\nimport * as decoding from \"lib0/decoding\";\nimport * as encoding from \"lib0/encoding\";\nimport * as math from \"lib0/math\";\nimport { Observable } from \"lib0/observable\";\nimport * as time from \"lib0/time\";\nimport * as url from \"lib0/url\";\nimport { nanoid } from \"nanoid\";\nimport * as authProtocol from \"y-protocols/auth\";\nimport * as awarenessProtocol from \"y-protocols/awareness\";\nimport * as syncProtocol from \"y-protocols/sync\";\nimport { Doc as YDoc } from \"yjs\";\n\nexport const messageSync = 0;\nexport const messageQueryAwareness = 3;\nexport const messageAwareness = 1;\nexport const messageAuth = 2;\n\n// Disable BroadcastChannel by default in Cloudflare Workers / Node\nconst DEFAULT_DISABLE_BC = typeof window === \"undefined\";\n\nconst messageHandlers: Array<\n (\n encoder: encoding.Encoder,\n decoder: decoding.Decoder,\n provider: WebsocketProvider,\n emitSynced: boolean,\n messageType: number\n ) => void\n> = [];\n\nmessageHandlers[messageSync] = (\n encoder,\n decoder,\n provider,\n emitSynced,\n _messageType\n) => {\n encoding.writeVarUint(encoder, messageSync);\n const syncMessageType = syncProtocol.readSyncMessage(\n decoder,\n encoder,\n provider.doc,\n provider\n );\n if (\n emitSynced &&\n syncMessageType === syncProtocol.messageYjsSyncStep2 &&\n !provider.synced\n ) {\n provider.synced = true;\n }\n};\n\nmessageHandlers[messageQueryAwareness] = (\n encoder,\n _decoder,\n provider,\n _emitSynced,\n _messageType\n) => {\n encoding.writeVarUint(encoder, messageAwareness);\n encoding.writeVarUint8Array(\n encoder,\n awarenessProtocol.encodeAwarenessUpdate(\n provider.awareness,\n Array.from(provider.awareness.getStates().keys())\n )\n );\n};\n\nmessageHandlers[messageAwareness] = (\n _encoder,\n decoder,\n provider,\n _emitSynced,\n _messageType\n) => {\n awarenessProtocol.applyAwarenessUpdate(\n provider.awareness,\n decoding.readVarUint8Array(decoder),\n provider\n );\n};\n\nmessageHandlers[messageAuth] = (\n _encoder,\n decoder,\n provider,\n _emitSynced,\n _messageType\n) => {\n authProtocol.readAuthMessage(decoder, provider.doc, (_ydoc, reason) =>\n permissionDeniedHandler(provider, reason)\n );\n};\n\nfunction permissionDeniedHandler(provider: WebsocketProvider, reason: string) {\n console.warn(`Permission denied to access ${provider.url}.\\n${reason}`);\n}\n\nfunction readMessage(\n provider: WebsocketProvider,\n buf: Uint8Array,\n emitSynced: boolean\n): encoding.Encoder {\n const decoder = decoding.createDecoder(buf);\n const encoder = encoding.createEncoder();\n const messageType = decoding.readVarUint(decoder);\n const messageHandler = provider.messageHandlers[messageType];\n if (/** @type {any} */ messageHandler) {\n messageHandler(encoder, decoder, provider, emitSynced, messageType);\n } else {\n console.error(\"Unable to compute message\");\n }\n return encoder;\n}\n\nfunction setupWS(provider: WebsocketProvider) {\n if (provider.shouldConnect && provider.ws === null) {\n if (!provider._WS) {\n throw new Error(\n \"No WebSocket implementation available, did you forget to pass options.WebSocketPolyfill?\"\n );\n }\n const websocket = new provider._WS(provider.url);\n websocket.binaryType = \"arraybuffer\";\n provider.ws = websocket;\n provider.wsconnecting = true;\n provider.wsconnected = false;\n provider.synced = false;\n\n websocket.addEventListener(\"message\", (event) => {\n if (typeof event.data === \"string\") {\n // Handle custom messages with __YPS: prefix\n if (event.data.startsWith(\"__YPS:\")) {\n const customMessage = event.data.slice(6); // Remove __YPS: prefix\n provider.emit(\"custom-message\", [customMessage]);\n }\n return;\n }\n provider.wsLastMessageReceived = time.getUnixTime();\n const encoder = readMessage(provider, new Uint8Array(event.data), true);\n if (encoding.length(encoder) > 1) {\n websocket.send(encoding.toUint8Array(encoder));\n }\n });\n websocket.addEventListener(\"error\", (event) => {\n provider.emit(\"connection-error\", [event, provider]);\n });\n websocket.addEventListener(\"close\", (event) => {\n provider.emit(\"connection-close\", [event, provider]);\n provider.ws = null;\n provider.wsconnecting = false;\n if (provider.wsconnected) {\n provider.wsconnected = false;\n provider.synced = false;\n // update awareness (all users except local left)\n const removedClients = Array.from(\n provider.awareness.getStates().keys()\n ).filter((client) => client !== provider.doc.clientID);\n awarenessProtocol.removeAwarenessStates(\n provider.awareness,\n removedClients,\n provider\n );\n // Clear stale meta for remote clients so their awareness\n // updates are accepted on reconnect (clock check starts fresh)\n for (const clientID of removedClients) {\n provider.awareness.meta.delete(clientID);\n }\n provider.emit(\"status\", [\n {\n status: \"disconnected\"\n }\n ]);\n } else {\n provider.wsUnsuccessfulReconnects++;\n }\n // Start with no reconnect timeout and increase timeout by\n // using exponential backoff starting with 100ms\n setTimeout(\n setupWS,\n math.min(\n math.pow(2, provider.wsUnsuccessfulReconnects) * 100,\n provider.maxBackoffTime\n ),\n provider\n );\n });\n websocket.addEventListener(\"open\", () => {\n provider.wsLastMessageReceived = time.getUnixTime();\n provider.wsconnecting = false;\n provider.wsconnected = true;\n provider.wsUnsuccessfulReconnects = 0;\n provider.emit(\"status\", [\n {\n status: \"connected\"\n }\n ]);\n // always send sync step 1 when connected\n const encoder = encoding.createEncoder();\n encoding.writeVarUint(encoder, messageSync);\n syncProtocol.writeSyncStep1(encoder, provider.doc);\n websocket.send(encoding.toUint8Array(encoder));\n // broadcast local awareness state\n if (provider.awareness.getLocalState() !== null) {\n // Re-set local state to bump the awareness clock, ensuring\n // remote clients accept the update even if they have stale meta\n provider.awareness.setLocalState(provider.awareness.getLocalState());\n const encoderAwarenessState = encoding.createEncoder();\n encoding.writeVarUint(encoderAwarenessState, messageAwareness);\n encoding.writeVarUint8Array(\n encoderAwarenessState,\n awarenessProtocol.encodeAwarenessUpdate(provider.awareness, [\n provider.doc.clientID\n ])\n );\n websocket.send(encoding.toUint8Array(encoderAwarenessState));\n }\n });\n provider.emit(\"status\", [\n {\n status: \"connecting\"\n }\n ]);\n }\n}\n\nfunction broadcastMessage(provider: WebsocketProvider, buf: Uint8Array) {\n const ws = provider.ws;\n if (provider.wsconnected && ws && ws.readyState === ws.OPEN) {\n ws.send(buf);\n }\n if (provider.bcconnected) {\n bc.publish(provider.bcChannel, buf, provider);\n }\n}\n\ntype AwarenessUpdate = {\n added: number[];\n updated: number[];\n removed: number[];\n};\n\nconst DefaultWebSocket = typeof WebSocket === \"undefined\" ? null : WebSocket;\n\n/**\n * Websocket Provider for Yjs. Creates a websocket connection to sync the shared document.\n * The document name is attached to the provided url. I.e. the following example\n * creates a websocket connection to http://localhost:1234/my-document-name\n *\n * @example\n * import * as Y from 'yjs'\n * import { WebsocketProvider } from 'y-websocket'\n * const doc = new Y.Doc()\n * const provider = new WebsocketProvider('http://localhost:1234', 'my-document-name', doc)\n *\n * @extends {Observable<string>}\n */\nexport class WebsocketProvider extends Observable<string> {\n maxBackoffTime: number;\n bcChannel: string;\n url: string;\n roomname: string;\n doc: YDoc;\n _WS: typeof WebSocket;\n awareness: awarenessProtocol.Awareness;\n wsconnected: boolean;\n wsconnecting: boolean;\n bcconnected: boolean;\n disableBc: boolean;\n wsUnsuccessfulReconnects: number;\n messageHandlers: typeof messageHandlers;\n _synced: boolean;\n ws: WebSocket | null;\n wsLastMessageReceived: number;\n shouldConnect: boolean; // Whether to connect to other peers or not\n _resyncInterval: ReturnType<typeof setInterval> | number;\n _bcSubscriber: (message: Uint8Array, origin: unknown) => void;\n _updateHandler: (update: Uint8Array, origin: unknown) => void;\n _awarenessUpdateHandler: (update: AwarenessUpdate, origin: unknown) => void;\n _unloadHandler: () => void;\n\n constructor(\n serverUrl: string,\n roomname: string,\n doc: YDoc,\n {\n connect = true,\n awareness = new awarenessProtocol.Awareness(doc),\n params = {},\n isPrefixedUrl = false,\n WebSocketPolyfill = DefaultWebSocket, // Optionally provide a WebSocket polyfill\n resyncInterval = -1, // Request server state every `resyncInterval` milliseconds\n maxBackoffTime = 2500, // Maximum amount of time to wait before trying to reconnect (we try to reconnect using exponential backoff)\n disableBc = DEFAULT_DISABLE_BC // Disable cross-tab BroadcastChannel communication\n }: {\n connect?: boolean;\n awareness?: awarenessProtocol.Awareness;\n params?: { [s: string]: string };\n isPrefixedUrl?: boolean;\n WebSocketPolyfill?: typeof WebSocket | null;\n resyncInterval?: number;\n maxBackoffTime?: number;\n disableBc?: boolean;\n } = {}\n ) {\n super();\n // ensure that url is always ends with /\n while (serverUrl[serverUrl.length - 1] === \"/\") {\n serverUrl = serverUrl.slice(0, serverUrl.length - 1);\n }\n const encodedParams = url.encodeQueryParams(params);\n this.maxBackoffTime = maxBackoffTime;\n this.bcChannel = `${serverUrl}/${roomname}`;\n this.url = isPrefixedUrl\n ? serverUrl\n : `${serverUrl}/${roomname}${encodedParams.length === 0 ? \"\" : `?${encodedParams}`}`;\n this.roomname = roomname;\n this.doc = doc;\n this._WS = WebSocketPolyfill!;\n this.awareness = awareness;\n this.wsconnected = false;\n this.wsconnecting = false;\n this.bcconnected = false;\n this.disableBc = disableBc;\n this.wsUnsuccessfulReconnects = 0;\n this.messageHandlers = messageHandlers.slice();\n\n this._synced = false;\n\n this.ws = null;\n this.wsLastMessageReceived = 0;\n\n this.shouldConnect = connect;\n\n this._resyncInterval = 0;\n if (resyncInterval > 0) {\n this._resyncInterval = /** @type {any} */ setInterval(() => {\n if (this.ws && this.ws.readyState === WebSocket.OPEN) {\n // resend sync step 1\n const encoder = encoding.createEncoder();\n encoding.writeVarUint(encoder, messageSync);\n syncProtocol.writeSyncStep1(encoder, doc);\n this.ws.send(encoding.toUint8Array(encoder));\n }\n }, resyncInterval);\n }\n\n this._bcSubscriber = (data: Uint8Array, origin: unknown) => {\n if (origin !== this) {\n const encoder = readMessage(this, new Uint8Array(data), false);\n if (encoding.length(encoder) > 1) {\n bc.publish(this.bcChannel, encoding.toUint8Array(encoder), this);\n }\n }\n };\n /**\n * Listens to Yjs updates and sends them to remote peers (ws and broadcastchannel)\n */\n this._updateHandler = (update: Uint8Array, origin: unknown) => {\n if (origin !== this) {\n const encoder = encoding.createEncoder();\n encoding.writeVarUint(encoder, messageSync);\n syncProtocol.writeUpdate(encoder, update);\n broadcastMessage(this, encoding.toUint8Array(encoder));\n }\n };\n this.doc.on(\"update\", this._updateHandler);\n\n this._awarenessUpdateHandler = (\n { added, updated, removed }: AwarenessUpdate,\n _origin: unknown\n ) => {\n const changedClients = added.concat(updated).concat(removed);\n const encoder = encoding.createEncoder();\n encoding.writeVarUint(encoder, messageAwareness);\n encoding.writeVarUint8Array(\n encoder,\n awarenessProtocol.encodeAwarenessUpdate(awareness, changedClients)\n );\n broadcastMessage(this, encoding.toUint8Array(encoder));\n };\n this._unloadHandler = () => {\n awarenessProtocol.removeAwarenessStates(\n this.awareness,\n [doc.clientID],\n \"window unload\"\n );\n };\n if (typeof window !== \"undefined\") {\n window.addEventListener(\"unload\", this._unloadHandler);\n } else if (\n typeof process !== \"undefined\" &&\n typeof process.on === \"function\"\n ) {\n process.on(\"exit\", this._unloadHandler);\n }\n // Listen on 'change' (not 'update') so that clock-only awareness\n // renewals (the 15-second heartbeat) do NOT produce network traffic.\n // Only actual state changes (cursor moved, name changed, etc.) are sent.\n // This allows Durable Objects to hibernate when sessions are idle.\n awareness.on(\"change\", this._awarenessUpdateHandler);\n\n // Disable the awareness protocol's built-in check interval.\n // It renews the local clock every 15s (causing wire traffic that defeats\n // DO hibernation) and removes remote peers after 30s of inactivity.\n // We handle peer cleanup via WebSocket close events instead.\n clearInterval(\n (\n awareness as unknown as {\n _checkInterval: ReturnType<typeof setInterval>;\n }\n )._checkInterval\n );\n if (connect) {\n this.connect();\n }\n }\n\n /**\n * @type {boolean}\n */\n get synced() {\n return this._synced;\n }\n\n set synced(state) {\n if (this._synced !== state) {\n this._synced = state;\n this.emit(\"synced\", [state]);\n this.emit(\"sync\", [state]);\n }\n }\n\n destroy() {\n if (this._resyncInterval !== 0) {\n clearInterval(this._resyncInterval);\n }\n this.disconnect();\n if (typeof window !== \"undefined\") {\n window.removeEventListener(\"unload\", this._unloadHandler);\n } else if (\n typeof process !== \"undefined\" &&\n typeof process.off === \"function\"\n ) {\n process.off(\"exit\", this._unloadHandler);\n }\n this.awareness.off(\"change\", this._awarenessUpdateHandler);\n this.doc.off(\"update\", this._updateHandler);\n super.destroy();\n }\n\n connectBc() {\n if (this.disableBc) {\n return;\n }\n if (!this.bcconnected) {\n bc.subscribe(this.bcChannel, this._bcSubscriber);\n this.bcconnected = true;\n }\n // send sync step1 to bc\n // write sync step 1\n const encoderSync = encoding.createEncoder();\n encoding.writeVarUint(encoderSync, messageSync);\n syncProtocol.writeSyncStep1(encoderSync, this.doc);\n bc.publish(this.bcChannel, encoding.toUint8Array(encoderSync), this);\n // broadcast local state\n const encoderState = encoding.createEncoder();\n encoding.writeVarUint(encoderState, messageSync);\n syncProtocol.writeSyncStep2(encoderState, this.doc);\n bc.publish(this.bcChannel, encoding.toUint8Array(encoderState), this);\n // write queryAwareness\n const encoderAwarenessQuery = encoding.createEncoder();\n encoding.writeVarUint(encoderAwarenessQuery, messageQueryAwareness);\n bc.publish(\n this.bcChannel,\n encoding.toUint8Array(encoderAwarenessQuery),\n this\n );\n // broadcast local awareness state\n const encoderAwarenessState = encoding.createEncoder();\n encoding.writeVarUint(encoderAwarenessState, messageAwareness);\n encoding.writeVarUint8Array(\n encoderAwarenessState,\n awarenessProtocol.encodeAwarenessUpdate(this.awareness, [\n this.doc.clientID\n ])\n );\n bc.publish(\n this.bcChannel,\n encoding.toUint8Array(encoderAwarenessState),\n this\n );\n }\n\n disconnectBc() {\n // broadcast message with local awareness state set to null (indicating disconnect)\n const encoder = encoding.createEncoder();\n encoding.writeVarUint(encoder, messageAwareness);\n encoding.writeVarUint8Array(\n encoder,\n awarenessProtocol.encodeAwarenessUpdate(\n this.awareness,\n [this.doc.clientID],\n new Map()\n )\n );\n broadcastMessage(this, encoding.toUint8Array(encoder));\n if (this.bcconnected) {\n bc.unsubscribe(this.bcChannel, this._bcSubscriber);\n this.bcconnected = false;\n }\n }\n\n disconnect() {\n this.shouldConnect = false;\n this.disconnectBc();\n if (this.ws !== null) {\n this.ws.close();\n }\n }\n\n connect() {\n this.shouldConnect = true;\n if (!this.wsconnected && this.ws === null) {\n setupWS(this);\n this.connectBc();\n }\n }\n}\n\nfunction assertType(value: unknown, label: string, type: string) {\n if (typeof value !== type) {\n throw new Error(\n `Invalid \"${label}\" parameter provided to YProvider. Expected: ${type}, received: ${value as string}`\n );\n }\n}\n\ntype Params = Record<string, string | null | undefined>;\ntype ParamsProvider = Params | (() => Params | Promise<Params>);\ntype BaseProviderOptions = ConstructorParameters<typeof WebsocketProvider>[3];\n\ntype YProviderOptions = Omit<NonNullable<BaseProviderOptions>, \"params\"> & {\n connectionId?: string;\n party?: string;\n prefix?: string;\n params?: ParamsProvider;\n protocol?: \"ws\" | \"wss\";\n};\n\nexport default class YProvider extends WebsocketProvider {\n id: string;\n #params?: ParamsProvider;\n\n constructor(\n host: string,\n room: string,\n doc?: YDoc,\n options: YProviderOptions = {}\n ) {\n assertType(host, \"host\", \"string\");\n assertType(room, \"room\", \"string\");\n\n // strip the protocol from the beginning of `host` if any\n host = host.replace(/^(http|https|ws|wss):\\/\\//, \"\");\n\n // strip trailing slash from host if any\n if (host.endsWith(\"/\")) {\n host = host.slice(0, -1);\n }\n\n const serverUrl = `${\n options.protocol ||\n (host.startsWith(\"localhost:\") ||\n host.startsWith(\"127.0.0.1:\") ||\n host.startsWith(\"192.168.\") ||\n host.startsWith(\"10.\") ||\n (host.startsWith(\"172.\") &&\n host.split(\".\")[1] >= \"16\" &&\n host.split(\".\")[1] <= \"31\")\n ? \"ws\"\n : \"wss\")\n }://${host}${options.prefix || `/parties/${options.party || \"main\"}`}`;\n\n // use provided id, or generate a random one\n const id = options.connectionId ?? nanoid(10);\n\n // don't pass params to WebsocketProvider, we override them in connect()\n const { params, connect = true, ...rest } = options;\n\n // don't connect until we've updated the url parameters\n const baseOptions = {\n ...rest,\n isPrefixedUrl: !!options.prefix,\n connect: false\n };\n\n super(serverUrl, room, doc ?? new YDoc(), baseOptions);\n\n this.id = id;\n this.#params = params;\n\n if (connect) {\n void this.connect();\n }\n }\n\n async connect() {\n try {\n // get updated url parameters\n const nextParams =\n typeof this.#params === \"function\"\n ? await this.#params()\n : this.#params;\n // override current url parameters before connecting\n const urlParams = new URLSearchParams([[\"_pk\", this.id]]);\n if (nextParams) {\n for (const [key, value] of Object.entries(nextParams)) {\n // filter out null/undefined values\n if (value !== null && value !== undefined) {\n urlParams.append(key, value);\n }\n }\n }\n\n const nextUrl = new URL(this.url);\n nextUrl.search = urlParams.toString();\n this.url = nextUrl.toString();\n\n // finally, connect\n super.connect();\n } catch (err) {\n console.error(\"Failed to open connecton to PartyServer\", err);\n throw err;\n }\n }\n\n sendMessage(message: string) {\n this.ws?.send(`__YPS:${message}`);\n }\n}\n"],"mappings":";;;;;;;;;;;;;;AAaA,MAAa,cAAc;AAC3B,MAAa,wBAAwB;AACrC,MAAa,mBAAmB;AAChC,MAAa,cAAc;AAG3B,MAAM,qBAAqB,OAAO,WAAW;AAE7C,MAAM,kBAQF,EAAE;AAEN,gBAAgB,gBACd,SACA,SACA,UACA,YACA,iBACG;AACH,UAAS,aAAa,SAAS,YAAY;CAC3C,MAAM,kBAAkB,aAAa,gBACnC,SACA,SACA,SAAS,KACT,SACD;AACD,KACE,cACA,oBAAoB,aAAa,uBACjC,CAAC,SAAS,OAEV,UAAS,SAAS;;AAItB,gBAAgB,0BACd,SACA,UACA,UACA,aACA,iBACG;AACH,UAAS,aAAa,SAAS,iBAAiB;AAChD,UAAS,mBACP,SACA,kBAAkB,sBAChB,SAAS,WACT,MAAM,KAAK,SAAS,UAAU,WAAW,CAAC,MAAM,CAAC,CAClD,CACF;;AAGH,gBAAgB,qBACd,UACA,SACA,UACA,aACA,iBACG;AACH,mBAAkB,qBAChB,SAAS,WACT,SAAS,kBAAkB,QAAQ,EACnC,SACD;;AAGH,gBAAgB,gBACd,UACA,SACA,UACA,aACA,iBACG;AACH,cAAa,gBAAgB,SAAS,SAAS,MAAM,OAAO,WAC1D,wBAAwB,UAAU,OAAO,CAC1C;;AAGH,SAAS,wBAAwB,UAA6B,QAAgB;AAC5E,SAAQ,KAAK,+BAA+B,SAAS,IAAI,KAAK,SAAS;;AAGzE,SAAS,YACP,UACA,KACA,YACkB;CAClB,MAAM,UAAU,SAAS,cAAc,IAAI;CAC3C,MAAM,UAAU,SAAS,eAAe;CACxC,MAAM,cAAc,SAAS,YAAY,QAAQ;CACjD,MAAM,iBAAiB,SAAS,gBAAgB;AAChD,KAAuB,eACrB,gBAAe,SAAS,SAAS,UAAU,YAAY,YAAY;KAEnE,SAAQ,MAAM,4BAA4B;AAE5C,QAAO;;AAGT,SAAS,QAAQ,UAA6B;AAC5C,KAAI,SAAS,iBAAiB,SAAS,OAAO,MAAM;AAClD,MAAI,CAAC,SAAS,IACZ,OAAM,IAAI,MACR,2FACD;EAEH,MAAM,YAAY,IAAI,SAAS,IAAI,SAAS,IAAI;AAChD,YAAU,aAAa;AACvB,WAAS,KAAK;AACd,WAAS,eAAe;AACxB,WAAS,cAAc;AACvB,WAAS,SAAS;AAElB,YAAU,iBAAiB,YAAY,UAAU;AAC/C,OAAI,OAAO,MAAM,SAAS,UAAU;AAElC,QAAI,MAAM,KAAK,WAAW,SAAS,EAAE;KACnC,MAAM,gBAAgB,MAAM,KAAK,MAAM,EAAE;AACzC,cAAS,KAAK,kBAAkB,CAAC,cAAc,CAAC;;AAElD;;AAEF,YAAS,wBAAwB,KAAK,aAAa;GACnD,MAAM,UAAU,YAAY,UAAU,IAAI,WAAW,MAAM,KAAK,EAAE,KAAK;AACvE,OAAI,SAAS,OAAO,QAAQ,GAAG,EAC7B,WAAU,KAAK,SAAS,aAAa,QAAQ,CAAC;IAEhD;AACF,YAAU,iBAAiB,UAAU,UAAU;AAC7C,YAAS,KAAK,oBAAoB,CAAC,OAAO,SAAS,CAAC;IACpD;AACF,YAAU,iBAAiB,UAAU,UAAU;AAC7C,YAAS,KAAK,oBAAoB,CAAC,OAAO,SAAS,CAAC;AACpD,YAAS,KAAK;AACd,YAAS,eAAe;AACxB,OAAI,SAAS,aAAa;AACxB,aAAS,cAAc;AACvB,aAAS,SAAS;IAElB,MAAM,iBAAiB,MAAM,KAC3B,SAAS,UAAU,WAAW,CAAC,MAAM,CACtC,CAAC,QAAQ,WAAW,WAAW,SAAS,IAAI,SAAS;AACtD,sBAAkB,sBAChB,SAAS,WACT,gBACA,SACD;AAGD,SAAK,MAAM,YAAY,eACrB,UAAS,UAAU,KAAK,OAAO,SAAS;AAE1C,aAAS,KAAK,UAAU,CACtB,EACE,QAAQ,gBACT,CACF,CAAC;SAEF,UAAS;AAIX,cACE,SACA,KAAK,IACH,KAAK,IAAI,GAAG,SAAS,yBAAyB,GAAG,KACjD,SAAS,eACV,EACD,SACD;IACD;AACF,YAAU,iBAAiB,cAAc;AACvC,YAAS,wBAAwB,KAAK,aAAa;AACnD,YAAS,eAAe;AACxB,YAAS,cAAc;AACvB,YAAS,2BAA2B;AACpC,YAAS,KAAK,UAAU,CACtB,EACE,QAAQ,aACT,CACF,CAAC;GAEF,MAAM,UAAU,SAAS,eAAe;AACxC,YAAS,aAAa,SAAS,YAAY;AAC3C,gBAAa,eAAe,SAAS,SAAS,IAAI;AAClD,aAAU,KAAK,SAAS,aAAa,QAAQ,CAAC;AAE9C,OAAI,SAAS,UAAU,eAAe,KAAK,MAAM;AAG/C,aAAS,UAAU,cAAc,SAAS,UAAU,eAAe,CAAC;IACpE,MAAM,wBAAwB,SAAS,eAAe;AACtD,aAAS,aAAa,uBAAuB,iBAAiB;AAC9D,aAAS,mBACP,uBACA,kBAAkB,sBAAsB,SAAS,WAAW,CAC1D,SAAS,IAAI,SACd,CAAC,CACH;AACD,cAAU,KAAK,SAAS,aAAa,sBAAsB,CAAC;;IAE9D;AACF,WAAS,KAAK,UAAU,CACtB,EACE,QAAQ,cACT,CACF,CAAC;;;AAIN,SAAS,iBAAiB,UAA6B,KAAiB;CACtE,MAAM,KAAK,SAAS;AACpB,KAAI,SAAS,eAAe,MAAM,GAAG,eAAe,GAAG,KACrD,IAAG,KAAK,IAAI;AAEd,KAAI,SAAS,YACX,IAAG,QAAQ,SAAS,WAAW,KAAK,SAAS;;AAUjD,MAAM,mBAAmB,OAAO,cAAc,cAAc,OAAO;;;;;;;;;;;;;;AAenE,IAAa,oBAAb,cAAuC,WAAmB;CACxD;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CAEA,YACE,WACA,UACA,KACA,EACE,UAAU,MACV,YAAY,IAAI,kBAAkB,UAAU,IAAI,EAChD,SAAS,EAAE,EACX,gBAAgB,OAChB,oBAAoB,kBACpB,iBAAiB,IACjB,iBAAiB,MACjB,YAAY,uBAUV,EAAE,EACN;AACA,SAAO;AAEP,SAAO,UAAU,UAAU,SAAS,OAAO,IACzC,aAAY,UAAU,MAAM,GAAG,UAAU,SAAS,EAAE;EAEtD,MAAM,gBAAgB,IAAI,kBAAkB,OAAO;AACnD,OAAK,iBAAiB;AACtB,OAAK,YAAY,GAAG,UAAU,GAAG;AACjC,OAAK,MAAM,gBACP,YACA,GAAG,UAAU,GAAG,WAAW,cAAc,WAAW,IAAI,KAAK,IAAI;AACrE,OAAK,WAAW;AAChB,OAAK,MAAM;AACX,OAAK,MAAM;AACX,OAAK,YAAY;AACjB,OAAK,cAAc;AACnB,OAAK,eAAe;AACpB,OAAK,cAAc;AACnB,OAAK,YAAY;AACjB,OAAK,2BAA2B;AAChC,OAAK,kBAAkB,gBAAgB,OAAO;AAE9C,OAAK,UAAU;AAEf,OAAK,KAAK;AACV,OAAK,wBAAwB;AAE7B,OAAK,gBAAgB;AAErB,OAAK,kBAAkB;AACvB,MAAI,iBAAiB,EACnB,MAAK,kBAAqC,kBAAkB;AAC1D,OAAI,KAAK,MAAM,KAAK,GAAG,eAAe,UAAU,MAAM;IAEpD,MAAM,UAAU,SAAS,eAAe;AACxC,aAAS,aAAa,SAAS,YAAY;AAC3C,iBAAa,eAAe,SAAS,IAAI;AACzC,SAAK,GAAG,KAAK,SAAS,aAAa,QAAQ,CAAC;;KAE7C,eAAe;AAGpB,OAAK,iBAAiB,MAAkB,WAAoB;AAC1D,OAAI,WAAW,MAAM;IACnB,MAAM,UAAU,YAAY,MAAM,IAAI,WAAW,KAAK,EAAE,MAAM;AAC9D,QAAI,SAAS,OAAO,QAAQ,GAAG,EAC7B,IAAG,QAAQ,KAAK,WAAW,SAAS,aAAa,QAAQ,EAAE,KAAK;;;;;;AAOtE,OAAK,kBAAkB,QAAoB,WAAoB;AAC7D,OAAI,WAAW,MAAM;IACnB,MAAM,UAAU,SAAS,eAAe;AACxC,aAAS,aAAa,SAAS,YAAY;AAC3C,iBAAa,YAAY,SAAS,OAAO;AACzC,qBAAiB,MAAM,SAAS,aAAa,QAAQ,CAAC;;;AAG1D,OAAK,IAAI,GAAG,UAAU,KAAK,eAAe;AAE1C,OAAK,2BACH,EAAE,OAAO,SAAS,WAClB,YACG;GACH,MAAM,iBAAiB,MAAM,OAAO,QAAQ,CAAC,OAAO,QAAQ;GAC5D,MAAM,UAAU,SAAS,eAAe;AACxC,YAAS,aAAa,SAAS,iBAAiB;AAChD,YAAS,mBACP,SACA,kBAAkB,sBAAsB,WAAW,eAAe,CACnE;AACD,oBAAiB,MAAM,SAAS,aAAa,QAAQ,CAAC;;AAExD,OAAK,uBAAuB;AAC1B,qBAAkB,sBAChB,KAAK,WACL,CAAC,IAAI,SAAS,EACd,gBACD;;AAEH,MAAI,OAAO,WAAW,YACpB,QAAO,iBAAiB,UAAU,KAAK,eAAe;WAEtD,OAAO,YAAY,eACnB,OAAO,QAAQ,OAAO,WAEtB,SAAQ,GAAG,QAAQ,KAAK,eAAe;AAMzC,YAAU,GAAG,UAAU,KAAK,wBAAwB;AAMpD,gBAEI,UAGA,eACH;AACD,MAAI,QACF,MAAK,SAAS;;;;;CAOlB,IAAI,SAAS;AACX,SAAO,KAAK;;CAGd,IAAI,OAAO,OAAO;AAChB,MAAI,KAAK,YAAY,OAAO;AAC1B,QAAK,UAAU;AACf,QAAK,KAAK,UAAU,CAAC,MAAM,CAAC;AAC5B,QAAK,KAAK,QAAQ,CAAC,MAAM,CAAC;;;CAI9B,UAAU;AACR,MAAI,KAAK,oBAAoB,EAC3B,eAAc,KAAK,gBAAgB;AAErC,OAAK,YAAY;AACjB,MAAI,OAAO,WAAW,YACpB,QAAO,oBAAoB,UAAU,KAAK,eAAe;WAEzD,OAAO,YAAY,eACnB,OAAO,QAAQ,QAAQ,WAEvB,SAAQ,IAAI,QAAQ,KAAK,eAAe;AAE1C,OAAK,UAAU,IAAI,UAAU,KAAK,wBAAwB;AAC1D,OAAK,IAAI,IAAI,UAAU,KAAK,eAAe;AAC3C,QAAM,SAAS;;CAGjB,YAAY;AACV,MAAI,KAAK,UACP;AAEF,MAAI,CAAC,KAAK,aAAa;AACrB,MAAG,UAAU,KAAK,WAAW,KAAK,cAAc;AAChD,QAAK,cAAc;;EAIrB,MAAM,cAAc,SAAS,eAAe;AAC5C,WAAS,aAAa,aAAa,YAAY;AAC/C,eAAa,eAAe,aAAa,KAAK,IAAI;AAClD,KAAG,QAAQ,KAAK,WAAW,SAAS,aAAa,YAAY,EAAE,KAAK;EAEpE,MAAM,eAAe,SAAS,eAAe;AAC7C,WAAS,aAAa,cAAc,YAAY;AAChD,eAAa,eAAe,cAAc,KAAK,IAAI;AACnD,KAAG,QAAQ,KAAK,WAAW,SAAS,aAAa,aAAa,EAAE,KAAK;EAErE,MAAM,wBAAwB,SAAS,eAAe;AACtD,WAAS,aAAa,uBAAuB,sBAAsB;AACnE,KAAG,QACD,KAAK,WACL,SAAS,aAAa,sBAAsB,EAC5C,KACD;EAED,MAAM,wBAAwB,SAAS,eAAe;AACtD,WAAS,aAAa,uBAAuB,iBAAiB;AAC9D,WAAS,mBACP,uBACA,kBAAkB,sBAAsB,KAAK,WAAW,CACtD,KAAK,IAAI,SACV,CAAC,CACH;AACD,KAAG,QACD,KAAK,WACL,SAAS,aAAa,sBAAsB,EAC5C,KACD;;CAGH,eAAe;EAEb,MAAM,UAAU,SAAS,eAAe;AACxC,WAAS,aAAa,SAAS,iBAAiB;AAChD,WAAS,mBACP,SACA,kBAAkB,sBAChB,KAAK,WACL,CAAC,KAAK,IAAI,SAAS,kBACnB,IAAI,KAAK,CACV,CACF;AACD,mBAAiB,MAAM,SAAS,aAAa,QAAQ,CAAC;AACtD,MAAI,KAAK,aAAa;AACpB,MAAG,YAAY,KAAK,WAAW,KAAK,cAAc;AAClD,QAAK,cAAc;;;CAIvB,aAAa;AACX,OAAK,gBAAgB;AACrB,OAAK,cAAc;AACnB,MAAI,KAAK,OAAO,KACd,MAAK,GAAG,OAAO;;CAInB,UAAU;AACR,OAAK,gBAAgB;AACrB,MAAI,CAAC,KAAK,eAAe,KAAK,OAAO,MAAM;AACzC,WAAQ,KAAK;AACb,QAAK,WAAW;;;;AAKtB,SAAS,WAAW,OAAgB,OAAe,MAAc;AAC/D,KAAI,OAAO,UAAU,KACnB,OAAM,IAAI,MACR,YAAY,MAAM,+CAA+C,KAAK,cAAc,QACrF;;AAgBL,IAAqB,YAArB,cAAuC,kBAAkB;CACvD;CACA;CAEA,YACE,MACA,MACA,KACA,UAA4B,EAAE,EAC9B;AACA,aAAW,MAAM,QAAQ,SAAS;AAClC,aAAW,MAAM,QAAQ,SAAS;AAGlC,SAAO,KAAK,QAAQ,6BAA6B,GAAG;AAGpD,MAAI,KAAK,SAAS,IAAI,CACpB,QAAO,KAAK,MAAM,GAAG,GAAG;EAG1B,MAAM,YAAY,GAChB,QAAQ,aACP,KAAK,WAAW,aAAa,IAC9B,KAAK,WAAW,aAAa,IAC7B,KAAK,WAAW,WAAW,IAC3B,KAAK,WAAW,MAAM,IACrB,KAAK,WAAW,OAAO,IACtB,KAAK,MAAM,IAAI,CAAC,MAAM,QACtB,KAAK,MAAM,IAAI,CAAC,MAAM,OACpB,OACA,OACL,KAAK,OAAO,QAAQ,UAAU,YAAY,QAAQ,SAAS;EAG5D,MAAM,KAAK,QAAQ,gBAAgB,OAAO,GAAG;EAG7C,MAAM,EAAE,QAAQ,UAAU,MAAM,GAAG,SAAS;EAG5C,MAAM,cAAc;GAClB,GAAG;GACH,eAAe,CAAC,CAAC,QAAQ;GACzB,SAAS;GACV;AAED,QAAM,WAAW,MAAM,OAAO,IAAIA,KAAM,EAAE,YAAY;AAEtD,OAAK,KAAK;AACV,QAAKC,SAAU;AAEf,MAAI,QACF,CAAK,KAAK,SAAS;;CAIvB,MAAM,UAAU;AACd,MAAI;GAEF,MAAM,aACJ,OAAO,MAAKA,WAAY,aACpB,MAAM,MAAKA,QAAS,GACpB,MAAKA;GAEX,MAAM,YAAY,IAAI,gBAAgB,CAAC,CAAC,OAAO,KAAK,GAAG,CAAC,CAAC;AACzD,OAAI,YACF;SAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,WAAW,CAEnD,KAAI,UAAU,QAAQ,UAAU,OAC9B,WAAU,OAAO,KAAK,MAAM;;GAKlC,MAAM,UAAU,IAAI,IAAI,KAAK,IAAI;AACjC,WAAQ,SAAS,UAAU,UAAU;AACrC,QAAK,MAAM,QAAQ,UAAU;AAG7B,SAAM,SAAS;WACR,KAAK;AACZ,WAAQ,MAAM,2CAA2C,IAAI;AAC7D,SAAM;;;CAIV,YAAY,SAAiB;AAC3B,OAAK,IAAI,KAAK,SAAS,UAAU"}
|
package/dist/server/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
+
import { Connection, ConnectionContext, Server, WSMessage } from "partyserver";
|
|
1
2
|
import * as awarenessProtocol from "y-protocols/awareness";
|
|
2
3
|
import { Doc } from "yjs";
|
|
3
|
-
import { Connection, ConnectionContext, Server, WSMessage } from "partyserver";
|
|
4
4
|
|
|
5
5
|
//#region src/server/index.d.ts
|
|
6
6
|
type YjsRootType =
|
|
@@ -11,7 +11,6 @@ type YjsRootType =
|
|
|
11
11
|
| "XmlElement"
|
|
12
12
|
| "XmlFragment";
|
|
13
13
|
declare class WSSharedDoc extends Doc {
|
|
14
|
-
conns: Map<Connection, Set<number>>;
|
|
15
14
|
awareness: awarenessProtocol.Awareness;
|
|
16
15
|
constructor();
|
|
17
16
|
}
|
|
@@ -26,7 +25,7 @@ declare class YServer<
|
|
|
26
25
|
#private;
|
|
27
26
|
static callbackOptions: CallbackOptions;
|
|
28
27
|
readonly document: WSSharedDoc;
|
|
29
|
-
onLoad(): Promise<void>;
|
|
28
|
+
onLoad(): Promise<Doc | void>;
|
|
30
29
|
onSave(): Promise<void>;
|
|
31
30
|
/**
|
|
32
31
|
* Replaces the document with a different state using Yjs UndoManager key remapping.
|
package/dist/server/index.js
CHANGED
|
@@ -1,548 +1,45 @@
|
|
|
1
1
|
import * as decoding from "lib0/decoding";
|
|
2
2
|
import * as encoding from "lib0/encoding";
|
|
3
3
|
import debounce from "lodash.debounce";
|
|
4
|
-
import {
|
|
5
|
-
import { nanoid } from "nanoid";
|
|
4
|
+
import { Server } from "partyserver";
|
|
6
5
|
import * as awarenessProtocol from "y-protocols/awareness";
|
|
7
6
|
import * as syncProtocol from "y-protocols/sync";
|
|
8
7
|
import { Doc, UndoManager, XmlElement, XmlFragment, XmlText, applyUpdate, encodeStateAsUpdate, encodeStateVector } from "yjs";
|
|
9
8
|
|
|
10
|
-
//#region ../partyserver/dist/index.js
|
|
11
|
-
if (!("OPEN" in WebSocket)) {
|
|
12
|
-
const WebSocketStatus = {
|
|
13
|
-
CONNECTING: WebSocket.READY_STATE_CONNECTING,
|
|
14
|
-
OPEN: WebSocket.READY_STATE_OPEN,
|
|
15
|
-
CLOSING: WebSocket.READY_STATE_CLOSING,
|
|
16
|
-
CLOSED: WebSocket.READY_STATE_CLOSED
|
|
17
|
-
};
|
|
18
|
-
Object.assign(WebSocket, WebSocketStatus);
|
|
19
|
-
Object.assign(WebSocket.prototype, WebSocketStatus);
|
|
20
|
-
}
|
|
21
|
-
function tryGetPartyServerMeta(ws) {
|
|
22
|
-
try {
|
|
23
|
-
const attachment = WebSocket.prototype.deserializeAttachment.call(ws);
|
|
24
|
-
if (!attachment || typeof attachment !== "object") return null;
|
|
25
|
-
if (!("__pk" in attachment)) return null;
|
|
26
|
-
const pk = attachment.__pk;
|
|
27
|
-
if (!pk || typeof pk !== "object") return null;
|
|
28
|
-
const { id, server, tags } = pk;
|
|
29
|
-
if (typeof id !== "string" || typeof server !== "string") return null;
|
|
30
|
-
return {
|
|
31
|
-
id,
|
|
32
|
-
server,
|
|
33
|
-
tags: Array.isArray(tags) ? tags : []
|
|
34
|
-
};
|
|
35
|
-
} catch {
|
|
36
|
-
return null;
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
function isPartyServerWebSocket(ws) {
|
|
40
|
-
return tryGetPartyServerMeta(ws) !== null;
|
|
41
|
-
}
|
|
42
|
-
/**
|
|
43
|
-
* Cache websocket attachments to avoid having to rehydrate them on every property access.
|
|
44
|
-
*/
|
|
45
|
-
var AttachmentCache = class {
|
|
46
|
-
#cache = /* @__PURE__ */ new WeakMap();
|
|
47
|
-
get(ws) {
|
|
48
|
-
let attachment = this.#cache.get(ws);
|
|
49
|
-
if (!attachment) {
|
|
50
|
-
attachment = WebSocket.prototype.deserializeAttachment.call(ws);
|
|
51
|
-
if (attachment !== void 0) this.#cache.set(ws, attachment);
|
|
52
|
-
else throw new Error("Missing websocket attachment. This is most likely an issue in PartyServer, please open an issue at https://github.com/cloudflare/partykit/issues");
|
|
53
|
-
}
|
|
54
|
-
return attachment;
|
|
55
|
-
}
|
|
56
|
-
set(ws, attachment) {
|
|
57
|
-
this.#cache.set(ws, attachment);
|
|
58
|
-
WebSocket.prototype.serializeAttachment.call(ws, attachment);
|
|
59
|
-
}
|
|
60
|
-
};
|
|
61
|
-
const attachments = new AttachmentCache();
|
|
62
|
-
const connections = /* @__PURE__ */ new WeakSet();
|
|
63
|
-
const isWrapped = (ws) => {
|
|
64
|
-
return connections.has(ws);
|
|
65
|
-
};
|
|
66
|
-
/**
|
|
67
|
-
* Wraps a WebSocket with Connection fields that rehydrate the
|
|
68
|
-
* socket attachments lazily only when requested.
|
|
69
|
-
*/
|
|
70
|
-
const createLazyConnection = (ws) => {
|
|
71
|
-
if (isWrapped(ws)) return ws;
|
|
72
|
-
let initialState;
|
|
73
|
-
if ("state" in ws) {
|
|
74
|
-
initialState = ws.state;
|
|
75
|
-
delete ws.state;
|
|
76
|
-
}
|
|
77
|
-
const connection = Object.defineProperties(ws, {
|
|
78
|
-
id: { get() {
|
|
79
|
-
return attachments.get(ws).__pk.id;
|
|
80
|
-
} },
|
|
81
|
-
server: { get() {
|
|
82
|
-
return attachments.get(ws).__pk.server;
|
|
83
|
-
} },
|
|
84
|
-
tags: { get() {
|
|
85
|
-
return attachments.get(ws).__pk.tags ?? [];
|
|
86
|
-
} },
|
|
87
|
-
socket: { get() {
|
|
88
|
-
return ws;
|
|
89
|
-
} },
|
|
90
|
-
state: {
|
|
91
|
-
configurable: true,
|
|
92
|
-
get() {
|
|
93
|
-
return ws.deserializeAttachment();
|
|
94
|
-
}
|
|
95
|
-
},
|
|
96
|
-
setState: {
|
|
97
|
-
configurable: true,
|
|
98
|
-
value: function setState(setState$1) {
|
|
99
|
-
let state;
|
|
100
|
-
if (setState$1 instanceof Function) state = setState$1(this.state);
|
|
101
|
-
else state = setState$1;
|
|
102
|
-
ws.serializeAttachment(state);
|
|
103
|
-
return state;
|
|
104
|
-
}
|
|
105
|
-
},
|
|
106
|
-
deserializeAttachment: {
|
|
107
|
-
configurable: true,
|
|
108
|
-
value: function deserializeAttachment() {
|
|
109
|
-
return attachments.get(ws).__user ?? null;
|
|
110
|
-
}
|
|
111
|
-
},
|
|
112
|
-
serializeAttachment: {
|
|
113
|
-
configurable: true,
|
|
114
|
-
value: function serializeAttachment(attachment) {
|
|
115
|
-
const setting = {
|
|
116
|
-
...attachments.get(ws),
|
|
117
|
-
__user: attachment ?? null
|
|
118
|
-
};
|
|
119
|
-
attachments.set(ws, setting);
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
});
|
|
123
|
-
if (initialState) connection.setState(initialState);
|
|
124
|
-
connections.add(connection);
|
|
125
|
-
return connection;
|
|
126
|
-
};
|
|
127
|
-
var HibernatingConnectionIterator = class {
|
|
128
|
-
index = 0;
|
|
129
|
-
sockets;
|
|
130
|
-
constructor(state, tag) {
|
|
131
|
-
this.state = state;
|
|
132
|
-
this.tag = tag;
|
|
133
|
-
}
|
|
134
|
-
[Symbol.iterator]() {
|
|
135
|
-
return this;
|
|
136
|
-
}
|
|
137
|
-
next() {
|
|
138
|
-
const sockets = this.sockets ?? (this.sockets = this.state.getWebSockets(this.tag));
|
|
139
|
-
let socket;
|
|
140
|
-
while (socket = sockets[this.index++]) if (socket.readyState === WebSocket.READY_STATE_OPEN) {
|
|
141
|
-
if (!isPartyServerWebSocket(socket)) continue;
|
|
142
|
-
return {
|
|
143
|
-
done: false,
|
|
144
|
-
value: createLazyConnection(socket)
|
|
145
|
-
};
|
|
146
|
-
}
|
|
147
|
-
return {
|
|
148
|
-
done: true,
|
|
149
|
-
value: void 0
|
|
150
|
-
};
|
|
151
|
-
}
|
|
152
|
-
};
|
|
153
|
-
/**
|
|
154
|
-
* Deduplicate and validate connection tags.
|
|
155
|
-
* Returns the final tag array (always includes the connection id as the first tag).
|
|
156
|
-
*/
|
|
157
|
-
function prepareTags(connectionId, userTags) {
|
|
158
|
-
const tags = [connectionId, ...userTags.filter((t) => t !== connectionId)];
|
|
159
|
-
if (tags.length > 10) throw new Error("A connection can only have 10 tags, including the default id tag.");
|
|
160
|
-
for (const tag of tags) {
|
|
161
|
-
if (typeof tag !== "string") throw new Error(`A connection tag must be a string. Received: ${tag}`);
|
|
162
|
-
if (tag === "") throw new Error("A connection tag must not be an empty string.");
|
|
163
|
-
if (tag.length > 256) throw new Error("A connection tag must not exceed 256 characters");
|
|
164
|
-
}
|
|
165
|
-
return tags;
|
|
166
|
-
}
|
|
167
|
-
/**
|
|
168
|
-
* When not using hibernation, we track active connections manually.
|
|
169
|
-
*/
|
|
170
|
-
var InMemoryConnectionManager = class {
|
|
171
|
-
#connections = /* @__PURE__ */ new Map();
|
|
172
|
-
tags = /* @__PURE__ */ new WeakMap();
|
|
173
|
-
getCount() {
|
|
174
|
-
return this.#connections.size;
|
|
175
|
-
}
|
|
176
|
-
getConnection(id) {
|
|
177
|
-
return this.#connections.get(id);
|
|
178
|
-
}
|
|
179
|
-
*getConnections(tag) {
|
|
180
|
-
if (!tag) {
|
|
181
|
-
yield* this.#connections.values().filter((c) => c.readyState === WebSocket.READY_STATE_OPEN);
|
|
182
|
-
return;
|
|
183
|
-
}
|
|
184
|
-
for (const connection of this.#connections.values()) if ((this.tags.get(connection) ?? []).includes(tag)) yield connection;
|
|
185
|
-
}
|
|
186
|
-
accept(connection, options) {
|
|
187
|
-
connection.accept();
|
|
188
|
-
const tags = prepareTags(connection.id, options.tags);
|
|
189
|
-
this.#connections.set(connection.id, connection);
|
|
190
|
-
this.tags.set(connection, tags);
|
|
191
|
-
Object.defineProperty(connection, "tags", {
|
|
192
|
-
get: () => tags,
|
|
193
|
-
configurable: true
|
|
194
|
-
});
|
|
195
|
-
const removeConnection = () => {
|
|
196
|
-
this.#connections.delete(connection.id);
|
|
197
|
-
connection.removeEventListener("close", removeConnection);
|
|
198
|
-
connection.removeEventListener("error", removeConnection);
|
|
199
|
-
};
|
|
200
|
-
connection.addEventListener("close", removeConnection);
|
|
201
|
-
connection.addEventListener("error", removeConnection);
|
|
202
|
-
return connection;
|
|
203
|
-
}
|
|
204
|
-
};
|
|
205
|
-
/**
|
|
206
|
-
* When opting into hibernation, the platform tracks connections for us.
|
|
207
|
-
*/
|
|
208
|
-
var HibernatingConnectionManager = class {
|
|
209
|
-
constructor(controller) {
|
|
210
|
-
this.controller = controller;
|
|
211
|
-
}
|
|
212
|
-
getCount() {
|
|
213
|
-
let count = 0;
|
|
214
|
-
for (const ws of this.controller.getWebSockets()) if (isPartyServerWebSocket(ws)) count++;
|
|
215
|
-
return count;
|
|
216
|
-
}
|
|
217
|
-
getConnection(id) {
|
|
218
|
-
const matching = this.controller.getWebSockets(id).filter((ws) => {
|
|
219
|
-
return tryGetPartyServerMeta(ws)?.id === id;
|
|
220
|
-
});
|
|
221
|
-
if (matching.length === 0) return void 0;
|
|
222
|
-
if (matching.length === 1) return createLazyConnection(matching[0]);
|
|
223
|
-
throw new Error(`More than one connection found for id ${id}. Did you mean to use getConnections(tag) instead?`);
|
|
224
|
-
}
|
|
225
|
-
getConnections(tag) {
|
|
226
|
-
return new HibernatingConnectionIterator(this.controller, tag);
|
|
227
|
-
}
|
|
228
|
-
accept(connection, options) {
|
|
229
|
-
const tags = prepareTags(connection.id, options.tags);
|
|
230
|
-
this.controller.acceptWebSocket(connection, tags);
|
|
231
|
-
connection.serializeAttachment({
|
|
232
|
-
__pk: {
|
|
233
|
-
id: connection.id,
|
|
234
|
-
server: options.server,
|
|
235
|
-
tags
|
|
236
|
-
},
|
|
237
|
-
__user: null
|
|
238
|
-
});
|
|
239
|
-
return createLazyConnection(connection);
|
|
240
|
-
}
|
|
241
|
-
};
|
|
242
|
-
var Server = class extends DurableObject {
|
|
243
|
-
static options = { hibernate: false };
|
|
244
|
-
#status = "zero";
|
|
245
|
-
#ParentClass = Object.getPrototypeOf(this).constructor;
|
|
246
|
-
#connectionManager = this.#ParentClass.options.hibernate ? new HibernatingConnectionManager(this.ctx) : new InMemoryConnectionManager();
|
|
247
|
-
/**
|
|
248
|
-
* Execute SQL queries against the Server's database
|
|
249
|
-
* @template T Type of the returned rows
|
|
250
|
-
* @param strings SQL query template strings
|
|
251
|
-
* @param values Values to be inserted into the query
|
|
252
|
-
* @returns Array of query results
|
|
253
|
-
*/
|
|
254
|
-
sql(strings, ...values) {
|
|
255
|
-
let query = "";
|
|
256
|
-
try {
|
|
257
|
-
query = strings.reduce((acc, str, i) => acc + str + (i < values.length ? "?" : ""), "");
|
|
258
|
-
return [...this.ctx.storage.sql.exec(query, ...values)];
|
|
259
|
-
} catch (e) {
|
|
260
|
-
console.error(`failed to execute sql query: ${query}`, e);
|
|
261
|
-
throw this.onException(e);
|
|
262
|
-
}
|
|
263
|
-
}
|
|
264
|
-
constructor(ctx, env$1) {
|
|
265
|
-
super(ctx, env$1);
|
|
266
|
-
}
|
|
267
|
-
/**
|
|
268
|
-
* Handle incoming requests to the server.
|
|
269
|
-
*/
|
|
270
|
-
async fetch(request) {
|
|
271
|
-
try {
|
|
272
|
-
const props = request.headers.get("x-partykit-props");
|
|
273
|
-
if (props) this.#_props = JSON.parse(props);
|
|
274
|
-
if (!this.#_name) {
|
|
275
|
-
const room = request.headers.get("x-partykit-room");
|
|
276
|
-
if (!room) throw new Error(`Missing namespace or room headers when connecting to ${this.#ParentClass.name}.
|
|
277
|
-
Did you try connecting directly to this Durable Object? Try using getServerByName(namespace, id) instead.`);
|
|
278
|
-
await this.setName(room);
|
|
279
|
-
} else if (this.#status !== "started") await this.#initialize();
|
|
280
|
-
const url = new URL(request.url);
|
|
281
|
-
if (url.pathname === "/cdn-cgi/partyserver/set-name/") return Response.json({ ok: true });
|
|
282
|
-
if (request.headers.get("Upgrade")?.toLowerCase() !== "websocket") return await this.onRequest(request);
|
|
283
|
-
else {
|
|
284
|
-
const { 0: clientWebSocket, 1: serverWebSocket } = new WebSocketPair();
|
|
285
|
-
let connectionId = url.searchParams.get("_pk");
|
|
286
|
-
if (!connectionId) connectionId = nanoid();
|
|
287
|
-
let connection = Object.assign(serverWebSocket, {
|
|
288
|
-
id: connectionId,
|
|
289
|
-
server: this.name,
|
|
290
|
-
tags: [],
|
|
291
|
-
state: null,
|
|
292
|
-
setState(setState) {
|
|
293
|
-
let state;
|
|
294
|
-
if (setState instanceof Function) state = setState(this.state);
|
|
295
|
-
else state = setState;
|
|
296
|
-
this.state = state;
|
|
297
|
-
return this.state;
|
|
298
|
-
}
|
|
299
|
-
});
|
|
300
|
-
const ctx = { request };
|
|
301
|
-
const tags = await this.getConnectionTags(connection, ctx);
|
|
302
|
-
connection = this.#connectionManager.accept(connection, {
|
|
303
|
-
tags,
|
|
304
|
-
server: this.name
|
|
305
|
-
});
|
|
306
|
-
if (!this.#ParentClass.options.hibernate) this.#attachSocketEventHandlers(connection);
|
|
307
|
-
await this.onConnect(connection, ctx);
|
|
308
|
-
return new Response(null, {
|
|
309
|
-
status: 101,
|
|
310
|
-
webSocket: clientWebSocket
|
|
311
|
-
});
|
|
312
|
-
}
|
|
313
|
-
} catch (err) {
|
|
314
|
-
console.error(`Error in ${this.#ParentClass.name}:${this.#_name ?? "<unnamed>"} fetch:`, err);
|
|
315
|
-
if (!(err instanceof Error)) throw err;
|
|
316
|
-
if (request.headers.get("Upgrade") === "websocket") {
|
|
317
|
-
const pair = new WebSocketPair();
|
|
318
|
-
pair[1].accept();
|
|
319
|
-
pair[1].send(JSON.stringify({ error: err.stack }));
|
|
320
|
-
pair[1].close(1011, "Uncaught exception during session setup");
|
|
321
|
-
return new Response(null, {
|
|
322
|
-
status: 101,
|
|
323
|
-
webSocket: pair[0]
|
|
324
|
-
});
|
|
325
|
-
} else return new Response(err.stack, { status: 500 });
|
|
326
|
-
}
|
|
327
|
-
}
|
|
328
|
-
async webSocketMessage(ws, message) {
|
|
329
|
-
if (!isPartyServerWebSocket(ws)) return;
|
|
330
|
-
try {
|
|
331
|
-
const connection = createLazyConnection(ws);
|
|
332
|
-
await this.setName(connection.server);
|
|
333
|
-
return this.onMessage(connection, message);
|
|
334
|
-
} catch (e) {
|
|
335
|
-
console.error(`Error in ${this.#ParentClass.name}:${this.#_name ?? "<unnamed>"} webSocketMessage:`, e);
|
|
336
|
-
}
|
|
337
|
-
}
|
|
338
|
-
async webSocketClose(ws, code, reason, wasClean) {
|
|
339
|
-
if (!isPartyServerWebSocket(ws)) return;
|
|
340
|
-
try {
|
|
341
|
-
const connection = createLazyConnection(ws);
|
|
342
|
-
await this.setName(connection.server);
|
|
343
|
-
return this.onClose(connection, code, reason, wasClean);
|
|
344
|
-
} catch (e) {
|
|
345
|
-
console.error(`Error in ${this.#ParentClass.name}:${this.#_name ?? "<unnamed>"} webSocketClose:`, e);
|
|
346
|
-
}
|
|
347
|
-
}
|
|
348
|
-
async webSocketError(ws, error) {
|
|
349
|
-
if (!isPartyServerWebSocket(ws)) return;
|
|
350
|
-
try {
|
|
351
|
-
const connection = createLazyConnection(ws);
|
|
352
|
-
await this.setName(connection.server);
|
|
353
|
-
return this.onError(connection, error);
|
|
354
|
-
} catch (e) {
|
|
355
|
-
console.error(`Error in ${this.#ParentClass.name}:${this.#_name ?? "<unnamed>"} webSocketError:`, e);
|
|
356
|
-
}
|
|
357
|
-
}
|
|
358
|
-
async #initialize() {
|
|
359
|
-
let error;
|
|
360
|
-
await this.ctx.blockConcurrencyWhile(async () => {
|
|
361
|
-
this.#status = "starting";
|
|
362
|
-
try {
|
|
363
|
-
await this.onStart(this.#_props);
|
|
364
|
-
this.#status = "started";
|
|
365
|
-
} catch (e) {
|
|
366
|
-
this.#status = "zero";
|
|
367
|
-
error = e;
|
|
368
|
-
}
|
|
369
|
-
});
|
|
370
|
-
if (error) throw error;
|
|
371
|
-
}
|
|
372
|
-
#attachSocketEventHandlers(connection) {
|
|
373
|
-
const handleMessageFromClient = (event) => {
|
|
374
|
-
this.onMessage(connection, event.data)?.catch((e) => {
|
|
375
|
-
console.error("onMessage error:", e);
|
|
376
|
-
});
|
|
377
|
-
};
|
|
378
|
-
const handleCloseFromClient = (event) => {
|
|
379
|
-
connection.removeEventListener("message", handleMessageFromClient);
|
|
380
|
-
connection.removeEventListener("close", handleCloseFromClient);
|
|
381
|
-
this.onClose(connection, event.code, event.reason, event.wasClean)?.catch((e) => {
|
|
382
|
-
console.error("onClose error:", e);
|
|
383
|
-
});
|
|
384
|
-
};
|
|
385
|
-
const handleErrorFromClient = (e) => {
|
|
386
|
-
connection.removeEventListener("message", handleMessageFromClient);
|
|
387
|
-
connection.removeEventListener("error", handleErrorFromClient);
|
|
388
|
-
this.onError(connection, e.error)?.catch((e$1) => {
|
|
389
|
-
console.error("onError error:", e$1);
|
|
390
|
-
});
|
|
391
|
-
};
|
|
392
|
-
connection.addEventListener("close", handleCloseFromClient);
|
|
393
|
-
connection.addEventListener("error", handleErrorFromClient);
|
|
394
|
-
connection.addEventListener("message", handleMessageFromClient);
|
|
395
|
-
}
|
|
396
|
-
#_name;
|
|
397
|
-
#_longErrorAboutNameThrown = false;
|
|
398
|
-
/**
|
|
399
|
-
* The name for this server. Write-once-only.
|
|
400
|
-
*/
|
|
401
|
-
get name() {
|
|
402
|
-
if (!this.#_name) if (!this.#_longErrorAboutNameThrown) {
|
|
403
|
-
this.#_longErrorAboutNameThrown = true;
|
|
404
|
-
throw new Error(`Attempting to read .name on ${this.#ParentClass.name} before it was set. The name can be set by explicitly calling .setName(name) on the stub, or by using routePartyKitRequest(). This is a known issue and will be fixed soon. Follow https://github.com/cloudflare/workerd/issues/2240 for more updates.`);
|
|
405
|
-
} else throw new Error(`Attempting to read .name on ${this.#ParentClass.name} before it was set.`);
|
|
406
|
-
return this.#_name;
|
|
407
|
-
}
|
|
408
|
-
async setName(name) {
|
|
409
|
-
if (!name) throw new Error("A name is required.");
|
|
410
|
-
if (this.#_name && this.#_name !== name) throw new Error(`This server already has a name: ${this.#_name}, attempting to set to: ${name}`);
|
|
411
|
-
this.#_name = name;
|
|
412
|
-
if (this.#status !== "started") await this.#initialize();
|
|
413
|
-
}
|
|
414
|
-
#sendMessageToConnection(connection, message) {
|
|
415
|
-
try {
|
|
416
|
-
connection.send(message);
|
|
417
|
-
} catch (_e) {
|
|
418
|
-
connection.close(1011, "Unexpected error");
|
|
419
|
-
}
|
|
420
|
-
}
|
|
421
|
-
/** Send a message to all connected clients, except connection ids listed in `without` */
|
|
422
|
-
broadcast(msg, without) {
|
|
423
|
-
for (const connection of this.#connectionManager.getConnections()) if (!without || !without.includes(connection.id)) this.#sendMessageToConnection(connection, msg);
|
|
424
|
-
}
|
|
425
|
-
/** Get a connection by connection id */
|
|
426
|
-
getConnection(id) {
|
|
427
|
-
return this.#connectionManager.getConnection(id);
|
|
428
|
-
}
|
|
429
|
-
/**
|
|
430
|
-
* Get all connections. Optionally, you can provide a tag to filter returned connections.
|
|
431
|
-
* Use `Server#getConnectionTags` to tag the connection on connect.
|
|
432
|
-
*/
|
|
433
|
-
getConnections(tag) {
|
|
434
|
-
return this.#connectionManager.getConnections(tag);
|
|
435
|
-
}
|
|
436
|
-
/**
|
|
437
|
-
* You can tag a connection to filter them in Server#getConnections.
|
|
438
|
-
* Each connection supports up to 9 tags, each tag max length is 256 characters.
|
|
439
|
-
*/
|
|
440
|
-
getConnectionTags(connection, context) {
|
|
441
|
-
return [];
|
|
442
|
-
}
|
|
443
|
-
#_props;
|
|
444
|
-
/**
|
|
445
|
-
* Called when the server is started for the first time.
|
|
446
|
-
*/
|
|
447
|
-
onStart(props) {}
|
|
448
|
-
/**
|
|
449
|
-
* Called when a new connection is made to the server.
|
|
450
|
-
*/
|
|
451
|
-
onConnect(connection, ctx) {
|
|
452
|
-
console.log(`Connection ${connection.id} connected to ${this.#ParentClass.name}:${this.name}`);
|
|
453
|
-
}
|
|
454
|
-
/**
|
|
455
|
-
* Called when a message is received from a connection.
|
|
456
|
-
*/
|
|
457
|
-
onMessage(connection, message) {
|
|
458
|
-
console.log(`Received message on connection ${this.#ParentClass.name}:${connection.id}`);
|
|
459
|
-
console.info(`Implement onMessage on ${this.#ParentClass.name} to handle this message.`);
|
|
460
|
-
}
|
|
461
|
-
/**
|
|
462
|
-
* Called when a connection is closed.
|
|
463
|
-
*/
|
|
464
|
-
onClose(connection, code, reason, wasClean) {}
|
|
465
|
-
/**
|
|
466
|
-
* Called when an error occurs on a connection.
|
|
467
|
-
*/
|
|
468
|
-
onError(connection, error) {
|
|
469
|
-
console.error(`Error on connection ${connection.id} in ${this.#ParentClass.name}:${this.name}:`, error);
|
|
470
|
-
console.info(`Implement onError on ${this.#ParentClass.name} to handle this error.`);
|
|
471
|
-
}
|
|
472
|
-
/**
|
|
473
|
-
* Called when a request is made to the server.
|
|
474
|
-
*/
|
|
475
|
-
onRequest(request) {
|
|
476
|
-
console.warn(`onRequest hasn't been implemented on ${this.#ParentClass.name}:${this.name} responding to ${request.url}`);
|
|
477
|
-
return new Response("Not implemented", { status: 404 });
|
|
478
|
-
}
|
|
479
|
-
/**
|
|
480
|
-
* Called when an exception occurs.
|
|
481
|
-
* @param error - The error that occurred.
|
|
482
|
-
*/
|
|
483
|
-
onException(error) {
|
|
484
|
-
console.error(`Exception in ${this.#ParentClass.name}:${this.name}:`, error);
|
|
485
|
-
console.info(`Implement onException on ${this.#ParentClass.name} to handle this error.`);
|
|
486
|
-
}
|
|
487
|
-
onAlarm() {
|
|
488
|
-
console.log(`Implement onAlarm on ${this.#ParentClass.name} to handle alarms.`);
|
|
489
|
-
}
|
|
490
|
-
async alarm() {
|
|
491
|
-
if (this.#status !== "started") await this.#initialize();
|
|
492
|
-
await this.onAlarm();
|
|
493
|
-
}
|
|
494
|
-
};
|
|
495
|
-
|
|
496
|
-
//#endregion
|
|
497
9
|
//#region src/server/index.ts
|
|
498
10
|
const snapshotOrigin = Symbol("snapshot-origin");
|
|
499
11
|
const wsReadyStateConnecting = 0;
|
|
500
12
|
const wsReadyStateOpen = 1;
|
|
501
13
|
const messageSync = 0;
|
|
502
14
|
const messageAwareness = 1;
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
15
|
+
/**
|
|
16
|
+
* Internal key used in connection.setState() to track which awareness
|
|
17
|
+
* client IDs are controlled by each connection. This survives hibernation
|
|
18
|
+
* because connection state is persisted to WebSocket attachments.
|
|
19
|
+
*/
|
|
20
|
+
const AWARENESS_IDS_KEY = "__ypsAwarenessIds";
|
|
21
|
+
function getAwarenessIds(conn) {
|
|
22
|
+
try {
|
|
23
|
+
return conn.state?.[AWARENESS_IDS_KEY] ?? [];
|
|
24
|
+
} catch {
|
|
25
|
+
return [];
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
function setAwarenessIds(conn, ids) {
|
|
29
|
+
try {
|
|
30
|
+
conn.setState((prev) => ({
|
|
31
|
+
...prev,
|
|
32
|
+
[AWARENESS_IDS_KEY]: ids
|
|
33
|
+
}));
|
|
34
|
+
} catch {}
|
|
511
35
|
}
|
|
512
36
|
var WSSharedDoc = class extends Doc {
|
|
513
|
-
conns;
|
|
514
37
|
awareness;
|
|
515
38
|
constructor() {
|
|
516
39
|
super({ gc: true });
|
|
517
|
-
/**
|
|
518
|
-
* Maps from conn to set of controlled user ids. Delete all user ids from awareness when this conn is closed
|
|
519
|
-
*/
|
|
520
|
-
this.conns = /* @__PURE__ */ new Map();
|
|
521
40
|
this.awareness = new awarenessProtocol.Awareness(this);
|
|
522
41
|
this.awareness.setLocalState(null);
|
|
523
|
-
|
|
524
|
-
const changedClients = added.concat(updated, removed);
|
|
525
|
-
if (conn !== null) {
|
|
526
|
-
const connControlledIDs = this.conns.get(conn);
|
|
527
|
-
if (connControlledIDs !== void 0) {
|
|
528
|
-
added.forEach((clientID) => {
|
|
529
|
-
connControlledIDs.add(clientID);
|
|
530
|
-
});
|
|
531
|
-
removed.forEach((clientID) => {
|
|
532
|
-
connControlledIDs.delete(clientID);
|
|
533
|
-
});
|
|
534
|
-
}
|
|
535
|
-
}
|
|
536
|
-
const encoder = encoding.createEncoder();
|
|
537
|
-
encoding.writeVarUint(encoder, messageAwareness);
|
|
538
|
-
encoding.writeVarUint8Array(encoder, awarenessProtocol.encodeAwarenessUpdate(this.awareness, changedClients));
|
|
539
|
-
const buff = encoding.toUint8Array(encoder);
|
|
540
|
-
this.conns.forEach((_, c) => {
|
|
541
|
-
send(this, c, buff);
|
|
542
|
-
});
|
|
543
|
-
};
|
|
544
|
-
this.awareness.on("update", awarenessChangeHandler);
|
|
545
|
-
this.on("update", updateHandler);
|
|
42
|
+
clearInterval(this.awareness._checkInterval);
|
|
546
43
|
}
|
|
547
44
|
};
|
|
548
45
|
const CALLBACK_DEFAULTS = {
|
|
@@ -566,25 +63,11 @@ function readSyncMessage(decoder, encoder, doc, transactionOrigin, readOnly = fa
|
|
|
566
63
|
}
|
|
567
64
|
return messageType;
|
|
568
65
|
}
|
|
569
|
-
function
|
|
570
|
-
if (
|
|
571
|
-
const controlledIds = doc.conns.get(conn);
|
|
572
|
-
doc.conns.delete(conn);
|
|
573
|
-
awarenessProtocol.removeAwarenessStates(doc.awareness, Array.from(controlledIds), null);
|
|
574
|
-
}
|
|
575
|
-
try {
|
|
576
|
-
conn.close();
|
|
577
|
-
} catch (e) {
|
|
578
|
-
console.warn("failed to close connection", e);
|
|
579
|
-
}
|
|
580
|
-
}
|
|
581
|
-
function send(doc, conn, m) {
|
|
582
|
-
if (conn.readyState !== void 0 && conn.readyState !== wsReadyStateConnecting && conn.readyState !== wsReadyStateOpen) closeConn(doc, conn);
|
|
66
|
+
function send(conn, m) {
|
|
67
|
+
if (conn.readyState !== void 0 && conn.readyState !== wsReadyStateConnecting && conn.readyState !== wsReadyStateOpen) return;
|
|
583
68
|
try {
|
|
584
69
|
conn.send(m);
|
|
585
|
-
} catch
|
|
586
|
-
closeConn(doc, conn);
|
|
587
|
-
}
|
|
70
|
+
} catch {}
|
|
588
71
|
}
|
|
589
72
|
var YServer = class extends Server {
|
|
590
73
|
static callbackOptions = {};
|
|
@@ -629,6 +112,29 @@ var YServer = class extends Server {
|
|
|
629
112
|
const state = encodeStateAsUpdate(src);
|
|
630
113
|
applyUpdate(this.document, state);
|
|
631
114
|
}
|
|
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
|
+
});
|
|
632
138
|
this.document.on("update", debounce((_update, _origin, _doc) => {
|
|
633
139
|
try {
|
|
634
140
|
this.onSave().catch((err) => {
|
|
@@ -638,6 +144,11 @@ var YServer = class extends Server {
|
|
|
638
144
|
console.error("failed to persist:", err);
|
|
639
145
|
}
|
|
640
146
|
}, this.#ParentClass.callbackOptions.debounceWait || CALLBACK_DEFAULTS.debounceWait, { maxWait: this.#ParentClass.callbackOptions.debounceMaxWait || CALLBACK_DEFAULTS.debounceMaxWait }));
|
|
147
|
+
const syncEncoder = encoding.createEncoder();
|
|
148
|
+
encoding.writeVarUint(syncEncoder, messageSync);
|
|
149
|
+
syncProtocol.writeSyncStep1(syncEncoder, this.document);
|
|
150
|
+
const syncMessage = encoding.toUint8Array(syncEncoder);
|
|
151
|
+
for (const conn of this.getConnections()) send(conn, syncMessage);
|
|
641
152
|
}
|
|
642
153
|
isReadOnly(connection) {
|
|
643
154
|
return false;
|
|
@@ -671,15 +182,15 @@ var YServer = class extends Server {
|
|
|
671
182
|
*/
|
|
672
183
|
broadcastCustomMessage(message, excludeConnection) {
|
|
673
184
|
const formattedMessage = `__YPS:${message}`;
|
|
674
|
-
this.
|
|
675
|
-
if (excludeConnection && conn === excludeConnection)
|
|
676
|
-
if (conn.readyState !== void 0 && conn.readyState !== wsReadyStateConnecting && conn.readyState !== wsReadyStateOpen)
|
|
185
|
+
for (const conn of this.getConnections()) {
|
|
186
|
+
if (excludeConnection && conn === excludeConnection) continue;
|
|
187
|
+
if (conn.readyState !== void 0 && conn.readyState !== wsReadyStateConnecting && conn.readyState !== wsReadyStateOpen) continue;
|
|
677
188
|
try {
|
|
678
189
|
conn.send(formattedMessage);
|
|
679
190
|
} catch (e) {
|
|
680
191
|
console.warn("Failed to broadcast custom message", e);
|
|
681
192
|
}
|
|
682
|
-
}
|
|
193
|
+
}
|
|
683
194
|
}
|
|
684
195
|
handleMessage(connection, message) {
|
|
685
196
|
if (typeof message === "string") {
|
|
@@ -699,11 +210,18 @@ var YServer = class extends Server {
|
|
|
699
210
|
case messageSync:
|
|
700
211
|
encoding.writeVarUint(encoder, messageSync);
|
|
701
212
|
readSyncMessage(decoder, encoder, this.document, connection, this.isReadOnly(connection));
|
|
702
|
-
if (encoding.length(encoder) > 1) send(
|
|
213
|
+
if (encoding.length(encoder) > 1) send(connection, encoding.toUint8Array(encoder));
|
|
703
214
|
break;
|
|
704
|
-
case messageAwareness:
|
|
705
|
-
|
|
215
|
+
case messageAwareness: {
|
|
216
|
+
const awarenessData = decoding.readVarUint8Array(decoder);
|
|
217
|
+
awarenessProtocol.applyAwarenessUpdate(this.document.awareness, awarenessData, connection);
|
|
218
|
+
const awarenessEncoder = encoding.createEncoder();
|
|
219
|
+
encoding.writeVarUint(awarenessEncoder, messageAwareness);
|
|
220
|
+
encoding.writeVarUint8Array(awarenessEncoder, awarenessData);
|
|
221
|
+
const awarenessBuff = encoding.toUint8Array(awarenessEncoder);
|
|
222
|
+
for (const c of this.getConnections()) send(c, awarenessBuff);
|
|
706
223
|
break;
|
|
224
|
+
}
|
|
707
225
|
}
|
|
708
226
|
} catch (err) {
|
|
709
227
|
console.error(err);
|
|
@@ -714,20 +232,20 @@ var YServer = class extends Server {
|
|
|
714
232
|
this.handleMessage(conn, message);
|
|
715
233
|
}
|
|
716
234
|
onClose(connection, _code, _reason, _wasClean) {
|
|
717
|
-
|
|
235
|
+
const controlledIds = getAwarenessIds(connection);
|
|
236
|
+
if (controlledIds.length > 0) awarenessProtocol.removeAwarenessStates(this.document.awareness, controlledIds, null);
|
|
718
237
|
}
|
|
719
238
|
onConnect(conn, _ctx) {
|
|
720
|
-
this.document.conns.set(conn, /* @__PURE__ */ new Set());
|
|
721
239
|
const encoder = encoding.createEncoder();
|
|
722
240
|
encoding.writeVarUint(encoder, messageSync);
|
|
723
241
|
syncProtocol.writeSyncStep1(encoder, this.document);
|
|
724
|
-
send(
|
|
242
|
+
send(conn, encoding.toUint8Array(encoder));
|
|
725
243
|
const awarenessStates = this.document.awareness.getStates();
|
|
726
244
|
if (awarenessStates.size > 0) {
|
|
727
|
-
const encoder
|
|
728
|
-
encoding.writeVarUint(encoder
|
|
729
|
-
encoding.writeVarUint8Array(encoder
|
|
730
|
-
send(
|
|
245
|
+
const encoder = encoding.createEncoder();
|
|
246
|
+
encoding.writeVarUint(encoder, messageAwareness);
|
|
247
|
+
encoding.writeVarUint8Array(encoder, awarenessProtocol.encodeAwarenessUpdate(this.document.awareness, Array.from(awarenessStates.keys())));
|
|
248
|
+
send(conn, encoding.toUint8Array(encoder));
|
|
731
249
|
}
|
|
732
250
|
}
|
|
733
251
|
};
|
package/dist/server/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":["#cache","#connections","#ParentClass","#_props","#_name","#status","#initialize","#connectionManager","#attachSocketEventHandlers","#_longErrorAboutNameThrown","#sendMessageToConnection","YDoc","controlledIds: Set<number>","#ParentClass","encoder"],"sources":["../../../partyserver/dist/index.js","../../src/server/index.ts"],"sourcesContent":["import { DurableObject, env } from \"cloudflare:workers\";\nimport { nanoid } from \"nanoid\";\n\n//#region src/connection.ts\nif (!(\"OPEN\" in WebSocket)) {\n\tconst WebSocketStatus = {\n\t\tCONNECTING: WebSocket.READY_STATE_CONNECTING,\n\t\tOPEN: WebSocket.READY_STATE_OPEN,\n\t\tCLOSING: WebSocket.READY_STATE_CLOSING,\n\t\tCLOSED: WebSocket.READY_STATE_CLOSED\n\t};\n\tObject.assign(WebSocket, WebSocketStatus);\n\tObject.assign(WebSocket.prototype, WebSocketStatus);\n}\nfunction tryGetPartyServerMeta(ws) {\n\ttry {\n\t\tconst attachment = WebSocket.prototype.deserializeAttachment.call(ws);\n\t\tif (!attachment || typeof attachment !== \"object\") return null;\n\t\tif (!(\"__pk\" in attachment)) return null;\n\t\tconst pk = attachment.__pk;\n\t\tif (!pk || typeof pk !== \"object\") return null;\n\t\tconst { id, server, tags } = pk;\n\t\tif (typeof id !== \"string\" || typeof server !== \"string\") return null;\n\t\treturn {\n\t\t\tid,\n\t\t\tserver,\n\t\t\ttags: Array.isArray(tags) ? tags : []\n\t\t};\n\t} catch {\n\t\treturn null;\n\t}\n}\nfunction isPartyServerWebSocket(ws) {\n\treturn tryGetPartyServerMeta(ws) !== null;\n}\n/**\n* Cache websocket attachments to avoid having to rehydrate them on every property access.\n*/\nvar AttachmentCache = class {\n\t#cache = /* @__PURE__ */ new WeakMap();\n\tget(ws) {\n\t\tlet attachment = this.#cache.get(ws);\n\t\tif (!attachment) {\n\t\t\tattachment = WebSocket.prototype.deserializeAttachment.call(ws);\n\t\t\tif (attachment !== void 0) this.#cache.set(ws, attachment);\n\t\t\telse throw new Error(\"Missing websocket attachment. This is most likely an issue in PartyServer, please open an issue at https://github.com/cloudflare/partykit/issues\");\n\t\t}\n\t\treturn attachment;\n\t}\n\tset(ws, attachment) {\n\t\tthis.#cache.set(ws, attachment);\n\t\tWebSocket.prototype.serializeAttachment.call(ws, attachment);\n\t}\n};\nconst attachments = new AttachmentCache();\nconst connections = /* @__PURE__ */ new WeakSet();\nconst isWrapped = (ws) => {\n\treturn connections.has(ws);\n};\n/**\n* Wraps a WebSocket with Connection fields that rehydrate the\n* socket attachments lazily only when requested.\n*/\nconst createLazyConnection = (ws) => {\n\tif (isWrapped(ws)) return ws;\n\tlet initialState;\n\tif (\"state\" in ws) {\n\t\tinitialState = ws.state;\n\t\tdelete ws.state;\n\t}\n\tconst connection = Object.defineProperties(ws, {\n\t\tid: { get() {\n\t\t\treturn attachments.get(ws).__pk.id;\n\t\t} },\n\t\tserver: { get() {\n\t\t\treturn attachments.get(ws).__pk.server;\n\t\t} },\n\t\ttags: { get() {\n\t\t\treturn attachments.get(ws).__pk.tags ?? [];\n\t\t} },\n\t\tsocket: { get() {\n\t\t\treturn ws;\n\t\t} },\n\t\tstate: {\n\t\t\tconfigurable: true,\n\t\t\tget() {\n\t\t\t\treturn ws.deserializeAttachment();\n\t\t\t}\n\t\t},\n\t\tsetState: {\n\t\t\tconfigurable: true,\n\t\t\tvalue: function setState(setState$1) {\n\t\t\t\tlet state;\n\t\t\t\tif (setState$1 instanceof Function) state = setState$1(this.state);\n\t\t\t\telse state = setState$1;\n\t\t\t\tws.serializeAttachment(state);\n\t\t\t\treturn state;\n\t\t\t}\n\t\t},\n\t\tdeserializeAttachment: {\n\t\t\tconfigurable: true,\n\t\t\tvalue: function deserializeAttachment() {\n\t\t\t\treturn attachments.get(ws).__user ?? null;\n\t\t\t}\n\t\t},\n\t\tserializeAttachment: {\n\t\t\tconfigurable: true,\n\t\t\tvalue: function serializeAttachment(attachment) {\n\t\t\t\tconst setting = {\n\t\t\t\t\t...attachments.get(ws),\n\t\t\t\t\t__user: attachment ?? null\n\t\t\t\t};\n\t\t\t\tattachments.set(ws, setting);\n\t\t\t}\n\t\t}\n\t});\n\tif (initialState) connection.setState(initialState);\n\tconnections.add(connection);\n\treturn connection;\n};\nvar HibernatingConnectionIterator = class {\n\tindex = 0;\n\tsockets;\n\tconstructor(state, tag) {\n\t\tthis.state = state;\n\t\tthis.tag = tag;\n\t}\n\t[Symbol.iterator]() {\n\t\treturn this;\n\t}\n\tnext() {\n\t\tconst sockets = this.sockets ?? (this.sockets = this.state.getWebSockets(this.tag));\n\t\tlet socket;\n\t\twhile (socket = sockets[this.index++]) if (socket.readyState === WebSocket.READY_STATE_OPEN) {\n\t\t\tif (!isPartyServerWebSocket(socket)) continue;\n\t\t\treturn {\n\t\t\t\tdone: false,\n\t\t\t\tvalue: createLazyConnection(socket)\n\t\t\t};\n\t\t}\n\t\treturn {\n\t\t\tdone: true,\n\t\t\tvalue: void 0\n\t\t};\n\t}\n};\n/**\n* Deduplicate and validate connection tags.\n* Returns the final tag array (always includes the connection id as the first tag).\n*/\nfunction prepareTags(connectionId, userTags) {\n\tconst tags = [connectionId, ...userTags.filter((t) => t !== connectionId)];\n\tif (tags.length > 10) throw new Error(\"A connection can only have 10 tags, including the default id tag.\");\n\tfor (const tag of tags) {\n\t\tif (typeof tag !== \"string\") throw new Error(`A connection tag must be a string. Received: ${tag}`);\n\t\tif (tag === \"\") throw new Error(\"A connection tag must not be an empty string.\");\n\t\tif (tag.length > 256) throw new Error(\"A connection tag must not exceed 256 characters\");\n\t}\n\treturn tags;\n}\n/**\n* When not using hibernation, we track active connections manually.\n*/\nvar InMemoryConnectionManager = class {\n\t#connections = /* @__PURE__ */ new Map();\n\ttags = /* @__PURE__ */ new WeakMap();\n\tgetCount() {\n\t\treturn this.#connections.size;\n\t}\n\tgetConnection(id) {\n\t\treturn this.#connections.get(id);\n\t}\n\t*getConnections(tag) {\n\t\tif (!tag) {\n\t\t\tyield* this.#connections.values().filter((c) => c.readyState === WebSocket.READY_STATE_OPEN);\n\t\t\treturn;\n\t\t}\n\t\tfor (const connection of this.#connections.values()) if ((this.tags.get(connection) ?? []).includes(tag)) yield connection;\n\t}\n\taccept(connection, options) {\n\t\tconnection.accept();\n\t\tconst tags = prepareTags(connection.id, options.tags);\n\t\tthis.#connections.set(connection.id, connection);\n\t\tthis.tags.set(connection, tags);\n\t\tObject.defineProperty(connection, \"tags\", {\n\t\t\tget: () => tags,\n\t\t\tconfigurable: true\n\t\t});\n\t\tconst removeConnection = () => {\n\t\t\tthis.#connections.delete(connection.id);\n\t\t\tconnection.removeEventListener(\"close\", removeConnection);\n\t\t\tconnection.removeEventListener(\"error\", removeConnection);\n\t\t};\n\t\tconnection.addEventListener(\"close\", removeConnection);\n\t\tconnection.addEventListener(\"error\", removeConnection);\n\t\treturn connection;\n\t}\n};\n/**\n* When opting into hibernation, the platform tracks connections for us.\n*/\nvar HibernatingConnectionManager = class {\n\tconstructor(controller) {\n\t\tthis.controller = controller;\n\t}\n\tgetCount() {\n\t\tlet count = 0;\n\t\tfor (const ws of this.controller.getWebSockets()) if (isPartyServerWebSocket(ws)) count++;\n\t\treturn count;\n\t}\n\tgetConnection(id) {\n\t\tconst matching = this.controller.getWebSockets(id).filter((ws) => {\n\t\t\treturn tryGetPartyServerMeta(ws)?.id === id;\n\t\t});\n\t\tif (matching.length === 0) return void 0;\n\t\tif (matching.length === 1) return createLazyConnection(matching[0]);\n\t\tthrow new Error(`More than one connection found for id ${id}. Did you mean to use getConnections(tag) instead?`);\n\t}\n\tgetConnections(tag) {\n\t\treturn new HibernatingConnectionIterator(this.controller, tag);\n\t}\n\taccept(connection, options) {\n\t\tconst tags = prepareTags(connection.id, options.tags);\n\t\tthis.controller.acceptWebSocket(connection, tags);\n\t\tconnection.serializeAttachment({\n\t\t\t__pk: {\n\t\t\t\tid: connection.id,\n\t\t\t\tserver: options.server,\n\t\t\t\ttags\n\t\t\t},\n\t\t\t__user: null\n\t\t});\n\t\treturn createLazyConnection(connection);\n\t}\n};\n\n//#endregion\n//#region src/index.ts\nconst serverMapCache = /* @__PURE__ */ new WeakMap();\nconst bindingNameCache = /* @__PURE__ */ new WeakMap();\n/**\n* For a given server namespace, create a server with a name.\n*/\nasync function getServerByName(serverNamespace, name, options) {\n\tif (options?.jurisdiction) serverNamespace = serverNamespace.jurisdiction(options.jurisdiction);\n\tconst id = serverNamespace.idFromName(name);\n\tconst stub = serverNamespace.get(id, options);\n\tconst req = new Request(\"http://dummy-example.cloudflare.com/cdn-cgi/partyserver/set-name/\");\n\treq.headers.set(\"x-partykit-room\", name);\n\tif (options?.props) req.headers.set(\"x-partykit-props\", JSON.stringify(options?.props));\n\tawait stub.fetch(req).then((res) => res.text());\n\treturn stub;\n}\nfunction camelCaseToKebabCase(str) {\n\tif (str === str.toUpperCase() && str !== str.toLowerCase()) return str.toLowerCase().replace(/_/g, \"-\");\n\tlet kebabified = str.replace(/[A-Z]/g, (letter) => `-${letter.toLowerCase()}`);\n\tkebabified = kebabified.startsWith(\"-\") ? kebabified.slice(1) : kebabified;\n\treturn kebabified.replace(/_/g, \"-\").replace(/-$/, \"\");\n}\n/**\n* Resolve CORS options into a concrete headers object (or null if CORS is disabled).\n*/\nfunction resolveCorsHeaders(cors) {\n\tif (cors === true) return {\n\t\t\"Access-Control-Allow-Origin\": \"*\",\n\t\t\"Access-Control-Allow-Methods\": \"GET, POST, HEAD, OPTIONS\",\n\t\t\"Access-Control-Allow-Headers\": \"*\",\n\t\t\"Access-Control-Max-Age\": \"86400\"\n\t};\n\tif (cors && typeof cors === \"object\") {\n\t\tconst h = new Headers(cors);\n\t\tconst record = {};\n\t\th.forEach((value, key) => {\n\t\t\trecord[key] = value;\n\t\t});\n\t\treturn record;\n\t}\n\treturn null;\n}\nasync function routePartykitRequest(req, env$1 = env, options) {\n\tif (!serverMapCache.has(env$1)) {\n\t\tconst namespaceMap = {};\n\t\tconst bindingNames$1 = {};\n\t\tfor (const [k, v] of Object.entries(env$1)) if (v && typeof v === \"object\" && \"idFromName\" in v && typeof v.idFromName === \"function\") {\n\t\t\tconst kebab = camelCaseToKebabCase(k);\n\t\t\tnamespaceMap[kebab] = v;\n\t\t\tbindingNames$1[kebab] = k;\n\t\t}\n\t\tserverMapCache.set(env$1, namespaceMap);\n\t\tbindingNameCache.set(env$1, bindingNames$1);\n\t}\n\tconst map = serverMapCache.get(env$1);\n\tconst bindingNames = bindingNameCache.get(env$1);\n\tconst prefixParts = (options?.prefix || \"parties\").split(\"/\");\n\tconst parts = new URL(req.url).pathname.split(\"/\").filter(Boolean);\n\tif (!prefixParts.every((part, index) => parts[index] === part) || parts.length < prefixParts.length + 2) return null;\n\tconst namespace = parts[prefixParts.length];\n\tconst name = parts[prefixParts.length + 1];\n\tif (name && namespace) {\n\t\tif (!map[namespace]) {\n\t\t\tif (namespace === \"main\") {\n\t\t\t\tconsole.warn(\"You appear to be migrating a PartyKit project to PartyServer.\");\n\t\t\t\tconsole.warn(`PartyServer doesn't have a \"main\" party by default. Try adding this to your PartySocket client:\\n \nparty: \"${camelCaseToKebabCase(Object.keys(map)[0])}\"`);\n\t\t\t} else console.error(`The url ${req.url} with namespace \"${namespace}\" and name \"${name}\" does not match any server namespace. \nDid you forget to add a durable object binding to the class ${namespace[0].toUpperCase() + namespace.slice(1)} in your wrangler.jsonc?`);\n\t\t\treturn new Response(\"Invalid request\", { status: 400 });\n\t\t}\n\t\tconst corsHeaders = resolveCorsHeaders(options?.cors);\n\t\tconst isWebSocket = req.headers.get(\"Upgrade\")?.toLowerCase() === \"websocket\";\n\t\tfunction withCorsHeaders(response) {\n\t\t\tif (!corsHeaders || isWebSocket) return response;\n\t\t\tconst newResponse = new Response(response.body, response);\n\t\t\tfor (const [key, value] of Object.entries(corsHeaders)) newResponse.headers.set(key, value);\n\t\t\treturn newResponse;\n\t\t}\n\t\tif (req.method === \"OPTIONS\" && corsHeaders) return new Response(null, { headers: corsHeaders });\n\t\tlet doNamespace = map[namespace];\n\t\tif (options?.jurisdiction) doNamespace = doNamespace.jurisdiction(options.jurisdiction);\n\t\tconst id = doNamespace.idFromName(name);\n\t\tconst stub = doNamespace.get(id, options);\n\t\treq = new Request(req);\n\t\treq.headers.set(\"x-partykit-room\", name);\n\t\treq.headers.set(\"x-partykit-namespace\", namespace);\n\t\tif (options?.jurisdiction) req.headers.set(\"x-partykit-jurisdiction\", options.jurisdiction);\n\t\tif (options?.props) req.headers.set(\"x-partykit-props\", JSON.stringify(options?.props));\n\t\tconst className = bindingNames[namespace];\n\t\tlet partyDeprecationWarned = false;\n\t\tconst lobby = {\n\t\t\tget party() {\n\t\t\t\tif (!partyDeprecationWarned) {\n\t\t\t\t\tpartyDeprecationWarned = true;\n\t\t\t\t\tconsole.warn(\"lobby.party is deprecated and currently returns the kebab-case namespace (e.g. \\\"my-agent\\\"). Use lobby.className instead to get the Durable Object class name (e.g. \\\"MyAgent\\\"). In the next major version, lobby.party will return the class name.\");\n\t\t\t\t}\n\t\t\t\treturn namespace;\n\t\t\t},\n\t\t\tclassName,\n\t\t\tname\n\t\t};\n\t\tif (isWebSocket) {\n\t\t\tif (options?.onBeforeConnect) {\n\t\t\t\tconst reqOrRes = await options.onBeforeConnect(req, lobby);\n\t\t\t\tif (reqOrRes instanceof Request) req = reqOrRes;\n\t\t\t\telse if (reqOrRes instanceof Response) return reqOrRes;\n\t\t\t}\n\t\t} else if (options?.onBeforeRequest) {\n\t\t\tconst reqOrRes = await options.onBeforeRequest(req, lobby);\n\t\t\tif (reqOrRes instanceof Request) req = reqOrRes;\n\t\t\telse if (reqOrRes instanceof Response) return withCorsHeaders(reqOrRes);\n\t\t}\n\t\treturn withCorsHeaders(await stub.fetch(req));\n\t} else return null;\n}\nvar Server = class extends DurableObject {\n\tstatic options = { hibernate: false };\n\t#status = \"zero\";\n\t#ParentClass = Object.getPrototypeOf(this).constructor;\n\t#connectionManager = this.#ParentClass.options.hibernate ? new HibernatingConnectionManager(this.ctx) : new InMemoryConnectionManager();\n\t/**\n\t* Execute SQL queries against the Server's database\n\t* @template T Type of the returned rows\n\t* @param strings SQL query template strings\n\t* @param values Values to be inserted into the query\n\t* @returns Array of query results\n\t*/\n\tsql(strings, ...values) {\n\t\tlet query = \"\";\n\t\ttry {\n\t\t\tquery = strings.reduce((acc, str, i) => acc + str + (i < values.length ? \"?\" : \"\"), \"\");\n\t\t\treturn [...this.ctx.storage.sql.exec(query, ...values)];\n\t\t} catch (e) {\n\t\t\tconsole.error(`failed to execute sql query: ${query}`, e);\n\t\t\tthrow this.onException(e);\n\t\t}\n\t}\n\tconstructor(ctx, env$1) {\n\t\tsuper(ctx, env$1);\n\t}\n\t/**\n\t* Handle incoming requests to the server.\n\t*/\n\tasync fetch(request) {\n\t\ttry {\n\t\t\tconst props = request.headers.get(\"x-partykit-props\");\n\t\t\tif (props) this.#_props = JSON.parse(props);\n\t\t\tif (!this.#_name) {\n\t\t\t\tconst room = request.headers.get(\"x-partykit-room\");\n\t\t\t\tif (!room) throw new Error(`Missing namespace or room headers when connecting to ${this.#ParentClass.name}.\nDid you try connecting directly to this Durable Object? Try using getServerByName(namespace, id) instead.`);\n\t\t\t\tawait this.setName(room);\n\t\t\t} else if (this.#status !== \"started\") await this.#initialize();\n\t\t\tconst url = new URL(request.url);\n\t\t\tif (url.pathname === \"/cdn-cgi/partyserver/set-name/\") return Response.json({ ok: true });\n\t\t\tif (request.headers.get(\"Upgrade\")?.toLowerCase() !== \"websocket\") return await this.onRequest(request);\n\t\t\telse {\n\t\t\t\tconst { 0: clientWebSocket, 1: serverWebSocket } = new WebSocketPair();\n\t\t\t\tlet connectionId = url.searchParams.get(\"_pk\");\n\t\t\t\tif (!connectionId) connectionId = nanoid();\n\t\t\t\tlet connection = Object.assign(serverWebSocket, {\n\t\t\t\t\tid: connectionId,\n\t\t\t\t\tserver: this.name,\n\t\t\t\t\ttags: [],\n\t\t\t\t\tstate: null,\n\t\t\t\t\tsetState(setState) {\n\t\t\t\t\t\tlet state;\n\t\t\t\t\t\tif (setState instanceof Function) state = setState(this.state);\n\t\t\t\t\t\telse state = setState;\n\t\t\t\t\t\tthis.state = state;\n\t\t\t\t\t\treturn this.state;\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t\tconst ctx = { request };\n\t\t\t\tconst tags = await this.getConnectionTags(connection, ctx);\n\t\t\t\tconnection = this.#connectionManager.accept(connection, {\n\t\t\t\t\ttags,\n\t\t\t\t\tserver: this.name\n\t\t\t\t});\n\t\t\t\tif (!this.#ParentClass.options.hibernate) this.#attachSocketEventHandlers(connection);\n\t\t\t\tawait this.onConnect(connection, ctx);\n\t\t\t\treturn new Response(null, {\n\t\t\t\t\tstatus: 101,\n\t\t\t\t\twebSocket: clientWebSocket\n\t\t\t\t});\n\t\t\t}\n\t\t} catch (err) {\n\t\t\tconsole.error(`Error in ${this.#ParentClass.name}:${this.#_name ?? \"<unnamed>\"} fetch:`, err);\n\t\t\tif (!(err instanceof Error)) throw err;\n\t\t\tif (request.headers.get(\"Upgrade\") === \"websocket\") {\n\t\t\t\tconst pair = new WebSocketPair();\n\t\t\t\tpair[1].accept();\n\t\t\t\tpair[1].send(JSON.stringify({ error: err.stack }));\n\t\t\t\tpair[1].close(1011, \"Uncaught exception during session setup\");\n\t\t\t\treturn new Response(null, {\n\t\t\t\t\tstatus: 101,\n\t\t\t\t\twebSocket: pair[0]\n\t\t\t\t});\n\t\t\t} else return new Response(err.stack, { status: 500 });\n\t\t}\n\t}\n\tasync webSocketMessage(ws, message) {\n\t\tif (!isPartyServerWebSocket(ws)) return;\n\t\ttry {\n\t\t\tconst connection = createLazyConnection(ws);\n\t\t\tawait this.setName(connection.server);\n\t\t\treturn this.onMessage(connection, message);\n\t\t} catch (e) {\n\t\t\tconsole.error(`Error in ${this.#ParentClass.name}:${this.#_name ?? \"<unnamed>\"} webSocketMessage:`, e);\n\t\t}\n\t}\n\tasync webSocketClose(ws, code, reason, wasClean) {\n\t\tif (!isPartyServerWebSocket(ws)) return;\n\t\ttry {\n\t\t\tconst connection = createLazyConnection(ws);\n\t\t\tawait this.setName(connection.server);\n\t\t\treturn this.onClose(connection, code, reason, wasClean);\n\t\t} catch (e) {\n\t\t\tconsole.error(`Error in ${this.#ParentClass.name}:${this.#_name ?? \"<unnamed>\"} webSocketClose:`, e);\n\t\t}\n\t}\n\tasync webSocketError(ws, error) {\n\t\tif (!isPartyServerWebSocket(ws)) return;\n\t\ttry {\n\t\t\tconst connection = createLazyConnection(ws);\n\t\t\tawait this.setName(connection.server);\n\t\t\treturn this.onError(connection, error);\n\t\t} catch (e) {\n\t\t\tconsole.error(`Error in ${this.#ParentClass.name}:${this.#_name ?? \"<unnamed>\"} webSocketError:`, e);\n\t\t}\n\t}\n\tasync #initialize() {\n\t\tlet error;\n\t\tawait this.ctx.blockConcurrencyWhile(async () => {\n\t\t\tthis.#status = \"starting\";\n\t\t\ttry {\n\t\t\t\tawait this.onStart(this.#_props);\n\t\t\t\tthis.#status = \"started\";\n\t\t\t} catch (e) {\n\t\t\t\tthis.#status = \"zero\";\n\t\t\t\terror = e;\n\t\t\t}\n\t\t});\n\t\tif (error) throw error;\n\t}\n\t#attachSocketEventHandlers(connection) {\n\t\tconst handleMessageFromClient = (event) => {\n\t\t\tthis.onMessage(connection, event.data)?.catch((e) => {\n\t\t\t\tconsole.error(\"onMessage error:\", e);\n\t\t\t});\n\t\t};\n\t\tconst handleCloseFromClient = (event) => {\n\t\t\tconnection.removeEventListener(\"message\", handleMessageFromClient);\n\t\t\tconnection.removeEventListener(\"close\", handleCloseFromClient);\n\t\t\tthis.onClose(connection, event.code, event.reason, event.wasClean)?.catch((e) => {\n\t\t\t\tconsole.error(\"onClose error:\", e);\n\t\t\t});\n\t\t};\n\t\tconst handleErrorFromClient = (e) => {\n\t\t\tconnection.removeEventListener(\"message\", handleMessageFromClient);\n\t\t\tconnection.removeEventListener(\"error\", handleErrorFromClient);\n\t\t\tthis.onError(connection, e.error)?.catch((e$1) => {\n\t\t\t\tconsole.error(\"onError error:\", e$1);\n\t\t\t});\n\t\t};\n\t\tconnection.addEventListener(\"close\", handleCloseFromClient);\n\t\tconnection.addEventListener(\"error\", handleErrorFromClient);\n\t\tconnection.addEventListener(\"message\", handleMessageFromClient);\n\t}\n\t#_name;\n\t#_longErrorAboutNameThrown = false;\n\t/**\n\t* The name for this server. Write-once-only.\n\t*/\n\tget name() {\n\t\tif (!this.#_name) if (!this.#_longErrorAboutNameThrown) {\n\t\t\tthis.#_longErrorAboutNameThrown = true;\n\t\t\tthrow new Error(`Attempting to read .name on ${this.#ParentClass.name} before it was set. The name can be set by explicitly calling .setName(name) on the stub, or by using routePartyKitRequest(). This is a known issue and will be fixed soon. Follow https://github.com/cloudflare/workerd/issues/2240 for more updates.`);\n\t\t} else throw new Error(`Attempting to read .name on ${this.#ParentClass.name} before it was set.`);\n\t\treturn this.#_name;\n\t}\n\tasync setName(name) {\n\t\tif (!name) throw new Error(\"A name is required.\");\n\t\tif (this.#_name && this.#_name !== name) throw new Error(`This server already has a name: ${this.#_name}, attempting to set to: ${name}`);\n\t\tthis.#_name = name;\n\t\tif (this.#status !== \"started\") await this.#initialize();\n\t}\n\t#sendMessageToConnection(connection, message) {\n\t\ttry {\n\t\t\tconnection.send(message);\n\t\t} catch (_e) {\n\t\t\tconnection.close(1011, \"Unexpected error\");\n\t\t}\n\t}\n\t/** Send a message to all connected clients, except connection ids listed in `without` */\n\tbroadcast(msg, without) {\n\t\tfor (const connection of this.#connectionManager.getConnections()) if (!without || !without.includes(connection.id)) this.#sendMessageToConnection(connection, msg);\n\t}\n\t/** Get a connection by connection id */\n\tgetConnection(id) {\n\t\treturn this.#connectionManager.getConnection(id);\n\t}\n\t/**\n\t* Get all connections. Optionally, you can provide a tag to filter returned connections.\n\t* Use `Server#getConnectionTags` to tag the connection on connect.\n\t*/\n\tgetConnections(tag) {\n\t\treturn this.#connectionManager.getConnections(tag);\n\t}\n\t/**\n\t* You can tag a connection to filter them in Server#getConnections.\n\t* Each connection supports up to 9 tags, each tag max length is 256 characters.\n\t*/\n\tgetConnectionTags(connection, context) {\n\t\treturn [];\n\t}\n\t#_props;\n\t/**\n\t* Called when the server is started for the first time.\n\t*/\n\tonStart(props) {}\n\t/**\n\t* Called when a new connection is made to the server.\n\t*/\n\tonConnect(connection, ctx) {\n\t\tconsole.log(`Connection ${connection.id} connected to ${this.#ParentClass.name}:${this.name}`);\n\t}\n\t/**\n\t* Called when a message is received from a connection.\n\t*/\n\tonMessage(connection, message) {\n\t\tconsole.log(`Received message on connection ${this.#ParentClass.name}:${connection.id}`);\n\t\tconsole.info(`Implement onMessage on ${this.#ParentClass.name} to handle this message.`);\n\t}\n\t/**\n\t* Called when a connection is closed.\n\t*/\n\tonClose(connection, code, reason, wasClean) {}\n\t/**\n\t* Called when an error occurs on a connection.\n\t*/\n\tonError(connection, error) {\n\t\tconsole.error(`Error on connection ${connection.id} in ${this.#ParentClass.name}:${this.name}:`, error);\n\t\tconsole.info(`Implement onError on ${this.#ParentClass.name} to handle this error.`);\n\t}\n\t/**\n\t* Called when a request is made to the server.\n\t*/\n\tonRequest(request) {\n\t\tconsole.warn(`onRequest hasn't been implemented on ${this.#ParentClass.name}:${this.name} responding to ${request.url}`);\n\t\treturn new Response(\"Not implemented\", { status: 404 });\n\t}\n\t/**\n\t* Called when an exception occurs.\n\t* @param error - The error that occurred.\n\t*/\n\tonException(error) {\n\t\tconsole.error(`Exception in ${this.#ParentClass.name}:${this.name}:`, error);\n\t\tconsole.info(`Implement onException on ${this.#ParentClass.name} to handle this error.`);\n\t}\n\tonAlarm() {\n\t\tconsole.log(`Implement onAlarm on ${this.#ParentClass.name} to handle alarms.`);\n\t}\n\tasync alarm() {\n\t\tif (this.#status !== \"started\") await this.#initialize();\n\t\tawait this.onAlarm();\n\t}\n};\n\n//#endregion\nexport { Server, getServerByName, routePartykitRequest };\n//# sourceMappingURL=index.js.map","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// biome-ignore lint/correctness/noUnusedVariables: it's fine\nconst wsReadyStateClosing = 2;\n// biome-ignore lint/correctness/noUnusedVariables: it's fine\nconst wsReadyStateClosed = 3;\n\nconst messageSync = 0;\nconst messageAwareness = 1;\n// biome-ignore lint/correctness/noUnusedVariables: it's fine\nconst messageAuth = 2;\n\nfunction updateHandler(update: Uint8Array, _origin: unknown, doc: WSSharedDoc) {\n const encoder = encoding.createEncoder();\n encoding.writeVarUint(encoder, messageSync);\n syncProtocol.writeUpdate(encoder, update);\n const message = encoding.toUint8Array(encoder);\n doc.conns.forEach((_, conn) => {\n send(doc, conn, message);\n });\n}\n\nclass WSSharedDoc extends YDoc {\n conns: Map<Connection, Set<number>>;\n awareness: awarenessProtocol.Awareness;\n\n constructor() {\n super({ gc: true });\n\n /**\n * Maps from conn to set of controlled user ids. Delete all user ids from awareness when this conn is closed\n */\n this.conns = new Map();\n\n this.awareness = new awarenessProtocol.Awareness(this);\n this.awareness.setLocalState(null);\n\n const awarenessChangeHandler = (\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 // Origin is the connection that made the change\n ) => {\n const changedClients = added.concat(updated, removed);\n if (conn !== null) {\n const connControlledIDs =\n /** @type {Set<number>} */ this.conns.get(conn);\n if (connControlledIDs !== undefined) {\n added.forEach((clientID) => {\n connControlledIDs.add(clientID);\n });\n removed.forEach((clientID) => {\n connControlledIDs.delete(clientID);\n });\n }\n }\n // broadcast awareness update\n const encoder = encoding.createEncoder();\n encoding.writeVarUint(encoder, messageAwareness);\n encoding.writeVarUint8Array(\n encoder,\n awarenessProtocol.encodeAwarenessUpdate(this.awareness, changedClients)\n );\n const buff = encoding.toUint8Array(encoder);\n this.conns.forEach((_, c) => {\n send(this, c, buff);\n });\n };\n this.awareness.on(\"update\", awarenessChangeHandler);\n // @ts-expect-error - TODO: fix this\n this.on(\"update\", updateHandler);\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 closeConn(doc: WSSharedDoc, conn: Connection): void {\n if (doc.conns.has(conn)) {\n const controlledIds: Set<number> = doc.conns.get(conn)!;\n doc.conns.delete(conn);\n awarenessProtocol.removeAwarenessStates(\n doc.awareness,\n Array.from(controlledIds),\n null\n );\n }\n try {\n conn.close();\n } catch (e) {\n console.warn(\"failed to close connection\", e);\n }\n}\n\nfunction send(doc: WSSharedDoc, conn: Connection, m: Uint8Array) {\n if (\n conn.readyState !== undefined &&\n conn.readyState !== wsReadyStateConnecting &&\n conn.readyState !== wsReadyStateOpen\n ) {\n closeConn(doc, conn);\n }\n try {\n conn.send(m);\n } catch (_e) {\n closeConn(doc, conn);\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<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 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\n // biome-ignore lint/correctness/noUnusedFunctionParameters: so autocomplete works\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 // biome-ignore lint/correctness/noUnusedFunctionParameters: so autocomplete works\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 this.document.conns.forEach((_, conn) => {\n if (excludeConnection && conn === excludeConnection) {\n return;\n }\n if (\n conn.readyState !== undefined &&\n conn.readyState !== wsReadyStateConnecting &&\n conn.readyState !== wsReadyStateOpen\n ) {\n return;\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(this.document, connection, encoding.toUint8Array(encoder));\n }\n break;\n case messageAwareness: {\n awarenessProtocol.applyAwarenessUpdate(\n this.document.awareness,\n decoding.readVarUint8Array(decoder),\n connection\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 closeConn(this.document, connection);\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 // conn.binaryType = \"arraybuffer\"; // from y-websocket, breaks in our runtime\n\n this.document.conns.set(conn, new Set());\n\n // send sync step 1\n const encoder = encoding.createEncoder();\n encoding.writeVarUint(encoder, messageSync);\n syncProtocol.writeSyncStep1(encoder, this.document);\n send(this.document, 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(this.document, conn, encoding.toUint8Array(encoder));\n }\n }\n}\n"],"mappings":"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"}
|
|
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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"}
|
package/package.json
CHANGED
|
@@ -1,11 +1,23 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "y-partyserver",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.1.0",
|
|
4
|
+
"description": "",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"collaboration",
|
|
7
|
+
"text-editors",
|
|
8
|
+
"yjs"
|
|
9
|
+
],
|
|
10
|
+
"homepage": "https://github.com/cloudflare/partykit/tree/main/packages/y-partyserver",
|
|
11
|
+
"license": "ISC",
|
|
12
|
+
"author": "Sunil Pai <spai@cloudflare.com>",
|
|
4
13
|
"repository": {
|
|
5
14
|
"type": "git",
|
|
6
15
|
"url": "git://github.com/cloudflare/partykit.git"
|
|
7
16
|
},
|
|
8
|
-
"
|
|
17
|
+
"files": [
|
|
18
|
+
"dist",
|
|
19
|
+
"README.md"
|
|
20
|
+
],
|
|
9
21
|
"type": "module",
|
|
10
22
|
"exports": {
|
|
11
23
|
".": {
|
|
@@ -25,36 +37,28 @@
|
|
|
25
37
|
}
|
|
26
38
|
},
|
|
27
39
|
"scripts": {
|
|
28
|
-
"build": "tsx scripts/build.ts"
|
|
40
|
+
"build": "tsx scripts/build.ts",
|
|
41
|
+
"check:test": "vitest -r src/tests --watch false",
|
|
42
|
+
"test": "vitest -r src/tests",
|
|
43
|
+
"test:integration": "vitest -r src/tests --config vitest.integration.config.ts --watch false",
|
|
44
|
+
"test:hibernation": "vitest -r src/tests --config vitest.hibernation.config.ts --watch false"
|
|
29
45
|
},
|
|
30
|
-
"files": [
|
|
31
|
-
"dist",
|
|
32
|
-
"README.md"
|
|
33
|
-
],
|
|
34
|
-
"keywords": [
|
|
35
|
-
"yjs",
|
|
36
|
-
"collaboration",
|
|
37
|
-
"text-editors"
|
|
38
|
-
],
|
|
39
|
-
"author": "Sunil Pai <spai@cloudflare.com>",
|
|
40
|
-
"license": "ISC",
|
|
41
|
-
"description": "",
|
|
42
46
|
"dependencies": {
|
|
43
|
-
"lib0": "^0.2.
|
|
47
|
+
"lib0": "^0.2.117",
|
|
44
48
|
"lodash.debounce": "^4.0.8",
|
|
45
49
|
"nanoid": "^5.1.6",
|
|
46
50
|
"y-protocols": "^1.0.7"
|
|
47
51
|
},
|
|
52
|
+
"devDependencies": {
|
|
53
|
+
"@cloudflare/workers-types": "^4.20260303.0",
|
|
54
|
+
"@types/lodash.debounce": "^4.0.9",
|
|
55
|
+
"partyserver": ">=0.3.1",
|
|
56
|
+
"ws": "^8.19.0",
|
|
57
|
+
"yjs": "^13.6.29"
|
|
58
|
+
},
|
|
48
59
|
"peerDependencies": {
|
|
49
60
|
"@cloudflare/workers-types": "^4.20240729.0",
|
|
50
|
-
"partyserver": "
|
|
61
|
+
"partyserver": ">=0.2.0 <1.0.0",
|
|
51
62
|
"yjs": "^13.6.14"
|
|
52
|
-
},
|
|
53
|
-
"devDependencies": {
|
|
54
|
-
"@cloudflare/workers-types": "^4.20251218.0",
|
|
55
|
-
"@types/lodash.debounce": "^4.0.9",
|
|
56
|
-
"partyserver": "^0.2.0",
|
|
57
|
-
"ws": "^8.18.3",
|
|
58
|
-
"yjs": "^13.6.28"
|
|
59
63
|
}
|
|
60
64
|
}
|