Compare commits
15 commits
Author | SHA1 | Date | |
---|---|---|---|
b573b06909 | |||
e0bb79dbdb | |||
e36d9f8544 | |||
284a7a45e3 | |||
89c7db9e81 | |||
ea0a6a7b07 | |||
aab0d93bce | |||
f62b1f6e8a | |||
ce00da10a5 | |||
7cfded56a8 | |||
16ced3d449 | |||
84ad5f6489 | |||
d03d5237da | |||
8a418335e2 | |||
512e194203 |
16 changed files with 496 additions and 50 deletions
4
.gitignore
vendored
4
.gitignore
vendored
|
@ -4,3 +4,7 @@ node_modules/
|
||||||
package-lock.json
|
package-lock.json
|
||||||
|
|
||||||
dist/
|
dist/
|
||||||
|
|
||||||
|
# direnv
|
||||||
|
.envrc
|
||||||
|
.direnv/
|
||||||
|
|
|
@ -1,6 +0,0 @@
|
||||||
{
|
|
||||||
"deno.enable": true,
|
|
||||||
"deno.lint": true,
|
|
||||||
"deno.unstable": false,
|
|
||||||
"tsserver.enable": false
|
|
||||||
}
|
|
58
Makefile
Normal file
58
Makefile
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
DENO := deno
|
||||||
|
COV := $(DENO) coverage cov_profile
|
||||||
|
FMT := $(DENO) fmt *.ts examples/*.ts
|
||||||
|
NPM_PUB := npm publish
|
||||||
|
|
||||||
|
D2 := wd2
|
||||||
|
DIAGRAMS := \
|
||||||
|
turnstile \
|
||||||
|
project
|
||||||
|
|
||||||
|
dev: fmt tests cov
|
||||||
|
|
||||||
|
ci: fmt-check tests-cov
|
||||||
|
|
||||||
|
tests: clean
|
||||||
|
$(DENO) test --coverage=cov_profile *.test.ts
|
||||||
|
|
||||||
|
tests-cov: tests
|
||||||
|
$(COV) --lcov > cov_profile/cov.lcov
|
||||||
|
|
||||||
|
cov:
|
||||||
|
$(COV)
|
||||||
|
|
||||||
|
fmt:
|
||||||
|
$(FMT)
|
||||||
|
|
||||||
|
fmt-check:
|
||||||
|
$(FMT) --check
|
||||||
|
|
||||||
|
build: clean | $(DIAGRAMS)
|
||||||
|
tsc && tsc -p tsconfig.cjs.json
|
||||||
|
|
||||||
|
clean:
|
||||||
|
@echo clean test coverage data
|
||||||
|
rm -rf cov_profile
|
||||||
|
@echo clean build target
|
||||||
|
rm -rf dist
|
||||||
|
@echo clean diagrams
|
||||||
|
rm -rf assets/*.svg
|
||||||
|
|
||||||
|
define d2rule
|
||||||
|
$(1)_args := $(D2) $$($(1)_theme_args) assets/$(1).d2 assets/$(1).svg
|
||||||
|
|
||||||
|
$(1): ; $$($(1)_args)
|
||||||
|
$(1)-w: ; $$($(1)_args) -w
|
||||||
|
endef
|
||||||
|
|
||||||
|
$(foreach d2name,$(DIAGRAMS),$(eval $(call d2rule,$(d2name))))
|
||||||
|
|
||||||
|
dry-pub:
|
||||||
|
$(NPM_PUB) --dry-run
|
||||||
|
|
||||||
|
pub:
|
||||||
|
$(NPM_PUB)
|
||||||
|
|
||||||
|
help:
|
||||||
|
cat Makefile
|
||||||
|
|
11
README.md
11
README.md
|
@ -26,28 +26,23 @@ const sm = new StateMachineBuilder()
|
||||||
.build(locked);
|
.build(locked);
|
||||||
```
|
```
|
||||||
|
|
||||||
You can find the full example in the [examples](./examples/) folder.
|
You can find more examples [here](./examples/README.md)
|
||||||
|
|
||||||
# Installation
|
# Installation
|
||||||
|
|
||||||
## Deno
|
## Deno
|
||||||
|
|
||||||
if you use the [Deno](https://deno.land), just add following to your
|
if you use the [Deno](https://deno.land), just add the following to your
|
||||||
`import_map.json`
|
`import_map.json`
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"imports": {
|
"imports": {
|
||||||
"it-fsm": "https://git.pleshevski.ru/pleshevskiy/it-fsm/src/commit/e3796c92639e3483a2a2a01a89912561561e796f/fsm.ts"
|
"it-fsm": "https://git.pleshevski.ru/pleshevskiy/it-fsm/raw/tag/v2.0.3/fsm.ts"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
or you can use branch instead
|
|
||||||
`https://git.pleshevski.ru/pleshevskiy/paren/raw/branch/main/`
|
|
||||||
|
|
||||||
> **Note**: I recommend to use a specific commit instead of branch
|
|
||||||
|
|
||||||
## Node.js
|
## Node.js
|
||||||
|
|
||||||
If you use the Node.js or in a browser as ES module, you may need to install it
|
If you use the Node.js or in a browser as ES module, you may need to install it
|
||||||
|
|
36
assets/project.d2
Normal file
36
assets/project.d2
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
# port: 43333
|
||||||
|
# layout: dagre
|
||||||
|
# theme: 101
|
||||||
|
# pad: 0
|
||||||
|
# sketch: true
|
||||||
|
|
||||||
|
direction: right
|
||||||
|
|
||||||
|
Project: {
|
||||||
|
pen: Pending {
|
||||||
|
shape: circle
|
||||||
|
width: 110
|
||||||
|
}
|
||||||
|
act: Active {
|
||||||
|
shape: circle
|
||||||
|
style.fill: "#9ae49a"
|
||||||
|
width: 110
|
||||||
|
}
|
||||||
|
com: Completed {
|
||||||
|
shape: circle
|
||||||
|
style.fill: "#eaea7e"
|
||||||
|
width: 110
|
||||||
|
}
|
||||||
|
arc: Archived {
|
||||||
|
shape: circle
|
||||||
|
style.fill: "#ff7e7e"
|
||||||
|
width: 110
|
||||||
|
}
|
||||||
|
|
||||||
|
pen -> act: Activate
|
||||||
|
pen -> arc: Archive
|
||||||
|
|
||||||
|
act -> com: Complete
|
||||||
|
|
||||||
|
arc -> pen: Restore
|
||||||
|
}
|
81
assets/project.svg
Normal file
81
assets/project.svg
Normal file
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 313 KiB |
26
assets/turnstile.d2
Normal file
26
assets/turnstile.d2
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
# port: 43334
|
||||||
|
# layout: elk
|
||||||
|
# theme: 101
|
||||||
|
# pad: 0
|
||||||
|
# sketch: true
|
||||||
|
|
||||||
|
direction: right
|
||||||
|
|
||||||
|
Turnstile: {
|
||||||
|
l: Locked {
|
||||||
|
shape: circle
|
||||||
|
style.fill: "#fd7373"
|
||||||
|
width: 110
|
||||||
|
}
|
||||||
|
u: Unlocked {
|
||||||
|
shape: circle
|
||||||
|
style.fill: "#59c061"
|
||||||
|
width: 110
|
||||||
|
}
|
||||||
|
|
||||||
|
l -> l: push
|
||||||
|
u -> l: push
|
||||||
|
|
||||||
|
l -> u: coin
|
||||||
|
u -> u: coin
|
||||||
|
}
|
81
assets/turnstile.svg
Normal file
81
assets/turnstile.svg
Normal file
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 307 KiB |
9
deno.lock
Normal file
9
deno.lock
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
{
|
||||||
|
"version": "2",
|
||||||
|
"remote": {
|
||||||
|
"https://deno.land/std@0.165.0/fmt/colors.ts": "9e36a716611dcd2e4865adea9c4bec916b5c60caad4cdcdc630d4974e6bb8bd4",
|
||||||
|
"https://deno.land/std@0.165.0/testing/_diff.ts": "a23e7fc2b4d8daa3e158fa06856bedf5334ce2a2831e8bf9e509717f455adb2c",
|
||||||
|
"https://deno.land/std@0.165.0/testing/_format.ts": "cd11136e1797791045e639e9f0f4640d5b4166148796cad37e6ef75f7d7f3832",
|
||||||
|
"https://deno.land/std@0.165.0/testing/asserts.ts": "1e340c589853e82e0807629ba31a43c84ebdcdeca910c4a9705715dfdb0f5ce8"
|
||||||
|
}
|
||||||
|
}
|
27
examples/README.md
Normal file
27
examples/README.md
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
# Examples
|
||||||
|
|
||||||
|
## Turnstile
|
||||||
|
|
||||||
|
An example of a simple mechanism that can be modeled by a state machine is a
|
||||||
|
turnstile. A turnstile, used to control access to subways and amusement park
|
||||||
|
rides, is a gate with three rotating arms at waist height, one across the
|
||||||
|
entryway. Initially the arms are locked, blocking the entry, preventing patrons
|
||||||
|
from passing through. Depositing a coin or token in a slot on the turnstile
|
||||||
|
unlocks the arms, allowing a single customer to push through. After the customer
|
||||||
|
passes through, the arms are locked again until another coin is inserted.
|
||||||
|
|
||||||
|
![Turnstile diagram](https://git.pleshevski.ru/pleshevskiy/it-fsm/raw/branch/main/assets/turnstile.svg)
|
||||||
|
|
||||||
|
```sh
|
||||||
|
deno run ./examples/turnstile.ts
|
||||||
|
```
|
||||||
|
|
||||||
|
## Project
|
||||||
|
|
||||||
|
This is more complex example illustrate all features of this library
|
||||||
|
|
||||||
|
![Project diagram](https://git.pleshevski.ru/pleshevskiy/it-fsm/raw/branch/main/assets/project.svg)
|
||||||
|
|
||||||
|
```sh
|
||||||
|
deno run ./examples/project.ts
|
||||||
|
```
|
|
@ -35,6 +35,7 @@ const smbProject = new StateMachineBuilder<
|
||||||
.withTransitions([
|
.withTransitions([
|
||||||
[ProjectStatus.Pending, [ProjectStatus.Active, ProjectStatus.Archived]],
|
[ProjectStatus.Pending, [ProjectStatus.Active, ProjectStatus.Archived]],
|
||||||
[ProjectStatus.Active, [ProjectStatus.Completed]],
|
[ProjectStatus.Active, [ProjectStatus.Completed]],
|
||||||
|
[ProjectStatus.Archived, [ProjectStatus.Pending]],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
async function main() {
|
async function main() {
|
141
flake.lock
Normal file
141
flake.lock
Normal file
|
@ -0,0 +1,141 @@
|
||||||
|
{
|
||||||
|
"nodes": {
|
||||||
|
"flake-utils": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1676283394,
|
||||||
|
"narHash": "sha256-XX2f9c3iySLCw54rJ/CZs+ZK6IQy7GXNY4nSOyu2QG4=",
|
||||||
|
"owner": "numtide",
|
||||||
|
"repo": "flake-utils",
|
||||||
|
"rev": "3db36a8b464d0c4532ba1c7dda728f4576d6d073",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "numtide",
|
||||||
|
"repo": "flake-utils",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"flake-utils_2": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1676283394,
|
||||||
|
"narHash": "sha256-XX2f9c3iySLCw54rJ/CZs+ZK6IQy7GXNY4nSOyu2QG4=",
|
||||||
|
"owner": "numtide",
|
||||||
|
"repo": "flake-utils",
|
||||||
|
"rev": "3db36a8b464d0c4532ba1c7dda728f4576d6d073",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "numtide",
|
||||||
|
"repo": "flake-utils",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"flake-utils_3": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1667395993,
|
||||||
|
"narHash": "sha256-nuEHfE/LcWyuSWnS8t12N1wc105Qtau+/OdUAjtQ0rA=",
|
||||||
|
"owner": "numtide",
|
||||||
|
"repo": "flake-utils",
|
||||||
|
"rev": "5aed5285a952e0b949eb3ba02c12fa4fcfef535f",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "numtide",
|
||||||
|
"repo": "flake-utils",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nixpkgs": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1676518424,
|
||||||
|
"narHash": "sha256-OsJSBwl9Hayh/bmxDtUyxm2U6btaBHuLvviE9KpMmwQ=",
|
||||||
|
"owner": "NixOS",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"rev": "a592a97fcedae7a06b8506623b25fd38a032ad13",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "NixOS",
|
||||||
|
"ref": "nixpkgs-unstable",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nixpkgs_2": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1676518424,
|
||||||
|
"narHash": "sha256-OsJSBwl9Hayh/bmxDtUyxm2U6btaBHuLvviE9KpMmwQ=",
|
||||||
|
"owner": "NixOS",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"rev": "a592a97fcedae7a06b8506623b25fd38a032ad13",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"id": "nixpkgs",
|
||||||
|
"type": "indirect"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nixpkgs_3": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1669261230,
|
||||||
|
"narHash": "sha256-AjddxRPd5y5jge77281P3O8+Cnafj842Xg59rwV4x+0=",
|
||||||
|
"owner": "NixOS",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"rev": "8e8b5f3b1e899bf5d250279578c0283705b8cdb4",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"id": "nixpkgs",
|
||||||
|
"type": "indirect"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": {
|
||||||
|
"inputs": {
|
||||||
|
"flake-utils": "flake-utils",
|
||||||
|
"nixpkgs": "nixpkgs",
|
||||||
|
"wd2": "wd2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"tools": {
|
||||||
|
"inputs": {
|
||||||
|
"flake-utils": "flake-utils_3",
|
||||||
|
"nixpkgs": "nixpkgs_3"
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1676488781,
|
||||||
|
"narHash": "sha256-lP2UWGwangM1OSBOx0EFXOtnSBuyTJn3yza3Ujb9/C8=",
|
||||||
|
"ref": "refs/heads/main",
|
||||||
|
"rev": "5f07d4e256c1d8caefd52563737b1e3c023b5d35",
|
||||||
|
"revCount": 21,
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://git.pleshevski.ru/mynix/tools"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://git.pleshevski.ru/mynix/tools"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"wd2": {
|
||||||
|
"inputs": {
|
||||||
|
"flake-utils": "flake-utils_2",
|
||||||
|
"nixpkgs": "nixpkgs_2",
|
||||||
|
"tools": "tools"
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1676583860,
|
||||||
|
"narHash": "sha256-LuGEJMECOtqFJHw65ErtEDa7MnF5e+QmiGO583ZK9BE=",
|
||||||
|
"ref": "refs/heads/main",
|
||||||
|
"rev": "1e05739a5e39b982e3670997a3845ab9741f4ca1",
|
||||||
|
"revCount": 13,
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://git.pleshevski.ru/pleshevskiy/wd2"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://git.pleshevski.ru/pleshevskiy/wd2"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": "root",
|
||||||
|
"version": 7
|
||||||
|
}
|
24
flake.nix
Normal file
24
flake.nix
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
{
|
||||||
|
inputs = {
|
||||||
|
nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
|
||||||
|
flake-utils.url = "github:numtide/flake-utils";
|
||||||
|
wd2.url = "git+https://git.pleshevski.ru/pleshevskiy/wd2";
|
||||||
|
};
|
||||||
|
|
||||||
|
outputs = { self, nixpkgs, flake-utils, wd2, ... }:
|
||||||
|
flake-utils.lib.eachDefaultSystem (system:
|
||||||
|
let
|
||||||
|
pkgs = import nixpkgs { inherit system; };
|
||||||
|
in
|
||||||
|
{
|
||||||
|
devShells.default = pkgs.mkShell {
|
||||||
|
packages = with pkgs; [
|
||||||
|
d2 # to format .d2 files
|
||||||
|
wd2.packages.${system}.default
|
||||||
|
deno
|
||||||
|
nodejs-18_x
|
||||||
|
nodePackages.typescript
|
||||||
|
];
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
|
@ -17,9 +17,9 @@
|
||||||
|
|
||||||
import {
|
import {
|
||||||
assertEquals,
|
assertEquals,
|
||||||
|
assertRejects,
|
||||||
assertThrows,
|
assertThrows,
|
||||||
assertThrowsAsync,
|
} from "https://deno.land/std@0.165.0/testing/asserts.ts";
|
||||||
} from "https://deno.land/std@0.105.0/testing/asserts.ts";
|
|
||||||
import * as fsm from "./fsm.ts";
|
import * as fsm from "./fsm.ts";
|
||||||
|
|
||||||
enum ProjectStatus {
|
enum ProjectStatus {
|
||||||
|
@ -168,7 +168,7 @@ Deno.test("should throw error if transition to the state doesn't exist", () => {
|
||||||
const sm = new fsm.StateMachineBuilder()
|
const sm = new fsm.StateMachineBuilder()
|
||||||
.withStates(Object.values(ProjectStatus))
|
.withStates(Object.values(ProjectStatus))
|
||||||
.build(ProjectStatus.Pending);
|
.build(ProjectStatus.Pending);
|
||||||
assertThrowsAsync(
|
assertRejects(
|
||||||
() => sm.tryChangeState(ProjectStatus.Active, null),
|
() => sm.tryChangeState(ProjectStatus.Active, null),
|
||||||
fsm.FsmError,
|
fsm.FsmError,
|
||||||
`cannot change state from "${ProjectStatus.Pending}" to "${ProjectStatus.Active}"`,
|
`cannot change state from "${ProjectStatus.Pending}" to "${ProjectStatus.Active}"`,
|
||||||
|
@ -195,7 +195,7 @@ Deno.test("should throw error if beforeExit action returns false", () => {
|
||||||
[ProjectStatus.Pending, [ProjectStatus.Active]],
|
[ProjectStatus.Pending, [ProjectStatus.Active]],
|
||||||
])
|
])
|
||||||
.build(ProjectStatus.Pending);
|
.build(ProjectStatus.Pending);
|
||||||
assertThrowsAsync(
|
assertRejects(
|
||||||
() => sm.tryChangeState(ProjectStatus.Active, null),
|
() => sm.tryChangeState(ProjectStatus.Active, null),
|
||||||
fsm.FsmError,
|
fsm.FsmError,
|
||||||
`cannot change state from "${ProjectStatus.Pending}" to "${ProjectStatus.Active}"`,
|
`cannot change state from "${ProjectStatus.Pending}" to "${ProjectStatus.Active}"`,
|
||||||
|
|
25
makefile
25
makefile
|
@ -1,25 +0,0 @@
|
||||||
DENO := deno
|
|
||||||
COV := $(DENO) coverage cov_profile
|
|
||||||
FMT := $(DENO) fmt *.ts examples/*.ts
|
|
||||||
|
|
||||||
dev: fmt tests cov
|
|
||||||
|
|
||||||
ci: fmt-check tests-cov
|
|
||||||
|
|
||||||
tests: clean
|
|
||||||
$(DENO) test --coverage=cov_profile *.test.ts
|
|
||||||
|
|
||||||
tests-cov: tests
|
|
||||||
$(COV) --lcov > cov_profile/cov.lcov
|
|
||||||
|
|
||||||
cov:
|
|
||||||
$(COV)
|
|
||||||
|
|
||||||
fmt:
|
|
||||||
$(FMT)
|
|
||||||
|
|
||||||
fmt-check:
|
|
||||||
$(FMT) --check
|
|
||||||
|
|
||||||
clean:
|
|
||||||
rm -rf cov_profile dist
|
|
|
@ -4,10 +4,7 @@
|
||||||
"description": "Simple finite state machine for nodejs",
|
"description": "Simple finite state machine for nodejs",
|
||||||
"readme": "README.md",
|
"readme": "README.md",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "tsc && tsc -p tsconfig.cjs.json",
|
"prepublishOnly": "make build"
|
||||||
"check": "tsc --noEmit && tsc -p tsconfig.cjs.json --noEmit",
|
|
||||||
"clean": "rm -rf ./dist",
|
|
||||||
"prepublishOnly": "npm run clean && npm run build"
|
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
|
@ -28,9 +25,6 @@
|
||||||
"url": "https://github.com/pleshevskiy/it-fsm/issues"
|
"url": "https://github.com/pleshevskiy/it-fsm/issues"
|
||||||
},
|
},
|
||||||
"homepage": "https://git.pleshevski.ru/pleshevskiy/it-fsm#readme",
|
"homepage": "https://git.pleshevski.ru/pleshevskiy/it-fsm#readme",
|
||||||
"devDependencies": {
|
|
||||||
"typescript": "^4.9.3"
|
|
||||||
},
|
|
||||||
"main": "./dist/cjs/fsm.js",
|
"main": "./dist/cjs/fsm.js",
|
||||||
"module": "./dist/esm/fsm.js",
|
"module": "./dist/esm/fsm.js",
|
||||||
"types": "./dist/esm/fsm.d.ts",
|
"types": "./dist/esm/fsm.d.ts",
|
||||||
|
|
Loading…
Reference in a new issue