{"version":3,"file":"route-guard.mjs","sources":["../../../../src/lib/routing/guards/route-guard.ts"],"sourcesContent":["/**\n * @file Base Route Guard Interface and Types\n * @description Defines the core route guard interface and types for implementing\n * navigation guards. Inspired by Angular route guards with React adaptations.\n *\n * @module @/lib/routing/guards/route-guard\n *\n * This module provides:\n * - Base guard interface definition\n * - Guard result types\n * - Guard context structure\n * - Guard execution utilities\n * - Type-safe guard creation\n *\n * @example\n * ```typescript\n * import { RouteGuard, createGuard, GuardResult } from '@/lib/routing/guards/route-guard';\n *\n * const myGuard = createGuard({\n *   name: 'myGuard',\n *   canActivate: async (context) => {\n *     if (someCondition) {\n *       return GuardResult.allow();\n *     }\n *     return GuardResult.redirect('/login');\n *   },\n * });\n * ```\n */\n\n// =============================================================================\n// Types\n// =============================================================================\n\n/**\n * Guard execution timing\n */\nexport type GuardTiming =\n  | 'canActivate'      // Before route activation\n  | 'canDeactivate'    // Before leaving route\n  | 'canMatch'         // Before route matching\n  | 'canLoad';         // Before lazy loading\n\n/**\n * Guard result type\n */\nexport type GuardResultType =\n  | 'allow'      // Allow navigation\n  | 'deny'       // Block navigation\n  | 'redirect'   // Redirect to another route\n  | 'pending';   // Guard is still processing\n\n/**\n * Guard result object\n */\nexport interface GuardResultObject {\n  /** Result type */\n  readonly type: GuardResultType;\n  /** Redirect path (if type is 'redirect') */\n  readonly redirectTo?: string;\n  /** Redirect options */\n  readonly redirectOptions?: RedirectOptions;\n  /** Denial reason (if type is 'deny') */\n  readonly reason?: string;\n  /** Additional data */\n  readonly data?: Record<string, unknown>;\n}\n\n/**\n * Redirect options\n */\nexport interface RedirectOptions {\n  /** Replace current history entry */\n  readonly replace?: boolean;\n  /** State to pass to redirect target */\n  readonly state?: unknown;\n  /** Query parameters */\n  readonly query?: Record<string, string>;\n  /** Preserve current query parameters */\n  readonly preserveQuery?: boolean;\n}\n\n/**\n * Guard context passed to guard functions\n */\nexport interface GuardContext {\n  /** Target route path */\n  readonly path: string;\n  /** Route parameters */\n  readonly params: Record<string, string>;\n  /** Query parameters */\n  readonly query: Record<string, string>;\n  /** Current route (for canDeactivate) */\n  readonly currentRoute?: string;\n  /** Next route (for canDeactivate) */\n  readonly nextRoute?: string;\n  /** User context */\n  readonly user?: GuardUser;\n  /** Feature flags */\n  readonly features?: Record<string, boolean>;\n  /** Custom data from route or parent guards */\n  readonly data: Record<string, unknown>;\n  /** Navigation state */\n  readonly state?: unknown;\n  /** Whether this is initial load */\n  readonly isInitialLoad: boolean;\n  /** Parent route context (for nested routes) */\n  readonly parent?: GuardContext;\n}\n\n/**\n * User context for guards\n */\nexport interface GuardUser {\n  /** User ID */\n  readonly id: string;\n  /** Whether user is authenticated */\n  readonly isAuthenticated: boolean;\n  /** User roles */\n  readonly roles: readonly string[];\n  /** User permissions */\n  readonly permissions: readonly string[];\n  /** User metadata */\n  readonly metadata?: Record<string, unknown>;\n}\n\n/**\n * Guard function signature\n */\nexport type GuardFunction = (\n  context: GuardContext\n) => GuardResultObject | Promise<GuardResultObject>;\n\n/**\n * Guard configuration\n */\nexport interface GuardConfig {\n  /** Unique guard name */\n  readonly name: string;\n  /** Guard description */\n  readonly description?: string;\n  /** Guard timing */\n  readonly timing?: GuardTiming;\n  /** Guard priority (lower = runs first) */\n  readonly priority?: number;\n  /** Whether guard is async */\n  readonly async?: boolean;\n  /** Timeout in milliseconds */\n  readonly timeout?: number;\n  /** Routes this guard applies to (glob patterns) */\n  readonly routes?: readonly string[];\n  /** Routes to exclude (glob patterns) */\n  readonly exclude?: readonly string[];\n  /** Whether guard is enabled */\n  readonly enabled?: boolean;\n  /** Feature flag for this guard */\n  readonly featureFlag?: string;\n  /** canActivate implementation */\n  readonly canActivate?: GuardFunction;\n  /** canDeactivate implementation */\n  readonly canDeactivate?: GuardFunction;\n  /** canMatch implementation */\n  readonly canMatch?: GuardFunction;\n  /** canLoad implementation */\n  readonly canLoad?: GuardFunction;\n}\n\n/**\n * Route guard interface\n */\nexport interface RouteGuard extends GuardConfig {\n  /** Execute the guard */\n  execute: (timing: GuardTiming, context: GuardContext) => Promise<GuardResultObject>;\n  /** Check if guard applies to a route */\n  appliesTo: (path: string) => boolean;\n}\n\n/**\n * Guard execution result\n */\nexport interface GuardExecutionResult {\n  /** Guard name */\n  readonly guardName: string;\n  /** Result from guard */\n  readonly result: GuardResultObject;\n  /** Execution duration (ms) */\n  readonly durationMs: number;\n  /** Whether guard timed out */\n  readonly timedOut: boolean;\n  /** Error if guard threw */\n  readonly error?: Error;\n}\n\n// =============================================================================\n// GuardResult Helper Class\n// =============================================================================\n\n/**\n * Helper class for creating guard results\n *\n * @example\n * ```typescript\n * // Allow navigation\n * return GuardResult.allow();\n *\n * // Deny with reason\n * return GuardResult.deny('Insufficient permissions');\n *\n * // Redirect\n * return GuardResult.redirect('/login', { replace: true });\n * ```\n */\nexport class GuardResult {\n  /**\n   * Allow navigation\n   *\n   * @param data - Optional data to pass along\n   * @returns Allow result\n   */\n  static allow(data?: Record<string, unknown>): GuardResultObject {\n    return {\n      type: 'allow',\n      data,\n    };\n  }\n\n  /**\n   * Deny navigation\n   *\n   * @param reason - Reason for denial\n   * @param data - Optional data\n   * @returns Deny result\n   */\n  static deny(reason?: string, data?: Record<string, unknown>): GuardResultObject {\n    return {\n      type: 'deny',\n      reason,\n      data,\n    };\n  }\n\n  /**\n   * Redirect to another route\n   *\n   * @param path - Path to redirect to\n   * @param options - Redirect options\n   * @returns Redirect result\n   */\n  static redirect(path: string, options?: RedirectOptions): GuardResultObject {\n    return {\n      type: 'redirect',\n      redirectTo: path,\n      redirectOptions: options,\n    };\n  }\n\n  /**\n   * Pending result (guard is still processing)\n   *\n   * @returns Pending result\n   */\n  static pending(): GuardResultObject {\n    return {\n      type: 'pending',\n    };\n  }\n\n  /**\n   * Create result from boolean\n   *\n   * @param allowed - Whether navigation is allowed\n   * @param denyReason - Reason if denied\n   * @returns Result based on boolean\n   */\n  static fromBoolean(allowed: boolean, denyReason?: string): GuardResultObject {\n    return allowed\n      ? GuardResult.allow()\n      : GuardResult.deny(denyReason ?? 'Access denied');\n  }\n\n  /**\n   * Check if result allows navigation\n   *\n   * @param result - Result to check\n   * @returns True if result allows navigation\n   */\n  static isAllowed(result: GuardResultObject): boolean {\n    return result.type === 'allow';\n  }\n\n  /**\n   * Check if result denies navigation\n   *\n   * @param result - Result to check\n   * @returns True if result denies navigation\n   */\n  static isDenied(result: GuardResultObject): boolean {\n    return result.type === 'deny';\n  }\n\n  /**\n   * Check if result is a redirect\n   *\n   * @param result - Result to check\n   * @returns True if result is a redirect\n   */\n  static isRedirect(result: GuardResultObject): boolean {\n    return result.type === 'redirect';\n  }\n}\n\n// =============================================================================\n// BaseRouteGuard Class\n// =============================================================================\n\n/**\n * Base class for route guards\n *\n * @example\n * ```typescript\n * class MyGuard extends BaseRouteGuard {\n *   constructor() {\n *     super({\n *       name: 'myGuard',\n *       canActivate: async (ctx) => {\n *         return GuardResult.allow();\n *       },\n *     });\n *   }\n * }\n * ```\n */\nexport class BaseRouteGuard implements RouteGuard {\n  readonly name: string;\n  readonly description?: string;\n  readonly timing?: GuardTiming;\n  readonly priority: number;\n  readonly async: boolean;\n  readonly timeout: number;\n  readonly routes?: readonly string[];\n  readonly exclude?: readonly string[];\n  readonly enabled: boolean;\n  readonly featureFlag?: string;\n  readonly canActivate?: GuardFunction;\n  readonly canDeactivate?: GuardFunction;\n  readonly canMatch?: GuardFunction;\n  readonly canLoad?: GuardFunction;\n\n  constructor(config: GuardConfig) {\n    this.name = config.name;\n    this.description = config.description;\n    this.timing = config.timing;\n    this.priority = config.priority ?? 100;\n    this.async = config.async ?? true;\n    this.timeout = config.timeout ?? 30000;\n    this.routes = config.routes;\n    this.exclude = config.exclude;\n    this.enabled = config.enabled ?? true;\n    this.featureFlag = config.featureFlag;\n    this.canActivate = config.canActivate;\n    this.canDeactivate = config.canDeactivate;\n    this.canMatch = config.canMatch;\n    this.canLoad = config.canLoad;\n  }\n\n  /**\n   * Execute the guard for a specific timing\n   */\n  async execute(timing: GuardTiming, context: GuardContext): Promise<GuardResultObject> {\n    if (!this.enabled) {\n      return GuardResult.allow();\n    }\n\n    if (!this.appliesTo(context.path)) {\n      return GuardResult.allow();\n    }\n\n    const handler = this.getHandler(timing);\n    if (!handler) {\n      return GuardResult.allow();\n    }\n\n    try {\n      const result = await this.executeWithTimeout(handler, context);\n      return result;\n    } catch (error) {\n      console.error(`Guard \"${this.name}\" threw an error:`, error);\n      return GuardResult.deny(`Guard error: ${(error as Error).message}`);\n    }\n  }\n\n  /**\n   * Check if guard applies to a route\n   */\n  appliesTo(path: string): boolean {\n    // Check exclusions first\n    if (this.exclude) {\n      for (const pattern of this.exclude) {\n        if (this.matchPattern(pattern, path)) {\n          return false;\n        }\n      }\n    }\n\n    // Check inclusions\n    if (this.routes && this.routes.length > 0) {\n      for (const pattern of this.routes) {\n        if (this.matchPattern(pattern, path)) {\n          return true;\n        }\n      }\n      return false;\n    }\n\n    // No route restrictions = applies to all\n    return true;\n  }\n\n  /**\n   * Get the handler for a timing\n   */\n  private getHandler(timing: GuardTiming): GuardFunction | undefined {\n    switch (timing) {\n      case 'canActivate':\n        return this.canActivate;\n      case 'canDeactivate':\n        return this.canDeactivate;\n      case 'canMatch':\n        return this.canMatch;\n      case 'canLoad':\n        return this.canLoad;\n      default:\n        return undefined;\n    }\n  }\n\n  /**\n   * Execute handler with timeout\n   */\n  private async executeWithTimeout(\n    handler: GuardFunction,\n    context: GuardContext\n  ): Promise<GuardResultObject> {\n    return new Promise((resolve, reject) => {\n      const timer = setTimeout(() => {\n        reject(new Error(`Guard \"${this.name}\" timed out after ${this.timeout}ms`));\n      }, this.timeout);\n\n      Promise.resolve(handler(context))\n        .then((result) => {\n          clearTimeout(timer);\n          resolve(result);\n        })\n        .catch((error: unknown) => {\n          clearTimeout(timer);\n          reject(error instanceof Error ? error : new Error(String(error)));\n        });\n    });\n  }\n\n  /**\n   * Match a glob pattern against a path\n   */\n  private matchPattern(pattern: string, path: string): boolean {\n    const regexStr = pattern\n      .replace(/[.+^${}()|[\\]\\\\]/g, '\\\\$&')\n      .replace(/\\*\\*/g, '{{GLOBSTAR}}')\n      .replace(/\\*/g, '[^/]*')\n      .replace(/\\{\\{GLOBSTAR\\}\\}/g, '.*');\n\n    const regex = new RegExp(`^${regexStr}$`);\n    return regex.test(path);\n  }\n}\n\n// =============================================================================\n// Factory Functions\n// =============================================================================\n\n/**\n * Create a route guard\n *\n * @param config - Guard configuration\n * @returns Route guard instance\n */\nexport function createGuard(config: GuardConfig): RouteGuard {\n  return new BaseRouteGuard(config);\n}\n\n/**\n * Create a guard context\n *\n * @param options - Context options\n * @returns Guard context\n */\nexport function createGuardContext(\n  options: Partial<GuardContext> & { path: string }\n): GuardContext {\n  return {\n    path: options.path,\n    params: options.params ?? {},\n    query: options.query ?? {},\n    currentRoute: options.currentRoute,\n    nextRoute: options.nextRoute,\n    user: options.user,\n    features: options.features,\n    data: options.data ?? {},\n    state: options.state,\n    isInitialLoad: options.isInitialLoad ?? false,\n    parent: options.parent,\n  };\n}\n\n// =============================================================================\n// Utility Functions\n// =============================================================================\n\n/**\n * Merge guard results (returns first non-allow result)\n *\n * @param results - Array of guard results\n * @returns Merged result\n */\nexport function mergeGuardResults(results: readonly GuardResultObject[]): GuardResultObject {\n  for (const result of results) {\n    if (result.type !== 'allow') {\n      return result;\n    }\n  }\n  return GuardResult.allow();\n}\n\n/**\n * Check if all results allow navigation\n *\n * @param results - Array of guard results\n * @returns True if all allow\n */\nexport function allResultsAllow(results: readonly GuardResultObject[]): boolean {\n  return results.every(r => r.type === 'allow');\n}\n\n/**\n * Get the first redirect result\n *\n * @param results - Array of guard results\n * @returns First redirect result or undefined\n */\nexport function getFirstRedirect(results: readonly GuardResultObject[]): GuardResultObject | undefined {\n  return results.find(r => r.type === 'redirect');\n}\n\n/**\n * Get all denial reasons\n *\n * @param results - Array of guard results\n * @returns Array of denial reasons\n */\nexport function getDenialReasons(results: readonly GuardResultObject[]): string[] {\n  return results\n    .filter(r => r.type === 'deny' && (r.reason !== null && r.reason !== undefined && r.reason !== ''))\n    .map(r => r.reason ?? '');\n}\n\n// =============================================================================\n// Type Guards\n// =============================================================================\n\n/**\n * Type guard for GuardResultObject\n */\nexport function isGuardResult(value: unknown): value is GuardResultObject {\n  return (\n    typeof value === 'object' &&\n    value !== null &&\n    'type' in value &&\n    ['allow', 'deny', 'redirect', 'pending'].includes((value as GuardResultObject).type)\n  );\n}\n\n/**\n * Type guard for RouteGuard\n */\nexport function isRouteGuard(value: unknown): value is RouteGuard {\n  return (\n    typeof value === 'object' &&\n    value !== null &&\n    'name' in value &&\n    'execute' in value &&\n    'appliesTo' in value\n  );\n}\n\n/**\n * Type guard for GuardContext\n */\nexport function isGuardContext(value: unknown): value is GuardContext {\n  return (\n    typeof value === 'object' &&\n    value !== null &&\n    'path' in value &&\n    'params' in value &&\n    'data' in value\n  );\n}\n"],"names":["GuardResult","data","reason","path","options","allowed","denyReason","result","BaseRouteGuard","config","timing","context","handler","error","pattern","resolve","reject","timer","regexStr","createGuardContext","mergeGuardResults","results","getFirstRedirect","r","getDenialReasons"],"mappings":"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"}