@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.
- package/README.md +174 -13
- package/dist/cjs/development/core.js.map +2 -2
- package/dist/cjs/development/extract.js +500 -0
- package/dist/cjs/development/extract.js.map +7 -0
- package/dist/cjs/development/index.js +135 -439
- package/dist/cjs/development/index.js.map +4 -4
- package/dist/cjs/production/core.js +1 -1
- package/dist/cjs/production/extract.js +5 -0
- package/dist/cjs/production/index.js +4 -5
- package/dist/esm/development/core.js.map +2 -2
- package/dist/esm/development/extract.js +486 -0
- package/dist/esm/development/extract.js.map +7 -0
- package/dist/esm/development/index.js +135 -446
- package/dist/esm/development/index.js.map +4 -4
- package/dist/esm/production/core.js +1 -1
- package/dist/esm/production/extract.js +5 -0
- package/dist/esm/production/index.js +4 -5
- package/dist/types/extract.d.ts +15 -1
- package/dist/types/extract.d.ts.map +1 -1
- package/dist/types/index.d.ts +58 -8
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/matcher.d.ts +318 -0
- package/dist/types/matcher.d.ts.map +1 -0
- package/package.json +13 -1
- package/src/extract.ts +61 -61
- package/src/generators.ts +6 -6
- package/src/index.ts +89 -10
- package/src/matcher.ts +544 -0
|
@@ -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.
|
|
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
|
-
|
|
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
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
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
|
|