@simbo/graceful-exit
v1.1.0
Published
Gracefully terminate a Node.js process with predictable exit codes, clear console output, and optional teardown steps.
Downloads
18
Maintainers
Readme
Graceful Exit
Gracefully terminate a Node.js process with predictable exit codes, clear console output, and optional teardown steps.
It distinguishes between numeric exit codes, user‑initiated cancellations (e.g.,
Inquirer’s ExitPromptError), user‑facing errors with hints, and unknown
errors.
Features
✅ Simple API:
gracefulExit(error?, exitCode?)returnsPromise<never>and exits the process.🧹 Teardown support: Run any number of sync/async cleanup steps before exiting.
🧭 Smart exit codes:
- Uses the explicit
exitCodeargument if provided. - Else, if
erroris a non‑zero number, uses that. - Else, 1 when exiting due to an error, 0 when clean.
- Uses the explicit
🗣️ User‑friendly output: Pretty console messages via
@simbo/cli-outputhelpers.🙋 Special cases handled:
- Inquirer's
ExitPromptError→ prints a "Prompt Cancelled" message. UserFacingError→ prints message and an optional hint (custom or default).- Unknown values → passed through
stringifyError().
- Inquirer's
Installation
Install @simbo/graceful-exit from the npm registry:
npm i [-D] @simbo/graceful-exitUsage
For a complete API reference, see the documentation.
Basic
import { gracefulExit } from '@simbo/graceful-exit';
try {
// Your code …
if (shouldExitEarly) {
await gracefulExit(); // exits with code 0
}
// … more work
await gracefulExit(); // exits with code 0
} catch (error) {
await gracefulExit(error); // exits with code 1
}With explicit exit code
await gracefulExit(new Error('Fatal'), 2); // prints error, exits with code 2
await gracefulExit(undefined, 2); // no output, exits with code 2With teardown steps
Register cleanup steps that run before the process exits.
import { gracefulExit } from '@simbo/graceful-exit';
import {
onTeardown,
offTeardown,
type TeardownStep,
} from '@simbo/graceful-exit/teardown';
// Teardown may be sync or async:
const closeDatabase: TeardownStep = async () => database.close();
const trackExit: TeardownStep = async (error?: unknown, code?: number) =>
(error || code) && (await metrics.trackExit(error, code));
const sayGoodbye: TeardownStep = () => console.log('Goodbye!');
// Register the teardown steps:
onTeardown(closeDatabase);
onTeardown(trackExit);
onTeardown(sayGoodbye);
// Now steps will be called on any graceful exit.
try {
// …work
// Optionally remove a teardown step:
if (condition) {
offTeardown(trackExit);
}
await gracefulExit();
} catch (error) {
await gracefulExit(error);
}Steps are stored in a Set and executed in registration order.
[!NOTE]
If any teardown step fails during an otherwise successful exit (code0), the exit code is switched to1and the error is logged.
Error type handling
await gracefulExit(2);
// → prints "Exiting. (Error #2)" and exits with 2
await gracefulExit(new Error('Oops'));
// → pretty‑printed via stringifyError
import { UserFacingError } from '@simbo/user-facing-error';
await gracefulExit(
new UserFacingError('Invalid input', 'Use --help for usage'),
);
// → prints message + hint
// Inquirer cancellation (ExitPromptError)
import { ExitPromptError } from '@inquirer/core';
const e = new ExitPromptError();
await gracefulExit(e);
// → prints "Prompt Cancelled" and exits with 1