178 lines
5.5 KiB
JavaScript
178 lines
5.5 KiB
JavaScript
//#region rolldown:runtime
|
|
var __create = Object.create;
|
|
var __defProp = Object.defineProperty;
|
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
var __getProtoOf = Object.getPrototypeOf;
|
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
var __copyProps = (to, from, except, desc) => {
|
|
if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
|
|
key = keys[i];
|
|
if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
|
|
get: ((k) => from[k]).bind(null, key),
|
|
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
});
|
|
}
|
|
return to;
|
|
};
|
|
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
|
|
value: mod,
|
|
enumerable: true
|
|
}) : target, mod));
|
|
|
|
//#endregion
|
|
let hono_ws = require("hono/ws");
|
|
hono_ws = __toESM(hono_ws);
|
|
let ws = require("ws");
|
|
ws = __toESM(ws);
|
|
let node_http = require("node:http");
|
|
node_http = __toESM(node_http);
|
|
|
|
//#region src/events.ts
|
|
/**
|
|
* @link https://developer.mozilla.org/en-US/docs/Web/API/CloseEvent
|
|
*/
|
|
const 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 ?? "";
|
|
}
|
|
};
|
|
|
|
//#endregion
|
|
//#region src/index.ts
|
|
const generateConnectionSymbol = () => Symbol("connection");
|
|
/** @example `c.env[CONNECTION_SYMBOL_KEY]` */
|
|
const CONNECTION_SYMBOL_KEY = Symbol("CONNECTION_SYMBOL_KEY");
|
|
/**
|
|
* Create WebSockets for Node.js
|
|
* @param init Options
|
|
* @returns NodeWebSocket
|
|
*/
|
|
const createNodeWebSocket = (init) => {
|
|
const wss = new ws.WebSocketServer({ noServer: true });
|
|
const waiterMap = /* @__PURE__ */ new Map();
|
|
wss.on("connection", (ws$1, request) => {
|
|
const waiter = waiterMap.get(request);
|
|
if (waiter) {
|
|
waiter.resolve(ws$1);
|
|
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
|
|
};
|
|
const response = 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 ${response.status.toString()} ${node_http.STATUS_CODES[response.status] ?? ""}\r\nConnection: close\r
|
|
Content-Length: 0\r
|
|
\r
|
|
`);
|
|
waiterMap.delete(request);
|
|
return;
|
|
}
|
|
wss.handleUpgrade(request, socket, head, (ws$1) => {
|
|
wss.emit("connection", ws$1, request);
|
|
});
|
|
});
|
|
},
|
|
upgradeWebSocket: (0, hono_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$1 = await nodeUpgradeWebSocket(c.env.incoming, connectionSymbol);
|
|
const messagesReceivedInStarting = [];
|
|
const bufferMessage = (data, isBinary) => {
|
|
messagesReceivedInStarting.push([data, isBinary]);
|
|
};
|
|
ws$1.on("message", bufferMessage);
|
|
const ctx = {
|
|
binaryType: "arraybuffer",
|
|
close(code, reason) {
|
|
ws$1.close(code, reason);
|
|
},
|
|
protocol: ws$1.protocol,
|
|
raw: ws$1,
|
|
get readyState() {
|
|
return ws$1.readyState;
|
|
},
|
|
send(source, opts) {
|
|
ws$1.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 data$1 of datas) try {
|
|
events?.onMessage?.(new MessageEvent("message", { data: isBinary ? data$1 instanceof ArrayBuffer ? data$1 : data$1.buffer.slice(data$1.byteOffset, data$1.byteOffset + data$1.byteLength) : data$1.toString("utf-8") }), ctx);
|
|
} catch (e) {
|
|
(options?.onError ?? console.error)(e);
|
|
}
|
|
};
|
|
ws$1.off("message", bufferMessage);
|
|
for (const message of messagesReceivedInStarting) handleMessage(...message);
|
|
ws$1.on("message", (data, isBinary) => {
|
|
handleMessage(data, isBinary);
|
|
});
|
|
ws$1.on("close", (code, reason) => {
|
|
try {
|
|
events?.onClose?.(new CloseEvent("close", {
|
|
code,
|
|
reason: reason.toString()
|
|
}), ctx);
|
|
} catch (e) {
|
|
(options?.onError ?? console.error)(e);
|
|
}
|
|
});
|
|
ws$1.on("error", (error) => {
|
|
try {
|
|
events?.onError?.(new ErrorEvent("error", { error }), ctx);
|
|
} catch (e) {
|
|
(options?.onError ?? console.error)(e);
|
|
}
|
|
});
|
|
})();
|
|
return new Response();
|
|
})
|
|
};
|
|
};
|
|
|
|
//#endregion
|
|
exports.createNodeWebSocket = createNodeWebSocket; |