/* * Adapted from @graphql-yoga/subscription * Original source: https://github.com/graphql-hive/graphql-yoga * License: MIT * * Copyright (c) 2024 The Guild, GraphQL Yoga Contributors * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ import { Repeater } from "./repeater.js"; import type { TypedEventTarget, TypedEvent, EventEnvelope } from "./types.js"; export type PubSubEventMap = { [eventType: string]: unknown; }; export type PubSubEvent< TEventMap extends PubSubEventMap, TType extends Extract = Extract, > = TypedEvent>; export type PubSubEventTarget = TypedEventTarget< PubSubEvent >; export type PubSubConfig = { eventTarget?: PubSubEventTarget; }; export type PubSub = { publish>( type: TType, id: string, payload: TEventMap[TType], ): void; subscribe>( type: TType, id: string, ): Repeater>; }; export function createPubSub( config?: PubSubConfig, ): PubSub { const target = config?.eventTarget ?? (new EventTarget() as PubSubEventTarget); return { publish>( type: TType, id: string, payload: TEventMap[TType], ) { if (type.startsWith("__")) { throw new Error( `Event types starting with "__" are reserved for adapter control messages. Received: "${type}"`, ); } const envelope: EventEnvelope = { type, id, payload }; const topic = `${type}:${id}`; const event = new CustomEvent(topic, { detail: envelope }) as PubSubEvent< TEventMap, TType >; target.dispatchEvent(event); }, subscribe>( type: TType, id: string, ): Repeater> { const topic = `${type}:${id}`; return new Repeater(function subscriptionRepeater( next: (value: unknown) => Promise, stop: Promise, ) { function pubsubEventListener(event: CustomEvent) { next(event.detail); } stop.then(function subscriptionRepeaterStopHandler() { target.removeEventListener(topic as TType, pubsubEventListener as EventListener); }); target.addEventListener(topic as TType, pubsubEventListener as EventListener, undefined); }) as Repeater>; }, }; }