Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
//#region \0rolldown/runtime.js
var __create$1 = Object.create;
var __defProp$1 = Object.defineProperty;
var __getOwnPropDesc$1 = Object.getOwnPropertyDescriptor;
var __getOwnPropNames$1 = Object.getOwnPropertyNames;
var __getProtoOf$1 = Object.getPrototypeOf;
var __hasOwnProp$1 = Object.prototype.hasOwnProperty;
var __copyProps$1 = (to, from, except, desc) => {
	if (from && typeof from === "object" || typeof from === "function") {
		for (var keys = __getOwnPropNames$1(from), i = 0, n = keys.length, key; i < n; i++) {
			key = keys[i];
			if (!__hasOwnProp$1.call(to, key) && key !== except) {
				__defProp$1(to, key, {
					get: ((k) => from[k]).bind(null, key),
					enumerable: !(desc = __getOwnPropDesc$1(from, key)) || desc.enumerable
				});
			}
		}
	}
	return to;
};
var __toESM$1 = (mod, isNodeMode, target) => (target = mod != null ? __create$1(__getProtoOf$1(mod)) : {}, __copyProps$1(isNodeMode || !mod || !mod.__esModule ? __defProp$1(target, "default", {
	value: mod,
	enumerable: true
}) : target, mod));

//#endregion
let _hocuspocus_common = require("@hocuspocus/common");
let lib0_decoding = require("lib0/decoding");
lib0_decoding = __toESM$1(lib0_decoding);
let lib0_encoding = require("lib0/encoding");
let y_protocols_awareness = require("y-protocols/awareness");
let y_protocols_sync = require("y-protocols/sync");
let yjs = require("yjs");
yjs = __toESM$1(yjs);
let async_mutex = require("async-mutex");
let node_crypto = require("node:crypto");
node_crypto = __toESM$1(node_crypto);
let node_url = require("node:url");
let node_http = require("node:http");
let node_module = require("node:module");
let kleur = require("kleur");
kleur = __toESM$1(kleur);

//#region packages/server/src/IncomingMessage.ts
var IncomingMessage = class {
	constructor(input) {
		if (!(input instanceof Uint8Array)) input = new Uint8Array(input);
		this.decoder = (0, lib0_decoding.createDecoder)(input);
	}
	get encoder() {
		if (!this.encoderInternal) this.encoderInternal = (0, lib0_encoding.createEncoder)();
		return this.encoderInternal;
	}
	readVarUint8Array() {
		return (0, lib0_decoding.readVarUint8Array)(this.decoder);
	}
	peekVarUint8Array() {
		const { pos } = this.decoder;
		const result = (0, lib0_decoding.readVarUint8Array)(this.decoder);
		this.decoder.pos = pos;
		return result;
	}
	readVarUint() {
		return (0, lib0_decoding.readVarUint)(this.decoder);
	}
	readVarString() {
		return (0, lib0_decoding.readVarString)(this.decoder);
	}
	toUint8Array() {
		return (0, lib0_encoding.toUint8Array)(this.encoder);
	}
	writeVarUint(type) {
		(0, lib0_encoding.writeVarUint)(this.encoder, type);
	}
	writeVarString(string) {
		(0, lib0_encoding.writeVarString)(this.encoder, string);
	}
	get length() {
		return (0, lib0_encoding.length)(this.encoder);
	}
};

//#endregion
//#region packages/server/src/types.ts
function isTransactionOrigin(origin) {
	return typeof origin === "object" && origin !== null && "source" in origin && (origin.source === "connection" || origin.source === "redis" || origin.source === "local");
}
function shouldSkipStoreHooks(origin) {
	if (!isTransactionOrigin(origin)) return false;
	switch (origin.source) {
		case "connection": return false;
		case "redis": return true;
		case "local": return origin.skipStoreHooks ?? false;
	}
}
let MessageType = /* @__PURE__ */ function(MessageType) {
	MessageType[MessageType["Unknown"] = -1] = "Unknown";
	MessageType[MessageType["Sync"] = 0] = "Sync";
	MessageType[MessageType["Awareness"] = 1] = "Awareness";
	MessageType[MessageType["Auth"] = 2] = "Auth";
	MessageType[MessageType["QueryAwareness"] = 3] = "QueryAwareness";
	MessageType[MessageType["SyncReply"] = 4] = "SyncReply";
	MessageType[MessageType["Stateless"] = 5] = "Stateless";
	MessageType[MessageType["BroadcastStateless"] = 6] = "BroadcastStateless";
	MessageType[MessageType["CLOSE"] = 7] = "CLOSE";
	MessageType[MessageType["SyncStatus"] = 8] = "SyncStatus";
	MessageType[MessageType["Ping"] = 9] = "Ping";
	MessageType[MessageType["Pong"] = 10] = "Pong";
	return MessageType;
}({});

//#endregion
//#region packages/server/src/OutgoingMessage.ts
var OutgoingMessage = class {
	constructor(documentName) {
		this.encoder = (0, lib0_encoding.createEncoder)();
		(0, lib0_encoding.writeVarString)(this.encoder, documentName);
	}
	createSyncMessage() {
		this.type = MessageType.Sync;
		(0, lib0_encoding.writeVarUint)(this.encoder, MessageType.Sync);
		return this;
	}
	createSyncReplyMessage() {
		this.type = MessageType.SyncReply;
		(0, lib0_encoding.writeVarUint)(this.encoder, MessageType.SyncReply);
		return this;
	}
	createAwarenessUpdateMessage(awareness, changedClients) {
		this.type = MessageType.Awareness;
		this.category = "Update";
		const message = (0, y_protocols_awareness.encodeAwarenessUpdate)(awareness, changedClients || Array.from(awareness.getStates().keys()));
		(0, lib0_encoding.writeVarUint)(this.encoder, MessageType.Awareness);
		(0, lib0_encoding.writeVarUint8Array)(this.encoder, message);
		return this;
	}
	writeQueryAwareness() {
		this.type = MessageType.QueryAwareness;
		this.category = "Update";
		(0, lib0_encoding.writeVarUint)(this.encoder, MessageType.QueryAwareness);
		return this;
	}
	writeTokenSyncRequest() {
		this.type = MessageType.Auth;
		this.category = "TokenSync";
		(0, lib0_encoding.writeVarUint)(this.encoder, MessageType.Auth);
		(0, _hocuspocus_common.writeTokenSyncRequest)(this.encoder);
		return this;
	}
	writeAuthenticated(readonly) {
		this.type = MessageType.Auth;
		this.category = "Authenticated";
		(0, lib0_encoding.writeVarUint)(this.encoder, MessageType.Auth);
		(0, _hocuspocus_common.writeAuthenticated)(this.encoder, readonly ? "readonly" : "read-write");
		return this;
	}
	writePermissionDenied(reason) {
		this.type = MessageType.Auth;
		this.category = "PermissionDenied";
		(0, lib0_encoding.writeVarUint)(this.encoder, MessageType.Auth);
		(0, _hocuspocus_common.writePermissionDenied)(this.encoder, reason);
		return this;
	}
	writeFirstSyncStepFor(document) {
		this.category = "SyncStep1";
		(0, y_protocols_sync.writeSyncStep1)(this.encoder, document);
		return this;
	}
	writeUpdate(update) {
		this.category = "Update";
		(0, y_protocols_sync.writeUpdate)(this.encoder, update);
		return this;
	}
	writeStateless(payload) {
		this.category = "Stateless";
		(0, lib0_encoding.writeVarUint)(this.encoder, MessageType.Stateless);
		(0, lib0_encoding.writeVarString)(this.encoder, payload);
		return this;
	}
	writeBroadcastStateless(payload) {
		this.category = "Stateless";
		(0, lib0_encoding.writeVarUint)(this.encoder, MessageType.BroadcastStateless);
		(0, lib0_encoding.writeVarString)(this.encoder, payload);
		return this;
	}
	writeSyncStatus(updateSaved) {
		this.category = "SyncStatus";
		(0, lib0_encoding.writeVarUint)(this.encoder, MessageType.SyncStatus);
		(0, lib0_encoding.writeVarUint)(this.encoder, updateSaved ? 1 : 0);
		return this;
	}
	writeCloseMessage(reason) {
		this.type = MessageType.CLOSE;
		(0, lib0_encoding.writeVarUint)(this.encoder, MessageType.CLOSE);
		(0, lib0_encoding.writeVarString)(this.encoder, reason);
		return this;
	}
	toUint8Array() {
		return (0, lib0_encoding.toUint8Array)(this.encoder);
	}
};

//#endregion
//#region packages/server/src/MessageReceiver.ts
var MessageReceiver = class {
	constructor(message, defaultTransactionOrigin) {
		this.message = message;
		this.defaultTransactionOrigin = defaultTransactionOrigin;
	}
	async apply(document, connection, reply) {
		const { message } = this;
		const type = message.readVarUint();
		const emptyMessageLength = message.length;
		switch (type) {
			case MessageType.Sync:
			case MessageType.SyncReply:
				message.writeVarUint(MessageType.Sync);
				await this.readSyncMessage(message, document, connection, reply, type !== MessageType.SyncReply);
				if (message.length > emptyMessageLength + 1) {
					if (reply) reply(message.toUint8Array());
					else if (connection) connection.send(message.toUint8Array());
				}
				break;
			case MessageType.Awareness: {
				let update = message.readVarUint8Array();
				const origin = connection ? {
					source: "connection",
					connection
				} : this.defaultTransactionOrigin ?? { source: "local" };
				const scratch = new y_protocols_awareness.Awareness(new yjs.Doc());
				(0, y_protocols_awareness.applyAwarenessUpdate)(scratch, update, null);
				await document.callbacks.beforeHandleAwareness(document, scratch.getStates(), origin);
				update = (0, y_protocols_awareness.encodeAwarenessUpdate)(scratch, [...scratch.getStates().keys()]);
				(0, y_protocols_awareness.applyAwarenessUpdate)(document.awareness, update, origin);
				break;
			}
			case MessageType.QueryAwareness:
				this.applyQueryAwarenessMessage(document, connection, reply);
				break;
			case MessageType.Stateless:
				connection?.callbacks.statelessCallback({
					connection,
					documentName: document.name,
					document,
					payload: (0, lib0_decoding.readVarString)(message.decoder)
				});
				break;
			case MessageType.BroadcastStateless: {
				const msg = message.readVarString();
				document.getConnections().forEach((connection) => {
					connection.sendStateless(msg);
				});
				break;
			}
			case MessageType.CLOSE:
				connection?.close({
					code: 1e3,
					reason: "provider_initiated"
				});
				break;
			case MessageType.Auth:
				if (message.readVarUint() === _hocuspocus_common.AuthMessageType.Token) {
					connection?.callbacks.onTokenSyncCallback({ token: message.readVarString() });
					break;
				}
				console.error("Received an authentication message on a connection that is already fully authenticated. Probably your provider has been destroyed + recreated really fast.");
				break;
			default: console.error(`Unable to handle message of type ${type}: no handler defined! Are your provider/server versions aligned?`);
		}
	}
	async readSyncMessage(message, document, connection, reply, requestFirstSync = true) {
		const type = message.readVarUint();
		const messageAddress = connection?.messageAddress ?? document.name;
		if (connection) await connection.callbacks.beforeSync(connection, {
			type,
			payload: message.peekVarUint8Array()
		});
		switch (type) {
			case y_protocols_sync.messageYjsSyncStep1:
				(0, y_protocols_sync.readSyncStep1)(message.decoder, message.encoder, document);
				if (reply && requestFirstSync) reply(new OutgoingMessage(messageAddress).createSyncReplyMessage().writeFirstSyncStepFor(document).toUint8Array());
				else if (connection) {
					const syncMessage = new OutgoingMessage(messageAddress).createSyncMessage().writeFirstSyncStepFor(document);
					connection.send(syncMessage.toUint8Array());
				}
				break;
			case y_protocols_sync.messageYjsSyncStep2:
				if (connection?.readOnly) {
					const snapshot = yjs.snapshot(document);
					const update = lib0_decoding.readVarUint8Array(message.decoder);
					if (yjs.snapshotContainsUpdate(snapshot, update)) {
						const ackMessage = new OutgoingMessage(messageAddress).writeSyncStatus(true);
						connection.send(ackMessage.toUint8Array());
					} else {
						const ackMessage = new OutgoingMessage(messageAddress).writeSyncStatus(false);
						connection.send(ackMessage.toUint8Array());
					}
					break;
				}
				(0, y_protocols_sync.readSyncStep2)(message.decoder, document, connection ? {
					source: "connection",
					connection
				} : this.defaultTransactionOrigin ?? { source: "local" });
				if (connection) connection.send(new OutgoingMessage(messageAddress).writeSyncStatus(true).toUint8Array());
				break;
			case y_protocols_sync.messageYjsUpdate:
				if (connection?.readOnly) {
					connection.send(new OutgoingMessage(messageAddress).writeSyncStatus(false).toUint8Array());
					break;
				}
				(0, y_protocols_sync.readUpdate)(message.decoder, document, connection ? {
					source: "connection",
					connection
				} : this.defaultTransactionOrigin ?? { source: "local" });
				if (connection) connection.send(new OutgoingMessage(messageAddress).writeSyncStatus(true).toUint8Array());
				break;
			default: throw new Error(`Received a message with an unknown type: ${type}`);
		}
		return type;
	}
	applyQueryAwarenessMessage(document, connection, reply) {
		const message = new OutgoingMessage(connection?.messageAddress ?? document.name).createAwarenessUpdateMessage(document.awareness);
		if (reply) reply(message.toUint8Array());
	}
};

//#endregion
//#region packages/server/src/Connection.ts
var Connection = class {
	/**
	* The address string prefixed to outgoing messages.
	* Session-aware clients get `documentName\0sessionId`; legacy clients get plain `documentName`.
	*/
	get messageAddress() {
		return this.sessionId ? `${this.document.name}\0${this.sessionId}` : this.document.name;
	}
	/**
	* Constructor.
	*/
	constructor(connection, request, document, socketId, context, readOnly = false, sessionId, providerVersion) {
		this.callbacks = {
			onClose: [(document, event) => {}],
			beforeHandleMessage: (connection, update) => Promise.resolve(),
			beforeSync: (connection, payload) => Promise.resolve(),
			statelessCallback: (payload) => Promise.resolve(),
			onTokenSyncCallback: (payload) => Promise.resolve()
		};
		this.messageQueue = [];
		this.processingPromise = Promise.resolve();
		this.webSocket = connection;
		this.context = context;
		this.document = document;
		this.request = request;
		this.socketId = socketId;
		this.readOnly = readOnly;
		this.sessionId = sessionId ?? null;
		this.providerVersion = providerVersion ?? null;
		this.document.addConnection(this);
		this.sendCurrentAwareness();
	}
	/**
	* Set a callback that will be triggered when the connection is closed
	*/
	onClose(callback) {
		this.callbacks.onClose.push(callback);
		return this;
	}
	/**
	* Set a callback that will be triggered when an stateless message is received
	*/
	onStatelessCallback(callback) {
		this.callbacks.statelessCallback = callback;
		return this;
	}
	/**
	* Set a callback that will be triggered before an message is handled
	*/
	beforeHandleMessage(callback) {
		this.callbacks.beforeHandleMessage = callback;
		return this;
	}
	/**
	* Set a callback that will be triggered before a sync message is handled
	*/
	beforeSync(callback) {
		this.callbacks.beforeSync = callback;
		return this;
	}
	/**
	* Set a callback that will be triggered when on token sync message is received
	*/
	onTokenSyncCallback(callback) {
		this.callbacks.onTokenSyncCallback = callback;
		return this;
	}
	/**
	* Returns a promise that resolves when all queued messages have been processed.
	*/
	waitForPendingMessages() {
		return this.processingPromise;
	}
	/**
	* Send the given message
	*/
	send(message) {
		if (this.webSocket.readyState === _hocuspocus_common.WsReadyStates.Closing || this.webSocket.readyState === _hocuspocus_common.WsReadyStates.Closed) {
			this.close();
			return;
		}
		try {
			this.webSocket.send(message);
		} catch (exception) {
			this.close();
		}
	}
	/**
	* Send a stateless message with payload
	*/
	sendStateless(payload) {
		const message = new OutgoingMessage(this.messageAddress).writeStateless(payload);
		this.send(message.toUint8Array());
	}
	/**
	* Request current token from the client
	*/
	requestToken() {
		const message = new OutgoingMessage(this.messageAddress).writeTokenSyncRequest();
		this.send(message.toUint8Array());
	}
	/**
	* Graceful wrapper around the WebSocket close method.
	*/
	close(event) {
		if (this.document.hasConnection(this)) {
			this.document.removeConnection(this);
			this.callbacks.onClose.forEach((callback) => callback(this.document, event));
			const closeMessage = new OutgoingMessage(this.messageAddress);
			closeMessage.writeCloseMessage(event?.reason ?? "Server closed the connection");
			this.send(closeMessage.toUint8Array());
		}
	}
	/**
	* Send the current document awareness to the client, if any
	* @private
	*/
	sendCurrentAwareness() {
		if (!this.document.hasAwarenessStates()) return;
		const awarenessMessage = new OutgoingMessage(this.messageAddress).createAwarenessUpdateMessage(this.document.awareness);
		this.send(awarenessMessage.toUint8Array());
	}
	/**
	* Handle an incoming message
	* @public
	*/
	handleMessage(data) {
		this.messageQueue.push(data);
		if (this.messageQueue.length === 1) this.processingPromise = this.processMessages();
	}
	async processMessages() {
		while (this.messageQueue.length > 0) {
			const rawUpdate = this.messageQueue.at(0);
			const message = new IncomingMessage(rawUpdate);
			const rawKey = message.readVarString();
			const sepIdx = rawKey.indexOf("\0");
			const documentName = sepIdx === -1 ? rawKey : rawKey.substring(0, sepIdx);
			if (documentName !== this.document.name) {
				this.messageQueue.shift();
				continue;
			}
			message.writeVarString(this.messageAddress);
			try {
				await this.callbacks.beforeHandleMessage(this, rawUpdate);
				await new MessageReceiver(message).apply(this.document, this);
			} catch (e) {
				console.error(`closing connection ${this.socketId} (while handling ${documentName}) because of exception`, e);
				this.close({
					code: "code" in e && typeof e.code === "number" ? e.code : _hocuspocus_common.ResetConnection.code,
					reason: "reason" in e ? e.reason : _hocuspocus_common.ResetConnection.reason
				});
			}
			this.messageQueue.shift();
		}
	}
};

//#endregion
//#region packages/server/src/Document.ts
var Document = class extends yjs.Doc {
	/**
	* Constructor.
	*/
	constructor(name, yDocOptions) {
		super(yDocOptions);
		this.callbacks = {
			onUpdate: (document, origin, update) => {},
			beforeBroadcastStateless: (document, stateless) => {},
			beforeHandleAwareness: (document, states, transactionOrigin) => Promise.resolve()
		};
		this.connections = /* @__PURE__ */ new Map();
		this.directConnectionsCount = 0;
		this.isDestroyed = false;
		this.saveMutex = new async_mutex.Mutex();
		this.lastChangeTime = 0;
		this.name = name;
		this.awareness = new y_protocols_awareness.Awareness(this);
		this.awareness.setLocalState(null);
		this.awareness.on("update", this.handleAwarenessUpdate.bind(this));
		this.on("update", this.handleUpdate.bind(this));
		this.isLoading = true;
	}
	/**
	* Check if the Document (XMLFragment or Map) is empty
	*/
	isEmpty(fieldName) {
		return !this.get(fieldName)._start && !this.get(fieldName)._map.size;
	}
	/**
	* Merge the given document(s) into this one
	*/
	merge(documents) {
		(Array.isArray(documents) ? documents : [documents]).forEach((document) => {
			(0, yjs.applyUpdate)(this, (0, yjs.encodeStateAsUpdate)(document));
		});
		return this;
	}
	/**
	* Set a callback that will be triggered when the document is updated
	*/
	onUpdate(callback) {
		this.callbacks.onUpdate = callback;
		return this;
	}
	/**
	* Set a callback that will be triggered before a stateless message is broadcasted
	*/
	beforeBroadcastStateless(callback) {
		this.callbacks.beforeBroadcastStateless = callback;
		return this;
	}
	/**
	* Set a callback that will be triggered before an inbound awareness update
	* is applied to this document's awareness state. The callback receives the
	* document, the decoded per-client states as a mutable `Map`, and the
	* `TransactionOrigin` that will be forwarded to `applyAwarenessUpdate`.
	* Use `isTransactionOrigin(origin)` to discriminate sources. Mutate the
	* map in place (set/delete/field changes) to rewrite the update, or throw
	* to reject it entirely.
	*/
	beforeHandleAwareness(callback) {
		this.callbacks.beforeHandleAwareness = callback;
		return this;
	}
	/**
	* Register a connection and a set of clients on this document keyed by the
	* underlying websocket connection
	*/
	addConnection(connection) {
		this.connections.set(connection, { clients: /* @__PURE__ */ new Set() });
		return this;
	}
	/**
	* Is the given connection registered on this document
	*/
	hasConnection(connection) {
		return this.connections.has(connection);
	}
	/**
	* Remove the given connection from this document
	*/
	removeConnection(connection) {
		const entry = this.connections.get(connection);
		if (entry) (0, y_protocols_awareness.removeAwarenessStates)(this.awareness, Array.from(entry.clients), null);
		this.connections.delete(connection);
		return this;
	}
	addDirectConnection() {
		this.directConnectionsCount += 1;
		return this;
	}
	removeDirectConnection() {
		if (this.directConnectionsCount > 0) this.directConnectionsCount -= 1;
		return this;
	}
	/**
	* Get the number of active connections for this document
	*/
	getConnectionsCount() {
		return this.connections.size + this.directConnectionsCount;
	}
	/**
	* Get an array of registered connections
	*/
	getConnections() {
		return Array.from(this.connections.keys());
	}
	/**
	* Get the client ids for the given connection instance
	*/
	getClients(connection) {
		const entry = this.connections.get(connection);
		return entry?.clients === void 0 ? /* @__PURE__ */ new Set() : entry.clients;
	}
	/**
	* Has the document awareness states
	*/
	hasAwarenessStates() {
		return this.awareness.getStates().size > 0;
	}
	/**
	* Apply the given awareness update
	*/
	applyAwarenessUpdate(connection, update) {
		(0, y_protocols_awareness.applyAwarenessUpdate)(this.awareness, update, {
			source: "connection",
			connection
		});
		return this;
	}
	/**
	* Handle an awareness update and sync changes to clients
	* @private
	*/
	handleAwarenessUpdate({ added, updated, removed }, origin) {
		const changedClients = added.concat(updated, removed);
		const originConnection = isTransactionOrigin(origin) && origin.source === "connection" ? origin.connection : null;
		if (originConnection !== null) {
			const entry = this.connections.get(originConnection);
			if (entry) {
				added.forEach((clientId) => entry.clients.add(clientId));
				removed.forEach((clientId) => entry.clients.delete(clientId));
			}
		}
		for (const connection of this.getConnections()) {
			const awarenessMessage = new OutgoingMessage(connection.messageAddress).createAwarenessUpdateMessage(this.awareness, changedClients);
			connection.send(awarenessMessage.toUint8Array());
		}
		return this;
	}
	/**
	* Handle an updated document and sync changes to clients
	*/
	handleUpdate(update, origin) {
		this.callbacks.onUpdate(this, origin, update);
		for (const connection of this.getConnections()) {
			const message = new OutgoingMessage(connection.messageAddress).createSyncMessage().writeUpdate(update);
			connection.send(message.toUint8Array());
		}
		return this;
	}
	/**
	* Broadcast stateless message to all connections
	*/
	broadcastStateless(payload, filter) {
		this.callbacks.beforeBroadcastStateless(this, payload);
		(filter ? this.getConnections().filter(filter) : this.getConnections()).forEach((connection) => {
			connection.sendStateless(payload);
		});
	}
	destroy() {
		super.destroy();
		this.isDestroyed = true;
	}
};

//#endregion
//#region packages/server/package.json
var version = "4.1.0";

//#endregion
//#region packages/server/src/util/getParameters.ts
/**
* Get parameters by the given request
*/
function getParameters(request) {
	const url = request?.url;
	if (!url) return new URLSearchParams();
	return url.includes("://") ? new URL(url).searchParams : new URLSearchParams(url.split("?")[1] || "");
}

//#endregion
//#region packages/server/src/ClientConnection.ts
/**
* The `ClientConnection` class is responsible for handling an incoming WebSocket
*
* TODO-refactor:
* - use event handlers instead of calling hooks directly, hooks should probably be called from Hocuspocus.ts
*/
var ClientConnection = class {
	/**
	* The `ClientConnection` class receives incoming WebSocket connections,
	* runs all hooks:
	*
	*  - onConnect for all connections
	*  - onAuthenticate only if required
	*
	* … and if nothings fails it'll fully establish the connection and
	* load the Document then.
	*/
	constructor(websocket, request, documentProvider, hooks, opts, defaultContext = {}) {
		this.websocket = websocket;
		this.request = request;
		this.documentProvider = documentProvider;
		this.hooks = hooks;
		this.opts = opts;
		this.defaultContext = defaultContext;
		this.documentConnections = {};
		this.incomingMessageQueue = {};
		this.documentConnectionsEstablished = /* @__PURE__ */ new Set();
		this.hookPayloads = {};
		this.callbacks = { onClose: [(document, payload) => {}] };
		this.socketId = node_crypto.default.randomUUID();
		this.lastMessageReceivedAt = Date.now();
		this.check = () => {
			if (Date.now() - this.lastMessageReceivedAt > this.timeout) this.close(_hocuspocus_common.ConnectionTimeout);
		};
		this.setUpNewConnection = async (rawKey, documentName, sessionId) => {
			const hookPayload = this.hookPayloads[rawKey];
			const document = await this.documentProvider.createDocument(documentName, hookPayload.request, hookPayload.socketId, hookPayload.connectionConfig, hookPayload.context);
			const connection = this.createConnection(this.websocket, document, hookPayload, sessionId, hookPayload.providerVersion);
			connection.onClose((document, event) => {
				delete this.hookPayloads[rawKey];
				delete this.documentConnections[rawKey];
				delete this.incomingMessageQueue[rawKey];
				this.documentConnectionsEstablished.delete(rawKey);
			});
			connection.onTokenSyncCallback(async (payload) => {
				try {
					return await this.hooks("onTokenSync", {
						...hookPayload,
						...payload,
						document,
						connection,
						documentName
					}, (contextAdditions) => {
						hookPayload.context = {
							...hookPayload.context,
							...contextAdditions
						};
					});
				} catch (err) {
					console.error(err);
					const error = {
						..._hocuspocus_common.Unauthorized,
						...err
					};
					connection.close({
						code: error.code,
						reason: error.reason
					});
				}
			});
			this.documentConnections[rawKey] = connection;
			if (this.websocket.readyState === _hocuspocus_common.WsReadyStates.Closing || this.websocket.readyState === _hocuspocus_common.WsReadyStates.Closed) {
				this.close();
				return;
			}
			this.incomingMessageQueue[rawKey]?.forEach((input) => {
				connection.handleMessage(input);
			});
			await this.hooks("connected", {
				...hookPayload,
				documentName,
				context: hookPayload.context,
				connection
			});
		};
		this.handleQueueingMessage = async (data, rawKey, documentName) => {
			try {
				const tmpMsg = new IncomingMessage(data);
				lib0_decoding.readVarString(tmpMsg.decoder);
				if (!(lib0_decoding.readVarUint(tmpMsg.decoder) === MessageType.Auth && !this.documentConnectionsEstablished.has(rawKey))) {
					this.incomingMessageQueue[rawKey].push(data);
					return;
				}
				this.documentConnectionsEstablished.add(rawKey);
				lib0_decoding.readVarUint(tmpMsg.decoder);
				const token = lib0_decoding.readVarString(tmpMsg.decoder);
				let providerVersion = null;
				if (lib0_decoding.hasContent(tmpMsg.decoder)) providerVersion = lib0_decoding.readVarString(tmpMsg.decoder);
				const sepIdx = rawKey.indexOf("\0");
				const sessionId = sepIdx === -1 ? null : rawKey.substring(sepIdx + 1);
				const responseAddress = rawKey;
				try {
					const hookPayload = this.hookPayloads[rawKey];
					hookPayload.providerVersion = providerVersion;
					await this.hooks("onConnect", {
						...hookPayload,
						documentName
					}, (contextAdditions) => {
						hookPayload.context = {
							...hookPayload.context,
							...contextAdditions
						};
					});
					await this.hooks("onAuthenticate", {
						token,
						...hookPayload,
						documentName
					}, (contextAdditions) => {
						hookPayload.context = {
							...hookPayload.context,
							...contextAdditions
						};
					});
					hookPayload.connectionConfig.isAuthenticated = true;
					const message = new OutgoingMessage(responseAddress).writeAuthenticated(hookPayload.connectionConfig.readOnly);
					this.websocket.send(message.toUint8Array());
					await this.setUpNewConnection(rawKey, documentName, sessionId);
				} catch (err) {
					const error = err || _hocuspocus_common.Forbidden;
					const message = new OutgoingMessage(responseAddress).writePermissionDenied(error.reason ?? "permission-denied");
					this.websocket.send(message.toUint8Array());
					this.documentConnectionsEstablished.delete(rawKey);
					delete this.hookPayloads[rawKey];
					delete this.incomingMessageQueue[rawKey];
				}
			} catch (error) {
				console.error(error);
				this.websocket.close(_hocuspocus_common.ResetConnection.code, _hocuspocus_common.ResetConnection.reason);
			}
		};
		this.handleMessage = (data) => {
			this.lastMessageReceivedAt = Date.now();
			try {
				const tmpMsg = new IncomingMessage(data);
				const rawKey = lib0_decoding.readVarString(tmpMsg.decoder);
				const sepIdx = rawKey.indexOf("\0");
				const documentName = sepIdx === -1 ? rawKey : rawKey.substring(0, sepIdx);
				const connection = this.documentConnections[rawKey] ?? this.documentConnections[documentName];
				if (connection) {
					connection.handleMessage(data);
					return;
				}
				if (this.incomingMessageQueue[rawKey] === void 0) {
					this.incomingMessageQueue[rawKey] = [];
					if (this.hookPayloads[rawKey]) throw new Error("first message, but hookPayloads exists");
					this.hookPayloads[rawKey] = {
						instance: this.documentProvider,
						request: this.request,
						connectionConfig: {
							readOnly: false,
							isAuthenticated: false
						},
						requestHeaders: this.request.headers,
						requestParameters: getParameters(this.request),
						socketId: this.socketId,
						context: { ...this.defaultContext },
						providerVersion: null
					};
				}
				this.handleQueueingMessage(data, rawKey, documentName);
			} catch (closeError) {
				console.error(closeError);
				this.websocket.close(_hocuspocus_common.Unauthorized.code, _hocuspocus_common.Unauthorized.reason);
			}
		};
		this.timeout = opts.timeout;
		this.pingInterval = setInterval(this.check, this.timeout);
	}
	/**
	* Handle WebSocket close event. Call this from your integration
	* when the WebSocket connection closes.
	*/
	handleClose(event) {
		this.close(event);
		clearInterval(this.pingInterval);
	}
	close(event) {
		Object.values(this.documentConnections).forEach((connection) => connection.close(event));
	}
	/**
	* Set a callback that will be triggered when the connection is closed
	*/
	onClose(callback) {
		this.callbacks.onClose.push(callback);
		return this;
	}
	/**
	* Create a new connection by the given request and document
	*/
	createConnection(connection, document, hookPayload, sessionId, providerVersion) {
		const instance = new Connection(connection, hookPayload.request, document, hookPayload.socketId, hookPayload.context, hookPayload.connectionConfig.readOnly, sessionId, providerVersion);
		instance.onClose(async (document, event) => {
			await instance.waitForPendingMessages();
			const disconnectHookPayload = {
				instance: this.documentProvider,
				clientsCount: document.getConnectionsCount(),
				context: hookPayload.context,
				document,
				socketId: hookPayload.socketId,
				documentName: document.name,
				requestHeaders: hookPayload.request.headers,
				requestParameters: getParameters(hookPayload.request)
			};
			await this.hooks("onDisconnect", disconnectHookPayload);
			this.callbacks.onClose.forEach((callback) => callback(document, disconnectHookPayload));
		});
		instance.onStatelessCallback(async (payload) => {
			try {
				return await this.hooks("onStateless", payload);
			} catch (error) {
				if (error?.message) throw error;
			}
		});
		instance.beforeHandleMessage((connection, update) => {
			const beforeHandleMessagePayload = {
				instance: this.documentProvider,
				clientsCount: document.getConnectionsCount(),
				context: hookPayload.context,
				document,
				socketId: hookPayload.socketId,
				connection,
				documentName: document.name,
				requestHeaders: hookPayload.request.headers,
				requestParameters: getParameters(hookPayload.request),
				update
			};
			return this.hooks("beforeHandleMessage", beforeHandleMessagePayload);
		});
		instance.beforeSync((connection, payload) => {
			const beforeSyncPayload = {
				clientsCount: document.getConnectionsCount(),
				context: hookPayload.context,
				document,
				documentName: document.name,
				connection,
				type: payload.type,
				payload: payload.payload
			};
			return this.hooks("beforeSync", beforeSyncPayload);
		});
		return instance;
	}
};

//#endregion
//#region packages/server/src/DirectConnection.ts
var DirectConnection = class {
	/**
	* Constructor.
	*/
	constructor(document, instance, context) {
		this.document = null;
		this.document = document;
		this.instance = instance;
		this.context = context ?? {};
		this.document.addDirectConnection();
	}
	async transact(transaction) {
		if (!this.document) throw new Error("direct connection closed");
		this.document.transact((x) => {
			transaction(this.document);
		}, {
			source: "local",
			context: this.context
		});
	}
	async disconnect() {
		if (this.document) {
			this.document?.removeDirectConnection();
			await this.instance.storeDocumentHooks(this.document, {
				clientsCount: this.document.getConnectionsCount(),
				lastContext: this.context,
				lastTransactionOrigin: {
					source: "local",
					context: this.context
				},
				document: this.document,
				documentName: this.document.name,
				instance: this.instance
			}, true);
			if (this.document.getConnectionsCount() === 0 && !this.document.saveMutex.isLocked()) {
				await this.instance.hooks("onDisconnect", {
					instance: this.instance,
					clientsCount: this.document.getConnectionsCount(),
					context: this.context,
					document: this.document,
					socketId: "server",
					documentName: this.document.name,
					requestHeaders: new Headers(),
					requestParameters: new node_url.URLSearchParams()
				});
				await this.instance.unloadDocument(this.document);
			}
			this.document = null;
		}
	}
};

//#endregion
//#region packages/server/src/util/debounce.ts
const useDebounce = () => {
	const timers = /* @__PURE__ */ new Map();
	const runningExecutions = /* @__PURE__ */ new Map();
	const debounce = async (id, func, debounce, maxDebounce) => {
		const old = timers.get(id);
		const start = old?.start || Date.now();
		const run = async () => {
			if (runningExecutions.has(id)) await runningExecutions.get(id);
			timers.delete(id);
			const execution = func();
			runningExecutions.set(id, execution);
			const executionResult = await execution;
			runningExecutions.delete(id);
			return executionResult;
		};
		if (old?.timeout) clearTimeout(old.timeout);
		if (debounce === 0) return run();
		if (Date.now() - start >= maxDebounce) return run();
		timers.set(id, {
			start,
			timeout: setTimeout(run, debounce),
			func: run
		});
	};
	const executeNow = (id) => {
		const old = timers.get(id);
		if (old) {
			clearTimeout(old.timeout);
			return old.func();
		}
	};
	const isDebounced = (id) => {
		return timers.has(id);
	};
	const isCurrentlyExecuting = (id) => {
		return runningExecutions.has(id);
	};
	return {
		debounce,
		isDebounced,
		isCurrentlyExecuting,
		executeNow
	};
};

//#endregion
//#region packages/server/src/Hocuspocus.ts
const defaultConfiguration = {
	name: null,
	timeout: 6e4,
	debounce: 2e3,
	maxDebounce: 1e4,
	quiet: false,
	yDocOptions: {
		gc: true,
		gcFilter: () => true
	},
	unloadImmediately: true
};
var Hocuspocus = class {
	constructor(configuration) {
		this.configuration = {
			...defaultConfiguration,
			extensions: [],
			onConfigure: () => new Promise((r) => r(null)),
			onListen: () => new Promise((r) => r(null)),
			onUpgrade: () => new Promise((r) => r(null)),
			onConnect: () => new Promise((r) => r(null)),
			connected: () => new Promise((r) => r(null)),
			beforeHandleMessage: () => new Promise((r) => r(null)),
			beforeHandleAwareness: () => new Promise((r) => r()),
			beforeSync: () => new Promise((r) => r(null)),
			beforeBroadcastStateless: () => new Promise((r) => r(null)),
			onStateless: () => new Promise((r) => r(null)),
			onChange: () => new Promise((r) => r(null)),
			onCreateDocument: () => new Promise((r) => r(null)),
			onLoadDocument: () => new Promise((r) => r(null)),
			onStoreDocument: () => new Promise((r) => r(null)),
			afterStoreDocument: () => new Promise((r) => r(null)),
			onAwarenessUpdate: () => new Promise((r) => r(null)),
			onRequest: () => new Promise((r) => r(null)),
			onDisconnect: () => new Promise((r) => r(null)),
			onDestroy: () => new Promise((r) => r(null))
		};
		this.loadingDocuments = /* @__PURE__ */ new Map();
		this.unloadingDocuments = /* @__PURE__ */ new Map();
		this.documents = /* @__PURE__ */ new Map();
		this.debouncer = useDebounce();
		if (configuration) this.configure(configuration);
	}
	/**
	* Configure Hocuspocus
	*/
	configure(configuration) {
		this.configuration = {
			...this.configuration,
			...configuration
		};
		this.configuration.extensions.sort((a, b) => {
			const one = typeof a.priority === "undefined" ? 100 : a.priority;
			const two = typeof b.priority === "undefined" ? 100 : b.priority;
			if (one > two) return -1;
			if (one < two) return 1;
			return 0;
		});
		this.configuration.extensions.push({
			onConfigure: this.configuration.onConfigure,
			onListen: this.configuration.onListen,
			onUpgrade: this.configuration.onUpgrade,
			onConnect: this.configuration.onConnect,
			connected: this.configuration.connected,
			onAuthenticate: this.configuration.onAuthenticate,
			onTokenSync: this.configuration.onTokenSync,
			onLoadDocument: this.configuration.onLoadDocument,
			afterLoadDocument: this.configuration.afterLoadDocument,
			beforeHandleMessage: this.configuration.beforeHandleMessage,
			beforeHandleAwareness: this.configuration.beforeHandleAwareness,
			beforeBroadcastStateless: this.configuration.beforeBroadcastStateless,
			beforeSync: this.configuration.beforeSync,
			onStateless: this.configuration.onStateless,
			onChange: this.configuration.onChange,
			onStoreDocument: this.configuration.onStoreDocument,
			afterStoreDocument: this.configuration.afterStoreDocument,
			onAwarenessUpdate: this.configuration.onAwarenessUpdate,
			onRequest: this.configuration.onRequest,
			beforeUnloadDocument: this.configuration.beforeUnloadDocument,
			afterUnloadDocument: this.configuration.afterUnloadDocument,
			onDisconnect: this.configuration.onDisconnect,
			onDestroy: this.configuration.onDestroy
		});
		this.hooks("onConfigure", {
			configuration: this.configuration,
			version,
			instance: this
		});
		return this;
	}
	/**
	* Get the total number of active documents
	*/
	getDocumentsCount() {
		return this.documents.size;
	}
	/**
	* Get the total number of active connections
	*/
	getConnectionsCount() {
		const uniqueSocketIds = /* @__PURE__ */ new Set();
		const totalDirectConnections = Array.from(this.documents.values()).reduce((acc, document) => {
			document.getConnections().forEach(({ socketId }) => {
				uniqueSocketIds.add(socketId);
			});
			return acc + document.directConnectionsCount;
		}, 0);
		return uniqueSocketIds.size + totalDirectConnections;
	}
	/**
	* Immediately execute all pending debounced onStoreDocument calls.
	* Useful during shutdown to ensure documents are persisted and unloaded
	* before the server exits, even when unloadImmediately is false.
	*/
	flushPendingStores() {
		this.documents.forEach((document) => {
			const debounceId = `onStoreDocument-${document.name}`;
			if (!document.isLoading && this.debouncer.isDebounced(debounceId)) this.debouncer.executeNow(debounceId);
		});
	}
	/**
	* Force close one or more connections
	*/
	closeConnections(documentName) {
		this.documents.forEach((document) => {
			if (documentName && document.name !== documentName) return;
			document.connections.forEach((_clients, connection) => {
				connection.close(_hocuspocus_common.ResetConnection);
			});
		});
	}
	/**
	* The `handleConnection` method receives incoming WebSocket connections,
	* runs all hooks:
	*
	*  - onConnect for all connections
	*  - onAuthenticate only if required
	*
	* … and if nothing fails it'll fully establish the connection and
	* load the Document then.
	*/
	handleConnection(incoming, request, defaultContext = {}) {
		const clientConnection = new ClientConnection(incoming, request, this, this.hooks.bind(this), { timeout: this.configuration.timeout }, defaultContext);
		clientConnection.onClose((document, hookPayload) => {
			if (document.getConnectionsCount() > 0) return;
			if (!document.isLoading && this.debouncer.isDebounced(`onStoreDocument-${document.name}`)) {
				if (this.configuration.unloadImmediately) this.debouncer.executeNow(`onStoreDocument-${document.name}`);
			} else this.unloadDocument(document);
		});
		return clientConnection;
	}
	/**
	* Handle update of the given document
	*
	* "connection" is not necessarily type "Connection", it's the Yjs "origin" (which is "Connection" if
	* the update is incoming from the provider, but can be anything if the updates is originated from an extension.
	*/
	handleDocumentUpdate(document, origin, update) {
		const connection = isTransactionOrigin(origin) && origin.source === "connection" ? origin.connection : void 0;
		const request = connection?.request;
		const context = isTransactionOrigin(origin) ? origin.source === "connection" ? origin.connection.context : origin.source === "local" ? origin.context ?? {} : {} : {};
		const changePayload = {
			instance: this,
			clientsCount: document.getConnectionsCount(),
			document,
			documentName: document.name,
			requestHeaders: request?.headers ?? new Headers(),
			requestParameters: getParameters(request),
			socketId: connection?.socketId ?? "",
			update,
			transactionOrigin: origin,
			connection,
			context
		};
		this.hooks("onChange", changePayload);
		if (shouldSkipStoreHooks(origin)) return;
		const storePayload = {
			instance: this,
			clientsCount: document.getConnectionsCount(),
			document,
			lastContext: context,
			lastTransactionOrigin: origin,
			documentName: document.name
		};
		this.storeDocumentHooks(document, storePayload);
	}
	/**
	* Create a new document by the given request
	*/
	async createDocument(documentName, request, socketId, connection, context) {
		if (!documentName.trim()) throw new Error("Document name must not be empty");
		const existingLoadingDoc = this.loadingDocuments.get(documentName);
		if (existingLoadingDoc) return existingLoadingDoc;
		const existingDoc = this.documents.get(documentName);
		if (existingDoc) return Promise.resolve(existingDoc);
		const loadDocPromise = this.loadDocument(documentName, request, socketId, connection, context);
		this.loadingDocuments.set(documentName, loadDocPromise);
		try {
			const doc = await loadDocPromise;
			this.documents.set(documentName, doc);
			this.loadingDocuments.delete(documentName);
			return doc;
		} catch (e) {
			this.loadingDocuments.delete(documentName);
			throw e;
		}
	}
	async loadDocument(documentName, request, socketId, connectionConfig, context) {
		const requestHeaders = request.headers;
		const requestParameters = getParameters(request);
		const resolvedContext = context ?? {};
		const yDocOptions = await this.hooks("onCreateDocument", {
			documentName,
			requestHeaders,
			requestParameters,
			connectionConfig,
			context: resolvedContext,
			socketId,
			instance: this
		});
		const document = new Document(documentName, {
			...this.configuration.yDocOptions,
			...yDocOptions
		});
		const hookPayload = {
			instance: this,
			context: resolvedContext,
			connectionConfig,
			document,
			documentName,
			socketId,
			requestHeaders,
			requestParameters
		};
		try {
			await this.hooks("onLoadDocument", hookPayload, (loadedDocument) => {
				if (loadedDocument instanceof yjs.Doc) (0, yjs.applyUpdate)(document, (0, yjs.encodeStateAsUpdate)(loadedDocument));
				else if (loadedDocument instanceof Uint8Array) (0, yjs.applyUpdate)(document, loadedDocument);
			});
		} catch (e) {
			this.closeConnections(documentName);
			this.unloadDocument(document);
			throw e;
		}
		document.isLoading = false;
		document.onUpdate((document, origin, update) => {
			document.lastChangeTime = Date.now();
			this.handleDocumentUpdate(document, origin, update);
		});
		await this.hooks("afterLoadDocument", hookPayload);
		document.beforeBroadcastStateless((document, stateless) => {
			const hookPayload = {
				document,
				documentName: document.name,
				payload: stateless
			};
			this.hooks("beforeBroadcastStateless", hookPayload);
		});
		document.beforeHandleAwareness((document, states, transactionOrigin) => {
			const connection = isTransactionOrigin(transactionOrigin) && transactionOrigin.source === "connection" ? transactionOrigin.connection : void 0;
			const request = connection?.request;
			return this.hooks("beforeHandleAwareness", {
				awareness: document.awareness,
				clientsCount: document.getConnectionsCount(),
				context: connection?.context,
				document,
				documentName: document.name,
				instance: this,
				requestHeaders: request?.headers ?? new Headers(),
				requestParameters: request ? getParameters(request) : new URLSearchParams(),
				socketId: connection?.socketId ?? "",
				transactionOrigin,
				connection,
				states
			});
		});
		document.awareness.on("update", (update, origin) => {
			this.hooks("onAwarenessUpdate", {
				document,
				documentName,
				instance: this,
				...update,
				transactionOrigin: origin,
				connection: isTransactionOrigin(origin) && origin.source === "connection" ? origin.connection : void 0,
				awareness: document.awareness,
				states: (0, _hocuspocus_common.awarenessStatesToArray)(document.awareness.getStates())
			});
		});
		return document;
	}
	storeDocumentHooks(document, hookPayload, immediately) {
		const debounceId = `onStoreDocument-${document.name}`;
		return this.debouncer.debounce(debounceId, async () => {
			try {
				await document.saveMutex.runExclusive(async () => {
					await this.hooks("onStoreDocument", hookPayload);
					await this.hooks("afterStoreDocument", hookPayload);
				});
			} catch (error) {
				if (error instanceof _hocuspocus_common.SkipFurtherHooksError) {
					setTimeout(() => {
						if (this.shouldUnloadDocument(document)) this.unloadDocument(document);
					}, 0);
					return;
				}
				console.error("Caught error during storeDocumentHooks. Document stays in memory to avoid data loss", error);
				return;
			}
			setTimeout(() => {
				if (this.shouldUnloadDocument(document)) this.unloadDocument(document);
			}, 0);
		}, immediately ? 0 : this.configuration.debounce, this.configuration.maxDebounce);
	}
	/**
	* Run the given hook on all configured extensions.
	* Runs the given callback after each hook.
	*/
	hooks(name, payload, callback = null) {
		const { extensions } = this.configuration;
		let chain = Promise.resolve();
		extensions.filter((extension) => typeof extension[name] === "function").forEach((extension) => {
			chain = chain.then(() => extension[name]?.(payload)).catch((error) => {
				if (error?.message) console.error(`[${name}]`, error.message);
				throw error;
			});
			if (callback) chain = chain.then((...args) => callback(...args));
		});
		return chain;
	}
	shouldUnloadDocument(document) {
		return (this.debouncer.isDebounced(`onStoreDocument-${document.name}`) || this.debouncer.isCurrentlyExecuting(`onStoreDocument-${document.name}`) || document.saveMutex.isLocked()) === false && document.getConnectionsCount() === 0;
	}
	async unloadDocument(document) {
		const documentName = document.name;
		if (!this.shouldUnloadDocument(document)) return;
		if (!this.documents.has(documentName)) return;
		if (this.unloadingDocuments.has(documentName)) return this.unloadingDocuments.get(documentName);
		const actualUnloadingLogic = async () => {
			try {
				await this.hooks("beforeUnloadDocument", {
					instance: this,
					documentName,
					document
				});
			} catch (e) {
				return;
			}
			if (!this.shouldUnloadDocument(document)) return;
			this.documents.delete(documentName);
			document.destroy();
			await this.hooks("afterUnloadDocument", {
				instance: this,
				documentName
			});
		};
		const unloading = actualUnloadingLogic();
		this.unloadingDocuments.set(documentName, Promise.resolve(unloading));
		await unloading;
		this.unloadingDocuments.delete(documentName);
	}
	async openDirectConnection(documentName, context) {
		return new DirectConnection(await this.createDocument(documentName, new Request("http://localhost"), node_crypto.default.randomUUID(), {
			isAuthenticated: true,
			readOnly: false
		}, context), this, context);
	}
};

//#endregion
//#region node_modules/.pnpm/crossws@0.4.4/node_modules/crossws/dist/_chunks/rolldown-runtime.mjs
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __commonJSMin = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
var __copyProps = (to, from, except, desc) => {
	if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
		key = keys[i];
		if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
			get: ((k) => from[k]).bind(null, key),
			enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
		});
	}
	return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
	value: mod,
	enumerable: true
}) : target, mod));
var __require = /* @__PURE__ */ (0, node_module.createRequire)(require("url").pathToFileURL(__filename).href);

//#endregion
//#region node_modules/.pnpm/crossws@0.4.4/node_modules/crossws/dist/_chunks/adapter.mjs
var AdapterHookable = class {
	options;
	constructor(options) {
		this.options = options || {};
	}
	callHook(name, arg1, arg2) {
		const globalHook = this.options.hooks?.[name];
		const globalPromise = globalHook?.(arg1, arg2);
		const request = arg1.request || arg1;
		const resolveHooksPromise = this.options.resolve?.(request);
		if (!resolveHooksPromise) return globalPromise;
		const resolvePromise = resolveHooksPromise instanceof Promise ? resolveHooksPromise.then((hooks) => hooks?.[name]) : resolveHooksPromise?.[name];
		return Promise.all([globalPromise, resolvePromise]).then(([globalRes, hook]) => {
			const hookResPromise = hook?.(arg1, arg2);
			return hookResPromise instanceof Promise ? hookResPromise.then((hookRes) => hookRes || globalRes) : hookResPromise || globalRes;
		});
	}
	async upgrade(request) {
		let namespace = this.options.getNamespace?.(request) ?? new URL(request.url).pathname;
		const context = request.context || {};
		try {
			const res = await this.callHook("upgrade", request);
			if (!res) return {
				context,
				namespace
			};
			if (res.namespace) namespace = res.namespace;
			if (res.context) Object.assign(context, res.context);
			if (res instanceof Response) return {
				context,
				namespace,
				endResponse: res
			};
			if (res.headers) return {
				context,
				namespace,
				upgradeHeaders: res.headers
			};
		} catch (error) {
			const errResponse = error.response || error;
			if (errResponse instanceof Response) return {
				context,
				namespace,
				endResponse: errResponse
			};
			throw error;
		}
		return {
			context,
			namespace
		};
	}
};
function adapterUtils(globalPeers) {
	return {
		peers: globalPeers,
		publish(topic, message, options) {
			for (const peers of options?.namespace ? [globalPeers.get(options.namespace) || []] : globalPeers.values()) {
				let firstPeerWithTopic;
				for (const peer of peers) if (peer.topics.has(topic)) {
					firstPeerWithTopic = peer;
					break;
				}
				if (firstPeerWithTopic) {
					firstPeerWithTopic.send(message, options);
					firstPeerWithTopic.publish(topic, message, options);
				}
			}
		}
	};
}
function getPeers(globalPeers, namespace) {
	if (!namespace) throw new Error("Websocket publish namespace missing.");
	let peers = globalPeers.get(namespace);
	if (!peers) {
		peers = /* @__PURE__ */ new Set();
		globalPeers.set(namespace, peers);
	}
	return peers;
}

//#endregion
//#region node_modules/.pnpm/crossws@0.4.4/node_modules/crossws/dist/_chunks/libs/ws.mjs
var require_constants = /* @__PURE__ */ __commonJSMin(((exports, module) => {
	const BINARY_TYPES = [
		"nodebuffer",
		"arraybuffer",
		"fragments"
	];
	const hasBlob = typeof Blob !== "undefined";
	if (hasBlob) BINARY_TYPES.push("blob");
	module.exports = {
		BINARY_TYPES,
		CLOSE_TIMEOUT: 3e4,
		EMPTY_BUFFER: Buffer.alloc(0),
		GUID: "258EAFA5-E914-47DA-95CA-C5AB0DC85B11",
		hasBlob,
		kForOnEventAttribute: Symbol("kIsForOnEventAttribute"),
		kListener: Symbol("kListener"),
		kStatusCode: Symbol("status-code"),
		kWebSocket: Symbol("websocket"),
		NOOP: () => {}
	};
}));
var require_buffer_util = /* @__PURE__ */ __commonJSMin(((exports, module) => {
	const { EMPTY_BUFFER } = require_constants();
	const FastBuffer = Buffer[Symbol.species];
	/**
	* Merges an array of buffers into a new buffer.
	*
	* @param {Buffer[]} list The array of buffers to concat
	* @param {Number} totalLength The total length of buffers in the list
	* @return {Buffer} The resulting buffer
	* @public
	*/
	function concat(list, totalLength) {
		if (list.length === 0) return EMPTY_BUFFER;
		if (list.length === 1) return list[0];
		const target = Buffer.allocUnsafe(totalLength);
		let offset = 0;
		for (let i = 0; i < list.length; i++) {
			const buf = list[i];
			target.set(buf, offset);
			offset += buf.length;
		}
		if (offset < totalLength) return new FastBuffer(target.buffer, target.byteOffset, offset);
		return target;
	}
	/**
	* Masks a buffer using the given mask.
	*
	* @param {Buffer} source The buffer to mask
	* @param {Buffer} mask The mask to use
	* @param {Buffer} output The buffer where to store the result
	* @param {Number} offset The offset at which to start writing
	* @param {Number} length The number of bytes to mask.
	* @public
	*/
	function _mask(source, mask, output, offset, length) {
		for (let i = 0; i < length; i++) output[offset + i] = source[i] ^ mask[i & 3];
	}
	/**
	* Unmasks a buffer using the given mask.
	*
	* @param {Buffer} buffer The buffer to unmask
	* @param {Buffer} mask The mask to use
	* @public
	*/
	function _unmask(buffer, mask) {
		for (let i = 0; i < buffer.length; i++) buffer[i] ^= mask[i & 3];
	}
	/**
	* Converts a buffer to an `ArrayBuffer`.
	*
	* @param {Buffer} buf The buffer to convert
	* @return {ArrayBuffer} Converted buffer
	* @public
	*/
	function toArrayBuffer(buf) {
		if (buf.length === buf.buffer.byteLength) return buf.buffer;
		return buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.length);
	}
	/**
	* Converts `data` to a `Buffer`.
	*
	* @param {*} data The data to convert
	* @return {Buffer} The buffer
	* @throws {TypeError}
	* @public
	*/
	function toBuffer(data) {
		toBuffer.readOnly = true;
		if (Buffer.isBuffer(data)) return data;
		let buf;
		if (data instanceof ArrayBuffer) buf = new FastBuffer(data);
		else if (ArrayBuffer.isView(data)) buf = new FastBuffer(data.buffer, data.byteOffset, data.byteLength);
		else {
			buf = Buffer.from(data);
			toBuffer.readOnly = false;
		}
		return buf;
	}
	module.exports = {
		concat,
		mask: _mask,
		toArrayBuffer,
		toBuffer,
		unmask: _unmask
	};
	/* istanbul ignore else  */
	if (!process.env.WS_NO_BUFFER_UTIL) try {
		const bufferUtil = __require("bufferutil");
		module.exports.mask = function(source, mask, output, offset, length) {
			if (length < 48) _mask(source, mask, output, offset, length);
			else bufferUtil.mask(source, mask, output, offset, length);
		};
		module.exports.unmask = function(buffer, mask) {
			if (buffer.length < 32) _unmask(buffer, mask);
			else bufferUtil.unmask(buffer, mask);
		};
	} catch (e) {}
}));
var require_limiter = /* @__PURE__ */ __commonJSMin(((exports, module) => {
	const kDone = Symbol("kDone");
	const kRun = Symbol("kRun");
	/**
	* A very simple job queue with adjustable concurrency. Adapted from
	* https://github.com/STRML/async-limiter
	*/
	var Limiter = class {
		/**
		* Creates a new `Limiter`.
		*
		* @param {Number} [concurrency=Infinity] The maximum number of jobs allowed
		*     to run concurrently
		*/
		constructor(concurrency) {
			this[kDone] = () => {
				this.pending--;
				this[kRun]();
			};
			this.concurrency = concurrency || Infinity;
			this.jobs = [];
			this.pending = 0;
		}
		/**
		* Adds a job to the queue.
		*
		* @param {Function} job The job to run
		* @public
		*/
		add(job) {
			this.jobs.push(job);
			this[kRun]();
		}
		/**
		* Removes a job from the queue and runs it if possible.
		*
		* @private
		*/
		[kRun]() {
			if (this.pending === this.concurrency) return;
			if (this.jobs.length) {
				const job = this.jobs.shift();
				this.pending++;
				job(this[kDone]);
			}
		}
	};
	module.exports = Limiter;
}));
var require_permessage_deflate = /* @__PURE__ */ __commonJSMin(((exports, module) => {
	const zlib = __require("zlib");
	const bufferUtil = require_buffer_util();
	const Limiter = require_limiter();
	const { kStatusCode } = require_constants();
	const FastBuffer = Buffer[Symbol.species];
	const TRAILER = Buffer.from([
		0,
		0,
		255,
		255
	]);
	const kPerMessageDeflate = Symbol("permessage-deflate");
	const kTotalLength = Symbol("total-length");
	const kCallback = Symbol("callback");
	const kBuffers = Symbol("buffers");
	const kError = Symbol("error");
	let zlibLimiter;
	/**
	* permessage-deflate implementation.
	*/
	var PerMessageDeflate = class {
		/**
		* Creates a PerMessageDeflate instance.
		*
		* @param {Object} [options] Configuration options
		* @param {(Boolean|Number)} [options.clientMaxWindowBits] Advertise support
		*     for, or request, a custom client window size
		* @param {Boolean} [options.clientNoContextTakeover=false] Advertise/
		*     acknowledge disabling of client context takeover
		* @param {Number} [options.concurrencyLimit=10] The number of concurrent
		*     calls to zlib
		* @param {(Boolean|Number)} [options.serverMaxWindowBits] Request/confirm the
		*     use of a custom server window size
		* @param {Boolean} [options.serverNoContextTakeover=false] Request/accept
		*     disabling of server context takeover
		* @param {Number} [options.threshold=1024] Size (in bytes) below which
		*     messages should not be compressed if context takeover is disabled
		* @param {Object} [options.zlibDeflateOptions] Options to pass to zlib on
		*     deflate
		* @param {Object} [options.zlibInflateOptions] Options to pass to zlib on
		*     inflate
		* @param {Boolean} [isServer=false] Create the instance in either server or
		*     client mode
		* @param {Number} [maxPayload=0] The maximum allowed message length
		*/
		constructor(options, isServer, maxPayload) {
			this._maxPayload = maxPayload | 0;
			this._options = options || {};
			this._threshold = this._options.threshold !== void 0 ? this._options.threshold : 1024;
			this._isServer = !!isServer;
			this._deflate = null;
			this._inflate = null;
			this.params = null;
			if (!zlibLimiter) zlibLimiter = new Limiter(this._options.concurrencyLimit !== void 0 ? this._options.concurrencyLimit : 10);
		}
		/**
		* @type {String}
		*/
		static get extensionName() {
			return "permessage-deflate";
		}
		/**
		* Create an extension negotiation offer.
		*
		* @return {Object} Extension parameters
		* @public
		*/
		offer() {
			const params = {};
			if (this._options.serverNoContextTakeover) params.server_no_context_takeover = true;
			if (this._options.clientNoContextTakeover) params.client_no_context_takeover = true;
			if (this._options.serverMaxWindowBits) params.server_max_window_bits = this._options.serverMaxWindowBits;
			if (this._options.clientMaxWindowBits) params.client_max_window_bits = this._options.clientMaxWindowBits;
			else if (this._options.clientMaxWindowBits == null) params.client_max_window_bits = true;
			return params;
		}
		/**
		* Accept an extension negotiation offer/response.
		*
		* @param {Array} configurations The extension negotiation offers/reponse
		* @return {Object} Accepted configuration
		* @public
		*/
		accept(configurations) {
			configurations = this.normalizeParams(configurations);
			this.params = this._isServer ? this.acceptAsServer(configurations) : this.acceptAsClient(configurations);
			return this.params;
		}
		/**
		* Releases all resources used by the extension.
		*
		* @public
		*/
		cleanup() {
			if (this._inflate) {
				this._inflate.close();
				this._inflate = null;
			}
			if (this._deflate) {
				const callback = this._deflate[kCallback];
				this._deflate.close();
				this._deflate = null;
				if (callback) callback(/* @__PURE__ */ new Error("The deflate stream was closed while data was being processed"));
			}
		}
		/**
		*  Accept an extension negotiation offer.
		*
		* @param {Array} offers The extension negotiation offers
		* @return {Object} Accepted configuration
		* @private
		*/
		acceptAsServer(offers) {
			const opts = this._options;
			const accepted = offers.find((params) => {
				if (opts.serverNoContextTakeover === false && params.server_no_context_takeover || params.server_max_window_bits && (opts.serverMaxWindowBits === false || typeof opts.serverMaxWindowBits === "number" && opts.serverMaxWindowBits > params.server_max_window_bits) || typeof opts.clientMaxWindowBits === "number" && !params.client_max_window_bits) return false;
				return true;
			});
			if (!accepted) throw new Error("None of the extension offers can be accepted");
			if (opts.serverNoContextTakeover) accepted.server_no_context_takeover = true;
			if (opts.clientNoContextTakeover) accepted.client_no_context_takeover = true;
			if (typeof opts.serverMaxWindowBits === "number") accepted.server_max_window_bits = opts.serverMaxWindowBits;
			if (typeof opts.clientMaxWindowBits === "number") accepted.client_max_window_bits = opts.clientMaxWindowBits;
			else if (accepted.client_max_window_bits === true || opts.clientMaxWindowBits === false) delete accepted.client_max_window_bits;
			return accepted;
		}
		/**
		* Accept the extension negotiation response.
		*
		* @param {Array} response The extension negotiation response
		* @return {Object} Accepted configuration
		* @private
		*/
		acceptAsClient(response) {
			const params = response[0];
			if (this._options.clientNoContextTakeover === false && params.client_no_context_takeover) throw new Error("Unexpected parameter \"client_no_context_takeover\"");
			if (!params.client_max_window_bits) {
				if (typeof this._options.clientMaxWindowBits === "number") params.client_max_window_bits = this._options.clientMaxWindowBits;
			} else if (this._options.clientMaxWindowBits === false || typeof this._options.clientMaxWindowBits === "number" && params.client_max_window_bits > this._options.clientMaxWindowBits) throw new Error("Unexpected or invalid parameter \"client_max_window_bits\"");
			return params;
		}
		/**
		* Normalize parameters.
		*
		* @param {Array} configurations The extension negotiation offers/reponse
		* @return {Array} The offers/response with normalized parameters
		* @private
		*/
		normalizeParams(configurations) {
			configurations.forEach((params) => {
				Object.keys(params).forEach((key) => {
					let value = params[key];
					if (value.length > 1) throw new Error(`Parameter "${key}" must have only a single value`);
					value = value[0];
					if (key === "client_max_window_bits") {
						if (value !== true) {
							const num = +value;
							if (!Number.isInteger(num) || num < 8 || num > 15) throw new TypeError(`Invalid value for parameter "${key}": ${value}`);
							value = num;
						} else if (!this._isServer) throw new TypeError(`Invalid value for parameter "${key}": ${value}`);
					} else if (key === "server_max_window_bits") {
						const num = +value;
						if (!Number.isInteger(num) || num < 8 || num > 15) throw new TypeError(`Invalid value for parameter "${key}": ${value}`);
						value = num;
					} else if (key === "client_no_context_takeover" || key === "server_no_context_takeover") {
						if (value !== true) throw new TypeError(`Invalid value for parameter "${key}": ${value}`);
					} else throw new Error(`Unknown parameter "${key}"`);
					params[key] = value;
				});
			});
			return configurations;
		}
		/**
		* Decompress data. Concurrency limited.
		*
		* @param {Buffer} data Compressed data
		* @param {Boolean} fin Specifies whether or not this is the last fragment
		* @param {Function} callback Callback
		* @public
		*/
		decompress(data, fin, callback) {
			zlibLimiter.add((done) => {
				this._decompress(data, fin, (err, result) => {
					done();
					callback(err, result);
				});
			});
		}
		/**
		* Compress data. Concurrency limited.
		*
		* @param {(Buffer|String)} data Data to compress
		* @param {Boolean} fin Specifies whether or not this is the last fragment
		* @param {Function} callback Callback
		* @public
		*/
		compress(data, fin, callback) {
			zlibLimiter.add((done) => {
				this._compress(data, fin, (err, result) => {
					done();
					callback(err, result);
				});
			});
		}
		/**
		* Decompress data.
		*
		* @param {Buffer} data Compressed data
		* @param {Boolean} fin Specifies whether or not this is the last fragment
		* @param {Function} callback Callback
		* @private
		*/
		_decompress(data, fin, callback) {
			const endpoint = this._isServer ? "client" : "server";
			if (!this._inflate) {
				const key = `${endpoint}_max_window_bits`;
				const windowBits = typeof this.params[key] !== "number" ? zlib.Z_DEFAULT_WINDOWBITS : this.params[key];
				this._inflate = zlib.createInflateRaw({
					...this._options.zlibInflateOptions,
					windowBits
				});
				this._inflate[kPerMessageDeflate] = this;
				this._inflate[kTotalLength] = 0;
				this._inflate[kBuffers] = [];
				this._inflate.on("error", inflateOnError);
				this._inflate.on("data", inflateOnData);
			}
			this._inflate[kCallback] = callback;
			this._inflate.write(data);
			if (fin) this._inflate.write(TRAILER);
			this._inflate.flush(() => {
				const err = this._inflate[kError];
				if (err) {
					this._inflate.close();
					this._inflate = null;
					callback(err);
					return;
				}
				const data = bufferUtil.concat(this._inflate[kBuffers], this._inflate[kTotalLength]);
				if (this._inflate._readableState.endEmitted) {
					this._inflate.close();
					this._inflate = null;
				} else {
					this._inflate[kTotalLength] = 0;
					this._inflate[kBuffers] = [];
					if (fin && this.params[`${endpoint}_no_context_takeover`]) this._inflate.reset();
				}
				callback(null, data);
			});
		}
		/**
		* Compress data.
		*
		* @param {(Buffer|String)} data Data to compress
		* @param {Boolean} fin Specifies whether or not this is the last fragment
		* @param {Function} callback Callback
		* @private
		*/
		_compress(data, fin, callback) {
			const endpoint = this._isServer ? "server" : "client";
			if (!this._deflate) {
				const key = `${endpoint}_max_window_bits`;
				const windowBits = typeof this.params[key] !== "number" ? zlib.Z_DEFAULT_WINDOWBITS : this.params[key];
				this._deflate = zlib.createDeflateRaw({
					...this._options.zlibDeflateOptions,
					windowBits
				});
				this._deflate[kTotalLength] = 0;
				this._deflate[kBuffers] = [];
				this._deflate.on("data", deflateOnData);
			}
			this._deflate[kCallback] = callback;
			this._deflate.write(data);
			this._deflate.flush(zlib.Z_SYNC_FLUSH, () => {
				if (!this._deflate) return;
				let data = bufferUtil.concat(this._deflate[kBuffers], this._deflate[kTotalLength]);
				if (fin) data = new FastBuffer(data.buffer, data.byteOffset, data.length - 4);
				this._deflate[kCallback] = null;
				this._deflate[kTotalLength] = 0;
				this._deflate[kBuffers] = [];
				if (fin && this.params[`${endpoint}_no_context_takeover`]) this._deflate.reset();
				callback(null, data);
			});
		}
	};
	module.exports = PerMessageDeflate;
	/**
	* The listener of the `zlib.DeflateRaw` stream `'data'` event.
	*
	* @param {Buffer} chunk A chunk of data
	* @private
	*/
	function deflateOnData(chunk) {
		this[kBuffers].push(chunk);
		this[kTotalLength] += chunk.length;
	}
	/**
	* The listener of the `zlib.InflateRaw` stream `'data'` event.
	*
	* @param {Buffer} chunk A chunk of data
	* @private
	*/
	function inflateOnData(chunk) {
		this[kTotalLength] += chunk.length;
		if (this[kPerMessageDeflate]._maxPayload < 1 || this[kTotalLength] <= this[kPerMessageDeflate]._maxPayload) {
			this[kBuffers].push(chunk);
			return;
		}
		this[kError] = /* @__PURE__ */ new RangeError("Max payload size exceeded");
		this[kError].code = "WS_ERR_UNSUPPORTED_MESSAGE_LENGTH";
		this[kError][kStatusCode] = 1009;
		this.removeListener("data", inflateOnData);
		this.reset();
	}
	/**
	* The listener of the `zlib.InflateRaw` stream `'error'` event.
	*
	* @param {Error} err The emitted error
	* @private
	*/
	function inflateOnError(err) {
		this[kPerMessageDeflate]._inflate = null;
		if (this[kError]) {
			this[kCallback](this[kError]);
			return;
		}
		err[kStatusCode] = 1007;
		this[kCallback](err);
	}
}));
var require_validation = /* @__PURE__ */ __commonJSMin(((exports, module) => {
	const { isUtf8 } = __require("buffer");
	const { hasBlob } = require_constants();
	const tokenChars = [
		0,
		0,
		0,
		0,
		0,
		0,
		0,
		0,
		0,
		0,
		0,
		0,
		0,
		0,
		0,
		0,
		0,
		0,
		0,
		0,
		0,
		0,
		0,
		0,
		0,
		0,
		0,
		0,
		0,
		0,
		0,
		0,
		0,
		1,
		0,
		1,
		1,
		1,
		1,
		1,
		0,
		0,
		1,
		1,
		0,
		1,
		1,
		0,
		1,
		1,
		1,
		1,
		1,
		1,
		1,
		1,
		1,
		1,
		0,
		0,
		0,
		0,
		0,
		0,
		0,
		1,
		1,
		1,
		1,
		1,
		1,
		1,
		1,
		1,
		1,
		1,
		1,
		1,
		1,
		1,
		1,
		1,
		1,
		1,
		1,
		1,
		1,
		1,
		1,
		1,
		1,
		0,
		0,
		0,
		1,
		1,
		1,
		1,
		1,
		1,
		1,
		1,
		1,
		1,
		1,
		1,
		1,
		1,
		1,
		1,
		1,
		1,
		1,
		1,
		1,
		1,
		1,
		1,
		1,
		1,
		1,
		1,
		1,
		0,
		1,
		0,
		1,
		0
	];
	/**
	* Checks if a status code is allowed in a close frame.
	*
	* @param {Number} code The status code
	* @return {Boolean} `true` if the status code is valid, else `false`
	* @public
	*/
	function isValidStatusCode(code) {
		return code >= 1e3 && code <= 1014 && code !== 1004 && code !== 1005 && code !== 1006 || code >= 3e3 && code <= 4999;
	}
	/**
	* Checks if a given buffer contains only correct UTF-8.
	* Ported from https://www.cl.cam.ac.uk/%7Emgk25/ucs/utf8_check.c by
	* Markus Kuhn.
	*
	* @param {Buffer} buf The buffer to check
	* @return {Boolean} `true` if `buf` contains only correct UTF-8, else `false`
	* @public
	*/
	function _isValidUTF8(buf) {
		const len = buf.length;
		let i = 0;
		while (i < len) if ((buf[i] & 128) === 0) i++;
		else if ((buf[i] & 224) === 192) {
			if (i + 1 === len || (buf[i + 1] & 192) !== 128 || (buf[i] & 254) === 192) return false;
			i += 2;
		} else if ((buf[i] & 240) === 224) {
			if (i + 2 >= len || (buf[i + 1] & 192) !== 128 || (buf[i + 2] & 192) !== 128 || buf[i] === 224 && (buf[i + 1] & 224) === 128 || buf[i] === 237 && (buf[i + 1] & 224) === 160) return false;
			i += 3;
		} else if ((buf[i] & 248) === 240) {
			if (i + 3 >= len || (buf[i + 1] & 192) !== 128 || (buf[i + 2] & 192) !== 128 || (buf[i + 3] & 192) !== 128 || buf[i] === 240 && (buf[i + 1] & 240) === 128 || buf[i] === 244 && buf[i + 1] > 143 || buf[i] > 244) return false;
			i += 4;
		} else return false;
		return true;
	}
	/**
	* Determines whether a value is a `Blob`.
	*
	* @param {*} value The value to be tested
	* @return {Boolean} `true` if `value` is a `Blob`, else `false`
	* @private
	*/
	function isBlob(value) {
		return hasBlob && typeof value === "object" && typeof value.arrayBuffer === "function" && typeof value.type === "string" && typeof value.stream === "function" && (value[Symbol.toStringTag] === "Blob" || value[Symbol.toStringTag] === "File");
	}
	module.exports = {
		isBlob,
		isValidStatusCode,
		isValidUTF8: _isValidUTF8,
		tokenChars
	};
	if (isUtf8) module.exports.isValidUTF8 = function(buf) {
		return buf.length < 24 ? _isValidUTF8(buf) : isUtf8(buf);
	};
	else if (!process.env.WS_NO_UTF_8_VALIDATE) try {
		const isValidUTF8 = __require("utf-8-validate");
		module.exports.isValidUTF8 = function(buf) {
			return buf.length < 32 ? _isValidUTF8(buf) : isValidUTF8(buf);
		};
	} catch (e) {}
}));
var require_receiver = /* @__PURE__ */ __commonJSMin(((exports, module) => {
	const { Writable } = __require("stream");
	const PerMessageDeflate = require_permessage_deflate();
	const { BINARY_TYPES, EMPTY_BUFFER, kStatusCode, kWebSocket } = require_constants();
	const { concat, toArrayBuffer, unmask } = require_buffer_util();
	const { isValidStatusCode, isValidUTF8 } = require_validation();
	const FastBuffer = Buffer[Symbol.species];
	const GET_INFO = 0;
	const GET_PAYLOAD_LENGTH_16 = 1;
	const GET_PAYLOAD_LENGTH_64 = 2;
	const GET_MASK = 3;
	const GET_DATA = 4;
	const INFLATING = 5;
	const DEFER_EVENT = 6;
	/**
	* HyBi Receiver implementation.
	*
	* @extends Writable
	*/
	var Receiver = class extends Writable {
		/**
		* Creates a Receiver instance.
		*
		* @param {Object} [options] Options object
		* @param {Boolean} [options.allowSynchronousEvents=true] Specifies whether
		*     any of the `'message'`, `'ping'`, and `'pong'` events can be emitted
		*     multiple times in the same tick
		* @param {String} [options.binaryType=nodebuffer] The type for binary data
		* @param {Object} [options.extensions] An object containing the negotiated
		*     extensions
		* @param {Boolean} [options.isServer=false] Specifies whether to operate in
		*     client or server mode
		* @param {Number} [options.maxPayload=0] The maximum allowed message length
		* @param {Boolean} [options.skipUTF8Validation=false] Specifies whether or
		*     not to skip UTF-8 validation for text and close messages
		*/
		constructor(options = {}) {
			super();
			this._allowSynchronousEvents = options.allowSynchronousEvents !== void 0 ? options.allowSynchronousEvents : true;
			this._binaryType = options.binaryType || BINARY_TYPES[0];
			this._extensions = options.extensions || {};
			this._isServer = !!options.isServer;
			this._maxPayload = options.maxPayload | 0;
			this._skipUTF8Validation = !!options.skipUTF8Validation;
			this[kWebSocket] = void 0;
			this._bufferedBytes = 0;
			this._buffers = [];
			this._compressed = false;
			this._payloadLength = 0;
			this._mask = void 0;
			this._fragmented = 0;
			this._masked = false;
			this._fin = false;
			this._opcode = 0;
			this._totalPayloadLength = 0;
			this._messageLength = 0;
			this._fragments = [];
			this._errored = false;
			this._loop = false;
			this._state = GET_INFO;
		}
		/**
		* Implements `Writable.prototype._write()`.
		*
		* @param {Buffer} chunk The chunk of data to write
		* @param {String} encoding The character encoding of `chunk`
		* @param {Function} cb Callback
		* @private
		*/
		_write(chunk, encoding, cb) {
			if (this._opcode === 8 && this._state == GET_INFO) return cb();
			this._bufferedBytes += chunk.length;
			this._buffers.push(chunk);
			this.startLoop(cb);
		}
		/**
		* Consumes `n` bytes from the buffered data.
		*
		* @param {Number} n The number of bytes to consume
		* @return {Buffer} The consumed bytes
		* @private
		*/
		consume(n) {
			this._bufferedBytes -= n;
			if (n === this._buffers[0].length) return this._buffers.shift();
			if (n < this._buffers[0].length) {
				const buf = this._buffers[0];
				this._buffers[0] = new FastBuffer(buf.buffer, buf.byteOffset + n, buf.length - n);
				return new FastBuffer(buf.buffer, buf.byteOffset, n);
			}
			const dst = Buffer.allocUnsafe(n);
			do {
				const buf = this._buffers[0];
				const offset = dst.length - n;
				if (n >= buf.length) dst.set(this._buffers.shift(), offset);
				else {
					dst.set(new Uint8Array(buf.buffer, buf.byteOffset, n), offset);
					this._buffers[0] = new FastBuffer(buf.buffer, buf.byteOffset + n, buf.length - n);
				}
				n -= buf.length;
			} while (n > 0);
			return dst;
		}
		/**
		* Starts the parsing loop.
		*
		* @param {Function} cb Callback
		* @private
		*/
		startLoop(cb) {
			this._loop = true;
			do
				switch (this._state) {
					case GET_INFO:
						this.getInfo(cb);
						break;
					case GET_PAYLOAD_LENGTH_16:
						this.getPayloadLength16(cb);
						break;
					case GET_PAYLOAD_LENGTH_64:
						this.getPayloadLength64(cb);
						break;
					case GET_MASK:
						this.getMask();
						break;
					case GET_DATA:
						this.getData(cb);
						break;
					case INFLATING:
					case DEFER_EVENT:
						this._loop = false;
						return;
				}
			while (this._loop);
			if (!this._errored) cb();
		}
		/**
		* Reads the first two bytes of a frame.
		*
		* @param {Function} cb Callback
		* @private
		*/
		getInfo(cb) {
			if (this._bufferedBytes < 2) {
				this._loop = false;
				return;
			}
			const buf = this.consume(2);
			if ((buf[0] & 48) !== 0) {
				cb(this.createError(RangeError, "RSV2 and RSV3 must be clear", true, 1002, "WS_ERR_UNEXPECTED_RSV_2_3"));
				return;
			}
			const compressed = (buf[0] & 64) === 64;
			if (compressed && !this._extensions[PerMessageDeflate.extensionName]) {
				cb(this.createError(RangeError, "RSV1 must be clear", true, 1002, "WS_ERR_UNEXPECTED_RSV_1"));
				return;
			}
			this._fin = (buf[0] & 128) === 128;
			this._opcode = buf[0] & 15;
			this._payloadLength = buf[1] & 127;
			if (this._opcode === 0) {
				if (compressed) {
					cb(this.createError(RangeError, "RSV1 must be clear", true, 1002, "WS_ERR_UNEXPECTED_RSV_1"));
					return;
				}
				if (!this._fragmented) {
					cb(this.createError(RangeError, "invalid opcode 0", true, 1002, "WS_ERR_INVALID_OPCODE"));
					return;
				}
				this._opcode = this._fragmented;
			} else if (this._opcode === 1 || this._opcode === 2) {
				if (this._fragmented) {
					cb(this.createError(RangeError, `invalid opcode ${this._opcode}`, true, 1002, "WS_ERR_INVALID_OPCODE"));
					return;
				}
				this._compressed = compressed;
			} else if (this._opcode > 7 && this._opcode < 11) {
				if (!this._fin) {
					cb(this.createError(RangeError, "FIN must be set", true, 1002, "WS_ERR_EXPECTED_FIN"));
					return;
				}
				if (compressed) {
					cb(this.createError(RangeError, "RSV1 must be clear", true, 1002, "WS_ERR_UNEXPECTED_RSV_1"));
					return;
				}
				if (this._payloadLength > 125 || this._opcode === 8 && this._payloadLength === 1) {
					cb(this.createError(RangeError, `invalid payload length ${this._payloadLength}`, true, 1002, "WS_ERR_INVALID_CONTROL_PAYLOAD_LENGTH"));
					return;
				}
			} else {
				cb(this.createError(RangeError, `invalid opcode ${this._opcode}`, true, 1002, "WS_ERR_INVALID_OPCODE"));
				return;
			}
			if (!this._fin && !this._fragmented) this._fragmented = this._opcode;
			this._masked = (buf[1] & 128) === 128;
			if (this._isServer) {
				if (!this._masked) {
					cb(this.createError(RangeError, "MASK must be set", true, 1002, "WS_ERR_EXPECTED_MASK"));
					return;
				}
			} else if (this._masked) {
				cb(this.createError(RangeError, "MASK must be clear", true, 1002, "WS_ERR_UNEXPECTED_MASK"));
				return;
			}
			if (this._payloadLength === 126) this._state = GET_PAYLOAD_LENGTH_16;
			else if (this._payloadLength === 127) this._state = GET_PAYLOAD_LENGTH_64;
			else this.haveLength(cb);
		}
		/**
		* Gets extended payload length (7+16).
		*
		* @param {Function} cb Callback
		* @private
		*/
		getPayloadLength16(cb) {
			if (this._bufferedBytes < 2) {
				this._loop = false;
				return;
			}
			this._payloadLength = this.consume(2).readUInt16BE(0);
			this.haveLength(cb);
		}
		/**
		* Gets extended payload length (7+64).
		*
		* @param {Function} cb Callback
		* @private
		*/
		getPayloadLength64(cb) {
			if (this._bufferedBytes < 8) {
				this._loop = false;
				return;
			}
			const buf = this.consume(8);
			const num = buf.readUInt32BE(0);
			if (num > Math.pow(2, 21) - 1) {
				cb(this.createError(RangeError, "Unsupported WebSocket frame: payload length > 2^53 - 1", false, 1009, "WS_ERR_UNSUPPORTED_DATA_PAYLOAD_LENGTH"));
				return;
			}
			this._payloadLength = num * Math.pow(2, 32) + buf.readUInt32BE(4);
			this.haveLength(cb);
		}
		/**
		* Payload length has been read.
		*
		* @param {Function} cb Callback
		* @private
		*/
		haveLength(cb) {
			if (this._payloadLength && this._opcode < 8) {
				this._totalPayloadLength += this._payloadLength;
				if (this._totalPayloadLength > this._maxPayload && this._maxPayload > 0) {
					cb(this.createError(RangeError, "Max payload size exceeded", false, 1009, "WS_ERR_UNSUPPORTED_MESSAGE_LENGTH"));
					return;
				}
			}
			if (this._masked) this._state = GET_MASK;
			else this._state = GET_DATA;
		}
		/**
		* Reads mask bytes.
		*
		* @private
		*/
		getMask() {
			if (this._bufferedBytes < 4) {
				this._loop = false;
				return;
			}
			this._mask = this.consume(4);
			this._state = GET_DATA;
		}
		/**
		* Reads data bytes.
		*
		* @param {Function} cb Callback
		* @private
		*/
		getData(cb) {
			let data = EMPTY_BUFFER;
			if (this._payloadLength) {
				if (this._bufferedBytes < this._payloadLength) {
					this._loop = false;
					return;
				}
				data = this.consume(this._payloadLength);
				if (this._masked && (this._mask[0] | this._mask[1] | this._mask[2] | this._mask[3]) !== 0) unmask(data, this._mask);
			}
			if (this._opcode > 7) {
				this.controlMessage(data, cb);
				return;
			}
			if (this._compressed) {
				this._state = INFLATING;
				this.decompress(data, cb);
				return;
			}
			if (data.length) {
				this._messageLength = this._totalPayloadLength;
				this._fragments.push(data);
			}
			this.dataMessage(cb);
		}
		/**
		* Decompresses data.
		*
		* @param {Buffer} data Compressed data
		* @param {Function} cb Callback
		* @private
		*/
		decompress(data, cb) {
			this._extensions[PerMessageDeflate.extensionName].decompress(data, this._fin, (err, buf) => {
				if (err) return cb(err);
				if (buf.length) {
					this._messageLength += buf.length;
					if (this._messageLength > this._maxPayload && this._maxPayload > 0) {
						cb(this.createError(RangeError, "Max payload size exceeded", false, 1009, "WS_ERR_UNSUPPORTED_MESSAGE_LENGTH"));
						return;
					}
					this._fragments.push(buf);
				}
				this.dataMessage(cb);
				if (this._state === GET_INFO) this.startLoop(cb);
			});
		}
		/**
		* Handles a data message.
		*
		* @param {Function} cb Callback
		* @private
		*/
		dataMessage(cb) {
			if (!this._fin) {
				this._state = GET_INFO;
				return;
			}
			const messageLength = this._messageLength;
			const fragments = this._fragments;
			this._totalPayloadLength = 0;
			this._messageLength = 0;
			this._fragmented = 0;
			this._fragments = [];
			if (this._opcode === 2) {
				let data;
				if (this._binaryType === "nodebuffer") data = concat(fragments, messageLength);
				else if (this._binaryType === "arraybuffer") data = toArrayBuffer(concat(fragments, messageLength));
				else if (this._binaryType === "blob") data = new Blob(fragments);
				else data = fragments;
				if (this._allowSynchronousEvents) {
					this.emit("message", data, true);
					this._state = GET_INFO;
				} else {
					this._state = DEFER_EVENT;
					setImmediate(() => {
						this.emit("message", data, true);
						this._state = GET_INFO;
						this.startLoop(cb);
					});
				}
			} else {
				const buf = concat(fragments, messageLength);
				if (!this._skipUTF8Validation && !isValidUTF8(buf)) {
					cb(this.createError(Error, "invalid UTF-8 sequence", true, 1007, "WS_ERR_INVALID_UTF8"));
					return;
				}
				if (this._state === INFLATING || this._allowSynchronousEvents) {
					this.emit("message", buf, false);
					this._state = GET_INFO;
				} else {
					this._state = DEFER_EVENT;
					setImmediate(() => {
						this.emit("message", buf, false);
						this._state = GET_INFO;
						this.startLoop(cb);
					});
				}
			}
		}
		/**
		* Handles a control message.
		*
		* @param {Buffer} data Data to handle
		* @return {(Error|RangeError|undefined)} A possible error
		* @private
		*/
		controlMessage(data, cb) {
			if (this._opcode === 8) {
				if (data.length === 0) {
					this._loop = false;
					this.emit("conclude", 1005, EMPTY_BUFFER);
					this.end();
				} else {
					const code = data.readUInt16BE(0);
					if (!isValidStatusCode(code)) {
						cb(this.createError(RangeError, `invalid status code ${code}`, true, 1002, "WS_ERR_INVALID_CLOSE_CODE"));
						return;
					}
					const buf = new FastBuffer(data.buffer, data.byteOffset + 2, data.length - 2);
					if (!this._skipUTF8Validation && !isValidUTF8(buf)) {
						cb(this.createError(Error, "invalid UTF-8 sequence", true, 1007, "WS_ERR_INVALID_UTF8"));
						return;
					}
					this._loop = false;
					this.emit("conclude", code, buf);
					this.end();
				}
				this._state = GET_INFO;
				return;
			}
			if (this._allowSynchronousEvents) {
				this.emit(this._opcode === 9 ? "ping" : "pong", data);
				this._state = GET_INFO;
			} else {
				this._state = DEFER_EVENT;
				setImmediate(() => {
					this.emit(this._opcode === 9 ? "ping" : "pong", data);
					this._state = GET_INFO;
					this.startLoop(cb);
				});
			}
		}
		/**
		* Builds an error object.
		*
		* @param {function(new:Error|RangeError)} ErrorCtor The error constructor
		* @param {String} message The error message
		* @param {Boolean} prefix Specifies whether or not to add a default prefix to
		*     `message`
		* @param {Number} statusCode The status code
		* @param {String} errorCode The exposed error code
		* @return {(Error|RangeError)} The error
		* @private
		*/
		createError(ErrorCtor, message, prefix, statusCode, errorCode) {
			this._loop = false;
			this._errored = true;
			const err = new ErrorCtor(prefix ? `Invalid WebSocket frame: ${message}` : message);
			Error.captureStackTrace(err, this.createError);
			err.code = errorCode;
			err[kStatusCode] = statusCode;
			return err;
		}
	};
	module.exports = Receiver;
}));
var require_sender = /* @__PURE__ */ __commonJSMin(((exports, module) => {
	const { Duplex: Duplex$3 } = __require("stream");
	const { randomFillSync } = __require("crypto");
	const PerMessageDeflate = require_permessage_deflate();
	const { EMPTY_BUFFER, kWebSocket, NOOP } = require_constants();
	const { isBlob, isValidStatusCode } = require_validation();
	const { mask: applyMask, toBuffer } = require_buffer_util();
	const kByteLength = Symbol("kByteLength");
	const maskBuffer = Buffer.alloc(4);
	const RANDOM_POOL_SIZE = 8 * 1024;
	let randomPool;
	let randomPoolPointer = RANDOM_POOL_SIZE;
	const DEFAULT = 0;
	const DEFLATING = 1;
	const GET_BLOB_DATA = 2;
	module.exports = class Sender {
		/**
		* Creates a Sender instance.
		*
		* @param {Duplex} socket The connection socket
		* @param {Object} [extensions] An object containing the negotiated extensions
		* @param {Function} [generateMask] The function used to generate the masking
		*     key
		*/
		constructor(socket, extensions, generateMask) {
			this._extensions = extensions || {};
			if (generateMask) {
				this._generateMask = generateMask;
				this._maskBuffer = Buffer.alloc(4);
			}
			this._socket = socket;
			this._firstFragment = true;
			this._compress = false;
			this._bufferedBytes = 0;
			this._queue = [];
			this._state = DEFAULT;
			this.onerror = NOOP;
			this[kWebSocket] = void 0;
		}
		/**
		* Frames a piece of data according to the HyBi WebSocket protocol.
		*
		* @param {(Buffer|String)} data The data to frame
		* @param {Object} options Options object
		* @param {Boolean} [options.fin=false] Specifies whether or not to set the
		*     FIN bit
		* @param {Function} [options.generateMask] The function used to generate the
		*     masking key
		* @param {Boolean} [options.mask=false] Specifies whether or not to mask
		*     `data`
		* @param {Buffer} [options.maskBuffer] The buffer used to store the masking
		*     key
		* @param {Number} options.opcode The opcode
		* @param {Boolean} [options.readOnly=false] Specifies whether `data` can be
		*     modified
		* @param {Boolean} [options.rsv1=false] Specifies whether or not to set the
		*     RSV1 bit
		* @return {(Buffer|String)[]} The framed data
		* @public
		*/
		static frame(data, options) {
			let mask;
			let merge = false;
			let offset = 2;
			let skipMasking = false;
			if (options.mask) {
				mask = options.maskBuffer || maskBuffer;
				if (options.generateMask) options.generateMask(mask);
				else {
					if (randomPoolPointer === RANDOM_POOL_SIZE) {
						/* istanbul ignore else  */
						if (randomPool === void 0) randomPool = Buffer.alloc(RANDOM_POOL_SIZE);
						randomFillSync(randomPool, 0, RANDOM_POOL_SIZE);
						randomPoolPointer = 0;
					}
					mask[0] = randomPool[randomPoolPointer++];
					mask[1] = randomPool[randomPoolPointer++];
					mask[2] = randomPool[randomPoolPointer++];
					mask[3] = randomPool[randomPoolPointer++];
				}
				skipMasking = (mask[0] | mask[1] | mask[2] | mask[3]) === 0;
				offset = 6;
			}
			let dataLength;
			if (typeof data === "string") if ((!options.mask || skipMasking) && options[kByteLength] !== void 0) dataLength = options[kByteLength];
			else {
				data = Buffer.from(data);
				dataLength = data.length;
			}
			else {
				dataLength = data.length;
				merge = options.mask && options.readOnly && !skipMasking;
			}
			let payloadLength = dataLength;
			if (dataLength >= 65536) {
				offset += 8;
				payloadLength = 127;
			} else if (dataLength > 125) {
				offset += 2;
				payloadLength = 126;
			}
			const target = Buffer.allocUnsafe(merge ? dataLength + offset : offset);
			target[0] = options.fin ? options.opcode | 128 : options.opcode;
			if (options.rsv1) target[0] |= 64;
			target[1] = payloadLength;
			if (payloadLength === 126) target.writeUInt16BE(dataLength, 2);
			else if (payloadLength === 127) {
				target[2] = target[3] = 0;
				target.writeUIntBE(dataLength, 4, 6);
			}
			if (!options.mask) return [target, data];
			target[1] |= 128;
			target[offset - 4] = mask[0];
			target[offset - 3] = mask[1];
			target[offset - 2] = mask[2];
			target[offset - 1] = mask[3];
			if (skipMasking) return [target, data];
			if (merge) {
				applyMask(data, mask, target, offset, dataLength);
				return [target];
			}
			applyMask(data, mask, data, 0, dataLength);
			return [target, data];
		}
		/**
		* Sends a close message to the other peer.
		*
		* @param {Number} [code] The status code component of the body
		* @param {(String|Buffer)} [data] The message component of the body
		* @param {Boolean} [mask=false] Specifies whether or not to mask the message
		* @param {Function} [cb] Callback
		* @public
		*/
		close(code, data, mask, cb) {
			let buf;
			if (code === void 0) buf = EMPTY_BUFFER;
			else if (typeof code !== "number" || !isValidStatusCode(code)) throw new TypeError("First argument must be a valid error code number");
			else if (data === void 0 || !data.length) {
				buf = Buffer.allocUnsafe(2);
				buf.writeUInt16BE(code, 0);
			} else {
				const length = Buffer.byteLength(data);
				if (length > 123) throw new RangeError("The message must not be greater than 123 bytes");
				buf = Buffer.allocUnsafe(2 + length);
				buf.writeUInt16BE(code, 0);
				if (typeof data === "string") buf.write(data, 2);
				else buf.set(data, 2);
			}
			const options = {
				[kByteLength]: buf.length,
				fin: true,
				generateMask: this._generateMask,
				mask,
				maskBuffer: this._maskBuffer,
				opcode: 8,
				readOnly: false,
				rsv1: false
			};
			if (this._state !== DEFAULT) this.enqueue([
				this.dispatch,
				buf,
				false,
				options,
				cb
			]);
			else this.sendFrame(Sender.frame(buf, options), cb);
		}
		/**
		* Sends a ping message to the other peer.
		*
		* @param {*} data The message to send
		* @param {Boolean} [mask=false] Specifies whether or not to mask `data`
		* @param {Function} [cb] Callback
		* @public
		*/
		ping(data, mask, cb) {
			let byteLength;
			let readOnly;
			if (typeof data === "string") {
				byteLength = Buffer.byteLength(data);
				readOnly = false;
			} else if (isBlob(data)) {
				byteLength = data.size;
				readOnly = false;
			} else {
				data = toBuffer(data);
				byteLength = data.length;
				readOnly = toBuffer.readOnly;
			}
			if (byteLength > 125) throw new RangeError("The data size must not be greater than 125 bytes");
			const options = {
				[kByteLength]: byteLength,
				fin: true,
				generateMask: this._generateMask,
				mask,
				maskBuffer: this._maskBuffer,
				opcode: 9,
				readOnly,
				rsv1: false
			};
			if (isBlob(data)) if (this._state !== DEFAULT) this.enqueue([
				this.getBlobData,
				data,
				false,
				options,
				cb
			]);
			else this.getBlobData(data, false, options, cb);
			else if (this._state !== DEFAULT) this.enqueue([
				this.dispatch,
				data,
				false,
				options,
				cb
			]);
			else this.sendFrame(Sender.frame(data, options), cb);
		}
		/**
		* Sends a pong message to the other peer.
		*
		* @param {*} data The message to send
		* @param {Boolean} [mask=false] Specifies whether or not to mask `data`
		* @param {Function} [cb] Callback
		* @public
		*/
		pong(data, mask, cb) {
			let byteLength;
			let readOnly;
			if (typeof data === "string") {
				byteLength = Buffer.byteLength(data);
				readOnly = false;
			} else if (isBlob(data)) {
				byteLength = data.size;
				readOnly = false;
			} else {
				data = toBuffer(data);
				byteLength = data.length;
				readOnly = toBuffer.readOnly;
			}
			if (byteLength > 125) throw new RangeError("The data size must not be greater than 125 bytes");
			const options = {
				[kByteLength]: byteLength,
				fin: true,
				generateMask: this._generateMask,
				mask,
				maskBuffer: this._maskBuffer,
				opcode: 10,
				readOnly,
				rsv1: false
			};
			if (isBlob(data)) if (this._state !== DEFAULT) this.enqueue([
				this.getBlobData,
				data,
				false,
				options,
				cb
			]);
			else this.getBlobData(data, false, options, cb);
			else if (this._state !== DEFAULT) this.enqueue([
				this.dispatch,
				data,
				false,
				options,
				cb
			]);
			else this.sendFrame(Sender.frame(data, options), cb);
		}
		/**
		* Sends a data message to the other peer.
		*
		* @param {*} data The message to send
		* @param {Object} options Options object
		* @param {Boolean} [options.binary=false] Specifies whether `data` is binary
		*     or text
		* @param {Boolean} [options.compress=false] Specifies whether or not to
		*     compress `data`
		* @param {Boolean} [options.fin=false] Specifies whether the fragment is the
		*     last one
		* @param {Boolean} [options.mask=false] Specifies whether or not to mask
		*     `data`
		* @param {Function} [cb] Callback
		* @public
		*/
		send(data, options, cb) {
			const perMessageDeflate = this._extensions[PerMessageDeflate.extensionName];
			let opcode = options.binary ? 2 : 1;
			let rsv1 = options.compress;
			let byteLength;
			let readOnly;
			if (typeof data === "string") {
				byteLength = Buffer.byteLength(data);
				readOnly = false;
			} else if (isBlob(data)) {
				byteLength = data.size;
				readOnly = false;
			} else {
				data = toBuffer(data);
				byteLength = data.length;
				readOnly = toBuffer.readOnly;
			}
			if (this._firstFragment) {
				this._firstFragment = false;
				if (rsv1 && perMessageDeflate && perMessageDeflate.params[perMessageDeflate._isServer ? "server_no_context_takeover" : "client_no_context_takeover"]) rsv1 = byteLength >= perMessageDeflate._threshold;
				this._compress = rsv1;
			} else {
				rsv1 = false;
				opcode = 0;
			}
			if (options.fin) this._firstFragment = true;
			const opts = {
				[kByteLength]: byteLength,
				fin: options.fin,
				generateMask: this._generateMask,
				mask: options.mask,
				maskBuffer: this._maskBuffer,
				opcode,
				readOnly,
				rsv1
			};
			if (isBlob(data)) if (this._state !== DEFAULT) this.enqueue([
				this.getBlobData,
				data,
				this._compress,
				opts,
				cb
			]);
			else this.getBlobData(data, this._compress, opts, cb);
			else if (this._state !== DEFAULT) this.enqueue([
				this.dispatch,
				data,
				this._compress,
				opts,
				cb
			]);
			else this.dispatch(data, this._compress, opts, cb);
		}
		/**
		* Gets the contents of a blob as binary data.
		*
		* @param {Blob} blob The blob
		* @param {Boolean} [compress=false] Specifies whether or not to compress
		*     the data
		* @param {Object} options Options object
		* @param {Boolean} [options.fin=false] Specifies whether or not to set the
		*     FIN bit
		* @param {Function} [options.generateMask] The function used to generate the
		*     masking key
		* @param {Boolean} [options.mask=false] Specifies whether or not to mask
		*     `data`
		* @param {Buffer} [options.maskBuffer] The buffer used to store the masking
		*     key
		* @param {Number} options.opcode The opcode
		* @param {Boolean} [options.readOnly=false] Specifies whether `data` can be
		*     modified
		* @param {Boolean} [options.rsv1=false] Specifies whether or not to set the
		*     RSV1 bit
		* @param {Function} [cb] Callback
		* @private
		*/
		getBlobData(blob, compress, options, cb) {
			this._bufferedBytes += options[kByteLength];
			this._state = GET_BLOB_DATA;
			blob.arrayBuffer().then((arrayBuffer) => {
				if (this._socket.destroyed) {
					const err = /* @__PURE__ */ new Error("The socket was closed while the blob was being read");
					process.nextTick(callCallbacks, this, err, cb);
					return;
				}
				this._bufferedBytes -= options[kByteLength];
				const data = toBuffer(arrayBuffer);
				if (!compress) {
					this._state = DEFAULT;
					this.sendFrame(Sender.frame(data, options), cb);
					this.dequeue();
				} else this.dispatch(data, compress, options, cb);
			}).catch((err) => {
				process.nextTick(onError, this, err, cb);
			});
		}
		/**
		* Dispatches a message.
		*
		* @param {(Buffer|String)} data The message to send
		* @param {Boolean} [compress=false] Specifies whether or not to compress
		*     `data`
		* @param {Object} options Options object
		* @param {Boolean} [options.fin=false] Specifies whether or not to set the
		*     FIN bit
		* @param {Function} [options.generateMask] The function used to generate the
		*     masking key
		* @param {Boolean} [options.mask=false] Specifies whether or not to mask
		*     `data`
		* @param {Buffer} [options.maskBuffer] The buffer used to store the masking
		*     key
		* @param {Number} options.opcode The opcode
		* @param {Boolean} [options.readOnly=false] Specifies whether `data` can be
		*     modified
		* @param {Boolean} [options.rsv1=false] Specifies whether or not to set the
		*     RSV1 bit
		* @param {Function} [cb] Callback
		* @private
		*/
		dispatch(data, compress, options, cb) {
			if (!compress) {
				this.sendFrame(Sender.frame(data, options), cb);
				return;
			}
			const perMessageDeflate = this._extensions[PerMessageDeflate.extensionName];
			this._bufferedBytes += options[kByteLength];
			this._state = DEFLATING;
			perMessageDeflate.compress(data, options.fin, (_, buf) => {
				if (this._socket.destroyed) {
					callCallbacks(this, /* @__PURE__ */ new Error("The socket was closed while data was being compressed"), cb);
					return;
				}
				this._bufferedBytes -= options[kByteLength];
				this._state = DEFAULT;
				options.readOnly = false;
				this.sendFrame(Sender.frame(buf, options), cb);
				this.dequeue();
			});
		}
		/**
		* Executes queued send operations.
		*
		* @private
		*/
		dequeue() {
			while (this._state === DEFAULT && this._queue.length) {
				const params = this._queue.shift();
				this._bufferedBytes -= params[3][kByteLength];
				Reflect.apply(params[0], this, params.slice(1));
			}
		}
		/**
		* Enqueues a send operation.
		*
		* @param {Array} params Send operation parameters.
		* @private
		*/
		enqueue(params) {
			this._bufferedBytes += params[3][kByteLength];
			this._queue.push(params);
		}
		/**
		* Sends a frame.
		*
		* @param {(Buffer | String)[]} list The frame to send
		* @param {Function} [cb] Callback
		* @private
		*/
		sendFrame(list, cb) {
			if (list.length === 2) {
				this._socket.cork();
				this._socket.write(list[0]);
				this._socket.write(list[1], cb);
				this._socket.uncork();
			} else this._socket.write(list[0], cb);
		}
	};
	/**
	* Calls queued callbacks with an error.
	*
	* @param {Sender} sender The `Sender` instance
	* @param {Error} err The error to call the callbacks with
	* @param {Function} [cb] The first callback
	* @private
	*/
	function callCallbacks(sender, err, cb) {
		if (typeof cb === "function") cb(err);
		for (let i = 0; i < sender._queue.length; i++) {
			const params = sender._queue[i];
			const callback = params[params.length - 1];
			if (typeof callback === "function") callback(err);
		}
	}
	/**
	* Handles a `Sender` error.
	*
	* @param {Sender} sender The `Sender` instance
	* @param {Error} err The error
	* @param {Function} [cb] The first pending callback
	* @private
	*/
	function onError(sender, err, cb) {
		callCallbacks(sender, err, cb);
		sender.onerror(err);
	}
}));
var require_event_target = /* @__PURE__ */ __commonJSMin(((exports, module) => {
	const { kForOnEventAttribute, kListener } = require_constants();
	const kCode = Symbol("kCode");
	const kData = Symbol("kData");
	const kError = Symbol("kError");
	const kMessage = Symbol("kMessage");
	const kReason = Symbol("kReason");
	const kTarget = Symbol("kTarget");
	const kType = Symbol("kType");
	const kWasClean = Symbol("kWasClean");
	/**
	* Class representing an event.
	*/
	var Event = class {
		/**
		* Create a new `Event`.
		*
		* @param {String} type The name of the event
		* @throws {TypeError} If the `type` argument is not specified
		*/
		constructor(type) {
			this[kTarget] = null;
			this[kType] = type;
		}
		/**
		* @type {*}
		*/
		get target() {
			return this[kTarget];
		}
		/**
		* @type {String}
		*/
		get type() {
			return this[kType];
		}
	};
	Object.defineProperty(Event.prototype, "target", { enumerable: true });
	Object.defineProperty(Event.prototype, "type", { enumerable: true });
	/**
	* Class representing a close event.
	*
	* @extends Event
	*/
	var CloseEvent = class extends Event {
		/**
		* Create a new `CloseEvent`.
		*
		* @param {String} type The name of the event
		* @param {Object} [options] A dictionary object that allows for setting
		*     attributes via object members of the same name
		* @param {Number} [options.code=0] The status code explaining why the
		*     connection was closed
		* @param {String} [options.reason=''] A human-readable string explaining why
		*     the connection was closed
		* @param {Boolean} [options.wasClean=false] Indicates whether or not the
		*     connection was cleanly closed
		*/
		constructor(type, options = {}) {
			super(type);
			this[kCode] = options.code === void 0 ? 0 : options.code;
			this[kReason] = options.reason === void 0 ? "" : options.reason;
			this[kWasClean] = options.wasClean === void 0 ? false : options.wasClean;
		}
		/**
		* @type {Number}
		*/
		get code() {
			return this[kCode];
		}
		/**
		* @type {String}
		*/
		get reason() {
			return this[kReason];
		}
		/**
		* @type {Boolean}
		*/
		get wasClean() {
			return this[kWasClean];
		}
	};
	Object.defineProperty(CloseEvent.prototype, "code", { enumerable: true });
	Object.defineProperty(CloseEvent.prototype, "reason", { enumerable: true });
	Object.defineProperty(CloseEvent.prototype, "wasClean", { enumerable: true });
	/**
	* Class representing an error event.
	*
	* @extends Event
	*/
	var ErrorEvent = class extends Event {
		/**
		* Create a new `ErrorEvent`.
		*
		* @param {String} type The name of the event
		* @param {Object} [options] A dictionary object that allows for setting
		*     attributes via object members of the same name
		* @param {*} [options.error=null] The error that generated this event
		* @param {String} [options.message=''] The error message
		*/
		constructor(type, options = {}) {
			super(type);
			this[kError] = options.error === void 0 ? null : options.error;
			this[kMessage] = options.message === void 0 ? "" : options.message;
		}
		/**
		* @type {*}
		*/
		get error() {
			return this[kError];
		}
		/**
		* @type {String}
		*/
		get message() {
			return this[kMessage];
		}
	};
	Object.defineProperty(ErrorEvent.prototype, "error", { enumerable: true });
	Object.defineProperty(ErrorEvent.prototype, "message", { enumerable: true });
	/**
	* Class representing a message event.
	*
	* @extends Event
	*/
	var MessageEvent = class extends Event {
		/**
		* Create a new `MessageEvent`.
		*
		* @param {String} type The name of the event
		* @param {Object} [options] A dictionary object that allows for setting
		*     attributes via object members of the same name
		* @param {*} [options.data=null] The message content
		*/
		constructor(type, options = {}) {
			super(type);
			this[kData] = options.data === void 0 ? null : options.data;
		}
		/**
		* @type {*}
		*/
		get data() {
			return this[kData];
		}
	};
	Object.defineProperty(MessageEvent.prototype, "data", { enumerable: true });
	module.exports = {
		CloseEvent,
		ErrorEvent,
		Event,
		EventTarget: {
			addEventListener(type, handler, options = {}) {
				for (const listener of this.listeners(type)) if (!options[kForOnEventAttribute] && listener[kListener] === handler && !listener[kForOnEventAttribute]) return;
				let wrapper;
				if (type === "message") wrapper = function onMessage(data, isBinary) {
					const event = new MessageEvent("message", { data: isBinary ? data : data.toString() });
					event[kTarget] = this;
					callListener(handler, this, event);
				};
				else if (type === "close") wrapper = function onClose(code, message) {
					const event = new CloseEvent("close", {
						code,
						reason: message.toString(),
						wasClean: this._closeFrameReceived && this._closeFrameSent
					});
					event[kTarget] = this;
					callListener(handler, this, event);
				};
				else if (type === "error") wrapper = function onError(error) {
					const event = new ErrorEvent("error", {
						error,
						message: error.message
					});
					event[kTarget] = this;
					callListener(handler, this, event);
				};
				else if (type === "open") wrapper = function onOpen() {
					const event = new Event("open");
					event[kTarget] = this;
					callListener(handler, this, event);
				};
				else return;
				wrapper[kForOnEventAttribute] = !!options[kForOnEventAttribute];
				wrapper[kListener] = handler;
				if (options.once) this.once(type, wrapper);
				else this.on(type, wrapper);
			},
			removeEventListener(type, handler) {
				for (const listener of this.listeners(type)) if (listener[kListener] === handler && !listener[kForOnEventAttribute]) {
					this.removeListener(type, listener);
					break;
				}
			}
		},
		MessageEvent
	};
	/**
	* Call an event listener
	*
	* @param {(Function|Object)} listener The listener to call
	* @param {*} thisArg The value to use as `this`` when calling the listener
	* @param {Event} event The event to pass to the listener
	* @private
	*/
	function callListener(listener, thisArg, event) {
		if (typeof listener === "object" && listener.handleEvent) listener.handleEvent.call(listener, event);
		else listener.call(thisArg, event);
	}
}));
var require_extension = /* @__PURE__ */ __commonJSMin(((exports, module) => {
	const { tokenChars } = require_validation();
	/**
	* Adds an offer to the map of extension offers or a parameter to the map of
	* parameters.
	*
	* @param {Object} dest The map of extension offers or parameters
	* @param {String} name The extension or parameter name
	* @param {(Object|Boolean|String)} elem The extension parameters or the
	*     parameter value
	* @private
	*/
	function push(dest, name, elem) {
		if (dest[name] === void 0) dest[name] = [elem];
		else dest[name].push(elem);
	}
	/**
	* Parses the `Sec-WebSocket-Extensions` header into an object.
	*
	* @param {String} header The field value of the header
	* @return {Object} The parsed object
	* @public
	*/
	function parse(header) {
		const offers = Object.create(null);
		let params = Object.create(null);
		let mustUnescape = false;
		let isEscaping = false;
		let inQuotes = false;
		let extensionName;
		let paramName;
		let start = -1;
		let code = -1;
		let end = -1;
		let i = 0;
		for (; i < header.length; i++) {
			code = header.charCodeAt(i);
			if (extensionName === void 0) if (end === -1 && tokenChars[code] === 1) {
				if (start === -1) start = i;
			} else if (i !== 0 && (code === 32 || code === 9)) {
				if (end === -1 && start !== -1) end = i;
			} else if (code === 59 || code === 44) {
				if (start === -1) throw new SyntaxError(`Unexpected character at index ${i}`);
				if (end === -1) end = i;
				const name = header.slice(start, end);
				if (code === 44) {
					push(offers, name, params);
					params = Object.create(null);
				} else extensionName = name;
				start = end = -1;
			} else throw new SyntaxError(`Unexpected character at index ${i}`);
			else if (paramName === void 0) if (end === -1 && tokenChars[code] === 1) {
				if (start === -1) start = i;
			} else if (code === 32 || code === 9) {
				if (end === -1 && start !== -1) end = i;
			} else if (code === 59 || code === 44) {
				if (start === -1) throw new SyntaxError(`Unexpected character at index ${i}`);
				if (end === -1) end = i;
				push(params, header.slice(start, end), true);
				if (code === 44) {
					push(offers, extensionName, params);
					params = Object.create(null);
					extensionName = void 0;
				}
				start = end = -1;
			} else if (code === 61 && start !== -1 && end === -1) {
				paramName = header.slice(start, i);
				start = end = -1;
			} else throw new SyntaxError(`Unexpected character at index ${i}`);
			else if (isEscaping) {
				if (tokenChars[code] !== 1) throw new SyntaxError(`Unexpected character at index ${i}`);
				if (start === -1) start = i;
				else if (!mustUnescape) mustUnescape = true;
				isEscaping = false;
			} else if (inQuotes) if (tokenChars[code] === 1) {
				if (start === -1) start = i;
			} else if (code === 34 && start !== -1) {
				inQuotes = false;
				end = i;
			} else if (code === 92) isEscaping = true;
			else throw new SyntaxError(`Unexpected character at index ${i}`);
			else if (code === 34 && header.charCodeAt(i - 1) === 61) inQuotes = true;
			else if (end === -1 && tokenChars[code] === 1) {
				if (start === -1) start = i;
			} else if (start !== -1 && (code === 32 || code === 9)) {
				if (end === -1) end = i;
			} else if (code === 59 || code === 44) {
				if (start === -1) throw new SyntaxError(`Unexpected character at index ${i}`);
				if (end === -1) end = i;
				let value = header.slice(start, end);
				if (mustUnescape) {
					value = value.replace(/\\/g, "");
					mustUnescape = false;
				}
				push(params, paramName, value);
				if (code === 44) {
					push(offers, extensionName, params);
					params = Object.create(null);
					extensionName = void 0;
				}
				paramName = void 0;
				start = end = -1;
			} else throw new SyntaxError(`Unexpected character at index ${i}`);
		}
		if (start === -1 || inQuotes || code === 32 || code === 9) throw new SyntaxError("Unexpected end of input");
		if (end === -1) end = i;
		const token = header.slice(start, end);
		if (extensionName === void 0) push(offers, token, params);
		else {
			if (paramName === void 0) push(params, token, true);
			else if (mustUnescape) push(params, paramName, token.replace(/\\/g, ""));
			else push(params, paramName, token);
			push(offers, extensionName, params);
		}
		return offers;
	}
	/**
	* Builds the `Sec-WebSocket-Extensions` header field value.
	*
	* @param {Object} extensions The map of extensions and parameters to format
	* @return {String} A string representing the given object
	* @public
	*/
	function format(extensions) {
		return Object.keys(extensions).map((extension) => {
			let configurations = extensions[extension];
			if (!Array.isArray(configurations)) configurations = [configurations];
			return configurations.map((params) => {
				return [extension].concat(Object.keys(params).map((k) => {
					let values = params[k];
					if (!Array.isArray(values)) values = [values];
					return values.map((v) => v === true ? k : `${k}=${v}`).join("; ");
				})).join("; ");
			}).join(", ");
		}).join(", ");
	}
	module.exports = {
		format,
		parse
	};
}));
var require_websocket = /* @__PURE__ */ __commonJSMin(((exports, module) => {
	const EventEmitter$1 = __require("events");
	const https = __require("https");
	const http$1 = __require("http");
	const net = __require("net");
	const tls = __require("tls");
	const { randomBytes, createHash: createHash$1 } = __require("crypto");
	const { Duplex: Duplex$2, Readable } = __require("stream");
	const { URL } = __require("url");
	const PerMessageDeflate = require_permessage_deflate();
	const Receiver = require_receiver();
	const Sender = require_sender();
	const { isBlob } = require_validation();
	const { BINARY_TYPES, CLOSE_TIMEOUT, EMPTY_BUFFER, GUID, kForOnEventAttribute, kListener, kStatusCode, kWebSocket, NOOP } = require_constants();
	const { EventTarget: { addEventListener, removeEventListener } } = require_event_target();
	const { format, parse } = require_extension();
	const { toBuffer } = require_buffer_util();
	const kAborted = Symbol("kAborted");
	const protocolVersions = [8, 13];
	const readyStates = [
		"CONNECTING",
		"OPEN",
		"CLOSING",
		"CLOSED"
	];
	const subprotocolRegex = /^[!#$%&'*+\-.0-9A-Z^_`|a-z~]+$/;
	/**
	* Class representing a WebSocket.
	*
	* @extends EventEmitter
	*/
	var WebSocket = class WebSocket extends EventEmitter$1 {
		/**
		* Create a new `WebSocket`.
		*
		* @param {(String|URL)} address The URL to which to connect
		* @param {(String|String[])} [protocols] The subprotocols
		* @param {Object} [options] Connection options
		*/
		constructor(address, protocols, options) {
			super();
			this._binaryType = BINARY_TYPES[0];
			this._closeCode = 1006;
			this._closeFrameReceived = false;
			this._closeFrameSent = false;
			this._closeMessage = EMPTY_BUFFER;
			this._closeTimer = null;
			this._errorEmitted = false;
			this._extensions = {};
			this._paused = false;
			this._protocol = "";
			this._readyState = WebSocket.CONNECTING;
			this._receiver = null;
			this._sender = null;
			this._socket = null;
			if (address !== null) {
				this._bufferedAmount = 0;
				this._isServer = false;
				this._redirects = 0;
				if (protocols === void 0) protocols = [];
				else if (!Array.isArray(protocols)) if (typeof protocols === "object" && protocols !== null) {
					options = protocols;
					protocols = [];
				} else protocols = [protocols];
				initAsClient(this, address, protocols, options);
			} else {
				this._autoPong = options.autoPong;
				this._closeTimeout = options.closeTimeout;
				this._isServer = true;
			}
		}
		/**
		* For historical reasons, the custom "nodebuffer" type is used by the default
		* instead of "blob".
		*
		* @type {String}
		*/
		get binaryType() {
			return this._binaryType;
		}
		set binaryType(type) {
			if (!BINARY_TYPES.includes(type)) return;
			this._binaryType = type;
			if (this._receiver) this._receiver._binaryType = type;
		}
		/**
		* @type {Number}
		*/
		get bufferedAmount() {
			if (!this._socket) return this._bufferedAmount;
			return this._socket._writableState.length + this._sender._bufferedBytes;
		}
		/**
		* @type {String}
		*/
		get extensions() {
			return Object.keys(this._extensions).join();
		}
		/**
		* @type {Boolean}
		*/
		get isPaused() {
			return this._paused;
		}
		/**
		* @type {Function}
		*/
		/* istanbul ignore next */
		get onclose() {
			return null;
		}
		/**
		* @type {Function}
		*/
		/* istanbul ignore next */
		get onerror() {
			return null;
		}
		/**
		* @type {Function}
		*/
		/* istanbul ignore next */
		get onopen() {
			return null;
		}
		/**
		* @type {Function}
		*/
		/* istanbul ignore next */
		get onmessage() {
			return null;
		}
		/**
		* @type {String}
		*/
		get protocol() {
			return this._protocol;
		}
		/**
		* @type {Number}
		*/
		get readyState() {
			return this._readyState;
		}
		/**
		* @type {String}
		*/
		get url() {
			return this._url;
		}
		/**
		* Set up the socket and the internal resources.
		*
		* @param {Duplex} socket The network socket between the server and client
		* @param {Buffer} head The first packet of the upgraded stream
		* @param {Object} options Options object
		* @param {Boolean} [options.allowSynchronousEvents=false] Specifies whether
		*     any of the `'message'`, `'ping'`, and `'pong'` events can be emitted
		*     multiple times in the same tick
		* @param {Function} [options.generateMask] The function used to generate the
		*     masking key
		* @param {Number} [options.maxPayload=0] The maximum allowed message size
		* @param {Boolean} [options.skipUTF8Validation=false] Specifies whether or
		*     not to skip UTF-8 validation for text and close messages
		* @private
		*/
		setSocket(socket, head, options) {
			const receiver = new Receiver({
				allowSynchronousEvents: options.allowSynchronousEvents,
				binaryType: this.binaryType,
				extensions: this._extensions,
				isServer: this._isServer,
				maxPayload: options.maxPayload,
				skipUTF8Validation: options.skipUTF8Validation
			});
			const sender = new Sender(socket, this._extensions, options.generateMask);
			this._receiver = receiver;
			this._sender = sender;
			this._socket = socket;
			receiver[kWebSocket] = this;
			sender[kWebSocket] = this;
			socket[kWebSocket] = this;
			receiver.on("conclude", receiverOnConclude);
			receiver.on("drain", receiverOnDrain);
			receiver.on("error", receiverOnError);
			receiver.on("message", receiverOnMessage);
			receiver.on("ping", receiverOnPing);
			receiver.on("pong", receiverOnPong);
			sender.onerror = senderOnError;
			if (socket.setTimeout) socket.setTimeout(0);
			if (socket.setNoDelay) socket.setNoDelay();
			if (head.length > 0) socket.unshift(head);
			socket.on("close", socketOnClose);
			socket.on("data", socketOnData);
			socket.on("end", socketOnEnd);
			socket.on("error", socketOnError);
			this._readyState = WebSocket.OPEN;
			this.emit("open");
		}
		/**
		* Emit the `'close'` event.
		*
		* @private
		*/
		emitClose() {
			if (!this._socket) {
				this._readyState = WebSocket.CLOSED;
				this.emit("close", this._closeCode, this._closeMessage);
				return;
			}
			if (this._extensions[PerMessageDeflate.extensionName]) this._extensions[PerMessageDeflate.extensionName].cleanup();
			this._receiver.removeAllListeners();
			this._readyState = WebSocket.CLOSED;
			this.emit("close", this._closeCode, this._closeMessage);
		}
		/**
		* Start a closing handshake.
		*
		*          +----------+   +-----------+   +----------+
		*     - - -|ws.close()|-->|close frame|-->|ws.close()|- - -
		*    |     +----------+   +-----------+   +----------+     |
		*          +----------+   +-----------+         |
		* CLOSING  |ws.close()|<--|close frame|<--+-----+       CLOSING
		*          +----------+   +-----------+   |
		*    |           |                        |   +---+        |
		*                +------------------------+-->|fin| - - - -
		*    |         +---+                      |   +---+
		*     - - - - -|fin|<---------------------+
		*              +---+
		*
		* @param {Number} [code] Status code explaining why the connection is closing
		* @param {(String|Buffer)} [data] The reason why the connection is
		*     closing
		* @public
		*/
		close(code, data) {
			if (this.readyState === WebSocket.CLOSED) return;
			if (this.readyState === WebSocket.CONNECTING) {
				abortHandshake(this, this._req, "WebSocket was closed before the connection was established");
				return;
			}
			if (this.readyState === WebSocket.CLOSING) {
				if (this._closeFrameSent && (this._closeFrameReceived || this._receiver._writableState.errorEmitted)) this._socket.end();
				return;
			}
			this._readyState = WebSocket.CLOSING;
			this._sender.close(code, data, !this._isServer, (err) => {
				if (err) return;
				this._closeFrameSent = true;
				if (this._closeFrameReceived || this._receiver._writableState.errorEmitted) this._socket.end();
			});
			setCloseTimer(this);
		}
		/**
		* Pause the socket.
		*
		* @public
		*/
		pause() {
			if (this.readyState === WebSocket.CONNECTING || this.readyState === WebSocket.CLOSED) return;
			this._paused = true;
			this._socket.pause();
		}
		/**
		* Send a ping.
		*
		* @param {*} [data] The data to send
		* @param {Boolean} [mask] Indicates whether or not to mask `data`
		* @param {Function} [cb] Callback which is executed when the ping is sent
		* @public
		*/
		ping(data, mask, cb) {
			if (this.readyState === WebSocket.CONNECTING) throw new Error("WebSocket is not open: readyState 0 (CONNECTING)");
			if (typeof data === "function") {
				cb = data;
				data = mask = void 0;
			} else if (typeof mask === "function") {
				cb = mask;
				mask = void 0;
			}
			if (typeof data === "number") data = data.toString();
			if (this.readyState !== WebSocket.OPEN) {
				sendAfterClose(this, data, cb);
				return;
			}
			if (mask === void 0) mask = !this._isServer;
			this._sender.ping(data || EMPTY_BUFFER, mask, cb);
		}
		/**
		* Send a pong.
		*
		* @param {*} [data] The data to send
		* @param {Boolean} [mask] Indicates whether or not to mask `data`
		* @param {Function} [cb] Callback which is executed when the pong is sent
		* @public
		*/
		pong(data, mask, cb) {
			if (this.readyState === WebSocket.CONNECTING) throw new Error("WebSocket is not open: readyState 0 (CONNECTING)");
			if (typeof data === "function") {
				cb = data;
				data = mask = void 0;
			} else if (typeof mask === "function") {
				cb = mask;
				mask = void 0;
			}
			if (typeof data === "number") data = data.toString();
			if (this.readyState !== WebSocket.OPEN) {
				sendAfterClose(this, data, cb);
				return;
			}
			if (mask === void 0) mask = !this._isServer;
			this._sender.pong(data || EMPTY_BUFFER, mask, cb);
		}
		/**
		* Resume the socket.
		*
		* @public
		*/
		resume() {
			if (this.readyState === WebSocket.CONNECTING || this.readyState === WebSocket.CLOSED) return;
			this._paused = false;
			if (!this._receiver._writableState.needDrain) this._socket.resume();
		}
		/**
		* Send a data message.
		*
		* @param {*} data The message to send
		* @param {Object} [options] Options object
		* @param {Boolean} [options.binary] Specifies whether `data` is binary or
		*     text
		* @param {Boolean} [options.compress] Specifies whether or not to compress
		*     `data`
		* @param {Boolean} [options.fin=true] Specifies whether the fragment is the
		*     last one
		* @param {Boolean} [options.mask] Specifies whether or not to mask `data`
		* @param {Function} [cb] Callback which is executed when data is written out
		* @public
		*/
		send(data, options, cb) {
			if (this.readyState === WebSocket.CONNECTING) throw new Error("WebSocket is not open: readyState 0 (CONNECTING)");
			if (typeof options === "function") {
				cb = options;
				options = {};
			}
			if (typeof data === "number") data = data.toString();
			if (this.readyState !== WebSocket.OPEN) {
				sendAfterClose(this, data, cb);
				return;
			}
			const opts = {
				binary: typeof data !== "string",
				mask: !this._isServer,
				compress: true,
				fin: true,
				...options
			};
			if (!this._extensions[PerMessageDeflate.extensionName]) opts.compress = false;
			this._sender.send(data || EMPTY_BUFFER, opts, cb);
		}
		/**
		* Forcibly close the connection.
		*
		* @public
		*/
		terminate() {
			if (this.readyState === WebSocket.CLOSED) return;
			if (this.readyState === WebSocket.CONNECTING) {
				abortHandshake(this, this._req, "WebSocket was closed before the connection was established");
				return;
			}
			if (this._socket) {
				this._readyState = WebSocket.CLOSING;
				this._socket.destroy();
			}
		}
	};
	/**
	* @constant {Number} CONNECTING
	* @memberof WebSocket
	*/
	Object.defineProperty(WebSocket, "CONNECTING", {
		enumerable: true,
		value: readyStates.indexOf("CONNECTING")
	});
	/**
	* @constant {Number} CONNECTING
	* @memberof WebSocket.prototype
	*/
	Object.defineProperty(WebSocket.prototype, "CONNECTING", {
		enumerable: true,
		value: readyStates.indexOf("CONNECTING")
	});
	/**
	* @constant {Number} OPEN
	* @memberof WebSocket
	*/
	Object.defineProperty(WebSocket, "OPEN", {
		enumerable: true,
		value: readyStates.indexOf("OPEN")
	});
	/**
	* @constant {Number} OPEN
	* @memberof WebSocket.prototype
	*/
	Object.defineProperty(WebSocket.prototype, "OPEN", {
		enumerable: true,
		value: readyStates.indexOf("OPEN")
	});
	/**
	* @constant {Number} CLOSING
	* @memberof WebSocket
	*/
	Object.defineProperty(WebSocket, "CLOSING", {
		enumerable: true,
		value: readyStates.indexOf("CLOSING")
	});
	/**
	* @constant {Number} CLOSING
	* @memberof WebSocket.prototype
	*/
	Object.defineProperty(WebSocket.prototype, "CLOSING", {
		enumerable: true,
		value: readyStates.indexOf("CLOSING")
	});
	/**
	* @constant {Number} CLOSED
	* @memberof WebSocket
	*/
	Object.defineProperty(WebSocket, "CLOSED", {
		enumerable: true,
		value: readyStates.indexOf("CLOSED")
	});
	/**
	* @constant {Number} CLOSED
	* @memberof WebSocket.prototype
	*/
	Object.defineProperty(WebSocket.prototype, "CLOSED", {
		enumerable: true,
		value: readyStates.indexOf("CLOSED")
	});
	[
		"binaryType",
		"bufferedAmount",
		"extensions",
		"isPaused",
		"protocol",
		"readyState",
		"url"
	].forEach((property) => {
		Object.defineProperty(WebSocket.prototype, property, { enumerable: true });
	});
	[
		"open",
		"error",
		"close",
		"message"
	].forEach((method) => {
		Object.defineProperty(WebSocket.prototype, `on${method}`, {
			enumerable: true,
			get() {
				for (const listener of this.listeners(method)) if (listener[kForOnEventAttribute]) return listener[kListener];
				return null;
			},
			set(handler) {
				for (const listener of this.listeners(method)) if (listener[kForOnEventAttribute]) {
					this.removeListener(method, listener);
					break;
				}
				if (typeof handler !== "function") return;
				this.addEventListener(method, handler, { [kForOnEventAttribute]: true });
			}
		});
	});
	WebSocket.prototype.addEventListener = addEventListener;
	WebSocket.prototype.removeEventListener = removeEventListener;
	module.exports = WebSocket;
	/**
	* Initialize a WebSocket client.
	*
	* @param {WebSocket} websocket The client to initialize
	* @param {(String|URL)} address The URL to which to connect
	* @param {Array} protocols The subprotocols
	* @param {Object} [options] Connection options
	* @param {Boolean} [options.allowSynchronousEvents=true] Specifies whether any
	*     of the `'message'`, `'ping'`, and `'pong'` events can be emitted multiple
	*     times in the same tick
	* @param {Boolean} [options.autoPong=true] Specifies whether or not to
	*     automatically send a pong in response to a ping
	* @param {Number} [options.closeTimeout=30000] Duration in milliseconds to wait
	*     for the closing handshake to finish after `websocket.close()` is called
	* @param {Function} [options.finishRequest] A function which can be used to
	*     customize the headers of each http request before it is sent
	* @param {Boolean} [options.followRedirects=false] Whether or not to follow
	*     redirects
	* @param {Function} [options.generateMask] The function used to generate the
	*     masking key
	* @param {Number} [options.handshakeTimeout] Timeout in milliseconds for the
	*     handshake request
	* @param {Number} [options.maxPayload=104857600] The maximum allowed message
	*     size
	* @param {Number} [options.maxRedirects=10] The maximum number of redirects
	*     allowed
	* @param {String} [options.origin] Value of the `Origin` or
	*     `Sec-WebSocket-Origin` header
	* @param {(Boolean|Object)} [options.perMessageDeflate=true] Enable/disable
	*     permessage-deflate
	* @param {Number} [options.protocolVersion=13] Value of the
	*     `Sec-WebSocket-Version` header
	* @param {Boolean} [options.skipUTF8Validation=false] Specifies whether or
	*     not to skip UTF-8 validation for text and close messages
	* @private
	*/
	function initAsClient(websocket, address, protocols, options) {
		const opts = {
			allowSynchronousEvents: true,
			autoPong: true,
			closeTimeout: CLOSE_TIMEOUT,
			protocolVersion: protocolVersions[1],
			maxPayload: 100 * 1024 * 1024,
			skipUTF8Validation: false,
			perMessageDeflate: true,
			followRedirects: false,
			maxRedirects: 10,
			...options,
			socketPath: void 0,
			hostname: void 0,
			protocol: void 0,
			timeout: void 0,
			method: "GET",
			host: void 0,
			path: void 0,
			port: void 0
		};
		websocket._autoPong = opts.autoPong;
		websocket._closeTimeout = opts.closeTimeout;
		if (!protocolVersions.includes(opts.protocolVersion)) throw new RangeError(`Unsupported protocol version: ${opts.protocolVersion} (supported versions: ${protocolVersions.join(", ")})`);
		let parsedUrl;
		if (address instanceof URL) parsedUrl = address;
		else try {
			parsedUrl = new URL(address);
		} catch (e) {
			throw new SyntaxError(`Invalid URL: ${address}`);
		}
		if (parsedUrl.protocol === "http:") parsedUrl.protocol = "ws:";
		else if (parsedUrl.protocol === "https:") parsedUrl.protocol = "wss:";
		websocket._url = parsedUrl.href;
		const isSecure = parsedUrl.protocol === "wss:";
		const isIpcUrl = parsedUrl.protocol === "ws+unix:";
		let invalidUrlMessage;
		if (parsedUrl.protocol !== "ws:" && !isSecure && !isIpcUrl) invalidUrlMessage = "The URL's protocol must be one of \"ws:\", \"wss:\", \"http:\", \"https:\", or \"ws+unix:\"";
		else if (isIpcUrl && !parsedUrl.pathname) invalidUrlMessage = "The URL's pathname is empty";
		else if (parsedUrl.hash) invalidUrlMessage = "The URL contains a fragment identifier";
		if (invalidUrlMessage) {
			const err = new SyntaxError(invalidUrlMessage);
			if (websocket._redirects === 0) throw err;
			else {
				emitErrorAndClose(websocket, err);
				return;
			}
		}
		const defaultPort = isSecure ? 443 : 80;
		const key = randomBytes(16).toString("base64");
		const request = isSecure ? https.request : http$1.request;
		const protocolSet = /* @__PURE__ */ new Set();
		let perMessageDeflate;
		opts.createConnection = opts.createConnection || (isSecure ? tlsConnect : netConnect);
		opts.defaultPort = opts.defaultPort || defaultPort;
		opts.port = parsedUrl.port || defaultPort;
		opts.host = parsedUrl.hostname.startsWith("[") ? parsedUrl.hostname.slice(1, -1) : parsedUrl.hostname;
		opts.headers = {
			...opts.headers,
			"Sec-WebSocket-Version": opts.protocolVersion,
			"Sec-WebSocket-Key": key,
			Connection: "Upgrade",
			Upgrade: "websocket"
		};
		opts.path = parsedUrl.pathname + parsedUrl.search;
		opts.timeout = opts.handshakeTimeout;
		if (opts.perMessageDeflate) {
			perMessageDeflate = new PerMessageDeflate(opts.perMessageDeflate !== true ? opts.perMessageDeflate : {}, false, opts.maxPayload);
			opts.headers["Sec-WebSocket-Extensions"] = format({ [PerMessageDeflate.extensionName]: perMessageDeflate.offer() });
		}
		if (protocols.length) {
			for (const protocol of protocols) {
				if (typeof protocol !== "string" || !subprotocolRegex.test(protocol) || protocolSet.has(protocol)) throw new SyntaxError("An invalid or duplicated subprotocol was specified");
				protocolSet.add(protocol);
			}
			opts.headers["Sec-WebSocket-Protocol"] = protocols.join(",");
		}
		if (opts.origin) if (opts.protocolVersion < 13) opts.headers["Sec-WebSocket-Origin"] = opts.origin;
		else opts.headers.Origin = opts.origin;
		if (parsedUrl.username || parsedUrl.password) opts.auth = `${parsedUrl.username}:${parsedUrl.password}`;
		if (isIpcUrl) {
			const parts = opts.path.split(":");
			opts.socketPath = parts[0];
			opts.path = parts[1];
		}
		let req;
		if (opts.followRedirects) {
			if (websocket._redirects === 0) {
				websocket._originalIpc = isIpcUrl;
				websocket._originalSecure = isSecure;
				websocket._originalHostOrSocketPath = isIpcUrl ? opts.socketPath : parsedUrl.host;
				const headers = options && options.headers;
				options = {
					...options,
					headers: {}
				};
				if (headers) for (const [key, value] of Object.entries(headers)) options.headers[key.toLowerCase()] = value;
			} else if (websocket.listenerCount("redirect") === 0) {
				const isSameHost = isIpcUrl ? websocket._originalIpc ? opts.socketPath === websocket._originalHostOrSocketPath : false : websocket._originalIpc ? false : parsedUrl.host === websocket._originalHostOrSocketPath;
				if (!isSameHost || websocket._originalSecure && !isSecure) {
					delete opts.headers.authorization;
					delete opts.headers.cookie;
					if (!isSameHost) delete opts.headers.host;
					opts.auth = void 0;
				}
			}
			if (opts.auth && !options.headers.authorization) options.headers.authorization = "Basic " + Buffer.from(opts.auth).toString("base64");
			req = websocket._req = request(opts);
			if (websocket._redirects) websocket.emit("redirect", websocket.url, req);
		} else req = websocket._req = request(opts);
		if (opts.timeout) req.on("timeout", () => {
			abortHandshake(websocket, req, "Opening handshake has timed out");
		});
		req.on("error", (err) => {
			if (req === null || req[kAborted]) return;
			req = websocket._req = null;
			emitErrorAndClose(websocket, err);
		});
		req.on("response", (res) => {
			const location = res.headers.location;
			const statusCode = res.statusCode;
			if (location && opts.followRedirects && statusCode >= 300 && statusCode < 400) {
				if (++websocket._redirects > opts.maxRedirects) {
					abortHandshake(websocket, req, "Maximum redirects exceeded");
					return;
				}
				req.abort();
				let addr;
				try {
					addr = new URL(location, address);
				} catch (e) {
					emitErrorAndClose(websocket, /* @__PURE__ */ new SyntaxError(`Invalid URL: ${location}`));
					return;
				}
				initAsClient(websocket, addr, protocols, options);
			} else if (!websocket.emit("unexpected-response", req, res)) abortHandshake(websocket, req, `Unexpected server response: ${res.statusCode}`);
		});
		req.on("upgrade", (res, socket, head) => {
			websocket.emit("upgrade", res);
			if (websocket.readyState !== WebSocket.CONNECTING) return;
			req = websocket._req = null;
			const upgrade = res.headers.upgrade;
			if (upgrade === void 0 || upgrade.toLowerCase() !== "websocket") {
				abortHandshake(websocket, socket, "Invalid Upgrade header");
				return;
			}
			const digest = createHash$1("sha1").update(key + GUID).digest("base64");
			if (res.headers["sec-websocket-accept"] !== digest) {
				abortHandshake(websocket, socket, "Invalid Sec-WebSocket-Accept header");
				return;
			}
			const serverProt = res.headers["sec-websocket-protocol"];
			let protError;
			if (serverProt !== void 0) {
				if (!protocolSet.size) protError = "Server sent a subprotocol but none was requested";
				else if (!protocolSet.has(serverProt)) protError = "Server sent an invalid subprotocol";
			} else if (protocolSet.size) protError = "Server sent no subprotocol";
			if (protError) {
				abortHandshake(websocket, socket, protError);
				return;
			}
			if (serverProt) websocket._protocol = serverProt;
			const secWebSocketExtensions = res.headers["sec-websocket-extensions"];
			if (secWebSocketExtensions !== void 0) {
				if (!perMessageDeflate) {
					abortHandshake(websocket, socket, "Server sent a Sec-WebSocket-Extensions header but no extension was requested");
					return;
				}
				let extensions;
				try {
					extensions = parse(secWebSocketExtensions);
				} catch (err) {
					abortHandshake(websocket, socket, "Invalid Sec-WebSocket-Extensions header");
					return;
				}
				const extensionNames = Object.keys(extensions);
				if (extensionNames.length !== 1 || extensionNames[0] !== PerMessageDeflate.extensionName) {
					abortHandshake(websocket, socket, "Server indicated an extension that was not requested");
					return;
				}
				try {
					perMessageDeflate.accept(extensions[PerMessageDeflate.extensionName]);
				} catch (err) {
					abortHandshake(websocket, socket, "Invalid Sec-WebSocket-Extensions header");
					return;
				}
				websocket._extensions[PerMessageDeflate.extensionName] = perMessageDeflate;
			}
			websocket.setSocket(socket, head, {
				allowSynchronousEvents: opts.allowSynchronousEvents,
				generateMask: opts.generateMask,
				maxPayload: opts.maxPayload,
				skipUTF8Validation: opts.skipUTF8Validation
			});
		});
		if (opts.finishRequest) opts.finishRequest(req, websocket);
		else req.end();
	}
	/**
	* Emit the `'error'` and `'close'` events.
	*
	* @param {WebSocket} websocket The WebSocket instance
	* @param {Error} The error to emit
	* @private
	*/
	function emitErrorAndClose(websocket, err) {
		websocket._readyState = WebSocket.CLOSING;
		websocket._errorEmitted = true;
		websocket.emit("error", err);
		websocket.emitClose();
	}
	/**
	* Create a `net.Socket` and initiate a connection.
	*
	* @param {Object} options Connection options
	* @return {net.Socket} The newly created socket used to start the connection
	* @private
	*/
	function netConnect(options) {
		options.path = options.socketPath;
		return net.connect(options);
	}
	/**
	* Create a `tls.TLSSocket` and initiate a connection.
	*
	* @param {Object} options Connection options
	* @return {tls.TLSSocket} The newly created socket used to start the connection
	* @private
	*/
	function tlsConnect(options) {
		options.path = void 0;
		if (!options.servername && options.servername !== "") options.servername = net.isIP(options.host) ? "" : options.host;
		return tls.connect(options);
	}
	/**
	* Abort the handshake and emit an error.
	*
	* @param {WebSocket} websocket The WebSocket instance
	* @param {(http.ClientRequest|net.Socket|tls.Socket)} stream The request to
	*     abort or the socket to destroy
	* @param {String} message The error message
	* @private
	*/
	function abortHandshake(websocket, stream, message) {
		websocket._readyState = WebSocket.CLOSING;
		const err = new Error(message);
		Error.captureStackTrace(err, abortHandshake);
		if (stream.setHeader) {
			stream[kAborted] = true;
			stream.abort();
			if (stream.socket && !stream.socket.destroyed) stream.socket.destroy();
			process.nextTick(emitErrorAndClose, websocket, err);
		} else {
			stream.destroy(err);
			stream.once("error", websocket.emit.bind(websocket, "error"));
			stream.once("close", websocket.emitClose.bind(websocket));
		}
	}
	/**
	* Handle cases where the `ping()`, `pong()`, or `send()` methods are called
	* when the `readyState` attribute is `CLOSING` or `CLOSED`.
	*
	* @param {WebSocket} websocket The WebSocket instance
	* @param {*} [data] The data to send
	* @param {Function} [cb] Callback
	* @private
	*/
	function sendAfterClose(websocket, data, cb) {
		if (data) {
			const length = isBlob(data) ? data.size : toBuffer(data).length;
			if (websocket._socket) websocket._sender._bufferedBytes += length;
			else websocket._bufferedAmount += length;
		}
		if (cb) {
			const err = /* @__PURE__ */ new Error(`WebSocket is not open: readyState ${websocket.readyState} (${readyStates[websocket.readyState]})`);
			process.nextTick(cb, err);
		}
	}
	/**
	* The listener of the `Receiver` `'conclude'` event.
	*
	* @param {Number} code The status code
	* @param {Buffer} reason The reason for closing
	* @private
	*/
	function receiverOnConclude(code, reason) {
		const websocket = this[kWebSocket];
		websocket._closeFrameReceived = true;
		websocket._closeMessage = reason;
		websocket._closeCode = code;
		if (websocket._socket[kWebSocket] === void 0) return;
		websocket._socket.removeListener("data", socketOnData);
		process.nextTick(resume, websocket._socket);
		if (code === 1005) websocket.close();
		else websocket.close(code, reason);
	}
	/**
	* The listener of the `Receiver` `'drain'` event.
	*
	* @private
	*/
	function receiverOnDrain() {
		const websocket = this[kWebSocket];
		if (!websocket.isPaused) websocket._socket.resume();
	}
	/**
	* The listener of the `Receiver` `'error'` event.
	*
	* @param {(RangeError|Error)} err The emitted error
	* @private
	*/
	function receiverOnError(err) {
		const websocket = this[kWebSocket];
		if (websocket._socket[kWebSocket] !== void 0) {
			websocket._socket.removeListener("data", socketOnData);
			process.nextTick(resume, websocket._socket);
			websocket.close(err[kStatusCode]);
		}
		if (!websocket._errorEmitted) {
			websocket._errorEmitted = true;
			websocket.emit("error", err);
		}
	}
	/**
	* The listener of the `Receiver` `'finish'` event.
	*
	* @private
	*/
	function receiverOnFinish() {
		this[kWebSocket].emitClose();
	}
	/**
	* The listener of the `Receiver` `'message'` event.
	*
	* @param {Buffer|ArrayBuffer|Buffer[])} data The message
	* @param {Boolean} isBinary Specifies whether the message is binary or not
	* @private
	*/
	function receiverOnMessage(data, isBinary) {
		this[kWebSocket].emit("message", data, isBinary);
	}
	/**
	* The listener of the `Receiver` `'ping'` event.
	*
	* @param {Buffer} data The data included in the ping frame
	* @private
	*/
	function receiverOnPing(data) {
		const websocket = this[kWebSocket];
		if (websocket._autoPong) websocket.pong(data, !this._isServer, NOOP);
		websocket.emit("ping", data);
	}
	/**
	* The listener of the `Receiver` `'pong'` event.
	*
	* @param {Buffer} data The data included in the pong frame
	* @private
	*/
	function receiverOnPong(data) {
		this[kWebSocket].emit("pong", data);
	}
	/**
	* Resume a readable stream
	*
	* @param {Readable} stream The readable stream
	* @private
	*/
	function resume(stream) {
		stream.resume();
	}
	/**
	* The `Sender` error event handler.
	*
	* @param {Error} The error
	* @private
	*/
	function senderOnError(err) {
		const websocket = this[kWebSocket];
		if (websocket.readyState === WebSocket.CLOSED) return;
		if (websocket.readyState === WebSocket.OPEN) {
			websocket._readyState = WebSocket.CLOSING;
			setCloseTimer(websocket);
		}
		this._socket.end();
		if (!websocket._errorEmitted) {
			websocket._errorEmitted = true;
			websocket.emit("error", err);
		}
	}
	/**
	* Set a timer to destroy the underlying raw socket of a WebSocket.
	*
	* @param {WebSocket} websocket The WebSocket instance
	* @private
	*/
	function setCloseTimer(websocket) {
		websocket._closeTimer = setTimeout(websocket._socket.destroy.bind(websocket._socket), websocket._closeTimeout);
	}
	/**
	* The listener of the socket `'close'` event.
	*
	* @private
	*/
	function socketOnClose() {
		const websocket = this[kWebSocket];
		this.removeListener("close", socketOnClose);
		this.removeListener("data", socketOnData);
		this.removeListener("end", socketOnEnd);
		websocket._readyState = WebSocket.CLOSING;
		if (!this._readableState.endEmitted && !websocket._closeFrameReceived && !websocket._receiver._writableState.errorEmitted && this._readableState.length !== 0) {
			const chunk = this.read(this._readableState.length);
			websocket._receiver.write(chunk);
		}
		websocket._receiver.end();
		this[kWebSocket] = void 0;
		clearTimeout(websocket._closeTimer);
		if (websocket._receiver._writableState.finished || websocket._receiver._writableState.errorEmitted) websocket.emitClose();
		else {
			websocket._receiver.on("error", receiverOnFinish);
			websocket._receiver.on("finish", receiverOnFinish);
		}
	}
	/**
	* The listener of the socket `'data'` event.
	*
	* @param {Buffer} chunk A chunk of data
	* @private
	*/
	function socketOnData(chunk) {
		if (!this[kWebSocket]._receiver.write(chunk)) this.pause();
	}
	/**
	* The listener of the socket `'end'` event.
	*
	* @private
	*/
	function socketOnEnd() {
		const websocket = this[kWebSocket];
		websocket._readyState = WebSocket.CLOSING;
		websocket._receiver.end();
		this.end();
	}
	/**
	* The listener of the socket `'error'` event.
	*
	* @private
	*/
	function socketOnError() {
		const websocket = this[kWebSocket];
		this.removeListener("error", socketOnError);
		this.on("error", NOOP);
		if (websocket) {
			websocket._readyState = WebSocket.CLOSING;
			this.destroy();
		}
	}
}));
var require_stream = /* @__PURE__ */ __commonJSMin(((exports, module) => {
	require_websocket();
	const { Duplex: Duplex$1 } = __require("stream");
	/**
	* Emits the `'close'` event on a stream.
	*
	* @param {Duplex} stream The stream.
	* @private
	*/
	function emitClose(stream) {
		stream.emit("close");
	}
	/**
	* The listener of the `'end'` event.
	*
	* @private
	*/
	function duplexOnEnd() {
		if (!this.destroyed && this._writableState.finished) this.destroy();
	}
	/**
	* The listener of the `'error'` event.
	*
	* @param {Error} err The error
	* @private
	*/
	function duplexOnError(err) {
		this.removeListener("error", duplexOnError);
		this.destroy();
		if (this.listenerCount("error") === 0) this.emit("error", err);
	}
	/**
	* Wraps a `WebSocket` in a duplex stream.
	*
	* @param {WebSocket} ws The `WebSocket` to wrap
	* @param {Object} [options] The options for the `Duplex` constructor
	* @return {Duplex} The duplex stream
	* @public
	*/
	function createWebSocketStream(ws, options) {
		let terminateOnDestroy = true;
		const duplex = new Duplex$1({
			...options,
			autoDestroy: false,
			emitClose: false,
			objectMode: false,
			writableObjectMode: false
		});
		ws.on("message", function message(msg, isBinary) {
			const data = !isBinary && duplex._readableState.objectMode ? msg.toString() : msg;
			if (!duplex.push(data)) ws.pause();
		});
		ws.once("error", function error(err) {
			if (duplex.destroyed) return;
			terminateOnDestroy = false;
			duplex.destroy(err);
		});
		ws.once("close", function close() {
			if (duplex.destroyed) return;
			duplex.push(null);
		});
		duplex._destroy = function(err, callback) {
			if (ws.readyState === ws.CLOSED) {
				callback(err);
				process.nextTick(emitClose, duplex);
				return;
			}
			let called = false;
			ws.once("error", function error(err) {
				called = true;
				callback(err);
			});
			ws.once("close", function close() {
				if (!called) callback(err);
				process.nextTick(emitClose, duplex);
			});
			if (terminateOnDestroy) ws.terminate();
		};
		duplex._final = function(callback) {
			if (ws.readyState === ws.CONNECTING) {
				ws.once("open", function open() {
					duplex._final(callback);
				});
				return;
			}
			if (ws._socket === null) return;
			if (ws._socket._writableState.finished) {
				callback();
				if (duplex._readableState.endEmitted) duplex.destroy();
			} else {
				ws._socket.once("finish", function finish() {
					callback();
				});
				ws.close();
			}
		};
		duplex._read = function() {
			if (ws.isPaused) ws.resume();
		};
		duplex._write = function(chunk, encoding, callback) {
			if (ws.readyState === ws.CONNECTING) {
				ws.once("open", function open() {
					duplex._write(chunk, encoding, callback);
				});
				return;
			}
			ws.send(chunk, callback);
		};
		duplex.on("end", duplexOnEnd);
		duplex.on("error", duplexOnError);
		return duplex;
	}
	module.exports = createWebSocketStream;
}));
var require_subprotocol = /* @__PURE__ */ __commonJSMin(((exports, module) => {
	const { tokenChars } = require_validation();
	/**
	* Parses the `Sec-WebSocket-Protocol` header into a set of subprotocol names.
	*
	* @param {String} header The field value of the header
	* @return {Set} The subprotocol names
	* @public
	*/
	function parse(header) {
		const protocols = /* @__PURE__ */ new Set();
		let start = -1;
		let end = -1;
		let i = 0;
		for (; i < header.length; i++) {
			const code = header.charCodeAt(i);
			if (end === -1 && tokenChars[code] === 1) {
				if (start === -1) start = i;
			} else if (i !== 0 && (code === 32 || code === 9)) {
				if (end === -1 && start !== -1) end = i;
			} else if (code === 44) {
				if (start === -1) throw new SyntaxError(`Unexpected character at index ${i}`);
				if (end === -1) end = i;
				const protocol = header.slice(start, end);
				if (protocols.has(protocol)) throw new SyntaxError(`The "${protocol}" subprotocol is duplicated`);
				protocols.add(protocol);
				start = end = -1;
			} else throw new SyntaxError(`Unexpected character at index ${i}`);
		}
		if (start === -1 || end !== -1) throw new SyntaxError("Unexpected end of input");
		const protocol = header.slice(start, i);
		if (protocols.has(protocol)) throw new SyntaxError(`The "${protocol}" subprotocol is duplicated`);
		protocols.add(protocol);
		return protocols;
	}
	module.exports = { parse };
}));
var require_websocket_server = /* @__PURE__ */ __commonJSMin(((exports, module) => {
	const EventEmitter = __require("events");
	const http = __require("http");
	const { Duplex } = __require("stream");
	const { createHash } = __require("crypto");
	const extension = require_extension();
	const PerMessageDeflate = require_permessage_deflate();
	const subprotocol = require_subprotocol();
	const WebSocket = require_websocket();
	const { CLOSE_TIMEOUT, GUID, kWebSocket } = require_constants();
	const keyRegex = /^[+/0-9A-Za-z]{22}==$/;
	const RUNNING = 0;
	const CLOSING = 1;
	const CLOSED = 2;
	/**
	* Class representing a WebSocket server.
	*
	* @extends EventEmitter
	*/
	var WebSocketServer = class extends EventEmitter {
		/**
		* Create a `WebSocketServer` instance.
		*
		* @param {Object} options Configuration options
		* @param {Boolean} [options.allowSynchronousEvents=true] Specifies whether
		*     any of the `'message'`, `'ping'`, and `'pong'` events can be emitted
		*     multiple times in the same tick
		* @param {Boolean} [options.autoPong=true] Specifies whether or not to
		*     automatically send a pong in response to a ping
		* @param {Number} [options.backlog=511] The maximum length of the queue of
		*     pending connections
		* @param {Boolean} [options.clientTracking=true] Specifies whether or not to
		*     track clients
		* @param {Number} [options.closeTimeout=30000] Duration in milliseconds to
		*     wait for the closing handshake to finish after `websocket.close()` is
		*     called
		* @param {Function} [options.handleProtocols] A hook to handle protocols
		* @param {String} [options.host] The hostname where to bind the server
		* @param {Number} [options.maxPayload=104857600] The maximum allowed message
		*     size
		* @param {Boolean} [options.noServer=false] Enable no server mode
		* @param {String} [options.path] Accept only connections matching this path
		* @param {(Boolean|Object)} [options.perMessageDeflate=false] Enable/disable
		*     permessage-deflate
		* @param {Number} [options.port] The port where to bind the server
		* @param {(http.Server|https.Server)} [options.server] A pre-created HTTP/S
		*     server to use
		* @param {Boolean} [options.skipUTF8Validation=false] Specifies whether or
		*     not to skip UTF-8 validation for text and close messages
		* @param {Function} [options.verifyClient] A hook to reject connections
		* @param {Function} [options.WebSocket=WebSocket] Specifies the `WebSocket`
		*     class to use. It must be the `WebSocket` class or class that extends it
		* @param {Function} [callback] A listener for the `listening` event
		*/
		constructor(options, callback) {
			super();
			options = {
				allowSynchronousEvents: true,
				autoPong: true,
				maxPayload: 100 * 1024 * 1024,
				skipUTF8Validation: false,
				perMessageDeflate: false,
				handleProtocols: null,
				clientTracking: true,
				closeTimeout: CLOSE_TIMEOUT,
				verifyClient: null,
				noServer: false,
				backlog: null,
				server: null,
				host: null,
				path: null,
				port: null,
				WebSocket,
				...options
			};
			if (options.port == null && !options.server && !options.noServer || options.port != null && (options.server || options.noServer) || options.server && options.noServer) throw new TypeError("One and only one of the \"port\", \"server\", or \"noServer\" options must be specified");
			if (options.port != null) {
				this._server = http.createServer((req, res) => {
					const body = http.STATUS_CODES[426];
					res.writeHead(426, {
						"Content-Length": body.length,
						"Content-Type": "text/plain"
					});
					res.end(body);
				});
				this._server.listen(options.port, options.host, options.backlog, callback);
			} else if (options.server) this._server = options.server;
			if (this._server) {
				const emitConnection = this.emit.bind(this, "connection");
				this._removeListeners = addListeners(this._server, {
					listening: this.emit.bind(this, "listening"),
					error: this.emit.bind(this, "error"),
					upgrade: (req, socket, head) => {
						this.handleUpgrade(req, socket, head, emitConnection);
					}
				});
			}
			if (options.perMessageDeflate === true) options.perMessageDeflate = {};
			if (options.clientTracking) {
				this.clients = /* @__PURE__ */ new Set();
				this._shouldEmitClose = false;
			}
			this.options = options;
			this._state = RUNNING;
		}
		/**
		* Returns the bound address, the address family name, and port of the server
		* as reported by the operating system if listening on an IP socket.
		* If the server is listening on a pipe or UNIX domain socket, the name is
		* returned as a string.
		*
		* @return {(Object|String|null)} The address of the server
		* @public
		*/
		address() {
			if (this.options.noServer) throw new Error("The server is operating in \"noServer\" mode");
			if (!this._server) return null;
			return this._server.address();
		}
		/**
		* Stop the server from accepting new connections and emit the `'close'` event
		* when all existing connections are closed.
		*
		* @param {Function} [cb] A one-time listener for the `'close'` event
		* @public
		*/
		close(cb) {
			if (this._state === CLOSED) {
				if (cb) this.once("close", () => {
					cb(/* @__PURE__ */ new Error("The server is not running"));
				});
				process.nextTick(emitClose, this);
				return;
			}
			if (cb) this.once("close", cb);
			if (this._state === CLOSING) return;
			this._state = CLOSING;
			if (this.options.noServer || this.options.server) {
				if (this._server) {
					this._removeListeners();
					this._removeListeners = this._server = null;
				}
				if (this.clients) if (!this.clients.size) process.nextTick(emitClose, this);
				else this._shouldEmitClose = true;
				else process.nextTick(emitClose, this);
			} else {
				const server = this._server;
				this._removeListeners();
				this._removeListeners = this._server = null;
				server.close(() => {
					emitClose(this);
				});
			}
		}
		/**
		* See if a given request should be handled by this server instance.
		*
		* @param {http.IncomingMessage} req Request object to inspect
		* @return {Boolean} `true` if the request is valid, else `false`
		* @public
		*/
		shouldHandle(req) {
			if (this.options.path) {
				const index = req.url.indexOf("?");
				if ((index !== -1 ? req.url.slice(0, index) : req.url) !== this.options.path) return false;
			}
			return true;
		}
		/**
		* Handle a HTTP Upgrade request.
		*
		* @param {http.IncomingMessage} req The request object
		* @param {Duplex} socket The network socket between the server and client
		* @param {Buffer} head The first packet of the upgraded stream
		* @param {Function} cb Callback
		* @public
		*/
		handleUpgrade(req, socket, head, cb) {
			socket.on("error", socketOnError);
			const key = req.headers["sec-websocket-key"];
			const upgrade = req.headers.upgrade;
			const version = +req.headers["sec-websocket-version"];
			if (req.method !== "GET") {
				abortHandshakeOrEmitwsClientError(this, req, socket, 405, "Invalid HTTP method");
				return;
			}
			if (upgrade === void 0 || upgrade.toLowerCase() !== "websocket") {
				abortHandshakeOrEmitwsClientError(this, req, socket, 400, "Invalid Upgrade header");
				return;
			}
			if (key === void 0 || !keyRegex.test(key)) {
				abortHandshakeOrEmitwsClientError(this, req, socket, 400, "Missing or invalid Sec-WebSocket-Key header");
				return;
			}
			if (version !== 13 && version !== 8) {
				abortHandshakeOrEmitwsClientError(this, req, socket, 400, "Missing or invalid Sec-WebSocket-Version header", { "Sec-WebSocket-Version": "13, 8" });
				return;
			}
			if (!this.shouldHandle(req)) {
				abortHandshake(socket, 400);
				return;
			}
			const secWebSocketProtocol = req.headers["sec-websocket-protocol"];
			let protocols = /* @__PURE__ */ new Set();
			if (secWebSocketProtocol !== void 0) try {
				protocols = subprotocol.parse(secWebSocketProtocol);
			} catch (err) {
				abortHandshakeOrEmitwsClientError(this, req, socket, 400, "Invalid Sec-WebSocket-Protocol header");
				return;
			}
			const secWebSocketExtensions = req.headers["sec-websocket-extensions"];
			const extensions = {};
			if (this.options.perMessageDeflate && secWebSocketExtensions !== void 0) {
				const perMessageDeflate = new PerMessageDeflate(this.options.perMessageDeflate, true, this.options.maxPayload);
				try {
					const offers = extension.parse(secWebSocketExtensions);
					if (offers[PerMessageDeflate.extensionName]) {
						perMessageDeflate.accept(offers[PerMessageDeflate.extensionName]);
						extensions[PerMessageDeflate.extensionName] = perMessageDeflate;
					}
				} catch (err) {
					abortHandshakeOrEmitwsClientError(this, req, socket, 400, "Invalid or unacceptable Sec-WebSocket-Extensions header");
					return;
				}
			}
			if (this.options.verifyClient) {
				const info = {
					origin: req.headers[`${version === 8 ? "sec-websocket-origin" : "origin"}`],
					secure: !!(req.socket.authorized || req.socket.encrypted),
					req
				};
				if (this.options.verifyClient.length === 2) {
					this.options.verifyClient(info, (verified, code, message, headers) => {
						if (!verified) return abortHandshake(socket, code || 401, message, headers);
						this.completeUpgrade(extensions, key, protocols, req, socket, head, cb);
					});
					return;
				}
				if (!this.options.verifyClient(info)) return abortHandshake(socket, 401);
			}
			this.completeUpgrade(extensions, key, protocols, req, socket, head, cb);
		}
		/**
		* Upgrade the connection to WebSocket.
		*
		* @param {Object} extensions The accepted extensions
		* @param {String} key The value of the `Sec-WebSocket-Key` header
		* @param {Set} protocols The subprotocols
		* @param {http.IncomingMessage} req The request object
		* @param {Duplex} socket The network socket between the server and client
		* @param {Buffer} head The first packet of the upgraded stream
		* @param {Function} cb Callback
		* @throws {Error} If called more than once with the same socket
		* @private
		*/
		completeUpgrade(extensions, key, protocols, req, socket, head, cb) {
			if (!socket.readable || !socket.writable) return socket.destroy();
			if (socket[kWebSocket]) throw new Error("server.handleUpgrade() was called more than once with the same socket, possibly due to a misconfiguration");
			if (this._state > RUNNING) return abortHandshake(socket, 503);
			const headers = [
				"HTTP/1.1 101 Switching Protocols",
				"Upgrade: websocket",
				"Connection: Upgrade",
				`Sec-WebSocket-Accept: ${createHash("sha1").update(key + GUID).digest("base64")}`
			];
			const ws = new this.options.WebSocket(null, void 0, this.options);
			if (protocols.size) {
				const protocol = this.options.handleProtocols ? this.options.handleProtocols(protocols, req) : protocols.values().next().value;
				if (protocol) {
					headers.push(`Sec-WebSocket-Protocol: ${protocol}`);
					ws._protocol = protocol;
				}
			}
			if (extensions[PerMessageDeflate.extensionName]) {
				const params = extensions[PerMessageDeflate.extensionName].params;
				const value = extension.format({ [PerMessageDeflate.extensionName]: [params] });
				headers.push(`Sec-WebSocket-Extensions: ${value}`);
				ws._extensions = extensions;
			}
			this.emit("headers", headers, req);
			socket.write(headers.concat("\r\n").join("\r\n"));
			socket.removeListener("error", socketOnError);
			ws.setSocket(socket, head, {
				allowSynchronousEvents: this.options.allowSynchronousEvents,
				maxPayload: this.options.maxPayload,
				skipUTF8Validation: this.options.skipUTF8Validation
			});
			if (this.clients) {
				this.clients.add(ws);
				ws.on("close", () => {
					this.clients.delete(ws);
					if (this._shouldEmitClose && !this.clients.size) process.nextTick(emitClose, this);
				});
			}
			cb(ws, req);
		}
	};
	module.exports = WebSocketServer;
	/**
	* Add event listeners on an `EventEmitter` using a map of <event, listener>
	* pairs.
	*
	* @param {EventEmitter} server The event emitter
	* @param {Object.<String, Function>} map The listeners to add
	* @return {Function} A function that will remove the added listeners when
	*     called
	* @private
	*/
	function addListeners(server, map) {
		for (const event of Object.keys(map)) server.on(event, map[event]);
		return function removeListeners() {
			for (const event of Object.keys(map)) server.removeListener(event, map[event]);
		};
	}
	/**
	* Emit a `'close'` event on an `EventEmitter`.
	*
	* @param {EventEmitter} server The event emitter
	* @private
	*/
	function emitClose(server) {
		server._state = CLOSED;
		server.emit("close");
	}
	/**
	* Handle socket errors.
	*
	* @private
	*/
	function socketOnError() {
		this.destroy();
	}
	/**
	* Close the connection when preconditions are not fulfilled.
	*
	* @param {Duplex} socket The socket of the upgrade request
	* @param {Number} code The HTTP response status code
	* @param {String} [message] The HTTP response body
	* @param {Object} [headers] Additional HTTP response headers
	* @private
	*/
	function abortHandshake(socket, code, message, headers) {
		message = message || http.STATUS_CODES[code];
		headers = {
			Connection: "close",
			"Content-Type": "text/html",
			"Content-Length": Buffer.byteLength(message),
			...headers
		};
		socket.once("finish", socket.destroy);
		socket.end(`HTTP/1.1 ${code} ${http.STATUS_CODES[code]}\r\n` + Object.keys(headers).map((h) => `${h}: ${headers[h]}`).join("\r\n") + "\r\n\r\n" + message);
	}
	/**
	* Emit a `'wsClientError'` event on a `WebSocketServer` if there is at least
	* one listener for it, otherwise call `abortHandshake()`.
	*
	* @param {WebSocketServer} server The WebSocket server
	* @param {http.IncomingMessage} req The request object
	* @param {Duplex} socket The socket of the upgrade request
	* @param {Number} code The HTTP response status code
	* @param {String} message The HTTP response body
	* @param {Object} [headers] The HTTP response headers
	* @private
	*/
	function abortHandshakeOrEmitwsClientError(server, req, socket, code, message, headers) {
		if (server.listenerCount("wsClientError")) {
			const err = new Error(message);
			Error.captureStackTrace(err, abortHandshakeOrEmitwsClientError);
			server.emit("wsClientError", err, socket, req);
		} else abortHandshake(socket, code, message, headers);
	}
}));
require_stream();
require_receiver();
require_sender();
var import_websocket = /* @__PURE__ */ __toESM(require_websocket(), 1);
var import_websocket_server = /* @__PURE__ */ __toESM(require_websocket_server(), 1);

//#endregion
//#region node_modules/.pnpm/crossws@0.4.4/node_modules/crossws/dist/_chunks/peer.mjs
const kNodeInspect = /* @__PURE__ */ Symbol.for("nodejs.util.inspect.custom");
function toBufferLike(val) {
	if (val === void 0 || val === null) return "";
	const type = typeof val;
	if (type === "string") return val;
	if (type === "number" || type === "boolean" || type === "bigint") return val.toString();
	if (type === "function" || type === "symbol") return "{}";
	if (val instanceof Uint8Array || val instanceof ArrayBuffer) return val;
	if (isPlainObject(val)) return JSON.stringify(val);
	return val;
}
function isPlainObject(value) {
	if (value === null || typeof value !== "object") return false;
	const prototype = Object.getPrototypeOf(value);
	if (prototype !== null && prototype !== Object.prototype && Object.getPrototypeOf(prototype) !== null) return false;
	if (Symbol.iterator in value) return false;
	if (Symbol.toStringTag in value) return Object.prototype.toString.call(value) === "[object Module]";
	return true;
}
var Message = class {
	/** Access to the original [message event](https://developer.mozilla.org/en-US/docs/Web/API/WebSocket/message_event) if available. */
	event;
	/** Access to the Peer that emitted the message. */
	peer;
	/** Raw message data (can be of any type). */
	rawData;
	#id;
	#uint8Array;
	#arrayBuffer;
	#blob;
	#text;
	#json;
	constructor(rawData, peer, event) {
		this.rawData = rawData || "";
		this.peer = peer;
		this.event = event;
	}
	/**
	* Unique random [uuid v4](https://developer.mozilla.org/en-US/docs/Glossary/UUID) identifier for the message.
	*/
	get id() {
		if (!this.#id) this.#id = crypto.randomUUID();
		return this.#id;
	}
	/**
	* Get data as [Uint8Array](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array) value.
	*
	* If raw data is in any other format or string, it will be automatically converted and encoded.
	*/
	uint8Array() {
		const _uint8Array = this.#uint8Array;
		if (_uint8Array) return _uint8Array;
		const rawData = this.rawData;
		if (rawData instanceof Uint8Array) return this.#uint8Array = rawData;
		if (rawData instanceof ArrayBuffer || rawData instanceof SharedArrayBuffer) {
			this.#arrayBuffer = rawData;
			return this.#uint8Array = new Uint8Array(rawData);
		}
		if (typeof rawData === "string") {
			this.#text = rawData;
			return this.#uint8Array = new TextEncoder().encode(this.#text);
		}
		if (Symbol.iterator in rawData) return this.#uint8Array = new Uint8Array(rawData);
		if (typeof rawData?.length === "number") return this.#uint8Array = new Uint8Array(rawData);
		if (rawData instanceof DataView) return this.#uint8Array = new Uint8Array(rawData.buffer, rawData.byteOffset, rawData.byteLength);
		throw new TypeError(`Unsupported message type: ${Object.prototype.toString.call(rawData)}`);
	}
	/**
	* Get data as [ArrayBuffer](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/ArrayBuffer) or [SharedArrayBuffer](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/SharedArrayBuffer) value.
	*
	* If raw data is in any other format or string, it will be automatically converted and encoded.
	*/
	arrayBuffer() {
		const _arrayBuffer = this.#arrayBuffer;
		if (_arrayBuffer) return _arrayBuffer;
		const rawData = this.rawData;
		if (rawData instanceof ArrayBuffer || rawData instanceof SharedArrayBuffer) return this.#arrayBuffer = rawData;
		return this.#arrayBuffer = this.uint8Array().buffer;
	}
	/**
	* Get data as [Blob](https://developer.mozilla.org/en-US/docs/Web/API/Blob) value.
	*
	* If raw data is in any other format or string, it will be automatically converted and encoded. */
	blob() {
		const _blob = this.#blob;
		if (_blob) return _blob;
		const rawData = this.rawData;
		if (rawData instanceof Blob) return this.#blob = rawData;
		return this.#blob = new Blob([this.uint8Array()]);
	}
	/**
	* Get stringified text version of the message.
	*
	* If raw data is in any other format, it will be automatically converted and decoded.
	*/
	text() {
		const _text = this.#text;
		if (_text) return _text;
		const rawData = this.rawData;
		if (typeof rawData === "string") return this.#text = rawData;
		return this.#text = new TextDecoder().decode(this.uint8Array());
	}
	/**
	* Get parsed version of the message text with [`JSON.parse()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/parse).
	*/
	json() {
		const _json = this.#json;
		if (_json) return _json;
		return this.#json = JSON.parse(this.text());
	}
	/**
	* Message data (value varies based on `peer.websocket.binaryType`).
	*/
	get data() {
		switch (this.peer?.websocket?.binaryType) {
			case "arraybuffer": return this.arrayBuffer();
			case "blob": return this.blob();
			case "nodebuffer": return globalThis.Buffer ? Buffer.from(this.uint8Array()) : this.uint8Array();
			case "uint8array": return this.uint8Array();
			case "text": return this.text();
			default: return this.rawData;
		}
	}
	toString() {
		return this.text();
	}
	[Symbol.toPrimitive]() {
		return this.text();
	}
	[kNodeInspect]() {
		return { message: {
			id: this.id,
			peer: this.peer,
			text: this.text()
		} };
	}
};
var Peer = class {
	_internal;
	_topics;
	_id;
	#ws;
	constructor(internal) {
		this._topics = /* @__PURE__ */ new Set();
		this._internal = internal;
	}
	get context() {
		return this._internal.context ??= {};
	}
	get namespace() {
		return this._internal.namespace;
	}
	/**
	* Unique random [uuid v4](https://developer.mozilla.org/en-US/docs/Glossary/UUID) identifier for the peer.
	*/
	get id() {
		if (!this._id) this._id = crypto.randomUUID();
		return this._id;
	}
	/** IP address of the peer */
	get remoteAddress() {}
	/** upgrade request */
	get request() {
		return this._internal.request;
	}
	/**
	* Get the [WebSocket](https://developer.mozilla.org/en-US/docs/Web/API/WebSocket) instance.
	*
	* **Note:** crossws adds polyfill for the following properties if native values are not available:
	* - `protocol`: Extracted from the `sec-websocket-protocol` header.
	* - `extensions`: Extracted from the `sec-websocket-extensions` header.
	* - `url`: Extracted from the request URL (http -> ws).
	* */
	get websocket() {
		if (!this.#ws) {
			const _ws = this._internal.ws;
			const _request = this._internal.request;
			this.#ws = _request ? createWsProxy(_ws, _request) : _ws;
		}
		return this.#ws;
	}
	/** All connected peers to the server */
	get peers() {
		return this._internal.peers || /* @__PURE__ */ new Set();
	}
	/** All topics, this peer has been subscribed to. */
	get topics() {
		return this._topics;
	}
	/** Abruptly close the connection */
	terminate() {
		this.close();
	}
	/** Subscribe to a topic */
	subscribe(topic) {
		this._topics.add(topic);
	}
	/** Unsubscribe from a topic */
	unsubscribe(topic) {
		this._topics.delete(topic);
	}
	toString() {
		return this.id;
	}
	[Symbol.toPrimitive]() {
		return this.id;
	}
	[Symbol.toStringTag]() {
		return "WebSocket";
	}
	[kNodeInspect]() {
		return { peer: {
			id: this.id,
			ip: this.remoteAddress
		} };
	}
};
function createWsProxy(ws, request) {
	return new Proxy(ws, { get: (target, prop) => {
		const value = Reflect.get(target, prop);
		if (!value) switch (prop) {
			case "protocol": return request?.headers?.get("sec-websocket-protocol") || "";
			case "extensions": return request?.headers?.get("sec-websocket-extensions") || "";
			case "url": return request?.url?.replace(/^http/, "ws") || void 0;
		}
		return value;
	} });
}

//#endregion
//#region node_modules/.pnpm/crossws@0.4.4/node_modules/crossws/dist/_chunks/_request.mjs
const StubRequest = /* @__PURE__ */ (() => {
	class StubRequest {
		url;
		_abortController;
		_headers;
		_init;
		constructor(url, init = {}) {
			this.url = url;
			this._init = init;
		}
		get headers() {
			if (!this._headers) this._headers = new Headers(this._init?.headers);
			return this._headers;
		}
		clone() {
			return new StubRequest(this.url, this._init);
		}
		get method() {
			return "GET";
		}
		get signal() {
			if (!this._abortController) this._abortController = new AbortController();
			return this._abortController.signal;
		}
		get cache() {
			return "default";
		}
		get credentials() {
			return "same-origin";
		}
		get destination() {
			return "";
		}
		get integrity() {
			return "";
		}
		get keepalive() {
			return false;
		}
		get redirect() {
			return "follow";
		}
		get mode() {
			return "cors";
		}
		get referrer() {
			return "about:client";
		}
		get referrerPolicy() {
			return "";
		}
		get body() {
			return null;
		}
		get bodyUsed() {
			return false;
		}
		arrayBuffer() {
			return Promise.resolve(/* @__PURE__ */ new ArrayBuffer(0));
		}
		blob() {
			return Promise.resolve(new Blob());
		}
		bytes() {
			return Promise.resolve(new Uint8Array());
		}
		formData() {
			return Promise.resolve(new FormData());
		}
		json() {
			return Promise.resolve(JSON.parse(""));
		}
		text() {
			return Promise.resolve("");
		}
	}
	Object.setPrototypeOf(StubRequest.prototype, globalThis.Request.prototype);
	return StubRequest;
})();

//#endregion
//#region node_modules/.pnpm/crossws@0.4.4/node_modules/crossws/dist/_chunks/error.mjs
var WSError = class extends Error {
	constructor(...args) {
		super(...args);
		this.name = "WSError";
	}
};

//#endregion
//#region node_modules/.pnpm/crossws@0.4.4/node_modules/crossws/dist/adapters/node.mjs
const nodeAdapter = (options = {}) => {
	if ("Deno" in globalThis || "Bun" in globalThis) throw new Error("[crossws] Using Node.js adapter in an incompatible environment.");
	const hooks = new AdapterHookable(options);
	const globalPeers = /* @__PURE__ */ new Map();
	const wss = options.wss || new import_websocket_server.default({
		noServer: true,
		handleProtocols: () => false,
		...options.serverOptions
	});
	wss.on("connection", (ws, nodeReq) => {
		const request = new NodeReqProxy(nodeReq);
		const peers = getPeers(globalPeers, nodeReq._namespace);
		const peer = new NodePeer({
			ws,
			request,
			peers,
			nodeReq,
			namespace: nodeReq._namespace
		});
		peers.add(peer);
		hooks.callHook("open", peer);
		ws.on("message", (data) => {
			if (Array.isArray(data)) data = Buffer.concat(data);
			hooks.callHook("message", peer, new Message(data, peer));
		});
		ws.on("error", (error) => {
			peers.delete(peer);
			hooks.callHook("error", peer, new WSError(error));
		});
		ws.on("close", (code, reason) => {
			peers.delete(peer);
			hooks.callHook("close", peer, {
				code,
				reason: reason?.toString()
			});
		});
	});
	wss.on("headers", (outgoingHeaders, req) => {
		const upgradeHeaders = req._upgradeHeaders;
		if (upgradeHeaders) for (const [key, value] of new Headers(upgradeHeaders)) outgoingHeaders.push(`${key}: ${value}`);
	});
	return {
		...adapterUtils(globalPeers),
		handleUpgrade: async (nodeReq, socket, head, webRequest) => {
			const request = webRequest || new NodeReqProxy(nodeReq);
			const { upgradeHeaders, endResponse, context, namespace } = await hooks.upgrade(request);
			if (endResponse) return sendResponse(socket, endResponse);
			nodeReq._request = request;
			nodeReq._upgradeHeaders = upgradeHeaders;
			nodeReq._context = context;
			nodeReq._namespace = namespace;
			wss.handleUpgrade(nodeReq, socket, head, (ws) => {
				wss.emit("connection", ws, nodeReq);
			});
		},
		closeAll: (code, data, force) => {
			for (const client of wss.clients) if (force) client.terminate();
			else client.close(code, data);
		}
	};
};
var node_default = nodeAdapter;
var NodePeer = class extends Peer {
	get remoteAddress() {
		return this._internal.nodeReq.socket?.remoteAddress;
	}
	get context() {
		return this._internal.nodeReq._context;
	}
	send(data, options) {
		const dataBuff = toBufferLike(data);
		const isBinary = typeof dataBuff !== "string";
		this._internal.ws.send(dataBuff, {
			compress: options?.compress,
			binary: isBinary,
			...options
		});
		return 0;
	}
	publish(topic, data, options) {
		const dataBuff = toBufferLike(data);
		const isBinary = typeof data !== "string";
		const sendOptions = {
			compress: options?.compress,
			binary: isBinary,
			...options
		};
		for (const peer of this._internal.peers) if (peer !== this && peer._topics.has(topic)) peer._internal.ws.send(dataBuff, sendOptions);
	}
	close(code, data) {
		this._internal.ws.close(code, data);
	}
	terminate() {
		this._internal.ws.terminate();
	}
};
var NodeReqProxy = class extends StubRequest {
	constructor(req) {
		const host = req.headers["host"] || "localhost";
		const url = `${req.socket?.encrypted ?? req.headers["x-forwarded-proto"] === "https" ? "https" : "http"}://${host}${req.url}`;
		super(url, { headers: req.headers });
	}
};
async function sendResponse(socket, res) {
	const head = [`HTTP/1.1 ${res.status || 200} ${res.statusText || ""}`, ...[...res.headers.entries()].map(([key, value]) => `${encodeURIComponent(key)}: ${encodeURIComponent(value)}`)];
	socket.write(head.join("\r\n") + "\r\n\r\n");
	if (res.body) for await (const chunk of res.body) socket.write(chunk);
	return new Promise((resolve) => {
		socket.end(() => {
			socket.destroy();
			resolve();
		});
	});
}

//#endregion
//#region packages/server/src/Server.ts
const defaultServerConfiguration = {
	port: 80,
	address: "0.0.0.0",
	stopOnSignals: true
};
var Server = class {
	constructor(configuration) {
		this.configuration = {
			...defaultConfiguration,
			...defaultServerConfiguration,
			extensions: []
		};
		this.setupHttpUpgrade = () => {
			this.httpServer.on("upgrade", async (request, socket, head) => {
				try {
					await this.hocuspocus.hooks("onUpgrade", {
						request,
						socket,
						head,
						instance: this.hocuspocus
					});
					this.crossws.handleUpgrade(request, socket, head);
				} catch (error) {
					if (error) throw error;
				}
			});
		};
		this.requestHandler = async (request, response) => {
			try {
				await this.hocuspocus.hooks("onRequest", {
					request,
					response,
					instance: this.hocuspocus
				});
				response.writeHead(200, { "Content-Type": "text/plain" });
				response.end("Welcome to Hocuspocus!");
			} catch (error) {
				if (error) throw error;
			}
		};
		if (configuration) this.configuration = {
			...this.configuration,
			...configuration
		};
		this.hocuspocus = new Hocuspocus(this.configuration);
		this.hocuspocus.server = this;
		this.httpServer = (0, node_http.createServer)(this.requestHandler);
		this.crossws = node_default({
			serverOptions: this.configuration.websocketOptions,
			hooks: {
				open: (peer) => {
					peer._hocuspocus = this.hocuspocus.handleConnection(peer.websocket, peer.request);
				},
				message: (peer, message) => {
					peer._hocuspocus?.handleMessage(message.uint8Array());
				},
				close: (peer, event) => {
					peer._hocuspocus?.handleClose({
						code: event.code,
						reason: event.reason
					});
				},
				error: (peer, error) => {
					console.error("WebSocket error for peer:", peer.id);
					console.error(error);
				}
			}
		});
		this.setupHttpUpgrade();
	}
	async listen(port, callback = null) {
		if (port) this.configuration.port = port;
		if (typeof callback === "function") this.hocuspocus.configuration.extensions.push({ onListen: callback });
		if (this.configuration.stopOnSignals) {
			const signalHandler = async () => {
				await this.destroy();
				process.exit(0);
			};
			process.on("SIGINT", signalHandler);
			process.on("SIGQUIT", signalHandler);
			process.on("SIGTERM", signalHandler);
		}
		return new Promise((resolve, reject) => {
			this.httpServer.listen({
				port: this.configuration.port,
				address: this.configuration.address
			}, async () => {
				if (!this.configuration.quiet && String(process.env.NODE_ENV) !== "testing") this.showStartScreen();
				const onListenPayload = {
					instance: this.hocuspocus,
					configuration: this.configuration,
					port: this.address.port
				};
				try {
					await this.hocuspocus.hooks("onListen", onListenPayload);
					resolve(this.hocuspocus);
				} catch (e) {
					reject(e);
				}
			});
		});
	}
	get address() {
		return this.httpServer.address() || {
			port: this.configuration.port,
			address: this.configuration.address,
			family: "IPv4"
		};
	}
	async destroy() {
		await new Promise((resolve) => {
			this.httpServer.close();
			try {
				this.configuration.extensions.push({ async afterUnloadDocument({ instance }) {
					if (instance.getDocumentsCount() === 0) resolve();
				} });
				if (this.hocuspocus.getDocumentsCount() === 0) resolve();
				this.hocuspocus.closeConnections();
				this.hocuspocus.flushPendingStores();
			} catch (error) {
				console.error(error);
			}
		});
		await this.hocuspocus.hooks("onDestroy", { instance: this.hocuspocus });
	}
	get URL() {
		return `${this.configuration.address}:${this.address.port}`;
	}
	get webSocketURL() {
		return `ws://${this.URL}`;
	}
	get httpURL() {
		return `http://${this.URL}`;
	}
	showStartScreen() {
		const name = this.configuration.name ? ` (${this.configuration.name})` : "";
		console.log();
		console.log(`  ${kleur.default.cyan(`Hocuspocus v${version}${name}`)}${kleur.default.green(" running at:")}`);
		console.log();
		console.log(`  > HTTP: ${kleur.default.cyan(`${this.httpURL}`)}`);
		console.log(`  > WebSocket: ${this.webSocketURL}`);
		const extensions = this.configuration?.extensions.map((extension) => {
			return extension.extensionName ?? extension.constructor?.name;
		}).filter((name) => name).filter((name) => name !== "Object");
		if (!extensions.length) return;
		console.log();
		console.log("  Extensions:");
		extensions.forEach((name) => {
			console.log(`  - ${name}`);
		});
		console.log();
		console.log(`  ${kleur.default.green("Ready.")}`);
		console.log();
	}
};

//#endregion
exports.Connection = Connection;
exports.Document = Document;
exports.Hocuspocus = Hocuspocus;
exports.IncomingMessage = IncomingMessage;
exports.MessageReceiver = MessageReceiver;
exports.MessageType = MessageType;
exports.OutgoingMessage = OutgoingMessage;
exports.Server = Server;
exports.defaultConfiguration = defaultConfiguration;
exports.defaultServerConfiguration = defaultServerConfiguration;
exports.isTransactionOrigin = isTransactionOrigin;
exports.shouldSkipStoreHooks = shouldSkipStoreHooks;
exports.useDebounce = useDebounce;
//# sourceMappingURL=hocuspocus-server.cjs.map