@ryanfw/universal-route 6.0.0 → 6.0.2

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
@@ -62,8 +62,12 @@ var compilePath = (path) => {
62
62
  }
63
63
  const parts = String(path).split("/").filter(Boolean).map((part) => {
64
64
  if (part.startsWith(":")) {
65
- const name = part.slice(1);
66
- return { src: `(?<${name}>[^/]+)`, name };
65
+ const raw = part.slice(1);
66
+ if (raw.endsWith("+")) {
67
+ const name = raw.slice(0, -1);
68
+ return { src: `(?<${name}>.+)`, name };
69
+ }
70
+ return { src: `(?<${raw}>[^/]+)`, name: raw };
67
71
  }
68
72
  return { src: escapeRegex(part), name: null };
69
73
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ryanfw/universal-route",
3
- "version": "6.0.0",
3
+ "version": "6.0.2",
4
4
  "description": "Routing system for React vertical slice architecture applications.",
5
5
  "repository": {
6
6
  "type": "git",
package/dist/index.d.mts DELETED
@@ -1,88 +0,0 @@
1
- import React, { ComponentType, AnchorHTMLAttributes, Context } from 'react';
2
- import { BrowserHistory, MemoryHistory } from 'history';
3
-
4
- interface RouteDefinition {
5
- path: string;
6
- Component?: ComponentType<any>;
7
- element?: ComponentType<any>;
8
- render?: ComponentType<any>;
9
- reducerKey?: string;
10
- }
11
- type RouteMapValue = ComponentType<any> | [ComponentType<any>] | [ComponentType<any>, string] | {
12
- Component?: ComponentType<any>;
13
- element?: ComponentType<any>;
14
- render?: ComponentType<any>;
15
- reducerKey?: string;
16
- };
17
- type RouteMap = Record<string, RouteMapValue>;
18
- type RoutesInput = RouteDefinition[] | RouteMap;
19
- interface PreparedRoute {
20
- path: string;
21
- Component: ComponentType<any>;
22
- reducerKey?: string;
23
- matcher: (pathname: string) => {
24
- params: Record<string, string>;
25
- } | null;
26
- }
27
- interface RouteMatchResult {
28
- Component: ComponentType<any>;
29
- params: Record<string, string>;
30
- reducerKey?: string;
31
- }
32
- declare const _default: {
33
- prepare: (routes?: RoutesInput) => PreparedRoute[];
34
- match: (routes: RoutesInput | PreparedRoute[], pathname: string) => RouteMatchResult;
35
- };
36
-
37
- type LinkTo = string | {
38
- pathname: string;
39
- search?: string;
40
- hash?: string;
41
- };
42
- interface NavigateOptions {
43
- replace?: boolean;
44
- state?: unknown;
45
- }
46
- interface LinkProps extends Omit<AnchorHTMLAttributes<HTMLAnchorElement>, "href"> {
47
- to: LinkTo;
48
- replace?: boolean;
49
- state?: unknown;
50
- }
51
- interface StoreContextValue {
52
- state: Record<string, any>;
53
- dispatch: ((action: Record<string, unknown>) => void) | false;
54
- pageRefresher?: unknown;
55
- }
56
- declare const navigate: (to: LinkTo, options?: NavigateOptions) => void;
57
- declare const Link: React.FC<LinkProps>;
58
- declare const createRouter: (routes: RoutesInput, storeContext?: Context<StoreContextValue>) => React.FC<Record<string, any>>;
59
-
60
- declare const appHistory: BrowserHistory | null;
61
- declare const makeMemoryHistory: (initialEntries?: string[]) => MemoryHistory;
62
-
63
- interface ProgressAPI {
64
- start(): void;
65
- done(): void;
66
- }
67
- interface HandleHistoryChangeOptions {
68
- history?: BrowserHistory | null;
69
- fetchImpl?: ((url: string, init?: RequestInit) => Promise<Response>) | null;
70
- setTitle?: (title: string) => void;
71
- progress?: ProgressAPI;
72
- }
73
- declare function handleHistoryChange(dispatch: (action: {
74
- type: string;
75
- data: Record<string, unknown>;
76
- }) => void, { history, fetchImpl, setTitle, progress, }?: HandleHistoryChangeOptions): () => void;
77
-
78
- interface ScrollPosition {
79
- x: number;
80
- y: number;
81
- }
82
- declare const getScrollPosition: () => ScrollPosition;
83
- declare const setScrollToSessionStorage: () => void;
84
- declare const setScrollForKey: (key: string, pos?: ScrollPosition) => void;
85
- declare function getScrollFromSessionStorage(key: "*"): Record<string, ScrollPosition>;
86
- declare function getScrollFromSessionStorage(key: string): ScrollPosition | null;
87
-
88
- export { Link, type LinkProps, type LinkTo, type NavigateOptions, type PreparedRoute, type RouteDefinition, type RouteMap, type RouteMatchResult, type RoutesInput, type ScrollPosition, appHistory, createRouter, getScrollFromSessionStorage, getScrollPosition, handleHistoryChange, makeMemoryHistory, navigate, _default as routesHelper, setScrollForKey, setScrollToSessionStorage };
package/dist/index.d.ts DELETED
@@ -1,88 +0,0 @@
1
- import React, { ComponentType, AnchorHTMLAttributes, Context } from 'react';
2
- import { BrowserHistory, MemoryHistory } from 'history';
3
-
4
- interface RouteDefinition {
5
- path: string;
6
- Component?: ComponentType<any>;
7
- element?: ComponentType<any>;
8
- render?: ComponentType<any>;
9
- reducerKey?: string;
10
- }
11
- type RouteMapValue = ComponentType<any> | [ComponentType<any>] | [ComponentType<any>, string] | {
12
- Component?: ComponentType<any>;
13
- element?: ComponentType<any>;
14
- render?: ComponentType<any>;
15
- reducerKey?: string;
16
- };
17
- type RouteMap = Record<string, RouteMapValue>;
18
- type RoutesInput = RouteDefinition[] | RouteMap;
19
- interface PreparedRoute {
20
- path: string;
21
- Component: ComponentType<any>;
22
- reducerKey?: string;
23
- matcher: (pathname: string) => {
24
- params: Record<string, string>;
25
- } | null;
26
- }
27
- interface RouteMatchResult {
28
- Component: ComponentType<any>;
29
- params: Record<string, string>;
30
- reducerKey?: string;
31
- }
32
- declare const _default: {
33
- prepare: (routes?: RoutesInput) => PreparedRoute[];
34
- match: (routes: RoutesInput | PreparedRoute[], pathname: string) => RouteMatchResult;
35
- };
36
-
37
- type LinkTo = string | {
38
- pathname: string;
39
- search?: string;
40
- hash?: string;
41
- };
42
- interface NavigateOptions {
43
- replace?: boolean;
44
- state?: unknown;
45
- }
46
- interface LinkProps extends Omit<AnchorHTMLAttributes<HTMLAnchorElement>, "href"> {
47
- to: LinkTo;
48
- replace?: boolean;
49
- state?: unknown;
50
- }
51
- interface StoreContextValue {
52
- state: Record<string, any>;
53
- dispatch: ((action: Record<string, unknown>) => void) | false;
54
- pageRefresher?: unknown;
55
- }
56
- declare const navigate: (to: LinkTo, options?: NavigateOptions) => void;
57
- declare const Link: React.FC<LinkProps>;
58
- declare const createRouter: (routes: RoutesInput, storeContext?: Context<StoreContextValue>) => React.FC<Record<string, any>>;
59
-
60
- declare const appHistory: BrowserHistory | null;
61
- declare const makeMemoryHistory: (initialEntries?: string[]) => MemoryHistory;
62
-
63
- interface ProgressAPI {
64
- start(): void;
65
- done(): void;
66
- }
67
- interface HandleHistoryChangeOptions {
68
- history?: BrowserHistory | null;
69
- fetchImpl?: ((url: string, init?: RequestInit) => Promise<Response>) | null;
70
- setTitle?: (title: string) => void;
71
- progress?: ProgressAPI;
72
- }
73
- declare function handleHistoryChange(dispatch: (action: {
74
- type: string;
75
- data: Record<string, unknown>;
76
- }) => void, { history, fetchImpl, setTitle, progress, }?: HandleHistoryChangeOptions): () => void;
77
-
78
- interface ScrollPosition {
79
- x: number;
80
- y: number;
81
- }
82
- declare const getScrollPosition: () => ScrollPosition;
83
- declare const setScrollToSessionStorage: () => void;
84
- declare const setScrollForKey: (key: string, pos?: ScrollPosition) => void;
85
- declare function getScrollFromSessionStorage(key: "*"): Record<string, ScrollPosition>;
86
- declare function getScrollFromSessionStorage(key: string): ScrollPosition | null;
87
-
88
- export { Link, type LinkProps, type LinkTo, type NavigateOptions, type PreparedRoute, type RouteDefinition, type RouteMap, type RouteMatchResult, type RoutesInput, type ScrollPosition, appHistory, createRouter, getScrollFromSessionStorage, getScrollPosition, handleHistoryChange, makeMemoryHistory, navigate, _default as routesHelper, setScrollForKey, setScrollToSessionStorage };
package/dist/index.js.map DELETED
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/index.ts","../src/router.tsx","../src/history.ts","../src/helper.ts","../src/scroll.ts","../src/handleHistoryChange.ts"],"sourcesContent":["export { Link, navigate, createRouter } from \"./router\";\nexport { appHistory, makeMemoryHistory } from \"./history\";\nexport { default as routesHelper } from \"./helper\";\nexport { default as handleHistoryChange } from \"./handleHistoryChange\";\nexport {\n getScrollPosition,\n setScrollToSessionStorage,\n setScrollForKey,\n getScrollFromSessionStorage,\n} from \"./scroll\";\n\nexport type { LinkProps, LinkTo, NavigateOptions } from \"./router\";\nexport type {\n RoutesInput,\n RouteDefinition,\n RouteMap,\n PreparedRoute,\n RouteMatchResult,\n} from \"./helper\";\nexport type { ScrollPosition } from \"./scroll\";\n","import React, {\n useContext,\n useMemo,\n useRef,\n useSyncExternalStore,\n type AnchorHTMLAttributes,\n type ComponentType,\n type Context,\n type MouseEvent,\n} from \"react\";\nimport type { BrowserHistory } from \"history\";\nimport history from \"./history\";\nimport helper, {\n type PreparedRoute,\n type RouteMatchResult,\n type RoutesInput,\n} from \"./helper\";\n\nexport type LinkTo =\n | string\n | {\n pathname: string;\n search?: string;\n hash?: string;\n };\n\nexport interface NavigateOptions {\n replace?: boolean;\n state?: unknown;\n}\n\nexport interface LinkProps\n extends Omit<AnchorHTMLAttributes<HTMLAnchorElement>, \"href\"> {\n to: LinkTo;\n replace?: boolean;\n state?: unknown;\n}\n\ninterface StoreContextValue {\n state: Record<string, any>;\n dispatch: ((action: Record<string, unknown>) => void) | false;\n pageRefresher?: unknown;\n}\n\nconst appHistory: BrowserHistory | null = history;\n\nconst getHistoryOrThrow = (): BrowserHistory => {\n if (!appHistory) {\n throw new Error(\n \"History is unavailable in this environment. Use makeMemoryHistory for non-browser usage.\",\n );\n }\n return appHistory;\n};\n\nexport const navigate = (to: LinkTo, options: NavigateOptions = {}): void => {\n const { replace = false, state } = options;\n const activeHistory = getHistoryOrThrow();\n if (replace) activeHistory.replace(to, state);\n else activeHistory.push(to, state);\n};\n\nconst toHref = (to: LinkTo): string => {\n if (typeof to === \"string\") return to;\n const { pathname, search = \"\", hash = \"\" } = to;\n if (typeof pathname !== \"string\" || pathname.length === 0) {\n throw new TypeError(\"Location object 'pathname' must be a non-empty string\");\n }\n return `${pathname}${search}${hash}`;\n};\n\nconst isHttpLikeHref = (href: string): boolean => {\n if (href.startsWith(\"//\")) return false;\n if (\n href.startsWith(\"/\") ||\n href.startsWith(\"./\") ||\n href.startsWith(\"../\") ||\n href.startsWith(\"?\") ||\n href.startsWith(\"#\")\n ) {\n return true;\n }\n const protocolMatch = href.match(/^([a-zA-Z][a-zA-Z\\d+.-]*):/);\n if (!protocolMatch) return true;\n const protocol = protocolMatch[1].toLowerCase();\n if (protocol !== \"http\" && protocol !== \"https\") return false;\n if (typeof window === \"undefined\" || !window.location) return false;\n try {\n return new URL(href, window.location.href).origin === window.location.origin;\n } catch {\n return false;\n }\n};\n\nconst toClientPath = (to: LinkTo, href: string): LinkTo => {\n if (typeof to !== \"string\") return to;\n if (!/^https?:/i.test(href)) return to;\n try {\n const url = new URL(href, window.location.href);\n return `${url.pathname}${url.search}${url.hash}`;\n } catch {\n return to;\n }\n};\n\nconst shouldHandleClientNavigation = (\n event: MouseEvent<HTMLAnchorElement>,\n anchorProps: { target?: string; download?: string; href: string },\n): boolean => {\n if (\n event.defaultPrevented ||\n event.button !== 0 ||\n event.metaKey ||\n event.altKey ||\n event.ctrlKey ||\n event.shiftKey\n ) {\n return false;\n }\n const { target, download, href } = anchorProps;\n if (download !== undefined) return false;\n if (target && target !== \"_self\") return false;\n return isHttpLikeHref(href);\n};\n\nexport const Link: React.FC<LinkProps> = ({\n to,\n replace = false,\n state,\n onClick,\n ...rest\n}) => {\n const href = toHref(to);\n\n const handleClick = (e: MouseEvent<HTMLAnchorElement>): void => {\n if (onClick) onClick(e);\n if (!shouldHandleClientNavigation(e, { ...rest, href })) return;\n e.preventDefault();\n const activeHistory = getHistoryOrThrow();\n const nextTo = toClientPath(to, href);\n if (replace) activeHistory.replace(nextTo, state);\n else activeHistory.push(nextTo, state);\n };\n\n return <a href={href} onClick={handleClick} {...rest} />;\n};\n\nconst matchRoute = (\n routes: RoutesInput | PreparedRoute[],\n pathname: string,\n): RouteMatchResult => helper.match(routes, pathname);\n\nexport const createRouter = (\n routes: RoutesInput,\n storeContext?: Context<StoreContextValue>,\n): React.FC<Record<string, any>> => {\n const Router: React.FC<Record<string, any>> = (props) => {\n const appState: StoreContextValue =\n (storeContext && useContext(storeContext)) || { state: props, dispatch: false };\n\n const { pageRefresher } = appState;\n const { state, dispatch } = appState;\n\n const preparedRoutes = useMemo(() => helper.prepare(routes), [routes]);\n\n const readHistoryLocation = (): string =>\n (history?.location?.pathname || \"\") + (history?.location?.search || \"\");\n const initialLocation = (state && state.location) || readHistoryLocation();\n const lastLocRef = useRef(initialLocation);\n const hasNavigationRef = useRef(false);\n const getLocationSnapshot = (): string =>\n hasNavigationRef.current ? readHistoryLocation() : initialLocation;\n\n const loc = useSyncExternalStore(\n (onStoreChange) => {\n if (!history || typeof history.listen !== \"function\") return () => {};\n const unlisten = history.listen(({ location, action }) => {\n const nextLoc = (location.pathname || \"\") + (location.search || \"\");\n if (nextLoc !== lastLocRef.current) {\n lastLocRef.current = nextLoc;\n hasNavigationRef.current = true;\n if (typeof dispatch === \"function\") {\n dispatch({\n type: \"LOCATION_CHANGED\",\n location: nextLoc,\n meta: { action },\n });\n }\n }\n onStoreChange();\n });\n return () => {\n if (typeof unlisten === \"function\") unlisten();\n };\n },\n getLocationSnapshot,\n getLocationSnapshot,\n );\n\n const activePathname = (loc || \"\").split(\"?\")[0];\n const matched = useMemo(\n () => matchRoute(preparedRoutes, activePathname),\n [preparedRoutes, activePathname],\n );\n\n const Component: ComponentType<any> = matched?.Component || (() => null);\n const routeParams = matched?.params || {};\n\n return (\n <Component\n {...state}\n {...routeParams}\n dispatch={dispatch}\n pageRefresher={pageRefresher}\n />\n );\n };\n\n return Router;\n};\n\nexport default createRouter;\n","// Modernized history singleton with test-friendly exports (ESM)\nimport {\n createBrowserHistory,\n createMemoryHistory,\n type BrowserHistory,\n type MemoryHistory,\n} from \"history\";\n\nexport const appHistory: BrowserHistory | null =\n typeof window !== \"undefined\" &&\n window.document &&\n typeof window.document.createElement === \"function\"\n ? createBrowserHistory()\n : null;\n\nexport const makeMemoryHistory = (initialEntries: string[] = [\"/\"]): MemoryHistory =>\n createMemoryHistory({ initialEntries });\n\nexport default appHistory;\n","// src/helper.ts\n// Route preparation & matching utility for Universal Route.\n// Accepts either an array of route objects OR a map of { \"/path\": Component | [Component, reducerKey] }.\nimport type { ComponentType } from \"react\";\n\nexport interface RouteDefinition {\n path: string;\n Component?: ComponentType<any>;\n element?: ComponentType<any>;\n render?: ComponentType<any>;\n reducerKey?: string;\n}\n\nexport type RouteMapValue =\n | ComponentType<any>\n | [ComponentType<any>]\n | [ComponentType<any>, string]\n | {\n Component?: ComponentType<any>;\n element?: ComponentType<any>;\n render?: ComponentType<any>;\n reducerKey?: string;\n };\n\nexport type RouteMap = Record<string, RouteMapValue>;\n\nexport type RoutesInput = RouteDefinition[] | RouteMap;\n\nexport interface PreparedRoute {\n path: string;\n Component: ComponentType<any>;\n reducerKey?: string;\n matcher: (pathname: string) => { params: Record<string, string> } | null;\n}\n\nexport interface RouteMatchResult {\n Component: ComponentType<any>;\n params: Record<string, string>;\n reducerKey?: string;\n}\n\ntype NormalizedRoute = {\n path: string;\n Component: ComponentType<any>;\n reducerKey?: string;\n};\n\nconst isPreparedRouteArray = (\n routes: RoutesInput | PreparedRoute[],\n): routes is PreparedRoute[] =>\n Array.isArray(routes) &&\n routes.every((route) => typeof route === \"object\" && typeof (route as PreparedRoute).matcher === \"function\");\n\nconst escapeRegex = (s: string): string => s.replace(/[-/\\\\^$*+?.()|[\\]{}]/g, \"\\\\$&\");\nconst decodeParam = (value: string): string => {\n try {\n return decodeURIComponent(value);\n } catch {\n return value;\n }\n};\n\n// Compile a path pattern like \"/users/:id\" into a RegExp with named groups.\n// Supports \"*\" or \"/*\" catch-all.\nconst compilePath = (path: string): { regex: RegExp; names: string[] } => {\n if (!path || path === \"/\") {\n return { regex: /^\\/?$/, names: [] };\n }\n if (path === \"*\" || path === \"/*\") {\n return { regex: /^.*$/, names: [] };\n }\n\n const parts: Array<{ src: string; name: string | null }> = String(path)\n .split(\"/\")\n .filter(Boolean)\n .map((part) => {\n if (part.startsWith(\":\")) {\n const name = part.slice(1);\n return { src: `(?<${name}>[^/]+)`, name };\n }\n return { src: escapeRegex(part), name: null };\n });\n\n const pattern = \"^/\" + parts.map((p) => p.src).join(\"/\") + \"/?$\";\n const names = parts.filter((p) => p.name).map((p) => p.name as string);\n return { regex: new RegExp(pattern), names };\n};\n\n// Graceful 404 Component that renders the text \"404\" (no React import required)\nconst Generic404 = (): string => \"404\";\n\n// Normalize a single map entry: (path, value) -> { path, Component, reducerKey }\nconst normalizeMapEntry = (path: string, value: RouteMapValue): NormalizedRoute => {\n let Component: ComponentType<any>;\n let reducerKey: string | undefined;\n\n if (Array.isArray(value)) {\n [Component, reducerKey] = value;\n } else if (typeof value === \"function\") {\n Component = value;\n } else if (value && typeof value === \"object\") {\n Component = value.Component || value.element || value.render || (() => null);\n reducerKey = value.reducerKey;\n } else {\n Component = () => null;\n }\n\n return { path, Component, reducerKey };\n};\n\n// Normalize a route object from array form: { path, Component | element | render, reducerKey? }\nconst normalizeArrayEntry = (routeObj: RouteDefinition = { path: \"/\" }): NormalizedRoute => {\n const { path = \"/\", reducerKey } = routeObj;\n const Component = routeObj.Component || routeObj.element || routeObj.render || (() => null);\n return { path, Component, reducerKey };\n};\n\n// Convert routes (array or map) into a uniform list of { path, Component, reducerKey }\nconst toList = (routes: RoutesInput): NormalizedRoute[] => {\n if (Array.isArray(routes)) {\n return routes.map(normalizeArrayEntry);\n }\n\n return Object.entries(routes).map(([path, value]) => normalizeMapEntry(path, value));\n};\n\n// Public: prepare routes by attaching a matcher to each entry.\nexport const prepare = (routes: RoutesInput = []): PreparedRoute[] => {\n const list = toList(routes);\n\n return list.map((r) => {\n // Catch-all\n if (r.path === \"*\" || r.path === \"/*\") {\n return { ...r, matcher: () => ({ params: {} }) };\n }\n\n const { regex, names } = compilePath(r.path);\n const matcher = (pathname: string): { params: Record<string, string> } | null => {\n const m = regex.exec(pathname);\n if (!m) return null;\n\n const params = m.groups\n ? Object.fromEntries(\n Object.entries(m.groups).map(([k, v]) => [k, decodeParam(v as string)]),\n )\n : names.reduce<Record<string, string>>((acc, name, i) => {\n acc[name] = decodeParam(m[i + 1]);\n return acc;\n }, {});\n\n return { params };\n };\n\n return { ...r, matcher };\n });\n};\n\n// Internal: find a match in a prepared list\nconst matchOne = (preparedRoutes: PreparedRoute[], pathname: string): RouteMatchResult => {\n for (const r of preparedRoutes) {\n if (typeof r.matcher !== \"function\") continue;\n const res = r.matcher(pathname);\n if (res) {\n return {\n Component: r.Component,\n params: res.params || {},\n reducerKey: r.reducerKey,\n };\n }\n }\n\n // Catch-all fallback if provided\n const star = preparedRoutes.find((r) => r.path === \"*\" || r.path === \"/*\");\n if (star) {\n return {\n Component: star.Component,\n params: {},\n reducerKey: star.reducerKey,\n };\n }\n\n // Generic 404 fallback\n return { Component: Generic404, params: {} };\n};\n\n// Public: match() accepts either raw routes or an already-prepared list\nexport const match = (\n routes: RoutesInput | PreparedRoute[],\n pathname: string,\n): RouteMatchResult => {\n const prepared = isPreparedRouteArray(routes) ? routes : prepare(routes);\n return matchOne(prepared, pathname);\n};\n\nexport default { prepare, match };\n","// Centralized scroll helpers with query-aware keys\nconst SCROLL_KEY = \"scroll\";\nconst MAX_SCROLL_ENTRIES = 100;\n\nexport interface ScrollPosition {\n x: number;\n y: number;\n}\n\nexport const getScrollPosition = (): ScrollPosition => ({\n y: window.pageYOffset || document.documentElement.scrollTop || 0,\n x: window.pageXOffset || document.documentElement.scrollLeft || 0,\n});\n\nconst currentKey = (): string => {\n const { pathname, search } = window.location;\n return `${pathname}${search || \"\"}`;\n};\n\nconst readStore = (): Record<string, ScrollPosition> => {\n if (typeof sessionStorage === \"undefined\") return {};\n try {\n const blob = sessionStorage.getItem(SCROLL_KEY);\n return blob ? (JSON.parse(blob) as Record<string, ScrollPosition>) : {};\n } catch {\n return {};\n }\n};\n\nconst writeStore = (obj: Record<string, ScrollPosition>): void => {\n if (typeof sessionStorage === \"undefined\") return;\n try {\n const keys = Object.keys(obj);\n if (keys.length > MAX_SCROLL_ENTRIES) {\n const excess = keys.length - MAX_SCROLL_ENTRIES;\n for (let i = 0; i < excess; i += 1) {\n delete obj[keys[i]];\n }\n }\n sessionStorage.setItem(SCROLL_KEY, JSON.stringify(obj));\n } catch {\n /* ignore quota/security errors */\n }\n};\n\nexport const setScrollToSessionStorage = (): void => {\n const store = readStore();\n const key = currentKey();\n if (store[key]) delete store[key];\n store[key] = getScrollPosition();\n writeStore(store);\n};\n\nexport const setScrollForKey = (key: string, pos?: ScrollPosition): void => {\n const store = readStore();\n if (store[key]) delete store[key];\n store[key] = pos || getScrollPosition();\n writeStore(store);\n};\n\nexport function getScrollFromSessionStorage(key: \"*\"): Record<string, ScrollPosition>;\nexport function getScrollFromSessionStorage(key: string): ScrollPosition | null;\nexport function getScrollFromSessionStorage(\n key?: string,\n): Record<string, ScrollPosition> | ScrollPosition | null {\n const store = readStore();\n if (key === \"*\" || key === undefined) return store;\n return store[key] || null;\n}\n","import type { BrowserHistory, Update } from \"history\";\nimport { getScrollFromSessionStorage, type ScrollPosition } from \"./scroll\";\n\nexport interface ProgressAPI {\n start(): void;\n done(): void;\n}\n\nexport interface HandleHistoryChangeOptions {\n history?: BrowserHistory | null;\n fetchImpl?: ((url: string, init?: RequestInit) => Promise<Response>) | null;\n setTitle?: (title: string) => void;\n progress?: ProgressAPI;\n}\n\nconst makeUuid = (): string => {\n // Browser / Workers\n if (typeof globalThis !== \"undefined\" && globalThis.crypto) {\n if (typeof globalThis.crypto.randomUUID === \"function\") {\n return globalThis.crypto.randomUUID();\n }\n // RFC4122 v4 via getRandomValues\n const buf = new Uint8Array(16);\n globalThis.crypto.getRandomValues(buf);\n buf[6] = (buf[6] & 0x0f) | 0x40;\n buf[8] = (buf[8] & 0x3f) | 0x80;\n const hex = [...buf].map((b) => b.toString(16).padStart(2, \"0\"));\n return `${hex.slice(0, 4).join(\"\")}-${hex.slice(4, 6).join(\"\")}-${hex\n .slice(6, 8)\n .join(\"\")}-${hex.slice(8, 10).join(\"\")}-${hex.slice(10).join(\"\")}`;\n }\n // Node\n try {\n const { randomUUID } = (globalThis as any).require(\"node:crypto\");\n if (typeof randomUUID === \"function\") return randomUUID();\n } catch {}\n // Last-resort (non-crypto)\n return \"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx\".replace(/[xy]/g, (c) => {\n const r = (Math.random() * 16) | 0;\n const v = c === \"x\" ? r : (r & 0x3) | 0x8;\n return v.toString(16);\n });\n};\n\nconst INSTALLED = Symbol.for(\"handleHistoryChange:installed\");\nlet _inFlight: AbortController | null = null;\nlet _latestRequestId: number = 0;\nlet _scrollRestoreTimeout: ReturnType<typeof setTimeout> | null = null;\n\nconst clearScrollRestoreTimeout = (): void => {\n if (_scrollRestoreTimeout) {\n clearTimeout(_scrollRestoreTimeout);\n _scrollRestoreTimeout = null;\n }\n};\n\nconst originOf = (): string => {\n try {\n if (\n typeof window !== \"undefined\" &&\n window.location &&\n window.location.origin\n ) {\n return window.location.origin;\n }\n } catch {}\n return \"http://localhost\";\n};\n\nconst buildUrl = (loc: { pathname?: string; search?: string }): string => {\n const url = new URL((loc.pathname || \"/\") + (loc.search || \"\"), originOf());\n url.searchParams.set(\"uuid\", makeUuid());\n return url.toString();\n};\n\nconst kindFrom = (status: number): \"ok\" | \"404\" | \"5xx\" => {\n if (status === 404) return \"404\";\n if (Math.floor(status / 100) === 5) return \"5xx\";\n return \"ok\";\n};\n\nexport default function handleHistoryChange(\n dispatch: (action: { type: string; data: Record<string, unknown> }) => void,\n {\n history,\n fetchImpl = (typeof fetch !== \"undefined\" && fetch) || null,\n setTitle = function (t: string): void {\n if (typeof document !== \"undefined\" && t) document.title = t;\n },\n progress = { start() {}, done() {} }, // optional in tests\n }: HandleHistoryChangeOptions = {},\n): () => void {\n if (!history || !fetchImpl) {\n return () => {};\n }\n\n if ((history as any)[INSTALLED]) {\n return () => {};\n }\n (history as any)[INSTALLED] = true;\n\n const unlisten = history.listen(function ({ location, action }: Update): void {\n // Abort prior request\n if (_inFlight && typeof _inFlight.abort === \"function\") {\n try {\n _inFlight.abort();\n } catch {}\n }\n clearScrollRestoreTimeout();\n const requestId = ++_latestRequestId;\n _inFlight =\n typeof AbortController !== \"undefined\" ? new AbortController() : null;\n\n if (progress && typeof progress.done === \"function\") progress.done();\n if (progress && typeof progress.start === \"function\") progress.start();\n\n const url = buildUrl(location);\n\n // Single promise chain so one microtask drain should be enough in tests\n Promise.resolve(\n fetchImpl(url, {\n method: \"GET\",\n headers: { Accept: \"application/json\" },\n signal: _inFlight ? _inFlight.signal : undefined,\n }),\n )\n .then(function (res): Promise<{ status: number; data: Record<string, unknown> }> {\n const jp = res && res.json ? res.json() : {};\n return Promise.resolve(jp)\n .then(function (data) {\n return { status: res ? res.status : 503, data: (data as Record<string, unknown>) || {} };\n })\n .catch(function () {\n return { status: res ? res.status : 503, data: {} };\n });\n })\n .catch(function () {\n return { status: 503, data: {} };\n })\n .then(function ({ status, data }): void {\n if (requestId !== _latestRequestId) return;\n if (progress && typeof progress.done === \"function\") progress.done();\n\n // Authorization redirect wins\n const authLoc = (data as { authorization?: { location?: string } }).authorization\n ?.location;\n let finalLoc = authLoc || location.pathname || \"/\";\n\n // Map 404/5xx if no explicit auth redirect\n if (!authLoc) {\n const k = kindFrom(status);\n if (k === \"404\") finalLoc = \"/404\";\n else if (k === \"5xx\") finalLoc = \"/500\";\n }\n\n dispatch({\n type: \"CHANGE_PAGE\",\n data: Object.assign({}, data, { location: finalLoc }),\n });\n\n // Title from top-level data.title\n const title = (data as { title?: string }).title;\n if (title) {\n // eslint-disable-next-line no-console\n setTitle(title);\n }\n\n // Scroll behavior: top on PUSH; restore for POP/REPLACE\n if (typeof window !== \"undefined\" && window.scrollTo) {\n if (action === \"PUSH\") {\n window.scrollTo(0, 0);\n } else {\n const key = (location.pathname || \"/\") + (location.search || \"\");\n const prev: ScrollPosition | null = getScrollFromSessionStorage(key);\n if (prev) {\n _scrollRestoreTimeout = setTimeout(function () {\n window.scrollTo(prev.x || 0, prev.y || 0);\n _scrollRestoreTimeout = null;\n }, 250);\n }\n }\n }\n });\n });\n\n return (): void => {\n if (typeof unlisten === \"function\") unlisten();\n clearScrollRestoreTimeout();\n if (_inFlight && typeof _inFlight.abort === \"function\") {\n try {\n _inFlight.abort();\n } catch {}\n }\n _inFlight = null;\n (history as any)[INSTALLED] = false;\n };\n}\n\n// Test helpers (reset does nothing now because the guard is per-history instance)\nexport const __test__: {\n reset: () => void;\n state: () => { inFlight: boolean };\n} = {\n reset: function (): void {\n _latestRequestId = 0;\n _inFlight = null;\n clearScrollRestoreTimeout();\n },\n state: function (): { inFlight: boolean } {\n return {\n inFlight: !!_inFlight,\n };\n },\n};\n"],"mappings":"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","names":["appHistory"]}
package/dist/index.mjs DELETED
@@ -1,473 +0,0 @@
1
- // src/router.tsx
2
- import {
3
- useContext,
4
- useMemo,
5
- useRef,
6
- useSyncExternalStore
7
- } from "react";
8
-
9
- // src/history.ts
10
- import {
11
- createBrowserHistory,
12
- createMemoryHistory
13
- } from "history";
14
- var appHistory = typeof window !== "undefined" && window.document && typeof window.document.createElement === "function" ? createBrowserHistory() : null;
15
- var makeMemoryHistory = (initialEntries = ["/"]) => createMemoryHistory({ initialEntries });
16
- var history_default = appHistory;
17
-
18
- // src/helper.ts
19
- var isPreparedRouteArray = (routes) => Array.isArray(routes) && routes.every((route) => typeof route === "object" && typeof route.matcher === "function");
20
- var escapeRegex = (s) => s.replace(/[-/\\^$*+?.()|[\]{}]/g, "\\$&");
21
- var decodeParam = (value) => {
22
- try {
23
- return decodeURIComponent(value);
24
- } catch {
25
- return value;
26
- }
27
- };
28
- var compilePath = (path) => {
29
- if (!path || path === "/") {
30
- return { regex: /^\/?$/, names: [] };
31
- }
32
- if (path === "*" || path === "/*") {
33
- return { regex: /^.*$/, names: [] };
34
- }
35
- const parts = String(path).split("/").filter(Boolean).map((part) => {
36
- if (part.startsWith(":")) {
37
- const name = part.slice(1);
38
- return { src: `(?<${name}>[^/]+)`, name };
39
- }
40
- return { src: escapeRegex(part), name: null };
41
- });
42
- const pattern = "^/" + parts.map((p) => p.src).join("/") + "/?$";
43
- const names = parts.filter((p) => p.name).map((p) => p.name);
44
- return { regex: new RegExp(pattern), names };
45
- };
46
- var Generic404 = () => "404";
47
- var normalizeMapEntry = (path, value) => {
48
- let Component;
49
- let reducerKey;
50
- if (Array.isArray(value)) {
51
- [Component, reducerKey] = value;
52
- } else if (typeof value === "function") {
53
- Component = value;
54
- } else if (value && typeof value === "object") {
55
- Component = value.Component || value.element || value.render || (() => null);
56
- reducerKey = value.reducerKey;
57
- } else {
58
- Component = () => null;
59
- }
60
- return { path, Component, reducerKey };
61
- };
62
- var normalizeArrayEntry = (routeObj = { path: "/" }) => {
63
- const { path = "/", reducerKey } = routeObj;
64
- const Component = routeObj.Component || routeObj.element || routeObj.render || (() => null);
65
- return { path, Component, reducerKey };
66
- };
67
- var toList = (routes) => {
68
- if (Array.isArray(routes)) {
69
- return routes.map(normalizeArrayEntry);
70
- }
71
- return Object.entries(routes).map(([path, value]) => normalizeMapEntry(path, value));
72
- };
73
- var prepare = (routes = []) => {
74
- const list = toList(routes);
75
- return list.map((r) => {
76
- if (r.path === "*" || r.path === "/*") {
77
- return { ...r, matcher: () => ({ params: {} }) };
78
- }
79
- const { regex, names } = compilePath(r.path);
80
- const matcher = (pathname) => {
81
- const m = regex.exec(pathname);
82
- if (!m) return null;
83
- const params = m.groups ? Object.fromEntries(
84
- Object.entries(m.groups).map(([k, v]) => [k, decodeParam(v)])
85
- ) : names.reduce((acc, name, i) => {
86
- acc[name] = decodeParam(m[i + 1]);
87
- return acc;
88
- }, {});
89
- return { params };
90
- };
91
- return { ...r, matcher };
92
- });
93
- };
94
- var matchOne = (preparedRoutes, pathname) => {
95
- for (const r of preparedRoutes) {
96
- if (typeof r.matcher !== "function") continue;
97
- const res = r.matcher(pathname);
98
- if (res) {
99
- return {
100
- Component: r.Component,
101
- params: res.params || {},
102
- reducerKey: r.reducerKey
103
- };
104
- }
105
- }
106
- const star = preparedRoutes.find((r) => r.path === "*" || r.path === "/*");
107
- if (star) {
108
- return {
109
- Component: star.Component,
110
- params: {},
111
- reducerKey: star.reducerKey
112
- };
113
- }
114
- return { Component: Generic404, params: {} };
115
- };
116
- var match = (routes, pathname) => {
117
- const prepared = isPreparedRouteArray(routes) ? routes : prepare(routes);
118
- return matchOne(prepared, pathname);
119
- };
120
- var helper_default = { prepare, match };
121
-
122
- // src/router.tsx
123
- import { jsx } from "react/jsx-runtime";
124
- var appHistory2 = history_default;
125
- var getHistoryOrThrow = () => {
126
- if (!appHistory2) {
127
- throw new Error(
128
- "History is unavailable in this environment. Use makeMemoryHistory for non-browser usage."
129
- );
130
- }
131
- return appHistory2;
132
- };
133
- var navigate = (to, options = {}) => {
134
- const { replace = false, state } = options;
135
- const activeHistory = getHistoryOrThrow();
136
- if (replace) activeHistory.replace(to, state);
137
- else activeHistory.push(to, state);
138
- };
139
- var toHref = (to) => {
140
- if (typeof to === "string") return to;
141
- const { pathname, search = "", hash = "" } = to;
142
- if (typeof pathname !== "string" || pathname.length === 0) {
143
- throw new TypeError("Location object 'pathname' must be a non-empty string");
144
- }
145
- return `${pathname}${search}${hash}`;
146
- };
147
- var isHttpLikeHref = (href) => {
148
- if (href.startsWith("//")) return false;
149
- if (href.startsWith("/") || href.startsWith("./") || href.startsWith("../") || href.startsWith("?") || href.startsWith("#")) {
150
- return true;
151
- }
152
- const protocolMatch = href.match(/^([a-zA-Z][a-zA-Z\d+.-]*):/);
153
- if (!protocolMatch) return true;
154
- const protocol = protocolMatch[1].toLowerCase();
155
- if (protocol !== "http" && protocol !== "https") return false;
156
- if (typeof window === "undefined" || !window.location) return false;
157
- try {
158
- return new URL(href, window.location.href).origin === window.location.origin;
159
- } catch {
160
- return false;
161
- }
162
- };
163
- var toClientPath = (to, href) => {
164
- if (typeof to !== "string") return to;
165
- if (!/^https?:/i.test(href)) return to;
166
- try {
167
- const url = new URL(href, window.location.href);
168
- return `${url.pathname}${url.search}${url.hash}`;
169
- } catch {
170
- return to;
171
- }
172
- };
173
- var shouldHandleClientNavigation = (event, anchorProps) => {
174
- if (event.defaultPrevented || event.button !== 0 || event.metaKey || event.altKey || event.ctrlKey || event.shiftKey) {
175
- return false;
176
- }
177
- const { target, download, href } = anchorProps;
178
- if (download !== void 0) return false;
179
- if (target && target !== "_self") return false;
180
- return isHttpLikeHref(href);
181
- };
182
- var Link = ({
183
- to,
184
- replace = false,
185
- state,
186
- onClick,
187
- ...rest
188
- }) => {
189
- const href = toHref(to);
190
- const handleClick = (e) => {
191
- if (onClick) onClick(e);
192
- if (!shouldHandleClientNavigation(e, { ...rest, href })) return;
193
- e.preventDefault();
194
- const activeHistory = getHistoryOrThrow();
195
- const nextTo = toClientPath(to, href);
196
- if (replace) activeHistory.replace(nextTo, state);
197
- else activeHistory.push(nextTo, state);
198
- };
199
- return /* @__PURE__ */ jsx("a", { href, onClick: handleClick, ...rest });
200
- };
201
- var matchRoute = (routes, pathname) => helper_default.match(routes, pathname);
202
- var createRouter = (routes, storeContext) => {
203
- const Router = (props) => {
204
- const appState = storeContext && useContext(storeContext) || { state: props, dispatch: false };
205
- const { pageRefresher } = appState;
206
- const { state, dispatch } = appState;
207
- const preparedRoutes = useMemo(() => helper_default.prepare(routes), [routes]);
208
- const readHistoryLocation = () => (history_default?.location?.pathname || "") + (history_default?.location?.search || "");
209
- const initialLocation = state && state.location || readHistoryLocation();
210
- const lastLocRef = useRef(initialLocation);
211
- const hasNavigationRef = useRef(false);
212
- const getLocationSnapshot = () => hasNavigationRef.current ? readHistoryLocation() : initialLocation;
213
- const loc = useSyncExternalStore(
214
- (onStoreChange) => {
215
- if (!history_default || typeof history_default.listen !== "function") return () => {
216
- };
217
- const unlisten = history_default.listen(({ location, action }) => {
218
- const nextLoc = (location.pathname || "") + (location.search || "");
219
- if (nextLoc !== lastLocRef.current) {
220
- lastLocRef.current = nextLoc;
221
- hasNavigationRef.current = true;
222
- if (typeof dispatch === "function") {
223
- dispatch({
224
- type: "LOCATION_CHANGED",
225
- location: nextLoc,
226
- meta: { action }
227
- });
228
- }
229
- }
230
- onStoreChange();
231
- });
232
- return () => {
233
- if (typeof unlisten === "function") unlisten();
234
- };
235
- },
236
- getLocationSnapshot,
237
- getLocationSnapshot
238
- );
239
- const activePathname = (loc || "").split("?")[0];
240
- const matched = useMemo(
241
- () => matchRoute(preparedRoutes, activePathname),
242
- [preparedRoutes, activePathname]
243
- );
244
- const Component = matched?.Component || (() => null);
245
- const routeParams = matched?.params || {};
246
- return /* @__PURE__ */ jsx(
247
- Component,
248
- {
249
- ...state,
250
- ...routeParams,
251
- dispatch,
252
- pageRefresher
253
- }
254
- );
255
- };
256
- return Router;
257
- };
258
-
259
- // src/scroll.ts
260
- var SCROLL_KEY = "scroll";
261
- var MAX_SCROLL_ENTRIES = 100;
262
- var getScrollPosition = () => ({
263
- y: window.pageYOffset || document.documentElement.scrollTop || 0,
264
- x: window.pageXOffset || document.documentElement.scrollLeft || 0
265
- });
266
- var currentKey = () => {
267
- const { pathname, search } = window.location;
268
- return `${pathname}${search || ""}`;
269
- };
270
- var readStore = () => {
271
- if (typeof sessionStorage === "undefined") return {};
272
- try {
273
- const blob = sessionStorage.getItem(SCROLL_KEY);
274
- return blob ? JSON.parse(blob) : {};
275
- } catch {
276
- return {};
277
- }
278
- };
279
- var writeStore = (obj) => {
280
- if (typeof sessionStorage === "undefined") return;
281
- try {
282
- const keys = Object.keys(obj);
283
- if (keys.length > MAX_SCROLL_ENTRIES) {
284
- const excess = keys.length - MAX_SCROLL_ENTRIES;
285
- for (let i = 0; i < excess; i += 1) {
286
- delete obj[keys[i]];
287
- }
288
- }
289
- sessionStorage.setItem(SCROLL_KEY, JSON.stringify(obj));
290
- } catch {
291
- }
292
- };
293
- var setScrollToSessionStorage = () => {
294
- const store = readStore();
295
- const key = currentKey();
296
- if (store[key]) delete store[key];
297
- store[key] = getScrollPosition();
298
- writeStore(store);
299
- };
300
- var setScrollForKey = (key, pos) => {
301
- const store = readStore();
302
- if (store[key]) delete store[key];
303
- store[key] = pos || getScrollPosition();
304
- writeStore(store);
305
- };
306
- function getScrollFromSessionStorage(key) {
307
- const store = readStore();
308
- if (key === "*" || key === void 0) return store;
309
- return store[key] || null;
310
- }
311
-
312
- // src/handleHistoryChange.ts
313
- var makeUuid = () => {
314
- if (typeof globalThis !== "undefined" && globalThis.crypto) {
315
- if (typeof globalThis.crypto.randomUUID === "function") {
316
- return globalThis.crypto.randomUUID();
317
- }
318
- const buf = new Uint8Array(16);
319
- globalThis.crypto.getRandomValues(buf);
320
- buf[6] = buf[6] & 15 | 64;
321
- buf[8] = buf[8] & 63 | 128;
322
- const hex = [...buf].map((b) => b.toString(16).padStart(2, "0"));
323
- return `${hex.slice(0, 4).join("")}-${hex.slice(4, 6).join("")}-${hex.slice(6, 8).join("")}-${hex.slice(8, 10).join("")}-${hex.slice(10).join("")}`;
324
- }
325
- try {
326
- const { randomUUID } = globalThis.require("node:crypto");
327
- if (typeof randomUUID === "function") return randomUUID();
328
- } catch {
329
- }
330
- return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c) => {
331
- const r = Math.random() * 16 | 0;
332
- const v = c === "x" ? r : r & 3 | 8;
333
- return v.toString(16);
334
- });
335
- };
336
- var INSTALLED = /* @__PURE__ */ Symbol.for("handleHistoryChange:installed");
337
- var _inFlight = null;
338
- var _latestRequestId = 0;
339
- var _scrollRestoreTimeout = null;
340
- var clearScrollRestoreTimeout = () => {
341
- if (_scrollRestoreTimeout) {
342
- clearTimeout(_scrollRestoreTimeout);
343
- _scrollRestoreTimeout = null;
344
- }
345
- };
346
- var originOf = () => {
347
- try {
348
- if (typeof window !== "undefined" && window.location && window.location.origin) {
349
- return window.location.origin;
350
- }
351
- } catch {
352
- }
353
- return "http://localhost";
354
- };
355
- var buildUrl = (loc) => {
356
- const url = new URL((loc.pathname || "/") + (loc.search || ""), originOf());
357
- url.searchParams.set("uuid", makeUuid());
358
- return url.toString();
359
- };
360
- var kindFrom = (status) => {
361
- if (status === 404) return "404";
362
- if (Math.floor(status / 100) === 5) return "5xx";
363
- return "ok";
364
- };
365
- function handleHistoryChange(dispatch, {
366
- history,
367
- fetchImpl = typeof fetch !== "undefined" && fetch || null,
368
- setTitle = function(t) {
369
- if (typeof document !== "undefined" && t) document.title = t;
370
- },
371
- progress = { start() {
372
- }, done() {
373
- } }
374
- // optional in tests
375
- } = {}) {
376
- if (!history || !fetchImpl) {
377
- return () => {
378
- };
379
- }
380
- if (history[INSTALLED]) {
381
- return () => {
382
- };
383
- }
384
- history[INSTALLED] = true;
385
- const unlisten = history.listen(function({ location, action }) {
386
- if (_inFlight && typeof _inFlight.abort === "function") {
387
- try {
388
- _inFlight.abort();
389
- } catch {
390
- }
391
- }
392
- clearScrollRestoreTimeout();
393
- const requestId = ++_latestRequestId;
394
- _inFlight = typeof AbortController !== "undefined" ? new AbortController() : null;
395
- if (progress && typeof progress.done === "function") progress.done();
396
- if (progress && typeof progress.start === "function") progress.start();
397
- const url = buildUrl(location);
398
- Promise.resolve(
399
- fetchImpl(url, {
400
- method: "GET",
401
- headers: { Accept: "application/json" },
402
- signal: _inFlight ? _inFlight.signal : void 0
403
- })
404
- ).then(function(res) {
405
- const jp = res && res.json ? res.json() : {};
406
- return Promise.resolve(jp).then(function(data) {
407
- return { status: res ? res.status : 503, data: data || {} };
408
- }).catch(function() {
409
- return { status: res ? res.status : 503, data: {} };
410
- });
411
- }).catch(function() {
412
- return { status: 503, data: {} };
413
- }).then(function({ status, data }) {
414
- if (requestId !== _latestRequestId) return;
415
- if (progress && typeof progress.done === "function") progress.done();
416
- const authLoc = data.authorization?.location;
417
- let finalLoc = authLoc || location.pathname || "/";
418
- if (!authLoc) {
419
- const k = kindFrom(status);
420
- if (k === "404") finalLoc = "/404";
421
- else if (k === "5xx") finalLoc = "/500";
422
- }
423
- dispatch({
424
- type: "CHANGE_PAGE",
425
- data: Object.assign({}, data, { location: finalLoc })
426
- });
427
- const title = data.title;
428
- if (title) {
429
- setTitle(title);
430
- }
431
- if (typeof window !== "undefined" && window.scrollTo) {
432
- if (action === "PUSH") {
433
- window.scrollTo(0, 0);
434
- } else {
435
- const key = (location.pathname || "/") + (location.search || "");
436
- const prev = getScrollFromSessionStorage(key);
437
- if (prev) {
438
- _scrollRestoreTimeout = setTimeout(function() {
439
- window.scrollTo(prev.x || 0, prev.y || 0);
440
- _scrollRestoreTimeout = null;
441
- }, 250);
442
- }
443
- }
444
- }
445
- });
446
- });
447
- return () => {
448
- if (typeof unlisten === "function") unlisten();
449
- clearScrollRestoreTimeout();
450
- if (_inFlight && typeof _inFlight.abort === "function") {
451
- try {
452
- _inFlight.abort();
453
- } catch {
454
- }
455
- }
456
- _inFlight = null;
457
- history[INSTALLED] = false;
458
- };
459
- }
460
- export {
461
- Link,
462
- appHistory,
463
- createRouter,
464
- getScrollFromSessionStorage,
465
- getScrollPosition,
466
- handleHistoryChange,
467
- makeMemoryHistory,
468
- navigate,
469
- helper_default as routesHelper,
470
- setScrollForKey,
471
- setScrollToSessionStorage
472
- };
473
- //# sourceMappingURL=index.mjs.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/router.tsx","../src/history.ts","../src/helper.ts","../src/scroll.ts","../src/handleHistoryChange.ts"],"sourcesContent":["import React, {\n useContext,\n useMemo,\n useRef,\n useSyncExternalStore,\n type AnchorHTMLAttributes,\n type ComponentType,\n type Context,\n type MouseEvent,\n} from \"react\";\nimport type { BrowserHistory } from \"history\";\nimport history from \"./history\";\nimport helper, {\n type PreparedRoute,\n type RouteMatchResult,\n type RoutesInput,\n} from \"./helper\";\n\nexport type LinkTo =\n | string\n | {\n pathname: string;\n search?: string;\n hash?: string;\n };\n\nexport interface NavigateOptions {\n replace?: boolean;\n state?: unknown;\n}\n\nexport interface LinkProps\n extends Omit<AnchorHTMLAttributes<HTMLAnchorElement>, \"href\"> {\n to: LinkTo;\n replace?: boolean;\n state?: unknown;\n}\n\ninterface StoreContextValue {\n state: Record<string, any>;\n dispatch: ((action: Record<string, unknown>) => void) | false;\n pageRefresher?: unknown;\n}\n\nconst appHistory: BrowserHistory | null = history;\n\nconst getHistoryOrThrow = (): BrowserHistory => {\n if (!appHistory) {\n throw new Error(\n \"History is unavailable in this environment. Use makeMemoryHistory for non-browser usage.\",\n );\n }\n return appHistory;\n};\n\nexport const navigate = (to: LinkTo, options: NavigateOptions = {}): void => {\n const { replace = false, state } = options;\n const activeHistory = getHistoryOrThrow();\n if (replace) activeHistory.replace(to, state);\n else activeHistory.push(to, state);\n};\n\nconst toHref = (to: LinkTo): string => {\n if (typeof to === \"string\") return to;\n const { pathname, search = \"\", hash = \"\" } = to;\n if (typeof pathname !== \"string\" || pathname.length === 0) {\n throw new TypeError(\"Location object 'pathname' must be a non-empty string\");\n }\n return `${pathname}${search}${hash}`;\n};\n\nconst isHttpLikeHref = (href: string): boolean => {\n if (href.startsWith(\"//\")) return false;\n if (\n href.startsWith(\"/\") ||\n href.startsWith(\"./\") ||\n href.startsWith(\"../\") ||\n href.startsWith(\"?\") ||\n href.startsWith(\"#\")\n ) {\n return true;\n }\n const protocolMatch = href.match(/^([a-zA-Z][a-zA-Z\\d+.-]*):/);\n if (!protocolMatch) return true;\n const protocol = protocolMatch[1].toLowerCase();\n if (protocol !== \"http\" && protocol !== \"https\") return false;\n if (typeof window === \"undefined\" || !window.location) return false;\n try {\n return new URL(href, window.location.href).origin === window.location.origin;\n } catch {\n return false;\n }\n};\n\nconst toClientPath = (to: LinkTo, href: string): LinkTo => {\n if (typeof to !== \"string\") return to;\n if (!/^https?:/i.test(href)) return to;\n try {\n const url = new URL(href, window.location.href);\n return `${url.pathname}${url.search}${url.hash}`;\n } catch {\n return to;\n }\n};\n\nconst shouldHandleClientNavigation = (\n event: MouseEvent<HTMLAnchorElement>,\n anchorProps: { target?: string; download?: string; href: string },\n): boolean => {\n if (\n event.defaultPrevented ||\n event.button !== 0 ||\n event.metaKey ||\n event.altKey ||\n event.ctrlKey ||\n event.shiftKey\n ) {\n return false;\n }\n const { target, download, href } = anchorProps;\n if (download !== undefined) return false;\n if (target && target !== \"_self\") return false;\n return isHttpLikeHref(href);\n};\n\nexport const Link: React.FC<LinkProps> = ({\n to,\n replace = false,\n state,\n onClick,\n ...rest\n}) => {\n const href = toHref(to);\n\n const handleClick = (e: MouseEvent<HTMLAnchorElement>): void => {\n if (onClick) onClick(e);\n if (!shouldHandleClientNavigation(e, { ...rest, href })) return;\n e.preventDefault();\n const activeHistory = getHistoryOrThrow();\n const nextTo = toClientPath(to, href);\n if (replace) activeHistory.replace(nextTo, state);\n else activeHistory.push(nextTo, state);\n };\n\n return <a href={href} onClick={handleClick} {...rest} />;\n};\n\nconst matchRoute = (\n routes: RoutesInput | PreparedRoute[],\n pathname: string,\n): RouteMatchResult => helper.match(routes, pathname);\n\nexport const createRouter = (\n routes: RoutesInput,\n storeContext?: Context<StoreContextValue>,\n): React.FC<Record<string, any>> => {\n const Router: React.FC<Record<string, any>> = (props) => {\n const appState: StoreContextValue =\n (storeContext && useContext(storeContext)) || { state: props, dispatch: false };\n\n const { pageRefresher } = appState;\n const { state, dispatch } = appState;\n\n const preparedRoutes = useMemo(() => helper.prepare(routes), [routes]);\n\n const readHistoryLocation = (): string =>\n (history?.location?.pathname || \"\") + (history?.location?.search || \"\");\n const initialLocation = (state && state.location) || readHistoryLocation();\n const lastLocRef = useRef(initialLocation);\n const hasNavigationRef = useRef(false);\n const getLocationSnapshot = (): string =>\n hasNavigationRef.current ? readHistoryLocation() : initialLocation;\n\n const loc = useSyncExternalStore(\n (onStoreChange) => {\n if (!history || typeof history.listen !== \"function\") return () => {};\n const unlisten = history.listen(({ location, action }) => {\n const nextLoc = (location.pathname || \"\") + (location.search || \"\");\n if (nextLoc !== lastLocRef.current) {\n lastLocRef.current = nextLoc;\n hasNavigationRef.current = true;\n if (typeof dispatch === \"function\") {\n dispatch({\n type: \"LOCATION_CHANGED\",\n location: nextLoc,\n meta: { action },\n });\n }\n }\n onStoreChange();\n });\n return () => {\n if (typeof unlisten === \"function\") unlisten();\n };\n },\n getLocationSnapshot,\n getLocationSnapshot,\n );\n\n const activePathname = (loc || \"\").split(\"?\")[0];\n const matched = useMemo(\n () => matchRoute(preparedRoutes, activePathname),\n [preparedRoutes, activePathname],\n );\n\n const Component: ComponentType<any> = matched?.Component || (() => null);\n const routeParams = matched?.params || {};\n\n return (\n <Component\n {...state}\n {...routeParams}\n dispatch={dispatch}\n pageRefresher={pageRefresher}\n />\n );\n };\n\n return Router;\n};\n\nexport default createRouter;\n","// Modernized history singleton with test-friendly exports (ESM)\nimport {\n createBrowserHistory,\n createMemoryHistory,\n type BrowserHistory,\n type MemoryHistory,\n} from \"history\";\n\nexport const appHistory: BrowserHistory | null =\n typeof window !== \"undefined\" &&\n window.document &&\n typeof window.document.createElement === \"function\"\n ? createBrowserHistory()\n : null;\n\nexport const makeMemoryHistory = (initialEntries: string[] = [\"/\"]): MemoryHistory =>\n createMemoryHistory({ initialEntries });\n\nexport default appHistory;\n","// src/helper.ts\n// Route preparation & matching utility for Universal Route.\n// Accepts either an array of route objects OR a map of { \"/path\": Component | [Component, reducerKey] }.\nimport type { ComponentType } from \"react\";\n\nexport interface RouteDefinition {\n path: string;\n Component?: ComponentType<any>;\n element?: ComponentType<any>;\n render?: ComponentType<any>;\n reducerKey?: string;\n}\n\nexport type RouteMapValue =\n | ComponentType<any>\n | [ComponentType<any>]\n | [ComponentType<any>, string]\n | {\n Component?: ComponentType<any>;\n element?: ComponentType<any>;\n render?: ComponentType<any>;\n reducerKey?: string;\n };\n\nexport type RouteMap = Record<string, RouteMapValue>;\n\nexport type RoutesInput = RouteDefinition[] | RouteMap;\n\nexport interface PreparedRoute {\n path: string;\n Component: ComponentType<any>;\n reducerKey?: string;\n matcher: (pathname: string) => { params: Record<string, string> } | null;\n}\n\nexport interface RouteMatchResult {\n Component: ComponentType<any>;\n params: Record<string, string>;\n reducerKey?: string;\n}\n\ntype NormalizedRoute = {\n path: string;\n Component: ComponentType<any>;\n reducerKey?: string;\n};\n\nconst isPreparedRouteArray = (\n routes: RoutesInput | PreparedRoute[],\n): routes is PreparedRoute[] =>\n Array.isArray(routes) &&\n routes.every((route) => typeof route === \"object\" && typeof (route as PreparedRoute).matcher === \"function\");\n\nconst escapeRegex = (s: string): string => s.replace(/[-/\\\\^$*+?.()|[\\]{}]/g, \"\\\\$&\");\nconst decodeParam = (value: string): string => {\n try {\n return decodeURIComponent(value);\n } catch {\n return value;\n }\n};\n\n// Compile a path pattern like \"/users/:id\" into a RegExp with named groups.\n// Supports \"*\" or \"/*\" catch-all.\nconst compilePath = (path: string): { regex: RegExp; names: string[] } => {\n if (!path || path === \"/\") {\n return { regex: /^\\/?$/, names: [] };\n }\n if (path === \"*\" || path === \"/*\") {\n return { regex: /^.*$/, names: [] };\n }\n\n const parts: Array<{ src: string; name: string | null }> = String(path)\n .split(\"/\")\n .filter(Boolean)\n .map((part) => {\n if (part.startsWith(\":\")) {\n const name = part.slice(1);\n return { src: `(?<${name}>[^/]+)`, name };\n }\n return { src: escapeRegex(part), name: null };\n });\n\n const pattern = \"^/\" + parts.map((p) => p.src).join(\"/\") + \"/?$\";\n const names = parts.filter((p) => p.name).map((p) => p.name as string);\n return { regex: new RegExp(pattern), names };\n};\n\n// Graceful 404 Component that renders the text \"404\" (no React import required)\nconst Generic404 = (): string => \"404\";\n\n// Normalize a single map entry: (path, value) -> { path, Component, reducerKey }\nconst normalizeMapEntry = (path: string, value: RouteMapValue): NormalizedRoute => {\n let Component: ComponentType<any>;\n let reducerKey: string | undefined;\n\n if (Array.isArray(value)) {\n [Component, reducerKey] = value;\n } else if (typeof value === \"function\") {\n Component = value;\n } else if (value && typeof value === \"object\") {\n Component = value.Component || value.element || value.render || (() => null);\n reducerKey = value.reducerKey;\n } else {\n Component = () => null;\n }\n\n return { path, Component, reducerKey };\n};\n\n// Normalize a route object from array form: { path, Component | element | render, reducerKey? }\nconst normalizeArrayEntry = (routeObj: RouteDefinition = { path: \"/\" }): NormalizedRoute => {\n const { path = \"/\", reducerKey } = routeObj;\n const Component = routeObj.Component || routeObj.element || routeObj.render || (() => null);\n return { path, Component, reducerKey };\n};\n\n// Convert routes (array or map) into a uniform list of { path, Component, reducerKey }\nconst toList = (routes: RoutesInput): NormalizedRoute[] => {\n if (Array.isArray(routes)) {\n return routes.map(normalizeArrayEntry);\n }\n\n return Object.entries(routes).map(([path, value]) => normalizeMapEntry(path, value));\n};\n\n// Public: prepare routes by attaching a matcher to each entry.\nexport const prepare = (routes: RoutesInput = []): PreparedRoute[] => {\n const list = toList(routes);\n\n return list.map((r) => {\n // Catch-all\n if (r.path === \"*\" || r.path === \"/*\") {\n return { ...r, matcher: () => ({ params: {} }) };\n }\n\n const { regex, names } = compilePath(r.path);\n const matcher = (pathname: string): { params: Record<string, string> } | null => {\n const m = regex.exec(pathname);\n if (!m) return null;\n\n const params = m.groups\n ? Object.fromEntries(\n Object.entries(m.groups).map(([k, v]) => [k, decodeParam(v as string)]),\n )\n : names.reduce<Record<string, string>>((acc, name, i) => {\n acc[name] = decodeParam(m[i + 1]);\n return acc;\n }, {});\n\n return { params };\n };\n\n return { ...r, matcher };\n });\n};\n\n// Internal: find a match in a prepared list\nconst matchOne = (preparedRoutes: PreparedRoute[], pathname: string): RouteMatchResult => {\n for (const r of preparedRoutes) {\n if (typeof r.matcher !== \"function\") continue;\n const res = r.matcher(pathname);\n if (res) {\n return {\n Component: r.Component,\n params: res.params || {},\n reducerKey: r.reducerKey,\n };\n }\n }\n\n // Catch-all fallback if provided\n const star = preparedRoutes.find((r) => r.path === \"*\" || r.path === \"/*\");\n if (star) {\n return {\n Component: star.Component,\n params: {},\n reducerKey: star.reducerKey,\n };\n }\n\n // Generic 404 fallback\n return { Component: Generic404, params: {} };\n};\n\n// Public: match() accepts either raw routes or an already-prepared list\nexport const match = (\n routes: RoutesInput | PreparedRoute[],\n pathname: string,\n): RouteMatchResult => {\n const prepared = isPreparedRouteArray(routes) ? routes : prepare(routes);\n return matchOne(prepared, pathname);\n};\n\nexport default { prepare, match };\n","// Centralized scroll helpers with query-aware keys\nconst SCROLL_KEY = \"scroll\";\nconst MAX_SCROLL_ENTRIES = 100;\n\nexport interface ScrollPosition {\n x: number;\n y: number;\n}\n\nexport const getScrollPosition = (): ScrollPosition => ({\n y: window.pageYOffset || document.documentElement.scrollTop || 0,\n x: window.pageXOffset || document.documentElement.scrollLeft || 0,\n});\n\nconst currentKey = (): string => {\n const { pathname, search } = window.location;\n return `${pathname}${search || \"\"}`;\n};\n\nconst readStore = (): Record<string, ScrollPosition> => {\n if (typeof sessionStorage === \"undefined\") return {};\n try {\n const blob = sessionStorage.getItem(SCROLL_KEY);\n return blob ? (JSON.parse(blob) as Record<string, ScrollPosition>) : {};\n } catch {\n return {};\n }\n};\n\nconst writeStore = (obj: Record<string, ScrollPosition>): void => {\n if (typeof sessionStorage === \"undefined\") return;\n try {\n const keys = Object.keys(obj);\n if (keys.length > MAX_SCROLL_ENTRIES) {\n const excess = keys.length - MAX_SCROLL_ENTRIES;\n for (let i = 0; i < excess; i += 1) {\n delete obj[keys[i]];\n }\n }\n sessionStorage.setItem(SCROLL_KEY, JSON.stringify(obj));\n } catch {\n /* ignore quota/security errors */\n }\n};\n\nexport const setScrollToSessionStorage = (): void => {\n const store = readStore();\n const key = currentKey();\n if (store[key]) delete store[key];\n store[key] = getScrollPosition();\n writeStore(store);\n};\n\nexport const setScrollForKey = (key: string, pos?: ScrollPosition): void => {\n const store = readStore();\n if (store[key]) delete store[key];\n store[key] = pos || getScrollPosition();\n writeStore(store);\n};\n\nexport function getScrollFromSessionStorage(key: \"*\"): Record<string, ScrollPosition>;\nexport function getScrollFromSessionStorage(key: string): ScrollPosition | null;\nexport function getScrollFromSessionStorage(\n key?: string,\n): Record<string, ScrollPosition> | ScrollPosition | null {\n const store = readStore();\n if (key === \"*\" || key === undefined) return store;\n return store[key] || null;\n}\n","import type { BrowserHistory, Update } from \"history\";\nimport { getScrollFromSessionStorage, type ScrollPosition } from \"./scroll\";\n\nexport interface ProgressAPI {\n start(): void;\n done(): void;\n}\n\nexport interface HandleHistoryChangeOptions {\n history?: BrowserHistory | null;\n fetchImpl?: ((url: string, init?: RequestInit) => Promise<Response>) | null;\n setTitle?: (title: string) => void;\n progress?: ProgressAPI;\n}\n\nconst makeUuid = (): string => {\n // Browser / Workers\n if (typeof globalThis !== \"undefined\" && globalThis.crypto) {\n if (typeof globalThis.crypto.randomUUID === \"function\") {\n return globalThis.crypto.randomUUID();\n }\n // RFC4122 v4 via getRandomValues\n const buf = new Uint8Array(16);\n globalThis.crypto.getRandomValues(buf);\n buf[6] = (buf[6] & 0x0f) | 0x40;\n buf[8] = (buf[8] & 0x3f) | 0x80;\n const hex = [...buf].map((b) => b.toString(16).padStart(2, \"0\"));\n return `${hex.slice(0, 4).join(\"\")}-${hex.slice(4, 6).join(\"\")}-${hex\n .slice(6, 8)\n .join(\"\")}-${hex.slice(8, 10).join(\"\")}-${hex.slice(10).join(\"\")}`;\n }\n // Node\n try {\n const { randomUUID } = (globalThis as any).require(\"node:crypto\");\n if (typeof randomUUID === \"function\") return randomUUID();\n } catch {}\n // Last-resort (non-crypto)\n return \"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx\".replace(/[xy]/g, (c) => {\n const r = (Math.random() * 16) | 0;\n const v = c === \"x\" ? r : (r & 0x3) | 0x8;\n return v.toString(16);\n });\n};\n\nconst INSTALLED = Symbol.for(\"handleHistoryChange:installed\");\nlet _inFlight: AbortController | null = null;\nlet _latestRequestId: number = 0;\nlet _scrollRestoreTimeout: ReturnType<typeof setTimeout> | null = null;\n\nconst clearScrollRestoreTimeout = (): void => {\n if (_scrollRestoreTimeout) {\n clearTimeout(_scrollRestoreTimeout);\n _scrollRestoreTimeout = null;\n }\n};\n\nconst originOf = (): string => {\n try {\n if (\n typeof window !== \"undefined\" &&\n window.location &&\n window.location.origin\n ) {\n return window.location.origin;\n }\n } catch {}\n return \"http://localhost\";\n};\n\nconst buildUrl = (loc: { pathname?: string; search?: string }): string => {\n const url = new URL((loc.pathname || \"/\") + (loc.search || \"\"), originOf());\n url.searchParams.set(\"uuid\", makeUuid());\n return url.toString();\n};\n\nconst kindFrom = (status: number): \"ok\" | \"404\" | \"5xx\" => {\n if (status === 404) return \"404\";\n if (Math.floor(status / 100) === 5) return \"5xx\";\n return \"ok\";\n};\n\nexport default function handleHistoryChange(\n dispatch: (action: { type: string; data: Record<string, unknown> }) => void,\n {\n history,\n fetchImpl = (typeof fetch !== \"undefined\" && fetch) || null,\n setTitle = function (t: string): void {\n if (typeof document !== \"undefined\" && t) document.title = t;\n },\n progress = { start() {}, done() {} }, // optional in tests\n }: HandleHistoryChangeOptions = {},\n): () => void {\n if (!history || !fetchImpl) {\n return () => {};\n }\n\n if ((history as any)[INSTALLED]) {\n return () => {};\n }\n (history as any)[INSTALLED] = true;\n\n const unlisten = history.listen(function ({ location, action }: Update): void {\n // Abort prior request\n if (_inFlight && typeof _inFlight.abort === \"function\") {\n try {\n _inFlight.abort();\n } catch {}\n }\n clearScrollRestoreTimeout();\n const requestId = ++_latestRequestId;\n _inFlight =\n typeof AbortController !== \"undefined\" ? new AbortController() : null;\n\n if (progress && typeof progress.done === \"function\") progress.done();\n if (progress && typeof progress.start === \"function\") progress.start();\n\n const url = buildUrl(location);\n\n // Single promise chain so one microtask drain should be enough in tests\n Promise.resolve(\n fetchImpl(url, {\n method: \"GET\",\n headers: { Accept: \"application/json\" },\n signal: _inFlight ? _inFlight.signal : undefined,\n }),\n )\n .then(function (res): Promise<{ status: number; data: Record<string, unknown> }> {\n const jp = res && res.json ? res.json() : {};\n return Promise.resolve(jp)\n .then(function (data) {\n return { status: res ? res.status : 503, data: (data as Record<string, unknown>) || {} };\n })\n .catch(function () {\n return { status: res ? res.status : 503, data: {} };\n });\n })\n .catch(function () {\n return { status: 503, data: {} };\n })\n .then(function ({ status, data }): void {\n if (requestId !== _latestRequestId) return;\n if (progress && typeof progress.done === \"function\") progress.done();\n\n // Authorization redirect wins\n const authLoc = (data as { authorization?: { location?: string } }).authorization\n ?.location;\n let finalLoc = authLoc || location.pathname || \"/\";\n\n // Map 404/5xx if no explicit auth redirect\n if (!authLoc) {\n const k = kindFrom(status);\n if (k === \"404\") finalLoc = \"/404\";\n else if (k === \"5xx\") finalLoc = \"/500\";\n }\n\n dispatch({\n type: \"CHANGE_PAGE\",\n data: Object.assign({}, data, { location: finalLoc }),\n });\n\n // Title from top-level data.title\n const title = (data as { title?: string }).title;\n if (title) {\n // eslint-disable-next-line no-console\n setTitle(title);\n }\n\n // Scroll behavior: top on PUSH; restore for POP/REPLACE\n if (typeof window !== \"undefined\" && window.scrollTo) {\n if (action === \"PUSH\") {\n window.scrollTo(0, 0);\n } else {\n const key = (location.pathname || \"/\") + (location.search || \"\");\n const prev: ScrollPosition | null = getScrollFromSessionStorage(key);\n if (prev) {\n _scrollRestoreTimeout = setTimeout(function () {\n window.scrollTo(prev.x || 0, prev.y || 0);\n _scrollRestoreTimeout = null;\n }, 250);\n }\n }\n }\n });\n });\n\n return (): void => {\n if (typeof unlisten === \"function\") unlisten();\n clearScrollRestoreTimeout();\n if (_inFlight && typeof _inFlight.abort === \"function\") {\n try {\n _inFlight.abort();\n } catch {}\n }\n _inFlight = null;\n (history as any)[INSTALLED] = false;\n };\n}\n\n// Test helpers (reset does nothing now because the guard is per-history instance)\nexport const __test__: {\n reset: () => void;\n state: () => { inFlight: boolean };\n} = {\n reset: function (): void {\n _latestRequestId = 0;\n _inFlight = null;\n clearScrollRestoreTimeout();\n },\n state: function (): { inFlight: boolean } {\n return {\n inFlight: !!_inFlight,\n };\n },\n};\n"],"mappings":"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","names":["appHistory"]}