@trpc/server
Version:
1 lines • 11 kB
Source Map (JSON)
{"version":3,"file":"next-app-dir.mjs","names":[],"sources":["../../src/adapters/next-app-dir/redirect.ts","../../src/adapters/next-app-dir/rethrowNextErrors.ts","../../src/adapters/next-app-dir/nextAppDirCaller.ts","../../src/adapters/next-app-dir/notFound.ts"],"sourcesContent":["import type { RedirectType } from 'next/navigation';\nimport { TRPCError } from '../../@trpc/server';\n\n/**\n * @internal\n */\nexport class TRPCRedirectError extends TRPCError {\n public readonly args;\n constructor(url: URL | string, redirectType?: RedirectType) {\n super({\n // TODO(?): This should maybe a custom error code\n code: 'UNPROCESSABLE_CONTENT',\n message: `Redirect error to \"${url}\" that will be handled by Next.js`,\n });\n\n this.args = [url.toString(), redirectType] as const;\n }\n}\n\n/**\n * Like `next/navigation`'s `redirect()` but throws a `TRPCError` that later will be handled by Next.js\n * This provides better typesafety than the `next/navigation`'s `redirect()` since the action continues\n * to execute on the frontend even if Next's `redirect()` has a return type of `never`.\n * @public\n * @remark You should only use this if you're also using `nextAppDirCaller`.\n */\nexport const redirect = (url: URL | string, redirectType?: RedirectType) => {\n // We rethrow this internally so the returntype on the client is undefined.\n return new TRPCRedirectError(url, redirectType) as unknown as undefined;\n};\n","import * as nextNavigation from 'next/navigation';\nimport type { TRPCError } from '../../@trpc/server';\nimport { TRPCRedirectError } from './redirect';\n\n/**\n * @remarks The helpers from `next/dist/client/components/*` has been removed in Next.js 15.\n * Inlining them here instead...\n * @see https://github.com/vercel/next.js/blob/5ae286ffd664e5c76841ed64f6e2da85a0835922/packages/next/src/client/components/redirect.ts#L97-L123\n */\nconst REDIRECT_ERROR_CODE = 'NEXT_REDIRECT';\nfunction isRedirectError(error: unknown) {\n if (\n typeof error !== 'object' ||\n error === null ||\n !('digest' in error) ||\n typeof error.digest !== 'string'\n ) {\n return false;\n }\n const [errorCode, type, destination, status] = error.digest.split(';', 4);\n const statusCode = Number(status);\n return (\n errorCode === REDIRECT_ERROR_CODE &&\n (type === 'replace' || type === 'push') &&\n typeof destination === 'string' &&\n !isNaN(statusCode)\n );\n}\n\n/**\n * @remarks The helpers from `next/dist/client/components/*` has been removed in Next.js 15.\n * Inlining them here instead...\n * @see https://github.com/vercel/next.js/blob/5ae286ffd664e5c76841ed64f6e2da85a0835922/packages/next/src/client/components/not-found.ts#L33-L39\n */\nconst NOT_FOUND_ERROR_CODE = 'NEXT_NOT_FOUND';\nfunction isNotFoundError(error: unknown) {\n if (typeof error !== 'object' || error === null || !('digest' in error)) {\n return false;\n }\n return error.digest === NOT_FOUND_ERROR_CODE;\n}\n\n/**\n * Rethrow errors that should be handled by Next.js\n */\nexport const rethrowNextErrors = (error: TRPCError) => {\n if (error.code === 'NOT_FOUND') {\n nextNavigation.notFound();\n }\n if (error instanceof TRPCRedirectError) {\n nextNavigation.redirect(...error.args);\n }\n const { cause } = error;\n\n // Next.js 15 has `unstable_rethrow`. Use that if it exists.\n if (\n 'unstable_rethrow' in nextNavigation &&\n typeof nextNavigation.unstable_rethrow === 'function'\n ) {\n nextNavigation.unstable_rethrow(cause);\n }\n\n // Before Next.js 15, we have to check and rethrow the error manually.\n if (isRedirectError(cause) || isNotFoundError(cause)) {\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n throw cause!;\n }\n};\n","import type { CreateContextCallback } from '../../@trpc/server';\nimport { getTRPCErrorFromUnknown, TRPCError } from '../../@trpc/server';\n// eslint-disable-next-line no-restricted-imports\nimport { formDataToObject } from '../../unstable-core-do-not-import';\n// FIXME: fix lint rule, this is ok\n// eslint-disable-next-line no-restricted-imports\nimport type { ErrorHandlerOptions } from '../../unstable-core-do-not-import/procedure';\n// FIXME: fix lint rule, this is ok\n// eslint-disable-next-line no-restricted-imports\nimport type { CallerOverride } from '../../unstable-core-do-not-import/procedureBuilder';\n// FIXME: fix lint rule, this is ok\n// eslint-disable-next-line no-restricted-imports\nimport type {\n MaybePromise,\n Simplify,\n} from '../../unstable-core-do-not-import/types';\nimport { TRPCRedirectError } from './redirect';\nimport { rethrowNextErrors } from './rethrowNextErrors';\n\n/**\n * Create a caller that works with Next.js React Server Components & Server Actions\n */\nexport function nextAppDirCaller<TContext, TMeta>(\n config: Simplify<\n {\n /**\n * Extract the path from the procedure metadata\n */\n pathExtractor?: (opts: { meta: TMeta }) => string;\n /**\n * Transform form data to a `Record` before passing it to the procedure\n * @default true\n */\n normalizeFormData?: boolean;\n /**\n * Called when an error occurs in the handler\n */\n onError?: (opts: ErrorHandlerOptions<TContext>) => void;\n } & CreateContextCallback<TContext, () => MaybePromise<TContext>>\n >,\n): CallerOverride<TContext> {\n const {\n normalizeFormData = true,\n\n // rethrowNextErrors = true\n } = config;\n const createContext = async (): Promise<TContext> => {\n return config?.createContext?.() ?? ({} as TContext);\n };\n\n return async (opts) => {\n const path =\n config.pathExtractor?.({ meta: opts._def.meta as TMeta }) ?? '';\n const ctx: TContext = await createContext().catch((cause) => {\n const error = new TRPCError({\n code: 'INTERNAL_SERVER_ERROR',\n message: 'Failed to create context',\n cause,\n });\n\n throw error;\n });\n\n const handleError = (cause: unknown) => {\n const error = getTRPCErrorFromUnknown(cause);\n\n config.onError?.({\n ctx,\n error,\n input: opts.args[0],\n path,\n type: opts._def.type,\n });\n\n rethrowNextErrors(error);\n\n throw error;\n };\n switch (opts._def.type) {\n case 'mutation': {\n /**\n * When you wrap an action with useFormState, it gets an extra argument as its first argument.\n * The submitted form data is therefore its second argument instead of its first as it would usually be.\n * The new first argument that gets added is the current state of the form.\n * @see https://react.dev/reference/react-dom/hooks/useFormState#my-action-can-no-longer-read-the-submitted-form-data\n */\n let input = opts.args.length === 1 ? opts.args[0] : opts.args[1];\n if (normalizeFormData && input instanceof FormData) {\n input = formDataToObject(input);\n }\n\n return await opts\n .invoke({\n type: opts._def.type,\n ctx,\n getRawInput: async () => input,\n path,\n input,\n signal: undefined,\n batchIndex: 0,\n })\n .then((data) => {\n if (data instanceof TRPCRedirectError) throw data;\n return data;\n })\n .catch(handleError);\n }\n case 'query': {\n const input = opts.args[0];\n return await opts\n .invoke({\n type: opts._def.type,\n ctx,\n getRawInput: async () => input,\n path,\n input,\n signal: undefined,\n batchIndex: 0,\n })\n .then((data) => {\n if (data instanceof TRPCRedirectError) throw data;\n return data;\n })\n .catch(handleError);\n }\n case 'subscription':\n default: {\n throw new TRPCError({\n code: 'NOT_IMPLEMENTED',\n message: `Not implemented for type ${opts._def.type}`,\n });\n }\n }\n };\n}\n","import type { notFound as __notFound } from 'next/navigation';\nimport { TRPCError } from '../../@trpc/server';\n\n/**\n * Like `next/navigation`'s `notFound()` but throws a `TRPCError` that later will be handled by Next.js\n * @public\n */\nexport const notFound: typeof __notFound = () => {\n throw new TRPCError({\n code: 'NOT_FOUND',\n });\n};\n"],"mappings":"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"}