UNPKG

@tanstack/router-core

Version:

Modern and scalable routing for React applications

1 lines 51.4 kB
{"version":3,"file":"new-process-route-tree.cjs","names":[],"sources":["../../src/new-process-route-tree.ts"],"sourcesContent":["import { invariant } from './invariant'\nimport { trimPathRight } from './path'\nimport { createSieveCache } from './sieve-cache'\nimport { last } from './utils'\nimport type { SieveCache } from './sieve-cache'\nimport type { InterpolationSegment, RouteInterpolation } from './path'\n\nexport const SEGMENT_TYPE_PATHNAME = 0\nexport const SEGMENT_TYPE_PARAM = 1\nexport const SEGMENT_TYPE_WILDCARD = 2\nexport const SEGMENT_TYPE_OPTIONAL_PARAM = 3\nconst SEGMENT_TYPE_INDEX = 4\nconst SEGMENT_TYPE_PATHLESS = 5 // only used in matching to represent pathless routes that need to carry more information\n\n/**\n * All the kinds of segments that can be present in a route path.\n */\nexport type SegmentKind =\n  | typeof SEGMENT_TYPE_PATHNAME\n  | typeof SEGMENT_TYPE_PARAM\n  | typeof SEGMENT_TYPE_WILDCARD\n  | typeof SEGMENT_TYPE_OPTIONAL_PARAM\n\n/**\n * All the kinds of segments that can be present in the segment tree.\n */\ntype ExtendedSegmentKind =\n  | SegmentKind\n  | typeof SEGMENT_TYPE_INDEX\n  | typeof SEGMENT_TYPE_PATHLESS\n\nexport type DynamicPathSegment = [\n  kind: Exclude<SegmentKind, typeof SEGMENT_TYPE_PATHNAME>,\n  key: string,\n  prefix: string,\n  /** Undefined marks a bare splat, which discards the remaining template. */\n  suffix: string | undefined,\n]\n\nexport function getParamNames(data: RouteInterpolation): Array<string> {\n  const cached = data.names\n  if (cached) {\n    return cached\n  }\n  const keys: Array<string> = []\n  for (const segment of data) {\n    if (typeof segment !== 'string') {\n      keys.push(segment[1 /* key */])\n    }\n  }\n  return (data.names = keys)\n}\n\n/** Parse one segment for matching and retain the same record for interpolation. */\nexport function parseSegment(\n  /** The full path string containing the segment. */\n  path: string,\n  /** The starting index of the segment within the path. */\n  start: number,\n  /** The next slash, or the length of the path. */\n  end: number,\n): InterpolationSegment {\n  const part = path.substring(start, end)\n  if (part.charCodeAt(0) === 36) {\n    return part.length === 1\n      ? [SEGMENT_TYPE_WILDCARD, '_splat', '', undefined]\n      : [SEGMENT_TYPE_PARAM, part.substring(1), '', '']\n  }\n  const open = part.indexOf('{')\n  if (open >= 0) {\n    const close = part.indexOf('}', open)\n    const optional = part.charCodeAt(open + 1) === 45\n    const nameStart = open + (optional ? 3 : 2)\n    if (\n      close >= 0 &&\n      part.charCodeAt(nameStart - 1) === 36 &&\n      (!optional || nameStart < close)\n    ) {\n      const key = part.substring(nameStart, close)\n      return [\n        optional\n          ? SEGMENT_TYPE_OPTIONAL_PARAM\n          : key\n            ? SEGMENT_TYPE_PARAM\n            : SEGMENT_TYPE_WILDCARD,\n        key || '_splat',\n        part.substring(0, open),\n        path.substring(start + close + 1, key ? end : path.length),\n      ]\n    }\n  }\n  return part\n}\n\ntype ParsedRoute<T extends RouteLike> = [\n  node: AnySegmentNode<T>,\n  cursor: number,\n  segments: RouteInterpolation | undefined,\n]\n\n/** Compile a template, optionally attaching its new segments to the matching trie. */\nexport function parseSegments<TRouteLike extends RouteLike>(\n  defaultCaseSensitive: boolean,\n  route: TRouteLike,\n  start: number,\n): RouteInterpolation\nexport function parseSegments<TRouteLike extends RouteLike>(\n  defaultCaseSensitive: boolean,\n  route: TRouteLike,\n  start: number,\n  node: AnySegmentNode<TRouteLike>,\n  dynamicListsToSort?: Array<Array<DynamicSegmentNode<TRouteLike>>>,\n  parentInterpolation?: RouteInterpolation,\n): ParsedRoute<TRouteLike>\nexport function parseSegments<TRouteLike extends RouteLike>(\n  defaultCaseSensitive: boolean,\n  route: TRouteLike,\n  start: number,\n  node?: AnySegmentNode<TRouteLike>,\n  /** Each dynamic sibling list is recorded once, when it first needs sorting. */\n  dynamicListsToSort?: Array<Array<DynamicSegmentNode<TRouteLike>>>,\n  parentInterpolation?: RouteInterpolation,\n): RouteInterpolation | ParsedRoute<TRouteLike> {\n  let cursor = start\n  const path = route.fullPath ?? route.from\n  const options = route.options\n  const length = path.length\n  const literalEnd = path.endsWith('/') ? length - 1 : length\n  const caseSensitive = options?.caseSensitive ?? defaultCaseSensitive\n  const parseParams = options?.params?.parse ?? options?.parseParams\n  let interpolation: Array<InterpolationSegment> | undefined\n  let literalStart = parentInterpolation ? start - 1 : 0\n  if (!node || path.includes('$')) {\n    interpolation = parentInterpolation?.slice() ?? []\n    const tail = last(interpolation)\n    if (\n      tail &&\n      typeof tail !== 'string' &&\n      tail[0 /* kind */] === SEGMENT_TYPE_WILDCARD\n    ) {\n      // A parent's wildcard consumes the child's template too.\n      // `start` follows that entire template, including its trailing slash.\n      interpolation[interpolation.length - 1] = [\n        tail[0 /* kind */],\n        tail[1 /* key */],\n        tail[2 /* prefix */],\n        tail[3 /* suffix */] === undefined\n          ? undefined\n          : tail[3 /* suffix */] +\n            path.substring(\n              start - (path[start - 2] === '/' ? 2 : 1),\n              literalEnd,\n            ),\n      ]\n      literalStart = length\n    }\n  }\n  while (cursor < length) {\n    const start = cursor\n    const next = path.indexOf('/', start)\n    let end = next === -1 ? length : next\n    const segment = parseSegment(path, start, end)\n    cursor = end + 1\n    let nextNode: AnySegmentNode<TRouteLike>\n    if (typeof segment === 'string') {\n      if (!node) {\n        continue\n      }\n      let name = segment\n      let staticChildren: Map<string, StaticSegmentNode<TRouteLike>>\n      if (caseSensitive) {\n        staticChildren = node.static ??= new Map()\n      } else {\n        name = segment.toLowerCase()\n        staticChildren = node.staticInsensitive ??= new Map()\n      }\n      const existingNode = staticChildren.get(name)\n      if (existingNode) {\n        nextNode = existingNode\n      } else {\n        const next = createSegmentNode(node)\n        nextNode = next\n        staticChildren.set(name, next)\n      }\n    } else {\n      const kind = segment[0 /* kind */]\n      let prefix = segment[2 /* prefix */]\n      let suffix = segment[3 /* suffix */] ?? ''\n      if (kind === SEGMENT_TYPE_WILDCARD) {\n        end = length\n        cursor = end + 1\n      }\n      if (interpolation && literalStart < end) {\n        // Retain original spelling before matcher case folding.\n        if (literalStart < start - 1) {\n          interpolation.push(path.substring(literalStart, start - 1))\n        }\n        segment[2 /* prefix */] = '/' + prefix\n        if (\n          kind === SEGMENT_TYPE_WILDCARD &&\n          segment[3 /* suffix */] !== undefined &&\n          literalEnd < length\n        ) {\n          segment[3 /* suffix */] = suffix.slice(0, -1)\n        }\n        interpolation.push(segment)\n        literalStart = end\n      }\n      if (!node) {\n        continue\n      }\n      const actuallyCaseSensitive = caseSensitive && !!(prefix || suffix)\n      if (!caseSensitive) {\n        prefix = prefix.toLowerCase()\n        suffix = suffix.toLowerCase()\n      }\n      const siblings =\n        kind === SEGMENT_TYPE_PARAM\n          ? (node.dynamic ??= [])\n          : kind === SEGMENT_TYPE_OPTIONAL_PARAM\n            ? (node.optional ??= [])\n            : (node.wildcard ??= [])\n      const existingNode =\n        // Keep wildcard aliases as separate match candidates, even when\n        // they have the same shape and no parser.\n        kind !== SEGMENT_TYPE_WILDCARD &&\n        !parseParams &&\n        siblings.find(\n          (s) =>\n            !s.parse &&\n            s.caseSensitive === actuallyCaseSensitive &&\n            s.prefix === prefix &&\n            s.suffix === suffix,\n        )\n      if (existingNode) {\n        nextNode = existingNode\n      } else {\n        const next = createSegmentNode(\n          node,\n          kind,\n          actuallyCaseSensitive,\n          prefix,\n          suffix,\n        )\n        nextNode = next\n        siblings.push(next)\n        if (siblings.length === 2) {\n          dynamicListsToSort?.push(siblings)\n        }\n      }\n    }\n    node = nextNode\n  }\n\n  if (interpolation && literalStart < literalEnd) {\n    interpolation.push(path.substring(literalStart, literalEnd))\n  }\n  // Discard push()'s spare capacity before retaining this array on both owners.\n  const segmentData = interpolation?.slice()\n  if (!node) {\n    return segmentData!\n  }\n\n  // create pathless node\n  if (\n    parseParams &&\n    route.children &&\n    !route.isRoot &&\n    route.id &&\n    route.id.charCodeAt(route.id.lastIndexOf('/') + 1) === 95 /* '_' */\n  ) {\n    const pathlessNode = createSegmentNode(node, SEGMENT_TYPE_PATHLESS)\n    ;(node.pathless ??= []).push(pathlessNode)\n    node = pathlessNode\n  }\n\n  const isLeaf = (route.path || !route.children) && !route.isRoot\n  // create index node\n  if (isLeaf && literalEnd < length) {\n    const indexNode = createSegmentNode(node, SEGMENT_TYPE_INDEX)\n    node.index = indexNode\n    node = indexNode\n  }\n\n  node.parse = parseParams ?? null\n  node.priority = options?.params?.priority ?? 0\n  // A shared candidate keeps the original names of its first terminal route.\n  if (!node.route) {\n    node.data = segmentData\n    if (isLeaf) {\n      node.route = route\n    }\n  }\n  return [node, cursor, segmentData]\n}\n\nfunction sortDynamic(\n  a: {\n    prefix: string\n    suffix: string\n    caseSensitive: boolean\n    parse: null | ((params: Record<string, string>) => unknown)\n    priority: number\n  },\n  b: {\n    prefix: string\n    suffix: string\n    caseSensitive: boolean\n    parse: null | ((params: Record<string, string>) => unknown)\n    priority: number\n  },\n) {\n  if (a.parse && !b.parse) return -1\n  if (!a.parse && b.parse) return 1\n  if (a.parse && b.parse && (a.priority || b.priority))\n    return b.priority - a.priority\n  if (a.prefix && b.prefix && a.prefix !== b.prefix) {\n    if (a.prefix.startsWith(b.prefix)) return -1\n    if (b.prefix.startsWith(a.prefix)) return 1\n  }\n  if (a.suffix && b.suffix && a.suffix !== b.suffix) {\n    if (a.suffix.endsWith(b.suffix)) return -1\n    if (b.suffix.endsWith(a.suffix)) return 1\n  }\n  if (a.prefix && !b.prefix) return -1\n  if (!a.prefix && b.prefix) return 1\n  if (a.suffix && !b.suffix) return -1\n  if (!a.suffix && b.suffix) return 1\n  if (a.caseSensitive && !b.caseSensitive) return -1\n  if (!a.caseSensitive && b.caseSensitive) return 1\n\n  // Equal specificity preserves route declaration order through stable sort.\n  return 0\n}\n\nfunction createSegmentNode<T extends RouteLike>(\n  parent?: AnySegmentNode<T>,\n  kind?: StaticSegmentNode<T>['kind'],\n): StaticSegmentNode<T>\nfunction createSegmentNode<T extends RouteLike>(\n  parent: AnySegmentNode<T>,\n  kind:\n    | typeof SEGMENT_TYPE_PARAM\n    | typeof SEGMENT_TYPE_WILDCARD\n    | typeof SEGMENT_TYPE_OPTIONAL_PARAM,\n  caseSensitive: boolean,\n  prefix: string,\n  suffix: string,\n): DynamicSegmentNode<T>\nfunction createSegmentNode<T extends RouteLike>(\n  parent?: AnySegmentNode<T>,\n  kind: ExtendedSegmentKind = SEGMENT_TYPE_PATHNAME,\n  caseSensitive?: boolean,\n  prefix?: string,\n  suffix?: string,\n): SegmentNode<T> {\n  return {\n    kind,\n    depth: parent ? parent.depth + 1 : 0,\n    pathless: null,\n    index: null,\n    static: null,\n    staticInsensitive: null,\n    dynamic: null,\n    optional: null,\n    wildcard: null,\n    route: null,\n    data: undefined,\n    parent,\n    parse: null,\n    priority: 0,\n    caseSensitive,\n    prefix,\n    suffix,\n  }\n}\n\ntype StaticSegmentNode<T extends RouteLike> = SegmentNode<T> & {\n  kind:\n    | typeof SEGMENT_TYPE_PATHNAME\n    | typeof SEGMENT_TYPE_PATHLESS\n    | typeof SEGMENT_TYPE_INDEX\n}\n\ntype DynamicSegmentNode<T extends RouteLike> = SegmentNode<T> & {\n  kind:\n    | typeof SEGMENT_TYPE_PARAM\n    | typeof SEGMENT_TYPE_WILDCARD\n    | typeof SEGMENT_TYPE_OPTIONAL_PARAM\n  prefix: string\n  suffix: string\n  caseSensitive: boolean\n}\n\ntype AnySegmentNode<T extends RouteLike> =\n  | StaticSegmentNode<T>\n  | DynamicSegmentNode<T>\n\ntype SegmentNode<T extends RouteLike> = {\n  kind: ExtendedSegmentKind\n  prefix?: string\n  suffix?: string\n  caseSensitive?: boolean\n\n  pathless: Array<StaticSegmentNode<T>> | null\n\n  /** Exact index segment (highest priority) */\n  index: StaticSegmentNode<T> | null\n\n  /** Static segments (2nd priority) */\n  static: Map<string, StaticSegmentNode<T>> | null\n\n  /** Case insensitive static segments (3rd highest priority) */\n  staticInsensitive: Map<string, StaticSegmentNode<T>> | null\n\n  /** Dynamic segments ($param) */\n  dynamic: Array<DynamicSegmentNode<T>> | null\n\n  /** Optional dynamic segments ({-$param}) */\n  optional: Array<DynamicSegmentNode<T>> | null\n\n  /** Wildcard segments ($ - lowest priority) */\n  wildcard: Array<DynamicSegmentNode<T>> | null\n\n  /** Terminal route (if this path can end here) */\n  route: T | null\n\n  /** Original template data for this candidate or parse gate. */\n  data: RouteInterpolation | undefined\n\n  parent: AnySegmentNode<T> | undefined\n\n  depth: number\n\n  /** route.options.params.parse function, set on the last node of the route */\n  parse: null | ((params: Record<string, string>) => unknown)\n\n  /** route.options.params.priority ?? 0 */\n  priority: number\n}\n\ntype RouteLike = {\n  _interpolation?: RouteInterpolation\n  id?: string\n  path?: string // relative path from the parent,\n  children?: Array<RouteLike> // child routes,\n  parentRoute?: RouteLike // parent route,\n  isRoot?: boolean\n  options?: {\n    caseSensitive?: boolean\n    parseParams?: (params: Record<string, string>) => unknown\n    params?: {\n      parse?: (params: Record<string, string>) => unknown\n      priority?: number\n    }\n  }\n} &\n  // router tree\n  (| { fullPath: string; from?: never } // full path from the root\n    // flat route masks list\n    | { fullPath?: never; from: string } // full path from the root\n  )\n\nexport type ProcessedTree<\n  TTree extends Extract<RouteLike, { fullPath: string }>,\n  TFlat extends Extract<RouteLike, { from: string }>,\n  TSingle extends Extract<RouteLike, { from: string }>,\n> = {\n  /** a representation of the `routeTree` as a segment tree */\n  segmentTree: AnySegmentNode<TTree>\n  /** a mini route tree generated from the flat `routeMasks` list */\n  masksTree: AnySegmentNode<TFlat> | null\n  /** @deprecated keep until v2 so that `router.matchRoute` can keep not caring about the actual route tree */\n  singleCache: SieveCache<string, AnySegmentNode<TSingle>>\n  /** a cache of route matches from the `segmentTree` */\n  matchCache: SieveCache<string, RouteMatch<TTree> | null>\n  /** a cache of route matches from the `masksTree` */\n  flatCache: SieveCache<string, ReturnType<typeof findMatch<TFlat>>> | null\n}\n\nexport function processRouteMasks<\n  TRouteLike extends Extract<RouteLike, { from: string }>,\n>(\n  routeList: Array<TRouteLike>,\n  processedTree: ProcessedTree<any, TRouteLike, any>,\n) {\n  const segmentTree = createSegmentNode<TRouteLike>()\n  const dynamicListsToSort: Array<Array<DynamicSegmentNode<TRouteLike>>> = []\n  function visit(\n    route: TRouteLike,\n    start: number,\n    parentNode: AnySegmentNode<TRouteLike>,\n    parentInterpolation?: RouteInterpolation,\n  ) {\n    const [node, cursor, segments] = parseSegments(\n      false,\n      route,\n      start,\n      parentNode,\n      dynamicListsToSort,\n      parentInterpolation,\n    )\n    if (route.children) {\n      for (const child of route.children) {\n        visit(child as TRouteLike, cursor, node, segments)\n      }\n    }\n  }\n  for (const route of routeList) {\n    visit(route, 1, segmentTree)\n  }\n  for (const nodes of dynamicListsToSort) {\n    nodes.sort(sortDynamic)\n  }\n  processedTree.masksTree = segmentTree\n  processedTree.flatCache = createSieveCache<\n    string,\n    ReturnType<typeof findMatch<TRouteLike>>\n  >(1000)\n}\n\n/**\n * Take an arbitrary list of routes, create a tree from them (if it hasn't been created already), and match a path against it.\n */\nexport function findFlatMatch<T extends Extract<RouteLike, { from: string }>>(\n  /** The path to match. */\n  path: string,\n  /** The `processedTree` returned by the initial `processRouteTree` call. */\n  processedTree: ProcessedTree<any, T, any>,\n) {\n  path ||= '/'\n  const cached = processedTree.flatCache!.get(path)\n  if (cached !== undefined) return cached\n  const result = findMatch(path, processedTree.masksTree!)\n  processedTree.flatCache!.set(path, result)\n  return result\n}\n\n/**\n * @deprecated keep until v2 so that `router.matchRoute` can keep not caring about the actual route tree\n */\nexport function findSingleMatch(\n  from: string,\n  caseSensitive: boolean,\n  fuzzy: boolean,\n  path: string,\n  processedTree: ProcessedTree<any, any, { from: string }>,\n) {\n  from ||= '/'\n  path ||= '/'\n  const key = caseSensitive ? `case\\0${from}` : from\n  let tree = processedTree.singleCache.get(key)\n  if (!tree) {\n    // single flat routes (router.matchRoute) are not eagerly processed,\n    // if we haven't seen this route before, process it now\n    tree = createSegmentNode<{ from: string }>()\n    parseSegments(caseSensitive, { from }, 1, tree)\n    processedTree.singleCache.set(key, tree)\n  }\n  return findMatch(path, tree, fuzzy)\n}\n\ntype RouteMatch<T extends Extract<RouteLike, { fullPath: string }>> = {\n  route: T\n  rawParams: Record<string, string>\n  branch: ReadonlyArray<T>\n}\n\nexport function findRouteMatch<\n  T extends Extract<RouteLike, { fullPath: string }>,\n>(\n  /** The path to match against the route tree. */\n  path: string,\n  /** The `processedTree` returned by the initial `processRouteTree` call. */\n  processedTree: ProcessedTree<T, any, any>,\n  /** If `true`, allows fuzzy matching (partial matches), i.e. which node in the tree would have been an exact match if the `path` had been shorter? */\n  fuzzy = false,\n): RouteMatch<T> | null {\n  const key = fuzzy ? path : `nofuzz\\0${path}` // the main use for `findRouteMatch` is fuzzy:true, so we optimize for that case\n  const cached = processedTree.matchCache.get(key)\n  if (cached !== undefined) return cached\n  path ||= '/'\n  let result: RouteMatch<T> | null\n\n  try {\n    result = findMatch(\n      path,\n      processedTree.segmentTree,\n      fuzzy,\n    ) as RouteMatch<T> | null\n  } catch (err) {\n    if (err instanceof URIError) {\n      result = null\n    } else {\n      throw err\n    }\n  }\n\n  if (result) result.branch = buildRouteBranch(result.route)\n  processedTree.matchCache.set(key, result)\n  return result\n}\n\nexport interface ProcessRouteTreeResult<\n  TRouteLike extends Extract<RouteLike, { fullPath: string }> & { id: string },\n> {\n  /** Should be considered a black box, needs to be provided to all matching functions in this module. */\n  processedTree: ProcessedTree<TRouteLike, any, any>\n  /** A lookup map of routes by their unique IDs. */\n  routesById: Record<string, TRouteLike>\n  /** A lookup map of routes by their trimmed full paths. */\n  routesByPath: Record<string, TRouteLike>\n}\n\n/**\n * Processes a route tree into a segment trie for efficient path matching.\n * Also builds lookup maps for routes by ID and by trimmed full path.\n */\nexport function processRouteTree<\n  TRouteLike extends Extract<RouteLike, { fullPath: string }> & {\n    id: string\n    init: (originalIndex: number) => void\n  },\n>(\n  /** The root of the route tree to process. */\n  routeTree: TRouteLike,\n  /** Whether matching should be case sensitive by default (overridden by individual route options). */\n  caseSensitive: boolean = false,\n): ProcessRouteTreeResult<TRouteLike> {\n  const segmentTree = createSegmentNode<TRouteLike>()\n  const dynamicListsToSort: Array<Array<DynamicSegmentNode<TRouteLike>>> = []\n  const routesById = {} as Record<string, TRouteLike>\n  const routesByPath = {} as Record<string, TRouteLike>\n  let index = 0\n  function visit(\n    route: TRouteLike,\n    start: number,\n    parentNode: AnySegmentNode<TRouteLike>,\n    parentInterpolation?: RouteInterpolation,\n  ) {\n    route.init(index)\n\n    if (route.id in routesById) {\n      if (process.env.NODE_ENV !== 'production') {\n        throw new Error(\n          `Invariant failed: Duplicate routes found with id: ${String(route.id)}`,\n        )\n      }\n\n      invariant()\n    }\n\n    routesById[route.id] = route\n    if (index !== 0 && route.path) {\n      const trimmedFullPath = trimPathRight(route.fullPath)\n      if (!routesByPath[trimmedFullPath] || route.fullPath.endsWith('/')) {\n        routesByPath[trimmedFullPath] = route\n      }\n    }\n    index++\n\n    const [node, cursor, segments] = parseSegments(\n      caseSensitive,\n      route,\n      start,\n      parentNode,\n      dynamicListsToSort,\n      parentInterpolation,\n    )\n    route._interpolation = segments\n    if (route.children) {\n      for (const child of route.children) {\n        visit(child as TRouteLike, cursor, node, segments)\n      }\n    }\n  }\n  visit(routeTree, 1, segmentTree)\n  for (const nodes of dynamicListsToSort) {\n    nodes.sort(sortDynamic)\n  }\n  const processedTree: ProcessedTree<TRouteLike, any, any> = {\n    segmentTree,\n    singleCache: createSieveCache<string, AnySegmentNode<any>>(1000),\n    matchCache: createSieveCache<string, RouteMatch<TRouteLike> | null>(1000),\n    flatCache: null,\n    masksTree: null,\n  }\n  return {\n    processedTree,\n    routesById,\n    routesByPath,\n  }\n}\n\nfunction findMatch<T extends RouteLike>(\n  path: string,\n  segmentTree: AnySegmentNode<T>,\n  fuzzy = false,\n): {\n  route: T\n  /**\n   * The raw (unparsed) params extracted from the path.\n   * This will be the exhaustive list of all params defined in the route's path.\n   */\n  rawParams: Record<string, string>\n} | null {\n  const parts = path.split('/')\n  const leaf = getNodeMatch(path, parts, segmentTree, fuzzy)\n  if (!leaf) return null\n  const [rawParams] = extractParams(path, parts, leaf)\n  return {\n    route: leaf.node.route!,\n    rawParams,\n  }\n}\n\ntype ParamExtractionState = {\n  part: number\n  node: number\n  path: number\n  param: number\n}\n\n/**\n * This function is \"resumable\":\n * - the `leaf` input can contain `extract` and `rawParams` properties from a previous `extractParams` call\n * - the returned `state` can be passed back as `extract` in a future call to continue extracting params from where we left off\n *\n * Inputs are *not* mutated.\n */\nfunction extractParams<T extends RouteLike>(\n  path: string,\n  parts: Array<string>,\n  leaf: {\n    node: AnySegmentNode<T>\n    skipped: number\n    extract?: ParamExtractionState\n    rawParams?: Record<string, string>\n  },\n): [rawParams: Record<string, string>, state: ParamExtractionState] {\n  const list = buildBranch(leaf.node)\n  const names = leaf.node.data && getParamNames(leaf.node.data)\n  const rawParams: Record<string, string> = Object.create(null)\n  /** which segment of the path we're currently processing */\n  let partIndex = leaf.extract?.part ?? 0\n  /** which node of the route tree branch we're currently processing */\n  let nodeIndex = leaf.extract?.node ?? 0\n  /** index of the 1st character of the segment we're processing in the path string */\n  let pathIndex = leaf.extract?.path ?? 0\n  /** Next original parameter name, independent of pathless/static nodes. */\n  let paramIndex = leaf.extract?.param ?? 0\n  for (; nodeIndex < list.length; partIndex++, nodeIndex++, pathIndex++) {\n    const node = list[nodeIndex]!\n    // index nodes are terminating nodes, nothing to extract, just leave\n    if (node.kind === SEGMENT_TYPE_INDEX) break\n    // pathless nodes do not consume a path segment\n    if (node.kind === SEGMENT_TYPE_PATHLESS) {\n      partIndex--\n      pathIndex--\n      continue\n    }\n    const part = parts[partIndex]\n    const currentPathIndex = pathIndex\n    if (part) pathIndex += part.length\n    if (\n      node.kind === SEGMENT_TYPE_PARAM ||\n      node.kind === SEGMENT_TYPE_OPTIONAL_PARAM\n    ) {\n      const name = names![paramIndex++]!\n      if (\n        node.kind === SEGMENT_TYPE_OPTIONAL_PARAM &&\n        leaf.skipped & (1 << nodeIndex)\n      ) {\n        partIndex-- // stay on the same part\n        pathIndex = currentPathIndex - 1 // undo pathIndex advancement; -1 to account for loop increment\n        continue\n      }\n      const value =\n        node.suffix || node.prefix\n          ? part!.substring(\n              node.prefix.length,\n              part!.length - node.suffix.length,\n            )\n          : part!\n      if (value || node.kind === SEGMENT_TYPE_PARAM) {\n        rawParams[name] = decodeURIComponent(value)\n      }\n    } else if (node.kind === SEGMENT_TYPE_WILDCARD) {\n      const n = node\n      const value = path.substring(\n        currentPathIndex + n.prefix.length,\n        path.length - n.suffix.length,\n      )\n      const splat = decodeURIComponent(value)\n      // TODO: Deprecate *\n      rawParams['*'] = splat\n      rawParams._splat = splat\n      break\n    }\n  }\n  if (leaf.rawParams) Object.assign(rawParams, leaf.rawParams)\n  return [\n    rawParams,\n    {\n      part: partIndex,\n      node: nodeIndex,\n      path: pathIndex,\n      param: paramIndex,\n    },\n  ]\n}\n\nexport function buildRouteBranch<T extends RouteLike>(route: T) {\n  const list = [route]\n  while (route.parentRoute) {\n    route = route.parentRoute as T\n    list.push(route)\n  }\n  list.reverse()\n  return list\n}\n\nfunction buildBranch<T extends RouteLike>(node: AnySegmentNode<T>) {\n  const list: Array<AnySegmentNode<T>> = Array(node.depth + 1)\n  do {\n    list[node.depth] = node\n    node = node.parent!\n  } while (node)\n  return list\n}\n\ntype MatchStackFrame<T extends RouteLike> = {\n  node: AnySegmentNode<T>\n  /** index of the segment of path */\n  index: number\n  /**\n   * Bitmask of skipped optional segments.\n   *\n   * This is a very performant way of storing an \"array of booleans\", but it means beyond 32 segments we can't track skipped optionals.\n   * If we really really need to support more than 32 segments we can switch to using a `BigInt` here. It's about 2x slower in worst case scenarios.\n   */\n  skipped: number\n  /** Positional bitmasks tracking which consumed URL segments matched each segment kind. */\n  statics: number\n  dynamics: number\n  optionals: number\n  /** intermediary state for param extraction */\n  extract?: ParamExtractionState\n  /** intermediary params from param extraction */\n  rawParams?: Record<string, string>\n}\n\nfunction getNodeMatch<T extends RouteLike>(\n  path: string,\n  parts: Array<string>,\n  segmentTree: AnySegmentNode<T>,\n  fuzzy: boolean,\n) {\n  // quick check for root index\n  // this is an optimization, algorithm should work correctly without this block\n  if (path === '/' && segmentTree.index)\n    return { node: segmentTree.index, skipped: 0 } as Pick<\n      Frame,\n      'node' | 'skipped'\n    >\n\n  const trailingSlash = !last(parts)\n  const pathIsIndex = trailingSlash && path !== '/'\n  const partsLength = parts.length - (trailingSlash ? 1 : 0)\n\n  type Frame = MatchStackFrame<T>\n\n  // use a stack to explore all possible paths (params cause branching)\n  // iterate \"backwards\" (low priority first) so that we can push() each candidate, and pop() the highest priority candidate first\n  // - pros: it is depth-first, so we find full matches faster\n  // - cons: we cannot short-circuit, because highest priority matches are at the end of the loop (for loop with i--) (but we have no good short-circuiting anyway)\n  // other possible approaches:\n  // - shift instead of pop (measure performance difference), this allows iterating \"forwards\" (effectively breadth-first)\n  // - never remove from the stack, keep a cursor instead. Then we can push \"forwards\" and avoid reversing the order of candidates (effectively breadth-first)\n  const stack: Array<Frame> = [\n    {\n      node: segmentTree,\n      index: 1,\n      skipped: 0,\n      statics: 0,\n      dynamics: 0,\n      optionals: 0,\n    },\n  ]\n\n  let bestFuzzy: Frame | null = null\n  let bestMatch: Frame | null = null\n\n  while (stack.length) {\n    const frame = stack.pop()!\n    const { node, index, skipped, statics, dynamics, optionals } = frame\n    let { extract, rawParams } = frame\n\n    // Wildcard candidates are pushed speculatively as fallbacks in case a\n    // higher-priority wildcard later fails params.parse. If a better wildcard\n    // has already validated and become bestMatch, lower-priority wildcard\n    // fallbacks cannot win anymore and should not run params.parse.\n    if (\n      node.kind === SEGMENT_TYPE_WILDCARD &&\n      node.route &&\n      !isFrameMoreSpecific(bestMatch, frame)\n    ) {\n      continue\n    }\n\n    if (node.parse) {\n      const result = validateParseParams(path, parts, frame)\n      if (!result) continue\n      rawParams = frame.rawParams\n      extract = frame.extract\n    }\n\n    // In fuzzy mode, track the best partial match we've found so far\n    if (\n      fuzzy &&\n      node.route &&\n      node.kind !== SEGMENT_TYPE_INDEX &&\n      isFrameMoreSpecific(bestFuzzy, frame)\n    ) {\n      bestFuzzy = frame\n    }\n\n    const isBeyondPath = index === partsLength\n    if (isBeyondPath) {\n      if (\n        node.route &&\n        (!pathIsIndex ||\n          node.kind === SEGMENT_TYPE_INDEX ||\n          node.kind === SEGMENT_TYPE_WILDCARD) &&\n        isFrameMoreSpecific(bestMatch, frame)\n      ) {\n        bestMatch = frame\n      }\n      // beyond the length of the path parts, only some segment types can match\n      if (!node.optional && !node.wildcard && !node.index && !node.pathless)\n        continue\n    }\n\n    const part = isBeyondPath ? undefined : parts[index]!\n    let lowerPart: string\n\n    // 0. Try index match\n    if (isBeyondPath && node.index) {\n      const indexFrame = {\n        node: node.index,\n        index,\n        skipped,\n        statics,\n        dynamics,\n        optionals,\n        extract,\n        rawParams,\n      }\n      let indexValid = true\n      if (node.index.parse) {\n        const result = validateParseParams(path, parts, indexFrame)\n        if (!result) indexValid = false\n      }\n      if (indexValid) {\n        // perfect match, no need to continue\n        // this is an optimization, algorithm should work correctly without this block\n        if (\n          !dynamics &&\n          !optionals &&\n          !skipped &&\n          isPerfectStaticMatch(statics, partsLength)\n        ) {\n          return indexFrame\n        }\n        if (isFrameMoreSpecific(bestMatch, indexFrame)) {\n          // index matches skip the stack because they cannot have children\n          bestMatch = indexFrame\n        }\n      }\n    }\n\n    // 5. Try wildcard match\n    if (node.wildcard) {\n      for (let i = node.wildcard.length - 1; i >= 0; i--) {\n        const segment = node.wildcard[i]!\n        const { prefix, suffix } = segment\n        if (prefix) {\n          if (isBeyondPath) continue\n          const casePart = segment.caseSensitive\n            ? part\n            : (lowerPart ??= part!.toLowerCase())\n          if (!casePart!.startsWith(prefix)) continue\n        }\n        if (suffix) {\n          if (isBeyondPath) continue\n          const end = parts.slice(index).join('/')\n          const suffixPart = end.slice(-suffix.length)\n          const casePart = segment.caseSensitive\n            ? suffixPart\n            : suffixPart.toLowerCase()\n          if (\n            casePart !== suffix ||\n            end.length - suffix.length < prefix.length\n          ) {\n            continue\n          }\n        }\n        // wildcard matches consume the rest of the URL and cannot have children\n        stack.push({\n          node: segment,\n          index: partsLength,\n          skipped,\n          statics,\n          dynamics,\n          optionals,\n          extract,\n          rawParams,\n        })\n      }\n    }\n\n    // 4. Try optional match\n    if (node.optional) {\n      // A skipped optional is keyed by the child node's trie depth.\n      const nextSkipped = skipped | (1 << (node.depth + 1))\n      for (let i = node.optional.length - 1; i >= 0; i--) {\n        const segment = node.optional[i]!\n        // when skipping, the node advances by 1, but the index doesn't\n        stack.push({\n          node: segment,\n          index,\n          skipped: nextSkipped,\n          statics,\n          dynamics,\n          optionals,\n          extract,\n          rawParams,\n        }) // enqueue skipping the optional\n      }\n      if (!isBeyondPath) {\n        for (let i = node.optional.length - 1; i >= 0; i--) {\n          const segment = node.optional[i]!\n          const { prefix, suffix } = segment\n          if (prefix || suffix) {\n            const casePart = segment.caseSensitive\n              ? part!\n              : (lowerPart ??= part!.toLowerCase())\n            if (prefix && !casePart.startsWith(prefix)) continue\n            if (\n              suffix &&\n              casePart.indexOf(suffix, casePart.length - suffix.length) <\n                prefix.length\n            ) {\n              continue\n            }\n          }\n          stack.push({\n            node: segment,\n            index: index + 1,\n            skipped,\n            statics,\n            dynamics,\n            optionals: optionals + segmentScore(partsLength, index),\n            extract,\n            rawParams,\n          })\n        }\n      }\n    }\n\n    // 3. Try dynamic match\n    if (!isBeyondPath && node.dynamic && part) {\n      for (let i = node.dynamic.length - 1; i >= 0; i--) {\n        const segment = node.dynamic[i]!\n        const { prefix, suffix } = segment\n        if (prefix || suffix) {\n          const casePart = segment.caseSensitive\n            ? part\n            : (lowerPart ??= part.toLowerCase())\n          if (prefix && !casePart.startsWith(prefix)) continue\n          if (\n            suffix &&\n            casePart.indexOf(suffix, casePart.length - suffix.length) <\n              prefix.length\n          ) {\n            continue\n          }\n        }\n        stack.push({\n          node: segment,\n          index: index + 1,\n          skipped,\n          statics,\n          dynamics: dynamics + segmentScore(partsLength, index),\n          optionals,\n          extract,\n          rawParams,\n        })\n      }\n    }\n\n    // 2. Try case insensitive static match\n    if (!isBeyondPath && node.staticInsensitive) {\n      const match = node.staticInsensitive.get(\n        (lowerPart ??= part!.toLowerCase()),\n      )\n      if (match) {\n        stack.push({\n          node: match,\n          index: index + 1,\n          skipped,\n          statics: statics + segmentScore(partsLength, index),\n          dynamics,\n          optionals,\n          extract,\n          rawParams,\n        })\n      }\n    }\n\n    // 1. Try static match\n    if (!isBeyondPath && node.static) {\n      const match = node.static.get(part!)\n      if (match) {\n        stack.push({\n          node: match,\n          index: index + 1,\n          skipped,\n          statics: statics + segmentScore(partsLength, index),\n          dynamics,\n          optionals,\n          extract,\n          rawParams,\n        })\n      }\n    }\n\n    // 0. Try pathless match\n    if (node.pathless) {\n      for (let i = node.pathless.length - 1; i >= 0; i--) {\n        const segment = node.pathless[i]!\n        stack.push({\n          node: segment,\n          index,\n          skipped,\n          statics,\n          dynamics,\n          optionals,\n          extract,\n          rawParams,\n        })\n      }\n    }\n  }\n\n  if (bestMatch) return bestMatch\n\n  if (fuzzy && bestFuzzy) {\n    let sliceIndex = bestFuzzy.index\n    for (let i = 0; i < bestFuzzy.index; i++) {\n      sliceIndex += parts[i]!.length\n    }\n    const splat = sliceIndex === path.length ? '/' : path.slice(sliceIndex)\n    bestFuzzy.rawParams ??= Object.create(null)\n    bestFuzzy.rawParams!['**'] = decodeURIComponent(splat)\n    return bestFuzzy\n  }\n\n  return null\n}\n\nfunction segmentScore(partsLength: number, index: number): number {\n  // The specificity scores are bitmasks over consumed URL segments. Earlier\n  // URL segments should dominate later ones when comparing scores, so the\n  // first real segment gets the highest bit and the last gets bit 0. Since\n  // `parts[0]` is the empty string before the leading slash, real URL segments\n  // are [1, partsLength), making this segment's bit `partsLength - index - 1`.\n  return 2 ** (partsLength - index - 1)\n}\n\nfunction isPerfectStaticMatch(statics: number, partsLength: number): boolean {\n  return statics === 2 ** (partsLength - 1) - 1\n}\n\nfunction validateParseParams<T extends RouteLike>(\n  path: string,\n  parts: Array<string>,\n  frame: MatchStackFrame<T>,\n) {\n  let rawParams: Record<string, string>\n  let state: ParamExtractionState\n\n  try {\n    ;[rawParams, state] = extractParams(path, parts, frame)\n  } catch {\n    return null\n  }\n\n  frame.rawParams = rawParams\n  frame.extract = state\n\n  if (!frame.node.parse) return true\n\n  try {\n    if (frame.node.parse(rawParams) === false) return null\n  } catch {\n    // Thrown parse errors should be surfaced on the selected match by\n    // extractStrictParams, not used as fallback route selection.\n  }\n\n  return true\n}\n\nfunction isFrameMoreSpecific(\n  // the stack frame previously saved as \"best match\"\n  prev: MatchStackFrame<any> | null,\n  // the candidate stack frame\n  next: MatchStackFrame<any>,\n): boolean {\n  if (!prev) return true\n  return (\n    next.statics > prev.statics ||\n    (next.statics === prev.statics &&\n      (next.dynamics > prev.dynamics ||\n        (next.dynamics === prev.dynamics &&\n          (next.optionals > prev.optionals ||\n            (next.optionals === prev.optionals &&\n              ((next.node.kind === SEGMENT_TYPE_INDEX) >\n                (prev.node.kind === SEGMENT_TYPE_INDEX) ||\n                ((next.node.kind === SEGMENT_TYPE_INDEX) ===\n                  (prev.node.kind === SEGMENT_TYPE_INDEX) &&\n                  next.node.depth > prev.node.depth)))))))\n  )\n}\n"],"mappings":"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"}