"use strict";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
  for (var name in all)
    __defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
  if (from && typeof from === "object" || typeof from === "function") {
    for (let key of __getOwnPropNames(from))
      if (!__hasOwnProp.call(to, key) && key !== except)
        __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
  }
  return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);

// src/index.ts
var index_exports = {};
__export(index_exports, {
  createNodeWebSocket: () => createNodeWebSocket
});
module.exports = __toCommonJS(index_exports);
var import_ws = require("hono/ws");
var import_ws2 = require("ws");

// src/events.ts
var CloseEvent = globalThis.CloseEvent ?? class extends Event {
  #eventInitDict;
  constructor(type, eventInitDict = {}) {
    super(type, eventInitDict);
    this.#eventInitDict = eventInitDict;
  }
  get wasClean() {
    return this.#eventInitDict.wasClean ?? false;
  }
  get code() {
    return this.#eventInitDict.code ?? 0;
  }
  get reason() {
    return this.#eventInitDict.reason ?? "";
  }
};

// src/index.ts
var generateConnectionSymbol = () => Symbol("connection");
var CONNECTION_SYMBOL_KEY = Symbol("CONNECTION_SYMBOL_KEY");
var createNodeWebSocket = (init) => {
  const wss = new import_ws2.WebSocketServer({ noServer: true });
  const waiterMap = /* @__PURE__ */ new Map();
  wss.on("connection", (ws, request) => {
    const waiter = waiterMap.get(request);
    if (waiter) {
      waiter.resolve(ws);
      waiterMap.delete(request);
    }
  });
  const nodeUpgradeWebSocket = (request, connectionSymbol) => {
    return new Promise((resolve) => {
      waiterMap.set(request, { resolve, connectionSymbol });
    });
  };
  return {
    wss,
    injectWebSocket(server) {
      server.on("upgrade", async (request, socket, head) => {
        const url = new URL(request.url ?? "/", init.baseUrl ?? "http://localhost");
        const headers = new Headers();
        for (const key in request.headers) {
          const value = request.headers[key];
          if (!value) {
            continue;
          }
          headers.append(key, Array.isArray(value) ? value[0] : value);
        }
        const env = {
          incoming: request,
          outgoing: void 0
        };
        await init.app.request(url, { headers }, env);
        const waiter = waiterMap.get(request);
        if (!waiter || waiter.connectionSymbol !== env[CONNECTION_SYMBOL_KEY]) {
          socket.end(
            "HTTP/1.1 400 Bad Request\r\nConnection: close\r\nContent-Length: 0\r\n\r\n"
          );
          waiterMap.delete(request);
          return;
        }
        wss.handleUpgrade(request, socket, head, (ws) => {
          wss.emit("connection", ws, request);
        });
      });
    },
    upgradeWebSocket: (0, import_ws.defineWebSocketHelper)(async (c, events, options) => {
      if (c.req.header("upgrade")?.toLowerCase() !== "websocket") {
        return;
      }
      const connectionSymbol = generateConnectionSymbol();
      c.env[CONNECTION_SYMBOL_KEY] = connectionSymbol;
      (async () => {
        const ws = await nodeUpgradeWebSocket(c.env.incoming, connectionSymbol);
        const messagesReceivedInStarting = [];
        const bufferMessage = (data, isBinary) => {
          messagesReceivedInStarting.push([data, isBinary]);
        };
        ws.on("message", bufferMessage);
        const ctx = {
          binaryType: "arraybuffer",
          close(code, reason) {
            ws.close(code, reason);
          },
          protocol: ws.protocol,
          raw: ws,
          get readyState() {
            return ws.readyState;
          },
          send(source, opts) {
            ws.send(source, {
              compress: opts?.compress
            });
          },
          url: new URL(c.req.url)
        };
        try {
          events?.onOpen?.(new Event("open"), ctx);
        } catch (e) {
          ;
          (options?.onError ?? console.error)(e);
        }
        const handleMessage = (data, isBinary) => {
          const datas = Array.isArray(data) ? data : [data];
          for (const data2 of datas) {
            try {
              events?.onMessage?.(
                new MessageEvent("message", {
                  data: isBinary ? data2 instanceof ArrayBuffer ? data2 : data2.buffer.slice(data2.byteOffset, data2.byteOffset + data2.byteLength) : data2.toString("utf-8")
                }),
                ctx
              );
            } catch (e) {
              ;
              (options?.onError ?? console.error)(e);
            }
          }
        };
        ws.off("message", bufferMessage);
        for (const message of messagesReceivedInStarting) {
          handleMessage(...message);
        }
        ws.on("message", (data, isBinary) => {
          handleMessage(data, isBinary);
        });
        ws.on("close", (code, reason) => {
          try {
            events?.onClose?.(new CloseEvent("close", { code, reason: reason.toString() }), ctx);
          } catch (e) {
            ;
            (options?.onError ?? console.error)(e);
          }
        });
        ws.on("error", (error) => {
          try {
            events?.onError?.(
              new ErrorEvent("error", {
                error
              }),
              ctx
            );
          } catch (e) {
            ;
            (options?.onError ?? console.error)(e);
          }
        });
      })();
      return new Response();
    })
  };
};
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
  createNodeWebSocket
});
