// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. // TODO(petamoriken): enable prefer-primordials for node polyfills // deno-lint-ignore-file prefer-primordials import { ERR_INVALID_ARG_TYPE } from "ext:deno_node/internal/errors.ts"; import { validateFunction } from "ext:deno_node/internal/validators.mjs"; import { nextTick } from "node:process"; type Subscriber = (message: unknown, name?: string) => void; export class Channel { _subscribers: Subscriber[]; name: string; constructor(name: string) { this._subscribers = []; this.name = name; } publish(message: unknown) { for (const subscriber of this._subscribers) { try { subscriber(message, this.name); } catch (err) { nextTick(() => { throw err; }); } } } subscribe(subscription: Subscriber) { validateFunction(subscription, "subscription"); this._subscribers.push(subscription); } unsubscribe(subscription: Subscriber) { if (!this._subscribers.includes(subscription)) { return false; } this._subscribers.splice(this._subscribers.indexOf(subscription), 1); return true; } get hasSubscribers() { return this._subscribers.length > 0; } } const channels: Record = {}; export function channel(name: string) { if (typeof name !== "string" && typeof name !== "symbol") { throw new ERR_INVALID_ARG_TYPE("channel", ["string", "symbol"], name); } if (!Object.hasOwn(channels, name)) { channels[name] = new Channel(name); } return channels[name]; } export function hasSubscribers(name: string) { if (!Object.hasOwn(channels, name)) { return false; } return channels[name].hasSubscribers; } export function subscribe(name: string, subscription: Subscriber) { const c = channel(name); return c.subscribe(subscription); } export function unsubscribe(name: string, subscription: Subscriber) { const c = channel(name); return c.unsubscribe(subscription); } export default { channel, hasSubscribers, subscribe, unsubscribe, Channel, };