Comparing version 0.6.0 to 0.7.0
@@ -26,3 +26,4 @@ { | ||
"no-restricted-syntax": ["off"], | ||
"no-param-reassign": "off" | ||
"no-param-reassign": "off", | ||
"no-use-before-define": ["error", {"functions": false, "classes": false}] | ||
}, | ||
@@ -29,0 +30,0 @@ "plugins": [ |
@@ -24,2 +24,3 @@ 'use strict'; | ||
fsExtra.copySync(DEFAULT_PATH, path); | ||
fs.chmodSync(path, 0o600); | ||
return true; | ||
@@ -37,3 +38,5 @@ } catch (err) { | ||
try { | ||
fs.writeFileSync(path, configContents); | ||
fs.writeFileSync(path, configContents, { | ||
mode: 0o600 | ||
}); | ||
} catch (err) { | ||
@@ -40,0 +43,0 @@ configDebug(`failed to write config to "${path}": ${err}`); |
@@ -32,3 +32,5 @@ 'use strict'; | ||
EXPRESS_APP.get('/api/config/validatePermissions', (req, res) => { | ||
Config.validatePermissions().then((result) => { | ||
Config | ||
.validatePermissions() | ||
.then((result) => { | ||
res.json(result); | ||
@@ -35,0 +37,0 @@ }); |
@@ -21,2 +21,11 @@ 'use strict'; | ||
// check inspect arguments so we can fork properly | ||
let inspectPortNumberIncrement = 0; | ||
let inspectLevel = 0; | ||
if (process.execArgv.some(arg => arg.includes('--inspect='))) { | ||
inspectLevel = 1; | ||
} else if (process.execArgv.some(arg => arg.includes('--inspect-brk='))) { | ||
inspectLevel = 2; | ||
} | ||
/** | ||
@@ -60,7 +69,8 @@ * Flow class | ||
* Note that a path cannot be initiated twice because it is used for saveStatuses() | ||
* @static | ||
* @param {String} path The path to the directory containing the flows. | ||
* @return {Object.<String, Flow>} list of flows by their _id | ||
* @param {String} path The path to the directory containing the flows. | ||
* @param {Boolean} cleanVault Indicates whether the json data from each flow | ||
* needs vault sanitizing. | ||
* @return {Object.<String, Flow>} List of flows by their _id. | ||
*/ | ||
static initFromPath(flowPath) { | ||
static initFromPath(flowPath, cleanVault) { | ||
flowDebug(`init flows from "${flowPath}"`); | ||
@@ -100,3 +110,3 @@ if (this.flowPath) { | ||
const flow = new Flow(XIBLE); | ||
flow.initJson(json); | ||
flow.initJson(json, cleanVault); | ||
flows[flow._id] = flow; | ||
@@ -117,3 +127,3 @@ } | ||
* @param {String} flowPath The path to the directory containing the flows. | ||
* @return {Object.<String, Flow>} list of flows by their _id | ||
* @return {Object.<String, Flow>} List of flows by their _id. | ||
* @since 0.5.0 | ||
@@ -134,5 +144,5 @@ */ | ||
flows[flowId].forceStart() | ||
.catch((err) => { | ||
flowDebug(`failed to start "${flowId}": ${err}`); | ||
}); | ||
.catch((err) => { | ||
flowDebug(`failed to start "${flowId}": ${err}`); | ||
}); | ||
} else if (flows[flowId].initLevel === Flow.INITLEVEL_FLOW) { | ||
@@ -285,2 +295,3 @@ flows[flowId].init(); | ||
// remove those from the json (the json is used for saving) | ||
// save the vault data thereafter | ||
if (cleanVault) { | ||
@@ -301,2 +312,4 @@ const nodeVaultKeys = nodeConstr.vault; | ||
} | ||
} else if (xibleNode.vault) { | ||
Object.assign(xibleNode.data, xibleNode.vault.get()); | ||
} | ||
@@ -467,3 +480,4 @@ | ||
/* | ||
//uncommenting this needs to take care of commented _trackerTriggerTime elsewhere | ||
// uncommenting this needs to take care of commented _trackerTriggerTime elsewhere | ||
// and this.node.emit('triggerout', this); in /app/Node/index.js | ||
node.prependListener('triggerout', (output) => { | ||
@@ -484,3 +498,4 @@ | ||
} else { | ||
msg = `triggered '${output.name}' @ ${d.getHours()}:${d.getMinutes()}:${d.getSeconds()}:${d.getMilliseconds()}`; | ||
msg = `triggered '${output.name}' @ | ||
${d.getHours()}:${d.getMinutes()}:${d.getSeconds()}:${d.getMilliseconds()}`; | ||
} | ||
@@ -626,9 +641,9 @@ | ||
// trigger the action nodes | ||
// trigger the action nodes | ||
for (let i = 0; i < actionNodes.length; i += 1) { | ||
actionNodes[i].getInputs() | ||
.filter(input => input.type === 'trigger') | ||
.forEach((input) => { | ||
input.emit('trigger', null, flowState); | ||
}); | ||
.filter(input => input.type === 'trigger') | ||
.forEach((input) => { | ||
input.emit('trigger', null, flowState); | ||
}); | ||
} | ||
@@ -670,3 +685,3 @@ }); | ||
return this.init() | ||
.then(startFlow); | ||
.then(startFlow); | ||
@@ -688,3 +703,3 @@ case Flow.STATE_STOPPING: | ||
return this.stop() | ||
.then(() => this.forceStart(directNodes)); | ||
.then(() => this.forceStart(directNodes)); | ||
@@ -697,2 +712,4 @@ case Flow.STATE_STARTING: | ||
} | ||
return Promise.reject(new Error('Flow in unknown state')); | ||
} | ||
@@ -755,3 +772,11 @@ | ||
let flow; | ||
this.worker = fork(`${__dirname}/../../child.js`); | ||
const execArgv = []; | ||
if (inspectLevel === 1) { | ||
execArgv.push(`--inspect=0.0.0.0:${9229 + (inspectPortNumberIncrement += 1)}`); | ||
} else if (inspectLevel === 2) { | ||
execArgv.push(`--inspect-brk=0.0.0.0:${9229 + (inspectPortNumberIncrement += 1)}`); | ||
} | ||
this.worker = fork(`${__dirname}/../../child.js`, { | ||
execArgv | ||
}); | ||
this.worker.on('message', (message) => { | ||
@@ -761,3 +786,2 @@ switch (message.method) { | ||
case 'initializing': | ||
if (this.worker && this.worker.connected) { | ||
@@ -783,3 +807,2 @@ const initializingDiff = process.hrtime(this.timing.initStart); | ||
case 'initialized': | ||
this.timing.initEnd = process.hrtime(); | ||
@@ -802,3 +825,2 @@ const initializedDiff = process.hrtime(this.timing.initStart); | ||
case 'started': | ||
this.state = Flow.STATE_STARTED; | ||
@@ -857,3 +879,2 @@ this.emit('started'); | ||
case 'stop': | ||
this.forceStop(); | ||
@@ -863,3 +884,2 @@ break; | ||
case 'broadcastWebSocket': | ||
XIBLE.broadcastWebSocket(message.message); | ||
@@ -869,3 +889,2 @@ break; | ||
case 'usage': | ||
this.usage = message.usage; | ||
@@ -892,3 +911,3 @@ break; | ||
this.init() | ||
.catch((err) => { console.error(err); }); | ||
.catch(err => console.error(err)); | ||
} | ||
@@ -914,2 +933,4 @@ }); | ||
* @param {Node[]} directNodes nodes to direct | ||
* @fires Node#trigger | ||
* @fires Node#init | ||
* @return {Promise} | ||
@@ -954,3 +975,2 @@ */ | ||
case 'started': | ||
this.timing.startEnd = process.hrtime(); | ||
@@ -979,3 +999,3 @@ const startedDiff = process.hrtime(this.timing.startStart); | ||
process.nextTick(() => { | ||
// init all nodes | ||
// init all nodes | ||
for (let i = 0; i < this.nodes.length; i += 1) { | ||
@@ -985,3 +1005,3 @@ this.nodes[i].emit('init', flowState); | ||
// trigger all event objects that are listening | ||
// trigger all event objects that are listening | ||
for (let i = 0; i < this.nodes.length; i += 1) { | ||
@@ -1035,2 +1055,4 @@ if (this.nodes[i].type === 'event') { | ||
} | ||
return Promise.reject(new Error('Flow in unknown state')); | ||
} | ||
@@ -1044,72 +1066,71 @@ | ||
stop() { | ||
if (!XIBLE.child) { | ||
if (this.state !== Flow.STATE_STARTED && this.state !== Flow.STATE_INITIALIZED) { | ||
return Promise.reject('cannot stop; flow is not started or initialized'); | ||
} | ||
this.state = Flow.STATE_STOPPING; | ||
if (XIBLE.child) { | ||
flowDebug('stopping flow from worker'); | ||
return new Promise((resolve) => { | ||
this.saveStatus(false); | ||
// close any node that wants to | ||
this.nodes.forEach(node => node.emit('close')); | ||
if (this.worker) { | ||
XIBLE.broadcastWebSocket({ | ||
method: 'xible.flow.stopping', | ||
flowId: this._id | ||
}); | ||
flowDebug('stopped flow from worker'); | ||
process.exit(0); | ||
} | ||
flowDebug('stopping flow from master'); | ||
let killTimeout; | ||
if (this.state !== Flow.STATE_STARTED && this.state !== Flow.STATE_INITIALIZED) { | ||
return Promise.reject('cannot stop; flow is not started or initialized'); | ||
} | ||
this.state = Flow.STATE_STOPPING; | ||
this.worker.once('exit', () => { | ||
if (killTimeout) { | ||
clearTimeout(killTimeout); | ||
killTimeout = null; | ||
} | ||
return new Promise((resolve) => { | ||
this.saveStatus(false); | ||
resolve(this); | ||
}); | ||
if (this.worker) { | ||
XIBLE.broadcastWebSocket({ | ||
method: 'xible.flow.stopping', | ||
flowId: this._id | ||
}); | ||
this.worker.on('disconnect', () => { | ||
if (this.worker) { | ||
flowDebug('killing worker the normal way'); | ||
this.worker.kill(); | ||
} else if (killTimeout) { | ||
clearTimeout(killTimeout); | ||
killTimeout = null; | ||
} | ||
}); | ||
flowDebug('stopping flow from master'); | ||
let killTimeout; | ||
this.worker.send({ | ||
method: 'stop' | ||
}); | ||
this.worker.once('exit', () => { | ||
if (killTimeout) { | ||
clearTimeout(killTimeout); | ||
killTimeout = null; | ||
} | ||
this.worker.disconnect(); | ||
resolve(this); | ||
}); | ||
// forcibly kill after 5 seconds | ||
killTimeout = setTimeout(() => { | ||
flowDebug('killing worker from master using SIGKILL'); | ||
this.worker.kill('SIGKILL'); | ||
this.worker.on('disconnect', () => { | ||
if (this.worker) { | ||
flowDebug('killing worker the normal way'); | ||
this.worker.kill(); | ||
} else if (killTimeout) { | ||
clearTimeout(killTimeout); | ||
killTimeout = null; | ||
} | ||
}); | ||
// cleanup all open statuses | ||
XIBLE.broadcastWebSocket({ | ||
method: 'xible.flow.removeAllStatuses', | ||
flowId: this._id | ||
}); | ||
this.worker.send({ | ||
method: 'stop' | ||
}); | ||
killTimeout = null; | ||
}, 5000); | ||
} else { | ||
resolve(this); | ||
} | ||
}); | ||
} | ||
this.worker.disconnect(); | ||
flowDebug('stopping flow from worker'); | ||
// forcibly kill after 5 seconds | ||
killTimeout = setTimeout(() => { | ||
flowDebug('killing worker from master using SIGKILL'); | ||
this.worker.kill('SIGKILL'); | ||
// close any node that wants to | ||
this.nodes.forEach(node => node.emit('close')); | ||
// cleanup all open statuses | ||
XIBLE.broadcastWebSocket({ | ||
method: 'xible.flow.removeAllStatuses', | ||
flowId: this._id | ||
}); | ||
flowDebug('stopped flow from worker'); | ||
process.exit(0); | ||
killTimeout = null; | ||
}, 5000); | ||
} else { | ||
resolve(this); | ||
} | ||
}); | ||
} | ||
@@ -1132,3 +1153,3 @@ | ||
*/ | ||
this.set = function (node, obj) { | ||
this.set = function FlowStateSet(node, obj) { | ||
if (!(node instanceof XIBLE.Node)) { | ||
@@ -1149,3 +1170,3 @@ throw new Error('node must be instanceof Node'); | ||
*/ | ||
this.get = function (node) { | ||
this.get = function FlowStateGet(node) { | ||
if (!(node instanceof XIBLE.Node)) { | ||
@@ -1162,3 +1183,3 @@ throw new Error('node must be instanceof Node'); | ||
*/ | ||
this.split = function () { | ||
this.split = function FlowStateSplit() { | ||
return new FlowState(Object.assign({}, states)); | ||
@@ -1165,0 +1186,0 @@ }; |
@@ -61,10 +61,11 @@ 'use strict'; | ||
EXPRESS_APP.patch('/api/flows/:flowId/stop', (req, res) => { | ||
req.locals.flow.forceStop() | ||
.then(() => { | ||
res.end(); | ||
}) | ||
.catch((err) => { | ||
console.error(err); | ||
res.status(500).end(); | ||
}); | ||
req.locals.flow | ||
.forceStop() | ||
.then(() => { | ||
res.end(); | ||
}) | ||
.catch((err) => { | ||
console.error(err); | ||
res.status(500).end(); | ||
}); | ||
}); | ||
@@ -75,10 +76,11 @@ | ||
EXPRESS_APP.patch('/api/flows/:flowId/start', (req, res) => { | ||
req.locals.flow.forceStart() | ||
.then(() => { | ||
res.end(); | ||
}) | ||
.catch((err) => { | ||
console.error(err); | ||
res.status(500).end(); | ||
}); | ||
req.locals.flow | ||
.forceStart() | ||
.then(() => { | ||
res.end(); | ||
}) | ||
.catch((err) => { | ||
console.error(err); | ||
res.status(500).end(); | ||
}); | ||
}); | ||
@@ -131,3 +133,5 @@ | ||
const flow = req.locals.flow; | ||
flow.forceStop().then(() => { | ||
flow | ||
.forceStop() | ||
.then(() => { | ||
// init the newly provided json over the existing flow | ||
@@ -137,3 +141,5 @@ flow.initJson(req.body, true); | ||
// save it to file | ||
flow.save().then(() => { | ||
flow | ||
.save() | ||
.then(() => { | ||
// output the flow id | ||
@@ -151,8 +157,9 @@ res.json({ | ||
const flow = req.locals.flow; | ||
flow.forceStop().then(() => { | ||
flow.delete().then(() => { | ||
res.end(); | ||
}); | ||
flow | ||
.forceStop() | ||
.then(() => flow.delete()) | ||
.then(() => { | ||
res.end(); | ||
}); | ||
}); | ||
}; |
@@ -17,2 +17,17 @@ /* eslint-disable no-use-before-define */ | ||
/** | ||
* Trigger event which is applied to event nodes when the flow starts, | ||
* after all nodes have received the init event. | ||
* It is also applied to a node when it gets triggered through | ||
* an input trigger after a call from nodeOutput.trigger(). | ||
* @event Node#trigger | ||
* @param {FlowState} state A blank state, equal to the state provided on the init event. | ||
*/ | ||
/** | ||
* Init event which is applied to all nodes, when the flow starts. | ||
* @event Node#init | ||
* @param {FlowState} state The initial (blank) state of the flow. | ||
*/ | ||
/** | ||
* Node class | ||
@@ -58,5 +73,2 @@ * @extends EventEmitter | ||
this.vault = new NodeVault(this); | ||
// add vault data to the data field | ||
Object.assign(this.data, this.vault.get()); | ||
} | ||
@@ -690,2 +702,11 @@ | ||
/** | ||
* This event is emitted after a node calls trigger() on one of its outputs. | ||
* The event is fired for each of the input triggers connected to the output | ||
* where trigger() was called upon. | ||
* @event NodeInput#trigger | ||
* @param {Connector} conn The connector responsible for the trigger. | ||
* @param {FlowState} state The state provided from the calling node. | ||
*/ | ||
/** | ||
* Class for inputs of a Node. | ||
@@ -699,2 +720,3 @@ * @extends NodeIo | ||
* @param {FlowState} state The flowstate at the time of calling. | ||
* @fires NodeOutput#trigger | ||
* @returns {Promise.<Array>} An array of all values | ||
@@ -713,3 +735,5 @@ * as returned by the outputs on the other ends of connected connectors or globals. | ||
if (!conns.length && this.global) { | ||
conns = this.node.flow.getGlobalOutputsByType(this.type).map(output => ({ | ||
conns = this.node.flow | ||
.getGlobalOutputsByType(this.type) | ||
.map(output => ({ | ||
origin: output | ||
@@ -729,7 +753,10 @@ })); | ||
const conn = conns[i]; | ||
let calledBack = false; | ||
// trigger the input | ||
conn.origin.emit('trigger', conn, state, (value) => { // eslint-disable-line | ||
// let everyone know that the trigger is done | ||
conn.origin.emit('triggerdone'); | ||
// verify that this callback wasn't already made. | ||
if (calledBack) { | ||
throw new Error('Already called back'); | ||
} | ||
calledBack = true; | ||
@@ -759,2 +786,13 @@ // we only send arrays between nodes | ||
/** | ||
* This event is emitted after a node calls getValues() on one of its inputs. | ||
* The event is fired for each of the outputs connected to the input | ||
* where getValues() was called upon. | ||
* @event NodeOutput#trigger | ||
* @param {Connector} conn The connector responsible for the trigger. | ||
* @param {FlowState} state The state provided from the calling node. | ||
* @param {Function} callback Use the callback function to return your value. | ||
* You can only callback once, otherwise an Error is thrown. | ||
*/ | ||
/** | ||
* Class for outputs of a Node. | ||
@@ -768,2 +806,3 @@ * @extends NodeIo | ||
* @param {FlowState} state The flowstate at the time of calling. | ||
* @fires NodeInput#trigger | ||
* @throws {Error} Throws whenever this.type !== 'trigger'. | ||
@@ -777,3 +816,3 @@ */ | ||
this.node.emit('triggerout', this); | ||
// this.node.emit('triggerout', this); | ||
@@ -809,3 +848,5 @@ const conns = this.connectors; | ||
vaultDebug('creating new'); | ||
fs.writeFileSync(vaultPath, '{}'); | ||
fs.writeFileSync(vaultPath, '{}', { | ||
mode: 0o600 | ||
}); | ||
} | ||
@@ -816,3 +857,3 @@ | ||
} catch (err) { | ||
vaultDebug(`could not open "${vaultPath}"`); | ||
vaultDebug(`could not open "${vaultPath}"\n`, err); | ||
} | ||
@@ -823,5 +864,7 @@ } | ||
try { | ||
fs.writeFileSync(vaultPath, JSON.stringify(vault)); | ||
} catch (e) { | ||
vaultDebug(`could not save "${vaultPath}"`); | ||
fs.writeFileSync(vaultPath, JSON.stringify(vault), { | ||
mode: 0o600 | ||
}); | ||
} catch (err) { | ||
vaultDebug(`could not save "${vaultPath}"\n`, err); | ||
} | ||
@@ -828,0 +871,0 @@ } |
@@ -140,17 +140,17 @@ 'use strict'; | ||
const onSuccess = () => XIBLE.Node | ||
.initFromPath(nodeDestDir) | ||
.then(cleanUp) | ||
.then(() => { | ||
// see if we can/need to reinit flows that are not runnable | ||
const flows = XIBLE.getFlows(); | ||
for (const flowId in flows) { | ||
if (!flows[flowId].runnable) { | ||
flows[flowId].initJson(flows[flowId].json); | ||
} | ||
.initFromPath(nodeDestDir) | ||
.then(cleanUp) | ||
.then(() => { | ||
// see if we can/need to reinit flows that are not runnable | ||
const flows = XIBLE.getFlows(); | ||
for (const flowId in flows) { | ||
if (!flows[flowId].runnable) { | ||
flows[flowId].initJson(flows[flowId].json); | ||
} | ||
}) | ||
.then(resolve) | ||
.catch((onSuccessErr) => { | ||
reject(onSuccessErr); | ||
}); | ||
} | ||
}) | ||
.then(resolve) | ||
.catch((onSuccessErr) => { | ||
reject(onSuccessErr); | ||
}); | ||
@@ -157,0 +157,0 @@ // remove existing node directory |
@@ -9,16 +9,3 @@ 'use strict'; | ||
XIBLE_REGISTRY.NodePack | ||
.getAll() | ||
.then((nodePacks) => { | ||
res.json(nodePacks); | ||
}) | ||
.catch((err) => { | ||
console.error(err); | ||
res.status(500).end(); | ||
}); | ||
return; | ||
} | ||
XIBLE_REGISTRY.NodePack | ||
.search(searchString) | ||
.getAll() | ||
.then((nodePacks) => { | ||
@@ -31,2 +18,15 @@ res.json(nodePacks); | ||
}); | ||
return; | ||
} | ||
XIBLE_REGISTRY.NodePack | ||
.search(searchString) | ||
.then((nodePacks) => { | ||
res.json(nodePacks); | ||
}) | ||
.catch((err) => { | ||
console.error(err); | ||
res.status(500).end(); | ||
}); | ||
}); | ||
@@ -39,8 +39,8 @@ | ||
XIBLE_REGISTRY.NodePack | ||
.getByName(nodePackName) | ||
.then((nodePack) => { | ||
req.locals.nodePack = nodePack; | ||
next(); | ||
}) | ||
.catch(() => res.status(404).end()); | ||
.getByName(nodePackName) | ||
.then((nodePack) => { | ||
req.locals.nodePack = nodePack; | ||
next(); | ||
}) | ||
.catch(() => res.status(404).end()); | ||
}); | ||
@@ -54,11 +54,12 @@ | ||
EXPRESS_APP.patch('/api/registry/nodepacks/:nodePackName/install', (req, res) => { | ||
req.locals.nodePack.install() | ||
.then(() => { | ||
res.end(); | ||
}) | ||
.catch((err) => { | ||
console.error(err); | ||
res.status(500).end(); | ||
}); | ||
req.locals.nodePack | ||
.install() | ||
.then(() => { | ||
res.end(); | ||
}) | ||
.catch((err) => { | ||
console.error(err); | ||
res.status(500).end(); | ||
}); | ||
}); | ||
}; |
# Change Log | ||
All notable changes to the XIBLE project will be documented in this file. | ||
All notable changes to the [XIBLE project](https://xible.io) will be documented in this file. | ||
@@ -10,2 +10,26 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/) | ||
## [0.7.0][] | ||
### Added | ||
- Flows can be [started](https://xible.io/docs/commandlinetools/xible#flow.start)/[stopped](https://xible.io/docs/commandlinetools/xible#flow.stop) and [deleted](https://xible.io/docs/commandlinetools/xible#flow.delete) using the cli. See the [xible cli documentation](https://xible.io/docs/commandlinetools/xible) for more information. ([#18](https://github.com/SpectrumBroad/xible/issues/18)) | ||
- The `xible` command line interface supports the 'config' context just like `xiblepm`. ([#22](https://github.com/SpectrumBroad/xible/issues/22)) | ||
- `console.log`, `console.error` and `console.clear` nodes are now included in XIBLE. `log.console` is deprecated and will be removed in a future release. ([#27](https://github.com/SpectrumBroad/xible/issues/27)) | ||
### Changed | ||
- `xiblepm flow remove` is deprecated in favor of `xible flow delete`. | ||
- The [command line interface tools](https://xible.io/docs/commandlinetools) `xible` & `xiblepm` are now stored in `./bin`. | ||
- The `document.assign` node only callbacks once when multiple documents are hooked up to the 'document' input. | ||
- Callbacks from nodes in response to `output.on('trigger')` resulting from a `input.getValues()` call can now only be called back once. An error is thrown if called multiple times. | ||
- [Direct mode](https://xible.io/docs/editor#direct) can now be enabled/disabled through the settings. It is disabled by default. ([#26](https://github.com/SpectrumBroad/xible/issues/26)) | ||
### Fixed | ||
- Resizing the editor to only narrowly fit the cpu/mem/delay charts no longer toggles the scrollbar in- and out of view. ([#11](https://github.com/SpectrumBroad/xible/issues/11)) | ||
- Publishing flows to the registry no longer includes the vault data ([#25](https://github.com/SpectrumBroad/xible/issues/25)) | ||
## [0.6.0][] - 2017-06-09 | ||
@@ -61,3 +85,4 @@ ### Added | ||
[Unreleased]: https://github.com/SpectrumBroad/xible/compare/v0.6.0...HEAD | ||
[Unreleased]: https://github.com/SpectrumBroad/xible/compare/v0.7.0...HEAD | ||
[0.7.0]: https://github.com/SpectrumBroad/xible/compare/v0.6.0...v0.7.0 | ||
[0.6.0]: https://github.com/SpectrumBroad/xible/compare/v0.5.0...v0.6.0 | ||
@@ -64,0 +89,0 @@ [0.5.0]: https://github.com/SpectrumBroad/xible/compare/v0.4.1...v0.5.0 |
88
child.js
@@ -55,3 +55,2 @@ 'use strict'; | ||
case 'init': | ||
xible = new Xible({ | ||
@@ -83,27 +82,27 @@ child: true | ||
xible.init() | ||
.then(() => { | ||
flow = new xible.Flow(); | ||
flow.initJson(message.flow); | ||
.then(() => { | ||
flow = new xible.Flow(); | ||
flow.initJson(message.flow); | ||
// inform the master that we initialized | ||
if (process.connected) { | ||
process.send({ | ||
method: 'initialized' | ||
}); | ||
} | ||
}) | ||
.catch((err) => { | ||
console.error(err); | ||
// inform the master that we initialized | ||
if (process.connected) { | ||
process.send({ | ||
method: 'initialized' | ||
}); | ||
} | ||
}) | ||
.catch((err) => { | ||
console.error(err); | ||
if (process.connected) { | ||
process.send({ | ||
method: 'stop', | ||
error: err | ||
}); | ||
} | ||
if (process.connected) { | ||
process.send({ | ||
method: 'stop', | ||
error: err | ||
}); | ||
} | ||
if (flow) { | ||
flow.stop(); | ||
} | ||
}); | ||
if (flow) { | ||
flow.stop(); | ||
} | ||
}); | ||
@@ -113,3 +112,2 @@ break; | ||
case 'start': | ||
let startPromise; | ||
@@ -123,24 +121,24 @@ if (message.directNodes) { | ||
startPromise | ||
.then(() => { | ||
// inform the master that we actually started | ||
if (process.connected) { | ||
process.send({ | ||
method: 'started' | ||
}); | ||
} | ||
}) | ||
.catch((err) => { | ||
console.error(err); | ||
.then(() => { | ||
// inform the master that we actually started | ||
if (process.connected) { | ||
process.send({ | ||
method: 'started' | ||
}); | ||
} | ||
}) | ||
.catch((err) => { | ||
console.error(err); | ||
if (process.connected) { | ||
process.send({ | ||
method: 'stop', | ||
error: err | ||
}); | ||
} | ||
if (process.connected) { | ||
process.send({ | ||
method: 'stop', | ||
error: err | ||
}); | ||
} | ||
if (flow) { | ||
flow.stop(); | ||
} | ||
}); | ||
if (flow) { | ||
flow.stop(); | ||
} | ||
}); | ||
@@ -150,3 +148,2 @@ break; | ||
case 'stop': | ||
if (flow) { | ||
@@ -159,3 +156,2 @@ flow.stop(); | ||
case 'directNodes': | ||
if (flow) { | ||
@@ -162,0 +158,0 @@ flow.direct(message.directNodes); |
@@ -23,2 +23,16 @@ { | ||
"editor": { | ||
"nodes": { | ||
"statuses": { | ||
"max": null | ||
} | ||
}, | ||
"flows": { | ||
"allowdirect": false | ||
}, | ||
"viewstate": { | ||
"zoomstateonopen": "untouched" | ||
} | ||
}, | ||
"registry": { | ||
@@ -25,0 +39,0 @@ "url": "https://registry.xible.io", |
class XibleEditorNode extends xibleWrapper.Node { | ||
constructor(obj, ignoreData) { | ||
let el = document.createElement('div'); | ||
@@ -10,11 +9,11 @@ el.classList.add('node'); | ||
//add ios | ||
// add ios | ||
let ios = el.appendChild(document.createElement('div')); | ||
ios.classList.add('io'); | ||
//add input list | ||
// add input list | ||
let inputList = ios.appendChild(document.createElement('ul')); | ||
inputList.classList.add('input'); | ||
//add output list | ||
// add output list | ||
let outputList = ios.appendChild(document.createElement('ul')); | ||
@@ -31,4 +30,4 @@ outputList.classList.add('output'); | ||
//add additional content | ||
if (this.hostsEditorContent) { //load editor static hosted content for this node | ||
// add additional content | ||
if (this.hostsEditorContent) { // load editor static hosted content for this node | ||
this.getAndProcessEditorContent(); | ||
@@ -42,3 +41,3 @@ } else if (!this.nodeExists && obj.editorContent) { | ||
//selection handlers | ||
// selection handlers | ||
this.element.addEventListener('mousedown', (event) => { | ||
@@ -55,5 +54,4 @@ if (this.editor) { | ||
//direct handler | ||
// direct handler | ||
headerEl.addEventListener('dblclick', (event) => { | ||
if (!this.editor || this.type !== 'action' || !this.editor.browserSupport) { | ||
@@ -63,18 +61,24 @@ return; | ||
this.flow.undirect(); | ||
// check if direct mode is alowed before continuing | ||
xibleWrapper.Config | ||
.getValue('editor.flows.allowdirect') | ||
.then((allowDirect) => { | ||
if(!allowDirect) { | ||
return; | ||
} | ||
//fetch all related connectors and nodes for the double clicked node | ||
let related = XibleEditorNode.getAllInputObjectNodes(this); | ||
this.flow.undirect(); | ||
//don't forget about globals | ||
related.nodes = related.nodes.concat(this.flow.getGlobalNodes()); | ||
// fetch all related connectors and nodes for the double clicked node | ||
let related = XibleEditorNode.getAllInputObjectNodes(this); | ||
related.nodes.forEach((node) => { | ||
// don't forget about globals | ||
related.nodes = related.nodes.concat(this.flow.getGlobalNodes()); | ||
node._directSetDataListener = () => this.editor.loadedFlow.direct(related); | ||
node.on('setdata', node._directSetDataListener); | ||
related.nodes.forEach((node) => { | ||
node._directSetDataListener = () => this.editor.loadedFlow.direct(related); | ||
node.on('setdata', node._directSetDataListener); | ||
}); | ||
}); | ||
this.editor.loadedFlow.nodes | ||
this.editor.loadedFlow.nodes | ||
.filter((node) => related.nodes.indexOf(node) === -1) | ||
@@ -85,3 +89,3 @@ .forEach((node) => { | ||
this.editor.loadedFlow.connectors | ||
this.editor.loadedFlow.connectors | ||
.filter((connector) => related.connectors.indexOf(connector) === -1) | ||
@@ -92,8 +96,7 @@ .forEach((connector) => { | ||
this.editor.loadedFlow.direct(related); | ||
this.editor.loadedFlow.direct(related); | ||
}); | ||
}); | ||
if (!obj.nodeExists) { | ||
this.element.classList.add('fail'); | ||
@@ -105,5 +108,3 @@ this.addStatus({ | ||
}); | ||
} | ||
} | ||
@@ -338,3 +339,2 @@ | ||
updateProgressBarById(statusId, status) { | ||
if (!this.statusEl || !statusId || !status || typeof status.percentage !== 'number') { | ||
@@ -344,6 +344,5 @@ return; | ||
let li = this.statusEl.querySelector('li.bar[data-statusid="' + statusId + '"]'); | ||
const li = this.statusEl.querySelector('li.bar[data-statusid="' + statusId + '"]'); | ||
if (li) { | ||
let bar = li.querySelector('.holder>div'); | ||
const bar = li.querySelector('.holder>div'); | ||
bar.style.transition = 'none'; | ||
@@ -353,8 +352,7 @@ bar.style.width = `${status.percentage}%`; | ||
if (status.updateOverTime) { | ||
//check when this progressbar should start (future) | ||
//or when it started (past) | ||
// check when this progressbar should start (future) | ||
// or when it started (past) | ||
let startDiff = Date.now() - status.startDate + this.editor.serverClientDateDifference; | ||
//max it out | ||
// max it out | ||
if (startDiff > status.updateOverTime) { | ||
@@ -364,4 +362,4 @@ startDiff = status.updateOverTime; | ||
//if this progressbar should have started in the past | ||
//calculate where the width should be right now | ||
// if this progressbar should have started in the past | ||
// calculate where the width should be right now | ||
if (startDiff > 0) { | ||
@@ -374,6 +372,4 @@ bar.style.width = `${startDiff/status.updateOverTime*100}%`; | ||
bar.style.width = '100%'; | ||
} | ||
} | ||
} | ||
@@ -387,54 +383,53 @@ | ||
xibleWrapper.Config | ||
.getValue('editor.nodes.statuses.max') | ||
.then((configMaxStatuses) => { | ||
let statusCount = 0; | ||
let ul = this.statusEl; | ||
if (!ul) { | ||
ul = this.statusEl = this.element.appendChild(document.createElement('ul')); | ||
ul.classList.add('statuses'); | ||
} else { | ||
statusCount = ul.querySelectorAll('li:not(.bar)').length; | ||
} | ||
.getValue('editor.nodes.statuses.max') | ||
.then((configMaxStatuses) => { | ||
let statusCount = 0; | ||
let ul = this.statusEl; | ||
if (!ul) { | ||
ul = this.statusEl = this.element.appendChild(document.createElement('ul')); | ||
ul.classList.add('statuses'); | ||
} else { | ||
statusCount = ul.querySelectorAll('li:not(.bar)').length; | ||
} | ||
// remove all statuses above the max config setting | ||
if (typeof configMaxStatuses === 'number' && statusCount >= configMaxStatuses && ul.firstChild) { | ||
while (statusCount >= configMaxStatuses && ul.firstChild) { | ||
const removeChild = ul.firstChild; | ||
this.removeStatusById(removeChild.getAttribute('data-statusid')); | ||
statusCount -= 1; | ||
} | ||
// remove all statuses above the max config setting | ||
if (typeof configMaxStatuses === 'number' && statusCount >= configMaxStatuses && ul.firstChild) { | ||
while (statusCount >= configMaxStatuses && ul.firstChild) { | ||
const removeChild = ul.firstChild; | ||
this.removeStatusById(removeChild.getAttribute('data-statusid')); | ||
statusCount -= 1; | ||
} | ||
} | ||
if (configMaxStatuses === 0) { | ||
return; | ||
} | ||
if (configMaxStatuses === 0) { | ||
return; | ||
} | ||
let li = ul.appendChild(document.createElement('li')); | ||
li.setAttribute('data-statusid', status._id); | ||
const li = ul.appendChild(document.createElement('li')); | ||
li.setAttribute('data-statusid', status._id); | ||
if (status.color) { | ||
li.classList.add(status.color); | ||
} | ||
if (status.color) { | ||
li.classList.add(status.color); | ||
} | ||
if (typeof status.message === 'string') { | ||
let messageLineSplit = status.message.split('\n'); | ||
for (let i = 0; i < messageLineSplit.length; i += 1) { | ||
if (i) { | ||
li.appendChild(document.createElement('br')); | ||
} | ||
li.appendChild(document.createTextNode(messageLineSplit[i])); | ||
if (typeof status.message === 'string') { | ||
let messageLineSplit = status.message.split('\n'); | ||
for (let i = 0; i < messageLineSplit.length; i += 1) { | ||
if (i) { | ||
li.appendChild(document.createElement('br')); | ||
} | ||
messageLineSplit = null; | ||
li.appendChild(document.createTextNode(messageLineSplit[i])); | ||
} | ||
messageLineSplit = null; | ||
} | ||
if (status.timeout) { | ||
this.statusTimeouts[status._id] = window.setTimeout(() => { | ||
this.removeStatusById(status._id); | ||
}, status.timeout); | ||
} | ||
}); | ||
if (status.timeout) { | ||
this.statusTimeouts[status._id] = window.setTimeout(() => { | ||
this.removeStatusById(status._id); | ||
}, status.timeout); | ||
} | ||
}); | ||
} | ||
updateStatusById(statusId, status) { | ||
if (!this.statusEl) { | ||
@@ -444,7 +439,5 @@ return; | ||
let li = this.statusEl.querySelector(`li[data-statusid="${statusId}"]`); | ||
const li = this.statusEl.querySelector(`li[data-statusid="${statusId}"]`); | ||
if (li) { | ||
if (status.message) { | ||
if (li.lastChild) { | ||
@@ -455,18 +448,12 @@ li.removeChild(li.lastChild); | ||
li.appendChild(document.createTextNode(status.message)); | ||
} | ||
} | ||
} | ||
removeStatusById(statusId, timeout) { | ||
//clear timeout | ||
if (this.statusTimeouts[statusId]) { | ||
window.clearTimeout(this.statusTimeouts[statusId]); | ||
this.statusTimeouts[statusId] = null; | ||
delete this.statusTimeouts[statusId]; | ||
} | ||
@@ -476,7 +463,5 @@ | ||
if (this.statusEl) { | ||
let li = this.statusEl.querySelector(`li[data-statusid="${statusId}"]`); | ||
const li = this.statusEl.querySelector(`li[data-statusid="${statusId}"]`); | ||
if (li) { | ||
let fn = () => { | ||
const fn = () => { | ||
if (this.statusEl) { | ||
@@ -492,11 +477,7 @@ this.statusEl.removeChild(li); | ||
} | ||
} | ||
} | ||
} | ||
removeAllStatuses() { | ||
//clear all timeouts | ||
@@ -517,3 +498,2 @@ let statusId; | ||
} | ||
} | ||
@@ -520,0 +500,0 @@ |
@@ -18,3 +18,3 @@ View.routes['/editor'] = function(EL) { | ||
</p> | ||
<section class="buttons"> | ||
<section class="buttons editor"> | ||
<button type="button" id="xibleFlowDeployButton">Deploy</button> | ||
@@ -26,17 +26,21 @@ <button type="button" id="xibleFlowStartButton">Start</button> | ||
</section> | ||
<section id="console"> | ||
<div class="stats"> | ||
<section class="stats"> | ||
<div> | ||
<canvas id="cpuChart"></canvas> | ||
<label id="cpu">cpu</label> | ||
</div> | ||
<label id="cpu">cpu</label> | ||
</section> | ||
<section class="stats"> | ||
<div> | ||
<canvas id="memChart"></canvas> | ||
<label id="rss">rss</label> | ||
<label id="heapTotal">heap total</label> | ||
<label id="heapUsed">heap used</label> | ||
</div> | ||
<label id="rss">rss</label> | ||
<label id="heapTotal">heap total</label> | ||
<label id="heapUsed">heap used</label> | ||
</section> | ||
<section class="stats"> | ||
<div> | ||
<canvas id="delayChart"></canvas> | ||
<label id="delay">event loop delay</label> | ||
</div> | ||
<label id="delay">event loop delay</label> | ||
</section> | ||
@@ -159,2 +163,4 @@ </div> | ||
options: { | ||
responsive: true, | ||
maintainAspectRatio: false, | ||
legend: { | ||
@@ -225,2 +231,4 @@ display: false | ||
options: { | ||
responsive: true, | ||
maintainAspectRatio: false, | ||
legend: { | ||
@@ -275,2 +283,4 @@ display: false | ||
options: { | ||
responsive: true, | ||
maintainAspectRatio: false, | ||
legend: { | ||
@@ -277,0 +287,0 @@ display: false |
@@ -8,3 +8,3 @@ View.routes['/nodes'] = function(EL) { | ||
<ul> | ||
<li><a href="/test">blaat</a></li> | ||
<li><a href="/test"></a></li> | ||
</ul> | ||
@@ -11,0 +11,0 @@ </section> |
@@ -20,2 +20,3 @@ View.routes['/settings'] = function(EL) { | ||
<li><a href="/settings/editor#nodes" onclick="settingsViewHolder.navigate('/settings/editor#nodes'); return false;">Nodes</a></li> | ||
<li><a href="/settings/editor#flows" onclick="settingsViewHolder.navigate('/settings/editor#flows'); return false;">Flows</a></li> | ||
<li><a href="/settings/editor#viewstate" onclick="settingsViewHolder.navigate('/settings/editor#viewstate'); return false;">Viewstate</a></li> | ||
@@ -22,0 +23,0 @@ </ul> |
@@ -22,2 +22,23 @@ View.routes['/settings/editor'] = function(EL) { | ||
<section id="flows"> | ||
<h2>Flows</h2> | ||
<p class="warning"> | ||
Running a flow in direct mode can have undesired side-effects. Take special note of the <a href="https://xible.io/docs/editor#direct" target="_blank" rel="noopener">online documentation</a>. | ||
</p> | ||
<dl> | ||
<dd class="checkbox"> | ||
<label for="settingsEditorFlowsAllowDirect"> | ||
<input type="checkbox" value="true" id="settingsEditorFlowsAllowDirect" data-configpath="editor.flows.allowdirect" /> | ||
<span></span> | ||
</label> | ||
</dd> | ||
<dt class="checkbox"> | ||
<label for="settingsEditorFlowsAllowDirect"> | ||
Allow direct mode | ||
<div>Enable or disable the ability to run part of a flow directly.</div> | ||
</label> | ||
</dt> | ||
</dl> | ||
</section> | ||
<section id="viewstate"> | ||
@@ -24,0 +45,0 @@ <h2>Viewstate</h2> |
79
index.js
@@ -5,2 +5,3 @@ 'use strict'; | ||
const os = require('os'); | ||
const fs = require('fs'); | ||
const debug = require('debug'); | ||
@@ -63,3 +64,3 @@ | ||
this.persistentWebSocketMessages = {}; | ||
appNames = ['Config', 'Flow', 'Node', 'Registry']; | ||
appNames = ['Config', 'CliQueue', 'Flow', 'Node', 'Registry']; | ||
} | ||
@@ -72,2 +73,31 @@ | ||
/** | ||
* Creates a PID file in path `${this.configPath}.pid` | ||
*/ | ||
writePidFile() { | ||
if (!this.configPath) { | ||
throw new Error('Cannot write PID file, configPath not set.'); | ||
} | ||
fs.writeFile(`${this.configPath}.pid`, process.pid, { | ||
mode: 0o600 | ||
}, (err) => { | ||
if (err) { | ||
throw err; | ||
} | ||
xibleDebug('PID file created'); | ||
}); | ||
} | ||
/** | ||
* Removes the PID file from path `${this.configPath}.pid` | ||
* This is a sync action as it can be called on process.exit | ||
*/ | ||
removePidFile() { | ||
if (!this.configPath) { | ||
throw new Error('Cannot remove PID file, configPath not set.'); | ||
} | ||
fs.unlinkSync(`${this.configPath}.pid`); | ||
xibleDebug('PID file removed'); | ||
} | ||
// load nodes and flows | ||
@@ -89,2 +119,17 @@ init(obj) { | ||
// write PID file | ||
this.writePidFile(); | ||
this.CliQueue.init(); | ||
process.on('SIGINT', () => { | ||
process.exit(1); | ||
}); | ||
process.on('SIGTERM', () => { | ||
process.exit(1); | ||
}); | ||
process.on('exit', () => { | ||
this.CliQueue.removeFile(); | ||
this.removePidFile(); | ||
xibleDebug('exit'); | ||
}); | ||
this.startWeb(); | ||
@@ -203,4 +248,4 @@ | ||
return Math.floor((1 + Math.random()) * 0x10000) | ||
.toString(16) | ||
.substring(1); | ||
.toString(16) | ||
.substring(1); | ||
} | ||
@@ -219,4 +264,2 @@ return `${s4() + s4()}-${s4()}-${s4()}-${s4()}-${s4()}${s4()}${s4()}`; | ||
const spdy = require('spdy'); | ||
const fs = require('fs'); | ||
const expressApp = this.expressApp; | ||
@@ -256,3 +299,4 @@ | ||
const webSocketServer = new ws.Server({ | ||
server: webServer | ||
server: webServer, | ||
ssl: this.secure | ||
}); | ||
@@ -366,3 +410,2 @@ this.webSocketServer = webSocketServer; | ||
case 'xible.node.addProgressBar': | ||
this.setPersistentWebSocketMessage(message); | ||
@@ -379,3 +422,2 @@ | ||
case 'xible.node.updateProgressBarById': | ||
copyMessage = Object.assign({}, message); | ||
@@ -388,3 +430,2 @@ copyMessage.method = 'xible.node.addProgressBar'; | ||
case 'xible.node.updateStatusById': | ||
copyMessage = Object.assign({}, message); | ||
@@ -479,24 +520,4 @@ copyMessage.method = 'xible.node.addStatus'; | ||
deleteFlow(flow, callback) { | ||
if (flow._id) { | ||
delete this.flows[flow._id]; | ||
} | ||
if (typeof callback === 'function') { | ||
callback(); | ||
} | ||
} | ||
deleteFlowById(id, callback) { | ||
if (id) { | ||
delete this.flows[id]; | ||
} | ||
if (typeof callback === 'function') { | ||
callback(); | ||
} | ||
} | ||
} | ||
module.exports = Xible; |
@@ -1,38 +0,29 @@ | ||
module.exports = function(NODE) { | ||
'use strict'; | ||
let docIn = NODE.getInputByName('document'); | ||
let variableIn = NODE.getInputByName('variable'); | ||
module.exports = (NODE) => { | ||
const docIn = NODE.getInputByName('document'); | ||
const variableIn = NODE.getInputByName('variable'); | ||
let docOut = NODE.getOutputByName('document'); | ||
const docOut = NODE.getOutputByName('document'); | ||
docOut.on('trigger', (conn, state, callback) => { | ||
Promise.all([docIn.getValues(state), variableIn.getValues(state)]).then(([docs, variables]) => { | ||
const assignedDocs = docs.map((doc) => { | ||
// copy the document | ||
// FIXME: should merge deep | ||
const copyDoc = Object.assign({}, doc); | ||
docOut.on('trigger', (conn, state, callback) => { | ||
// add/overwrite the new vars | ||
variables.forEach((variable) => { | ||
let val = variable.values; | ||
if (val.length === 1) { | ||
val = val[0]; | ||
} | ||
Promise.all([docIn.getValues(state), variableIn.getValues(state)]).then(([docs, variables]) => { | ||
docs.forEach((doc) => { | ||
//copy the document | ||
//FIXME: should merge deep | ||
doc = Object.assign({}, doc); | ||
//add/overwrite the new vars | ||
variables.forEach((variable) => { | ||
let val = variable.values; | ||
if (val.length === 1) { | ||
val = val[0]; | ||
} | ||
doc[variable.name] = val; | ||
}); | ||
callback(doc); | ||
}); | ||
}); | ||
}); | ||
copyDoc[variable.name] = val; | ||
}); | ||
return copyDoc; | ||
}); | ||
callback(assignedDocs); | ||
}); | ||
}); | ||
}; |
{ | ||
"name": "document.assign", | ||
"type": "object", | ||
"description": "Assigns new key/value pairs to an existing document.", | ||
"description": "Assigns new key/value pairs to an existing document. Returns a new document, does not update the existing document.", | ||
"inputs": { | ||
"document": { | ||
"type": "document" | ||
}, | ||
"type": "document" | ||
}, | ||
"variable": { | ||
"type": "variable" | ||
} | ||
"type": "variable" | ||
} | ||
}, | ||
"outputs": { | ||
"document": { | ||
"type": "document" | ||
} | ||
"type": "document" | ||
} | ||
} | ||
} |
@@ -1,15 +0,13 @@ | ||
module.exports = function(NODE) { | ||
'use strict'; | ||
let docIn = NODE.getInputByName('document'); | ||
module.exports = (NODE) => { | ||
const docIn = NODE.getInputByName('document'); | ||
let stringOut = NODE.getOutputByName('json'); | ||
stringOut.on('trigger', (conn, state, callback) => { | ||
docIn.getValues(state).then((docs) => { | ||
callback(JSON.stringify(docs)); | ||
}); | ||
}); | ||
const stringOut = NODE.getOutputByName('json'); | ||
stringOut.on('trigger', (conn, state, callback) => { | ||
docIn.getValues(state) | ||
.then((docs) => { | ||
callback(JSON.stringify(docs)); | ||
}); | ||
}); | ||
}; |
@@ -7,10 +7,10 @@ { | ||
"document": { | ||
"type": "document" | ||
} | ||
"type": "document" | ||
} | ||
}, | ||
"outputs": { | ||
"json": { | ||
"type": "string" | ||
} | ||
"type": "string" | ||
} | ||
} | ||
} |
@@ -1,29 +0,22 @@ | ||
module.exports = function(NODE) { | ||
'use strict'; | ||
let variableIn = NODE.getInputByName('variable'); | ||
module.exports = (NODE) => { | ||
const variableIn = NODE.getInputByName('variable'); | ||
let docOut = NODE.getOutputByName('document'); | ||
const docOut = NODE.getOutputByName('document'); | ||
docOut.on('trigger', (conn, state, callback) => { | ||
variableIn.getValues(state).then((variables) => { | ||
const doc = {}; | ||
variables.forEach((variable) => { | ||
let val = variable.values; | ||
if (val.length === 1) { | ||
val = val[0]; | ||
} | ||
docOut.on('trigger', (conn, state, callback) => { | ||
doc[variable.name] = val; | ||
}); | ||
variableIn.getValues(state).then((variables) => { | ||
let doc = {}; | ||
variables.forEach((variable) => { | ||
let val = variable.values; | ||
if (val.length === 1) { | ||
val = val[0]; | ||
} | ||
doc[variable.name] = val; | ||
}); | ||
callback(doc); | ||
}); | ||
}); | ||
callback(doc); | ||
}); | ||
}); | ||
}; |
@@ -7,10 +7,10 @@ { | ||
"variable": { | ||
"type": "variable" | ||
} | ||
"type": "variable" | ||
} | ||
}, | ||
"outputs": { | ||
"document": { | ||
"type": "document" | ||
} | ||
"type": "document" | ||
} | ||
} | ||
} |
'use strict'; | ||
function getFlow(flowId, callback) { | ||
let messageHandler = (message) => { | ||
if (message.flowId !== flowId || message.method !== 'returnFlow') { | ||
return; | ||
} | ||
process.removeListener('message', messageHandler); | ||
messageHandler = null; | ||
callback(message.flow); | ||
}; | ||
process.on('message', messageHandler); | ||
process.send({ | ||
method: 'getFlowById', | ||
flowId | ||
}); | ||
} | ||
module.exports = (NODE) => { | ||
@@ -7,20 +26,15 @@ const flowOut = NODE.getOutputByName('flow'); | ||
const flowId = NODE.data.flowName || NODE.flow.name; | ||
getFlow(flowId, callback); | ||
}); | ||
let messageHandler = (message) => { | ||
if (message.flowId !== flowId || message.method !== 'returnFlow') { | ||
const timingOut = NODE.getOutputByName('timing'); | ||
timingOut.on('trigger', (conn, state, callback) => { | ||
const flowId = NODE.data.flowName || NODE.flow.name; | ||
getFlow(flowId, (flow) => { | ||
if (!flow) { | ||
return; | ||
} | ||
process.removeListener('message', messageHandler); | ||
messageHandler = null; | ||
callback(message.flow); | ||
}; | ||
process.on('message', messageHandler); | ||
process.send({ | ||
method: 'getFlowById', | ||
flowId | ||
callback(flow.timing); | ||
}); | ||
}); | ||
}; |
{ | ||
"name": "xible.flow", | ||
"type": "object", | ||
"description": "Defines the current flow.", | ||
"description": "Defines a flow.", | ||
"outputs": { | ||
"flow": { | ||
"type": "xible.flow" | ||
}, | ||
"timing": { | ||
"type": "document", | ||
"description": "Returns the timing object on the flow." | ||
} | ||
} | ||
} |
{ | ||
"name": "xible", | ||
"version": "0.6.0", | ||
"version": "0.7.0", | ||
"description": "xible", | ||
@@ -21,16 +21,25 @@ "license": "MIT", | ||
"visual", | ||
"flow", | ||
"workflow", | ||
"connector", | ||
"string", | ||
"wire", | ||
"programming", | ||
"node" | ||
"automation", | ||
"automate", | ||
"flexible", | ||
"node", | ||
"nodejs", | ||
"javascript" | ||
], | ||
"scripts": { | ||
"start": "node cli.js server start", | ||
"debug": "export DEBUG=\"xible*\" && ./node_modules/.bin/nodemon cli.js server start", | ||
"inspect": "export DEBUG=\"xible*\" && ./node_modules/.bin/nodemon --inspect cli.js server start", | ||
"debug": "export DEBUG=\"xible*\" && ./node_modules/.bin/nodemon ./bin/xible.js server start", | ||
"inspect": "export DEBUG=\"xible*\" && ./node_modules/.bin/nodemon --inspect=0.0.0.0:9229 ./bin/xible.js server start", | ||
"inspect-brk": "export DEBUG=\"xible*\" && ./node_modules/.bin/nodemon --inspect-brk=0.0.0.0:9229 ./bin/xible.js server start", | ||
"prepublish": "npm shrinkwrap --only=prod" | ||
}, | ||
"bin": { | ||
"xible": "./cli.js", | ||
"xiblepm": "./pmcli.js" | ||
"xible": "./bin/xible.js", | ||
"xiblepm": "./bin/xiblepm.js" | ||
}, | ||
@@ -51,3 +60,4 @@ "main": "index.js", | ||
"sanitize-filename": "*", | ||
"fs-extra": "*" | ||
"fs-extra": "*", | ||
"strip-ansi": "*" | ||
}, | ||
@@ -54,0 +64,0 @@ "devDependencies": { |
@@ -5,5 +5,8 @@ # XIBLE | ||
## Installation | ||
Once you have Node.js installed, simply run; | ||
1. `sudo npm install -g xible` | ||
See the [installation documentation](https://xible.io/docs/installation) for details. | ||
Once you have Node.js installed; | ||
- On Linux run: `sudo npm install xible -g` | ||
- On Windows run: `npm install xible -g` | ||
## Start XIBLE | ||
@@ -20,4 +23,4 @@ After installation is completed; | ||
## Browser support | ||
The browser based graphical editor which comes with XIBLE is currently only supported by the Google Chrome browser; versions 53 and up. Opera, which is based on the same engine as Chrome, is also supported starting from version 44. | ||
The browser based [graphical editor](http://xible.io/docs/editor/) which comes with XIBLE is currently only supported by the Google Chrome browser; versions 53 and up. Opera, which is based on the same engine as Chrome, is also supported starting from version 44. | ||
Other browsers may work, but some editor functionality is unavailable if Shadow DOM v1 is not implemented. It will be possible to start, stop and delete flows, but deploying and saving is disabled. Also, the detail settings of nodes are not visible. | ||
Other browsers may work, but some editor functionality is unavailable if [Shadow DOM v1](http://caniuse.com/#search=shadow%20dom%20v1) is not implemented. It will be possible to start, stop and delete flows, but deploying and saving is disabled. Also, the detail settings of nodes are not visible. |
@@ -5,3 +5,5 @@ 'use strict'; | ||
EXPRESS_APP.get('/api/validateFlowPermissions', (req, res) => { | ||
XIBLE.Flow.validatePermissions().then((result) => { | ||
XIBLE.Flow | ||
.validatePermissions() | ||
.then((result) => { | ||
res.json(result); | ||
@@ -8,0 +10,0 @@ }); |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Wildcard dependency
QualityPackage has a dependency with a floating version range. This can cause issues if the dependency publishes a new major version.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
1132925
136
10837
25
11
12
22
+ Addedstrip-ansi@*
+ Addedansi-regex@6.1.0(transitive)
+ Addedstrip-ansi@7.1.0(transitive)