{"id":22505785,"url":"https://github.com/kanongil/exiting","last_synced_at":"2025-08-10T12:26:56.602Z","repository":{"id":44552264,"uuid":"41672394","full_name":"kanongil/exiting","owner":"kanongil","description":"Safely shutdown http://hapijs.com servers.","archived":false,"fork":false,"pushed_at":"2024-03-15T22:15:53.000Z","size":79,"stargazers_count":26,"open_issues_count":1,"forks_count":4,"subscribers_count":5,"default_branch":"master","last_synced_at":"2025-08-09T05:51:50.572Z","etag":null,"topics":["hapi","javascript","node"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-2-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/kanongil.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2015-08-31T11:44:41.000Z","updated_at":"2025-07-21T16:49:51.000Z","dependencies_parsed_at":"2024-06-18T20:11:03.883Z","dependency_job_id":null,"html_url":"https://github.com/kanongil/exiting","commit_stats":{"total_commits":55,"total_committers":3,"mean_commits":"18.333333333333332","dds":0.07272727272727275,"last_synced_commit":"46073098e49868e5c0459f75bdf42cd4af780c38"},"previous_names":[],"tags_count":13,"template":false,"template_full_name":null,"purl":"pkg:github/kanongil/exiting","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kanongil%2Fexiting","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kanongil%2Fexiting/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kanongil%2Fexiting/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kanongil%2Fexiting/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/kanongil","download_url":"https://codeload.github.com/kanongil/exiting/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kanongil%2Fexiting/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":269704291,"owners_count":24462118,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-08-10T02:00:08.965Z","response_time":71,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["hapi","javascript","node"],"created_at":"2024-12-07T00:33:21.163Z","updated_at":"2025-08-10T12:26:56.548Z","avatar_url":"https://github.com/kanongil.png","language":"JavaScript","readme":"# Exiting\n\nSafely shutdown [hapi.js](http://hapijs.com/) servers whenever the process exits.\n\n[![Node.js CI](https://github.com/kanongil/exiting/actions/workflows/ci.yaml/badge.svg?branch=master)](https://github.com/kanongil/exiting/actions/workflows/ci.yaml)\n\n## Details\n\nWhile it is simple to start and stop a server, ensuring proper shutdown on external, or internal,\ntriggers can be cumbersome to handle properly.\n**exiting** makes this easy by managing your Hapi servers, taking care of starting and stopping \nthem as appropriate.\n\nDepending on the exit trigger, the hapi servers will either be gracefully stopped or aborted (by only\ntriggering `onPreStop` hooks).\nThe exit triggers are handled as detailed:\n\n * Graceful exit with code `0`:\n   * `process.exit()` with exit code `0`.\n   * Unhandled `SIGINT` kill signal, through eg. `ctrl-c`.\n   * Unhandled `SIGTERM` kill signal.\n   * Unhandled `SIGQUIT` kill signal.\n * Aborted exit:\n   * `process.exit()` with non-zero exit code.\n   * Unhandled `SIGHUP` kill signal (code `1`).\n   * Any uncaught exception (code `1`).\n   * Any unhandled rejection (code `1`).\n   * Any closed connection listeners, eg. on worker disconnect (code `255`).\n\nIf shutting down one of the servers is too slow, a timeout will eventually trigger an exit (exit code `255`).\n\nThe shutdown logic is programmed to handle almost any conceivable exit condition, and provides\n100% test coverage.\nThe only instances that `onPreHook` code is not called, are uncatchable signals, like `SIGKILL`,\nand fatal errors that trigger during shutdown.\n\n## Example\n\nBasic server example:\n\n```js\nconst Hapi = require('hapi');\nconst Exiting = require('exiting');\n\nconst server = Hapi.Server();\nconst manager = Exiting.createManager(server);\n\nserver.events.on('stop', () =\u003e {\n\n    console.log('Server stopped.');\n});\n\nconst provision = async () =\u003e {\n\n    server.route({\n        method: 'GET',\n        path: '/',\n        handler: () =\u003e 'Hello' \n    });\n\n    await manager.start();\n\n    console.log('Server started at:', server.info.uri);\n};\n\nprovision();\n```\n\nThe server and process life-cycle will now be managed by **exiting**.\n\nIf you need to delay the shutdown for processing, you can install an extention function on the\n`onPreStop` or `onPostStop` extension points, eg:\n\n```js\nserver.ext('onPreStop', () =\u003e {\n\n    return new Promise((resolve) =\u003e {\n\n        setTimeout(resolve, 1000);\n    });\n});\n```\n\nMultiple servers example:\n\n```js\nconst Hapi = require('hapi');\nconst Exiting = require('exiting');\n\nconst publicServer = Hapi.Server();\nconst adminServer = Hapi.Server();\nconst manager = Exiting.createManager([publicServer, adminServer]);\n\nconst provision = async () =\u003e {\n\n    publicServer.route({\n        method: 'GET',\n        path: '/',\n        handler: () =\u003e 'Hello'\n    });\n\n    adminServer.route({\n        method: 'GET',\n        path: '/',\n        handler: () =\u003e 'Hello Admin'\n    });\n\n    await manager.start();\n\n    console.log('Public server started at:', publicServer.info.uri);\n    console.log('Admin server started at:', adminServer.info.uri);\n};\n\nprovision();\n```\n\n## Installation\n\nInstall using npm: `npm install exiting`.\n\n## Usage\n\nTo enable **exiting** for you server, replace the call to `server.start()` with\n`Exiting.createManager(server).start()`.\n\n### Exiting.createManager(servers, [options])\n\nCreate a new exit manager for one or more hapi.js servers. The `options` object supports:\n\n * `exitTimeout` - When exiting, force process exit after this amount of ms has elapsed. Default: `5000`.\n\n### await manager.start()\n\nStarts the manager and all the managed servers, as if `server.start()` is called on each server.\nIf any server fails to start, all will be stopped with `server.stop()` before the error is re-thrown.\n\nNote that `process.exit()` is monkey patched to intercept such calls.\nStarting also installs the signal handlers and an `uncaughtException` handler.\n\n### await manager.stop([options])\n\nStops the manager and all the servers, as if `server.stop()` is called on each server.\n\n## Notes on process.exit()\n\nThe `process.exit()` method is handled in a special manner that allows the asyncronous stop\nlogic to resolve before actually exiting. Since this can be called from anywhere in the code,\nand subsequent code is never expected to be executed, the manager will throw an\n`Exiting.ProcessExitError` to attempt to escape the current execution context. This allows\nsomething like the following to still exit:\n\n```js\nwhile (true) {\n    process.exit(1);\n}\n```\n\nThis might not always work, and can potentially cause a lock up instead of exiting.\nEg. with this code:\n\n```js\ntry {\n    process.exit(1);\n}\ncatch (err) {\n    /* do nothing */\n}\nwhile (true) {}\n```\n\nYou should avoid using `process.exit()` in your own code, and call `manager.stop()` instead.\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkanongil%2Fexiting","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkanongil%2Fexiting","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkanongil%2Fexiting/lists"}