y-partyserver 0.0.0-528adea → 0.0.0-5ea5287

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
@@ -1,54 +1,155 @@
1
1
  ## y-partyserver
2
2
 
3
- > [!CAUTION]
4
- > This project is in its experimental early stages and is not recommended for production use.
3
+ > [!WARNING]
4
+ > This project is experimental and is not yet recommended for production use.
5
5
 
6
- A [Yjs](https://docs.yjs.dev/) backend for [PartyServer](https://npmjs.com/package/partyserver). Build collaborative text editors and more, powered by [Durable Objects](https://developers.cloudflare.com/durable-objects/)!
6
+ [//]: # "keep in sync with packages/y-partyserver/README.md"
7
+ [//]: # "keep in sync with docs/reference/y-partyserver.md"
7
8
 
8
- Step 1: Define your server as a subclass of `YjsServer`
9
+ `y-partyserver` is an addon library for `partyserver` designed to host backends for [Yjs](https://yjs.dev), a high-performance library of data structures for building collaborative software.
10
+
11
+ _This document assumes some familiarity with Yjs. If you're new to Yjs, you can learn more about it in the [official documentation](https://docs.yjs.dev)._
12
+
13
+ ## Setting up a Yjs Server
14
+
15
+ Like PartyServer, `YServer` is a class that extends `DurableObject` (as well as PartyServer's `Server`). The simplest Yjs backend can be set up like so:
9
16
 
10
17
  ```ts
11
- import { YjsServer } from "y-partyserver";
18
+ export { YServer as MyYServer } from "y-partyserver";
12
19
 
13
- // define your YjsServer Durable Object
14
- export default class Document extends YjsServer {
15
- // optionally load / save with a database
16
- async onSave() {
17
- // this.document is a Y.Doc instance
18
- // that you can serialise to your database
20
+ // then setup wrangler.toml and a default fetch handler
21
+ // like you would for PartyServer.
22
+ ```
23
+
24
+ ## Connecting from the client
25
+
26
+ Use the provider to connect to this server from your client:
27
+
28
+ ```ts
29
+ import YProvider from "y-partyserver/provider";
30
+ import * as Y from "yjs";
31
+
32
+ const yDoc = new Y.Doc();
33
+
34
+ const provider = new YProvider("localhost:8787", "my-document-name", yDoc);
35
+ ```
36
+
37
+ You can add additional options to the provider:
38
+
39
+ ```tsx
40
+ const provider = new YProvider(
41
+ /* host */
42
+ "localhost:8787",
43
+ /* document/room name */
44
+ "my-document-name",
45
+ /* Yjs document instance */
46
+ yDoc,
47
+ {
48
+ /* whether to connect to the server immediately */
49
+ connect: false,
50
+ /* the party server path to connect to, defaults to "main" */
51
+ party: "my-party",
52
+ /* the path to the Yjs document on the server
53
+ * This replaces the default path of /parties/:party/:room.
54
+ */
55
+ prefix: "/my/own/path",
56
+ /* use your own Yjs awareness instance */
57
+ awareness: new awarenessProtocol.Awareness(yDoc),
58
+ /* query params to add to the websocket connection
59
+ * This can be an object or a function that returns an object
60
+ */
61
+ params: async () => ({
62
+ token: await getAuthToken()
63
+ }),
64
+ /* the WebSocket implementation to use
65
+ * This can be a polyfill or a custom implementation
66
+ */
67
+ WebSocketPolyfill: WebSocket,
68
+ /* the interval at which to resync the document
69
+ * This is set to -1 by default to disable resyncing by polling
70
+ */
71
+ resyncInterval: -1,
72
+ /* Maximum amount of time to wait before trying to reconnect
73
+ * (we try to reconnect using exponential backoff)
74
+ */
75
+ maxBackoffTimeout: 2500,
76
+
77
+ /* Disable cross-tab BroadcastChannel communication */
78
+ disableBc: false
19
79
  }
80
+ );
81
+ ```
82
+
83
+ ## Usage with React
84
+
85
+ If you're using React, then you can use the hook version of the provider: `useYProvider`.
86
+
87
+ ```ts
88
+ import useYProvider from "y-partyserver/react";
89
+
90
+ function App() {
91
+ const provider = useYProvider({
92
+ host: "localhost:8787", // optional, defaults to window.location.host
93
+ room: "my-document-name",
94
+ party: "my-party", // optional, defaults to "main"
95
+ doc: yDoc, // optional!
96
+ options
97
+ });
98
+ }
99
+ ```
100
+
101
+ ### Persistence
102
+
103
+ By default, PartyKit maintains a copy of the Yjs document as long as at least one client is connected to the server. When all clients disconnect, the document state may be lost.
104
+
105
+ To persist the Yjs document state between sessions, you can configure onLoad and onSave methods:
106
+
107
+ ```ts
108
+ // server.ts
109
+ import { YServer } from "y-partyserver";
110
+
111
+ export class MyDocument extends YServer {
112
+ /* control how often the onSave handler
113
+ * is called with these options */
114
+ static callbackOptions = {
115
+ // all of these are optional
116
+ debounceWait: /* number, default = */ 2000,
117
+ debounceMaxWait: /* number, default = */ 10000,
118
+ timeout: /* number, default = */ 5000
119
+ };
20
120
 
21
121
  async onLoad() {
22
- // this is called ONCE when the server "starts" up
23
- // you can apply any state to this.document
122
+ // load a document from a database, or some remote resource
123
+ // and apply it on to the Yjs document instance at `this.document`
124
+ const content = (await fetchDataFromExternalService(
125
+ this.name
126
+ )) as Uint8Array;
127
+ if (content) {
128
+ Y.applyUpdate(this.document, content);
129
+ }
130
+ return;
24
131
  }
25
- }
26
132
 
27
- // wire it up to your fetch handler
28
- export {
29
- async fetch(request: Request): Promise<Response> {
30
- return (
31
- // this will route requests to /parties/document/:id
32
- // to the Document Durable Object
33
- (await routePartykitRequest(request, this.env)) ||
34
- new Response("Not Found", { status: 404 })
133
+ async onSave() {
134
+ // called every few seconds after edits, and when the room empties
135
+ // you can use this to write to a database or some external storage
136
+
137
+ await sendDataToExternalService(
138
+ this.name,
139
+ Y.encodeStateAsUpdate(this.document) satisfies Uint8Array
35
140
  );
36
141
  }
37
142
  }
143
+ ```
38
144
 
145
+ `onLoad` is called once when a client connects to the server. It should initialise the Yjs document instance at `this.document`. Once the document has been loaded, it's kept in memory until the session ends.
39
146
 
40
- ```
147
+ `onSave` is called periodically after the document has been edited, and when the room is emptied. It should be used to save the document state to a database or some other external storage.
41
148
 
42
- Step 2: Configure `wrangler.toml`
149
+ ## Learn more
43
150
 
44
- ```toml
45
- [[durable_objects.bindings]]
46
- name = "Document"
47
- class_name = "Document"
151
+ For more information, refer to the [official Yjs documentation](https://docs.yjs.dev/ecosystem/editor-bindings). Examples provided in the Yjs documentation should work seamlessly with `y-partyserver` (ensure to replace `y-websocket` with `y-partyserver/provider`).
48
152
 
49
- [[migrations]]
50
- tag = "v1" # Should be unique for each entry
51
- new_classes = ["Document"]
52
- ```
153
+ ### TODO:
53
154
 
54
- Step 3: Connect from your client. Check your editor's documentation for how to connect to a Yjs server.
155
+ readonly mode
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  sendChunked
3
- } from "./chunk-UKCMZSNL.js";
3
+ } from "./chunk-TFXFSMIH.js";
4
4
 
5
5
  // src/provider/index.ts
6
6
  import * as bc from "lib0/broadcastchannel";
@@ -10,6 +10,7 @@ import * as math from "lib0/math";
10
10
  import { Observable } from "lib0/observable";
11
11
  import * as time from "lib0/time";
12
12
  import * as url from "lib0/url";
13
+ import { nanoid } from "nanoid";
13
14
  import * as authProtocol from "y-protocols/auth";
14
15
  import * as awarenessProtocol from "y-protocols/awareness";
15
16
  import * as syncProtocol from "y-protocols/sync";
@@ -402,32 +403,14 @@ var WebsocketProvider = class extends Observable {
402
403
  }
403
404
  }
404
405
  };
405
- function generateUUID() {
406
- if (crypto && crypto.randomUUID) {
407
- return crypto.randomUUID();
408
- }
409
- let d = (/* @__PURE__ */ new Date()).getTime();
410
- let d2 = typeof performance !== "undefined" && performance.now && performance.now() * 1e3 || 0;
411
- return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function(c) {
412
- let r = Math.random() * 16;
413
- if (d > 0) {
414
- r = (d + r) % 16 | 0;
415
- d = Math.floor(d / 16);
416
- } else {
417
- r = (d2 + r) % 16 | 0;
418
- d2 = Math.floor(d2 / 16);
419
- }
420
- return (c === "x" ? r : r & 3 | 8).toString(16);
421
- });
422
- }
423
406
  function assertType(value, label, type) {
424
407
  if (typeof value !== type) {
425
408
  throw new Error(
426
- `Invalid "${label}" parameter provided to YPartyServerProvider. Expected: ${type}, received: ${value}`
409
+ `Invalid "${label}" parameter provided to YProvider. Expected: ${type}, received: ${value}`
427
410
  );
428
411
  }
429
412
  }
430
- var YPartyServerProvider = class extends WebsocketProvider {
413
+ var YProvider = class extends WebsocketProvider {
431
414
  id;
432
415
  #params;
433
416
  constructor(host, room, doc, options = {}) {
@@ -438,7 +421,7 @@ var YPartyServerProvider = class extends WebsocketProvider {
438
421
  host.slice(0, -1);
439
422
  }
440
423
  const serverUrl = `${options.protocol || (host.startsWith("localhost:") || host.startsWith("127.0.0.1:") || host.startsWith("192.168.") || host.startsWith("10.") || host.startsWith("172.") && host.split(".")[1] >= "16" && host.split(".")[1] <= "31" ? "ws" : "wss")}://${host}${options.prefix || `/parties/${options.party || "main"}`}`;
441
- const id = options.connectionId ?? generateUUID();
424
+ const id = options.connectionId ?? nanoid(10);
442
425
  const { params, connect = true, ...rest } = options;
443
426
  const baseOptions = {
444
427
  ...rest,
@@ -480,6 +463,6 @@ export {
480
463
  messageAwareness,
481
464
  messageAuth,
482
465
  WebsocketProvider,
483
- YPartyServerProvider
466
+ YProvider
484
467
  };
485
- //# sourceMappingURL=chunk-ZH6V7DD2.js.map
468
+ //# sourceMappingURL=chunk-2D4YILH5.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"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 // ignore text messages\n return;\n }\n provider.wsLastMessageReceived = time.getUnixTime();\n // eslint-disable-next-line @typescript-eslint/no-unsafe-argument\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 */\n// eslint-disable-next-line deprecation/deprecation\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 +\n \"/\" +\n roomname +\n (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"],"mappings":"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","names":[]}
@@ -11,7 +11,7 @@ var sendChunked = (data, ws) => {
11
11
  }
12
12
  if (!warnedAboutLargeMessage) {
13
13
  console.warn(
14
- "[y-partykit]",
14
+ "[y-partyserver]",
15
15
  "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.",
16
16
  `Message size: ${(data.byteLength / 1e3 / 1e3).toFixed(1)}MB`
17
17
  );
@@ -123,4 +123,4 @@ export {
123
123
  sendChunked,
124
124
  handleChunked
125
125
  };
126
- //# sourceMappingURL=chunk-UKCMZSNL.js.map
126
+ //# sourceMappingURL=chunk-TFXFSMIH.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"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 } 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 MessageData = ArrayBufferLike | string;\ntype MessageHandler = (conn: Connection, message: MessageData) => 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: MessageData) => void\n): MessageHandler => {\n let batch: ArrayBuffer[] | undefined;\n let start: Batch | undefined;\n\n return (connection, message) => {\n if (typeof message === \"string\") {\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 // eslint-disable-next-line @typescript-eslint/ban-ts-comment\n // @ts-ignore typescript hell\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 // eslint-disable-next-line @typescript-eslint/ban-ts-comment\n // @ts-ignore typescript hell\n batch.push(message);\n } else {\n // eslint-disable-next-line @typescript-eslint/ban-ts-comment\n // @ts-ignore 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: MessageData): 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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","names":[]}
@@ -99,31 +99,28 @@ declare class WebsocketProvider extends Observable<string> {
99
99
  type Params = Record<string, string | null | undefined>;
100
100
  type ParamsProvider = Params | (() => Params | Promise<Params>);
101
101
  type BaseProviderOptions = ConstructorParameters<typeof WebsocketProvider>[3];
102
- type YPartyServerProviderOptions = Omit<
103
- NonNullable<BaseProviderOptions>,
104
- "params"
105
- > & {
102
+ type YProviderOptions = Omit<NonNullable<BaseProviderOptions>, "params"> & {
106
103
  connectionId?: string;
107
104
  party?: string;
108
105
  prefix?: string;
109
106
  params?: ParamsProvider;
110
107
  protocol?: "ws" | "wss";
111
108
  };
112
- declare class YPartyServerProvider extends WebsocketProvider {
109
+ declare class YProvider extends WebsocketProvider {
113
110
  #private;
114
111
  id: string;
115
112
  constructor(
116
113
  host: string,
117
114
  room: string,
118
115
  doc?: Doc,
119
- options?: YPartyServerProviderOptions
116
+ options?: YProviderOptions
120
117
  );
121
118
  connect(): Promise<void>;
122
119
  }
123
120
 
124
121
  export {
125
122
  WebsocketProvider,
126
- YPartyServerProvider as default,
123
+ YProvider as default,
127
124
  messageAuth,
128
125
  messageAwareness,
129
126
  messageQueryAwareness,
@@ -1,15 +1,15 @@
1
1
  import {
2
2
  WebsocketProvider,
3
- YPartyServerProvider,
3
+ YProvider,
4
4
  messageAuth,
5
5
  messageAwareness,
6
6
  messageQueryAwareness,
7
7
  messageSync
8
- } from "../chunk-ZH6V7DD2.js";
9
- import "../chunk-UKCMZSNL.js";
8
+ } from "../chunk-2D4YILH5.js";
9
+ import "../chunk-TFXFSMIH.js";
10
10
  export {
11
11
  WebsocketProvider,
12
- YPartyServerProvider as default,
12
+ YProvider as default,
13
13
  messageAuth,
14
14
  messageAwareness,
15
15
  messageQueryAwareness,
@@ -1,22 +1,20 @@
1
1
  import * as Y from "yjs";
2
2
 
3
- import YPartyServerProvider from "./index.js";
3
+ import YProvider from "./index.js";
4
4
 
5
5
  import "lib0/decoding";
6
6
  import "lib0/encoding";
7
7
  import "lib0/observable";
8
8
  import "y-protocols/awareness";
9
9
 
10
- type UseYPartyServerProviderOptions = {
10
+ type UseYProviderOptions = {
11
11
  host?: string | undefined;
12
12
  room: string;
13
13
  party?: string;
14
14
  doc?: Y.Doc;
15
15
  prefix?: string;
16
- options?: ConstructorParameters<typeof YPartyServerProvider>[3];
16
+ options?: ConstructorParameters<typeof YProvider>[3];
17
17
  };
18
- declare function useYProvider(
19
- yProviderOptions: UseYPartyServerProviderOptions
20
- ): YPartyServerProvider;
18
+ declare function useYProvider(yProviderOptions: UseYProviderOptions): YProvider;
21
19
 
22
20
  export { useYProvider as default };
@@ -1,14 +1,14 @@
1
1
  import {
2
- YPartyServerProvider
3
- } from "../chunk-ZH6V7DD2.js";
4
- import "../chunk-UKCMZSNL.js";
2
+ YProvider
3
+ } from "../chunk-2D4YILH5.js";
4
+ import "../chunk-TFXFSMIH.js";
5
5
 
6
6
  // src/provider/react.tsx
7
7
  import { useEffect, useState } from "react";
8
8
  function useYProvider(yProviderOptions) {
9
9
  const { host, room, party, doc, options, prefix } = yProviderOptions;
10
10
  const [provider] = useState(
11
- () => new YPartyServerProvider(
11
+ () => new YProvider(
12
12
  host || (typeof window !== "undefined" ? window.location.host : "dummy-domain.com"),
13
13
  room,
14
14
  doc,
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/provider/react.tsx"],"sourcesContent":["import { useEffect, useState } from \"react\";\n\nimport YPartyServerProvider from \"./index\";\n\nimport type * as Y from \"yjs\";\n\ntype UseYPartyServerProviderOptions = {\n host?: string | undefined;\n room: string;\n party?: string;\n doc?: Y.Doc;\n prefix?: string;\n options?: ConstructorParameters<typeof YPartyServerProvider>[3];\n};\n\nexport default function useYProvider(\n yProviderOptions: UseYPartyServerProviderOptions\n) {\n const { host, room, party, doc, options, prefix } = yProviderOptions;\n const [provider] = useState<YPartyServerProvider>(\n () =>\n new YPartyServerProvider(\n host ||\n (typeof window !== \"undefined\"\n ? window.location.host\n : \"dummy-domain.com\"),\n room,\n doc,\n {\n connect: false,\n party,\n prefix,\n ...options\n }\n )\n );\n\n useEffect(() => {\n void provider.connect();\n return () => provider.disconnect();\n }, [provider]);\n return provider;\n}\n"],"mappings":";;;;;;AAAA,SAAS,WAAW,gBAAgB;AAerB,SAAR,aACL,kBACA;AACA,QAAM,EAAE,MAAM,MAAM,OAAO,KAAK,SAAS,OAAO,IAAI;AACpD,QAAM,CAAC,QAAQ,IAAI;AAAA,IACjB,MACE,IAAI;AAAA,MACF,SACG,OAAO,WAAW,cACf,OAAO,SAAS,OAChB;AAAA,MACN;AAAA,MACA;AAAA,MACA;AAAA,QACE,SAAS;AAAA,QACT;AAAA,QACA;AAAA,QACA,GAAG;AAAA,MACL;AAAA,IACF;AAAA,EACJ;AAEA,YAAU,MAAM;AACd,SAAK,SAAS,QAAQ;AACtB,WAAO,MAAM,SAAS,WAAW;AAAA,EACnC,GAAG,CAAC,QAAQ,CAAC;AACb,SAAO;AACT;","names":[]}
1
+ {"version":3,"sources":["../../src/provider/react.tsx"],"sourcesContent":["import { useEffect, useState } from \"react\";\n\nimport YProvider from \"./index\";\n\nimport type * as Y from \"yjs\";\n\ntype UseYProviderOptions = {\n host?: string | undefined;\n room: string;\n party?: string;\n doc?: Y.Doc;\n prefix?: string;\n options?: ConstructorParameters<typeof YProvider>[3];\n};\n\nexport default function useYProvider(yProviderOptions: UseYProviderOptions) {\n const { host, room, party, doc, options, prefix } = yProviderOptions;\n const [provider] = useState<YProvider>(\n () =>\n new YProvider(\n host ||\n (typeof window !== \"undefined\"\n ? window.location.host\n : \"dummy-domain.com\"),\n room,\n doc,\n {\n connect: false,\n party,\n prefix,\n ...options\n }\n )\n );\n\n useEffect(() => {\n void provider.connect();\n return () => provider.disconnect();\n }, [provider]);\n return provider;\n}\n"],"mappings":";;;;;;AAAA,SAAS,WAAW,gBAAgB;AAerB,SAAR,aAA8B,kBAAuC;AAC1E,QAAM,EAAE,MAAM,MAAM,OAAO,KAAK,SAAS,OAAO,IAAI;AACpD,QAAM,CAAC,QAAQ,IAAI;AAAA,IACjB,MACE,IAAI;AAAA,MACF,SACG,OAAO,WAAW,cACf,OAAO,SAAS,OAChB;AAAA,MACN;AAAA,MACA;AAAA,MACA;AAAA,QACE,SAAS;AAAA,QACT;AAAA,QACA;AAAA,QACA,GAAG;AAAA,MACL;AAAA,IACF;AAAA,EACJ;AAEA,YAAU,MAAM;AACd,SAAK,SAAS,QAAQ;AACtB,WAAO,MAAM,SAAS,WAAW;AAAA,EACnC,GAAG,CAAC,QAAQ,CAAC;AACb,SAAO;AACT;","names":[]}
@@ -12,7 +12,7 @@ interface CallbackOptions {
12
12
  debounceMaxWait?: number;
13
13
  timeout?: number;
14
14
  }
15
- declare class YjsDocument<Env = unknown> extends Server<Env> {
15
+ declare class YServer<Env = unknown> extends Server<Env> {
16
16
  #private;
17
17
  static callbackOptions: CallbackOptions;
18
18
  readonly document: WSSharedDoc;
@@ -29,4 +29,4 @@ declare class YjsDocument<Env = unknown> extends Server<Env> {
29
29
  onConnect(conn: Connection<unknown>, _ctx: ConnectionContext): void;
30
30
  }
31
31
 
32
- export { YjsDocument };
32
+ export { YServer };
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  handleChunked
3
- } from "../chunk-UKCMZSNL.js";
3
+ } from "../chunk-TFXFSMIH.js";
4
4
 
5
5
  // src/server/index.ts
6
6
  import * as decoding from "lib0/decoding";
@@ -113,7 +113,7 @@ function send(doc, conn, m) {
113
113
  closeConn(doc, conn);
114
114
  }
115
115
  }
116
- var YjsDocument = class extends Server {
116
+ var YServer = class extends Server {
117
117
  static callbackOptions = {};
118
118
  // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access
119
119
  #ParentClass = Object.getPrototypeOf(this).constructor;
@@ -215,6 +215,6 @@ var YjsDocument = class extends Server {
215
215
  }
216
216
  };
217
217
  export {
218
- YjsDocument
218
+ YServer
219
219
  };
220
220
  //# sourceMappingURL=index.js.map
@@ -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 { Server } from \"partyserver\";\nimport * as awarenessProtocol from \"y-protocols/awareness\";\nimport * as syncProtocol from \"y-protocols/sync\";\nimport { applyUpdate, encodeStateAsUpdate, Doc as YDoc } from \"yjs\";\n\nimport { handleChunked } from \"../shared/chunking\";\n\nimport type { Connection, ConnectionContext } from \"partyserver\";\n\nconst wsReadyStateConnecting = 0;\nconst wsReadyStateOpen = 1;\nconst wsReadyStateClosing = 2; // eslint-disable-line\nconst wsReadyStateClosed = 3; // eslint-disable-line\n\nconst messageSync = 0;\nconst messageAwareness = 1;\nconst messageAuth = 2; // eslint-disable-line\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 // eslint-disable-next-line @typescript-eslint/no-unused-vars\n } catch (e) {\n closeConn(doc, conn);\n }\n}\n\ninterface CallbackOptions {\n debounceWait?: number;\n debounceMaxWait?: number;\n timeout?: number;\n}\n\nexport class YjsDocument<Env = unknown> extends Server<Env> {\n static callbackOptions: CallbackOptions = {};\n\n // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access\n #ParentClass: typeof YjsDocument = Object.getPrototypeOf(this).constructor;\n readonly document = new WSSharedDoc();\n\n // eslint-disable-next-line @typescript-eslint/require-await\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 onMessage = handleChunked((conn, message) => {\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 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 conn,\n // TODO: readonly conections\n false\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, conn, encoding.toUint8Array(encoder));\n }\n break;\n case messageAwareness: {\n awarenessProtocol.applyAwarenessUpdate(\n this.document.awareness,\n decoding.readVarUint8Array(decoder),\n conn\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 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(conn: Connection<unknown>, _ctx: ConnectionContext) {\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 { Server } from \"partyserver\";\nimport * as awarenessProtocol from \"y-protocols/awareness\";\nimport * as syncProtocol from \"y-protocols/sync\";\nimport { applyUpdate, encodeStateAsUpdate, Doc as YDoc } from \"yjs\";\n\nimport { handleChunked } from \"../shared/chunking\";\n\nimport type { Connection, ConnectionContext } from \"partyserver\";\n\nconst wsReadyStateConnecting = 0;\nconst wsReadyStateOpen = 1;\nconst wsReadyStateClosing = 2; // eslint-disable-line\nconst wsReadyStateClosed = 3; // eslint-disable-line\n\nconst messageSync = 0;\nconst messageAwareness = 1;\nconst messageAuth = 2; // eslint-disable-line\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 // eslint-disable-next-line @typescript-eslint/no-unused-vars\n } catch (e) {\n closeConn(doc, conn);\n }\n}\n\ninterface 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 // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access\n #ParentClass: typeof YServer = Object.getPrototypeOf(this).constructor;\n readonly document = new WSSharedDoc();\n\n // eslint-disable-next-line @typescript-eslint/require-await\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 onMessage = handleChunked((conn, message) => {\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 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 conn,\n // TODO: readonly conections\n false\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, conn, encoding.toUint8Array(encoder));\n }\n break;\n case messageAwareness: {\n awarenessProtocol.applyAwarenessUpdate(\n this.document.awareness,\n decoding.readVarUint8Array(decoder),\n conn\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 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(conn: Connection<unknown>, _ctx: ConnectionContext) {\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.0-528adea",
3
+ "version": "0.0.0-5ea5287",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git://github.com/threepointone/partyserver.git"
@@ -35,23 +35,24 @@
35
35
  "collaboration",
36
36
  "text-editors"
37
37
  ],
38
- "author": "Sunil Pai <threepointone@gmail.com>",
38
+ "author": "Sunil Pai <spai@cloudflare.com>",
39
39
  "license": "ISC",
40
40
  "description": "",
41
41
  "dependencies": {
42
42
  "lib0": "^0.2.95",
43
43
  "lodash.debounce": "^4.0.8",
44
+ "nanoid": "^5.0.7",
44
45
  "y-protocols": "^1.0.6"
45
46
  },
46
47
  "peerDependencies": {
47
- "@cloudflare/workers-types": "^4.20240718.0",
48
- "partyserver": "0.0.0-528adea",
48
+ "@cloudflare/workers-types": "^4.20240729.0",
49
+ "partyserver": "0.0.0-5ea5287",
49
50
  "yjs": "^13.6.14"
50
51
  },
51
52
  "devDependencies": {
52
53
  "@cloudflare/workers-types": "^4.20240729.0",
53
54
  "@types/lodash.debounce": "^4.0.9",
54
- "partyserver": "0.0.0-528adea",
55
+ "partyserver": "0.0.0-5ea5287",
55
56
  "ws": "^8.18.0",
56
57
  "yjs": "^13.6.18"
57
58
  }
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/shared/chunking.ts"],"sourcesContent":["// This file contains a shared implementation of chunking logic for binary\n// WebSocket messages. Because the PartyKit platform limits individual\n\nimport type { Connection } from \"partyserver\";\n\n// WebSocket messages to 1MB, we need to split larger messages into chunks.\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 MessageData = ArrayBufferLike | string;\ntype MessageHandler = (conn: Connection, message: MessageData) => 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-partykit]\",\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: MessageData) => void\n): MessageHandler => {\n let batch: ArrayBuffer[] | undefined;\n let start: Batch | undefined;\n\n return (connection, message) => {\n if (typeof message === \"string\") {\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 // eslint-disable-next-line @typescript-eslint/ban-ts-comment\n // @ts-ignore typescript hell\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 // eslint-disable-next-line @typescript-eslint/ban-ts-comment\n // @ts-ignore typescript hell\n batch.push(message);\n } else {\n // eslint-disable-next-line @typescript-eslint/ban-ts-comment\n // @ts-ignore 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: MessageData): 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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","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"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 * 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 // ignore text messages\n return;\n }\n provider.wsLastMessageReceived = time.getUnixTime();\n // eslint-disable-next-line @typescript-eslint/no-unsafe-argument\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 */\n// eslint-disable-next-line deprecation/deprecation\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 +\n \"/\" +\n roomname +\n (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 generateUUID(): string {\n // Public Domain/MIT\n if (crypto && crypto.randomUUID) {\n return crypto.randomUUID();\n }\n let d = new Date().getTime(); //Timestamp\n let d2 =\n (typeof performance !== \"undefined\" &&\n performance.now &&\n performance.now() * 1000) ||\n 0; //Time in microseconds since page-load or 0 if unsupported\n return \"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx\".replace(/[xy]/g, function (c) {\n let r = Math.random() * 16; //random number between 0 and 16\n if (d > 0) {\n //Use timestamp until depleted\n r = (d + r) % 16 | 0;\n d = Math.floor(d / 16);\n } else {\n //Use microseconds since page-load if supported\n r = (d2 + r) % 16 | 0;\n d2 = Math.floor(d2 / 16);\n }\n return (c === \"x\" ? r : (r & 0x3) | 0x8).toString(16);\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 YPartyServerProvider. 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 YPartyServerProviderOptions = Omit<\n NonNullable<BaseProviderOptions>,\n \"params\"\n> & {\n connectionId?: string;\n party?: string;\n prefix?: string;\n params?: ParamsProvider;\n protocol?: \"ws\" | \"wss\";\n};\n\nexport default class YPartyServerProvider extends WebsocketProvider {\n id: string;\n #params?: ParamsProvider;\n\n constructor(\n host: string,\n room: string,\n doc?: YDoc,\n options: YPartyServerProviderOptions = {}\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 ?? generateUUID();\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"],"mappings":"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","names":[]}