-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathindex.js
117 lines (105 loc) · 2.78 KB
/
index.js
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
116
117
const destroySym = Symbol();
module.exports = function system(...specs) {
const nodes = specs.map(s => createNode(...s));
const nodeByFactory = p => find(nodes, n => n.factory === p);
nodes.forEach(node => {
node.edges = compact(node.params.map(nodeByFactory));
node._edges = copyArray(node.edges);
});
// main node, used only for build system
const main = createNode(() => Promise.resolve('main node'));
const hasNoIncomingEdge = node => nodes.every(n => n._edges.indexOf(node) === -1);
// toposort
const L = [];
const S = nodes.filter(n => hasNoIncomingEdge(n));
main.edges = copyArray(S);
while (S.length) {
const node = S.pop();
L.push(node);
let i = node._edges.length;
while (i--) {
const m = node._edges[i];
node._edges.pop();
if (hasNoIncomingEdge(m)) {
S.push(m);
}
}
}
const nodeWithEdge = find(nodes, n => n._edges && n._edges.length > 0);
if (nodeWithEdge) {
return Promise.reject(new Error('Cycles found'));
}
const instances = [];
const initNode = node => {
const { factory, params } = node;
let c = Promise.resolve(null);
node.edges.forEach(e => {
c = c.then(() => initNode(e));
});
let tid;
return c.then(() => {
// if node already inited, return instance
if (node.instance != null) {
return node.instance;
}
const paramsWithInstances = params.map(p => {
const n = nodeByFactory(p);
return n != null && n.instance != null ? n.instance : p;
});
tid = setTimeout(() => {
logger.error(new Error('Node init timeout'), {
factory: factory.toString().substring(0, 100)
});
}, 35000);
// if not inited - init it
return factory(...paramsWithInstances);
}).then(instance => {
if (tid) {
clearTimeout(tid);
}
node.instance = instance; // eslint-disable-line
instances.push(instance);
});
};
return initNode(main).then(() => {
const sys = f => {
const node = nodeByFactory(f);
if (node == null) {
return null;
}
return node.instance;
};
sys.destroy = () => {
return instances.reduce((p, i) => p.then(() => {
return typeof i[destroySym] === 'function' ?
i[destroySym]() :
null;
}), Promise.resolve());
};
return sys;
});
}
module.exports.destroy = destroySym;
function createNode(factory, ...params) {
return {
factory,
params,
edges: [],
_edges: [],
instance: null
};
}
function copyArray(edges) {
return edges.slice();
}
function compact(list) {
return list.filter(Boolean);
}
function find(list, foo) {
for (let i = 0, l = list.length; i < l; i++) {
if (foo(list[i])) {
return list[i];
}
}
return null;
}