@gquittet/graceful-server
Advanced tools
Comparing version 1.0.0 to 1.1.0
@@ -1,1 +0,1 @@ | ||
module.exports=function(e){var t={};function n(o){if(t[o])return t[o].exports;var r=t[o]={i:o,l:!1,exports:{}};return e[o].call(r.exports,r,r.exports,n),r.l=!0,r.exports}return n.m=e,n.c=t,n.d=function(e,t,o){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:o})},n.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var o=Object.create(null);if(n.r(o),Object.defineProperty(o,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var r in e)n.d(o,r,function(t){return e[t]}.bind(null,r));return o},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="",n(n.s=1)}([function(e,t){e.exports=require("events")},function(e,t,n){"use strict";n.r(t);const o={closePromises:[],timeout:1e3,livenessEndpoint:"/live",readinessEndpoint:"/ready"};let r=!0;var s=o;var i=[{type:"SIGHUP",code:1},{type:"SIGBREAK",code:1},{type:"SIGINT",code:2},{type:"SIGTERM",code:15},{type:"uncaughtException",code:2}];var a,u=e=>{for(const t of i)process.on(t.type,async n=>{await e.shutdown(t.type,t.code,n)});return e};!function(e){e.NOT_READY="NOT_READY",e.READY="READY",e.SHUTDOWN="SHUTDOWN",e.SHUTTING_DOWN="SHUTTING_DOWN"}(a||(a={}));var c=a;var d=e=>new Promise(t=>setTimeout(t,e));var l=(e,t)=>async(n,o,r)=>{const{timeout:i,closePromises:a}=s,u=r&&r.message?r:new Error(n);t.status.set(c.SHUTTING_DOWN,u),await d(i),await Promise.all(a.map(e=>e())),await e.stop(),t.status.set(c.SHUTDOWN,u),process.exit(128+o)},f=n(0);var p=e=>(t,n)=>{const{livenessEndpoint:o,readinessEndpoint:r}=s;if(!n.headersSent)return t.url===o&&"GET"===t.method?(n.statusCode=200,n.setHeader("Content-Type","application/json"),n.end(JSON.stringify({uptime:0|process.uptime()}))):t.url===r&&"GET"===t.method?e.isReady()?(n.statusCode=200,n.setHeader("Content-Type","application/json"),n.end(JSON.stringify({status:"ready"}))):(n.statusCode=503,n.end()):void 0};var y=e=>{const t=e||new Set,n=e=>{e.destroy(),t.delete(e)};return{onConnection:e=>{t.add(e),e.once("close",()=>t.delete(e))},closeAll:async()=>t.forEach(n)}};var v=(e,t)=>{const n=y(),o=y();let r=!1;e.on("connection",n.onConnection),e.on("secureConnection",o.onConnection);const s=e.listeners("request");e.removeAllListeners("request"),e.on("request",p(t)),s.forEach(n=>e.on("request",(e,o)=>{t.isReady()?n(e,o):e.socket.destroy()}));return Object.assign(e,{stop:async()=>{if(e.listening&&!r)return r=!0,e.removeAllListeners("request"),e.on("request",(e,t)=>{if(!t.headersSent)return t.setHeader("connection","close")}),await Promise.all([n.closeAll(),o.closeAll()]),new Promise((t,n)=>{e.close(e=>{e?n(e):t()})})}})};var m=e=>{let t=c.NOT_READY;return{set:function(n,o){return t=n,e.emit(n,o),this},get:()=>t,setReady:function(){this.set(c.READY)},isReady:()=>t===c.READY}};var O=e=>{const t=new f.EventEmitter,n=m(t),o=v(e,n);return{status:n,init:function(){return u(this)},shutdown:function(e,t,n){return l(o,this)(e,t,n)},on:(e,n)=>t.on(e,n)}};const E=(e,t)=>{var n;n=t,r&&(Object.freeze(Object.assign(o,n)),r=!1);const s=O(e).init();return{isReady:()=>s.status.isReady(),setReady:()=>s.status.setReady(),on:s.on}};Object.assign(E,c);t.default=E}]).default; | ||
module.exports=function(e){var t={};function n(o){if(t[o])return t[o].exports;var r=t[o]={i:o,l:!1,exports:{}};return e[o].call(r.exports,r,r.exports,n),r.l=!0,r.exports}return n.m=e,n.c=t,n.d=function(e,t,o){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:o})},n.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var o=Object.create(null);if(n.r(o),Object.defineProperty(o,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var r in e)n.d(o,r,function(t){return e[t]}.bind(null,r));return o},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="",n(n.s=1)}([function(e,t){e.exports=require("events")},function(e,t,n){"use strict";n.r(t);const o={closePromises:[],timeout:1e3,livenessEndpoint:"/live",readinessEndpoint:"/ready"};let r=!0;var s=o;var i=[{type:"SIGHUP",code:1},{type:"SIGBREAK",code:1},{type:"SIGINT",code:2},{type:"SIGTERM",code:15},{type:"uncaughtException",code:2}];var a,u=e=>{for(const t of i)process.on(t.type,async n=>{await e.shutdown(t.type,t.code,n)});return e};!function(e){e.NOT_READY="NOT_READY",e.READY="READY",e.SHUTDOWN="SHUTDOWN",e.SHUTTING_DOWN="SHUTTING_DOWN"}(a||(a={}));var c=a;var d=e=>new Promise(t=>setTimeout(t,e));var l=(e,t)=>async(n,o,r)=>{const{timeout:i,closePromises:a}=s,u=r&&r.message?r:new Error(n);t.status.set(c.SHUTTING_DOWN,u),await d(i),await Promise.all(a.map(e=>e())),await e.stop(),t.status.set(c.SHUTDOWN,u),process.exit(128+o)},f=n(0);var p=e=>(t,n)=>{const{livenessEndpoint:o,readinessEndpoint:r}=s;if(!n.headersSent)return t.url===o&&"GET"===t.method?(n.statusCode=200,n.setHeader("Content-Type","application/json"),n.end(JSON.stringify({uptime:0|process.uptime()}))):t.url===r&&"GET"===t.method?e.isReady()?(n.statusCode=200,n.setHeader("Content-Type","application/json"),n.end(JSON.stringify({status:"ready"}))):(n.statusCode=503,n.end()):void 0};var y=e=>{const t=e||new Set,n=e=>{e.destroy(),t.delete(e)};return{onConnection:e=>{t.add(e),e.once("close",()=>t.delete(e))},closeAll:async()=>t.forEach(n)}};var v=(e,t)=>{const n=y(),o=y();let r=!1;e.on("connection",n.onConnection),e.on("secureConnection",o.onConnection);const s=e.listeners("request");e.removeAllListeners("request"),e.on("request",p(t)),s.forEach(n=>e.on("request",(e,o)=>{t.isReady()?n(e,o):e.socket.destroy()}));return Object.assign(e,{stop:async()=>{if(e.listening&&!r)return r=!0,e.removeAllListeners("request"),e.on("request",(e,t)=>{if(!t.headersSent)return t.setHeader("connection","close")}),await Promise.all([n.closeAll(),o.closeAll()]),new Promise((t,n)=>{e.close(e=>{e?n(e):t()})})}})};var m=e=>{let t=c.NOT_READY;return{set:function(n,o){return t=n,e.emit(n,o),this},get:()=>t,setReady:function(){this.set(c.READY)},isReady:()=>t===c.READY}};var O=e=>{const t=new f.EventEmitter,n=m(t),o=v(e,n);return{status:n,init:function(){return u(this)},shutdown:function(e,t,n){return l(o,this)(e,t,n)},on:(e,n)=>t.on(e,n)}};const E=Object.assign((e,t)=>{var n;n=t,r&&(Object.freeze(Object.assign(o,n)),r=!1);const s=O(e).init();return{isReady:()=>s.status.isReady(),setReady:()=>s.status.setReady(),on:s.on}},c);t.default=E}]).default; |
@@ -1,4 +0,5 @@ | ||
import IOptions from '@/interface/options'; | ||
import IGracefulServerOptions from "../interface/gracefulServerOptions"; | ||
import IOptions from "../interface/options"; | ||
declare const options: IOptions; | ||
export declare const makeOptions: (newOptions?: IOptions | undefined) => IOptions; | ||
export declare const makeOptions: (newOptions?: IGracefulServerOptions | undefined) => IOptions; | ||
export default options; |
/// <reference types="node" /> | ||
import ImprovedServer from '@/interface/improvedServer'; | ||
import IStatus from '@/interface/status'; | ||
import ImprovedServer from "../interface/improvedServer"; | ||
import IStatus from "../interface/status"; | ||
import http from 'http'; | ||
declare const improvedServer: (server: http.Server, serverStatus: IStatus) => ImprovedServer; | ||
export default improvedServer; |
/// <reference types="node" /> | ||
import ICore from '@/interface/core'; | ||
import ICore from "../interface/core"; | ||
import http from 'http'; | ||
declare const core: (server: http.Server) => ICore; | ||
export default core; |
/// <reference types="node" /> | ||
import IStatus from '@/interface/status'; | ||
import IStatus from "../interface/status"; | ||
import { EventEmitter } from 'events'; | ||
declare const Status: (eventEmitter: EventEmitter) => IStatus; | ||
export default Status; |
/// <reference types="node" /> | ||
import IGracefulServer from '@/interface/gracefulServer'; | ||
import IOptions from '@/interface/options'; | ||
import State from "./core/state"; | ||
import IGracefulServer from "./interface/gracefulServer"; | ||
import IGracefulServerOptions from "./interface/gracefulServerOptions"; | ||
import http from 'http'; | ||
declare const GracefulServer: (server: http.Server, options: IOptions) => IGracefulServer; | ||
declare const GracefulServer: ((server: http.Server, options?: IGracefulServerOptions | undefined) => IGracefulServer) & typeof State; | ||
export default GracefulServer; |
/// <reference types="node" /> | ||
import { EventEmitter } from 'events'; | ||
import IStatus from './status'; | ||
import IStatus from "./status"; | ||
export default interface ICore { | ||
@@ -5,0 +5,0 @@ status: IStatus; |
@@ -1,2 +0,2 @@ | ||
import State from '@/core/state'; | ||
import State from "../core/state"; | ||
export default interface IStatus { | ||
@@ -3,0 +3,0 @@ set: (status: State, error?: Error) => IStatus; |
@@ -1,3 +0,3 @@ | ||
import ICore from '@/interface/core'; | ||
import ICore from "../interface/core"; | ||
declare const init: (parent: ICore) => ICore; | ||
export default init; |
/// <reference types="node" /> | ||
import IStatus from '@/interface/status'; | ||
import IStatus from "../interface/status"; | ||
import http from 'http'; | ||
declare const onRequest: (serverStatus: IStatus) => (req: http.IncomingMessage, res: http.ServerResponse) => void; | ||
export default onRequest; |
@@ -1,4 +0,4 @@ | ||
import ICore from '@/interface/core'; | ||
import ImprovedServer from '@/interface/improvedServer'; | ||
import ICore from "../interface/core"; | ||
import ImprovedServer from "../interface/improvedServer"; | ||
declare const shutdown: (server: ImprovedServer, parent: ICore) => (type: string, value: number, body?: Error | undefined) => Promise<never>; | ||
export default shutdown; |
@@ -30,3 +30,3 @@ { | ||
"license": "MIT", | ||
"version": "1.0.0", | ||
"version": "1.1.0", | ||
"main": "./lib/index.js", | ||
@@ -36,6 +36,6 @@ "types": "./lib/types/index.d.ts", | ||
"clean": "rimraf lib/", | ||
"type-check": "tsc --noEmit", | ||
"type-check": "ttsc --noEmit", | ||
"type-check:watch": "npm run type-check -- --watch", | ||
"build": "npm run build:types && npm run bundle", | ||
"build:types": "tsc --emitDeclarationOnly", | ||
"build:types": "ttsc --emitDeclarationOnly", | ||
"bundle": "webpack", | ||
@@ -81,3 +81,5 @@ "lint": "eslint src/**/*.ts", | ||
"ts-node": "^8.6.2", | ||
"ttypescript": "^1.5.10", | ||
"typescript": "^3.7.5", | ||
"typescript-transform-paths": "^1.1.14", | ||
"webpack": "^4.41.5", | ||
@@ -84,0 +86,0 @@ "webpack-cli": "^3.3.10" |
102
README.md
@@ -37,6 +37,12 @@ <h1 align="center"> | ||
- [Yarn](#yarn) | ||
- [Endpoint](#endpoint) | ||
- [<code>/live</code>](#live) | ||
- [<code>/ready</code>](#ready) | ||
- [Example](#example) | ||
- [ExpressJS](#expressjs) | ||
- [HTTP Server](#http-server) | ||
- [Options](#options) | ||
- [API](#api) | ||
- [GracefulServer](#gracefulserver) | ||
- [IGracefulServerOptions](#igracefulserveroptions) | ||
- [GracefulServer Instance](#gracefulserver-instance) | ||
- [Integration with Docker](#integration-with-docker) | ||
@@ -60,2 +66,4 @@ - [HEALTH CHECK in Dockerfile](#health-check-in-dockerfile) | ||
✔ It avoid boilerplate codes. | ||
## Requirements | ||
@@ -79,2 +87,32 @@ | ||
## Endpoint | ||
Below you can find the default endpoint but you can setup them. To do that, check out the [Options](#options) part. | ||
<a name="lightship-behaviour-live"></a> | ||
### <code>/live</code> | ||
The endpoint responds: | ||
* `200` status code with the uptime of the server in second. | ||
```json | ||
{ "uptime": 42 } | ||
``` | ||
Used to configure liveness probe. | ||
<a name="lightship-behaviour-ready"></a> | ||
### <code>/ready</code> | ||
The endpoint responds: | ||
* `200` status code if the server is ready. | ||
```json | ||
{ "status": "ready" } | ||
``` | ||
* `503` status code with an empty response if the server is not ready (started, shutting down, etc). | ||
## Example | ||
@@ -86,9 +124,19 @@ | ||
const express = require('express') | ||
const helmet = require('helmet') | ||
const http = require('http') | ||
const GracefulServer = require('@gquittet/graceful-server') | ||
const { connectToDb, closeDbConnection } = require('./db') | ||
const app = express() | ||
app.disable('x-powered-by') | ||
// Exclude the liveness and readiness endpoints from express | ||
app.get('/live', () => {}) | ||
app.get('/ready', () => {}) | ||
// Add below your express middleware | ||
app.use(helmet()) | ||
app.get('/test', (_, res) => { | ||
return res.send({ uptime: process.uptime() | 0 }) | ||
return res.send({ uptime: process.uptime() | 0 }) | ||
}) | ||
@@ -99,2 +147,14 @@ | ||
gracefulServer.on(GracefulServer.READY, () => { | ||
console.log('Server is ready') | ||
}) | ||
gracefulServer.on(GracefulServer.SHUTTING_DOWN, () => { | ||
console.log('Server is shutting down') | ||
}) | ||
gracefulServer.on(GracefulServer.SHUTDOWN, error => { | ||
console.log('Server is down because of', error.message) | ||
}) | ||
server.listen(8080, async () => { | ||
@@ -144,4 +204,16 @@ await connectToDb() | ||
## Options | ||
## API | ||
### GracefulServer | ||
```typescript | ||
((server: http.Server, options?: IGracefulServerOptions | undefined) => IGracefulServer) & typeof State | ||
``` | ||
where `State` is an enum that contains `READY`, `SHUTTING_DOWN` and `SHUTDOWN`. | ||
### IGracefulServerOptions | ||
All of the below options are optional. | ||
| Name | Type | Default | Description | | ||
@@ -154,2 +226,12 @@ | ----------------- | :------------------------: | :-----: | ---------------------------------------------------------------: | | ||
### GracefulServer Instance | ||
```typescript | ||
export default interface IGracefulServer { | ||
isReady: () => Boolean | ||
setReady: () => void | ||
on: (name: string, callback: (...args: any[]) => void) => EventEmitter, | ||
} | ||
``` | ||
## Integration with Docker | ||
@@ -278,3 +360,3 @@ | ||
path: /live | ||
port: 9000 | ||
port: 8080 | ||
failureThreshold: 3 | ||
@@ -293,6 +375,16 @@ initialDelaySeconds: 10 | ||
★ [Bret Fisher](https://github.com/BretFisher) | ||
★ [Stoppable](https://github.com/hunterloftis/stoppable) | ||
★ [http-terminator](https://github.com/gajus/http-terminator) | ||
★ [Bret Fisher](https://github.com/BretFisher) for its great articles and videos | ||
★ [IBM documentation](https://cloud.ibm.com/docs/node?topic=nodejs-node-healthcheck) | ||
★ [Node HTTP documentation](https://nodejs.org/api/http.html) | ||
★ [Cloud Health](https://github.com/CloudNativeJS/cloud-health) | ||
★ [Cloud Health Connect](https://github.com/CloudNativeJS/cloud-health-connect) | ||
## Donate | ||
@@ -299,0 +391,0 @@ |
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
21891
22
106
386
37