clear-react-router 1.7.9 → 1.8.1

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.
package/dist/index.js CHANGED
@@ -2,7 +2,7 @@ import { Suspense, createContext, lazy, useCallback, useContext, useEffect, useM
2
2
  //#region \0rolldown/runtime.js
3
3
  var __commonJSMin = (cb, mod) => () => (mod || (cb((mod = { exports: {} }).exports, mod), cb = null), mod.exports);
4
4
  //#endregion
5
- //#region state/createState.ts
5
+ //#region create.ts
6
6
  var create = (initialState) => {
7
7
  let state = initialState;
8
8
  const subscribers = /* @__PURE__ */ new Set();
@@ -28,35 +28,25 @@ var create = (initialState) => {
28
28
  var useGlobalState = ({ subscribe, getState, setState }) => {
29
29
  return [useSyncExternalStore(subscribe, getState), setState];
30
30
  };
31
- var createState = (initialState) => {
32
- const store = create(initialState);
33
- return () => useGlobalState(store);
31
+ //#endregion
32
+ //#region utils/commitState.ts
33
+ var createCommitState = ({ isLoadingState, routeItemDataState, loaderFallbackState, currentLoaderState, prevPathnameRef, loaderStateRef }) => (nextLocation, routeItem) => {
34
+ routeItemDataState.setState({
35
+ routeItem,
36
+ location: nextLocation
37
+ });
38
+ currentLoaderState.setState(loaderStateRef.value);
39
+ isLoadingState.setState(false);
40
+ loaderFallbackState.setState(void 0);
41
+ prevPathnameRef.set(nextLocation.pathname);
42
+ const fullPath = nextLocation.search ? `${nextLocation.pathname}${nextLocation.search}` : nextLocation.pathname;
43
+ if (fullPath === window.location.pathname + window.location.search) return;
44
+ history.pushState(null, "", fullPath);
34
45
  };
35
46
  //#endregion
36
47
  //#region constants.ts
37
48
  var emptyLoaderState = {};
38
49
  //#endregion
39
- //#region state/state.ts
40
- var isLoadingState = create(false);
41
- var useIsLoading = () => useGlobalState(isLoadingState);
42
- var useBlockedRoute = createState({
43
- from: "",
44
- to: ""
45
- });
46
- var loaderFallbackState = create(void 0);
47
- var useLoaderFallback = () => useGlobalState(loaderFallbackState);
48
- var routeItemDataState = create({
49
- routeItem: void 0,
50
- location: {}
51
- });
52
- var useRouteItemData = () => useGlobalState(routeItemDataState);
53
- var currentLoaderState = create(emptyLoaderState);
54
- var useCurrentLoaderState = () => useGlobalState(currentLoaderState);
55
- var scrollMapState = create({});
56
- var useScrollMap = () => useGlobalState(scrollMapState);
57
- var contextState = create({});
58
- var useContextState = () => useGlobalState(contextState);
59
- //#endregion
60
50
  //#region \0@oxc-project+runtime@0.133.0/helpers/esm/typeof.js
61
51
  function _typeof(o) {
62
52
  "@babel/helpers - typeof";
@@ -95,25 +85,40 @@ function _defineProperty(e, r, t) {
95
85
  }) : e[r] = t, e;
96
86
  }
97
87
  //#endregion
98
- //#region cell.ts
99
- var Cell = class {
100
- constructor(_value) {
101
- _defineProperty(this, "_value", void 0);
102
- this._value = _value;
88
+ //#region config/routerConfig.ts
89
+ var RouterConfig = class {
90
+ constructor() {
91
+ _defineProperty(this, "routes", []);
92
+ _defineProperty(this, "prefetch", "hover");
93
+ _defineProperty(this, "isAnimated", false);
94
+ _defineProperty(this, "showFallbackOnAnimation", false);
95
+ _defineProperty(this, "hoverPrefetchDelay", 150);
96
+ _defineProperty(this, "beforeLoad", void 0);
97
+ _defineProperty(this, "afterLoad", void 0);
98
+ _defineProperty(this, "defaultRetry", void 0);
103
99
  }
104
- get value() {
105
- return this._value;
100
+ configure(config) {
101
+ Object.assign(this, config);
106
102
  }
107
- set(action) {
108
- this._value = typeof action === "function" ? action(this._value) : action;
103
+ };
104
+ var routerConfig = new RouterConfig();
105
+ //#endregion
106
+ //#region utils/commitNavigation.ts
107
+ var createCommitNavigation = (navigationExecutor, prevPathnameRef) => (nextLocation, routeItem) => {
108
+ const { isAnimated } = routerConfig;
109
+ if (!isAnimated || !prevPathnameRef.value) {
110
+ navigationExecutor(nextLocation, routeItem);
111
+ return;
112
+ }
113
+ try {
114
+ document.startViewTransition(() => navigationExecutor(nextLocation, routeItem));
115
+ } catch {
116
+ navigationExecutor(nextLocation, routeItem);
109
117
  }
110
118
  };
111
- var loaderStateRef = new Cell(emptyLoaderState);
112
- var prevPathnameRef = new Cell("");
113
- var timestampMap = /* @__PURE__ */ new Map();
114
119
  //#endregion
115
120
  //#region utils/isCacheItemFresh.ts
116
- var isCacheItemFresh = ({ routeItem, pathname }) => {
121
+ var createIsCacheItemFresh = (timestampMap) => ({ routeItem, pathname }) => {
117
122
  if (!routeItem) return true;
118
123
  const currentCacheTimestamp = timestampMap.get(pathname);
119
124
  if (!currentCacheTimestamp) return false;
@@ -216,204 +221,397 @@ var comparePaths = (el, pathname) => {
216
221
  const splitPathname = pathname.split("/").filter(Boolean);
217
222
  return splitElementPath.every((item, index) => item === splitPathname[2 * index]) && splitPathname.length === splitElementPath.length + paramsLength;
218
223
  };
224
+ var findRoute = (pathname, includeAll) => {
225
+ if (includeAll) return routerConfig.routes.find((el) => el.path === "/*" || comparePaths(el, pathname));
226
+ return routerConfig.routes.find((el) => comparePaths(el, pathname));
227
+ };
219
228
  //#endregion
220
- //#region utils/getContext.ts
221
- var getContext = () => {
222
- return {
229
+ //#region runtime/navigate.ts
230
+ var navigationSeq = 0;
231
+ var createNavigate = (routerState, revalidateCache) => {
232
+ const { loaderStateRef, scrollMapState, prevPathnameRef, loaderFallbackState, isLoadingState, contextState, timestampMap } = routerState;
233
+ const commitNavigation = createCommitNavigation(createCommitState(routerState), prevPathnameRef);
234
+ const isCacheItemFresh = createIsCacheItemFresh(timestampMap);
235
+ const getContext = () => ({
223
236
  context: contextState.getState(),
224
237
  setContext: contextState.setState
238
+ });
239
+ const routeResolve = (location) => {
240
+ loaderStateRef.set(emptyLoaderState);
241
+ const nextItem = findRoute(location.pathname, true);
242
+ return {
243
+ nextItem,
244
+ params: getParamsObject({
245
+ params: nextItem?.params,
246
+ pathname: location.pathname
247
+ })
248
+ };
225
249
  };
250
+ const beforeLoad = async (routeItem, params) => {
251
+ const { beforeLoad } = routerConfig;
252
+ const runBeforeLoad = async (loaderFn) => {
253
+ const redirect = async (redirected) => await navigate(typeof redirected === "string" ? { pathname: redirected } : redirected);
254
+ try {
255
+ await loaderFn({
256
+ ...getContext(),
257
+ redirect,
258
+ params
259
+ });
260
+ loaderStateRef.set((prev) => ({
261
+ ...prev,
262
+ beforeLoadError: null
263
+ }));
264
+ } catch (error) {
265
+ loaderStateRef.set((prev) => ({
266
+ ...prev,
267
+ beforeLoadError: error
268
+ }));
269
+ }
270
+ };
271
+ if (beforeLoad) await runBeforeLoad(beforeLoad);
272
+ if (routeItem?.beforeLoad) await runBeforeLoad(routeItem?.beforeLoad);
273
+ };
274
+ const prepareNavigation = (routeItem, location) => {
275
+ const { isAnimated, showFallbackOnAnimation: showFallback } = routerConfig;
276
+ scrollMapState.setState((prevState) => {
277
+ const scrollPosition = document.scrollingElement?.scrollTop ?? 0;
278
+ if (!scrollPosition || prevState[prevPathnameRef.value] === scrollPosition) return prevState;
279
+ return {
280
+ ...prevState,
281
+ [prevPathnameRef.value]: scrollPosition
282
+ };
283
+ });
284
+ loaderFallbackState.setState(isCacheItemFresh({
285
+ routeItem,
286
+ pathname: location.pathname
287
+ }) || isAnimated && !showFallback ? void 0 : routeItem?.loaderFallback);
288
+ };
289
+ const loader = async (routeItem, location) => {
290
+ if (!routeItem?.loader) return;
291
+ isLoadingState.setState(true);
292
+ await revalidateCache({
293
+ routeItem,
294
+ pathname: location.pathname
295
+ });
296
+ };
297
+ const afterLoad = async (routeItem, params) => {
298
+ const { afterLoad } = routerConfig;
299
+ if (routeItem?.afterLoad) await routeItem.afterLoad({
300
+ ...getContext(),
301
+ params
302
+ });
303
+ if (afterLoad) await afterLoad({
304
+ ...getContext(),
305
+ params
306
+ });
307
+ };
308
+ const navigate = async (nextLocation) => {
309
+ navigationSeq = navigationSeq + 1;
310
+ const seq = navigationSeq;
311
+ const { nextItem, params } = routeResolve(nextLocation);
312
+ await beforeLoad(nextItem, params);
313
+ if (seq !== navigationSeq) return;
314
+ prepareNavigation(nextItem, nextLocation);
315
+ await loader(nextItem, nextLocation);
316
+ if (seq !== navigationSeq) return;
317
+ commitNavigation(nextLocation, nextItem);
318
+ await afterLoad(nextItem, params);
319
+ };
320
+ return navigate;
226
321
  };
227
322
  //#endregion
228
- //#region utils/revalidateCache.ts
229
- var loaderMapRef = {};
230
- var loadingPromises = /* @__PURE__ */ new Map();
231
- var revalidateCache = ({ routeItem, pathname }) => {
232
- if (!routeItem?.loader) return;
233
- if (loadingPromises.has(pathname)) return loadingPromises.get(pathname);
234
- if (isCacheItemFresh({
235
- routeItem,
236
- pathname
237
- })) {
238
- loaderStateRef.set(loaderMapRef[pathname]);
239
- return;
240
- }
241
- const promise = (async () => {
242
- if (!routeItem?.loader) return;
323
+ //#region runtime/invalidate.ts
324
+ var redirect = () => Promise.resolve();
325
+ var createInvalidate = ({ routeItemDataState, loaderStateRef, timestampMap, currentLoaderState, contextState }, revalidateCache) => {
326
+ const invalidatePath = async (routeItem, pathname) => {
327
+ const routePathname = routeItemDataState.getState().location.pathname;
328
+ timestampMap.delete(pathname);
329
+ const params = getParamsObject({
330
+ params: routeItem?.params,
331
+ pathname
332
+ });
243
333
  try {
244
- const { context, setContext } = getContext();
245
- const params = getParamsObject({
246
- params: routeItem.params,
247
- pathname
248
- });
249
- const result = await routeItem?.loader({
250
- params,
251
- context,
252
- setContext
253
- });
254
- timestampMap.set(pathname, Date.now());
334
+ if (routeItem?.beforeLoad) {
335
+ const context = contextState.getState();
336
+ const setContext = contextState.setState;
337
+ await routeItem.beforeLoad({
338
+ context,
339
+ redirect,
340
+ params,
341
+ setContext
342
+ });
343
+ }
255
344
  loaderStateRef.set((prev) => ({
256
345
  ...prev,
257
- data: result,
258
- loaderError: null
346
+ beforeLoadError: null
259
347
  }));
260
- loaderMapRef[pathname] = loaderStateRef.value;
261
348
  } catch (error) {
262
349
  loaderStateRef.set((prev) => ({
263
350
  ...prev,
264
- data: null,
265
- loaderError: error
351
+ beforeLoadError: error
266
352
  }));
267
- } finally {
268
- loadingPromises.delete(pathname);
269
353
  }
270
- })();
271
- loadingPromises.set(pathname, promise);
272
- return promise;
354
+ await revalidateCache({
355
+ routeItem,
356
+ pathname
357
+ });
358
+ if (pathname === routePathname) currentLoaderState.setState(loaderStateRef.value);
359
+ };
360
+ const invalidateItem = async (pathname, withChildren) => {
361
+ const routeItem = findRoute(pathname);
362
+ if (!routeItem) return;
363
+ const pathnameArray = [];
364
+ for (const [key] of timestampMap) if (comparePaths(routeItem, key)) pathnameArray.push(key);
365
+ await Promise.all(pathnameArray.map((pathname) => invalidatePath(routeItem, pathname)));
366
+ if (withChildren && routeItem.children?.length) {
367
+ const childPathList = routeItem.children.map((el) => el.path);
368
+ await Promise.all(childPathList.map((el) => invalidateItem(`${pathname}${el}`, true)));
369
+ }
370
+ };
371
+ return async (pathList, options) => {
372
+ const routePathname = routeItemDataState.getState().location.pathname;
373
+ const pathnameList = Array.isArray(pathList) ? pathList : pathList ? [pathList] : [routePathname];
374
+ await Promise.all(pathnameList.map((pathname) => invalidateItem(pathname, options?.withChildren)));
375
+ };
273
376
  };
274
377
  //#endregion
275
- //#region utils/commitState.ts
276
- var commitState = (nextLocation, routeItem) => {
277
- routeItemDataState.setState({
278
- routeItem,
279
- location: nextLocation
378
+ //#region runtime/prefetch.ts
379
+ var createPrefetch = (revalidateCache) => async (pathname) => {
380
+ const item = findRoute(pathname);
381
+ if (item) await revalidateCache({
382
+ routeItem: item,
383
+ pathname
280
384
  });
281
- currentLoaderState.setState(loaderStateRef.value);
282
- isLoadingState.setState(false);
283
- loaderFallbackState.setState(void 0);
284
- prevPathnameRef.set(nextLocation.pathname);
285
- const fullPath = nextLocation.search ? `${nextLocation.pathname}${nextLocation.search}` : nextLocation.pathname;
286
- if (fullPath === window.location.pathname + window.location.search) return;
287
- history.pushState(null, "", fullPath);
288
385
  };
289
386
  //#endregion
290
- //#region config/routerConfig.ts
291
- var RouterConfig = class {
292
- constructor() {
293
- _defineProperty(this, "routes", []);
294
- _defineProperty(this, "prefetch", "hover");
295
- _defineProperty(this, "isAnimated", false);
296
- _defineProperty(this, "showFallbackOnAnimation", false);
297
- _defineProperty(this, "hoverPrefetchDelay", 150);
298
- _defineProperty(this, "beforeLoad", void 0);
299
- _defineProperty(this, "afterLoad", void 0);
300
- }
301
- configure(config) {
302
- Object.assign(this, config);
303
- }
387
+ //#region utils/revalidateCache.ts
388
+ var loaderMapRef = {};
389
+ var loadingPromises = /* @__PURE__ */ new Map();
390
+ var isObjectRetry = (arg) => typeof arg === "object";
391
+ var createRetry = (arg) => {
392
+ if (arg === void 0) return null;
393
+ return {
394
+ count: isObjectRetry(arg) ? arg.count : arg,
395
+ delay: isObjectRetry(arg) ? arg.delay : 0
396
+ };
397
+ };
398
+ var getRetry = (routeItem) => {
399
+ const routeRetry = createRetry(routeItem?.retry);
400
+ const globalRetry = createRetry(routerConfig.defaultRetry);
401
+ if (!routeRetry && !globalRetry) return null;
402
+ return {
403
+ count: routeRetry ? routeRetry.count : globalRetry?.count || 0,
404
+ delay: routeRetry ? routeRetry.delay : globalRetry?.delay || 0
405
+ };
406
+ };
407
+ var sleep = async (ms) => new Promise((resolve) => setTimeout(resolve, ms));
408
+ var createRevalidateCache = (routerState) => {
409
+ const revalidateCache = async ({ routeItem, pathname }, retried = 0) => {
410
+ if (!routeItem?.loader) return;
411
+ const isCacheItemFresh = createIsCacheItemFresh(routerState.timestampMap);
412
+ const { loaderStateRef, timestampMap, contextState } = routerState;
413
+ if (loadingPromises.has(pathname)) return loadingPromises.get(pathname);
414
+ if (isCacheItemFresh({
415
+ routeItem,
416
+ pathname
417
+ })) {
418
+ loaderStateRef.set(loaderMapRef[pathname]);
419
+ return;
420
+ }
421
+ const promise = (async () => {
422
+ if (!routeItem?.loader) return;
423
+ try {
424
+ const context = contextState.getState();
425
+ const setContext = contextState.setState;
426
+ const params = getParamsObject({
427
+ params: routeItem.params,
428
+ pathname
429
+ });
430
+ const result = await routeItem?.loader({
431
+ params,
432
+ context,
433
+ setContext
434
+ });
435
+ timestampMap.set(pathname, Date.now());
436
+ loaderStateRef.set((prev) => ({
437
+ ...prev,
438
+ data: result,
439
+ loaderError: null
440
+ }));
441
+ loaderMapRef[pathname] = loaderStateRef.value;
442
+ } catch (error) {
443
+ const retry = getRetry(routeItem);
444
+ if (retry && retry.count > retried) {
445
+ loadingPromises.delete(pathname);
446
+ if (retry.delay) await sleep(retry.delay);
447
+ await revalidateCache({
448
+ routeItem,
449
+ pathname
450
+ }, retried + 1);
451
+ } else loaderStateRef.set((prev) => ({
452
+ ...prev,
453
+ data: null,
454
+ loaderError: error
455
+ }));
456
+ } finally {
457
+ loadingPromises.delete(pathname);
458
+ }
459
+ })();
460
+ loadingPromises.set(pathname, promise);
461
+ return promise;
462
+ };
463
+ return revalidateCache;
304
464
  };
305
- var routerConfig = new RouterConfig();
306
465
  //#endregion
307
- //#region utils/commitNavigation.ts
308
- var commitNavigation = (nextLocation, routeItem) => {
309
- const { isAnimated } = routerConfig;
310
- if (!isAnimated || !prevPathnameRef.value) {
311
- commitState(nextLocation, routeItem);
312
- return;
466
+ //#region cell.ts
467
+ var Cell = class {
468
+ constructor(_value) {
469
+ _defineProperty(this, "_value", void 0);
470
+ this._value = _value;
313
471
  }
314
- try {
315
- document.startViewTransition(() => commitState(nextLocation, routeItem));
316
- } catch {
317
- commitState(nextLocation, routeItem);
472
+ get value() {
473
+ return this._value;
474
+ }
475
+ set(action) {
476
+ this._value = typeof action === "function" ? action(this._value) : action;
318
477
  }
319
478
  };
320
- var findRoute = (pathname, includeAll) => {
321
- if (includeAll) return routerConfig.routes.find((el) => el.path === "*" || comparePaths(el, pathname));
322
- return routerConfig.routes.find((el) => el.path === "*" || comparePaths(el, pathname));
323
- };
479
+ new Cell(emptyLoaderState);
480
+ new Cell("");
324
481
  //#endregion
325
- //#region runtime/navigate.ts
326
- var navigationSeq = 0;
327
- var routeResolve = (location) => {
328
- loaderStateRef.set(emptyLoaderState);
329
- const nextItem = findRoute(location.pathname, true);
330
- return {
331
- nextItem,
332
- params: getParamsObject({
333
- params: nextItem?.params,
482
+ //#region utils/createRouterInstance.ts
483
+ var createRouterInstance = () => {
484
+ const routerState = {
485
+ isLoadingState: create(false),
486
+ loaderFallbackState: create(void 0),
487
+ routeItemDataState: create({
488
+ routeItem: void 0,
489
+ location: {}
490
+ }),
491
+ currentLoaderState: create(emptyLoaderState),
492
+ scrollMapState: create({}),
493
+ contextState: create({}),
494
+ blockedRouteState: create({
495
+ from: "",
496
+ to: ""
497
+ }),
498
+ loaderStateRef: new Cell(emptyLoaderState),
499
+ prevPathnameRef: new Cell(""),
500
+ timestampMap: /* @__PURE__ */ new Map()
501
+ };
502
+ const revalidateCache = createRevalidateCache(routerState);
503
+ const navigate = createNavigate(routerState, revalidateCache);
504
+ const invalidate = createInvalidate(routerState, revalidateCache);
505
+ const prefetch = createPrefetch(revalidateCache);
506
+ const useGetAction = (actionKey) => {
507
+ const { routeItem, location } = routerState.routeItemDataState.getState();
508
+ const context = routerState.contextState.getState();
509
+ const setContext = routerState.contextState.setState;
510
+ const params = getParamsObject({
511
+ params: routeItem?.params,
334
512
  pathname: location.pathname
335
- })
513
+ });
514
+ if (!routeItem) throw new Error("Route not found");
515
+ if (!routeItem.actions) throw new Error("Route action creator not found");
516
+ const action = routeItem.actions({
517
+ context,
518
+ setContext,
519
+ params,
520
+ invalidate
521
+ })[actionKey];
522
+ if (!action) throw new Error(`Action "${actionKey}" not found`);
523
+ return {
524
+ currentAction: action,
525
+ invalidate
526
+ };
336
527
  };
337
- };
338
- var beforeLoad = async (routeItem, params) => {
339
- const { beforeLoad } = routerConfig;
340
- const runBeforeLoad = async (loaderFn) => {
341
- const redirect = async (redirected) => await navigate(typeof redirected === "string" ? { pathname: redirected } : redirected);
342
- try {
343
- await loaderFn({
344
- ...getContext(),
345
- redirect,
346
- params
347
- });
348
- loaderStateRef.set((prev) => ({
349
- ...prev,
350
- beforeLoadError: null
351
- }));
352
- } catch (error) {
353
- loaderStateRef.set((prev) => ({
354
- ...prev,
355
- beforeLoadError: error
356
- }));
528
+ return {
529
+ state: {
530
+ isLoadingState: routerState.isLoadingState,
531
+ loaderFallbackState: routerState.loaderFallbackState,
532
+ routeItemDataState: routerState.routeItemDataState,
533
+ currentLoaderState: routerState.currentLoaderState,
534
+ scrollMapState: routerState.scrollMapState,
535
+ contextState: routerState.contextState,
536
+ blockedRouteState: routerState.blockedRouteState,
537
+ prevPathnameRef: routerState.prevPathnameRef
538
+ },
539
+ runtime: {
540
+ navigate,
541
+ invalidate,
542
+ prefetch
543
+ },
544
+ hooks: {
545
+ useIsLoading: () => useGlobalState(routerState.isLoadingState),
546
+ useBlockedRoute: () => useGlobalState(routerState.blockedRouteState),
547
+ useLoaderFallback: () => useGlobalState(routerState.loaderFallbackState),
548
+ useRouteItemData: () => useGlobalState(routerState.routeItemDataState),
549
+ useCurrentLoaderState: () => useGlobalState(routerState.currentLoaderState),
550
+ useScrollMap: () => useGlobalState(routerState.scrollMapState),
551
+ useContextState: () => useGlobalState(routerState.contextState),
552
+ useParams: () => {
553
+ const routeItemData = routerState.routeItemDataState.getState();
554
+ return getParamsObject({
555
+ params: routeItemData.routeItem?.params,
556
+ pathname: routeItemData.location.pathname
557
+ });
558
+ },
559
+ useNavigate: () => {
560
+ const { blockedRouteState } = routerState;
561
+ const { location } = routerState.routeItemDataState.getState();
562
+ return async (arg) => {
563
+ if (arg !== -1 && blockedRouteState.getState().from) {
564
+ const to = typeof arg === "object" ? arg.pathname : arg;
565
+ blockedRouteState.setState((prevState) => ({
566
+ ...prevState,
567
+ to
568
+ }));
569
+ return;
570
+ }
571
+ if (arg === -1) return history.go(arg);
572
+ if (typeof arg === "string") {
573
+ if (arg !== location.pathname) await navigate({ pathname: arg });
574
+ } else if (JSON.stringify(arg) !== JSON.stringify(location)) await navigate(arg);
575
+ };
576
+ },
577
+ useRestoreScroll: () => {
578
+ const { pathname } = routerState.routeItemDataState.getState().location;
579
+ const scrollMap = routerState.scrollMapState.getState();
580
+ return () => {
581
+ if (scrollMap[pathname]) requestAnimationFrame(() => window.scrollTo({
582
+ top: scrollMap[pathname],
583
+ behavior: "smooth"
584
+ }));
585
+ };
586
+ },
587
+ useGetAction,
588
+ useAction: (action, options = {}) => {
589
+ const { currentAction, invalidate } = useGetAction(action);
590
+ return async (formData) => {
591
+ try {
592
+ const result = await currentAction(formData);
593
+ await invalidate();
594
+ options.onSuccess?.(result);
595
+ } catch (error) {
596
+ options.onError?.(error);
597
+ }
598
+ };
599
+ }
357
600
  }
358
601
  };
359
- if (beforeLoad) await runBeforeLoad(beforeLoad);
360
- if (routeItem?.beforeLoad) await runBeforeLoad(routeItem?.beforeLoad);
361
- };
362
- var prepareNavigation = (routeItem, location) => {
363
- const { isAnimated, showFallbackOnAnimation: showFallback } = routerConfig;
364
- scrollMapState.setState((prevState) => {
365
- const scrollPosition = document.scrollingElement?.scrollTop ?? 0;
366
- if (!scrollPosition || prevState[prevPathnameRef.value] === scrollPosition) return prevState;
367
- return {
368
- ...prevState,
369
- [prevPathnameRef.value]: scrollPosition
370
- };
371
- });
372
- loaderFallbackState.setState(isCacheItemFresh({
373
- routeItem,
374
- pathname: location.pathname
375
- }) || isAnimated && !showFallback ? void 0 : routeItem?.loaderFallback);
376
- };
377
- var loader = async (routeItem, location) => {
378
- if (!routeItem?.loader) return;
379
- isLoadingState.setState(true);
380
- await revalidateCache({
381
- routeItem,
382
- pathname: location.pathname
383
- });
384
- };
385
- var afterLoad = async (routeItem, params) => {
386
- const { afterLoad } = routerConfig;
387
- if (routeItem?.afterLoad) await routeItem.afterLoad({
388
- ...getContext(),
389
- params
390
- });
391
- if (afterLoad) await afterLoad({
392
- ...getContext(),
393
- params
394
- });
395
- };
396
- var navigate = async (nextLocation) => {
397
- navigationSeq = navigationSeq + 1;
398
- const seq = navigationSeq;
399
- const { nextItem, params } = routeResolve(nextLocation);
400
- await beforeLoad(nextItem, params);
401
- if (seq !== navigationSeq) return;
402
- prepareNavigation(nextItem, nextLocation);
403
- await loader(nextItem, nextLocation);
404
- if (seq !== navigationSeq) return;
405
- commitNavigation(nextLocation, nextItem);
406
- await afterLoad(nextItem, params);
407
602
  };
408
603
  //#endregion
604
+ //#region instance.ts
605
+ var router = createRouterInstance();
606
+ //#endregion
409
607
  //#region hooks/useNavigation.ts
410
608
  var useNavigation = () => {
411
- const [blockedRoute, setBlockedRoute] = useBlockedRoute();
609
+ const { state: { prevPathnameRef, blockedRouteState }, runtime: { navigate } } = router;
412
610
  useEffect(() => {
413
611
  const handler = async (event) => {
414
612
  const newLocation = parseWindowLocation(event.target.location);
415
- if (prevPathnameRef.value === blockedRoute.from) {
416
- setBlockedRoute({
613
+ if (prevPathnameRef.value === blockedRouteState.getState().from) {
614
+ blockedRouteState.setState({
417
615
  from: prevPathnameRef.value,
418
616
  to: newLocation.pathname
419
617
  });
@@ -422,12 +620,16 @@ var useNavigation = () => {
422
620
  };
423
621
  window.addEventListener("popstate", handler);
424
622
  return () => window.removeEventListener("popstate", handler);
425
- }, [blockedRoute.from, setBlockedRoute]);
623
+ }, [
624
+ blockedRouteState,
625
+ navigate,
626
+ prevPathnameRef.value
627
+ ]);
426
628
  useEffect(() => {
427
629
  const currentLocation = parseWindowLocation(window.location);
428
630
  navigate(currentLocation);
429
631
  prevPathnameRef.set(currentLocation.pathname);
430
- }, []);
632
+ }, [navigate, prevPathnameRef]);
431
633
  };
432
634
  //#endregion
433
635
  //#region hooks/useApplyCustomAnimation.ts
@@ -467,20 +669,16 @@ var useApplyCustomAnimation = (animationDuration) => {
467
669
  }, [animationDuration]);
468
670
  };
469
671
  //#endregion
672
+ //#region hooks/useLocation.ts
673
+ var useLocation = () => {
674
+ const [routeItemData] = router.hooks.useRouteItemData();
675
+ return routeItemData.location;
676
+ };
677
+ //#endregion
470
678
  //#region hooks/usePreserveScroll.ts
471
679
  var usePreserveScroll = (preserveScroll) => {
472
- const [routeItemData] = useRouteItemData();
473
- const [scrollMap] = useScrollMap();
474
- const { pathname } = routeItemData.location;
475
- const restoreScroll = useCallback(() => {
476
- if (!pathname || !scrollMap[pathname]) return;
477
- requestAnimationFrame(() => {
478
- window.scrollTo({
479
- top: scrollMap[pathname],
480
- behavior: "smooth"
481
- });
482
- });
483
- }, [pathname, scrollMap]);
680
+ const restoreScroll = router.hooks.useRestoreScroll();
681
+ const { pathname } = useLocation();
484
682
  useEffect(() => {
485
683
  if (preserveScroll) restoreScroll();
486
684
  }, [
@@ -497,7 +695,7 @@ var useSetRouterConfig = (routerProps) => {
497
695
  //#endregion
498
696
  //#region hooks/useSetInitialContext.ts
499
697
  var useSetInitialContext = (initialContext) => {
500
- const [, setContext] = useContextState();
698
+ const [, setContext] = router.hooks.useContextState();
501
699
  useEffect(() => {
502
700
  if (initialContext) setContext(initialContext);
503
701
  }, [initialContext, setContext]);
@@ -514,7 +712,8 @@ var renderElement = (Component) => {
514
712
  //#endregion
515
713
  //#region components/Router.tsx
516
714
  var EmptyBoundary = ({ children }) => children;
517
- var Router = ({ routes, beforeLoad, afterLoad, animationDuration, isAnimated = false, spinner = true, preserveScroll = true, showFallbackOnAnimation = false, prefetch = "hover", hoverPrefetchDelay = 150, errorBoundary: ErrorBoundary = EmptyBoundary, context: initialContext, defaultLoaderFallback, defaultErrorElement }) => {
715
+ var Router = ({ routes, beforeLoad, afterLoad, animationDuration, isAnimated = false, spinner = true, preserveScroll = true, showFallbackOnAnimation = false, prefetch = "hover", hoverPrefetchDelay = 150, errorBoundary: ErrorBoundary = EmptyBoundary, context: initialContext, defaultLoaderFallback, defaultErrorElement, defaultRetry }) => {
716
+ const { useIsLoading, useLoaderFallback, useRouteItemData, useCurrentLoaderState } = router.hooks;
518
717
  const [isLoading] = useIsLoading();
519
718
  const [currentLoaderFallback] = useLoaderFallback();
520
719
  const [routeItemData] = useRouteItemData();
@@ -527,7 +726,8 @@ var Router = ({ routes, beforeLoad, afterLoad, animationDuration, isAnimated = f
527
726
  hoverPrefetchDelay,
528
727
  showFallbackOnAnimation,
529
728
  beforeLoad,
530
- afterLoad
729
+ afterLoad,
730
+ defaultRetry
531
731
  });
532
732
  useApplyCustomAnimation(animationDuration);
533
733
  useSetInitialContext(initialContext);
@@ -546,98 +746,51 @@ var Router = ({ routes, beforeLoad, afterLoad, animationDuration, isAnimated = f
546
746
  });
547
747
  };
548
748
  //#endregion
549
- //#region runtime/prefetch.ts
550
- var prefetch = async (pathname) => {
551
- const item = findRoute(pathname);
552
- if (item) await revalidateCache({
553
- routeItem: item,
554
- pathname
555
- });
556
- };
557
- //#endregion
558
- //#region hooks/useLocation.ts
559
- var useLocation = () => {
560
- const [routeItemData] = useRouteItemData();
561
- return routeItemData.location;
562
- };
563
- //#endregion
564
- //#region hooks/useLatest.ts
565
- var useLatest = (value) => {
566
- const ref = useRef(value);
567
- useEffect(() => {
568
- ref.current = value;
569
- }, [value]);
570
- return ref;
571
- };
572
- //#endregion
573
749
  //#region hooks/useNavigate.ts
574
- var useNavigate = () => {
575
- const [blockedRoute, setBlockedRoute] = useBlockedRoute();
576
- const locationRef = useLatest(useLocation());
577
- const blockedRouteRef = useLatest(blockedRoute);
578
- return useCallback(async (arg) => {
579
- if (!navigate) throw new Error("Router has not been initialized. Did you forget to render <Router />?");
580
- if (arg !== -1 && blockedRouteRef.current.from) {
581
- const to = typeof arg === "object" ? arg.pathname : arg;
582
- setBlockedRoute((prevState) => ({
583
- ...prevState,
584
- to
585
- }));
586
- return;
587
- }
588
- if (arg === -1) return history.go(arg);
589
- if (typeof arg === "string") {
590
- if (arg !== locationRef.current.pathname) await navigate({ pathname: arg });
591
- } else if (JSON.stringify(arg) !== JSON.stringify(locationRef.current)) await navigate(arg);
592
- }, [
593
- blockedRouteRef,
594
- locationRef,
595
- setBlockedRoute
596
- ]);
597
- };
750
+ var useNavigate = router.hooks.useNavigate;
598
751
  //#endregion
599
752
  //#region components/Link.tsx
600
753
  var Link = ({ children, to, prefetch: prefetchLink, hoverPrefetchDelay }) => {
601
754
  const { prefetch: configPrefetch, hoverPrefetchDelay: configPrefetchDelay } = routerConfig;
602
- const prefetch$1 = prefetchLink || configPrefetch;
755
+ const prefetch = prefetchLink || configPrefetch;
603
756
  const prefetchDelay = hoverPrefetchDelay ?? configPrefetchDelay;
604
757
  const navigate = useNavigate();
605
758
  const timeout = useRef(0);
606
759
  const ref = useRef(null);
607
760
  const onMouseEnter = useCallback(() => {
608
- if (prefetch$1 !== "hover" || !prefetchDelay) return;
761
+ if (prefetch !== "hover" || !prefetchDelay) return;
609
762
  if (timeout.current) clearTimeout(timeout.current);
610
- timeout.current = window.setTimeout(() => prefetch(to), prefetchDelay);
763
+ timeout.current = window.setTimeout(() => router.runtime.prefetch(to), prefetchDelay);
611
764
  }, [
612
- prefetch$1,
765
+ prefetch,
613
766
  prefetchDelay,
614
767
  to
615
768
  ]);
616
769
  const onMouseLeave = useCallback(() => {
617
- if (prefetch$1 !== "hover" || !prefetchDelay) return;
770
+ if (prefetch !== "hover" || !prefetchDelay) return;
618
771
  if (timeout.current) {
619
772
  clearTimeout(timeout.current);
620
773
  timeout.current = 0;
621
774
  }
622
- }, [prefetch$1, prefetchDelay]);
775
+ }, [prefetch, prefetchDelay]);
623
776
  useEffect(() => {
624
- if (prefetch$1 !== "render") return;
777
+ if (prefetch !== "render") return;
625
778
  (async () => {
626
- await prefetch(to);
779
+ await router.runtime.prefetch(to);
627
780
  })();
628
- }, [prefetch$1, to]);
781
+ }, [prefetch, to]);
629
782
  useEffect(() => {
630
- if (prefetch$1 !== "viewport") return;
783
+ if (prefetch !== "viewport") return;
631
784
  const element = ref.current;
632
785
  const observer = new IntersectionObserver(async () => {
633
- await prefetch(to);
786
+ await router.runtime.prefetch(to);
634
787
  observer.disconnect();
635
788
  });
636
789
  if (element) observer.observe(element);
637
790
  return () => {
638
791
  if (element) observer.disconnect();
639
792
  };
640
- }, [prefetch$1, to]);
793
+ }, [prefetch, to]);
641
794
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("a", {
642
795
  ref,
643
796
  style: { cursor: "pointer" },
@@ -657,97 +810,9 @@ var FormProvider = ({ children, isSubmitting }) => /* @__PURE__ */ (0, import_js
657
810
  children
658
811
  });
659
812
  //#endregion
660
- //#region runtime/invalidate.ts
661
- var redirect = () => Promise.resolve();
662
- var invalidatePath = async (routeItem, pathname) => {
663
- const routePathname = routeItemDataState.getState().location.pathname;
664
- timestampMap.delete(pathname);
665
- const params = getParamsObject({
666
- params: routeItem?.params,
667
- pathname
668
- });
669
- try {
670
- if (routeItem?.beforeLoad) {
671
- const { context, setContext } = getContext();
672
- await routeItem.beforeLoad({
673
- context,
674
- redirect,
675
- params,
676
- setContext
677
- });
678
- }
679
- loaderStateRef.set((prev) => ({
680
- ...prev,
681
- beforeLoadError: null
682
- }));
683
- } catch (error) {
684
- loaderStateRef.set((prev) => ({
685
- ...prev,
686
- beforeLoadError: error
687
- }));
688
- }
689
- await revalidateCache({
690
- routeItem,
691
- pathname
692
- });
693
- if (pathname === routePathname) currentLoaderState.setState(loaderStateRef.value);
694
- };
695
- var invalidateItem = async (pathname, withChildren) => {
696
- const routeItem = findRoute(pathname);
697
- if (!routeItem) return;
698
- const pathnameArray = [];
699
- for (const [key] of timestampMap) if (comparePaths(routeItem, key)) pathnameArray.push(key);
700
- await Promise.all(pathnameArray.map((pathname) => invalidatePath(routeItem, pathname)));
701
- if (withChildren && routeItem.children?.length) {
702
- const childPathList = routeItem.children.map((el) => el.path);
703
- await Promise.all(childPathList.map((el) => invalidateItem(`${pathname}${el}`, withChildren)));
704
- }
705
- };
706
- async function invalidate(pathList, options) {
707
- const routePathname = routeItemDataState.getState().location.pathname;
708
- const pathnameList = Array.isArray(pathList) ? pathList : pathList ? [pathList] : [routePathname];
709
- await Promise.all(pathnameList.map((pathname) => invalidateItem(pathname, options?.withChildren)));
710
- }
711
- //#endregion
712
- //#region hooks/useInvalidate.ts
713
- var useInvalidate = () => invalidate;
714
- //#endregion
715
- //#region hooks/useParams.ts
716
- var useParams = () => {
717
- const [routeItemData] = useRouteItemData();
718
- const { routeItem, location: { pathname } } = routeItemData;
719
- return useMemo(() => routeItem ? getParamsObject({
720
- params: routeItem?.params,
721
- pathname
722
- }) : void 0, [pathname, routeItem]);
723
- };
724
- //#endregion
725
- //#region hooks/useGetAction.ts
726
- var useGetAction = (actionKey) => {
727
- const invalidate = useInvalidate();
728
- const [routeItemData] = useRouteItemData();
729
- const [context, setContext] = useContextState();
730
- const params = useParams();
731
- const { routeItem } = routeItemData;
732
- const latestContext = useLatest(context);
733
- if (!routeItem) throw new Error("Route not found");
734
- if (!routeItem.actions) throw new Error("Route action creator not found");
735
- const action = routeItem.actions({
736
- context: latestContext.current,
737
- setContext,
738
- params,
739
- invalidate
740
- })[actionKey];
741
- if (!action) throw new Error(`Action "${actionKey}" not found`);
742
- return {
743
- currentAction: action,
744
- invalidate
745
- };
746
- };
747
- //#endregion
748
813
  //#region components/Form.tsx
749
814
  var Form = ({ children, action, onSuccess, onError, autoReset = true }) => {
750
- const { currentAction, invalidate } = useGetAction(action);
815
+ const { currentAction, invalidate } = router.hooks.useGetAction(action);
751
816
  const [isSubmitting, setIsSubmitting] = useState(false);
752
817
  const onSubmit = async (evt) => {
753
818
  evt.preventDefault();
@@ -772,14 +837,21 @@ var Form = ({ children, action, onSuccess, onError, autoReset = true }) => {
772
837
  });
773
838
  };
774
839
  //#endregion
840
+ //#region hooks/useParams.ts
841
+ var useParams = router.hooks.useParams;
842
+ //#endregion
775
843
  //#region hooks/useLoaderState.ts
776
844
  var useLoaderState = () => {
777
- const [loaderState] = useCurrentLoaderState();
845
+ const [loaderState] = router.hooks.useCurrentLoaderState();
778
846
  return loaderState;
779
847
  };
780
848
  //#endregion
849
+ //#region hooks/useInvalidate.ts
850
+ var useInvalidate = () => router.runtime.invalidate;
851
+ //#endregion
781
852
  //#region hooks/useBlocker.ts
782
853
  var useBlocker = (blockerFn) => {
854
+ const { hooks: { useBlockedRoute, useRouteItemData }, runtime: { navigate } } = router;
783
855
  const [blockedRoute, setBlockedRoute] = useBlockedRoute();
784
856
  const [routeItemData] = useRouteItemData();
785
857
  const { location: { pathname } } = routeItemData;
@@ -799,7 +871,7 @@ var useBlocker = (blockerFn) => {
799
871
  from: "",
800
872
  to: ""
801
873
  };
802
- }), [setBlockedRoute]);
874
+ }), [navigate, setBlockedRoute]);
803
875
  const blockerState = useMemo(() => {
804
876
  if (blockedRoute.from && blockedRoute.to) return "blocked";
805
877
  if (blockedRoute.from) return "charged";
@@ -822,29 +894,11 @@ var useBlocker = (blockerFn) => {
822
894
  };
823
895
  //#endregion
824
896
  //#region hooks/useAction.ts
825
- var useAction = (action, options = {}) => {
826
- const { currentAction, invalidate } = useGetAction(action);
827
- const latestOnSuccess = useLatest(options?.onSuccess);
828
- const latestOnError = useLatest(options?.onError);
829
- return useCallback(async (formData) => {
830
- try {
831
- const result = await currentAction(formData);
832
- await invalidate();
833
- latestOnSuccess.current?.(result);
834
- } catch (error) {
835
- latestOnError.current?.(error);
836
- }
837
- }, [
838
- currentAction,
839
- invalidate,
840
- latestOnError,
841
- latestOnSuccess
842
- ]);
843
- };
897
+ var useAction = router.hooks.useAction;
844
898
  //#endregion
845
899
  //#region hooks/useRouterContext.ts
846
900
  var useRouterContext = () => {
847
- const [context, setContext] = useContextState();
901
+ const [context, setContext] = router.hooks.useContextState();
848
902
  return {
849
903
  context,
850
904
  setContext
@@ -880,6 +934,15 @@ var useSearch = () => {
880
934
  return useSyncExternalStore(subscribe, getSnapshot, getSnapshot);
881
935
  };
882
936
  //#endregion
937
+ //#region hooks/useLatest.ts
938
+ var useLatest = (value) => {
939
+ const ref = useRef(value);
940
+ useEffect(() => {
941
+ ref.current = value;
942
+ }, [value]);
943
+ return ref;
944
+ };
945
+ //#endregion
883
946
  //#region hooks/useSearchParams.ts
884
947
  var useSearchParams = () => {
885
948
  const search = useSearch();