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