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