@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.
Files changed (50) hide show
  1. package/README.md +90 -15
  2. package/dist/cjs/development/core.js +1852 -0
  3. package/dist/cjs/development/core.js.map +7 -0
  4. package/dist/cjs/development/index.js +1348 -1374
  5. package/dist/cjs/development/index.js.map +4 -4
  6. package/dist/cjs/production/core.js +1 -0
  7. package/dist/cjs/production/index.js +5 -5
  8. package/dist/esm/development/core.js +1829 -0
  9. package/dist/esm/development/core.js.map +7 -0
  10. package/dist/esm/development/index.js +1348 -1374
  11. package/dist/esm/development/index.js.map +4 -4
  12. package/dist/esm/production/core.js +1 -0
  13. package/dist/esm/production/index.js +5 -5
  14. package/dist/types/core.d.ts +18 -0
  15. package/dist/types/core.d.ts.map +1 -0
  16. package/dist/types/functional-combinators.d.ts +60 -5
  17. package/dist/types/functional-combinators.d.ts.map +1 -1
  18. package/dist/types/index.d.ts +242 -19
  19. package/dist/types/index.d.ts.map +1 -1
  20. package/dist/types/middleware/composition.d.ts +460 -0
  21. package/dist/types/middleware/composition.d.ts.map +1 -0
  22. package/dist/types/middleware/core.d.ts +196 -0
  23. package/dist/types/middleware/core.d.ts.map +1 -0
  24. package/dist/types/middleware/history.d.ts +54 -0
  25. package/dist/types/middleware/history.d.ts.map +1 -0
  26. package/dist/types/middleware/index.d.ts +10 -0
  27. package/dist/types/middleware/index.d.ts.map +1 -0
  28. package/dist/types/middleware/snapshot.d.ts +63 -0
  29. package/dist/types/middleware/snapshot.d.ts.map +1 -0
  30. package/dist/types/middleware/time-travel.d.ts +81 -0
  31. package/dist/types/middleware/time-travel.d.ts.map +1 -0
  32. package/package.json +19 -6
  33. package/src/core.ts +167 -0
  34. package/src/entry-react.ts +9 -0
  35. package/src/entry-solid.ts +9 -0
  36. package/src/functional-combinators.ts +76 -3
  37. package/src/index.ts +376 -102
  38. package/src/middleware/composition.ts +944 -0
  39. package/src/middleware/core.ts +573 -0
  40. package/src/middleware/history.ts +104 -0
  41. package/src/middleware/index.ts +13 -0
  42. package/src/middleware/snapshot.ts +153 -0
  43. package/src/middleware/time-travel.ts +236 -0
  44. package/src/middleware.ts +735 -1614
  45. package/src/prototype_functional.ts +46 -0
  46. package/src/reproduce_issue.ts +26 -0
  47. package/dist/types/middleware.d.ts +0 -1048
  48. package/dist/types/middleware.d.ts.map +0 -1
  49. package/dist/types/runtime-extract.d.ts +0 -53
  50. 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
+ }