📦 @simbo/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.
âś… Simple API: gracefulExit(error?, exitCode?)
returns Promise<never>
and exits the process.
đź§ą Teardown support: Run any number of sync/async cleanup steps before exiting.
đź§ Smart exit codes:
exitCode
argument if provided.error
is a non‑zero number, uses that.🗣️ User‑friendly output: Pretty console messages via @simbo/cli-output
helpers.
🙋 Special cases handled:
ExitPromptError
→ prints a "Prompt Cancelled" message.UserFacingError
→ prints message and an optional hint (custom or default).stringifyError()
.Install @simbo/graceful-exit
from the npm registry:
npm i [-D] @simbo/graceful-exit
For a complete API reference, see the documentation.
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
}
await gracefulExit(new Error('Fatal'), 2); // prints error, exits with code 2
await gracefulExit(undefined, 2); // no output, exits with code 2
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.
If any teardown step fails during an otherwise successful exit (code 0
), the
exit code is switched to 1
and the error is logged.
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