UNPKG

@tanstack/devtools-event-client

Version:

TanStack Event Client is a lightweight event client for TanStack Devtools event bus.

1 lines • 13.4 kB
{"version":3,"file":"plugin.cjs","names":["#connected","#connecting","#queuedEvents","#eventTarget","#onConnected","#retryCount","#maxRetries","#retryConnection","#failedToConnect","#pluginId","#enabled","#debug","#connectIntervalId","#connectEveryMs","#internalEventTarget","#connectFunction"],"sources":["../../src/plugin.ts"],"sourcesContent":["import type { AllDevtoolsEvents, TanStackDevtoolsEvent } from './types'\n\ndeclare global {\n var __TANSTACK_EVENT_TARGET__: EventTarget | null\n}\n\nexport class EventClient<TEventMap extends Record<string, any>> {\n #enabled = true\n #pluginId: string\n #eventTarget: () => EventTarget\n #debug: boolean\n #queuedEvents: Array<TanStackDevtoolsEvent<string, any>>\n #connected: boolean\n #connectIntervalId: number | null\n #connectEveryMs: number\n #retryCount = 0\n #maxRetries = 5\n #connecting = false\n #failedToConnect = false\n #internalEventTarget: EventTarget | null = null\n\n #onConnected = () => {\n this.debugLog('Connected to event bus')\n this.#connected = true\n this.#connecting = false\n this.debugLog('Emitting queued events', this.#queuedEvents)\n this.#queuedEvents.forEach((event) => this.emitEventToBus(event))\n this.#queuedEvents = []\n this.stopConnectLoop()\n this.#eventTarget().removeEventListener(\n 'tanstack-connect-success',\n this.#onConnected,\n )\n }\n // fired off right away and then at intervals\n #retryConnection = () => {\n if (this.#retryCount < this.#maxRetries) {\n this.#retryCount++\n this.dispatchCustomEvent('tanstack-connect', {})\n\n return\n }\n this.#eventTarget().removeEventListener(\n 'tanstack-connect',\n this.#retryConnection,\n )\n this.#failedToConnect = true\n this.debugLog('Max retries reached, giving up on connection')\n this.stopConnectLoop()\n }\n\n // This is run to register connection handlers on first emit attempt\n #connectFunction = () => {\n if (this.#connecting) return\n this.#connecting = true\n this.#eventTarget().addEventListener(\n 'tanstack-connect-success',\n this.#onConnected,\n )\n this.#retryConnection()\n }\n\n constructor({\n pluginId,\n debug = false,\n enabled = true,\n reconnectEveryMs = 300,\n }: {\n pluginId: string\n debug?: boolean\n reconnectEveryMs?: number\n enabled?: boolean\n }) {\n this.#pluginId = pluginId\n this.#enabled = enabled\n this.#eventTarget = this.getGlobalTarget\n this.#debug = debug\n this.debugLog(' Initializing event subscription for plugin', this.#pluginId)\n this.#queuedEvents = []\n this.#connected = false\n this.#failedToConnect = false\n this.#connectIntervalId = null\n this.#connectEveryMs = reconnectEveryMs\n }\n\n private startConnectLoop() {\n // if connected, trying to connect, or the internalId is already set, do nothing\n if (this.#connectIntervalId !== null || this.#connected) return\n this.debugLog(`Starting connect loop (every ${this.#connectEveryMs}ms)`)\n\n this.#connectIntervalId = setInterval(\n this.#retryConnection,\n this.#connectEveryMs,\n ) as unknown as number\n }\n\n private stopConnectLoop() {\n this.#connecting = false\n\n if (this.#connectIntervalId === null) {\n return\n }\n clearInterval(this.#connectIntervalId)\n this.#connectIntervalId = null\n this.#queuedEvents = []\n this.debugLog('Stopped connect loop')\n }\n\n private debugLog(...args: Array<any>) {\n if (this.#debug) {\n console.log(`🌴 [tanstack-devtools:${this.#pluginId}-plugin]`, ...args)\n }\n }\n private getGlobalTarget() {\n // server one is the global event target\n if (\n typeof globalThis !== 'undefined' &&\n globalThis.__TANSTACK_EVENT_TARGET__\n ) {\n this.debugLog('Using global event target')\n return globalThis.__TANSTACK_EVENT_TARGET__\n }\n // CLient event target is the browser window object\n if (\n typeof window !== 'undefined' &&\n typeof window.addEventListener !== 'undefined'\n ) {\n this.debugLog('Using window as event target')\n\n return window\n }\n // Protect against non-web environments like react-native\n const eventTarget =\n typeof EventTarget !== 'undefined' ? new EventTarget() : undefined\n\n // For non-web environments like react-native\n if (\n typeof eventTarget === 'undefined' ||\n typeof eventTarget.addEventListener === 'undefined'\n ) {\n this.debugLog(\n 'No event mechanism available, running in non-web environment',\n )\n return {\n addEventListener: () => {},\n removeEventListener: () => {},\n dispatchEvent: () => false,\n }\n }\n\n this.debugLog('Using new EventTarget as fallback')\n return eventTarget\n }\n\n getPluginId() {\n return this.#pluginId\n }\n\n private dispatchCustomEventShim(eventName: string, detail: any) {\n try {\n const event = new Event(eventName, {\n detail: detail,\n } as any)\n this.#eventTarget().dispatchEvent(event)\n } catch (e) {\n this.debugLog('Failed to dispatch shim event')\n }\n }\n\n private dispatchCustomEvent(eventName: string, detail?: any) {\n try {\n this.#eventTarget().dispatchEvent(new CustomEvent(eventName, { detail }))\n } catch (e) {\n this.dispatchCustomEventShim(eventName, detail)\n }\n }\n\n private emitEventToBus(event: TanStackDevtoolsEvent<string, any>) {\n this.debugLog('Emitting event to client bus', event)\n this.dispatchCustomEvent('tanstack-dispatch-event', event)\n }\n\n createEventPayload<TEvent extends keyof TEventMap & string>(\n eventSuffix: TEvent,\n payload: TEventMap[TEvent],\n ) {\n return {\n type: `${this.#pluginId}:${eventSuffix}`,\n payload,\n pluginId: this.#pluginId,\n }\n }\n emit<TEvent extends keyof TEventMap & string>(\n eventSuffix: TEvent,\n payload: TEventMap[TEvent],\n ) {\n if (!this.#enabled) {\n this.debugLog(\n 'Event bus client is disabled, not emitting event',\n eventSuffix,\n payload,\n )\n return\n }\n if (this.#internalEventTarget) {\n this.debugLog(\n 'Emitting event to internal event target',\n eventSuffix,\n payload,\n )\n this.#internalEventTarget.dispatchEvent(\n new CustomEvent(`${this.#pluginId}:${eventSuffix}`, {\n detail: this.createEventPayload(eventSuffix, payload),\n }),\n )\n }\n\n if (this.#failedToConnect) {\n this.debugLog('Previously failed to connect, not emitting to bus')\n return\n }\n // wait to connect to the bus\n if (!this.#connected) {\n this.debugLog('Bus not available, will be pushed as soon as connected')\n this.#queuedEvents.push(this.createEventPayload(eventSuffix, payload))\n // start connection to event bus\n if (typeof CustomEvent !== 'undefined' && !this.#connecting) {\n this.#connectFunction()\n this.startConnectLoop()\n }\n return\n }\n // emit right now\n return this.emitEventToBus(this.createEventPayload(eventSuffix, payload))\n }\n\n on<TEvent extends keyof TEventMap & string>(\n eventSuffix: TEvent,\n cb: (event: TanStackDevtoolsEvent<TEvent, TEventMap[TEvent]>) => void,\n options?: {\n withEventTarget?: boolean\n },\n ) {\n const withEventTarget = options?.withEventTarget ?? false\n const eventName = `${this.#pluginId}:${eventSuffix}` as const\n if (withEventTarget) {\n if (!this.#internalEventTarget) {\n this.#internalEventTarget = new EventTarget()\n }\n this.#internalEventTarget.addEventListener(eventName, (e) => {\n cb((e as CustomEvent).detail)\n })\n }\n if (!this.#enabled) {\n this.debugLog(\n 'Event bus client is disabled, not registering event',\n eventName,\n )\n return () => {}\n }\n const handler = (e: Event) => {\n this.debugLog('Received event from bus', (e as CustomEvent).detail)\n cb((e as CustomEvent).detail)\n }\n this.#eventTarget().addEventListener(eventName, handler)\n this.debugLog('Registered event to bus', eventName)\n return () => {\n if (withEventTarget) {\n this.#internalEventTarget?.removeEventListener(eventName, handler)\n }\n this.#eventTarget().removeEventListener(eventName, handler)\n }\n }\n\n onAll(cb: (event: TanStackDevtoolsEvent<string, any>) => void) {\n if (!this.#enabled) {\n this.debugLog('Event bus client is disabled, not registering event')\n return () => {}\n }\n\n const handler = (e: Event) => {\n const event = (e as CustomEvent).detail\n\n cb(event)\n }\n this.#eventTarget().addEventListener('tanstack-devtools-global', handler)\n return () =>\n this.#eventTarget().removeEventListener(\n 'tanstack-devtools-global',\n handler,\n )\n }\n onAllPluginEvents(cb: (event: AllDevtoolsEvents<TEventMap>) => void) {\n if (!this.#enabled) {\n this.debugLog('Event bus client is disabled, not registering event')\n return () => {}\n }\n const handler = (e: Event) => {\n const event = (e as CustomEvent).detail\n if (this.#pluginId && event.pluginId !== this.#pluginId) {\n return\n }\n cb(event)\n }\n this.#eventTarget().addEventListener('tanstack-devtools-global', handler)\n return () =>\n this.#eventTarget().removeEventListener(\n 'tanstack-devtools-global',\n handler,\n )\n }\n}\n"],"mappings":"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"}