UNPKG

2.83 kBPlain TextView Raw
1/**
2 * @license
3 * Copyright (c) 2015 The Polymer Project Authors. All rights reserved.
4 * This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
5 * The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
6 * The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
7 * Code distributed by Google as part of the polymer project is also
8 * subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
9 */
10'use strict';
11
12/**
13 * CleanKill hooks the interrupt handler, and provides callbacks for your code
14 * to cleanly shut down before the process exits.
15 */
16
17/**
18 * The type of a cleankill interrupt handler.
19 */
20export type Handler = () => Promise<void>;
21
22const interruptHandlers: Handler[] = [];
23
24/**
25 * Register a handler to occur on SIGINT. The handler must return a promise if
26 * it has any asynchronous work to do. The process will be terminated once
27 * all handlers complete. If a handler throws or the promise it returns rejects
28 * then the process will be terminated immediately.
29 */
30export function onInterrupt(handler: Handler): void {
31 interruptHandlers.push(handler);
32}
33
34/**
35 * Waits for all promises to settle, then rejects with the first error, if any.
36 */
37export async function promiseAllStrict(
38 promises: Promise<any>[]): Promise<void> {
39 let errors = await Promise.all(
40 promises.map((p) => p.then(() => null, (e) => e)));
41 let firstError = errors.find((e) => e != null);
42 if (firstError) {
43 throw firstError;
44 }
45}
46
47/**
48 * Call all interrupt handlers, and call the callback when they all complete.
49 *
50 * Clears the list of interrupt handlers.
51 */
52export async function close(): Promise<void> {
53 const promises = interruptHandlers.map((handler) => handler());
54 // Empty the array in place. Looks weird, totally works.
55 interruptHandlers.length = 0;
56
57 await promiseAllStrict(promises);
58}
59
60let interrupted = false;
61
62/**
63 * Calls all interrupt handlers, then exits with exit code 0.
64 *
65 * If called more than once it skips waiting for the interrupt handlers to
66 * finish and exits with exit code 1. If there are any errors with interrupt
67 * handlers, the process exits immediately with exit code 2.
68 *
69 * This function is called when a SIGINT is received.
70 */
71export function interrupt(): void {
72 if (interrupted) {
73 console.log('\nGot multiple interrupts, exiting immediately!');
74 return process.exit(1);
75 }
76 interrupted = true;
77
78 close().then(() => process.exit(0), (error) => {
79 error = error || 'cleankill interrupt handler failed without a message';
80 console.error(error.stack || error.message || error);
81 process.exit(2);
82 });
83 console.log('\nShutting down. Press ctrl-c again to kill immediately.');
84}
85
86process.on('SIGINT', interrupt);