Files
Tiber365/node_modules/crossws/dist/websocket/sse.mjs
2025-07-24 18:46:24 +02:00

128 lines
3.3 KiB
JavaScript

const _EventTarget = EventTarget;
const defaultOptions = Object.freeze({
bidir: true,
stream: true
});
class WebSocketSSE extends _EventTarget {
static CONNECTING = 0;
static OPEN = 1;
static CLOSING = 2;
static CLOSED = 3;
CONNECTING = 0;
OPEN = 1;
CLOSING = 2;
CLOSED = 3;
/* eslint-disable unicorn/no-null */
onclose = null;
onerror = null;
onopen = null;
onmessage = null;
/* eslint-enable unicorn/no-null */
binaryType = "blob";
readyState = WebSocketSSE.CONNECTING;
url;
protocol = "";
extensions = "";
bufferedAmount = 0;
#options = {};
#sse;
#id;
#sendController;
#queue = [];
constructor(url, init) {
super();
this.url = url.replace(/^ws/, "http");
if (typeof init === "string") {
this.#options = { ...defaultOptions, protocols: init };
} else if (Array.isArray(init)) {
this.#options = { ...defaultOptions, protocols: init };
} else {
this.#options = { ...defaultOptions, ...init };
}
this.#sse = new EventSource(this.url);
this.#sse.addEventListener("open", (_sseEvent) => {
this.readyState = WebSocketSSE.OPEN;
const event = new Event("open");
this.onopen?.(event);
this.dispatchEvent(event);
});
this.#sse.addEventListener("message", (sseEvent) => {
const _event = new MessageEvent("message", {
data: sseEvent.data
});
this.onmessage?.(_event);
this.dispatchEvent(_event);
});
if (this.#options.bidir) {
this.#sse.addEventListener("crossws-id", (sseEvent) => {
this.#id = sseEvent.data;
if (this.#options.stream) {
fetch(this.url, {
method: "POST",
// @ts-expect-error
duplex: "half",
headers: {
"content-type": "application/octet-stream",
"x-crossws-id": this.#id
},
body: new ReadableStream({
start: (controller) => {
this.#sendController = controller;
},
cancel: () => {
this.#sendController = void 0;
}
}).pipeThrough(new TextEncoderStream())
}).catch(() => {
});
}
for (const data of this.#queue) {
this.send(data);
}
this.#queue = [];
});
}
this.#sse.addEventListener("error", (_sseEvent) => {
const event = new Event("error");
this.onerror?.(event);
this.dispatchEvent(event);
});
this.#sse.addEventListener("close", (_sseEvent) => {
this.readyState = WebSocketSSE.CLOSED;
const event = new Event("close");
this.onclose?.(event);
this.dispatchEvent(event);
});
}
close(_code, _reason) {
this.readyState = WebSocketSSE.CLOSING;
this.#sse.close();
this.readyState = WebSocketSSE.CLOSED;
}
async send(data) {
if (!this.#options.bidir) {
throw new Error("bdir option is not enabled!");
}
if (this.readyState !== WebSocketSSE.OPEN) {
throw new Error("WebSocket is not open!");
}
if (!this.#id) {
this.#queue.push(data);
return;
}
if (this.#sendController) {
this.#sendController.enqueue(data);
return;
}
await fetch(this.url, {
method: "POST",
headers: {
"x-crossws-id": this.#id
},
body: data
});
}
}
export { WebSocketSSE };