]>
git.r.bdr.sh - rbdr/grafn/blob - lib/grafn.js
4 kVertexNotFound: 'There is no vertex with the name: ',
12 * The definition of a vertex that can be executed in the graph.
14 * @typedef {object} tVertex
15 * @property {string} name The name of the vertex
16 * @property {string[]} [dependencies=[]] The names of vertices that need to run before this one
17 * @property {function} action The action to execute
21 * Represents a graph of functions. You can call run on any specific vertex,
22 * which will trigger execution of it and its dependencies.
24 * It guarantees that each vertex will only run once.
26 * It can be represented in graphviz format, highlighting fulfilled and
31 module
.exports
= class Grafn
{
36 this._dependents
= {};
41 * Executes the named vertex and all its dependents
45 * @param {string} vertexName the name of the vertex to run
46 * @throws Will throw an error if a requested vertex does not exist
48 async
run(vertexName
) {
50 const vertex
= this._vertices
[vertexName
];
53 throw new Error(internals
.kVertexNotFound
+ vertexName
);
56 if (!vertex
.isFulfilled
&& this._dependenciesFulfilled(vertex
.dependencies
)) {
58 this._state
[vertexName
] = await vertex
.action(this._state
);
59 vertex
.isFulfilled
= true;
62 vertex
.isRejected
= true;
67 await Promise
.all(this._dependents
[vertexName
].map((dependent
) => this.run(dependent
)));
71 * Adds a vertex to the graph.
75 * @param {tVertex} vertex the definition of the vertex to add
77 vertex({ name
, action
, dependencies
= [] }) {
79 this._vertices
[name
] = { action
, dependencies
, isFulfilled: false, isRejected: false };
80 this._dependents
[name
] = this._dependents
[name
] || [];
82 dependencies
.forEach((dependency
) => {
84 this._dependents
[dependency
] = this._dependents
[dependency
] || [];
85 this._dependents
[dependency
].push(name
);
90 * Converts the graph to a graphviz digraph. If vertices have been executed,
91 * they will be highlighted depending on whether they fulfilled or rejected.
95 * @return {string} The graphviz digraph representation
99 const string
= ['digraph {'];
101 Object
.entries(this._vertices
).forEach(([name
, vertex
]) => {
103 string
.push(` ${name}${this._vertexColor(vertex)}`);
104 vertex
.dependencies
.forEach((dependency
) => string
.push(` ${dependency} -> ${name}`));
109 return string
.join('\n');
112 // Given a list of dependencies, check that all of them are fulfilled
114 _dependenciesFulfilled(dependencies
) {
117 .map((dependency
) => (this._vertices
[dependency
] || {}).isFulfilled
)
118 .reduce((test
, isFulfilled
) => test
&& isFulfilled
, true);
121 // Given the state of a vertex, returns the graphviz color configuration
123 _vertexColor(vertex
) {
125 if (vertex
.isFulfilled
) {
126 return `[color=${internals.kColors.fulfilled}]`;
129 if (vertex
.isRejected
) {
130 return `[color=${internals.kColors.rejected}]`;