@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
JavaScript
;
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