taskchef 3.0.1 → 3.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.codex-plugin/plugin.json +1 -1
- package/BACKLOG.md +15 -0
- package/README.md +28 -7
- package/SPEC.md +79 -33
- package/index.js +18 -0
- package/node_modules/graceful-fs/LICENSE +15 -0
- package/node_modules/graceful-fs/README.md +143 -0
- package/node_modules/graceful-fs/clone.js +23 -0
- package/node_modules/graceful-fs/graceful-fs.js +448 -0
- package/node_modules/graceful-fs/legacy-streams.js +118 -0
- package/node_modules/graceful-fs/package.json +53 -0
- package/node_modules/graceful-fs/polyfills.js +355 -0
- package/node_modules/proper-lockfile/CHANGELOG.md +108 -0
- package/node_modules/proper-lockfile/LICENSE +21 -0
- package/node_modules/proper-lockfile/README.md +183 -0
- package/node_modules/proper-lockfile/index.js +40 -0
- package/node_modules/proper-lockfile/lib/adapter.js +85 -0
- package/node_modules/proper-lockfile/lib/lockfile.js +342 -0
- package/node_modules/proper-lockfile/lib/mtime-precision.js +55 -0
- package/node_modules/proper-lockfile/package.json +71 -0
- package/node_modules/retry/.npmignore +3 -0
- package/node_modules/retry/.travis.yml +15 -0
- package/node_modules/retry/License +21 -0
- package/node_modules/retry/Makefile +18 -0
- package/node_modules/retry/README.md +227 -0
- package/node_modules/retry/equation.gif +0 -0
- package/node_modules/retry/example/dns.js +31 -0
- package/node_modules/retry/example/stop.js +40 -0
- package/node_modules/retry/index.js +1 -0
- package/node_modules/retry/lib/retry.js +100 -0
- package/node_modules/retry/lib/retry_operation.js +158 -0
- package/node_modules/retry/package.json +32 -0
- package/node_modules/retry/test/common.js +10 -0
- package/node_modules/retry/test/integration/test-forever.js +24 -0
- package/node_modules/retry/test/integration/test-retry-operation.js +258 -0
- package/node_modules/retry/test/integration/test-retry-wrap.js +101 -0
- package/node_modules/retry/test/integration/test-timeouts.js +69 -0
- package/node_modules/signal-exit/LICENSE.txt +16 -0
- package/node_modules/signal-exit/README.md +39 -0
- package/node_modules/signal-exit/index.js +202 -0
- package/node_modules/signal-exit/package.json +38 -0
- package/node_modules/signal-exit/signals.js +53 -0
- package/package.json +5 -2
- package/skills/taskchef-bootstrap/SKILL.md +2 -3
- package/skills/taskchef-delegate/SKILL.md +75 -11
- package/skills/taskchef-report/SKILL.md +20 -10
- package/src/cli.js +19 -1
- package/src/delegation.js +555 -0
- package/src/workspace.js +53 -172
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
var common = require('../common');
|
|
2
|
+
var assert = common.assert;
|
|
3
|
+
var fake = common.fake.create();
|
|
4
|
+
var retry = require(common.dir.lib + '/retry');
|
|
5
|
+
|
|
6
|
+
(function testReset() {
|
|
7
|
+
var error = new Error('some error');
|
|
8
|
+
var operation = retry.operation([1, 2, 3]);
|
|
9
|
+
var attempts = 0;
|
|
10
|
+
|
|
11
|
+
var finalCallback = fake.callback('finalCallback');
|
|
12
|
+
fake.expectAnytime(finalCallback);
|
|
13
|
+
|
|
14
|
+
var expectedFinishes = 1;
|
|
15
|
+
var finishes = 0;
|
|
16
|
+
|
|
17
|
+
var fn = function() {
|
|
18
|
+
operation.attempt(function(currentAttempt) {
|
|
19
|
+
attempts++;
|
|
20
|
+
assert.equal(currentAttempt, attempts);
|
|
21
|
+
if (operation.retry(error)) {
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
finishes++
|
|
26
|
+
assert.equal(expectedFinishes, finishes);
|
|
27
|
+
assert.strictEqual(attempts, 4);
|
|
28
|
+
assert.strictEqual(operation.attempts(), attempts);
|
|
29
|
+
assert.strictEqual(operation.mainError(), error);
|
|
30
|
+
|
|
31
|
+
if (finishes < 2) {
|
|
32
|
+
attempts = 0;
|
|
33
|
+
expectedFinishes++;
|
|
34
|
+
operation.reset();
|
|
35
|
+
fn()
|
|
36
|
+
} else {
|
|
37
|
+
finalCallback();
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
fn();
|
|
43
|
+
})();
|
|
44
|
+
|
|
45
|
+
(function testErrors() {
|
|
46
|
+
var operation = retry.operation();
|
|
47
|
+
|
|
48
|
+
var error = new Error('some error');
|
|
49
|
+
var error2 = new Error('some other error');
|
|
50
|
+
operation._errors.push(error);
|
|
51
|
+
operation._errors.push(error2);
|
|
52
|
+
|
|
53
|
+
assert.deepEqual(operation.errors(), [error, error2]);
|
|
54
|
+
})();
|
|
55
|
+
|
|
56
|
+
(function testMainErrorReturnsMostFrequentError() {
|
|
57
|
+
var operation = retry.operation();
|
|
58
|
+
var error = new Error('some error');
|
|
59
|
+
var error2 = new Error('some other error');
|
|
60
|
+
|
|
61
|
+
operation._errors.push(error);
|
|
62
|
+
operation._errors.push(error2);
|
|
63
|
+
operation._errors.push(error);
|
|
64
|
+
|
|
65
|
+
assert.strictEqual(operation.mainError(), error);
|
|
66
|
+
})();
|
|
67
|
+
|
|
68
|
+
(function testMainErrorReturnsLastErrorOnEqualCount() {
|
|
69
|
+
var operation = retry.operation();
|
|
70
|
+
var error = new Error('some error');
|
|
71
|
+
var error2 = new Error('some other error');
|
|
72
|
+
|
|
73
|
+
operation._errors.push(error);
|
|
74
|
+
operation._errors.push(error2);
|
|
75
|
+
|
|
76
|
+
assert.strictEqual(operation.mainError(), error2);
|
|
77
|
+
})();
|
|
78
|
+
|
|
79
|
+
(function testAttempt() {
|
|
80
|
+
var operation = retry.operation();
|
|
81
|
+
var fn = new Function();
|
|
82
|
+
|
|
83
|
+
var timeoutOpts = {
|
|
84
|
+
timeout: 1,
|
|
85
|
+
cb: function() {}
|
|
86
|
+
};
|
|
87
|
+
operation.attempt(fn, timeoutOpts);
|
|
88
|
+
|
|
89
|
+
assert.strictEqual(fn, operation._fn);
|
|
90
|
+
assert.strictEqual(timeoutOpts.timeout, operation._operationTimeout);
|
|
91
|
+
assert.strictEqual(timeoutOpts.cb, operation._operationTimeoutCb);
|
|
92
|
+
})();
|
|
93
|
+
|
|
94
|
+
(function testRetry() {
|
|
95
|
+
var error = new Error('some error');
|
|
96
|
+
var operation = retry.operation([1, 2, 3]);
|
|
97
|
+
var attempts = 0;
|
|
98
|
+
|
|
99
|
+
var finalCallback = fake.callback('finalCallback');
|
|
100
|
+
fake.expectAnytime(finalCallback);
|
|
101
|
+
|
|
102
|
+
var fn = function() {
|
|
103
|
+
operation.attempt(function(currentAttempt) {
|
|
104
|
+
attempts++;
|
|
105
|
+
assert.equal(currentAttempt, attempts);
|
|
106
|
+
if (operation.retry(error)) {
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
assert.strictEqual(attempts, 4);
|
|
111
|
+
assert.strictEqual(operation.attempts(), attempts);
|
|
112
|
+
assert.strictEqual(operation.mainError(), error);
|
|
113
|
+
finalCallback();
|
|
114
|
+
});
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
fn();
|
|
118
|
+
})();
|
|
119
|
+
|
|
120
|
+
(function testRetryForever() {
|
|
121
|
+
var error = new Error('some error');
|
|
122
|
+
var operation = retry.operation({ retries: 3, forever: true });
|
|
123
|
+
var attempts = 0;
|
|
124
|
+
|
|
125
|
+
var finalCallback = fake.callback('finalCallback');
|
|
126
|
+
fake.expectAnytime(finalCallback);
|
|
127
|
+
|
|
128
|
+
var fn = function() {
|
|
129
|
+
operation.attempt(function(currentAttempt) {
|
|
130
|
+
attempts++;
|
|
131
|
+
assert.equal(currentAttempt, attempts);
|
|
132
|
+
if (attempts !== 6 && operation.retry(error)) {
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
assert.strictEqual(attempts, 6);
|
|
137
|
+
assert.strictEqual(operation.attempts(), attempts);
|
|
138
|
+
assert.strictEqual(operation.mainError(), error);
|
|
139
|
+
finalCallback();
|
|
140
|
+
});
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
fn();
|
|
144
|
+
})();
|
|
145
|
+
|
|
146
|
+
(function testRetryForeverNoRetries() {
|
|
147
|
+
var error = new Error('some error');
|
|
148
|
+
var delay = 50
|
|
149
|
+
var operation = retry.operation({
|
|
150
|
+
retries: null,
|
|
151
|
+
forever: true,
|
|
152
|
+
minTimeout: delay,
|
|
153
|
+
maxTimeout: delay
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
var attempts = 0;
|
|
157
|
+
var startTime = new Date().getTime();
|
|
158
|
+
|
|
159
|
+
var finalCallback = fake.callback('finalCallback');
|
|
160
|
+
fake.expectAnytime(finalCallback);
|
|
161
|
+
|
|
162
|
+
var fn = function() {
|
|
163
|
+
operation.attempt(function(currentAttempt) {
|
|
164
|
+
attempts++;
|
|
165
|
+
assert.equal(currentAttempt, attempts);
|
|
166
|
+
if (attempts !== 4 && operation.retry(error)) {
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
var endTime = new Date().getTime();
|
|
171
|
+
var minTime = startTime + (delay * 3);
|
|
172
|
+
var maxTime = minTime + 20 // add a little headroom for code execution time
|
|
173
|
+
assert(endTime >= minTime)
|
|
174
|
+
assert(endTime < maxTime)
|
|
175
|
+
assert.strictEqual(attempts, 4);
|
|
176
|
+
assert.strictEqual(operation.attempts(), attempts);
|
|
177
|
+
assert.strictEqual(operation.mainError(), error);
|
|
178
|
+
finalCallback();
|
|
179
|
+
});
|
|
180
|
+
};
|
|
181
|
+
|
|
182
|
+
fn();
|
|
183
|
+
})();
|
|
184
|
+
|
|
185
|
+
(function testStop() {
|
|
186
|
+
var error = new Error('some error');
|
|
187
|
+
var operation = retry.operation([1, 2, 3]);
|
|
188
|
+
var attempts = 0;
|
|
189
|
+
|
|
190
|
+
var finalCallback = fake.callback('finalCallback');
|
|
191
|
+
fake.expectAnytime(finalCallback);
|
|
192
|
+
|
|
193
|
+
var fn = function() {
|
|
194
|
+
operation.attempt(function(currentAttempt) {
|
|
195
|
+
attempts++;
|
|
196
|
+
assert.equal(currentAttempt, attempts);
|
|
197
|
+
|
|
198
|
+
if (attempts === 2) {
|
|
199
|
+
operation.stop();
|
|
200
|
+
|
|
201
|
+
assert.strictEqual(attempts, 2);
|
|
202
|
+
assert.strictEqual(operation.attempts(), attempts);
|
|
203
|
+
assert.strictEqual(operation.mainError(), error);
|
|
204
|
+
finalCallback();
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
if (operation.retry(error)) {
|
|
208
|
+
return;
|
|
209
|
+
}
|
|
210
|
+
});
|
|
211
|
+
};
|
|
212
|
+
|
|
213
|
+
fn();
|
|
214
|
+
})();
|
|
215
|
+
|
|
216
|
+
(function testMaxRetryTime() {
|
|
217
|
+
var error = new Error('some error');
|
|
218
|
+
var maxRetryTime = 30;
|
|
219
|
+
var operation = retry.operation({
|
|
220
|
+
minTimeout: 1,
|
|
221
|
+
maxRetryTime: maxRetryTime
|
|
222
|
+
});
|
|
223
|
+
var attempts = 0;
|
|
224
|
+
|
|
225
|
+
var finalCallback = fake.callback('finalCallback');
|
|
226
|
+
fake.expectAnytime(finalCallback);
|
|
227
|
+
|
|
228
|
+
var longAsyncFunction = function (wait, callback){
|
|
229
|
+
setTimeout(callback, wait);
|
|
230
|
+
};
|
|
231
|
+
|
|
232
|
+
var fn = function() {
|
|
233
|
+
var startTime = new Date().getTime();
|
|
234
|
+
operation.attempt(function(currentAttempt) {
|
|
235
|
+
attempts++;
|
|
236
|
+
assert.equal(currentAttempt, attempts);
|
|
237
|
+
|
|
238
|
+
if (attempts !== 2) {
|
|
239
|
+
if (operation.retry(error)) {
|
|
240
|
+
return;
|
|
241
|
+
}
|
|
242
|
+
} else {
|
|
243
|
+
var curTime = new Date().getTime();
|
|
244
|
+
longAsyncFunction(maxRetryTime - (curTime - startTime - 1), function(){
|
|
245
|
+
if (operation.retry(error)) {
|
|
246
|
+
assert.fail('timeout should be occurred');
|
|
247
|
+
return;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
assert.strictEqual(operation.mainError(), error);
|
|
251
|
+
finalCallback();
|
|
252
|
+
});
|
|
253
|
+
}
|
|
254
|
+
});
|
|
255
|
+
};
|
|
256
|
+
|
|
257
|
+
fn();
|
|
258
|
+
})();
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
var common = require('../common');
|
|
2
|
+
var assert = common.assert;
|
|
3
|
+
var fake = common.fake.create();
|
|
4
|
+
var retry = require(common.dir.lib + '/retry');
|
|
5
|
+
|
|
6
|
+
function getLib() {
|
|
7
|
+
return {
|
|
8
|
+
fn1: function() {},
|
|
9
|
+
fn2: function() {},
|
|
10
|
+
fn3: function() {}
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
(function wrapAll() {
|
|
15
|
+
var lib = getLib();
|
|
16
|
+
retry.wrap(lib);
|
|
17
|
+
assert.equal(lib.fn1.name, 'bound retryWrapper');
|
|
18
|
+
assert.equal(lib.fn2.name, 'bound retryWrapper');
|
|
19
|
+
assert.equal(lib.fn3.name, 'bound retryWrapper');
|
|
20
|
+
}());
|
|
21
|
+
|
|
22
|
+
(function wrapAllPassOptions() {
|
|
23
|
+
var lib = getLib();
|
|
24
|
+
retry.wrap(lib, {retries: 2});
|
|
25
|
+
assert.equal(lib.fn1.name, 'bound retryWrapper');
|
|
26
|
+
assert.equal(lib.fn2.name, 'bound retryWrapper');
|
|
27
|
+
assert.equal(lib.fn3.name, 'bound retryWrapper');
|
|
28
|
+
assert.equal(lib.fn1.options.retries, 2);
|
|
29
|
+
assert.equal(lib.fn2.options.retries, 2);
|
|
30
|
+
assert.equal(lib.fn3.options.retries, 2);
|
|
31
|
+
}());
|
|
32
|
+
|
|
33
|
+
(function wrapDefined() {
|
|
34
|
+
var lib = getLib();
|
|
35
|
+
retry.wrap(lib, ['fn2', 'fn3']);
|
|
36
|
+
assert.notEqual(lib.fn1.name, 'bound retryWrapper');
|
|
37
|
+
assert.equal(lib.fn2.name, 'bound retryWrapper');
|
|
38
|
+
assert.equal(lib.fn3.name, 'bound retryWrapper');
|
|
39
|
+
}());
|
|
40
|
+
|
|
41
|
+
(function wrapDefinedAndPassOptions() {
|
|
42
|
+
var lib = getLib();
|
|
43
|
+
retry.wrap(lib, {retries: 2}, ['fn2', 'fn3']);
|
|
44
|
+
assert.notEqual(lib.fn1.name, 'bound retryWrapper');
|
|
45
|
+
assert.equal(lib.fn2.name, 'bound retryWrapper');
|
|
46
|
+
assert.equal(lib.fn3.name, 'bound retryWrapper');
|
|
47
|
+
assert.equal(lib.fn2.options.retries, 2);
|
|
48
|
+
assert.equal(lib.fn3.options.retries, 2);
|
|
49
|
+
}());
|
|
50
|
+
|
|
51
|
+
(function runWrappedWithoutError() {
|
|
52
|
+
var callbackCalled;
|
|
53
|
+
var lib = {method: function(a, b, callback) {
|
|
54
|
+
assert.equal(a, 1);
|
|
55
|
+
assert.equal(b, 2);
|
|
56
|
+
assert.equal(typeof callback, 'function');
|
|
57
|
+
callback();
|
|
58
|
+
}};
|
|
59
|
+
retry.wrap(lib);
|
|
60
|
+
lib.method(1, 2, function() {
|
|
61
|
+
callbackCalled = true;
|
|
62
|
+
});
|
|
63
|
+
assert.ok(callbackCalled);
|
|
64
|
+
}());
|
|
65
|
+
|
|
66
|
+
(function runWrappedSeveralWithoutError() {
|
|
67
|
+
var callbacksCalled = 0;
|
|
68
|
+
var lib = {
|
|
69
|
+
fn1: function (a, callback) {
|
|
70
|
+
assert.equal(a, 1);
|
|
71
|
+
assert.equal(typeof callback, 'function');
|
|
72
|
+
callback();
|
|
73
|
+
},
|
|
74
|
+
fn2: function (a, callback) {
|
|
75
|
+
assert.equal(a, 2);
|
|
76
|
+
assert.equal(typeof callback, 'function');
|
|
77
|
+
callback();
|
|
78
|
+
}
|
|
79
|
+
};
|
|
80
|
+
retry.wrap(lib, {}, ['fn1', 'fn2']);
|
|
81
|
+
lib.fn1(1, function() {
|
|
82
|
+
callbacksCalled++;
|
|
83
|
+
});
|
|
84
|
+
lib.fn2(2, function() {
|
|
85
|
+
callbacksCalled++;
|
|
86
|
+
});
|
|
87
|
+
assert.equal(callbacksCalled, 2);
|
|
88
|
+
}());
|
|
89
|
+
|
|
90
|
+
(function runWrappedWithError() {
|
|
91
|
+
var callbackCalled;
|
|
92
|
+
var lib = {method: function(callback) {
|
|
93
|
+
callback(new Error('Some error'));
|
|
94
|
+
}};
|
|
95
|
+
retry.wrap(lib, {retries: 1});
|
|
96
|
+
lib.method(function(err) {
|
|
97
|
+
callbackCalled = true;
|
|
98
|
+
assert.ok(err instanceof Error);
|
|
99
|
+
});
|
|
100
|
+
assert.ok(!callbackCalled);
|
|
101
|
+
}());
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
var common = require('../common');
|
|
2
|
+
var assert = common.assert;
|
|
3
|
+
var retry = require(common.dir.lib + '/retry');
|
|
4
|
+
|
|
5
|
+
(function testDefaultValues() {
|
|
6
|
+
var timeouts = retry.timeouts();
|
|
7
|
+
|
|
8
|
+
assert.equal(timeouts.length, 10);
|
|
9
|
+
assert.equal(timeouts[0], 1000);
|
|
10
|
+
assert.equal(timeouts[1], 2000);
|
|
11
|
+
assert.equal(timeouts[2], 4000);
|
|
12
|
+
})();
|
|
13
|
+
|
|
14
|
+
(function testDefaultValuesWithRandomize() {
|
|
15
|
+
var minTimeout = 5000;
|
|
16
|
+
var timeouts = retry.timeouts({
|
|
17
|
+
minTimeout: minTimeout,
|
|
18
|
+
randomize: true
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
assert.equal(timeouts.length, 10);
|
|
22
|
+
assert.ok(timeouts[0] > minTimeout);
|
|
23
|
+
assert.ok(timeouts[1] > timeouts[0]);
|
|
24
|
+
assert.ok(timeouts[2] > timeouts[1]);
|
|
25
|
+
})();
|
|
26
|
+
|
|
27
|
+
(function testPassedTimeoutsAreUsed() {
|
|
28
|
+
var timeoutsArray = [1000, 2000, 3000];
|
|
29
|
+
var timeouts = retry.timeouts(timeoutsArray);
|
|
30
|
+
assert.deepEqual(timeouts, timeoutsArray);
|
|
31
|
+
assert.notStrictEqual(timeouts, timeoutsArray);
|
|
32
|
+
})();
|
|
33
|
+
|
|
34
|
+
(function testTimeoutsAreWithinBoundaries() {
|
|
35
|
+
var minTimeout = 1000;
|
|
36
|
+
var maxTimeout = 10000;
|
|
37
|
+
var timeouts = retry.timeouts({
|
|
38
|
+
minTimeout: minTimeout,
|
|
39
|
+
maxTimeout: maxTimeout
|
|
40
|
+
});
|
|
41
|
+
for (var i = 0; i < timeouts; i++) {
|
|
42
|
+
assert.ok(timeouts[i] >= minTimeout);
|
|
43
|
+
assert.ok(timeouts[i] <= maxTimeout);
|
|
44
|
+
}
|
|
45
|
+
})();
|
|
46
|
+
|
|
47
|
+
(function testTimeoutsAreIncremental() {
|
|
48
|
+
var timeouts = retry.timeouts();
|
|
49
|
+
var lastTimeout = timeouts[0];
|
|
50
|
+
for (var i = 0; i < timeouts; i++) {
|
|
51
|
+
assert.ok(timeouts[i] > lastTimeout);
|
|
52
|
+
lastTimeout = timeouts[i];
|
|
53
|
+
}
|
|
54
|
+
})();
|
|
55
|
+
|
|
56
|
+
(function testTimeoutsAreIncrementalForFactorsLessThanOne() {
|
|
57
|
+
var timeouts = retry.timeouts({
|
|
58
|
+
retries: 3,
|
|
59
|
+
factor: 0.5
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
var expected = [250, 500, 1000];
|
|
63
|
+
assert.deepEqual(expected, timeouts);
|
|
64
|
+
})();
|
|
65
|
+
|
|
66
|
+
(function testRetries() {
|
|
67
|
+
var timeouts = retry.timeouts({retries: 2});
|
|
68
|
+
assert.strictEqual(timeouts.length, 2);
|
|
69
|
+
})();
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
The ISC License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2015, Contributors
|
|
4
|
+
|
|
5
|
+
Permission to use, copy, modify, and/or distribute this software
|
|
6
|
+
for any purpose with or without fee is hereby granted, provided
|
|
7
|
+
that the above copyright notice and this permission notice
|
|
8
|
+
appear in all copies.
|
|
9
|
+
|
|
10
|
+
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
11
|
+
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
|
|
12
|
+
OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE
|
|
13
|
+
LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES
|
|
14
|
+
OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
|
|
15
|
+
WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
|
|
16
|
+
ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# signal-exit
|
|
2
|
+
|
|
3
|
+
[](https://travis-ci.org/tapjs/signal-exit)
|
|
4
|
+
[](https://coveralls.io/r/tapjs/signal-exit?branch=master)
|
|
5
|
+
[](https://www.npmjs.com/package/signal-exit)
|
|
6
|
+
[](https://github.com/conventional-changelog/standard-version)
|
|
7
|
+
|
|
8
|
+
When you want to fire an event no matter how a process exits:
|
|
9
|
+
|
|
10
|
+
* reaching the end of execution.
|
|
11
|
+
* explicitly having `process.exit(code)` called.
|
|
12
|
+
* having `process.kill(pid, sig)` called.
|
|
13
|
+
* receiving a fatal signal from outside the process
|
|
14
|
+
|
|
15
|
+
Use `signal-exit`.
|
|
16
|
+
|
|
17
|
+
```js
|
|
18
|
+
var onExit = require('signal-exit')
|
|
19
|
+
|
|
20
|
+
onExit(function (code, signal) {
|
|
21
|
+
console.log('process exited!')
|
|
22
|
+
})
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## API
|
|
26
|
+
|
|
27
|
+
`var remove = onExit(function (code, signal) {}, options)`
|
|
28
|
+
|
|
29
|
+
The return value of the function is a function that will remove the
|
|
30
|
+
handler.
|
|
31
|
+
|
|
32
|
+
Note that the function *only* fires for signals if the signal would
|
|
33
|
+
cause the process to exit. That is, there are no other listeners, and
|
|
34
|
+
it is a fatal signal.
|
|
35
|
+
|
|
36
|
+
## Options
|
|
37
|
+
|
|
38
|
+
* `alwaysLast`: Run this handler after any other signal or exit
|
|
39
|
+
handlers. This causes `process.emit` to be monkeypatched.
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
// Note: since nyc uses this module to output coverage, any lines
|
|
2
|
+
// that are in the direct sync flow of nyc's outputCoverage are
|
|
3
|
+
// ignored, since we can never get coverage for them.
|
|
4
|
+
// grab a reference to node's real process object right away
|
|
5
|
+
var process = global.process
|
|
6
|
+
|
|
7
|
+
const processOk = function (process) {
|
|
8
|
+
return process &&
|
|
9
|
+
typeof process === 'object' &&
|
|
10
|
+
typeof process.removeListener === 'function' &&
|
|
11
|
+
typeof process.emit === 'function' &&
|
|
12
|
+
typeof process.reallyExit === 'function' &&
|
|
13
|
+
typeof process.listeners === 'function' &&
|
|
14
|
+
typeof process.kill === 'function' &&
|
|
15
|
+
typeof process.pid === 'number' &&
|
|
16
|
+
typeof process.on === 'function'
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// some kind of non-node environment, just no-op
|
|
20
|
+
/* istanbul ignore if */
|
|
21
|
+
if (!processOk(process)) {
|
|
22
|
+
module.exports = function () {
|
|
23
|
+
return function () {}
|
|
24
|
+
}
|
|
25
|
+
} else {
|
|
26
|
+
var assert = require('assert')
|
|
27
|
+
var signals = require('./signals.js')
|
|
28
|
+
var isWin = /^win/i.test(process.platform)
|
|
29
|
+
|
|
30
|
+
var EE = require('events')
|
|
31
|
+
/* istanbul ignore if */
|
|
32
|
+
if (typeof EE !== 'function') {
|
|
33
|
+
EE = EE.EventEmitter
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
var emitter
|
|
37
|
+
if (process.__signal_exit_emitter__) {
|
|
38
|
+
emitter = process.__signal_exit_emitter__
|
|
39
|
+
} else {
|
|
40
|
+
emitter = process.__signal_exit_emitter__ = new EE()
|
|
41
|
+
emitter.count = 0
|
|
42
|
+
emitter.emitted = {}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Because this emitter is a global, we have to check to see if a
|
|
46
|
+
// previous version of this library failed to enable infinite listeners.
|
|
47
|
+
// I know what you're about to say. But literally everything about
|
|
48
|
+
// signal-exit is a compromise with evil. Get used to it.
|
|
49
|
+
if (!emitter.infinite) {
|
|
50
|
+
emitter.setMaxListeners(Infinity)
|
|
51
|
+
emitter.infinite = true
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
module.exports = function (cb, opts) {
|
|
55
|
+
/* istanbul ignore if */
|
|
56
|
+
if (!processOk(global.process)) {
|
|
57
|
+
return function () {}
|
|
58
|
+
}
|
|
59
|
+
assert.equal(typeof cb, 'function', 'a callback must be provided for exit handler')
|
|
60
|
+
|
|
61
|
+
if (loaded === false) {
|
|
62
|
+
load()
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
var ev = 'exit'
|
|
66
|
+
if (opts && opts.alwaysLast) {
|
|
67
|
+
ev = 'afterexit'
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
var remove = function () {
|
|
71
|
+
emitter.removeListener(ev, cb)
|
|
72
|
+
if (emitter.listeners('exit').length === 0 &&
|
|
73
|
+
emitter.listeners('afterexit').length === 0) {
|
|
74
|
+
unload()
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
emitter.on(ev, cb)
|
|
78
|
+
|
|
79
|
+
return remove
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
var unload = function unload () {
|
|
83
|
+
if (!loaded || !processOk(global.process)) {
|
|
84
|
+
return
|
|
85
|
+
}
|
|
86
|
+
loaded = false
|
|
87
|
+
|
|
88
|
+
signals.forEach(function (sig) {
|
|
89
|
+
try {
|
|
90
|
+
process.removeListener(sig, sigListeners[sig])
|
|
91
|
+
} catch (er) {}
|
|
92
|
+
})
|
|
93
|
+
process.emit = originalProcessEmit
|
|
94
|
+
process.reallyExit = originalProcessReallyExit
|
|
95
|
+
emitter.count -= 1
|
|
96
|
+
}
|
|
97
|
+
module.exports.unload = unload
|
|
98
|
+
|
|
99
|
+
var emit = function emit (event, code, signal) {
|
|
100
|
+
/* istanbul ignore if */
|
|
101
|
+
if (emitter.emitted[event]) {
|
|
102
|
+
return
|
|
103
|
+
}
|
|
104
|
+
emitter.emitted[event] = true
|
|
105
|
+
emitter.emit(event, code, signal)
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// { <signal>: <listener fn>, ... }
|
|
109
|
+
var sigListeners = {}
|
|
110
|
+
signals.forEach(function (sig) {
|
|
111
|
+
sigListeners[sig] = function listener () {
|
|
112
|
+
/* istanbul ignore if */
|
|
113
|
+
if (!processOk(global.process)) {
|
|
114
|
+
return
|
|
115
|
+
}
|
|
116
|
+
// If there are no other listeners, an exit is coming!
|
|
117
|
+
// Simplest way: remove us and then re-send the signal.
|
|
118
|
+
// We know that this will kill the process, so we can
|
|
119
|
+
// safely emit now.
|
|
120
|
+
var listeners = process.listeners(sig)
|
|
121
|
+
if (listeners.length === emitter.count) {
|
|
122
|
+
unload()
|
|
123
|
+
emit('exit', null, sig)
|
|
124
|
+
/* istanbul ignore next */
|
|
125
|
+
emit('afterexit', null, sig)
|
|
126
|
+
/* istanbul ignore next */
|
|
127
|
+
if (isWin && sig === 'SIGHUP') {
|
|
128
|
+
// "SIGHUP" throws an `ENOSYS` error on Windows,
|
|
129
|
+
// so use a supported signal instead
|
|
130
|
+
sig = 'SIGINT'
|
|
131
|
+
}
|
|
132
|
+
/* istanbul ignore next */
|
|
133
|
+
process.kill(process.pid, sig)
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
})
|
|
137
|
+
|
|
138
|
+
module.exports.signals = function () {
|
|
139
|
+
return signals
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
var loaded = false
|
|
143
|
+
|
|
144
|
+
var load = function load () {
|
|
145
|
+
if (loaded || !processOk(global.process)) {
|
|
146
|
+
return
|
|
147
|
+
}
|
|
148
|
+
loaded = true
|
|
149
|
+
|
|
150
|
+
// This is the number of onSignalExit's that are in play.
|
|
151
|
+
// It's important so that we can count the correct number of
|
|
152
|
+
// listeners on signals, and don't wait for the other one to
|
|
153
|
+
// handle it instead of us.
|
|
154
|
+
emitter.count += 1
|
|
155
|
+
|
|
156
|
+
signals = signals.filter(function (sig) {
|
|
157
|
+
try {
|
|
158
|
+
process.on(sig, sigListeners[sig])
|
|
159
|
+
return true
|
|
160
|
+
} catch (er) {
|
|
161
|
+
return false
|
|
162
|
+
}
|
|
163
|
+
})
|
|
164
|
+
|
|
165
|
+
process.emit = processEmit
|
|
166
|
+
process.reallyExit = processReallyExit
|
|
167
|
+
}
|
|
168
|
+
module.exports.load = load
|
|
169
|
+
|
|
170
|
+
var originalProcessReallyExit = process.reallyExit
|
|
171
|
+
var processReallyExit = function processReallyExit (code) {
|
|
172
|
+
/* istanbul ignore if */
|
|
173
|
+
if (!processOk(global.process)) {
|
|
174
|
+
return
|
|
175
|
+
}
|
|
176
|
+
process.exitCode = code || /* istanbul ignore next */ 0
|
|
177
|
+
emit('exit', process.exitCode, null)
|
|
178
|
+
/* istanbul ignore next */
|
|
179
|
+
emit('afterexit', process.exitCode, null)
|
|
180
|
+
/* istanbul ignore next */
|
|
181
|
+
originalProcessReallyExit.call(process, process.exitCode)
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
var originalProcessEmit = process.emit
|
|
185
|
+
var processEmit = function processEmit (ev, arg) {
|
|
186
|
+
if (ev === 'exit' && processOk(global.process)) {
|
|
187
|
+
/* istanbul ignore else */
|
|
188
|
+
if (arg !== undefined) {
|
|
189
|
+
process.exitCode = arg
|
|
190
|
+
}
|
|
191
|
+
var ret = originalProcessEmit.apply(this, arguments)
|
|
192
|
+
/* istanbul ignore next */
|
|
193
|
+
emit('exit', process.exitCode, null)
|
|
194
|
+
/* istanbul ignore next */
|
|
195
|
+
emit('afterexit', process.exitCode, null)
|
|
196
|
+
/* istanbul ignore next */
|
|
197
|
+
return ret
|
|
198
|
+
} else {
|
|
199
|
+
return originalProcessEmit.apply(this, arguments)
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
}
|