{"version":3,"file":"path-extractor.mjs","sources":["../../../../src/lib/routing/discovery/path-extractor.ts"],"sourcesContent":["/**\n * @file Path Extractor for Route Discovery\n * @description Extracts URL paths from file system structure using convention-based parsing.\n * Supports Next.js-style file naming conventions including dynamic routes, catch-all routes,\n * route groups, and parallel routes.\n *\n * @module @/lib/routing/discovery/path-extractor\n *\n * This module provides:\n * - File path to URL path conversion\n * - Dynamic segment extraction\n * - Route parameter parsing\n * - Path hierarchy analysis\n * - Convention-based naming support\n *\n * @example\n * ```typescript\n * import { extractPathFromFile, parseSegments } from '@/lib/routing/discovery/path-extractor';\n *\n * const urlPath = extractPathFromFile('users/[id]/posts/[postId]/page.tsx');\n * console.log(urlPath); // '/users/:id/posts/:postId'\n *\n * const segments = parseSegments('users/[id]/posts');\n * console.log(segments);\n * // [\n * //   { type: 'static', value: 'users', raw: 'users' },\n * //   { type: 'dynamic', value: 'id', raw: '[id]' },\n * //   { type: 'static', value: 'posts', raw: 'posts' },\n * // ]\n * ```\n */\n\n// =============================================================================\n// Core Utility Imports\n// =============================================================================\n\nimport {\n  parsePathParams as coreParsePathParams,\n  getPatternSpecificity as coreGetPatternSpecificity,\n  isChildPath as coreIsChildPath,\n  normalizePath as coreNormalizePath,\n} from '../core/path-utils';\n\n// =============================================================================\n// Types\n// =============================================================================\n\n/**\n * Types of path segments that can be extracted\n */\nexport type SegmentType =\n  | 'static' // Regular static segment (e.g., 'users')\n  | 'dynamic' // Dynamic parameter (e.g., [id])\n  | 'optional' // Optional parameter (e.g., [[id]])\n  | 'catch-all' // Catch-all parameter (e.g., [...slug])\n  | 'optional-catch-all' // Optional catch-all (e.g., [[...slug]])\n  | 'group' // Route group (e.g., (auth))\n  | 'parallel' // Parallel route slot (e.g., @modal)\n  | 'intercepting'; // Intercepting route (e.g., (.)photo)\n\n/**\n * Parsed path segment with full metadata\n */\nexport interface ParsedSegment {\n  /** Type of segment */\n  readonly type: SegmentType;\n  /** Extracted value (parameter name or static value) */\n  readonly value: string;\n  /** Original raw segment string */\n  readonly raw: string;\n  /** Whether segment is optional */\n  readonly optional: boolean;\n  /** Position in the path */\n  readonly position: number;\n  /** Constraint type (if any) */\n  readonly constraint?: SegmentConstraint;\n}\n\n/**\n * Constraint that can be applied to dynamic segments\n */\nexport interface SegmentConstraint {\n  /** Constraint type */\n  readonly type: 'regex' | 'enum' | 'custom';\n  /** Constraint pattern or values */\n  readonly pattern: string | readonly string[];\n  /** Validation function name */\n  readonly validator?: string;\n}\n\n/**\n * Extracted path information\n */\nexport interface ExtractedPath {\n  /** The URL path pattern */\n  readonly urlPath: string;\n  /** All segments parsed */\n  readonly segments: readonly ParsedSegment[];\n  /** Parameter names extracted */\n  readonly params: readonly string[];\n  /** Optional parameters */\n  readonly optionalParams: readonly string[];\n  /** Whether path has catch-all */\n  readonly hasCatchAll: boolean;\n  /** Whether path has optional catch-all */\n  readonly hasOptionalCatchAll: boolean;\n  /** Route groups this path belongs to */\n  readonly groups: readonly string[];\n  /** Parallel slots in this path */\n  readonly parallelSlots: readonly string[];\n  /** Nesting depth */\n  readonly depth: number;\n  /** Whether this is an index route */\n  readonly isIndex: boolean;\n  /** Whether this is a layout route */\n  readonly isLayout: boolean;\n}\n\n/**\n * Path extraction configuration\n */\nexport interface PathExtractorConfig {\n  /** File names that represent index routes */\n  readonly indexFileNames: readonly string[];\n  /** File names that represent layout routes */\n  readonly layoutFileNames: readonly string[];\n  /** Strip these extensions from file names */\n  readonly stripExtensions: readonly string[];\n  /** Segment naming convention */\n  readonly convention: 'nextjs' | 'remix' | 'custom';\n  /** Custom segment parser */\n  readonly customParser?: (segment: string) => ParsedSegment | null;\n  /** Feature flag for advanced features */\n  readonly featureFlag?: string;\n}\n\n/**\n * Path hierarchy node\n */\nexport interface PathNode {\n  /** Segment at this level */\n  readonly segment: ParsedSegment;\n  /** Full path to this node */\n  readonly path: string;\n  /** Parent node (if any) */\n  readonly parent: PathNode | null;\n  /** Child nodes */\n  readonly children: readonly PathNode[];\n  /** Associated files */\n  readonly files: readonly string[];\n}\n\n// =============================================================================\n// Constants\n// =============================================================================\n\n/**\n * Default path extractor configuration\n */\nexport const DEFAULT_EXTRACTOR_CONFIG: PathExtractorConfig = {\n  indexFileNames: ['index', 'page', '_index'],\n  layoutFileNames: ['layout', '_layout', 'root'],\n  stripExtensions: ['.tsx', '.ts', '.jsx', '.js'],\n  convention: 'nextjs',\n};\n\n/**\n * Segment parsing patterns for Next.js convention\n */\nconst NEXTJS_PATTERNS = {\n  // Dynamic segment: [param]\n  dynamic: /^\\[([a-zA-Z_][a-zA-Z0-9_]*)\\]$/,\n  // Optional segment: [[param]]\n  optional: /^\\[\\[([a-zA-Z_][a-zA-Z0-9_]*)\\]\\]$/,\n  // Catch-all: [...param]\n  catchAll: /^\\[\\.\\.\\.([a-zA-Z_][a-zA-Z0-9_]*)\\]$/,\n  // Optional catch-all: [[...param]]\n  optionalCatchAll: /^\\[\\[\\.\\.\\.([a-zA-Z_][a-zA-Z0-9_]*)\\]\\]$/,\n  // Route group: (groupName)\n  group: /^\\(([a-zA-Z_][a-zA-Z0-9_-]*)\\)$/,\n  // Parallel route: @slotName\n  parallel: /^@([a-zA-Z_][a-zA-Z0-9_]*)$/,\n  // Intercepting route: (.)segment, (..)segment, (...)segment\n  intercepting: /^\\((\\.{1,3})\\)(.+)$/,\n} as const;\n\n/**\n * Segment parsing patterns for Remix convention\n */\nconst REMIX_PATTERNS = {\n  // Dynamic segment: $param\n  dynamic: /^\\$([a-zA-Z_][a-zA-Z0-9_]*)$/,\n  // Optional segment: ($param)\n  optional: /^\\(\\$([a-zA-Z_][a-zA-Z0-9_]*)\\)$/,\n  // Catch-all: $ or $.tsx\n  catchAll: /^\\$$/,\n  // Pathless layout: _index or __layout\n  pathless: /^_/,\n} as const;\n\n// =============================================================================\n// Segment Parsing Functions\n// =============================================================================\n\n/**\n * Parse a single path segment into a ParsedSegment\n *\n * @param segment - Raw segment string\n * @param position - Position in the path\n * @param config - Extractor configuration\n * @returns Parsed segment or null if invalid\n *\n * @example\n * ```typescript\n * parseSegment('[id]', 0);\n * // { type: 'dynamic', value: 'id', raw: '[id]', optional: false, position: 0 }\n *\n * parseSegment('[[...slug]]', 1);\n * // { type: 'optional-catch-all', value: 'slug', raw: '[[...slug]]', optional: true, position: 1 }\n * ```\n */\nexport function parseSegment(\n  segment: string,\n  position: number,\n  config: PathExtractorConfig = DEFAULT_EXTRACTOR_CONFIG\n): ParsedSegment | null {\n  if (!segment || segment.trim() === '') {\n    return null;\n  }\n\n  // Try custom parser first\n  if (config.customParser) {\n    const custom = config.customParser(segment);\n    if (custom) {\n      return { ...custom, position };\n    }\n  }\n\n  const patterns = config.convention === 'remix' ? REMIX_PATTERNS : NEXTJS_PATTERNS;\n\n  // Check for optional catch-all first (most specific)\n  if ('optionalCatchAll' in patterns) {\n    const optionalCatchAllMatch = segment.match(patterns.optionalCatchAll);\n    const optionalCatchAllValue = optionalCatchAllMatch?.[1];\n    if (\n      optionalCatchAllValue !== null &&\n      optionalCatchAllValue !== undefined &&\n      optionalCatchAllValue !== ''\n    ) {\n      return {\n        type: 'optional-catch-all',\n        value: optionalCatchAllValue,\n        raw: segment,\n        optional: true,\n        position,\n      };\n    }\n  }\n\n  // Check for catch-all\n  const catchAllMatch = segment.match(patterns.catchAll);\n  if (catchAllMatch) {\n    const value = config.convention === 'remix' ? '*' : (catchAllMatch[1] ?? '*');\n    return {\n      type: 'catch-all',\n      value,\n      raw: segment,\n      optional: false,\n      position,\n    };\n  }\n\n  // Check for optional dynamic\n  if ('optional' in patterns) {\n    const optionalMatch = segment.match(patterns.optional);\n    if (optionalMatch?.[1] != null && optionalMatch[1] !== '') {\n      return {\n        type: 'optional',\n        value: optionalMatch[1],\n        raw: segment,\n        optional: true,\n        position,\n      };\n    }\n  }\n\n  // Check for dynamic\n  const dynamicMatch = segment.match(patterns.dynamic);\n  if (dynamicMatch?.[1] != null && dynamicMatch[1] !== '') {\n    return {\n      type: 'dynamic',\n      value: dynamicMatch[1],\n      raw: segment,\n      optional: false,\n      position,\n    };\n  }\n\n  // Next.js specific patterns\n  if (config.convention === 'nextjs') {\n    // Check for route group\n    const groupMatch = segment.match(NEXTJS_PATTERNS.group);\n    if (groupMatch?.[1] != null && groupMatch[1] !== '') {\n      return {\n        type: 'group',\n        value: groupMatch[1],\n        raw: segment,\n        optional: false,\n        position,\n      };\n    }\n\n    // Check for parallel route\n    const parallelMatch = segment.match(NEXTJS_PATTERNS.parallel);\n    if (parallelMatch?.[1] != null && parallelMatch[1] !== '') {\n      return {\n        type: 'parallel',\n        value: parallelMatch[1],\n        raw: segment,\n        optional: false,\n        position,\n      };\n    }\n\n    // Check for intercepting route\n    const interceptingMatch = segment.match(NEXTJS_PATTERNS.intercepting);\n    if (\n      interceptingMatch?.[1] != null &&\n      interceptingMatch[1] !== '' &&\n      interceptingMatch[2] != null &&\n      interceptingMatch[2] !== ''\n    ) {\n      return {\n        type: 'intercepting',\n        value: interceptingMatch[2],\n        raw: segment,\n        optional: false,\n        position,\n        constraint: {\n          type: 'custom',\n          pattern: interceptingMatch[1], // The dots indicating interception level\n        },\n      };\n    }\n  }\n\n  // Static segment\n  return {\n    type: 'static',\n    value: segment,\n    raw: segment,\n    optional: false,\n    position,\n  };\n}\n\n/**\n * Parse all segments from a path string\n *\n * @param path - File path to parse (e.g., 'users/[id]/posts')\n * @param config - Extractor configuration\n * @returns Array of parsed segments\n *\n * @example\n * ```typescript\n * const segments = parseSegments('users/[id]/posts/[[...slug]]');\n * // Returns 4 segments: static, dynamic, static, optional-catch-all\n * ```\n */\nexport function parseSegments(\n  path: string,\n  config: PathExtractorConfig = DEFAULT_EXTRACTOR_CONFIG\n): readonly ParsedSegment[] {\n  // Normalize path separators and split\n  const normalizedPath = path.replace(/\\\\/g, '/');\n  const parts = normalizedPath.split('/').filter(Boolean);\n\n  const segments: ParsedSegment[] = [];\n\n  for (let i = 0; i < parts.length; i++) {\n    const part = parts[i];\n    if (part == null || part === '') continue;\n\n    // Strip file extension from last segment\n    let segmentName = part;\n    if (i === parts.length - 1) {\n      for (const ext of config.stripExtensions) {\n        if (segmentName.endsWith(ext)) {\n          segmentName = segmentName.slice(0, -ext.length);\n          break;\n        }\n      }\n    }\n\n    // Skip index/layout file names (they don't contribute to URL path)\n    if (i === parts.length - 1) {\n      if (\n        config.indexFileNames.includes(segmentName) ||\n        config.layoutFileNames.includes(segmentName)\n      ) {\n        continue;\n      }\n    }\n\n    const segment = parseSegment(segmentName, i, config);\n    if (segment) {\n      segments.push(segment);\n    }\n  }\n\n  return Object.freeze(segments);\n}\n\n// =============================================================================\n// Path Extraction Functions\n// =============================================================================\n\n/**\n * Convert parsed segments to a URL path pattern\n *\n * @param segments - Parsed segments\n * @returns URL path string (e.g., '/users/:id/posts')\n */\nexport function segmentsToUrlPath(segments: readonly ParsedSegment[]): string {\n  const parts: string[] = [];\n\n  for (const segment of segments) {\n    switch (segment.type) {\n      case 'static':\n        parts.push(segment.value);\n        break;\n      case 'dynamic':\n        parts.push(`:${segment.value}`);\n        break;\n      case 'optional':\n        parts.push(`:${segment.value}?`);\n        break;\n      case 'catch-all':\n        parts.push('*');\n        break;\n      case 'optional-catch-all':\n        parts.push('*?');\n        break;\n      case 'group':\n        // Route groups don't appear in URL\n        break;\n      case 'parallel':\n        // Parallel slots don't appear in URL\n        break;\n      case 'intercepting':\n        parts.push(segment.value);\n        break;\n    }\n  }\n\n  return `/${parts.join('/')}`;\n}\n\n/**\n * Extract full path information from a file path\n *\n * @param filePath - File path relative to routes directory\n * @param config - Extractor configuration\n * @returns Extracted path information\n *\n * @example\n * ```typescript\n * const extracted = extractPathFromFile('users/[id]/posts/[postId]/page.tsx');\n * console.log(extracted.urlPath);       // '/users/:id/posts/:postId'\n * console.log(extracted.params);        // ['id', 'postId']\n * console.log(extracted.isIndex);       // true (page.tsx is index)\n * ```\n */\nexport function extractPathFromFile(\n  filePath: string,\n  config: PathExtractorConfig = DEFAULT_EXTRACTOR_CONFIG\n): ExtractedPath {\n  const segments = parseSegments(filePath, config);\n  const urlPath = segmentsToUrlPath(segments);\n\n  // Extract parameter information\n  const params: string[] = [];\n  const optionalParams: string[] = [];\n  const groups: string[] = [];\n  const parallelSlots: string[] = [];\n  let hasCatchAll = false;\n  let hasOptionalCatchAll = false;\n\n  for (const segment of segments) {\n    switch (segment.type) {\n      case 'dynamic':\n        params.push(segment.value);\n        break;\n      case 'optional':\n        params.push(segment.value);\n        optionalParams.push(segment.value);\n        break;\n      case 'catch-all':\n        params.push(segment.value);\n        hasCatchAll = true;\n        break;\n      case 'optional-catch-all':\n        params.push(segment.value);\n        optionalParams.push(segment.value);\n        hasOptionalCatchAll = true;\n        break;\n      case 'group':\n        groups.push(segment.value);\n        break;\n      case 'parallel':\n        parallelSlots.push(segment.value);\n        break;\n    }\n  }\n\n  // Determine if this is an index or layout route\n  const normalizedPath = filePath.replace(/\\\\/g, '/');\n  const fileName = normalizedPath.split('/').pop() ?? '';\n  const baseFileName = config.stripExtensions.reduce(\n    (name, ext) => (name.endsWith(ext) ? name.slice(0, -ext.length) : name),\n    fileName\n  );\n\n  const isIndex = config.indexFileNames.includes(baseFileName);\n  const isLayout = config.layoutFileNames.includes(baseFileName);\n\n  return {\n    urlPath,\n    segments,\n    params: Object.freeze(params),\n    optionalParams: Object.freeze(optionalParams),\n    hasCatchAll,\n    hasOptionalCatchAll,\n    groups: Object.freeze(groups),\n    parallelSlots: Object.freeze(parallelSlots),\n    depth: segments.filter((s) => s.type !== 'group' && s.type !== 'parallel').length,\n    isIndex,\n    isLayout,\n  };\n}\n\n/**\n * Extract parameters from a URL given a path pattern\n *\n * Delegates to core path utilities for the actual extraction.\n *\n * @param pattern - URL pattern (e.g., '/users/:id/posts/:postId')\n * @param url - Actual URL to extract from (e.g., '/users/123/posts/456')\n * @returns Extracted parameters or null if no match\n *\n * @example\n * ```typescript\n * const params = extractParamsFromUrl('/users/:id', '/users/123');\n * console.log(params); // { id: '123' }\n * ```\n */\nexport function extractParamsFromUrl(pattern: string, url: string): Record<string, string> | null {\n  // Delegate to core path utilities\n  return coreParsePathParams(pattern, url);\n}\n\n// =============================================================================\n// Path Hierarchy Functions\n// =============================================================================\n\n/**\n * Build a path hierarchy tree from multiple file paths\n *\n * @param filePaths - Array of file paths\n * @param config - Extractor configuration\n * @returns Root nodes of the path hierarchy\n *\n * @example\n * ```typescript\n * const tree = buildPathTree([\n *   'users/page.tsx',\n *   'users/[id]/page.tsx',\n *   'users/[id]/posts/page.tsx',\n * ]);\n * // Returns hierarchical tree structure\n * ```\n */\nexport function buildPathTree(\n  filePaths: readonly string[],\n  config: PathExtractorConfig = DEFAULT_EXTRACTOR_CONFIG\n): readonly PathNode[] {\n  const nodeMap = new Map<string, PathNode & { children: PathNode[]; files: string[] }>();\n  const rootNodes: (PathNode & { children: PathNode[]; files: string[] })[] = [];\n\n  for (const filePath of filePaths) {\n    const segments = parseSegments(filePath, config);\n    let currentPath = '';\n    let parentNode: (PathNode & { children: PathNode[]; files: string[] }) | null = null;\n\n    for (const segment of segments) {\n      // Skip groups and parallel slots for hierarchy\n      if (segment.type === 'group' || segment.type === 'parallel') {\n        continue;\n      }\n\n      currentPath = currentPath ? `${currentPath}/${segment.raw}` : segment.raw;\n\n      let node = nodeMap.get(currentPath);\n      if (!node) {\n        node = {\n          segment,\n          path: currentPath,\n          parent: parentNode,\n          children: [],\n          files: [],\n        };\n        nodeMap.set(currentPath, node);\n\n        if (parentNode) {\n          parentNode.children.push(node);\n        } else {\n          rootNodes.push(node);\n        }\n      }\n\n      parentNode = node;\n    }\n\n    // Add file to the leaf node\n    if (parentNode) {\n      parentNode.files.push(filePath);\n    }\n  }\n\n  // Freeze the tree structure\n  const freezeNode = (node: PathNode): PathNode => ({\n    ...node,\n    children: Object.freeze(Array.from(node.children).map(freezeNode)),\n    files: Object.freeze([...node.files]),\n  });\n\n  return Object.freeze(rootNodes.map(freezeNode));\n}\n\n/**\n * Find the common ancestor path for multiple paths\n *\n * @param paths - Array of URL paths\n * @returns Common ancestor path or '/' if none\n */\nexport function findCommonAncestor(paths: readonly string[]): string {\n  if (paths.length === 0) return '/';\n  if (paths.length === 1) return paths[0] ?? '/';\n\n  const splitPaths = paths.map((p) => p.split('/').filter(Boolean));\n  const [firstPath] = splitPaths;\n  if (!firstPath) return '/';\n\n  const commonParts: string[] = [];\n\n  for (let i = 0; i < firstPath.length; i++) {\n    const part = firstPath[i];\n    if (part != null && splitPaths.every((p) => p[i] === part)) {\n      commonParts.push(part);\n    } else {\n      break;\n    }\n  }\n\n  return `/${commonParts.join('/')}`;\n}\n\n/**\n * Check if one path is a child of another\n *\n * Delegates to core path utilities.\n *\n * @param childPath - Potential child path\n * @param parentPath - Potential parent path\n * @returns True if childPath is nested under parentPath\n */\nexport function isChildPath(childPath: string, parentPath: string): boolean {\n  // Delegate to core path utilities\n  return coreIsChildPath(childPath, parentPath);\n}\n\n/**\n * Get the relative path from parent to child\n *\n * @param childPath - Child path\n * @param parentPath - Parent path\n * @returns Relative path or null if not a child\n */\nexport function getRelativePath(childPath: string, parentPath: string): string | null {\n  if (!isChildPath(childPath, parentPath)) {\n    return null;\n  }\n\n  const normalizedParent = parentPath === '/' ? '' : coreNormalizePath(parentPath);\n  return childPath.slice(normalizedParent.length);\n}\n\n// =============================================================================\n// Path Validation Functions\n// =============================================================================\n\n/**\n * Validate that a path pattern is well-formed\n *\n * @param pattern - URL path pattern to validate\n * @returns Validation result with any errors\n */\nexport function validatePathPattern(pattern: string): {\n  readonly isValid: boolean;\n  readonly errors: readonly string[];\n} {\n  const errors: string[] = [];\n\n  // Check for empty pattern\n  if (!pattern || pattern.trim() === '') {\n    errors.push('Path pattern cannot be empty');\n    return { isValid: false, errors: Object.freeze(errors) };\n  }\n\n  // Must start with /\n  if (!pattern.startsWith('/')) {\n    errors.push('Path pattern must start with /');\n  }\n\n  // Check for double slashes\n  if (/\\/\\//.test(pattern)) {\n    errors.push('Path pattern cannot contain double slashes');\n  }\n\n  // Check for valid parameter names\n  const paramMatches = pattern.match(/:[a-zA-Z_][a-zA-Z0-9_]*/g) ?? [];\n  const paramNames = new Set<string>();\n  for (const param of paramMatches) {\n    const name = param.slice(1).replace('?', '');\n    if (paramNames.has(name)) {\n      errors.push(`Duplicate parameter name: ${name}`);\n    }\n    paramNames.add(name);\n  }\n\n  // Check catch-all position (must be last)\n  const catchAllIndex = pattern.indexOf('*');\n  if (catchAllIndex !== -1 && catchAllIndex !== pattern.length - 1) {\n    const afterCatchAll = pattern.slice(catchAllIndex + 1);\n    if (afterCatchAll && afterCatchAll !== '?' && afterCatchAll !== '/') {\n      errors.push('Catch-all (*) must be at the end of the path');\n    }\n  }\n\n  return {\n    isValid: errors.length === 0,\n    errors: Object.freeze(errors),\n  };\n}\n\n/**\n * Compare two path patterns for specificity (more specific = higher)\n *\n * Delegates to core path utilities for specificity calculation.\n *\n * @param a - First path pattern\n * @param b - Second path pattern\n * @returns Negative if a < b, positive if a > b, 0 if equal\n */\nexport function comparePathSpecificity(a: string, b: string): number {\n  // Use core specificity calculation\n  const specificityA = coreGetPatternSpecificity(a);\n  const specificityB = coreGetPatternSpecificity(b);\n  return specificityA - specificityB;\n}\n"],"names":["DEFAULT_EXTRACTOR_CONFIG","NEXTJS_PATTERNS","REMIX_PATTERNS","parseSegment","segment","position","config","custom","patterns","optionalCatchAllValue","catchAllMatch","optionalMatch","dynamicMatch","groupMatch","parallelMatch","interceptingMatch","parseSegments","path","parts","segments","i","part","segmentName","ext","segmentsToUrlPath","extractPathFromFile","filePath","urlPath","params","optionalParams","groups","parallelSlots","hasCatchAll","hasOptionalCatchAll","fileName","baseFileName","name","isIndex","isLayout","s","validatePathPattern","pattern","errors","paramMatches","paramNames","param","catchAllIndex","afterCatchAll"],"mappings":"AA+JO,MAAMA,IAAgD;AAAA,EAC3D,gBAAgB,CAAC,SAAS,QAAQ,QAAQ;AAAA,EAC1C,iBAAiB,CAAC,UAAU,WAAW,MAAM;AAAA,EAC7C,iBAAiB,CAAC,QAAQ,OAAO,QAAQ,KAAK;AAAA,EAC9C,YAAY;AACd,GAKMC,IAAkB;AAAA;AAAA,EAEtB,SAAS;AAAA;AAAA,EAET,UAAU;AAAA;AAAA,EAEV,UAAU;AAAA;AAAA,EAEV,kBAAkB;AAAA;AAAA,EAElB,OAAO;AAAA;AAAA,EAEP,UAAU;AAAA;AAAA,EAEV,cAAc;AAChB,GAKMC,IAAiB;AAAA;AAAA,EAErB,SAAS;AAAA;AAAA,EAET,UAAU;AAAA;AAAA,EAEV,UAAU;AAAA;AAAA,EAEV,UAAU;AACZ;AAuBO,SAASC,EACdC,GACAC,GACAC,IAA8BN,GACR;AACtB,MAAI,CAACI,KAAWA,EAAQ,KAAA,MAAW;AACjC,WAAO;AAIT,MAAIE,EAAO,cAAc;AACvB,UAAMC,IAASD,EAAO,aAAaF,CAAO;AAC1C,QAAIG;AACF,aAAO,EAAE,GAAGA,GAAQ,UAAAF,EAAA;AAAA,EAExB;AAEA,QAAMG,IAAWF,EAAO,eAAe,UAAUJ,IAAiBD;AAGlE,MAAI,sBAAsBO,GAAU;AAElC,UAAMC,IADwBL,EAAQ,MAAMI,EAAS,gBAAgB,IACf,CAAC;AACvD,QACEC,KAA0B,QAE1BA,MAA0B;AAE1B,aAAO;AAAA,QACL,MAAM;AAAA,QACN,OAAOA;AAAA,QACP,KAAKL;AAAA,QACL,UAAU;AAAA,QACV,UAAAC;AAAA,MAAA;AAAA,EAGN;AAGA,QAAMK,IAAgBN,EAAQ,MAAMI,EAAS,QAAQ;AACrD,MAAIE;AAEF,WAAO;AAAA,MACL,MAAM;AAAA,MACN,OAHYJ,EAAO,eAAe,UAAU,MAAOI,EAAc,CAAC,KAAK;AAAA,MAIvE,KAAKN;AAAA,MACL,UAAU;AAAA,MACV,UAAAC;AAAA,IAAA;AAKJ,MAAI,cAAcG,GAAU;AAC1B,UAAMG,IAAgBP,EAAQ,MAAMI,EAAS,QAAQ;AACrD,QAAIG,IAAgB,CAAC,KAAK,QAAQA,EAAc,CAAC,MAAM;AACrD,aAAO;AAAA,QACL,MAAM;AAAA,QACN,OAAOA,EAAc,CAAC;AAAA,QACtB,KAAKP;AAAA,QACL,UAAU;AAAA,QACV,UAAAC;AAAA,MAAA;AAAA,EAGN;AAGA,QAAMO,IAAeR,EAAQ,MAAMI,EAAS,OAAO;AACnD,MAAII,IAAe,CAAC,KAAK,QAAQA,EAAa,CAAC,MAAM;AACnD,WAAO;AAAA,MACL,MAAM;AAAA,MACN,OAAOA,EAAa,CAAC;AAAA,MACrB,KAAKR;AAAA,MACL,UAAU;AAAA,MACV,UAAAC;AAAA,IAAA;AAKJ,MAAIC,EAAO,eAAe,UAAU;AAElC,UAAMO,IAAaT,EAAQ,MAAMH,EAAgB,KAAK;AACtD,QAAIY,IAAa,CAAC,KAAK,QAAQA,EAAW,CAAC,MAAM;AAC/C,aAAO;AAAA,QACL,MAAM;AAAA,QACN,OAAOA,EAAW,CAAC;AAAA,QACnB,KAAKT;AAAA,QACL,UAAU;AAAA,QACV,UAAAC;AAAA,MAAA;AAKJ,UAAMS,IAAgBV,EAAQ,MAAMH,EAAgB,QAAQ;AAC5D,QAAIa,IAAgB,CAAC,KAAK,QAAQA,EAAc,CAAC,MAAM;AACrD,aAAO;AAAA,QACL,MAAM;AAAA,QACN,OAAOA,EAAc,CAAC;AAAA,QACtB,KAAKV;AAAA,QACL,UAAU;AAAA,QACV,UAAAC;AAAA,MAAA;AAKJ,UAAMU,IAAoBX,EAAQ,MAAMH,EAAgB,YAAY;AACpE,QACEc,IAAoB,CAAC,KAAK,QAC1BA,EAAkB,CAAC,MAAM,MACzBA,EAAkB,CAAC,KAAK,QACxBA,EAAkB,CAAC,MAAM;AAEzB,aAAO;AAAA,QACL,MAAM;AAAA,QACN,OAAOA,EAAkB,CAAC;AAAA,QAC1B,KAAKX;AAAA,QACL,UAAU;AAAA,QACV,UAAAC;AAAA,QACA,YAAY;AAAA,UACV,MAAM;AAAA,UACN,SAASU,EAAkB,CAAC;AAAA;AAAA,QAAA;AAAA,MAC9B;AAAA,EAGN;AAGA,SAAO;AAAA,IACL,MAAM;AAAA,IACN,OAAOX;AAAA,IACP,KAAKA;AAAA,IACL,UAAU;AAAA,IACV,UAAAC;AAAA,EAAA;AAEJ;AAeO,SAASW,EACdC,GACAX,IAA8BN,GACJ;AAG1B,QAAMkB,IADiBD,EAAK,QAAQ,OAAO,GAAG,EACjB,MAAM,GAAG,EAAE,OAAO,OAAO,GAEhDE,IAA4B,CAAA;AAElC,WAASC,IAAI,GAAGA,IAAIF,EAAM,QAAQE,KAAK;AACrC,UAAMC,IAAOH,EAAME,CAAC;AACpB,QAAIC,KAAQ,QAAQA,MAAS,GAAI;AAGjC,QAAIC,IAAcD;AAClB,QAAID,MAAMF,EAAM,SAAS;AACvB,iBAAWK,KAAOjB,EAAO;AACvB,YAAIgB,EAAY,SAASC,CAAG,GAAG;AAC7B,UAAAD,IAAcA,EAAY,MAAM,GAAG,CAACC,EAAI,MAAM;AAC9C;AAAA,QACF;AAAA;AAKJ,QAAIH,MAAMF,EAAM,SAAS,MAErBZ,EAAO,eAAe,SAASgB,CAAW,KAC1ChB,EAAO,gBAAgB,SAASgB,CAAW;AAE3C;AAIJ,UAAMlB,IAAUD,EAAamB,GAAaF,GAAGd,CAAM;AACnD,IAAIF,KACFe,EAAS,KAAKf,CAAO;AAAA,EAEzB;AAEA,SAAO,OAAO,OAAOe,CAAQ;AAC/B;AAYO,SAASK,EAAkBL,GAA4C;AAC5E,QAAMD,IAAkB,CAAA;AAExB,aAAWd,KAAWe;AACpB,YAAQf,EAAQ,MAAA;AAAA,MACd,KAAK;AACH,QAAAc,EAAM,KAAKd,EAAQ,KAAK;AACxB;AAAA,MACF,KAAK;AACH,QAAAc,EAAM,KAAK,IAAId,EAAQ,KAAK,EAAE;AAC9B;AAAA,MACF,KAAK;AACH,QAAAc,EAAM,KAAK,IAAId,EAAQ,KAAK,GAAG;AAC/B;AAAA,MACF,KAAK;AACH,QAAAc,EAAM,KAAK,GAAG;AACd;AAAA,MACF,KAAK;AACH,QAAAA,EAAM,KAAK,IAAI;AACf;AAAA,MACF,KAAK;AAEH;AAAA,MACF,KAAK;AAEH;AAAA,MACF,KAAK;AACH,QAAAA,EAAM,KAAKd,EAAQ,KAAK;AACxB;AAAA,IAAA;AAIN,SAAO,IAAIc,EAAM,KAAK,GAAG,CAAC;AAC5B;AAiBO,SAASO,EACdC,GACApB,IAA8BN,GACf;AACf,QAAMmB,IAAWH,EAAcU,GAAUpB,CAAM,GACzCqB,IAAUH,EAAkBL,CAAQ,GAGpCS,IAAmB,CAAA,GACnBC,IAA2B,CAAA,GAC3BC,IAAmB,CAAA,GACnBC,IAA0B,CAAA;AAChC,MAAIC,IAAc,IACdC,IAAsB;AAE1B,aAAW7B,KAAWe;AACpB,YAAQf,EAAQ,MAAA;AAAA,MACd,KAAK;AACH,QAAAwB,EAAO,KAAKxB,EAAQ,KAAK;AACzB;AAAA,MACF,KAAK;AACH,QAAAwB,EAAO,KAAKxB,EAAQ,KAAK,GACzByB,EAAe,KAAKzB,EAAQ,KAAK;AACjC;AAAA,MACF,KAAK;AACH,QAAAwB,EAAO,KAAKxB,EAAQ,KAAK,GACzB4B,IAAc;AACd;AAAA,MACF,KAAK;AACH,QAAAJ,EAAO,KAAKxB,EAAQ,KAAK,GACzByB,EAAe,KAAKzB,EAAQ,KAAK,GACjC6B,IAAsB;AACtB;AAAA,MACF,KAAK;AACH,QAAAH,EAAO,KAAK1B,EAAQ,KAAK;AACzB;AAAA,MACF,KAAK;AACH,QAAA2B,EAAc,KAAK3B,EAAQ,KAAK;AAChC;AAAA,IAAA;AAMN,QAAM8B,IADiBR,EAAS,QAAQ,OAAO,GAAG,EAClB,MAAM,GAAG,EAAE,SAAS,IAC9CS,IAAe7B,EAAO,gBAAgB;AAAA,IAC1C,CAAC8B,GAAMb,MAASa,EAAK,SAASb,CAAG,IAAIa,EAAK,MAAM,GAAG,CAACb,EAAI,MAAM,IAAIa;AAAA,IAClEF;AAAA,EAAA,GAGIG,IAAU/B,EAAO,eAAe,SAAS6B,CAAY,GACrDG,IAAWhC,EAAO,gBAAgB,SAAS6B,CAAY;AAE7D,SAAO;AAAA,IACL,SAAAR;AAAA,IACA,UAAAR;AAAA,IACA,QAAQ,OAAO,OAAOS,CAAM;AAAA,IAC5B,gBAAgB,OAAO,OAAOC,CAAc;AAAA,IAC5C,aAAAG;AAAA,IACA,qBAAAC;AAAA,IACA,QAAQ,OAAO,OAAOH,CAAM;AAAA,IAC5B,eAAe,OAAO,OAAOC,CAAa;AAAA,IAC1C,OAAOZ,EAAS,OAAO,CAACoB,MAAMA,EAAE,SAAS,WAAWA,EAAE,SAAS,UAAU,EAAE;AAAA,IAC3E,SAAAF;AAAA,IACA,UAAAC;AAAA,EAAA;AAEJ;AAwKO,SAASE,EAAoBC,GAGlC;AACA,QAAMC,IAAmB,CAAA;AAGzB,MAAI,CAACD,KAAWA,EAAQ,KAAA,MAAW;AACjC,WAAAC,EAAO,KAAK,8BAA8B,GACnC,EAAE,SAAS,IAAO,QAAQ,OAAO,OAAOA,CAAM,EAAA;AAIvD,EAAKD,EAAQ,WAAW,GAAG,KACzBC,EAAO,KAAK,gCAAgC,GAI1C,OAAO,KAAKD,CAAO,KACrBC,EAAO,KAAK,4CAA4C;AAI1D,QAAMC,IAAeF,EAAQ,MAAM,0BAA0B,KAAK,CAAA,GAC5DG,wBAAiB,IAAA;AACvB,aAAWC,KAASF,GAAc;AAChC,UAAMP,IAAOS,EAAM,MAAM,CAAC,EAAE,QAAQ,KAAK,EAAE;AAC3C,IAAID,EAAW,IAAIR,CAAI,KACrBM,EAAO,KAAK,6BAA6BN,CAAI,EAAE,GAEjDQ,EAAW,IAAIR,CAAI;AAAA,EACrB;AAGA,QAAMU,IAAgBL,EAAQ,QAAQ,GAAG;AACzC,MAAIK,MAAkB,MAAMA,MAAkBL,EAAQ,SAAS,GAAG;AAChE,UAAMM,IAAgBN,EAAQ,MAAMK,IAAgB,CAAC;AACrD,IAAIC,KAAiBA,MAAkB,OAAOA,MAAkB,OAC9DL,EAAO,KAAK,8CAA8C;AAAA,EAE9D;AAEA,SAAO;AAAA,IACL,SAASA,EAAO,WAAW;AAAA,IAC3B,QAAQ,OAAO,OAAOA,CAAM;AAAA,EAAA;AAEhC;"}