UNPKG

react-server-actions

Version:

A package for working with actions in React and Next.js

146 lines 6.26 kB
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