server-act 1.1.6 → 1.2.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 +37 -33
- package/dist/index.d.mts +13 -12
- package/dist/index.d.ts +13 -12
- package/dist/index.js +12 -8
- package/dist/index.mjs +12 -8
- package/package.json +6 -9
package/README.md
CHANGED
|
@@ -21,10 +21,10 @@ pnpm add server-act zod
|
|
|
21
21
|
|
|
22
22
|
```ts
|
|
23
23
|
// action.ts
|
|
24
|
-
|
|
24
|
+
"use server";
|
|
25
25
|
|
|
26
|
-
import {serverAct} from
|
|
27
|
-
import {z} from
|
|
26
|
+
import { serverAct } from "server-act";
|
|
27
|
+
import { z } from "zod";
|
|
28
28
|
|
|
29
29
|
export const sayHelloAction = serverAct
|
|
30
30
|
.input(
|
|
@@ -32,20 +32,20 @@ export const sayHelloAction = serverAct
|
|
|
32
32
|
name: z.string(),
|
|
33
33
|
}),
|
|
34
34
|
)
|
|
35
|
-
.action(async ({input}) => {
|
|
35
|
+
.action(async ({ input }) => {
|
|
36
36
|
return `Hello, ${input.name}`;
|
|
37
37
|
});
|
|
38
38
|
```
|
|
39
39
|
|
|
40
40
|
```tsx
|
|
41
41
|
// client-component.tsx
|
|
42
|
-
|
|
42
|
+
"use client";
|
|
43
43
|
|
|
44
|
-
import {sayHelloAction} from
|
|
44
|
+
import { sayHelloAction } from "./action";
|
|
45
45
|
|
|
46
46
|
export const ClientComponent = () => {
|
|
47
47
|
const onClick = () => {
|
|
48
|
-
const message = await sayHelloAction({name:
|
|
48
|
+
const message = await sayHelloAction({ name: "John" });
|
|
49
49
|
console.log(message); // Hello, John
|
|
50
50
|
};
|
|
51
51
|
|
|
@@ -61,79 +61,83 @@ export const ClientComponent = () => {
|
|
|
61
61
|
|
|
62
62
|
```ts
|
|
63
63
|
// action.ts
|
|
64
|
-
|
|
64
|
+
"use server";
|
|
65
65
|
|
|
66
|
-
import {serverAct} from
|
|
67
|
-
import {z} from
|
|
66
|
+
import { serverAct } from "server-act";
|
|
67
|
+
import { z } from "zod";
|
|
68
68
|
|
|
69
69
|
export const sayHelloAction = serverAct
|
|
70
70
|
.middleware(() => {
|
|
71
|
-
const userId =
|
|
72
|
-
return {userId};
|
|
71
|
+
const userId = "...";
|
|
72
|
+
return { userId };
|
|
73
73
|
})
|
|
74
74
|
.input(
|
|
75
75
|
z.object({
|
|
76
76
|
name: z.string(),
|
|
77
77
|
}),
|
|
78
78
|
)
|
|
79
|
-
.action(async ({ctx, input}) => {
|
|
80
|
-
console.log(
|
|
79
|
+
.action(async ({ ctx, input }) => {
|
|
80
|
+
console.log("User ID", ctx.userId);
|
|
81
81
|
return `Hello, ${input.name}`;
|
|
82
82
|
});
|
|
83
83
|
```
|
|
84
84
|
|
|
85
|
-
### `
|
|
85
|
+
### `useActionState` Support
|
|
86
86
|
|
|
87
|
-
> `
|
|
87
|
+
> `useActionState` Documentation:
|
|
88
88
|
>
|
|
89
|
-
> - https://
|
|
90
|
-
> - https://react.dev/reference/react-dom/hooks/useFormState
|
|
89
|
+
> - https://react.dev/reference/react/useActionState
|
|
91
90
|
|
|
92
91
|
We recommend using [zod-form-data](https://www.npmjs.com/package/zod-form-data) for input validation.
|
|
93
92
|
|
|
94
93
|
```ts
|
|
95
94
|
// action.ts;
|
|
96
|
-
|
|
95
|
+
"use server";
|
|
97
96
|
|
|
98
|
-
import {serverAct} from
|
|
99
|
-
import {z} from
|
|
100
|
-
import {zfd} from
|
|
97
|
+
import { serverAct } from "server-act";
|
|
98
|
+
import { z } from "zod";
|
|
99
|
+
import { zfd } from "zod-form-data";
|
|
101
100
|
|
|
102
101
|
export const sayHelloAction = serverAct
|
|
103
102
|
.input(
|
|
104
103
|
zfd.formData({
|
|
105
104
|
name: zfd.text(
|
|
106
105
|
z
|
|
107
|
-
.string({required_error: `You haven't told me your name`})
|
|
108
|
-
.
|
|
106
|
+
.string({ required_error: `You haven't told me your name` })
|
|
107
|
+
.max(20, { message: "Any shorter name? You name is too long 😬" }),
|
|
109
108
|
),
|
|
110
109
|
}),
|
|
111
110
|
)
|
|
112
|
-
.formAction(async ({input, formErrors, ctx}) => {
|
|
111
|
+
.formAction(async ({ formData, input, formErrors, ctx }) => {
|
|
113
112
|
if (formErrors) {
|
|
114
|
-
return {formErrors: formErrors.formErrors.fieldErrors};
|
|
113
|
+
return { formData, formErrors: formErrors.formErrors.fieldErrors };
|
|
115
114
|
}
|
|
116
|
-
return {message: `Hello, ${input.name}!`};
|
|
115
|
+
return { message: `Hello, ${input.name}!` };
|
|
117
116
|
});
|
|
118
117
|
```
|
|
119
118
|
|
|
120
119
|
```tsx
|
|
121
120
|
// client-component.tsx
|
|
122
|
-
|
|
121
|
+
"use client";
|
|
123
122
|
|
|
124
|
-
import {
|
|
123
|
+
import { useActionState } from "react";
|
|
124
|
+
import { sayHelloAction } from "./action";
|
|
125
125
|
|
|
126
126
|
export const ClientComponent = () => {
|
|
127
|
-
const [state, dispatch] = useFormState(sayHelloAction,
|
|
127
|
+
const [state, dispatch] = useFormState(sayHelloAction, undefined);
|
|
128
128
|
|
|
129
129
|
return (
|
|
130
130
|
<form action={dispatch}>
|
|
131
|
-
<input
|
|
132
|
-
|
|
131
|
+
<input
|
|
132
|
+
name="name"
|
|
133
|
+
required
|
|
134
|
+
defaultValue={state?.formData?.get("name")?.toString()}
|
|
135
|
+
/>
|
|
136
|
+
{state?.formErrors?.name?.map((error) => <p key={error}>{error}</p>)}
|
|
133
137
|
|
|
134
138
|
<button type="submit">Submit</button>
|
|
135
139
|
|
|
136
|
-
{!!state
|
|
140
|
+
{!!state?.message && <p>{state.message}</p>}
|
|
137
141
|
</form>
|
|
138
142
|
);
|
|
139
143
|
};
|
package/dist/index.d.mts
CHANGED
|
@@ -7,8 +7,8 @@ type Prettify<T> = {
|
|
|
7
7
|
[P in keyof T]: T[P];
|
|
8
8
|
} & {};
|
|
9
9
|
type SanitizeFunctionParam<T extends (param: any) => any> = T extends (param: infer P) => infer R ? Equals<P, undefined> extends true ? () => R : Equals<P, P | undefined> extends true ? (param?: P) => R : (param: P) => R : never;
|
|
10
|
-
type InferParserType<T, TType extends
|
|
11
|
-
type InferInputType<T, TType extends
|
|
10
|
+
type InferParserType<T, TType extends "in" | "out"> = T extends z.ZodEffects<infer I, any, any> ? I[TType extends "in" ? "_input" : "_output"] : T extends z.ZodType ? T[TType extends "in" ? "_input" : "_output"] : never;
|
|
11
|
+
type InferInputType<T, TType extends "in" | "out"> = T extends UnsetMarker ? undefined : InferParserType<T, TType>;
|
|
12
12
|
type InferContextType<T> = T extends UnsetMarker ? undefined : T;
|
|
13
13
|
interface ActionParams<TInput = unknown, TContext = unknown> {
|
|
14
14
|
_input: TInput;
|
|
@@ -19,7 +19,7 @@ interface ActionBuilder<TParams extends ActionParams> {
|
|
|
19
19
|
* Middleware allows you to run code before the action, its return value will pass as context to the action.
|
|
20
20
|
*/
|
|
21
21
|
middleware: <TContext>(middleware: () => Promise<TContext> | TContext) => ActionBuilder<{
|
|
22
|
-
_input: TParams[
|
|
22
|
+
_input: TParams["_input"];
|
|
23
23
|
_context: TContext;
|
|
24
24
|
}>;
|
|
25
25
|
/**
|
|
@@ -27,28 +27,29 @@ interface ActionBuilder<TParams extends ActionParams> {
|
|
|
27
27
|
*/
|
|
28
28
|
input: <TParser extends z.ZodType>(input: TParser) => ActionBuilder<{
|
|
29
29
|
_input: TParser;
|
|
30
|
-
_context: TParams[
|
|
30
|
+
_context: TParams["_context"];
|
|
31
31
|
}>;
|
|
32
32
|
/**
|
|
33
33
|
* Create an action.
|
|
34
34
|
*/
|
|
35
35
|
action: <TOutput>(action: (params: {
|
|
36
|
-
ctx: InferContextType<TParams[
|
|
37
|
-
input: InferInputType<TParams[
|
|
38
|
-
}) => Promise<TOutput>) => SanitizeFunctionParam<(input: InferInputType<TParams[
|
|
36
|
+
ctx: InferContextType<TParams["_context"]>;
|
|
37
|
+
input: InferInputType<TParams["_input"], "out">;
|
|
38
|
+
}) => Promise<TOutput>) => SanitizeFunctionParam<(input: InferInputType<TParams["_input"], "in">) => Promise<TOutput>>;
|
|
39
39
|
/**
|
|
40
40
|
* Create an action for React `useFormState`
|
|
41
41
|
*/
|
|
42
|
-
formAction: <TState>(action: (params: Prettify<{
|
|
43
|
-
ctx: InferContextType<TParams[
|
|
42
|
+
formAction: <TState, TPrevState = undefined>(action: (params: Prettify<{
|
|
43
|
+
ctx: InferContextType<TParams["_context"]>;
|
|
44
44
|
prevState: any;
|
|
45
|
+
formData: FormData;
|
|
45
46
|
} & ({
|
|
46
|
-
input: InferInputType<TParams[
|
|
47
|
+
input: InferInputType<TParams["_input"], "out">;
|
|
47
48
|
formErrors?: undefined;
|
|
48
49
|
} | {
|
|
49
50
|
input?: undefined;
|
|
50
|
-
formErrors: z.ZodError<InferInputType<TParams[
|
|
51
|
-
})>) => Promise<TState>) => (prevState: TState, formData: FormData) => Promise<TState>;
|
|
51
|
+
formErrors: z.ZodError<InferInputType<TParams["_input"], "in">>;
|
|
52
|
+
})>) => Promise<TState>) => (prevState: TState | TPrevState, formData: FormData) => Promise<TState | TPrevState>;
|
|
52
53
|
}
|
|
53
54
|
/**
|
|
54
55
|
* Server action builder
|
package/dist/index.d.ts
CHANGED
|
@@ -7,8 +7,8 @@ type Prettify<T> = {
|
|
|
7
7
|
[P in keyof T]: T[P];
|
|
8
8
|
} & {};
|
|
9
9
|
type SanitizeFunctionParam<T extends (param: any) => any> = T extends (param: infer P) => infer R ? Equals<P, undefined> extends true ? () => R : Equals<P, P | undefined> extends true ? (param?: P) => R : (param: P) => R : never;
|
|
10
|
-
type InferParserType<T, TType extends
|
|
11
|
-
type InferInputType<T, TType extends
|
|
10
|
+
type InferParserType<T, TType extends "in" | "out"> = T extends z.ZodEffects<infer I, any, any> ? I[TType extends "in" ? "_input" : "_output"] : T extends z.ZodType ? T[TType extends "in" ? "_input" : "_output"] : never;
|
|
11
|
+
type InferInputType<T, TType extends "in" | "out"> = T extends UnsetMarker ? undefined : InferParserType<T, TType>;
|
|
12
12
|
type InferContextType<T> = T extends UnsetMarker ? undefined : T;
|
|
13
13
|
interface ActionParams<TInput = unknown, TContext = unknown> {
|
|
14
14
|
_input: TInput;
|
|
@@ -19,7 +19,7 @@ interface ActionBuilder<TParams extends ActionParams> {
|
|
|
19
19
|
* Middleware allows you to run code before the action, its return value will pass as context to the action.
|
|
20
20
|
*/
|
|
21
21
|
middleware: <TContext>(middleware: () => Promise<TContext> | TContext) => ActionBuilder<{
|
|
22
|
-
_input: TParams[
|
|
22
|
+
_input: TParams["_input"];
|
|
23
23
|
_context: TContext;
|
|
24
24
|
}>;
|
|
25
25
|
/**
|
|
@@ -27,28 +27,29 @@ interface ActionBuilder<TParams extends ActionParams> {
|
|
|
27
27
|
*/
|
|
28
28
|
input: <TParser extends z.ZodType>(input: TParser) => ActionBuilder<{
|
|
29
29
|
_input: TParser;
|
|
30
|
-
_context: TParams[
|
|
30
|
+
_context: TParams["_context"];
|
|
31
31
|
}>;
|
|
32
32
|
/**
|
|
33
33
|
* Create an action.
|
|
34
34
|
*/
|
|
35
35
|
action: <TOutput>(action: (params: {
|
|
36
|
-
ctx: InferContextType<TParams[
|
|
37
|
-
input: InferInputType<TParams[
|
|
38
|
-
}) => Promise<TOutput>) => SanitizeFunctionParam<(input: InferInputType<TParams[
|
|
36
|
+
ctx: InferContextType<TParams["_context"]>;
|
|
37
|
+
input: InferInputType<TParams["_input"], "out">;
|
|
38
|
+
}) => Promise<TOutput>) => SanitizeFunctionParam<(input: InferInputType<TParams["_input"], "in">) => Promise<TOutput>>;
|
|
39
39
|
/**
|
|
40
40
|
* Create an action for React `useFormState`
|
|
41
41
|
*/
|
|
42
|
-
formAction: <TState>(action: (params: Prettify<{
|
|
43
|
-
ctx: InferContextType<TParams[
|
|
42
|
+
formAction: <TState, TPrevState = undefined>(action: (params: Prettify<{
|
|
43
|
+
ctx: InferContextType<TParams["_context"]>;
|
|
44
44
|
prevState: any;
|
|
45
|
+
formData: FormData;
|
|
45
46
|
} & ({
|
|
46
|
-
input: InferInputType<TParams[
|
|
47
|
+
input: InferInputType<TParams["_input"], "out">;
|
|
47
48
|
formErrors?: undefined;
|
|
48
49
|
} | {
|
|
49
50
|
input?: undefined;
|
|
50
|
-
formErrors: z.ZodError<InferInputType<TParams[
|
|
51
|
-
})>) => Promise<TState>) => (prevState: TState, formData: FormData) => Promise<TState>;
|
|
51
|
+
formErrors: z.ZodError<InferInputType<TParams["_input"], "in">>;
|
|
52
|
+
})>) => Promise<TState>) => (prevState: TState | TPrevState, formData: FormData) => Promise<TState | TPrevState>;
|
|
52
53
|
}
|
|
53
54
|
/**
|
|
54
55
|
* Server action builder
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Object.defineProperty(exports, '__esModule', { value: true });
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) {
|
|
4
4
|
try {
|
|
5
5
|
var info = gen[key](arg);
|
|
6
6
|
var value = info.value;
|
|
@@ -43,10 +43,10 @@ function _extends() {
|
|
|
43
43
|
};
|
|
44
44
|
return _extends.apply(this, arguments);
|
|
45
45
|
}
|
|
46
|
-
|
|
46
|
+
function createNewServerActionBuilder(def) {
|
|
47
47
|
return createServerActionBuilder(def);
|
|
48
|
-
}
|
|
49
|
-
|
|
48
|
+
}
|
|
49
|
+
function createServerActionBuilder(initDef = {}) {
|
|
50
50
|
const _def = _extends({
|
|
51
51
|
input: undefined,
|
|
52
52
|
middleware: undefined
|
|
@@ -59,13 +59,14 @@ const createServerActionBuilder = (initDef = {})=>{
|
|
|
59
59
|
input
|
|
60
60
|
})),
|
|
61
61
|
action: (action)=>{
|
|
62
|
+
// biome-ignore lint/suspicious/noExplicitAny: Intended
|
|
62
63
|
return /*#__PURE__*/ _async_to_generator(function*(input) {
|
|
63
64
|
const ctx = yield _def.middleware == null ? void 0 : _def.middleware.call(_def);
|
|
64
65
|
if (_def.input) {
|
|
65
66
|
const result = _def.input.safeParse(input);
|
|
66
67
|
if (!result.success) {
|
|
67
|
-
console.error(
|
|
68
|
-
throw new Error(
|
|
68
|
+
console.error("❌ Input validation error:", result.error.errors);
|
|
69
|
+
throw new Error("Input validation error");
|
|
69
70
|
}
|
|
70
71
|
}
|
|
71
72
|
return yield action({
|
|
@@ -78,29 +79,32 @@ const createServerActionBuilder = (initDef = {})=>{
|
|
|
78
79
|
return /*#__PURE__*/ _async_to_generator(function*(prevState, formData) {
|
|
79
80
|
const ctx = yield _def.middleware == null ? void 0 : _def.middleware.call(_def);
|
|
80
81
|
if (_def.input) {
|
|
81
|
-
const result = _def.input.
|
|
82
|
+
const result = yield _def.input.safeParseAsync(formData);
|
|
82
83
|
if (!result.success) {
|
|
83
84
|
return yield action({
|
|
84
85
|
ctx,
|
|
85
86
|
prevState,
|
|
87
|
+
formData,
|
|
86
88
|
formErrors: result.error
|
|
87
89
|
});
|
|
88
90
|
}
|
|
89
91
|
return yield action({
|
|
90
92
|
ctx,
|
|
91
93
|
prevState,
|
|
94
|
+
formData,
|
|
92
95
|
input: result.data
|
|
93
96
|
});
|
|
94
97
|
}
|
|
95
98
|
return yield action({
|
|
96
99
|
ctx,
|
|
97
100
|
prevState,
|
|
101
|
+
formData,
|
|
98
102
|
input: undefined
|
|
99
103
|
});
|
|
100
104
|
});
|
|
101
105
|
}
|
|
102
106
|
};
|
|
103
|
-
}
|
|
107
|
+
}
|
|
104
108
|
/**
|
|
105
109
|
* Server action builder
|
|
106
110
|
*/ const serverAct = createServerActionBuilder();
|
package/dist/index.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) {
|
|
2
2
|
try {
|
|
3
3
|
var info = gen[key](arg);
|
|
4
4
|
var value = info.value;
|
|
@@ -41,10 +41,10 @@ function _extends() {
|
|
|
41
41
|
};
|
|
42
42
|
return _extends.apply(this, arguments);
|
|
43
43
|
}
|
|
44
|
-
|
|
44
|
+
function createNewServerActionBuilder(def) {
|
|
45
45
|
return createServerActionBuilder(def);
|
|
46
|
-
}
|
|
47
|
-
|
|
46
|
+
}
|
|
47
|
+
function createServerActionBuilder(initDef = {}) {
|
|
48
48
|
const _def = _extends({
|
|
49
49
|
input: undefined,
|
|
50
50
|
middleware: undefined
|
|
@@ -57,13 +57,14 @@ const createServerActionBuilder = (initDef = {})=>{
|
|
|
57
57
|
input
|
|
58
58
|
})),
|
|
59
59
|
action: (action)=>{
|
|
60
|
+
// biome-ignore lint/suspicious/noExplicitAny: Intended
|
|
60
61
|
return /*#__PURE__*/ _async_to_generator(function*(input) {
|
|
61
62
|
const ctx = yield _def.middleware == null ? void 0 : _def.middleware.call(_def);
|
|
62
63
|
if (_def.input) {
|
|
63
64
|
const result = _def.input.safeParse(input);
|
|
64
65
|
if (!result.success) {
|
|
65
|
-
console.error(
|
|
66
|
-
throw new Error(
|
|
66
|
+
console.error("❌ Input validation error:", result.error.errors);
|
|
67
|
+
throw new Error("Input validation error");
|
|
67
68
|
}
|
|
68
69
|
}
|
|
69
70
|
return yield action({
|
|
@@ -76,29 +77,32 @@ const createServerActionBuilder = (initDef = {})=>{
|
|
|
76
77
|
return /*#__PURE__*/ _async_to_generator(function*(prevState, formData) {
|
|
77
78
|
const ctx = yield _def.middleware == null ? void 0 : _def.middleware.call(_def);
|
|
78
79
|
if (_def.input) {
|
|
79
|
-
const result = _def.input.
|
|
80
|
+
const result = yield _def.input.safeParseAsync(formData);
|
|
80
81
|
if (!result.success) {
|
|
81
82
|
return yield action({
|
|
82
83
|
ctx,
|
|
83
84
|
prevState,
|
|
85
|
+
formData,
|
|
84
86
|
formErrors: result.error
|
|
85
87
|
});
|
|
86
88
|
}
|
|
87
89
|
return yield action({
|
|
88
90
|
ctx,
|
|
89
91
|
prevState,
|
|
92
|
+
formData,
|
|
90
93
|
input: result.data
|
|
91
94
|
});
|
|
92
95
|
}
|
|
93
96
|
return yield action({
|
|
94
97
|
ctx,
|
|
95
98
|
prevState,
|
|
99
|
+
formData,
|
|
96
100
|
input: undefined
|
|
97
101
|
});
|
|
98
102
|
});
|
|
99
103
|
}
|
|
100
104
|
};
|
|
101
|
-
}
|
|
105
|
+
}
|
|
102
106
|
/**
|
|
103
107
|
* Server action builder
|
|
104
108
|
*/ const serverAct = createServerActionBuilder();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "server-act",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"homepage": "https://github.com/chungweileong94/server-act#readme",
|
|
5
5
|
"author": "chungweileong94",
|
|
6
6
|
"license": "MIT",
|
|
@@ -44,16 +44,13 @@
|
|
|
44
44
|
"action"
|
|
45
45
|
],
|
|
46
46
|
"devDependencies": {
|
|
47
|
-
"bunchee": "^
|
|
48
|
-
"
|
|
49
|
-
"eslint-config-whiteroom": "^3.3.0",
|
|
50
|
-
"prettier": "^3.0.3",
|
|
51
|
-
"typescript": "^5.2.2",
|
|
47
|
+
"bunchee": "^5.1.2",
|
|
48
|
+
"typescript": "^5.4.5",
|
|
52
49
|
"zod": "^3.22.2",
|
|
53
50
|
"zod-form-data": "^2.0.2"
|
|
54
51
|
},
|
|
55
52
|
"peerDependencies": {
|
|
56
|
-
"typescript": "
|
|
53
|
+
"typescript": ">=5.0.0",
|
|
57
54
|
"zod": "^3.22.2"
|
|
58
55
|
},
|
|
59
56
|
"peerDependenciesMeta": {
|
|
@@ -64,7 +61,7 @@
|
|
|
64
61
|
"scripts": {
|
|
65
62
|
"build": "bunchee",
|
|
66
63
|
"dev": "bunchee -w",
|
|
67
|
-
"
|
|
68
|
-
"
|
|
64
|
+
"typecheck": "tsc --noEmit",
|
|
65
|
+
"test": "vitest run"
|
|
69
66
|
}
|
|
70
67
|
}
|