y-partyserver 0.0.47 → 0.0.48

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.
@@ -2,6 +2,13 @@ import { Server, Connection, WSMessage, ConnectionContext } from "partyserver";
2
2
  import * as awarenessProtocol from "y-protocols/awareness";
3
3
  import { Doc } from "yjs";
4
4
 
5
+ type YjsRootType =
6
+ | "Text"
7
+ | "Map"
8
+ | "Array"
9
+ | "XmlText"
10
+ | "XmlElement"
11
+ | "XmlFragment";
5
12
  declare class WSSharedDoc extends Doc {
6
13
  conns: Map<Connection, Set<number>>;
7
14
  awareness: awarenessProtocol.Awareness;
@@ -18,6 +25,16 @@ declare class YServer<Env = unknown> extends Server<Env> {
18
25
  readonly document: WSSharedDoc;
19
26
  onLoad(): Promise<void>;
20
27
  onSave(): Promise<void>;
28
+ /**
29
+ * Replaces the document with a different state using Yjs UndoManager key remapping.
30
+ *
31
+ * @param snapshotUpdate - The snapshot update to replace the document with.
32
+ * @param getMetadata (optional) - A function that returns the type of the root for a given key.
33
+ */
34
+ unstable_replaceDocument(
35
+ snapshotUpdate: Uint8Array,
36
+ getMetadata?: (key: string) => YjsRootType
37
+ ): void;
21
38
  onStart(): Promise<void>;
22
39
  isReadOnly(connection: Connection): boolean;
23
40
  handleMessage(connection: Connection, message: WSMessage): void;
@@ -9,7 +9,17 @@ import debounce from "lodash.debounce";
9
9
  import { Server } from "partyserver";
10
10
  import * as awarenessProtocol from "y-protocols/awareness";
11
11
  import * as syncProtocol from "y-protocols/sync";
12
- import { applyUpdate, Doc as YDoc, encodeStateAsUpdate } from "yjs";
12
+ import {
13
+ applyUpdate,
14
+ Doc as YDoc,
15
+ encodeStateAsUpdate,
16
+ encodeStateVector,
17
+ UndoManager,
18
+ XmlText,
19
+ XmlElement,
20
+ XmlFragment
21
+ } from "yjs";
22
+ var snapshotOrigin = Symbol("snapshot-origin");
13
23
  var wsReadyStateConnecting = 0;
14
24
  var wsReadyStateOpen = 1;
15
25
  var messageSync = 0;
@@ -122,6 +132,58 @@ var YServer = class extends Server {
122
132
  }
123
133
  async onSave() {
124
134
  }
135
+ /**
136
+ * Replaces the document with a different state using Yjs UndoManager key remapping.
137
+ *
138
+ * @param snapshotUpdate - The snapshot update to replace the document with.
139
+ * @param getMetadata (optional) - A function that returns the type of the root for a given key.
140
+ */
141
+ unstable_replaceDocument(snapshotUpdate, getMetadata = () => "Map") {
142
+ try {
143
+ const doc = this.document;
144
+ const snapshotDoc = new YDoc();
145
+ applyUpdate(snapshotDoc, snapshotUpdate, snapshotOrigin);
146
+ const currentStateVector = encodeStateVector(doc);
147
+ const snapshotStateVector = encodeStateVector(snapshotDoc);
148
+ const changesSinceSnapshotUpdate = encodeStateAsUpdate(
149
+ doc,
150
+ snapshotStateVector
151
+ );
152
+ const undoManager = new UndoManager(
153
+ [...snapshotDoc.share.keys()].map((key) => {
154
+ const type = getMetadata(key);
155
+ if (type === "Text") {
156
+ return snapshotDoc.getText(key);
157
+ } else if (type === "Map") {
158
+ return snapshotDoc.getMap(key);
159
+ } else if (type === "Array") {
160
+ return snapshotDoc.getArray(key);
161
+ } else if (type === "XmlText") {
162
+ return snapshotDoc.get(key, XmlText);
163
+ } else if (type === "XmlElement") {
164
+ return snapshotDoc.get(key, XmlElement);
165
+ } else if (type === "XmlFragment") {
166
+ return snapshotDoc.get(key, XmlFragment);
167
+ }
168
+ throw new Error(`Unknown root type: ${type} for key: ${key}`);
169
+ }),
170
+ {
171
+ trackedOrigins: /* @__PURE__ */ new Set([snapshotOrigin])
172
+ }
173
+ );
174
+ applyUpdate(snapshotDoc, changesSinceSnapshotUpdate, snapshotOrigin);
175
+ undoManager.undo();
176
+ const documentChangesSinceSnapshotUpdate = encodeStateAsUpdate(
177
+ snapshotDoc,
178
+ currentStateVector
179
+ );
180
+ applyUpdate(this.document, documentChangesSinceSnapshotUpdate);
181
+ } catch (error) {
182
+ throw new Error(
183
+ `Failed to replace document: ${error instanceof Error ? error.message : "Unknown error"}`
184
+ );
185
+ }
186
+ }
125
187
  async onStart() {
126
188
  const src = await this.onLoad();
127
189
  if (src != null) {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/server/index.ts"],"sourcesContent":["import * as decoding from \"lib0/decoding\";\nimport * as encoding from \"lib0/encoding\";\nimport debounce from \"lodash.debounce\";\nimport type { Connection, ConnectionContext, WSMessage } from \"partyserver\";\nimport { Server } from \"partyserver\";\nimport * as awarenessProtocol from \"y-protocols/awareness\";\nimport * as syncProtocol from \"y-protocols/sync\";\nimport { applyUpdate, Doc as YDoc, encodeStateAsUpdate } from \"yjs\";\n\nimport { handleChunked } from \"../shared/chunking\";\n\nconst wsReadyStateConnecting = 0;\nconst wsReadyStateOpen = 1;\nconst wsReadyStateClosing = 2;\nconst wsReadyStateClosed = 3;\n\nconst messageSync = 0;\nconst messageAwareness = 1;\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) => send(doc, conn, message));\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 = 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\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 isReadOnly(connection: Connection): boolean {\n // to be implemented by the user\n return false;\n }\n\n // @ts-ignore something something typescript\n handleMessage(connection: Connection, message: WSMessage) {\n if (typeof message === \"string\") {\n console.warn(\n `Received non-binary message. Override onMessage on ${this.#ParentClass.name} to handle string messages if required`\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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","names":["encoder"]}
1
+ {"version":3,"sources":["../../src/server/index.ts"],"sourcesContent":["import * as decoding from \"lib0/decoding\";\nimport * as encoding from \"lib0/encoding\";\nimport debounce from \"lodash.debounce\";\nimport type { Connection, ConnectionContext, WSMessage } from \"partyserver\";\nimport { Server } from \"partyserver\";\nimport * as awarenessProtocol from \"y-protocols/awareness\";\nimport * as syncProtocol from \"y-protocols/sync\";\nimport {\n applyUpdate,\n Doc as YDoc,\n encodeStateAsUpdate,\n encodeStateVector,\n UndoManager,\n XmlText,\n XmlElement,\n XmlFragment\n} from \"yjs\";\n\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;\nconst wsReadyStateClosing = 2;\nconst wsReadyStateClosed = 3;\n\nconst messageSync = 0;\nconst messageAwareness = 1;\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) => send(doc, conn, message));\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 isReadOnly(connection: Connection): boolean {\n // to be implemented by the user\n return false;\n }\n\n // @ts-ignore something something typescript\n handleMessage(connection: Connection, message: WSMessage) {\n if (typeof message === \"string\") {\n console.warn(\n `Received non-binary message. Override onMessage on ${this.#ParentClass.name} to handle string messages if required`\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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","names":["encoder"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "y-partyserver",
3
- "version": "0.0.47",
3
+ "version": "0.0.48",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git://github.com/cloudflare/partykit.git"