Skip to content

Commit 02d3caf

Browse files
committed
feat: use modular built-in multi validation system
1 parent 7f2a4f7 commit 02d3caf

File tree

7 files changed

+29
-31
lines changed

7 files changed

+29
-31
lines changed

packages/next-safe-action/src/action-builder.ts

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@ import type {
1212
ServerCodeFn,
1313
StateServerCodeFn,
1414
} from "./index.types";
15-
import { ActionMetadataError, DEFAULT_SERVER_ERROR_MESSAGE, isError, zodValidate } from "./utils";
16-
import type { Infer, InferArray, InferIn, InferInArray, Schema } from "./validation-adapters";
15+
import { ActionMetadataError, DEFAULT_SERVER_ERROR_MESSAGE, isError } from "./utils";
16+
import type { Infer, InferArray, InferIn, InferInArray, Schema, ValidationAdapter } from "./validation-adapters";
1717
import { ActionValidationError, buildValidationErrors } from "./validation-errors";
1818
import type {
1919
BindArgsValidationErrors,
@@ -35,6 +35,7 @@ export function actionBuilder<
3535
>(args: {
3636
schemaFn?: SF;
3737
bindArgsSchemas?: BAS;
38+
validationAdapter: ValidationAdapter;
3839
handleValidationErrorsShape: HandleValidationErrorsShapeFn<S, CVE>;
3940
handleBindArgsValidationErrorsShape: HandleBindArgsValidationErrorsShapeFn<BAS, CBAVE>;
4041
metadataSchema: MetadataSchema;
@@ -104,7 +105,7 @@ export function actionBuilder<
104105
if (idx === 0) {
105106
if (args.metadataSchema) {
106107
// Validate metadata input.
107-
if (!(await zodValidate(args.metadataSchema, args.metadata)).success) {
108+
if (!(await args.validationAdapter.validate(args.metadataSchema, args.metadata)).success) {
108109
throw new ActionMetadataError(
109110
"Invalid metadata input. Please be sure to pass metadata via `metadata` method before defining the action."
110111
);
@@ -141,11 +142,11 @@ export function actionBuilder<
141142
}
142143

143144
// Otherwise, parse input with the schema.
144-
return zodValidate(await args.schemaFn(), input);
145+
return args.validationAdapter.validate(await args.schemaFn(), input);
145146
}
146147

147148
// Otherwise, we're processing bind args client inputs.
148-
return zodValidate(bindArgsSchemas[i]!, input);
149+
return args.validationAdapter.validate(bindArgsSchemas[i]!, input);
149150
})
150151
);
151152

packages/next-safe-action/src/index.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,12 +53,18 @@ export const createSafeActionClient = <
5353
SafeActionClientOpts<ServerError, MetadataSchema, ODVES>["handleReturnedServerError"]
5454
>);
5555

56+
// FIXME: require validation adapter
57+
if (!createOpts?.validationAdapter) {
58+
throw new Error("Validation adapter is required");
59+
}
60+
5661
return new SafeActionClient({
5762
middlewareFns: [async ({ next }) => next({ ctx: undefined })],
5863
handleServerErrorLog,
5964
handleReturnedServerError,
6065
schemaFn: undefined,
6166
bindArgsSchemas: [],
67+
validationAdapter: createOpts.validationAdapter(),
6268
ctxType: undefined,
6369
metadataSchema: (createOpts?.defineMetadataSchema?.() ?? undefined) as MetadataSchema,
6470
metadata: undefined as MetadataSchema extends Schema ? Infer<MetadataSchema> : undefined,

packages/next-safe-action/src/index.types.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import type { MaybePromise, Prettify } from "./utils.types";
2-
import type { Infer, InferArray, InferIn, InferInArray, Schema } from "./validation-adapters";
2+
import type { Infer, InferArray, InferIn, InferInArray, Schema, ValidationAdapter } from "./validation-adapters";
33
import type { BindArgsValidationErrors, ValidationErrors } from "./validation-errors.types";
44

55
/**
@@ -26,6 +26,7 @@ export type SafeActionClientOpts<
2626
MetadataSchema extends Schema | undefined,
2727
ODVES extends DVES | undefined,
2828
> = {
29+
validationAdapter: () => ValidationAdapter;
2930
defineMetadataSchema?: () => MetadataSchema;
3031
handleReturnedServerError?: (
3132
error: Error,

packages/next-safe-action/src/safe-action-client.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import type {
88
ServerCodeFn,
99
StateServerCodeFn,
1010
} from "./index.types";
11-
import type { Infer, Schema } from "./validation-adapters";
11+
import type { Infer, Schema, ValidationAdapter } from "./validation-adapters";
1212
import type {
1313
BindArgsValidationErrors,
1414
FlattenedBindArgsValidationErrors,
@@ -42,6 +42,7 @@ export class SafeActionClient<
4242
readonly #metadata: MD;
4343
readonly #schemaFn: SF;
4444
readonly #bindArgsSchemas: BAS;
45+
readonly #validationAdapter: ValidationAdapter;
4546
readonly #handleValidationErrorsShape: HandleValidationErrorsShapeFn<S, CVE>;
4647
readonly #handleBindArgsValidationErrorsShape: HandleBindArgsValidationErrorsShapeFn<BAS, CBAVE>;
4748
readonly #defaultValidationErrorsShape: ODVES;
@@ -54,6 +55,7 @@ export class SafeActionClient<
5455
metadata: MD;
5556
schemaFn: SF;
5657
bindArgsSchemas: BAS;
58+
validationAdapter: ValidationAdapter;
5759
handleValidationErrorsShape: HandleValidationErrorsShapeFn<S, CVE>;
5860
handleBindArgsValidationErrorsShape: HandleBindArgsValidationErrorsShapeFn<BAS, CBAVE>;
5961
ctxType: Ctx;
@@ -71,6 +73,7 @@ export class SafeActionClient<
7173
this.#metadata = opts.metadata;
7274
this.#schemaFn = (opts.schemaFn ?? undefined) as SF;
7375
this.#bindArgsSchemas = opts.bindArgsSchemas ?? [];
76+
this.#validationAdapter = opts.validationAdapter;
7477
this.#handleValidationErrorsShape = opts.handleValidationErrorsShape;
7578
this.#handleBindArgsValidationErrorsShape = opts.handleBindArgsValidationErrorsShape;
7679
this.#defaultValidationErrorsShape = opts.defaultValidationErrorsShape;
@@ -92,6 +95,7 @@ export class SafeActionClient<
9295
metadata: this.#metadata,
9396
schemaFn: this.#schemaFn,
9497
bindArgsSchemas: this.#bindArgsSchemas,
98+
validationAdapter: this.#validationAdapter,
9599
handleValidationErrorsShape: this.#handleValidationErrorsShape,
96100
handleBindArgsValidationErrorsShape: this.#handleBindArgsValidationErrorsShape,
97101
ctxType: undefined as NextCtx,
@@ -115,6 +119,7 @@ export class SafeActionClient<
115119
metadata: data,
116120
schemaFn: this.#schemaFn,
117121
bindArgsSchemas: this.#bindArgsSchemas,
122+
validationAdapter: this.#validationAdapter,
118123
handleValidationErrorsShape: this.#handleValidationErrorsShape,
119124
handleBindArgsValidationErrorsShape: this.#handleBindArgsValidationErrorsShape,
120125
ctxType: undefined as Ctx,
@@ -155,6 +160,7 @@ export class SafeActionClient<
155160
}
156161
: async () => schema) as SF,
157162
bindArgsSchemas: this.#bindArgsSchemas,
163+
validationAdapter: this.#validationAdapter,
158164
handleValidationErrorsShape: (utils?.handleValidationErrorsShape ??
159165
this.#handleValidationErrorsShape) as HandleValidationErrorsShapeFn<AS, OCVE>,
160166
handleBindArgsValidationErrorsShape: this.#handleBindArgsValidationErrorsShape,
@@ -188,6 +194,7 @@ export class SafeActionClient<
188194
metadata: this.#metadata,
189195
schemaFn: this.#schemaFn,
190196
bindArgsSchemas,
197+
validationAdapter: this.#validationAdapter,
191198
handleValidationErrorsShape: this.#handleValidationErrorsShape,
192199
handleBindArgsValidationErrorsShape: (utils?.handleBindArgsValidationErrorsShape ??
193200
this.#handleBindArgsValidationErrorsShape) as HandleBindArgsValidationErrorsShapeFn<OBAS, OCBAVE>,
@@ -217,6 +224,7 @@ export class SafeActionClient<
217224
metadata: this.#metadata,
218225
schemaFn: this.#schemaFn,
219226
bindArgsSchemas: this.#bindArgsSchemas,
227+
validationAdapter: this.#validationAdapter,
220228
handleValidationErrorsShape: this.#handleValidationErrorsShape,
221229
handleBindArgsValidationErrorsShape: this.#handleBindArgsValidationErrorsShape,
222230
throwValidationErrors: this.#throwValidationErrors,
@@ -244,6 +252,7 @@ export class SafeActionClient<
244252
metadata: this.#metadata,
245253
schemaFn: this.#schemaFn,
246254
bindArgsSchemas: this.#bindArgsSchemas,
255+
validationAdapter: this.#validationAdapter,
247256
handleValidationErrorsShape: this.#handleValidationErrorsShape,
248257
handleBindArgsValidationErrorsShape: this.#handleBindArgsValidationErrorsShape,
249258
throwValidationErrors: this.#throwValidationErrors,

packages/next-safe-action/src/utils.ts

Lines changed: 0 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,7 @@
1-
import type { Infer, Schema } from "./validation-adapters";
2-
31
export const DEFAULT_SERVER_ERROR_MESSAGE = "Something went wrong while executing the operation.";
42

53
export const isError = (error: unknown): error is Error => error instanceof Error;
64

7-
// Validate with Zod.
8-
export async function zodValidate<S extends Schema>(s: S, data: unknown) {
9-
const result = await s.safeParseAsync(data);
10-
11-
if (result.success) {
12-
return {
13-
success: true,
14-
data: result.data as Infer<S>,
15-
} as const;
16-
}
17-
18-
return {
19-
success: false,
20-
issues: result.error.issues.map(({ message, path }) => ({ message, path })),
21-
} as const;
22-
}
23-
245
/**
256
* This error is thrown when an action's metadata input is invalid, i.e. when there's a mismatch between the
267
* type of the metadata schema returned from `defineMetadataSchema` and the actual input.

packages/next-safe-action/src/validation-adapters/libs/zod.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import type { z } from "zod";
22
import type { Infer, ValidationAdapter } from "../types";
33

4-
class ZodAdapter<S extends z.ZodType> implements ValidationAdapter<S> {
5-
async validate(schema: S, data: unknown) {
4+
class ZodAdapter implements ValidationAdapter {
5+
async validate<S extends z.ZodType>(schema: S, data: unknown) {
66
const result = await schema.safeParseAsync(data);
77

88
if (result.success) {
@@ -20,5 +20,5 @@ class ZodAdapter<S extends z.ZodType> implements ValidationAdapter<S> {
2020
}
2121

2222
export function zodAdapter() {
23-
return new ZodAdapter<z.ZodType>();
23+
return new ZodAdapter();
2424
}

packages/next-safe-action/src/validation-adapters/types.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@ export type ValidationIssue = {
1515
path?: Array<string | number | symbol>;
1616
};
1717

18-
export interface ValidationAdapter<S extends Schema> {
19-
validate(
18+
export interface ValidationAdapter {
19+
validate<S extends Schema>(
2020
schema: S,
2121
data: unknown
2222
): Promise<{ success: true; data: Infer<S> } | { success: false; issues: ValidationIssue[] }>;

0 commit comments

Comments
 (0)