@tanstack/router-core 1.167.4 → 1.168.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 (59) hide show
  1. package/dist/cjs/index.cjs +3 -0
  2. package/dist/cjs/index.d.cts +2 -0
  3. package/dist/cjs/load-matches.cjs +14 -9
  4. package/dist/cjs/load-matches.cjs.map +1 -1
  5. package/dist/cjs/router.cjs +135 -151
  6. package/dist/cjs/router.cjs.map +1 -1
  7. package/dist/cjs/router.d.cts +16 -10
  8. package/dist/cjs/scroll-restoration.cjs +5 -4
  9. package/dist/cjs/scroll-restoration.cjs.map +1 -1
  10. package/dist/cjs/ssr/createRequestHandler.cjs +2 -2
  11. package/dist/cjs/ssr/createRequestHandler.cjs.map +1 -1
  12. package/dist/cjs/ssr/ssr-client.cjs +14 -17
  13. package/dist/cjs/ssr/ssr-client.cjs.map +1 -1
  14. package/dist/cjs/ssr/ssr-server.cjs +1 -1
  15. package/dist/cjs/ssr/ssr-server.cjs.map +1 -1
  16. package/dist/cjs/stores.cjs +148 -0
  17. package/dist/cjs/stores.cjs.map +1 -0
  18. package/dist/cjs/stores.d.cts +70 -0
  19. package/dist/cjs/utils.cjs +7 -0
  20. package/dist/cjs/utils.cjs.map +1 -1
  21. package/dist/cjs/utils.d.cts +1 -0
  22. package/dist/esm/index.d.ts +2 -0
  23. package/dist/esm/index.js +2 -1
  24. package/dist/esm/load-matches.js +14 -9
  25. package/dist/esm/load-matches.js.map +1 -1
  26. package/dist/esm/router.d.ts +16 -10
  27. package/dist/esm/router.js +135 -151
  28. package/dist/esm/router.js.map +1 -1
  29. package/dist/esm/scroll-restoration.js +5 -4
  30. package/dist/esm/scroll-restoration.js.map +1 -1
  31. package/dist/esm/ssr/createRequestHandler.js +2 -2
  32. package/dist/esm/ssr/createRequestHandler.js.map +1 -1
  33. package/dist/esm/ssr/ssr-client.js +14 -17
  34. package/dist/esm/ssr/ssr-client.js.map +1 -1
  35. package/dist/esm/ssr/ssr-server.js +1 -1
  36. package/dist/esm/ssr/ssr-server.js.map +1 -1
  37. package/dist/esm/stores.d.ts +70 -0
  38. package/dist/esm/stores.js +146 -0
  39. package/dist/esm/stores.js.map +1 -0
  40. package/dist/esm/utils.d.ts +1 -0
  41. package/dist/esm/utils.js +7 -1
  42. package/dist/esm/utils.js.map +1 -1
  43. package/package.json +3 -3
  44. package/src/index.ts +11 -0
  45. package/src/load-matches.ts +23 -11
  46. package/src/router.ts +238 -252
  47. package/src/scroll-restoration.ts +6 -5
  48. package/src/ssr/createRequestHandler.ts +5 -4
  49. package/src/ssr/ssr-client.ts +17 -18
  50. package/src/ssr/ssr-server.ts +1 -1
  51. package/src/stores.ts +342 -0
  52. package/src/utils.ts +9 -0
  53. package/dist/cjs/utils/batch.cjs +0 -16
  54. package/dist/cjs/utils/batch.cjs.map +0 -1
  55. package/dist/cjs/utils/batch.d.cts +0 -1
  56. package/dist/esm/utils/batch.d.ts +0 -1
  57. package/dist/esm/utils/batch.js +0 -15
  58. package/dist/esm/utils/batch.js.map +0 -1
  59. package/src/utils/batch.ts +0 -18
@@ -262,7 +262,7 @@ export function setupScrollRestoration(router: AnyRouter, force?: boolean) {
262
262
  // // console.log('mutation observer restoreScroll')
263
263
  // restoreScroll(
264
264
  // storageKey,
265
- // getKey(router.state.location),
265
+ // getKey(router.stores.location.state),
266
266
  // router.options.scrollRestorationBehavior,
267
267
  // )
268
268
  // })
@@ -306,7 +306,7 @@ export function setupScrollRestoration(router: AnyRouter, force?: boolean) {
306
306
  }
307
307
  }
308
308
 
309
- const restoreKey = getKey(router.state.location)
309
+ const restoreKey = getKey(router.stores.location.state)
310
310
 
311
311
  scrollRestorationCache.set((state) => {
312
312
  const keyEntry = (state[restoreKey] ||= {} as ScrollRestorationByElement)
@@ -389,11 +389,12 @@ export function setupScrollRestoration(router: AnyRouter, force?: boolean) {
389
389
  */
390
390
  export function handleHashScroll(router: AnyRouter) {
391
391
  if (typeof document !== 'undefined' && (document as any).querySelector) {
392
+ const location = router.stores.location.state
392
393
  const hashScrollIntoViewOptions =
393
- router.state.location.state.__hashScrollIntoViewOptions ?? true
394
+ location.state.__hashScrollIntoViewOptions ?? true
394
395
 
395
- if (hashScrollIntoViewOptions && router.state.location.hash !== '') {
396
- const el = document.getElementById(router.state.location.hash)
396
+ if (hashScrollIntoViewOptions && location.hash !== '') {
397
+ const el = document.getElementById(location.hash)
397
398
  if (el) {
398
399
  el.scrollIntoView(hashScrollIntoViewOptions)
399
400
  }
@@ -78,12 +78,13 @@ export function createRequestHandler<TRouter extends AnyRouter>({
78
78
  }
79
79
 
80
80
  function getRequestHeaders(opts: { router: AnyRouter }): Headers {
81
- const matchHeaders = opts.router.state.matches.map<AnyHeaders>(
82
- (match) => match.headers,
83
- )
81
+ const matchHeaders =
82
+ opts.router.stores.activeMatchesSnapshot.state.map<AnyHeaders>(
83
+ (match) => match.headers,
84
+ )
84
85
 
85
86
  // Handle Redirects
86
- const { redirect } = opts.router.state
87
+ const redirect = opts.router.stores.redirect.state
87
88
  if (redirect) {
88
89
  matchHeaders.push(redirect.headers)
89
90
  }
@@ -1,5 +1,4 @@
1
1
  import invariant from 'tiny-invariant'
2
- import { batch } from '../utils/batch'
3
2
  import { isNotFound } from '../not-found'
4
3
  import { createControlledPromise } from '../utils'
5
4
  import { hydrateSsrMatchId } from './ssr-match-id'
@@ -86,7 +85,7 @@ export async function hydrate(router: AnyRouter): Promise<any> {
86
85
  }
87
86
 
88
87
  // Hydrate the router state
89
- const matches = router.matchRoutes(router.state.location)
88
+ const matches = router.matchRoutes(router.stores.location.state)
90
89
 
91
90
  // kick off loading the route chunks
92
91
  const routeChunkPromise = Promise.all(
@@ -153,10 +152,7 @@ export async function hydrate(router: AnyRouter): Promise<any> {
153
152
  }
154
153
  })
155
154
 
156
- router.__store.setState((s) => ({
157
- ...s,
158
- matches,
159
- }))
155
+ router.stores.setActiveMatches(matches)
160
156
 
161
157
  // Allow the user to handle custom hydration data
162
158
  await router.options.hydrate?.(dehydratedData)
@@ -164,12 +160,14 @@ export async function hydrate(router: AnyRouter): Promise<any> {
164
160
  // now that all necessary data is hydrated:
165
161
  // 1) fully reconstruct the route context
166
162
  // 2) execute `head()` and `scripts()` for each match
163
+ const activeMatches = router.stores.activeMatchesSnapshot.state
164
+ const location = router.stores.location.state
167
165
  await Promise.all(
168
- router.state.matches.map(async (match) => {
166
+ activeMatches.map(async (match) => {
169
167
  try {
170
168
  const route = router.looseRoutesById[match.routeId]!
171
169
 
172
- const parentMatch = router.state.matches[match.index - 1]
170
+ const parentMatch = activeMatches[match.index - 1]
173
171
  const parentContext = parentMatch?.context ?? router.options.context
174
172
 
175
173
  // `context()` was already executed by `matchRoutes`, however route context was not yet fully reconstructed
@@ -180,11 +178,11 @@ export async function hydrate(router: AnyRouter): Promise<any> {
180
178
  deps: match.loaderDeps,
181
179
  params: match.params,
182
180
  context: parentContext ?? {},
183
- location: router.state.location,
181
+ location,
184
182
  navigate: (opts: any) =>
185
183
  router.navigate({
186
184
  ...opts,
187
- _fromLocation: router.state.location,
185
+ _fromLocation: location,
188
186
  }),
189
187
  buildLocation: router.buildLocation,
190
188
  cause: match.cause,
@@ -205,7 +203,7 @@ export async function hydrate(router: AnyRouter): Promise<any> {
205
203
 
206
204
  const assetContext = {
207
205
  ssr: router.options.ssr,
208
- matches: router.state.matches,
206
+ matches: activeMatches,
209
207
  match,
210
208
  params: match.params,
211
209
  loaderData: match.loaderData,
@@ -270,16 +268,17 @@ export async function hydrate(router: AnyRouter): Promise<any> {
270
268
  match._nonReactive.displayPendingPromise = loadPromise
271
269
 
272
270
  loadPromise.then(() => {
273
- batch(() => {
271
+ router.batch(() => {
274
272
  // ensure router is not in status 'pending' anymore
275
273
  // this usually happens in Transitioner but if loading synchronously resolves,
276
274
  // Transitioner won't be rendered while loading so it cannot track the change from loading:true to loading:false
277
- if (router.__store.state.status === 'pending') {
278
- router.__store.setState((s) => ({
279
- ...s,
280
- status: 'idle',
281
- resolvedLocation: s.location,
282
- }))
275
+ if (router.stores.status.state === 'pending') {
276
+ router.batch(() => {
277
+ router.stores.status.setState(() => 'idle')
278
+ router.stores.resolvedLocation.setState(
279
+ () => router.stores.location.state,
280
+ )
281
+ })
283
282
  }
284
283
  // hide the pending component once the load is finished
285
284
  router.updateMatch(match.id, (prev) => ({
@@ -202,7 +202,7 @@ export function attachRouterServerSsrUtils({
202
202
  },
203
203
  dehydrate: async () => {
204
204
  invariant(!_dehydrated, 'router is already dehydrated!')
205
- let matchesToDehydrate = router.state.matches
205
+ let matchesToDehydrate = router.stores.activeMatchesSnapshot.state
206
206
  if (router.isShell()) {
207
207
  // In SPA mode we only want to dehydrate the root match
208
208
  matchesToDehydrate = matchesToDehydrate.slice(0, 1)
package/src/stores.ts ADDED
@@ -0,0 +1,342 @@
1
+ import { createLRUCache } from './lru-cache'
2
+ import { arraysEqual } from './utils'
3
+
4
+ import type { AnyRoute } from './route'
5
+ import type { RouterState } from './router'
6
+ import type { FullSearchSchema } from './routeInfo'
7
+ import type { ParsedLocation } from './location'
8
+ import type { AnyRedirect } from './redirect'
9
+ import type { AnyRouteMatch } from './Matches'
10
+
11
+ export interface RouterReadableStore<TValue> {
12
+ readonly state: TValue
13
+ }
14
+
15
+ export interface RouterWritableStore<
16
+ TValue,
17
+ > extends RouterReadableStore<TValue> {
18
+ setState: (updater: (prev: TValue) => TValue) => void
19
+ }
20
+
21
+ export type RouterBatchFn = (fn: () => void) => void
22
+
23
+ export type MutableStoreFactory = <TValue>(
24
+ initialValue: TValue,
25
+ ) => RouterWritableStore<TValue>
26
+
27
+ export type ReadonlyStoreFactory = <TValue>(
28
+ read: () => TValue,
29
+ ) => RouterReadableStore<TValue>
30
+
31
+ export type GetStoreConfig = (opts: { isServer?: boolean }) => StoreConfig
32
+
33
+ export type StoreConfig = {
34
+ createMutableStore: MutableStoreFactory
35
+ createReadonlyStore: ReadonlyStoreFactory
36
+ batch: RouterBatchFn
37
+ init?: (stores: RouterStores<AnyRoute>) => void
38
+ }
39
+
40
+ type MatchStore = RouterWritableStore<AnyRouteMatch> & {
41
+ routeId?: string
42
+ }
43
+ type ReadableStore<TValue> = RouterReadableStore<TValue>
44
+
45
+ /** SSR non-reactive createMutableStore */
46
+ export function createNonReactiveMutableStore<TValue>(
47
+ initialValue: TValue,
48
+ ): RouterWritableStore<TValue> {
49
+ let value = initialValue
50
+
51
+ return {
52
+ get state() {
53
+ return value
54
+ },
55
+ setState(updater: (prev: TValue) => TValue) {
56
+ value = updater(value)
57
+ },
58
+ }
59
+ }
60
+
61
+ /** SSR non-reactive createReadonlyStore */
62
+ export function createNonReactiveReadonlyStore<TValue>(
63
+ read: () => TValue,
64
+ ): RouterReadableStore<TValue> {
65
+ return {
66
+ get state() {
67
+ return read()
68
+ },
69
+ }
70
+ }
71
+
72
+ export interface RouterStores<in out TRouteTree extends AnyRoute> {
73
+ status: RouterWritableStore<RouterState<TRouteTree>['status']>
74
+ loadedAt: RouterWritableStore<number>
75
+ isLoading: RouterWritableStore<boolean>
76
+ isTransitioning: RouterWritableStore<boolean>
77
+ location: RouterWritableStore<ParsedLocation<FullSearchSchema<TRouteTree>>>
78
+ resolvedLocation: RouterWritableStore<
79
+ ParsedLocation<FullSearchSchema<TRouteTree>> | undefined
80
+ >
81
+ statusCode: RouterWritableStore<number>
82
+ redirect: RouterWritableStore<AnyRedirect | undefined>
83
+ matchesId: RouterWritableStore<Array<string>>
84
+ pendingMatchesId: RouterWritableStore<Array<string>>
85
+ /** @internal */
86
+ cachedMatchesId: RouterWritableStore<Array<string>>
87
+ activeMatchesSnapshot: ReadableStore<Array<AnyRouteMatch>>
88
+ pendingMatchesSnapshot: ReadableStore<Array<AnyRouteMatch>>
89
+ cachedMatchesSnapshot: ReadableStore<Array<AnyRouteMatch>>
90
+ firstMatchId: ReadableStore<string | undefined>
91
+ hasPendingMatches: ReadableStore<boolean>
92
+ matchRouteReactivity: ReadableStore<{
93
+ locationHref: string
94
+ resolvedLocationHref: string | undefined
95
+ status: RouterState<TRouteTree>['status']
96
+ }>
97
+ __store: RouterReadableStore<RouterState<TRouteTree>>
98
+
99
+ activeMatchStoresById: Map<string, MatchStore>
100
+ pendingMatchStoresById: Map<string, MatchStore>
101
+ cachedMatchStoresById: Map<string, MatchStore>
102
+
103
+ /**
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.
108
+ */
109
+ getMatchStoreByRouteId: (
110
+ routeId: string,
111
+ ) => RouterReadableStore<AnyRouteMatch | undefined>
112
+
113
+ setActiveMatches: (nextMatches: Array<AnyRouteMatch>) => void
114
+ setPendingMatches: (nextMatches: Array<AnyRouteMatch>) => void
115
+ setCachedMatches: (nextMatches: Array<AnyRouteMatch>) => void
116
+ }
117
+
118
+ export function createRouterStores<TRouteTree extends AnyRoute>(
119
+ initialState: RouterState<TRouteTree>,
120
+ config: StoreConfig,
121
+ ): RouterStores<TRouteTree> {
122
+ const { createMutableStore, createReadonlyStore, batch, init } = config
123
+
124
+ // non reactive utilities
125
+ const activeMatchStoresById = new Map<string, MatchStore>()
126
+ const pendingMatchStoresById = new Map<string, MatchStore>()
127
+ const cachedMatchStoresById = new Map<string, MatchStore>()
128
+
129
+ // 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 pendingMatchesId = createMutableStore<Array<string>>([])
140
+ const cachedMatchesId = createMutableStore<Array<string>>([])
141
+
142
+ // 1st order derived stores
143
+ const activeMatchesSnapshot = createReadonlyStore(() =>
144
+ readPoolMatches(activeMatchStoresById, matchesId.state),
145
+ )
146
+ const pendingMatchesSnapshot = createReadonlyStore(() =>
147
+ readPoolMatches(pendingMatchStoresById, pendingMatchesId.state),
148
+ )
149
+ const cachedMatchesSnapshot = createReadonlyStore(() =>
150
+ readPoolMatches(cachedMatchStoresById, cachedMatchesId.state),
151
+ )
152
+ const firstMatchId = createReadonlyStore(() => matchesId.state[0])
153
+ const hasPendingMatches = createReadonlyStore(() =>
154
+ matchesId.state.some((matchId) => {
155
+ const store = activeMatchStoresById.get(matchId)
156
+ return store?.state.status === 'pending'
157
+ }),
158
+ )
159
+ const matchRouteReactivity = createReadonlyStore(() => ({
160
+ locationHref: location.state.href,
161
+ resolvedLocationHref: resolvedLocation.state?.href,
162
+ status: status.state,
163
+ }))
164
+
165
+ // compatibility "big" state store
166
+ const __store = createReadonlyStore(() => ({
167
+ status: status.state,
168
+ loadedAt: loadedAt.state,
169
+ isLoading: isLoading.state,
170
+ isTransitioning: isTransitioning.state,
171
+ matches: activeMatchesSnapshot.state,
172
+ location: location.state,
173
+ resolvedLocation: resolvedLocation.state,
174
+ statusCode: statusCode.state,
175
+ redirect: redirect.state,
176
+ }))
177
+
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 getMatchStoreByRouteId(
192
+ routeId: string,
193
+ ): RouterReadableStore<AnyRouteMatch | undefined> {
194
+ let cached = matchStoreByRouteIdCache.get(routeId)
195
+ if (!cached) {
196
+ cached = createReadonlyStore(() => {
197
+ // Reading matchesId.state tracks it as a dependency.
198
+ // When matchesId changes (navigation), this computed re-evaluates.
199
+ const ids = matchesId.state
200
+ for (const id of ids) {
201
+ const matchStore = activeMatchStoresById.get(id)
202
+ if (matchStore && matchStore.routeId === routeId) {
203
+ // Reading matchStore.state tracks it as a dependency.
204
+ // When the match store's state changes, this re-evaluates.
205
+ return matchStore.state
206
+ }
207
+ }
208
+ return undefined
209
+ })
210
+ matchStoreByRouteIdCache.set(routeId, cached)
211
+ }
212
+ return cached
213
+ }
214
+
215
+ const store = {
216
+ // atoms
217
+ status,
218
+ loadedAt,
219
+ isLoading,
220
+ isTransitioning,
221
+ location,
222
+ resolvedLocation,
223
+ statusCode,
224
+ redirect,
225
+ matchesId,
226
+ pendingMatchesId,
227
+ cachedMatchesId,
228
+
229
+ // derived
230
+ activeMatchesSnapshot,
231
+ pendingMatchesSnapshot,
232
+ cachedMatchesSnapshot,
233
+ firstMatchId,
234
+ hasPendingMatches,
235
+ matchRouteReactivity,
236
+
237
+ // non-reactive state
238
+ activeMatchStoresById,
239
+ pendingMatchStoresById,
240
+ cachedMatchStoresById,
241
+
242
+ // compatibility "big" state
243
+ __store,
244
+
245
+ // per-key computed stores
246
+ getMatchStoreByRouteId,
247
+
248
+ // methods
249
+ setActiveMatches,
250
+ setPendingMatches,
251
+ setCachedMatches,
252
+ }
253
+
254
+ // initialize the active matches
255
+ setActiveMatches(initialState.matches as Array<AnyRouteMatch>)
256
+ init?.(store)
257
+
258
+ // setters to update non-reactive utilities in sync with the reactive stores
259
+ function setActiveMatches(nextMatches: Array<AnyRouteMatch>) {
260
+ reconcileMatchPool(
261
+ nextMatches,
262
+ activeMatchStoresById,
263
+ matchesId,
264
+ createMutableStore,
265
+ batch,
266
+ )
267
+ }
268
+
269
+ function setPendingMatches(nextMatches: Array<AnyRouteMatch>) {
270
+ reconcileMatchPool(
271
+ nextMatches,
272
+ pendingMatchStoresById,
273
+ pendingMatchesId,
274
+ createMutableStore,
275
+ batch,
276
+ )
277
+ }
278
+
279
+ function setCachedMatches(nextMatches: Array<AnyRouteMatch>) {
280
+ reconcileMatchPool(
281
+ nextMatches,
282
+ cachedMatchStoresById,
283
+ cachedMatchesId,
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.state)
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)
320
+ }
321
+ }
322
+
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
330
+ }
331
+
332
+ existing.routeId = nextMatch.routeId
333
+ if (existing.state !== nextMatch) {
334
+ existing.setState(() => nextMatch)
335
+ }
336
+ }
337
+
338
+ if (!arraysEqual(idStore.state, nextIds)) {
339
+ idStore.setState(() => nextIds)
340
+ }
341
+ })
342
+ }
package/src/utils.ts CHANGED
@@ -697,3 +697,12 @@ export function buildDevStylesUrl(
697
697
  const normalizedBasepath = trimmedBasepath === '' ? '' : `/${trimmedBasepath}`
698
698
  return `${normalizedBasepath}/@tanstack-start/styles.css?routes=${encodeURIComponent(routeIds.join(','))}`
699
699
  }
700
+
701
+ export function arraysEqual<T>(a: Array<T>, b: Array<T>) {
702
+ if (a === b) return true
703
+ if (a.length !== b.length) return false
704
+ for (let i = 0; i < a.length; i++) {
705
+ if (a[i] !== b[i]) return false
706
+ }
707
+ return true
708
+ }
@@ -1,16 +0,0 @@
1
- require("../_virtual/_rolldown/runtime.cjs");
2
- let _tanstack_store = require("@tanstack/store");
3
- let _tanstack_router_core_isServer = require("@tanstack/router-core/isServer");
4
- //#region src/utils/batch.ts
5
- function batch(fn) {
6
- if (_tanstack_router_core_isServer.isServer) return fn();
7
- let result;
8
- (0, _tanstack_store.batch)(() => {
9
- result = fn();
10
- });
11
- return result;
12
- }
13
- //#endregion
14
- exports.batch = batch;
15
-
16
- //# sourceMappingURL=batch.cjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"batch.cjs","names":[],"sources":["../../../src/utils/batch.ts"],"sourcesContent":["import { batch as storeBatch } from '@tanstack/store'\n\nimport { isServer } from '@tanstack/router-core/isServer'\n\n// `@tanstack/store`'s `batch` is for reactive notification batching.\n// On the server we don't subscribe/render reactively, so a lightweight\n// implementation that just executes is enough.\nexport function batch<T>(fn: () => T): T {\n if (isServer) {\n return fn()\n }\n\n let result!: T\n storeBatch(() => {\n result = fn()\n })\n return result\n}\n"],"mappings":";;;;AAOA,SAAgB,MAAS,IAAgB;AACvC,KAAI,+BAAA,SACF,QAAO,IAAI;CAGb,IAAI;AACJ,EAAA,GAAA,gBAAA,aAAiB;AACf,WAAS,IAAI;GACb;AACF,QAAO"}
@@ -1 +0,0 @@
1
- export declare function batch<T>(fn: () => T): T;
@@ -1 +0,0 @@
1
- export declare function batch<T>(fn: () => T): T;
@@ -1,15 +0,0 @@
1
- import { batch } from "@tanstack/store";
2
- import { isServer } from "@tanstack/router-core/isServer";
3
- //#region src/utils/batch.ts
4
- function batch$1(fn) {
5
- if (isServer) return fn();
6
- let result;
7
- batch(() => {
8
- result = fn();
9
- });
10
- return result;
11
- }
12
- //#endregion
13
- export { batch$1 as batch };
14
-
15
- //# sourceMappingURL=batch.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"batch.js","names":[],"sources":["../../../src/utils/batch.ts"],"sourcesContent":["import { batch as storeBatch } from '@tanstack/store'\n\nimport { isServer } from '@tanstack/router-core/isServer'\n\n// `@tanstack/store`'s `batch` is for reactive notification batching.\n// On the server we don't subscribe/render reactively, so a lightweight\n// implementation that just executes is enough.\nexport function batch<T>(fn: () => T): T {\n if (isServer) {\n return fn()\n }\n\n let result!: T\n storeBatch(() => {\n result = fn()\n })\n return result\n}\n"],"mappings":";;;AAOA,SAAgB,QAAS,IAAgB;AACvC,KAAI,SACF,QAAO,IAAI;CAGb,IAAI;AACJ,aAAiB;AACf,WAAS,IAAI;GACb;AACF,QAAO"}
@@ -1,18 +0,0 @@
1
- import { batch as storeBatch } from '@tanstack/store'
2
-
3
- import { isServer } from '@tanstack/router-core/isServer'
4
-
5
- // `@tanstack/store`'s `batch` is for reactive notification batching.
6
- // On the server we don't subscribe/render reactively, so a lightweight
7
- // implementation that just executes is enough.
8
- export function batch<T>(fn: () => T): T {
9
- if (isServer) {
10
- return fn()
11
- }
12
-
13
- let result!: T
14
- storeBatch(() => {
15
- result = fn()
16
- })
17
- return result
18
- }