@doeixd/machine 0.0.13 → 0.0.18
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 +77 -25
- package/dist/cjs/development/core.js +1852 -0
- package/dist/cjs/development/core.js.map +7 -0
- package/dist/cjs/development/index.js +1377 -1372
- package/dist/cjs/development/index.js.map +4 -4
- package/dist/cjs/production/core.js +1 -0
- package/dist/cjs/production/index.js +5 -5
- package/dist/esm/development/core.js +1829 -0
- package/dist/esm/development/core.js.map +7 -0
- package/dist/esm/development/index.js +1377 -1372
- package/dist/esm/development/index.js.map +4 -4
- package/dist/esm/production/core.js +1 -0
- package/dist/esm/production/index.js +5 -5
- package/dist/types/core.d.ts +18 -0
- package/dist/types/core.d.ts.map +1 -0
- package/dist/types/extract.d.ts +15 -1
- package/dist/types/extract.d.ts.map +1 -1
- package/dist/types/functional-combinators.d.ts +3 -5
- package/dist/types/functional-combinators.d.ts.map +1 -1
- package/dist/types/index.d.ts +254 -18
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/middleware/composition.d.ts +460 -0
- package/dist/types/middleware/composition.d.ts.map +1 -0
- package/dist/types/middleware/core.d.ts +196 -0
- package/dist/types/middleware/core.d.ts.map +1 -0
- package/dist/types/middleware/history.d.ts +54 -0
- package/dist/types/middleware/history.d.ts.map +1 -0
- package/dist/types/middleware/index.d.ts +10 -0
- package/dist/types/middleware/index.d.ts.map +1 -0
- package/dist/types/middleware/snapshot.d.ts +63 -0
- package/dist/types/middleware/snapshot.d.ts.map +1 -0
- package/dist/types/middleware/time-travel.d.ts +81 -0
- package/dist/types/middleware/time-travel.d.ts.map +1 -0
- package/package.json +19 -6
- package/src/core.ts +167 -0
- package/src/entry-react.ts +9 -0
- package/src/entry-solid.ts +9 -0
- package/src/extract.ts +61 -61
- package/src/functional-combinators.ts +3 -3
- package/src/generators.ts +6 -6
- package/src/index.ts +389 -101
- package/src/middleware/composition.ts +944 -0
- package/src/middleware/core.ts +573 -0
- package/src/middleware/history.ts +104 -0
- package/src/middleware/index.ts +13 -0
- package/src/middleware/snapshot.ts +153 -0
- package/src/middleware/time-travel.ts +236 -0
- package/src/middleware.ts +735 -1614
- package/src/prototype_functional.ts +46 -0
- package/src/reproduce_issue.ts +26 -0
- package/dist/types/middleware.d.ts +0 -1048
- package/dist/types/middleware.d.ts.map +0 -1
- package/dist/types/runtime-extract.d.ts +0 -53
- package/dist/types/runtime-extract.d.ts.map +0 -1
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
|
|
package/src/index.ts
CHANGED
|
@@ -15,34 +15,66 @@
|
|
|
15
15
|
export type MaybePromise<T> = T | Promise<T>;
|
|
16
16
|
|
|
17
17
|
/**
|
|
18
|
-
* The fundamental shape of
|
|
19
|
-
*
|
|
18
|
+
* The fundamental shape of a synchronous machine. This is a highly advanced
|
|
19
|
+
* generic type that performs two critical functions at compile time:
|
|
20
|
+
*
|
|
21
|
+
* 1. **Extraction:** It intelligently infers the pure transitions object from
|
|
22
|
+
* the flexible argument `A` (which can be a plain object, a factory
|
|
23
|
+
* function, or the augmented `this` from another transition).
|
|
24
|
+
*
|
|
25
|
+
* 2. **Filtering:** After extracting the transitions, it filters them, keeping
|
|
26
|
+
* only the functions that return a valid `Machine`.
|
|
27
|
+
*
|
|
28
|
+
* This makes the `Machine` type itself the single source of truth for what
|
|
29
|
+
* constitutes a valid, type-safe machine, enabling a remarkably clean and
|
|
30
|
+
* powerful API for `createMachine`.
|
|
31
|
+
*
|
|
32
|
+
* @template C The context object type.
|
|
33
|
+
* @template A The raw, flexible argument for transitions (object, factory, or `this`).
|
|
20
34
|
*/
|
|
21
|
-
export type Machine<
|
|
22
|
-
|
|
35
|
+
export type Machine<
|
|
36
|
+
C extends object,
|
|
37
|
+
T extends object = {}
|
|
38
|
+
> = {
|
|
23
39
|
readonly context: C;
|
|
24
|
-
} &
|
|
40
|
+
} & T;
|
|
25
41
|
|
|
26
42
|
/**
|
|
27
43
|
* The shape of an asynchronous machine, where transitions can return Promises.
|
|
28
44
|
* Async transitions receive an AbortSignal as the last parameter for cancellation support.
|
|
29
45
|
* @template C - The context object type.
|
|
30
46
|
*/
|
|
31
|
-
export type AsyncMachine<
|
|
32
|
-
|
|
47
|
+
export type AsyncMachine<
|
|
48
|
+
C extends object,
|
|
49
|
+
T extends object = {}
|
|
50
|
+
> = {
|
|
33
51
|
readonly context: C;
|
|
34
|
-
} &
|
|
52
|
+
} & T;
|
|
35
53
|
|
|
36
54
|
/**
|
|
37
55
|
* Utility type to extract the parameters of an async transition function,
|
|
38
56
|
* which includes TransitionOptions as the last parameter.
|
|
39
57
|
*/
|
|
40
|
-
export type AsyncTransitionArgs<M extends AsyncMachine<any>, K extends keyof M & string> =
|
|
41
|
-
M[K] extends (...
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
58
|
+
export type AsyncTransitionArgs<M extends AsyncMachine<any, any>, K extends keyof M & string> =
|
|
59
|
+
M[K] extends (...a: infer A) => any
|
|
60
|
+
? A extends [...infer Rest, TransitionOptions] ? Rest : A
|
|
61
|
+
: never;
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* A helper type to define a distinct state in a state machine (a "typestate").
|
|
65
|
+
* Allows defining the context and transitions in a single generic type.
|
|
66
|
+
* @template C - The context specific to this state.
|
|
67
|
+
* @template T - The transitions available in this state.
|
|
68
|
+
*/
|
|
69
|
+
export type TypeState<C extends object, T extends object = {}> = Machine<C, T>;
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* A helper type to define a distinct async state in a state machine.
|
|
73
|
+
* @template C - The context specific to this state.
|
|
74
|
+
* @template T - The transitions available in this state.
|
|
75
|
+
*/
|
|
76
|
+
export type AsyncTypeState<C extends object, T extends object = {}> = AsyncMachine<C, T>;
|
|
77
|
+
|
|
46
78
|
|
|
47
79
|
/**
|
|
48
80
|
* Options passed to async transition functions, including cancellation support.
|
|
@@ -57,6 +89,8 @@ export interface TransitionOptions {
|
|
|
57
89
|
// SECTION: TYPE UTILITIES & INTROSPECTION
|
|
58
90
|
// =============================================================================
|
|
59
91
|
|
|
92
|
+
|
|
93
|
+
|
|
60
94
|
/**
|
|
61
95
|
* Extracts the context type `C` from a machine type `M`.
|
|
62
96
|
* @template M - The machine type.
|
|
@@ -77,7 +111,6 @@ export type Transitions<M extends BaseMachine<any>> = Omit<M, "context">;
|
|
|
77
111
|
*/
|
|
78
112
|
export type TransitionArgs<M extends Machine<any>, K extends keyof M & string> =
|
|
79
113
|
M[K] extends (...args: infer A) => any ? A : never;
|
|
80
|
-
|
|
81
114
|
/**
|
|
82
115
|
* Extracts the names of all transitions as a string union type.
|
|
83
116
|
* @template M - The machine type.
|
|
@@ -103,10 +136,10 @@ export type BaseMachine<C extends object> = {
|
|
|
103
136
|
*/
|
|
104
137
|
export type DeepReadonly<T> = {
|
|
105
138
|
readonly [P in keyof T]: T[P] extends object
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
139
|
+
? T[P] extends (...args: any[]) => any
|
|
140
|
+
? T[P]
|
|
141
|
+
: DeepReadonly<T[P]>
|
|
142
|
+
: T[P];
|
|
110
143
|
};
|
|
111
144
|
|
|
112
145
|
/**
|
|
@@ -118,6 +151,10 @@ export type DeepReadonly<T> = {
|
|
|
118
151
|
*/
|
|
119
152
|
export type InferMachine<F extends (...args: any[]) => any> = ReturnType<F>;
|
|
120
153
|
|
|
154
|
+
|
|
155
|
+
export type EventFromTransitions<T extends Record<string, (...args: any[]) => any>> =
|
|
156
|
+
{ [K in keyof T & string]: { type: K; args: T[K] extends (...a: infer A) => any ? A : never } }[keyof T & string];
|
|
157
|
+
|
|
121
158
|
/**
|
|
122
159
|
* A discriminated union type representing an event that can be dispatched to a machine.
|
|
123
160
|
* This is automatically generated from a machine's type signature, ensuring full type safety.
|
|
@@ -128,11 +165,153 @@ export type InferMachine<F extends (...args: any[]) => any> = ReturnType<F>;
|
|
|
128
165
|
*/
|
|
129
166
|
export type Event<M extends BaseMachine<any>> = {
|
|
130
167
|
[K in keyof Omit<M, "context"> & string]: M[K] extends (...args: infer A) => any
|
|
131
|
-
|
|
132
|
-
|
|
168
|
+
? { type: K; args: A }
|
|
169
|
+
: never
|
|
133
170
|
}[keyof Omit<M, "context"> & string];
|
|
134
171
|
|
|
135
172
|
|
|
173
|
+
/**
|
|
174
|
+
* A helper type for use with TypeScript's `satisfies` operator to provide
|
|
175
|
+
* strong, immediate type-checking for standalone transition objects.
|
|
176
|
+
*
|
|
177
|
+
* This solves the "chicken-and-egg" problem where you need the final machine
|
|
178
|
+
* type to correctly type the transitions object, but you need the transitions
|
|
179
|
+
* object to create the machine. By forward-declaring the machine type and using
|
|
180
|
+
* `satisfies TransitionsFor<...>`, you get full IntelliSense and error-checking
|
|
181
|
+
* at the exact location of your transition definitions.
|
|
182
|
+
*
|
|
183
|
+
* @template C The context object type for the machine.
|
|
184
|
+
* @template T The literal type of the transitions object itself (`typeof myTransitions`).
|
|
185
|
+
*
|
|
186
|
+
* @example
|
|
187
|
+
* import { createMachine, Machine, TransitionsFor } from '@doeixd/machine';
|
|
188
|
+
*
|
|
189
|
+
* // 1. Define the context for your machine.
|
|
190
|
+
* type CounterContext = { count: number };
|
|
191
|
+
*
|
|
192
|
+
* // 2. Forward-declare the final machine type. This is the key step that
|
|
193
|
+
* // breaks the circular dependency for the type checker.
|
|
194
|
+
* type CounterMachine = Machine<CounterContext> & typeof counterTransitions;
|
|
195
|
+
*
|
|
196
|
+
* // 3. Define the transitions object, using `satisfies` to apply the helper type.
|
|
197
|
+
* // This provides immediate type-checking and full autocompletion for `this`.
|
|
198
|
+
* const counterTransitions = {
|
|
199
|
+
* increment() {
|
|
200
|
+
* // `this` is now fully typed!
|
|
201
|
+
* // IntelliSense knows `this.count` is a number and
|
|
202
|
+
* // `this.transitions.add` is a function.
|
|
203
|
+
* return createMachine({ count: this.count + 1 }, this.transitions);
|
|
204
|
+
* },
|
|
205
|
+
* add(n: number) {
|
|
206
|
+
* return createMachine({ count: this.count + n }, this.transitions);
|
|
207
|
+
* },
|
|
208
|
+
* // ❌ TypeScript will immediately throw a compile error on the next line
|
|
209
|
+
* // because the return type 'string' does not satisfy 'Machine<any>'.
|
|
210
|
+
* invalidTransition() {
|
|
211
|
+
* return "this is not a machine";
|
|
212
|
+
* }
|
|
213
|
+
* } satisfies TransitionsFor<CounterContext, typeof counterTransitions>;
|
|
214
|
+
*
|
|
215
|
+
* // 4. Create the machine instance. The `createMachine` call is now
|
|
216
|
+
* // guaranteed to be type-safe because `counterTransitions` has already
|
|
217
|
+
* // been validated.
|
|
218
|
+
* export function createCounter(initialCount = 0): CounterMachine {
|
|
219
|
+
* return createMachine({ count: initialCount }, counterTransitions);
|
|
220
|
+
* }
|
|
221
|
+
*/
|
|
222
|
+
export type TransitionsFor<C extends object, T extends Record<string, any>> = {
|
|
223
|
+
[K in keyof T]: (this: C & { transitions: T }, ...args: Parameters<T[K] extends (...a: infer A) => any ? (...a: A) => any : never>) => Machine<any, any>;
|
|
224
|
+
};
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* A helper type for use with the `satisfies` operator to provide strong
|
|
228
|
+
* type-checking for standalone asynchronous transition objects.
|
|
229
|
+
*/
|
|
230
|
+
export type AsyncTransitionsFor<C extends object, T extends Record<string, any>> = {
|
|
231
|
+
[K in keyof T]: (this: C & { transitions: T }, ...args: Parameters<T[K] extends (...a: infer A) => any ? (...a: A) => any : never>) => MaybePromise<AsyncMachine<any, any>>;
|
|
232
|
+
};
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* A mapped type that iterates over a transitions object `T` and keeps only the
|
|
236
|
+
* keys whose functions return a valid `Machine`. This provides a "self-correcting"
|
|
237
|
+
* type that prevents the definition of invalid transitions at compile time.
|
|
238
|
+
*
|
|
239
|
+
* It acts as a filter at the type level. When used in the return type of a
|
|
240
|
+
* function like `createMachine`, it ensures that the resulting machine object
|
|
241
|
+
* will not have any properties corresponding to functions that were defined
|
|
242
|
+
* with an incorrect return type. This provides immediate, precise feedback to
|
|
243
|
+
* the developer, making it impossible to create a machine with an invalid
|
|
244
|
+
* transition shape.
|
|
245
|
+
*
|
|
246
|
+
* @template T The raw transitions object type provided by the user.
|
|
247
|
+
*
|
|
248
|
+
* @example
|
|
249
|
+
* import { createMachine, Machine } from '@doeixd/machine';
|
|
250
|
+
*
|
|
251
|
+
* const machine = createMachine({ value: 'A' }, {
|
|
252
|
+
* // This is a valid transition because it returns a `Machine`.
|
|
253
|
+
* // The key 'goToB' will be PRESERVED in the final type.
|
|
254
|
+
* goToB() {
|
|
255
|
+
* return createMachine({ value: 'B' }, this.transitions);
|
|
256
|
+
* },
|
|
257
|
+
*
|
|
258
|
+
* // This is an INVALID transition because it returns a string.
|
|
259
|
+
* // The key 'invalid' will be OMITTED from the final type.
|
|
260
|
+
* invalid() {
|
|
261
|
+
* return "This is not a Machine object";
|
|
262
|
+
* },
|
|
263
|
+
*
|
|
264
|
+
* // This is also invalid as it's not a function.
|
|
265
|
+
* // The key 'alsoInvalid' will be OMITTED from the final type.
|
|
266
|
+
* alsoInvalid: 123
|
|
267
|
+
* });
|
|
268
|
+
*
|
|
269
|
+
* // --- USAGE ---
|
|
270
|
+
*
|
|
271
|
+
* // ✅ This call is valid and works as expected.
|
|
272
|
+
* const nextState = machine.goToB();
|
|
273
|
+
*
|
|
274
|
+
* // ❌ This line will cause a COMPILE-TIME a ERROR because the `FilterValidTransitions`
|
|
275
|
+
* // type has removed the 'invalid' key from the `machine`'s type signature.
|
|
276
|
+
* //
|
|
277
|
+
* // Error: Property 'invalid' does not exist on type
|
|
278
|
+
* // 'Machine<{ value: string; }> & { goToB: () => Machine<...>; }'.
|
|
279
|
+
* //
|
|
280
|
+
* machine.invalid();
|
|
281
|
+
*/
|
|
282
|
+
export type FilterValidTransitions<T> = {
|
|
283
|
+
[K in keyof T as T[K] extends (...args: any[]) => Machine<any> ? K : never]: T[K];
|
|
284
|
+
};
|
|
285
|
+
|
|
286
|
+
/**
|
|
287
|
+
* A conditional type that intelligently extracts the pure transitions object `T`
|
|
288
|
+
* from the flexible second argument of `createMachine`.
|
|
289
|
+
*
|
|
290
|
+
* It handles three cases:
|
|
291
|
+
* 1. If the argument is the augmented `this` context (`C & { transitions: T }`), it extracts `T`.
|
|
292
|
+
* 2. If the argument is a factory function `((ctx: C) => T)`, it infers and returns `T`.
|
|
293
|
+
* 3. If the argument is already the pure transitions object `T`, it returns it as is.
|
|
294
|
+
*/
|
|
295
|
+
export type ExtractTransitions<Arg, C extends object> = Arg extends (
|
|
296
|
+
...args: any[]
|
|
297
|
+
) => infer R
|
|
298
|
+
? R // Case 2: It's a factory function, extract the return type `R`.
|
|
299
|
+
: Arg extends C & { transitions: infer T }
|
|
300
|
+
? T // Case 1: It's the augmented `this` context, extract `T` from `transitions`.
|
|
301
|
+
: Arg; // Case 3: It's already the plain transitions object.
|
|
302
|
+
|
|
303
|
+
/** Keep only keys whose value is a function that returns a Machine. */
|
|
304
|
+
export type ValidTransitions<T> = {
|
|
305
|
+
[K in keyof T as T[K] extends (...a: any[]) => Machine<any, any> ? K : never]:
|
|
306
|
+
T[K] extends (...a: infer A) => Machine<infer C2, infer T2> ? (...a: A) => Machine<C2, T2> : never;
|
|
307
|
+
};
|
|
308
|
+
|
|
309
|
+
/** Same for async transitions (functions returning MaybePromise<AsyncMachine>). */
|
|
310
|
+
export type ValidAsyncTransitions<T> = {
|
|
311
|
+
[K in keyof T as T[K] extends (...a: any[]) => MaybePromise<AsyncMachine<any, any>> ? K : never]:
|
|
312
|
+
T[K] extends (...a: infer A) => MaybePromise<AsyncMachine<infer C2, infer T2>> ? (...a: A) => MaybePromise<AsyncMachine<C2, T2>> : never;
|
|
313
|
+
};
|
|
314
|
+
|
|
136
315
|
// =============================================================================
|
|
137
316
|
// SECTION: MACHINE CREATION (FUNCTIONAL & OOP)
|
|
138
317
|
// =============================================================================
|
|
@@ -146,18 +325,145 @@ export type Event<M extends BaseMachine<any>> = {
|
|
|
146
325
|
* @param fns - An object containing transition function definitions.
|
|
147
326
|
* @returns A new machine instance.
|
|
148
327
|
*/
|
|
328
|
+
/**
|
|
329
|
+
* Helper to transform transition functions to be bound (no 'this' requirement).
|
|
330
|
+
*/
|
|
331
|
+
export type BindTransitions<T> = {
|
|
332
|
+
[K in keyof T]: T[K] extends (this: any, ...args: infer A) => infer R
|
|
333
|
+
? (...args: A) => R
|
|
334
|
+
: T[K];
|
|
335
|
+
};
|
|
336
|
+
|
|
337
|
+
/**
|
|
338
|
+
* Creates a synchronous state machine from a context and a factory function.
|
|
339
|
+
* This "Functional Builder" pattern allows for type-safe transitions without
|
|
340
|
+
* manually passing `this` or `transitions`.
|
|
341
|
+
*
|
|
342
|
+
* @template C - The context object type.
|
|
343
|
+
* @template T - The transitions object type.
|
|
344
|
+
* @param context - The initial state context.
|
|
345
|
+
* @param factory - A function that receives a `transition` helper and returns the transitions object.
|
|
346
|
+
* @returns A new machine instance.
|
|
347
|
+
*/
|
|
348
|
+
export function createMachine<C extends object, T extends Record<string, (this: C, ...args: any[]) => any>>(
|
|
349
|
+
context: C,
|
|
350
|
+
factory: (transition: (newContext: C) => Machine<C, any>) => T
|
|
351
|
+
): Machine<C, BindTransitions<T>>;
|
|
352
|
+
|
|
353
|
+
/**
|
|
354
|
+
* Creates a synchronous state machine from a context and transition functions.
|
|
355
|
+
* This is the core factory for the functional approach.
|
|
356
|
+
*
|
|
357
|
+
* @template C - The context object type.
|
|
358
|
+
* @param context - The initial state context.
|
|
359
|
+
* @param fns - An object containing transition function definitions.
|
|
360
|
+
* @returns A new machine instance.
|
|
361
|
+
*/
|
|
362
|
+
export function createMachine<C extends object, T extends Record<string, (this: { context: C } & T, ...args: any[]) => any> & { context?: any }>(
|
|
363
|
+
context: C,
|
|
364
|
+
fns: T
|
|
365
|
+
): { context: C } & T;
|
|
366
|
+
|
|
367
|
+
/**
|
|
368
|
+
* Creates a synchronous state machine by copying context and transitions from an existing machine.
|
|
369
|
+
* This is useful for creating a new machine with updated context but the same transitions.
|
|
370
|
+
*
|
|
371
|
+
* @template C - The context object type.
|
|
372
|
+
* @template M - The machine type to copy transitions from.
|
|
373
|
+
* @param context - The new context.
|
|
374
|
+
* @param machine - The machine to copy transitions from.
|
|
375
|
+
* @returns A new machine instance with the given context and copied transitions.
|
|
376
|
+
*/
|
|
377
|
+
export function createMachine<C extends object, M extends BaseMachine<C>>(
|
|
378
|
+
context: C,
|
|
379
|
+
machine: M
|
|
380
|
+
): Machine<C, Transitions<M>>;
|
|
381
|
+
|
|
382
|
+
/**
|
|
383
|
+
* Creates a synchronous state machine from a context and transition functions that expect `this` to be the context object.
|
|
384
|
+
* This is used internally by utilities that need to bind transitions to context objects.
|
|
385
|
+
*
|
|
386
|
+
* @template C - The context object type.
|
|
387
|
+
* @param context - The initial state context.
|
|
388
|
+
* @param fns - An object containing transition function definitions that expect `this` to be the context.
|
|
389
|
+
* @returns A new machine instance.
|
|
390
|
+
*/
|
|
149
391
|
export function createMachine<C extends object, T extends Record<string, (this: C, ...args: any[]) => any>>(
|
|
150
392
|
context: C,
|
|
151
393
|
fns: T
|
|
152
|
-
):
|
|
394
|
+
): Machine<C, T>;
|
|
395
|
+
|
|
396
|
+
export function createMachine(context: any, fnsOrFactory: any): any {
|
|
397
|
+
if (typeof fnsOrFactory === 'function') {
|
|
398
|
+
let transitions: any;
|
|
399
|
+
const transition = (newContext: any) => {
|
|
400
|
+
const machine = createMachine(newContext, transitions);
|
|
401
|
+
// Re-bind transitions to the new context
|
|
402
|
+
const boundTransitions = Object.fromEntries(
|
|
403
|
+
Object.entries(transitions).map(([key, fn]) => [
|
|
404
|
+
key,
|
|
405
|
+
(fn as Function).bind(newContext)
|
|
406
|
+
])
|
|
407
|
+
);
|
|
408
|
+
return Object.assign(machine, boundTransitions);
|
|
409
|
+
};
|
|
410
|
+
transitions = fnsOrFactory(transition);
|
|
411
|
+
|
|
412
|
+
// Bind transitions to initial context
|
|
413
|
+
const boundTransitions = Object.fromEntries(
|
|
414
|
+
Object.entries(transitions).map(([key, fn]) => [
|
|
415
|
+
key,
|
|
416
|
+
(fn as Function).bind(context)
|
|
417
|
+
])
|
|
418
|
+
);
|
|
419
|
+
|
|
420
|
+
return Object.assign({ context }, boundTransitions);
|
|
421
|
+
}
|
|
422
|
+
|
|
153
423
|
// If fns is a machine (has context property), extract just the transition functions
|
|
154
|
-
const transitions = 'context' in
|
|
155
|
-
Object.entries(
|
|
156
|
-
) :
|
|
424
|
+
const transitions = 'context' in fnsOrFactory ? Object.fromEntries(
|
|
425
|
+
Object.entries(fnsOrFactory).filter(([key]) => key !== 'context')
|
|
426
|
+
) : fnsOrFactory;
|
|
427
|
+
|
|
428
|
+
// For normal object transitions, we might also need binding if they use `this`
|
|
429
|
+
// But existing code expects `this` to be the machine (context + transitions).
|
|
430
|
+
// The new API expects `this` to be just context.
|
|
431
|
+
|
|
157
432
|
const machine = Object.assign({ context }, transitions);
|
|
158
|
-
return machine
|
|
433
|
+
return machine;
|
|
159
434
|
}
|
|
160
435
|
|
|
436
|
+
/**
|
|
437
|
+
* Creates an asynchronous state machine from a context and a factory function.
|
|
438
|
+
* This "Functional Builder" pattern allows for type-safe transitions without
|
|
439
|
+
* manually passing `this` or `transitions`.
|
|
440
|
+
*
|
|
441
|
+
* @template C - The context object type.
|
|
442
|
+
* @template T - The transitions object type.
|
|
443
|
+
* @param context - The initial state context.
|
|
444
|
+
* @param factory - A function that receives a `transition` helper and returns the transitions object.
|
|
445
|
+
* @returns A new async machine instance.
|
|
446
|
+
*/
|
|
447
|
+
export function createAsyncMachine<C extends object, T extends Record<string, (this: C, ...args: any[]) => any>>(
|
|
448
|
+
context: C,
|
|
449
|
+
factory: (transition: (newContext: C) => AsyncMachine<C, T>) => T
|
|
450
|
+
): AsyncMachine<C, BindTransitions<T>>;
|
|
451
|
+
|
|
452
|
+
/**
|
|
453
|
+
* Creates an asynchronous state machine by copying context and transitions from an existing machine.
|
|
454
|
+
* This is useful for creating a new machine with updated context but the same transitions.
|
|
455
|
+
*
|
|
456
|
+
* @template C - The context object type.
|
|
457
|
+
* @template M - The machine type to copy transitions from.
|
|
458
|
+
* @param context - The new context.
|
|
459
|
+
* @param machine - The machine to copy transitions from.
|
|
460
|
+
* @returns A new async machine instance with the given context and copied transitions.
|
|
461
|
+
*/
|
|
462
|
+
export function createAsyncMachine<C extends object, M extends BaseMachine<C>>(
|
|
463
|
+
context: C,
|
|
464
|
+
machine: M
|
|
465
|
+
): AsyncMachine<C, Transitions<M>>;
|
|
466
|
+
|
|
161
467
|
/**
|
|
162
468
|
* Creates an asynchronous state machine from a context and async transition functions.
|
|
163
469
|
*
|
|
@@ -169,8 +475,42 @@ export function createMachine<C extends object, T extends Record<string, (this:
|
|
|
169
475
|
export function createAsyncMachine<C extends object, T extends Record<string, (this: C, ...args: any[]) => any>>(
|
|
170
476
|
context: C,
|
|
171
477
|
fns: T
|
|
172
|
-
):
|
|
173
|
-
|
|
478
|
+
): AsyncMachine<C, T>;
|
|
479
|
+
|
|
480
|
+
export function createAsyncMachine(context: any, fnsOrFactory: any): any {
|
|
481
|
+
if (typeof fnsOrFactory === 'function') {
|
|
482
|
+
let transitions: any;
|
|
483
|
+
const transition = (newContext: any) => {
|
|
484
|
+
const machine = createAsyncMachine(newContext, transitions);
|
|
485
|
+
// Re-bind transitions to the new context
|
|
486
|
+
const boundTransitions = Object.fromEntries(
|
|
487
|
+
Object.entries(transitions).map(([key, fn]) => [
|
|
488
|
+
key,
|
|
489
|
+
(fn as Function).bind(newContext)
|
|
490
|
+
])
|
|
491
|
+
);
|
|
492
|
+
return Object.assign(machine, boundTransitions);
|
|
493
|
+
};
|
|
494
|
+
transitions = fnsOrFactory(transition);
|
|
495
|
+
|
|
496
|
+
// Bind transitions to initial context
|
|
497
|
+
const boundTransitions = Object.fromEntries(
|
|
498
|
+
Object.entries(transitions).map(([key, fn]) => [
|
|
499
|
+
key,
|
|
500
|
+
(fn as Function).bind(context)
|
|
501
|
+
])
|
|
502
|
+
);
|
|
503
|
+
|
|
504
|
+
return Object.assign({ context }, boundTransitions);
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
// If fns is a machine (has context property), extract just the transition functions
|
|
508
|
+
const transitions = 'context' in fnsOrFactory ? Object.fromEntries(
|
|
509
|
+
Object.entries(fnsOrFactory).filter(([key]) => key !== 'context')
|
|
510
|
+
) : fnsOrFactory;
|
|
511
|
+
|
|
512
|
+
const machine = Object.assign({ context }, transitions);
|
|
513
|
+
return machine;
|
|
174
514
|
}
|
|
175
515
|
|
|
176
516
|
/**
|
|
@@ -196,16 +536,16 @@ export function createMachineFactory<C extends object>() {
|
|
|
196
536
|
) => {
|
|
197
537
|
type MachineFns = {
|
|
198
538
|
[K in keyof T]: (
|
|
199
|
-
this: C
|
|
539
|
+
this: Machine<C>,
|
|
200
540
|
...args: T[K] extends (ctx: C, ...args: infer A) => C ? A : never
|
|
201
|
-
) => Machine<C
|
|
541
|
+
) => MaybePromise<Machine<C>>;
|
|
202
542
|
};
|
|
203
543
|
|
|
204
544
|
const fns = Object.fromEntries(
|
|
205
545
|
Object.entries(transformers).map(([key, transform]) => [
|
|
206
546
|
key,
|
|
207
|
-
function (this: C
|
|
208
|
-
const newContext = (transform as any)(this, ...args);
|
|
547
|
+
function (this: Machine<C>, ...args: any[]) {
|
|
548
|
+
const newContext = (transform as any)(this.context, ...args);
|
|
209
549
|
return createMachine(newContext, fns as any);
|
|
210
550
|
},
|
|
211
551
|
])
|
|
@@ -241,7 +581,7 @@ export function setContext<M extends Machine<any>>(
|
|
|
241
581
|
? (newContextOrFn as (ctx: Readonly<Context<M>>) => Context<M>)(context)
|
|
242
582
|
: newContextOrFn;
|
|
243
583
|
|
|
244
|
-
return createMachine(newContext, transitions) as M;
|
|
584
|
+
return createMachine(newContext, transitions as any) as M;
|
|
245
585
|
}
|
|
246
586
|
|
|
247
587
|
/**
|
|
@@ -263,7 +603,7 @@ export function overrideTransitions<
|
|
|
263
603
|
): Machine<Context<M>> & Omit<Transitions<M>, keyof T> & T {
|
|
264
604
|
const { context, ...originalTransitions } = machine;
|
|
265
605
|
const newTransitions = { ...originalTransitions, ...overrides };
|
|
266
|
-
return createMachine(context, newTransitions) as any;
|
|
606
|
+
return createMachine(context, newTransitions as any) as any;
|
|
267
607
|
}
|
|
268
608
|
|
|
269
609
|
/**
|
|
@@ -285,7 +625,7 @@ export function extendTransitions<
|
|
|
285
625
|
>(machine: M, newTransitions: T): M & T {
|
|
286
626
|
const { context, ...originalTransitions } = machine;
|
|
287
627
|
const combinedTransitions = { ...originalTransitions, ...newTransitions };
|
|
288
|
-
return createMachine(context, combinedTransitions) as M & T;
|
|
628
|
+
return createMachine(context, combinedTransitions as any) as M & T;
|
|
289
629
|
}
|
|
290
630
|
|
|
291
631
|
/**
|
|
@@ -339,8 +679,8 @@ export function combineFactories<
|
|
|
339
679
|
): (
|
|
340
680
|
...args: Parameters<F1>
|
|
341
681
|
) => Machine<Context<ReturnType<F1>> & Context<ReturnType<F2>>> &
|
|
342
|
-
|
|
343
|
-
|
|
682
|
+
Omit<ReturnType<F1>, 'context'> &
|
|
683
|
+
Omit<ReturnType<F2>, 'context'> {
|
|
344
684
|
return (...args: Parameters<F1>) => {
|
|
345
685
|
// Create instances from both factories
|
|
346
686
|
const machine1 = factory1(...args);
|
|
@@ -357,7 +697,7 @@ export function combineFactories<
|
|
|
357
697
|
const combinedTransitions = { ...transitions1, ...transitions2 };
|
|
358
698
|
|
|
359
699
|
// Create the combined machine
|
|
360
|
-
return createMachine(combinedContext, combinedTransitions) as any;
|
|
700
|
+
return createMachine(combinedContext, combinedTransitions as any) as any;
|
|
361
701
|
};
|
|
362
702
|
}
|
|
363
703
|
|
|
@@ -375,7 +715,7 @@ export function createMachineBuilder<M extends Machine<any>>(
|
|
|
375
715
|
): (context: Context<M>) => M {
|
|
376
716
|
const { context, ...transitions } = templateMachine;
|
|
377
717
|
return (newContext: Context<M>): M => {
|
|
378
|
-
return createMachine(newContext, transitions) as M;
|
|
718
|
+
return createMachine(newContext, transitions as any) as M;
|
|
379
719
|
};
|
|
380
720
|
}
|
|
381
721
|
|
|
@@ -607,7 +947,7 @@ export function next<C extends object>(
|
|
|
607
947
|
update: (ctx: Readonly<C>) => C
|
|
608
948
|
): Machine<C> {
|
|
609
949
|
const { context, ...transitions } = m;
|
|
610
|
-
return createMachine(update(context), transitions) as Machine<C>;
|
|
950
|
+
return createMachine(update(context), transitions as any) as Machine<C>;
|
|
611
951
|
}
|
|
612
952
|
|
|
613
953
|
/**
|
|
@@ -694,30 +1034,17 @@ export {
|
|
|
694
1034
|
} from './primitives';
|
|
695
1035
|
|
|
696
1036
|
// =============================================================================
|
|
697
|
-
// SECTION: STATECHART EXTRACTION
|
|
1037
|
+
// SECTION: STATECHART EXTRACTION (Build-time only)
|
|
698
1038
|
// =============================================================================
|
|
699
1039
|
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
generateChart,
|
|
704
|
-
type MachineConfig,
|
|
705
|
-
type ExtractionConfig
|
|
706
|
-
} from './extract';
|
|
707
|
-
|
|
708
|
-
// =============================================================================
|
|
709
|
-
// SECTION: RUNTIME EXTRACTION
|
|
710
|
-
// =============================================================================
|
|
1040
|
+
// Note: Extraction tools are available as dev dependencies for build-time use
|
|
1041
|
+
// They are not included in the runtime bundle for size optimization
|
|
1042
|
+
// Use: npx tsx scripts/extract-statechart.ts
|
|
711
1043
|
|
|
712
|
-
export {
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
extractFromInstance
|
|
717
|
-
} from './runtime-extract';
|
|
718
|
-
|
|
719
|
-
// Export runtime metadata symbol and type (for advanced use)
|
|
720
|
-
export { RUNTIME_META, type RuntimeTransitionMeta } from './primitives';
|
|
1044
|
+
export type {
|
|
1045
|
+
MachineConfig,
|
|
1046
|
+
ExtractionConfig
|
|
1047
|
+
} from './extract';
|
|
721
1048
|
|
|
722
1049
|
|
|
723
1050
|
export * from './multi'
|
|
@@ -730,46 +1057,7 @@ export * from './extract'
|
|
|
730
1057
|
// SECTION: MIDDLEWARE & INTERCEPTION
|
|
731
1058
|
// =============================================================================
|
|
732
1059
|
|
|
733
|
-
export
|
|
734
|
-
createMiddleware,
|
|
735
|
-
withLogging,
|
|
736
|
-
withAnalytics,
|
|
737
|
-
withValidation,
|
|
738
|
-
withPermissions,
|
|
739
|
-
withErrorReporting,
|
|
740
|
-
withPerformanceMonitoring,
|
|
741
|
-
withRetry,
|
|
742
|
-
withHistory,
|
|
743
|
-
withSnapshot,
|
|
744
|
-
withTimeTravel,
|
|
745
|
-
compose,
|
|
746
|
-
composeTyped,
|
|
747
|
-
createPipeline,
|
|
748
|
-
createMiddlewareRegistry,
|
|
749
|
-
when,
|
|
750
|
-
inDevelopment,
|
|
751
|
-
whenContext,
|
|
752
|
-
combine,
|
|
753
|
-
branch,
|
|
754
|
-
isMiddlewareFn,
|
|
755
|
-
isConditionalMiddleware,
|
|
756
|
-
createCustomMiddleware,
|
|
757
|
-
type MiddlewareHooks,
|
|
758
|
-
type MiddlewareOptions,
|
|
759
|
-
type MiddlewareContext,
|
|
760
|
-
type MiddlewareResult,
|
|
761
|
-
type MiddlewareError,
|
|
762
|
-
type HistoryEntry,
|
|
763
|
-
type ContextSnapshot,
|
|
764
|
-
type Serializer,
|
|
765
|
-
type MiddlewareFn,
|
|
766
|
-
type ConditionalMiddleware,
|
|
767
|
-
type NamedMiddleware,
|
|
768
|
-
type PipelineConfig,
|
|
769
|
-
type PipelineResult,
|
|
770
|
-
chain,
|
|
771
|
-
withDebugging
|
|
772
|
-
} from './middleware';
|
|
1060
|
+
export * from './middleware/index';
|
|
773
1061
|
|
|
774
1062
|
// =============================================================================
|
|
775
1063
|
// SECTION: UTILITIES & HELPERS
|