{"version":3,"sources":["../../../../src/server/route-matcher-managers/default-route-matcher-manager.ts"],"sourcesContent":["import { isDynamicRoute } from '../../shared/lib/router/utils'\nimport type { RouteKind } from '../route-kind'\nimport type { RouteMatch } from '../route-matches/route-match'\nimport type { RouteDefinition } from '../route-definitions/route-definition'\nimport type { RouteMatcherProvider } from '../route-matcher-providers/route-matcher-provider'\nimport type { RouteMatcher } from '../route-matchers/route-matcher'\nimport type { MatchOptions, RouteMatcherManager } from './route-matcher-manager'\nimport { getSortedRoutes } from '../../shared/lib/router/utils'\nimport { LocaleRouteMatcher } from '../route-matchers/locale-route-matcher'\nimport { ensureLeadingSlash } from '../../shared/lib/page-path/ensure-leading-slash'\nimport { DetachedPromise } from '../../lib/detached-promise'\n\ninterface RouteMatchers {\n  static: ReadonlyArray<RouteMatcher>\n  dynamic: ReadonlyArray<RouteMatcher>\n  duplicates: Record<string, ReadonlyArray<RouteMatcher>>\n}\n\nexport class DefaultRouteMatcherManager implements RouteMatcherManager {\n  private readonly providers: Array<RouteMatcherProvider> = []\n  protected readonly matchers: RouteMatchers = {\n    static: [],\n    dynamic: [],\n    duplicates: {},\n  }\n  private lastCompilationID = this.compilationID\n\n  /**\n   * When this value changes, it indicates that a change has been introduced\n   * that requires recompilation.\n   */\n  private get compilationID() {\n    return this.providers.length\n  }\n\n  private waitTillReadyPromise?: Promise<void>\n  public async waitTillReady(): Promise<void> {\n    if (this.waitTillReadyPromise) {\n      await this.waitTillReadyPromise\n      delete this.waitTillReadyPromise\n    }\n  }\n\n  private previousMatchers: ReadonlyArray<RouteMatcher> = []\n  public async reload() {\n    const { promise, resolve, reject } = new DetachedPromise<void>()\n    this.waitTillReadyPromise = promise\n\n    // Grab the compilation ID for this run, we'll verify it at the end to\n    // ensure that if any routes were added before reloading is finished that\n    // we error out.\n    const compilationID = this.compilationID\n\n    try {\n      // Collect all the matchers from each provider.\n      const matchers: Array<RouteMatcher> = []\n\n      // Get all the providers matchers.\n      const providersMatchers: ReadonlyArray<ReadonlyArray<RouteMatcher>> =\n        await Promise.all(this.providers.map((provider) => provider.matchers()))\n\n      // Use this to detect duplicate pathnames.\n      const all = new Map<string, RouteMatcher>()\n      const duplicates: Record<string, RouteMatcher[]> = {}\n      for (const providerMatchers of providersMatchers) {\n        for (const matcher of providerMatchers) {\n          // Reset duplicated matches when reloading from pages conflicting state.\n          if (matcher.duplicated) delete matcher.duplicated\n          // Test to see if the matcher being added is a duplicate.\n          const duplicate = all.get(matcher.definition.pathname)\n          if (duplicate) {\n            // This looks a little weird, but essentially if the pathname\n            // already exists in the duplicates map, then we got that array\n            // reference. Otherwise, we create a new array with the original\n            // duplicate first. Then we push the new matcher into the duplicate\n            // array, and reset it to the duplicates object (which may be a\n            // no-op if the pathname already existed in the duplicates object).\n            // Then we set the array of duplicates on both the original\n            // duplicate object and the new one, so we can keep them in sync.\n            // If a new duplicate is found, and it matches an existing pathname,\n            // the retrieval of the `other` will actually return the array\n            // reference used by all other duplicates. This is why ReadonlyArray\n            // is so important! Array's are always references!\n            const others = duplicates[matcher.definition.pathname] ?? [\n              duplicate,\n            ]\n            others.push(matcher)\n            duplicates[matcher.definition.pathname] = others\n\n            // Add duplicated details to each route.\n            duplicate.duplicated = others\n            matcher.duplicated = others\n\n            // TODO: see if we should error for duplicates in production?\n          }\n\n          matchers.push(matcher)\n\n          // Add the matcher's pathname to the set.\n          all.set(matcher.definition.pathname, matcher)\n        }\n      }\n\n      // Update the duplicate matchers. This is used in the development manager\n      // to warn about duplicates.\n      this.matchers.duplicates = duplicates\n\n      // If the cache is the same as what we just parsed, we can exit now. We\n      // can tell by using the `===` which compares object identity, which for\n      // the manifest matchers, will return the same matcher each time.\n      if (\n        this.previousMatchers.length === matchers.length &&\n        this.previousMatchers.every(\n          (cachedMatcher, index) => cachedMatcher === matchers[index]\n        )\n      ) {\n        return\n      }\n      this.previousMatchers = matchers\n\n      // For matchers that are for static routes, filter them now.\n      this.matchers.static = matchers.filter((matcher) => !matcher.isDynamic)\n\n      // For matchers that are for dynamic routes, filter them and sort them now.\n      const dynamic = matchers.filter((matcher) => matcher.isDynamic)\n\n      // As `getSortedRoutes` only takes an array of strings, we need to create\n      // a map of the pathnames (used for sorting) and the matchers. When we\n      // have locales, there may be multiple matches for the same pathname. To\n      // handle this, we keep a map of all the indexes (in `reference`) and\n      // merge them in later.\n\n      const reference = new Map<string, number[]>()\n      const pathnames = new Array<string>()\n      for (let index = 0; index < dynamic.length; index++) {\n        // Grab the pathname from the definition.\n        const pathname = dynamic[index].definition.pathname\n\n        // Grab the index in the dynamic array, push it into the reference.\n        const indexes = reference.get(pathname) ?? []\n        indexes.push(index)\n\n        // If this is the first one set it. If it isn't, we don't need to\n        // because pushing above on the array will mutate the array already\n        // stored there because array's are always a reference!\n        if (indexes.length === 1) reference.set(pathname, indexes)\n        // Otherwise, continue, we've already added this pathname before.\n        else continue\n\n        pathnames.push(pathname)\n      }\n\n      // Sort the array of pathnames.\n      const sorted = getSortedRoutes(pathnames)\n\n      // For each of the sorted pathnames, iterate over them, grabbing the list\n      // of indexes and merging them back into the new `sortedDynamicMatchers`\n      // array. The order of the same matching pathname doesn't matter because\n      // they will have other matching characteristics (like the locale) that\n      // is considered.\n      const sortedDynamicMatchers: Array<RouteMatcher> = []\n      for (const pathname of sorted) {\n        const indexes = reference.get(pathname)\n        if (!Array.isArray(indexes)) {\n          throw new Error('Invariant: expected to find identity in indexes map')\n        }\n\n        const dynamicMatches = indexes.map((index) => dynamic[index])\n\n        sortedDynamicMatchers.push(...dynamicMatches)\n      }\n\n      this.matchers.dynamic = sortedDynamicMatchers\n\n      // This means that there was a new matcher pushed while we were waiting\n      if (this.compilationID !== compilationID) {\n        throw new Error(\n          'Invariant: expected compilation to finish before new matchers were added, possible missing await'\n        )\n      }\n    } catch (err) {\n      reject(err)\n    } finally {\n      // The compilation ID matched, so mark the complication as finished.\n      this.lastCompilationID = compilationID\n      resolve()\n    }\n  }\n\n  public push(provider: RouteMatcherProvider): void {\n    this.providers.push(provider)\n  }\n\n  public async test(pathname: string, options: MatchOptions): Promise<boolean> {\n    // See if there's a match for the pathname...\n    const match = await this.match(pathname, options)\n\n    // This default implementation only needs to check to see if there _was_ a\n    // match. The development matcher actually changes it's behavior by not\n    // recompiling the routes.\n    return match !== null\n  }\n\n  public async match(\n    pathname: string,\n    options: MatchOptions\n  ): Promise<RouteMatch<RouteDefinition<RouteKind>> | null> {\n    // \"Iterate\" over the match options. Once we found a single match, exit with\n    // it, otherwise return null below. If no match is found, the inner block\n    // won't be called.\n    for await (const match of this.matchAll(pathname, options)) {\n      return match\n    }\n\n    return null\n  }\n\n  /**\n   * This is a point for other managers to override to inject other checking\n   * behavior like duplicate route checking on a per-request basis.\n   *\n   * @param pathname the pathname to validate against\n   * @param matcher the matcher to validate/test with\n   * @returns the match if found\n   */\n  protected validate(\n    pathname: string,\n    matcher: RouteMatcher,\n    options: MatchOptions\n  ): RouteMatch | null {\n    if (matcher instanceof LocaleRouteMatcher) {\n      return matcher.match(pathname, options)\n    }\n\n    // If the locale was inferred from the default locale, then it will have\n    // already added a locale to the pathname. We need to remove it before\n    // matching because this matcher is not locale aware.\n    if (options.i18n?.inferredFromDefault) {\n      return matcher.match(options.i18n.pathname)\n    }\n\n    return matcher.match(pathname)\n  }\n\n  public async *matchAll(\n    pathname: string,\n    options: MatchOptions\n  ): AsyncGenerator<RouteMatch<RouteDefinition<RouteKind>>, null, undefined> {\n    // Guard against the matcher manager from being run before it needs to be\n    // recompiled. This was preferred to re-running the compilation here because\n    // it should be re-ran only when it changes. If a match is attempted before\n    // this is done, it indicates that there is a case where a provider is added\n    // before it was recompiled (an error). We also don't want to affect request\n    // times.\n    if (this.lastCompilationID !== this.compilationID) {\n      throw new Error(\n        'Invariant: expected routes to have been loaded before match'\n      )\n    }\n\n    // Ensure that path matching is done with a leading slash.\n    pathname = ensureLeadingSlash(pathname)\n\n    // If this pathname doesn't look like a dynamic route, and this pathname is\n    // listed in the normalized list of routes, then return it. This ensures\n    // that when a route like `/user/[id]` is encountered, it doesn't just match\n    // with the list of normalized routes.\n    if (!isDynamicRoute(pathname)) {\n      for (const matcher of this.matchers.static) {\n        const match = this.validate(pathname, matcher, options)\n        if (!match) continue\n\n        yield match\n      }\n    }\n\n    // If we should skip handling dynamic routes, exit now.\n    if (options?.skipDynamic) return null\n\n    // Loop over the dynamic matchers, yielding each match.\n    for (const matcher of this.matchers.dynamic) {\n      const match = this.validate(pathname, matcher, options)\n      if (!match) continue\n\n      yield match\n    }\n\n    // We tried direct matching against the pathname and against all the dynamic\n    // paths, so there was no match.\n    return null\n  }\n}\n"],"names":["isDynamicRoute","getSortedRoutes","LocaleRouteMatcher","ensureLeadingSlash","DetachedPromise","DefaultRouteMatcherManager","compilationID","providers","length","waitTillReady","waitTillReadyPromise","reload","promise","resolve","reject","matchers","providersMatchers","Promise","all","map","provider","Map","duplicates","providerMatchers","matcher","duplicated","duplicate","get","definition","pathname","others","push","set","previousMatchers","every","cachedMatcher","index","static","filter","isDynamic","dynamic","reference","pathnames","Array","indexes","sorted","sortedDynamicMatchers","isArray","Error","dynamicMatches","err","lastCompilationID","test","options","match","matchAll","validate","i18n","inferredFromDefault","skipDynamic"],"mappings":"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","ignoreList":[0]}