kui-shell
Version:
This is the monorepo for Kui, the hybrid command-line/GUI electron-based Kubernetes tool
652 lines • 28.6 kB
JavaScript
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
const debug_1 = require("debug");
const util_1 = require("./util");
const AST = require("./ast");
const subtext_1 = require("./subtext");
const debug = debug_1.default('plugins/wskflow/fsm2graph');
const maxWidth = 100;
const defaultHeight = 20;
const defaultCharWidth = 5;
const defaultCharHeight = 10;
const capitalize = (str) => str.charAt(0).toUpperCase() + str.substring(1);
function id2log(id) {
return id
.replace(/-components/g, '')
.replace(/__origin/g, '')
.replace(/__terminus/g, '');
}
class RenderState {
constructor(acts) {
this.dummyCount = 0;
this.taskIndex = 0;
this.activations = acts;
if (acts) {
this.activations.sort((a, b) => {
return a.start - b.start;
});
}
this.visited = undefined;
this.graphData = {
id: 'root',
label: 'root',
children: [],
edges: []
};
}
addDummy(sources = [], targets, obj, options, directionS, directionT) {
const dummyId = 'dummy_' + this.dummyCount;
let o;
let port;
this.dummyCount++;
obj.children.push(this.drawNodeNew(dummyId, options.label || dummyId, options.type || 'Dummy', sources, options));
if (sources && sources.length > 0) {
o = this.drawEdgeNew(sources[0], dummyId, obj);
port = o.targetPort;
obj.edges.push(o);
for (let i = 1; i < sources.length; i++) {
obj.edges.push(this.drawEdgeNew(sources[i], dummyId, obj, undefined, directionS, undefined, port));
}
}
if (targets && targets.length > 0) {
o = this.drawEdgeNew(dummyId, targets[0], obj);
port = o.sourcePort;
obj.edges.push(o);
for (let i = 1; i < targets.length; i++) {
obj.edges.push(this.drawEdgeNew(dummyId, targets[i], obj, undefined, directionT, port, undefined));
}
}
return dummyId;
}
drawNodeNew(id, label, type, properties, options) {
const o = {
id,
label,
type,
ports: [],
value: options && options.value,
tooltip: options && options.tooltip,
tooltipHeader: options && options.tooltipHeader,
properties: {}
};
if (this.visited) {
if (id === 'Entry') {
this.visited[id] = [0];
o.visited = this.visited[id];
}
else if (id === 'Exit') {
if (Array.isArray(properties)) {
properties.forEach(p => {
if (this.visited[p]) {
this.visited[id] = [this.activations.length - 1];
}
});
}
o.visited = this.visited[id];
}
else {
const iid = id2log(id);
if (this.visited[iid]) {
if (type === 'action') {
this.visited[iid].forEach((v, i) => {
this.visited[iid][i]++;
});
}
if (type === 'retain') {
if ((this.visited[iid].length >= 1 && id.endsWith('__origin')) ||
(this.visited[iid].length === 2 && id.endsWith('__terminus'))) {
o.visited = this.visited[iid];
}
}
else {
o.visited = this.visited[iid];
}
}
}
}
if (this.visited && (this.visited[id] || id === 'Entry')) {
o.visited = this.visited[id] || [0];
}
if (type !== 'try' && type !== 'handler') {
o.properties['de.cau.cs.kieler.portConstraints'] = 'FIXED_ORDER';
}
if (options && options.leftToRight) {
delete o.properties['de.cau.cs.kieler.portConstraints'];
}
if (type !== 'Dummy' && type !== 'Exit' && properties) {
o.layoutOptions = {};
Object.keys(properties).forEach(p => {
o.properties[p] = properties[p];
o.layoutOptions[p] = properties[p];
});
}
if (options && options.leftToRight) {
o.properties = {
direction: 'RIGHT',
'org.eclipse.elk.direction': 'RIGHT'
};
}
if (o.type === 'action') {
if (label.indexOf('|') !== -1) {
o.name = label.substring(0, label.indexOf('|'));
label = label.substring(label.indexOf('|') + 1);
o.label = label;
}
else {
o.name = label;
}
if (label.lastIndexOf('/') !== -1 && label.lastIndexOf('/') < label.length - 1) {
o.label = label.substring(label.lastIndexOf('/') + 1);
}
o.height = defaultHeight;
if (o.label.length < 40) {
o.width = o.label.length * defaultCharWidth + 10;
}
else {
o.label = o.label.substring(0, 40) + '...';
o.width = 40 * defaultCharWidth + 10;
}
if (this.actions) {
if (this.actions[o.name] === undefined)
this.actions[o.name] = [];
this.actions[o.name].push(o.id);
}
o.taskIndex = this.taskIndex++;
}
else if (o.type === 'function') {
o.fullFunctionCode = label;
const prettyCode = label;
const { nLines, maxLineLength } = util_1.textualPropertiesOfCode(prettyCode);
if (options.renderFunctionsInView) {
const charWidthForCode = defaultCharWidth;
o.width = Math.min(maxWidth, maxLineLength * charWidthForCode);
o.height = Math.max(2.25, nLines) * defaultCharHeight;
o.multiLineLabel = prettyCode.split(/[\n\r]/).map(line => {
const width = o.width / charWidthForCode;
if (width >= line.length) {
return line;
}
else {
return line.substring(0, width) + '\u2026';
}
});
o.prettyCode = prettyCode;
delete o.label;
}
else {
o.width = 8;
o.height = 8;
o.tooltip = prettyCode;
delete o.label;
}
o.taskIndex = this.taskIndex++;
}
else if (o.type === 'try_catch') {
o.properties = {
direction: 'RIGHT',
'org.eclipse.elk.direction': 'RIGHT'
};
o.children = [
{
id: `${id}-body`,
label: 'try',
type: 'try',
ports: [],
properties: {},
children: [],
edges: [],
visited: this.visited ? this.visited[id2log(`${id}-body`)] : undefined
},
{
id: `${id}-handler`,
label: 'error handler',
type: 'handler',
ports: [],
properties: {},
children: [],
edges: [],
visited: this.visited ? this.visited[id2log(`${id}-<handler`)] : undefined
}
];
o.edges = [this.drawEdgeNew(`${id}-body`, `${id}-handler`, o, undefined, 'RIGHT')];
}
else if (o.type === 'Entry' || o.type === 'Exit') {
o.width = 18;
o.height = 18;
}
else if (o.type === 'retain') {
o.width = 4;
o.height = 4;
}
else if (o.type === 'Dummy') {
if (o.label === o.id) {
o.width = 4;
o.height = 4;
}
else {
o.width = 0.875 * o.label.length * defaultCharWidth;
o.height = 0.875 * defaultCharHeight;
}
if (this.visited && Array.isArray(properties)) {
properties.forEach((_s) => {
const s = id2log(_s);
if (this.visited[s]) {
this.visited[s].forEach(a => {
if (this.activations[a].response.success) {
if (this.visited[o.id] === undefined) {
this.visited[o.id] = [];
o.visited = [];
}
this.visited[o.id].push(a);
o.visited.push(a);
}
});
}
});
}
}
else if (o.type === 'let' || o.type === 'literal') {
if (o.label.length > 30) {
o.width = 30 * defaultCharWidth + 10;
}
else {
o.width = o.label.length * defaultCharWidth + 10;
}
o.height = defaultHeight;
o.tooltip = o.label;
delete o.label;
o.width = 20;
o.height = 20;
}
else if (o.type === 'retry') {
o.children = [];
o.edges = [];
o.retryCount = label;
o.label = `Retry ${label} time${parseInt(label, 10) > 1 ? 's' : ''}`;
}
else if (o.type === 'repeat') {
o.children = [];
o.edges = [];
o.repeatCount = label;
o.label = `Repeat ${label} time${parseInt(label, 10) > 1 ? 's' : ''}`;
}
return o;
}
drawEdgeNew(sourceId, targetId, layer, type, direction, sourcePort, targetPort) {
for (let i = 0; i < layer.children.length; i++) {
if (layer.children[i].id === sourceId) {
if (type) {
if (type === 'true' || type === 'false') {
sourcePort = `${sourceId}_p${type}`;
layer.children[i].properties.choice = true;
}
}
else if (layer.children[i].properties.choice) {
sourcePort = `${sourceId}_pfalse`;
}
else {
sourcePort = `${sourceId}_p${layer.children[i].ports.length}`;
}
layer.children[i].ports.push({
id: sourcePort,
properties: { portSide: direction || 'SOUTH' }
});
}
if (layer.children[i].id === targetId) {
targetPort = `${targetId}_p${layer.children[i].ports.length}`;
layer.children[i].ports.push({
id: targetPort,
properties: { portSide: direction || 'NORTH' }
});
}
if (sourcePort && targetPort) {
break;
}
}
if (sourcePort === undefined || targetPort === undefined) {
debug('source or target not found', sourceId, targetId, layer, this.graphData);
}
return {
id: sourceId + '_' + sourcePort + '->' + targetId + '_' + targetPort,
source: sourceId,
sourcePort: sourcePort,
target: targetId,
targetPort: targetPort,
visited: this.visited ? !!(this.visited[sourceId] && this.visited[targetId]) : undefined
};
}
ir2graph(ir, gm, id, prevId, options = {}) {
if (ir.type === 'sequence' || ir.type === 'seq' || Array.isArray(ir)) {
let count = 0;
let prev;
let array;
if (AST.isSequence(ir)) {
array = ir.components;
}
else {
array = ir;
}
array.forEach(obj => {
if (obj.options && obj.options.helper) {
}
else {
prev = this.ir2graph(obj, gm, `${id}-${count}`, count > 0 ? prev : prevId, options);
count++;
}
});
return prev;
}
else {
if (AST.isAction(ir)) {
let name = ir.name;
if (ir.displayLabel) {
name += `|${ir.displayLabel}`;
}
gm.children.push(this.drawNodeNew(id, name, ir.type, undefined, options));
if (prevId) {
prevId.forEach(pid => gm.edges.push(this.drawEdgeNew(pid, id, gm)));
}
return [id];
}
else if (AST.isFunction(ir)) {
gm.children.push(this.drawNodeNew(id, ir.function.exec.prettyCode || ir.function.exec.code, ir.type, undefined, options));
if (prevId) {
prevId.forEach(pid => gm.edges.push(this.drawEdgeNew(pid, id, gm)));
}
return [id];
}
else if (AST.isConditional(ir)) {
const firstTestId = gm.children.length;
const lastTestId = this.ir2graph(ir.test, gm, `${id}-test`, undefined, options);
const firstConsId = gm.children.length;
const lastConsId = this.ir2graph(ir.consequent, gm, `${id}-consequent`, undefined, options);
let firstAltId;
let lastAltId;
if (ir.alternate.type !== 'empty') {
firstAltId = gm.children.length;
lastAltId = this.ir2graph(ir.alternate, gm, `${id}-alternate`, undefined, options);
}
if (prevId) {
prevId.forEach(pid => gm.edges.push(this.drawEdgeNew(pid, gm.children[firstTestId].id, gm)));
}
let ltid;
if (lastTestId.length > 1) {
ltid = this.addDummy(lastTestId, undefined, gm, options);
}
else {
ltid = lastTestId[0];
}
gm.edges.push(this.drawEdgeNew(ltid, gm.children[firstConsId].id, gm, 'true'));
if (lastAltId && lastAltId.length > 0) {
gm.edges.push(this.drawEdgeNew(ltid, gm.children[firstAltId].id, gm, 'false'));
}
else {
lastAltId = [ltid];
}
const exitConcentrator = this.addDummy(lastAltId.concat(lastConsId), undefined, gm, options);
return [exitConcentrator];
}
else if (AST.isTry(ir)) {
gm.children.push(this.drawNodeNew(id, 'Try-Catch', 'try_catch', undefined, options));
if (prevId) {
prevId.forEach(pid => gm.edges.push(this.drawEdgeNew(pid, id, gm)));
}
else {
gm.children[gm.children.length - 1].properties.compoundNoParents = true;
}
const tryCatchPart = gm.children[gm.children.length - 1];
const tryPart = tryCatchPart.children[0];
const catchPart = tryCatchPart.children[1];
this.ir2graph(ir.body, tryPart, tryPart.id + '-components', undefined, options);
this.ir2graph(ir.handler, catchPart, catchPart.id + '-components', undefined, options);
return [gm.children[gm.children.length - 1].id];
}
else if (AST.isWhile(ir) || AST.isDoWhile(ir)) {
let firstTestId;
let firstBodyId;
let lastTestId;
let lastBodyId;
if (AST.isWhile(ir)) {
firstTestId = gm.children.length;
lastTestId = this.ir2graph(ir.test, gm, `${id}-test`, undefined, options);
if (prevId) {
prevId.forEach(pid => gm.edges.push(this.drawEdgeNew(pid, gm.children[firstTestId].id, gm)));
}
firstBodyId = gm.children.length;
lastBodyId = this.ir2graph(ir.body, gm, `${id}-body`, undefined, options);
}
else if (AST.isDoWhile(ir)) {
firstBodyId = gm.children.length;
lastBodyId = this.ir2graph(ir.body, gm, `${id}-body`, undefined, options);
firstTestId = gm.children.length;
lastTestId = this.ir2graph(ir.test, gm, `${id}-test`, undefined, options);
}
let ltid;
let lbid;
if (lastTestId.length > 1) {
ltid = this.addDummy(lastTestId, undefined, gm, options);
}
else {
ltid = lastTestId[0];
}
if (lastBodyId.length > 1) {
lbid = this.addDummy(lastBodyId, undefined, gm, options);
}
else {
lbid = lastBodyId[0];
}
gm.edges.push(this.drawEdgeNew(ltid, gm.children[firstBodyId].id, gm, 'true'));
gm.edges.push(this.drawEdgeNew(lbid, gm.children[firstTestId].id, gm));
if (prevId && (ir.type === 'dowhile' || ir.type === 'dowhile_nosave')) {
prevId.forEach(pid => gm.edges.push(this.drawEdgeNew(pid, gm.children[firstBodyId].id, gm)));
}
return [ltid];
}
else if (AST.isRetain(ir)) {
gm.children.push(this.drawNodeNew(`${id}__origin`, '', ir.type, undefined, options));
if (prevId) {
prevId.forEach(pid => gm.edges.push(this.drawEdgeNew(pid, `${id}__origin`, gm)));
}
const lastNodes = this.ir2graph(ir.components, gm, id, [`${id}__origin`], options);
gm.children.push(this.drawNodeNew(`${id}__terminus`, '', ir.type, undefined, options));
if (lastNodes) {
lastNodes.forEach(pid => gm.edges.push(this.drawEdgeNew(pid, `${id}__terminus`, gm)));
}
const forwardingEdge = this.drawEdgeNew(`${id}__origin`, `${id}__terminus`, gm, undefined, 'EAST');
forwardingEdge.properties = { type: 'retain' };
gm.edges.push(forwardingEdge);
return [`${id}__terminus`];
}
else if (AST.isRetryOrRepeat(ir)) {
gm.children.push(this.drawNodeNew(id, ir.count, ir.type, undefined, options));
if (prevId) {
prevId.forEach(pid => gm.edges.push(this.drawEdgeNew(pid, id, gm)));
}
this.ir2graph(ir.components, gm.children[gm.children.length - 1], `${id}-components`, undefined, options);
return [gm.children[gm.children.length - 1].id];
}
else if (AST.isLet(ir)) {
const s = JSON.stringify(ir.declarations, undefined, 4);
gm.children.push(this.drawNodeNew(id, s, ir.type, undefined, Object.assign(options, { value: ir.declarations })));
if (prevId) {
prevId.forEach(pid => gm.edges.push(this.drawEdgeNew(pid, id, gm)));
}
return this.ir2graph(ir.components, gm, `${id}-components`, [id], options);
}
else if (AST.isLiteral(ir)) {
const s = JSON.stringify(ir.value, undefined, 4);
gm.children.push(this.drawNodeNew(id, s, ir.type, undefined, Object.assign(options, { value: ir.value })));
if (prevId) {
prevId.forEach(pid => gm.edges.push(this.drawEdgeNew(pid, id, gm)));
}
return [id];
}
else if (AST.isFinally(ir)) {
const lastBodyNode = this.ir2graph(ir.body, gm, `${id}-body`, prevId, options);
return this.ir2graph(ir.finalizer, gm, `${id}-finalizer`, lastBodyNode, options);
}
else if (AST.isParallelLike(ir)) {
const label = ir.type === 'map' || ir.type === 'forall' ? 'Parallel Map over Array' : 'Execute Tasks in Parallel';
const tooltipHeader = ir.type === 'par' || ir.type === 'parallel' ? 'Parallel' : ir.type === 'map' ? 'Map' : ir.type;
const tooltip = ir.type === 'map' || ir.type === 'forall'
? 'Executes a single task in parallel for each element of the input array'
: 'Executes a set of tasks in parallel, passing the same input data to each task';
let parent = prevId;
if (ir.set) {
parent = this.ir2graph(ir.set, gm, `${id}-${ir.type}-set`, parent, options);
}
const fork = this.addDummy(parent, undefined, gm, {
label,
tooltipHeader,
tooltip
});
parent = [fork];
let exits;
if (AST.isMapLike(ir)) {
const body = ir.components || ir.body;
const ellipsis = { type: 'literal', value: '...' };
const spreadItOut = [body, body, ellipsis, body];
exits = spreadItOut.map((component, idx) => {
return this.ir2graph(component, gm, `${id}-component-${idx}`, parent, options)[0];
});
}
else {
exits = ir.components.map((component, idx) => {
return this.ir2graph(component, gm, `${id}-component-${idx}`, parent, options)[0];
});
}
const exitConcentrator = this.addDummy(exits, undefined, gm, {
label: 'Wait for Completion',
tooltipHeader: `${tooltipHeader} completion`,
tooltip: 'Wait for the parallel tasks to complete, and then return an array of results'
});
return [exitConcentrator];
}
else if (AST.isComponentBearing(ir)) {
const label = capitalize(ir.type);
const type = ir.type;
const body = this.drawNodeNew(id, label, type, undefined, options);
body.children = [];
body.edges = [];
gm.children.push(body);
if (prevId) {
prevId.forEach(pid => gm.edges.push(this.drawEdgeNew(pid, id, gm)));
}
this.ir2graph(ir.components, body, `${id}-components`, undefined, options);
return [id];
}
else if (typeof ir === 'object') {
const type = 'action';
const label = ir.type;
gm.children.push(this.drawNodeNew(id, label, type, undefined, options));
if (prevId) {
prevId.forEach(pid => gm.edges.push(this.drawEdgeNew(pid, id, gm)));
}
debug('generic handler', ir, label, type);
return [id];
}
}
}
}
const numNonFunctions = (composition) => {
if (composition === undefined)
return 0;
if (composition.type === 'function') {
return 0;
}
else if (composition.type) {
let sum = 0;
for (const key in composition) {
sum += numNonFunctions(composition[key]);
}
return sum + 1;
}
else if (Array.isArray(composition)) {
return composition.reduce((sum, sub) => sum + numNonFunctions(sub), 0);
}
else {
return 0;
}
};
const isSimpleComposition = (ir) => {
const isShort = AST.isComponentArrayBearing(ir) ? ir.components.length <= 2 : true;
const numNonFuncs = numNonFunctions(ir);
const atMostOneNonFunction = numNonFuncs <= 3;
debug('isSimpleComposition', isShort, numNonFuncs);
return isShort && atMostOneNonFunction;
};
function fsm2graph(tab, ir, containerElement, acts, options, rule) {
return __awaiter(this, void 0, void 0, function* () {
const renderState = new RenderState(acts);
if (renderState.activations) {
debug('activations', renderState.activations);
renderState.visited = {};
renderState.activations.forEach((a, index) => {
if (a.logs) {
a.logs.forEach(log => {
if (log.indexOf('Entering composition') !== -1) {
let path = log.substring(log.lastIndexOf(' ') + 1);
path = path.replace(/[\[\.]/g, '-').replace(/\]/g, '');
if (renderState.visited[path] === undefined)
renderState.visited[path] = [];
renderState.visited[path].push(index);
}
});
}
});
Object.keys(renderState.visited).forEach(k => {
const seg = k.split('-');
seg.pop();
const path = seg.join('-');
if (renderState.visited[path] === undefined)
renderState.visited[path] = [];
renderState.visited[path] = renderState.visited[path].concat(renderState.visited[k]);
});
debug('visited nodes:', renderState.visited);
}
else {
renderState.actions = {};
}
debug('generating graph model');
const renderFunctionsInView = isSimpleComposition(ir);
const viewOptions = Object.assign({ renderFunctionsInView }, options);
if ((rule && rule.trigger) || AST.isOn(ir)) {
debug('using rule as start', rule);
const start = renderState.drawNodeNew('Entry', 'trigger', 'Entry');
start.properties.kind = 'trigger';
start.properties.kindDetail = rule ? rule.trigger.name : AST.isOn(ir) && ir.trigger;
start.width = 24;
renderState.graphData.children.push(start);
if (AST.isOn(ir)) {
ir = ir.components;
}
}
else {
renderState.graphData.children.push(renderState.drawNodeNew('Entry', 'start', 'Entry'));
}
let lastNodes = renderState.ir2graph(ir, renderState.graphData, 'composition', ['Entry'], viewOptions);
if (lastNodes === undefined) {
lastNodes = ['Entry'];
}
renderState.graphData.children.push(renderState.drawNodeNew('Exit', 'end', 'Exit', lastNodes));
lastNodes.forEach(pid => renderState.graphData.edges.push(renderState.drawEdgeNew(pid, 'Exit', renderState.graphData)));
debug('graphData', renderState.graphData);
const subtext = yield subtext_1.default(tab, renderState.actions, renderState.activations, renderState.graphData, options);
debug('subtext', subtext);
debug('inserting DOM, calling graph2doms');
const graph2doms = (yield Promise.resolve().then(() => require('./graph2doms'))).default;
const response = yield graph2doms(tab, renderState.graphData, containerElement, renderState.activations);
return Object.assign(response, { subtext });
});
}
exports.default = fsm2graph;
//# sourceMappingURL=fsm2graph.js.map