{"version":3,"file":"nest.cjs","names":["SetMetadata","GrantClient","_decorate","Global","Module","ForbiddenException","extractTokenFromRequest","UnauthorizedException","NotFoundException","_decorate","Injectable","_decorateParam","Inject","_decorateMetadata","GrantClient"],"sources":["../src/nest/grant.decorator.ts","../src/nest/grant.module.ts","../src/nest/grant.guard.ts"],"sourcesContent":["import { SetMetadata } from '@nestjs/common';\n\n/**\n * Metadata key for Grant options (resource, action) on route handlers.\n * Used by GrantGuard when options are not passed in the constructor.\n */\nexport const GRANT_OPTIONS_KEY = 'grant:options';\n\n/**\n * Options for the Grant guard (resource, action; resourceResolver only via guard constructor).\n */\nexport interface GrantOptions {\n  /** The resource slug to check (e.g., \"Organization\", \"Project\", \"Document\") */\n  resource: string;\n  /** The action to check (e.g., \"Query\", \"Create\", \"Update\", \"Delete\") */\n  action: string;\n}\n\n/**\n * Decorator to require a Grant permission on a route.\n * Use with GrantGuard (injected) so the guard reads resource/action from this metadata.\n *\n * @example\n * ```ts\n * @Get()\n * @Grant('Document', 'Query')\n * @UseGuards(GrantGuard)\n * list() { ... }\n *\n * @Post()\n * @Grant('Document', 'Create')\n * @UseGuards(GrantGuard)\n * create(@Body() dto: CreateDocumentDto) { ... }\n * ```\n *\n * For dynamic resource resolution (resourceResolver), use the guard with explicit options:\n * `@UseGuards(new GrantGuard(grantClient, { resource: 'Document', action: 'Update', resourceResolver: ... }))`\n */\nexport function Grant(resource: string, action: string): ReturnType<typeof SetMetadata>;\nexport function Grant(options: GrantOptions): ReturnType<typeof SetMetadata>;\nexport function Grant(\n  resourceOrOptions: string | GrantOptions,\n  action?: string\n): ReturnType<typeof SetMetadata> {\n  const options: GrantOptions =\n    typeof resourceOrOptions === 'string' && action !== undefined\n      ? { resource: resourceOrOptions, action }\n      : (resourceOrOptions as GrantOptions);\n  return SetMetadata(GRANT_OPTIONS_KEY, options);\n}\n","import { DynamicModule, Global, Module } from '@nestjs/common';\n\nimport { GrantClient } from '../grant-client';\nimport type { GrantServerConfig } from '../types';\n\nexport const GRANT_CLIENT = 'GrantClient';\n\n/**\n * NestJS module that provides GrantClient for injection.\n *\n * @example\n * ```ts\n * // app.module.ts\n * import { GrantModule } from '@grantjs/server/nest';\n *\n * @Module({\n *   imports: [\n *     GrantModule.forRoot({\n *       apiUrl: process.env.GRANT_API_URL!,\n *       getToken: (req) => req.headers?.authorization?.replace?.('Bearer ', '') ?? null,\n *     }),\n *   ],\n * })\n * export class AppModule {}\n * ```\n *\n * Then inject GrantClient in controllers and use with GrantGuard.\n */\n@Global()\n@Module({})\nexport class GrantModule {\n  static forRoot(config: GrantServerConfig): DynamicModule {\n    const client = new GrantClient(config);\n    return {\n      module: GrantModule,\n      providers: [\n        { provide: GrantClient, useValue: client },\n        { provide: GRANT_CLIENT, useValue: client },\n      ],\n      exports: [GrantClient, GRANT_CLIENT],\n    };\n  }\n}\n","import {\n  CanActivate,\n  ExecutionContext,\n  ForbiddenException,\n  Inject,\n  Injectable,\n  NotFoundException,\n  UnauthorizedException,\n} from '@nestjs/common';\nimport { Reflector } from '@nestjs/core';\n\nimport { GrantClient } from '../grant-client';\nimport { debugGrant } from '../utils/debug';\nimport { extractTokenFromRequest } from '../utils/token-extractor';\nimport { GRANT_OPTIONS_KEY } from './grant.decorator';\nimport { GRANT_CLIENT } from './grant.module';\n\n/** Injection token for GrantGuard. Use with @UseGuards(GRANT_GUARD) so Nest resolves the guard from the container (with GrantClient injected). */\nexport const GRANT_GUARD = 'GrantGuard';\n\nimport type { AuthorizationResult, ResourceResolver } from '../types';\nimport type { GrantOptions } from './grant.decorator';\n\n/**\n * Extended request type with Grant authorization result (for use in controllers).\n */\nexport interface AuthorizedRequest {\n  authorization?: AuthorizationResult;\n}\n\n/**\n * Guard options when using explicit options (includes optional resourceResolver).\n */\nexport interface GrantGuardOptions {\n  resource: string;\n  action: string;\n  resourceResolver?: ResourceResolver;\n}\n\n/**\n * NestJS guard that checks Grant authorization before allowing access to a route.\n *\n * Use in one of two ways:\n *\n * 1. With decorator (resource/action from metadata):\n *    - Import GrantModule.forRoot() in AppModule. In a feature module, add\n *      `{ provide: GRANT_GUARD, useClass: GrantGuard }` and use @Grant + @UseGuards(GRANT_GUARD).\n *    - Using the GRANT_GUARD token ensures Nest resolves the guard from the container (GrantClient injected).\n *\n * 2. With explicit options (supports resourceResolver):\n *    - @UseGuards(new GrantGuard(grantClient, { resource: 'Document', action: 'Update', resourceResolver: async ({ request }) => ... }))\n *\n * On success, the authorization result is attached to the request as request.authorization.\n * On failure, throws UnauthorizedException (401), ForbiddenException (403), or NotFoundException (404).\n */\nfunction isGrantGuardOptions(x: unknown): x is GrantGuardOptions {\n  return (\n    typeof x === 'object' &&\n    x !== null &&\n    'resource' in x &&\n    'action' in x &&\n    typeof (x as GrantGuardOptions).resource === 'string' &&\n    typeof (x as GrantGuardOptions).action === 'string'\n  );\n}\n\n@Injectable()\nexport class GrantGuard implements CanActivate {\n  private readonly explicitOptions?: GrantGuardOptions;\n  private readonly reflector?: Reflector;\n\n  constructor(\n    @Inject(GRANT_CLIENT) private readonly client: GrantClient,\n    reflectorOrOptions?: Reflector | GrantGuardOptions\n  ) {\n    if (reflectorOrOptions && isGrantGuardOptions(reflectorOrOptions)) {\n      this.explicitOptions = reflectorOrOptions;\n    } else {\n      this.reflector = reflectorOrOptions as Reflector | undefined;\n    }\n  }\n\n  private getOptionsFromContext(context: ExecutionContext): GrantOptions | undefined {\n    const handler = context.getHandler();\n    const clazz = context.getClass();\n    // Prefer Reflector when available (Nest DI)\n    if (this.reflector) {\n      const fromHandler = this.reflector.get<GrantOptions>(GRANT_OPTIONS_KEY, handler);\n      const fromClass = this.reflector.get<GrantOptions>(GRANT_OPTIONS_KEY, clazz);\n      if (fromHandler) return fromHandler;\n      if (fromClass) return fromClass;\n    }\n    // Fallback: read metadata via Reflect (works when Reflector is not injected, e.g. ESM/CJS boundary)\n    if (typeof Reflect !== 'undefined' && typeof Reflect.getMetadata === 'function') {\n      const fromHandler = Reflect.getMetadata(GRANT_OPTIONS_KEY, handler) as\n        GrantOptions | undefined;\n      const fromClass = Reflect.getMetadata(GRANT_OPTIONS_KEY, clazz) as GrantOptions | undefined;\n      if (fromHandler) return fromHandler;\n      if (fromClass) return fromClass;\n    }\n    return undefined;\n  }\n\n  async canActivate(context: ExecutionContext): Promise<boolean> {\n    const request = context.switchToHttp().getRequest();\n\n    const options = this.explicitOptions ?? this.getOptionsFromContext(context);\n\n    if (options?.resource && options?.action) {\n      debugGrant('Nest', {\n        resource: options.resource,\n        action: options.action,\n        hasReflector: !!this.reflector,\n        handler: context.getHandler()?.name,\n        controller: context.getClass()?.name,\n      });\n    }\n\n    if (!options || !options.resource || !options.action) {\n      throw new ForbiddenException(\n        'Grant options (resource, action) required. Use @Grant() or pass options to GrantGuard.'\n      );\n    }\n\n    if (!this.client) {\n      throw new ForbiddenException(\n        'GrantGuard: GrantClient not injected. Import GrantModule.forRoot() in AppModule and register GrantGuard as a provider.'\n      );\n    }\n\n    const token = await extractTokenFromRequest(request, this.client.config);\n    if (!token) {\n      throw new UnauthorizedException({ error: 'Unauthorized', code: 'UNAUTHENTICATED' });\n    }\n\n    let resolvedResource: Record<string, unknown> | null = null;\n\n    const opts = options as GrantGuardOptions;\n    if (opts.resourceResolver) {\n      resolvedResource = await opts.resourceResolver({\n        resourceSlug: options.resource,\n        request,\n      });\n      if (!resolvedResource) {\n        throw new NotFoundException({ error: 'Resource not found', code: 'NOT_FOUND' });\n      }\n    }\n\n    const result = await this.client.isAuthorized(\n      options.resource,\n      options.action,\n      {\n        context: {\n          resource: resolvedResource ?? undefined,\n        },\n      },\n      request\n    );\n\n    debugGrant('Nest', {\n      resource: options.resource,\n      action: options.action,\n      authorized: result.authorized,\n      ...(result.authorized ? {} : { reason: result.reason }),\n    });\n\n    if (!result.authorized) {\n      throw new ForbiddenException({\n        error: 'Forbidden',\n        code: 'FORBIDDEN',\n        reason: result.reason,\n      });\n    }\n\n    (request as AuthorizedRequest).authorization = result;\n    return true;\n  }\n}\n"],"mappings":"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"}