From 8ce41739808f79e84860af57d741f5cd10c6b4cb Mon Sep 17 00:00:00 2001 From: Federico Pinna Date: Fri, 9 Dec 2016 12:07:07 +0100 Subject: [PATCH] feat(initial): Initial version --- .gitignore | 39 +++++++++++++++++++++++++++ .travis.yml | 23 ++++++++++++++++ LICENSE | 21 +++++++++++++++ README.md | 8 ++++++ package.json | 68 +++++++++++++++++++++++++++++++++++++++++++++++ src/index.test.js | 59 ++++++++++++++++++++++++++++++++++++++++ src/index.ts | 44 ++++++++++++++++++++++++++++++ tsconfig.json | 13 +++++++++ 8 files changed, 275 insertions(+) create mode 100644 .gitignore create mode 100644 .travis.yml create mode 100644 LICENSE create mode 100644 README.md create mode 100644 package.json create mode 100644 src/index.test.js create mode 100644 src/index.ts create mode 100644 tsconfig.json diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..326adcc --- /dev/null +++ b/.gitignore @@ -0,0 +1,39 @@ +# Logs +logs +*.log +npm-debug.log* + +# Runtime data +pids +*.pid +*.seed + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage + +# nyc test coverage +.nyc_output + +# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (http://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +node_modules +jspm_packages + +# Optional npm cache directory +.npm + +# Optional REPL history +.node_repl_history + +dist diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..58c91ae --- /dev/null +++ b/.travis.yml @@ -0,0 +1,23 @@ +sudo: false +language: node_js +cache: + directories: + - node_modules +branches: + only: + - master +notifications: + email: false +node_js: + - '6' +before_install: + - npm i -g npm@^2.0.0 +before_script: + - npm prune +script: + - npm run build + - npm run cover + - npm run check-coverage +after_success: + - npm run report-coverage + - npm run semantic-release diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..971a400 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2016 Federico Pinna + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..709d313 --- /dev/null +++ b/README.md @@ -0,0 +1,8 @@ +# fsehx +Extended Finite State Event Handler for JavaScript + +[![travis build](https://img.shields.io/travis/0xfede/fsehx.svg)](https://travis-ci.org/0xfede/fsehx) +[![codecov coverage](https://img.shields.io/codecov/c/github/0xfede/fsehx.svg)](https://codecov.io/gh/0xfede/fsehx) +[![npm version](https://img.shields.io/npm/v/fsehx.svg)](https://www.npmjs.com/package/fsehx) + +**TBA** \ No newline at end of file diff --git a/package.json b/package.json new file mode 100644 index 0000000..ddb7fca --- /dev/null +++ b/package.json @@ -0,0 +1,68 @@ +{ + "name": "fsehx", + "version": "0.0.0-semantically-released", + "description": "Extended Finite State Event Handler for JavaScript", + "main": "dist/index.js", + "typings": "dist/index.d.ts", + "scripts": { + "prebuild": "rimraf dist", + "build": "tsc", + "commit": "git-cz", + "check-coverage": "nyc check-coverage --statements 100 --branches 100 --functions 100 --lines 100", + "report-coverage": "cat ./coverage/lcov.info | codecov", + "watch:test": "npm t -- -w", + "test": "mocha src/*.test.js", + "cover": "nyc --reporter=lcov --reporter=text npm t", + "semantic-release": "semantic-release pre && npm publish && semantic-release post" + }, + "repository": { + "type": "git", + "url": "https://github.com/0xfede/fsehx.git" + }, + "keywords": [ + "fsm", + "finite", + "state", + "machine", + "event", + "handler", + "typescript", + "javascript" + ], + "files": [ + "dist", + "README.md", + "LICENSE" + ], + "author": "Federico Pinna (http://www.vivocha.com/)", + "license": "MIT", + "bugs": { + "url": "https://github.com/0xfede/fsehx/issues" + }, + "homepage": "https://github.com/0xfede/fsehx#readme", + "devDependencies": { + "@types/node": "0.0.2", + "chai": "3.5.0", + "chai-spies": "0.7.1", + "codecov.io": "0.1.6", + "commitizen": "2.8.6", + "cz-conventional-changelog": "1.2.0", + "ghooks": "1.3.2", + "mocha": "3.0.2", + "nyc": "8.3.0", + "rimraf": "2.5.4", + "semantic-release": "^4.3.5", + "typescript": "2.0.3" + }, + "config": { + "commitizen": { + "path": "node_modules/cz-conventional-changelog" + }, + "ghooks": { + "pre-commit": "npm run build && npm run cover && npm run check-coverage" + } + }, + "dependencies": { + "fseh": "1.8.0" + } +} diff --git a/src/index.test.js b/src/index.test.js new file mode 100644 index 0000000..72f3726 --- /dev/null +++ b/src/index.test.js @@ -0,0 +1,59 @@ +var chai = require('chai') + , spies = require('chai-spies') + , should = chai.should() + , MachineX = require('../dist/index').MachineX + +chai.use(spies); + +describe('fsehx', function() { + + describe('enter', function() { + it('should emit events when entering a state', function() { + var m = new MachineX({ + start: {} + }); + + var named_pre_entry = chai.spy(); + var pre_entry = chai.spy(); + var named_entry = chai.spy(); + var entry = chai.spy(); + var named = chai.spy(); + + m.on('start:pre-entry', named_pre_entry); + m.on('pre-entry', pre_entry); + m.on('start:entry', named_entry); + m.on('entry', entry); + m.on('start', named); + + should.not.exist(m.state); + m.enter('start'); + should.exist(m.state); + m.state.should.be.a('string'); + m.state.should.equal('start'); + named_pre_entry.should.have.been.called.once(); + pre_entry.should.have.been.called.once.with('start'); + named_entry.should.have.been.called.once(); + entry.should.have.been.called.once.with('start'); + named.should.have.been.called.once(); + }); + + it('should emit events when entering a state', function() { + var m = new MachineX({ + start: {}, + end: {} + }, 'start'); + + var named_exit = chai.spy(); + var exit = chai.spy(); + + m.on('start:exit', named_exit); + m.on('exit', exit); + + m.enter('end'); + named_exit.should.have.been.called.once(); + exit.should.have.been.called.once.with('start'); + }); + + }); + +}); \ No newline at end of file diff --git a/src/index.ts b/src/index.ts new file mode 100644 index 0000000..ed5bf73 --- /dev/null +++ b/src/index.ts @@ -0,0 +1,44 @@ +import { EventEmitter } from 'events'; +import { Machine } from 'fseh'; + +export class MachineX extends Machine implements EventEmitter { + + // Machine overrides + enter(state: string): void { + if (this.state) { + this.emit(`${this.state}:exit`); + this.emit('exit', this.state); + } + this.emit(`${state}:pre-entry`); + this.emit('pre-entry', state); + super.enter(state); + this.emit(`${state}:entry`); + this.emit('entry', state); + this.emit(state); + } + + // EventEmitter interface + addListener: (event: string | symbol, listener: Function) => this; + on: (event: string | symbol, listener: Function) => this; + once: (event: string | symbol, listener: Function) => this; + prependListener: (event: string | symbol, listener: Function) => this; + prependOnceListener: (event: string | symbol, listener: Function) => this; + removeListener: (event: string | symbol, listener: Function) => this; + removeAllListeners: (event?: string | symbol) => this; + setMaxListeners: (n: number) => this; + getMaxListeners: () => number; + listeners: (event: string | symbol) => Function[]; + emit: (event: string | symbol, ...args: any[]) => boolean; + eventNames: () => (string | symbol)[]; + listenerCount: (type: string | symbol) => number; +} + + +function applyMixins(derivedCtor: any, baseCtors: any[]) { + baseCtors.forEach(baseCtor => { + Object.getOwnPropertyNames(baseCtor.prototype).forEach(name => { + derivedCtor.prototype[name] = baseCtor.prototype[name]; + }); + }); +} +applyMixins(MachineX, [EventEmitter]); diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..9f17fc5 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,13 @@ +{ + "compilerOptions": { + "module": "commonjs", + "target": "es5", + "noImplicitAny": false, + "sourceMap": true, + "declaration": true, + "outDir": "dist" + }, + "files": [ + "src/index.ts" + ] +}