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