ee-core 2.0.3 → 2.1.0-beta.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/bin/tools.js +4 -0
- package/config/config.default.js +9 -0
- package/const/channel.js +10 -1
- package/core/lib/ee.js +1 -1
- package/core/lib/loader/file_loader.js +2 -2
- package/core/lib/loader/mixin/config.js +1 -1
- package/core/lib/utils/index.js +1 -1
- package/ee/eeApp.js +8 -7
- package/exception/index.js +40 -12
- package/httpclient/index.js +4 -12
- package/index.js +1 -1
- package/jobs/child/app.js +11 -2
- package/jobs/child/forkProcess.js +81 -6
- package/jobs/child/index.js +41 -45
- package/jobs/child-pool/index.js +205 -0
- package/jobs/index.js +3 -1
- package/jobs/load-balancer/algorithm/index.js +12 -0
- package/jobs/load-balancer/algorithm/minimumConnection.js +19 -0
- package/jobs/load-balancer/algorithm/polling.js +12 -0
- package/jobs/load-balancer/algorithm/random.js +10 -0
- package/jobs/load-balancer/algorithm/specify.js +15 -0
- package/jobs/load-balancer/algorithm/weights.js +22 -0
- package/jobs/load-balancer/algorithm/weightsMinimumConnection.js +30 -0
- package/jobs/load-balancer/algorithm/weightsPolling.js +23 -0
- package/jobs/load-balancer/algorithm/weightsRandom.js +17 -0
- package/jobs/load-balancer/consts.js +10 -0
- package/jobs/load-balancer/index.js +202 -0
- package/jobs/load-balancer/scheduler.js +32 -0
- package/loader/index.js +22 -2
- package/message/childMessage.js +23 -0
- package/package.json +1 -6
- package/ps/index.js +44 -0
- package/tools/encrypt.js +105 -45
- package/utils/co.js +237 -0
- package/utils/depd/index.js +538 -0
- package/utils/depd/lib/browser/index.js +77 -0
- package/utils/extend.js +73 -0
- package/utils/get-port/index.d.ts +64 -0
- package/utils/get-port/index.js +109 -0
- package/utils/helper.js +25 -1
- package/utils/index.js +46 -0
- package/utils/ip.js +261 -0
- package/utils/is.js +2 -1
- package/utils/time/index.js +20 -0
- package/utils/time/ms.js +162 -0
- package/jobs/childPool/app.js +0 -62
- package/jobs/childPool/forkProcess.js +0 -81
- package/jobs/childPool/index.js +0 -71
- package/jobs/childPool/pool.js +0 -67
- /package/{oldUtils → old-utils}/index.js +0 -0
package/message/childMessage.js
CHANGED
|
@@ -9,8 +9,18 @@ class ChildMessage {
|
|
|
9
9
|
* 向主进程发消息
|
|
10
10
|
*/
|
|
11
11
|
sendToMain(eventName, params = {}) {
|
|
12
|
+
let receiver = Channel.receiver.childJob;
|
|
13
|
+
return this.send(eventName, params, receiver);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* 向主进程发消息
|
|
18
|
+
*/
|
|
19
|
+
send(eventName, params = {}, receiver) {
|
|
20
|
+
let eventReceiver = receiver || Channel.receiver.forkProcess;
|
|
12
21
|
let message = {
|
|
13
22
|
channel: Channel.process.sendToMain,
|
|
23
|
+
eventReceiver,
|
|
14
24
|
event: eventName,
|
|
15
25
|
data: params,
|
|
16
26
|
}
|
|
@@ -24,6 +34,19 @@ class ChildMessage {
|
|
|
24
34
|
exit(code = 0) {
|
|
25
35
|
return process.exit(code);
|
|
26
36
|
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* 发送错误到控制台
|
|
40
|
+
*/
|
|
41
|
+
sendErrorToTerminal(err) {
|
|
42
|
+
let errTips = (err && typeof err == 'object') ? err.toString() : '';
|
|
43
|
+
errTips += ' Error !!! Please See file ee-core.log or ee-error-xxx.log for details !'
|
|
44
|
+
let message = {
|
|
45
|
+
channel: Channel.process.showException,
|
|
46
|
+
data: errTips
|
|
47
|
+
}
|
|
48
|
+
process.send(message);
|
|
49
|
+
}
|
|
27
50
|
}
|
|
28
51
|
|
|
29
52
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ee-core",
|
|
3
|
-
"version": "2.0.3",
|
|
3
|
+
"version": "2.1.0-beta.3",
|
|
4
4
|
"description": "ee core",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -14,16 +14,11 @@
|
|
|
14
14
|
"dependencies": {
|
|
15
15
|
"agentkeepalive": "^4.2.0",
|
|
16
16
|
"bytenode": "^1.3.6",
|
|
17
|
-
"co": "^4.6.0",
|
|
18
17
|
"debug": "^4.3.3",
|
|
19
|
-
"depd": "^2.0.0",
|
|
20
18
|
"egg-errors": "^2.3.0",
|
|
21
19
|
"egg-logger": "^2.7.1",
|
|
22
|
-
"extend2": "^1.0.1",
|
|
23
20
|
"fs-extra": "^10.0.0",
|
|
24
|
-
"get-port": "^5.1.1",
|
|
25
21
|
"globby": "^10.0.0",
|
|
26
|
-
"humanize-ms": "^1.2.1",
|
|
27
22
|
"is-type-of": "^1.2.1",
|
|
28
23
|
"javascript-obfuscator": "^4.0.0",
|
|
29
24
|
"koa": "^2.13.4",
|
package/ps/index.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
const path = require('path');
|
|
2
2
|
const eis = require('../utils/is');
|
|
3
|
+
const Log = require('../log');
|
|
3
4
|
|
|
4
5
|
/**
|
|
5
6
|
* 当前进程的所有env
|
|
@@ -238,4 +239,47 @@ exports.makeMessage = function(msg = {}) {
|
|
|
238
239
|
}, msg);
|
|
239
240
|
|
|
240
241
|
return message;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* 退出ChildJob进程
|
|
246
|
+
*/
|
|
247
|
+
exports.exitChildJob = function(code = 0) {
|
|
248
|
+
try {
|
|
249
|
+
let args = JSON.parse(process.argv[2]);
|
|
250
|
+
if (args.type == 'childJob') {
|
|
251
|
+
process.exit(code);
|
|
252
|
+
}
|
|
253
|
+
} catch (e) {
|
|
254
|
+
process.exit(code);
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
|
|
259
|
+
/**
|
|
260
|
+
* 任务类型 ChildJob
|
|
261
|
+
*/
|
|
262
|
+
exports.isChildJob = function() {
|
|
263
|
+
try {
|
|
264
|
+
let args = JSON.parse(process.argv[2]);
|
|
265
|
+
if (args.type == 'childJob') {
|
|
266
|
+
return true;
|
|
267
|
+
}
|
|
268
|
+
} catch (e) {
|
|
269
|
+
return false;
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
/**
|
|
274
|
+
* 任务类型 ChildPoolJob
|
|
275
|
+
*/
|
|
276
|
+
exports.isChildPoolJob = function() {
|
|
277
|
+
try {
|
|
278
|
+
let args = JSON.parse(process.argv[2]);
|
|
279
|
+
if (args.type == 'childPoolJob') {
|
|
280
|
+
return true;
|
|
281
|
+
}
|
|
282
|
+
} catch (e) {
|
|
283
|
+
return false;
|
|
284
|
+
}
|
|
241
285
|
}
|
package/tools/encrypt.js
CHANGED
|
@@ -7,6 +7,7 @@ const is = require('is-type-of');
|
|
|
7
7
|
const bytenode = require('bytenode');
|
|
8
8
|
const crypto = require('crypto');
|
|
9
9
|
const JavaScriptObfuscator = require('javascript-obfuscator');
|
|
10
|
+
const globby = require('globby');
|
|
10
11
|
const UtilsJson = require('../utils/json');
|
|
11
12
|
|
|
12
13
|
class Encrypt {
|
|
@@ -16,11 +17,14 @@ class Encrypt {
|
|
|
16
17
|
this.encryptCodeDir = path.join(this.basePath, 'public');
|
|
17
18
|
this.config = this.loadConfig('encrypt.js');
|
|
18
19
|
this.filesExt = this.config.fileExt || ['.js'];
|
|
19
|
-
this.type = this.config.type || '
|
|
20
|
+
this.type = this.config.type || 'confusion';
|
|
20
21
|
this.bOpt = this.config.bytecodeOptions || {};
|
|
21
22
|
this.cOpt = this.config.confusionOptions || {};
|
|
23
|
+
this.cleanFiles = this.config.cleanFiles || ['electron'];
|
|
22
24
|
|
|
23
25
|
const directory = this.config.directory || ['electron'];
|
|
26
|
+
this.patterns = this.config.files || null;
|
|
27
|
+
this.specificFiles = [ 'electron/preload/bridge.js' ];
|
|
24
28
|
this.tmpFile = ''; // todo
|
|
25
29
|
this.mapFile = ''; // todo
|
|
26
30
|
|
|
@@ -34,49 +38,80 @@ class Encrypt {
|
|
|
34
38
|
}
|
|
35
39
|
}
|
|
36
40
|
|
|
37
|
-
// 检查存在的目录
|
|
38
41
|
for (let i = 0; i < directory.length; i++) {
|
|
39
42
|
let codeDirPath = path.join(this.basePath, directory[i]);
|
|
40
43
|
if (fs.existsSync(codeDirPath)) {
|
|
41
44
|
this.dirs.push(directory[i]);
|
|
42
45
|
}
|
|
43
46
|
}
|
|
44
|
-
|
|
47
|
+
|
|
48
|
+
this.codefiles = this._initCodeFiles();
|
|
49
|
+
//console.log('[ee-core] [tools/encrypt] codefiles:', this.codefiles);
|
|
45
50
|
}
|
|
46
51
|
|
|
47
52
|
/**
|
|
48
|
-
*
|
|
53
|
+
* 初始化需要加密的文件列表
|
|
49
54
|
*/
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
for (let i = 0; i < this.dirs.length; i++) {
|
|
54
|
-
// check code dir
|
|
55
|
-
let codeDirPath = path.join(this.basePath, this.dirs[i]);
|
|
56
|
-
if (!fs.existsSync(codeDirPath)) {
|
|
57
|
-
console.log('[ee-core] [tools/encrypt] ERROR: backup %s is not exist', codeDirPath);
|
|
58
|
-
return
|
|
59
|
-
}
|
|
55
|
+
_initCodeFiles() {
|
|
56
|
+
if (!this.patterns) return;
|
|
60
57
|
|
|
61
|
-
|
|
58
|
+
const files = globby.sync(this.patterns, { cwd: this.basePath });
|
|
59
|
+
return files;
|
|
60
|
+
}
|
|
62
61
|
|
|
63
|
-
|
|
64
|
-
|
|
62
|
+
/**
|
|
63
|
+
* 备份代码
|
|
64
|
+
*/
|
|
65
|
+
backup() {
|
|
66
|
+
// clean
|
|
67
|
+
this.cleanCode();
|
|
65
68
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
this.
|
|
70
|
-
|
|
69
|
+
console.log('[ee-core] [tools/encrypt] backup start');
|
|
70
|
+
if (this.patterns) {
|
|
71
|
+
this.codefiles.forEach((filepath) => {
|
|
72
|
+
let source = path.join(this.basePath, filepath);
|
|
73
|
+
if (fs.existsSync(source)) {
|
|
74
|
+
let target = path.join(this.encryptCodeDir, filepath);
|
|
75
|
+
fsPro.copySync(source, target);
|
|
76
|
+
}
|
|
77
|
+
})
|
|
78
|
+
} else {
|
|
79
|
+
for (let i = 0; i < this.dirs.length; i++) {
|
|
80
|
+
// check code dir
|
|
81
|
+
let codeDirPath = path.join(this.basePath, this.dirs[i]);
|
|
82
|
+
if (!fs.existsSync(codeDirPath)) {
|
|
83
|
+
console.log('[ee-core] [tools/encrypt] ERROR: backup %s is not exist', codeDirPath);
|
|
84
|
+
return
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// copy
|
|
88
|
+
let targetDir = path.join(this.encryptCodeDir, this.dirs[i]);
|
|
89
|
+
console.log('[ee-core] [tools/encrypt] backup target Dir:', targetDir);
|
|
90
|
+
if (!fs.existsSync(targetDir)) {
|
|
91
|
+
this.mkdir(targetDir);
|
|
92
|
+
this.chmodPath(targetDir, '777');
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
fsPro.copySync(codeDirPath, targetDir);
|
|
71
96
|
}
|
|
72
|
-
|
|
73
|
-
fsPro.copySync(codeDirPath, targetDir);
|
|
74
97
|
}
|
|
98
|
+
|
|
75
99
|
console.log('[ee-core] [tools/encrypt] backup end');
|
|
76
100
|
return true;
|
|
77
101
|
}
|
|
78
102
|
|
|
79
|
-
|
|
103
|
+
/**
|
|
104
|
+
* 清除加密代码
|
|
105
|
+
*/
|
|
106
|
+
cleanCode() {
|
|
107
|
+
this.cleanFiles.forEach((file) => {
|
|
108
|
+
let tmpFile = path.join(this.encryptCodeDir, file);
|
|
109
|
+
this.rmBackup(tmpFile);
|
|
110
|
+
console.log('[ee-core] [tools/encrypt] clean up tmp files:', tmpFile);
|
|
111
|
+
})
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
prepare() {
|
|
80
115
|
if (this.type == 'bytecode') {
|
|
81
116
|
let filename = this.config.mangle || this.config.mangle.file || null;
|
|
82
117
|
if (filename) {
|
|
@@ -96,11 +131,28 @@ class Encrypt {
|
|
|
96
131
|
/**
|
|
97
132
|
* 加密代码
|
|
98
133
|
*/
|
|
99
|
-
encrypt
|
|
134
|
+
encrypt() {
|
|
100
135
|
console.log('[ee-core] [tools/encrypt] start ciphering');
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
136
|
+
if (this.patterns) {
|
|
137
|
+
for (const file of this.codefiles) {
|
|
138
|
+
const fullpath = path.join(this.encryptCodeDir, file);
|
|
139
|
+
if (!fs.statSync(fullpath).isFile()) continue;
|
|
140
|
+
|
|
141
|
+
// 特殊文件处理
|
|
142
|
+
if (this.specificFiles.includes(file)) {
|
|
143
|
+
this.generate(fullpath, 'confusion');
|
|
144
|
+
continue;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
this.generate(fullpath);
|
|
148
|
+
}
|
|
149
|
+
} else {
|
|
150
|
+
console.log('[ee-core] [tools/encrypt] !!!!!! please use the new encryption method !!!!!!');
|
|
151
|
+
for (let i = 0; i < this.dirs.length; i++) {
|
|
152
|
+
let codeDirPath = path.join(this.encryptCodeDir, this.dirs[i]);
|
|
153
|
+
this.loop(codeDirPath);
|
|
154
|
+
}
|
|
155
|
+
console.log('[ee-core] [tools/encrypt] !!!!!! please use the new encryption method !!!!!!');
|
|
104
156
|
}
|
|
105
157
|
|
|
106
158
|
console.log('[ee-core] [tools/encrypt] end ciphering');
|
|
@@ -109,7 +161,7 @@ class Encrypt {
|
|
|
109
161
|
/**
|
|
110
162
|
* 递归
|
|
111
163
|
*/
|
|
112
|
-
loop
|
|
164
|
+
loop(dirPath) {
|
|
113
165
|
let files = [];
|
|
114
166
|
if (fs.existsSync(dirPath)) {
|
|
115
167
|
files = fs.readdirSync(dirPath);
|
|
@@ -130,10 +182,13 @@ class Encrypt {
|
|
|
130
182
|
/**
|
|
131
183
|
* 生成文件
|
|
132
184
|
*/
|
|
133
|
-
generate
|
|
134
|
-
|
|
185
|
+
generate(curPath, type) {
|
|
186
|
+
let encryptType = type ? type : this.type;
|
|
187
|
+
console.log(`[ee-core] [tools/encrypt] file: ${curPath} (${encryptType})`);
|
|
188
|
+
|
|
189
|
+
if (encryptType == 'bytecode') {
|
|
135
190
|
this.generateBytecodeFile(curPath);
|
|
136
|
-
} else if (
|
|
191
|
+
} else if (encryptType == 'confusion') {
|
|
137
192
|
this.generateJSConfuseFile(curPath);
|
|
138
193
|
} else {
|
|
139
194
|
this.generateJSConfuseFile(curPath);
|
|
@@ -144,7 +199,7 @@ class Encrypt {
|
|
|
144
199
|
/**
|
|
145
200
|
* 使用 javascript-obfuscator 生成压缩/混淆文件
|
|
146
201
|
*/
|
|
147
|
-
generateJSConfuseFile
|
|
202
|
+
generateJSConfuseFile(file) {
|
|
148
203
|
let opt = Object.assign({
|
|
149
204
|
compact: true,
|
|
150
205
|
stringArray: true,
|
|
@@ -159,7 +214,7 @@ class Encrypt {
|
|
|
159
214
|
/**
|
|
160
215
|
* 生成字节码文件
|
|
161
216
|
*/
|
|
162
|
-
generateBytecodeFile
|
|
217
|
+
generateBytecodeFile(curPath) {
|
|
163
218
|
if (path.extname(curPath) !== '.js') {
|
|
164
219
|
return
|
|
165
220
|
}
|
|
@@ -181,10 +236,9 @@ class Encrypt {
|
|
|
181
236
|
/**
|
|
182
237
|
* 移除备份
|
|
183
238
|
*/
|
|
184
|
-
rmBackup
|
|
185
|
-
if (fs.existsSync(
|
|
186
|
-
|
|
187
|
-
fsPro.removeSync(dir);
|
|
239
|
+
rmBackup(file) {
|
|
240
|
+
if (fs.existsSync(file)) {
|
|
241
|
+
fsPro.removeSync(file);
|
|
188
242
|
}
|
|
189
243
|
return;
|
|
190
244
|
}
|
|
@@ -192,7 +246,7 @@ class Encrypt {
|
|
|
192
246
|
/**
|
|
193
247
|
* 检查文件是否存在
|
|
194
248
|
*/
|
|
195
|
-
fileExist
|
|
249
|
+
fileExist(filePath) {
|
|
196
250
|
try {
|
|
197
251
|
return fs.statSync(filePath).isFile();
|
|
198
252
|
} catch (err) {
|
|
@@ -200,7 +254,7 @@ class Encrypt {
|
|
|
200
254
|
}
|
|
201
255
|
};
|
|
202
256
|
|
|
203
|
-
mkdir
|
|
257
|
+
mkdir(dirpath, dirname) {
|
|
204
258
|
// 判断是否是第一次调用
|
|
205
259
|
if (typeof dirname === 'undefined') {
|
|
206
260
|
if (fs.existsSync(dirpath)) {
|
|
@@ -222,7 +276,7 @@ class Encrypt {
|
|
|
222
276
|
}
|
|
223
277
|
};
|
|
224
278
|
|
|
225
|
-
chmodPath
|
|
279
|
+
chmodPath(path, mode) {
|
|
226
280
|
let files = [];
|
|
227
281
|
if (fs.existsSync(path)) {
|
|
228
282
|
files = fs.readdirSync(path);
|
|
@@ -238,7 +292,7 @@ class Encrypt {
|
|
|
238
292
|
}
|
|
239
293
|
};
|
|
240
294
|
|
|
241
|
-
loadConfig
|
|
295
|
+
loadConfig(prop) {
|
|
242
296
|
const filepath = path.join(this.basePath, 'electron', 'config', prop);
|
|
243
297
|
if (!fs.existsSync(filepath)) {
|
|
244
298
|
return {};
|
|
@@ -254,7 +308,7 @@ class Encrypt {
|
|
|
254
308
|
return ret || {};
|
|
255
309
|
};
|
|
256
310
|
|
|
257
|
-
md5
|
|
311
|
+
md5(file) {
|
|
258
312
|
const buffer = fs.readFileSync(file);
|
|
259
313
|
const hash = crypto.createHash('md5');
|
|
260
314
|
hash.update(buffer, 'utf8');
|
|
@@ -270,6 +324,12 @@ const run = () => {
|
|
|
270
324
|
e.encrypt();
|
|
271
325
|
}
|
|
272
326
|
|
|
327
|
+
const clean = () => {
|
|
328
|
+
const e = new Encrypt();
|
|
329
|
+
e.cleanCode();
|
|
330
|
+
}
|
|
331
|
+
|
|
273
332
|
module.exports = {
|
|
274
|
-
run
|
|
333
|
+
run,
|
|
334
|
+
clean,
|
|
275
335
|
};
|
package/utils/co.js
ADDED
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
|
|
2
|
+
/**
|
|
3
|
+
* slice() reference.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
var slice = Array.prototype.slice;
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Expose `co`.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
module.exports = co['default'] = co.co = co;
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Wrap the given generator `fn` into a
|
|
16
|
+
* function that returns a promise.
|
|
17
|
+
* This is a separate function so that
|
|
18
|
+
* every `co()` call doesn't create a new,
|
|
19
|
+
* unnecessary closure.
|
|
20
|
+
*
|
|
21
|
+
* @param {GeneratorFunction} fn
|
|
22
|
+
* @return {Function}
|
|
23
|
+
* @api public
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
co.wrap = function (fn) {
|
|
27
|
+
createPromise.__generatorFunction__ = fn;
|
|
28
|
+
return createPromise;
|
|
29
|
+
function createPromise() {
|
|
30
|
+
return co.call(this, fn.apply(this, arguments));
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Execute the generator function or a generator
|
|
36
|
+
* and return a promise.
|
|
37
|
+
*
|
|
38
|
+
* @param {Function} fn
|
|
39
|
+
* @return {Promise}
|
|
40
|
+
* @api public
|
|
41
|
+
*/
|
|
42
|
+
|
|
43
|
+
function co(gen) {
|
|
44
|
+
var ctx = this;
|
|
45
|
+
var args = slice.call(arguments, 1)
|
|
46
|
+
|
|
47
|
+
// we wrap everything in a promise to avoid promise chaining,
|
|
48
|
+
// which leads to memory leak errors.
|
|
49
|
+
// see https://github.com/tj/co/issues/180
|
|
50
|
+
return new Promise(function(resolve, reject) {
|
|
51
|
+
if (typeof gen === 'function') gen = gen.apply(ctx, args);
|
|
52
|
+
if (!gen || typeof gen.next !== 'function') return resolve(gen);
|
|
53
|
+
|
|
54
|
+
onFulfilled();
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* @param {Mixed} res
|
|
58
|
+
* @return {Promise}
|
|
59
|
+
* @api private
|
|
60
|
+
*/
|
|
61
|
+
|
|
62
|
+
function onFulfilled(res) {
|
|
63
|
+
var ret;
|
|
64
|
+
try {
|
|
65
|
+
ret = gen.next(res);
|
|
66
|
+
} catch (e) {
|
|
67
|
+
return reject(e);
|
|
68
|
+
}
|
|
69
|
+
next(ret);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* @param {Error} err
|
|
74
|
+
* @return {Promise}
|
|
75
|
+
* @api private
|
|
76
|
+
*/
|
|
77
|
+
|
|
78
|
+
function onRejected(err) {
|
|
79
|
+
var ret;
|
|
80
|
+
try {
|
|
81
|
+
ret = gen.throw(err);
|
|
82
|
+
} catch (e) {
|
|
83
|
+
return reject(e);
|
|
84
|
+
}
|
|
85
|
+
next(ret);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Get the next value in the generator,
|
|
90
|
+
* return a promise.
|
|
91
|
+
*
|
|
92
|
+
* @param {Object} ret
|
|
93
|
+
* @return {Promise}
|
|
94
|
+
* @api private
|
|
95
|
+
*/
|
|
96
|
+
|
|
97
|
+
function next(ret) {
|
|
98
|
+
if (ret.done) return resolve(ret.value);
|
|
99
|
+
var value = toPromise.call(ctx, ret.value);
|
|
100
|
+
if (value && isPromise(value)) return value.then(onFulfilled, onRejected);
|
|
101
|
+
return onRejected(new TypeError('You may only yield a function, promise, generator, array, or object, '
|
|
102
|
+
+ 'but the following object was passed: "' + String(ret.value) + '"'));
|
|
103
|
+
}
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Convert a `yield`ed value into a promise.
|
|
109
|
+
*
|
|
110
|
+
* @param {Mixed} obj
|
|
111
|
+
* @return {Promise}
|
|
112
|
+
* @api private
|
|
113
|
+
*/
|
|
114
|
+
|
|
115
|
+
function toPromise(obj) {
|
|
116
|
+
if (!obj) return obj;
|
|
117
|
+
if (isPromise(obj)) return obj;
|
|
118
|
+
if (isGeneratorFunction(obj) || isGenerator(obj)) return co.call(this, obj);
|
|
119
|
+
if ('function' == typeof obj) return thunkToPromise.call(this, obj);
|
|
120
|
+
if (Array.isArray(obj)) return arrayToPromise.call(this, obj);
|
|
121
|
+
if (isObject(obj)) return objectToPromise.call(this, obj);
|
|
122
|
+
return obj;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Convert a thunk to a promise.
|
|
127
|
+
*
|
|
128
|
+
* @param {Function}
|
|
129
|
+
* @return {Promise}
|
|
130
|
+
* @api private
|
|
131
|
+
*/
|
|
132
|
+
|
|
133
|
+
function thunkToPromise(fn) {
|
|
134
|
+
var ctx = this;
|
|
135
|
+
return new Promise(function (resolve, reject) {
|
|
136
|
+
fn.call(ctx, function (err, res) {
|
|
137
|
+
if (err) return reject(err);
|
|
138
|
+
if (arguments.length > 2) res = slice.call(arguments, 1);
|
|
139
|
+
resolve(res);
|
|
140
|
+
});
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Convert an array of "yieldables" to a promise.
|
|
146
|
+
* Uses `Promise.all()` internally.
|
|
147
|
+
*
|
|
148
|
+
* @param {Array} obj
|
|
149
|
+
* @return {Promise}
|
|
150
|
+
* @api private
|
|
151
|
+
*/
|
|
152
|
+
|
|
153
|
+
function arrayToPromise(obj) {
|
|
154
|
+
return Promise.all(obj.map(toPromise, this));
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Convert an object of "yieldables" to a promise.
|
|
159
|
+
* Uses `Promise.all()` internally.
|
|
160
|
+
*
|
|
161
|
+
* @param {Object} obj
|
|
162
|
+
* @return {Promise}
|
|
163
|
+
* @api private
|
|
164
|
+
*/
|
|
165
|
+
|
|
166
|
+
function objectToPromise(obj){
|
|
167
|
+
var results = new obj.constructor();
|
|
168
|
+
var keys = Object.keys(obj);
|
|
169
|
+
var promises = [];
|
|
170
|
+
for (var i = 0; i < keys.length; i++) {
|
|
171
|
+
var key = keys[i];
|
|
172
|
+
var promise = toPromise.call(this, obj[key]);
|
|
173
|
+
if (promise && isPromise(promise)) defer(promise, key);
|
|
174
|
+
else results[key] = obj[key];
|
|
175
|
+
}
|
|
176
|
+
return Promise.all(promises).then(function () {
|
|
177
|
+
return results;
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
function defer(promise, key) {
|
|
181
|
+
// predefine the key in the result
|
|
182
|
+
results[key] = undefined;
|
|
183
|
+
promises.push(promise.then(function (res) {
|
|
184
|
+
results[key] = res;
|
|
185
|
+
}));
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Check if `obj` is a promise.
|
|
191
|
+
*
|
|
192
|
+
* @param {Object} obj
|
|
193
|
+
* @return {Boolean}
|
|
194
|
+
* @api private
|
|
195
|
+
*/
|
|
196
|
+
|
|
197
|
+
function isPromise(obj) {
|
|
198
|
+
return 'function' == typeof obj.then;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* Check if `obj` is a generator.
|
|
203
|
+
*
|
|
204
|
+
* @param {Mixed} obj
|
|
205
|
+
* @return {Boolean}
|
|
206
|
+
* @api private
|
|
207
|
+
*/
|
|
208
|
+
|
|
209
|
+
function isGenerator(obj) {
|
|
210
|
+
return 'function' == typeof obj.next && 'function' == typeof obj.throw;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* Check if `obj` is a generator function.
|
|
215
|
+
*
|
|
216
|
+
* @param {Mixed} obj
|
|
217
|
+
* @return {Boolean}
|
|
218
|
+
* @api private
|
|
219
|
+
*/
|
|
220
|
+
function isGeneratorFunction(obj) {
|
|
221
|
+
var constructor = obj.constructor;
|
|
222
|
+
if (!constructor) return false;
|
|
223
|
+
if ('GeneratorFunction' === constructor.name || 'GeneratorFunction' === constructor.displayName) return true;
|
|
224
|
+
return isGenerator(constructor.prototype);
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
/**
|
|
228
|
+
* Check for plain object.
|
|
229
|
+
*
|
|
230
|
+
* @param {Mixed} val
|
|
231
|
+
* @return {Boolean}
|
|
232
|
+
* @api private
|
|
233
|
+
*/
|
|
234
|
+
|
|
235
|
+
function isObject(val) {
|
|
236
|
+
return Object == val.constructor;
|
|
237
|
+
}
|