@context-action/core 0.8.4 โ†’ 0.8.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,4 +1,7 @@
1
1
  //#region src/execution-modes.ts
2
+ function isPromiseLike(value) {
3
+ return (typeof value === "object" || typeof value === "function") && value !== null && typeof value.then === "function";
4
+ }
2
5
  /**
3
6
  * Create standardized error handling for handlers
4
7
  *
@@ -58,12 +61,15 @@ async function executeSequential(context, createController) {
58
61
  i++;
59
62
  continue;
60
63
  }
64
+ (context.executedHandlers ?? (context.executedHandlers = [])).push(registration);
61
65
  const result = registration.handler(context.payload, controller);
66
+ const asyncResult = isPromiseLike(result) ? Promise.resolve(result) : void 0;
67
+ const trackedResult = asyncResult && context.trackHandlerPromise ? context.trackHandlerPromise(asyncResult) : asyncResult;
62
68
  if (registration.config.blocking) {
63
- const handlerResult = result instanceof Promise ? await result : result;
69
+ const handlerResult = trackedResult ? await trackedResult : result;
64
70
  if (handlerResult !== void 0 && !context.terminated) context.results.push(handlerResult);
65
- } else if (result instanceof Promise) {
66
- const promiseWithErrorHandling = result.then((asyncResult) => {
71
+ } else if (trackedResult) {
72
+ const promiseWithErrorHandling = trackedResult.then((asyncResult) => {
67
73
  if (asyncResult !== void 0 && !context.terminated) context.results.push(asyncResult);
68
74
  return asyncResult;
69
75
  }).catch((error) => {
@@ -161,10 +167,9 @@ async function executeParallel(context, createController) {
161
167
  skipped: true
162
168
  };
163
169
  }
170
+ (context.executedHandlers ?? (context.executedHandlers = [])).push(registration);
164
171
  const result = registration.handler(context.payload, controller);
165
- let handlerResult;
166
- if (result instanceof Promise) handlerResult = await result;
167
- else handlerResult = result;
172
+ const handlerResult = isPromiseLike(result) ? await Promise.resolve(result) : result;
168
173
  /** Collect result if handler returned something and pipeline wasn't terminated */
169
174
  if (handlerResult !== void 0 && !context.terminated) context.results.push(handlerResult);
170
175
  return {
@@ -183,8 +188,9 @@ async function executeParallel(context, createController) {
183
188
  };
184
189
  }
185
190
  });
191
+ const trackedHandlerPromises = context.trackHandlerPromise ? handlerPromises.map((promise) => context.trackHandlerPromise(promise)) : handlerPromises;
186
192
  /** Wait for all handlers to complete */
187
- const results = await Promise.allSettled(handlerPromises);
193
+ const results = await Promise.allSettled(trackedHandlerPromises);
188
194
  /** Check for any rejected blocking handlers */
189
195
  const failures = results.filter((result, index) => {
190
196
  if (result.status === "rejected") return runnableHandlers[index]?.config.blocking ?? false;
@@ -203,8 +209,10 @@ async function executeParallel(context, createController) {
203
209
  *
204
210
  * Executes all qualifying handlers simultaneously using Promise.race, where
205
211
  * the first handler to complete determines the pipeline result. Other handlers
206
- * are effectively cancelled. Useful for scenarios where you want the fastest
207
- * response from multiple equivalent handlers.
212
+ * continue in the background and remain tracked for lifecycle cleanup; handlers
213
+ * must observe the controller signal for cooperative external cancellation.
214
+ * Useful for scenarios where you want the fastest response from multiple
215
+ * equivalent handlers.
208
216
  *
209
217
  * @template T - The payload type for the action
210
218
  * @template R - The result type for handlers
@@ -245,10 +253,9 @@ async function executeRace(context, createController) {
245
253
  skipped: true
246
254
  };
247
255
  }
256
+ (context.executedHandlers ?? (context.executedHandlers = [])).push(registration);
248
257
  const result = registration.handler(context.payload, controller);
249
- let handlerResult;
250
- if (result instanceof Promise) handlerResult = await result;
251
- else handlerResult = result;
258
+ const handlerResult = isPromiseLike(result) ? await Promise.resolve(result) : result;
252
259
  return {
253
260
  success: true,
254
261
  handlerId: registration.id,
@@ -266,8 +273,9 @@ async function executeRace(context, createController) {
266
273
  };
267
274
  }
268
275
  });
269
- /** Race all handlers */
270
- const winner = await Promise.race(handlerPromises);
276
+ const trackedHandlerPromises = context.trackHandlerPromise ? handlerPromises.map((promise) => context.trackHandlerPromise(promise)) : handlerPromises;
277
+ /** Race all handlers while retaining every loser for lifecycle draining. */
278
+ const winner = await Promise.race(trackedHandlerPromises);
271
279
  /** If the winner failed and was blocking, throw the error */
272
280
  if (!winner.success && winner.registration?.config.blocking) throw winner.error;
273
281
  /** Collect result from the winning handler */
@@ -316,7 +324,11 @@ var ActionGuard = class {
316
324
  this.cleanupIntervalMs = 3e4;
317
325
  this.maxGuards = 1e3;
318
326
  this.accessOrder = [];
319
- if (autoCleanup) this.startAutoCleanup();
327
+ this.autoCleanupEnabled = autoCleanup;
328
+ }
329
+ /** Start cleanup only after the first guard is used. */
330
+ ensureAutoCleanup() {
331
+ if (this.autoCleanupEnabled && !this.cleanupInterval) this.startAutoCleanup();
320
332
  }
321
333
  /**
322
334
  * Start automatic cleanup of idle guard states
@@ -324,9 +336,17 @@ var ActionGuard = class {
324
336
  * @internal
325
337
  */
326
338
  startAutoCleanup() {
339
+ if (this.cleanupInterval) return;
327
340
  this.cleanupInterval = setInterval(() => {
328
341
  this.performCleanup();
329
342
  }, this.cleanupIntervalMs);
343
+ this.cleanupInterval.unref?.();
344
+ }
345
+ stopAutoCleanup() {
346
+ if (this.cleanupInterval) {
347
+ clearInterval(this.cleanupInterval);
348
+ this.cleanupInterval = void 0;
349
+ }
330
350
  }
331
351
  /**
332
352
  * ๐Ÿ”ง Optimized cleanup with early exit and batched operations
@@ -335,7 +355,10 @@ var ActionGuard = class {
335
355
  */
336
356
  performCleanup() {
337
357
  const guardCount = this.guards.size;
338
- if (guardCount === 0) return;
358
+ if (guardCount === 0) {
359
+ this.stopAutoCleanup();
360
+ return;
361
+ }
339
362
  const now = Date.now();
340
363
  const keysToDelete = [];
341
364
  if (guardCount <= 10) this.guards.forEach((state, key) => {
@@ -365,6 +388,7 @@ var ActionGuard = class {
365
388
  if (accessIndex !== -1) this.accessOrder.splice(accessIndex, 1);
366
389
  });
367
390
  if (typeof process !== "undefined" && process.env?.DEBUG_CONTEXT_ACTION) console.debug(`[ActionGuard] Cleaned up ${keysToDelete.length} idle guards`);
391
+ if (this.guards.size === 0) this.stopAutoCleanup();
368
392
  }
369
393
  }
370
394
  /**
@@ -423,6 +447,7 @@ var ActionGuard = class {
423
447
  * @internal
424
448
  */
425
449
  async debounce(actionKey, debounceMs) {
450
+ this.ensureAutoCleanup();
426
451
  this.evictIfNeeded();
427
452
  /** Get or create guard state for this action */
428
453
  let state = this.guards.get(actionKey);
@@ -483,6 +508,7 @@ var ActionGuard = class {
483
508
  * @internal
484
509
  */
485
510
  throttle(actionKey, throttleMs) {
511
+ this.ensureAutoCleanup();
486
512
  this.evictIfNeeded();
487
513
  /** Get or create guard state for this action */
488
514
  let state = this.guards.get(actionKey);
@@ -550,6 +576,9 @@ var ActionGuard = class {
550
576
  state.throttleTimer = void 0;
551
577
  }
552
578
  this.guards.delete(actionKey);
579
+ const accessIndex = this.accessOrder.indexOf(actionKey);
580
+ if (accessIndex !== -1) this.accessOrder.splice(accessIndex, 1);
581
+ if (this.guards.size === 0) this.stopAutoCleanup();
553
582
  }
554
583
  }
555
584
  /**
@@ -574,6 +603,8 @@ var ActionGuard = class {
574
603
  });
575
604
  /** Remove all guard states from memory */
576
605
  this.guards.clear();
606
+ this.accessOrder = [];
607
+ this.stopAutoCleanup();
577
608
  }
578
609
  /**
579
610
  * Get current guard state for debugging purposes
@@ -611,12 +642,7 @@ var ActionGuard = class {
611
642
  * @internal
612
643
  */
613
644
  destroy() {
614
- if (this.cleanupInterval) {
615
- clearInterval(this.cleanupInterval);
616
- this.cleanupInterval = void 0;
617
- }
618
645
  this.clearAll();
619
- this.accessOrder = [];
620
646
  }
621
647
  /**
622
648
  * ๐Ÿ†• Get statistics about active guards
@@ -669,27 +695,42 @@ var OperationQueue = class {
669
695
  * @returns Promise๋กœ ๋ž˜ํ•‘๋œ ์ž‘์—… ๊ฒฐ๊ณผ
670
696
  */
671
697
  enqueue(operation, priority = 0) {
672
- return new Promise((resolve, reject) => {
673
- const queuedOperation = {
674
- id: `${this.name}-${++this.operationCounter}`,
675
- operation,
676
- resolve,
677
- reject,
678
- priority,
679
- timestamp: Date.now()
680
- };
681
- let insertIndex = this.queue.length;
682
- for (let i = 0; i < this.queue.length; i++) {
683
- const item = this.queue[i];
684
- if (item && (item.priority || 0) < priority) {
685
- insertIndex = i;
686
- break;
698
+ return this.enqueueWithHandle(operation, priority).promise;
699
+ }
700
+ /** Enqueue an operation and retain a handle for pre-start cancellation. */
701
+ enqueueWithHandle(operation, priority = 0) {
702
+ let queuedOperation;
703
+ return {
704
+ promise: new Promise((resolve, reject) => {
705
+ queuedOperation = {
706
+ id: `${this.name}-${++this.operationCounter}`,
707
+ operation,
708
+ resolve,
709
+ reject,
710
+ priority,
711
+ timestamp: Date.now()
712
+ };
713
+ let insertIndex = this.queue.length;
714
+ for (let i = 0; i < this.queue.length; i++) {
715
+ const item = this.queue[i];
716
+ if (item && (item.priority || 0) < priority) {
717
+ insertIndex = i;
718
+ break;
719
+ }
687
720
  }
721
+ this.queue.splice(insertIndex, 0, queuedOperation);
722
+ if (this.processingPromise) this.notifyNewOperation();
723
+ this.processQueue();
724
+ }),
725
+ cancel: (reason = /* @__PURE__ */ new Error("Queue operation cancelled")) => {
726
+ const index = this.queue.indexOf(queuedOperation);
727
+ if (index === -1) return false;
728
+ this.queue.splice(index, 1);
729
+ queuedOperation.reject(reason);
730
+ this.notifyNewOperation();
731
+ return true;
688
732
  }
689
- this.queue.splice(insertIndex, 0, queuedOperation);
690
- if (this.processingPromise) this.notifyNewOperation();
691
- this.processQueue();
692
- });
733
+ };
693
734
  }
694
735
  /**
695
736
  * ๐Ÿ†• ํ ์ฒ˜๋ฆฌ ๋ฉ”์ธ ๋กœ์ง - ๋™์‹œ์„ฑ ์ œ์–ด ๋ฐ ๋น„๋™๊ธฐ ์ง€์›
@@ -795,12 +836,14 @@ var OperationQueue = class {
795
836
  /**
796
837
  * ํ ๋น„์šฐ๊ธฐ (ํ…Œ์ŠคํŠธ์šฉ)
797
838
  */
798
- clear() {
839
+ clear(options = {}) {
840
+ const rejectPending = options.rejectPending ?? true;
841
+ const reason = options.reason ?? /* @__PURE__ */ new Error("Queue cleared");
799
842
  this.queue.forEach((operation) => {
800
- operation.reject(/* @__PURE__ */ new Error("Queue cleared"));
843
+ if (rejectPending) operation.reject(reason);
844
+ else operation.resolve(void 0);
801
845
  });
802
846
  this.queue = [];
803
- this.processingPromise = null;
804
847
  this.pendingResolvers.splice(0).forEach((resolve) => resolve());
805
848
  }
806
849
  /**
@@ -817,8 +860,179 @@ var OperationQueue = class {
817
860
  }
818
861
  };
819
862
 
863
+ //#endregion
864
+ //#region src/errors.ts
865
+ /**
866
+ * Action payload ๊ฒ€์ฆ ์‹คํŒจ ์—๋Ÿฌ
867
+ *
868
+ * dispatch ์‹œ Zod ์Šคํ‚ค๋งˆ ๊ฒ€์ฆ์ด ์‹คํŒจํ•˜๋ฉด ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค.
869
+ * (validationMode๊ฐ€ 'strict'์ผ ๋•Œ๋งŒ throw)
870
+ *
871
+ * @example
872
+ * ```typescript
873
+ * try {
874
+ * dispatch('updateUser', { id: '', name: 'John' });
875
+ * } catch (error) {
876
+ * if (error instanceof ActionValidationError) {
877
+ * console.log('Action:', error.action);
878
+ * console.log('Issues:', error.issues);
879
+ * console.log('Formatted:', error.formattedErrors);
880
+ * }
881
+ * }
882
+ * ```
883
+ */
884
+ var ActionValidationError = class ActionValidationError extends Error {
885
+ /**
886
+ * @param action - ๊ฒ€์ฆ ์‹คํŒจํ•œ action ์ด๋ฆ„
887
+ * @param zodError - Zod ๊ฒ€์ฆ ์—๋Ÿฌ ๊ฐ์ฒด (ZodError compatible)
888
+ */
889
+ constructor(action, zodError) {
890
+ const message = `Action "${action}" payload validation failed: ${zodError && typeof zodError === "object" && "message" in zodError ? String(zodError.message) : "Validation failed"}`;
891
+ super(message);
892
+ this.name = "ActionValidationError";
893
+ this.action = action;
894
+ this.zodError = zodError;
895
+ Object.setPrototypeOf(this, ActionValidationError.prototype);
896
+ }
897
+ /**
898
+ * Zod ๊ฒ€์ฆ ์ด์Šˆ ๋ชฉ๋ก
899
+ */
900
+ get issues() {
901
+ if (this.zodError && typeof this.zodError === "object" && "issues" in this.zodError && Array.isArray(this.zodError.issues)) return this.zodError.issues;
902
+ return [];
903
+ }
904
+ /**
905
+ * ํฌ๋งท๋œ ์—๋Ÿฌ ๊ฐ์ฒด (ํ•„๋“œ๋ณ„ ์—๋Ÿฌ ๋ฉ”์‹œ์ง€)
906
+ */
907
+ get formattedErrors() {
908
+ if (this.zodError && typeof this.zodError === "object" && "format" in this.zodError && typeof this.zodError.format === "function") return this.zodError.format();
909
+ return {};
910
+ }
911
+ /**
912
+ * ํ”Œ๋žซ ์—๋Ÿฌ ๋งต (ํ•„๋“œ๋ช… โ†’ ์—๋Ÿฌ ๋ฉ”์‹œ์ง€ ๋ฐฐ์—ด)
913
+ */
914
+ get flattenedErrors() {
915
+ if (this.zodError && typeof this.zodError === "object" && "flatten" in this.zodError && typeof this.zodError.flatten === "function") return this.zodError.flatten();
916
+ return {
917
+ fieldErrors: {},
918
+ formErrors: []
919
+ };
920
+ }
921
+ /**
922
+ * ์ฒซ ๋ฒˆ์งธ ์—๋Ÿฌ ๋ฉ”์‹œ์ง€
923
+ */
924
+ get firstError() {
925
+ return this.issues[0]?.message;
926
+ }
927
+ /**
928
+ * ์—๋Ÿฌ ๋ฐœ์ƒ ํ•„๋“œ ๊ฒฝ๋กœ ๋ชฉ๋ก
929
+ */
930
+ get errorPaths() {
931
+ return this.issues.map((issue) => issue.path.map((p) => String(p)).join("."));
932
+ }
933
+ /**
934
+ * JSON ์ง๋ ฌํ™”
935
+ */
936
+ toJSON() {
937
+ return {
938
+ name: this.name,
939
+ action: this.action,
940
+ message: this.message,
941
+ issues: this.issues
942
+ };
943
+ }
944
+ };
945
+ /**
946
+ * Raised when a dispatch exceeds its configured wall-clock timeout.
947
+ * The underlying handler receives an aborted controller signal and the internal
948
+ * queue keeps draining it safely, while the caller is released immediately with
949
+ * this error.
950
+ */
951
+ var ActionTimeoutError = class ActionTimeoutError extends Error {
952
+ constructor(action, timeout) {
953
+ super(`Action "${action}" timed out after ${timeout}ms`);
954
+ this.action = action;
955
+ this.timeout = timeout;
956
+ this.name = "ActionTimeoutError";
957
+ Object.setPrototypeOf(this, ActionTimeoutError.prototype);
958
+ }
959
+ };
960
+ /** Raised when work is submitted after an ActionRegister begins shutdown. */
961
+ var ActionRegisterDestroyedError = class ActionRegisterDestroyedError extends Error {
962
+ constructor(registerName, state) {
963
+ super(`ActionRegister "${registerName}" is ${state} and cannot accept new work`);
964
+ this.registerName = registerName;
965
+ this.state = state;
966
+ this.name = "ActionRegisterDestroyedError";
967
+ Object.setPrototypeOf(this, ActionRegisterDestroyedError.prototype);
968
+ }
969
+ };
970
+ /**
971
+ * ActionValidationError ํƒ€์ž… ๊ฐ€๋“œ
972
+ */
973
+ function isActionValidationError(error) {
974
+ return error instanceof ActionValidationError;
975
+ }
976
+ /** ActionTimeoutError type guard. */
977
+ function isActionTimeoutError(error) {
978
+ return error instanceof ActionTimeoutError;
979
+ }
980
+ /** ActionRegisterDestroyedError type guard. */
981
+ function isActionRegisterDestroyedError(error) {
982
+ return error instanceof ActionRegisterDestroyedError;
983
+ }
984
+
820
985
  //#endregion
821
986
  //#region src/ActionRegister.ts
987
+ const dispatchOptionKeys = /* @__PURE__ */ new Set([
988
+ "autoAbort",
989
+ "debounce",
990
+ "executionMode",
991
+ "filter",
992
+ "immediate",
993
+ "queuePriority",
994
+ "result",
995
+ "retryOnError",
996
+ "signal",
997
+ "throttle",
998
+ "timeout"
999
+ ]);
1000
+ function isRecord(value) {
1001
+ return value !== null && typeof value === "object" && !Array.isArray(value);
1002
+ }
1003
+ function isOptionalNumber(value) {
1004
+ return value === void 0 || typeof value === "number";
1005
+ }
1006
+ function isOptionalBoolean(value) {
1007
+ return value === void 0 || typeof value === "boolean";
1008
+ }
1009
+ /**
1010
+ * Recognize the deprecated options-first void-action proxy call.
1011
+ *
1012
+ * Every supplied field is validated so ordinary payloads that merely overlap
1013
+ * one option name are not silently reinterpreted as dispatch options.
1014
+ */
1015
+ function isDispatchOptions(value) {
1016
+ if (!isRecord(value)) return false;
1017
+ const keys = Object.keys(value);
1018
+ if (keys.length === 0 || keys.some((key) => !dispatchOptionKeys.has(key))) return false;
1019
+ return keys.every((key) => {
1020
+ const optionValue = value[key];
1021
+ switch (key) {
1022
+ case "debounce":
1023
+ case "queuePriority":
1024
+ case "throttle":
1025
+ case "timeout": return isOptionalNumber(optionValue);
1026
+ case "immediate": return isOptionalBoolean(optionValue);
1027
+ case "executionMode": return optionValue === void 0 || optionValue === "sequential" || optionValue === "parallel" || optionValue === "race";
1028
+ case "signal": return optionValue === void 0 || isRecord(optionValue) && typeof optionValue.aborted === "boolean" && typeof optionValue.addEventListener === "function";
1029
+ case "retryOnError": return optionValue === void 0 || isRecord(optionValue) && typeof optionValue.maxAttempts === "number" && typeof optionValue.delay === "number";
1030
+ case "autoAbort": return optionValue === void 0 || isRecord(optionValue) && typeof optionValue.enabled === "boolean" && isOptionalBoolean(optionValue.allowHandlerAbort) && (optionValue.onControllerCreated === void 0 || typeof optionValue.onControllerCreated === "function");
1031
+ case "filter": return optionValue === void 0 || isRecord(optionValue) && (optionValue.handlerIds === void 0 || Array.isArray(optionValue.handlerIds) && optionValue.handlerIds.every((item) => typeof item === "string")) && (optionValue.excludeHandlerIds === void 0 || Array.isArray(optionValue.excludeHandlerIds) && optionValue.excludeHandlerIds.every((item) => typeof item === "string")) && (optionValue.priority === void 0 || isRecord(optionValue.priority)) && (optionValue.custom === void 0 || typeof optionValue.custom === "function");
1032
+ case "result": return optionValue === void 0 || isRecord(optionValue) && (optionValue.strategy === void 0 || optionValue.strategy === "first" || optionValue.strategy === "last" || optionValue.strategy === "all" || optionValue.strategy === "merge" || optionValue.strategy === "custom") && (optionValue.merger === void 0 || typeof optionValue.merger === "function") && isOptionalBoolean(optionValue.collect) && isOptionalNumber(optionValue.maxResults) && isOptionalBoolean(optionValue.includeErrors);
1033
+ }
1034
+ });
1035
+ }
822
1036
  /**
823
1037
  * Action Register for managing action handlers with priority-based execution
824
1038
  *
@@ -834,35 +1048,6 @@ var OperationQueue = class {
834
1048
  *
835
1049
  * @public
836
1050
  */
837
- /**
838
- * Type guard to determine if an object is DispatchOptions
839
- * Extracted as utility function for reuse and performance
840
- *
841
- * @param obj - Object to check
842
- * @returns True if object is DispatchOptions
843
- * @internal
844
- */
845
- function isDispatchOptions(obj) {
846
- if (!obj || typeof obj !== "object") return false;
847
- if ("debounce" in obj && typeof obj.debounce === "number") return true;
848
- if ("throttle" in obj && typeof obj.throttle === "number") return true;
849
- if ("executionMode" in obj) return true;
850
- if ("signal" in obj && obj.signal instanceof AbortSignal) return true;
851
- if ("immediate" in obj && typeof obj.immediate === "boolean") return true;
852
- if ("queuePriority" in obj && typeof obj.queuePriority === "number") return true;
853
- if ("timeout" in obj && typeof obj.timeout === "number") return true;
854
- if ("retryOnError" in obj && typeof obj.retryOnError === "object") return true;
855
- if ("autoAbort" in obj && typeof obj.autoAbort === "object") return true;
856
- if ("filter" in obj && typeof obj.filter === "object" && obj.filter !== null) {
857
- const filter = obj.filter;
858
- if ("handlerIds" in filter || "excludeHandlerIds" in filter || "priority" in filter || "custom" in filter) return true;
859
- }
860
- if ("result" in obj && typeof obj.result === "object" && obj.result !== null) {
861
- const result = obj.result;
862
- if ("strategy" in result || "merger" in result || "collect" in result || "maxResults" in result || "includeErrors" in result) return true;
863
- }
864
- return false;
865
- }
866
1051
  var ActionRegister = class {
867
1052
  constructor(config = {}) {
868
1053
  this.pipelines = /* @__PURE__ */ new Map();
@@ -874,6 +1059,11 @@ var ActionRegister = class {
874
1059
  this.handlerIdCounter = 0;
875
1060
  this.controllerPool = [];
876
1061
  this.maxControllerPoolSize = 10;
1062
+ this.lifecycleState = "active";
1063
+ this.lifecycleController = new AbortController();
1064
+ this.activeDispatches = /* @__PURE__ */ new Set();
1065
+ this.activeHandlerPromises = /* @__PURE__ */ new Set();
1066
+ this.dispatchConstructionDepth = 0;
877
1067
  this.name = config.name || "ActionRegister";
878
1068
  this.registryConfig = config.registry;
879
1069
  this.maxHandlersPerAction = config.registry?.maxHandlersPerAction ?? 1e3;
@@ -890,10 +1080,10 @@ var ActionRegister = class {
890
1080
  }
891
1081
  /**
892
1082
  * ๐Ÿ†• Action-based dispatcher
893
- *
1083
+ *
894
1084
  * Provides function-based access to actions for more convenient dispatching.
895
1085
  * Each action becomes a callable function that can be invoked directly.
896
- *
1086
+ *
897
1087
  * @example
898
1088
  * ```typescript
899
1089
  * interface MyActions extends ActionPayloadMap {
@@ -906,19 +1096,19 @@ var ActionRegister = class {
906
1096
  * // Function-based dispatching
907
1097
  * await registry.actions.userLogin({ userId: '123', email: 'test@example.com' });
908
1098
  * await registry.actions.resetApp();
1099
+ * await registry.actions.resetApp({ debounce: 100 }); // Legacy options-first form
1100
+ * await registry.actions.resetApp(undefined, { debounce: 100 });
909
1101
  * ```
910
1102
  *
911
1103
  * @public
912
1104
  */
913
1105
  get actions() {
914
1106
  if (!this._actionsProxy) this._actionsProxy = new Proxy({}, { get: (_target, prop) => {
915
- if (typeof prop === "string" && this.pipelines.has(prop)) {
916
- const actionKey = prop;
917
- return (payloadOrOptions, options) => {
918
- if (payloadOrOptions && isDispatchOptions(payloadOrOptions)) return this.dispatch(actionKey, void 0, payloadOrOptions);
919
- else return this.dispatch(actionKey, payloadOrOptions, options);
920
- };
921
- }
1107
+ const actionKey = prop;
1108
+ if (typeof prop === "string" && this.pipelines.has(actionKey)) return (payloadOrOptions, options) => {
1109
+ if (isDispatchOptions(payloadOrOptions)) return this.dispatch(actionKey, void 0, payloadOrOptions);
1110
+ return this.dispatch(actionKey, payloadOrOptions, options);
1111
+ };
922
1112
  } });
923
1113
  return this._actionsProxy;
924
1114
  }
@@ -935,6 +1125,10 @@ var ActionRegister = class {
935
1125
  *
936
1126
  * // Actions without payload
937
1127
  * const result = await registry.actionsWithResult.userLogout();
1128
+ * const debouncedResult = await registry.actionsWithResult.userLogout(
1129
+ * undefined,
1130
+ * { debounce: 100 }
1131
+ * );
938
1132
  *
939
1133
  * // With options
940
1134
  * const result = await registry.actionsWithResult.processData(
@@ -947,13 +1141,11 @@ var ActionRegister = class {
947
1141
  */
948
1142
  get actionsWithResult() {
949
1143
  if (!this._actionsWithResultProxy) this._actionsWithResultProxy = new Proxy({}, { get: (_target, prop) => {
950
- if (typeof prop === "string" && this.pipelines.has(prop)) {
951
- const actionKey = prop;
952
- return (payloadOrOptions, options) => {
953
- if (payloadOrOptions && isDispatchOptions(payloadOrOptions)) return this.dispatchWithResult(actionKey, void 0, payloadOrOptions);
954
- else return this.dispatchWithResult(actionKey, payloadOrOptions, options);
955
- };
956
- }
1144
+ const actionKey = prop;
1145
+ if (typeof prop === "string" && this.pipelines.has(actionKey)) return (payloadOrOptions, options) => {
1146
+ if (isDispatchOptions(payloadOrOptions)) return this.dispatchWithResult(actionKey, void 0, payloadOrOptions);
1147
+ return this.dispatchWithResult(actionKey, payloadOrOptions, options);
1148
+ };
957
1149
  } });
958
1150
  return this._actionsWithResultProxy;
959
1151
  }
@@ -973,6 +1165,7 @@ var ActionRegister = class {
973
1165
  * @public
974
1166
  */
975
1167
  register(action, handler, config = {}) {
1168
+ this.assertAcceptingWork();
976
1169
  const handlerId = config.id || this.generateHandlerId(action);
977
1170
  return this._performRegistrationSync(action, handler, config, handlerId);
978
1171
  }
@@ -985,6 +1178,15 @@ var ActionRegister = class {
985
1178
  console[level](`๐ŸŽฏ [${timestamp}] [${this.name}] ${message}`, data || "");
986
1179
  }
987
1180
  }
1181
+ assertAcceptingWork() {
1182
+ if (this.lifecycleState !== "active") throw new ActionRegisterDestroyedError(this.name, this.lifecycleState);
1183
+ }
1184
+ rejectedLifecyclePromise() {
1185
+ const error = new ActionRegisterDestroyedError(this.name, this.lifecycleState === "active" ? "destroyed" : this.lifecycleState);
1186
+ const rejected = Promise.reject(error);
1187
+ rejected.catch(() => {});
1188
+ return rejected;
1189
+ }
988
1190
  /**
989
1191
  * ๐Ÿ”ง Generate unique handler ID using optimized counter-based approach
990
1192
  */
@@ -1123,16 +1325,245 @@ var ActionRegister = class {
1123
1325
  });
1124
1326
  return unregister;
1125
1327
  }
1126
- async dispatch(action, payload, options) {
1127
- if (options?.immediate || !this.dispatchQueue) return this._performDispatch(action, payload, options);
1128
- else return this.dispatchQueue.enqueue(async () => {
1129
- return this._performDispatch(action, payload, options);
1328
+ dispatch(action, payload, options) {
1329
+ if (this.lifecycleState !== "active") return this.rejectedLifecyclePromise();
1330
+ const timeoutScope = this.createTimeoutScope(action, options);
1331
+ const dispatchHandlerPromises = /* @__PURE__ */ new Set();
1332
+ const attemptState = { count: 0 };
1333
+ const operation = async () => {
1334
+ if (!timeoutScope.options?.signal?.aborted) this.validatePayload(action, payload);
1335
+ return this.executeWithRetry(async () => {
1336
+ const executedHandlers = [];
1337
+ try {
1338
+ return await this._performDispatch(action, payload, timeoutScope.options, attemptState.count > 1, executedHandlers, dispatchHandlerPromises);
1339
+ } finally {
1340
+ this.cleanupOneTimeHandlers(action, executedHandlers, dispatchHandlerPromises);
1341
+ }
1342
+ }, timeoutScope.options, attemptState, void 0, () => this.getHandlerCount(action) > 0);
1343
+ };
1344
+ const hasTimingGuard = options?.debounce !== void 0 || options?.throttle !== void 0 || this.pipelines.get(action)?.some((handler) => handler.config.debounce !== void 0 || handler.config.throttle !== void 0) === true;
1345
+ let dispatchPromise;
1346
+ this.dispatchConstructionDepth += 1;
1347
+ try {
1348
+ if (timeoutScope.options?.immediate || hasTimingGuard || !this.dispatchQueue) dispatchPromise = operation();
1349
+ else {
1350
+ const queued = this.dispatchQueue.enqueueWithHandle(operation, timeoutScope.options?.queuePriority ?? 0);
1351
+ timeoutScope.onTimeout((error) => queued.cancel(error));
1352
+ dispatchPromise = queued.promise;
1353
+ }
1354
+ this.trackDispatchPromise(dispatchPromise);
1355
+ } finally {
1356
+ this.dispatchConstructionDepth -= 1;
1357
+ }
1358
+ const observedPromise = this.raceWithTimeout(dispatchPromise, timeoutScope, dispatchHandlerPromises).catch((error) => {
1359
+ this.invokeErrorHandler(error, action, payload, options, attemptState.count);
1360
+ throw error;
1361
+ });
1362
+ observedPromise.catch(() => {});
1363
+ return observedPromise;
1364
+ }
1365
+ /** Execute a dispatch operation with an optional whole-action retry policy. */
1366
+ async executeWithRetry(operation, options, attemptState, shouldRetryResult, canRetry = () => true) {
1367
+ const configuredAttempts = options?.retryOnError?.maxAttempts ?? 1;
1368
+ const maxAttempts = Number.isFinite(configuredAttempts) ? Math.max(1, Math.floor(configuredAttempts)) : 1;
1369
+ const retryDelay = Math.max(0, options?.retryOnError?.delay ?? 0);
1370
+ while (attemptState.count < maxAttempts) {
1371
+ attemptState.count += 1;
1372
+ try {
1373
+ const result = await operation();
1374
+ if (!(shouldRetryResult?.(result) ?? false) || attemptState.count >= maxAttempts || options?.signal?.aborted || !canRetry()) return result;
1375
+ } catch (error) {
1376
+ if (error instanceof ActionValidationError || attemptState.count >= maxAttempts || options?.signal?.aborted || !canRetry()) throw error;
1377
+ }
1378
+ await this.waitForRetry(retryDelay, options?.signal);
1379
+ }
1380
+ return operation();
1381
+ }
1382
+ trackDispatchPromise(promise) {
1383
+ this.activeDispatches.add(promise);
1384
+ const remove = () => this.activeDispatches.delete(promise);
1385
+ promise.then(remove, remove);
1386
+ return promise;
1387
+ }
1388
+ trackHandlerPromise(promise, dispatchHandlerPromises) {
1389
+ this.activeHandlerPromises.add(promise);
1390
+ dispatchHandlerPromises.add(promise);
1391
+ const remove = () => {
1392
+ this.activeHandlerPromises.delete(promise);
1393
+ dispatchHandlerPromises.delete(promise);
1394
+ };
1395
+ promise.then(remove, remove);
1396
+ return promise;
1397
+ }
1398
+ trackGlobalHandlerPromise(promise) {
1399
+ this.activeHandlerPromises.add(promise);
1400
+ const remove = () => this.activeHandlerPromises.delete(promise);
1401
+ promise.then(remove, remove);
1402
+ return promise;
1403
+ }
1404
+ /** Abort-aware retry delay so cancellation does not wait for the full backoff. */
1405
+ waitForRetry(delay, signal) {
1406
+ if (delay <= 0 || signal?.aborted) return Promise.resolve();
1407
+ return new Promise((resolve) => {
1408
+ const timer = setTimeout(finish, delay);
1409
+ const abort = () => finish();
1410
+ function finish() {
1411
+ clearTimeout(timer);
1412
+ signal?.removeEventListener("abort", abort);
1413
+ resolve();
1414
+ }
1415
+ signal?.addEventListener("abort", abort, { once: true });
1130
1416
  });
1131
1417
  }
1418
+ /** Build a wall-clock timeout that also participates in pipeline cancellation. */
1419
+ createTimeoutScope(action, options) {
1420
+ const hasTimeout = options?.timeout !== void 0 && Number.isFinite(options.timeout);
1421
+ const timeout = hasTimeout ? Math.max(0, options.timeout) : void 0;
1422
+ const timeoutController = hasTimeout ? new AbortController() : void 0;
1423
+ const signalCleanups = [];
1424
+ const timeoutCallbacks = /* @__PURE__ */ new Set();
1425
+ const signals = [
1426
+ this.lifecycleController.signal,
1427
+ options?.signal,
1428
+ timeoutController?.signal
1429
+ ].filter((candidate) => Boolean(candidate));
1430
+ let signal = signals[0];
1431
+ if (signals.length > 1) if (typeof AbortSignal.any === "function") signal = AbortSignal.any(signals);
1432
+ else {
1433
+ const mergedController = new AbortController();
1434
+ const forwardAbort = (source) => {
1435
+ if (!mergedController.signal.aborted) mergedController.abort(source.reason);
1436
+ };
1437
+ for (const source of signals) {
1438
+ if (source.aborted) {
1439
+ forwardAbort(source);
1440
+ break;
1441
+ }
1442
+ const listener = () => forwardAbort(source);
1443
+ source.addEventListener("abort", listener, { once: true });
1444
+ signalCleanups.push(() => source.removeEventListener("abort", listener));
1445
+ }
1446
+ signal = mergedController.signal;
1447
+ }
1448
+ let timer;
1449
+ const timeoutPromise = timeoutController && timeout !== void 0 ? new Promise((_, reject) => {
1450
+ timer = setTimeout(() => {
1451
+ const error = new ActionTimeoutError(String(action), timeout);
1452
+ timeoutController.abort(error);
1453
+ timeoutCallbacks.forEach((callback) => callback(error));
1454
+ reject(error);
1455
+ }, timeout);
1456
+ }) : void 0;
1457
+ return {
1458
+ options: {
1459
+ ...options,
1460
+ signal
1461
+ },
1462
+ timeoutPromise,
1463
+ onTimeout: (callback) => timeoutCallbacks.add(callback),
1464
+ cleanup: () => {
1465
+ if (timer !== void 0) clearTimeout(timer);
1466
+ timeoutCallbacks.clear();
1467
+ },
1468
+ cleanupSignals: () => signalCleanups.forEach((cleanup) => cleanup())
1469
+ };
1470
+ }
1471
+ /** Expose timeout failure while allowing the queued operation to drain safely. */
1472
+ raceWithTimeout(operation, scope, dispatchHandlerPromises) {
1473
+ const exposed = scope.timeoutPromise ? Promise.race([operation, scope.timeoutPromise]) : operation;
1474
+ const cleanupAfterStartedHandlers = () => {
1475
+ scope.cleanup();
1476
+ this.cleanupSignalsAfterStartedHandlers(scope.cleanupSignals, dispatchHandlerPromises);
1477
+ };
1478
+ exposed.then(cleanupAfterStartedHandlers, cleanupAfterStartedHandlers);
1479
+ return exposed;
1480
+ }
1481
+ cleanupSignalsAfterStartedHandlers(cleanup, dispatchHandlerPromises) {
1482
+ const handlersStillRunning = [...dispatchHandlerPromises];
1483
+ if (handlersStillRunning.length === 0) {
1484
+ cleanup();
1485
+ return;
1486
+ }
1487
+ Promise.allSettled(handlersStillRunning).then(cleanup);
1488
+ }
1489
+ /** Invoke the configured error handler without allowing it to replace the dispatch error. */
1490
+ invokeErrorHandler(error, action, payload, options, attempts) {
1491
+ const errorHandler = this.registryConfig?.errorHandler;
1492
+ if (!errorHandler) return;
1493
+ const normalizedError = error instanceof Error ? error : new Error(String(error));
1494
+ try {
1495
+ const handlerResult = errorHandler(normalizedError, {
1496
+ action: String(action),
1497
+ payload,
1498
+ options,
1499
+ attempts,
1500
+ phase: normalizedError instanceof ActionTimeoutError ? "timeout" : normalizedError instanceof ActionValidationError ? "validation" : "execution"
1501
+ });
1502
+ if (handlerResult && typeof handlerResult.then === "function") Promise.resolve(handlerResult).catch((handlerError) => {
1503
+ this.log("Global async error handler failed", handlerError, "warn");
1504
+ });
1505
+ } catch (handlerError) {
1506
+ this.log("Global error handler failed", handlerError, "warn");
1507
+ }
1508
+ }
1509
+ /**
1510
+ * Validate an action payload against the configured schema.
1511
+ * Shared by all dispatch paths so result collection cannot bypass validation.
1512
+ */
1513
+ validatePayload(action, payload) {
1514
+ if (!this.registryConfig?.schema || this.registryConfig.validateOnDispatch === false) return;
1515
+ const actionName = String(action);
1516
+ const actionSchema = this.registryConfig.schema[actionName];
1517
+ if (!actionSchema) return;
1518
+ let result;
1519
+ try {
1520
+ result = actionSchema.safeParse(payload);
1521
+ } catch (error) {
1522
+ throw new ActionValidationError(actionName, error);
1523
+ }
1524
+ if (result.success) return {
1525
+ passed: true,
1526
+ errors: []
1527
+ };
1528
+ const mode = this.registryConfig.validationMode ?? "strict";
1529
+ if (mode === "strict") throw new ActionValidationError(actionName, result.error);
1530
+ if (mode === "warn") {
1531
+ console.warn(`Action "${actionName}" payload validation failed:`, result.error.message);
1532
+ this.log(`Validation warning for action '${actionName}'`, { issues: result.error.issues }, "warn");
1533
+ }
1534
+ return {
1535
+ passed: false,
1536
+ errors: result.error.issues.map((issue) => issue.message)
1537
+ };
1538
+ }
1539
+ createAbortedExecutionResult(startTime, handlersSkipped = 0, validation) {
1540
+ const endTime = Date.now();
1541
+ return {
1542
+ success: false,
1543
+ aborted: true,
1544
+ abortReason: "Action dispatch aborted by signal",
1545
+ terminated: false,
1546
+ validation,
1547
+ result: void 0,
1548
+ successResults: [],
1549
+ results: [],
1550
+ failedResults: [],
1551
+ execution: {
1552
+ duration: endTime - startTime,
1553
+ handlersExecuted: 0,
1554
+ handlersSkipped,
1555
+ handlersFailed: 0,
1556
+ startTime,
1557
+ endTime
1558
+ },
1559
+ handlers: [],
1560
+ errors: []
1561
+ };
1562
+ }
1132
1563
  /**
1133
1564
  * ๐Ÿ†• ์‹ค์ œ ๋””์ŠคํŒจ์น˜ ์ž‘์—… ์ˆ˜ํ–‰ (ํ์—์„œ ํ˜ธ์ถœ๋จ)
1134
1565
  */
1135
- async _performDispatch(action, payload, options) {
1566
+ async _performDispatch(action, payload, options, skipGuards, executedHandlers, dispatchHandlerPromises) {
1136
1567
  this.log(`Starting dispatch for action '${String(action)}'`, {
1137
1568
  hasPayload: payload !== void 0,
1138
1569
  payloadType: payload?.constructor?.name || typeof payload,
@@ -1144,6 +1575,7 @@ var ActionRegister = class {
1144
1575
  if (options?.autoAbort?.onControllerCreated && autoAbortController) options.autoAbort.onControllerCreated(autoAbortController);
1145
1576
  if (effectiveSignal?.aborted) {
1146
1577
  this.log(`Dispatch aborted before execution for '${String(action)}'`);
1578
+ cleanup();
1147
1579
  return;
1148
1580
  }
1149
1581
  const pipeline = this.pipelines.get(action);
@@ -1159,6 +1591,7 @@ var ActionRegister = class {
1159
1591
  console.warn("๐Ÿ’ก Tip: Register a handler using registry.register() before dispatching this action.");
1160
1592
  console.warn("๐Ÿ“‹ Available actions:", Array.from(this.pipelines.keys()));
1161
1593
  this.log(`No handlers found for action '${String(action)}', dispatch cancelled`, {}, "warn");
1594
+ cleanup();
1162
1595
  return;
1163
1596
  }
1164
1597
  const filteredHandlers = options?.filter ? this.filterHandlers(pipeline, options.filter) : pipeline;
@@ -1179,17 +1612,32 @@ var ActionRegister = class {
1179
1612
  break;
1180
1613
  }
1181
1614
  }
1182
- if (debounceMs !== void 0) {
1183
- if (!await this.actionGuard.debounce(actionKey, debounceMs)) return;
1615
+ if (!skipGuards && debounceMs !== void 0) {
1616
+ if (!await this.actionGuard.debounce(actionKey, debounceMs)) {
1617
+ cleanup();
1618
+ return;
1619
+ }
1184
1620
  }
1185
- if (throttleMs !== void 0) {
1186
- if (!this.actionGuard.throttle(actionKey, throttleMs)) return;
1621
+ if (!skipGuards && throttleMs !== void 0) {
1622
+ if (!this.actionGuard.throttle(actionKey, throttleMs)) {
1623
+ cleanup();
1624
+ return;
1625
+ }
1626
+ }
1627
+ if (effectiveSignal?.aborted) {
1628
+ this.log(`Dispatch aborted during guard processing for '${String(action)}'`);
1629
+ cleanup();
1630
+ return;
1187
1631
  }
1188
1632
  const currentExecutionMode = options?.executionMode || this.actionExecutionModes.get(action) || this.executionMode;
1189
1633
  const context = {
1190
1634
  action: String(action),
1191
1635
  payload,
1192
- handlers: filteredHandlers,
1636
+ handlers: [...filteredHandlers],
1637
+ executedHandlers: [],
1638
+ deferOnceCleanup: true,
1639
+ signal: effectiveSignal ?? this.lifecycleController.signal,
1640
+ trackHandlerPromise: (promise) => this.trackHandlerPromise(promise, dispatchHandlerPromises),
1193
1641
  aborted: false,
1194
1642
  abortReason: void 0,
1195
1643
  currentIndex: 0,
@@ -1203,17 +1651,19 @@ var ActionRegister = class {
1203
1651
  };
1204
1652
  const abortHandler = effectiveSignal ? () => {
1205
1653
  context.aborted = true;
1206
- context.abortReason = "Action dispatch aborted by signal";
1654
+ context.abortReason = typeof effectiveSignal.reason === "string" ? effectiveSignal.reason : "Action dispatch aborted by signal";
1207
1655
  } : void 0;
1208
- if (effectiveSignal && abortHandler) effectiveSignal.addEventListener("abort", abortHandler);
1656
+ if (effectiveSignal && abortHandler) effectiveSignal.addEventListener("abort", abortHandler, { once: true });
1209
1657
  try {
1210
- await this.executePipeline(context, autoAbortController, options?.autoAbort);
1658
+ await this.executePipeline(context, dispatchHandlerPromises, autoAbortController, options?.autoAbort);
1211
1659
  this.log(`Pipeline execution succeeded for ${String(action)}`);
1212
1660
  } catch (error) {
1213
1661
  this.log(`Pipeline execution failed for ${String(action)}`, error, "error");
1214
1662
  throw error;
1215
1663
  } finally {
1216
- cleanup();
1664
+ executedHandlers?.push(...context.executedHandlers ?? []);
1665
+ if (effectiveSignal && abortHandler) effectiveSignal.removeEventListener("abort", abortHandler);
1666
+ this.cleanupSignalsAfterStartedHandlers(cleanup, dispatchHandlerPromises);
1217
1667
  }
1218
1668
  }
1219
1669
  /**
@@ -1229,41 +1679,70 @@ var ActionRegister = class {
1229
1679
  *
1230
1680
  * @public
1231
1681
  */
1232
- async dispatchWithResult(action, payload, options) {
1682
+ dispatchWithResult(action, payload, options) {
1683
+ if (this.lifecycleState !== "active") return this.rejectedLifecyclePromise();
1684
+ const timeoutScope = this.createTimeoutScope(action, options);
1685
+ const dispatchHandlerPromises = /* @__PURE__ */ new Set();
1686
+ const attemptState = { count: 0 };
1687
+ let validation;
1688
+ const operation = async () => {
1689
+ if (!timeoutScope.options?.signal?.aborted) validation = this.validatePayload(action, payload);
1690
+ return this.executeWithRetry(async () => {
1691
+ const executedHandlers = [];
1692
+ try {
1693
+ return await this._performDispatchWithResult(action, payload, timeoutScope.options, validation, attemptState.count > 1, executedHandlers, dispatchHandlerPromises);
1694
+ } finally {
1695
+ this.cleanupOneTimeHandlers(action, executedHandlers, dispatchHandlerPromises);
1696
+ }
1697
+ }, timeoutScope.options, attemptState, (result) => !result.success && !result.aborted && this.getHandlerCount(action) > 0, () => this.getHandlerCount(action) > 0);
1698
+ };
1699
+ const shouldQueue = !timeoutScope.options?.immediate && Boolean(this.dispatchQueue) && timeoutScope.options?.queuePriority !== void 0;
1700
+ let dispatchPromise;
1701
+ this.dispatchConstructionDepth += 1;
1702
+ try {
1703
+ if (shouldQueue) {
1704
+ const queued = this.dispatchQueue.enqueueWithHandle(operation, timeoutScope.options.queuePriority);
1705
+ timeoutScope.onTimeout((error) => queued.cancel(error));
1706
+ dispatchPromise = queued.promise;
1707
+ } else dispatchPromise = operation();
1708
+ this.trackDispatchPromise(dispatchPromise);
1709
+ } finally {
1710
+ this.dispatchConstructionDepth -= 1;
1711
+ }
1712
+ const observedPromise = this.raceWithTimeout(dispatchPromise, timeoutScope, dispatchHandlerPromises).then((result) => {
1713
+ if (!result.success && !result.aborted) {
1714
+ const terminalError = result.errors[result.errors.length - 1]?.error ?? /* @__PURE__ */ new Error(`Action "${String(action)}" failed`);
1715
+ this.invokeErrorHandler(terminalError, action, payload, options, attemptState.count);
1716
+ }
1717
+ return result;
1718
+ }, (error) => {
1719
+ this.invokeErrorHandler(error, action, payload, options, attemptState.count);
1720
+ throw error;
1721
+ });
1722
+ observedPromise.catch(() => {});
1723
+ return observedPromise;
1724
+ }
1725
+ async _performDispatchWithResult(action, payload, options, validation, skipGuards, executedHandlers, dispatchHandlerPromises) {
1233
1726
  const _startTime = Date.now();
1234
1727
  const [effectiveSignal, autoAbortController, cleanup] = this.createAbortSignal(options);
1235
1728
  if (options?.autoAbort?.onControllerCreated && autoAbortController) options.autoAbort.onControllerCreated(autoAbortController);
1236
- if (effectiveSignal?.aborted) return {
1237
- success: false,
1238
- aborted: true,
1239
- abortReason: "Action dispatch aborted by signal",
1240
- terminated: false,
1241
- result: void 0,
1242
- successResults: [],
1243
- results: [],
1244
- failedResults: [],
1245
- execution: {
1246
- duration: 0,
1247
- handlersExecuted: 0,
1248
- handlersSkipped: 0,
1249
- handlersFailed: 0,
1250
- startTime: _startTime,
1251
- endTime: _startTime
1252
- },
1253
- handlers: [],
1254
- errors: []
1255
- };
1729
+ if (effectiveSignal?.aborted) {
1730
+ cleanup();
1731
+ return this.createAbortedExecutionResult(_startTime, 0, validation);
1732
+ }
1256
1733
  const pipeline = this.pipelines.get(action);
1257
1734
  if (!pipeline || pipeline.length === 0) {
1258
1735
  const warningMessage = `โš ๏ธ Action '${String(action)}' has no registered handlers. This action will be ignored.`;
1259
1736
  console.warn(warningMessage);
1260
1737
  console.warn("๐Ÿ’ก Tip: Register a handler using registry.register() before dispatching this action.");
1261
1738
  console.warn("๐Ÿ“‹ Available actions:", Array.from(this.pipelines.keys()));
1739
+ cleanup();
1262
1740
  return {
1263
1741
  success: true,
1264
1742
  aborted: false,
1265
1743
  abortReason: void 0,
1266
1744
  terminated: false,
1745
+ validation,
1267
1746
  result: void 0,
1268
1747
  successResults: [],
1269
1748
  results: [],
@@ -1282,13 +1761,27 @@ var ActionRegister = class {
1282
1761
  }
1283
1762
  const filteredHandlers = options?.filter ? this.filterHandlers(pipeline, options.filter) : pipeline;
1284
1763
  const actionKey = String(action);
1285
- const guardResult = await this.applyActionGuardControlsWithResult(actionKey, filteredHandlers, options, _startTime, pipeline.length);
1286
- if (guardResult) return guardResult;
1764
+ const guardResult = skipGuards ? null : await this.applyActionGuardControlsWithResult(actionKey, filteredHandlers, options, _startTime, pipeline.length);
1765
+ if (effectiveSignal?.aborted) {
1766
+ cleanup();
1767
+ return this.createAbortedExecutionResult(_startTime, pipeline.length, validation);
1768
+ }
1769
+ if (guardResult) {
1770
+ cleanup();
1771
+ return {
1772
+ ...guardResult,
1773
+ validation
1774
+ };
1775
+ }
1287
1776
  const currentExecutionMode = options?.executionMode || this.actionExecutionModes.get(action) || this.executionMode;
1288
1777
  const context = {
1289
1778
  action: String(action),
1290
1779
  payload,
1291
- handlers: filteredHandlers,
1780
+ handlers: [...filteredHandlers],
1781
+ executedHandlers: [],
1782
+ deferOnceCleanup: true,
1783
+ signal: effectiveSignal ?? this.lifecycleController.signal,
1784
+ trackHandlerPromise: (promise) => this.trackHandlerPromise(promise, dispatchHandlerPromises),
1292
1785
  aborted: false,
1293
1786
  abortReason: void 0,
1294
1787
  currentIndex: 0,
@@ -1314,12 +1807,12 @@ var ActionRegister = class {
1314
1807
  });
1315
1808
  const abortHandler = effectiveSignal ? () => {
1316
1809
  context.aborted = true;
1317
- context.abortReason = "Action dispatch aborted by signal";
1810
+ context.abortReason = typeof effectiveSignal.reason === "string" ? effectiveSignal.reason : "Action dispatch aborted by signal";
1318
1811
  } : void 0;
1319
- if (effectiveSignal && abortHandler) effectiveSignal.addEventListener("abort", abortHandler);
1812
+ if (effectiveSignal && abortHandler) effectiveSignal.addEventListener("abort", abortHandler, { once: true });
1320
1813
  let errors = [];
1321
1814
  try {
1322
- await this.executePipeline(context, autoAbortController, options?.autoAbort);
1815
+ await this.executePipeline(context, dispatchHandlerPromises, autoAbortController, options?.autoAbort);
1323
1816
  errors = context.collectedErrors || [];
1324
1817
  const executedCount = Math.min(context.currentIndex + (context.aborted ? 0 : 1), filteredHandlers.length);
1325
1818
  for (let i = 0; i < executedCount; i++) {
@@ -1345,7 +1838,9 @@ var ActionRegister = class {
1345
1838
  if (handlerResult) handlerResult.executed = true;
1346
1839
  }
1347
1840
  } finally {
1348
- cleanup();
1841
+ executedHandlers.push(...context.executedHandlers ?? []);
1842
+ if (effectiveSignal && abortHandler) effectiveSignal.removeEventListener("abort", abortHandler);
1843
+ this.cleanupSignalsAfterStartedHandlers(cleanup, dispatchHandlerPromises);
1349
1844
  }
1350
1845
  const endTime = Date.now();
1351
1846
  const processedResult = this.processResults(context, options?.result);
@@ -1355,11 +1850,12 @@ var ActionRegister = class {
1355
1850
  error: err.error,
1356
1851
  expectedType: typeof processedResult
1357
1852
  }));
1358
- const executionResult = {
1853
+ return {
1359
1854
  success: !executionError && !context.aborted,
1360
1855
  aborted: context.aborted,
1361
1856
  abortReason: context.abortReason,
1362
1857
  terminated: context.terminated,
1858
+ validation,
1363
1859
  result: processedResult,
1364
1860
  successResults,
1365
1861
  results: context.results,
@@ -1380,9 +1876,6 @@ var ActionRegister = class {
1380
1876
  severity: "non-blocking"
1381
1877
  }))
1382
1878
  };
1383
- /** Clean up one-time handlers after execution */
1384
- this.cleanupOneTimeHandlers(action, context.handlers);
1385
- return executionResult;
1386
1879
  }
1387
1880
  /**
1388
1881
  * ๐Ÿ”ง Unified method for dispatchWithResult that returns ExecutionResult on guard rejection
@@ -1471,6 +1964,7 @@ var ActionRegister = class {
1471
1964
  getControllerFromPool(context, autoAbortController, autoAbortOptions) {
1472
1965
  let controller = this.controllerPool.pop();
1473
1966
  if (!controller) controller = {};
1967
+ controller.signal = context.signal ?? this.lifecycleController.signal;
1474
1968
  controller.abort = (reason) => {
1475
1969
  context.aborted = true;
1476
1970
  context.abortReason = reason;
@@ -1549,7 +2043,7 @@ var ActionRegister = class {
1549
2043
  return limitedResults[limitedResults.length - 1];
1550
2044
  }
1551
2045
  }
1552
- async executePipeline(context, autoAbortController, autoAbortOptions) {
2046
+ async executePipeline(context, dispatchHandlerPromises, autoAbortController, autoAbortOptions) {
1553
2047
  const createController = (_registration, _index) => {
1554
2048
  return this.getControllerFromPool(context, autoAbortController, autoAbortOptions);
1555
2049
  };
@@ -1565,28 +2059,27 @@ var ActionRegister = class {
1565
2059
  break;
1566
2060
  default: throw new Error(`Unknown execution mode: ${context.executionMode}`);
1567
2061
  }
1568
- this.cleanupOneTimeHandlers(context.action, context.handlers);
2062
+ if (!context.deferOnceCleanup) this.cleanupOneTimeHandlers(context.action, context.executedHandlers ?? [], dispatchHandlerPromises);
1569
2063
  }
1570
- cleanupOneTimeHandlers(action, executedHandlers) {
1571
- const pipeline = this.pipelines.get(action);
1572
- if (!pipeline) return;
2064
+ cleanupOneTimeHandlers(action, executedHandlers, dispatchHandlerPromises) {
1573
2065
  const oneTimeHandlers = executedHandlers.filter((reg) => reg.config.once);
1574
2066
  if (oneTimeHandlers.length === 0) return;
2067
+ const handlersStillRunning = [...dispatchHandlerPromises];
2068
+ const shouldDeferCleanup = handlersStillRunning.length > 0;
1575
2069
  oneTimeHandlers.forEach((registration) => {
1576
- const index = pipeline.findIndex((reg) => reg.id === registration.id);
1577
- if (index !== -1) {
1578
- pipeline.splice(index, 1);
1579
- if (this.registryConfig?.debug && true) console.log(`๐ŸŽฏ One-time handler removed: ${String(action)}`, {
2070
+ if (this.removeRegistration(action, registration, !shouldDeferCleanup)) {
2071
+ if (shouldDeferCleanup && typeof registration.config.cleanup === "function") {
2072
+ const cleanupPromise = Promise.allSettled(handlersStillRunning).then(() => {
2073
+ this.runRegistrationCleanup(action, registration);
2074
+ });
2075
+ this.trackGlobalHandlerPromise(cleanupPromise).catch(() => {});
2076
+ }
2077
+ this.log(`One-time handler removed: ${String(action)}`, {
1580
2078
  handlerId: registration.id,
1581
- remainingHandlers: pipeline.length,
1582
- registry: this.name
2079
+ remainingHandlers: this.getHandlerCount(action)
1583
2080
  });
1584
2081
  }
1585
2082
  });
1586
- if (pipeline.length === 0) {
1587
- this.pipelines.delete(action);
1588
- this.lastRegisteredTimestamps.delete(action);
1589
- }
1590
2083
  }
1591
2084
  /**
1592
2085
  * Get the number of registered handlers for an action
@@ -1639,8 +2132,13 @@ var ActionRegister = class {
1639
2132
  * @public
1640
2133
  */
1641
2134
  clearAction(action) {
2135
+ const pipeline = this.pipelines.get(action);
2136
+ if (pipeline) [...pipeline].forEach((registration) => {
2137
+ this.removeRegistration(action, registration);
2138
+ });
1642
2139
  this.pipelines.delete(action);
1643
2140
  this.lastRegisteredTimestamps.delete(action);
2141
+ this.actionGuard.clearGuards(String(action));
1644
2142
  }
1645
2143
  /**
1646
2144
  * Remove all handlers for all actions
@@ -1650,8 +2148,13 @@ var ActionRegister = class {
1650
2148
  * @public
1651
2149
  */
1652
2150
  clearAll() {
2151
+ [...this.pipelines.keys()].forEach((action) => {
2152
+ this.clearAction(action);
2153
+ });
1653
2154
  this.pipelines.clear();
1654
2155
  this.lastRegisteredTimestamps.clear();
2156
+ this.unregisterFunctions.clear();
2157
+ this.actionGuard.clearAll();
1655
2158
  }
1656
2159
  /**
1657
2160
  * Get the name of this action register
@@ -1780,29 +2283,36 @@ var ActionRegister = class {
1780
2283
  */
1781
2284
  createUnregisterFunction(action, handlerId, registration) {
1782
2285
  return () => {
1783
- const pipeline = this.pipelines.get(action);
1784
- if (!pipeline) return;
1785
- const index = pipeline.findIndex((reg) => reg.id === handlerId && reg === registration);
1786
- if (index !== -1) {
1787
- pipeline.splice(index, 1);
1788
- this.unregisterFunctions.delete(handlerId);
1789
- if (pipeline.length === 0) {
1790
- this.pipelines.delete(action);
1791
- this.lastRegisteredTimestamps.delete(action);
1792
- }
1793
- if (registration.config.cleanup && typeof registration.config.cleanup === "function") try {
1794
- registration.config.cleanup();
1795
- } catch (cleanupError) {
1796
- this.log(`Cleanup error during unregister: ${String(action)}`, cleanupError, "warn");
1797
- }
1798
- this.log(`Handler unregistered: ${String(action)}`, {
1799
- handlerId,
1800
- remainingHandlers: pipeline.length,
1801
- actionRemoved: pipeline.length === 0
1802
- });
1803
- }
2286
+ if (this.removeRegistration(action, registration)) this.log(`Handler unregistered: ${String(action)}`, {
2287
+ handlerId,
2288
+ remainingHandlers: this.getHandlerCount(action),
2289
+ actionRemoved: !this.pipelines.has(action)
2290
+ });
1804
2291
  };
1805
2292
  }
2293
+ /** Remove a registration and release every resource owned by it exactly once. */
2294
+ removeRegistration(action, registration, runCleanup = true) {
2295
+ const pipeline = this.pipelines.get(action);
2296
+ if (!pipeline) return false;
2297
+ const index = pipeline.findIndex((candidate) => candidate === registration);
2298
+ if (index === -1) return false;
2299
+ pipeline.splice(index, 1);
2300
+ this.unregisterFunctions.delete(registration.id);
2301
+ if (runCleanup) this.runRegistrationCleanup(action, registration);
2302
+ if (pipeline.length === 0) {
2303
+ this.pipelines.delete(action);
2304
+ this.lastRegisteredTimestamps.delete(action);
2305
+ }
2306
+ return true;
2307
+ }
2308
+ runRegistrationCleanup(action, registration) {
2309
+ if (!registration.config.cleanup) return;
2310
+ try {
2311
+ registration.config.cleanup();
2312
+ } catch (cleanupError) {
2313
+ this.log(`Cleanup error while removing handler: ${String(action)}`, cleanupError, "warn");
2314
+ }
2315
+ }
1806
2316
  /**
1807
2317
  * Gets the total count of registered unregister functions
1808
2318
  *
@@ -1822,29 +2332,77 @@ var ActionRegister = class {
1822
2332
  hasUnregisterFunction(handlerId) {
1823
2333
  return this.unregisterFunctions.has(handlerId);
1824
2334
  }
1825
- /**
1826
- * ๐Ÿ†• Destroy method for comprehensive cleanup
1827
- *
1828
- * Cleans up all internal resources including pipelines, guards, queues, and statistics.
1829
- * Should be called when the ActionRegister is no longer needed to prevent memory leaks.
1830
- *
1831
- * @public
1832
- */
1833
- destroy() {
1834
- this.unregisterFunctions.clear();
1835
- for (const [action, pipeline] of this.pipelines.entries()) for (const registration of pipeline) if (registration.config.cleanup && typeof registration.config.cleanup === "function") try {
1836
- registration.config.cleanup();
1837
- } catch (cleanupError) {
1838
- this.log(`Cleanup error for handler during destroy: ${String(action)}`, cleanupError, "warn");
2335
+ /** Reject queued dispatches without releasing registered handlers. */
2336
+ cancelPendingDispatches() {
2337
+ this.dispatchQueue?.clear({ rejectPending: true });
2338
+ }
2339
+ beginShutdown() {
2340
+ if (this.destroyAsyncPromise) return this.destroyAsyncPromise;
2341
+ if (this.lifecycleState === "destroyed") {
2342
+ this.destroyAsyncPromise = Promise.resolve();
2343
+ return this.destroyAsyncPromise;
1839
2344
  }
1840
- this.pipelines.clear();
1841
- this.lastRegisteredTimestamps.clear();
2345
+ this.lifecycleState = "closing";
2346
+ const shutdownError = new ActionRegisterDestroyedError(this.name, "closing");
2347
+ let resolveShutdown;
2348
+ let rejectShutdown;
2349
+ this.destroyAsyncPromise = new Promise((resolve, reject) => {
2350
+ resolveShutdown = resolve;
2351
+ rejectShutdown = reject;
2352
+ });
2353
+ if (!this.lifecycleController.signal.aborted) this.lifecycleController.abort(shutdownError);
1842
2354
  this.actionGuard.destroy();
1843
- this.dispatchQueue?.clear?.();
2355
+ this.dispatchQueue?.clear({
2356
+ rejectPending: true,
2357
+ reason: shutdownError
2358
+ });
2359
+ if (this.dispatchConstructionDepth === 0 && this.activeDispatches.size === 0 && this.activeHandlerPromises.size === 0) {
2360
+ this.finalizeDestroy();
2361
+ resolveShutdown();
2362
+ return this.destroyAsyncPromise;
2363
+ }
2364
+ const drainAndFinalize = async () => {
2365
+ while (this.activeDispatches.size > 0 || this.activeHandlerPromises.size > 0) await Promise.allSettled([...this.activeDispatches, ...this.activeHandlerPromises]);
2366
+ this.finalizeDestroy();
2367
+ };
2368
+ Promise.resolve().then(drainAndFinalize).then(resolveShutdown, rejectShutdown);
2369
+ this.destroyAsyncPromise.catch((error) => {
2370
+ this.log("ActionRegister async destroy failed", error, "warn");
2371
+ });
2372
+ return this.destroyAsyncPromise;
2373
+ }
2374
+ finalizeDestroy() {
2375
+ if (this.lifecycleState === "destroyed") return;
2376
+ this.clearAll();
1844
2377
  this.actionExecutionModes.clear();
1845
2378
  this.controllerPool.length = 0;
2379
+ this.lifecycleState = "destroyed";
1846
2380
  this.log("ActionRegister destroyed");
1847
2381
  }
2382
+ /**
2383
+ * ๐Ÿ†• Destroy method for comprehensive cleanup
2384
+ *
2385
+ * Begins terminal cleanup of pipelines, guards, queues, and statistics. Cleanup
2386
+ * remains synchronous when no work has started; otherwise active handlers drain
2387
+ * in the background. Use destroyAsync() when completion must be observed.
2388
+ *
2389
+ * @public
2390
+ */
2391
+ destroy() {
2392
+ this.beginShutdown();
2393
+ }
2394
+ /**
2395
+ * Begin terminal shutdown and resolve after all started handlers have settled
2396
+ * and their registered cleanup functions have run.
2397
+ *
2398
+ * Repeated calls return the same promise. New registrations and dispatches are
2399
+ * rejected as soon as shutdown begins.
2400
+ *
2401
+ * @public
2402
+ */
2403
+ destroyAsync() {
2404
+ return this.beginShutdown();
2405
+ }
1848
2406
  };
1849
2407
 
1850
2408
  //#endregion
@@ -1933,12 +2491,18 @@ function createActionHandler(registry, action, handler, config) {
1933
2491
  let currentUnregister;
1934
2492
  let isRegistered = false;
1935
2493
  return {
2494
+ /**
2495
+ * Register the handler and return cleanup function
2496
+ */
1936
2497
  register() {
1937
2498
  if (isRegistered && currentUnregister) currentUnregister();
1938
2499
  currentUnregister = registry.register(action, handler, finalConfig);
1939
2500
  isRegistered = true;
1940
2501
  return currentUnregister;
1941
2502
  },
2503
+ /**
2504
+ * Unregister the handler if currently registered
2505
+ */
1942
2506
  unregister() {
1943
2507
  if (isRegistered && currentUnregister) {
1944
2508
  currentUnregister();
@@ -1946,6 +2510,9 @@ function createActionHandler(registry, action, handler, config) {
1946
2510
  isRegistered = false;
1947
2511
  }
1948
2512
  },
2513
+ /**
2514
+ * Register and return cleanup function (React useEffect pattern)
2515
+ */
1949
2516
  registerWithCleanup() {
1950
2517
  const unregisterFn = this.register();
1951
2518
  return () => {
@@ -1962,18 +2529,33 @@ function createActionHandler(registry, action, handler, config) {
1962
2529
  * Provides debugging and development helpers specifically for React environments.
1963
2530
  */
1964
2531
  const ReactDevUtils = {
2532
+ /**
2533
+ * Enable detailed React integration debugging
2534
+ */
1965
2535
  enableDebugMode() {
1966
2536
  if (typeof window !== "undefined") window.__CONTEXT_ACTION_REACT_DEBUG__ = true;
1967
2537
  },
2538
+ /**
2539
+ * Disable React integration debugging
2540
+ */
1968
2541
  disableDebugMode() {
1969
2542
  if (typeof window !== "undefined") window.__CONTEXT_ACTION_REACT_DEBUG__ = false;
1970
2543
  },
2544
+ /**
2545
+ * Check if React debug mode is enabled
2546
+ */
1971
2547
  isDebugMode() {
1972
2548
  return typeof window !== "undefined" && Boolean(window.__CONTEXT_ACTION_REACT_DEBUG__);
1973
2549
  },
2550
+ /**
2551
+ * Log React-specific debugging information
2552
+ */
1974
2553
  log(component, action, message, data) {
1975
2554
  if (this.isDebugMode()) console.log(`๐ŸŽฏ [React-ActionRegister] [${component}] ${action}: ${message}`, data || "");
1976
2555
  },
2556
+ /**
2557
+ * Get React integration statistics
2558
+ */
1977
2559
  getStats(registry) {
1978
2560
  const registryInfo = registry.getRegistryInfo();
1979
2561
  let reactHandlers = 0;
@@ -2025,5 +2607,140 @@ function isReactActionError(error) {
2025
2607
  }
2026
2608
 
2027
2609
  //#endregion
2028
- export { ActionGuard, ActionRegister, ReactActionError, ReactDevUtils, createActionHandler, executeParallel, executeRace, executeSequential, isReactActionError };
2610
+ //#region src/action-schema.ts
2611
+ /**
2612
+ * Zod ์Šคํ‚ค๋งˆ๋ฅผ JSON Schema๋กœ ๋ณ€ํ™˜ (Zod 4 ๋„ค์ดํ‹ฐ๋ธŒ API)
2613
+ *
2614
+ * @param schema - Zod ์Šคํ‚ค๋งˆ
2615
+ * @returns JSON Schema (draft-7)
2616
+ */
2617
+ function zodToJsonSchema(schema, zodModule) {
2618
+ return zodModule.toJSONSchema(schema, {
2619
+ target: "draft-7",
2620
+ metadata: zodModule.globalRegistry
2621
+ });
2622
+ }
2623
+ /**
2624
+ * Zod ์Šคํ‚ค๋งˆ ๊ธฐ๋ฐ˜ Action ์ •์˜
2625
+ *
2626
+ * defineTool ํŒจํ„ด์„ ๊ธฐ๋ฐ˜์œผ๋กœ context-action์— ๋งž๊ฒŒ ๊ตฌํ˜„:
2627
+ * - Single Source of Truth: Zod ์Šคํ‚ค๋งˆ๋กœ ํƒ€์ž… + ๊ฒ€์ฆ + ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ ํ†ตํ•ฉ
2628
+ * - ๋Ÿฐํƒ€์ž„ ๊ฒ€์ฆ: validate(), safeParse()
2629
+ * - Tool Chain ํ˜ธํ™˜: toMCP(), toOpenAI(), toAnthropic()
2630
+ *
2631
+ * @param options - Action ์ •์˜ ์˜ต์…˜
2632
+ * @param zodModule - Zod ๋ชจ๋“ˆ (peerDependency๋กœ ์ฃผ์ž…)
2633
+ * @returns UnifiedAction ์ธ์Šคํ„ด์Šค
2634
+ *
2635
+ * @example
2636
+ * ```typescript
2637
+ * import { z } from 'zod';
2638
+ * import { defineAction } from '@context-action/core';
2639
+ *
2640
+ * const updateUserAction = defineAction({
2641
+ * name: 'updateUser',
2642
+ * description: 'Update user profile',
2643
+ * parameters: z.object({
2644
+ * id: z.string().min(1).meta({ description: 'User ID' }),
2645
+ * name: z.string().min(2).max(50).meta({ description: 'User name' }),
2646
+ * email: z.string().email().optional(),
2647
+ * }),
2648
+ * }, z);
2649
+ *
2650
+ * // ๊ฒ€์ฆ
2651
+ * const validated = updateUserAction.validate({ id: '123', name: 'John' });
2652
+ *
2653
+ * // Tool chain ๋ณ€ํ™˜
2654
+ * const mcpTool = updateUserAction.toMCP();
2655
+ * ```
2656
+ */
2657
+ function defineAction(options, zodModule) {
2658
+ const { name, description, parameters } = options;
2659
+ const jsonSchema = zodToJsonSchema(parameters, zodModule);
2660
+ return {
2661
+ name,
2662
+ description,
2663
+ zodSchema: parameters,
2664
+ jsonSchema,
2665
+ validate: (payload) => {
2666
+ return parameters.parse(payload);
2667
+ },
2668
+ safeParse: (payload) => {
2669
+ return parameters.safeParse(payload);
2670
+ },
2671
+ toJSONSchema: () => jsonSchema,
2672
+ toMCP: () => ({
2673
+ name,
2674
+ description,
2675
+ inputSchema: jsonSchema
2676
+ }),
2677
+ toOpenAI: () => ({
2678
+ type: "function",
2679
+ function: {
2680
+ name,
2681
+ description,
2682
+ parameters: {
2683
+ type: "object",
2684
+ properties: jsonSchema.properties ?? {},
2685
+ required: jsonSchema.required
2686
+ }
2687
+ }
2688
+ }),
2689
+ toAnthropic: () => ({
2690
+ name,
2691
+ description,
2692
+ input_schema: jsonSchema
2693
+ })
2694
+ };
2695
+ }
2696
+ /**
2697
+ * ๋‹ค์ค‘ Action ์Šคํ‚ค๋งˆ ์ƒ์„ฑ
2698
+ *
2699
+ * ์—ฌ๋Ÿฌ defineAction์„ ๋ฌถ์–ด์„œ ActionSchemaMap ์ƒ์„ฑ
2700
+ *
2701
+ * @param actions - UnifiedAction ๋งต
2702
+ * @returns ActionSchemaMap
2703
+ *
2704
+ * @example
2705
+ * ```typescript
2706
+ * const userActionSchema = createActionSchema({
2707
+ * updateUser: defineAction({ ... }, z),
2708
+ * deleteUser: defineAction({ ... }, z),
2709
+ * });
2710
+ *
2711
+ * type UserActions = InferActionPayloadMap<typeof userActionSchema>;
2712
+ * ```
2713
+ */
2714
+ function createActionSchema(actions) {
2715
+ return actions;
2716
+ }
2717
+ /**
2718
+ * Zod ๋ชจ๋“ˆ์„ ๋ฐ”์ธ๋”ฉํ•œ defineAction ํŒฉํ† ๋ฆฌ ์ƒ์„ฑ
2719
+ *
2720
+ * ๋งค๋ฒˆ z ๋ชจ๋“ˆ์„ ์ „๋‹ฌํ•˜์ง€ ์•Š์•„๋„ ๋˜๋„๋ก ํŒฉํ† ๋ฆฌ ํŒจํ„ด ์ œ๊ณต
2721
+ *
2722
+ * @param zodModule - Zod ๋ชจ๋“ˆ
2723
+ * @returns defineAction ํ•จ์ˆ˜ (z ๋ฐ”์ธ๋”ฉ๋จ)
2724
+ *
2725
+ * @example
2726
+ * ```typescript
2727
+ * import { z } from 'zod';
2728
+ * import { createActionFactory } from '@context-action/core';
2729
+ *
2730
+ * const defineAction = createActionFactory(z);
2731
+ *
2732
+ * const updateUser = defineAction({
2733
+ * name: 'updateUser',
2734
+ * parameters: z.object({ id: z.string() }),
2735
+ * });
2736
+ * ```
2737
+ */
2738
+ function createActionFactory(zodModule) {
2739
+ return (options) => {
2740
+ return defineAction(options, zodModule);
2741
+ };
2742
+ }
2743
+
2744
+ //#endregion
2745
+ export { ActionGuard, ActionRegister, ActionRegisterDestroyedError, ActionTimeoutError, ActionValidationError, ReactActionError, ReactDevUtils, createActionFactory, createActionHandler, createActionSchema, defineAction, executeParallel, executeRace, executeSequential, isActionRegisterDestroyedError, isActionTimeoutError, isActionValidationError, isReactActionError, zodToJsonSchema };
2029
2746
  //# sourceMappingURL=index.js.map