@context-action/core 0.2.2 → 0.3.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.js CHANGED
@@ -27,9 +27,42 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
27
27
  //#region src/execution-modes.ts
28
28
  /**
29
29
  * Execute handlers in sequential mode (one after another)
30
+ *
31
+ * Executes action handlers one at a time in priority order (highest first).
32
+ * Supports both blocking and non-blocking handlers, with proper abort and
33
+ * termination handling. Handlers can modify payload for subsequent handlers
34
+ * and jump to different priority levels.
35
+ *
36
+ * @template T - The payload type for the action
37
+ * @template R - The result type for handlers
38
+ *
39
+ * @param context - Pipeline execution context containing handlers and state
40
+ * @param createController - Factory function for creating pipeline controllers
41
+ *
42
+ * @throws {Error} When a blocking handler fails or validation errors occur
43
+ *
44
+ * @example
45
+ * ```typescript
46
+ * // This is called internally by ActionRegister.dispatch()
47
+ * // when executionMode is 'sequential'
48
+ *
49
+ * // Handlers execute in this order (by priority):
50
+ * // 1. Priority 100: Validation handler
51
+ * // 2. Priority 50: Business logic handler
52
+ * // 3. Priority 10: Logging handler
53
+ *
54
+ * await executeSequential(context, (registration, index) => ({
55
+ * abort: (reason) => { context.aborted = true; context.abortReason = reason },
56
+ * modifyPayload: (modifier) => { context.payload = modifier(context.payload) },
57
+ * // ... other controller methods
58
+ * }))
59
+ * ```
60
+ *
61
+ * @public
30
62
  */
31
63
  async function executeSequential(context, createController) {
32
64
  let i = 0;
65
+ const nonBlockingPromises = [];
33
66
  while (i < context.handlers.length) {
34
67
  if (context.aborted || context.terminated) break;
35
68
  const registration = context.handlers[i];
@@ -55,10 +88,15 @@ async function executeSequential(context, createController) {
55
88
  if (handlerResult !== void 0 && !context.terminated) context.results.push(handlerResult);
56
89
  } else if (result !== void 0 && !context.terminated)
57
90
  /** Collect synchronous result */
58
- if (result instanceof Promise) result.then((asyncResult) => {
59
- if (asyncResult !== void 0 && !context.terminated) context.results.push(asyncResult);
60
- }).catch(() => {});
61
- else context.results.push(result);
91
+ if (result instanceof Promise) {
92
+ const promiseWithHandling = result.then((asyncResult) => {
93
+ if (asyncResult !== void 0 && !context.terminated) context.results.push(asyncResult);
94
+ return asyncResult;
95
+ }).catch((error) => {
96
+ throw error;
97
+ });
98
+ nonBlockingPromises.push(promiseWithHandling);
99
+ } else context.results.push(result);
62
100
  /** Check if pipeline was terminated by controller.return() */
63
101
  if (context.terminated) break;
64
102
  /** Handle jump to priority AFTER handler execution */
@@ -75,12 +113,56 @@ async function executeSequential(context, createController) {
75
113
  } else i++;
76
114
  } catch (error) {
77
115
  if (registration.config.blocking) throw error;
78
- i++;
116
+ throw error;
79
117
  }
80
118
  }
119
+ if (nonBlockingPromises.length > 0) await Promise.all(nonBlockingPromises);
81
120
  }
82
121
  /**
83
122
  * Execute handlers in parallel mode (all at once)
123
+ *
124
+ * Executes all qualifying action handlers simultaneously using Promise.allSettled.
125
+ * Supports both blocking and non-blocking handlers. Blocking handlers can still
126
+ * fail the entire pipeline if they throw errors.
127
+ *
128
+ * @template T - The payload type for the action
129
+ * @template R - The result type for handlers
130
+ *
131
+ * @param context - Pipeline execution context containing handlers and state
132
+ * @param createController - Factory function for creating pipeline controllers
133
+ *
134
+ * @throws {Error} When any blocking handler fails
135
+ *
136
+ * @example
137
+ * ```typescript
138
+ * // This is called internally by ActionRegister.dispatch()
139
+ * // when executionMode is 'parallel'
140
+ *
141
+ * // All handlers execute simultaneously:
142
+ * // - Analytics handler (non-blocking)
143
+ * // - Validation handler (blocking)
144
+ * // - Update handler (blocking)
145
+ * // - Notification handler (non-blocking)
146
+ *
147
+ * await executeParallel(context, (registration, index) => ({
148
+ * abort: (reason) => { context.aborted = true },
149
+ * setResult: (result) => { context.results.push(result) },
150
+ * // ... other controller methods
151
+ * }))
152
+ * ```
153
+ *
154
+ * @example Use Case
155
+ * ```typescript
156
+ * // Perfect for independent operations
157
+ * register.setActionExecutionMode('logEvent', 'parallel')
158
+ *
159
+ * // These can all run simultaneously:
160
+ * register.register('logEvent', analyticsHandler, { blocking: false })
161
+ * register.register('logEvent', metricsHandler, { blocking: false })
162
+ * register.register('logEvent', auditHandler, { blocking: true })
163
+ * ```
164
+ *
165
+ * @public
84
166
  */
85
167
  async function executeParallel(context, createController) {
86
168
  /** Filter handlers that should run */
@@ -97,8 +179,10 @@ async function executeParallel(context, createController) {
97
179
  try {
98
180
  const result = registration.handler(context.payload, controller);
99
181
  let handlerResult;
100
- if (result instanceof Promise) handlerResult = await result;
101
- else handlerResult = result;
182
+ if (result instanceof Promise) {
183
+ const resolved = await result;
184
+ handlerResult = resolved;
185
+ } else handlerResult = result;
102
186
  /** Collect result if handler returned something and pipeline wasn't terminated */
103
187
  if (handlerResult !== void 0 && !context.terminated) context.results.push(handlerResult);
104
188
  return {
@@ -140,6 +224,56 @@ async function executeParallel(context, createController) {
140
224
  }
141
225
  /**
142
226
  * Execute handlers in race mode (first to complete wins)
227
+ *
228
+ * Executes all qualifying handlers simultaneously using Promise.race, where
229
+ * the first handler to complete determines the pipeline result. Other handlers
230
+ * are effectively cancelled. Useful for scenarios where you want the fastest
231
+ * response from multiple equivalent handlers.
232
+ *
233
+ * @template T - The payload type for the action
234
+ * @template R - The result type for handlers
235
+ *
236
+ * @param context - Pipeline execution context containing handlers and state
237
+ * @param createController - Factory function for creating pipeline controllers
238
+ *
239
+ * @throws {Error} When the winning handler fails and is blocking
240
+ *
241
+ * @example
242
+ * ```typescript
243
+ * // This is called internally by ActionRegister.dispatch()
244
+ * // when executionMode is 'race'
245
+ *
246
+ * // Multiple data sources racing for fastest response:
247
+ * // - Database handler (might be slow)
248
+ * // - Cache handler (usually fast)
249
+ * // - API handler (variable speed)
250
+ * //
251
+ * // Whichever completes first wins
252
+ *
253
+ * await executeRace(context, (registration, index) => ({
254
+ * return: (result) => {
255
+ * context.terminated = true
256
+ * context.terminationResult = result
257
+ * },
258
+ * // ... other controller methods
259
+ * }))
260
+ * ```
261
+ *
262
+ * @example Use Case
263
+ * ```typescript
264
+ * // Race between multiple data sources
265
+ * register.setActionExecutionMode('fetchUserData', 'race')
266
+ *
267
+ * // These handlers race for fastest response:
268
+ * register.register('fetchUserData', cacheHandler) // Usually fastest
269
+ * register.register('fetchUserData', databaseHandler) // Reliable fallback
270
+ * register.register('fetchUserData', apiHandler) // External source
271
+ *
272
+ * // First to complete wins, others are ignored
273
+ * const result = await register.dispatchWithResult('fetchUserData', { id: '123' })
274
+ * ```
275
+ *
276
+ * @public
143
277
  */
144
278
  async function executeRace(context, createController) {
145
279
  /** Filter handlers that should run */
@@ -157,8 +291,10 @@ async function executeRace(context, createController) {
157
291
  try {
158
292
  const result = registration.handler(context.payload, controller);
159
293
  let handlerResult;
160
- if (result instanceof Promise) handlerResult = await result;
161
- else handlerResult = result;
294
+ if (result instanceof Promise) {
295
+ const resolved = await result;
296
+ handlerResult = resolved;
297
+ } else handlerResult = result;
162
298
  return {
163
299
  success: true,
164
300
  handlerId: registration.id,
@@ -189,8 +325,8 @@ async function executeRace(context, createController) {
189
325
  }
190
326
 
191
327
  //#endregion
192
- //#region ../../node_modules/.pnpm/@oxc-project+runtime@0.81.0/node_modules/@oxc-project/runtime/src/helpers/typeof.js
193
- 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) => {
328
+ //#region ../../node_modules/.pnpm/@oxc-project+runtime@0.82.2/node_modules/@oxc-project/runtime/src/helpers/typeof.js
329
+ var require_typeof = /* @__PURE__ */ __commonJS({ "../../node_modules/.pnpm/@oxc-project+runtime@0.82.2/node_modules/@oxc-project/runtime/src/helpers/typeof.js": ((exports, module) => {
194
330
  function _typeof$2(o) {
195
331
  "@babel/helpers - typeof";
196
332
  return module.exports = _typeof$2 = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function(o$1) {
@@ -203,8 +339,8 @@ var require_typeof = /* @__PURE__ */ __commonJS({ "../../node_modules/.pnpm/@oxc
203
339
  }) });
204
340
 
205
341
  //#endregion
206
- //#region ../../node_modules/.pnpm/@oxc-project+runtime@0.81.0/node_modules/@oxc-project/runtime/src/helpers/toPrimitive.js
207
- 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) => {
342
+ //#region ../../node_modules/.pnpm/@oxc-project+runtime@0.82.2/node_modules/@oxc-project/runtime/src/helpers/toPrimitive.js
343
+ var require_toPrimitive = /* @__PURE__ */ __commonJS({ "../../node_modules/.pnpm/@oxc-project+runtime@0.82.2/node_modules/@oxc-project/runtime/src/helpers/toPrimitive.js": ((exports, module) => {
208
344
  var _typeof$1 = require_typeof()["default"];
209
345
  function toPrimitive$1(t, r) {
210
346
  if ("object" != _typeof$1(t) || !t) return t;
@@ -220,8 +356,8 @@ var require_toPrimitive = /* @__PURE__ */ __commonJS({ "../../node_modules/.pnpm
220
356
  }) });
221
357
 
222
358
  //#endregion
223
- //#region ../../node_modules/.pnpm/@oxc-project+runtime@0.81.0/node_modules/@oxc-project/runtime/src/helpers/toPropertyKey.js
224
- 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) => {
359
+ //#region ../../node_modules/.pnpm/@oxc-project+runtime@0.82.2/node_modules/@oxc-project/runtime/src/helpers/toPropertyKey.js
360
+ var require_toPropertyKey = /* @__PURE__ */ __commonJS({ "../../node_modules/.pnpm/@oxc-project+runtime@0.82.2/node_modules/@oxc-project/runtime/src/helpers/toPropertyKey.js": ((exports, module) => {
225
361
  var _typeof = require_typeof()["default"];
226
362
  var toPrimitive = require_toPrimitive();
227
363
  function toPropertyKey$1(t) {
@@ -232,8 +368,8 @@ var require_toPropertyKey = /* @__PURE__ */ __commonJS({ "../../node_modules/.pn
232
368
  }) });
233
369
 
234
370
  //#endregion
235
- //#region ../../node_modules/.pnpm/@oxc-project+runtime@0.81.0/node_modules/@oxc-project/runtime/src/helpers/defineProperty.js
236
- 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) => {
371
+ //#region ../../node_modules/.pnpm/@oxc-project+runtime@0.82.2/node_modules/@oxc-project/runtime/src/helpers/defineProperty.js
372
+ var require_defineProperty = /* @__PURE__ */ __commonJS({ "../../node_modules/.pnpm/@oxc-project+runtime@0.82.2/node_modules/@oxc-project/runtime/src/helpers/defineProperty.js": ((exports, module) => {
237
373
  var toPropertyKey = require_toPropertyKey();
238
374
  function _defineProperty$3(e, r, t) {
239
375
  return (r = toPropertyKey(r)) in e ? Object.defineProperty(e, r, {
@@ -251,41 +387,81 @@ var require_defineProperty = /* @__PURE__ */ __commonJS({ "../../node_modules/.p
251
387
  var import_defineProperty$2 = /* @__PURE__ */ __toESM(require_defineProperty(), 1);
252
388
  /**
253
389
  * Action Guard system for managing action execution timing
254
- * @implements action-guard
255
- * @implements performance-optimization
256
- * @implements user-experience-optimization
257
- * @implements class-naming
258
- * @memberof core-concepts
259
- * @internal
260
- * @since 1.0.0
261
390
  *
262
- * Manages action execution timing through debouncing and throttling
263
- * @implements performance-optimization
391
+ * Provides performance optimization and user experience enhancement through
392
+ * debouncing and throttling mechanisms. Debouncing waits for a pause in calls
393
+ * before executing, while throttling limits execution frequency.
264
394
  *
265
- * @example
395
+ * @example Debouncing Search Input
266
396
  * ```typescript
267
- * const guard = new ActionGuard(logger);
397
+ * const guard = new ActionGuard()
398
+ *
399
+ * // Wait 300ms after user stops typing before searching
400
+ * register.register('searchUsers', async (payload, controller) => {
401
+ * const query = payload.query
402
+ * if (query.length < 2) return
403
+ *
404
+ * const results = await userService.search(query)
405
+ * controller.setResult(results)
406
+ * }, {
407
+ * debounce: 300, // Built into ActionRegister via ActionGuard
408
+ * tags: ['search', 'user-input']
409
+ * })
410
+ * ```
268
411
  *
269
- * // Debounce search input (wait 300ms after typing stops)
412
+ * @example Throttling High-Frequency Events
413
+ * ```typescript
414
+ * // Limit scroll position updates to once per 100ms
415
+ * register.register('updateScrollPosition', (payload, controller) => {
416
+ * scrollState.setValue(payload.position)
417
+ * }, {
418
+ * throttle: 100, // Built into ActionRegister via ActionGuard
419
+ * tags: ['scroll', 'performance']
420
+ * })
421
+ * ```
422
+ *
423
+ * @example Manual Usage (Advanced)
424
+ * ```typescript
425
+ * const guard = new ActionGuard()
426
+ *
427
+ * // Manual debouncing
270
428
  * if (await guard.debounce('search', 300)) {
271
- * executeSearch();
429
+ * performSearch() // Only executes after 300ms pause
272
430
  * }
273
431
  *
274
- * // Throttle scroll handler (max once per 100ms)
432
+ * // Manual throttling
275
433
  * if (guard.throttle('scroll', 100)) {
276
- * updateScrollPosition();
434
+ * updateUI() // Max once per 100ms
277
435
  * }
278
436
  * ```
437
+ *
438
+ * @internal
279
439
  */
280
440
  var ActionGuard = class {
281
441
  constructor() {
282
442
  (0, import_defineProperty$2.default)(this, "guards", /* @__PURE__ */ new Map());
283
443
  }
284
444
  /**
285
- * Check if action should be debounced
286
- * @param actionKey - Unique key for the action
287
- * @param debounceMs - Debounce delay in milliseconds
288
- * @returns Promise that resolves when debounce period is complete
445
+ * Apply debouncing to an action
446
+ *
447
+ * Debouncing waits for a specified delay after the last call before allowing
448
+ * execution. Each new call resets the timer. Useful for search inputs, resize
449
+ * handlers, and other high-frequency user interactions.
450
+ *
451
+ * @param actionKey - Unique identifier for the action being debounced
452
+ * @param debounceMs - Delay in milliseconds to wait after the last call
453
+ *
454
+ * @returns Promise resolving to true if execution should proceed, false if cancelled
455
+ *
456
+ * @example Search Input Debouncing
457
+ * ```typescript
458
+ * // Only search after user stops typing for 300ms
459
+ * if (await guard.debounce('userSearch', 300)) {
460
+ * performSearch(query)
461
+ * }
462
+ * ```
463
+ *
464
+ * @internal
289
465
  */
290
466
  async debounce(actionKey, debounceMs) {
291
467
  /** Get or create guard state for this action */
@@ -320,10 +496,26 @@ var ActionGuard = class {
320
496
  });
321
497
  }
322
498
  /**
323
- * Check if action should be throttled
324
- * @param actionKey - Unique key for the action
325
- * @param throttleMs - Throttle delay in milliseconds
326
- * @returns True if action should proceed, false if throttled
499
+ * Apply throttling to an action
500
+ *
501
+ * Throttling limits execution frequency by ensuring a minimum interval between
502
+ * calls. Unlike debouncing, throttling executes immediately on the first call
503
+ * and then blocks subsequent calls until the interval expires.
504
+ *
505
+ * @param actionKey - Unique identifier for the action being throttled
506
+ * @param throttleMs - Minimum interval in milliseconds between executions
507
+ *
508
+ * @returns True if execution should proceed, false if currently throttled
509
+ *
510
+ * @example Scroll Handler Throttling
511
+ * ```typescript
512
+ * // Update scroll position max once per 100ms
513
+ * if (guard.throttle('scrollUpdate', 100)) {
514
+ * updateScrollPosition()
515
+ * }
516
+ * ```
517
+ *
518
+ * @internal
327
519
  */
328
520
  throttle(actionKey, throttleMs) {
329
521
  /** Get or create guard state for this action */
@@ -362,8 +554,14 @@ var ActionGuard = class {
362
554
  return false;
363
555
  }
364
556
  /**
365
- * Clear all guards for an action
366
- * @param actionKey - Action key to clear
557
+ * Clear all guard state for a specific action
558
+ *
559
+ * Removes debounce and throttle timers for the specified action,
560
+ * preventing memory leaks and allowing immediate re-execution.
561
+ *
562
+ * @param actionKey - Action identifier to clear guards for
563
+ *
564
+ * @internal
367
565
  */
368
566
  clearGuards(actionKey) {
369
567
  const state = this.guards.get(actionKey);
@@ -380,7 +578,12 @@ var ActionGuard = class {
380
578
  }
381
579
  }
382
580
  /**
383
- * Clear all guards
581
+ * Clear all guard states for all actions
582
+ *
583
+ * Removes all active debounce and throttle timers, useful for cleanup
584
+ * when shutting down the action system or resetting state.
585
+ *
586
+ * @internal
384
587
  */
385
588
  clearAll() {
386
589
  /** Iterate through all guard states and clear their timers */
@@ -398,14 +601,28 @@ var ActionGuard = class {
398
601
  this.guards.clear();
399
602
  }
400
603
  /**
401
- * Get current guard state for debugging
402
- * @param actionKey - Action key to inspect
604
+ * Get current guard state for debugging purposes
605
+ *
606
+ * Returns the internal state for a specific action, including timer
607
+ * information and execution timestamps.
608
+ *
609
+ * @param actionKey - Action identifier to inspect
610
+ * @returns Guard state or undefined if no state exists
611
+ *
612
+ * @internal
403
613
  */
404
614
  getGuardState(actionKey) {
405
615
  return this.guards.get(actionKey);
406
616
  }
407
617
  /**
408
- * Get all active guards for debugging
618
+ * Get all active guard states for debugging purposes
619
+ *
620
+ * Returns a copy of all current guard states, useful for monitoring
621
+ * and debugging rate limiting behavior across all actions.
622
+ *
623
+ * @returns Map of action keys to their guard states
624
+ *
625
+ * @internal
409
626
  */
410
627
  getAllGuardStates() {
411
628
  return new Map(this.guards);
@@ -522,33 +739,84 @@ var OperationQueue = class {
522
739
  //#region src/ActionRegister.ts
523
740
  var import_defineProperty = /* @__PURE__ */ __toESM(require_defineProperty(), 1);
524
741
  /**
525
- * 중앙화된 액션 등록 디스패치 시스템으로, 타입 안전한 액션 파이프라인 관리를 제공하는 핵심 클래스입니다.
742
+ * Action Register for managing action handlers with priority-based execution
526
743
  *
527
- * @implements {ActionRegister}
528
- * @implements {Action Pipeline System}
529
- * @memberof core-concepts
744
+ * Central action registration and dispatch system providing type-safe action pipeline management.
745
+ * Supports sequential, parallel, and race execution modes with advanced handler filtering,
746
+ * throttling, debouncing, and comprehensive result collection.
530
747
  *
531
- * @example
748
+ * @template TActionMap - Action payload mapping interface extending ActionPayloadMap
749
+ *
750
+ * @example Basic Usage
532
751
  * ```typescript
533
752
  * interface AppActions extends ActionPayloadMap {
534
- * updateUser: { id: string; name: string };
535
- * calculateTotal: void;
753
+ * updateUser: { id: string; name: string; email: string }
754
+ * deleteUser: { id: string }
755
+ * resetUser: void
536
756
  * }
537
757
  *
538
758
  * const register = new ActionRegister<AppActions>({
539
759
  * name: 'AppRegister',
540
- * logLevel: LogLevel.DEBUG
541
- * });
760
+ * registry: { debug: true, maxHandlers: 10 }
761
+ * })
762
+ *
763
+ * // Register handler with priority
764
+ * register.register('updateUser', async (payload, controller) => {
765
+ * await userService.update(payload.id, payload)
766
+ * controller.setResult({ success: true, userId: payload.id })
767
+ * }, { priority: 10, tags: ['user', 'crud'] })
768
+ *
769
+ * // Dispatch action
770
+ * await register.dispatch('updateUser', {
771
+ * id: '123',
772
+ * name: 'John Doe',
773
+ * email: 'john@example.com'
774
+ * })
775
+ * ```
542
776
  *
543
- * // 핸들러 등록
544
- * register.register('updateUser', ({ id, name }, controller) => {
545
- * userStore.setValue({ id, name });
546
- * // 핸들러가 자동으로 다음 핸들러로 진행
547
- * }, { priority: 10 });
777
+ * @example With Multiple Handlers
778
+ * ```typescript
779
+ * // High priority validation handler
780
+ * register.register('updateUser', async (payload, controller) => {
781
+ * if (!payload.email.includes('@')) {
782
+ * controller.abort('Invalid email format')
783
+ * return
784
+ * }
785
+ * }, { priority: 100, category: 'validation' })
548
786
  *
549
- * // 액션 디스패치
550
- * await register.dispatch('updateUser', { id: '1', name: 'John' });
787
+ * // Lower priority update handler
788
+ * register.register('updateUser', async (payload, controller) => {
789
+ * const user = await userService.update(payload.id, payload)
790
+ * controller.setResult(user)
791
+ * }, { priority: 50, category: 'business-logic' })
551
792
  * ```
793
+ *
794
+ * @example Advanced Configuration
795
+ * ```typescript
796
+ * const register = new ActionRegister<AppActions>({
797
+ * name: 'AdvancedRegister',
798
+ * registry: {
799
+ * debug: true,
800
+ * maxHandlers: 20,
801
+ * defaultExecutionMode: 'parallel',
802
+ * autoCleanup: true
803
+ * }
804
+ * })
805
+ *
806
+ * // Handler with debouncing and tags
807
+ * register.register('searchUsers', async (payload, controller) => {
808
+ * const results = await userService.search(payload.query)
809
+ * controller.setResult(results)
810
+ * }, {
811
+ * priority: 10,
812
+ * debounce: 300,
813
+ * tags: ['search', 'user'],
814
+ * category: 'query',
815
+ * once: false
816
+ * })
817
+ * ```
818
+ *
819
+ * @public
552
820
  */
553
821
  var ActionRegister = class {
554
822
  constructor(config = {}) {
@@ -575,6 +843,43 @@ var ActionRegister = class {
575
843
  concurrencyProtection: true
576
844
  });
577
845
  }
846
+ /**
847
+ * Register an action handler with optional configuration
848
+ *
849
+ * @param action - The action type to register handler for
850
+ * @param handler - The handler function to execute
851
+ * @param config - Optional handler configuration including priority, tags, etc.
852
+ *
853
+ * @returns Unregister function to remove this handler
854
+ *
855
+ * @throws {Error} When maximum handlers limit is reached
856
+ *
857
+ * @example Basic Registration
858
+ * ```typescript
859
+ * const unregister = register.register('updateUser', async (payload, controller) => {
860
+ * await userService.update(payload.id, payload)
861
+ * })
862
+ *
863
+ * // Later remove the handler
864
+ * unregister()
865
+ * ```
866
+ *
867
+ * @example With Priority and Configuration
868
+ * ```typescript
869
+ * register.register('validateUser', async (payload, controller) => {
870
+ * if (!payload.email) {
871
+ * controller.abort('Email is required')
872
+ * }
873
+ * }, {
874
+ * priority: 100,
875
+ * tags: ['validation'],
876
+ * category: 'security',
877
+ * once: false
878
+ * })
879
+ * ```
880
+ *
881
+ * @public
882
+ */
578
883
  register(action, handler, config = {}) {
579
884
  const handlerId = config.id || `handler_${++this.handlerCounter}_${Math.random().toString(36).substr(2, 5)}`;
580
885
  const unregisterFn = this._performRegistrationSync(action, handler, config, handlerId);
@@ -707,6 +1012,48 @@ var ActionRegister = class {
707
1012
  }
708
1013
  };
709
1014
  }
1015
+ /**
1016
+ * Dispatch an action with optional execution options
1017
+ *
1018
+ * @param action - The action type to dispatch
1019
+ * @param payload - The action payload data
1020
+ * @param options - Optional dispatch options (execution mode, filters, etc.)
1021
+ *
1022
+ * @returns Promise that resolves when all handlers complete
1023
+ *
1024
+ * @throws {Error} When action dispatching fails
1025
+ *
1026
+ * @example Basic Dispatch
1027
+ * ```typescript
1028
+ * await register.dispatch('updateUser', {
1029
+ * id: '123',
1030
+ * name: 'John Doe',
1031
+ * email: 'john@example.com'
1032
+ * })
1033
+ * ```
1034
+ *
1035
+ * @example With Options
1036
+ * ```typescript
1037
+ * await register.dispatch('updateUser', payload, {
1038
+ * executionMode: 'parallel',
1039
+ * timeout: 5000,
1040
+ * filter: {
1041
+ * tags: ['validation', 'business-logic'],
1042
+ * excludeCategory: 'analytics'
1043
+ * }
1044
+ * })
1045
+ * ```
1046
+ *
1047
+ * @example With Throttling
1048
+ * ```typescript
1049
+ * await register.dispatch('searchUsers', { query: 'john' }, {
1050
+ * throttle: 300,
1051
+ * debounce: 100
1052
+ * })
1053
+ * ```
1054
+ *
1055
+ * @public
1056
+ */
710
1057
  async dispatch(action, payload, options) {
711
1058
  return this.dispatchQueue.enqueue(async () => {
712
1059
  return this._performDispatch(action, payload, options);
@@ -716,6 +1063,28 @@ var ActionRegister = class {
716
1063
  * 🆕 실제 디스패치 작업 수행 (큐에서 호출됨)
717
1064
  */
718
1065
  async _performDispatch(action, payload, options) {
1066
+ if (payload && typeof payload === "object" && payload !== null && typeof process !== "undefined" && process.env?.NODE_ENV === "development") {
1067
+ payload instanceof Event;
1068
+ payload instanceof Element;
1069
+ payload.preventDefault;
1070
+ payload.stopPropagation;
1071
+ payload.currentTarget;
1072
+ const hasTarget = payload.target !== void 0;
1073
+ hasTarget && payload.target;
1074
+ hasTarget && payload.target instanceof Element;
1075
+ if (typeof process !== "undefined" && process.env?.DEBUG_CONTEXT_ACTION || typeof process !== "undefined" && process.env?.NODE_ENV === "development") {
1076
+ const nestedDOMProperties = [];
1077
+ Object.keys(payload).forEach((key) => {
1078
+ const prop = payload[key];
1079
+ if (prop instanceof Element || prop instanceof Event) nestedDOMProperties.push(`${key}: ${prop instanceof Element ? "Element" : "Event"}`);
1080
+ });
1081
+ if (nestedDOMProperties.length > 0) console.debug(`[Context-Action] 📋 Nested DOM objects in action "${String(action)}":`, {
1082
+ registry: this.name,
1083
+ nestedDOMProperties,
1084
+ note: "This is informational - usually not a problem"
1085
+ });
1086
+ }
1087
+ }
719
1088
  let autoAbortController;
720
1089
  let effectiveSignal = options?.signal;
721
1090
  if (options?.autoAbort?.enabled) {
@@ -783,7 +1152,9 @@ var ActionRegister = class {
783
1152
  if (effectiveSignal && abortHandler) effectiveSignal.addEventListener("abort", abortHandler);
784
1153
  try {
785
1154
  await this.executePipeline(context, autoAbortController, options?.autoAbort);
1155
+ console.log(`[ActionRegister] Pipeline execution succeeded for ${String(action)}`);
786
1156
  } catch (error) {
1157
+ console.log(`[ActionRegister] Pipeline execution failed for ${String(action)}:`, error);
787
1158
  executionSuccess = false;
788
1159
  throw error;
789
1160
  } finally {
@@ -792,6 +1163,43 @@ var ActionRegister = class {
792
1163
  this.updateExecutionStats(action, executionSuccess, duration);
793
1164
  }
794
1165
  }
1166
+ /**
1167
+ * Dispatch an action and return detailed execution results
1168
+ *
1169
+ * @param action - The action type to dispatch
1170
+ * @param payload - The action payload data
1171
+ * @param options - Optional dispatch options including result collection strategy
1172
+ *
1173
+ * @returns Promise resolving to comprehensive execution results
1174
+ *
1175
+ * @example Basic Result Collection
1176
+ * ```typescript
1177
+ * const result = await register.dispatchWithResult('updateUser', payload)
1178
+ *
1179
+ * if (result.success) {
1180
+ * console.log(`Executed ${result.execution.handlersExecuted} handlers`)
1181
+ * console.log(`Duration: ${result.execution.duration}ms`)
1182
+ * }
1183
+ * ```
1184
+ *
1185
+ * @example Advanced Result Processing
1186
+ * ```typescript
1187
+ * const result = await register.dispatchWithResult('processOrder', order, {
1188
+ * result: {
1189
+ * collect: true,
1190
+ * strategy: 'merge',
1191
+ * maxResults: 5,
1192
+ * merger: (results) => results.reduce((acc, curr) => ({ ...acc, ...curr }), {})
1193
+ * }
1194
+ * })
1195
+ *
1196
+ * if (result.terminated) {
1197
+ * console.log('Handler returned early:', result.result)
1198
+ * }
1199
+ * ```
1200
+ *
1201
+ * @public
1202
+ */
795
1203
  async dispatchWithResult(action, payload, options) {
796
1204
  const startTime = Date.now();
797
1205
  let autoAbortController;
@@ -1094,22 +1502,105 @@ var ActionRegister = class {
1094
1502
  if (success) stats.successCount++;
1095
1503
  else stats.errorCount++;
1096
1504
  }
1505
+ /**
1506
+ * Get the number of registered handlers for an action
1507
+ *
1508
+ * @param action - The action type to count handlers for
1509
+ *
1510
+ * @returns Number of registered handlers
1511
+ *
1512
+ * @example
1513
+ * ```typescript
1514
+ * register.register('updateUser', handler1)
1515
+ * register.register('updateUser', handler2)
1516
+ *
1517
+ * console.log(register.getHandlerCount('updateUser')) // 2
1518
+ * ```
1519
+ *
1520
+ * @public
1521
+ */
1097
1522
  getHandlerCount(action) {
1098
1523
  const pipeline = this.pipelines.get(action);
1099
1524
  return pipeline ? pipeline.length : 0;
1100
1525
  }
1526
+ /**
1527
+ * Check if an action has any registered handlers
1528
+ *
1529
+ * @param action - The action type to check
1530
+ *
1531
+ * @returns True if action has handlers, false otherwise
1532
+ *
1533
+ * @example
1534
+ * ```typescript
1535
+ * if (register.hasHandlers('updateUser')) {
1536
+ * await register.dispatch('updateUser', userData)
1537
+ * }
1538
+ * ```
1539
+ *
1540
+ * @public
1541
+ */
1101
1542
  hasHandlers(action) {
1102
1543
  return this.getHandlerCount(action) > 0;
1103
1544
  }
1545
+ /**
1546
+ * Get all registered action types
1547
+ *
1548
+ * @returns Array of all registered action types
1549
+ *
1550
+ * @example
1551
+ * ```typescript
1552
+ * const actions = register.getRegisteredActions()
1553
+ * console.log('Registered actions:', actions) // ['updateUser', 'deleteUser', 'resetUser']
1554
+ * ```
1555
+ *
1556
+ * @public
1557
+ */
1104
1558
  getRegisteredActions() {
1105
1559
  return Array.from(this.pipelines.keys());
1106
1560
  }
1561
+ /**
1562
+ * Remove all handlers for a specific action
1563
+ *
1564
+ * @param action - The action type to clear handlers for
1565
+ *
1566
+ * @example
1567
+ * ```typescript
1568
+ * register.clearAction('updateUser')
1569
+ * console.log(register.hasHandlers('updateUser')) // false
1570
+ * ```
1571
+ *
1572
+ * @public
1573
+ */
1107
1574
  clearAction(action) {
1108
1575
  this.pipelines.delete(action);
1109
1576
  }
1577
+ /**
1578
+ * Remove all handlers for all actions
1579
+ *
1580
+ * @example
1581
+ * ```typescript
1582
+ * register.clearAll()
1583
+ * console.log(register.getRegisteredActions().length) // 0
1584
+ * ```
1585
+ *
1586
+ * @public
1587
+ */
1110
1588
  clearAll() {
1111
1589
  this.pipelines.clear();
1112
1590
  }
1591
+ /**
1592
+ * Get the name of this action register
1593
+ *
1594
+ * @returns The register name
1595
+ *
1596
+ * @example
1597
+ * ```typescript
1598
+ * const register = new ActionRegister({ name: 'UserRegister' })
1599
+ * console.log(register.getName()) // 'UserRegister'
1600
+ * ```
1601
+ *
1602
+ * @public
1603
+ */
1113
1604
  getName() {
1114
1605
  return this.name;
1115
1606
  }
@@ -1163,6 +1654,7 @@ var ActionRegister = class {
1163
1654
  return {
1164
1655
  action,
1165
1656
  handlerCount: pipeline.length,
1657
+ totalHandlers: pipeline.length,
1166
1658
  handlersByPriority,
1167
1659
  executionStats
1168
1660
  };