@context-action/core 0.8.1 โ†’ 0.8.6

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/README.md CHANGED
@@ -1,6 +1,8 @@
1
1
  # @context-action/core
2
2
 
3
- Type-safe action pipeline management library for JavaScript/TypeScript applications with advanced filtering, performance optimizations, and React integration support.
3
+ Type-safe action pipeline management library for **vanilla JavaScript/TypeScript** applications with advanced filtering, performance optimizations, and optional React integration support.
4
+
5
+ > **โœจ Framework-Agnostic**: Works with vanilla JavaScript, React, Vue, Svelte, or any JavaScript environment. No framework dependencies required!
4
6
 
5
7
  ## Installation
6
8
 
@@ -10,6 +12,15 @@ npm install @context-action/core
10
12
  pnpm install @context-action/core
11
13
  ```
12
14
 
15
+ ### CDN (for quick prototyping)
16
+
17
+ ```html
18
+ <script type="module">
19
+ import { ActionRegister } from 'https://esm.sh/@context-action/core@latest';
20
+ // Your code here
21
+ </script>
22
+ ```
23
+
13
24
  ## Quick Start
14
25
 
15
26
  ```typescript
@@ -42,6 +53,94 @@ await actions.dispatch('increment');
42
53
  await actions.dispatch('setCount', 42);
43
54
  ```
44
55
 
56
+ ## ๐ŸŒŸ Vanilla JavaScript Support
57
+
58
+ **@context-action/core works perfectly with vanilla JavaScript!** No React, Vue, or any framework required.
59
+
60
+ ### Browser Example (HTML + JavaScript)
61
+
62
+ ```html
63
+ <!DOCTYPE html>
64
+ <html>
65
+ <head>
66
+ <title>Context-Action Example</title>
67
+ </head>
68
+ <body>
69
+ <div id="counter">0</div>
70
+ <button id="increment">Increment</button>
71
+
72
+ <script type="module">
73
+ import { ActionRegister } from 'https://esm.sh/@context-action/core@latest';
74
+
75
+ // Simple store
76
+ class Store {
77
+ constructor(initialState) {
78
+ this.state = initialState;
79
+ this.listeners = new Set();
80
+ }
81
+ getValue() { return this.state; }
82
+ setValue(newState) {
83
+ this.state = newState;
84
+ this.listeners.forEach(fn => fn(this.state));
85
+ }
86
+ subscribe(listener) {
87
+ this.listeners.add(listener);
88
+ return () => this.listeners.delete(listener);
89
+ }
90
+ }
91
+
92
+ // Create store and actions
93
+ const counterStore = new Store({ count: 0 });
94
+ const actions = new ActionRegister({ name: 'Counter' });
95
+
96
+ // Register handler
97
+ actions.register('increment', () => {
98
+ const current = counterStore.getValue();
99
+ counterStore.setValue({ count: current.count + 1 });
100
+ });
101
+
102
+ // Subscribe to updates
103
+ counterStore.subscribe(state => {
104
+ document.getElementById('counter').textContent = state.count;
105
+ });
106
+
107
+ // Wire up button
108
+ document.getElementById('increment').onclick = () => {
109
+ actions.dispatch('increment');
110
+ };
111
+ </script>
112
+ </body>
113
+ </html>
114
+ ```
115
+
116
+ ### Node.js Example
117
+
118
+ ```javascript
119
+ import { ActionRegister } from '@context-action/core';
120
+
121
+ const actions = new ActionRegister({ name: 'MyApp' });
122
+
123
+ actions.register('processData', async (data, controller) => {
124
+ console.log('Processing:', data);
125
+
126
+ // Business logic here
127
+ const result = await someAsyncOperation(data);
128
+
129
+ controller.setResult(result);
130
+ }, { priority: 100 });
131
+
132
+ // Dispatch action
133
+ const result = await actions.dispatchWithResult('processData', {
134
+ input: 'example'
135
+ });
136
+
137
+ console.log('Result:', result.successResults);
138
+ ```
139
+
140
+ **๐Ÿ“š Learn More:**
141
+ - [Vanilla JavaScript Guide](../../docs/en/guide/vanilla-js-guide.md) - Complete guide with examples
142
+ - [Live Examples](../../examples/vanilla-js/) - Interactive HTML examples (counter, todo app)
143
+
45
144
  ### Memory Management
46
145
 
47
146
  ```typescript
@@ -489,5 +588,7 @@ Apache-2.0
489
588
 
490
589
  - [Main Repository](https://github.com/mineclover/context-action)
491
590
  - [Documentation](https://mineclover.github.io/context-action/)
492
- - [React Package](../react/README.md)
493
- - [Examples](../../example/README.md)
591
+ - [Vanilla JS Guide](../../docs/en/guide/vanilla-js-guide.md) - Complete vanilla JavaScript guide
592
+ - [Vanilla JS Examples](../../examples/vanilla-js/) - Interactive examples (counter, todo app)
593
+ - [React Package](../react/README.md) - React integration
594
+ - [Examples](../../example/README.md) - React example application
package/dist/index.cjs CHANGED
@@ -315,27 +315,91 @@ var ActionGuard = class {
315
315
  this.guards = /* @__PURE__ */ new Map();
316
316
  this.maxIdleTime = 6e4;
317
317
  this.cleanupIntervalMs = 3e4;
318
+ this.maxGuards = 1e3;
319
+ this.accessOrder = [];
318
320
  if (autoCleanup) this.startAutoCleanup();
319
321
  }
320
322
  /**
321
323
  * Start automatic cleanup of idle guard states
322
- *
324
+ *
323
325
  * @internal
324
326
  */
325
327
  startAutoCleanup() {
326
328
  this.cleanupInterval = setInterval(() => {
327
- const now = Date.now();
328
- const keysToDelete = [];
329
- this.guards.forEach((state, key) => {
329
+ this.performCleanup();
330
+ }, this.cleanupIntervalMs);
331
+ }
332
+ /**
333
+ * ๐Ÿ”ง Optimized cleanup with early exit and batched operations
334
+ *
335
+ * @internal
336
+ */
337
+ performCleanup() {
338
+ const guardCount = this.guards.size;
339
+ if (guardCount === 0) return;
340
+ const now = Date.now();
341
+ const keysToDelete = [];
342
+ if (guardCount <= 10) this.guards.forEach((state, key) => {
343
+ const isIdle = now - state.lastExecuted > this.maxIdleTime;
344
+ const hasActiveTimers = state.debounceTimer || state.throttleTimer;
345
+ if (isIdle && !hasActiveTimers) keysToDelete.push(key);
346
+ });
347
+ else {
348
+ const entriesToCheck = Math.min(this.accessOrder.length, Math.ceil(guardCount / 4));
349
+ for (let i = 0; i < entriesToCheck; i++) {
350
+ const key = this.accessOrder[i];
351
+ if (!key) continue;
352
+ const state = this.guards.get(key);
353
+ if (!state) {
354
+ keysToDelete.push(key);
355
+ continue;
356
+ }
330
357
  const isIdle = now - state.lastExecuted > this.maxIdleTime;
331
358
  const hasActiveTimers = state.debounceTimer || state.throttleTimer;
332
359
  if (isIdle && !hasActiveTimers) keysToDelete.push(key);
333
- });
334
- if (keysToDelete.length > 0) {
335
- keysToDelete.forEach((key) => this.guards.delete(key));
336
- if (typeof process !== "undefined" && process.env?.DEBUG_CONTEXT_ACTION) console.debug(`[ActionGuard] Cleaned up ${keysToDelete.length} idle guards`);
337
360
  }
338
- }, this.cleanupIntervalMs);
361
+ }
362
+ if (keysToDelete.length > 0) {
363
+ keysToDelete.forEach((key) => {
364
+ this.guards.delete(key);
365
+ const accessIndex = this.accessOrder.indexOf(key);
366
+ if (accessIndex !== -1) this.accessOrder.splice(accessIndex, 1);
367
+ });
368
+ if (typeof process !== "undefined" && process.env?.DEBUG_CONTEXT_ACTION) console.debug(`[ActionGuard] Cleaned up ${keysToDelete.length} idle guards`);
369
+ }
370
+ }
371
+ /**
372
+ * ๐Ÿ”ง Update access order for LRU tracking
373
+ *
374
+ * @internal
375
+ */
376
+ updateAccessOrder(key) {
377
+ const existingIndex = this.accessOrder.indexOf(key);
378
+ if (existingIndex !== -1) this.accessOrder.splice(existingIndex, 1);
379
+ this.accessOrder.push(key);
380
+ }
381
+ /**
382
+ * ๐Ÿ”ง Evict oldest guards if max limit exceeded
383
+ *
384
+ * @internal
385
+ */
386
+ evictIfNeeded() {
387
+ if (this.guards.size >= this.maxGuards) {
388
+ const evictCount = Math.ceil(this.maxGuards * .1);
389
+ this.accessOrder.slice(0, evictCount).forEach((key) => {
390
+ const state = this.guards.get(key);
391
+ if (state) {
392
+ if (state.debounceTimer) {
393
+ clearTimeout(state.debounceTimer);
394
+ if (state.debounceResolve) state.debounceResolve(false);
395
+ }
396
+ if (state.throttleTimer) clearTimeout(state.throttleTimer);
397
+ }
398
+ this.guards.delete(key);
399
+ });
400
+ this.accessOrder = this.accessOrder.slice(evictCount);
401
+ if (typeof process !== "undefined" && process.env?.DEBUG_CONTEXT_ACTION) console.debug(`[ActionGuard] Evicted ${evictCount} oldest guards due to limit`);
402
+ }
339
403
  }
340
404
  /**
341
405
  * Apply debouncing to an action
@@ -360,6 +424,7 @@ var ActionGuard = class {
360
424
  * @internal
361
425
  */
362
426
  async debounce(actionKey, debounceMs) {
427
+ this.evictIfNeeded();
363
428
  /** Get or create guard state for this action */
364
429
  let state = this.guards.get(actionKey);
365
430
  if (!state) {
@@ -374,6 +439,7 @@ var ActionGuard = class {
374
439
  };
375
440
  this.guards.set(actionKey, state);
376
441
  }
442
+ this.updateAccessOrder(actionKey);
377
443
  /** Clear any existing debounce timer to restart the delay period */
378
444
  if (state.debounceTimer) {
379
445
  clearTimeout(state.debounceTimer);
@@ -418,6 +484,7 @@ var ActionGuard = class {
418
484
  * @internal
419
485
  */
420
486
  throttle(actionKey, throttleMs) {
487
+ this.evictIfNeeded();
421
488
  /** Get or create guard state for this action */
422
489
  let state = this.guards.get(actionKey);
423
490
  if (!state) {
@@ -432,6 +499,7 @@ var ActionGuard = class {
432
499
  };
433
500
  this.guards.set(actionKey, state);
434
501
  }
502
+ this.updateAccessOrder(actionKey);
435
503
  const now = Date.now();
436
504
  const timeSinceLastExecution = now - state.lastExecuted;
437
505
  /** Check if enough time has passed since last execution */
@@ -549,6 +617,7 @@ var ActionGuard = class {
549
617
  this.cleanupInterval = void 0;
550
618
  }
551
619
  this.clearAll();
620
+ this.accessOrder = [];
552
621
  }
553
622
  /**
554
623
  * ๐Ÿ†• Get statistics about active guards
@@ -633,7 +702,11 @@ var OperationQueue = class {
633
702
  * - ์ž‘์—… ์™„๋ฃŒ ์‹œ ๋Œ€๊ธฐ ์ค‘์ธ ํ”„๋กœ์„ธ์Šค์—๊ฒŒ ์ž๋™ ์•Œ๋ฆผ
634
703
  */
635
704
  async processQueue() {
636
- if (this.processingPromise) return this.processingPromise;
705
+ if (this.processingPromise) {
706
+ await this.processingPromise;
707
+ if (this.queue.length > 0 && !this.processingPromise) return this.processQueue();
708
+ return;
709
+ }
637
710
  this.processingPromise = this._doProcess();
638
711
  try {
639
712
  await this.processingPromise;
@@ -745,6 +818,95 @@ var OperationQueue = class {
745
818
  }
746
819
  };
747
820
 
821
+ //#endregion
822
+ //#region src/errors.ts
823
+ /**
824
+ * Action payload ๊ฒ€์ฆ ์‹คํŒจ ์—๋Ÿฌ
825
+ *
826
+ * dispatch ์‹œ Zod ์Šคํ‚ค๋งˆ ๊ฒ€์ฆ์ด ์‹คํŒจํ•˜๋ฉด ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค.
827
+ * (validationMode๊ฐ€ 'strict'์ผ ๋•Œ๋งŒ throw)
828
+ *
829
+ * @example
830
+ * ```typescript
831
+ * try {
832
+ * dispatch('updateUser', { id: '', name: 'John' });
833
+ * } catch (error) {
834
+ * if (error instanceof ActionValidationError) {
835
+ * console.log('Action:', error.action);
836
+ * console.log('Issues:', error.issues);
837
+ * console.log('Formatted:', error.formattedErrors);
838
+ * }
839
+ * }
840
+ * ```
841
+ */
842
+ var ActionValidationError = class ActionValidationError extends Error {
843
+ /**
844
+ * @param action - ๊ฒ€์ฆ ์‹คํŒจํ•œ action ์ด๋ฆ„
845
+ * @param zodError - Zod ๊ฒ€์ฆ ์—๋Ÿฌ ๊ฐ์ฒด (ZodError compatible)
846
+ */
847
+ constructor(action, zodError) {
848
+ const message = `Action "${action}" payload validation failed: ${zodError && typeof zodError === "object" && "message" in zodError ? String(zodError.message) : "Validation failed"}`;
849
+ super(message);
850
+ this.name = "ActionValidationError";
851
+ this.action = action;
852
+ this.zodError = zodError;
853
+ Object.setPrototypeOf(this, ActionValidationError.prototype);
854
+ }
855
+ /**
856
+ * Zod ๊ฒ€์ฆ ์ด์Šˆ ๋ชฉ๋ก
857
+ */
858
+ get issues() {
859
+ if (this.zodError && typeof this.zodError === "object" && "issues" in this.zodError && Array.isArray(this.zodError.issues)) return this.zodError.issues;
860
+ return [];
861
+ }
862
+ /**
863
+ * ํฌ๋งท๋œ ์—๋Ÿฌ ๊ฐ์ฒด (ํ•„๋“œ๋ณ„ ์—๋Ÿฌ ๋ฉ”์‹œ์ง€)
864
+ */
865
+ get formattedErrors() {
866
+ if (this.zodError && typeof this.zodError === "object" && "format" in this.zodError && typeof this.zodError.format === "function") return this.zodError.format();
867
+ return {};
868
+ }
869
+ /**
870
+ * ํ”Œ๋žซ ์—๋Ÿฌ ๋งต (ํ•„๋“œ๋ช… โ†’ ์—๋Ÿฌ ๋ฉ”์‹œ์ง€ ๋ฐฐ์—ด)
871
+ */
872
+ get flattenedErrors() {
873
+ if (this.zodError && typeof this.zodError === "object" && "flatten" in this.zodError && typeof this.zodError.flatten === "function") return this.zodError.flatten();
874
+ return {
875
+ fieldErrors: {},
876
+ formErrors: []
877
+ };
878
+ }
879
+ /**
880
+ * ์ฒซ ๋ฒˆ์งธ ์—๋Ÿฌ ๋ฉ”์‹œ์ง€
881
+ */
882
+ get firstError() {
883
+ return this.issues[0]?.message;
884
+ }
885
+ /**
886
+ * ์—๋Ÿฌ ๋ฐœ์ƒ ํ•„๋“œ ๊ฒฝ๋กœ ๋ชฉ๋ก
887
+ */
888
+ get errorPaths() {
889
+ return this.issues.map((issue) => issue.path.map((p) => String(p)).join("."));
890
+ }
891
+ /**
892
+ * JSON ์ง๋ ฌํ™”
893
+ */
894
+ toJSON() {
895
+ return {
896
+ name: this.name,
897
+ action: this.action,
898
+ message: this.message,
899
+ issues: this.issues
900
+ };
901
+ }
902
+ };
903
+ /**
904
+ * ActionValidationError ํƒ€์ž… ๊ฐ€๋“œ
905
+ */
906
+ function isActionValidationError(error) {
907
+ return error instanceof ActionValidationError;
908
+ }
909
+
748
910
  //#endregion
749
911
  //#region src/ActionRegister.ts
750
912
  /**
@@ -762,6 +924,35 @@ var OperationQueue = class {
762
924
  *
763
925
  * @public
764
926
  */
927
+ /**
928
+ * Type guard to determine if an object is DispatchOptions
929
+ * Extracted as utility function for reuse and performance
930
+ *
931
+ * @param obj - Object to check
932
+ * @returns True if object is DispatchOptions
933
+ * @internal
934
+ */
935
+ function isDispatchOptions(obj) {
936
+ if (!obj || typeof obj !== "object") return false;
937
+ if ("debounce" in obj && typeof obj.debounce === "number") return true;
938
+ if ("throttle" in obj && typeof obj.throttle === "number") return true;
939
+ if ("executionMode" in obj) return true;
940
+ if ("signal" in obj && obj.signal instanceof AbortSignal) return true;
941
+ if ("immediate" in obj && typeof obj.immediate === "boolean") return true;
942
+ if ("queuePriority" in obj && typeof obj.queuePriority === "number") return true;
943
+ if ("timeout" in obj && typeof obj.timeout === "number") return true;
944
+ if ("retryOnError" in obj && typeof obj.retryOnError === "object") return true;
945
+ if ("autoAbort" in obj && typeof obj.autoAbort === "object") return true;
946
+ if ("filter" in obj && typeof obj.filter === "object" && obj.filter !== null) {
947
+ const filter = obj.filter;
948
+ if ("handlerIds" in filter || "excludeHandlerIds" in filter || "priority" in filter || "custom" in filter) return true;
949
+ }
950
+ if ("result" in obj && typeof obj.result === "object" && obj.result !== null) {
951
+ const result = obj.result;
952
+ if ("strategy" in result || "merger" in result || "collect" in result || "maxResults" in result || "includeErrors" in result) return true;
953
+ }
954
+ return false;
955
+ }
765
956
  var ActionRegister = class {
766
957
  constructor(config = {}) {
767
958
  this.pipelines = /* @__PURE__ */ new Map();
@@ -810,18 +1001,16 @@ var ActionRegister = class {
810
1001
  * @public
811
1002
  */
812
1003
  get actions() {
813
- return new Proxy({}, { get: (target, prop) => {
1004
+ if (!this._actionsProxy) this._actionsProxy = new Proxy({}, { get: (_target, prop) => {
814
1005
  if (typeof prop === "string" && this.pipelines.has(prop)) {
815
1006
  const actionKey = prop;
816
1007
  return (payloadOrOptions, options) => {
817
- const isDispatchOptions = (obj) => {
818
- return obj && typeof obj === "object" && ("debounce" in obj || "throttle" in obj || "executionMode" in obj || "signal" in obj || "immediate" in obj || "filter" in obj || "result" in obj);
819
- };
820
1008
  if (payloadOrOptions && isDispatchOptions(payloadOrOptions)) return this.dispatch(actionKey, void 0, payloadOrOptions);
821
1009
  else return this.dispatch(actionKey, payloadOrOptions, options);
822
1010
  };
823
1011
  }
824
1012
  } });
1013
+ return this._actionsProxy;
825
1014
  }
826
1015
  /**
827
1016
  * Actions-based dispatching with result collection
@@ -847,18 +1036,16 @@ var ActionRegister = class {
847
1036
  * @returns Proxy object with action functions that return ExecutionResult
848
1037
  */
849
1038
  get actionsWithResult() {
850
- return new Proxy({}, { get: (target, prop) => {
1039
+ if (!this._actionsWithResultProxy) this._actionsWithResultProxy = new Proxy({}, { get: (_target, prop) => {
851
1040
  if (typeof prop === "string" && this.pipelines.has(prop)) {
852
1041
  const actionKey = prop;
853
1042
  return (payloadOrOptions, options) => {
854
- const isDispatchOptions = (obj) => {
855
- return obj && typeof obj === "object" && ("debounce" in obj || "throttle" in obj || "executionMode" in obj || "signal" in obj || "immediate" in obj || "filter" in obj || "result" in obj);
856
- };
857
1043
  if (payloadOrOptions && isDispatchOptions(payloadOrOptions)) return this.dispatchWithResult(actionKey, void 0, payloadOrOptions);
858
1044
  else return this.dispatchWithResult(actionKey, payloadOrOptions, options);
859
1045
  };
860
1046
  }
861
1047
  } });
1048
+ return this._actionsWithResultProxy;
862
1049
  }
863
1050
  /**
864
1051
  * Register an action handler with optional configuration
@@ -1043,6 +1230,20 @@ var ActionRegister = class {
1043
1230
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
1044
1231
  });
1045
1232
  if (payload instanceof Event && process.env.NODE_ENV === "development") console.warn(`Event object passed to action "${String(action)}"`, payload.type);
1233
+ if (this.registryConfig?.schema && this.registryConfig?.validateOnDispatch !== false) {
1234
+ const actionSchema = this.registryConfig.schema[action];
1235
+ if (actionSchema) {
1236
+ const result = actionSchema.safeParse(payload);
1237
+ if (!result.success) {
1238
+ const mode = this.registryConfig.validationMode ?? "strict";
1239
+ if (mode === "strict") throw new ActionValidationError(action, result.error);
1240
+ else if (mode === "warn") {
1241
+ console.warn(`Action "${String(action)}" payload validation failed:`, result.error.message);
1242
+ this.log(`Validation warning for action '${String(action)}'`, { issues: result.error.issues }, "warn");
1243
+ }
1244
+ }
1245
+ }
1246
+ }
1046
1247
  const [effectiveSignal, autoAbortController, cleanup] = this.createAbortSignal(options);
1047
1248
  if (options?.autoAbort?.onControllerCreated && autoAbortController) options.autoAbort.onControllerCreated(autoAbortController);
1048
1249
  if (effectiveSignal?.aborted) {
@@ -1931,13 +2132,154 @@ function isReactActionError(error) {
1931
2132
  return error instanceof ReactActionError;
1932
2133
  }
1933
2134
 
2135
+ //#endregion
2136
+ //#region src/action-schema.ts
2137
+ /**
2138
+ * Zod ์Šคํ‚ค๋งˆ๋ฅผ JSON Schema๋กœ ๋ณ€ํ™˜ (Zod 4 ๋„ค์ดํ‹ฐ๋ธŒ API)
2139
+ *
2140
+ * @param schema - Zod ์Šคํ‚ค๋งˆ
2141
+ * @returns JSON Schema (draft-7)
2142
+ */
2143
+ function zodToJsonSchema(schema, zodModule) {
2144
+ return zodModule.toJSONSchema(schema, {
2145
+ target: "draft-7",
2146
+ metadata: zodModule.globalRegistry
2147
+ });
2148
+ }
2149
+ /**
2150
+ * Zod ์Šคํ‚ค๋งˆ ๊ธฐ๋ฐ˜ Action ์ •์˜
2151
+ *
2152
+ * defineTool ํŒจํ„ด์„ ๊ธฐ๋ฐ˜์œผ๋กœ context-action์— ๋งž๊ฒŒ ๊ตฌํ˜„:
2153
+ * - Single Source of Truth: Zod ์Šคํ‚ค๋งˆ๋กœ ํƒ€์ž… + ๊ฒ€์ฆ + ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ ํ†ตํ•ฉ
2154
+ * - ๋Ÿฐํƒ€์ž„ ๊ฒ€์ฆ: validate(), safeParse()
2155
+ * - Tool Chain ํ˜ธํ™˜: toMCP(), toOpenAI(), toAnthropic()
2156
+ *
2157
+ * @param options - Action ์ •์˜ ์˜ต์…˜
2158
+ * @param zodModule - Zod ๋ชจ๋“ˆ (peerDependency๋กœ ์ฃผ์ž…)
2159
+ * @returns UnifiedAction ์ธ์Šคํ„ด์Šค
2160
+ *
2161
+ * @example
2162
+ * ```typescript
2163
+ * import { z } from 'zod';
2164
+ * import { defineAction } from '@context-action/core';
2165
+ *
2166
+ * const updateUserAction = defineAction({
2167
+ * name: 'updateUser',
2168
+ * description: 'Update user profile',
2169
+ * parameters: z.object({
2170
+ * id: z.string().min(1).meta({ description: 'User ID' }),
2171
+ * name: z.string().min(2).max(50).meta({ description: 'User name' }),
2172
+ * email: z.string().email().optional(),
2173
+ * }),
2174
+ * }, z);
2175
+ *
2176
+ * // ๊ฒ€์ฆ
2177
+ * const validated = updateUserAction.validate({ id: '123', name: 'John' });
2178
+ *
2179
+ * // Tool chain ๋ณ€ํ™˜
2180
+ * const mcpTool = updateUserAction.toMCP();
2181
+ * ```
2182
+ */
2183
+ function defineAction(options, zodModule) {
2184
+ const { name, description, parameters } = options;
2185
+ const jsonSchema = zodToJsonSchema(parameters, zodModule);
2186
+ return {
2187
+ name,
2188
+ description,
2189
+ zodSchema: parameters,
2190
+ jsonSchema,
2191
+ validate: (payload) => {
2192
+ return parameters.parse(payload);
2193
+ },
2194
+ safeParse: (payload) => {
2195
+ return parameters.safeParse(payload);
2196
+ },
2197
+ toJSONSchema: () => jsonSchema,
2198
+ toMCP: () => ({
2199
+ name,
2200
+ description,
2201
+ inputSchema: jsonSchema
2202
+ }),
2203
+ toOpenAI: () => ({
2204
+ type: "function",
2205
+ function: {
2206
+ name,
2207
+ description,
2208
+ parameters: {
2209
+ type: "object",
2210
+ properties: jsonSchema.properties ?? {},
2211
+ required: jsonSchema.required
2212
+ }
2213
+ }
2214
+ }),
2215
+ toAnthropic: () => ({
2216
+ name,
2217
+ description,
2218
+ input_schema: jsonSchema
2219
+ })
2220
+ };
2221
+ }
2222
+ /**
2223
+ * ๋‹ค์ค‘ Action ์Šคํ‚ค๋งˆ ์ƒ์„ฑ
2224
+ *
2225
+ * ์—ฌ๋Ÿฌ defineAction์„ ๋ฌถ์–ด์„œ ActionSchemaMap ์ƒ์„ฑ
2226
+ *
2227
+ * @param actions - UnifiedAction ๋งต
2228
+ * @returns ActionSchemaMap
2229
+ *
2230
+ * @example
2231
+ * ```typescript
2232
+ * const userActionSchema = createActionSchema({
2233
+ * updateUser: defineAction({ ... }, z),
2234
+ * deleteUser: defineAction({ ... }, z),
2235
+ * });
2236
+ *
2237
+ * type UserActions = InferActionPayloadMap<typeof userActionSchema>;
2238
+ * ```
2239
+ */
2240
+ function createActionSchema(actions) {
2241
+ return actions;
2242
+ }
2243
+ /**
2244
+ * Zod ๋ชจ๋“ˆ์„ ๋ฐ”์ธ๋”ฉํ•œ defineAction ํŒฉํ† ๋ฆฌ ์ƒ์„ฑ
2245
+ *
2246
+ * ๋งค๋ฒˆ z ๋ชจ๋“ˆ์„ ์ „๋‹ฌํ•˜์ง€ ์•Š์•„๋„ ๋˜๋„๋ก ํŒฉํ† ๋ฆฌ ํŒจํ„ด ์ œ๊ณต
2247
+ *
2248
+ * @param zodModule - Zod ๋ชจ๋“ˆ
2249
+ * @returns defineAction ํ•จ์ˆ˜ (z ๋ฐ”์ธ๋”ฉ๋จ)
2250
+ *
2251
+ * @example
2252
+ * ```typescript
2253
+ * import { z } from 'zod';
2254
+ * import { createActionFactory } from '@context-action/core';
2255
+ *
2256
+ * const defineAction = createActionFactory(z);
2257
+ *
2258
+ * const updateUser = defineAction({
2259
+ * name: 'updateUser',
2260
+ * parameters: z.object({ id: z.string() }),
2261
+ * });
2262
+ * ```
2263
+ */
2264
+ function createActionFactory(zodModule) {
2265
+ return (options) => {
2266
+ return defineAction(options, zodModule);
2267
+ };
2268
+ }
2269
+
1934
2270
  //#endregion
1935
2271
  exports.ActionGuard = ActionGuard;
1936
2272
  exports.ActionRegister = ActionRegister;
2273
+ exports.ActionValidationError = ActionValidationError;
1937
2274
  exports.ReactActionError = ReactActionError;
1938
2275
  exports.ReactDevUtils = ReactDevUtils;
2276
+ exports.createActionFactory = createActionFactory;
1939
2277
  exports.createActionHandler = createActionHandler;
2278
+ exports.createActionSchema = createActionSchema;
2279
+ exports.defineAction = defineAction;
1940
2280
  exports.executeParallel = executeParallel;
1941
2281
  exports.executeRace = executeRace;
1942
2282
  exports.executeSequential = executeSequential;
1943
- exports.isReactActionError = isReactActionError;
2283
+ exports.isActionValidationError = isActionValidationError;
2284
+ exports.isReactActionError = isReactActionError;
2285
+ exports.zodToJsonSchema = zodToJsonSchema;