Big News: Socket raises $60M Series C at a $1B valuation to secure software supply chains for AI-driven development.Announcement
Sign In

@trigo/fsm

Package Overview
Dependencies
Maintainers
3
Versions
23
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@trigo/fsm - npm Package Compare versions

Comparing version
3.3.1
to
3.4.0
+2508
docs/ast/source/execute-transition-rules.js.json
{
"type": "File",
"start": 0,
"end": 286,
"loc": {
"start": {
"line": 1,
"column": 0
},
"end": {
"line": 17,
"column": 0
}
},
"program": {
"type": "Program",
"start": 0,
"end": 286,
"loc": {
"start": {
"line": 1,
"column": 0
},
"end": {
"line": 17,
"column": 0
}
},
"sourceType": "module",
"body": [
{
"type": "ExportDefaultDeclaration",
"start": 31,
"end": 285,
"loc": {
"start": {
"line": 4,
"column": 0
},
"end": {
"line": 16,
"column": 2
}
},
"declaration": {
"type": "ArrowFunctionExpression",
"start": 46,
"end": 284,
"loc": {
"start": {
"line": 4,
"column": 15
},
"end": {
"line": 16,
"column": 1
}
},
"id": null,
"generator": false,
"expression": false,
"async": false,
"params": [
{
"type": "Identifier",
"start": 47,
"end": 62,
"loc": {
"start": {
"line": 4,
"column": 16
},
"end": {
"line": 4,
"column": 31
},
"identifierName": "transitionRules"
},
"name": "transitionRules",
"leadingComments": null
},
{
"type": "Identifier",
"start": 64,
"end": 67,
"loc": {
"start": {
"line": 4,
"column": 33
},
"end": {
"line": 4,
"column": 36
},
"identifierName": "ctx"
},
"name": "ctx"
}
],
"body": {
"type": "BlockStatement",
"start": 72,
"end": 284,
"loc": {
"start": {
"line": 4,
"column": 41
},
"end": {
"line": 16,
"column": 1
}
},
"body": [
{
"type": "VariableDeclaration",
"start": 75,
"end": 158,
"loc": {
"start": {
"line": 5,
"column": 1
},
"end": {
"line": 5,
"column": 84
}
},
"declarations": [
{
"type": "VariableDeclarator",
"start": 81,
"end": 157,
"loc": {
"start": {
"line": 5,
"column": 7
},
"end": {
"line": 5,
"column": 83
}
},
"id": {
"type": "Identifier",
"start": 81,
"end": 86,
"loc": {
"start": {
"line": 5,
"column": 7
},
"end": {
"line": 5,
"column": 12
},
"identifierName": "rules"
},
"name": "rules"
},
"init": {
"type": "ConditionalExpression",
"start": 89,
"end": 157,
"loc": {
"start": {
"line": 5,
"column": 15
},
"end": {
"line": 5,
"column": 83
}
},
"test": {
"type": "CallExpression",
"start": 89,
"end": 119,
"loc": {
"start": {
"line": 5,
"column": 15
},
"end": {
"line": 5,
"column": 45
}
},
"callee": {
"type": "MemberExpression",
"start": 89,
"end": 102,
"loc": {
"start": {
"line": 5,
"column": 15
},
"end": {
"line": 5,
"column": 28
}
},
"object": {
"type": "Identifier",
"start": 89,
"end": 94,
"loc": {
"start": {
"line": 5,
"column": 15
},
"end": {
"line": 5,
"column": 20
},
"identifierName": "Array"
},
"name": "Array"
},
"property": {
"type": "Identifier",
"start": 95,
"end": 102,
"loc": {
"start": {
"line": 5,
"column": 21
},
"end": {
"line": 5,
"column": 28
},
"identifierName": "isArray"
},
"name": "isArray"
},
"computed": false
},
"arguments": [
{
"type": "Identifier",
"start": 103,
"end": 118,
"loc": {
"start": {
"line": 5,
"column": 29
},
"end": {
"line": 5,
"column": 44
},
"identifierName": "transitionRules"
},
"name": "transitionRules"
}
]
},
"consequent": {
"type": "Identifier",
"start": 122,
"end": 137,
"loc": {
"start": {
"line": 5,
"column": 48
},
"end": {
"line": 5,
"column": 63
},
"identifierName": "transitionRules"
},
"name": "transitionRules"
},
"alternate": {
"type": "ArrayExpression",
"start": 140,
"end": 157,
"loc": {
"start": {
"line": 5,
"column": 66
},
"end": {
"line": 5,
"column": 83
}
},
"elements": [
{
"type": "Identifier",
"start": 141,
"end": 156,
"loc": {
"start": {
"line": 5,
"column": 67
},
"end": {
"line": 5,
"column": 82
},
"identifierName": "transitionRules"
},
"name": "transitionRules"
}
]
}
}
}
],
"kind": "const"
},
{
"type": "ForOfStatement",
"start": 161,
"end": 262,
"loc": {
"start": {
"line": 7,
"column": 1
},
"end": {
"line": 13,
"column": 2
}
},
"left": {
"type": "VariableDeclaration",
"start": 166,
"end": 176,
"loc": {
"start": {
"line": 7,
"column": 6
},
"end": {
"line": 7,
"column": 16
}
},
"declarations": [
{
"type": "VariableDeclarator",
"start": 172,
"end": 176,
"loc": {
"start": {
"line": 7,
"column": 12
},
"end": {
"line": 7,
"column": 16
}
},
"id": {
"type": "Identifier",
"start": 172,
"end": 176,
"loc": {
"start": {
"line": 7,
"column": 12
},
"end": {
"line": 7,
"column": 16
},
"identifierName": "rule"
},
"name": "rule"
},
"init": null
}
],
"kind": "const"
},
"right": {
"type": "Identifier",
"start": 180,
"end": 185,
"loc": {
"start": {
"line": 7,
"column": 20
},
"end": {
"line": 7,
"column": 25
},
"identifierName": "rules"
},
"name": "rules"
},
"body": {
"type": "BlockStatement",
"start": 187,
"end": 262,
"loc": {
"start": {
"line": 7,
"column": 27
},
"end": {
"line": 13,
"column": 2
}
},
"body": [
{
"type": "IfStatement",
"start": 191,
"end": 259,
"loc": {
"start": {
"line": 8,
"column": 2
},
"end": {
"line": 12,
"column": 3
}
},
"test": {
"type": "Identifier",
"start": 195,
"end": 199,
"loc": {
"start": {
"line": 8,
"column": 6
},
"end": {
"line": 8,
"column": 10
},
"identifierName": "rule"
},
"name": "rule"
},
"consequent": {
"type": "BlockStatement",
"start": 201,
"end": 259,
"loc": {
"start": {
"line": 8,
"column": 12
},
"end": {
"line": 12,
"column": 3
}
},
"body": [
{
"type": "TryStatement",
"start": 206,
"end": 255,
"loc": {
"start": {
"line": 9,
"column": 3
},
"end": {
"line": 11,
"column": 4
}
},
"block": {
"type": "BlockStatement",
"start": 210,
"end": 224,
"loc": {
"start": {
"line": 9,
"column": 7
},
"end": {
"line": 9,
"column": 21
}
},
"body": [
{
"type": "ExpressionStatement",
"start": 212,
"end": 222,
"loc": {
"start": {
"line": 9,
"column": 9
},
"end": {
"line": 9,
"column": 19
}
},
"expression": {
"type": "CallExpression",
"start": 212,
"end": 221,
"loc": {
"start": {
"line": 9,
"column": 9
},
"end": {
"line": 9,
"column": 18
}
},
"callee": {
"type": "Identifier",
"start": 212,
"end": 216,
"loc": {
"start": {
"line": 9,
"column": 9
},
"end": {
"line": 9,
"column": 13
},
"identifierName": "rule"
},
"name": "rule"
},
"arguments": [
{
"type": "Identifier",
"start": 217,
"end": 220,
"loc": {
"start": {
"line": 9,
"column": 14
},
"end": {
"line": 9,
"column": 17
},
"identifierName": "ctx"
},
"name": "ctx"
}
]
}
}
],
"directives": []
},
"handler": {
"type": "CatchClause",
"start": 225,
"end": 255,
"loc": {
"start": {
"line": 9,
"column": 22
},
"end": {
"line": 11,
"column": 4
}
},
"param": {
"type": "Identifier",
"start": 232,
"end": 233,
"loc": {
"start": {
"line": 9,
"column": 29
},
"end": {
"line": 9,
"column": 30
},
"identifierName": "e"
},
"name": "e"
},
"body": {
"type": "BlockStatement",
"start": 235,
"end": 255,
"loc": {
"start": {
"line": 9,
"column": 32
},
"end": {
"line": 11,
"column": 4
}
},
"body": [
{
"type": "ReturnStatement",
"start": 241,
"end": 250,
"loc": {
"start": {
"line": 10,
"column": 4
},
"end": {
"line": 10,
"column": 13
}
},
"argument": {
"type": "Identifier",
"start": 248,
"end": 249,
"loc": {
"start": {
"line": 10,
"column": 11
},
"end": {
"line": 10,
"column": 12
},
"identifierName": "e"
},
"name": "e"
}
}
],
"directives": []
}
},
"guardedHandlers": [],
"finalizer": null
}
],
"directives": []
},
"alternate": null
}
],
"directives": []
}
},
{
"type": "ReturnStatement",
"start": 265,
"end": 282,
"loc": {
"start": {
"line": 15,
"column": 1
},
"end": {
"line": 15,
"column": 18
}
},
"argument": {
"type": "Identifier",
"start": 272,
"end": 281,
"loc": {
"start": {
"line": 15,
"column": 8
},
"end": {
"line": 15,
"column": 17
},
"identifierName": "undefined"
},
"name": "undefined"
}
}
],
"directives": []
},
"leadingComments": [
{
"type": "CommentBlock",
"value": "* @private ",
"start": 15,
"end": 30,
"loc": {
"start": {
"line": 3,
"column": 0
},
"end": {
"line": 3,
"column": 15
}
}
}
],
"trailingComments": []
},
"leadingComments": [
{
"type": "CommentBlock",
"value": "* @private ",
"start": 15,
"end": 30,
"loc": {
"start": {
"line": 3,
"column": 0
},
"end": {
"line": 3,
"column": 15
}
}
}
]
}
],
"directives": [
{
"type": "Directive",
"start": 0,
"end": 13,
"loc": {
"start": {
"line": 1,
"column": 0
},
"end": {
"line": 1,
"column": 13
}
},
"value": {
"type": "DirectiveLiteral",
"start": 0,
"end": 12,
"loc": {
"start": {
"line": 1,
"column": 0
},
"end": {
"line": 1,
"column": 12
}
},
"value": "use strict",
"extra": {
"raw": "'use strict'",
"rawValue": "use strict"
},
"trailingComments": null
},
"trailingComments": [
{
"type": "CommentBlock",
"value": "* @private ",
"start": 15,
"end": 30,
"loc": {
"start": {
"line": 3,
"column": 0
},
"end": {
"line": 3,
"column": 15
}
}
}
]
}
]
},
"comments": [
{
"type": "CommentBlock",
"value": "* @private ",
"start": 15,
"end": 30,
"loc": {
"start": {
"line": 3,
"column": 0
},
"end": {
"line": 3,
"column": 15
}
}
}
],
"tokens": [
{
"type": {
"label": "string",
"beforeExpr": false,
"startsExpr": true,
"rightAssociative": false,
"isLoop": false,
"isAssign": false,
"prefix": false,
"postfix": false,
"binop": null,
"updateContext": null
},
"value": "use strict",
"start": 0,
"end": 12,
"loc": {
"start": {
"line": 1,
"column": 0
},
"end": {
"line": 1,
"column": 12
}
}
},
{
"type": {
"label": ";",
"beforeExpr": true,
"startsExpr": false,
"rightAssociative": false,
"isLoop": false,
"isAssign": false,
"prefix": false,
"postfix": false,
"binop": null,
"updateContext": null
},
"start": 12,
"end": 13,
"loc": {
"start": {
"line": 1,
"column": 12
},
"end": {
"line": 1,
"column": 13
}
}
},
{
"type": "CommentBlock",
"value": "* @private ",
"start": 15,
"end": 30,
"loc": {
"start": {
"line": 3,
"column": 0
},
"end": {
"line": 3,
"column": 15
}
}
},
{
"type": {
"label": "export",
"keyword": "export",
"beforeExpr": false,
"startsExpr": false,
"rightAssociative": false,
"isLoop": false,
"isAssign": false,
"prefix": false,
"postfix": false,
"binop": null,
"updateContext": null
},
"value": "export",
"start": 31,
"end": 37,
"loc": {
"start": {
"line": 4,
"column": 0
},
"end": {
"line": 4,
"column": 6
}
}
},
{
"type": {
"label": "default",
"keyword": "default",
"beforeExpr": true,
"startsExpr": false,
"rightAssociative": false,
"isLoop": false,
"isAssign": false,
"prefix": false,
"postfix": false,
"binop": null,
"updateContext": null
},
"value": "default",
"start": 38,
"end": 45,
"loc": {
"start": {
"line": 4,
"column": 7
},
"end": {
"line": 4,
"column": 14
}
}
},
{
"type": {
"label": "(",
"beforeExpr": true,
"startsExpr": true,
"rightAssociative": false,
"isLoop": false,
"isAssign": false,
"prefix": false,
"postfix": false,
"binop": null
},
"start": 46,
"end": 47,
"loc": {
"start": {
"line": 4,
"column": 15
},
"end": {
"line": 4,
"column": 16
}
}
},
{
"type": {
"label": "name",
"beforeExpr": false,
"startsExpr": true,
"rightAssociative": false,
"isLoop": false,
"isAssign": false,
"prefix": false,
"postfix": false,
"binop": null
},
"value": "transitionRules",
"start": 47,
"end": 62,
"loc": {
"start": {
"line": 4,
"column": 16
},
"end": {
"line": 4,
"column": 31
}
}
},
{
"type": {
"label": ",",
"beforeExpr": true,
"startsExpr": false,
"rightAssociative": false,
"isLoop": false,
"isAssign": false,
"prefix": false,
"postfix": false,
"binop": null,
"updateContext": null
},
"start": 62,
"end": 63,
"loc": {
"start": {
"line": 4,
"column": 31
},
"end": {
"line": 4,
"column": 32
}
}
},
{
"type": {
"label": "name",
"beforeExpr": false,
"startsExpr": true,
"rightAssociative": false,
"isLoop": false,
"isAssign": false,
"prefix": false,
"postfix": false,
"binop": null
},
"value": "ctx",
"start": 64,
"end": 67,
"loc": {
"start": {
"line": 4,
"column": 33
},
"end": {
"line": 4,
"column": 36
}
}
},
{
"type": {
"label": ")",
"beforeExpr": false,
"startsExpr": false,
"rightAssociative": false,
"isLoop": false,
"isAssign": false,
"prefix": false,
"postfix": false,
"binop": null
},
"start": 67,
"end": 68,
"loc": {
"start": {
"line": 4,
"column": 36
},
"end": {
"line": 4,
"column": 37
}
}
},
{
"type": {
"label": "=>",
"beforeExpr": true,
"startsExpr": false,
"rightAssociative": false,
"isLoop": false,
"isAssign": false,
"prefix": false,
"postfix": false,
"binop": null,
"updateContext": null
},
"start": 69,
"end": 71,
"loc": {
"start": {
"line": 4,
"column": 38
},
"end": {
"line": 4,
"column": 40
}
}
},
{
"type": {
"label": "{",
"beforeExpr": true,
"startsExpr": true,
"rightAssociative": false,
"isLoop": false,
"isAssign": false,
"prefix": false,
"postfix": false,
"binop": null
},
"start": 72,
"end": 73,
"loc": {
"start": {
"line": 4,
"column": 41
},
"end": {
"line": 4,
"column": 42
}
}
},
{
"type": {
"label": "const",
"keyword": "const",
"beforeExpr": false,
"startsExpr": false,
"rightAssociative": false,
"isLoop": false,
"isAssign": false,
"prefix": false,
"postfix": false,
"binop": null,
"updateContext": null
},
"value": "const",
"start": 75,
"end": 80,
"loc": {
"start": {
"line": 5,
"column": 1
},
"end": {
"line": 5,
"column": 6
}
}
},
{
"type": {
"label": "name",
"beforeExpr": false,
"startsExpr": true,
"rightAssociative": false,
"isLoop": false,
"isAssign": false,
"prefix": false,
"postfix": false,
"binop": null
},
"value": "rules",
"start": 81,
"end": 86,
"loc": {
"start": {
"line": 5,
"column": 7
},
"end": {
"line": 5,
"column": 12
}
}
},
{
"type": {
"label": "=",
"beforeExpr": true,
"startsExpr": false,
"rightAssociative": false,
"isLoop": false,
"isAssign": true,
"prefix": false,
"postfix": false,
"binop": null,
"updateContext": null
},
"value": "=",
"start": 87,
"end": 88,
"loc": {
"start": {
"line": 5,
"column": 13
},
"end": {
"line": 5,
"column": 14
}
}
},
{
"type": {
"label": "name",
"beforeExpr": false,
"startsExpr": true,
"rightAssociative": false,
"isLoop": false,
"isAssign": false,
"prefix": false,
"postfix": false,
"binop": null
},
"value": "Array",
"start": 89,
"end": 94,
"loc": {
"start": {
"line": 5,
"column": 15
},
"end": {
"line": 5,
"column": 20
}
}
},
{
"type": {
"label": ".",
"beforeExpr": false,
"startsExpr": false,
"rightAssociative": false,
"isLoop": false,
"isAssign": false,
"prefix": false,
"postfix": false,
"binop": null,
"updateContext": null
},
"start": 94,
"end": 95,
"loc": {
"start": {
"line": 5,
"column": 20
},
"end": {
"line": 5,
"column": 21
}
}
},
{
"type": {
"label": "name",
"beforeExpr": false,
"startsExpr": true,
"rightAssociative": false,
"isLoop": false,
"isAssign": false,
"prefix": false,
"postfix": false,
"binop": null
},
"value": "isArray",
"start": 95,
"end": 102,
"loc": {
"start": {
"line": 5,
"column": 21
},
"end": {
"line": 5,
"column": 28
}
}
},
{
"type": {
"label": "(",
"beforeExpr": true,
"startsExpr": true,
"rightAssociative": false,
"isLoop": false,
"isAssign": false,
"prefix": false,
"postfix": false,
"binop": null
},
"start": 102,
"end": 103,
"loc": {
"start": {
"line": 5,
"column": 28
},
"end": {
"line": 5,
"column": 29
}
}
},
{
"type": {
"label": "name",
"beforeExpr": false,
"startsExpr": true,
"rightAssociative": false,
"isLoop": false,
"isAssign": false,
"prefix": false,
"postfix": false,
"binop": null
},
"value": "transitionRules",
"start": 103,
"end": 118,
"loc": {
"start": {
"line": 5,
"column": 29
},
"end": {
"line": 5,
"column": 44
}
}
},
{
"type": {
"label": ")",
"beforeExpr": false,
"startsExpr": false,
"rightAssociative": false,
"isLoop": false,
"isAssign": false,
"prefix": false,
"postfix": false,
"binop": null
},
"start": 118,
"end": 119,
"loc": {
"start": {
"line": 5,
"column": 44
},
"end": {
"line": 5,
"column": 45
}
}
},
{
"type": {
"label": "?",
"beforeExpr": true,
"startsExpr": false,
"rightAssociative": false,
"isLoop": false,
"isAssign": false,
"prefix": false,
"postfix": false,
"binop": null,
"updateContext": null
},
"start": 120,
"end": 121,
"loc": {
"start": {
"line": 5,
"column": 46
},
"end": {
"line": 5,
"column": 47
}
}
},
{
"type": {
"label": "name",
"beforeExpr": false,
"startsExpr": true,
"rightAssociative": false,
"isLoop": false,
"isAssign": false,
"prefix": false,
"postfix": false,
"binop": null
},
"value": "transitionRules",
"start": 122,
"end": 137,
"loc": {
"start": {
"line": 5,
"column": 48
},
"end": {
"line": 5,
"column": 63
}
}
},
{
"type": {
"label": ":",
"beforeExpr": true,
"startsExpr": false,
"rightAssociative": false,
"isLoop": false,
"isAssign": false,
"prefix": false,
"postfix": false,
"binop": null,
"updateContext": null
},
"start": 138,
"end": 139,
"loc": {
"start": {
"line": 5,
"column": 64
},
"end": {
"line": 5,
"column": 65
}
}
},
{
"type": {
"label": "[",
"beforeExpr": true,
"startsExpr": true,
"rightAssociative": false,
"isLoop": false,
"isAssign": false,
"prefix": false,
"postfix": false,
"binop": null,
"updateContext": null
},
"start": 140,
"end": 141,
"loc": {
"start": {
"line": 5,
"column": 66
},
"end": {
"line": 5,
"column": 67
}
}
},
{
"type": {
"label": "name",
"beforeExpr": false,
"startsExpr": true,
"rightAssociative": false,
"isLoop": false,
"isAssign": false,
"prefix": false,
"postfix": false,
"binop": null
},
"value": "transitionRules",
"start": 141,
"end": 156,
"loc": {
"start": {
"line": 5,
"column": 67
},
"end": {
"line": 5,
"column": 82
}
}
},
{
"type": {
"label": "]",
"beforeExpr": false,
"startsExpr": false,
"rightAssociative": false,
"isLoop": false,
"isAssign": false,
"prefix": false,
"postfix": false,
"binop": null,
"updateContext": null
},
"start": 156,
"end": 157,
"loc": {
"start": {
"line": 5,
"column": 82
},
"end": {
"line": 5,
"column": 83
}
}
},
{
"type": {
"label": ";",
"beforeExpr": true,
"startsExpr": false,
"rightAssociative": false,
"isLoop": false,
"isAssign": false,
"prefix": false,
"postfix": false,
"binop": null,
"updateContext": null
},
"start": 157,
"end": 158,
"loc": {
"start": {
"line": 5,
"column": 83
},
"end": {
"line": 5,
"column": 84
}
}
},
{
"type": {
"label": "for",
"keyword": "for",
"beforeExpr": false,
"startsExpr": false,
"rightAssociative": false,
"isLoop": true,
"isAssign": false,
"prefix": false,
"postfix": false,
"binop": null,
"updateContext": null
},
"value": "for",
"start": 161,
"end": 164,
"loc": {
"start": {
"line": 7,
"column": 1
},
"end": {
"line": 7,
"column": 4
}
}
},
{
"type": {
"label": "(",
"beforeExpr": true,
"startsExpr": true,
"rightAssociative": false,
"isLoop": false,
"isAssign": false,
"prefix": false,
"postfix": false,
"binop": null
},
"start": 165,
"end": 166,
"loc": {
"start": {
"line": 7,
"column": 5
},
"end": {
"line": 7,
"column": 6
}
}
},
{
"type": {
"label": "const",
"keyword": "const",
"beforeExpr": false,
"startsExpr": false,
"rightAssociative": false,
"isLoop": false,
"isAssign": false,
"prefix": false,
"postfix": false,
"binop": null,
"updateContext": null
},
"value": "const",
"start": 166,
"end": 171,
"loc": {
"start": {
"line": 7,
"column": 6
},
"end": {
"line": 7,
"column": 11
}
}
},
{
"type": {
"label": "name",
"beforeExpr": false,
"startsExpr": true,
"rightAssociative": false,
"isLoop": false,
"isAssign": false,
"prefix": false,
"postfix": false,
"binop": null
},
"value": "rule",
"start": 172,
"end": 176,
"loc": {
"start": {
"line": 7,
"column": 12
},
"end": {
"line": 7,
"column": 16
}
}
},
{
"type": {
"label": "name",
"beforeExpr": false,
"startsExpr": true,
"rightAssociative": false,
"isLoop": false,
"isAssign": false,
"prefix": false,
"postfix": false,
"binop": null
},
"value": "of",
"start": 177,
"end": 179,
"loc": {
"start": {
"line": 7,
"column": 17
},
"end": {
"line": 7,
"column": 19
}
}
},
{
"type": {
"label": "name",
"beforeExpr": false,
"startsExpr": true,
"rightAssociative": false,
"isLoop": false,
"isAssign": false,
"prefix": false,
"postfix": false,
"binop": null
},
"value": "rules",
"start": 180,
"end": 185,
"loc": {
"start": {
"line": 7,
"column": 20
},
"end": {
"line": 7,
"column": 25
}
}
},
{
"type": {
"label": ")",
"beforeExpr": false,
"startsExpr": false,
"rightAssociative": false,
"isLoop": false,
"isAssign": false,
"prefix": false,
"postfix": false,
"binop": null
},
"start": 185,
"end": 186,
"loc": {
"start": {
"line": 7,
"column": 25
},
"end": {
"line": 7,
"column": 26
}
}
},
{
"type": {
"label": "{",
"beforeExpr": true,
"startsExpr": true,
"rightAssociative": false,
"isLoop": false,
"isAssign": false,
"prefix": false,
"postfix": false,
"binop": null
},
"start": 187,
"end": 188,
"loc": {
"start": {
"line": 7,
"column": 27
},
"end": {
"line": 7,
"column": 28
}
}
},
{
"type": {
"label": "if",
"keyword": "if",
"beforeExpr": false,
"startsExpr": false,
"rightAssociative": false,
"isLoop": false,
"isAssign": false,
"prefix": false,
"postfix": false,
"binop": null,
"updateContext": null
},
"value": "if",
"start": 191,
"end": 193,
"loc": {
"start": {
"line": 8,
"column": 2
},
"end": {
"line": 8,
"column": 4
}
}
},
{
"type": {
"label": "(",
"beforeExpr": true,
"startsExpr": true,
"rightAssociative": false,
"isLoop": false,
"isAssign": false,
"prefix": false,
"postfix": false,
"binop": null
},
"start": 194,
"end": 195,
"loc": {
"start": {
"line": 8,
"column": 5
},
"end": {
"line": 8,
"column": 6
}
}
},
{
"type": {
"label": "name",
"beforeExpr": false,
"startsExpr": true,
"rightAssociative": false,
"isLoop": false,
"isAssign": false,
"prefix": false,
"postfix": false,
"binop": null
},
"value": "rule",
"start": 195,
"end": 199,
"loc": {
"start": {
"line": 8,
"column": 6
},
"end": {
"line": 8,
"column": 10
}
}
},
{
"type": {
"label": ")",
"beforeExpr": false,
"startsExpr": false,
"rightAssociative": false,
"isLoop": false,
"isAssign": false,
"prefix": false,
"postfix": false,
"binop": null
},
"start": 199,
"end": 200,
"loc": {
"start": {
"line": 8,
"column": 10
},
"end": {
"line": 8,
"column": 11
}
}
},
{
"type": {
"label": "{",
"beforeExpr": true,
"startsExpr": true,
"rightAssociative": false,
"isLoop": false,
"isAssign": false,
"prefix": false,
"postfix": false,
"binop": null
},
"start": 201,
"end": 202,
"loc": {
"start": {
"line": 8,
"column": 12
},
"end": {
"line": 8,
"column": 13
}
}
},
{
"type": {
"label": "try",
"keyword": "try",
"beforeExpr": false,
"startsExpr": false,
"rightAssociative": false,
"isLoop": false,
"isAssign": false,
"prefix": false,
"postfix": false,
"binop": null,
"updateContext": null
},
"value": "try",
"start": 206,
"end": 209,
"loc": {
"start": {
"line": 9,
"column": 3
},
"end": {
"line": 9,
"column": 6
}
}
},
{
"type": {
"label": "{",
"beforeExpr": true,
"startsExpr": true,
"rightAssociative": false,
"isLoop": false,
"isAssign": false,
"prefix": false,
"postfix": false,
"binop": null
},
"start": 210,
"end": 211,
"loc": {
"start": {
"line": 9,
"column": 7
},
"end": {
"line": 9,
"column": 8
}
}
},
{
"type": {
"label": "name",
"beforeExpr": false,
"startsExpr": true,
"rightAssociative": false,
"isLoop": false,
"isAssign": false,
"prefix": false,
"postfix": false,
"binop": null
},
"value": "rule",
"start": 212,
"end": 216,
"loc": {
"start": {
"line": 9,
"column": 9
},
"end": {
"line": 9,
"column": 13
}
}
},
{
"type": {
"label": "(",
"beforeExpr": true,
"startsExpr": true,
"rightAssociative": false,
"isLoop": false,
"isAssign": false,
"prefix": false,
"postfix": false,
"binop": null
},
"start": 216,
"end": 217,
"loc": {
"start": {
"line": 9,
"column": 13
},
"end": {
"line": 9,
"column": 14
}
}
},
{
"type": {
"label": "name",
"beforeExpr": false,
"startsExpr": true,
"rightAssociative": false,
"isLoop": false,
"isAssign": false,
"prefix": false,
"postfix": false,
"binop": null
},
"value": "ctx",
"start": 217,
"end": 220,
"loc": {
"start": {
"line": 9,
"column": 14
},
"end": {
"line": 9,
"column": 17
}
}
},
{
"type": {
"label": ")",
"beforeExpr": false,
"startsExpr": false,
"rightAssociative": false,
"isLoop": false,
"isAssign": false,
"prefix": false,
"postfix": false,
"binop": null
},
"start": 220,
"end": 221,
"loc": {
"start": {
"line": 9,
"column": 17
},
"end": {
"line": 9,
"column": 18
}
}
},
{
"type": {
"label": ";",
"beforeExpr": true,
"startsExpr": false,
"rightAssociative": false,
"isLoop": false,
"isAssign": false,
"prefix": false,
"postfix": false,
"binop": null,
"updateContext": null
},
"start": 221,
"end": 222,
"loc": {
"start": {
"line": 9,
"column": 18
},
"end": {
"line": 9,
"column": 19
}
}
},
{
"type": {
"label": "}",
"beforeExpr": false,
"startsExpr": false,
"rightAssociative": false,
"isLoop": false,
"isAssign": false,
"prefix": false,
"postfix": false,
"binop": null
},
"start": 223,
"end": 224,
"loc": {
"start": {
"line": 9,
"column": 20
},
"end": {
"line": 9,
"column": 21
}
}
},
{
"type": {
"label": "catch",
"keyword": "catch",
"beforeExpr": false,
"startsExpr": false,
"rightAssociative": false,
"isLoop": false,
"isAssign": false,
"prefix": false,
"postfix": false,
"binop": null,
"updateContext": null
},
"value": "catch",
"start": 225,
"end": 230,
"loc": {
"start": {
"line": 9,
"column": 22
},
"end": {
"line": 9,
"column": 27
}
}
},
{
"type": {
"label": "(",
"beforeExpr": true,
"startsExpr": true,
"rightAssociative": false,
"isLoop": false,
"isAssign": false,
"prefix": false,
"postfix": false,
"binop": null
},
"start": 231,
"end": 232,
"loc": {
"start": {
"line": 9,
"column": 28
},
"end": {
"line": 9,
"column": 29
}
}
},
{
"type": {
"label": "name",
"beforeExpr": false,
"startsExpr": true,
"rightAssociative": false,
"isLoop": false,
"isAssign": false,
"prefix": false,
"postfix": false,
"binop": null
},
"value": "e",
"start": 232,
"end": 233,
"loc": {
"start": {
"line": 9,
"column": 29
},
"end": {
"line": 9,
"column": 30
}
}
},
{
"type": {
"label": ")",
"beforeExpr": false,
"startsExpr": false,
"rightAssociative": false,
"isLoop": false,
"isAssign": false,
"prefix": false,
"postfix": false,
"binop": null
},
"start": 233,
"end": 234,
"loc": {
"start": {
"line": 9,
"column": 30
},
"end": {
"line": 9,
"column": 31
}
}
},
{
"type": {
"label": "{",
"beforeExpr": true,
"startsExpr": true,
"rightAssociative": false,
"isLoop": false,
"isAssign": false,
"prefix": false,
"postfix": false,
"binop": null
},
"start": 235,
"end": 236,
"loc": {
"start": {
"line": 9,
"column": 32
},
"end": {
"line": 9,
"column": 33
}
}
},
{
"type": {
"label": "return",
"keyword": "return",
"beforeExpr": true,
"startsExpr": false,
"rightAssociative": false,
"isLoop": false,
"isAssign": false,
"prefix": false,
"postfix": false,
"binop": null,
"updateContext": null
},
"value": "return",
"start": 241,
"end": 247,
"loc": {
"start": {
"line": 10,
"column": 4
},
"end": {
"line": 10,
"column": 10
}
}
},
{
"type": {
"label": "name",
"beforeExpr": false,
"startsExpr": true,
"rightAssociative": false,
"isLoop": false,
"isAssign": false,
"prefix": false,
"postfix": false,
"binop": null
},
"value": "e",
"start": 248,
"end": 249,
"loc": {
"start": {
"line": 10,
"column": 11
},
"end": {
"line": 10,
"column": 12
}
}
},
{
"type": {
"label": ";",
"beforeExpr": true,
"startsExpr": false,
"rightAssociative": false,
"isLoop": false,
"isAssign": false,
"prefix": false,
"postfix": false,
"binop": null,
"updateContext": null
},
"start": 249,
"end": 250,
"loc": {
"start": {
"line": 10,
"column": 12
},
"end": {
"line": 10,
"column": 13
}
}
},
{
"type": {
"label": "}",
"beforeExpr": false,
"startsExpr": false,
"rightAssociative": false,
"isLoop": false,
"isAssign": false,
"prefix": false,
"postfix": false,
"binop": null
},
"start": 254,
"end": 255,
"loc": {
"start": {
"line": 11,
"column": 3
},
"end": {
"line": 11,
"column": 4
}
}
},
{
"type": {
"label": "}",
"beforeExpr": false,
"startsExpr": false,
"rightAssociative": false,
"isLoop": false,
"isAssign": false,
"prefix": false,
"postfix": false,
"binop": null
},
"start": 258,
"end": 259,
"loc": {
"start": {
"line": 12,
"column": 2
},
"end": {
"line": 12,
"column": 3
}
}
},
{
"type": {
"label": "}",
"beforeExpr": false,
"startsExpr": false,
"rightAssociative": false,
"isLoop": false,
"isAssign": false,
"prefix": false,
"postfix": false,
"binop": null
},
"start": 261,
"end": 262,
"loc": {
"start": {
"line": 13,
"column": 1
},
"end": {
"line": 13,
"column": 2
}
}
},
{
"type": {
"label": "return",
"keyword": "return",
"beforeExpr": true,
"startsExpr": false,
"rightAssociative": false,
"isLoop": false,
"isAssign": false,
"prefix": false,
"postfix": false,
"binop": null,
"updateContext": null
},
"value": "return",
"start": 265,
"end": 271,
"loc": {
"start": {
"line": 15,
"column": 1
},
"end": {
"line": 15,
"column": 7
}
}
},
{
"type": {
"label": "name",
"beforeExpr": false,
"startsExpr": true,
"rightAssociative": false,
"isLoop": false,
"isAssign": false,
"prefix": false,
"postfix": false,
"binop": null
},
"value": "undefined",
"start": 272,
"end": 281,
"loc": {
"start": {
"line": 15,
"column": 8
},
"end": {
"line": 15,
"column": 17
}
}
},
{
"type": {
"label": ";",
"beforeExpr": true,
"startsExpr": false,
"rightAssociative": false,
"isLoop": false,
"isAssign": false,
"prefix": false,
"postfix": false,
"binop": null,
"updateContext": null
},
"start": 281,
"end": 282,
"loc": {
"start": {
"line": 15,
"column": 17
},
"end": {
"line": 15,
"column": 18
}
}
},
{
"type": {
"label": "}",
"beforeExpr": false,
"startsExpr": false,
"rightAssociative": false,
"isLoop": false,
"isAssign": false,
"prefix": false,
"postfix": false,
"binop": null
},
"start": 283,
"end": 284,
"loc": {
"start": {
"line": 16,
"column": 0
},
"end": {
"line": 16,
"column": 1
}
}
},
{
"type": {
"label": ";",
"beforeExpr": true,
"startsExpr": false,
"rightAssociative": false,
"isLoop": false,
"isAssign": false,
"prefix": false,
"postfix": false,
"binop": null,
"updateContext": null
},
"start": 284,
"end": 285,
"loc": {
"start": {
"line": 16,
"column": 1
},
"end": {
"line": 16,
"column": 2
}
}
},
{
"type": {
"label": "eof",
"beforeExpr": false,
"startsExpr": false,
"rightAssociative": false,
"isLoop": false,
"isAssign": false,
"prefix": false,
"postfix": false,
"binop": null,
"updateContext": null
},
"start": 286,
"end": 286,
"loc": {
"start": {
"line": 17,
"column": 0
},
"end": {
"line": 17,
"column": 0
}
}
}
]
}

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<base data-ice="baseUrl" href="../../">
<title data-ice="title">lib/execute-transition-rules.js | API Document</title>
<link type="text/css" rel="stylesheet" href="css/style.css">
<link type="text/css" rel="stylesheet" href="css/prettify-tomorrow.css">
<script src="script/prettify/prettify.js"></script>
<script src="script/manual.js"></script>
</head>
<body class="layout-container" data-ice="rootContainer">
<header>
<a href="./">Home</a>
<a href="identifiers.html">Reference</a>
<a href="source.html">Source</a>
<div class="search-box">
<span>
<img src="./image/search.png">
<span class="search-input-edge"></span><input class="search-input"><span class="search-input-edge"></span>
</span>
<ul class="search-result"></ul>
</div>
</header>
<nav class="navigation" data-ice="nav"><div>
<ul>
<li data-ice="doc"><span data-ice="kind" class="kind-class">C</span><span data-ice="name"><span><a href="class/lib/composit-state.js~CompositState.html">CompositState</a></span></span></li>
<li data-ice="doc"><span data-ice="kind" class="kind-class">C</span><span data-ice="name"><span><a href="class/lib/fsm.js~FSM.html">FSM</a></span></span></li>
</ul>
</div>
</nav>
<div class="content" data-ice="content"><h1 data-ice="title">lib/execute-transition-rules.js</h1>
<pre class="source-code line-number raw-source-code"><code class="prettyprint linenums" data-ice="content">&apos;use strict&apos;;
/** @private */
module.exports = (transitionRules, ctx) =&gt; {
const rules = Array.isArray(transitionRules) ? transitionRules : [transitionRules];
for (const rule of rules) {
if (rule) {
try { rule(ctx); } catch (e) {
return e;
}
}
}
return undefined;
};
</code></pre>
</div>
<footer class="footer">
Generated by <a href="https://esdoc.org">ESDoc<span data-ice="esdocVersion">(0.5.2)</span><img src="./image/esdoc-logo-mini-black.png"></a>
</footer>
<script src="script/search_index.js"></script>
<script src="script/search.js"></script>
<script src="script/pretty-print.js"></script>
<script src="script/inherited-summary.js"></script>
<script src="script/test-summary.js"></script>
<script src="script/inner-link.js"></script>
<script src="script/patch-for-local.js"></script>
</body>
</html>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<base data-ice="baseUrl" href="../../">
<title data-ice="title">lib/execute-transition-rules.specs.js | API Document</title>
<link type="text/css" rel="stylesheet" href="css/style.css">
<link type="text/css" rel="stylesheet" href="css/prettify-tomorrow.css">
<script src="script/prettify/prettify.js"></script>
<script src="script/manual.js"></script>
</head>
<body class="layout-container" data-ice="rootContainer">
<header>
<a href="./">Home</a>
<a href="identifiers.html">Reference</a>
<a href="source.html">Source</a>
<div class="search-box">
<span>
<img src="./image/search.png">
<span class="search-input-edge"></span><input class="search-input"><span class="search-input-edge"></span>
</span>
<ul class="search-result"></ul>
</div>
</header>
<nav class="navigation" data-ice="nav"><div>
<ul>
<li data-ice="doc"><span data-ice="kind" class="kind-class">C</span><span data-ice="name"><span><a href="class/lib/composit-state.js~CompositState.html">CompositState</a></span></span></li>
<li data-ice="doc"><span data-ice="kind" class="kind-class">C</span><span data-ice="name"><span><a href="class/lib/fsm.js~FSM.html">FSM</a></span></span></li>
</ul>
</div>
</nav>
<div class="content" data-ice="content"><h1 data-ice="title">lib/execute-transition-rules.specs.js</h1>
<pre class="source-code line-number raw-source-code"><code class="prettyprint linenums" data-ice="content">&apos;use strict&apos;;
/* eslint-env node, mocha */
/* eslint no-unused-expressions: 0, arrow-body-style: 0 */
const { expect } = require(&apos;chai&apos;);
const executeTransitionRules = require(&apos;./execute-transition-rules&apos;);
describe(&apos;execute-transition-rules&apos;, () =&gt; {
it(&apos;should return undefined if the transitionRules are not set&apos;, async () =&gt; {
const result = executeTransitionRules(undefined, {});
expect(result).to.be.undefined;
});
it(&apos;should return undefined if the transitionRules are a empty array&apos;, async () =&gt; {
const result = executeTransitionRules([], {});
expect(result).to.be.undefined;
});
it(&apos;should return undefined if the transitionRules are a satisfied&apos;, async () =&gt; {
const rules = [
() =&gt; {},
() =&gt; {},
() =&gt; {},
];
const result = executeTransitionRules(rules, {});
expect(result).to.be.undefined;
});
it(&apos;should return a error-string if the transitionRules are not satisfied&apos;, async () =&gt; {
const rules = [
() =&gt; {},
() =&gt; { throw new Error(&apos;some error&apos;); },
() =&gt; {},
];
const result = executeTransitionRules(rules, {});
expect(result.message).to.equal(&apos;some error&apos;);
});
});
</code></pre>
</div>
<footer class="footer">
Generated by <a href="https://esdoc.org">ESDoc<span data-ice="esdocVersion">(0.5.2)</span><img src="./image/esdoc-logo-mini-black.png"></a>
</footer>
<script src="script/search_index.js"></script>
<script src="script/search.js"></script>
<script src="script/pretty-print.js"></script>
<script src="script/inherited-summary.js"></script>
<script src="script/test-summary.js"></script>
<script src="script/inner-link.js"></script>
<script src="script/patch-for-local.js"></script>
</body>
</html>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<base data-ice="baseUrl" href="../../">
<title data-ice="title">lib/fsm.fluent-api.specs.js | API Document</title>
<link type="text/css" rel="stylesheet" href="css/style.css">
<link type="text/css" rel="stylesheet" href="css/prettify-tomorrow.css">
<script src="script/prettify/prettify.js"></script>
<script src="script/manual.js"></script>
</head>
<body class="layout-container" data-ice="rootContainer">
<header>
<a href="./">Home</a>
<a href="identifiers.html">Reference</a>
<a href="source.html">Source</a>
<div class="search-box">
<span>
<img src="./image/search.png">
<span class="search-input-edge"></span><input class="search-input"><span class="search-input-edge"></span>
</span>
<ul class="search-result"></ul>
</div>
</header>
<nav class="navigation" data-ice="nav"><div>
<ul>
<li data-ice="doc"><span data-ice="kind" class="kind-class">C</span><span data-ice="name"><span><a href="class/lib/composit-state.js~CompositState.html">CompositState</a></span></span></li>
<li data-ice="doc"><span data-ice="kind" class="kind-class">C</span><span data-ice="name"><span><a href="class/lib/fsm.js~FSM.html">FSM</a></span></span></li>
</ul>
</div>
</nav>
<div class="content" data-ice="content"><h1 data-ice="title">lib/fsm.fluent-api.specs.js</h1>
<pre class="source-code line-number raw-source-code"><code class="prettyprint linenums" data-ice="content">&apos;use strict&apos;;
/* eslint-env node, mocha */
/* eslint no-unused-expressions: 0, one-var-declaration-per-line: 0, one-var: 0, no-empty: 0 */
const bb = require(&apos;bluebird&apos;);
const FSM = require(&apos;./fsm&apos;);
const { expect } = require(&apos;chai&apos;);
const compositState = require(&apos;./composit-state&apos;);
describe(&apos;FSM&apos;, () =&gt; {
describe(&apos;fluent build API&apos;, () =&gt; {
it(&apos;can add transition&apos;, async () =&gt; {
const f = new FSM({});
f.addTransition({ name: &apos;trans&apos;, from: &apos;a&apos;, to: &apos;b&apos; })
.addTransition({ name: &apos;trans2&apos;, from: [&apos;a&apos;, &apos;b&apos;], to: &apos;c&apos; })
.init(&apos;a&apos;);
await f.trans();
expect(f.state).to.equal(&apos;b&apos;);
await f.trans2();
expect(f.state).to.equal(&apos;c&apos;);
});
it(&apos;can add transition array&apos;, async () =&gt; {
const f = new FSM({});
f.addTransition([{ name: &apos;trans&apos;, from: &apos;a&apos;, to: &apos;b&apos; },
{ name: &apos;trans2&apos;, from: [&apos;a&apos;, &apos;b&apos;], to: &apos;c&apos; }])
.init(&apos;a&apos;);
await f.trans();
expect(f.state).to.equal(&apos;b&apos;);
await f.trans2();
expect(f.state).to.equal(&apos;c&apos;);
});
it(&apos;validates transitions&apos;, () =&gt; {
const f = new FSM({});
expect(() =&gt; f.addTransition({})).to.throw(/Invalid transition/);
});
it(&apos;does not allow trasition name clashes&apos;, () =&gt; {
const f = new FSM({});
f.addTransition({ name: &apos;a-to-b&apos;, from: &apos;a&apos;, to: &apos;b&apos; });
expect(() =&gt; f.addTransition({ name: &apos;a:to:b&apos;, from: &apos;a&apos;, to: &apos;b&apos; })).to.throw(/Ambigious transtion name/);
});
it(&apos;does not allow transiion named like native FSM methods&apos;, () =&gt; {
const f = new FSM({});
expect(() =&gt; f.addTransition({ name: &apos;execute&apos;, from: &apos;a&apos;, to: &apos;b&apos; })).to.throw(/Forbidden transition name/);
expect(() =&gt; f.addTransition({ name: &apos;add:transition&apos;, from: &apos;a&apos;, to: &apos;b&apos; })).to.throw(/Forbidden transition name/);
});
});
});
</code></pre>
</div>
<footer class="footer">
Generated by <a href="https://esdoc.org">ESDoc<span data-ice="esdocVersion">(0.5.2)</span><img src="./image/esdoc-logo-mini-black.png"></a>
</footer>
<script src="script/search_index.js"></script>
<script src="script/search.js"></script>
<script src="script/pretty-print.js"></script>
<script src="script/inherited-summary.js"></script>
<script src="script/test-summary.js"></script>
<script src="script/inner-link.js"></script>
<script src="script/patch-for-local.js"></script>
</body>
</html>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<base data-ice="baseUrl" href="../../">
<title data-ice="title">lib/fsm.rest-api.specs.js | API Document</title>
<link type="text/css" rel="stylesheet" href="css/style.css">
<link type="text/css" rel="stylesheet" href="css/prettify-tomorrow.css">
<script src="script/prettify/prettify.js"></script>
<script src="script/manual.js"></script>
</head>
<body class="layout-container" data-ice="rootContainer">
<header>
<a href="./">Home</a>
<a href="identifiers.html">Reference</a>
<a href="source.html">Source</a>
<div class="search-box">
<span>
<img src="./image/search.png">
<span class="search-input-edge"></span><input class="search-input"><span class="search-input-edge"></span>
</span>
<ul class="search-result"></ul>
</div>
</header>
<nav class="navigation" data-ice="nav"><div>
<ul>
<li data-ice="doc"><span data-ice="kind" class="kind-class">C</span><span data-ice="name"><span><a href="class/lib/composit-state.js~CompositState.html">CompositState</a></span></span></li>
<li data-ice="doc"><span data-ice="kind" class="kind-class">C</span><span data-ice="name"><span><a href="class/lib/fsm.js~FSM.html">FSM</a></span></span></li>
</ul>
</div>
</nav>
<div class="content" data-ice="content"><h1 data-ice="title">lib/fsm.rest-api.specs.js</h1>
<pre class="source-code line-number raw-source-code"><code class="prettyprint linenums" data-ice="content">&apos;use strict&apos;;
/* eslint-env node, mocha */
/* eslint no-unused-expressions: 0, one-var-declaration-per-line: 0, one-var: 0, no-empty: 0 */
const bb = require(&apos;bluebird&apos;);
const FSM = require(&apos;./fsm&apos;);
const { expect } = require(&apos;chai&apos;);
describe(&apos;FSM&apos;, () =&gt; {
describe(&apos;transition REST API&apos;, () =&gt; {
const data = {
resId: &apos;42&apos;,
_embedded: {
event: {
resId: &apos;22&apos;,
},
},
};
let fsm, cfg;
let savedContext;
beforeEach(() =&gt; {
cfg = {
initialState: &apos;a&apos;,
transitions: [{
name: &apos;t1&apos;,
from: &apos;*&apos;,
to: &apos;a&apos;,
api: {
path: &apos;/entity/trans&apos;,
method: &apos;patch&apos;,
},
}, {
name: &apos;t2&apos;,
from: &apos;*&apos;,
to: &apos;a&apos;,
api: {
path: &apos;/entity/{resId}/trans/{subId}&apos;,
method: &apos;patch&apos;,
params: {
resId: &apos;data.resId&apos;,
subId: &apos;data._embedded.event.resId&apos;,
},
},
}, {
name: &apos;t3&apos;,
from: &apos;*&apos;,
to: &apos;a&apos;,
}, {
name: &apos;t4&apos;,
from: &apos;*&apos;,
to: &apos;a&apos;,
api: {
path: &apos;/{entity}/{resId}/trans/{subId}&apos;,
method: &apos;patch&apos;,
params: {
resId: &apos;data.resId&apos;,
subId: &apos;data._embedded.event.resId&apos;,
},
},
}, {
name: &apos;t5&apos;,
from: &apos;*&apos;,
to: &apos;a&apos;,
api: {
path: &apos;/entity/trans&apos;,
method: &apos;patch&apos;,
},
rules: [
() =&gt; {},
() =&gt; {},
],
}, {
name: &apos;t6&apos;,
from: &apos;*&apos;,
to: &apos;a&apos;,
api: {
path: &apos;/entity/trans&apos;,
method: &apos;patch&apos;,
},
rules: [
() =&gt; {},
() =&gt; { throw new Error(&apos;some error&apos;); },
],
}, {
name: &apos;t7&apos;,
from: &apos;*&apos;,
to: &apos;a&apos;,
api: {
path: &apos;/entity/trans&apos;,
method: &apos;patch&apos;,
},
rules: [
(ctx) =&gt; { savedContext = ctx; },
],
}],
saveState: () =&gt; {},
willChangeState: () =&gt; bb.delay(5),
data,
};
});
it(&apos;exposes &quot;restApi()&quot; function&apos;, async () =&gt; {
fsm = new FSM(cfg);
expect(fsm.restApi).to.be.a(&apos;function&apos;);
});
it(&apos;filters transitions without &quot;api&quot; property&apos;, async () =&gt; {
fsm = new FSM(cfg);
const r = fsm.restApi();
expect(r.t1).to.exist;
expect(r.t2).to.exist;
expect(r.t3).not.to.exist;
});
it(&apos;returns parsed api object&apos;, async () =&gt; {
fsm = new FSM(cfg);
const r = fsm.restApi();
expect(r.t1).to.eql({
href: &apos;/entity/trans&apos;,
method: &apos;patch&apos;,
});
});
it(&apos;returns &quot;self&quot; when declared&apos;, async () =&gt; {
fsm = new FSM(Object.assign({}, cfg, {
api: {
self: {
path: &apos;/entity/{resId}&apos;,
},
params: {
resId: &apos;data.resId&apos;,
},
},
}));
const r = fsm.restApi();
expect(r.self).to.eql({
href: &apos;/entity/42&apos;,
method: &apos;get&apos;,
});
});
it(&apos;can mix global &amp; transition local params in same route&apos;, () =&gt; {
fsm = new FSM(Object.assign({}, cfg, {
api: {
data: {
entity: &apos;events&apos;,
},
self: {
path: &apos;/{entity}/{resId}&apos;,
},
params: {
entity: &apos;api.data.entity&apos;,
resId: &apos;data.resId&apos;,
},
},
}));
const r = fsm.restApi();
expect(r.t4).to.eql({
href: &apos;/events/42/trans/22&apos;,
method: &apos;patch&apos;,
});
});
it(&apos;can declare static params data in &quot;api.data&quot; object&apos;, () =&gt; {
fsm = new FSM(Object.assign({}, cfg, {
api: {
data: {
entity: &apos;events&apos;,
},
self: {
path: &apos;/{entity}/{resId}&apos;,
},
params: {
entity: &apos;api.data.entity&apos;,
resId: &apos;data.resId&apos;,
},
},
}));
const r = fsm.restApi();
expect(r.self).to.eql({
href: &apos;/events/42&apos;,
method: &apos;get&apos;,
});
});
it(&apos;should return the restApi object if all rules are satisfied&apos;, async () =&gt; {
fsm = new FSM(cfg);
const r = fsm.restApi();
expect(r.t5).to.eql({
href: &apos;/entity/trans&apos;,
method: &apos;patch&apos;,
});
});
it(&apos;should return a restApi object with a error message if not all rules are satisfied&apos;, async () =&gt; {
fsm = new FSM(cfg);
const r = fsm.restApi();
expect(r.t6).to.eql({
href: &apos;/entity/trans&apos;,
method: &apos;patch&apos;,
error: {
message: &apos;some error&apos;,
},
});
});
it(&apos;should pass the context of the fsm to the rules functions&apos;, async () =&gt; {
fsm = new FSM(cfg);
fsm.restApi();
expect(savedContext.data).to.eql(data);
});
it(&apos;should execute the rules functions on an fsm without a data object without any error&apos;, async () =&gt; {
const testCfg = {
initialState: &apos;a&apos;,
transitions: [
{
name: &apos;t&apos;,
from: &apos;*&apos;,
to: &apos;a&apos;,
api: {
path: &apos;/entity/trans&apos;,
method: &apos;patch&apos;,
},
rules: [
(ctx) =&gt; { savedContext = ctx; },
],
},
],
saveState: () =&gt; {},
willChangeState: () =&gt; bb.delay(5),
};
fsm = new FSM(testCfg);
const r = fsm.restApi();
expect(savedContext.data).to.be.undefined;
expect(r.t).to.eql({
href: &apos;/entity/trans&apos;,
method: &apos;patch&apos;,
});
});
});
});
</code></pre>
</div>
<footer class="footer">
Generated by <a href="https://esdoc.org">ESDoc<span data-ice="esdocVersion">(0.5.2)</span><img src="./image/esdoc-logo-mini-black.png"></a>
</footer>
<script src="script/search_index.js"></script>
<script src="script/search.js"></script>
<script src="script/pretty-print.js"></script>
<script src="script/inherited-summary.js"></script>
<script src="script/test-summary.js"></script>
<script src="script/inner-link.js"></script>
<script src="script/patch-for-local.js"></script>
</body>
</html>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<base data-ice="baseUrl" href="../../">
<title data-ice="title">lib/fsm.statics.specs.js | API Document</title>
<link type="text/css" rel="stylesheet" href="css/style.css">
<link type="text/css" rel="stylesheet" href="css/prettify-tomorrow.css">
<script src="script/prettify/prettify.js"></script>
<script src="script/manual.js"></script>
</head>
<body class="layout-container" data-ice="rootContainer">
<header>
<a href="./">Home</a>
<a href="identifiers.html">Reference</a>
<a href="source.html">Source</a>
<div class="search-box">
<span>
<img src="./image/search.png">
<span class="search-input-edge"></span><input class="search-input"><span class="search-input-edge"></span>
</span>
<ul class="search-result"></ul>
</div>
</header>
<nav class="navigation" data-ice="nav"><div>
<ul>
<li data-ice="doc"><span data-ice="kind" class="kind-class">C</span><span data-ice="name"><span><a href="class/lib/composit-state.js~CompositState.html">CompositState</a></span></span></li>
<li data-ice="doc"><span data-ice="kind" class="kind-class">C</span><span data-ice="name"><span><a href="class/lib/fsm.js~FSM.html">FSM</a></span></span></li>
</ul>
</div>
</nav>
<div class="content" data-ice="content"><h1 data-ice="title">lib/fsm.statics.specs.js</h1>
<pre class="source-code line-number raw-source-code"><code class="prettyprint linenums" data-ice="content">&apos;use strict&apos;;
/* eslint-env node, mocha */
/* eslint no-unused-expressions: 0, one-var-declaration-per-line: 0, one-var: 0, no-empty: 0 */
const FSM = require(&apos;./fsm&apos;);
const { expect } = require(&apos;chai&apos;);
const compositState = require(&apos;./composit-state&apos;);
describe(&apos;FSM&apos;, () =&gt; {
describe(&apos;statics&apos;, () =&gt; {
it(&apos;exposes compositState tool&apos;, () =&gt; {
expect(FSM.compositState).to.equal(compositState);
});
it(&apos;cannot set compositState&apos;, () =&gt; {
expect(() =&gt; { FSM.compositState = () =&gt; {}; }).to.throw();
});
it(&apos;exposes toFunctionName() helper&apos;, () =&gt; {
expect(FSM.toFunctionName(&apos;test:transition:oida&apos;)).to.equal(&apos;testTransitionOida&apos;);
});
});
});
</code></pre>
</div>
<footer class="footer">
Generated by <a href="https://esdoc.org">ESDoc<span data-ice="esdocVersion">(0.5.2)</span><img src="./image/esdoc-logo-mini-black.png"></a>
</footer>
<script src="script/search_index.js"></script>
<script src="script/search.js"></script>
<script src="script/pretty-print.js"></script>
<script src="script/inherited-summary.js"></script>
<script src="script/test-summary.js"></script>
<script src="script/inner-link.js"></script>
<script src="script/patch-for-local.js"></script>
</body>
</html>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<base data-ice="baseUrl" href="../../">
<title data-ice="title">lib/fsm.transition-execution.specs.js | API Document</title>
<link type="text/css" rel="stylesheet" href="css/style.css">
<link type="text/css" rel="stylesheet" href="css/prettify-tomorrow.css">
<script src="script/prettify/prettify.js"></script>
<script src="script/manual.js"></script>
</head>
<body class="layout-container" data-ice="rootContainer">
<header>
<a href="./">Home</a>
<a href="identifiers.html">Reference</a>
<a href="source.html">Source</a>
<div class="search-box">
<span>
<img src="./image/search.png">
<span class="search-input-edge"></span><input class="search-input"><span class="search-input-edge"></span>
</span>
<ul class="search-result"></ul>
</div>
</header>
<nav class="navigation" data-ice="nav"><div>
<ul>
<li data-ice="doc"><span data-ice="kind" class="kind-class">C</span><span data-ice="name"><span><a href="class/lib/composit-state.js~CompositState.html">CompositState</a></span></span></li>
<li data-ice="doc"><span data-ice="kind" class="kind-class">C</span><span data-ice="name"><span><a href="class/lib/fsm.js~FSM.html">FSM</a></span></span></li>
</ul>
</div>
</nav>
<div class="content" data-ice="content"><h1 data-ice="title">lib/fsm.transition-execution.specs.js</h1>
<pre class="source-code line-number raw-source-code"><code class="prettyprint linenums" data-ice="content">&apos;use strict&apos;;
/* eslint-env node, mocha */
/* eslint no-unused-expressions: 0, one-var-declaration-per-line: 0, one-var: 0, no-empty: 0 */
const bb = require(&apos;bluebird&apos;);
const FSM = require(&apos;./fsm&apos;);
const { expect } = require(&apos;chai&apos;);
describe(&apos;FSM&apos;, () =&gt; {
describe(&apos;saveState&apos;, () =&gt; {
it(&apos;can use async &quot;saveState&quot;&apos;, async () =&gt; {
let saved;
const fsm = new FSM({
initialState: &apos;a&apos;,
transitions: [{ name: &apos;trans&apos;, from: &apos;a&apos;, to: &apos;b&apos; }],
saveState: async (ctx) =&gt; {
await bb.delay(5);
saved = ctx.fsm.state;
},
});
await fsm.trans();
expect(saved).to.equal(&apos;b&apos;);
});
it(&apos;can use sync &quot;saveState&quot;&apos;, async () =&gt; {
let ctx, args;
const fsm = new FSM({
initialState: &apos;a&apos;,
transitions: [{ name: &apos;trans&apos;, from: &apos;a&apos;, to: &apos;b&apos; }],
saveState: (c, a) =&gt; {
ctx = c;
args = a;
},
});
await fsm.trans(42, &apos;test&apos;);
expect(ctx).to.eql({
from: &apos;a&apos;,
to: &apos;b&apos;,
transition: &apos;trans&apos;,
fsm,
results: {
willChangeState: null,
beforeTrans: null,
didChangeState: null,
willSaveState: null,
didSaveState: null,
saveState: undefined,
},
});
expect(args).to.eql([42, &apos;test&apos;]);
});
it(&apos;throw when exection is throw in sync &quot;saveState&quot;&apos;, async () =&gt; {
let error;
try {
const fsm = new FSM({
initialState: &apos;a&apos;,
transitions: [{ name: &apos;trans&apos;, from: &apos;a&apos;, to: &apos;b&apos; }],
saveState: () =&gt; {
throw new Error(&apos;save failed&apos;);
},
});
await fsm.trans();
} catch (e) {
error = e;
}
expect(error).to.exist;
expect(error.message).to.equal(&apos;save failed&apos;);
});
it(&apos;throw when exection is throw in async &quot;saveState&quot;&apos;, async () =&gt; {
let error;
try {
const fsm = new FSM({
initialState: &apos;a&apos;,
transitions: [{ name: &apos;trans&apos;, from: &apos;a&apos;, to: &apos;b&apos; }],
saveState: async () =&gt; {
await bb.delay(20);
throw new Error(&apos;save failed&apos;);
},
});
await fsm.trans();
} catch (e) {
error = e;
}
expect(error).to.exist;
expect(error.message).to.equal(&apos;save failed&apos;);
});
});
describe(&apos;transiton events&apos;, () =&gt; {
let fsm, cfg, ts;
const data = { test: &apos;data&apos; };
beforeEach(() =&gt; {
ts = [];
cfg = {
initialState: &apos;a&apos;,
data,
transitions: [{ name: &apos;trans&apos;, from: &apos;a&apos;, to: &apos;b&apos; }],
saveState: () =&gt; &apos;saveState&apos;,
willChangeState: async (ctx, a1, a2) =&gt; {
await bb.delay(5);
ts.push({ name: &apos;willChangeState&apos;, ctx, args: [a1, a2] });
return &apos;willChangeState&apos;;
},
didChangeState: async (ctx, ...args) =&gt; {
await bb.delay(5);
ts.push({ name: &apos;didChangeState&apos;, ctx, args });
return &apos;didChangeState&apos;;
},
willSaveState: async (ctx, ...args) =&gt; {
await bb.delay(5);
ts.push({ name: &apos;willSaveState&apos;, ctx, args });
return &apos;willSaveState&apos;;
},
didSaveState: async (ctx, ...args) =&gt; {
await bb.delay(5);
ts.push({ name: &apos;didSaveState&apos;, ctx, args });
return &apos;didSaveState&apos;;
},
eventHandler: {
beforeTrans: async (ctx, ...args) =&gt; {
await bb.delay(5);
ts.push({ name: &apos;beforeTrans&apos;, ctx, args });
return &apos;beforeTrans&apos;;
},
afterTrans: async (ctx, ...args) =&gt; {
await bb.delay(5);
ts.push({ name: &apos;afterTrans&apos;, ctx, args });
return &apos;afterTrans&apos;;
},
},
};
});
it(&apos;calls transition methods with &quot;ctx&quot; and args&apos;, async () =&gt; {
fsm = new FSM(cfg);
await fsm.trans(42, &apos;test&apos;);
expect(ts[0].ctx).to.eql({
from: &apos;a&apos;,
to: &apos;b&apos;,
transition: &apos;trans&apos;,
fsm,
data,
results: {
willChangeState: &apos;willChangeState&apos;,
beforeTrans: &apos;beforeTrans&apos;,
didChangeState: &apos;didChangeState&apos;,
willSaveState: &apos;willSaveState&apos;,
didSaveState: &apos;didSaveState&apos;,
saveState: &apos;saveState&apos;,
},
});
expect(ts[0].args).to.eql([42, &apos;test&apos;]);
});
it(&apos;with &quot;execute(trasnition)&quot; calls transition methods with &quot;ctx&quot; and args&apos;, async () =&gt; {
fsm = new FSM(cfg);
await fsm.execute(&apos;trans&apos;, 42, &apos;test&apos;);
expect(ts[0].ctx).to.eql({
from: &apos;a&apos;,
to: &apos;b&apos;,
transition: &apos;trans&apos;,
fsm,
data,
results: {
willChangeState: &apos;willChangeState&apos;,
beforeTrans: &apos;beforeTrans&apos;,
didChangeState: &apos;didChangeState&apos;,
willSaveState: &apos;willSaveState&apos;,
didSaveState: &apos;didSaveState&apos;,
saveState: &apos;saveState&apos;,
},
});
expect(ts[0].args).to.eql([42, &apos;test&apos;]);
});
it(&apos;calls transition events in order&apos;, async () =&gt; {
fsm = new FSM(cfg);
await fsm.trans();
expect(ts[0].name).to.equal(&apos;willChangeState&apos;);
expect(ts[1].name).to.equal(&apos;beforeTrans&apos;);
expect(ts[2].name).to.equal(&apos;didChangeState&apos;);
expect(ts[3].name).to.equal(&apos;willSaveState&apos;);
expect(ts[4].name).to.equal(&apos;didSaveState&apos;);
expect(ts[5].name).to.equal(&apos;afterTrans&apos;);
});
it(&apos;returns all transitionHanlder results&apos;, async () =&gt; {
fsm = new FSM(cfg);
const result = await fsm.trans();
expect(result).to.eql({
willChangeState: &apos;willChangeState&apos;,
beforeTrans: &apos;beforeTrans&apos;,
afterTrans: &apos;afterTrans&apos;,
didChangeState: &apos;didChangeState&apos;,
willSaveState: &apos;willSaveState&apos;,
saveState: &apos;saveState&apos;,
didSaveState: &apos;didSaveState&apos;,
});
});
it(&apos;throw error when beforeTransiton handler fails does not change state&apos;, async () =&gt; {
cfg.eventHandler.beforeTrans = async () =&gt; {
await bb.delay(5);
throw new Error(&apos;derdo&apos;);
};
let error;
try {
fsm = new FSM(cfg);
await fsm.trans();
} catch (e) {
error = e;
}
expect(error).to.exist;
expect(error.message).to.equal(&apos;derdo&apos;);
expect(ts[0].name).to.equal(&apos;willChangeState&apos;);
expect(ts.length).to.equal(1);
expect(fsm.state).to.equal(&apos;a&apos;);
});
it(&apos;throw error when afterTransiton handler fails changes state&apos;, async () =&gt; {
cfg.eventHandler.afterTrans = async () =&gt; {
await bb.delay(5);
throw new Error(&apos;derdo&apos;);
};
let error;
try {
fsm = new FSM(cfg);
await fsm.trans();
} catch (e) {
error = e;
}
expect(error).to.exist;
expect(error.message).to.equal(&apos;derdo&apos;);
expect(ts[0].name).to.equal(&apos;willChangeState&apos;);
expect(ts[1].name).to.equal(&apos;beforeTrans&apos;);
expect(ts[2].name).to.equal(&apos;didChangeState&apos;);
expect(ts[3].name).to.equal(&apos;willSaveState&apos;);
expect(ts[4].name).to.equal(&apos;didSaveState&apos;);
expect(ts.length).to.equal(5);
expect(fsm.state).to.equal(&apos;b&apos;);
});
});
describe(&apos;global transition events&apos;, () =&gt; {
let fsm, cfg, ts;
beforeEach(() =&gt; {
ts = [];
cfg = {
initialState: &apos;a&apos;,
transitions: [{ name: &apos;trans&apos;, from: &apos;a&apos;, to: &apos;b&apos; }],
saveState: () =&gt; {},
willChangeState: (ctx, ...args) =&gt; {
ts.push({ name: &apos;willChangeState&apos;, ctx, args });
},
didChangeState: (ctx, ...args) =&gt; {
ts.push({ name: &apos;didChangeState&apos;, ctx, args });
},
willSaveState: (ctx, ...args) =&gt; {
ts.push({ name: &apos;willSaveState&apos;, ctx, args });
},
didSaveState: (ctx, ...args) =&gt; {
ts.push({ name: &apos;didSaveState&apos;, ctx, args });
},
};
});
it(&apos;calls transition events in order&apos;, async () =&gt; {
fsm = new FSM(cfg);
await fsm.trans();
expect(ts[0].name).to.equal(&apos;willChangeState&apos;);
expect(ts[1].name).to.equal(&apos;didChangeState&apos;);
expect(ts[2].name).to.equal(&apos;willSaveState&apos;);
expect(ts[3].name).to.equal(&apos;didSaveState&apos;);
});
it(&apos;calls will &amp; didSaveState only called when saveState is defined&apos;, async () =&gt; {
delete cfg.saveState;
fsm = new FSM(cfg);
await fsm.trans();
expect(ts[0].name).to.equal(&apos;willChangeState&apos;);
expect(ts[1].name).to.equal(&apos;didChangeState&apos;);
expect(ts.length).to.equal(2);
});
it(&apos;throw error when willChangeState &amp; state does not change&apos;, async () =&gt; {
cfg.willChangeState = () =&gt; {
throw new Error(&apos;derdo&apos;);
};
let error;
try {
fsm = new FSM(cfg);
await fsm.trans();
} catch (e) {
error = e;
}
expect(error).to.exist;
expect(error.message).to.equal(&apos;derdo&apos;);
expect(fsm.state).to.equal(&apos;a&apos;);
});
it(&apos;throw error when didChangeState &amp; state changes&apos;, async () =&gt; {
cfg.didChangeState = () =&gt; {
throw new Error(&apos;derdo&apos;);
};
let error;
try {
fsm = new FSM(cfg);
await fsm.trans();
} catch (e) {
error = e;
}
expect(error).to.exist;
expect(error.message).to.equal(&apos;derdo&apos;);
expect(fsm.state).to.equal(&apos;b&apos;);
});
});
describe(&apos;race conditions&apos;, () =&gt; {
let fsm, cfg;
beforeEach(() =&gt; {
cfg = {
initialState: &apos;a&apos;,
transitions: [{ name: &apos;trans&apos;, from: [&apos;a&apos;, &apos;b&apos;], to: &apos;b&apos; }, { name: &apos;trans2&apos;, from: [&apos;a&apos;, &apos;b&apos;], to: &apos;a&apos; }],
saveState: () =&gt; {},
willChangeState: () =&gt; bb.delay(5),
};
});
it(&apos;cannot start transition while anothr one is running&apos;, (done) =&gt; {
fsm = new FSM(cfg);
fsm.trans();
fsm.trans2().then(() =&gt; done(new Error(&apos;should have failed&apos;))).catch((err) =&gt; {
expect(err.message).to.equal(&apos;Cannot start transition when during running transition&apos;);
done();
});
});
it(&apos;can run transition sequential&apos;, async () =&gt; {
fsm = new FSM(cfg);
await fsm.trans();
await fsm.trans2();
});
it(&apos;can run transtition after a trasition was abourted&apos;, async () =&gt; {
cfg.eventHandler = {
beforeTrans: async () =&gt; {
await bb.delay(5);
throw new Error(&apos;uuppps&apos;);
},
};
fsm = new FSM(cfg);
try {
await fsm.trans();
} catch (e) {}
await fsm.trans2();
});
});
});
</code></pre>
</div>
<footer class="footer">
Generated by <a href="https://esdoc.org">ESDoc<span data-ice="esdocVersion">(0.5.2)</span><img src="./image/esdoc-logo-mini-black.png"></a>
</footer>
<script src="script/search_index.js"></script>
<script src="script/search.js"></script>
<script src="script/pretty-print.js"></script>
<script src="script/inherited-summary.js"></script>
<script src="script/test-summary.js"></script>
<script src="script/inner-link.js"></script>
<script src="script/patch-for-local.js"></script>
</body>
</html>
'use strict';
/* eslint-env node, mocha */
/* eslint no-unused-expressions: 0, one-var-declaration-per-line: 0, one-var: 0, no-empty: 0 */
const FSM = require('./fsm');
const { expect } = require('chai');
describe('FSM', () => {
describe('fluent build API', () => {
it('can add transition', async () => {
const f = new FSM({});
f.addTransition({ name: 'trans', from: 'a', to: 'b' })
.addTransition({ name: 'trans2', from: ['a', 'b'], to: 'c' })
.init('a');
await f.trans();
expect(f.state).to.equal('b');
await f.trans2();
expect(f.state).to.equal('c');
});
it('can add transition array', async () => {
const f = new FSM({});
f.addTransition([{ name: 'trans', from: 'a', to: 'b' },
{ name: 'trans2', from: ['a', 'b'], to: 'c' }])
.init('a');
await f.trans();
expect(f.state).to.equal('b');
await f.trans2();
expect(f.state).to.equal('c');
});
it('validates transitions', () => {
const f = new FSM({});
expect(() => f.addTransition({})).to.throw(/Invalid transition/);
});
it('does not allow trasition name clashes', () => {
const f = new FSM({});
f.addTransition({ name: 'a-to-b', from: 'a', to: 'b' });
expect(() => f.addTransition({ name: 'a:to:b', from: 'a', to: 'b' })).to.throw(/Ambigious transtion name/);
});
it('does not allow transiion named like native FSM methods', () => {
const f = new FSM({});
expect(() => f.addTransition({ name: 'execute', from: 'a', to: 'b' })).to.throw(/Forbidden transition name/);
expect(() => f.addTransition({ name: 'add:transition', from: 'a', to: 'b' })).to.throw(/Forbidden transition name/);
});
});
});
'use strict';
/* eslint-env node, mocha */
/* eslint no-unused-expressions: 0, one-var-declaration-per-line: 0, one-var: 0, no-empty: 0 */
const bb = require('bluebird');
const FSM = require('./fsm');
const { expect } = require('chai');
describe('FSM', () => {
describe('transition REST API', () => {
const data = {
resId: '42',
_embedded: {
event: {
resId: '22',
},
},
};
let fsm, cfg;
let savedContext;
beforeEach(() => {
cfg = {
initialState: 'a',
transitions: [{
name: 't1',
from: '*',
to: 'a',
api: {
path: '/entity/trans',
method: 'patch',
},
}, {
name: 't2',
from: '*',
to: 'a',
api: {
path: '/entity/{resId}/trans/{subId}',
method: 'patch',
params: {
resId: 'data.resId',
subId: 'data._embedded.event.resId',
},
},
}, {
name: 't3',
from: '*',
to: 'a',
}, {
name: 't4',
from: '*',
to: 'a',
api: {
path: '/{entity}/{resId}/trans/{subId}',
method: 'patch',
params: {
resId: 'data.resId',
subId: 'data._embedded.event.resId',
},
},
}, {
name: 't5',
from: '*',
to: 'a',
api: {
path: '/entity/trans',
method: 'patch',
},
rules: [
() => {},
() => {},
],
}, {
name: 't6',
from: '*',
to: 'a',
api: {
path: '/entity/trans',
method: 'patch',
},
rules: [
() => {},
() => { throw new Error('some error'); },
],
}, {
name: 't7',
from: '*',
to: 'a',
api: {
path: '/entity/trans',
method: 'patch',
},
rules: [
(ctx) => { savedContext = ctx; },
],
}],
saveState: () => {},
willChangeState: () => bb.delay(5),
data,
};
});
it('exposes "restApi()" function', async () => {
fsm = new FSM(cfg);
expect(fsm.restApi).to.be.a('function');
});
it('filters transitions without "api" property', async () => {
fsm = new FSM(cfg);
const r = fsm.restApi();
expect(r.t1).to.exist;
expect(r.t2).to.exist;
expect(r.t3).not.to.exist;
});
it('returns parsed api object', async () => {
fsm = new FSM(cfg);
const r = fsm.restApi();
expect(r.t1).to.eql({
href: '/entity/trans',
method: 'patch',
});
});
it('returns "self" when declared', async () => {
fsm = new FSM(Object.assign({}, cfg, {
api: {
self: {
path: '/entity/{resId}',
},
params: {
resId: 'data.resId',
},
},
}));
const r = fsm.restApi();
expect(r.self).to.eql({
href: '/entity/42',
method: 'get',
});
});
it('can mix global & transition local params in same route', () => {
fsm = new FSM(Object.assign({}, cfg, {
api: {
data: {
entity: 'events',
},
self: {
path: '/{entity}/{resId}',
},
params: {
entity: 'api.data.entity',
resId: 'data.resId',
},
},
}));
const r = fsm.restApi();
expect(r.t4).to.eql({
href: '/events/42/trans/22',
method: 'patch',
});
});
it('can declare static params data in "api.data" object', () => {
fsm = new FSM(Object.assign({}, cfg, {
api: {
data: {
entity: 'events',
},
self: {
path: '/{entity}/{resId}',
},
params: {
entity: 'api.data.entity',
resId: 'data.resId',
},
},
}));
const r = fsm.restApi();
expect(r.self).to.eql({
href: '/events/42',
method: 'get',
});
});
it('should return the restApi object if all rules are satisfied', async () => {
fsm = new FSM(cfg);
const r = fsm.restApi();
expect(r.t5).to.eql({
href: '/entity/trans',
method: 'patch',
});
});
it('should return a restApi object with a error message if not all rules are satisfied', async () => {
fsm = new FSM(cfg);
const r = fsm.restApi();
expect(r.t6).to.eql({
href: '/entity/trans',
method: 'patch',
error: {
message: 'some error',
},
});
});
it('should pass the context of the fsm to the rules functions', async () => {
fsm = new FSM(cfg);
fsm.restApi();
expect(savedContext.data).to.eql(data);
});
it('should execute the rules functions on an fsm without a data object without any error', async () => {
const testCfg = {
initialState: 'a',
transitions: [
{
name: 't',
from: '*',
to: 'a',
api: {
path: '/entity/trans',
method: 'patch',
},
rules: [
(ctx) => { savedContext = ctx; },
],
},
],
saveState: () => {},
willChangeState: () => bb.delay(5),
};
fsm = new FSM(testCfg);
const r = fsm.restApi();
expect(savedContext.data).to.be.undefined;
expect(r.t).to.eql({
href: '/entity/trans',
method: 'patch',
});
});
});
});
'use strict';
/* eslint-env node, mocha */
/* eslint no-unused-expressions: 0, one-var-declaration-per-line: 0, one-var: 0, no-empty: 0 */
const FSM = require('./fsm');
const { expect } = require('chai');
const compositState = require('./composit-state');
describe('FSM', () => {
describe('statics', () => {
it('exposes compositState tool', () => {
expect(FSM.compositState).to.equal(compositState);
});
it('cannot set compositState', () => {
expect(() => { FSM.compositState = () => {}; }).to.throw();
});
it('exposes toFunctionName() helper', () => {
expect(FSM.toFunctionName('test:transition:oida')).to.equal('testTransitionOida');
});
});
});
'use strict';
/* eslint-env node, mocha */
/* eslint no-unused-expressions: 0, one-var-declaration-per-line: 0, one-var: 0, no-empty: 0 */
const bb = require('bluebird');
const FSM = require('./fsm');
const { expect } = require('chai');
describe('FSM', () => {
describe('saveState', () => {
it('can use async "saveState"', async () => {
let saved;
const fsm = new FSM({
initialState: 'a',
transitions: [{ name: 'trans', from: 'a', to: 'b' }],
saveState: async (ctx) => {
await bb.delay(5);
saved = ctx.fsm.state;
},
});
await fsm.trans();
expect(saved).to.equal('b');
});
it('can use sync "saveState"', async () => {
let ctx, args;
const fsm = new FSM({
initialState: 'a',
transitions: [{ name: 'trans', from: 'a', to: 'b' }],
saveState: (c, a) => {
ctx = c;
args = a;
},
});
await fsm.trans(42, 'test');
expect(ctx).to.eql({
from: 'a',
to: 'b',
transition: 'trans',
fsm,
results: {
willChangeState: null,
beforeTrans: null,
didChangeState: null,
willSaveState: null,
didSaveState: null,
saveState: undefined,
},
});
expect(args).to.eql([42, 'test']);
});
it('throw when exection is throw in sync "saveState"', async () => {
let error;
try {
const fsm = new FSM({
initialState: 'a',
transitions: [{ name: 'trans', from: 'a', to: 'b' }],
saveState: () => {
throw new Error('save failed');
},
});
await fsm.trans();
} catch (e) {
error = e;
}
expect(error).to.exist;
expect(error.message).to.equal('save failed');
});
it('throw when exection is throw in async "saveState"', async () => {
let error;
try {
const fsm = new FSM({
initialState: 'a',
transitions: [{ name: 'trans', from: 'a', to: 'b' }],
saveState: async () => {
await bb.delay(20);
throw new Error('save failed');
},
});
await fsm.trans();
} catch (e) {
error = e;
}
expect(error).to.exist;
expect(error.message).to.equal('save failed');
});
});
describe('transiton events', () => {
let fsm, cfg, ts;
const data = { test: 'data' };
beforeEach(() => {
ts = [];
cfg = {
initialState: 'a',
data,
transitions: [{ name: 'trans', from: 'a', to: 'b' }],
saveState: () => 'saveState',
willChangeState: async (ctx, a1, a2) => {
await bb.delay(5);
ts.push({ name: 'willChangeState', ctx, args: [a1, a2] });
return 'willChangeState';
},
didChangeState: async (ctx, ...args) => {
await bb.delay(5);
ts.push({ name: 'didChangeState', ctx, args });
return 'didChangeState';
},
willSaveState: async (ctx, ...args) => {
await bb.delay(5);
ts.push({ name: 'willSaveState', ctx, args });
return 'willSaveState';
},
didSaveState: async (ctx, ...args) => {
await bb.delay(5);
ts.push({ name: 'didSaveState', ctx, args });
return 'didSaveState';
},
eventHandler: {
beforeTrans: async (ctx, ...args) => {
await bb.delay(5);
ts.push({ name: 'beforeTrans', ctx, args });
return 'beforeTrans';
},
afterTrans: async (ctx, ...args) => {
await bb.delay(5);
ts.push({ name: 'afterTrans', ctx, args });
return 'afterTrans';
},
},
};
});
it('calls transition methods with "ctx" and args', async () => {
fsm = new FSM(cfg);
await fsm.trans(42, 'test');
expect(ts[0].ctx).to.eql({
from: 'a',
to: 'b',
transition: 'trans',
fsm,
data,
results: {
willChangeState: 'willChangeState',
beforeTrans: 'beforeTrans',
didChangeState: 'didChangeState',
willSaveState: 'willSaveState',
didSaveState: 'didSaveState',
saveState: 'saveState',
},
});
expect(ts[0].args).to.eql([42, 'test']);
});
it('with "execute(trasnition)" calls transition methods with "ctx" and args', async () => {
fsm = new FSM(cfg);
await fsm.execute('trans', 42, 'test');
expect(ts[0].ctx).to.eql({
from: 'a',
to: 'b',
transition: 'trans',
fsm,
data,
results: {
willChangeState: 'willChangeState',
beforeTrans: 'beforeTrans',
didChangeState: 'didChangeState',
willSaveState: 'willSaveState',
didSaveState: 'didSaveState',
saveState: 'saveState',
},
});
expect(ts[0].args).to.eql([42, 'test']);
});
it('calls transition events in order', async () => {
fsm = new FSM(cfg);
await fsm.trans();
expect(ts[0].name).to.equal('willChangeState');
expect(ts[1].name).to.equal('beforeTrans');
expect(ts[2].name).to.equal('didChangeState');
expect(ts[3].name).to.equal('willSaveState');
expect(ts[4].name).to.equal('didSaveState');
expect(ts[5].name).to.equal('afterTrans');
});
it('returns all transitionHanlder results', async () => {
fsm = new FSM(cfg);
const result = await fsm.trans();
expect(result).to.eql({
willChangeState: 'willChangeState',
beforeTrans: 'beforeTrans',
afterTrans: 'afterTrans',
didChangeState: 'didChangeState',
willSaveState: 'willSaveState',
saveState: 'saveState',
didSaveState: 'didSaveState',
});
});
it('throw error when beforeTransiton handler fails does not change state', async () => {
cfg.eventHandler.beforeTrans = async () => {
await bb.delay(5);
throw new Error('derdo');
};
let error;
try {
fsm = new FSM(cfg);
await fsm.trans();
} catch (e) {
error = e;
}
expect(error).to.exist;
expect(error.message).to.equal('derdo');
expect(ts[0].name).to.equal('willChangeState');
expect(ts.length).to.equal(1);
expect(fsm.state).to.equal('a');
});
it('throw error when afterTransiton handler fails changes state', async () => {
cfg.eventHandler.afterTrans = async () => {
await bb.delay(5);
throw new Error('derdo');
};
let error;
try {
fsm = new FSM(cfg);
await fsm.trans();
} catch (e) {
error = e;
}
expect(error).to.exist;
expect(error.message).to.equal('derdo');
expect(ts[0].name).to.equal('willChangeState');
expect(ts[1].name).to.equal('beforeTrans');
expect(ts[2].name).to.equal('didChangeState');
expect(ts[3].name).to.equal('willSaveState');
expect(ts[4].name).to.equal('didSaveState');
expect(ts.length).to.equal(5);
expect(fsm.state).to.equal('b');
});
});
describe('global transition events', () => {
let fsm, cfg, ts;
beforeEach(() => {
ts = [];
cfg = {
initialState: 'a',
transitions: [{ name: 'trans', from: 'a', to: 'b' }],
saveState: () => {},
willChangeState: (ctx, ...args) => {
ts.push({ name: 'willChangeState', ctx, args });
},
didChangeState: (ctx, ...args) => {
ts.push({ name: 'didChangeState', ctx, args });
},
willSaveState: (ctx, ...args) => {
ts.push({ name: 'willSaveState', ctx, args });
},
didSaveState: (ctx, ...args) => {
ts.push({ name: 'didSaveState', ctx, args });
},
};
});
it('calls transition events in order', async () => {
fsm = new FSM(cfg);
await fsm.trans();
expect(ts[0].name).to.equal('willChangeState');
expect(ts[1].name).to.equal('didChangeState');
expect(ts[2].name).to.equal('willSaveState');
expect(ts[3].name).to.equal('didSaveState');
});
it('calls will & didSaveState only called when saveState is defined', async () => {
delete cfg.saveState;
fsm = new FSM(cfg);
await fsm.trans();
expect(ts[0].name).to.equal('willChangeState');
expect(ts[1].name).to.equal('didChangeState');
expect(ts.length).to.equal(2);
});
it('throw error when willChangeState & state does not change', async () => {
cfg.willChangeState = () => {
throw new Error('derdo');
};
let error;
try {
fsm = new FSM(cfg);
await fsm.trans();
} catch (e) {
error = e;
}
expect(error).to.exist;
expect(error.message).to.equal('derdo');
expect(fsm.state).to.equal('a');
});
it('throw error when didChangeState & state changes', async () => {
cfg.didChangeState = () => {
throw new Error('derdo');
};
let error;
try {
fsm = new FSM(cfg);
await fsm.trans();
} catch (e) {
error = e;
}
expect(error).to.exist;
expect(error.message).to.equal('derdo');
expect(fsm.state).to.equal('b');
});
});
describe('race conditions', () => {
let fsm, cfg;
beforeEach(() => {
cfg = {
initialState: 'a',
transitions: [{ name: 'trans', from: ['a', 'b'], to: 'b' }, { name: 'trans2', from: ['a', 'b'], to: 'a' }],
saveState: () => {},
willChangeState: () => bb.delay(5),
};
});
it('cannot start transition while anothr one is running', (done) => {
fsm = new FSM(cfg);
fsm.trans();
fsm.trans2().then(() => done(new Error('should have failed'))).catch((err) => {
expect(err.message).to.equal('Cannot start transition when during running transition');
done();
});
});
it('can run transition sequential', async () => {
fsm = new FSM(cfg);
await fsm.trans();
await fsm.trans2();
});
it('can run transtition after a trasition was abourted', async () => {
cfg.eventHandler = {
beforeTrans: async () => {
await bb.delay(5);
throw new Error('uuppps');
},
};
fsm = new FSM(cfg);
try {
await fsm.trans();
} catch (e) {}
await fsm.trans2();
});
});
});
+1
-1

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

v8.6.0
v9.1.0

@@ -1,2 +0,2 @@

FROM trigo/node-base:8.6-yarn-lib
FROM trigo/node-base:9-yarn-lib

@@ -42,3 +42,3 @@ <!DOCTYPE html>

<div class="content" data-ice="content"><div class="header-notice">
<div data-ice="importPath" class="import-path"><pre class="prettyprint"><code data-ice="importPathCode">import FSM from &apos;<span><a href="file/lib/fsm.js.html#lineNumber25">@trigo/fsm/lib/fsm.js</a></span>&apos;</code></pre></div>
<div data-ice="importPath" class="import-path"><pre class="prettyprint"><code data-ice="importPathCode">import FSM from &apos;<span><a href="file/lib/fsm.js.html#lineNumber27">@trigo/fsm/lib/fsm.js</a></span>&apos;</code></pre></div>
<span data-ice="access">public</span>

@@ -49,3 +49,3 @@ <span data-ice="kind">class</span>

<span data-ice="source">| <span><a href="file/lib/fsm.js.html#lineNumber25">source</a></span></span>
<span data-ice="source">| <span><a href="file/lib/fsm.js.html#lineNumber27">source</a></span></span>
</div>

@@ -352,3 +352,3 @@

<span data-ice="name"><span><a href="class/lib/fsm.js~FSM.html#instance-method-allTransitions">allTransitions</a></span></span><span data-ice="signature">(): <span><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object">object</a></span></span>
<span data-ice="name"><span><a href="class/lib/fsm.js~FSM.html#instance-method-allTransitions">allTransitions</a></span></span><span data-ice="signature">(): <span><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array">Array</a></span>&lt;<span><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object">object</a></span>&gt;</span>
</p>

@@ -360,6 +360,7 @@ </div>

<div data-ice="description"><p>Get all transititons that are defined for the state machine in the format</p>
<pre><code class="lang-javascript"><code class="source-code prettyprint">{
t1: { from: &apos;a&apos;, to: &apos;b&apos;, ...},
t2: { from: &apos;a&apos;, to: &apos;b&apos;, ...},
t3: { from: &apos;a&apos;, to: &apos;b&apos;, ...},
<pre><code class="lang-javascript"><code class="source-code prettyprint">[
{ name: &apos;a-to-b&apos;, from: &apos;a&apos;, to: &apos;b&apos; },
{ name: &apos;no:op&apos;, from: &apos;a&apos;, to: &apos;*&apos; },
{ name: &apos;b-to-c&apos;, from: &apos;b&apos;, to: &apos;c&apos; },
]
}</code>

@@ -522,3 +523,3 @@ </code></pre>

<span data-ice="source"><span><a href="file/lib/fsm.js.html#lineNumber31">source</a></span></span>
<span data-ice="source"><span><a href="file/lib/fsm.js.html#lineNumber33">source</a></span></span>
</span>

@@ -583,3 +584,3 @@ </h3>

<span data-ice="source"><span><a href="file/lib/fsm.js.html#lineNumber42">source</a></span></span>
<span data-ice="source"><span><a href="file/lib/fsm.js.html#lineNumber44">source</a></span></span>
</span>

@@ -661,3 +662,3 @@ </h3>

<span data-ice="source"><span><a href="file/lib/fsm.js.html#lineNumber80">source</a></span></span>
<span data-ice="source"><span><a href="file/lib/fsm.js.html#lineNumber82">source</a></span></span>
</span>

@@ -812,3 +813,3 @@ </h3>

<span data-ice="source"><span><a href="file/lib/fsm.js.html#lineNumber292">source</a></span></span>
<span data-ice="source"><span><a href="file/lib/fsm.js.html#lineNumber294">source</a></span></span>
</span>

@@ -870,3 +871,3 @@ </h3>

<span data-ice="source"><span><a href="file/lib/fsm.js.html#lineNumber134">source</a></span></span>
<span data-ice="source"><span><a href="file/lib/fsm.js.html#lineNumber136">source</a></span></span>
</span>

@@ -928,3 +929,3 @@ </h3>

<span data-ice="source"><span><a href="file/lib/fsm.js.html#lineNumber143">source</a></span></span>
<span data-ice="source"><span><a href="file/lib/fsm.js.html#lineNumber145">source</a></span></span>
</span>

@@ -973,3 +974,3 @@ </h3>

<span data-ice="source"><span><a href="file/lib/fsm.js.html#lineNumber118">source</a></span></span>
<span data-ice="source"><span><a href="file/lib/fsm.js.html#lineNumber120">source</a></span></span>
</span>

@@ -1034,3 +1035,3 @@ </h3>

<span data-ice="source"><span><a href="file/lib/fsm.js.html#lineNumber262">source</a></span></span>
<span data-ice="source"><span><a href="file/lib/fsm.js.html#lineNumber264">source</a></span></span>
</span>

@@ -1088,4 +1089,4 @@ </h3>

<tr data-ice="property" data-depth="1">
<td data-ice="name" data-depth="1">transition.form</td>
<td data-ice="type"><span><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String">string</a></span> | <span><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object">object</a></span></td>
<td data-ice="name" data-depth="1">transition.to</td>
<td data-ice="type"><span><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String">string</a></span> | <span><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object">object</a></span> | <span><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function">function</a></span></td>
<td data-ice="appendix"></td>

@@ -1097,3 +1098,6 @@ <td data-ice="description"><p>defines the state that is created after the transition<br><br>

{ state: &apos;s1&apos; } =&gt; sets the property &quot;state&quot; of the current state
to &quot;s1&quot; eg. patches the existing state object representation</code>
to &quot;s1&quot; eg. patches the existing state object representation
async (state, ctx) -&gt; String =&gt; stets state from result of the function
{ state: async (state, ctx) -&gt; String } =&gt; stets substate from result of the function
the result of the function mus be a vaild state string value</code>
</code></pre>

@@ -1144,7 +1148,7 @@ </td>

<span data-ice="name">allTransitions</span><span data-ice="signature">(): <span><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object">object</a></span></span>
<span data-ice="name">allTransitions</span><span data-ice="signature">(): <span><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array">Array</a></span>&lt;<span><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object">object</a></span>&gt;</span>
<span class="right-info">
<span data-ice="source"><span><a href="file/lib/fsm.js.html#lineNumber166">source</a></span></span>
<span data-ice="source"><span><a href="file/lib/fsm.js.html#lineNumber169">source</a></span></span>
</span>

@@ -1157,6 +1161,7 @@ </h3>

<div data-ice="description"><p>Get all transititons that are defined for the state machine in the format</p>
<pre><code class="lang-javascript"><code class="source-code prettyprint">{
t1: { from: &apos;a&apos;, to: &apos;b&apos;, ...},
t2: { from: &apos;a&apos;, to: &apos;b&apos;, ...},
t3: { from: &apos;a&apos;, to: &apos;b&apos;, ...},
<pre><code class="lang-javascript"><code class="source-code prettyprint">[
{ name: &apos;a-to-b&apos;, from: &apos;a&apos;, to: &apos;b&apos; },
{ name: &apos;no:op&apos;, from: &apos;a&apos;, to: &apos;*&apos; },
{ name: &apos;b-to-c&apos;, from: &apos;b&apos;, to: &apos;c&apos; },
]
}</code>

@@ -1176,3 +1181,3 @@ </code></pre>

<tr>
<td class="return-type" data-ice="returnType"><span><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object">object</a></span></td>
<td class="return-type" data-ice="returnType"><span><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array">Array</a></span>&lt;<span><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object">object</a></span>&gt;</td>
<td class="return-desc" data-ice="returnDescription"><p>transitions</p>

@@ -1214,3 +1219,3 @@ </td>

<span data-ice="source"><span><a href="file/lib/fsm.js.html#lineNumber229">source</a></span></span>
<span data-ice="source"><span><a href="file/lib/fsm.js.html#lineNumber228">source</a></span></span>
</span>

@@ -1296,3 +1301,3 @@ </h3>

<span data-ice="source"><span><a href="file/lib/fsm.js.html#lineNumber108">source</a></span></span>
<span data-ice="source"><span><a href="file/lib/fsm.js.html#lineNumber110">source</a></span></span>
</span>

@@ -1371,3 +1376,3 @@ </h3>

<span data-ice="source"><span><a href="file/lib/fsm.js.html#lineNumber195">source</a></span></span>
<span data-ice="source"><span><a href="file/lib/fsm.js.html#lineNumber193">source</a></span></span>
</span>

@@ -1444,3 +1449,3 @@ </h3>

<span data-ice="source"><span><a href="file/lib/fsm.js.html#lineNumber152">source</a></span></span>
<span data-ice="source"><span><a href="file/lib/fsm.js.html#lineNumber154">source</a></span></span>
</span>

@@ -1447,0 +1452,0 @@ </h3>

@@ -53,2 +53,4 @@ <!DOCTYPE html>

const parseTrasitionApi = require(&apos;./parse-transition-api&apos;);
const executeTransitionRules = require(&apos;./execute-transition-rules&apos;);
const isValidStateValue = require(&apos;./is-valid-state-value&apos;);

@@ -201,16 +203,12 @@ const callIfSet = async (handler, ctx, args) =&gt; {

* ```javascript
* {
* t1: { from: &apos;a&apos;, to: &apos;b&apos;, ...},
* t2: { from: &apos;a&apos;, to: &apos;b&apos;, ...},
* t3: { from: &apos;a&apos;, to: &apos;b&apos;, ...},
* [
* { name: &apos;a-to-b&apos;, from: &apos;a&apos;, to: &apos;b&apos; },
* { name: &apos;no:op&apos;, from: &apos;a&apos;, to: &apos;*&apos; },
* { name: &apos;b-to-c&apos;, from: &apos;b&apos;, to: &apos;c&apos; },
* ]
* }```
* @return {object} transitions
* @return {Array.&lt;object&gt;} transitions
*/
allTransitions() {
const ts = {};
this._transitions.forEach((t) =&gt; {
ts[t.name] = Object.assign({}, t);
delete ts[t.name].name;
});
return ts;
return this._transitions.map(t =&gt; Object.assign({}, t));
}

@@ -258,2 +256,3 @@

},
rules: t.rules,
});

@@ -297,3 +296,3 @@ });

* ```
* @param {(string|object} transition.form defines the state that is created after the transition&lt;br&gt;&lt;br&gt;
* @param {(string|object|function} transition.to defines the state that is created after the transition&lt;br&gt;&lt;br&gt;
* Supported syntax:

@@ -305,2 +304,5 @@ * ```javascript

* to &quot;s1&quot; eg. patches the existing state object representation
* async (state, ctx) -&gt; String =&gt; stets state from result of the function
* { state: async (state, ctx) -&gt; String } =&gt; stets substate from result of the function
* the result of the function mus be a vaild state string value
* ```

@@ -346,9 +348,31 @@ */

const validNames = this.transitions().map(toCamelCase);
if (!currentTransition || (validNames.indexOf(&apos;*&apos;) === -1 &amp;&amp; validNames.indexOf(transitionName) === -1)) {
throw new Error(`Invalid transition: &quot;${transitionName}&quot; in state: &quot;${this.state}&quot;`);
}
const from = this.state;
let to = from;
const ctx = {
transition: transitionName,
from,
fsm: this,
};
if (this._data) {
ctx.data = this._data;
}
const execTransitionFunction = async (fn, state, ctxArg) =&gt; {
const res = await fn(state, ctxArg);
if (!isValidStateValue(res)) {
throw new Error(`Invalid &quot;to&quot; function result: &quot;${res}&quot; not a valid state string`);
}
return res;
};
if (currentTransition.to !== &apos;*&apos;) {
if (typeof currentTransition.to === &apos;string&apos;) {
if (typeof currentTransition.to === &apos;function&apos;) {
to = await execTransitionFunction(currentTransition.to, from, ctx);
} else if (typeof currentTransition.to === &apos;string&apos;) {
to = currentTransition.to;

@@ -358,2 +382,9 @@ } else if (typeof currentTransition.to === &apos;object&apos;) {

const newStateObj = Object.assign({}, stateObj, currentTransition.to);
for (const key of Object.keys(currentTransition.to)) {
if (typeof currentTransition.to[key] === &apos;function&apos;) {
newStateObj[key] = await execTransitionFunction(currentTransition.to[key], stateObj[key], ctx);
} else {
newStateObj[key] = currentTransition.to[key];
}
}
to = FSM.compositState.build(newStateObj);

@@ -363,11 +394,13 @@ }

const ctx = {
transition: transitionName,
from,
to,
fsm: this,
};
if (this._data) {
ctx.data = this._data;
ctx.to = to;
const rules = currentTransition.rules || [];
const error = executeTransitionRules(rules, ctx);
if (error) {
const newError = new Error(`Invalid transition: &quot;${transitionName}&quot;. Reason: ${error.message}`);
newError.inner = error;
throw newError;
}
const beforeHandler = `before${transitionName[0].toUpperCase()}${transitionName.substring(1)}`;

@@ -374,0 +407,0 @@ const afterHandler = `after${transitionName[0].toUpperCase()}${transitionName.substring(1)}`;

@@ -48,130 +48,6 @@ <!DOCTYPE html>

const bb = require(&apos;bluebird&apos;);
const FSM = require(&apos;./fsm&apos;);
const { expect } = require(&apos;chai&apos;);
const compositState = require(&apos;./composit-state&apos;);
describe(&apos;FSM&apos;, () =&gt; {
describe(&apos;statics&apos;, () =&gt; {
it(&apos;exposes compositState tool&apos;, () =&gt; {
expect(FSM.compositState).to.equal(compositState);
});
it(&apos;cannot set compositState&apos;, () =&gt; {
expect(() =&gt; { FSM.compositState = () =&gt; {}; }).to.throw();
});
it(&apos;exposes toFunctionName() helper&apos;, () =&gt; {
expect(FSM.toFunctionName(&apos;test:transition:oida&apos;)).to.equal(&apos;testTransitionOida&apos;);
});
});
describe(&apos;can use state Objects (compositState)&apos;, () =&gt; {
it(&apos;to in object notation patches from stat&apos;, async () =&gt; {
const f = new FSM({
initialState: &apos;ns1:def|ns2:fe&apos;,
transitions: [
{ name: &apos;t1&apos;, from: { ns1: &apos;def&apos;, ns2: &apos;fe&apos; }, to: { ns1: &apos;b&apos; } },
],
});
// expect(f.states()).to.eql([&apos;ns1:def|ns2:fe&apos;, &apos;ns1:b|ns2:fe&apos;].sort());
await f.t1();
expect(f.state).to.equal(&apos;ns1:b|ns2:fe&apos;);
});
it(&apos;creates state permutation with object notation&apos;, async () =&gt; {
const f = new FSM({
initialState: &apos;k1:k11|k2:k21&apos;,
transitions: [
{ name: &apos;t1&apos;, from: { k1: [&apos;k11&apos;, &apos;k12&apos;], k2: [&apos;k21&apos;, &apos;k22&apos;] }, to: &apos;*&apos; },
],
});
f.init(&apos;k1:k11|k2:k21&apos;);
await f.t1();
expect(f.state).to.equal(&apos;k1:k11|k2:k21&apos;);
f.init(&apos;k1:k12|k2:k21&apos;);
await f.t1();
expect(f.state).to.equal(&apos;k1:k12|k2:k21&apos;);
f.init(&apos;k1:k11|k2:k22&apos;);
await f.t1();
expect(f.state).to.equal(&apos;k1:k11|k2:k22&apos;);
f.init(&apos;k1:k12|k2:k22&apos;);
await f.t1();
expect(f.state).to.equal(&apos;k1:k12|k2:k22&apos;);
});
});
describe(&apos;fluent build API&apos;, () =&gt; {
it(&apos;can add transition&apos;, async () =&gt; {
const f = new FSM({});
f.addTransition({ name: &apos;trans&apos;, from: &apos;a&apos;, to: &apos;b&apos; })
.addTransition({ name: &apos;trans2&apos;, from: [&apos;a&apos;, &apos;b&apos;], to: &apos;c&apos; })
.init(&apos;a&apos;);
await f.trans();
expect(f.state).to.equal(&apos;b&apos;);
await f.trans2();
expect(f.state).to.equal(&apos;c&apos;);
});
it(&apos;can add transition array&apos;, async () =&gt; {
const f = new FSM({});
f.addTransition([{ name: &apos;trans&apos;, from: &apos;a&apos;, to: &apos;b&apos; },
{ name: &apos;trans2&apos;, from: [&apos;a&apos;, &apos;b&apos;], to: &apos;c&apos; }])
.init(&apos;a&apos;);
await f.trans();
expect(f.state).to.equal(&apos;b&apos;);
await f.trans2();
expect(f.state).to.equal(&apos;c&apos;);
});
it(&apos;validates transitions&apos;, () =&gt; {
const f = new FSM({});
expect(() =&gt; f.addTransition({})).to.throw(/Invalid transition/);
});
it(&apos;does not allow trasition name clashes&apos;, () =&gt; {
const f = new FSM({});
f.addTransition({ name: &apos;a-to-b&apos;, from: &apos;a&apos;, to: &apos;b&apos; });
expect(() =&gt; f.addTransition({ name: &apos;a:to:b&apos;, from: &apos;a&apos;, to: &apos;b&apos; })).to.throw(/Ambigious transtion name/);
});
it(&apos;does not allow transiion named like native FSM methods&apos;, () =&gt; {
const f = new FSM({});
expect(() =&gt; f.addTransition({ name: &apos;execute&apos;, from: &apos;a&apos;, to: &apos;b&apos; })).to.throw(/Forbidden transition name/);
expect(() =&gt; f.addTransition({ name: &apos;add:transition&apos;, from: &apos;a&apos;, to: &apos;b&apos; })).to.throw(/Forbidden transition name/);
});
});
describe(&apos;large machine&apos;, () =&gt; {
const transitions = [];
const cntSubstate = 100;
const cntValue = 100;
const cntTransiotions = 100;
before(() =&gt; {
const state = {};
for (let i = 0; i &lt; cntSubstate; i++) {
state[`substate_${i}`] = [];
for (let j = 0; j &lt; cntValue; j++) {
state[`substate_${i}`].push(`value_${i}_${j}`);
}
}
for (let t = 0; t &lt; cntTransiotions; t++) {
transitions.push({
name: `trans:${t}`,
from: state,
to: &apos;*&apos;,
});
}
});
it(&apos;initialize large FSM&apos;, async () =&gt; {
const d1 = new Date();
const test = new FSM({ transitions });
const d2 = new Date();
const ms = d2.getTime() - d1.getTime();
expect(ms).to.lt(1000);
expect(test).to.exist;
});
});
describe(&apos;ctor, API &amp; state constrainsts&apos;, () =&gt; {

@@ -184,3 +60,5 @@ let fsm;

{ name: &apos;no:op&apos;, from: &apos;a&apos;, to: &apos;*&apos; },
{ name: &apos;b-to-c&apos;, from: &apos;b&apos;, to: &apos;c&apos; }],
{ name: &apos;b-to-c&apos;, from: &apos;b&apos;, to: &apos;c&apos; },
{ name: &apos;a-to-c&apos;, from: &apos;a&apos;, to: &apos;c&apos;, rules: [() =&gt; { throw new Error(&apos;some error&apos;); }] },
],
});

@@ -251,11 +129,11 @@ fsm.init(&apos;a&apos;);

it(&apos;can list valid transitions&apos;, async () =&gt; {
expect(fsm.transitions()).to.eql([&apos;a-to-b&apos;, &apos;no:op&apos;]);
expect(fsm.transitions()).to.eql([&apos;a-to-b&apos;, &apos;no:op&apos;, &apos;a-to-c&apos;]);
});
it.only(&apos;can list all transitions&apos;, async () =&gt; {
expect(fsm.allTransitions()).to.eql({
&apos;a-to-b&apos;: { from: &apos;a&apos;, to: &apos;b&apos; },
&apos;no:op&apos;: { from: &apos;a&apos;, to: &apos;*&apos; },
&apos;b-to-c&apos;: { from: &apos;b&apos;, to: &apos;c&apos; },
});
it(&apos;can list all transitions&apos;, async () =&gt; {
const transitions = fsm.allTransitions();
const transitionNames = transitions.map(t =&gt; t.name);
const expectedTransitions = [&apos;a-to-b&apos;, &apos;no:op&apos;, &apos;b-to-c&apos;, &apos;a-to-c&apos;];
expect(transitions.length).to.eql(4);
expect(transitionNames).to.eql(expectedTransitions);
});

@@ -273,7 +151,17 @@

});
});
describe(&apos;transition definitions&apos;, () =&gt; {
it(&apos;cannot run valid transition if rules are not satisfied&apos;, async () =&gt; {
let error;
try {
await fsm.aToC();
} catch (e) {
error = e;
}
expect(error).to.exist;
expect(error.inner.message).to.equal(&apos;some error&apos;);
expect(error.message).to.equal(&apos;Invalid transition: &quot;aToC&quot;. Reason: some error&apos;);
});
it(&apos;can use array in from clause&apos;, async () =&gt; {
const fsm = new FSM({
fsm = new FSM({
initialState: &apos;a&apos;,

@@ -301,490 +189,205 @@ transitions: [

describe(&apos;saveState&apos;, () =&gt; {
it(&apos;can use async &quot;saveState&quot;&apos;, async () =&gt; {
let saved;
describe(&apos;&quot;to&quot; state functions&apos;, () =&gt; {
it(&apos;can set to: async () =&gt; newState&apos;, async () =&gt; {
const fsm = new FSM({
initialState: &apos;a&apos;,
transitions: [{ name: &apos;trans&apos;, from: &apos;a&apos;, to: &apos;b&apos; }],
saveState: async (ctx) =&gt; {
await bb.delay(5);
saved = ctx.fsm.state;
},
transitions: [
{ name: &apos;trans1&apos;, from: &apos;a&apos;, to: async () =&gt; &apos;b&apos; },
],
});
await fsm.trans();
expect(saved).to.equal(&apos;b&apos;);
await fsm.trans1();
expect(fsm.state).to.equal(&apos;b&apos;);
});
it(&apos;can use sync &quot;saveState&quot;&apos;, async () =&gt; {
let ctx, args;
it(&apos;it awaits the async function&apos;, async () =&gt; {
const fsm = new FSM({
initialState: &apos;a&apos;,
transitions: [{ name: &apos;trans&apos;, from: &apos;a&apos;, to: &apos;b&apos; }],
saveState: (c, a) =&gt; {
ctx = c;
args = a;
},
transitions: [
{ name: &apos;trans1&apos;,
from: &apos;a&apos;,
to: async () =&gt; {
await bb.delay(10);
return &apos;b&apos;;
} },
],
});
await fsm.trans(42, &apos;test&apos;);
expect(ctx).to.eql({
from: &apos;a&apos;,
to: &apos;b&apos;,
transition: &apos;trans&apos;,
fsm,
results: {
willChangeState: null,
beforeTrans: null,
didChangeState: null,
willSaveState: null,
didSaveState: null,
saveState: undefined,
},
});
expect(args).to.eql([42, &apos;test&apos;]);
});
it(&apos;throw when exection is throw in sync &quot;saveState&quot;&apos;, async () =&gt; {
let error;
try {
const fsm = new FSM({
initialState: &apos;a&apos;,
transitions: [{ name: &apos;trans&apos;, from: &apos;a&apos;, to: &apos;b&apos; }],
saveState: () =&gt; {
throw new Error(&apos;save failed&apos;);
},
});
await fsm.trans();
} catch (e) {
error = e;
}
expect(error).to.exist;
expect(error.message).to.equal(&apos;save failed&apos;);
await fsm.trans1();
expect(fsm.state).to.equal(&apos;b&apos;);
});
it(&apos;throw when exection is throw in async &quot;saveState&quot;&apos;, async () =&gt; {
let error;
try {
const fsm = new FSM({
initialState: &apos;a&apos;,
transitions: [{ name: &apos;trans&apos;, from: &apos;a&apos;, to: &apos;b&apos; }],
saveState: async () =&gt; {
await bb.delay(20);
throw new Error(&apos;save failed&apos;);
},
});
await fsm.trans();
} catch (e) {
error = e;
}
expect(error).to.exist;
expect(error.message).to.equal(&apos;save failed&apos;);
});
});
describe(&apos;transiton events&apos;, () =&gt; {
let fsm, cfg, ts;
const data = { test: &apos;data&apos; };
beforeEach(() =&gt; {
ts = [];
cfg = {
it(&apos;aborts transition &amp; throws error whe function throws&apos;, async () =&gt; {
const fsm = new FSM({
initialState: &apos;a&apos;,
data,
transitions: [{ name: &apos;trans&apos;, from: &apos;a&apos;, to: &apos;b&apos; }],
saveState: () =&gt; &apos;saveState&apos;,
willChangeState: async (ctx, a1, a2) =&gt; {
await bb.delay(5);
ts.push({ name: &apos;willChangeState&apos;, ctx, args: [a1, a2] });
return &apos;willChangeState&apos;;
},
didChangeState: async (ctx, ...args) =&gt; {
await bb.delay(5);
ts.push({ name: &apos;didChangeState&apos;, ctx, args });
return &apos;didChangeState&apos;;
},
willSaveState: async (ctx, ...args) =&gt; {
await bb.delay(5);
ts.push({ name: &apos;willSaveState&apos;, ctx, args });
return &apos;willSaveState&apos;;
},
didSaveState: async (ctx, ...args) =&gt; {
await bb.delay(5);
ts.push({ name: &apos;didSaveState&apos;, ctx, args });
return &apos;didSaveState&apos;;
},
eventHandler: {
beforeTrans: async (ctx, ...args) =&gt; {
await bb.delay(5);
ts.push({ name: &apos;beforeTrans&apos;, ctx, args });
return &apos;beforeTrans&apos;;
},
afterTrans: async (ctx, ...args) =&gt; {
await bb.delay(5);
ts.push({ name: &apos;afterTrans&apos;, ctx, args });
return &apos;afterTrans&apos;;
},
},
};
});
it(&apos;calls transition methods with &quot;ctx&quot; and args&apos;, async () =&gt; {
fsm = new FSM(cfg);
await fsm.trans(42, &apos;test&apos;);
expect(ts[0].ctx).to.eql({
from: &apos;a&apos;,
to: &apos;b&apos;,
transition: &apos;trans&apos;,
fsm,
data,
results: {
willChangeState: &apos;willChangeState&apos;,
beforeTrans: &apos;beforeTrans&apos;,
didChangeState: &apos;didChangeState&apos;,
willSaveState: &apos;willSaveState&apos;,
didSaveState: &apos;didSaveState&apos;,
saveState: &apos;saveState&apos;,
},
transitions: [
{ name: &apos;trans1&apos;,
from: &apos;a&apos;,
to: async () =&gt; { throw new Error(&apos;asf&apos;); } },
],
});
expect(ts[0].args).to.eql([42, &apos;test&apos;]);
});
it(&apos;with &quot;execute(trasnition)&quot; calls transition methods with &quot;ctx&quot; and args&apos;, async () =&gt; {
fsm = new FSM(cfg);
await fsm.execute(&apos;trans&apos;, 42, &apos;test&apos;);
expect(ts[0].ctx).to.eql({
from: &apos;a&apos;,
to: &apos;b&apos;,
transition: &apos;trans&apos;,
fsm,
data,
results: {
willChangeState: &apos;willChangeState&apos;,
beforeTrans: &apos;beforeTrans&apos;,
didChangeState: &apos;didChangeState&apos;,
willSaveState: &apos;willSaveState&apos;,
didSaveState: &apos;didSaveState&apos;,
saveState: &apos;saveState&apos;,
},
});
expect(ts[0].args).to.eql([42, &apos;test&apos;]);
});
it(&apos;calls transition events in order&apos;, async () =&gt; {
fsm = new FSM(cfg);
await fsm.trans();
expect(ts[0].name).to.equal(&apos;willChangeState&apos;);
expect(ts[1].name).to.equal(&apos;beforeTrans&apos;);
expect(ts[2].name).to.equal(&apos;didChangeState&apos;);
expect(ts[3].name).to.equal(&apos;willSaveState&apos;);
expect(ts[4].name).to.equal(&apos;didSaveState&apos;);
expect(ts[5].name).to.equal(&apos;afterTrans&apos;);
});
it(&apos;returns all transitionHanlder results&apos;, async () =&gt; {
fsm = new FSM(cfg);
const result = await fsm.trans();
expect(result).to.eql({
willChangeState: &apos;willChangeState&apos;,
beforeTrans: &apos;beforeTrans&apos;,
afterTrans: &apos;afterTrans&apos;,
didChangeState: &apos;didChangeState&apos;,
willSaveState: &apos;willSaveState&apos;,
saveState: &apos;saveState&apos;,
didSaveState: &apos;didSaveState&apos;,
});
});
it(&apos;throw error when beforeTransiton handler fails does not change state&apos;, async () =&gt; {
cfg.eventHandler.beforeTrans = async () =&gt; {
await bb.delay(5);
throw new Error(&apos;derdo&apos;);
};
let error;
let thrown = false;
try {
fsm = new FSM(cfg);
await fsm.trans();
await fsm.trans1();
} catch (e) {
error = e;
thrown = true;
}
expect(error).to.exist;
expect(error.message).to.equal(&apos;derdo&apos;);
expect(ts[0].name).to.equal(&apos;willChangeState&apos;);
expect(ts.length).to.equal(1);
expect(thrown).to.be.true;
expect(fsm.state).to.equal(&apos;a&apos;);
});
it(&apos;throw error when afterTransiton handler fails changes state&apos;, async () =&gt; {
cfg.eventHandler.afterTrans = async () =&gt; {
await bb.delay(5);
throw new Error(&apos;derdo&apos;);
};
let error;
try {
fsm = new FSM(cfg);
await fsm.trans();
} catch (e) {
error = e;
}
expect(error).to.exist;
expect(error.message).to.equal(&apos;derdo&apos;);
expect(ts[0].name).to.equal(&apos;willChangeState&apos;);
expect(ts[1].name).to.equal(&apos;beforeTrans&apos;);
expect(ts[2].name).to.equal(&apos;didChangeState&apos;);
expect(ts[3].name).to.equal(&apos;willSaveState&apos;);
expect(ts[4].name).to.equal(&apos;didSaveState&apos;);
expect(ts.length).to.equal(5);
expect(fsm.state).to.equal(&apos;b&apos;);
});
});
describe(&apos;global transition events&apos;, () =&gt; {
let fsm, cfg, ts;
beforeEach(() =&gt; {
ts = [];
cfg = {
it(&apos;validates the functions resukt to a valid state string&apos;, async () =&gt; {
const fsm = new FSM({
initialState: &apos;a&apos;,
transitions: [{ name: &apos;trans&apos;, from: &apos;a&apos;, to: &apos;b&apos; }],
saveState: () =&gt; {},
willChangeState: (ctx, ...args) =&gt; {
ts.push({ name: &apos;willChangeState&apos;, ctx, args });
},
didChangeState: (ctx, ...args) =&gt; {
ts.push({ name: &apos;didChangeState&apos;, ctx, args });
},
willSaveState: (ctx, ...args) =&gt; {
ts.push({ name: &apos;willSaveState&apos;, ctx, args });
},
didSaveState: (ctx, ...args) =&gt; {
ts.push({ name: &apos;didSaveState&apos;, ctx, args });
},
};
});
transitions: [
{ name: &apos;trans1&apos;,
from: &apos;a&apos;,
to: async () =&gt; &apos;&apos; },
],
});
it(&apos;calls transition events in order&apos;, async () =&gt; {
fsm = new FSM(cfg);
await fsm.trans();
expect(ts[0].name).to.equal(&apos;willChangeState&apos;);
expect(ts[1].name).to.equal(&apos;didChangeState&apos;);
expect(ts[2].name).to.equal(&apos;willSaveState&apos;);
expect(ts[3].name).to.equal(&apos;didSaveState&apos;);
});
it(&apos;calls will &amp; didSaveState only called when saveState is defined&apos;, async () =&gt; {
delete cfg.saveState;
fsm = new FSM(cfg);
await fsm.trans();
expect(ts[0].name).to.equal(&apos;willChangeState&apos;);
expect(ts[1].name).to.equal(&apos;didChangeState&apos;);
expect(ts.length).to.equal(2);
});
it(&apos;throw error when willChangeState &amp; state does not change&apos;, async () =&gt; {
cfg.willChangeState = () =&gt; {
throw new Error(&apos;derdo&apos;);
};
let error;
let err;
try {
fsm = new FSM(cfg);
await fsm.trans();
await fsm.trans1();
} catch (e) {
error = e;
err = e;
}
expect(error).to.exist;
expect(error.message).to.equal(&apos;derdo&apos;);
expect(fsm.state).to.equal(&apos;a&apos;);
expect(err.message).to.contain(&apos;not a valid state string&apos;);
});
it(&apos;throw error when didChangeState &amp; state changes&apos;, async () =&gt; {
cfg.didChangeState = () =&gt; {
throw new Error(&apos;derdo&apos;);
};
let error;
try {
fsm = new FSM(cfg);
await fsm.trans();
} catch (e) {
error = e;
}
expect(error).to.exist;
expect(error.message).to.equal(&apos;derdo&apos;);
expect(fsm.state).to.equal(&apos;b&apos;);
});
});
describe(&apos;race conditions&apos;, () =&gt; {
let fsm, cfg;
beforeEach(() =&gt; {
cfg = {
it(&apos;passes the state value as first argument to &quot;to&quot; function&apos;, async () =&gt; {
let stateArg;
const fsm = new FSM({
initialState: &apos;a&apos;,
transitions: [{ name: &apos;trans&apos;, from: [&apos;a&apos;, &apos;b&apos;], to: &apos;b&apos; }, { name: &apos;trans2&apos;, from: [&apos;a&apos;, &apos;b&apos;], to: &apos;a&apos; }],
saveState: () =&gt; {},
willChangeState: () =&gt; bb.delay(5),
};
});
transitions: [
{ name: &apos;trans1&apos;,
from: &apos;a&apos;,
to: async (state) =&gt; {
stateArg = state;
state = &apos;asgsdg&apos;; // eslint-disable-line
return &apos;b&apos;;
} },
it(&apos;cannot start transition while anothr one is running&apos;, (done) =&gt; {
fsm = new FSM(cfg);
fsm.trans();
fsm.trans2().then(() =&gt; done(new Error(&apos;should have failed&apos;))).catch((err) =&gt; {
expect(err.message).to.equal(&apos;Cannot start transition when during running transition&apos;);
done();
],
});
});
it(&apos;can run transition sequential&apos;, async () =&gt; {
fsm = new FSM(cfg);
await fsm.trans();
await fsm.trans2();
});
it(&apos;can run transtition after a trasition was abourted&apos;, async () =&gt; {
cfg.eventHandler = {
beforeTrans: async () =&gt; {
await bb.delay(5);
throw new Error(&apos;uuppps&apos;);
},
};
fsm = new FSM(cfg);
try {
await fsm.trans();
} catch (e) {}
await fsm.trans2();
await fsm.trans1();
expect(stateArg).to.equal(&apos;a&apos;);
});
});
it(&apos;passes the ctx object as second argument to &quot;to&quot; function&apos;, async () =&gt; {
let ctxArg;
const fsm = new FSM({
initialState: &apos;a&apos;,
transitions: [
{ name: &apos;trans1&apos;,
from: &apos;a&apos;,
to: async (state, ctx) =&gt; {
ctxArg = ctx;
return &apos;b&apos;;
} },
describe(&apos;transition REST API&apos;, () =&gt; {
let fsm, cfg;
beforeEach(() =&gt; {
cfg = {
initialState: &apos;a&apos;,
transitions: [{
name: &apos;t1&apos;,
from: &apos;*&apos;,
to: &apos;a&apos;,
api: {
path: &apos;/entity/trans&apos;,
method: &apos;patch&apos;,
},
}, {
name: &apos;t2&apos;,
from: &apos;*&apos;,
to: &apos;a&apos;,
api: {
path: &apos;/entity/{resId}/trans/{subId}&apos;,
method: &apos;patch&apos;,
params: {
resId: &apos;data.resId&apos;,
subId: &apos;data._embedded.event.resId&apos;,
},
},
}, {
name: &apos;t3&apos;,
from: &apos;*&apos;,
to: &apos;a&apos;,
}, {
name: &apos;t4&apos;,
from: &apos;*&apos;,
to: &apos;a&apos;,
api: {
path: &apos;/{entity}/{resId}/trans/{subId}&apos;,
method: &apos;patch&apos;,
params: {
resId: &apos;data.resId&apos;,
subId: &apos;data._embedded.event.resId&apos;,
},
},
}],
saveState: () =&gt; {},
willChangeState: () =&gt; bb.delay(5),
],
data: {
resId: &apos;42&apos;,
_embedded: {
event: {
resId: &apos;22&apos;,
},
},
as: &apos;sa&apos;,
},
};
});
});
it(&apos;exposes &quot;restApi()&quot; function&apos;, async () =&gt; {
fsm = new FSM(cfg);
expect(fsm.restApi).to.be.a(&apos;function&apos;);
await fsm.trans1();
expect(ctxArg.fsm).to.equal(fsm);
expect(ctxArg.data).to.equal(fsm.data);
});
it(&apos;filters transitions without &quot;api&quot; property&apos;, async () =&gt; {
fsm = new FSM(cfg);
const r = fsm.restApi();
expect(r.t1).to.exist;
expect(r.t2).to.exist;
expect(r.t3).not.to.exist;
it(&apos;works on state object properties&apos;, async () =&gt; {
let ctxArg, stateArg;
const fsm = new FSM({
initialState: FSM.compositState.build({ s1: &apos;s1&apos;, s2: &apos;s2&apos; }),
transitions: [
{ name: &apos;trans1&apos;,
from: { s1: &apos;s1&apos;, s2: &apos;s2&apos; },
to: { s1: async (state, ctx) =&gt; {
stateArg = state;
ctxArg = ctx;
return &apos;b&apos;;
} } },
],
data: {
as: &apos;sa&apos;,
},
});
await fsm.trans1();
expect(stateArg).to.equal(&apos;s1&apos;);
expect(ctxArg.fsm).to.equal(fsm);
expect(ctxArg.data).to.equal(fsm.data);
expect(FSM.compositState.parse(fsm.state)).to.eql({ s1: &apos;b&apos;, s2: &apos;s2&apos; });
});
});
it(&apos;returns parsed api object&apos;, async () =&gt; {
fsm = new FSM(cfg);
const r = fsm.restApi();
expect(r.t1).to.eql({
href: &apos;/entity/trans&apos;,
method: &apos;patch&apos;,
describe(&apos;can use state Objects (compositState)&apos;, () =&gt; {
it(&apos;to in object notation patches from stat&apos;, async () =&gt; {
const f = new FSM({
initialState: &apos;ns1:def|ns2:fe&apos;,
transitions: [
{ name: &apos;t1&apos;, from: { ns1: &apos;def&apos;, ns2: &apos;fe&apos; }, to: { ns1: &apos;b&apos; } },
],
});
// expect(f.states()).to.eql([&apos;ns1:def|ns2:fe&apos;, &apos;ns1:b|ns2:fe&apos;].sort());
await f.t1();
expect(f.state).to.equal(&apos;ns1:b|ns2:fe&apos;);
});
it(&apos;returns &quot;self&quot; when declared&apos;, async () =&gt; {
fsm = new FSM(Object.assign({}, cfg, {
api: {
self: {
path: &apos;/entity/{resId}&apos;,
},
params: {
resId: &apos;data.resId&apos;,
},
},
}));
const r = fsm.restApi();
expect(r.self).to.eql({
href: &apos;/entity/42&apos;,
method: &apos;get&apos;,
it(&apos;creates state permutation with object notation&apos;, async () =&gt; {
const f = new FSM({
initialState: &apos;k1:k11|k2:k21&apos;,
transitions: [
{ name: &apos;t1&apos;, from: { k1: [&apos;k11&apos;, &apos;k12&apos;], k2: [&apos;k21&apos;, &apos;k22&apos;] }, to: &apos;*&apos; },
],
});
});
it(&apos;can mix global &amp; transition local params in same route&apos;, () =&gt; {
fsm = new FSM(Object.assign({}, cfg, {
api: {
data: {
entity: &apos;events&apos;,
},
self: {
path: &apos;/{entity}/{resId}&apos;,
},
params: {
entity: &apos;api.data.entity&apos;,
resId: &apos;data.resId&apos;,
},
},
}));
const r = fsm.restApi();
expect(r.t4).to.eql({
href: &apos;/events/42/trans/22&apos;,
method: &apos;patch&apos;,
});
f.init(&apos;k1:k11|k2:k21&apos;);
await f.t1();
expect(f.state).to.equal(&apos;k1:k11|k2:k21&apos;);
f.init(&apos;k1:k12|k2:k21&apos;);
await f.t1();
expect(f.state).to.equal(&apos;k1:k12|k2:k21&apos;);
f.init(&apos;k1:k11|k2:k22&apos;);
await f.t1();
expect(f.state).to.equal(&apos;k1:k11|k2:k22&apos;);
f.init(&apos;k1:k12|k2:k22&apos;);
await f.t1();
expect(f.state).to.equal(&apos;k1:k12|k2:k22&apos;);
});
});
it(&apos;can declare static params data in &quot;api.data&quot; object&apos;, () =&gt; {
fsm = new FSM(Object.assign({}, cfg, {
api: {
data: {
entity: &apos;events&apos;,
},
self: {
path: &apos;/{entity}/{resId}&apos;,
},
params: {
entity: &apos;api.data.entity&apos;,
resId: &apos;data.resId&apos;,
},
},
}));
const r = fsm.restApi();
expect(r.self).to.eql({
href: &apos;/events/42&apos;,
method: &apos;get&apos;,
});
describe(&apos;large machine&apos;, () =&gt; {
const transitions = [];
const cntSubstate = 100;
const cntValue = 100;
const cntTransiotions = 100;
before(() =&gt; {
const state = {};
for (let i = 0; i &lt; cntSubstate; i++) {
state[`substate_${i}`] = [];
for (let j = 0; j &lt; cntValue; j++) {
state[`substate_${i}`].push(`value_${i}_${j}`);
}
}
for (let t = 0; t &lt; cntTransiotions; t++) {
transitions.push({
name: `trans:${t}`,
from: state,
to: &apos;*&apos;,
});
}
});
it(&apos;initialize large FSM&apos;, async () =&gt; {
const d1 = new Date();
const test = new FSM({ transitions });
const d2 = new Date();
const ms = d2.getTime() - d1.getTime();
expect(ms).to.lt(1000);
expect(test).to.exist;
});
});

@@ -791,0 +394,0 @@ });

@@ -44,2 +44,4 @@ <!DOCTYPE html>

const executeTransitionRules = require(&apos;./execute-transition-rules&apos;);
function getNested(theObject, path) {

@@ -84,6 +86,5 @@ try {

/** @private */
module.exports = ({ api, ctx }) =&gt; {
module.exports = ({ api, ctx, rules }) =&gt; {
if (!api) throw new Error(&apos;Argument &quot;api&quot; missing&apos;);
if (!api.path) throw new Error(&apos;Argument &quot;api.path&quot; missing&apos;);
const method = api.method || &apos;get&apos;;

@@ -100,2 +101,4 @@

const params = extractParams(href);
const transitionRules = rules || [];
const error = executeTransitionRules(transitionRules, ctx);

@@ -110,2 +113,7 @@ const link = {

}
if (error) {
link.error = { message: error.message };
}
return link;

@@ -112,0 +120,0 @@ };

@@ -132,2 +132,41 @@ <!DOCTYPE html>

});
it(&apos;returns a valid api object if the rule array is not satisfied&apos;, () =&gt; {
const res = parseApi({
api: {
path: &apos;/test&apos;,
method: &apos;patch&apos;,
},
rules: [
() =&gt; {},
() =&gt; {},
],
});
expect(res).to.eql({
href: &apos;/test&apos;,
method: &apos;patch&apos;,
});
});
it(&apos;returns a invalid api object if the rule array is not satisfied&apos;, () =&gt; {
const res = parseApi({
api: {
path: &apos;/test&apos;,
method: &apos;patch&apos;,
},
rules: [
() =&gt; {},
() =&gt; { throw new Error(&apos;some error&apos;); },
],
});
expect(res).to.eql({
href: &apos;/test&apos;,
method: &apos;patch&apos;,
error: {
message: &apos;some error&apos;,
},
});
});
});

@@ -134,0 +173,0 @@ </code></pre>

@@ -55,3 +55,3 @@ <!DOCTYPE html>

fromVal.forEach((val) =&gt; {
if (!isValidStateValue(val) || from === &apos;__uninitialized__&apos;) {
if ((typeof val === &apos;string&apos;) &amp;&amp; (!isValidStateValue(val) || from === &apos;__uninitialized__&apos;)) {
throw new Error(`Invalid state value! from: ${from}`);

@@ -58,0 +58,0 @@ }

@@ -80,2 +80,10 @@ <!DOCTYPE html>

});
it(&apos;&quot;to&quot; can be a function&apos;, () =&gt; {
parseTrasition({ name: &apos;t&apos;, from: &apos;a&apos;, to: () =&gt; {} });
});
it(&apos;&quot;to&quot; substate can be a function&apos;, () =&gt; {
parseTrasition({ name: &apos;t&apos;, from: { s1: &apos;a&apos;, s2: &apos;b&apos; }, to: { s1: &apos;a&apos;, s2: () =&gt; {} } });
});
});

@@ -82,0 +90,0 @@ </code></pre>

{
"name": "@trigo/fsm",
"version": "3.1.0",
"version": "3.3.1",
"description": "FSM - Finite State Machine",

@@ -13,3 +13,2 @@ "main": "index.js",

"devDependencies": {
"@trigo/eslint-config-trigo": "^3.3.0",
"bluebird": "^3.5.0",

@@ -19,3 +18,3 @@ "chai": "^4.0.2",

"eslint": "^4.1.1",
"eslint-config-airbnb-base": "^11.2.0",
"eslint-config-trigo": "^4.2.4",
"eslint-plugin-import": "^2.6.1",

@@ -22,0 +21,0 @@ "eslint-plugin-mocha": "^4.11.0",

@@ -381,2 +381,14 @@ window.esdocSearchIndex = [

[
"lib/execute-transition-rules.js",
"file/lib/execute-transition-rules.js.html",
"lib/execute-transition-rules.js",
"file"
],
[
"lib/execute-transition-rules.specs.js",
"file/lib/execute-transition-rules.specs.js.html",
"lib/execute-transition-rules.specs.js",
"file"
],
[
"lib/find-current-transition.js",

@@ -406,2 +418,8 @@ "file/lib/find-current-transition.js.html",

[
"lib/fsm.fluent-api.specs.js",
"file/lib/fsm.fluent-api.specs.js.html",
"lib/fsm.fluent-api.specs.js",
"file"
],
[
"lib/fsm.js",

@@ -491,2 +509,8 @@ "file/lib/fsm.js.html",

[
"lib/fsm.rest-api.specs.js",
"file/lib/fsm.rest-api.specs.js.html",
"lib/fsm.rest-api.specs.js",
"file"
],
[
"lib/fsm.specs.js",

@@ -498,2 +522,14 @@ "file/lib/fsm.specs.js.html",

[
"lib/fsm.statics.specs.js",
"file/lib/fsm.statics.specs.js.html",
"lib/fsm.statics.specs.js",
"file"
],
[
"lib/fsm.transition-execution.specs.js",
"file/lib/fsm.transition-execution.specs.js.html",
"lib/fsm.transition-execution.specs.js",
"file"
],
[
"lib/get-all-taken-names.js",

@@ -500,0 +536,0 @@ "file/lib/get-all-taken-names.js.html",

@@ -62,3 +62,3 @@ <!DOCTYPE html>

<td style="display: none;" data-ice="lines">9</td>
<td style="display: none;" data-ice="updated">2017-04-04 21:36:26 (UTC)</td>
<td style="display: none;" data-ice="updated">2017-10-04 09:25:19 (UTC)</td>
</tr>

@@ -71,3 +71,3 @@ <tr data-ice="file">

<td style="display: none;" data-ice="lines">21</td>
<td style="display: none;" data-ice="updated">2017-04-03 20:41:15 (UTC)</td>
<td style="display: none;" data-ice="updated">2017-10-04 09:25:19 (UTC)</td>
</tr>

@@ -80,3 +80,3 @@ <tr data-ice="file">

<td style="display: none;" data-ice="lines">61</td>
<td style="display: none;" data-ice="updated">2017-04-05 08:28:24 (UTC)</td>
<td style="display: none;" data-ice="updated">2017-10-04 09:25:19 (UTC)</td>
</tr>

@@ -89,5 +89,21 @@ <tr data-ice="file">

<td style="display: none;" data-ice="lines">24</td>
<td style="display: none;" data-ice="updated">2017-04-04 22:44:26 (UTC)</td>
<td style="display: none;" data-ice="updated">2017-10-04 09:25:19 (UTC)</td>
</tr>
<tr data-ice="file">
<td data-ice="filePath"><span><a href="file/lib/execute-transition-rules.js.html">lib/execute-transition-rules.js</a></span></td>
<td data-ice="identifier" class="identifiers">-</td>
<td class="coverage"><span data-ice="coverage">-</span></td>
<td style="display: none;" data-ice="size">288 byte</td>
<td style="display: none;" data-ice="lines">16</td>
<td style="display: none;" data-ice="updated">2017-11-03 08:21:12 (UTC)</td>
</tr>
<tr data-ice="file">
<td data-ice="filePath"><span><a href="file/lib/execute-transition-rules.specs.js.html">lib/execute-transition-rules.specs.js</a></span></td>
<td data-ice="identifier" class="identifiers">-</td>
<td class="coverage"><span data-ice="coverage">-</span></td>
<td style="display: none;" data-ice="size">1142 byte</td>
<td style="display: none;" data-ice="lines">41</td>
<td style="display: none;" data-ice="updated">2017-11-03 08:21:12 (UTC)</td>
</tr>
<tr data-ice="file">
<td data-ice="filePath"><span><a href="file/lib/find-current-transition.js.html">lib/find-current-transition.js</a></span></td>

@@ -98,3 +114,3 @@ <td data-ice="identifier" class="identifiers">-</td>

<td style="display: none;" data-ice="lines">14</td>
<td style="display: none;" data-ice="updated">2017-05-03 10:42:50 (UTC)</td>
<td style="display: none;" data-ice="updated">2017-10-04 09:25:19 (UTC)</td>
</tr>

@@ -107,3 +123,3 @@ <tr data-ice="file">

<td style="display: none;" data-ice="lines">26</td>
<td style="display: none;" data-ice="updated">2017-05-03 10:42:58 (UTC)</td>
<td style="display: none;" data-ice="updated">2017-10-04 09:25:19 (UTC)</td>
</tr>

@@ -116,3 +132,3 @@ <tr data-ice="file">

<td style="display: none;" data-ice="lines">36</td>
<td style="display: none;" data-ice="updated">2017-08-06 19:55:01 (UTC)</td>
<td style="display: none;" data-ice="updated">2017-10-04 09:25:19 (UTC)</td>
</tr>

@@ -125,21 +141,53 @@ <tr data-ice="file">

<td style="display: none;" data-ice="lines">91</td>
<td style="display: none;" data-ice="updated">2017-05-03 10:40:31 (UTC)</td>
<td style="display: none;" data-ice="updated">2017-10-04 09:25:19 (UTC)</td>
</tr>
<tr data-ice="file">
<td data-ice="filePath"><span><a href="file/lib/fsm.fluent-api.specs.js.html">lib/fsm.fluent-api.specs.js</a></span></td>
<td data-ice="identifier" class="identifiers">-</td>
<td class="coverage"><span data-ice="coverage">-</span></td>
<td style="display: none;" data-ice="size">1736 byte</td>
<td style="display: none;" data-ice="lines">52</td>
<td style="display: none;" data-ice="updated">2017-11-05 09:30:47 (UTC)</td>
</tr>
<tr data-ice="file">
<td data-ice="filePath"><span><a href="file/lib/fsm.js.html">lib/fsm.js</a></span></td>
<td data-ice="identifier" class="identifiers"><span><a href="class/lib/fsm.js~FSM.html">FSM</a></span></td>
<td class="coverage"><span data-ice="coverage">100 %</span><span data-ice="coverageCount" class="coverage-count">14/14</span></td>
<td style="display: none;" data-ice="size">11036 byte</td>
<td style="display: none;" data-ice="lines">362</td>
<td style="display: none;" data-ice="updated">2017-08-06 19:53:44 (UTC)</td>
<td style="display: none;" data-ice="size">12387 byte</td>
<td style="display: none;" data-ice="lines">395</td>
<td style="display: none;" data-ice="updated">2017-11-05 13:00:02 (UTC)</td>
</tr>
<tr data-ice="file">
<td data-ice="filePath"><span><a href="file/lib/fsm.rest-api.specs.js.html">lib/fsm.rest-api.specs.js</a></span></td>
<td data-ice="identifier" class="identifiers">-</td>
<td class="coverage"><span data-ice="coverage">-</span></td>
<td style="display: none;" data-ice="size">4912 byte</td>
<td style="display: none;" data-ice="lines">248</td>
<td style="display: none;" data-ice="updated">2017-11-05 09:33:44 (UTC)</td>
</tr>
<tr data-ice="file">
<td data-ice="filePath"><span><a href="file/lib/fsm.specs.js.html">lib/fsm.specs.js</a></span></td>
<td data-ice="identifier" class="identifiers">-</td>
<td class="coverage"><span data-ice="coverage">-</span></td>
<td style="display: none;" data-ice="size">18893 byte</td>
<td style="display: none;" data-ice="lines">745</td>
<td style="display: none;" data-ice="updated">2017-08-06 19:41:02 (UTC)</td>
<td style="display: none;" data-ice="size">8589 byte</td>
<td style="display: none;" data-ice="lines">348</td>
<td style="display: none;" data-ice="updated">2017-11-05 12:57:17 (UTC)</td>
</tr>
<tr data-ice="file">
<td data-ice="filePath"><span><a href="file/lib/fsm.statics.specs.js.html">lib/fsm.statics.specs.js</a></span></td>
<td data-ice="identifier" class="identifiers">-</td>
<td class="coverage"><span data-ice="coverage">-</span></td>
<td style="display: none;" data-ice="size">677 byte</td>
<td style="display: none;" data-ice="lines">24</td>
<td style="display: none;" data-ice="updated">2017-11-05 09:29:49 (UTC)</td>
</tr>
<tr data-ice="file">
<td data-ice="filePath"><span><a href="file/lib/fsm.transition-execution.specs.js.html">lib/fsm.transition-execution.specs.js</a></span></td>
<td data-ice="identifier" class="identifiers">-</td>
<td class="coverage"><span data-ice="coverage">-</span></td>
<td style="display: none;" data-ice="size">9421 byte</td>
<td style="display: none;" data-ice="lines">355</td>
<td style="display: none;" data-ice="updated">2017-11-05 09:47:30 (UTC)</td>
</tr>
<tr data-ice="file">
<td data-ice="filePath"><span><a href="file/lib/get-all-taken-names.js.html">lib/get-all-taken-names.js</a></span></td>

@@ -150,3 +198,3 @@ <td data-ice="identifier" class="identifiers">-</td>

<td style="display: none;" data-ice="lines">11</td>
<td style="display: none;" data-ice="updated">2017-04-04 21:37:58 (UTC)</td>
<td style="display: none;" data-ice="updated">2017-10-04 09:25:19 (UTC)</td>
</tr>

@@ -159,3 +207,3 @@ <tr data-ice="file">

<td style="display: none;" data-ice="lines">6</td>
<td style="display: none;" data-ice="updated">2017-08-06 19:55:37 (UTC)</td>
<td style="display: none;" data-ice="updated">2017-10-04 09:25:19 (UTC)</td>
</tr>

@@ -166,5 +214,5 @@ <tr data-ice="file">

<td class="coverage"><span data-ice="coverage">-</span></td>
<td style="display: none;" data-ice="size">1404 byte</td>
<td style="display: none;" data-ice="lines">67</td>
<td style="display: none;" data-ice="updated">2017-08-04 14:30:03 (UTC)</td>
<td style="display: none;" data-ice="size">1642 byte</td>
<td style="display: none;" data-ice="lines">75</td>
<td style="display: none;" data-ice="updated">2017-11-04 15:24:41 (UTC)</td>
</tr>

@@ -175,5 +223,5 @@ <tr data-ice="file">

<td class="coverage"><span data-ice="coverage">-</span></td>
<td style="display: none;" data-ice="size">1723 byte</td>
<td style="display: none;" data-ice="lines">91</td>
<td style="display: none;" data-ice="updated">2017-08-04 14:26:46 (UTC)</td>
<td style="display: none;" data-ice="size">2370 byte</td>
<td style="display: none;" data-ice="lines">130</td>
<td style="display: none;" data-ice="updated">2017-11-04 15:24:41 (UTC)</td>
</tr>

@@ -184,5 +232,5 @@ <tr data-ice="file">

<td class="coverage"><span data-ice="coverage">-</span></td>
<td style="display: none;" data-ice="size">1055 byte</td>
<td style="display: none;" data-ice="size">1086 byte</td>
<td style="display: none;" data-ice="lines">33</td>
<td style="display: none;" data-ice="updated">2017-05-03 11:32:37 (UTC)</td>
<td style="display: none;" data-ice="updated">2017-11-05 09:28:19 (UTC)</td>
</tr>

@@ -193,5 +241,5 @@ <tr data-ice="file">

<td class="coverage"><span data-ice="coverage">-</span></td>
<td style="display: none;" data-ice="size">2138 byte</td>
<td style="display: none;" data-ice="lines">39</td>
<td style="display: none;" data-ice="updated">2017-05-03 11:14:19 (UTC)</td>
<td style="display: none;" data-ice="size">2385 byte</td>
<td style="display: none;" data-ice="lines">47</td>
<td style="display: none;" data-ice="updated">2017-11-05 09:28:39 (UTC)</td>
</tr>

@@ -204,3 +252,3 @@ <tr data-ice="file">

<td style="display: none;" data-ice="lines">11</td>
<td style="display: none;" data-ice="updated">2017-04-04 21:38:08 (UTC)</td>
<td style="display: none;" data-ice="updated">2017-10-04 09:25:19 (UTC)</td>
</tr>

@@ -213,3 +261,3 @@ <tr data-ice="file">

<td style="display: none;" data-ice="lines">16</td>
<td style="display: none;" data-ice="updated">2017-03-06 21:33:14 (UTC)</td>
<td style="display: none;" data-ice="updated">2017-10-04 09:25:19 (UTC)</td>
</tr>

@@ -216,0 +264,0 @@ </tbody>

@@ -13,2 +13,3 @@ 'use strict';

const executeTransitionRules = require('./execute-transition-rules');
const isValidStateValue = require('./is-valid-state-value');

@@ -252,3 +253,3 @@ const callIfSet = async (handler, ctx, args) => {

* ```
* @param {(string|object} transition.form defines the state that is created after the transition<br><br>
* @param {(string|object|function} transition.to defines the state that is created after the transition<br><br>
* Supported syntax:

@@ -260,2 +261,5 @@ * ```javascript

* to "s1" eg. patches the existing state object representation
* async (state, ctx) -> String => stets state from result of the function
* { state: async (state, ctx) -> String } => stets substate from result of the function
* the result of the function mus be a vaild state string value
* ```

@@ -309,4 +313,23 @@ */

const ctx = {
transition: transitionName,
from,
fsm: this,
};
if (this._data) {
ctx.data = this._data;
}
const execTransitionFunction = async (fn, state, ctxArg) => {
const res = await fn(state, ctxArg);
if (!isValidStateValue(res)) {
throw new Error(`Invalid "to" function result: "${res}" not a valid state string`);
}
return res;
};
if (currentTransition.to !== '*') {
if (typeof currentTransition.to === 'string') {
if (typeof currentTransition.to === 'function') {
to = await execTransitionFunction(currentTransition.to, from, ctx);
} else if (typeof currentTransition.to === 'string') {
to = currentTransition.to;

@@ -316,2 +339,9 @@ } else if (typeof currentTransition.to === 'object') {

const newStateObj = Object.assign({}, stateObj, currentTransition.to);
for (const key of Object.keys(currentTransition.to)) {
if (typeof currentTransition.to[key] === 'function') {
newStateObj[key] = await execTransitionFunction(currentTransition.to[key], stateObj[key], ctx);
} else {
newStateObj[key] = currentTransition.to[key];
}
}
to = FSM.compositState.build(newStateObj);

@@ -321,12 +351,5 @@ }

const ctx = {
transition: transitionName,
from,
to,
fsm: this,
};
if (this._data) {
ctx.data = this._data;
}
ctx.to = to;
const rules = currentTransition.rules || [];

@@ -333,0 +356,0 @@ const error = executeTransitionRules(rules, ctx);

@@ -9,127 +9,4 @@ 'use strict';

const { expect } = require('chai');
const compositState = require('./composit-state');
describe('FSM', () => {
describe('statics', () => {
it('exposes compositState tool', () => {
expect(FSM.compositState).to.equal(compositState);
});
it('cannot set compositState', () => {
expect(() => { FSM.compositState = () => {}; }).to.throw();
});
it('exposes toFunctionName() helper', () => {
expect(FSM.toFunctionName('test:transition:oida')).to.equal('testTransitionOida');
});
});
describe('can use state Objects (compositState)', () => {
it('to in object notation patches from stat', async () => {
const f = new FSM({
initialState: 'ns1:def|ns2:fe',
transitions: [
{ name: 't1', from: { ns1: 'def', ns2: 'fe' }, to: { ns1: 'b' } },
],
});
// expect(f.states()).to.eql(['ns1:def|ns2:fe', 'ns1:b|ns2:fe'].sort());
await f.t1();
expect(f.state).to.equal('ns1:b|ns2:fe');
});
it('creates state permutation with object notation', async () => {
const f = new FSM({
initialState: 'k1:k11|k2:k21',
transitions: [
{ name: 't1', from: { k1: ['k11', 'k12'], k2: ['k21', 'k22'] }, to: '*' },
],
});
f.init('k1:k11|k2:k21');
await f.t1();
expect(f.state).to.equal('k1:k11|k2:k21');
f.init('k1:k12|k2:k21');
await f.t1();
expect(f.state).to.equal('k1:k12|k2:k21');
f.init('k1:k11|k2:k22');
await f.t1();
expect(f.state).to.equal('k1:k11|k2:k22');
f.init('k1:k12|k2:k22');
await f.t1();
expect(f.state).to.equal('k1:k12|k2:k22');
});
});
describe('fluent build API', () => {
it('can add transition', async () => {
const f = new FSM({});
f.addTransition({ name: 'trans', from: 'a', to: 'b' })
.addTransition({ name: 'trans2', from: ['a', 'b'], to: 'c' })
.init('a');
await f.trans();
expect(f.state).to.equal('b');
await f.trans2();
expect(f.state).to.equal('c');
});
it('can add transition array', async () => {
const f = new FSM({});
f.addTransition([{ name: 'trans', from: 'a', to: 'b' },
{ name: 'trans2', from: ['a', 'b'], to: 'c' }])
.init('a');
await f.trans();
expect(f.state).to.equal('b');
await f.trans2();
expect(f.state).to.equal('c');
});
it('validates transitions', () => {
const f = new FSM({});
expect(() => f.addTransition({})).to.throw(/Invalid transition/);
});
it('does not allow trasition name clashes', () => {
const f = new FSM({});
f.addTransition({ name: 'a-to-b', from: 'a', to: 'b' });
expect(() => f.addTransition({ name: 'a:to:b', from: 'a', to: 'b' })).to.throw(/Ambigious transtion name/);
});
it('does not allow transiion named like native FSM methods', () => {
const f = new FSM({});
expect(() => f.addTransition({ name: 'execute', from: 'a', to: 'b' })).to.throw(/Forbidden transition name/);
expect(() => f.addTransition({ name: 'add:transition', from: 'a', to: 'b' })).to.throw(/Forbidden transition name/);
});
});
describe('large machine', () => {
const transitions = [];
const cntSubstate = 100;
const cntValue = 100;
const cntTransiotions = 100;
before(() => {
const state = {};
for (let i = 0; i < cntSubstate; i++) {
state[`substate_${i}`] = [];
for (let j = 0; j < cntValue; j++) {
state[`substate_${i}`].push(`value_${i}_${j}`);
}
}
for (let t = 0; t < cntTransiotions; t++) {
transitions.push({
name: `trans:${t}`,
from: state,
to: '*',
});
}
});
it('initialize large FSM', async () => {
const d1 = new Date();
const test = new FSM({ transitions });
const d2 = new Date();
const ms = d2.getTime() - d1.getTime();
expect(ms).to.lt(1000);
expect(test).to.exist;
});
});
describe('ctor, API & state constrainsts', () => {

@@ -243,7 +120,5 @@ let fsm;

});
});
describe('transition definitions', () => {
it('can use array in from clause', async () => {
const fsm = new FSM({
fsm = new FSM({
initialState: 'a',

@@ -271,586 +146,206 @@ transitions: [

describe('saveState', () => {
it('can use async "saveState"', async () => {
let saved;
describe('"to" state functions', () => {
it('can set to: async () => newState', async () => {
const fsm = new FSM({
initialState: 'a',
transitions: [{ name: 'trans', from: 'a', to: 'b' }],
saveState: async (ctx) => {
await bb.delay(5);
saved = ctx.fsm.state;
},
transitions: [
{ name: 'trans1', from: 'a', to: async () => 'b' },
],
});
await fsm.trans();
expect(saved).to.equal('b');
await fsm.trans1();
expect(fsm.state).to.equal('b');
});
it('can use sync "saveState"', async () => {
let ctx, args;
it('it awaits the async function', async () => {
const fsm = new FSM({
initialState: 'a',
transitions: [{ name: 'trans', from: 'a', to: 'b' }],
saveState: (c, a) => {
ctx = c;
args = a;
},
transitions: [
{ name: 'trans1',
from: 'a',
to: async () => {
await bb.delay(10);
return 'b';
} },
],
});
await fsm.trans(42, 'test');
expect(ctx).to.eql({
from: 'a',
to: 'b',
transition: 'trans',
fsm,
results: {
willChangeState: null,
beforeTrans: null,
didChangeState: null,
willSaveState: null,
didSaveState: null,
saveState: undefined,
},
});
expect(args).to.eql([42, 'test']);
});
it('throw when exection is throw in sync "saveState"', async () => {
let error;
try {
const fsm = new FSM({
initialState: 'a',
transitions: [{ name: 'trans', from: 'a', to: 'b' }],
saveState: () => {
throw new Error('save failed');
},
});
await fsm.trans();
} catch (e) {
error = e;
}
expect(error).to.exist;
expect(error.message).to.equal('save failed');
await fsm.trans1();
expect(fsm.state).to.equal('b');
});
it('throw when exection is throw in async "saveState"', async () => {
let error;
try {
const fsm = new FSM({
initialState: 'a',
transitions: [{ name: 'trans', from: 'a', to: 'b' }],
saveState: async () => {
await bb.delay(20);
throw new Error('save failed');
},
});
await fsm.trans();
} catch (e) {
error = e;
}
expect(error).to.exist;
expect(error.message).to.equal('save failed');
});
});
describe('transiton events', () => {
let fsm, cfg, ts;
const data = { test: 'data' };
beforeEach(() => {
ts = [];
cfg = {
it('aborts transition & throws error whe function throws', async () => {
const fsm = new FSM({
initialState: 'a',
data,
transitions: [{ name: 'trans', from: 'a', to: 'b' }],
saveState: () => 'saveState',
willChangeState: async (ctx, a1, a2) => {
await bb.delay(5);
ts.push({ name: 'willChangeState', ctx, args: [a1, a2] });
return 'willChangeState';
},
didChangeState: async (ctx, ...args) => {
await bb.delay(5);
ts.push({ name: 'didChangeState', ctx, args });
return 'didChangeState';
},
willSaveState: async (ctx, ...args) => {
await bb.delay(5);
ts.push({ name: 'willSaveState', ctx, args });
return 'willSaveState';
},
didSaveState: async (ctx, ...args) => {
await bb.delay(5);
ts.push({ name: 'didSaveState', ctx, args });
return 'didSaveState';
},
eventHandler: {
beforeTrans: async (ctx, ...args) => {
await bb.delay(5);
ts.push({ name: 'beforeTrans', ctx, args });
return 'beforeTrans';
},
afterTrans: async (ctx, ...args) => {
await bb.delay(5);
ts.push({ name: 'afterTrans', ctx, args });
return 'afterTrans';
},
},
};
});
it('calls transition methods with "ctx" and args', async () => {
fsm = new FSM(cfg);
await fsm.trans(42, 'test');
expect(ts[0].ctx).to.eql({
from: 'a',
to: 'b',
transition: 'trans',
fsm,
data,
results: {
willChangeState: 'willChangeState',
beforeTrans: 'beforeTrans',
didChangeState: 'didChangeState',
willSaveState: 'willSaveState',
didSaveState: 'didSaveState',
saveState: 'saveState',
},
transitions: [
{ name: 'trans1',
from: 'a',
to: async () => { throw new Error('asf'); } },
],
});
expect(ts[0].args).to.eql([42, 'test']);
});
it('with "execute(trasnition)" calls transition methods with "ctx" and args', async () => {
fsm = new FSM(cfg);
await fsm.execute('trans', 42, 'test');
expect(ts[0].ctx).to.eql({
from: 'a',
to: 'b',
transition: 'trans',
fsm,
data,
results: {
willChangeState: 'willChangeState',
beforeTrans: 'beforeTrans',
didChangeState: 'didChangeState',
willSaveState: 'willSaveState',
didSaveState: 'didSaveState',
saveState: 'saveState',
},
});
expect(ts[0].args).to.eql([42, 'test']);
});
it('calls transition events in order', async () => {
fsm = new FSM(cfg);
await fsm.trans();
expect(ts[0].name).to.equal('willChangeState');
expect(ts[1].name).to.equal('beforeTrans');
expect(ts[2].name).to.equal('didChangeState');
expect(ts[3].name).to.equal('willSaveState');
expect(ts[4].name).to.equal('didSaveState');
expect(ts[5].name).to.equal('afterTrans');
});
it('returns all transitionHanlder results', async () => {
fsm = new FSM(cfg);
const result = await fsm.trans();
expect(result).to.eql({
willChangeState: 'willChangeState',
beforeTrans: 'beforeTrans',
afterTrans: 'afterTrans',
didChangeState: 'didChangeState',
willSaveState: 'willSaveState',
saveState: 'saveState',
didSaveState: 'didSaveState',
});
});
it('throw error when beforeTransiton handler fails does not change state', async () => {
cfg.eventHandler.beforeTrans = async () => {
await bb.delay(5);
throw new Error('derdo');
};
let error;
let thrown = false;
try {
fsm = new FSM(cfg);
await fsm.trans();
await fsm.trans1();
} catch (e) {
error = e;
thrown = true;
}
expect(error).to.exist;
expect(error.message).to.equal('derdo');
expect(ts[0].name).to.equal('willChangeState');
expect(ts.length).to.equal(1);
expect(thrown).to.be.true;
expect(fsm.state).to.equal('a');
});
it('throw error when afterTransiton handler fails changes state', async () => {
cfg.eventHandler.afterTrans = async () => {
await bb.delay(5);
throw new Error('derdo');
};
let error;
try {
fsm = new FSM(cfg);
await fsm.trans();
} catch (e) {
error = e;
}
expect(error).to.exist;
expect(error.message).to.equal('derdo');
expect(ts[0].name).to.equal('willChangeState');
expect(ts[1].name).to.equal('beforeTrans');
expect(ts[2].name).to.equal('didChangeState');
expect(ts[3].name).to.equal('willSaveState');
expect(ts[4].name).to.equal('didSaveState');
expect(ts.length).to.equal(5);
expect(fsm.state).to.equal('b');
});
});
describe('global transition events', () => {
let fsm, cfg, ts;
beforeEach(() => {
ts = [];
cfg = {
it('validates the functions resukt to a valid state string', async () => {
const fsm = new FSM({
initialState: 'a',
transitions: [{ name: 'trans', from: 'a', to: 'b' }],
saveState: () => {},
willChangeState: (ctx, ...args) => {
ts.push({ name: 'willChangeState', ctx, args });
},
didChangeState: (ctx, ...args) => {
ts.push({ name: 'didChangeState', ctx, args });
},
willSaveState: (ctx, ...args) => {
ts.push({ name: 'willSaveState', ctx, args });
},
didSaveState: (ctx, ...args) => {
ts.push({ name: 'didSaveState', ctx, args });
},
};
});
transitions: [
{ name: 'trans1',
from: 'a',
to: async () => '' },
],
});
it('calls transition events in order', async () => {
fsm = new FSM(cfg);
await fsm.trans();
expect(ts[0].name).to.equal('willChangeState');
expect(ts[1].name).to.equal('didChangeState');
expect(ts[2].name).to.equal('willSaveState');
expect(ts[3].name).to.equal('didSaveState');
});
it('calls will & didSaveState only called when saveState is defined', async () => {
delete cfg.saveState;
fsm = new FSM(cfg);
await fsm.trans();
expect(ts[0].name).to.equal('willChangeState');
expect(ts[1].name).to.equal('didChangeState');
expect(ts.length).to.equal(2);
});
it('throw error when willChangeState & state does not change', async () => {
cfg.willChangeState = () => {
throw new Error('derdo');
};
let error;
let err;
try {
fsm = new FSM(cfg);
await fsm.trans();
await fsm.trans1();
} catch (e) {
error = e;
err = e;
}
expect(error).to.exist;
expect(error.message).to.equal('derdo');
expect(fsm.state).to.equal('a');
expect(err.message).to.contain('not a valid state string');
});
it('throw error when didChangeState & state changes', async () => {
cfg.didChangeState = () => {
throw new Error('derdo');
};
let error;
try {
fsm = new FSM(cfg);
await fsm.trans();
} catch (e) {
error = e;
}
expect(error).to.exist;
expect(error.message).to.equal('derdo');
expect(fsm.state).to.equal('b');
});
});
describe('race conditions', () => {
let fsm, cfg;
beforeEach(() => {
cfg = {
it('passes the state value as first argument to "to" function', async () => {
let stateArg;
const fsm = new FSM({
initialState: 'a',
transitions: [{ name: 'trans', from: ['a', 'b'], to: 'b' }, { name: 'trans2', from: ['a', 'b'], to: 'a' }],
saveState: () => {},
willChangeState: () => bb.delay(5),
};
});
transitions: [
{ name: 'trans1',
from: 'a',
to: async (state) => {
stateArg = state;
state = 'asgsdg'; // eslint-disable-line
return 'b';
} },
it('cannot start transition while anothr one is running', (done) => {
fsm = new FSM(cfg);
fsm.trans();
fsm.trans2().then(() => done(new Error('should have failed'))).catch((err) => {
expect(err.message).to.equal('Cannot start transition when during running transition');
done();
],
});
});
it('can run transition sequential', async () => {
fsm = new FSM(cfg);
await fsm.trans();
await fsm.trans2();
});
it('can run transtition after a trasition was abourted', async () => {
cfg.eventHandler = {
beforeTrans: async () => {
await bb.delay(5);
throw new Error('uuppps');
},
};
fsm = new FSM(cfg);
try {
await fsm.trans();
} catch (e) {}
await fsm.trans2();
await fsm.trans1();
expect(stateArg).to.equal('a');
});
});
describe('transition REST API', () => {
const data = {
resId: '42',
_embedded: {
event: {
resId: '22',
},
},
};
let fsm, cfg;
let savedContext;
beforeEach(() => {
cfg = {
it('passes the ctx object as second argument to "to" function', async () => {
let ctxArg;
const fsm = new FSM({
initialState: 'a',
transitions: [{
name: 't1',
from: '*',
to: 'a',
api: {
path: '/entity/trans',
method: 'patch',
},
}, {
name: 't2',
from: '*',
to: 'a',
api: {
path: '/entity/{resId}/trans/{subId}',
method: 'patch',
params: {
resId: 'data.resId',
subId: 'data._embedded.event.resId',
},
},
}, {
name: 't3',
from: '*',
to: 'a',
}, {
name: 't4',
from: '*',
to: 'a',
api: {
path: '/{entity}/{resId}/trans/{subId}',
method: 'patch',
params: {
resId: 'data.resId',
subId: 'data._embedded.event.resId',
},
},
}, {
name: 't5',
from: '*',
to: 'a',
api: {
path: '/entity/trans',
method: 'patch',
},
rules: [
() => {},
() => {},
],
}, {
name: 't6',
from: '*',
to: 'a',
api: {
path: '/entity/trans',
method: 'patch',
},
rules: [
() => {},
() => { throw new Error('some error'); },
],
}, {
name: 't7',
from: '*',
to: 'a',
api: {
path: '/entity/trans',
method: 'patch',
},
rules: [
(ctx) => { savedContext = ctx; },
],
}],
saveState: () => {},
willChangeState: () => bb.delay(5),
data,
};
});
transitions: [
{ name: 'trans1',
from: 'a',
to: async (state, ctx) => {
ctxArg = ctx;
return 'b';
} },
it('exposes "restApi()" function', async () => {
fsm = new FSM(cfg);
expect(fsm.restApi).to.be.a('function');
});
it('filters transitions without "api" property', async () => {
fsm = new FSM(cfg);
const r = fsm.restApi();
expect(r.t1).to.exist;
expect(r.t2).to.exist;
expect(r.t3).not.to.exist;
});
it('returns parsed api object', async () => {
fsm = new FSM(cfg);
const r = fsm.restApi();
expect(r.t1).to.eql({
href: '/entity/trans',
method: 'patch',
});
});
it('returns "self" when declared', async () => {
fsm = new FSM(Object.assign({}, cfg, {
api: {
self: {
path: '/entity/{resId}',
},
params: {
resId: 'data.resId',
},
],
data: {
as: 'sa',
},
}));
const r = fsm.restApi();
expect(r.self).to.eql({
href: '/entity/42',
method: 'get',
});
});
it('can mix global & transition local params in same route', () => {
fsm = new FSM(Object.assign({}, cfg, {
api: {
data: {
entity: 'events',
},
self: {
path: '/{entity}/{resId}',
},
params: {
entity: 'api.data.entity',
resId: 'data.resId',
},
},
}));
const r = fsm.restApi();
expect(r.t4).to.eql({
href: '/events/42/trans/22',
method: 'patch',
});
await fsm.trans1();
expect(ctxArg.fsm).to.equal(fsm);
expect(ctxArg.data).to.equal(fsm.data);
});
it('can declare static params data in "api.data" object', () => {
fsm = new FSM(Object.assign({}, cfg, {
api: {
data: {
entity: 'events',
},
self: {
path: '/{entity}/{resId}',
},
params: {
entity: 'api.data.entity',
resId: 'data.resId',
},
it('works on state object properties', async () => {
let ctxArg, stateArg;
const fsm = new FSM({
initialState: FSM.compositState.build({ s1: 's1', s2: 's2' }),
transitions: [
{ name: 'trans1',
from: { s1: 's1', s2: 's2' },
to: { s1: async (state, ctx) => {
stateArg = state;
ctxArg = ctx;
return 'b';
} } },
],
data: {
as: 'sa',
},
}));
const r = fsm.restApi();
expect(r.self).to.eql({
href: '/events/42',
method: 'get',
});
});
it('should return the restApi object if all rules are satisfied', async () => {
fsm = new FSM(cfg);
const r = fsm.restApi();
expect(r.t5).to.eql({
href: '/entity/trans',
method: 'patch',
});
await fsm.trans1();
expect(stateArg).to.equal('s1');
expect(ctxArg.fsm).to.equal(fsm);
expect(ctxArg.data).to.equal(fsm.data);
expect(FSM.compositState.parse(fsm.state)).to.eql({ s1: 'b', s2: 's2' });
});
});
it('should return a restApi object with a error message if not all rules are satisfied', async () => {
fsm = new FSM(cfg);
const r = fsm.restApi();
expect(r.t6).to.eql({
href: '/entity/trans',
method: 'patch',
error: {
message: 'some error',
},
describe('can use state Objects (compositState)', () => {
it('to in object notation patches from stat', async () => {
const f = new FSM({
initialState: 'ns1:def|ns2:fe',
transitions: [
{ name: 't1', from: { ns1: 'def', ns2: 'fe' }, to: { ns1: 'b' } },
],
});
// expect(f.states()).to.eql(['ns1:def|ns2:fe', 'ns1:b|ns2:fe'].sort());
await f.t1();
expect(f.state).to.equal('ns1:b|ns2:fe');
});
it('should pass the context of the fsm to the rules functions', async () => {
fsm = new FSM(cfg);
fsm.restApi();
expect(savedContext.data).to.eql(data);
});
it('should execute the rules functions on an fsm without a data object without any error', async () => {
const testCfg = {
initialState: 'a',
it('creates state permutation with object notation', async () => {
const f = new FSM({
initialState: 'k1:k11|k2:k21',
transitions: [
{
name: 't',
from: '*',
to: 'a',
api: {
path: '/entity/trans',
method: 'patch',
},
rules: [
(ctx) => { savedContext = ctx; },
],
},
{ name: 't1', from: { k1: ['k11', 'k12'], k2: ['k21', 'k22'] }, to: '*' },
],
saveState: () => {},
willChangeState: () => bb.delay(5),
};
});
fsm = new FSM(testCfg);
const r = fsm.restApi();
expect(savedContext.data).to.be.undefined;
expect(r.t).to.eql({
href: '/entity/trans',
method: 'patch',
});
f.init('k1:k11|k2:k21');
await f.t1();
expect(f.state).to.equal('k1:k11|k2:k21');
f.init('k1:k12|k2:k21');
await f.t1();
expect(f.state).to.equal('k1:k12|k2:k21');
f.init('k1:k11|k2:k22');
await f.t1();
expect(f.state).to.equal('k1:k11|k2:k22');
f.init('k1:k12|k2:k22');
await f.t1();
expect(f.state).to.equal('k1:k12|k2:k22');
});
});
describe('large machine', () => {
const transitions = [];
const cntSubstate = 100;
const cntValue = 100;
const cntTransiotions = 100;
before(() => {
const state = {};
for (let i = 0; i < cntSubstate; i++) {
state[`substate_${i}`] = [];
for (let j = 0; j < cntValue; j++) {
state[`substate_${i}`].push(`value_${i}_${j}`);
}
}
for (let t = 0; t < cntTransiotions; t++) {
transitions.push({
name: `trans:${t}`,
from: state,
to: '*',
});
}
});
it('initialize large FSM', async () => {
const d1 = new Date();
const test = new FSM({ transitions });
const d2 = new Date();
const ms = d2.getTime() - d1.getTime();
expect(ms).to.lt(1000);
expect(test).to.exist;
});
});
});

@@ -14,3 +14,3 @@ 'use strict';

fromVal.forEach((val) => {
if (!isValidStateValue(val) || from === '__uninitialized__') {
if ((typeof val === 'string') && (!isValidStateValue(val) || from === '__uninitialized__')) {
throw new Error(`Invalid state value! from: ${from}`);

@@ -17,0 +17,0 @@ }

@@ -39,2 +39,10 @@ 'use strict';

});
it('"to" can be a function', () => {
parseTrasition({ name: 't', from: 'a', to: () => {} });
});
it('"to" substate can be a function', () => {
parseTrasition({ name: 't', from: { s1: 'a', s2: 'b' }, to: { s1: 'a', s2: () => {} } });
});
});
{
"name": "@trigo/fsm",
"version": "3.3.1",
"version": "3.4.0",
"description": "FSM - Finite State Machine",

@@ -5,0 +5,0 @@ "main": "index.js",

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display