notif-agent 1.0.0 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -79,6 +79,10 @@ const httpServer = http.createServer((req, res) => {
79
79
  httpServer.listen(3000);
80
80
  ```
81
81
 
82
+ > **CORS:** `notif-agent` sets no CORS headers. If the client runs on another origin, set `Access-Control-Allow-Origin` yourself (a specific origin, not `*`, when using `withCredentials`).
83
+ >
84
+ > **Channels:** a client with no channel list gets every **public** channel. `private-*` and `presence-*` channels are only delivered after you grant them via `options.channels` or `server.subscribeClient()`.
85
+
82
86
  #### Next.js App Router (`app/api/sse/route.ts`)
83
87
 
84
88
  ```typescript
package/dist/index.js CHANGED
@@ -420,6 +420,7 @@ var ConnectionManager = class {
420
420
  }
421
421
  handleConnectionFailure(error) {
422
422
  this.log("Connection failed or dropped", error);
423
+ this.clientId = null;
423
424
  this.closeActiveTransport();
424
425
  this.clearHeartbeat();
425
426
  if (this.isManuallyClosed) {
@@ -559,11 +560,6 @@ var NotifAgent = class {
559
560
  this.connection.bind("handshake", () => {
560
561
  triggerChannelSubscriptions();
561
562
  });
562
- this.connection.bind("connected", () => {
563
- if (this.connection.clientId || !this.options.authEndpoint) {
564
- triggerChannelSubscriptions();
565
- }
566
- });
567
563
  }
568
564
  /**
569
565
  * Subscribe to a channel.
@@ -582,7 +578,7 @@ var NotifAgent = class {
582
578
  if (!channel) {
583
579
  channel = new Channel(channelName);
584
580
  this.channels.set(channelName, channel);
585
- if (this.connection.state === "connected") {
581
+ if (this.connection.state === "connected" && this.connection.clientId) {
586
582
  setTimeout(() => {
587
583
  const isPrivate = channelName.startsWith("private-") || channelName.startsWith("presence-");
588
584
  if (isPrivate && this.options.authEndpoint) {
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/client/EventEmitter.ts","../src/client/Channel.ts","../src/client/ConnectionManager.ts","../src/client/NotifAgent.ts","../src/index.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["import { EventCallback, GlobalEventCallback, EventMetadata } from './types';\n\nexport class EventEmitter {\n private listeners: Map<string, Set<EventCallback>> = new Map();\n private globalListeners: Set<GlobalEventCallback> = new Set();\n\n /**\n * Bind a callback to a specific event name.\n */\n public bind<T = any>(event: string, callback: EventCallback<T>): this {\n if (!this.listeners.has(event)) {\n this.listeners.set(event, new Set());\n }\n this.listeners.get(event)!.add(callback);\n return this;\n }\n\n /**\n * Unbind a callback from a specific event name, or all callbacks if callback is omitted.\n * If both event and callback are omitted, all listeners are removed.\n */\n public unbind<T = any>(event?: string, callback?: EventCallback<T>): this {\n if (!event) {\n this.listeners.clear();\n return this;\n }\n\n if (!callback) {\n this.listeners.delete(event);\n return this;\n }\n\n const set = this.listeners.get(event);\n if (set) {\n set.delete(callback);\n if (set.size === 0) {\n this.listeners.delete(event);\n }\n }\n\n return this;\n }\n\n /**\n * Bind a callback to ALL incoming events on this emitter.\n */\n public bind_global(callback: GlobalEventCallback): this {\n this.globalListeners.add(callback);\n return this;\n }\n\n /**\n * Unbind a global callback, or all global callbacks if callback is omitted.\n */\n public unbind_global(callback?: GlobalEventCallback): this {\n if (!callback) {\n this.globalListeners.clear();\n return this;\n }\n this.globalListeners.delete(callback);\n return this;\n }\n\n /**\n * Emit an event to registered listeners and global listeners.\n */\n public emit<T = any>(event: string, data: T, metadata?: EventMetadata): void {\n const set = this.listeners.get(event);\n if (set) {\n for (const cb of Array.from(set)) {\n try {\n cb(data, metadata);\n } catch (err) {\n console.error(`[notif-agent] Error in listener for event \"${event}\":`, err);\n }\n }\n }\n\n for (const globalCb of Array.from(this.globalListeners)) {\n try {\n globalCb(event, data, metadata);\n } catch (err) {\n console.error(`[notif-agent] Error in global listener for event \"${event}\":`, err);\n }\n }\n }\n\n /**\n * Clear all listeners.\n */\n public clear(): void {\n this.listeners.clear();\n this.globalListeners.clear();\n }\n}\n","import { EventEmitter } from './EventEmitter';\nimport { ChannelSubscriptionState, EventCallback, GlobalEventCallback, EventMetadata } from './types';\n\nexport class Channel {\n public readonly name: string;\n public state: ChannelSubscriptionState = 'subscribing';\n private emitter: EventEmitter = new EventEmitter();\n\n constructor(name: string) {\n this.name = name;\n }\n\n /**\n * Whether the channel is currently successfully subscribed.\n */\n public get subscribed(): boolean {\n return this.state === 'subscribed';\n }\n\n /**\n * Bind an event listener to this channel.\n *\n * Example:\n * ```ts\n * channel.bind('new-comment', (data) => {\n * console.log(data);\n * });\n * ```\n */\n public bind<T = any>(event: string, callback: EventCallback<T>): this {\n this.emitter.bind(event, callback);\n return this;\n }\n\n /**\n * Unbind an event listener from this channel.\n */\n public unbind<T = any>(event?: string, callback?: EventCallback<T>): this {\n this.emitter.unbind(event, callback);\n return this;\n }\n\n /**\n * Bind a global listener to all events received on this channel.\n *\n * Example:\n * ```ts\n * channel.bind_global((eventName, data) => {\n * console.log(`Event ${eventName}:`, data);\n * });\n * ```\n */\n public bind_global(callback: GlobalEventCallback): this {\n this.emitter.bind_global(callback);\n return this;\n }\n\n /**\n * Unbind a global listener from this channel.\n */\n public unbind_global(callback?: GlobalEventCallback): this {\n this.emitter.unbind_global(callback);\n return this;\n }\n\n /**\n * Internal method to dispatch an event onto this channel.\n */\n public handleEvent<T = any>(event: string, data: T, metadata?: EventMetadata): void {\n this.emitter.emit(event, data, {\n ...metadata,\n event,\n channel: this.name,\n });\n }\n\n /**\n * Internal method to update subscription status and fire Pusher-compatible subscription events.\n */\n public setSubscribed(data?: any): void {\n this.state = 'subscribed';\n // Pusher compatibility and standard notif-agent event\n this.handleEvent('subscription_succeeded', data ?? {});\n this.handleEvent('pusher:subscription_succeeded', data ?? {});\n }\n\n /**\n * Internal method to set subscription error.\n */\n public setSubscriptionError(error: any): void {\n this.state = 'error';\n this.handleEvent('subscription_error', error);\n this.handleEvent('pusher:subscription_error', error);\n }\n\n /**\n * Internal method called when channel is unsubscribed.\n */\n public setUnsubscribed(): void {\n this.state = 'unsubscribed';\n this.emitter.clear();\n }\n}\n","import { EventEmitter } from './EventEmitter';\nimport {\n ConnectionState,\n NotifAgentOptions,\n SSEMessagePayload,\n StateChangePayload,\n} from './types';\n\nexport class ConnectionManager {\n public state: ConnectionState = 'initialized';\n public clientId: string | null = null;\n\n /**\n * Pusher-compatible alias for clientId.\n */\n public get socket_id(): string | null {\n return this.clientId;\n }\n\n private emitter: EventEmitter = new EventEmitter();\n private options: Required<Omit<NotifAgentOptions, 'headers' | 'queryParams' | 'url' | 'authEndpoint' | 'auth'>> & {\n url: string;\n headers?: NotifAgentOptions['headers'];\n queryParams?: NotifAgentOptions['queryParams'];\n authEndpoint?: NotifAgentOptions['authEndpoint'];\n auth?: NotifAgentOptions['auth'];\n };\n\n private eventSource: EventSource | null = null;\n private abortController: AbortController | null = null;\n private reconnectAttempts = 0;\n private reconnectTimer: ReturnType<typeof setTimeout> | null = null;\n private heartbeatTimer: ReturnType<typeof setTimeout> | null = null;\n private isManuallyClosed = false;\n\n constructor(url: string, options: NotifAgentOptions = {}) {\n this.options = {\n url,\n headers: options.headers,\n queryParams: options.queryParams,\n withCredentials: options.withCredentials ?? false,\n transport: options.transport ?? 'auto',\n reconnectBaseDelay: options.reconnectBaseDelay ?? 1000,\n reconnectMaxDelay: options.reconnectMaxDelay ?? 30000,\n maxReconnectAttempts: options.maxReconnectAttempts ?? Infinity,\n heartbeatTimeout: options.heartbeatTimeout ?? 45000,\n autoConnect: options.autoConnect ?? true,\n debug: options.debug ?? false,\n };\n\n if (this.options.autoConnect) {\n // Connect on next tick so callers have time to bind connection listeners\n setTimeout(() => {\n if (!this.isManuallyClosed && this.state === 'initialized') {\n this.connect();\n }\n }, 0);\n }\n }\n\n public bind<T = any>(event: string, callback: (data: T) => void): this {\n this.emitter.bind(event, callback);\n return this;\n }\n\n public unbind<T = any>(event?: string, callback?: (data: T) => void): this {\n this.emitter.unbind(event, callback);\n return this;\n }\n\n public connect(): void {\n this.isManuallyClosed = false;\n this.clearTimers();\n\n if (this.state === 'connected' || this.state === 'connecting') {\n return;\n }\n\n this.transition('connecting');\n this.establishConnection();\n }\n\n public disconnect(): void {\n this.isManuallyClosed = true;\n this.clientId = null;\n this.clearTimers();\n this.closeActiveTransport();\n this.transition('disconnected');\n }\n\n private transition(nextState: ConnectionState): void {\n if (this.state === nextState) return;\n\n const previous = this.state;\n this.state = nextState;\n\n this.log(`State transition: ${previous} -> ${nextState}`);\n\n const payload: StateChangePayload = { previous, current: nextState };\n this.emitter.emit('state_change', payload);\n this.emitter.emit(nextState, payload);\n }\n\n private async establishConnection(): Promise<void> {\n try {\n const headers = await this.resolveHeaders();\n const hasCustomHeaders = headers && Object.keys(headers).length > 0;\n const shouldUseFetch =\n this.options.transport === 'fetch' ||\n (this.options.transport === 'auto' && (hasCustomHeaders || typeof EventSource === 'undefined'));\n\n const fullUrl = this.buildUrl();\n\n if (shouldUseFetch) {\n this.log(`Connecting via Fetch SSE stream: ${fullUrl}`);\n this.connectViaFetch(fullUrl, headers);\n } else {\n this.log(`Connecting via native EventSource: ${fullUrl}`);\n this.connectViaEventSource(fullUrl);\n }\n } catch (err) {\n this.handleConnectionFailure(err);\n }\n }\n\n private connectViaEventSource(url: string): void {\n this.closeActiveTransport();\n\n try {\n const es = new EventSource(url, {\n withCredentials: this.options.withCredentials,\n });\n this.eventSource = es;\n\n es.onopen = () => {\n this.handleConnectionSuccess();\n };\n\n es.onmessage = (e: MessageEvent) => {\n this.resetHeartbeat();\n this.parseAndDispatchMessage('message', e.data, e.lastEventId);\n };\n\n es.onerror = (err) => {\n this.log('EventSource error occurred', err);\n if (es.readyState === EventSource.CLOSED) {\n this.handleConnectionFailure(err);\n } else {\n // EventSource is in CONNECTING state attempting internal reconnect\n this.transition('unavailable');\n }\n };\n } catch (err) {\n this.handleConnectionFailure(err);\n }\n }\n\n private async connectViaFetch(url: string, headers?: Record<string, string>): Promise<void> {\n this.closeActiveTransport();\n this.abortController = new AbortController();\n\n try {\n const requestHeaders: Record<string, string> = {\n Accept: 'text/event-stream',\n 'Cache-Control': 'no-cache',\n ...headers,\n };\n\n const response = await fetch(url, {\n method: 'GET',\n headers: requestHeaders,\n credentials: this.options.withCredentials ? 'include' : 'same-origin',\n signal: this.abortController.signal,\n });\n\n if (!response.ok) {\n throw new Error(`SSE connection failed with HTTP status ${response.status}: ${response.statusText}`);\n }\n\n if (!response.body) {\n throw new Error('Response body is null or stream reading is not supported');\n }\n\n this.handleConnectionSuccess();\n\n const reader = response.body.getReader();\n const decoder = new TextDecoder('utf-8');\n let buffer = '';\n\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n\n this.resetHeartbeat();\n buffer += decoder.decode(value, { stream: true });\n\n // Process SSE frames (blocks ending with double newline)\n const parts = buffer.split(/\\r\\n\\r\\n|\\n\\n/);\n buffer = parts.pop() ?? '';\n\n for (const part of parts) {\n if (part.trim().length > 0) {\n this.processSSEFrame(part);\n }\n }\n }\n\n // Stream closed normally from server\n if (!this.isManuallyClosed) {\n this.handleConnectionFailure(new Error('SSE stream ended by server'));\n }\n } catch (err: any) {\n if (err.name === 'AbortError' || this.isManuallyClosed) {\n // Expected cancellation\n return;\n }\n this.handleConnectionFailure(err);\n }\n }\n\n private processSSEFrame(frame: string): void {\n const lines = frame.split(/\\r\\n|\\r|\\n/);\n let event = 'message';\n let data = '';\n let id: string | undefined;\n\n for (const line of lines) {\n if (line.startsWith(':')) {\n // Comment / ping heartbeat\n this.resetHeartbeat();\n continue;\n }\n\n if (line.startsWith('event:')) {\n event = line.slice(6).trim();\n } else if (line.startsWith('data:')) {\n const value = line.slice(5).trim();\n data += (data ? '\\n' : '') + value;\n } else if (line.startsWith('id:')) {\n id = line.slice(3).trim();\n } else if (line.startsWith('retry:')) {\n const retryMs = parseInt(line.slice(6).trim(), 10);\n if (!isNaN(retryMs)) {\n this.options.reconnectBaseDelay = retryMs;\n }\n }\n }\n\n if (data || event !== 'message') {\n this.parseAndDispatchMessage(event, data, id);\n }\n }\n\n private parseAndDispatchMessage(sseEvent: string, rawData: string, id?: string): void {\n let parsedData: any = rawData;\n let channel: string | undefined;\n let eventName = sseEvent;\n\n if (rawData) {\n try {\n const json = JSON.parse(rawData);\n // Check if server packaged channel & event inside json payload\n if (typeof json === 'object' && json !== null) {\n if (json.channel && typeof json.channel === 'string') {\n channel = json.channel;\n }\n if (json.event && typeof json.event === 'string') {\n eventName = json.event;\n }\n if ('data' in json) {\n parsedData = json.data;\n } else {\n parsedData = json;\n }\n } else {\n parsedData = json;\n }\n } catch {\n parsedData = rawData;\n }\n }\n\n // Handle handshake event from NotifServer\n if (eventName === 'connected' && parsedData && typeof parsedData === 'object' && parsedData.clientId) {\n this.clientId = parsedData.clientId;\n this.emitter.emit('handshake', parsedData);\n }\n\n // Handle heartbeat pings\n if (eventName === 'ping' || eventName === 'heartbeat' || eventName === ':ping') {\n this.resetHeartbeat();\n return;\n }\n\n const payload: SSEMessagePayload = {\n channel,\n event: eventName,\n data: parsedData,\n id,\n timestamp: Date.now(),\n };\n\n this.emitter.emit('message', payload);\n }\n\n private handleConnectionSuccess(): void {\n this.reconnectAttempts = 0;\n this.transition('connected');\n this.resetHeartbeat();\n }\n\n private handleConnectionFailure(error: any): void {\n this.log('Connection failed or dropped', error);\n this.closeActiveTransport();\n this.clearHeartbeat();\n\n if (this.isManuallyClosed) {\n return;\n }\n\n this.emitter.emit('error', error);\n\n this.reconnectAttempts++;\n if (this.reconnectAttempts > this.options.maxReconnectAttempts) {\n this.log(`Max reconnect attempts (${this.options.maxReconnectAttempts}) exceeded. State -> failed`);\n this.transition('failed');\n return;\n }\n\n this.transition('unavailable');\n this.scheduleReconnect();\n }\n\n private scheduleReconnect(): void {\n if (this.reconnectTimer) clearTimeout(this.reconnectTimer);\n\n // Exponential backoff with jitter\n const delay = Math.min(\n this.options.reconnectBaseDelay * Math.pow(1.5, this.reconnectAttempts - 1),\n this.options.reconnectMaxDelay\n );\n const jitter = delay * 0.2 * (Math.random() - 0.5); // +/- 10%\n const totalDelay = Math.max(100, Math.round(delay + jitter));\n\n this.log(`Scheduling reconnect in ${totalDelay}ms (attempt #${this.reconnectAttempts})`);\n\n this.reconnectTimer = setTimeout(() => {\n if (!this.isManuallyClosed && this.state !== 'connected') {\n this.transition('connecting');\n this.establishConnection();\n }\n }, totalDelay);\n }\n\n private resetHeartbeat(): void {\n this.clearHeartbeat();\n\n if (this.options.heartbeatTimeout <= 0) return;\n\n this.heartbeatTimer = setTimeout(() => {\n this.log(`Heartbeat timeout (${this.options.heartbeatTimeout}ms) exceeded. Connection dropped.`);\n this.handleConnectionFailure(new Error('Heartbeat timeout'));\n }, this.options.heartbeatTimeout);\n }\n\n private clearHeartbeat(): void {\n if (this.heartbeatTimer) {\n clearTimeout(this.heartbeatTimer);\n this.heartbeatTimer = null;\n }\n }\n\n private clearTimers(): void {\n this.clearHeartbeat();\n if (this.reconnectTimer) {\n clearTimeout(this.reconnectTimer);\n this.reconnectTimer = null;\n }\n }\n\n private closeActiveTransport(): void {\n if (this.eventSource) {\n try {\n this.eventSource.close();\n } catch {}\n this.eventSource = null;\n }\n\n if (this.abortController) {\n try {\n this.abortController.abort();\n } catch {}\n this.abortController = null;\n }\n }\n\n private buildUrl(): string {\n const url = new URL(this.options.url, typeof window !== 'undefined' ? window.location.href : 'http://localhost');\n if (this.options.queryParams) {\n for (const [key, value] of Object.entries(this.options.queryParams)) {\n if (value !== undefined && value !== null) {\n url.searchParams.set(key, String(value));\n }\n }\n }\n return url.toString();\n }\n\n private async resolveHeaders(): Promise<Record<string, string> | undefined> {\n if (!this.options.headers) return undefined;\n if (typeof this.options.headers === 'function') {\n return await this.options.headers();\n }\n return this.options.headers;\n }\n\n private log(...args: any[]): void {\n if (this.options.debug) {\n console.log('[notif-agent:connection]', ...args);\n }\n }\n}\n","import { Channel } from './Channel';\nimport { ConnectionManager } from './ConnectionManager';\nimport { EventEmitter } from './EventEmitter';\nimport {\n EventCallback,\n GlobalEventCallback,\n NotifAgentOptions,\n SSEMessagePayload,\n} from './types';\n\nexport class NotifAgent {\n public readonly connection: ConnectionManager;\n private options: NotifAgentOptions;\n private channels: Map<string, Channel> = new Map();\n private globalEmitter: EventEmitter = new EventEmitter();\n\n /**\n * Unique client connection ID provided by NotifServer handshake.\n */\n public get clientId(): string | null {\n return this.connection.clientId;\n }\n\n /**\n * Pusher-compatible alias for clientId.\n */\n public get socket_id(): string | null {\n return this.connection.clientId;\n }\n\n constructor(urlOrOptions: string | NotifAgentOptions, maybeOptions?: NotifAgentOptions) {\n let options: NotifAgentOptions;\n\n if (typeof urlOrOptions === 'string') {\n options = { ...(maybeOptions ?? {}), url: urlOrOptions };\n } else {\n options = urlOrOptions;\n }\n\n if (!options.url) {\n throw new Error('[notif-agent] SSE URL must be specified');\n }\n\n this.options = options;\n this.connection = new ConnectionManager(options.url, options);\n\n // Listen to connection messages to route events\n this.connection.bind<SSEMessagePayload>('message', (payload) => {\n this.routeMessage(payload);\n });\n\n const triggerChannelSubscriptions = () => {\n for (const channel of this.channels.values()) {\n const isPrivate = channel.name.startsWith('private-') || channel.name.startsWith('presence-');\n if (isPrivate && this.options.authEndpoint) {\n this.authorizeChannel(channel);\n } else {\n channel.setSubscribed();\n }\n }\n };\n\n // On handshake with server clientId, authorize channels\n this.connection.bind('handshake', () => {\n triggerChannelSubscriptions();\n });\n\n // On reconnect / connected\n this.connection.bind('connected', () => {\n if (this.connection.clientId || !this.options.authEndpoint) {\n triggerChannelSubscriptions();\n }\n });\n }\n\n /**\n * Subscribe to a channel.\n *\n * Example:\n * ```ts\n * const channel = agent.subscribe('orders');\n * channel.bind('created', (order) => { ... });\n * ```\n */\n public subscribe(channelName: string): Channel {\n if (!channelName) {\n throw new Error('[notif-agent] Channel name cannot be empty');\n }\n\n let channel = this.channels.get(channelName);\n if (!channel) {\n channel = new Channel(channelName);\n this.channels.set(channelName, channel);\n\n // If already connected, mark channel as subscribed or authorize if private\n if (this.connection.state === 'connected') {\n setTimeout(() => {\n const isPrivate = channelName.startsWith('private-') || channelName.startsWith('presence-');\n if (isPrivate && this.options.authEndpoint) {\n this.authorizeChannel(channel!);\n } else {\n channel?.setSubscribed();\n }\n }, 0);\n }\n }\n\n return channel;\n }\n\n /**\n * Internal authorization helper for private/presence channels.\n */\n private async authorizeChannel(channel: Channel): Promise<void> {\n if (!this.options.authEndpoint) {\n channel.setSubscribed();\n return;\n }\n\n try {\n let customHeaders: Record<string, string> = {};\n if (typeof this.options.auth?.headers === 'function') {\n customHeaders = await this.options.auth.headers();\n } else if (this.options.auth?.headers) {\n customHeaders = this.options.auth.headers;\n }\n\n const body = {\n clientId: this.connection.clientId,\n socket_id: this.connection.clientId,\n channel: channel.name,\n ...(this.options.auth?.params ?? {}),\n };\n\n const response = await fetch(this.options.authEndpoint, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n ...customHeaders,\n },\n body: JSON.stringify(body),\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(`[notif-agent] Authorization failed (${response.status}): ${errorText}`);\n }\n\n const authData = await response.json().catch(() => ({}));\n channel.setSubscribed(authData);\n } catch (err) {\n channel.setSubscriptionError(err);\n }\n }\n\n /**\n * Unsubscribe from a channel.\n */\n public unsubscribe(channelName: string): void {\n const channel = this.channels.get(channelName);\n if (channel) {\n channel.setUnsubscribed();\n this.channels.delete(channelName);\n }\n }\n\n /**\n * Get an existing subscribed channel by name, if any.\n */\n public channel(channelName: string): Channel | undefined {\n return this.channels.get(channelName);\n }\n\n /**\n * Return all currently subscribed channels.\n */\n public allChannels(): Channel[] {\n return Array.from(this.channels.values());\n }\n\n /**\n * Bind to an event across all channels or global events.\n *\n * Example:\n * ```ts\n * agent.bind('system-alert', (data) => console.log(data));\n * ```\n */\n public bind<T = any>(event: string, callback: EventCallback<T>): this {\n this.globalEmitter.bind(event, callback);\n return this;\n }\n\n /**\n * Unbind an event listener.\n */\n public unbind<T = any>(event?: string, callback?: EventCallback<T>): this {\n this.globalEmitter.unbind(event, callback);\n return this;\n }\n\n /**\n * Bind to all events regardless of name.\n */\n public bind_global(callback: GlobalEventCallback): this {\n this.globalEmitter.bind_global(callback);\n return this;\n }\n\n /**\n * Unbind a global event listener.\n */\n public unbind_global(callback?: GlobalEventCallback): this {\n this.globalEmitter.unbind_global(callback);\n return this;\n }\n\n /**\n * Connect to the SSE endpoint.\n */\n public connect(): void {\n this.connection.connect();\n }\n\n /**\n * Disconnect from the SSE endpoint.\n */\n public disconnect(): void {\n this.connection.disconnect();\n }\n\n /**\n * Route incoming SSE message to global emitters and specific channels.\n */\n private routeMessage(payload: SSEMessagePayload): void {\n const metadata = {\n channel: payload.channel,\n event: payload.event,\n id: payload.id,\n timestamp: payload.timestamp,\n };\n\n // 1. Dispatch to global agent emitter\n this.globalEmitter.emit(payload.event, payload.data, metadata);\n\n // 2. Dispatch to specific channel if message contains a channel\n if (payload.channel) {\n const targetChannel = this.channels.get(payload.channel);\n if (targetChannel) {\n targetChannel.handleEvent(payload.event, payload.data, metadata);\n }\n } else {\n // If message has no channel specified, notify all active channels\n for (const ch of this.channels.values()) {\n ch.handleEvent(payload.event, payload.data, metadata);\n }\n }\n }\n}\n","import { NotifAgent } from './client/NotifAgent';\n\nexport { NotifAgent } from './client/NotifAgent';\nexport { Channel } from './client/Channel';\nexport { ConnectionManager } from './client/ConnectionManager';\nexport { EventEmitter } from './client/EventEmitter';\nexport * from './client/types';\n\nexport default NotifAgent;\n"]}
1
+ {"version":3,"sources":["../src/client/EventEmitter.ts","../src/client/Channel.ts","../src/client/ConnectionManager.ts","../src/client/NotifAgent.ts","../src/index.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["import { EventCallback, GlobalEventCallback, EventMetadata } from './types';\n\nexport class EventEmitter {\n private listeners: Map<string, Set<EventCallback>> = new Map();\n private globalListeners: Set<GlobalEventCallback> = new Set();\n\n /**\n * Bind a callback to a specific event name.\n */\n public bind<T = any>(event: string, callback: EventCallback<T>): this {\n if (!this.listeners.has(event)) {\n this.listeners.set(event, new Set());\n }\n this.listeners.get(event)!.add(callback);\n return this;\n }\n\n /**\n * Unbind a callback from a specific event name, or all callbacks if callback is omitted.\n * If both event and callback are omitted, all listeners are removed.\n */\n public unbind<T = any>(event?: string, callback?: EventCallback<T>): this {\n if (!event) {\n this.listeners.clear();\n return this;\n }\n\n if (!callback) {\n this.listeners.delete(event);\n return this;\n }\n\n const set = this.listeners.get(event);\n if (set) {\n set.delete(callback);\n if (set.size === 0) {\n this.listeners.delete(event);\n }\n }\n\n return this;\n }\n\n /**\n * Bind a callback to ALL incoming events on this emitter.\n */\n public bind_global(callback: GlobalEventCallback): this {\n this.globalListeners.add(callback);\n return this;\n }\n\n /**\n * Unbind a global callback, or all global callbacks if callback is omitted.\n */\n public unbind_global(callback?: GlobalEventCallback): this {\n if (!callback) {\n this.globalListeners.clear();\n return this;\n }\n this.globalListeners.delete(callback);\n return this;\n }\n\n /**\n * Emit an event to registered listeners and global listeners.\n */\n public emit<T = any>(event: string, data: T, metadata?: EventMetadata): void {\n const set = this.listeners.get(event);\n if (set) {\n for (const cb of Array.from(set)) {\n try {\n cb(data, metadata);\n } catch (err) {\n console.error(`[notif-agent] Error in listener for event \"${event}\":`, err);\n }\n }\n }\n\n for (const globalCb of Array.from(this.globalListeners)) {\n try {\n globalCb(event, data, metadata);\n } catch (err) {\n console.error(`[notif-agent] Error in global listener for event \"${event}\":`, err);\n }\n }\n }\n\n /**\n * Clear all listeners.\n */\n public clear(): void {\n this.listeners.clear();\n this.globalListeners.clear();\n }\n}\n","import { EventEmitter } from './EventEmitter';\nimport { ChannelSubscriptionState, EventCallback, GlobalEventCallback, EventMetadata } from './types';\n\nexport class Channel {\n public readonly name: string;\n public state: ChannelSubscriptionState = 'subscribing';\n private emitter: EventEmitter = new EventEmitter();\n\n constructor(name: string) {\n this.name = name;\n }\n\n /**\n * Whether the channel is currently successfully subscribed.\n */\n public get subscribed(): boolean {\n return this.state === 'subscribed';\n }\n\n /**\n * Bind an event listener to this channel.\n *\n * Example:\n * ```ts\n * channel.bind('new-comment', (data) => {\n * console.log(data);\n * });\n * ```\n */\n public bind<T = any>(event: string, callback: EventCallback<T>): this {\n this.emitter.bind(event, callback);\n return this;\n }\n\n /**\n * Unbind an event listener from this channel.\n */\n public unbind<T = any>(event?: string, callback?: EventCallback<T>): this {\n this.emitter.unbind(event, callback);\n return this;\n }\n\n /**\n * Bind a global listener to all events received on this channel.\n *\n * Example:\n * ```ts\n * channel.bind_global((eventName, data) => {\n * console.log(`Event ${eventName}:`, data);\n * });\n * ```\n */\n public bind_global(callback: GlobalEventCallback): this {\n this.emitter.bind_global(callback);\n return this;\n }\n\n /**\n * Unbind a global listener from this channel.\n */\n public unbind_global(callback?: GlobalEventCallback): this {\n this.emitter.unbind_global(callback);\n return this;\n }\n\n /**\n * Internal method to dispatch an event onto this channel.\n */\n public handleEvent<T = any>(event: string, data: T, metadata?: EventMetadata): void {\n this.emitter.emit(event, data, {\n ...metadata,\n event,\n channel: this.name,\n });\n }\n\n /**\n * Internal method to update subscription status and fire Pusher-compatible subscription events.\n */\n public setSubscribed(data?: any): void {\n this.state = 'subscribed';\n // Pusher compatibility and standard notif-agent event\n this.handleEvent('subscription_succeeded', data ?? {});\n this.handleEvent('pusher:subscription_succeeded', data ?? {});\n }\n\n /**\n * Internal method to set subscription error.\n */\n public setSubscriptionError(error: any): void {\n this.state = 'error';\n this.handleEvent('subscription_error', error);\n this.handleEvent('pusher:subscription_error', error);\n }\n\n /**\n * Internal method called when channel is unsubscribed.\n */\n public setUnsubscribed(): void {\n this.state = 'unsubscribed';\n this.emitter.clear();\n }\n}\n","import { EventEmitter } from './EventEmitter';\nimport {\n ConnectionState,\n NotifAgentOptions,\n SSEMessagePayload,\n StateChangePayload,\n} from './types';\n\nexport class ConnectionManager {\n public state: ConnectionState = 'initialized';\n public clientId: string | null = null;\n\n /**\n * Pusher-compatible alias for clientId.\n */\n public get socket_id(): string | null {\n return this.clientId;\n }\n\n private emitter: EventEmitter = new EventEmitter();\n private options: Required<Omit<NotifAgentOptions, 'headers' | 'queryParams' | 'url' | 'authEndpoint' | 'auth'>> & {\n url: string;\n headers?: NotifAgentOptions['headers'];\n queryParams?: NotifAgentOptions['queryParams'];\n authEndpoint?: NotifAgentOptions['authEndpoint'];\n auth?: NotifAgentOptions['auth'];\n };\n\n private eventSource: EventSource | null = null;\n private abortController: AbortController | null = null;\n private reconnectAttempts = 0;\n private reconnectTimer: ReturnType<typeof setTimeout> | null = null;\n private heartbeatTimer: ReturnType<typeof setTimeout> | null = null;\n private isManuallyClosed = false;\n\n constructor(url: string, options: NotifAgentOptions = {}) {\n this.options = {\n url,\n headers: options.headers,\n queryParams: options.queryParams,\n withCredentials: options.withCredentials ?? false,\n transport: options.transport ?? 'auto',\n reconnectBaseDelay: options.reconnectBaseDelay ?? 1000,\n reconnectMaxDelay: options.reconnectMaxDelay ?? 30000,\n maxReconnectAttempts: options.maxReconnectAttempts ?? Infinity,\n heartbeatTimeout: options.heartbeatTimeout ?? 45000,\n autoConnect: options.autoConnect ?? true,\n debug: options.debug ?? false,\n };\n\n if (this.options.autoConnect) {\n // Connect on next tick so callers have time to bind connection listeners\n setTimeout(() => {\n if (!this.isManuallyClosed && this.state === 'initialized') {\n this.connect();\n }\n }, 0);\n }\n }\n\n public bind<T = any>(event: string, callback: (data: T) => void): this {\n this.emitter.bind(event, callback);\n return this;\n }\n\n public unbind<T = any>(event?: string, callback?: (data: T) => void): this {\n this.emitter.unbind(event, callback);\n return this;\n }\n\n public connect(): void {\n this.isManuallyClosed = false;\n this.clearTimers();\n\n if (this.state === 'connected' || this.state === 'connecting') {\n return;\n }\n\n this.transition('connecting');\n this.establishConnection();\n }\n\n public disconnect(): void {\n this.isManuallyClosed = true;\n this.clientId = null;\n this.clearTimers();\n this.closeActiveTransport();\n this.transition('disconnected');\n }\n\n private transition(nextState: ConnectionState): void {\n if (this.state === nextState) return;\n\n const previous = this.state;\n this.state = nextState;\n\n this.log(`State transition: ${previous} -> ${nextState}`);\n\n const payload: StateChangePayload = { previous, current: nextState };\n this.emitter.emit('state_change', payload);\n this.emitter.emit(nextState, payload);\n }\n\n private async establishConnection(): Promise<void> {\n try {\n const headers = await this.resolveHeaders();\n const hasCustomHeaders = headers && Object.keys(headers).length > 0;\n const shouldUseFetch =\n this.options.transport === 'fetch' ||\n (this.options.transport === 'auto' && (hasCustomHeaders || typeof EventSource === 'undefined'));\n\n const fullUrl = this.buildUrl();\n\n if (shouldUseFetch) {\n this.log(`Connecting via Fetch SSE stream: ${fullUrl}`);\n this.connectViaFetch(fullUrl, headers);\n } else {\n this.log(`Connecting via native EventSource: ${fullUrl}`);\n this.connectViaEventSource(fullUrl);\n }\n } catch (err) {\n this.handleConnectionFailure(err);\n }\n }\n\n private connectViaEventSource(url: string): void {\n this.closeActiveTransport();\n\n try {\n const es = new EventSource(url, {\n withCredentials: this.options.withCredentials,\n });\n this.eventSource = es;\n\n es.onopen = () => {\n this.handleConnectionSuccess();\n };\n\n es.onmessage = (e: MessageEvent) => {\n this.resetHeartbeat();\n this.parseAndDispatchMessage('message', e.data, e.lastEventId);\n };\n\n es.onerror = (err) => {\n this.log('EventSource error occurred', err);\n if (es.readyState === EventSource.CLOSED) {\n this.handleConnectionFailure(err);\n } else {\n // EventSource is in CONNECTING state attempting internal reconnect\n this.transition('unavailable');\n }\n };\n } catch (err) {\n this.handleConnectionFailure(err);\n }\n }\n\n private async connectViaFetch(url: string, headers?: Record<string, string>): Promise<void> {\n this.closeActiveTransport();\n this.abortController = new AbortController();\n\n try {\n const requestHeaders: Record<string, string> = {\n Accept: 'text/event-stream',\n 'Cache-Control': 'no-cache',\n ...headers,\n };\n\n const response = await fetch(url, {\n method: 'GET',\n headers: requestHeaders,\n credentials: this.options.withCredentials ? 'include' : 'same-origin',\n signal: this.abortController.signal,\n });\n\n if (!response.ok) {\n throw new Error(`SSE connection failed with HTTP status ${response.status}: ${response.statusText}`);\n }\n\n if (!response.body) {\n throw new Error('Response body is null or stream reading is not supported');\n }\n\n this.handleConnectionSuccess();\n\n const reader = response.body.getReader();\n const decoder = new TextDecoder('utf-8');\n let buffer = '';\n\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n\n this.resetHeartbeat();\n buffer += decoder.decode(value, { stream: true });\n\n // Process SSE frames (blocks ending with double newline)\n const parts = buffer.split(/\\r\\n\\r\\n|\\n\\n/);\n buffer = parts.pop() ?? '';\n\n for (const part of parts) {\n if (part.trim().length > 0) {\n this.processSSEFrame(part);\n }\n }\n }\n\n // Stream closed normally from server\n if (!this.isManuallyClosed) {\n this.handleConnectionFailure(new Error('SSE stream ended by server'));\n }\n } catch (err: any) {\n if (err.name === 'AbortError' || this.isManuallyClosed) {\n // Expected cancellation\n return;\n }\n this.handleConnectionFailure(err);\n }\n }\n\n private processSSEFrame(frame: string): void {\n const lines = frame.split(/\\r\\n|\\r|\\n/);\n let event = 'message';\n let data = '';\n let id: string | undefined;\n\n for (const line of lines) {\n if (line.startsWith(':')) {\n // Comment / ping heartbeat\n this.resetHeartbeat();\n continue;\n }\n\n if (line.startsWith('event:')) {\n event = line.slice(6).trim();\n } else if (line.startsWith('data:')) {\n const value = line.slice(5).trim();\n data += (data ? '\\n' : '') + value;\n } else if (line.startsWith('id:')) {\n id = line.slice(3).trim();\n } else if (line.startsWith('retry:')) {\n const retryMs = parseInt(line.slice(6).trim(), 10);\n if (!isNaN(retryMs)) {\n this.options.reconnectBaseDelay = retryMs;\n }\n }\n }\n\n if (data || event !== 'message') {\n this.parseAndDispatchMessage(event, data, id);\n }\n }\n\n private parseAndDispatchMessage(sseEvent: string, rawData: string, id?: string): void {\n let parsedData: any = rawData;\n let channel: string | undefined;\n let eventName = sseEvent;\n\n if (rawData) {\n try {\n const json = JSON.parse(rawData);\n // Check if server packaged channel & event inside json payload\n if (typeof json === 'object' && json !== null) {\n if (json.channel && typeof json.channel === 'string') {\n channel = json.channel;\n }\n if (json.event && typeof json.event === 'string') {\n eventName = json.event;\n }\n if ('data' in json) {\n parsedData = json.data;\n } else {\n parsedData = json;\n }\n } else {\n parsedData = json;\n }\n } catch {\n parsedData = rawData;\n }\n }\n\n // Handle handshake event from NotifServer\n if (eventName === 'connected' && parsedData && typeof parsedData === 'object' && parsedData.clientId) {\n this.clientId = parsedData.clientId;\n this.emitter.emit('handshake', parsedData);\n }\n\n // Handle heartbeat pings\n if (eventName === 'ping' || eventName === 'heartbeat' || eventName === ':ping') {\n this.resetHeartbeat();\n return;\n }\n\n const payload: SSEMessagePayload = {\n channel,\n event: eventName,\n data: parsedData,\n id,\n timestamp: Date.now(),\n };\n\n this.emitter.emit('message', payload);\n }\n\n private handleConnectionSuccess(): void {\n this.reconnectAttempts = 0;\n this.transition('connected');\n this.resetHeartbeat();\n }\n\n private handleConnectionFailure(error: any): void {\n this.log('Connection failed or dropped', error);\n this.clientId = null; // the server forgets this id once the stream closes\n this.closeActiveTransport();\n this.clearHeartbeat();\n\n if (this.isManuallyClosed) {\n return;\n }\n\n this.emitter.emit('error', error);\n\n this.reconnectAttempts++;\n if (this.reconnectAttempts > this.options.maxReconnectAttempts) {\n this.log(`Max reconnect attempts (${this.options.maxReconnectAttempts}) exceeded. State -> failed`);\n this.transition('failed');\n return;\n }\n\n this.transition('unavailable');\n this.scheduleReconnect();\n }\n\n private scheduleReconnect(): void {\n if (this.reconnectTimer) clearTimeout(this.reconnectTimer);\n\n // Exponential backoff with jitter\n const delay = Math.min(\n this.options.reconnectBaseDelay * Math.pow(1.5, this.reconnectAttempts - 1),\n this.options.reconnectMaxDelay\n );\n const jitter = delay * 0.2 * (Math.random() - 0.5); // +/- 10%\n const totalDelay = Math.max(100, Math.round(delay + jitter));\n\n this.log(`Scheduling reconnect in ${totalDelay}ms (attempt #${this.reconnectAttempts})`);\n\n this.reconnectTimer = setTimeout(() => {\n if (!this.isManuallyClosed && this.state !== 'connected') {\n this.transition('connecting');\n this.establishConnection();\n }\n }, totalDelay);\n }\n\n private resetHeartbeat(): void {\n this.clearHeartbeat();\n\n if (this.options.heartbeatTimeout <= 0) return;\n\n this.heartbeatTimer = setTimeout(() => {\n this.log(`Heartbeat timeout (${this.options.heartbeatTimeout}ms) exceeded. Connection dropped.`);\n this.handleConnectionFailure(new Error('Heartbeat timeout'));\n }, this.options.heartbeatTimeout);\n }\n\n private clearHeartbeat(): void {\n if (this.heartbeatTimer) {\n clearTimeout(this.heartbeatTimer);\n this.heartbeatTimer = null;\n }\n }\n\n private clearTimers(): void {\n this.clearHeartbeat();\n if (this.reconnectTimer) {\n clearTimeout(this.reconnectTimer);\n this.reconnectTimer = null;\n }\n }\n\n private closeActiveTransport(): void {\n if (this.eventSource) {\n try {\n this.eventSource.close();\n } catch {}\n this.eventSource = null;\n }\n\n if (this.abortController) {\n try {\n this.abortController.abort();\n } catch {}\n this.abortController = null;\n }\n }\n\n private buildUrl(): string {\n const url = new URL(this.options.url, typeof window !== 'undefined' ? window.location.href : 'http://localhost');\n if (this.options.queryParams) {\n for (const [key, value] of Object.entries(this.options.queryParams)) {\n if (value !== undefined && value !== null) {\n url.searchParams.set(key, String(value));\n }\n }\n }\n return url.toString();\n }\n\n private async resolveHeaders(): Promise<Record<string, string> | undefined> {\n if (!this.options.headers) return undefined;\n if (typeof this.options.headers === 'function') {\n return await this.options.headers();\n }\n return this.options.headers;\n }\n\n private log(...args: any[]): void {\n if (this.options.debug) {\n console.log('[notif-agent:connection]', ...args);\n }\n }\n}\n","import { Channel } from './Channel';\nimport { ConnectionManager } from './ConnectionManager';\nimport { EventEmitter } from './EventEmitter';\nimport {\n EventCallback,\n GlobalEventCallback,\n NotifAgentOptions,\n SSEMessagePayload,\n} from './types';\n\nexport class NotifAgent {\n public readonly connection: ConnectionManager;\n private options: NotifAgentOptions;\n private channels: Map<string, Channel> = new Map();\n private globalEmitter: EventEmitter = new EventEmitter();\n\n /**\n * Unique client connection ID provided by NotifServer handshake.\n */\n public get clientId(): string | null {\n return this.connection.clientId;\n }\n\n /**\n * Pusher-compatible alias for clientId.\n */\n public get socket_id(): string | null {\n return this.connection.clientId;\n }\n\n constructor(urlOrOptions: string | NotifAgentOptions, maybeOptions?: NotifAgentOptions) {\n let options: NotifAgentOptions;\n\n if (typeof urlOrOptions === 'string') {\n options = { ...(maybeOptions ?? {}), url: urlOrOptions };\n } else {\n options = urlOrOptions;\n }\n\n if (!options.url) {\n throw new Error('[notif-agent] SSE URL must be specified');\n }\n\n this.options = options;\n this.connection = new ConnectionManager(options.url, options);\n\n // Listen to connection messages to route events\n this.connection.bind<SSEMessagePayload>('message', (payload) => {\n this.routeMessage(payload);\n });\n\n const triggerChannelSubscriptions = () => {\n for (const channel of this.channels.values()) {\n const isPrivate = channel.name.startsWith('private-') || channel.name.startsWith('presence-');\n if (isPrivate && this.options.authEndpoint) {\n this.authorizeChannel(channel);\n } else {\n channel.setSubscribed();\n }\n }\n };\n\n // Subscribe once per connection, after the server hands us a clientId (also on every reconnect).\n this.connection.bind('handshake', () => {\n triggerChannelSubscriptions();\n });\n }\n\n /**\n * Subscribe to a channel.\n *\n * Example:\n * ```ts\n * const channel = agent.subscribe('orders');\n * channel.bind('created', (order) => { ... });\n * ```\n */\n public subscribe(channelName: string): Channel {\n if (!channelName) {\n throw new Error('[notif-agent] Channel name cannot be empty');\n }\n\n let channel = this.channels.get(channelName);\n if (!channel) {\n channel = new Channel(channelName);\n this.channels.set(channelName, channel);\n\n // Handshake already done: subscribe now. Otherwise the handshake handler will.\n if (this.connection.state === 'connected' && this.connection.clientId) {\n setTimeout(() => {\n const isPrivate = channelName.startsWith('private-') || channelName.startsWith('presence-');\n if (isPrivate && this.options.authEndpoint) {\n this.authorizeChannel(channel!);\n } else {\n channel?.setSubscribed();\n }\n }, 0);\n }\n }\n\n return channel;\n }\n\n /**\n * Internal authorization helper for private/presence channels.\n */\n private async authorizeChannel(channel: Channel): Promise<void> {\n if (!this.options.authEndpoint) {\n channel.setSubscribed();\n return;\n }\n\n try {\n let customHeaders: Record<string, string> = {};\n if (typeof this.options.auth?.headers === 'function') {\n customHeaders = await this.options.auth.headers();\n } else if (this.options.auth?.headers) {\n customHeaders = this.options.auth.headers;\n }\n\n const body = {\n clientId: this.connection.clientId,\n socket_id: this.connection.clientId,\n channel: channel.name,\n ...(this.options.auth?.params ?? {}),\n };\n\n const response = await fetch(this.options.authEndpoint, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n ...customHeaders,\n },\n body: JSON.stringify(body),\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(`[notif-agent] Authorization failed (${response.status}): ${errorText}`);\n }\n\n const authData = await response.json().catch(() => ({}));\n channel.setSubscribed(authData);\n } catch (err) {\n channel.setSubscriptionError(err);\n }\n }\n\n /**\n * Unsubscribe from a channel.\n */\n public unsubscribe(channelName: string): void {\n const channel = this.channels.get(channelName);\n if (channel) {\n channel.setUnsubscribed();\n this.channels.delete(channelName);\n }\n }\n\n /**\n * Get an existing subscribed channel by name, if any.\n */\n public channel(channelName: string): Channel | undefined {\n return this.channels.get(channelName);\n }\n\n /**\n * Return all currently subscribed channels.\n */\n public allChannels(): Channel[] {\n return Array.from(this.channels.values());\n }\n\n /**\n * Bind to an event across all channels or global events.\n *\n * Example:\n * ```ts\n * agent.bind('system-alert', (data) => console.log(data));\n * ```\n */\n public bind<T = any>(event: string, callback: EventCallback<T>): this {\n this.globalEmitter.bind(event, callback);\n return this;\n }\n\n /**\n * Unbind an event listener.\n */\n public unbind<T = any>(event?: string, callback?: EventCallback<T>): this {\n this.globalEmitter.unbind(event, callback);\n return this;\n }\n\n /**\n * Bind to all events regardless of name.\n */\n public bind_global(callback: GlobalEventCallback): this {\n this.globalEmitter.bind_global(callback);\n return this;\n }\n\n /**\n * Unbind a global event listener.\n */\n public unbind_global(callback?: GlobalEventCallback): this {\n this.globalEmitter.unbind_global(callback);\n return this;\n }\n\n /**\n * Connect to the SSE endpoint.\n */\n public connect(): void {\n this.connection.connect();\n }\n\n /**\n * Disconnect from the SSE endpoint.\n */\n public disconnect(): void {\n this.connection.disconnect();\n }\n\n /**\n * Route incoming SSE message to global emitters and specific channels.\n */\n private routeMessage(payload: SSEMessagePayload): void {\n const metadata = {\n channel: payload.channel,\n event: payload.event,\n id: payload.id,\n timestamp: payload.timestamp,\n };\n\n // 1. Dispatch to global agent emitter\n this.globalEmitter.emit(payload.event, payload.data, metadata);\n\n // 2. Dispatch to specific channel if message contains a channel\n if (payload.channel) {\n const targetChannel = this.channels.get(payload.channel);\n if (targetChannel) {\n targetChannel.handleEvent(payload.event, payload.data, metadata);\n }\n } else {\n // If message has no channel specified, notify all active channels\n for (const ch of this.channels.values()) {\n ch.handleEvent(payload.event, payload.data, metadata);\n }\n }\n }\n}\n","import { NotifAgent } from './client/NotifAgent';\n\nexport { NotifAgent } from './client/NotifAgent';\nexport { Channel } from './client/Channel';\nexport { ConnectionManager } from './client/ConnectionManager';\nexport { EventEmitter } from './client/EventEmitter';\nexport * from './client/types';\n\nexport default NotifAgent;\n"]}
package/dist/index.mjs CHANGED
@@ -416,6 +416,7 @@ var ConnectionManager = class {
416
416
  }
417
417
  handleConnectionFailure(error) {
418
418
  this.log("Connection failed or dropped", error);
419
+ this.clientId = null;
419
420
  this.closeActiveTransport();
420
421
  this.clearHeartbeat();
421
422
  if (this.isManuallyClosed) {
@@ -555,11 +556,6 @@ var NotifAgent = class {
555
556
  this.connection.bind("handshake", () => {
556
557
  triggerChannelSubscriptions();
557
558
  });
558
- this.connection.bind("connected", () => {
559
- if (this.connection.clientId || !this.options.authEndpoint) {
560
- triggerChannelSubscriptions();
561
- }
562
- });
563
559
  }
564
560
  /**
565
561
  * Subscribe to a channel.
@@ -578,7 +574,7 @@ var NotifAgent = class {
578
574
  if (!channel) {
579
575
  channel = new Channel(channelName);
580
576
  this.channels.set(channelName, channel);
581
- if (this.connection.state === "connected") {
577
+ if (this.connection.state === "connected" && this.connection.clientId) {
582
578
  setTimeout(() => {
583
579
  const isPrivate = channelName.startsWith("private-") || channelName.startsWith("presence-");
584
580
  if (isPrivate && this.options.authEndpoint) {
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/client/EventEmitter.ts","../src/client/Channel.ts","../src/client/ConnectionManager.ts","../src/client/NotifAgent.ts","../src/index.ts"],"names":[],"mappings":"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","file":"index.mjs","sourcesContent":["import { EventCallback, GlobalEventCallback, EventMetadata } from './types';\n\nexport class EventEmitter {\n private listeners: Map<string, Set<EventCallback>> = new Map();\n private globalListeners: Set<GlobalEventCallback> = new Set();\n\n /**\n * Bind a callback to a specific event name.\n */\n public bind<T = any>(event: string, callback: EventCallback<T>): this {\n if (!this.listeners.has(event)) {\n this.listeners.set(event, new Set());\n }\n this.listeners.get(event)!.add(callback);\n return this;\n }\n\n /**\n * Unbind a callback from a specific event name, or all callbacks if callback is omitted.\n * If both event and callback are omitted, all listeners are removed.\n */\n public unbind<T = any>(event?: string, callback?: EventCallback<T>): this {\n if (!event) {\n this.listeners.clear();\n return this;\n }\n\n if (!callback) {\n this.listeners.delete(event);\n return this;\n }\n\n const set = this.listeners.get(event);\n if (set) {\n set.delete(callback);\n if (set.size === 0) {\n this.listeners.delete(event);\n }\n }\n\n return this;\n }\n\n /**\n * Bind a callback to ALL incoming events on this emitter.\n */\n public bind_global(callback: GlobalEventCallback): this {\n this.globalListeners.add(callback);\n return this;\n }\n\n /**\n * Unbind a global callback, or all global callbacks if callback is omitted.\n */\n public unbind_global(callback?: GlobalEventCallback): this {\n if (!callback) {\n this.globalListeners.clear();\n return this;\n }\n this.globalListeners.delete(callback);\n return this;\n }\n\n /**\n * Emit an event to registered listeners and global listeners.\n */\n public emit<T = any>(event: string, data: T, metadata?: EventMetadata): void {\n const set = this.listeners.get(event);\n if (set) {\n for (const cb of Array.from(set)) {\n try {\n cb(data, metadata);\n } catch (err) {\n console.error(`[notif-agent] Error in listener for event \"${event}\":`, err);\n }\n }\n }\n\n for (const globalCb of Array.from(this.globalListeners)) {\n try {\n globalCb(event, data, metadata);\n } catch (err) {\n console.error(`[notif-agent] Error in global listener for event \"${event}\":`, err);\n }\n }\n }\n\n /**\n * Clear all listeners.\n */\n public clear(): void {\n this.listeners.clear();\n this.globalListeners.clear();\n }\n}\n","import { EventEmitter } from './EventEmitter';\nimport { ChannelSubscriptionState, EventCallback, GlobalEventCallback, EventMetadata } from './types';\n\nexport class Channel {\n public readonly name: string;\n public state: ChannelSubscriptionState = 'subscribing';\n private emitter: EventEmitter = new EventEmitter();\n\n constructor(name: string) {\n this.name = name;\n }\n\n /**\n * Whether the channel is currently successfully subscribed.\n */\n public get subscribed(): boolean {\n return this.state === 'subscribed';\n }\n\n /**\n * Bind an event listener to this channel.\n *\n * Example:\n * ```ts\n * channel.bind('new-comment', (data) => {\n * console.log(data);\n * });\n * ```\n */\n public bind<T = any>(event: string, callback: EventCallback<T>): this {\n this.emitter.bind(event, callback);\n return this;\n }\n\n /**\n * Unbind an event listener from this channel.\n */\n public unbind<T = any>(event?: string, callback?: EventCallback<T>): this {\n this.emitter.unbind(event, callback);\n return this;\n }\n\n /**\n * Bind a global listener to all events received on this channel.\n *\n * Example:\n * ```ts\n * channel.bind_global((eventName, data) => {\n * console.log(`Event ${eventName}:`, data);\n * });\n * ```\n */\n public bind_global(callback: GlobalEventCallback): this {\n this.emitter.bind_global(callback);\n return this;\n }\n\n /**\n * Unbind a global listener from this channel.\n */\n public unbind_global(callback?: GlobalEventCallback): this {\n this.emitter.unbind_global(callback);\n return this;\n }\n\n /**\n * Internal method to dispatch an event onto this channel.\n */\n public handleEvent<T = any>(event: string, data: T, metadata?: EventMetadata): void {\n this.emitter.emit(event, data, {\n ...metadata,\n event,\n channel: this.name,\n });\n }\n\n /**\n * Internal method to update subscription status and fire Pusher-compatible subscription events.\n */\n public setSubscribed(data?: any): void {\n this.state = 'subscribed';\n // Pusher compatibility and standard notif-agent event\n this.handleEvent('subscription_succeeded', data ?? {});\n this.handleEvent('pusher:subscription_succeeded', data ?? {});\n }\n\n /**\n * Internal method to set subscription error.\n */\n public setSubscriptionError(error: any): void {\n this.state = 'error';\n this.handleEvent('subscription_error', error);\n this.handleEvent('pusher:subscription_error', error);\n }\n\n /**\n * Internal method called when channel is unsubscribed.\n */\n public setUnsubscribed(): void {\n this.state = 'unsubscribed';\n this.emitter.clear();\n }\n}\n","import { EventEmitter } from './EventEmitter';\nimport {\n ConnectionState,\n NotifAgentOptions,\n SSEMessagePayload,\n StateChangePayload,\n} from './types';\n\nexport class ConnectionManager {\n public state: ConnectionState = 'initialized';\n public clientId: string | null = null;\n\n /**\n * Pusher-compatible alias for clientId.\n */\n public get socket_id(): string | null {\n return this.clientId;\n }\n\n private emitter: EventEmitter = new EventEmitter();\n private options: Required<Omit<NotifAgentOptions, 'headers' | 'queryParams' | 'url' | 'authEndpoint' | 'auth'>> & {\n url: string;\n headers?: NotifAgentOptions['headers'];\n queryParams?: NotifAgentOptions['queryParams'];\n authEndpoint?: NotifAgentOptions['authEndpoint'];\n auth?: NotifAgentOptions['auth'];\n };\n\n private eventSource: EventSource | null = null;\n private abortController: AbortController | null = null;\n private reconnectAttempts = 0;\n private reconnectTimer: ReturnType<typeof setTimeout> | null = null;\n private heartbeatTimer: ReturnType<typeof setTimeout> | null = null;\n private isManuallyClosed = false;\n\n constructor(url: string, options: NotifAgentOptions = {}) {\n this.options = {\n url,\n headers: options.headers,\n queryParams: options.queryParams,\n withCredentials: options.withCredentials ?? false,\n transport: options.transport ?? 'auto',\n reconnectBaseDelay: options.reconnectBaseDelay ?? 1000,\n reconnectMaxDelay: options.reconnectMaxDelay ?? 30000,\n maxReconnectAttempts: options.maxReconnectAttempts ?? Infinity,\n heartbeatTimeout: options.heartbeatTimeout ?? 45000,\n autoConnect: options.autoConnect ?? true,\n debug: options.debug ?? false,\n };\n\n if (this.options.autoConnect) {\n // Connect on next tick so callers have time to bind connection listeners\n setTimeout(() => {\n if (!this.isManuallyClosed && this.state === 'initialized') {\n this.connect();\n }\n }, 0);\n }\n }\n\n public bind<T = any>(event: string, callback: (data: T) => void): this {\n this.emitter.bind(event, callback);\n return this;\n }\n\n public unbind<T = any>(event?: string, callback?: (data: T) => void): this {\n this.emitter.unbind(event, callback);\n return this;\n }\n\n public connect(): void {\n this.isManuallyClosed = false;\n this.clearTimers();\n\n if (this.state === 'connected' || this.state === 'connecting') {\n return;\n }\n\n this.transition('connecting');\n this.establishConnection();\n }\n\n public disconnect(): void {\n this.isManuallyClosed = true;\n this.clientId = null;\n this.clearTimers();\n this.closeActiveTransport();\n this.transition('disconnected');\n }\n\n private transition(nextState: ConnectionState): void {\n if (this.state === nextState) return;\n\n const previous = this.state;\n this.state = nextState;\n\n this.log(`State transition: ${previous} -> ${nextState}`);\n\n const payload: StateChangePayload = { previous, current: nextState };\n this.emitter.emit('state_change', payload);\n this.emitter.emit(nextState, payload);\n }\n\n private async establishConnection(): Promise<void> {\n try {\n const headers = await this.resolveHeaders();\n const hasCustomHeaders = headers && Object.keys(headers).length > 0;\n const shouldUseFetch =\n this.options.transport === 'fetch' ||\n (this.options.transport === 'auto' && (hasCustomHeaders || typeof EventSource === 'undefined'));\n\n const fullUrl = this.buildUrl();\n\n if (shouldUseFetch) {\n this.log(`Connecting via Fetch SSE stream: ${fullUrl}`);\n this.connectViaFetch(fullUrl, headers);\n } else {\n this.log(`Connecting via native EventSource: ${fullUrl}`);\n this.connectViaEventSource(fullUrl);\n }\n } catch (err) {\n this.handleConnectionFailure(err);\n }\n }\n\n private connectViaEventSource(url: string): void {\n this.closeActiveTransport();\n\n try {\n const es = new EventSource(url, {\n withCredentials: this.options.withCredentials,\n });\n this.eventSource = es;\n\n es.onopen = () => {\n this.handleConnectionSuccess();\n };\n\n es.onmessage = (e: MessageEvent) => {\n this.resetHeartbeat();\n this.parseAndDispatchMessage('message', e.data, e.lastEventId);\n };\n\n es.onerror = (err) => {\n this.log('EventSource error occurred', err);\n if (es.readyState === EventSource.CLOSED) {\n this.handleConnectionFailure(err);\n } else {\n // EventSource is in CONNECTING state attempting internal reconnect\n this.transition('unavailable');\n }\n };\n } catch (err) {\n this.handleConnectionFailure(err);\n }\n }\n\n private async connectViaFetch(url: string, headers?: Record<string, string>): Promise<void> {\n this.closeActiveTransport();\n this.abortController = new AbortController();\n\n try {\n const requestHeaders: Record<string, string> = {\n Accept: 'text/event-stream',\n 'Cache-Control': 'no-cache',\n ...headers,\n };\n\n const response = await fetch(url, {\n method: 'GET',\n headers: requestHeaders,\n credentials: this.options.withCredentials ? 'include' : 'same-origin',\n signal: this.abortController.signal,\n });\n\n if (!response.ok) {\n throw new Error(`SSE connection failed with HTTP status ${response.status}: ${response.statusText}`);\n }\n\n if (!response.body) {\n throw new Error('Response body is null or stream reading is not supported');\n }\n\n this.handleConnectionSuccess();\n\n const reader = response.body.getReader();\n const decoder = new TextDecoder('utf-8');\n let buffer = '';\n\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n\n this.resetHeartbeat();\n buffer += decoder.decode(value, { stream: true });\n\n // Process SSE frames (blocks ending with double newline)\n const parts = buffer.split(/\\r\\n\\r\\n|\\n\\n/);\n buffer = parts.pop() ?? '';\n\n for (const part of parts) {\n if (part.trim().length > 0) {\n this.processSSEFrame(part);\n }\n }\n }\n\n // Stream closed normally from server\n if (!this.isManuallyClosed) {\n this.handleConnectionFailure(new Error('SSE stream ended by server'));\n }\n } catch (err: any) {\n if (err.name === 'AbortError' || this.isManuallyClosed) {\n // Expected cancellation\n return;\n }\n this.handleConnectionFailure(err);\n }\n }\n\n private processSSEFrame(frame: string): void {\n const lines = frame.split(/\\r\\n|\\r|\\n/);\n let event = 'message';\n let data = '';\n let id: string | undefined;\n\n for (const line of lines) {\n if (line.startsWith(':')) {\n // Comment / ping heartbeat\n this.resetHeartbeat();\n continue;\n }\n\n if (line.startsWith('event:')) {\n event = line.slice(6).trim();\n } else if (line.startsWith('data:')) {\n const value = line.slice(5).trim();\n data += (data ? '\\n' : '') + value;\n } else if (line.startsWith('id:')) {\n id = line.slice(3).trim();\n } else if (line.startsWith('retry:')) {\n const retryMs = parseInt(line.slice(6).trim(), 10);\n if (!isNaN(retryMs)) {\n this.options.reconnectBaseDelay = retryMs;\n }\n }\n }\n\n if (data || event !== 'message') {\n this.parseAndDispatchMessage(event, data, id);\n }\n }\n\n private parseAndDispatchMessage(sseEvent: string, rawData: string, id?: string): void {\n let parsedData: any = rawData;\n let channel: string | undefined;\n let eventName = sseEvent;\n\n if (rawData) {\n try {\n const json = JSON.parse(rawData);\n // Check if server packaged channel & event inside json payload\n if (typeof json === 'object' && json !== null) {\n if (json.channel && typeof json.channel === 'string') {\n channel = json.channel;\n }\n if (json.event && typeof json.event === 'string') {\n eventName = json.event;\n }\n if ('data' in json) {\n parsedData = json.data;\n } else {\n parsedData = json;\n }\n } else {\n parsedData = json;\n }\n } catch {\n parsedData = rawData;\n }\n }\n\n // Handle handshake event from NotifServer\n if (eventName === 'connected' && parsedData && typeof parsedData === 'object' && parsedData.clientId) {\n this.clientId = parsedData.clientId;\n this.emitter.emit('handshake', parsedData);\n }\n\n // Handle heartbeat pings\n if (eventName === 'ping' || eventName === 'heartbeat' || eventName === ':ping') {\n this.resetHeartbeat();\n return;\n }\n\n const payload: SSEMessagePayload = {\n channel,\n event: eventName,\n data: parsedData,\n id,\n timestamp: Date.now(),\n };\n\n this.emitter.emit('message', payload);\n }\n\n private handleConnectionSuccess(): void {\n this.reconnectAttempts = 0;\n this.transition('connected');\n this.resetHeartbeat();\n }\n\n private handleConnectionFailure(error: any): void {\n this.log('Connection failed or dropped', error);\n this.closeActiveTransport();\n this.clearHeartbeat();\n\n if (this.isManuallyClosed) {\n return;\n }\n\n this.emitter.emit('error', error);\n\n this.reconnectAttempts++;\n if (this.reconnectAttempts > this.options.maxReconnectAttempts) {\n this.log(`Max reconnect attempts (${this.options.maxReconnectAttempts}) exceeded. State -> failed`);\n this.transition('failed');\n return;\n }\n\n this.transition('unavailable');\n this.scheduleReconnect();\n }\n\n private scheduleReconnect(): void {\n if (this.reconnectTimer) clearTimeout(this.reconnectTimer);\n\n // Exponential backoff with jitter\n const delay = Math.min(\n this.options.reconnectBaseDelay * Math.pow(1.5, this.reconnectAttempts - 1),\n this.options.reconnectMaxDelay\n );\n const jitter = delay * 0.2 * (Math.random() - 0.5); // +/- 10%\n const totalDelay = Math.max(100, Math.round(delay + jitter));\n\n this.log(`Scheduling reconnect in ${totalDelay}ms (attempt #${this.reconnectAttempts})`);\n\n this.reconnectTimer = setTimeout(() => {\n if (!this.isManuallyClosed && this.state !== 'connected') {\n this.transition('connecting');\n this.establishConnection();\n }\n }, totalDelay);\n }\n\n private resetHeartbeat(): void {\n this.clearHeartbeat();\n\n if (this.options.heartbeatTimeout <= 0) return;\n\n this.heartbeatTimer = setTimeout(() => {\n this.log(`Heartbeat timeout (${this.options.heartbeatTimeout}ms) exceeded. Connection dropped.`);\n this.handleConnectionFailure(new Error('Heartbeat timeout'));\n }, this.options.heartbeatTimeout);\n }\n\n private clearHeartbeat(): void {\n if (this.heartbeatTimer) {\n clearTimeout(this.heartbeatTimer);\n this.heartbeatTimer = null;\n }\n }\n\n private clearTimers(): void {\n this.clearHeartbeat();\n if (this.reconnectTimer) {\n clearTimeout(this.reconnectTimer);\n this.reconnectTimer = null;\n }\n }\n\n private closeActiveTransport(): void {\n if (this.eventSource) {\n try {\n this.eventSource.close();\n } catch {}\n this.eventSource = null;\n }\n\n if (this.abortController) {\n try {\n this.abortController.abort();\n } catch {}\n this.abortController = null;\n }\n }\n\n private buildUrl(): string {\n const url = new URL(this.options.url, typeof window !== 'undefined' ? window.location.href : 'http://localhost');\n if (this.options.queryParams) {\n for (const [key, value] of Object.entries(this.options.queryParams)) {\n if (value !== undefined && value !== null) {\n url.searchParams.set(key, String(value));\n }\n }\n }\n return url.toString();\n }\n\n private async resolveHeaders(): Promise<Record<string, string> | undefined> {\n if (!this.options.headers) return undefined;\n if (typeof this.options.headers === 'function') {\n return await this.options.headers();\n }\n return this.options.headers;\n }\n\n private log(...args: any[]): void {\n if (this.options.debug) {\n console.log('[notif-agent:connection]', ...args);\n }\n }\n}\n","import { Channel } from './Channel';\nimport { ConnectionManager } from './ConnectionManager';\nimport { EventEmitter } from './EventEmitter';\nimport {\n EventCallback,\n GlobalEventCallback,\n NotifAgentOptions,\n SSEMessagePayload,\n} from './types';\n\nexport class NotifAgent {\n public readonly connection: ConnectionManager;\n private options: NotifAgentOptions;\n private channels: Map<string, Channel> = new Map();\n private globalEmitter: EventEmitter = new EventEmitter();\n\n /**\n * Unique client connection ID provided by NotifServer handshake.\n */\n public get clientId(): string | null {\n return this.connection.clientId;\n }\n\n /**\n * Pusher-compatible alias for clientId.\n */\n public get socket_id(): string | null {\n return this.connection.clientId;\n }\n\n constructor(urlOrOptions: string | NotifAgentOptions, maybeOptions?: NotifAgentOptions) {\n let options: NotifAgentOptions;\n\n if (typeof urlOrOptions === 'string') {\n options = { ...(maybeOptions ?? {}), url: urlOrOptions };\n } else {\n options = urlOrOptions;\n }\n\n if (!options.url) {\n throw new Error('[notif-agent] SSE URL must be specified');\n }\n\n this.options = options;\n this.connection = new ConnectionManager(options.url, options);\n\n // Listen to connection messages to route events\n this.connection.bind<SSEMessagePayload>('message', (payload) => {\n this.routeMessage(payload);\n });\n\n const triggerChannelSubscriptions = () => {\n for (const channel of this.channels.values()) {\n const isPrivate = channel.name.startsWith('private-') || channel.name.startsWith('presence-');\n if (isPrivate && this.options.authEndpoint) {\n this.authorizeChannel(channel);\n } else {\n channel.setSubscribed();\n }\n }\n };\n\n // On handshake with server clientId, authorize channels\n this.connection.bind('handshake', () => {\n triggerChannelSubscriptions();\n });\n\n // On reconnect / connected\n this.connection.bind('connected', () => {\n if (this.connection.clientId || !this.options.authEndpoint) {\n triggerChannelSubscriptions();\n }\n });\n }\n\n /**\n * Subscribe to a channel.\n *\n * Example:\n * ```ts\n * const channel = agent.subscribe('orders');\n * channel.bind('created', (order) => { ... });\n * ```\n */\n public subscribe(channelName: string): Channel {\n if (!channelName) {\n throw new Error('[notif-agent] Channel name cannot be empty');\n }\n\n let channel = this.channels.get(channelName);\n if (!channel) {\n channel = new Channel(channelName);\n this.channels.set(channelName, channel);\n\n // If already connected, mark channel as subscribed or authorize if private\n if (this.connection.state === 'connected') {\n setTimeout(() => {\n const isPrivate = channelName.startsWith('private-') || channelName.startsWith('presence-');\n if (isPrivate && this.options.authEndpoint) {\n this.authorizeChannel(channel!);\n } else {\n channel?.setSubscribed();\n }\n }, 0);\n }\n }\n\n return channel;\n }\n\n /**\n * Internal authorization helper for private/presence channels.\n */\n private async authorizeChannel(channel: Channel): Promise<void> {\n if (!this.options.authEndpoint) {\n channel.setSubscribed();\n return;\n }\n\n try {\n let customHeaders: Record<string, string> = {};\n if (typeof this.options.auth?.headers === 'function') {\n customHeaders = await this.options.auth.headers();\n } else if (this.options.auth?.headers) {\n customHeaders = this.options.auth.headers;\n }\n\n const body = {\n clientId: this.connection.clientId,\n socket_id: this.connection.clientId,\n channel: channel.name,\n ...(this.options.auth?.params ?? {}),\n };\n\n const response = await fetch(this.options.authEndpoint, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n ...customHeaders,\n },\n body: JSON.stringify(body),\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(`[notif-agent] Authorization failed (${response.status}): ${errorText}`);\n }\n\n const authData = await response.json().catch(() => ({}));\n channel.setSubscribed(authData);\n } catch (err) {\n channel.setSubscriptionError(err);\n }\n }\n\n /**\n * Unsubscribe from a channel.\n */\n public unsubscribe(channelName: string): void {\n const channel = this.channels.get(channelName);\n if (channel) {\n channel.setUnsubscribed();\n this.channels.delete(channelName);\n }\n }\n\n /**\n * Get an existing subscribed channel by name, if any.\n */\n public channel(channelName: string): Channel | undefined {\n return this.channels.get(channelName);\n }\n\n /**\n * Return all currently subscribed channels.\n */\n public allChannels(): Channel[] {\n return Array.from(this.channels.values());\n }\n\n /**\n * Bind to an event across all channels or global events.\n *\n * Example:\n * ```ts\n * agent.bind('system-alert', (data) => console.log(data));\n * ```\n */\n public bind<T = any>(event: string, callback: EventCallback<T>): this {\n this.globalEmitter.bind(event, callback);\n return this;\n }\n\n /**\n * Unbind an event listener.\n */\n public unbind<T = any>(event?: string, callback?: EventCallback<T>): this {\n this.globalEmitter.unbind(event, callback);\n return this;\n }\n\n /**\n * Bind to all events regardless of name.\n */\n public bind_global(callback: GlobalEventCallback): this {\n this.globalEmitter.bind_global(callback);\n return this;\n }\n\n /**\n * Unbind a global event listener.\n */\n public unbind_global(callback?: GlobalEventCallback): this {\n this.globalEmitter.unbind_global(callback);\n return this;\n }\n\n /**\n * Connect to the SSE endpoint.\n */\n public connect(): void {\n this.connection.connect();\n }\n\n /**\n * Disconnect from the SSE endpoint.\n */\n public disconnect(): void {\n this.connection.disconnect();\n }\n\n /**\n * Route incoming SSE message to global emitters and specific channels.\n */\n private routeMessage(payload: SSEMessagePayload): void {\n const metadata = {\n channel: payload.channel,\n event: payload.event,\n id: payload.id,\n timestamp: payload.timestamp,\n };\n\n // 1. Dispatch to global agent emitter\n this.globalEmitter.emit(payload.event, payload.data, metadata);\n\n // 2. Dispatch to specific channel if message contains a channel\n if (payload.channel) {\n const targetChannel = this.channels.get(payload.channel);\n if (targetChannel) {\n targetChannel.handleEvent(payload.event, payload.data, metadata);\n }\n } else {\n // If message has no channel specified, notify all active channels\n for (const ch of this.channels.values()) {\n ch.handleEvent(payload.event, payload.data, metadata);\n }\n }\n }\n}\n","import { NotifAgent } from './client/NotifAgent';\n\nexport { NotifAgent } from './client/NotifAgent';\nexport { Channel } from './client/Channel';\nexport { ConnectionManager } from './client/ConnectionManager';\nexport { EventEmitter } from './client/EventEmitter';\nexport * from './client/types';\n\nexport default NotifAgent;\n"]}
1
+ {"version":3,"sources":["../src/client/EventEmitter.ts","../src/client/Channel.ts","../src/client/ConnectionManager.ts","../src/client/NotifAgent.ts","../src/index.ts"],"names":[],"mappings":"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","file":"index.mjs","sourcesContent":["import { EventCallback, GlobalEventCallback, EventMetadata } from './types';\n\nexport class EventEmitter {\n private listeners: Map<string, Set<EventCallback>> = new Map();\n private globalListeners: Set<GlobalEventCallback> = new Set();\n\n /**\n * Bind a callback to a specific event name.\n */\n public bind<T = any>(event: string, callback: EventCallback<T>): this {\n if (!this.listeners.has(event)) {\n this.listeners.set(event, new Set());\n }\n this.listeners.get(event)!.add(callback);\n return this;\n }\n\n /**\n * Unbind a callback from a specific event name, or all callbacks if callback is omitted.\n * If both event and callback are omitted, all listeners are removed.\n */\n public unbind<T = any>(event?: string, callback?: EventCallback<T>): this {\n if (!event) {\n this.listeners.clear();\n return this;\n }\n\n if (!callback) {\n this.listeners.delete(event);\n return this;\n }\n\n const set = this.listeners.get(event);\n if (set) {\n set.delete(callback);\n if (set.size === 0) {\n this.listeners.delete(event);\n }\n }\n\n return this;\n }\n\n /**\n * Bind a callback to ALL incoming events on this emitter.\n */\n public bind_global(callback: GlobalEventCallback): this {\n this.globalListeners.add(callback);\n return this;\n }\n\n /**\n * Unbind a global callback, or all global callbacks if callback is omitted.\n */\n public unbind_global(callback?: GlobalEventCallback): this {\n if (!callback) {\n this.globalListeners.clear();\n return this;\n }\n this.globalListeners.delete(callback);\n return this;\n }\n\n /**\n * Emit an event to registered listeners and global listeners.\n */\n public emit<T = any>(event: string, data: T, metadata?: EventMetadata): void {\n const set = this.listeners.get(event);\n if (set) {\n for (const cb of Array.from(set)) {\n try {\n cb(data, metadata);\n } catch (err) {\n console.error(`[notif-agent] Error in listener for event \"${event}\":`, err);\n }\n }\n }\n\n for (const globalCb of Array.from(this.globalListeners)) {\n try {\n globalCb(event, data, metadata);\n } catch (err) {\n console.error(`[notif-agent] Error in global listener for event \"${event}\":`, err);\n }\n }\n }\n\n /**\n * Clear all listeners.\n */\n public clear(): void {\n this.listeners.clear();\n this.globalListeners.clear();\n }\n}\n","import { EventEmitter } from './EventEmitter';\nimport { ChannelSubscriptionState, EventCallback, GlobalEventCallback, EventMetadata } from './types';\n\nexport class Channel {\n public readonly name: string;\n public state: ChannelSubscriptionState = 'subscribing';\n private emitter: EventEmitter = new EventEmitter();\n\n constructor(name: string) {\n this.name = name;\n }\n\n /**\n * Whether the channel is currently successfully subscribed.\n */\n public get subscribed(): boolean {\n return this.state === 'subscribed';\n }\n\n /**\n * Bind an event listener to this channel.\n *\n * Example:\n * ```ts\n * channel.bind('new-comment', (data) => {\n * console.log(data);\n * });\n * ```\n */\n public bind<T = any>(event: string, callback: EventCallback<T>): this {\n this.emitter.bind(event, callback);\n return this;\n }\n\n /**\n * Unbind an event listener from this channel.\n */\n public unbind<T = any>(event?: string, callback?: EventCallback<T>): this {\n this.emitter.unbind(event, callback);\n return this;\n }\n\n /**\n * Bind a global listener to all events received on this channel.\n *\n * Example:\n * ```ts\n * channel.bind_global((eventName, data) => {\n * console.log(`Event ${eventName}:`, data);\n * });\n * ```\n */\n public bind_global(callback: GlobalEventCallback): this {\n this.emitter.bind_global(callback);\n return this;\n }\n\n /**\n * Unbind a global listener from this channel.\n */\n public unbind_global(callback?: GlobalEventCallback): this {\n this.emitter.unbind_global(callback);\n return this;\n }\n\n /**\n * Internal method to dispatch an event onto this channel.\n */\n public handleEvent<T = any>(event: string, data: T, metadata?: EventMetadata): void {\n this.emitter.emit(event, data, {\n ...metadata,\n event,\n channel: this.name,\n });\n }\n\n /**\n * Internal method to update subscription status and fire Pusher-compatible subscription events.\n */\n public setSubscribed(data?: any): void {\n this.state = 'subscribed';\n // Pusher compatibility and standard notif-agent event\n this.handleEvent('subscription_succeeded', data ?? {});\n this.handleEvent('pusher:subscription_succeeded', data ?? {});\n }\n\n /**\n * Internal method to set subscription error.\n */\n public setSubscriptionError(error: any): void {\n this.state = 'error';\n this.handleEvent('subscription_error', error);\n this.handleEvent('pusher:subscription_error', error);\n }\n\n /**\n * Internal method called when channel is unsubscribed.\n */\n public setUnsubscribed(): void {\n this.state = 'unsubscribed';\n this.emitter.clear();\n }\n}\n","import { EventEmitter } from './EventEmitter';\nimport {\n ConnectionState,\n NotifAgentOptions,\n SSEMessagePayload,\n StateChangePayload,\n} from './types';\n\nexport class ConnectionManager {\n public state: ConnectionState = 'initialized';\n public clientId: string | null = null;\n\n /**\n * Pusher-compatible alias for clientId.\n */\n public get socket_id(): string | null {\n return this.clientId;\n }\n\n private emitter: EventEmitter = new EventEmitter();\n private options: Required<Omit<NotifAgentOptions, 'headers' | 'queryParams' | 'url' | 'authEndpoint' | 'auth'>> & {\n url: string;\n headers?: NotifAgentOptions['headers'];\n queryParams?: NotifAgentOptions['queryParams'];\n authEndpoint?: NotifAgentOptions['authEndpoint'];\n auth?: NotifAgentOptions['auth'];\n };\n\n private eventSource: EventSource | null = null;\n private abortController: AbortController | null = null;\n private reconnectAttempts = 0;\n private reconnectTimer: ReturnType<typeof setTimeout> | null = null;\n private heartbeatTimer: ReturnType<typeof setTimeout> | null = null;\n private isManuallyClosed = false;\n\n constructor(url: string, options: NotifAgentOptions = {}) {\n this.options = {\n url,\n headers: options.headers,\n queryParams: options.queryParams,\n withCredentials: options.withCredentials ?? false,\n transport: options.transport ?? 'auto',\n reconnectBaseDelay: options.reconnectBaseDelay ?? 1000,\n reconnectMaxDelay: options.reconnectMaxDelay ?? 30000,\n maxReconnectAttempts: options.maxReconnectAttempts ?? Infinity,\n heartbeatTimeout: options.heartbeatTimeout ?? 45000,\n autoConnect: options.autoConnect ?? true,\n debug: options.debug ?? false,\n };\n\n if (this.options.autoConnect) {\n // Connect on next tick so callers have time to bind connection listeners\n setTimeout(() => {\n if (!this.isManuallyClosed && this.state === 'initialized') {\n this.connect();\n }\n }, 0);\n }\n }\n\n public bind<T = any>(event: string, callback: (data: T) => void): this {\n this.emitter.bind(event, callback);\n return this;\n }\n\n public unbind<T = any>(event?: string, callback?: (data: T) => void): this {\n this.emitter.unbind(event, callback);\n return this;\n }\n\n public connect(): void {\n this.isManuallyClosed = false;\n this.clearTimers();\n\n if (this.state === 'connected' || this.state === 'connecting') {\n return;\n }\n\n this.transition('connecting');\n this.establishConnection();\n }\n\n public disconnect(): void {\n this.isManuallyClosed = true;\n this.clientId = null;\n this.clearTimers();\n this.closeActiveTransport();\n this.transition('disconnected');\n }\n\n private transition(nextState: ConnectionState): void {\n if (this.state === nextState) return;\n\n const previous = this.state;\n this.state = nextState;\n\n this.log(`State transition: ${previous} -> ${nextState}`);\n\n const payload: StateChangePayload = { previous, current: nextState };\n this.emitter.emit('state_change', payload);\n this.emitter.emit(nextState, payload);\n }\n\n private async establishConnection(): Promise<void> {\n try {\n const headers = await this.resolveHeaders();\n const hasCustomHeaders = headers && Object.keys(headers).length > 0;\n const shouldUseFetch =\n this.options.transport === 'fetch' ||\n (this.options.transport === 'auto' && (hasCustomHeaders || typeof EventSource === 'undefined'));\n\n const fullUrl = this.buildUrl();\n\n if (shouldUseFetch) {\n this.log(`Connecting via Fetch SSE stream: ${fullUrl}`);\n this.connectViaFetch(fullUrl, headers);\n } else {\n this.log(`Connecting via native EventSource: ${fullUrl}`);\n this.connectViaEventSource(fullUrl);\n }\n } catch (err) {\n this.handleConnectionFailure(err);\n }\n }\n\n private connectViaEventSource(url: string): void {\n this.closeActiveTransport();\n\n try {\n const es = new EventSource(url, {\n withCredentials: this.options.withCredentials,\n });\n this.eventSource = es;\n\n es.onopen = () => {\n this.handleConnectionSuccess();\n };\n\n es.onmessage = (e: MessageEvent) => {\n this.resetHeartbeat();\n this.parseAndDispatchMessage('message', e.data, e.lastEventId);\n };\n\n es.onerror = (err) => {\n this.log('EventSource error occurred', err);\n if (es.readyState === EventSource.CLOSED) {\n this.handleConnectionFailure(err);\n } else {\n // EventSource is in CONNECTING state attempting internal reconnect\n this.transition('unavailable');\n }\n };\n } catch (err) {\n this.handleConnectionFailure(err);\n }\n }\n\n private async connectViaFetch(url: string, headers?: Record<string, string>): Promise<void> {\n this.closeActiveTransport();\n this.abortController = new AbortController();\n\n try {\n const requestHeaders: Record<string, string> = {\n Accept: 'text/event-stream',\n 'Cache-Control': 'no-cache',\n ...headers,\n };\n\n const response = await fetch(url, {\n method: 'GET',\n headers: requestHeaders,\n credentials: this.options.withCredentials ? 'include' : 'same-origin',\n signal: this.abortController.signal,\n });\n\n if (!response.ok) {\n throw new Error(`SSE connection failed with HTTP status ${response.status}: ${response.statusText}`);\n }\n\n if (!response.body) {\n throw new Error('Response body is null or stream reading is not supported');\n }\n\n this.handleConnectionSuccess();\n\n const reader = response.body.getReader();\n const decoder = new TextDecoder('utf-8');\n let buffer = '';\n\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n\n this.resetHeartbeat();\n buffer += decoder.decode(value, { stream: true });\n\n // Process SSE frames (blocks ending with double newline)\n const parts = buffer.split(/\\r\\n\\r\\n|\\n\\n/);\n buffer = parts.pop() ?? '';\n\n for (const part of parts) {\n if (part.trim().length > 0) {\n this.processSSEFrame(part);\n }\n }\n }\n\n // Stream closed normally from server\n if (!this.isManuallyClosed) {\n this.handleConnectionFailure(new Error('SSE stream ended by server'));\n }\n } catch (err: any) {\n if (err.name === 'AbortError' || this.isManuallyClosed) {\n // Expected cancellation\n return;\n }\n this.handleConnectionFailure(err);\n }\n }\n\n private processSSEFrame(frame: string): void {\n const lines = frame.split(/\\r\\n|\\r|\\n/);\n let event = 'message';\n let data = '';\n let id: string | undefined;\n\n for (const line of lines) {\n if (line.startsWith(':')) {\n // Comment / ping heartbeat\n this.resetHeartbeat();\n continue;\n }\n\n if (line.startsWith('event:')) {\n event = line.slice(6).trim();\n } else if (line.startsWith('data:')) {\n const value = line.slice(5).trim();\n data += (data ? '\\n' : '') + value;\n } else if (line.startsWith('id:')) {\n id = line.slice(3).trim();\n } else if (line.startsWith('retry:')) {\n const retryMs = parseInt(line.slice(6).trim(), 10);\n if (!isNaN(retryMs)) {\n this.options.reconnectBaseDelay = retryMs;\n }\n }\n }\n\n if (data || event !== 'message') {\n this.parseAndDispatchMessage(event, data, id);\n }\n }\n\n private parseAndDispatchMessage(sseEvent: string, rawData: string, id?: string): void {\n let parsedData: any = rawData;\n let channel: string | undefined;\n let eventName = sseEvent;\n\n if (rawData) {\n try {\n const json = JSON.parse(rawData);\n // Check if server packaged channel & event inside json payload\n if (typeof json === 'object' && json !== null) {\n if (json.channel && typeof json.channel === 'string') {\n channel = json.channel;\n }\n if (json.event && typeof json.event === 'string') {\n eventName = json.event;\n }\n if ('data' in json) {\n parsedData = json.data;\n } else {\n parsedData = json;\n }\n } else {\n parsedData = json;\n }\n } catch {\n parsedData = rawData;\n }\n }\n\n // Handle handshake event from NotifServer\n if (eventName === 'connected' && parsedData && typeof parsedData === 'object' && parsedData.clientId) {\n this.clientId = parsedData.clientId;\n this.emitter.emit('handshake', parsedData);\n }\n\n // Handle heartbeat pings\n if (eventName === 'ping' || eventName === 'heartbeat' || eventName === ':ping') {\n this.resetHeartbeat();\n return;\n }\n\n const payload: SSEMessagePayload = {\n channel,\n event: eventName,\n data: parsedData,\n id,\n timestamp: Date.now(),\n };\n\n this.emitter.emit('message', payload);\n }\n\n private handleConnectionSuccess(): void {\n this.reconnectAttempts = 0;\n this.transition('connected');\n this.resetHeartbeat();\n }\n\n private handleConnectionFailure(error: any): void {\n this.log('Connection failed or dropped', error);\n this.clientId = null; // the server forgets this id once the stream closes\n this.closeActiveTransport();\n this.clearHeartbeat();\n\n if (this.isManuallyClosed) {\n return;\n }\n\n this.emitter.emit('error', error);\n\n this.reconnectAttempts++;\n if (this.reconnectAttempts > this.options.maxReconnectAttempts) {\n this.log(`Max reconnect attempts (${this.options.maxReconnectAttempts}) exceeded. State -> failed`);\n this.transition('failed');\n return;\n }\n\n this.transition('unavailable');\n this.scheduleReconnect();\n }\n\n private scheduleReconnect(): void {\n if (this.reconnectTimer) clearTimeout(this.reconnectTimer);\n\n // Exponential backoff with jitter\n const delay = Math.min(\n this.options.reconnectBaseDelay * Math.pow(1.5, this.reconnectAttempts - 1),\n this.options.reconnectMaxDelay\n );\n const jitter = delay * 0.2 * (Math.random() - 0.5); // +/- 10%\n const totalDelay = Math.max(100, Math.round(delay + jitter));\n\n this.log(`Scheduling reconnect in ${totalDelay}ms (attempt #${this.reconnectAttempts})`);\n\n this.reconnectTimer = setTimeout(() => {\n if (!this.isManuallyClosed && this.state !== 'connected') {\n this.transition('connecting');\n this.establishConnection();\n }\n }, totalDelay);\n }\n\n private resetHeartbeat(): void {\n this.clearHeartbeat();\n\n if (this.options.heartbeatTimeout <= 0) return;\n\n this.heartbeatTimer = setTimeout(() => {\n this.log(`Heartbeat timeout (${this.options.heartbeatTimeout}ms) exceeded. Connection dropped.`);\n this.handleConnectionFailure(new Error('Heartbeat timeout'));\n }, this.options.heartbeatTimeout);\n }\n\n private clearHeartbeat(): void {\n if (this.heartbeatTimer) {\n clearTimeout(this.heartbeatTimer);\n this.heartbeatTimer = null;\n }\n }\n\n private clearTimers(): void {\n this.clearHeartbeat();\n if (this.reconnectTimer) {\n clearTimeout(this.reconnectTimer);\n this.reconnectTimer = null;\n }\n }\n\n private closeActiveTransport(): void {\n if (this.eventSource) {\n try {\n this.eventSource.close();\n } catch {}\n this.eventSource = null;\n }\n\n if (this.abortController) {\n try {\n this.abortController.abort();\n } catch {}\n this.abortController = null;\n }\n }\n\n private buildUrl(): string {\n const url = new URL(this.options.url, typeof window !== 'undefined' ? window.location.href : 'http://localhost');\n if (this.options.queryParams) {\n for (const [key, value] of Object.entries(this.options.queryParams)) {\n if (value !== undefined && value !== null) {\n url.searchParams.set(key, String(value));\n }\n }\n }\n return url.toString();\n }\n\n private async resolveHeaders(): Promise<Record<string, string> | undefined> {\n if (!this.options.headers) return undefined;\n if (typeof this.options.headers === 'function') {\n return await this.options.headers();\n }\n return this.options.headers;\n }\n\n private log(...args: any[]): void {\n if (this.options.debug) {\n console.log('[notif-agent:connection]', ...args);\n }\n }\n}\n","import { Channel } from './Channel';\nimport { ConnectionManager } from './ConnectionManager';\nimport { EventEmitter } from './EventEmitter';\nimport {\n EventCallback,\n GlobalEventCallback,\n NotifAgentOptions,\n SSEMessagePayload,\n} from './types';\n\nexport class NotifAgent {\n public readonly connection: ConnectionManager;\n private options: NotifAgentOptions;\n private channels: Map<string, Channel> = new Map();\n private globalEmitter: EventEmitter = new EventEmitter();\n\n /**\n * Unique client connection ID provided by NotifServer handshake.\n */\n public get clientId(): string | null {\n return this.connection.clientId;\n }\n\n /**\n * Pusher-compatible alias for clientId.\n */\n public get socket_id(): string | null {\n return this.connection.clientId;\n }\n\n constructor(urlOrOptions: string | NotifAgentOptions, maybeOptions?: NotifAgentOptions) {\n let options: NotifAgentOptions;\n\n if (typeof urlOrOptions === 'string') {\n options = { ...(maybeOptions ?? {}), url: urlOrOptions };\n } else {\n options = urlOrOptions;\n }\n\n if (!options.url) {\n throw new Error('[notif-agent] SSE URL must be specified');\n }\n\n this.options = options;\n this.connection = new ConnectionManager(options.url, options);\n\n // Listen to connection messages to route events\n this.connection.bind<SSEMessagePayload>('message', (payload) => {\n this.routeMessage(payload);\n });\n\n const triggerChannelSubscriptions = () => {\n for (const channel of this.channels.values()) {\n const isPrivate = channel.name.startsWith('private-') || channel.name.startsWith('presence-');\n if (isPrivate && this.options.authEndpoint) {\n this.authorizeChannel(channel);\n } else {\n channel.setSubscribed();\n }\n }\n };\n\n // Subscribe once per connection, after the server hands us a clientId (also on every reconnect).\n this.connection.bind('handshake', () => {\n triggerChannelSubscriptions();\n });\n }\n\n /**\n * Subscribe to a channel.\n *\n * Example:\n * ```ts\n * const channel = agent.subscribe('orders');\n * channel.bind('created', (order) => { ... });\n * ```\n */\n public subscribe(channelName: string): Channel {\n if (!channelName) {\n throw new Error('[notif-agent] Channel name cannot be empty');\n }\n\n let channel = this.channels.get(channelName);\n if (!channel) {\n channel = new Channel(channelName);\n this.channels.set(channelName, channel);\n\n // Handshake already done: subscribe now. Otherwise the handshake handler will.\n if (this.connection.state === 'connected' && this.connection.clientId) {\n setTimeout(() => {\n const isPrivate = channelName.startsWith('private-') || channelName.startsWith('presence-');\n if (isPrivate && this.options.authEndpoint) {\n this.authorizeChannel(channel!);\n } else {\n channel?.setSubscribed();\n }\n }, 0);\n }\n }\n\n return channel;\n }\n\n /**\n * Internal authorization helper for private/presence channels.\n */\n private async authorizeChannel(channel: Channel): Promise<void> {\n if (!this.options.authEndpoint) {\n channel.setSubscribed();\n return;\n }\n\n try {\n let customHeaders: Record<string, string> = {};\n if (typeof this.options.auth?.headers === 'function') {\n customHeaders = await this.options.auth.headers();\n } else if (this.options.auth?.headers) {\n customHeaders = this.options.auth.headers;\n }\n\n const body = {\n clientId: this.connection.clientId,\n socket_id: this.connection.clientId,\n channel: channel.name,\n ...(this.options.auth?.params ?? {}),\n };\n\n const response = await fetch(this.options.authEndpoint, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n ...customHeaders,\n },\n body: JSON.stringify(body),\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(`[notif-agent] Authorization failed (${response.status}): ${errorText}`);\n }\n\n const authData = await response.json().catch(() => ({}));\n channel.setSubscribed(authData);\n } catch (err) {\n channel.setSubscriptionError(err);\n }\n }\n\n /**\n * Unsubscribe from a channel.\n */\n public unsubscribe(channelName: string): void {\n const channel = this.channels.get(channelName);\n if (channel) {\n channel.setUnsubscribed();\n this.channels.delete(channelName);\n }\n }\n\n /**\n * Get an existing subscribed channel by name, if any.\n */\n public channel(channelName: string): Channel | undefined {\n return this.channels.get(channelName);\n }\n\n /**\n * Return all currently subscribed channels.\n */\n public allChannels(): Channel[] {\n return Array.from(this.channels.values());\n }\n\n /**\n * Bind to an event across all channels or global events.\n *\n * Example:\n * ```ts\n * agent.bind('system-alert', (data) => console.log(data));\n * ```\n */\n public bind<T = any>(event: string, callback: EventCallback<T>): this {\n this.globalEmitter.bind(event, callback);\n return this;\n }\n\n /**\n * Unbind an event listener.\n */\n public unbind<T = any>(event?: string, callback?: EventCallback<T>): this {\n this.globalEmitter.unbind(event, callback);\n return this;\n }\n\n /**\n * Bind to all events regardless of name.\n */\n public bind_global(callback: GlobalEventCallback): this {\n this.globalEmitter.bind_global(callback);\n return this;\n }\n\n /**\n * Unbind a global event listener.\n */\n public unbind_global(callback?: GlobalEventCallback): this {\n this.globalEmitter.unbind_global(callback);\n return this;\n }\n\n /**\n * Connect to the SSE endpoint.\n */\n public connect(): void {\n this.connection.connect();\n }\n\n /**\n * Disconnect from the SSE endpoint.\n */\n public disconnect(): void {\n this.connection.disconnect();\n }\n\n /**\n * Route incoming SSE message to global emitters and specific channels.\n */\n private routeMessage(payload: SSEMessagePayload): void {\n const metadata = {\n channel: payload.channel,\n event: payload.event,\n id: payload.id,\n timestamp: payload.timestamp,\n };\n\n // 1. Dispatch to global agent emitter\n this.globalEmitter.emit(payload.event, payload.data, metadata);\n\n // 2. Dispatch to specific channel if message contains a channel\n if (payload.channel) {\n const targetChannel = this.channels.get(payload.channel);\n if (targetChannel) {\n targetChannel.handleEvent(payload.event, payload.data, metadata);\n }\n } else {\n // If message has no channel specified, notify all active channels\n for (const ch of this.channels.values()) {\n ch.handleEvent(payload.event, payload.data, metadata);\n }\n }\n }\n}\n","import { NotifAgent } from './client/NotifAgent';\n\nexport { NotifAgent } from './client/NotifAgent';\nexport { Channel } from './client/Channel';\nexport { ConnectionManager } from './client/ConnectionManager';\nexport { EventEmitter } from './client/EventEmitter';\nexport * from './client/types';\n\nexport default NotifAgent;\n"]}
package/dist/server.d.mts CHANGED
@@ -119,6 +119,14 @@ declare class NotifServer {
119
119
  */
120
120
  close(): void;
121
121
  private removeClient;
122
+ /**
123
+ * App-given channels are final; the URL can only add public channels when the app gave none.
124
+ * `*` (all public channels) is the default when neither names any.
125
+ */
126
+ private resolveChannels;
127
+ /** `*` never covers private channels; those need an explicit grant (options.channels or subscribeClient). */
128
+ private isSubscribed;
129
+ private formatMessage;
122
130
  private startHeartbeat;
123
131
  private log;
124
132
  }
package/dist/server.d.ts CHANGED
@@ -119,6 +119,14 @@ declare class NotifServer {
119
119
  */
120
120
  close(): void;
121
121
  private removeClient;
122
+ /**
123
+ * App-given channels are final; the URL can only add public channels when the app gave none.
124
+ * `*` (all public channels) is the default when neither names any.
125
+ */
126
+ private resolveChannels;
127
+ /** `*` never covers private channels; those need an explicit grant (options.channels or subscribeClient). */
128
+ private isSubscribed;
129
+ private formatMessage;
122
130
  private startHeartbeat;
123
131
  private log;
124
132
  }
package/dist/server.js CHANGED
@@ -1,6 +1,8 @@
1
1
  'use strict';
2
2
 
3
3
  // src/server/NotifServer.ts
4
+ var isPrivate = (ch) => ch.startsWith("private-") || ch.startsWith("presence-");
5
+ var MAX_BUFFERED_BYTES = 1024 * 1024;
4
6
  var NotifServer = class {
5
7
  clients = /* @__PURE__ */ new Map();
6
8
  heartbeatInterval;
@@ -28,7 +30,7 @@ var NotifServer = class {
28
30
  const channels = Array.isArray(channel) ? channel : [channel];
29
31
  for (const ch of channels) {
30
32
  for (const client of this.clients.values()) {
31
- if (client.channels.has(ch) || client.channels.has("*")) {
33
+ if (this.isSubscribed(client, ch)) {
32
34
  client.send(event, data, { channel: ch, id: options?.id, sseEvent: options?.sseEvent });
33
35
  }
34
36
  }
@@ -100,7 +102,7 @@ var NotifServer = class {
100
102
  getSubscribersCount(channel) {
101
103
  let count = 0;
102
104
  for (const client of this.clients.values()) {
103
- if (client.channels.has(channel) || client.channels.has("*")) {
105
+ if (this.isSubscribed(client, channel)) {
104
106
  count++;
105
107
  }
106
108
  }
@@ -121,31 +123,24 @@ var NotifServer = class {
121
123
  "Content-Type": "text/event-stream",
122
124
  "Cache-Control": "no-cache, no-transform",
123
125
  Connection: "keep-alive",
124
- "X-Accel-Buffering": "no",
126
+ "X-Accel-Buffering": "no"
125
127
  // Disable proxy buffering for Nginx
126
- "Access-Control-Allow-Origin": "*"
127
128
  });
128
129
  if (typeof res.flushHeaders === "function") {
129
130
  res.flushHeaders();
130
131
  }
131
132
  const clientId = options?.clientId ?? this.generateClientId();
132
- const subscribedChannels = new Set(options?.channels ?? []);
133
- if (req.url) {
134
- try {
135
- const url = new URL(req.url, "http://localhost");
136
- const queryChannels = url.searchParams.get("channels");
137
- if (queryChannels) {
138
- for (const ch of queryChannels.split(",")) {
139
- const trimmed = ch.trim();
140
- if (trimmed) subscribedChannels.add(trimmed);
141
- }
142
- }
143
- } catch {
133
+ const subscribedChannels = this.resolveChannels(req.url, options?.channels);
134
+ const write = (chunk) => {
135
+ const ok = res.write(chunk);
136
+ if (typeof res.flush === "function") {
137
+ res.flush();
144
138
  }
145
- }
146
- if (subscribedChannels.size === 0 && !options?.channels) {
147
- subscribedChannels.add("*");
148
- }
139
+ if (!ok && res.writableLength > MAX_BUFFERED_BYTES) {
140
+ this.log(`Client ${clientId} too slow, dropping`);
141
+ client.close();
142
+ }
143
+ };
149
144
  const client = {
150
145
  id: clientId,
151
146
  userId: options?.userId,
@@ -153,35 +148,16 @@ var NotifServer = class {
153
148
  meta: options?.meta ?? {},
154
149
  send: (event, data, opt) => {
155
150
  try {
156
- let message = "";
157
- if (opt?.id) message += `id: ${opt.id}
158
- `;
159
- message += `event: ${opt?.sseEvent ?? "message"}
160
- `;
161
- const payload = {
162
- channel: opt?.channel,
163
- event,
164
- data
165
- };
166
- message += `data: ${JSON.stringify(payload)}
167
-
168
- `;
169
- res.write(message);
170
- if (typeof res.flush === "function") {
171
- res.flush();
172
- }
151
+ write(this.formatMessage(event, data, opt));
173
152
  } catch (err) {
174
153
  this.log(`Error writing to client ${clientId}:`, err);
175
154
  }
176
155
  },
177
156
  sendComment: (comment) => {
178
157
  try {
179
- res.write(`: ${comment}
158
+ write(`: ${comment}
180
159
 
181
160
  `);
182
- if (typeof res.flush === "function") {
183
- res.flush();
184
- }
185
161
  } catch {
186
162
  }
187
163
  },
@@ -217,23 +193,7 @@ var NotifServer = class {
217
193
  */
218
194
  createWebResponse(request, options) {
219
195
  const clientId = options?.clientId ?? this.generateClientId();
220
- const subscribedChannels = new Set(options?.channels ?? []);
221
- if (request?.url) {
222
- try {
223
- const url = new URL(request.url);
224
- const queryChannels = url.searchParams.get("channels");
225
- if (queryChannels) {
226
- for (const ch of queryChannels.split(",")) {
227
- const trimmed = ch.trim();
228
- if (trimmed) subscribedChannels.add(trimmed);
229
- }
230
- }
231
- } catch {
232
- }
233
- }
234
- if (subscribedChannels.size === 0 && !options?.channels) {
235
- subscribedChannels.add("*");
236
- }
196
+ const subscribedChannels = this.resolveChannels(request?.url, options?.channels);
237
197
  const encoder = new TextEncoder();
238
198
  const stream = new ReadableStream({
239
199
  start: (controller) => {
@@ -244,20 +204,7 @@ var NotifServer = class {
244
204
  meta: options?.meta ?? {},
245
205
  send: (event, data, opt) => {
246
206
  try {
247
- let message = "";
248
- if (opt?.id) message += `id: ${opt.id}
249
- `;
250
- message += `event: ${opt?.sseEvent ?? "message"}
251
- `;
252
- const payload = {
253
- channel: opt?.channel,
254
- event,
255
- data
256
- };
257
- message += `data: ${JSON.stringify(payload)}
258
-
259
- `;
260
- controller.enqueue(encoder.encode(message));
207
+ controller.enqueue(encoder.encode(this.formatMessage(event, data, opt)));
261
208
  } catch (err) {
262
209
  this.log(`Error enqueueing to client ${clientId}:`, err);
263
210
  }
@@ -290,8 +237,7 @@ var NotifServer = class {
290
237
  headers: {
291
238
  "Content-Type": "text/event-stream",
292
239
  "Cache-Control": "no-cache, no-transform",
293
- Connection: "keep-alive",
294
- "Access-Control-Allow-Origin": "*"
240
+ Connection: "keep-alive"
295
241
  }
296
242
  });
297
243
  }
@@ -314,10 +260,45 @@ var NotifServer = class {
314
260
  this.log(`Client disconnected: ${clientId}`);
315
261
  }
316
262
  }
263
+ /**
264
+ * App-given channels are final; the URL can only add public channels when the app gave none.
265
+ * `*` (all public channels) is the default when neither names any.
266
+ */
267
+ resolveChannels(rawUrl, allowed) {
268
+ if (allowed) return new Set(allowed);
269
+ const channels = /* @__PURE__ */ new Set();
270
+ if (rawUrl) {
271
+ try {
272
+ const query = new URL(rawUrl, "http://localhost").searchParams.get("channels") ?? "";
273
+ for (const ch of query.split(",")) {
274
+ const trimmed = ch.trim();
275
+ if (trimmed && !isPrivate(trimmed)) channels.add(trimmed);
276
+ }
277
+ } catch {
278
+ }
279
+ }
280
+ if (channels.size === 0) channels.add("*");
281
+ return channels;
282
+ }
283
+ /** `*` never covers private channels; those need an explicit grant (options.channels or subscribeClient). */
284
+ isSubscribed(client, channel) {
285
+ return client.channels.has(channel) || client.channels.has("*") && !isPrivate(channel);
286
+ }
287
+ formatMessage(event, data, opt) {
288
+ let message = "";
289
+ if (opt?.id) message += `id: ${opt.id}
290
+ `;
291
+ message += `event: ${opt?.sseEvent ?? "message"}
292
+ `;
293
+ message += `data: ${JSON.stringify({ channel: opt?.channel, event, data })}
294
+
295
+ `;
296
+ return message;
297
+ }
317
298
  startHeartbeat() {
318
299
  this.heartbeatTimer = setInterval(() => {
319
300
  for (const client of this.clients.values()) {
320
- client.sendComment("ping");
301
+ client.send("ping", {});
321
302
  }
322
303
  }, this.heartbeatInterval);
323
304
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/server/NotifServer.ts"],"names":[],"mappings":"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","file":"server.js","sourcesContent":["import type { IncomingMessage, ServerResponse } from 'http';\n\nexport interface NotifClient {\n id: string;\n userId?: string;\n channels: Set<string>;\n send: (event: string, data: any, options?: { channel?: string; id?: string; sseEvent?: string }) => void;\n sendComment: (comment: string) => void;\n close: () => void;\n meta: Record<string, any>;\n}\n\nexport interface NotifServerOptions {\n /**\n * Heartbeat interval in milliseconds to keep SSE connections open through proxies.\n * Defaults to 20000ms (20s). Set to 0 to disable.\n */\n heartbeatInterval?: number;\n\n /**\n * Optional custom client ID generator.\n */\n generateClientId?: () => string;\n\n /**\n * Debug logging.\n */\n debug?: boolean;\n}\n\nexport interface TriggerOptions {\n id?: string;\n sseEvent?: string;\n}\n\nexport class NotifServer {\n private clients: Map<string, NotifClient> = new Map();\n private heartbeatInterval: number;\n private heartbeatTimer: ReturnType<typeof setInterval> | null = null;\n private generateClientId: () => string;\n private debug: boolean;\n\n constructor(options: NotifServerOptions = {}) {\n this.heartbeatInterval = options.heartbeatInterval ?? 20000;\n this.debug = options.debug ?? false;\n this.generateClientId =\n options.generateClientId ?? (() => `client_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`);\n\n if (this.heartbeatInterval > 0) {\n this.startHeartbeat();\n }\n }\n\n /**\n * Broadcast an event to one or more channels.\n * Matches Pusher's `pusher.trigger(channel, event, data)` API.\n *\n * Example:\n * ```ts\n * server.trigger('orders', 'order-created', { id: 123, amount: 49.99 });\n * ```\n */\n public trigger(channel: string | string[], event: string, data: any, options?: TriggerOptions): void {\n const channels = Array.isArray(channel) ? channel : [channel];\n\n for (const ch of channels) {\n for (const client of this.clients.values()) {\n if (client.channels.has(ch) || client.channels.has('*')) {\n client.send(event, data, { channel: ch, id: options?.id, sseEvent: options?.sseEvent });\n }\n }\n }\n }\n\n /**\n * Alias for trigger().\n */\n public broadcast(channel: string | string[], event: string, data: any, options?: TriggerOptions): void {\n this.trigger(channel, event, data, options);\n }\n\n /**\n * Send an event directly to a specific client ID.\n */\n public sendToClient(clientId: string, event: string, data: any, options?: TriggerOptions & { channel?: string }): boolean {\n const client = this.clients.get(clientId);\n if (client) {\n client.send(event, data, { channel: options?.channel, id: options?.id, sseEvent: options?.sseEvent });\n return true;\n }\n return false;\n }\n\n /**\n * Send an event to all connected clients belonging to a specific user ID.\n */\n public sendToUser(userId: string, event: string, data: any, options?: TriggerOptions & { channel?: string }): number {\n let count = 0;\n for (const client of this.clients.values()) {\n if (client.userId === userId) {\n client.send(event, data, { channel: options?.channel, id: options?.id, sseEvent: options?.sseEvent });\n count++;\n }\n }\n return count;\n }\n\n /**\n * Subscribe a connected client to a channel.\n */\n public subscribeClient(clientId: string, channel: string): boolean {\n const client = this.clients.get(clientId);\n if (client) {\n client.channels.add(channel);\n this.log(`Client ${clientId} subscribed to \"${channel}\"`);\n return true;\n }\n return false;\n }\n\n /**\n * Unsubscribe a connected client from a channel.\n */\n public unsubscribeClient(clientId: string, channel: string): boolean {\n const client = this.clients.get(clientId);\n if (client) {\n const removed = client.channels.delete(channel);\n this.log(`Client ${clientId} unsubscribed from \"${channel}\"`);\n return removed;\n }\n return false;\n }\n\n /**\n * Get total connected clients count.\n */\n public getClientCount(): number {\n return this.clients.size;\n }\n\n /**\n * Get subscribers count for a specific channel.\n */\n public getSubscribersCount(channel: string): number {\n let count = 0;\n for (const client of this.clients.values()) {\n if (client.channels.has(channel) || client.channels.has('*')) {\n count++;\n }\n }\n return count;\n }\n\n /**\n * Handle incoming HTTP request in Node.js / Express / Fastify environment.\n *\n * Example:\n * ```ts\n * app.get('/sse', (req, res) => {\n * server.handleRequest(req, res);\n * });\n * ```\n */\n public handleRequest(\n req: IncomingMessage,\n res: ServerResponse,\n options?: {\n clientId?: string;\n userId?: string;\n channels?: string[];\n meta?: Record<string, any>;\n }\n ): NotifClient {\n // Set standard SSE HTTP headers\n res.writeHead(200, {\n 'Content-Type': 'text/event-stream',\n 'Cache-Control': 'no-cache, no-transform',\n Connection: 'keep-alive',\n 'X-Accel-Buffering': 'no', // Disable proxy buffering for Nginx\n 'Access-Control-Allow-Origin': '*',\n });\n\n if (typeof (res as any).flushHeaders === 'function') {\n (res as any).flushHeaders();\n }\n\n const clientId = options?.clientId ?? this.generateClientId();\n\n // Parse initial channels from query parameters or options\n const subscribedChannels = new Set<string>(options?.channels ?? []);\n if (req.url) {\n try {\n const url = new URL(req.url, 'http://localhost');\n const queryChannels = url.searchParams.get('channels');\n if (queryChannels) {\n for (const ch of queryChannels.split(',')) {\n const trimmed = ch.trim();\n if (trimmed) subscribedChannels.add(trimmed);\n }\n }\n } catch {}\n }\n\n if (subscribedChannels.size === 0 && !options?.channels) {\n subscribedChannels.add('*');\n }\n\n const client: NotifClient = {\n id: clientId,\n userId: options?.userId,\n channels: subscribedChannels,\n meta: options?.meta ?? {},\n send: (event: string, data: any, opt?: { channel?: string; id?: string; sseEvent?: string }) => {\n try {\n let message = '';\n if (opt?.id) message += `id: ${opt.id}\\n`;\n message += `event: ${opt?.sseEvent ?? 'message'}\\n`;\n\n const payload = {\n channel: opt?.channel,\n event,\n data,\n };\n message += `data: ${JSON.stringify(payload)}\\n\\n`;\n\n res.write(message);\n if (typeof (res as any).flush === 'function') {\n (res as any).flush();\n }\n } catch (err) {\n this.log(`Error writing to client ${clientId}:`, err);\n }\n },\n sendComment: (comment: string) => {\n try {\n res.write(`: ${comment}\\n\\n`);\n if (typeof (res as any).flush === 'function') {\n (res as any).flush();\n }\n } catch {}\n },\n close: () => {\n try {\n res.end();\n } catch {}\n this.removeClient(clientId);\n },\n };\n\n this.clients.set(clientId, client);\n this.log(`Client connected: ${clientId}, channels: [${Array.from(subscribedChannels).join(', ')}]`);\n\n // Send connection established event\n client.send('connected', { clientId });\n\n // Handle connection close\n req.on('close', () => {\n this.removeClient(clientId);\n });\n\n res.on('close', () => {\n this.removeClient(clientId);\n });\n\n return client;\n }\n\n /**\n * Handle Web standard Request (Next.js App Router, Edge runtime, Hono, Cloudflare Workers).\n * Returns a standard `Response` object with a `ReadableStream`.\n *\n * Example:\n * ```ts\n * export function GET(request: Request) {\n * return server.createWebResponse(request);\n * }\n * ```\n */\n public createWebResponse(\n request?: Request,\n options?: {\n clientId?: string;\n userId?: string;\n channels?: string[];\n meta?: Record<string, any>;\n }\n ): Response {\n const clientId = options?.clientId ?? this.generateClientId();\n const subscribedChannels = new Set<string>(options?.channels ?? []);\n\n if (request?.url) {\n try {\n const url = new URL(request.url);\n const queryChannels = url.searchParams.get('channels');\n if (queryChannels) {\n for (const ch of queryChannels.split(',')) {\n const trimmed = ch.trim();\n if (trimmed) subscribedChannels.add(trimmed);\n }\n }\n } catch {}\n }\n\n if (subscribedChannels.size === 0 && !options?.channels) {\n subscribedChannels.add('*');\n }\n\n const encoder = new TextEncoder();\n let controllerRef: ReadableStreamDefaultController<Uint8Array> | null = null;\n\n const stream = new ReadableStream<Uint8Array>({\n start: (controller) => {\n controllerRef = controller;\n\n const client: NotifClient = {\n id: clientId,\n userId: options?.userId,\n channels: subscribedChannels,\n meta: options?.meta ?? {},\n send: (event: string, data: any, opt?: { channel?: string; id?: string; sseEvent?: string }) => {\n try {\n let message = '';\n if (opt?.id) message += `id: ${opt.id}\\n`;\n message += `event: ${opt?.sseEvent ?? 'message'}\\n`;\n\n const payload = {\n channel: opt?.channel,\n event,\n data,\n };\n message += `data: ${JSON.stringify(payload)}\\n\\n`;\n\n controller.enqueue(encoder.encode(message));\n } catch (err) {\n this.log(`Error enqueueing to client ${clientId}:`, err);\n }\n },\n sendComment: (comment: string) => {\n try {\n controller.enqueue(encoder.encode(`: ${comment}\\n\\n`));\n } catch {}\n },\n close: () => {\n try {\n controller.close();\n } catch {}\n this.removeClient(clientId);\n },\n };\n\n this.clients.set(clientId, client);\n this.log(`Web client connected: ${clientId}`);\n\n // Send connection handshake\n client.send('connected', { clientId });\n },\n cancel: () => {\n this.removeClient(clientId);\n },\n });\n\n return new Response(stream, {\n headers: {\n 'Content-Type': 'text/event-stream',\n 'Cache-Control': 'no-cache, no-transform',\n Connection: 'keep-alive',\n 'Access-Control-Allow-Origin': '*',\n },\n });\n }\n\n /**\n * Stop the server heartbeat and disconnect all clients.\n */\n public close(): void {\n if (this.heartbeatTimer) {\n clearInterval(this.heartbeatTimer);\n this.heartbeatTimer = null;\n }\n\n for (const client of this.clients.values()) {\n client.close();\n }\n this.clients.clear();\n }\n\n private removeClient(clientId: string): void {\n if (this.clients.has(clientId)) {\n this.clients.delete(clientId);\n this.log(`Client disconnected: ${clientId}`);\n }\n }\n\n private startHeartbeat(): void {\n this.heartbeatTimer = setInterval(() => {\n for (const client of this.clients.values()) {\n client.sendComment('ping');\n }\n }, this.heartbeatInterval);\n }\n\n private log(...args: any[]): void {\n if (this.debug) {\n console.log('[notif-agent:server]', ...args);\n }\n }\n}\n"]}
1
+ {"version":3,"sources":["../src/server/NotifServer.ts"],"names":[],"mappings":"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","file":"server.js","sourcesContent":["import type { IncomingMessage, ServerResponse } from 'http';\n\nexport interface NotifClient {\n id: string;\n userId?: string;\n channels: Set<string>;\n send: (event: string, data: any, options?: { channel?: string; id?: string; sseEvent?: string }) => void;\n sendComment: (comment: string) => void;\n close: () => void;\n meta: Record<string, any>;\n}\n\nexport interface NotifServerOptions {\n /**\n * Heartbeat interval in milliseconds to keep SSE connections open through proxies.\n * Defaults to 20000ms (20s). Set to 0 to disable.\n */\n heartbeatInterval?: number;\n\n /**\n * Optional custom client ID generator.\n */\n generateClientId?: () => string;\n\n /**\n * Debug logging.\n */\n debug?: boolean;\n}\n\nexport interface TriggerOptions {\n id?: string;\n sseEvent?: string;\n}\n\nconst isPrivate = (ch: string) => ch.startsWith('private-') || ch.startsWith('presence-');\n\n// A client whose unread buffer grows past this is dropped instead of buffering forever.\nconst MAX_BUFFERED_BYTES = 1024 * 1024;\n\nexport class NotifServer {\n private clients: Map<string, NotifClient> = new Map();\n private heartbeatInterval: number;\n private heartbeatTimer: ReturnType<typeof setInterval> | null = null;\n private generateClientId: () => string;\n private debug: boolean;\n\n constructor(options: NotifServerOptions = {}) {\n this.heartbeatInterval = options.heartbeatInterval ?? 20000;\n this.debug = options.debug ?? false;\n this.generateClientId =\n options.generateClientId ?? (() => `client_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`);\n\n if (this.heartbeatInterval > 0) {\n this.startHeartbeat();\n }\n }\n\n /**\n * Broadcast an event to one or more channels.\n * Matches Pusher's `pusher.trigger(channel, event, data)` API.\n *\n * Example:\n * ```ts\n * server.trigger('orders', 'order-created', { id: 123, amount: 49.99 });\n * ```\n */\n public trigger(channel: string | string[], event: string, data: any, options?: TriggerOptions): void {\n const channels = Array.isArray(channel) ? channel : [channel];\n\n for (const ch of channels) {\n for (const client of this.clients.values()) {\n if (this.isSubscribed(client, ch)) {\n client.send(event, data, { channel: ch, id: options?.id, sseEvent: options?.sseEvent });\n }\n }\n }\n }\n\n /**\n * Alias for trigger().\n */\n public broadcast(channel: string | string[], event: string, data: any, options?: TriggerOptions): void {\n this.trigger(channel, event, data, options);\n }\n\n /**\n * Send an event directly to a specific client ID.\n */\n public sendToClient(clientId: string, event: string, data: any, options?: TriggerOptions & { channel?: string }): boolean {\n const client = this.clients.get(clientId);\n if (client) {\n client.send(event, data, { channel: options?.channel, id: options?.id, sseEvent: options?.sseEvent });\n return true;\n }\n return false;\n }\n\n /**\n * Send an event to all connected clients belonging to a specific user ID.\n */\n public sendToUser(userId: string, event: string, data: any, options?: TriggerOptions & { channel?: string }): number {\n let count = 0;\n for (const client of this.clients.values()) {\n if (client.userId === userId) {\n client.send(event, data, { channel: options?.channel, id: options?.id, sseEvent: options?.sseEvent });\n count++;\n }\n }\n return count;\n }\n\n /**\n * Subscribe a connected client to a channel.\n */\n public subscribeClient(clientId: string, channel: string): boolean {\n const client = this.clients.get(clientId);\n if (client) {\n client.channels.add(channel);\n this.log(`Client ${clientId} subscribed to \"${channel}\"`);\n return true;\n }\n return false;\n }\n\n /**\n * Unsubscribe a connected client from a channel.\n */\n public unsubscribeClient(clientId: string, channel: string): boolean {\n const client = this.clients.get(clientId);\n if (client) {\n const removed = client.channels.delete(channel);\n this.log(`Client ${clientId} unsubscribed from \"${channel}\"`);\n return removed;\n }\n return false;\n }\n\n /**\n * Get total connected clients count.\n */\n public getClientCount(): number {\n return this.clients.size;\n }\n\n /**\n * Get subscribers count for a specific channel.\n */\n public getSubscribersCount(channel: string): number {\n let count = 0;\n for (const client of this.clients.values()) {\n if (this.isSubscribed(client, channel)) {\n count++;\n }\n }\n return count;\n }\n\n /**\n * Handle incoming HTTP request in Node.js / Express / Fastify environment.\n *\n * Example:\n * ```ts\n * app.get('/sse', (req, res) => {\n * server.handleRequest(req, res);\n * });\n * ```\n */\n public handleRequest(\n req: IncomingMessage,\n res: ServerResponse,\n options?: {\n clientId?: string;\n userId?: string;\n channels?: string[];\n meta?: Record<string, any>;\n }\n ): NotifClient {\n // Set standard SSE HTTP headers\n res.writeHead(200, {\n 'Content-Type': 'text/event-stream',\n 'Cache-Control': 'no-cache, no-transform',\n Connection: 'keep-alive',\n 'X-Accel-Buffering': 'no', // Disable proxy buffering for Nginx\n });\n\n if (typeof (res as any).flushHeaders === 'function') {\n (res as any).flushHeaders();\n }\n\n const clientId = options?.clientId ?? this.generateClientId();\n const subscribedChannels = this.resolveChannels(req.url, options?.channels);\n\n const write = (chunk: string) => {\n const ok = res.write(chunk);\n if (typeof (res as any).flush === 'function') {\n (res as any).flush();\n }\n if (!ok && res.writableLength > MAX_BUFFERED_BYTES) {\n this.log(`Client ${clientId} too slow, dropping`);\n client.close();\n }\n };\n\n const client: NotifClient = {\n id: clientId,\n userId: options?.userId,\n channels: subscribedChannels,\n meta: options?.meta ?? {},\n send: (event, data, opt) => {\n try {\n write(this.formatMessage(event, data, opt));\n } catch (err) {\n this.log(`Error writing to client ${clientId}:`, err);\n }\n },\n sendComment: (comment: string) => {\n try {\n write(`: ${comment}\\n\\n`);\n } catch {}\n },\n close: () => {\n try {\n res.end();\n } catch {}\n this.removeClient(clientId);\n },\n };\n\n this.clients.set(clientId, client);\n this.log(`Client connected: ${clientId}, channels: [${Array.from(subscribedChannels).join(', ')}]`);\n\n // Send connection established event\n client.send('connected', { clientId });\n\n // Handle connection close\n req.on('close', () => {\n this.removeClient(clientId);\n });\n\n res.on('close', () => {\n this.removeClient(clientId);\n });\n\n return client;\n }\n\n /**\n * Handle Web standard Request (Next.js App Router, Edge runtime, Hono, Cloudflare Workers).\n * Returns a standard `Response` object with a `ReadableStream`.\n *\n * Example:\n * ```ts\n * export function GET(request: Request) {\n * return server.createWebResponse(request);\n * }\n * ```\n */\n public createWebResponse(\n request?: Request,\n options?: {\n clientId?: string;\n userId?: string;\n channels?: string[];\n meta?: Record<string, any>;\n }\n ): Response {\n const clientId = options?.clientId ?? this.generateClientId();\n const subscribedChannels = this.resolveChannels(request?.url, options?.channels);\n\n const encoder = new TextEncoder();\n let controllerRef: ReadableStreamDefaultController<Uint8Array> | null = null;\n\n const stream = new ReadableStream<Uint8Array>({\n start: (controller) => {\n controllerRef = controller;\n\n const client: NotifClient = {\n id: clientId,\n userId: options?.userId,\n channels: subscribedChannels,\n meta: options?.meta ?? {},\n send: (event, data, opt) => {\n try {\n controller.enqueue(encoder.encode(this.formatMessage(event, data, opt)));\n } catch (err) {\n this.log(`Error enqueueing to client ${clientId}:`, err);\n }\n },\n sendComment: (comment: string) => {\n try {\n controller.enqueue(encoder.encode(`: ${comment}\\n\\n`));\n } catch {}\n },\n close: () => {\n try {\n controller.close();\n } catch {}\n this.removeClient(clientId);\n },\n };\n\n this.clients.set(clientId, client);\n this.log(`Web client connected: ${clientId}`);\n\n // Send connection handshake\n client.send('connected', { clientId });\n },\n cancel: () => {\n this.removeClient(clientId);\n },\n });\n\n return new Response(stream, {\n headers: {\n 'Content-Type': 'text/event-stream',\n 'Cache-Control': 'no-cache, no-transform',\n Connection: 'keep-alive',\n },\n });\n }\n\n /**\n * Stop the server heartbeat and disconnect all clients.\n */\n public close(): void {\n if (this.heartbeatTimer) {\n clearInterval(this.heartbeatTimer);\n this.heartbeatTimer = null;\n }\n\n for (const client of this.clients.values()) {\n client.close();\n }\n this.clients.clear();\n }\n\n private removeClient(clientId: string): void {\n if (this.clients.has(clientId)) {\n this.clients.delete(clientId);\n this.log(`Client disconnected: ${clientId}`);\n }\n }\n\n /**\n * App-given channels are final; the URL can only add public channels when the app gave none.\n * `*` (all public channels) is the default when neither names any.\n */\n private resolveChannels(rawUrl: string | undefined, allowed?: string[]): Set<string> {\n if (allowed) return new Set(allowed);\n\n const channels = new Set<string>();\n if (rawUrl) {\n try {\n const query = new URL(rawUrl, 'http://localhost').searchParams.get('channels') ?? '';\n for (const ch of query.split(',')) {\n const trimmed = ch.trim();\n if (trimmed && !isPrivate(trimmed)) channels.add(trimmed);\n }\n } catch {}\n }\n\n if (channels.size === 0) channels.add('*');\n return channels;\n }\n\n /** `*` never covers private channels; those need an explicit grant (options.channels or subscribeClient). */\n private isSubscribed(client: NotifClient, channel: string): boolean {\n return client.channels.has(channel) || (client.channels.has('*') && !isPrivate(channel));\n }\n\n private formatMessage(event: string, data: any, opt?: { channel?: string; id?: string; sseEvent?: string }): string {\n let message = '';\n if (opt?.id) message += `id: ${opt.id}\\n`;\n message += `event: ${opt?.sseEvent ?? 'message'}\\n`;\n message += `data: ${JSON.stringify({ channel: opt?.channel, event, data })}\\n\\n`;\n return message;\n }\n\n private startHeartbeat(): void {\n // A real event, not an SSE comment: browser EventSource never surfaces comments,\n // so the client's heartbeat timeout would fire on every quiet connection.\n this.heartbeatTimer = setInterval(() => {\n for (const client of this.clients.values()) {\n client.send('ping', {});\n }\n }, this.heartbeatInterval);\n }\n\n private log(...args: any[]): void {\n if (this.debug) {\n console.log('[notif-agent:server]', ...args);\n }\n }\n}\n"]}
package/dist/server.mjs CHANGED
@@ -1,4 +1,6 @@
1
1
  // src/server/NotifServer.ts
2
+ var isPrivate = (ch) => ch.startsWith("private-") || ch.startsWith("presence-");
3
+ var MAX_BUFFERED_BYTES = 1024 * 1024;
2
4
  var NotifServer = class {
3
5
  clients = /* @__PURE__ */ new Map();
4
6
  heartbeatInterval;
@@ -26,7 +28,7 @@ var NotifServer = class {
26
28
  const channels = Array.isArray(channel) ? channel : [channel];
27
29
  for (const ch of channels) {
28
30
  for (const client of this.clients.values()) {
29
- if (client.channels.has(ch) || client.channels.has("*")) {
31
+ if (this.isSubscribed(client, ch)) {
30
32
  client.send(event, data, { channel: ch, id: options?.id, sseEvent: options?.sseEvent });
31
33
  }
32
34
  }
@@ -98,7 +100,7 @@ var NotifServer = class {
98
100
  getSubscribersCount(channel) {
99
101
  let count = 0;
100
102
  for (const client of this.clients.values()) {
101
- if (client.channels.has(channel) || client.channels.has("*")) {
103
+ if (this.isSubscribed(client, channel)) {
102
104
  count++;
103
105
  }
104
106
  }
@@ -119,31 +121,24 @@ var NotifServer = class {
119
121
  "Content-Type": "text/event-stream",
120
122
  "Cache-Control": "no-cache, no-transform",
121
123
  Connection: "keep-alive",
122
- "X-Accel-Buffering": "no",
124
+ "X-Accel-Buffering": "no"
123
125
  // Disable proxy buffering for Nginx
124
- "Access-Control-Allow-Origin": "*"
125
126
  });
126
127
  if (typeof res.flushHeaders === "function") {
127
128
  res.flushHeaders();
128
129
  }
129
130
  const clientId = options?.clientId ?? this.generateClientId();
130
- const subscribedChannels = new Set(options?.channels ?? []);
131
- if (req.url) {
132
- try {
133
- const url = new URL(req.url, "http://localhost");
134
- const queryChannels = url.searchParams.get("channels");
135
- if (queryChannels) {
136
- for (const ch of queryChannels.split(",")) {
137
- const trimmed = ch.trim();
138
- if (trimmed) subscribedChannels.add(trimmed);
139
- }
140
- }
141
- } catch {
131
+ const subscribedChannels = this.resolveChannels(req.url, options?.channels);
132
+ const write = (chunk) => {
133
+ const ok = res.write(chunk);
134
+ if (typeof res.flush === "function") {
135
+ res.flush();
142
136
  }
143
- }
144
- if (subscribedChannels.size === 0 && !options?.channels) {
145
- subscribedChannels.add("*");
146
- }
137
+ if (!ok && res.writableLength > MAX_BUFFERED_BYTES) {
138
+ this.log(`Client ${clientId} too slow, dropping`);
139
+ client.close();
140
+ }
141
+ };
147
142
  const client = {
148
143
  id: clientId,
149
144
  userId: options?.userId,
@@ -151,35 +146,16 @@ var NotifServer = class {
151
146
  meta: options?.meta ?? {},
152
147
  send: (event, data, opt) => {
153
148
  try {
154
- let message = "";
155
- if (opt?.id) message += `id: ${opt.id}
156
- `;
157
- message += `event: ${opt?.sseEvent ?? "message"}
158
- `;
159
- const payload = {
160
- channel: opt?.channel,
161
- event,
162
- data
163
- };
164
- message += `data: ${JSON.stringify(payload)}
165
-
166
- `;
167
- res.write(message);
168
- if (typeof res.flush === "function") {
169
- res.flush();
170
- }
149
+ write(this.formatMessage(event, data, opt));
171
150
  } catch (err) {
172
151
  this.log(`Error writing to client ${clientId}:`, err);
173
152
  }
174
153
  },
175
154
  sendComment: (comment) => {
176
155
  try {
177
- res.write(`: ${comment}
156
+ write(`: ${comment}
178
157
 
179
158
  `);
180
- if (typeof res.flush === "function") {
181
- res.flush();
182
- }
183
159
  } catch {
184
160
  }
185
161
  },
@@ -215,23 +191,7 @@ var NotifServer = class {
215
191
  */
216
192
  createWebResponse(request, options) {
217
193
  const clientId = options?.clientId ?? this.generateClientId();
218
- const subscribedChannels = new Set(options?.channels ?? []);
219
- if (request?.url) {
220
- try {
221
- const url = new URL(request.url);
222
- const queryChannels = url.searchParams.get("channels");
223
- if (queryChannels) {
224
- for (const ch of queryChannels.split(",")) {
225
- const trimmed = ch.trim();
226
- if (trimmed) subscribedChannels.add(trimmed);
227
- }
228
- }
229
- } catch {
230
- }
231
- }
232
- if (subscribedChannels.size === 0 && !options?.channels) {
233
- subscribedChannels.add("*");
234
- }
194
+ const subscribedChannels = this.resolveChannels(request?.url, options?.channels);
235
195
  const encoder = new TextEncoder();
236
196
  const stream = new ReadableStream({
237
197
  start: (controller) => {
@@ -242,20 +202,7 @@ var NotifServer = class {
242
202
  meta: options?.meta ?? {},
243
203
  send: (event, data, opt) => {
244
204
  try {
245
- let message = "";
246
- if (opt?.id) message += `id: ${opt.id}
247
- `;
248
- message += `event: ${opt?.sseEvent ?? "message"}
249
- `;
250
- const payload = {
251
- channel: opt?.channel,
252
- event,
253
- data
254
- };
255
- message += `data: ${JSON.stringify(payload)}
256
-
257
- `;
258
- controller.enqueue(encoder.encode(message));
205
+ controller.enqueue(encoder.encode(this.formatMessage(event, data, opt)));
259
206
  } catch (err) {
260
207
  this.log(`Error enqueueing to client ${clientId}:`, err);
261
208
  }
@@ -288,8 +235,7 @@ var NotifServer = class {
288
235
  headers: {
289
236
  "Content-Type": "text/event-stream",
290
237
  "Cache-Control": "no-cache, no-transform",
291
- Connection: "keep-alive",
292
- "Access-Control-Allow-Origin": "*"
238
+ Connection: "keep-alive"
293
239
  }
294
240
  });
295
241
  }
@@ -312,10 +258,45 @@ var NotifServer = class {
312
258
  this.log(`Client disconnected: ${clientId}`);
313
259
  }
314
260
  }
261
+ /**
262
+ * App-given channels are final; the URL can only add public channels when the app gave none.
263
+ * `*` (all public channels) is the default when neither names any.
264
+ */
265
+ resolveChannels(rawUrl, allowed) {
266
+ if (allowed) return new Set(allowed);
267
+ const channels = /* @__PURE__ */ new Set();
268
+ if (rawUrl) {
269
+ try {
270
+ const query = new URL(rawUrl, "http://localhost").searchParams.get("channels") ?? "";
271
+ for (const ch of query.split(",")) {
272
+ const trimmed = ch.trim();
273
+ if (trimmed && !isPrivate(trimmed)) channels.add(trimmed);
274
+ }
275
+ } catch {
276
+ }
277
+ }
278
+ if (channels.size === 0) channels.add("*");
279
+ return channels;
280
+ }
281
+ /** `*` never covers private channels; those need an explicit grant (options.channels or subscribeClient). */
282
+ isSubscribed(client, channel) {
283
+ return client.channels.has(channel) || client.channels.has("*") && !isPrivate(channel);
284
+ }
285
+ formatMessage(event, data, opt) {
286
+ let message = "";
287
+ if (opt?.id) message += `id: ${opt.id}
288
+ `;
289
+ message += `event: ${opt?.sseEvent ?? "message"}
290
+ `;
291
+ message += `data: ${JSON.stringify({ channel: opt?.channel, event, data })}
292
+
293
+ `;
294
+ return message;
295
+ }
315
296
  startHeartbeat() {
316
297
  this.heartbeatTimer = setInterval(() => {
317
298
  for (const client of this.clients.values()) {
318
- client.sendComment("ping");
299
+ client.send("ping", {});
319
300
  }
320
301
  }, this.heartbeatInterval);
321
302
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/server/NotifServer.ts"],"names":[],"mappings":"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","file":"server.mjs","sourcesContent":["import type { IncomingMessage, ServerResponse } from 'http';\n\nexport interface NotifClient {\n id: string;\n userId?: string;\n channels: Set<string>;\n send: (event: string, data: any, options?: { channel?: string; id?: string; sseEvent?: string }) => void;\n sendComment: (comment: string) => void;\n close: () => void;\n meta: Record<string, any>;\n}\n\nexport interface NotifServerOptions {\n /**\n * Heartbeat interval in milliseconds to keep SSE connections open through proxies.\n * Defaults to 20000ms (20s). Set to 0 to disable.\n */\n heartbeatInterval?: number;\n\n /**\n * Optional custom client ID generator.\n */\n generateClientId?: () => string;\n\n /**\n * Debug logging.\n */\n debug?: boolean;\n}\n\nexport interface TriggerOptions {\n id?: string;\n sseEvent?: string;\n}\n\nexport class NotifServer {\n private clients: Map<string, NotifClient> = new Map();\n private heartbeatInterval: number;\n private heartbeatTimer: ReturnType<typeof setInterval> | null = null;\n private generateClientId: () => string;\n private debug: boolean;\n\n constructor(options: NotifServerOptions = {}) {\n this.heartbeatInterval = options.heartbeatInterval ?? 20000;\n this.debug = options.debug ?? false;\n this.generateClientId =\n options.generateClientId ?? (() => `client_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`);\n\n if (this.heartbeatInterval > 0) {\n this.startHeartbeat();\n }\n }\n\n /**\n * Broadcast an event to one or more channels.\n * Matches Pusher's `pusher.trigger(channel, event, data)` API.\n *\n * Example:\n * ```ts\n * server.trigger('orders', 'order-created', { id: 123, amount: 49.99 });\n * ```\n */\n public trigger(channel: string | string[], event: string, data: any, options?: TriggerOptions): void {\n const channels = Array.isArray(channel) ? channel : [channel];\n\n for (const ch of channels) {\n for (const client of this.clients.values()) {\n if (client.channels.has(ch) || client.channels.has('*')) {\n client.send(event, data, { channel: ch, id: options?.id, sseEvent: options?.sseEvent });\n }\n }\n }\n }\n\n /**\n * Alias for trigger().\n */\n public broadcast(channel: string | string[], event: string, data: any, options?: TriggerOptions): void {\n this.trigger(channel, event, data, options);\n }\n\n /**\n * Send an event directly to a specific client ID.\n */\n public sendToClient(clientId: string, event: string, data: any, options?: TriggerOptions & { channel?: string }): boolean {\n const client = this.clients.get(clientId);\n if (client) {\n client.send(event, data, { channel: options?.channel, id: options?.id, sseEvent: options?.sseEvent });\n return true;\n }\n return false;\n }\n\n /**\n * Send an event to all connected clients belonging to a specific user ID.\n */\n public sendToUser(userId: string, event: string, data: any, options?: TriggerOptions & { channel?: string }): number {\n let count = 0;\n for (const client of this.clients.values()) {\n if (client.userId === userId) {\n client.send(event, data, { channel: options?.channel, id: options?.id, sseEvent: options?.sseEvent });\n count++;\n }\n }\n return count;\n }\n\n /**\n * Subscribe a connected client to a channel.\n */\n public subscribeClient(clientId: string, channel: string): boolean {\n const client = this.clients.get(clientId);\n if (client) {\n client.channels.add(channel);\n this.log(`Client ${clientId} subscribed to \"${channel}\"`);\n return true;\n }\n return false;\n }\n\n /**\n * Unsubscribe a connected client from a channel.\n */\n public unsubscribeClient(clientId: string, channel: string): boolean {\n const client = this.clients.get(clientId);\n if (client) {\n const removed = client.channels.delete(channel);\n this.log(`Client ${clientId} unsubscribed from \"${channel}\"`);\n return removed;\n }\n return false;\n }\n\n /**\n * Get total connected clients count.\n */\n public getClientCount(): number {\n return this.clients.size;\n }\n\n /**\n * Get subscribers count for a specific channel.\n */\n public getSubscribersCount(channel: string): number {\n let count = 0;\n for (const client of this.clients.values()) {\n if (client.channels.has(channel) || client.channels.has('*')) {\n count++;\n }\n }\n return count;\n }\n\n /**\n * Handle incoming HTTP request in Node.js / Express / Fastify environment.\n *\n * Example:\n * ```ts\n * app.get('/sse', (req, res) => {\n * server.handleRequest(req, res);\n * });\n * ```\n */\n public handleRequest(\n req: IncomingMessage,\n res: ServerResponse,\n options?: {\n clientId?: string;\n userId?: string;\n channels?: string[];\n meta?: Record<string, any>;\n }\n ): NotifClient {\n // Set standard SSE HTTP headers\n res.writeHead(200, {\n 'Content-Type': 'text/event-stream',\n 'Cache-Control': 'no-cache, no-transform',\n Connection: 'keep-alive',\n 'X-Accel-Buffering': 'no', // Disable proxy buffering for Nginx\n 'Access-Control-Allow-Origin': '*',\n });\n\n if (typeof (res as any).flushHeaders === 'function') {\n (res as any).flushHeaders();\n }\n\n const clientId = options?.clientId ?? this.generateClientId();\n\n // Parse initial channels from query parameters or options\n const subscribedChannels = new Set<string>(options?.channels ?? []);\n if (req.url) {\n try {\n const url = new URL(req.url, 'http://localhost');\n const queryChannels = url.searchParams.get('channels');\n if (queryChannels) {\n for (const ch of queryChannels.split(',')) {\n const trimmed = ch.trim();\n if (trimmed) subscribedChannels.add(trimmed);\n }\n }\n } catch {}\n }\n\n if (subscribedChannels.size === 0 && !options?.channels) {\n subscribedChannels.add('*');\n }\n\n const client: NotifClient = {\n id: clientId,\n userId: options?.userId,\n channels: subscribedChannels,\n meta: options?.meta ?? {},\n send: (event: string, data: any, opt?: { channel?: string; id?: string; sseEvent?: string }) => {\n try {\n let message = '';\n if (opt?.id) message += `id: ${opt.id}\\n`;\n message += `event: ${opt?.sseEvent ?? 'message'}\\n`;\n\n const payload = {\n channel: opt?.channel,\n event,\n data,\n };\n message += `data: ${JSON.stringify(payload)}\\n\\n`;\n\n res.write(message);\n if (typeof (res as any).flush === 'function') {\n (res as any).flush();\n }\n } catch (err) {\n this.log(`Error writing to client ${clientId}:`, err);\n }\n },\n sendComment: (comment: string) => {\n try {\n res.write(`: ${comment}\\n\\n`);\n if (typeof (res as any).flush === 'function') {\n (res as any).flush();\n }\n } catch {}\n },\n close: () => {\n try {\n res.end();\n } catch {}\n this.removeClient(clientId);\n },\n };\n\n this.clients.set(clientId, client);\n this.log(`Client connected: ${clientId}, channels: [${Array.from(subscribedChannels).join(', ')}]`);\n\n // Send connection established event\n client.send('connected', { clientId });\n\n // Handle connection close\n req.on('close', () => {\n this.removeClient(clientId);\n });\n\n res.on('close', () => {\n this.removeClient(clientId);\n });\n\n return client;\n }\n\n /**\n * Handle Web standard Request (Next.js App Router, Edge runtime, Hono, Cloudflare Workers).\n * Returns a standard `Response` object with a `ReadableStream`.\n *\n * Example:\n * ```ts\n * export function GET(request: Request) {\n * return server.createWebResponse(request);\n * }\n * ```\n */\n public createWebResponse(\n request?: Request,\n options?: {\n clientId?: string;\n userId?: string;\n channels?: string[];\n meta?: Record<string, any>;\n }\n ): Response {\n const clientId = options?.clientId ?? this.generateClientId();\n const subscribedChannels = new Set<string>(options?.channels ?? []);\n\n if (request?.url) {\n try {\n const url = new URL(request.url);\n const queryChannels = url.searchParams.get('channels');\n if (queryChannels) {\n for (const ch of queryChannels.split(',')) {\n const trimmed = ch.trim();\n if (trimmed) subscribedChannels.add(trimmed);\n }\n }\n } catch {}\n }\n\n if (subscribedChannels.size === 0 && !options?.channels) {\n subscribedChannels.add('*');\n }\n\n const encoder = new TextEncoder();\n let controllerRef: ReadableStreamDefaultController<Uint8Array> | null = null;\n\n const stream = new ReadableStream<Uint8Array>({\n start: (controller) => {\n controllerRef = controller;\n\n const client: NotifClient = {\n id: clientId,\n userId: options?.userId,\n channels: subscribedChannels,\n meta: options?.meta ?? {},\n send: (event: string, data: any, opt?: { channel?: string; id?: string; sseEvent?: string }) => {\n try {\n let message = '';\n if (opt?.id) message += `id: ${opt.id}\\n`;\n message += `event: ${opt?.sseEvent ?? 'message'}\\n`;\n\n const payload = {\n channel: opt?.channel,\n event,\n data,\n };\n message += `data: ${JSON.stringify(payload)}\\n\\n`;\n\n controller.enqueue(encoder.encode(message));\n } catch (err) {\n this.log(`Error enqueueing to client ${clientId}:`, err);\n }\n },\n sendComment: (comment: string) => {\n try {\n controller.enqueue(encoder.encode(`: ${comment}\\n\\n`));\n } catch {}\n },\n close: () => {\n try {\n controller.close();\n } catch {}\n this.removeClient(clientId);\n },\n };\n\n this.clients.set(clientId, client);\n this.log(`Web client connected: ${clientId}`);\n\n // Send connection handshake\n client.send('connected', { clientId });\n },\n cancel: () => {\n this.removeClient(clientId);\n },\n });\n\n return new Response(stream, {\n headers: {\n 'Content-Type': 'text/event-stream',\n 'Cache-Control': 'no-cache, no-transform',\n Connection: 'keep-alive',\n 'Access-Control-Allow-Origin': '*',\n },\n });\n }\n\n /**\n * Stop the server heartbeat and disconnect all clients.\n */\n public close(): void {\n if (this.heartbeatTimer) {\n clearInterval(this.heartbeatTimer);\n this.heartbeatTimer = null;\n }\n\n for (const client of this.clients.values()) {\n client.close();\n }\n this.clients.clear();\n }\n\n private removeClient(clientId: string): void {\n if (this.clients.has(clientId)) {\n this.clients.delete(clientId);\n this.log(`Client disconnected: ${clientId}`);\n }\n }\n\n private startHeartbeat(): void {\n this.heartbeatTimer = setInterval(() => {\n for (const client of this.clients.values()) {\n client.sendComment('ping');\n }\n }, this.heartbeatInterval);\n }\n\n private log(...args: any[]): void {\n if (this.debug) {\n console.log('[notif-agent:server]', ...args);\n }\n }\n}\n"]}
1
+ {"version":3,"sources":["../src/server/NotifServer.ts"],"names":[],"mappings":"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","file":"server.mjs","sourcesContent":["import type { IncomingMessage, ServerResponse } from 'http';\n\nexport interface NotifClient {\n id: string;\n userId?: string;\n channels: Set<string>;\n send: (event: string, data: any, options?: { channel?: string; id?: string; sseEvent?: string }) => void;\n sendComment: (comment: string) => void;\n close: () => void;\n meta: Record<string, any>;\n}\n\nexport interface NotifServerOptions {\n /**\n * Heartbeat interval in milliseconds to keep SSE connections open through proxies.\n * Defaults to 20000ms (20s). Set to 0 to disable.\n */\n heartbeatInterval?: number;\n\n /**\n * Optional custom client ID generator.\n */\n generateClientId?: () => string;\n\n /**\n * Debug logging.\n */\n debug?: boolean;\n}\n\nexport interface TriggerOptions {\n id?: string;\n sseEvent?: string;\n}\n\nconst isPrivate = (ch: string) => ch.startsWith('private-') || ch.startsWith('presence-');\n\n// A client whose unread buffer grows past this is dropped instead of buffering forever.\nconst MAX_BUFFERED_BYTES = 1024 * 1024;\n\nexport class NotifServer {\n private clients: Map<string, NotifClient> = new Map();\n private heartbeatInterval: number;\n private heartbeatTimer: ReturnType<typeof setInterval> | null = null;\n private generateClientId: () => string;\n private debug: boolean;\n\n constructor(options: NotifServerOptions = {}) {\n this.heartbeatInterval = options.heartbeatInterval ?? 20000;\n this.debug = options.debug ?? false;\n this.generateClientId =\n options.generateClientId ?? (() => `client_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`);\n\n if (this.heartbeatInterval > 0) {\n this.startHeartbeat();\n }\n }\n\n /**\n * Broadcast an event to one or more channels.\n * Matches Pusher's `pusher.trigger(channel, event, data)` API.\n *\n * Example:\n * ```ts\n * server.trigger('orders', 'order-created', { id: 123, amount: 49.99 });\n * ```\n */\n public trigger(channel: string | string[], event: string, data: any, options?: TriggerOptions): void {\n const channels = Array.isArray(channel) ? channel : [channel];\n\n for (const ch of channels) {\n for (const client of this.clients.values()) {\n if (this.isSubscribed(client, ch)) {\n client.send(event, data, { channel: ch, id: options?.id, sseEvent: options?.sseEvent });\n }\n }\n }\n }\n\n /**\n * Alias for trigger().\n */\n public broadcast(channel: string | string[], event: string, data: any, options?: TriggerOptions): void {\n this.trigger(channel, event, data, options);\n }\n\n /**\n * Send an event directly to a specific client ID.\n */\n public sendToClient(clientId: string, event: string, data: any, options?: TriggerOptions & { channel?: string }): boolean {\n const client = this.clients.get(clientId);\n if (client) {\n client.send(event, data, { channel: options?.channel, id: options?.id, sseEvent: options?.sseEvent });\n return true;\n }\n return false;\n }\n\n /**\n * Send an event to all connected clients belonging to a specific user ID.\n */\n public sendToUser(userId: string, event: string, data: any, options?: TriggerOptions & { channel?: string }): number {\n let count = 0;\n for (const client of this.clients.values()) {\n if (client.userId === userId) {\n client.send(event, data, { channel: options?.channel, id: options?.id, sseEvent: options?.sseEvent });\n count++;\n }\n }\n return count;\n }\n\n /**\n * Subscribe a connected client to a channel.\n */\n public subscribeClient(clientId: string, channel: string): boolean {\n const client = this.clients.get(clientId);\n if (client) {\n client.channels.add(channel);\n this.log(`Client ${clientId} subscribed to \"${channel}\"`);\n return true;\n }\n return false;\n }\n\n /**\n * Unsubscribe a connected client from a channel.\n */\n public unsubscribeClient(clientId: string, channel: string): boolean {\n const client = this.clients.get(clientId);\n if (client) {\n const removed = client.channels.delete(channel);\n this.log(`Client ${clientId} unsubscribed from \"${channel}\"`);\n return removed;\n }\n return false;\n }\n\n /**\n * Get total connected clients count.\n */\n public getClientCount(): number {\n return this.clients.size;\n }\n\n /**\n * Get subscribers count for a specific channel.\n */\n public getSubscribersCount(channel: string): number {\n let count = 0;\n for (const client of this.clients.values()) {\n if (this.isSubscribed(client, channel)) {\n count++;\n }\n }\n return count;\n }\n\n /**\n * Handle incoming HTTP request in Node.js / Express / Fastify environment.\n *\n * Example:\n * ```ts\n * app.get('/sse', (req, res) => {\n * server.handleRequest(req, res);\n * });\n * ```\n */\n public handleRequest(\n req: IncomingMessage,\n res: ServerResponse,\n options?: {\n clientId?: string;\n userId?: string;\n channels?: string[];\n meta?: Record<string, any>;\n }\n ): NotifClient {\n // Set standard SSE HTTP headers\n res.writeHead(200, {\n 'Content-Type': 'text/event-stream',\n 'Cache-Control': 'no-cache, no-transform',\n Connection: 'keep-alive',\n 'X-Accel-Buffering': 'no', // Disable proxy buffering for Nginx\n });\n\n if (typeof (res as any).flushHeaders === 'function') {\n (res as any).flushHeaders();\n }\n\n const clientId = options?.clientId ?? this.generateClientId();\n const subscribedChannels = this.resolveChannels(req.url, options?.channels);\n\n const write = (chunk: string) => {\n const ok = res.write(chunk);\n if (typeof (res as any).flush === 'function') {\n (res as any).flush();\n }\n if (!ok && res.writableLength > MAX_BUFFERED_BYTES) {\n this.log(`Client ${clientId} too slow, dropping`);\n client.close();\n }\n };\n\n const client: NotifClient = {\n id: clientId,\n userId: options?.userId,\n channels: subscribedChannels,\n meta: options?.meta ?? {},\n send: (event, data, opt) => {\n try {\n write(this.formatMessage(event, data, opt));\n } catch (err) {\n this.log(`Error writing to client ${clientId}:`, err);\n }\n },\n sendComment: (comment: string) => {\n try {\n write(`: ${comment}\\n\\n`);\n } catch {}\n },\n close: () => {\n try {\n res.end();\n } catch {}\n this.removeClient(clientId);\n },\n };\n\n this.clients.set(clientId, client);\n this.log(`Client connected: ${clientId}, channels: [${Array.from(subscribedChannels).join(', ')}]`);\n\n // Send connection established event\n client.send('connected', { clientId });\n\n // Handle connection close\n req.on('close', () => {\n this.removeClient(clientId);\n });\n\n res.on('close', () => {\n this.removeClient(clientId);\n });\n\n return client;\n }\n\n /**\n * Handle Web standard Request (Next.js App Router, Edge runtime, Hono, Cloudflare Workers).\n * Returns a standard `Response` object with a `ReadableStream`.\n *\n * Example:\n * ```ts\n * export function GET(request: Request) {\n * return server.createWebResponse(request);\n * }\n * ```\n */\n public createWebResponse(\n request?: Request,\n options?: {\n clientId?: string;\n userId?: string;\n channels?: string[];\n meta?: Record<string, any>;\n }\n ): Response {\n const clientId = options?.clientId ?? this.generateClientId();\n const subscribedChannels = this.resolveChannels(request?.url, options?.channels);\n\n const encoder = new TextEncoder();\n let controllerRef: ReadableStreamDefaultController<Uint8Array> | null = null;\n\n const stream = new ReadableStream<Uint8Array>({\n start: (controller) => {\n controllerRef = controller;\n\n const client: NotifClient = {\n id: clientId,\n userId: options?.userId,\n channels: subscribedChannels,\n meta: options?.meta ?? {},\n send: (event, data, opt) => {\n try {\n controller.enqueue(encoder.encode(this.formatMessage(event, data, opt)));\n } catch (err) {\n this.log(`Error enqueueing to client ${clientId}:`, err);\n }\n },\n sendComment: (comment: string) => {\n try {\n controller.enqueue(encoder.encode(`: ${comment}\\n\\n`));\n } catch {}\n },\n close: () => {\n try {\n controller.close();\n } catch {}\n this.removeClient(clientId);\n },\n };\n\n this.clients.set(clientId, client);\n this.log(`Web client connected: ${clientId}`);\n\n // Send connection handshake\n client.send('connected', { clientId });\n },\n cancel: () => {\n this.removeClient(clientId);\n },\n });\n\n return new Response(stream, {\n headers: {\n 'Content-Type': 'text/event-stream',\n 'Cache-Control': 'no-cache, no-transform',\n Connection: 'keep-alive',\n },\n });\n }\n\n /**\n * Stop the server heartbeat and disconnect all clients.\n */\n public close(): void {\n if (this.heartbeatTimer) {\n clearInterval(this.heartbeatTimer);\n this.heartbeatTimer = null;\n }\n\n for (const client of this.clients.values()) {\n client.close();\n }\n this.clients.clear();\n }\n\n private removeClient(clientId: string): void {\n if (this.clients.has(clientId)) {\n this.clients.delete(clientId);\n this.log(`Client disconnected: ${clientId}`);\n }\n }\n\n /**\n * App-given channels are final; the URL can only add public channels when the app gave none.\n * `*` (all public channels) is the default when neither names any.\n */\n private resolveChannels(rawUrl: string | undefined, allowed?: string[]): Set<string> {\n if (allowed) return new Set(allowed);\n\n const channels = new Set<string>();\n if (rawUrl) {\n try {\n const query = new URL(rawUrl, 'http://localhost').searchParams.get('channels') ?? '';\n for (const ch of query.split(',')) {\n const trimmed = ch.trim();\n if (trimmed && !isPrivate(trimmed)) channels.add(trimmed);\n }\n } catch {}\n }\n\n if (channels.size === 0) channels.add('*');\n return channels;\n }\n\n /** `*` never covers private channels; those need an explicit grant (options.channels or subscribeClient). */\n private isSubscribed(client: NotifClient, channel: string): boolean {\n return client.channels.has(channel) || (client.channels.has('*') && !isPrivate(channel));\n }\n\n private formatMessage(event: string, data: any, opt?: { channel?: string; id?: string; sseEvent?: string }): string {\n let message = '';\n if (opt?.id) message += `id: ${opt.id}\\n`;\n message += `event: ${opt?.sseEvent ?? 'message'}\\n`;\n message += `data: ${JSON.stringify({ channel: opt?.channel, event, data })}\\n\\n`;\n return message;\n }\n\n private startHeartbeat(): void {\n // A real event, not an SSE comment: browser EventSource never surfaces comments,\n // so the client's heartbeat timeout would fire on every quiet connection.\n this.heartbeatTimer = setInterval(() => {\n for (const client of this.clients.values()) {\n client.send('ping', {});\n }\n }, this.heartbeatInterval);\n }\n\n private log(...args: any[]): void {\n if (this.debug) {\n console.log('[notif-agent:server]', ...args);\n }\n }\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "notif-agent",
3
- "version": "1.0.0",
3
+ "version": "2.0.0",
4
4
  "description": "A lightweight Pusher-like real-time notification client and server library powered by Server-Sent Events (SSE)",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",