UNPKG

next-safe-action

Version:

Type safe and validated Server Actions in your Next.js project.

1,043 lines (1,028 loc) 35.4 kB
// src/adapters/zod.ts var ZodAdapter = class { async validate(schema, data) { const result = await schema.safeParseAsync(data); if (result.success) { return { success: true, data: result.data, }; } return { success: false, issues: result.error.issues.map(({ message, path }) => ({ message, path })), }; } }; function zodAdapter() { return new ZodAdapter(); } // ../../node_modules/.pnpm/deepmerge-ts@7.1.4/node_modules/deepmerge-ts/dist/index.mjs var actions = { defaultMerge: Symbol("deepmerge-ts: default merge"), skip: Symbol("deepmerge-ts: skip"), }; var actionsInto = { defaultMerge: actions.defaultMerge, }; function defaultMetaDataUpdater(previousMeta, metaMeta) { return metaMeta; } function defaultFilterValues(values, meta) { return values.filter((value) => value !== void 0); } var ObjectType; (function (ObjectType2) { ObjectType2[(ObjectType2["NOT"] = 0)] = "NOT"; ObjectType2[(ObjectType2["RECORD"] = 1)] = "RECORD"; ObjectType2[(ObjectType2["ARRAY"] = 2)] = "ARRAY"; ObjectType2[(ObjectType2["SET"] = 3)] = "SET"; ObjectType2[(ObjectType2["MAP"] = 4)] = "MAP"; ObjectType2[(ObjectType2["OTHER"] = 5)] = "OTHER"; })(ObjectType || (ObjectType = {})); function getObjectType(object) { if (typeof object !== "object" || object === null) { return 0; } if (Array.isArray(object)) { return 2; } if (isRecord(object)) { return 1; } if (object instanceof Set) { return 3; } if (object instanceof Map) { return 4; } return 5; } function getKeys(objects) { const keys = /* @__PURE__ */ new Set(); for (const object of objects) { for (const key of [...Object.keys(object), ...Object.getOwnPropertySymbols(object)]) { keys.add(key); } } return keys; } function objectHasProperty(object, property) { return typeof object === "object" && Object.prototype.propertyIsEnumerable.call(object, property); } function getIterableOfIterables(iterables) { let m_iterablesIndex = 0; let m_iterator = iterables[0]?.[Symbol.iterator](); return { [Symbol.iterator]() { return { next() { do { if (m_iterator === void 0) { return { done: true, value: void 0 }; } const result = m_iterator.next(); if (result.done === true) { m_iterablesIndex += 1; m_iterator = iterables[m_iterablesIndex]?.[Symbol.iterator](); continue; } return { done: false, value: result.value, }; } while (true); }, }; }, }; } var validRecordToStringValues = ["[object Object]", "[object Module]"]; function isRecord(value) { if (!validRecordToStringValues.includes(Object.prototype.toString.call(value))) { return false; } const { constructor } = value; if (constructor === void 0) { return true; } const prototype = constructor.prototype; if ( prototype === null || typeof prototype !== "object" || !validRecordToStringValues.includes(Object.prototype.toString.call(prototype)) ) { return false; } if (!prototype.hasOwnProperty("isPrototypeOf")) { return false; } return true; } function mergeRecords$1(values, utils, meta) { const result = {}; for (const key of getKeys(values)) { const propValues = []; for (const value of values) { if (objectHasProperty(value, key)) { propValues.push(value[key]); } } if (propValues.length === 0) { continue; } const updatedMeta = utils.metaDataUpdater(meta, { key, parents: values, }); const propertyResult = mergeUnknowns(propValues, utils, updatedMeta); if (propertyResult === actions.skip) { continue; } if (key === "__proto__") { Object.defineProperty(result, key, { value: propertyResult, configurable: true, enumerable: true, writable: true, }); } else { result[key] = propertyResult; } } return result; } function mergeArrays$1(values) { return values.flat(); } function mergeSets$1(values) { return new Set(getIterableOfIterables(values)); } function mergeMaps$1(values) { return new Map(getIterableOfIterables(values)); } function mergeOthers$1(values) { return values.at(-1); } var mergeFunctions = { mergeRecords: mergeRecords$1, mergeArrays: mergeArrays$1, mergeSets: mergeSets$1, mergeMaps: mergeMaps$1, mergeOthers: mergeOthers$1, }; function deepmerge(...objects) { return deepmergeCustom({})(...objects); } function deepmergeCustom(options, rootMetaData) { const utils = getUtils(options, customizedDeepmerge); function customizedDeepmerge(...objects) { return mergeUnknowns(objects, utils, rootMetaData); } return customizedDeepmerge; } function getUtils(options, customizedDeepmerge) { return { defaultMergeFunctions: mergeFunctions, mergeFunctions: { ...mergeFunctions, ...Object.fromEntries( Object.entries(options) .filter(([key, option]) => Object.hasOwn(mergeFunctions, key)) .map(([key, option]) => (option === false ? [key, mergeFunctions.mergeOthers] : [key, option])) ), }, metaDataUpdater: options.metaDataUpdater ?? defaultMetaDataUpdater, deepmerge: customizedDeepmerge, useImplicitDefaultMerging: options.enableImplicitDefaultMerging ?? false, filterValues: options.filterValues === false ? void 0 : (options.filterValues ?? defaultFilterValues), actions, }; } function mergeUnknowns(values, utils, meta) { const filteredValues = utils.filterValues?.(values, meta) ?? values; if (filteredValues.length === 0) { return void 0; } if (filteredValues.length === 1) { return mergeOthers(filteredValues, utils, meta); } const type = getObjectType(filteredValues[0]); if (type !== 0 && type !== 5) { for (let m_index = 1; m_index < filteredValues.length; m_index++) { if (getObjectType(filteredValues[m_index]) === type) { continue; } return mergeOthers(filteredValues, utils, meta); } } switch (type) { case 1: { return mergeRecords(filteredValues, utils, meta); } case 2: { return mergeArrays(filteredValues, utils, meta); } case 3: { return mergeSets(filteredValues, utils, meta); } case 4: { return mergeMaps(filteredValues, utils, meta); } default: { return mergeOthers(filteredValues, utils, meta); } } } function mergeRecords(values, utils, meta) { const result = utils.mergeFunctions.mergeRecords(values, utils, meta); if ( result === actions.defaultMerge || (utils.useImplicitDefaultMerging && result === void 0 && utils.mergeFunctions.mergeRecords !== utils.defaultMergeFunctions.mergeRecords) ) { return utils.defaultMergeFunctions.mergeRecords(values, utils, meta); } return result; } function mergeArrays(values, utils, meta) { const result = utils.mergeFunctions.mergeArrays(values, utils, meta); if ( result === actions.defaultMerge || (utils.useImplicitDefaultMerging && result === void 0 && utils.mergeFunctions.mergeArrays !== utils.defaultMergeFunctions.mergeArrays) ) { return utils.defaultMergeFunctions.mergeArrays(values); } return result; } function mergeSets(values, utils, meta) { const result = utils.mergeFunctions.mergeSets(values, utils, meta); if ( result === actions.defaultMerge || (utils.useImplicitDefaultMerging && result === void 0 && utils.mergeFunctions.mergeSets !== utils.defaultMergeFunctions.mergeSets) ) { return utils.defaultMergeFunctions.mergeSets(values); } return result; } function mergeMaps(values, utils, meta) { const result = utils.mergeFunctions.mergeMaps(values, utils, meta); if ( result === actions.defaultMerge || (utils.useImplicitDefaultMerging && result === void 0 && utils.mergeFunctions.mergeMaps !== utils.defaultMergeFunctions.mergeMaps) ) { return utils.defaultMergeFunctions.mergeMaps(values); } return result; } function mergeOthers(values, utils, meta) { const result = utils.mergeFunctions.mergeOthers(values, utils, meta); if ( result === actions.defaultMerge || (utils.useImplicitDefaultMerging && result === void 0 && utils.mergeFunctions.mergeOthers !== utils.defaultMergeFunctions.mergeOthers) ) { return utils.defaultMergeFunctions.mergeOthers(values); } return result; } // src/next/errors/bailout-to-csr.ts var BAILOUT_TO_CSR = "BAILOUT_TO_CLIENT_SIDE_RENDERING"; function isBailoutToCSRError(err) { if (typeof err !== "object" || err === null || !("digest" in err)) { return false; } return err.digest === BAILOUT_TO_CSR; } // src/next/errors/http-access-fallback.ts var HTTPAccessErrorStatus = { NOT_FOUND: 404, FORBIDDEN: 403, UNAUTHORIZED: 401, }; var ALLOWED_CODES = new Set(Object.values(HTTPAccessErrorStatus)); var HTTP_ERROR_FALLBACK_ERROR_CODE = "NEXT_HTTP_ERROR_FALLBACK"; function isHTTPAccessFallbackError(error) { if (typeof error !== "object" || error === null || !("digest" in error) || typeof error.digest !== "string") { return false; } const [prefix, httpStatus] = error.digest.split(";"); return prefix === HTTP_ERROR_FALLBACK_ERROR_CODE && ALLOWED_CODES.has(Number(httpStatus)); } function getAccessFallbackHTTPStatus(error) { const httpStatus = error.digest.split(";")[1]; return Number(httpStatus); } // src/next/errors/redirect.ts var RedirectStatusCode = /* @__PURE__ */ ((RedirectStatusCode2) => { RedirectStatusCode2[(RedirectStatusCode2["SeeOther"] = 303)] = "SeeOther"; RedirectStatusCode2[(RedirectStatusCode2["TemporaryRedirect"] = 307)] = "TemporaryRedirect"; RedirectStatusCode2[(RedirectStatusCode2["PermanentRedirect"] = 308)] = "PermanentRedirect"; return RedirectStatusCode2; })(RedirectStatusCode || {}); var REDIRECT_ERROR_CODE = "NEXT_REDIRECT"; function isRedirectError(error) { if (typeof error !== "object" || error === null || !("digest" in error) || typeof error.digest !== "string") { return false; } const digest = error.digest.split(";"); const [errorCode, type] = digest; const destination = digest.slice(2, -2).join(";"); const status = digest.at(-2); const statusCode = Number(status); return ( errorCode === REDIRECT_ERROR_CODE && (type === "replace" || type === "push") && typeof destination === "string" && !isNaN(statusCode) && statusCode in RedirectStatusCode ); } // src/next/errors/router.ts function isNextRouterError(error) { return isRedirectError(error) || isHTTPAccessFallbackError(error); } // src/next/errors/dynamic-usage.ts var DYNAMIC_ERROR_CODE = "DYNAMIC_SERVER_USAGE"; function isDynamicServerError(err) { if (typeof err !== "object" || err === null || !("digest" in err) || typeof err.digest !== "string") { return false; } return err.digest === DYNAMIC_ERROR_CODE; } function isDynamicPostponeReason(reason) { return ( reason.includes("needs to bail out of prerendering at this point because it used") && reason.includes("Learn more: https://nextjs.org/docs/messages/ppr-caught-error") ); } function isDynamicPostpone(err) { if ( typeof err === "object" && err !== null && // eslint-disable-next-line typeof err.message === "string" ) { return isDynamicPostponeReason(err.message); } return false; } var isDynamicUsageError = (err) => isDynamicServerError(err) || isBailoutToCSRError(err) || isNextRouterError(err) || isDynamicPostpone(err); // src/next/errors/postpone.ts var REACT_POSTPONE_TYPE = Symbol.for("react.postpone"); function isPostpone(error) { return ( typeof error === "object" && error !== null && // eslint-disable-next-line error.$$typeof === REACT_POSTPONE_TYPE ); } // src/next/errors/index.ts var FrameworkErrorHandler = class _FrameworkErrorHandler { #frameworkError; static isFrameworkError(error) { return isNextRouterError(error) || isBailoutToCSRError(error) || isDynamicUsageError(error) || isPostpone(error); } handleError(e) { if (_FrameworkErrorHandler.isFrameworkError(e)) { this.#frameworkError = e; return; } throw e; } get error() { return this.#frameworkError; } get isRedirectError() { return isRedirectError(this.#frameworkError); } get isNotFoundError() { return isHTTPAccessFallbackError(this.#frameworkError) && getAccessFallbackHTTPStatus(this.#frameworkError) === 404; } get isForbiddenError() { return isHTTPAccessFallbackError(this.#frameworkError) && getAccessFallbackHTTPStatus(this.#frameworkError) === 403; } get isUnauthorizedError() { return isHTTPAccessFallbackError(this.#frameworkError) && getAccessFallbackHTTPStatus(this.#frameworkError) === 401; } }; // src/utils.ts var DEFAULT_SERVER_ERROR_MESSAGE = "Something went wrong while executing the operation."; var isError = (error) => error instanceof Error; var winningBoolean = (...args) => { return args.reduce((acc, v) => (typeof v === "boolean" ? v : acc), false); }; // src/validation-errors.ts var buildValidationErrors = (issues) => { const ve = {}; for (const issue of issues) { const { path, message } = issue; if (!path || path.length === 0) { ve._errors = ve._errors ? [...ve._errors, message] : [message]; continue; } let ref = ve; for (let i = 0; i < path.length - 1; i++) { const k = path[i]; if (!ref[k]) { ref[k] = {}; } ref = ref[k]; } const key = path[path.length - 1]; ref[key] = ref[key]?._errors ? { ...structuredClone(ref[key]), _errors: [...ref[key]._errors, message], } : { ...structuredClone(ref[key]), _errors: [message] }; } return ve; }; var ActionServerValidationError = class extends Error { validationErrors; constructor(validationErrors) { super("Server Action server validation error(s) occurred"); this.validationErrors = validationErrors; } }; var ActionValidationError = class extends Error { validationErrors; constructor(validationErrors) { super("Server Action validation error(s) occurred"); this.validationErrors = validationErrors; } }; function returnValidationErrors(schema, validationErrors) { throw new ActionServerValidationError(validationErrors); } function formatValidationErrors(validationErrors) { return validationErrors; } function formatBindArgsValidationErrors(bindArgsValidationErrors) { return bindArgsValidationErrors; } function flattenValidationErrors(validationErrors) { const flattened = { formErrors: [], fieldErrors: {}, }; for (const [key, value] of Object.entries(validationErrors ?? {})) { if (key === "_errors" && Array.isArray(value)) { flattened.formErrors = [...value]; } else { if ("_errors" in value) { flattened.fieldErrors[key] = [...value._errors]; } } } return flattened; } function flattenBindArgsValidationErrors(bindArgsValidationErrors) { return bindArgsValidationErrors.map((ve) => flattenValidationErrors(ve)); } var ActionMetadataValidationError = class extends Error { validationErrors; constructor(validationErrors) { super("Invalid metadata input. Please be sure to pass metadata via `metadata` method before defining the action."); this.name = "ActionMetadataError"; this.validationErrors = validationErrors; } }; var ActionOutputDataValidationError = class extends Error { validationErrors; constructor(validationErrors) { super( "Invalid action data (output). Please be sure to return data following the shape of the schema passed to `dataSchema` method." ); this.name = "ActionOutputDataError"; this.validationErrors = validationErrors; } }; // src/action-builder.ts function actionBuilder(args) { const bindArgsSchemas = args.bindArgsSchemas ?? []; function buildAction({ withState }) { return { action: (serverCodeFn, utils) => { return async (...clientInputs) => { let currentCtx = {}; const middlewareResult = { success: false }; let prevResult = void 0; const parsedInputDatas = []; const frameworkErrorHandler = new FrameworkErrorHandler(); if (withState) { prevResult = clientInputs.splice(bindArgsSchemas.length, 1)[0]; } if (bindArgsSchemas.length + 1 > clientInputs.length) { clientInputs.push(void 0); } const executeMiddlewareStack = async (idx = 0) => { if (frameworkErrorHandler.error) { return; } const middlewareFn = args.middlewareFns[idx]; middlewareResult.ctx = currentCtx; try { if (idx === 0) { if (args.metadataSchema) { const parsedMd = await args.validationAdapter.validate(args.metadataSchema, args.metadata); if (!parsedMd.success) { throw new ActionMetadataValidationError(buildValidationErrors(parsedMd.issues)); } } } if (middlewareFn) { await middlewareFn({ clientInput: clientInputs.at(-1), // pass raw client input bindArgsClientInputs: bindArgsSchemas.length ? clientInputs.slice(0, -1) : [], ctx: currentCtx, metadata: args.metadata, next: async (nextOpts) => { currentCtx = deepmerge(currentCtx, nextOpts?.ctx ?? {}); await executeMiddlewareStack(idx + 1); return middlewareResult; }, }).catch((e) => frameworkErrorHandler.handleError(e)); } else { const parsedInputs = await Promise.all( clientInputs.map(async (input, i) => { if (i === clientInputs.length - 1) { if (typeof args.inputSchemaFn === "undefined") { return { success: true, data: void 0, }; } return args.validationAdapter.validate(await args.inputSchemaFn(), input); } return args.validationAdapter.validate(bindArgsSchemas[i], input); }) ); let hasBindValidationErrors = false; const bindArgsValidationErrors = Array(parsedInputs.length - 1).fill({}); for (let i = 0; i < parsedInputs.length; i++) { const parsedInput = parsedInputs[i]; if (parsedInput.success) { parsedInputDatas.push(parsedInput.data); } else { if (i < parsedInputs.length - 1) { bindArgsValidationErrors[i] = buildValidationErrors(parsedInput.issues); hasBindValidationErrors = true; } else { const validationErrors = buildValidationErrors(parsedInput.issues); middlewareResult.validationErrors = await Promise.resolve( args.handleValidationErrorsShape(validationErrors, { clientInput: clientInputs.at(-1), bindArgsClientInputs: bindArgsSchemas.length ? clientInputs.slice(0, -1) : [], ctx: currentCtx, metadata: args.metadata, }) ); } } } if (hasBindValidationErrors) { middlewareResult.bindArgsValidationErrors = await Promise.resolve( args.handleBindArgsValidationErrorsShape(bindArgsValidationErrors, { clientInput: clientInputs.at(-1), bindArgsClientInputs: bindArgsSchemas.length ? clientInputs.slice(0, -1) : [], ctx: currentCtx, metadata: args.metadata, }) ); } if (middlewareResult.validationErrors || middlewareResult.bindArgsValidationErrors) { return; } const scfArgs = []; scfArgs[0] = { parsedInput: parsedInputDatas.at(-1), bindArgsParsedInputs: parsedInputDatas.slice(0, -1), clientInput: clientInputs.at(-1), bindArgsClientInputs: bindArgsSchemas.length ? clientInputs.slice(0, -1) : [], ctx: currentCtx, metadata: args.metadata, }; if (withState) { scfArgs[1] = { prevResult: structuredClone(prevResult) }; } const data = await serverCodeFn(...scfArgs).catch((e) => frameworkErrorHandler.handleError(e)); if (typeof args.outputSchema !== "undefined" && !frameworkErrorHandler.error) { const parsedData = await args.validationAdapter.validate(args.outputSchema, data); if (!parsedData.success) { throw new ActionOutputDataValidationError(buildValidationErrors(parsedData.issues)); } } middlewareResult.success = true; middlewareResult.data = data; middlewareResult.parsedInput = parsedInputDatas.at(-1); middlewareResult.bindArgsParsedInputs = parsedInputDatas.slice(0, -1); } } catch (e) { if (e instanceof ActionServerValidationError) { const ve = e.validationErrors; middlewareResult.validationErrors = await Promise.resolve( args.handleValidationErrorsShape(ve, { clientInput: clientInputs.at(-1), bindArgsClientInputs: bindArgsSchemas.length ? clientInputs.slice(0, -1) : [], ctx: currentCtx, metadata: args.metadata, }) ); } else { const error = isError(e) ? e : new Error(DEFAULT_SERVER_ERROR_MESSAGE); const returnedError = await Promise.resolve( args.handleServerError(error, { clientInput: clientInputs.at(-1), // pass raw client input bindArgsClientInputs: bindArgsSchemas.length ? clientInputs.slice(0, -1) : [], ctx: currentCtx, metadata: args.metadata, }) ); middlewareResult.serverError = returnedError; } } }; await executeMiddlewareStack(); const callbackPromises = []; if (frameworkErrorHandler.error) { callbackPromises.push( utils?.onSuccess?.({ data: void 0, metadata: args.metadata, ctx: currentCtx, clientInput: clientInputs.at(-1), bindArgsClientInputs: bindArgsSchemas.length ? clientInputs.slice(0, -1) : [], parsedInput: parsedInputDatas.at(-1), bindArgsParsedInputs: parsedInputDatas.slice(0, -1), hasRedirected: frameworkErrorHandler.isRedirectError, hasNotFound: frameworkErrorHandler.isNotFoundError, hasForbidden: frameworkErrorHandler.isForbiddenError, hasUnauthorized: frameworkErrorHandler.isUnauthorizedError, }) ); callbackPromises.push( utils?.onSettled?.({ metadata: args.metadata, ctx: currentCtx, clientInput: clientInputs.at(-1), bindArgsClientInputs: bindArgsSchemas.length ? clientInputs.slice(0, -1) : [], result: {}, hasRedirected: frameworkErrorHandler.isRedirectError, hasNotFound: frameworkErrorHandler.isNotFoundError, hasForbidden: frameworkErrorHandler.isForbiddenError, hasUnauthorized: frameworkErrorHandler.isUnauthorizedError, }) ); await Promise.all(callbackPromises); throw frameworkErrorHandler.error; } const actionResult = {}; if (typeof middlewareResult.validationErrors !== "undefined") { if (winningBoolean(args.throwValidationErrors, utils?.throwValidationErrors)) { throw new ActionValidationError(middlewareResult.validationErrors); } else { actionResult.validationErrors = middlewareResult.validationErrors; } } if (typeof middlewareResult.bindArgsValidationErrors !== "undefined") { actionResult.bindArgsValidationErrors = middlewareResult.bindArgsValidationErrors; } if (typeof middlewareResult.serverError !== "undefined") { if (utils?.throwServerError) { throw middlewareResult.serverError; } else { actionResult.serverError = middlewareResult.serverError; } } if (middlewareResult.success) { if (typeof middlewareResult.data !== "undefined") { actionResult.data = middlewareResult.data; } callbackPromises.push( utils?.onSuccess?.({ metadata: args.metadata, ctx: currentCtx, data: actionResult.data, clientInput: clientInputs.at(-1), bindArgsClientInputs: bindArgsSchemas.length ? clientInputs.slice(0, -1) : [], parsedInput: parsedInputDatas.at(-1), bindArgsParsedInputs: parsedInputDatas.slice(0, -1), hasRedirected: false, hasNotFound: false, hasForbidden: false, hasUnauthorized: false, }) ); } else { callbackPromises.push( utils?.onError?.({ metadata: args.metadata, ctx: currentCtx, clientInput: clientInputs.at(-1), bindArgsClientInputs: bindArgsSchemas.length ? clientInputs.slice(0, -1) : [], error: actionResult, }) ); } callbackPromises.push( utils?.onSettled?.({ metadata: args.metadata, ctx: currentCtx, clientInput: clientInputs.at(-1), bindArgsClientInputs: bindArgsSchemas.length ? clientInputs.slice(0, -1) : [], result: actionResult, hasRedirected: false, hasNotFound: false, hasForbidden: false, hasUnauthorized: false, }) ); await Promise.all(callbackPromises); return actionResult; }; }, }; } return { /** * Define the action. * @param serverCodeFn Code that will be executed on the **server side** * * {@link https://next-safe-action.dev/docs/define-actions/instance-methods#action--stateaction See docs for more information} */ action: buildAction({ withState: false }).action, /** * Define the stateful action. To be used with the [`useStateAction`](https://next-safe-action.dev/docs/execute-actions/hooks/usestateaction) hook. * @param serverCodeFn Code that will be executed on the **server side** * * {@link https://next-safe-action.dev/docs/define-actions/instance-methods#action--stateaction See docs for more information} */ stateAction: buildAction({ withState: true }).action, }; } // src/safe-action-client.ts var SafeActionClient = class _SafeActionClient { #handleServerError; #middlewareFns; #metadataSchema; #metadata; #inputSchemaFn; #outputSchema; #ctxType; #bindArgsSchemas; #validationAdapter; #handleValidationErrorsShape; #handleBindArgsValidationErrorsShape; #defaultValidationErrorsShape; #throwValidationErrors; constructor(opts) { this.#middlewareFns = opts.middlewareFns; this.#handleServerError = opts.handleServerError; this.#metadataSchema = opts.metadataSchema; this.#metadata = opts.metadata; this.#inputSchemaFn = opts.inputSchemaFn ?? void 0; this.#outputSchema = opts.outputSchema; this.#bindArgsSchemas = opts.bindArgsSchemas ?? []; this.#validationAdapter = opts.validationAdapter; this.#ctxType = opts.ctxType; this.#handleValidationErrorsShape = opts.handleValidationErrorsShape; this.#handleBindArgsValidationErrorsShape = opts.handleBindArgsValidationErrorsShape; this.#defaultValidationErrorsShape = opts.defaultValidationErrorsShape; this.#throwValidationErrors = opts.throwValidationErrors; } /** * Use a middleware function. * @param middlewareFn Middleware function * * {@link https://next-safe-action.dev/docs/define-actions/instance-methods#use See docs for more information} */ use(middlewareFn) { return new _SafeActionClient({ middlewareFns: [...this.#middlewareFns, middlewareFn], handleServerError: this.#handleServerError, metadataSchema: this.#metadataSchema, metadata: this.#metadata, inputSchemaFn: this.#inputSchemaFn, outputSchema: this.#outputSchema, bindArgsSchemas: this.#bindArgsSchemas, validationAdapter: this.#validationAdapter, handleValidationErrorsShape: this.#handleValidationErrorsShape, handleBindArgsValidationErrorsShape: this.#handleBindArgsValidationErrorsShape, ctxType: {}, defaultValidationErrorsShape: this.#defaultValidationErrorsShape, throwValidationErrors: this.#throwValidationErrors, }); } /** * Define metadata for the action. * @param data Metadata with the same type as the return value of the [`defineMetadataSchema`](https://next-safe-action.dev/docs/define-actions/create-the-client#definemetadataschema) optional initialization function * * {@link https://next-safe-action.dev/docs/define-actions/instance-methods#metadata See docs for more information} */ metadata(data) { return new _SafeActionClient({ middlewareFns: this.#middlewareFns, handleServerError: this.#handleServerError, metadataSchema: this.#metadataSchema, metadata: data, inputSchemaFn: this.#inputSchemaFn, bindArgsSchemas: this.#bindArgsSchemas, outputSchema: this.#outputSchema, validationAdapter: this.#validationAdapter, handleValidationErrorsShape: this.#handleValidationErrorsShape, handleBindArgsValidationErrorsShape: this.#handleBindArgsValidationErrorsShape, ctxType: {}, defaultValidationErrorsShape: this.#defaultValidationErrorsShape, throwValidationErrors: this.#throwValidationErrors, }); } /** * Define the input validation schema for the action. * @param inputSchema Input validation schema * @param utils Optional utils object * * {@link https://next-safe-action.dev/docs/define-actions/create-the-client#inputschema See docs for more information} */ schema(inputSchema, utils) { return new _SafeActionClient({ middlewareFns: this.#middlewareFns, handleServerError: this.#handleServerError, metadataSchema: this.#metadataSchema, metadata: this.#metadata, // @ts-expect-error inputSchemaFn: inputSchema[Symbol.toStringTag] === "AsyncFunction" ? async () => { const prevSchema = await this.#inputSchemaFn?.(); return inputSchema(prevSchema); } : async () => inputSchema, bindArgsSchemas: this.#bindArgsSchemas, outputSchema: this.#outputSchema, validationAdapter: this.#validationAdapter, handleValidationErrorsShape: utils?.handleValidationErrorsShape ?? this.#handleValidationErrorsShape, handleBindArgsValidationErrorsShape: this.#handleBindArgsValidationErrorsShape, ctxType: {}, defaultValidationErrorsShape: this.#defaultValidationErrorsShape, throwValidationErrors: this.#throwValidationErrors, }); } /** * Define the bind args input validation schema for the action. * @param bindArgsSchemas Bind args input validation schemas * @param utils Optional utils object * * {@link https://next-safe-action.dev/docs/define-actions/instance-methods#bindargsschemas See docs for more information} */ bindArgsSchemas(bindArgsSchemas, utils) { return new _SafeActionClient({ middlewareFns: this.#middlewareFns, handleServerError: this.#handleServerError, metadataSchema: this.#metadataSchema, metadata: this.#metadata, inputSchemaFn: this.#inputSchemaFn, bindArgsSchemas, outputSchema: this.#outputSchema, validationAdapter: this.#validationAdapter, handleValidationErrorsShape: this.#handleValidationErrorsShape, handleBindArgsValidationErrorsShape: utils?.handleBindArgsValidationErrorsShape ?? this.#handleBindArgsValidationErrorsShape, ctxType: {}, defaultValidationErrorsShape: this.#defaultValidationErrorsShape, throwValidationErrors: this.#throwValidationErrors, }); } /** * Define the output data validation schema for the action. * @param schema Output data validation schema * * {@link https://next-safe-action.dev/docs/define-actions/create-the-client#outputschema See docs for more information} */ outputSchema(dataSchema) { return new _SafeActionClient({ middlewareFns: this.#middlewareFns, handleServerError: this.#handleServerError, metadataSchema: this.#metadataSchema, metadata: this.#metadata, inputSchemaFn: this.#inputSchemaFn, bindArgsSchemas: this.#bindArgsSchemas, outputSchema: dataSchema, validationAdapter: this.#validationAdapter, handleValidationErrorsShape: this.#handleValidationErrorsShape, handleBindArgsValidationErrorsShape: this.#handleBindArgsValidationErrorsShape, ctxType: {}, defaultValidationErrorsShape: this.#defaultValidationErrorsShape, throwValidationErrors: this.#throwValidationErrors, }); } /** * Define the action. * @param serverCodeFn Code that will be executed on the **server side** * @param [cb] Optional callbacks that will be called after action execution, on the server. * * {@link https://next-safe-action.dev/docs/define-actions/instance-methods#action--stateaction See docs for more information} */ action(serverCodeFn, utils) { return actionBuilder({ handleServerError: this.#handleServerError, middlewareFns: this.#middlewareFns, ctxType: this.#ctxType, metadataSchema: this.#metadataSchema, metadata: this.#metadata, inputSchemaFn: this.#inputSchemaFn, bindArgsSchemas: this.#bindArgsSchemas, outputSchema: this.#outputSchema, validationAdapter: this.#validationAdapter, handleValidationErrorsShape: this.#handleValidationErrorsShape, handleBindArgsValidationErrorsShape: this.#handleBindArgsValidationErrorsShape, throwValidationErrors: this.#throwValidationErrors, }).action(serverCodeFn, utils); } /** * Define the stateful action. * To be used with the [`useStateAction`](https://next-safe-action.dev/docs/execute-actions/hooks/usestateaction) hook. * @param serverCodeFn Code that will be executed on the **server side** * @param [cb] Optional callbacks that will be called after action execution, on the server. * * {@link https://next-safe-action.dev/docs/define-actions/instance-methods#action--stateaction See docs for more information} */ stateAction(serverCodeFn, utils) { return actionBuilder({ handleServerError: this.#handleServerError, middlewareFns: this.#middlewareFns, ctxType: this.#ctxType, metadataSchema: this.#metadataSchema, metadata: this.#metadata, inputSchemaFn: this.#inputSchemaFn, bindArgsSchemas: this.#bindArgsSchemas, outputSchema: this.#outputSchema, validationAdapter: this.#validationAdapter, handleValidationErrorsShape: this.#handleValidationErrorsShape, handleBindArgsValidationErrorsShape: this.#handleBindArgsValidationErrorsShape, throwValidationErrors: this.#throwValidationErrors, }).stateAction(serverCodeFn, utils); } }; // src/middleware.ts var createMiddleware = () => { return { define: (middlewareFn) => middlewareFn, }; }; // src/index.ts var createSafeActionClient = (createOpts) => { const handleServerError = createOpts?.handleServerError || ((e) => { console.error("Action error:", e.message); return DEFAULT_SERVER_ERROR_MESSAGE; }); return new SafeActionClient({ middlewareFns: [async ({ next }) => next({ ctx: {} })], handleServerError, inputSchemaFn: void 0, bindArgsSchemas: [], outputSchema: void 0, validationAdapter: createOpts?.validationAdapter ?? zodAdapter(), // use zod adapter by default ctxType: {}, metadataSchema: createOpts?.defineMetadataSchema?.() ?? void 0, metadata: void 0, defaultValidationErrorsShape: createOpts?.defaultValidationErrorsShape ?? "formatted", throwValidationErrors: Boolean(createOpts?.throwValidationErrors), handleValidationErrorsShape: async (ve) => createOpts?.defaultValidationErrorsShape === "flattened" ? flattenValidationErrors(ve) : formatValidationErrors(ve), handleBindArgsValidationErrorsShape: async (ve) => createOpts?.defaultValidationErrorsShape === "flattened" ? flattenBindArgsValidationErrors(ve) : formatBindArgsValidationErrors(ve), }); }; export { ActionMetadataValidationError, ActionOutputDataValidationError, ActionValidationError, DEFAULT_SERVER_ERROR_MESSAGE, createMiddleware, createSafeActionClient, flattenBindArgsValidationErrors, flattenValidationErrors, formatBindArgsValidationErrors, formatValidationErrors, returnValidationErrors, }; //# sourceMappingURL=index.mjs.map