@context-action/core 0.4.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +379 -161
- package/dist/index.cjs +671 -286
- package/dist/index.d.cts +82 -21
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.ts +82 -21
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +665 -286
- package/dist/index.js.map +1 -1
- package/package.json +12 -12
package/dist/index.js
CHANGED
|
@@ -1,31 +1,23 @@
|
|
|
1
|
-
//#region rolldown:runtime
|
|
2
|
-
var __create = Object.create;
|
|
3
|
-
var __defProp = Object.defineProperty;
|
|
4
|
-
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
-
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
-
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
-
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
-
var __commonJS = (cb, mod) => function() {
|
|
9
|
-
return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
|
|
10
|
-
};
|
|
11
|
-
var __copyProps = (to, from, except, desc) => {
|
|
12
|
-
if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
|
|
13
|
-
key = keys[i];
|
|
14
|
-
if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
|
|
15
|
-
get: ((k) => from[k]).bind(null, key),
|
|
16
|
-
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
17
|
-
});
|
|
18
|
-
}
|
|
19
|
-
return to;
|
|
20
|
-
};
|
|
21
|
-
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
|
|
22
|
-
value: mod,
|
|
23
|
-
enumerable: true
|
|
24
|
-
}) : target, mod));
|
|
25
|
-
|
|
26
|
-
//#endregion
|
|
27
1
|
//#region src/execution-modes.ts
|
|
28
2
|
/**
|
|
3
|
+
* Create standardized error handling for handlers
|
|
4
|
+
*
|
|
5
|
+
* @param error - The error that occurred
|
|
6
|
+
* @param registration - The handler registration that failed
|
|
7
|
+
* @returns Standardized HandlerError object
|
|
8
|
+
*
|
|
9
|
+
* @internal
|
|
10
|
+
*/
|
|
11
|
+
function handleExecutionError(error, registration) {
|
|
12
|
+
const errorObj = error instanceof Error ? error : new Error(String(error));
|
|
13
|
+
return {
|
|
14
|
+
handlerId: registration.id,
|
|
15
|
+
error: errorObj,
|
|
16
|
+
timestamp: Date.now(),
|
|
17
|
+
severity: registration.config.blocking ? "blocking" : "non-blocking"
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
29
21
|
* Execute handlers in sequential mode (one after another)
|
|
30
22
|
*
|
|
31
23
|
* Executes action handlers one at a time in priority order (highest first).
|
|
@@ -48,6 +40,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
48
40
|
async function executeSequential(context, createController) {
|
|
49
41
|
let i = 0;
|
|
50
42
|
const nonBlockingPromises = [];
|
|
43
|
+
const errors = [];
|
|
51
44
|
while (i < context.handlers.length) {
|
|
52
45
|
if (context.aborted || context.terminated) break;
|
|
53
46
|
const registration = context.handlers[i];
|
|
@@ -56,22 +49,24 @@ async function executeSequential(context, createController) {
|
|
|
56
49
|
try {
|
|
57
50
|
if (context.aborted) break;
|
|
58
51
|
const result = registration.handler(context.payload, controller);
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
const handlerResult = await result;
|
|
62
|
-
/** Collect result if handler returned something and wasn't terminated */
|
|
52
|
+
if (registration.config.blocking) {
|
|
53
|
+
const handlerResult = result instanceof Promise ? await result : result;
|
|
63
54
|
if (handlerResult !== void 0 && !context.terminated) context.results.push(handlerResult);
|
|
64
|
-
} else if (result
|
|
65
|
-
|
|
66
|
-
if (result instanceof Promise) {
|
|
67
|
-
const promiseWithHandling = result.then((asyncResult) => {
|
|
55
|
+
} else if (result instanceof Promise) {
|
|
56
|
+
const promiseWithErrorHandling = result.then((asyncResult) => {
|
|
68
57
|
if (asyncResult !== void 0 && !context.terminated) context.results.push(asyncResult);
|
|
69
58
|
return asyncResult;
|
|
70
59
|
}).catch((error) => {
|
|
71
|
-
|
|
60
|
+
const handlerError = handleExecutionError(error, registration);
|
|
61
|
+
errors.push({
|
|
62
|
+
handlerId: handlerError.handlerId,
|
|
63
|
+
error: handlerError.error,
|
|
64
|
+
timestamp: handlerError.timestamp
|
|
65
|
+
});
|
|
66
|
+
return void 0;
|
|
72
67
|
});
|
|
73
|
-
nonBlockingPromises.push(
|
|
74
|
-
} else context.results.push(result);
|
|
68
|
+
nonBlockingPromises.push(promiseWithErrorHandling);
|
|
69
|
+
} else if (result !== void 0 && !context.terminated) context.results.push(result);
|
|
75
70
|
/** Check if pipeline was terminated by controller.return() */
|
|
76
71
|
if (context.terminated) break;
|
|
77
72
|
/** Handle jump to priority AFTER handler execution */
|
|
@@ -87,11 +82,20 @@ async function executeSequential(context, createController) {
|
|
|
87
82
|
}
|
|
88
83
|
} else i++;
|
|
89
84
|
} catch (error) {
|
|
90
|
-
|
|
91
|
-
throw error;
|
|
85
|
+
const handlerError = handleExecutionError(error, registration);
|
|
86
|
+
throw handlerError.error;
|
|
92
87
|
}
|
|
93
88
|
}
|
|
94
|
-
if (nonBlockingPromises.length > 0) await Promise.
|
|
89
|
+
if (nonBlockingPromises.length > 0) await Promise.allSettled(nonBlockingPromises);
|
|
90
|
+
if (errors.length > 0) {
|
|
91
|
+
const handlerErrors = errors.map((err) => ({
|
|
92
|
+
handlerId: err.handlerId,
|
|
93
|
+
error: err.error,
|
|
94
|
+
timestamp: err.timestamp,
|
|
95
|
+
severity: "non-blocking"
|
|
96
|
+
}));
|
|
97
|
+
context.collectedErrors = handlerErrors;
|
|
98
|
+
}
|
|
95
99
|
}
|
|
96
100
|
/**
|
|
97
101
|
* Execute handlers in parallel mode (all at once)
|
|
@@ -134,11 +138,12 @@ async function executeParallel(context, createController) {
|
|
|
134
138
|
terminated: context.terminated
|
|
135
139
|
};
|
|
136
140
|
} catch (error) {
|
|
137
|
-
|
|
141
|
+
const handlerError = handleExecutionError(error, registration);
|
|
142
|
+
if (handlerError.severity === "blocking") throw handlerError.error;
|
|
138
143
|
return {
|
|
139
144
|
success: false,
|
|
140
145
|
handlerId: registration.id,
|
|
141
|
-
error
|
|
146
|
+
error: handlerError.error
|
|
142
147
|
};
|
|
143
148
|
}
|
|
144
149
|
});
|
|
@@ -206,10 +211,11 @@ async function executeRace(context, createController) {
|
|
|
206
211
|
terminated: context.terminated
|
|
207
212
|
};
|
|
208
213
|
} catch (error) {
|
|
214
|
+
const handlerError = handleExecutionError(error, registration);
|
|
209
215
|
return {
|
|
210
216
|
success: false,
|
|
211
217
|
handlerId: registration.id,
|
|
212
|
-
error,
|
|
218
|
+
error: handlerError.error,
|
|
213
219
|
registration
|
|
214
220
|
};
|
|
215
221
|
}
|
|
@@ -227,67 +233,8 @@ async function executeRace(context, createController) {
|
|
|
227
233
|
}
|
|
228
234
|
}
|
|
229
235
|
|
|
230
|
-
//#endregion
|
|
231
|
-
//#region ../../node_modules/.pnpm/@oxc-project+runtime@0.82.2/node_modules/@oxc-project/runtime/src/helpers/typeof.js
|
|
232
|
-
var require_typeof = /* @__PURE__ */ __commonJS({ "../../node_modules/.pnpm/@oxc-project+runtime@0.82.2/node_modules/@oxc-project/runtime/src/helpers/typeof.js": ((exports, module) => {
|
|
233
|
-
function _typeof$2(o) {
|
|
234
|
-
"@babel/helpers - typeof";
|
|
235
|
-
return module.exports = _typeof$2 = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function(o$1) {
|
|
236
|
-
return typeof o$1;
|
|
237
|
-
} : function(o$1) {
|
|
238
|
-
return o$1 && "function" == typeof Symbol && o$1.constructor === Symbol && o$1 !== Symbol.prototype ? "symbol" : typeof o$1;
|
|
239
|
-
}, module.exports.__esModule = true, module.exports["default"] = module.exports, _typeof$2(o);
|
|
240
|
-
}
|
|
241
|
-
module.exports = _typeof$2, module.exports.__esModule = true, module.exports["default"] = module.exports;
|
|
242
|
-
}) });
|
|
243
|
-
|
|
244
|
-
//#endregion
|
|
245
|
-
//#region ../../node_modules/.pnpm/@oxc-project+runtime@0.82.2/node_modules/@oxc-project/runtime/src/helpers/toPrimitive.js
|
|
246
|
-
var require_toPrimitive = /* @__PURE__ */ __commonJS({ "../../node_modules/.pnpm/@oxc-project+runtime@0.82.2/node_modules/@oxc-project/runtime/src/helpers/toPrimitive.js": ((exports, module) => {
|
|
247
|
-
var _typeof$1 = require_typeof()["default"];
|
|
248
|
-
function toPrimitive$1(t, r) {
|
|
249
|
-
if ("object" != _typeof$1(t) || !t) return t;
|
|
250
|
-
var e = t[Symbol.toPrimitive];
|
|
251
|
-
if (void 0 !== e) {
|
|
252
|
-
var i = e.call(t, r || "default");
|
|
253
|
-
if ("object" != _typeof$1(i)) return i;
|
|
254
|
-
throw new TypeError("@@toPrimitive must return a primitive value.");
|
|
255
|
-
}
|
|
256
|
-
return ("string" === r ? String : Number)(t);
|
|
257
|
-
}
|
|
258
|
-
module.exports = toPrimitive$1, module.exports.__esModule = true, module.exports["default"] = module.exports;
|
|
259
|
-
}) });
|
|
260
|
-
|
|
261
|
-
//#endregion
|
|
262
|
-
//#region ../../node_modules/.pnpm/@oxc-project+runtime@0.82.2/node_modules/@oxc-project/runtime/src/helpers/toPropertyKey.js
|
|
263
|
-
var require_toPropertyKey = /* @__PURE__ */ __commonJS({ "../../node_modules/.pnpm/@oxc-project+runtime@0.82.2/node_modules/@oxc-project/runtime/src/helpers/toPropertyKey.js": ((exports, module) => {
|
|
264
|
-
var _typeof = require_typeof()["default"];
|
|
265
|
-
var toPrimitive = require_toPrimitive();
|
|
266
|
-
function toPropertyKey$1(t) {
|
|
267
|
-
var i = toPrimitive(t, "string");
|
|
268
|
-
return "symbol" == _typeof(i) ? i : i + "";
|
|
269
|
-
}
|
|
270
|
-
module.exports = toPropertyKey$1, module.exports.__esModule = true, module.exports["default"] = module.exports;
|
|
271
|
-
}) });
|
|
272
|
-
|
|
273
|
-
//#endregion
|
|
274
|
-
//#region ../../node_modules/.pnpm/@oxc-project+runtime@0.82.2/node_modules/@oxc-project/runtime/src/helpers/defineProperty.js
|
|
275
|
-
var require_defineProperty = /* @__PURE__ */ __commonJS({ "../../node_modules/.pnpm/@oxc-project+runtime@0.82.2/node_modules/@oxc-project/runtime/src/helpers/defineProperty.js": ((exports, module) => {
|
|
276
|
-
var toPropertyKey = require_toPropertyKey();
|
|
277
|
-
function _defineProperty$3(e, r, t) {
|
|
278
|
-
return (r = toPropertyKey(r)) in e ? Object.defineProperty(e, r, {
|
|
279
|
-
value: t,
|
|
280
|
-
enumerable: !0,
|
|
281
|
-
configurable: !0,
|
|
282
|
-
writable: !0
|
|
283
|
-
}) : e[r] = t, e;
|
|
284
|
-
}
|
|
285
|
-
module.exports = _defineProperty$3, module.exports.__esModule = true, module.exports["default"] = module.exports;
|
|
286
|
-
}) });
|
|
287
|
-
|
|
288
236
|
//#endregion
|
|
289
237
|
//#region src/action-guard.ts
|
|
290
|
-
var import_defineProperty$2 = /* @__PURE__ */ __toESM(require_defineProperty(), 1);
|
|
291
238
|
/**
|
|
292
239
|
* Action Guard system for managing action execution timing
|
|
293
240
|
*
|
|
@@ -317,8 +264,31 @@ var import_defineProperty$2 = /* @__PURE__ */ __toESM(require_defineProperty(),
|
|
|
317
264
|
* @internal
|
|
318
265
|
*/
|
|
319
266
|
var ActionGuard = class {
|
|
320
|
-
constructor() {
|
|
321
|
-
|
|
267
|
+
constructor(autoCleanup = true) {
|
|
268
|
+
this.guards = /* @__PURE__ */ new Map();
|
|
269
|
+
this.maxIdleTime = 6e4;
|
|
270
|
+
this.cleanupIntervalMs = 3e4;
|
|
271
|
+
if (autoCleanup) this.startAutoCleanup();
|
|
272
|
+
}
|
|
273
|
+
/**
|
|
274
|
+
* Start automatic cleanup of idle guard states
|
|
275
|
+
*
|
|
276
|
+
* @internal
|
|
277
|
+
*/
|
|
278
|
+
startAutoCleanup() {
|
|
279
|
+
this.cleanupInterval = setInterval(() => {
|
|
280
|
+
const now = Date.now();
|
|
281
|
+
const keysToDelete = [];
|
|
282
|
+
this.guards.forEach((state, key) => {
|
|
283
|
+
const isIdle = now - state.lastExecuted > this.maxIdleTime;
|
|
284
|
+
const hasActiveTimers = state.debounceTimer || state.throttleTimer;
|
|
285
|
+
if (isIdle && !hasActiveTimers) keysToDelete.push(key);
|
|
286
|
+
});
|
|
287
|
+
if (keysToDelete.length > 0) {
|
|
288
|
+
keysToDelete.forEach((key) => this.guards.delete(key));
|
|
289
|
+
if (typeof process !== "undefined" && process.env?.DEBUG_CONTEXT_ACTION) console.debug(`[ActionGuard] Cleaned up ${keysToDelete.length} idle guards`);
|
|
290
|
+
}
|
|
291
|
+
}, this.cleanupIntervalMs);
|
|
322
292
|
}
|
|
323
293
|
/**
|
|
324
294
|
* Apply debouncing to an action
|
|
@@ -448,10 +418,17 @@ var ActionGuard = class {
|
|
|
448
418
|
/** Clear debounce timer if active to prevent memory leaks */
|
|
449
419
|
if (state.debounceTimer) {
|
|
450
420
|
clearTimeout(state.debounceTimer);
|
|
451
|
-
if (state.debounceResolve)
|
|
421
|
+
if (state.debounceResolve) {
|
|
422
|
+
state.debounceResolve(false);
|
|
423
|
+
state.debounceResolve = void 0;
|
|
424
|
+
}
|
|
425
|
+
state.debounceTimer = void 0;
|
|
452
426
|
}
|
|
453
427
|
/** Clear throttle timer if active to prevent memory leaks */
|
|
454
|
-
if (state.throttleTimer)
|
|
428
|
+
if (state.throttleTimer) {
|
|
429
|
+
clearTimeout(state.throttleTimer);
|
|
430
|
+
state.throttleTimer = void 0;
|
|
431
|
+
}
|
|
455
432
|
/** Remove guard state from memory */
|
|
456
433
|
this.guards.delete(actionKey);
|
|
457
434
|
}
|
|
@@ -506,11 +483,42 @@ var ActionGuard = class {
|
|
|
506
483
|
getAllGuardStates() {
|
|
507
484
|
return new Map(this.guards);
|
|
508
485
|
}
|
|
486
|
+
/**
|
|
487
|
+
* 🆕 Explicit destroy method for comprehensive cleanup
|
|
488
|
+
*
|
|
489
|
+
* Cleans up all timers, promises, and intervals to prevent memory leaks.
|
|
490
|
+
* Should be called when ActionGuard is no longer needed.
|
|
491
|
+
*
|
|
492
|
+
* @internal
|
|
493
|
+
*/
|
|
494
|
+
destroy() {
|
|
495
|
+
if (this.cleanupInterval) {
|
|
496
|
+
clearInterval(this.cleanupInterval);
|
|
497
|
+
this.cleanupInterval = void 0;
|
|
498
|
+
}
|
|
499
|
+
this.clearAll();
|
|
500
|
+
}
|
|
501
|
+
/**
|
|
502
|
+
* 🆕 Get statistics about active guards
|
|
503
|
+
*
|
|
504
|
+
* @returns Statistics about guard usage
|
|
505
|
+
*
|
|
506
|
+
* @internal
|
|
507
|
+
*/
|
|
508
|
+
getStats() {
|
|
509
|
+
let withTimers = 0;
|
|
510
|
+
this.guards.forEach((state) => {
|
|
511
|
+
if (state.debounceTimer || state.throttleTimer) withTimers++;
|
|
512
|
+
});
|
|
513
|
+
return {
|
|
514
|
+
activeGuards: this.guards.size,
|
|
515
|
+
withTimers
|
|
516
|
+
};
|
|
517
|
+
}
|
|
509
518
|
};
|
|
510
519
|
|
|
511
520
|
//#endregion
|
|
512
521
|
//#region src/concurrency/OperationQueue.ts
|
|
513
|
-
var import_defineProperty$1 = /* @__PURE__ */ __toESM(require_defineProperty(), 1);
|
|
514
522
|
/**
|
|
515
523
|
* 작업 큐 관리자
|
|
516
524
|
*
|
|
@@ -519,13 +527,17 @@ var import_defineProperty$1 = /* @__PURE__ */ __toESM(require_defineProperty(),
|
|
|
519
527
|
* 2. 우선순위 지원 - 중요한 작업 우선 처리
|
|
520
528
|
* 3. 에러 처리 - 개별 작업 실패가 전체에 영향 주지 않음
|
|
521
529
|
* 4. 메모리 관리 - 완료된 작업 자동 정리
|
|
530
|
+
* 5. 🆕 동시성 제어 - maxConcurrency로 동시 실행 제한
|
|
522
531
|
*/
|
|
523
532
|
var OperationQueue = class {
|
|
524
|
-
constructor(name = "OperationQueue") {
|
|
533
|
+
constructor(name = "OperationQueue", maxConcurrency = 1) {
|
|
525
534
|
this.name = name;
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
535
|
+
this.queue = [];
|
|
536
|
+
this.processingPromise = null;
|
|
537
|
+
this.operationCounter = 0;
|
|
538
|
+
this.activeOperations = 0;
|
|
539
|
+
this.runningOperations = /* @__PURE__ */ new Set();
|
|
540
|
+
this.maxConcurrency = Math.max(1, maxConcurrency);
|
|
529
541
|
}
|
|
530
542
|
/**
|
|
531
543
|
* 작업을 큐에 추가하고 실행 결과를 반환
|
|
@@ -554,35 +566,56 @@ var OperationQueue = class {
|
|
|
554
566
|
});
|
|
555
567
|
}
|
|
556
568
|
/**
|
|
557
|
-
* 큐 처리 메인 로직
|
|
569
|
+
* 🆕 큐 처리 메인 로직 - 동시성 제어 지원
|
|
558
570
|
*
|
|
559
|
-
*
|
|
571
|
+
* maxConcurrency에 따라 동시 실행 작업 수를 제한하여 동시성 문제 방지
|
|
560
572
|
*/
|
|
561
573
|
async processQueue() {
|
|
562
|
-
if (this.
|
|
563
|
-
this.
|
|
574
|
+
if (this.processingPromise) return this.processingPromise;
|
|
575
|
+
this.processingPromise = this._doProcess();
|
|
564
576
|
try {
|
|
565
|
-
|
|
577
|
+
await this.processingPromise;
|
|
578
|
+
} finally {
|
|
579
|
+
this.processingPromise = null;
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
async _doProcess() {
|
|
583
|
+
while (this.queue.length > 0 || this.runningOperations.size > 0) {
|
|
584
|
+
while (this.queue.length > 0 && this.activeOperations < this.maxConcurrency) {
|
|
566
585
|
const operation = this.queue.shift();
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
586
|
+
this.activeOperations++;
|
|
587
|
+
const operationPromise = this.executeOperation(operation);
|
|
588
|
+
this.runningOperations.add(operationPromise);
|
|
589
|
+
operationPromise.finally(() => {
|
|
590
|
+
this.activeOperations--;
|
|
591
|
+
this.runningOperations.delete(operationPromise);
|
|
592
|
+
});
|
|
573
593
|
}
|
|
574
|
-
|
|
575
|
-
this.isProcessing = false;
|
|
594
|
+
if (this.runningOperations.size > 0) await Promise.race(this.runningOperations);
|
|
576
595
|
}
|
|
577
596
|
}
|
|
578
597
|
/**
|
|
579
|
-
*
|
|
598
|
+
* 🆕 개별 작업 실행 로직
|
|
599
|
+
*/
|
|
600
|
+
async executeOperation(operation) {
|
|
601
|
+
try {
|
|
602
|
+
const result = await Promise.resolve(operation.operation());
|
|
603
|
+
operation.resolve(result);
|
|
604
|
+
} catch (error) {
|
|
605
|
+
operation.reject(error);
|
|
606
|
+
}
|
|
607
|
+
}
|
|
608
|
+
/**
|
|
609
|
+
* 🆕 현재 큐 상태 조회 (디버깅용) - 동시성 정보 포함
|
|
580
610
|
*/
|
|
581
611
|
getQueueInfo() {
|
|
582
612
|
return {
|
|
583
613
|
name: this.name,
|
|
584
614
|
queueLength: this.queue.length,
|
|
585
|
-
isProcessing: this.
|
|
615
|
+
isProcessing: Boolean(this.processingPromise),
|
|
616
|
+
activeOperations: this.activeOperations,
|
|
617
|
+
maxConcurrency: this.maxConcurrency,
|
|
618
|
+
runningOperationsCount: this.runningOperations.size,
|
|
586
619
|
operations: this.queue.map((op) => ({
|
|
587
620
|
id: op.id,
|
|
588
621
|
priority: op.priority,
|
|
@@ -591,6 +624,18 @@ var OperationQueue = class {
|
|
|
591
624
|
};
|
|
592
625
|
}
|
|
593
626
|
/**
|
|
627
|
+
* 🆕 동시성 설정 조회
|
|
628
|
+
*/
|
|
629
|
+
getConcurrencyInfo() {
|
|
630
|
+
return {
|
|
631
|
+
maxConcurrency: this.maxConcurrency,
|
|
632
|
+
activeOperations: this.activeOperations,
|
|
633
|
+
availableSlots: this.maxConcurrency - this.activeOperations,
|
|
634
|
+
queuedOperations: this.queue.length,
|
|
635
|
+
efficiency: this.activeOperations / this.maxConcurrency
|
|
636
|
+
};
|
|
637
|
+
}
|
|
638
|
+
/**
|
|
594
639
|
* 큐 비우기 (테스트용)
|
|
595
640
|
*/
|
|
596
641
|
clear() {
|
|
@@ -598,7 +643,7 @@ var OperationQueue = class {
|
|
|
598
643
|
operation.reject(/* @__PURE__ */ new Error("Queue cleared"));
|
|
599
644
|
});
|
|
600
645
|
this.queue = [];
|
|
601
|
-
this.
|
|
646
|
+
this.processingPromise = null;
|
|
602
647
|
}
|
|
603
648
|
/**
|
|
604
649
|
* 큐 크기 조회
|
|
@@ -610,13 +655,12 @@ var OperationQueue = class {
|
|
|
610
655
|
* 처리 중 여부 조회
|
|
611
656
|
*/
|
|
612
657
|
get processing() {
|
|
613
|
-
return this.
|
|
658
|
+
return Boolean(this.processingPromise);
|
|
614
659
|
}
|
|
615
660
|
};
|
|
616
661
|
|
|
617
662
|
//#endregion
|
|
618
663
|
//#region src/ActionRegister.ts
|
|
619
|
-
var import_defineProperty = /* @__PURE__ */ __toESM(require_defineProperty(), 1);
|
|
620
664
|
/**
|
|
621
665
|
* Action Register for managing action handlers with priority-based execution
|
|
622
666
|
*
|
|
@@ -634,27 +678,23 @@ var import_defineProperty = /* @__PURE__ */ __toESM(require_defineProperty(), 1)
|
|
|
634
678
|
*/
|
|
635
679
|
var ActionRegister = class {
|
|
636
680
|
constructor(config = {}) {
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
(0, import_defineProperty.default)(this, "name", void 0);
|
|
643
|
-
(0, import_defineProperty.default)(this, "registryConfig", void 0);
|
|
644
|
-
(0, import_defineProperty.default)(this, "executionStats", /* @__PURE__ */ new Map());
|
|
645
|
-
(0, import_defineProperty.default)(this, "registrationQueue", void 0);
|
|
646
|
-
(0, import_defineProperty.default)(this, "dispatchQueue", void 0);
|
|
681
|
+
this.pipelines = /* @__PURE__ */ new Map();
|
|
682
|
+
this.executionMode = "sequential";
|
|
683
|
+
this.actionExecutionModes = /* @__PURE__ */ new Map();
|
|
684
|
+
this.filterCache = /* @__PURE__ */ new Map();
|
|
685
|
+
this.filterCacheMaxSize = 100;
|
|
647
686
|
this.name = config.name || "ActionRegister";
|
|
648
687
|
this.registryConfig = config.registry;
|
|
649
|
-
this.
|
|
650
|
-
this.
|
|
651
|
-
this.
|
|
688
|
+
this.maxHandlersPerAction = config.registry?.maxHandlersPerAction ?? 1e3;
|
|
689
|
+
this.isDebugMode = Boolean(this.registryConfig?.debug && process.env.NODE_ENV === "development");
|
|
690
|
+
this.actionGuard = new ActionGuard(this.registryConfig?.autoCleanup !== false);
|
|
691
|
+
if (config.registry?.useConcurrencyQueue !== false) this.dispatchQueue = new OperationQueue(`${this.name}-Dispatch`);
|
|
652
692
|
if (this.registryConfig?.defaultExecutionMode) this.executionMode = this.registryConfig.defaultExecutionMode;
|
|
653
|
-
|
|
693
|
+
this.log("ActionRegister initialized", {
|
|
654
694
|
defaultExecutionMode: this.executionMode,
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
695
|
+
autoCleanup: this.registryConfig?.autoCleanup !== false,
|
|
696
|
+
concurrencyQueue: Boolean(this.dispatchQueue),
|
|
697
|
+
debugMode: this.isDebugMode
|
|
658
698
|
});
|
|
659
699
|
}
|
|
660
700
|
/**
|
|
@@ -673,12 +713,82 @@ var ActionRegister = class {
|
|
|
673
713
|
* @public
|
|
674
714
|
*/
|
|
675
715
|
register(action, handler, config = {}) {
|
|
676
|
-
const handlerId = config.id ||
|
|
716
|
+
const handlerId = config.id || this.generateHandlerId(action);
|
|
677
717
|
const unregisterFn = this._performRegistrationSync(action, handler, config, handlerId);
|
|
678
718
|
return unregisterFn;
|
|
679
719
|
}
|
|
680
720
|
/**
|
|
681
|
-
* 🆕
|
|
721
|
+
* 🆕 Unified logging method with cached debug mode check
|
|
722
|
+
*/
|
|
723
|
+
log(message, data, level = "log") {
|
|
724
|
+
if (this.isDebugMode) {
|
|
725
|
+
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
726
|
+
console[level](`🎯 [${timestamp}] [${this.name}] ${message}`, data || "");
|
|
727
|
+
}
|
|
728
|
+
}
|
|
729
|
+
/**
|
|
730
|
+
* 🆕 Generate unique handler ID using crypto
|
|
731
|
+
*/
|
|
732
|
+
generateHandlerId(action) {
|
|
733
|
+
const uuid = crypto.randomUUID();
|
|
734
|
+
return `${String(action)}_${uuid.slice(0, 8)}`;
|
|
735
|
+
}
|
|
736
|
+
/**
|
|
737
|
+
* 🔧 Create and merge AbortSignal instances with proper cleanup
|
|
738
|
+
*
|
|
739
|
+
* @param options Dispatch options containing signal and autoAbort configuration
|
|
740
|
+
* @returns [effectiveSignal, autoAbortController, cleanupFunction]
|
|
741
|
+
*/
|
|
742
|
+
createAbortSignal(options) {
|
|
743
|
+
const signals = [];
|
|
744
|
+
const cleanups = [];
|
|
745
|
+
let autoAbortController;
|
|
746
|
+
if (options?.signal) signals.push(options.signal);
|
|
747
|
+
if (options?.autoAbort?.enabled) {
|
|
748
|
+
autoAbortController = new AbortController();
|
|
749
|
+
signals.push(autoAbortController.signal);
|
|
750
|
+
}
|
|
751
|
+
if (signals.length === 0) return [
|
|
752
|
+
void 0,
|
|
753
|
+
autoAbortController,
|
|
754
|
+
() => {}
|
|
755
|
+
];
|
|
756
|
+
if (signals.length === 1) return [
|
|
757
|
+
signals[0],
|
|
758
|
+
autoAbortController,
|
|
759
|
+
() => cleanups.forEach((c) => c())
|
|
760
|
+
];
|
|
761
|
+
let effectiveSignal;
|
|
762
|
+
if (typeof AbortSignal.any === "function") effectiveSignal = AbortSignal.any(signals);
|
|
763
|
+
else {
|
|
764
|
+
const mergedController = new AbortController();
|
|
765
|
+
effectiveSignal = mergedController.signal;
|
|
766
|
+
signals.forEach((signal) => {
|
|
767
|
+
if (signal.aborted) mergedController.abort();
|
|
768
|
+
else {
|
|
769
|
+
const abortHandler = () => mergedController.abort();
|
|
770
|
+
signal.addEventListener("abort", abortHandler, { once: true });
|
|
771
|
+
cleanups.push(() => signal.removeEventListener("abort", abortHandler));
|
|
772
|
+
}
|
|
773
|
+
});
|
|
774
|
+
}
|
|
775
|
+
const cleanup = () => {
|
|
776
|
+
cleanups.forEach((c) => {
|
|
777
|
+
try {
|
|
778
|
+
c();
|
|
779
|
+
} catch (error) {
|
|
780
|
+
this.log("Cleanup error during AbortSignal cleanup", error, "warn");
|
|
781
|
+
}
|
|
782
|
+
});
|
|
783
|
+
};
|
|
784
|
+
return [
|
|
785
|
+
effectiveSignal,
|
|
786
|
+
autoAbortController,
|
|
787
|
+
cleanup
|
|
788
|
+
];
|
|
789
|
+
}
|
|
790
|
+
/**
|
|
791
|
+
* 🆕 Perform synchronous handler registration
|
|
682
792
|
*/
|
|
683
793
|
_performRegistrationSync(action, handler, config, handlerId) {
|
|
684
794
|
const registration = {
|
|
@@ -689,31 +799,65 @@ var ActionRegister = class {
|
|
|
689
799
|
blocking: config.blocking ?? false,
|
|
690
800
|
once: config.once ?? false,
|
|
691
801
|
debounce: config.debounce ?? void 0,
|
|
692
|
-
throttle: config.throttle ?? void 0
|
|
802
|
+
throttle: config.throttle ?? void 0,
|
|
803
|
+
replaceExisting: config.replaceExisting ?? false
|
|
693
804
|
},
|
|
694
805
|
id: handlerId
|
|
695
806
|
};
|
|
696
807
|
if (!this.pipelines.has(action)) this.pipelines.set(action, []);
|
|
697
808
|
const pipeline = this.pipelines.get(action);
|
|
809
|
+
if (pipeline.length >= this.maxHandlersPerAction) {
|
|
810
|
+
console.warn(`Handler limit (${this.maxHandlersPerAction}) reached for action "${String(action)}". Registration ignored.`);
|
|
811
|
+
return () => {};
|
|
812
|
+
}
|
|
698
813
|
const existingIndex = pipeline.findIndex((reg) => reg.id === handlerId);
|
|
699
|
-
if (existingIndex !== -1)
|
|
700
|
-
|
|
814
|
+
if (existingIndex !== -1) if (config.replaceExisting) {
|
|
815
|
+
const oldRegistration = pipeline[existingIndex];
|
|
816
|
+
if (oldRegistration && typeof oldRegistration.cleanup === "function") try {
|
|
817
|
+
oldRegistration.cleanup();
|
|
818
|
+
} catch (cleanupError) {
|
|
819
|
+
this.log(`Cleanup error for replaced handler: ${String(action)}`, cleanupError, "warn");
|
|
820
|
+
}
|
|
821
|
+
pipeline[existingIndex] = registration;
|
|
822
|
+
pipeline.sort((a, b) => b.config.priority - a.config.priority);
|
|
823
|
+
this.invalidateFilterCache();
|
|
824
|
+
this.log(`Handler replaced: ${String(action)}`, {
|
|
825
|
+
handlerId,
|
|
826
|
+
priority: config.priority,
|
|
827
|
+
totalHandlers: pipeline.length,
|
|
828
|
+
oldHandlerCleaned: Boolean(oldRegistration.cleanup)
|
|
829
|
+
});
|
|
830
|
+
return () => {
|
|
831
|
+
const index = pipeline.findIndex((reg) => reg.id === handlerId && reg === registration);
|
|
832
|
+
if (index !== -1) {
|
|
833
|
+
pipeline.splice(index, 1);
|
|
834
|
+
this.invalidateFilterCache();
|
|
835
|
+
this.log(`Replaced handler unregistered: ${String(action)}`, { handlerId });
|
|
836
|
+
}
|
|
837
|
+
};
|
|
838
|
+
} else {
|
|
839
|
+
this.log(`Handler duplicate ignored: ${String(action)}`, {
|
|
840
|
+
handlerId,
|
|
841
|
+
note: "Use replaceExisting:true to replace"
|
|
842
|
+
}, "warn");
|
|
843
|
+
return () => {};
|
|
844
|
+
}
|
|
701
845
|
pipeline.push(registration);
|
|
702
846
|
pipeline.sort((a, b) => b.config.priority - a.config.priority);
|
|
703
|
-
|
|
847
|
+
this.invalidateFilterCache();
|
|
848
|
+
this.log(`Handler registered: ${String(action)}`, {
|
|
704
849
|
handlerId,
|
|
705
850
|
priority: config.priority,
|
|
706
|
-
totalHandlers: pipeline.length
|
|
707
|
-
registry: this.name
|
|
851
|
+
totalHandlers: pipeline.length
|
|
708
852
|
});
|
|
709
853
|
return () => {
|
|
710
854
|
const index = pipeline.findIndex((reg) => reg.id === handlerId && reg === registration);
|
|
711
855
|
if (index !== -1) {
|
|
712
856
|
pipeline.splice(index, 1);
|
|
713
|
-
|
|
857
|
+
this.invalidateFilterCache();
|
|
858
|
+
this.log(`Handler unregistered: ${String(action)}`, {
|
|
714
859
|
handlerId,
|
|
715
|
-
remainingHandlers: pipeline.length
|
|
716
|
-
registry: this.name
|
|
860
|
+
remainingHandlers: pipeline.length
|
|
717
861
|
});
|
|
718
862
|
}
|
|
719
863
|
};
|
|
@@ -734,7 +878,8 @@ var ActionRegister = class {
|
|
|
734
878
|
* @public
|
|
735
879
|
*/
|
|
736
880
|
async dispatch(action, payload, options) {
|
|
737
|
-
|
|
881
|
+
if (options?.immediate || !this.dispatchQueue) return this._performDispatch(action, payload, options);
|
|
882
|
+
else return this.dispatchQueue.enqueue(async () => {
|
|
738
883
|
return this._performDispatch(action, payload, options);
|
|
739
884
|
});
|
|
740
885
|
}
|
|
@@ -742,47 +887,13 @@ var ActionRegister = class {
|
|
|
742
887
|
* 🆕 실제 디스패치 작업 수행 (큐에서 호출됨)
|
|
743
888
|
*/
|
|
744
889
|
async _performDispatch(action, payload, options) {
|
|
745
|
-
if (payload &&
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
payload.preventDefault;
|
|
749
|
-
payload.stopPropagation;
|
|
750
|
-
payload.currentTarget;
|
|
751
|
-
const hasTarget = payload.target !== void 0;
|
|
752
|
-
hasTarget && payload.target;
|
|
753
|
-
hasTarget && payload.target instanceof Element;
|
|
754
|
-
if (typeof process !== "undefined" && process.env?.DEBUG_CONTEXT_ACTION || typeof process !== "undefined" && process.env?.NODE_ENV === "development") {
|
|
755
|
-
const nestedDOMProperties = [];
|
|
756
|
-
Object.keys(payload).forEach((key) => {
|
|
757
|
-
const prop = payload[key];
|
|
758
|
-
if (prop instanceof Element || prop instanceof Event) nestedDOMProperties.push(`${key}: ${prop instanceof Element ? "Element" : "Event"}`);
|
|
759
|
-
});
|
|
760
|
-
if (nestedDOMProperties.length > 0) console.debug(`[Context-Action] 📋 Nested DOM objects in action "${String(action)}":`, {
|
|
761
|
-
registry: this.name,
|
|
762
|
-
nestedDOMProperties,
|
|
763
|
-
note: "This is informational - usually not a problem"
|
|
764
|
-
});
|
|
765
|
-
}
|
|
766
|
-
}
|
|
767
|
-
let autoAbortController;
|
|
768
|
-
let effectiveSignal = options?.signal;
|
|
769
|
-
if (options?.autoAbort?.enabled) {
|
|
770
|
-
autoAbortController = new AbortController();
|
|
771
|
-
effectiveSignal = autoAbortController.signal;
|
|
772
|
-
if (options.autoAbort.onControllerCreated) options.autoAbort.onControllerCreated(autoAbortController);
|
|
773
|
-
if (options?.signal) {
|
|
774
|
-
const originalSignal = options.signal;
|
|
775
|
-
if (originalSignal.aborted) autoAbortController.abort();
|
|
776
|
-
else {
|
|
777
|
-
const abortHandler$1 = () => autoAbortController.abort();
|
|
778
|
-
originalSignal.addEventListener("abort", abortHandler$1, { once: true });
|
|
779
|
-
}
|
|
780
|
-
}
|
|
781
|
-
}
|
|
890
|
+
if (payload instanceof Event && process.env.NODE_ENV === "development") console.warn(`Event object passed to action "${String(action)}"`, payload.type);
|
|
891
|
+
const [effectiveSignal, autoAbortController, cleanup] = this.createAbortSignal(options);
|
|
892
|
+
if (options?.autoAbort?.onControllerCreated && autoAbortController) options.autoAbort.onControllerCreated(autoAbortController);
|
|
782
893
|
if (effectiveSignal?.aborted) return;
|
|
783
894
|
const pipeline = this.pipelines.get(action);
|
|
784
895
|
if (!pipeline || pipeline.length === 0) return;
|
|
785
|
-
const filteredHandlers = this.filterHandlers(
|
|
896
|
+
const filteredHandlers = options?.filter ? this.filterHandlers(pipeline, options.filter) : pipeline;
|
|
786
897
|
const actionKey = String(action);
|
|
787
898
|
let throttleMs;
|
|
788
899
|
let debounceMs;
|
|
@@ -822,8 +933,6 @@ var ActionRegister = class {
|
|
|
822
933
|
terminated: false,
|
|
823
934
|
terminationResult: void 0
|
|
824
935
|
};
|
|
825
|
-
const startTime = Date.now();
|
|
826
|
-
let executionSuccess = true;
|
|
827
936
|
const abortHandler = effectiveSignal ? () => {
|
|
828
937
|
context.aborted = true;
|
|
829
938
|
context.abortReason = "Action dispatch aborted by signal";
|
|
@@ -831,15 +940,12 @@ var ActionRegister = class {
|
|
|
831
940
|
if (effectiveSignal && abortHandler) effectiveSignal.addEventListener("abort", abortHandler);
|
|
832
941
|
try {
|
|
833
942
|
await this.executePipeline(context, autoAbortController, options?.autoAbort);
|
|
834
|
-
|
|
943
|
+
this.log(`Pipeline execution succeeded for ${String(action)}`);
|
|
835
944
|
} catch (error) {
|
|
836
|
-
|
|
837
|
-
executionSuccess = false;
|
|
945
|
+
this.log(`Pipeline execution failed for ${String(action)}`, error, "error");
|
|
838
946
|
throw error;
|
|
839
947
|
} finally {
|
|
840
|
-
|
|
841
|
-
const duration = Date.now() - startTime;
|
|
842
|
-
this.updateExecutionStats(action, executionSuccess, duration);
|
|
948
|
+
cleanup();
|
|
843
949
|
}
|
|
844
950
|
}
|
|
845
951
|
/**
|
|
@@ -856,36 +962,25 @@ var ActionRegister = class {
|
|
|
856
962
|
* @public
|
|
857
963
|
*/
|
|
858
964
|
async dispatchWithResult(action, payload, options) {
|
|
859
|
-
const
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
if (options?.autoAbort?.enabled) {
|
|
863
|
-
autoAbortController = new AbortController();
|
|
864
|
-
effectiveSignal = autoAbortController.signal;
|
|
865
|
-
if (options.autoAbort.onControllerCreated) options.autoAbort.onControllerCreated(autoAbortController);
|
|
866
|
-
if (options?.signal) {
|
|
867
|
-
const originalSignal = options.signal;
|
|
868
|
-
if (originalSignal.aborted) autoAbortController.abort();
|
|
869
|
-
else {
|
|
870
|
-
const abortHandler$1 = () => autoAbortController.abort();
|
|
871
|
-
originalSignal.addEventListener("abort", abortHandler$1, { once: true });
|
|
872
|
-
}
|
|
873
|
-
}
|
|
874
|
-
}
|
|
965
|
+
const _startTime = Date.now();
|
|
966
|
+
const [effectiveSignal, autoAbortController, cleanup] = this.createAbortSignal(options);
|
|
967
|
+
if (options?.autoAbort?.onControllerCreated && autoAbortController) options.autoAbort.onControllerCreated(autoAbortController);
|
|
875
968
|
if (effectiveSignal?.aborted) return {
|
|
876
969
|
success: false,
|
|
877
970
|
aborted: true,
|
|
878
971
|
abortReason: "Action dispatch aborted by signal",
|
|
879
972
|
terminated: false,
|
|
880
973
|
result: void 0,
|
|
974
|
+
successResults: [],
|
|
881
975
|
results: [],
|
|
976
|
+
failedResults: [],
|
|
882
977
|
execution: {
|
|
883
978
|
duration: 0,
|
|
884
979
|
handlersExecuted: 0,
|
|
885
980
|
handlersSkipped: 0,
|
|
886
981
|
handlersFailed: 0,
|
|
887
|
-
startTime,
|
|
888
|
-
endTime:
|
|
982
|
+
startTime: _startTime,
|
|
983
|
+
endTime: _startTime
|
|
889
984
|
},
|
|
890
985
|
handlers: [],
|
|
891
986
|
errors: []
|
|
@@ -896,19 +991,21 @@ var ActionRegister = class {
|
|
|
896
991
|
aborted: false,
|
|
897
992
|
terminated: false,
|
|
898
993
|
result: void 0,
|
|
994
|
+
successResults: [],
|
|
899
995
|
results: [],
|
|
996
|
+
failedResults: [],
|
|
900
997
|
execution: {
|
|
901
998
|
duration: 0,
|
|
902
999
|
handlersExecuted: 0,
|
|
903
1000
|
handlersSkipped: 0,
|
|
904
1001
|
handlersFailed: 0,
|
|
905
|
-
startTime,
|
|
906
|
-
endTime:
|
|
1002
|
+
startTime: _startTime,
|
|
1003
|
+
endTime: _startTime
|
|
907
1004
|
},
|
|
908
1005
|
handlers: [],
|
|
909
1006
|
errors: []
|
|
910
1007
|
};
|
|
911
|
-
const filteredHandlers = this.filterHandlers(
|
|
1008
|
+
const filteredHandlers = options?.filter ? this.filterHandlers(pipeline, options.filter) : pipeline;
|
|
912
1009
|
const actionKey = String(action);
|
|
913
1010
|
let throttleMs;
|
|
914
1011
|
let debounceMs;
|
|
@@ -934,13 +1031,15 @@ var ActionRegister = class {
|
|
|
934
1031
|
abortReason: "Debounced execution",
|
|
935
1032
|
terminated: false,
|
|
936
1033
|
result: void 0,
|
|
1034
|
+
successResults: [],
|
|
937
1035
|
results: [],
|
|
1036
|
+
failedResults: [],
|
|
938
1037
|
execution: {
|
|
939
|
-
duration: Date.now() -
|
|
1038
|
+
duration: Date.now() - _startTime,
|
|
940
1039
|
handlersExecuted: 0,
|
|
941
1040
|
handlersSkipped: pipeline.length,
|
|
942
1041
|
handlersFailed: 0,
|
|
943
|
-
startTime,
|
|
1042
|
+
startTime: _startTime,
|
|
944
1043
|
endTime: Date.now()
|
|
945
1044
|
},
|
|
946
1045
|
handlers: [],
|
|
@@ -955,13 +1054,15 @@ var ActionRegister = class {
|
|
|
955
1054
|
abortReason: "Throttled execution",
|
|
956
1055
|
terminated: false,
|
|
957
1056
|
result: void 0,
|
|
1057
|
+
successResults: [],
|
|
958
1058
|
results: [],
|
|
1059
|
+
failedResults: [],
|
|
959
1060
|
execution: {
|
|
960
|
-
duration: Date.now() -
|
|
1061
|
+
duration: Date.now() - _startTime,
|
|
961
1062
|
handlersExecuted: 0,
|
|
962
1063
|
handlersSkipped: pipeline.length,
|
|
963
1064
|
handlersFailed: 0,
|
|
964
|
-
startTime,
|
|
1065
|
+
startTime: _startTime,
|
|
965
1066
|
endTime: Date.now()
|
|
966
1067
|
},
|
|
967
1068
|
handlers: [],
|
|
@@ -1000,47 +1101,92 @@ var ActionRegister = class {
|
|
|
1000
1101
|
timestamp: Date.now()
|
|
1001
1102
|
});
|
|
1002
1103
|
} finally {
|
|
1003
|
-
|
|
1104
|
+
cleanup();
|
|
1004
1105
|
}
|
|
1005
1106
|
const endTime = Date.now();
|
|
1006
|
-
|
|
1007
|
-
this.updateExecutionStats(action, executionSuccess, endTime - startTime);
|
|
1107
|
+
!executionError && context.aborted;
|
|
1008
1108
|
const processedResult = this.processResults(context, options?.result);
|
|
1109
|
+
const successResults = context.results.filter((result) => result !== void 0);
|
|
1110
|
+
const failedResults = errors.map((err) => ({
|
|
1111
|
+
handlerId: err.handlerId,
|
|
1112
|
+
error: err.error,
|
|
1113
|
+
expectedType: typeof processedResult
|
|
1114
|
+
}));
|
|
1009
1115
|
const executionResult = {
|
|
1010
1116
|
success: !executionError && !context.aborted,
|
|
1011
1117
|
aborted: context.aborted,
|
|
1012
1118
|
abortReason: context.abortReason,
|
|
1013
1119
|
terminated: context.terminated,
|
|
1014
1120
|
result: processedResult,
|
|
1121
|
+
successResults,
|
|
1015
1122
|
results: context.results,
|
|
1123
|
+
failedResults,
|
|
1016
1124
|
execution: {
|
|
1017
|
-
duration: endTime -
|
|
1125
|
+
duration: endTime - _startTime,
|
|
1018
1126
|
handlersExecuted: context.currentIndex + (context.aborted ? 0 : 1),
|
|
1019
1127
|
handlersSkipped: Math.max(0, filteredHandlers.length - (context.currentIndex + 1)),
|
|
1020
1128
|
handlersFailed: errors.length,
|
|
1021
|
-
startTime,
|
|
1129
|
+
startTime: _startTime,
|
|
1022
1130
|
endTime
|
|
1023
1131
|
},
|
|
1024
1132
|
handlers: handlerResults,
|
|
1025
|
-
errors
|
|
1133
|
+
errors: errors.map((err) => ({
|
|
1134
|
+
handlerId: err.handlerId,
|
|
1135
|
+
error: err.error,
|
|
1136
|
+
timestamp: err.timestamp,
|
|
1137
|
+
severity: "non-blocking"
|
|
1138
|
+
}))
|
|
1026
1139
|
};
|
|
1027
1140
|
/** Clean up one-time handlers after execution */
|
|
1028
1141
|
this.cleanupOneTimeHandlers(action, context.handlers);
|
|
1029
1142
|
return executionResult;
|
|
1030
1143
|
}
|
|
1144
|
+
/**
|
|
1145
|
+
* 🔧 Generate cache key for filter options
|
|
1146
|
+
*/
|
|
1147
|
+
generateFilterCacheKey(filterOptions) {
|
|
1148
|
+
if (!filterOptions) return "no-filter";
|
|
1149
|
+
const key = [
|
|
1150
|
+
filterOptions.handlerIds?.sort().join(",") || "none",
|
|
1151
|
+
filterOptions.excludeHandlerIds?.sort().join(",") || "none",
|
|
1152
|
+
filterOptions.priority?.min?.toString() || "none",
|
|
1153
|
+
filterOptions.priority?.max?.toString() || "none",
|
|
1154
|
+
filterOptions.custom ? "custom" : "none"
|
|
1155
|
+
].join("|");
|
|
1156
|
+
return key;
|
|
1157
|
+
}
|
|
1158
|
+
/**
|
|
1159
|
+
* 🔧 Clear filter cache when pipelines change
|
|
1160
|
+
*/
|
|
1161
|
+
invalidateFilterCache() {
|
|
1162
|
+
this.filterCache.clear();
|
|
1163
|
+
}
|
|
1031
1164
|
filterHandlers(handlers, filterOptions) {
|
|
1032
1165
|
if (!filterOptions) return handlers;
|
|
1033
|
-
|
|
1166
|
+
const cacheKey = this.generateFilterCacheKey(filterOptions);
|
|
1167
|
+
if (!filterOptions.custom) {
|
|
1168
|
+
const cached = this.filterCache.get(cacheKey);
|
|
1169
|
+
if (cached) return cached;
|
|
1170
|
+
}
|
|
1171
|
+
const filtered = handlers.filter((registration) => {
|
|
1034
1172
|
const config = registration.config;
|
|
1035
|
-
if (filterOptions.handlerIds && filterOptions.handlerIds.
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
if (filterOptions.
|
|
1173
|
+
if (filterOptions.handlerIds?.length && !filterOptions.handlerIds.includes(config.id)) return false;
|
|
1174
|
+
if (filterOptions.excludeHandlerIds?.length && filterOptions.excludeHandlerIds.includes(config.id)) return false;
|
|
1175
|
+
if (filterOptions.priority) {
|
|
1176
|
+
if (filterOptions.priority.min !== void 0 && config.priority < filterOptions.priority.min) return false;
|
|
1177
|
+
if (filterOptions.priority.max !== void 0 && config.priority > filterOptions.priority.max) return false;
|
|
1040
1178
|
}
|
|
1041
1179
|
if (filterOptions.custom && !filterOptions.custom(config)) return false;
|
|
1042
1180
|
return true;
|
|
1043
1181
|
});
|
|
1182
|
+
if (!filterOptions.custom) {
|
|
1183
|
+
if (this.filterCache.size >= this.filterCacheMaxSize) {
|
|
1184
|
+
const firstKey = this.filterCache.keys().next().value;
|
|
1185
|
+
if (firstKey) this.filterCache.delete(firstKey);
|
|
1186
|
+
}
|
|
1187
|
+
this.filterCache.set(cacheKey, filtered);
|
|
1188
|
+
}
|
|
1189
|
+
return filtered;
|
|
1044
1190
|
}
|
|
1045
1191
|
processResults(context, resultOptions) {
|
|
1046
1192
|
if (!resultOptions || !resultOptions.collect) return void 0;
|
|
@@ -1126,26 +1272,6 @@ var ActionRegister = class {
|
|
|
1126
1272
|
});
|
|
1127
1273
|
}
|
|
1128
1274
|
/**
|
|
1129
|
-
* Update execution statistics for an action
|
|
1130
|
-
*
|
|
1131
|
-
* @param action Action name
|
|
1132
|
-
* @param success Whether execution was successful
|
|
1133
|
-
* @param duration Execution duration in milliseconds
|
|
1134
|
-
*/
|
|
1135
|
-
updateExecutionStats(action, success, duration) {
|
|
1136
|
-
if (!this.executionStats.has(action)) this.executionStats.set(action, {
|
|
1137
|
-
totalExecutions: 0,
|
|
1138
|
-
totalDuration: 0,
|
|
1139
|
-
successCount: 0,
|
|
1140
|
-
errorCount: 0
|
|
1141
|
-
});
|
|
1142
|
-
const stats = this.executionStats.get(action);
|
|
1143
|
-
stats.totalExecutions++;
|
|
1144
|
-
stats.totalDuration += duration;
|
|
1145
|
-
if (success) stats.successCount++;
|
|
1146
|
-
else stats.errorCount++;
|
|
1147
|
-
}
|
|
1148
|
-
/**
|
|
1149
1275
|
* Get the number of registered handlers for an action
|
|
1150
1276
|
*
|
|
1151
1277
|
* @param action - The action type to count handlers for
|
|
@@ -1197,6 +1323,7 @@ var ActionRegister = class {
|
|
|
1197
1323
|
*/
|
|
1198
1324
|
clearAction(action) {
|
|
1199
1325
|
this.pipelines.delete(action);
|
|
1326
|
+
this.invalidateFilterCache();
|
|
1200
1327
|
}
|
|
1201
1328
|
/**
|
|
1202
1329
|
* Remove all handlers for all actions
|
|
@@ -1207,6 +1334,7 @@ var ActionRegister = class {
|
|
|
1207
1334
|
*/
|
|
1208
1335
|
clearAll() {
|
|
1209
1336
|
this.pipelines.clear();
|
|
1337
|
+
this.invalidateFilterCache();
|
|
1210
1338
|
}
|
|
1211
1339
|
/**
|
|
1212
1340
|
* Get the name of this action register
|
|
@@ -1254,13 +1382,7 @@ var ActionRegister = class {
|
|
|
1254
1382
|
priority,
|
|
1255
1383
|
handlers: handlers.map((h) => ({ id: h.config.id }))
|
|
1256
1384
|
}));
|
|
1257
|
-
const
|
|
1258
|
-
const executionStats = stats ? {
|
|
1259
|
-
totalExecutions: stats.totalExecutions,
|
|
1260
|
-
averageDuration: stats.totalExecutions > 0 ? stats.totalDuration / stats.totalExecutions : 0,
|
|
1261
|
-
successRate: stats.totalExecutions > 0 ? stats.successCount / stats.totalExecutions * 100 : 0,
|
|
1262
|
-
errorCount: stats.errorCount
|
|
1263
|
-
} : void 0;
|
|
1385
|
+
const executionStats = void 0;
|
|
1264
1386
|
return {
|
|
1265
1387
|
action,
|
|
1266
1388
|
handlerCount: pipeline.length,
|
|
@@ -1306,22 +1428,6 @@ var ActionRegister = class {
|
|
|
1306
1428
|
if (this.registryConfig?.debug && process.env.NODE_ENV === "development") console.log(`🎯 Execution mode reset for action '${String(action)}' to default: ${this.executionMode}`);
|
|
1307
1429
|
}
|
|
1308
1430
|
/**
|
|
1309
|
-
* Clear execution statistics for all actions
|
|
1310
|
-
*/
|
|
1311
|
-
clearExecutionStats() {
|
|
1312
|
-
this.executionStats.clear();
|
|
1313
|
-
if (this.registryConfig?.debug && process.env.NODE_ENV === "development") console.log(`🎯 Execution statistics cleared for registry: ${this.name}`);
|
|
1314
|
-
}
|
|
1315
|
-
/**
|
|
1316
|
-
* Clear execution statistics for a specific action
|
|
1317
|
-
*
|
|
1318
|
-
* @param action Action name
|
|
1319
|
-
*/
|
|
1320
|
-
clearActionExecutionStats(action) {
|
|
1321
|
-
this.executionStats.delete(action);
|
|
1322
|
-
if (this.registryConfig?.debug && process.env.NODE_ENV === "development") console.log(`🎯 Execution statistics cleared for action: ${String(action)}`);
|
|
1323
|
-
}
|
|
1324
|
-
/**
|
|
1325
1431
|
* Get registry configuration (for debugging and inspection)
|
|
1326
1432
|
*
|
|
1327
1433
|
* @returns Current registry configuration
|
|
@@ -1335,10 +1441,283 @@ var ActionRegister = class {
|
|
|
1335
1441
|
* @returns Whether debug mode is enabled
|
|
1336
1442
|
*/
|
|
1337
1443
|
isDebugEnabled() {
|
|
1338
|
-
return
|
|
1444
|
+
return this.isDebugMode;
|
|
1445
|
+
}
|
|
1446
|
+
/**
|
|
1447
|
+
* 🆕 Destroy method for comprehensive cleanup
|
|
1448
|
+
*
|
|
1449
|
+
* Cleans up all internal resources including pipelines, guards, queues, and statistics.
|
|
1450
|
+
* Should be called when the ActionRegister is no longer needed to prevent memory leaks.
|
|
1451
|
+
*
|
|
1452
|
+
* @public
|
|
1453
|
+
*/
|
|
1454
|
+
destroy() {
|
|
1455
|
+
this.pipelines.clear();
|
|
1456
|
+
this.actionGuard.destroy();
|
|
1457
|
+
this.dispatchQueue?.clear?.();
|
|
1458
|
+
this.actionExecutionModes.clear();
|
|
1459
|
+
this.filterCache.clear();
|
|
1460
|
+
this.log("ActionRegister destroyed");
|
|
1339
1461
|
}
|
|
1340
1462
|
};
|
|
1341
1463
|
|
|
1342
1464
|
//#endregion
|
|
1343
|
-
|
|
1465
|
+
//#region src/react-helpers.ts
|
|
1466
|
+
/**
|
|
1467
|
+
* 🔧 Create action handler registration configuration for React components
|
|
1468
|
+
*
|
|
1469
|
+
* Creates a configuration object that can be used with React's useEffect to properly
|
|
1470
|
+
* register and unregister action handlers with lifecycle management and cleanup.
|
|
1471
|
+
* This is NOT a hook - it's a factory function for React hook integration.
|
|
1472
|
+
*
|
|
1473
|
+
* @template T - ActionPayloadMap type
|
|
1474
|
+
* @template K - Action key type
|
|
1475
|
+
*
|
|
1476
|
+
* @param registry - ActionRegister instance
|
|
1477
|
+
* @param action - Action name to register handler for
|
|
1478
|
+
* @param handler - Handler function (should be memoized with useCallback)
|
|
1479
|
+
* @param config - Handler configuration
|
|
1480
|
+
*
|
|
1481
|
+
* @returns Configuration object with register/unregister functions
|
|
1482
|
+
*
|
|
1483
|
+
* @example Basic Usage with useEffect
|
|
1484
|
+
* ```tsx
|
|
1485
|
+
* import { useCallback, useEffect } from 'react';
|
|
1486
|
+
* import { createActionHandler } from '@context-action/core/react-helpers';
|
|
1487
|
+
*
|
|
1488
|
+
* function MyComponent() {
|
|
1489
|
+
* const registry = useActionRegister();
|
|
1490
|
+
*
|
|
1491
|
+
* const handleUserUpdate = useCallback(async (payload, controller) => {
|
|
1492
|
+
* // Handler logic here
|
|
1493
|
+
* }, []);
|
|
1494
|
+
*
|
|
1495
|
+
* useEffect(() => {
|
|
1496
|
+
* const { register, unregister } = createActionHandler(
|
|
1497
|
+
* registry,
|
|
1498
|
+
* 'updateUser',
|
|
1499
|
+
* handleUserUpdate,
|
|
1500
|
+
* { priority: 10 }
|
|
1501
|
+
* );
|
|
1502
|
+
*
|
|
1503
|
+
* const cleanup = register();
|
|
1504
|
+
* return () => {
|
|
1505
|
+
* cleanup();
|
|
1506
|
+
* unregister();
|
|
1507
|
+
* };
|
|
1508
|
+
* }, [registry, handleUserUpdate]);
|
|
1509
|
+
* }
|
|
1510
|
+
* ```
|
|
1511
|
+
*
|
|
1512
|
+
* @example With Automatic Cleanup
|
|
1513
|
+
* ```tsx
|
|
1514
|
+
* const [userId, setUserId] = useState('123');
|
|
1515
|
+
*
|
|
1516
|
+
* const handleUserUpdate = useCallback(async (payload, controller) => {
|
|
1517
|
+
* console.log('Updating user:', userId, payload);
|
|
1518
|
+
* }, [userId]);
|
|
1519
|
+
*
|
|
1520
|
+
* useEffect(() => {
|
|
1521
|
+
* const handlerManager = createActionHandler(
|
|
1522
|
+
* registry,
|
|
1523
|
+
* 'updateUser',
|
|
1524
|
+
* handleUserUpdate,
|
|
1525
|
+
* { priority: 10 }
|
|
1526
|
+
* );
|
|
1527
|
+
*
|
|
1528
|
+
* // Simplified registration with automatic cleanup
|
|
1529
|
+
* return handlerManager.registerWithCleanup();
|
|
1530
|
+
* }, [registry, handleUserUpdate, userId]);
|
|
1531
|
+
* ```
|
|
1532
|
+
*
|
|
1533
|
+
* @public
|
|
1534
|
+
*/
|
|
1535
|
+
function createActionHandler(registry, action, handler, config) {
|
|
1536
|
+
const finalConfig = createReactHandlerConfig(String(action), void 0, config);
|
|
1537
|
+
let currentUnregister;
|
|
1538
|
+
let isRegistered = false;
|
|
1539
|
+
return {
|
|
1540
|
+
register() {
|
|
1541
|
+
if (isRegistered && currentUnregister) currentUnregister();
|
|
1542
|
+
currentUnregister = registry.register(action, handler, finalConfig);
|
|
1543
|
+
isRegistered = true;
|
|
1544
|
+
return currentUnregister;
|
|
1545
|
+
},
|
|
1546
|
+
unregister() {
|
|
1547
|
+
if (isRegistered && currentUnregister) {
|
|
1548
|
+
currentUnregister();
|
|
1549
|
+
currentUnregister = void 0;
|
|
1550
|
+
isRegistered = false;
|
|
1551
|
+
}
|
|
1552
|
+
},
|
|
1553
|
+
registerWithCleanup() {
|
|
1554
|
+
const unregisterFn = this.register();
|
|
1555
|
+
return () => {
|
|
1556
|
+
unregisterFn();
|
|
1557
|
+
this.unregister();
|
|
1558
|
+
};
|
|
1559
|
+
},
|
|
1560
|
+
config: finalConfig
|
|
1561
|
+
};
|
|
1562
|
+
}
|
|
1563
|
+
/**
|
|
1564
|
+
* 🆕 React handler configuration factory
|
|
1565
|
+
*
|
|
1566
|
+
* Creates optimized handler configurations for React environments with
|
|
1567
|
+
* proper cleanup and unique ID generation.
|
|
1568
|
+
*
|
|
1569
|
+
* @template T - ActionPayloadMap type
|
|
1570
|
+
* @template K - Action key type
|
|
1571
|
+
*
|
|
1572
|
+
* @param action - Action name
|
|
1573
|
+
* @param componentId - Optional component identifier for debugging
|
|
1574
|
+
* @param config - Base handler configuration
|
|
1575
|
+
*
|
|
1576
|
+
* @returns Optimized configuration for React environments
|
|
1577
|
+
*
|
|
1578
|
+
* @example
|
|
1579
|
+
* ```tsx
|
|
1580
|
+
* function MyComponent({ userId }: { userId: string }) {
|
|
1581
|
+
* const registry = useActionRegister();
|
|
1582
|
+
*
|
|
1583
|
+
* useEffect(() => {
|
|
1584
|
+
* const config = createReactHandlerConfig('updateUser', 'MyComponent', {
|
|
1585
|
+
* priority: 10
|
|
1586
|
+
* });
|
|
1587
|
+
*
|
|
1588
|
+
* const unregister = registry.register('updateUser', handler, config);
|
|
1589
|
+
* return unregister;
|
|
1590
|
+
* }, [registry, handler]);
|
|
1591
|
+
* }
|
|
1592
|
+
* ```
|
|
1593
|
+
*
|
|
1594
|
+
* @public
|
|
1595
|
+
*/
|
|
1596
|
+
function createReactHandlerConfig(action, componentId, config = {}) {
|
|
1597
|
+
const timestamp = Date.now();
|
|
1598
|
+
const random = Math.random().toString(36).substr(2, 5);
|
|
1599
|
+
return {
|
|
1600
|
+
priority: config.priority ?? 0,
|
|
1601
|
+
id: config.id || `${componentId || "react"}_${action}_${timestamp}_${random}`,
|
|
1602
|
+
blocking: config.blocking ?? false,
|
|
1603
|
+
once: config.once ?? false,
|
|
1604
|
+
debounce: config.debounce ?? void 0,
|
|
1605
|
+
throttle: config.throttle ?? void 0,
|
|
1606
|
+
replaceExisting: true
|
|
1607
|
+
};
|
|
1608
|
+
}
|
|
1609
|
+
/**
|
|
1610
|
+
* 🆕 React action dispatcher factory
|
|
1611
|
+
*
|
|
1612
|
+
* Creates a dispatcher function optimized for React component usage
|
|
1613
|
+
* with proper error boundaries and async handling.
|
|
1614
|
+
*
|
|
1615
|
+
* @template T - ActionPayloadMap type
|
|
1616
|
+
*
|
|
1617
|
+
* @param registry - ActionRegister instance
|
|
1618
|
+
* @param errorHandler - Optional error handler for unhandled dispatch errors
|
|
1619
|
+
*
|
|
1620
|
+
* @returns Optimized dispatch function for React components
|
|
1621
|
+
*
|
|
1622
|
+
* @example
|
|
1623
|
+
* ```tsx
|
|
1624
|
+
* function MyComponent() {
|
|
1625
|
+
* const registry = useActionRegister();
|
|
1626
|
+
*
|
|
1627
|
+
* const dispatch = createReactDispatcher(registry, (error, action, payload) => {
|
|
1628
|
+
* console.error(`Failed to dispatch ${action}:`, error);
|
|
1629
|
+
* });
|
|
1630
|
+
*
|
|
1631
|
+
* const handleClick = useCallback(() => {
|
|
1632
|
+
* dispatch('userClick', { buttonId: 'submit' });
|
|
1633
|
+
* }, [dispatch]);
|
|
1634
|
+
* }
|
|
1635
|
+
* ```
|
|
1636
|
+
*
|
|
1637
|
+
* @public
|
|
1638
|
+
*/
|
|
1639
|
+
function createReactDispatcher(registry, errorHandler) {
|
|
1640
|
+
return async (action, payload, options) => {
|
|
1641
|
+
try {
|
|
1642
|
+
await registry.dispatch(action, payload, {
|
|
1643
|
+
immediate: false,
|
|
1644
|
+
...options
|
|
1645
|
+
});
|
|
1646
|
+
} catch (error) {
|
|
1647
|
+
const errorObj = error instanceof Error ? error : new Error(String(error));
|
|
1648
|
+
if (errorHandler) errorHandler(errorObj, action, payload);
|
|
1649
|
+
else console.error(`[ActionRegister] Dispatch failed for action '${String(action)}':`, errorObj);
|
|
1650
|
+
}
|
|
1651
|
+
};
|
|
1652
|
+
}
|
|
1653
|
+
/**
|
|
1654
|
+
* 🆕 React development utilities
|
|
1655
|
+
*
|
|
1656
|
+
* Provides debugging and development helpers specifically for React environments.
|
|
1657
|
+
*/
|
|
1658
|
+
const ReactDevUtils = {
|
|
1659
|
+
enableDebugMode() {
|
|
1660
|
+
if (typeof window !== "undefined") window.__CONTEXT_ACTION_REACT_DEBUG__ = true;
|
|
1661
|
+
},
|
|
1662
|
+
disableDebugMode() {
|
|
1663
|
+
if (typeof window !== "undefined") window.__CONTEXT_ACTION_REACT_DEBUG__ = false;
|
|
1664
|
+
},
|
|
1665
|
+
isDebugMode() {
|
|
1666
|
+
return typeof window !== "undefined" && Boolean(window.__CONTEXT_ACTION_REACT_DEBUG__);
|
|
1667
|
+
},
|
|
1668
|
+
log(component, action, message, data) {
|
|
1669
|
+
if (this.isDebugMode()) console.log(`🎯 [React-ActionRegister] [${component}] ${action}: ${message}`, data || "");
|
|
1670
|
+
},
|
|
1671
|
+
getStats(registry) {
|
|
1672
|
+
const registryInfo = registry.getRegistryInfo();
|
|
1673
|
+
let reactHandlers = 0;
|
|
1674
|
+
registry.getRegisteredActions().forEach((action) => {
|
|
1675
|
+
const stats = registry.getActionStats(action);
|
|
1676
|
+
if (stats) stats.handlersByPriority.forEach((priorityGroup) => {
|
|
1677
|
+
priorityGroup.handlers.forEach((handler) => {
|
|
1678
|
+
if (handler.id.includes("react")) reactHandlers++;
|
|
1679
|
+
});
|
|
1680
|
+
});
|
|
1681
|
+
});
|
|
1682
|
+
return {
|
|
1683
|
+
totalHandlers: registryInfo.totalHandlers,
|
|
1684
|
+
reactHandlers,
|
|
1685
|
+
registryInfo
|
|
1686
|
+
};
|
|
1687
|
+
}
|
|
1688
|
+
};
|
|
1689
|
+
/**
|
|
1690
|
+
* 🆕 React Error Boundary integration
|
|
1691
|
+
*
|
|
1692
|
+
* Utilities for integrating ActionRegister errors with React Error Boundaries.
|
|
1693
|
+
*/
|
|
1694
|
+
var ReactActionError = class ReactActionError extends Error {
|
|
1695
|
+
constructor(message, action, payload, handlerId, originalError) {
|
|
1696
|
+
super(message);
|
|
1697
|
+
this.name = "ReactActionError";
|
|
1698
|
+
this.action = action;
|
|
1699
|
+
this.payload = payload;
|
|
1700
|
+
this.handlerId = handlerId;
|
|
1701
|
+
this.timestamp = Date.now();
|
|
1702
|
+
if (originalError && originalError.stack) this.stack = originalError.stack;
|
|
1703
|
+
}
|
|
1704
|
+
/**
|
|
1705
|
+
* Create a React Error Boundary compatible error
|
|
1706
|
+
*/
|
|
1707
|
+
static fromActionError(originalError, action, payload, handlerId) {
|
|
1708
|
+
return new ReactActionError(`Action '${action}' failed: ${originalError.message}`, action, payload, handlerId, originalError);
|
|
1709
|
+
}
|
|
1710
|
+
};
|
|
1711
|
+
/**
|
|
1712
|
+
* 🆕 Type guard for React Action Errors
|
|
1713
|
+
*
|
|
1714
|
+
* @param error - Error to check
|
|
1715
|
+
* @returns True if error is a ReactActionError
|
|
1716
|
+
*/
|
|
1717
|
+
function isReactActionError(error) {
|
|
1718
|
+
return error instanceof ReactActionError;
|
|
1719
|
+
}
|
|
1720
|
+
|
|
1721
|
+
//#endregion
|
|
1722
|
+
export { ActionGuard, ActionRegister, ReactActionError, ReactDevUtils, createActionHandler, createReactDispatcher, createReactHandlerConfig, executeParallel, executeRace, executeSequential, isReactActionError };
|
|
1344
1723
|
//# sourceMappingURL=index.js.map
|