@doeixd/machine 0.0.13 → 0.0.17
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 +67 -15
- package/dist/cjs/development/core.js +1852 -0
- package/dist/cjs/development/core.js.map +7 -0
- package/dist/cjs/development/index.js +1341 -1374
- 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 +1341 -1374
- 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/functional-combinators.d.ts +3 -5
- package/dist/types/functional-combinators.d.ts.map +1 -1
- package/dist/types/index.d.ts +241 -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/functional-combinators.ts +3 -3
- package/src/index.ts +374 -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/index.ts
CHANGED
|
@@ -15,34 +15,51 @@
|
|
|
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
|
-
: never;
|
|
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
|
+
|
|
46
63
|
|
|
47
64
|
/**
|
|
48
65
|
* Options passed to async transition functions, including cancellation support.
|
|
@@ -57,6 +74,8 @@ export interface TransitionOptions {
|
|
|
57
74
|
// SECTION: TYPE UTILITIES & INTROSPECTION
|
|
58
75
|
// =============================================================================
|
|
59
76
|
|
|
77
|
+
|
|
78
|
+
|
|
60
79
|
/**
|
|
61
80
|
* Extracts the context type `C` from a machine type `M`.
|
|
62
81
|
* @template M - The machine type.
|
|
@@ -77,7 +96,6 @@ export type Transitions<M extends BaseMachine<any>> = Omit<M, "context">;
|
|
|
77
96
|
*/
|
|
78
97
|
export type TransitionArgs<M extends Machine<any>, K extends keyof M & string> =
|
|
79
98
|
M[K] extends (...args: infer A) => any ? A : never;
|
|
80
|
-
|
|
81
99
|
/**
|
|
82
100
|
* Extracts the names of all transitions as a string union type.
|
|
83
101
|
* @template M - The machine type.
|
|
@@ -103,10 +121,10 @@ export type BaseMachine<C extends object> = {
|
|
|
103
121
|
*/
|
|
104
122
|
export type DeepReadonly<T> = {
|
|
105
123
|
readonly [P in keyof T]: T[P] extends object
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
124
|
+
? T[P] extends (...args: any[]) => any
|
|
125
|
+
? T[P]
|
|
126
|
+
: DeepReadonly<T[P]>
|
|
127
|
+
: T[P];
|
|
110
128
|
};
|
|
111
129
|
|
|
112
130
|
/**
|
|
@@ -118,6 +136,10 @@ export type DeepReadonly<T> = {
|
|
|
118
136
|
*/
|
|
119
137
|
export type InferMachine<F extends (...args: any[]) => any> = ReturnType<F>;
|
|
120
138
|
|
|
139
|
+
|
|
140
|
+
export type EventFromTransitions<T extends Record<string, (...args: any[]) => any>> =
|
|
141
|
+
{ [K in keyof T & string]: { type: K; args: T[K] extends (...a: infer A) => any ? A : never } }[keyof T & string];
|
|
142
|
+
|
|
121
143
|
/**
|
|
122
144
|
* A discriminated union type representing an event that can be dispatched to a machine.
|
|
123
145
|
* This is automatically generated from a machine's type signature, ensuring full type safety.
|
|
@@ -128,11 +150,153 @@ export type InferMachine<F extends (...args: any[]) => any> = ReturnType<F>;
|
|
|
128
150
|
*/
|
|
129
151
|
export type Event<M extends BaseMachine<any>> = {
|
|
130
152
|
[K in keyof Omit<M, "context"> & string]: M[K] extends (...args: infer A) => any
|
|
131
|
-
|
|
132
|
-
|
|
153
|
+
? { type: K; args: A }
|
|
154
|
+
: never
|
|
133
155
|
}[keyof Omit<M, "context"> & string];
|
|
134
156
|
|
|
135
157
|
|
|
158
|
+
/**
|
|
159
|
+
* A helper type for use with TypeScript's `satisfies` operator to provide
|
|
160
|
+
* strong, immediate type-checking for standalone transition objects.
|
|
161
|
+
*
|
|
162
|
+
* This solves the "chicken-and-egg" problem where you need the final machine
|
|
163
|
+
* type to correctly type the transitions object, but you need the transitions
|
|
164
|
+
* object to create the machine. By forward-declaring the machine type and using
|
|
165
|
+
* `satisfies TransitionsFor<...>`, you get full IntelliSense and error-checking
|
|
166
|
+
* at the exact location of your transition definitions.
|
|
167
|
+
*
|
|
168
|
+
* @template C The context object type for the machine.
|
|
169
|
+
* @template T The literal type of the transitions object itself (`typeof myTransitions`).
|
|
170
|
+
*
|
|
171
|
+
* @example
|
|
172
|
+
* import { createMachine, Machine, TransitionsFor } from '@doeixd/machine';
|
|
173
|
+
*
|
|
174
|
+
* // 1. Define the context for your machine.
|
|
175
|
+
* type CounterContext = { count: number };
|
|
176
|
+
*
|
|
177
|
+
* // 2. Forward-declare the final machine type. This is the key step that
|
|
178
|
+
* // breaks the circular dependency for the type checker.
|
|
179
|
+
* type CounterMachine = Machine<CounterContext> & typeof counterTransitions;
|
|
180
|
+
*
|
|
181
|
+
* // 3. Define the transitions object, using `satisfies` to apply the helper type.
|
|
182
|
+
* // This provides immediate type-checking and full autocompletion for `this`.
|
|
183
|
+
* const counterTransitions = {
|
|
184
|
+
* increment() {
|
|
185
|
+
* // `this` is now fully typed!
|
|
186
|
+
* // IntelliSense knows `this.count` is a number and
|
|
187
|
+
* // `this.transitions.add` is a function.
|
|
188
|
+
* return createMachine({ count: this.count + 1 }, this.transitions);
|
|
189
|
+
* },
|
|
190
|
+
* add(n: number) {
|
|
191
|
+
* return createMachine({ count: this.count + n }, this.transitions);
|
|
192
|
+
* },
|
|
193
|
+
* // ❌ TypeScript will immediately throw a compile error on the next line
|
|
194
|
+
* // because the return type 'string' does not satisfy 'Machine<any>'.
|
|
195
|
+
* invalidTransition() {
|
|
196
|
+
* return "this is not a machine";
|
|
197
|
+
* }
|
|
198
|
+
* } satisfies TransitionsFor<CounterContext, typeof counterTransitions>;
|
|
199
|
+
*
|
|
200
|
+
* // 4. Create the machine instance. The `createMachine` call is now
|
|
201
|
+
* // guaranteed to be type-safe because `counterTransitions` has already
|
|
202
|
+
* // been validated.
|
|
203
|
+
* export function createCounter(initialCount = 0): CounterMachine {
|
|
204
|
+
* return createMachine({ count: initialCount }, counterTransitions);
|
|
205
|
+
* }
|
|
206
|
+
*/
|
|
207
|
+
export type TransitionsFor<C extends object, T extends Record<string, any>> = {
|
|
208
|
+
[K in keyof T]: (this: C & { transitions: T }, ...args: Parameters<T[K] extends (...a: infer A) => any ? (...a: A) => any : never>) => Machine<any, any>;
|
|
209
|
+
};
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* A helper type for use with the `satisfies` operator to provide strong
|
|
213
|
+
* type-checking for standalone asynchronous transition objects.
|
|
214
|
+
*/
|
|
215
|
+
export type AsyncTransitionsFor<C extends object, T extends Record<string, any>> = {
|
|
216
|
+
[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>>;
|
|
217
|
+
};
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* A mapped type that iterates over a transitions object `T` and keeps only the
|
|
221
|
+
* keys whose functions return a valid `Machine`. This provides a "self-correcting"
|
|
222
|
+
* type that prevents the definition of invalid transitions at compile time.
|
|
223
|
+
*
|
|
224
|
+
* It acts as a filter at the type level. When used in the return type of a
|
|
225
|
+
* function like `createMachine`, it ensures that the resulting machine object
|
|
226
|
+
* will not have any properties corresponding to functions that were defined
|
|
227
|
+
* with an incorrect return type. This provides immediate, precise feedback to
|
|
228
|
+
* the developer, making it impossible to create a machine with an invalid
|
|
229
|
+
* transition shape.
|
|
230
|
+
*
|
|
231
|
+
* @template T The raw transitions object type provided by the user.
|
|
232
|
+
*
|
|
233
|
+
* @example
|
|
234
|
+
* import { createMachine, Machine } from '@doeixd/machine';
|
|
235
|
+
*
|
|
236
|
+
* const machine = createMachine({ value: 'A' }, {
|
|
237
|
+
* // This is a valid transition because it returns a `Machine`.
|
|
238
|
+
* // The key 'goToB' will be PRESERVED in the final type.
|
|
239
|
+
* goToB() {
|
|
240
|
+
* return createMachine({ value: 'B' }, this.transitions);
|
|
241
|
+
* },
|
|
242
|
+
*
|
|
243
|
+
* // This is an INVALID transition because it returns a string.
|
|
244
|
+
* // The key 'invalid' will be OMITTED from the final type.
|
|
245
|
+
* invalid() {
|
|
246
|
+
* return "This is not a Machine object";
|
|
247
|
+
* },
|
|
248
|
+
*
|
|
249
|
+
* // This is also invalid as it's not a function.
|
|
250
|
+
* // The key 'alsoInvalid' will be OMITTED from the final type.
|
|
251
|
+
* alsoInvalid: 123
|
|
252
|
+
* });
|
|
253
|
+
*
|
|
254
|
+
* // --- USAGE ---
|
|
255
|
+
*
|
|
256
|
+
* // ✅ This call is valid and works as expected.
|
|
257
|
+
* const nextState = machine.goToB();
|
|
258
|
+
*
|
|
259
|
+
* // ❌ This line will cause a COMPILE-TIME a ERROR because the `FilterValidTransitions`
|
|
260
|
+
* // type has removed the 'invalid' key from the `machine`'s type signature.
|
|
261
|
+
* //
|
|
262
|
+
* // Error: Property 'invalid' does not exist on type
|
|
263
|
+
* // 'Machine<{ value: string; }> & { goToB: () => Machine<...>; }'.
|
|
264
|
+
* //
|
|
265
|
+
* machine.invalid();
|
|
266
|
+
*/
|
|
267
|
+
export type FilterValidTransitions<T> = {
|
|
268
|
+
[K in keyof T as T[K] extends (...args: any[]) => Machine<any> ? K : never]: T[K];
|
|
269
|
+
};
|
|
270
|
+
|
|
271
|
+
/**
|
|
272
|
+
* A conditional type that intelligently extracts the pure transitions object `T`
|
|
273
|
+
* from the flexible second argument of `createMachine`.
|
|
274
|
+
*
|
|
275
|
+
* It handles three cases:
|
|
276
|
+
* 1. If the argument is the augmented `this` context (`C & { transitions: T }`), it extracts `T`.
|
|
277
|
+
* 2. If the argument is a factory function `((ctx: C) => T)`, it infers and returns `T`.
|
|
278
|
+
* 3. If the argument is already the pure transitions object `T`, it returns it as is.
|
|
279
|
+
*/
|
|
280
|
+
export type ExtractTransitions<Arg, C extends object> = Arg extends (
|
|
281
|
+
...args: any[]
|
|
282
|
+
) => infer R
|
|
283
|
+
? R // Case 2: It's a factory function, extract the return type `R`.
|
|
284
|
+
: Arg extends C & { transitions: infer T }
|
|
285
|
+
? T // Case 1: It's the augmented `this` context, extract `T` from `transitions`.
|
|
286
|
+
: Arg; // Case 3: It's already the plain transitions object.
|
|
287
|
+
|
|
288
|
+
/** Keep only keys whose value is a function that returns a Machine. */
|
|
289
|
+
export type ValidTransitions<T> = {
|
|
290
|
+
[K in keyof T as T[K] extends (...a: any[]) => Machine<any, any> ? K : never]:
|
|
291
|
+
T[K] extends (...a: infer A) => Machine<infer C2, infer T2> ? (...a: A) => Machine<C2, T2> : never;
|
|
292
|
+
};
|
|
293
|
+
|
|
294
|
+
/** Same for async transitions (functions returning MaybePromise<AsyncMachine>). */
|
|
295
|
+
export type ValidAsyncTransitions<T> = {
|
|
296
|
+
[K in keyof T as T[K] extends (...a: any[]) => MaybePromise<AsyncMachine<any, any>> ? K : never]:
|
|
297
|
+
T[K] extends (...a: infer A) => MaybePromise<AsyncMachine<infer C2, infer T2>> ? (...a: A) => MaybePromise<AsyncMachine<C2, T2>> : never;
|
|
298
|
+
};
|
|
299
|
+
|
|
136
300
|
// =============================================================================
|
|
137
301
|
// SECTION: MACHINE CREATION (FUNCTIONAL & OOP)
|
|
138
302
|
// =============================================================================
|
|
@@ -146,18 +310,145 @@ export type Event<M extends BaseMachine<any>> = {
|
|
|
146
310
|
* @param fns - An object containing transition function definitions.
|
|
147
311
|
* @returns A new machine instance.
|
|
148
312
|
*/
|
|
313
|
+
/**
|
|
314
|
+
* Helper to transform transition functions to be bound (no 'this' requirement).
|
|
315
|
+
*/
|
|
316
|
+
export type BindTransitions<T> = {
|
|
317
|
+
[K in keyof T]: T[K] extends (this: any, ...args: infer A) => infer R
|
|
318
|
+
? (...args: A) => R
|
|
319
|
+
: T[K];
|
|
320
|
+
};
|
|
321
|
+
|
|
322
|
+
/**
|
|
323
|
+
* Creates a synchronous state machine from a context and a factory function.
|
|
324
|
+
* This "Functional Builder" pattern allows for type-safe transitions without
|
|
325
|
+
* manually passing `this` or `transitions`.
|
|
326
|
+
*
|
|
327
|
+
* @template C - The context object type.
|
|
328
|
+
* @template T - The transitions object type.
|
|
329
|
+
* @param context - The initial state context.
|
|
330
|
+
* @param factory - A function that receives a `transition` helper and returns the transitions object.
|
|
331
|
+
* @returns A new machine instance.
|
|
332
|
+
*/
|
|
333
|
+
export function createMachine<C extends object, T extends Record<string, (this: C, ...args: any[]) => any>>(
|
|
334
|
+
context: C,
|
|
335
|
+
factory: (transition: (newContext: C) => Machine<C, T>) => T
|
|
336
|
+
): Machine<C, BindTransitions<T>>;
|
|
337
|
+
|
|
338
|
+
/**
|
|
339
|
+
* Creates a synchronous state machine from a context and transition functions.
|
|
340
|
+
* This is the core factory for the functional approach.
|
|
341
|
+
*
|
|
342
|
+
* @template C - The context object type.
|
|
343
|
+
* @param context - The initial state context.
|
|
344
|
+
* @param fns - An object containing transition function definitions.
|
|
345
|
+
* @returns A new machine instance.
|
|
346
|
+
*/
|
|
347
|
+
export function createMachine<C extends object, T extends Record<string, (this: { context: C } & T, ...args: any[]) => any> & { context?: any }>(
|
|
348
|
+
context: C,
|
|
349
|
+
fns: T
|
|
350
|
+
): { context: C } & T;
|
|
351
|
+
|
|
352
|
+
/**
|
|
353
|
+
* Creates a synchronous state machine by copying context and transitions from an existing machine.
|
|
354
|
+
* This is useful for creating a new machine with updated context but the same transitions.
|
|
355
|
+
*
|
|
356
|
+
* @template C - The context object type.
|
|
357
|
+
* @template M - The machine type to copy transitions from.
|
|
358
|
+
* @param context - The new context.
|
|
359
|
+
* @param machine - The machine to copy transitions from.
|
|
360
|
+
* @returns A new machine instance with the given context and copied transitions.
|
|
361
|
+
*/
|
|
362
|
+
export function createMachine<C extends object, M extends BaseMachine<C>>(
|
|
363
|
+
context: C,
|
|
364
|
+
machine: M
|
|
365
|
+
): Machine<C, Transitions<M>>;
|
|
366
|
+
|
|
367
|
+
/**
|
|
368
|
+
* Creates a synchronous state machine from a context and transition functions that expect `this` to be the context object.
|
|
369
|
+
* This is used internally by utilities that need to bind transitions to context objects.
|
|
370
|
+
*
|
|
371
|
+
* @template C - The context object type.
|
|
372
|
+
* @param context - The initial state context.
|
|
373
|
+
* @param fns - An object containing transition function definitions that expect `this` to be the context.
|
|
374
|
+
* @returns A new machine instance.
|
|
375
|
+
*/
|
|
149
376
|
export function createMachine<C extends object, T extends Record<string, (this: C, ...args: any[]) => any>>(
|
|
150
377
|
context: C,
|
|
151
378
|
fns: T
|
|
152
|
-
):
|
|
379
|
+
): Machine<C, T>;
|
|
380
|
+
|
|
381
|
+
export function createMachine(context: any, fnsOrFactory: any): any {
|
|
382
|
+
if (typeof fnsOrFactory === 'function') {
|
|
383
|
+
let transitions: any;
|
|
384
|
+
const transition = (newContext: any) => {
|
|
385
|
+
const machine = createMachine(newContext, transitions);
|
|
386
|
+
// Re-bind transitions to the new context
|
|
387
|
+
const boundTransitions = Object.fromEntries(
|
|
388
|
+
Object.entries(transitions).map(([key, fn]) => [
|
|
389
|
+
key,
|
|
390
|
+
(fn as Function).bind(newContext)
|
|
391
|
+
])
|
|
392
|
+
);
|
|
393
|
+
return Object.assign(machine, boundTransitions);
|
|
394
|
+
};
|
|
395
|
+
transitions = fnsOrFactory(transition);
|
|
396
|
+
|
|
397
|
+
// Bind transitions to initial context
|
|
398
|
+
const boundTransitions = Object.fromEntries(
|
|
399
|
+
Object.entries(transitions).map(([key, fn]) => [
|
|
400
|
+
key,
|
|
401
|
+
(fn as Function).bind(context)
|
|
402
|
+
])
|
|
403
|
+
);
|
|
404
|
+
|
|
405
|
+
return Object.assign({ context }, boundTransitions);
|
|
406
|
+
}
|
|
407
|
+
|
|
153
408
|
// If fns is a machine (has context property), extract just the transition functions
|
|
154
|
-
const transitions = 'context' in
|
|
155
|
-
Object.entries(
|
|
156
|
-
) :
|
|
409
|
+
const transitions = 'context' in fnsOrFactory ? Object.fromEntries(
|
|
410
|
+
Object.entries(fnsOrFactory).filter(([key]) => key !== 'context')
|
|
411
|
+
) : fnsOrFactory;
|
|
412
|
+
|
|
413
|
+
// For normal object transitions, we might also need binding if they use `this`
|
|
414
|
+
// But existing code expects `this` to be the machine (context + transitions).
|
|
415
|
+
// The new API expects `this` to be just context.
|
|
416
|
+
|
|
157
417
|
const machine = Object.assign({ context }, transitions);
|
|
158
|
-
return machine
|
|
418
|
+
return machine;
|
|
159
419
|
}
|
|
160
420
|
|
|
421
|
+
/**
|
|
422
|
+
* Creates an asynchronous state machine from a context and a factory function.
|
|
423
|
+
* This "Functional Builder" pattern allows for type-safe transitions without
|
|
424
|
+
* manually passing `this` or `transitions`.
|
|
425
|
+
*
|
|
426
|
+
* @template C - The context object type.
|
|
427
|
+
* @template T - The transitions object type.
|
|
428
|
+
* @param context - The initial state context.
|
|
429
|
+
* @param factory - A function that receives a `transition` helper and returns the transitions object.
|
|
430
|
+
* @returns A new async machine instance.
|
|
431
|
+
*/
|
|
432
|
+
export function createAsyncMachine<C extends object, T extends Record<string, (this: C, ...args: any[]) => any>>(
|
|
433
|
+
context: C,
|
|
434
|
+
factory: (transition: (newContext: C) => AsyncMachine<C, T>) => T
|
|
435
|
+
): AsyncMachine<C, BindTransitions<T>>;
|
|
436
|
+
|
|
437
|
+
/**
|
|
438
|
+
* Creates an asynchronous state machine by copying context and transitions from an existing machine.
|
|
439
|
+
* This is useful for creating a new machine with updated context but the same transitions.
|
|
440
|
+
*
|
|
441
|
+
* @template C - The context object type.
|
|
442
|
+
* @template M - The machine type to copy transitions from.
|
|
443
|
+
* @param context - The new context.
|
|
444
|
+
* @param machine - The machine to copy transitions from.
|
|
445
|
+
* @returns A new async machine instance with the given context and copied transitions.
|
|
446
|
+
*/
|
|
447
|
+
export function createAsyncMachine<C extends object, M extends BaseMachine<C>>(
|
|
448
|
+
context: C,
|
|
449
|
+
machine: M
|
|
450
|
+
): AsyncMachine<C, Transitions<M>>;
|
|
451
|
+
|
|
161
452
|
/**
|
|
162
453
|
* Creates an asynchronous state machine from a context and async transition functions.
|
|
163
454
|
*
|
|
@@ -169,8 +460,42 @@ export function createMachine<C extends object, T extends Record<string, (this:
|
|
|
169
460
|
export function createAsyncMachine<C extends object, T extends Record<string, (this: C, ...args: any[]) => any>>(
|
|
170
461
|
context: C,
|
|
171
462
|
fns: T
|
|
172
|
-
):
|
|
173
|
-
|
|
463
|
+
): AsyncMachine<C, T>;
|
|
464
|
+
|
|
465
|
+
export function createAsyncMachine(context: any, fnsOrFactory: any): any {
|
|
466
|
+
if (typeof fnsOrFactory === 'function') {
|
|
467
|
+
let transitions: any;
|
|
468
|
+
const transition = (newContext: any) => {
|
|
469
|
+
const machine = createAsyncMachine(newContext, transitions);
|
|
470
|
+
// Re-bind transitions to the new context
|
|
471
|
+
const boundTransitions = Object.fromEntries(
|
|
472
|
+
Object.entries(transitions).map(([key, fn]) => [
|
|
473
|
+
key,
|
|
474
|
+
(fn as Function).bind(newContext)
|
|
475
|
+
])
|
|
476
|
+
);
|
|
477
|
+
return Object.assign(machine, boundTransitions);
|
|
478
|
+
};
|
|
479
|
+
transitions = fnsOrFactory(transition);
|
|
480
|
+
|
|
481
|
+
// Bind transitions to initial context
|
|
482
|
+
const boundTransitions = Object.fromEntries(
|
|
483
|
+
Object.entries(transitions).map(([key, fn]) => [
|
|
484
|
+
key,
|
|
485
|
+
(fn as Function).bind(context)
|
|
486
|
+
])
|
|
487
|
+
);
|
|
488
|
+
|
|
489
|
+
return Object.assign({ context }, boundTransitions);
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
// If fns is a machine (has context property), extract just the transition functions
|
|
493
|
+
const transitions = 'context' in fnsOrFactory ? Object.fromEntries(
|
|
494
|
+
Object.entries(fnsOrFactory).filter(([key]) => key !== 'context')
|
|
495
|
+
) : fnsOrFactory;
|
|
496
|
+
|
|
497
|
+
const machine = Object.assign({ context }, transitions);
|
|
498
|
+
return machine;
|
|
174
499
|
}
|
|
175
500
|
|
|
176
501
|
/**
|
|
@@ -196,16 +521,16 @@ export function createMachineFactory<C extends object>() {
|
|
|
196
521
|
) => {
|
|
197
522
|
type MachineFns = {
|
|
198
523
|
[K in keyof T]: (
|
|
199
|
-
this: C
|
|
524
|
+
this: Machine<C>,
|
|
200
525
|
...args: T[K] extends (ctx: C, ...args: infer A) => C ? A : never
|
|
201
|
-
) => Machine<C
|
|
526
|
+
) => MaybePromise<Machine<C>>;
|
|
202
527
|
};
|
|
203
528
|
|
|
204
529
|
const fns = Object.fromEntries(
|
|
205
530
|
Object.entries(transformers).map(([key, transform]) => [
|
|
206
531
|
key,
|
|
207
|
-
function (this: C
|
|
208
|
-
const newContext = (transform as any)(this, ...args);
|
|
532
|
+
function (this: Machine<C>, ...args: any[]) {
|
|
533
|
+
const newContext = (transform as any)(this.context, ...args);
|
|
209
534
|
return createMachine(newContext, fns as any);
|
|
210
535
|
},
|
|
211
536
|
])
|
|
@@ -241,7 +566,7 @@ export function setContext<M extends Machine<any>>(
|
|
|
241
566
|
? (newContextOrFn as (ctx: Readonly<Context<M>>) => Context<M>)(context)
|
|
242
567
|
: newContextOrFn;
|
|
243
568
|
|
|
244
|
-
return createMachine(newContext, transitions) as M;
|
|
569
|
+
return createMachine(newContext, transitions as any) as M;
|
|
245
570
|
}
|
|
246
571
|
|
|
247
572
|
/**
|
|
@@ -263,7 +588,7 @@ export function overrideTransitions<
|
|
|
263
588
|
): Machine<Context<M>> & Omit<Transitions<M>, keyof T> & T {
|
|
264
589
|
const { context, ...originalTransitions } = machine;
|
|
265
590
|
const newTransitions = { ...originalTransitions, ...overrides };
|
|
266
|
-
return createMachine(context, newTransitions) as any;
|
|
591
|
+
return createMachine(context, newTransitions as any) as any;
|
|
267
592
|
}
|
|
268
593
|
|
|
269
594
|
/**
|
|
@@ -285,7 +610,7 @@ export function extendTransitions<
|
|
|
285
610
|
>(machine: M, newTransitions: T): M & T {
|
|
286
611
|
const { context, ...originalTransitions } = machine;
|
|
287
612
|
const combinedTransitions = { ...originalTransitions, ...newTransitions };
|
|
288
|
-
return createMachine(context, combinedTransitions) as M & T;
|
|
613
|
+
return createMachine(context, combinedTransitions as any) as M & T;
|
|
289
614
|
}
|
|
290
615
|
|
|
291
616
|
/**
|
|
@@ -339,8 +664,8 @@ export function combineFactories<
|
|
|
339
664
|
): (
|
|
340
665
|
...args: Parameters<F1>
|
|
341
666
|
) => Machine<Context<ReturnType<F1>> & Context<ReturnType<F2>>> &
|
|
342
|
-
|
|
343
|
-
|
|
667
|
+
Omit<ReturnType<F1>, 'context'> &
|
|
668
|
+
Omit<ReturnType<F2>, 'context'> {
|
|
344
669
|
return (...args: Parameters<F1>) => {
|
|
345
670
|
// Create instances from both factories
|
|
346
671
|
const machine1 = factory1(...args);
|
|
@@ -357,7 +682,7 @@ export function combineFactories<
|
|
|
357
682
|
const combinedTransitions = { ...transitions1, ...transitions2 };
|
|
358
683
|
|
|
359
684
|
// Create the combined machine
|
|
360
|
-
return createMachine(combinedContext, combinedTransitions) as any;
|
|
685
|
+
return createMachine(combinedContext, combinedTransitions as any) as any;
|
|
361
686
|
};
|
|
362
687
|
}
|
|
363
688
|
|
|
@@ -375,7 +700,7 @@ export function createMachineBuilder<M extends Machine<any>>(
|
|
|
375
700
|
): (context: Context<M>) => M {
|
|
376
701
|
const { context, ...transitions } = templateMachine;
|
|
377
702
|
return (newContext: Context<M>): M => {
|
|
378
|
-
return createMachine(newContext, transitions) as M;
|
|
703
|
+
return createMachine(newContext, transitions as any) as M;
|
|
379
704
|
};
|
|
380
705
|
}
|
|
381
706
|
|
|
@@ -607,7 +932,7 @@ export function next<C extends object>(
|
|
|
607
932
|
update: (ctx: Readonly<C>) => C
|
|
608
933
|
): Machine<C> {
|
|
609
934
|
const { context, ...transitions } = m;
|
|
610
|
-
return createMachine(update(context), transitions) as Machine<C>;
|
|
935
|
+
return createMachine(update(context), transitions as any) as Machine<C>;
|
|
611
936
|
}
|
|
612
937
|
|
|
613
938
|
/**
|
|
@@ -694,30 +1019,17 @@ export {
|
|
|
694
1019
|
} from './primitives';
|
|
695
1020
|
|
|
696
1021
|
// =============================================================================
|
|
697
|
-
// SECTION: STATECHART EXTRACTION
|
|
698
|
-
// =============================================================================
|
|
699
|
-
|
|
700
|
-
export {
|
|
701
|
-
extractMachine,
|
|
702
|
-
extractMachines,
|
|
703
|
-
generateChart,
|
|
704
|
-
type MachineConfig,
|
|
705
|
-
type ExtractionConfig
|
|
706
|
-
} from './extract';
|
|
707
|
-
|
|
708
|
-
// =============================================================================
|
|
709
|
-
// SECTION: RUNTIME EXTRACTION
|
|
1022
|
+
// SECTION: STATECHART EXTRACTION (Build-time only)
|
|
710
1023
|
// =============================================================================
|
|
711
1024
|
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
generateStatechart,
|
|
716
|
-
extractFromInstance
|
|
717
|
-
} from './runtime-extract';
|
|
1025
|
+
// Note: Extraction tools are available as dev dependencies for build-time use
|
|
1026
|
+
// They are not included in the runtime bundle for size optimization
|
|
1027
|
+
// Use: npx tsx scripts/extract-statechart.ts
|
|
718
1028
|
|
|
719
|
-
|
|
720
|
-
|
|
1029
|
+
export type {
|
|
1030
|
+
MachineConfig,
|
|
1031
|
+
ExtractionConfig
|
|
1032
|
+
} from './extract';
|
|
721
1033
|
|
|
722
1034
|
|
|
723
1035
|
export * from './multi'
|
|
@@ -730,46 +1042,7 @@ export * from './extract'
|
|
|
730
1042
|
// SECTION: MIDDLEWARE & INTERCEPTION
|
|
731
1043
|
// =============================================================================
|
|
732
1044
|
|
|
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';
|
|
1045
|
+
export * from './middleware/index';
|
|
773
1046
|
|
|
774
1047
|
// =============================================================================
|
|
775
1048
|
// SECTION: UTILITIES & HELPERS
|