Skip to content

Commit f1c8fd4

Browse files
committed
first commit
0 parents  commit f1c8fd4

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

41 files changed

+7551
-0
lines changed

.gitattributes

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
bin/** linguist-vendored
2+
*.js linguist-vendored

.gitignore

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
.idea
2+
.goose
3+
.goosehints
4+
coverage
5+
node_modules

.npmrc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
registry = "https://registry.npmjs.org/"

.prettierrc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{}

LICENSE

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
* PureMVC Typescript Utility - State Machine - Copyright © 2007-2025 Neil Manuell, Cliff Hall
2+
* PureMVC - Copyright © 2007-2025 Futurescale, Inc.
3+
* All rights reserved.
4+
5+
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
6+
7+
* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
8+
* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
9+
* Neither the name of Futurescale, Inc., PureMVC.org, nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
10+
11+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

README.md

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
## [PureMVC](http://puremvc.github.com/) [Typescript](https://github.com/PureMVC/puremvc-typescript-multicore-framework/wiki) Utility: State Machine
2+
This utility provides a simple yet effective Finite State Machine implementation, which allows the definition of discrete states, and the valid transitions to other states available from any given state, and the actions which trigger the transitions. A mechanism is provided for defining the entire state machine in JSON and having a fully populated StateMachine injected into the PureMVC app.
3+
4+
## Status
5+
Production - [Version 1.0.0](https://github.com/PureMVC/puremvc-typescript-util-statemachine/blob/master/VERSION)
6+
7+
## Platforms / Technologies
8+
* [Typescript](http://en.wikipedia.org/wiki/Typescript)
9+
10+
## State Representation
11+
* The States held and navigated by the StateMachine are instances of a State class, which carries several critical pieces of information about that State. Each State has optional associated Notifications to be sent on entry into the State and exit from the State.
12+
* The exiting notification carries a reference in the body to the State that we are transitioning to. This helps actors respond properly by anticipating the destination state.
13+
* The entering notification for a State carries a reference in the body to the state we are entering as well, in case you've sub-classed State to pass data.
14+
* It is up to the programmer to define and register commands or mediators with interest in these entering and exiting notifications, the state machine simply sends them at the appropriate times.
15+
* The State class also exposes methods for defining and removing transitions. A transition simply maps an action name to a target State name.
16+
17+
## State Transitions
18+
* The transition from one state to the next is triggered by any actor sending a StateMachine.ACTION Notification. Include the name of the action in the Notification's type parameter.
19+
* Actions are what trigger the StateMachine to initiate the transition from the one State to the next. There is no formal Action class at this time, it is merely a name that will trigger a State transition.
20+
* It is up to the application to ensure any special conditions for making the transition are met before sending the StateMachine.ACTION Notification, which will initiate the transition immediately if one is defined for the input action given the current State.
21+
* Any actor responding to the Notification sent when exiting a state may send a StateMachine.CANCEL notification, which will cause the StateMachine not to enter the next State. The programmer must insure that no other responses to the current State's exit notification need to be rolled back. This is best done by checking any items that could lead to a StateMachine.CANCEL being sent before initiating any exit activity such as visual transitions or form clearing/population, thus avoiding the need to rollback to restore the application to the state being exited.
22+
* Finally, when a transition is complete, the StateMachine sends a StateMachine.CHANGED Notification, with a reference to the new State. This is sent once any exiting notification for the previous state, as well as any entering notification for the new state have both been executed, and the current state of the StateMachine has been updated to the new state.
23+
24+
## FSM Injector
25+
* Also included in this release is useful class that allows you to define your FSM in an JSON format, and pass it to the FSMInjector where it will be parsed, and a fully populated StateMachine instance will be created and registered via your Facade.
26+
* The FSMInjector extends Notifier to give it a reference to the Facade for injecting the completed StateMachine.
27+
* The JSON format for the FSM Injector is simple. For instance here is the FSM for a door:
28+
29+
> {
30+
> initial: "CLOSED"
31+
> states: [
32+
> {
33+
> name: "OPENED"
34+
> entering: "openingNote"
35+
> transitions: [
36+
> {
37+
> action: "CLOSE"
38+
> target: "CLOSED"
39+
> },
40+
> ]
41+
> },
42+
> {
43+
> name: "CLOSED"
44+
> entering: "closingNote"
45+
> transitions: [
46+
> {
47+
> action: "OPEN"
48+
> target: "CLOSED"
49+
> },
50+
> {
51+
> action: "LOCK"
52+
> target: "LOCKED"
53+
> },
54+
> ]
55+
> },
56+
> {
57+
> name: "LOCKED"
58+
> entering: "lockingNote"
59+
> exiting: "unlockingNote"
60+
> transitions: [
61+
> {
62+
> action: "UNLOCK"
63+
> target: "CLOSED"
64+
> },
65+
> ]
66+
> }
67+
> }
68+
69+
* The above FSM defines three discrete states OPENED, CLOSED and LOCKED.
70+
* The actions OPEN, CLOSE and LOCK are used to trigger state transitions.
71+
* It is only possible to LOCK the door when it is CLOSED, because only the CLOSED state defines a transition targeting the LOCKED state.
72+
* It is not possible to OPEN the door from the LOCKED state because no transition is defined targeting the OPEN state.
73+
* And when you UNLOCK the door, it returns to the CLOSED state, where it is once again possible to OPEN or LOCK.
74+
* An exiting notification is defined only for exiting the OPEN state to illustrate that entering and exiting notifications are optional.
75+
76+
## License
77+
* PureMVC Typescript Utility - State Machine - Copyright © 2007-2025 Neil Manuell, Cliff Hall
78+
* PureMVC - Copyright © 2007-2025 Futurescale, Inc.
79+
* All rights reserved.
80+
81+
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
82+
83+
* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
84+
* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
85+
* Neither the name of Futurescale, Inc., PureMVC.org, nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
86+
87+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

VERSION

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
PureMVC Typescript Utility - State Machine
2+
--------------------------------------------------------------------------
3+
Release Date: 2/24/25
4+
Platform: Typescript
5+
Version: 1
6+
Revision: 0
7+
Authors: Neil Manuell <neil.manuell@puremvc.org>
8+
: Cliff Hall <cliff.hall@puremvc.org>
9+
--------------------------------------------------------------------------
10+
11+
1.0 Initial port from the AS3 source.
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
'use strict';
2+
const PUREMVC = '../../node_modules/@puremvc/puremvc-typescript-multicore-framework';
3+
4+
// THIS IS THE MOST ABSURD THING I'VE EVER HAD TO DO TO MAKE A TEST SUITE WORK.
5+
module.exports = jest.requireActual(PUREMVC);

bin/cjs/AsyncCommand.js

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
"use strict";
2+
Object.defineProperty(exports, "__esModule", { value: true });
3+
exports.AsyncCommand = void 0;
4+
const puremvc_typescript_multicore_framework_1 = require("@puremvc/puremvc-typescript-multicore-framework");
5+
/**
6+
* A base <code>IAsyncCommand</code> implementation.
7+
*
8+
* <P>
9+
* Your subclass should override the <code>execute</code>
10+
* method where your business logic will handle the <code>INotification</code>. </P>
11+
*
12+
* @see AsyncMacroCommand
13+
*/
14+
class AsyncCommand extends puremvc_typescript_multicore_framework_1.SimpleCommand {
15+
constructor() {
16+
super(...arguments);
17+
this.onComplete = null;
18+
}
19+
/**
20+
* Registers the callback for a parent <code>AsyncMacroCommand</code>.
21+
*
22+
* @param value The <code>AsyncMacroCommand</code> method to call on completion
23+
*/
24+
setOnComplete(value) {
25+
this.onComplete = value;
26+
}
27+
/**
28+
* Notify the parent <code>AsyncMacroCommand</code> that this command is complete.
29+
* <P>
30+
* Call this method from your subclass to signify that your asynchronous command
31+
* has finished.</P>
32+
*/
33+
commandComplete() {
34+
if (this.onComplete)
35+
this.onComplete();
36+
}
37+
}
38+
exports.AsyncCommand = AsyncCommand;

bin/cjs/AsyncMacroCommand.js

Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
"use strict";
2+
Object.defineProperty(exports, "__esModule", { value: true });
3+
exports.AsyncMacroCommand = void 0;
4+
const AsyncCommand_1 = require("./AsyncCommand");
5+
const puremvc_typescript_multicore_framework_1 = require("@puremvc/puremvc-typescript-multicore-framework");
6+
/**
7+
* A base <code>ICommand</code> implementation that executes other
8+
* <code>ICommand</code>s asynchronously.
9+
*
10+
* <P>
11+
* An <code>AsyncMacroCommand</code> maintains a list of
12+
* <code>ICommand</code> Class references called <i>SubCommands</i>.</P>
13+
*
14+
* <P>
15+
* When <code>execute</code> is called, the <code>AsyncMacroCommand</code>
16+
* caches a reference to the <code>INotification</code> and calls
17+
* <code>nextCommand</code>.</P>
18+
*
19+
* <P>
20+
* If there are still <i>SubCommands</i>'s to be executed,
21+
* the <code>nextCommand</code> method instantiates and calls <code>execute</code>
22+
* on each of its <i>SubCommands</i> in turn. Each <i>SubCommand</i> will be passed
23+
* a reference to the original <code>INotification</code> that was passed to the
24+
* <code>AsyncMacroCommand</code>'s <code>execute</code> method. If the
25+
* <i>SubCommand</i> to execute is an <code>IAsyncCommand</code>, the
26+
* next <i>SubCommand</i> will not be executed until the previous
27+
* <code>IAsyncCommand</code> has called its <i>commandComplete</i> method.</P>
28+
*
29+
* <P>
30+
* Unlike <code>AsyncCommand</code> and <code>SimpleCommand</code>, your subclass
31+
* should not override <code>execute</code>, but instead, should
32+
* override the <code>initializeAsyncMacroCommand</code> method,
33+
* calling <code>addSubCommand</code> once for each <i>SubCommand</i>
34+
* to be executed.</P>
35+
*
36+
* @see AsyncCommand
37+
*/
38+
class AsyncMacroCommand extends puremvc_typescript_multicore_framework_1.Notifier {
39+
/**
40+
* Constructor.
41+
*
42+
* <P>
43+
* You should not need to define a constructor,
44+
* instead, override the <code>initializeAsyncMacroCommand</code>
45+
* method.</P>
46+
*
47+
* <P>
48+
* If your subclass does define a constructor, be
49+
* sure to call <code>super()</code>.</P>
50+
*/
51+
constructor() {
52+
super();
53+
this.note = null;
54+
this.onComplete = null;
55+
this.subCommands = new Array();
56+
this.note = null;
57+
this.onComplete = null;
58+
this.initializeAsyncMacroCommand();
59+
}
60+
/**
61+
* Initialize the <code>AsyncMacroCommand</code>.
62+
*
63+
* <P>
64+
* In your subclass, override this method to
65+
* initialize the <code>AsyncMacroCommand</code>'s <i>SubCommand</i>
66+
* list with <code>ICommand</code> class references.
67+
* </P>
68+
*
69+
* <listing>
70+
* // Initialize MyMacroCommand
71+
* override protected function initializeAsyncMacroCommand() : void
72+
* {
73+
* addSubCommand( () => new FirstCommand() );
74+
* addSubCommand( () => SecondCommand() );
75+
* addSubCommand( () => ThirdCommand() );
76+
* }
77+
* </listing>
78+
*
79+
* <P>
80+
* Note that <i>SubCommand</i>s may be any <code>ICommand</code> implementor,
81+
* <code>AsyncMacroCommand</code>s, <code>AsyncCommand</code>s,
82+
* <code>MacroCommand</code>s or <code>SimpleCommands</code> are all acceptable.
83+
*/
84+
initializeAsyncMacroCommand() { }
85+
/**
86+
* Add a <i>SubCommand</i>.
87+
* <P>
88+
* The <i>SubCommands</i> will be called in First In/First Out (FIFO)
89+
* order.</P>
90+
*
91+
* @param factory a factory that returns an instance that implements <code>ICommand</code>.
92+
*/
93+
addSubCommand(factory) {
94+
this.subCommands.push(factory);
95+
}
96+
/**
97+
* Registers the callback for a parent <code>AsyncMacroCommand</code>.
98+
*
99+
* @param value The <code>AsyncMacroCommand</code> method to call on completion
100+
*/
101+
setOnComplete(value) {
102+
this.onComplete = value;
103+
}
104+
/**
105+
* Starts execution of this <code>AsyncMacroCommand</code>'s <i>SubCommands</i>.
106+
*
107+
* <P>
108+
* The <i>SubCommands</i> will be called in First In/First Out (FIFO) order.
109+
* </P>
110+
*
111+
* @param notification the <code>INotification</code> object to be passsed to each <i>SubCommand</i>.
112+
*/
113+
execute(notification) {
114+
this.note = notification;
115+
this.nextCommand();
116+
}
117+
/**
118+
* Execute this <code>AsyncMacroCommand</code>'s next <i>SubCommand</i>.
119+
*
120+
* <P>
121+
* If the next <i>SubCommand</i> is asynchronous, a callback is registered for
122+
* the command completion, else the next command is run.</P>
123+
*/
124+
nextCommand() {
125+
if (this.subCommands.length > 0) {
126+
const factory = this.subCommands.shift();
127+
const commandInstance = factory();
128+
const isAsync = commandInstance instanceof AsyncMacroCommand ||
129+
commandInstance instanceof AsyncCommand_1.AsyncCommand;
130+
if (isAsync) {
131+
commandInstance.setOnComplete(this.nextCommand);
132+
}
133+
commandInstance.initializeNotifier(this.multitonKey);
134+
if (this.note)
135+
commandInstance.execute(this.note);
136+
if (!isAsync)
137+
this.nextCommand();
138+
}
139+
else {
140+
if (this.onComplete !== null)
141+
this.onComplete();
142+
this.note = null;
143+
this.onComplete = null;
144+
}
145+
}
146+
}
147+
exports.AsyncMacroCommand = AsyncMacroCommand;

0 commit comments

Comments
 (0)