"use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } async function _asyncNullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return await rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; } var _class; var _class2; var _chunkGLWCU3YDcjs = require('./chunk-GLWCU3YD.cjs'); // src/index.ts var _hono = require('hono'); var _client = require('hono/client'); // src/yjs/index.ts var _cloudflareworkers = require('cloudflare:workers'); var _awareness = require('y-protocols/awareness'); var _yjs = require('yjs'); // src/yjs/remote/ws-shared-doc.ts var _decoding = require('lib0/decoding'); var _encoding = require('lib0/encoding'); var _sync = require('y-protocols/sync'); // src/yjs/message-type/index.ts var messageType = { sync: 0, awareness: 1 }; var isMessageType = (type) => { return Object.keys(messageType).includes(type); }; var createTypedEncoder = (type) => { if (!isMessageType(type)) { throw new Error(`Unsupported message type: ${type}`); } const encoder = _encoding.createEncoder.call(void 0, ); _encoding.writeVarUint.call(void 0, encoder, messageType[type]); return encoder; }; // src/yjs/remote/ws-shared-doc.ts var WSSharedDoc = (_class = class extends _yjs.Doc { __init() {this.listeners = /* @__PURE__ */ new Set()} __init2() {this.awareness = new (0, _awareness.Awareness)(this)} constructor(gc = true) { super({ gc });_class.prototype.__init.call(this);_class.prototype.__init2.call(this);; this.awareness.setLocalState(null); this.awareness.on("update", (changes) => { this.awarenessChangeHandler(changes); }); this.on("update", (update) => { this.syncMessageHandler(update); }); } update(message) { const encoder = _encoding.createEncoder.call(void 0, ); const decoder = _decoding.createDecoder.call(void 0, message); const type = _decoding.readVarUint.call(void 0, decoder); switch (type) { case messageType.sync: { _encoding.writeVarUint.call(void 0, encoder, messageType.sync); _sync.readSyncMessage.call(void 0, decoder, encoder, this, null); if (_encoding.length.call(void 0, encoder) > 1) { this._notify(_encoding.toUint8Array.call(void 0, encoder)); } break; } case messageType.awareness: { _awareness.applyAwarenessUpdate.call(void 0, this.awareness, _decoding.readVarUint8Array.call(void 0, decoder), null); break; } } } notify(listener) { this.listeners.add(listener); return () => { this.listeners.delete(listener); }; } syncMessageHandler(update) { const encoder = createTypedEncoder("sync"); _sync.writeUpdate.call(void 0, encoder, update); this._notify(_encoding.toUint8Array.call(void 0, encoder)); } awarenessChangeHandler({ added, updated, removed }) { const changed = [...added, ...updated, ...removed]; const encoder = createTypedEncoder("awareness"); const update = _awareness.encodeAwarenessUpdate.call(void 0, this.awareness, changed, this.awareness.states ); _encoding.writeVarUint8Array.call(void 0, encoder, update); this._notify(_encoding.toUint8Array.call(void 0, encoder)); } _notify(message) { for (const subscriber of this.listeners) { subscriber(message); } } }, _class); // src/yjs/client/setup.ts var setupWSConnection = (ws, doc) => { { const encoder = createTypedEncoder("sync"); _sync.writeSyncStep1.call(void 0, encoder, doc); ws.send(_encoding.toUint8Array.call(void 0, encoder)); } { const states = doc.awareness.getStates(); if (states.size > 0) { const encoder = createTypedEncoder("awareness"); const update = _awareness.encodeAwarenessUpdate.call(void 0, doc.awareness, Array.from(states.keys()) ); _encoding.writeVarUint8Array.call(void 0, encoder, update); ws.send(_encoding.toUint8Array.call(void 0, encoder)); } } }; // src/yjs/hono/index.ts var createApp = (service) => { const app2 = new (0, _hono.Hono)(); return app2.get("/rooms/:roomId", async (c) => { const roomId = c.req.param("roomId"); const client = await service.createRoom(roomId); return new Response(null, { webSocket: client, status: 101, statusText: "Switching Protocols" }); }); }; // src/yjs/storage/index.ts // src/yjs/storage/storage-key/index.ts var storageKey = (key) => { return `ydoc:${key.type}:${_nullishCoalesce(key.name, () => ( ""))}`; }; // src/yjs/storage/index.ts var YTransactionStorageImpl = class { // eslint-disable-next-line no-useless-constructor constructor(storage, options) { this.storage = storage; this.MAX_BYTES = _nullishCoalesce(_optionalChain([options, 'optionalAccess', _ => _.maxBytes]), () => ( 10 * 1024)); if (this.MAX_BYTES > 128 * 1024) { throw new Error("maxBytes must be less than 128KB"); } this.MAX_UPDATES = _nullishCoalesce(_optionalChain([options, 'optionalAccess', _2 => _2.maxUpdates]), () => ( 500)); } async getYDoc() { const snapshot = await this.storage.get( storageKey({ type: "state", name: "doc" }) ); const data = await this.storage.list({ prefix: storageKey({ type: "update" }) }); const updates = Array.from(data.values()); const doc = new (0, _yjs.Doc)(); doc.transact(() => { if (snapshot) { _yjs.applyUpdate.call(void 0, doc, snapshot); } for (const update of updates) { _yjs.applyUpdate.call(void 0, doc, update); } }); return doc; } storeUpdate(update) { return this.storage.transaction(async (tx) => { const bytes = await _asyncNullishCoalesce(await tx.get(storageKey({ type: "state", name: "bytes" })), async () => ( 0)); const count = await _asyncNullishCoalesce(await tx.get(storageKey({ type: "state", name: "count" })), async () => ( 0)); const updateBytes = bytes + update.byteLength; const updateCount = count + 1; if (updateBytes > this.MAX_BYTES || updateCount > this.MAX_UPDATES) { const doc = await this.getYDoc(); _yjs.applyUpdate.call(void 0, doc, update); await this._commit(doc, tx); } else { await tx.put(storageKey({ type: "state", name: "bytes" }), updateBytes); await tx.put(storageKey({ type: "state", name: "count" }), updateCount); await tx.put(storageKey({ type: "update", name: updateCount }), update); } }); } async _commit(doc, tx) { const data = await tx.list({ prefix: storageKey({ type: "update" }) }); for (const update2 of data.values()) { _yjs.applyUpdate.call(void 0, doc, update2); } const update = _yjs.encodeStateAsUpdate.call(void 0, doc); await tx.delete(Array.from(data.keys())); await tx.put(storageKey({ type: "state", name: "bytes" }), 0); await tx.put(storageKey({ type: "state", name: "count" }), 0); await tx.put(storageKey({ type: "state", name: "doc" }), update); } async commit() { const doc = await this.getYDoc(); return this.storage.transaction(async (tx) => { await this._commit(doc, tx); }); } }; // src/yjs/index.ts var YDurableObjects = (_class2 = class extends _cloudflareworkers.DurableObject { constructor(state, env) { super(state, env);_class2.prototype.__init3.call(this);_class2.prototype.__init4.call(this);_class2.prototype.__init5.call(this);_class2.prototype.__init6.call(this);_class2.prototype.__init7.call(this);; this.state = state; this.env = env; void this.state.blockConcurrencyWhile(this.onStart.bind(this)); } __init3() {this.app = createApp({ createRoom: this.createRoom.bind(this) })} __init4() {this.doc = new WSSharedDoc()} __init5() {this.storage = new YTransactionStorageImpl({ get: (key) => this.state.storage.get(key), list: (options) => this.state.storage.list(options), put: (key, value) => this.state.storage.put(key, value), delete: async (key) => this.state.storage.delete(Array.isArray(key) ? key : [key]), transaction: (closure) => this.state.storage.transaction(closure) })} __init6() {this.sessions = /* @__PURE__ */ new Map()} __init7() {this.awarenessClients = /* @__PURE__ */ new Set()} async onStart() { const doc = await this.storage.getYDoc(); _yjs.applyUpdate.call(void 0, this.doc, _yjs.encodeStateAsUpdate.call(void 0, doc)); for (const ws of this.state.getWebSockets()) { this.registerWebSocket(ws); } this.doc.on("update", async (update) => { await this.storage.storeUpdate(update); }); this.doc.awareness.on( "update", async ({ added, removed, updated }) => { for (const client of [...added, ...updated]) { this.awarenessClients.add(client); } for (const client of removed) { this.awarenessClients.delete(client); } } ); } createRoom(roomId) { const pair = new WebSocketPair(); const client = pair[0]; const server = pair[1]; server.serializeAttachment({ roomId, connectedAt: /* @__PURE__ */ new Date() }); this.state.acceptWebSocket(server); this.registerWebSocket(server); return client; } fetch(request) { return this.app.request(request, void 0, this.env); } async updateYDoc(update) { this.doc.update(update); await this.cleanup(); } async getYDoc() { return _yjs.encodeStateAsUpdate.call(void 0, this.doc); } async webSocketMessage(ws, message) { if (!(message instanceof ArrayBuffer)) return; const update = new Uint8Array(message); await this.updateYDoc(update); } async webSocketError(ws) { await this.unregisterWebSocket(ws); await this.cleanup(); } async webSocketClose(ws) { await this.unregisterWebSocket(ws); await this.cleanup(); } registerWebSocket(ws) { setupWSConnection(ws, this.doc); const s = this.doc.notify((message) => { ws.send(message); }); this.sessions.set(ws, s); } async unregisterWebSocket(ws) { try { const dispose = this.sessions.get(ws); _optionalChain([dispose, 'optionalCall', _3 => _3()]); this.sessions.delete(ws); const clientIds = this.awarenessClients; _awareness.removeAwarenessStates.call(void 0, this.doc.awareness, Array.from(clientIds), null); } catch (e) { console.error(e); } } async cleanup() { if (this.sessions.size < 1) { await this.storage.commit(); } } }, _class2); // src/index.ts var app = new (0, _hono.Hono)(); var yRoute = (selector) => { const route = app.get("/:id", _chunkGLWCU3YDcjs.upgrade.call(void 0, ), async (c) => { const obj = selector(c.env); const stub = obj.get(obj.idFromName(c.req.param("id"))); const url = new URL("/", c.req.url); const client = _client.hc.call(void 0, url.toString(), { fetch: stub.fetch.bind(stub) }); const res = await client.rooms[":roomId"].$get( { param: { roomId: c.req.param("id") } }, { init: { headers: c.req.raw.headers } } ); return new Response(null, { webSocket: res.webSocket, status: res.status, statusText: res.statusText }); }); return route; }; exports.WSSharedDoc = WSSharedDoc; exports.YDurableObjects = YDurableObjects; exports.yRoute = yRoute; //# sourceMappingURL=index.cjs.map