better-sse
Advanced tools
Comparing version 0.9.0 to 0.10.0
@@ -1,2 +0,2 @@ | ||
!function(e,t){if("object"==typeof exports&&"object"==typeof module)module.exports=t();else if("function"==typeof define&&define.amd)define([],t);else{var s=t();for(var i in s)("object"==typeof exports?exports:e)[i]=s[i]}}(global,(()=>(()=>{"use strict";var e={n:t=>{var s=t&&t.__esModule?()=>t.default:()=>t;return e.d(s,{a:s}),s},d:(t,s)=>{for(var i in s)e.o(s,i)&&!e.o(t,i)&&Object.defineProperty(t,i,{enumerable:!0,get:s[i]})},o:(e,t)=>Object.prototype.hasOwnProperty.call(e,t),r:e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})}},t={};e.r(t),e.d(t,{Channel:()=>f,Session:()=>u,createChannel:()=>v,createSession:()=>p});const s=require("http"),i=require("events");var r=e.n(i);class n extends(r()){addListener(e,t){return super.addListener(e,t)}prependListener(e,t){return super.prependListener(e,t)}prependOnceListener(e,t){return super.prependOnceListener(e,t)}on(e,t){return super.on(e,t)}once(e,t){return super.once(e,t)}emit(e,...t){return super.emit(e,...t)}off(e,t){return super.off(e,t)}removeListener(e,t){return super.removeListener(e,t)}}const o=e=>JSON.stringify(e),a=/(\r\n|\r|\n)/g,h=/\n+$/g,l=e=>{let t=e;return t=t.replace(a,"\n"),t=t.replace(h,""),t},d=require("crypto");let c;c=d.randomUUID?()=>(0,d.randomUUID)():()=>(0,d.randomBytes)(4).toString("hex");class u extends n{constructor(e,t,i={}){var r,n,a,h,d,u,p;super(),this.lastId="",this.isConnected=!1,this.state={},this.buffer="",this.initialize=()=>{var e,t,i;const r=`http://${this.req.headers.host}${this.req.url}`,n=new URL(r).searchParams;if(this.trustClientEventId){const s=null!==(i=null!==(t=null!==(e=this.req.headers["last-event-id"])&&void 0!==e?e:n.get("lastEventId"))&&void 0!==t?t:n.get("evs_last_event_id"))&&void 0!==i?i:"";this.lastId=s}const o={};this.res instanceof s.ServerResponse?(o["Content-Type"]="text/event-stream",o["Cache-Control"]="private, no-cache, no-store, no-transform, must-revalidate, max-age=0",o.Connection="keep-alive",o.Pragma="no-cache",o["X-Accel-Buffering"]="no"):(o["content-type"]="text/event-stream",o["cache-control"]="private, no-cache, no-store, no-transform, must-revalidate, max-age=0",o.pragma="no-cache",o["x-accel-buffering"]="no");for(const[e,t]of Object.entries(this.headers))o[e]=null!=t?t:"";this.res.writeHead(this.statusCode,o),n.has("padding")&&this.comment(" ".repeat(2049)).dispatch(),n.has("evs_preamble")&&this.comment(" ".repeat(2056)).dispatch(),null!==this.initialRetry&&this.retry(this.initialRetry).dispatch(),this.flush(),null!==this.keepAliveInterval&&(this.keepAliveTimer=setInterval(this.keepAlive,this.keepAliveInterval)),this.isConnected=!0,this.emit("connected")},this.onDisconnected=()=>{this.keepAliveTimer&&clearInterval(this.keepAliveTimer),this.isConnected=!1,this.emit("disconnected")},this.writeField=(e,t)=>{const s=this.sanitize(t);return this.buffer+=e+":"+s+"\n",this},this.keepAlive=()=>{this.comment().dispatch().flush()},this.data=e=>{const t=this.serialize(e);return this.writeField("data",t),this},this.id=(e="")=>(this.writeField("id",e),this.lastId=e,this),this.retry=e=>{const t=e.toString();return this.writeField("retry",t),this},this.comment=(e="")=>(this.writeField("",e),this),this.dispatch=()=>(this.buffer+="\n",this),this.flush=()=>(this.res.write(this.buffer),this.buffer="",this),this.push=(e,t="message",s=c())=>(this.event(t).id(s).data(e).dispatch().flush(),this.emit("push",e,t,s),this),this.stream=async(e,t={})=>{const{eventName:s="stream"}=t;return new Promise(((t,i)=>{e.on("data",(e=>{let t;t=Buffer.isBuffer(e)?e.toString():e,this.push(t,s)})),e.once("end",(()=>t(!0))),e.once("close",(()=>t(!0))),e.once("error",(e=>i(e)))}))},this.iterate=async(e,t={})=>{const{eventName:s="iteration"}=t;for await(const t of e)this.push(t,s)},this.req=e,this.res=t,this.serialize=null!==(r=i.serializer)&&void 0!==r?r:o,this.sanitize=null!==(n=i.sanitizer)&&void 0!==n?n:l,this.trustClientEventId=null===(a=i.trustClientEventId)||void 0===a||a,this.initialRetry=null===i.retry?null:null!==(h=i.retry)&&void 0!==h?h:2e3,this.keepAliveInterval=null===i.keepAlive?null:null!==(d=i.keepAlive)&&void 0!==d?d:1e4,this.statusCode=null!==(u=i.statusCode)&&void 0!==u?u:200,this.headers=null!==(p=i.headers)&&void 0!==p?p:{},this.req.once("close",this.onDisconnected),setImmediate(this.initialize)}event(e){return this.writeField("event",e),this}}const p=(...e)=>new Promise((t=>{const s=new u(...e);s.once("connected",(()=>{t(s)}))}));class f extends n{constructor(){super(),this.state={},this.sessions=new Set,this.broadcast=(e,t="message",s={})=>{const i=c();let r;r=s.filter?Array.from(this.sessions).filter(s.filter):this.sessions;for(const s of r)s.push(e,t,i);return this.emit("broadcast",e,t,i),this}}get activeSessions(){return Array.from(this.sessions)}get sessionCount(){return this.sessions.size}register(e){if(this.sessions.has(e))return this;if(!e.isConnected)throw new Error("Cannot register a non-active session.");return e.once("disconnected",(()=>{this.emit("session-disconnected",e),this.deregister(e)})),this.sessions.add(e),this.emit("session-registered",e),this}deregister(e){return this.sessions.has(e)?(this.sessions.delete(e),this.emit("session-deregistered",e),this):this}}const v=(...e)=>new f(...e);return t})())); | ||
!function(e,t){if("object"==typeof exports&&"object"==typeof module)module.exports=t();else if("function"==typeof define&&define.amd)define([],t);else{var s=t();for(var i in s)("object"==typeof exports?exports:e)[i]=s[i]}}(global,(()=>(()=>{"use strict";var e={n:t=>{var s=t&&t.__esModule?()=>t.default:()=>t;return e.d(s,{a:s}),s},d:(t,s)=>{for(var i in s)e.o(s,i)&&!e.o(t,i)&&Object.defineProperty(t,i,{enumerable:!0,get:s[i]})},o:(e,t)=>Object.prototype.hasOwnProperty.call(e,t),r:e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})}},t={};e.r(t),e.d(t,{Channel:()=>b,EventBuffer:()=>d,Session:()=>v,createChannel:()=>g,createEventBuffer:()=>y,createSession:()=>m});const s=require("http"),i=e=>JSON.stringify(e),r=/(\r\n|\r|\n)/g,n=/\n+$/g,h=e=>{let t=e;return t=t.replace(r,"\n"),t=t.replace(n,""),t},o=require("crypto");let a;a=o.randomUUID?()=>(0,o.randomUUID)():()=>(0,o.randomBytes)(4).toString("hex");const l=e=>async(t,s={})=>{const{eventName:i="stream"}=s;return await new Promise(((s,r)=>{t.on("data",(t=>{let s;s=Buffer.isBuffer(t)?t.toString():t,e(s,i)})),t.once("end",(()=>s(!0))),t.once("close",(()=>s(!0))),t.once("error",(e=>r(e)))}))},c=e=>async(t,s={})=>{const{eventName:i="iteration"}=s;for await(const s of t)e(s,i)};class d{constructor(e={}){var t,s;this.buffer="",this.writeField=(e,t)=>{const s=this.sanitize(t);return this.buffer+=e+":"+s+"\n",this},this.data=e=>{const t=this.serialize(e);return this.writeField("data",t),this},this.id=(e="")=>(this.writeField("id",e),this),this.retry=e=>{const t=e.toString();return this.writeField("retry",t),this},this.comment=(e="")=>(this.writeField("",e),this),this.dispatch=()=>(this.buffer+="\n",this),this.push=(e,t="message",s=a())=>(this.event(t).id(s).data(e).dispatch(),this),this.stream=l(this.push),this.iterate=c(this.push),this.clear=()=>(this.buffer="",this),this.read=()=>this.buffer,this.serialize=null!==(t=e.serializer)&&void 0!==t?t:i,this.sanitize=null!==(s=e.sanitizer)&&void 0!==s?s:h}event(e){return this.writeField("event",e),this}}const u=require("events");var f=e.n(u);class p extends(f()){addListener(e,t){return super.addListener(e,t)}prependListener(e,t){return super.prependListener(e,t)}prependOnceListener(e,t){return super.prependOnceListener(e,t)}on(e,t){return super.on(e,t)}once(e,t){return super.once(e,t)}emit(e,...t){return super.emit(e,...t)}off(e,t){return super.off(e,t)}removeListener(e,t){return super.removeListener(e,t)}}class v extends p{constructor(e,t,r={}){var n,o,u,f,p,v,m;super(),this.lastId="",this.isConnected=!1,this.state={},this.initialize=()=>{var e,t,i;const r=`http://${this.req.headers.host}${this.req.url}`,n=new URL(r).searchParams;if(this.trustClientEventId){const s=null!==(i=null!==(t=null!==(e=this.req.headers["last-event-id"])&&void 0!==e?e:n.get("lastEventId"))&&void 0!==t?t:n.get("evs_last_event_id"))&&void 0!==i?i:"";this.lastId=s}const h={};this.res instanceof s.ServerResponse?(h["Content-Type"]="text/event-stream",h["Cache-Control"]="private, no-cache, no-store, no-transform, must-revalidate, max-age=0",h.Connection="keep-alive",h.Pragma="no-cache",h["X-Accel-Buffering"]="no"):(h["content-type"]="text/event-stream",h["cache-control"]="private, no-cache, no-store, no-transform, must-revalidate, max-age=0",h.pragma="no-cache",h["x-accel-buffering"]="no");for(const[e,t]of Object.entries(this.headers))h[e]=null!=t?t:"";this.res.writeHead(this.statusCode,h),n.has("padding")&&this.buffer.comment(" ".repeat(2049)).dispatch(),n.has("evs_preamble")&&this.buffer.comment(" ".repeat(2056)).dispatch(),null!==this.initialRetry&&this.buffer.retry(this.initialRetry).dispatch(),this.flush(),null!==this.keepAliveInterval&&(this.keepAliveTimer=setInterval(this.keepAlive,this.keepAliveInterval)),this.isConnected=!0,this.emit("connected")},this.onDisconnected=()=>{this.keepAliveTimer&&clearInterval(this.keepAliveTimer),this.isConnected=!1,this.emit("disconnected")},this.keepAlive=()=>{this.buffer.comment().dispatch(),this.flush()},this.data=e=>(this.buffer.data(e),this),this.id=(e="")=>(this.buffer.id(e),this.lastId=e,this),this.retry=e=>(this.buffer.retry(e),this),this.comment=e=>(this.buffer.comment(e),this),this.dispatch=()=>(this.buffer.dispatch(),this),this.flush=()=>(this.res.write(this.buffer.read()),this.buffer.clear(),this),this.push=(e,t="message",s=a())=>(this.buffer.push(e,t,s),this.flush(),this.lastId=s,this.emit("push",e,t,s),this),this.stream=l(this.push),this.iterate=c(this.push),this.batch=async e=>{if(e instanceof d)this.res.write(e.read());else{const t=new d({serializer:this.serialize,sanitizer:this.sanitize});await e(t),this.res.write(t.read())}},this.req=e,this.res=t;const b=null!==(n=r.serializer)&&void 0!==n?n:i,g=null!==(o=r.sanitizer)&&void 0!==o?o:h;this.serialize=b,this.sanitize=g,this.buffer=new d({serializer:b,sanitizer:g}),this.trustClientEventId=null===(u=r.trustClientEventId)||void 0===u||u,this.initialRetry=null===r.retry?null:null!==(f=r.retry)&&void 0!==f?f:2e3,this.keepAliveInterval=null===r.keepAlive?null:null!==(p=r.keepAlive)&&void 0!==p?p:1e4,this.statusCode=null!==(v=r.statusCode)&&void 0!==v?v:200,this.headers=null!==(m=r.headers)&&void 0!==m?m:{},this.req.once("close",this.onDisconnected),setImmediate(this.initialize)}event(e){return this.buffer.event(e),this}}const m=(...e)=>new Promise((t=>{const s=new v(...e);s.once("connected",(()=>{t(s)}))}));class b extends p{constructor(){super(),this.state={},this.sessions=new Set,this.broadcast=(e,t="message",s={})=>{const i=a(),r=s.filter?this.activeSessions.filter(s.filter):this.sessions;for(const s of r)s.push(e,t,i);return this.emit("broadcast",e,t,i),this}}get activeSessions(){return Array.from(this.sessions)}get sessionCount(){return this.sessions.size}register(e){if(this.sessions.has(e))return this;if(!e.isConnected)throw new Error("Cannot register a non-active session.");return e.once("disconnected",(()=>{this.emit("session-disconnected",e),this.deregister(e)})),this.sessions.add(e),this.emit("session-registered",e),this}deregister(e){return this.sessions.has(e)?(this.sessions.delete(e),this.emit("session-deregistered",e),this):this}}const g=(...e)=>new b(...e),y=(...e)=>new d(...e);return t})())); | ||
//# sourceMappingURL=index.js.map |
@@ -61,5 +61,12 @@ import { TypedEmitter, EventMap } from "./lib/TypedEmitter"; | ||
/** | ||
* Broadcast an event with the given data and name to every active session registered with this channel. | ||
* Broadcast an event to every active session registered with this channel. | ||
* | ||
* Under the hood this calls the `push` method on every active session. | ||
* | ||
* If no event name is given, the event name is set to `"message"`. | ||
* | ||
* Note that the broadcasted event will have the same ID across all receiving sessions instead of generating a unique ID for each. | ||
* | ||
* @param data - Data to write. | ||
* @param eventName - Event name to write. | ||
*/ | ||
@@ -66,0 +73,0 @@ broadcast: (data: unknown, eventName?: string, options?: BroadcastOptions<SessionState>) => this; |
@@ -5,1 +5,5 @@ export * from "./Session"; | ||
export * from "./createChannel"; | ||
export * from "./EventBuffer"; | ||
export * from "./createEventBuffer"; | ||
export type { StreamOptions } from "./lib/createPushFromStream"; | ||
export type { IterateOptions } from "./lib/createPushFromIterable"; |
@@ -1,2 +0,2 @@ | ||
!function(e,t){if("object"==typeof exports&&"object"==typeof module)module.exports=t();else if("function"==typeof define&&define.amd)define([],t);else{var s=t();for(var i in s)("object"==typeof exports?exports:e)[i]=s[i]}}(global,(()=>(()=>{"use strict";var e={n:t=>{var s=t&&t.__esModule?()=>t.default:()=>t;return e.d(s,{a:s}),s},d:(t,s)=>{for(var i in s)e.o(s,i)&&!e.o(t,i)&&Object.defineProperty(t,i,{enumerable:!0,get:s[i]})},o:(e,t)=>Object.prototype.hasOwnProperty.call(e,t),r:e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})}},t={};e.r(t),e.d(t,{Channel:()=>f,Session:()=>u,createChannel:()=>v,createSession:()=>p});const s=require("http"),i=require("events");var r=e.n(i);class n extends(r()){addListener(e,t){return super.addListener(e,t)}prependListener(e,t){return super.prependListener(e,t)}prependOnceListener(e,t){return super.prependOnceListener(e,t)}on(e,t){return super.on(e,t)}once(e,t){return super.once(e,t)}emit(e,...t){return super.emit(e,...t)}off(e,t){return super.off(e,t)}removeListener(e,t){return super.removeListener(e,t)}}const o=e=>JSON.stringify(e),a=/(\r\n|\r|\n)/g,h=/\n+$/g,l=e=>{let t=e;return t=t.replace(a,"\n"),t=t.replace(h,""),t},d=require("crypto");let c;c=d.randomUUID?()=>(0,d.randomUUID)():()=>(0,d.randomBytes)(4).toString("hex");class u extends n{constructor(e,t,i={}){var r,n,a,h,d,u,p;super(),this.lastId="",this.isConnected=!1,this.state={},this.buffer="",this.initialize=()=>{var e,t,i;const r=`http://${this.req.headers.host}${this.req.url}`,n=new URL(r).searchParams;if(this.trustClientEventId){const s=null!==(i=null!==(t=null!==(e=this.req.headers["last-event-id"])&&void 0!==e?e:n.get("lastEventId"))&&void 0!==t?t:n.get("evs_last_event_id"))&&void 0!==i?i:"";this.lastId=s}const o={};this.res instanceof s.ServerResponse?(o["Content-Type"]="text/event-stream",o["Cache-Control"]="private, no-cache, no-store, no-transform, must-revalidate, max-age=0",o.Connection="keep-alive",o.Pragma="no-cache",o["X-Accel-Buffering"]="no"):(o["content-type"]="text/event-stream",o["cache-control"]="private, no-cache, no-store, no-transform, must-revalidate, max-age=0",o.pragma="no-cache",o["x-accel-buffering"]="no");for(const[e,t]of Object.entries(this.headers))o[e]=null!=t?t:"";this.res.writeHead(this.statusCode,o),n.has("padding")&&this.comment(" ".repeat(2049)).dispatch(),n.has("evs_preamble")&&this.comment(" ".repeat(2056)).dispatch(),null!==this.initialRetry&&this.retry(this.initialRetry).dispatch(),this.flush(),null!==this.keepAliveInterval&&(this.keepAliveTimer=setInterval(this.keepAlive,this.keepAliveInterval)),this.isConnected=!0,this.emit("connected")},this.onDisconnected=()=>{this.keepAliveTimer&&clearInterval(this.keepAliveTimer),this.isConnected=!1,this.emit("disconnected")},this.writeField=(e,t)=>{const s=this.sanitize(t);return this.buffer+=e+":"+s+"\n",this},this.keepAlive=()=>{this.comment().dispatch().flush()},this.data=e=>{const t=this.serialize(e);return this.writeField("data",t),this},this.id=(e="")=>(this.writeField("id",e),this.lastId=e,this),this.retry=e=>{const t=e.toString();return this.writeField("retry",t),this},this.comment=(e="")=>(this.writeField("",e),this),this.dispatch=()=>(this.buffer+="\n",this),this.flush=()=>(this.res.write(this.buffer),this.buffer="",this),this.push=(e,t="message",s=c())=>(this.event(t).id(s).data(e).dispatch().flush(),this.emit("push",e,t,s),this),this.stream=async(e,t={})=>{const{eventName:s="stream"}=t;return new Promise(((t,i)=>{e.on("data",(e=>{let t;t=Buffer.isBuffer(e)?e.toString():e,this.push(t,s)})),e.once("end",(()=>t(!0))),e.once("close",(()=>t(!0))),e.once("error",(e=>i(e)))}))},this.iterate=async(e,t={})=>{const{eventName:s="iteration"}=t;for await(const t of e)this.push(t,s)},this.req=e,this.res=t,this.serialize=null!==(r=i.serializer)&&void 0!==r?r:o,this.sanitize=null!==(n=i.sanitizer)&&void 0!==n?n:l,this.trustClientEventId=null===(a=i.trustClientEventId)||void 0===a||a,this.initialRetry=null===i.retry?null:null!==(h=i.retry)&&void 0!==h?h:2e3,this.keepAliveInterval=null===i.keepAlive?null:null!==(d=i.keepAlive)&&void 0!==d?d:1e4,this.statusCode=null!==(u=i.statusCode)&&void 0!==u?u:200,this.headers=null!==(p=i.headers)&&void 0!==p?p:{},this.req.once("close",this.onDisconnected),setImmediate(this.initialize)}event(e){return this.writeField("event",e),this}}const p=(...e)=>new Promise((t=>{const s=new u(...e);s.once("connected",(()=>{t(s)}))}));class f extends n{constructor(){super(),this.state={},this.sessions=new Set,this.broadcast=(e,t="message",s={})=>{const i=c();let r;r=s.filter?Array.from(this.sessions).filter(s.filter):this.sessions;for(const s of r)s.push(e,t,i);return this.emit("broadcast",e,t,i),this}}get activeSessions(){return Array.from(this.sessions)}get sessionCount(){return this.sessions.size}register(e){if(this.sessions.has(e))return this;if(!e.isConnected)throw new Error("Cannot register a non-active session.");return e.once("disconnected",(()=>{this.emit("session-disconnected",e),this.deregister(e)})),this.sessions.add(e),this.emit("session-registered",e),this}deregister(e){return this.sessions.has(e)?(this.sessions.delete(e),this.emit("session-deregistered",e),this):this}}const v=(...e)=>new f(...e);return t})())); | ||
!function(e,t){if("object"==typeof exports&&"object"==typeof module)module.exports=t();else if("function"==typeof define&&define.amd)define([],t);else{var s=t();for(var i in s)("object"==typeof exports?exports:e)[i]=s[i]}}(global,(()=>(()=>{"use strict";var e={n:t=>{var s=t&&t.__esModule?()=>t.default:()=>t;return e.d(s,{a:s}),s},d:(t,s)=>{for(var i in s)e.o(s,i)&&!e.o(t,i)&&Object.defineProperty(t,i,{enumerable:!0,get:s[i]})},o:(e,t)=>Object.prototype.hasOwnProperty.call(e,t),r:e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})}},t={};e.r(t),e.d(t,{Channel:()=>b,EventBuffer:()=>d,Session:()=>v,createChannel:()=>g,createEventBuffer:()=>y,createSession:()=>m});const s=require("http"),i=e=>JSON.stringify(e),r=/(\r\n|\r|\n)/g,n=/\n+$/g,h=e=>{let t=e;return t=t.replace(r,"\n"),t=t.replace(n,""),t},o=require("crypto");let a;a=o.randomUUID?()=>(0,o.randomUUID)():()=>(0,o.randomBytes)(4).toString("hex");const l=e=>async(t,s={})=>{const{eventName:i="stream"}=s;return await new Promise(((s,r)=>{t.on("data",(t=>{let s;s=Buffer.isBuffer(t)?t.toString():t,e(s,i)})),t.once("end",(()=>s(!0))),t.once("close",(()=>s(!0))),t.once("error",(e=>r(e)))}))},c=e=>async(t,s={})=>{const{eventName:i="iteration"}=s;for await(const s of t)e(s,i)};class d{constructor(e={}){var t,s;this.buffer="",this.writeField=(e,t)=>{const s=this.sanitize(t);return this.buffer+=e+":"+s+"\n",this},this.data=e=>{const t=this.serialize(e);return this.writeField("data",t),this},this.id=(e="")=>(this.writeField("id",e),this),this.retry=e=>{const t=e.toString();return this.writeField("retry",t),this},this.comment=(e="")=>(this.writeField("",e),this),this.dispatch=()=>(this.buffer+="\n",this),this.push=(e,t="message",s=a())=>(this.event(t).id(s).data(e).dispatch(),this),this.stream=l(this.push),this.iterate=c(this.push),this.clear=()=>(this.buffer="",this),this.read=()=>this.buffer,this.serialize=null!==(t=e.serializer)&&void 0!==t?t:i,this.sanitize=null!==(s=e.sanitizer)&&void 0!==s?s:h}event(e){return this.writeField("event",e),this}}const u=require("events");var f=e.n(u);class p extends(f()){addListener(e,t){return super.addListener(e,t)}prependListener(e,t){return super.prependListener(e,t)}prependOnceListener(e,t){return super.prependOnceListener(e,t)}on(e,t){return super.on(e,t)}once(e,t){return super.once(e,t)}emit(e,...t){return super.emit(e,...t)}off(e,t){return super.off(e,t)}removeListener(e,t){return super.removeListener(e,t)}}class v extends p{constructor(e,t,r={}){var n,o,u,f,p,v,m;super(),this.lastId="",this.isConnected=!1,this.state={},this.initialize=()=>{var e,t,i;const r=`http://${this.req.headers.host}${this.req.url}`,n=new URL(r).searchParams;if(this.trustClientEventId){const s=null!==(i=null!==(t=null!==(e=this.req.headers["last-event-id"])&&void 0!==e?e:n.get("lastEventId"))&&void 0!==t?t:n.get("evs_last_event_id"))&&void 0!==i?i:"";this.lastId=s}const h={};this.res instanceof s.ServerResponse?(h["Content-Type"]="text/event-stream",h["Cache-Control"]="private, no-cache, no-store, no-transform, must-revalidate, max-age=0",h.Connection="keep-alive",h.Pragma="no-cache",h["X-Accel-Buffering"]="no"):(h["content-type"]="text/event-stream",h["cache-control"]="private, no-cache, no-store, no-transform, must-revalidate, max-age=0",h.pragma="no-cache",h["x-accel-buffering"]="no");for(const[e,t]of Object.entries(this.headers))h[e]=null!=t?t:"";this.res.writeHead(this.statusCode,h),n.has("padding")&&this.buffer.comment(" ".repeat(2049)).dispatch(),n.has("evs_preamble")&&this.buffer.comment(" ".repeat(2056)).dispatch(),null!==this.initialRetry&&this.buffer.retry(this.initialRetry).dispatch(),this.flush(),null!==this.keepAliveInterval&&(this.keepAliveTimer=setInterval(this.keepAlive,this.keepAliveInterval)),this.isConnected=!0,this.emit("connected")},this.onDisconnected=()=>{this.keepAliveTimer&&clearInterval(this.keepAliveTimer),this.isConnected=!1,this.emit("disconnected")},this.keepAlive=()=>{this.buffer.comment().dispatch(),this.flush()},this.data=e=>(this.buffer.data(e),this),this.id=(e="")=>(this.buffer.id(e),this.lastId=e,this),this.retry=e=>(this.buffer.retry(e),this),this.comment=e=>(this.buffer.comment(e),this),this.dispatch=()=>(this.buffer.dispatch(),this),this.flush=()=>(this.res.write(this.buffer.read()),this.buffer.clear(),this),this.push=(e,t="message",s=a())=>(this.buffer.push(e,t,s),this.flush(),this.lastId=s,this.emit("push",e,t,s),this),this.stream=l(this.push),this.iterate=c(this.push),this.batch=async e=>{if(e instanceof d)this.res.write(e.read());else{const t=new d({serializer:this.serialize,sanitizer:this.sanitize});await e(t),this.res.write(t.read())}},this.req=e,this.res=t;const b=null!==(n=r.serializer)&&void 0!==n?n:i,g=null!==(o=r.sanitizer)&&void 0!==o?o:h;this.serialize=b,this.sanitize=g,this.buffer=new d({serializer:b,sanitizer:g}),this.trustClientEventId=null===(u=r.trustClientEventId)||void 0===u||u,this.initialRetry=null===r.retry?null:null!==(f=r.retry)&&void 0!==f?f:2e3,this.keepAliveInterval=null===r.keepAlive?null:null!==(p=r.keepAlive)&&void 0!==p?p:1e4,this.statusCode=null!==(v=r.statusCode)&&void 0!==v?v:200,this.headers=null!==(m=r.headers)&&void 0!==m?m:{},this.req.once("close",this.onDisconnected),setImmediate(this.initialize)}event(e){return this.buffer.event(e),this}}const m=(...e)=>new Promise((t=>{const s=new v(...e);s.once("connected",(()=>{t(s)}))}));class b extends p{constructor(){super(),this.state={},this.sessions=new Set,this.broadcast=(e,t="message",s={})=>{const i=a(),r=s.filter?this.activeSessions.filter(s.filter):this.sessions;for(const s of r)s.push(e,t,i);return this.emit("broadcast",e,t,i),this}}get activeSessions(){return Array.from(this.sessions)}get sessionCount(){return this.sessions.size}register(e){if(this.sessions.has(e))return this;if(!e.isConnected)throw new Error("Cannot register a non-active session.");return e.once("disconnected",(()=>{this.emit("session-disconnected",e),this.deregister(e)})),this.sessions.add(e),this.emit("session-registered",e),this}deregister(e){return this.sessions.has(e)?(this.sessions.delete(e),this.emit("session-deregistered",e),this):this}}const g=(...e)=>new b(...e),y=(...e)=>new d(...e);return t})())); | ||
//# sourceMappingURL=index.js.map |
@@ -5,2 +5,3 @@ import http from "http"; | ||
import { Session } from "../Session"; | ||
import { EventBuffer } from "../EventBuffer"; | ||
declare const createHttpServer: () => Promise<http.Server>; | ||
@@ -11,2 +12,3 @@ declare const createHttp2Server: () => Promise<http2.Http2Server>; | ||
declare const waitForConnect: (session: Session) => Promise<void>; | ||
export { createHttpServer, createHttp2Server, closeServer, getUrl, waitForConnect, }; | ||
declare const getBuffer: (session: Session) => EventBuffer; | ||
export { createHttpServer, createHttp2Server, closeServer, getUrl, waitForConnect, getBuffer, }; |
/// <reference types="node" /> | ||
/// <reference types="node" /> | ||
import { Readable } from "stream"; | ||
import { IncomingMessage as Http1ServerRequest, ServerResponse as Http1ServerResponse, OutgoingHttpHeaders } from "http"; | ||
import { Http2ServerRequest, Http2ServerResponse } from "http2"; | ||
import { EventBuffer, EventBufferOptions } from "./EventBuffer"; | ||
import { TypedEmitter, EventMap } from "./lib/TypedEmitter"; | ||
import { SerializerFunction } from "./lib/serialize"; | ||
import { SanitizerFunction } from "./lib/sanitize"; | ||
interface SessionOptions { | ||
interface SessionOptions extends Pick<EventBufferOptions, "serializer" | "sanitizer"> { | ||
/** | ||
* Serialize data to a string that can be written. | ||
* | ||
* Note that only values written with `.data()` or `.push()` are serialized, as everything else is assumed to already be a string. | ||
* | ||
* Defaults to `JSON.stringify`. | ||
*/ | ||
serializer?: SerializerFunction; | ||
/** | ||
* Sanitize values so as to not prematurely dispatch events when writing fields whose text inadvertently contains newlines. | ||
* | ||
* By default, CR, LF and CRLF characters are replaced with a single LF character (`\n`) and then any trailing LF characters are stripped so as to prevent a blank line being written and accidentally dispatching the event before `.dispatch()` is called. | ||
*/ | ||
sanitizer?: SanitizerFunction; | ||
/** | ||
* Whether to trust or ignore the last event ID given by the client in the `Last-Event-ID` request header. | ||
@@ -70,18 +54,2 @@ * | ||
} | ||
interface StreamOptions { | ||
/** | ||
* Event name/type to be emitted when stream data is sent to the client. | ||
* | ||
* Defaults to `"stream"`. | ||
*/ | ||
eventName?: string; | ||
} | ||
interface IterateOptions { | ||
/** | ||
* Event name/type to be emitted when iterable data is sent to the client. | ||
* | ||
* Defaults to `"iteration"`. | ||
*/ | ||
eventName?: string; | ||
} | ||
interface DefaultSessionState { | ||
@@ -106,6 +74,2 @@ [key: string]: unknown; | ||
* | ||
* As a performance optimisation, all events and data are first written to an internal buffer | ||
* where it is stored until it is flushed to the client by calling the `flush` method. This is | ||
* done for you when using the `push` helper method. | ||
* | ||
* @param req - The Node HTTP {@link https://nodejs.org/api/http.html#http_class_http_incomingmessage | ServerResponse} object. | ||
@@ -121,3 +85,3 @@ * @param res - The Node HTTP {@link https://nodejs.org/api/http.html#http_class_http_serverresponse | IncomingMessage} object. | ||
* | ||
* For security reasons, keep in mind that the client can provide *any* initial ID here. Use the `trustClientEventId` to ignore the client-given initial ID. | ||
* For security reasons, keep in mind that the client can provide *any* initial ID here. Use the `trustClientEventId` constructor option to ignore the client-given initial ID. | ||
* | ||
@@ -142,7 +106,2 @@ * @readonly | ||
state: State; | ||
/** | ||
* Internal buffer used to store raw data from written fields. | ||
* | ||
* When Session#dispatch is called its buffer data will be flushed. | ||
*/ | ||
private buffer; | ||
@@ -169,59 +128,39 @@ /** | ||
private onDisconnected; | ||
/** | ||
* Write a line with a field key and value appended with a newline character. | ||
*/ | ||
private writeField; | ||
private keepAlive; | ||
/** | ||
* Set the event to the given name (also referred to as the event "type" in the specification). | ||
* | ||
* @param type - Event name/type. | ||
* @deprecated see https://github.com/MatthewWid/better-sse/issues/52 | ||
*/ | ||
event(type: string): this; | ||
/** | ||
* Write an arbitrary data field that is automatically serialized to a string using the given `serializer` function option or JSON stringification by default. | ||
* | ||
* @param data - Data to serialize and write. | ||
* @deprecated see https://github.com/MatthewWid/better-sse/issues/52 | ||
*/ | ||
data: (data: unknown) => this; | ||
/** | ||
* Set the event ID to the given string. | ||
* | ||
* Defaults to an empty string if no argument is given. | ||
* | ||
* @param id - Identification string to write. | ||
* @deprecated see https://github.com/MatthewWid/better-sse/issues/52 | ||
*/ | ||
id: (id?: string) => this; | ||
/** | ||
* Set the suggested reconnection time to the given milliseconds. | ||
* | ||
* @param time - Time in milliseconds to retry. | ||
* @deprecated see https://github.com/MatthewWid/better-sse/issues/52 | ||
*/ | ||
retry: (time: number) => this; | ||
/** | ||
* Write a comment (an ignored field). | ||
* | ||
* This will not fire an event, but is often used to keep the connection alive. | ||
* | ||
* @param text - Text of the comment. Otherwise writes an empty field value. | ||
* @deprecated see https://github.com/MatthewWid/better-sse/issues/52 | ||
*/ | ||
comment: (text?: string) => this; | ||
/** | ||
* Indicate that the event has finished being created by writing an additional newline character. | ||
* | ||
* Note that this does **not** send the written data to the client. Use `flush` to flush the internal buffer. | ||
* @deprecated see https://github.com/MatthewWid/better-sse/issues/52 | ||
*/ | ||
dispatch: () => this; | ||
/** | ||
* Flush the buffered data to the client and clear the buffer. | ||
* Flush the contents of the internal buffer to the client and clear the buffer. | ||
* | ||
* @deprecated see https://github.com/MatthewWid/better-sse/issues/52 | ||
*/ | ||
flush: () => this; | ||
/** | ||
* Create, write, dispatch and flush an event with the given data to the client all at once. | ||
* Push an event to the client. | ||
* | ||
* This is equivalent to calling the methods `event`, `id`, `data`, `dispatch` and `flush` in that order. | ||
* | ||
* If no event name is given, the event name is set to `"message"`. | ||
* | ||
* If no event ID is given, the event ID (and thus the `lastid` property) is set to a unique string generated using a cryptographic pseudorandom number generator. | ||
* If no event ID is given, the event ID (and thus the `lastId` property) is set to a unique string generated using a cryptographic pseudorandom number generator. | ||
* | ||
@@ -236,11 +175,9 @@ * Emits the `push` event with the given data, event name and event ID in that order. | ||
/** | ||
* Pipe readable stream data to the client. | ||
* Pipe readable stream data as a series of events to the client. | ||
* | ||
* Each data emission by the stream pushes a new event to the client. | ||
* | ||
* This uses the `push` method under the hood. | ||
* | ||
* If no event name is given in the options object, the event name is set to `"stream"`. | ||
* If no event name is given in the `options` object, the event name is set to `"stream"`. | ||
* | ||
* @param stream - Readable stream to consume from. | ||
* @param stream - Readable stream to consume data from. | ||
* @param options - Options to alter how the stream is flushed to the client. | ||
@@ -250,19 +187,32 @@ * | ||
*/ | ||
stream: (stream: Readable, options?: StreamOptions) => Promise<boolean>; | ||
stream: (stream: import("stream").Readable, options?: import("./lib/createPushFromStream").StreamOptions) => Promise<boolean>; | ||
/** | ||
* Iterate over an iterable and send yielded values to the client. | ||
* Iterate over an iterable and send yielded values as events to the client. | ||
* | ||
* Each yield pushes a new event to the client. | ||
* | ||
* This uses the `push` method under the hood. | ||
* | ||
* If no event name is given in the options object, the event name is set to `"iteration"`. | ||
* If no event name is given in the `options` object, the event name is set to `"iteration"`. | ||
* | ||
* @param iterable - Iterable to consume data from. | ||
* | ||
* @returns A promise that resolves once all the data has been yielded from the iterable. | ||
* @returns A promise that resolves once all data has been successfully yielded from the iterable. | ||
*/ | ||
iterate: <DataType = unknown>(iterable: Iterable<DataType> | AsyncIterable<DataType>, options?: IterateOptions) => Promise<void>; | ||
iterate: <DataType = unknown>(iterable: Iterable<DataType> | AsyncIterable<DataType>, options?: import("./lib/createPushFromIterable").IterateOptions) => Promise<void>; | ||
/** | ||
* Batch and send multiple events at once. | ||
* | ||
* If given an `EventBuffer` instance, its contents will be sent to the client. | ||
* | ||
* If given a callback, it will be passed an instance of `EventBuffer` which uses the same serializer and sanitizer as the session. | ||
* Once its execution completes - or once it resolves if it returns a promise - the contents of the passed `EventBuffer` will be sent to the client. | ||
* | ||
* @param batcher - Event buffer to get contents from, or callback that takes an event buffer to write to. | ||
* | ||
* @returns A promise that resolves once all data from the event buffer has been successfully sent to the client. | ||
* | ||
* @see EventBuffer | ||
*/ | ||
batch: (batcher: EventBuffer | ((buffer: EventBuffer) => void | Promise<void>)) => Promise<void>; | ||
} | ||
export type { SessionOptions, StreamOptions, IterateOptions, SessionEvents, DefaultSessionState, }; | ||
export type { SessionOptions, SessionEvents, DefaultSessionState }; | ||
export { Session }; |
{ | ||
"name": "better-sse", | ||
"description": "Dead simple, dependency-less, spec-compliant server-side events implementation for Node, written in TypeScript.", | ||
"version": "0.9.0", | ||
"version": "0.10.0", | ||
"main": "./build/index.js", | ||
@@ -6,0 +6,0 @@ "types": "./build/index.d.ts", |
@@ -39,3 +39,3 @@ # Better SSE | ||
* Add or override the response status code and headers. | ||
* Fine-grained control by either sending [individual fields](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#fields) of events or by sending full events with simple helpers. | ||
* Fine-grained control by either sending [individual fields](./docs/api.md#eventbuffer) of events or by sending [full events with simple helpers](./docs/api.md#sessionpush-data-unknown-eventname-string-eventid-string--this). | ||
* Pipe [streams](https://nodejs.org/api/stream.html#stream_readable_streams) and [iterables](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Iterators_and_Generators) directly from the server to the client as a series of events. | ||
@@ -143,7 +143,7 @@ * Support for popular EventStream polyfills [`event-source-polyfill`](https://www.npmjs.com/package/event-source-polyfill) and [`eventsource-polyfill`](https://www.npmjs.com/package/eventsource-polyfill). | ||
This library is always open to contributions, whether it be code, bug reports, documentation or anything else. | ||
This library is always open to contributions whether it be code, bug reports, documentation or anything else. | ||
Please submit suggestions, bugs and issues to the [GitHub issues page](https://github.com/MatthewWid/better-sse/issues). | ||
For code or documentation changes, [submit a pull request on GitHub](https://github.com/MatthewWid/better-sse/pulls). | ||
For code or documentation changes [submit a pull request on GitHub](https://github.com/MatthewWid/better-sse/pulls). | ||
@@ -150,0 +150,0 @@ ## Local Development |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
42851
19
546