@context-action/core 0.0.4 → 0.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +661 -478
- package/dist/index.d.cts +147 -585
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.ts +147 -585
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +661 -479
- package/dist/index.js.map +1 -1
- package/package.json +3 -5
package/dist/index.cjs
CHANGED
|
@@ -24,116 +24,91 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
24
24
|
}) : target, mod));
|
|
25
25
|
|
|
26
26
|
//#endregion
|
|
27
|
-
const __context_action_logger = __toESM(require("@context-action/logger"));
|
|
28
27
|
|
|
29
28
|
//#region src/execution-modes.ts
|
|
30
29
|
/**
|
|
31
30
|
* Execute handlers in sequential mode (one after another)
|
|
32
|
-
* @implements execution-modes
|
|
33
|
-
* @implements sequential-execution
|
|
34
|
-
* @memberof core-concepts
|
|
35
|
-
* @internal
|
|
36
|
-
* @since 1.0.0
|
|
37
|
-
*
|
|
38
|
-
* Executes action handlers sequentially in priority order, supporting flow control,
|
|
39
|
-
* conditional execution, and priority jumping within the pipeline.
|
|
40
|
-
*
|
|
41
|
-
* @template T - The type of the payload being processed
|
|
42
|
-
* @param context - Pipeline execution context with handlers and state
|
|
43
|
-
* @param createController - Factory function for creating pipeline controllers
|
|
44
|
-
* @param logger - Logger instance for tracing execution
|
|
45
|
-
* @returns Promise that resolves when all handlers complete or pipeline aborts
|
|
46
|
-
*
|
|
47
|
-
* Features:
|
|
48
|
-
* - Priority-based execution order (higher priority first)
|
|
49
|
-
* - Support for priority jumping within execution
|
|
50
|
-
* - Conditional handler execution (condition/validation checks)
|
|
51
|
-
* - Blocking/non-blocking handler support
|
|
52
|
-
* - Comprehensive error handling and recovery
|
|
53
31
|
*/
|
|
54
|
-
async function executeSequential(context, createController
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
if (context.aborted)
|
|
58
|
-
logger.trace("Sequential execution aborted", {
|
|
59
|
-
atIndex: i,
|
|
60
|
-
reason: context.abortReason
|
|
61
|
-
});
|
|
62
|
-
break;
|
|
63
|
-
}
|
|
64
|
-
if (context.jumpToPriority !== void 0) {
|
|
65
|
-
const jumpIndex = context.handlers.findIndex((handler) => handler.config.priority === context.jumpToPriority);
|
|
66
|
-
if (jumpIndex !== -1 && jumpIndex !== i) {
|
|
67
|
-
logger.trace("Jumping to priority", {
|
|
68
|
-
fromIndex: i,
|
|
69
|
-
toIndex: jumpIndex,
|
|
70
|
-
priority: context.jumpToPriority
|
|
71
|
-
});
|
|
72
|
-
i = jumpIndex - 1;
|
|
73
|
-
context.jumpToPriority = void 0;
|
|
74
|
-
continue;
|
|
75
|
-
}
|
|
76
|
-
context.jumpToPriority = void 0;
|
|
77
|
-
}
|
|
32
|
+
async function executeSequential(context, createController) {
|
|
33
|
+
let i = 0;
|
|
34
|
+
while (i < context.handlers.length) {
|
|
35
|
+
if (context.aborted || context.terminated) break;
|
|
78
36
|
const registration = context.handlers[i];
|
|
79
37
|
context.currentIndex = i;
|
|
38
|
+
/** Check condition if provided */
|
|
80
39
|
if (registration.config.condition && !registration.config.condition()) {
|
|
81
|
-
|
|
40
|
+
i++;
|
|
82
41
|
continue;
|
|
83
42
|
}
|
|
43
|
+
/** Check validation if provided */
|
|
84
44
|
if (registration.config.validation && !registration.config.validation(context.payload)) {
|
|
85
|
-
|
|
45
|
+
i++;
|
|
86
46
|
continue;
|
|
87
47
|
}
|
|
88
48
|
const controller = createController(registration, i);
|
|
89
49
|
try {
|
|
90
|
-
|
|
91
|
-
handlerId: registration.id,
|
|
92
|
-
priority: registration.config.priority
|
|
93
|
-
});
|
|
50
|
+
if (context.aborted) break;
|
|
94
51
|
const result = registration.handler(context.payload, controller);
|
|
52
|
+
/** Wait for async handlers if they're blocking */
|
|
95
53
|
if (registration.config.blocking && result instanceof Promise) {
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
54
|
+
const handlerResult = await result;
|
|
55
|
+
/** Collect result if handler returned something and wasn't terminated */
|
|
56
|
+
if (handlerResult !== void 0 && !context.terminated) context.results.push(handlerResult);
|
|
57
|
+
} else if (result !== void 0 && !context.terminated)
|
|
58
|
+
/** Collect synchronous result */
|
|
59
|
+
if (result instanceof Promise) result.then((asyncResult) => {
|
|
60
|
+
if (asyncResult !== void 0 && !context.terminated) context.results.push(asyncResult);
|
|
61
|
+
}).catch(() => {});
|
|
62
|
+
else context.results.push(result);
|
|
63
|
+
/** Check if pipeline was terminated by controller.return() */
|
|
64
|
+
if (context.terminated) break;
|
|
65
|
+
/** Handle jump to priority AFTER handler execution */
|
|
66
|
+
if (context.jumpToPriority !== void 0) {
|
|
67
|
+
const jumpIndex = context.handlers.findIndex((handler) => handler.config.priority === context.jumpToPriority);
|
|
68
|
+
if (jumpIndex !== -1) {
|
|
69
|
+
i = jumpIndex;
|
|
70
|
+
context.jumpToPriority = void 0;
|
|
71
|
+
continue;
|
|
72
|
+
} else {
|
|
73
|
+
context.jumpToPriority = void 0;
|
|
74
|
+
i++;
|
|
75
|
+
}
|
|
76
|
+
} else i++;
|
|
100
77
|
} catch (error) {
|
|
101
|
-
logger.error(`Handler '${registration.id}' threw an error`, error);
|
|
102
78
|
if (registration.config.blocking) throw error;
|
|
79
|
+
i++;
|
|
103
80
|
}
|
|
104
81
|
}
|
|
105
82
|
}
|
|
106
83
|
/**
|
|
107
84
|
* Execute handlers in parallel mode (all at once)
|
|
108
|
-
* @internal
|
|
109
85
|
*/
|
|
110
|
-
async function executeParallel(context, createController
|
|
111
|
-
|
|
86
|
+
async function executeParallel(context, createController) {
|
|
87
|
+
/** Filter handlers that should run */
|
|
112
88
|
const runnableHandlers = context.handlers.filter((registration, _index) => {
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
if (registration.config.validation && !registration.config.validation(context.payload)) {
|
|
118
|
-
logger.debug(`Skipping handler '${registration.id}' - validation failed`);
|
|
119
|
-
return false;
|
|
120
|
-
}
|
|
89
|
+
/** Check condition */
|
|
90
|
+
if (registration.config.condition && !registration.config.condition()) return false;
|
|
91
|
+
/** Check validation */
|
|
92
|
+
if (registration.config.validation && !registration.config.validation(context.payload)) return false;
|
|
121
93
|
return true;
|
|
122
94
|
});
|
|
123
|
-
|
|
95
|
+
/** Create promises for all handlers */
|
|
124
96
|
const handlerPromises = runnableHandlers.map(async (registration, _index) => {
|
|
125
97
|
const controller = createController(registration, _index);
|
|
126
98
|
try {
|
|
127
|
-
logger.trace(`Starting parallel handler '${registration.id}'`);
|
|
128
99
|
const result = registration.handler(context.payload, controller);
|
|
129
|
-
|
|
130
|
-
|
|
100
|
+
let handlerResult;
|
|
101
|
+
if (result instanceof Promise) handlerResult = await result;
|
|
102
|
+
else handlerResult = result;
|
|
103
|
+
/** Collect result if handler returned something and pipeline wasn't terminated */
|
|
104
|
+
if (handlerResult !== void 0 && !context.terminated) context.results.push(handlerResult);
|
|
131
105
|
return {
|
|
132
106
|
success: true,
|
|
133
|
-
handlerId: registration.id
|
|
107
|
+
handlerId: registration.id,
|
|
108
|
+
result: handlerResult,
|
|
109
|
+
terminated: context.terminated
|
|
134
110
|
};
|
|
135
111
|
} catch (error) {
|
|
136
|
-
logger.error(`Parallel handler '${registration.id}' failed`, error);
|
|
137
112
|
if (registration.config.blocking) throw error;
|
|
138
113
|
return {
|
|
139
114
|
success: false,
|
|
@@ -142,7 +117,9 @@ async function executeParallel(context, createController, logger) {
|
|
|
142
117
|
};
|
|
143
118
|
}
|
|
144
119
|
});
|
|
120
|
+
/** Wait for all handlers to complete */
|
|
145
121
|
const results = await Promise.allSettled(handlerPromises);
|
|
122
|
+
/** Check for any rejected blocking handlers */
|
|
146
123
|
const failures = results.filter((result, index) => {
|
|
147
124
|
if (result.status === "rejected") {
|
|
148
125
|
const registration = runnableHandlers[index];
|
|
@@ -154,47 +131,43 @@ async function executeParallel(context, createController, logger) {
|
|
|
154
131
|
const firstFailure = failures[0];
|
|
155
132
|
throw firstFailure.reason;
|
|
156
133
|
}
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
134
|
+
/** Check if any handler terminated the pipeline */
|
|
135
|
+
const terminatedResults = results.filter((result) => result.status === "fulfilled" && result.value.terminated);
|
|
136
|
+
if (terminatedResults.length > 0) {
|
|
137
|
+
context.terminated = true;
|
|
138
|
+
const firstTerminated = terminatedResults[0];
|
|
139
|
+
context.terminationResult = firstTerminated.value.result;
|
|
140
|
+
}
|
|
161
141
|
}
|
|
162
142
|
/**
|
|
163
143
|
* Execute handlers in race mode (first to complete wins)
|
|
164
|
-
* @internal
|
|
165
144
|
*/
|
|
166
|
-
async function executeRace(context, createController
|
|
167
|
-
|
|
145
|
+
async function executeRace(context, createController) {
|
|
146
|
+
/** Filter handlers that should run */
|
|
168
147
|
const runnableHandlers = context.handlers.filter((registration, _index) => {
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
if (registration.config.validation && !registration.config.validation(context.payload)) {
|
|
174
|
-
logger.debug(`Skipping handler '${registration.id}' - validation failed`);
|
|
175
|
-
return false;
|
|
176
|
-
}
|
|
148
|
+
/** Check condition */
|
|
149
|
+
if (registration.config.condition && !registration.config.condition()) return false;
|
|
150
|
+
/** Check validation */
|
|
151
|
+
if (registration.config.validation && !registration.config.validation(context.payload)) return false;
|
|
177
152
|
return true;
|
|
178
153
|
});
|
|
179
|
-
if (runnableHandlers.length === 0)
|
|
180
|
-
|
|
181
|
-
return;
|
|
182
|
-
}
|
|
183
|
-
logger.trace(`Racing ${runnableHandlers.length} handlers`);
|
|
154
|
+
if (runnableHandlers.length === 0) return;
|
|
155
|
+
/** Create promises for all handlers */
|
|
184
156
|
const handlerPromises = runnableHandlers.map(async (registration, _index) => {
|
|
185
157
|
const controller = createController(registration, _index);
|
|
186
158
|
try {
|
|
187
|
-
logger.trace(`Starting race handler '${registration.id}'`);
|
|
188
159
|
const result = registration.handler(context.payload, controller);
|
|
189
|
-
|
|
190
|
-
|
|
160
|
+
let handlerResult;
|
|
161
|
+
if (result instanceof Promise) handlerResult = await result;
|
|
162
|
+
else handlerResult = result;
|
|
191
163
|
return {
|
|
192
164
|
success: true,
|
|
193
165
|
handlerId: registration.id,
|
|
194
|
-
registration
|
|
166
|
+
registration,
|
|
167
|
+
result: handlerResult,
|
|
168
|
+
terminated: context.terminated
|
|
195
169
|
};
|
|
196
170
|
} catch (error) {
|
|
197
|
-
logger.error(`Race handler '${registration.id}' failed`, error);
|
|
198
171
|
return {
|
|
199
172
|
success: false,
|
|
200
173
|
handlerId: registration.id,
|
|
@@ -203,16 +176,16 @@ async function executeRace(context, createController, logger) {
|
|
|
203
176
|
};
|
|
204
177
|
}
|
|
205
178
|
});
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
179
|
+
/** Race all handlers */
|
|
180
|
+
const winner = await Promise.race(handlerPromises);
|
|
181
|
+
/** If the winner failed and was blocking, throw the error */
|
|
182
|
+
if (!winner.success && winner.registration?.config.blocking) throw winner.error;
|
|
183
|
+
/** Collect result from the winning handler */
|
|
184
|
+
if (winner.success && winner.result !== void 0) context.results.push(winner.result);
|
|
185
|
+
/** Check if the winning handler terminated the pipeline */
|
|
186
|
+
if (winner.success && winner.terminated) {
|
|
187
|
+
context.terminated = true;
|
|
188
|
+
context.terminationResult = winner.result;
|
|
216
189
|
}
|
|
217
190
|
}
|
|
218
191
|
|
|
@@ -282,19 +255,13 @@ var import_defineProperty$1 = __toESM(require_defineProperty(), 1);
|
|
|
282
255
|
* @implements action-guard
|
|
283
256
|
* @implements performance-optimization
|
|
284
257
|
* @implements user-experience-optimization
|
|
258
|
+
* @implements class-naming
|
|
285
259
|
* @memberof core-concepts
|
|
286
260
|
* @internal
|
|
287
261
|
* @since 1.0.0
|
|
288
262
|
*
|
|
289
|
-
*
|
|
290
|
-
*
|
|
291
|
-
* per action to prevent unnecessary or excessive action invocations.
|
|
292
|
-
*
|
|
293
|
-
* Key Features:
|
|
294
|
-
* - Debouncing: Delay execution until activity stops
|
|
295
|
-
* - Throttling: Limit execution frequency to intervals
|
|
296
|
-
* - Per-action state management with automatic cleanup
|
|
297
|
-
* - Memory leak prevention through proper timer management
|
|
263
|
+
* Manages action execution timing through debouncing and throttling
|
|
264
|
+
* @implements performance-optimization
|
|
298
265
|
*
|
|
299
266
|
* @example
|
|
300
267
|
* ```typescript
|
|
@@ -312,10 +279,8 @@ var import_defineProperty$1 = __toESM(require_defineProperty(), 1);
|
|
|
312
279
|
* ```
|
|
313
280
|
*/
|
|
314
281
|
var ActionGuard = class {
|
|
315
|
-
constructor(
|
|
282
|
+
constructor() {
|
|
316
283
|
(0, import_defineProperty$1.default)(this, "guards", /* @__PURE__ */ new Map());
|
|
317
|
-
(0, import_defineProperty$1.default)(this, "logger", void 0);
|
|
318
|
-
this.logger = logger;
|
|
319
284
|
}
|
|
320
285
|
/**
|
|
321
286
|
* Check if action should be debounced
|
|
@@ -324,27 +289,29 @@ var ActionGuard = class {
|
|
|
324
289
|
* @returns Promise that resolves when debounce period is complete
|
|
325
290
|
*/
|
|
326
291
|
async debounce(actionKey, debounceMs) {
|
|
327
|
-
|
|
292
|
+
/** Get or create guard state for this action */
|
|
328
293
|
let state = this.guards.get(actionKey);
|
|
329
294
|
if (!state) {
|
|
295
|
+
/** Initialize new guard state with default values */
|
|
330
296
|
state = {
|
|
331
297
|
lastExecuted: 0,
|
|
332
298
|
isThrottled: false
|
|
333
299
|
};
|
|
334
300
|
this.guards.set(actionKey, state);
|
|
335
301
|
}
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
302
|
+
/** Clear any existing debounce timer to restart the delay period */
|
|
303
|
+
/** This implements the "debounce" behavior where rapid calls reset the timer */
|
|
304
|
+
if (state.debounceTimer) clearTimeout(state.debounceTimer);
|
|
305
|
+
/** Create new debounce promise that resolves after the delay period */
|
|
306
|
+
/** The promise will only resolve if no new debounce requests arrive */
|
|
340
307
|
return new Promise((resolve) => {
|
|
341
308
|
state.debounceTimer = setTimeout(() => {
|
|
342
|
-
|
|
309
|
+
/** Clean up timer reference to prevent memory leaks */
|
|
343
310
|
state.debounceTimer = void 0;
|
|
311
|
+
/** Update last execution timestamp for throttling calculations */
|
|
344
312
|
state.lastExecuted = Date.now();
|
|
345
313
|
resolve(true);
|
|
346
314
|
}, debounceMs);
|
|
347
|
-
this.logger.trace(`Set debounce timer for '${actionKey}'`, { delay: debounceMs });
|
|
348
315
|
});
|
|
349
316
|
}
|
|
350
317
|
/**
|
|
@@ -354,9 +321,10 @@ var ActionGuard = class {
|
|
|
354
321
|
* @returns True if action should proceed, false if throttled
|
|
355
322
|
*/
|
|
356
323
|
throttle(actionKey, throttleMs) {
|
|
357
|
-
|
|
324
|
+
/** Get or create guard state for this action */
|
|
358
325
|
let state = this.guards.get(actionKey);
|
|
359
326
|
if (!state) {
|
|
327
|
+
/** Initialize new guard state with default values */
|
|
360
328
|
state = {
|
|
361
329
|
lastExecuted: 0,
|
|
362
330
|
isThrottled: false
|
|
@@ -365,30 +333,27 @@ var ActionGuard = class {
|
|
|
365
333
|
}
|
|
366
334
|
const now = Date.now();
|
|
367
335
|
const timeSinceLastExecution = now - state.lastExecuted;
|
|
336
|
+
/** Check if enough time has passed since last execution */
|
|
337
|
+
/** If throttle period has elapsed, allow immediate execution */
|
|
368
338
|
if (timeSinceLastExecution >= throttleMs) {
|
|
339
|
+
/** Update execution timestamp and clear throttled state */
|
|
369
340
|
state.lastExecuted = now;
|
|
370
341
|
state.isThrottled = false;
|
|
371
|
-
this.logger.trace(`Throttle passed for '${actionKey}'`, {
|
|
372
|
-
timeSinceLastExecution,
|
|
373
|
-
throttleMs
|
|
374
|
-
});
|
|
375
342
|
return true;
|
|
376
343
|
}
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
344
|
+
/** If already in throttled state, don't create duplicate timers */
|
|
345
|
+
/** This prevents timer accumulation and unnecessary processing */
|
|
346
|
+
if (state.isThrottled) return false;
|
|
347
|
+
/** Set throttle timer to automatically clear the throttled state */
|
|
348
|
+
/** Calculate remaining time until throttle period expires */
|
|
381
349
|
state.isThrottled = true;
|
|
382
350
|
const remainingTime = throttleMs - timeSinceLastExecution;
|
|
351
|
+
/** Create timer to reset throttled state when period expires */
|
|
383
352
|
state.throttleTimer = setTimeout(() => {
|
|
353
|
+
/** Clear throttled state and timer reference */
|
|
384
354
|
state.isThrottled = false;
|
|
385
355
|
state.throttleTimer = void 0;
|
|
386
|
-
this.logger.trace(`Throttle period ended for '${actionKey}'`);
|
|
387
356
|
}, remainingTime);
|
|
388
|
-
this.logger.trace(`Action '${actionKey}' throttled`, {
|
|
389
|
-
timeSinceLastExecution,
|
|
390
|
-
remainingTime
|
|
391
|
-
});
|
|
392
357
|
return false;
|
|
393
358
|
}
|
|
394
359
|
/**
|
|
@@ -396,26 +361,30 @@ var ActionGuard = class {
|
|
|
396
361
|
* @param actionKey - Action key to clear
|
|
397
362
|
*/
|
|
398
363
|
clearGuards(actionKey) {
|
|
399
|
-
this.logger.trace(`Clearing guards for '${actionKey}'`);
|
|
400
364
|
const state = this.guards.get(actionKey);
|
|
401
365
|
if (state) {
|
|
366
|
+
/** Clear debounce timer if active to prevent memory leaks */
|
|
402
367
|
if (state.debounceTimer) clearTimeout(state.debounceTimer);
|
|
368
|
+
/** Clear throttle timer if active to prevent memory leaks */
|
|
403
369
|
if (state.throttleTimer) clearTimeout(state.throttleTimer);
|
|
370
|
+
/** Remove guard state from memory */
|
|
404
371
|
this.guards.delete(actionKey);
|
|
405
|
-
this.logger.debug(`Cleared guards for '${actionKey}'`);
|
|
406
372
|
}
|
|
407
373
|
}
|
|
408
374
|
/**
|
|
409
375
|
* Clear all guards
|
|
410
376
|
*/
|
|
411
377
|
clearAll() {
|
|
412
|
-
|
|
378
|
+
/** Iterate through all guard states and clear their timers */
|
|
379
|
+
/** This prevents memory leaks when clearing the entire guard system */
|
|
413
380
|
for (const [, state] of this.guards) {
|
|
381
|
+
/** Clear any active debounce timers */
|
|
414
382
|
if (state.debounceTimer) clearTimeout(state.debounceTimer);
|
|
383
|
+
/** Clear any active throttle timers */
|
|
415
384
|
if (state.throttleTimer) clearTimeout(state.throttleTimer);
|
|
416
385
|
}
|
|
386
|
+
/** Remove all guard states from memory */
|
|
417
387
|
this.guards.clear();
|
|
418
|
-
this.logger.debug("Cleared all action guards");
|
|
419
388
|
}
|
|
420
389
|
/**
|
|
421
390
|
* Get current guard state for debugging
|
|
@@ -436,220 +405,56 @@ var ActionGuard = class {
|
|
|
436
405
|
//#region src/ActionRegister.ts
|
|
437
406
|
var import_defineProperty = __toESM(require_defineProperty(), 1);
|
|
438
407
|
/**
|
|
439
|
-
*
|
|
440
|
-
* @internal
|
|
441
|
-
*/
|
|
442
|
-
var SimpleEventEmitter = class {
|
|
443
|
-
constructor() {
|
|
444
|
-
(0, import_defineProperty.default)(this, "listeners", /* @__PURE__ */ new Map());
|
|
445
|
-
}
|
|
446
|
-
on(event, handler) {
|
|
447
|
-
if (!this.listeners.has(event)) this.listeners.set(event, /* @__PURE__ */ new Set());
|
|
448
|
-
this.listeners.get(event).add(handler);
|
|
449
|
-
return () => this.off(event, handler);
|
|
450
|
-
}
|
|
451
|
-
emit(event, data) {
|
|
452
|
-
const eventListeners = this.listeners.get(event);
|
|
453
|
-
if (eventListeners) eventListeners.forEach((handler) => {
|
|
454
|
-
try {
|
|
455
|
-
handler(data);
|
|
456
|
-
} catch (error) {
|
|
457
|
-
console.error(`Error in event handler for ${String(event)}:`, error);
|
|
458
|
-
}
|
|
459
|
-
});
|
|
460
|
-
}
|
|
461
|
-
off(event, handler) {
|
|
462
|
-
const eventListeners = this.listeners.get(event);
|
|
463
|
-
if (eventListeners) {
|
|
464
|
-
eventListeners.delete(handler);
|
|
465
|
-
if (eventListeners.size === 0) this.listeners.delete(event);
|
|
466
|
-
}
|
|
467
|
-
}
|
|
468
|
-
removeAllListeners(event) {
|
|
469
|
-
if (event) this.listeners.delete(event);
|
|
470
|
-
else this.listeners.clear();
|
|
471
|
-
}
|
|
472
|
-
};
|
|
473
|
-
/**
|
|
474
|
-
* Central action registration and dispatch system
|
|
475
|
-
* @implements action-pipeline-system
|
|
476
|
-
* @implements actionregister
|
|
477
|
-
* @memberof core-concepts
|
|
408
|
+
* 중앙화된 액션 등록 및 디스패치 시스템으로, 타입 안전한 액션 파이프라인 관리를 제공하는 핵심 클래스입니다.
|
|
478
409
|
*
|
|
479
|
-
*
|
|
480
|
-
* @
|
|
410
|
+
* @implements {ActionRegister}
|
|
411
|
+
* @implements {Action Pipeline System}
|
|
412
|
+
* @memberof core-concepts
|
|
481
413
|
*
|
|
482
414
|
* @example
|
|
483
415
|
* ```typescript
|
|
484
416
|
* interface AppActions extends ActionPayloadMap {
|
|
485
|
-
* increment: void;
|
|
486
|
-
* setCount: number;
|
|
487
417
|
* updateUser: { id: string; name: string };
|
|
418
|
+
* calculateTotal: void;
|
|
488
419
|
* }
|
|
489
|
-
*
|
|
490
|
-
* const
|
|
491
|
-
*
|
|
492
|
-
*
|
|
493
|
-
*
|
|
494
|
-
*
|
|
420
|
+
*
|
|
421
|
+
* const register = new ActionRegister<AppActions>({
|
|
422
|
+
* name: 'AppRegister',
|
|
423
|
+
* logLevel: LogLevel.DEBUG
|
|
424
|
+
* });
|
|
425
|
+
*
|
|
426
|
+
* // 핸들러 등록
|
|
427
|
+
* register.register('updateUser', ({ id, name }, controller) => {
|
|
428
|
+
* userStore.setValue({ id, name });
|
|
495
429
|
* controller.next();
|
|
496
430
|
* }, { priority: 10 });
|
|
497
|
-
*
|
|
498
|
-
*
|
|
499
|
-
*
|
|
500
|
-
* controller.next();
|
|
501
|
-
* });
|
|
502
|
-
*
|
|
503
|
-
* // Dispatch actions with type safety
|
|
504
|
-
* await actionRegister.dispatch('increment');
|
|
505
|
-
* await actionRegister.dispatch('setCount', 42);
|
|
431
|
+
*
|
|
432
|
+
* // 액션 디스패치
|
|
433
|
+
* await register.dispatch('updateUser', { id: '1', name: 'John' });
|
|
506
434
|
* ```
|
|
507
435
|
*/
|
|
508
436
|
var ActionRegister = class {
|
|
509
437
|
constructor(config = {}) {
|
|
510
438
|
(0, import_defineProperty.default)(this, "pipelines", /* @__PURE__ */ new Map());
|
|
511
439
|
(0, import_defineProperty.default)(this, "handlerCounter", 0);
|
|
512
|
-
(0, import_defineProperty.default)(this, "logger", void 0);
|
|
513
|
-
(0, import_defineProperty.default)(this, "events", new SimpleEventEmitter());
|
|
514
|
-
(0, import_defineProperty.default)(this, "config", void 0);
|
|
515
440
|
(0, import_defineProperty.default)(this, "actionGuard", void 0);
|
|
516
441
|
(0, import_defineProperty.default)(this, "executionMode", "sequential");
|
|
517
442
|
(0, import_defineProperty.default)(this, "actionExecutionModes", /* @__PURE__ */ new Map());
|
|
518
|
-
(0, import_defineProperty.default)(
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
const startTime = Date.now();
|
|
530
|
-
this.logger.trace(`Starting dispatch for action '${String(action)}'`, {
|
|
531
|
-
action,
|
|
532
|
-
payload,
|
|
533
|
-
startTime
|
|
534
|
-
});
|
|
535
|
-
this.events.emit("action:start", {
|
|
536
|
-
action,
|
|
537
|
-
payload
|
|
538
|
-
});
|
|
539
|
-
this.logger.trace(`Emitted 'action:start' event`);
|
|
540
|
-
this.logger.debug(`Dispatching action '${String(action)}'`, { payload });
|
|
541
|
-
const pipeline = this.pipelines.get(action);
|
|
542
|
-
if (!pipeline || pipeline.length === 0) {
|
|
543
|
-
this.logger.warn(`No handlers registered for action '${String(action)}'`);
|
|
544
|
-
this.logger.trace(`Dispatch completed early - no handlers`);
|
|
545
|
-
return;
|
|
546
|
-
}
|
|
547
|
-
this.logger.trace(`Found ${pipeline.length} handlers for action '${String(action)}'`, { handlerIds: pipeline.map((reg) => reg.id) });
|
|
548
|
-
const currentExecutionMode = this.actionExecutionModes.get(action) || this.executionMode;
|
|
549
|
-
const context = {
|
|
550
|
-
action: String(action),
|
|
551
|
-
payload,
|
|
552
|
-
handlers: [...pipeline],
|
|
553
|
-
aborted: false,
|
|
554
|
-
abortReason: void 0,
|
|
555
|
-
currentIndex: 0,
|
|
556
|
-
jumpToPriority: void 0,
|
|
557
|
-
executionMode: currentExecutionMode
|
|
558
|
-
};
|
|
559
|
-
try {
|
|
560
|
-
await this.executePipeline(context);
|
|
561
|
-
const metrics = {
|
|
562
|
-
action: String(action),
|
|
563
|
-
executionTime: Date.now() - startTime,
|
|
564
|
-
handlerCount: context.handlers.length,
|
|
565
|
-
success: !context.aborted,
|
|
566
|
-
timestamp: Date.now()
|
|
567
|
-
};
|
|
568
|
-
if (context.aborted) {
|
|
569
|
-
metrics.error = context.abortReason;
|
|
570
|
-
this.events.emit("action:abort", {
|
|
571
|
-
action,
|
|
572
|
-
payload,
|
|
573
|
-
reason: context.abortReason
|
|
574
|
-
});
|
|
575
|
-
} else this.events.emit("action:complete", {
|
|
576
|
-
action,
|
|
577
|
-
payload,
|
|
578
|
-
metrics
|
|
579
|
-
});
|
|
580
|
-
this.logger.debug(`Completed action '${String(action)}'`, metrics);
|
|
581
|
-
} catch (error) {
|
|
582
|
-
const metrics = {
|
|
583
|
-
action: String(action),
|
|
584
|
-
executionTime: Date.now() - startTime,
|
|
585
|
-
handlerCount: context.handlers.length,
|
|
586
|
-
success: false,
|
|
587
|
-
error: error.message || "Unknown error",
|
|
588
|
-
timestamp: Date.now()
|
|
589
|
-
};
|
|
590
|
-
this.logger.error(`Error executing action '${String(action)}'`, metrics);
|
|
591
|
-
this.events.emit("action:error", {
|
|
592
|
-
action,
|
|
593
|
-
payload,
|
|
594
|
-
error: error instanceof Error ? error : new Error(String(error))
|
|
595
|
-
});
|
|
596
|
-
throw error;
|
|
597
|
-
}
|
|
598
|
-
}
|
|
599
|
-
);
|
|
600
|
-
this.config = {
|
|
601
|
-
logger: config.logger || (0, __context_action_logger.createLogger)(config.logLevel),
|
|
602
|
-
logLevel: config.logLevel ?? 3,
|
|
603
|
-
name: config.name || (0, __context_action_logger.getLoggerNameFromEnv)(),
|
|
604
|
-
debug: config.debug ?? (0, __context_action_logger.getDebugFromEnv)(),
|
|
605
|
-
defaultExecutionMode: config.defaultExecutionMode ?? "sequential"
|
|
606
|
-
};
|
|
607
|
-
this.logger = this.config.logger;
|
|
608
|
-
this.actionGuard = new ActionGuard(this.logger);
|
|
609
|
-
this.executionMode = this.config.defaultExecutionMode;
|
|
610
|
-
this.logger.trace(`${this.config.name} constructor called`, { config });
|
|
611
|
-
if (this.config.debug) this.logger.info(`${this.config.name} initialized`, {
|
|
612
|
-
logLevel: this.config.logLevel,
|
|
613
|
-
debug: this.config.debug,
|
|
614
|
-
defaultExecutionMode: this.executionMode
|
|
443
|
+
(0, import_defineProperty.default)(this, "name", void 0);
|
|
444
|
+
(0, import_defineProperty.default)(this, "registryConfig", void 0);
|
|
445
|
+
(0, import_defineProperty.default)(this, "executionStats", /* @__PURE__ */ new Map());
|
|
446
|
+
this.name = config.name || "ActionRegister";
|
|
447
|
+
this.registryConfig = config.registry;
|
|
448
|
+
this.actionGuard = new ActionGuard();
|
|
449
|
+
if (this.registryConfig?.defaultExecutionMode) this.executionMode = this.registryConfig.defaultExecutionMode;
|
|
450
|
+
if (this.registryConfig?.debug && process.env.NODE_ENV === "development") console.log(`🎯 ActionRegister created: ${this.name}`, {
|
|
451
|
+
defaultExecutionMode: this.executionMode,
|
|
452
|
+
maxHandlers: this.registryConfig.maxHandlers,
|
|
453
|
+
autoCleanup: this.registryConfig.autoCleanup ?? true
|
|
615
454
|
});
|
|
616
|
-
this.logger.trace(`${this.config.name} constructor completed`);
|
|
617
455
|
}
|
|
618
|
-
/**
|
|
619
|
-
* Register action handler with pipeline
|
|
620
|
-
* @implements action-handler
|
|
621
|
-
*
|
|
622
|
-
* Register a handler for an action in the pipeline
|
|
623
|
-
* @param action - The action name to handle
|
|
624
|
-
* @param handler - The handler function to execute
|
|
625
|
-
* @param config - Optional configuration for the handler
|
|
626
|
-
* @returns Unregister function to remove the handler
|
|
627
|
-
*
|
|
628
|
-
* @example
|
|
629
|
-
* ```typescript
|
|
630
|
-
* const unregister = actionRegister.register('updateUser',
|
|
631
|
-
* async (payload, controller) => {
|
|
632
|
-
* // Validate payload
|
|
633
|
-
* if (!payload.id) {
|
|
634
|
-
* controller.abort('User ID is required');
|
|
635
|
-
* return;
|
|
636
|
-
* }
|
|
637
|
-
*
|
|
638
|
-
* // Process update
|
|
639
|
-
* await updateUserInStore(payload);
|
|
640
|
-
* controller.next();
|
|
641
|
-
* },
|
|
642
|
-
* { priority: 10, blocking: true }
|
|
643
|
-
* );
|
|
644
|
-
*
|
|
645
|
-
* // Later, remove the handler
|
|
646
|
-
* unregister();
|
|
647
|
-
* ```
|
|
648
|
-
*/
|
|
649
456
|
register(action, handler, config = {}) {
|
|
650
|
-
|
|
651
|
-
const handlerId = config.id || `handler_${++this.handlerCounter}`;
|
|
652
|
-
this.logger.trace(`Generated handler ID: ${handlerId}`);
|
|
457
|
+
const handlerId = config.id || `handler_${++this.handlerCounter}_${Math.random().toString(36).substr(2, 5)}`;
|
|
653
458
|
const registration = {
|
|
654
459
|
handler,
|
|
655
460
|
config: {
|
|
@@ -658,227 +463,605 @@ var ActionRegister = class {
|
|
|
658
463
|
blocking: config.blocking ?? false,
|
|
659
464
|
once: config.once ?? false,
|
|
660
465
|
condition: config.condition || (() => true),
|
|
661
|
-
debounce: config.debounce,
|
|
662
|
-
throttle: config.throttle,
|
|
663
|
-
validation: config.validation,
|
|
664
|
-
middleware: config.middleware ?? false
|
|
466
|
+
debounce: config.debounce ?? void 0,
|
|
467
|
+
throttle: config.throttle ?? void 0,
|
|
468
|
+
validation: config.validation ?? void 0,
|
|
469
|
+
middleware: config.middleware ?? false,
|
|
470
|
+
tags: config.tags ?? [],
|
|
471
|
+
category: config.category ?? void 0,
|
|
472
|
+
description: config.description ?? void 0,
|
|
473
|
+
version: config.version ?? void 0,
|
|
474
|
+
returnType: config.returnType ?? "value",
|
|
475
|
+
timeout: config.timeout ?? void 0,
|
|
476
|
+
retries: config.retries ?? 0,
|
|
477
|
+
dependencies: config.dependencies ?? [],
|
|
478
|
+
conflicts: config.conflicts ?? [],
|
|
479
|
+
environment: config.environment ?? void 0,
|
|
480
|
+
feature: config.feature ?? void 0,
|
|
481
|
+
metrics: config.metrics ?? {
|
|
482
|
+
collectTiming: false,
|
|
483
|
+
collectErrors: false,
|
|
484
|
+
customMetrics: {}
|
|
485
|
+
},
|
|
486
|
+
metadata: config.metadata ?? {}
|
|
665
487
|
},
|
|
666
488
|
id: handlerId
|
|
667
489
|
};
|
|
668
|
-
this.
|
|
669
|
-
id: handlerId,
|
|
670
|
-
config: registration.config
|
|
671
|
-
} });
|
|
672
|
-
if (!this.pipelines.has(action)) {
|
|
673
|
-
this.logger.trace(`Creating new pipeline for action: ${String(action)}`);
|
|
674
|
-
this.pipelines.set(action, []);
|
|
675
|
-
this.logger.debug(`Created pipeline for action: ${String(action)}`);
|
|
676
|
-
}
|
|
490
|
+
if (!this.pipelines.has(action)) this.pipelines.set(action, []);
|
|
677
491
|
const pipeline = this.pipelines.get(action);
|
|
678
|
-
|
|
679
|
-
if (
|
|
680
|
-
|
|
681
|
-
this.logger.trace(`Duplicate handler registration aborted`);
|
|
682
|
-
return () => {};
|
|
683
|
-
}
|
|
492
|
+
const existingIndex = pipeline.findIndex((reg) => reg.id === handlerId);
|
|
493
|
+
if (existingIndex !== -1) return () => {};
|
|
494
|
+
if (this.registryConfig?.maxHandlers && pipeline.length >= this.registryConfig.maxHandlers) throw new Error(`Maximum number of handlers (${this.registryConfig.maxHandlers}) reached for action '${String(action)}' in registry '${this.name}'`);
|
|
684
495
|
pipeline.push(registration);
|
|
685
|
-
this.logger.trace(`Added handler to pipeline, current length: ${pipeline.length}`);
|
|
686
496
|
pipeline.sort((a, b) => b.config.priority - a.config.priority);
|
|
687
|
-
this.
|
|
688
|
-
id: reg.id,
|
|
689
|
-
priority: reg.config.priority
|
|
690
|
-
})) });
|
|
691
|
-
this.logger.debug(`Registered handler for action '${String(action)}'`, {
|
|
497
|
+
if (this.registryConfig?.debug && process.env.NODE_ENV === "development") console.log(`🎯 Handler registered: ${String(action)}`, {
|
|
692
498
|
handlerId,
|
|
693
|
-
priority:
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
action,
|
|
699
|
-
handlerId,
|
|
700
|
-
config: registration.config
|
|
499
|
+
priority: config.priority,
|
|
500
|
+
tags: config.tags,
|
|
501
|
+
category: config.category,
|
|
502
|
+
totalHandlers: pipeline.length,
|
|
503
|
+
registry: this.name
|
|
701
504
|
});
|
|
702
505
|
return () => {
|
|
703
|
-
|
|
704
|
-
const index = pipeline.findIndex((reg) => reg.id === handlerId);
|
|
506
|
+
const index = pipeline.findIndex((reg) => reg.id === handlerId && reg === registration);
|
|
705
507
|
if (index !== -1) {
|
|
706
508
|
pipeline.splice(index, 1);
|
|
707
|
-
this.
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
handlerId
|
|
509
|
+
if (this.registryConfig?.debug && process.env.NODE_ENV === "development") console.log(`🎯 Handler unregistered: ${String(action)}`, {
|
|
510
|
+
handlerId,
|
|
511
|
+
remainingHandlers: pipeline.length,
|
|
512
|
+
registry: this.name
|
|
712
513
|
});
|
|
713
|
-
}
|
|
514
|
+
}
|
|
714
515
|
};
|
|
715
516
|
}
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
517
|
+
async dispatch(action, payload, options) {
|
|
518
|
+
let autoAbortController;
|
|
519
|
+
let effectiveSignal = options?.signal;
|
|
520
|
+
if (options?.autoAbort?.enabled) {
|
|
521
|
+
autoAbortController = new AbortController();
|
|
522
|
+
effectiveSignal = autoAbortController.signal;
|
|
523
|
+
if (options.autoAbort.onControllerCreated) options.autoAbort.onControllerCreated(autoAbortController);
|
|
524
|
+
if (options?.signal) {
|
|
525
|
+
const originalSignal = options.signal;
|
|
526
|
+
if (originalSignal.aborted) autoAbortController.abort();
|
|
527
|
+
else {
|
|
528
|
+
const abortHandler$1 = () => autoAbortController.abort();
|
|
529
|
+
originalSignal.addEventListener("abort", abortHandler$1, { once: true });
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
if (effectiveSignal?.aborted) return;
|
|
534
|
+
const pipeline = this.pipelines.get(action);
|
|
535
|
+
if (!pipeline || pipeline.length === 0) return;
|
|
536
|
+
const filteredHandlers = this.filterHandlers([...pipeline], options?.filter);
|
|
537
|
+
const actionKey = String(action);
|
|
538
|
+
let throttleMs;
|
|
539
|
+
let debounceMs;
|
|
540
|
+
if (options?.throttle !== void 0) throttleMs = options.throttle;
|
|
541
|
+
else if (filteredHandlers.length > 0) {
|
|
542
|
+
for (const handler of filteredHandlers) if (handler.config.throttle !== void 0) {
|
|
543
|
+
throttleMs = handler.config.throttle;
|
|
544
|
+
break;
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
if (options?.debounce !== void 0) debounceMs = options.debounce;
|
|
548
|
+
else if (filteredHandlers.length > 0) {
|
|
549
|
+
for (const handler of filteredHandlers) if (handler.config.debounce !== void 0) {
|
|
550
|
+
debounceMs = handler.config.debounce;
|
|
551
|
+
break;
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
if (debounceMs !== void 0) {
|
|
555
|
+
const shouldProceed = await this.actionGuard.debounce(actionKey, debounceMs);
|
|
556
|
+
if (!shouldProceed) return;
|
|
557
|
+
}
|
|
558
|
+
if (throttleMs !== void 0) {
|
|
559
|
+
const shouldProceed = this.actionGuard.throttle(actionKey, throttleMs);
|
|
560
|
+
if (!shouldProceed) return;
|
|
561
|
+
}
|
|
562
|
+
const currentExecutionMode = options?.executionMode || this.actionExecutionModes.get(action) || this.executionMode;
|
|
563
|
+
const context = {
|
|
564
|
+
action: String(action),
|
|
565
|
+
payload,
|
|
566
|
+
handlers: filteredHandlers,
|
|
567
|
+
aborted: false,
|
|
568
|
+
abortReason: void 0,
|
|
569
|
+
currentIndex: 0,
|
|
570
|
+
jumpToPriority: void 0,
|
|
571
|
+
executionMode: currentExecutionMode,
|
|
572
|
+
results: [],
|
|
573
|
+
terminated: false,
|
|
574
|
+
terminationResult: void 0
|
|
575
|
+
};
|
|
576
|
+
const startTime = Date.now();
|
|
577
|
+
let executionSuccess = true;
|
|
578
|
+
const abortHandler = effectiveSignal ? () => {
|
|
579
|
+
context.aborted = true;
|
|
580
|
+
context.abortReason = "Action dispatch aborted by signal";
|
|
581
|
+
} : void 0;
|
|
582
|
+
if (effectiveSignal && abortHandler) effectiveSignal.addEventListener("abort", abortHandler);
|
|
583
|
+
try {
|
|
584
|
+
await this.executePipeline(context, autoAbortController, options?.autoAbort);
|
|
585
|
+
} catch (error) {
|
|
586
|
+
executionSuccess = false;
|
|
587
|
+
throw error;
|
|
588
|
+
} finally {
|
|
589
|
+
if (effectiveSignal && abortHandler) effectiveSignal.removeEventListener("abort", abortHandler);
|
|
590
|
+
const duration = Date.now() - startTime;
|
|
591
|
+
this.updateExecutionStats(action, executionSuccess, duration);
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
async dispatchWithResult(action, payload, options) {
|
|
595
|
+
const startTime = Date.now();
|
|
596
|
+
let autoAbortController;
|
|
597
|
+
let effectiveSignal = options?.signal;
|
|
598
|
+
if (options?.autoAbort?.enabled) {
|
|
599
|
+
autoAbortController = new AbortController();
|
|
600
|
+
effectiveSignal = autoAbortController.signal;
|
|
601
|
+
if (options.autoAbort.onControllerCreated) options.autoAbort.onControllerCreated(autoAbortController);
|
|
602
|
+
if (options?.signal) {
|
|
603
|
+
const originalSignal = options.signal;
|
|
604
|
+
if (originalSignal.aborted) autoAbortController.abort();
|
|
605
|
+
else {
|
|
606
|
+
const abortHandler$1 = () => autoAbortController.abort();
|
|
607
|
+
originalSignal.addEventListener("abort", abortHandler$1, { once: true });
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
if (effectiveSignal?.aborted) return {
|
|
612
|
+
success: false,
|
|
613
|
+
aborted: true,
|
|
614
|
+
abortReason: "Action dispatch aborted by signal",
|
|
615
|
+
terminated: false,
|
|
616
|
+
result: void 0,
|
|
617
|
+
results: [],
|
|
618
|
+
execution: {
|
|
619
|
+
duration: 0,
|
|
620
|
+
handlersExecuted: 0,
|
|
621
|
+
handlersSkipped: 0,
|
|
622
|
+
handlersFailed: 0,
|
|
623
|
+
startTime,
|
|
624
|
+
endTime: startTime
|
|
625
|
+
},
|
|
626
|
+
handlers: [],
|
|
627
|
+
errors: []
|
|
628
|
+
};
|
|
629
|
+
const pipeline = this.pipelines.get(action);
|
|
630
|
+
if (!pipeline || pipeline.length === 0) return {
|
|
631
|
+
success: true,
|
|
632
|
+
aborted: false,
|
|
633
|
+
terminated: false,
|
|
634
|
+
result: void 0,
|
|
635
|
+
results: [],
|
|
636
|
+
execution: {
|
|
637
|
+
duration: 0,
|
|
638
|
+
handlersExecuted: 0,
|
|
639
|
+
handlersSkipped: 0,
|
|
640
|
+
handlersFailed: 0,
|
|
641
|
+
startTime,
|
|
642
|
+
endTime: startTime
|
|
643
|
+
},
|
|
644
|
+
handlers: [],
|
|
645
|
+
errors: []
|
|
646
|
+
};
|
|
647
|
+
const filteredHandlers = this.filterHandlers([...pipeline], options?.filter);
|
|
648
|
+
const actionKey = String(action);
|
|
649
|
+
let throttleMs;
|
|
650
|
+
let debounceMs;
|
|
651
|
+
if (options?.throttle !== void 0) throttleMs = options.throttle;
|
|
652
|
+
else if (filteredHandlers.length > 0) {
|
|
653
|
+
for (const handler of filteredHandlers) if (handler.config.throttle !== void 0) {
|
|
654
|
+
throttleMs = handler.config.throttle;
|
|
655
|
+
break;
|
|
656
|
+
}
|
|
657
|
+
}
|
|
658
|
+
if (options?.debounce !== void 0) debounceMs = options.debounce;
|
|
659
|
+
else if (filteredHandlers.length > 0) {
|
|
660
|
+
for (const handler of filteredHandlers) if (handler.config.debounce !== void 0) {
|
|
661
|
+
debounceMs = handler.config.debounce;
|
|
662
|
+
break;
|
|
663
|
+
}
|
|
664
|
+
}
|
|
665
|
+
if (debounceMs !== void 0) {
|
|
666
|
+
const shouldProceed = await this.actionGuard.debounce(actionKey, debounceMs);
|
|
667
|
+
if (!shouldProceed) return {
|
|
668
|
+
success: false,
|
|
669
|
+
aborted: true,
|
|
670
|
+
abortReason: "Debounced execution",
|
|
671
|
+
terminated: false,
|
|
672
|
+
result: void 0,
|
|
673
|
+
results: [],
|
|
674
|
+
execution: {
|
|
675
|
+
duration: Date.now() - startTime,
|
|
676
|
+
handlersExecuted: 0,
|
|
677
|
+
handlersSkipped: pipeline.length,
|
|
678
|
+
handlersFailed: 0,
|
|
679
|
+
startTime,
|
|
680
|
+
endTime: Date.now()
|
|
681
|
+
},
|
|
682
|
+
handlers: [],
|
|
683
|
+
errors: []
|
|
684
|
+
};
|
|
685
|
+
}
|
|
686
|
+
if (throttleMs !== void 0) {
|
|
687
|
+
const shouldProceed = this.actionGuard.throttle(actionKey, throttleMs);
|
|
688
|
+
if (!shouldProceed) return {
|
|
689
|
+
success: false,
|
|
690
|
+
aborted: true,
|
|
691
|
+
abortReason: "Throttled execution",
|
|
692
|
+
terminated: false,
|
|
693
|
+
result: void 0,
|
|
694
|
+
results: [],
|
|
695
|
+
execution: {
|
|
696
|
+
duration: Date.now() - startTime,
|
|
697
|
+
handlersExecuted: 0,
|
|
698
|
+
handlersSkipped: pipeline.length,
|
|
699
|
+
handlersFailed: 0,
|
|
700
|
+
startTime,
|
|
701
|
+
endTime: Date.now()
|
|
702
|
+
},
|
|
703
|
+
handlers: [],
|
|
704
|
+
errors: []
|
|
705
|
+
};
|
|
706
|
+
}
|
|
707
|
+
const currentExecutionMode = options?.executionMode || this.actionExecutionModes.get(action) || this.executionMode;
|
|
708
|
+
const context = {
|
|
709
|
+
action: String(action),
|
|
710
|
+
payload,
|
|
711
|
+
handlers: filteredHandlers,
|
|
712
|
+
aborted: false,
|
|
713
|
+
abortReason: void 0,
|
|
714
|
+
currentIndex: 0,
|
|
715
|
+
jumpToPriority: void 0,
|
|
716
|
+
executionMode: currentExecutionMode,
|
|
717
|
+
results: [],
|
|
718
|
+
terminated: false,
|
|
719
|
+
terminationResult: void 0
|
|
720
|
+
};
|
|
721
|
+
let executionError;
|
|
722
|
+
const handlerResults = [];
|
|
723
|
+
const errors = [];
|
|
724
|
+
const abortHandler = effectiveSignal ? () => {
|
|
725
|
+
context.aborted = true;
|
|
726
|
+
context.abortReason = "Action dispatch aborted by signal";
|
|
727
|
+
} : void 0;
|
|
728
|
+
if (effectiveSignal && abortHandler) effectiveSignal.addEventListener("abort", abortHandler);
|
|
729
|
+
try {
|
|
730
|
+
await this.executePipeline(context, autoAbortController, options?.autoAbort);
|
|
731
|
+
} catch (error) {
|
|
732
|
+
executionError = error instanceof Error ? error : new Error(String(error));
|
|
733
|
+
errors.push({
|
|
734
|
+
handlerId: "pipeline",
|
|
735
|
+
error: executionError,
|
|
736
|
+
timestamp: Date.now()
|
|
737
|
+
});
|
|
738
|
+
} finally {
|
|
739
|
+
if (effectiveSignal && abortHandler) effectiveSignal.removeEventListener("abort", abortHandler);
|
|
740
|
+
}
|
|
741
|
+
const endTime = Date.now();
|
|
742
|
+
const executionSuccess = !executionError && !context.aborted;
|
|
743
|
+
this.updateExecutionStats(action, executionSuccess, endTime - startTime);
|
|
744
|
+
const processedResult = this.processResults(context, options?.result);
|
|
745
|
+
const executionResult = {
|
|
746
|
+
success: !executionError && !context.aborted,
|
|
747
|
+
aborted: context.aborted,
|
|
748
|
+
abortReason: context.abortReason,
|
|
749
|
+
terminated: context.terminated,
|
|
750
|
+
result: processedResult,
|
|
751
|
+
results: context.results,
|
|
752
|
+
execution: {
|
|
753
|
+
duration: endTime - startTime,
|
|
754
|
+
handlersExecuted: context.currentIndex + (context.aborted ? 0 : 1),
|
|
755
|
+
handlersSkipped: Math.max(0, filteredHandlers.length - (context.currentIndex + 1)),
|
|
756
|
+
handlersFailed: errors.length,
|
|
757
|
+
startTime,
|
|
758
|
+
endTime
|
|
759
|
+
},
|
|
760
|
+
handlers: handlerResults,
|
|
761
|
+
errors
|
|
762
|
+
};
|
|
763
|
+
/** Clean up one-time handlers after execution */
|
|
764
|
+
this.cleanupOneTimeHandlers(action, context.handlers);
|
|
765
|
+
return executionResult;
|
|
766
|
+
}
|
|
767
|
+
filterHandlers(handlers, filterOptions) {
|
|
768
|
+
if (!filterOptions) return handlers;
|
|
769
|
+
return handlers.filter((registration) => {
|
|
770
|
+
const config = registration.config;
|
|
771
|
+
if (filterOptions.tags && filterOptions.tags.length > 0) {
|
|
772
|
+
const hasMatchingTag = filterOptions.tags.some((tag) => config.tags.includes(tag));
|
|
773
|
+
if (!hasMatchingTag) return false;
|
|
774
|
+
}
|
|
775
|
+
if (filterOptions.category && config.category !== filterOptions.category) return false;
|
|
776
|
+
if (filterOptions.handlerIds && filterOptions.handlerIds.length > 0) {
|
|
777
|
+
if (!filterOptions.handlerIds.includes(config.id)) return false;
|
|
778
|
+
}
|
|
779
|
+
if (filterOptions.environment && config.environment !== filterOptions.environment) return false;
|
|
780
|
+
if (filterOptions.feature && config.feature !== filterOptions.feature) return false;
|
|
781
|
+
if (filterOptions.excludeTags && filterOptions.excludeTags.length > 0) {
|
|
782
|
+
const hasExcludedTag = filterOptions.excludeTags.some((tag) => config.tags.includes(tag));
|
|
783
|
+
if (hasExcludedTag) return false;
|
|
784
|
+
}
|
|
785
|
+
if (filterOptions.excludeCategory && config.category === filterOptions.excludeCategory) return false;
|
|
786
|
+
if (filterOptions.excludeHandlerIds && filterOptions.excludeHandlerIds.length > 0) {
|
|
787
|
+
if (filterOptions.excludeHandlerIds.includes(config.id)) return false;
|
|
788
|
+
}
|
|
789
|
+
if (filterOptions.custom && !filterOptions.custom(config)) return false;
|
|
790
|
+
return true;
|
|
726
791
|
});
|
|
727
|
-
|
|
792
|
+
}
|
|
793
|
+
processResults(context, resultOptions) {
|
|
794
|
+
if (!resultOptions || !resultOptions.collect) return void 0;
|
|
795
|
+
const results = context.results;
|
|
796
|
+
if (context.terminated && context.terminationResult !== void 0) return context.terminationResult;
|
|
797
|
+
const limitedResults = resultOptions.maxResults ? results.slice(0, resultOptions.maxResults) : results;
|
|
798
|
+
if (limitedResults.length === 0) return void 0;
|
|
799
|
+
switch (resultOptions.strategy) {
|
|
800
|
+
case "first": return limitedResults[0];
|
|
801
|
+
case "last": return limitedResults[limitedResults.length - 1];
|
|
802
|
+
case "all": return limitedResults;
|
|
803
|
+
case "merge":
|
|
804
|
+
if (resultOptions.merger) return resultOptions.merger(limitedResults);
|
|
805
|
+
return limitedResults[limitedResults.length - 1];
|
|
806
|
+
case "custom":
|
|
807
|
+
if (resultOptions.merger) return resultOptions.merger(limitedResults);
|
|
808
|
+
throw new Error("Custom result strategy requires a merger function");
|
|
809
|
+
default: return limitedResults;
|
|
810
|
+
}
|
|
811
|
+
}
|
|
812
|
+
async executePipeline(context, autoAbortController, autoAbortOptions) {
|
|
813
|
+
const createController = (_registration, _index) => {
|
|
728
814
|
return {
|
|
729
815
|
next: () => {},
|
|
730
816
|
abort: (reason) => {
|
|
731
|
-
this.logger.trace(`Handler '${registration.id}' is aborting pipeline`, { reason });
|
|
732
817
|
context.aborted = true;
|
|
733
818
|
context.abortReason = reason;
|
|
734
|
-
|
|
819
|
+
if (autoAbortController && autoAbortOptions?.allowHandlerAbort) autoAbortController.abort(reason);
|
|
735
820
|
},
|
|
736
821
|
modifyPayload: (modifier) => {
|
|
737
|
-
this.logger.trace(`Handler '${registration.id}' is modifying payload`);
|
|
738
|
-
const oldPayload = context.payload;
|
|
739
822
|
context.payload = modifier(context.payload);
|
|
740
|
-
this.logger.debug(`Payload modified by handler '${registration.id}'`);
|
|
741
|
-
this.logger.trace(`Payload change`, {
|
|
742
|
-
oldPayload,
|
|
743
|
-
newPayload: context.payload
|
|
744
|
-
});
|
|
745
823
|
},
|
|
746
824
|
getPayload: () => context.payload,
|
|
747
825
|
jumpToPriority: (priority) => {
|
|
748
|
-
this.logger.trace(`Handler '${registration.id}' jumping to priority ${priority}`);
|
|
749
826
|
context.jumpToPriority = priority;
|
|
827
|
+
},
|
|
828
|
+
return: (result) => {
|
|
829
|
+
context.terminated = true;
|
|
830
|
+
context.terminationResult = result;
|
|
831
|
+
},
|
|
832
|
+
setResult: (result) => {
|
|
833
|
+
context.results.push(result);
|
|
834
|
+
},
|
|
835
|
+
getResults: () => {
|
|
836
|
+
return [...context.results];
|
|
837
|
+
},
|
|
838
|
+
mergeResult: (merger) => {
|
|
839
|
+
const currentResult = context.results[context.results.length - 1];
|
|
840
|
+
const previousResults = context.results.slice(0, -1);
|
|
841
|
+
const mergedResult = merger(previousResults, currentResult);
|
|
842
|
+
context.results[context.results.length - 1] = mergedResult;
|
|
750
843
|
}
|
|
751
844
|
};
|
|
752
845
|
};
|
|
753
846
|
switch (context.executionMode) {
|
|
754
847
|
case "sequential":
|
|
755
|
-
await executeSequential(context, createController
|
|
848
|
+
await executeSequential(context, createController);
|
|
756
849
|
break;
|
|
757
850
|
case "parallel":
|
|
758
|
-
await executeParallel(context, createController
|
|
851
|
+
await executeParallel(context, createController);
|
|
759
852
|
break;
|
|
760
853
|
case "race":
|
|
761
|
-
await executeRace(context, createController
|
|
854
|
+
await executeRace(context, createController);
|
|
762
855
|
break;
|
|
763
856
|
default: throw new Error(`Unknown execution mode: ${context.executionMode}`);
|
|
764
857
|
}
|
|
765
858
|
this.cleanupOneTimeHandlers(context.action, context.handlers);
|
|
766
859
|
}
|
|
767
|
-
/**
|
|
768
|
-
* Clean up one-time handlers after pipeline execution
|
|
769
|
-
* @internal
|
|
770
|
-
*/
|
|
771
860
|
cleanupOneTimeHandlers(action, executedHandlers) {
|
|
772
861
|
const pipeline = this.pipelines.get(action);
|
|
773
862
|
if (!pipeline) return;
|
|
774
863
|
const oneTimeHandlers = executedHandlers.filter((reg) => reg.config.once);
|
|
775
864
|
if (oneTimeHandlers.length === 0) return;
|
|
776
|
-
this.logger.trace(`Cleaning up ${oneTimeHandlers.length} one-time handlers`);
|
|
777
865
|
oneTimeHandlers.forEach((registration) => {
|
|
778
866
|
const index = pipeline.findIndex((reg) => reg.id === registration.id);
|
|
779
867
|
if (index !== -1) {
|
|
780
868
|
pipeline.splice(index, 1);
|
|
781
|
-
this.
|
|
869
|
+
if (this.registryConfig?.debug && process.env.NODE_ENV === "development") console.log(`🎯 One-time handler removed: ${String(action)}`, {
|
|
870
|
+
handlerId: registration.id,
|
|
871
|
+
remainingHandlers: pipeline.length,
|
|
872
|
+
registry: this.name
|
|
873
|
+
});
|
|
782
874
|
}
|
|
783
875
|
});
|
|
784
|
-
this.logger.trace(`Pipeline now has ${pipeline.length} handlers`);
|
|
785
876
|
}
|
|
786
877
|
/**
|
|
787
|
-
*
|
|
788
|
-
*
|
|
789
|
-
* @
|
|
878
|
+
* Update execution statistics for an action
|
|
879
|
+
*
|
|
880
|
+
* @param action Action name
|
|
881
|
+
* @param success Whether execution was successful
|
|
882
|
+
* @param duration Execution duration in milliseconds
|
|
790
883
|
*/
|
|
884
|
+
updateExecutionStats(action, success, duration) {
|
|
885
|
+
if (!this.executionStats.has(action)) this.executionStats.set(action, {
|
|
886
|
+
totalExecutions: 0,
|
|
887
|
+
totalDuration: 0,
|
|
888
|
+
successCount: 0,
|
|
889
|
+
errorCount: 0
|
|
890
|
+
});
|
|
891
|
+
const stats = this.executionStats.get(action);
|
|
892
|
+
stats.totalExecutions++;
|
|
893
|
+
stats.totalDuration += duration;
|
|
894
|
+
if (success) stats.successCount++;
|
|
895
|
+
else stats.errorCount++;
|
|
896
|
+
}
|
|
791
897
|
getHandlerCount(action) {
|
|
792
898
|
const pipeline = this.pipelines.get(action);
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
899
|
+
return pipeline ? pipeline.length : 0;
|
|
900
|
+
}
|
|
901
|
+
hasHandlers(action) {
|
|
902
|
+
return this.getHandlerCount(action) > 0;
|
|
903
|
+
}
|
|
904
|
+
getRegisteredActions() {
|
|
905
|
+
return Array.from(this.pipelines.keys());
|
|
906
|
+
}
|
|
907
|
+
clearAction(action) {
|
|
908
|
+
this.pipelines.delete(action);
|
|
909
|
+
}
|
|
910
|
+
clearAll() {
|
|
911
|
+
this.pipelines.clear();
|
|
912
|
+
}
|
|
913
|
+
getName() {
|
|
914
|
+
return this.name;
|
|
796
915
|
}
|
|
797
916
|
/**
|
|
798
|
-
*
|
|
799
|
-
*
|
|
800
|
-
* @returns
|
|
917
|
+
* Get comprehensive registry information (similar to DeclarativeStoreRegistry pattern)
|
|
918
|
+
*
|
|
919
|
+
* @returns Registry information including actions, handlers, and execution modes
|
|
801
920
|
*/
|
|
802
|
-
|
|
803
|
-
const
|
|
804
|
-
|
|
805
|
-
|
|
921
|
+
getRegistryInfo() {
|
|
922
|
+
const totalHandlers = Array.from(this.pipelines.values()).reduce((total, pipeline) => total + pipeline.length, 0);
|
|
923
|
+
return {
|
|
924
|
+
name: this.name,
|
|
925
|
+
totalActions: this.pipelines.size,
|
|
926
|
+
totalHandlers,
|
|
927
|
+
registeredActions: Array.from(this.pipelines.keys()),
|
|
928
|
+
actionExecutionModes: new Map(this.actionExecutionModes),
|
|
929
|
+
defaultExecutionMode: this.executionMode
|
|
930
|
+
};
|
|
806
931
|
}
|
|
807
932
|
/**
|
|
808
|
-
* Get
|
|
809
|
-
*
|
|
933
|
+
* Get detailed statistics for a specific action
|
|
934
|
+
*
|
|
935
|
+
* @param action Action name to get statistics for
|
|
936
|
+
* @returns Detailed handler statistics
|
|
810
937
|
*/
|
|
811
|
-
|
|
812
|
-
const
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
938
|
+
getActionStats(action) {
|
|
939
|
+
const pipeline = this.pipelines.get(action);
|
|
940
|
+
if (!pipeline) return null;
|
|
941
|
+
const priorityMap = /* @__PURE__ */ new Map();
|
|
942
|
+
pipeline.forEach((handler) => {
|
|
943
|
+
if (!priorityMap.has(handler.config.priority)) priorityMap.set(handler.config.priority, []);
|
|
944
|
+
priorityMap.get(handler.config.priority).push(handler);
|
|
816
945
|
});
|
|
817
|
-
|
|
946
|
+
const handlersByPriority = Array.from(priorityMap.entries()).sort(([a], [b]) => b - a).map(([priority, handlers]) => ({
|
|
947
|
+
priority,
|
|
948
|
+
handlers: handlers.map((h) => ({
|
|
949
|
+
id: h.config.id,
|
|
950
|
+
tags: h.config.tags,
|
|
951
|
+
category: h.config.category,
|
|
952
|
+
description: h.config.description,
|
|
953
|
+
version: h.config.version
|
|
954
|
+
}))
|
|
955
|
+
}));
|
|
956
|
+
const stats = this.executionStats.get(action);
|
|
957
|
+
const executionStats = stats ? {
|
|
958
|
+
totalExecutions: stats.totalExecutions,
|
|
959
|
+
averageDuration: stats.totalExecutions > 0 ? stats.totalDuration / stats.totalExecutions : 0,
|
|
960
|
+
successRate: stats.totalExecutions > 0 ? stats.successCount / stats.totalExecutions * 100 : 0,
|
|
961
|
+
errorCount: stats.errorCount
|
|
962
|
+
} : void 0;
|
|
963
|
+
return {
|
|
964
|
+
action,
|
|
965
|
+
handlerCount: pipeline.length,
|
|
966
|
+
handlersByPriority,
|
|
967
|
+
executionStats
|
|
968
|
+
};
|
|
818
969
|
}
|
|
819
970
|
/**
|
|
820
|
-
*
|
|
821
|
-
*
|
|
971
|
+
* Get statistics for all registered actions
|
|
972
|
+
*
|
|
973
|
+
* @returns Array of statistics for all actions
|
|
822
974
|
*/
|
|
823
|
-
|
|
824
|
-
this.
|
|
825
|
-
const pipeline = this.pipelines.get(action);
|
|
826
|
-
if (pipeline) {
|
|
827
|
-
const handlerCount = pipeline.length;
|
|
828
|
-
this.pipelines.delete(action);
|
|
829
|
-
this.logger.debug(`Cleared ${handlerCount} handlers for action '${String(action)}'`);
|
|
830
|
-
this.logger.trace(`Action '${String(action)}' pipeline removed`);
|
|
831
|
-
} else this.logger.trace(`No pipeline found for action '${String(action)}' to clear`);
|
|
975
|
+
getAllActionStats() {
|
|
976
|
+
return Array.from(this.pipelines.keys()).map((action) => this.getActionStats(action)).filter((stats) => stats !== null);
|
|
832
977
|
}
|
|
833
978
|
/**
|
|
834
|
-
*
|
|
979
|
+
* Get handlers by tag across all actions
|
|
980
|
+
*
|
|
981
|
+
* @param tag Tag to filter handlers by
|
|
982
|
+
* @returns Map of actions to handlers with the specified tag
|
|
835
983
|
*/
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
const
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
984
|
+
getHandlersByTag(tag) {
|
|
985
|
+
const result = /* @__PURE__ */ new Map();
|
|
986
|
+
for (const [action, pipeline] of this.pipelines.entries()) {
|
|
987
|
+
const matchingHandlers = pipeline.filter((handler) => handler.config.tags.includes(tag));
|
|
988
|
+
if (matchingHandlers.length > 0) result.set(action, matchingHandlers);
|
|
989
|
+
}
|
|
990
|
+
return result;
|
|
991
|
+
}
|
|
992
|
+
/**
|
|
993
|
+
* Get handlers by category across all actions
|
|
994
|
+
*
|
|
995
|
+
* @param category Category to filter handlers by
|
|
996
|
+
* @returns Map of actions to handlers with the specified category
|
|
997
|
+
*/
|
|
998
|
+
getHandlersByCategory(category) {
|
|
999
|
+
const result = /* @__PURE__ */ new Map();
|
|
1000
|
+
for (const [action, pipeline] of this.pipelines.entries()) {
|
|
1001
|
+
const matchingHandlers = pipeline.filter((handler) => handler.config.category === category);
|
|
1002
|
+
if (matchingHandlers.length > 0) result.set(action, matchingHandlers);
|
|
1003
|
+
}
|
|
1004
|
+
return result;
|
|
851
1005
|
}
|
|
852
1006
|
/**
|
|
853
|
-
*
|
|
854
|
-
*
|
|
855
|
-
* @param
|
|
856
|
-
* @
|
|
1007
|
+
* Set execution mode for a specific action
|
|
1008
|
+
*
|
|
1009
|
+
* @param action Action name
|
|
1010
|
+
* @param mode Execution mode to set
|
|
857
1011
|
*/
|
|
858
|
-
|
|
859
|
-
|
|
1012
|
+
setActionExecutionMode(action, mode) {
|
|
1013
|
+
this.actionExecutionModes.set(action, mode);
|
|
1014
|
+
if (this.registryConfig?.debug && process.env.NODE_ENV === "development") console.log(`🎯 Execution mode set for action '${String(action)}': ${mode}`);
|
|
860
1015
|
}
|
|
861
1016
|
/**
|
|
862
|
-
*
|
|
863
|
-
*
|
|
864
|
-
* @param
|
|
1017
|
+
* Get execution mode for a specific action
|
|
1018
|
+
*
|
|
1019
|
+
* @param action Action name
|
|
1020
|
+
* @returns Execution mode for the action, or default if not set
|
|
865
1021
|
*/
|
|
866
|
-
|
|
867
|
-
this.
|
|
1022
|
+
getActionExecutionMode(action) {
|
|
1023
|
+
return this.actionExecutionModes.get(action) || this.executionMode;
|
|
868
1024
|
}
|
|
869
1025
|
/**
|
|
870
|
-
*
|
|
871
|
-
*
|
|
1026
|
+
* Remove execution mode override for a specific action
|
|
1027
|
+
*
|
|
1028
|
+
* @param action Action name
|
|
872
1029
|
*/
|
|
873
|
-
|
|
874
|
-
|
|
1030
|
+
removeActionExecutionMode(action) {
|
|
1031
|
+
this.actionExecutionModes.delete(action);
|
|
1032
|
+
if (this.registryConfig?.debug && process.env.NODE_ENV === "development") console.log(`🎯 Execution mode reset for action '${String(action)}' to default: ${this.executionMode}`);
|
|
875
1033
|
}
|
|
876
1034
|
/**
|
|
877
|
-
*
|
|
878
|
-
|
|
1035
|
+
* Clear execution statistics for all actions
|
|
1036
|
+
*/
|
|
1037
|
+
clearExecutionStats() {
|
|
1038
|
+
this.executionStats.clear();
|
|
1039
|
+
if (this.registryConfig?.debug && process.env.NODE_ENV === "development") console.log(`🎯 Execution statistics cleared for registry: ${this.name}`);
|
|
1040
|
+
}
|
|
1041
|
+
/**
|
|
1042
|
+
* Clear execution statistics for a specific action
|
|
1043
|
+
*
|
|
1044
|
+
* @param action Action name
|
|
1045
|
+
*/
|
|
1046
|
+
clearActionExecutionStats(action) {
|
|
1047
|
+
this.executionStats.delete(action);
|
|
1048
|
+
if (this.registryConfig?.debug && process.env.NODE_ENV === "development") console.log(`🎯 Execution statistics cleared for action: ${String(action)}`);
|
|
1049
|
+
}
|
|
1050
|
+
/**
|
|
1051
|
+
* Get registry configuration (for debugging and inspection)
|
|
1052
|
+
*
|
|
1053
|
+
* @returns Current registry configuration
|
|
1054
|
+
*/
|
|
1055
|
+
getRegistryConfig() {
|
|
1056
|
+
return this.registryConfig;
|
|
1057
|
+
}
|
|
1058
|
+
/**
|
|
1059
|
+
* Check if registry has debug mode enabled
|
|
1060
|
+
*
|
|
1061
|
+
* @returns Whether debug mode is enabled
|
|
879
1062
|
*/
|
|
880
|
-
|
|
881
|
-
return this.
|
|
1063
|
+
isDebugEnabled() {
|
|
1064
|
+
return Boolean(this.registryConfig?.debug && process.env.NODE_ENV === "development");
|
|
882
1065
|
}
|
|
883
1066
|
};
|
|
884
1067
|
|