{"version":3,"file":"stores.cjs","names":[],"sources":["../../src/stores.ts"],"sourcesContent":["import { createLRUCache } from './lru-cache'\nimport { arraysEqual } 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  readonly state: TValue\n}\n\nexport interface RouterWritableStore<\n  TValue,\n> extends RouterReadableStore<TValue> {\n  setState: (updater: (prev: TValue) => 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 state() {\n      return value\n    },\n    setState(updater: (prev: TValue) => TValue) {\n      value = updater(value)\n    },\n  }\n}\n\n/** SSR non-reactive createReadonlyStore */\nexport function createNonReactiveReadonlyStore<TValue>(\n  read: () => TValue,\n): RouterReadableStore<TValue> {\n  return {\n    get state() {\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  pendingMatchesId: RouterWritableStore<Array<string>>\n  /** @internal */\n  cachedMatchesId: RouterWritableStore<Array<string>>\n  activeMatchesSnapshot: ReadableStore<Array<AnyRouteMatch>>\n  pendingMatchesSnapshot: ReadableStore<Array<AnyRouteMatch>>\n  cachedMatchesSnapshot: ReadableStore<Array<AnyRouteMatch>>\n  firstMatchId: ReadableStore<string | undefined>\n  hasPendingMatches: ReadableStore<boolean>\n  matchRouteReactivity: ReadableStore<{\n    locationHref: string\n    resolvedLocationHref: string | undefined\n    status: RouterState<TRouteTree>['status']\n  }>\n  __store: RouterReadableStore<RouterState<TRouteTree>>\n\n  activeMatchStoresById: Map<string, MatchStore>\n  pendingMatchStoresById: Map<string, MatchStore>\n  cachedMatchStoresById: 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  getMatchStoreByRouteId: (\n    routeId: string,\n  ) => RouterReadableStore<AnyRouteMatch | undefined>\n\n  setActiveMatches: (nextMatches: Array<AnyRouteMatch>) => void\n  setPendingMatches: (nextMatches: Array<AnyRouteMatch>) => void\n  setCachedMatches: (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 activeMatchStoresById = new Map<string, MatchStore>()\n  const pendingMatchStoresById = new Map<string, MatchStore>()\n  const cachedMatchStoresById = 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 pendingMatchesId = createMutableStore<Array<string>>([])\n  const cachedMatchesId = createMutableStore<Array<string>>([])\n\n  // 1st order derived stores\n  const activeMatchesSnapshot = createReadonlyStore(() =>\n    readPoolMatches(activeMatchStoresById, matchesId.state),\n  )\n  const pendingMatchesSnapshot = createReadonlyStore(() =>\n    readPoolMatches(pendingMatchStoresById, pendingMatchesId.state),\n  )\n  const cachedMatchesSnapshot = createReadonlyStore(() =>\n    readPoolMatches(cachedMatchStoresById, cachedMatchesId.state),\n  )\n  const firstMatchId = createReadonlyStore(() => matchesId.state[0])\n  const hasPendingMatches = createReadonlyStore(() =>\n    matchesId.state.some((matchId) => {\n      const store = activeMatchStoresById.get(matchId)\n      return store?.state.status === 'pending'\n    }),\n  )\n  const matchRouteReactivity = createReadonlyStore(() => ({\n    locationHref: location.state.href,\n    resolvedLocationHref: resolvedLocation.state?.href,\n    status: status.state,\n  }))\n\n  // compatibility \"big\" state store\n  const __store = createReadonlyStore(() => ({\n    status: status.state,\n    loadedAt: loadedAt.state,\n    isLoading: isLoading.state,\n    isTransitioning: isTransitioning.state,\n    matches: activeMatchesSnapshot.state,\n    location: location.state,\n    resolvedLocation: resolvedLocation.state,\n    statusCode: statusCode.state,\n    redirect: redirect.state,\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 getMatchStoreByRouteId(\n    routeId: string,\n  ): RouterReadableStore<AnyRouteMatch | undefined> {\n    let cached = matchStoreByRouteIdCache.get(routeId)\n    if (!cached) {\n      cached = createReadonlyStore(() => {\n        // Reading matchesId.state tracks it as a dependency.\n        // When matchesId changes (navigation), this computed re-evaluates.\n        const ids = matchesId.state\n        for (const id of ids) {\n          const matchStore = activeMatchStoresById.get(id)\n          if (matchStore && matchStore.routeId === routeId) {\n            // Reading matchStore.state tracks it as a dependency.\n            // When the match store's state changes, this re-evaluates.\n            return matchStore.state\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    pendingMatchesId,\n    cachedMatchesId,\n\n    // derived\n    activeMatchesSnapshot,\n    pendingMatchesSnapshot,\n    cachedMatchesSnapshot,\n    firstMatchId,\n    hasPendingMatches,\n    matchRouteReactivity,\n\n    // non-reactive state\n    activeMatchStoresById,\n    pendingMatchStoresById,\n    cachedMatchStoresById,\n\n    // compatibility \"big\" state\n    __store,\n\n    // per-key computed stores\n    getMatchStoreByRouteId,\n\n    // methods\n    setActiveMatches,\n    setPendingMatches,\n    setCachedMatches,\n  }\n\n  // initialize the active matches\n  setActiveMatches(initialState.matches as Array<AnyRouteMatch>)\n  init?.(store)\n\n  // setters to update non-reactive utilities in sync with the reactive stores\n  function setActiveMatches(nextMatches: Array<AnyRouteMatch>) {\n    reconcileMatchPool(\n      nextMatches,\n      activeMatchStoresById,\n      matchesId,\n      createMutableStore,\n      batch,\n    )\n  }\n\n  function setPendingMatches(nextMatches: Array<AnyRouteMatch>) {\n    reconcileMatchPool(\n      nextMatches,\n      pendingMatchStoresById,\n      pendingMatchesId,\n      createMutableStore,\n      batch,\n    )\n  }\n\n  function setCachedMatches(nextMatches: Array<AnyRouteMatch>) {\n    reconcileMatchPool(\n      nextMatches,\n      cachedMatchStoresById,\n      cachedMatchesId,\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.state)\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.state !== nextMatch) {\n        existing.setState(() => nextMatch)\n      }\n    }\n\n    if (!arraysEqual(idStore.state, nextIds)) {\n      idStore.setState(() => nextIds)\n    }\n  })\n}\n"],"mappings":"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"}