{"version":3,"file":"parallel-routes.mjs","sources":["../../../../src/lib/routing/advanced/parallel-routes.ts"],"sourcesContent":["/**\n * @file Parallel Routes Support\n * @description Implements parallel route loading patterns inspired by Next.js App Router.\n * Enables simultaneous loading of multiple route segments with independent loading states.\n *\n * @module @/lib/routing/advanced/parallel-routes\n *\n * This module provides:\n * - Parallel route slot definitions\n * - Simultaneous route segment loading\n * - Independent loading/error states per slot\n * - Slot composition and rendering\n * - Named slot routing conventions\n *\n * @example\n * ```typescript\n * import { createParallelRoutes, ParallelRouteSlot } from '@/lib/routing/advanced/parallel-routes';\n *\n * const parallel = createParallelRoutes({\n *   slots: {\n *     main: { path: '/dashboard', component: DashboardPage },\n *     sidebar: { path: '@sidebar', component: SidebarPanel },\n *     modal: { path: '@modal', component: ModalContainer },\n *   },\n * });\n * ```\n */\n\nimport type { ComponentType, ReactNode } from 'react';\nimport {\n  parsePathParams as coreParsePathParams,\n  getPatternSpecificity as coreGetPatternSpecificity,\n} from '../core/path-utils';\n\n// =============================================================================\n// Types\n// =============================================================================\n\n/**\n * Parallel route slot configuration\n */\nexport interface ParallelRouteSlot<TProps = unknown> {\n  /** Slot identifier (e.g., '@modal', '@sidebar') */\n  readonly name: string;\n  /** Path pattern for this slot (can include params) */\n  readonly path: string;\n  /** Component to render in this slot */\n  readonly component: ComponentType<TProps>;\n  /** Loading component for this slot */\n  readonly loading?: ComponentType;\n  /** Error component for this slot */\n  readonly error?: ComponentType<{ error: Error; reset: () => void }>;\n  /** Default component when slot is inactive */\n  readonly default?: ComponentType;\n  /** Whether this slot is optional */\n  readonly optional?: boolean;\n  /** Custom slot metadata */\n  readonly meta?: Record<string, unknown>;\n  /** Feature flag for this slot */\n  readonly featureFlag?: string;\n}\n\n/**\n * Parallel routes configuration\n */\nexport interface ParallelRoutesConfig {\n  /** Named slots for parallel rendering */\n  readonly slots: Record<string, Omit<ParallelRouteSlot, 'name'>>;\n  /** Default slot to use if none specified */\n  readonly defaultSlot?: string;\n  /** Layout component that wraps all slots */\n  readonly layout?: ComponentType<{ children: ReactNode; slots: Record<string, ReactNode> }>;\n  /** Enable slot-level code splitting */\n  readonly codeSplit?: boolean;\n  /** Feature flag for parallel routes */\n  readonly featureFlag?: string;\n}\n\n/**\n * Slot render state\n */\nexport interface SlotRenderState {\n  /** Whether slot is loading */\n  readonly isLoading: boolean;\n  /** Whether slot has error */\n  readonly hasError: boolean;\n  /** Error if any */\n  readonly error: Error | null;\n  /** Whether slot is active */\n  readonly isActive: boolean;\n  /** Current render content */\n  readonly content: ReactNode | null;\n}\n\n/**\n * Parallel routes render context\n */\nexport interface ParallelRoutesContext {\n  /** All slot states */\n  readonly slots: ReadonlyMap<string, SlotRenderState>;\n  /** Active slot names */\n  readonly activeSlots: readonly string[];\n  /** Refresh a specific slot */\n  readonly refreshSlot: (slotName: string) => Promise<void>;\n  /** Set slot active/inactive */\n  readonly setSlotActive: (slotName: string, active: boolean) => void;\n  /** Get slot render content */\n  readonly getSlotContent: (slotName: string) => ReactNode | null;\n}\n\n/**\n * Slot match result\n */\nexport interface SlotMatch {\n  /** Matched slot */\n  readonly slot: ParallelRouteSlot;\n  /** Extracted parameters */\n  readonly params: Record<string, string>;\n  /** Full matched path */\n  readonly path: string;\n  /** Match score (for sorting) */\n  readonly score: number;\n}\n\n/**\n * Parallel route resolution result\n */\nexport interface ParallelRouteResolution {\n  /** Matched slots */\n  readonly matches: ReadonlyMap<string, SlotMatch>;\n  /** Unmatched slot names */\n  readonly unmatched: readonly string[];\n  /** Resolution timestamp */\n  readonly timestamp: number;\n}\n\n// =============================================================================\n// Constants\n// =============================================================================\n\n/**\n * Slot name prefix for parallel routes\n */\nexport const SLOT_PREFIX = '@';\n\n/**\n * Default slot name\n */\nexport const DEFAULT_SLOT_NAME = 'children';\n\n// =============================================================================\n// ParallelRoutes Class\n// =============================================================================\n\n/**\n * Manages parallel route slot rendering\n *\n * @example\n * ```typescript\n * const parallel = new ParallelRoutes({\n *   slots: {\n *     main: { path: '/', component: MainPage },\n *     sidebar: { path: '@sidebar', component: Sidebar },\n *   },\n * });\n *\n * const matches = parallel.resolveSlots('/dashboard');\n * ```\n */\nexport class ParallelRoutes {\n  private readonly config: ParallelRoutesConfig;\n  private readonly slots: Map<string, ParallelRouteSlot>;\n  private readonly slotStates: Map<string, SlotRenderState>;\n\n  constructor(config: ParallelRoutesConfig) {\n    this.config = config;\n    this.slots = new Map();\n    this.slotStates = new Map();\n\n    // Initialize slots\n    for (const [name, slotConfig] of Object.entries(config.slots)) {\n      const slot: ParallelRouteSlot = {\n        ...slotConfig,\n        name,\n      };\n      this.slots.set(name, slot);\n      this.slotStates.set(name, {\n        isLoading: false,\n        hasError: false,\n        error: null,\n        isActive: false,\n        content: null,\n      });\n    }\n  }\n\n  /**\n   * Get all slot configurations\n   */\n  getSlots(): ReadonlyMap<string, ParallelRouteSlot> {\n    return this.slots;\n  }\n\n  /**\n   * Get a specific slot configuration\n   */\n  getSlot(name: string): ParallelRouteSlot | undefined {\n    return this.slots.get(name);\n  }\n\n  /**\n   * Get slot state\n   */\n  getSlotState(name: string): SlotRenderState | undefined {\n    return this.slotStates.get(name);\n  }\n\n  /**\n   * Resolve which slots match a given path\n   *\n   * @param path - Current URL path\n   * @returns Resolution result with matched slots\n   */\n  resolveSlots(path: string): ParallelRouteResolution {\n    const matches = new Map<string, SlotMatch>();\n    const unmatched: string[] = [];\n\n    for (const [name, slot] of this.slots) {\n      const match = this.matchSlot(slot, path);\n      if (match) {\n        matches.set(name, match);\n      } else if (slot.optional !== true) {\n        unmatched.push(name);\n      }\n    }\n\n    return {\n      matches,\n      unmatched,\n      timestamp: Date.now(),\n    };\n  }\n\n  /**\n   * Update slot state\n   */\n  updateSlotState(name: string, update: Partial<SlotRenderState>): void {\n    const current = this.slotStates.get(name);\n    if (current) {\n      this.slotStates.set(name, { ...current, ...update });\n    }\n  }\n\n  /**\n   * Create render context for use in components\n   */\n  createContext(): ParallelRoutesContext {\n    return {\n      slots: this.slotStates,\n      activeSlots: Array.from(this.slotStates.entries())\n        .filter(([_, state]) => state.isActive)\n        .map(([name]) => name),\n      refreshSlot: async (slotName: string) => {\n        this.updateSlotState(slotName, { isLoading: true, hasError: false, error: null });\n        // Actual refresh logic would be implemented by the consumer\n      },\n      setSlotActive: (slotName: string, active: boolean) => {\n        this.updateSlotState(slotName, { isActive: active });\n      },\n      getSlotContent: async (slotName: string) => {\n        return this.slotStates.get(slotName)?.content ?? null;\n      },\n    };\n  }\n\n  /**\n   * Get slot names that should render for a given path\n   */\n  getActiveSlotsForPath(path: string): string[] {\n    const resolution = this.resolveSlots(path);\n    return Array.from(resolution.matches.keys());\n  }\n\n  /**\n   * Check if a slot is configured\n   */\n  hasSlot(name: string): boolean {\n    return this.slots.has(name);\n  }\n\n  /**\n   * Get the default slot\n   */\n  getDefaultSlot(): ParallelRouteSlot | undefined {\n    const defaultName = this.config.defaultSlot ?? DEFAULT_SLOT_NAME;\n    return this.slots.get(defaultName);\n  }\n\n  /**\n   * Get layout component\n   */\n  getLayout():\n    | ComponentType<{ children: ReactNode; slots: Record<string, ReactNode> }>\n    | undefined {\n    return this.config.layout;\n  }\n\n  /**\n   * Match a single slot against a path\n   */\n  private matchSlot(slot: ParallelRouteSlot, path: string): SlotMatch | null {\n    // Parallel slots (starting with @) match against slot-specific paths\n    if (slot.path.startsWith(SLOT_PREFIX)) {\n      // Check if path includes the slot indicator\n      const slotPath = slot.path.slice(1); // Remove @\n      if (path.includes(`/${SLOT_PREFIX}${slotPath}`)) {\n        return {\n          slot,\n          params: {},\n          path: slot.path,\n          score: this.calculateMatchScore(slot.path, path),\n        };\n      }\n      return null;\n    }\n\n    // Regular path matching with parameter extraction\n    const params = this.extractParams(slot.path, path);\n    if (params !== null) {\n      return {\n        slot,\n        params,\n        path,\n        score: this.calculateMatchScore(slot.path, path),\n      };\n    }\n\n    return null;\n  }\n\n  /**\n   * Extract parameters from a path given a pattern\n   *\n   * Delegates to core path utilities.\n   */\n  private extractParams(pattern: string, path: string): Record<string, string> | null {\n    return coreParsePathParams(pattern, path);\n  }\n\n  /**\n   * Calculate match score for sorting\n   *\n   * Delegates to core pattern specificity calculation.\n   */\n  private calculateMatchScore(pattern: string, _path: string): number {\n    return coreGetPatternSpecificity(pattern);\n  }\n}\n\n// =============================================================================\n// Factory Functions\n// =============================================================================\n\n/**\n * Create a new ParallelRoutes instance\n *\n * @param config - Parallel routes configuration\n * @returns Configured ParallelRoutes instance\n */\nexport function createParallelRoutes(config: ParallelRoutesConfig): ParallelRoutes {\n  return new ParallelRoutes(config);\n}\n\n/**\n * Create a single slot configuration\n *\n * @param name - Slot name\n * @param config - Slot configuration\n * @returns Complete slot configuration\n */\nexport function createSlot<TProps = unknown>(\n  name: string,\n  config: Omit<ParallelRouteSlot<TProps>, 'name'>\n): ParallelRouteSlot<TProps> {\n  return {\n    ...config,\n    name,\n  };\n}\n\n// =============================================================================\n// Utility Functions\n// =============================================================================\n\n/**\n * Check if a path segment is a slot reference\n *\n * @param segment - Path segment to check\n * @returns True if segment is a slot reference\n */\nexport function isSlotSegment(segment: string): boolean {\n  return segment.startsWith(SLOT_PREFIX);\n}\n\n/**\n * Extract slot name from a segment\n *\n * @param segment - Slot segment (e.g., '@modal')\n * @returns Slot name (e.g., 'modal')\n */\nexport function extractSlotName(segment: string): string {\n  return segment.startsWith(SLOT_PREFIX) ? segment.slice(1) : segment;\n}\n\n/**\n * Create a slot path from a slot name\n *\n * @param name - Slot name\n * @returns Slot path (e.g., '@modal')\n */\nexport function createSlotPath(name: string): string {\n  return name.startsWith(SLOT_PREFIX) ? name : `${SLOT_PREFIX}${name}`;\n}\n\n/**\n * Merge slot states for composite rendering\n *\n * @param states - Array of slot states\n * @returns Merged loading/error state\n */\nexport function mergeSlotStates(states: readonly SlotRenderState[]): {\n  isAnyLoading: boolean;\n  isAllLoading: boolean;\n  hasAnyError: boolean;\n  errors: readonly Error[];\n  isAllActive: boolean;\n} {\n  const errors = states.filter((s): s is typeof s & { error: Error } => s.error != null).map(s => s.error);\n\n  return {\n    isAnyLoading: states.some(s => s.isLoading),\n    isAllLoading: states.every(s => s.isLoading),\n    hasAnyError: errors.length > 0,\n    errors,\n    isAllActive: states.every(s => s.isActive),\n  };\n}\n\n/**\n * Sort slots by priority for rendering order\n *\n * @param slots - Slots to sort\n * @param priority - Priority map (slot name -> priority number, higher = first)\n * @returns Sorted slots\n */\nexport function sortSlotsByPriority(\n  slots: readonly ParallelRouteSlot[],\n  priority: Record<string, number> = {}\n): readonly ParallelRouteSlot[] {\n  return [...slots].sort((a, b) => {\n    const priorityA = priority[a.name] ?? 0;\n    const priorityB = priority[b.name] ?? 0;\n    return priorityB - priorityA;\n  });\n}\n\n// =============================================================================\n// Type Guards\n// =============================================================================\n\n/**\n * Type guard for ParallelRouteSlot\n */\nexport function isParallelRouteSlot(value: unknown): value is ParallelRouteSlot {\n  return (\n    typeof value === 'object' &&\n    value !== null &&\n    'name' in value &&\n    'path' in value &&\n    'component' in value\n  );\n}\n\n/**\n * Type guard for SlotMatch\n */\nexport function isSlotMatch(value: unknown): value is SlotMatch {\n  return (\n    typeof value === 'object' &&\n    value !== null &&\n    'slot' in value &&\n    'params' in value &&\n    'score' in value\n  );\n}\n"],"names":["SLOT_PREFIX","DEFAULT_SLOT_NAME","ParallelRoutes","config","name","slotConfig","slot","path","matches","unmatched","match","update","current","_","state","slotName","active","resolution","defaultName","slotPath","params","pattern","coreParsePathParams","_path","coreGetPatternSpecificity","createParallelRoutes"],"mappings":"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"}