{"version":3,"sources":["../src/index.ts"],"sourcesContent":["import { NextFetchEvent, NextRequest, NextResponse } from 'next/server';\nimport {\n  AuthRules,\n  BaseOptions,\n  ExtractParams,\n  MiddlewareOptions,\n  ParamsObject,\n  Path,\n  RuleFunction,\n  Routes,\n  RedirectOptions,\n} from './types';\n\n/**\n * This module provides a clean way to protect your Next.js routes using the Next.js middleware pattern.\n * It allows you to protect your Next.js routes in a typesafe way with an easy-to-use API.\n *\n * @example\n * // Define the type of the data you want to perform the checks on\n * type Data = User\n *\n * // Import the necessary dependencies\n * import Middleware, { RuleFunction } from \"@triyanox/next-middleware\";\n * import { routes } from \"./lib/link$\";\n *\n * // Define your route protection rules\n * const isLoggedIn: RuleFunction<Data, typeof routes> = ({ data, next, redirect, path }) => {...};\n * const isNotLoggedIn: RuleFunction<Data, typeof routes> = async ({ data, next, redirect }) => {...};\n * const isAdmin: RuleFunction<Data, typeof routes> = ({ data, next, redirect, path }) => {...};\n *\n * // Construct the middleware and perform the checks\n * const middleware = new Middleware<typeof routes, Data>({...});\n *\n * // Export the middleware from your `middleware.ts` file in your next.js project\n * export default middleware.handle.bind(middleware);\n * export const config = {...};\n *\n * @see {@link https://github.com/triyanox/next-middleware|GitHub}\n */ class Middleware<\n  R extends Record<\n    string,\n    {\n      path: string;\n    }\n  >,\n  T = any,\n> {\n  private fetch: (req: NextRequest) => Promise<T | undefined>;\n  private rules: AuthRules<T, R, keyof R & string>;\n  private authPaths: Array<keyof R & string>;\n  private onError?: (req: NextRequest) => Promise<NextResponse> | NextResponse;\n\n  constructor(options: MiddlewareOptions<R, T>) {\n    this.fetch = options.fetch;\n    this.rules = options.rules;\n    this.authPaths = options.authPaths;\n    this.onError = options.onError;\n  }\n\n  private extractParams<R extends string>(\n    path: R,\n    pathname: string,\n  ): ParamsObject<R> {\n    const pathParts = path.split('/');\n    const pathnameParts = pathname.split('/');\n\n    return pathParts.reduce((params, part, index) => {\n      if (part.startsWith('[') && part.endsWith(']')) {\n        const key = part.slice(1, -1) as ExtractParams<R>[number];\n        return {\n          ...params,\n          [key]: pathnameParts[index],\n        };\n      }\n      return params;\n    }, {} as ParamsObject<R>);\n  }\n\n  private isWildcard<R extends string>(path: R): boolean {\n    return path.endsWith('/*');\n  }\n\n  private match<R extends string>(path: R, pathname: string): boolean {\n    const pathParts = path.split('/');\n    const pathnameParts = pathname.split('/');\n    return pathParts.every((part, index) => {\n      if (part === '*') {\n        return true;\n      }\n      if (part.startsWith('[') && part.endsWith(']')) {\n        return true;\n      }\n      return part === pathnameParts[index];\n    });\n  }\n\n  private sortPaths(paths: Array<keyof R & string>): Array<keyof R & string> {\n    return paths.sort((a, b) => {\n      if (this.isWildcard(a) && !this.isWildcard(b)) {\n        return 1;\n      }\n      return -1;\n    });\n  }\n\n  private async apply(req: NextRequest, pathname: string, data: T) {\n    const allPaths = Object.keys(this.rules);\n    const matchedPaths = allPaths.filter((path) => this.match(path, pathname));\n    const sortedPaths = this.sortPaths(matchedPaths);\n\n    for (const path of sortedPaths) {\n      const rules = this.rules[path] || [];\n      const params = this.extractParams(path, pathname);\n\n      let nextCalled = false;\n      let redirectPath: (keyof R & string) | undefined;\n\n      const next = async () => {\n        nextCalled = true;\n        return undefined;\n      };\n      const redirect = (\n        path: keyof R & string,\n        options?: RedirectOptions<keyof R & string>,\n      ) => {\n        redirectPath = path;\n        if (options) {\n          if ('params' in options) {\n            const params = options.params;\n            const pathParts = path.split('/');\n            const newPathParts = pathParts.map((part) => {\n              if (part.startsWith('[') && part.endsWith(']')) {\n                const key = part.slice(1, -1);\n                return params[key as keyof typeof params];\n              }\n              return part;\n            });\n            const newPath = newPathParts.join('/');\n            redirectPath = newPath;\n          }\n          if (options.query) {\n            const searchParams = new URLSearchParams(options.query);\n            redirectPath += `?${searchParams.toString()}`;\n          }\n          if (options.hash) {\n            redirectPath += `#${options.hash}`;\n          }\n        }\n      };\n\n      for (const rule of rules) {\n        await rule({\n          data,\n          params,\n          next,\n          redirect,\n          path: new URL(req.url).pathname,\n        });\n        if (redirectPath) {\n          break;\n        }\n        if (nextCalled) {\n          continue;\n        }\n      }\n\n      if (!nextCalled && !redirectPath) {\n        return NextResponse.next();\n      }\n      if (redirectPath && redirectPath !== new URL(req.url).pathname) {\n        return NextResponse.redirect(new URL(redirectPath, req.url));\n      }\n\n      return NextResponse.next();\n    }\n  }\n\n  /**\n   * The default handler for the middleware\n   */\n  async handle(req: NextRequest, ev: NextFetchEvent) {\n    const pathname = new URL(req.url).pathname;\n    if (\n      this.authPaths.some((path) => {\n        const pathParts = path.split('/');\n        const pathnameParts = pathname.split('/');\n        return pathParts.every((part, index) => {\n          if (part === '*') {\n            return true;\n          }\n          if (part.startsWith('[') && part.endsWith(']')) {\n            return true;\n          }\n          return part === pathnameParts[index];\n        });\n      })\n    ) {\n      try {\n        const data = await this.fetch(req);\n        if (!data) {\n          throw 'Missing data from `fetch`';\n        }\n        return await this.apply(req, pathname, data);\n      } catch (e) {\n        if (this.onError) {\n          return await this.onError(req);\n        }\n      }\n    } else {\n      return NextResponse.next();\n    }\n  }\n}\n\nexport default Middleware;\nexport { Middleware };\nexport type {\n  AuthRules,\n  BaseOptions,\n  ExtractParams,\n  MiddlewareOptions,\n  ParamsObject,\n  Path,\n  RuleFunction,\n  Routes,\n};\n"],"mappings":"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","names":["src_exports","__export","Middleware","src_default","__toCommonJS","import_server","options","path","pathname","pathParts","pathnameParts","params","part","index","key","paths","a","b","req","data","matchedPaths","sortedPaths","rules","nextCalled","redirectPath","next","redirect","searchParams","rule","ev"]}