use-server-action 1.1.4 → 2.0.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/README.md CHANGED
@@ -16,26 +16,30 @@ npm install use-server-action
16
16
  // app/actions.ts
17
17
  "use server";
18
18
 
19
- import { serverAction, success, error } from "use-server-action/server";
19
+ import { createAction, createContextMiddleware } from "use-server-action/server";
20
20
 
21
- export const createUser = serverAction(async (name: string) => {
22
- if (!name.trim()) {
23
- throw new Error("Name is required");
24
- }
21
+ // Simple action
22
+ export const createUser = createAction<{ name: string }>()
23
+ .handle(async (ctx, input) => {
24
+ const user = await db.user.create({ data: { name: input.name } });
25
+ return { ok: true, data: user };
26
+ });
25
27
 
26
- const user = await db.user.create({ data: { name } });
27
- return user;
28
+ // With authentication middleware
29
+ const withAuth = createContextMiddleware(async (next, ctx, ...args) => {
30
+ const user = await getUser();
31
+ if (!user) {
32
+ return { ok: false, message: "Unauthorized", code: "UNAUTHORIZED" };
33
+ }
34
+ return next({ ...ctx, user }, ...args);
28
35
  });
29
36
 
30
- // Or handle errors manually for more control:
31
- export const deleteUserAction = async (id: string) => {
32
- try {
33
- await db.user.delete({ where: { id } });
34
- return success({ deleted: true });
35
- } catch (e) {
36
- return error("Failed to delete user", "DELETE_FAILED");
37
- }
38
- };
37
+ export const deleteUser = createAction<{ id: string }>()
38
+ .use(withAuth)
39
+ .handle(async (ctx, input) => {
40
+ await db.user.delete({ where: { id: input.id, ownerId: ctx.user.id } });
41
+ return { ok: true, data: { deleted: true } };
42
+ });
39
43
  ```
40
44
 
41
45
  ### 2. Use in a client component
@@ -47,26 +51,14 @@ import { useServerAction } from "use-server-action";
47
51
  import { createUser } from "./actions";
48
52
 
49
53
  export function CreateUserForm() {
50
- const {
51
- execute,
52
- data,
53
- error,
54
- isPending,
55
- isSuccess,
56
- isError,
57
- reset,
58
- } = useServerAction({
54
+ const { execute, data, error, isPending, isSuccess, isError } = useServerAction({
59
55
  action: createUser,
60
- onSuccess: (user) => {
61
- console.log("User created:", user);
62
- },
63
- onError: (message, code) => {
64
- console.error(`Error [${code}]:`, message);
65
- },
56
+ onSuccess: (user) => console.log("User created:", user),
57
+ onError: (message, code) => console.error(`Error [${code}]:`, message),
66
58
  });
67
59
 
68
60
  return (
69
- <form action={(formData) => execute(formData.get("name") as string)}>
61
+ <form action={(formData) => execute({ name: formData.get("name") as string })}>
70
62
  <input name="name" placeholder="Name" disabled={isPending} />
71
63
  <button type="submit" disabled={isPending}>
72
64
  {isPending ? "Creating..." : "Create User"}
@@ -78,6 +70,14 @@ export function CreateUserForm() {
78
70
  }
79
71
  ```
80
72
 
73
+ ## Features
74
+
75
+ - **Type-safe** - Full TypeScript support with inferred types for context and inputs
76
+ - **Middleware support** - Chain middleware with `createAction().use(middleware)`
77
+ - **Context accumulation** - Middleware can add typed context for downstream handlers
78
+ - **Automatic error handling** - Thrown errors are caught and returned as error results
79
+ - **Zod validation** - Built-in `withZodValidation` middleware for input validation
80
+
81
81
  ## Documentation
82
82
 
83
83
  You can view the documentation [here](https://use-server-action.jackh.sh)
package/dist/index.d.mts CHANGED
@@ -1,5 +1,5 @@
1
- import { S as ServerActionResult } from './server-action-HthxxP-Y.mjs';
2
- export { b as ServerActionError, c as ServerActionFn, a as ServerActionSuccess } from './server-action-HthxxP-Y.mjs';
1
+ import { S as ServerActionResult } from './server-action-Cm80HhlX.mjs';
2
+ export { b as ServerActionError, c as ServerActionFn, a as ServerActionSuccess } from './server-action-Cm80HhlX.mjs';
3
3
 
4
4
  type UseServerActionInput<P extends unknown[], T> = {
5
5
  action: (...args: P) => Promise<ServerActionResult<T>>;
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { S as ServerActionResult } from './server-action-HthxxP-Y.js';
2
- export { b as ServerActionError, c as ServerActionFn, a as ServerActionSuccess } from './server-action-HthxxP-Y.js';
1
+ import { S as ServerActionResult } from './server-action-Cm80HhlX.js';
2
+ export { b as ServerActionError, c as ServerActionFn, a as ServerActionSuccess } from './server-action-Cm80HhlX.js';
3
3
 
4
4
  type UseServerActionInput<P extends unknown[], T> = {
5
5
  action: (...args: P) => Promise<ServerActionResult<T>>;
@@ -17,6 +17,24 @@ type ServerActionOptions = {
17
17
  /**
18
18
  * Wraps an async function to return a standardized ServerActionResult.
19
19
  * Catches any thrown errors and converts them to error results.
20
+ *
21
+ * @deprecated Use `createAction().input(schema).handle(fn)` instead.
22
+ * The new API provides better type inference and built-in validation.
23
+ *
24
+ * @example
25
+ * ```ts
26
+ * // Old way (deprecated):
27
+ * const myAction = serverAction(async (input: MyInput) => {
28
+ * return await doSomething(input);
29
+ * });
30
+ *
31
+ * // New way:
32
+ * const myAction = createAction()
33
+ * .input(mySchema)
34
+ * .handle(async (ctx, input) => {
35
+ * return { ok: true, data: await doSomething(input) };
36
+ * });
37
+ * ```
20
38
  */
21
39
  declare function serverAction<P extends unknown[], T>(fn: (...args: P) => Promise<T>, options?: ServerActionOptions): ServerActionFn<P, T>;
22
40
  /**
@@ -17,6 +17,24 @@ type ServerActionOptions = {
17
17
  /**
18
18
  * Wraps an async function to return a standardized ServerActionResult.
19
19
  * Catches any thrown errors and converts them to error results.
20
+ *
21
+ * @deprecated Use `createAction().input(schema).handle(fn)` instead.
22
+ * The new API provides better type inference and built-in validation.
23
+ *
24
+ * @example
25
+ * ```ts
26
+ * // Old way (deprecated):
27
+ * const myAction = serverAction(async (input: MyInput) => {
28
+ * return await doSomething(input);
29
+ * });
30
+ *
31
+ * // New way:
32
+ * const myAction = createAction()
33
+ * .input(mySchema)
34
+ * .handle(async (ctx, input) => {
35
+ * return { ok: true, data: await doSomething(input) };
36
+ * });
37
+ * ```
20
38
  */
21
39
  declare function serverAction<P extends unknown[], T>(fn: (...args: P) => Promise<T>, options?: ServerActionOptions): ServerActionFn<P, T>;
22
40
  /**
package/dist/server.d.mts CHANGED
@@ -1,11 +1,191 @@
1
- import { S as ServerActionResult } from './server-action-HthxxP-Y.mjs';
2
- export { b as ServerActionError, c as ServerActionFn, a as ServerActionSuccess, e as error, f as isError, i as isSuccess, s as serverAction, d as success, u as unwrap, g as unwrapOr } from './server-action-HthxxP-Y.mjs';
1
+ import { S as ServerActionResult } from './server-action-Cm80HhlX.mjs';
2
+ export { b as ServerActionError, c as ServerActionFn, a as ServerActionSuccess, e as error, f as isError, i as isSuccess, s as serverAction, d as success, u as unwrap, g as unwrapOr } from './server-action-Cm80HhlX.mjs';
3
3
 
4
+ /**
5
+ * Base context type - all contexts extend from this
6
+ */
7
+ type BaseContext = Record<string, unknown>;
4
8
  /**
5
9
  * A middleware function that wraps a server action.
6
10
  * Receives the next function in the chain and the parameters passed to the action.
7
11
  */
8
12
  type Middleware<P extends unknown[], T> = (next: (...params: P) => Promise<ServerActionResult<T>>, ...params: P) => Promise<ServerActionResult<T>>;
13
+ /**
14
+ * A context-aware middleware that can receive context from previous middleware
15
+ * and add new context for downstream middleware.
16
+ *
17
+ * @template P - The parameter types for the action
18
+ * @template T - The return type of the action
19
+ * @template CtxIn - The context type this middleware expects to receive
20
+ * @template CtxOut - The context type this middleware adds (merged with CtxIn for next)
21
+ */
22
+ type ContextMiddleware<P extends unknown[], T, CtxIn extends BaseContext = BaseContext, CtxOut extends BaseContext = BaseContext> = (next: (ctx: CtxIn & CtxOut, ...params: P) => Promise<ServerActionResult<T>>, ctx: CtxIn, ...params: P) => Promise<ServerActionResult<T>>;
23
+ /**
24
+ * A Zod-like schema interface for validation.
25
+ * Works with Zod, Valibot, or any schema library with a compatible safeParse method.
26
+ */
27
+ type ValidationSchema<T> = {
28
+ safeParse(data: unknown): {
29
+ success: true;
30
+ data: T;
31
+ } | {
32
+ success: false;
33
+ error: {
34
+ message?: string;
35
+ errors?: Array<{
36
+ message: string;
37
+ }>;
38
+ };
39
+ };
40
+ };
41
+ type WithValidationOptions = {
42
+ /** Error code to return on validation failure. Defaults to "VALIDATION_ERROR" */
43
+ code?: string;
44
+ /** Custom error message formatter */
45
+ formatError?: (error: {
46
+ message?: string;
47
+ errors?: Array<{
48
+ message: string;
49
+ }>;
50
+ }) => string;
51
+ };
52
+
53
+ /**
54
+ * Creates a type-safe context-aware middleware.
55
+ *
56
+ * @example
57
+ * ```ts
58
+ * type User = { id: string; name: string };
59
+ *
60
+ * const withAuth = createContextMiddleware<
61
+ * [string], // Parameters
62
+ * SomeReturnType, // Return type
63
+ * {}, // Input context (none required)
64
+ * { user: User } // Output context (adds user)
65
+ * >(async (next, ctx, input) => {
66
+ * const user = await authenticate();
67
+ * if (!user) {
68
+ * return { ok: false, message: "Unauthorized", code: "UNAUTHORIZED" };
69
+ * }
70
+ * // Pass the new context with user added
71
+ * return next({ ...ctx, user }, input);
72
+ * });
73
+ * ```
74
+ */
75
+ declare function createContextMiddleware<P extends unknown[], T, CtxIn extends BaseContext = BaseContext, CtxOut extends BaseContext = BaseContext>(handler: ContextMiddleware<P, T, CtxIn, CtxOut>): ContextMiddleware<P, T, CtxIn, CtxOut>;
76
+
77
+ /**
78
+ * A builder for creating server actions with type-safe context accumulation.
79
+ *
80
+ * @template TInput - The input parameters as a tuple type
81
+ * @template TContext - The accumulated context type from all middleware
82
+ */
83
+ declare class ActionBuilder<TInput extends unknown[] = unknown[], TContext extends BaseContext = {}> {
84
+ private middlewares;
85
+ private constructor();
86
+ /**
87
+ * Create a new ActionBuilder instance
88
+ */
89
+ static create<TInput extends unknown[]>(): ActionBuilder<TInput, {}>;
90
+ /**
91
+ * Add a context-aware middleware to the chain.
92
+ * The middleware receives the accumulated context and can add new context.
93
+ *
94
+ * @example
95
+ * ```ts
96
+ * const withUser = createContextMiddleware(async (next, ctx, ...args) => {
97
+ * const user = await getUser();
98
+ * if (!user) {
99
+ * return { ok: false, message: "Unauthorized", code: "UNAUTHORIZED" };
100
+ * }
101
+ * return next({ ...ctx, user }, ...args);
102
+ * });
103
+ *
104
+ * const action = createAction<[{ name: string }]>()
105
+ * .use(withUser)
106
+ * .handle(async (ctx, input) => {
107
+ * // ctx.user is fully typed!
108
+ * });
109
+ * ```
110
+ */
111
+ use<CtxOut extends BaseContext>(middleware: ContextMiddleware<TInput, any, TContext, CtxOut>): ActionBuilder<TInput, TContext & CtxOut>;
112
+ /**
113
+ * Add a regular (non-context) middleware to the chain.
114
+ * The middleware can transform input/output but doesn't modify context.
115
+ *
116
+ * @example
117
+ * ```ts
118
+ * const withLogging = createMiddleware(async (next, ...args) => {
119
+ * console.log("Args:", args);
120
+ * const result = await next(...args);
121
+ * console.log("Result:", result);
122
+ * return result;
123
+ * });
124
+ *
125
+ * const action = createAction<[string]>()
126
+ * .use(withLogging)
127
+ * .handle(async (ctx, input) => { ... });
128
+ * ```
129
+ */
130
+ use(middleware: Middleware<TInput, any>): ActionBuilder<TInput, TContext>;
131
+ /**
132
+ * Define the action handler. This finalizes the builder and returns the executable action.
133
+ * The handler receives the accumulated context and input parameters.
134
+ *
135
+ * @example
136
+ * ```ts
137
+ * const action = createAction<[{ name: string; email: string }]>()
138
+ * .use(withUser)
139
+ * .handle(async (ctx, input) => {
140
+ * // ctx and input are fully typed
141
+ * return { ok: true, data: result };
142
+ * });
143
+ *
144
+ * // Call the action
145
+ * const result = await action({ name: "John", email: "john@example.com" });
146
+ * ```
147
+ */
148
+ handle<TOutput>(handler: (ctx: TContext, ...args: TInput) => Promise<ServerActionResult<TOutput>>): (...args: TInput) => Promise<ServerActionResult<TOutput>>;
149
+ }
150
+ /**
151
+ * Converts a type to a tuple of arguments.
152
+ * - `void` becomes `[]` (no args)
153
+ * - Single type `T` becomes `[T]`
154
+ * - Tuple type `[A, B]` stays as `[A, B]`
155
+ */
156
+ type ToArgs<T> = [T] extends [void] ? [] : T extends unknown[] ? T : [T];
157
+ /**
158
+ * Creates a new action builder with type-safe context accumulation.
159
+ *
160
+ * @example
161
+ * ```ts
162
+ * // Single input parameter
163
+ * const createUser = createAction<{ name: string; email: string }>()
164
+ * .use(withUser)
165
+ * .handle(async (ctx, input) => {
166
+ * await db.users.create({ ...input, createdBy: ctx.user.id });
167
+ * return { ok: true, data: { success: true } };
168
+ * });
169
+ *
170
+ * // Multiple input parameters (use tuple)
171
+ * const updateUser = createAction<[string, { name: string }]>()
172
+ * .use(withUser)
173
+ * .handle(async (ctx, id, data) => {
174
+ * await db.users.update(id, data);
175
+ * return { ok: true, data: { success: true } };
176
+ * });
177
+ *
178
+ * // With validation middleware
179
+ * const createPost = createAction<z.infer<typeof postSchema>>()
180
+ * .use(withZodValidation(postSchema))
181
+ * .use(withUser)
182
+ * .handle(async (ctx, input) => {
183
+ * return { ok: true, data: await db.posts.create(input) };
184
+ * });
185
+ * ```
186
+ */
187
+ declare function createAction<TInput = void>(): ActionBuilder<ToArgs<TInput>, {}>;
188
+
9
189
  /**
10
190
  * Creates a type-safe middleware function.
11
191
  *
@@ -24,77 +204,80 @@ declare function createMiddleware<P extends unknown[], T>(handler: Middleware<P,
24
204
  * Applies middleware to a server action.
25
205
  * Middleware is executed in order (first middleware wraps second, etc).
26
206
  *
207
+ * @deprecated Use `createAction().input(schema).use(middleware).handle(fn)` instead.
208
+ * This provides better type inference and a more composable API.
209
+ *
27
210
  * @example
28
211
  * ```ts
212
+ * // Old way (deprecated):
29
213
  * const protectedAction = applyMiddleware(
30
214
  * myServerAction,
31
215
  * [withAuth, withLogging, withValidation]
32
216
  * );
217
+ *
218
+ * // New way:
219
+ * const protectedAction = createAction()
220
+ * .input(schema)
221
+ * .use(withAuth)
222
+ * .use(withLogging)
223
+ * .handle(async (ctx, input) => { ... });
33
224
  * ```
34
225
  */
35
226
  declare function applyMiddleware<P extends unknown[], T>(action: (...params: P) => Promise<ServerActionResult<T>>, middleware: Middleware<P, T>[]): (...params: P) => Promise<ServerActionResult<T>>;
36
227
  /**
37
228
  * Composes multiple middleware into a single middleware.
38
229
  *
230
+ * @deprecated Use `createAction().use(mw1).use(mw2).handle(fn)` instead.
231
+ * The builder pattern provides better type inference for chained middleware.
232
+ *
39
233
  * @example
40
234
  * ```ts
235
+ * // Old way (deprecated):
41
236
  * const combined = composeMiddleware(withAuth, withLogging, withValidation);
42
237
  * const protectedAction = applyMiddleware(myAction, [combined]);
238
+ *
239
+ * // New way:
240
+ * const protectedAction = createAction()
241
+ * .input(schema)
242
+ * .use(withAuth)
243
+ * .use(withLogging)
244
+ * .handle(async (ctx, input) => { ... });
43
245
  * ```
44
246
  */
45
247
  declare function composeMiddleware<P extends unknown[], T>(...middleware: Middleware<P, T>[]): Middleware<P, T>;
46
- /**
47
- * A Zod-like schema interface for validation.
48
- * Works with Zod, Valibot, or any schema library with a compatible safeParse method.
49
- */
50
- type ValidationSchema<T> = {
51
- safeParse(data: unknown): {
52
- success: true;
53
- data: T;
54
- } | {
55
- success: false;
56
- error: {
57
- message?: string;
58
- errors?: Array<{
59
- message: string;
60
- }>;
61
- };
62
- };
63
- };
64
- type WithValidationOptions = {
65
- /** Error code to return on validation failure. Defaults to "VALIDATION_ERROR" */
66
- code?: string;
67
- /** Custom error message formatter */
68
- formatError?: (error: {
69
- message?: string;
70
- errors?: Array<{
71
- message: string;
72
- }>;
73
- }) => string;
74
- };
75
248
  /**
76
249
  * Creates a middleware that validates the first parameter against a schema.
77
250
  * Works with Zod, Valibot, or any library with a compatible safeParse method.
78
251
  *
252
+ * @deprecated Use `createAction().input(schema).handle(fn)` instead.
253
+ * The `.input()` method provides automatic type inference from the schema.
254
+ *
79
255
  * @example
80
256
  * ```ts
81
257
  * import { z } from "zod";
82
- * import { withValidation, applyMiddleware } from "use-server-action/server";
83
258
  *
84
- * const CreateUserSchema = z.object({
259
+ * const schema = z.object({
85
260
  * name: z.string().min(1),
86
261
  * email: z.string().email(),
87
262
  * });
88
263
  *
264
+ * // Old way (deprecated):
89
265
  * const createUser = applyMiddleware(
90
- * serverAction(async (input: z.infer<typeof CreateUserSchema>) => {
266
+ * serverAction(async (input: z.infer<typeof schema>) => {
91
267
  * return await db.user.create({ data: input });
92
268
  * }),
93
- * [withValidation(CreateUserSchema)]
269
+ * [withZodValidation(schema)]
94
270
  * );
271
+ *
272
+ * // New way:
273
+ * const createUser = createAction()
274
+ * .input(schema) // Type is inferred automatically!
275
+ * .handle(async (ctx, input) => {
276
+ * return { ok: true, data: await db.user.create({ data: input }) };
277
+ * });
95
278
  * ```
96
279
  */
97
- declare function withValidation<TInput, T>(schema: ValidationSchema<TInput>, options?: WithValidationOptions): Middleware<[TInput], T>;
280
+ declare function withZodValidation<TInput, T>(schema: ValidationSchema<TInput>, options?: WithValidationOptions): Middleware<[TInput], T>;
98
281
  /**
99
282
  * Creates a middleware that logs action calls and results.
100
283
  */
@@ -104,4 +287,4 @@ declare function withLogging<P extends unknown[], T>(logger?: {
104
287
  onError?: (message: string, code: string | undefined, params: P) => void;
105
288
  }): Middleware<P, T>;
106
289
 
107
- export { type Middleware, ServerActionResult, type ValidationSchema, type WithValidationOptions, applyMiddleware, composeMiddleware, createMiddleware, withLogging, withValidation };
290
+ export { type BaseContext, type ContextMiddleware, type Middleware, ServerActionResult, type ValidationSchema, type WithValidationOptions, applyMiddleware, composeMiddleware, createAction, createContextMiddleware, createMiddleware, withLogging, withZodValidation };
package/dist/server.d.ts CHANGED
@@ -1,11 +1,191 @@
1
- import { S as ServerActionResult } from './server-action-HthxxP-Y.js';
2
- export { b as ServerActionError, c as ServerActionFn, a as ServerActionSuccess, e as error, f as isError, i as isSuccess, s as serverAction, d as success, u as unwrap, g as unwrapOr } from './server-action-HthxxP-Y.js';
1
+ import { S as ServerActionResult } from './server-action-Cm80HhlX.js';
2
+ export { b as ServerActionError, c as ServerActionFn, a as ServerActionSuccess, e as error, f as isError, i as isSuccess, s as serverAction, d as success, u as unwrap, g as unwrapOr } from './server-action-Cm80HhlX.js';
3
3
 
4
+ /**
5
+ * Base context type - all contexts extend from this
6
+ */
7
+ type BaseContext = Record<string, unknown>;
4
8
  /**
5
9
  * A middleware function that wraps a server action.
6
10
  * Receives the next function in the chain and the parameters passed to the action.
7
11
  */
8
12
  type Middleware<P extends unknown[], T> = (next: (...params: P) => Promise<ServerActionResult<T>>, ...params: P) => Promise<ServerActionResult<T>>;
13
+ /**
14
+ * A context-aware middleware that can receive context from previous middleware
15
+ * and add new context for downstream middleware.
16
+ *
17
+ * @template P - The parameter types for the action
18
+ * @template T - The return type of the action
19
+ * @template CtxIn - The context type this middleware expects to receive
20
+ * @template CtxOut - The context type this middleware adds (merged with CtxIn for next)
21
+ */
22
+ type ContextMiddleware<P extends unknown[], T, CtxIn extends BaseContext = BaseContext, CtxOut extends BaseContext = BaseContext> = (next: (ctx: CtxIn & CtxOut, ...params: P) => Promise<ServerActionResult<T>>, ctx: CtxIn, ...params: P) => Promise<ServerActionResult<T>>;
23
+ /**
24
+ * A Zod-like schema interface for validation.
25
+ * Works with Zod, Valibot, or any schema library with a compatible safeParse method.
26
+ */
27
+ type ValidationSchema<T> = {
28
+ safeParse(data: unknown): {
29
+ success: true;
30
+ data: T;
31
+ } | {
32
+ success: false;
33
+ error: {
34
+ message?: string;
35
+ errors?: Array<{
36
+ message: string;
37
+ }>;
38
+ };
39
+ };
40
+ };
41
+ type WithValidationOptions = {
42
+ /** Error code to return on validation failure. Defaults to "VALIDATION_ERROR" */
43
+ code?: string;
44
+ /** Custom error message formatter */
45
+ formatError?: (error: {
46
+ message?: string;
47
+ errors?: Array<{
48
+ message: string;
49
+ }>;
50
+ }) => string;
51
+ };
52
+
53
+ /**
54
+ * Creates a type-safe context-aware middleware.
55
+ *
56
+ * @example
57
+ * ```ts
58
+ * type User = { id: string; name: string };
59
+ *
60
+ * const withAuth = createContextMiddleware<
61
+ * [string], // Parameters
62
+ * SomeReturnType, // Return type
63
+ * {}, // Input context (none required)
64
+ * { user: User } // Output context (adds user)
65
+ * >(async (next, ctx, input) => {
66
+ * const user = await authenticate();
67
+ * if (!user) {
68
+ * return { ok: false, message: "Unauthorized", code: "UNAUTHORIZED" };
69
+ * }
70
+ * // Pass the new context with user added
71
+ * return next({ ...ctx, user }, input);
72
+ * });
73
+ * ```
74
+ */
75
+ declare function createContextMiddleware<P extends unknown[], T, CtxIn extends BaseContext = BaseContext, CtxOut extends BaseContext = BaseContext>(handler: ContextMiddleware<P, T, CtxIn, CtxOut>): ContextMiddleware<P, T, CtxIn, CtxOut>;
76
+
77
+ /**
78
+ * A builder for creating server actions with type-safe context accumulation.
79
+ *
80
+ * @template TInput - The input parameters as a tuple type
81
+ * @template TContext - The accumulated context type from all middleware
82
+ */
83
+ declare class ActionBuilder<TInput extends unknown[] = unknown[], TContext extends BaseContext = {}> {
84
+ private middlewares;
85
+ private constructor();
86
+ /**
87
+ * Create a new ActionBuilder instance
88
+ */
89
+ static create<TInput extends unknown[]>(): ActionBuilder<TInput, {}>;
90
+ /**
91
+ * Add a context-aware middleware to the chain.
92
+ * The middleware receives the accumulated context and can add new context.
93
+ *
94
+ * @example
95
+ * ```ts
96
+ * const withUser = createContextMiddleware(async (next, ctx, ...args) => {
97
+ * const user = await getUser();
98
+ * if (!user) {
99
+ * return { ok: false, message: "Unauthorized", code: "UNAUTHORIZED" };
100
+ * }
101
+ * return next({ ...ctx, user }, ...args);
102
+ * });
103
+ *
104
+ * const action = createAction<[{ name: string }]>()
105
+ * .use(withUser)
106
+ * .handle(async (ctx, input) => {
107
+ * // ctx.user is fully typed!
108
+ * });
109
+ * ```
110
+ */
111
+ use<CtxOut extends BaseContext>(middleware: ContextMiddleware<TInput, any, TContext, CtxOut>): ActionBuilder<TInput, TContext & CtxOut>;
112
+ /**
113
+ * Add a regular (non-context) middleware to the chain.
114
+ * The middleware can transform input/output but doesn't modify context.
115
+ *
116
+ * @example
117
+ * ```ts
118
+ * const withLogging = createMiddleware(async (next, ...args) => {
119
+ * console.log("Args:", args);
120
+ * const result = await next(...args);
121
+ * console.log("Result:", result);
122
+ * return result;
123
+ * });
124
+ *
125
+ * const action = createAction<[string]>()
126
+ * .use(withLogging)
127
+ * .handle(async (ctx, input) => { ... });
128
+ * ```
129
+ */
130
+ use(middleware: Middleware<TInput, any>): ActionBuilder<TInput, TContext>;
131
+ /**
132
+ * Define the action handler. This finalizes the builder and returns the executable action.
133
+ * The handler receives the accumulated context and input parameters.
134
+ *
135
+ * @example
136
+ * ```ts
137
+ * const action = createAction<[{ name: string; email: string }]>()
138
+ * .use(withUser)
139
+ * .handle(async (ctx, input) => {
140
+ * // ctx and input are fully typed
141
+ * return { ok: true, data: result };
142
+ * });
143
+ *
144
+ * // Call the action
145
+ * const result = await action({ name: "John", email: "john@example.com" });
146
+ * ```
147
+ */
148
+ handle<TOutput>(handler: (ctx: TContext, ...args: TInput) => Promise<ServerActionResult<TOutput>>): (...args: TInput) => Promise<ServerActionResult<TOutput>>;
149
+ }
150
+ /**
151
+ * Converts a type to a tuple of arguments.
152
+ * - `void` becomes `[]` (no args)
153
+ * - Single type `T` becomes `[T]`
154
+ * - Tuple type `[A, B]` stays as `[A, B]`
155
+ */
156
+ type ToArgs<T> = [T] extends [void] ? [] : T extends unknown[] ? T : [T];
157
+ /**
158
+ * Creates a new action builder with type-safe context accumulation.
159
+ *
160
+ * @example
161
+ * ```ts
162
+ * // Single input parameter
163
+ * const createUser = createAction<{ name: string; email: string }>()
164
+ * .use(withUser)
165
+ * .handle(async (ctx, input) => {
166
+ * await db.users.create({ ...input, createdBy: ctx.user.id });
167
+ * return { ok: true, data: { success: true } };
168
+ * });
169
+ *
170
+ * // Multiple input parameters (use tuple)
171
+ * const updateUser = createAction<[string, { name: string }]>()
172
+ * .use(withUser)
173
+ * .handle(async (ctx, id, data) => {
174
+ * await db.users.update(id, data);
175
+ * return { ok: true, data: { success: true } };
176
+ * });
177
+ *
178
+ * // With validation middleware
179
+ * const createPost = createAction<z.infer<typeof postSchema>>()
180
+ * .use(withZodValidation(postSchema))
181
+ * .use(withUser)
182
+ * .handle(async (ctx, input) => {
183
+ * return { ok: true, data: await db.posts.create(input) };
184
+ * });
185
+ * ```
186
+ */
187
+ declare function createAction<TInput = void>(): ActionBuilder<ToArgs<TInput>, {}>;
188
+
9
189
  /**
10
190
  * Creates a type-safe middleware function.
11
191
  *
@@ -24,77 +204,80 @@ declare function createMiddleware<P extends unknown[], T>(handler: Middleware<P,
24
204
  * Applies middleware to a server action.
25
205
  * Middleware is executed in order (first middleware wraps second, etc).
26
206
  *
207
+ * @deprecated Use `createAction().input(schema).use(middleware).handle(fn)` instead.
208
+ * This provides better type inference and a more composable API.
209
+ *
27
210
  * @example
28
211
  * ```ts
212
+ * // Old way (deprecated):
29
213
  * const protectedAction = applyMiddleware(
30
214
  * myServerAction,
31
215
  * [withAuth, withLogging, withValidation]
32
216
  * );
217
+ *
218
+ * // New way:
219
+ * const protectedAction = createAction()
220
+ * .input(schema)
221
+ * .use(withAuth)
222
+ * .use(withLogging)
223
+ * .handle(async (ctx, input) => { ... });
33
224
  * ```
34
225
  */
35
226
  declare function applyMiddleware<P extends unknown[], T>(action: (...params: P) => Promise<ServerActionResult<T>>, middleware: Middleware<P, T>[]): (...params: P) => Promise<ServerActionResult<T>>;
36
227
  /**
37
228
  * Composes multiple middleware into a single middleware.
38
229
  *
230
+ * @deprecated Use `createAction().use(mw1).use(mw2).handle(fn)` instead.
231
+ * The builder pattern provides better type inference for chained middleware.
232
+ *
39
233
  * @example
40
234
  * ```ts
235
+ * // Old way (deprecated):
41
236
  * const combined = composeMiddleware(withAuth, withLogging, withValidation);
42
237
  * const protectedAction = applyMiddleware(myAction, [combined]);
238
+ *
239
+ * // New way:
240
+ * const protectedAction = createAction()
241
+ * .input(schema)
242
+ * .use(withAuth)
243
+ * .use(withLogging)
244
+ * .handle(async (ctx, input) => { ... });
43
245
  * ```
44
246
  */
45
247
  declare function composeMiddleware<P extends unknown[], T>(...middleware: Middleware<P, T>[]): Middleware<P, T>;
46
- /**
47
- * A Zod-like schema interface for validation.
48
- * Works with Zod, Valibot, or any schema library with a compatible safeParse method.
49
- */
50
- type ValidationSchema<T> = {
51
- safeParse(data: unknown): {
52
- success: true;
53
- data: T;
54
- } | {
55
- success: false;
56
- error: {
57
- message?: string;
58
- errors?: Array<{
59
- message: string;
60
- }>;
61
- };
62
- };
63
- };
64
- type WithValidationOptions = {
65
- /** Error code to return on validation failure. Defaults to "VALIDATION_ERROR" */
66
- code?: string;
67
- /** Custom error message formatter */
68
- formatError?: (error: {
69
- message?: string;
70
- errors?: Array<{
71
- message: string;
72
- }>;
73
- }) => string;
74
- };
75
248
  /**
76
249
  * Creates a middleware that validates the first parameter against a schema.
77
250
  * Works with Zod, Valibot, or any library with a compatible safeParse method.
78
251
  *
252
+ * @deprecated Use `createAction().input(schema).handle(fn)` instead.
253
+ * The `.input()` method provides automatic type inference from the schema.
254
+ *
79
255
  * @example
80
256
  * ```ts
81
257
  * import { z } from "zod";
82
- * import { withValidation, applyMiddleware } from "use-server-action/server";
83
258
  *
84
- * const CreateUserSchema = z.object({
259
+ * const schema = z.object({
85
260
  * name: z.string().min(1),
86
261
  * email: z.string().email(),
87
262
  * });
88
263
  *
264
+ * // Old way (deprecated):
89
265
  * const createUser = applyMiddleware(
90
- * serverAction(async (input: z.infer<typeof CreateUserSchema>) => {
266
+ * serverAction(async (input: z.infer<typeof schema>) => {
91
267
  * return await db.user.create({ data: input });
92
268
  * }),
93
- * [withValidation(CreateUserSchema)]
269
+ * [withZodValidation(schema)]
94
270
  * );
271
+ *
272
+ * // New way:
273
+ * const createUser = createAction()
274
+ * .input(schema) // Type is inferred automatically!
275
+ * .handle(async (ctx, input) => {
276
+ * return { ok: true, data: await db.user.create({ data: input }) };
277
+ * });
95
278
  * ```
96
279
  */
97
- declare function withValidation<TInput, T>(schema: ValidationSchema<TInput>, options?: WithValidationOptions): Middleware<[TInput], T>;
280
+ declare function withZodValidation<TInput, T>(schema: ValidationSchema<TInput>, options?: WithValidationOptions): Middleware<[TInput], T>;
98
281
  /**
99
282
  * Creates a middleware that logs action calls and results.
100
283
  */
@@ -104,4 +287,4 @@ declare function withLogging<P extends unknown[], T>(logger?: {
104
287
  onError?: (message: string, code: string | undefined, params: P) => void;
105
288
  }): Middleware<P, T>;
106
289
 
107
- export { type Middleware, ServerActionResult, type ValidationSchema, type WithValidationOptions, applyMiddleware, composeMiddleware, createMiddleware, withLogging, withValidation };
290
+ export { type BaseContext, type ContextMiddleware, type Middleware, ServerActionResult, type ValidationSchema, type WithValidationOptions, applyMiddleware, composeMiddleware, createAction, createContextMiddleware, createMiddleware, withLogging, withZodValidation };
package/dist/server.js CHANGED
@@ -1,4 +1,5 @@
1
1
  "use strict";
2
+ "use server";
2
3
  var __defProp = Object.defineProperty;
3
4
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
5
  var __getOwnPropNames = Object.getOwnPropertyNames;
@@ -22,6 +23,8 @@ var server_exports = {};
22
23
  __export(server_exports, {
23
24
  applyMiddleware: () => applyMiddleware,
24
25
  composeMiddleware: () => composeMiddleware,
26
+ createAction: () => createAction,
27
+ createContextMiddleware: () => createContextMiddleware,
25
28
  createMiddleware: () => createMiddleware,
26
29
  error: () => error,
27
30
  isError: () => isError,
@@ -31,7 +34,7 @@ __export(server_exports, {
31
34
  unwrap: () => unwrap,
32
35
  unwrapOr: () => unwrapOr,
33
36
  withLogging: () => withLogging,
34
- withValidation: () => withValidation
37
+ withZodValidation: () => withZodValidation
35
38
  });
36
39
  module.exports = __toCommonJS(server_exports);
37
40
 
@@ -75,7 +78,90 @@ function unwrapOr(result, defaultValue) {
75
78
  return defaultValue;
76
79
  }
77
80
 
78
- // src/server/middleware.ts
81
+ // src/server/context-middleware.ts
82
+ var CONTEXT_MIDDLEWARE = /* @__PURE__ */ Symbol("contextMiddleware");
83
+ function isContextMiddleware(fn) {
84
+ return typeof fn === "function" && fn[CONTEXT_MIDDLEWARE] === true;
85
+ }
86
+ function createContextMiddleware(handler) {
87
+ handler[CONTEXT_MIDDLEWARE] = true;
88
+ return handler;
89
+ }
90
+
91
+ // src/server/action-builder.ts
92
+ var ActionBuilder = class _ActionBuilder {
93
+ constructor(middlewares = []) {
94
+ this.middlewares = [];
95
+ this.middlewares = middlewares;
96
+ }
97
+ /**
98
+ * Create a new ActionBuilder instance
99
+ */
100
+ static create() {
101
+ return new _ActionBuilder();
102
+ }
103
+ use(middleware) {
104
+ return new _ActionBuilder([
105
+ ...this.middlewares,
106
+ middleware
107
+ ]);
108
+ }
109
+ /**
110
+ * Define the action handler. This finalizes the builder and returns the executable action.
111
+ * The handler receives the accumulated context and input parameters.
112
+ *
113
+ * @example
114
+ * ```ts
115
+ * const action = createAction<[{ name: string; email: string }]>()
116
+ * .use(withUser)
117
+ * .handle(async (ctx, input) => {
118
+ * // ctx and input are fully typed
119
+ * return { ok: true, data: result };
120
+ * });
121
+ *
122
+ * // Call the action
123
+ * const result = await action({ name: "John", email: "john@example.com" });
124
+ * ```
125
+ */
126
+ handle(handler) {
127
+ const middlewares = [...this.middlewares];
128
+ return async (...args) => {
129
+ const finalHandler = async (ctx, ...a) => {
130
+ return handler(ctx, ...a);
131
+ };
132
+ const chain = middlewares.reduceRight((next, mw) => {
133
+ if (isContextMiddleware(mw)) {
134
+ return async (ctx, ...a) => {
135
+ return mw(
136
+ async (newCtx, ...params) => next(newCtx, ...params),
137
+ ctx,
138
+ ...a
139
+ );
140
+ };
141
+ } else {
142
+ return async (ctx, ...a) => {
143
+ return mw(
144
+ async (...params) => next(ctx, ...params),
145
+ ...a
146
+ );
147
+ };
148
+ }
149
+ }, finalHandler);
150
+ try {
151
+ return await chain({}, ...args);
152
+ } catch (err) {
153
+ const message = err instanceof Error ? err.message : "Unknown error";
154
+ const code = err instanceof Error ? err.name : "UNKNOWN_ERROR";
155
+ return { ok: false, message, code };
156
+ }
157
+ };
158
+ }
159
+ };
160
+ function createAction() {
161
+ return ActionBuilder.create();
162
+ }
163
+
164
+ // src/server/middleware-utils.ts
79
165
  function createMiddleware(handler) {
80
166
  return handler;
81
167
  }
@@ -94,7 +180,7 @@ function composeMiddleware(...middleware) {
94
180
  return chain(...params);
95
181
  };
96
182
  }
97
- function withValidation(schema, options = {}) {
183
+ function withZodValidation(schema, options = {}) {
98
184
  const { code = "VALIDATION_ERROR", formatError } = options;
99
185
  return async (next, input) => {
100
186
  const result = schema.safeParse(input);
@@ -121,6 +207,8 @@ function withLogging(logger = {}) {
121
207
  0 && (module.exports = {
122
208
  applyMiddleware,
123
209
  composeMiddleware,
210
+ createAction,
211
+ createContextMiddleware,
124
212
  createMiddleware,
125
213
  error,
126
214
  isError,
@@ -130,6 +218,6 @@ function withLogging(logger = {}) {
130
218
  unwrap,
131
219
  unwrapOr,
132
220
  withLogging,
133
- withValidation
221
+ withZodValidation
134
222
  });
135
223
  //# sourceMappingURL=server.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/server/index.ts","../src/server/server-action.ts","../src/server/middleware.ts"],"sourcesContent":["export {\n serverAction,\n success,\n error,\n isSuccess,\n isError,\n unwrap,\n unwrapOr,\n} from \"./server-action\";\nexport type {\n ServerActionResult,\n ServerActionSuccess,\n ServerActionError,\n ServerActionFn,\n} from \"./server-action\";\n\nexport {\n createMiddleware,\n applyMiddleware,\n composeMiddleware,\n withValidation,\n withLogging,\n} from \"./middleware\";\nexport type {\n Middleware,\n ValidationSchema,\n WithValidationOptions,\n} from \"./middleware\";\n","\"use server\";\n\nexport type ServerActionSuccess<T> = {\n ok: true;\n data: T;\n};\n\nexport type ServerActionError = {\n ok: false;\n message: string;\n code?: string;\n};\n\nexport type ServerActionResult<T> = ServerActionSuccess<T> | ServerActionError;\n\nexport function success<T>(data: T): ServerActionSuccess<T> {\n return { ok: true, data };\n}\n\nexport function error(message: string, code?: string): ServerActionError {\n return { ok: false, message, code };\n}\n\nexport type ServerActionFn<P extends unknown[], T> = (\n ...args: P\n) => Promise<ServerActionResult<T>>;\n\ntype ServerActionOptions = {\n onError?: (error: unknown) => void;\n};\n\n/**\n * Wraps an async function to return a standardized ServerActionResult.\n * Catches any thrown errors and converts them to error results.\n */\nexport function serverAction<P extends unknown[], T>(\n fn: (...args: P) => Promise<T>,\n options?: ServerActionOptions,\n): ServerActionFn<P, T> {\n return async (...args: P): Promise<ServerActionResult<T>> => {\n try {\n const data = await fn(...args);\n return success(data);\n } catch (err) {\n options?.onError?.(err);\n\n if (err instanceof Error) {\n return error(err.message, err.name);\n }\n\n return error(\"An unexpected error occurred\", \"UNKNOWN_ERROR\");\n }\n };\n}\n\n/**\n * Type guard to check if a result is successful\n */\nexport function isSuccess<T>(\n result: ServerActionResult<T>,\n): result is ServerActionSuccess<T> {\n return result.ok === true;\n}\n\n/**\n * Type guard to check if a result is an error\n */\nexport function isError<T>(\n result: ServerActionResult<T>,\n): result is ServerActionError {\n return result.ok === false;\n}\n\n/**\n * Unwraps a successful result or throws the error message\n */\nexport function unwrap<T>(result: ServerActionResult<T>): T {\n if (result.ok) {\n return result.data;\n }\n throw new Error(result.message);\n}\n\n/**\n * Unwraps a successful result or returns a default value\n */\nexport function unwrapOr<T>(result: ServerActionResult<T>, defaultValue: T): T {\n if (result.ok) {\n return result.data;\n }\n return defaultValue;\n}\n","\"use server\";\n\nimport type { ServerActionResult } from \"./server-action\";\n\n/**\n * A middleware function that wraps a server action.\n * Receives the next function in the chain and the parameters passed to the action.\n */\nexport type Middleware<P extends unknown[], T> = (\n next: (...params: P) => Promise<ServerActionResult<T>>,\n ...params: P\n) => Promise<ServerActionResult<T>>;\n\n/**\n * Creates a type-safe middleware function.\n *\n * @example\n * ```ts\n * const withLogging = createMiddleware(async (next, ...params) => {\n * console.log(\"Calling action with:\", params);\n * const result = await next(...params);\n * console.log(\"Result:\", result);\n * return result;\n * });\n * ```\n */\nexport function createMiddleware<P extends unknown[], T>(\n handler: Middleware<P, T>,\n): Middleware<P, T> {\n return handler;\n}\n\n/**\n * Applies middleware to a server action.\n * Middleware is executed in order (first middleware wraps second, etc).\n *\n * @example\n * ```ts\n * const protectedAction = applyMiddleware(\n * myServerAction,\n * [withAuth, withLogging, withValidation]\n * );\n * ```\n */\nexport function applyMiddleware<P extends unknown[], T>(\n action: (...params: P) => Promise<ServerActionResult<T>>,\n middleware: Middleware<P, T>[],\n): (...params: P) => Promise<ServerActionResult<T>> {\n return middleware.reduceRight(\n (next, mw) =>\n (...params: P) =>\n mw(next, ...params),\n action,\n );\n}\n\n/**\n * Composes multiple middleware into a single middleware.\n *\n * @example\n * ```ts\n * const combined = composeMiddleware(withAuth, withLogging, withValidation);\n * const protectedAction = applyMiddleware(myAction, [combined]);\n * ```\n */\nexport function composeMiddleware<P extends unknown[], T>(\n ...middleware: Middleware<P, T>[]\n): Middleware<P, T> {\n return (next, ...params) => {\n const chain = middleware.reduceRight(\n (nextFn, mw) =>\n (...p: P) =>\n mw(nextFn, ...p),\n next,\n );\n return chain(...params);\n };\n}\n\n/**\n * A Zod-like schema interface for validation.\n * Works with Zod, Valibot, or any schema library with a compatible safeParse method.\n */\nexport type ValidationSchema<T> = {\n safeParse(data: unknown):\n | { success: true; data: T }\n | { success: false; error: { message?: string; errors?: Array<{ message: string }> } };\n};\n\nexport type WithValidationOptions = {\n /** Error code to return on validation failure. Defaults to \"VALIDATION_ERROR\" */\n code?: string;\n /** Custom error message formatter */\n formatError?: (error: {\n message?: string;\n errors?: Array<{ message: string }>;\n }) => string;\n};\n\n/**\n * Creates a middleware that validates the first parameter against a schema.\n * Works with Zod, Valibot, or any library with a compatible safeParse method.\n *\n * @example\n * ```ts\n * import { z } from \"zod\";\n * import { withValidation, applyMiddleware } from \"use-server-action/server\";\n *\n * const CreateUserSchema = z.object({\n * name: z.string().min(1),\n * email: z.string().email(),\n * });\n *\n * const createUser = applyMiddleware(\n * serverAction(async (input: z.infer<typeof CreateUserSchema>) => {\n * return await db.user.create({ data: input });\n * }),\n * [withValidation(CreateUserSchema)]\n * );\n * ```\n */\nexport function withValidation<TInput, T>(\n schema: ValidationSchema<TInput>,\n options: WithValidationOptions = {},\n): Middleware<[TInput], T> {\n const { code = \"VALIDATION_ERROR\", formatError } = options;\n\n return async (next, input) => {\n const result = schema.safeParse(input);\n\n if (!result.success) {\n const message = formatError\n ? formatError(result.error)\n : result.error.errors?.[0]?.message ??\n result.error.message ??\n \"Validation failed\";\n\n return { ok: false, message, code };\n }\n\n return next(result.data);\n };\n}\n\n/**\n * Creates a middleware that logs action calls and results.\n */\nexport function withLogging<P extends unknown[], T>(\n logger: {\n onCall?: (params: P) => void;\n onSuccess?: (data: T, params: P) => void;\n onError?: (\n message: string,\n code: string | undefined,\n params: P,\n ) => void;\n } = {},\n): Middleware<P, T> {\n return async (next, ...params) => {\n logger.onCall?.(params);\n const result = await next(...params);\n if (result.ok) {\n logger.onSuccess?.(result.data, params);\n } else {\n logger.onError?.(result.message, result.code, params);\n }\n return result;\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACeO,SAAS,QAAW,MAAiC;AACxD,SAAO,EAAE,IAAI,MAAM,KAAK;AAC5B;AAEO,SAAS,MAAM,SAAiB,MAAkC;AACrE,SAAO,EAAE,IAAI,OAAO,SAAS,KAAK;AACtC;AAcO,SAAS,aACZ,IACA,SACoB;AACpB,SAAO,UAAU,SAA4C;AACzD,QAAI;AACA,YAAM,OAAO,MAAM,GAAG,GAAG,IAAI;AAC7B,aAAO,QAAQ,IAAI;AAAA,IACvB,SAAS,KAAK;AACV,eAAS,UAAU,GAAG;AAEtB,UAAI,eAAe,OAAO;AACtB,eAAO,MAAM,IAAI,SAAS,IAAI,IAAI;AAAA,MACtC;AAEA,aAAO,MAAM,gCAAgC,eAAe;AAAA,IAChE;AAAA,EACJ;AACJ;AAKO,SAAS,UACZ,QACgC;AAChC,SAAO,OAAO,OAAO;AACzB;AAKO,SAAS,QACZ,QAC2B;AAC3B,SAAO,OAAO,OAAO;AACzB;AAKO,SAAS,OAAU,QAAkC;AACxD,MAAI,OAAO,IAAI;AACX,WAAO,OAAO;AAAA,EAClB;AACA,QAAM,IAAI,MAAM,OAAO,OAAO;AAClC;AAKO,SAAS,SAAY,QAA+B,cAAoB;AAC3E,MAAI,OAAO,IAAI;AACX,WAAO,OAAO;AAAA,EAClB;AACA,SAAO;AACX;;;ACjEO,SAAS,iBACZ,SACgB;AAChB,SAAO;AACX;AAcO,SAAS,gBACZ,QACA,YACgD;AAChD,SAAO,WAAW;AAAA,IACd,CAAC,MAAM,OACH,IAAI,WACA,GAAG,MAAM,GAAG,MAAM;AAAA,IAC1B;AAAA,EACJ;AACJ;AAWO,SAAS,qBACT,YACa;AAChB,SAAO,CAAC,SAAS,WAAW;AACxB,UAAM,QAAQ,WAAW;AAAA,MACrB,CAAC,QAAQ,OACL,IAAI,MACA,GAAG,QAAQ,GAAG,CAAC;AAAA,MACvB;AAAA,IACJ;AACA,WAAO,MAAM,GAAG,MAAM;AAAA,EAC1B;AACJ;AA4CO,SAAS,eACZ,QACA,UAAiC,CAAC,GACX;AACvB,QAAM,EAAE,OAAO,oBAAoB,YAAY,IAAI;AAEnD,SAAO,OAAO,MAAM,UAAU;AAC1B,UAAM,SAAS,OAAO,UAAU,KAAK;AAErC,QAAI,CAAC,OAAO,SAAS;AACjB,YAAM,UAAU,cACV,YAAY,OAAO,KAAK,IACxB,OAAO,MAAM,SAAS,CAAC,GAAG,WAC1B,OAAO,MAAM,WACb;AAEN,aAAO,EAAE,IAAI,OAAO,SAAS,KAAK;AAAA,IACtC;AAEA,WAAO,KAAK,OAAO,IAAI;AAAA,EAC3B;AACJ;AAKO,SAAS,YACZ,SAQI,CAAC,GACW;AAChB,SAAO,OAAO,SAAS,WAAW;AAC9B,WAAO,SAAS,MAAM;AACtB,UAAM,SAAS,MAAM,KAAK,GAAG,MAAM;AACnC,QAAI,OAAO,IAAI;AACX,aAAO,YAAY,OAAO,MAAM,MAAM;AAAA,IAC1C,OAAO;AACH,aAAO,UAAU,OAAO,SAAS,OAAO,MAAM,MAAM;AAAA,IACxD;AACA,WAAO;AAAA,EACX;AACJ;","names":[]}
1
+ {"version":3,"sources":["../src/server/index.ts","../src/server/server-action.ts","../src/server/context-middleware.ts","../src/server/action-builder.ts","../src/server/middleware-utils.ts"],"sourcesContent":["\"use server\";\n\n// Server action utilities\nexport {\n serverAction,\n success,\n error,\n isSuccess,\n isError,\n unwrap,\n unwrapOr,\n} from \"./server-action\";\nexport type {\n ServerActionResult,\n ServerActionSuccess,\n ServerActionError,\n ServerActionFn,\n} from \"./server-action\";\n\n// Types\nexport type {\n BaseContext,\n ContextMiddleware,\n Middleware,\n ValidationSchema,\n WithValidationOptions,\n} from \"./types\";\n\n// Context middleware\nexport { createContextMiddleware } from \"./context-middleware\";\n\n// Action builder\nexport { createAction } from \"./action-builder\";\n\n// Middleware utilities\nexport {\n createMiddleware,\n applyMiddleware,\n composeMiddleware,\n withZodValidation,\n withLogging,\n} from \"./middleware-utils\";\n","\"use server\";\n\nexport type ServerActionSuccess<T> = {\n ok: true;\n data: T;\n};\n\nexport type ServerActionError = {\n ok: false;\n message: string;\n code?: string;\n};\n\nexport type ServerActionResult<T> = ServerActionSuccess<T> | ServerActionError;\n\nexport function success<T>(data: T): ServerActionSuccess<T> {\n return { ok: true, data };\n}\n\nexport function error(message: string, code?: string): ServerActionError {\n return { ok: false, message, code };\n}\n\nexport type ServerActionFn<P extends unknown[], T> = (\n ...args: P\n) => Promise<ServerActionResult<T>>;\n\ntype ServerActionOptions = {\n onError?: (error: unknown) => void;\n};\n\n/**\n * Wraps an async function to return a standardized ServerActionResult.\n * Catches any thrown errors and converts them to error results.\n *\n * @deprecated Use `createAction().input(schema).handle(fn)` instead.\n * The new API provides better type inference and built-in validation.\n *\n * @example\n * ```ts\n * // Old way (deprecated):\n * const myAction = serverAction(async (input: MyInput) => {\n * return await doSomething(input);\n * });\n *\n * // New way:\n * const myAction = createAction()\n * .input(mySchema)\n * .handle(async (ctx, input) => {\n * return { ok: true, data: await doSomething(input) };\n * });\n * ```\n */\nexport function serverAction<P extends unknown[], T>(\n fn: (...args: P) => Promise<T>,\n options?: ServerActionOptions,\n): ServerActionFn<P, T> {\n return async (...args: P): Promise<ServerActionResult<T>> => {\n try {\n const data = await fn(...args);\n return success(data);\n } catch (err) {\n options?.onError?.(err);\n\n if (err instanceof Error) {\n return error(err.message, err.name);\n }\n\n return error(\"An unexpected error occurred\", \"UNKNOWN_ERROR\");\n }\n };\n}\n\n/**\n * Type guard to check if a result is successful\n */\nexport function isSuccess<T>(\n result: ServerActionResult<T>,\n): result is ServerActionSuccess<T> {\n return result.ok === true;\n}\n\n/**\n * Type guard to check if a result is an error\n */\nexport function isError<T>(\n result: ServerActionResult<T>,\n): result is ServerActionError {\n return result.ok === false;\n}\n\n/**\n * Unwraps a successful result or throws the error message\n */\nexport function unwrap<T>(result: ServerActionResult<T>): T {\n if (result.ok) {\n return result.data;\n }\n throw new Error(result.message);\n}\n\n/**\n * Unwraps a successful result or returns a default value\n */\nexport function unwrapOr<T>(result: ServerActionResult<T>, defaultValue: T): T {\n if (result.ok) {\n return result.data;\n }\n return defaultValue;\n}\n","\"use server\";\n\nimport type { BaseContext, ContextMiddleware } from \"./types\";\n\n/**\n * Symbol to identify context-aware middleware at runtime\n */\nexport const CONTEXT_MIDDLEWARE = Symbol(\"contextMiddleware\");\n\n/**\n * Check if a middleware is context-aware\n */\nexport function isContextMiddleware(fn: unknown): boolean {\n return typeof fn === \"function\" && (fn as any)[CONTEXT_MIDDLEWARE] === true;\n}\n\n/**\n * Creates a type-safe context-aware middleware.\n *\n * @example\n * ```ts\n * type User = { id: string; name: string };\n *\n * const withAuth = createContextMiddleware<\n * [string], // Parameters\n * SomeReturnType, // Return type\n * {}, // Input context (none required)\n * { user: User } // Output context (adds user)\n * >(async (next, ctx, input) => {\n * const user = await authenticate();\n * if (!user) {\n * return { ok: false, message: \"Unauthorized\", code: \"UNAUTHORIZED\" };\n * }\n * // Pass the new context with user added\n * return next({ ...ctx, user }, input);\n * });\n * ```\n */\nexport function createContextMiddleware<\n P extends unknown[],\n T,\n CtxIn extends BaseContext = BaseContext,\n CtxOut extends BaseContext = BaseContext,\n>(\n handler: ContextMiddleware<P, T, CtxIn, CtxOut>,\n): ContextMiddleware<P, T, CtxIn, CtxOut> {\n (handler as any)[CONTEXT_MIDDLEWARE] = true;\n return handler;\n}\n","\"use server\";\n\nimport type { ServerActionResult } from \"./server-action\";\nimport type {\n BaseContext,\n ContextMiddleware,\n Middleware,\n} from \"./types\";\nimport { isContextMiddleware } from \"./context-middleware\";\n\n/**\n * A builder for creating server actions with type-safe context accumulation.\n *\n * @template TInput - The input parameters as a tuple type\n * @template TContext - The accumulated context type from all middleware\n */\nclass ActionBuilder<\n TInput extends unknown[] = unknown[],\n TContext extends BaseContext = {},\n> {\n private middlewares: Array<\n ContextMiddleware<any, any, any, any> | Middleware<any, any>\n > = [];\n\n private constructor(\n middlewares: Array<\n ContextMiddleware<any, any, any, any> | Middleware<any, any>\n > = [],\n ) {\n this.middlewares = middlewares;\n }\n\n /**\n * Create a new ActionBuilder instance\n */\n static create<TInput extends unknown[]>(): ActionBuilder<TInput, {}> {\n return new ActionBuilder<TInput, {}>();\n }\n\n /**\n * Add a context-aware middleware to the chain.\n * The middleware receives the accumulated context and can add new context.\n *\n * @example\n * ```ts\n * const withUser = createContextMiddleware(async (next, ctx, ...args) => {\n * const user = await getUser();\n * if (!user) {\n * return { ok: false, message: \"Unauthorized\", code: \"UNAUTHORIZED\" };\n * }\n * return next({ ...ctx, user }, ...args);\n * });\n *\n * const action = createAction<[{ name: string }]>()\n * .use(withUser)\n * .handle(async (ctx, input) => {\n * // ctx.user is fully typed!\n * });\n * ```\n */\n use<CtxOut extends BaseContext>(\n middleware: ContextMiddleware<TInput, any, TContext, CtxOut>,\n ): ActionBuilder<TInput, TContext & CtxOut>;\n\n /**\n * Add a regular (non-context) middleware to the chain.\n * The middleware can transform input/output but doesn't modify context.\n *\n * @example\n * ```ts\n * const withLogging = createMiddleware(async (next, ...args) => {\n * console.log(\"Args:\", args);\n * const result = await next(...args);\n * console.log(\"Result:\", result);\n * return result;\n * });\n *\n * const action = createAction<[string]>()\n * .use(withLogging)\n * .handle(async (ctx, input) => { ... });\n * ```\n */\n use(middleware: Middleware<TInput, any>): ActionBuilder<TInput, TContext>;\n\n use(middleware: any): any {\n return new ActionBuilder<TInput, TContext>([\n ...this.middlewares,\n middleware,\n ]);\n }\n\n /**\n * Define the action handler. This finalizes the builder and returns the executable action.\n * The handler receives the accumulated context and input parameters.\n *\n * @example\n * ```ts\n * const action = createAction<[{ name: string; email: string }]>()\n * .use(withUser)\n * .handle(async (ctx, input) => {\n * // ctx and input are fully typed\n * return { ok: true, data: result };\n * });\n *\n * // Call the action\n * const result = await action({ name: \"John\", email: \"john@example.com\" });\n * ```\n */\n handle<TOutput>(\n handler: (\n ctx: TContext,\n ...args: TInput\n ) => Promise<ServerActionResult<TOutput>>,\n ): (...args: TInput) => Promise<ServerActionResult<TOutput>> {\n const middlewares = [...this.middlewares];\n\n return async (...args: TInput): Promise<ServerActionResult<TOutput>> => {\n // Build the middleware chain\n type CtxNextFn = (\n ctx: BaseContext,\n ...args: TInput\n ) => Promise<ServerActionResult<TOutput>>;\n\n const finalHandler: CtxNextFn = async (ctx, ...a) => {\n return handler(ctx as TContext, ...a);\n };\n\n // Build chain from right to left (last middleware wraps closest to handler)\n const chain = middlewares.reduceRight<CtxNextFn>((next, mw) => {\n if (isContextMiddleware(mw)) {\n // Context middleware: (next, ctx, ...params) => ...\n return async (ctx, ...a) => {\n return (mw as ContextMiddleware<TInput, TOutput, any, any>)(\n async (newCtx, ...params) => next(newCtx, ...(params as TInput)),\n ctx,\n ...a,\n );\n };\n } else {\n // Regular middleware: (next, ...params) => ...\n // Pass context through unchanged\n return async (ctx, ...a) => {\n return (mw as Middleware<TInput, TOutput>)(\n async (...params) => next(ctx, ...(params as TInput)),\n ...a,\n );\n };\n }\n }, finalHandler);\n\n // Start with empty context\n try {\n return await chain({}, ...args);\n } catch (err) {\n const message =\n err instanceof Error ? err.message : \"Unknown error\";\n const code =\n err instanceof Error ? err.name : \"UNKNOWN_ERROR\";\n return { ok: false, message, code };\n }\n };\n }\n}\n\n/**\n * Converts a type to a tuple of arguments.\n * - `void` becomes `[]` (no args)\n * - Single type `T` becomes `[T]`\n * - Tuple type `[A, B]` stays as `[A, B]`\n */\ntype ToArgs<T> = [T] extends [void] ? [] : T extends unknown[] ? T : [T];\n\n/**\n * Creates a new action builder with type-safe context accumulation.\n *\n * @example\n * ```ts\n * // Single input parameter\n * const createUser = createAction<{ name: string; email: string }>()\n * .use(withUser)\n * .handle(async (ctx, input) => {\n * await db.users.create({ ...input, createdBy: ctx.user.id });\n * return { ok: true, data: { success: true } };\n * });\n *\n * // Multiple input parameters (use tuple)\n * const updateUser = createAction<[string, { name: string }]>()\n * .use(withUser)\n * .handle(async (ctx, id, data) => {\n * await db.users.update(id, data);\n * return { ok: true, data: { success: true } };\n * });\n *\n * // With validation middleware\n * const createPost = createAction<z.infer<typeof postSchema>>()\n * .use(withZodValidation(postSchema))\n * .use(withUser)\n * .handle(async (ctx, input) => {\n * return { ok: true, data: await db.posts.create(input) };\n * });\n * ```\n */\nexport function createAction<TInput = void>(): ActionBuilder<ToArgs<TInput>, {}> {\n return ActionBuilder.create<ToArgs<TInput>>();\n}\n","\"use server\";\n\nimport type { ServerActionResult } from \"./server-action\";\nimport type { Middleware, ValidationSchema, WithValidationOptions } from \"./types\";\n\n/**\n * Creates a type-safe middleware function.\n *\n * @example\n * ```ts\n * const withLogging = createMiddleware(async (next, ...params) => {\n * console.log(\"Calling action with:\", params);\n * const result = await next(...params);\n * console.log(\"Result:\", result);\n * return result;\n * });\n * ```\n */\nexport function createMiddleware<P extends unknown[], T>(\n handler: Middleware<P, T>,\n): Middleware<P, T> {\n return handler;\n}\n\n/**\n * Applies middleware to a server action.\n * Middleware is executed in order (first middleware wraps second, etc).\n *\n * @deprecated Use `createAction().input(schema).use(middleware).handle(fn)` instead.\n * This provides better type inference and a more composable API.\n *\n * @example\n * ```ts\n * // Old way (deprecated):\n * const protectedAction = applyMiddleware(\n * myServerAction,\n * [withAuth, withLogging, withValidation]\n * );\n *\n * // New way:\n * const protectedAction = createAction()\n * .input(schema)\n * .use(withAuth)\n * .use(withLogging)\n * .handle(async (ctx, input) => { ... });\n * ```\n */\nexport function applyMiddleware<P extends unknown[], T>(\n action: (...params: P) => Promise<ServerActionResult<T>>,\n middleware: Middleware<P, T>[],\n): (...params: P) => Promise<ServerActionResult<T>> {\n return middleware.reduceRight(\n (next, mw) =>\n (...params: P) =>\n mw(next, ...params),\n action,\n );\n}\n\n/**\n * Composes multiple middleware into a single middleware.\n *\n * @deprecated Use `createAction().use(mw1).use(mw2).handle(fn)` instead.\n * The builder pattern provides better type inference for chained middleware.\n *\n * @example\n * ```ts\n * // Old way (deprecated):\n * const combined = composeMiddleware(withAuth, withLogging, withValidation);\n * const protectedAction = applyMiddleware(myAction, [combined]);\n *\n * // New way:\n * const protectedAction = createAction()\n * .input(schema)\n * .use(withAuth)\n * .use(withLogging)\n * .handle(async (ctx, input) => { ... });\n * ```\n */\nexport function composeMiddleware<P extends unknown[], T>(\n ...middleware: Middleware<P, T>[]\n): Middleware<P, T> {\n return (next, ...params) => {\n const chain = middleware.reduceRight(\n (nextFn, mw) =>\n (...p: P) =>\n mw(nextFn, ...p),\n next,\n );\n return chain(...params);\n };\n}\n\n/**\n * Creates a middleware that validates the first parameter against a schema.\n * Works with Zod, Valibot, or any library with a compatible safeParse method.\n *\n * @deprecated Use `createAction().input(schema).handle(fn)` instead.\n * The `.input()` method provides automatic type inference from the schema.\n *\n * @example\n * ```ts\n * import { z } from \"zod\";\n *\n * const schema = z.object({\n * name: z.string().min(1),\n * email: z.string().email(),\n * });\n *\n * // Old way (deprecated):\n * const createUser = applyMiddleware(\n * serverAction(async (input: z.infer<typeof schema>) => {\n * return await db.user.create({ data: input });\n * }),\n * [withZodValidation(schema)]\n * );\n *\n * // New way:\n * const createUser = createAction()\n * .input(schema) // Type is inferred automatically!\n * .handle(async (ctx, input) => {\n * return { ok: true, data: await db.user.create({ data: input }) };\n * });\n * ```\n */\nexport function withZodValidation<TInput, T>(\n schema: ValidationSchema<TInput>,\n options: WithValidationOptions = {},\n): Middleware<[TInput], T> {\n const { code = \"VALIDATION_ERROR\", formatError } = options;\n\n return async (next, input) => {\n const result = schema.safeParse(input);\n\n if (!result.success) {\n const message = formatError\n ? formatError(result.error)\n : (result.error.errors?.[0]?.message ??\n result.error.message ??\n \"Validation failed\");\n\n return { ok: false, message, code };\n }\n\n return next(result.data);\n };\n}\n\n/**\n * Creates a middleware that logs action calls and results.\n */\nexport function withLogging<P extends unknown[], T>(\n logger: {\n onCall?: (params: P) => void;\n onSuccess?: (data: T, params: P) => void;\n onError?: (\n message: string,\n code: string | undefined,\n params: P,\n ) => void;\n } = {},\n): Middleware<P, T> {\n return async (next, ...params) => {\n logger.onCall?.(params);\n const result = await next(...params);\n if (result.ok) {\n logger.onSuccess?.(result.data, params);\n } else {\n logger.onError?.(result.message, result.code, params);\n }\n return result;\n };\n}\n"],"mappings":"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","names":[]}
package/dist/server.mjs CHANGED
@@ -1,3 +1,5 @@
1
+ "use server";
2
+
1
3
  // src/server/server-action.ts
2
4
  function success(data) {
3
5
  return { ok: true, data };
@@ -38,7 +40,90 @@ function unwrapOr(result, defaultValue) {
38
40
  return defaultValue;
39
41
  }
40
42
 
41
- // src/server/middleware.ts
43
+ // src/server/context-middleware.ts
44
+ var CONTEXT_MIDDLEWARE = /* @__PURE__ */ Symbol("contextMiddleware");
45
+ function isContextMiddleware(fn) {
46
+ return typeof fn === "function" && fn[CONTEXT_MIDDLEWARE] === true;
47
+ }
48
+ function createContextMiddleware(handler) {
49
+ handler[CONTEXT_MIDDLEWARE] = true;
50
+ return handler;
51
+ }
52
+
53
+ // src/server/action-builder.ts
54
+ var ActionBuilder = class _ActionBuilder {
55
+ constructor(middlewares = []) {
56
+ this.middlewares = [];
57
+ this.middlewares = middlewares;
58
+ }
59
+ /**
60
+ * Create a new ActionBuilder instance
61
+ */
62
+ static create() {
63
+ return new _ActionBuilder();
64
+ }
65
+ use(middleware) {
66
+ return new _ActionBuilder([
67
+ ...this.middlewares,
68
+ middleware
69
+ ]);
70
+ }
71
+ /**
72
+ * Define the action handler. This finalizes the builder and returns the executable action.
73
+ * The handler receives the accumulated context and input parameters.
74
+ *
75
+ * @example
76
+ * ```ts
77
+ * const action = createAction<[{ name: string; email: string }]>()
78
+ * .use(withUser)
79
+ * .handle(async (ctx, input) => {
80
+ * // ctx and input are fully typed
81
+ * return { ok: true, data: result };
82
+ * });
83
+ *
84
+ * // Call the action
85
+ * const result = await action({ name: "John", email: "john@example.com" });
86
+ * ```
87
+ */
88
+ handle(handler) {
89
+ const middlewares = [...this.middlewares];
90
+ return async (...args) => {
91
+ const finalHandler = async (ctx, ...a) => {
92
+ return handler(ctx, ...a);
93
+ };
94
+ const chain = middlewares.reduceRight((next, mw) => {
95
+ if (isContextMiddleware(mw)) {
96
+ return async (ctx, ...a) => {
97
+ return mw(
98
+ async (newCtx, ...params) => next(newCtx, ...params),
99
+ ctx,
100
+ ...a
101
+ );
102
+ };
103
+ } else {
104
+ return async (ctx, ...a) => {
105
+ return mw(
106
+ async (...params) => next(ctx, ...params),
107
+ ...a
108
+ );
109
+ };
110
+ }
111
+ }, finalHandler);
112
+ try {
113
+ return await chain({}, ...args);
114
+ } catch (err) {
115
+ const message = err instanceof Error ? err.message : "Unknown error";
116
+ const code = err instanceof Error ? err.name : "UNKNOWN_ERROR";
117
+ return { ok: false, message, code };
118
+ }
119
+ };
120
+ }
121
+ };
122
+ function createAction() {
123
+ return ActionBuilder.create();
124
+ }
125
+
126
+ // src/server/middleware-utils.ts
42
127
  function createMiddleware(handler) {
43
128
  return handler;
44
129
  }
@@ -57,7 +142,7 @@ function composeMiddleware(...middleware) {
57
142
  return chain(...params);
58
143
  };
59
144
  }
60
- function withValidation(schema, options = {}) {
145
+ function withZodValidation(schema, options = {}) {
61
146
  const { code = "VALIDATION_ERROR", formatError } = options;
62
147
  return async (next, input) => {
63
148
  const result = schema.safeParse(input);
@@ -83,6 +168,8 @@ function withLogging(logger = {}) {
83
168
  export {
84
169
  applyMiddleware,
85
170
  composeMiddleware,
171
+ createAction,
172
+ createContextMiddleware,
86
173
  createMiddleware,
87
174
  error,
88
175
  isError,
@@ -92,6 +179,6 @@ export {
92
179
  unwrap,
93
180
  unwrapOr,
94
181
  withLogging,
95
- withValidation
182
+ withZodValidation
96
183
  };
97
184
  //# sourceMappingURL=server.mjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/server/server-action.ts","../src/server/middleware.ts"],"sourcesContent":["\"use server\";\n\nexport type ServerActionSuccess<T> = {\n ok: true;\n data: T;\n};\n\nexport type ServerActionError = {\n ok: false;\n message: string;\n code?: string;\n};\n\nexport type ServerActionResult<T> = ServerActionSuccess<T> | ServerActionError;\n\nexport function success<T>(data: T): ServerActionSuccess<T> {\n return { ok: true, data };\n}\n\nexport function error(message: string, code?: string): ServerActionError {\n return { ok: false, message, code };\n}\n\nexport type ServerActionFn<P extends unknown[], T> = (\n ...args: P\n) => Promise<ServerActionResult<T>>;\n\ntype ServerActionOptions = {\n onError?: (error: unknown) => void;\n};\n\n/**\n * Wraps an async function to return a standardized ServerActionResult.\n * Catches any thrown errors and converts them to error results.\n */\nexport function serverAction<P extends unknown[], T>(\n fn: (...args: P) => Promise<T>,\n options?: ServerActionOptions,\n): ServerActionFn<P, T> {\n return async (...args: P): Promise<ServerActionResult<T>> => {\n try {\n const data = await fn(...args);\n return success(data);\n } catch (err) {\n options?.onError?.(err);\n\n if (err instanceof Error) {\n return error(err.message, err.name);\n }\n\n return error(\"An unexpected error occurred\", \"UNKNOWN_ERROR\");\n }\n };\n}\n\n/**\n * Type guard to check if a result is successful\n */\nexport function isSuccess<T>(\n result: ServerActionResult<T>,\n): result is ServerActionSuccess<T> {\n return result.ok === true;\n}\n\n/**\n * Type guard to check if a result is an error\n */\nexport function isError<T>(\n result: ServerActionResult<T>,\n): result is ServerActionError {\n return result.ok === false;\n}\n\n/**\n * Unwraps a successful result or throws the error message\n */\nexport function unwrap<T>(result: ServerActionResult<T>): T {\n if (result.ok) {\n return result.data;\n }\n throw new Error(result.message);\n}\n\n/**\n * Unwraps a successful result or returns a default value\n */\nexport function unwrapOr<T>(result: ServerActionResult<T>, defaultValue: T): T {\n if (result.ok) {\n return result.data;\n }\n return defaultValue;\n}\n","\"use server\";\n\nimport type { ServerActionResult } from \"./server-action\";\n\n/**\n * A middleware function that wraps a server action.\n * Receives the next function in the chain and the parameters passed to the action.\n */\nexport type Middleware<P extends unknown[], T> = (\n next: (...params: P) => Promise<ServerActionResult<T>>,\n ...params: P\n) => Promise<ServerActionResult<T>>;\n\n/**\n * Creates a type-safe middleware function.\n *\n * @example\n * ```ts\n * const withLogging = createMiddleware(async (next, ...params) => {\n * console.log(\"Calling action with:\", params);\n * const result = await next(...params);\n * console.log(\"Result:\", result);\n * return result;\n * });\n * ```\n */\nexport function createMiddleware<P extends unknown[], T>(\n handler: Middleware<P, T>,\n): Middleware<P, T> {\n return handler;\n}\n\n/**\n * Applies middleware to a server action.\n * Middleware is executed in order (first middleware wraps second, etc).\n *\n * @example\n * ```ts\n * const protectedAction = applyMiddleware(\n * myServerAction,\n * [withAuth, withLogging, withValidation]\n * );\n * ```\n */\nexport function applyMiddleware<P extends unknown[], T>(\n action: (...params: P) => Promise<ServerActionResult<T>>,\n middleware: Middleware<P, T>[],\n): (...params: P) => Promise<ServerActionResult<T>> {\n return middleware.reduceRight(\n (next, mw) =>\n (...params: P) =>\n mw(next, ...params),\n action,\n );\n}\n\n/**\n * Composes multiple middleware into a single middleware.\n *\n * @example\n * ```ts\n * const combined = composeMiddleware(withAuth, withLogging, withValidation);\n * const protectedAction = applyMiddleware(myAction, [combined]);\n * ```\n */\nexport function composeMiddleware<P extends unknown[], T>(\n ...middleware: Middleware<P, T>[]\n): Middleware<P, T> {\n return (next, ...params) => {\n const chain = middleware.reduceRight(\n (nextFn, mw) =>\n (...p: P) =>\n mw(nextFn, ...p),\n next,\n );\n return chain(...params);\n };\n}\n\n/**\n * A Zod-like schema interface for validation.\n * Works with Zod, Valibot, or any schema library with a compatible safeParse method.\n */\nexport type ValidationSchema<T> = {\n safeParse(data: unknown):\n | { success: true; data: T }\n | { success: false; error: { message?: string; errors?: Array<{ message: string }> } };\n};\n\nexport type WithValidationOptions = {\n /** Error code to return on validation failure. Defaults to \"VALIDATION_ERROR\" */\n code?: string;\n /** Custom error message formatter */\n formatError?: (error: {\n message?: string;\n errors?: Array<{ message: string }>;\n }) => string;\n};\n\n/**\n * Creates a middleware that validates the first parameter against a schema.\n * Works with Zod, Valibot, or any library with a compatible safeParse method.\n *\n * @example\n * ```ts\n * import { z } from \"zod\";\n * import { withValidation, applyMiddleware } from \"use-server-action/server\";\n *\n * const CreateUserSchema = z.object({\n * name: z.string().min(1),\n * email: z.string().email(),\n * });\n *\n * const createUser = applyMiddleware(\n * serverAction(async (input: z.infer<typeof CreateUserSchema>) => {\n * return await db.user.create({ data: input });\n * }),\n * [withValidation(CreateUserSchema)]\n * );\n * ```\n */\nexport function withValidation<TInput, T>(\n schema: ValidationSchema<TInput>,\n options: WithValidationOptions = {},\n): Middleware<[TInput], T> {\n const { code = \"VALIDATION_ERROR\", formatError } = options;\n\n return async (next, input) => {\n const result = schema.safeParse(input);\n\n if (!result.success) {\n const message = formatError\n ? formatError(result.error)\n : result.error.errors?.[0]?.message ??\n result.error.message ??\n \"Validation failed\";\n\n return { ok: false, message, code };\n }\n\n return next(result.data);\n };\n}\n\n/**\n * Creates a middleware that logs action calls and results.\n */\nexport function withLogging<P extends unknown[], T>(\n logger: {\n onCall?: (params: P) => void;\n onSuccess?: (data: T, params: P) => void;\n onError?: (\n message: string,\n code: string | undefined,\n params: P,\n ) => void;\n } = {},\n): Middleware<P, T> {\n return async (next, ...params) => {\n logger.onCall?.(params);\n const result = await next(...params);\n if (result.ok) {\n logger.onSuccess?.(result.data, params);\n } else {\n logger.onError?.(result.message, result.code, params);\n }\n return result;\n };\n}\n"],"mappings":";AAeO,SAAS,QAAW,MAAiC;AACxD,SAAO,EAAE,IAAI,MAAM,KAAK;AAC5B;AAEO,SAAS,MAAM,SAAiB,MAAkC;AACrE,SAAO,EAAE,IAAI,OAAO,SAAS,KAAK;AACtC;AAcO,SAAS,aACZ,IACA,SACoB;AACpB,SAAO,UAAU,SAA4C;AACzD,QAAI;AACA,YAAM,OAAO,MAAM,GAAG,GAAG,IAAI;AAC7B,aAAO,QAAQ,IAAI;AAAA,IACvB,SAAS,KAAK;AACV,eAAS,UAAU,GAAG;AAEtB,UAAI,eAAe,OAAO;AACtB,eAAO,MAAM,IAAI,SAAS,IAAI,IAAI;AAAA,MACtC;AAEA,aAAO,MAAM,gCAAgC,eAAe;AAAA,IAChE;AAAA,EACJ;AACJ;AAKO,SAAS,UACZ,QACgC;AAChC,SAAO,OAAO,OAAO;AACzB;AAKO,SAAS,QACZ,QAC2B;AAC3B,SAAO,OAAO,OAAO;AACzB;AAKO,SAAS,OAAU,QAAkC;AACxD,MAAI,OAAO,IAAI;AACX,WAAO,OAAO;AAAA,EAClB;AACA,QAAM,IAAI,MAAM,OAAO,OAAO;AAClC;AAKO,SAAS,SAAY,QAA+B,cAAoB;AAC3E,MAAI,OAAO,IAAI;AACX,WAAO,OAAO;AAAA,EAClB;AACA,SAAO;AACX;;;ACjEO,SAAS,iBACZ,SACgB;AAChB,SAAO;AACX;AAcO,SAAS,gBACZ,QACA,YACgD;AAChD,SAAO,WAAW;AAAA,IACd,CAAC,MAAM,OACH,IAAI,WACA,GAAG,MAAM,GAAG,MAAM;AAAA,IAC1B;AAAA,EACJ;AACJ;AAWO,SAAS,qBACT,YACa;AAChB,SAAO,CAAC,SAAS,WAAW;AACxB,UAAM,QAAQ,WAAW;AAAA,MACrB,CAAC,QAAQ,OACL,IAAI,MACA,GAAG,QAAQ,GAAG,CAAC;AAAA,MACvB;AAAA,IACJ;AACA,WAAO,MAAM,GAAG,MAAM;AAAA,EAC1B;AACJ;AA4CO,SAAS,eACZ,QACA,UAAiC,CAAC,GACX;AACvB,QAAM,EAAE,OAAO,oBAAoB,YAAY,IAAI;AAEnD,SAAO,OAAO,MAAM,UAAU;AAC1B,UAAM,SAAS,OAAO,UAAU,KAAK;AAErC,QAAI,CAAC,OAAO,SAAS;AACjB,YAAM,UAAU,cACV,YAAY,OAAO,KAAK,IACxB,OAAO,MAAM,SAAS,CAAC,GAAG,WAC1B,OAAO,MAAM,WACb;AAEN,aAAO,EAAE,IAAI,OAAO,SAAS,KAAK;AAAA,IACtC;AAEA,WAAO,KAAK,OAAO,IAAI;AAAA,EAC3B;AACJ;AAKO,SAAS,YACZ,SAQI,CAAC,GACW;AAChB,SAAO,OAAO,SAAS,WAAW;AAC9B,WAAO,SAAS,MAAM;AACtB,UAAM,SAAS,MAAM,KAAK,GAAG,MAAM;AACnC,QAAI,OAAO,IAAI;AACX,aAAO,YAAY,OAAO,MAAM,MAAM;AAAA,IAC1C,OAAO;AACH,aAAO,UAAU,OAAO,SAAS,OAAO,MAAM,MAAM;AAAA,IACxD;AACA,WAAO;AAAA,EACX;AACJ;","names":[]}
1
+ {"version":3,"sources":["../src/server/server-action.ts","../src/server/context-middleware.ts","../src/server/action-builder.ts","../src/server/middleware-utils.ts"],"sourcesContent":["\"use server\";\n\nexport type ServerActionSuccess<T> = {\n ok: true;\n data: T;\n};\n\nexport type ServerActionError = {\n ok: false;\n message: string;\n code?: string;\n};\n\nexport type ServerActionResult<T> = ServerActionSuccess<T> | ServerActionError;\n\nexport function success<T>(data: T): ServerActionSuccess<T> {\n return { ok: true, data };\n}\n\nexport function error(message: string, code?: string): ServerActionError {\n return { ok: false, message, code };\n}\n\nexport type ServerActionFn<P extends unknown[], T> = (\n ...args: P\n) => Promise<ServerActionResult<T>>;\n\ntype ServerActionOptions = {\n onError?: (error: unknown) => void;\n};\n\n/**\n * Wraps an async function to return a standardized ServerActionResult.\n * Catches any thrown errors and converts them to error results.\n *\n * @deprecated Use `createAction().input(schema).handle(fn)` instead.\n * The new API provides better type inference and built-in validation.\n *\n * @example\n * ```ts\n * // Old way (deprecated):\n * const myAction = serverAction(async (input: MyInput) => {\n * return await doSomething(input);\n * });\n *\n * // New way:\n * const myAction = createAction()\n * .input(mySchema)\n * .handle(async (ctx, input) => {\n * return { ok: true, data: await doSomething(input) };\n * });\n * ```\n */\nexport function serverAction<P extends unknown[], T>(\n fn: (...args: P) => Promise<T>,\n options?: ServerActionOptions,\n): ServerActionFn<P, T> {\n return async (...args: P): Promise<ServerActionResult<T>> => {\n try {\n const data = await fn(...args);\n return success(data);\n } catch (err) {\n options?.onError?.(err);\n\n if (err instanceof Error) {\n return error(err.message, err.name);\n }\n\n return error(\"An unexpected error occurred\", \"UNKNOWN_ERROR\");\n }\n };\n}\n\n/**\n * Type guard to check if a result is successful\n */\nexport function isSuccess<T>(\n result: ServerActionResult<T>,\n): result is ServerActionSuccess<T> {\n return result.ok === true;\n}\n\n/**\n * Type guard to check if a result is an error\n */\nexport function isError<T>(\n result: ServerActionResult<T>,\n): result is ServerActionError {\n return result.ok === false;\n}\n\n/**\n * Unwraps a successful result or throws the error message\n */\nexport function unwrap<T>(result: ServerActionResult<T>): T {\n if (result.ok) {\n return result.data;\n }\n throw new Error(result.message);\n}\n\n/**\n * Unwraps a successful result or returns a default value\n */\nexport function unwrapOr<T>(result: ServerActionResult<T>, defaultValue: T): T {\n if (result.ok) {\n return result.data;\n }\n return defaultValue;\n}\n","\"use server\";\n\nimport type { BaseContext, ContextMiddleware } from \"./types\";\n\n/**\n * Symbol to identify context-aware middleware at runtime\n */\nexport const CONTEXT_MIDDLEWARE = Symbol(\"contextMiddleware\");\n\n/**\n * Check if a middleware is context-aware\n */\nexport function isContextMiddleware(fn: unknown): boolean {\n return typeof fn === \"function\" && (fn as any)[CONTEXT_MIDDLEWARE] === true;\n}\n\n/**\n * Creates a type-safe context-aware middleware.\n *\n * @example\n * ```ts\n * type User = { id: string; name: string };\n *\n * const withAuth = createContextMiddleware<\n * [string], // Parameters\n * SomeReturnType, // Return type\n * {}, // Input context (none required)\n * { user: User } // Output context (adds user)\n * >(async (next, ctx, input) => {\n * const user = await authenticate();\n * if (!user) {\n * return { ok: false, message: \"Unauthorized\", code: \"UNAUTHORIZED\" };\n * }\n * // Pass the new context with user added\n * return next({ ...ctx, user }, input);\n * });\n * ```\n */\nexport function createContextMiddleware<\n P extends unknown[],\n T,\n CtxIn extends BaseContext = BaseContext,\n CtxOut extends BaseContext = BaseContext,\n>(\n handler: ContextMiddleware<P, T, CtxIn, CtxOut>,\n): ContextMiddleware<P, T, CtxIn, CtxOut> {\n (handler as any)[CONTEXT_MIDDLEWARE] = true;\n return handler;\n}\n","\"use server\";\n\nimport type { ServerActionResult } from \"./server-action\";\nimport type {\n BaseContext,\n ContextMiddleware,\n Middleware,\n} from \"./types\";\nimport { isContextMiddleware } from \"./context-middleware\";\n\n/**\n * A builder for creating server actions with type-safe context accumulation.\n *\n * @template TInput - The input parameters as a tuple type\n * @template TContext - The accumulated context type from all middleware\n */\nclass ActionBuilder<\n TInput extends unknown[] = unknown[],\n TContext extends BaseContext = {},\n> {\n private middlewares: Array<\n ContextMiddleware<any, any, any, any> | Middleware<any, any>\n > = [];\n\n private constructor(\n middlewares: Array<\n ContextMiddleware<any, any, any, any> | Middleware<any, any>\n > = [],\n ) {\n this.middlewares = middlewares;\n }\n\n /**\n * Create a new ActionBuilder instance\n */\n static create<TInput extends unknown[]>(): ActionBuilder<TInput, {}> {\n return new ActionBuilder<TInput, {}>();\n }\n\n /**\n * Add a context-aware middleware to the chain.\n * The middleware receives the accumulated context and can add new context.\n *\n * @example\n * ```ts\n * const withUser = createContextMiddleware(async (next, ctx, ...args) => {\n * const user = await getUser();\n * if (!user) {\n * return { ok: false, message: \"Unauthorized\", code: \"UNAUTHORIZED\" };\n * }\n * return next({ ...ctx, user }, ...args);\n * });\n *\n * const action = createAction<[{ name: string }]>()\n * .use(withUser)\n * .handle(async (ctx, input) => {\n * // ctx.user is fully typed!\n * });\n * ```\n */\n use<CtxOut extends BaseContext>(\n middleware: ContextMiddleware<TInput, any, TContext, CtxOut>,\n ): ActionBuilder<TInput, TContext & CtxOut>;\n\n /**\n * Add a regular (non-context) middleware to the chain.\n * The middleware can transform input/output but doesn't modify context.\n *\n * @example\n * ```ts\n * const withLogging = createMiddleware(async (next, ...args) => {\n * console.log(\"Args:\", args);\n * const result = await next(...args);\n * console.log(\"Result:\", result);\n * return result;\n * });\n *\n * const action = createAction<[string]>()\n * .use(withLogging)\n * .handle(async (ctx, input) => { ... });\n * ```\n */\n use(middleware: Middleware<TInput, any>): ActionBuilder<TInput, TContext>;\n\n use(middleware: any): any {\n return new ActionBuilder<TInput, TContext>([\n ...this.middlewares,\n middleware,\n ]);\n }\n\n /**\n * Define the action handler. This finalizes the builder and returns the executable action.\n * The handler receives the accumulated context and input parameters.\n *\n * @example\n * ```ts\n * const action = createAction<[{ name: string; email: string }]>()\n * .use(withUser)\n * .handle(async (ctx, input) => {\n * // ctx and input are fully typed\n * return { ok: true, data: result };\n * });\n *\n * // Call the action\n * const result = await action({ name: \"John\", email: \"john@example.com\" });\n * ```\n */\n handle<TOutput>(\n handler: (\n ctx: TContext,\n ...args: TInput\n ) => Promise<ServerActionResult<TOutput>>,\n ): (...args: TInput) => Promise<ServerActionResult<TOutput>> {\n const middlewares = [...this.middlewares];\n\n return async (...args: TInput): Promise<ServerActionResult<TOutput>> => {\n // Build the middleware chain\n type CtxNextFn = (\n ctx: BaseContext,\n ...args: TInput\n ) => Promise<ServerActionResult<TOutput>>;\n\n const finalHandler: CtxNextFn = async (ctx, ...a) => {\n return handler(ctx as TContext, ...a);\n };\n\n // Build chain from right to left (last middleware wraps closest to handler)\n const chain = middlewares.reduceRight<CtxNextFn>((next, mw) => {\n if (isContextMiddleware(mw)) {\n // Context middleware: (next, ctx, ...params) => ...\n return async (ctx, ...a) => {\n return (mw as ContextMiddleware<TInput, TOutput, any, any>)(\n async (newCtx, ...params) => next(newCtx, ...(params as TInput)),\n ctx,\n ...a,\n );\n };\n } else {\n // Regular middleware: (next, ...params) => ...\n // Pass context through unchanged\n return async (ctx, ...a) => {\n return (mw as Middleware<TInput, TOutput>)(\n async (...params) => next(ctx, ...(params as TInput)),\n ...a,\n );\n };\n }\n }, finalHandler);\n\n // Start with empty context\n try {\n return await chain({}, ...args);\n } catch (err) {\n const message =\n err instanceof Error ? err.message : \"Unknown error\";\n const code =\n err instanceof Error ? err.name : \"UNKNOWN_ERROR\";\n return { ok: false, message, code };\n }\n };\n }\n}\n\n/**\n * Converts a type to a tuple of arguments.\n * - `void` becomes `[]` (no args)\n * - Single type `T` becomes `[T]`\n * - Tuple type `[A, B]` stays as `[A, B]`\n */\ntype ToArgs<T> = [T] extends [void] ? [] : T extends unknown[] ? T : [T];\n\n/**\n * Creates a new action builder with type-safe context accumulation.\n *\n * @example\n * ```ts\n * // Single input parameter\n * const createUser = createAction<{ name: string; email: string }>()\n * .use(withUser)\n * .handle(async (ctx, input) => {\n * await db.users.create({ ...input, createdBy: ctx.user.id });\n * return { ok: true, data: { success: true } };\n * });\n *\n * // Multiple input parameters (use tuple)\n * const updateUser = createAction<[string, { name: string }]>()\n * .use(withUser)\n * .handle(async (ctx, id, data) => {\n * await db.users.update(id, data);\n * return { ok: true, data: { success: true } };\n * });\n *\n * // With validation middleware\n * const createPost = createAction<z.infer<typeof postSchema>>()\n * .use(withZodValidation(postSchema))\n * .use(withUser)\n * .handle(async (ctx, input) => {\n * return { ok: true, data: await db.posts.create(input) };\n * });\n * ```\n */\nexport function createAction<TInput = void>(): ActionBuilder<ToArgs<TInput>, {}> {\n return ActionBuilder.create<ToArgs<TInput>>();\n}\n","\"use server\";\n\nimport type { ServerActionResult } from \"./server-action\";\nimport type { Middleware, ValidationSchema, WithValidationOptions } from \"./types\";\n\n/**\n * Creates a type-safe middleware function.\n *\n * @example\n * ```ts\n * const withLogging = createMiddleware(async (next, ...params) => {\n * console.log(\"Calling action with:\", params);\n * const result = await next(...params);\n * console.log(\"Result:\", result);\n * return result;\n * });\n * ```\n */\nexport function createMiddleware<P extends unknown[], T>(\n handler: Middleware<P, T>,\n): Middleware<P, T> {\n return handler;\n}\n\n/**\n * Applies middleware to a server action.\n * Middleware is executed in order (first middleware wraps second, etc).\n *\n * @deprecated Use `createAction().input(schema).use(middleware).handle(fn)` instead.\n * This provides better type inference and a more composable API.\n *\n * @example\n * ```ts\n * // Old way (deprecated):\n * const protectedAction = applyMiddleware(\n * myServerAction,\n * [withAuth, withLogging, withValidation]\n * );\n *\n * // New way:\n * const protectedAction = createAction()\n * .input(schema)\n * .use(withAuth)\n * .use(withLogging)\n * .handle(async (ctx, input) => { ... });\n * ```\n */\nexport function applyMiddleware<P extends unknown[], T>(\n action: (...params: P) => Promise<ServerActionResult<T>>,\n middleware: Middleware<P, T>[],\n): (...params: P) => Promise<ServerActionResult<T>> {\n return middleware.reduceRight(\n (next, mw) =>\n (...params: P) =>\n mw(next, ...params),\n action,\n );\n}\n\n/**\n * Composes multiple middleware into a single middleware.\n *\n * @deprecated Use `createAction().use(mw1).use(mw2).handle(fn)` instead.\n * The builder pattern provides better type inference for chained middleware.\n *\n * @example\n * ```ts\n * // Old way (deprecated):\n * const combined = composeMiddleware(withAuth, withLogging, withValidation);\n * const protectedAction = applyMiddleware(myAction, [combined]);\n *\n * // New way:\n * const protectedAction = createAction()\n * .input(schema)\n * .use(withAuth)\n * .use(withLogging)\n * .handle(async (ctx, input) => { ... });\n * ```\n */\nexport function composeMiddleware<P extends unknown[], T>(\n ...middleware: Middleware<P, T>[]\n): Middleware<P, T> {\n return (next, ...params) => {\n const chain = middleware.reduceRight(\n (nextFn, mw) =>\n (...p: P) =>\n mw(nextFn, ...p),\n next,\n );\n return chain(...params);\n };\n}\n\n/**\n * Creates a middleware that validates the first parameter against a schema.\n * Works with Zod, Valibot, or any library with a compatible safeParse method.\n *\n * @deprecated Use `createAction().input(schema).handle(fn)` instead.\n * The `.input()` method provides automatic type inference from the schema.\n *\n * @example\n * ```ts\n * import { z } from \"zod\";\n *\n * const schema = z.object({\n * name: z.string().min(1),\n * email: z.string().email(),\n * });\n *\n * // Old way (deprecated):\n * const createUser = applyMiddleware(\n * serverAction(async (input: z.infer<typeof schema>) => {\n * return await db.user.create({ data: input });\n * }),\n * [withZodValidation(schema)]\n * );\n *\n * // New way:\n * const createUser = createAction()\n * .input(schema) // Type is inferred automatically!\n * .handle(async (ctx, input) => {\n * return { ok: true, data: await db.user.create({ data: input }) };\n * });\n * ```\n */\nexport function withZodValidation<TInput, T>(\n schema: ValidationSchema<TInput>,\n options: WithValidationOptions = {},\n): Middleware<[TInput], T> {\n const { code = \"VALIDATION_ERROR\", formatError } = options;\n\n return async (next, input) => {\n const result = schema.safeParse(input);\n\n if (!result.success) {\n const message = formatError\n ? formatError(result.error)\n : (result.error.errors?.[0]?.message ??\n result.error.message ??\n \"Validation failed\");\n\n return { ok: false, message, code };\n }\n\n return next(result.data);\n };\n}\n\n/**\n * Creates a middleware that logs action calls and results.\n */\nexport function withLogging<P extends unknown[], T>(\n logger: {\n onCall?: (params: P) => void;\n onSuccess?: (data: T, params: P) => void;\n onError?: (\n message: string,\n code: string | undefined,\n params: P,\n ) => void;\n } = {},\n): Middleware<P, T> {\n return async (next, ...params) => {\n logger.onCall?.(params);\n const result = await next(...params);\n if (result.ok) {\n logger.onSuccess?.(result.data, params);\n } else {\n logger.onError?.(result.message, result.code, params);\n }\n return result;\n };\n}\n"],"mappings":"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","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "use-server-action",
3
- "version": "1.1.4",
3
+ "version": "2.0.0",
4
4
  "description": "A React hook for working with Next.js server actions",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",