react-server-actions
Version:
A package for working with actions in React and Next.js
146 lines • 6.26 kB
JavaScript
import { z } from 'zod';
import { decodeFormData, serializeFormData, serializeInvalidResult, } from './decode_form.js';
import { _actionError, _actionInvalid, _actionSuccess, error, invalid, isErrorActionResult, isInvalidActionResult, } from './helpers.js';
import {} from './types.js';
export class ActionClient {
handleExceptionsAsFormErrors;
onFormError;
actionConvertEmptyTo;
actionWithParamConvertEmptyTo;
log;
constructor(options = {
handleExceptionsAsFormErrors: true,
onFormError: undefined,
actionConvertEmptyTo: 'undefined',
actionWithParamConvertEmptyTo: 'undefined',
log: false,
}) {
this.handleExceptionsAsFormErrors =
options.handleExceptionsAsFormErrors ?? true;
this.onFormError = options.onFormError;
this.actionConvertEmptyTo = options.actionConvertEmptyTo ?? 'undefined';
this.actionWithParamConvertEmptyTo =
options.actionWithParamConvertEmptyTo ?? 'undefined';
this.log = options.log ?? false;
}
// ** Action wrapper
action = (schema, fn, options) => {
return async (_prevState, data) => {
if (this.log) {
console.log('[react-server-actions] 1 formData', data);
}
const formData = decodeFormData(data, this.actionConvertEmptyTo);
if (this.log) {
console.log('[react-server-actions] 2 formData decoded', formData);
}
const parsedData = await schema.safeParseAsync(formData);
if (this.log) {
console.log('[react-server-actions] 3 parsedData', parsedData);
}
const serializedData = serializeFormData(formData);
if (this.log) {
console.log('[react-server-actions] 4 serializedData', serializedData);
}
if (!parsedData.success) {
console.log('[react-server-actions] 5 invalid', serializeInvalidResult(parsedData.error));
return _actionInvalid(serializedData, invalid(serializeInvalidResult(parsedData.error)));
}
try {
const actionResponse = await fn({
data: parsedData.data,
formData: data,
});
if (actionResponse && isInvalidActionResult(actionResponse)) {
// Permit to return a InvalidActionResult from the action for custom validations
return _actionInvalid(serializedData, actionResponse);
}
if (actionResponse && isErrorActionResult(actionResponse)) {
// Permit to return a ErrorActionResult from the action for custom errors
return _actionError(serializedData, actionResponse);
}
return _actionSuccess(serializedData, actionResponse);
}
catch (e) {
if (options?.handleExceptionsAsFormErrors ??
this.handleExceptionsAsFormErrors) {
if (this.isNextInternalError(e)) {
throw e;
}
else {
if (this.onFormError) {
this.onFormError(e instanceof Error ? e : new Error(String(e)));
}
return _actionError(serializedData, error(e));
}
}
else {
throw e;
}
}
};
};
actionWithParam = (schema, fn, options) => {
return async (param, _prevState, data) => {
if (this.log) {
console.log('[react-server-actions] 1 formData', data);
}
const formData = decodeFormData(data, this.actionWithParamConvertEmptyTo);
if (this.log) {
console.log('[react-server-actions] 2 formData decoded', formData);
}
const parsedData = await schema.safeParseAsync(formData);
if (this.log) {
console.log('[react-server-actions] 3 parsedData', parsedData);
}
const serializedData = serializeFormData(formData);
if (this.log) {
console.log('[react-server-actions] 4 serializedData', serializedData);
}
if (!parsedData.success) {
return _actionInvalid(serializedData, invalid(serializeInvalidResult(parsedData.error)));
}
try {
const actionResponse = await fn({
param,
data: parsedData.data,
formData: data,
});
if (actionResponse && isInvalidActionResult(actionResponse)) {
return _actionInvalid(serializedData, actionResponse);
}
if (actionResponse && isErrorActionResult(actionResponse)) {
return _actionError(serializedData, actionResponse);
}
return _actionSuccess(serializedData, actionResponse);
}
catch (e) {
if (options?.handleExceptionsAsFormErrors ??
this.handleExceptionsAsFormErrors) {
if (this.isNextInternalError(e)) {
throw e;
}
else {
if (this.onFormError) {
this.onFormError(e instanceof Error ? e : new Error(String(e)));
}
return _actionError(serializedData, error(e));
}
}
else {
throw e;
}
}
};
};
/**
* Checks if an error is a Next.js internal error that should be re-thrown.
* Next.js internal errors have a `digest` property starting with "NEXT_".
*/
isNextInternalError = (error) => {
return (error instanceof Error &&
'digest' in error &&
typeof error.digest === 'string' &&
error.digest.startsWith('NEXT_'));
};
}
//# sourceMappingURL=actions.js.map