{"version":3,"sources":["../../../../../../src/shared/lib/router/utils/get-dynamic-param.ts"],"sourcesContent":["import type { DynamicParam } from '../../../../server/app-render/app-render'\nimport type { LoaderTree } from '../../../../server/lib/app-dir-module'\nimport type { OpaqueFallbackRouteParams } from '../../../../server/request/fallback-params'\nimport type { Params } from '../../../../server/request/params'\nimport type { DynamicParamTypesShort } from '../../app-router-types'\nimport { InvariantError } from '../../invariant-error'\nimport { parseLoaderTree } from './parse-loader-tree'\nimport { parseAppRoute, parseAppRouteSegment } from '../routes/app'\nimport { resolveParamValue } from './resolve-param-value'\n\n/**\n * Gets the value of a param from the params object. This correctly handles the\n * case where the param is a fallback route param and encodes the resulting\n * value.\n *\n * @param interpolatedParams - The params object.\n * @param segmentKey - The key of the segment.\n * @param fallbackRouteParams - The fallback route params.\n * @returns The value of the param.\n */\nfunction getParamValue(\n  interpolatedParams: Params,\n  segmentKey: string,\n  fallbackRouteParams: OpaqueFallbackRouteParams | null\n) {\n  let value = interpolatedParams[segmentKey]\n\n  if (fallbackRouteParams?.has(segmentKey)) {\n    // We know that the fallback route params has the segment key because we\n    // checked that above.\n    const [searchValue] = fallbackRouteParams.get(segmentKey)!\n    value = searchValue\n  } else if (Array.isArray(value)) {\n    value = value.map((i) => encodeURIComponent(i))\n  } else if (typeof value === 'string') {\n    value = encodeURIComponent(value)\n  }\n\n  return value\n}\n\nexport function interpolateParallelRouteParams(\n  loaderTree: LoaderTree,\n  params: Params,\n  pagePath: string,\n  fallbackRouteParams: OpaqueFallbackRouteParams | null\n): Params {\n  const interpolated = structuredClone(params)\n\n  // Stack-based traversal with depth tracking\n  const stack: Array<{ tree: LoaderTree; depth: number }> = [\n    { tree: loaderTree, depth: 0 },\n  ]\n\n  // Parse the route from the provided page path.\n  const route = parseAppRoute(pagePath, true)\n\n  while (stack.length > 0) {\n    const { tree, depth } = stack.pop()!\n    const { segment, parallelRoutes } = parseLoaderTree(tree)\n\n    const appSegment = parseAppRouteSegment(segment)\n\n    if (\n      appSegment?.type === 'dynamic' &&\n      !interpolated.hasOwnProperty(appSegment.param.paramName) &&\n      // If the param is in the fallback route params, we don't need to\n      // interpolate it because it's already marked as being unknown.\n      !fallbackRouteParams?.has(appSegment.param.paramName)\n    ) {\n      const { paramName, paramType } = appSegment.param\n\n      const paramValue = resolveParamValue(\n        paramName,\n        paramType,\n        depth,\n        route,\n        interpolated\n      )\n\n      if (paramValue !== undefined) {\n        interpolated[paramName] = paramValue\n      } else if (paramType !== 'optional-catchall') {\n        throw new InvariantError(\n          `Could not resolve param value for segment: ${paramName}`\n        )\n      }\n    }\n\n    // Calculate next depth - increment if this is not a route group and not empty\n    let nextDepth = depth\n    if (\n      appSegment &&\n      appSegment.type !== 'route-group' &&\n      appSegment.type !== 'parallel-route'\n    ) {\n      nextDepth++\n    }\n\n    // Add all parallel routes to the stack for processing\n    for (const parallelRoute of Object.values(parallelRoutes)) {\n      stack.push({ tree: parallelRoute, depth: nextDepth })\n    }\n  }\n\n  return interpolated\n}\n\n/**\n *\n * Shared logic on client and server for creating a dynamic param value.\n *\n * This code needs to be shared with the client so it can extract dynamic route\n * params from the URL without a server request.\n *\n * Because everything in this module is sent to the client, we should aim to\n * keep this code as simple as possible. The special case handling for catchall\n * and optional is, alas, unfortunate.\n */\nexport function getDynamicParam(\n  interpolatedParams: Params,\n  segmentKey: string,\n  dynamicParamType: DynamicParamTypesShort,\n  fallbackRouteParams: OpaqueFallbackRouteParams | null,\n  staticSiblings: readonly string[] | null\n): DynamicParam {\n  let value: string | string[] | undefined = getParamValue(\n    interpolatedParams,\n    segmentKey,\n    fallbackRouteParams\n  )\n\n  // handle the case where an optional catchall does not have a value,\n  // e.g. `/dashboard/[[...slug]]` when requesting `/dashboard`\n  if (!value || value.length === 0) {\n    if (dynamicParamType === 'oc') {\n      return {\n        param: segmentKey,\n        value: null,\n        type: dynamicParamType,\n        treeSegment: [segmentKey, '', dynamicParamType, staticSiblings],\n      }\n    }\n\n    throw new InvariantError(\n      `Missing value for segment key: \"${segmentKey}\" with dynamic param type: ${dynamicParamType}`\n    )\n  }\n\n  const paramCacheKey = Array.isArray(value) ? value.join('/') : value\n\n  return {\n    param: segmentKey,\n    // The value that is passed to user code.\n    value,\n    // The value that is rendered in the router tree.\n    // TODO: If the number of static siblings exceeds some threshold (e.g.,\n    // dozens or hundreds), consider sending a Bloom filter instead of the full\n    // array to reduce payload size. The client would then use the Bloom filter\n    // to check membership with a small false positive rate.\n    treeSegment: [segmentKey, paramCacheKey, dynamicParamType, staticSiblings],\n    type: dynamicParamType,\n  }\n}\n\n/**\n * Regular expression pattern used to match route parameters.\n * Matches both single parameters and parameter groups.\n * Examples:\n *   - `[[...slug]]` matches parameter group with key 'slug', repeat: true, optional: true\n *   - `[...slug]` matches parameter group with key 'slug', repeat: true, optional: false\n *   - `[[foo]]` matches parameter with key 'foo', repeat: false, optional: true\n *   - `[bar]` matches parameter with key 'bar', repeat: false, optional: false\n */\nexport const PARAMETER_PATTERN = /^([^[]*)\\[((?:\\[[^\\]]*\\])|[^\\]]+)\\](.*)$/\n\n/**\n * Parses a given parameter from a route to a data structure that can be used\n * to generate the parametrized route.\n * Examples:\n *   - `[[...slug]]` -> `{ key: 'slug', repeat: true, optional: true }`\n *   - `[...slug]` -> `{ key: 'slug', repeat: true, optional: false }`\n *   - `[[foo]]` -> `{ key: 'foo', repeat: false, optional: true }`\n *   - `[bar]` -> `{ key: 'bar', repeat: false, optional: false }`\n *   - `fizz` -> `{ key: 'fizz', repeat: false, optional: false }`\n * @param param - The parameter to parse.\n * @returns The parsed parameter as a data structure.\n */\nexport function parseParameter(param: string) {\n  const match = param.match(PARAMETER_PATTERN)\n\n  if (!match) {\n    return parseMatchedParameter(param)\n  }\n\n  return parseMatchedParameter(match[2])\n}\n\n/**\n * Parses a matched parameter from the PARAMETER_PATTERN regex to a data structure that can be used\n * to generate the parametrized route.\n * Examples:\n *   - `[...slug]` -> `{ key: 'slug', repeat: true, optional: true }`\n *   - `...slug` -> `{ key: 'slug', repeat: true, optional: false }`\n *   - `[foo]` -> `{ key: 'foo', repeat: false, optional: true }`\n *   - `bar` -> `{ key: 'bar', repeat: false, optional: false }`\n * @param param - The matched parameter to parse.\n * @returns The parsed parameter as a data structure.\n */\nexport function parseMatchedParameter(param: string) {\n  const optional = param.startsWith('[') && param.endsWith(']')\n  if (optional) {\n    param = param.slice(1, -1)\n  }\n  const repeat = param.startsWith('...')\n  if (repeat) {\n    param = param.slice(3)\n  }\n  return { key: param, repeat, optional }\n}\n"],"names":["InvariantError","parseLoaderTree","parseAppRoute","parseAppRouteSegment","resolveParamValue","getParamValue","interpolatedParams","segmentKey","fallbackRouteParams","value","has","searchValue","get","Array","isArray","map","i","encodeURIComponent","interpolateParallelRouteParams","loaderTree","params","pagePath","interpolated","structuredClone","stack","tree","depth","route","length","pop","segment","parallelRoutes","appSegment","type","hasOwnProperty","param","paramName","paramType","paramValue","undefined","nextDepth","parallelRoute","Object","values","push","getDynamicParam","dynamicParamType","staticSiblings","treeSegment","paramCacheKey","join","PARAMETER_PATTERN","parseParameter","match","parseMatchedParameter","optional","startsWith","endsWith","slice","repeat","key"],"mappings":"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","ignoreList":[0]}