New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

chine

Package Overview
Dependencies
Maintainers
1
Versions
2
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

chine - npm Package Compare versions

Comparing version 0.1.0 to 0.2.0

.jshintrc

116

demo/auth.js

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

"use strict";
var readline = require('readline');

@@ -13,34 +15,34 @@

fsm.state(function() {
// Assign a name
this.name('initial');
// Define the states from which it is legal
// to transition to this state. Attempts to transition
// to or from anything else will result in an exception
this.incoming('wait for username');
this.outgoing('wait for username');
// Execute this code as soon as the machine transitions into
// this state
// Assign a name
this.enter(function() {
// In this case, force the machine to run as soon as you
// transition, triggering the execution of this state automatically
this.machine.run();
});
// This code is execute when the machine is run in this state
// This closure *must* either transition to a new state; attempting
// to run the machine on the same state twice without transitioning
// to a new state first will result in an exception.
this.run(function() {
console.log('Enter username');
// Force a transition
this.transition('wait for username');
});
this.name('initial');
// Define the states from which it is legal
// to transition to this state. Attempts to transition
// to or from anything else will result in an exception
this.incoming('wait for username');
this.outgoing('wait for username');
// Execute this code as soon as the machine transitions into
// this state
this.enter(function() {
// In this case, force the machine to run as soon as you
// transition, triggering the execution of this state automatically
this.machine.run();
});
// This code is execute when the machine is run in this state
// This closure *must* either transition to a new state; attempting
// to run the machine on the same state twice without transitioning
// to a new state first will result in an exception.
this.run(function() {
console.log('Enter username');
// Force a transition
this.transition('wait for username');
});
});

@@ -51,17 +53,17 @@

fsm.state(function() {
this.name('wait for username');
this.incoming('initial');
this.outgoing('success', 'initial');
this.run(function(input) {
if (input == 'marco') {
console.log('Congratulations! You unlock the secret');
this.transition('success');
} else {
console.log('Invalid username. No prize for you.\n');
this.transition('initial');
}
});
this.name('wait for username');
this.incoming('initial');
this.outgoing('success', 'initial');
this.run(function(input) {
if (input === 'marco') {
console.log('Congratulations! You unlock the secret');
this.transition('success');
} else {
console.log('Invalid username. No prize for you.\n');
this.transition('initial');
}
});
});

@@ -72,8 +74,8 @@

fsm.state(function() {
this.name('success');
this.incoming('wait for username');
this.enter(function() {
this.emit('success');
});
this.name('success');
this.incoming('wait for username');
this.enter(function() {
this.emit('success');
});
});

@@ -84,4 +86,4 @@

var rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
input: process.stdin,
output: process.stdout,
});

@@ -99,4 +101,4 @@

fsm.on('success', function() {
console.log('Closing down. Goodbye!');
rl.close();
console.log('Closing down. Goodbye!');
rl.close();
});

@@ -108,2 +110,2 @@

fsm.compile();
fsm.run();
fsm.run();

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

"use strict";
var util = require('util');

@@ -7,12 +9,12 @@

var Chine = function Chine(initialState) {
if (!initialState) {
throw new Error('You must specify an initial state');
}
if (!initialState) {
throw new Error('You must specify an initial state');
}
this.compiled = false;
this.states = {};
this.initialState = initialState;
this.runtime = {};
this.compiled = false;
this.states = {};
this.initialState = initialState;
this.runtime = {};
};

@@ -23,169 +25,175 @@

Chine.prototype._checkCompiled = function checkMachineCompiled() {
if (this.compiled) throw new Error('You cannot modify a machine once it has been compiled');
if (this.compiled) {
throw new Error('You cannot modify a machine once it has been compiled');
}
};
Chine.prototype.state = function addStateToMachine(callback) {
this._checkCompiled();
var state = new State(callback);
state.validate();
if (state.name() in this.states) {
throw new Error('Duplicate state name ' + state.name());
}
this.states[state.name()] = state;
this._checkCompiled();
var state = new State(callback);
state.validate();
if (state.name() in this.states) {
throw new Error('Duplicate state name ' + state.name());
}
this.states[state.name()] = state;
};
Chine.prototype._buildRuntime = function buildMachineRuntime(serialized) {
var runtime = serialized || {};
var runtime = serialized || {};
runtime.run = this.run.bind(this);
runtime._machine = this;
runtime.run = this.run.bind(this);
runtime._machine = this;
if (!serialized) {
runtime.previousState = null;
runtime.currentState = this.initialState;
runtime._hasRunCurrentState = false;
runtime._runtimes = {};
Object.keys(this.states).forEach(function(stateName) {
runtime._runtimes[stateName] = this.states[stateName]._createRuntime(runtime);
}, this);
}
this.runtime = runtime;
}
if (!serialized) {
runtime.previousState = null;
runtime.currentState = this.initialState;
runtime._hasRunCurrentState = false;
runtime._runtimes = {};
Object.keys(this.states).forEach(function(stateName) {
runtime._runtimes[stateName] = this.states[stateName]._createRuntime(runtime);
}, this);
}
this.runtime = runtime;
};
Chine.prototype.clone = function createMachineClone(skipRuntime) {
if (!this.compiled) {
throw new Error('You cannot clone a machine that has not yet been compiled.');
}
var result = new Chine(this.initialState);
result.compiled = true;
result.states = this.states;
if (!skipRuntime) {
result._buildRuntime();
}
return result;
if (!this.compiled) {
throw new Error('You cannot clone a machine that has not yet been compiled.');
}
var result = new Chine(this.initialState);
result.compiled = true;
result.states = this.states;
if (!skipRuntime) {
result._buildRuntime();
}
return result;
};
Chine.prototype.serialize = function serializeMachine() {
if (!this.compiled) {
throw new Error('You cannot serialize a machine that has not yet been compiled.');
}
if (!this.compiled) {
throw new Error('You cannot serialize a machine that has not yet been compiled.');
}
var result = {};
Object.keys(this.runtime).forEach(function(key) {
result[key] = this.runtime[key];
}, this);
delete result.run;
delete result._machine;
result._runtimes = {};
Object.keys(this.runtime._runtimes).forEach(function(key) {
result._runtimes[key] = State._serializeRuntime(this.runtime._runtimes[key]);
}, this);
return result;
var result = {};
Object.keys(this.runtime).forEach(function(key) {
result[key] = this.runtime[key];
}, this);
delete result.run;
delete result._machine;
result._runtimes = {};
Object.keys(this.runtime._runtimes).forEach(function(key) {
result._runtimes[key] = State._serializeRuntime(this.runtime._runtimes[key]);
}, this);
return result;
};
Chine.prototype.unserialize = function unserializeMachine(runtime) {
if (!this.compiled) {
throw new Error('You cannot unserialize a machine that has not yet been compiled.');
}
var result = this.clone(true);
result._buildRuntime(runtime);
Object.keys(runtime._runtimes).forEach(function(key) {
runtime._runtimes[key] = this.states[key]._createRuntime(runtime, runtime._runtimes[key]);
}, this);
return result;
if (!this.compiled) {
throw new Error('You cannot unserialize a machine that has not yet been compiled.');
}
var result = this.clone(true);
result._buildRuntime(runtime);
Object.keys(runtime._runtimes).forEach(function(key) {
runtime._runtimes[key] = this.states[key]._createRuntime(runtime, runtime._runtimes[key]);
}, this);
return result;
};
Chine.prototype.compile = function compileMachine() {
Object.keys(this.states).forEach(function(stateName) {
var state = this.states[stateName];
state.incoming().forEach(function(incomingStateName) {
var incomingState = this.states[incomingStateName];
if (!incomingState) {
throw new Error('State ' + stateName + ' allows incoming transition from nonexistent state ' + incomingStateName);
}
if (incomingState.outgoing().indexOf(stateName) === -1) {
throw new Error('State ' + stateName + ' allows incoming transitions from ' + incomingStateName + ', but it is not in its allowed outgoing routes.');
}
}, this);
state.outgoing().forEach(function(outgoingStateName) {
var outgoingState = this.states[outgoingStateName];
if (!outgoingState) {
throw new Error('State ' + stateName + ' allows outgoing transition to nonexistent state ' + outgoingStateName);
}
if (outgoingState.incoming().indexOf(stateName) === -1) {
throw new Error('State ' + stateName + ' allows outgoing transitions to ' + outgoingStateName + ', but it is not in its allowed incoming routes.');
}
}, this);
Object.keys(this.states).forEach(function(stateName) {
var state = this.states[stateName];
state.incoming().forEach(function(incomingStateName) {
var incomingState = this.states[incomingStateName];
if (!incomingState) {
throw new Error('State ' + stateName + ' allows incoming transition from nonexistent state ' + incomingStateName);
}
if (incomingState.outgoing().indexOf(stateName) === -1) {
throw new Error('State ' + stateName + ' allows incoming transitions from ' + incomingStateName + ', but it is not in its allowed outgoing routes.');
}
}, this);
if (!(this.initialState in this.states)) {
throw new Error('Invalid initial state ' + this.initialState);
}
if (this.states[this.initialState].incoming().length === 0 && this.states[this.initialState].enter()) {
throw new Error('The initial state `' + this.initialState + '` has an enter handler that will never be called.');
}
this._buildRuntime();
this.compiled = true;
}
state.outgoing().forEach(function(outgoingStateName) {
var outgoingState = this.states[outgoingStateName];
if (!outgoingState) {
throw new Error('State ' + stateName + ' allows outgoing transition to nonexistent state ' + outgoingStateName);
}
if (outgoingState.incoming().indexOf(stateName) === -1) {
throw new Error('State ' + stateName + ' allows outgoing transitions to ' + outgoingStateName + ', but it is not in its allowed incoming routes.');
}
}, this);
}, this);
if (!(this.initialState in this.states)) {
throw new Error('Invalid initial state ' + this.initialState);
}
if (this.states[this.initialState].incoming().length === 0 && this.states[this.initialState].enter()) {
throw new Error('The initial state `' + this.initialState + '` has an enter handler that will never be called.');
}
this._buildRuntime();
this.compiled = true;
};
Chine.prototype.transition = function transitionMachine(stateName) {
var state = this.states[stateName];
if (!state) {
throw new Error('Cannot transition to state ' + state + ', which doesn\'t exist');
}
state._checkIncomingTransition(this.runtime.currentState);
var stateRuntime = this.runtime._runtimes[stateName];
this.states[this.runtime.currentState]._handleLeave(stateRuntime);
this.runtime.previousState = this.runtime.currentState;
this.runtime.currentState = stateName;
var state = this.states[stateName];
this.runtime._hasRunCurrentState = false;
this.states[this.runtime.currentState]._handleEnter(stateRuntime);
if (!state) {
throw new Error('Cannot transition to state ' + state + ', which doesn\'t exist');
}
state._checkIncomingTransition(this.runtime.currentState);
var stateRuntime = this.runtime._runtimes[stateName];
this.states[this.runtime.currentState]._handleLeave(stateRuntime);
this.runtime.previousState = this.runtime.currentState;
this.runtime.currentState = stateName;
this.runtime._hasRunCurrentState = false;
this.states[this.runtime.currentState]._handleEnter(stateRuntime);
};
Chine.prototype.run = function runMachine() {
if (this.runtime._hasRunCurrentState) {
throw new Error('The current state has already run.');
}
var state = this.states[this.runtime.currentState];
state._run(this.runtime._runtimes[this.runtime.currentState], arguments);
if (this.runtime._hasRunCurrentState) {
throw new Error('The current state has already run.');
}
var state = this.states[this.runtime.currentState];
state._run(this.runtime._runtimes[this.runtime.currentState], arguments);
};
module.exports = Chine;
Chine.prototype.getCurrentState = function getCurrentState() {
return this.runtime.currentState;
};
module.exports = Chine;

@@ -0,146 +1,156 @@

"use strict";
var State = function State(setup) {
this._name = null;
this._runFunction = null;
this._enter = null;
this._leave = null;
this._incomingStates = {};
this._outgoingStates = {};
this._name = null;
setup.call(this);
this._runFunction = null;
this._enter = null;
this._leave = null;
this._incomingStates = {};
this._outgoingStates = {};
setup.call(this);
};
State.prototype.validate = function validateState() {
if (!this.name()) throw new Error('The state ' + this + ' does not have a name.');
if (!this.run() && this._outgoingStates.length) throw new Error('The state ' + this.name() + ' does not have a run function.');
if (Object.keys(this._incomingStates).length == 0)
if (Object.keys(this._outgoingStates).length == 0) {
throw new Error('The state' + this.name() + ' has no incoming or outgoing routes');
if (!this.name()) {
throw new Error('The state ' + this + ' does not have a name.');
}
if (!this.run() && this._outgoingStates.length) {
throw new Error('The state ' + this.name() + ' does not have a run function.');
}
if (Object.keys(this._incomingStates).length === 0) {
if (Object.keys(this._outgoingStates).length === 0) {
throw new Error('The state' + this.name() + ' has no incoming or outgoing routes');
}
}
};
State.prototype._createRuntime = function createStateRuntime(machine, runtime) {
runtime = runtime || {};
runtime._state = this;
runtime.machine = machine;
runtime.transition = this.transition.bind(runtime);
runtime.emit = this.emit.bind(this);
runtime = runtime || {};
return runtime;
runtime._state = this;
runtime.machine = machine;
runtime.transition = this.transition.bind(runtime);
runtime.emit = this.emit.bind(this);
return runtime;
};
State._serializeRuntime = function serializeStateRuntime(runtime) {
var result = {};
Object.keys(result).forEach(function(key) {
result[key] = runtime[key];
});
delete runtime._state;
delete runtime.machine;
delete runtime.transition;
delete runtime.bind;
return result;
var result = {};
Object.keys(result).forEach(function(key) {
result[key] = runtime[key];
});
delete runtime._state;
delete runtime.machine;
delete runtime.transition;
delete runtime.bind;
return result;
};
State.prototype.name = function nameState(name) {
if (name) {
this._name = name;
} else {
return this._name;
}
if (name) {
this._name = name;
} else {
return this._name;
}
};
State.prototype.incoming = function allowIncomingStates() {
if (arguments.length) {
this._incomingStates = {};
for (var index = 0, length = arguments.length; index < length; index++) {
this._incomingStates[arguments[index]] = 1;
}
} else {
return Object.keys(this._incomingStates);
if (arguments.length) {
this._incomingStates = {};
for (var index = 0, length = arguments.length; index < length; index++) {
this._incomingStates[arguments[index]] = 1;
}
} else {
return Object.keys(this._incomingStates);
}
};
State.prototype.outgoing = function allowOutgoingStates() {
if (arguments.length) {
this._outgoingStates = {};
for (var index = 0, length = arguments.length; index < length; index++) {
this._outgoingStates[arguments[index]] = 1;
}
} else {
return Object.keys(this._outgoingStates);
if (arguments.length) {
this._outgoingStates = {};
for (var index = 0, length = arguments.length; index < length; index++) {
this._outgoingStates[arguments[index]] = 1;
}
} else {
return Object.keys(this._outgoingStates);
}
};
State.prototype.enter = function setEnterCallback(callback) {
if (arguments.length) {
this._enter = callback;
} else {
return this._before;
}
if (arguments.length) {
this._enter = callback;
} else {
return this._before;
}
};
State.prototype.leave = function setLeaveCallback(callback) {
if (arguments.length) {
this._leave = callback;
} else {
return this._leave;
}
if (arguments.length) {
this._leave = callback;
} else {
return this._leave;
}
};
State.prototype.run = function setStateProcessor(run) {
if (arguments.length) {
this._runFunction = run;
} else {
return this._runFunction;
}
if (arguments.length) {
this._runFunction = run;
} else {
return this._runFunction;
}
};
State.prototype.transition = function transitionState(state) {
if (!state) throw new Error('You must provide a state name when transitioning');
if (!(state in this._state._outgoingStates)) {
throw new Error('You cannot transition from state ' + this._name + ' to state ' + state + ' because the latter is not in the former\'s allowed outgoing routes.');
}
this.machine._machine.transition(state);
if (!state) {
throw new Error('You must provide a state name when transitioning');
}
if (!(state in this._state._outgoingStates)) {
throw new Error('You cannot transition from state ' + this._name + ' to state ' + state + ' because the latter is not in the former\'s allowed outgoing routes.');
}
this.machine._machine.transition(state);
};
State.prototype._checkIncomingTransition = function canTransitionFromState(state) {
if (!(state in this._incomingStates)) {
throw new Error('You cannot transition from state ' + state + ' to state ' + this._name + ' because the former is not in the latter\'s allowed incoming routes.');
}
if (!(state in this._incomingStates)) {
throw new Error('You cannot transition from state ' + state + ' to state ' + this._name + ' because the former is not in the latter\'s allowed incoming routes.');
}
};
State.prototype.emit = function emitProxy() {
this.machine.emit.apply(this.machine, arguments);
}
this.machine.emit.apply(this.machine, arguments);
};
State.prototype._handleEnter = function enterState(runtime) {
if (this._enter) {
this._enter.call(runtime);
}
if (this._enter) {
this._enter.call(runtime);
}
};
State.prototype._run = function runState(runtime, parameters) {
if (!this._runFunction) {
throw new Error('You cannot run on the `' + this.name() + '` state, because it does not have a run function.');
}
this._runFunction.apply(runtime, parameters);
if (!this._runFunction) {
throw new Error('You cannot run on the `' + this.name() + '` state, because it does not have a run function.');
}
this._runFunction.apply(runtime, parameters);
};
State.prototype._handleLeave = function leaveState(runtime) {
if (this._leave) {
this._leave.call(runtime);
}
if (this._leave) {
this._leave.call(runtime);
}
};
module.exports = State;
module.exports = State;
{
"name": "chine",
"version": "0.1.0",
"version": "0.2.0",
"author": "Marco Tabini <marcot@tabini.ca>",

@@ -5,0 +5,0 @@ "description": "A simple, asynchronous finite-state machine router",

@@ -24,12 +24,12 @@ # Chine: a finite-state machine execution engine

// Assign a name
this.name('initial');
// Define the states from which it is legal
// to transition to this state. Attempts to transition
// to or from anything else will result in an exception
this.incoming('wait for username');
this.outgoing('wait for username');
// Execute this code as soon as the machine transitions into

@@ -41,6 +41,6 @@ // this state

// transition, triggering the execution of this state automatically
this.machine.run();
});
// This code is execute when the machine is run in this state

@@ -50,6 +50,6 @@ // This closure *must* either transition to a new state; attempting

// to a new state first will result in an exception.
this.run(function() {
console.log('Enter username');
// Force a transition

@@ -66,11 +66,11 @@ this.transition('wait for username');

this.outgoing('success', 'initial');
this.run(function(input) {
if (input == 'marco') {
console.log('Congratulations! You unlock the secret');
this.transition('success');
} else {
console.log('Invalid username. No prize for you.\n');
this.transition('initial');

@@ -86,3 +86,3 @@ }

this.incoming('wait for username');
this.enter(function() {

@@ -124,3 +124,3 @@ this.emit('success');

Any time the `run` method of the machine is executed, the corresponding `run` method of the current state is invoked. At each state, you get to decide what can be done and what the next state is going to be. Chine helps you by giving you way to express which steps are admissible in input and output, thus reducing the complexity of the overall task.
Any time the `run` method of the machine is executed, the corresponding `run` method of the current state is invoked. At each state, you get to decide what can be done and what the next state is going to be. Chine helps you by giving you way to express which steps are admissible in input and output, thus reducing the complexity of the overall task.

@@ -130,3 +130,3 @@ Here's what happens in this script as you go through its execution (you can run it a copy of it in the `/demo` directory):

node demo/auth.js
As the script starts, it defines the various states of the fsm and specifies the starting state, `initial`. It then creates a `readline` object and starts listening for user input.

@@ -138,8 +138,8 @@

invalid
We have now entered a new line of text, which causes the `line` event to trigger on our `readline` instance. The `fsm.run` method is used as its handler; it receives the text of the line, which is transparently passed to the `run` method of the current state.
We have now entered a new line of text, which causes the `line` event to trigger on our `readline` instance. The `fsm.run` method is used as its handler; it receives the text of the line, which is transparently passed to the `run` method of the current state.
Here, we check the input and transition to `success` if it's correct. In `success`'s `run` method, we output some more text and, since `Chine` is a subclass of `EventEmitter`, emit the `success` event, which is caught by a handler that terminates the script.
If the value the user has input is _incorrect,_ on the other hand, we transition back to `initial`. In `initial`, we now have an `enter` handler, which is triggered as soon as the machine enters the state. In enter, we simply tell the machine to run again, which causes our state to be executed and the cycle to start from the beginning.
If the value the user has input is _incorrect,_ on the other hand, we transition back to `initial`. In `initial`, we now have an `enter` handler, which is triggered as soon as the machine enters the state. In enter, we simply tell the machine to run again, which causes our state to be executed and the cycle to start from the beginning.

@@ -191,2 +191,2 @@ This is important, because transitioning to a state does not cause it to be executed—if we didn't have an `enter` handler that forces the machine to execute one more time, the process would just stall. (Incidentally, the `enter` handler is not called when the machine is first run in its starting state.)

Patches are welcome, but only if accompanied by a matching test case. Bug reports, questions, and comments are equally appreciated! You can also reach the author directly [on Twitter](https://twitter.com/mtabini).
Patches are welcome, but only if accompanied by a matching test case. Bug reports, questions, and comments are equally appreciated! You can also reach the author directly [on Twitter](https://twitter.com/mtabini).
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