@doeixd/machine 0.0.17 → 0.0.19

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,318 @@
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 HandledMachines<Handlers extends readonly any[]> = Handlers extends readonly [infer H, ...infer Rest] ? (H extends CaseHandler<any, infer M, any> ? M : never) | HandledMachines<Rest> : never;
89
+ /**
90
+ * Checks if all machine types in Union have been handled.
91
+ * Returns true if exhaustive, otherwise returns an error type with missing cases.
92
+ */
93
+ export type IsExhaustive<Union, Handled> = Exclude<Union, Handled> extends never ? true : {
94
+ readonly __error: 'Non-exhaustive match - missing cases';
95
+ readonly __missing: Exclude<Union, Handled>;
96
+ };
97
+ /**
98
+ * Pattern matching builder returned by matcher.when().
99
+ */
100
+ export interface WhenBuilder<_Cases extends readonly MatcherCase<any, any, any>[], M> {
101
+ /**
102
+ * Execute pattern matching with exhaustiveness checking.
103
+ *
104
+ * @template R - The return type of all handlers
105
+ * @param handlers - Array of case handlers followed by exhaustiveness marker
106
+ * @returns The result of the matched handler, or compile error if not exhaustive
107
+ *
108
+ * @example
109
+ * ```typescript
110
+ * match.when(machine).is<string>(
111
+ * match.case.idle(() => 'idle'),
112
+ * match.case.loading(() => 'loading'),
113
+ * match.exhaustive
114
+ * );
115
+ * ```
116
+ */
117
+ is<R>(...handlers: [...any[], ExhaustivenessMarker]): IsExhaustive<M, HandledMachines<typeof handlers>> extends true ? R : IsExhaustive<M, HandledMachines<typeof handlers>>;
118
+ }
119
+ /**
120
+ * The main Matcher interface with three APIs.
121
+ */
122
+ export interface Matcher<Cases extends readonly MatcherCase<any, any, any>[]> {
123
+ /**
124
+ * API 1: Type guard access via dynamic properties.
125
+ *
126
+ * @example
127
+ * ```typescript
128
+ * if (match.is.loading(machine)) {
129
+ * // machine is narrowed to LoadingMachine
130
+ * }
131
+ * ```
132
+ */
133
+ readonly is: {
134
+ [Name in CaseNames<Cases>]: <M>(machine: M) => machine is Extract<M, CasesToMapping<Cases>[Name]>;
135
+ };
136
+ /**
137
+ * API 2a: Pattern matching builder.
138
+ *
139
+ * @example
140
+ * ```typescript
141
+ * match.when(machine).is<string>(
142
+ * match.case.idle(() => 'idle'),
143
+ * match.case.loading(() => 'loading'),
144
+ * match.exhaustive
145
+ * );
146
+ * ```
147
+ */
148
+ when: <M>(machine: M) => WhenBuilder<Cases, M>;
149
+ /**
150
+ * API 2b: Case handler creator for pattern matching.
151
+ *
152
+ * @example
153
+ * ```typescript
154
+ * match.case.loading((m) => `Loading: ${m.context.startTime}`)
155
+ * ```
156
+ */
157
+ readonly case: {
158
+ [Name in CaseNames<Cases>]: <R>(handler: (machine: CasesToMapping<Cases>[Name]) => R) => CaseHandler<Name, CasesToMapping<Cases>[Name], R>;
159
+ };
160
+ /**
161
+ * API 2c: Exhaustiveness marker for pattern matching.
162
+ */
163
+ readonly exhaustive: ExhaustivenessMarker;
164
+ /**
165
+ * API 3: Simple match - returns the name of the matched case or null.
166
+ *
167
+ * @example
168
+ * ```typescript
169
+ * const name = match(machine); // 'idle' | 'loading' | 'success' | null
170
+ * ```
171
+ */
172
+ <M>(machine: M): M extends MatcherUnion<Cases> ? CaseNames<Cases> | null : null;
173
+ }
174
+ /**
175
+ * Creates a type-safe matcher for discriminating between machine types.
176
+ *
177
+ * @template Cases - Tuple of [name, MachineType, predicate] configurations
178
+ * @param cases - Array of matcher case definitions
179
+ * @returns A matcher object with three APIs: is (type guards), when (pattern matching), and direct call (simple match)
180
+ *
181
+ * @example
182
+ * ```typescript
183
+ * // Class-based matching
184
+ * const match = createMatcher(
185
+ * ['idle', IdleMachine, (m): m is IdleMachine => m instanceof IdleMachine],
186
+ * ['loading', LoadingMachine, (m): m is LoadingMachine => m instanceof LoadingMachine]
187
+ * );
188
+ *
189
+ * // Or use helper functions
190
+ * const match = createMatcher(
191
+ * classCase('idle', IdleMachine),
192
+ * classCase('loading', LoadingMachine)
193
+ * );
194
+ * ```
195
+ */
196
+ export declare function createMatcher<const Cases extends readonly MatcherCase<string, any, (m: any) => m is any>[]>(...cases: Cases): Matcher<Cases>;
197
+ /**
198
+ * Creates a class-based matcher case using instanceof checking.
199
+ * This is the most common pattern for Type-State machines.
200
+ *
201
+ * @template Name - The unique name for this case
202
+ * @template T - The class constructor
203
+ * @param name - The name to use for this case
204
+ * @param machineClass - The class to check with instanceof
205
+ * @returns A matcher case tuple
206
+ *
207
+ * @example
208
+ * ```typescript
209
+ * const match = createMatcher(
210
+ * classCase('idle', IdleMachine),
211
+ * classCase('loading', LoadingMachine),
212
+ * classCase('success', SuccessMachine)
213
+ * );
214
+ * ```
215
+ */
216
+ 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>>;
217
+ /**
218
+ * Creates a discriminated union matcher case based on a context property.
219
+ * This integrates with the existing hasState utility for context-based discrimination.
220
+ *
221
+ * @template Name - The unique name for this case
222
+ * @template M - The machine type (use Machine<YourContextUnion> for proper narrowing)
223
+ * @template K - The context key to check
224
+ * @template V - The value to match
225
+ * @param name - The name to use for this case
226
+ * @param key - The context property to check
227
+ * @param value - The value the property should equal
228
+ * @returns A matcher case tuple
229
+ *
230
+ * @example
231
+ * ```typescript
232
+ * type FetchContext =
233
+ * | { status: 'idle' }
234
+ * | { status: 'loading' }
235
+ * | { status: 'success'; data: string };
236
+ *
237
+ * const match = createMatcher(
238
+ * discriminantCase<'idle', Machine<FetchContext>, 'status', 'idle'>('idle', 'status', 'idle'),
239
+ * discriminantCase<'loading', Machine<FetchContext>, 'status', 'loading'>('loading', 'status', 'loading'),
240
+ * discriminantCase<'success', Machine<FetchContext>, 'status', 'success'>('success', 'status', 'success')
241
+ * );
242
+ * ```
243
+ */
244
+ 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 & {
245
+ context: Extract<Context<M>, {
246
+ [P in K]: V;
247
+ }>;
248
+ }, (m: M) => m is M & {
249
+ context: Extract<Context<M>, {
250
+ [P in K]: V;
251
+ }>;
252
+ }>;
253
+ /**
254
+ * Creates a custom matcher case with a user-defined predicate.
255
+ * For advanced matching logic beyond instanceof or discriminants.
256
+ *
257
+ * @template Name - The unique name for this case
258
+ * @template M - The machine type this case matches (inferred from predicate)
259
+ * @param name - The name to use for this case
260
+ * @param predicate - A type guard function that determines if a machine matches
261
+ * @returns A matcher case tuple
262
+ *
263
+ * @example
264
+ * ```typescript
265
+ * const match = createMatcher(
266
+ * customCase('complex', (m): m is ComplexMachine => {
267
+ * return m.context.value > 10 && m.context.status === 'active';
268
+ * })
269
+ * );
270
+ * ```
271
+ */
272
+ 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>;
273
+ /**
274
+ * Creates a discriminated matcher builder for a specific context union type.
275
+ * Provides better type inference by capturing the context type upfront.
276
+ *
277
+ * @template C - The discriminated union context type
278
+ * @returns A builder object with a `case` method for defining cases with less boilerplate
279
+ *
280
+ * @example
281
+ * ```typescript
282
+ * type FetchContext =
283
+ * | { status: 'idle' }
284
+ * | { status: 'loading'; startTime: number }
285
+ * | { status: 'success'; data: string };
286
+ *
287
+ * const builder = forContext<FetchContext>();
288
+ *
289
+ * const match = createMatcher(
290
+ * builder.case('idle', 'status', 'idle'),
291
+ * builder.case('loading', 'status', 'loading'),
292
+ * builder.case('success', 'status', 'success')
293
+ * );
294
+ *
295
+ * // Full type inference and narrowing works!
296
+ * if (match.is.success(machine)) {
297
+ * console.log(machine.context.data); // ✓ TypeScript knows data exists
298
+ * }
299
+ * ```
300
+ */
301
+ export declare function forContext<C extends object>(): {
302
+ /**
303
+ * Creates a discriminated union case with full type inference.
304
+ */
305
+ case<Name extends string, K extends keyof C, V extends C[K]>(name: Name, key: K, value: V): MatcherCase<Name, {
306
+ readonly context: C;
307
+ } & {
308
+ context: Extract<C, { [P in K]: V; }>;
309
+ }, (m: {
310
+ readonly context: C;
311
+ }) => m is {
312
+ readonly context: C;
313
+ } & {
314
+ context: Extract<C, { [P in K]: V; }>;
315
+ }>;
316
+ };
317
+ export {};
318
+ //# 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.17",
3
+ "version": "0.0.19",
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/extract.ts CHANGED
@@ -106,8 +106,8 @@ export interface ExtractionConfig {
106
106
  * @returns A JSON-compatible value (string, number, object, array).
107
107
  * @internal
108
108
  */
109
- // @ts-expect-error - verbose parameter is used but TypeScript doesn't detect it
110
- function _typeToJson(type: Type, verbose = false): any {
109
+
110
+ export function _typeToJson(type: Type, verbose = false): any {
111
111
  // --- Terminal Types ---
112
112
  const symbol = type.getSymbol();
113
113
  if (symbol && symbol.getDeclarations().some(Node.isClassDeclaration)) {
@@ -324,65 +324,65 @@ function extractFromCallExpression(call: Node, verbose = false): any | null {
324
324
  break;
325
325
 
326
326
  case 'action':
327
- // Args: (action, transition)
328
- if (args[0]) {
329
- const actionMeta = parseObjectLiteral(args[0]);
330
- if (Object.keys(actionMeta).length > 0) {
331
- metadata.actions = [actionMeta];
332
- }
333
- }
334
- // Recurse into wrapped transition
335
- if (args[1] && Node.isCallExpression(args[1])) {
336
- const nested = extractFromCallExpression(args[1], verbose);
337
- if (nested) {
338
- Object.assign(metadata, nested);
339
- }
340
- }
341
- break;
342
-
343
- case 'guard':
344
- // Args: (condition, transition, options?)
345
- // Extract description from options object (third argument)
346
- if (args[2]) {
347
- const options = parseObjectLiteral(args[2]);
348
- if (options.description) {
349
- metadata.description = options.description;
350
- }
351
- }
352
- // Add a generic guard condition for static analysis
353
- metadata.guards = [{ name: 'runtime_guard', description: metadata.description || 'Synchronous condition check' }];
354
- // Recurse into the transition (second argument)
355
- if (args[1] && Node.isCallExpression(args[1])) {
356
- const nested = extractFromCallExpression(args[1], verbose);
357
- if (nested) {
358
- Object.assign(metadata, nested);
359
- }
360
- }
361
- break;
362
-
363
- case 'guardAsync':
364
- // Args: (condition, transition, options?)
365
- // Extract description from options object (third argument)
366
- if (args[2]) {
367
- const options = parseObjectLiteral(args[2]);
368
- if (options.description) {
369
- metadata.description = options.description;
370
- }
371
- }
372
- // Add a generic guard condition for static analysis
373
- metadata.guards = [{ name: 'runtime_guard_async', description: metadata.description || 'Asynchronous condition check' }];
374
- // Recurse into the transition (second argument)
375
- if (args[1] && Node.isCallExpression(args[1])) {
376
- const nested = extractFromCallExpression(args[1], verbose);
377
- if (nested) {
378
- Object.assign(metadata, nested);
379
- }
380
- }
381
- break;
382
-
383
- default:
384
- // Not a DSL primitive we recognize
385
- return null;
327
+ // Args: (action, transition)
328
+ if (args[0]) {
329
+ const actionMeta = parseObjectLiteral(args[0]);
330
+ if (Object.keys(actionMeta).length > 0) {
331
+ metadata.actions = [actionMeta];
332
+ }
333
+ }
334
+ // Recurse into wrapped transition
335
+ if (args[1] && Node.isCallExpression(args[1])) {
336
+ const nested = extractFromCallExpression(args[1], verbose);
337
+ if (nested) {
338
+ Object.assign(metadata, nested);
339
+ }
340
+ }
341
+ break;
342
+
343
+ case 'guard':
344
+ // Args: (condition, transition, options?)
345
+ // Extract description from options object (third argument)
346
+ if (args[2]) {
347
+ const options = parseObjectLiteral(args[2]);
348
+ if (options.description) {
349
+ metadata.description = options.description;
350
+ }
351
+ }
352
+ // Add a generic guard condition for static analysis
353
+ metadata.guards = [{ name: 'runtime_guard', description: metadata.description || 'Synchronous condition check' }];
354
+ // Recurse into the transition (second argument)
355
+ if (args[1] && Node.isCallExpression(args[1])) {
356
+ const nested = extractFromCallExpression(args[1], verbose);
357
+ if (nested) {
358
+ Object.assign(metadata, nested);
359
+ }
360
+ }
361
+ break;
362
+
363
+ case 'guardAsync':
364
+ // Args: (condition, transition, options?)
365
+ // Extract description from options object (third argument)
366
+ if (args[2]) {
367
+ const options = parseObjectLiteral(args[2]);
368
+ if (options.description) {
369
+ metadata.description = options.description;
370
+ }
371
+ }
372
+ // Add a generic guard condition for static analysis
373
+ metadata.guards = [{ name: 'runtime_guard_async', description: metadata.description || 'Asynchronous condition check' }];
374
+ // Recurse into the transition (second argument)
375
+ if (args[1] && Node.isCallExpression(args[1])) {
376
+ const nested = extractFromCallExpression(args[1], verbose);
377
+ if (nested) {
378
+ Object.assign(metadata, nested);
379
+ }
380
+ }
381
+ break;
382
+
383
+ default:
384
+ // Not a DSL primitive we recognize
385
+ return null;
386
386
  }
387
387
 
388
388
  return Object.keys(metadata).length > 0 ? metadata : null;
package/src/generators.ts CHANGED
@@ -135,12 +135,12 @@ export function run<C extends any = any, M extends { context: C } & Record<strin
135
135
 
136
136
  // If the generator has returned (done), we have our final value
137
137
  if (done) {
138
- return value;
138
+ return value as T;
139
139
  }
140
140
 
141
141
  // Otherwise, the yielded value becomes our new current state
142
142
  // This state will be sent back into the generator on the next iteration
143
- current = value;
143
+ current = value as M;
144
144
  }
145
145
  }
146
146
 
@@ -346,10 +346,10 @@ export function runWithDebug<C extends any = any, M extends { context: C } & Rec
346
346
 
347
347
  if (done) {
348
348
  console.log('Final:', value);
349
- return value;
349
+ return value as T;
350
350
  }
351
351
 
352
- current = value;
352
+ current = value as M;
353
353
  stepCount++;
354
354
  logger(stepCount, current);
355
355
  }
@@ -391,10 +391,10 @@ export async function runAsync<C extends any = any, M extends { context: C } & R
391
391
  const { value, done } = await generator.next(current);
392
392
 
393
393
  if (done) {
394
- return value;
394
+ return value as T;
395
395
  }
396
396
 
397
- current = value;
397
+ current = value as M;
398
398
  }
399
399
  }
400
400