passerelle
Version:
TypeScript BroadcastChannel wrapper for structured Events and Async/Await.
112 lines (110 loc) • 3.21 kB
JavaScript
// src/channel/channel.ts
var createChannel = (channelName) => {
const channel = new BroadcastChannel(channelName);
const eventHandlers = /* @__PURE__ */ new Map();
const awaitHandlers = /* @__PURE__ */ new Map();
const pendingRequests = /* @__PURE__ */ new Map();
const handleEventMessage = (message) => {
const handlers = eventHandlers.get(message.action);
if (!handlers) return;
for (const handler of handlers) {
handler(message.payload);
}
};
const handleRequestMessage = async (message) => {
const handler = awaitHandlers.get(message.action);
if (!handler) {
throw new Error(`No handler registered for action "${message.action}"`);
}
const result = await handler(message.payload);
channel.postMessage({
type: "response",
requestId: message.requestId,
result
});
};
const handleResponseMessage = (message) => {
const pending = pendingRequests.get(message.requestId);
if (!pending) return;
clearTimeout(pending.timeout);
pendingRequests.delete(message.requestId);
pending.resolve(message.result);
};
const handleMessage = (message) => {
switch (message.data.type) {
case "event":
handleEventMessage(message.data);
break;
case "request":
handleRequestMessage(message.data);
break;
case "response":
handleResponseMessage(message.data);
break;
default:
throw new Error(`Unknown message type received "${message}"`);
}
};
const onEvent = (action, handler) => {
if (!eventHandlers.has(action)) {
eventHandlers.set(action, /* @__PURE__ */ new Set());
}
const handlers = eventHandlers.get(action);
if (handlers) {
handlers.add(handler);
}
};
const onAwait = (action, handler) => {
awaitHandlers.set(action, handler);
};
const sendEvent = (action, payload) => {
const handlers = eventHandlers.get(action);
if (handlers) {
for (const handler of handlers) {
handler(payload);
}
}
channel.postMessage({
type: "event",
action,
payload
});
};
const sendAwait = (action, payload) => {
return new Promise((resolve, reject) => {
const requestId = Date.now().toString(36) + Math.random().toString(36).substring(2);
const timeout = setTimeout(() => {
if (pendingRequests.has(requestId)) {
pendingRequests.delete(requestId);
reject(new Error(`Request timeout: ${action}`));
}
}, 5e3);
pendingRequests.set(requestId, { resolve, reject, timeout });
channel.postMessage({
type: "request",
requestId,
action,
payload
});
});
};
const destroy = () => {
channel.removeEventListener("message", handleMessage);
for (const [_id, { reject }] of pendingRequests.entries()) {
reject(new Error("Channel closed"));
}
pendingRequests.clear();
eventHandlers.clear();
awaitHandlers.clear();
channel.close();
};
channel.addEventListener("message", handleMessage);
return {
onEvent,
onAwait,
sendEvent,
sendAwait,
destroy
};
};
export { createChannel };