@doeixd/machine 0.0.18 → 0.0.20

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.
@@ -0,0 +1,325 @@
1
+ /**
2
+ * @file Advanced Pattern Matching for State Machines
3
+ * @description
4
+ * Provides type-safe pattern matching utilities for discriminating between machine types.
5
+ * Supports three complementary APIs: type guards, exhaustive pattern matching, and simple matching.
6
+ *
7
+ * @example
8
+ * ```typescript
9
+ * // Define a matcher for class-based machines
10
+ * const match = createMatcher(
11
+ * classCase('idle', IdleMachine),
12
+ * classCase('loading', LoadingMachine),
13
+ * classCase('success', SuccessMachine)
14
+ * );
15
+ *
16
+ * // API 1: Type Guards
17
+ * if (match.is.loading(machine)) {
18
+ * // machine is narrowed to LoadingMachine
19
+ * }
20
+ *
21
+ * // API 2: Exhaustive Pattern Matching
22
+ * const result = match.when(machine).is<string>(
23
+ * match.case.idle(() => 'idle'),
24
+ * match.case.loading(() => 'loading'),
25
+ * match.case.success(m => m.context.data),
26
+ * match.exhaustive
27
+ * );
28
+ *
29
+ * // API 3: Simple Match
30
+ * const name = match(machine); // 'idle' | 'loading' | 'success' | null
31
+ * ```
32
+ */
33
+ import type { Machine, Context } from './index';
34
+ /**
35
+ * A matcher case tuple that defines a state pattern.
36
+ *
37
+ * @template Name - The unique name for this case (used for type guards and pattern matching)
38
+ * @template M - The machine type this case matches
39
+ * @template Pred - The predicate function that determines if a machine matches this case
40
+ */
41
+ export type MatcherCase<Name extends string, M, Pred extends (m: any) => m is M> = readonly [
42
+ name: Name,
43
+ machineType: M,
44
+ predicate: Pred
45
+ ];
46
+ /**
47
+ * Extracts the machine type from a MatcherCase.
48
+ */
49
+ type CaseToMachine<C> = C extends MatcherCase<any, infer M, any> ? M : never;
50
+ /**
51
+ * Extracts the case name from a MatcherCase.
52
+ */
53
+ type CaseToName<C> = C extends MatcherCase<infer Name, any, any> ? Name : never;
54
+ /**
55
+ * Builds a mapping from case names to their machine types.
56
+ */
57
+ export type CasesToMapping<Cases extends readonly MatcherCase<any, any, any>[]> = {
58
+ [C in Cases[number] as CaseToName<C>]: CaseToMachine<C>;
59
+ };
60
+ /**
61
+ * Creates a union of all possible machine types from the cases.
62
+ */
63
+ export type MatcherUnion<Cases extends readonly MatcherCase<any, any, any>[]> = Cases[number] extends MatcherCase<any, infer M, any> ? M : never;
64
+ /**
65
+ * Extracts the union of all case names.
66
+ */
67
+ export type CaseNames<Cases extends readonly MatcherCase<any, any, any>[]> = CaseToName<Cases[number]>;
68
+ /**
69
+ * A branded type representing a case handler in pattern matching.
70
+ * This is used internally to track which cases have been handled.
71
+ */
72
+ export type CaseHandler<Name extends string, M, R> = {
73
+ readonly __brand: 'CaseHandler';
74
+ readonly __name: Name;
75
+ readonly __machine: M;
76
+ readonly __return: R;
77
+ readonly handler: (machine: M) => R;
78
+ };
79
+ /**
80
+ * Exhaustiveness marker - signals that all cases must be handled.
81
+ */
82
+ export type ExhaustivenessMarker = {
83
+ readonly __exhaustive: true;
84
+ };
85
+ /**
86
+ * Extracts machine types from an array of case handlers.
87
+ */
88
+ type ExtractHandledMachines<H extends readonly any[]> = H extends readonly [infer First, ...infer Rest] ? (First extends CaseHandler<any, infer M, any> ? M : never) | ExtractHandledMachines<Rest> : never;
89
+ /**
90
+ * Extracts return types from an array of case handlers.
91
+ */
92
+ type ExtractHandlerReturn<H extends readonly any[]> = H extends readonly CaseHandler<any, any, infer R>[] ? R : never;
93
+ /**
94
+ * Checks if all machine types in Union have been handled.
95
+ * Returns true if exhaustive, otherwise returns an error type with missing cases.
96
+ */
97
+ export type IsExhaustive<Union, Handled> = Exclude<Union, Handled> extends never ? true : {
98
+ readonly __error: 'Non-exhaustive match - missing cases';
99
+ readonly __missing: Exclude<Union, Handled>;
100
+ };
101
+ /**
102
+ * Pattern matching builder returned by matcher.when().
103
+ */
104
+ export interface WhenBuilder<_Cases extends readonly MatcherCase<any, any, any>[], M> {
105
+ /**
106
+ * Execute pattern matching with exhaustiveness checking.
107
+ *
108
+ * @template R - The return type of all handlers
109
+ * @param handlers - Array of case handlers followed by exhaustiveness marker
110
+ * @returns The result of the matched handler, or compile error if not exhaustive
111
+ *
112
+ * @example
113
+ * ```typescript
114
+ * match.when(machine).is<string>(
115
+ * match.case.idle(() => 'idle'),
116
+ * match.case.loading(() => 'loading'),
117
+ * match.exhaustive
118
+ * );
119
+ * ```
120
+ */
121
+ /**
122
+ * Overload 1: Infer return type from handlers (Enables exhaustiveness checking).
123
+ */
124
+ is<H extends readonly CaseHandler<CaseNames<_Cases>, any, any>[]>(...handlers: [...H, ExhaustivenessMarker]): IsExhaustive<M, ExtractHandledMachines<H>> extends true ? ExtractHandlerReturn<H> : IsExhaustive<M, ExtractHandledMachines<H>>;
125
+ /**
126
+ * Overload 2: Explicit return type (No exhaustiveness checking).
127
+ */
128
+ is<R>(...handlers: [...CaseHandler<CaseNames<_Cases>, any, R>[], ExhaustivenessMarker]): R;
129
+ }
130
+ /**
131
+ * The main Matcher interface with three APIs.
132
+ */
133
+ export interface Matcher<Cases extends readonly MatcherCase<any, any, any>[]> {
134
+ /**
135
+ * API 1: Type guard access via dynamic properties.
136
+ *
137
+ * @example
138
+ * ```typescript
139
+ * if (match.is.loading(machine)) {
140
+ * // machine is narrowed to LoadingMachine
141
+ * }
142
+ * ```
143
+ */
144
+ readonly is: {
145
+ [Name in CaseNames<Cases>]: (machine: any) => machine is CasesToMapping<Cases>[Name];
146
+ };
147
+ /**
148
+ * API 2a: Pattern matching builder.
149
+ *
150
+ * @example
151
+ * ```typescript
152
+ * match.when(machine).is<string>(
153
+ * match.case.idle(() => 'idle'),
154
+ * match.case.loading(() => 'loading'),
155
+ * match.exhaustive
156
+ * );
157
+ * ```
158
+ */
159
+ when: <M>(machine: M) => WhenBuilder<Cases, M>;
160
+ /**
161
+ * API 2b: Case handler creator for pattern matching.
162
+ *
163
+ * @example
164
+ * ```typescript
165
+ * match.case.loading((m) => `Loading: ${m.context.startTime}`)
166
+ * ```
167
+ */
168
+ readonly case: {
169
+ [Name in CaseNames<Cases>]: <R>(handler: (machine: CasesToMapping<Cases>[Name]) => R) => CaseHandler<Name, CasesToMapping<Cases>[Name], R>;
170
+ };
171
+ /**
172
+ * API 2c: Exhaustiveness marker for pattern matching.
173
+ */
174
+ readonly exhaustive: ExhaustivenessMarker;
175
+ /**
176
+ * API 3: Simple match - returns the name of the matched case or null.
177
+ *
178
+ * @example
179
+ * ```typescript
180
+ * const name = match(machine); // 'idle' | 'loading' | 'success' | null
181
+ * ```
182
+ */
183
+ <M>(machine: M): M extends MatcherUnion<Cases> ? CaseNames<Cases> | null : null;
184
+ }
185
+ /**
186
+ * Creates a type-safe matcher for discriminating between machine types.
187
+ *
188
+ * @template Cases - Tuple of [name, MachineType, predicate] configurations
189
+ * @param cases - Array of matcher case definitions
190
+ * @returns A matcher object with three APIs: is (type guards), when (pattern matching), and direct call (simple match)
191
+ *
192
+ * @example
193
+ * ```typescript
194
+ * // Class-based matching
195
+ * const match = createMatcher(
196
+ * ['idle', IdleMachine, (m): m is IdleMachine => m instanceof IdleMachine],
197
+ * ['loading', LoadingMachine, (m): m is LoadingMachine => m instanceof LoadingMachine]
198
+ * );
199
+ *
200
+ * // Or use helper functions
201
+ * const match = createMatcher(
202
+ * classCase('idle', IdleMachine),
203
+ * classCase('loading', LoadingMachine)
204
+ * );
205
+ * ```
206
+ */
207
+ export declare function createMatcher<const Cases extends readonly MatcherCase<string, any, (m: any) => m is any>[]>(...cases: Cases): Matcher<Cases>;
208
+ /**
209
+ * Creates a class-based matcher case using instanceof checking.
210
+ * This is the most common pattern for Type-State machines.
211
+ *
212
+ * @template Name - The unique name for this case
213
+ * @template T - The class constructor
214
+ * @param name - The name to use for this case
215
+ * @param machineClass - The class to check with instanceof
216
+ * @returns A matcher case tuple
217
+ *
218
+ * @example
219
+ * ```typescript
220
+ * const match = createMatcher(
221
+ * classCase('idle', IdleMachine),
222
+ * classCase('loading', LoadingMachine),
223
+ * classCase('success', SuccessMachine)
224
+ * );
225
+ * ```
226
+ */
227
+ export declare function classCase<Name extends string, T extends abstract new (...args: any[]) => any>(name: Name, machineClass: T): MatcherCase<Name, InstanceType<T>, (m: any) => m is InstanceType<T>>;
228
+ /**
229
+ * Creates a discriminated union matcher case based on a context property.
230
+ * This integrates with the existing hasState utility for context-based discrimination.
231
+ *
232
+ * @template Name - The unique name for this case
233
+ * @template M - The machine type (use Machine<YourContextUnion> for proper narrowing)
234
+ * @template K - The context key to check
235
+ * @template V - The value to match
236
+ * @param name - The name to use for this case
237
+ * @param key - The context property to check
238
+ * @param value - The value the property should equal
239
+ * @returns A matcher case tuple
240
+ *
241
+ * @example
242
+ * ```typescript
243
+ * type FetchContext =
244
+ * | { status: 'idle' }
245
+ * | { status: 'loading' }
246
+ * | { status: 'success'; data: string };
247
+ *
248
+ * const match = createMatcher(
249
+ * discriminantCase<'idle', Machine<FetchContext>, 'status', 'idle'>('idle', 'status', 'idle'),
250
+ * discriminantCase<'loading', Machine<FetchContext>, 'status', 'loading'>('loading', 'status', 'loading'),
251
+ * discriminantCase<'success', Machine<FetchContext>, 'status', 'success'>('success', 'status', 'success')
252
+ * );
253
+ * ```
254
+ */
255
+ export declare function discriminantCase<Name extends string, M extends Machine<any> = Machine<any>, K extends keyof Context<M> = any, V extends Context<M>[K] = any>(name: Name, key: K, value: V): MatcherCase<Name, M & {
256
+ context: Extract<Context<M>, {
257
+ [P in K]: V;
258
+ }>;
259
+ }, (m: M) => m is M & {
260
+ context: Extract<Context<M>, {
261
+ [P in K]: V;
262
+ }>;
263
+ }>;
264
+ /**
265
+ * Creates a custom matcher case with a user-defined predicate.
266
+ * For advanced matching logic beyond instanceof or discriminants.
267
+ *
268
+ * @template Name - The unique name for this case
269
+ * @template M - The machine type this case matches (inferred from predicate)
270
+ * @param name - The name to use for this case
271
+ * @param predicate - A type guard function that determines if a machine matches
272
+ * @returns A matcher case tuple
273
+ *
274
+ * @example
275
+ * ```typescript
276
+ * const match = createMatcher(
277
+ * customCase('complex', (m): m is ComplexMachine => {
278
+ * return m.context.value > 10 && m.context.status === 'active';
279
+ * })
280
+ * );
281
+ * ```
282
+ */
283
+ export declare function customCase<const Name extends string, M>(name: Name, predicate: (m: any) => m is M): MatcherCase<Name, M, (m: any) => m is M>;
284
+ /**
285
+ * Creates a discriminated matcher builder for a specific context union type.
286
+ * Provides better type inference by capturing the context type upfront.
287
+ *
288
+ * @template C - The discriminated union context type
289
+ * @returns A builder object with a `case` method for defining cases with less boilerplate
290
+ *
291
+ * @example
292
+ * ```typescript
293
+ * type FetchContext =
294
+ * | { status: 'idle' }
295
+ * | { status: 'loading'; startTime: number }
296
+ * | { status: 'success'; data: string };
297
+ *
298
+ * const builder = forContext<FetchContext>();
299
+ *
300
+ * const match = createMatcher(
301
+ * builder.case('idle', 'status', 'idle'),
302
+ * builder.case('loading', 'status', 'loading'),
303
+ * builder.case('success', 'status', 'success')
304
+ * );
305
+ *
306
+ * // Full type inference and narrowing works!
307
+ * if (match.is.success(machine)) {
308
+ * console.log(machine.context.data); // ✓ TypeScript knows data exists
309
+ * }
310
+ * ```
311
+ */
312
+ export declare function forContext<C extends object>(): {
313
+ /**
314
+ * Creates a discriminated union case with full type inference.
315
+ */
316
+ case<Name extends string, K extends keyof C, V extends C[K]>(name: Name, key: K, value: V): MatcherCase<Name, {
317
+ readonly context: Extract<C, { [P in K]: V; }>;
318
+ }, (m: {
319
+ readonly context: C;
320
+ }) => m is {
321
+ readonly context: Extract<C, { [P in K]: V; }>;
322
+ }>;
323
+ };
324
+ export {};
325
+ //# sourceMappingURL=matcher.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"matcher.d.ts","sourceRoot":"","sources":["../../src/matcher.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@doeixd/machine",
3
- "version": "0.0.18",
3
+ "version": "0.0.20",
4
4
  "files": [
5
5
  "dist",
6
6
  "src"
@@ -115,12 +115,24 @@
115
115
  },
116
116
  "require": "./dist/cjs/production/core.js",
117
117
  "import": "./dist/esm/production/core.js"
118
+ },
119
+ "./extract": {
120
+ "types": "./dist/types/extract.d.ts",
121
+ "development": {
122
+ "require": "./dist/cjs/development/extract.js",
123
+ "import": "./dist/esm/development/extract.js"
124
+ },
125
+ "require": "./dist/cjs/production/extract.js",
126
+ "import": "./dist/esm/production/extract.js"
118
127
  }
119
128
  },
120
129
  "typesVersions": {
121
130
  "*": {
122
131
  "core": [
123
132
  "./dist/types/core.d.ts"
133
+ ],
134
+ "extract": [
135
+ "./dist/types/extract.d.ts"
124
136
  ]
125
137
  }
126
138
  },
package/src/index.ts CHANGED
@@ -345,7 +345,7 @@ export type BindTransitions<T> = {
345
345
  * @param factory - A function that receives a `transition` helper and returns the transitions object.
346
346
  * @returns A new machine instance.
347
347
  */
348
- export function createMachine<C extends object, T extends Record<string, (this: C, ...args: any[]) => any>>(
348
+ export function createMachine<C extends object, T extends Record<string, (this: C, ...args: any[]) => any> = Record<string, (this: C, ...args: any[]) => any>>(
349
349
  context: C,
350
350
  factory: (transition: (newContext: C) => Machine<C, any>) => T
351
351
  ): Machine<C, BindTransitions<T>>;
@@ -584,6 +584,41 @@ export function setContext<M extends Machine<any>>(
584
584
  return createMachine(newContext, transitions as any) as M;
585
585
  }
586
586
 
587
+ /**
588
+ * Creates a minimal machine-like object with just a context property.
589
+ * Useful for creating test fixtures and working with pattern matching utilities.
590
+ *
591
+ * @template C - The context type
592
+ * @param context - The context object
593
+ * @returns An object with a readonly context property
594
+ *
595
+ * @example
596
+ * ```typescript
597
+ * // For testing with discriminated unions
598
+ * type FetchContext =
599
+ * | { status: 'idle' }
600
+ * | { status: 'success'; data: string };
601
+ *
602
+ * const idleMachine = createContext<FetchContext>({ status: 'idle' });
603
+ * const successMachine = createContext<FetchContext>({ status: 'success', data: 'result' });
604
+ *
605
+ * // Works with pattern matching
606
+ * const match = createMatcher(
607
+ * discriminantCase('idle', 'status', 'idle'),
608
+ * discriminantCase('success', 'status', 'success')
609
+ * );
610
+ *
611
+ * if (match.is.success(successMachine)) {
612
+ * console.log(successMachine.context.data); // TypeScript knows data exists
613
+ * }
614
+ * ```
615
+ */
616
+ export function createContext<C extends object>(
617
+ context: C
618
+ ): { readonly context: C } {
619
+ return { context };
620
+ }
621
+
587
622
  /**
588
623
  * Creates a new machine by overriding or adding transition functions to an existing machine.
589
624
  * Ideal for mocking in tests or decorating functionality. The original machine is unchanged.
@@ -764,7 +799,8 @@ export function matchMachine<
764
799
 
765
800
  /**
766
801
  * Type-safe helper to assert that a machine's context has a specific discriminant value.
767
- * This narrows the type of the context based on the discriminant.
802
+ * This narrows the type of the context based on the discriminant, properly handling
803
+ * discriminated unions.
768
804
  *
769
805
  * @template M - The machine type.
770
806
  * @template K - The discriminant key.
@@ -775,8 +811,12 @@ export function matchMachine<
775
811
  * @returns True if the discriminant matches, with type narrowing.
776
812
  *
777
813
  * @example
778
- * if (hasState(machine, 'status', 'loading')) {
779
- * // machine.context.status is narrowed to 'loading'
814
+ * type Context = { status: 'idle' } | { status: 'loading' } | { status: 'success'; data: string };
815
+ * const machine = createMachine<Context>({ status: 'success', data: 'test' }, {});
816
+ *
817
+ * if (hasState(machine, 'status', 'success')) {
818
+ * // machine.context is narrowed to { status: 'success'; data: string }
819
+ * console.log(machine.context.data); // ✓ TypeScript knows about 'data'
780
820
  * }
781
821
  */
782
822
  export function hasState<
@@ -787,7 +827,7 @@ export function hasState<
787
827
  machine: M,
788
828
  key: K,
789
829
  value: V
790
- ): machine is M & { context: Context<M> & { [P in K]: V } } {
830
+ ): machine is M & { context: Extract<Context<M>, { [P in K]: V }> } {
791
831
  return machine.context[key] === value;
792
832
  }
793
833
 
@@ -1043,16 +1083,19 @@ export {
1043
1083
 
1044
1084
  export type {
1045
1085
  MachineConfig,
1046
- ExtractionConfig
1086
+ ExtractionConfig,
1087
+ ParallelRegionConfig,
1088
+ ChildStatesConfig
1047
1089
  } from './extract';
1048
1090
 
1091
+ // Note: Extraction functions (extractMachine, extractMachines, generateChart) are NOT exported
1092
+ // to keep them out of the runtime bundle. Use the CLI tool or import directly from the source
1093
+ // file for build-time statechart generation.
1049
1094
 
1050
1095
  export * from './multi'
1051
1096
 
1052
1097
  export * from './higher-order'
1053
1098
 
1054
- export * from './extract'
1055
-
1056
1099
  // =============================================================================
1057
1100
  // SECTION: MIDDLEWARE & INTERCEPTION
1058
1101
  // =============================================================================
@@ -1084,4 +1127,25 @@ export {
1084
1127
  createTransitionExtender,
1085
1128
  createFunctionalMachine,
1086
1129
  state
1087
- } from './functional-combinators';
1130
+ } from './functional-combinators';
1131
+
1132
+ // =============================================================================
1133
+ // SECTION: PATTERN MATCHING
1134
+ // =============================================================================
1135
+
1136
+ export {
1137
+ createMatcher,
1138
+ classCase,
1139
+ discriminantCase,
1140
+ customCase,
1141
+ forContext,
1142
+ type MatcherCase,
1143
+ type CasesToMapping,
1144
+ type MatcherUnion,
1145
+ type CaseNames,
1146
+ type CaseHandler,
1147
+ type ExhaustivenessMarker,
1148
+ type IsExhaustive,
1149
+ type WhenBuilder,
1150
+ type Matcher
1151
+ } from './matcher';