y-partyserver 0.0.0-01b5782

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 ADDED
@@ -0,0 +1,54 @@
1
+ ## y-partyserver
2
+
3
+ > [!CAUTION]
4
+ > This project is in its experimental early stages and is not recommended for production use.
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/)!
7
+
8
+ Step 1: Define your server as a subclass of `YjsServer`
9
+
10
+ ```ts
11
+ import { YjsServer } from "y-partyserver";
12
+
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
19
+ }
20
+
21
+ async onLoad() {
22
+ // this is called ONCE when the server "starts" up
23
+ // you can apply any state to this.document
24
+ }
25
+ }
26
+
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 Server.partyFetch(request, this.env)) ||
34
+ new Response("Not Found", { status: 404 })
35
+ );
36
+ }
37
+ }
38
+
39
+
40
+ ```
41
+
42
+ Step 2: Configure `wrangler.toml`
43
+
44
+ ```toml
45
+ [[durable_objects.bindings]]
46
+ name = "Document"
47
+ class_name = "Document"
48
+
49
+ [[migrations]]
50
+ tag = "v1" # Should be unique for each entry
51
+ new_classes = ["Document"]
52
+ ```
53
+
54
+ Step 3: Connect from your client. Check your editor's documentation for how to connect to a Yjs server.
@@ -0,0 +1,126 @@
1
+ // src/shared/chunking.ts
2
+ var CHUNK_MAX_SIZE = 1e6;
3
+ var BATCH_SENTINEL = "y-pk-batch";
4
+ var TRACE_ENABLED = false;
5
+ var trace = (...args) => TRACE_ENABLED && console.log(...args);
6
+ var warnedAboutLargeMessage = false;
7
+ var sendChunked = (data, ws) => {
8
+ if (data.byteLength <= CHUNK_MAX_SIZE) {
9
+ ws.send(data);
10
+ return;
11
+ }
12
+ if (!warnedAboutLargeMessage) {
13
+ console.warn(
14
+ "[y-partykit]",
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
+ `Message size: ${(data.byteLength / 1e3 / 1e3).toFixed(1)}MB`
17
+ );
18
+ warnedAboutLargeMessage = true;
19
+ }
20
+ const id = (Date.now() + Math.random()).toString(36).substring(10);
21
+ const chunks = Math.ceil(data.byteLength / CHUNK_MAX_SIZE);
22
+ ws.send(
23
+ serializeBatchMarker({
24
+ id,
25
+ type: "start",
26
+ size: data.byteLength,
27
+ count: chunks
28
+ })
29
+ );
30
+ let sentSize = 0;
31
+ let sentChunks = 0;
32
+ for (let i = 0; i < chunks; i++) {
33
+ const chunk = data.slice(CHUNK_MAX_SIZE * i, CHUNK_MAX_SIZE * (i + 1));
34
+ ws.send(chunk);
35
+ sentChunks += 1;
36
+ sentSize += chunk.byteLength;
37
+ }
38
+ ws.send(
39
+ serializeBatchMarker({
40
+ id,
41
+ type: "end",
42
+ size: sentSize,
43
+ count: sentChunks
44
+ })
45
+ );
46
+ };
47
+ var handleChunked = (receive) => {
48
+ let batch;
49
+ let start;
50
+ return (connection, message) => {
51
+ if (typeof message === "string") {
52
+ return;
53
+ }
54
+ if (isBatchSentinel(message)) {
55
+ const marker = parseBatchMarker(message);
56
+ if (marker.type === "start") {
57
+ batch = [];
58
+ start = marker;
59
+ }
60
+ if (marker.type === "end") {
61
+ if (batch) {
62
+ try {
63
+ assertEquality(start == null ? void 0 : start.id, marker.id, "client id");
64
+ assertEquality(start == null ? void 0 : start.count, marker.count, "client counts");
65
+ assertEquality(start == null ? void 0 : start.size, marker.size, "client size");
66
+ const size = batch.reduce(
67
+ (sum, buffer) => sum + buffer.byteLength,
68
+ 0
69
+ );
70
+ const bytes = new Uint8Array(size);
71
+ let bytesWritten = 0;
72
+ for (const chunk of batch) {
73
+ bytes.set(new Uint8Array(chunk), bytesWritten);
74
+ bytesWritten += chunk.byteLength;
75
+ }
76
+ assertEquality(marker.count, batch.length, "received batch count");
77
+ assertEquality(marker.size, bytesWritten, "client size");
78
+ receive(connection, bytes);
79
+ } catch (e) {
80
+ console.error(e);
81
+ throw e;
82
+ } finally {
83
+ batch = void 0;
84
+ start = void 0;
85
+ }
86
+ }
87
+ }
88
+ } else if (batch) {
89
+ batch.push(message);
90
+ } else {
91
+ receive(connection, new Uint8Array(message));
92
+ }
93
+ };
94
+ };
95
+ function assertEquality(expected, actual, label) {
96
+ if (expected !== actual) {
97
+ throw new Error(
98
+ `Mismatching ${label}! Expected ${expected}, got ${actual}`
99
+ );
100
+ } else {
101
+ trace(`Matching ${label}: ${expected}`);
102
+ }
103
+ }
104
+ function isBatchSentinel(msg) {
105
+ return typeof msg === "string" && msg.startsWith(BATCH_SENTINEL);
106
+ }
107
+ function serializeBatchMarker(batch) {
108
+ return `${BATCH_SENTINEL}#${JSON.stringify(batch)}`;
109
+ }
110
+ function parseBatchMarker(msg) {
111
+ const [sentinel, data] = msg.split("#", 2);
112
+ if (sentinel !== BATCH_SENTINEL) {
113
+ throw new Error("Unexpected batch marker: " + msg);
114
+ }
115
+ const batch = JSON.parse(data);
116
+ if (batch.type !== "start" && batch.type !== "end") {
117
+ throw new Error("Unexpected batch data: " + msg);
118
+ }
119
+ return batch;
120
+ }
121
+
122
+ export {
123
+ sendChunked,
124
+ handleChunked
125
+ };
126
+ //# sourceMappingURL=chunk-UKCMZSNL.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 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":[]}
@@ -0,0 +1,483 @@
1
+ import {
2
+ sendChunked
3
+ } from "./chunk-UKCMZSNL.js";
4
+
5
+ // src/provider/index.ts
6
+ import * as bc from "lib0/broadcastchannel";
7
+ import * as decoding from "lib0/decoding";
8
+ import * as encoding from "lib0/encoding";
9
+ import * as math from "lib0/math";
10
+ import { Observable } from "lib0/observable";
11
+ import * as time from "lib0/time";
12
+ import * as url from "lib0/url";
13
+ import * as authProtocol from "y-protocols/auth";
14
+ import * as awarenessProtocol from "y-protocols/awareness";
15
+ import * as syncProtocol from "y-protocols/sync";
16
+ import { Doc as YDoc } from "yjs";
17
+ var messageSync = 0;
18
+ var messageQueryAwareness = 3;
19
+ var messageAwareness = 1;
20
+ var messageAuth = 2;
21
+ var DEFAULT_DISABLE_BC = typeof window === "undefined";
22
+ function assert(condition, message) {
23
+ if (!condition) {
24
+ throw new Error(message);
25
+ }
26
+ }
27
+ var messageHandlers = [];
28
+ messageHandlers[messageSync] = (encoder, decoder, provider, emitSynced, _messageType) => {
29
+ encoding.writeVarUint(encoder, messageSync);
30
+ const syncMessageType = syncProtocol.readSyncMessage(
31
+ decoder,
32
+ encoder,
33
+ provider.doc,
34
+ provider
35
+ );
36
+ if (emitSynced && syncMessageType === syncProtocol.messageYjsSyncStep2 && !provider.synced) {
37
+ provider.synced = true;
38
+ }
39
+ };
40
+ messageHandlers[messageQueryAwareness] = (encoder, _decoder, provider, _emitSynced, _messageType) => {
41
+ encoding.writeVarUint(encoder, messageAwareness);
42
+ encoding.writeVarUint8Array(
43
+ encoder,
44
+ awarenessProtocol.encodeAwarenessUpdate(
45
+ provider.awareness,
46
+ Array.from(provider.awareness.getStates().keys())
47
+ )
48
+ );
49
+ };
50
+ messageHandlers[messageAwareness] = (_encoder, decoder, provider, _emitSynced, _messageType) => {
51
+ awarenessProtocol.applyAwarenessUpdate(
52
+ provider.awareness,
53
+ decoding.readVarUint8Array(decoder),
54
+ provider
55
+ );
56
+ };
57
+ messageHandlers[messageAuth] = (_encoder, decoder, provider, _emitSynced, _messageType) => {
58
+ authProtocol.readAuthMessage(
59
+ decoder,
60
+ provider.doc,
61
+ (_ydoc, reason) => permissionDeniedHandler(provider, reason)
62
+ );
63
+ };
64
+ var messageReconnectTimeout = 3e4;
65
+ function permissionDeniedHandler(provider, reason) {
66
+ console.warn(`Permission denied to access ${provider.url}.
67
+ ${reason}`);
68
+ }
69
+ function readMessage(provider, buf, emitSynced) {
70
+ const decoder = decoding.createDecoder(buf);
71
+ const encoder = encoding.createEncoder();
72
+ const messageType = decoding.readVarUint(decoder);
73
+ const messageHandler = provider.messageHandlers[messageType];
74
+ if (
75
+ /** @type {any} */
76
+ messageHandler
77
+ ) {
78
+ messageHandler(encoder, decoder, provider, emitSynced, messageType);
79
+ } else {
80
+ console.error("Unable to compute message");
81
+ }
82
+ return encoder;
83
+ }
84
+ function setupWS(provider) {
85
+ if (provider.shouldConnect && provider.ws === null) {
86
+ if (!provider._WS) {
87
+ throw new Error(
88
+ "No WebSocket implementation available, did you forget to pass options.WebSocketPolyfill?"
89
+ );
90
+ }
91
+ const websocket = new provider._WS(provider.url);
92
+ websocket.binaryType = "arraybuffer";
93
+ provider.ws = websocket;
94
+ provider.wsconnecting = true;
95
+ provider.wsconnected = false;
96
+ provider.synced = false;
97
+ websocket.addEventListener("message", (event) => {
98
+ if (typeof event.data === "string") {
99
+ return;
100
+ }
101
+ provider.wsLastMessageReceived = time.getUnixTime();
102
+ const encoder = readMessage(provider, new Uint8Array(event.data), true);
103
+ if (encoding.length(encoder) > 1) {
104
+ sendChunked(encoding.toUint8Array(encoder), websocket);
105
+ }
106
+ });
107
+ websocket.addEventListener("error", (event) => {
108
+ provider.emit("connection-error", [event, provider]);
109
+ });
110
+ websocket.addEventListener("close", (event) => {
111
+ provider.emit("connection-close", [event, provider]);
112
+ provider.ws = null;
113
+ provider.wsconnecting = false;
114
+ if (provider.wsconnected) {
115
+ provider.wsconnected = false;
116
+ provider.synced = false;
117
+ awarenessProtocol.removeAwarenessStates(
118
+ provider.awareness,
119
+ Array.from(provider.awareness.getStates().keys()).filter(
120
+ (client) => client !== provider.doc.clientID
121
+ ),
122
+ provider
123
+ );
124
+ provider.emit("status", [
125
+ {
126
+ status: "disconnected"
127
+ }
128
+ ]);
129
+ } else {
130
+ provider.wsUnsuccessfulReconnects++;
131
+ }
132
+ setTimeout(
133
+ setupWS,
134
+ math.min(
135
+ math.pow(2, provider.wsUnsuccessfulReconnects) * 100,
136
+ provider.maxBackoffTime
137
+ ),
138
+ provider
139
+ );
140
+ });
141
+ websocket.addEventListener("open", () => {
142
+ provider.wsLastMessageReceived = time.getUnixTime();
143
+ provider.wsconnecting = false;
144
+ provider.wsconnected = true;
145
+ provider.wsUnsuccessfulReconnects = 0;
146
+ provider.emit("status", [
147
+ {
148
+ status: "connected"
149
+ }
150
+ ]);
151
+ const encoder = encoding.createEncoder();
152
+ encoding.writeVarUint(encoder, messageSync);
153
+ syncProtocol.writeSyncStep1(encoder, provider.doc);
154
+ sendChunked(encoding.toUint8Array(encoder), websocket);
155
+ if (provider.awareness.getLocalState() !== null) {
156
+ const encoderAwarenessState = encoding.createEncoder();
157
+ encoding.writeVarUint(encoderAwarenessState, messageAwareness);
158
+ encoding.writeVarUint8Array(
159
+ encoderAwarenessState,
160
+ awarenessProtocol.encodeAwarenessUpdate(provider.awareness, [
161
+ provider.doc.clientID
162
+ ])
163
+ );
164
+ sendChunked(encoding.toUint8Array(encoderAwarenessState), websocket);
165
+ }
166
+ });
167
+ provider.emit("status", [
168
+ {
169
+ status: "connecting"
170
+ }
171
+ ]);
172
+ }
173
+ }
174
+ function broadcastMessage(provider, buf) {
175
+ const ws = provider.ws;
176
+ if (provider.wsconnected && ws && ws.readyState === ws.OPEN) {
177
+ sendChunked(buf, ws);
178
+ }
179
+ if (provider.bcconnected) {
180
+ bc.publish(provider.bcChannel, buf, provider);
181
+ }
182
+ }
183
+ var DefaultWebSocket = typeof WebSocket === "undefined" ? null : WebSocket;
184
+ var WebsocketProvider = class extends Observable {
185
+ maxBackoffTime;
186
+ bcChannel;
187
+ url;
188
+ roomname;
189
+ doc;
190
+ _WS;
191
+ awareness;
192
+ wsconnected;
193
+ wsconnecting;
194
+ bcconnected;
195
+ disableBc;
196
+ wsUnsuccessfulReconnects;
197
+ messageHandlers;
198
+ _synced;
199
+ ws;
200
+ wsLastMessageReceived;
201
+ shouldConnect;
202
+ // Whether to connect to other peers or not
203
+ _resyncInterval;
204
+ _bcSubscriber;
205
+ _updateHandler;
206
+ _awarenessUpdateHandler;
207
+ _unloadHandler;
208
+ _checkInterval;
209
+ constructor(serverUrl, roomname, doc, {
210
+ connect = true,
211
+ awareness = new awarenessProtocol.Awareness(doc),
212
+ params = {},
213
+ WebSocketPolyfill = DefaultWebSocket,
214
+ // Optionally provide a WebSocket polyfill
215
+ resyncInterval = -1,
216
+ // Request server state every `resyncInterval` milliseconds
217
+ maxBackoffTime = 2500,
218
+ // Maximum amount of time to wait before trying to reconnect (we try to reconnect using exponential backoff)
219
+ disableBc = DEFAULT_DISABLE_BC
220
+ // Disable cross-tab BroadcastChannel communication
221
+ } = {}) {
222
+ super();
223
+ while (serverUrl[serverUrl.length - 1] === "/") {
224
+ serverUrl = serverUrl.slice(0, serverUrl.length - 1);
225
+ }
226
+ const encodedParams = url.encodeQueryParams(params);
227
+ this.maxBackoffTime = maxBackoffTime;
228
+ this.bcChannel = serverUrl + "/" + roomname;
229
+ this.url = serverUrl + "/" + roomname + (encodedParams.length === 0 ? "" : "?" + encodedParams);
230
+ this.roomname = roomname;
231
+ this.doc = doc;
232
+ this._WS = WebSocketPolyfill;
233
+ this.awareness = awareness;
234
+ this.wsconnected = false;
235
+ this.wsconnecting = false;
236
+ this.bcconnected = false;
237
+ this.disableBc = disableBc;
238
+ this.wsUnsuccessfulReconnects = 0;
239
+ this.messageHandlers = messageHandlers.slice();
240
+ this._synced = false;
241
+ this.ws = null;
242
+ this.wsLastMessageReceived = 0;
243
+ this.shouldConnect = connect;
244
+ this._resyncInterval = 0;
245
+ if (resyncInterval > 0) {
246
+ this._resyncInterval = /** @type {any} */
247
+ setInterval(() => {
248
+ if (this.ws && this.ws.readyState === WebSocket.OPEN) {
249
+ const encoder = encoding.createEncoder();
250
+ encoding.writeVarUint(encoder, messageSync);
251
+ syncProtocol.writeSyncStep1(encoder, doc);
252
+ sendChunked(encoding.toUint8Array(encoder), this.ws);
253
+ }
254
+ }, resyncInterval);
255
+ }
256
+ this._bcSubscriber = (data, origin) => {
257
+ if (origin !== this) {
258
+ const encoder = readMessage(this, new Uint8Array(data), false);
259
+ if (encoding.length(encoder) > 1) {
260
+ bc.publish(this.bcChannel, encoding.toUint8Array(encoder), this);
261
+ }
262
+ }
263
+ };
264
+ this._updateHandler = (update, origin) => {
265
+ if (origin !== this) {
266
+ const encoder = encoding.createEncoder();
267
+ encoding.writeVarUint(encoder, messageSync);
268
+ syncProtocol.writeUpdate(encoder, update);
269
+ broadcastMessage(this, encoding.toUint8Array(encoder));
270
+ }
271
+ };
272
+ this.doc.on("update", this._updateHandler);
273
+ this._awarenessUpdateHandler = ({ added, updated, removed }, _origin) => {
274
+ const changedClients = added.concat(updated).concat(removed);
275
+ const encoder = encoding.createEncoder();
276
+ encoding.writeVarUint(encoder, messageAwareness);
277
+ encoding.writeVarUint8Array(
278
+ encoder,
279
+ awarenessProtocol.encodeAwarenessUpdate(awareness, changedClients)
280
+ );
281
+ broadcastMessage(this, encoding.toUint8Array(encoder));
282
+ };
283
+ this._unloadHandler = () => {
284
+ awarenessProtocol.removeAwarenessStates(
285
+ this.awareness,
286
+ [doc.clientID],
287
+ "window unload"
288
+ );
289
+ };
290
+ if (typeof window !== "undefined") {
291
+ window.addEventListener("unload", this._unloadHandler);
292
+ } else if (typeof process !== "undefined" && typeof process.on === "function") {
293
+ process.on("exit", this._unloadHandler);
294
+ }
295
+ awareness.on("update", this._awarenessUpdateHandler);
296
+ this._checkInterval = /** @type {any} */
297
+ setInterval(() => {
298
+ if (this.wsconnected && messageReconnectTimeout < time.getUnixTime() - this.wsLastMessageReceived) {
299
+ assert(this.ws !== null, "ws must not be null");
300
+ this.ws.close();
301
+ }
302
+ }, messageReconnectTimeout / 10);
303
+ if (connect) {
304
+ this.connect();
305
+ }
306
+ }
307
+ /**
308
+ * @type {boolean}
309
+ */
310
+ get synced() {
311
+ return this._synced;
312
+ }
313
+ set synced(state) {
314
+ if (this._synced !== state) {
315
+ this._synced = state;
316
+ this.emit("synced", [state]);
317
+ this.emit("sync", [state]);
318
+ }
319
+ }
320
+ destroy() {
321
+ if (this._resyncInterval !== 0) {
322
+ clearInterval(this._resyncInterval);
323
+ }
324
+ clearInterval(this._checkInterval);
325
+ this.disconnect();
326
+ if (typeof window !== "undefined") {
327
+ window.removeEventListener("unload", this._unloadHandler);
328
+ } else if (typeof process !== "undefined" && typeof process.off === "function") {
329
+ process.off("exit", this._unloadHandler);
330
+ }
331
+ this.awareness.off("update", this._awarenessUpdateHandler);
332
+ this.doc.off("update", this._updateHandler);
333
+ super.destroy();
334
+ }
335
+ connectBc() {
336
+ if (this.disableBc) {
337
+ return;
338
+ }
339
+ if (!this.bcconnected) {
340
+ bc.subscribe(this.bcChannel, this._bcSubscriber);
341
+ this.bcconnected = true;
342
+ }
343
+ const encoderSync = encoding.createEncoder();
344
+ encoding.writeVarUint(encoderSync, messageSync);
345
+ syncProtocol.writeSyncStep1(encoderSync, this.doc);
346
+ bc.publish(this.bcChannel, encoding.toUint8Array(encoderSync), this);
347
+ const encoderState = encoding.createEncoder();
348
+ encoding.writeVarUint(encoderState, messageSync);
349
+ syncProtocol.writeSyncStep2(encoderState, this.doc);
350
+ bc.publish(this.bcChannel, encoding.toUint8Array(encoderState), this);
351
+ const encoderAwarenessQuery = encoding.createEncoder();
352
+ encoding.writeVarUint(encoderAwarenessQuery, messageQueryAwareness);
353
+ bc.publish(
354
+ this.bcChannel,
355
+ encoding.toUint8Array(encoderAwarenessQuery),
356
+ this
357
+ );
358
+ const encoderAwarenessState = encoding.createEncoder();
359
+ encoding.writeVarUint(encoderAwarenessState, messageAwareness);
360
+ encoding.writeVarUint8Array(
361
+ encoderAwarenessState,
362
+ awarenessProtocol.encodeAwarenessUpdate(this.awareness, [
363
+ this.doc.clientID
364
+ ])
365
+ );
366
+ bc.publish(
367
+ this.bcChannel,
368
+ encoding.toUint8Array(encoderAwarenessState),
369
+ this
370
+ );
371
+ }
372
+ disconnectBc() {
373
+ const encoder = encoding.createEncoder();
374
+ encoding.writeVarUint(encoder, messageAwareness);
375
+ encoding.writeVarUint8Array(
376
+ encoder,
377
+ awarenessProtocol.encodeAwarenessUpdate(
378
+ this.awareness,
379
+ [this.doc.clientID],
380
+ /* @__PURE__ */ new Map()
381
+ )
382
+ );
383
+ broadcastMessage(this, encoding.toUint8Array(encoder));
384
+ if (this.bcconnected) {
385
+ bc.unsubscribe(this.bcChannel, this._bcSubscriber);
386
+ this.bcconnected = false;
387
+ }
388
+ }
389
+ disconnect() {
390
+ this.shouldConnect = false;
391
+ this.disconnectBc();
392
+ if (this.ws !== null) {
393
+ this.ws.close();
394
+ }
395
+ }
396
+ connect() {
397
+ this.shouldConnect = true;
398
+ if (!this.wsconnected && this.ws === null) {
399
+ setupWS(this);
400
+ this.connectBc();
401
+ }
402
+ }
403
+ };
404
+ function generateUUID() {
405
+ if (crypto && crypto.randomUUID) {
406
+ return crypto.randomUUID();
407
+ }
408
+ let d = (/* @__PURE__ */ new Date()).getTime();
409
+ let d2 = typeof performance !== "undefined" && performance.now && performance.now() * 1e3 || 0;
410
+ return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function(c) {
411
+ let r = Math.random() * 16;
412
+ if (d > 0) {
413
+ r = (d + r) % 16 | 0;
414
+ d = Math.floor(d / 16);
415
+ } else {
416
+ r = (d2 + r) % 16 | 0;
417
+ d2 = Math.floor(d2 / 16);
418
+ }
419
+ return (c === "x" ? r : r & 3 | 8).toString(16);
420
+ });
421
+ }
422
+ function assertType(value, label, type) {
423
+ if (typeof value !== type) {
424
+ throw new Error(
425
+ `Invalid "${label}" parameter provided to YPartyKitProvider. Expected: ${type}, received: ${value}`
426
+ );
427
+ }
428
+ }
429
+ var YPartyKitProvider = class extends WebsocketProvider {
430
+ id;
431
+ #params;
432
+ constructor(host, room, doc, options = {}) {
433
+ assertType(host, "host", "string");
434
+ assertType(room, "room", "string");
435
+ host = host.replace(/^(http|https|ws|wss):\/\//, "");
436
+ if (host.endsWith("/")) {
437
+ host.slice(0, -1);
438
+ }
439
+ 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.party ? `/parties/${options.party}` : "/party"}`;
440
+ const id = options.connectionId ?? generateUUID();
441
+ const { params, connect = true, ...rest } = options;
442
+ const baseOptions = {
443
+ ...rest,
444
+ connect: false
445
+ };
446
+ super(serverUrl, room, doc ?? new YDoc(), baseOptions);
447
+ this.id = id;
448
+ this.#params = params;
449
+ if (connect) {
450
+ void this.connect();
451
+ }
452
+ }
453
+ async connect() {
454
+ try {
455
+ const nextParams = typeof this.#params === "function" ? await this.#params() : this.#params;
456
+ const urlParams = new URLSearchParams([["_pk", this.id]]);
457
+ if (nextParams) {
458
+ for (const [key, value] of Object.entries(nextParams)) {
459
+ if (value !== null && value !== void 0) {
460
+ urlParams.append(key, value);
461
+ }
462
+ }
463
+ }
464
+ const nextUrl = new URL(this.url);
465
+ nextUrl.search = urlParams.toString();
466
+ this.url = nextUrl.toString();
467
+ super.connect();
468
+ } catch (err) {
469
+ console.error("Failed to open connecton to PartyKit", err);
470
+ throw err;
471
+ }
472
+ }
473
+ };
474
+
475
+ export {
476
+ messageSync,
477
+ messageQueryAwareness,
478
+ messageAwareness,
479
+ messageAuth,
480
+ WebsocketProvider,
481
+ YPartyKitProvider
482
+ };
483
+ //# sourceMappingURL=chunk-UVQRROFJ.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 * 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 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 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 =\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 YPartyKitProvider. 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 YPartyKitProviderOptions = Omit<\n NonNullable<BaseProviderOptions>,\n \"params\"\n> & {\n connectionId?: string;\n party?: string;\n params?: ParamsProvider;\n protocol?: \"ws\" | \"wss\";\n};\n\nexport default class YPartyKitProvider extends WebsocketProvider {\n id: string;\n #params?: ParamsProvider;\n\n constructor(\n host: string,\n room: string,\n doc?: YDoc,\n options: YPartyKitProviderOptions = {}\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.party ? `/parties/${options.party}` : \"/party\"}`;\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 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 PartyKit\", err);\n throw err;\n }\n }\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,128 @@
1
+ import * as decoding from "lib0/decoding";
2
+ import * as encoding from "lib0/encoding";
3
+ import { Observable } from "lib0/observable";
4
+ import * as awarenessProtocol from "y-protocols/awareness";
5
+ import { Doc } from "yjs";
6
+
7
+ declare const messageSync = 0;
8
+ declare const messageQueryAwareness = 3;
9
+ declare const messageAwareness = 1;
10
+ declare const messageAuth = 2;
11
+ declare const messageHandlers: Array<
12
+ (
13
+ encoder: encoding.Encoder,
14
+ decoder: decoding.Decoder,
15
+ provider: WebsocketProvider,
16
+ emitSynced: boolean,
17
+ messageType: number
18
+ ) => void
19
+ >;
20
+ type AwarenessUpdate = {
21
+ added: number[];
22
+ updated: number[];
23
+ removed: number[];
24
+ };
25
+ /**
26
+ * Websocket Provider for Yjs. Creates a websocket connection to sync the shared document.
27
+ * The document name is attached to the provided url. I.e. the following example
28
+ * creates a websocket connection to http://localhost:1234/my-document-name
29
+ *
30
+ * @example
31
+ * import * as Y from 'yjs'
32
+ * import { WebsocketProvider } from 'y-websocket'
33
+ * const doc = new Y.Doc()
34
+ * const provider = new WebsocketProvider('http://localhost:1234', 'my-document-name', doc)
35
+ *
36
+ * @extends {Observable<string>}
37
+ */
38
+ declare class WebsocketProvider extends Observable<string> {
39
+ maxBackoffTime: number;
40
+ bcChannel: string;
41
+ url: string;
42
+ roomname: string;
43
+ doc: Doc;
44
+ _WS: typeof WebSocket;
45
+ awareness: awarenessProtocol.Awareness;
46
+ wsconnected: boolean;
47
+ wsconnecting: boolean;
48
+ bcconnected: boolean;
49
+ disableBc: boolean;
50
+ wsUnsuccessfulReconnects: number;
51
+ messageHandlers: typeof messageHandlers;
52
+ _synced: boolean;
53
+ ws: WebSocket | null;
54
+ wsLastMessageReceived: number;
55
+ shouldConnect: boolean;
56
+ _resyncInterval: ReturnType<typeof setInterval> | number;
57
+ _bcSubscriber: (message: Uint8Array, origin: unknown) => void;
58
+ _updateHandler: (update: Uint8Array, origin: unknown) => void;
59
+ _awarenessUpdateHandler: (update: AwarenessUpdate, origin: unknown) => void;
60
+ _unloadHandler: () => void;
61
+ _checkInterval: ReturnType<typeof setInterval> | number;
62
+ constructor(
63
+ serverUrl: string,
64
+ roomname: string,
65
+ doc: Doc,
66
+ {
67
+ connect,
68
+ awareness,
69
+ params,
70
+ WebSocketPolyfill, // Optionally provide a WebSocket polyfill
71
+ resyncInterval, // Request server state every `resyncInterval` milliseconds
72
+ maxBackoffTime, // Maximum amount of time to wait before trying to reconnect (we try to reconnect using exponential backoff)
73
+ disableBc
74
+ }?: {
75
+ connect?: boolean;
76
+ awareness?: awarenessProtocol.Awareness;
77
+ params?: {
78
+ [s: string]: string;
79
+ };
80
+ WebSocketPolyfill?: typeof WebSocket | null;
81
+ resyncInterval?: number;
82
+ maxBackoffTime?: number;
83
+ disableBc?: boolean;
84
+ }
85
+ );
86
+ /**
87
+ * @type {boolean}
88
+ */
89
+ get synced(): boolean;
90
+ set synced(state: boolean);
91
+ destroy(): void;
92
+ connectBc(): void;
93
+ disconnectBc(): void;
94
+ disconnect(): void;
95
+ connect(): void;
96
+ }
97
+ type Params = Record<string, string | null | undefined>;
98
+ type ParamsProvider = Params | (() => Params | Promise<Params>);
99
+ type BaseProviderOptions = ConstructorParameters<typeof WebsocketProvider>[3];
100
+ type YPartyKitProviderOptions = Omit<
101
+ NonNullable<BaseProviderOptions>,
102
+ "params"
103
+ > & {
104
+ connectionId?: string;
105
+ party?: string;
106
+ params?: ParamsProvider;
107
+ protocol?: "ws" | "wss";
108
+ };
109
+ declare class YPartyKitProvider extends WebsocketProvider {
110
+ #private;
111
+ id: string;
112
+ constructor(
113
+ host: string,
114
+ room: string,
115
+ doc?: Doc,
116
+ options?: YPartyKitProviderOptions
117
+ );
118
+ connect(): Promise<void>;
119
+ }
120
+
121
+ export {
122
+ WebsocketProvider,
123
+ YPartyKitProvider as default,
124
+ messageAuth,
125
+ messageAwareness,
126
+ messageQueryAwareness,
127
+ messageSync
128
+ };
@@ -0,0 +1,18 @@
1
+ import {
2
+ WebsocketProvider,
3
+ YPartyKitProvider,
4
+ messageAuth,
5
+ messageAwareness,
6
+ messageQueryAwareness,
7
+ messageSync
8
+ } from "../chunk-UVQRROFJ.js";
9
+ import "../chunk-UKCMZSNL.js";
10
+ export {
11
+ WebsocketProvider,
12
+ YPartyKitProvider as default,
13
+ messageAuth,
14
+ messageAwareness,
15
+ messageQueryAwareness,
16
+ messageSync
17
+ };
18
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -0,0 +1,21 @@
1
+ import * as Y from "yjs";
2
+
3
+ import YPartyKitProvider from "./index.js";
4
+
5
+ import "lib0/decoding";
6
+ import "lib0/encoding";
7
+ import "lib0/observable";
8
+ import "y-protocols/awareness";
9
+
10
+ type UseYPartyKitProviderOptions = {
11
+ host?: string | undefined;
12
+ room: string;
13
+ party?: string;
14
+ doc?: Y.Doc;
15
+ options?: ConstructorParameters<typeof YPartyKitProvider>[3];
16
+ };
17
+ declare function useYProvider(
18
+ yProviderOptions: UseYPartyKitProviderOptions
19
+ ): YPartyKitProvider;
20
+
21
+ export { useYProvider as default };
@@ -0,0 +1,31 @@
1
+ import {
2
+ YPartyKitProvider
3
+ } from "../chunk-UVQRROFJ.js";
4
+ import "../chunk-UKCMZSNL.js";
5
+
6
+ // src/provider/react.tsx
7
+ import { useEffect, useState } from "react";
8
+ function useYProvider(yProviderOptions) {
9
+ const { host, room, party, doc, options } = yProviderOptions;
10
+ const [provider] = useState(
11
+ () => new YPartyKitProvider(
12
+ host || (typeof window !== "undefined" ? window.location.host : "dummy-domain.com"),
13
+ room,
14
+ doc,
15
+ {
16
+ connect: false,
17
+ party,
18
+ ...options
19
+ }
20
+ )
21
+ );
22
+ useEffect(() => {
23
+ void provider.connect();
24
+ return () => provider.disconnect();
25
+ }, [provider]);
26
+ return provider;
27
+ }
28
+ export {
29
+ useYProvider as default
30
+ };
31
+ //# sourceMappingURL=react.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/provider/react.tsx"],"sourcesContent":["import { useEffect, useState } from \"react\";\n\nimport YPartyKitProvider from \"./index\";\n\nimport type * as Y from \"yjs\";\n\ntype UseYPartyKitProviderOptions = {\n host?: string | undefined;\n room: string;\n party?: string;\n doc?: Y.Doc;\n options?: ConstructorParameters<typeof YPartyKitProvider>[3];\n};\n\nexport default function useYProvider(\n yProviderOptions: UseYPartyKitProviderOptions\n) {\n const { host, room, party, doc, options } = yProviderOptions;\n const [provider] = useState<YPartyKitProvider>(\n () =>\n new YPartyKitProvider(\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 ...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;AAcrB,SAAR,aACL,kBACA;AACA,QAAM,EAAE,MAAM,MAAM,OAAO,KAAK,QAAQ,IAAI;AAC5C,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,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":[]}
@@ -0,0 +1,32 @@
1
+ import { Connection, ConnectionContext, Server } from "partyserver";
2
+ import * as awarenessProtocol from "y-protocols/awareness";
3
+ import { Doc } from "yjs";
4
+
5
+ declare class WSSharedDoc extends Doc {
6
+ conns: Map<Connection, Set<number>>;
7
+ awareness: awarenessProtocol.Awareness;
8
+ constructor();
9
+ }
10
+ interface CallbackOptions {
11
+ debounceWait?: number;
12
+ debounceMaxWait?: number;
13
+ timeout?: number;
14
+ }
15
+ declare class YjsDocument<Env> extends Server<Env> {
16
+ #private;
17
+ static callbackOptions: CallbackOptions;
18
+ readonly document: WSSharedDoc;
19
+ onLoad(): Promise<void>;
20
+ onSave(): Promise<void>;
21
+ onStart(): Promise<void>;
22
+ onMessage: (conn: Connection, message: string | ArrayBufferLike) => void;
23
+ onClose(
24
+ connection: Connection<unknown>,
25
+ _code: number,
26
+ _reason: string,
27
+ _wasClean: boolean
28
+ ): void | Promise<void>;
29
+ onConnect(conn: Connection<unknown>, _ctx: ConnectionContext): void;
30
+ }
31
+
32
+ export { YjsDocument };
@@ -0,0 +1,220 @@
1
+ import {
2
+ handleChunked
3
+ } from "../chunk-UKCMZSNL.js";
4
+
5
+ // src/server/index.ts
6
+ import * as decoding from "lib0/decoding";
7
+ import * as encoding from "lib0/encoding";
8
+ import debounce from "lodash.debounce";
9
+ import { Server } from "partyserver";
10
+ import * as awarenessProtocol from "y-protocols/awareness";
11
+ import * as syncProtocol from "y-protocols/sync";
12
+ import { applyUpdate, encodeStateAsUpdate, Doc as YDoc } from "yjs";
13
+ var wsReadyStateConnecting = 0;
14
+ var wsReadyStateOpen = 1;
15
+ var messageSync = 0;
16
+ var messageAwareness = 1;
17
+ function updateHandler(update, origin, doc) {
18
+ const encoder = encoding.createEncoder();
19
+ encoding.writeVarUint(encoder, messageSync);
20
+ syncProtocol.writeUpdate(encoder, update);
21
+ const message = encoding.toUint8Array(encoder);
22
+ doc.conns.forEach((_, conn) => send(doc, conn, message));
23
+ }
24
+ var WSSharedDoc = class extends YDoc {
25
+ conns;
26
+ awareness;
27
+ constructor() {
28
+ super({ gc: true });
29
+ this.conns = /* @__PURE__ */ new Map();
30
+ this.awareness = new awarenessProtocol.Awareness(this);
31
+ this.awareness.setLocalState(null);
32
+ const awarenessChangeHandler = ({
33
+ added,
34
+ updated,
35
+ removed
36
+ }, conn) => {
37
+ const changedClients = added.concat(updated, removed);
38
+ if (conn !== null) {
39
+ const connControlledIDs = (
40
+ /** @type {Set<number>} */
41
+ this.conns.get(conn)
42
+ );
43
+ if (connControlledIDs !== void 0) {
44
+ added.forEach((clientID) => {
45
+ connControlledIDs.add(clientID);
46
+ });
47
+ removed.forEach((clientID) => {
48
+ connControlledIDs.delete(clientID);
49
+ });
50
+ }
51
+ }
52
+ const encoder = encoding.createEncoder();
53
+ encoding.writeVarUint(encoder, messageAwareness);
54
+ encoding.writeVarUint8Array(
55
+ encoder,
56
+ awarenessProtocol.encodeAwarenessUpdate(this.awareness, changedClients)
57
+ );
58
+ const buff = encoding.toUint8Array(encoder);
59
+ this.conns.forEach((_, c) => {
60
+ send(this, c, buff);
61
+ });
62
+ };
63
+ this.awareness.on("update", awarenessChangeHandler);
64
+ this.on("update", updateHandler);
65
+ }
66
+ };
67
+ var CALLBACK_DEFAULTS = {
68
+ debounceWait: 2e3,
69
+ debounceMaxWait: 1e4,
70
+ timeout: 5e3
71
+ };
72
+ function readSyncMessage(decoder, encoder, doc, transactionOrigin, readOnly = false) {
73
+ const messageType = decoding.readVarUint(decoder);
74
+ switch (messageType) {
75
+ case syncProtocol.messageYjsSyncStep1:
76
+ syncProtocol.readSyncStep1(decoder, encoder, doc);
77
+ break;
78
+ case syncProtocol.messageYjsSyncStep2:
79
+ if (!readOnly)
80
+ syncProtocol.readSyncStep2(decoder, doc, transactionOrigin);
81
+ break;
82
+ case syncProtocol.messageYjsUpdate:
83
+ if (!readOnly) syncProtocol.readUpdate(decoder, doc, transactionOrigin);
84
+ break;
85
+ default:
86
+ throw new Error("Unknown message type");
87
+ }
88
+ return messageType;
89
+ }
90
+ function closeConn(doc, conn) {
91
+ if (doc.conns.has(conn)) {
92
+ const controlledIds = doc.conns.get(conn);
93
+ doc.conns.delete(conn);
94
+ awarenessProtocol.removeAwarenessStates(
95
+ doc.awareness,
96
+ Array.from(controlledIds),
97
+ null
98
+ );
99
+ }
100
+ try {
101
+ conn.close();
102
+ } catch (e) {
103
+ console.warn("failed to close connection", e);
104
+ }
105
+ }
106
+ function send(doc, conn, m) {
107
+ if (conn.readyState !== void 0 && conn.readyState !== wsReadyStateConnecting && conn.readyState !== wsReadyStateOpen) {
108
+ closeConn(doc, conn);
109
+ }
110
+ try {
111
+ conn.send(m);
112
+ } catch (e) {
113
+ closeConn(doc, conn);
114
+ }
115
+ }
116
+ var YjsDocument = class extends Server {
117
+ static callbackOptions = {};
118
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access
119
+ #ParentClass = Object.getPrototypeOf(this).constructor;
120
+ document = new WSSharedDoc();
121
+ // eslint-disable-next-line @typescript-eslint/require-await
122
+ async onLoad() {
123
+ return;
124
+ }
125
+ async onSave() {
126
+ }
127
+ async onStart() {
128
+ const src = await this.onLoad();
129
+ if (src != null) {
130
+ const state = encodeStateAsUpdate(src);
131
+ applyUpdate(this.document, state);
132
+ }
133
+ this.document.on(
134
+ "update",
135
+ debounce(
136
+ (_update, _origin, _doc) => {
137
+ try {
138
+ this.onSave().catch((err) => {
139
+ console.error("failed to persist:", err);
140
+ });
141
+ } catch (err) {
142
+ console.error("failed to persist:", err);
143
+ }
144
+ },
145
+ this.#ParentClass.callbackOptions.debounceWait || CALLBACK_DEFAULTS.debounceWait,
146
+ {
147
+ maxWait: this.#ParentClass.callbackOptions.debounceMaxWait || CALLBACK_DEFAULTS.debounceMaxWait
148
+ }
149
+ )
150
+ );
151
+ }
152
+ onMessage = handleChunked((conn, message) => {
153
+ if (typeof message === "string") {
154
+ console.warn(
155
+ `Received non-binary message. Override onMessage on ${this.#ParentClass.name} to handle string messages if required`
156
+ );
157
+ return;
158
+ }
159
+ try {
160
+ const encoder = encoding.createEncoder();
161
+ const decoder = decoding.createDecoder(message);
162
+ const messageType = decoding.readVarUint(decoder);
163
+ switch (messageType) {
164
+ case messageSync:
165
+ encoding.writeVarUint(encoder, messageSync);
166
+ readSyncMessage(
167
+ decoder,
168
+ encoder,
169
+ this.document,
170
+ conn,
171
+ // TODO: readonly conections
172
+ false
173
+ );
174
+ if (encoding.length(encoder) > 1) {
175
+ send(this.document, conn, encoding.toUint8Array(encoder));
176
+ }
177
+ break;
178
+ case messageAwareness: {
179
+ awarenessProtocol.applyAwarenessUpdate(
180
+ this.document.awareness,
181
+ decoding.readVarUint8Array(decoder),
182
+ conn
183
+ );
184
+ break;
185
+ }
186
+ }
187
+ } catch (err) {
188
+ console.error(err);
189
+ this.document.emit("error", [err]);
190
+ }
191
+ });
192
+ onClose(connection, _code, _reason, _wasClean) {
193
+ closeConn(this.document, connection);
194
+ }
195
+ // TODO: explore why onError gets triggered when a connection closes
196
+ onConnect(conn, _ctx) {
197
+ this.document.conns.set(conn, /* @__PURE__ */ new Set());
198
+ const encoder = encoding.createEncoder();
199
+ encoding.writeVarUint(encoder, messageSync);
200
+ syncProtocol.writeSyncStep1(encoder, this.document);
201
+ send(this.document, conn, encoding.toUint8Array(encoder));
202
+ const awarenessStates = this.document.awareness.getStates();
203
+ if (awarenessStates.size > 0) {
204
+ const encoder2 = encoding.createEncoder();
205
+ encoding.writeVarUint(encoder2, messageAwareness);
206
+ encoding.writeVarUint8Array(
207
+ encoder2,
208
+ awarenessProtocol.encodeAwarenessUpdate(
209
+ this.document.awareness,
210
+ Array.from(awarenessStates.keys())
211
+ )
212
+ );
213
+ send(this.document, conn, encoding.toUint8Array(encoder2));
214
+ }
215
+ }
216
+ };
217
+ export {
218
+ YjsDocument
219
+ };
220
+ //# sourceMappingURL=index.js.map
@@ -0,0 +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 } 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> 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":";;;;;AAAA,YAAY,cAAc;AAC1B,YAAY,cAAc;AAC1B,OAAO,cAAc;AACrB,SAAS,cAAc;AACvB,YAAY,uBAAuB;AACnC,YAAY,kBAAkB;AAC9B,SAAS,aAAa,qBAAqB,OAAO,YAAY;AAM9D,IAAM,yBAAyB;AAC/B,IAAM,mBAAmB;AAIzB,IAAM,cAAc;AACpB,IAAM,mBAAmB;AAGzB,SAAS,cAAc,QAAoB,QAAiB,KAAkB;AAC5E,QAAM,UAAmB,uBAAc;AACvC,EAAS,sBAAa,SAAS,WAAW;AAC1C,EAAa,yBAAY,SAAS,MAAM;AACxC,QAAM,UAAmB,sBAAa,OAAO;AAC7C,MAAI,MAAM,QAAQ,CAAC,GAAG,SAAS,KAAK,KAAK,MAAM,OAAO,CAAC;AACzD;AAEA,IAAM,cAAN,cAA0B,KAAK;AAAA,EAC7B;AAAA,EACA;AAAA,EAEA,cAAc;AACZ,UAAM,EAAE,IAAI,KAAK,CAAC;AAKlB,SAAK,QAAQ,oBAAI,IAAI;AAErB,SAAK,YAAY,IAAsB,4BAAU,IAAI;AACrD,SAAK,UAAU,cAAc,IAAI;AAEjC,UAAM,yBAAyB,CAC7B;AAAA,MACE;AAAA,MACA;AAAA,MACA;AAAA,IACF,GAKA,SACG;AACH,YAAM,iBAAiB,MAAM,OAAO,SAAS,OAAO;AACpD,UAAI,SAAS,MAAM;AACjB,cAAM;AAAA;AAAA,UACuB,KAAK,MAAM,IAAI,IAAI;AAAA;AAChD,YAAI,sBAAsB,QAAW;AACnC,gBAAM,QAAQ,CAAC,aAAa;AAC1B,8BAAkB,IAAI,QAAQ;AAAA,UAChC,CAAC;AACD,kBAAQ,QAAQ,CAAC,aAAa;AAC5B,8BAAkB,OAAO,QAAQ;AAAA,UACnC,CAAC;AAAA,QACH;AAAA,MACF;AAEA,YAAM,UAAmB,uBAAc;AACvC,MAAS,sBAAa,SAAS,gBAAgB;AAC/C,MAAS;AAAA,QACP;AAAA,QACkB,wCAAsB,KAAK,WAAW,cAAc;AAAA,MACxE;AACA,YAAM,OAAgB,sBAAa,OAAO;AAC1C,WAAK,MAAM,QAAQ,CAAC,GAAG,MAAM;AAC3B,aAAK,MAAM,GAAG,IAAI;AAAA,MACpB,CAAC;AAAA,IACH;AACA,SAAK,UAAU,GAAG,UAAU,sBAAsB;AAElD,SAAK,GAAG,UAAU,aAAa;AAAA,EACjC;AACF;AAEA,IAAM,oBAAoB;AAAA,EACxB,cAAc;AAAA,EACd,iBAAiB;AAAA,EACjB,SAAS;AACX;AAEA,SAAS,gBACP,SACA,SACA,KACA,mBACA,WAAW,OACX;AACA,QAAM,cAAuB,qBAAY,OAAO;AAChD,UAAQ,aAAa;AAAA,IACnB,KAAkB;AAChB,MAAa,2BAAc,SAAS,SAAS,GAAG;AAChD;AAAA,IACF,KAAkB;AAChB,UAAI,CAAC;AACH,QAAa,2BAAc,SAAS,KAAK,iBAAiB;AAC5D;AAAA,IACF,KAAkB;AAChB,UAAI,CAAC,SAAU,CAAa,wBAAW,SAAS,KAAK,iBAAiB;AACtE;AAAA,IACF;AACE,YAAM,IAAI,MAAM,sBAAsB;AAAA,EAC1C;AACA,SAAO;AACT;AAEA,SAAS,UAAU,KAAkB,MAAwB;AAC3D,MAAI,IAAI,MAAM,IAAI,IAAI,GAAG;AACvB,UAAM,gBAA6B,IAAI,MAAM,IAAI,IAAI;AACrD,QAAI,MAAM,OAAO,IAAI;AACrB,IAAkB;AAAA,MAChB,IAAI;AAAA,MACJ,MAAM,KAAK,aAAa;AAAA,MACxB;AAAA,IACF;AAAA,EACF;AACA,MAAI;AACF,SAAK,MAAM;AAAA,EACb,SAAS,GAAG;AACV,YAAQ,KAAK,8BAA8B,CAAC;AAAA,EAC9C;AACF;AAEA,SAAS,KAAK,KAAkB,MAAkB,GAAe;AAC/D,MACE,KAAK,eAAe,UACpB,KAAK,eAAe,0BACpB,KAAK,eAAe,kBACpB;AACA,cAAU,KAAK,IAAI;AAAA,EACrB;AACA,MAAI;AACF,SAAK,KAAK,CAAC;AAAA,EACb,SAAS,GAAG;AACV,cAAU,KAAK,IAAI;AAAA,EACrB;AACF;AAQO,IAAM,cAAN,cAA+B,OAAY;AAAA,EAChD,OAAO,kBAAmC,CAAC;AAAA;AAAA,EAG3C,eAAmC,OAAO,eAAe,IAAI,EAAE;AAAA,EACtD,WAAW,IAAI,YAAY;AAAA;AAAA,EAGpC,MAAM,SAAwB;AAE5B;AAAA,EACF;AAAA,EAEA,MAAM,SAAwB;AAAA,EAAC;AAAA,EAE/B,MAAM,UAAyB;AAC7B,UAAM,MAAM,MAAM,KAAK,OAAO;AAC9B,QAAI,OAAO,MAAM;AACf,YAAM,QAAQ,oBAAoB,GAAG;AACrC,kBAAY,KAAK,UAAU,KAAK;AAAA,IAClC;AAEA,SAAK,SAAS;AAAA,MACZ;AAAA,MACA;AAAA,QACE,CAAC,SAAqB,SAAqB,SAAe;AACxD,cAAI;AACF,iBAAK,OAAO,EAAE,MAAM,CAAC,QAAQ;AAC3B,sBAAQ,MAAM,sBAAsB,GAAG;AAAA,YACzC,CAAC;AAAA,UACH,SAAS,KAAK;AACZ,oBAAQ,MAAM,sBAAsB,GAAG;AAAA,UACzC;AAAA,QACF;AAAA,QACA,KAAK,aAAa,gBAAgB,gBAChC,kBAAkB;AAAA,QACpB;AAAA,UACE,SACE,KAAK,aAAa,gBAAgB,mBAClC,kBAAkB;AAAA,QACtB;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA,EAEA,YAAY,cAAc,CAAC,MAAM,YAAY;AAC3C,QAAI,OAAO,YAAY,UAAU;AAC/B,cAAQ;AAAA,QACN,sDAAsD,KAAK,aAAa,IAAI;AAAA,MAC9E;AACA;AAAA,IACF;AACA,QAAI;AACF,YAAM,UAAmB,uBAAc;AAEvC,YAAM,UAAmB,uBAAc,OAAqB;AAC5D,YAAM,cAAuB,qBAAY,OAAO;AAChD,cAAQ,aAAa;AAAA,QACnB,KAAK;AACH,UAAS,sBAAa,SAAS,WAAW;AAC1C;AAAA,YACE;AAAA,YACA;AAAA,YACA,KAAK;AAAA,YACL;AAAA;AAAA,YAEA;AAAA,UACF;AAKA,cAAa,gBAAO,OAAO,IAAI,GAAG;AAChC,iBAAK,KAAK,UAAU,MAAe,sBAAa,OAAO,CAAC;AAAA,UAC1D;AACA;AAAA,QACF,KAAK,kBAAkB;AACrB,UAAkB;AAAA,YAChB,KAAK,SAAS;AAAA,YACL,2BAAkB,OAAO;AAAA,YAClC;AAAA,UACF;AACA;AAAA,QACF;AAAA,MACF;AAAA,IACF,SAAS,KAAK;AACZ,cAAQ,MAAM,GAAG;AAEjB,WAAK,SAAS,KAAK,SAAS,CAAC,GAAG,CAAC;AAAA,IACnC;AAAA,EACF,CAAC;AAAA,EAED,QACE,YACA,OACA,SACA,WACsB;AACtB,cAAU,KAAK,UAAU,UAAU;AAAA,EACrC;AAAA;AAAA,EAIA,UAAU,MAA2B,MAAyB;AAG5D,SAAK,SAAS,MAAM,IAAI,MAAM,oBAAI,IAAI,CAAC;AAGvC,UAAM,UAAmB,uBAAc;AACvC,IAAS,sBAAa,SAAS,WAAW;AAC1C,IAAa,4BAAe,SAAS,KAAK,QAAQ;AAClD,SAAK,KAAK,UAAU,MAAe,sBAAa,OAAO,CAAC;AACxD,UAAM,kBAAkB,KAAK,SAAS,UAAU,UAAU;AAC1D,QAAI,gBAAgB,OAAO,GAAG;AAC5B,YAAMA,WAAmB,uBAAc;AACvC,MAAS,sBAAaA,UAAS,gBAAgB;AAC/C,MAAS;AAAA,QACPA;AAAA,QACkB;AAAA,UAChB,KAAK,SAAS;AAAA,UACd,MAAM,KAAK,gBAAgB,KAAK,CAAC;AAAA,QACnC;AAAA,MACF;AACA,WAAK,KAAK,UAAU,MAAe,sBAAaA,QAAO,CAAC;AAAA,IAC1D;AAAA,EACF;AACF;","names":["encoder"]}
package/package.json ADDED
@@ -0,0 +1,56 @@
1
+ {
2
+ "name": "y-partyserver",
3
+ "version": "0.0.0-01b5782",
4
+ "repository": {
5
+ "type": "git",
6
+ "url": "git://github.com/threepointone/partyserver"
7
+ },
8
+ "type": "module",
9
+ "exports": {
10
+ ".": {
11
+ "require": "./dist/server/index.js",
12
+ "import": "./dist/server/index.js",
13
+ "types": "./dist/server/index.d.ts"
14
+ },
15
+ "./provider": {
16
+ "require": "./dist/provider/index.js",
17
+ "import": "./dist/provider/index.js",
18
+ "types": "./dist/provider/index.d.ts"
19
+ },
20
+ "./react": {
21
+ "require": "./dist/provider/react.js",
22
+ "import": "./dist/provider/react.js",
23
+ "types": "./dist/provider/react.d.ts"
24
+ }
25
+ },
26
+ "scripts": {
27
+ "build": "bun scripts/build.ts"
28
+ },
29
+ "files": [
30
+ "dist",
31
+ "README.md"
32
+ ],
33
+ "keywords": [
34
+ "yjs",
35
+ "collaboration",
36
+ "text-editors"
37
+ ],
38
+ "author": "Sunil Pai <threepointone@gmail.com>",
39
+ "license": "ISC",
40
+ "description": "",
41
+ "dependencies": {
42
+ "lib0": "^0.2.92",
43
+ "lodash.debounce": "^4.0.8",
44
+ "y-protocols": "^1.0.6"
45
+ },
46
+ "peerDependencies": {
47
+ "@cloudflare/workers-types": "^4.20240529.0",
48
+ "yjs": "^13.6.14"
49
+ },
50
+ "devDependencies": {
51
+ "@cloudflare/workers-types": "^4.20240529.0",
52
+ "@types/lodash.debounce": "^4.0.9",
53
+ "ws": "^8.11.0",
54
+ "yjs": "^13.6.14"
55
+ }
56
+ }