y-partyserver 0.0.54 → 2.0.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/README.md CHANGED
@@ -16,7 +16,7 @@ Like PartyServer, `YServer` is a class that extends `DurableObject` (as well as
16
16
  ```ts
17
17
  export { YServer as MyYServer } from "y-partyserver";
18
18
 
19
- // then setup wrangler.toml and a default fetch handler
19
+ // then setup wrangler.jsonc and a default fetch handler
20
20
  // like you would for PartyServer.
21
21
  ```
22
22
 
@@ -1,4 +1,3 @@
1
- import { n as sendChunked } from "../chunking-DIusuGmM.js";
2
1
  import * as decoding from "lib0/decoding";
3
2
  import * as encoding from "lib0/encoding";
4
3
  import { nanoid } from "nanoid";
@@ -69,7 +68,7 @@ function setupWS(provider) {
69
68
  }
70
69
  provider.wsLastMessageReceived = time.getUnixTime();
71
70
  const encoder = readMessage(provider, new Uint8Array(event.data), true);
72
- if (encoding.length(encoder) > 1) sendChunked(encoding.toUint8Array(encoder), websocket);
71
+ if (encoding.length(encoder) > 1) websocket.send(encoding.toUint8Array(encoder));
73
72
  });
74
73
  websocket.addEventListener("error", (event) => {
75
74
  provider.emit("connection-error", [event, provider]);
@@ -95,12 +94,12 @@ function setupWS(provider) {
95
94
  const encoder = encoding.createEncoder();
96
95
  encoding.writeVarUint(encoder, messageSync);
97
96
  syncProtocol.writeSyncStep1(encoder, provider.doc);
98
- sendChunked(encoding.toUint8Array(encoder), websocket);
97
+ websocket.send(encoding.toUint8Array(encoder));
99
98
  if (provider.awareness.getLocalState() !== null) {
100
99
  const encoderAwarenessState = encoding.createEncoder();
101
100
  encoding.writeVarUint(encoderAwarenessState, messageAwareness);
102
101
  encoding.writeVarUint8Array(encoderAwarenessState, awarenessProtocol.encodeAwarenessUpdate(provider.awareness, [provider.doc.clientID]));
103
- sendChunked(encoding.toUint8Array(encoderAwarenessState), websocket);
102
+ websocket.send(encoding.toUint8Array(encoderAwarenessState));
104
103
  }
105
104
  });
106
105
  provider.emit("status", [{ status: "connecting" }]);
@@ -108,7 +107,7 @@ function setupWS(provider) {
108
107
  }
109
108
  function broadcastMessage(provider, buf) {
110
109
  const ws = provider.ws;
111
- if (provider.wsconnected && ws && ws.readyState === ws.OPEN) sendChunked(buf, ws);
110
+ if (provider.wsconnected && ws && ws.readyState === ws.OPEN) ws.send(buf);
112
111
  if (provider.bcconnected) bc.publish(provider.bcChannel, buf, provider);
113
112
  }
114
113
  const DefaultWebSocket = typeof WebSocket === "undefined" ? null : WebSocket;
@@ -176,7 +175,7 @@ var WebsocketProvider = class extends Observable {
176
175
  const encoder = encoding.createEncoder();
177
176
  encoding.writeVarUint(encoder, messageSync);
178
177
  syncProtocol.writeSyncStep1(encoder, doc);
179
- sendChunked(encoding.toUint8Array(encoder), this.ws);
178
+ this.ws.send(encoding.toUint8Array(encoder));
180
179
  }
181
180
  }, resyncInterval);
182
181
  this._bcSubscriber = (data, origin) => {
@@ -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\nimport { sendChunked } from \"../shared/chunking\";\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 sendChunked(encoding.toUint8Array(encoder), websocket);\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 sendChunked(encoding.toUint8Array(encoder), websocket);\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 sendChunked(encoding.toUint8Array(encoderAwarenessState), websocket);\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 sendChunked(buf, ws);\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 sendChunked(encoding.toUint8Array(encoder), this.ws);\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":["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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"}
@@ -20,7 +20,9 @@ interface CallbackOptions {
20
20
  debounceMaxWait?: number;
21
21
  timeout?: number;
22
22
  }
23
- declare class YServer<Env = unknown> extends Server<Env> {
23
+ declare class YServer<
24
+ Env extends Cloudflare.Env = Cloudflare.Env
25
+ > extends Server<Env> {
24
26
  #private;
25
27
  static callbackOptions: CallbackOptions;
26
28
  readonly document: WSSharedDoc;
@@ -58,7 +60,7 @@ declare class YServer<Env = unknown> extends Server<Env> {
58
60
  */
59
61
  broadcastCustomMessage(message: string, excludeConnection?: Connection): void;
60
62
  handleMessage(connection: Connection, message: WSMessage): void;
61
- onMessage: (conn: Connection, message: WSMessage) => void;
63
+ onMessage(conn: Connection, message: WSMessage): void;
62
64
  onClose(
63
65
  connection: Connection<unknown>,
64
66
  _code: number,
@@ -1,4 +1,3 @@
1
- import { t as handleChunked } from "../chunking-DIusuGmM.js";
2
1
  import * as decoding from "lib0/decoding";
3
2
  import * as encoding from "lib0/encoding";
4
3
  import debounce from "lodash.debounce";
@@ -19,6 +18,27 @@ if (!("OPEN" in WebSocket)) {
19
18
  Object.assign(WebSocket, WebSocketStatus);
20
19
  Object.assign(WebSocket.prototype, WebSocketStatus);
21
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
+ }
22
42
  /**
23
43
  * Cache websocket attachments to avoid having to rehydrate them on every property access.
24
44
  */
@@ -61,29 +81,44 @@ const createLazyConnection = (ws) => {
61
81
  server: { get() {
62
82
  return attachments.get(ws).__pk.server;
63
83
  } },
84
+ tags: { get() {
85
+ return attachments.get(ws).__pk.tags ?? [];
86
+ } },
64
87
  socket: { get() {
65
88
  return ws;
66
89
  } },
67
- state: { get() {
68
- return ws.deserializeAttachment();
69
- } },
70
- setState: { value: function setState(setState$1) {
71
- let state;
72
- if (setState$1 instanceof Function) state = setState$1(this.state);
73
- else state = setState$1;
74
- ws.serializeAttachment(state);
75
- return state;
76
- } },
77
- deserializeAttachment: { value: function deserializeAttachment() {
78
- return attachments.get(ws).__user ?? null;
79
- } },
80
- serializeAttachment: { value: function serializeAttachment(attachment) {
81
- const setting = {
82
- ...attachments.get(ws),
83
- __user: attachment ?? null
84
- };
85
- attachments.set(ws, setting);
86
- } }
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
+ }
87
122
  });
88
123
  if (initialState) connection.setState(initialState);
89
124
  connections.add(connection);
@@ -102,10 +137,13 @@ var HibernatingConnectionIterator = class {
102
137
  next() {
103
138
  const sockets = this.sockets ?? (this.sockets = this.state.getWebSockets(this.tag));
104
139
  let socket;
105
- while (socket = sockets[this.index++]) if (socket.readyState === WebSocket.READY_STATE_OPEN) return {
106
- done: false,
107
- value: createLazyConnection(socket)
108
- };
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
+ }
109
147
  return {
110
148
  done: true,
111
149
  value: void 0
@@ -113,6 +151,20 @@ var HibernatingConnectionIterator = class {
113
151
  }
114
152
  };
115
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
+ /**
116
168
  * When not using hibernation, we track active connections manually.
117
169
  */
118
170
  var InMemoryConnectionManager = class {
@@ -133,8 +185,13 @@ var InMemoryConnectionManager = class {
133
185
  }
134
186
  accept(connection, options) {
135
187
  connection.accept();
188
+ const tags = prepareTags(connection.id, options.tags);
136
189
  this.#connections.set(connection.id, connection);
137
- this.tags.set(connection, [connection.id, ...options.tags.filter((t) => t !== connection.id)]);
190
+ this.tags.set(connection, tags);
191
+ Object.defineProperty(connection, "tags", {
192
+ get: () => tags,
193
+ configurable: true
194
+ });
138
195
  const removeConnection = () => {
139
196
  this.#connections.delete(connection.id);
140
197
  connection.removeEventListener("close", removeConnection);
@@ -153,30 +210,29 @@ var HibernatingConnectionManager = class {
153
210
  this.controller = controller;
154
211
  }
155
212
  getCount() {
156
- return Number(this.controller.getWebSockets().length);
213
+ let count = 0;
214
+ for (const ws of this.controller.getWebSockets()) if (isPartyServerWebSocket(ws)) count++;
215
+ return count;
157
216
  }
158
217
  getConnection(id) {
159
- const sockets = this.controller.getWebSockets(id);
160
- if (sockets.length === 0) return void 0;
161
- if (sockets.length === 1) return createLazyConnection(sockets[0]);
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]);
162
223
  throw new Error(`More than one connection found for id ${id}. Did you mean to use getConnections(tag) instead?`);
163
224
  }
164
225
  getConnections(tag) {
165
226
  return new HibernatingConnectionIterator(this.controller, tag);
166
227
  }
167
228
  accept(connection, options) {
168
- const tags = [connection.id, ...options.tags.filter((t) => t !== connection.id)];
169
- if (tags.length > 10) throw new Error("A connection can only have 10 tags, including the default id tag.");
170
- for (const tag of tags) {
171
- if (typeof tag !== "string") throw new Error(`A connection tag must be a string. Received: ${tag}`);
172
- if (tag === "") throw new Error("A connection tag must not be an empty string.");
173
- if (tag.length > 256) throw new Error("A connection tag must not exceed 256 characters");
174
- }
229
+ const tags = prepareTags(connection.id, options.tags);
175
230
  this.controller.acceptWebSocket(connection, tags);
176
231
  connection.serializeAttachment({
177
232
  __pk: {
178
233
  id: connection.id,
179
- server: options.server
234
+ server: options.server,
235
+ tags
180
236
  },
181
237
  __user: null
182
238
  });
@@ -205,39 +261,24 @@ var Server = class extends DurableObject {
205
261
  throw this.onException(e);
206
262
  }
207
263
  }
208
- constructor(ctx, env) {
209
- super(ctx, env);
264
+ constructor(ctx, env$1) {
265
+ super(ctx, env$1);
210
266
  }
211
267
  /**
212
268
  * Handle incoming requests to the server.
213
269
  */
214
270
  async fetch(request) {
215
- const props = request.headers.get("x-partykit-props");
216
- if (props) try {
217
- this.#_props = JSON.parse(props);
218
- } catch {
219
- console.error("Internal error parsing context props.");
220
- }
221
- if (!this.#_name) {
222
- const room = request.headers.get("x-partykit-room");
223
- if (!room) throw new Error(`Missing namespace or room headers when connecting to ${this.#ParentClass.name}.
224
- Did you try connecting directly to this Durable Object? Try using getServerByName(namespace, id) instead.`);
225
- await this.setName(room);
226
- }
227
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();
228
280
  const url = new URL(request.url);
229
281
  if (url.pathname === "/cdn-cgi/partyserver/set-name/") return Response.json({ ok: true });
230
- if (url.pathname === "/cdn-cgi/partyserver/keep-alive/") {
231
- if (request.headers.get("Upgrade")?.toLowerCase() === "websocket") {
232
- const { 0: client, 1: server } = new WebSocketPair();
233
- this.ctx.acceptWebSocket(server, ["partyserver-keepalive"]);
234
- return new Response(null, {
235
- status: 101,
236
- webSocket: client
237
- });
238
- }
239
- return new Response("WebSocket required", { status: 426 });
240
- }
241
282
  if (request.headers.get("Upgrade")?.toLowerCase() !== "websocket") return await this.onRequest(request);
242
283
  else {
243
284
  const { 0: clientWebSocket, 1: serverWebSocket } = new WebSocketPair();
@@ -246,6 +287,7 @@ Did you try connecting directly to this Durable Object? Try using getServerByNam
246
287
  let connection = Object.assign(serverWebSocket, {
247
288
  id: connectionId,
248
289
  server: this.name,
290
+ tags: [],
249
291
  state: null,
250
292
  setState(setState) {
251
293
  let state;
@@ -269,7 +311,7 @@ Did you try connecting directly to this Durable Object? Try using getServerByNam
269
311
  });
270
312
  }
271
313
  } catch (err) {
272
- console.error(`Error in ${this.#ParentClass.name}:${this.name} fetch:`, err);
314
+ console.error(`Error in ${this.#ParentClass.name}:${this.#_name ?? "<unnamed>"} fetch:`, err);
273
315
  if (!(err instanceof Error)) throw err;
274
316
  if (request.headers.get("Upgrade") === "websocket") {
275
317
  const pair = new WebSocketPair();
@@ -284,35 +326,48 @@ Did you try connecting directly to this Durable Object? Try using getServerByNam
284
326
  }
285
327
  }
286
328
  async webSocketMessage(ws, message) {
287
- if (this.ctx.getTags(ws).includes("partyserver-keepalive")) {
288
- if (message === "ping") ws.send("pong");
289
- return;
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);
290
336
  }
291
- const connection = createLazyConnection(ws);
292
- await this.setName(connection.server);
293
- if (this.#status !== "started") await this.#initialize();
294
- return this.onMessage(connection, message);
295
337
  }
296
338
  async webSocketClose(ws, code, reason, wasClean) {
297
- if (this.ctx.getTags(ws).includes("partyserver-keepalive")) return;
298
- const connection = createLazyConnection(ws);
299
- await this.setName(connection.server);
300
- if (this.#status !== "started") await this.#initialize();
301
- return this.onClose(connection, 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
+ }
302
347
  }
303
348
  async webSocketError(ws, error) {
304
- if (this.ctx.getTags(ws).includes("partyserver-keepalive")) return;
305
- const connection = createLazyConnection(ws);
306
- await this.setName(connection.server);
307
- if (this.#status !== "started") await this.#initialize();
308
- return this.onError(connection, 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
+ }
309
357
  }
310
358
  async #initialize() {
359
+ let error;
311
360
  await this.ctx.blockConcurrencyWhile(async () => {
312
361
  this.#status = "starting";
313
- await this.onStart(this.#_props);
314
- this.#status = "started";
362
+ try {
363
+ await this.onStart(this.#_props);
364
+ this.#status = "started";
365
+ } catch (e) {
366
+ this.#status = "zero";
367
+ error = e;
368
+ }
315
369
  });
370
+ if (error) throw error;
316
371
  }
317
372
  #attachSocketEventHandlers(connection) {
318
373
  const handleMessageFromClient = (event) => {
@@ -352,11 +407,9 @@ Did you try connecting directly to this Durable Object? Try using getServerByNam
352
407
  }
353
408
  async setName(name) {
354
409
  if (!name) throw new Error("A name is required.");
355
- if (this.#_name && this.#_name !== name) throw new Error("This server already has a name.");
410
+ if (this.#_name && this.#_name !== name) throw new Error(`This server already has a name: ${this.#_name}, attempting to set to: ${name}`);
356
411
  this.#_name = name;
357
- if (this.#status !== "started") await this.ctx.blockConcurrencyWhile(async () => {
358
- await this.#initialize();
359
- });
412
+ if (this.#status !== "started") await this.#initialize();
360
413
  }
361
414
  #sendMessageToConnection(connection, message) {
362
415
  try {
@@ -387,68 +440,6 @@ Did you try connecting directly to this Durable Object? Try using getServerByNam
387
440
  getConnectionTags(connection, context) {
388
441
  return [];
389
442
  }
390
- /**
391
- * Execute a long-running async function while keeping the Durable Object alive.
392
- *
393
- * Durable Objects normally terminate 70-140s after the last network request.
394
- * This method keeps the DO alive by establishing a WebSocket connection to itself
395
- * and sending periodic ping messages.
396
- *
397
- * @experimental This API is experimental and may change in future versions.
398
- *
399
- * @param fn - The async function to execute
400
- * @param timeoutMs - Maximum time to keep the DO alive (default: 30 minutes)
401
- * @returns The result of the async function
402
- *
403
- * @remarks
404
- * Requires the `enable_ctx_exports` compatibility flag in wrangler.jsonc:
405
- * ```json
406
- * {
407
- * "compatibility_flags": ["enable_ctx_exports"]
408
- * }
409
- * ```
410
- *
411
- * @example
412
- * ```typescript
413
- * const result = await this.experimental_waitUntil(async () => {
414
- * // Long-running operation
415
- * await processLargeDataset();
416
- * return { success: true };
417
- * }, 60 * 60 * 1000); // 1 hour timeout
418
- * ```
419
- */
420
- async experimental_waitUntil(fn, timeoutMs = 1800 * 1e3) {
421
- const exports = this.ctx.exports;
422
- if (!exports) throw new Error("waitUntil requires the 'enable_ctx_exports' compatibility flag. Add it to your wrangler.jsonc: { \"compatibility_flags\": [\"enable_ctx_exports\"] }");
423
- const namespace = exports[this.#ParentClass.name];
424
- if (!namespace) throw new Error(`Could not find namespace for ${this.#ParentClass.name} in ctx.exports. Make sure the class name matches your Durable Object binding.`);
425
- const ws = (await namespace.get(this.ctx.id).fetch("http://dummy-example.cloudflare.com/cdn-cgi/partyserver/keep-alive/", { headers: {
426
- Upgrade: "websocket",
427
- "x-partykit-room": this.name
428
- } })).webSocket;
429
- if (!ws) throw new Error("Failed to establish keep-alive WebSocket connection");
430
- ws.accept();
431
- const pingInterval = setInterval(() => {
432
- try {
433
- ws.send("ping");
434
- } catch {}
435
- }, 1e4);
436
- let timeoutId;
437
- const timeoutPromise = new Promise((_, reject) => {
438
- timeoutId = setTimeout(() => {
439
- reject(/* @__PURE__ */ new Error(`experimental_waitUntil timed out after ${timeoutMs}ms`));
440
- }, timeoutMs);
441
- });
442
- try {
443
- return await Promise.race([fn(), timeoutPromise]);
444
- } finally {
445
- clearTimeout(timeoutId);
446
- clearInterval(pingInterval);
447
- try {
448
- ws.close(1e3, "Complete");
449
- } catch {}
450
- }
451
- }
452
443
  #_props;
453
444
  /**
454
445
  * Called when the server is started for the first time.
@@ -702,7 +693,8 @@ var YServer = class extends Server {
702
693
  }
703
694
  try {
704
695
  const encoder = encoding.createEncoder();
705
- const decoder = decoding.createDecoder(message);
696
+ const uint8Array = message instanceof Uint8Array ? message : message instanceof ArrayBuffer ? new Uint8Array(message) : new Uint8Array(message.buffer, message.byteOffset, message.byteLength);
697
+ const decoder = decoding.createDecoder(uint8Array);
706
698
  switch (decoding.readVarUint(decoder)) {
707
699
  case messageSync:
708
700
  encoding.writeVarUint(encoder, messageSync);
@@ -718,7 +710,9 @@ var YServer = class extends Server {
718
710
  this.document.emit("error", [err]);
719
711
  }
720
712
  }
721
- onMessage = handleChunked((conn, message) => this.handleMessage(conn, message));
713
+ onMessage(conn, message) {
714
+ this.handleMessage(conn, message);
715
+ }
722
716
  onClose(connection, _code, _reason, _wasClean) {
723
717
  closeConn(this.document, connection);
724
718
  }
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":["#cache","#connections","#ParentClass","#_props","#_name","#connectionManager","#attachSocketEventHandlers","#status","#initialize","#_longErrorAboutNameThrown","#sendMessageToConnection","YDoc","controlledIds: Set<number>","#ParentClass","encoder"],"sources":["../../../partyserver/dist/index.js","../../src/server/index.ts"],"sourcesContent":["import { DurableObject } 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}\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\tsocket: { get() {\n\t\t\treturn ws;\n\t\t} },\n\t\tstate: { get() {\n\t\t\treturn ws.deserializeAttachment();\n\t\t} },\n\t\tsetState: { value: function setState(setState$1) {\n\t\t\tlet state;\n\t\t\tif (setState$1 instanceof Function) state = setState$1(this.state);\n\t\t\telse state = setState$1;\n\t\t\tws.serializeAttachment(state);\n\t\t\treturn state;\n\t\t} },\n\t\tdeserializeAttachment: { value: function deserializeAttachment() {\n\t\t\treturn attachments.get(ws).__user ?? null;\n\t\t} },\n\t\tserializeAttachment: { value: function serializeAttachment(attachment) {\n\t\t\tconst setting = {\n\t\t\t\t...attachments.get(ws),\n\t\t\t\t__user: attachment ?? null\n\t\t\t};\n\t\t\tattachments.set(ws, setting);\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) return {\n\t\t\tdone: false,\n\t\t\tvalue: createLazyConnection(socket)\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* 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\tthis.#connections.set(connection.id, connection);\n\t\tthis.tags.set(connection, [connection.id, ...options.tags.filter((t) => t !== connection.id)]);\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\treturn Number(this.controller.getWebSockets().length);\n\t}\n\tgetConnection(id) {\n\t\tconst sockets = this.controller.getWebSockets(id);\n\t\tif (sockets.length === 0) return void 0;\n\t\tif (sockets.length === 1) return createLazyConnection(sockets[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 = [connection.id, ...options.tags.filter((t) => t !== connection.id)];\n\t\tif (tags.length > 10) throw new Error(\"A connection can only have 10 tags, including the default id tag.\");\n\t\tfor (const tag of tags) {\n\t\t\tif (typeof tag !== \"string\") throw new Error(`A connection tag must be a string. Received: ${tag}`);\n\t\t\tif (tag === \"\") throw new Error(\"A connection tag must not be an empty string.\");\n\t\t\tif (tag.length > 256) throw new Error(\"A connection tag must not exceed 256 characters\");\n\t\t}\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},\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();\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()).catch((e) => {\n\t\tconsole.error(\"Could not set server name:\", e);\n\t});\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* A utility function for PartyKit style routing.\n*/\nasync function routePartykitRequest(req, env, options) {\n\tif (!serverMapCache.has(env)) serverMapCache.set(env, Object.entries(env).reduce((acc, [k, v]) => {\n\t\tif (v && typeof v === \"object\" && \"idFromName\" in v && typeof v.idFromName === \"function\") {\n\t\t\tObject.assign(acc, { [camelCaseToKebabCase(k)]: v });\n\t\t\treturn acc;\n\t\t}\n\t\treturn acc;\n\t}, {}));\n\tconst map = serverMapCache.get(env);\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]) if (namespace === \"main\") {\n\t\t\tconsole.warn(\"You appear to be migrating a PartyKit project to PartyServer.\");\n\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} else console.error(`The url ${req.url} does not match any server namespace. \nDid you forget to add a durable object binding to the class in your wrangler.toml?`);\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\tif (req.headers.get(\"Upgrade\")?.toLowerCase() === \"websocket\") {\n\t\t\tif (options?.onBeforeConnect) {\n\t\t\t\tconst reqOrRes = await options.onBeforeConnect(req, {\n\t\t\t\t\tparty: namespace,\n\t\t\t\t\tname\n\t\t\t\t});\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, {\n\t\t\t\tparty: namespace,\n\t\t\t\tname\n\t\t\t});\n\t\t\tif (reqOrRes instanceof Request) req = reqOrRes;\n\t\t\telse if (reqOrRes instanceof Response) return reqOrRes;\n\t\t}\n\t\treturn 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) {\n\t\tsuper(ctx, env);\n\t}\n\t/**\n\t* Handle incoming requests to the server.\n\t*/\n\tasync fetch(request) {\n\t\tconst props = request.headers.get(\"x-partykit-props\");\n\t\tif (props) try {\n\t\t\tthis.#_props = JSON.parse(props);\n\t\t} catch {\n\t\t\tconsole.error(\"Internal error parsing context props.\");\n\t\t}\n\t\tif (!this.#_name) {\n\t\t\tconst room = request.headers.get(\"x-partykit-room\");\n\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\tawait this.setName(room);\n\t\t}\n\t\ttry {\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 (url.pathname === \"/cdn-cgi/partyserver/keep-alive/\") {\n\t\t\t\tif (request.headers.get(\"Upgrade\")?.toLowerCase() === \"websocket\") {\n\t\t\t\t\tconst { 0: client, 1: server } = new WebSocketPair();\n\t\t\t\t\tthis.ctx.acceptWebSocket(server, [\"partyserver-keepalive\"]);\n\t\t\t\t\treturn new Response(null, {\n\t\t\t\t\t\tstatus: 101,\n\t\t\t\t\t\twebSocket: client\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\treturn new Response(\"WebSocket required\", { status: 426 });\n\t\t\t}\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\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} 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 (this.ctx.getTags(ws).includes(\"partyserver-keepalive\")) {\n\t\t\tif (message === \"ping\") ws.send(\"pong\");\n\t\t\treturn;\n\t\t}\n\t\tconst connection = createLazyConnection(ws);\n\t\tawait this.setName(connection.server);\n\t\tif (this.#status !== \"started\") await this.#initialize();\n\t\treturn this.onMessage(connection, message);\n\t}\n\tasync webSocketClose(ws, code, reason, wasClean) {\n\t\tif (this.ctx.getTags(ws).includes(\"partyserver-keepalive\")) return;\n\t\tconst connection = createLazyConnection(ws);\n\t\tawait this.setName(connection.server);\n\t\tif (this.#status !== \"started\") await this.#initialize();\n\t\treturn this.onClose(connection, code, reason, wasClean);\n\t}\n\tasync webSocketError(ws, error) {\n\t\tif (this.ctx.getTags(ws).includes(\"partyserver-keepalive\")) return;\n\t\tconst connection = createLazyConnection(ws);\n\t\tawait this.setName(connection.server);\n\t\tif (this.#status !== \"started\") await this.#initialize();\n\t\treturn this.onError(connection, error);\n\t}\n\tasync #initialize() {\n\t\tawait this.ctx.blockConcurrencyWhile(async () => {\n\t\t\tthis.#status = \"starting\";\n\t\t\tawait this.onStart(this.#_props);\n\t\t\tthis.#status = \"started\";\n\t\t});\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.\");\n\t\tthis.#_name = name;\n\t\tif (this.#status !== \"started\") await this.ctx.blockConcurrencyWhile(async () => {\n\t\t\tawait this.#initialize();\n\t\t});\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/**\n\t* Execute a long-running async function while keeping the Durable Object alive.\n\t*\n\t* Durable Objects normally terminate 70-140s after the last network request.\n\t* This method keeps the DO alive by establishing a WebSocket connection to itself\n\t* and sending periodic ping messages.\n\t*\n\t* @experimental This API is experimental and may change in future versions.\n\t*\n\t* @param fn - The async function to execute\n\t* @param timeoutMs - Maximum time to keep the DO alive (default: 30 minutes)\n\t* @returns The result of the async function\n\t*\n\t* @remarks\n\t* Requires the `enable_ctx_exports` compatibility flag in wrangler.jsonc:\n\t* ```json\n\t* {\n\t* \"compatibility_flags\": [\"enable_ctx_exports\"]\n\t* }\n\t* ```\n\t*\n\t* @example\n\t* ```typescript\n\t* const result = await this.experimental_waitUntil(async () => {\n\t* // Long-running operation\n\t* await processLargeDataset();\n\t* return { success: true };\n\t* }, 60 * 60 * 1000); // 1 hour timeout\n\t* ```\n\t*/\n\tasync experimental_waitUntil(fn, timeoutMs = 1800 * 1e3) {\n\t\tconst exports = this.ctx.exports;\n\t\tif (!exports) throw new Error(\"waitUntil requires the 'enable_ctx_exports' compatibility flag. Add it to your wrangler.jsonc: { \\\"compatibility_flags\\\": [\\\"enable_ctx_exports\\\"] }\");\n\t\tconst namespace = exports[this.#ParentClass.name];\n\t\tif (!namespace) throw new Error(`Could not find namespace for ${this.#ParentClass.name} in ctx.exports. Make sure the class name matches your Durable Object binding.`);\n\t\tconst ws = (await namespace.get(this.ctx.id).fetch(\"http://dummy-example.cloudflare.com/cdn-cgi/partyserver/keep-alive/\", { headers: {\n\t\t\tUpgrade: \"websocket\",\n\t\t\t\"x-partykit-room\": this.name\n\t\t} })).webSocket;\n\t\tif (!ws) throw new Error(\"Failed to establish keep-alive WebSocket connection\");\n\t\tws.accept();\n\t\tconst pingInterval = setInterval(() => {\n\t\t\ttry {\n\t\t\t\tws.send(\"ping\");\n\t\t\t} catch {}\n\t\t}, 1e4);\n\t\tlet timeoutId;\n\t\tconst timeoutPromise = new Promise((_, reject) => {\n\t\t\ttimeoutId = setTimeout(() => {\n\t\t\t\treject(/* @__PURE__ */ new Error(`experimental_waitUntil timed out after ${timeoutMs}ms`));\n\t\t\t}, timeoutMs);\n\t\t});\n\t\ttry {\n\t\t\treturn await Promise.race([fn(), timeoutPromise]);\n\t\t} finally {\n\t\t\tclearTimeout(timeoutId);\n\t\t\tclearInterval(pingInterval);\n\t\t\ttry {\n\t\t\t\tws.close(1e3, \"Complete\");\n\t\t\t} catch {}\n\t\t}\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\nimport { handleChunked } from \"../shared/chunking\";\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<Env = unknown> 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 // TODO: this type seems odd\n const decoder = decoding.createDecoder(message as unknown as 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 = handleChunked((conn, message) =>\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":["#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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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "y-partyserver",
3
- "version": "0.0.54",
3
+ "version": "2.0.0",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git://github.com/cloudflare/partykit.git"
@@ -47,13 +47,13 @@
47
47
  },
48
48
  "peerDependencies": {
49
49
  "@cloudflare/workers-types": "^4.20240729.0",
50
- "partyserver": "^0.0.78",
50
+ "partyserver": "^0.2.0",
51
51
  "yjs": "^13.6.14"
52
52
  },
53
53
  "devDependencies": {
54
54
  "@cloudflare/workers-types": "^4.20251218.0",
55
55
  "@types/lodash.debounce": "^4.0.9",
56
- "partyserver": "^0.0.78",
56
+ "partyserver": "^0.2.0",
57
57
  "ws": "^8.18.3",
58
58
  "yjs": "^13.6.28"
59
59
  }
@@ -1,120 +0,0 @@
1
- //#region src/shared/chunking.ts
2
- const CHUNK_MAX_SIZE = 1e6;
3
- const BATCH_SENTINEL = "y-pk-batch";
4
- const TRACE_ENABLED = false;
5
- const trace = (...args) => TRACE_ENABLED;
6
- let warnedAboutLargeMessage = false;
7
- /**
8
- * Takes an ArrayBuffer and sends it in chunks to the provided send function,
9
- * along with a start and end marker.
10
- *
11
- * 1. The sender splits the message into chunks of 1MB or less.
12
- * 2. The sender sends a chunk start marker
13
- * 3. The sender sends each chunk as a individual packet
14
- * 4. The sender sends a chunk end marker
15
- */
16
- const sendChunked = (data, ws) => {
17
- if (data.byteLength <= CHUNK_MAX_SIZE) {
18
- ws.send(data);
19
- return;
20
- }
21
- if (!warnedAboutLargeMessage) {
22
- console.warn("[y-partyserver]", "The Y.js update size exceeds 1MB, which is the maximum size for an individual update. The update will be split into chunks. This is an experimental feature.", `Message size: ${(data.byteLength / 1e3 / 1e3).toFixed(1)}MB`);
23
- warnedAboutLargeMessage = true;
24
- }
25
- const id = (Date.now() + Math.random()).toString(36).substring(10);
26
- const chunks = Math.ceil(data.byteLength / CHUNK_MAX_SIZE);
27
- ws.send(serializeBatchMarker({
28
- id,
29
- type: "start",
30
- size: data.byteLength,
31
- count: chunks
32
- }));
33
- let sentSize = 0;
34
- let sentChunks = 0;
35
- for (let i = 0; i < chunks; i++) {
36
- const chunk = data.slice(CHUNK_MAX_SIZE * i, CHUNK_MAX_SIZE * (i + 1));
37
- ws.send(chunk);
38
- sentChunks += 1;
39
- sentSize += chunk.byteLength;
40
- }
41
- ws.send(serializeBatchMarker({
42
- id,
43
- type: "end",
44
- size: sentSize,
45
- count: sentChunks
46
- }));
47
- };
48
- /**
49
- * Creates a WebSocket message handler that can handle chunked messages.
50
- *
51
- * Reassembles the chunks into a single ArrayBuffer and pass it to the
52
- * provided receive function.
53
- *
54
- * 1. The server receives a chunk start marker
55
- * 2. The server pools each chunk until it receives a chunk end marker
56
- * 3. The server validates that the received data matches the expected size
57
- * 4. The server forwards the message to handlers
58
- */
59
- const handleChunked = (receive) => {
60
- let batch;
61
- let start;
62
- return (connection, message) => {
63
- if (typeof message === "string" && !isBatchSentinel(message)) return;
64
- if (isBatchSentinel(message)) {
65
- const marker = parseBatchMarker(message);
66
- if (marker.type === "start") {
67
- batch = [];
68
- start = marker;
69
- }
70
- if (marker.type === "end") {
71
- if (batch) try {
72
- assertEquality(start?.id, marker.id, "client id");
73
- assertEquality(start?.count, marker.count, "client counts");
74
- assertEquality(start?.size, marker.size, "client size");
75
- const size = batch.reduce((sum, buffer) => sum + buffer.byteLength, 0);
76
- const bytes = new Uint8Array(size);
77
- let bytesWritten = 0;
78
- for (const chunk of batch) {
79
- bytes.set(new Uint8Array(chunk), bytesWritten);
80
- bytesWritten += chunk.byteLength;
81
- }
82
- assertEquality(marker.count, batch.length, "received batch count");
83
- assertEquality(marker.size, bytesWritten, "client size");
84
- receive(connection, bytes);
85
- } catch (e) {
86
- console.error(e);
87
- throw e;
88
- } finally {
89
- batch = void 0;
90
- start = void 0;
91
- }
92
- }
93
- } else if (batch) batch.push(message);
94
- else receive(connection, new Uint8Array(message));
95
- };
96
- };
97
- function assertEquality(expected, actual, label) {
98
- if (expected !== actual) throw new Error(`Mismatching ${label}! Expected ${expected}, got ${actual}`);
99
- else trace(`Matching ${label}: ${expected}`);
100
- }
101
- /** Checks whether a message is batch marker */
102
- function isBatchSentinel(msg) {
103
- return typeof msg === "string" && msg.startsWith(BATCH_SENTINEL);
104
- }
105
- /** Encodes a batch marker message so that it can be safely parsed */
106
- function serializeBatchMarker(batch) {
107
- return `${BATCH_SENTINEL}#${JSON.stringify(batch)}`;
108
- }
109
- /** Parses a batch marker messages and throws if its invalid */
110
- function parseBatchMarker(msg) {
111
- const [sentinel, data] = msg.split("#", 2);
112
- if (sentinel !== BATCH_SENTINEL) throw new Error(`Unexpected batch marker: ${msg}`);
113
- const batch = JSON.parse(data);
114
- if (batch.type !== "start" && batch.type !== "end") throw new Error(`Unexpected batch data: ${msg}`);
115
- return batch;
116
- }
117
-
118
- //#endregion
119
- export { sendChunked as n, handleChunked as t };
120
- //# sourceMappingURL=chunking-DIusuGmM.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"chunking-DIusuGmM.js","names":["batch: ArrayBuffer[] | undefined","start: Batch | undefined"],"sources":["../src/shared/chunking.ts"],"sourcesContent":["// This file contains a shared implementation of chunking logic for binary\n// WebSocket messages. Because the Workers platform limits individual\n// WebSocket messages to 1MB, we need to split larger messages into chunks.\n\nimport type { Connection, WSMessage } from \"partyserver\";\n\nexport const CHUNK_MAX_SIZE = 1_000_000;\n\nconst BATCH_SENTINEL = \"y-pk-batch\";\nconst TRACE_ENABLED = false;\nconst trace = (...args: unknown[]) => TRACE_ENABLED && console.log(...args);\n\nlet warnedAboutLargeMessage = false;\n\ntype MessageHandler = (conn: Connection, message: WSMessage) => void;\ntype Batch = {\n id: string;\n type: \"start\" | \"end\";\n size: number;\n count: number;\n};\n\n/**\n * Takes an ArrayBuffer and sends it in chunks to the provided send function,\n * along with a start and end marker.\n *\n * 1. The sender splits the message into chunks of 1MB or less.\n * 2. The sender sends a chunk start marker\n * 3. The sender sends each chunk as a individual packet\n * 4. The sender sends a chunk end marker\n */\nexport const sendChunked = (data: Uint8Array, ws: WebSocket) => {\n if (data.byteLength <= CHUNK_MAX_SIZE) {\n ws.send(data);\n return;\n }\n\n if (!warnedAboutLargeMessage) {\n console.warn(\n \"[y-partyserver]\",\n \"The Y.js update size exceeds 1MB, which is the maximum size for an individual update. The update will be split into chunks. This is an experimental feature.\",\n `Message size: ${(data.byteLength / 1000 / 1000).toFixed(1)}MB`\n );\n warnedAboutLargeMessage = true;\n }\n\n const id = (Date.now() + Math.random()).toString(36).substring(10);\n const chunks = Math.ceil(data.byteLength / CHUNK_MAX_SIZE);\n\n ws.send(\n serializeBatchMarker({\n id,\n type: \"start\",\n size: data.byteLength,\n count: chunks\n })\n );\n\n let sentSize = 0;\n let sentChunks = 0;\n for (let i = 0; i < chunks; i++) {\n const chunk = data.slice(CHUNK_MAX_SIZE * i, CHUNK_MAX_SIZE * (i + 1));\n ws.send(chunk);\n sentChunks += 1;\n sentSize += chunk.byteLength;\n }\n\n ws.send(\n serializeBatchMarker({\n id,\n type: \"end\",\n size: sentSize,\n count: sentChunks\n })\n );\n};\n\n/**\n * Creates a WebSocket message handler that can handle chunked messages.\n *\n * Reassembles the chunks into a single ArrayBuffer and pass it to the\n * provided receive function.\n *\n * 1. The server receives a chunk start marker\n * 2. The server pools each chunk until it receives a chunk end marker\n * 3. The server validates that the received data matches the expected size\n * 4. The server forwards the message to handlers\n */\nexport const handleChunked = (\n receive: (conn: Connection, data: WSMessage) => void\n): MessageHandler => {\n let batch: ArrayBuffer[] | undefined;\n let start: Batch | undefined;\n\n return (connection, message) => {\n if (typeof message === \"string\" && !isBatchSentinel(message)) {\n return;\n }\n if (isBatchSentinel(message)) {\n const marker = parseBatchMarker(message);\n if (marker.type === \"start\") {\n batch = [];\n start = marker;\n }\n\n if (marker.type === \"end\") {\n if (batch) {\n try {\n // validate start and end markers match\n assertEquality(start?.id, marker.id, \"client id\");\n assertEquality(start?.count, marker.count, \"client counts\");\n assertEquality(start?.size, marker.size, \"client size\");\n\n // combine chunks into single buffer\n const size = batch.reduce(\n (sum, buffer) => sum + buffer.byteLength,\n 0\n );\n const bytes = new Uint8Array(size);\n let bytesWritten = 0;\n for (const chunk of batch) {\n bytes.set(new Uint8Array(chunk), bytesWritten);\n bytesWritten += chunk.byteLength;\n }\n\n // validate data as read matches expected\n assertEquality(marker.count, batch.length, \"received batch count\");\n assertEquality(marker.size, bytesWritten, \"client size\");\n receive(connection, bytes);\n } catch (e) {\n console.error(e);\n throw e;\n } finally {\n batch = undefined;\n start = undefined;\n }\n }\n }\n } else if (batch) {\n // @ts-expect-error typescript hell\n batch.push(message);\n } else {\n // @ts-expect-error typescript hell\n receive(connection, new Uint8Array(message));\n }\n };\n};\n\nfunction assertEquality(expected: unknown, actual: unknown, label: string) {\n if (expected !== actual) {\n throw new Error(\n `Mismatching ${label}! Expected ${expected as string}, got ${actual as string}`\n );\n } else {\n trace(`Matching ${label}: ${expected as string}`);\n }\n}\n\n/** Checks whether a message is batch marker */\nfunction isBatchSentinel(msg: WSMessage): msg is string {\n return typeof msg === \"string\" && msg.startsWith(BATCH_SENTINEL);\n}\n\n/** Encodes a batch marker message so that it can be safely parsed */\nfunction serializeBatchMarker(batch: Batch): string {\n return `${BATCH_SENTINEL}#${JSON.stringify(batch)}`;\n}\n\n/** Parses a batch marker messages and throws if its invalid */\nexport function parseBatchMarker(msg: string) {\n const [sentinel, data] = msg.split(\"#\", 2);\n if (sentinel !== BATCH_SENTINEL) {\n throw new Error(`Unexpected batch marker: ${msg}`);\n }\n\n const batch = JSON.parse(data) as Batch;\n if (batch.type !== \"start\" && batch.type !== \"end\") {\n throw new Error(`Unexpected batch data: ${msg}`);\n }\n\n return batch;\n}\n"],"mappings":"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"}