From 2e7e812f14204db770b05a020cdafb7f3378023d Mon Sep 17 00:00:00 2001 From: Unknown Date: Sun, 28 May 2017 22:27:20 +0200 Subject: [PATCH] feat(*): add the sseHub() middleware and the Hub class --- src/hub.ts | 26 ++++++++++++++++++ src/index.ts | 2 ++ src/sse_hub_middleware.ts | 58 +++++++++++++++++++++++++++++++++++++++ test/index.test.ts | 5 ++++ 4 files changed, 91 insertions(+) create mode 100644 src/hub.ts create mode 100644 src/sse_hub_middleware.ts diff --git a/src/hub.ts b/src/hub.ts new file mode 100644 index 0000000..a63959d --- /dev/null +++ b/src/hub.ts @@ -0,0 +1,26 @@ +import * as fmt from './sse_formatter'; +import { ISseFunctions } from './sse_middleware'; + +export class Hub { + private clients = new Set(); + + public register(funcs: ISseFunctions): void { + this.clients.add(funcs); + } + + public unregister(funcs: ISseFunctions): void { + this.clients.delete(funcs); + } + + public data(data: fmt.SSEValue, id?: string): void { + this.clients.forEach(client => client.data(data, id)); + } + + public event(event: string, data: fmt.SSEValue, id?: string): void { + this.clients.forEach(client => client.event(event, data, id)); + } + + public comment(comment: string): void { + this.clients.forEach(client => client.comment(comment)); + } +} diff --git a/src/index.ts b/src/index.ts index d75f30f..6c63304 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,3 +1,5 @@ +export * from './hub'; export { ISseMiddlewareOptions } from './sse_handler_middleware'; export * from './sse_middleware'; +export * from './sse_hub_middleware'; export { sse as default } from './sse_middleware'; diff --git a/src/sse_hub_middleware.ts b/src/sse_hub_middleware.ts new file mode 100644 index 0000000..ffda605 --- /dev/null +++ b/src/sse_hub_middleware.ts @@ -0,0 +1,58 @@ +import { compose } from 'compose-middleware'; +import { Handler, NextFunction, Request, Response } from 'express'; +import { Hub } from './hub'; +import { ISseMiddlewareOptions } from './sse_handler_middleware'; +import { ISseFunctions, ISseResponse, sse } from './sse_middleware'; + +export interface ISseHubFunctions extends ISseFunctions { + /** + * Holds the broadcasting variants of the normal SSE functions. + */ + broadcast: ISseFunctions; +} + +/** + * An ISseHubResponse is an augmented ISseResponse that contains a `sse.broadcast` property that contains the normal + * SSE functions, except that they will send messages to every client connected to the hub. + * + * Example: + * res.sse.event('myevent', data'); // send to the client that originated the request. + * res.sse.broadcast.event('myevent', 'data'); // send to every client that passed through the middleware. + */ +export interface ISseHubResponse extends Response { + sse: ISseHubFunctions; +} + +export interface ISseHubMiddlewareOptions extends ISseMiddlewareOptions { + hub: Hub; +} + +/** + * SSE middleware that configures an Express response for an SSE session, installs `sse.*` functions on the Response + * object, as well as the `sse.broadcast.*` variants. + * + * @param options An ISseMiddlewareOptions to configure the middleware's behaviour. + */ +export function sseHub(options: Partial = {}): Handler { + const { hub = new Hub() } = options; + + function middleware(req: Request, res: ISseResponse, next: NextFunction): void { + //=> Register the SSE functions of that client on the hub + hub.register(res.sse); + + //=> Unregister the user from the hub when its connection gets closed + res.once('close', () => hub.unregister(res.sse)); + + //=> Make hub's functions available on the response + (res as ISseHubResponse).sse.broadcast = { + data: hub.data.bind(hub), + event: hub.event.bind(hub), + comment: hub.comment.bind(hub), + }; + + //=> Done + next(); + } + + return compose(sse(options), middleware); +} diff --git a/test/index.test.ts b/test/index.test.ts index 009b49b..552e6e0 100644 --- a/test/index.test.ts +++ b/test/index.test.ts @@ -1,5 +1,6 @@ import { expect } from 'chai'; import * as index from '../src/index'; +import * as sseHubMiddleware from '../src/sse_hub_middleware'; import * as sseMiddleware from '../src/sse_middleware'; describe('index', () => { @@ -8,5 +9,9 @@ describe('index', () => { expect(index.sse).to.equal(sseMiddleware.sse); expect(index.default).to.equal(sseMiddleware.sse); }); + + it('exports { sseHub }', () => { + expect(index.sseHub).to.equal(sseHubMiddleware.sseHub); + }); }); });