Skip to content

Commit 8596aab

Browse files
committedJun 11, 2024
fix: correctly handle internal framework errors
1 parent 9ddb51e commit 8596aab

File tree

1 file changed

+39
-16
lines changed

1 file changed

+39
-16
lines changed
 

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

+39-16
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ export function actionBuilder<
7676
let prevResult: PrevResult | undefined = undefined;
7777
const valFn = args.validationStrategy === "zod" ? zodValidate : validate;
7878
const parsedInputDatas: any[] = [];
79+
let frameworkError: Error | null = null;
7980

8081
if (withState) {
8182
// Previous state is placed between bind args and main arg inputs, so it's always at the index of
@@ -93,6 +94,10 @@ export function actionBuilder<
9394

9495
// Execute the middleware stack.
9596
const executeMiddlewareStack = async (idx = 0) => {
97+
if (frameworkError) {
98+
return;
99+
}
100+
96101
const middlewareFn = args.middlewareFns[idx];
97102
middlewareResult.ctx = prevCtx;
98103

@@ -213,8 +218,8 @@ export function actionBuilder<
213218
// processed internally by Next.js.
214219
if (isRedirectError(e) || isNotFoundError(e)) {
215220
middlewareResult.success = true;
216-
// If an internal framework error occurred, throw it, so it will be processed by Next.js.
217-
throw e;
221+
frameworkError = e;
222+
return;
218223
}
219224

220225
// If error is `ActionServerValidationError`, return `validationErrors` as if schema validation would fail.
@@ -242,10 +247,30 @@ export function actionBuilder<
242247
// Execute middleware chain + action function.
243248
await executeMiddlewareStack();
244249

245-
const actionResult: SafeActionResult<ServerError, S, BAS, CVE, CBAVE, Data> = {};
250+
// If an internal framework error occurred, throw it, so it will be processed by Next.js.
251+
if (frameworkError) {
252+
await Promise.resolve(
253+
cb?.onSuccess?.({
254+
data: undefined,
255+
clientInput: clientInputs.at(-1) as S extends Schema ? InferIn<S> : undefined,
256+
bindArgsClientInputs: (bindArgsSchemas.length ? clientInputs.slice(0, -1) : []) as InferInArray<BAS>,
257+
parsedInput: parsedInputDatas.at(-1) as S extends Schema ? Infer<S> : undefined,
258+
bindArgsParsedInputs: parsedInputDatas.slice(0, -1) as InferArray<BAS>,
259+
})
260+
);
246261

247-
// This flag is needed because `onSuccess` callback is triggered even when there's no result data.
248-
let hasError = true;
262+
await Promise.resolve(
263+
cb?.onSettled?.({
264+
clientInput: clientInputs.at(-1) as S extends Schema ? InferIn<S> : undefined,
265+
bindArgsClientInputs: (bindArgsSchemas.length ? clientInputs.slice(0, -1) : []) as InferInArray<BAS>,
266+
result: {},
267+
})
268+
);
269+
270+
throw frameworkError;
271+
}
272+
273+
const actionResult: SafeActionResult<ServerError, S, BAS, CVE, CBAVE, Data> = {};
249274

250275
if (typeof middlewareResult.validationErrors !== "undefined") {
251276
actionResult.validationErrors = middlewareResult.validationErrors as CVE;
@@ -259,17 +284,7 @@ export function actionBuilder<
259284
actionResult.serverError = middlewareResult.serverError;
260285
}
261286

262-
hasError = false;
263-
264-
if (hasError) {
265-
await Promise.resolve(
266-
cb?.onError?.({
267-
clientInput: clientInputs.at(-1) as S extends Schema ? InferIn<S> : undefined,
268-
bindArgsClientInputs: (bindArgsSchemas.length ? clientInputs.slice(0, -1) : []) as InferInArray<BAS>,
269-
error: actionResult,
270-
})
271-
);
272-
} else {
287+
if (middlewareResult.success) {
273288
if (typeof middlewareResult.data !== "undefined") {
274289
actionResult.data = middlewareResult.data as Data;
275290
}
@@ -283,6 +298,14 @@ export function actionBuilder<
283298
bindArgsParsedInputs: parsedInputDatas.slice(0, -1) as InferArray<BAS>,
284299
})
285300
);
301+
} else {
302+
await Promise.resolve(
303+
cb?.onError?.({
304+
clientInput: clientInputs.at(-1) as S extends Schema ? InferIn<S> : undefined,
305+
bindArgsClientInputs: (bindArgsSchemas.length ? clientInputs.slice(0, -1) : []) as InferInArray<BAS>,
306+
error: actionResult,
307+
})
308+
);
286309
}
287310

288311
// onSettled, if provided, is always executed.

0 commit comments

Comments
 (0)
Please sign in to comment.