{"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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"}