UNPKG

better-auth

Version:

The most comprehensive authentication framework for TypeScript.

179 lines (177 loc) • 6.46 kB
import { hasRequestState, runWithEndpointContext, runWithRequestState } from "@better-auth/core/context"; import { shouldPublishLog } from "@better-auth/core/env"; import { APIError, toResponse } from "better-call"; import { createDefu } from "defu"; //#region src/api/to-auth-endpoints.ts const defuReplaceArrays = createDefu((obj, key, value) => { if (Array.isArray(obj[key]) && Array.isArray(value)) { obj[key] = value; return true; } }); function toAuthEndpoints(endpoints, ctx) { const api = {}; for (const [key, endpoint] of Object.entries(endpoints)) { api[key] = async (context) => { const run = async () => { const authContext = await ctx; let internalContext = { ...context, context: { ...authContext, returned: void 0, responseHeaders: void 0, session: null }, path: endpoint.path, headers: context?.headers ? new Headers(context?.headers) : void 0 }; return runWithEndpointContext(internalContext, async () => { const { beforeHooks, afterHooks } = getHooks(authContext); const before = await runBeforeHooks(internalContext, beforeHooks); /** * If `before.context` is returned, it should * get merged with the original context */ if ("context" in before && before.context && typeof before.context === "object") { const { headers, ...rest } = before.context; /** * Headers should be merged differently * so the hook doesn't override the whole * header */ if (headers) headers.forEach((value, key$1) => { internalContext.headers.set(key$1, value); }); internalContext = defuReplaceArrays(rest, internalContext); } else if (before) return context?.asResponse ? toResponse(before, { headers: context?.headers }) : context?.returnHeaders ? { headers: context?.headers, response: before } : before; internalContext.asResponse = false; internalContext.returnHeaders = true; internalContext.returnStatus = true; const result = await runWithEndpointContext(internalContext, () => endpoint(internalContext)).catch((e) => { if (e instanceof APIError) /** * API Errors from response are caught * and returned to hooks */ return { response: e, status: e.statusCode, headers: e.headers ? new Headers(e.headers) : null }; throw e; }); if (result && result instanceof Response) return result; internalContext.context.returned = result.response; internalContext.context.responseHeaders = result.headers; const after = await runAfterHooks(internalContext, afterHooks); if (after.response) result.response = after.response; if (result.response instanceof APIError && shouldPublishLog(authContext.logger.level, "debug")) result.response.stack = result.response.errorStack; if (result.response instanceof APIError && !context?.asResponse) throw result.response; return context?.asResponse ? toResponse(result.response, { headers: result.headers, status: result.status }) : context?.returnHeaders ? context?.returnStatus ? { headers: result.headers, response: result.response, status: result.status } : { headers: result.headers, response: result.response } : context?.returnStatus ? { response: result.response, status: result.status } : result.response; }); }; if (await hasRequestState()) return run(); else return runWithRequestState(/* @__PURE__ */ new WeakMap(), run); }; api[key].path = endpoint.path; api[key].options = endpoint.options; } return api; } async function runBeforeHooks(context, hooks) { let modifiedContext = {}; for (const hook of hooks) if (hook.matcher(context)) { const result = await hook.handler({ ...context, returnHeaders: false }).catch((e) => { if (e instanceof APIError && shouldPublishLog(context.context.logger.level, "debug")) e.stack = e.errorStack; throw e; }); if (result && typeof result === "object") { if ("context" in result && typeof result.context === "object") { const { headers, ...rest } = result.context; if (headers instanceof Headers) if (modifiedContext.headers) headers.forEach((value, key) => { modifiedContext.headers?.set(key, value); }); else modifiedContext.headers = headers; modifiedContext = defuReplaceArrays(rest, modifiedContext); continue; } return result; } } return { context: modifiedContext }; } async function runAfterHooks(context, hooks) { for (const hook of hooks) if (hook.matcher(context)) { const result = await hook.handler(context).catch((e) => { if (e instanceof APIError) { if (shouldPublishLog(context.context.logger.level, "debug")) e.stack = e.errorStack; return { response: e, headers: e.headers ? new Headers(e.headers) : null }; } throw e; }); if (result.headers) result.headers.forEach((value, key) => { if (!context.context.responseHeaders) context.context.responseHeaders = new Headers({ [key]: value }); else if (key.toLowerCase() === "set-cookie") context.context.responseHeaders.append(key, value); else context.context.responseHeaders.set(key, value); }); if (result.response) context.context.returned = result.response; } return { response: context.context.returned, headers: context.context.responseHeaders }; } function getHooks(authContext) { const plugins = authContext.options.plugins || []; const beforeHooks = []; const afterHooks = []; if (authContext.options.hooks?.before) beforeHooks.push({ matcher: () => true, handler: authContext.options.hooks.before }); if (authContext.options.hooks?.after) afterHooks.push({ matcher: () => true, handler: authContext.options.hooks.after }); const pluginBeforeHooks = plugins.map((plugin) => { if (plugin.hooks?.before) return plugin.hooks.before; }).filter((plugin) => plugin !== void 0).flat(); const pluginAfterHooks = plugins.map((plugin) => { if (plugin.hooks?.after) return plugin.hooks.after; }).filter((plugin) => plugin !== void 0).flat(); /** * Add plugin added hooks at last */ if (pluginBeforeHooks.length) beforeHooks.push(...pluginBeforeHooks); if (pluginAfterHooks.length) afterHooks.push(...pluginAfterHooks); return { beforeHooks, afterHooks }; } //#endregion export { toAuthEndpoints }; //# sourceMappingURL=to-auth-endpoints.mjs.map