@tanstack/history 1.61.0 → 1.81.3

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.
@@ -18,7 +18,7 @@ function createHistory(opts) {
18
18
  let blockers = [];
19
19
  const notify = () => {
20
20
  location = opts.getLocation();
21
- subscribers.forEach((subscriber) => subscriber());
21
+ subscribers.forEach((subscriber) => subscriber({ location }));
22
22
  };
23
23
  const tryNavigation = async (task, navigateOpts) => {
24
24
  var _a;
@@ -216,35 +216,31 @@ function createMemoryHistory(opts = {
216
216
  }) {
217
217
  const entries = opts.initialEntries;
218
218
  let index = opts.initialIndex ?? entries.length - 1;
219
- let currentState = {
220
- key: createRandomKey()
221
- };
222
- const getLocation = () => parseHref(entries[index], currentState);
219
+ const states = entries.map(() => ({}));
220
+ const getLocation = () => parseHref(entries[index], states[index]);
223
221
  return createHistory({
224
222
  getLocation,
225
223
  getLength: () => entries.length,
226
224
  pushState: (path, state) => {
227
- currentState = state;
228
225
  if (index < entries.length - 1) {
229
226
  entries.splice(index + 1);
227
+ states.splice(index + 1);
230
228
  }
229
+ states.push(state);
231
230
  entries.push(path);
232
231
  index = Math.max(entries.length - 1, 0);
233
232
  },
234
233
  replaceState: (path, state) => {
235
- currentState = state;
234
+ states[index] = state;
236
235
  entries[index] = path;
237
236
  },
238
237
  back: () => {
239
- currentState = assignKey(currentState);
240
238
  index = Math.max(index - 1, 0);
241
239
  },
242
240
  forward: () => {
243
- currentState = assignKey(currentState);
244
241
  index = Math.min(index + 1, entries.length - 1);
245
242
  },
246
243
  go: (n) => {
247
- currentState = assignKey(currentState);
248
244
  index = Math.min(Math.max(index + n, 0), entries.length - 1);
249
245
  },
250
246
  createHref: (path) => path
@@ -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}\nexport interface RouterHistory {\n location: HistoryLocation\n length: number\n subscribers: Set<() => void>\n subscribe: (cb: () => 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 createHref: (href: string) => string\n block: (blocker: BlockerFn) => () => void\n flush: () => void\n destroy: () => void\n notify: () => void\n _ignoreSubscribers?: boolean\n}\n\nexport interface HistoryLocation extends ParsedPath {\n state: HistoryState\n}\n\nexport interface ParsedPath {\n href: string\n pathname: string\n search: string\n hash: string\n}\n\nexport interface HistoryState {\n key?: string\n}\n\ntype ShouldAllowNavigation = any\n\nexport type BlockerFn = () =>\n | Promise<ShouldAllowNavigation>\n | ShouldAllowNavigation\n\nconst pushStateEvent = 'pushstate'\nconst popStateEvent = 'popstate'\nconst beforeUnloadEvent = 'beforeunload'\n\nconst beforeUnloadListener = (event: Event) => {\n event.preventDefault()\n // @ts-expect-error\n return (event.returnValue = '')\n}\n\nconst stopBlocking = () => {\n removeEventListener(beforeUnloadEvent, beforeUnloadListener, {\n capture: true,\n })\n}\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: () => void\n forward: () => void\n createHref: (path: string) => string\n flush?: () => void\n destroy?: () => void\n onBlocked?: (onUpdate: () => void) => void\n}): RouterHistory {\n let location = opts.getLocation()\n const subscribers = new Set<() => void>()\n let blockers: Array<BlockerFn> = []\n\n const notify = () => {\n location = opts.getLocation()\n subscribers.forEach((subscriber) => subscriber())\n }\n\n const tryNavigation = async (\n task: () => void,\n navigateOpts?: NavigateOptions,\n ) => {\n const ignoreBlocker = navigateOpts?.ignoreBlocker ?? false\n if (!ignoreBlocker && typeof document !== 'undefined' && blockers.length) {\n for (const blocker of blockers) {\n const allowed = await blocker()\n if (!allowed) {\n opts.onBlocked?.(notify)\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: () => void) => {\n subscribers.add(cb)\n\n return () => {\n subscribers.delete(cb)\n }\n },\n push: (path, state, navigateOpts) => {\n state = assignKey(state)\n tryNavigation(() => {\n opts.pushState(path, state)\n notify()\n }, navigateOpts)\n },\n replace: (path, state, navigateOpts) => {\n state = assignKey(state)\n tryNavigation(() => {\n opts.replaceState(path, state)\n notify()\n }, navigateOpts)\n },\n go: (index, navigateOpts) => {\n tryNavigation(() => {\n opts.go(index)\n notify()\n }, navigateOpts)\n },\n back: (navigateOpts) => {\n tryNavigation(() => {\n opts.back()\n notify()\n }, navigateOpts)\n },\n forward: (navigateOpts) => {\n tryNavigation(() => {\n opts.forward()\n notify()\n }, navigateOpts)\n },\n createHref: (str) => opts.createHref(str),\n block: (blocker) => {\n blockers.push(blocker)\n\n if (blockers.length === 1) {\n addEventListener(beforeUnloadEvent, beforeUnloadListener, {\n capture: true,\n })\n }\n\n return () => {\n blockers = blockers.filter((b) => b !== blocker)\n\n if (!blockers.length) {\n stopBlocking()\n }\n }\n },\n flush: () => opts.flush?.(),\n destroy: () => opts.destroy?.(),\n notify,\n }\n}\n\nfunction assignKey(state: HistoryState | undefined) {\n if (!state) {\n state = {} as HistoryState\n }\n return {\n ...state,\n key: createRandomKey(),\n }\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 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 let currentLocation = parseLocation()\n let rollbackLocation: HistoryLocation | undefined\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 const onPushPop = () => {\n currentLocation = parseLocation()\n history.notify()\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: () => win.history.back(),\n forward: () => win.history.forward(),\n go: (n) => win.history.go(n),\n createHref: (href) => createHref(href),\n flush,\n destroy: () => {\n win.history.pushState = originalPushState\n win.history.replaceState = originalReplaceState\n win.removeEventListener(pushStateEvent, onPushPop)\n win.removeEventListener(popStateEvent, onPushPop)\n },\n onBlocked: (onUpdate) => {\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 // Notify subscribers\n onUpdate()\n }\n },\n })\n\n win.addEventListener(pushStateEvent, onPushPop)\n win.addEventListener(popStateEvent, onPushPop)\n\n win.history.pushState = function (...args: Array<any>) {\n const res = originalPushState.apply(win.history, args)\n if (!history._ignoreSubscribers) onPushPop()\n return res\n }\n\n win.history.replaceState = function (...args: Array<any>) {\n const res = originalReplaceState.apply(win.history, args)\n if (!history._ignoreSubscribers) onPushPop()\n return res\n }\n\n return history\n}\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 hashHref = win.location.hash.split('#').slice(1).join('#') ?? '/'\n return parseHref(hashHref, win.history.state)\n },\n createHref: (href) =>\n `${win.location.pathname}${win.location.search}#${href}`,\n })\n}\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 ?? entries.length - 1\n let currentState = {\n key: createRandomKey(),\n } as HistoryState\n\n const getLocation = () => parseHref(entries[index]!, currentState)\n\n return createHistory({\n getLocation,\n getLength: () => entries.length,\n pushState: (path, state) => {\n currentState = 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 }\n entries.push(path)\n index = Math.max(entries.length - 1, 0)\n },\n replaceState: (path, state) => {\n currentState = state\n entries[index] = path\n },\n back: () => {\n currentState = assignKey(currentState)\n index = Math.max(index - 1, 0)\n },\n forward: () => {\n currentState = assignKey(currentState)\n index = Math.min(index + 1, entries.length - 1)\n },\n go: (n) => {\n currentState = assignKey(currentState)\n index = Math.min(Math.max(index + n, 0), entries.length - 1)\n },\n createHref: (path) => path,\n })\n}\n\nexport function parseHref(\n href: string,\n state: HistoryState | undefined,\n): HistoryLocation {\n const hashIndex = href.indexOf('#')\n const searchIndex = href.indexOf('?')\n\n return {\n href,\n pathname: href.substring(\n 0,\n hashIndex > 0\n ? searchIndex > 0\n ? Math.min(hashIndex, searchIndex)\n : hashIndex\n : searchIndex > 0\n ? searchIndex\n : href.length,\n ),\n hash: hashIndex > -1 ? href.substring(hashIndex) : '',\n search:\n searchIndex > -1\n ? href.slice(searchIndex, hashIndex === -1 ? undefined : hashIndex)\n : '',\n state: state || {},\n }\n}\n\n// Thanks co-pilot!\nfunction createRandomKey() {\n return (Math.random() + 1).toString(36).substring(7)\n}\n"],"names":[],"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}\nexport interface RouterHistory {\n location: HistoryLocation\n length: number\n subscribers: Set<(opts: { location: HistoryLocation }) => void>\n subscribe: (cb: (opts: { location: HistoryLocation }) => 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 createHref: (href: string) => string\n block: (blocker: BlockerFn) => () => void\n flush: () => void\n destroy: () => void\n notify: () => void\n _ignoreSubscribers?: boolean\n}\n\nexport interface HistoryLocation extends ParsedPath {\n state: HistoryState\n}\n\nexport interface ParsedPath {\n href: string\n pathname: string\n search: string\n hash: string\n}\n\nexport interface HistoryState {\n key?: string\n}\n\ntype ShouldAllowNavigation = any\n\nexport type BlockerFn = () =>\n | Promise<ShouldAllowNavigation>\n | ShouldAllowNavigation\n\nconst pushStateEvent = 'pushstate'\nconst popStateEvent = 'popstate'\nconst beforeUnloadEvent = 'beforeunload'\n\nconst beforeUnloadListener = (event: Event) => {\n event.preventDefault()\n // @ts-expect-error\n return (event.returnValue = '')\n}\n\nconst stopBlocking = () => {\n removeEventListener(beforeUnloadEvent, beforeUnloadListener, {\n capture: true,\n })\n}\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: () => void\n forward: () => void\n createHref: (path: string) => string\n flush?: () => void\n destroy?: () => void\n onBlocked?: (onUpdate: () => void) => void\n}): RouterHistory {\n let location = opts.getLocation()\n const subscribers = new Set<(opts: { location: HistoryLocation }) => void>()\n let blockers: Array<BlockerFn> = []\n\n const notify = () => {\n location = opts.getLocation()\n subscribers.forEach((subscriber) => subscriber({ location }))\n }\n\n const tryNavigation = async (\n task: () => void,\n navigateOpts?: NavigateOptions,\n ) => {\n const ignoreBlocker = navigateOpts?.ignoreBlocker ?? false\n if (!ignoreBlocker && typeof document !== 'undefined' && blockers.length) {\n for (const blocker of blockers) {\n const allowed = await blocker()\n if (!allowed) {\n opts.onBlocked?.(notify)\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: { location: HistoryLocation }) => void) => {\n subscribers.add(cb)\n\n return () => {\n subscribers.delete(cb)\n }\n },\n push: (path, state, navigateOpts) => {\n state = assignKey(state)\n tryNavigation(() => {\n opts.pushState(path, state)\n notify()\n }, navigateOpts)\n },\n replace: (path, state, navigateOpts) => {\n state = assignKey(state)\n tryNavigation(() => {\n opts.replaceState(path, state)\n notify()\n }, navigateOpts)\n },\n go: (index, navigateOpts) => {\n tryNavigation(() => {\n opts.go(index)\n notify()\n }, navigateOpts)\n },\n back: (navigateOpts) => {\n tryNavigation(() => {\n opts.back()\n notify()\n }, navigateOpts)\n },\n forward: (navigateOpts) => {\n tryNavigation(() => {\n opts.forward()\n notify()\n }, navigateOpts)\n },\n createHref: (str) => opts.createHref(str),\n block: (blocker) => {\n blockers.push(blocker)\n\n if (blockers.length === 1) {\n addEventListener(beforeUnloadEvent, beforeUnloadListener, {\n capture: true,\n })\n }\n\n return () => {\n blockers = blockers.filter((b) => b !== blocker)\n\n if (!blockers.length) {\n stopBlocking()\n }\n }\n },\n flush: () => opts.flush?.(),\n destroy: () => opts.destroy?.(),\n notify,\n }\n}\n\nfunction assignKey(state: HistoryState | undefined) {\n if (!state) {\n state = {} as HistoryState\n }\n return {\n ...state,\n key: createRandomKey(),\n }\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 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 let currentLocation = parseLocation()\n let rollbackLocation: HistoryLocation | undefined\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 const onPushPop = () => {\n currentLocation = parseLocation()\n history.notify()\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: () => win.history.back(),\n forward: () => win.history.forward(),\n go: (n) => win.history.go(n),\n createHref: (href) => createHref(href),\n flush,\n destroy: () => {\n win.history.pushState = originalPushState\n win.history.replaceState = originalReplaceState\n win.removeEventListener(pushStateEvent, onPushPop)\n win.removeEventListener(popStateEvent, onPushPop)\n },\n onBlocked: (onUpdate) => {\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 // Notify subscribers\n onUpdate()\n }\n },\n })\n\n win.addEventListener(pushStateEvent, onPushPop)\n win.addEventListener(popStateEvent, onPushPop)\n\n win.history.pushState = function (...args: Array<any>) {\n const res = originalPushState.apply(win.history, args)\n if (!history._ignoreSubscribers) onPushPop()\n return res\n }\n\n win.history.replaceState = function (...args: Array<any>) {\n const res = originalReplaceState.apply(win.history, args)\n if (!history._ignoreSubscribers) onPushPop()\n return res\n }\n\n return history\n}\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 hashHref = win.location.hash.split('#').slice(1).join('#') ?? '/'\n return parseHref(hashHref, win.history.state)\n },\n createHref: (href) =>\n `${win.location.pathname}${win.location.search}#${href}`,\n })\n}\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 ?? entries.length - 1\n const states = entries.map(() => ({}) as HistoryState)\n\n const getLocation = () => parseHref(entries[index]!, states[index])\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 })\n}\n\nexport function parseHref(\n href: string,\n state: HistoryState | undefined,\n): HistoryLocation {\n const hashIndex = href.indexOf('#')\n const searchIndex = href.indexOf('?')\n\n return {\n href,\n pathname: href.substring(\n 0,\n hashIndex > 0\n ? searchIndex > 0\n ? Math.min(hashIndex, searchIndex)\n : hashIndex\n : searchIndex > 0\n ? searchIndex\n : href.length,\n ),\n hash: hashIndex > -1 ? href.substring(hashIndex) : '',\n search:\n searchIndex > -1\n ? href.slice(searchIndex, hashIndex === -1 ? undefined : hashIndex)\n : '',\n state: state || {},\n }\n}\n\n// Thanks co-pilot!\nfunction createRandomKey() {\n return (Math.random() + 1).toString(36).substring(7)\n}\n"],"names":[],"mappings":";;AA8CA,MAAM,iBAAiB;AACvB,MAAM,gBAAgB;AACtB,MAAM,oBAAoB;AAE1B,MAAM,uBAAuB,CAAC,UAAiB;AAC7C,QAAM,eAAe;AAErB,SAAQ,MAAM,cAAc;AAC9B;AAEA,MAAM,eAAe,MAAM;AACzB,sBAAoB,mBAAmB,sBAAsB;AAAA,IAC3D,SAAS;AAAA,EAAA,CACV;AACH;AAEO,SAAS,cAAc,MAYZ;AACZ,MAAA,WAAW,KAAK,YAAY;AAC1B,QAAA,kCAAkB,IAAmD;AAC3E,MAAI,WAA6B,CAAC;AAElC,QAAM,SAAS,MAAM;AACnB,eAAW,KAAK,YAAY;AAC5B,gBAAY,QAAQ,CAAC,eAAe,WAAW,EAAE,SAAA,CAAU,CAAC;AAAA,EAC9D;AAEM,QAAA,gBAAgB,OACpB,MACA,iBACG;;AACG,UAAA,iBAAgB,6CAAc,kBAAiB;AACrD,QAAI,CAAC,iBAAiB,OAAO,aAAa,eAAe,SAAS,QAAQ;AACxE,iBAAW,WAAW,UAAU;AACxB,cAAA,UAAU,MAAM,QAAQ;AAC9B,YAAI,CAAC,SAAS;AACZ,qBAAK,cAAL,8BAAiB;AACjB;AAAA,QAAA;AAAA,MACF;AAAA,IACF;AAGG,SAAA;AAAA,EACP;AAEO,SAAA;AAAA,IACL,IAAI,WAAW;AACN,aAAA;AAAA,IACT;AAAA,IACA,IAAI,SAAS;AACX,aAAO,KAAK,UAAU;AAAA,IACxB;AAAA,IACA;AAAA,IACA,WAAW,CAAC,OAAsD;AAChE,kBAAY,IAAI,EAAE;AAElB,aAAO,MAAM;AACX,oBAAY,OAAO,EAAE;AAAA,MACvB;AAAA,IACF;AAAA,IACA,MAAM,CAAC,MAAM,OAAO,iBAAiB;AACnC,cAAQ,UAAU,KAAK;AACvB,oBAAc,MAAM;AACb,aAAA,UAAU,MAAM,KAAK;AACnB,eAAA;AAAA,SACN,YAAY;AAAA,IACjB;AAAA,IACA,SAAS,CAAC,MAAM,OAAO,iBAAiB;AACtC,cAAQ,UAAU,KAAK;AACvB,oBAAc,MAAM;AACb,aAAA,aAAa,MAAM,KAAK;AACtB,eAAA;AAAA,SACN,YAAY;AAAA,IACjB;AAAA,IACA,IAAI,CAAC,OAAO,iBAAiB;AAC3B,oBAAc,MAAM;AAClB,aAAK,GAAG,KAAK;AACN,eAAA;AAAA,SACN,YAAY;AAAA,IACjB;AAAA,IACA,MAAM,CAAC,iBAAiB;AACtB,oBAAc,MAAM;AAClB,aAAK,KAAK;AACH,eAAA;AAAA,SACN,YAAY;AAAA,IACjB;AAAA,IACA,SAAS,CAAC,iBAAiB;AACzB,oBAAc,MAAM;AAClB,aAAK,QAAQ;AACN,eAAA;AAAA,SACN,YAAY;AAAA,IACjB;AAAA,IACA,YAAY,CAAC,QAAQ,KAAK,WAAW,GAAG;AAAA,IACxC,OAAO,CAAC,YAAY;AAClB,eAAS,KAAK,OAAO;AAEjB,UAAA,SAAS,WAAW,GAAG;AACzB,yBAAiB,mBAAmB,sBAAsB;AAAA,UACxD,SAAS;AAAA,QAAA,CACV;AAAA,MAAA;AAGH,aAAO,MAAM;AACX,mBAAW,SAAS,OAAO,CAAC,MAAM,MAAM,OAAO;AAE3C,YAAA,CAAC,SAAS,QAAQ;AACP,uBAAA;AAAA,QAAA;AAAA,MAEjB;AAAA,IACF;AAAA,IACA,OAAO,MAAA;;AAAM,wBAAK,UAAL;AAAA;AAAA,IACb,SAAS,MAAA;;AAAM,wBAAK,YAAL;AAAA;AAAA,IACf;AAAA,EACF;AACF;AAEA,SAAS,UAAU,OAAiC;AAClD,MAAI,CAAC,OAAO;AACV,YAAQ,CAAC;AAAA,EAAA;AAEJ,SAAA;AAAA,IACL,GAAG;AAAA,IACH,KAAK,gBAAgB;AAAA,EACvB;AACF;AAkBO,SAAS,qBAAqB,MAInB;AAChB,QAAM,OACJ,6BAAM,YACL,OAAO,aAAa,cAAc,SAAU;AAEzC,QAAA,oBAAoB,IAAI,QAAQ;AAChC,QAAA,uBAAuB,IAAI,QAAQ;AAEzC,QAAM,cAAa,6BAAM,gBAAe,CAAC,SAAS;AAC5C,QAAA,iBACJ,6BAAM,mBACL,MACC;AAAA,IACE,GAAG,IAAI,SAAS,QAAQ,GAAG,IAAI,SAAS,MAAM,GAAG,IAAI,SAAS,IAAI;AAAA,IAClE,IAAI,QAAQ;AAAA,EAAA;AAGlB,MAAI,kBAAkB,cAAc;AAChC,MAAA;AAEJ,QAAM,cAAc,MAAM;AAEtB,MAAA;AAaA,MAAA;AAGJ,QAAM,QAAQ,MAAM;AAClB,QAAI,CAAC,MAAM;AACT;AAAA,IAAA;AAIF,YAAQ,qBAAqB;AAG5B,KAAC,KAAK,SAAS,IAAI,QAAQ,YAAY,IAAI,QAAQ;AAAA,MAClD,KAAK;AAAA,MACL;AAAA,MACA,KAAK;AAAA,IACP;AAGA,YAAQ,qBAAqB;AAGtB,WAAA;AACK,gBAAA;AACO,uBAAA;AAAA,EACrB;AAGA,QAAM,qBAAqB,CACzB,MACA,UACA,UACG;AACG,UAAA,OAAO,WAAW,QAAQ;AAEhC,QAAI,CAAC,WAAW;AACK,yBAAA;AAAA,IAAA;AAIH,sBAAA,UAAU,UAAU,KAAK;AAGpC,WAAA;AAAA,MACL;AAAA,MACA;AAAA,MACA,SAAQ,6BAAM,WAAU,SAAS;AAAA,IACnC;AAEA,QAAI,CAAC,WAAW;AAEd,kBAAY,QAAQ,QAAQ,EAAE,KAAK,MAAM,OAAO;AAAA,IAAA;AAAA,EAEpD;AAEA,QAAM,YAAY,MAAM;AACtB,sBAAkB,cAAc;AAChC,YAAQ,OAAO;AAAA,EACjB;AAEA,QAAM,UAAU,cAAc;AAAA,IAC5B;AAAA,IACA,WAAW,MAAM,IAAI,QAAQ;AAAA,IAC7B,WAAW,CAAC,MAAM,UAAU,mBAAmB,QAAQ,MAAM,KAAK;AAAA,IAClE,cAAc,CAAC,MAAM,UAAU,mBAAmB,WAAW,MAAM,KAAK;AAAA,IACxE,MAAM,MAAM,IAAI,QAAQ,KAAK;AAAA,IAC7B,SAAS,MAAM,IAAI,QAAQ,QAAQ;AAAA,IACnC,IAAI,CAAC,MAAM,IAAI,QAAQ,GAAG,CAAC;AAAA,IAC3B,YAAY,CAAC,SAAS,WAAW,IAAI;AAAA,IACrC;AAAA,IACA,SAAS,MAAM;AACb,UAAI,QAAQ,YAAY;AACxB,UAAI,QAAQ,eAAe;AACvB,UAAA,oBAAoB,gBAAgB,SAAS;AAC7C,UAAA,oBAAoB,eAAe,SAAS;AAAA,IAClD;AAAA,IACA,WAAW,CAAC,aAAa;AAGnB,UAAA,oBAAoB,oBAAoB,kBAAkB;AAC1C,0BAAA;AAET,iBAAA;AAAA,MAAA;AAAA,IACX;AAAA,EACF,CACD;AAEG,MAAA,iBAAiB,gBAAgB,SAAS;AAC1C,MAAA,iBAAiB,eAAe,SAAS;AAEzC,MAAA,QAAQ,YAAY,YAAa,MAAkB;AACrD,UAAM,MAAM,kBAAkB,MAAM,IAAI,SAAS,IAAI;AACjD,QAAA,CAAC,QAAQ,mBAA8B,WAAA;AACpC,WAAA;AAAA,EACT;AAEI,MAAA,QAAQ,eAAe,YAAa,MAAkB;AACxD,UAAM,MAAM,qBAAqB,MAAM,IAAI,SAAS,IAAI;AACpD,QAAA,CAAC,QAAQ,mBAA8B,WAAA;AACpC,WAAA;AAAA,EACT;AAEO,SAAA;AACT;AAEO,SAAS,kBAAkB,MAAwC;AACxE,QAAM,OACJ,6BAAM,YACL,OAAO,aAAa,cAAc,SAAU;AAC/C,SAAO,qBAAqB;AAAA,IAC1B,QAAQ;AAAA,IACR,eAAe,MAAM;AACnB,YAAM,WAAW,IAAI,SAAS,KAAK,MAAM,GAAG,EAAE,MAAM,CAAC,EAAE,KAAK,GAAG,KAAK;AACpE,aAAO,UAAU,UAAU,IAAI,QAAQ,KAAK;AAAA,IAC9C;AAAA,IACA,YAAY,CAAC,SACX,GAAG,IAAI,SAAS,QAAQ,GAAG,IAAI,SAAS,MAAM,IAAI,IAAI;AAAA,EAAA,CACzD;AACH;AAEO,SAAS,oBACd,OAGI;AAAA,EACF,gBAAgB,CAAC,GAAG;AACtB,GACe;AACf,QAAM,UAAU,KAAK;AACrB,MAAI,QAAQ,KAAK,gBAAgB,QAAQ,SAAS;AAClD,QAAM,SAAS,QAAQ,IAAI,OAAO,CAAmB,EAAA;AAE/C,QAAA,cAAc,MAAM,UAAU,QAAQ,KAAK,GAAI,OAAO,KAAK,CAAC;AAElE,SAAO,cAAc;AAAA,IACnB;AAAA,IACA,WAAW,MAAM,QAAQ;AAAA,IACzB,WAAW,CAAC,MAAM,UAAU;AAEtB,UAAA,QAAQ,QAAQ,SAAS,GAAG;AACtB,gBAAA,OAAO,QAAQ,CAAC;AACjB,eAAA,OAAO,QAAQ,CAAC;AAAA,MAAA;AAEzB,aAAO,KAAK,KAAK;AACjB,cAAQ,KAAK,IAAI;AACjB,cAAQ,KAAK,IAAI,QAAQ,SAAS,GAAG,CAAC;AAAA,IACxC;AAAA,IACA,cAAc,CAAC,MAAM,UAAU;AAC7B,aAAO,KAAK,IAAI;AAChB,cAAQ,KAAK,IAAI;AAAA,IACnB;AAAA,IACA,MAAM,MAAM;AACV,cAAQ,KAAK,IAAI,QAAQ,GAAG,CAAC;AAAA,IAC/B;AAAA,IACA,SAAS,MAAM;AACb,cAAQ,KAAK,IAAI,QAAQ,GAAG,QAAQ,SAAS,CAAC;AAAA,IAChD;AAAA,IACA,IAAI,CAAC,MAAM;AACD,cAAA,KAAK,IAAI,KAAK,IAAI,QAAQ,GAAG,CAAC,GAAG,QAAQ,SAAS,CAAC;AAAA,IAC7D;AAAA,IACA,YAAY,CAAC,SAAS;AAAA,EAAA,CACvB;AACH;AAEgB,SAAA,UACd,MACA,OACiB;AACX,QAAA,YAAY,KAAK,QAAQ,GAAG;AAC5B,QAAA,cAAc,KAAK,QAAQ,GAAG;AAE7B,SAAA;AAAA,IACL;AAAA,IACA,UAAU,KAAK;AAAA,MACb;AAAA,MACA,YAAY,IACR,cAAc,IACZ,KAAK,IAAI,WAAW,WAAW,IAC/B,YACF,cAAc,IACZ,cACA,KAAK;AAAA,IACb;AAAA,IACA,MAAM,YAAY,KAAK,KAAK,UAAU,SAAS,IAAI;AAAA,IACnD,QACE,cAAc,KACV,KAAK,MAAM,aAAa,cAAc,KAAK,SAAY,SAAS,IAChE;AAAA,IACN,OAAO,SAAS,CAAA;AAAA,EAClB;AACF;AAGA,SAAS,kBAAkB;AACjB,UAAA,KAAK,WAAW,GAAG,SAAS,EAAE,EAAE,UAAU,CAAC;AACrD;;;;;;"}
@@ -4,8 +4,12 @@ export interface NavigateOptions {
4
4
  export interface RouterHistory {
5
5
  location: HistoryLocation;
6
6
  length: number;
7
- subscribers: Set<() => void>;
8
- subscribe: (cb: () => void) => () => void;
7
+ subscribers: Set<(opts: {
8
+ location: HistoryLocation;
9
+ }) => void>;
10
+ subscribe: (cb: (opts: {
11
+ location: HistoryLocation;
12
+ }) => void) => () => void;
9
13
  push: (path: string, state?: any, navigateOpts?: NavigateOptions) => void;
10
14
  replace: (path: string, state?: any, navigateOpts?: NavigateOptions) => void;
11
15
  go: (index: number, navigateOpts?: NavigateOptions) => void;
@@ -4,8 +4,12 @@ export interface NavigateOptions {
4
4
  export interface RouterHistory {
5
5
  location: HistoryLocation;
6
6
  length: number;
7
- subscribers: Set<() => void>;
8
- subscribe: (cb: () => void) => () => void;
7
+ subscribers: Set<(opts: {
8
+ location: HistoryLocation;
9
+ }) => void>;
10
+ subscribe: (cb: (opts: {
11
+ location: HistoryLocation;
12
+ }) => void) => () => void;
9
13
  push: (path: string, state?: any, navigateOpts?: NavigateOptions) => void;
10
14
  replace: (path: string, state?: any, navigateOpts?: NavigateOptions) => void;
11
15
  go: (index: number, navigateOpts?: NavigateOptions) => void;
package/dist/esm/index.js CHANGED
@@ -16,7 +16,7 @@ function createHistory(opts) {
16
16
  let blockers = [];
17
17
  const notify = () => {
18
18
  location = opts.getLocation();
19
- subscribers.forEach((subscriber) => subscriber());
19
+ subscribers.forEach((subscriber) => subscriber({ location }));
20
20
  };
21
21
  const tryNavigation = async (task, navigateOpts) => {
22
22
  var _a;
@@ -214,35 +214,31 @@ function createMemoryHistory(opts = {
214
214
  }) {
215
215
  const entries = opts.initialEntries;
216
216
  let index = opts.initialIndex ?? entries.length - 1;
217
- let currentState = {
218
- key: createRandomKey()
219
- };
220
- const getLocation = () => parseHref(entries[index], currentState);
217
+ const states = entries.map(() => ({}));
218
+ const getLocation = () => parseHref(entries[index], states[index]);
221
219
  return createHistory({
222
220
  getLocation,
223
221
  getLength: () => entries.length,
224
222
  pushState: (path, state) => {
225
- currentState = state;
226
223
  if (index < entries.length - 1) {
227
224
  entries.splice(index + 1);
225
+ states.splice(index + 1);
228
226
  }
227
+ states.push(state);
229
228
  entries.push(path);
230
229
  index = Math.max(entries.length - 1, 0);
231
230
  },
232
231
  replaceState: (path, state) => {
233
- currentState = state;
232
+ states[index] = state;
234
233
  entries[index] = path;
235
234
  },
236
235
  back: () => {
237
- currentState = assignKey(currentState);
238
236
  index = Math.max(index - 1, 0);
239
237
  },
240
238
  forward: () => {
241
- currentState = assignKey(currentState);
242
239
  index = Math.min(index + 1, entries.length - 1);
243
240
  },
244
241
  go: (n) => {
245
- currentState = assignKey(currentState);
246
242
  index = Math.min(Math.max(index + n, 0), entries.length - 1);
247
243
  },
248
244
  createHref: (path) => path
@@ -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}\nexport interface RouterHistory {\n location: HistoryLocation\n length: number\n subscribers: Set<() => void>\n subscribe: (cb: () => 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 createHref: (href: string) => string\n block: (blocker: BlockerFn) => () => void\n flush: () => void\n destroy: () => void\n notify: () => void\n _ignoreSubscribers?: boolean\n}\n\nexport interface HistoryLocation extends ParsedPath {\n state: HistoryState\n}\n\nexport interface ParsedPath {\n href: string\n pathname: string\n search: string\n hash: string\n}\n\nexport interface HistoryState {\n key?: string\n}\n\ntype ShouldAllowNavigation = any\n\nexport type BlockerFn = () =>\n | Promise<ShouldAllowNavigation>\n | ShouldAllowNavigation\n\nconst pushStateEvent = 'pushstate'\nconst popStateEvent = 'popstate'\nconst beforeUnloadEvent = 'beforeunload'\n\nconst beforeUnloadListener = (event: Event) => {\n event.preventDefault()\n // @ts-expect-error\n return (event.returnValue = '')\n}\n\nconst stopBlocking = () => {\n removeEventListener(beforeUnloadEvent, beforeUnloadListener, {\n capture: true,\n })\n}\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: () => void\n forward: () => void\n createHref: (path: string) => string\n flush?: () => void\n destroy?: () => void\n onBlocked?: (onUpdate: () => void) => void\n}): RouterHistory {\n let location = opts.getLocation()\n const subscribers = new Set<() => void>()\n let blockers: Array<BlockerFn> = []\n\n const notify = () => {\n location = opts.getLocation()\n subscribers.forEach((subscriber) => subscriber())\n }\n\n const tryNavigation = async (\n task: () => void,\n navigateOpts?: NavigateOptions,\n ) => {\n const ignoreBlocker = navigateOpts?.ignoreBlocker ?? false\n if (!ignoreBlocker && typeof document !== 'undefined' && blockers.length) {\n for (const blocker of blockers) {\n const allowed = await blocker()\n if (!allowed) {\n opts.onBlocked?.(notify)\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: () => void) => {\n subscribers.add(cb)\n\n return () => {\n subscribers.delete(cb)\n }\n },\n push: (path, state, navigateOpts) => {\n state = assignKey(state)\n tryNavigation(() => {\n opts.pushState(path, state)\n notify()\n }, navigateOpts)\n },\n replace: (path, state, navigateOpts) => {\n state = assignKey(state)\n tryNavigation(() => {\n opts.replaceState(path, state)\n notify()\n }, navigateOpts)\n },\n go: (index, navigateOpts) => {\n tryNavigation(() => {\n opts.go(index)\n notify()\n }, navigateOpts)\n },\n back: (navigateOpts) => {\n tryNavigation(() => {\n opts.back()\n notify()\n }, navigateOpts)\n },\n forward: (navigateOpts) => {\n tryNavigation(() => {\n opts.forward()\n notify()\n }, navigateOpts)\n },\n createHref: (str) => opts.createHref(str),\n block: (blocker) => {\n blockers.push(blocker)\n\n if (blockers.length === 1) {\n addEventListener(beforeUnloadEvent, beforeUnloadListener, {\n capture: true,\n })\n }\n\n return () => {\n blockers = blockers.filter((b) => b !== blocker)\n\n if (!blockers.length) {\n stopBlocking()\n }\n }\n },\n flush: () => opts.flush?.(),\n destroy: () => opts.destroy?.(),\n notify,\n }\n}\n\nfunction assignKey(state: HistoryState | undefined) {\n if (!state) {\n state = {} as HistoryState\n }\n return {\n ...state,\n key: createRandomKey(),\n }\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 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 let currentLocation = parseLocation()\n let rollbackLocation: HistoryLocation | undefined\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 const onPushPop = () => {\n currentLocation = parseLocation()\n history.notify()\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: () => win.history.back(),\n forward: () => win.history.forward(),\n go: (n) => win.history.go(n),\n createHref: (href) => createHref(href),\n flush,\n destroy: () => {\n win.history.pushState = originalPushState\n win.history.replaceState = originalReplaceState\n win.removeEventListener(pushStateEvent, onPushPop)\n win.removeEventListener(popStateEvent, onPushPop)\n },\n onBlocked: (onUpdate) => {\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 // Notify subscribers\n onUpdate()\n }\n },\n })\n\n win.addEventListener(pushStateEvent, onPushPop)\n win.addEventListener(popStateEvent, onPushPop)\n\n win.history.pushState = function (...args: Array<any>) {\n const res = originalPushState.apply(win.history, args)\n if (!history._ignoreSubscribers) onPushPop()\n return res\n }\n\n win.history.replaceState = function (...args: Array<any>) {\n const res = originalReplaceState.apply(win.history, args)\n if (!history._ignoreSubscribers) onPushPop()\n return res\n }\n\n return history\n}\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 hashHref = win.location.hash.split('#').slice(1).join('#') ?? '/'\n return parseHref(hashHref, win.history.state)\n },\n createHref: (href) =>\n `${win.location.pathname}${win.location.search}#${href}`,\n })\n}\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 ?? entries.length - 1\n let currentState = {\n key: createRandomKey(),\n } as HistoryState\n\n const getLocation = () => parseHref(entries[index]!, currentState)\n\n return createHistory({\n getLocation,\n getLength: () => entries.length,\n pushState: (path, state) => {\n currentState = 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 }\n entries.push(path)\n index = Math.max(entries.length - 1, 0)\n },\n replaceState: (path, state) => {\n currentState = state\n entries[index] = path\n },\n back: () => {\n currentState = assignKey(currentState)\n index = Math.max(index - 1, 0)\n },\n forward: () => {\n currentState = assignKey(currentState)\n index = Math.min(index + 1, entries.length - 1)\n },\n go: (n) => {\n currentState = assignKey(currentState)\n index = Math.min(Math.max(index + n, 0), entries.length - 1)\n },\n createHref: (path) => path,\n })\n}\n\nexport function parseHref(\n href: string,\n state: HistoryState | undefined,\n): HistoryLocation {\n const hashIndex = href.indexOf('#')\n const searchIndex = href.indexOf('?')\n\n return {\n href,\n pathname: href.substring(\n 0,\n hashIndex > 0\n ? searchIndex > 0\n ? Math.min(hashIndex, searchIndex)\n : hashIndex\n : searchIndex > 0\n ? searchIndex\n : href.length,\n ),\n hash: hashIndex > -1 ? href.substring(hashIndex) : '',\n search:\n searchIndex > -1\n ? href.slice(searchIndex, hashIndex === -1 ? undefined : hashIndex)\n : '',\n state: state || {},\n }\n}\n\n// Thanks co-pilot!\nfunction createRandomKey() {\n return (Math.random() + 1).toString(36).substring(7)\n}\n"],"names":[],"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}\nexport interface RouterHistory {\n location: HistoryLocation\n length: number\n subscribers: Set<(opts: { location: HistoryLocation }) => void>\n subscribe: (cb: (opts: { location: HistoryLocation }) => 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 createHref: (href: string) => string\n block: (blocker: BlockerFn) => () => void\n flush: () => void\n destroy: () => void\n notify: () => void\n _ignoreSubscribers?: boolean\n}\n\nexport interface HistoryLocation extends ParsedPath {\n state: HistoryState\n}\n\nexport interface ParsedPath {\n href: string\n pathname: string\n search: string\n hash: string\n}\n\nexport interface HistoryState {\n key?: string\n}\n\ntype ShouldAllowNavigation = any\n\nexport type BlockerFn = () =>\n | Promise<ShouldAllowNavigation>\n | ShouldAllowNavigation\n\nconst pushStateEvent = 'pushstate'\nconst popStateEvent = 'popstate'\nconst beforeUnloadEvent = 'beforeunload'\n\nconst beforeUnloadListener = (event: Event) => {\n event.preventDefault()\n // @ts-expect-error\n return (event.returnValue = '')\n}\n\nconst stopBlocking = () => {\n removeEventListener(beforeUnloadEvent, beforeUnloadListener, {\n capture: true,\n })\n}\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: () => void\n forward: () => void\n createHref: (path: string) => string\n flush?: () => void\n destroy?: () => void\n onBlocked?: (onUpdate: () => void) => void\n}): RouterHistory {\n let location = opts.getLocation()\n const subscribers = new Set<(opts: { location: HistoryLocation }) => void>()\n let blockers: Array<BlockerFn> = []\n\n const notify = () => {\n location = opts.getLocation()\n subscribers.forEach((subscriber) => subscriber({ location }))\n }\n\n const tryNavigation = async (\n task: () => void,\n navigateOpts?: NavigateOptions,\n ) => {\n const ignoreBlocker = navigateOpts?.ignoreBlocker ?? false\n if (!ignoreBlocker && typeof document !== 'undefined' && blockers.length) {\n for (const blocker of blockers) {\n const allowed = await blocker()\n if (!allowed) {\n opts.onBlocked?.(notify)\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: { location: HistoryLocation }) => void) => {\n subscribers.add(cb)\n\n return () => {\n subscribers.delete(cb)\n }\n },\n push: (path, state, navigateOpts) => {\n state = assignKey(state)\n tryNavigation(() => {\n opts.pushState(path, state)\n notify()\n }, navigateOpts)\n },\n replace: (path, state, navigateOpts) => {\n state = assignKey(state)\n tryNavigation(() => {\n opts.replaceState(path, state)\n notify()\n }, navigateOpts)\n },\n go: (index, navigateOpts) => {\n tryNavigation(() => {\n opts.go(index)\n notify()\n }, navigateOpts)\n },\n back: (navigateOpts) => {\n tryNavigation(() => {\n opts.back()\n notify()\n }, navigateOpts)\n },\n forward: (navigateOpts) => {\n tryNavigation(() => {\n opts.forward()\n notify()\n }, navigateOpts)\n },\n createHref: (str) => opts.createHref(str),\n block: (blocker) => {\n blockers.push(blocker)\n\n if (blockers.length === 1) {\n addEventListener(beforeUnloadEvent, beforeUnloadListener, {\n capture: true,\n })\n }\n\n return () => {\n blockers = blockers.filter((b) => b !== blocker)\n\n if (!blockers.length) {\n stopBlocking()\n }\n }\n },\n flush: () => opts.flush?.(),\n destroy: () => opts.destroy?.(),\n notify,\n }\n}\n\nfunction assignKey(state: HistoryState | undefined) {\n if (!state) {\n state = {} as HistoryState\n }\n return {\n ...state,\n key: createRandomKey(),\n }\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 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 let currentLocation = parseLocation()\n let rollbackLocation: HistoryLocation | undefined\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 const onPushPop = () => {\n currentLocation = parseLocation()\n history.notify()\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: () => win.history.back(),\n forward: () => win.history.forward(),\n go: (n) => win.history.go(n),\n createHref: (href) => createHref(href),\n flush,\n destroy: () => {\n win.history.pushState = originalPushState\n win.history.replaceState = originalReplaceState\n win.removeEventListener(pushStateEvent, onPushPop)\n win.removeEventListener(popStateEvent, onPushPop)\n },\n onBlocked: (onUpdate) => {\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 // Notify subscribers\n onUpdate()\n }\n },\n })\n\n win.addEventListener(pushStateEvent, onPushPop)\n win.addEventListener(popStateEvent, onPushPop)\n\n win.history.pushState = function (...args: Array<any>) {\n const res = originalPushState.apply(win.history, args)\n if (!history._ignoreSubscribers) onPushPop()\n return res\n }\n\n win.history.replaceState = function (...args: Array<any>) {\n const res = originalReplaceState.apply(win.history, args)\n if (!history._ignoreSubscribers) onPushPop()\n return res\n }\n\n return history\n}\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 hashHref = win.location.hash.split('#').slice(1).join('#') ?? '/'\n return parseHref(hashHref, win.history.state)\n },\n createHref: (href) =>\n `${win.location.pathname}${win.location.search}#${href}`,\n })\n}\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 ?? entries.length - 1\n const states = entries.map(() => ({}) as HistoryState)\n\n const getLocation = () => parseHref(entries[index]!, states[index])\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 })\n}\n\nexport function parseHref(\n href: string,\n state: HistoryState | undefined,\n): HistoryLocation {\n const hashIndex = href.indexOf('#')\n const searchIndex = href.indexOf('?')\n\n return {\n href,\n pathname: href.substring(\n 0,\n hashIndex > 0\n ? searchIndex > 0\n ? Math.min(hashIndex, searchIndex)\n : hashIndex\n : searchIndex > 0\n ? searchIndex\n : href.length,\n ),\n hash: hashIndex > -1 ? href.substring(hashIndex) : '',\n search:\n searchIndex > -1\n ? href.slice(searchIndex, hashIndex === -1 ? undefined : hashIndex)\n : '',\n state: state || {},\n }\n}\n\n// Thanks co-pilot!\nfunction createRandomKey() {\n return (Math.random() + 1).toString(36).substring(7)\n}\n"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tanstack/history",
3
- "version": "1.61.0",
3
+ "version": "1.81.3",
4
4
  "description": "Modern and scalable routing for React applications",
5
5
  "author": "Tanner Linsley",
6
6
  "license": "MIT",
package/src/index.ts CHANGED
@@ -8,8 +8,8 @@ export interface NavigateOptions {
8
8
  export interface RouterHistory {
9
9
  location: HistoryLocation
10
10
  length: number
11
- subscribers: Set<() => void>
12
- subscribe: (cb: () => void) => () => void
11
+ subscribers: Set<(opts: { location: HistoryLocation }) => void>
12
+ subscribe: (cb: (opts: { location: HistoryLocation }) => void) => () => void
13
13
  push: (path: string, state?: any, navigateOpts?: NavigateOptions) => void
14
14
  replace: (path: string, state?: any, navigateOpts?: NavigateOptions) => void
15
15
  go: (index: number, navigateOpts?: NavigateOptions) => void
@@ -74,12 +74,12 @@ export function createHistory(opts: {
74
74
  onBlocked?: (onUpdate: () => void) => void
75
75
  }): RouterHistory {
76
76
  let location = opts.getLocation()
77
- const subscribers = new Set<() => void>()
77
+ const subscribers = new Set<(opts: { location: HistoryLocation }) => void>()
78
78
  let blockers: Array<BlockerFn> = []
79
79
 
80
80
  const notify = () => {
81
81
  location = opts.getLocation()
82
- subscribers.forEach((subscriber) => subscriber())
82
+ subscribers.forEach((subscriber) => subscriber({ location }))
83
83
  }
84
84
 
85
85
  const tryNavigation = async (
@@ -108,7 +108,7 @@ export function createHistory(opts: {
108
108
  return opts.getLength()
109
109
  },
110
110
  subscribers,
111
- subscribe: (cb: () => void) => {
111
+ subscribe: (cb: (opts: { location: HistoryLocation }) => void) => {
112
112
  subscribers.add(cb)
113
113
 
114
114
  return () => {
@@ -366,38 +366,34 @@ export function createMemoryHistory(
366
366
  ): RouterHistory {
367
367
  const entries = opts.initialEntries
368
368
  let index = opts.initialIndex ?? entries.length - 1
369
- let currentState = {
370
- key: createRandomKey(),
371
- } as HistoryState
369
+ const states = entries.map(() => ({}) as HistoryState)
372
370
 
373
- const getLocation = () => parseHref(entries[index]!, currentState)
371
+ const getLocation = () => parseHref(entries[index]!, states[index])
374
372
 
375
373
  return createHistory({
376
374
  getLocation,
377
375
  getLength: () => entries.length,
378
376
  pushState: (path, state) => {
379
- currentState = state
380
377
  // Removes all subsequent entries after the current index to start a new branch
381
378
  if (index < entries.length - 1) {
382
379
  entries.splice(index + 1)
380
+ states.splice(index + 1)
383
381
  }
382
+ states.push(state)
384
383
  entries.push(path)
385
384
  index = Math.max(entries.length - 1, 0)
386
385
  },
387
386
  replaceState: (path, state) => {
388
- currentState = state
387
+ states[index] = state
389
388
  entries[index] = path
390
389
  },
391
390
  back: () => {
392
- currentState = assignKey(currentState)
393
391
  index = Math.max(index - 1, 0)
394
392
  },
395
393
  forward: () => {
396
- currentState = assignKey(currentState)
397
394
  index = Math.min(index + 1, entries.length - 1)
398
395
  },
399
396
  go: (n) => {
400
- currentState = assignKey(currentState)
401
397
  index = Math.min(Math.max(index + n, 0), entries.length - 1)
402
398
  },
403
399
  createHref: (path) => path,