@vedmalex/statemachine 1.0.0-beta.1

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/dist/index.cjs ADDED
@@ -0,0 +1,3989 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ EnhancedErrorHandler: () => ErrorHandler,
24
+ EnhancedStateMachineError: () => EnhancedStateMachineError,
25
+ ErrorAnalytics: () => ErrorAnalytics,
26
+ ErrorCategory: () => ErrorCategory,
27
+ ErrorSeverity: () => ErrorSeverity,
28
+ FallbackStateRecoveryStrategy: () => FallbackStateRecoveryStrategy,
29
+ LocalStorageAdapter: () => LocalStorageAdapter,
30
+ MemoryAdapter: () => MemoryAdapter,
31
+ RetryRecoveryStrategy: () => RetryRecoveryStrategy,
32
+ ServerAdapter: () => ServerAdapter,
33
+ SessionStorageAdapter: () => SessionStorageAdapter,
34
+ StateMachine: () => StateMachine,
35
+ StateMachineError: () => StateMachineError,
36
+ createEnhancedError: () => createEnhancedError,
37
+ createMachine: () => createMachine,
38
+ isAdapter: () => isAdapter,
39
+ isRecoverableError: () => isRecoverableError,
40
+ isValidConfig: () => isValidConfig,
41
+ validateConfig: () => validateConfig,
42
+ validateConfigStrict: () => validateConfigStrict
43
+ });
44
+ module.exports = __toCommonJS(index_exports);
45
+
46
+ // src/scheduler.ts
47
+ var TimerScheduler = class {
48
+ // Min-Heap: элемент с наименьшим executeAt всегда в index 0
49
+ heap = [];
50
+ // Set для быстрой проверки отмененных задач (Lazy Cancellation)
51
+ // Это позволяет удалять задачи за O(1) без поиска в куче O(n)
52
+ activeTokens = /* @__PURE__ */ new WeakSet();
53
+ intervalId = null;
54
+ pollingInterval = 100;
55
+ // ms
56
+ constructor() {
57
+ }
58
+ /**
59
+ * Настройка режима опроса
60
+ * @param interval Интервал проверки в мс. Если 0 или null - авто-тик выключается (ручной режим)
61
+ */
62
+ setPollingInterval(interval) {
63
+ this.stop();
64
+ if (interval !== null && interval > 0) {
65
+ this.pollingInterval = interval;
66
+ this.start();
67
+ }
68
+ }
69
+ /**
70
+ * Проверка активности планировщика
71
+ */
72
+ isActive() {
73
+ return this.intervalId !== null;
74
+ }
75
+ /**
76
+ * Запуск единого таймера
77
+ */
78
+ start() {
79
+ if (this.intervalId) return;
80
+ this.intervalId = setInterval(() => this.process(), this.pollingInterval);
81
+ }
82
+ /**
83
+ * Остановка единого таймера
84
+ */
85
+ stop() {
86
+ if (this.intervalId) {
87
+ clearInterval(this.intervalId);
88
+ this.intervalId = null;
89
+ }
90
+ }
91
+ /**
92
+ * Добавить задачу
93
+ * @param delay задержка в мс
94
+ * @param callback функция для выполнения
95
+ * @returns токен для отмены
96
+ */
97
+ schedule(delay, callback) {
98
+ const token = {};
99
+ const executeAt = Date.now() + delay;
100
+ const task = { token, executeAt, callback };
101
+ this.activeTokens.add(token);
102
+ this.insert(task);
103
+ return token;
104
+ }
105
+ /**
106
+ * Отменить задачу
107
+ * @param token токен задачи
108
+ */
109
+ cancel(token) {
110
+ this.activeTokens.delete(token);
111
+ }
112
+ /**
113
+ * Обработать очередь (вызывается таймером или вручную)
114
+ */
115
+ process(now = Date.now()) {
116
+ while (this.heap.length > 0) {
117
+ const task = this.heap[0];
118
+ if (task === void 0) break;
119
+ if (task.executeAt > now) {
120
+ break;
121
+ }
122
+ this.extractMin();
123
+ if (this.activeTokens.has(task.token)) {
124
+ this.activeTokens.delete(task.token);
125
+ try {
126
+ task.callback();
127
+ } catch (e) {
128
+ console.error("Error in scheduled task", e);
129
+ }
130
+ }
131
+ }
132
+ }
133
+ /**
134
+ * Очистить все задачи
135
+ */
136
+ clear() {
137
+ this.heap = [];
138
+ this.activeTokens = /* @__PURE__ */ new WeakSet();
139
+ }
140
+ // === Min-Heap Implementation ===
141
+ insert(task) {
142
+ this.heap.push(task);
143
+ this.bubbleUp(this.heap.length - 1);
144
+ }
145
+ extractMin() {
146
+ if (this.heap.length === 0) return void 0;
147
+ const min = this.heap[0];
148
+ if (min === void 0) return void 0;
149
+ const last = this.heap.pop();
150
+ if (this.heap.length > 0 && last !== void 0) {
151
+ this.heap[0] = last;
152
+ this.sinkDown(0);
153
+ }
154
+ return min;
155
+ }
156
+ bubbleUp(index) {
157
+ while (index > 0) {
158
+ const parentIndex = Math.floor((index - 1) / 2);
159
+ const parent = this.heap[parentIndex];
160
+ const current = this.heap[index];
161
+ if (parent === void 0 || current === void 0) break;
162
+ if (parent.executeAt <= current.executeAt) break;
163
+ this.swap(index, parentIndex);
164
+ index = parentIndex;
165
+ }
166
+ }
167
+ sinkDown(index) {
168
+ const length = this.heap.length;
169
+ const element = this.heap[index];
170
+ if (element === void 0) return;
171
+ while (true) {
172
+ const leftChildIdx = 2 * index + 1;
173
+ const rightChildIdx = 2 * index + 2;
174
+ let swapIdx = null;
175
+ if (leftChildIdx < length) {
176
+ const leftChild = this.heap[leftChildIdx];
177
+ if (leftChild !== void 0 && leftChild.executeAt < element.executeAt) {
178
+ swapIdx = leftChildIdx;
179
+ }
180
+ }
181
+ if (rightChildIdx < length) {
182
+ const rightChild = this.heap[rightChildIdx];
183
+ if (rightChild !== void 0) {
184
+ if (swapIdx === null && rightChild.executeAt < element.executeAt || /* c8 ignore next */
185
+ swapIdx !== null && this.heap[swapIdx] !== void 0 && rightChild.executeAt < this.heap[swapIdx].executeAt) {
186
+ swapIdx = rightChildIdx;
187
+ }
188
+ }
189
+ }
190
+ if (swapIdx === null) break;
191
+ this.swap(index, swapIdx);
192
+ index = swapIdx;
193
+ }
194
+ }
195
+ swap(a, b) {
196
+ const temp = this.heap[a];
197
+ const other = this.heap[b];
198
+ if (temp === void 0 || other === void 0) return;
199
+ this.heap[a] = other;
200
+ this.heap[b] = temp;
201
+ }
202
+ };
203
+ function createDefaultScheduler() {
204
+ return new TimerScheduler();
205
+ }
206
+
207
+ // src/logger.ts
208
+ var LogLevel = {
209
+ DEBUG: 0,
210
+ INFO: 1,
211
+ WARN: 2,
212
+ ERROR: 3,
213
+ FATAL: 4,
214
+ OFF: 5
215
+ };
216
+ var LogLevelNames = {
217
+ [LogLevel.DEBUG]: "DEBUG",
218
+ [LogLevel.INFO]: "INFO",
219
+ [LogLevel.WARN]: "WARN",
220
+ [LogLevel.ERROR]: "ERROR",
221
+ [LogLevel.FATAL]: "FATAL",
222
+ [LogLevel.OFF]: "OFF"
223
+ };
224
+ var DEFAULT_LOGGER_CONFIG = {
225
+ level: LogLevel.WARN,
226
+ // Only warnings and above in production
227
+ enableConsole: true,
228
+ enableStructuredLogging: false,
229
+ timestampFormat: "iso",
230
+ includeStackTrace: false
231
+ };
232
+ var ConsoleAppender = class {
233
+ config;
234
+ constructor(config) {
235
+ this.config = config;
236
+ }
237
+ append(entry) {
238
+ if (!this.config.enableConsole) return;
239
+ const timestamp = this.formatTimestamp(entry.timestamp);
240
+ const levelName = LogLevelNames[entry.level];
241
+ const prefix = `[${timestamp}] [${levelName}] [${entry.source}]`;
242
+ if (this.config.enableStructuredLogging) {
243
+ const structuredEntry = {
244
+ timestamp: entry.timestamp,
245
+ level: levelName,
246
+ source: entry.source,
247
+ message: entry.message,
248
+ context: entry.context,
249
+ error: entry.error?.message
250
+ };
251
+ console.log(JSON.stringify(structuredEntry));
252
+ } else {
253
+ const message = `${prefix} ${entry.message}`;
254
+ switch (entry.level) {
255
+ case LogLevel.DEBUG:
256
+ case LogLevel.INFO:
257
+ console.log(message, entry.context || "");
258
+ break;
259
+ case LogLevel.WARN:
260
+ console.warn(message, entry.context || "");
261
+ break;
262
+ case LogLevel.ERROR:
263
+ case LogLevel.FATAL:
264
+ console.error(message, entry.context || "", entry.error || "");
265
+ if (this.config.includeStackTrace && entry.error) {
266
+ console.error(entry.error.stack);
267
+ }
268
+ break;
269
+ }
270
+ }
271
+ }
272
+ formatTimestamp(timestamp) {
273
+ switch (this.config.timestampFormat) {
274
+ case "unix":
275
+ return timestamp.toString();
276
+ case "relative":
277
+ return `+${timestamp - Date.now()}ms`;
278
+ case "iso":
279
+ default:
280
+ return new Date(timestamp).toISOString();
281
+ }
282
+ }
283
+ };
284
+ var Logger = class _Logger {
285
+ config;
286
+ appenders = [];
287
+ source;
288
+ constructor(source, config = {}) {
289
+ this.source = source;
290
+ this.config = { ...DEFAULT_LOGGER_CONFIG, ...config };
291
+ this.appenders.push(new ConsoleAppender(this.config));
292
+ }
293
+ // Add custom appender
294
+ addAppender(appender) {
295
+ this.appenders.push(appender);
296
+ }
297
+ // Remove appender
298
+ removeAppender(appender) {
299
+ const index = this.appenders.indexOf(appender);
300
+ if (index > -1) {
301
+ this.appenders.splice(index, 1);
302
+ }
303
+ }
304
+ // Update configuration
305
+ updateConfig(config) {
306
+ this.config = { ...this.config, ...config };
307
+ }
308
+ // Check if level is enabled
309
+ isLevelEnabled(level) {
310
+ return level >= this.config.level && this.config.level !== LogLevel.OFF;
311
+ }
312
+ // Core logging method
313
+ log(level, message, context, error) {
314
+ if (!this.isLevelEnabled(level)) return;
315
+ const entry = {
316
+ timestamp: Date.now(),
317
+ level,
318
+ message,
319
+ ...context !== void 0 ? { context } : {},
320
+ ...error !== void 0 ? { error } : {},
321
+ source: this.source
322
+ };
323
+ this.appenders.forEach((appender) => {
324
+ try {
325
+ appender.append(entry);
326
+ } catch (e) {
327
+ console.error("Logger appender failed:", e);
328
+ }
329
+ });
330
+ }
331
+ // Convenience methods
332
+ debug(message, context) {
333
+ this.log(LogLevel.DEBUG, message, context);
334
+ }
335
+ info(message, context) {
336
+ this.log(LogLevel.INFO, message, context);
337
+ }
338
+ warn(message, context) {
339
+ this.log(LogLevel.WARN, message, context);
340
+ }
341
+ error(message, context, error) {
342
+ this.log(LogLevel.ERROR, message, context, error);
343
+ }
344
+ fatal(message, context, error) {
345
+ this.log(LogLevel.FATAL, message, context, error);
346
+ }
347
+ // Create child logger with additional context
348
+ child(childSource, _additionalContext) {
349
+ const childLogger = new _Logger(`${this.source}.${childSource}`, this.config);
350
+ childLogger.appenders = this.appenders;
351
+ return childLogger;
352
+ }
353
+ };
354
+ var LoggerFactory = class _LoggerFactory {
355
+ static defaultConfig = DEFAULT_LOGGER_CONFIG;
356
+ static loggers = /* @__PURE__ */ new Map();
357
+ static setDefaultConfig(config) {
358
+ _LoggerFactory.defaultConfig = { ...DEFAULT_LOGGER_CONFIG, ...config };
359
+ }
360
+ static getLogger(source, config) {
361
+ const key = source;
362
+ if (!_LoggerFactory.loggers.has(key)) {
363
+ const loggerConfig = config ? { ..._LoggerFactory.defaultConfig, ...config } : _LoggerFactory.defaultConfig;
364
+ _LoggerFactory.loggers.set(key, new Logger(source, loggerConfig));
365
+ }
366
+ return _LoggerFactory.loggers.get(key);
367
+ }
368
+ static clearLoggers() {
369
+ _LoggerFactory.loggers.clear();
370
+ }
371
+ };
372
+ var getLogger = LoggerFactory.getLogger;
373
+ var stateMachineLogger = getLogger("StateMachine");
374
+ var securityLogger = getLogger("Security");
375
+
376
+ // src/security.ts
377
+ var BUILTIN_PATTERNS = [
378
+ {
379
+ pattern: /\beval\s*\(/gi,
380
+ risk: "critical",
381
+ description: "Direct eval() usage",
382
+ category: "eval"
383
+ },
384
+ {
385
+ pattern: /\bFunction\s*\(/gi,
386
+ risk: "high",
387
+ description: "Dynamic Function constructor call",
388
+ category: "eval"
389
+ },
390
+ {
391
+ pattern: /new\s+Function\s*\(/gi,
392
+ risk: "high",
393
+ description: "Dynamic function creation",
394
+ category: "eval"
395
+ },
396
+ {
397
+ pattern: /setTimeout\s*\(\s*['"`][^'"`]*['"`]/gi,
398
+ risk: "high",
399
+ description: "setTimeout with string code",
400
+ category: "eval"
401
+ },
402
+ {
403
+ pattern: /setInterval\s*\(\s*['"`][^'"`]*['"`]/gi,
404
+ risk: "high",
405
+ description: "setInterval with string code",
406
+ category: "eval"
407
+ },
408
+ {
409
+ pattern: /\bfetch\s*\(/gi,
410
+ risk: "medium",
411
+ description: "Network request via fetch",
412
+ category: "network"
413
+ },
414
+ {
415
+ pattern: /XMLHttpRequest/gi,
416
+ risk: "medium",
417
+ description: "XMLHttpRequest usage",
418
+ category: "network"
419
+ },
420
+ {
421
+ pattern: /WebSocket/gi,
422
+ risk: "medium",
423
+ description: "WebSocket connection",
424
+ category: "network"
425
+ },
426
+ {
427
+ pattern: /require\s*\(\s*['"`]fs['"`]\s*\)/gi,
428
+ risk: "high",
429
+ description: 'File system access via require("fs")',
430
+ category: "filesystem"
431
+ },
432
+ {
433
+ pattern: /import.*from\s*['"`]fs['"`]/gi,
434
+ risk: "high",
435
+ description: "File system access via import",
436
+ category: "filesystem"
437
+ },
438
+ {
439
+ pattern: /document\./gi,
440
+ risk: "medium",
441
+ description: "DOM access",
442
+ category: "dom"
443
+ },
444
+ {
445
+ pattern: /window\./gi,
446
+ risk: "medium",
447
+ description: "Window object access",
448
+ category: "dom"
449
+ },
450
+ {
451
+ pattern: /innerHTML/gi,
452
+ risk: "high",
453
+ description: "innerHTML manipulation (XSS risk)",
454
+ category: "dom"
455
+ },
456
+ {
457
+ pattern: /global\./gi,
458
+ risk: "high",
459
+ description: "Global scope access",
460
+ category: "global"
461
+ },
462
+ {
463
+ pattern: /process\./gi,
464
+ risk: "high",
465
+ description: "Process object access",
466
+ category: "process"
467
+ },
468
+ {
469
+ pattern: /child_process/gi,
470
+ risk: "critical",
471
+ description: "Child process execution",
472
+ category: "process"
473
+ },
474
+ {
475
+ pattern: /exec\s*\(/gi,
476
+ risk: "critical",
477
+ description: "Command execution",
478
+ category: "process"
479
+ },
480
+ {
481
+ pattern: /require\s*\(/gi,
482
+ risk: "critical",
483
+ description: "CommonJS require",
484
+ category: "module"
485
+ },
486
+ {
487
+ pattern: /import\s*\(/gi,
488
+ risk: "critical",
489
+ description: "Dynamic import",
490
+ category: "module"
491
+ },
492
+ {
493
+ pattern: /__proto__/gi,
494
+ risk: "critical",
495
+ description: "Prototype pollution",
496
+ category: "prototype"
497
+ },
498
+ {
499
+ pattern: /constructor/gi,
500
+ risk: "critical",
501
+ description: "Constructor access",
502
+ category: "prototype"
503
+ },
504
+ {
505
+ pattern: /prototype/gi,
506
+ risk: "critical",
507
+ description: "Prototype access",
508
+ category: "prototype"
509
+ },
510
+ {
511
+ pattern: /\[['"]__proto__['"]\]/gi,
512
+ risk: "critical",
513
+ description: "Prototype pollution via bracket notation",
514
+ category: "prototype"
515
+ },
516
+ {
517
+ pattern: /constructor\s*\[/gi,
518
+ risk: "critical",
519
+ description: "Constructor access via bracket notation",
520
+ category: "prototype"
521
+ }
522
+ ];
523
+ var DEFAULT_SECURITY_CONFIG = {
524
+ allowedFunctionNames: /* @__PURE__ */ new Set([
525
+ // Common safe function patterns
526
+ "onEnter",
527
+ "onExit",
528
+ "onBeforeEnter",
529
+ "onAfterEnter",
530
+ "onBeforeExit",
531
+ "onAfterExit",
532
+ "onTransition",
533
+ "onError",
534
+ "guard",
535
+ "onBefore",
536
+ "onAfter",
537
+ "onSuccess",
538
+ // Internal serializer function names (used as metadata)
539
+ "config_onError",
540
+ "state_onError",
541
+ "event_onBefore",
542
+ "event_onAfter",
543
+ "event_onSuccess",
544
+ "event_onError",
545
+ "transition_guard",
546
+ "transition_onTransition",
547
+ "transition_onError",
548
+ "invoke_cond",
549
+ "invoke_action",
550
+ // Legacy deserialization marker
551
+ "legacy_deserialization"
552
+ ]),
553
+ maxFunctionLength: 1e4,
554
+ // 10KB limit
555
+ enableValidation: true,
556
+ customPatterns: []
557
+ };
558
+ var FunctionValidator = class {
559
+ config;
560
+ allPatterns;
561
+ constructor(config = {}) {
562
+ this.config = { ...DEFAULT_SECURITY_CONFIG, ...config };
563
+ this.allPatterns = [
564
+ ...BUILTIN_PATTERNS,
565
+ ...this.config.customPatterns ?? []
566
+ ];
567
+ }
568
+ addCustomPattern(pattern) {
569
+ this.allPatterns.push(pattern);
570
+ }
571
+ /**
572
+ * Validates function body for security threats.
573
+ *
574
+ * Never throws: returns a structured result.
575
+ */
576
+ validate(body, functionName) {
577
+ if (!this.config.enableValidation) {
578
+ return {
579
+ isValid: true,
580
+ errors: [],
581
+ riskLevel: "low",
582
+ detectedPatterns: []
583
+ };
584
+ }
585
+ const errors = [];
586
+ const detectedPatterns = [];
587
+ let riskLevel = "low";
588
+ if (body.length > this.config.maxFunctionLength) {
589
+ errors.push(
590
+ `Function body exceeds maximum length of ${this.config.maxFunctionLength} characters`
591
+ );
592
+ riskLevel = "high";
593
+ }
594
+ const riskPriority = {
595
+ low: 0,
596
+ medium: 1,
597
+ high: 2,
598
+ critical: 3
599
+ };
600
+ for (const { pattern, risk, description } of this.allPatterns) {
601
+ if (pattern.global || pattern.sticky) {
602
+ pattern.lastIndex = 0;
603
+ }
604
+ if (pattern.test(body)) {
605
+ errors.push(`Function contains dangerous pattern: ${description}`);
606
+ detectedPatterns.push(description);
607
+ if (riskPriority[risk] > riskPriority[riskLevel]) {
608
+ riskLevel = risk;
609
+ }
610
+ }
611
+ }
612
+ if (functionName && !this.config.allowedFunctionNames.has(functionName)) {
613
+ errors.push(`Function name '${functionName}' is not in allowed list`);
614
+ if (riskLevel === "low") riskLevel = "medium";
615
+ }
616
+ return {
617
+ isValid: errors.length === 0,
618
+ errors,
619
+ riskLevel,
620
+ detectedPatterns
621
+ };
622
+ }
623
+ /**
624
+ * Validates function body for security threats
625
+ */
626
+ validateFunctionBody(body, functionName) {
627
+ const result = this.validate(body, functionName);
628
+ if (!result.isValid) {
629
+ throw new Error(result.errors.join("; "));
630
+ }
631
+ return result.isValid;
632
+ }
633
+ /**
634
+ * Creates a security hash for function validation
635
+ */
636
+ async createSecurityHash(body, metadata) {
637
+ const encoder = new TextEncoder();
638
+ const data = encoder.encode(body + JSON.stringify(metadata));
639
+ if (typeof crypto !== "undefined" && crypto.subtle) {
640
+ const hashBuffer = await crypto.subtle.digest("SHA-256", data);
641
+ return Array.from(new Uint8Array(hashBuffer)).map((b) => b.toString(16).padStart(2, "0")).join("");
642
+ }
643
+ const str = body + JSON.stringify(metadata);
644
+ let hash = 2166136261;
645
+ for (let i = 0; i < str.length; i++) {
646
+ hash ^= str.charCodeAt(i);
647
+ hash = Math.imul(hash, 16777619);
648
+ }
649
+ return (hash >>> 0).toString(16);
650
+ }
651
+ /**
652
+ * Validates security hash
653
+ */
654
+ async validateSecurityHash(body, metadata, expectedHash) {
655
+ const actualHash = await this.createSecurityHash(body, metadata);
656
+ return actualHash === expectedHash;
657
+ }
658
+ };
659
+ var SafeFunctionSerializer = class {
660
+ validator;
661
+ constructor(config) {
662
+ this.validator = new FunctionValidator(config);
663
+ }
664
+ /**
665
+ * Safely serializes an action with security validation (Async)
666
+ */
667
+ async serializeActionAsync(action, functionName) {
668
+ if (typeof action === "string") {
669
+ return { type: "string", name: action };
670
+ } else if (typeof action === "function") {
671
+ const body = action.toString();
672
+ this.validator.validateFunctionBody(body, functionName);
673
+ const metadata = {
674
+ length: body.length,
675
+ createdAt: Date.now(),
676
+ ...functionName !== void 0 ? { functionName } : {}
677
+ };
678
+ const hash = await this.validator.createSecurityHash(body, metadata);
679
+ return {
680
+ type: "function",
681
+ body,
682
+ hash,
683
+ metadata
684
+ };
685
+ }
686
+ return void 0;
687
+ }
688
+ /**
689
+ * Legacy synchronous serialization (Uses weak hash, kept for backward compatibility)
690
+ * @deprecated Use serializeActionAsync instead for better security
691
+ */
692
+ serializeAction(action, functionName) {
693
+ if (typeof action === "string") {
694
+ return { type: "string", name: action };
695
+ } else if (typeof action === "function") {
696
+ const body = action.toString();
697
+ this.validator.validateFunctionBody(body, functionName);
698
+ const metadata = {
699
+ length: body.length,
700
+ createdAt: Date.now(),
701
+ ...functionName !== void 0 ? { functionName } : {}
702
+ };
703
+ const str = body + JSON.stringify(metadata);
704
+ let hash = 2166136261;
705
+ for (let i = 0; i < str.length; i++) {
706
+ hash ^= str.charCodeAt(i);
707
+ hash = Math.imul(hash, 16777619);
708
+ }
709
+ return {
710
+ type: "function",
711
+ body,
712
+ hash: (hash >>> 0).toString(16),
713
+ metadata
714
+ };
715
+ }
716
+ return void 0;
717
+ }
718
+ /**
719
+ * Safely deserializes an action with security validation (Async)
720
+ */
721
+ async deserializeActionAsync(serializedAction) {
722
+ if (!serializedAction) {
723
+ return void 0;
724
+ }
725
+ if (serializedAction.type === "string") {
726
+ return serializedAction.name;
727
+ } else if (serializedAction.type === "function") {
728
+ try {
729
+ const isValid = await this.validator.validateSecurityHash(
730
+ serializedAction.body,
731
+ serializedAction.metadata,
732
+ serializedAction.hash
733
+ );
734
+ if (!isValid) {
735
+ const str = serializedAction.body + JSON.stringify(serializedAction.metadata);
736
+ let hash = 2166136261;
737
+ for (let i = 0; i < str.length; i++) {
738
+ hash ^= str.charCodeAt(i);
739
+ hash = Math.imul(hash, 16777619);
740
+ }
741
+ const legacyHash = (hash >>> 0).toString(16);
742
+ if (legacyHash !== serializedAction.hash) {
743
+ throw new Error("Function security hash validation failed");
744
+ }
745
+ }
746
+ this.validator.validateFunctionBody(
747
+ serializedAction.body,
748
+ serializedAction.metadata.functionName
749
+ );
750
+ return this.createSafeFunction(serializedAction.body);
751
+ } catch (e) {
752
+ securityLogger.error(
753
+ "Error deserializing function",
754
+ {
755
+ functionName: serializedAction.metadata.functionName,
756
+ bodyLength: serializedAction.body.length,
757
+ hash: serializedAction.hash
758
+ },
759
+ e instanceof Error ? e : new Error(String(e))
760
+ );
761
+ return void 0;
762
+ }
763
+ }
764
+ return void 0;
765
+ }
766
+ /**
767
+ * Safely deserializes an action (Synchronous - Legacy/Weak)
768
+ */
769
+ deserializeAction(serializedAction) {
770
+ if (!serializedAction) {
771
+ return void 0;
772
+ }
773
+ if (serializedAction.type === "string") {
774
+ return serializedAction.name;
775
+ } else if (serializedAction.type === "function") {
776
+ try {
777
+ const str = serializedAction.body + JSON.stringify(serializedAction.metadata);
778
+ let hash = 2166136261;
779
+ for (let i = 0; i < str.length; i++) {
780
+ hash ^= str.charCodeAt(i);
781
+ hash = Math.imul(hash, 16777619);
782
+ }
783
+ const calculatedHash = (hash >>> 0).toString(16);
784
+ if (calculatedHash !== serializedAction.hash) {
785
+ if (serializedAction.hash.length === 64) {
786
+ console.warn(
787
+ "Warning: Skipping verification of SHA-256 hash in synchronous deserializeAction. Use deserializeActionAsync for security."
788
+ );
789
+ } else {
790
+ throw new Error("Function security hash validation failed (Legacy)");
791
+ }
792
+ }
793
+ this.validator.validateFunctionBody(
794
+ serializedAction.body,
795
+ serializedAction.metadata.functionName
796
+ );
797
+ return this.createSafeFunction(serializedAction.body);
798
+ } catch (e) {
799
+ securityLogger.error(
800
+ "Error deserializing function",
801
+ {
802
+ functionName: serializedAction.metadata.functionName,
803
+ bodyLength: serializedAction.body.length,
804
+ hash: serializedAction.hash
805
+ },
806
+ e instanceof Error ? e : new Error(String(e))
807
+ );
808
+ return void 0;
809
+ }
810
+ }
811
+ return void 0;
812
+ }
813
+ /**
814
+ * Safely deserializes legacy function strings (without security metadata)
815
+ * This method provides backward compatibility for old string-based function serialization
816
+ * while maintaining security through validation and sandboxing.
817
+ */
818
+ deserializeLegacyString(action) {
819
+ if (!action || typeof action !== "string") {
820
+ return void 0;
821
+ }
822
+ try {
823
+ this.validator.validateFunctionBody(action, "legacy_deserialization");
824
+ return this.createSafeFunction(action);
825
+ } catch (e) {
826
+ securityLogger.error(
827
+ "Blocked unsafe legacy function string",
828
+ { actionLength: action.length },
829
+ e instanceof Error ? e : new Error(String(e))
830
+ );
831
+ return void 0;
832
+ }
833
+ }
834
+ /**
835
+ * Creates a function safely without using new Function()
836
+ * Uses a whitelist approach with predefined function templates
837
+ */
838
+ createSafeFunction(body) {
839
+ try {
840
+ const source = body.trim();
841
+ const executor = new Function(
842
+ "adaptee",
843
+ "args",
844
+ `
845
+ // Block dangerous globals by creating local variables that shadow them
846
+ var eval = undefined;
847
+ var Function = undefined;
848
+ var setTimeout = undefined;
849
+ var setInterval = undefined;
850
+ var document = undefined;
851
+ var window = undefined;
852
+ var global = undefined;
853
+ var process = undefined;
854
+ var require = undefined;
855
+
856
+ const __fn = (${source});
857
+ if (typeof __fn !== 'function') {
858
+ throw new Error('Deserialized source is not a function');
859
+ }
860
+ return __fn(adaptee, ...(Array.isArray(args) ? args : []));
861
+ `
862
+ );
863
+ return ((adaptee, ...args) => executor(adaptee, args));
864
+ } catch (e) {
865
+ const message = e instanceof Error ? e.message : String(e);
866
+ throw new Error(`Failed to create safe function: ${message}`);
867
+ }
868
+ }
869
+ };
870
+ var safeFunctionSerializer = new SafeFunctionSerializer();
871
+ var serializeAction = safeFunctionSerializer.serializeAction.bind(
872
+ safeFunctionSerializer
873
+ );
874
+ var deserializeAction = safeFunctionSerializer.deserializeAction.bind(
875
+ safeFunctionSerializer
876
+ );
877
+
878
+ // src/monitoring.ts
879
+ var HealthStatus = {
880
+ HEALTHY: "healthy",
881
+ WARNING: "warning",
882
+ CRITICAL: "critical",
883
+ UNKNOWN: "unknown"
884
+ };
885
+ var DEFAULT_MONITORING_CONFIG = {
886
+ enabled: true,
887
+ metricsInterval: 5e3,
888
+ // 5 seconds
889
+ healthCheckInterval: 3e4,
890
+ // 30 seconds
891
+ maxMetricsHistory: 1e3,
892
+ thresholds: {
893
+ transitionTimeWarning: 100,
894
+ // 100ms
895
+ transitionTimeCritical: 500,
896
+ // 500ms
897
+ errorRateWarning: 5,
898
+ // 5%
899
+ errorRateCritical: 15
900
+ // 15%
901
+ }
902
+ };
903
+ var MetricsCollector = class {
904
+ metrics = [];
905
+ config;
906
+ startTime = Date.now();
907
+ transitionCount = 0;
908
+ errorCount = 0;
909
+ constructor(config = {}) {
910
+ this.config = { ...DEFAULT_MONITORING_CONFIG, ...config };
911
+ }
912
+ /**
913
+ * Record a state transition
914
+ */
915
+ recordTransition(transitionTime) {
916
+ if (!this.config.enabled) return;
917
+ this.transitionCount++;
918
+ const metrics = {
919
+ transitionTime,
920
+ stateChangeCount: this.transitionCount,
921
+ errorCount: this.errorCount,
922
+ timestamp: Date.now()
923
+ };
924
+ this.addMetrics(metrics);
925
+ this.logMetrics(metrics);
926
+ }
927
+ /**
928
+ * Record an error
929
+ */
930
+ recordError() {
931
+ if (!this.config.enabled) return;
932
+ this.errorCount++;
933
+ }
934
+ /**
935
+ * Get current metrics summary
936
+ */
937
+ getMetricsSummary() {
938
+ const now = Date.now();
939
+ const uptime = now - this.startTime;
940
+ const recentMetrics = this.getRecentMetrics(6e4);
941
+ const averageTransitionTime = recentMetrics.length > 0 ? recentMetrics.reduce((sum, m) => sum + m.transitionTime, 0) / recentMetrics.length : 0;
942
+ const errorRate = this.transitionCount > 0 ? this.errorCount / this.transitionCount * 100 : 0;
943
+ return {
944
+ totalTransitions: this.transitionCount,
945
+ totalErrors: this.errorCount,
946
+ averageTransitionTime,
947
+ errorRate,
948
+ uptime
949
+ };
950
+ }
951
+ /**
952
+ * Get metrics history
953
+ */
954
+ getMetricsHistory(timeRange) {
955
+ if (timeRange) {
956
+ return this.getRecentMetrics(timeRange);
957
+ }
958
+ return [...this.metrics];
959
+ }
960
+ /**
961
+ * Clear metrics history
962
+ */
963
+ clearMetrics() {
964
+ this.metrics = [];
965
+ this.transitionCount = 0;
966
+ this.errorCount = 0;
967
+ this.startTime = Date.now();
968
+ }
969
+ /**
970
+ * Get recent metrics within time range
971
+ */
972
+ getRecentMetrics(timeRange) {
973
+ const cutoff = Date.now() - timeRange;
974
+ return this.metrics.filter((m) => m.timestamp >= cutoff);
975
+ }
976
+ /**
977
+ * Add metrics to history with size limit
978
+ */
979
+ addMetrics(metrics) {
980
+ this.metrics.push(metrics);
981
+ if (this.metrics.length > this.config.maxMetricsHistory) {
982
+ this.metrics = this.metrics.slice(-this.config.maxMetricsHistory);
983
+ }
984
+ }
985
+ /**
986
+ * Log metrics if significant
987
+ */
988
+ logMetrics(metrics) {
989
+ const { thresholds } = this.config;
990
+ if (metrics.transitionTime > thresholds.transitionTimeCritical) {
991
+ stateMachineLogger.error("Critical transition time detected", {
992
+ transitionTime: metrics.transitionTime,
993
+ threshold: thresholds.transitionTimeCritical
994
+ });
995
+ } else if (metrics.transitionTime > thresholds.transitionTimeWarning) {
996
+ stateMachineLogger.warn("Slow transition detected", {
997
+ transitionTime: metrics.transitionTime,
998
+ threshold: thresholds.transitionTimeWarning
999
+ });
1000
+ }
1001
+ }
1002
+ };
1003
+ var PerformanceMonitor = class {
1004
+ metricsCollector;
1005
+ config;
1006
+ intervalId;
1007
+ isRunning = false;
1008
+ constructor(metricsCollector, config = {}) {
1009
+ this.metricsCollector = metricsCollector;
1010
+ this.config = { ...DEFAULT_MONITORING_CONFIG, ...config };
1011
+ }
1012
+ /**
1013
+ * Start performance monitoring
1014
+ */
1015
+ start() {
1016
+ if (this.isRunning || !this.config.enabled) return;
1017
+ this.isRunning = true;
1018
+ this.intervalId = setInterval(() => {
1019
+ this.collectMetrics();
1020
+ }, this.config.metricsInterval);
1021
+ stateMachineLogger.info("Performance monitoring started", {
1022
+ interval: this.config.metricsInterval
1023
+ });
1024
+ }
1025
+ /**
1026
+ * Stop performance monitoring
1027
+ */
1028
+ stop() {
1029
+ if (!this.isRunning) return;
1030
+ this.isRunning = false;
1031
+ if (this.intervalId) {
1032
+ clearInterval(this.intervalId);
1033
+ this.intervalId = void 0;
1034
+ }
1035
+ stateMachineLogger.info("Performance monitoring stopped");
1036
+ }
1037
+ /**
1038
+ * Get current performance status
1039
+ */
1040
+ getPerformanceStatus() {
1041
+ const summary = this.metricsCollector.getMetricsSummary();
1042
+ const issues = [];
1043
+ let status = HealthStatus.HEALTHY;
1044
+ if (summary.errorRate >= this.config.thresholds.errorRateCritical) {
1045
+ status = HealthStatus.CRITICAL;
1046
+ issues.push(`Critical error rate: ${summary.errorRate.toFixed(2)}%`);
1047
+ } else if (summary.errorRate >= this.config.thresholds.errorRateWarning) {
1048
+ status = HealthStatus.WARNING;
1049
+ issues.push(`High error rate: ${summary.errorRate.toFixed(2)}%`);
1050
+ }
1051
+ if (summary.averageTransitionTime >= this.config.thresholds.transitionTimeCritical) {
1052
+ status = HealthStatus.CRITICAL;
1053
+ issues.push(
1054
+ `Critical transition time: ${summary.averageTransitionTime.toFixed(2)}ms`
1055
+ );
1056
+ } else if (summary.averageTransitionTime >= this.config.thresholds.transitionTimeWarning) {
1057
+ if (status === HealthStatus.HEALTHY) status = HealthStatus.WARNING;
1058
+ issues.push(
1059
+ `Slow transitions: ${summary.averageTransitionTime.toFixed(2)}ms`
1060
+ );
1061
+ }
1062
+ return { status, summary, issues };
1063
+ }
1064
+ /**
1065
+ * Collect current metrics
1066
+ */
1067
+ collectMetrics() {
1068
+ const summary = this.metricsCollector.getMetricsSummary();
1069
+ stateMachineLogger.debug("Performance metrics collected", {
1070
+ totalTransitions: summary.totalTransitions,
1071
+ errorRate: summary.errorRate,
1072
+ averageTransitionTime: summary.averageTransitionTime
1073
+ });
1074
+ }
1075
+ };
1076
+ var HealthChecker = class {
1077
+ performanceMonitor;
1078
+ config;
1079
+ intervalId;
1080
+ isRunning = false;
1081
+ lastHealthCheck;
1082
+ constructor(performanceMonitor, config = {}) {
1083
+ this.performanceMonitor = performanceMonitor;
1084
+ this.config = { ...DEFAULT_MONITORING_CONFIG, ...config };
1085
+ }
1086
+ /**
1087
+ * Start health checking
1088
+ */
1089
+ start() {
1090
+ if (this.isRunning || !this.config.enabled) return;
1091
+ this.isRunning = true;
1092
+ this.intervalId = setInterval(() => {
1093
+ this.performHealthCheck();
1094
+ }, this.config.healthCheckInterval);
1095
+ stateMachineLogger.info("Health checking started", {
1096
+ interval: this.config.healthCheckInterval
1097
+ });
1098
+ }
1099
+ /**
1100
+ * Stop health checking
1101
+ */
1102
+ stop() {
1103
+ if (!this.isRunning) return;
1104
+ this.isRunning = false;
1105
+ if (this.intervalId) {
1106
+ clearInterval(this.intervalId);
1107
+ this.intervalId = void 0;
1108
+ }
1109
+ stateMachineLogger.info("Health checking stopped");
1110
+ }
1111
+ /**
1112
+ * Perform immediate health check
1113
+ */
1114
+ performHealthCheck() {
1115
+ const performanceStatus = this.performanceMonitor.getPerformanceStatus();
1116
+ const result = {
1117
+ status: performanceStatus.status,
1118
+ message: this.generateHealthMessage(performanceStatus),
1119
+ timestamp: Date.now(),
1120
+ metrics: performanceStatus.summary
1121
+ };
1122
+ this.lastHealthCheck = result;
1123
+ this.logHealthCheck(result);
1124
+ return result;
1125
+ }
1126
+ /**
1127
+ * Get last health check result
1128
+ */
1129
+ getLastHealthCheck() {
1130
+ return this.lastHealthCheck;
1131
+ }
1132
+ /**
1133
+ * Generate health message based on status
1134
+ */
1135
+ generateHealthMessage(performanceStatus) {
1136
+ const { status, issues, summary } = performanceStatus;
1137
+ if (status === HealthStatus.HEALTHY) {
1138
+ return `StateMachine is healthy. ${summary.totalTransitions} transitions, ${summary.errorRate.toFixed(2)}% error rate`;
1139
+ }
1140
+ if (status === HealthStatus.WARNING) {
1141
+ return `StateMachine has warnings: ${issues.join(", ")}`;
1142
+ }
1143
+ if (status === HealthStatus.CRITICAL) {
1144
+ return `StateMachine is in critical state: ${issues.join(", ")}`;
1145
+ }
1146
+ return "StateMachine health status unknown";
1147
+ }
1148
+ /**
1149
+ * Log health check results
1150
+ */
1151
+ logHealthCheck(result) {
1152
+ const logData = {
1153
+ status: result.status,
1154
+ message: result.message,
1155
+ metrics: result.metrics
1156
+ };
1157
+ switch (result.status) {
1158
+ case HealthStatus.HEALTHY:
1159
+ stateMachineLogger.debug("Health check passed", logData);
1160
+ break;
1161
+ case HealthStatus.WARNING:
1162
+ stateMachineLogger.warn("Health check warning", logData);
1163
+ break;
1164
+ case HealthStatus.CRITICAL:
1165
+ stateMachineLogger.error("Health check critical", logData);
1166
+ break;
1167
+ /* c8 ignore next 2 */
1168
+ default:
1169
+ stateMachineLogger.info("Health check completed", logData);
1170
+ }
1171
+ }
1172
+ };
1173
+ var StateMachineMonitor = class {
1174
+ metricsCollector;
1175
+ performanceMonitor;
1176
+ healthChecker;
1177
+ config;
1178
+ constructor(config = {}) {
1179
+ this.config = { ...DEFAULT_MONITORING_CONFIG, ...config };
1180
+ this.metricsCollector = new MetricsCollector(this.config);
1181
+ this.performanceMonitor = new PerformanceMonitor(
1182
+ this.metricsCollector,
1183
+ this.config
1184
+ );
1185
+ this.healthChecker = new HealthChecker(this.performanceMonitor, this.config);
1186
+ }
1187
+ /**
1188
+ * Start all monitoring components
1189
+ */
1190
+ start() {
1191
+ this.performanceMonitor.start();
1192
+ this.healthChecker.start();
1193
+ stateMachineLogger.info("StateMachine monitoring started", {
1194
+ config: this.config
1195
+ });
1196
+ }
1197
+ /**
1198
+ * Stop all monitoring components
1199
+ */
1200
+ stop() {
1201
+ this.performanceMonitor.stop();
1202
+ this.healthChecker.stop();
1203
+ stateMachineLogger.info("StateMachine monitoring stopped");
1204
+ }
1205
+ /**
1206
+ * Record a transition for monitoring (F-T4-TS-5 widening: added success + context params)
1207
+ */
1208
+ recordTransition(transitionTime, success = true, _context) {
1209
+ this.metricsCollector.recordTransition(transitionTime);
1210
+ if (!success) this.metricsCollector.recordError();
1211
+ }
1212
+ /**
1213
+ * Record an error for monitoring (F-T4-TS-5 widening: added error + context params)
1214
+ */
1215
+ recordError(_error, _context) {
1216
+ this.metricsCollector.recordError();
1217
+ }
1218
+ /**
1219
+ * Record an event for monitoring (optional IMonitor extension)
1220
+ */
1221
+ recordEvent(_eventName, _duration) {
1222
+ }
1223
+ /**
1224
+ * Get aggregate metrics snapshot (optional IMonitor extension)
1225
+ */
1226
+ getMetrics() {
1227
+ const summary = this.metricsCollector.getMetricsSummary();
1228
+ return {
1229
+ totalTransitions: summary.totalTransitions,
1230
+ successCount: summary.totalTransitions - summary.totalErrors,
1231
+ errorCount: summary.totalErrors,
1232
+ averageDuration: summary.averageTransitionTime
1233
+ };
1234
+ }
1235
+ /**
1236
+ * Get comprehensive monitoring report
1237
+ */
1238
+ getMonitoringReport() {
1239
+ const health = this.healthChecker.performHealthCheck();
1240
+ const performance = this.performanceMonitor.getPerformanceStatus();
1241
+ const metrics = this.metricsCollector.getMetricsSummary();
1242
+ return {
1243
+ health,
1244
+ performance,
1245
+ metrics,
1246
+ config: this.config
1247
+ };
1248
+ }
1249
+ /**
1250
+ * Export metrics for external monitoring systems
1251
+ */
1252
+ exportMetrics() {
1253
+ const metrics = this.metricsCollector.getMetricsSummary();
1254
+ const health = this.healthChecker.getLastHealthCheck();
1255
+ const jsonExport = {
1256
+ timestamp: Date.now(),
1257
+ metrics,
1258
+ health,
1259
+ config: this.config
1260
+ };
1261
+ const prometheusExport = this.generatePrometheusMetrics(metrics, health);
1262
+ return {
1263
+ prometheus: prometheusExport,
1264
+ json: jsonExport
1265
+ };
1266
+ }
1267
+ /**
1268
+ * Generate Prometheus-compatible metrics (without memory metrics)
1269
+ */
1270
+ generatePrometheusMetrics(metrics, health) {
1271
+ const lines = [];
1272
+ lines.push(
1273
+ "# HELP statemachine_transitions_total Total number of state transitions"
1274
+ );
1275
+ lines.push("# TYPE statemachine_transitions_total counter");
1276
+ lines.push(`statemachine_transitions_total ${metrics.totalTransitions}`);
1277
+ lines.push("# HELP statemachine_errors_total Total number of errors");
1278
+ lines.push("# TYPE statemachine_errors_total counter");
1279
+ lines.push(`statemachine_errors_total ${metrics.totalErrors}`);
1280
+ lines.push(
1281
+ "# HELP statemachine_transition_time_avg Average transition time in milliseconds"
1282
+ );
1283
+ lines.push("# TYPE statemachine_transition_time_avg gauge");
1284
+ lines.push(
1285
+ `statemachine_transition_time_avg ${metrics.averageTransitionTime}`
1286
+ );
1287
+ lines.push("# HELP statemachine_error_rate Error rate percentage");
1288
+ lines.push("# TYPE statemachine_error_rate gauge");
1289
+ lines.push(`statemachine_error_rate ${metrics.errorRate}`);
1290
+ lines.push("# HELP statemachine_uptime Uptime in milliseconds");
1291
+ lines.push("# TYPE statemachine_uptime gauge");
1292
+ lines.push(`statemachine_uptime ${metrics.uptime}`);
1293
+ if (health) {
1294
+ const healthValue = health.status === HealthStatus.HEALTHY ? 1 : health.status === HealthStatus.WARNING ? 0.5 : 0;
1295
+ lines.push(
1296
+ "# HELP statemachine_health Health status (1=healthy, 0.5=warning, 0=critical)"
1297
+ );
1298
+ lines.push("# TYPE statemachine_health gauge");
1299
+ lines.push(`statemachine_health ${healthValue}`);
1300
+ }
1301
+ return lines.join("\n");
1302
+ }
1303
+ };
1304
+ function createDefaultMonitor() {
1305
+ return new StateMachineMonitor();
1306
+ }
1307
+
1308
+ // src/types.ts
1309
+ var StateMachineError = class _StateMachineError extends Error {
1310
+ context;
1311
+ constructor(message, context, cause) {
1312
+ super(message);
1313
+ this.name = "StateMachineError";
1314
+ this.context = context;
1315
+ this.cause = cause;
1316
+ if (Error.captureStackTrace) {
1317
+ Error.captureStackTrace(this, _StateMachineError);
1318
+ }
1319
+ }
1320
+ toString() {
1321
+ const { state, event, action, transition, phase } = this.context;
1322
+ const details = [];
1323
+ if (state) details.push(`state: ${state}`);
1324
+ if (event) details.push(`event: ${event}`);
1325
+ if (action) details.push(`action: ${action}`);
1326
+ if (transition) details.push(`transition: ${transition}`);
1327
+ if (phase) details.push(`phase: ${phase}`);
1328
+ return `${this.name}: ${this.message}${details.length ? ` (${details.join(", ")})` : ""}`;
1329
+ }
1330
+ };
1331
+ function isAdapter(inp) {
1332
+ return !!inp && typeof inp === "object" && "set" in inp && "get" in inp;
1333
+ }
1334
+ var MemoryAdapter = class {
1335
+ adaptee;
1336
+ _savedState;
1337
+ constructor(data) {
1338
+ this.adaptee = data;
1339
+ }
1340
+ set(property, value) {
1341
+ this.adaptee[property] = value;
1342
+ }
1343
+ get(property) {
1344
+ return this.adaptee[property];
1345
+ }
1346
+ // StatePersistenceAdapter implementation
1347
+ async save(state) {
1348
+ this._savedState = state;
1349
+ }
1350
+ async restore() {
1351
+ return this._savedState;
1352
+ }
1353
+ };
1354
+ var LocalStorageAdapter = class {
1355
+ adaptee;
1356
+ key;
1357
+ constructor(data, storageKey = "state_machine") {
1358
+ this.adaptee = data;
1359
+ this.key = storageKey;
1360
+ }
1361
+ set(property, value) {
1362
+ this.adaptee[property] = value;
1363
+ }
1364
+ get(property) {
1365
+ return this.adaptee[property];
1366
+ }
1367
+ // StatePersistenceAdapter implementation
1368
+ async save(state) {
1369
+ const data = { ...this.adaptee, state };
1370
+ globalThis.localStorage.setItem(this.key, JSON.stringify(data));
1371
+ }
1372
+ async restore() {
1373
+ const stored = globalThis.localStorage.getItem(this.key);
1374
+ return stored ? JSON.parse(stored).state : { currentState: "", history: {} };
1375
+ }
1376
+ };
1377
+ var SessionStorageAdapter = class {
1378
+ adaptee;
1379
+ key;
1380
+ constructor(data, storageKey = "state_machine") {
1381
+ this.adaptee = data;
1382
+ this.key = storageKey;
1383
+ }
1384
+ set(property, value) {
1385
+ this.adaptee[property] = value;
1386
+ }
1387
+ get(property) {
1388
+ return this.adaptee[property];
1389
+ }
1390
+ // StatePersistenceAdapter implementation
1391
+ async save(state) {
1392
+ const data = { ...this.adaptee, state };
1393
+ globalThis.sessionStorage.setItem(this.key, JSON.stringify(data));
1394
+ }
1395
+ async restore() {
1396
+ const stored = globalThis.sessionStorage.getItem(this.key);
1397
+ return stored ? JSON.parse(stored).state : void 0;
1398
+ }
1399
+ };
1400
+ var ServerAdapter = class _ServerAdapter {
1401
+ adaptee;
1402
+ static data = {};
1403
+ endpoint;
1404
+ constructor(data, endpoint = "/api/state") {
1405
+ this.adaptee = data;
1406
+ this.endpoint = endpoint;
1407
+ }
1408
+ set(property, value) {
1409
+ this.adaptee[property] = value;
1410
+ }
1411
+ get(property) {
1412
+ return this.adaptee[property];
1413
+ }
1414
+ // StatePersistenceAdapter implementation (simulated async)
1415
+ async save(state) {
1416
+ if (state) {
1417
+ await new Promise((resolve) => setTimeout(resolve, 100));
1418
+ _ServerAdapter.data[this.endpoint] = state;
1419
+ }
1420
+ }
1421
+ async restore() {
1422
+ await new Promise((resolve) => setTimeout(resolve, 100));
1423
+ return _ServerAdapter.data[this.endpoint] || { currentState: "", history: {} };
1424
+ }
1425
+ };
1426
+
1427
+ // src/error_handling.ts
1428
+ var ErrorSeverity = {
1429
+ LOW: "low",
1430
+ MEDIUM: "medium",
1431
+ HIGH: "high",
1432
+ CRITICAL: "critical"
1433
+ };
1434
+ var ErrorCategory = {
1435
+ VALIDATION: "validation",
1436
+ TRANSITION: "transition",
1437
+ ACTION: "action",
1438
+ SERIALIZATION: "serialization",
1439
+ CONFIGURATION: "configuration",
1440
+ SECURITY: "security",
1441
+ PERFORMANCE: "performance",
1442
+ NETWORK: "network",
1443
+ UNKNOWN: "unknown"
1444
+ };
1445
+ var EnhancedStateMachineError = class extends StateMachineError {
1446
+ severity;
1447
+ category;
1448
+ extendedContext;
1449
+ recoverable;
1450
+ errorCode;
1451
+ constructor(message, context, options = {}) {
1452
+ super(message, context, options.cause);
1453
+ this.severity = options.severity || ErrorSeverity.MEDIUM;
1454
+ this.category = options.category || ErrorCategory.UNKNOWN;
1455
+ this.recoverable = options.recoverable ?? true;
1456
+ this.errorCode = options.errorCode || this.generateErrorCode();
1457
+ this.extendedContext = {
1458
+ ...context,
1459
+ timestamp: Date.now(),
1460
+ stackTrace: this.stack,
1461
+ additionalData: options.additionalData
1462
+ };
1463
+ }
1464
+ generateErrorCode() {
1465
+ const categoryCode = this.category.toUpperCase().substring(0, 3);
1466
+ const severityCode = this.severity.toUpperCase().substring(0, 1);
1467
+ const timestamp = Date.now().toString().slice(-6);
1468
+ return `SM_${categoryCode}_${severityCode}_${timestamp}`;
1469
+ }
1470
+ toJSON() {
1471
+ return {
1472
+ name: this.name,
1473
+ message: this.message,
1474
+ errorCode: this.errorCode,
1475
+ severity: this.severity,
1476
+ category: this.category,
1477
+ recoverable: this.recoverable,
1478
+ context: this.extendedContext,
1479
+ stack: this.stack
1480
+ };
1481
+ }
1482
+ };
1483
+ var RetryRecoveryStrategy = class {
1484
+ name = "retry";
1485
+ maxRetries;
1486
+ retryDelay;
1487
+ constructor(maxRetries = 3, retryDelay = 1e3) {
1488
+ this.maxRetries = maxRetries;
1489
+ this.retryDelay = retryDelay;
1490
+ }
1491
+ canRecover(error) {
1492
+ return error.recoverable && error.category !== ErrorCategory.SECURITY && error.severity !== ErrorSeverity.CRITICAL;
1493
+ }
1494
+ async recover(error, context) {
1495
+ const retryCount = context.retryCount || 0;
1496
+ if (retryCount >= this.maxRetries) {
1497
+ stateMachineLogger.warn("Max retries exceeded", {
1498
+ errorCode: error.errorCode,
1499
+ retryCount,
1500
+ maxRetries: this.maxRetries
1501
+ });
1502
+ return false;
1503
+ }
1504
+ stateMachineLogger.info("Attempting error recovery", {
1505
+ errorCode: error.errorCode,
1506
+ strategy: this.name,
1507
+ attempt: retryCount + 1
1508
+ });
1509
+ await new Promise((resolve) => setTimeout(resolve, this.retryDelay));
1510
+ context.retryCount = retryCount + 1;
1511
+ return true;
1512
+ }
1513
+ };
1514
+ var FallbackStateRecoveryStrategy = class {
1515
+ name = "fallback_state";
1516
+ fallbackState;
1517
+ constructor(fallbackState = "error_state") {
1518
+ this.fallbackState = fallbackState;
1519
+ }
1520
+ canRecover(error) {
1521
+ return error.category === ErrorCategory.TRANSITION || error.category === ErrorCategory.ACTION;
1522
+ }
1523
+ async recover(error, context) {
1524
+ try {
1525
+ if (context.stateMachine && typeof context.stateMachine.setCurrentState === "function") {
1526
+ stateMachineLogger.info("Recovering to fallback state", {
1527
+ errorCode: error.errorCode,
1528
+ fallbackState: this.fallbackState,
1529
+ originalState: error.context.state
1530
+ });
1531
+ context.stateMachine.setCurrentState(this.fallbackState);
1532
+ return true;
1533
+ }
1534
+ } catch (recoveryError) {
1535
+ stateMachineLogger.error(
1536
+ "Fallback recovery failed",
1537
+ {
1538
+ errorCode: error.errorCode,
1539
+ fallbackState: this.fallbackState
1540
+ },
1541
+ /* c8 ignore next */
1542
+ recoveryError instanceof Error ? recoveryError : new Error(String(recoveryError))
1543
+ );
1544
+ }
1545
+ return false;
1546
+ }
1547
+ };
1548
+ var ErrorAnalytics = class {
1549
+ errors = [];
1550
+ maxStoredErrors;
1551
+ constructor(maxStoredErrors = 1e3) {
1552
+ this.maxStoredErrors = maxStoredErrors;
1553
+ }
1554
+ recordError(error) {
1555
+ this.errors.push(error);
1556
+ if (this.errors.length > this.maxStoredErrors) {
1557
+ this.errors = this.errors.slice(-this.maxStoredErrors);
1558
+ }
1559
+ stateMachineLogger.error(
1560
+ "StateMachine error recorded",
1561
+ {
1562
+ errorCode: error.errorCode,
1563
+ severity: error.severity,
1564
+ category: error.category,
1565
+ recoverable: error.recoverable
1566
+ },
1567
+ error
1568
+ );
1569
+ }
1570
+ getErrorStats() {
1571
+ const now = Date.now();
1572
+ const oneHourAgo = now - 60 * 60 * 1e3;
1573
+ const recentErrors = this.errors.filter(
1574
+ (e) => e.extendedContext.timestamp > oneHourAgo
1575
+ );
1576
+ const bySeverity = Object.values(ErrorSeverity).reduce(
1577
+ (acc, severity) => {
1578
+ acc[severity] = this.errors.filter(
1579
+ (e) => e.severity === severity
1580
+ ).length;
1581
+ return acc;
1582
+ },
1583
+ {}
1584
+ );
1585
+ const byCategory = Object.values(ErrorCategory).reduce(
1586
+ (acc, category) => {
1587
+ acc[category] = this.errors.filter(
1588
+ (e) => e.category === category
1589
+ ).length;
1590
+ return acc;
1591
+ },
1592
+ {}
1593
+ );
1594
+ return {
1595
+ total: this.errors.length,
1596
+ bySeverity,
1597
+ byCategory,
1598
+ recentErrors: recentErrors.length,
1599
+ errorRate: recentErrors.length / 60
1600
+ // errors per minute
1601
+ };
1602
+ }
1603
+ getTopErrors(limit = 10) {
1604
+ const errorGroups = /* @__PURE__ */ new Map();
1605
+ this.errors.forEach((error) => {
1606
+ const key = `${error.message}_${error.category}`;
1607
+ const existing = errorGroups.get(key);
1608
+ if (existing) {
1609
+ existing.count++;
1610
+ existing.lastOccurrence = Math.max(
1611
+ existing.lastOccurrence,
1612
+ error.extendedContext.timestamp
1613
+ );
1614
+ } else {
1615
+ errorGroups.set(key, {
1616
+ errorCode: error.errorCode,
1617
+ message: error.message,
1618
+ count: 1,
1619
+ lastOccurrence: error.extendedContext.timestamp,
1620
+ severity: error.severity,
1621
+ category: error.category
1622
+ });
1623
+ }
1624
+ });
1625
+ return Array.from(errorGroups.values()).sort((a, b) => b.count - a.count).slice(0, limit);
1626
+ }
1627
+ clearErrors() {
1628
+ this.errors = [];
1629
+ }
1630
+ };
1631
+ var ErrorHandler = class {
1632
+ recoveryStrategies = [];
1633
+ analytics;
1634
+ enabled = true;
1635
+ constructor() {
1636
+ this.analytics = new ErrorAnalytics();
1637
+ this.addRecoveryStrategy(new RetryRecoveryStrategy());
1638
+ this.addRecoveryStrategy(new FallbackStateRecoveryStrategy());
1639
+ }
1640
+ addRecoveryStrategy(strategy) {
1641
+ this.recoveryStrategies.push(strategy);
1642
+ }
1643
+ removeRecoveryStrategy(strategyName) {
1644
+ this.recoveryStrategies = this.recoveryStrategies.filter(
1645
+ (s) => s.name !== strategyName
1646
+ );
1647
+ }
1648
+ async handleError(error, context = {}) {
1649
+ if (!this.enabled) {
1650
+ return false;
1651
+ }
1652
+ const enhancedError = error instanceof EnhancedStateMachineError ? error : this.convertToEnhancedError(error, context);
1653
+ this.analytics.recordError(enhancedError);
1654
+ for (const strategy of this.recoveryStrategies) {
1655
+ if (strategy.canRecover(enhancedError)) {
1656
+ try {
1657
+ const recovered = await strategy.recover(enhancedError, context);
1658
+ if (recovered) {
1659
+ stateMachineLogger.info("Error recovery successful", {
1660
+ errorCode: enhancedError.errorCode,
1661
+ strategy: strategy.name
1662
+ });
1663
+ return true;
1664
+ }
1665
+ } catch (recoveryError) {
1666
+ stateMachineLogger.error(
1667
+ "Recovery strategy failed",
1668
+ {
1669
+ errorCode: enhancedError.errorCode,
1670
+ strategy: strategy.name
1671
+ },
1672
+ /* c8 ignore next */
1673
+ recoveryError instanceof Error ? recoveryError : new Error(String(recoveryError))
1674
+ );
1675
+ }
1676
+ }
1677
+ }
1678
+ stateMachineLogger.error("All recovery strategies failed", {
1679
+ errorCode: enhancedError.errorCode,
1680
+ severity: enhancedError.severity,
1681
+ category: enhancedError.category
1682
+ });
1683
+ return false;
1684
+ }
1685
+ convertToEnhancedError(error, context) {
1686
+ let category = ErrorCategory.UNKNOWN;
1687
+ let severity = ErrorSeverity.MEDIUM;
1688
+ if (error.message.includes("transition") || error.message.includes("state")) {
1689
+ category = ErrorCategory.TRANSITION;
1690
+ } else if (error.message.includes("action")) {
1691
+ category = ErrorCategory.ACTION;
1692
+ } else if (error.message.includes("validation")) {
1693
+ category = ErrorCategory.VALIDATION;
1694
+ severity = ErrorSeverity.HIGH;
1695
+ } else if (error.message.includes("security") || error.message.includes("injection")) {
1696
+ category = ErrorCategory.SECURITY;
1697
+ severity = ErrorSeverity.CRITICAL;
1698
+ }
1699
+ return new EnhancedStateMachineError(
1700
+ error.message,
1701
+ context.errorContext || {},
1702
+ {
1703
+ category,
1704
+ severity,
1705
+ cause: error,
1706
+ additionalData: context
1707
+ }
1708
+ );
1709
+ }
1710
+ getAnalytics() {
1711
+ return this.analytics;
1712
+ }
1713
+ enable() {
1714
+ this.enabled = true;
1715
+ }
1716
+ disable() {
1717
+ this.enabled = false;
1718
+ }
1719
+ isEnabled() {
1720
+ return this.enabled;
1721
+ }
1722
+ };
1723
+ function createDefaultErrorHandler() {
1724
+ return new ErrorHandler();
1725
+ }
1726
+ function createEnhancedError(message, context, options) {
1727
+ return new EnhancedStateMachineError(message, context, options);
1728
+ }
1729
+ function isRecoverableError(error) {
1730
+ if (error instanceof EnhancedStateMachineError) {
1731
+ return error.recoverable;
1732
+ }
1733
+ return true;
1734
+ }
1735
+
1736
+ // src/state_machine.ts
1737
+ var ConsoleLogger = {
1738
+ debug: () => {
1739
+ },
1740
+ // По умолчанию выключено для Lite
1741
+ info: () => {
1742
+ },
1743
+ warn: (msg, ctx) => console.warn(msg, ctx),
1744
+ error: (msg, ctx, err) => console.error(msg, ctx, err)
1745
+ };
1746
+ var StateMachine = class _StateMachine {
1747
+ // Приватные поля для зависимостей
1748
+ logger;
1749
+ monitor;
1750
+ scheduler;
1751
+ errorHandler;
1752
+ // Свойства
1753
+ states;
1754
+ events;
1755
+ stateAttribute;
1756
+ onError;
1757
+ adaptee;
1758
+ context;
1759
+ historyMap = /* @__PURE__ */ new Map();
1760
+ initialState;
1761
+ persistenceAdapter;
1762
+ activeTimers = /* @__PURE__ */ new Map();
1763
+ stateEntryTimes = /* @__PURE__ */ new Map();
1764
+ // Event Queue Infrastructure (SCXML Run-to-Completion)
1765
+ externalQueue = [];
1766
+ internalQueue = [];
1767
+ isProcessing = false;
1768
+ eventIdCounter = 0;
1769
+ transitionDepth = 0;
1770
+ MAX_TRANSITION_DEPTH = 100;
1771
+ // Optional configuration
1772
+ transitionTimeout;
1773
+ errorState;
1774
+ abortOnExitError;
1775
+ maxQueueDepth = 1e3;
1776
+ // Transition state visibility
1777
+ _isTransitioning = false;
1778
+ _targetState;
1779
+ get isTransitioning() {
1780
+ return this._isTransitioning;
1781
+ }
1782
+ get targetState() {
1783
+ return this._targetState;
1784
+ }
1785
+ // Геттеры и сеттеры
1786
+ set currentState(state) {
1787
+ if (!this.adaptee) throw new StateMachineError("no adaptee", { state });
1788
+ this.setCurrentState(state, this.adaptee);
1789
+ }
1790
+ get currentState() {
1791
+ if (!this.adaptee) {
1792
+ const s = this.getCurrentState();
1793
+ throw new StateMachineError("no adaptee", s !== void 0 ? { state: s } : {});
1794
+ }
1795
+ return this.getCurrentState(this.adaptee) ?? "";
1796
+ }
1797
+ // Конструктор
1798
+ constructor(config, adaptee, options) {
1799
+ this.initialState = config.initialState;
1800
+ this.logger = options?.logger ?? ConsoleLogger;
1801
+ this.monitor = options?.monitor ?? createDefaultMonitor();
1802
+ this.scheduler = options?.scheduler ?? createDefaultScheduler();
1803
+ this.errorHandler = options?.errorHandler ?? createDefaultErrorHandler();
1804
+ if (adaptee) {
1805
+ if (!isAdapter(adaptee)) {
1806
+ this.adaptee = new MemoryAdapter(adaptee);
1807
+ } else {
1808
+ this.adaptee = adaptee;
1809
+ }
1810
+ }
1811
+ if (options?.transitionTimeout !== void 0) {
1812
+ this.transitionTimeout = options.transitionTimeout;
1813
+ }
1814
+ if (options?.errorState !== void 0) {
1815
+ this.errorState = options.errorState;
1816
+ }
1817
+ if (options?.abortOnExitError !== void 0) {
1818
+ this.abortOnExitError = options.abortOnExitError;
1819
+ }
1820
+ if (options?.maxQueueDepth !== void 0) {
1821
+ this.maxQueueDepth = options.maxQueueDepth;
1822
+ }
1823
+ if (config.onError !== void 0) {
1824
+ this.onError = config.onError;
1825
+ }
1826
+ this.stateAttribute = config.stateAttribute;
1827
+ this.states = /* @__PURE__ */ new Map();
1828
+ this.events = new Map(
1829
+ Object.entries(config.events).map(([name, value]) => [
1830
+ name,
1831
+ {
1832
+ name,
1833
+ ...value,
1834
+ transitions: value.transitions.map((t) => ({
1835
+ ...t,
1836
+ // Support 'action' alias for 'onTransition'
1837
+ onTransition: t.onTransition || t.action
1838
+ }))
1839
+ }
1840
+ ])
1841
+ );
1842
+ this.processStates(config.states);
1843
+ if (adaptee) {
1844
+ this.persistenceAdapter = adaptee;
1845
+ this.setInitialState(config.initialState);
1846
+ }
1847
+ this.logger.info("StateMachine initialized", { name: config.name });
1848
+ }
1849
+ // Основные публичные методы
1850
+ setContext(context) {
1851
+ this.context = context;
1852
+ }
1853
+ enqueueEvent(eventName, obj, args, type) {
1854
+ if (this.externalQueue.length + this.internalQueue.length >= this.maxQueueDepth) {
1855
+ const _s0 = this.getCurrentState(obj);
1856
+ return Promise.reject(
1857
+ new StateMachineError("Event queue overflow \u2014 possible infinite loop", {
1858
+ /* c8 ignore next */
1859
+ ..._s0 !== void 0 ? { state: _s0 } : {},
1860
+ event: eventName
1861
+ })
1862
+ );
1863
+ }
1864
+ if (type === "external") {
1865
+ return new Promise((resolve, reject) => {
1866
+ this.externalQueue.push({
1867
+ id: `ext_${++this.eventIdCounter}`,
1868
+ eventName,
1869
+ obj,
1870
+ args,
1871
+ resolve,
1872
+ reject,
1873
+ timestamp: Date.now(),
1874
+ type: "external"
1875
+ });
1876
+ this.scheduleProcessing();
1877
+ });
1878
+ }
1879
+ this.internalQueue.push({
1880
+ id: `int_${++this.eventIdCounter}`,
1881
+ eventName,
1882
+ obj,
1883
+ args,
1884
+ timestamp: Date.now(),
1885
+ type: "internal"
1886
+ });
1887
+ return Promise.resolve(true);
1888
+ }
1889
+ raiseEvent(eventName, obj, ...args) {
1890
+ this.internalQueue.push({
1891
+ id: `int_${++this.eventIdCounter}`,
1892
+ eventName,
1893
+ obj,
1894
+ args,
1895
+ timestamp: Date.now(),
1896
+ type: "internal"
1897
+ });
1898
+ }
1899
+ scheduleProcessing() {
1900
+ if (this.isProcessing) return;
1901
+ queueMicrotask(() => this.processQueues());
1902
+ }
1903
+ async processQueues() {
1904
+ if (this.isProcessing) return;
1905
+ this.isProcessing = true;
1906
+ try {
1907
+ while (this.internalQueue.length > 0 || this.externalQueue.length > 0) {
1908
+ if (this.transitionDepth >= this.MAX_TRANSITION_DEPTH) {
1909
+ const _s1 = this.getCurrentState();
1910
+ const error = new StateMachineError(
1911
+ "Max transition depth exceeded \u2014 possible infinite loop",
1912
+ /* c8 ignore next */
1913
+ { ..._s1 !== void 0 ? { state: _s1 } : {}, event: "processQueues" }
1914
+ );
1915
+ while (this.externalQueue.length > 0) {
1916
+ const evt = this.externalQueue.shift();
1917
+ evt.reject?.(error);
1918
+ }
1919
+ this.internalQueue.length = 0;
1920
+ throw error;
1921
+ }
1922
+ if (this.internalQueue.length > 0) {
1923
+ const evt = this.internalQueue.shift();
1924
+ this.transitionDepth++;
1925
+ try {
1926
+ await this.executeQueuedTransition(evt);
1927
+ } finally {
1928
+ this.transitionDepth--;
1929
+ }
1930
+ } else {
1931
+ const evt = this.externalQueue.shift();
1932
+ this.transitionDepth++;
1933
+ try {
1934
+ const result = await this.executeQueuedTransition(evt);
1935
+ evt.resolve?.(result);
1936
+ } catch (error) {
1937
+ evt.reject?.(error);
1938
+ } finally {
1939
+ this.transitionDepth--;
1940
+ }
1941
+ }
1942
+ }
1943
+ } finally {
1944
+ this.isProcessing = false;
1945
+ this.transitionDepth = 0;
1946
+ }
1947
+ }
1948
+ async executeQueuedTransition(queuedEvent) {
1949
+ const { eventName, obj, args } = queuedEvent;
1950
+ const targetObj = obj;
1951
+ let currentStateRaw = this.getCurrentState(targetObj);
1952
+ if (!currentStateRaw) {
1953
+ this.setInitialState(this.initialState, targetObj);
1954
+ currentStateRaw = this.getCurrentState(targetObj);
1955
+ }
1956
+ const currentState = currentStateRaw ?? "";
1957
+ let event = this.events.get(eventName);
1958
+ let transitions = event ? event.transitions.filter(
1959
+ (t) => this.isTransitionPossible(t, currentState)
1960
+ ) : [];
1961
+ if (!transitions.length) {
1962
+ const wildcardEvent = this.events.get("*");
1963
+ if (wildcardEvent) {
1964
+ const wildcardTransitions = wildcardEvent.transitions.filter(
1965
+ (t) => this.isTransitionPossible(t, currentState)
1966
+ );
1967
+ if (wildcardTransitions.length > 0) {
1968
+ event = wildcardEvent;
1969
+ transitions = wildcardTransitions;
1970
+ }
1971
+ }
1972
+ }
1973
+ if (!event || !transitions.length) {
1974
+ throw new StateMachineError(
1975
+ `Invalid event: ${eventName} for state: ${currentState}`,
1976
+ { state: currentState, event: eventName }
1977
+ );
1978
+ }
1979
+ const allowedTransition = await this.getAllowedTransitions(
1980
+ targetObj,
1981
+ transitions,
1982
+ ...args
1983
+ ).catch((error) => {
1984
+ this.logger.error(
1985
+ "Error determining allowed transition",
1986
+ { event: eventName, state: currentState },
1987
+ /* c8 ignore next */
1988
+ error instanceof Error ? error : new Error(String(error))
1989
+ );
1990
+ return void 0;
1991
+ });
1992
+ if (!allowedTransition) {
1993
+ return false;
1994
+ }
1995
+ const toState = await this.applyTransition(
1996
+ targetObj,
1997
+ currentState,
1998
+ allowedTransition,
1999
+ args,
2000
+ eventName,
2001
+ event
2002
+ ).catch((error) => {
2003
+ this.logger.error(
2004
+ "Error applying transition",
2005
+ {
2006
+ event: eventName,
2007
+ state: currentState,
2008
+ transition: `${allowedTransition.from} -> ${allowedTransition.to}`
2009
+ },
2010
+ /* c8 ignore next */
2011
+ error instanceof Error ? error : new Error(String(error))
2012
+ );
2013
+ if (this.errorHandler.isEnabled()) {
2014
+ this.monitor.recordError(
2015
+ error instanceof Error ? error : new Error(String(error)),
2016
+ { state: currentState, event: eventName }
2017
+ );
2018
+ }
2019
+ throw error;
2020
+ });
2021
+ if (!toState) {
2022
+ return false;
2023
+ }
2024
+ this.setCurrentState(toState.name, targetObj);
2025
+ return true;
2026
+ }
2027
+ async fireEvent(eventName, obj, ...args) {
2028
+ let targetObj;
2029
+ if (!obj) {
2030
+ if (this.adaptee) targetObj = this.adaptee;
2031
+ else
2032
+ throw new StateMachineError("no adaptee or object passed", {
2033
+ event: String(eventName)
2034
+ });
2035
+ } else if (!isAdapter(obj)) {
2036
+ args.unshift(obj);
2037
+ if (this.adaptee) targetObj = this.adaptee;
2038
+ else
2039
+ throw new StateMachineError("no adaptee or object passed", {
2040
+ event: String(eventName)
2041
+ });
2042
+ } else {
2043
+ targetObj = obj;
2044
+ }
2045
+ return this.enqueueEvent(String(eventName), targetObj, args, "external");
2046
+ }
2047
+ getQueueDepth() {
2048
+ return {
2049
+ internal: this.internalQueue.length,
2050
+ external: this.externalQueue.length,
2051
+ total: this.internalQueue.length + this.externalQueue.length
2052
+ };
2053
+ }
2054
+ getQueuedEvents() {
2055
+ const now = Date.now();
2056
+ const mapEvent = (evt) => ({
2057
+ id: evt.id,
2058
+ event: evt.eventName,
2059
+ age: now - evt.timestamp,
2060
+ type: evt.type
2061
+ });
2062
+ return [
2063
+ ...this.internalQueue.map(mapEvent),
2064
+ ...this.externalQueue.map(mapEvent)
2065
+ ];
2066
+ }
2067
+ isProcessingEvents() {
2068
+ return this.isProcessing;
2069
+ }
2070
+ /**
2071
+ * Checks if an event can be fired in the current state.
2072
+ *
2073
+ * @param eventName - The name of the event to check.
2074
+ * @param adaptee - Optional adapter/object to check against (defaults to internal adaptee).
2075
+ * @returns boolean - True if the event has a valid transition from the current state (guards are not executed).
2076
+ */
2077
+ canFireEvent(eventName, adaptee) {
2078
+ const targetAdaptee = adaptee || this.adaptee;
2079
+ if (!targetAdaptee) return false;
2080
+ const currentState = this.getCurrentState(targetAdaptee);
2081
+ const effectiveState = currentState === "" ? this.getInitialCompositeState(this.initialState) : currentState;
2082
+ if (effectiveState === void 0) return false;
2083
+ const event = this.events.get(String(eventName));
2084
+ if (!event || !event.transitions.some(
2085
+ (t) => this.isTransitionPossible(t, effectiveState)
2086
+ )) {
2087
+ const wildcardEvent = this.events.get("*");
2088
+ if (wildcardEvent) {
2089
+ return wildcardEvent.transitions.some(
2090
+ (t) => this.isTransitionPossible(t, effectiveState)
2091
+ );
2092
+ }
2093
+ return false;
2094
+ }
2095
+ return true;
2096
+ }
2097
+ getAvailableEvents(adaptee) {
2098
+ const available = [];
2099
+ for (const [name] of this.events) {
2100
+ if (this.canFireEvent(name, adaptee)) {
2101
+ available.push(String(name));
2102
+ }
2103
+ }
2104
+ return available;
2105
+ }
2106
+ async reset(adaptee) {
2107
+ const targetAdaptee = adaptee || this.adaptee;
2108
+ if (!targetAdaptee) {
2109
+ const _s2 = this.getCurrentState();
2110
+ throw new StateMachineError("no adaptee", _s2 !== void 0 ? { state: _s2 } : {});
2111
+ }
2112
+ this.historyMap.clear();
2113
+ for (const timers of this.activeTimers.values()) {
2114
+ for (const id of timers) {
2115
+ this.clearTimer(id);
2116
+ }
2117
+ }
2118
+ this.activeTimers.clear();
2119
+ this.stateEntryTimes.clear();
2120
+ this.setInitialState(this.initialState, targetAdaptee);
2121
+ }
2122
+ getStateHistory() {
2123
+ return Object.fromEntries(this.historyMap);
2124
+ }
2125
+ getCurrentStateInfo() {
2126
+ const currentState = this.getCurrentState();
2127
+ if (currentState === void 0) return void 0;
2128
+ const isComposite = currentState.includes("|");
2129
+ if (isComposite) {
2130
+ const activeStates = currentState.split("|").filter(Boolean);
2131
+ const regions2 = activeStates.map((s) => this.getRegionKey(s));
2132
+ return {
2133
+ name: currentState,
2134
+ isComposite: true,
2135
+ regions: regions2,
2136
+ children: activeStates
2137
+ };
2138
+ }
2139
+ const state = this.states.get(currentState);
2140
+ if (!state) return void 0;
2141
+ const parent = currentState.includes(".") ? currentState.split(".").slice(0, -1).join(".") : void 0;
2142
+ const children = this.getDirectChildren(currentState);
2143
+ const regions = state.regions ? Object.keys(state.regions) : void 0;
2144
+ return {
2145
+ name: currentState,
2146
+ ...state.display !== void 0 ? { display: state.display } : {},
2147
+ isComposite: Boolean(state.regions),
2148
+ ...regions !== void 0 ? { regions } : {},
2149
+ ...parent !== void 0 ? { parent } : {},
2150
+ ...children.length ? { children } : {}
2151
+ };
2152
+ }
2153
+ /**
2154
+ * Checks if the machine is in a specific state.
2155
+ * Supports hierarchical states (e.g. 'parent' matches 'parent.child').
2156
+ *
2157
+ * @param expectedState - The state name to check.
2158
+ * @param adaptee - Optional adapter to check against.
2159
+ * @returns boolean - True if the current state matches or is a substate of the expected state.
2160
+ */
2161
+ isInState(expectedState, adaptee) {
2162
+ const currentState = this.getCurrentState(adaptee);
2163
+ if (!currentState) return expectedState === "";
2164
+ const currentParts = currentState.split("|").sort();
2165
+ const expectedParts = expectedState.split("|").sort();
2166
+ if (currentParts.length !== expectedParts.length) return false;
2167
+ return currentParts.every((part, index) => part === expectedParts[index]);
2168
+ }
2169
+ attachToObject(object, eventMap) {
2170
+ for (const objectEventName in eventMap) {
2171
+ if (eventMap.hasOwnProperty(objectEventName)) {
2172
+ const stateMachineEventName = eventMap[objectEventName];
2173
+ if (stateMachineEventName === void 0) continue;
2174
+ if (typeof object.addEventListener === "function") {
2175
+ object.addEventListener(objectEventName, (...args) => {
2176
+ this.fireEvent(stateMachineEventName, object, ...args).catch(
2177
+ (e) => this.logger.error(
2178
+ "Error firing event",
2179
+ {
2180
+ objectEventName,
2181
+ stateMachineEventName
2182
+ },
2183
+ /* c8 ignore next */
2184
+ e instanceof Error ? e : new Error(String(e))
2185
+ )
2186
+ );
2187
+ });
2188
+ } else if (typeof object.on === "function") {
2189
+ object.on(objectEventName, (...args) => {
2190
+ this.fireEvent(stateMachineEventName, object, ...args).catch(
2191
+ (e) => this.logger.error(
2192
+ "Error firing event",
2193
+ {
2194
+ objectEventName,
2195
+ stateMachineEventName
2196
+ },
2197
+ /* c8 ignore next */
2198
+ e instanceof Error ? e : new Error(String(e))
2199
+ )
2200
+ );
2201
+ });
2202
+ } else {
2203
+ object[`on${objectEventName}`] = async (...args) => {
2204
+ return this.fireEvent(stateMachineEventName, object, ...args).catch(
2205
+ (e) => this.logger.error(
2206
+ "Error firing event",
2207
+ {
2208
+ objectEventName,
2209
+ stateMachineEventName
2210
+ },
2211
+ /* c8 ignore next */
2212
+ e instanceof Error ? e : new Error(String(e))
2213
+ )
2214
+ );
2215
+ };
2216
+ }
2217
+ }
2218
+ }
2219
+ }
2220
+ // Методы для работы с состоянием
2221
+ async saveState(adapter) {
2222
+ const targetAdapter = adapter || this.persistenceAdapter;
2223
+ if (!targetAdapter) {
2224
+ return;
2225
+ }
2226
+ const currentState = this.getCurrentState() ?? "";
2227
+ const history = Object.fromEntries(this.historyMap);
2228
+ const stateData = {
2229
+ currentState,
2230
+ history,
2231
+ stateEntryTimes: Object.fromEntries(this.stateEntryTimes)
2232
+ };
2233
+ await targetAdapter.save(stateData);
2234
+ }
2235
+ async restoreState(adapter) {
2236
+ const targetAdapter = adapter || this.persistenceAdapter;
2237
+ if (!targetAdapter) {
2238
+ return;
2239
+ }
2240
+ const result = await targetAdapter.restore();
2241
+ this.validateCompositeState(result.currentState);
2242
+ this.historyMap = new Map(Object.entries(result.history));
2243
+ if (result.stateEntryTimes) {
2244
+ this.stateEntryTimes = new Map(Object.entries(result.stateEntryTimes));
2245
+ }
2246
+ this.setCurrentState(result.currentState);
2247
+ this.resumeTimers();
2248
+ }
2249
+ // Статические методы
2250
+ static fromData(config, initialState, context, options) {
2251
+ const sm = new _StateMachine(config, context, options);
2252
+ if (sm.adaptee) {
2253
+ sm.setCurrentState(initialState || config.initialState);
2254
+ }
2255
+ return sm;
2256
+ }
2257
+ static fromJSON(jsonData, obj, options) {
2258
+ const parsedData = JSON.parse(jsonData);
2259
+ const { config, currentState, historyMap, stateEntryTimes } = parsedData;
2260
+ const deserializedStates = _StateMachine.deserializeStates(
2261
+ config.states
2262
+ );
2263
+ const deserializedEvents = _StateMachine.deserializeEvents(
2264
+ config.events
2265
+ );
2266
+ const smConfig = {
2267
+ name: "DeserializedStateMachine",
2268
+ initialState: config.initialState,
2269
+ stateAttribute: config.stateAttribute,
2270
+ states: deserializedStates,
2271
+ events: deserializedEvents,
2272
+ onError: _StateMachine.deserializeAction(config.onError)
2273
+ };
2274
+ const sm = new _StateMachine(
2275
+ smConfig,
2276
+ obj,
2277
+ options
2278
+ );
2279
+ sm.historyMap = new Map(historyMap);
2280
+ if (stateEntryTimes) {
2281
+ sm.stateEntryTimes = new Map(stateEntryTimes);
2282
+ }
2283
+ if (sm.adaptee && currentState) {
2284
+ sm.setCurrentState(currentState);
2285
+ sm.resumeTimers();
2286
+ }
2287
+ return sm;
2288
+ }
2289
+ /**
2290
+ * Securely deserializes a StateMachine from JSON string (Async).
2291
+ * Verifies cryptographic hashes of serialized functions.
2292
+ */
2293
+ static async fromSecureJSON(jsonData, obj, options) {
2294
+ const parsedData = JSON.parse(jsonData);
2295
+ const { config, currentState, historyMap, stateEntryTimes } = parsedData;
2296
+ const deserializedStates = await _StateMachine.deserializeStatesAsync(config.states);
2297
+ const deserializedEvents = await _StateMachine.deserializeEventsAsync(config.events);
2298
+ const smConfig = {
2299
+ name: "DeserializedStateMachine",
2300
+ initialState: config.initialState,
2301
+ stateAttribute: config.stateAttribute,
2302
+ states: deserializedStates,
2303
+ events: deserializedEvents,
2304
+ onError: await _StateMachine.deserializeActionAsync(
2305
+ config.onError
2306
+ )
2307
+ };
2308
+ const sm = new _StateMachine(
2309
+ smConfig,
2310
+ obj,
2311
+ options
2312
+ );
2313
+ sm.historyMap = new Map(historyMap);
2314
+ if (stateEntryTimes) {
2315
+ sm.stateEntryTimes = new Map(stateEntryTimes);
2316
+ }
2317
+ if (sm.adaptee && currentState) {
2318
+ sm.setCurrentState(currentState);
2319
+ sm.resumeTimers();
2320
+ }
2321
+ return sm;
2322
+ }
2323
+ /**
2324
+ * Deserialize states configuration (Async)
2325
+ */
2326
+ static async deserializeStatesAsync(statesConfig) {
2327
+ const result = {};
2328
+ for (const [name, stateData] of Object.entries(statesConfig)) {
2329
+ const deserializedState = {
2330
+ ...stateData,
2331
+ onBeforeEnter: await _StateMachine.deserializeActionAsync(
2332
+ stateData.onBeforeEnter
2333
+ ),
2334
+ onEnter: await _StateMachine.deserializeActionAsync(stateData.onEnter),
2335
+ onAfterEnter: await _StateMachine.deserializeActionAsync(
2336
+ stateData.onAfterEnter
2337
+ ),
2338
+ onBeforeExit: await _StateMachine.deserializeActionAsync(
2339
+ stateData.onBeforeExit
2340
+ ),
2341
+ onExit: await _StateMachine.deserializeActionAsync(stateData.onExit),
2342
+ onAfterExit: await _StateMachine.deserializeActionAsync(
2343
+ stateData.onAfterExit
2344
+ ),
2345
+ onError: await _StateMachine.deserializeActionAsync(stateData.onError)
2346
+ };
2347
+ if (stateData.invoke && Array.isArray(stateData.invoke)) {
2348
+ deserializedState.invoke = await Promise.all(
2349
+ stateData.invoke.map(async (inv) => ({
2350
+ ...inv,
2351
+ cond: await _StateMachine.deserializeActionAsync(inv.cond),
2352
+ action: await _StateMachine.deserializeActionAsync(inv.action)
2353
+ }))
2354
+ );
2355
+ }
2356
+ result[name] = deserializedState;
2357
+ }
2358
+ return result;
2359
+ }
2360
+ /**
2361
+ * Deserialize states configuration
2362
+ */
2363
+ static deserializeStates(statesConfig) {
2364
+ return Object.entries(statesConfig).reduce(
2365
+ (acc, [name, stateData]) => {
2366
+ const deserializedState = {
2367
+ ...stateData,
2368
+ onBeforeEnter: _StateMachine.deserializeAction(
2369
+ stateData.onBeforeEnter
2370
+ ),
2371
+ onEnter: _StateMachine.deserializeAction(stateData.onEnter),
2372
+ onAfterEnter: _StateMachine.deserializeAction(stateData.onAfterEnter),
2373
+ onBeforeExit: _StateMachine.deserializeAction(stateData.onBeforeExit),
2374
+ onExit: _StateMachine.deserializeAction(stateData.onExit),
2375
+ onAfterExit: _StateMachine.deserializeAction(stateData.onAfterExit),
2376
+ onError: _StateMachine.deserializeAction(stateData.onError)
2377
+ };
2378
+ if (stateData.invoke && Array.isArray(stateData.invoke)) {
2379
+ deserializedState.invoke = stateData.invoke.map((inv) => ({
2380
+ ...inv,
2381
+ cond: _StateMachine.deserializeAction(inv.cond),
2382
+ action: _StateMachine.deserializeAction(inv.action)
2383
+ }));
2384
+ }
2385
+ acc[name] = deserializedState;
2386
+ return acc;
2387
+ },
2388
+ {}
2389
+ );
2390
+ }
2391
+ /**
2392
+ * Deserialize events configuration (Async)
2393
+ */
2394
+ static async deserializeEventsAsync(eventsConfig) {
2395
+ const result = {};
2396
+ for (const [name, eventData] of Object.entries(eventsConfig)) {
2397
+ result[name] = {
2398
+ ...eventData,
2399
+ onBefore: await _StateMachine.deserializeActionAsync(eventData.onBefore),
2400
+ onAfter: await _StateMachine.deserializeActionAsync(eventData.onAfter),
2401
+ onSuccess: await _StateMachine.deserializeActionAsync(
2402
+ eventData.onSuccess
2403
+ ),
2404
+ onError: await _StateMachine.deserializeActionAsync(eventData.onError),
2405
+ transitions: await Promise.all(
2406
+ eventData.transitions.map(
2407
+ (transitionData) => _StateMachine.deserializeTransitionAsync(transitionData)
2408
+ )
2409
+ )
2410
+ };
2411
+ }
2412
+ return result;
2413
+ }
2414
+ /**
2415
+ * Deserialize events configuration
2416
+ */
2417
+ static deserializeEvents(eventsConfig) {
2418
+ return Object.entries(eventsConfig).reduce(
2419
+ (acc, [name, eventData]) => {
2420
+ acc[name] = {
2421
+ ...eventData,
2422
+ onBefore: _StateMachine.deserializeAction(eventData.onBefore),
2423
+ onAfter: _StateMachine.deserializeAction(eventData.onAfter),
2424
+ onSuccess: _StateMachine.deserializeAction(eventData.onSuccess),
2425
+ onError: _StateMachine.deserializeAction(eventData.onError),
2426
+ transitions: eventData.transitions.map(
2427
+ (transitionData) => _StateMachine.deserializeTransition(transitionData)
2428
+ )
2429
+ };
2430
+ return acc;
2431
+ },
2432
+ {}
2433
+ );
2434
+ }
2435
+ /**
2436
+ * Deserialize transition configuration (Async)
2437
+ */
2438
+ static async deserializeTransitionAsync(transitionData) {
2439
+ return {
2440
+ ...transitionData,
2441
+ guard: await _StateMachine.deserializeActionAsync(transitionData.guard),
2442
+ // Support 'action' alias for 'onTransition'
2443
+ onTransition: await _StateMachine.deserializeActionAsync(
2444
+ transitionData.onTransition || transitionData.action
2445
+ ),
2446
+ onError: await _StateMachine.deserializeActionAsync(
2447
+ transitionData.onError
2448
+ )
2449
+ };
2450
+ }
2451
+ /**
2452
+ * Deserialize transition configuration
2453
+ */
2454
+ static deserializeTransition(transitionData) {
2455
+ return {
2456
+ ...transitionData,
2457
+ guard: _StateMachine.deserializeAction(transitionData.guard),
2458
+ // Support 'action' alias for 'onTransition' for compatibility
2459
+ onTransition: _StateMachine.deserializeAction(
2460
+ transitionData.onTransition || transitionData.action
2461
+ ),
2462
+ onError: _StateMachine.deserializeAction(transitionData.onError)
2463
+ };
2464
+ }
2465
+ /**
2466
+ * Static method for deserializing actions (Async)
2467
+ */
2468
+ static async deserializeActionAsync(action) {
2469
+ if (!action) return action;
2470
+ if (typeof action === "object" && action.type === "function") {
2471
+ return safeFunctionSerializer.deserializeActionAsync(action);
2472
+ }
2473
+ return _StateMachine.deserializeAction(action);
2474
+ }
2475
+ /**
2476
+ * Static method for deserializing actions (now using safe deserializer)
2477
+ */
2478
+ static deserializeAction(action) {
2479
+ if (!action) return action;
2480
+ if (typeof action === "object" && action.type === "function") {
2481
+ return safeFunctionSerializer.deserializeAction(action);
2482
+ }
2483
+ if (typeof action === "string") {
2484
+ const trimmed = action.trim();
2485
+ if (trimmed.startsWith("function") || trimmed.startsWith("(") || trimmed.includes("=>")) {
2486
+ const safeFn = safeFunctionSerializer.deserializeLegacyString(action);
2487
+ if (safeFn) {
2488
+ return safeFn;
2489
+ }
2490
+ return void 0;
2491
+ }
2492
+ }
2493
+ return action;
2494
+ }
2495
+ static fromJSONWithContext(jsonData, context, options) {
2496
+ const sm = _StateMachine.fromJSON(
2497
+ jsonData,
2498
+ void 0,
2499
+ options
2500
+ );
2501
+ if (context !== void 0) {
2502
+ sm.setContext(context);
2503
+ }
2504
+ return sm;
2505
+ }
2506
+ // Приватные методы
2507
+ setCurrentState(state, obj) {
2508
+ const adaptee = obj || this.adaptee;
2509
+ const stateConfig = this.states.get(state);
2510
+ if (stateConfig?.history && stateConfig.history !== "deep" && stateConfig.regions) {
2511
+ const historyState = this.historyMap.get(state);
2512
+ if (historyState && adaptee) {
2513
+ const currentState = this.getCurrentState(adaptee) ?? "";
2514
+ const newCompositeState = this.updatePartialState(
2515
+ currentState,
2516
+ state,
2517
+ historyState
2518
+ );
2519
+ adaptee.set(
2520
+ this.stateAttribute,
2521
+ newCompositeState
2522
+ );
2523
+ return;
2524
+ }
2525
+ }
2526
+ if (stateConfig?.history === "deep" && stateConfig.regions) {
2527
+ const historyState = this.historyMap.get(state);
2528
+ if (historyState && adaptee) {
2529
+ adaptee.set(
2530
+ this.stateAttribute,
2531
+ historyState
2532
+ );
2533
+ return;
2534
+ }
2535
+ }
2536
+ if (adaptee) {
2537
+ this.setCurrentStateInternal(state, adaptee);
2538
+ }
2539
+ }
2540
+ setCurrentStateInternal(state, adaptee) {
2541
+ const currentState = this.getCurrentState(adaptee) || "";
2542
+ const currentStateMap = this.parseCompositeState(currentState);
2543
+ const newStateParts = state.split("|");
2544
+ const isRootState = newStateParts.length === 1 && !state.includes(".");
2545
+ if (isRootState) {
2546
+ currentStateMap.clear();
2547
+ currentStateMap.set(state, state);
2548
+ } else {
2549
+ for (const newStatePart of newStateParts) {
2550
+ if (!this.states.has(newStatePart)) {
2551
+ throw new StateMachineError(
2552
+ `Invalid state path: ${newStatePart} in composite state: ${state} states: ${Array.from(this.states.keys()).join(",")}`,
2553
+ { state: newStatePart }
2554
+ );
2555
+ }
2556
+ const regionKey = this.getRegionKey(newStatePart);
2557
+ const stateConfig = this.states.get(newStatePart);
2558
+ for (const [existingRegionKey] of currentStateMap.entries()) {
2559
+ if (existingRegionKey === regionKey || existingRegionKey.startsWith(regionKey + ".") || regionKey.startsWith(existingRegionKey + ".")) {
2560
+ currentStateMap.delete(existingRegionKey);
2561
+ }
2562
+ }
2563
+ if (stateConfig?.regions) {
2564
+ const initialStatesForRegions = this.getInitialStatesForRegions(
2565
+ stateConfig.regions,
2566
+ newStatePart
2567
+ );
2568
+ const regionStates = initialStatesForRegions.split("|");
2569
+ for (const regionState of regionStates) {
2570
+ const regionKeyNested = this.getRegionKey(regionState);
2571
+ currentStateMap.set(regionKeyNested, regionState);
2572
+ }
2573
+ } else {
2574
+ currentStateMap.set(regionKey, newStatePart);
2575
+ }
2576
+ }
2577
+ for (const [key, value] of currentStateMap.entries()) {
2578
+ if (!value.includes(".")) {
2579
+ currentStateMap.delete(key);
2580
+ }
2581
+ }
2582
+ }
2583
+ const newCompositeState = Array.from(currentStateMap.values()).join("|");
2584
+ this.validateCompositeState(newCompositeState);
2585
+ adaptee.set(
2586
+ this.stateAttribute,
2587
+ newCompositeState
2588
+ );
2589
+ }
2590
+ getCurrentState(adaptee) {
2591
+ const targetAdaptee = adaptee || this.adaptee;
2592
+ if (!targetAdaptee) return;
2593
+ const currentState = targetAdaptee.get(this.stateAttribute);
2594
+ if (!currentState) return "";
2595
+ const stateParts = currentState.split("|");
2596
+ for (const statePart of stateParts) {
2597
+ if (!this.states.has(statePart)) {
2598
+ throw new StateMachineError(
2599
+ `Invalid state path in current state: ${statePart}`,
2600
+ { state: currentState }
2601
+ );
2602
+ }
2603
+ }
2604
+ return currentState;
2605
+ }
2606
+ setInitialState(initialState, obj) {
2607
+ const targetAdaptee = obj || this.adaptee;
2608
+ if (!targetAdaptee) return;
2609
+ const stateConfig = this.states.get(initialState);
2610
+ let initialStates;
2611
+ if (stateConfig?.regions) {
2612
+ initialStates = this.getInitialStatesForRegions(
2613
+ stateConfig.regions,
2614
+ initialState
2615
+ );
2616
+ } else {
2617
+ initialStates = initialState;
2618
+ }
2619
+ this.setCurrentState(initialStates, targetAdaptee);
2620
+ const stateParts = initialStates.split("|");
2621
+ const context = {
2622
+ state: initialStates,
2623
+ phase: "enter"
2624
+ };
2625
+ for (const statePart of stateParts) {
2626
+ this.executeEnterActions(
2627
+ targetAdaptee,
2628
+ statePart,
2629
+ [],
2630
+ context
2631
+ ).catch((err) => {
2632
+ this.logger.error(
2633
+ "Error in initial state enter actions",
2634
+ { state: statePart },
2635
+ err
2636
+ );
2637
+ });
2638
+ }
2639
+ }
2640
+ getInitialCompositeState(initialState) {
2641
+ const stateConfig = this.states.get(initialState);
2642
+ if (stateConfig?.regions) {
2643
+ return this.getInitialStatesForRegions(stateConfig.regions, initialState);
2644
+ }
2645
+ return initialState;
2646
+ }
2647
+ getDirectChildren(stateName) {
2648
+ const prefix = `${stateName}.`;
2649
+ const depth = stateName.split(".").length + 1;
2650
+ const children = [];
2651
+ for (const name of this.states.keys()) {
2652
+ const nameStr = String(name);
2653
+ if (!nameStr.startsWith(prefix)) continue;
2654
+ if (nameStr.split(".").length !== depth) continue;
2655
+ children.push(nameStr);
2656
+ }
2657
+ return children;
2658
+ }
2659
+ getInitialStatesForRegions(regions, parentPath) {
2660
+ const regionStates = [];
2661
+ for (const [regionName, regionStatesConfig] of Object.entries(regions)) {
2662
+ const regionPath = `${parentPath}.${regionName}`;
2663
+ const initialState = regionStatesConfig.initial || Object.keys(regionStatesConfig)[0];
2664
+ const fullPath = `${regionPath}.${initialState}`;
2665
+ const stateConfig = this.states.get(fullPath);
2666
+ if (stateConfig?.regions) {
2667
+ const nestedInitialStates = this.getInitialStatesForRegions(
2668
+ stateConfig.regions,
2669
+ fullPath
2670
+ );
2671
+ regionStates.push(...nestedInitialStates.split("|"));
2672
+ } else {
2673
+ regionStates.push(fullPath);
2674
+ }
2675
+ }
2676
+ return regionStates.join("|");
2677
+ }
2678
+ validateCompositeState(compositeState) {
2679
+ const stateParts = compositeState.split("|");
2680
+ const regionKeys = /* @__PURE__ */ new Set();
2681
+ for (const statePart of stateParts) {
2682
+ const regionKey = this.getRegionKey(statePart);
2683
+ if (regionKeys.has(regionKey)) {
2684
+ throw new StateMachineError(
2685
+ `Contradictory state detected: multiple states for region ${regionKey} in composite state ${compositeState}`,
2686
+ { state: compositeState }
2687
+ );
2688
+ }
2689
+ regionKeys.add(regionKey);
2690
+ }
2691
+ }
2692
+ processStates(statesConfig, parentStateName) {
2693
+ for (const [name, value] of Object.entries(statesConfig)) {
2694
+ if (name === "initial") continue;
2695
+ const stateName = parentStateName ? `${parentStateName}.${name}` : name;
2696
+ const state = { name: stateName, ...value };
2697
+ this.states.set(stateName, state);
2698
+ if (value.regions) {
2699
+ this.processRegions(value.regions, stateName);
2700
+ }
2701
+ }
2702
+ }
2703
+ processRegions(regionsConfig, parentStateName) {
2704
+ for (const [regionName, regionStatesConfig] of Object.entries(
2705
+ regionsConfig
2706
+ )) {
2707
+ this.processStates(regionStatesConfig, `${parentStateName}.${regionName}`);
2708
+ }
2709
+ }
2710
+ processError(adaptee, context, ...fallback) {
2711
+ let handler = (adaptee2, err) => {
2712
+ let currentState;
2713
+ try {
2714
+ if (adaptee2 && typeof adaptee2.get === "function") {
2715
+ currentState = this.getCurrentState(
2716
+ adaptee2
2717
+ ) ?? "";
2718
+ } else {
2719
+ currentState = context.state || "";
2720
+ }
2721
+ } catch {
2722
+ currentState = context.state || "";
2723
+ }
2724
+ const _phase = err instanceof StateMachineError ? err.context.phase ?? context.phase : context.phase;
2725
+ const _event = err instanceof StateMachineError ? err.context.event ?? context.event : context.event;
2726
+ const _action = err instanceof StateMachineError ? err.context.action ?? context.action : context.action;
2727
+ const _transition = err instanceof StateMachineError ? err.context.transition ?? context.transition : context.transition;
2728
+ const errorContext = {
2729
+ state: currentState,
2730
+ ..._phase !== void 0 ? { phase: _phase } : {},
2731
+ ..._event !== void 0 ? { event: _event } : {},
2732
+ ..._action !== void 0 ? { action: _action } : {},
2733
+ ..._transition !== void 0 ? { transition: _transition } : {}
2734
+ };
2735
+ throw new StateMachineError(
2736
+ `Error in state machine: ${err ? err instanceof Error ? err.message : String(err) : "Unknown error"}`,
2737
+ errorContext,
2738
+ err instanceof StateMachineError ? err.cause : err instanceof Error ? err : void 0
2739
+ );
2740
+ };
2741
+ const handlers = (fallback ?? [this.onError]).filter(Boolean);
2742
+ if (handlers.length > 0) {
2743
+ const r = handlers.map(
2744
+ (action) => action ? typeof action === "function" ? action : this.context && this.context[action] || adaptee.get(action) : void 0
2745
+ ).filter(Boolean).find((t) => t);
2746
+ if (r) handler = r;
2747
+ }
2748
+ return (...args) => {
2749
+ const targetAdaptee = args.length >= 2 ? args[0] : adaptee;
2750
+ const error = args.length >= 2 ? args[1] : args[0];
2751
+ return handler(targetAdaptee, error);
2752
+ };
2753
+ }
2754
+ async callAction(obj, actionName, ...args) {
2755
+ const _callActionState = this.getCurrentState(
2756
+ obj
2757
+ );
2758
+ const context = {
2759
+ /* c8 ignore next */
2760
+ ..._callActionState !== void 0 ? { state: _callActionState } : {},
2761
+ phase: "action",
2762
+ action: typeof actionName === "string" ? actionName : "anonymous"
2763
+ };
2764
+ const executeAction = async () => {
2765
+ try {
2766
+ if (this.context && this.context[actionName]) {
2767
+ const action = this.context[actionName];
2768
+ if (typeof action === "function") {
2769
+ const result = action(this.context, ...args);
2770
+ return result instanceof Promise ? await result : result;
2771
+ }
2772
+ } else if (typeof actionName === "function") {
2773
+ const result = actionName(obj, ...args);
2774
+ return result instanceof Promise ? await result : result;
2775
+ } else if (obj.get(actionName)) {
2776
+ const action = obj.get(actionName);
2777
+ if (typeof action === "function") {
2778
+ const result = action(obj, ...args);
2779
+ return result instanceof Promise ? await result : result;
2780
+ }
2781
+ }
2782
+ throw new StateMachineError("No action found", context);
2783
+ } catch (error) {
2784
+ if (error instanceof StateMachineError) throw error;
2785
+ throw new StateMachineError(
2786
+ `Error executing action: ${error instanceof Error ? error.message : String(error)}`,
2787
+ context,
2788
+ /* c8 ignore next */
2789
+ error instanceof Error ? error : void 0
2790
+ );
2791
+ }
2792
+ };
2793
+ if (this.transitionTimeout && this.transitionTimeout > 0) {
2794
+ return Promise.race([
2795
+ executeAction(),
2796
+ new Promise(
2797
+ (_, reject) => setTimeout(
2798
+ () => reject(new StateMachineError("Transition timeout", {
2799
+ /* c8 ignore next */
2800
+ action: typeof actionName === "string" ? actionName : "anonymous",
2801
+ phase: "action"
2802
+ })),
2803
+ this.transitionTimeout
2804
+ )
2805
+ )
2806
+ ]);
2807
+ }
2808
+ return executeAction();
2809
+ }
2810
+ async getAllowedTransitions(obj, transitions, ...args) {
2811
+ let highestPriority = Number.NEGATIVE_INFINITY;
2812
+ let selectedTransition;
2813
+ for (const transition of transitions) {
2814
+ if ((transition.priority ?? Number.NEGATIVE_INFINITY) < highestPriority) {
2815
+ continue;
2816
+ }
2817
+ const _guardState = this.getCurrentState(obj);
2818
+ const context = {
2819
+ /* c8 ignore next */
2820
+ ..._guardState !== void 0 ? { state: _guardState } : {},
2821
+ phase: "guard",
2822
+ transition: `${transition.from} -> ${transition.to}`
2823
+ };
2824
+ const guardResult = transition.guard ? await this.callAction(obj, transition.guard, ...args).catch(
2825
+ this.processError(
2826
+ obj,
2827
+ context,
2828
+ transition.onError,
2829
+ this.onError
2830
+ )
2831
+ ) : true;
2832
+ if (!guardResult) {
2833
+ continue;
2834
+ }
2835
+ highestPriority = transition.priority ?? Number.NEGATIVE_INFINITY;
2836
+ selectedTransition = transition;
2837
+ }
2838
+ return selectedTransition;
2839
+ }
2840
+ isTransitionPossible(transition, currentState) {
2841
+ const currentStates = this.parseCompositeState(currentState);
2842
+ if (transition.from === "*") {
2843
+ return true;
2844
+ }
2845
+ const fromStates = transition.from.split("|");
2846
+ return fromStates.every((fromState) => {
2847
+ if (fromState === "*") return true;
2848
+ const regionKey = this.getRegionKey(fromState);
2849
+ const currentStateForRegion = currentStates.get(regionKey);
2850
+ if (!currentStateForRegion) return false;
2851
+ return currentStateForRegion === fromState || this.isParentState(currentStateForRegion, fromState);
2852
+ });
2853
+ }
2854
+ isParentState(parentState, childState) {
2855
+ return childState.startsWith(parentState + ".") || childState === parentState;
2856
+ }
2857
+ async applyTransition(obj, currentState, transition, args, eventName, event) {
2858
+ const context = {
2859
+ state: currentState,
2860
+ event: String(eventName),
2861
+ transition: `${transition.from} -> ${transition.to}`,
2862
+ phase: "transition"
2863
+ };
2864
+ const targetState = transition.to === "*" ? currentState : transition.to;
2865
+ this._isTransitioning = true;
2866
+ this._targetState = targetState;
2867
+ try {
2868
+ if (transition.guard) {
2869
+ const allow = await this.callAction(obj, transition.guard, ...args).catch(
2870
+ this.processError(
2871
+ obj,
2872
+ { ...context, phase: "guard" },
2873
+ transition.onError,
2874
+ this.onError
2875
+ )
2876
+ );
2877
+ if (!allow) {
2878
+ return void 0;
2879
+ }
2880
+ }
2881
+ if (event.onBefore) {
2882
+ await this.callAction(obj, event.onBefore, ...args).catch(
2883
+ this.processError(
2884
+ obj,
2885
+ { ...context },
2886
+ transition.onError,
2887
+ this.onError
2888
+ )
2889
+ );
2890
+ }
2891
+ try {
2892
+ await this.executeExitActions(obj, transition.from, args, context);
2893
+ } catch (error) {
2894
+ if (this.abortOnExitError) {
2895
+ this.logger.warn("Transition aborted due to onExit error", { state: currentState, error });
2896
+ return void 0;
2897
+ }
2898
+ throw error;
2899
+ }
2900
+ this.manageStateHistory(transition.from, currentState, obj);
2901
+ if (transition.onTransition) {
2902
+ await this.callAction(obj, transition.onTransition, ...args).catch(
2903
+ this.processError(
2904
+ obj,
2905
+ { ...context },
2906
+ transition.onError,
2907
+ this.onError
2908
+ )
2909
+ );
2910
+ }
2911
+ try {
2912
+ await this.executeEnterActions(obj, transition.to, args, context);
2913
+ } catch (error) {
2914
+ if (this.errorState) {
2915
+ this.logger.error(`Failed to enter state '${targetState}'. Fallback to error state '${this.errorState}'`, { error });
2916
+ const newState2 = this.updateState(currentState, this.errorState);
2917
+ this.setCurrentState(newState2, obj);
2918
+ return this.states.get(this.errorState);
2919
+ }
2920
+ throw error;
2921
+ }
2922
+ if (event.onAfter) {
2923
+ try {
2924
+ await this.callAction(obj, event.onAfter, ...args);
2925
+ } catch (error) {
2926
+ const errorHandler = this.processError(
2927
+ obj,
2928
+ { ...context },
2929
+ event.onError,
2930
+ this.onError
2931
+ );
2932
+ errorHandler(obj, error);
2933
+ }
2934
+ }
2935
+ const transitionStartTime = Date.now();
2936
+ const newState = this.updateState(currentState, targetState);
2937
+ this.setCurrentState(newState, obj);
2938
+ const transitionTime = Date.now() - transitionStartTime;
2939
+ this.monitor.recordTransition(transitionTime, true);
2940
+ return this.states.get(targetState);
2941
+ } finally {
2942
+ this._isTransitioning = false;
2943
+ this._targetState = void 0;
2944
+ }
2945
+ }
2946
+ /**
2947
+ * Execute exit actions for a state
2948
+ */
2949
+ async executeExitActions(obj, fromStateName, args, context) {
2950
+ const fromState = this.states.get(fromStateName);
2951
+ if (this.activeTimers.has(fromStateName)) {
2952
+ const timers = this.activeTimers.get(fromStateName) || [];
2953
+ for (const timerId of timers) {
2954
+ this.clearTimer(timerId);
2955
+ }
2956
+ this.activeTimers.delete(fromStateName);
2957
+ }
2958
+ this.stateEntryTimes.delete(fromStateName);
2959
+ if (!fromState) return;
2960
+ const exitContext = { ...context, phase: "exit" };
2961
+ const exitActions = [
2962
+ fromState.onBeforeExit,
2963
+ fromState.onExit,
2964
+ fromState.onAfterExit
2965
+ ];
2966
+ for (const action of exitActions) {
2967
+ if (action) {
2968
+ await this.callAction(obj, action, ...args).catch(
2969
+ this.processError(obj, exitContext, fromState.onError, this.onError)
2970
+ );
2971
+ }
2972
+ }
2973
+ }
2974
+ /**
2975
+ * Execute enter actions for a state
2976
+ */
2977
+ async executeEnterActions(obj, toStateName, args, context) {
2978
+ const toState = this.states.get(toStateName);
2979
+ if (!toState) return;
2980
+ const enterContext = { ...context, phase: "enter" };
2981
+ const enterActions = [
2982
+ toState.onBeforeEnter,
2983
+ toState.onEnter,
2984
+ toState.onAfterEnter
2985
+ ];
2986
+ for (const action of enterActions) {
2987
+ if (action) {
2988
+ await this.callAction(obj, action, ...args).catch(
2989
+ this.processError(obj, enterContext, toState.onError, this.onError)
2990
+ );
2991
+ }
2992
+ }
2993
+ if (toState.invoke && toState.invoke.length > 0) {
2994
+ if (!this.stateEntryTimes.has(toStateName)) {
2995
+ this.stateEntryTimes.set(toStateName, Date.now());
2996
+ }
2997
+ const timers = [];
2998
+ for (const invocation of toState.invoke) {
2999
+ if (invocation.cond) {
3000
+ try {
3001
+ const shouldInvoke = invocation.cond(obj.adaptee);
3002
+ if (!shouldInvoke) continue;
3003
+ } catch (e) {
3004
+ this.logger.error(
3005
+ "Error in invoke condition",
3006
+ { state: toStateName },
3007
+ e
3008
+ );
3009
+ continue;
3010
+ }
3011
+ }
3012
+ const callback = async () => {
3013
+ const currentState = this.getCurrentState(obj);
3014
+ if (currentState?.split("|").includes(toStateName)) {
3015
+ try {
3016
+ if (invocation.action) {
3017
+ await this.callAction(obj, invocation.action);
3018
+ }
3019
+ this.raiseEvent(invocation.event, obj);
3020
+ this.scheduleProcessing();
3021
+ } catch (err) {
3022
+ this.logger.error(
3023
+ "Invocation error",
3024
+ { state: toStateName, event: invocation.event },
3025
+ err
3026
+ );
3027
+ }
3028
+ }
3029
+ };
3030
+ const timerId = this.setTimer(callback, invocation.delay);
3031
+ timers.push(timerId);
3032
+ }
3033
+ this.activeTimers.set(toStateName, timers);
3034
+ }
3035
+ }
3036
+ /**
3037
+ * Helper to set timer (native or scheduled)
3038
+ */
3039
+ setTimer(callback, delay) {
3040
+ const scheduler = this.scheduler;
3041
+ if (scheduler.isActive()) {
3042
+ return scheduler.schedule(delay, callback);
3043
+ }
3044
+ return setTimeout(callback, delay);
3045
+ }
3046
+ /**
3047
+ * Helper to clear timer
3048
+ */
3049
+ clearTimer(timerId) {
3050
+ const scheduler = this.scheduler;
3051
+ if (scheduler.isActive() && typeof timerId === "object" && timerId !== null && !("ref" in timerId)) {
3052
+ scheduler.cancel(timerId);
3053
+ } else {
3054
+ clearTimeout(timerId);
3055
+ }
3056
+ }
3057
+ /**
3058
+ * Manage state history for transitions
3059
+ */
3060
+ manageStateHistory(fromStateName, currentState, obj) {
3061
+ const fromState = this.states.get(fromStateName);
3062
+ if (fromState?.history) {
3063
+ if (fromState.history === "deep") {
3064
+ this.historyMap.set(fromState.name, currentState);
3065
+ } else if (fromState.history === "shallow" && fromState.regions) {
3066
+ this.historyMap.set(fromState.name, this.getCurrentState(obj) ?? "");
3067
+ }
3068
+ }
3069
+ const fromStateParent = this.findParentStateWithHistory(fromStateName);
3070
+ if (fromStateParent?.history) {
3071
+ this.historyMap.set(fromStateParent.name, currentState);
3072
+ }
3073
+ }
3074
+ findParentStateWithHistory(stateName) {
3075
+ let current = stateName;
3076
+ while (current.includes(".")) {
3077
+ current = current.split(".").slice(0, -1).join(".");
3078
+ const state = this.states.get(current);
3079
+ if (state?.history) return state;
3080
+ }
3081
+ return void 0;
3082
+ }
3083
+ updatePartialState(currentState, parentState, newSubstate) {
3084
+ if (!currentState) return newSubstate;
3085
+ const currentParts = currentState.split("|");
3086
+ const regionKey = parentState.split(".").slice(0, -1).join(".");
3087
+ let updatedParts = currentParts.map((part) => {
3088
+ const partRegionKey = this.getRegionKey(part);
3089
+ if (partRegionKey === regionKey) {
3090
+ return newSubstate;
3091
+ }
3092
+ if (partRegionKey.startsWith(`${regionKey}.`)) {
3093
+ return null;
3094
+ }
3095
+ return part;
3096
+ }).filter((m) => m !== null && m !== void 0);
3097
+ if (!updatedParts.some((part) => this.getRegionKey(part) === regionKey)) {
3098
+ const regionState = this.states.get(newSubstate);
3099
+ if (regionState?.regions) {
3100
+ return currentState;
3101
+ } else {
3102
+ updatedParts.push(newSubstate);
3103
+ }
3104
+ }
3105
+ updatedParts = updatedParts.filter((part) => part !== null);
3106
+ const newCompositeState = updatedParts.join("|");
3107
+ this.validateCompositeState(newCompositeState);
3108
+ return newCompositeState;
3109
+ }
3110
+ updateState(currentState, toState) {
3111
+ const currentStateMap = this.parseCompositeState(currentState);
3112
+ const toStateParts = toState.split("|");
3113
+ if (toStateParts.length === 1 && !toState.includes(".")) {
3114
+ currentStateMap.clear();
3115
+ currentStateMap.set(toState, toState);
3116
+ return toState;
3117
+ }
3118
+ for (const toStatePart of toStateParts) {
3119
+ const regionKey = this.getRegionKey(toStatePart);
3120
+ const stateConfig = this.states.get(toStatePart);
3121
+ this.removeConflictingStates(currentStateMap, regionKey);
3122
+ if (stateConfig?.regions) {
3123
+ this.addRegionStates(currentStateMap, stateConfig, toStatePart);
3124
+ } else {
3125
+ currentStateMap.set(regionKey, toStatePart);
3126
+ }
3127
+ }
3128
+ this.cleanupRootStates(currentStateMap);
3129
+ const newCompositeState = Array.from(currentStateMap.values()).join("|");
3130
+ this.validateCompositeState(newCompositeState);
3131
+ return newCompositeState;
3132
+ }
3133
+ /**
3134
+ * Remove conflicting states from the state map
3135
+ */
3136
+ removeConflictingStates(currentStateMap, regionKey) {
3137
+ const keysToDelete = [];
3138
+ for (const [key, value] of currentStateMap.entries()) {
3139
+ const existingRegionKey = this.getRegionKey(value);
3140
+ if (existingRegionKey === regionKey || existingRegionKey.startsWith(`${regionKey}.`)) {
3141
+ keysToDelete.push(key);
3142
+ }
3143
+ }
3144
+ for (const key of keysToDelete) {
3145
+ currentStateMap.delete(key);
3146
+ }
3147
+ }
3148
+ /**
3149
+ * Add region states to the state map
3150
+ */
3151
+ addRegionStates(currentStateMap, stateConfig, toStatePart) {
3152
+ const initialStatesForRegions = this.getInitialStatesForRegions(
3153
+ stateConfig.regions,
3154
+ toStatePart
3155
+ );
3156
+ const regionStates = initialStatesForRegions.split("|");
3157
+ for (const regionState of regionStates) {
3158
+ const regionKeyNested = this.getRegionKey(regionState);
3159
+ currentStateMap.set(regionKeyNested, regionState);
3160
+ }
3161
+ }
3162
+ /**
3163
+ * Clean up root states from the state map
3164
+ */
3165
+ cleanupRootStates(currentStateMap) {
3166
+ const keysToDelete = [];
3167
+ for (const [key, value] of currentStateMap.entries()) {
3168
+ if (!value.includes(".")) {
3169
+ keysToDelete.push(key);
3170
+ }
3171
+ }
3172
+ for (const key of keysToDelete) {
3173
+ currentStateMap.delete(key);
3174
+ }
3175
+ }
3176
+ parseCompositeState(compositeState) {
3177
+ const stateMap = /* @__PURE__ */ new Map();
3178
+ if (!compositeState) return stateMap;
3179
+ const stateParts = compositeState.split("|");
3180
+ for (let i = 0; i < stateParts.length; i++) {
3181
+ const statePart = stateParts[i];
3182
+ if (statePart === void 0) continue;
3183
+ const regionKey = this.getRegionKey(statePart);
3184
+ stateMap.set(regionKey, statePart);
3185
+ }
3186
+ return stateMap;
3187
+ }
3188
+ getRegionKey(statePath) {
3189
+ const lastDotIndex = statePath.lastIndexOf(".");
3190
+ return lastDotIndex === -1 ? statePath : statePath.substring(0, lastDotIndex);
3191
+ }
3192
+ resumeTimers() {
3193
+ for (const timers of this.activeTimers.values()) {
3194
+ for (const id of timers) {
3195
+ this.clearTimer(id);
3196
+ }
3197
+ }
3198
+ this.activeTimers.clear();
3199
+ const currentState = this.getCurrentState();
3200
+ if (!currentState) return;
3201
+ const activeStates = currentState.split("|");
3202
+ const activeStatesSet = new Set(activeStates);
3203
+ for (const stateName of this.stateEntryTimes.keys()) {
3204
+ if (!activeStatesSet.has(stateName)) {
3205
+ this.stateEntryTimes.delete(stateName);
3206
+ }
3207
+ }
3208
+ const now = Date.now();
3209
+ for (const stateName of activeStates) {
3210
+ const state = this.states.get(stateName);
3211
+ if (!state || !state.invoke || state.invoke.length === 0) continue;
3212
+ const entryTime = this.stateEntryTimes.get(stateName);
3213
+ const startTime = entryTime || now;
3214
+ if (!entryTime) {
3215
+ this.stateEntryTimes.set(stateName, startTime);
3216
+ }
3217
+ const elapsed = now - startTime;
3218
+ const timers = [];
3219
+ for (const invocation of state.invoke) {
3220
+ const remaining = Math.max(0, invocation.delay - elapsed);
3221
+ const callback = async () => {
3222
+ const current = this.getCurrentState(this.adaptee);
3223
+ if (current?.split("|").includes(stateName)) {
3224
+ try {
3225
+ if (invocation.cond && this.adaptee) {
3226
+ try {
3227
+ if (!invocation.cond(this.adaptee.adaptee)) return;
3228
+ } catch (_e) {
3229
+ return;
3230
+ }
3231
+ }
3232
+ if (invocation.action && this.adaptee) {
3233
+ await this.callAction(this.adaptee, invocation.action);
3234
+ }
3235
+ await this.fireEvent(invocation.event).catch((err) => {
3236
+ this.logger.error(
3237
+ "Resumed timer transition error",
3238
+ { state: stateName, event: invocation.event },
3239
+ err
3240
+ );
3241
+ });
3242
+ } catch (_err) {
3243
+ }
3244
+ }
3245
+ };
3246
+ const timerId = this.setTimer(callback, remaining);
3247
+ timers.push(timerId);
3248
+ }
3249
+ this.activeTimers.set(stateName, timers);
3250
+ }
3251
+ }
3252
+ // Методы сериализации
3253
+ toJSON() {
3254
+ const serializedStates = {};
3255
+ const serializedEvents = {};
3256
+ for (const [name, state] of this.states) {
3257
+ serializedStates[name] = this.serializeState(state);
3258
+ }
3259
+ for (const [name, event] of this.events) {
3260
+ serializedEvents[name] = this.serializeEvent(event);
3261
+ }
3262
+ const config = {
3263
+ initialState: this.initialState,
3264
+ stateAttribute: this.stateAttribute,
3265
+ states: serializedStates,
3266
+ events: serializedEvents,
3267
+ onError: this.serializeAction(this.onError, "config_onError")
3268
+ };
3269
+ return JSON.stringify({
3270
+ config,
3271
+ currentState: this.getCurrentState(),
3272
+ historyMap: Array.from(this.historyMap.entries()),
3273
+ stateEntryTimes: Array.from(this.stateEntryTimes.entries())
3274
+ });
3275
+ }
3276
+ /**
3277
+ * Securely serializes the StateMachine to JSON (Async).
3278
+ * Generates cryptographic hashes for all functions.
3279
+ */
3280
+ async toSecureJSON() {
3281
+ const serializedStates = {};
3282
+ const serializedEvents = {};
3283
+ for (const [name, state] of this.states) {
3284
+ serializedStates[name] = await this.serializeStateAsync(state);
3285
+ }
3286
+ for (const [name, event] of this.events) {
3287
+ serializedEvents[name] = await this.serializeEventAsync(event);
3288
+ }
3289
+ const config = {
3290
+ initialState: this.initialState,
3291
+ stateAttribute: this.stateAttribute,
3292
+ states: serializedStates,
3293
+ events: serializedEvents,
3294
+ onError: await this.serializeActionAsync(this.onError, "config_onError")
3295
+ };
3296
+ return JSON.stringify({
3297
+ config,
3298
+ currentState: this.getCurrentState(),
3299
+ historyMap: Array.from(this.historyMap.entries()),
3300
+ stateEntryTimes: Array.from(this.stateEntryTimes.entries())
3301
+ });
3302
+ }
3303
+ /**
3304
+ * Serialize state with optimized performance (Async)
3305
+ */
3306
+ async serializeStateAsync(state) {
3307
+ const { name: _name, ...stateWithoutName } = state;
3308
+ const serializedState = {
3309
+ ...stateWithoutName,
3310
+ onBeforeEnter: await this.serializeActionAsync(
3311
+ state.onBeforeEnter,
3312
+ "onBeforeEnter"
3313
+ ),
3314
+ onEnter: await this.serializeActionAsync(state.onEnter, "onEnter"),
3315
+ onAfterEnter: await this.serializeActionAsync(
3316
+ state.onAfterEnter,
3317
+ "onAfterEnter"
3318
+ ),
3319
+ onBeforeExit: await this.serializeActionAsync(
3320
+ state.onBeforeExit,
3321
+ "onBeforeExit"
3322
+ ),
3323
+ onExit: await this.serializeActionAsync(state.onExit, "onExit"),
3324
+ onAfterExit: await this.serializeActionAsync(
3325
+ state.onAfterExit,
3326
+ "onAfterExit"
3327
+ ),
3328
+ onError: await this.serializeActionAsync(state.onError, "state_onError")
3329
+ };
3330
+ if (state.invoke) {
3331
+ serializedState.invoke = await Promise.all(
3332
+ state.invoke.map(async (inv) => ({
3333
+ ...inv,
3334
+ cond: inv.cond != null ? await safeFunctionSerializer.serializeActionAsync(
3335
+ inv.cond,
3336
+ "invoke_cond"
3337
+ ) : void 0,
3338
+ action: inv.action != null ? await safeFunctionSerializer.serializeActionAsync(
3339
+ inv.action,
3340
+ "invoke_action"
3341
+ ) : void 0
3342
+ }))
3343
+ );
3344
+ }
3345
+ return serializedState;
3346
+ }
3347
+ /**
3348
+ * Serialize state with optimized performance
3349
+ */
3350
+ serializeState(state) {
3351
+ const { name: _name, ...stateWithoutName } = state;
3352
+ const serializedState = {
3353
+ ...stateWithoutName,
3354
+ onBeforeEnter: this.serializeAction(state.onBeforeEnter, "onBeforeEnter"),
3355
+ onEnter: this.serializeAction(state.onEnter, "onEnter"),
3356
+ onAfterEnter: this.serializeAction(state.onAfterEnter, "onAfterEnter"),
3357
+ onBeforeExit: this.serializeAction(state.onBeforeExit, "onBeforeExit"),
3358
+ onExit: this.serializeAction(state.onExit, "onExit"),
3359
+ onAfterExit: this.serializeAction(state.onAfterExit, "onAfterExit"),
3360
+ onError: this.serializeAction(state.onError, "state_onError")
3361
+ };
3362
+ if (state.invoke) {
3363
+ serializedState.invoke = state.invoke.map((inv) => ({
3364
+ ...inv,
3365
+ cond: inv.cond != null ? safeFunctionSerializer.serializeAction(inv.cond, "invoke_cond") : void 0,
3366
+ action: inv.action != null ? safeFunctionSerializer.serializeAction(inv.action, "invoke_action") : void 0
3367
+ }));
3368
+ }
3369
+ return serializedState;
3370
+ }
3371
+ /**
3372
+ * Serialize event with optimized performance (Async)
3373
+ */
3374
+ async serializeEventAsync(event) {
3375
+ const { name: _name, ...eventWithoutName } = event;
3376
+ return {
3377
+ ...eventWithoutName,
3378
+ onBefore: await this.serializeActionAsync(
3379
+ event.onBefore,
3380
+ "event_onBefore"
3381
+ ),
3382
+ onAfter: await this.serializeActionAsync(event.onAfter, "event_onAfter"),
3383
+ onSuccess: await this.serializeActionAsync(
3384
+ event.onSuccess,
3385
+ "event_onSuccess"
3386
+ ),
3387
+ onError: await this.serializeActionAsync(event.onError, "event_onError"),
3388
+ transitions: await Promise.all(
3389
+ event.transitions.map((t) => this.serializeTransitionAsync(t))
3390
+ )
3391
+ };
3392
+ }
3393
+ /**
3394
+ * Serialize transition (Async)
3395
+ */
3396
+ async serializeTransitionAsync(transition) {
3397
+ return {
3398
+ ...transition,
3399
+ guard: await this.serializeActionAsync(
3400
+ transition.guard,
3401
+ "transition_guard"
3402
+ ),
3403
+ onTransition: await this.serializeActionAsync(
3404
+ transition.onTransition,
3405
+ "transition_onTransition"
3406
+ ),
3407
+ onError: await this.serializeActionAsync(
3408
+ transition.onError,
3409
+ "transition_onError"
3410
+ )
3411
+ };
3412
+ }
3413
+ /**
3414
+ * Serialize event with optimized performance
3415
+ */
3416
+ serializeEvent(event) {
3417
+ const { name: _name, ...eventWithoutName } = event;
3418
+ return {
3419
+ ...eventWithoutName,
3420
+ onBefore: this.serializeAction(event.onBefore, "event_onBefore"),
3421
+ onAfter: this.serializeAction(event.onAfter, "event_onAfter"),
3422
+ onSuccess: this.serializeAction(event.onSuccess, "event_onSuccess"),
3423
+ onError: this.serializeAction(event.onError, "event_onError"),
3424
+ transitions: event.transitions.map((transition) => ({
3425
+ ...transition,
3426
+ guard: this.serializeAction(transition.guard, "transition_guard"),
3427
+ onTransition: this.serializeAction(
3428
+ transition.onTransition,
3429
+ "transition_onTransition"
3430
+ ),
3431
+ onError: this.serializeAction(transition.onError, "transition_onError")
3432
+ }))
3433
+ };
3434
+ }
3435
+ async serializeActionAsync(action, name) {
3436
+ if (!action) return void 0;
3437
+ return safeFunctionSerializer.serializeActionAsync(action, name);
3438
+ }
3439
+ /**
3440
+ * Optimized action serialization using safe serializer
3441
+ */
3442
+ serializeAction(action, functionName) {
3443
+ if (typeof action === "function") {
3444
+ return safeFunctionSerializer.serializeAction(action, functionName);
3445
+ }
3446
+ return action;
3447
+ }
3448
+ };
3449
+
3450
+ // src/lite.ts
3451
+ function createMachine(config, owner, options) {
3452
+ if (owner && "get" in owner) {
3453
+ return new StateMachine(config, owner, options);
3454
+ } else if (owner) {
3455
+ const adapter = new MemoryAdapter(owner);
3456
+ return new StateMachine(config, adapter, options);
3457
+ } else {
3458
+ return new StateMachine(config, void 0, options);
3459
+ }
3460
+ }
3461
+
3462
+ // src/config_validator.ts
3463
+ var DEFAULT_VALIDATION_CONFIG = {
3464
+ strictMode: false,
3465
+ allowEmptyStates: false,
3466
+ allowEmptyEvents: false,
3467
+ maxStateDepth: 10,
3468
+ maxStatesCount: 1e3,
3469
+ maxEventsCount: 1e3,
3470
+ requireInitialState: true,
3471
+ validateTransitionPaths: true,
3472
+ validateActionReferences: false
3473
+ // Disabled by default as actions might be dynamic
3474
+ };
3475
+ var ConfigValidator = class {
3476
+ config;
3477
+ errors = [];
3478
+ warnings = [];
3479
+ constructor(config = {}) {
3480
+ this.config = { ...DEFAULT_VALIDATION_CONFIG, ...config };
3481
+ }
3482
+ /**
3483
+ * Validates a complete StateMachine configuration
3484
+ */
3485
+ validate(smConfig) {
3486
+ this.errors = [];
3487
+ this.warnings = [];
3488
+ try {
3489
+ this.validateBasicStructure(smConfig);
3490
+ this.validateStates(smConfig.states, "states");
3491
+ this.validateEvents(smConfig.events, smConfig.states, "events");
3492
+ this.validateInitialState(smConfig);
3493
+ this.validateCrossReferences(smConfig);
3494
+ this.validatePerformanceConstraints(smConfig);
3495
+ this.runCustomRules(smConfig);
3496
+ } catch (error) {
3497
+ const msg = error instanceof Error ? error.message : String(error);
3498
+ this.addError(
3499
+ "VALIDATION_FAILED",
3500
+ `Validation process failed: ${msg}`,
3501
+ "validation"
3502
+ );
3503
+ }
3504
+ const result = {
3505
+ isValid: this.errors.length === 0,
3506
+ errors: this.errors,
3507
+ warnings: this.warnings
3508
+ };
3509
+ if (!result.isValid) {
3510
+ stateMachineLogger.error("StateMachine configuration validation failed", {
3511
+ errorCount: this.errors.length,
3512
+ warningCount: this.warnings.length,
3513
+ configName: smConfig.name
3514
+ });
3515
+ } else if (this.warnings.length > 0) {
3516
+ stateMachineLogger.warn("StateMachine configuration has warnings", {
3517
+ warningCount: this.warnings.length,
3518
+ configName: smConfig.name
3519
+ });
3520
+ }
3521
+ return result;
3522
+ }
3523
+ /**
3524
+ * Runs custom validation rules
3525
+ */
3526
+ runCustomRules(smConfig) {
3527
+ if (!this.config.customRules || this.config.customRules.length === 0) return;
3528
+ const context = {
3529
+ addError: (code, msg, path, sugg) => this.addError(code, msg, path, void 0, sugg),
3530
+ addWarning: (code, msg, path, sugg) => this.addWarning(code, msg, path, void 0, sugg)
3531
+ };
3532
+ for (const rule of this.config.customRules) {
3533
+ try {
3534
+ rule.validate(smConfig, context);
3535
+ } catch (e) {
3536
+ this.addError(
3537
+ "CUSTOM_RULE_ERROR",
3538
+ `Rule ${rule.id} failed: ${e instanceof Error ? e.message : String(e)}`,
3539
+ "root"
3540
+ );
3541
+ }
3542
+ }
3543
+ }
3544
+ validateBasicStructure(smConfig) {
3545
+ if (!smConfig.name || typeof smConfig.name !== "string") {
3546
+ this.addError(
3547
+ "MISSING_NAME",
3548
+ "StateMachine must have a valid name",
3549
+ "name"
3550
+ );
3551
+ }
3552
+ if (!smConfig.stateAttribute || typeof smConfig.stateAttribute !== "string") {
3553
+ this.addError(
3554
+ "MISSING_STATE_ATTRIBUTE",
3555
+ "StateMachine must have a valid stateAttribute",
3556
+ "stateAttribute"
3557
+ );
3558
+ }
3559
+ if (!smConfig.states || typeof smConfig.states !== "object") {
3560
+ this.addError(
3561
+ "MISSING_STATES",
3562
+ "StateMachine must have states configuration",
3563
+ "states"
3564
+ );
3565
+ return;
3566
+ }
3567
+ if (!smConfig.events || typeof smConfig.events !== "object") {
3568
+ this.addError(
3569
+ "MISSING_EVENTS",
3570
+ "StateMachine must have events configuration",
3571
+ "events"
3572
+ );
3573
+ return;
3574
+ }
3575
+ if (!this.config.allowEmptyStates && Object.keys(smConfig.states).length === 0) {
3576
+ this.addError(
3577
+ "EMPTY_STATES",
3578
+ "StateMachine cannot have empty states collection",
3579
+ "states"
3580
+ );
3581
+ }
3582
+ if (!this.config.allowEmptyEvents && Object.keys(smConfig.events).length === 0) {
3583
+ this.addError(
3584
+ "EMPTY_EVENTS",
3585
+ "StateMachine cannot have empty events collection",
3586
+ "events"
3587
+ );
3588
+ }
3589
+ }
3590
+ validateStates(states, basePath, depth = 0) {
3591
+ if (depth > this.config.maxStateDepth) {
3592
+ this.addError(
3593
+ "MAX_DEPTH_EXCEEDED",
3594
+ `State nesting depth exceeds maximum of ${this.config.maxStateDepth}`,
3595
+ basePath
3596
+ );
3597
+ return;
3598
+ }
3599
+ for (const [stateName, stateConfig] of Object.entries(states)) {
3600
+ const statePath = `${basePath}.${stateName}`;
3601
+ this.validateState(stateConfig, statePath, depth);
3602
+ }
3603
+ }
3604
+ validateState(state, path, depth) {
3605
+ if (!path.split(".").pop()) {
3606
+ this.addError("INVALID_STATE_NAME", "State name cannot be empty", path);
3607
+ }
3608
+ if (state.display && typeof state.display !== "string") {
3609
+ this.addWarning(
3610
+ "INVALID_DISPLAY",
3611
+ "State display should be a string",
3612
+ `${path}.display`
3613
+ );
3614
+ }
3615
+ if (state.regions) {
3616
+ if (typeof state.regions !== "object") {
3617
+ this.addError(
3618
+ "INVALID_REGIONS",
3619
+ "State regions must be an object",
3620
+ `${path}.regions`
3621
+ );
3622
+ } else {
3623
+ for (const [regionName, regionStates] of Object.entries(
3624
+ state.regions
3625
+ )) {
3626
+ if (!regionName) {
3627
+ this.addError(
3628
+ "EMPTY_REGION_NAME",
3629
+ "Region name cannot be empty",
3630
+ `${path}.regions`
3631
+ );
3632
+ continue;
3633
+ }
3634
+ this.validateStates(
3635
+ regionStates,
3636
+ `${path}.regions.${regionName}`,
3637
+ depth + 1
3638
+ );
3639
+ }
3640
+ }
3641
+ }
3642
+ if (state.history && !["deep", "shallow"].includes(state.history)) {
3643
+ this.addError(
3644
+ "INVALID_HISTORY",
3645
+ 'State history must be "deep" or "shallow"',
3646
+ `${path}.history`
3647
+ );
3648
+ }
3649
+ if (state.regions && state.initial) {
3650
+ const hasInitialState = Object.values(state.regions).some(
3651
+ (regionStates) => Object.keys(regionStates).includes(state.initial)
3652
+ );
3653
+ if (!hasInitialState) {
3654
+ this.addError(
3655
+ "INVALID_INITIAL_STATE",
3656
+ `Initial state "${state.initial}" not found in any region`,
3657
+ `${path}.initial`
3658
+ );
3659
+ }
3660
+ }
3661
+ if (state.invoke) {
3662
+ if (!Array.isArray(state.invoke)) {
3663
+ this.addError(
3664
+ "INVALID_INVOKE",
3665
+ 'State "invoke" must be an array of StateInvocation',
3666
+ `${path}.invoke`
3667
+ );
3668
+ } else {
3669
+ state.invoke.forEach((inv, index) => {
3670
+ const invPath = `${path}.invoke[${index}]`;
3671
+ if (typeof inv.delay !== "number" || inv.delay < 0) {
3672
+ this.addError(
3673
+ "INVALID_DELAY",
3674
+ "Delay must be a positive number",
3675
+ `${invPath}.delay`
3676
+ );
3677
+ }
3678
+ if (!inv.event || typeof inv.event !== "string") {
3679
+ this.addError(
3680
+ "INVALID_EVENT_NAME",
3681
+ "Event name must be a string",
3682
+ `${invPath}.event`
3683
+ );
3684
+ }
3685
+ if (inv.cond && typeof inv.cond !== "function") {
3686
+ this.addError(
3687
+ "INVALID_COND",
3688
+ "Condition must be a function",
3689
+ `${invPath}.cond`
3690
+ );
3691
+ }
3692
+ });
3693
+ }
3694
+ }
3695
+ }
3696
+ validateEvents(events, states, basePath) {
3697
+ for (const [eventName, eventConfig] of Object.entries(events)) {
3698
+ const eventPath = `${basePath}.${eventName}`;
3699
+ this.validateEvent(eventConfig, eventPath, states);
3700
+ }
3701
+ }
3702
+ validateEvent(event, path, states) {
3703
+ if (!event.transitions || !Array.isArray(event.transitions)) {
3704
+ this.addError(
3705
+ "MISSING_TRANSITIONS",
3706
+ "Event must have transitions array",
3707
+ `${path}.transitions`
3708
+ );
3709
+ return;
3710
+ }
3711
+ if (event.transitions.length === 0) {
3712
+ this.addWarning(
3713
+ "EMPTY_TRANSITIONS",
3714
+ "Event has no transitions",
3715
+ `${path}.transitions`
3716
+ );
3717
+ }
3718
+ event.transitions.forEach((transition, index) => {
3719
+ this.validateTransition(
3720
+ transition,
3721
+ `${path}.transitions[${index}]`,
3722
+ states
3723
+ );
3724
+ });
3725
+ const priorities = event.transitions.map((t) => t.priority).filter((p) => p !== void 0);
3726
+ if (priorities.length > 0 && priorities.length !== event.transitions.length) {
3727
+ this.addWarning(
3728
+ "MIXED_PRIORITIES",
3729
+ "Some transitions have priority while others do not",
3730
+ `${path}.transitions`
3731
+ );
3732
+ }
3733
+ }
3734
+ validateTransition(transition, path, states) {
3735
+ if (!transition.from || typeof transition.from !== "string") {
3736
+ this.addError(
3737
+ "INVALID_FROM_STATE",
3738
+ "Transition must have a valid from state",
3739
+ `${path}.from`
3740
+ );
3741
+ } else if (this.config.validateTransitionPaths) {
3742
+ this.validateStatePath(transition.from, states, `${path}.from`);
3743
+ }
3744
+ if (!transition.to || typeof transition.to !== "string") {
3745
+ this.addError(
3746
+ "INVALID_TO_STATE",
3747
+ "Transition must have a valid to state",
3748
+ `${path}.to`
3749
+ );
3750
+ } else if (this.config.validateTransitionPaths) {
3751
+ this.validateStatePath(transition.to, states, `${path}.to`);
3752
+ }
3753
+ if (transition.priority !== void 0 && typeof transition.priority !== "number") {
3754
+ this.addError(
3755
+ "INVALID_PRIORITY",
3756
+ "Transition priority must be a number",
3757
+ `${path}.priority`
3758
+ );
3759
+ }
3760
+ if (transition.from === transition.to) {
3761
+ this.addWarning(
3762
+ "SELF_TRANSITION",
3763
+ "Transition from and to states are the same",
3764
+ path
3765
+ );
3766
+ }
3767
+ }
3768
+ validateStatePath(statePath, states, path) {
3769
+ const stateParts = statePath.split("|");
3770
+ for (const part of stateParts) {
3771
+ if (!this.isValidStatePath(part, states)) {
3772
+ this.addError(
3773
+ "INVALID_STATE_PATH",
3774
+ `State path "${part}" does not exist`,
3775
+ path
3776
+ );
3777
+ }
3778
+ }
3779
+ }
3780
+ isValidStatePath(statePath, states) {
3781
+ const parts = statePath.split(".");
3782
+ let currentStates = states;
3783
+ for (let i = 0; i < parts.length; i++) {
3784
+ const part = parts[i];
3785
+ if (part === void 0) return false;
3786
+ if (i === 0) {
3787
+ if (!currentStates[part]) {
3788
+ return false;
3789
+ }
3790
+ const state = currentStates[part];
3791
+ if (parts.length === 1) {
3792
+ return true;
3793
+ }
3794
+ if (!state.regions) {
3795
+ return false;
3796
+ }
3797
+ } else if (i === 1) {
3798
+ const rootStatePart = parts[0];
3799
+ if (rootStatePart === void 0) return false;
3800
+ const rootState = currentStates[rootStatePart];
3801
+ if (!rootState || !rootState.regions || !rootState.regions[part]) {
3802
+ return false;
3803
+ }
3804
+ currentStates = rootState.regions[part];
3805
+ } else {
3806
+ if (!currentStates[part]) {
3807
+ return false;
3808
+ }
3809
+ const state = currentStates[part];
3810
+ if (i === parts.length - 1) {
3811
+ return true;
3812
+ }
3813
+ if (!state.regions) {
3814
+ return false;
3815
+ }
3816
+ }
3817
+ }
3818
+ return true;
3819
+ }
3820
+ validateInitialState(smConfig) {
3821
+ if (this.config.requireInitialState) {
3822
+ if (!smConfig.initialState) {
3823
+ this.addError(
3824
+ "MISSING_INITIAL_STATE",
3825
+ "StateMachine must have an initial state",
3826
+ "initialState"
3827
+ );
3828
+ return;
3829
+ }
3830
+ if (!smConfig.states[smConfig.initialState]) {
3831
+ this.addError(
3832
+ "INVALID_INITIAL_STATE",
3833
+ `Initial state "${smConfig.initialState}" does not exist`,
3834
+ "initialState",
3835
+ void 0,
3836
+ `Check the 'states' object. Ensure that '${smConfig.initialState}' is defined as a root state.`
3837
+ );
3838
+ }
3839
+ }
3840
+ }
3841
+ validateCrossReferences(smConfig) {
3842
+ const allStateNames = /* @__PURE__ */ new Set();
3843
+ this.collectStateNames(smConfig.states, "", allStateNames);
3844
+ const reachableStates = /* @__PURE__ */ new Set();
3845
+ if (smConfig.initialState) {
3846
+ reachableStates.add(smConfig.initialState);
3847
+ }
3848
+ for (const event of Object.values(smConfig.events)) {
3849
+ for (const transition of event.transitions) {
3850
+ if (transition.to) {
3851
+ const toParts = transition.to.split("|");
3852
+ toParts.forEach((part) => reachableStates.add(part));
3853
+ }
3854
+ }
3855
+ }
3856
+ for (const stateName of allStateNames) {
3857
+ if (!reachableStates.has(stateName)) {
3858
+ this.addWarning(
3859
+ "UNREACHABLE_STATE",
3860
+ `State "${stateName}" is not reachable`,
3861
+ `states.${stateName}`
3862
+ );
3863
+ }
3864
+ }
3865
+ for (const [eventName, event] of Object.entries(smConfig.events)) {
3866
+ const hasValidTransitions = event.transitions.some(
3867
+ (t) => allStateNames.has(t.from) && allStateNames.has(t.to)
3868
+ );
3869
+ if (!hasValidTransitions) {
3870
+ this.addWarning(
3871
+ "UNUSED_EVENT",
3872
+ `Event "${eventName}" has no valid transitions`,
3873
+ `events.${eventName}`
3874
+ );
3875
+ }
3876
+ }
3877
+ }
3878
+ collectStateNames(states, prefix, collector) {
3879
+ for (const [stateName, state] of Object.entries(states)) {
3880
+ const fullName = prefix ? `${prefix}.${stateName}` : stateName;
3881
+ collector.add(fullName);
3882
+ if (state.regions) {
3883
+ for (const [regionName, regionStates] of Object.entries(
3884
+ state.regions
3885
+ )) {
3886
+ this.collectStateNames(
3887
+ regionStates,
3888
+ `${fullName}.${regionName}`,
3889
+ collector
3890
+ );
3891
+ }
3892
+ }
3893
+ }
3894
+ }
3895
+ validatePerformanceConstraints(smConfig) {
3896
+ const stateCount = this.countStates(smConfig.states);
3897
+ const eventCount = Object.keys(smConfig.events).length;
3898
+ if (stateCount > this.config.maxStatesCount) {
3899
+ this.addWarning(
3900
+ "TOO_MANY_STATES",
3901
+ `State count (${stateCount}) exceeds recommended maximum (${this.config.maxStatesCount})`,
3902
+ "states"
3903
+ );
3904
+ }
3905
+ if (eventCount > this.config.maxEventsCount) {
3906
+ this.addWarning(
3907
+ "TOO_MANY_EVENTS",
3908
+ `Event count (${eventCount}) exceeds recommended maximum (${this.config.maxEventsCount})`,
3909
+ "events"
3910
+ );
3911
+ }
3912
+ let totalTransitions = 0;
3913
+ for (const event of Object.values(smConfig.events)) {
3914
+ totalTransitions += event.transitions.length;
3915
+ }
3916
+ if (totalTransitions > stateCount * 3) {
3917
+ this.addWarning(
3918
+ "COMPLEX_TRANSITIONS",
3919
+ "High transition-to-state ratio may impact performance",
3920
+ "events"
3921
+ );
3922
+ }
3923
+ }
3924
+ countStates(states) {
3925
+ let count = Object.keys(states).length;
3926
+ for (const state of Object.values(states)) {
3927
+ if (state.regions) {
3928
+ for (const regionStates of Object.values(state.regions)) {
3929
+ count += this.countStates(regionStates);
3930
+ }
3931
+ }
3932
+ }
3933
+ return count;
3934
+ }
3935
+ addError(code, message, path, details, suggestion) {
3936
+ this.errors.push({
3937
+ code,
3938
+ message,
3939
+ severity: "error",
3940
+ path,
3941
+ ...details !== void 0 ? { details } : {},
3942
+ ...suggestion !== void 0 ? { suggestion } : {}
3943
+ });
3944
+ }
3945
+ addWarning(code, message, path, details, suggestion) {
3946
+ this.warnings.push({
3947
+ code,
3948
+ message,
3949
+ severity: "warning",
3950
+ path,
3951
+ ...details !== void 0 ? { details } : {},
3952
+ ...suggestion !== void 0 ? { suggestion } : {}
3953
+ });
3954
+ }
3955
+ };
3956
+ function validateConfig(config, validationConfig) {
3957
+ const validator = new ConfigValidator(validationConfig);
3958
+ return validator.validate(config);
3959
+ }
3960
+ function validateConfigStrict(config) {
3961
+ return validateConfig(config, { strictMode: true });
3962
+ }
3963
+ function isValidConfig(config) {
3964
+ return validateConfig(config).isValid;
3965
+ }
3966
+ // Annotate the CommonJS export names for ESM import in node:
3967
+ 0 && (module.exports = {
3968
+ EnhancedErrorHandler,
3969
+ EnhancedStateMachineError,
3970
+ ErrorAnalytics,
3971
+ ErrorCategory,
3972
+ ErrorSeverity,
3973
+ FallbackStateRecoveryStrategy,
3974
+ LocalStorageAdapter,
3975
+ MemoryAdapter,
3976
+ RetryRecoveryStrategy,
3977
+ ServerAdapter,
3978
+ SessionStorageAdapter,
3979
+ StateMachine,
3980
+ StateMachineError,
3981
+ createEnhancedError,
3982
+ createMachine,
3983
+ isAdapter,
3984
+ isRecoverableError,
3985
+ isValidConfig,
3986
+ validateConfig,
3987
+ validateConfigStrict
3988
+ });
3989
+ //# sourceMappingURL=index.cjs.map