UNPKG

vite-plugin-sharedworker

Version:

Make SharedWorker works like Remote Procedure Call easily

155 lines (150 loc) 4.32 kB
'use strict'; function makeRpcPayload(id, name, args) { return { command: "rpc", data: { id, name, args } }; } function makeBroadcastPayload(data) { return { command: "broadcast", data }; } function makePingPayload() { return { command: "ping" }; } function parsePayload(payload) { if (payload.command === "ping") { return payload; } else if (payload.command === "rpc") { return payload; } else if (payload.command === "broadcast") { return payload; } else { return void 0; } } function random(l, r) { return l + Math.round(Math.random() * (r - l)); } const character_table = "0123456789abcdefghijklmnopqrstuvwxyz"; function randomString(length = 8) { return Array.apply(null, Array(length)).map(() => character_table[random(0, character_table.length - 1)]).join(""); } function defineSharedWorker(self, fns) { fns = fns.filter((fn) => typeof fn === "function"); const map = /* @__PURE__ */ new Map(); for (const fn of fns) { map.set(fn.name, fn); } const ports = /* @__PURE__ */ new Map(); setInterval(() => { const now = (/* @__PURE__ */ new Date()).getTime(); for (const [port, date] of ports) { if (now - date.getTime() >= 2e3) { ports.delete(port); } } }, 1e3); const messageCallbacks = []; self.addEventListener("connect", (event) => { const port = event.ports[0]; ports.set(port, /* @__PURE__ */ new Date()); port.addEventListener("message", async (event2) => { const payload = parsePayload(event2.data); if (payload) { if (payload.command === "rpc") { const fn = map.get(payload.data.name); if (fn) { const result = await fn.apply(event2, payload.data.args); port.postMessage(makeRpcPayload(payload.data.id, payload.data.name, result)); } else { console.error(`Unknown message: ${JSON.stringify(payload, null, 2)}`); } } else if (payload.command === "broadcast") { await Promise.all(messageCallbacks.map((fn) => fn.apply(event2, [payload.data]))); } else if (payload.command === "ping") { ports.set(port, /* @__PURE__ */ new Date()); } } }); port.start(); }); return { ports() { return [...ports.keys()]; }, dispatch(port, data) { port.postMessage(makeBroadcastPayload(data)); }, addMessageListener(fn) { messageCallbacks.push(fn); }, broadcast(data) { for (const port of ports.keys()) { port.postMessage(makeBroadcastPayload(data)); } } }; } function defineClientFactory(worker) { worker.port.start(); const callbacks = /* @__PURE__ */ new Map(); const messageCallbacks = []; worker.port.addEventListener("message", async (event) => { const payload = parsePayload(event.data); if (payload) { if (payload.command === "rpc") { const callback = callbacks.get(payload.data.id); if (callback) { callback(payload.data); callbacks.delete(payload.data.id); } else { console.error(`Unknown message: ${JSON.stringify(payload, null, 2)}`); } } else if (payload.command === "broadcast") { await Promise.all(messageCallbacks.map((fn) => fn.apply(event, [payload.data]))); } } }); setInterval(() => { worker.port.postMessage(makePingPayload()); }, 500); return { defineFunction(name) { return (...args) => { const id = randomString(); const payload = makeRpcPayload(id, name, args); return new Promise((res) => { callbacks.set(id, (payload2) => { res(payload2.args); }); worker.port.postMessage(payload); }); }; }, defineClient() { return { addMessageListener(fn) { messageCallbacks.push(fn); }, dispatch(data) { worker.port.postMessage(makeBroadcastPayload(data)); } }; } }; } exports.defineClientFactory = defineClientFactory; exports.defineSharedWorker = defineSharedWorker; exports.makeBroadcastPayload = makeBroadcastPayload; exports.makePingPayload = makePingPayload; exports.makeRpcPayload = makeRpcPayload; exports.parsePayload = parsePayload;