var __defProp = Object.defineProperty;
var __typeError = (msg) => {
  throw TypeError(msg);
};
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
var __accessCheck = (obj, member, msg) => member.has(obj) || __typeError("Cannot " + msg);
var __privateGet = (obj, member, getter) => (__accessCheck(obj, member, "read from private field"), getter ? getter.call(obj) : member.get(obj));
var __privateAdd = (obj, member, value) => member.has(obj) ? __typeError("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "write to private field"), setter ? setter.call(obj, value) : member.set(obj, value), value);
var __privateMethod = (obj, member, method) => (__accessCheck(obj, member, "access private method"), method);
var __privateWrapper = (obj, member, setter, getter) => ({
  set _(value) {
    __privateSet(obj, member, value, setter);
  },
  get _() {
    return __privateGet(obj, member, getter);
  }
});
var _syncInfoByDocId, _ourSubscriptions, _theirSubscriptions, _generousPeers, _subscribedDocsByPeer, _log, _RemoteHeadsSubscriptions_instances, isPeerSubscribedToDoc_fn, changedHeads_fn, _log2, _adaptersByPeer, _count, _sessionId, _ephemeralSessionCounts, _storageAdapter, _storedHeads, _chunkInfos, _compacting, _log3, _StorageSubsystem_instances, saveIncremental_fn, saveTotal_fn, shouldSave_fn, shouldCompact_fn, _log4, _saveDebounceRate, _saveFn, _handleCache, _shareConfig, _remoteHeadsSubscriptions, _remoteHeadsGossipingEnabled, _progressCache, _saveFns, _idFactory, _Repo_instances, registerHandleWithSubsystems_fn, receiveMessage_fn, _throttledSaveSyncStateHandlers, saveSyncState_fn, getHandle_fn, loadDocumentWithProgress_fn, loadDocument_fn, _broadcastChannel, _disconnected, _ready, _markReady, _readyPromise, _options, _connectedPeers, _BroadcastChannelNetworkAdapter_instances, announceConnection_fn, _ready2, _readyResolver, _readyPromise2, _WebSocketClientAdapter_instances, forceReady_fn, _retryIntervalId, _log5;
import { E as EventEmitter, d as debug, a as isRepoMessage, b as isEphemeralMessage, s as sha256Exports, v as v4, l as loadIncremental, c as init, e as stats, g as getHeads, f as saveSince, h as save, j as decodeSyncState, k as encodeSyncState, m as headsAreSame, n as encodeHeads, C as CollectionSynchronizer, t as throttle, D as DocHandle, o as from, p as emptyChange, q as parseAutomergeUrl, r as generateAutomergeUrl, u as binaryToDocumentId, w as clone, i as isValidAutomergeUrl, x as interpretAsDocumentId, U as UNAVAILABLE, y as DELETED, R as READY, z as abortable, A as AbortError, B as load, F as UNLOADED, G as getAugmentedNamespace, H as getDefaultExportFromCjs, I as encode, J as decode } from "./fullfat_node-75TjwUrn.js";
import require$$0$3 from "events";
import require$$1$1 from "https";
import require$$2$1 from "http";
import require$$3 from "net";
import require$$4 from "tls";
import crypto from "crypto";
import require$$0$2 from "stream";
import require$$7 from "url";
import require$$0 from "zlib";
import require$$0$1 from "buffer";
import "tty";
import "util";
import "os";
import "node:crypto";
import "module";
import "fs";
class RemoteHeadsSubscriptions extends EventEmitter {
  constructor() {
    super(...arguments);
    __privateAdd(this, _RemoteHeadsSubscriptions_instances);
    // Last known heads and timestamp for each storageId that we know about
    __privateAdd(this, _syncInfoByDocId, /* @__PURE__ */ new Map());
    // Storage IDs we have subscribed to via Repo.subscribeToRemoteHeads
    __privateAdd(this, _ourSubscriptions, /* @__PURE__ */ new Set());
    // Storage IDs other peers have subscribed to by sending us a control message
    __privateAdd(this, _theirSubscriptions, /* @__PURE__ */ new Map());
    // Peers we will always share remote heads with even if they are not subscribed
    __privateAdd(this, _generousPeers, /* @__PURE__ */ new Set());
    // Documents each peer has open, we need this information so we only send remote heads of documents that the peer knows
    __privateAdd(this, _subscribedDocsByPeer, /* @__PURE__ */ new Map());
    __privateAdd(this, _log, debug("automerge-repo:remote-heads-subscriptions"));
  }
  subscribeToRemotes(remotes) {
    __privateGet(this, _log).call(this, "subscribeToRemotes", remotes);
    const remotesToAdd = [];
    for (const remote of remotes) {
      if (!__privateGet(this, _ourSubscriptions).has(remote)) {
        __privateGet(this, _ourSubscriptions).add(remote);
        remotesToAdd.push(remote);
      }
    }
    if (remotesToAdd.length > 0) {
      this.emit("change-remote-subs", {
        add: remotesToAdd,
        peers: Array.from(__privateGet(this, _generousPeers))
      });
    }
  }
  unsubscribeFromRemotes(remotes) {
    __privateGet(this, _log).call(this, "subscribeToRemotes", remotes);
    const remotesToRemove = [];
    for (const remote of remotes) {
      if (__privateGet(this, _ourSubscriptions).has(remote)) {
        __privateGet(this, _ourSubscriptions).delete(remote);
        if (!__privateGet(this, _theirSubscriptions).has(remote)) {
          remotesToRemove.push(remote);
        }
      }
    }
    if (remotesToRemove.length > 0) {
      this.emit("change-remote-subs", {
        remove: remotesToRemove,
        peers: Array.from(__privateGet(this, _generousPeers))
      });
    }
  }
  handleControlMessage(control) {
    const remotesToAdd = [];
    const remotesToRemove = [];
    const addedRemotesWeKnow = [];
    __privateGet(this, _log).call(this, "handleControlMessage", control);
    if (control.add) {
      for (const remote of control.add) {
        let theirSubs = __privateGet(this, _theirSubscriptions).get(remote);
        if (__privateGet(this, _ourSubscriptions).has(remote) || theirSubs) {
          addedRemotesWeKnow.push(remote);
        }
        if (!theirSubs) {
          theirSubs = /* @__PURE__ */ new Set();
          __privateGet(this, _theirSubscriptions).set(remote, theirSubs);
          if (!__privateGet(this, _ourSubscriptions).has(remote)) {
            remotesToAdd.push(remote);
          }
        }
        theirSubs.add(control.senderId);
      }
    }
    if (control.remove) {
      for (const remote of control.remove) {
        const theirSubs = __privateGet(this, _theirSubscriptions).get(remote);
        if (theirSubs) {
          theirSubs.delete(control.senderId);
          if (theirSubs.size == 0 && !__privateGet(this, _ourSubscriptions).has(remote)) {
            remotesToRemove.push(remote);
          }
        }
      }
    }
    if (remotesToAdd.length > 0 || remotesToRemove.length > 0) {
      this.emit("change-remote-subs", {
        peers: Array.from(__privateGet(this, _generousPeers)),
        add: remotesToAdd,
        remove: remotesToRemove
      });
    }
    for (const remote of addedRemotesWeKnow) {
      const subscribedDocs = __privateGet(this, _subscribedDocsByPeer).get(control.senderId);
      if (subscribedDocs) {
        for (const documentId of subscribedDocs) {
          const syncInfo = __privateGet(this, _syncInfoByDocId).get(documentId);
          if (!syncInfo) {
            continue;
          }
          const syncInfoForRemote = syncInfo.get(remote);
          if (syncInfoForRemote) {
            this.emit("notify-remote-heads", {
              targetId: control.senderId,
              documentId,
              heads: syncInfoForRemote.lastHeads,
              timestamp: syncInfoForRemote.lastSyncTimestamp,
              storageId: remote
            });
          }
        }
      }
    }
  }
  /** A peer we are not directly connected to has changed their heads */
  handleRemoteHeads(msg) {
    __privateGet(this, _log).call(this, "handleRemoteHeads", msg);
    const changedHeads = __privateMethod(this, _RemoteHeadsSubscriptions_instances, changedHeads_fn).call(this, msg);
    for (const event of changedHeads) {
      if (__privateGet(this, _ourSubscriptions).has(event.storageId)) {
        this.emit("remote-heads-changed", event);
      }
    }
    for (const event of changedHeads) {
      for (const peer of __privateGet(this, _generousPeers)) {
        if (peer === msg.senderId) {
          continue;
        }
        this.emit("notify-remote-heads", {
          targetId: peer,
          documentId: event.documentId,
          heads: event.remoteHeads,
          timestamp: event.timestamp,
          storageId: event.storageId
        });
      }
    }
    for (const event of changedHeads) {
      const theirSubs = __privateGet(this, _theirSubscriptions).get(event.storageId);
      if (theirSubs) {
        for (const peerId of theirSubs) {
          if (__privateMethod(this, _RemoteHeadsSubscriptions_instances, isPeerSubscribedToDoc_fn).call(this, peerId, event.documentId)) {
            this.emit("notify-remote-heads", {
              targetId: peerId,
              documentId: event.documentId,
              heads: event.remoteHeads,
              timestamp: event.timestamp,
              storageId: event.storageId
            });
          }
        }
      }
    }
  }
  /** A peer we are directly connected to has updated their heads */
  handleImmediateRemoteHeadsChanged(documentId, storageId, heads) {
    __privateGet(this, _log).call(this, "handleLocalHeadsChanged", documentId, storageId, heads);
    const remote = __privateGet(this, _syncInfoByDocId).get(documentId);
    const timestamp = Date.now();
    if (!remote) {
      __privateGet(this, _syncInfoByDocId).set(documentId, /* @__PURE__ */ new Map([
        [storageId, { lastSyncTimestamp: timestamp, lastHeads: heads }]
      ]));
    } else {
      const docRemote = remote.get(storageId);
      if (!docRemote || docRemote.lastSyncTimestamp < Date.now()) {
        remote.set(storageId, {
          lastSyncTimestamp: Date.now(),
          lastHeads: heads
        });
      }
    }
    const theirSubs = __privateGet(this, _theirSubscriptions).get(storageId);
    if (theirSubs) {
      for (const peerId of theirSubs) {
        if (__privateMethod(this, _RemoteHeadsSubscriptions_instances, isPeerSubscribedToDoc_fn).call(this, peerId, documentId)) {
          this.emit("notify-remote-heads", {
            targetId: peerId,
            documentId,
            heads,
            timestamp,
            storageId
          });
        }
      }
    }
  }
  addGenerousPeer(peerId) {
    __privateGet(this, _log).call(this, "addGenerousPeer", peerId);
    __privateGet(this, _generousPeers).add(peerId);
    if (__privateGet(this, _ourSubscriptions).size > 0) {
      this.emit("change-remote-subs", {
        add: Array.from(__privateGet(this, _ourSubscriptions)),
        peers: [peerId]
      });
    }
    for (const [documentId, remote] of __privateGet(this, _syncInfoByDocId)) {
      for (const [storageId, { lastHeads, lastSyncTimestamp }] of remote) {
        this.emit("notify-remote-heads", {
          targetId: peerId,
          documentId,
          heads: lastHeads,
          timestamp: lastSyncTimestamp,
          storageId
        });
      }
    }
  }
  removePeer(peerId) {
    __privateGet(this, _log).call(this, "removePeer", peerId);
    const remotesToRemove = [];
    __privateGet(this, _generousPeers).delete(peerId);
    __privateGet(this, _subscribedDocsByPeer).delete(peerId);
    for (const [storageId, peerIds] of __privateGet(this, _theirSubscriptions)) {
      if (peerIds.has(peerId)) {
        peerIds.delete(peerId);
        if (peerIds.size == 0) {
          remotesToRemove.push(storageId);
          __privateGet(this, _theirSubscriptions).delete(storageId);
        }
      }
    }
    if (remotesToRemove.length > 0) {
      this.emit("change-remote-subs", {
        remove: remotesToRemove,
        peers: Array.from(__privateGet(this, _generousPeers))
      });
    }
  }
  subscribePeerToDoc(peerId, documentId) {
    let subscribedDocs = __privateGet(this, _subscribedDocsByPeer).get(peerId);
    if (!subscribedDocs) {
      subscribedDocs = /* @__PURE__ */ new Set();
      __privateGet(this, _subscribedDocsByPeer).set(peerId, subscribedDocs);
    }
    subscribedDocs.add(documentId);
    const remoteHeads = __privateGet(this, _syncInfoByDocId).get(documentId);
    if (remoteHeads) {
      for (const [storageId, lastHeads] of remoteHeads) {
        const subscribedPeers = __privateGet(this, _theirSubscriptions).get(storageId);
        if (subscribedPeers && subscribedPeers.has(peerId)) {
          this.emit("notify-remote-heads", {
            targetId: peerId,
            documentId,
            heads: lastHeads.lastHeads,
            timestamp: lastHeads.lastSyncTimestamp,
            storageId
          });
        }
      }
    }
  }
}
_syncInfoByDocId = new WeakMap();
_ourSubscriptions = new WeakMap();
_theirSubscriptions = new WeakMap();
_generousPeers = new WeakMap();
_subscribedDocsByPeer = new WeakMap();
_log = new WeakMap();
_RemoteHeadsSubscriptions_instances = new WeakSet();
isPeerSubscribedToDoc_fn = function(peerId, documentId) {
  const subscribedDocs = __privateGet(this, _subscribedDocsByPeer).get(peerId);
  return subscribedDocs && subscribedDocs.has(documentId);
};
/** Returns the (document, storageId) pairs which have changed after processing msg */
changedHeads_fn = function(msg) {
  const changedHeads = [];
  const { documentId, newHeads } = msg;
  for (const [storageId, { heads, timestamp }] of Object.entries(newHeads)) {
    if (!__privateGet(this, _ourSubscriptions).has(storageId) && !__privateGet(this, _theirSubscriptions).has(storageId)) {
      continue;
    }
    let remote = __privateGet(this, _syncInfoByDocId).get(documentId);
    if (!remote) {
      remote = /* @__PURE__ */ new Map();
      __privateGet(this, _syncInfoByDocId).set(documentId, remote);
    }
    const docRemote = remote.get(storageId);
    if (docRemote && docRemote.lastSyncTimestamp >= timestamp) {
      continue;
    } else {
      remote.set(storageId, {
        lastSyncTimestamp: timestamp,
        lastHeads: heads
      });
      changedHeads.push({
        documentId,
        storageId,
        remoteHeads: heads,
        timestamp
      });
    }
  }
  return changedHeads;
};
const getEphemeralMessageSource = (message) => `${message.senderId}:${message.sessionId}`;
class NetworkSubsystem extends EventEmitter {
  constructor(adapters, peerId, peerMetadata) {
    super();
    __publicField(this, "peerId");
    __publicField(this, "peerMetadata");
    __privateAdd(this, _log2);
    __privateAdd(this, _adaptersByPeer, {});
    __privateAdd(this, _count, 0);
    __privateAdd(this, _sessionId, Math.random().toString(36).slice(2));
    __privateAdd(this, _ephemeralSessionCounts, {});
    __publicField(this, "adapters", []);
    __publicField(this, "isReady", () => {
      return this.adapters.every((a) => a.isReady());
    });
    __publicField(this, "whenReady", async () => {
      return Promise.all(this.adapters.map((a) => a.whenReady()));
    });
    this.peerId = peerId;
    this.peerMetadata = peerMetadata;
    __privateSet(this, _log2, debug(`automerge-repo:network:${this.peerId}`));
    adapters.forEach((a) => this.addNetworkAdapter(a));
  }
  disconnect() {
    this.adapters.forEach((a) => a.disconnect());
  }
  reconnect() {
    this.adapters.forEach((a) => a.connect(this.peerId));
  }
  addNetworkAdapter(networkAdapter) {
    this.adapters.push(networkAdapter);
    networkAdapter.on("peer-candidate", ({ peerId, peerMetadata }) => {
      __privateGet(this, _log2).call(this, `peer candidate: ${peerId} `);
      if (!__privateGet(this, _adaptersByPeer)[peerId]) {
        __privateGet(this, _adaptersByPeer)[peerId] = networkAdapter;
      }
      this.emit("peer", { peerId, peerMetadata });
    });
    networkAdapter.on("peer-disconnected", ({ peerId }) => {
      __privateGet(this, _log2).call(this, `peer disconnected: ${peerId} `);
      delete __privateGet(this, _adaptersByPeer)[peerId];
      this.emit("peer-disconnected", { peerId });
    });
    networkAdapter.on("message", (msg) => {
      if (!isRepoMessage(msg)) {
        __privateGet(this, _log2).call(this, `invalid message: ${JSON.stringify(msg)}`);
        return;
      }
      __privateGet(this, _log2).call(this, `message from ${msg.senderId}`);
      if (isEphemeralMessage(msg)) {
        const source = getEphemeralMessageSource(msg);
        if (__privateGet(this, _ephemeralSessionCounts)[source] === void 0 || msg.count > __privateGet(this, _ephemeralSessionCounts)[source]) {
          __privateGet(this, _ephemeralSessionCounts)[source] = msg.count;
          this.emit("message", msg);
        }
        return;
      }
      this.emit("message", msg);
    });
    networkAdapter.on("close", () => {
      __privateGet(this, _log2).call(this, "adapter closed");
      Object.entries(__privateGet(this, _adaptersByPeer)).forEach(([peerId, other]) => {
        if (other === networkAdapter) {
          delete __privateGet(this, _adaptersByPeer)[peerId];
        }
      });
      this.adapters = this.adapters.filter((a) => a !== networkAdapter);
    });
    this.peerMetadata.then((peerMetadata) => {
      networkAdapter.connect(this.peerId, peerMetadata);
    }).catch((err) => {
      __privateGet(this, _log2).call(this, "error connecting to network", err);
    });
  }
  // TODO: this probably introduces a race condition for the ready event
  // but I plan to refactor that as part of this branch in another patch
  removeNetworkAdapter(networkAdapter) {
    this.adapters = this.adapters.filter((a) => a !== networkAdapter);
    networkAdapter.disconnect();
  }
  send(message) {
    const peer = __privateGet(this, _adaptersByPeer)[message.targetId];
    if (!peer) {
      __privateGet(this, _log2).call(this, `Tried to send message but peer not found: ${message.targetId}`);
      return;
    }
    const prepareMessage = (message2) => {
      if (message2.type === "ephemeral") {
        if ("count" in message2) {
          return message2;
        } else {
          return {
            ...message2,
            count: ++__privateWrapper(this, _count)._,
            sessionId: __privateGet(this, _sessionId),
            senderId: this.peerId
          };
        }
      } else {
        return {
          ...message2,
          senderId: this.peerId
        };
      }
    };
    const outbound = prepareMessage(message);
    __privateGet(this, _log2).call(this, "sending message %o", outbound);
    peer.send(outbound);
  }
}
_log2 = new WeakMap();
_adaptersByPeer = new WeakMap();
_count = new WeakMap();
_sessionId = new WeakMap();
_ephemeralSessionCounts = new WeakMap();
function mergeArrays(myArrays) {
  let length = 0;
  myArrays.forEach((item) => {
    length += item.length;
  });
  const mergedArray = new Uint8Array(length);
  let offset = 0;
  myArrays.forEach((item) => {
    mergedArray.set(item, offset);
    offset += item.length;
  });
  return mergedArray;
}
function keyHash(binary) {
  const hash = sha256Exports.hash(binary);
  return bufferToHexString(hash);
}
function headsHash(heads) {
  const encoder = new TextEncoder();
  const headsbinary = mergeArrays(heads.map((h) => encoder.encode(h)));
  return keyHash(headsbinary);
}
function bufferToHexString(data) {
  return Array.from(data, (byte) => byte.toString(16).padStart(2, "0")).join("");
}
class StorageSubsystem extends EventEmitter {
  constructor(storageAdapter) {
    super();
    __privateAdd(this, _StorageSubsystem_instances);
    /** The storage adapter to use for saving and loading documents */
    __privateAdd(this, _storageAdapter);
    /** Record of the latest heads we've loaded or saved for each document  */
    __privateAdd(this, _storedHeads, /* @__PURE__ */ new Map());
    /** Metadata on the chunks we've already loaded for each document */
    __privateAdd(this, _chunkInfos, /* @__PURE__ */ new Map());
    /** Flag to avoid compacting when a compaction is already underway */
    __privateAdd(this, _compacting, false);
    __privateAdd(this, _log3, debug(`automerge-repo:storage-subsystem`));
    __privateSet(this, _storageAdapter, storageAdapter);
  }
  async id() {
    const storedId = await __privateGet(this, _storageAdapter).load(["storage-adapter-id"]);
    let id;
    if (storedId) {
      id = new TextDecoder().decode(storedId);
    } else {
      id = v4();
      await __privateGet(this, _storageAdapter).save(["storage-adapter-id"], new TextEncoder().encode(id));
    }
    return id;
  }
  // ARBITRARY KEY/VALUE STORAGE
  // The `load`, `save`, and `remove` methods are for generic key/value storage, as opposed to
  // Automerge documents. For example, they're used by the LocalFirstAuthProvider to persist the
  // encrypted team graph that encodes group membership and permissions.
  //
  // The namespace parameter is to prevent collisions with other users of the storage subsystem.
  // Typically this will be the name of the plug-in, adapter, or other system that is using it. For
  // example, the LocalFirstAuthProvider uses the namespace `LocalFirstAuthProvider`.
  /** Loads a value from storage. */
  async load(namespace, key) {
    const storageKey = [namespace, key];
    return await __privateGet(this, _storageAdapter).load(storageKey);
  }
  /** Saves a value in storage. */
  async save(namespace, key, data) {
    const storageKey = [namespace, key];
    await __privateGet(this, _storageAdapter).save(storageKey, data);
  }
  /** Removes a value from storage. */
  async remove(namespace, key) {
    const storageKey = [namespace, key];
    await __privateGet(this, _storageAdapter).remove(storageKey);
  }
  // AUTOMERGE DOCUMENT STORAGE
  /**
   * Loads and combines document chunks from storage, with snapshots first.
   */
  async loadDocData(documentId) {
    const snapshotChunks = await __privateGet(this, _storageAdapter).loadRange([
      documentId,
      "snapshot"
    ]);
    const incrementalChunks = await __privateGet(this, _storageAdapter).loadRange([
      documentId,
      "incremental"
    ]);
    const binaries = [];
    const chunkInfos = [];
    for (const chunk of snapshotChunks) {
      if (chunk.data === void 0)
        continue;
      chunkInfos.push({
        key: chunk.key,
        type: "snapshot",
        size: chunk.data.length
      });
      binaries.push(chunk.data);
    }
    for (const chunk of incrementalChunks) {
      if (chunk.data === void 0)
        continue;
      chunkInfos.push({
        key: chunk.key,
        type: "incremental",
        size: chunk.data.length
      });
      binaries.push(chunk.data);
    }
    __privateGet(this, _chunkInfos).set(documentId, chunkInfos);
    if (binaries.length === 0) {
      return null;
    }
    return mergeArrays(binaries);
  }
  /**
   * Loads the Automerge document with the given ID from storage.
   */
  async loadDoc(documentId) {
    const binary = await this.loadDocData(documentId);
    if (!binary)
      return null;
    const start = performance.now();
    const newDoc = loadIncremental(init(), binary);
    const end = performance.now();
    this.emit("document-loaded", {
      documentId,
      durationMillis: end - start,
      ...stats(newDoc)
    });
    __privateGet(this, _storedHeads).set(documentId, getHeads(newDoc));
    return newDoc;
  }
  /**
   * Saves the provided Automerge document to storage.
   *
   * @remarks
   * Under the hood this makes incremental saves until the incremental size is greater than the
   * snapshot size, at which point the document is compacted into a single snapshot.
   */
  async saveDoc(documentId, doc) {
    if (!__privateMethod(this, _StorageSubsystem_instances, shouldSave_fn).call(this, documentId, doc))
      return;
    const sourceChunks = __privateGet(this, _chunkInfos).get(documentId) ?? [];
    if (__privateMethod(this, _StorageSubsystem_instances, shouldCompact_fn).call(this, sourceChunks)) {
      await __privateMethod(this, _StorageSubsystem_instances, saveTotal_fn).call(this, documentId, doc, sourceChunks);
    } else {
      await __privateMethod(this, _StorageSubsystem_instances, saveIncremental_fn).call(this, documentId, doc);
    }
    __privateGet(this, _storedHeads).set(documentId, getHeads(doc));
  }
  /**
   * Removes the Automerge document with the given ID from storage
   */
  async removeDoc(documentId) {
    await __privateGet(this, _storageAdapter).removeRange([documentId, "snapshot"]);
    await __privateGet(this, _storageAdapter).removeRange([documentId, "incremental"]);
    await __privateGet(this, _storageAdapter).removeRange([documentId, "sync-state"]);
  }
  async loadSyncState(documentId, storageId) {
    const key = [documentId, "sync-state", storageId];
    try {
      const loaded = await __privateGet(this, _storageAdapter).load(key);
      return loaded ? decodeSyncState(loaded) : void 0;
    } catch (e) {
      __privateGet(this, _log3).call(this, `Error loading sync state for ${documentId} from ${storageId}`);
      return void 0;
    }
  }
  async saveSyncState(documentId, storageId, syncState) {
    const key = [documentId, "sync-state", storageId];
    await __privateGet(this, _storageAdapter).save(key, encodeSyncState(syncState));
  }
}
_storageAdapter = new WeakMap();
_storedHeads = new WeakMap();
_chunkInfos = new WeakMap();
_compacting = new WeakMap();
_log3 = new WeakMap();
_StorageSubsystem_instances = new WeakSet();
saveIncremental_fn = async function(documentId, doc) {
  const sinceHeads = __privateGet(this, _storedHeads).get(documentId) ?? [];
  const start = performance.now();
  const binary = saveSince(doc, sinceHeads);
  const end = performance.now();
  this.emit("doc-saved", {
    documentId,
    durationMillis: end - start,
    sinceHeads
  });
  if (binary && binary.length > 0) {
    const key = [documentId, "incremental", keyHash(binary)];
    __privateGet(this, _log3).call(this, `Saving incremental ${key} for document ${documentId}`);
    await __privateGet(this, _storageAdapter).save(key, binary);
    if (!__privateGet(this, _chunkInfos).has(documentId)) {
      __privateGet(this, _chunkInfos).set(documentId, []);
    }
    __privateGet(this, _chunkInfos).get(documentId).push({
      key,
      type: "incremental",
      size: binary.length
    });
    __privateGet(this, _storedHeads).set(documentId, getHeads(doc));
  } else {
    return Promise.resolve();
  }
};
saveTotal_fn = async function(documentId, doc, sourceChunks) {
  var _a;
  __privateSet(this, _compacting, true);
  const start = performance.now();
  const binary = save(doc);
  const end = performance.now();
  this.emit("doc-compacted", { documentId, durationMillis: end - start });
  const snapshotHash = headsHash(getHeads(doc));
  const key = [documentId, "snapshot", snapshotHash];
  const oldKeys = new Set(sourceChunks.map((c) => c.key).filter((k) => k[2] !== snapshotHash));
  __privateGet(this, _log3).call(this, `Saving snapshot ${key} for document ${documentId}`);
  __privateGet(this, _log3).call(this, `deleting old chunks ${Array.from(oldKeys)}`);
  await __privateGet(this, _storageAdapter).save(key, binary);
  for (const key2 of oldKeys) {
    await __privateGet(this, _storageAdapter).remove(key2);
  }
  const newChunkInfos = ((_a = __privateGet(this, _chunkInfos).get(documentId)) == null ? void 0 : _a.filter((c) => !oldKeys.has(c.key))) ?? [];
  newChunkInfos.push({ key, type: "snapshot", size: binary.length });
  __privateGet(this, _chunkInfos).set(documentId, newChunkInfos);
  __privateSet(this, _compacting, false);
};
/**
 * Returns true if the document has changed since the last time it was saved.
 */
shouldSave_fn = function(documentId, doc) {
  const oldHeads = __privateGet(this, _storedHeads).get(documentId);
  if (!oldHeads) {
    return true;
  }
  const newHeads = getHeads(doc);
  if (headsAreSame(encodeHeads(newHeads), encodeHeads(oldHeads))) {
    return false;
  }
  return true;
};
/**
 * We only compact if the incremental size is greater than the snapshot size.
 */
shouldCompact_fn = function(sourceChunks) {
  if (__privateGet(this, _compacting))
    return false;
  let snapshotSize = 0;
  let incrementalSize = 0;
  for (const chunk of sourceChunks) {
    if (chunk.type === "snapshot") {
      snapshotSize += chunk.size;
    } else {
      incrementalSize += chunk.size;
    }
  }
  return snapshotSize < 1024 || incrementalSize >= snapshotSize;
};
function randomPeerId() {
  return "peer-" + Math.random().toString(36).slice(4);
}
class Repo extends EventEmitter {
  constructor({ storage, network = [], peerId = randomPeerId(), sharePolicy, shareConfig, isEphemeral = storage === void 0, enableRemoteHeadsGossiping = false, denylist = [], saveDebounceRate = 100, idFactory } = {}) {
    super();
    __privateAdd(this, _Repo_instances);
    __privateAdd(this, _log4);
    /** @hidden */
    __publicField(this, "networkSubsystem");
    /** @hidden */
    __publicField(this, "storageSubsystem");
    /** @hidden */
    __privateAdd(this, _saveDebounceRate);
    /** @hidden */
    __privateAdd(this, _saveFn);
    __privateAdd(this, _handleCache, {});
    /** @hidden */
    __publicField(this, "synchronizer");
    __privateAdd(this, _shareConfig, {
      announce: async () => true,
      access: async () => true
    });
    /** maps peer id to to persistence information (storageId, isEphemeral), access by collection synchronizer  */
    /** @hidden */
    __publicField(this, "peerMetadataByPeerId", {});
    __privateAdd(this, _remoteHeadsSubscriptions, new RemoteHeadsSubscriptions());
    __privateAdd(this, _remoteHeadsGossipingEnabled, false);
    __privateAdd(this, _progressCache, {});
    __privateAdd(this, _saveFns, {});
    __privateAdd(this, _idFactory);
    __privateAdd(this, _throttledSaveSyncStateHandlers, {});
    __publicField(this, "subscribeToRemotes", (remotes) => {
      if (__privateGet(this, _remoteHeadsGossipingEnabled)) {
        __privateGet(this, _log4).call(this, "subscribeToRemotes", { remotes });
        __privateGet(this, _remoteHeadsSubscriptions).subscribeToRemotes(remotes);
      } else {
        __privateGet(this, _log4).call(this, "WARN: subscribeToRemotes called but remote heads gossiping is not enabled");
      }
    });
    __publicField(this, "storageId", async () => {
      if (!this.storageSubsystem) {
        return void 0;
      } else {
        return this.storageSubsystem.id();
      }
    });
    __privateSet(this, _remoteHeadsGossipingEnabled, enableRemoteHeadsGossiping);
    __privateSet(this, _log4, debug(`automerge-repo:repo`));
    __privateSet(this, _idFactory, idFactory || null);
    if (sharePolicy != null && shareConfig != null) {
      throw new Error("cannot provide both sharePolicy and shareConfig at once");
    }
    if (sharePolicy) {
      __privateSet(this, _shareConfig, {
        announce: sharePolicy,
        access: async () => true
      });
    }
    if (shareConfig) {
      __privateSet(this, _shareConfig, shareConfig);
    }
    this.on("delete-document", ({ documentId }) => {
      this.synchronizer.removeDocument(documentId);
      if (storageSubsystem) {
        storageSubsystem.removeDoc(documentId).catch((err) => {
          __privateGet(this, _log4).call(this, "error deleting document", { documentId, err });
        });
      }
    });
    this.synchronizer = new CollectionSynchronizer(this, denylist);
    this.synchronizer.on("message", (message) => {
      __privateGet(this, _log4).call(this, `sending ${message.type} message to ${message.targetId}`);
      networkSubsystem.send(message);
    });
    this.synchronizer.on("metrics", (event) => this.emit("doc-metrics", event));
    if (__privateGet(this, _remoteHeadsGossipingEnabled)) {
      this.synchronizer.on("open-doc", ({ peerId: peerId2, documentId }) => {
        __privateGet(this, _remoteHeadsSubscriptions).subscribePeerToDoc(peerId2, documentId);
      });
    }
    const storageSubsystem = storage ? new StorageSubsystem(storage) : void 0;
    if (storageSubsystem) {
      storageSubsystem.on("document-loaded", (event) => this.emit("doc-metrics", { type: "doc-loaded", ...event }));
      storageSubsystem.on("doc-compacted", (event) => this.emit("doc-metrics", { type: "doc-compacted", ...event }));
      storageSubsystem.on("doc-saved", (event) => this.emit("doc-metrics", { type: "doc-saved", ...event }));
    }
    this.storageSubsystem = storageSubsystem;
    __privateSet(this, _saveDebounceRate, saveDebounceRate);
    if (this.storageSubsystem) {
      __privateSet(this, _saveFn, ({ handle, doc }) => {
        let fn = __privateGet(this, _saveFns)[handle.documentId];
        if (!fn) {
          fn = throttle(({ doc: doc2, handle: handle2 }) => {
            void this.storageSubsystem.saveDoc(handle2.documentId, doc2);
          }, __privateGet(this, _saveDebounceRate));
          __privateGet(this, _saveFns)[handle.documentId] = fn;
        }
        fn({ handle, doc });
      });
    } else {
      __privateSet(this, _saveFn, () => {
      });
    }
    const myPeerMetadata = (async () => ({
      storageId: await (storageSubsystem == null ? void 0 : storageSubsystem.id()),
      isEphemeral
    }))();
    const networkSubsystem = new NetworkSubsystem(network, peerId, myPeerMetadata);
    this.networkSubsystem = networkSubsystem;
    networkSubsystem.on("peer", async ({ peerId: peerId2, peerMetadata }) => {
      __privateGet(this, _log4).call(this, "peer connected", { peerId: peerId2 });
      if (peerMetadata) {
        this.peerMetadataByPeerId[peerId2] = { ...peerMetadata };
      }
      __privateGet(this, _shareConfig).announce(peerId2).then((shouldShare) => {
        if (shouldShare && __privateGet(this, _remoteHeadsGossipingEnabled)) {
          __privateGet(this, _remoteHeadsSubscriptions).addGenerousPeer(peerId2);
        }
      }).catch((err) => {
        console.log("error in share policy", { err });
      });
      this.synchronizer.addPeer(peerId2);
    });
    networkSubsystem.on("peer-disconnected", ({ peerId: peerId2 }) => {
      this.synchronizer.removePeer(peerId2);
      __privateGet(this, _remoteHeadsSubscriptions).removePeer(peerId2);
    });
    networkSubsystem.on("message", async (msg) => {
      __privateMethod(this, _Repo_instances, receiveMessage_fn).call(this, msg);
    });
    this.synchronizer.on("sync-state", (message) => {
      var _a;
      __privateMethod(this, _Repo_instances, saveSyncState_fn).call(this, message);
      const handle = __privateGet(this, _handleCache)[message.documentId];
      const { storageId } = this.peerMetadataByPeerId[message.peerId] || {};
      if (!storageId) {
        return;
      }
      const heads = (_a = handle.getSyncInfo(storageId)) == null ? void 0 : _a.lastHeads;
      const haveHeadsChanged = message.syncState.theirHeads && (!heads || !headsAreSame(heads, encodeHeads(message.syncState.theirHeads)));
      if (haveHeadsChanged && message.syncState.theirHeads) {
        handle.setSyncInfo(storageId, {
          lastHeads: encodeHeads(message.syncState.theirHeads),
          lastSyncTimestamp: Date.now()
        });
        if (storageId && __privateGet(this, _remoteHeadsGossipingEnabled)) {
          __privateGet(this, _remoteHeadsSubscriptions).handleImmediateRemoteHeadsChanged(message.documentId, storageId, encodeHeads(message.syncState.theirHeads));
        }
      }
    });
    if (__privateGet(this, _remoteHeadsGossipingEnabled)) {
      __privateGet(this, _remoteHeadsSubscriptions).on("notify-remote-heads", (message) => {
        this.networkSubsystem.send({
          type: "remote-heads-changed",
          targetId: message.targetId,
          documentId: message.documentId,
          newHeads: {
            [message.storageId]: {
              heads: message.heads,
              timestamp: message.timestamp
            }
          }
        });
      });
      __privateGet(this, _remoteHeadsSubscriptions).on("change-remote-subs", (message) => {
        __privateGet(this, _log4).call(this, "change-remote-subs", message);
        for (const peer of message.peers) {
          this.networkSubsystem.send({
            type: "remote-subscription-change",
            targetId: peer,
            add: message.add,
            remove: message.remove
          });
        }
      });
      __privateGet(this, _remoteHeadsSubscriptions).on("remote-heads-changed", ({ documentId, storageId, remoteHeads, timestamp }) => {
        const handle = __privateGet(this, _handleCache)[documentId];
        handle.setSyncInfo(storageId, {
          lastHeads: remoteHeads,
          lastSyncTimestamp: timestamp
        });
      });
    }
  }
  /** Returns all the handles we have cached. */
  get handles() {
    return __privateGet(this, _handleCache);
  }
  /** Returns a list of all connected peer ids */
  get peers() {
    return this.synchronizer.peers;
  }
  get peerId() {
    return this.networkSubsystem.peerId;
  }
  /** @hidden */
  get sharePolicy() {
    return __privateGet(this, _shareConfig).announce;
  }
  /** @hidden */
  set sharePolicy(policy) {
    __privateGet(this, _shareConfig).announce = policy;
  }
  /** @hidden */
  get shareConfig() {
    return __privateGet(this, _shareConfig);
  }
  /** @hidden */
  set shareConfig(config) {
    __privateSet(this, _shareConfig, config);
  }
  getStorageIdOfPeer(peerId) {
    var _a;
    return (_a = this.peerMetadataByPeerId[peerId]) == null ? void 0 : _a.storageId;
  }
  /**
   * Creates a new document and returns a handle to it. The initial value of the document is an
   * empty object `{}` unless an initial value is provided. Its documentId is generated by the
   * system. we emit a `document` event to advertise interest in the document.
   */
  create(initialValue) {
    let initialDoc;
    if (initialValue) {
      initialDoc = from(initialValue);
    } else {
      initialDoc = emptyChange(init());
    }
    const { documentId } = parseAutomergeUrl(generateAutomergeUrl());
    const handle = __privateMethod(this, _Repo_instances, getHandle_fn).call(this, {
      documentId
    });
    __privateMethod(this, _Repo_instances, registerHandleWithSubsystems_fn).call(this, handle);
    handle.update(() => {
      return initialDoc;
    });
    handle.doneLoading();
    return handle;
  }
  /**
   * Creates a new document and returns a handle to it. The initial value of the
   * document is an empty object `{}` unless an initial value is provided. The
   * main difference between this and Repo.create is that if an `idGenerator`
   * was provided at repo construction, that idGenerator will be used to
   * generate the document ID of the document returned by this method.
   *
   * This is a hidden, experimental API which is subject to change or removal without notice.
   * @hidden
   * @experimental
   */
  async create2(initialValue) {
    let initialDoc;
    if (initialValue) {
      initialDoc = from(initialValue);
    } else {
      initialDoc = emptyChange(init());
    }
    let { documentId } = parseAutomergeUrl(generateAutomergeUrl());
    if (__privateGet(this, _idFactory)) {
      const rawDocId = await __privateGet(this, _idFactory).call(this, getHeads(initialDoc));
      documentId = binaryToDocumentId(rawDocId);
    }
    const handle = __privateMethod(this, _Repo_instances, getHandle_fn).call(this, {
      documentId
    });
    __privateMethod(this, _Repo_instances, registerHandleWithSubsystems_fn).call(this, handle);
    handle.update(() => {
      return initialDoc;
    });
    handle.doneLoading();
    return handle;
  }
  /** Create a new DocHandle by cloning the history of an existing DocHandle.
   *
   * @param clonedHandle - The handle to clone
   *
   * @remarks This is a wrapper around the `clone` function in the Automerge library.
   * The new `DocHandle` will have a new URL but will share history with the original,
   * which means that changes made to the cloned handle can be sensibly merged back
   * into the original.
   *
   * Any peers this `Repo` is connected to for whom `sharePolicy` returns `true` will
   * be notified of the newly created DocHandle.
   *
   */
  clone(clonedHandle) {
    if (!clonedHandle.isReady()) {
      throw new Error(`Cloned handle is not yet in ready state.
        (Try await handle.whenReady() first.)`);
    }
    const sourceDoc = clonedHandle.doc();
    const handle = this.create();
    handle.update(() => {
      return clone(sourceDoc);
    });
    return handle;
  }
  findWithProgress(id, options = {}) {
    const { signal } = options;
    const { documentId, heads } = isValidAutomergeUrl(id) ? parseAutomergeUrl(id) : { documentId: interpretAsDocumentId(id), heads: void 0 };
    if (__privateGet(this, _handleCache)[documentId]) {
      const handle2 = __privateGet(this, _handleCache)[documentId];
      if (handle2.state === UNAVAILABLE) {
        const result2 = {
          state: "unavailable",
          error: new Error(`Document ${id} is unavailable`),
          handle: handle2
        };
        return result2;
      }
      if (handle2.state === DELETED) {
        const result2 = {
          state: "failed",
          error: new Error(`Document ${id} was deleted`),
          handle: handle2
        };
        return result2;
      }
      if (handle2.state === READY) {
        const result2 = {
          state: "ready",
          handle: heads ? handle2.view(heads) : handle2
        };
        return result2;
      }
    }
    const cachedProgress = __privateGet(this, _progressCache)[documentId];
    if (cachedProgress) {
      const handle2 = __privateGet(this, _handleCache)[documentId];
      if (handle2 && (handle2.state === READY || handle2.state === UNAVAILABLE || handle2.state === DELETED || handle2.state === "loading")) {
        return cachedProgress;
      }
    }
    const handle = __privateMethod(this, _Repo_instances, getHandle_fn).call(this, { documentId });
    const initial = {
      state: "loading",
      progress: 0,
      handle
    };
    const progressSignal = {
      subscribers: /* @__PURE__ */ new Set(),
      currentProgress: void 0,
      notify: (progress) => {
        progressSignal.currentProgress = progress;
        progressSignal.subscribers.forEach((callback) => callback(progress));
        __privateGet(this, _progressCache)[documentId] = progress;
      },
      peek: () => progressSignal.currentProgress || initial,
      subscribe: (callback) => {
        progressSignal.subscribers.add(callback);
        return () => progressSignal.subscribers.delete(callback);
      }
    };
    progressSignal.notify(initial);
    void __privateMethod(this, _Repo_instances, loadDocumentWithProgress_fn).call(this, id, documentId, handle, progressSignal, signal ? abortable(new Promise(() => {
    }), signal) : new Promise(() => {
    }));
    const result = {
      ...initial,
      peek: progressSignal.peek,
      subscribe: progressSignal.subscribe
    };
    __privateGet(this, _progressCache)[documentId] = result;
    return result;
  }
  async find(id, options = {}) {
    const { allowableStates = ["ready"], signal } = options;
    if (signal == null ? void 0 : signal.aborted) {
      throw new AbortError();
    }
    const progress = this.findWithProgress(id, { signal });
    if ("subscribe" in progress) {
      __privateMethod(this, _Repo_instances, registerHandleWithSubsystems_fn).call(this, progress.handle);
      return new Promise((resolve, reject) => {
        const unsubscribe = progress.subscribe((state) => {
          if (allowableStates.includes(state.handle.state)) {
            unsubscribe();
            resolve(state.handle);
          } else if (state.state === "unavailable") {
            unsubscribe();
            reject(new Error(`Document ${id} is unavailable`));
          } else if (state.state === "failed") {
            unsubscribe();
            reject(state.error);
          }
        });
      });
    } else {
      if (progress.handle.state === READY) {
        return progress.handle;
      }
      await progress.handle.whenReady([READY, UNAVAILABLE]);
      if (progress.handle.state === "unavailable" && !allowableStates.includes(UNAVAILABLE)) {
        throw new Error(`Document ${id} is unavailable`);
      }
      return progress.handle;
    }
  }
  /**
   * Retrieves a document by id. It gets data from the local system, but also emits a `document`
   * event to advertise interest in the document.
   */
  async findClassic(id, options = {}) {
    const documentId = interpretAsDocumentId(id);
    const { allowableStates, signal } = options;
    return abortable((async () => {
      const handle = await __privateMethod(this, _Repo_instances, loadDocument_fn).call(this, documentId);
      if (!allowableStates) {
        await handle.whenReady([READY, UNAVAILABLE]);
        if (handle.state === UNAVAILABLE && !(signal == null ? void 0 : signal.aborted)) {
          throw new Error(`Document ${id} is unavailable`);
        }
      }
      return handle;
    })(), signal);
  }
  delete(id) {
    const documentId = interpretAsDocumentId(id);
    const handle = __privateMethod(this, _Repo_instances, getHandle_fn).call(this, { documentId });
    handle.delete();
    delete __privateGet(this, _handleCache)[documentId];
    delete __privateGet(this, _progressCache)[documentId];
    delete __privateGet(this, _saveFns)[documentId];
    this.emit("delete-document", { documentId });
  }
  /**
   * Exports a document to a binary format.
   * @param id - The url or documentId of the handle to export
   *
   * @returns Promise<Uint8Array | undefined> - A Promise containing the binary document,
   * or undefined if the document is unavailable.
   */
  async export(id) {
    const documentId = interpretAsDocumentId(id);
    const handle = await this.find(documentId);
    const doc = handle.doc();
    return save(doc);
  }
  /**
   * Imports document binary into the repo.
   * @param binary - The binary to import
   * @param args - Optional argument specifying what document ID to import into,
   *              if at all possible avoid using this, see the remarks below
   *
   * @remarks
   * If no document ID is provided, a new document will be created. When
   * specifying the document ID it is important to ensure that two documents using
   * the same ID share the same history - i.e. don't create a document with the
   * same ID on unrelated processes that have never communicated with each
   * other. If you need to ship around a bunch of documents with their IDs
   * consider using the `automerge-repo-bundles` package which provides a
   * serialization format for documents and IDs and handles the boilerplate of
   * importing and exporting these bundles.
   */
  import(binary, args) {
    const docId = args == null ? void 0 : args.docId;
    if (docId != null) {
      const handle = __privateMethod(this, _Repo_instances, getHandle_fn).call(this, { documentId: docId });
      handle.update((doc) => {
        return loadIncremental(doc, binary);
      });
      __privateMethod(this, _Repo_instances, registerHandleWithSubsystems_fn).call(this, handle);
      return handle;
    } else {
      const doc = load(binary);
      const handle = this.create();
      handle.update(() => {
        return clone(doc);
      });
      return handle;
    }
  }
  /**
   * Writes Documents to a disk.
   * @hidden this API is experimental and may change.
   * @param documents - if provided, only writes the specified documents.
   * @returns Promise<void>
   */
  async flush(documents) {
    if (!this.storageSubsystem) {
      return;
    }
    const handles = documents ? documents.map((id) => __privateGet(this, _handleCache)[id]) : Object.values(__privateGet(this, _handleCache));
    await Promise.all(handles.map(async (handle) => {
      return this.storageSubsystem.saveDoc(handle.documentId, handle.doc());
    }));
  }
  /**
   * Removes a DocHandle from the handleCache.
   * @hidden this API is experimental and may change.
   * @param documentId - documentId of the DocHandle to remove from handleCache, if present in cache.
   * @returns Promise<void>
   */
  async removeFromCache(documentId) {
    if (!__privateGet(this, _handleCache)[documentId]) {
      __privateGet(this, _log4).call(this, `WARN: removeFromCache called but handle not found in handleCache for documentId: ${documentId}`);
      return;
    }
    const handle = __privateMethod(this, _Repo_instances, getHandle_fn).call(this, { documentId });
    await handle.whenReady([READY, UNLOADED, DELETED, UNAVAILABLE]);
    const doc = handle.doc();
    if (doc) {
      if (handle.isReady()) {
        handle.unload();
      } else {
        __privateGet(this, _log4).call(this, `WARN: removeFromCache called but handle for documentId: ${documentId} in unexpected state: ${handle.state}`);
      }
      delete __privateGet(this, _handleCache)[documentId];
      delete __privateGet(this, _progressCache)[documentId];
      delete __privateGet(this, _saveFns)[documentId];
      this.synchronizer.removeDocument(documentId);
    } else {
      __privateGet(this, _log4).call(this, `WARN: removeFromCache called but doc undefined for documentId: ${documentId}`);
    }
  }
  shutdown() {
    this.networkSubsystem.adapters.forEach((adapter) => {
      adapter.disconnect();
    });
    return this.flush();
  }
  metrics() {
    return { documents: this.synchronizer.metrics() };
  }
  shareConfigChanged() {
    void this.synchronizer.reevaluateDocumentShare();
  }
}
_log4 = new WeakMap();
_saveDebounceRate = new WeakMap();
_saveFn = new WeakMap();
_handleCache = new WeakMap();
_shareConfig = new WeakMap();
_remoteHeadsSubscriptions = new WeakMap();
_remoteHeadsGossipingEnabled = new WeakMap();
_progressCache = new WeakMap();
_saveFns = new WeakMap();
_idFactory = new WeakMap();
_Repo_instances = new WeakSet();
// The `document` event is fired by the DocCollection any time we create a new document or look
// up a document by ID. We listen for it in order to wire up storage and network synchronization.
registerHandleWithSubsystems_fn = function(handle) {
  if (this.storageSubsystem) {
    const existingListeners = handle.listeners("heads-changed");
    if (!existingListeners.some((listener) => listener === __privateGet(this, _saveFn))) {
      handle.on("heads-changed", __privateGet(this, _saveFn));
    }
  }
  this.synchronizer.addDocument(handle);
};
receiveMessage_fn = function(message) {
  switch (message.type) {
    case "remote-subscription-change":
      if (__privateGet(this, _remoteHeadsGossipingEnabled)) {
        __privateGet(this, _remoteHeadsSubscriptions).handleControlMessage(message);
      }
      break;
    case "remote-heads-changed":
      if (__privateGet(this, _remoteHeadsGossipingEnabled)) {
        __privateGet(this, _remoteHeadsSubscriptions).handleRemoteHeads(message);
      }
      break;
    case "sync":
    case "request":
    case "ephemeral":
    case "doc-unavailable":
      this.synchronizer.receiveMessage(message).catch((err) => {
        console.log("error receiving message", { err, message });
      });
  }
};
_throttledSaveSyncStateHandlers = new WeakMap();
/** saves sync state throttled per storage id, if a peer doesn't have a storage id it's sync state is not persisted */
saveSyncState_fn = function(payload) {
  if (!this.storageSubsystem) {
    return;
  }
  const { storageId, isEphemeral } = this.peerMetadataByPeerId[payload.peerId] || {};
  if (!storageId || isEphemeral) {
    return;
  }
  let handler = __privateGet(this, _throttledSaveSyncStateHandlers)[storageId];
  if (!handler) {
    handler = __privateGet(this, _throttledSaveSyncStateHandlers)[storageId] = throttle(({ documentId, syncState }) => {
      void this.storageSubsystem.saveSyncState(documentId, storageId, syncState);
    }, __privateGet(this, _saveDebounceRate));
  }
  handler(payload);
};
/** Returns an existing handle if we have it; creates one otherwise. */
getHandle_fn = function({ documentId }) {
  if (__privateGet(this, _handleCache)[documentId])
    return __privateGet(this, _handleCache)[documentId];
  if (!documentId)
    throw new Error(`Invalid documentId ${documentId}`);
  const handle = new DocHandle(documentId);
  __privateGet(this, _handleCache)[documentId] = handle;
  return handle;
};
loadDocumentWithProgress_fn = async function(id, documentId, handle, progressSignal, abortPromise) {
  try {
    progressSignal.notify({
      state: "loading",
      progress: 25,
      handle
    });
    const loadingPromise = await (this.storageSubsystem ? this.storageSubsystem.loadDoc(handle.documentId) : Promise.resolve(null));
    const loadedDoc = await Promise.race([loadingPromise, abortPromise]);
    if (loadedDoc) {
      handle.update(() => loadedDoc);
      handle.doneLoading();
      progressSignal.notify({
        state: "loading",
        progress: 50,
        handle
      });
    } else {
      await Promise.race([this.networkSubsystem.whenReady(), abortPromise]);
      handle.request();
      progressSignal.notify({
        state: "loading",
        progress: 75,
        handle
      });
    }
    __privateMethod(this, _Repo_instances, registerHandleWithSubsystems_fn).call(this, handle);
    await Promise.race([handle.whenReady([READY, UNAVAILABLE]), abortPromise]);
    if (handle.state === UNAVAILABLE) {
      const unavailableProgress = {
        state: "unavailable",
        handle
      };
      progressSignal.notify(unavailableProgress);
      return;
    }
    if (handle.state === DELETED) {
      throw new Error(`Document ${id} was deleted`);
    }
    progressSignal.notify({ state: "ready", handle });
  } catch (error) {
    progressSignal.notify({
      state: "failed",
      error: (
        // In most JS environments DOMException extends Error, but not always, in some environments it's a separate type.
        // Some Node.js DOM polyfills do not always extend the Error
        // Jsdom polyfill doesn't extend Error, whereas happy-dom does.
        error instanceof Error || error instanceof DOMException ? error : new Error(String(error))
      ),
      handle: __privateMethod(this, _Repo_instances, getHandle_fn).call(this, { documentId })
    });
  }
};
loadDocument_fn = async function(documentId) {
  if (__privateGet(this, _handleCache)[documentId]) {
    return __privateGet(this, _handleCache)[documentId];
  }
  const handle = __privateMethod(this, _Repo_instances, getHandle_fn).call(this, { documentId });
  const loadedDoc = await (this.storageSubsystem ? this.storageSubsystem.loadDoc(handle.documentId) : Promise.resolve(null));
  if (loadedDoc) {
    handle.update(() => loadedDoc);
    handle.doneLoading();
  } else {
    await this.networkSubsystem.whenReady();
    handle.request();
  }
  __privateMethod(this, _Repo_instances, registerHandleWithSubsystems_fn).call(this, handle);
  return handle;
};
class NetworkAdapter extends EventEmitter {
  constructor() {
    super(...arguments);
    __publicField(this, "peerId");
    __publicField(this, "peerMetadata");
  }
}
class BroadcastChannelNetworkAdapter extends NetworkAdapter {
  constructor(options) {
    super();
    __privateAdd(this, _BroadcastChannelNetworkAdapter_instances);
    __privateAdd(this, _broadcastChannel);
    __privateAdd(this, _disconnected, false);
    __privateAdd(this, _ready, false);
    // reassigned in constructor, but keeps TS from complaining
    __privateAdd(this, _markReady, () => {
    });
    __privateAdd(this, _readyPromise);
    __privateAdd(this, _options);
    __privateAdd(this, _connectedPeers, []);
    __privateSet(this, _options, {
      channelName: "broadcast",
      peerWaitMs: 1e3,
      ...options ?? {}
    });
    __privateSet(this, _broadcastChannel, new BroadcastChannel(__privateGet(this, _options).channelName));
    __privateSet(this, _readyPromise, new Promise((resolve) => {
      __privateSet(this, _markReady, () => {
        __privateSet(this, _ready, true);
        resolve();
      });
      setTimeout(() => __privateGet(this, _markReady).call(this), __privateGet(this, _options).peerWaitMs);
    }));
  }
  isReady() {
    return __privateGet(this, _ready);
  }
  whenReady() {
    return __privateGet(this, _readyPromise);
  }
  connect(peerId, peerMetadata) {
    this.peerId = peerId;
    this.peerMetadata = peerMetadata;
    __privateSet(this, _disconnected, false);
    __privateGet(this, _broadcastChannel).addEventListener("message", (e) => {
      const message = e.data;
      if ("targetId" in message && message.targetId !== this.peerId) {
        return;
      }
      if (__privateGet(this, _disconnected)) {
        return;
      }
      const { senderId, type } = message;
      switch (type) {
        case "arrive":
          {
            const { peerMetadata: peerMetadata2 } = message;
            __privateGet(this, _broadcastChannel).postMessage({
              senderId: this.peerId,
              targetId: senderId,
              type: "welcome",
              peerMetadata: this.peerMetadata
            });
            __privateMethod(this, _BroadcastChannelNetworkAdapter_instances, announceConnection_fn).call(this, senderId, peerMetadata2);
          }
          break;
        case "welcome":
          {
            const { peerMetadata: peerMetadata2 } = message;
            __privateMethod(this, _BroadcastChannelNetworkAdapter_instances, announceConnection_fn).call(this, senderId, peerMetadata2);
          }
          break;
        case "leave":
          __privateSet(this, _connectedPeers, __privateGet(this, _connectedPeers).filter((p) => p !== senderId));
          this.emit("peer-disconnected", { peerId: senderId });
          break;
        default:
          if (!("data" in message)) {
            this.emit("message", message);
          } else {
            if (!message.data) {
              throw new Error("We got a message without data, we can't send this.");
            }
            const data = message.data;
            this.emit("message", {
              ...message,
              data: new Uint8Array(data)
            });
          }
          break;
      }
    });
    __privateGet(this, _broadcastChannel).postMessage({
      senderId: this.peerId,
      type: "arrive",
      peerMetadata
    });
  }
  send(message) {
    if (__privateGet(this, _disconnected)) {
      return false;
    }
    if ("data" in message) {
      __privateGet(this, _broadcastChannel).postMessage({
        ...message,
        data: message.data ? message.data.buffer.slice(message.data.byteOffset, message.data.byteOffset + message.data.byteLength) : void 0
      });
    } else {
      __privateGet(this, _broadcastChannel).postMessage(message);
    }
  }
  disconnect() {
    __privateGet(this, _broadcastChannel).postMessage({
      senderId: this.peerId,
      type: "leave"
    });
    for (const peerId of __privateGet(this, _connectedPeers)) {
      this.emit("peer-disconnected", { peerId });
    }
    __privateSet(this, _disconnected, true);
  }
}
_broadcastChannel = new WeakMap();
_disconnected = new WeakMap();
_ready = new WeakMap();
_markReady = new WeakMap();
_readyPromise = new WeakMap();
_options = new WeakMap();
_connectedPeers = new WeakMap();
_BroadcastChannelNetworkAdapter_instances = new WeakSet();
announceConnection_fn = function(peerId, peerMetadata) {
  __privateGet(this, _markReady).call(this);
  __privateGet(this, _connectedPeers).push(peerId);
  this.emit("peer-candidate", { peerId, peerMetadata });
};
class IndexedDBStorageAdapter {
  /** Create a new {@link IndexedDBStorageAdapter}.
   * @param database - The name of the database to use. Defaults to "automerge".
   * @param store - The name of the object store to use. Defaults to "documents".
   */
  constructor(database = "automerge", store = "documents") {
    __publicField(this, "database");
    __publicField(this, "store");
    __publicField(this, "dbPromise");
    this.database = database;
    this.store = store;
    this.dbPromise = this.createDatabasePromise();
  }
  createDatabasePromise() {
    return new Promise((resolve, reject) => {
      const request = indexedDB.open(this.database, 1);
      request.onerror = () => {
        reject(request.error);
      };
      request.onupgradeneeded = (event) => {
        const db = event.target.result;
        db.createObjectStore(this.store);
      };
      request.onsuccess = (event) => {
        const db = event.target.result;
        resolve(db);
      };
    });
  }
  async load(keyArray) {
    const db = await this.dbPromise;
    const transaction = db.transaction(this.store);
    const objectStore = transaction.objectStore(this.store);
    const request = objectStore.get(keyArray);
    return new Promise((resolve, reject) => {
      transaction.onerror = () => {
        reject(request.error);
      };
      request.onsuccess = (event) => {
        const result = event.target.result;
        if (result && typeof result === "object" && "binary" in result) {
          resolve(result.binary);
        } else {
          resolve(void 0);
        }
      };
    });
  }
  async save(keyArray, binary) {
    const db = await this.dbPromise;
    const transaction = db.transaction(this.store, "readwrite");
    const objectStore = transaction.objectStore(this.store);
    objectStore.put({ key: keyArray, binary }, keyArray);
    return new Promise((resolve, reject) => {
      transaction.onerror = () => {
        reject(transaction.error);
      };
      transaction.oncomplete = () => {
        resolve();
      };
    });
  }
  async remove(keyArray) {
    const db = await this.dbPromise;
    const transaction = db.transaction(this.store, "readwrite");
    const objectStore = transaction.objectStore(this.store);
    objectStore.delete(keyArray);
    return new Promise((resolve, reject) => {
      transaction.onerror = () => {
        reject(transaction.error);
      };
      transaction.oncomplete = () => {
        resolve();
      };
    });
  }
  async loadRange(keyPrefix) {
    const db = await this.dbPromise;
    const lowerBound = keyPrefix;
    const upperBound = [...keyPrefix, "\uFFFF"];
    const range = IDBKeyRange.bound(lowerBound, upperBound);
    const transaction = db.transaction(this.store);
    const objectStore = transaction.objectStore(this.store);
    const request = objectStore.openCursor(range);
    const result = [];
    return new Promise((resolve, reject) => {
      transaction.onerror = () => {
        reject(request.error);
      };
      request.onsuccess = (event) => {
        const cursor = event.target.result;
        if (cursor) {
          result.push({
            data: cursor.value.binary,
            key: cursor.key
          });
          cursor.continue();
        } else {
          resolve(result);
        }
      };
    });
  }
  async removeRange(keyPrefix) {
    const db = await this.dbPromise;
    const lowerBound = keyPrefix;
    const upperBound = [...keyPrefix, "\uFFFF"];
    const range = IDBKeyRange.bound(lowerBound, upperBound);
    const transaction = db.transaction(this.store, "readwrite");
    const objectStore = transaction.objectStore(this.store);
    objectStore.delete(range);
    return new Promise((resolve, reject) => {
      transaction.onerror = () => {
        reject(transaction.error);
      };
      transaction.oncomplete = () => {
        resolve();
      };
    });
  }
}
var bufferUtil = { exports: {} };
var constants;
var hasRequiredConstants;
function requireConstants() {
  if (hasRequiredConstants) return constants;
  hasRequiredConstants = 1;
  const BINARY_TYPES = ["nodebuffer", "arraybuffer", "fragments"];
  const hasBlob = typeof Blob !== "undefined";
  if (hasBlob) BINARY_TYPES.push("blob");
  constants = {
    BINARY_TYPES,
    CLOSE_TIMEOUT: 3e4,
    EMPTY_BUFFER: Buffer.alloc(0),
    GUID: "258EAFA5-E914-47DA-95CA-C5AB0DC85B11",
    hasBlob,
    kForOnEventAttribute: /* @__PURE__ */ Symbol("kIsForOnEventAttribute"),
    kListener: /* @__PURE__ */ Symbol("kListener"),
    kStatusCode: /* @__PURE__ */ Symbol("status-code"),
    kWebSocket: /* @__PURE__ */ Symbol("websocket"),
    NOOP: () => {
    }
  };
  return constants;
}
const __viteOptionalPeerDep_bufferutil_ws_true = {};
const __viteOptionalPeerDep_bufferutil_ws_true$1 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
  __proto__: null,
  default: __viteOptionalPeerDep_bufferutil_ws_true
}, Symbol.toStringTag, { value: "Module" }));
const require$$1 = /* @__PURE__ */ getAugmentedNamespace(__viteOptionalPeerDep_bufferutil_ws_true$1);
var hasRequiredBufferUtil;
function requireBufferUtil() {
  if (hasRequiredBufferUtil) return bufferUtil.exports;
  hasRequiredBufferUtil = 1;
  const { EMPTY_BUFFER } = requireConstants();
  const FastBuffer = Buffer[Symbol.species];
  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;
  }
  function _mask(source, mask, output, offset, length) {
    for (let i = 0; i < length; i++) {
      output[offset + i] = source[i] ^ mask[i & 3];
    }
  }
  function _unmask(buffer, mask) {
    for (let i = 0; i < buffer.length; i++) {
      buffer[i] ^= mask[i & 3];
    }
  }
  function toArrayBuffer2(buf) {
    if (buf.length === buf.buffer.byteLength) {
      return buf.buffer;
    }
    return buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.length);
  }
  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;
  }
  bufferUtil.exports = {
    concat,
    mask: _mask,
    toArrayBuffer: toArrayBuffer2,
    toBuffer,
    unmask: _unmask
  };
  if (!process.env.WS_NO_BUFFER_UTIL) {
    try {
      const bufferUtil$1 = require$$1;
      bufferUtil.exports.mask = function(source, mask, output, offset, length) {
        if (length < 48) _mask(source, mask, output, offset, length);
        else bufferUtil$1.mask(source, mask, output, offset, length);
      };
      bufferUtil.exports.unmask = function(buffer, mask) {
        if (buffer.length < 32) _unmask(buffer, mask);
        else bufferUtil$1.unmask(buffer, mask);
      };
    } catch (e) {
    }
  }
  return bufferUtil.exports;
}
var limiter;
var hasRequiredLimiter;
function requireLimiter() {
  if (hasRequiredLimiter) return limiter;
  hasRequiredLimiter = 1;
  const kDone = /* @__PURE__ */ Symbol("kDone");
  const kRun = /* @__PURE__ */ Symbol("kRun");
  class Limiter {
    /**
     * 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]);
      }
    }
  }
  limiter = Limiter;
  return limiter;
}
var permessageDeflate;
var hasRequiredPermessageDeflate;
function requirePermessageDeflate() {
  if (hasRequiredPermessageDeflate) return permessageDeflate;
  hasRequiredPermessageDeflate = 1;
  const zlib = require$$0;
  const bufferUtil2 = requireBufferUtil();
  const Limiter = requireLimiter();
  const { kStatusCode } = requireConstants();
  const FastBuffer = Buffer[Symbol.species];
  const TRAILER = Buffer.from([0, 0, 255, 255]);
  const kPerMessageDeflate = /* @__PURE__ */ Symbol("permessage-deflate");
  const kTotalLength = /* @__PURE__ */ Symbol("total-length");
  const kCallback = /* @__PURE__ */ Symbol("callback");
  const kBuffers = /* @__PURE__ */ Symbol("buffers");
  const kError = /* @__PURE__ */ Symbol("error");
  let zlibLimiter;
  class PerMessageDeflate {
    /**
     * 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) {
        const concurrency = this._options.concurrencyLimit !== void 0 ? this._options.concurrencyLimit : 10;
        zlibLimiter = new Limiter(concurrency);
      }
    }
    /**
     * @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(
            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 data2 = bufferUtil2.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, data2);
      });
    }
    /**
     * 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 data2 = bufferUtil2.concat(
          this._deflate[kBuffers],
          this._deflate[kTotalLength]
        );
        if (fin) {
          data2 = new FastBuffer(data2.buffer, data2.byteOffset, data2.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, data2);
      });
    }
  }
  permessageDeflate = PerMessageDeflate;
  function deflateOnData(chunk) {
    this[kBuffers].push(chunk);
    this[kTotalLength] += chunk.length;
  }
  function inflateOnData(chunk) {
    this[kTotalLength] += chunk.length;
    if (this[kPerMessageDeflate]._maxPayload < 1 || this[kTotalLength] <= this[kPerMessageDeflate]._maxPayload) {
      this[kBuffers].push(chunk);
      return;
    }
    this[kError] = new RangeError("Max payload size exceeded");
    this[kError].code = "WS_ERR_UNSUPPORTED_MESSAGE_LENGTH";
    this[kError][kStatusCode] = 1009;
    this.removeListener("data", inflateOnData);
    this.reset();
  }
  function inflateOnError(err) {
    this[kPerMessageDeflate]._inflate = null;
    if (this[kError]) {
      this[kCallback](this[kError]);
      return;
    }
    err[kStatusCode] = 1007;
    this[kCallback](err);
  }
  return permessageDeflate;
}
var validation = { exports: {} };
const __viteOptionalPeerDep_utf8Validate_ws_true = {};
const __viteOptionalPeerDep_utf8Validate_ws_true$1 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
  __proto__: null,
  default: __viteOptionalPeerDep_utf8Validate_ws_true
}, Symbol.toStringTag, { value: "Module" }));
const require$$2 = /* @__PURE__ */ getAugmentedNamespace(__viteOptionalPeerDep_utf8Validate_ws_true$1);
var hasRequiredValidation;
function requireValidation() {
  if (hasRequiredValidation) return validation.exports;
  hasRequiredValidation = 1;
  const { isUtf8 } = require$$0$1;
  const { hasBlob } = requireConstants();
  const tokenChars = [
    0,
    0,
    0,
    0,
    0,
    0,
    0,
    0,
    0,
    0,
    0,
    0,
    0,
    0,
    0,
    0,
    // 0 - 15
    0,
    0,
    0,
    0,
    0,
    0,
    0,
    0,
    0,
    0,
    0,
    0,
    0,
    0,
    0,
    0,
    // 16 - 31
    0,
    1,
    0,
    1,
    1,
    1,
    1,
    1,
    0,
    0,
    1,
    1,
    0,
    1,
    1,
    0,
    // 32 - 47
    1,
    1,
    1,
    1,
    1,
    1,
    1,
    1,
    1,
    1,
    0,
    0,
    0,
    0,
    0,
    0,
    // 48 - 63
    0,
    1,
    1,
    1,
    1,
    1,
    1,
    1,
    1,
    1,
    1,
    1,
    1,
    1,
    1,
    1,
    // 64 - 79
    1,
    1,
    1,
    1,
    1,
    1,
    1,
    1,
    1,
    1,
    1,
    0,
    0,
    0,
    1,
    1,
    // 80 - 95
    1,
    1,
    1,
    1,
    1,
    1,
    1,
    1,
    1,
    1,
    1,
    1,
    1,
    1,
    1,
    1,
    // 96 - 111
    1,
    1,
    1,
    1,
    1,
    1,
    1,
    1,
    1,
    1,
    1,
    0,
    1,
    0,
    1,
    0
    // 112 - 127
  ];
  function isValidStatusCode(code) {
    return code >= 1e3 && code <= 1014 && code !== 1004 && code !== 1005 && code !== 1006 || code >= 3e3 && code <= 4999;
  }
  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 || // Overlong
        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 || // Overlong
        buf[i] === 244 && buf[i + 1] > 143 || buf[i] > 244) {
          return false;
        }
        i += 4;
      } else {
        return false;
      }
    }
    return true;
  }
  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");
  }
  validation.exports = {
    isBlob,
    isValidStatusCode,
    isValidUTF8: _isValidUTF8,
    tokenChars
  };
  if (isUtf8) {
    validation.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$$2;
      validation.exports.isValidUTF8 = function(buf) {
        return buf.length < 32 ? _isValidUTF8(buf) : isValidUTF8(buf);
      };
    } catch (e) {
    }
  }
  return validation.exports;
}
var receiver;
var hasRequiredReceiver;
function requireReceiver() {
  if (hasRequiredReceiver) return receiver;
  hasRequiredReceiver = 1;
  const { Writable } = require$$0$2;
  const PerMessageDeflate = requirePermessageDeflate();
  const {
    BINARY_TYPES,
    EMPTY_BUFFER,
    kStatusCode,
    kWebSocket
  } = requireConstants();
  const { concat, toArrayBuffer: toArrayBuffer2, unmask } = requireBufferUtil();
  const { isValidStatusCode, isValidUTF8 } = requireValidation();
  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;
  class Receiver 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) {
        const error = this.createError(
          RangeError,
          "RSV2 and RSV3 must be clear",
          true,
          1002,
          "WS_ERR_UNEXPECTED_RSV_2_3"
        );
        cb(error);
        return;
      }
      const compressed = (buf[0] & 64) === 64;
      if (compressed && !this._extensions[PerMessageDeflate.extensionName]) {
        const error = this.createError(
          RangeError,
          "RSV1 must be clear",
          true,
          1002,
          "WS_ERR_UNEXPECTED_RSV_1"
        );
        cb(error);
        return;
      }
      this._fin = (buf[0] & 128) === 128;
      this._opcode = buf[0] & 15;
      this._payloadLength = buf[1] & 127;
      if (this._opcode === 0) {
        if (compressed) {
          const error = this.createError(
            RangeError,
            "RSV1 must be clear",
            true,
            1002,
            "WS_ERR_UNEXPECTED_RSV_1"
          );
          cb(error);
          return;
        }
        if (!this._fragmented) {
          const error = this.createError(
            RangeError,
            "invalid opcode 0",
            true,
            1002,
            "WS_ERR_INVALID_OPCODE"
          );
          cb(error);
          return;
        }
        this._opcode = this._fragmented;
      } else if (this._opcode === 1 || this._opcode === 2) {
        if (this._fragmented) {
          const error = this.createError(
            RangeError,
            `invalid opcode ${this._opcode}`,
            true,
            1002,
            "WS_ERR_INVALID_OPCODE"
          );
          cb(error);
          return;
        }
        this._compressed = compressed;
      } else if (this._opcode > 7 && this._opcode < 11) {
        if (!this._fin) {
          const error = this.createError(
            RangeError,
            "FIN must be set",
            true,
            1002,
            "WS_ERR_EXPECTED_FIN"
          );
          cb(error);
          return;
        }
        if (compressed) {
          const error = this.createError(
            RangeError,
            "RSV1 must be clear",
            true,
            1002,
            "WS_ERR_UNEXPECTED_RSV_1"
          );
          cb(error);
          return;
        }
        if (this._payloadLength > 125 || this._opcode === 8 && this._payloadLength === 1) {
          const error = this.createError(
            RangeError,
            `invalid payload length ${this._payloadLength}`,
            true,
            1002,
            "WS_ERR_INVALID_CONTROL_PAYLOAD_LENGTH"
          );
          cb(error);
          return;
        }
      } else {
        const error = this.createError(
          RangeError,
          `invalid opcode ${this._opcode}`,
          true,
          1002,
          "WS_ERR_INVALID_OPCODE"
        );
        cb(error);
        return;
      }
      if (!this._fin && !this._fragmented) this._fragmented = this._opcode;
      this._masked = (buf[1] & 128) === 128;
      if (this._isServer) {
        if (!this._masked) {
          const error = this.createError(
            RangeError,
            "MASK must be set",
            true,
            1002,
            "WS_ERR_EXPECTED_MASK"
          );
          cb(error);
          return;
        }
      } else if (this._masked) {
        const error = this.createError(
          RangeError,
          "MASK must be clear",
          true,
          1002,
          "WS_ERR_UNEXPECTED_MASK"
        );
        cb(error);
        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, 53 - 32) - 1) {
        const error = this.createError(
          RangeError,
          "Unsupported WebSocket frame: payload length > 2^53 - 1",
          false,
          1009,
          "WS_ERR_UNSUPPORTED_DATA_PAYLOAD_LENGTH"
        );
        cb(error);
        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) {
          const error = this.createError(
            RangeError,
            "Max payload size exceeded",
            false,
            1009,
            "WS_ERR_UNSUPPORTED_MESSAGE_LENGTH"
          );
          cb(error);
          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) {
      const perMessageDeflate = this._extensions[PerMessageDeflate.extensionName];
      perMessageDeflate.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) {
            const error = this.createError(
              RangeError,
              "Max payload size exceeded",
              false,
              1009,
              "WS_ERR_UNSUPPORTED_MESSAGE_LENGTH"
            );
            cb(error);
            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 = toArrayBuffer2(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)) {
          const error = this.createError(
            Error,
            "invalid UTF-8 sequence",
            true,
            1007,
            "WS_ERR_INVALID_UTF8"
          );
          cb(error);
          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)) {
            const error = this.createError(
              RangeError,
              `invalid status code ${code}`,
              true,
              1002,
              "WS_ERR_INVALID_CLOSE_CODE"
            );
            cb(error);
            return;
          }
          const buf = new FastBuffer(
            data.buffer,
            data.byteOffset + 2,
            data.length - 2
          );
          if (!this._skipUTF8Validation && !isValidUTF8(buf)) {
            const error = this.createError(
              Error,
              "invalid UTF-8 sequence",
              true,
              1007,
              "WS_ERR_INVALID_UTF8"
            );
            cb(error);
            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;
    }
  }
  receiver = Receiver;
  return receiver;
}
var sender;
var hasRequiredSender;
function requireSender() {
  if (hasRequiredSender) return sender;
  hasRequiredSender = 1;
  const { Duplex } = require$$0$2;
  const { randomFillSync } = crypto;
  const PerMessageDeflate = requirePermessageDeflate();
  const { EMPTY_BUFFER, kWebSocket, NOOP } = requireConstants();
  const { isBlob, isValidStatusCode } = requireValidation();
  const { mask: applyMask, toBuffer } = requireBufferUtil();
  const kByteLength = /* @__PURE__ */ 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;
  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) {
            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 = 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) {
          const err = new Error(
            "The socket was closed while data was being compressed"
          );
          callCallbacks(this, err, 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);
      }
    }
  }
  sender = Sender;
  function callCallbacks(sender2, err, cb) {
    if (typeof cb === "function") cb(err);
    for (let i = 0; i < sender2._queue.length; i++) {
      const params = sender2._queue[i];
      const callback = params[params.length - 1];
      if (typeof callback === "function") callback(err);
    }
  }
  function onError(sender2, err, cb) {
    callCallbacks(sender2, err, cb);
    sender2.onerror(err);
  }
  return sender;
}
var eventTarget;
var hasRequiredEventTarget;
function requireEventTarget() {
  if (hasRequiredEventTarget) return eventTarget;
  hasRequiredEventTarget = 1;
  const { kForOnEventAttribute, kListener } = requireConstants();
  const kCode = /* @__PURE__ */ Symbol("kCode");
  const kData = /* @__PURE__ */ Symbol("kData");
  const kError = /* @__PURE__ */ Symbol("kError");
  const kMessage = /* @__PURE__ */ Symbol("kMessage");
  const kReason = /* @__PURE__ */ Symbol("kReason");
  const kTarget = /* @__PURE__ */ Symbol("kTarget");
  const kType = /* @__PURE__ */ Symbol("kType");
  const kWasClean = /* @__PURE__ */ Symbol("kWasClean");
  class Event {
    /**
     * 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 CloseEvent 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 ErrorEvent 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 MessageEvent 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 });
  const EventTarget = {
    /**
     * Register an event listener.
     *
     * @param {String} type A string representing the event type to listen for
     * @param {(Function|Object)} handler The listener to add
     * @param {Object} [options] An options object specifies characteristics about
     *     the event listener
     * @param {Boolean} [options.once=false] A `Boolean` indicating that the
     *     listener should be invoked at most once after being added. If `true`,
     *     the listener would be automatically removed when invoked.
     * @public
     */
    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);
      }
    },
    /**
     * Remove an event listener.
     *
     * @param {String} type A string representing the event type to remove
     * @param {(Function|Object)} handler The listener to remove
     * @public
     */
    removeEventListener(type, handler) {
      for (const listener of this.listeners(type)) {
        if (listener[kListener] === handler && !listener[kForOnEventAttribute]) {
          this.removeListener(type, listener);
          break;
        }
      }
    }
  };
  eventTarget = {
    CloseEvent,
    ErrorEvent,
    Event,
    EventTarget,
    MessageEvent
  };
  function callListener(listener, thisArg, event) {
    if (typeof listener === "object" && listener.handleEvent) {
      listener.handleEvent.call(listener, event);
    } else {
      listener.call(thisArg, event);
    }
  }
  return eventTarget;
}
var extension;
var hasRequiredExtension;
function requireExtension() {
  if (hasRequiredExtension) return extension;
  hasRequiredExtension = 1;
  const { tokenChars } = requireValidation();
  function push(dest, name, elem) {
    if (dest[name] === void 0) dest[name] = [elem];
    else dest[name].push(elem);
  }
  function parse(header) {
    const offers = /* @__PURE__ */ Object.create(null);
    let params = /* @__PURE__ */ 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 = /* @__PURE__ */ 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 = /* @__PURE__ */ 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 = /* @__PURE__ */ 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;
  }
  function format(extensions) {
    return Object.keys(extensions).map((extension2) => {
      let configurations = extensions[extension2];
      if (!Array.isArray(configurations)) configurations = [configurations];
      return configurations.map((params) => {
        return [extension2].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(", ");
  }
  extension = { format, parse };
  return extension;
}
var websocket;
var hasRequiredWebsocket;
function requireWebsocket() {
  if (hasRequiredWebsocket) return websocket;
  hasRequiredWebsocket = 1;
  const EventEmitter2 = require$$0$3;
  const https = require$$1$1;
  const http = require$$2$1;
  const net = require$$3;
  const tls = require$$4;
  const { randomBytes, createHash } = crypto;
  const { Duplex, Readable } = require$$0$2;
  const { URL } = require$$7;
  const PerMessageDeflate = requirePermessageDeflate();
  const Receiver = requireReceiver();
  const Sender = requireSender();
  const { isBlob } = requireValidation();
  const {
    BINARY_TYPES,
    CLOSE_TIMEOUT,
    EMPTY_BUFFER,
    GUID,
    kForOnEventAttribute,
    kListener,
    kStatusCode,
    kWebSocket,
    NOOP
  } = requireConstants();
  const {
    EventTarget: { addEventListener, removeEventListener }
  } = requireEventTarget();
  const { format, parse } = requireExtension();
  const { toBuffer } = requireBufferUtil();
  const kAborted = /* @__PURE__ */ Symbol("kAborted");
  const protocolVersions = [8, 13];
  const readyStates = ["CONNECTING", "OPEN", "CLOSING", "CLOSED"];
  const subprotocolRegex = /^[!#$%&'*+\-.0-9A-Z^_`|a-z~]+$/;
  class WebSocket2 extends EventEmitter2 {
    /**
     * 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 = WebSocket2.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 receiver2 = new Receiver({
        allowSynchronousEvents: options.allowSynchronousEvents,
        binaryType: this.binaryType,
        extensions: this._extensions,
        isServer: this._isServer,
        maxPayload: options.maxPayload,
        skipUTF8Validation: options.skipUTF8Validation
      });
      const sender2 = new Sender(socket, this._extensions, options.generateMask);
      this._receiver = receiver2;
      this._sender = sender2;
      this._socket = socket;
      receiver2[kWebSocket] = this;
      sender2[kWebSocket] = this;
      socket[kWebSocket] = this;
      receiver2.on("conclude", receiverOnConclude);
      receiver2.on("drain", receiverOnDrain);
      receiver2.on("error", receiverOnError);
      receiver2.on("message", receiverOnMessage);
      receiver2.on("ping", receiverOnPing);
      receiver2.on("pong", receiverOnPong);
      sender2.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 = WebSocket2.OPEN;
      this.emit("open");
    }
    /**
     * Emit the `'close'` event.
     *
     * @private
     */
    emitClose() {
      if (!this._socket) {
        this._readyState = WebSocket2.CLOSED;
        this.emit("close", this._closeCode, this._closeMessage);
        return;
      }
      if (this._extensions[PerMessageDeflate.extensionName]) {
        this._extensions[PerMessageDeflate.extensionName].cleanup();
      }
      this._receiver.removeAllListeners();
      this._readyState = WebSocket2.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 === WebSocket2.CLOSED) return;
      if (this.readyState === WebSocket2.CONNECTING) {
        const msg = "WebSocket was closed before the connection was established";
        abortHandshake(this, this._req, msg);
        return;
      }
      if (this.readyState === WebSocket2.CLOSING) {
        if (this._closeFrameSent && (this._closeFrameReceived || this._receiver._writableState.errorEmitted)) {
          this._socket.end();
        }
        return;
      }
      this._readyState = WebSocket2.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 === WebSocket2.CONNECTING || this.readyState === WebSocket2.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 === WebSocket2.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 !== WebSocket2.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 === WebSocket2.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 !== WebSocket2.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 === WebSocket2.CONNECTING || this.readyState === WebSocket2.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 === WebSocket2.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 !== WebSocket2.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 === WebSocket2.CLOSED) return;
      if (this.readyState === WebSocket2.CONNECTING) {
        const msg = "WebSocket was closed before the connection was established";
        abortHandshake(this, this._req, msg);
        return;
      }
      if (this._socket) {
        this._readyState = WebSocket2.CLOSING;
        this._socket.destroy();
      }
    }
  }
  Object.defineProperty(WebSocket2, "CONNECTING", {
    enumerable: true,
    value: readyStates.indexOf("CONNECTING")
  });
  Object.defineProperty(WebSocket2.prototype, "CONNECTING", {
    enumerable: true,
    value: readyStates.indexOf("CONNECTING")
  });
  Object.defineProperty(WebSocket2, "OPEN", {
    enumerable: true,
    value: readyStates.indexOf("OPEN")
  });
  Object.defineProperty(WebSocket2.prototype, "OPEN", {
    enumerable: true,
    value: readyStates.indexOf("OPEN")
  });
  Object.defineProperty(WebSocket2, "CLOSING", {
    enumerable: true,
    value: readyStates.indexOf("CLOSING")
  });
  Object.defineProperty(WebSocket2.prototype, "CLOSING", {
    enumerable: true,
    value: readyStates.indexOf("CLOSING")
  });
  Object.defineProperty(WebSocket2, "CLOSED", {
    enumerable: true,
    value: readyStates.indexOf("CLOSED")
  });
  Object.defineProperty(WebSocket2.prototype, "CLOSED", {
    enumerable: true,
    value: readyStates.indexOf("CLOSED")
  });
  [
    "binaryType",
    "bufferedAmount",
    "extensions",
    "isPaused",
    "protocol",
    "readyState",
    "url"
  ].forEach((property) => {
    Object.defineProperty(WebSocket2.prototype, property, { enumerable: true });
  });
  ["open", "error", "close", "message"].forEach((method) => {
    Object.defineProperty(WebSocket2.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
        });
      }
    });
  });
  WebSocket2.prototype.addEventListener = addEventListener;
  WebSocket2.prototype.removeEventListener = removeEventListener;
  websocket = WebSocket2;
  function initAsClient(websocket2, 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
    };
    websocket2._autoPong = opts.autoPong;
    websocket2._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:";
    }
    websocket2._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 (websocket2._redirects === 0) {
        throw err;
      } else {
        emitErrorAndClose(websocket2, err);
        return;
      }
    }
    const defaultPort = isSecure ? 443 : 80;
    const key = randomBytes(16).toString("base64");
    const request = isSecure ? https.request : http.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 (websocket2._redirects === 0) {
        websocket2._originalIpc = isIpcUrl;
        websocket2._originalSecure = isSecure;
        websocket2._originalHostOrSocketPath = isIpcUrl ? opts.socketPath : parsedUrl.host;
        const headers = options && options.headers;
        options = { ...options, headers: {} };
        if (headers) {
          for (const [key2, value] of Object.entries(headers)) {
            options.headers[key2.toLowerCase()] = value;
          }
        }
      } else if (websocket2.listenerCount("redirect") === 0) {
        const isSameHost = isIpcUrl ? websocket2._originalIpc ? opts.socketPath === websocket2._originalHostOrSocketPath : false : websocket2._originalIpc ? false : parsedUrl.host === websocket2._originalHostOrSocketPath;
        if (!isSameHost || websocket2._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 = websocket2._req = request(opts);
      if (websocket2._redirects) {
        websocket2.emit("redirect", websocket2.url, req);
      }
    } else {
      req = websocket2._req = request(opts);
    }
    if (opts.timeout) {
      req.on("timeout", () => {
        abortHandshake(websocket2, req, "Opening handshake has timed out");
      });
    }
    req.on("error", (err) => {
      if (req === null || req[kAborted]) return;
      req = websocket2._req = null;
      emitErrorAndClose(websocket2, err);
    });
    req.on("response", (res) => {
      const location = res.headers.location;
      const statusCode = res.statusCode;
      if (location && opts.followRedirects && statusCode >= 300 && statusCode < 400) {
        if (++websocket2._redirects > opts.maxRedirects) {
          abortHandshake(websocket2, req, "Maximum redirects exceeded");
          return;
        }
        req.abort();
        let addr;
        try {
          addr = new URL(location, address);
        } catch (e) {
          const err = new SyntaxError(`Invalid URL: ${location}`);
          emitErrorAndClose(websocket2, err);
          return;
        }
        initAsClient(websocket2, addr, protocols, options);
      } else if (!websocket2.emit("unexpected-response", req, res)) {
        abortHandshake(
          websocket2,
          req,
          `Unexpected server response: ${res.statusCode}`
        );
      }
    });
    req.on("upgrade", (res, socket, head) => {
      websocket2.emit("upgrade", res);
      if (websocket2.readyState !== WebSocket2.CONNECTING) return;
      req = websocket2._req = null;
      const upgrade = res.headers.upgrade;
      if (upgrade === void 0 || upgrade.toLowerCase() !== "websocket") {
        abortHandshake(websocket2, socket, "Invalid Upgrade header");
        return;
      }
      const digest = createHash("sha1").update(key + GUID).digest("base64");
      if (res.headers["sec-websocket-accept"] !== digest) {
        abortHandshake(websocket2, 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(websocket2, socket, protError);
        return;
      }
      if (serverProt) websocket2._protocol = serverProt;
      const secWebSocketExtensions = res.headers["sec-websocket-extensions"];
      if (secWebSocketExtensions !== void 0) {
        if (!perMessageDeflate) {
          const message = "Server sent a Sec-WebSocket-Extensions header but no extension was requested";
          abortHandshake(websocket2, socket, message);
          return;
        }
        let extensions;
        try {
          extensions = parse(secWebSocketExtensions);
        } catch (err) {
          const message = "Invalid Sec-WebSocket-Extensions header";
          abortHandshake(websocket2, socket, message);
          return;
        }
        const extensionNames = Object.keys(extensions);
        if (extensionNames.length !== 1 || extensionNames[0] !== PerMessageDeflate.extensionName) {
          const message = "Server indicated an extension that was not requested";
          abortHandshake(websocket2, socket, message);
          return;
        }
        try {
          perMessageDeflate.accept(extensions[PerMessageDeflate.extensionName]);
        } catch (err) {
          const message = "Invalid Sec-WebSocket-Extensions header";
          abortHandshake(websocket2, socket, message);
          return;
        }
        websocket2._extensions[PerMessageDeflate.extensionName] = perMessageDeflate;
      }
      websocket2.setSocket(socket, head, {
        allowSynchronousEvents: opts.allowSynchronousEvents,
        generateMask: opts.generateMask,
        maxPayload: opts.maxPayload,
        skipUTF8Validation: opts.skipUTF8Validation
      });
    });
    if (opts.finishRequest) {
      opts.finishRequest(req, websocket2);
    } else {
      req.end();
    }
  }
  function emitErrorAndClose(websocket2, err) {
    websocket2._readyState = WebSocket2.CLOSING;
    websocket2._errorEmitted = true;
    websocket2.emit("error", err);
    websocket2.emitClose();
  }
  function netConnect(options) {
    options.path = options.socketPath;
    return net.connect(options);
  }
  function tlsConnect(options) {
    options.path = void 0;
    if (!options.servername && options.servername !== "") {
      options.servername = net.isIP(options.host) ? "" : options.host;
    }
    return tls.connect(options);
  }
  function abortHandshake(websocket2, stream2, message) {
    websocket2._readyState = WebSocket2.CLOSING;
    const err = new Error(message);
    Error.captureStackTrace(err, abortHandshake);
    if (stream2.setHeader) {
      stream2[kAborted] = true;
      stream2.abort();
      if (stream2.socket && !stream2.socket.destroyed) {
        stream2.socket.destroy();
      }
      process.nextTick(emitErrorAndClose, websocket2, err);
    } else {
      stream2.destroy(err);
      stream2.once("error", websocket2.emit.bind(websocket2, "error"));
      stream2.once("close", websocket2.emitClose.bind(websocket2));
    }
  }
  function sendAfterClose(websocket2, data, cb) {
    if (data) {
      const length = isBlob(data) ? data.size : toBuffer(data).length;
      if (websocket2._socket) websocket2._sender._bufferedBytes += length;
      else websocket2._bufferedAmount += length;
    }
    if (cb) {
      const err = new Error(
        `WebSocket is not open: readyState ${websocket2.readyState} (${readyStates[websocket2.readyState]})`
      );
      process.nextTick(cb, err);
    }
  }
  function receiverOnConclude(code, reason) {
    const websocket2 = this[kWebSocket];
    websocket2._closeFrameReceived = true;
    websocket2._closeMessage = reason;
    websocket2._closeCode = code;
    if (websocket2._socket[kWebSocket] === void 0) return;
    websocket2._socket.removeListener("data", socketOnData);
    process.nextTick(resume, websocket2._socket);
    if (code === 1005) websocket2.close();
    else websocket2.close(code, reason);
  }
  function receiverOnDrain() {
    const websocket2 = this[kWebSocket];
    if (!websocket2.isPaused) websocket2._socket.resume();
  }
  function receiverOnError(err) {
    const websocket2 = this[kWebSocket];
    if (websocket2._socket[kWebSocket] !== void 0) {
      websocket2._socket.removeListener("data", socketOnData);
      process.nextTick(resume, websocket2._socket);
      websocket2.close(err[kStatusCode]);
    }
    if (!websocket2._errorEmitted) {
      websocket2._errorEmitted = true;
      websocket2.emit("error", err);
    }
  }
  function receiverOnFinish() {
    this[kWebSocket].emitClose();
  }
  function receiverOnMessage(data, isBinary) {
    this[kWebSocket].emit("message", data, isBinary);
  }
  function receiverOnPing(data) {
    const websocket2 = this[kWebSocket];
    if (websocket2._autoPong) websocket2.pong(data, !this._isServer, NOOP);
    websocket2.emit("ping", data);
  }
  function receiverOnPong(data) {
    this[kWebSocket].emit("pong", data);
  }
  function resume(stream2) {
    stream2.resume();
  }
  function senderOnError(err) {
    const websocket2 = this[kWebSocket];
    if (websocket2.readyState === WebSocket2.CLOSED) return;
    if (websocket2.readyState === WebSocket2.OPEN) {
      websocket2._readyState = WebSocket2.CLOSING;
      setCloseTimer(websocket2);
    }
    this._socket.end();
    if (!websocket2._errorEmitted) {
      websocket2._errorEmitted = true;
      websocket2.emit("error", err);
    }
  }
  function setCloseTimer(websocket2) {
    websocket2._closeTimer = setTimeout(
      websocket2._socket.destroy.bind(websocket2._socket),
      websocket2._closeTimeout
    );
  }
  function socketOnClose() {
    const websocket2 = this[kWebSocket];
    this.removeListener("close", socketOnClose);
    this.removeListener("data", socketOnData);
    this.removeListener("end", socketOnEnd);
    websocket2._readyState = WebSocket2.CLOSING;
    if (!this._readableState.endEmitted && !websocket2._closeFrameReceived && !websocket2._receiver._writableState.errorEmitted && this._readableState.length !== 0) {
      const chunk = this.read(this._readableState.length);
      websocket2._receiver.write(chunk);
    }
    websocket2._receiver.end();
    this[kWebSocket] = void 0;
    clearTimeout(websocket2._closeTimer);
    if (websocket2._receiver._writableState.finished || websocket2._receiver._writableState.errorEmitted) {
      websocket2.emitClose();
    } else {
      websocket2._receiver.on("error", receiverOnFinish);
      websocket2._receiver.on("finish", receiverOnFinish);
    }
  }
  function socketOnData(chunk) {
    if (!this[kWebSocket]._receiver.write(chunk)) {
      this.pause();
    }
  }
  function socketOnEnd() {
    const websocket2 = this[kWebSocket];
    websocket2._readyState = WebSocket2.CLOSING;
    websocket2._receiver.end();
    this.end();
  }
  function socketOnError() {
    const websocket2 = this[kWebSocket];
    this.removeListener("error", socketOnError);
    this.on("error", NOOP);
    if (websocket2) {
      websocket2._readyState = WebSocket2.CLOSING;
      this.destroy();
    }
  }
  return websocket;
}
var stream;
var hasRequiredStream;
function requireStream() {
  if (hasRequiredStream) return stream;
  hasRequiredStream = 1;
  requireWebsocket();
  const { Duplex } = require$$0$2;
  function emitClose(stream2) {
    stream2.emit("close");
  }
  function duplexOnEnd() {
    if (!this.destroyed && this._writableState.finished) {
      this.destroy();
    }
  }
  function duplexOnError(err) {
    this.removeListener("error", duplexOnError);
    this.destroy();
    if (this.listenerCount("error") === 0) {
      this.emit("error", err);
    }
  }
  function createWebSocketStream(ws2, options) {
    let terminateOnDestroy = true;
    const duplex = new Duplex({
      ...options,
      autoDestroy: false,
      emitClose: false,
      objectMode: false,
      writableObjectMode: false
    });
    ws2.on("message", function message(msg, isBinary) {
      const data = !isBinary && duplex._readableState.objectMode ? msg.toString() : msg;
      if (!duplex.push(data)) ws2.pause();
    });
    ws2.once("error", function error(err) {
      if (duplex.destroyed) return;
      terminateOnDestroy = false;
      duplex.destroy(err);
    });
    ws2.once("close", function close() {
      if (duplex.destroyed) return;
      duplex.push(null);
    });
    duplex._destroy = function(err, callback) {
      if (ws2.readyState === ws2.CLOSED) {
        callback(err);
        process.nextTick(emitClose, duplex);
        return;
      }
      let called = false;
      ws2.once("error", function error(err2) {
        called = true;
        callback(err2);
      });
      ws2.once("close", function close() {
        if (!called) callback(err);
        process.nextTick(emitClose, duplex);
      });
      if (terminateOnDestroy) ws2.terminate();
    };
    duplex._final = function(callback) {
      if (ws2.readyState === ws2.CONNECTING) {
        ws2.once("open", function open() {
          duplex._final(callback);
        });
        return;
      }
      if (ws2._socket === null) return;
      if (ws2._socket._writableState.finished) {
        callback();
        if (duplex._readableState.endEmitted) duplex.destroy();
      } else {
        ws2._socket.once("finish", function finish() {
          callback();
        });
        ws2.close();
      }
    };
    duplex._read = function() {
      if (ws2.isPaused) ws2.resume();
    };
    duplex._write = function(chunk, encoding, callback) {
      if (ws2.readyState === ws2.CONNECTING) {
        ws2.once("open", function open() {
          duplex._write(chunk, encoding, callback);
        });
        return;
      }
      ws2.send(chunk, callback);
    };
    duplex.on("end", duplexOnEnd);
    duplex.on("error", duplexOnError);
    return duplex;
  }
  stream = createWebSocketStream;
  return stream;
}
var subprotocol;
var hasRequiredSubprotocol;
function requireSubprotocol() {
  if (hasRequiredSubprotocol) return subprotocol;
  hasRequiredSubprotocol = 1;
  const { tokenChars } = requireValidation();
  function parse(header) {
    const protocols = /* @__PURE__ */ new Set();
    let start = -1;
    let end = -1;
    let i = 0;
    for (i; 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 protocol2 = header.slice(start, end);
        if (protocols.has(protocol2)) {
          throw new SyntaxError(`The "${protocol2}" subprotocol is duplicated`);
        }
        protocols.add(protocol2);
        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;
  }
  subprotocol = { parse };
  return subprotocol;
}
var websocketServer;
var hasRequiredWebsocketServer;
function requireWebsocketServer() {
  if (hasRequiredWebsocketServer) return websocketServer;
  hasRequiredWebsocketServer = 1;
  const EventEmitter2 = require$$0$3;
  const http = require$$2$1;
  const { Duplex } = require$$0$2;
  const { createHash } = crypto;
  const extension2 = requireExtension();
  const PerMessageDeflate = requirePermessageDeflate();
  const subprotocol2 = requireSubprotocol();
  const WebSocket2 = requireWebsocket();
  const { CLOSE_TIMEOUT, GUID, kWebSocket } = requireConstants();
  const keyRegex = /^[+/0-9A-Za-z]{22}==$/;
  const RUNNING = 0;
  const CLOSING = 1;
  const CLOSED = 2;
  class WebSocketServer extends EventEmitter2 {
    /**
     * 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,
        // use default (511 as implemented in net.js)
        server: null,
        host: null,
        path: null,
        port: null,
        WebSocket: WebSocket2,
        ...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(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("?");
        const pathname = index !== -1 ? req.url.slice(0, index) : req.url;
        if (pathname !== 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") {
        const message = "Invalid HTTP method";
        abortHandshakeOrEmitwsClientError(this, req, socket, 405, message);
        return;
      }
      if (upgrade === void 0 || upgrade.toLowerCase() !== "websocket") {
        const message = "Invalid Upgrade header";
        abortHandshakeOrEmitwsClientError(this, req, socket, 400, message);
        return;
      }
      if (key === void 0 || !keyRegex.test(key)) {
        const message = "Missing or invalid Sec-WebSocket-Key header";
        abortHandshakeOrEmitwsClientError(this, req, socket, 400, message);
        return;
      }
      if (version !== 13 && version !== 8) {
        const message = "Missing or invalid Sec-WebSocket-Version header";
        abortHandshakeOrEmitwsClientError(this, req, socket, 400, message, {
          "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 = subprotocol2.parse(secWebSocketProtocol);
        } catch (err) {
          const message = "Invalid Sec-WebSocket-Protocol header";
          abortHandshakeOrEmitwsClientError(this, req, socket, 400, message);
          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 = extension2.parse(secWebSocketExtensions);
          if (offers[PerMessageDeflate.extensionName]) {
            perMessageDeflate.accept(offers[PerMessageDeflate.extensionName]);
            extensions[PerMessageDeflate.extensionName] = perMessageDeflate;
          }
        } catch (err) {
          const message = "Invalid or unacceptable Sec-WebSocket-Extensions header";
          abortHandshakeOrEmitwsClientError(this, req, socket, 400, message);
          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 digest = createHash("sha1").update(key + GUID).digest("base64");
      const headers = [
        "HTTP/1.1 101 Switching Protocols",
        "Upgrade: websocket",
        "Connection: Upgrade",
        `Sec-WebSocket-Accept: ${digest}`
      ];
      const ws2 = 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}`);
          ws2._protocol = protocol;
        }
      }
      if (extensions[PerMessageDeflate.extensionName]) {
        const params = extensions[PerMessageDeflate.extensionName].params;
        const value = extension2.format({
          [PerMessageDeflate.extensionName]: [params]
        });
        headers.push(`Sec-WebSocket-Extensions: ${value}`);
        ws2._extensions = extensions;
      }
      this.emit("headers", headers, req);
      socket.write(headers.concat("\r\n").join("\r\n"));
      socket.removeListener("error", socketOnError);
      ws2.setSocket(socket, head, {
        allowSynchronousEvents: this.options.allowSynchronousEvents,
        maxPayload: this.options.maxPayload,
        skipUTF8Validation: this.options.skipUTF8Validation
      });
      if (this.clients) {
        this.clients.add(ws2);
        ws2.on("close", () => {
          this.clients.delete(ws2);
          if (this._shouldEmitClose && !this.clients.size) {
            process.nextTick(emitClose, this);
          }
        });
      }
      cb(ws2, req);
    }
  }
  websocketServer = WebSocketServer;
  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]);
      }
    };
  }
  function emitClose(server) {
    server._state = CLOSED;
    server.emit("close");
  }
  function socketOnError() {
    this.destroy();
  }
  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
` + Object.keys(headers).map((h) => `${h}: ${headers[h]}`).join("\r\n") + "\r\n\r\n" + message
    );
  }
  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);
    }
  }
  return websocketServer;
}
var ws;
var hasRequiredWs;
function requireWs() {
  if (hasRequiredWs) return ws;
  hasRequiredWs = 1;
  const WebSocket2 = requireWebsocket();
  WebSocket2.createWebSocketStream = requireStream();
  WebSocket2.Server = requireWebsocketServer();
  WebSocket2.Receiver = requireReceiver();
  WebSocket2.Sender = requireSender();
  WebSocket2.WebSocket = WebSocket2;
  WebSocket2.WebSocketServer = WebSocket2.Server;
  ws = WebSocket2;
  return ws;
}
var node;
var hasRequiredNode;
function requireNode() {
  if (hasRequiredNode) return node;
  hasRequiredNode = 1;
  node = requireWs();
  return node;
}
var nodeExports = requireNode();
const WebSocket = /* @__PURE__ */ getDefaultExportFromCjs(nodeExports);
const isPeerMessage = (message) => message.type === "peer";
const isErrorMessage = (message) => message.type === "error";
const ProtocolV1 = "1";
function assert(value, message = "Assertion failed") {
  if (value === false || value === null || value === void 0) {
    const error = new Error(trimLines(message));
    error.stack = removeLine(error.stack, "assert.ts");
    throw error;
  }
}
const trimLines = (s) => s.split("\n").map((s2) => s2.trim()).join("\n");
const removeLine = (s = "", targetText) => s.split("\n").filter((line) => !line.includes(targetText)).join("\n");
const toArrayBuffer = (bytes) => {
  const { buffer, byteOffset, byteLength } = bytes;
  return buffer.slice(byteOffset, byteOffset + byteLength);
};
class WebSocketNetworkAdapter extends NetworkAdapter {
  constructor() {
    super(...arguments);
    __publicField(this, "socket");
  }
}
class WebSocketClientAdapter extends WebSocketNetworkAdapter {
  // this adapter only connects to one remote client at a time
  constructor(url, retryInterval = 5e3) {
    super();
    __privateAdd(this, _WebSocketClientAdapter_instances);
    __publicField(this, "url");
    __publicField(this, "retryInterval");
    __privateAdd(this, _ready2, false);
    __privateAdd(this, _readyResolver);
    __privateAdd(this, _readyPromise2, new Promise((resolve) => {
      __privateSet(this, _readyResolver, resolve);
    }));
    __privateAdd(this, _retryIntervalId);
    __privateAdd(this, _log5, debug("automerge-repo:websocket:browser"));
    __publicField(this, "remotePeerId");
    __publicField(this, "onOpen", () => {
      __privateGet(this, _log5).call(this, "open");
      clearInterval(__privateGet(this, _retryIntervalId));
      __privateSet(this, _retryIntervalId, void 0);
      this.join();
    });
    // When a socket closes, or disconnects, remove it from the array.
    __publicField(this, "onClose", () => {
      __privateGet(this, _log5).call(this, "close");
      if (this.remotePeerId)
        this.emit("peer-disconnected", { peerId: this.remotePeerId });
      if (this.retryInterval > 0 && !__privateGet(this, _retryIntervalId))
        setTimeout(() => {
          assert(this.peerId);
          return this.connect(this.peerId, this.peerMetadata);
        }, this.retryInterval);
    });
    __publicField(this, "onMessage", (event) => {
      this.receiveMessage(event.data);
    });
    /** The websocket error handler signature is different on node and the browser.  */
    __publicField(this, "onError", (event) => {
      if ("error" in event) {
        if (event.error.code !== "ECONNREFUSED") {
          throw event.error;
        }
      }
      __privateGet(this, _log5).call(this, "Connection failed, retrying...");
    });
    this.url = url;
    this.retryInterval = retryInterval;
    __privateSet(this, _log5, __privateGet(this, _log5).extend(url));
  }
  isReady() {
    return __privateGet(this, _ready2);
  }
  whenReady() {
    return __privateGet(this, _readyPromise2);
  }
  connect(peerId, peerMetadata) {
    if (!this.socket || !this.peerId) {
      __privateGet(this, _log5).call(this, "connecting");
      this.peerId = peerId;
      this.peerMetadata = peerMetadata ?? {};
    } else {
      __privateGet(this, _log5).call(this, "reconnecting");
      assert(peerId === this.peerId);
      this.socket.removeEventListener("open", this.onOpen);
      this.socket.removeEventListener("close", this.onClose);
      this.socket.removeEventListener("message", this.onMessage);
      this.socket.removeEventListener("error", this.onError);
    }
    if (!__privateGet(this, _retryIntervalId))
      __privateSet(this, _retryIntervalId, setInterval(() => {
        this.connect(peerId, peerMetadata);
      }, this.retryInterval));
    this.socket = new WebSocket(this.url);
    this.socket.binaryType = "arraybuffer";
    this.socket.addEventListener("open", this.onOpen);
    this.socket.addEventListener("close", this.onClose);
    this.socket.addEventListener("message", this.onMessage);
    this.socket.addEventListener("error", this.onError);
    setTimeout(() => __privateMethod(this, _WebSocketClientAdapter_instances, forceReady_fn).call(this), 1e3);
    this.join();
  }
  join() {
    assert(this.peerId);
    assert(this.socket);
    if (this.socket.readyState === WebSocket.OPEN) {
      this.send(joinMessage(this.peerId, this.peerMetadata));
    }
  }
  disconnect() {
    assert(this.peerId);
    assert(this.socket);
    const socket = this.socket;
    if (socket) {
      socket.removeEventListener("open", this.onOpen);
      socket.removeEventListener("close", this.onClose);
      socket.removeEventListener("message", this.onMessage);
      socket.removeEventListener("error", this.onError);
      socket.close();
    }
    clearInterval(__privateGet(this, _retryIntervalId));
    if (this.remotePeerId)
      this.emit("peer-disconnected", { peerId: this.remotePeerId });
    this.socket = void 0;
  }
  send(message) {
    var _a;
    if ("data" in message && ((_a = message.data) == null ? void 0 : _a.byteLength) === 0)
      throw new Error("Tried to send a zero-length message");
    assert(this.peerId);
    if (!this.socket) {
      __privateGet(this, _log5).call(this, "Tried to send on a disconnected socket.");
      return;
    }
    if (this.socket.readyState !== WebSocket.OPEN)
      throw new Error(`Websocket not ready (${this.socket.readyState})`);
    const encoded = encode(message);
    this.socket.send(toArrayBuffer(encoded));
  }
  peerCandidate(remotePeerId, peerMetadata) {
    assert(this.socket);
    __privateMethod(this, _WebSocketClientAdapter_instances, forceReady_fn).call(this);
    this.remotePeerId = remotePeerId;
    this.emit("peer-candidate", {
      peerId: remotePeerId,
      peerMetadata
    });
  }
  receiveMessage(messageBytes) {
    let message;
    try {
      message = decode(new Uint8Array(messageBytes));
    } catch (e) {
      __privateGet(this, _log5).call(this, "error decoding message:", e);
      return;
    }
    assert(this.socket);
    if (messageBytes.byteLength === 0)
      throw new Error("received a zero-length message");
    if (isPeerMessage(message)) {
      const { peerMetadata } = message;
      __privateGet(this, _log5).call(this, `peer: ${message.senderId}`);
      this.peerCandidate(message.senderId, peerMetadata);
    } else if (isErrorMessage(message)) {
      __privateGet(this, _log5).call(this, `error: ${message.message}`);
    } else {
      this.emit("message", message);
    }
  }
}
_ready2 = new WeakMap();
_readyResolver = new WeakMap();
_readyPromise2 = new WeakMap();
_WebSocketClientAdapter_instances = new WeakSet();
forceReady_fn = function() {
  var _a;
  if (!__privateGet(this, _ready2)) {
    __privateSet(this, _ready2, true);
    (_a = __privateGet(this, _readyResolver)) == null ? void 0 : _a.call(this);
  }
};
_retryIntervalId = new WeakMap();
_log5 = new WeakMap();
function joinMessage(senderId, peerMetadata) {
  return {
    type: "join",
    senderId,
    peerMetadata,
    supportedProtocolVersions: [ProtocolV1]
  };
}
debug("WebsocketServer");
let repo = null;
function getRepo() {
  if (repo) return repo;
  repo = new Repo({
    network: [
      new WebSocketClientAdapter("wss://sync.automerge.org"),
      new BroadcastChannelNetworkAdapter()
    ],
    storage: new IndexedDBStorageAdapter("p2p-poll-app")
  });
  return repo;
}
function cleanupRepo() {
  if (repo) {
    repo.shutdown();
    repo = null;
  }
}
export {
  cleanupRepo,
  getRepo
};
