@tanstack/history 1.7.1 → 1.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,265 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ const pushStateEvent = "pushstate";
4
+ const popStateEvent = "popstate";
5
+ const beforeUnloadEvent = "beforeunload";
6
+ const beforeUnloadListener = (event) => {
7
+ event.preventDefault();
8
+ return event.returnValue = "";
9
+ };
10
+ const stopBlocking = () => {
11
+ removeEventListener(beforeUnloadEvent, beforeUnloadListener, {
12
+ capture: true
13
+ });
14
+ };
15
+ function createHistory(opts) {
16
+ let location = opts.getLocation();
17
+ let subscribers = /* @__PURE__ */ new Set();
18
+ let blockers = [];
19
+ const onUpdate = () => {
20
+ location = opts.getLocation();
21
+ subscribers.forEach((subscriber) => subscriber());
22
+ };
23
+ const tryNavigation = async (task) => {
24
+ var _a;
25
+ if (typeof document !== "undefined" && blockers.length) {
26
+ for (let blocker of blockers) {
27
+ const allowed = await blocker();
28
+ if (!allowed) {
29
+ (_a = opts.onBlocked) == null ? void 0 : _a.call(opts, onUpdate);
30
+ return;
31
+ }
32
+ }
33
+ }
34
+ task();
35
+ };
36
+ return {
37
+ get location() {
38
+ return location;
39
+ },
40
+ subscribe: (cb) => {
41
+ subscribers.add(cb);
42
+ return () => {
43
+ subscribers.delete(cb);
44
+ };
45
+ },
46
+ push: (path, state) => {
47
+ state = assignKey(state);
48
+ tryNavigation(() => {
49
+ opts.pushState(path, state);
50
+ onUpdate();
51
+ });
52
+ },
53
+ replace: (path, state) => {
54
+ state = assignKey(state);
55
+ tryNavigation(() => {
56
+ opts.replaceState(path, state);
57
+ onUpdate();
58
+ });
59
+ },
60
+ go: (index) => {
61
+ tryNavigation(() => {
62
+ opts.go(index);
63
+ });
64
+ },
65
+ back: () => {
66
+ tryNavigation(() => {
67
+ opts.back();
68
+ });
69
+ },
70
+ forward: () => {
71
+ tryNavigation(() => {
72
+ opts.forward();
73
+ });
74
+ },
75
+ createHref: (str) => opts.createHref(str),
76
+ block: (blocker) => {
77
+ blockers.push(blocker);
78
+ if (blockers.length === 1) {
79
+ addEventListener(beforeUnloadEvent, beforeUnloadListener, {
80
+ capture: true
81
+ });
82
+ }
83
+ return () => {
84
+ blockers = blockers.filter((b) => b !== blocker);
85
+ if (!blockers.length) {
86
+ stopBlocking();
87
+ }
88
+ };
89
+ },
90
+ flush: () => {
91
+ var _a;
92
+ return (_a = opts.flush) == null ? void 0 : _a.call(opts);
93
+ },
94
+ destroy: () => {
95
+ var _a;
96
+ return (_a = opts.destroy) == null ? void 0 : _a.call(opts);
97
+ },
98
+ notify: onUpdate
99
+ };
100
+ }
101
+ function assignKey(state) {
102
+ if (!state) {
103
+ state = {};
104
+ }
105
+ return {
106
+ ...state,
107
+ key: createRandomKey()
108
+ };
109
+ }
110
+ function createBrowserHistory(opts) {
111
+ const win = (opts == null ? void 0 : opts.window) ?? (typeof document !== "undefined" ? window : void 0);
112
+ const createHref = (opts == null ? void 0 : opts.createHref) ?? ((path) => path);
113
+ const parseLocation = (opts == null ? void 0 : opts.parseLocation) ?? (() => parseHref(
114
+ `${win.location.pathname}${win.location.search}${win.location.hash}`,
115
+ win.history.state
116
+ ));
117
+ let currentLocation = parseLocation();
118
+ let rollbackLocation;
119
+ const getLocation = () => currentLocation;
120
+ let next;
121
+ let tracking = true;
122
+ let scheduled;
123
+ const untrack = (fn) => {
124
+ tracking = false;
125
+ fn();
126
+ tracking = true;
127
+ };
128
+ const flush = () => {
129
+ untrack(() => {
130
+ if (!next)
131
+ return;
132
+ win.history[next.isPush ? "pushState" : "replaceState"](
133
+ next.state,
134
+ "",
135
+ next.href
136
+ );
137
+ next = void 0;
138
+ scheduled = void 0;
139
+ rollbackLocation = void 0;
140
+ });
141
+ };
142
+ const queueHistoryAction = (type, destHref, state) => {
143
+ const href = createHref(destHref);
144
+ if (!scheduled) {
145
+ rollbackLocation = currentLocation;
146
+ }
147
+ currentLocation = parseHref(destHref, state);
148
+ next = {
149
+ href,
150
+ state,
151
+ isPush: (next == null ? void 0 : next.isPush) || type === "push"
152
+ };
153
+ if (!scheduled) {
154
+ scheduled = Promise.resolve().then(() => flush());
155
+ }
156
+ };
157
+ const onPushPop = () => {
158
+ currentLocation = parseLocation();
159
+ history.notify();
160
+ };
161
+ var originalPushState = win.history.pushState;
162
+ var originalReplaceState = win.history.replaceState;
163
+ const history = createHistory({
164
+ getLocation,
165
+ pushState: (href, state) => queueHistoryAction("push", href, state),
166
+ replaceState: (href, state) => queueHistoryAction("replace", href, state),
167
+ back: () => win.history.back(),
168
+ forward: () => win.history.forward(),
169
+ go: (n) => win.history.go(n),
170
+ createHref: (href) => createHref(href),
171
+ flush,
172
+ destroy: () => {
173
+ win.history.pushState = originalPushState;
174
+ win.history.replaceState = originalReplaceState;
175
+ win.removeEventListener(pushStateEvent, onPushPop);
176
+ win.removeEventListener(popStateEvent, onPushPop);
177
+ },
178
+ onBlocked: (onUpdate) => {
179
+ if (rollbackLocation && currentLocation !== rollbackLocation) {
180
+ currentLocation = rollbackLocation;
181
+ onUpdate();
182
+ }
183
+ }
184
+ });
185
+ win.addEventListener(pushStateEvent, onPushPop);
186
+ win.addEventListener(popStateEvent, onPushPop);
187
+ win.history.pushState = function() {
188
+ let res = originalPushState.apply(win.history, arguments);
189
+ if (tracking)
190
+ history.notify();
191
+ return res;
192
+ };
193
+ win.history.replaceState = function() {
194
+ let res = originalReplaceState.apply(win.history, arguments);
195
+ if (tracking)
196
+ history.notify();
197
+ return res;
198
+ };
199
+ return history;
200
+ }
201
+ function createHashHistory(opts) {
202
+ const win = (opts == null ? void 0 : opts.window) ?? (typeof document !== "undefined" ? window : void 0);
203
+ return createBrowserHistory({
204
+ window: win,
205
+ parseLocation: () => {
206
+ const hashHref = win.location.hash.split("#").slice(1).join("#") ?? "/";
207
+ return parseHref(hashHref, win.history.state);
208
+ },
209
+ createHref: (href) => `${win.location.pathname}${win.location.search}#${href}`
210
+ });
211
+ }
212
+ function createMemoryHistory(opts = {
213
+ initialEntries: ["/"]
214
+ }) {
215
+ const entries = opts.initialEntries;
216
+ let index = opts.initialIndex ?? entries.length - 1;
217
+ let currentState = {
218
+ key: createRandomKey()
219
+ };
220
+ const getLocation = () => parseHref(entries[index], currentState);
221
+ return createHistory({
222
+ getLocation,
223
+ pushState: (path, state) => {
224
+ currentState = state;
225
+ entries.push(path);
226
+ index++;
227
+ },
228
+ replaceState: (path, state) => {
229
+ currentState = state;
230
+ entries[index] = path;
231
+ },
232
+ back: () => {
233
+ index--;
234
+ },
235
+ forward: () => {
236
+ index = Math.min(index + 1, entries.length - 1);
237
+ },
238
+ go: (n) => {
239
+ index = Math.min(Math.max(index + n, 0), entries.length - 1);
240
+ },
241
+ createHref: (path) => path
242
+ });
243
+ }
244
+ function parseHref(href, state) {
245
+ let hashIndex = href.indexOf("#");
246
+ let searchIndex = href.indexOf("?");
247
+ return {
248
+ href,
249
+ pathname: href.substring(
250
+ 0,
251
+ hashIndex > 0 ? searchIndex > 0 ? Math.min(hashIndex, searchIndex) : hashIndex : searchIndex > 0 ? searchIndex : href.length
252
+ ),
253
+ hash: hashIndex > -1 ? href.substring(hashIndex) : "",
254
+ search: searchIndex > -1 ? href.slice(searchIndex, hashIndex === -1 ? void 0 : hashIndex) : "",
255
+ state: state || {}
256
+ };
257
+ }
258
+ function createRandomKey() {
259
+ return (Math.random() + 1).toString(36).substring(7);
260
+ }
261
+ exports.createBrowserHistory = createBrowserHistory;
262
+ exports.createHashHistory = createHashHistory;
263
+ exports.createHistory = createHistory;
264
+ exports.createMemoryHistory = createMemoryHistory;
265
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +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 RouterHistory {\n location: HistoryLocation\n subscribe: (cb: () => void) => () => void\n push: (path: string, state?: any) => void\n replace: (path: string, state?: any) => void\n go: (index: number) => void\n back: () => void\n forward: () => void\n createHref: (href: string) => string\n block: (blocker: BlockerFn) => () => void\n flush: () => void\n destroy: () => void\n notify: () => void\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-ignore\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 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 let subscribers = new Set<() => void>()\n let blockers: BlockerFn[] = []\n\n const onUpdate = () => {\n location = opts.getLocation()\n subscribers.forEach((subscriber) => subscriber())\n }\n\n const tryNavigation = async (task: () => void) => {\n if (typeof document !== 'undefined' && blockers.length) {\n for (let blocker of blockers) {\n const allowed = await blocker()\n if (!allowed) {\n opts.onBlocked?.(onUpdate)\n return\n }\n }\n }\n\n task()\n }\n\n return {\n get location() {\n return location\n },\n subscribe: (cb: () => void) => {\n subscribers.add(cb)\n\n return () => {\n subscribers.delete(cb)\n }\n },\n push: (path: string, state: any) => {\n state = assignKey(state)\n tryNavigation(() => {\n opts.pushState(path, state)\n onUpdate()\n })\n },\n replace: (path: string, state: any) => {\n state = assignKey(state)\n tryNavigation(() => {\n opts.replaceState(path, state)\n onUpdate()\n })\n },\n go: (index) => {\n tryNavigation(() => {\n opts.go(index)\n })\n },\n back: () => {\n tryNavigation(() => {\n opts.back()\n })\n },\n forward: () => {\n tryNavigation(() => {\n opts.forward()\n })\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: onUpdate,\n }\n}\n\nfunction assignKey(state: HistoryState) {\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 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 // Because we are proactively updating the location\n // in memory before actually updating the browser history,\n // we need to track when we are doing this so we don't\n // notify subscribers twice on the last update.\n let tracking = true\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 is a wrapper to prevent any of the callback's\n // side effects from causing a subscriber notification\n const untrack = (fn: () => void) => {\n tracking = false\n fn()\n tracking = true\n }\n\n // This function flushes the next update to the browser history\n const flush = () => {\n // Do not notify subscribers about this push/replace call\n untrack(() => {\n if (!next) return\n win.history[next.isPush ? 'pushState' : 'replaceState'](\n next.state,\n '',\n next.href,\n )\n // Reset the nextIsPush flag and clear the scheduled update\n next = undefined\n scheduled = undefined\n rollbackLocation = undefined\n })\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 var originalPushState = win.history.pushState\n var originalReplaceState = win.history.replaceState\n\n const history = createHistory({\n getLocation,\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 () {\n let res = originalPushState.apply(win.history, arguments as any)\n if (tracking) history.notify()\n return res\n }\n\n win.history.replaceState = function () {\n let res = originalReplaceState.apply(win.history, arguments as any)\n if (tracking) history.notify()\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: 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\n pushState: (path, state) => {\n currentState = state\n entries.push(path)\n index++\n },\n replaceState: (path, state) => {\n currentState = state\n entries[index] = path\n },\n back: () => {\n index--\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\nfunction parseHref(href: string, state: HistoryState): HistoryLocation {\n let hashIndex = href.indexOf('#')\n let 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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"}
@@ -0,0 +1,69 @@
1
+ export interface RouterHistory {
2
+ location: HistoryLocation;
3
+ subscribe: (cb: () => void) => () => void;
4
+ push: (path: string, state?: any) => void;
5
+ replace: (path: string, state?: any) => void;
6
+ go: (index: number) => void;
7
+ back: () => void;
8
+ forward: () => void;
9
+ createHref: (href: string) => string;
10
+ block: (blocker: BlockerFn) => () => void;
11
+ flush: () => void;
12
+ destroy: () => void;
13
+ notify: () => void;
14
+ }
15
+ export interface HistoryLocation extends ParsedPath {
16
+ state: HistoryState;
17
+ }
18
+ export interface ParsedPath {
19
+ href: string;
20
+ pathname: string;
21
+ search: string;
22
+ hash: string;
23
+ }
24
+ export interface HistoryState {
25
+ key: string;
26
+ }
27
+ type ShouldAllowNavigation = any;
28
+ export type BlockerFn = () => Promise<ShouldAllowNavigation> | ShouldAllowNavigation;
29
+ export declare function createHistory(opts: {
30
+ getLocation: () => HistoryLocation;
31
+ pushState: (path: string, state: any) => void;
32
+ replaceState: (path: string, state: any) => void;
33
+ go: (n: number) => void;
34
+ back: () => void;
35
+ forward: () => void;
36
+ createHref: (path: string) => string;
37
+ flush?: () => void;
38
+ destroy?: () => void;
39
+ onBlocked?: (onUpdate: () => void) => void;
40
+ }): RouterHistory;
41
+ /**
42
+ * Creates a history object that can be used to interact with the browser's
43
+ * navigation. This is a lightweight API wrapping the browser's native methods.
44
+ * It is designed to work with TanStack Router, but could be used as a standalone API as well.
45
+ * IMPORTANT: This API implements history throttling via a microtask to prevent
46
+ * excessive calls to the history API. In some browsers, calling history.pushState or
47
+ * history.replaceState in quick succession can cause the browser to ignore subsequent
48
+ * calls. This API smooths out those differences and ensures that your application
49
+ * state will *eventually* match the browser state. In most cases, this is not a problem,
50
+ * but if you need to ensure that the browser state is up to date, you can use the
51
+ * `history.flush` method to immediately flush all pending state changes to the browser URL.
52
+ * @param opts
53
+ * @param opts.getHref A function that returns the current href (path + search + hash)
54
+ * @param opts.createHref A function that takes a path and returns a href (path + search + hash)
55
+ * @returns A history instance
56
+ */
57
+ export declare function createBrowserHistory(opts?: {
58
+ parseLocation?: () => HistoryLocation;
59
+ createHref?: (path: string) => string;
60
+ window?: any;
61
+ }): RouterHistory;
62
+ export declare function createHashHistory(opts?: {
63
+ window?: any;
64
+ }): RouterHistory;
65
+ export declare function createMemoryHistory(opts?: {
66
+ initialEntries: string[];
67
+ initialIndex?: number;
68
+ }): RouterHistory;
69
+ export {};
@@ -1,14 +1,4 @@
1
- /**
2
- * @tanstack/history/src/index.ts
3
- *
4
- * Copyright (c) TanStack
5
- *
6
- * This source code is licensed under the MIT license found in the
7
- * LICENSE.md file in the root directory of this source tree.
8
- *
9
- * @license MIT
10
- */
11
- interface RouterHistory {
1
+ export interface RouterHistory {
12
2
  location: HistoryLocation;
13
3
  subscribe: (cb: () => void) => () => void;
14
4
  push: (path: string, state?: any) => void;
@@ -22,21 +12,21 @@ interface RouterHistory {
22
12
  destroy: () => void;
23
13
  notify: () => void;
24
14
  }
25
- interface HistoryLocation extends ParsedPath {
15
+ export interface HistoryLocation extends ParsedPath {
26
16
  state: HistoryState;
27
17
  }
28
- interface ParsedPath {
18
+ export interface ParsedPath {
29
19
  href: string;
30
20
  pathname: string;
31
21
  search: string;
32
22
  hash: string;
33
23
  }
34
- interface HistoryState {
24
+ export interface HistoryState {
35
25
  key: string;
36
26
  }
37
27
  type ShouldAllowNavigation = any;
38
- type BlockerFn = () => Promise<ShouldAllowNavigation> | ShouldAllowNavigation;
39
- declare function createHistory(opts: {
28
+ export type BlockerFn = () => Promise<ShouldAllowNavigation> | ShouldAllowNavigation;
29
+ export declare function createHistory(opts: {
40
30
  getLocation: () => HistoryLocation;
41
31
  pushState: (path: string, state: any) => void;
42
32
  replaceState: (path: string, state: any) => void;
@@ -64,17 +54,16 @@ declare function createHistory(opts: {
64
54
  * @param opts.createHref A function that takes a path and returns a href (path + search + hash)
65
55
  * @returns A history instance
66
56
  */
67
- declare function createBrowserHistory(opts?: {
57
+ export declare function createBrowserHistory(opts?: {
68
58
  parseLocation?: () => HistoryLocation;
69
59
  createHref?: (path: string) => string;
70
60
  window?: any;
71
61
  }): RouterHistory;
72
- declare function createHashHistory(opts?: {
62
+ export declare function createHashHistory(opts?: {
73
63
  window?: any;
74
64
  }): RouterHistory;
75
- declare function createMemoryHistory(opts?: {
65
+ export declare function createMemoryHistory(opts?: {
76
66
  initialEntries: string[];
77
67
  initialIndex?: number;
78
68
  }): RouterHistory;
79
-
80
- export { type BlockerFn, type HistoryLocation, type HistoryState, type ParsedPath, type RouterHistory, createBrowserHistory, createHashHistory, createHistory, createMemoryHistory };
69
+ export {};
@@ -0,0 +1,265 @@
1
+ const pushStateEvent = "pushstate";
2
+ const popStateEvent = "popstate";
3
+ const beforeUnloadEvent = "beforeunload";
4
+ const beforeUnloadListener = (event) => {
5
+ event.preventDefault();
6
+ return event.returnValue = "";
7
+ };
8
+ const stopBlocking = () => {
9
+ removeEventListener(beforeUnloadEvent, beforeUnloadListener, {
10
+ capture: true
11
+ });
12
+ };
13
+ function createHistory(opts) {
14
+ let location = opts.getLocation();
15
+ let subscribers = /* @__PURE__ */ new Set();
16
+ let blockers = [];
17
+ const onUpdate = () => {
18
+ location = opts.getLocation();
19
+ subscribers.forEach((subscriber) => subscriber());
20
+ };
21
+ const tryNavigation = async (task) => {
22
+ var _a;
23
+ if (typeof document !== "undefined" && blockers.length) {
24
+ for (let blocker of blockers) {
25
+ const allowed = await blocker();
26
+ if (!allowed) {
27
+ (_a = opts.onBlocked) == null ? void 0 : _a.call(opts, onUpdate);
28
+ return;
29
+ }
30
+ }
31
+ }
32
+ task();
33
+ };
34
+ return {
35
+ get location() {
36
+ return location;
37
+ },
38
+ subscribe: (cb) => {
39
+ subscribers.add(cb);
40
+ return () => {
41
+ subscribers.delete(cb);
42
+ };
43
+ },
44
+ push: (path, state) => {
45
+ state = assignKey(state);
46
+ tryNavigation(() => {
47
+ opts.pushState(path, state);
48
+ onUpdate();
49
+ });
50
+ },
51
+ replace: (path, state) => {
52
+ state = assignKey(state);
53
+ tryNavigation(() => {
54
+ opts.replaceState(path, state);
55
+ onUpdate();
56
+ });
57
+ },
58
+ go: (index) => {
59
+ tryNavigation(() => {
60
+ opts.go(index);
61
+ });
62
+ },
63
+ back: () => {
64
+ tryNavigation(() => {
65
+ opts.back();
66
+ });
67
+ },
68
+ forward: () => {
69
+ tryNavigation(() => {
70
+ opts.forward();
71
+ });
72
+ },
73
+ createHref: (str) => opts.createHref(str),
74
+ block: (blocker) => {
75
+ blockers.push(blocker);
76
+ if (blockers.length === 1) {
77
+ addEventListener(beforeUnloadEvent, beforeUnloadListener, {
78
+ capture: true
79
+ });
80
+ }
81
+ return () => {
82
+ blockers = blockers.filter((b) => b !== blocker);
83
+ if (!blockers.length) {
84
+ stopBlocking();
85
+ }
86
+ };
87
+ },
88
+ flush: () => {
89
+ var _a;
90
+ return (_a = opts.flush) == null ? void 0 : _a.call(opts);
91
+ },
92
+ destroy: () => {
93
+ var _a;
94
+ return (_a = opts.destroy) == null ? void 0 : _a.call(opts);
95
+ },
96
+ notify: onUpdate
97
+ };
98
+ }
99
+ function assignKey(state) {
100
+ if (!state) {
101
+ state = {};
102
+ }
103
+ return {
104
+ ...state,
105
+ key: createRandomKey()
106
+ };
107
+ }
108
+ function createBrowserHistory(opts) {
109
+ const win = (opts == null ? void 0 : opts.window) ?? (typeof document !== "undefined" ? window : void 0);
110
+ const createHref = (opts == null ? void 0 : opts.createHref) ?? ((path) => path);
111
+ const parseLocation = (opts == null ? void 0 : opts.parseLocation) ?? (() => parseHref(
112
+ `${win.location.pathname}${win.location.search}${win.location.hash}`,
113
+ win.history.state
114
+ ));
115
+ let currentLocation = parseLocation();
116
+ let rollbackLocation;
117
+ const getLocation = () => currentLocation;
118
+ let next;
119
+ let tracking = true;
120
+ let scheduled;
121
+ const untrack = (fn) => {
122
+ tracking = false;
123
+ fn();
124
+ tracking = true;
125
+ };
126
+ const flush = () => {
127
+ untrack(() => {
128
+ if (!next)
129
+ return;
130
+ win.history[next.isPush ? "pushState" : "replaceState"](
131
+ next.state,
132
+ "",
133
+ next.href
134
+ );
135
+ next = void 0;
136
+ scheduled = void 0;
137
+ rollbackLocation = void 0;
138
+ });
139
+ };
140
+ const queueHistoryAction = (type, destHref, state) => {
141
+ const href = createHref(destHref);
142
+ if (!scheduled) {
143
+ rollbackLocation = currentLocation;
144
+ }
145
+ currentLocation = parseHref(destHref, state);
146
+ next = {
147
+ href,
148
+ state,
149
+ isPush: (next == null ? void 0 : next.isPush) || type === "push"
150
+ };
151
+ if (!scheduled) {
152
+ scheduled = Promise.resolve().then(() => flush());
153
+ }
154
+ };
155
+ const onPushPop = () => {
156
+ currentLocation = parseLocation();
157
+ history.notify();
158
+ };
159
+ var originalPushState = win.history.pushState;
160
+ var originalReplaceState = win.history.replaceState;
161
+ const history = createHistory({
162
+ getLocation,
163
+ pushState: (href, state) => queueHistoryAction("push", href, state),
164
+ replaceState: (href, state) => queueHistoryAction("replace", href, state),
165
+ back: () => win.history.back(),
166
+ forward: () => win.history.forward(),
167
+ go: (n) => win.history.go(n),
168
+ createHref: (href) => createHref(href),
169
+ flush,
170
+ destroy: () => {
171
+ win.history.pushState = originalPushState;
172
+ win.history.replaceState = originalReplaceState;
173
+ win.removeEventListener(pushStateEvent, onPushPop);
174
+ win.removeEventListener(popStateEvent, onPushPop);
175
+ },
176
+ onBlocked: (onUpdate) => {
177
+ if (rollbackLocation && currentLocation !== rollbackLocation) {
178
+ currentLocation = rollbackLocation;
179
+ onUpdate();
180
+ }
181
+ }
182
+ });
183
+ win.addEventListener(pushStateEvent, onPushPop);
184
+ win.addEventListener(popStateEvent, onPushPop);
185
+ win.history.pushState = function() {
186
+ let res = originalPushState.apply(win.history, arguments);
187
+ if (tracking)
188
+ history.notify();
189
+ return res;
190
+ };
191
+ win.history.replaceState = function() {
192
+ let res = originalReplaceState.apply(win.history, arguments);
193
+ if (tracking)
194
+ history.notify();
195
+ return res;
196
+ };
197
+ return history;
198
+ }
199
+ function createHashHistory(opts) {
200
+ const win = (opts == null ? void 0 : opts.window) ?? (typeof document !== "undefined" ? window : void 0);
201
+ return createBrowserHistory({
202
+ window: win,
203
+ parseLocation: () => {
204
+ const hashHref = win.location.hash.split("#").slice(1).join("#") ?? "/";
205
+ return parseHref(hashHref, win.history.state);
206
+ },
207
+ createHref: (href) => `${win.location.pathname}${win.location.search}#${href}`
208
+ });
209
+ }
210
+ function createMemoryHistory(opts = {
211
+ initialEntries: ["/"]
212
+ }) {
213
+ const entries = opts.initialEntries;
214
+ let index = opts.initialIndex ?? entries.length - 1;
215
+ let currentState = {
216
+ key: createRandomKey()
217
+ };
218
+ const getLocation = () => parseHref(entries[index], currentState);
219
+ return createHistory({
220
+ getLocation,
221
+ pushState: (path, state) => {
222
+ currentState = state;
223
+ entries.push(path);
224
+ index++;
225
+ },
226
+ replaceState: (path, state) => {
227
+ currentState = state;
228
+ entries[index] = path;
229
+ },
230
+ back: () => {
231
+ index--;
232
+ },
233
+ forward: () => {
234
+ index = Math.min(index + 1, entries.length - 1);
235
+ },
236
+ go: (n) => {
237
+ index = Math.min(Math.max(index + n, 0), entries.length - 1);
238
+ },
239
+ createHref: (path) => path
240
+ });
241
+ }
242
+ function parseHref(href, state) {
243
+ let hashIndex = href.indexOf("#");
244
+ let searchIndex = href.indexOf("?");
245
+ return {
246
+ href,
247
+ pathname: href.substring(
248
+ 0,
249
+ hashIndex > 0 ? searchIndex > 0 ? Math.min(hashIndex, searchIndex) : hashIndex : searchIndex > 0 ? searchIndex : href.length
250
+ ),
251
+ hash: hashIndex > -1 ? href.substring(hashIndex) : "",
252
+ search: searchIndex > -1 ? href.slice(searchIndex, hashIndex === -1 ? void 0 : hashIndex) : "",
253
+ state: state || {}
254
+ };
255
+ }
256
+ function createRandomKey() {
257
+ return (Math.random() + 1).toString(36).substring(7);
258
+ }
259
+ export {
260
+ createBrowserHistory,
261
+ createHashHistory,
262
+ createHistory,
263
+ createMemoryHistory
264
+ };
265
+ //# sourceMappingURL=index.js.map