small-state 0.2.0 → 0.3.0

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/README.ja.md CHANGED
@@ -9,6 +9,13 @@ A state machine inspired by [XState](https://stately.ai/docs/xstate).
9
9
  - 迷子になりにくい記述
10
10
 
11
11
 
12
+ ## Installation
13
+
14
+ ```
15
+ npm i small-state
16
+ ```
17
+
18
+
12
19
  ## FSM(Finite State Machine / 有限ステートマシン)とは
13
20
 
14
21
  有限個の状態と、状態間の遷移を定義するモデル。
@@ -237,8 +244,26 @@ idle 内で LOGIN_SUCCESS を受け取ると、定義に従い "loggedIn" ステ
237
244
  - 'notStarted' - まだ .start() されていない。
238
245
  - 'active' - .start() され、稼働している。
239
246
  - 'stopped' - .stop() され、停止している。
240
- - 'done' - 最終状態に入り完了し、停止した。
247
+ - 'done' - 最終状態に入り完了し、正常停止した。
241
248
  - 'error' - エラーで停止した。
249
+ - `type Runtime<M extends AnyMachine>`
250
+ - `type Snapshot<M extends AnyMachine>` - Runtime の getSnapshot の項参照。
251
+
252
+ ### 補助関数
253
+
254
+ - `isMachine(v: unknown): v is AnyMachine` - 引数がマシンかどうかを判定。
255
+ - `isRuntime(v: unknown): v is Runtime<AnyMachine>` - 引数がランタイムかどうかを判定。
256
+ - `defServiceFn(serviceFn: ServiceFn): ServiceFn`
257
+ - サービス関数を定義する。
258
+ - マシン外部でサービス関数を定義するときか、invoke に直接関数を渡すときに裸の関数だと型推論が崩れてしまうが、この関数でラップすることで型を通すことができる。
259
+ - `waitFor<R extends Runtime<any>>(runtime, cond, options?): Promise<SnapshotFrom<R>>` - 渡した runtime が条件を満たすまで待機する。
260
+
261
+
262
+ ### メイン関数
263
+
264
+ - `makeMachine(config, states): Machine` - マシンを作る。
265
+ - `makeRuntime(machine, option?): Runtime` - マシンを実際に動かすランタイムを作る。
266
+
242
267
 
243
268
  ### 予約されているイベントタイプ
244
269
 
@@ -253,16 +278,6 @@ idle 内で LOGIN_SUCCESS を受け取ると、定義に従い "loggedIn" ステ
253
278
  - `{type: ':done:', output: unknown}` - そのサービスが正常終了したときに発火する
254
279
  - `{type: ':error:', error: unknown}` - そのサービスが異常狩猟したときに発火する
255
280
 
256
- ### 補助関数
257
-
258
- - `defServiceFn(serviceFn): ServiceFn` - サービス関数を定義する。
259
- - マシン外部でサービス関数を定義するときか、invoke に直接関数を渡すときに裸の関数だと型推論が崩れてしまうが、この関数でラップすることで型を通すことができる。
260
- - `waitFor<RT extends Runtime<any>>(runtime, cond, options?): Promise<SnapshotFrom<RT>>` - 渡した runtime が条件を満たすまで待機する。
261
-
262
- ### メイン関数
263
-
264
- - `makeMachine(config, states): Machine` - マシンを作る。
265
- - `makeRuntime(machine, option?): Runtime` - マシンを実際に動かすランタイムを作る。
266
281
 
267
282
  ### `makeMachine(config: MachineConfig, defineStates: (tools)=>Record<string, StateDefinition>): Machine`
268
283
 
@@ -473,7 +488,8 @@ Runtime は以下のメンバーを持つ:
473
488
  - `output: O | undefined` - ランタイムが最終状態に入るまでは undefined。最終状態に入ったとき計算され結果がここに設定される。
474
489
  - `error: unknown` - エラーが生じてランタイムが停止したときにそのエラー内容が入る。それまでは undefined。
475
490
  - `_children` - デバッグ用。業務では使用しないのが望ましい。キーにサービス名、値に invoke などで起動中のサービスが入る。subKey を設定して起動している場合、キーは `${serviceName}:${subKey}` になる。
476
- - 怠惰のために runtime から直接プロパティでsnapshot を参照できる getter が用意されている。以下のものが設定されている。
491
+ - `_runtime` - このスナップショットを作ったランタイム自身。
492
+ - 怠惰のために runtime から直接プロパティでsnapshot を参照できる getter が用意されている。以下のものが設定されている:
477
493
  - states, stateValue, context, runtimeStatus, output
478
494
  - `subscribe(onSnapshot: ((current: Snapshot, prev: Snapshot)=>void)|undefined, onError?:(err:unknown)=>void): UnsubscribeFn`
479
495
  - snapshot が更新されるたびに呼ばれる観測関数を登録する。ただし第一引数の関数はエラーは監視しない。それは省略可能な第二引数の関数の役割である。
@@ -508,6 +524,7 @@ Runtime は以下のメンバーを持つ:
508
524
  | | XState | small-state |
509
525
  | - | - | - |
510
526
  | パス区切り文字 | `.` | `/` |
527
+ | ロジックを持つのは | マシン | ランタイム |
511
528
  | context 更新 | assign | update: action内引数 |
512
529
  | 子から親への通信 | sendParent | emit |
513
530
  | 親から子への通信に使うもの | actor id | service name (と subKey) |
@@ -545,5 +562,3 @@ Runtime は以下のメンバーを持つ:
545
562
  - parallel のインターフェースの改修
546
563
  - もう少し分かりやすく
547
564
  - 連続で入れ子にできないのを改善する
548
- - error 時に正しく捕捉しランタイムを "error" 状態にする
549
- - Runtime のエラーメッセージにマシン名を入れる。
package/README.md CHANGED
@@ -13,6 +13,13 @@ A state machine library inspired by [XState](https://stately.ai/docs/xstate).
13
13
  - Clean syntax that is hard to get lost in
14
14
 
15
15
 
16
+ ## Installation
17
+
18
+ ```
19
+ npm i small-state
20
+ ```
21
+
22
+
16
23
  ## What is an FSM (Finite State Machine)?
17
24
 
18
25
  A Finite State Machine is a computational model defined by a finite number of states and transitions between those states.
@@ -243,8 +250,25 @@ Since `loggedIn` is defined as `null`, it acts as a final state. The machine sto
243
250
  - `'notStarted'` - `.start()` has not been called yet.
244
251
  - `'active'` - `.start()` has been called and the machine is running.
245
252
  - `'stopped'` - `.stop()` was called and execution is halted.
246
- - `'done'` - The machine reached a final state and finished execution.
253
+ - `'done'` - Entered the final state, completed, and successfully stopped.
247
254
  - `'error'` - Execution stopped due to an error.
255
+ - `type Runtime<M AnyMachine extends>`
256
+ - `type Snapshot<M AnyMachine extends>` - Refer to the Runtime getSnapshot section.
257
+
258
+ ### Utility Functions
259
+
260
+ - `isMachine(v: unknown): v is AnyMachine` - Determines whether the argument is a machine.
261
+ - `isRuntime(v: unknown): v is Runtime<AnyMachine>` - Determines whether the argument is a runtime.
262
+ - `defServiceFn(serviceFn): ServiceFn`
263
+ - Defines a service function.
264
+ - Passing plain functions directly to `invoke` or defining them outside a machine configuration might cause type inference to break. Wrapping them with `defServiceFn` preserves type inference.
265
+ - `waitFor<R extends Runtime<any>>(runtime, cond, options?): Promise<SnapshotFrom<R>>` - Returns a Promise that resolves when the specified `runtime` satisfies `cond`.
266
+
267
+ ### Primary Functions
268
+
269
+ - `makeMachine(config, states): Machine` - Creates a machine definition.
270
+ - `makeRuntime(machine, option?): Runtime` - Creates an executable runtime instance for a machine.
271
+
248
272
 
249
273
  ### Reserved Event Types
250
274
 
@@ -259,17 +283,6 @@ The following event names are currently reserved:
259
283
  - `{type: ':done:', output: unknown}` - Fires when the invoked service completes successfully.
260
284
  - `{type: ':error:', error: unknown}` - Fires when the invoked service terminates with an error.
261
285
 
262
- ### Utility Functions
263
-
264
- - `defServiceFn(serviceFn): ServiceFn` - Defines a service function.
265
- - Passing plain functions directly to `invoke` or defining them outside a machine configuration might cause type inference to break. Wrapping them with `defServiceFn` preserves type inference.
266
- - `waitFor<RT extends Runtime<any>>(runtime, cond, options?): Promise<SnapshotFrom<RT>>` - Returns a Promise that resolves when the specified `runtime` satisfies `cond`.
267
-
268
- ### Primary Functions
269
-
270
- - `makeMachine(config, states): Machine` - Creates a machine definition.
271
- - `makeRuntime(machine, option?): Runtime` - Creates an executable runtime instance for a machine.
272
-
273
286
  ### `makeMachine(config: MachineConfig, defineStates: (tools)=>Record<string, StateDefinition>): Machine`
274
287
 
275
288
  #### makeMachine > `MachineConfig`
@@ -478,6 +491,7 @@ Runtime instances expose the following members:
478
491
  - `output: O | undefined` - Contains final execution output values when `runtimeStatus === 'done'`; `undefined` otherwise.
479
492
  - `error: unknown` - Contains error details if execution halted due to errors; `undefined` otherwise.
480
493
  - `_children` - Debugging member. Keyed by service name mapping to running service instances. When `subKey` is present, keys map as `${serviceName}:${subKey}`.
494
+ - `_runtime` - The runtime instance itself that generated this snapshot.
481
495
  - Direct shortcut getters exist on `runtime` instances for convenience:
482
496
  - `states`, `stateValue`, `context`, `runtimeStatus`, `output`
483
497
  - `subscribe(onSnapshot: ((current: Snapshot, prev: Snapshot)=>void)|undefined, onError?:(err:unknown)=>void): UnsubscribeFn`
@@ -513,6 +527,7 @@ Runtime instances expose the following members:
513
527
  | Feature / Behavior | XState | small-state |
514
528
  | - | - | - |
515
529
  | Path Delimiter | `.` | `/` |
530
+ | Logic ownership | Machine | Runtime |
516
531
  | Context Updates | `assign` | `update` argument inside action handlers |
517
532
  | Child to Parent Messaging | `sendParent` | `emit` |
518
533
  | Parent to Child Messaging | actor id | service name (and optional `subKey`) |
@@ -550,5 +565,3 @@ The following features are not supported:
550
565
  - Refactor `parallel` interface structure
551
566
  - Improve clarity and usability
552
567
  - Support nested continuous parallel structures
553
- - Improve error handling to catch runtime errors reliably and set status to `"error"`
554
- - Include machine name strings in Runtime error messages
package/dist/index.cjs CHANGED
@@ -519,6 +519,11 @@ function getDefinitionAndCatalog(config, defineStates) {
519
519
  function defServiceFn(fn) {
520
520
  return fn;
521
521
  }
522
+ function isMachine(v) {
523
+ if (!(v && typeof v === "object" && "internal" in v)) return false;
524
+ const { internal } = v;
525
+ return !!(internal && typeof internal === "object" && MachineSymbol in internal);
526
+ }
522
527
  function makeMachine(config, defineStates) {
523
528
  const { definition, topLvDefItem, stateCatalog } = getDefinitionAndCatalog(config, defineStates);
524
529
  const { name, initContext, output, guards, actions, services } = config;
@@ -1487,6 +1492,9 @@ function exitAndEntry(tExecs, crrStates, actionRunner, { guards, services, state
1487
1492
  }
1488
1493
  //#endregion
1489
1494
  //#region src/runtime/Runtime.ts
1495
+ function isRuntime(v) {
1496
+ return v instanceof Runtime;
1497
+ }
1490
1498
  function makeRuntime(machine, options) {
1491
1499
  return new Runtime(machine, options);
1492
1500
  }
@@ -1663,8 +1671,10 @@ var Runtime = class {
1663
1671
  output: void 0
1664
1672
  }, this.#input)),
1665
1673
  error: void 0,
1666
- _children: this.#outbound.children
1674
+ _children: this.#outbound.children,
1675
+ _runtime: this
1667
1676
  };
1677
+ Object.defineProperty(this.#snapshot, "_runtime", { enumerable: false });
1668
1678
  {
1669
1679
  const snapshot = this.#snapshot;
1670
1680
  for (const { onSnapshot } of this.#subscribers) {
@@ -1737,6 +1747,8 @@ async function waitFor(runtime, cond, options) {
1737
1747
  }
1738
1748
  //#endregion
1739
1749
  exports.defServiceFn = defServiceFn;
1750
+ exports.isMachine = isMachine;
1751
+ exports.isRuntime = isRuntime;
1740
1752
  exports.makeMachine = makeMachine;
1741
1753
  exports.makeRuntime = makeRuntime;
1742
1754
  exports.waitFor = waitFor;
package/dist/index.d.cts CHANGED
@@ -59,7 +59,9 @@ type EmittableEvent = {
59
59
  [key: string]: any;
60
60
  };
61
61
  type StateName = string;
62
- type PureETypeString<E extends SendableEvent> = E extends unknown ? Exclude<keyof E, "type"> extends never ? E['type'] : never : any;
62
+ type O_PureETypeString<E extends SendableEvent> = E extends unknown ? Exclude<keyof E, "type"> extends never ? E['type'] : never : any;
63
+ type O_InferMachineSendableEvent<M extends AnyMachine> = M extends Machine<any, infer E, any, any, any, any, any, any, any> ? E : never;
64
+ type SendFnEvent<M extends AnyMachine> = O_InferMachineSendableEvent<M> extends (infer E extends SendableEvent) ? E | O_PureETypeString<E> : never;
63
65
  type FnBaseParams<C extends MContext, Ex extends SendableEvent, I, Additional = unknown, Prm = any> = [arg: Expand<{
64
66
  c: C;
65
67
  context: C;
@@ -98,10 +100,10 @@ type EnqueueFn<C extends MContext, Ex extends SendableEvent, I, EM extends Emitt
98
100
  };
99
101
  type UpdateFnParam<C extends MContext> = { [K in keyof C]?: C[K] | (() => (C[K])); };
100
102
  type UpdateFn<C extends MContext> = (updates: UpdateFnParam<C>) => unknown;
101
- type O_InferSendableEvent<S extends ServiceRecord[string]> = S extends Machine<any, infer E, any, any, any, any, any, any, any> ? E : S extends AnyFn ? (Parameters<S>[2] extends ListenFn<infer Er> ? Er : never) : never;
103
+ type O_InferServiceSendableEvent<S extends ServiceRecord[string]> = S extends Machine<any, infer E, any, any, any, any, any, any, any> ? E : S extends AnyFn ? (Parameters<S>[2] extends ListenFn<infer Er> ? Er : never) : never;
102
104
  type SendToFn<S extends ServiceRecord> = {
103
- <Sk extends keyof S>(serviceName: Sk, subKey: string | number, event: O_InferSendableEvent<S[Sk]>): void;
104
- <Sk extends keyof S>(serviceName: Sk, event: O_InferSendableEvent<S[Sk]>): void;
105
+ <Sk extends keyof S>(serviceName: Sk, subKey: string | number, event: O_InferServiceSendableEvent<S[Sk]>): void;
106
+ <Sk extends keyof S>(serviceName: Sk, event: O_InferServiceSendableEvent<S[Sk]>): void;
105
107
  };
106
108
  type EmitFn<EM extends EmittableEvent> = (event: EM) => void;
107
109
  type ListenFn<E extends SendableEvent> = (listener: (event: E) => void) => void;
@@ -212,7 +214,7 @@ type X_HiddenImpl = any & {
212
214
  __brand: 'HiddenImpl';
213
215
  };
214
216
  type X_DefTypeRefluxer<C extends MContext, E extends SendableEvent, I // Input
215
- , EM extends EmittableEvent, G extends Record<string, Placeholder>, A extends Record<string, Placeholder>, S extends ServiceRecord = ServiceRecord, Eb extends BuiltinEType = BuiltinEType, R = unknown> = () => R;
217
+ , EM extends EmittableEvent, G extends Record<string, Placeholder>, A extends Record<string, Placeholder>, S extends ServiceRecord = ServiceRecord, Eb extends BuiltinEType = BuiltinEType, Ret = unknown> = () => Ret;
216
218
  type X_DefineStatesTools = {
217
219
  on: typeof on;
218
220
  entryHook: typeof entryHook;
@@ -292,13 +294,14 @@ declare function invoke<C extends MContext, E extends SendableEvent, I, EM exten
292
294
  declare function invoke<C extends MContext, E extends SendableEvent, I, EM extends EmittableEvent, const G extends Record<string, Placeholder>, const A extends Record<string, Placeholder>, S extends ServiceRecord, Src extends keyof S | InvokeSrcExpr<[SrcInput] extends [never] ? any : SrcInput>, SrcInput = never>(service: Src, config: InvokeConfig<C, E, I, S, Src, SrcInput>, ...events: InvocationEvents<C, I, EM, G, A, S, O_InferIvkSrcTypes<Src, S>>): readonly [typeof InvokeSymbol, X_DefTypeRefluxer<C, E, I, EM, G, A, S, BuiltinEType, unknown>, null, X_HiddenImpl, X_HiddenImpl, InvocationEvents<C, I, EM, G, A, S, O_InferIvkSrcTypes<Src, S>>];
293
295
  type X_GlobalEvents<C extends MContext, E extends SendableEvent, I, EM extends EmittableEvent, G extends Record<string, Placeholder>, A extends Record<string, Placeholder>> = Array<readonly [E['type'] | BuiltinEType, X_DefTypeRefluxer<C, E, I, EM, G, A>, X_HiddenImpl]>;
294
296
  export declare function defServiceFn<TFn extends ServiceFn>(fn: TFn): TFn;
295
- export declare function makeMachine<C extends MContext, const E extends SendableEvent, const I, const EM extends EmittableEvent, const O, const S extends ServiceRecord, const D extends { [K in string]: StateDefinition<C, E, I, EM, G, A, S>; }, const G extends Record<string, Placeholder> = Record<string, Placeholder>, const A extends Record<string, Placeholder> = Record<string, Placeholder>>(config: Expand<MachineConfig<C, E, I, EM, O, G, A, S>>, defineStates: (defineStatesTools: Expand<X_DefineStatesTools>) => readonly [X_GlobalEvents<C, E, I, EM, G, A>, D] | readonly [D] | D): Readonly<Machine<C, E, I, EM, O, G, A, S, {
297
+ export declare function isMachine(v: unknown): v is AnyMachine;
298
+ export declare function makeMachine<C extends MContext, const E extends SendableEvent, const I, const EM extends EmittableEvent, const O, const S extends ServiceRecord, const D extends { [K in string]: StateDefinition<C, E, I, EM, G, A, S>; }, const G extends Record<string, Placeholder> = Record<string, Placeholder>, const A extends Record<string, Placeholder> = Record<string, Placeholder>>(config: Expand<MachineConfig<C, E, I, EM, O, G, A, S>>, defineStates: (defineStatesTools: Expand<X_DefineStatesTools>) => readonly [X_GlobalEvents<C, E, I, EM, G, A>, D] | readonly [D] | D): Machine<C, E, I, EM, O, G, A, S, {
296
299
  initial: string;
297
300
  stateOf: { [K in keyof D]: {
298
301
  def: NStateItemDef<X_ExtractEType<D[K]>>;
299
302
  sub: X_ExtractSubStDefinition<D[K]>;
300
303
  }; };
301
- }>>;
304
+ }>;
302
305
  //#endregion
303
306
  //#region src/runtime/r.types.d.ts
304
307
  type Child<TService> = TService extends AnyMachine ? Runtime<TService> : TService extends AnyFn ? {
@@ -308,82 +311,59 @@ type Child<TService> = TService extends AnyMachine ? Runtime<TService> : TServic
308
311
  type Children<S extends ServiceRecord> = { [K in keyof S & string as K | `${K}:${string | number}`]?: Child<S[K]>; } & {
309
312
  [key: string]: Child<any>;
310
313
  };
311
- type InferredMachineTypes<M extends AnyMachine> = IsAny<M> extends true ? {
312
- C: any;
313
- E: any;
314
- I: any;
315
- EM: any;
316
- O: any;
317
- G: any;
318
- A: any;
319
- S: any;
320
- ND: any;
321
- M: AnyMachine;
322
- } : M extends Machine<infer C, infer E, infer I, infer EM, infer O, infer G, infer A, infer S, infer ND> ? {
323
- C: C;
324
- E: E;
325
- I: I;
326
- EM: EM;
327
- O: O;
328
- G: G;
329
- A: A;
330
- S: S;
331
- ND: ND;
332
- M: Machine<C, E, I, EM, O, G, A, S, ND>;
333
- } : never;
334
- type InferMInput<M extends AnyMachine> = M extends Machine<any, any, infer I, any, any, any, any, any, any> ? I : never;
335
- type SnapshotFrom<RT extends Runtime<any>> = RT extends Runtime<any, infer Mt> ? Snapshot<Mt['C'], Mt['O'], Mt['S']> : never;
314
+ type SnapshotFrom<R extends Runtime<any>> = R extends Runtime<infer M> ? Snapshot<M> : never;
336
315
  //#endregion
337
316
  //#region src/runtime/Runtime.d.ts
338
317
  type RuntimeStatus = 'notStarted' | 'active' | 'stopped' | 'done' | 'error';
339
- type Snapshot<C extends MContext, O, S extends ServiceRecord> = {
318
+ type C<M extends AnyMachine> = M extends Machine<infer C, any, any, any, any, any, any, any, any> ? C : never;
319
+ type I<M extends AnyMachine> = M extends Machine<any, any, infer I, any, any, any, any, any, any> ? I : never;
320
+ type EM$1<M extends AnyMachine> = M extends Machine<any, any, any, infer EM, any, any, any, any, any> ? EM : never;
321
+ type O$1<M extends AnyMachine> = M extends Machine<any, any, any, any, infer O, any, any, any, any> ? O : never;
322
+ type S<M extends AnyMachine> = M extends Machine<any, any, any, any, any, any, any, infer S, any> ? S : never;
323
+ type X_RemovePrivateKeys<T extends Record<string, unknown>> = { [K in keyof T as K extends `_${string}` ? never : K]: T[K]; };
324
+ type Snapshot<M extends AnyMachine> = {
340
325
  runtimeId: string;
341
326
  states: string[];
342
327
  stateValue: string | string[] | undefined;
343
- context: Expand<X_RemovePrivateKeys<C>>;
344
- c: Expand<X_RemovePrivateKeys<C>>;
328
+ context: Expand<X_RemovePrivateKeys<C<M>>>;
329
+ c: Expand<X_RemovePrivateKeys<C<M>>>;
345
330
  runtimeStatus: RuntimeStatus;
346
- output: O | undefined;
331
+ output: O$1<M> | undefined;
347
332
  error: unknown;
348
- _children: Children<S>;
333
+ _children: Children<S<M>>;
334
+ _runtime: Runtime<M>;
349
335
  };
350
- type Subscriber<C extends MContext, O, S extends ServiceRecord> = {
351
- onSnapshot?: (current: Expand<Snapshot<C, O, S>>, prev: Expand<Snapshot<C, O, S>>) => void;
336
+ type Subscriber<M extends AnyMachine> = {
337
+ onSnapshot?: (current: Expand<Snapshot<M>>, prev: Expand<Snapshot<M>>) => void;
352
338
  onError?: (err: unknown) => void;
353
339
  };
354
- type RuntimeOptions<I> = {
340
+ export declare function isRuntime(v: unknown): v is Runtime<AnyMachine>;
341
+ type RuntimeOptions<M extends AnyMachine> = {
355
342
  id?: string;
356
- input?: () => I;
343
+ input?: () => I<M>;
357
344
  };
358
- type X_RemovePrivateKeys<T extends Record<string, unknown>> = { [K in keyof T as K extends `_${string}` ? never : K]: T[K]; };
359
- export declare function makeRuntime<M extends AnyMachine>(machine: M, options?: RuntimeOptions<InferMInput<M>>): Runtime<M, InferredMachineTypes<M>>;
360
- type C$1<Mt extends InferredMachineTypes<any>> = Mt['C'];
361
- type E$1<Mt extends InferredMachineTypes<any>> = Mt['E'];
362
- type I$1<Mt extends InferredMachineTypes<any>> = Mt['I'];
363
- type EM$1<Mt extends InferredMachineTypes<any>> = Mt['EM'];
364
- type O$1<Mt extends InferredMachineTypes<any>> = Mt['O'];
365
- type S$1<Mt extends InferredMachineTypes<any>> = Mt['S'];
366
- declare class Runtime<M extends AnyMachine, Mt extends InferredMachineTypes<M> = InferredMachineTypes<M>> {
345
+ export declare function makeRuntime<M extends AnyMachine>(machine: M, options?: RuntimeOptions<M>): Runtime<M>;
346
+ declare class Runtime<M extends AnyMachine> {
367
347
  #private;
368
348
  readonly id: string;
369
- readonly machine: Mt['M'];
349
+ readonly machine: M;
370
350
  get states(): string[];
371
351
  get stateValue(): string | string[] | undefined;
372
- get context(): X_RemovePrivateKeys<C$1<Mt>> extends (infer T) ? { [K in keyof T]: X_RemovePrivateKeys<C$1<Mt>>[K]; } : never;
352
+ get context(): X_RemovePrivateKeys<C<M>> extends (infer T) ? { [K in keyof T]: X_RemovePrivateKeys<C<M>>[K]; } : never;
373
353
  get runtimeStatus(): RuntimeStatus;
374
- get output(): O$1<Mt> | undefined;
375
- getSnapshot(): Snapshot<C$1<Mt>, O$1<Mt>, S$1<Mt>>;
376
- constructor(machine: M, { id, input }?: RuntimeOptions<I$1<Mt>>);
377
- subscribe(onSnapshot: Subscriber<C$1<Mt>, O$1<Mt>, S$1<Mt>>['onSnapshot'], onError?: Subscriber<C$1<Mt>, O$1<Mt>, S$1<Mt>>['onError']): () => void;
354
+ get output(): O$1<M> | undefined;
355
+ getSnapshot(): Snapshot<M>;
356
+ constructor(machine: M, { id, input }?: RuntimeOptions<I<M>>);
357
+ subscribe(onSnapshot: Subscriber<M>['onSnapshot'], onError?: Subscriber<M>['onError']): () => void;
378
358
  start(): this;
379
- send(event: E$1<Mt> | PureETypeString<E$1<Mt>>): this;
359
+ send(event: SendFnEvent<M>): this;
380
360
  stop(): this;
381
- onEmitted(emittedEventType: EM$1<Mt>['type'], handler: (emittedEvent: EM$1<Mt>) => void): () => void;
361
+ onEmitted(emittedEventType: EM$1<M>['type'], handler: (emittedEvent: EM$1<M>) => void): () => void;
382
362
  }
383
363
  //#endregion
384
364
  //#region src/tools/waitFor.d.ts
385
- export declare function waitFor<RT extends Runtime<any>>(runtime: RT, cond: (snapshot: SnapshotFrom<RT>) => boolean, options?: {
365
+ export declare function waitFor<R extends Runtime<any>>(runtime: R, cond: (snapshot: SnapshotFrom<R>) => boolean, options?: {
386
366
  timeout?: number;
387
- }): Promise<SnapshotFrom<RT>>;
367
+ }): Promise<SnapshotFrom<R>>;
388
368
  //#endregion
389
- export type { AnyMachine, BuiltinEType, EmitFn as Emit, EmittableEvent, ListenFn as Listen, MContext, Runtime, RuntimeOptions, RuntimeStatus, SendableEvent, ServiceFn };
369
+ export type { AnyMachine, BuiltinEType, EmitFn as Emit, EmittableEvent, ListenFn as Listen, MContext, Runtime, RuntimeOptions, RuntimeStatus, SendFnEvent, SendableEvent, ServiceFn, Snapshot };
package/dist/index.d.mts CHANGED
@@ -59,7 +59,9 @@ type EmittableEvent = {
59
59
  [key: string]: any;
60
60
  };
61
61
  type StateName = string;
62
- type PureETypeString<E extends SendableEvent> = E extends unknown ? Exclude<keyof E, "type"> extends never ? E['type'] : never : any;
62
+ type O_PureETypeString<E extends SendableEvent> = E extends unknown ? Exclude<keyof E, "type"> extends never ? E['type'] : never : any;
63
+ type O_InferMachineSendableEvent<M extends AnyMachine> = M extends Machine<any, infer E, any, any, any, any, any, any, any> ? E : never;
64
+ type SendFnEvent<M extends AnyMachine> = O_InferMachineSendableEvent<M> extends (infer E extends SendableEvent) ? E | O_PureETypeString<E> : never;
63
65
  type FnBaseParams<C extends MContext, Ex extends SendableEvent, I, Additional = unknown, Prm = any> = [arg: Expand<{
64
66
  c: C;
65
67
  context: C;
@@ -98,10 +100,10 @@ type EnqueueFn<C extends MContext, Ex extends SendableEvent, I, EM extends Emitt
98
100
  };
99
101
  type UpdateFnParam<C extends MContext> = { [K in keyof C]?: C[K] | (() => (C[K])); };
100
102
  type UpdateFn<C extends MContext> = (updates: UpdateFnParam<C>) => unknown;
101
- type O_InferSendableEvent<S extends ServiceRecord[string]> = S extends Machine<any, infer E, any, any, any, any, any, any, any> ? E : S extends AnyFn ? (Parameters<S>[2] extends ListenFn<infer Er> ? Er : never) : never;
103
+ type O_InferServiceSendableEvent<S extends ServiceRecord[string]> = S extends Machine<any, infer E, any, any, any, any, any, any, any> ? E : S extends AnyFn ? (Parameters<S>[2] extends ListenFn<infer Er> ? Er : never) : never;
102
104
  type SendToFn<S extends ServiceRecord> = {
103
- <Sk extends keyof S>(serviceName: Sk, subKey: string | number, event: O_InferSendableEvent<S[Sk]>): void;
104
- <Sk extends keyof S>(serviceName: Sk, event: O_InferSendableEvent<S[Sk]>): void;
105
+ <Sk extends keyof S>(serviceName: Sk, subKey: string | number, event: O_InferServiceSendableEvent<S[Sk]>): void;
106
+ <Sk extends keyof S>(serviceName: Sk, event: O_InferServiceSendableEvent<S[Sk]>): void;
105
107
  };
106
108
  type EmitFn<EM extends EmittableEvent> = (event: EM) => void;
107
109
  type ListenFn<E extends SendableEvent> = (listener: (event: E) => void) => void;
@@ -212,7 +214,7 @@ type X_HiddenImpl = any & {
212
214
  __brand: 'HiddenImpl';
213
215
  };
214
216
  type X_DefTypeRefluxer<C extends MContext, E extends SendableEvent, I // Input
215
- , EM extends EmittableEvent, G extends Record<string, Placeholder>, A extends Record<string, Placeholder>, S extends ServiceRecord = ServiceRecord, Eb extends BuiltinEType = BuiltinEType, R = unknown> = () => R;
217
+ , EM extends EmittableEvent, G extends Record<string, Placeholder>, A extends Record<string, Placeholder>, S extends ServiceRecord = ServiceRecord, Eb extends BuiltinEType = BuiltinEType, Ret = unknown> = () => Ret;
216
218
  type X_DefineStatesTools = {
217
219
  on: typeof on;
218
220
  entryHook: typeof entryHook;
@@ -292,13 +294,14 @@ declare function invoke<C extends MContext, E extends SendableEvent, I, EM exten
292
294
  declare function invoke<C extends MContext, E extends SendableEvent, I, EM extends EmittableEvent, const G extends Record<string, Placeholder>, const A extends Record<string, Placeholder>, S extends ServiceRecord, Src extends keyof S | InvokeSrcExpr<[SrcInput] extends [never] ? any : SrcInput>, SrcInput = never>(service: Src, config: InvokeConfig<C, E, I, S, Src, SrcInput>, ...events: InvocationEvents<C, I, EM, G, A, S, O_InferIvkSrcTypes<Src, S>>): readonly [typeof InvokeSymbol, X_DefTypeRefluxer<C, E, I, EM, G, A, S, BuiltinEType, unknown>, null, X_HiddenImpl, X_HiddenImpl, InvocationEvents<C, I, EM, G, A, S, O_InferIvkSrcTypes<Src, S>>];
293
295
  type X_GlobalEvents<C extends MContext, E extends SendableEvent, I, EM extends EmittableEvent, G extends Record<string, Placeholder>, A extends Record<string, Placeholder>> = Array<readonly [E['type'] | BuiltinEType, X_DefTypeRefluxer<C, E, I, EM, G, A>, X_HiddenImpl]>;
294
296
  export declare function defServiceFn<TFn extends ServiceFn>(fn: TFn): TFn;
295
- export declare function makeMachine<C extends MContext, const E extends SendableEvent, const I, const EM extends EmittableEvent, const O, const S extends ServiceRecord, const D extends { [K in string]: StateDefinition<C, E, I, EM, G, A, S>; }, const G extends Record<string, Placeholder> = Record<string, Placeholder>, const A extends Record<string, Placeholder> = Record<string, Placeholder>>(config: Expand<MachineConfig<C, E, I, EM, O, G, A, S>>, defineStates: (defineStatesTools: Expand<X_DefineStatesTools>) => readonly [X_GlobalEvents<C, E, I, EM, G, A>, D] | readonly [D] | D): Readonly<Machine<C, E, I, EM, O, G, A, S, {
297
+ export declare function isMachine(v: unknown): v is AnyMachine;
298
+ export declare function makeMachine<C extends MContext, const E extends SendableEvent, const I, const EM extends EmittableEvent, const O, const S extends ServiceRecord, const D extends { [K in string]: StateDefinition<C, E, I, EM, G, A, S>; }, const G extends Record<string, Placeholder> = Record<string, Placeholder>, const A extends Record<string, Placeholder> = Record<string, Placeholder>>(config: Expand<MachineConfig<C, E, I, EM, O, G, A, S>>, defineStates: (defineStatesTools: Expand<X_DefineStatesTools>) => readonly [X_GlobalEvents<C, E, I, EM, G, A>, D] | readonly [D] | D): Machine<C, E, I, EM, O, G, A, S, {
296
299
  initial: string;
297
300
  stateOf: { [K in keyof D]: {
298
301
  def: NStateItemDef<X_ExtractEType<D[K]>>;
299
302
  sub: X_ExtractSubStDefinition<D[K]>;
300
303
  }; };
301
- }>>;
304
+ }>;
302
305
  //#endregion
303
306
  //#region src/runtime/r.types.d.ts
304
307
  type Child<TService> = TService extends AnyMachine ? Runtime<TService> : TService extends AnyFn ? {
@@ -308,82 +311,59 @@ type Child<TService> = TService extends AnyMachine ? Runtime<TService> : TServic
308
311
  type Children<S extends ServiceRecord> = { [K in keyof S & string as K | `${K}:${string | number}`]?: Child<S[K]>; } & {
309
312
  [key: string]: Child<any>;
310
313
  };
311
- type InferredMachineTypes<M extends AnyMachine> = IsAny<M> extends true ? {
312
- C: any;
313
- E: any;
314
- I: any;
315
- EM: any;
316
- O: any;
317
- G: any;
318
- A: any;
319
- S: any;
320
- ND: any;
321
- M: AnyMachine;
322
- } : M extends Machine<infer C, infer E, infer I, infer EM, infer O, infer G, infer A, infer S, infer ND> ? {
323
- C: C;
324
- E: E;
325
- I: I;
326
- EM: EM;
327
- O: O;
328
- G: G;
329
- A: A;
330
- S: S;
331
- ND: ND;
332
- M: Machine<C, E, I, EM, O, G, A, S, ND>;
333
- } : never;
334
- type InferMInput<M extends AnyMachine> = M extends Machine<any, any, infer I, any, any, any, any, any, any> ? I : never;
335
- type SnapshotFrom<RT extends Runtime<any>> = RT extends Runtime<any, infer Mt> ? Snapshot<Mt['C'], Mt['O'], Mt['S']> : never;
314
+ type SnapshotFrom<R extends Runtime<any>> = R extends Runtime<infer M> ? Snapshot<M> : never;
336
315
  //#endregion
337
316
  //#region src/runtime/Runtime.d.ts
338
317
  type RuntimeStatus = 'notStarted' | 'active' | 'stopped' | 'done' | 'error';
339
- type Snapshot<C extends MContext, O, S extends ServiceRecord> = {
318
+ type C<M extends AnyMachine> = M extends Machine<infer C, any, any, any, any, any, any, any, any> ? C : never;
319
+ type I<M extends AnyMachine> = M extends Machine<any, any, infer I, any, any, any, any, any, any> ? I : never;
320
+ type EM$1<M extends AnyMachine> = M extends Machine<any, any, any, infer EM, any, any, any, any, any> ? EM : never;
321
+ type O$1<M extends AnyMachine> = M extends Machine<any, any, any, any, infer O, any, any, any, any> ? O : never;
322
+ type S<M extends AnyMachine> = M extends Machine<any, any, any, any, any, any, any, infer S, any> ? S : never;
323
+ type X_RemovePrivateKeys<T extends Record<string, unknown>> = { [K in keyof T as K extends `_${string}` ? never : K]: T[K]; };
324
+ type Snapshot<M extends AnyMachine> = {
340
325
  runtimeId: string;
341
326
  states: string[];
342
327
  stateValue: string | string[] | undefined;
343
- context: Expand<X_RemovePrivateKeys<C>>;
344
- c: Expand<X_RemovePrivateKeys<C>>;
328
+ context: Expand<X_RemovePrivateKeys<C<M>>>;
329
+ c: Expand<X_RemovePrivateKeys<C<M>>>;
345
330
  runtimeStatus: RuntimeStatus;
346
- output: O | undefined;
331
+ output: O$1<M> | undefined;
347
332
  error: unknown;
348
- _children: Children<S>;
333
+ _children: Children<S<M>>;
334
+ _runtime: Runtime<M>;
349
335
  };
350
- type Subscriber<C extends MContext, O, S extends ServiceRecord> = {
351
- onSnapshot?: (current: Expand<Snapshot<C, O, S>>, prev: Expand<Snapshot<C, O, S>>) => void;
336
+ type Subscriber<M extends AnyMachine> = {
337
+ onSnapshot?: (current: Expand<Snapshot<M>>, prev: Expand<Snapshot<M>>) => void;
352
338
  onError?: (err: unknown) => void;
353
339
  };
354
- type RuntimeOptions<I> = {
340
+ export declare function isRuntime(v: unknown): v is Runtime<AnyMachine>;
341
+ type RuntimeOptions<M extends AnyMachine> = {
355
342
  id?: string;
356
- input?: () => I;
343
+ input?: () => I<M>;
357
344
  };
358
- type X_RemovePrivateKeys<T extends Record<string, unknown>> = { [K in keyof T as K extends `_${string}` ? never : K]: T[K]; };
359
- export declare function makeRuntime<M extends AnyMachine>(machine: M, options?: RuntimeOptions<InferMInput<M>>): Runtime<M, InferredMachineTypes<M>>;
360
- type C$1<Mt extends InferredMachineTypes<any>> = Mt['C'];
361
- type E$1<Mt extends InferredMachineTypes<any>> = Mt['E'];
362
- type I$1<Mt extends InferredMachineTypes<any>> = Mt['I'];
363
- type EM$1<Mt extends InferredMachineTypes<any>> = Mt['EM'];
364
- type O$1<Mt extends InferredMachineTypes<any>> = Mt['O'];
365
- type S$1<Mt extends InferredMachineTypes<any>> = Mt['S'];
366
- declare class Runtime<M extends AnyMachine, Mt extends InferredMachineTypes<M> = InferredMachineTypes<M>> {
345
+ export declare function makeRuntime<M extends AnyMachine>(machine: M, options?: RuntimeOptions<M>): Runtime<M>;
346
+ declare class Runtime<M extends AnyMachine> {
367
347
  #private;
368
348
  readonly id: string;
369
- readonly machine: Mt['M'];
349
+ readonly machine: M;
370
350
  get states(): string[];
371
351
  get stateValue(): string | string[] | undefined;
372
- get context(): X_RemovePrivateKeys<C$1<Mt>> extends (infer T) ? { [K in keyof T]: X_RemovePrivateKeys<C$1<Mt>>[K]; } : never;
352
+ get context(): X_RemovePrivateKeys<C<M>> extends (infer T) ? { [K in keyof T]: X_RemovePrivateKeys<C<M>>[K]; } : never;
373
353
  get runtimeStatus(): RuntimeStatus;
374
- get output(): O$1<Mt> | undefined;
375
- getSnapshot(): Snapshot<C$1<Mt>, O$1<Mt>, S$1<Mt>>;
376
- constructor(machine: M, { id, input }?: RuntimeOptions<I$1<Mt>>);
377
- subscribe(onSnapshot: Subscriber<C$1<Mt>, O$1<Mt>, S$1<Mt>>['onSnapshot'], onError?: Subscriber<C$1<Mt>, O$1<Mt>, S$1<Mt>>['onError']): () => void;
354
+ get output(): O$1<M> | undefined;
355
+ getSnapshot(): Snapshot<M>;
356
+ constructor(machine: M, { id, input }?: RuntimeOptions<I<M>>);
357
+ subscribe(onSnapshot: Subscriber<M>['onSnapshot'], onError?: Subscriber<M>['onError']): () => void;
378
358
  start(): this;
379
- send(event: E$1<Mt> | PureETypeString<E$1<Mt>>): this;
359
+ send(event: SendFnEvent<M>): this;
380
360
  stop(): this;
381
- onEmitted(emittedEventType: EM$1<Mt>['type'], handler: (emittedEvent: EM$1<Mt>) => void): () => void;
361
+ onEmitted(emittedEventType: EM$1<M>['type'], handler: (emittedEvent: EM$1<M>) => void): () => void;
382
362
  }
383
363
  //#endregion
384
364
  //#region src/tools/waitFor.d.ts
385
- export declare function waitFor<RT extends Runtime<any>>(runtime: RT, cond: (snapshot: SnapshotFrom<RT>) => boolean, options?: {
365
+ export declare function waitFor<R extends Runtime<any>>(runtime: R, cond: (snapshot: SnapshotFrom<R>) => boolean, options?: {
386
366
  timeout?: number;
387
- }): Promise<SnapshotFrom<RT>>;
367
+ }): Promise<SnapshotFrom<R>>;
388
368
  //#endregion
389
- export type { AnyMachine, BuiltinEType, EmitFn as Emit, EmittableEvent, ListenFn as Listen, MContext, Runtime, RuntimeOptions, RuntimeStatus, SendableEvent, ServiceFn };
369
+ export type { AnyMachine, BuiltinEType, EmitFn as Emit, EmittableEvent, ListenFn as Listen, MContext, Runtime, RuntimeOptions, RuntimeStatus, SendFnEvent, SendableEvent, ServiceFn, Snapshot };
package/dist/index.mjs CHANGED
@@ -518,6 +518,11 @@ function getDefinitionAndCatalog(config, defineStates) {
518
518
  function defServiceFn(fn) {
519
519
  return fn;
520
520
  }
521
+ function isMachine(v) {
522
+ if (!(v && typeof v === "object" && "internal" in v)) return false;
523
+ const { internal } = v;
524
+ return !!(internal && typeof internal === "object" && MachineSymbol in internal);
525
+ }
521
526
  function makeMachine(config, defineStates) {
522
527
  const { definition, topLvDefItem, stateCatalog } = getDefinitionAndCatalog(config, defineStates);
523
528
  const { name, initContext, output, guards, actions, services } = config;
@@ -1486,6 +1491,9 @@ function exitAndEntry(tExecs, crrStates, actionRunner, { guards, services, state
1486
1491
  }
1487
1492
  //#endregion
1488
1493
  //#region src/runtime/Runtime.ts
1494
+ function isRuntime(v) {
1495
+ return v instanceof Runtime;
1496
+ }
1489
1497
  function makeRuntime(machine, options) {
1490
1498
  return new Runtime(machine, options);
1491
1499
  }
@@ -1662,8 +1670,10 @@ var Runtime = class {
1662
1670
  output: void 0
1663
1671
  }, this.#input)),
1664
1672
  error: void 0,
1665
- _children: this.#outbound.children
1673
+ _children: this.#outbound.children,
1674
+ _runtime: this
1666
1675
  };
1676
+ Object.defineProperty(this.#snapshot, "_runtime", { enumerable: false });
1667
1677
  {
1668
1678
  const snapshot = this.#snapshot;
1669
1679
  for (const { onSnapshot } of this.#subscribers) {
@@ -1735,4 +1745,4 @@ async function waitFor(runtime, cond, options) {
1735
1745
  });
1736
1746
  }
1737
1747
  //#endregion
1738
- export { defServiceFn, makeMachine, makeRuntime, waitFor };
1748
+ export { defServiceFn, isMachine, isRuntime, makeMachine, makeRuntime, waitFor };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "small-state",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "A state machine inspired by XState.",
5
5
  "author": "qyvvy",
6
6
  "license": "MIT",
@@ -27,7 +27,9 @@
27
27
  "dist",
28
28
  "THIRD_PARTY_LICENSES.md"
29
29
  ],
30
- "engines": { "node": ">=18" },
30
+ "engines": {
31
+ "node": ">=18"
32
+ },
31
33
  "dependencies": {
32
34
  "arktype": "^2.2.8"
33
35
  },