server-act 1.1.3 → 1.1.5
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/LICENSE +21 -0
- package/README.md +29 -30
- package/dist/index.d.mts +8 -7
- package/dist/index.d.ts +8 -7
- package/dist/index.js +104 -70
- package/dist/index.mjs +103 -46
- package/package.json +22 -11
- package/.eslintrc.json +0 -11
- package/.prettierrc.json +0 -15
- package/.turbo/turbo-build.log +0 -22
- package/CHANGELOG.md +0 -80
- package/dist/index.js.map +0 -1
- package/dist/index.mjs.map +0 -1
- package/src/index.test.ts +0 -110
- package/src/index.ts +0 -130
- package/tsconfig.json +0 -30
- package/tsup.config.ts +0 -11
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 Chung Wei Leong
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -21,31 +21,31 @@ pnpm add server-act zod
|
|
|
21
21
|
|
|
22
22
|
```ts
|
|
23
23
|
// action.ts
|
|
24
|
-
|
|
24
|
+
'use server';
|
|
25
25
|
|
|
26
|
-
import {
|
|
27
|
-
import {
|
|
26
|
+
import {serverAct} from 'server-act';
|
|
27
|
+
import {z} from 'zod';
|
|
28
28
|
|
|
29
29
|
export const sayHelloAction = serverAct
|
|
30
30
|
.input(
|
|
31
31
|
z.object({
|
|
32
32
|
name: z.string(),
|
|
33
|
-
})
|
|
33
|
+
}),
|
|
34
34
|
)
|
|
35
|
-
.action(async ({
|
|
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 {
|
|
44
|
+
import {sayHelloAction} from './action';
|
|
45
45
|
|
|
46
46
|
export const ClientComponent = () => {
|
|
47
47
|
const onClick = () => {
|
|
48
|
-
const message = await sayHelloAction({
|
|
48
|
+
const message = await sayHelloAction({name: 'John'});
|
|
49
49
|
console.log(message); // Hello, John
|
|
50
50
|
};
|
|
51
51
|
|
|
@@ -61,23 +61,23 @@ export const ClientComponent = () => {
|
|
|
61
61
|
|
|
62
62
|
```ts
|
|
63
63
|
// action.ts
|
|
64
|
-
|
|
64
|
+
'use server';
|
|
65
65
|
|
|
66
|
-
import {
|
|
67
|
-
import {
|
|
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 {
|
|
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 ({
|
|
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
|
```
|
|
@@ -93,39 +93,38 @@ We recommend using [zod-form-data](https://www.npmjs.com/package/zod-form-data)
|
|
|
93
93
|
|
|
94
94
|
```ts
|
|
95
95
|
// action.ts;
|
|
96
|
-
|
|
96
|
+
'use server';
|
|
97
97
|
|
|
98
|
-
import {
|
|
99
|
-
import {
|
|
100
|
-
import {
|
|
98
|
+
import {serverAct} from 'server-act';
|
|
99
|
+
import {z} from 'zod';
|
|
100
|
+
import {zfd} from 'zod-form-data';
|
|
101
101
|
|
|
102
102
|
export const sayHelloAction = serverAct
|
|
103
|
-
.middleware(requestTimeMiddleware)
|
|
104
103
|
.input(
|
|
105
104
|
zfd.formData({
|
|
106
105
|
name: zfd.text(
|
|
107
106
|
z
|
|
108
|
-
.string({
|
|
109
|
-
.nonempty({
|
|
107
|
+
.string({required_error: `You haven't told me your name`})
|
|
108
|
+
.nonempty({message: 'You need to tell me your name!'}),
|
|
110
109
|
),
|
|
111
|
-
})
|
|
110
|
+
}),
|
|
112
111
|
)
|
|
113
|
-
.formAction(async ({
|
|
112
|
+
.formAction(async ({input, formErrors, ctx}) => {
|
|
114
113
|
if (formErrors) {
|
|
115
|
-
return {
|
|
114
|
+
return {formErrors: formErrors.formErrors.fieldErrors};
|
|
116
115
|
}
|
|
117
|
-
return {
|
|
116
|
+
return {message: `Hello, ${input.name}!`};
|
|
118
117
|
});
|
|
119
118
|
```
|
|
120
119
|
|
|
121
120
|
```tsx
|
|
122
121
|
// client-component.tsx
|
|
123
|
-
|
|
122
|
+
'use client';
|
|
124
123
|
|
|
125
|
-
import {
|
|
124
|
+
import {sayHelloAction} from './action';
|
|
126
125
|
|
|
127
126
|
export const ClientComponent = () => {
|
|
128
|
-
const [state, dispatch] = useFormState(sayHelloAction, {
|
|
127
|
+
const [state, dispatch] = useFormState(sayHelloAction, {formErrors: {}});
|
|
129
128
|
|
|
130
129
|
return (
|
|
131
130
|
<form action={dispatch}>
|
package/dist/index.d.mts
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
|
|
3
|
+
declare const unsetMarker: unique symbol;
|
|
4
|
+
type UnsetMarker = typeof unsetMarker;
|
|
3
5
|
type Prettify<T> = {
|
|
4
6
|
[P in keyof T]: T[P];
|
|
5
7
|
} & {};
|
|
6
|
-
declare const unsetMarker: unique symbol;
|
|
7
|
-
type UnsetMarker = typeof unsetMarker;
|
|
8
8
|
type OptionalizeUndefined<T> = undefined extends T ? [param?: T] : [param: T];
|
|
9
|
-
type InferParserType<
|
|
9
|
+
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;
|
|
10
|
+
type InferInputType<T, TType extends 'in' | 'out'> = T extends UnsetMarker ? undefined : InferParserType<T, TType>;
|
|
10
11
|
type InferContextType<T> = T extends UnsetMarker ? undefined : T;
|
|
11
12
|
interface ActionParams<TInput = unknown, TContext = unknown> {
|
|
12
13
|
_input: TInput;
|
|
@@ -32,8 +33,8 @@ interface ActionBuilder<TParams extends ActionParams> {
|
|
|
32
33
|
*/
|
|
33
34
|
action: <TOutput>(action: (params: {
|
|
34
35
|
ctx: InferContextType<TParams['_context']>;
|
|
35
|
-
input:
|
|
36
|
-
}) => Promise<TOutput>) => (...[input]: OptionalizeUndefined<
|
|
36
|
+
input: InferInputType<TParams['_input'], 'out'>;
|
|
37
|
+
}) => Promise<TOutput>) => (...[input]: OptionalizeUndefined<InferInputType<TParams['_input'], 'in'>>) => Promise<TOutput>;
|
|
37
38
|
/**
|
|
38
39
|
* Create an action for React `useFormState`
|
|
39
40
|
*/
|
|
@@ -41,11 +42,11 @@ interface ActionBuilder<TParams extends ActionParams> {
|
|
|
41
42
|
ctx: InferContextType<TParams['_context']>;
|
|
42
43
|
prevState: any;
|
|
43
44
|
} & ({
|
|
44
|
-
input:
|
|
45
|
+
input: InferInputType<TParams['_input'], 'out'>;
|
|
45
46
|
formErrors?: undefined;
|
|
46
47
|
} | {
|
|
47
48
|
input?: undefined;
|
|
48
|
-
formErrors: z.ZodError<
|
|
49
|
+
formErrors: z.ZodError<InferInputType<TParams['_input'], 'in'>>;
|
|
49
50
|
})>) => Promise<TState>) => (prevState: TState, formData: FormData) => Promise<TState>;
|
|
50
51
|
}
|
|
51
52
|
/**
|
package/dist/index.d.ts
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
|
|
3
|
+
declare const unsetMarker: unique symbol;
|
|
4
|
+
type UnsetMarker = typeof unsetMarker;
|
|
3
5
|
type Prettify<T> = {
|
|
4
6
|
[P in keyof T]: T[P];
|
|
5
7
|
} & {};
|
|
6
|
-
declare const unsetMarker: unique symbol;
|
|
7
|
-
type UnsetMarker = typeof unsetMarker;
|
|
8
8
|
type OptionalizeUndefined<T> = undefined extends T ? [param?: T] : [param: T];
|
|
9
|
-
type InferParserType<
|
|
9
|
+
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;
|
|
10
|
+
type InferInputType<T, TType extends 'in' | 'out'> = T extends UnsetMarker ? undefined : InferParserType<T, TType>;
|
|
10
11
|
type InferContextType<T> = T extends UnsetMarker ? undefined : T;
|
|
11
12
|
interface ActionParams<TInput = unknown, TContext = unknown> {
|
|
12
13
|
_input: TInput;
|
|
@@ -32,8 +33,8 @@ interface ActionBuilder<TParams extends ActionParams> {
|
|
|
32
33
|
*/
|
|
33
34
|
action: <TOutput>(action: (params: {
|
|
34
35
|
ctx: InferContextType<TParams['_context']>;
|
|
35
|
-
input:
|
|
36
|
-
}) => Promise<TOutput>) => (...[input]: OptionalizeUndefined<
|
|
36
|
+
input: InferInputType<TParams['_input'], 'out'>;
|
|
37
|
+
}) => Promise<TOutput>) => (...[input]: OptionalizeUndefined<InferInputType<TParams['_input'], 'in'>>) => Promise<TOutput>;
|
|
37
38
|
/**
|
|
38
39
|
* Create an action for React `useFormState`
|
|
39
40
|
*/
|
|
@@ -41,11 +42,11 @@ interface ActionBuilder<TParams extends ActionParams> {
|
|
|
41
42
|
ctx: InferContextType<TParams['_context']>;
|
|
42
43
|
prevState: any;
|
|
43
44
|
} & ({
|
|
44
|
-
input:
|
|
45
|
+
input: InferInputType<TParams['_input'], 'out'>;
|
|
45
46
|
formErrors?: undefined;
|
|
46
47
|
} | {
|
|
47
48
|
input?: undefined;
|
|
48
|
-
formErrors: z.ZodError<
|
|
49
|
+
formErrors: z.ZodError<InferInputType<TParams['_input'], 'in'>>;
|
|
49
50
|
})>) => Promise<TState>) => (prevState: TState, formData: FormData) => Promise<TState>;
|
|
50
51
|
}
|
|
51
52
|
/**
|
package/dist/index.js
CHANGED
|
@@ -1,74 +1,108 @@
|
|
|
1
|
-
|
|
2
|
-
var __defProp = Object.defineProperty;
|
|
3
|
-
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
-
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
-
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
-
var __export = (target, all) => {
|
|
7
|
-
for (var name in all)
|
|
8
|
-
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
-
};
|
|
10
|
-
var __copyProps = (to, from, except, desc) => {
|
|
11
|
-
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
-
for (let key of __getOwnPropNames(from))
|
|
13
|
-
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
-
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
-
}
|
|
16
|
-
return to;
|
|
17
|
-
};
|
|
18
|
-
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
1
|
+
Object.defineProperty(exports, '__esModule', { value: true });
|
|
19
2
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
})
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
3
|
+
/* eslint-disable @typescript-eslint/no-unsafe-assignment */ /* eslint-disable @typescript-eslint/no-explicit-any */ function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) {
|
|
4
|
+
try {
|
|
5
|
+
var info = gen[key](arg);
|
|
6
|
+
var value = info.value;
|
|
7
|
+
} catch (error) {
|
|
8
|
+
reject(error);
|
|
9
|
+
return;
|
|
10
|
+
}
|
|
11
|
+
if (info.done) {
|
|
12
|
+
resolve(value);
|
|
13
|
+
} else {
|
|
14
|
+
Promise.resolve(value).then(_next, _throw);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
function _async_to_generator(fn) {
|
|
18
|
+
return function() {
|
|
19
|
+
var self = this, args = arguments;
|
|
20
|
+
return new Promise(function(resolve, reject) {
|
|
21
|
+
var gen = fn.apply(self, args);
|
|
22
|
+
function _next(value) {
|
|
23
|
+
asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value);
|
|
24
|
+
}
|
|
25
|
+
function _throw(err) {
|
|
26
|
+
asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err);
|
|
27
|
+
}
|
|
28
|
+
_next(undefined);
|
|
29
|
+
});
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
function _extends() {
|
|
33
|
+
_extends = Object.assign || function(target) {
|
|
34
|
+
for(var i = 1; i < arguments.length; i++){
|
|
35
|
+
var source = arguments[i];
|
|
36
|
+
for(var key in source){
|
|
37
|
+
if (Object.prototype.hasOwnProperty.call(source, key)) {
|
|
38
|
+
target[key] = source[key];
|
|
39
|
+
}
|
|
40
|
+
}
|
|
49
41
|
}
|
|
50
|
-
return
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
42
|
+
return target;
|
|
43
|
+
};
|
|
44
|
+
return _extends.apply(this, arguments);
|
|
45
|
+
}
|
|
46
|
+
const createNewServerActionBuilder = (def)=>{
|
|
47
|
+
return createServerActionBuilder(def);
|
|
48
|
+
};
|
|
49
|
+
const createServerActionBuilder = (initDef = {})=>{
|
|
50
|
+
const _def = _extends({
|
|
51
|
+
input: undefined,
|
|
52
|
+
middleware: undefined
|
|
53
|
+
}, initDef);
|
|
54
|
+
return {
|
|
55
|
+
middleware: (middleware)=>createNewServerActionBuilder(_extends({}, _def, {
|
|
56
|
+
middleware
|
|
57
|
+
})),
|
|
58
|
+
input: (input)=>createNewServerActionBuilder(_extends({}, _def, {
|
|
59
|
+
input
|
|
60
|
+
})),
|
|
61
|
+
action: (action)=>{
|
|
62
|
+
return /*#__PURE__*/ _async_to_generator(function*(input) {
|
|
63
|
+
const ctx = yield _def.middleware == null ? void 0 : _def.middleware.call(_def);
|
|
64
|
+
if (_def.input) {
|
|
65
|
+
const result = _def.input.safeParse(input);
|
|
66
|
+
if (!result.success) {
|
|
67
|
+
console.error('❌ Input validation error:', result.error.errors);
|
|
68
|
+
throw new Error('Input validation error');
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
return yield action({
|
|
72
|
+
ctx,
|
|
73
|
+
input
|
|
74
|
+
});
|
|
75
|
+
});
|
|
76
|
+
},
|
|
77
|
+
formAction: (action)=>{
|
|
78
|
+
return /*#__PURE__*/ _async_to_generator(function*(prevState, formData) {
|
|
79
|
+
const ctx = yield _def.middleware == null ? void 0 : _def.middleware.call(_def);
|
|
80
|
+
if (_def.input) {
|
|
81
|
+
const result = _def.input.safeParse(formData);
|
|
82
|
+
if (!result.success) {
|
|
83
|
+
return yield action({
|
|
84
|
+
ctx,
|
|
85
|
+
prevState,
|
|
86
|
+
formErrors: result.error
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
return yield action({
|
|
90
|
+
ctx,
|
|
91
|
+
prevState,
|
|
92
|
+
input: result.data
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
return yield action({
|
|
96
|
+
ctx,
|
|
97
|
+
prevState,
|
|
98
|
+
input: undefined
|
|
99
|
+
});
|
|
100
|
+
});
|
|
63
101
|
}
|
|
64
|
-
|
|
65
|
-
};
|
|
66
|
-
}
|
|
67
|
-
};
|
|
102
|
+
};
|
|
68
103
|
};
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
//# sourceMappingURL=index.js.map
|
|
104
|
+
/**
|
|
105
|
+
* Server action builder
|
|
106
|
+
*/ const serverAct = createServerActionBuilder();
|
|
107
|
+
|
|
108
|
+
exports.serverAct = serverAct;
|
package/dist/index.mjs
CHANGED
|
@@ -1,49 +1,106 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
var
|
|
4
|
-
|
|
5
|
-
}
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
input: void 0,
|
|
9
|
-
middleware: void 0,
|
|
10
|
-
...initDef
|
|
11
|
-
};
|
|
12
|
-
return {
|
|
13
|
-
middleware: (middleware) => createServerActionBuilder({ ..._def, middleware }),
|
|
14
|
-
input: (input) => createNewServerActionBuilder({ ..._def, input }),
|
|
15
|
-
action: (action) => {
|
|
16
|
-
return async (input) => {
|
|
17
|
-
var _a;
|
|
18
|
-
const ctx = await ((_a = _def.middleware) == null ? void 0 : _a.call(_def));
|
|
19
|
-
if (_def.input) {
|
|
20
|
-
const result = _def.input.safeParse(input);
|
|
21
|
-
if (!result.success) {
|
|
22
|
-
console.error("\u274C Input validation error:", result.error.errors);
|
|
23
|
-
throw new Error("Input validation error");
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
return await action({ ctx, input });
|
|
27
|
-
};
|
|
28
|
-
},
|
|
29
|
-
formAction: (action) => {
|
|
30
|
-
return async (prevState, formData) => {
|
|
31
|
-
var _a;
|
|
32
|
-
const ctx = await ((_a = _def.middleware) == null ? void 0 : _a.call(_def));
|
|
33
|
-
if (_def.input) {
|
|
34
|
-
const result = _def.input.safeParse(formData);
|
|
35
|
-
if (!result.success) {
|
|
36
|
-
return await action({ ctx, prevState, formErrors: result.error });
|
|
37
|
-
}
|
|
38
|
-
return await action({ ctx, prevState, input: result.data });
|
|
39
|
-
}
|
|
40
|
-
return await action({ ctx, prevState, input: void 0 });
|
|
41
|
-
};
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-unsafe-assignment */ /* eslint-disable @typescript-eslint/no-explicit-any */ function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) {
|
|
2
|
+
try {
|
|
3
|
+
var info = gen[key](arg);
|
|
4
|
+
var value = info.value;
|
|
5
|
+
} catch (error) {
|
|
6
|
+
reject(error);
|
|
7
|
+
return;
|
|
42
8
|
}
|
|
43
|
-
|
|
9
|
+
if (info.done) {
|
|
10
|
+
resolve(value);
|
|
11
|
+
} else {
|
|
12
|
+
Promise.resolve(value).then(_next, _throw);
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
function _async_to_generator(fn) {
|
|
16
|
+
return function() {
|
|
17
|
+
var self = this, args = arguments;
|
|
18
|
+
return new Promise(function(resolve, reject) {
|
|
19
|
+
var gen = fn.apply(self, args);
|
|
20
|
+
function _next(value) {
|
|
21
|
+
asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value);
|
|
22
|
+
}
|
|
23
|
+
function _throw(err) {
|
|
24
|
+
asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err);
|
|
25
|
+
}
|
|
26
|
+
_next(undefined);
|
|
27
|
+
});
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
function _extends() {
|
|
31
|
+
_extends = Object.assign || function(target) {
|
|
32
|
+
for(var i = 1; i < arguments.length; i++){
|
|
33
|
+
var source = arguments[i];
|
|
34
|
+
for(var key in source){
|
|
35
|
+
if (Object.prototype.hasOwnProperty.call(source, key)) {
|
|
36
|
+
target[key] = source[key];
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
return target;
|
|
41
|
+
};
|
|
42
|
+
return _extends.apply(this, arguments);
|
|
43
|
+
}
|
|
44
|
+
const createNewServerActionBuilder = (def)=>{
|
|
45
|
+
return createServerActionBuilder(def);
|
|
44
46
|
};
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
47
|
+
const createServerActionBuilder = (initDef = {})=>{
|
|
48
|
+
const _def = _extends({
|
|
49
|
+
input: undefined,
|
|
50
|
+
middleware: undefined
|
|
51
|
+
}, initDef);
|
|
52
|
+
return {
|
|
53
|
+
middleware: (middleware)=>createNewServerActionBuilder(_extends({}, _def, {
|
|
54
|
+
middleware
|
|
55
|
+
})),
|
|
56
|
+
input: (input)=>createNewServerActionBuilder(_extends({}, _def, {
|
|
57
|
+
input
|
|
58
|
+
})),
|
|
59
|
+
action: (action)=>{
|
|
60
|
+
return /*#__PURE__*/ _async_to_generator(function*(input) {
|
|
61
|
+
const ctx = yield _def.middleware == null ? void 0 : _def.middleware.call(_def);
|
|
62
|
+
if (_def.input) {
|
|
63
|
+
const result = _def.input.safeParse(input);
|
|
64
|
+
if (!result.success) {
|
|
65
|
+
console.error('❌ Input validation error:', result.error.errors);
|
|
66
|
+
throw new Error('Input validation error');
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return yield action({
|
|
70
|
+
ctx,
|
|
71
|
+
input
|
|
72
|
+
});
|
|
73
|
+
});
|
|
74
|
+
},
|
|
75
|
+
formAction: (action)=>{
|
|
76
|
+
return /*#__PURE__*/ _async_to_generator(function*(prevState, formData) {
|
|
77
|
+
const ctx = yield _def.middleware == null ? void 0 : _def.middleware.call(_def);
|
|
78
|
+
if (_def.input) {
|
|
79
|
+
const result = _def.input.safeParse(formData);
|
|
80
|
+
if (!result.success) {
|
|
81
|
+
return yield action({
|
|
82
|
+
ctx,
|
|
83
|
+
prevState,
|
|
84
|
+
formErrors: result.error
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
return yield action({
|
|
88
|
+
ctx,
|
|
89
|
+
prevState,
|
|
90
|
+
input: result.data
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
return yield action({
|
|
94
|
+
ctx,
|
|
95
|
+
prevState,
|
|
96
|
+
input: undefined
|
|
97
|
+
});
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
};
|
|
48
101
|
};
|
|
49
|
-
|
|
102
|
+
/**
|
|
103
|
+
* Server action builder
|
|
104
|
+
*/ const serverAct = createServerActionBuilder();
|
|
105
|
+
|
|
106
|
+
export { serverAct };
|
package/package.json
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "server-act",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.5",
|
|
4
4
|
"homepage": "https://github.com/chungweileong94/server-act#readme",
|
|
5
|
+
"author": "chungweileong94",
|
|
6
|
+
"license": "MIT",
|
|
5
7
|
"repository": {
|
|
6
8
|
"type": "git",
|
|
7
9
|
"url": "git+https://github.com/chungweileong94/server-act.git"
|
|
@@ -11,15 +13,25 @@
|
|
|
11
13
|
},
|
|
12
14
|
"main": "dist/index.js",
|
|
13
15
|
"module": "dist/index.mjs",
|
|
14
|
-
"types": "dist/index.d.ts",
|
|
16
|
+
"types": "./dist/index.d.ts",
|
|
15
17
|
"exports": {
|
|
16
18
|
".": {
|
|
17
|
-
"
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
19
|
+
"import": {
|
|
20
|
+
"types": "./dist/index.d.mts",
|
|
21
|
+
"default": "./dist/index.mjs"
|
|
22
|
+
},
|
|
23
|
+
"require": {
|
|
24
|
+
"types": "./dist/index.d.ts",
|
|
25
|
+
"default": "./dist/index.js"
|
|
26
|
+
}
|
|
27
|
+
}
|
|
22
28
|
},
|
|
29
|
+
"files": [
|
|
30
|
+
"dist",
|
|
31
|
+
"package.json",
|
|
32
|
+
"LICENSE",
|
|
33
|
+
"README.md"
|
|
34
|
+
],
|
|
23
35
|
"keywords": [
|
|
24
36
|
"next",
|
|
25
37
|
"nextjs",
|
|
@@ -31,13 +43,11 @@
|
|
|
31
43
|
"server action",
|
|
32
44
|
"action"
|
|
33
45
|
],
|
|
34
|
-
"author": "chungweileong94",
|
|
35
|
-
"license": "MIT",
|
|
36
46
|
"devDependencies": {
|
|
47
|
+
"bunchee": "^4.3.3",
|
|
37
48
|
"eslint": "^8.49.0",
|
|
38
49
|
"eslint-config-whiteroom": "^3.3.0",
|
|
39
50
|
"prettier": "^3.0.3",
|
|
40
|
-
"tsup": "^7.2.0",
|
|
41
51
|
"typescript": "^5.2.2",
|
|
42
52
|
"zod": "^3.22.2",
|
|
43
53
|
"zod-form-data": "^2.0.2"
|
|
@@ -52,7 +62,8 @@
|
|
|
52
62
|
}
|
|
53
63
|
},
|
|
54
64
|
"scripts": {
|
|
55
|
-
"build": "
|
|
65
|
+
"build": "bunchee",
|
|
66
|
+
"dev": "bunchee -w",
|
|
56
67
|
"test": "vitest run",
|
|
57
68
|
"lint": "eslint src --ext .ts"
|
|
58
69
|
}
|
package/.eslintrc.json
DELETED
package/.prettierrc.json
DELETED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"arrowParens": "always",
|
|
3
|
-
"bracketSpacing": false,
|
|
4
|
-
"htmlWhitespaceSensitivity": "css",
|
|
5
|
-
"insertPragma": false,
|
|
6
|
-
"bracketSameLine": false,
|
|
7
|
-
"jsxSingleQuote": false,
|
|
8
|
-
"printWidth": 120,
|
|
9
|
-
"proseWrap": "preserve",
|
|
10
|
-
"quoteProps": "as-needed",
|
|
11
|
-
"semi": true,
|
|
12
|
-
"singleQuote": true,
|
|
13
|
-
"trailingComma": "all",
|
|
14
|
-
"useTabs": false
|
|
15
|
-
}
|
package/.turbo/turbo-build.log
DELETED
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
> server-act@1.1.3 build /home/runner/work/server-act/server-act/packages/server-act
|
|
3
|
-
> tsup
|
|
4
|
-
|
|
5
|
-
[34mCLI[39m Building entry: ./src/index.ts
|
|
6
|
-
[34mCLI[39m Using tsconfig: tsconfig.json
|
|
7
|
-
[34mCLI[39m tsup v7.2.0
|
|
8
|
-
[34mCLI[39m Using tsup config: /home/runner/work/server-act/server-act/packages/server-act/tsup.config.ts
|
|
9
|
-
[34mCLI[39m Target: node16
|
|
10
|
-
[34mCLI[39m Cleaning output folder
|
|
11
|
-
[34mESM[39m Build start
|
|
12
|
-
[34mCJS[39m Build start
|
|
13
|
-
[32mESM[39m [1mdist/index.mjs [22m[32m1.56 KB[39m
|
|
14
|
-
[32mESM[39m [1mdist/index.mjs.map [22m[32m5.50 KB[39m
|
|
15
|
-
[32mESM[39m ⚡️ Build success in 12ms
|
|
16
|
-
[32mCJS[39m [1mdist/index.js [22m[32m2.53 KB[39m
|
|
17
|
-
[32mCJS[39m [1mdist/index.js.map [22m[32m5.54 KB[39m
|
|
18
|
-
[32mCJS[39m ⚡️ Build success in 12ms
|
|
19
|
-
[34mDTS[39m Build start
|
|
20
|
-
[32mDTS[39m ⚡️ Build success in 999ms
|
|
21
|
-
[32mDTS[39m [1mdist/index.d.mts [22m[32m2.17 KB[39m
|
|
22
|
-
[32mDTS[39m [1mdist/index.d.ts [22m[32m2.17 KB[39m
|
package/CHANGELOG.md
DELETED
|
@@ -1,80 +0,0 @@
|
|
|
1
|
-
# server-act
|
|
2
|
-
|
|
3
|
-
## 1.1.3
|
|
4
|
-
|
|
5
|
-
### Patch Changes
|
|
6
|
-
|
|
7
|
-
- [#14](https://github.com/chungweileong94/server-act/pull/14) [`8bb348e`](https://github.com/chungweileong94/server-act/commit/8bb348ee0ed7a60a2498a37cab86c7271c205752) Thanks [@chungweileong94](https://github.com/chungweileong94)! - Remove `zod-validation-error` dependency
|
|
8
|
-
|
|
9
|
-
## 1.1.2
|
|
10
|
-
|
|
11
|
-
### Patch Changes
|
|
12
|
-
|
|
13
|
-
- a832b11: Improve form errors type infer
|
|
14
|
-
|
|
15
|
-
## 1.1.1
|
|
16
|
-
|
|
17
|
-
### Patch Changes
|
|
18
|
-
|
|
19
|
-
- 8d5b6e5: Fixed zod error type in `formAction` when using FormData
|
|
20
|
-
|
|
21
|
-
## 1.1.0
|
|
22
|
-
|
|
23
|
-
### Minor Changes
|
|
24
|
-
|
|
25
|
-
- 48b9164: Remove formData parsing for `formAction`
|
|
26
|
-
|
|
27
|
-
## 1.0.0
|
|
28
|
-
|
|
29
|
-
### Major Changes
|
|
30
|
-
|
|
31
|
-
- b4318a8: Get `formAction` out of experimental!
|
|
32
|
-
|
|
33
|
-
## 0.0.10
|
|
34
|
-
|
|
35
|
-
### Patch Changes
|
|
36
|
-
|
|
37
|
-
- a2ab457: Fixed form action doc
|
|
38
|
-
|
|
39
|
-
## 0.0.9
|
|
40
|
-
|
|
41
|
-
### Patch Changes
|
|
42
|
-
|
|
43
|
-
- d8682f2: Documentation for experimental form action
|
|
44
|
-
|
|
45
|
-
## 0.0.8
|
|
46
|
-
|
|
47
|
-
### Patch Changes
|
|
48
|
-
|
|
49
|
-
- 566261e: Change form action error to ZodError
|
|
50
|
-
- ead7149: Prettify form action params type
|
|
51
|
-
|
|
52
|
-
## 0.0.7
|
|
53
|
-
|
|
54
|
-
### Patch Changes
|
|
55
|
-
|
|
56
|
-
- 50e2853: New experimental form action
|
|
57
|
-
|
|
58
|
-
## 0.0.6
|
|
59
|
-
|
|
60
|
-
### Patch Changes
|
|
61
|
-
|
|
62
|
-
- fedd1d4: Update README
|
|
63
|
-
|
|
64
|
-
## 0.0.5
|
|
65
|
-
|
|
66
|
-
### Patch Changes
|
|
67
|
-
|
|
68
|
-
- 7e5d9c9: Support middleware
|
|
69
|
-
|
|
70
|
-
## 0.0.1
|
|
71
|
-
|
|
72
|
-
### Patch Changes
|
|
73
|
-
|
|
74
|
-
- 01d52a7: First Release!
|
|
75
|
-
|
|
76
|
-
## 0.0.1
|
|
77
|
-
|
|
78
|
-
### Patch Changes
|
|
79
|
-
|
|
80
|
-
- First Release!
|
package/dist/index.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["/* eslint-disable @typescript-eslint/no-unsafe-assignment */\n/* eslint-disable @typescript-eslint/no-explicit-any */\nimport {type z} from 'zod';\n\ntype Prettify<T> = {\n [P in keyof T]: T[P];\n // eslint-disable-next-line @typescript-eslint/ban-types\n} & {};\n\nconst unsetMarker = Symbol('unsetMarker');\ntype UnsetMarker = typeof unsetMarker;\n\ntype OptionalizeUndefined<T> = undefined extends T ? [param?: T] : [param: T];\n\ntype InferParserType<TParser, TType extends 'in' | 'out'> = TParser extends UnsetMarker\n ? undefined\n : TParser extends z.ZodType\n ? TParser[TType extends 'in' ? '_input' : '_output']\n : never;\n\ntype InferContextType<T> = T extends UnsetMarker ? undefined : T;\n\ninterface ActionParams<TInput = unknown, TContext = unknown> {\n _input: TInput;\n _context: TContext;\n}\n\ninterface ActionBuilder<TParams extends ActionParams> {\n /**\n * Middleware allows you to run code before the action, its return value will pass as context to the action.\n */\n middleware: <TContext>(\n middleware: () => Promise<TContext> | TContext,\n ) => ActionBuilder<{_input: TParams['_input']; _context: TContext}>;\n /**\n * Input validation for the action.\n */\n input: <TParser extends z.ZodType>(input: TParser) => ActionBuilder<{_input: TParser; _context: TParams['_context']}>;\n /**\n * Create an action.\n */\n action: <TOutput>(\n action: (params: {\n ctx: InferContextType<TParams['_context']>;\n input: InferParserType<TParams['_input'], 'out'>;\n }) => Promise<TOutput>,\n ) => (...[input]: OptionalizeUndefined<InferParserType<TParams['_input'], 'in'>>) => Promise<TOutput>;\n /**\n * Create an action for React `useFormState`\n */\n formAction: <TState>(\n action: (\n params: Prettify<\n {\n ctx: InferContextType<TParams['_context']>;\n prevState: any; // FIXME: This supposes to be `TState`, but we can't, as it will break the type.\n } & (\n | {input: InferParserType<TParams['_input'], 'out'>; formErrors?: undefined}\n | {\n input?: undefined;\n formErrors: z.ZodError<\n TParams['_input'] extends z.ZodEffects<infer T, unknown, unknown>\n ? InferParserType<T, 'in'>\n : InferParserType<TParams['_input'], 'in'>\n >;\n }\n )\n >,\n ) => Promise<TState>,\n ) => (prevState: TState, formData: FormData) => Promise<TState>;\n}\ntype AnyActionBuilder = ActionBuilder<any>;\n\ninterface ActionBuilderDef<TParams extends ActionParams<any>> {\n input: TParams['_input'];\n middleware: (() => Promise<TParams['_context']> | TParams['_context']) | undefined;\n}\ntype AnyActionBuilderDef = ActionBuilderDef<any>;\n\nconst createNewServerActionBuilder = (def: Partial<AnyActionBuilderDef>) => {\n return createServerActionBuilder(def);\n};\n\nconst createServerActionBuilder = (\n initDef: Partial<AnyActionBuilderDef> = {},\n): ActionBuilder<{\n _input: UnsetMarker;\n _context: UnsetMarker;\n}> => {\n const _def: ActionBuilderDef<{_input: z.ZodType | undefined; _context: undefined}> = {\n input: undefined,\n middleware: undefined,\n ...initDef,\n };\n return {\n middleware: (middleware) => createServerActionBuilder({..._def, middleware}) as AnyActionBuilder,\n input: (input) => createNewServerActionBuilder({..._def, input}) as AnyActionBuilder,\n action: (action) => {\n return async (input) => {\n const ctx = await _def.middleware?.();\n if (_def.input) {\n const result = _def.input.safeParse(input);\n if (!result.success) {\n console.error('❌ Input validation error:', result.error.errors);\n throw new Error('Input validation error');\n }\n }\n return await action({ctx, input});\n };\n },\n formAction: (action) => {\n return async (prevState, formData) => {\n const ctx = await _def.middleware?.();\n if (_def.input) {\n const result = _def.input.safeParse(formData);\n if (!result.success) {\n return await action({ctx, prevState, formErrors: result.error});\n }\n return await action({ctx, prevState, input: result.data});\n }\n return await action({ctx, prevState, input: undefined});\n };\n },\n };\n};\n\n/**\n * Server action builder\n */\nexport const serverAct = createServerActionBuilder();\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AASA,IAAM,cAAc,OAAO,aAAa;AAsExC,IAAM,+BAA+B,CAAC,QAAsC;AAC1E,SAAO,0BAA0B,GAAG;AACtC;AAEA,IAAM,4BAA4B,CAChC,UAAwC,CAAC,MAIrC;AACJ,QAAM,OAA+E;AAAA,IACnF,OAAO;AAAA,IACP,YAAY;AAAA,IACZ,GAAG;AAAA,EACL;AACA,SAAO;AAAA,IACL,YAAY,CAAC,eAAe,0BAA0B,EAAC,GAAG,MAAM,WAAU,CAAC;AAAA,IAC3E,OAAO,CAAC,UAAU,6BAA6B,EAAC,GAAG,MAAM,MAAK,CAAC;AAAA,IAC/D,QAAQ,CAAC,WAAW;AAClB,aAAO,OAAO,UAAU;AAlG9B;AAmGQ,cAAM,MAAM,QAAM,UAAK,eAAL;AAClB,YAAI,KAAK,OAAO;AACd,gBAAM,SAAS,KAAK,MAAM,UAAU,KAAK;AACzC,cAAI,CAAC,OAAO,SAAS;AACnB,oBAAQ,MAAM,kCAA6B,OAAO,MAAM,MAAM;AAC9D,kBAAM,IAAI,MAAM,wBAAwB;AAAA,UAC1C;AAAA,QACF;AACA,eAAO,MAAM,OAAO,EAAC,KAAK,MAAK,CAAC;AAAA,MAClC;AAAA,IACF;AAAA,IACA,YAAY,CAAC,WAAW;AACtB,aAAO,OAAO,WAAW,aAAa;AA/G5C;AAgHQ,cAAM,MAAM,QAAM,UAAK,eAAL;AAClB,YAAI,KAAK,OAAO;AACd,gBAAM,SAAS,KAAK,MAAM,UAAU,QAAQ;AAC5C,cAAI,CAAC,OAAO,SAAS;AACnB,mBAAO,MAAM,OAAO,EAAC,KAAK,WAAW,YAAY,OAAO,MAAK,CAAC;AAAA,UAChE;AACA,iBAAO,MAAM,OAAO,EAAC,KAAK,WAAW,OAAO,OAAO,KAAI,CAAC;AAAA,QAC1D;AACA,eAAO,MAAM,OAAO,EAAC,KAAK,WAAW,OAAO,OAAS,CAAC;AAAA,MACxD;AAAA,IACF;AAAA,EACF;AACF;AAKO,IAAM,YAAY,0BAA0B;","names":[]}
|
package/dist/index.mjs.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["/* eslint-disable @typescript-eslint/no-unsafe-assignment */\n/* eslint-disable @typescript-eslint/no-explicit-any */\nimport {type z} from 'zod';\n\ntype Prettify<T> = {\n [P in keyof T]: T[P];\n // eslint-disable-next-line @typescript-eslint/ban-types\n} & {};\n\nconst unsetMarker = Symbol('unsetMarker');\ntype UnsetMarker = typeof unsetMarker;\n\ntype OptionalizeUndefined<T> = undefined extends T ? [param?: T] : [param: T];\n\ntype InferParserType<TParser, TType extends 'in' | 'out'> = TParser extends UnsetMarker\n ? undefined\n : TParser extends z.ZodType\n ? TParser[TType extends 'in' ? '_input' : '_output']\n : never;\n\ntype InferContextType<T> = T extends UnsetMarker ? undefined : T;\n\ninterface ActionParams<TInput = unknown, TContext = unknown> {\n _input: TInput;\n _context: TContext;\n}\n\ninterface ActionBuilder<TParams extends ActionParams> {\n /**\n * Middleware allows you to run code before the action, its return value will pass as context to the action.\n */\n middleware: <TContext>(\n middleware: () => Promise<TContext> | TContext,\n ) => ActionBuilder<{_input: TParams['_input']; _context: TContext}>;\n /**\n * Input validation for the action.\n */\n input: <TParser extends z.ZodType>(input: TParser) => ActionBuilder<{_input: TParser; _context: TParams['_context']}>;\n /**\n * Create an action.\n */\n action: <TOutput>(\n action: (params: {\n ctx: InferContextType<TParams['_context']>;\n input: InferParserType<TParams['_input'], 'out'>;\n }) => Promise<TOutput>,\n ) => (...[input]: OptionalizeUndefined<InferParserType<TParams['_input'], 'in'>>) => Promise<TOutput>;\n /**\n * Create an action for React `useFormState`\n */\n formAction: <TState>(\n action: (\n params: Prettify<\n {\n ctx: InferContextType<TParams['_context']>;\n prevState: any; // FIXME: This supposes to be `TState`, but we can't, as it will break the type.\n } & (\n | {input: InferParserType<TParams['_input'], 'out'>; formErrors?: undefined}\n | {\n input?: undefined;\n formErrors: z.ZodError<\n TParams['_input'] extends z.ZodEffects<infer T, unknown, unknown>\n ? InferParserType<T, 'in'>\n : InferParserType<TParams['_input'], 'in'>\n >;\n }\n )\n >,\n ) => Promise<TState>,\n ) => (prevState: TState, formData: FormData) => Promise<TState>;\n}\ntype AnyActionBuilder = ActionBuilder<any>;\n\ninterface ActionBuilderDef<TParams extends ActionParams<any>> {\n input: TParams['_input'];\n middleware: (() => Promise<TParams['_context']> | TParams['_context']) | undefined;\n}\ntype AnyActionBuilderDef = ActionBuilderDef<any>;\n\nconst createNewServerActionBuilder = (def: Partial<AnyActionBuilderDef>) => {\n return createServerActionBuilder(def);\n};\n\nconst createServerActionBuilder = (\n initDef: Partial<AnyActionBuilderDef> = {},\n): ActionBuilder<{\n _input: UnsetMarker;\n _context: UnsetMarker;\n}> => {\n const _def: ActionBuilderDef<{_input: z.ZodType | undefined; _context: undefined}> = {\n input: undefined,\n middleware: undefined,\n ...initDef,\n };\n return {\n middleware: (middleware) => createServerActionBuilder({..._def, middleware}) as AnyActionBuilder,\n input: (input) => createNewServerActionBuilder({..._def, input}) as AnyActionBuilder,\n action: (action) => {\n return async (input) => {\n const ctx = await _def.middleware?.();\n if (_def.input) {\n const result = _def.input.safeParse(input);\n if (!result.success) {\n console.error('❌ Input validation error:', result.error.errors);\n throw new Error('Input validation error');\n }\n }\n return await action({ctx, input});\n };\n },\n formAction: (action) => {\n return async (prevState, formData) => {\n const ctx = await _def.middleware?.();\n if (_def.input) {\n const result = _def.input.safeParse(formData);\n if (!result.success) {\n return await action({ctx, prevState, formErrors: result.error});\n }\n return await action({ctx, prevState, input: result.data});\n }\n return await action({ctx, prevState, input: undefined});\n };\n },\n };\n};\n\n/**\n * Server action builder\n */\nexport const serverAct = createServerActionBuilder();\n"],"mappings":";AASA,IAAM,cAAc,OAAO,aAAa;AAsExC,IAAM,+BAA+B,CAAC,QAAsC;AAC1E,SAAO,0BAA0B,GAAG;AACtC;AAEA,IAAM,4BAA4B,CAChC,UAAwC,CAAC,MAIrC;AACJ,QAAM,OAA+E;AAAA,IACnF,OAAO;AAAA,IACP,YAAY;AAAA,IACZ,GAAG;AAAA,EACL;AACA,SAAO;AAAA,IACL,YAAY,CAAC,eAAe,0BAA0B,EAAC,GAAG,MAAM,WAAU,CAAC;AAAA,IAC3E,OAAO,CAAC,UAAU,6BAA6B,EAAC,GAAG,MAAM,MAAK,CAAC;AAAA,IAC/D,QAAQ,CAAC,WAAW;AAClB,aAAO,OAAO,UAAU;AAlG9B;AAmGQ,cAAM,MAAM,QAAM,UAAK,eAAL;AAClB,YAAI,KAAK,OAAO;AACd,gBAAM,SAAS,KAAK,MAAM,UAAU,KAAK;AACzC,cAAI,CAAC,OAAO,SAAS;AACnB,oBAAQ,MAAM,kCAA6B,OAAO,MAAM,MAAM;AAC9D,kBAAM,IAAI,MAAM,wBAAwB;AAAA,UAC1C;AAAA,QACF;AACA,eAAO,MAAM,OAAO,EAAC,KAAK,MAAK,CAAC;AAAA,MAClC;AAAA,IACF;AAAA,IACA,YAAY,CAAC,WAAW;AACtB,aAAO,OAAO,WAAW,aAAa;AA/G5C;AAgHQ,cAAM,MAAM,QAAM,UAAK,eAAL;AAClB,YAAI,KAAK,OAAO;AACd,gBAAM,SAAS,KAAK,MAAM,UAAU,QAAQ;AAC5C,cAAI,CAAC,OAAO,SAAS;AACnB,mBAAO,MAAM,OAAO,EAAC,KAAK,WAAW,YAAY,OAAO,MAAK,CAAC;AAAA,UAChE;AACA,iBAAO,MAAM,OAAO,EAAC,KAAK,WAAW,OAAO,OAAO,KAAI,CAAC;AAAA,QAC1D;AACA,eAAO,MAAM,OAAO,EAAC,KAAK,WAAW,OAAO,OAAS,CAAC;AAAA,MACxD;AAAA,IACF;AAAA,EACF;AACF;AAKO,IAAM,YAAY,0BAA0B;","names":[]}
|
package/src/index.test.ts
DELETED
|
@@ -1,110 +0,0 @@
|
|
|
1
|
-
import {test, expect, expectTypeOf, vi, beforeEach, describe} from 'vitest';
|
|
2
|
-
import {z} from 'zod';
|
|
3
|
-
import {zfd} from 'zod-form-data';
|
|
4
|
-
|
|
5
|
-
import {serverAct} from '.';
|
|
6
|
-
|
|
7
|
-
describe.concurrent('action', () => {
|
|
8
|
-
test('should able to create action without input', async () => {
|
|
9
|
-
const action = serverAct.action(async () => Promise.resolve('bar'));
|
|
10
|
-
|
|
11
|
-
expectTypeOf(action).parameter(0).toBeUndefined();
|
|
12
|
-
expectTypeOf(action).returns.resolves.toBeString();
|
|
13
|
-
await expect(action()).resolves.toBe('bar');
|
|
14
|
-
});
|
|
15
|
-
|
|
16
|
-
test('should able to create action with input', async () => {
|
|
17
|
-
const action = serverAct.input(z.string()).action(async () => Promise.resolve('bar'));
|
|
18
|
-
|
|
19
|
-
expectTypeOf(action).parameter(0).toBeString();
|
|
20
|
-
expectTypeOf(action).returns.resolves.toBeString();
|
|
21
|
-
await expect(action('foo')).resolves.toBe('bar');
|
|
22
|
-
});
|
|
23
|
-
|
|
24
|
-
test('should throw error if the input is invalid', async () => {
|
|
25
|
-
const action = serverAct.input(z.string()).action(async () => Promise.resolve('bar'));
|
|
26
|
-
|
|
27
|
-
expectTypeOf(action).parameter(0).toBeString();
|
|
28
|
-
expectTypeOf(action).returns.resolves.toBeString();
|
|
29
|
-
|
|
30
|
-
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
31
|
-
// @ts-ignore
|
|
32
|
-
await expect(action(1)).rejects.toThrowError();
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
describe('middleware should be called once', () => {
|
|
36
|
-
const middlewareSpy = vi.fn(() => {
|
|
37
|
-
return {prefix: 'best'};
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
beforeEach(() => {
|
|
41
|
-
vi.restoreAllMocks();
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
test('without input', async () => {
|
|
45
|
-
const action = serverAct.middleware(middlewareSpy).action(async ({ctx}) => Promise.resolve(`${ctx.prefix}-bar`));
|
|
46
|
-
|
|
47
|
-
expectTypeOf(action).returns.resolves.toBeString();
|
|
48
|
-
await expect(action()).resolves.toBe('best-bar');
|
|
49
|
-
expect(middlewareSpy).toBeCalledTimes(1);
|
|
50
|
-
});
|
|
51
|
-
|
|
52
|
-
test('with input', async () => {
|
|
53
|
-
const actionWithInput = serverAct
|
|
54
|
-
.middleware(middlewareSpy)
|
|
55
|
-
.input(z.string())
|
|
56
|
-
.action(async ({ctx, input}) => Promise.resolve(`${ctx.prefix}-${input}-bar`));
|
|
57
|
-
|
|
58
|
-
expectTypeOf(actionWithInput).parameter(0).toBeString();
|
|
59
|
-
expectTypeOf(actionWithInput).returns.resolves.toBeString();
|
|
60
|
-
await expect(actionWithInput('foo')).resolves.toBe('best-foo-bar');
|
|
61
|
-
expect(middlewareSpy).toBeCalledTimes(1);
|
|
62
|
-
});
|
|
63
|
-
});
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
describe.concurrent('formAction', () => {
|
|
67
|
-
test('should able to create form action without input', async () => {
|
|
68
|
-
const action = serverAct.formAction(async () => Promise.resolve('bar'));
|
|
69
|
-
|
|
70
|
-
expectTypeOf(action).parameter(0).toBeString();
|
|
71
|
-
expectTypeOf(action).parameter(1).toEqualTypeOf<FormData>();
|
|
72
|
-
expectTypeOf(action).returns.resolves.toBeString();
|
|
73
|
-
|
|
74
|
-
const formData = new FormData();
|
|
75
|
-
await expect(action('foo', formData)).resolves.toMatchObject('bar');
|
|
76
|
-
});
|
|
77
|
-
|
|
78
|
-
test('should able to create form action with input', async () => {
|
|
79
|
-
const action = serverAct.input(zfd.formData({foo: zfd.text()})).formAction(async () => Promise.resolve('bar'));
|
|
80
|
-
|
|
81
|
-
expectTypeOf(action).parameter(0).toBeString();
|
|
82
|
-
expectTypeOf(action).parameter(1).toEqualTypeOf<FormData>();
|
|
83
|
-
expectTypeOf(action).returns.resolves.toBeString();
|
|
84
|
-
|
|
85
|
-
const formData = new FormData();
|
|
86
|
-
formData.append('foo', 'bar');
|
|
87
|
-
await expect(action('foo', formData)).resolves.toMatchObject('bar');
|
|
88
|
-
});
|
|
89
|
-
|
|
90
|
-
test('should return form errors if the input is invalid', async () => {
|
|
91
|
-
const action = serverAct
|
|
92
|
-
.input(zfd.formData({foo: zfd.text(z.string({required_error: 'Required'}))}))
|
|
93
|
-
.formAction(async ({formErrors}) => {
|
|
94
|
-
if (formErrors) {
|
|
95
|
-
return formErrors;
|
|
96
|
-
}
|
|
97
|
-
return Promise.resolve('bar');
|
|
98
|
-
});
|
|
99
|
-
|
|
100
|
-
expectTypeOf(action).parameter(0).toMatchTypeOf<string | z.ZodError<{foo: string}>>();
|
|
101
|
-
expectTypeOf(action).parameter(1).toEqualTypeOf<FormData>();
|
|
102
|
-
|
|
103
|
-
const formData = new FormData();
|
|
104
|
-
formData.append('bar', 'foo');
|
|
105
|
-
|
|
106
|
-
const result = await action('foo', formData);
|
|
107
|
-
expect(result).toBeInstanceOf(z.ZodError);
|
|
108
|
-
expect(result).toHaveProperty('formErrors.fieldErrors', {foo: ['Required']});
|
|
109
|
-
});
|
|
110
|
-
});
|
package/src/index.ts
DELETED
|
@@ -1,130 +0,0 @@
|
|
|
1
|
-
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
|
|
2
|
-
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
3
|
-
import {type z} from 'zod';
|
|
4
|
-
|
|
5
|
-
type Prettify<T> = {
|
|
6
|
-
[P in keyof T]: T[P];
|
|
7
|
-
// eslint-disable-next-line @typescript-eslint/ban-types
|
|
8
|
-
} & {};
|
|
9
|
-
|
|
10
|
-
const unsetMarker = Symbol('unsetMarker');
|
|
11
|
-
type UnsetMarker = typeof unsetMarker;
|
|
12
|
-
|
|
13
|
-
type OptionalizeUndefined<T> = undefined extends T ? [param?: T] : [param: T];
|
|
14
|
-
|
|
15
|
-
type InferParserType<TParser, TType extends 'in' | 'out'> = TParser extends UnsetMarker
|
|
16
|
-
? undefined
|
|
17
|
-
: TParser extends z.ZodType
|
|
18
|
-
? TParser[TType extends 'in' ? '_input' : '_output']
|
|
19
|
-
: never;
|
|
20
|
-
|
|
21
|
-
type InferContextType<T> = T extends UnsetMarker ? undefined : T;
|
|
22
|
-
|
|
23
|
-
interface ActionParams<TInput = unknown, TContext = unknown> {
|
|
24
|
-
_input: TInput;
|
|
25
|
-
_context: TContext;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
interface ActionBuilder<TParams extends ActionParams> {
|
|
29
|
-
/**
|
|
30
|
-
* Middleware allows you to run code before the action, its return value will pass as context to the action.
|
|
31
|
-
*/
|
|
32
|
-
middleware: <TContext>(
|
|
33
|
-
middleware: () => Promise<TContext> | TContext,
|
|
34
|
-
) => ActionBuilder<{_input: TParams['_input']; _context: TContext}>;
|
|
35
|
-
/**
|
|
36
|
-
* Input validation for the action.
|
|
37
|
-
*/
|
|
38
|
-
input: <TParser extends z.ZodType>(input: TParser) => ActionBuilder<{_input: TParser; _context: TParams['_context']}>;
|
|
39
|
-
/**
|
|
40
|
-
* Create an action.
|
|
41
|
-
*/
|
|
42
|
-
action: <TOutput>(
|
|
43
|
-
action: (params: {
|
|
44
|
-
ctx: InferContextType<TParams['_context']>;
|
|
45
|
-
input: InferParserType<TParams['_input'], 'out'>;
|
|
46
|
-
}) => Promise<TOutput>,
|
|
47
|
-
) => (...[input]: OptionalizeUndefined<InferParserType<TParams['_input'], 'in'>>) => Promise<TOutput>;
|
|
48
|
-
/**
|
|
49
|
-
* Create an action for React `useFormState`
|
|
50
|
-
*/
|
|
51
|
-
formAction: <TState>(
|
|
52
|
-
action: (
|
|
53
|
-
params: Prettify<
|
|
54
|
-
{
|
|
55
|
-
ctx: InferContextType<TParams['_context']>;
|
|
56
|
-
prevState: any; // FIXME: This supposes to be `TState`, but we can't, as it will break the type.
|
|
57
|
-
} & (
|
|
58
|
-
| {input: InferParserType<TParams['_input'], 'out'>; formErrors?: undefined}
|
|
59
|
-
| {
|
|
60
|
-
input?: undefined;
|
|
61
|
-
formErrors: z.ZodError<
|
|
62
|
-
TParams['_input'] extends z.ZodEffects<infer T, unknown, unknown>
|
|
63
|
-
? InferParserType<T, 'in'>
|
|
64
|
-
: InferParserType<TParams['_input'], 'in'>
|
|
65
|
-
>;
|
|
66
|
-
}
|
|
67
|
-
)
|
|
68
|
-
>,
|
|
69
|
-
) => Promise<TState>,
|
|
70
|
-
) => (prevState: TState, formData: FormData) => Promise<TState>;
|
|
71
|
-
}
|
|
72
|
-
type AnyActionBuilder = ActionBuilder<any>;
|
|
73
|
-
|
|
74
|
-
interface ActionBuilderDef<TParams extends ActionParams<any>> {
|
|
75
|
-
input: TParams['_input'];
|
|
76
|
-
middleware: (() => Promise<TParams['_context']> | TParams['_context']) | undefined;
|
|
77
|
-
}
|
|
78
|
-
type AnyActionBuilderDef = ActionBuilderDef<any>;
|
|
79
|
-
|
|
80
|
-
const createNewServerActionBuilder = (def: Partial<AnyActionBuilderDef>) => {
|
|
81
|
-
return createServerActionBuilder(def);
|
|
82
|
-
};
|
|
83
|
-
|
|
84
|
-
const createServerActionBuilder = (
|
|
85
|
-
initDef: Partial<AnyActionBuilderDef> = {},
|
|
86
|
-
): ActionBuilder<{
|
|
87
|
-
_input: UnsetMarker;
|
|
88
|
-
_context: UnsetMarker;
|
|
89
|
-
}> => {
|
|
90
|
-
const _def: ActionBuilderDef<{_input: z.ZodType | undefined; _context: undefined}> = {
|
|
91
|
-
input: undefined,
|
|
92
|
-
middleware: undefined,
|
|
93
|
-
...initDef,
|
|
94
|
-
};
|
|
95
|
-
return {
|
|
96
|
-
middleware: (middleware) => createServerActionBuilder({..._def, middleware}) as AnyActionBuilder,
|
|
97
|
-
input: (input) => createNewServerActionBuilder({..._def, input}) as AnyActionBuilder,
|
|
98
|
-
action: (action) => {
|
|
99
|
-
return async (input) => {
|
|
100
|
-
const ctx = await _def.middleware?.();
|
|
101
|
-
if (_def.input) {
|
|
102
|
-
const result = _def.input.safeParse(input);
|
|
103
|
-
if (!result.success) {
|
|
104
|
-
console.error('❌ Input validation error:', result.error.errors);
|
|
105
|
-
throw new Error('Input validation error');
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
return await action({ctx, input});
|
|
109
|
-
};
|
|
110
|
-
},
|
|
111
|
-
formAction: (action) => {
|
|
112
|
-
return async (prevState, formData) => {
|
|
113
|
-
const ctx = await _def.middleware?.();
|
|
114
|
-
if (_def.input) {
|
|
115
|
-
const result = _def.input.safeParse(formData);
|
|
116
|
-
if (!result.success) {
|
|
117
|
-
return await action({ctx, prevState, formErrors: result.error});
|
|
118
|
-
}
|
|
119
|
-
return await action({ctx, prevState, input: result.data});
|
|
120
|
-
}
|
|
121
|
-
return await action({ctx, prevState, input: undefined});
|
|
122
|
-
};
|
|
123
|
-
},
|
|
124
|
-
};
|
|
125
|
-
};
|
|
126
|
-
|
|
127
|
-
/**
|
|
128
|
-
* Server action builder
|
|
129
|
-
*/
|
|
130
|
-
export const serverAct = createServerActionBuilder();
|
package/tsconfig.json
DELETED
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"$schema": "https://json.schemastore.org/tsconfig",
|
|
3
|
-
"display": "Strictest",
|
|
4
|
-
"_version": "2.0.0",
|
|
5
|
-
"compilerOptions": {
|
|
6
|
-
"lib": [
|
|
7
|
-
"ES2020",
|
|
8
|
-
"DOM",
|
|
9
|
-
"DOM.Iterable"
|
|
10
|
-
],
|
|
11
|
-
"module": "NodeNext",
|
|
12
|
-
"moduleResolution": "NodeNext",
|
|
13
|
-
"outDir": "./dist",
|
|
14
|
-
"strict": true,
|
|
15
|
-
"allowUnusedLabels": false,
|
|
16
|
-
"allowUnreachableCode": false,
|
|
17
|
-
"exactOptionalPropertyTypes": true,
|
|
18
|
-
"noFallthroughCasesInSwitch": true,
|
|
19
|
-
"noImplicitOverride": true,
|
|
20
|
-
"noImplicitReturns": true,
|
|
21
|
-
"noPropertyAccessFromIndexSignature": true,
|
|
22
|
-
"noUncheckedIndexedAccess": true,
|
|
23
|
-
"noUnusedLocals": true,
|
|
24
|
-
"noUnusedParameters": true,
|
|
25
|
-
"checkJs": true,
|
|
26
|
-
"esModuleInterop": true,
|
|
27
|
-
"skipLibCheck": true,
|
|
28
|
-
"forceConsistentCasingInFileNames": true
|
|
29
|
-
}
|
|
30
|
-
}
|