@context-action/core 0.0.4 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/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, 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
- }
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
- logger.debug(`Skipping handler '${registration.id}' - condition not met`);
40
+ i++;
82
41
  continue;
83
42
  }
43
+ /** Check validation if provided */
84
44
  if (registration.config.validation && !registration.config.validation(context.payload)) {
85
- logger.debug(`Skipping handler '${registration.id}' - validation failed`);
45
+ i++;
86
46
  continue;
87
47
  }
88
48
  const controller = createController(registration, i);
89
49
  try {
90
- logger.trace(`Executing handler ${i + 1}/${context.handlers.length}`, {
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
- logger.trace(`Waiting for blocking handler '${registration.id}'`);
97
- await result;
98
- }
99
- logger.trace(`Handler '${registration.id}' completed`);
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, logger) {
111
- logger.trace("Executing in parallel mode", { handlerCount: context.handlers.length });
86
+ async function executeParallel(context, createController) {
87
+ /** Filter handlers that should run */
112
88
  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
- }
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
- logger.trace(`Running ${runnableHandlers.length} handlers in parallel`);
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
- if (result instanceof Promise) await result;
130
- logger.trace(`Parallel handler '${registration.id}' completed`);
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
- logger.trace("Parallel execution completed", {
158
- successful: results.filter((r) => r.status === "fulfilled").length,
159
- failed: results.filter((r) => r.status === "rejected").length
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, logger) {
167
- logger.trace("Executing in race mode", { handlerCount: context.handlers.length });
145
+ async function executeRace(context, createController) {
146
+ /** Filter handlers that should run */
168
147
  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
- }
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
- logger.trace("No runnable handlers for race mode");
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
- if (result instanceof Promise) await result;
190
- logger.trace(`Race handler '${registration.id}' completed`);
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,22 +176,22 @@ async function executeRace(context, createController, logger) {
203
176
  };
204
177
  }
205
178
  });
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;
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
 
219
192
  //#endregion
220
- //#region ../../node_modules/.pnpm/@oxc-project+runtime@0.77.3/node_modules/@oxc-project/runtime/src/helpers/typeof.js
221
- var require_typeof = __commonJS({ "../../node_modules/.pnpm/@oxc-project+runtime@0.77.3/node_modules/@oxc-project/runtime/src/helpers/typeof.js"(exports, module) {
193
+ //#region ../../node_modules/.pnpm/@oxc-project+runtime@0.81.0/node_modules/@oxc-project/runtime/src/helpers/typeof.js
194
+ var require_typeof = /* @__PURE__ */ __commonJS({ "../../node_modules/.pnpm/@oxc-project+runtime@0.81.0/node_modules/@oxc-project/runtime/src/helpers/typeof.js": ((exports, module) => {
222
195
  function _typeof$2(o) {
223
196
  "@babel/helpers - typeof";
224
197
  return module.exports = _typeof$2 = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function(o$1) {
@@ -228,11 +201,11 @@ var require_typeof = __commonJS({ "../../node_modules/.pnpm/@oxc-project+runtime
228
201
  }, module.exports.__esModule = true, module.exports["default"] = module.exports, _typeof$2(o);
229
202
  }
230
203
  module.exports = _typeof$2, module.exports.__esModule = true, module.exports["default"] = module.exports;
231
- } });
204
+ }) });
232
205
 
233
206
  //#endregion
234
- //#region ../../node_modules/.pnpm/@oxc-project+runtime@0.77.3/node_modules/@oxc-project/runtime/src/helpers/toPrimitive.js
235
- var require_toPrimitive = __commonJS({ "../../node_modules/.pnpm/@oxc-project+runtime@0.77.3/node_modules/@oxc-project/runtime/src/helpers/toPrimitive.js"(exports, module) {
207
+ //#region ../../node_modules/.pnpm/@oxc-project+runtime@0.81.0/node_modules/@oxc-project/runtime/src/helpers/toPrimitive.js
208
+ var require_toPrimitive = /* @__PURE__ */ __commonJS({ "../../node_modules/.pnpm/@oxc-project+runtime@0.81.0/node_modules/@oxc-project/runtime/src/helpers/toPrimitive.js": ((exports, module) => {
236
209
  var _typeof$1 = require_typeof()["default"];
237
210
  function toPrimitive$1(t, r) {
238
211
  if ("object" != _typeof$1(t) || !t) return t;
@@ -245,11 +218,11 @@ var require_toPrimitive = __commonJS({ "../../node_modules/.pnpm/@oxc-project+ru
245
218
  return ("string" === r ? String : Number)(t);
246
219
  }
247
220
  module.exports = toPrimitive$1, module.exports.__esModule = true, module.exports["default"] = module.exports;
248
- } });
221
+ }) });
249
222
 
250
223
  //#endregion
251
- //#region ../../node_modules/.pnpm/@oxc-project+runtime@0.77.3/node_modules/@oxc-project/runtime/src/helpers/toPropertyKey.js
252
- var require_toPropertyKey = __commonJS({ "../../node_modules/.pnpm/@oxc-project+runtime@0.77.3/node_modules/@oxc-project/runtime/src/helpers/toPropertyKey.js"(exports, module) {
224
+ //#region ../../node_modules/.pnpm/@oxc-project+runtime@0.81.0/node_modules/@oxc-project/runtime/src/helpers/toPropertyKey.js
225
+ var require_toPropertyKey = /* @__PURE__ */ __commonJS({ "../../node_modules/.pnpm/@oxc-project+runtime@0.81.0/node_modules/@oxc-project/runtime/src/helpers/toPropertyKey.js": ((exports, module) => {
253
226
  var _typeof = require_typeof()["default"];
254
227
  var toPrimitive = require_toPrimitive();
255
228
  function toPropertyKey$1(t) {
@@ -257,11 +230,11 @@ var require_toPropertyKey = __commonJS({ "../../node_modules/.pnpm/@oxc-project+
257
230
  return "symbol" == _typeof(i) ? i : i + "";
258
231
  }
259
232
  module.exports = toPropertyKey$1, module.exports.__esModule = true, module.exports["default"] = module.exports;
260
- } });
233
+ }) });
261
234
 
262
235
  //#endregion
263
- //#region ../../node_modules/.pnpm/@oxc-project+runtime@0.77.3/node_modules/@oxc-project/runtime/src/helpers/defineProperty.js
264
- var require_defineProperty = __commonJS({ "../../node_modules/.pnpm/@oxc-project+runtime@0.77.3/node_modules/@oxc-project/runtime/src/helpers/defineProperty.js"(exports, module) {
236
+ //#region ../../node_modules/.pnpm/@oxc-project+runtime@0.81.0/node_modules/@oxc-project/runtime/src/helpers/defineProperty.js
237
+ var require_defineProperty = /* @__PURE__ */ __commonJS({ "../../node_modules/.pnpm/@oxc-project+runtime@0.81.0/node_modules/@oxc-project/runtime/src/helpers/defineProperty.js": ((exports, module) => {
265
238
  var toPropertyKey = require_toPropertyKey();
266
239
  function _defineProperty$2(e, r, t) {
267
240
  return (r = toPropertyKey(r)) in e ? Object.defineProperty(e, r, {
@@ -272,29 +245,23 @@ var require_defineProperty = __commonJS({ "../../node_modules/.pnpm/@oxc-project
272
245
  }) : e[r] = t, e;
273
246
  }
274
247
  module.exports = _defineProperty$2, module.exports.__esModule = true, module.exports["default"] = module.exports;
275
- } });
248
+ }) });
276
249
 
277
250
  //#endregion
278
251
  //#region src/action-guard.ts
279
- var import_defineProperty$1 = __toESM(require_defineProperty(), 1);
252
+ var import_defineProperty$1 = /* @__PURE__ */ __toESM(require_defineProperty(), 1);
280
253
  /**
281
254
  * Action Guard system for managing action execution timing
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
- * 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
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(logger) {
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
- this.logger.trace(`Checking debounce for '${actionKey}'`, { debounceMs });
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
- if (state.debounceTimer) {
337
- clearTimeout(state.debounceTimer);
338
- this.logger.trace(`Cleared existing debounce timer for '${actionKey}'`);
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
- this.logger.trace(`Debounce completed for '${actionKey}'`);
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
- this.logger.trace(`Checking throttle for '${actionKey}'`, { throttleMs });
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
- if (state.isThrottled) {
378
- this.logger.trace(`Action '${actionKey}' is already throttled`);
379
- return false;
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
- this.logger.trace("Clearing all action guards");
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
@@ -434,222 +403,58 @@ var ActionGuard = class {
434
403
 
435
404
  //#endregion
436
405
  //#region src/ActionRegister.ts
437
- var import_defineProperty = __toESM(require_defineProperty(), 1);
438
- /**
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
- };
406
+ var import_defineProperty = /* @__PURE__ */ __toESM(require_defineProperty(), 1);
473
407
  /**
474
- * Central action registration and dispatch system
475
- * @implements action-pipeline-system
476
- * @implements actionregister
477
- * @memberof core-concepts
408
+ * 중앙화된 액션 등록 디스패치 시스템으로, 타입 안전한 액션 파이프라인 관리를 제공하는 핵심 클래스입니다.
478
409
  *
479
- * Core action pipeline management system with type-safe action dispatch
480
- * @template T - Action payload map defining available actions and their payload types
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 actionRegister = new ActionRegister<AppActions>();
491
- *
492
- * // Register handlers with priority and configuration
493
- * actionRegister.register('increment', (_, controller) => {
494
- * console.log('Incremented');
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
- * 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);
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
- 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 || (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
- 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}`);
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.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
- }
490
+ if (!this.pipelines.has(action)) this.pipelines.set(action, []);
677
491
  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
- }
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.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)}'`, {
497
+ if (this.registryConfig?.debug && process.env.NODE_ENV === "development") console.log(`🎯 Handler registered: ${String(action)}`, {
692
498
  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
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
- this.logger.trace(`Unregistering handler '${handlerId}' from action '${String(action)}'`);
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.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
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
- } else this.logger.trace(`Handler '${handlerId}' not found in pipeline for unregistration`);
514
+ }
714
515
  };
715
516
  }
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
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
- const createController = (registration, _index) => {
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
- this.logger.warn(`Pipeline aborted by handler '${registration.id}'`, { reason });
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, this.logger);
848
+ await executeSequential(context, createController);
756
849
  break;
757
850
  case "parallel":
758
- await executeParallel(context, createController, this.logger);
851
+ await executeParallel(context, createController);
759
852
  break;
760
853
  case "race":
761
- await executeRace(context, createController, this.logger);
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.logger.debug(`Removed one-time handler '${registration.id}'`);
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
- * Get the number of handlers registered for an action
788
- * @param action - The action to check
789
- * @returns Number of handlers registered
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
- const count = pipeline ? pipeline.length : 0;
794
- this.logger.trace(`Handler count for '${String(action)}': ${count}`);
795
- return count;
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
- * Check if any handlers are registered for an action
799
- * @param action - The action to check
800
- * @returns True if handlers are registered
917
+ * Get comprehensive registry information (similar to DeclarativeStoreRegistry pattern)
918
+ *
919
+ * @returns Registry information including actions, handlers, and execution modes
801
920
  */
802
- hasHandlers(action) {
803
- const hasHandlers = this.getHandlerCount(action) > 0;
804
- this.logger.trace(`Has handlers for '${String(action)}': ${hasHandlers}`);
805
- return hasHandlers;
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 all registered action names
809
- * @returns Array of action names
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
- getRegisteredActions() {
812
- const actions = Array.from(this.pipelines.keys());
813
- this.logger.trace(`Registered actions`, {
814
- actions,
815
- count: actions.length
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
- return actions;
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
- * Clear all handlers for a specific action
821
- * @param action - The action to clear
971
+ * Get statistics for all registered actions
972
+ *
973
+ * @returns Array of statistics for all actions
822
974
  */
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`);
975
+ getAllActionStats() {
976
+ return Array.from(this.pipelines.keys()).map((action) => this.getActionStats(action)).filter((stats) => stats !== null);
832
977
  }
833
978
  /**
834
- * Clear all handlers for all actions
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
- 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`);
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
- * 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
1007
+ * Set execution mode for a specific action
1008
+ *
1009
+ * @param action Action name
1010
+ * @param mode Execution mode to set
857
1011
  */
858
- on(event, handler) {
859
- return this.events.on(event, handler);
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
- * Remove event listener
863
- * @param event - Event name
864
- * @param handler - Event handler to remove
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
- off(event, handler) {
867
- this.events.off(event, handler);
1022
+ getActionExecutionMode(action) {
1023
+ return this.actionExecutionModes.get(action) || this.executionMode;
868
1024
  }
869
1025
  /**
870
- * Get current configuration
871
- * @returns Current ActionRegister configuration
1026
+ * Remove execution mode override for a specific action
1027
+ *
1028
+ * @param action Action name
872
1029
  */
873
- getConfig() {
874
- return { ...this.config };
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
- * Get logger instance
878
- * @returns Current logger instance
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
- getLogger() {
881
- return this.logger;
1063
+ isDebugEnabled() {
1064
+ return Boolean(this.registryConfig?.debug && process.env.NODE_ENV === "development");
882
1065
  }
883
1066
  };
884
1067