@tanstack/history 1.153.2 → 1.154.14
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/cjs/index.cjs +8 -11
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/cjs/index.d.cts +3 -27
- package/dist/esm/index.d.ts +3 -27
- package/dist/esm/index.js +8 -11
- package/dist/esm/index.js.map +1 -1
- package/package.json +1 -1
- package/src/index.ts +12 -47
package/dist/cjs/index.cjs
CHANGED
|
@@ -6,11 +6,9 @@ const beforeUnloadEvent = "beforeunload";
|
|
|
6
6
|
function createHistory(opts) {
|
|
7
7
|
let location = opts.getLocation();
|
|
8
8
|
const subscribers = /* @__PURE__ */ new Set();
|
|
9
|
-
const notify = (action
|
|
9
|
+
const notify = (action) => {
|
|
10
10
|
location = opts.getLocation();
|
|
11
|
-
subscribers.forEach(
|
|
12
|
-
(subscriber) => subscriber({ location, action, navigateOpts })
|
|
13
|
-
);
|
|
11
|
+
subscribers.forEach((subscriber) => subscriber({ location, action }));
|
|
14
12
|
};
|
|
15
13
|
const handleIndexChange = (action) => {
|
|
16
14
|
if (opts.notifyOnIndexChange ?? true) notify(action);
|
|
@@ -24,7 +22,7 @@ function createHistory(opts) {
|
|
|
24
22
|
const ignoreBlocker = navigateOpts?.ignoreBlocker ?? false;
|
|
25
23
|
if (ignoreBlocker) {
|
|
26
24
|
task();
|
|
27
|
-
return
|
|
25
|
+
return;
|
|
28
26
|
}
|
|
29
27
|
const blockers = opts.getBlockers?.() ?? [];
|
|
30
28
|
const isPushOrReplace = actionInfo.type === "PUSH" || actionInfo.type === "REPLACE";
|
|
@@ -38,12 +36,11 @@ function createHistory(opts) {
|
|
|
38
36
|
});
|
|
39
37
|
if (isBlocked) {
|
|
40
38
|
opts.onBlocked?.();
|
|
41
|
-
return
|
|
39
|
+
return;
|
|
42
40
|
}
|
|
43
41
|
}
|
|
44
42
|
}
|
|
45
43
|
task();
|
|
46
|
-
return { type: "SUCCESS" };
|
|
47
44
|
};
|
|
48
45
|
return {
|
|
49
46
|
get location() {
|
|
@@ -62,10 +59,10 @@ function createHistory(opts) {
|
|
|
62
59
|
push: (path, state, navigateOpts) => {
|
|
63
60
|
const currentIndex = location.state[stateIndexKey];
|
|
64
61
|
state = assignKeyAndIndex(currentIndex + 1, state);
|
|
65
|
-
|
|
62
|
+
tryNavigation({
|
|
66
63
|
task: () => {
|
|
67
64
|
opts.pushState(path, state);
|
|
68
|
-
notify({ type: "PUSH" }
|
|
65
|
+
notify({ type: "PUSH" });
|
|
69
66
|
},
|
|
70
67
|
navigateOpts,
|
|
71
68
|
type: "PUSH",
|
|
@@ -76,10 +73,10 @@ function createHistory(opts) {
|
|
|
76
73
|
replace: (path, state, navigateOpts) => {
|
|
77
74
|
const currentIndex = location.state[stateIndexKey];
|
|
78
75
|
state = assignKeyAndIndex(currentIndex, state);
|
|
79
|
-
|
|
76
|
+
tryNavigation({
|
|
80
77
|
task: () => {
|
|
81
78
|
opts.replaceState(path, state);
|
|
82
|
-
notify({ type: "REPLACE" }
|
|
79
|
+
notify({ type: "REPLACE" });
|
|
83
80
|
},
|
|
84
81
|
navigateOpts,
|
|
85
82
|
type: "REPLACE",
|
package/dist/cjs/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","sources":["../../src/index.ts"],"sourcesContent":["// While the public API was clearly inspired by the \"history\" npm package,\n// This implementation attempts to be more lightweight by\n// making assumptions about the way TanStack Router works\n\nexport interface NavigateOptions {\n ignoreBlocker?: boolean\n /** When true, Transitioner should skip calling load() - commitLocation handles it */\n skipTransitionerLoad?: boolean\n}\n\n/** Result of a navigation attempt (push/replace) */\nexport type NavigationResult = { type: 'SUCCESS' } | { type: 'BLOCKED' }\n\ntype SubscriberHistoryAction =\n | {\n type: Exclude<HistoryAction, 'GO'>\n }\n | {\n type: 'GO'\n index: number\n }\n\nexport type SubscriberArgs = {\n location: HistoryLocation\n action: SubscriberHistoryAction\n navigateOpts?: NavigateOptions\n}\n\nexport interface RouterHistory {\n location: HistoryLocation\n length: number\n subscribers: Set<(opts: SubscriberArgs) => void>\n subscribe: (cb: (opts: SubscriberArgs) => void) => () => void\n push: (\n path: string,\n state?: any,\n navigateOpts?: NavigateOptions,\n ) => Promise<NavigationResult>\n replace: (\n path: string,\n state?: any,\n navigateOpts?: NavigateOptions,\n ) => Promise<NavigationResult>\n go: (index: number, navigateOpts?: NavigateOptions) => void\n back: (navigateOpts?: NavigateOptions) => void\n forward: (navigateOpts?: NavigateOptions) => void\n canGoBack: () => boolean\n createHref: (href: string) => string\n block: (blocker: NavigationBlocker) => () => void\n flush: () => void\n destroy: () => void\n notify: (action: SubscriberHistoryAction) => void\n _ignoreSubscribers?: boolean\n}\n\nexport interface HistoryLocation extends ParsedPath {\n state: ParsedHistoryState\n}\n\nexport interface ParsedPath {\n href: string\n pathname: string\n search: string\n hash: string\n}\n\nexport interface HistoryState {}\n\nexport type ParsedHistoryState = HistoryState & {\n key?: string // TODO: Remove in v2 - use __TSR_key instead\n __TSR_key?: string\n __TSR_index: number\n /** Whether to reset scroll position on this navigation (default: true) */\n __TSR_resetScroll?: boolean\n /** Session id for cached TSR internals */\n __TSR_sessionId?: string\n /** Match snapshot for fast-path on back/forward navigation */\n __TSR_matches?: {\n routeIds: Array<string>\n params: Record<string, string>\n globalNotFoundRouteId?: string\n searchStr?: string\n validatedSearches?: Array<{\n search: Record<string, unknown>\n strictSearch: Record<string, unknown>\n }>\n }\n}\n\ntype ShouldAllowNavigation = any\n\nexport type HistoryAction = 'PUSH' | 'REPLACE' | 'FORWARD' | 'BACK' | 'GO'\n\nexport type BlockerFnArgs = {\n currentLocation: HistoryLocation\n nextLocation: HistoryLocation\n action: HistoryAction\n}\n\nexport type BlockerFn = (\n args: BlockerFnArgs,\n) => Promise<ShouldAllowNavigation> | ShouldAllowNavigation\n\nexport type NavigationBlocker = {\n blockerFn: BlockerFn\n enableBeforeUnload?: (() => boolean) | boolean\n}\n\ntype TryNavigateArgs = {\n task: () => void\n type: 'PUSH' | 'REPLACE' | 'BACK' | 'FORWARD' | 'GO'\n navigateOpts?: NavigateOptions\n} & (\n | {\n type: 'PUSH' | 'REPLACE'\n path: string\n state: any\n }\n | {\n type: 'BACK' | 'FORWARD' | 'GO'\n }\n)\n\nconst stateIndexKey = '__TSR_index'\nconst popStateEvent = 'popstate'\nconst beforeUnloadEvent = 'beforeunload'\n\nexport function createHistory(opts: {\n getLocation: () => HistoryLocation\n getLength: () => number\n pushState: (path: string, state: any) => void\n replaceState: (path: string, state: any) => void\n go: (n: number) => void\n back: (ignoreBlocker: boolean) => void\n forward: (ignoreBlocker: boolean) => void\n createHref: (path: string) => string\n flush?: () => void\n destroy?: () => void\n onBlocked?: () => void\n getBlockers?: () => Array<NavigationBlocker>\n setBlockers?: (blockers: Array<NavigationBlocker>) => void\n // Avoid notifying on forward/back/go, used for browser history as we already get notified by the popstate event\n notifyOnIndexChange?: boolean\n}): RouterHistory {\n let location = opts.getLocation()\n const subscribers = new Set<(opts: SubscriberArgs) => void>()\n\n const notify = (\n action: SubscriberHistoryAction,\n navigateOpts?: NavigateOptions,\n ) => {\n location = opts.getLocation()\n subscribers.forEach((subscriber) =>\n subscriber({ location, action, navigateOpts }),\n )\n }\n\n const handleIndexChange = (action: SubscriberHistoryAction) => {\n if (opts.notifyOnIndexChange ?? true) notify(action)\n else location = opts.getLocation()\n }\n\n const tryNavigation = async ({\n task,\n navigateOpts,\n ...actionInfo\n }: TryNavigateArgs): Promise<NavigationResult> => {\n const ignoreBlocker = navigateOpts?.ignoreBlocker ?? false\n if (ignoreBlocker) {\n task()\n return { type: 'SUCCESS' }\n }\n\n const blockers = opts.getBlockers?.() ?? []\n const isPushOrReplace =\n actionInfo.type === 'PUSH' || actionInfo.type === 'REPLACE'\n if (typeof document !== 'undefined' && blockers.length && isPushOrReplace) {\n for (const blocker of blockers) {\n const nextLocation = parseHref(actionInfo.path, actionInfo.state)\n const isBlocked = await blocker.blockerFn({\n currentLocation: location,\n nextLocation,\n action: actionInfo.type,\n })\n if (isBlocked) {\n opts.onBlocked?.()\n return { type: 'BLOCKED' }\n }\n }\n }\n\n task()\n return { type: 'SUCCESS' }\n }\n\n return {\n get location() {\n return location\n },\n get length() {\n return opts.getLength()\n },\n subscribers,\n subscribe: (cb: (opts: SubscriberArgs) => void) => {\n subscribers.add(cb)\n\n return () => {\n subscribers.delete(cb)\n }\n },\n push: (path, state, navigateOpts) => {\n const currentIndex = location.state[stateIndexKey]\n state = assignKeyAndIndex(currentIndex + 1, state)\n return tryNavigation({\n task: () => {\n opts.pushState(path, state)\n notify({ type: 'PUSH' }, navigateOpts)\n },\n navigateOpts,\n type: 'PUSH',\n path,\n state,\n })\n },\n replace: (path, state, navigateOpts) => {\n const currentIndex = location.state[stateIndexKey]\n state = assignKeyAndIndex(currentIndex, state)\n return tryNavigation({\n task: () => {\n opts.replaceState(path, state)\n notify({ type: 'REPLACE' }, navigateOpts)\n },\n navigateOpts,\n type: 'REPLACE',\n path,\n state,\n })\n },\n go: (index, navigateOpts) => {\n tryNavigation({\n task: () => {\n opts.go(index)\n handleIndexChange({ type: 'GO', index })\n },\n navigateOpts,\n type: 'GO',\n })\n },\n back: (navigateOpts) => {\n tryNavigation({\n task: () => {\n opts.back(navigateOpts?.ignoreBlocker ?? false)\n handleIndexChange({ type: 'BACK' })\n },\n navigateOpts,\n type: 'BACK',\n })\n },\n forward: (navigateOpts) => {\n tryNavigation({\n task: () => {\n opts.forward(navigateOpts?.ignoreBlocker ?? false)\n handleIndexChange({ type: 'FORWARD' })\n },\n navigateOpts,\n type: 'FORWARD',\n })\n },\n canGoBack: () => location.state[stateIndexKey] !== 0,\n createHref: (str) => opts.createHref(str),\n block: (blocker) => {\n if (!opts.setBlockers) return () => {}\n const blockers = opts.getBlockers?.() ?? []\n opts.setBlockers([...blockers, blocker])\n\n return () => {\n const blockers = opts.getBlockers?.() ?? []\n opts.setBlockers?.(blockers.filter((b) => b !== blocker))\n }\n },\n flush: () => opts.flush?.(),\n destroy: () => opts.destroy?.(),\n notify,\n }\n}\n\nfunction assignKeyAndIndex(index: number, state: HistoryState | undefined) {\n if (!state) {\n state = {} as HistoryState\n }\n const key = createRandomKey()\n return {\n ...state,\n key, // TODO: Remove in v2 - use __TSR_key instead\n __TSR_key: key,\n [stateIndexKey]: index,\n } as ParsedHistoryState\n}\n\n/**\n * Creates a history object that can be used to interact with the browser's\n * navigation. This is a lightweight API wrapping the browser's native methods.\n * It is designed to work with TanStack Router, but could be used as a standalone API as well.\n * IMPORTANT: This API implements history throttling via a microtask to prevent\n * excessive calls to the history API. In some browsers, calling history.pushState or\n * history.replaceState in quick succession can cause the browser to ignore subsequent\n * calls. This API smooths out those differences and ensures that your application\n * state will *eventually* match the browser state. In most cases, this is not a problem,\n * but if you need to ensure that the browser state is up to date, you can use the\n * `history.flush` method to immediately flush all pending state changes to the browser URL.\n * @param opts\n * @param opts.getHref A function that returns the current href (path + search + hash)\n * @param opts.createHref A function that takes a path and returns a href (path + search + hash)\n * @returns A history instance\n */\nexport function createBrowserHistory(opts?: {\n parseLocation?: () => HistoryLocation\n createHref?: (path: string) => string\n window?: any\n}): RouterHistory {\n const win =\n opts?.window ??\n (typeof document !== 'undefined' ? window : (undefined as any))\n\n const originalPushState = win.history.pushState\n const originalReplaceState = win.history.replaceState\n\n let blockers: Array<NavigationBlocker> = []\n const _getBlockers = () => blockers\n const _setBlockers = (newBlockers: Array<NavigationBlocker>) =>\n (blockers = newBlockers)\n\n const createHref = opts?.createHref ?? ((path) => path)\n const parseLocation =\n opts?.parseLocation ??\n (() =>\n parseHref(\n `${win.location.pathname}${win.location.search}${win.location.hash}`,\n win.history.state,\n ))\n\n // Ensure there is always a key to start\n if (!win.history.state?.__TSR_key && !win.history.state?.key) {\n const addedKey = createRandomKey()\n win.history.replaceState(\n {\n [stateIndexKey]: 0,\n key: addedKey, // TODO: Remove in v2 - use __TSR_key instead\n __TSR_key: addedKey,\n },\n '',\n )\n }\n\n let currentLocation = parseLocation()\n let rollbackLocation: HistoryLocation | undefined\n\n let nextPopIsGo = false\n let ignoreNextPop = false\n let skipBlockerNextPop = false\n let ignoreNextBeforeUnload = false\n\n const getLocation = () => currentLocation\n\n let next:\n | undefined\n | {\n // This is the latest location that we were attempting to push/replace\n href: string\n // This is the latest state that we were attempting to push/replace\n state: any\n // This is the latest type that we were attempting to push/replace\n isPush: boolean\n }\n\n // We need to track the current scheduled update to prevent\n // multiple updates from being scheduled at the same time.\n let scheduled: Promise<void> | undefined\n\n // This function flushes the next update to the browser history\n const flush = () => {\n if (!next) {\n return\n }\n\n // We need to ignore any updates to the subscribers while we update the browser history\n history._ignoreSubscribers = true\n\n // Update the browser history\n ;(next.isPush ? win.history.pushState : win.history.replaceState)(\n next.state,\n '',\n next.href,\n )\n\n // Stop ignoring subscriber updates\n history._ignoreSubscribers = false\n\n // Reset the nextIsPush flag and clear the scheduled update\n next = undefined\n scheduled = undefined\n rollbackLocation = undefined\n }\n\n // This function queues up a call to update the browser history\n const queueHistoryAction = (\n type: 'push' | 'replace',\n destHref: string,\n state: any,\n ) => {\n const href = createHref(destHref)\n\n if (!scheduled) {\n rollbackLocation = currentLocation\n }\n\n // Update the location in memory\n currentLocation = parseHref(destHref, state)\n\n // Keep track of the next location we need to flush to the URL\n next = {\n href,\n state,\n isPush: next?.isPush || type === 'push',\n }\n\n if (!scheduled) {\n // Schedule an update to the browser history\n scheduled = Promise.resolve().then(() => flush())\n }\n }\n\n // NOTE: this function can probably be removed\n const onPushPop = (type: 'PUSH' | 'REPLACE') => {\n currentLocation = parseLocation()\n history.notify({ type })\n }\n\n const onPushPopEvent = async () => {\n if (ignoreNextPop) {\n ignoreNextPop = false\n return\n }\n\n const nextLocation = parseLocation()\n const delta =\n nextLocation.state[stateIndexKey] - currentLocation.state[stateIndexKey]\n const isForward = delta === 1\n const isBack = delta === -1\n const isGo = (!isForward && !isBack) || nextPopIsGo\n nextPopIsGo = false\n\n const action = isGo ? 'GO' : isBack ? 'BACK' : 'FORWARD'\n const notify: SubscriberHistoryAction = isGo\n ? {\n type: 'GO',\n index: delta,\n }\n : {\n type: isBack ? 'BACK' : 'FORWARD',\n }\n\n if (skipBlockerNextPop) {\n skipBlockerNextPop = false\n } else {\n const blockers = _getBlockers()\n if (typeof document !== 'undefined' && blockers.length) {\n for (const blocker of blockers) {\n const isBlocked = await blocker.blockerFn({\n currentLocation,\n nextLocation,\n action,\n })\n if (isBlocked) {\n ignoreNextPop = true\n win.history.go(1)\n history.notify(notify)\n return\n }\n }\n }\n }\n\n currentLocation = parseLocation()\n history.notify(notify)\n }\n\n const onBeforeUnload = (e: BeforeUnloadEvent) => {\n if (ignoreNextBeforeUnload) {\n ignoreNextBeforeUnload = false\n return\n }\n\n let shouldBlock = false\n\n // If one blocker has a non-disabled beforeUnload, we should block\n const blockers = _getBlockers()\n if (typeof document !== 'undefined' && blockers.length) {\n for (const blocker of blockers) {\n const shouldHaveBeforeUnload = blocker.enableBeforeUnload ?? true\n if (shouldHaveBeforeUnload === true) {\n shouldBlock = true\n break\n }\n\n if (\n typeof shouldHaveBeforeUnload === 'function' &&\n shouldHaveBeforeUnload() === true\n ) {\n shouldBlock = true\n break\n }\n }\n }\n\n if (shouldBlock) {\n e.preventDefault()\n return (e.returnValue = '')\n }\n return\n }\n\n const history = createHistory({\n getLocation,\n getLength: () => win.history.length,\n pushState: (href, state) => queueHistoryAction('push', href, state),\n replaceState: (href, state) => queueHistoryAction('replace', href, state),\n back: (ignoreBlocker) => {\n if (ignoreBlocker) skipBlockerNextPop = true\n ignoreNextBeforeUnload = true\n return win.history.back()\n },\n forward: (ignoreBlocker) => {\n if (ignoreBlocker) skipBlockerNextPop = true\n ignoreNextBeforeUnload = true\n win.history.forward()\n },\n go: (n) => {\n nextPopIsGo = true\n win.history.go(n)\n },\n createHref: (href) => createHref(href),\n flush,\n destroy: () => {\n win.history.pushState = originalPushState\n win.history.replaceState = originalReplaceState\n win.removeEventListener(beforeUnloadEvent, onBeforeUnload, {\n capture: true,\n })\n win.removeEventListener(popStateEvent, onPushPopEvent)\n },\n onBlocked: () => {\n // If a navigation is blocked, we need to rollback the location\n // that we optimistically updated in memory.\n if (rollbackLocation && currentLocation !== rollbackLocation) {\n currentLocation = rollbackLocation\n }\n },\n getBlockers: _getBlockers,\n setBlockers: _setBlockers,\n notifyOnIndexChange: false,\n })\n\n win.addEventListener(beforeUnloadEvent, onBeforeUnload, { capture: true })\n win.addEventListener(popStateEvent, onPushPopEvent)\n\n win.history.pushState = function (...args: Array<any>) {\n const res = originalPushState.apply(win.history, args as any)\n if (!history._ignoreSubscribers) onPushPop('PUSH')\n return res\n }\n\n win.history.replaceState = function (...args: Array<any>) {\n const res = originalReplaceState.apply(win.history, args as any)\n if (!history._ignoreSubscribers) onPushPop('REPLACE')\n return res\n }\n\n return history\n}\n\n/**\n * Create a hash-based history implementation.\n * Useful for static hosts or environments without server URL rewriting.\n * @link https://tanstack.com/router/latest/docs/framework/react/guide/history-types\n */\nexport function createHashHistory(opts?: { window?: any }): RouterHistory {\n const win =\n opts?.window ??\n (typeof document !== 'undefined' ? window : (undefined as any))\n return createBrowserHistory({\n window: win,\n parseLocation: () => {\n const hashSplit = win.location.hash.split('#').slice(1)\n const pathPart = hashSplit[0] ?? '/'\n const searchPart = win.location.search\n const hashEntries = hashSplit.slice(1)\n const hashPart =\n hashEntries.length === 0 ? '' : `#${hashEntries.join('#')}`\n const hashHref = `${pathPart}${searchPart}${hashPart}`\n return parseHref(hashHref, win.history.state)\n },\n createHref: (href) =>\n `${win.location.pathname}${win.location.search}#${href}`,\n })\n}\n\n/**\n * Create an in-memory history implementation.\n * Ideal for server rendering, tests, and non-DOM environments.\n * @link https://tanstack.com/router/latest/docs/framework/react/guide/history-types\n */\nexport function createMemoryHistory(\n opts: {\n initialEntries: Array<string>\n initialIndex?: number\n } = {\n initialEntries: ['/'],\n },\n): RouterHistory {\n const entries = opts.initialEntries\n let index = opts.initialIndex\n ? Math.min(Math.max(opts.initialIndex, 0), entries.length - 1)\n : entries.length - 1\n const states = entries.map((_entry, index) =>\n assignKeyAndIndex(index, undefined),\n )\n\n const getLocation = () => parseHref(entries[index]!, states[index])\n\n let blockers: Array<NavigationBlocker> = []\n const _getBlockers = () => blockers\n const _setBlockers = (newBlockers: Array<NavigationBlocker>) =>\n (blockers = newBlockers)\n\n return createHistory({\n getLocation,\n getLength: () => entries.length,\n pushState: (path, state) => {\n // Removes all subsequent entries after the current index to start a new branch\n if (index < entries.length - 1) {\n entries.splice(index + 1)\n states.splice(index + 1)\n }\n states.push(state)\n entries.push(path)\n index = Math.max(entries.length - 1, 0)\n },\n replaceState: (path, state) => {\n states[index] = state\n entries[index] = path\n },\n back: () => {\n index = Math.max(index - 1, 0)\n },\n forward: () => {\n index = Math.min(index + 1, entries.length - 1)\n },\n go: (n) => {\n index = Math.min(Math.max(index + n, 0), entries.length - 1)\n },\n createHref: (path) => path,\n getBlockers: _getBlockers,\n setBlockers: _setBlockers,\n })\n}\n\n/**\n * Sanitize a path to prevent open redirect vulnerabilities.\n * Removes control characters and collapses leading double slashes.\n */\nfunction sanitizePath(path: string): string {\n // Remove ASCII control characters (0x00-0x1F) and DEL (0x7F)\n // These include CR (\\r = 0x0D), LF (\\n = 0x0A), and other potentially dangerous characters\n // eslint-disable-next-line no-control-regex\n let sanitized = path.replace(/[\\x00-\\x1f\\x7f]/g, '')\n\n // Prevent open redirect via protocol-relative URLs (e.g. \"//evil.com\")\n // Collapse leading double slashes to a single slash\n if (sanitized.startsWith('//')) {\n sanitized = '/' + sanitized.replace(/^\\/+/, '')\n }\n\n return sanitized\n}\n\nexport function parseHref(\n href: string,\n state: ParsedHistoryState | undefined,\n): HistoryLocation {\n const sanitizedHref = sanitizePath(href)\n const hashIndex = sanitizedHref.indexOf('#')\n const searchIndex = sanitizedHref.indexOf('?')\n\n const addedKey = createRandomKey()\n\n return {\n href: sanitizedHref,\n pathname: sanitizedHref.substring(\n 0,\n hashIndex > 0\n ? searchIndex > 0\n ? Math.min(hashIndex, searchIndex)\n : hashIndex\n : searchIndex > 0\n ? searchIndex\n : sanitizedHref.length,\n ),\n hash: hashIndex > -1 ? sanitizedHref.substring(hashIndex) : '',\n search:\n searchIndex > -1\n ? sanitizedHref.slice(\n searchIndex,\n hashIndex === -1 ? undefined : hashIndex,\n )\n : '',\n state: state || { [stateIndexKey]: 0, key: addedKey, __TSR_key: addedKey },\n }\n}\n\n// Thanks co-pilot!\nfunction createRandomKey() {\n return (Math.random() + 1).toString(36).substring(7)\n}\n"],"names":["blockers","index"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.cjs","sources":["../../src/index.ts"],"sourcesContent":["// While the public API was clearly inspired by the \"history\" npm package,\n// This implementation attempts to be more lightweight by\n// making assumptions about the way TanStack Router works\n\nexport interface NavigateOptions {\n ignoreBlocker?: boolean\n}\n\ntype SubscriberHistoryAction =\n | {\n type: Exclude<HistoryAction, 'GO'>\n }\n | {\n type: 'GO'\n index: number\n }\n\ntype SubscriberArgs = {\n location: HistoryLocation\n action: SubscriberHistoryAction\n}\n\nexport interface RouterHistory {\n location: HistoryLocation\n length: number\n subscribers: Set<(opts: SubscriberArgs) => void>\n subscribe: (cb: (opts: SubscriberArgs) => void) => () => void\n push: (path: string, state?: any, navigateOpts?: NavigateOptions) => void\n replace: (path: string, state?: any, navigateOpts?: NavigateOptions) => void\n go: (index: number, navigateOpts?: NavigateOptions) => void\n back: (navigateOpts?: NavigateOptions) => void\n forward: (navigateOpts?: NavigateOptions) => void\n canGoBack: () => boolean\n createHref: (href: string) => string\n block: (blocker: NavigationBlocker) => () => void\n flush: () => void\n destroy: () => void\n notify: (action: SubscriberHistoryAction) => void\n _ignoreSubscribers?: boolean\n}\n\nexport interface HistoryLocation extends ParsedPath {\n state: ParsedHistoryState\n}\n\nexport interface ParsedPath {\n href: string\n pathname: string\n search: string\n hash: string\n}\n\nexport interface HistoryState {}\n\nexport type ParsedHistoryState = HistoryState & {\n key?: string // TODO: Remove in v2 - use __TSR_key instead\n __TSR_key?: string\n __TSR_index: number\n}\n\ntype ShouldAllowNavigation = any\n\nexport type HistoryAction = 'PUSH' | 'REPLACE' | 'FORWARD' | 'BACK' | 'GO'\n\nexport type BlockerFnArgs = {\n currentLocation: HistoryLocation\n nextLocation: HistoryLocation\n action: HistoryAction\n}\n\nexport type BlockerFn = (\n args: BlockerFnArgs,\n) => Promise<ShouldAllowNavigation> | ShouldAllowNavigation\n\nexport type NavigationBlocker = {\n blockerFn: BlockerFn\n enableBeforeUnload?: (() => boolean) | boolean\n}\n\ntype TryNavigateArgs = {\n task: () => void\n type: 'PUSH' | 'REPLACE' | 'BACK' | 'FORWARD' | 'GO'\n navigateOpts?: NavigateOptions\n} & (\n | {\n type: 'PUSH' | 'REPLACE'\n path: string\n state: any\n }\n | {\n type: 'BACK' | 'FORWARD' | 'GO'\n }\n)\n\nconst stateIndexKey = '__TSR_index'\nconst popStateEvent = 'popstate'\nconst beforeUnloadEvent = 'beforeunload'\n\nexport function createHistory(opts: {\n getLocation: () => HistoryLocation\n getLength: () => number\n pushState: (path: string, state: any) => void\n replaceState: (path: string, state: any) => void\n go: (n: number) => void\n back: (ignoreBlocker: boolean) => void\n forward: (ignoreBlocker: boolean) => void\n createHref: (path: string) => string\n flush?: () => void\n destroy?: () => void\n onBlocked?: () => void\n getBlockers?: () => Array<NavigationBlocker>\n setBlockers?: (blockers: Array<NavigationBlocker>) => void\n // Avoid notifying on forward/back/go, used for browser history as we already get notified by the popstate event\n notifyOnIndexChange?: boolean\n}): RouterHistory {\n let location = opts.getLocation()\n const subscribers = new Set<(opts: SubscriberArgs) => void>()\n\n const notify = (action: SubscriberHistoryAction) => {\n location = opts.getLocation()\n subscribers.forEach((subscriber) => subscriber({ location, action }))\n }\n\n const handleIndexChange = (action: SubscriberHistoryAction) => {\n if (opts.notifyOnIndexChange ?? true) notify(action)\n else location = opts.getLocation()\n }\n\n const tryNavigation = async ({\n task,\n navigateOpts,\n ...actionInfo\n }: TryNavigateArgs) => {\n const ignoreBlocker = navigateOpts?.ignoreBlocker ?? false\n if (ignoreBlocker) {\n task()\n return\n }\n\n const blockers = opts.getBlockers?.() ?? []\n const isPushOrReplace =\n actionInfo.type === 'PUSH' || actionInfo.type === 'REPLACE'\n if (typeof document !== 'undefined' && blockers.length && isPushOrReplace) {\n for (const blocker of blockers) {\n const nextLocation = parseHref(actionInfo.path, actionInfo.state)\n const isBlocked = await blocker.blockerFn({\n currentLocation: location,\n nextLocation,\n action: actionInfo.type,\n })\n if (isBlocked) {\n opts.onBlocked?.()\n return\n }\n }\n }\n\n task()\n }\n\n return {\n get location() {\n return location\n },\n get length() {\n return opts.getLength()\n },\n subscribers,\n subscribe: (cb: (opts: SubscriberArgs) => void) => {\n subscribers.add(cb)\n\n return () => {\n subscribers.delete(cb)\n }\n },\n push: (path, state, navigateOpts) => {\n const currentIndex = location.state[stateIndexKey]\n state = assignKeyAndIndex(currentIndex + 1, state)\n tryNavigation({\n task: () => {\n opts.pushState(path, state)\n notify({ type: 'PUSH' })\n },\n navigateOpts,\n type: 'PUSH',\n path,\n state,\n })\n },\n replace: (path, state, navigateOpts) => {\n const currentIndex = location.state[stateIndexKey]\n state = assignKeyAndIndex(currentIndex, state)\n tryNavigation({\n task: () => {\n opts.replaceState(path, state)\n notify({ type: 'REPLACE' })\n },\n navigateOpts,\n type: 'REPLACE',\n path,\n state,\n })\n },\n go: (index, navigateOpts) => {\n tryNavigation({\n task: () => {\n opts.go(index)\n handleIndexChange({ type: 'GO', index })\n },\n navigateOpts,\n type: 'GO',\n })\n },\n back: (navigateOpts) => {\n tryNavigation({\n task: () => {\n opts.back(navigateOpts?.ignoreBlocker ?? false)\n handleIndexChange({ type: 'BACK' })\n },\n navigateOpts,\n type: 'BACK',\n })\n },\n forward: (navigateOpts) => {\n tryNavigation({\n task: () => {\n opts.forward(navigateOpts?.ignoreBlocker ?? false)\n handleIndexChange({ type: 'FORWARD' })\n },\n navigateOpts,\n type: 'FORWARD',\n })\n },\n canGoBack: () => location.state[stateIndexKey] !== 0,\n createHref: (str) => opts.createHref(str),\n block: (blocker) => {\n if (!opts.setBlockers) return () => {}\n const blockers = opts.getBlockers?.() ?? []\n opts.setBlockers([...blockers, blocker])\n\n return () => {\n const blockers = opts.getBlockers?.() ?? []\n opts.setBlockers?.(blockers.filter((b) => b !== blocker))\n }\n },\n flush: () => opts.flush?.(),\n destroy: () => opts.destroy?.(),\n notify,\n }\n}\n\nfunction assignKeyAndIndex(index: number, state: HistoryState | undefined) {\n if (!state) {\n state = {} as HistoryState\n }\n const key = createRandomKey()\n return {\n ...state,\n key, // TODO: Remove in v2 - use __TSR_key instead\n __TSR_key: key,\n [stateIndexKey]: index,\n } as ParsedHistoryState\n}\n\n/**\n * Creates a history object that can be used to interact with the browser's\n * navigation. This is a lightweight API wrapping the browser's native methods.\n * It is designed to work with TanStack Router, but could be used as a standalone API as well.\n * IMPORTANT: This API implements history throttling via a microtask to prevent\n * excessive calls to the history API. In some browsers, calling history.pushState or\n * history.replaceState in quick succession can cause the browser to ignore subsequent\n * calls. This API smooths out those differences and ensures that your application\n * state will *eventually* match the browser state. In most cases, this is not a problem,\n * but if you need to ensure that the browser state is up to date, you can use the\n * `history.flush` method to immediately flush all pending state changes to the browser URL.\n * @param opts\n * @param opts.getHref A function that returns the current href (path + search + hash)\n * @param opts.createHref A function that takes a path and returns a href (path + search + hash)\n * @returns A history instance\n */\nexport function createBrowserHistory(opts?: {\n parseLocation?: () => HistoryLocation\n createHref?: (path: string) => string\n window?: any\n}): RouterHistory {\n const win =\n opts?.window ??\n (typeof document !== 'undefined' ? window : (undefined as any))\n\n const originalPushState = win.history.pushState\n const originalReplaceState = win.history.replaceState\n\n let blockers: Array<NavigationBlocker> = []\n const _getBlockers = () => blockers\n const _setBlockers = (newBlockers: Array<NavigationBlocker>) =>\n (blockers = newBlockers)\n\n const createHref = opts?.createHref ?? ((path) => path)\n const parseLocation =\n opts?.parseLocation ??\n (() =>\n parseHref(\n `${win.location.pathname}${win.location.search}${win.location.hash}`,\n win.history.state,\n ))\n\n // Ensure there is always a key to start\n if (!win.history.state?.__TSR_key && !win.history.state?.key) {\n const addedKey = createRandomKey()\n win.history.replaceState(\n {\n [stateIndexKey]: 0,\n key: addedKey, // TODO: Remove in v2 - use __TSR_key instead\n __TSR_key: addedKey,\n },\n '',\n )\n }\n\n let currentLocation = parseLocation()\n let rollbackLocation: HistoryLocation | undefined\n\n let nextPopIsGo = false\n let ignoreNextPop = false\n let skipBlockerNextPop = false\n let ignoreNextBeforeUnload = false\n\n const getLocation = () => currentLocation\n\n let next:\n | undefined\n | {\n // This is the latest location that we were attempting to push/replace\n href: string\n // This is the latest state that we were attempting to push/replace\n state: any\n // This is the latest type that we were attempting to push/replace\n isPush: boolean\n }\n\n // We need to track the current scheduled update to prevent\n // multiple updates from being scheduled at the same time.\n let scheduled: Promise<void> | undefined\n\n // This function flushes the next update to the browser history\n const flush = () => {\n if (!next) {\n return\n }\n\n // We need to ignore any updates to the subscribers while we update the browser history\n history._ignoreSubscribers = true\n\n // Update the browser history\n ;(next.isPush ? win.history.pushState : win.history.replaceState)(\n next.state,\n '',\n next.href,\n )\n\n // Stop ignoring subscriber updates\n history._ignoreSubscribers = false\n\n // Reset the nextIsPush flag and clear the scheduled update\n next = undefined\n scheduled = undefined\n rollbackLocation = undefined\n }\n\n // This function queues up a call to update the browser history\n const queueHistoryAction = (\n type: 'push' | 'replace',\n destHref: string,\n state: any,\n ) => {\n const href = createHref(destHref)\n\n if (!scheduled) {\n rollbackLocation = currentLocation\n }\n\n // Update the location in memory\n currentLocation = parseHref(destHref, state)\n\n // Keep track of the next location we need to flush to the URL\n next = {\n href,\n state,\n isPush: next?.isPush || type === 'push',\n }\n\n if (!scheduled) {\n // Schedule an update to the browser history\n scheduled = Promise.resolve().then(() => flush())\n }\n }\n\n // NOTE: this function can probably be removed\n const onPushPop = (type: 'PUSH' | 'REPLACE') => {\n currentLocation = parseLocation()\n history.notify({ type })\n }\n\n const onPushPopEvent = async () => {\n if (ignoreNextPop) {\n ignoreNextPop = false\n return\n }\n\n const nextLocation = parseLocation()\n const delta =\n nextLocation.state[stateIndexKey] - currentLocation.state[stateIndexKey]\n const isForward = delta === 1\n const isBack = delta === -1\n const isGo = (!isForward && !isBack) || nextPopIsGo\n nextPopIsGo = false\n\n const action = isGo ? 'GO' : isBack ? 'BACK' : 'FORWARD'\n const notify: SubscriberHistoryAction = isGo\n ? {\n type: 'GO',\n index: delta,\n }\n : {\n type: isBack ? 'BACK' : 'FORWARD',\n }\n\n if (skipBlockerNextPop) {\n skipBlockerNextPop = false\n } else {\n const blockers = _getBlockers()\n if (typeof document !== 'undefined' && blockers.length) {\n for (const blocker of blockers) {\n const isBlocked = await blocker.blockerFn({\n currentLocation,\n nextLocation,\n action,\n })\n if (isBlocked) {\n ignoreNextPop = true\n win.history.go(1)\n history.notify(notify)\n return\n }\n }\n }\n }\n\n currentLocation = parseLocation()\n history.notify(notify)\n }\n\n const onBeforeUnload = (e: BeforeUnloadEvent) => {\n if (ignoreNextBeforeUnload) {\n ignoreNextBeforeUnload = false\n return\n }\n\n let shouldBlock = false\n\n // If one blocker has a non-disabled beforeUnload, we should block\n const blockers = _getBlockers()\n if (typeof document !== 'undefined' && blockers.length) {\n for (const blocker of blockers) {\n const shouldHaveBeforeUnload = blocker.enableBeforeUnload ?? true\n if (shouldHaveBeforeUnload === true) {\n shouldBlock = true\n break\n }\n\n if (\n typeof shouldHaveBeforeUnload === 'function' &&\n shouldHaveBeforeUnload() === true\n ) {\n shouldBlock = true\n break\n }\n }\n }\n\n if (shouldBlock) {\n e.preventDefault()\n return (e.returnValue = '')\n }\n return\n }\n\n const history = createHistory({\n getLocation,\n getLength: () => win.history.length,\n pushState: (href, state) => queueHistoryAction('push', href, state),\n replaceState: (href, state) => queueHistoryAction('replace', href, state),\n back: (ignoreBlocker) => {\n if (ignoreBlocker) skipBlockerNextPop = true\n ignoreNextBeforeUnload = true\n return win.history.back()\n },\n forward: (ignoreBlocker) => {\n if (ignoreBlocker) skipBlockerNextPop = true\n ignoreNextBeforeUnload = true\n win.history.forward()\n },\n go: (n) => {\n nextPopIsGo = true\n win.history.go(n)\n },\n createHref: (href) => createHref(href),\n flush,\n destroy: () => {\n win.history.pushState = originalPushState\n win.history.replaceState = originalReplaceState\n win.removeEventListener(beforeUnloadEvent, onBeforeUnload, {\n capture: true,\n })\n win.removeEventListener(popStateEvent, onPushPopEvent)\n },\n onBlocked: () => {\n // If a navigation is blocked, we need to rollback the location\n // that we optimistically updated in memory.\n if (rollbackLocation && currentLocation !== rollbackLocation) {\n currentLocation = rollbackLocation\n }\n },\n getBlockers: _getBlockers,\n setBlockers: _setBlockers,\n notifyOnIndexChange: false,\n })\n\n win.addEventListener(beforeUnloadEvent, onBeforeUnload, { capture: true })\n win.addEventListener(popStateEvent, onPushPopEvent)\n\n win.history.pushState = function (...args: Array<any>) {\n const res = originalPushState.apply(win.history, args as any)\n if (!history._ignoreSubscribers) onPushPop('PUSH')\n return res\n }\n\n win.history.replaceState = function (...args: Array<any>) {\n const res = originalReplaceState.apply(win.history, args as any)\n if (!history._ignoreSubscribers) onPushPop('REPLACE')\n return res\n }\n\n return history\n}\n\n/**\n * Create a hash-based history implementation.\n * Useful for static hosts or environments without server URL rewriting.\n * @link https://tanstack.com/router/latest/docs/framework/react/guide/history-types\n */\nexport function createHashHistory(opts?: { window?: any }): RouterHistory {\n const win =\n opts?.window ??\n (typeof document !== 'undefined' ? window : (undefined as any))\n return createBrowserHistory({\n window: win,\n parseLocation: () => {\n const hashSplit = win.location.hash.split('#').slice(1)\n const pathPart = hashSplit[0] ?? '/'\n const searchPart = win.location.search\n const hashEntries = hashSplit.slice(1)\n const hashPart =\n hashEntries.length === 0 ? '' : `#${hashEntries.join('#')}`\n const hashHref = `${pathPart}${searchPart}${hashPart}`\n return parseHref(hashHref, win.history.state)\n },\n createHref: (href) =>\n `${win.location.pathname}${win.location.search}#${href}`,\n })\n}\n\n/**\n * Create an in-memory history implementation.\n * Ideal for server rendering, tests, and non-DOM environments.\n * @link https://tanstack.com/router/latest/docs/framework/react/guide/history-types\n */\nexport function createMemoryHistory(\n opts: {\n initialEntries: Array<string>\n initialIndex?: number\n } = {\n initialEntries: ['/'],\n },\n): RouterHistory {\n const entries = opts.initialEntries\n let index = opts.initialIndex\n ? Math.min(Math.max(opts.initialIndex, 0), entries.length - 1)\n : entries.length - 1\n const states = entries.map((_entry, index) =>\n assignKeyAndIndex(index, undefined),\n )\n\n const getLocation = () => parseHref(entries[index]!, states[index])\n\n let blockers: Array<NavigationBlocker> = []\n const _getBlockers = () => blockers\n const _setBlockers = (newBlockers: Array<NavigationBlocker>) =>\n (blockers = newBlockers)\n\n return createHistory({\n getLocation,\n getLength: () => entries.length,\n pushState: (path, state) => {\n // Removes all subsequent entries after the current index to start a new branch\n if (index < entries.length - 1) {\n entries.splice(index + 1)\n states.splice(index + 1)\n }\n states.push(state)\n entries.push(path)\n index = Math.max(entries.length - 1, 0)\n },\n replaceState: (path, state) => {\n states[index] = state\n entries[index] = path\n },\n back: () => {\n index = Math.max(index - 1, 0)\n },\n forward: () => {\n index = Math.min(index + 1, entries.length - 1)\n },\n go: (n) => {\n index = Math.min(Math.max(index + n, 0), entries.length - 1)\n },\n createHref: (path) => path,\n getBlockers: _getBlockers,\n setBlockers: _setBlockers,\n })\n}\n\n/**\n * Sanitize a path to prevent open redirect vulnerabilities.\n * Removes control characters and collapses leading double slashes.\n */\nfunction sanitizePath(path: string): string {\n // Remove ASCII control characters (0x00-0x1F) and DEL (0x7F)\n // These include CR (\\r = 0x0D), LF (\\n = 0x0A), and other potentially dangerous characters\n // eslint-disable-next-line no-control-regex\n let sanitized = path.replace(/[\\x00-\\x1f\\x7f]/g, '')\n\n // Prevent open redirect via protocol-relative URLs (e.g. \"//evil.com\")\n // Collapse leading double slashes to a single slash\n if (sanitized.startsWith('//')) {\n sanitized = '/' + sanitized.replace(/^\\/+/, '')\n }\n\n return sanitized\n}\n\nexport function parseHref(\n href: string,\n state: ParsedHistoryState | undefined,\n): HistoryLocation {\n const sanitizedHref = sanitizePath(href)\n const hashIndex = sanitizedHref.indexOf('#')\n const searchIndex = sanitizedHref.indexOf('?')\n\n const addedKey = createRandomKey()\n\n return {\n href: sanitizedHref,\n pathname: sanitizedHref.substring(\n 0,\n hashIndex > 0\n ? searchIndex > 0\n ? Math.min(hashIndex, searchIndex)\n : hashIndex\n : searchIndex > 0\n ? searchIndex\n : sanitizedHref.length,\n ),\n hash: hashIndex > -1 ? sanitizedHref.substring(hashIndex) : '',\n search:\n searchIndex > -1\n ? sanitizedHref.slice(\n searchIndex,\n hashIndex === -1 ? undefined : hashIndex,\n )\n : '',\n state: state || { [stateIndexKey]: 0, key: addedKey, __TSR_key: addedKey },\n }\n}\n\n// Thanks co-pilot!\nfunction createRandomKey() {\n return (Math.random() + 1).toString(36).substring(7)\n}\n"],"names":["blockers","index"],"mappings":"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"}
|
package/dist/cjs/index.d.cts
CHANGED
|
@@ -1,32 +1,23 @@
|
|
|
1
1
|
export interface NavigateOptions {
|
|
2
2
|
ignoreBlocker?: boolean;
|
|
3
|
-
/** When true, Transitioner should skip calling load() - commitLocation handles it */
|
|
4
|
-
skipTransitionerLoad?: boolean;
|
|
5
3
|
}
|
|
6
|
-
/** Result of a navigation attempt (push/replace) */
|
|
7
|
-
export type NavigationResult = {
|
|
8
|
-
type: 'SUCCESS';
|
|
9
|
-
} | {
|
|
10
|
-
type: 'BLOCKED';
|
|
11
|
-
};
|
|
12
4
|
type SubscriberHistoryAction = {
|
|
13
5
|
type: Exclude<HistoryAction, 'GO'>;
|
|
14
6
|
} | {
|
|
15
7
|
type: 'GO';
|
|
16
8
|
index: number;
|
|
17
9
|
};
|
|
18
|
-
|
|
10
|
+
type SubscriberArgs = {
|
|
19
11
|
location: HistoryLocation;
|
|
20
12
|
action: SubscriberHistoryAction;
|
|
21
|
-
navigateOpts?: NavigateOptions;
|
|
22
13
|
};
|
|
23
14
|
export interface RouterHistory {
|
|
24
15
|
location: HistoryLocation;
|
|
25
16
|
length: number;
|
|
26
17
|
subscribers: Set<(opts: SubscriberArgs) => void>;
|
|
27
18
|
subscribe: (cb: (opts: SubscriberArgs) => void) => () => void;
|
|
28
|
-
push: (path: string, state?: any, navigateOpts?: NavigateOptions) =>
|
|
29
|
-
replace: (path: string, state?: any, navigateOpts?: NavigateOptions) =>
|
|
19
|
+
push: (path: string, state?: any, navigateOpts?: NavigateOptions) => void;
|
|
20
|
+
replace: (path: string, state?: any, navigateOpts?: NavigateOptions) => void;
|
|
30
21
|
go: (index: number, navigateOpts?: NavigateOptions) => void;
|
|
31
22
|
back: (navigateOpts?: NavigateOptions) => void;
|
|
32
23
|
forward: (navigateOpts?: NavigateOptions) => void;
|
|
@@ -53,21 +44,6 @@ export type ParsedHistoryState = HistoryState & {
|
|
|
53
44
|
key?: string;
|
|
54
45
|
__TSR_key?: string;
|
|
55
46
|
__TSR_index: number;
|
|
56
|
-
/** Whether to reset scroll position on this navigation (default: true) */
|
|
57
|
-
__TSR_resetScroll?: boolean;
|
|
58
|
-
/** Session id for cached TSR internals */
|
|
59
|
-
__TSR_sessionId?: string;
|
|
60
|
-
/** Match snapshot for fast-path on back/forward navigation */
|
|
61
|
-
__TSR_matches?: {
|
|
62
|
-
routeIds: Array<string>;
|
|
63
|
-
params: Record<string, string>;
|
|
64
|
-
globalNotFoundRouteId?: string;
|
|
65
|
-
searchStr?: string;
|
|
66
|
-
validatedSearches?: Array<{
|
|
67
|
-
search: Record<string, unknown>;
|
|
68
|
-
strictSearch: Record<string, unknown>;
|
|
69
|
-
}>;
|
|
70
|
-
};
|
|
71
47
|
};
|
|
72
48
|
type ShouldAllowNavigation = any;
|
|
73
49
|
export type HistoryAction = 'PUSH' | 'REPLACE' | 'FORWARD' | 'BACK' | 'GO';
|
package/dist/esm/index.d.ts
CHANGED
|
@@ -1,32 +1,23 @@
|
|
|
1
1
|
export interface NavigateOptions {
|
|
2
2
|
ignoreBlocker?: boolean;
|
|
3
|
-
/** When true, Transitioner should skip calling load() - commitLocation handles it */
|
|
4
|
-
skipTransitionerLoad?: boolean;
|
|
5
3
|
}
|
|
6
|
-
/** Result of a navigation attempt (push/replace) */
|
|
7
|
-
export type NavigationResult = {
|
|
8
|
-
type: 'SUCCESS';
|
|
9
|
-
} | {
|
|
10
|
-
type: 'BLOCKED';
|
|
11
|
-
};
|
|
12
4
|
type SubscriberHistoryAction = {
|
|
13
5
|
type: Exclude<HistoryAction, 'GO'>;
|
|
14
6
|
} | {
|
|
15
7
|
type: 'GO';
|
|
16
8
|
index: number;
|
|
17
9
|
};
|
|
18
|
-
|
|
10
|
+
type SubscriberArgs = {
|
|
19
11
|
location: HistoryLocation;
|
|
20
12
|
action: SubscriberHistoryAction;
|
|
21
|
-
navigateOpts?: NavigateOptions;
|
|
22
13
|
};
|
|
23
14
|
export interface RouterHistory {
|
|
24
15
|
location: HistoryLocation;
|
|
25
16
|
length: number;
|
|
26
17
|
subscribers: Set<(opts: SubscriberArgs) => void>;
|
|
27
18
|
subscribe: (cb: (opts: SubscriberArgs) => void) => () => void;
|
|
28
|
-
push: (path: string, state?: any, navigateOpts?: NavigateOptions) =>
|
|
29
|
-
replace: (path: string, state?: any, navigateOpts?: NavigateOptions) =>
|
|
19
|
+
push: (path: string, state?: any, navigateOpts?: NavigateOptions) => void;
|
|
20
|
+
replace: (path: string, state?: any, navigateOpts?: NavigateOptions) => void;
|
|
30
21
|
go: (index: number, navigateOpts?: NavigateOptions) => void;
|
|
31
22
|
back: (navigateOpts?: NavigateOptions) => void;
|
|
32
23
|
forward: (navigateOpts?: NavigateOptions) => void;
|
|
@@ -53,21 +44,6 @@ export type ParsedHistoryState = HistoryState & {
|
|
|
53
44
|
key?: string;
|
|
54
45
|
__TSR_key?: string;
|
|
55
46
|
__TSR_index: number;
|
|
56
|
-
/** Whether to reset scroll position on this navigation (default: true) */
|
|
57
|
-
__TSR_resetScroll?: boolean;
|
|
58
|
-
/** Session id for cached TSR internals */
|
|
59
|
-
__TSR_sessionId?: string;
|
|
60
|
-
/** Match snapshot for fast-path on back/forward navigation */
|
|
61
|
-
__TSR_matches?: {
|
|
62
|
-
routeIds: Array<string>;
|
|
63
|
-
params: Record<string, string>;
|
|
64
|
-
globalNotFoundRouteId?: string;
|
|
65
|
-
searchStr?: string;
|
|
66
|
-
validatedSearches?: Array<{
|
|
67
|
-
search: Record<string, unknown>;
|
|
68
|
-
strictSearch: Record<string, unknown>;
|
|
69
|
-
}>;
|
|
70
|
-
};
|
|
71
47
|
};
|
|
72
48
|
type ShouldAllowNavigation = any;
|
|
73
49
|
export type HistoryAction = 'PUSH' | 'REPLACE' | 'FORWARD' | 'BACK' | 'GO';
|
package/dist/esm/index.js
CHANGED
|
@@ -4,11 +4,9 @@ const beforeUnloadEvent = "beforeunload";
|
|
|
4
4
|
function createHistory(opts) {
|
|
5
5
|
let location = opts.getLocation();
|
|
6
6
|
const subscribers = /* @__PURE__ */ new Set();
|
|
7
|
-
const notify = (action
|
|
7
|
+
const notify = (action) => {
|
|
8
8
|
location = opts.getLocation();
|
|
9
|
-
subscribers.forEach(
|
|
10
|
-
(subscriber) => subscriber({ location, action, navigateOpts })
|
|
11
|
-
);
|
|
9
|
+
subscribers.forEach((subscriber) => subscriber({ location, action }));
|
|
12
10
|
};
|
|
13
11
|
const handleIndexChange = (action) => {
|
|
14
12
|
if (opts.notifyOnIndexChange ?? true) notify(action);
|
|
@@ -22,7 +20,7 @@ function createHistory(opts) {
|
|
|
22
20
|
const ignoreBlocker = navigateOpts?.ignoreBlocker ?? false;
|
|
23
21
|
if (ignoreBlocker) {
|
|
24
22
|
task();
|
|
25
|
-
return
|
|
23
|
+
return;
|
|
26
24
|
}
|
|
27
25
|
const blockers = opts.getBlockers?.() ?? [];
|
|
28
26
|
const isPushOrReplace = actionInfo.type === "PUSH" || actionInfo.type === "REPLACE";
|
|
@@ -36,12 +34,11 @@ function createHistory(opts) {
|
|
|
36
34
|
});
|
|
37
35
|
if (isBlocked) {
|
|
38
36
|
opts.onBlocked?.();
|
|
39
|
-
return
|
|
37
|
+
return;
|
|
40
38
|
}
|
|
41
39
|
}
|
|
42
40
|
}
|
|
43
41
|
task();
|
|
44
|
-
return { type: "SUCCESS" };
|
|
45
42
|
};
|
|
46
43
|
return {
|
|
47
44
|
get location() {
|
|
@@ -60,10 +57,10 @@ function createHistory(opts) {
|
|
|
60
57
|
push: (path, state, navigateOpts) => {
|
|
61
58
|
const currentIndex = location.state[stateIndexKey];
|
|
62
59
|
state = assignKeyAndIndex(currentIndex + 1, state);
|
|
63
|
-
|
|
60
|
+
tryNavigation({
|
|
64
61
|
task: () => {
|
|
65
62
|
opts.pushState(path, state);
|
|
66
|
-
notify({ type: "PUSH" }
|
|
63
|
+
notify({ type: "PUSH" });
|
|
67
64
|
},
|
|
68
65
|
navigateOpts,
|
|
69
66
|
type: "PUSH",
|
|
@@ -74,10 +71,10 @@ function createHistory(opts) {
|
|
|
74
71
|
replace: (path, state, navigateOpts) => {
|
|
75
72
|
const currentIndex = location.state[stateIndexKey];
|
|
76
73
|
state = assignKeyAndIndex(currentIndex, state);
|
|
77
|
-
|
|
74
|
+
tryNavigation({
|
|
78
75
|
task: () => {
|
|
79
76
|
opts.replaceState(path, state);
|
|
80
|
-
notify({ type: "REPLACE" }
|
|
77
|
+
notify({ type: "REPLACE" });
|
|
81
78
|
},
|
|
82
79
|
navigateOpts,
|
|
83
80
|
type: "REPLACE",
|
package/dist/esm/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":["../../src/index.ts"],"sourcesContent":["// While the public API was clearly inspired by the \"history\" npm package,\n// This implementation attempts to be more lightweight by\n// making assumptions about the way TanStack Router works\n\nexport interface NavigateOptions {\n ignoreBlocker?: boolean\n /** When true, Transitioner should skip calling load() - commitLocation handles it */\n skipTransitionerLoad?: boolean\n}\n\n/** Result of a navigation attempt (push/replace) */\nexport type NavigationResult = { type: 'SUCCESS' } | { type: 'BLOCKED' }\n\ntype SubscriberHistoryAction =\n | {\n type: Exclude<HistoryAction, 'GO'>\n }\n | {\n type: 'GO'\n index: number\n }\n\nexport type SubscriberArgs = {\n location: HistoryLocation\n action: SubscriberHistoryAction\n navigateOpts?: NavigateOptions\n}\n\nexport interface RouterHistory {\n location: HistoryLocation\n length: number\n subscribers: Set<(opts: SubscriberArgs) => void>\n subscribe: (cb: (opts: SubscriberArgs) => void) => () => void\n push: (\n path: string,\n state?: any,\n navigateOpts?: NavigateOptions,\n ) => Promise<NavigationResult>\n replace: (\n path: string,\n state?: any,\n navigateOpts?: NavigateOptions,\n ) => Promise<NavigationResult>\n go: (index: number, navigateOpts?: NavigateOptions) => void\n back: (navigateOpts?: NavigateOptions) => void\n forward: (navigateOpts?: NavigateOptions) => void\n canGoBack: () => boolean\n createHref: (href: string) => string\n block: (blocker: NavigationBlocker) => () => void\n flush: () => void\n destroy: () => void\n notify: (action: SubscriberHistoryAction) => void\n _ignoreSubscribers?: boolean\n}\n\nexport interface HistoryLocation extends ParsedPath {\n state: ParsedHistoryState\n}\n\nexport interface ParsedPath {\n href: string\n pathname: string\n search: string\n hash: string\n}\n\nexport interface HistoryState {}\n\nexport type ParsedHistoryState = HistoryState & {\n key?: string // TODO: Remove in v2 - use __TSR_key instead\n __TSR_key?: string\n __TSR_index: number\n /** Whether to reset scroll position on this navigation (default: true) */\n __TSR_resetScroll?: boolean\n /** Session id for cached TSR internals */\n __TSR_sessionId?: string\n /** Match snapshot for fast-path on back/forward navigation */\n __TSR_matches?: {\n routeIds: Array<string>\n params: Record<string, string>\n globalNotFoundRouteId?: string\n searchStr?: string\n validatedSearches?: Array<{\n search: Record<string, unknown>\n strictSearch: Record<string, unknown>\n }>\n }\n}\n\ntype ShouldAllowNavigation = any\n\nexport type HistoryAction = 'PUSH' | 'REPLACE' | 'FORWARD' | 'BACK' | 'GO'\n\nexport type BlockerFnArgs = {\n currentLocation: HistoryLocation\n nextLocation: HistoryLocation\n action: HistoryAction\n}\n\nexport type BlockerFn = (\n args: BlockerFnArgs,\n) => Promise<ShouldAllowNavigation> | ShouldAllowNavigation\n\nexport type NavigationBlocker = {\n blockerFn: BlockerFn\n enableBeforeUnload?: (() => boolean) | boolean\n}\n\ntype TryNavigateArgs = {\n task: () => void\n type: 'PUSH' | 'REPLACE' | 'BACK' | 'FORWARD' | 'GO'\n navigateOpts?: NavigateOptions\n} & (\n | {\n type: 'PUSH' | 'REPLACE'\n path: string\n state: any\n }\n | {\n type: 'BACK' | 'FORWARD' | 'GO'\n }\n)\n\nconst stateIndexKey = '__TSR_index'\nconst popStateEvent = 'popstate'\nconst beforeUnloadEvent = 'beforeunload'\n\nexport function createHistory(opts: {\n getLocation: () => HistoryLocation\n getLength: () => number\n pushState: (path: string, state: any) => void\n replaceState: (path: string, state: any) => void\n go: (n: number) => void\n back: (ignoreBlocker: boolean) => void\n forward: (ignoreBlocker: boolean) => void\n createHref: (path: string) => string\n flush?: () => void\n destroy?: () => void\n onBlocked?: () => void\n getBlockers?: () => Array<NavigationBlocker>\n setBlockers?: (blockers: Array<NavigationBlocker>) => void\n // Avoid notifying on forward/back/go, used for browser history as we already get notified by the popstate event\n notifyOnIndexChange?: boolean\n}): RouterHistory {\n let location = opts.getLocation()\n const subscribers = new Set<(opts: SubscriberArgs) => void>()\n\n const notify = (\n action: SubscriberHistoryAction,\n navigateOpts?: NavigateOptions,\n ) => {\n location = opts.getLocation()\n subscribers.forEach((subscriber) =>\n subscriber({ location, action, navigateOpts }),\n )\n }\n\n const handleIndexChange = (action: SubscriberHistoryAction) => {\n if (opts.notifyOnIndexChange ?? true) notify(action)\n else location = opts.getLocation()\n }\n\n const tryNavigation = async ({\n task,\n navigateOpts,\n ...actionInfo\n }: TryNavigateArgs): Promise<NavigationResult> => {\n const ignoreBlocker = navigateOpts?.ignoreBlocker ?? false\n if (ignoreBlocker) {\n task()\n return { type: 'SUCCESS' }\n }\n\n const blockers = opts.getBlockers?.() ?? []\n const isPushOrReplace =\n actionInfo.type === 'PUSH' || actionInfo.type === 'REPLACE'\n if (typeof document !== 'undefined' && blockers.length && isPushOrReplace) {\n for (const blocker of blockers) {\n const nextLocation = parseHref(actionInfo.path, actionInfo.state)\n const isBlocked = await blocker.blockerFn({\n currentLocation: location,\n nextLocation,\n action: actionInfo.type,\n })\n if (isBlocked) {\n opts.onBlocked?.()\n return { type: 'BLOCKED' }\n }\n }\n }\n\n task()\n return { type: 'SUCCESS' }\n }\n\n return {\n get location() {\n return location\n },\n get length() {\n return opts.getLength()\n },\n subscribers,\n subscribe: (cb: (opts: SubscriberArgs) => void) => {\n subscribers.add(cb)\n\n return () => {\n subscribers.delete(cb)\n }\n },\n push: (path, state, navigateOpts) => {\n const currentIndex = location.state[stateIndexKey]\n state = assignKeyAndIndex(currentIndex + 1, state)\n return tryNavigation({\n task: () => {\n opts.pushState(path, state)\n notify({ type: 'PUSH' }, navigateOpts)\n },\n navigateOpts,\n type: 'PUSH',\n path,\n state,\n })\n },\n replace: (path, state, navigateOpts) => {\n const currentIndex = location.state[stateIndexKey]\n state = assignKeyAndIndex(currentIndex, state)\n return tryNavigation({\n task: () => {\n opts.replaceState(path, state)\n notify({ type: 'REPLACE' }, navigateOpts)\n },\n navigateOpts,\n type: 'REPLACE',\n path,\n state,\n })\n },\n go: (index, navigateOpts) => {\n tryNavigation({\n task: () => {\n opts.go(index)\n handleIndexChange({ type: 'GO', index })\n },\n navigateOpts,\n type: 'GO',\n })\n },\n back: (navigateOpts) => {\n tryNavigation({\n task: () => {\n opts.back(navigateOpts?.ignoreBlocker ?? false)\n handleIndexChange({ type: 'BACK' })\n },\n navigateOpts,\n type: 'BACK',\n })\n },\n forward: (navigateOpts) => {\n tryNavigation({\n task: () => {\n opts.forward(navigateOpts?.ignoreBlocker ?? false)\n handleIndexChange({ type: 'FORWARD' })\n },\n navigateOpts,\n type: 'FORWARD',\n })\n },\n canGoBack: () => location.state[stateIndexKey] !== 0,\n createHref: (str) => opts.createHref(str),\n block: (blocker) => {\n if (!opts.setBlockers) return () => {}\n const blockers = opts.getBlockers?.() ?? []\n opts.setBlockers([...blockers, blocker])\n\n return () => {\n const blockers = opts.getBlockers?.() ?? []\n opts.setBlockers?.(blockers.filter((b) => b !== blocker))\n }\n },\n flush: () => opts.flush?.(),\n destroy: () => opts.destroy?.(),\n notify,\n }\n}\n\nfunction assignKeyAndIndex(index: number, state: HistoryState | undefined) {\n if (!state) {\n state = {} as HistoryState\n }\n const key = createRandomKey()\n return {\n ...state,\n key, // TODO: Remove in v2 - use __TSR_key instead\n __TSR_key: key,\n [stateIndexKey]: index,\n } as ParsedHistoryState\n}\n\n/**\n * Creates a history object that can be used to interact with the browser's\n * navigation. This is a lightweight API wrapping the browser's native methods.\n * It is designed to work with TanStack Router, but could be used as a standalone API as well.\n * IMPORTANT: This API implements history throttling via a microtask to prevent\n * excessive calls to the history API. In some browsers, calling history.pushState or\n * history.replaceState in quick succession can cause the browser to ignore subsequent\n * calls. This API smooths out those differences and ensures that your application\n * state will *eventually* match the browser state. In most cases, this is not a problem,\n * but if you need to ensure that the browser state is up to date, you can use the\n * `history.flush` method to immediately flush all pending state changes to the browser URL.\n * @param opts\n * @param opts.getHref A function that returns the current href (path + search + hash)\n * @param opts.createHref A function that takes a path and returns a href (path + search + hash)\n * @returns A history instance\n */\nexport function createBrowserHistory(opts?: {\n parseLocation?: () => HistoryLocation\n createHref?: (path: string) => string\n window?: any\n}): RouterHistory {\n const win =\n opts?.window ??\n (typeof document !== 'undefined' ? window : (undefined as any))\n\n const originalPushState = win.history.pushState\n const originalReplaceState = win.history.replaceState\n\n let blockers: Array<NavigationBlocker> = []\n const _getBlockers = () => blockers\n const _setBlockers = (newBlockers: Array<NavigationBlocker>) =>\n (blockers = newBlockers)\n\n const createHref = opts?.createHref ?? ((path) => path)\n const parseLocation =\n opts?.parseLocation ??\n (() =>\n parseHref(\n `${win.location.pathname}${win.location.search}${win.location.hash}`,\n win.history.state,\n ))\n\n // Ensure there is always a key to start\n if (!win.history.state?.__TSR_key && !win.history.state?.key) {\n const addedKey = createRandomKey()\n win.history.replaceState(\n {\n [stateIndexKey]: 0,\n key: addedKey, // TODO: Remove in v2 - use __TSR_key instead\n __TSR_key: addedKey,\n },\n '',\n )\n }\n\n let currentLocation = parseLocation()\n let rollbackLocation: HistoryLocation | undefined\n\n let nextPopIsGo = false\n let ignoreNextPop = false\n let skipBlockerNextPop = false\n let ignoreNextBeforeUnload = false\n\n const getLocation = () => currentLocation\n\n let next:\n | undefined\n | {\n // This is the latest location that we were attempting to push/replace\n href: string\n // This is the latest state that we were attempting to push/replace\n state: any\n // This is the latest type that we were attempting to push/replace\n isPush: boolean\n }\n\n // We need to track the current scheduled update to prevent\n // multiple updates from being scheduled at the same time.\n let scheduled: Promise<void> | undefined\n\n // This function flushes the next update to the browser history\n const flush = () => {\n if (!next) {\n return\n }\n\n // We need to ignore any updates to the subscribers while we update the browser history\n history._ignoreSubscribers = true\n\n // Update the browser history\n ;(next.isPush ? win.history.pushState : win.history.replaceState)(\n next.state,\n '',\n next.href,\n )\n\n // Stop ignoring subscriber updates\n history._ignoreSubscribers = false\n\n // Reset the nextIsPush flag and clear the scheduled update\n next = undefined\n scheduled = undefined\n rollbackLocation = undefined\n }\n\n // This function queues up a call to update the browser history\n const queueHistoryAction = (\n type: 'push' | 'replace',\n destHref: string,\n state: any,\n ) => {\n const href = createHref(destHref)\n\n if (!scheduled) {\n rollbackLocation = currentLocation\n }\n\n // Update the location in memory\n currentLocation = parseHref(destHref, state)\n\n // Keep track of the next location we need to flush to the URL\n next = {\n href,\n state,\n isPush: next?.isPush || type === 'push',\n }\n\n if (!scheduled) {\n // Schedule an update to the browser history\n scheduled = Promise.resolve().then(() => flush())\n }\n }\n\n // NOTE: this function can probably be removed\n const onPushPop = (type: 'PUSH' | 'REPLACE') => {\n currentLocation = parseLocation()\n history.notify({ type })\n }\n\n const onPushPopEvent = async () => {\n if (ignoreNextPop) {\n ignoreNextPop = false\n return\n }\n\n const nextLocation = parseLocation()\n const delta =\n nextLocation.state[stateIndexKey] - currentLocation.state[stateIndexKey]\n const isForward = delta === 1\n const isBack = delta === -1\n const isGo = (!isForward && !isBack) || nextPopIsGo\n nextPopIsGo = false\n\n const action = isGo ? 'GO' : isBack ? 'BACK' : 'FORWARD'\n const notify: SubscriberHistoryAction = isGo\n ? {\n type: 'GO',\n index: delta,\n }\n : {\n type: isBack ? 'BACK' : 'FORWARD',\n }\n\n if (skipBlockerNextPop) {\n skipBlockerNextPop = false\n } else {\n const blockers = _getBlockers()\n if (typeof document !== 'undefined' && blockers.length) {\n for (const blocker of blockers) {\n const isBlocked = await blocker.blockerFn({\n currentLocation,\n nextLocation,\n action,\n })\n if (isBlocked) {\n ignoreNextPop = true\n win.history.go(1)\n history.notify(notify)\n return\n }\n }\n }\n }\n\n currentLocation = parseLocation()\n history.notify(notify)\n }\n\n const onBeforeUnload = (e: BeforeUnloadEvent) => {\n if (ignoreNextBeforeUnload) {\n ignoreNextBeforeUnload = false\n return\n }\n\n let shouldBlock = false\n\n // If one blocker has a non-disabled beforeUnload, we should block\n const blockers = _getBlockers()\n if (typeof document !== 'undefined' && blockers.length) {\n for (const blocker of blockers) {\n const shouldHaveBeforeUnload = blocker.enableBeforeUnload ?? true\n if (shouldHaveBeforeUnload === true) {\n shouldBlock = true\n break\n }\n\n if (\n typeof shouldHaveBeforeUnload === 'function' &&\n shouldHaveBeforeUnload() === true\n ) {\n shouldBlock = true\n break\n }\n }\n }\n\n if (shouldBlock) {\n e.preventDefault()\n return (e.returnValue = '')\n }\n return\n }\n\n const history = createHistory({\n getLocation,\n getLength: () => win.history.length,\n pushState: (href, state) => queueHistoryAction('push', href, state),\n replaceState: (href, state) => queueHistoryAction('replace', href, state),\n back: (ignoreBlocker) => {\n if (ignoreBlocker) skipBlockerNextPop = true\n ignoreNextBeforeUnload = true\n return win.history.back()\n },\n forward: (ignoreBlocker) => {\n if (ignoreBlocker) skipBlockerNextPop = true\n ignoreNextBeforeUnload = true\n win.history.forward()\n },\n go: (n) => {\n nextPopIsGo = true\n win.history.go(n)\n },\n createHref: (href) => createHref(href),\n flush,\n destroy: () => {\n win.history.pushState = originalPushState\n win.history.replaceState = originalReplaceState\n win.removeEventListener(beforeUnloadEvent, onBeforeUnload, {\n capture: true,\n })\n win.removeEventListener(popStateEvent, onPushPopEvent)\n },\n onBlocked: () => {\n // If a navigation is blocked, we need to rollback the location\n // that we optimistically updated in memory.\n if (rollbackLocation && currentLocation !== rollbackLocation) {\n currentLocation = rollbackLocation\n }\n },\n getBlockers: _getBlockers,\n setBlockers: _setBlockers,\n notifyOnIndexChange: false,\n })\n\n win.addEventListener(beforeUnloadEvent, onBeforeUnload, { capture: true })\n win.addEventListener(popStateEvent, onPushPopEvent)\n\n win.history.pushState = function (...args: Array<any>) {\n const res = originalPushState.apply(win.history, args as any)\n if (!history._ignoreSubscribers) onPushPop('PUSH')\n return res\n }\n\n win.history.replaceState = function (...args: Array<any>) {\n const res = originalReplaceState.apply(win.history, args as any)\n if (!history._ignoreSubscribers) onPushPop('REPLACE')\n return res\n }\n\n return history\n}\n\n/**\n * Create a hash-based history implementation.\n * Useful for static hosts or environments without server URL rewriting.\n * @link https://tanstack.com/router/latest/docs/framework/react/guide/history-types\n */\nexport function createHashHistory(opts?: { window?: any }): RouterHistory {\n const win =\n opts?.window ??\n (typeof document !== 'undefined' ? window : (undefined as any))\n return createBrowserHistory({\n window: win,\n parseLocation: () => {\n const hashSplit = win.location.hash.split('#').slice(1)\n const pathPart = hashSplit[0] ?? '/'\n const searchPart = win.location.search\n const hashEntries = hashSplit.slice(1)\n const hashPart =\n hashEntries.length === 0 ? '' : `#${hashEntries.join('#')}`\n const hashHref = `${pathPart}${searchPart}${hashPart}`\n return parseHref(hashHref, win.history.state)\n },\n createHref: (href) =>\n `${win.location.pathname}${win.location.search}#${href}`,\n })\n}\n\n/**\n * Create an in-memory history implementation.\n * Ideal for server rendering, tests, and non-DOM environments.\n * @link https://tanstack.com/router/latest/docs/framework/react/guide/history-types\n */\nexport function createMemoryHistory(\n opts: {\n initialEntries: Array<string>\n initialIndex?: number\n } = {\n initialEntries: ['/'],\n },\n): RouterHistory {\n const entries = opts.initialEntries\n let index = opts.initialIndex\n ? Math.min(Math.max(opts.initialIndex, 0), entries.length - 1)\n : entries.length - 1\n const states = entries.map((_entry, index) =>\n assignKeyAndIndex(index, undefined),\n )\n\n const getLocation = () => parseHref(entries[index]!, states[index])\n\n let blockers: Array<NavigationBlocker> = []\n const _getBlockers = () => blockers\n const _setBlockers = (newBlockers: Array<NavigationBlocker>) =>\n (blockers = newBlockers)\n\n return createHistory({\n getLocation,\n getLength: () => entries.length,\n pushState: (path, state) => {\n // Removes all subsequent entries after the current index to start a new branch\n if (index < entries.length - 1) {\n entries.splice(index + 1)\n states.splice(index + 1)\n }\n states.push(state)\n entries.push(path)\n index = Math.max(entries.length - 1, 0)\n },\n replaceState: (path, state) => {\n states[index] = state\n entries[index] = path\n },\n back: () => {\n index = Math.max(index - 1, 0)\n },\n forward: () => {\n index = Math.min(index + 1, entries.length - 1)\n },\n go: (n) => {\n index = Math.min(Math.max(index + n, 0), entries.length - 1)\n },\n createHref: (path) => path,\n getBlockers: _getBlockers,\n setBlockers: _setBlockers,\n })\n}\n\n/**\n * Sanitize a path to prevent open redirect vulnerabilities.\n * Removes control characters and collapses leading double slashes.\n */\nfunction sanitizePath(path: string): string {\n // Remove ASCII control characters (0x00-0x1F) and DEL (0x7F)\n // These include CR (\\r = 0x0D), LF (\\n = 0x0A), and other potentially dangerous characters\n // eslint-disable-next-line no-control-regex\n let sanitized = path.replace(/[\\x00-\\x1f\\x7f]/g, '')\n\n // Prevent open redirect via protocol-relative URLs (e.g. \"//evil.com\")\n // Collapse leading double slashes to a single slash\n if (sanitized.startsWith('//')) {\n sanitized = '/' + sanitized.replace(/^\\/+/, '')\n }\n\n return sanitized\n}\n\nexport function parseHref(\n href: string,\n state: ParsedHistoryState | undefined,\n): HistoryLocation {\n const sanitizedHref = sanitizePath(href)\n const hashIndex = sanitizedHref.indexOf('#')\n const searchIndex = sanitizedHref.indexOf('?')\n\n const addedKey = createRandomKey()\n\n return {\n href: sanitizedHref,\n pathname: sanitizedHref.substring(\n 0,\n hashIndex > 0\n ? searchIndex > 0\n ? Math.min(hashIndex, searchIndex)\n : hashIndex\n : searchIndex > 0\n ? searchIndex\n : sanitizedHref.length,\n ),\n hash: hashIndex > -1 ? sanitizedHref.substring(hashIndex) : '',\n search:\n searchIndex > -1\n ? sanitizedHref.slice(\n searchIndex,\n hashIndex === -1 ? undefined : hashIndex,\n )\n : '',\n state: state || { [stateIndexKey]: 0, key: addedKey, __TSR_key: addedKey },\n }\n}\n\n// Thanks co-pilot!\nfunction createRandomKey() {\n return (Math.random() + 1).toString(36).substring(7)\n}\n"],"names":["blockers","index"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../../src/index.ts"],"sourcesContent":["// While the public API was clearly inspired by the \"history\" npm package,\n// This implementation attempts to be more lightweight by\n// making assumptions about the way TanStack Router works\n\nexport interface NavigateOptions {\n ignoreBlocker?: boolean\n}\n\ntype SubscriberHistoryAction =\n | {\n type: Exclude<HistoryAction, 'GO'>\n }\n | {\n type: 'GO'\n index: number\n }\n\ntype SubscriberArgs = {\n location: HistoryLocation\n action: SubscriberHistoryAction\n}\n\nexport interface RouterHistory {\n location: HistoryLocation\n length: number\n subscribers: Set<(opts: SubscriberArgs) => void>\n subscribe: (cb: (opts: SubscriberArgs) => void) => () => void\n push: (path: string, state?: any, navigateOpts?: NavigateOptions) => void\n replace: (path: string, state?: any, navigateOpts?: NavigateOptions) => void\n go: (index: number, navigateOpts?: NavigateOptions) => void\n back: (navigateOpts?: NavigateOptions) => void\n forward: (navigateOpts?: NavigateOptions) => void\n canGoBack: () => boolean\n createHref: (href: string) => string\n block: (blocker: NavigationBlocker) => () => void\n flush: () => void\n destroy: () => void\n notify: (action: SubscriberHistoryAction) => void\n _ignoreSubscribers?: boolean\n}\n\nexport interface HistoryLocation extends ParsedPath {\n state: ParsedHistoryState\n}\n\nexport interface ParsedPath {\n href: string\n pathname: string\n search: string\n hash: string\n}\n\nexport interface HistoryState {}\n\nexport type ParsedHistoryState = HistoryState & {\n key?: string // TODO: Remove in v2 - use __TSR_key instead\n __TSR_key?: string\n __TSR_index: number\n}\n\ntype ShouldAllowNavigation = any\n\nexport type HistoryAction = 'PUSH' | 'REPLACE' | 'FORWARD' | 'BACK' | 'GO'\n\nexport type BlockerFnArgs = {\n currentLocation: HistoryLocation\n nextLocation: HistoryLocation\n action: HistoryAction\n}\n\nexport type BlockerFn = (\n args: BlockerFnArgs,\n) => Promise<ShouldAllowNavigation> | ShouldAllowNavigation\n\nexport type NavigationBlocker = {\n blockerFn: BlockerFn\n enableBeforeUnload?: (() => boolean) | boolean\n}\n\ntype TryNavigateArgs = {\n task: () => void\n type: 'PUSH' | 'REPLACE' | 'BACK' | 'FORWARD' | 'GO'\n navigateOpts?: NavigateOptions\n} & (\n | {\n type: 'PUSH' | 'REPLACE'\n path: string\n state: any\n }\n | {\n type: 'BACK' | 'FORWARD' | 'GO'\n }\n)\n\nconst stateIndexKey = '__TSR_index'\nconst popStateEvent = 'popstate'\nconst beforeUnloadEvent = 'beforeunload'\n\nexport function createHistory(opts: {\n getLocation: () => HistoryLocation\n getLength: () => number\n pushState: (path: string, state: any) => void\n replaceState: (path: string, state: any) => void\n go: (n: number) => void\n back: (ignoreBlocker: boolean) => void\n forward: (ignoreBlocker: boolean) => void\n createHref: (path: string) => string\n flush?: () => void\n destroy?: () => void\n onBlocked?: () => void\n getBlockers?: () => Array<NavigationBlocker>\n setBlockers?: (blockers: Array<NavigationBlocker>) => void\n // Avoid notifying on forward/back/go, used for browser history as we already get notified by the popstate event\n notifyOnIndexChange?: boolean\n}): RouterHistory {\n let location = opts.getLocation()\n const subscribers = new Set<(opts: SubscriberArgs) => void>()\n\n const notify = (action: SubscriberHistoryAction) => {\n location = opts.getLocation()\n subscribers.forEach((subscriber) => subscriber({ location, action }))\n }\n\n const handleIndexChange = (action: SubscriberHistoryAction) => {\n if (opts.notifyOnIndexChange ?? true) notify(action)\n else location = opts.getLocation()\n }\n\n const tryNavigation = async ({\n task,\n navigateOpts,\n ...actionInfo\n }: TryNavigateArgs) => {\n const ignoreBlocker = navigateOpts?.ignoreBlocker ?? false\n if (ignoreBlocker) {\n task()\n return\n }\n\n const blockers = opts.getBlockers?.() ?? []\n const isPushOrReplace =\n actionInfo.type === 'PUSH' || actionInfo.type === 'REPLACE'\n if (typeof document !== 'undefined' && blockers.length && isPushOrReplace) {\n for (const blocker of blockers) {\n const nextLocation = parseHref(actionInfo.path, actionInfo.state)\n const isBlocked = await blocker.blockerFn({\n currentLocation: location,\n nextLocation,\n action: actionInfo.type,\n })\n if (isBlocked) {\n opts.onBlocked?.()\n return\n }\n }\n }\n\n task()\n }\n\n return {\n get location() {\n return location\n },\n get length() {\n return opts.getLength()\n },\n subscribers,\n subscribe: (cb: (opts: SubscriberArgs) => void) => {\n subscribers.add(cb)\n\n return () => {\n subscribers.delete(cb)\n }\n },\n push: (path, state, navigateOpts) => {\n const currentIndex = location.state[stateIndexKey]\n state = assignKeyAndIndex(currentIndex + 1, state)\n tryNavigation({\n task: () => {\n opts.pushState(path, state)\n notify({ type: 'PUSH' })\n },\n navigateOpts,\n type: 'PUSH',\n path,\n state,\n })\n },\n replace: (path, state, navigateOpts) => {\n const currentIndex = location.state[stateIndexKey]\n state = assignKeyAndIndex(currentIndex, state)\n tryNavigation({\n task: () => {\n opts.replaceState(path, state)\n notify({ type: 'REPLACE' })\n },\n navigateOpts,\n type: 'REPLACE',\n path,\n state,\n })\n },\n go: (index, navigateOpts) => {\n tryNavigation({\n task: () => {\n opts.go(index)\n handleIndexChange({ type: 'GO', index })\n },\n navigateOpts,\n type: 'GO',\n })\n },\n back: (navigateOpts) => {\n tryNavigation({\n task: () => {\n opts.back(navigateOpts?.ignoreBlocker ?? false)\n handleIndexChange({ type: 'BACK' })\n },\n navigateOpts,\n type: 'BACK',\n })\n },\n forward: (navigateOpts) => {\n tryNavigation({\n task: () => {\n opts.forward(navigateOpts?.ignoreBlocker ?? false)\n handleIndexChange({ type: 'FORWARD' })\n },\n navigateOpts,\n type: 'FORWARD',\n })\n },\n canGoBack: () => location.state[stateIndexKey] !== 0,\n createHref: (str) => opts.createHref(str),\n block: (blocker) => {\n if (!opts.setBlockers) return () => {}\n const blockers = opts.getBlockers?.() ?? []\n opts.setBlockers([...blockers, blocker])\n\n return () => {\n const blockers = opts.getBlockers?.() ?? []\n opts.setBlockers?.(blockers.filter((b) => b !== blocker))\n }\n },\n flush: () => opts.flush?.(),\n destroy: () => opts.destroy?.(),\n notify,\n }\n}\n\nfunction assignKeyAndIndex(index: number, state: HistoryState | undefined) {\n if (!state) {\n state = {} as HistoryState\n }\n const key = createRandomKey()\n return {\n ...state,\n key, // TODO: Remove in v2 - use __TSR_key instead\n __TSR_key: key,\n [stateIndexKey]: index,\n } as ParsedHistoryState\n}\n\n/**\n * Creates a history object that can be used to interact with the browser's\n * navigation. This is a lightweight API wrapping the browser's native methods.\n * It is designed to work with TanStack Router, but could be used as a standalone API as well.\n * IMPORTANT: This API implements history throttling via a microtask to prevent\n * excessive calls to the history API. In some browsers, calling history.pushState or\n * history.replaceState in quick succession can cause the browser to ignore subsequent\n * calls. This API smooths out those differences and ensures that your application\n * state will *eventually* match the browser state. In most cases, this is not a problem,\n * but if you need to ensure that the browser state is up to date, you can use the\n * `history.flush` method to immediately flush all pending state changes to the browser URL.\n * @param opts\n * @param opts.getHref A function that returns the current href (path + search + hash)\n * @param opts.createHref A function that takes a path and returns a href (path + search + hash)\n * @returns A history instance\n */\nexport function createBrowserHistory(opts?: {\n parseLocation?: () => HistoryLocation\n createHref?: (path: string) => string\n window?: any\n}): RouterHistory {\n const win =\n opts?.window ??\n (typeof document !== 'undefined' ? window : (undefined as any))\n\n const originalPushState = win.history.pushState\n const originalReplaceState = win.history.replaceState\n\n let blockers: Array<NavigationBlocker> = []\n const _getBlockers = () => blockers\n const _setBlockers = (newBlockers: Array<NavigationBlocker>) =>\n (blockers = newBlockers)\n\n const createHref = opts?.createHref ?? ((path) => path)\n const parseLocation =\n opts?.parseLocation ??\n (() =>\n parseHref(\n `${win.location.pathname}${win.location.search}${win.location.hash}`,\n win.history.state,\n ))\n\n // Ensure there is always a key to start\n if (!win.history.state?.__TSR_key && !win.history.state?.key) {\n const addedKey = createRandomKey()\n win.history.replaceState(\n {\n [stateIndexKey]: 0,\n key: addedKey, // TODO: Remove in v2 - use __TSR_key instead\n __TSR_key: addedKey,\n },\n '',\n )\n }\n\n let currentLocation = parseLocation()\n let rollbackLocation: HistoryLocation | undefined\n\n let nextPopIsGo = false\n let ignoreNextPop = false\n let skipBlockerNextPop = false\n let ignoreNextBeforeUnload = false\n\n const getLocation = () => currentLocation\n\n let next:\n | undefined\n | {\n // This is the latest location that we were attempting to push/replace\n href: string\n // This is the latest state that we were attempting to push/replace\n state: any\n // This is the latest type that we were attempting to push/replace\n isPush: boolean\n }\n\n // We need to track the current scheduled update to prevent\n // multiple updates from being scheduled at the same time.\n let scheduled: Promise<void> | undefined\n\n // This function flushes the next update to the browser history\n const flush = () => {\n if (!next) {\n return\n }\n\n // We need to ignore any updates to the subscribers while we update the browser history\n history._ignoreSubscribers = true\n\n // Update the browser history\n ;(next.isPush ? win.history.pushState : win.history.replaceState)(\n next.state,\n '',\n next.href,\n )\n\n // Stop ignoring subscriber updates\n history._ignoreSubscribers = false\n\n // Reset the nextIsPush flag and clear the scheduled update\n next = undefined\n scheduled = undefined\n rollbackLocation = undefined\n }\n\n // This function queues up a call to update the browser history\n const queueHistoryAction = (\n type: 'push' | 'replace',\n destHref: string,\n state: any,\n ) => {\n const href = createHref(destHref)\n\n if (!scheduled) {\n rollbackLocation = currentLocation\n }\n\n // Update the location in memory\n currentLocation = parseHref(destHref, state)\n\n // Keep track of the next location we need to flush to the URL\n next = {\n href,\n state,\n isPush: next?.isPush || type === 'push',\n }\n\n if (!scheduled) {\n // Schedule an update to the browser history\n scheduled = Promise.resolve().then(() => flush())\n }\n }\n\n // NOTE: this function can probably be removed\n const onPushPop = (type: 'PUSH' | 'REPLACE') => {\n currentLocation = parseLocation()\n history.notify({ type })\n }\n\n const onPushPopEvent = async () => {\n if (ignoreNextPop) {\n ignoreNextPop = false\n return\n }\n\n const nextLocation = parseLocation()\n const delta =\n nextLocation.state[stateIndexKey] - currentLocation.state[stateIndexKey]\n const isForward = delta === 1\n const isBack = delta === -1\n const isGo = (!isForward && !isBack) || nextPopIsGo\n nextPopIsGo = false\n\n const action = isGo ? 'GO' : isBack ? 'BACK' : 'FORWARD'\n const notify: SubscriberHistoryAction = isGo\n ? {\n type: 'GO',\n index: delta,\n }\n : {\n type: isBack ? 'BACK' : 'FORWARD',\n }\n\n if (skipBlockerNextPop) {\n skipBlockerNextPop = false\n } else {\n const blockers = _getBlockers()\n if (typeof document !== 'undefined' && blockers.length) {\n for (const blocker of blockers) {\n const isBlocked = await blocker.blockerFn({\n currentLocation,\n nextLocation,\n action,\n })\n if (isBlocked) {\n ignoreNextPop = true\n win.history.go(1)\n history.notify(notify)\n return\n }\n }\n }\n }\n\n currentLocation = parseLocation()\n history.notify(notify)\n }\n\n const onBeforeUnload = (e: BeforeUnloadEvent) => {\n if (ignoreNextBeforeUnload) {\n ignoreNextBeforeUnload = false\n return\n }\n\n let shouldBlock = false\n\n // If one blocker has a non-disabled beforeUnload, we should block\n const blockers = _getBlockers()\n if (typeof document !== 'undefined' && blockers.length) {\n for (const blocker of blockers) {\n const shouldHaveBeforeUnload = blocker.enableBeforeUnload ?? true\n if (shouldHaveBeforeUnload === true) {\n shouldBlock = true\n break\n }\n\n if (\n typeof shouldHaveBeforeUnload === 'function' &&\n shouldHaveBeforeUnload() === true\n ) {\n shouldBlock = true\n break\n }\n }\n }\n\n if (shouldBlock) {\n e.preventDefault()\n return (e.returnValue = '')\n }\n return\n }\n\n const history = createHistory({\n getLocation,\n getLength: () => win.history.length,\n pushState: (href, state) => queueHistoryAction('push', href, state),\n replaceState: (href, state) => queueHistoryAction('replace', href, state),\n back: (ignoreBlocker) => {\n if (ignoreBlocker) skipBlockerNextPop = true\n ignoreNextBeforeUnload = true\n return win.history.back()\n },\n forward: (ignoreBlocker) => {\n if (ignoreBlocker) skipBlockerNextPop = true\n ignoreNextBeforeUnload = true\n win.history.forward()\n },\n go: (n) => {\n nextPopIsGo = true\n win.history.go(n)\n },\n createHref: (href) => createHref(href),\n flush,\n destroy: () => {\n win.history.pushState = originalPushState\n win.history.replaceState = originalReplaceState\n win.removeEventListener(beforeUnloadEvent, onBeforeUnload, {\n capture: true,\n })\n win.removeEventListener(popStateEvent, onPushPopEvent)\n },\n onBlocked: () => {\n // If a navigation is blocked, we need to rollback the location\n // that we optimistically updated in memory.\n if (rollbackLocation && currentLocation !== rollbackLocation) {\n currentLocation = rollbackLocation\n }\n },\n getBlockers: _getBlockers,\n setBlockers: _setBlockers,\n notifyOnIndexChange: false,\n })\n\n win.addEventListener(beforeUnloadEvent, onBeforeUnload, { capture: true })\n win.addEventListener(popStateEvent, onPushPopEvent)\n\n win.history.pushState = function (...args: Array<any>) {\n const res = originalPushState.apply(win.history, args as any)\n if (!history._ignoreSubscribers) onPushPop('PUSH')\n return res\n }\n\n win.history.replaceState = function (...args: Array<any>) {\n const res = originalReplaceState.apply(win.history, args as any)\n if (!history._ignoreSubscribers) onPushPop('REPLACE')\n return res\n }\n\n return history\n}\n\n/**\n * Create a hash-based history implementation.\n * Useful for static hosts or environments without server URL rewriting.\n * @link https://tanstack.com/router/latest/docs/framework/react/guide/history-types\n */\nexport function createHashHistory(opts?: { window?: any }): RouterHistory {\n const win =\n opts?.window ??\n (typeof document !== 'undefined' ? window : (undefined as any))\n return createBrowserHistory({\n window: win,\n parseLocation: () => {\n const hashSplit = win.location.hash.split('#').slice(1)\n const pathPart = hashSplit[0] ?? '/'\n const searchPart = win.location.search\n const hashEntries = hashSplit.slice(1)\n const hashPart =\n hashEntries.length === 0 ? '' : `#${hashEntries.join('#')}`\n const hashHref = `${pathPart}${searchPart}${hashPart}`\n return parseHref(hashHref, win.history.state)\n },\n createHref: (href) =>\n `${win.location.pathname}${win.location.search}#${href}`,\n })\n}\n\n/**\n * Create an in-memory history implementation.\n * Ideal for server rendering, tests, and non-DOM environments.\n * @link https://tanstack.com/router/latest/docs/framework/react/guide/history-types\n */\nexport function createMemoryHistory(\n opts: {\n initialEntries: Array<string>\n initialIndex?: number\n } = {\n initialEntries: ['/'],\n },\n): RouterHistory {\n const entries = opts.initialEntries\n let index = opts.initialIndex\n ? Math.min(Math.max(opts.initialIndex, 0), entries.length - 1)\n : entries.length - 1\n const states = entries.map((_entry, index) =>\n assignKeyAndIndex(index, undefined),\n )\n\n const getLocation = () => parseHref(entries[index]!, states[index])\n\n let blockers: Array<NavigationBlocker> = []\n const _getBlockers = () => blockers\n const _setBlockers = (newBlockers: Array<NavigationBlocker>) =>\n (blockers = newBlockers)\n\n return createHistory({\n getLocation,\n getLength: () => entries.length,\n pushState: (path, state) => {\n // Removes all subsequent entries after the current index to start a new branch\n if (index < entries.length - 1) {\n entries.splice(index + 1)\n states.splice(index + 1)\n }\n states.push(state)\n entries.push(path)\n index = Math.max(entries.length - 1, 0)\n },\n replaceState: (path, state) => {\n states[index] = state\n entries[index] = path\n },\n back: () => {\n index = Math.max(index - 1, 0)\n },\n forward: () => {\n index = Math.min(index + 1, entries.length - 1)\n },\n go: (n) => {\n index = Math.min(Math.max(index + n, 0), entries.length - 1)\n },\n createHref: (path) => path,\n getBlockers: _getBlockers,\n setBlockers: _setBlockers,\n })\n}\n\n/**\n * Sanitize a path to prevent open redirect vulnerabilities.\n * Removes control characters and collapses leading double slashes.\n */\nfunction sanitizePath(path: string): string {\n // Remove ASCII control characters (0x00-0x1F) and DEL (0x7F)\n // These include CR (\\r = 0x0D), LF (\\n = 0x0A), and other potentially dangerous characters\n // eslint-disable-next-line no-control-regex\n let sanitized = path.replace(/[\\x00-\\x1f\\x7f]/g, '')\n\n // Prevent open redirect via protocol-relative URLs (e.g. \"//evil.com\")\n // Collapse leading double slashes to a single slash\n if (sanitized.startsWith('//')) {\n sanitized = '/' + sanitized.replace(/^\\/+/, '')\n }\n\n return sanitized\n}\n\nexport function parseHref(\n href: string,\n state: ParsedHistoryState | undefined,\n): HistoryLocation {\n const sanitizedHref = sanitizePath(href)\n const hashIndex = sanitizedHref.indexOf('#')\n const searchIndex = sanitizedHref.indexOf('?')\n\n const addedKey = createRandomKey()\n\n return {\n href: sanitizedHref,\n pathname: sanitizedHref.substring(\n 0,\n hashIndex > 0\n ? searchIndex > 0\n ? Math.min(hashIndex, searchIndex)\n : hashIndex\n : searchIndex > 0\n ? searchIndex\n : sanitizedHref.length,\n ),\n hash: hashIndex > -1 ? sanitizedHref.substring(hashIndex) : '',\n search:\n searchIndex > -1\n ? sanitizedHref.slice(\n searchIndex,\n hashIndex === -1 ? undefined : hashIndex,\n )\n : '',\n state: state || { [stateIndexKey]: 0, key: addedKey, __TSR_key: addedKey },\n }\n}\n\n// Thanks co-pilot!\nfunction createRandomKey() {\n return (Math.random() + 1).toString(36).substring(7)\n}\n"],"names":["blockers","index"],"mappings":"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"}
|
package/package.json
CHANGED
package/src/index.ts
CHANGED
|
@@ -4,13 +4,8 @@
|
|
|
4
4
|
|
|
5
5
|
export interface NavigateOptions {
|
|
6
6
|
ignoreBlocker?: boolean
|
|
7
|
-
/** When true, Transitioner should skip calling load() - commitLocation handles it */
|
|
8
|
-
skipTransitionerLoad?: boolean
|
|
9
7
|
}
|
|
10
8
|
|
|
11
|
-
/** Result of a navigation attempt (push/replace) */
|
|
12
|
-
export type NavigationResult = { type: 'SUCCESS' } | { type: 'BLOCKED' }
|
|
13
|
-
|
|
14
9
|
type SubscriberHistoryAction =
|
|
15
10
|
| {
|
|
16
11
|
type: Exclude<HistoryAction, 'GO'>
|
|
@@ -20,10 +15,9 @@ type SubscriberHistoryAction =
|
|
|
20
15
|
index: number
|
|
21
16
|
}
|
|
22
17
|
|
|
23
|
-
|
|
18
|
+
type SubscriberArgs = {
|
|
24
19
|
location: HistoryLocation
|
|
25
20
|
action: SubscriberHistoryAction
|
|
26
|
-
navigateOpts?: NavigateOptions
|
|
27
21
|
}
|
|
28
22
|
|
|
29
23
|
export interface RouterHistory {
|
|
@@ -31,16 +25,8 @@ export interface RouterHistory {
|
|
|
31
25
|
length: number
|
|
32
26
|
subscribers: Set<(opts: SubscriberArgs) => void>
|
|
33
27
|
subscribe: (cb: (opts: SubscriberArgs) => void) => () => void
|
|
34
|
-
push: (
|
|
35
|
-
|
|
36
|
-
state?: any,
|
|
37
|
-
navigateOpts?: NavigateOptions,
|
|
38
|
-
) => Promise<NavigationResult>
|
|
39
|
-
replace: (
|
|
40
|
-
path: string,
|
|
41
|
-
state?: any,
|
|
42
|
-
navigateOpts?: NavigateOptions,
|
|
43
|
-
) => Promise<NavigationResult>
|
|
28
|
+
push: (path: string, state?: any, navigateOpts?: NavigateOptions) => void
|
|
29
|
+
replace: (path: string, state?: any, navigateOpts?: NavigateOptions) => void
|
|
44
30
|
go: (index: number, navigateOpts?: NavigateOptions) => void
|
|
45
31
|
back: (navigateOpts?: NavigateOptions) => void
|
|
46
32
|
forward: (navigateOpts?: NavigateOptions) => void
|
|
@@ -70,21 +56,6 @@ export type ParsedHistoryState = HistoryState & {
|
|
|
70
56
|
key?: string // TODO: Remove in v2 - use __TSR_key instead
|
|
71
57
|
__TSR_key?: string
|
|
72
58
|
__TSR_index: number
|
|
73
|
-
/** Whether to reset scroll position on this navigation (default: true) */
|
|
74
|
-
__TSR_resetScroll?: boolean
|
|
75
|
-
/** Session id for cached TSR internals */
|
|
76
|
-
__TSR_sessionId?: string
|
|
77
|
-
/** Match snapshot for fast-path on back/forward navigation */
|
|
78
|
-
__TSR_matches?: {
|
|
79
|
-
routeIds: Array<string>
|
|
80
|
-
params: Record<string, string>
|
|
81
|
-
globalNotFoundRouteId?: string
|
|
82
|
-
searchStr?: string
|
|
83
|
-
validatedSearches?: Array<{
|
|
84
|
-
search: Record<string, unknown>
|
|
85
|
-
strictSearch: Record<string, unknown>
|
|
86
|
-
}>
|
|
87
|
-
}
|
|
88
59
|
}
|
|
89
60
|
|
|
90
61
|
type ShouldAllowNavigation = any
|
|
@@ -145,14 +116,9 @@ export function createHistory(opts: {
|
|
|
145
116
|
let location = opts.getLocation()
|
|
146
117
|
const subscribers = new Set<(opts: SubscriberArgs) => void>()
|
|
147
118
|
|
|
148
|
-
const notify = (
|
|
149
|
-
action: SubscriberHistoryAction,
|
|
150
|
-
navigateOpts?: NavigateOptions,
|
|
151
|
-
) => {
|
|
119
|
+
const notify = (action: SubscriberHistoryAction) => {
|
|
152
120
|
location = opts.getLocation()
|
|
153
|
-
subscribers.forEach((subscriber) =>
|
|
154
|
-
subscriber({ location, action, navigateOpts }),
|
|
155
|
-
)
|
|
121
|
+
subscribers.forEach((subscriber) => subscriber({ location, action }))
|
|
156
122
|
}
|
|
157
123
|
|
|
158
124
|
const handleIndexChange = (action: SubscriberHistoryAction) => {
|
|
@@ -164,11 +130,11 @@ export function createHistory(opts: {
|
|
|
164
130
|
task,
|
|
165
131
|
navigateOpts,
|
|
166
132
|
...actionInfo
|
|
167
|
-
}: TryNavigateArgs)
|
|
133
|
+
}: TryNavigateArgs) => {
|
|
168
134
|
const ignoreBlocker = navigateOpts?.ignoreBlocker ?? false
|
|
169
135
|
if (ignoreBlocker) {
|
|
170
136
|
task()
|
|
171
|
-
return
|
|
137
|
+
return
|
|
172
138
|
}
|
|
173
139
|
|
|
174
140
|
const blockers = opts.getBlockers?.() ?? []
|
|
@@ -184,13 +150,12 @@ export function createHistory(opts: {
|
|
|
184
150
|
})
|
|
185
151
|
if (isBlocked) {
|
|
186
152
|
opts.onBlocked?.()
|
|
187
|
-
return
|
|
153
|
+
return
|
|
188
154
|
}
|
|
189
155
|
}
|
|
190
156
|
}
|
|
191
157
|
|
|
192
158
|
task()
|
|
193
|
-
return { type: 'SUCCESS' }
|
|
194
159
|
}
|
|
195
160
|
|
|
196
161
|
return {
|
|
@@ -211,10 +176,10 @@ export function createHistory(opts: {
|
|
|
211
176
|
push: (path, state, navigateOpts) => {
|
|
212
177
|
const currentIndex = location.state[stateIndexKey]
|
|
213
178
|
state = assignKeyAndIndex(currentIndex + 1, state)
|
|
214
|
-
|
|
179
|
+
tryNavigation({
|
|
215
180
|
task: () => {
|
|
216
181
|
opts.pushState(path, state)
|
|
217
|
-
notify({ type: 'PUSH' }
|
|
182
|
+
notify({ type: 'PUSH' })
|
|
218
183
|
},
|
|
219
184
|
navigateOpts,
|
|
220
185
|
type: 'PUSH',
|
|
@@ -225,10 +190,10 @@ export function createHistory(opts: {
|
|
|
225
190
|
replace: (path, state, navigateOpts) => {
|
|
226
191
|
const currentIndex = location.state[stateIndexKey]
|
|
227
192
|
state = assignKeyAndIndex(currentIndex, state)
|
|
228
|
-
|
|
193
|
+
tryNavigation({
|
|
229
194
|
task: () => {
|
|
230
195
|
opts.replaceState(path, state)
|
|
231
|
-
notify({ type: 'REPLACE' }
|
|
196
|
+
notify({ type: 'REPLACE' })
|
|
232
197
|
},
|
|
233
198
|
navigateOpts,
|
|
234
199
|
type: 'REPLACE',
|