Skip to content

Commit

Permalink
refactor: simpler buildValidationErrors implementation, move `Valid…
Browse files Browse the repository at this point in the history
…ationErrors` type to index
  • Loading branch information
TheEdoRan committed Feb 5, 2024
1 parent 2e687cb commit 5d3f258
Show file tree
Hide file tree
Showing 3 changed files with 19 additions and 15 deletions.
4 changes: 2 additions & 2 deletions packages/next-safe-action/src/hooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ const useActionCallbacks = <const S extends Schema, const Data>(
* @param safeAction The typesafe action.
* @param callbacks Optional callbacks executed based on the action status.
*
* {@link https://next-safe-action.dev/docs/usage-from-client/hooks/useaction See an example}
* {@link https://next-safe-action.dev/docs/usage/client-components/hooks/useaction See an example}
*/
export const useAction = <const S extends Schema, const Data>(
safeAction: SafeAction<S, Data>,
Expand Down Expand Up @@ -174,7 +174,7 @@ export const useAction = <const S extends Schema, const Data>(
* @param reducer Optimistic state reducer.
* @param callbacks Optional callbacks executed based on the action status.
*
* {@link https://next-safe-action.dev/docs/usage-from-client/hooks/useoptimisticaction See an example}
* {@link https://next-safe-action.dev/docs/usage/client-components/hooks/useoptimisticaction See an example}
*/
export const useOptimisticAction = <const S extends Schema, const Data>(
safeAction: SafeAction<S, Data>,
Expand Down
7 changes: 6 additions & 1 deletion packages/next-safe-action/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import type { Infer, InferIn, Schema } from "@decs/typeschema";
import { wrap } from "@decs/typeschema";
import { isNotFoundError } from "next/dist/client/components/not-found.js";
import { isRedirectError } from "next/dist/client/components/redirect.js";
import type { MaybePromise, ValidationErrors } from "./utils";
import type { ErrorList, Extend, MaybePromise, SchemaErrors } from "./utils";
import { buildValidationErrors, isError } from "./utils";

// TYPES
Expand Down Expand Up @@ -33,6 +33,11 @@ export type ServerCodeFn<S extends Schema, Data, Context> = (
ctx: Context
) => Promise<Data>;

/**
* Type of the returned object when input validation fails.
*/
export type ValidationErrors<S extends Schema> = Extend<ErrorList & SchemaErrors<Infer<S>>>;

// UTILS

export const DEFAULT_SERVER_ERROR = "Something went wrong while executing the operation";
Expand Down
23 changes: 11 additions & 12 deletions packages/next-safe-action/src/utils.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import type { Infer, Schema, ValidationIssue } from "@decs/typeschema";
import type { Schema, ValidationIssue } from "@decs/typeschema";
import type { ValidationErrors } from ".";

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

Expand All @@ -8,20 +9,18 @@ export const isError = (error: unknown): error is Error => error instanceof Erro
export type MaybePromise<T> = Promise<T> | T;

// Extends an object without printing "&".
type Extend<S> = S extends infer U ? { [K in keyof U]: U[K] } : never;
export type Extend<S> = S extends infer U ? { [K in keyof U]: U[K] } : never;

// VALIDATION ERRORS

type ErrorList = { _errors?: string[] } & {};
// Object with an optional list of validation errors.
export type ErrorList = { _errors?: string[] } & {};

// Creates nested schema errors type using recursion.
type SchemaErrors<S> = {
// Creates nested schema validation errors type using recursion.
export type SchemaErrors<S> = {
[K in keyof S]?: S[K] extends object ? Extend<ErrorList & SchemaErrors<S[K]>> : ErrorList;
} & {};

// Merges root errors with nested schema errors.
export type ValidationErrors<S extends Schema> = Extend<ErrorList & SchemaErrors<Infer<S>>>;

// This function is used to build the validation errors object from a list of validation issues.
export const buildValidationErrors = <const S extends Schema>(issues: ValidationIssue[]) => {
const ve: any = {};
Expand All @@ -31,7 +30,7 @@ export const buildValidationErrors = <const S extends Schema>(issues: Validation

// When path is undefined or empty, set root errors.
if (!path || path.length === 0) {
ve._errors = Array.isArray(ve._errors) ? [...ve._errors, message] : [message];
ve._errors = ve._errors ? [...ve._errors, message] : [message];
continue;
}

Expand All @@ -42,7 +41,7 @@ export const buildValidationErrors = <const S extends Schema>(issues: Validation
for (let i = 0; i < path.length - 1; i++) {
const k = path[i]!;

if (!(k in ref)) {
if (!ref[k]) {
ref[k] = {};
}

Expand All @@ -54,8 +53,8 @@ export const buildValidationErrors = <const S extends Schema>(issues: Validation

// Set error for the current path. If `_errors` array exists, add the message to it.
ref[key] = (
Array.isArray(ref[key]?._errors)
? { ...structuredClone(ref[key]), _errors: [...ref[key]!._errors!, message] }
ref[key]?._errors
? { ...structuredClone(ref[key]), _errors: [...ref[key]._errors, message] }
: { ...structuredClone(ref[key]), _errors: [message] }
) satisfies ErrorList;
}
Expand Down

0 comments on commit 5d3f258

Please sign in to comment.