UNPKG

@qiwi/cyclone

Version:

"State machine" for basic purposes

1 lines 13.5 kB
{"version":3,"file":"cyclone.esm.mjs","sources":["../../src/main/ts/error.ts","../../src/main/ts/generator.ts","../../src/main/ts/log.ts","../../src/main/ts/machine.ts","../../src/main/ts/registry.ts","../../src/main/ts/factory.ts"],"sourcesContent":["export const TRANSITION_VIOLATION = 'Transition violation'\nexport const INVALID_UNLOCK_KEY = 'Invalid unlock key'\nexport const LOCK_VIOLATION = 'Lock violation'\nexport const UNREACHABLE_STATE = 'Unreachable state'\n\nexport class MachineError extends Error {}\n","export const generateDate = (): Date => new Date()\nexport const generateId = (): string => Math.random().toString()\n","export const LOG_PREFIX = '[cyclone]'\n\nexport const debug = console.debug.bind(console, LOG_PREFIX)\nexport const info = console.info.bind(console, LOG_PREFIX)\nexport const error = console.error.bind(console, LOG_PREFIX)\nexport const warn = console.warn.bind(console, LOG_PREFIX)\n\nexport const log = {\n debug,\n info,\n error,\n warn\n}\n","import {\n INVALID_UNLOCK_KEY,\n LOCK_VIOLATION,\n MachineError,\n TRANSITION_VIOLATION,\n UNREACHABLE_STATE\n} from './error'\nimport {\n generateDate,\n generateId\n} from './generator'\nimport { log } from './log'\n\ntype IState = string\n\ntype IAny = any\n\ntype IHandler = (data: IAny, ...payload: Array<IAny>) => IAny\n\ntype ITransitions = {\n [key: string]: IHandler | null | boolean\n}\n\ntype IMachineOpts = {\n transitions: ITransitions,\n initialState?: IState,\n initialData?: IAny,\n immutable?: boolean,\n historySize?: number\n}\n\nexport const DELIMITER = '>'\nexport const DEFAULT_HANDLER: IHandler = (...last) => last.pop()\nexport const DEFAULT_HISTORY_SIZE = 10\nexport const DEFAULT_OPTS: IMachineOpts = {\n transitions: {},\n historySize: DEFAULT_HISTORY_SIZE,\n immutable: false\n}\n\ntype IKey = string | null\n\ntype IDigest = {\n state?: IState,\n data?: IAny\n}\n\ntype IHistoryItem = {\n state: IState,\n data: IAny,\n id: string,\n date: Date\n}\ntype IHistory = IHistoryItem[]\n\ninterface IMachine {\n next(state: IState, ...payload: Array<IAny>): IMachine,\n prev(state?: IState): IMachine,\n current(): IDigest,\n lock(key?: IKey): IMachine,\n unlock(key: IKey): IMachine,\n\n transitions: ITransitions,\n history: IHistory,\n opts: IMachineOpts,\n key: IKey,\n id: string,\n}\n\ntype IPredicate = (item: IHistoryItem) => boolean\n\nexport class Machine implements IMachine {\n\n /**\n * Machine options.\n * @property\n */\n public opts: IMachineOpts\n\n /**\n * State history.\n * @property\n */\n public history: IHistory\n\n /**\n * Lock key.\n * @property\n */\n public key: IKey\n\n /**\n * Unique machine id\n * @property\n */\n public id: string\n\n /**\n * Transition handler map\n * @property\n */\n public transitions: ITransitions\n\n constructor(opts: IMachineOpts) {\n this.opts = { ...DEFAULT_OPTS, ...opts }\n this.history = []\n this.key = null\n this.id = generateId()\n this.transitions = opts.transitions\n\n if (typeof opts.initialState === 'string') {\n this.history.push({\n state: opts.initialState,\n data: opts.initialData,\n id: generateId(),\n date: generateDate()\n })\n }\n\n return this\n }\n\n /**\n * Provides next state transition.\n * @param state Next state name.\n * @param payload Any data for handler.\n */\n public next(state: IState, ...payload: Array<IAny>): IMachine {\n if (this.key) {\n throw new MachineError(LOCK_VIOLATION)\n }\n\n const handler = Machine.getHandler(state, this.history, this.transitions)\n const current = this.current()\n const data = handler(current.data, ...payload)\n const id = generateId()\n const date = generateDate()\n\n this.history.push({\n state,\n data,\n id,\n date\n })\n\n if (this.history.length > Machine.getHistoryLimit(this.opts.historySize)) {\n log.debug('history limit reached')\n this.history.shift()\n }\n\n return this\n }\n\n /**\n * Returns the machine's digest: state name and stored data.\n */\n public current(): IHistoryItem {\n return { ...this.history[this.history.length - 1] }\n }\n\n /**\n * Returns the last state, that satisfies the condition\n */\n public last(condition?: string | IPredicate): IHistoryItem | void {\n if (condition === undefined) {\n return this.current()\n }\n\n const filter = typeof condition === 'string'\n ? ({ state }: IHistoryItem) => state === condition\n : condition\n\n return [...this.history].reverse().find(filter)\n }\n\n /**\n * Reverts current state to the previous.\n * @param state\n */\n public prev(state?: string | IPredicate): IMachine {\n if (this.key) {\n throw new MachineError(LOCK_VIOLATION)\n }\n\n if (this.history.length < 2) {\n throw new MachineError(UNREACHABLE_STATE)\n }\n\n if (state === undefined) {\n this.history.pop()\n return this\n }\n\n const last = this.last(state)\n if (!last) {\n throw new MachineError(UNREACHABLE_STATE)\n }\n\n this.history.length = this.history.indexOf(last) + 1\n\n return this\n }\n\n /**\n * Locks the machine. Any transitions are prohibited before unlocking.\n * @param key\n */\n public lock(key?: IKey): IMachine {\n this.key = key || `lock${generateId()}`\n\n return this\n }\n\n /**\n * Unlocks the machine.\n * @param key\n */\n public unlock(key: IKey): IMachine {\n if (this.key !== key) {\n throw new MachineError(INVALID_UNLOCK_KEY)\n }\n\n this.key = null\n\n return this\n }\n\n public static getHistoryLimit(historySize?: number): number {\n if (historySize === undefined) {\n return DEFAULT_HISTORY_SIZE\n }\n\n if (historySize === -1) {\n return Number.POSITIVE_INFINITY\n }\n\n return historySize\n }\n\n public static getHandler(next: IState, history: IHistory, transitions: ITransitions): IHandler {\n const targetTransition = this.getTargetTransition(next, history)\n const nextTransition = this.getTransition(targetTransition, transitions)\n\n if (!nextTransition) {\n throw new MachineError(TRANSITION_VIOLATION)\n }\n\n const handler = transitions[nextTransition]\n\n return typeof handler === 'function'\n ? handler\n : DEFAULT_HANDLER\n }\n\n public static getTransition(targetTransition: string, transitions: ITransitions): string | void {\n // TODO Support wildcards\n // TODO Support OR operator\n // TODO Generate patterns in constructor\n return Object.keys(transitions)\n .filter(transition => targetTransition.length > transition.length\n ? new RegExp(`.*${transition}$`).test(targetTransition)\n : targetTransition === transition\n )\n .sort((a, b) => b.length - a.length)[0]\n }\n\n public static getTargetTransition(next: IState, history: IHistory): string {\n return [...history.map(({ state }: IHistoryItem) => state), next]\n .join(DELIMITER)\n }\n\n /**\n * Returns the last passes argument as a result\n * @param {any} state\n * @param {any} [payload]\n * @return {any}\n */\n public DEFAULT_HANDLER = DEFAULT_HANDLER\n\n}\n\nexport {\n IMachine, IHistory, ITransitions, IHistoryItem, IHandler, IMachineOpts\n}\n","export class Registry {\n\n store: { [key: string]: any }\n\n constructor () {\n this.store = {}\n }\n\n get (key: string): void {\n return this.store[key]\n }\n\n add (key: string, value: any): void {\n this.store[key] = value\n }\n\n remove (key: string): void {\n delete this.store[key]\n }\n\n}\n","import {\n DEFAULT_OPTS as DEFAULT_MACHINE_OPTS,\n IMachine,\n IMachineOpts,\n Machine} from './machine'\nimport {\n Registry\n} from './registry'\n\nexport const DEFAULT_TEMPLATE = {}\nexport const DEFAULT_TEMPLATE_REGISTRY = new Registry()\nexport const DEFAULT_MACHINE_REGISTRY = new Registry()\n\ntype IFactoryOpts = {\n machine?: IMachineOpts\n template?: string | Object\n templateRegistry?: Registry\n machineRegistry?: Registry\n}\n\nexport const factory = (opts: IFactoryOpts): IMachine => {\n // NOTE flowgen throws error on typed args destruction\n const {\n machine,\n template,\n templateRegistry = DEFAULT_TEMPLATE_REGISTRY,\n machineRegistry = DEFAULT_MACHINE_REGISTRY\n } = opts\n const _template = getTemplate(template, templateRegistry)\n const machineOpts: IMachineOpts = { ...DEFAULT_MACHINE_OPTS, ..._template, ...machine }\n const instance = new Machine(machineOpts)\n\n machineRegistry.add(instance.id, instance)\n\n return instance\n}\n\nexport const getTemplate = (template: string | Object | void, templateRegistry: Registry): any => {\n return typeof template === 'string'\n ? templateRegistry.get(template)\n : template\n}\n\nexport {\n IFactoryOpts\n}\n"],"names":["TRANSITION_VIOLATION","INVALID_UNLOCK_KEY","UNREACHABLE_STATE","MachineError","_Error","apply","this","arguments","_wrapNativeSuper","Error","Date","Math","random","toString","log","debug","console","bind","info","error","warn","DELIMITER","DEFAULT_HANDLER","pop","DEFAULT_HISTORY_SIZE","DEFAULT_OPTS","transitions","historySize","immutable","Machine","opts","history","key","id","_extends","generateId","initialState","push","state","data","initialData","date","generateDate","next","getHandler","current","handler","length","getHistoryLimit","shift","last","condition","undefined","filter","_ref","concat","reverse","find","prev","indexOf","_proto","lock","unlock","Number","POSITIVE_INFINITY","targetTransition","getTargetTransition","nextTransition","getTransition","Object","keys","transition","test","sort","a","b","map","_ref2","join","Registry","store","prototype","get","add","value","remove","DEFAULT_TEMPLATE","DEFAULT_TEMPLATE_REGISTRY","DEFAULT_MACHINE_REGISTRY","factory","machine","templateRegistry","machineRegistry","_opts$machineRegistry","_template","getTemplate","template","_opts$templateRegistr","machineOpts","DEFAULT_MACHINE_OPTS","instance"],"mappings":"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"}