UNPKG

@r_wohl/web-channel-message

Version:

A light weight type-safe library for communicating via the Channel Message Web API

256 lines (251 loc) 7.97 kB
"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 src_exports = {}; __export(src_exports, { ChannelObserver: () => ChannelObserver, SharedWebChannel: () => SharedWebChannel }); module.exports = __toCommonJS(src_exports); // node_modules/tsup/assets/cjs_shims.js var getImportMetaUrl = () => typeof document === "undefined" ? new URL("file:" + __filename).href : document.currentScript && document.currentScript.src || new URL("main.js", document.baseURI).href; var importMetaUrl = /* @__PURE__ */ getImportMetaUrl(); // src/simple-subject.ts var SimpleSubject = class { observers = /* @__PURE__ */ new Map(); subscribe(observer, key = "default") { if (this.observers.has(key)) { this.observers.get(key)?.push(observer); } else { this.observers.set(key, [observer]); } } unsubscribe(observer, key = "default") { const observersForKey = this.observers.get(key); if (!observersForKey) return; const observerIndex = observersForKey.indexOf(observer); if (observerIndex === -1) return; observersForKey.splice(observerIndex, 1); } update(data) { for (const [key, observerGroup] of this.observers.entries()) { if (data.key === "all" || data.key === key || data.key === void 0 && key === "default") { for (const observer of observerGroup) { observer.update(data); } } } } }; var simple_subject_default = SimpleSubject; // src/shared-web-channel.ts var callbacks = /* @__PURE__ */ new Map(); var isSupported = true; var SharedWebChannel = class { worker; subject; connections = 1; connectionsUpdateCallback; /** * Constructs a new SharedWebChannel instance. If you'll need more then one * instance throughout your application, it is recommended to provide a name. * * When omitted, the name will default to "default-shared-worker". * */ constructor(name = "default-shared-worker") { this.subject = new simple_subject_default(); if (typeof window == "undefined") { return; } try { this.worker = new SharedWorker( new URL("./worker.js", importMetaUrl), { type: "module", name } ); } catch (e) { isSupported = false; console.warn( "The shared worker module feature doesn't appear to be supported in this environment" ); return; } const forwardUpdate = (data) => { this.updateObservers(data); }; const forwardConnectionUpdate = (data) => { this.connections = data.channelData.connections; if (this.connectionsUpdateCallback) { this.connectionsUpdateCallback(data.channelData.connections); } }; this.worker.port.onmessage = function(event) { const receivedMessage = event.data; console.debug( "message received from shared worker: ", receivedMessage ); if (receivedMessage.type === "callback") { const callback = callbacks.get(receivedMessage.callbackKey); if (callback) { callback(receivedMessage.payload); } } if (receivedMessage.type === "observer") { forwardUpdate(receivedMessage); } if (receivedMessage.type === "internal") { forwardConnectionUpdate(receivedMessage); } }; window.addEventListener("beforeunload", () => { this.terminate(); }); window.addEventListener("unload", () => { this.terminate(); }); window.addEventListener("pagehide", () => { this.terminate(); }); this.worker.addEventListener("close", () => { this.terminate(); }); } updateObservers(data) { this.subject.update(data); } /** * Sends a `UserMessage` object to the SharedWorker * so it can be forwarded to other active channels. * * @example * * channel.sendMessage({ * //type: "callback" to trigger a callback function with corresponding callbackKey or "observer" to update one or more ChannelObservers. * type: "callback", * // action: "broadcast" to send to all OTHER app instances or "all" to send to all. * action: "broadcast", * // payload: optional; in "callback" mode this will be your callback's input * payload: "bg-red-500", * callbackKey: "set-bg-color", *}); * */ sendMessage(message) { if (!isSupported) { console.warn( "The shared worker module feature doesn't appear to be supported in this environment" ); if (message.type === "callback" && message.action === "all") { const callback = callbacks.get(message.callbackKey); if (callback) { callback(message.payload); } } if (message.type === "observer" && message.action === "all") { this.updateObservers(message); } return; } this.worker?.port.postMessage(message); } /** * Registers a callback with a key in a Map object. When a message sent with type `callback` * is received the `SharedWebChannel` will look for a callback with the corresponding * `callbackKey`, and -if found- execute it with the value in `payload` as input. * * @example * * channel.registerCallback("set-bg-color", setBgColor); * */ registerCallback(key, callback) { callbacks.set(key, callback); } /** * Registers a callback to be executed when the number of open connections * (the number of open browser session in different tabs/windows) changes. * * @example * * channel.onConnectionsUpdate(setInstances); * */ onConnectionsUpdate(callback) { this.connectionsUpdateCallback = callback; } terminate() { console.debug("terminating port connection"); this.worker?.port.postMessage({ type: "close" }); this.worker?.port.close(); this.worker = void 0; window.removeEventListener("beforeunload", () => { this.terminate(); }); window.removeEventListener("unload", () => { this.terminate(); }); window.removeEventListener("pagehide", () => { this.terminate(); }); } }; // src/channel-observer.ts var ChannelObserver = class { onUpdate; /** * Constructs a new `ChannelObserver` instance, that immediately subscribes to the provided channel's subject. * When the `SharedWebChannel` receives a message with action set to `observer`, it will update it's subject's subscribed observers. * If a `key` is provided then only messages with that key or `key: "all"` will trigger it's `onUpdate` function. * * @example * * const observer = new ChannelObserver(channel, (message) => { * const payload = message.payload as MyCustomType; * * if (payload) { * handlePayload(payload); * } * }); * * //and to cleanup the observer: * * channel.subject.unsubscribe(observer); */ constructor(channel, onUpdate, key) { this.onUpdate = onUpdate; channel.subject.subscribe(this, key); } update = (data) => { this.onUpdate(data); }; }; // Annotate the CommonJS export names for ESM import in node: 0 && (module.exports = { ChannelObserver, SharedWebChannel }); //# sourceMappingURL=index.js.map