Research
Security News
Malicious npm Packages Inject SSH Backdoors via Typosquatted Libraries
Socket’s threat research team has detected six malicious npm packages typosquatting popular libraries to insert SSH backdoors.
state-keeper
Advanced tools
StateKeeper is a simple but customizable state machine. Compared to other state machines, it works in a slightly different way: It observes one or more observables (sorry for the pun) and uses their events to transition to different states (a state can be as simple as a string).
It can be pretty useful for abstracting a complex state from basic low level events. If you are confused at this point don't worry, everything will become clear in a few lines.
An observable is an object able to fire events. Example of observables are:
In the next examples I will use the default jQuery object containing ".on" and ".trigger" methods.
In this example videoPlayer is an observable that fires a "play" event every time someone clicks on play. I can configure StateKeeper to keep track of the state of the player:
var state = StateKeeper(videoPlayer, {
play: [
{from:"ready", to:"playing"},
{from:"playing", to:"paused"},
{from:"paused", to:"playing"}
]
});
StateKeeper takes as parameters the observable (videoPlayer) and a map of transitions (optionally an options object). For each transition (fired by the observable) I have a list of state changes. In this example when the subject fires the event "play" I can change the state from "ready" (the default initial state) to "playing", from "playing" to "paused" and the other way around. Any other event will be ignored, for example there is no way to getting back to the initial "ready" state. Let's see what happens when videoPlayer fires events:
videoPlayer.trigger('play');
state.get() === "playing"
videoPlayer.trigger('play');
state.get() === "paused"
videoPlayer.trigger('play');
state.get() === "playing"
"get" is the method to call if you want to get the current state. You can do something when you enter into a state:
state.on('enter.playing', function (event){
console.log('The player is ... ehm ... playing')
});
You can also do something else when you are leaving a state:
state.on('leave.playing', function (event){
console.log('The player is paused')
});
state.on('leave.ready', function (event){
console.log('The player is activated for the first time')
});
With "on" you can attach a function when the state changes. A stateKeeper instance fires an event called "enter.statename" when it enters a new state and "leave.statename" when it is leaving a state. If you transition from a state to another with the same name a special event will be fired: "stay.statename". There are a few shortcuts available:
Now you should start realizing how this thing can be useful. With a simple declarative syntax we are able to keep track of the various state of the videoPlayer!
Event is the argument passed to the function. It contains 3 properties:
There are few cases where you want be able to define more complicated transition conditions.
var state = StateKeeper(videoPlayer, {
play: [
{from:"ready", to:"playing"},
{from:"playing", to:"paused"},
{from:"paused", to:"playing"}
],
reset: {from: new RegExp('.*'), to: 'ready'}
});
In this example if the videoPlayer fires the "reset" event I want to go back to the initial "ready" state whatever the current state is. For this transition I have used an object instead of the usual array of objects, I can do it when I have only one possible transition from one event. I can also use a function to do even more complex stuff:
var state = StateKeeper(videoPlayer, {
play: [
{from:"ready", to:"playing"},
{from:"playing", to:"paused"},
{from:"paused", to:"playing"}
],
reset: [
{from: new RegExp('.*'), to: 'ready'}
],
stop: [
{
from: function (currentState, evt){
return currentState === "playing" && this.duration === this.currentTime;
},
to: 'ended'
},
{
from: function (currentState, evt){
return currentState === "playing" && this.duration !== this.currentTime;
},
to: 'ready'
}
]
});
In this example I have used a function for deciding if I should transition to the "ended" state. If I get a "stop" event from the videoPlayer then I check the current state (it is passed as an argument to the function). The other argument is the original event object as it is passed by the function callback. The function also gets the subject (videoPlayer in this case) as "this". I check if the video has finished playing, in which case I transition to the "ended" state. Otherwise I go to the "ready" state.
The initial state is the string "ready" by default but you can change it in the options:
var state = StateKeeper(videoPlayer, {
... event/state map
},
{
initialState: "initial"
});
Until now I have used a simple string as event. In reality you could use an object. This will allow us to do something a bit more complex:
var state = StateKeeper(videoPlayer, {
play: [
{from:"ready", to: {name: "playing", number: 1}},
{from:"playing", to: function (state, evt){return {name: "paused", number: state.number};}},
{from:"paused", to: function (state, evt){return {name: "playing", number: state.number + 1};}}
]
});
state.on("playing", function (evt){
console.log("Playing for " + evt.state.number + " times");
});
A state object is a plain old js object containing a property "name" (this is required). This property will be used any time a string is expected (triggering events, transitioning from string/regexp). The "from" can also be defined as "object" or as a function taking the previous state as argument.
StateKeeper is able to keep track of many different observables. You can do this by passing, instead of a single object, a map of observables {label: observable}. You can then use the label to tell what event of what observable you should listen to:
var state = StateKeeper({video1: videoPlayer1, video2: videoplayer2}, {
"video1:play": [
{from:"ready", to: "video1_playing",
{from:"video1_playing", to: "ready"},
{from:"video2_playing", to: "all_playing"},
{from:"all_playing", to: "video2_playing"}
],
"video2:play": [
{from:"ready", to: "video2_playing"},
{from:"video2_playing", to: "ready"},
{from:"video1_playing", to: "all_playing"},
{from:"all_playing", to: "video1_playing"}
]
});
In this example I am listening to 2 different video players so that I have available a state that is the combination of both. The word "timer" in the constructor map is reserved.
If you need to you can perform the same transition using different events. In this case you will separate the events using the coma:
var state = StateKeeper(videoPlayer, {
"playcontent,playads": [
{from:"ready", to: "video1_playing"
],
// ...
});
You can do the same observing more than one object:
var state = StateKeeper({video1: videoPlayer1, video2: videoplayer2}, {
"video1:play,video2:play": [
{from:"ready", to: "video_playing",
],
// ...
});
Sometimes you really need to transition sequentially through a series of states, or you just need to go back to the initial state after a short time is passed. The timer helps to do so. It is a special subject (observable), and you can listen to its event prefixing the event name with "timer:". In the transition map you can add a field "timer" with the name of the event, if the transition succeeds the event will be triggered. Example:
var state = StateKeeper(videoPlayer, {
"play": [
{from:"ready", to: "firstPlay", timer: "start"},
{from:"playing", to: "pause"},
{from:"pause", to: "playing"}
],
"timer:start": [
{from:"firstPlay", to: "playing"}
]
});
In this example when the videoPlayer triggers the "play" event the first time (from the state "ready") the state will transition like this:
"ready" -> "firstPlay" -> "playing"
From that point on the "play" event can be used for transitioning from "play" to "pause" and the other way around. Adding an intermediate state can help to fire a specific event only if you get in a particular state from another:
state.on("firstPlay", function (){
// this is executed only the first time you play a video
});
Another useful use case for the timer is when you need to transition automatically using a timeout:
var state = StateKeeper(videoPlayer, {
"play": [
{from:"ready", to: "waitingAd", timer: "adTimeout", timer_time: 500}
],
"adPlay": [
{from:"waitingAd", to: "adPlaying"}
],
"adEnd": [
{from:"adPlaying", to: "contentPlaying"}
],
"timer:adTimeout": [
{from:"waitingAd", to: "contentPlaying"}
]
});
In this case the videoplayer waits for an ad to show before the video. After a timeout of 500ms it transitions directly to the contentPlaying state.
Last but not least, it may happen you need to trigger a state change without having a specific transition. You can do that defining a timer with an interval. This one will fire with a specific interval until the next state change:
var state = StateKeeper(videoplayer, {
play: [
{from:"ready", to: "loadVideo", timer_interval: 10, timer: "contentReady"}
],
"timer:contentReady": [
{
from:function (st){
return st === "loadVideo" && videoplayer.isReady;
},
to: "playing"
}
]
});
In the previous example the timer "contentReady" will fire many times until videoplayer.isReady is true and it will be possible to transition to "playing".
Statekeeper attaches event handlers to do its job. If you don't use it anymore you should call the "destroy" method for freeing the resources.
StateKeeper uses '.on' to listen for events on the subject. You can change the default method with the options:
var videoPlayer = document.getElementsByTagName('video')[0];
var state = StateKeeper(videoPlayer, {
play: [
{from:"ready", to:"playing"},
{from:"playing", to:"paused"},
{from:"paused", to:"playing"}
]
},
{
bindMathod: "addEventListener",
unbindMethod: "removeEventListener"
});
It is a good idea testing the StateKeeper setup (using your favourite unit test suite). For make things easier I suggest you to test transition by transition. You can do that setting the state by hand:
state.set("play");
And using a very simple observable implementation:
var Subject = require('state-keeper/src/subject');
or
var Subject = StateKeeper.Subject;
Then you can set it up using:
var subject = Subject();
You can pass the bind and unbind method names (default on, off):
var subject = Subject('addEventListener', 'removeEventListener');
Subject has a very simple api:
subject.on("test", function (evt){
console.log(evt);
});
subject.trigger('test', {hello: "world!"});
This will print '{hello: "world!"}'. So this is an example on how we can test a single transition, let's start from a state machine configured:
function getstate(videoPlayer){
var state = StateKeeper(videoPlayer, {
play: [
{from:"ready", to:"playing"},
{from:"playing", to:"paused"},
{from:"paused", to:"playing"}
]
});
return state;
}
I can inject a fake videoPlayer:
var videoPlayer = Subject();
var state = getstate(videoPlayer);
Then I test a specific transition:
state.set("paused"); // I start from this state
videoPlayer.trigger("play");
assert(state.get(), "playing");
Easy as that! If you use timers for transitioning automatically you can use sinonjs fake timers like this (I am using qunit here):
module("test transition", {
setup: function (){
this.clock = sinon.useFakeTimers();
// useFakeTimers does not fake setImmediate. I make it by hand
this.setImmediateOriginal = window.setImmediate;
window.setImmediate = function(fn) {
setTimeout(fn, 0);
};
this.videoPlayer = Subject();
this.state = getstate(this.videoPlayer);
},
tearDown: function (){
this.clock.restore();
window.setImmediate = this.setImmediateOriginal;
}
} );
test("from playing to paused", function() {
this.state.set("playing"); // I start from this state
this.videoPlayer.trigger("play");
assert(this.state.get(), "paused");
this.clock.tick(60);
assert(this.state.get(), "standby"); // after 60ms I transition to this state
});
In complex configurations it might be difficult to trace and debug state changes. For example in cases where we can't predict exactly the sequence of events. For this reason you can add a special function "onTransition" that will be executed synchronously every time a transition occurs:
var state = StateKeeper(videoPlayer, {
play: [
{from:"ready", to:"playing"},
{from:"playing", to:"paused"},
{from:"paused", to:"playing"}
]
},
{
onTransition: function (oldState, currentState, evtName, evt){
console.log(oldState + " -> " + evtName + " -> " + currentState);
}
});
This function has as arguments the old and the new state, the event (and subject) that triggered the transition and the evt argument passed to the event handler.
This example will output helpful logs like:
FAQs
state-keeper keeps track of the state of one or more observables
The npm package state-keeper receives a total of 15 weekly downloads. As such, state-keeper popularity was classified as not popular.
We found that state-keeper demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Research
Security News
Socket’s threat research team has detected six malicious npm packages typosquatting popular libraries to insert SSH backdoors.
Security News
MITRE's 2024 CWE Top 25 highlights critical software vulnerabilities like XSS, SQL Injection, and CSRF, reflecting shifts due to a refined ranking methodology.
Security News
In this segment of the Risky Business podcast, Feross Aboukhadijeh and Patrick Gray discuss the challenges of tracking malware discovered in open source softare.