@doeixd/machine 0.0.12 → 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 +90 -15
- package/dist/cjs/development/core.js +1852 -0
- package/dist/cjs/development/core.js.map +7 -0
- package/dist/cjs/development/index.js +1348 -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 +1348 -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 +60 -5
- package/dist/types/functional-combinators.d.ts.map +1 -1
- package/dist/types/index.d.ts +242 -19
- 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 +76 -3
- package/src/index.ts +376 -102
- 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
|
@@ -0,0 +1,944 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file Middleware composition and pipeline utilities
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import type { BaseMachine, Context } from '../index';
|
|
6
|
+
import type {
|
|
7
|
+
MiddlewareContext,
|
|
8
|
+
MiddlewareResult,
|
|
9
|
+
MiddlewareError,
|
|
10
|
+
MiddlewareHooks,
|
|
11
|
+
MiddlewareOptions
|
|
12
|
+
} from './core';
|
|
13
|
+
import {
|
|
14
|
+
withLogging,
|
|
15
|
+
withAnalytics,
|
|
16
|
+
withValidation,
|
|
17
|
+
withPermissions,
|
|
18
|
+
withErrorReporting,
|
|
19
|
+
withPerformanceMonitoring,
|
|
20
|
+
withRetry
|
|
21
|
+
} from './core';
|
|
22
|
+
import { withHistory, type HistoryEntry, type Serializer } from './history';
|
|
23
|
+
import { withSnapshot } from './snapshot';
|
|
24
|
+
import { withTimeTravel, type WithTimeTravel, type WithHistory, type WithSnapshot } from './time-travel';
|
|
25
|
+
|
|
26
|
+
// =============================================================================
|
|
27
|
+
// SECTION: COMPOSITION TYPES
|
|
28
|
+
// =============================================================================
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* A middleware function that transforms a machine.
|
|
32
|
+
* @template M - The input machine type
|
|
33
|
+
* @template R - The output machine type (usually extends M)
|
|
34
|
+
*/
|
|
35
|
+
export type MiddlewareFn<M extends BaseMachine<any>, R extends BaseMachine<any> = M> = (machine: M) => R;
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* A conditional middleware that may or may not be applied based on a predicate.
|
|
39
|
+
* @template M - The machine type
|
|
40
|
+
*/
|
|
41
|
+
export type ConditionalMiddleware<M extends BaseMachine<any>> = {
|
|
42
|
+
/** The middleware function to apply */
|
|
43
|
+
middleware: MiddlewareFn<M>;
|
|
44
|
+
/** Predicate function that determines if the middleware should be applied */
|
|
45
|
+
when: (machine: M) => boolean;
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* A named middleware entry for registry-based composition.
|
|
50
|
+
* @template M - The machine type
|
|
51
|
+
*/
|
|
52
|
+
export type NamedMiddleware<M extends BaseMachine<any>> = {
|
|
53
|
+
/** Unique name for the middleware */
|
|
54
|
+
name: string;
|
|
55
|
+
/** The middleware function */
|
|
56
|
+
middleware: MiddlewareFn<M>;
|
|
57
|
+
/** Optional description */
|
|
58
|
+
description?: string;
|
|
59
|
+
/** Optional priority for ordering (higher numbers = applied later) */
|
|
60
|
+
priority?: number;
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Configuration for middleware pipeline execution.
|
|
65
|
+
*/
|
|
66
|
+
export interface PipelineConfig {
|
|
67
|
+
/** Whether to continue execution if a middleware throws an error */
|
|
68
|
+
continueOnError?: boolean;
|
|
69
|
+
/** Whether to log errors from middlewares */
|
|
70
|
+
logErrors?: boolean;
|
|
71
|
+
/** Custom error handler */
|
|
72
|
+
onError?: (error: Error, middlewareIndex: number, middlewareName?: string) => void;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Result of pipeline execution.
|
|
77
|
+
*/
|
|
78
|
+
export type PipelineResult<M extends BaseMachine<any>> = M;
|
|
79
|
+
|
|
80
|
+
// =============================================================================
|
|
81
|
+
// SECTION: TYPE-LEVEL COMPOSITION
|
|
82
|
+
// =============================================================================
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Type-level utility for composing middleware return types.
|
|
86
|
+
* This enables perfect TypeScript inference when chaining middlewares.
|
|
87
|
+
*/
|
|
88
|
+
export type ComposeResult<
|
|
89
|
+
M extends BaseMachine<any>,
|
|
90
|
+
Ms extends readonly MiddlewareFn<any, any>[]
|
|
91
|
+
> = Ms extends readonly [infer First, ...infer Rest]
|
|
92
|
+
? First extends MiddlewareFn<any, infer R>
|
|
93
|
+
? Rest extends readonly MiddlewareFn<any, any>[]
|
|
94
|
+
? ComposeResult<R, Rest>
|
|
95
|
+
: R
|
|
96
|
+
: M
|
|
97
|
+
: M;
|
|
98
|
+
|
|
99
|
+
// =============================================================================
|
|
100
|
+
// SECTION: COMPOSITION FUNCTIONS
|
|
101
|
+
// =============================================================================
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Compose multiple middleware functions into a single middleware stack.
|
|
105
|
+
* Middleware is applied left-to-right (first middleware wraps outermost).
|
|
106
|
+
*
|
|
107
|
+
* @template M - The machine type
|
|
108
|
+
* @param machine - The base machine
|
|
109
|
+
* @param middlewares - Array of middleware functions
|
|
110
|
+
* @returns A new machine with all middleware applied
|
|
111
|
+
*/
|
|
112
|
+
export function compose<M extends BaseMachine<any>>(
|
|
113
|
+
machine: M,
|
|
114
|
+
...middlewares: Array<(m: M) => M>
|
|
115
|
+
): M {
|
|
116
|
+
return middlewares.reduce((acc, middleware) => middleware(acc), machine);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Type-safe middleware composition with perfect inference.
|
|
121
|
+
* Composes multiple middlewares into a single transformation chain.
|
|
122
|
+
*
|
|
123
|
+
* @template M - The input machine type
|
|
124
|
+
* @template Ms - Array of middleware functions
|
|
125
|
+
* @param machine - The machine to enhance
|
|
126
|
+
* @param middlewares - Middleware functions to apply in order
|
|
127
|
+
* @returns The machine with all middlewares applied, with precise type inference
|
|
128
|
+
*/
|
|
129
|
+
export function composeTyped<
|
|
130
|
+
M extends BaseMachine<any>,
|
|
131
|
+
Ms extends readonly MiddlewareFn<any, any>[]
|
|
132
|
+
>(
|
|
133
|
+
machine: M,
|
|
134
|
+
...middlewares: Ms
|
|
135
|
+
): ComposeResult<M, Ms> {
|
|
136
|
+
return middlewares.reduce((acc, middleware) => middleware(acc), machine) as ComposeResult<M, Ms>;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// =============================================================================
|
|
140
|
+
// SECTION: FLUENT API
|
|
141
|
+
// =============================================================================
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Fluent middleware composer for building complex middleware chains.
|
|
145
|
+
* Provides excellent TypeScript inference and IntelliSense.
|
|
146
|
+
*/
|
|
147
|
+
class MiddlewareChainBuilder<M extends BaseMachine<any>> {
|
|
148
|
+
constructor(private machine: M) {}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Add a middleware to the composition chain.
|
|
152
|
+
* @param middleware - The middleware function to add
|
|
153
|
+
* @returns A new composer with the middleware applied
|
|
154
|
+
*/
|
|
155
|
+
with<M2 extends MiddlewareFn<any, any>>(
|
|
156
|
+
middleware: M2
|
|
157
|
+
): MiddlewareChainBuilder<ReturnType<M2> extends BaseMachine<any> ? ReturnType<M2> : M> {
|
|
158
|
+
const result = middleware(this.machine);
|
|
159
|
+
return new MiddlewareChainBuilder(result as any);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Build the final machine with all middlewares applied.
|
|
164
|
+
*/
|
|
165
|
+
build(): M {
|
|
166
|
+
return this.machine;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Create a fluent middleware chain builder.
|
|
172
|
+
*
|
|
173
|
+
* @example
|
|
174
|
+
* ```typescript
|
|
175
|
+
* const enhanced = chain(counter)
|
|
176
|
+
* .with(withHistory())
|
|
177
|
+
* .with(withSnapshot())
|
|
178
|
+
* .with(withTimeTravel())
|
|
179
|
+
* .build();
|
|
180
|
+
* ```
|
|
181
|
+
*/
|
|
182
|
+
export function chain<M extends BaseMachine<any>>(machine: M) {
|
|
183
|
+
return new MiddlewareChainBuilder(machine);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// =============================================================================
|
|
187
|
+
// SECTION: CONDITIONAL MIDDLEWARE
|
|
188
|
+
// =============================================================================
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* Create a conditional middleware that only applies when a predicate is true.
|
|
192
|
+
*
|
|
193
|
+
* @template M - The machine type
|
|
194
|
+
* @param middleware - The middleware to conditionally apply
|
|
195
|
+
* @param predicate - Function that determines when to apply the middleware
|
|
196
|
+
* @returns A conditional middleware that can be called directly or used in pipelines
|
|
197
|
+
*/
|
|
198
|
+
export function when<M extends BaseMachine<any>>(
|
|
199
|
+
middleware: MiddlewareFn<M>,
|
|
200
|
+
predicate: (machine: M) => boolean
|
|
201
|
+
): ConditionalMiddleware<M> & MiddlewareFn<M> {
|
|
202
|
+
const conditional: ConditionalMiddleware<M> & MiddlewareFn<M> = function(machine: M) {
|
|
203
|
+
return predicate(machine) ? middleware(machine) : machine;
|
|
204
|
+
};
|
|
205
|
+
|
|
206
|
+
conditional.middleware = middleware;
|
|
207
|
+
conditional.when = predicate;
|
|
208
|
+
|
|
209
|
+
return conditional;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Create a middleware that only applies in development mode.
|
|
214
|
+
*
|
|
215
|
+
* @template M - The machine type
|
|
216
|
+
* @param middleware - The middleware to apply in development
|
|
217
|
+
* @returns A conditional middleware for development mode
|
|
218
|
+
*/
|
|
219
|
+
export function inDevelopment<M extends BaseMachine<any>>(
|
|
220
|
+
middleware: MiddlewareFn<M>
|
|
221
|
+
): ConditionalMiddleware<M> & MiddlewareFn<M> {
|
|
222
|
+
return when(middleware, () => {
|
|
223
|
+
return typeof process !== 'undefined'
|
|
224
|
+
? process.env.NODE_ENV === 'development'
|
|
225
|
+
: typeof window !== 'undefined'
|
|
226
|
+
? !window.location.hostname.includes('production')
|
|
227
|
+
: false;
|
|
228
|
+
});
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* Create a middleware that only applies when a context property matches a value.
|
|
233
|
+
*
|
|
234
|
+
* @template M - The machine type
|
|
235
|
+
* @template K - The context key
|
|
236
|
+
* @param key - The context property key
|
|
237
|
+
* @param value - The value to match
|
|
238
|
+
* @param middleware - The middleware to apply when the condition matches
|
|
239
|
+
* @returns A conditional middleware
|
|
240
|
+
*/
|
|
241
|
+
export function whenContext<M extends BaseMachine<any>, K extends keyof Context<M>>(
|
|
242
|
+
key: K,
|
|
243
|
+
value: Context<M>[K],
|
|
244
|
+
middleware: MiddlewareFn<M>
|
|
245
|
+
): ConditionalMiddleware<M> & MiddlewareFn<M> {
|
|
246
|
+
return when(middleware, (machine) => machine.context[key] === value);
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
// =============================================================================
|
|
250
|
+
// SECTION: MIDDLEWARE REGISTRY
|
|
251
|
+
// =============================================================================
|
|
252
|
+
|
|
253
|
+
/**
|
|
254
|
+
* Create a middleware registry for managing reusable middleware configurations.
|
|
255
|
+
*/
|
|
256
|
+
export function createMiddlewareRegistry<M extends BaseMachine<any>>() {
|
|
257
|
+
const registry = new Map<string, NamedMiddleware<M>>();
|
|
258
|
+
|
|
259
|
+
return {
|
|
260
|
+
/**
|
|
261
|
+
* Register a middleware by name.
|
|
262
|
+
*/
|
|
263
|
+
register(
|
|
264
|
+
name: string,
|
|
265
|
+
middleware: MiddlewareFn<M>,
|
|
266
|
+
description?: string,
|
|
267
|
+
priority?: number
|
|
268
|
+
): typeof this {
|
|
269
|
+
if (registry.has(name)) {
|
|
270
|
+
throw new Error(`Middleware '${name}' is already registered`);
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
registry.set(name, { name, middleware, description, priority });
|
|
274
|
+
return this;
|
|
275
|
+
},
|
|
276
|
+
|
|
277
|
+
/**
|
|
278
|
+
* Unregister a middleware by name.
|
|
279
|
+
*/
|
|
280
|
+
unregister(name: string): boolean {
|
|
281
|
+
return registry.delete(name);
|
|
282
|
+
},
|
|
283
|
+
|
|
284
|
+
/**
|
|
285
|
+
* Check if a middleware is registered.
|
|
286
|
+
*/
|
|
287
|
+
has(name: string): boolean {
|
|
288
|
+
return registry.has(name);
|
|
289
|
+
},
|
|
290
|
+
|
|
291
|
+
/**
|
|
292
|
+
* Get a registered middleware by name.
|
|
293
|
+
*/
|
|
294
|
+
get(name: string): NamedMiddleware<M> | undefined {
|
|
295
|
+
return registry.get(name);
|
|
296
|
+
},
|
|
297
|
+
|
|
298
|
+
/**
|
|
299
|
+
* List all registered middlewares.
|
|
300
|
+
*/
|
|
301
|
+
list(): NamedMiddleware<M>[] {
|
|
302
|
+
return Array.from(registry.values()).sort((a, b) => (a.priority ?? 0) - (b.priority ?? 0));
|
|
303
|
+
},
|
|
304
|
+
|
|
305
|
+
/**
|
|
306
|
+
* Apply a selection of registered middlewares to a machine.
|
|
307
|
+
* Middlewares are applied in priority order (lowest to highest).
|
|
308
|
+
*/
|
|
309
|
+
apply(machine: M, middlewareNames: string[]): M {
|
|
310
|
+
const middlewares = middlewareNames
|
|
311
|
+
.map(name => {
|
|
312
|
+
const entry = registry.get(name);
|
|
313
|
+
if (!entry) {
|
|
314
|
+
throw new Error(`Middleware '${name}' is not registered`);
|
|
315
|
+
}
|
|
316
|
+
return entry;
|
|
317
|
+
})
|
|
318
|
+
.sort((a, b) => (a.priority ?? 0) - (b.priority ?? 0));
|
|
319
|
+
|
|
320
|
+
return composeTyped(machine, ...middlewares.map(m => m.middleware));
|
|
321
|
+
},
|
|
322
|
+
|
|
323
|
+
/**
|
|
324
|
+
* Apply all registered middlewares to a machine in priority order.
|
|
325
|
+
*/
|
|
326
|
+
applyAll(machine: M): M {
|
|
327
|
+
const middlewares = this.list();
|
|
328
|
+
return composeTyped(machine, ...middlewares.map(m => m.middleware));
|
|
329
|
+
}
|
|
330
|
+
};
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
// =============================================================================
|
|
334
|
+
// SECTION: PIPELINES
|
|
335
|
+
// =============================================================================
|
|
336
|
+
|
|
337
|
+
/**
|
|
338
|
+
* Create a middleware pipeline with error handling and conditional execution.
|
|
339
|
+
*
|
|
340
|
+
* @template M - The machine type
|
|
341
|
+
* @param config - Pipeline configuration
|
|
342
|
+
* @returns A function that executes middlewares in a pipeline
|
|
343
|
+
*/
|
|
344
|
+
export function createPipeline<M extends BaseMachine<any>>(
|
|
345
|
+
config: PipelineConfig = {}
|
|
346
|
+
): {
|
|
347
|
+
<Ms extends Array<MiddlewareFn<M> | ConditionalMiddleware<M>>>(
|
|
348
|
+
machine: M,
|
|
349
|
+
...middlewares: Ms
|
|
350
|
+
): { machine: M; errors: Array<{ error: Error; middlewareIndex: number; middlewareName?: string }>; success: boolean };
|
|
351
|
+
} {
|
|
352
|
+
const {
|
|
353
|
+
continueOnError = false,
|
|
354
|
+
logErrors = true,
|
|
355
|
+
onError
|
|
356
|
+
} = config;
|
|
357
|
+
|
|
358
|
+
return (machine: M, ...middlewares: Array<MiddlewareFn<M> | ConditionalMiddleware<M>>) => {
|
|
359
|
+
let currentMachine = machine;
|
|
360
|
+
const errors: Array<{ error: Error; middlewareIndex: number; middlewareName?: string }> = [];
|
|
361
|
+
let success = true;
|
|
362
|
+
|
|
363
|
+
for (let i = 0; i < middlewares.length; i++) {
|
|
364
|
+
const middleware = middlewares[i];
|
|
365
|
+
|
|
366
|
+
try {
|
|
367
|
+
// Handle conditional middleware
|
|
368
|
+
if ('middleware' in middleware && 'when' in middleware) {
|
|
369
|
+
if (!middleware.when(currentMachine)) {
|
|
370
|
+
continue; // Skip this middleware
|
|
371
|
+
}
|
|
372
|
+
currentMachine = middleware.middleware(currentMachine);
|
|
373
|
+
} else {
|
|
374
|
+
// Regular middleware
|
|
375
|
+
currentMachine = (middleware as MiddlewareFn<M>)(currentMachine);
|
|
376
|
+
}
|
|
377
|
+
} catch (error) {
|
|
378
|
+
success = false;
|
|
379
|
+
if (!continueOnError) {
|
|
380
|
+
throw error;
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
errors.push({
|
|
384
|
+
error: error as Error,
|
|
385
|
+
middlewareIndex: i,
|
|
386
|
+
middlewareName: (middleware as any).name
|
|
387
|
+
});
|
|
388
|
+
|
|
389
|
+
if (logErrors) {
|
|
390
|
+
console.error(`Pipeline middleware error at index ${i}:`, error);
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
onError?.(error as Error, i, (middleware as any).name);
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
return { machine: currentMachine, errors, success };
|
|
398
|
+
};
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
// =============================================================================
|
|
402
|
+
// SECTION: UTILITY FUNCTIONS
|
|
403
|
+
// =============================================================================
|
|
404
|
+
|
|
405
|
+
/**
|
|
406
|
+
* Combine multiple middlewares with short-circuiting.
|
|
407
|
+
*/
|
|
408
|
+
export function combine<M extends BaseMachine<any>>(
|
|
409
|
+
...middlewares: Array<MiddlewareFn<M>>
|
|
410
|
+
): MiddlewareFn<M> {
|
|
411
|
+
return (machine: M) => composeTyped(machine, ...middlewares);
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
/**
|
|
415
|
+
* Create a middleware that applies different middlewares based on context.
|
|
416
|
+
*/
|
|
417
|
+
export function branch<M extends BaseMachine<any>>(
|
|
418
|
+
branches: Array<[predicate: (machine: M) => boolean, middleware: MiddlewareFn<M>]>,
|
|
419
|
+
fallback?: MiddlewareFn<M>
|
|
420
|
+
): MiddlewareFn<M> {
|
|
421
|
+
return (machine: M) => {
|
|
422
|
+
for (const [predicate, middleware] of branches) {
|
|
423
|
+
if (predicate(machine)) {
|
|
424
|
+
return middleware(machine);
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
return fallback ? fallback(machine) : machine;
|
|
428
|
+
};
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
// =============================================================================
|
|
432
|
+
// SECTION: ENHANCED TYPE GUARDS
|
|
433
|
+
// =============================================================================
|
|
434
|
+
|
|
435
|
+
/**
|
|
436
|
+
* Enhanced type guard to check if a value is a middleware function with better inference.
|
|
437
|
+
*/
|
|
438
|
+
export function isMiddlewareFn<M extends BaseMachine<any>, R extends BaseMachine<any> = M>(
|
|
439
|
+
value: any
|
|
440
|
+
): value is MiddlewareFn<M, R> {
|
|
441
|
+
return typeof value === 'function' && value.length === 1;
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
/**
|
|
445
|
+
* Enhanced type guard to check if a value is a conditional middleware with better inference.
|
|
446
|
+
*/
|
|
447
|
+
export function isConditionalMiddleware<M extends BaseMachine<any>>(
|
|
448
|
+
value: any
|
|
449
|
+
): value is ConditionalMiddleware<M> {
|
|
450
|
+
return (
|
|
451
|
+
value !== null &&
|
|
452
|
+
(typeof value === 'object' || typeof value === 'function') &&
|
|
453
|
+
'middleware' in value &&
|
|
454
|
+
'when' in value &&
|
|
455
|
+
isMiddlewareFn(value.middleware) &&
|
|
456
|
+
typeof value.when === 'function'
|
|
457
|
+
);
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
/**
|
|
461
|
+
* Type guard to check if a value is a middleware result with strict type checking.
|
|
462
|
+
*/
|
|
463
|
+
export function isMiddlewareResult<C extends object>(
|
|
464
|
+
value: any,
|
|
465
|
+
contextType?: C
|
|
466
|
+
): value is MiddlewareResult<C> {
|
|
467
|
+
return (
|
|
468
|
+
value !== null &&
|
|
469
|
+
typeof value === 'object' &&
|
|
470
|
+
'transitionName' in value &&
|
|
471
|
+
'prevContext' in value &&
|
|
472
|
+
'nextContext' in value &&
|
|
473
|
+
'args' in value &&
|
|
474
|
+
typeof value.transitionName === 'string' &&
|
|
475
|
+
Array.isArray(value.args) &&
|
|
476
|
+
(!contextType || (
|
|
477
|
+
isValidContext(value.prevContext, contextType) &&
|
|
478
|
+
isValidContext(value.nextContext, contextType)
|
|
479
|
+
))
|
|
480
|
+
);
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
/**
|
|
484
|
+
* Type guard to check if a value is middleware context with strict type checking.
|
|
485
|
+
*/
|
|
486
|
+
export function isMiddlewareContext<C extends object>(
|
|
487
|
+
value: any,
|
|
488
|
+
contextType?: C
|
|
489
|
+
): value is MiddlewareContext<C> {
|
|
490
|
+
return (
|
|
491
|
+
value !== null &&
|
|
492
|
+
typeof value === 'object' &&
|
|
493
|
+
'transitionName' in value &&
|
|
494
|
+
'context' in value &&
|
|
495
|
+
'args' in value &&
|
|
496
|
+
typeof value.transitionName === 'string' &&
|
|
497
|
+
Array.isArray(value.args) &&
|
|
498
|
+
(!contextType || isValidContext(value.context, contextType))
|
|
499
|
+
);
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
/**
|
|
503
|
+
* Type guard to check if a value is middleware error with strict type checking.
|
|
504
|
+
*/
|
|
505
|
+
export function isMiddlewareError<C extends object>(
|
|
506
|
+
value: any,
|
|
507
|
+
contextType?: C
|
|
508
|
+
): value is MiddlewareError<C> {
|
|
509
|
+
return (
|
|
510
|
+
value !== null &&
|
|
511
|
+
typeof value === 'object' &&
|
|
512
|
+
'transitionName' in value &&
|
|
513
|
+
'context' in value &&
|
|
514
|
+
'args' in value &&
|
|
515
|
+
'error' in value &&
|
|
516
|
+
typeof value.transitionName === 'string' &&
|
|
517
|
+
Array.isArray(value.args) &&
|
|
518
|
+
value.error instanceof Error &&
|
|
519
|
+
(!contextType || isValidContext(value.context, contextType))
|
|
520
|
+
);
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
/**
|
|
524
|
+
* Type guard to check if a value is middleware hooks with strict type checking.
|
|
525
|
+
*/
|
|
526
|
+
export function isMiddlewareHooks<C extends object>(
|
|
527
|
+
value: any,
|
|
528
|
+
_contextType?: C
|
|
529
|
+
): value is MiddlewareHooks<C> {
|
|
530
|
+
if (value === null || typeof value !== 'object') return false;
|
|
531
|
+
|
|
532
|
+
const hooks = value as Partial<MiddlewareHooks<C>>;
|
|
533
|
+
|
|
534
|
+
// Check before hook
|
|
535
|
+
if ('before' in hooks && hooks.before !== undefined) {
|
|
536
|
+
if (typeof hooks.before !== 'function') return false;
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
// Check after hook
|
|
540
|
+
if ('after' in hooks && hooks.after !== undefined) {
|
|
541
|
+
if (typeof hooks.after !== 'function') return false;
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
// Check error hook
|
|
545
|
+
if ('error' in hooks && hooks.error !== undefined) {
|
|
546
|
+
if (typeof hooks.error !== 'function') return false;
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
return true;
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
/**
|
|
553
|
+
* Type guard to check if a value is middleware options with strict type checking.
|
|
554
|
+
*/
|
|
555
|
+
export function isMiddlewareOptions(value: any): value is MiddlewareOptions {
|
|
556
|
+
return (
|
|
557
|
+
value === undefined ||
|
|
558
|
+
(value !== null &&
|
|
559
|
+
typeof value === 'object' &&
|
|
560
|
+
('continueOnError' in value ? typeof value.continueOnError === 'boolean' : true) &&
|
|
561
|
+
('logErrors' in value ? typeof value.logErrors === 'boolean' : true) &&
|
|
562
|
+
('onError' in value ? typeof value.onError === 'function' || value.onError === undefined : true))
|
|
563
|
+
);
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
/**
|
|
567
|
+
* Helper function to validate context objects.
|
|
568
|
+
*/
|
|
569
|
+
function isValidContext<C extends object>(value: any, _contextType: C): value is C {
|
|
570
|
+
return value !== null && typeof value === 'object';
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
/**
|
|
574
|
+
* Type guard to check if a value is a named middleware with strict type checking.
|
|
575
|
+
*/
|
|
576
|
+
export function isNamedMiddleware<M extends BaseMachine<any>>(
|
|
577
|
+
value: any
|
|
578
|
+
): value is NamedMiddleware<M> {
|
|
579
|
+
return (
|
|
580
|
+
value !== null &&
|
|
581
|
+
typeof value === 'object' &&
|
|
582
|
+
'name' in value &&
|
|
583
|
+
'middleware' in value &&
|
|
584
|
+
typeof value.name === 'string' &&
|
|
585
|
+
isMiddlewareFn(value.middleware) &&
|
|
586
|
+
('description' in value ? typeof value.description === 'string' || value.description === undefined : true) &&
|
|
587
|
+
('priority' in value ? typeof value.priority === 'number' || value.priority === undefined : true)
|
|
588
|
+
);
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
/**
|
|
592
|
+
* Type guard to check if a value is pipeline config with strict type checking.
|
|
593
|
+
*/
|
|
594
|
+
export function isPipelineConfig(value: any): value is PipelineConfig {
|
|
595
|
+
return (
|
|
596
|
+
value === undefined ||
|
|
597
|
+
(value !== null &&
|
|
598
|
+
typeof value === 'object' &&
|
|
599
|
+
('continueOnError' in value ? typeof value.continueOnError === 'boolean' : true) &&
|
|
600
|
+
('logErrors' in value ? typeof value.logErrors === 'boolean' : true) &&
|
|
601
|
+
('onError' in value ? typeof value.onError === 'function' || value.onError === undefined : true))
|
|
602
|
+
);
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
// =============================================================================
|
|
606
|
+
// SECTION: GENERIC MIDDLEWARE BUILDER
|
|
607
|
+
// =============================================================================
|
|
608
|
+
|
|
609
|
+
/**
|
|
610
|
+
* Configuration for logging middleware.
|
|
611
|
+
*/
|
|
612
|
+
export interface LoggingOptions {
|
|
613
|
+
logger?: (message: string) => void;
|
|
614
|
+
includeArgs?: boolean;
|
|
615
|
+
includeContext?: boolean;
|
|
616
|
+
logLevel?: 'debug' | 'info' | 'warn' | 'error';
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
/**
|
|
620
|
+
* Configuration for analytics middleware.
|
|
621
|
+
*/
|
|
622
|
+
export interface AnalyticsOptions {
|
|
623
|
+
eventPrefix?: string;
|
|
624
|
+
includePrevContext?: boolean;
|
|
625
|
+
includeArgs?: boolean;
|
|
626
|
+
includeTiming?: boolean;
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
/**
|
|
630
|
+
* Configuration for validation middleware.
|
|
631
|
+
*/
|
|
632
|
+
export interface ValidationOptions {
|
|
633
|
+
throwOnFailure?: boolean;
|
|
634
|
+
logFailures?: boolean;
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
/**
|
|
638
|
+
* Configuration for error reporting middleware.
|
|
639
|
+
*/
|
|
640
|
+
export interface ErrorReportingOptions {
|
|
641
|
+
includeArgs?: boolean;
|
|
642
|
+
includeStackTrace?: boolean;
|
|
643
|
+
reportTo?: string[];
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
/**
|
|
647
|
+
* Configuration for performance monitoring middleware.
|
|
648
|
+
*/
|
|
649
|
+
export interface PerformanceOptions {
|
|
650
|
+
includeArgs?: boolean;
|
|
651
|
+
includeContext?: boolean;
|
|
652
|
+
warnThreshold?: number;
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
/**
|
|
656
|
+
* Configuration for retry middleware.
|
|
657
|
+
*/
|
|
658
|
+
export interface RetryOptions {
|
|
659
|
+
maxAttempts?: number;
|
|
660
|
+
maxRetries?: number;
|
|
661
|
+
shouldRetry?: (error: Error, attempt: number) => boolean;
|
|
662
|
+
backoffMs?: number | ((attempt: number) => number);
|
|
663
|
+
delay?: number | ((attempt: number) => number);
|
|
664
|
+
backoffMultiplier?: number;
|
|
665
|
+
onRetry?: (error: Error, attempt: number) => void;
|
|
666
|
+
}
|
|
667
|
+
|
|
668
|
+
/**
|
|
669
|
+
* Configuration for history middleware.
|
|
670
|
+
*/
|
|
671
|
+
export interface HistoryOptions {
|
|
672
|
+
maxSize?: number;
|
|
673
|
+
serializer?: Serializer<any[]>;
|
|
674
|
+
onEntry?: (entry: HistoryEntry) => void;
|
|
675
|
+
includeTimestamps?: boolean;
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
/**
|
|
679
|
+
* Configuration for snapshot middleware.
|
|
680
|
+
*/
|
|
681
|
+
export interface SnapshotOptions {
|
|
682
|
+
maxSize?: number;
|
|
683
|
+
serializer?: Serializer<Context<any>>;
|
|
684
|
+
captureSnapshot?: (before: Context<any>, after: Context<any>) => any;
|
|
685
|
+
onlyOnChange?: boolean;
|
|
686
|
+
includeDiff?: boolean;
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
/**
|
|
690
|
+
* Configuration for time travel middleware.
|
|
691
|
+
*/
|
|
692
|
+
export interface TimeTravelOptions {
|
|
693
|
+
maxSize?: number;
|
|
694
|
+
serializer?: Serializer;
|
|
695
|
+
onRecord?: (type: 'history' | 'snapshot', data: any) => void;
|
|
696
|
+
enableReplay?: boolean;
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
/**
|
|
700
|
+
* Generic middleware builder with perfect TypeScript inference.
|
|
701
|
+
* Provides a fluent API for configuring and applying middleware.
|
|
702
|
+
*/
|
|
703
|
+
export class MiddlewareBuilder<M extends BaseMachine<any>> {
|
|
704
|
+
private middlewares: Array<(machine: any) => any> = [];
|
|
705
|
+
|
|
706
|
+
constructor(private machine: M) {}
|
|
707
|
+
|
|
708
|
+
/**
|
|
709
|
+
* Add logging middleware with type-safe configuration.
|
|
710
|
+
*/
|
|
711
|
+
withLogging(options?: LoggingOptions): MiddlewareBuilder<M> {
|
|
712
|
+
this.middlewares.push((machine: M) => withLogging(machine, options));
|
|
713
|
+
return this;
|
|
714
|
+
}
|
|
715
|
+
|
|
716
|
+
/**
|
|
717
|
+
* Add analytics middleware with type-safe configuration.
|
|
718
|
+
*/
|
|
719
|
+
withAnalytics(
|
|
720
|
+
track: (event: string, data?: any) => void,
|
|
721
|
+
options?: AnalyticsOptions
|
|
722
|
+
): MiddlewareBuilder<M> {
|
|
723
|
+
this.middlewares.push((machine: M) => withAnalytics(machine, track, options));
|
|
724
|
+
return this;
|
|
725
|
+
}
|
|
726
|
+
|
|
727
|
+
/**
|
|
728
|
+
* Add validation middleware with type-safe configuration.
|
|
729
|
+
*/
|
|
730
|
+
withValidation(
|
|
731
|
+
validator: (ctx: MiddlewareContext<Context<M>>) => boolean | void,
|
|
732
|
+
_options?: ValidationOptions
|
|
733
|
+
): MiddlewareBuilder<M> {
|
|
734
|
+
this.middlewares.push((machine: M) => withValidation(machine, validator));
|
|
735
|
+
return this;
|
|
736
|
+
}
|
|
737
|
+
|
|
738
|
+
/**
|
|
739
|
+
* Add permission checking middleware with type-safe configuration.
|
|
740
|
+
*/
|
|
741
|
+
withPermissions(
|
|
742
|
+
checker: (ctx: MiddlewareContext<Context<M>>) => boolean
|
|
743
|
+
): MiddlewareBuilder<M> {
|
|
744
|
+
this.middlewares.push((machine: M) => withPermissions(machine, checker));
|
|
745
|
+
return this;
|
|
746
|
+
}
|
|
747
|
+
|
|
748
|
+
/**
|
|
749
|
+
* Add error reporting middleware with type-safe configuration.
|
|
750
|
+
*/
|
|
751
|
+
withErrorReporting(
|
|
752
|
+
reporter: (error: Error, ctx: MiddlewareError<Context<M>>) => void,
|
|
753
|
+
options?: ErrorReportingOptions
|
|
754
|
+
): MiddlewareBuilder<M> {
|
|
755
|
+
this.middlewares.push((machine: M) => withErrorReporting(machine, reporter, options));
|
|
756
|
+
return this;
|
|
757
|
+
}
|
|
758
|
+
|
|
759
|
+
/**
|
|
760
|
+
* Add performance monitoring middleware with type-safe configuration.
|
|
761
|
+
*/
|
|
762
|
+
withPerformanceMonitoring(
|
|
763
|
+
tracker: (metric: { transitionName: string; duration: number; context: Context<M> }) => void,
|
|
764
|
+
_options?: PerformanceOptions
|
|
765
|
+
): MiddlewareBuilder<M> {
|
|
766
|
+
this.middlewares.push((machine: M) => withPerformanceMonitoring(machine, tracker));
|
|
767
|
+
return this;
|
|
768
|
+
}
|
|
769
|
+
|
|
770
|
+
/**
|
|
771
|
+
* Add retry middleware with type-safe configuration.
|
|
772
|
+
*/
|
|
773
|
+
withRetry(options?: RetryOptions): MiddlewareBuilder<M> {
|
|
774
|
+
this.middlewares.push((machine: M) => withRetry(machine, options));
|
|
775
|
+
return this;
|
|
776
|
+
}
|
|
777
|
+
|
|
778
|
+
/**
|
|
779
|
+
* Add history tracking middleware with type-safe configuration.
|
|
780
|
+
*/
|
|
781
|
+
withHistory(options?: HistoryOptions): MiddlewareBuilder<WithHistory<M>> {
|
|
782
|
+
this.middlewares.push((machine: M) => withHistory(machine, options));
|
|
783
|
+
return this as unknown as MiddlewareBuilder<WithHistory<M>>;
|
|
784
|
+
}
|
|
785
|
+
|
|
786
|
+
/**
|
|
787
|
+
* Add snapshot tracking middleware with type-safe configuration.
|
|
788
|
+
*/
|
|
789
|
+
withSnapshot(options?: SnapshotOptions): MiddlewareBuilder<WithSnapshot<M>> {
|
|
790
|
+
this.middlewares.push((machine: M) => withSnapshot(machine, options));
|
|
791
|
+
return this as unknown as MiddlewareBuilder<WithSnapshot<M>>;
|
|
792
|
+
}
|
|
793
|
+
|
|
794
|
+
/**
|
|
795
|
+
* Add time travel middleware with type-safe configuration.
|
|
796
|
+
*/
|
|
797
|
+
withTimeTravel(options?: TimeTravelOptions): MiddlewareBuilder<WithTimeTravel<M>> {
|
|
798
|
+
this.middlewares.push((machine: M) => withTimeTravel(machine, options));
|
|
799
|
+
return this as unknown as MiddlewareBuilder<WithTimeTravel<M>>;
|
|
800
|
+
}
|
|
801
|
+
|
|
802
|
+
/**
|
|
803
|
+
* Add debugging middleware (combination of history, snapshot, and time travel).
|
|
804
|
+
*/
|
|
805
|
+
withDebugging(): MiddlewareBuilder<WithDebugging<M>> {
|
|
806
|
+
this.middlewares.push((machine: M) => withDebugging(machine));
|
|
807
|
+
return this as unknown as MiddlewareBuilder<WithDebugging<M>>;
|
|
808
|
+
}
|
|
809
|
+
|
|
810
|
+
/**
|
|
811
|
+
* Add a custom middleware function.
|
|
812
|
+
*/
|
|
813
|
+
withCustom<R extends BaseMachine<any> = M>(
|
|
814
|
+
middleware: MiddlewareFn<M, R>
|
|
815
|
+
): MiddlewareBuilder<R> {
|
|
816
|
+
this.middlewares.push(middleware);
|
|
817
|
+
return this as unknown as MiddlewareBuilder<R>;
|
|
818
|
+
}
|
|
819
|
+
|
|
820
|
+
/**
|
|
821
|
+
* Add a conditional middleware.
|
|
822
|
+
*/
|
|
823
|
+
withConditional(
|
|
824
|
+
middleware: MiddlewareFn<M>,
|
|
825
|
+
predicate: (machine: M) => boolean
|
|
826
|
+
): MiddlewareBuilder<M> {
|
|
827
|
+
this.middlewares.push(when(middleware, predicate));
|
|
828
|
+
return this;
|
|
829
|
+
}
|
|
830
|
+
|
|
831
|
+
/**
|
|
832
|
+
* Build the final machine with all configured middleware applied.
|
|
833
|
+
*/
|
|
834
|
+
build(): M {
|
|
835
|
+
let result = this.machine;
|
|
836
|
+
for (const middleware of this.middlewares) {
|
|
837
|
+
result = middleware(result);
|
|
838
|
+
}
|
|
839
|
+
return result;
|
|
840
|
+
}
|
|
841
|
+
|
|
842
|
+
/**
|
|
843
|
+
* Get the middleware chain without building (for inspection or further composition).
|
|
844
|
+
*/
|
|
845
|
+
getChain(): Array<(machine: any) => any> {
|
|
846
|
+
return [...this.middlewares];
|
|
847
|
+
}
|
|
848
|
+
|
|
849
|
+
/**
|
|
850
|
+
* Clear all configured middleware.
|
|
851
|
+
*/
|
|
852
|
+
clear(): MiddlewareBuilder<M> {
|
|
853
|
+
this.middlewares = [];
|
|
854
|
+
return this;
|
|
855
|
+
}
|
|
856
|
+
}
|
|
857
|
+
|
|
858
|
+
/**
|
|
859
|
+
* Create a typed middleware builder for a machine.
|
|
860
|
+
* Provides perfect TypeScript inference for middleware configuration.
|
|
861
|
+
*
|
|
862
|
+
* @example
|
|
863
|
+
* ```typescript
|
|
864
|
+
* const enhancedMachine = middlewareBuilder(myMachine)
|
|
865
|
+
* .withLogging({ includeArgs: true })
|
|
866
|
+
* .withAnalytics(trackEvent)
|
|
867
|
+
* .withHistory({ maxSize: 100 })
|
|
868
|
+
* .withRetry({ maxAttempts: 3 })
|
|
869
|
+
* .build();
|
|
870
|
+
* ```
|
|
871
|
+
*/
|
|
872
|
+
export function middlewareBuilder<M extends BaseMachine<any>>(machine: M): MiddlewareBuilder<M> {
|
|
873
|
+
return new MiddlewareBuilder(machine);
|
|
874
|
+
}
|
|
875
|
+
|
|
876
|
+
/**
|
|
877
|
+
* Create a middleware factory function with pre-configured options.
|
|
878
|
+
* Useful for creating reusable middleware configurations.
|
|
879
|
+
*/
|
|
880
|
+
export function createMiddlewareFactory<M extends BaseMachine<any>>(
|
|
881
|
+
defaultOptions: {
|
|
882
|
+
logging?: LoggingOptions;
|
|
883
|
+
analytics?: { track: (event: string, data?: any) => void; options?: AnalyticsOptions };
|
|
884
|
+
history?: HistoryOptions;
|
|
885
|
+
snapshot?: SnapshotOptions;
|
|
886
|
+
timeTravel?: TimeTravelOptions;
|
|
887
|
+
retry?: RetryOptions;
|
|
888
|
+
} = {}
|
|
889
|
+
) {
|
|
890
|
+
return {
|
|
891
|
+
create: (machine: M) => {
|
|
892
|
+
const builder = middlewareBuilder(machine);
|
|
893
|
+
|
|
894
|
+
if (defaultOptions.logging) {
|
|
895
|
+
builder.withLogging(defaultOptions.logging);
|
|
896
|
+
}
|
|
897
|
+
|
|
898
|
+
if (defaultOptions.analytics) {
|
|
899
|
+
builder.withAnalytics(
|
|
900
|
+
defaultOptions.analytics.track,
|
|
901
|
+
defaultOptions.analytics.options
|
|
902
|
+
);
|
|
903
|
+
}
|
|
904
|
+
|
|
905
|
+
if (defaultOptions.history) {
|
|
906
|
+
builder.withHistory(defaultOptions.history);
|
|
907
|
+
}
|
|
908
|
+
|
|
909
|
+
if (defaultOptions.snapshot) {
|
|
910
|
+
builder.withSnapshot(defaultOptions.snapshot);
|
|
911
|
+
}
|
|
912
|
+
|
|
913
|
+
if (defaultOptions.timeTravel) {
|
|
914
|
+
builder.withTimeTravel(defaultOptions.timeTravel);
|
|
915
|
+
}
|
|
916
|
+
|
|
917
|
+
if (defaultOptions.retry) {
|
|
918
|
+
builder.withRetry(defaultOptions.retry);
|
|
919
|
+
}
|
|
920
|
+
|
|
921
|
+
return builder;
|
|
922
|
+
}
|
|
923
|
+
};
|
|
924
|
+
}
|
|
925
|
+
|
|
926
|
+
// =============================================================================
|
|
927
|
+
// SECTION: UTILITY FUNCTIONS
|
|
928
|
+
// =============================================================================
|
|
929
|
+
|
|
930
|
+
// =============================================================================
|
|
931
|
+
// SECTION: COMMON COMBINATIONS
|
|
932
|
+
// =============================================================================
|
|
933
|
+
|
|
934
|
+
/**
|
|
935
|
+
* Common middleware combination types for better DX.
|
|
936
|
+
*/
|
|
937
|
+
export type WithDebugging<M extends BaseMachine<any>> = WithTimeTravel<WithSnapshot<WithHistory<M>>>;
|
|
938
|
+
|
|
939
|
+
/**
|
|
940
|
+
* Convenience function for the most common debugging middleware stack.
|
|
941
|
+
*/
|
|
942
|
+
export function withDebugging<M extends BaseMachine<any>>(machine: M): WithDebugging<M> {
|
|
943
|
+
return withTimeTravel(withSnapshot(withHistory(machine)));
|
|
944
|
+
}
|