保誠-保戶業務員媒合平台
HelenHuang
2022-06-09 9bdb95c9e34cef640534e5e5a1e2225a80442000
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
'use strict';
const os = require('os');
const onExit = require('signal-exit');
 
const DEFAULT_FORCE_KILL_TIMEOUT = 1000 * 5;
 
// Monkey-patches `childProcess.kill()` to add `forceKillAfterTimeout` behavior
const spawnedKill = (kill, signal = 'SIGTERM', options = {}) => {
    const killResult = kill(signal);
    setKillTimeout(kill, signal, options, killResult);
    return killResult;
};
 
const setKillTimeout = (kill, signal, options, killResult) => {
    if (!shouldForceKill(signal, options, killResult)) {
        return;
    }
 
    const timeout = getForceKillAfterTimeout(options);
    const t = setTimeout(() => {
        kill('SIGKILL');
    }, timeout);
 
    // Guarded because there's no `.unref()` when `execa` is used in the renderer
    // process in Electron. This cannot be tested since we don't run tests in
    // Electron.
    // istanbul ignore else
    if (t.unref) {
        t.unref();
    }
};
 
const shouldForceKill = (signal, {forceKillAfterTimeout}, killResult) => {
    return isSigterm(signal) && forceKillAfterTimeout !== false && killResult;
};
 
const isSigterm = signal => {
    return signal === os.constants.signals.SIGTERM ||
        (typeof signal === 'string' && signal.toUpperCase() === 'SIGTERM');
};
 
const getForceKillAfterTimeout = ({forceKillAfterTimeout = true}) => {
    if (forceKillAfterTimeout === true) {
        return DEFAULT_FORCE_KILL_TIMEOUT;
    }
 
    if (!Number.isFinite(forceKillAfterTimeout) || forceKillAfterTimeout < 0) {
        throw new TypeError(`Expected the \`forceKillAfterTimeout\` option to be a non-negative integer, got \`${forceKillAfterTimeout}\` (${typeof forceKillAfterTimeout})`);
    }
 
    return forceKillAfterTimeout;
};
 
// `childProcess.cancel()`
const spawnedCancel = (spawned, context) => {
    const killResult = spawned.kill();
 
    if (killResult) {
        context.isCanceled = true;
    }
};
 
const timeoutKill = (spawned, signal, reject) => {
    spawned.kill(signal);
    reject(Object.assign(new Error('Timed out'), {timedOut: true, signal}));
};
 
// `timeout` option handling
const setupTimeout = (spawned, {timeout, killSignal = 'SIGTERM'}, spawnedPromise) => {
    if (timeout === 0 || timeout === undefined) {
        return spawnedPromise;
    }
 
    let timeoutId;
    const timeoutPromise = new Promise((resolve, reject) => {
        timeoutId = setTimeout(() => {
            timeoutKill(spawned, killSignal, reject);
        }, timeout);
    });
 
    const safeSpawnedPromise = spawnedPromise.finally(() => {
        clearTimeout(timeoutId);
    });
 
    return Promise.race([timeoutPromise, safeSpawnedPromise]);
};
 
const validateTimeout = ({timeout}) => {
    if (timeout !== undefined && (!Number.isFinite(timeout) || timeout < 0)) {
        throw new TypeError(`Expected the \`timeout\` option to be a non-negative integer, got \`${timeout}\` (${typeof timeout})`);
    }
};
 
// `cleanup` option handling
const setExitHandler = async (spawned, {cleanup, detached}, timedPromise) => {
    if (!cleanup || detached) {
        return timedPromise;
    }
 
    const removeExitHandler = onExit(() => {
        spawned.kill();
    });
 
    return timedPromise.finally(() => {
        removeExitHandler();
    });
};
 
module.exports = {
    spawnedKill,
    spawnedCancel,
    setupTimeout,
    validateTimeout,
    setExitHandler
};