@tanstack/router-core 1.171.14 → 1.171.16-pre.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (139) hide show
  1. package/dist/cjs/Matches.cjs.map +1 -1
  2. package/dist/cjs/Matches.d.cts +3 -12
  3. package/dist/cjs/await-signal.cjs +21 -0
  4. package/dist/cjs/await-signal.cjs.map +1 -0
  5. package/dist/cjs/await-signal.d.cts +1 -0
  6. package/dist/cjs/index.cjs +3 -1
  7. package/dist/cjs/index.d.cts +3 -2
  8. package/dist/cjs/isServer/client.cjs +2 -0
  9. package/dist/cjs/isServer/client.cjs.map +1 -1
  10. package/dist/cjs/isServer/client.d.cts +1 -0
  11. package/dist/cjs/isServer/development.cjs +2 -0
  12. package/dist/cjs/isServer/development.cjs.map +1 -1
  13. package/dist/cjs/isServer/development.d.cts +1 -0
  14. package/dist/cjs/isServer/server.cjs +2 -0
  15. package/dist/cjs/isServer/server.cjs.map +1 -1
  16. package/dist/cjs/isServer/server.d.cts +1 -0
  17. package/dist/cjs/load-client.cjs +1470 -0
  18. package/dist/cjs/load-client.cjs.map +1 -0
  19. package/dist/cjs/load-client.d.cts +147 -0
  20. package/dist/cjs/load-server.cjs +565 -0
  21. package/dist/cjs/load-server.cjs.map +1 -0
  22. package/dist/cjs/load-server.d.cts +16 -0
  23. package/dist/cjs/redirect.cjs.map +1 -1
  24. package/dist/cjs/redirect.d.cts +0 -1
  25. package/dist/cjs/route.cjs.map +1 -1
  26. package/dist/cjs/router.cjs +187 -445
  27. package/dist/cjs/router.cjs.map +1 -1
  28. package/dist/cjs/router.d.cts +62 -52
  29. package/dist/cjs/scroll-restoration.cjs +27 -34
  30. package/dist/cjs/scroll-restoration.cjs.map +1 -1
  31. package/dist/cjs/ssr/client.cjs +2 -2
  32. package/dist/cjs/ssr/createRequestHandler.cjs +60 -9
  33. package/dist/cjs/ssr/createRequestHandler.cjs.map +1 -1
  34. package/dist/cjs/ssr/createRequestHandler.d.cts +1 -0
  35. package/dist/cjs/ssr/handlerCallback.cjs +49 -4
  36. package/dist/cjs/ssr/handlerCallback.cjs.map +1 -1
  37. package/dist/cjs/ssr/handlerCallback.d.cts +3 -0
  38. package/dist/cjs/ssr/server.cjs +4 -0
  39. package/dist/cjs/ssr/server.d.cts +2 -2
  40. package/dist/cjs/ssr/ssr-client.d.cts +1 -10
  41. package/dist/cjs/ssr/ssr-match-id.cjs +2 -2
  42. package/dist/cjs/ssr/ssr-match-id.cjs.map +1 -1
  43. package/dist/cjs/ssr/ssr-server.cjs +6 -6
  44. package/dist/cjs/ssr/ssr-server.cjs.map +1 -1
  45. package/dist/cjs/ssr/transformStreamWithRouter.cjs +27 -1
  46. package/dist/cjs/ssr/transformStreamWithRouter.cjs.map +1 -1
  47. package/dist/cjs/ssr/transformStreamWithRouter.d.cts +2 -0
  48. package/dist/cjs/ssr/types.d.cts +0 -1
  49. package/dist/cjs/stores.cjs +30 -104
  50. package/dist/cjs/stores.cjs.map +1 -1
  51. package/dist/cjs/stores.d.cts +7 -32
  52. package/dist/cjs/utils.cjs +0 -4
  53. package/dist/cjs/utils.cjs.map +1 -1
  54. package/dist/esm/Matches.d.ts +3 -12
  55. package/dist/esm/Matches.js.map +1 -1
  56. package/dist/esm/await-signal.d.ts +1 -0
  57. package/dist/esm/await-signal.js +21 -0
  58. package/dist/esm/await-signal.js.map +1 -0
  59. package/dist/esm/index.d.ts +3 -2
  60. package/dist/esm/index.js +3 -2
  61. package/dist/esm/isServer/client.d.ts +1 -0
  62. package/dist/esm/isServer/client.js +2 -1
  63. package/dist/esm/isServer/client.js.map +1 -1
  64. package/dist/esm/isServer/development.d.ts +1 -0
  65. package/dist/esm/isServer/development.js +2 -1
  66. package/dist/esm/isServer/development.js.map +1 -1
  67. package/dist/esm/isServer/server.d.ts +1 -0
  68. package/dist/esm/isServer/server.js +2 -1
  69. package/dist/esm/isServer/server.js.map +1 -1
  70. package/dist/esm/load-client.d.ts +147 -0
  71. package/dist/esm/load-client.js +1462 -0
  72. package/dist/esm/load-client.js.map +1 -0
  73. package/dist/esm/load-server.d.ts +16 -0
  74. package/dist/esm/load-server.js +565 -0
  75. package/dist/esm/load-server.js.map +1 -0
  76. package/dist/esm/redirect.d.ts +0 -1
  77. package/dist/esm/redirect.js.map +1 -1
  78. package/dist/esm/route.js.map +1 -1
  79. package/dist/esm/router.d.ts +62 -52
  80. package/dist/esm/router.js +188 -446
  81. package/dist/esm/router.js.map +1 -1
  82. package/dist/esm/scroll-restoration.js +27 -34
  83. package/dist/esm/scroll-restoration.js.map +1 -1
  84. package/dist/esm/ssr/client.js +1 -1
  85. package/dist/esm/ssr/createRequestHandler.d.ts +1 -0
  86. package/dist/esm/ssr/createRequestHandler.js +61 -11
  87. package/dist/esm/ssr/createRequestHandler.js.map +1 -1
  88. package/dist/esm/ssr/handlerCallback.d.ts +3 -0
  89. package/dist/esm/ssr/handlerCallback.js +47 -5
  90. package/dist/esm/ssr/handlerCallback.js.map +1 -1
  91. package/dist/esm/ssr/server.d.ts +2 -2
  92. package/dist/esm/ssr/server.js +3 -3
  93. package/dist/esm/ssr/ssr-client.d.ts +1 -10
  94. package/dist/esm/ssr/ssr-match-id.js +2 -2
  95. package/dist/esm/ssr/ssr-match-id.js.map +1 -1
  96. package/dist/esm/ssr/ssr-server.js +6 -6
  97. package/dist/esm/ssr/ssr-server.js.map +1 -1
  98. package/dist/esm/ssr/transformStreamWithRouter.d.ts +2 -0
  99. package/dist/esm/ssr/transformStreamWithRouter.js +27 -1
  100. package/dist/esm/ssr/transformStreamWithRouter.js.map +1 -1
  101. package/dist/esm/ssr/types.d.ts +0 -1
  102. package/dist/esm/stores.d.ts +7 -32
  103. package/dist/esm/stores.js +30 -104
  104. package/dist/esm/stores.js.map +1 -1
  105. package/dist/esm/utils.js +1 -4
  106. package/dist/esm/utils.js.map +1 -1
  107. package/package.json +3 -3
  108. package/src/Matches.ts +6 -21
  109. package/src/await-signal.ts +27 -0
  110. package/src/index.ts +1 -3
  111. package/src/isServer/client.ts +1 -0
  112. package/src/isServer/development.ts +1 -0
  113. package/src/isServer/server.ts +1 -0
  114. package/src/load-client.ts +2701 -0
  115. package/src/load-server.ts +849 -0
  116. package/src/redirect.ts +0 -1
  117. package/src/route.ts +2 -11
  118. package/src/router.ts +380 -774
  119. package/src/scroll-restoration.ts +40 -56
  120. package/src/ssr/createRequestHandler.ts +109 -16
  121. package/src/ssr/handlerCallback.ts +76 -10
  122. package/src/ssr/server.ts +4 -1
  123. package/src/ssr/ssr-client.ts +1 -310
  124. package/src/ssr/ssr-match-id.ts +11 -2
  125. package/src/ssr/ssr-server.ts +9 -8
  126. package/src/ssr/transformStreamWithRouter.ts +37 -1
  127. package/src/ssr/types.ts +0 -1
  128. package/src/stores.ts +46 -208
  129. package/dist/cjs/load-matches.cjs +0 -658
  130. package/dist/cjs/load-matches.cjs.map +0 -1
  131. package/dist/cjs/load-matches.d.cts +0 -18
  132. package/dist/cjs/ssr/ssr-client.cjs +0 -183
  133. package/dist/cjs/ssr/ssr-client.cjs.map +0 -1
  134. package/dist/esm/load-matches.d.ts +0 -18
  135. package/dist/esm/load-matches.js +0 -656
  136. package/dist/esm/load-matches.js.map +0 -1
  137. package/dist/esm/ssr/ssr-client.js +0 -183
  138. package/dist/esm/ssr/ssr-client.js.map +0 -1
  139. package/src/load-matches.ts +0 -1278
package/src/stores.ts CHANGED
@@ -1,11 +1,9 @@
1
- import { createLRUCache } from './lru-cache'
2
1
  import { arraysEqual, functionalUpdate } from './utils'
3
2
 
4
3
  import type { AnyRoute } from './route'
5
4
  import type { RouterState } from './router'
6
5
  import type { FullSearchSchema } from './routeInfo'
7
6
  import type { ParsedLocation } from './location'
8
- import type { AnyRedirect } from './redirect'
9
7
  import type { AnyRouteMatch } from './Matches'
10
8
 
11
9
  export interface RouterReadableStore<TValue> {
@@ -34,12 +32,9 @@ export type StoreConfig = {
34
32
  createMutableStore: MutableStoreFactory
35
33
  createReadonlyStore: ReadonlyStoreFactory
36
34
  batch: RouterBatchFn
37
- init?: (stores: RouterStores<AnyRoute>) => void
38
35
  }
39
36
 
40
- type MatchStore = RouterWritableStore<AnyRouteMatch> & {
41
- routeId?: string
42
- }
37
+ type MatchStore = RouterWritableStore<AnyRouteMatch | undefined>
43
38
  type ReadableStore<TValue> = RouterReadableStore<TValue>
44
39
 
45
40
  /** SSR non-reactive createMutableStore */
@@ -71,272 +66,115 @@ export function createNonReactiveReadonlyStore<TValue>(
71
66
 
72
67
  export interface RouterStores<in out TRouteTree extends AnyRoute> {
73
68
  status: RouterWritableStore<RouterState<TRouteTree>['status']>
74
- loadedAt: RouterWritableStore<number>
75
- isLoading: RouterWritableStore<boolean>
76
- isTransitioning: RouterWritableStore<boolean>
77
69
  location: RouterWritableStore<ParsedLocation<FullSearchSchema<TRouteTree>>>
78
70
  resolvedLocation: RouterWritableStore<
79
71
  ParsedLocation<FullSearchSchema<TRouteTree>> | undefined
80
72
  >
81
- statusCode: RouterWritableStore<number>
82
- redirect: RouterWritableStore<AnyRedirect | undefined>
83
- matchesId: RouterWritableStore<Array<string>>
84
- pendingIds: RouterWritableStore<Array<string>>
85
- /** @internal */
86
- cachedIds: RouterWritableStore<Array<string>>
73
+ ids: RouterWritableStore<Array<string>>
87
74
  matches: ReadableStore<Array<AnyRouteMatch>>
88
- pendingMatches: ReadableStore<Array<AnyRouteMatch>>
89
- cachedMatches: ReadableStore<Array<AnyRouteMatch>>
90
- firstId: ReadableStore<string | undefined>
91
- hasPending: ReadableStore<boolean>
92
- matchRouteDeps: ReadableStore<{
93
- locationHref: string
94
- resolvedLocationHref: string | undefined
95
- status: RouterState<TRouteTree>['status']
96
- }>
97
75
  __store: RouterReadableStore<RouterState<TRouteTree>>
98
76
 
99
- matchStores: Map<string, MatchStore>
100
- pendingMatchStores: Map<string, MatchStore>
101
- cachedMatchStores: Map<string, MatchStore>
77
+ byRoute: Map<string, MatchStore>
102
78
 
103
79
  /**
104
- * Get a computed store that resolves a routeId to its current match state.
105
- * Returns the same cached store instance for repeated calls with the same key.
106
- * The computed depends on matchesId + the individual match store, so
107
- * subscribers are only notified when the resolved match state changes.
80
+ * Get the stable atom for a route's presented match. The atom remains in the
81
+ * pool when the route leaves and contains `undefined` until it re-enters.
108
82
  */
109
- getRouteMatchStore: (
83
+ getMatchStore: (
110
84
  routeId: string,
111
85
  ) => RouterReadableStore<AnyRouteMatch | undefined>
112
86
 
113
87
  setMatches: (nextMatches: Array<AnyRouteMatch>) => void
114
- setPending: (nextMatches: Array<AnyRouteMatch>) => void
115
- setCached: (nextMatches: Array<AnyRouteMatch>) => void
116
88
  }
117
89
 
118
90
  export function createRouterStores<TRouteTree extends AnyRoute>(
119
- initialState: RouterState<TRouteTree>,
91
+ initialLocation: RouterState<TRouteTree>['location'],
120
92
  config: StoreConfig,
121
93
  ): RouterStores<TRouteTree> {
122
- const { createMutableStore, createReadonlyStore, batch, init } = config
94
+ const { createMutableStore, createReadonlyStore, batch } = config
123
95
 
124
96
  // non reactive utilities
125
- const matchStores = new Map<string, MatchStore>()
126
- const pendingMatchStores = new Map<string, MatchStore>()
127
- const cachedMatchStores = new Map<string, MatchStore>()
97
+ const byRoute = new Map<string, MatchStore>()
128
98
 
129
99
  // atoms
130
- const status = createMutableStore(initialState.status)
131
- const loadedAt = createMutableStore(initialState.loadedAt)
132
- const isLoading = createMutableStore(initialState.isLoading)
133
- const isTransitioning = createMutableStore(initialState.isTransitioning)
134
- const location = createMutableStore(initialState.location)
135
- const resolvedLocation = createMutableStore(initialState.resolvedLocation)
136
- const statusCode = createMutableStore(initialState.statusCode)
137
- const redirect = createMutableStore(initialState.redirect)
138
- const matchesId = createMutableStore<Array<string>>([])
139
- const pendingIds = createMutableStore<Array<string>>([])
140
- const cachedIds = createMutableStore<Array<string>>([])
100
+ const status = createMutableStore<RouterState<TRouteTree>['status']>('idle')
101
+ const location = createMutableStore(initialLocation)
102
+ const resolvedLocation =
103
+ createMutableStore<RouterState<TRouteTree>['resolvedLocation']>(undefined)
104
+ const ids = createMutableStore<Array<string>>([])
141
105
 
142
106
  // 1st order derived stores
143
107
  const matches = createReadonlyStore(() =>
144
- readPoolMatches(matchStores, matchesId.get()),
145
- )
146
- const pendingMatches = createReadonlyStore(() =>
147
- readPoolMatches(pendingMatchStores, pendingIds.get()),
148
- )
149
- const cachedMatches = createReadonlyStore(() =>
150
- readPoolMatches(cachedMatchStores, cachedIds.get()),
151
- )
152
- const firstId = createReadonlyStore(() => matchesId.get()[0])
153
- const hasPending = createReadonlyStore(() =>
154
- matchesId.get().some((matchId) => {
155
- const store = matchStores.get(matchId)
156
- return store?.get().status === 'pending'
157
- }),
108
+ ids.get().map((id) => byRoute.get(id)!.get()!),
158
109
  )
159
- const matchRouteDeps = createReadonlyStore(() => ({
160
- locationHref: location.get().href,
161
- resolvedLocationHref: resolvedLocation.get()?.href,
162
- status: status.get(),
163
- }))
164
110
 
165
111
  // compatibility "big" state store
166
112
  const __store = createReadonlyStore(() => ({
167
113
  status: status.get(),
168
- loadedAt: loadedAt.get(),
169
- isLoading: isLoading.get(),
170
- isTransitioning: isTransitioning.get(),
114
+ isLoading: status.get() === 'pending',
171
115
  matches: matches.get(),
172
116
  location: location.get(),
173
117
  resolvedLocation: resolvedLocation.get(),
174
- statusCode: statusCode.get(),
175
- redirect: redirect.get(),
176
118
  }))
177
119
 
178
- // Per-routeId computed store cache.
179
- // Each entry resolves routeId → match state through the signal graph,
180
- // giving consumers a single store to subscribe to instead of the
181
- // two-level byRouteId matchStore pattern.
182
- //
183
- // 64 max size is arbitrary, this is only for active matches anyway so
184
- // it should be plenty. And we already have a 32 limit due to route
185
- // matching bitmask anyway.
186
- const matchStoreByRouteIdCache = createLRUCache<
187
- string,
188
- RouterReadableStore<AnyRouteMatch | undefined>
189
- >(64)
190
-
191
- function getRouteMatchStore(
192
- routeId: string,
193
- ): RouterReadableStore<AnyRouteMatch | undefined> {
194
- let cached = matchStoreByRouteIdCache.get(routeId)
195
- if (!cached) {
196
- cached = createReadonlyStore(() => {
197
- // Reading matchesId.get() tracks it as a dependency.
198
- // When matchesId changes (navigation), this computed re-evaluates.
199
- const ids = matchesId.get()
200
- for (const id of ids) {
201
- const matchStore = matchStores.get(id)
202
- if (matchStore && matchStore.routeId === routeId) {
203
- // Reading matchStore.get() tracks it as a dependency.
204
- // When the match store's state changes, this re-evaluates.
205
- return matchStore.get()
206
- }
207
- }
208
- return undefined
209
- })
210
- matchStoreByRouteIdCache.set(routeId, cached)
120
+ function getMatchStore(routeId: string): MatchStore {
121
+ let matchStore = byRoute.get(routeId)
122
+ if (!matchStore) {
123
+ matchStore = createMutableStore<AnyRouteMatch | undefined>(undefined)
124
+ byRoute.set(routeId, matchStore)
211
125
  }
212
- return cached
126
+ return matchStore
213
127
  }
214
128
 
215
129
  const store = {
216
130
  // atoms
217
131
  status,
218
- loadedAt,
219
- isLoading,
220
- isTransitioning,
221
132
  location,
222
133
  resolvedLocation,
223
- statusCode,
224
- redirect,
225
- matchesId,
226
- pendingIds,
227
- cachedIds,
134
+ ids,
228
135
 
229
136
  // derived
230
137
  matches,
231
- pendingMatches,
232
- cachedMatches,
233
- firstId,
234
- hasPending,
235
- matchRouteDeps,
236
138
 
237
139
  // non-reactive state
238
- matchStores,
239
- pendingMatchStores,
240
- cachedMatchStores,
140
+ byRoute,
241
141
 
242
142
  // compatibility "big" state
243
143
  __store,
244
144
 
245
- // per-key computed stores
246
- getRouteMatchStore,
145
+ // stable per-route presentation atoms
146
+ getMatchStore,
247
147
 
248
148
  // methods
249
149
  setMatches,
250
- setPending,
251
- setCached,
252
150
  }
253
151
 
254
- // initialize the active matches
255
- setMatches(initialState.matches as Array<AnyRouteMatch>)
256
- init?.(store)
257
-
258
152
  // setters to update non-reactive utilities in sync with the reactive stores
259
153
  function setMatches(nextMatches: Array<AnyRouteMatch>) {
260
- reconcileMatchPool(
261
- nextMatches,
262
- matchStores,
263
- matchesId,
264
- createMutableStore,
265
- batch,
266
- )
267
- }
268
-
269
- function setPending(nextMatches: Array<AnyRouteMatch>) {
270
- reconcileMatchPool(
271
- nextMatches,
272
- pendingMatchStores,
273
- pendingIds,
274
- createMutableStore,
275
- batch,
276
- )
277
- }
278
-
279
- function setCached(nextMatches: Array<AnyRouteMatch>) {
280
- reconcileMatchPool(
281
- nextMatches,
282
- cachedMatchStores,
283
- cachedIds,
284
- createMutableStore,
285
- batch,
286
- )
287
- }
288
-
289
- return store
290
- }
291
-
292
- function readPoolMatches(
293
- pool: Map<string, MatchStore>,
294
- ids: Array<string>,
295
- ): Array<AnyRouteMatch> {
296
- const matches: Array<AnyRouteMatch> = []
297
- for (const id of ids) {
298
- const matchStore = pool.get(id)
299
- if (matchStore) {
300
- matches.push(matchStore.get())
301
- }
302
- }
303
- return matches
304
- }
305
-
306
- function reconcileMatchPool(
307
- nextMatches: Array<AnyRouteMatch>,
308
- pool: Map<string, MatchStore>,
309
- idStore: RouterWritableStore<Array<string>>,
310
- createMutableStore: MutableStoreFactory,
311
- batch: RouterBatchFn,
312
- ): void {
313
- const nextIds = nextMatches.map((d) => d.id)
314
- const nextIdSet = new Set(nextIds)
315
-
316
- batch(() => {
317
- for (const id of pool.keys()) {
318
- if (!nextIdSet.has(id)) {
319
- pool.delete(id)
154
+ const previousIds = ids.get()
155
+ const nextIds = nextMatches.map((match) => match.routeId)
156
+
157
+ batch(() => {
158
+ // Publish lane membership first so framework trees reconcile departures
159
+ // before observers of a leaving route receive its tombstone.
160
+ if (!arraysEqual(previousIds, nextIds)) {
161
+ ids.set(nextIds)
320
162
  }
321
- }
322
163
 
323
- for (const nextMatch of nextMatches) {
324
- const existing = pool.get(nextMatch.id)
325
- if (!existing) {
326
- const matchStore = createMutableStore(nextMatch) as MatchStore
327
- matchStore.routeId = nextMatch.routeId
328
- pool.set(nextMatch.id, matchStore)
329
- continue
164
+ for (const id of previousIds) {
165
+ if (!nextIds.includes(id)) {
166
+ byRoute.get(id)!.set(() => undefined)
167
+ }
330
168
  }
331
169
 
332
- existing.routeId = nextMatch.routeId
333
- if (existing.get() !== nextMatch) {
334
- existing.set(nextMatch)
170
+ for (const nextMatch of nextMatches) {
171
+ const matchStore = getMatchStore(nextMatch.routeId)
172
+ if (matchStore.get() !== nextMatch) {
173
+ matchStore.set(nextMatch)
174
+ }
335
175
  }
336
- }
176
+ })
177
+ }
337
178
 
338
- if (!arraysEqual(idStore.get(), nextIds)) {
339
- idStore.set(nextIds)
340
- }
341
- })
179
+ return store
342
180
  }