Comparing version 1.1.2 to 2.0.0
import EventEmitter from 'eventemitter3'; | ||
export interface IAFSM extends AFSM { | ||
export interface IAFSM extends FSM { | ||
} | ||
export declare function From(...from: string[]): MethodDecorator; | ||
export declare function To(state: string): MethodDecorator; | ||
export declare type State = string | MiddleState; | ||
export declare class MiddleState { | ||
oldState: State; | ||
newState: string; | ||
action: string; | ||
constructor(oldState: State, newState: string, action: string); | ||
toString(): string; | ||
} | ||
export declare function ChangeState(from: string | string[], to: string): (target: any, propertyKey: string | symbol, descriptor: TypedPropertyDescriptor<any>) => void; | ||
export declare class FSM extends EventEmitter { | ||
_state: string; | ||
abortCtrl?: { | ||
aborted: boolean; | ||
}; | ||
name?: string | undefined; | ||
static STATECHANGED: string; | ||
static EABORT: Error; | ||
static ESTATE: Error; | ||
get state(): string; | ||
set state(value: string); | ||
} | ||
export declare class AFSM extends FSM { | ||
static OFF: string; | ||
static ON: string; | ||
static INIT: string; | ||
start(): Promise<void>; | ||
stop(): Promise<void>; | ||
forceStop(): Promise<void>; | ||
static get stateDiagram(): never[]; | ||
_state: State; | ||
abortCtrl?: { | ||
aborted: boolean; | ||
}; | ||
constructor(name?: string | undefined); | ||
get state(): State; | ||
set state(value: State); | ||
} |
133
index.js
@@ -1,29 +0,78 @@ | ||
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { | ||
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; | ||
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); | ||
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; | ||
return c > 3 && r && Object.defineProperty(target, key, r), r; | ||
}; | ||
import EventEmitter from 'eventemitter3'; | ||
const fromsKey = Symbol('froms'); | ||
export function From(...from) { | ||
return (target, propertyKey, descriptor) => { | ||
target[fromsKey] = target[fromsKey] || {}; | ||
target[fromsKey][propertyKey] = target[fromsKey][propertyKey] || []; | ||
target[fromsKey][propertyKey].push(...from); | ||
}; | ||
// 中间过渡状态 | ||
export class MiddleState { | ||
oldState; | ||
newState; | ||
action; | ||
constructor(oldState, newState, action) { | ||
this.oldState = oldState; | ||
this.newState = newState; | ||
this.action = action; | ||
} | ||
toString() { | ||
return `${this.action}ing`; | ||
} | ||
} | ||
export function To(state) { | ||
const stateDiagram = new Map(); | ||
//@ts-ignore | ||
if (typeof window !== 'undefined' && '__AFSM__' in window) | ||
window['__AFSM__'](stateDiagram); | ||
export function ChangeState(from, to) { | ||
return (target, propertyKey, descriptor) => { | ||
if (!stateDiagram.has(target)) { | ||
stateDiagram.set(target, []); | ||
Object.defineProperty(target, 'stateDiagram', { | ||
get() { | ||
let result = []; | ||
let plain = []; | ||
let forceTo = []; | ||
const stateConfig = stateDiagram.get(target); | ||
const allState = new Set(); | ||
stateConfig.forEach(({ from, to, action }) => { | ||
allState.add(to); | ||
allState.add(action + "ing"); | ||
if (typeof from === 'string') { | ||
allState.add(from); | ||
plain.push({ from, to, action }); | ||
} | ||
else { | ||
if (from.length) | ||
from.forEach(f => { | ||
allState.add(f); | ||
plain.push({ from: f, to, action }); | ||
}); | ||
else | ||
forceTo.push({ to, action }); | ||
} | ||
}); | ||
plain.forEach(({ from, to, action }) => { | ||
result.push(`${from} --> ${action}ing : ${action}`, `${action}ing --> ${to} : ${action} success`, `${action}ing --> ${from} : ${action} failed`); | ||
}); | ||
forceTo.forEach(({ to, action }) => { | ||
allState.forEach(f => { | ||
result.push(`${f} --> ${to} : ${action}`); | ||
}); | ||
}); | ||
return result; | ||
} | ||
}); | ||
} | ||
stateDiagram.get(target).push({ from, to, action: propertyKey.toString() }); | ||
const origin = descriptor.value; | ||
descriptor.value = async function (...arg) { | ||
const froms = target?.[fromsKey]?.[propertyKey]; | ||
if (froms) { | ||
if ((!froms.includes(this.state))) | ||
if (this.state === to || typeof this.state != "string") | ||
throw FSM.ESTATE; | ||
if (Array.isArray(from)) { | ||
if (from.length == 0) { | ||
if (this.abortCtrl) | ||
this.abortCtrl.aborted = true; | ||
} | ||
else if ((!from.includes(this.state))) | ||
throw FSM.ESTATE; | ||
} | ||
else if (this.abortCtrl) { | ||
this.abortCtrl.aborted = true; | ||
else { | ||
if (from !== this.state) | ||
throw FSM.ESTATE; | ||
} | ||
this.state = `${this.state}(${propertyKey})${state}`; | ||
this.state = new MiddleState(this.state, to, propertyKey); | ||
const abort = { aborted: false }; | ||
@@ -38,3 +87,3 @@ this.abortCtrl = abort; | ||
} | ||
this.state = state; | ||
this.state = to; | ||
return result; | ||
@@ -45,7 +94,17 @@ }; | ||
export class FSM extends EventEmitter { | ||
_state = ""; | ||
abortCtrl; | ||
name; | ||
static STATECHANGED = 'stateChanged'; | ||
static EABORT = new Error('abort'); | ||
static ESTATE = new Error('wrong state'); | ||
static INIT = "[*]"; | ||
static get stateDiagram() { | ||
return []; | ||
} | ||
_state = FSM.INIT; | ||
abortCtrl; | ||
constructor(name) { | ||
super(); | ||
this.name = name; | ||
this.name = this.name || this.constructor.name; | ||
} | ||
get state() { | ||
@@ -56,29 +115,7 @@ return this._state; | ||
const old = this._state; | ||
// console.debug(`${this.constructor.name} state change from ${this._state} to ${value}`); | ||
this._state = value; | ||
this.emit(value, old); | ||
this.emit(FSM.STATECHANGED, old, value); | ||
if (value) | ||
this.emit(value.toString(), old); | ||
this.emit(FSM.STATECHANGED, value, old); | ||
} | ||
} | ||
export class AFSM extends FSM { | ||
static OFF = 'off'; | ||
static ON = 'on'; | ||
static INIT = ''; | ||
async start() { | ||
} | ||
async stop() { | ||
} | ||
async forceStop() { | ||
} | ||
} | ||
__decorate([ | ||
From(AFSM.OFF, AFSM.INIT), | ||
To(AFSM.ON) | ||
], AFSM.prototype, "start", null); | ||
__decorate([ | ||
From(AFSM.ON), | ||
To(AFSM.OFF) | ||
], AFSM.prototype, "stop", null); | ||
__decorate([ | ||
To(AFSM.OFF) | ||
], AFSM.prototype, "forceStop", null); |
118
index.ts
import EventEmitter from 'eventemitter3'; | ||
export interface IAFSM extends AFSM { | ||
export interface IAFSM extends FSM { | ||
} | ||
const fromsKey = Symbol('froms'); | ||
export function From(...from: string[]): MethodDecorator { | ||
return (target: any, propertyKey: string | symbol, descriptor: TypedPropertyDescriptor<any>) => { | ||
target[fromsKey] = target[fromsKey] || {}; | ||
target[fromsKey][propertyKey] = target[fromsKey][propertyKey] || []; | ||
target[fromsKey][propertyKey].push(...from); | ||
}; | ||
export type State = string | MiddleState; | ||
// 中间过渡状态 | ||
export class MiddleState { | ||
constructor(public oldState: State, public newState: string, public action: string) { | ||
} | ||
toString() { | ||
return `${this.action}ing`; | ||
} | ||
} | ||
export function To(state: string): MethodDecorator { | ||
const stateDiagram = new Map<Object, { from: string | string[], to: string, action: string; }[]>(); | ||
export function ChangeState(from: string | string[], to: string) { | ||
return (target: any, propertyKey: string | symbol, descriptor: TypedPropertyDescriptor<any>) => { | ||
if (!stateDiagram.has(target)) { | ||
stateDiagram.set(target, []); | ||
Object.defineProperty(target, 'stateDiagram', { | ||
get() { | ||
let result: string[] = []; | ||
let plain: { from: string; to: string; action: string; }[] = []; | ||
let forceTo: { to: string; action: string; }[] = []; | ||
const stateConfig = stateDiagram.get(target)!; | ||
const allState = new Set(); | ||
stateConfig.forEach(({ from, to, action }) => { | ||
allState.add(to); | ||
if (typeof from === 'string') { | ||
allState.add(from); | ||
plain.push({ from, to, action }); | ||
allState.add(action + "ing"); | ||
} else { | ||
if (from.length) { | ||
from.forEach(f => { | ||
allState.add(f); | ||
plain.push({ from: f, to, action }); | ||
}); | ||
allState.add(action + "ing"); | ||
} | ||
else forceTo.push({ to, action }); | ||
} | ||
}); | ||
plain.forEach(({ from, to, action }) => { | ||
result.push(`${from} --> ${action}ing : ${action}`, `${action}ing --> ${to} : ${action} success`, `${action}ing --> ${from} : ${action} failed`); | ||
}); | ||
forceTo.forEach(({ to, action }) => { | ||
allState.forEach(f => { | ||
if (f !== to) | ||
result.push(`${f} --> ${action}ing : ${action}`, `${action}ing --> ${to} : ${action} success`); | ||
}); | ||
}); | ||
return result; | ||
} | ||
}); | ||
} | ||
stateDiagram.get(target)!.push({ from, to, action: propertyKey.toString() }); | ||
const origin = descriptor.value; | ||
descriptor.value = async function (this: IAFSM, ...arg: any[]) { | ||
const froms = target?.[fromsKey]?.[propertyKey]; | ||
if (froms) { | ||
if ((!froms.includes(this.state))) | ||
throw FSM.ESTATE; | ||
} else if (this.abortCtrl) { | ||
this.abortCtrl.aborted = true; | ||
if (this.state === to || typeof this.state != "string") throw FSM.ESTATE; | ||
if (Array.isArray(from)) { | ||
if (from.length == 0) { | ||
if (this.abortCtrl) this.abortCtrl.aborted = true; | ||
} else if ((!from.includes(this.state))) throw FSM.ESTATE; | ||
} else { | ||
if (from !== this.state) throw FSM.ESTATE; | ||
} | ||
this.state = `${this.state}(${propertyKey as string})${state}`; | ||
this.state = new MiddleState(this.state, to, propertyKey as string); | ||
const abort = { aborted: false }; | ||
@@ -33,3 +76,3 @@ this.abortCtrl = abort; | ||
} | ||
this.state = state; | ||
this.state = to; | ||
return result; | ||
@@ -39,37 +82,28 @@ }; | ||
} | ||
const hasDevTools = typeof window !== 'undefined' && '__AFSM__' in window; | ||
export class FSM extends EventEmitter { | ||
_state: string = ""; | ||
abortCtrl?: { aborted: boolean; }; | ||
static STATECHANGED = 'stateChanged'; | ||
static EABORT = new Error('abort'); | ||
static ESTATE = new Error('wrong state'); | ||
static INIT = "[*]"; | ||
get stateDiagram() { | ||
return []; | ||
} | ||
_state: State = FSM.INIT; | ||
abortCtrl?: { aborted: boolean; }; | ||
constructor(public name?: string) { | ||
super(); | ||
this.name = this.name || this.constructor.name; | ||
if (hasDevTools) window.dispatchEvent(new CustomEvent("createAFSM", { detail: { name: this.name, diagram: this.stateDiagram } })); | ||
} | ||
get state() { | ||
return this._state; | ||
} | ||
set state(value: string) { | ||
set state(value: State) { | ||
const old = this._state; | ||
// console.debug(`${this.constructor.name} state change from ${this._state} to ${value}`); | ||
this._state = value; | ||
this.emit(value, old); | ||
this.emit(FSM.STATECHANGED, old, value); | ||
if (value) this.emit(value.toString(), old); | ||
this.emit(FSM.STATECHANGED, value, old); | ||
if (hasDevTools) window.dispatchEvent(new CustomEvent("changeAFSM", { detail: { name: this.name, state: value } })); | ||
} | ||
} | ||
export class AFSM extends FSM { | ||
static OFF = 'off'; | ||
static ON = 'on'; | ||
static INIT = ''; | ||
@From(AFSM.OFF, AFSM.INIT) | ||
@To(AFSM.ON) | ||
async start() { | ||
} | ||
@From(AFSM.ON) | ||
@To(AFSM.OFF) | ||
async stop() { | ||
} | ||
@To(AFSM.OFF) | ||
async forceStop() { | ||
} | ||
} |
{ | ||
"name": "afsm", | ||
"version": "1.1.2", | ||
"version": "2.0.0", | ||
"description": "atomic fsm", | ||
@@ -9,3 +9,3 @@ "main": "index.js", | ||
"build": "tsc", | ||
"test": " node --loader ts-node/esm test.ts" | ||
"test": " node --loader ts-node/esm test/test.ts" | ||
}, | ||
@@ -12,0 +12,0 @@ "types": "index.d.ts", |
@@ -56,2 +56,19 @@ # 智能状态机 | ||
原子状态机有两种基本状态,即ON和OFF。可以用原子状态机组合成复杂的状态。 | ||
在start和stop中间是异步变更中的状态,所以一共有4种状态。 | ||
在start和stop中间是异步变更中的状态,所以一共有4种状态。 | ||
```mermaid | ||
stateDiagram-v2 | ||
[*] --> connecting : connect | ||
connecting --> connected : connect success | ||
connecting --> [*] : connect failed | ||
disconnected --> reconnecting : reconnect | ||
reconnecting --> reconnected : reconnect success | ||
reconnecting --> disconnected : reconnect failed | ||
connected --> disconnected : disconnect | ||
connecting --> disconnected : disconnect | ||
[*] --> disconnected : disconnect | ||
reconnected --> disconnected : disconnect | ||
reconnecting --> disconnected : disconnect | ||
disconnected --> disconnected : disconnect | ||
disconnecting --> disconnected : disconnect | ||
``` |
{ | ||
"include": ["index.ts"], | ||
"compilerOptions": { | ||
"experimentalDecorators": true, | ||
/* Visit https://aka.ms/tsconfig.json to read more about this file */ | ||
@@ -19,3 +18,3 @@ | ||
// "jsx": "preserve", /* Specify what JSX code is generated. */ | ||
// "experimentalDecorators": true, /* Enable experimental support for TC39 stage 2 draft decorators. */ | ||
"experimentalDecorators": true, /* Enable experimental support for TC39 stage 2 draft decorators. */ | ||
// "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */ | ||
@@ -22,0 +21,0 @@ // "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h' */ |
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
1190934
31
4711
73
1