@context-action/core 0.0.3 → 0.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -25,6 +25,171 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
25
25
 
26
26
  //#endregion
27
27
 
28
+ //#region src/execution-modes.ts
29
+ /**
30
+ * Execute handlers in sequential mode (one after another)
31
+ */
32
+ async function executeSequential(context, createController) {
33
+ let i = 0;
34
+ while (i < context.handlers.length) {
35
+ if (context.aborted || context.terminated) break;
36
+ const registration = context.handlers[i];
37
+ context.currentIndex = i;
38
+ /** Check condition if provided */
39
+ if (registration.config.condition && !registration.config.condition()) {
40
+ i++;
41
+ continue;
42
+ }
43
+ /** Check validation if provided */
44
+ if (registration.config.validation && !registration.config.validation(context.payload)) {
45
+ i++;
46
+ continue;
47
+ }
48
+ const controller = createController(registration, i);
49
+ try {
50
+ if (context.aborted) break;
51
+ const result = registration.handler(context.payload, controller);
52
+ /** Wait for async handlers if they're blocking */
53
+ if (registration.config.blocking && result instanceof Promise) {
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++;
77
+ } catch (error) {
78
+ if (registration.config.blocking) throw error;
79
+ i++;
80
+ }
81
+ }
82
+ }
83
+ /**
84
+ * Execute handlers in parallel mode (all at once)
85
+ */
86
+ async function executeParallel(context, createController) {
87
+ /** Filter handlers that should run */
88
+ const runnableHandlers = context.handlers.filter((registration, _index) => {
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;
93
+ return true;
94
+ });
95
+ /** Create promises for all handlers */
96
+ const handlerPromises = runnableHandlers.map(async (registration, _index) => {
97
+ const controller = createController(registration, _index);
98
+ try {
99
+ const result = registration.handler(context.payload, controller);
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);
105
+ return {
106
+ success: true,
107
+ handlerId: registration.id,
108
+ result: handlerResult,
109
+ terminated: context.terminated
110
+ };
111
+ } catch (error) {
112
+ if (registration.config.blocking) throw error;
113
+ return {
114
+ success: false,
115
+ handlerId: registration.id,
116
+ error
117
+ };
118
+ }
119
+ });
120
+ /** Wait for all handlers to complete */
121
+ const results = await Promise.allSettled(handlerPromises);
122
+ /** Check for any rejected blocking handlers */
123
+ const failures = results.filter((result, index) => {
124
+ if (result.status === "rejected") {
125
+ const registration = runnableHandlers[index];
126
+ return registration.config.blocking;
127
+ }
128
+ return false;
129
+ });
130
+ if (failures.length > 0) {
131
+ const firstFailure = failures[0];
132
+ throw firstFailure.reason;
133
+ }
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
+ }
141
+ }
142
+ /**
143
+ * Execute handlers in race mode (first to complete wins)
144
+ */
145
+ async function executeRace(context, createController) {
146
+ /** Filter handlers that should run */
147
+ const runnableHandlers = context.handlers.filter((registration, _index) => {
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;
152
+ return true;
153
+ });
154
+ if (runnableHandlers.length === 0) return;
155
+ /** Create promises for all handlers */
156
+ const handlerPromises = runnableHandlers.map(async (registration, _index) => {
157
+ const controller = createController(registration, _index);
158
+ try {
159
+ const result = registration.handler(context.payload, controller);
160
+ let handlerResult;
161
+ if (result instanceof Promise) handlerResult = await result;
162
+ else handlerResult = result;
163
+ return {
164
+ success: true,
165
+ handlerId: registration.id,
166
+ registration,
167
+ result: handlerResult,
168
+ terminated: context.terminated
169
+ };
170
+ } catch (error) {
171
+ return {
172
+ success: false,
173
+ handlerId: registration.id,
174
+ error,
175
+ registration
176
+ };
177
+ }
178
+ });
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;
189
+ }
190
+ }
191
+
192
+ //#endregion
28
193
  //#region ../../node_modules/.pnpm/@oxc-project+runtime@0.77.3/node_modules/@oxc-project/runtime/src/helpers/typeof.js
29
194
  var require_typeof = __commonJS({ "../../node_modules/.pnpm/@oxc-project+runtime@0.77.3/node_modules/@oxc-project/runtime/src/helpers/typeof.js"(exports, module) {
30
195
  function _typeof$2(o) {
@@ -83,173 +248,156 @@ var require_defineProperty = __commonJS({ "../../node_modules/.pnpm/@oxc-project
83
248
  } });
84
249
 
85
250
  //#endregion
86
- //#region src/logger.ts
251
+ //#region src/action-guard.ts
87
252
  var import_defineProperty$1 = __toESM(require_defineProperty(), 1);
88
253
  /**
89
- * Log levels in order of severity (lowest to highest)
90
- */
91
- let LogLevel = /* @__PURE__ */ function(LogLevel$1) {
92
- LogLevel$1[LogLevel$1["TRACE"] = 0] = "TRACE";
93
- LogLevel$1[LogLevel$1["DEBUG"] = 1] = "DEBUG";
94
- LogLevel$1[LogLevel$1["INFO"] = 2] = "INFO";
95
- LogLevel$1[LogLevel$1["WARN"] = 3] = "WARN";
96
- LogLevel$1[LogLevel$1["ERROR"] = 4] = "ERROR";
97
- LogLevel$1[LogLevel$1["FATAL"] = 5] = "FATAL";
98
- return LogLevel$1;
99
- }({});
100
- /**
101
- * Default console logger implementation
102
- */
103
- var ConsoleLogger = class {
104
- constructor(level = LogLevel.ERROR) {
105
- this.level = level;
106
- }
107
- shouldLog(level) {
108
- return level >= this.level;
109
- }
110
- formatMessage(level, message) {
111
- return `[${level.toUpperCase()}] ${message}`;
112
- }
113
- trace(message, ...args) {
114
- if (this.shouldLog(LogLevel.TRACE)) console.trace(this.formatMessage("trace", message), ...args);
115
- }
116
- debug(message, ...args) {
117
- if (this.shouldLog(LogLevel.DEBUG)) console.debug(this.formatMessage("debug", message), ...args);
118
- }
119
- info(message, ...args) {
120
- if (this.shouldLog(LogLevel.INFO)) console.info(this.formatMessage("info", message), ...args);
121
- }
122
- warn(message, ...args) {
123
- if (this.shouldLog(LogLevel.WARN)) console.warn(this.formatMessage("warn", message), ...args);
124
- }
125
- error(message, ...args) {
126
- if (this.shouldLog(LogLevel.ERROR)) console.error(this.formatMessage("error", message), ...args);
127
- }
128
- fatal(message, ...args) {
129
- if (this.shouldLog(LogLevel.FATAL)) console.error(this.formatMessage("fatal", message), ...args);
130
- }
131
- setLevel(level) {
132
- this.level = level;
133
- }
134
- };
135
- /**
136
- * Parse log level from string
137
- */
138
- function parseLogLevel(level) {
139
- const upperLevel = level.toUpperCase();
140
- switch (upperLevel) {
141
- case "TRACE": return LogLevel.TRACE;
142
- case "DEBUG": return LogLevel.DEBUG;
143
- case "INFO": return LogLevel.INFO;
144
- case "WARN": return LogLevel.WARN;
145
- case "ERROR": return LogLevel.ERROR;
146
- case "FATAL": return LogLevel.FATAL;
147
- default: return LogLevel.ERROR;
148
- }
149
- }
150
- /**
151
- * Get log level from environment variable or default to ERROR
152
- */
153
- function getLogLevelFromEnv() {
154
- if (typeof process !== "undefined" && process.env) {
155
- const envLevel = process.env.LOG_LEVEL || process.env.ACTION_LOG_LEVEL;
156
- if (envLevel) return parseLogLevel(envLevel);
157
- }
158
- return LogLevel.TRACE;
159
- }
160
- /**
161
- * Extract trace ID from payload if it exists
162
- */
163
- function extractTraceIdFromPayload(payload) {
164
- if (payload && typeof payload === "object") return payload._traceId || payload.traceId || payload.trace_id;
165
- return void 0;
166
- }
167
- /**
168
- * Extract session ID from payload if it exists
169
- */
170
- function extractSessionIdFromPayload(payload) {
171
- if (payload && typeof payload === "object") return payload._sessionId || payload.sessionId || payload.session_id;
172
- return void 0;
173
- }
174
- /**
175
- * Create OTEL context from payload
176
- */
177
- function createOtelContextFromPayload(payload) {
178
- return {
179
- traceId: extractTraceIdFromPayload(payload),
180
- sessionId: extractSessionIdFromPayload(payload),
181
- metadata: payload
182
- };
183
- }
184
- /**
185
- * OpenTelemetry-aware console logger implementation
254
+ * Action Guard system for managing action execution timing
255
+ * @implements action-guard
256
+ * @implements performance-optimization
257
+ * @implements user-experience-optimization
258
+ * @implements class-naming
259
+ * @memberof core-concepts
260
+ * @internal
261
+ * @since 1.0.0
262
+ *
263
+ * Manages action execution timing through debouncing and throttling
264
+ * @implements performance-optimization
265
+ *
266
+ * @example
267
+ * ```typescript
268
+ * const guard = new ActionGuard(logger);
269
+ *
270
+ * // Debounce search input (wait 300ms after typing stops)
271
+ * if (await guard.debounce('search', 300)) {
272
+ * executeSearch();
273
+ * }
274
+ *
275
+ * // Throttle scroll handler (max once per 100ms)
276
+ * if (guard.throttle('scroll', 100)) {
277
+ * updateScrollPosition();
278
+ * }
279
+ * ```
186
280
  */
187
- var OtelConsoleLogger = class extends ConsoleLogger {
188
- constructor(level = LogLevel.ERROR) {
189
- super(level);
190
- (0, import_defineProperty$1.default)(this, "context", {});
191
- }
192
- setContext(context) {
193
- this.context = {
194
- ...this.context,
195
- ...context
196
- };
281
+ var ActionGuard = class {
282
+ constructor() {
283
+ (0, import_defineProperty$1.default)(this, "guards", /* @__PURE__ */ new Map());
197
284
  }
198
- getContext() {
199
- return { ...this.context };
200
- }
201
- clearContext() {
202
- this.context = {};
203
- }
204
- formatWithContext(level, message) {
205
- const contextParts = [];
206
- if (this.context.sessionId) contextParts.push(`session=${this.context.sessionId}`);
207
- if (this.context.traceId) contextParts.push(`trace=${this.context.traceId}`);
208
- if (this.context.spanId) contextParts.push(`span=${this.context.spanId}`);
209
- const contextStr = contextParts.length > 0 ? ` [${contextParts.join(", ")}]` : "";
210
- return `[${level.toUpperCase()}]${contextStr} ${message}`;
211
- }
212
- logWithContext(level, message, ...args) {
213
- const levelName = LogLevel[level].toLowerCase();
214
- const formattedMessage = this.formatWithContext(levelName, message);
215
- switch (level) {
216
- case LogLevel.TRACE:
217
- if (this.shouldLog(LogLevel.TRACE)) console.trace(formattedMessage, ...args);
218
- break;
219
- case LogLevel.DEBUG:
220
- if (this.shouldLog(LogLevel.DEBUG)) console.debug(formattedMessage, ...args);
221
- break;
222
- case LogLevel.INFO:
223
- if (this.shouldLog(LogLevel.INFO)) console.info(formattedMessage, ...args);
224
- break;
225
- case LogLevel.WARN:
226
- if (this.shouldLog(LogLevel.WARN)) console.warn(formattedMessage, ...args);
227
- break;
228
- case LogLevel.ERROR:
229
- if (this.shouldLog(LogLevel.ERROR)) console.error(formattedMessage, ...args);
230
- break;
231
- case LogLevel.FATAL:
232
- if (this.shouldLog(LogLevel.FATAL)) console.error(formattedMessage, ...args);
233
- break;
285
+ /**
286
+ * Check if action should be debounced
287
+ * @param actionKey - Unique key for the action
288
+ * @param debounceMs - Debounce delay in milliseconds
289
+ * @returns Promise that resolves when debounce period is complete
290
+ */
291
+ async debounce(actionKey, debounceMs) {
292
+ /** Get or create guard state for this action */
293
+ let state = this.guards.get(actionKey);
294
+ if (!state) {
295
+ /** Initialize new guard state with default values */
296
+ state = {
297
+ lastExecuted: 0,
298
+ isThrottled: false
299
+ };
300
+ this.guards.set(actionKey, state);
234
301
  }
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 */
307
+ return new Promise((resolve) => {
308
+ state.debounceTimer = setTimeout(() => {
309
+ /** Clean up timer reference to prevent memory leaks */
310
+ state.debounceTimer = void 0;
311
+ /** Update last execution timestamp for throttling calculations */
312
+ state.lastExecuted = Date.now();
313
+ resolve(true);
314
+ }, debounceMs);
315
+ });
235
316
  }
236
- trace(message, ...args) {
237
- this.logWithContext(LogLevel.TRACE, message, ...args);
238
- }
239
- debug(message, ...args) {
240
- this.logWithContext(LogLevel.DEBUG, message, ...args);
317
+ /**
318
+ * Check if action should be throttled
319
+ * @param actionKey - Unique key for the action
320
+ * @param throttleMs - Throttle delay in milliseconds
321
+ * @returns True if action should proceed, false if throttled
322
+ */
323
+ throttle(actionKey, throttleMs) {
324
+ /** Get or create guard state for this action */
325
+ let state = this.guards.get(actionKey);
326
+ if (!state) {
327
+ /** Initialize new guard state with default values */
328
+ state = {
329
+ lastExecuted: 0,
330
+ isThrottled: false
331
+ };
332
+ this.guards.set(actionKey, state);
333
+ }
334
+ const now = Date.now();
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 */
338
+ if (timeSinceLastExecution >= throttleMs) {
339
+ /** Update execution timestamp and clear throttled state */
340
+ state.lastExecuted = now;
341
+ state.isThrottled = false;
342
+ return true;
343
+ }
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 */
349
+ state.isThrottled = true;
350
+ const remainingTime = throttleMs - timeSinceLastExecution;
351
+ /** Create timer to reset throttled state when period expires */
352
+ state.throttleTimer = setTimeout(() => {
353
+ /** Clear throttled state and timer reference */
354
+ state.isThrottled = false;
355
+ state.throttleTimer = void 0;
356
+ }, remainingTime);
357
+ return false;
241
358
  }
242
- info(message, ...args) {
243
- this.logWithContext(LogLevel.INFO, message, ...args);
359
+ /**
360
+ * Clear all guards for an action
361
+ * @param actionKey - Action key to clear
362
+ */
363
+ clearGuards(actionKey) {
364
+ const state = this.guards.get(actionKey);
365
+ if (state) {
366
+ /** Clear debounce timer if active to prevent memory leaks */
367
+ if (state.debounceTimer) clearTimeout(state.debounceTimer);
368
+ /** Clear throttle timer if active to prevent memory leaks */
369
+ if (state.throttleTimer) clearTimeout(state.throttleTimer);
370
+ /** Remove guard state from memory */
371
+ this.guards.delete(actionKey);
372
+ }
244
373
  }
245
- warn(message, ...args) {
246
- this.logWithContext(LogLevel.WARN, message, ...args);
374
+ /**
375
+ * Clear all guards
376
+ */
377
+ clearAll() {
378
+ /** Iterate through all guard states and clear their timers */
379
+ /** This prevents memory leaks when clearing the entire guard system */
380
+ for (const [, state] of this.guards) {
381
+ /** Clear any active debounce timers */
382
+ if (state.debounceTimer) clearTimeout(state.debounceTimer);
383
+ /** Clear any active throttle timers */
384
+ if (state.throttleTimer) clearTimeout(state.throttleTimer);
385
+ }
386
+ /** Remove all guard states from memory */
387
+ this.guards.clear();
247
388
  }
248
- error(message, ...args) {
249
- this.logWithContext(LogLevel.ERROR, message, ...args);
389
+ /**
390
+ * Get current guard state for debugging
391
+ * @param actionKey - Action key to inspect
392
+ */
393
+ getGuardState(actionKey) {
394
+ return this.guards.get(actionKey);
250
395
  }
251
- fatal(message, ...args) {
252
- this.logWithContext(LogLevel.FATAL, message, ...args);
396
+ /**
397
+ * Get all active guards for debugging
398
+ */
399
+ getAllGuardStates() {
400
+ return new Map(this.guards);
253
401
  }
254
402
  };
255
403
 
@@ -257,172 +405,669 @@ var OtelConsoleLogger = class extends ConsoleLogger {
257
405
  //#region src/ActionRegister.ts
258
406
  var import_defineProperty = __toESM(require_defineProperty(), 1);
259
407
  /**
260
- * Core action pipeline management system
261
- * @template T - Action payload map defining available actions and their payload types
408
+ * 중앙화된 액션 등록 디스패치 시스템으로, 타입 안전한 액션 파이프라인 관리를 제공하는 핵심 클래스입니다.
409
+ *
410
+ * @implements {ActionRegister}
411
+ * @implements {Action Pipeline System}
412
+ * @memberof core-concepts
413
+ *
262
414
  * @example
263
415
  * ```typescript
264
416
  * interface AppActions extends ActionPayloadMap {
265
- * increment: void;
266
- * setCount: number;
417
+ * updateUser: { id: string; name: string };
418
+ * calculateTotal: void;
267
419
  * }
268
420
  *
269
- * const actionRegister = new ActionRegister<AppActions>();
421
+ * const register = new ActionRegister<AppActions>({
422
+ * name: 'AppRegister',
423
+ * logLevel: LogLevel.DEBUG
424
+ * });
270
425
  *
271
- * // Register handlers
272
- * actionRegister.register('increment', () => console.log('Incremented'));
273
- * actionRegister.register('setCount', (count) => console.log(`Count: ${count}`));
426
+ * // 핸들러 등록
427
+ * register.register('updateUser', ({ id, name }, controller) => {
428
+ * userStore.setValue({ id, name });
429
+ * controller.next();
430
+ * }, { priority: 10 });
274
431
  *
275
- * // Dispatch actions
276
- * await actionRegister.dispatch('increment');
277
- * await actionRegister.dispatch('setCount', 42);
432
+ * // 액션 디스패치
433
+ * await register.dispatch('updateUser', { id: '1', name: 'John' });
278
434
  * ```
279
435
  */
280
436
  var ActionRegister = class {
281
- constructor(config) {
437
+ constructor(config = {}) {
282
438
  (0, import_defineProperty.default)(this, "pipelines", /* @__PURE__ */ new Map());
283
- (0, import_defineProperty.default)(this, "atomSetters", /* @__PURE__ */ new Map());
284
439
  (0, import_defineProperty.default)(this, "handlerCounter", 0);
285
- (0, import_defineProperty.default)(this, "logger", void 0);
286
- const envLogLevel = getLogLevelFromEnv();
287
- const configLogLevel = config?.logLevel ?? envLogLevel;
288
- if (config?.logger) this.logger = config.logger;
289
- else if (config?.useOtel) this.logger = new OtelConsoleLogger(configLogLevel);
290
- else this.logger = new ConsoleLogger(configLogLevel);
291
- if (config?.otelContext && this.logger instanceof OtelConsoleLogger) this.logger.setContext(config.otelContext);
292
- this.logger.debug("ActionRegister initialized", {
293
- logLevel: configLogLevel,
294
- useOtel: config?.useOtel ?? false,
295
- hasOtelContext: !!config?.otelContext
440
+ (0, import_defineProperty.default)(this, "actionGuard", void 0);
441
+ (0, import_defineProperty.default)(this, "executionMode", "sequential");
442
+ (0, import_defineProperty.default)(this, "actionExecutionModes", /* @__PURE__ */ new Map());
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
296
454
  });
297
455
  }
298
- /**
299
- * Register a handler for an action in the pipeline
300
- * @param action - The action name to handle
301
- * @param handler - The handler function to execute
302
- * @param config - Optional configuration for the handler
303
- * @returns Unregister function to remove the handler
304
- * @example
305
- * ```typescript
306
- * const unregister = actionRegister.register('increment', () => {
307
- * console.log('Incremented!');
308
- * }, { priority: 10 });
309
- *
310
- * // Later, remove the handler
311
- * unregister();
312
- * ```
313
- */
314
456
  register(action, handler, config = {}) {
315
- if (!this.pipelines.has(action)) {
316
- this.pipelines.set(action, /* @__PURE__ */ new Map());
317
- this.logger.debug(`Created new pipeline for action: ${String(action)}`);
318
- }
319
- const pipeline = this.pipelines.get(action);
320
- const handlerId = config.id || `handler_${++this.handlerCounter}`;
321
- if (pipeline.has(handlerId)) {
322
- this.logger.warn(`Handler with id ${handlerId} already exists for action: ${String(action)}`);
323
- return () => {};
324
- }
325
- pipeline.set(handlerId, {
457
+ const handlerId = config.id || `handler_${++this.handlerCounter}_${Math.random().toString(36).substr(2, 5)}`;
458
+ const registration = {
326
459
  handler,
327
- config
328
- });
329
- this.logger.debug(`Registered handler for action: ${String(action)}`, {
460
+ config: {
461
+ priority: config.priority ?? 0,
462
+ id: handlerId,
463
+ blocking: config.blocking ?? false,
464
+ once: config.once ?? false,
465
+ condition: config.condition || (() => true),
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 ?? {}
487
+ },
488
+ id: handlerId
489
+ };
490
+ if (!this.pipelines.has(action)) this.pipelines.set(action, []);
491
+ const pipeline = this.pipelines.get(action);
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}'`);
495
+ pipeline.push(registration);
496
+ pipeline.sort((a, b) => b.config.priority - a.config.priority);
497
+ if (this.registryConfig?.debug && process.env.NODE_ENV === "development") console.log(`🎯 Handler registered: ${String(action)}`, {
330
498
  handlerId,
331
- priority: config.priority ?? 0,
332
- blocking: config.blocking ?? false
499
+ priority: config.priority,
500
+ tags: config.tags,
501
+ category: config.category,
502
+ totalHandlers: pipeline.length,
503
+ registry: this.name
333
504
  });
334
- this.sortPipeline(action);
335
505
  return () => {
336
- pipeline.delete(handlerId);
337
- this.logger.debug(`Unregistered handler: ${handlerId} for action: ${String(action)}`);
506
+ const index = pipeline.findIndex((reg) => reg.id === handlerId && reg === registration);
507
+ if (index !== -1) {
508
+ pipeline.splice(index, 1);
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
513
+ });
514
+ }
338
515
  };
339
516
  }
340
- registerAtomSetter(name, setter) {
341
- this.atomSetters.set(name, setter);
342
- this.logger.debug(`Registered atom setter: ${name}`);
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
+ }
343
593
  }
344
- /**
345
- * Internal dispatch implementation
346
- * @internal
347
- */
348
- async dispatch(action, payload) {
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
+ };
349
629
  const pipeline = this.pipelines.get(action);
350
- if (this.logger instanceof OtelConsoleLogger && payload) {
351
- const otelContext = createOtelContextFromPayload(payload);
352
- if (otelContext.traceId || otelContext.sessionId) this.logger.setContext(otelContext);
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
+ }
353
657
  }
354
- this.logger.debug(`Dispatching action: ${String(action)}`, { payload });
355
- if (!pipeline || pipeline.size === 0) {
356
- this.logger.warn(`No handlers registered for action: ${String(action)}`);
357
- return;
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
+ }
358
664
  }
359
- let modifiedPayload = payload;
360
- const handlers = Array.from(pipeline.values());
361
- let shouldContinue = true;
362
- this.logger.trace(`Executing pipeline for action: ${String(action)}`, { handlerCount: handlers.length });
363
- for (const { handler, config } of handlers) {
364
- if (!shouldContinue) break;
365
- const controller = {
366
- next: () => {
367
- shouldContinue = true;
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()
368
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;
791
+ });
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) => {
814
+ return {
815
+ next: () => {},
369
816
  abort: (reason) => {
370
- shouldContinue = false;
371
- this.logger.warn(`Pipeline aborted: ${reason}`);
817
+ context.aborted = true;
818
+ context.abortReason = reason;
819
+ if (autoAbortController && autoAbortOptions?.allowHandlerAbort) autoAbortController.abort(reason);
372
820
  },
373
821
  modifyPayload: (modifier) => {
374
- modifiedPayload = modifier(modifiedPayload);
375
- this.logger.trace(`Payload modified for action: ${String(action)}`);
822
+ context.payload = modifier(context.payload);
823
+ },
824
+ getPayload: () => context.payload,
825
+ jumpToPriority: (priority) => {
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;
376
843
  }
377
844
  };
378
- try {
379
- if (config.blocking) await handler(modifiedPayload, controller);
380
- else handler(modifiedPayload, controller);
381
- } catch (error) {
382
- this.logger.error(`Error in pipeline handler for action: ${String(action)}`, error);
383
- if (config.blocking) throw error;
384
- }
845
+ };
846
+ switch (context.executionMode) {
847
+ case "sequential":
848
+ await executeSequential(context, createController);
849
+ break;
850
+ case "parallel":
851
+ await executeParallel(context, createController);
852
+ break;
853
+ case "race":
854
+ await executeRace(context, createController);
855
+ break;
856
+ default: throw new Error(`Unknown execution mode: ${context.executionMode}`);
385
857
  }
386
- this.logger.debug(`Completed dispatching action: ${String(action)}`);
858
+ this.cleanupOneTimeHandlers(context.action, context.handlers);
387
859
  }
388
- sortPipeline(action) {
860
+ cleanupOneTimeHandlers(action, executedHandlers) {
389
861
  const pipeline = this.pipelines.get(action);
390
862
  if (!pipeline) return;
391
- const sorted = Array.from(pipeline.entries()).sort(([, a], [, b]) => {
392
- const priorityA = a.config.priority ?? 0;
393
- const priorityB = b.config.priority ?? 0;
394
- return priorityB - priorityA;
863
+ const oneTimeHandlers = executedHandlers.filter((reg) => reg.config.once);
864
+ if (oneTimeHandlers.length === 0) return;
865
+ oneTimeHandlers.forEach((registration) => {
866
+ const index = pipeline.findIndex((reg) => reg.id === registration.id);
867
+ if (index !== -1) {
868
+ pipeline.splice(index, 1);
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
+ });
874
+ }
875
+ });
876
+ }
877
+ /**
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
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
395
890
  });
396
- pipeline.clear();
397
- sorted.forEach(([id, data]) => pipeline.set(id, data));
398
- this.logger.trace(`Sorted pipeline for action: ${String(action)}`, {
399
- handlerCount: sorted.length,
400
- priorities: sorted.map(([, data]) => data.config.priority ?? 0)
891
+ const stats = this.executionStats.get(action);
892
+ stats.totalExecutions++;
893
+ stats.totalDuration += duration;
894
+ if (success) stats.successCount++;
895
+ else stats.errorCount++;
896
+ }
897
+ getHandlerCount(action) {
898
+ const pipeline = this.pipelines.get(action);
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;
915
+ }
916
+ /**
917
+ * Get comprehensive registry information (similar to DeclarativeStoreRegistry pattern)
918
+ *
919
+ * @returns Registry information including actions, handlers, and execution modes
920
+ */
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
+ };
931
+ }
932
+ /**
933
+ * Get detailed statistics for a specific action
934
+ *
935
+ * @param action Action name to get statistics for
936
+ * @returns Detailed handler statistics
937
+ */
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);
401
945
  });
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
+ };
969
+ }
970
+ /**
971
+ * Get statistics for all registered actions
972
+ *
973
+ * @returns Array of statistics for all actions
974
+ */
975
+ getAllActionStats() {
976
+ return Array.from(this.pipelines.keys()).map((action) => this.getActionStats(action)).filter((stats) => stats !== null);
977
+ }
978
+ /**
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
983
+ */
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;
1005
+ }
1006
+ /**
1007
+ * Set execution mode for a specific action
1008
+ *
1009
+ * @param action Action name
1010
+ * @param mode Execution mode to set
1011
+ */
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}`);
1015
+ }
1016
+ /**
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
1021
+ */
1022
+ getActionExecutionMode(action) {
1023
+ return this.actionExecutionModes.get(action) || this.executionMode;
1024
+ }
1025
+ /**
1026
+ * Remove execution mode override for a specific action
1027
+ *
1028
+ * @param action Action name
1029
+ */
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}`);
1033
+ }
1034
+ /**
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
1062
+ */
1063
+ isDebugEnabled() {
1064
+ return Boolean(this.registryConfig?.debug && process.env.NODE_ENV === "development");
402
1065
  }
403
1066
  };
404
1067
 
405
1068
  //#endregion
406
- //#region src/types.ts
407
- function createAction(type, payload) {
408
- return {
409
- type,
410
- payload
411
- };
412
- }
413
- function isAction(action, type) {
414
- return action?.type === type;
415
- }
416
-
417
- //#endregion
1069
+ exports.ActionGuard = ActionGuard;
418
1070
  exports.ActionRegister = ActionRegister;
419
- exports.ConsoleLogger = ConsoleLogger;
420
- exports.LogLevel = LogLevel;
421
- exports.OtelConsoleLogger = OtelConsoleLogger;
422
- exports.createAction = createAction;
423
- exports.createOtelContextFromPayload = createOtelContextFromPayload;
424
- exports.extractSessionIdFromPayload = extractSessionIdFromPayload;
425
- exports.extractTraceIdFromPayload = extractTraceIdFromPayload;
426
- exports.getLogLevelFromEnv = getLogLevelFromEnv;
427
- exports.isAction = isAction;
428
- exports.parseLogLevel = parseLogLevel;
1071
+ exports.executeParallel = executeParallel;
1072
+ exports.executeRace = executeRace;
1073
+ exports.executeSequential = executeSequential;