{"version":3,"file":"stores.cjs","names":[],"sources":["../../src/stores.ts"],"sourcesContent":["import { createLRUCache } from './lru-cache'\nimport { arraysEqual, functionalUpdate } from './utils'\n\nimport type { AnyRoute } from './route'\nimport type { RouterState } from './router'\nimport type { FullSearchSchema } from './routeInfo'\nimport type { ParsedLocation } from './location'\nimport type { AnyRedirect } from './redirect'\nimport type { AnyRouteMatch } from './Matches'\n\nexport interface RouterReadableStore<TValue> {\n  get: () => TValue\n}\n\nexport interface RouterWritableStore<\n  TValue,\n> extends RouterReadableStore<TValue> {\n  set: ((updater: (prev: TValue) => TValue) => void) & ((value: TValue) => void)\n}\n\nexport type RouterBatchFn = (fn: () => void) => void\n\nexport type MutableStoreFactory = <TValue>(\n  initialValue: TValue,\n) => RouterWritableStore<TValue>\n\nexport type ReadonlyStoreFactory = <TValue>(\n  read: () => TValue,\n) => RouterReadableStore<TValue>\n\nexport type GetStoreConfig = (opts: { isServer?: boolean }) => StoreConfig\n\nexport type StoreConfig = {\n  createMutableStore: MutableStoreFactory\n  createReadonlyStore: ReadonlyStoreFactory\n  batch: RouterBatchFn\n  init?: (stores: RouterStores<AnyRoute>) => void\n}\n\ntype MatchStore = RouterWritableStore<AnyRouteMatch> & {\n  routeId?: string\n}\ntype ReadableStore<TValue> = RouterReadableStore<TValue>\n\n/** SSR non-reactive createMutableStore */\nexport function createNonReactiveMutableStore<TValue>(\n  initialValue: TValue,\n): RouterWritableStore<TValue> {\n  let value = initialValue\n\n  return {\n    get() {\n      return value\n    },\n    set(nextOrUpdater: TValue | ((prev: TValue) => TValue)) {\n      value = functionalUpdate(nextOrUpdater, value)\n    },\n  }\n}\n\n/** SSR non-reactive createReadonlyStore */\nexport function createNonReactiveReadonlyStore<TValue>(\n  read: () => TValue,\n): RouterReadableStore<TValue> {\n  return {\n    get() {\n      return read()\n    },\n  }\n}\n\nexport interface RouterStores<in out TRouteTree extends AnyRoute> {\n  status: RouterWritableStore<RouterState<TRouteTree>['status']>\n  loadedAt: RouterWritableStore<number>\n  isLoading: RouterWritableStore<boolean>\n  isTransitioning: RouterWritableStore<boolean>\n  location: RouterWritableStore<ParsedLocation<FullSearchSchema<TRouteTree>>>\n  resolvedLocation: RouterWritableStore<\n    ParsedLocation<FullSearchSchema<TRouteTree>> | undefined\n  >\n  statusCode: RouterWritableStore<number>\n  redirect: RouterWritableStore<AnyRedirect | undefined>\n  matchesId: RouterWritableStore<Array<string>>\n  pendingIds: RouterWritableStore<Array<string>>\n  /** @internal */\n  cachedIds: RouterWritableStore<Array<string>>\n  matches: ReadableStore<Array<AnyRouteMatch>>\n  pendingMatches: ReadableStore<Array<AnyRouteMatch>>\n  cachedMatches: ReadableStore<Array<AnyRouteMatch>>\n  firstId: ReadableStore<string | undefined>\n  hasPending: ReadableStore<boolean>\n  matchRouteDeps: ReadableStore<{\n    locationHref: string\n    resolvedLocationHref: string | undefined\n    status: RouterState<TRouteTree>['status']\n  }>\n  __store: RouterReadableStore<RouterState<TRouteTree>>\n\n  matchStores: Map<string, MatchStore>\n  pendingMatchStores: Map<string, MatchStore>\n  cachedMatchStores: Map<string, MatchStore>\n\n  /**\n   * Get a computed store that resolves a routeId to its current match state.\n   * Returns the same cached store instance for repeated calls with the same key.\n   * The computed depends on matchesId + the individual match store, so\n   * subscribers are only notified when the resolved match state changes.\n   */\n  getRouteMatchStore: (\n    routeId: string,\n  ) => RouterReadableStore<AnyRouteMatch | undefined>\n\n  setMatches: (nextMatches: Array<AnyRouteMatch>) => void\n  setPending: (nextMatches: Array<AnyRouteMatch>) => void\n  setCached: (nextMatches: Array<AnyRouteMatch>) => void\n}\n\nexport function createRouterStores<TRouteTree extends AnyRoute>(\n  initialState: RouterState<TRouteTree>,\n  config: StoreConfig,\n): RouterStores<TRouteTree> {\n  const { createMutableStore, createReadonlyStore, batch, init } = config\n\n  // non reactive utilities\n  const matchStores = new Map<string, MatchStore>()\n  const pendingMatchStores = new Map<string, MatchStore>()\n  const cachedMatchStores = new Map<string, MatchStore>()\n\n  // atoms\n  const status = createMutableStore(initialState.status)\n  const loadedAt = createMutableStore(initialState.loadedAt)\n  const isLoading = createMutableStore(initialState.isLoading)\n  const isTransitioning = createMutableStore(initialState.isTransitioning)\n  const location = createMutableStore(initialState.location)\n  const resolvedLocation = createMutableStore(initialState.resolvedLocation)\n  const statusCode = createMutableStore(initialState.statusCode)\n  const redirect = createMutableStore(initialState.redirect)\n  const matchesId = createMutableStore<Array<string>>([])\n  const pendingIds = createMutableStore<Array<string>>([])\n  const cachedIds = createMutableStore<Array<string>>([])\n\n  // 1st order derived stores\n  const matches = createReadonlyStore(() =>\n    readPoolMatches(matchStores, matchesId.get()),\n  )\n  const pendingMatches = createReadonlyStore(() =>\n    readPoolMatches(pendingMatchStores, pendingIds.get()),\n  )\n  const cachedMatches = createReadonlyStore(() =>\n    readPoolMatches(cachedMatchStores, cachedIds.get()),\n  )\n  const firstId = createReadonlyStore(() => matchesId.get()[0])\n  const hasPending = createReadonlyStore(() =>\n    matchesId.get().some((matchId) => {\n      const store = matchStores.get(matchId)\n      return store?.get().status === 'pending'\n    }),\n  )\n  const matchRouteDeps = createReadonlyStore(() => ({\n    locationHref: location.get().href,\n    resolvedLocationHref: resolvedLocation.get()?.href,\n    status: status.get(),\n  }))\n\n  // compatibility \"big\" state store\n  const __store = createReadonlyStore(() => ({\n    status: status.get(),\n    loadedAt: loadedAt.get(),\n    isLoading: isLoading.get(),\n    isTransitioning: isTransitioning.get(),\n    matches: matches.get(),\n    location: location.get(),\n    resolvedLocation: resolvedLocation.get(),\n    statusCode: statusCode.get(),\n    redirect: redirect.get(),\n  }))\n\n  // Per-routeId computed store cache.\n  // Each entry resolves routeId → match state through the signal graph,\n  // giving consumers a single store to subscribe to instead of the\n  // two-level byRouteId → matchStore pattern.\n  //\n  // 64 max size is arbitrary, this is only for active matches anyway so\n  // it should be plenty. And we already have a 32 limit due to route\n  // matching bitmask anyway.\n  const matchStoreByRouteIdCache = createLRUCache<\n    string,\n    RouterReadableStore<AnyRouteMatch | undefined>\n  >(64)\n\n  function getRouteMatchStore(\n    routeId: string,\n  ): RouterReadableStore<AnyRouteMatch | undefined> {\n    let cached = matchStoreByRouteIdCache.get(routeId)\n    if (!cached) {\n      cached = createReadonlyStore(() => {\n        // Reading matchesId.get() tracks it as a dependency.\n        // When matchesId changes (navigation), this computed re-evaluates.\n        const ids = matchesId.get()\n        for (const id of ids) {\n          const matchStore = matchStores.get(id)\n          if (matchStore && matchStore.routeId === routeId) {\n            // Reading matchStore.get() tracks it as a dependency.\n            // When the match store's state changes, this re-evaluates.\n            return matchStore.get()\n          }\n        }\n        return undefined\n      })\n      matchStoreByRouteIdCache.set(routeId, cached)\n    }\n    return cached\n  }\n\n  const store = {\n    // atoms\n    status,\n    loadedAt,\n    isLoading,\n    isTransitioning,\n    location,\n    resolvedLocation,\n    statusCode,\n    redirect,\n    matchesId,\n    pendingIds,\n    cachedIds,\n\n    // derived\n    matches,\n    pendingMatches,\n    cachedMatches,\n    firstId,\n    hasPending,\n    matchRouteDeps,\n\n    // non-reactive state\n    matchStores,\n    pendingMatchStores,\n    cachedMatchStores,\n\n    // compatibility \"big\" state\n    __store,\n\n    // per-key computed stores\n    getRouteMatchStore,\n\n    // methods\n    setMatches,\n    setPending,\n    setCached,\n  }\n\n  // initialize the active matches\n  setMatches(initialState.matches as Array<AnyRouteMatch>)\n  init?.(store)\n\n  // setters to update non-reactive utilities in sync with the reactive stores\n  function setMatches(nextMatches: Array<AnyRouteMatch>) {\n    reconcileMatchPool(\n      nextMatches,\n      matchStores,\n      matchesId,\n      createMutableStore,\n      batch,\n    )\n  }\n\n  function setPending(nextMatches: Array<AnyRouteMatch>) {\n    reconcileMatchPool(\n      nextMatches,\n      pendingMatchStores,\n      pendingIds,\n      createMutableStore,\n      batch,\n    )\n  }\n\n  function setCached(nextMatches: Array<AnyRouteMatch>) {\n    reconcileMatchPool(\n      nextMatches,\n      cachedMatchStores,\n      cachedIds,\n      createMutableStore,\n      batch,\n    )\n  }\n\n  return store\n}\n\nfunction readPoolMatches(\n  pool: Map<string, MatchStore>,\n  ids: Array<string>,\n): Array<AnyRouteMatch> {\n  const matches: Array<AnyRouteMatch> = []\n  for (const id of ids) {\n    const matchStore = pool.get(id)\n    if (matchStore) {\n      matches.push(matchStore.get())\n    }\n  }\n  return matches\n}\n\nfunction reconcileMatchPool(\n  nextMatches: Array<AnyRouteMatch>,\n  pool: Map<string, MatchStore>,\n  idStore: RouterWritableStore<Array<string>>,\n  createMutableStore: MutableStoreFactory,\n  batch: RouterBatchFn,\n): void {\n  const nextIds = nextMatches.map((d) => d.id)\n  const nextIdSet = new Set(nextIds)\n\n  batch(() => {\n    for (const id of pool.keys()) {\n      if (!nextIdSet.has(id)) {\n        pool.delete(id)\n      }\n    }\n\n    for (const nextMatch of nextMatches) {\n      const existing = pool.get(nextMatch.id)\n      if (!existing) {\n        const matchStore = createMutableStore(nextMatch) as MatchStore\n        matchStore.routeId = nextMatch.routeId\n        pool.set(nextMatch.id, matchStore)\n        continue\n      }\n\n      existing.routeId = nextMatch.routeId\n      if (existing.get() !== nextMatch) {\n        existing.set(nextMatch)\n      }\n    }\n\n    if (!arraysEqual(idStore.get(), nextIds)) {\n      idStore.set(nextIds)\n    }\n  })\n}\n"],"mappings":"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"}