JavaScript Finite State Machine Router
jFSMRouter (JavaScript Finite State Machine Router) is a JavaScript class that implements a Finite State Machine (FSM) integrated with a hash-based router for browsers. It allows centralized management of states, transitions, and routes.
The class uses the Singleton pattern: only one global instance exists, which you can access with:
import jFSMRouter from 'https://cdn.jsdelivr.net/gh/StefanoBalocco/jFSMRouter@1.1.5/jFSMRouter.min.js';
const router = jFSMRouter();States represent the logical steps of your application.
router.StateAdd('home'); // Adds the "home" state- Returns
trueif the state is successfully created. - The first state added becomes the initial current state.
router.StateDel('home'); // Removes the "home" state- OnEnter: functions called when entering a state.
- OnLeave: functions called when leaving a state.
function onEnter(prev, next) { console.log(`Entering ${next}`); }
function onLeave(curr, next) { console.log(`Leaving ${curr}`); }
router.StateOnEnterAdd('home', onEnter);
router.StateOnLeaveAdd('home', onLeave);To remove hooks:
router.StateOnEnterDel('home', onEnter);
router.StateOnLeaveDel('home', onLeave);Transitions define permissions and hooks between two states.
router.TransitionAdd('home', 'about');router.TransitionDel('home', 'about');- OnBefore: called before transitioning, can block it by returning
false. - OnAfter: called after the state change.
function before() { return confirm('Go to the About page?'); }
async function after() { console.log('Transition completed'); }
router.TransitionOnBeforeAdd('home', 'about', before);
router.TransitionOnAfterAdd('home', 'about', after);To remove hooks:
router.TransitionOnBeforeDel('home', 'about', before);
router.TransitionOnAfterDel('home', 'about', after);Each route is associated with a valid state.
// path: '/user/:id[09]'
router.RouteAdd(
'home', // required state
'/user/:id[09]', // path with variables
(pathDef, actual, vars) => { console.log(vars.id); },
() => true, // optional availability function
(pathDef, actual, vars) => { console.warn('Access denied'); } // 403
);- path may include variables like
:name[AZ09],:num[09],:str[AZ]. - routeFunction: callback called if all checks pass.
- available: sync or async function to allow/deny the route.
- routeFunction403: callback called in case of access denial (403).
router.RouteDel('/user/:id[09]');router.RouteSpecialAdd(404, () => { /* page not found */ });
router.RouteSpecialAdd(403, () => { /* access denied */ });
router.RouteSpecialAdd(500, () => { /* internal error */ });To force navigation:
router.Trigger('user/123'); // sets the hash and triggers routing- The
hashchangelistener callsCheckHash(). - More specific paths (higher weight) take priority.
- FSM handles the proper hook sequence: OnBefore → OnLeave → OnAfter → OnEnter.
<!DOCTYPE html>
<html>
<head><title>jFSMRouter Demo</title></head>
<body>
<script type="module">
import jFMSRouter from 'https://example.org/jFSMRouter.js';
const router = jFSMRouter();
// Define states
router.StateAdd('home');
router.StateAdd('user');
// State hooks
router.StateOnEnterAdd('home', () => console.log('Entered Home'));
router.StateOnEnterAdd('user', (_, prev) => console.log(`User ${prev}→user`));
// Transitions
router.TransitionAdd('home', 'user');
// Routing
router.RouteSpecialAdd(404, () => document.body.innerHTML = '<h1>404 Not Found</h1>');
router.RouteAdd(
'home', '/home', () => alert('Welcome!')
);
router.RouteAdd(
'user', '/user/:id[09]', (pd, act, { id }) =>
document.body.innerHTML = `<h1>User ${id}</h1>`
);
// Initial startup (if hash already present)
router.CheckHash();
</script>
</body>
</html>Notes:
- Uses ES Module syntax for import.
- Hook handling supports both sync and async functions.
- Avoid duplicate variable IDs in a single path (throws
Duplicate path idexception).