Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

function-tree

Package Overview
Dependencies
Maintainers
3
Versions
487
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

function-tree - npm Package Compare versions

Comparing version 0.4.7 to 0.5.0-0-alpha.21ed471e

.babelrc

60

package.json
{
"name": "function-tree",
"version": "0.4.7",
"version": "0.5.0-0-alpha.21ed471e",
"description": "When a function is not enough",
"main": "src/index.js",
"main": "lib/index.js",
"scripts": {
"clean": "rimraf lib && rimraf tests/*.js*",
"demo:redux": "node demos/server --demo redux",
"demo:mobx": "node demos/server --demo mobx",
"demo:node": "node demos/node/main.js",
"test": "nodeunit tests"
"test": "../../node_modules/.bin/mocha --compilers js:../../node_modules/babel-register 'src/**/*.test.js'",
"build": "BABEL_ENV=production ../../node_modules/.bin/babel src/ --out-dir=lib/ -s",
"coverage": "../../node_modules/.bin/nyc --reporter=lcov --reporter=json npm run test",
"prepublish": "npm run build"
},
"devDependencies": {
"axios": "^0.14.0",
"babel-loader": "^6.2.5",
"babel-plugin-transform-class-properties": "^6.11.5",
"babel-plugin-transform-decorators-legacy": "^1.3.4",
"babel-preset-es2015": "^6.14.0",
"babel-preset-react": "^6.11.1",
"body-parser": "^1.15.2",
"css-loader": "^0.24.0",
"express": "^4.14.0",
"html-webpack-plugin": "^2.22.0",
"mobx": "^2.5.1",
"mobx-react": "^3.5.5",
"nodeunit": "^0.10.2",
"picture-tube": "^1.0.0",
"react": "^15.3.1",
"react-dom": "^15.3.1",
"react-redux": "^4.4.5",
"redux": "^3.5.2",
"request": "^2.74.0",
"style-loader": "^0.13.1",
"webpack": "^1.13.2",
"webpack-dev-middleware": "^1.6.1"
},
"devDependencies": {},
"dependencies": {
"chalk": "^1.1.3",
"object-assign": "^4.1.0"
"eventemitter3": "^2.0.2"
},

@@ -46,3 +22,3 @@ "directories": {

"type": "git",
"url": "git+https://github.com/cerebral/function-tree.git"
"url": "git+https://github.com/cerebral/cerebral.git"
},

@@ -56,8 +32,20 @@ "keywords": [

],
"author": "Christian Alfoni",
"author": "Christian Alfoni <christianalfoni@gmail.com>",
"contributors": [
"Aleksey Guryanov <gurianov@gmail.com>"
],
"license": "MIT",
"bugs": {
"url": "https://github.com/cerebral/function-tree/issues"
"url": "https://github.com/cerebral/cerebral/issues"
},
"homepage": "https://github.com/cerebral/function-tree#readme"
"homepage": "https://github.com/cerebral/cerebral/tree/master/packages/function-tree#readme",
"nyc": {
"exclude": [
"node_modules",
"lib",
"tests",
"**/*.test.js",
"**/testHelper.js"
]
}
}

37

README.md

@@ -56,7 +56,7 @@ # function-tree

const FunctionTree = require('function-tree')
const ContextProvider = require('function-tree/providers/Context')
const ContextProvider = require('function-tree/providers').ContextProvider
const appMounted = require('../src/events/appMounted')
const window = {app: {}}
const execute = new FunctionTree([
const execute = FunctionTree([
ContextProvider({

@@ -84,3 +84,3 @@ window,

const execute = new FunctionTree([
const execute = FunctionTree([
// Providers

@@ -96,6 +96,6 @@ ])

import FunctionTree from 'function-tree'
import ContextProvider from 'function-tree/providers/Context'
import {ContextProvider} from 'function-tree/providers'
import request from 'request'
const execute = new FunctionTree([
const execute = FunctionTree([
ContextProvider({

@@ -295,3 +295,3 @@ request

const execute = new FunctionTree([
const execute = FunctionTree([
function MyProvider(context, functionDetails, payload) {

@@ -330,3 +330,3 @@ context // Current context

const execute = new FunctionTree()
const execute = FunctionTree()
const tree = [

@@ -367,3 +367,3 @@ funcA

const execute = new FunctionTree([])
const execute = FunctionTree([])
const tree = [

@@ -387,3 +387,3 @@ funcA, {

import FunctionTree from 'function-tree'
import ContextProvider from 'function-tree/providers/Context'
import {ContextProvider} from 'function-tree/providers'
import request from 'request'

@@ -396,3 +396,3 @@

const execute = new FunctionTree([
const execute = FunctionTree([
ContextProvider({

@@ -414,7 +414,6 @@ request

import FunctionTree from 'function-tree'
import DebuggerProvider from 'function-tree/providers/Debugger'
import ContextProvider from 'function-tree/providers/Context'
import {ContextProvider, DebuggerProvider} from 'function-tree/providers'
import request from 'request'
const execute = new FunctionTree([
const execute = FunctionTree([
DebuggerProvider({

@@ -446,3 +445,3 @@ colors: {

const execute = new FunctionTree([
const execute = FunctionTree([
NodeDebuggerProvider({

@@ -467,3 +466,3 @@ colors: {

const execute = new FunctionTree([])
const execute = FunctionTree([])
const tree = [

@@ -479,5 +478,11 @@ funcA

// When a function tree ends its execution
// When a function tree execution has ended
execute.on('end', (execution, payload) => {})
// When a function tree goes down a path
execute.on('pathStart', (execution, payload) => {})
// When a function tree ends execution of a path
execute.on('pathEnd', (execution, payload) => {})
// When a function in a function tree starts executing

@@ -484,0 +489,0 @@ execute.on('functionStart', (execution, functionDetails, payload) => {})

@@ -1,3 +0,1 @@

function Abort() {}
module.exports = Abort
export default class Abort {}

@@ -1,9 +0,9 @@

'use strict'
const assign = require('object-assign')
module.exports = function executeTree (tree, resolveFunctionResult, initialPayload, end) {
function runBranch (branch, index, payload, nextBranch) {
/*
Runs through the tree providing a "next" callback to process next step
of execution
*/
export default function executeTree (tree, resolveFunctionResult, initialPayload, branchStart, branchEnd, end) {
function runBranch (branch, index, payload, prevPayload, nextBranch) {
function runNextItem (result) {
runBranch(branch, index + 1, result, nextBranch)
runBranch(branch, index + 1, result, payload, nextBranch)
}

@@ -13,8 +13,10 @@

return function (result) {
let newPayload = assign({}, payload, result ? result.payload : {})
const newPayload = Object.assign({}, payload, result ? result.payload : {})
if (result && funcDetails.outputs) {
let outputs = Object.keys(funcDetails.outputs)
const outputs = Object.keys(funcDetails.outputs)
if (~outputs.indexOf(result.path)) {
runBranch(funcDetails.outputs[result.path], 0, newPayload, outputResult)
branchStart(funcDetails, result.path, newPayload)
runBranch(funcDetails.outputs[result.path], 0, newPayload, payload, outputResult)
} else {

@@ -29,20 +31,24 @@ throw new Error(`function-tree - function ${funcDetails.name} must use one of its possible outputs: ${outputs.join(', ')}.`)

let currentItem = branch[index]
const currentItem = branch[index]
if (!currentItem) {
nextBranch ? nextBranch(payload) : end(payload)
if (branch !== tree) {
branchEnd(payload)
}
nextBranch(payload)
} else if (Array.isArray(currentItem)) {
const itemLength = currentItem.length
currentItem.reduce((payloads, action) => {
resolveFunctionResult(action, payload, processFunctionOutput(action, (payload) => {
resolveFunctionResult(action, payload, prevPayload, processFunctionOutput(action, (payload) => {
payloads.push(payload)
if (payloads.length === itemLength) runNextItem(assign.apply(null, [{}].concat(payloads)))
if (payloads.length === itemLength) runNextItem(Object.assign.apply(Object, [{}].concat(payloads)))
}))
return payloads;
return payloads
}, [])
} else {
resolveFunctionResult(currentItem, payload, processFunctionOutput(currentItem, runNextItem))
resolveFunctionResult(currentItem, payload, prevPayload, processFunctionOutput(currentItem, runNextItem))
}
}
return runBranch(tree, 0, initialPayload, end)
return runBranch(tree, 0, initialPayload, null, end)
}

@@ -1,24 +0,28 @@

'use strict'
import EventEmitter from 'eventemitter3'
import executeTree from './executeTree'
import createStaticTree from './staticTree'
import ExecutionProvider from './providers/Execution'
import InputProvider from './providers/Input'
import PathProvider from './providers/Path'
import Path from './Path'
import Abort from './Abort'
const EventEmitter = require('events')
const executeTree = require('./executeTree')
const createStaticTree = require('./staticTree')
const ExecutionProvider = require('../providers/Execution')
const InputProvider = require('../providers/Input')
const PathProvider = require('../providers/Path')
const assign = require('object-assign')
const Path = require('./Path')
const Abort = require('./Abort')
function createUniqueId() {
/*
Need to create a unique ID for each execution to identify it
in debugger
*/
function createUniqueId () {
return Date.now() + '_' + Math.floor(Math.random() * 10000)
}
function isValidResult(result) {
/*
Validate any returned value from a function. Has
to be nothing or an object
*/
function isValidResult (result) {
return (
!result ||
(
result &&
!Array.isArray(result) &&
typeof result === 'object'
typeof result === 'object' &&
!Array.isArray(result)
)

@@ -28,172 +32,204 @@ )

function FunctionTreeExecution(name, staticTree, functionTree, errorCallback) {
this.id = createUniqueId()
this.name = name
this.staticTree = staticTree
this.functionTree = functionTree
this.datetime = Date.now()
this.errorCallback = errorCallback
this.runFunction = this.runFunction.bind(this)
/*
If it walks like a duck and quacks like a duck...
*/
function isPromise (result) {
return result && typeof result.then === 'function' && typeof result.catch === 'function'
}
FunctionTreeExecution.prototype.runFunction = function(funcDetails, payload, next) {
const context = this.createContext(funcDetails, payload)
const functionTree = this.functionTree
const errorCallback = this.errorCallback
const execution = this
class FunctionTreeExecution extends EventEmitter {
constructor (name, staticTree, functionTree, errorCallback) {
super()
this.id = createUniqueId()
this.name = name
this.staticTree = staticTree
this.functionTree = functionTree
this.datetime = Date.now()
this.errorCallback = errorCallback
functionTree.emit('functionStart', execution, funcDetails, payload)
const result = funcDetails.function(context)
if (result instanceof Abort) {
return functionTree.emit('abort', execution, funcDetails, payload)
this.runFunction = this.runFunction.bind(this)
}
if (result && result.then && result.catch && typeof result.then === 'function' && typeof result.catch === 'function') {
functionTree.emit('asyncFunction', execution, funcDetails, payload)
result
.then(function (result) {
if (result instanceof Path) {
functionTree.emit('functionEnd', execution, funcDetails, payload)
next(result.toJS())
} else if (funcDetails.outputs) {
functionTree.emit('functionEnd', execution, funcDetails, payload)
throw new Error('The result ' + JSON.stringify(result) + ' from function ' + funcDetails.name + ' needs to be a path')
} else if (isValidResult(result)) {
functionTree.emit('functionEnd', execution, funcDetails, payload)
next({
payload: result
})
} else {
functionTree.emit('functionEnd', execution, funcDetails, payload)
throw new Error('The result ' + JSON.stringify(result) + ' from function ' + funcDetails.name + ' is not a valid result')
}
})
.catch(function (result) {
if (result instanceof Error) {
errorCallback(result)
} else if (result instanceof Path) {
functionTree.emit('functionEnd', execution, funcDetails, payload)
next(result.toJS())
} else if (funcDetails.outputs) {
var error = new Error('The result ' + JSON.stringify(result) + ' from function ' + funcDetails.name + ' needs to be a path')
/*
Creates the context for the current function to be run,
emits events and handles its returned value. Also handles
the returned value being a promise
*/
runFunction (funcDetails, payload, prevPayload, next) {
const context = this.createContext(funcDetails, payload, prevPayload)
const functionTree = this.functionTree
const errorCallback = this.errorCallback
const execution = this
errorCallback(result)
} else if (isValidResult(result)) {
functionTree.emit('functionEnd', execution, funcDetails, payload)
next({
payload: result
})
} else {
var error = new Error('The result ' + JSON.stringify(result) + ' from function ' + funcDetails.name + ' is not a valid result')
functionTree.emit('functionStart', execution, funcDetails, payload)
const result = funcDetails.function(context)
errorCallback(error)
}
})
} else if (result instanceof Path) {
functionTree.emit('functionEnd', execution, funcDetails, payload)
next(result.toJS())
} else if (funcDetails.outputs) {
var error = new Error('The result ' + JSON.stringify(result) + ' from function ' + funcDetails.name + ' needs to be a path')
if (result instanceof Abort) {
return functionTree.emit('abort', execution, funcDetails, payload)
}
errorCallback(error)
} else if (isValidResult(result)) {
functionTree.emit('functionEnd', execution, funcDetails, payload)
next({
payload: result
})
} else {
var error = new Error('The result ' + JSON.stringify(result) + ' from function ' + funcDetails.name + ' is not a valid result')
errorCallback(error)
}
}
/*
If result is a promise we want to emit an event and wait for it to resolve to
move on
*/
if (isPromise(result)) {
functionTree.emit('asyncFunction', execution, funcDetails, payload)
result
.then(function (result) {
if (result instanceof Path) {
functionTree.emit('functionEnd', execution, funcDetails, payload)
next(result.toJS())
} else if (funcDetails.outputs) {
functionTree.emit('functionEnd', execution, funcDetails, payload)
throw new Error('The result ' + JSON.stringify(result) + ' from function ' + funcDetails.name + ' needs to be a path')
} else if (isValidResult(result)) {
functionTree.emit('functionEnd', execution, funcDetails, payload)
next({
payload: result
})
} else {
functionTree.emit('functionEnd', execution, funcDetails, payload)
throw new Error('The result ' + JSON.stringify(result) + ' from function ' + funcDetails.name + ' is not a valid result')
}
})
.catch(function (result) {
if (result instanceof Error) {
errorCallback(result)
} else if (result instanceof Path) {
functionTree.emit('functionEnd', execution, funcDetails, payload)
next(result.toJS())
} else if (funcDetails.outputs) {
let error = new Error('The result ' + JSON.stringify(result) + ' from function ' + funcDetails.name + ' needs to be a path')
FunctionTreeExecution.prototype.createContext = function(action, payload) {
return [
ExecutionProvider(this, Abort),
InputProvider(),
PathProvider()
].concat(this.functionTree.contextProviders).reduce(function(currentContext, contextProvider) {
var newContext = (
typeof contextProvider === 'function' ?
contextProvider(currentContext, action, payload)
:
assign(currentContext, contextProvider)
)
errorCallback(error)
} else if (isValidResult(result)) {
functionTree.emit('functionEnd', execution, funcDetails, payload)
next({
payload: result
})
} else {
let error = new Error('The result ' + JSON.stringify(result) + ' from function ' + funcDetails.name + ' is not a valid result')
if (newContext !== currentContext) {
throw new Error('function-tree: You are not returning the context from a provider')
errorCallback(error)
}
})
} else if (result instanceof Path) {
functionTree.emit('functionEnd', execution, funcDetails, payload)
next(result.toJS())
} else if (funcDetails.outputs) {
let error = new Error('The result ' + JSON.stringify(result) + ' from function ' + funcDetails.name + ' needs to be a path')
errorCallback(error)
} else if (isValidResult(result)) {
functionTree.emit('functionEnd', execution, funcDetails, payload)
next({
payload: result
})
} else {
let error = new Error('The result ' + JSON.stringify(result) + ' from function ' + funcDetails.name + ' is not a valid result')
errorCallback(error)
}
}
return newContext
}, {})
/*
Creates the context for the next running function
*/
createContext (funcDetails, payload, prevPayload) {
return [
ExecutionProvider(this, Abort),
InputProvider(),
PathProvider()
].concat(this.functionTree.contextProviders).reduce(function (currentContext, contextProvider) {
var newContext = (
typeof contextProvider === 'function'
? contextProvider(currentContext, funcDetails, payload, prevPayload)
: Object.assign(currentContext, contextProvider)
)
if (newContext !== currentContext) {
throw new Error('function-tree: You are not returning the context from a provider')
}
return newContext
}, {})
}
}
function FunctionTree(contextProviders) {
if (
!Boolean(this) ||
(typeof window !== 'undefined' && this === window)
) {
return new FunctionTree(contextProviders)
class FunctionTree extends EventEmitter {
constructor (contextProviders) {
super()
this.cachedTrees = []
this.cachedStaticTrees = []
this.contextProviders = contextProviders || []
this.runTree = this.runTree.bind(this)
this.runTree.on = this.on.bind(this)
this.runTree.once = this.once.bind(this)
this.runTree.off = this.removeListener.bind(this)
return this.runTree
}
this.cachedTrees = []
this.cachedStaticTrees = []
this.contextProviders = contextProviders || []
this.runTree = this.runTree.bind(this)
this.runTree.on = this.on.bind(this)
this.runTree.once = this.once.bind(this)
this.runTree.off = this.removeListener.bind(this)
/*
Analyses the tree to identify paths and its validity. This analysis
is cached. Then the method creates an execution for the tree to run.
*/
runTree () {
let name
let tree
let payload
let cb
let staticTree
const args = [].slice.call(arguments)
args.forEach((arg) => {
if (typeof arg === 'string') {
name = arg
} else if (Array.isArray(arg)) {
tree = arg
} else if (typeof arg === 'function') {
cb = arg
} else {
payload = arg
}
})
return this.runTree
}
if (!tree) {
throw new Error('function-tree - You did not pass in a function tree')
}
FunctionTree.prototype = Object.create(EventEmitter.prototype)
FunctionTree.prototype.runTree = function() {
var name
var tree
var payload
var cb
var staticTree
var args = [].slice.call(arguments)
args.forEach(function (arg) {
if (typeof arg === 'string') {
name = arg
return
} else if (Array.isArray(arg)) {
tree = arg
} else if (typeof arg === 'function') {
cb = arg
const treeIdx = this.cachedTrees.indexOf(tree)
if (treeIdx === -1) {
staticTree = createStaticTree(tree)
this.cachedTrees.push(tree)
this.cachedStaticTrees.push(staticTree)
} else {
payload = arg
staticTree = this.cachedStaticTrees[treeIdx]
}
})
const execution = new FunctionTreeExecution(name, staticTree, this, (error) => {
cb && cb(error, execution, payload)
setTimeout(() => {
this.emit('error', error, execution, payload)
})
})
if (!tree) {
throw new Error('function-tree - You did not pass in a function tree')
this.emit('start', execution, payload)
executeTree(
execution.staticTree,
execution.runFunction,
payload,
(funcDetails, path, currentPayload) => {
this.emit('pathStart', path, execution, funcDetails, currentPayload)
},
(currentPayload) => {
this.emit('pathEnd', execution, currentPayload)
},
(finalPayload) => {
this.emit('end', execution, finalPayload)
cb && cb(null, execution, finalPayload)
}
)
}
}
if (this.cachedTrees.indexOf(tree) === -1) {
staticTree = createStaticTree(tree)
this.cachedTrees.push(tree)
this.cachedStaticTrees.push(staticTree)
} else {
staticTree = this.cachedStaticTrees[this.cachedTrees.indexOf(tree)]
}
var execution = new FunctionTreeExecution(name, staticTree, this, function(error) {
cb && cb(error, execution, payload)
setTimeout(function () {
this.emit('error', error, execution, payload)
}.bind(this))
}.bind(this))
this.emit('start', execution, payload)
executeTree(execution.staticTree, execution.runFunction, payload, function() {
this.emit('end', execution, payload)
cb && cb(null, execution, payload)
}.bind(this))
};
module.exports = FunctionTree
export default (contextProviders) => {
return new FunctionTree(contextProviders)
}

@@ -1,13 +0,14 @@

function Path(path, payload) {
this.path = path
this.payload = payload
}
Path.prototype.toJS = function () {
return {
path: this.path,
payload: this.payload
class Path {
constructor (path, payload) {
this.path = path
this.payload = payload
}
toJS () {
return {
path: this.path,
payload: this.payload
}
}
}
module.exports = Path
export default Path

@@ -1,7 +0,6 @@

'use strict'
function getFunctionName (fn) {
var ret = fn.toString()
let ret = fn.toString()
ret = ret.substr('function '.length)
ret = ret.substr(0, ret.indexOf('('))
return ret

@@ -13,3 +12,3 @@ }

item = item.slice()
return item.map(function (subItem, index) {
return item.map((subItem, index) => {
if (typeof subItem === 'function') {

@@ -27,11 +26,11 @@ let nextSubItem = item[index + 1]

throw new Error('Signal Tree - Unexpected entry in signal chain')
}).filter(function (func) {
}).filter((func) => {
return !!func
})
} else if (typeof item === 'function') {
let func = item
let outputs = isChain
let funcDetails = {
const func = item
const outputs = isChain
const funcDetails = {
name: func.displayName || getFunctionName(func),
functionIndex: functions.indexOf(func) === -1 ? (functions.push(func) - 1) : functions.indexOf(func),
functionIndex: functions.push(func) - 1,
function: func

@@ -41,5 +40,5 @@ }

funcDetails.outputs = {}
Object.keys(outputs).forEach(function (key) {
Object.keys(outputs).forEach((key) => {
if (func.outputs && !~func.outputs.indexOf(key)) {
throw new Error(`function-tree - Outputs object doesn\'t match list of possible outputs defined for function.`)
throw new Error(`function-tree - Outputs object doesn't match list of possible outputs defined for function.`)
}

@@ -56,3 +55,3 @@ funcDetails.outputs[key] = traverse(functions, outputs[key], true)

module.exports = function(tree) {
export default (tree) => {
const functions = []

@@ -59,0 +58,0 @@

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc