UNPKG

@steelbreeze/broker

Version:

Lightweight publish and subscribe using Server-Sent Events for node and express

95 lines (80 loc) 3.58 kB
import * as http from 'http'; const EventSource = require('eventsource'); /** Specifies the location of a message broker server. */ export interface HTTPConfig { /** The host that the message brokers express application is running on. */ host: string; /** The port of the host that the message brokers express application is running on. */ port: number; /** The base URL of the message broker. */ path: string; } export interface ErrorHandler { (err: Error): void; } // A message provide by the server using Server-Sent Events interface ServerMessage { type: string; data: string; lastEventId: number; origin: string; } /** A message provided to a subscriber callback */ export interface ClientMessage { /** The topic name that the message was published on. */ topicName: string; /** The message broker server allocated id for the message; monotonically increments per topic. */ id: number; /** The data published by the publisher. */ data: string; } export interface ClientCallback { (message: ClientMessage): void; } /** * Returns the client API to the message broker, providing publish and subscribe operations. * @param config Configuration specifying the server, port and base URL path of the message broker server. * @returns Returns a client providing publish and subscribe operations. */ export function client(config: HTTPConfig) { return { /** * Publishes a message to a message broker for other client to receive based on their subscriptions. * @param topicName The topic to publish on. This may be one or more URL segments. * @param data The data to publish on the topic * @param onError Optional error handler callback */ publish: (topicName: string, data: string, onError: ErrorHandler | undefined = undefined): void => { var post = http.request({ hostname: config.host, port: config.port, path: `${config.path}/${topicName}`, method: 'POST', headers: { 'Content-Type': 'text/plain', 'Content-Length': Buffer.byteLength(data) } }); // trap and propigate error messages as required post.on('error', (err: Error) => { if (onError) { onError(err); } }); // send message to the server post.write(data); post.end(); }, /** * Registers a subscription to messages on the given topic. The callback is called each time a message to published on the same topic. * @param topicName The topic to subscribe to. This may be one or more URL segments. * @param callback The function to call when data is publised on the topic; passing a message object. */ subscribe: (topicName: string, callback: ClientCallback | undefined): void => { var eventSource = new EventSource(`http://${config.host}:${config.port}${config.path}/${topicName}`); var lastEventId = -1; // process messages from the server eventSource.onmessage = (event: ServerMessage): void => { // perform a duplicate message check if (event.lastEventId !== lastEventId) { lastEventId = event.lastEventId; // propigate message to the client if (callback) { callback({ topicName: topicName, id: event.lastEventId, data: event.data }); } } } } } }