@vedmalex/statemachine 1.0.0-beta.2 → 1.0.0-beta.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vedmalex/statemachine",
3
- "version": "1.0.0-beta.2",
3
+ "version": "1.0.0-beta.3",
4
4
  "description": "Hierarchical state machine for TypeScript with monitoring, validation, and persistence (lite-only DI-free surface).",
5
5
  "keywords": [
6
6
  "state-machine",
@@ -47,7 +47,7 @@
47
47
  "build:types": "tsc -p tsconfig.build.json --emitDeclarationOnly",
48
48
  "lint": "biome check .",
49
49
  "typecheck": "tsc --noEmit",
50
- "check": "biome check . && tsc --noEmit",
50
+ "check": "biome check . && tsc --noEmit && knip --no-progress",
51
51
  "test": "vitest run",
52
52
  "test:watch": "vitest",
53
53
  "test:coverage": "vitest run --coverage",
package/types/index.d.ts CHANGED
@@ -60,6 +60,29 @@ export type { IMonitor } from './types';
60
60
  * @unstable — timer scheduling injection contract; implement for WASM/host portability.
61
61
  */
62
62
  export type { ITimerScheduler } from './types';
63
+ /**
64
+ * @category Unstable
65
+ * @unstable — virtual-clock type alias; matches the `clock` option in StateMachineOptions.
66
+ */
67
+ export type { Clock } from './scheduler';
68
+ /**
69
+ * @category Unstable
70
+ * @unstable — factory for a deterministic, non-real-time scheduler. Pass the
71
+ * returned scheduler together with the same `clock` function in StateMachine
72
+ * options to enable virtual-time (DST) testing without real setTimeout.
73
+ *
74
+ * @example
75
+ * ```ts
76
+ * let t = 0
77
+ * const clock = () => t
78
+ * const scheduler = createVirtualScheduler(clock)
79
+ * const sm = new StateMachine(config, adapter, { clock, scheduler })
80
+ * await Promise.resolve() // flush microtasks: enter initial state + arm invoke timers
81
+ * t = 1000
82
+ * scheduler.process() // advance virtual time to 1000 ms
83
+ * ```
84
+ */
85
+ export { createVirtualScheduler } from './scheduler';
63
86
  /**
64
87
  * @category Unstable
65
88
  * @unstable — error-handler injection contract; implement to replace built-in recovery.
@@ -1,4 +1,9 @@
1
1
  import type { ITimerScheduler } from './types';
2
+ /**
3
+ * Clock function type: returns the current virtual or real time in ms.
4
+ * Matches the signature of `Date.now`.
5
+ */
6
+ export type Clock = () => number;
2
7
  /**
3
8
  * Тип для идентификатора таймера
4
9
  */
@@ -12,7 +17,8 @@ export declare class TimerScheduler {
12
17
  private activeTokens;
13
18
  private intervalId;
14
19
  private pollingInterval;
15
- constructor();
20
+ private readonly clock;
21
+ constructor(clock?: Clock);
16
22
  /**
17
23
  * Настройка режима опроса
18
24
  * @param interval Интервал проверки в мс. Если 0 или null - авто-тик выключается (ручной режим)
@@ -62,4 +68,28 @@ export declare class TimerScheduler {
62
68
  * Same-module placement required: TimerScheduler constructor is public within module.
63
69
  */
64
70
  export declare function createDefaultScheduler(): ITimerScheduler;
71
+ /**
72
+ * Returns a deterministic virtual-time scheduler driven entirely by the
73
+ * supplied `clock` function. Intended for tests, simulation, and DST.
74
+ *
75
+ * Guarantees:
76
+ * - `isActive()` always returns `true` — StateMachine routes all timers
77
+ * through it and never touches real `setTimeout`/`setInterval`.
78
+ * - No real timer is ever created.
79
+ * - `schedule(delay, cb)` queues a task at `clock() + delay`.
80
+ * - `process(now?)` drains every task whose `executeAt <= now`
81
+ * (default `now` is `clock()`).
82
+ *
83
+ * @example
84
+ * ```ts
85
+ * let t = 0
86
+ * const clock = () => t
87
+ * const scheduler = createVirtualScheduler(clock)
88
+ * const sm = new StateMachine(config, adaptee, { clock, scheduler })
89
+ * await Promise.resolve() // flush microtasks: enter initial state + arm invoke timers
90
+ * t = 1000
91
+ * scheduler.process() // fires all tasks due by t=1000
92
+ * ```
93
+ */
94
+ export declare function createVirtualScheduler(clock: Clock): ITimerScheduler;
65
95
  export {};
@@ -18,6 +18,8 @@ export declare class StateMachine<TOwner extends object, SMConfig extends StateM
18
18
  private monitor;
19
19
  private scheduler;
20
20
  private errorHandler;
21
+ private clock;
22
+ private schedulerProvided;
21
23
  private states;
22
24
  private events;
23
25
  private stateAttribute;
package/types/types.d.ts CHANGED
@@ -34,6 +34,12 @@ export interface ITimerScheduler {
34
34
  isActive(): boolean;
35
35
  schedule(delay: number, callback: () => void): object;
36
36
  cancel(token: object): void;
37
+ /**
38
+ * Optional manual drain — advance virtual time and fire all timers whose
39
+ * `executeAt <= now` (default `now` is the scheduler's own clock). Real-time
40
+ * schedulers driven by setInterval may leave this unimplemented.
41
+ */
42
+ process?(now?: number): void;
37
43
  }
38
44
  /** @unstable — transition observability context (additive on IMonitor). */
39
45
  export interface TransitionContext {
@@ -74,6 +80,13 @@ export interface StateMachineOptions {
74
80
  monitor?: IMonitor;
75
81
  scheduler?: ITimerScheduler;
76
82
  errorHandler?: IErrorHandler;
83
+ /**
84
+ * Clock function returning the current time in milliseconds.
85
+ * Default: `Date.now`. Inject a virtual clock together with a
86
+ * `scheduler` (see `createVirtualScheduler`) for deterministic replay / DST.
87
+ * Used for `stateEntryTimes`, `resumeTimers`, and `getQueuedEvents` age math.
88
+ */
89
+ clock?: () => number;
77
90
  /**
78
91
  * Maximum time (ms) to wait for async entry/exit actions.
79
92
  * If exceeded, the transition aborts with an error.