@tanstack/history 1.121.20 → 1.121.34

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,419 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ const stateIndexKey = "__TSR_index";
4
+ const popStateEvent = "popstate";
5
+ const beforeUnloadEvent = "beforeunload";
6
+ function createHistory(opts) {
7
+ let location = opts.getLocation();
8
+ const subscribers = /* @__PURE__ */ new Set();
9
+ const notify = (action) => {
10
+ location = opts.getLocation();
11
+ subscribers.forEach((subscriber) => subscriber({ location, action }));
12
+ };
13
+ const handleIndexChange = (action) => {
14
+ if (opts.notifyOnIndexChange ?? true) notify(action);
15
+ else location = opts.getLocation();
16
+ };
17
+ const tryNavigation = async ({
18
+ task,
19
+ navigateOpts,
20
+ ...actionInfo
21
+ }) => {
22
+ var _a, _b;
23
+ const ignoreBlocker = (navigateOpts == null ? void 0 : navigateOpts.ignoreBlocker) ?? false;
24
+ if (ignoreBlocker) {
25
+ task();
26
+ return;
27
+ }
28
+ const blockers = ((_a = opts.getBlockers) == null ? void 0 : _a.call(opts)) ?? [];
29
+ const isPushOrReplace = actionInfo.type === "PUSH" || actionInfo.type === "REPLACE";
30
+ if (typeof document !== "undefined" && blockers.length && isPushOrReplace) {
31
+ for (const blocker of blockers) {
32
+ const nextLocation = parseHref(actionInfo.path, actionInfo.state);
33
+ const isBlocked = await blocker.blockerFn({
34
+ currentLocation: location,
35
+ nextLocation,
36
+ action: actionInfo.type
37
+ });
38
+ if (isBlocked) {
39
+ (_b = opts.onBlocked) == null ? void 0 : _b.call(opts);
40
+ return;
41
+ }
42
+ }
43
+ }
44
+ task();
45
+ };
46
+ return {
47
+ get location() {
48
+ return location;
49
+ },
50
+ get length() {
51
+ return opts.getLength();
52
+ },
53
+ subscribers,
54
+ subscribe: (cb) => {
55
+ subscribers.add(cb);
56
+ return () => {
57
+ subscribers.delete(cb);
58
+ };
59
+ },
60
+ push: (path, state, navigateOpts) => {
61
+ const currentIndex = location.state[stateIndexKey];
62
+ state = assignKeyAndIndex(currentIndex + 1, state);
63
+ tryNavigation({
64
+ task: () => {
65
+ opts.pushState(path, state);
66
+ notify({ type: "PUSH" });
67
+ },
68
+ navigateOpts,
69
+ type: "PUSH",
70
+ path,
71
+ state
72
+ });
73
+ },
74
+ replace: (path, state, navigateOpts) => {
75
+ const currentIndex = location.state[stateIndexKey];
76
+ state = assignKeyAndIndex(currentIndex, state);
77
+ tryNavigation({
78
+ task: () => {
79
+ opts.replaceState(path, state);
80
+ notify({ type: "REPLACE" });
81
+ },
82
+ navigateOpts,
83
+ type: "REPLACE",
84
+ path,
85
+ state
86
+ });
87
+ },
88
+ go: (index, navigateOpts) => {
89
+ tryNavigation({
90
+ task: () => {
91
+ opts.go(index);
92
+ handleIndexChange({ type: "GO", index });
93
+ },
94
+ navigateOpts,
95
+ type: "GO"
96
+ });
97
+ },
98
+ back: (navigateOpts) => {
99
+ tryNavigation({
100
+ task: () => {
101
+ opts.back((navigateOpts == null ? void 0 : navigateOpts.ignoreBlocker) ?? false);
102
+ handleIndexChange({ type: "BACK" });
103
+ },
104
+ navigateOpts,
105
+ type: "BACK"
106
+ });
107
+ },
108
+ forward: (navigateOpts) => {
109
+ tryNavigation({
110
+ task: () => {
111
+ opts.forward((navigateOpts == null ? void 0 : navigateOpts.ignoreBlocker) ?? false);
112
+ handleIndexChange({ type: "FORWARD" });
113
+ },
114
+ navigateOpts,
115
+ type: "FORWARD"
116
+ });
117
+ },
118
+ canGoBack: () => location.state[stateIndexKey] !== 0,
119
+ createHref: (str) => opts.createHref(str),
120
+ block: (blocker) => {
121
+ var _a;
122
+ if (!opts.setBlockers) return () => {
123
+ };
124
+ const blockers = ((_a = opts.getBlockers) == null ? void 0 : _a.call(opts)) ?? [];
125
+ opts.setBlockers([...blockers, blocker]);
126
+ return () => {
127
+ var _a2, _b;
128
+ const blockers2 = ((_a2 = opts.getBlockers) == null ? void 0 : _a2.call(opts)) ?? [];
129
+ (_b = opts.setBlockers) == null ? void 0 : _b.call(opts, blockers2.filter((b) => b !== blocker));
130
+ };
131
+ },
132
+ flush: () => {
133
+ var _a;
134
+ return (_a = opts.flush) == null ? void 0 : _a.call(opts);
135
+ },
136
+ destroy: () => {
137
+ var _a;
138
+ return (_a = opts.destroy) == null ? void 0 : _a.call(opts);
139
+ },
140
+ notify
141
+ };
142
+ }
143
+ function assignKeyAndIndex(index, state) {
144
+ if (!state) {
145
+ state = {};
146
+ }
147
+ const key = createRandomKey();
148
+ return {
149
+ ...state,
150
+ key,
151
+ // TODO: Remove in v2 - use __TSR_key instead
152
+ __TSR_key: key,
153
+ [stateIndexKey]: index
154
+ };
155
+ }
156
+ function createBrowserHistory(opts) {
157
+ var _a, _b;
158
+ const win = (opts == null ? void 0 : opts.window) ?? (typeof document !== "undefined" ? window : void 0);
159
+ const originalPushState = win.history.pushState;
160
+ const originalReplaceState = win.history.replaceState;
161
+ let blockers = [];
162
+ const _getBlockers = () => blockers;
163
+ const _setBlockers = (newBlockers) => blockers = newBlockers;
164
+ const createHref = (opts == null ? void 0 : opts.createHref) ?? ((path) => path);
165
+ const parseLocation = (opts == null ? void 0 : opts.parseLocation) ?? (() => parseHref(
166
+ `${win.location.pathname}${win.location.search}${win.location.hash}`,
167
+ win.history.state
168
+ ));
169
+ if (!((_a = win.history.state) == null ? void 0 : _a.__TSR_key) && !((_b = win.history.state) == null ? void 0 : _b.key)) {
170
+ const addedKey = createRandomKey();
171
+ win.history.replaceState(
172
+ {
173
+ [stateIndexKey]: 0,
174
+ key: addedKey,
175
+ // TODO: Remove in v2 - use __TSR_key instead
176
+ __TSR_key: addedKey
177
+ },
178
+ ""
179
+ );
180
+ }
181
+ let currentLocation = parseLocation();
182
+ let rollbackLocation;
183
+ let nextPopIsGo = false;
184
+ let ignoreNextPop = false;
185
+ let skipBlockerNextPop = false;
186
+ let ignoreNextBeforeUnload = false;
187
+ const getLocation = () => currentLocation;
188
+ let next;
189
+ let scheduled;
190
+ const flush = () => {
191
+ if (!next) {
192
+ return;
193
+ }
194
+ history._ignoreSubscribers = true;
195
+ (next.isPush ? win.history.pushState : win.history.replaceState)(
196
+ next.state,
197
+ "",
198
+ next.href
199
+ );
200
+ history._ignoreSubscribers = false;
201
+ next = void 0;
202
+ scheduled = void 0;
203
+ rollbackLocation = void 0;
204
+ };
205
+ const queueHistoryAction = (type, destHref, state) => {
206
+ const href = createHref(destHref);
207
+ if (!scheduled) {
208
+ rollbackLocation = currentLocation;
209
+ }
210
+ currentLocation = parseHref(destHref, state);
211
+ next = {
212
+ href,
213
+ state,
214
+ isPush: (next == null ? void 0 : next.isPush) || type === "push"
215
+ };
216
+ if (!scheduled) {
217
+ scheduled = Promise.resolve().then(() => flush());
218
+ }
219
+ };
220
+ const onPushPop = (type) => {
221
+ currentLocation = parseLocation();
222
+ history.notify({ type });
223
+ };
224
+ const onPushPopEvent = async () => {
225
+ if (ignoreNextPop) {
226
+ ignoreNextPop = false;
227
+ return;
228
+ }
229
+ const nextLocation = parseLocation();
230
+ const delta = nextLocation.state[stateIndexKey] - currentLocation.state[stateIndexKey];
231
+ const isForward = delta === 1;
232
+ const isBack = delta === -1;
233
+ const isGo = !isForward && !isBack || nextPopIsGo;
234
+ nextPopIsGo = false;
235
+ const action = isGo ? "GO" : isBack ? "BACK" : "FORWARD";
236
+ const notify = isGo ? {
237
+ type: "GO",
238
+ index: delta
239
+ } : {
240
+ type: isBack ? "BACK" : "FORWARD"
241
+ };
242
+ if (skipBlockerNextPop) {
243
+ skipBlockerNextPop = false;
244
+ } else {
245
+ const blockers2 = _getBlockers();
246
+ if (typeof document !== "undefined" && blockers2.length) {
247
+ for (const blocker of blockers2) {
248
+ const isBlocked = await blocker.blockerFn({
249
+ currentLocation,
250
+ nextLocation,
251
+ action
252
+ });
253
+ if (isBlocked) {
254
+ ignoreNextPop = true;
255
+ win.history.go(1);
256
+ history.notify(notify);
257
+ return;
258
+ }
259
+ }
260
+ }
261
+ }
262
+ currentLocation = parseLocation();
263
+ history.notify(notify);
264
+ };
265
+ const onBeforeUnload = (e) => {
266
+ if (ignoreNextBeforeUnload) {
267
+ ignoreNextBeforeUnload = false;
268
+ return;
269
+ }
270
+ let shouldBlock = false;
271
+ const blockers2 = _getBlockers();
272
+ if (typeof document !== "undefined" && blockers2.length) {
273
+ for (const blocker of blockers2) {
274
+ const shouldHaveBeforeUnload = blocker.enableBeforeUnload ?? true;
275
+ if (shouldHaveBeforeUnload === true) {
276
+ shouldBlock = true;
277
+ break;
278
+ }
279
+ if (typeof shouldHaveBeforeUnload === "function" && shouldHaveBeforeUnload() === true) {
280
+ shouldBlock = true;
281
+ break;
282
+ }
283
+ }
284
+ }
285
+ if (shouldBlock) {
286
+ e.preventDefault();
287
+ return e.returnValue = "";
288
+ }
289
+ return;
290
+ };
291
+ const history = createHistory({
292
+ getLocation,
293
+ getLength: () => win.history.length,
294
+ pushState: (href, state) => queueHistoryAction("push", href, state),
295
+ replaceState: (href, state) => queueHistoryAction("replace", href, state),
296
+ back: (ignoreBlocker) => {
297
+ if (ignoreBlocker) skipBlockerNextPop = true;
298
+ ignoreNextBeforeUnload = true;
299
+ return win.history.back();
300
+ },
301
+ forward: (ignoreBlocker) => {
302
+ if (ignoreBlocker) skipBlockerNextPop = true;
303
+ ignoreNextBeforeUnload = true;
304
+ win.history.forward();
305
+ },
306
+ go: (n) => {
307
+ nextPopIsGo = true;
308
+ win.history.go(n);
309
+ },
310
+ createHref: (href) => createHref(href),
311
+ flush,
312
+ destroy: () => {
313
+ win.history.pushState = originalPushState;
314
+ win.history.replaceState = originalReplaceState;
315
+ win.removeEventListener(beforeUnloadEvent, onBeforeUnload, {
316
+ capture: true
317
+ });
318
+ win.removeEventListener(popStateEvent, onPushPopEvent);
319
+ },
320
+ onBlocked: () => {
321
+ if (rollbackLocation && currentLocation !== rollbackLocation) {
322
+ currentLocation = rollbackLocation;
323
+ }
324
+ },
325
+ getBlockers: _getBlockers,
326
+ setBlockers: _setBlockers,
327
+ notifyOnIndexChange: false
328
+ });
329
+ win.addEventListener(beforeUnloadEvent, onBeforeUnload, { capture: true });
330
+ win.addEventListener(popStateEvent, onPushPopEvent);
331
+ win.history.pushState = function(...args) {
332
+ const res = originalPushState.apply(win.history, args);
333
+ if (!history._ignoreSubscribers) onPushPop("PUSH");
334
+ return res;
335
+ };
336
+ win.history.replaceState = function(...args) {
337
+ const res = originalReplaceState.apply(win.history, args);
338
+ if (!history._ignoreSubscribers) onPushPop("REPLACE");
339
+ return res;
340
+ };
341
+ return history;
342
+ }
343
+ function createHashHistory(opts) {
344
+ const win = (opts == null ? void 0 : opts.window) ?? (typeof document !== "undefined" ? window : void 0);
345
+ return createBrowserHistory({
346
+ window: win,
347
+ parseLocation: () => {
348
+ const hashSplit = win.location.hash.split("#").slice(1);
349
+ const pathPart = hashSplit[0] ?? "/";
350
+ const searchPart = win.location.search;
351
+ const hashEntries = hashSplit.slice(1);
352
+ const hashPart = hashEntries.length === 0 ? "" : `#${hashEntries.join("#")}`;
353
+ const hashHref = `${pathPart}${searchPart}${hashPart}`;
354
+ return parseHref(hashHref, win.history.state);
355
+ },
356
+ createHref: (href) => `${win.location.pathname}${win.location.search}#${href}`
357
+ });
358
+ }
359
+ function createMemoryHistory(opts = {
360
+ initialEntries: ["/"]
361
+ }) {
362
+ const entries = opts.initialEntries;
363
+ let index = opts.initialIndex ? Math.min(Math.max(opts.initialIndex, 0), entries.length - 1) : entries.length - 1;
364
+ const states = entries.map(
365
+ (_entry, index2) => assignKeyAndIndex(index2, void 0)
366
+ );
367
+ const getLocation = () => parseHref(entries[index], states[index]);
368
+ return createHistory({
369
+ getLocation,
370
+ getLength: () => entries.length,
371
+ pushState: (path, state) => {
372
+ if (index < entries.length - 1) {
373
+ entries.splice(index + 1);
374
+ states.splice(index + 1);
375
+ }
376
+ states.push(state);
377
+ entries.push(path);
378
+ index = Math.max(entries.length - 1, 0);
379
+ },
380
+ replaceState: (path, state) => {
381
+ states[index] = state;
382
+ entries[index] = path;
383
+ },
384
+ back: () => {
385
+ index = Math.max(index - 1, 0);
386
+ },
387
+ forward: () => {
388
+ index = Math.min(index + 1, entries.length - 1);
389
+ },
390
+ go: (n) => {
391
+ index = Math.min(Math.max(index + n, 0), entries.length - 1);
392
+ },
393
+ createHref: (path) => path
394
+ });
395
+ }
396
+ function parseHref(href, state) {
397
+ const hashIndex = href.indexOf("#");
398
+ const searchIndex = href.indexOf("?");
399
+ const addedKey = createRandomKey();
400
+ return {
401
+ href,
402
+ pathname: href.substring(
403
+ 0,
404
+ hashIndex > 0 ? searchIndex > 0 ? Math.min(hashIndex, searchIndex) : hashIndex : searchIndex > 0 ? searchIndex : href.length
405
+ ),
406
+ hash: hashIndex > -1 ? href.substring(hashIndex) : "",
407
+ search: searchIndex > -1 ? href.slice(searchIndex, hashIndex === -1 ? void 0 : hashIndex) : "",
408
+ state: state || { [stateIndexKey]: 0, key: addedKey, __TSR_key: addedKey }
409
+ };
410
+ }
411
+ function createRandomKey() {
412
+ return (Math.random() + 1).toString(36).substring(7);
413
+ }
414
+ exports.createBrowserHistory = createBrowserHistory;
415
+ exports.createHashHistory = createHashHistory;
416
+ exports.createHistory = createHistory;
417
+ exports.createMemoryHistory = createMemoryHistory;
418
+ exports.parseHref = parseHref;
419
+ //# 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 NavigateOptions {\n ignoreBlocker?: boolean\n}\n\ntype SubscriberHistoryAction =\n | {\n type: Exclude<HistoryAction, 'GO'>\n }\n | {\n type: 'GO'\n index: number\n }\n\ntype SubscriberArgs = {\n location: HistoryLocation\n action: SubscriberHistoryAction\n}\n\nexport interface RouterHistory {\n location: HistoryLocation\n length: number\n subscribers: Set<(opts: SubscriberArgs) => void>\n subscribe: (cb: (opts: SubscriberArgs) => void) => () => void\n push: (path: string, state?: any, navigateOpts?: NavigateOptions) => void\n replace: (path: string, state?: any, navigateOpts?: NavigateOptions) => void\n go: (index: number, navigateOpts?: NavigateOptions) => void\n back: (navigateOpts?: NavigateOptions) => void\n forward: (navigateOpts?: NavigateOptions) => void\n canGoBack: () => boolean\n createHref: (href: string) => string\n block: (blocker: NavigationBlocker) => () => void\n flush: () => void\n destroy: () => void\n notify: (action: SubscriberHistoryAction) => void\n _ignoreSubscribers?: boolean\n}\n\nexport interface HistoryLocation extends ParsedPath {\n state: ParsedHistoryState\n}\n\nexport interface ParsedPath {\n href: string\n pathname: string\n search: string\n hash: string\n}\n\nexport interface HistoryState {}\n\nexport type ParsedHistoryState = HistoryState & {\n key?: string // TODO: Remove in v2 - use __TSR_key instead\n __TSR_key?: string\n __TSR_index: number\n}\n\ntype ShouldAllowNavigation = any\n\nexport type HistoryAction = 'PUSH' | 'REPLACE' | 'FORWARD' | 'BACK' | 'GO'\n\nexport type BlockerFnArgs = {\n currentLocation: HistoryLocation\n nextLocation: HistoryLocation\n action: HistoryAction\n}\n\nexport type BlockerFn = (\n args: BlockerFnArgs,\n) => Promise<ShouldAllowNavigation> | ShouldAllowNavigation\n\nexport type NavigationBlocker = {\n blockerFn: BlockerFn\n enableBeforeUnload?: (() => boolean) | boolean\n}\n\ntype TryNavigateArgs = {\n task: () => void\n type: 'PUSH' | 'REPLACE' | 'BACK' | 'FORWARD' | 'GO'\n navigateOpts?: NavigateOptions\n} & (\n | {\n type: 'PUSH' | 'REPLACE'\n path: string\n state: any\n }\n | {\n type: 'BACK' | 'FORWARD' | 'GO'\n }\n)\n\nconst stateIndexKey = '__TSR_index'\nconst popStateEvent = 'popstate'\nconst beforeUnloadEvent = 'beforeunload'\n\nexport function createHistory(opts: {\n getLocation: () => HistoryLocation\n getLength: () => number\n pushState: (path: string, state: any) => void\n replaceState: (path: string, state: any) => void\n go: (n: number) => void\n back: (ignoreBlocker: boolean) => void\n forward: (ignoreBlocker: boolean) => void\n createHref: (path: string) => string\n flush?: () => void\n destroy?: () => void\n onBlocked?: () => void\n getBlockers?: () => Array<NavigationBlocker>\n setBlockers?: (blockers: Array<NavigationBlocker>) => void\n // Avoid notifying on forward/back/go, used for browser history as we already get notified by the popstate event\n notifyOnIndexChange?: boolean\n}): RouterHistory {\n let location = opts.getLocation()\n const subscribers = new Set<(opts: SubscriberArgs) => void>()\n\n const notify = (action: SubscriberHistoryAction) => {\n location = opts.getLocation()\n subscribers.forEach((subscriber) => subscriber({ location, action }))\n }\n\n const handleIndexChange = (action: SubscriberHistoryAction) => {\n if (opts.notifyOnIndexChange ?? true) notify(action)\n else location = opts.getLocation()\n }\n\n const tryNavigation = async ({\n task,\n navigateOpts,\n ...actionInfo\n }: TryNavigateArgs) => {\n const ignoreBlocker = navigateOpts?.ignoreBlocker ?? false\n if (ignoreBlocker) {\n task()\n return\n }\n\n const blockers = opts.getBlockers?.() ?? []\n const isPushOrReplace =\n actionInfo.type === 'PUSH' || actionInfo.type === 'REPLACE'\n if (typeof document !== 'undefined' && blockers.length && isPushOrReplace) {\n for (const blocker of blockers) {\n const nextLocation = parseHref(actionInfo.path, actionInfo.state)\n const isBlocked = await blocker.blockerFn({\n currentLocation: location,\n nextLocation,\n action: actionInfo.type,\n })\n if (isBlocked) {\n opts.onBlocked?.()\n return\n }\n }\n }\n\n task()\n }\n\n return {\n get location() {\n return location\n },\n get length() {\n return opts.getLength()\n },\n subscribers,\n subscribe: (cb: (opts: SubscriberArgs) => void) => {\n subscribers.add(cb)\n\n return () => {\n subscribers.delete(cb)\n }\n },\n push: (path, state, navigateOpts) => {\n const currentIndex = location.state[stateIndexKey]\n state = assignKeyAndIndex(currentIndex + 1, state)\n tryNavigation({\n task: () => {\n opts.pushState(path, state)\n notify({ type: 'PUSH' })\n },\n navigateOpts,\n type: 'PUSH',\n path,\n state,\n })\n },\n replace: (path, state, navigateOpts) => {\n const currentIndex = location.state[stateIndexKey]\n state = assignKeyAndIndex(currentIndex, state)\n tryNavigation({\n task: () => {\n opts.replaceState(path, state)\n notify({ type: 'REPLACE' })\n },\n navigateOpts,\n type: 'REPLACE',\n path,\n state,\n })\n },\n go: (index, navigateOpts) => {\n tryNavigation({\n task: () => {\n opts.go(index)\n handleIndexChange({ type: 'GO', index })\n },\n navigateOpts,\n type: 'GO',\n })\n },\n back: (navigateOpts) => {\n tryNavigation({\n task: () => {\n opts.back(navigateOpts?.ignoreBlocker ?? false)\n handleIndexChange({ type: 'BACK' })\n },\n navigateOpts,\n type: 'BACK',\n })\n },\n forward: (navigateOpts) => {\n tryNavigation({\n task: () => {\n opts.forward(navigateOpts?.ignoreBlocker ?? false)\n handleIndexChange({ type: 'FORWARD' })\n },\n navigateOpts,\n type: 'FORWARD',\n })\n },\n canGoBack: () => location.state[stateIndexKey] !== 0,\n createHref: (str) => opts.createHref(str),\n block: (blocker) => {\n if (!opts.setBlockers) return () => {}\n const blockers = opts.getBlockers?.() ?? []\n opts.setBlockers([...blockers, blocker])\n\n return () => {\n const blockers = opts.getBlockers?.() ?? []\n opts.setBlockers?.(blockers.filter((b) => b !== blocker))\n }\n },\n flush: () => opts.flush?.(),\n destroy: () => opts.destroy?.(),\n notify,\n }\n}\n\nfunction assignKeyAndIndex(index: number, state: HistoryState | undefined) {\n if (!state) {\n state = {} as HistoryState\n }\n const key = createRandomKey()\n return {\n ...state,\n key, // TODO: Remove in v2 - use __TSR_key instead\n __TSR_key: key,\n [stateIndexKey]: index,\n } as ParsedHistoryState\n}\n\n/**\n * Creates a history object that can be used to interact with the browser's\n * navigation. This is a lightweight API wrapping the browser's native methods.\n * It is designed to work with TanStack Router, but could be used as a standalone API as well.\n * IMPORTANT: This API implements history throttling via a microtask to prevent\n * excessive calls to the history API. In some browsers, calling history.pushState or\n * history.replaceState in quick succession can cause the browser to ignore subsequent\n * calls. This API smooths out those differences and ensures that your application\n * state will *eventually* match the browser state. In most cases, this is not a problem,\n * but if you need to ensure that the browser state is up to date, you can use the\n * `history.flush` method to immediately flush all pending state changes to the browser URL.\n * @param opts\n * @param opts.getHref A function that returns the current href (path + search + hash)\n * @param opts.createHref A function that takes a path and returns a href (path + search + hash)\n * @returns A history instance\n */\nexport function createBrowserHistory(opts?: {\n parseLocation?: () => HistoryLocation\n createHref?: (path: string) => string\n window?: any\n}): RouterHistory {\n const win =\n opts?.window ??\n (typeof document !== 'undefined' ? window : (undefined as any))\n\n const originalPushState = win.history.pushState\n const originalReplaceState = win.history.replaceState\n\n let blockers: Array<NavigationBlocker> = []\n const _getBlockers = () => blockers\n const _setBlockers = (newBlockers: Array<NavigationBlocker>) =>\n (blockers = newBlockers)\n\n const createHref = opts?.createHref ?? ((path) => path)\n const parseLocation =\n opts?.parseLocation ??\n (() =>\n parseHref(\n `${win.location.pathname}${win.location.search}${win.location.hash}`,\n win.history.state,\n ))\n\n // Ensure there is always a key to start\n if (!win.history.state?.__TSR_key && !win.history.state?.key) {\n const addedKey = createRandomKey()\n win.history.replaceState(\n {\n [stateIndexKey]: 0,\n key: addedKey, // TODO: Remove in v2 - use __TSR_key instead\n __TSR_key: addedKey,\n },\n '',\n )\n }\n\n let currentLocation = parseLocation()\n let rollbackLocation: HistoryLocation | undefined\n\n let nextPopIsGo = false\n let ignoreNextPop = false\n let skipBlockerNextPop = false\n let ignoreNextBeforeUnload = false\n\n const getLocation = () => currentLocation\n\n let next:\n | undefined\n | {\n // This is the latest location that we were attempting to push/replace\n href: string\n // This is the latest state that we were attempting to push/replace\n state: any\n // This is the latest type that we were attempting to push/replace\n isPush: boolean\n }\n\n // We need to track the current scheduled update to prevent\n // multiple updates from being scheduled at the same time.\n let scheduled: Promise<void> | undefined\n\n // This function flushes the next update to the browser history\n const flush = () => {\n if (!next) {\n return\n }\n\n // We need to ignore any updates to the subscribers while we update the browser history\n history._ignoreSubscribers = true\n\n // Update the browser history\n ;(next.isPush ? win.history.pushState : win.history.replaceState)(\n next.state,\n '',\n next.href,\n )\n\n // Stop ignoring subscriber updates\n history._ignoreSubscribers = false\n\n // Reset the nextIsPush flag and clear the scheduled update\n next = undefined\n scheduled = undefined\n rollbackLocation = undefined\n }\n\n // This function queues up a call to update the browser history\n const queueHistoryAction = (\n type: 'push' | 'replace',\n destHref: string,\n state: any,\n ) => {\n const href = createHref(destHref)\n\n if (!scheduled) {\n rollbackLocation = currentLocation\n }\n\n // Update the location in memory\n currentLocation = parseHref(destHref, state)\n\n // Keep track of the next location we need to flush to the URL\n next = {\n href,\n state,\n isPush: next?.isPush || type === 'push',\n }\n\n if (!scheduled) {\n // Schedule an update to the browser history\n scheduled = Promise.resolve().then(() => flush())\n }\n }\n\n // NOTE: this function can probably be removed\n const onPushPop = (type: 'PUSH' | 'REPLACE') => {\n currentLocation = parseLocation()\n history.notify({ type })\n }\n\n const onPushPopEvent = async () => {\n if (ignoreNextPop) {\n ignoreNextPop = false\n return\n }\n\n const nextLocation = parseLocation()\n const delta =\n nextLocation.state[stateIndexKey] - currentLocation.state[stateIndexKey]\n const isForward = delta === 1\n const isBack = delta === -1\n const isGo = (!isForward && !isBack) || nextPopIsGo\n nextPopIsGo = false\n\n const action = isGo ? 'GO' : isBack ? 'BACK' : 'FORWARD'\n const notify: SubscriberHistoryAction = isGo\n ? {\n type: 'GO',\n index: delta,\n }\n : {\n type: isBack ? 'BACK' : 'FORWARD',\n }\n\n if (skipBlockerNextPop) {\n skipBlockerNextPop = false\n } else {\n const blockers = _getBlockers()\n if (typeof document !== 'undefined' && blockers.length) {\n for (const blocker of blockers) {\n const isBlocked = await blocker.blockerFn({\n currentLocation,\n nextLocation,\n action,\n })\n if (isBlocked) {\n ignoreNextPop = true\n win.history.go(1)\n history.notify(notify)\n return\n }\n }\n }\n }\n\n currentLocation = parseLocation()\n history.notify(notify)\n }\n\n const onBeforeUnload = (e: BeforeUnloadEvent) => {\n if (ignoreNextBeforeUnload) {\n ignoreNextBeforeUnload = false\n return\n }\n\n let shouldBlock = false\n\n // If one blocker has a non-disabled beforeUnload, we should block\n const blockers = _getBlockers()\n if (typeof document !== 'undefined' && blockers.length) {\n for (const blocker of blockers) {\n const shouldHaveBeforeUnload = blocker.enableBeforeUnload ?? true\n if (shouldHaveBeforeUnload === true) {\n shouldBlock = true\n break\n }\n\n if (\n typeof shouldHaveBeforeUnload === 'function' &&\n shouldHaveBeforeUnload() === true\n ) {\n shouldBlock = true\n break\n }\n }\n }\n\n if (shouldBlock) {\n e.preventDefault()\n return (e.returnValue = '')\n }\n return\n }\n\n const history = createHistory({\n getLocation,\n getLength: () => win.history.length,\n pushState: (href, state) => queueHistoryAction('push', href, state),\n replaceState: (href, state) => queueHistoryAction('replace', href, state),\n back: (ignoreBlocker) => {\n if (ignoreBlocker) skipBlockerNextPop = true\n ignoreNextBeforeUnload = true\n return win.history.back()\n },\n forward: (ignoreBlocker) => {\n if (ignoreBlocker) skipBlockerNextPop = true\n ignoreNextBeforeUnload = true\n win.history.forward()\n },\n go: (n) => {\n nextPopIsGo = true\n win.history.go(n)\n },\n createHref: (href) => createHref(href),\n flush,\n destroy: () => {\n win.history.pushState = originalPushState\n win.history.replaceState = originalReplaceState\n win.removeEventListener(beforeUnloadEvent, onBeforeUnload, {\n capture: true,\n })\n win.removeEventListener(popStateEvent, onPushPopEvent)\n },\n onBlocked: () => {\n // If a navigation is blocked, we need to rollback the location\n // that we optimistically updated in memory.\n if (rollbackLocation && currentLocation !== rollbackLocation) {\n currentLocation = rollbackLocation\n }\n },\n getBlockers: _getBlockers,\n setBlockers: _setBlockers,\n notifyOnIndexChange: false,\n })\n\n win.addEventListener(beforeUnloadEvent, onBeforeUnload, { capture: true })\n win.addEventListener(popStateEvent, onPushPopEvent)\n\n win.history.pushState = function (...args: Array<any>) {\n const res = originalPushState.apply(win.history, args as any)\n if (!history._ignoreSubscribers) onPushPop('PUSH')\n return res\n }\n\n win.history.replaceState = function (...args: Array<any>) {\n const res = originalReplaceState.apply(win.history, args as any)\n if (!history._ignoreSubscribers) onPushPop('REPLACE')\n return res\n }\n\n return history\n}\n\nexport function createHashHistory(opts?: { window?: any }): RouterHistory {\n const win =\n opts?.window ??\n (typeof document !== 'undefined' ? window : (undefined as any))\n return createBrowserHistory({\n window: win,\n parseLocation: () => {\n const hashSplit = win.location.hash.split('#').slice(1)\n const pathPart = hashSplit[0] ?? '/'\n const searchPart = win.location.search\n const hashEntries = hashSplit.slice(1)\n const hashPart =\n hashEntries.length === 0 ? '' : `#${hashEntries.join('#')}`\n const hashHref = `${pathPart}${searchPart}${hashPart}`\n return parseHref(hashHref, win.history.state)\n },\n createHref: (href) =>\n `${win.location.pathname}${win.location.search}#${href}`,\n })\n}\n\nexport function createMemoryHistory(\n opts: {\n initialEntries: Array<string>\n initialIndex?: number\n } = {\n initialEntries: ['/'],\n },\n): RouterHistory {\n const entries = opts.initialEntries\n let index = opts.initialIndex\n ? Math.min(Math.max(opts.initialIndex, 0), entries.length - 1)\n : entries.length - 1\n const states = entries.map((_entry, index) =>\n assignKeyAndIndex(index, undefined),\n )\n\n const getLocation = () => parseHref(entries[index]!, states[index])\n\n 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: ParsedHistoryState | undefined,\n): HistoryLocation {\n const hashIndex = href.indexOf('#')\n const searchIndex = href.indexOf('?')\n\n const addedKey = createRandomKey()\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 || { [stateIndexKey]: 0, key: addedKey, __TSR_key: addedKey },\n }\n}\n\n// Thanks co-pilot!\nfunction createRandomKey() {\n return (Math.random() + 1).toString(36).substring(7)\n}\n"],"names":["blockers","_a","index"],"mappings":"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"}
@@ -0,0 +1,105 @@
1
+ export interface NavigateOptions {
2
+ ignoreBlocker?: boolean;
3
+ }
4
+ type SubscriberHistoryAction = {
5
+ type: Exclude<HistoryAction, 'GO'>;
6
+ } | {
7
+ type: 'GO';
8
+ index: number;
9
+ };
10
+ type SubscriberArgs = {
11
+ location: HistoryLocation;
12
+ action: SubscriberHistoryAction;
13
+ };
14
+ export interface RouterHistory {
15
+ location: HistoryLocation;
16
+ length: number;
17
+ subscribers: Set<(opts: SubscriberArgs) => void>;
18
+ subscribe: (cb: (opts: SubscriberArgs) => void) => () => void;
19
+ push: (path: string, state?: any, navigateOpts?: NavigateOptions) => void;
20
+ replace: (path: string, state?: any, navigateOpts?: NavigateOptions) => void;
21
+ go: (index: number, navigateOpts?: NavigateOptions) => void;
22
+ back: (navigateOpts?: NavigateOptions) => void;
23
+ forward: (navigateOpts?: NavigateOptions) => void;
24
+ canGoBack: () => boolean;
25
+ createHref: (href: string) => string;
26
+ block: (blocker: NavigationBlocker) => () => void;
27
+ flush: () => void;
28
+ destroy: () => void;
29
+ notify: (action: SubscriberHistoryAction) => void;
30
+ _ignoreSubscribers?: boolean;
31
+ }
32
+ export interface HistoryLocation extends ParsedPath {
33
+ state: ParsedHistoryState;
34
+ }
35
+ export interface ParsedPath {
36
+ href: string;
37
+ pathname: string;
38
+ search: string;
39
+ hash: string;
40
+ }
41
+ export interface HistoryState {
42
+ }
43
+ export type ParsedHistoryState = HistoryState & {
44
+ key?: string;
45
+ __TSR_key?: string;
46
+ __TSR_index: number;
47
+ };
48
+ type ShouldAllowNavigation = any;
49
+ export type HistoryAction = 'PUSH' | 'REPLACE' | 'FORWARD' | 'BACK' | 'GO';
50
+ export type BlockerFnArgs = {
51
+ currentLocation: HistoryLocation;
52
+ nextLocation: HistoryLocation;
53
+ action: HistoryAction;
54
+ };
55
+ export type BlockerFn = (args: BlockerFnArgs) => Promise<ShouldAllowNavigation> | ShouldAllowNavigation;
56
+ export type NavigationBlocker = {
57
+ blockerFn: BlockerFn;
58
+ enableBeforeUnload?: (() => boolean) | boolean;
59
+ };
60
+ export declare function createHistory(opts: {
61
+ getLocation: () => HistoryLocation;
62
+ getLength: () => number;
63
+ pushState: (path: string, state: any) => void;
64
+ replaceState: (path: string, state: any) => void;
65
+ go: (n: number) => void;
66
+ back: (ignoreBlocker: boolean) => void;
67
+ forward: (ignoreBlocker: boolean) => void;
68
+ createHref: (path: string) => string;
69
+ flush?: () => void;
70
+ destroy?: () => void;
71
+ onBlocked?: () => void;
72
+ getBlockers?: () => Array<NavigationBlocker>;
73
+ setBlockers?: (blockers: Array<NavigationBlocker>) => void;
74
+ notifyOnIndexChange?: boolean;
75
+ }): RouterHistory;
76
+ /**
77
+ * Creates a history object that can be used to interact with the browser's
78
+ * navigation. This is a lightweight API wrapping the browser's native methods.
79
+ * It is designed to work with TanStack Router, but could be used as a standalone API as well.
80
+ * IMPORTANT: This API implements history throttling via a microtask to prevent
81
+ * excessive calls to the history API. In some browsers, calling history.pushState or
82
+ * history.replaceState in quick succession can cause the browser to ignore subsequent
83
+ * calls. This API smooths out those differences and ensures that your application
84
+ * state will *eventually* match the browser state. In most cases, this is not a problem,
85
+ * but if you need to ensure that the browser state is up to date, you can use the
86
+ * `history.flush` method to immediately flush all pending state changes to the browser URL.
87
+ * @param opts
88
+ * @param opts.getHref A function that returns the current href (path + search + hash)
89
+ * @param opts.createHref A function that takes a path and returns a href (path + search + hash)
90
+ * @returns A history instance
91
+ */
92
+ export declare function createBrowserHistory(opts?: {
93
+ parseLocation?: () => HistoryLocation;
94
+ createHref?: (path: string) => string;
95
+ window?: any;
96
+ }): RouterHistory;
97
+ export declare function createHashHistory(opts?: {
98
+ window?: any;
99
+ }): RouterHistory;
100
+ export declare function createMemoryHistory(opts?: {
101
+ initialEntries: Array<string>;
102
+ initialIndex?: number;
103
+ }): RouterHistory;
104
+ export declare function parseHref(href: string, state: ParsedHistoryState | undefined): HistoryLocation;
105
+ export {};
@@ -0,0 +1,105 @@
1
+ export interface NavigateOptions {
2
+ ignoreBlocker?: boolean;
3
+ }
4
+ type SubscriberHistoryAction = {
5
+ type: Exclude<HistoryAction, 'GO'>;
6
+ } | {
7
+ type: 'GO';
8
+ index: number;
9
+ };
10
+ type SubscriberArgs = {
11
+ location: HistoryLocation;
12
+ action: SubscriberHistoryAction;
13
+ };
14
+ export interface RouterHistory {
15
+ location: HistoryLocation;
16
+ length: number;
17
+ subscribers: Set<(opts: SubscriberArgs) => void>;
18
+ subscribe: (cb: (opts: SubscriberArgs) => void) => () => void;
19
+ push: (path: string, state?: any, navigateOpts?: NavigateOptions) => void;
20
+ replace: (path: string, state?: any, navigateOpts?: NavigateOptions) => void;
21
+ go: (index: number, navigateOpts?: NavigateOptions) => void;
22
+ back: (navigateOpts?: NavigateOptions) => void;
23
+ forward: (navigateOpts?: NavigateOptions) => void;
24
+ canGoBack: () => boolean;
25
+ createHref: (href: string) => string;
26
+ block: (blocker: NavigationBlocker) => () => void;
27
+ flush: () => void;
28
+ destroy: () => void;
29
+ notify: (action: SubscriberHistoryAction) => void;
30
+ _ignoreSubscribers?: boolean;
31
+ }
32
+ export interface HistoryLocation extends ParsedPath {
33
+ state: ParsedHistoryState;
34
+ }
35
+ export interface ParsedPath {
36
+ href: string;
37
+ pathname: string;
38
+ search: string;
39
+ hash: string;
40
+ }
41
+ export interface HistoryState {
42
+ }
43
+ export type ParsedHistoryState = HistoryState & {
44
+ key?: string;
45
+ __TSR_key?: string;
46
+ __TSR_index: number;
47
+ };
48
+ type ShouldAllowNavigation = any;
49
+ export type HistoryAction = 'PUSH' | 'REPLACE' | 'FORWARD' | 'BACK' | 'GO';
50
+ export type BlockerFnArgs = {
51
+ currentLocation: HistoryLocation;
52
+ nextLocation: HistoryLocation;
53
+ action: HistoryAction;
54
+ };
55
+ export type BlockerFn = (args: BlockerFnArgs) => Promise<ShouldAllowNavigation> | ShouldAllowNavigation;
56
+ export type NavigationBlocker = {
57
+ blockerFn: BlockerFn;
58
+ enableBeforeUnload?: (() => boolean) | boolean;
59
+ };
60
+ export declare function createHistory(opts: {
61
+ getLocation: () => HistoryLocation;
62
+ getLength: () => number;
63
+ pushState: (path: string, state: any) => void;
64
+ replaceState: (path: string, state: any) => void;
65
+ go: (n: number) => void;
66
+ back: (ignoreBlocker: boolean) => void;
67
+ forward: (ignoreBlocker: boolean) => void;
68
+ createHref: (path: string) => string;
69
+ flush?: () => void;
70
+ destroy?: () => void;
71
+ onBlocked?: () => void;
72
+ getBlockers?: () => Array<NavigationBlocker>;
73
+ setBlockers?: (blockers: Array<NavigationBlocker>) => void;
74
+ notifyOnIndexChange?: boolean;
75
+ }): RouterHistory;
76
+ /**
77
+ * Creates a history object that can be used to interact with the browser's
78
+ * navigation. This is a lightweight API wrapping the browser's native methods.
79
+ * It is designed to work with TanStack Router, but could be used as a standalone API as well.
80
+ * IMPORTANT: This API implements history throttling via a microtask to prevent
81
+ * excessive calls to the history API. In some browsers, calling history.pushState or
82
+ * history.replaceState in quick succession can cause the browser to ignore subsequent
83
+ * calls. This API smooths out those differences and ensures that your application
84
+ * state will *eventually* match the browser state. In most cases, this is not a problem,
85
+ * but if you need to ensure that the browser state is up to date, you can use the
86
+ * `history.flush` method to immediately flush all pending state changes to the browser URL.
87
+ * @param opts
88
+ * @param opts.getHref A function that returns the current href (path + search + hash)
89
+ * @param opts.createHref A function that takes a path and returns a href (path + search + hash)
90
+ * @returns A history instance
91
+ */
92
+ export declare function createBrowserHistory(opts?: {
93
+ parseLocation?: () => HistoryLocation;
94
+ createHref?: (path: string) => string;
95
+ window?: any;
96
+ }): RouterHistory;
97
+ export declare function createHashHistory(opts?: {
98
+ window?: any;
99
+ }): RouterHistory;
100
+ export declare function createMemoryHistory(opts?: {
101
+ initialEntries: Array<string>;
102
+ initialIndex?: number;
103
+ }): RouterHistory;
104
+ export declare function parseHref(href: string, state: ParsedHistoryState | undefined): HistoryLocation;
105
+ export {};
@@ -0,0 +1,419 @@
1
+ const stateIndexKey = "__TSR_index";
2
+ const popStateEvent = "popstate";
3
+ const beforeUnloadEvent = "beforeunload";
4
+ function createHistory(opts) {
5
+ let location = opts.getLocation();
6
+ const subscribers = /* @__PURE__ */ new Set();
7
+ const notify = (action) => {
8
+ location = opts.getLocation();
9
+ subscribers.forEach((subscriber) => subscriber({ location, action }));
10
+ };
11
+ const handleIndexChange = (action) => {
12
+ if (opts.notifyOnIndexChange ?? true) notify(action);
13
+ else location = opts.getLocation();
14
+ };
15
+ const tryNavigation = async ({
16
+ task,
17
+ navigateOpts,
18
+ ...actionInfo
19
+ }) => {
20
+ var _a, _b;
21
+ const ignoreBlocker = (navigateOpts == null ? void 0 : navigateOpts.ignoreBlocker) ?? false;
22
+ if (ignoreBlocker) {
23
+ task();
24
+ return;
25
+ }
26
+ const blockers = ((_a = opts.getBlockers) == null ? void 0 : _a.call(opts)) ?? [];
27
+ const isPushOrReplace = actionInfo.type === "PUSH" || actionInfo.type === "REPLACE";
28
+ if (typeof document !== "undefined" && blockers.length && isPushOrReplace) {
29
+ for (const blocker of blockers) {
30
+ const nextLocation = parseHref(actionInfo.path, actionInfo.state);
31
+ const isBlocked = await blocker.blockerFn({
32
+ currentLocation: location,
33
+ nextLocation,
34
+ action: actionInfo.type
35
+ });
36
+ if (isBlocked) {
37
+ (_b = opts.onBlocked) == null ? void 0 : _b.call(opts);
38
+ return;
39
+ }
40
+ }
41
+ }
42
+ task();
43
+ };
44
+ return {
45
+ get location() {
46
+ return location;
47
+ },
48
+ get length() {
49
+ return opts.getLength();
50
+ },
51
+ subscribers,
52
+ subscribe: (cb) => {
53
+ subscribers.add(cb);
54
+ return () => {
55
+ subscribers.delete(cb);
56
+ };
57
+ },
58
+ push: (path, state, navigateOpts) => {
59
+ const currentIndex = location.state[stateIndexKey];
60
+ state = assignKeyAndIndex(currentIndex + 1, state);
61
+ tryNavigation({
62
+ task: () => {
63
+ opts.pushState(path, state);
64
+ notify({ type: "PUSH" });
65
+ },
66
+ navigateOpts,
67
+ type: "PUSH",
68
+ path,
69
+ state
70
+ });
71
+ },
72
+ replace: (path, state, navigateOpts) => {
73
+ const currentIndex = location.state[stateIndexKey];
74
+ state = assignKeyAndIndex(currentIndex, state);
75
+ tryNavigation({
76
+ task: () => {
77
+ opts.replaceState(path, state);
78
+ notify({ type: "REPLACE" });
79
+ },
80
+ navigateOpts,
81
+ type: "REPLACE",
82
+ path,
83
+ state
84
+ });
85
+ },
86
+ go: (index, navigateOpts) => {
87
+ tryNavigation({
88
+ task: () => {
89
+ opts.go(index);
90
+ handleIndexChange({ type: "GO", index });
91
+ },
92
+ navigateOpts,
93
+ type: "GO"
94
+ });
95
+ },
96
+ back: (navigateOpts) => {
97
+ tryNavigation({
98
+ task: () => {
99
+ opts.back((navigateOpts == null ? void 0 : navigateOpts.ignoreBlocker) ?? false);
100
+ handleIndexChange({ type: "BACK" });
101
+ },
102
+ navigateOpts,
103
+ type: "BACK"
104
+ });
105
+ },
106
+ forward: (navigateOpts) => {
107
+ tryNavigation({
108
+ task: () => {
109
+ opts.forward((navigateOpts == null ? void 0 : navigateOpts.ignoreBlocker) ?? false);
110
+ handleIndexChange({ type: "FORWARD" });
111
+ },
112
+ navigateOpts,
113
+ type: "FORWARD"
114
+ });
115
+ },
116
+ canGoBack: () => location.state[stateIndexKey] !== 0,
117
+ createHref: (str) => opts.createHref(str),
118
+ block: (blocker) => {
119
+ var _a;
120
+ if (!opts.setBlockers) return () => {
121
+ };
122
+ const blockers = ((_a = opts.getBlockers) == null ? void 0 : _a.call(opts)) ?? [];
123
+ opts.setBlockers([...blockers, blocker]);
124
+ return () => {
125
+ var _a2, _b;
126
+ const blockers2 = ((_a2 = opts.getBlockers) == null ? void 0 : _a2.call(opts)) ?? [];
127
+ (_b = opts.setBlockers) == null ? void 0 : _b.call(opts, blockers2.filter((b) => b !== blocker));
128
+ };
129
+ },
130
+ flush: () => {
131
+ var _a;
132
+ return (_a = opts.flush) == null ? void 0 : _a.call(opts);
133
+ },
134
+ destroy: () => {
135
+ var _a;
136
+ return (_a = opts.destroy) == null ? void 0 : _a.call(opts);
137
+ },
138
+ notify
139
+ };
140
+ }
141
+ function assignKeyAndIndex(index, state) {
142
+ if (!state) {
143
+ state = {};
144
+ }
145
+ const key = createRandomKey();
146
+ return {
147
+ ...state,
148
+ key,
149
+ // TODO: Remove in v2 - use __TSR_key instead
150
+ __TSR_key: key,
151
+ [stateIndexKey]: index
152
+ };
153
+ }
154
+ function createBrowserHistory(opts) {
155
+ var _a, _b;
156
+ const win = (opts == null ? void 0 : opts.window) ?? (typeof document !== "undefined" ? window : void 0);
157
+ const originalPushState = win.history.pushState;
158
+ const originalReplaceState = win.history.replaceState;
159
+ let blockers = [];
160
+ const _getBlockers = () => blockers;
161
+ const _setBlockers = (newBlockers) => blockers = newBlockers;
162
+ const createHref = (opts == null ? void 0 : opts.createHref) ?? ((path) => path);
163
+ const parseLocation = (opts == null ? void 0 : opts.parseLocation) ?? (() => parseHref(
164
+ `${win.location.pathname}${win.location.search}${win.location.hash}`,
165
+ win.history.state
166
+ ));
167
+ if (!((_a = win.history.state) == null ? void 0 : _a.__TSR_key) && !((_b = win.history.state) == null ? void 0 : _b.key)) {
168
+ const addedKey = createRandomKey();
169
+ win.history.replaceState(
170
+ {
171
+ [stateIndexKey]: 0,
172
+ key: addedKey,
173
+ // TODO: Remove in v2 - use __TSR_key instead
174
+ __TSR_key: addedKey
175
+ },
176
+ ""
177
+ );
178
+ }
179
+ let currentLocation = parseLocation();
180
+ let rollbackLocation;
181
+ let nextPopIsGo = false;
182
+ let ignoreNextPop = false;
183
+ let skipBlockerNextPop = false;
184
+ let ignoreNextBeforeUnload = false;
185
+ const getLocation = () => currentLocation;
186
+ let next;
187
+ let scheduled;
188
+ const flush = () => {
189
+ if (!next) {
190
+ return;
191
+ }
192
+ history._ignoreSubscribers = true;
193
+ (next.isPush ? win.history.pushState : win.history.replaceState)(
194
+ next.state,
195
+ "",
196
+ next.href
197
+ );
198
+ history._ignoreSubscribers = false;
199
+ next = void 0;
200
+ scheduled = void 0;
201
+ rollbackLocation = void 0;
202
+ };
203
+ const queueHistoryAction = (type, destHref, state) => {
204
+ const href = createHref(destHref);
205
+ if (!scheduled) {
206
+ rollbackLocation = currentLocation;
207
+ }
208
+ currentLocation = parseHref(destHref, state);
209
+ next = {
210
+ href,
211
+ state,
212
+ isPush: (next == null ? void 0 : next.isPush) || type === "push"
213
+ };
214
+ if (!scheduled) {
215
+ scheduled = Promise.resolve().then(() => flush());
216
+ }
217
+ };
218
+ const onPushPop = (type) => {
219
+ currentLocation = parseLocation();
220
+ history.notify({ type });
221
+ };
222
+ const onPushPopEvent = async () => {
223
+ if (ignoreNextPop) {
224
+ ignoreNextPop = false;
225
+ return;
226
+ }
227
+ const nextLocation = parseLocation();
228
+ const delta = nextLocation.state[stateIndexKey] - currentLocation.state[stateIndexKey];
229
+ const isForward = delta === 1;
230
+ const isBack = delta === -1;
231
+ const isGo = !isForward && !isBack || nextPopIsGo;
232
+ nextPopIsGo = false;
233
+ const action = isGo ? "GO" : isBack ? "BACK" : "FORWARD";
234
+ const notify = isGo ? {
235
+ type: "GO",
236
+ index: delta
237
+ } : {
238
+ type: isBack ? "BACK" : "FORWARD"
239
+ };
240
+ if (skipBlockerNextPop) {
241
+ skipBlockerNextPop = false;
242
+ } else {
243
+ const blockers2 = _getBlockers();
244
+ if (typeof document !== "undefined" && blockers2.length) {
245
+ for (const blocker of blockers2) {
246
+ const isBlocked = await blocker.blockerFn({
247
+ currentLocation,
248
+ nextLocation,
249
+ action
250
+ });
251
+ if (isBlocked) {
252
+ ignoreNextPop = true;
253
+ win.history.go(1);
254
+ history.notify(notify);
255
+ return;
256
+ }
257
+ }
258
+ }
259
+ }
260
+ currentLocation = parseLocation();
261
+ history.notify(notify);
262
+ };
263
+ const onBeforeUnload = (e) => {
264
+ if (ignoreNextBeforeUnload) {
265
+ ignoreNextBeforeUnload = false;
266
+ return;
267
+ }
268
+ let shouldBlock = false;
269
+ const blockers2 = _getBlockers();
270
+ if (typeof document !== "undefined" && blockers2.length) {
271
+ for (const blocker of blockers2) {
272
+ const shouldHaveBeforeUnload = blocker.enableBeforeUnload ?? true;
273
+ if (shouldHaveBeforeUnload === true) {
274
+ shouldBlock = true;
275
+ break;
276
+ }
277
+ if (typeof shouldHaveBeforeUnload === "function" && shouldHaveBeforeUnload() === true) {
278
+ shouldBlock = true;
279
+ break;
280
+ }
281
+ }
282
+ }
283
+ if (shouldBlock) {
284
+ e.preventDefault();
285
+ return e.returnValue = "";
286
+ }
287
+ return;
288
+ };
289
+ const history = createHistory({
290
+ getLocation,
291
+ getLength: () => win.history.length,
292
+ pushState: (href, state) => queueHistoryAction("push", href, state),
293
+ replaceState: (href, state) => queueHistoryAction("replace", href, state),
294
+ back: (ignoreBlocker) => {
295
+ if (ignoreBlocker) skipBlockerNextPop = true;
296
+ ignoreNextBeforeUnload = true;
297
+ return win.history.back();
298
+ },
299
+ forward: (ignoreBlocker) => {
300
+ if (ignoreBlocker) skipBlockerNextPop = true;
301
+ ignoreNextBeforeUnload = true;
302
+ win.history.forward();
303
+ },
304
+ go: (n) => {
305
+ nextPopIsGo = true;
306
+ win.history.go(n);
307
+ },
308
+ createHref: (href) => createHref(href),
309
+ flush,
310
+ destroy: () => {
311
+ win.history.pushState = originalPushState;
312
+ win.history.replaceState = originalReplaceState;
313
+ win.removeEventListener(beforeUnloadEvent, onBeforeUnload, {
314
+ capture: true
315
+ });
316
+ win.removeEventListener(popStateEvent, onPushPopEvent);
317
+ },
318
+ onBlocked: () => {
319
+ if (rollbackLocation && currentLocation !== rollbackLocation) {
320
+ currentLocation = rollbackLocation;
321
+ }
322
+ },
323
+ getBlockers: _getBlockers,
324
+ setBlockers: _setBlockers,
325
+ notifyOnIndexChange: false
326
+ });
327
+ win.addEventListener(beforeUnloadEvent, onBeforeUnload, { capture: true });
328
+ win.addEventListener(popStateEvent, onPushPopEvent);
329
+ win.history.pushState = function(...args) {
330
+ const res = originalPushState.apply(win.history, args);
331
+ if (!history._ignoreSubscribers) onPushPop("PUSH");
332
+ return res;
333
+ };
334
+ win.history.replaceState = function(...args) {
335
+ const res = originalReplaceState.apply(win.history, args);
336
+ if (!history._ignoreSubscribers) onPushPop("REPLACE");
337
+ return res;
338
+ };
339
+ return history;
340
+ }
341
+ function createHashHistory(opts) {
342
+ const win = (opts == null ? void 0 : opts.window) ?? (typeof document !== "undefined" ? window : void 0);
343
+ return createBrowserHistory({
344
+ window: win,
345
+ parseLocation: () => {
346
+ const hashSplit = win.location.hash.split("#").slice(1);
347
+ const pathPart = hashSplit[0] ?? "/";
348
+ const searchPart = win.location.search;
349
+ const hashEntries = hashSplit.slice(1);
350
+ const hashPart = hashEntries.length === 0 ? "" : `#${hashEntries.join("#")}`;
351
+ const hashHref = `${pathPart}${searchPart}${hashPart}`;
352
+ return parseHref(hashHref, win.history.state);
353
+ },
354
+ createHref: (href) => `${win.location.pathname}${win.location.search}#${href}`
355
+ });
356
+ }
357
+ function createMemoryHistory(opts = {
358
+ initialEntries: ["/"]
359
+ }) {
360
+ const entries = opts.initialEntries;
361
+ let index = opts.initialIndex ? Math.min(Math.max(opts.initialIndex, 0), entries.length - 1) : entries.length - 1;
362
+ const states = entries.map(
363
+ (_entry, index2) => assignKeyAndIndex(index2, void 0)
364
+ );
365
+ const getLocation = () => parseHref(entries[index], states[index]);
366
+ return createHistory({
367
+ getLocation,
368
+ getLength: () => entries.length,
369
+ pushState: (path, state) => {
370
+ if (index < entries.length - 1) {
371
+ entries.splice(index + 1);
372
+ states.splice(index + 1);
373
+ }
374
+ states.push(state);
375
+ entries.push(path);
376
+ index = Math.max(entries.length - 1, 0);
377
+ },
378
+ replaceState: (path, state) => {
379
+ states[index] = state;
380
+ entries[index] = path;
381
+ },
382
+ back: () => {
383
+ index = Math.max(index - 1, 0);
384
+ },
385
+ forward: () => {
386
+ index = Math.min(index + 1, entries.length - 1);
387
+ },
388
+ go: (n) => {
389
+ index = Math.min(Math.max(index + n, 0), entries.length - 1);
390
+ },
391
+ createHref: (path) => path
392
+ });
393
+ }
394
+ function parseHref(href, state) {
395
+ const hashIndex = href.indexOf("#");
396
+ const searchIndex = href.indexOf("?");
397
+ const addedKey = createRandomKey();
398
+ return {
399
+ href,
400
+ pathname: href.substring(
401
+ 0,
402
+ hashIndex > 0 ? searchIndex > 0 ? Math.min(hashIndex, searchIndex) : hashIndex : searchIndex > 0 ? searchIndex : href.length
403
+ ),
404
+ hash: hashIndex > -1 ? href.substring(hashIndex) : "",
405
+ search: searchIndex > -1 ? href.slice(searchIndex, hashIndex === -1 ? void 0 : hashIndex) : "",
406
+ state: state || { [stateIndexKey]: 0, key: addedKey, __TSR_key: addedKey }
407
+ };
408
+ }
409
+ function createRandomKey() {
410
+ return (Math.random() + 1).toString(36).substring(7);
411
+ }
412
+ export {
413
+ createBrowserHistory,
414
+ createHashHistory,
415
+ createHistory,
416
+ createMemoryHistory,
417
+ parseHref
418
+ };
419
+ //# sourceMappingURL=index.js.map
@@ -0,0 +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}\n\ntype SubscriberHistoryAction =\n | {\n type: Exclude<HistoryAction, 'GO'>\n }\n | {\n type: 'GO'\n index: number\n }\n\ntype SubscriberArgs = {\n location: HistoryLocation\n action: SubscriberHistoryAction\n}\n\nexport interface RouterHistory {\n location: HistoryLocation\n length: number\n subscribers: Set<(opts: SubscriberArgs) => void>\n subscribe: (cb: (opts: SubscriberArgs) => void) => () => void\n push: (path: string, state?: any, navigateOpts?: NavigateOptions) => void\n replace: (path: string, state?: any, navigateOpts?: NavigateOptions) => void\n go: (index: number, navigateOpts?: NavigateOptions) => void\n back: (navigateOpts?: NavigateOptions) => void\n forward: (navigateOpts?: NavigateOptions) => void\n canGoBack: () => boolean\n createHref: (href: string) => string\n block: (blocker: NavigationBlocker) => () => void\n flush: () => void\n destroy: () => void\n notify: (action: SubscriberHistoryAction) => void\n _ignoreSubscribers?: boolean\n}\n\nexport interface HistoryLocation extends ParsedPath {\n state: ParsedHistoryState\n}\n\nexport interface ParsedPath {\n href: string\n pathname: string\n search: string\n hash: string\n}\n\nexport interface HistoryState {}\n\nexport type ParsedHistoryState = HistoryState & {\n key?: string // TODO: Remove in v2 - use __TSR_key instead\n __TSR_key?: string\n __TSR_index: number\n}\n\ntype ShouldAllowNavigation = any\n\nexport type HistoryAction = 'PUSH' | 'REPLACE' | 'FORWARD' | 'BACK' | 'GO'\n\nexport type BlockerFnArgs = {\n currentLocation: HistoryLocation\n nextLocation: HistoryLocation\n action: HistoryAction\n}\n\nexport type BlockerFn = (\n args: BlockerFnArgs,\n) => Promise<ShouldAllowNavigation> | ShouldAllowNavigation\n\nexport type NavigationBlocker = {\n blockerFn: BlockerFn\n enableBeforeUnload?: (() => boolean) | boolean\n}\n\ntype TryNavigateArgs = {\n task: () => void\n type: 'PUSH' | 'REPLACE' | 'BACK' | 'FORWARD' | 'GO'\n navigateOpts?: NavigateOptions\n} & (\n | {\n type: 'PUSH' | 'REPLACE'\n path: string\n state: any\n }\n | {\n type: 'BACK' | 'FORWARD' | 'GO'\n }\n)\n\nconst stateIndexKey = '__TSR_index'\nconst popStateEvent = 'popstate'\nconst beforeUnloadEvent = 'beforeunload'\n\nexport function createHistory(opts: {\n getLocation: () => HistoryLocation\n getLength: () => number\n pushState: (path: string, state: any) => void\n replaceState: (path: string, state: any) => void\n go: (n: number) => void\n back: (ignoreBlocker: boolean) => void\n forward: (ignoreBlocker: boolean) => void\n createHref: (path: string) => string\n flush?: () => void\n destroy?: () => void\n onBlocked?: () => void\n getBlockers?: () => Array<NavigationBlocker>\n setBlockers?: (blockers: Array<NavigationBlocker>) => void\n // Avoid notifying on forward/back/go, used for browser history as we already get notified by the popstate event\n notifyOnIndexChange?: boolean\n}): RouterHistory {\n let location = opts.getLocation()\n const subscribers = new Set<(opts: SubscriberArgs) => void>()\n\n const notify = (action: SubscriberHistoryAction) => {\n location = opts.getLocation()\n subscribers.forEach((subscriber) => subscriber({ location, action }))\n }\n\n const handleIndexChange = (action: SubscriberHistoryAction) => {\n if (opts.notifyOnIndexChange ?? true) notify(action)\n else location = opts.getLocation()\n }\n\n const tryNavigation = async ({\n task,\n navigateOpts,\n ...actionInfo\n }: TryNavigateArgs) => {\n const ignoreBlocker = navigateOpts?.ignoreBlocker ?? false\n if (ignoreBlocker) {\n task()\n return\n }\n\n const blockers = opts.getBlockers?.() ?? []\n const isPushOrReplace =\n actionInfo.type === 'PUSH' || actionInfo.type === 'REPLACE'\n if (typeof document !== 'undefined' && blockers.length && isPushOrReplace) {\n for (const blocker of blockers) {\n const nextLocation = parseHref(actionInfo.path, actionInfo.state)\n const isBlocked = await blocker.blockerFn({\n currentLocation: location,\n nextLocation,\n action: actionInfo.type,\n })\n if (isBlocked) {\n opts.onBlocked?.()\n return\n }\n }\n }\n\n task()\n }\n\n return {\n get location() {\n return location\n },\n get length() {\n return opts.getLength()\n },\n subscribers,\n subscribe: (cb: (opts: SubscriberArgs) => void) => {\n subscribers.add(cb)\n\n return () => {\n subscribers.delete(cb)\n }\n },\n push: (path, state, navigateOpts) => {\n const currentIndex = location.state[stateIndexKey]\n state = assignKeyAndIndex(currentIndex + 1, state)\n tryNavigation({\n task: () => {\n opts.pushState(path, state)\n notify({ type: 'PUSH' })\n },\n navigateOpts,\n type: 'PUSH',\n path,\n state,\n })\n },\n replace: (path, state, navigateOpts) => {\n const currentIndex = location.state[stateIndexKey]\n state = assignKeyAndIndex(currentIndex, state)\n tryNavigation({\n task: () => {\n opts.replaceState(path, state)\n notify({ type: 'REPLACE' })\n },\n navigateOpts,\n type: 'REPLACE',\n path,\n state,\n })\n },\n go: (index, navigateOpts) => {\n tryNavigation({\n task: () => {\n opts.go(index)\n handleIndexChange({ type: 'GO', index })\n },\n navigateOpts,\n type: 'GO',\n })\n },\n back: (navigateOpts) => {\n tryNavigation({\n task: () => {\n opts.back(navigateOpts?.ignoreBlocker ?? false)\n handleIndexChange({ type: 'BACK' })\n },\n navigateOpts,\n type: 'BACK',\n })\n },\n forward: (navigateOpts) => {\n tryNavigation({\n task: () => {\n opts.forward(navigateOpts?.ignoreBlocker ?? false)\n handleIndexChange({ type: 'FORWARD' })\n },\n navigateOpts,\n type: 'FORWARD',\n })\n },\n canGoBack: () => location.state[stateIndexKey] !== 0,\n createHref: (str) => opts.createHref(str),\n block: (blocker) => {\n if (!opts.setBlockers) return () => {}\n const blockers = opts.getBlockers?.() ?? []\n opts.setBlockers([...blockers, blocker])\n\n return () => {\n const blockers = opts.getBlockers?.() ?? []\n opts.setBlockers?.(blockers.filter((b) => b !== blocker))\n }\n },\n flush: () => opts.flush?.(),\n destroy: () => opts.destroy?.(),\n notify,\n }\n}\n\nfunction assignKeyAndIndex(index: number, state: HistoryState | undefined) {\n if (!state) {\n state = {} as HistoryState\n }\n const key = createRandomKey()\n return {\n ...state,\n key, // TODO: Remove in v2 - use __TSR_key instead\n __TSR_key: key,\n [stateIndexKey]: index,\n } as ParsedHistoryState\n}\n\n/**\n * Creates a history object that can be used to interact with the browser's\n * navigation. This is a lightweight API wrapping the browser's native methods.\n * It is designed to work with TanStack Router, but could be used as a standalone API as well.\n * IMPORTANT: This API implements history throttling via a microtask to prevent\n * excessive calls to the history API. In some browsers, calling history.pushState or\n * history.replaceState in quick succession can cause the browser to ignore subsequent\n * calls. This API smooths out those differences and ensures that your application\n * state will *eventually* match the browser state. In most cases, this is not a problem,\n * but if you need to ensure that the browser state is up to date, you can use the\n * `history.flush` method to immediately flush all pending state changes to the browser URL.\n * @param opts\n * @param opts.getHref A function that returns the current href (path + search + hash)\n * @param opts.createHref A function that takes a path and returns a href (path + search + hash)\n * @returns A history instance\n */\nexport function createBrowserHistory(opts?: {\n parseLocation?: () => HistoryLocation\n createHref?: (path: string) => string\n window?: any\n}): RouterHistory {\n const win =\n opts?.window ??\n (typeof document !== 'undefined' ? window : (undefined as any))\n\n const originalPushState = win.history.pushState\n const originalReplaceState = win.history.replaceState\n\n let blockers: Array<NavigationBlocker> = []\n const _getBlockers = () => blockers\n const _setBlockers = (newBlockers: Array<NavigationBlocker>) =>\n (blockers = newBlockers)\n\n const createHref = opts?.createHref ?? ((path) => path)\n const parseLocation =\n opts?.parseLocation ??\n (() =>\n parseHref(\n `${win.location.pathname}${win.location.search}${win.location.hash}`,\n win.history.state,\n ))\n\n // Ensure there is always a key to start\n if (!win.history.state?.__TSR_key && !win.history.state?.key) {\n const addedKey = createRandomKey()\n win.history.replaceState(\n {\n [stateIndexKey]: 0,\n key: addedKey, // TODO: Remove in v2 - use __TSR_key instead\n __TSR_key: addedKey,\n },\n '',\n )\n }\n\n let currentLocation = parseLocation()\n let rollbackLocation: HistoryLocation | undefined\n\n let nextPopIsGo = false\n let ignoreNextPop = false\n let skipBlockerNextPop = false\n let ignoreNextBeforeUnload = false\n\n const getLocation = () => currentLocation\n\n let next:\n | undefined\n | {\n // This is the latest location that we were attempting to push/replace\n href: string\n // This is the latest state that we were attempting to push/replace\n state: any\n // This is the latest type that we were attempting to push/replace\n isPush: boolean\n }\n\n // We need to track the current scheduled update to prevent\n // multiple updates from being scheduled at the same time.\n let scheduled: Promise<void> | undefined\n\n // This function flushes the next update to the browser history\n const flush = () => {\n if (!next) {\n return\n }\n\n // We need to ignore any updates to the subscribers while we update the browser history\n history._ignoreSubscribers = true\n\n // Update the browser history\n ;(next.isPush ? win.history.pushState : win.history.replaceState)(\n next.state,\n '',\n next.href,\n )\n\n // Stop ignoring subscriber updates\n history._ignoreSubscribers = false\n\n // Reset the nextIsPush flag and clear the scheduled update\n next = undefined\n scheduled = undefined\n rollbackLocation = undefined\n }\n\n // This function queues up a call to update the browser history\n const queueHistoryAction = (\n type: 'push' | 'replace',\n destHref: string,\n state: any,\n ) => {\n const href = createHref(destHref)\n\n if (!scheduled) {\n rollbackLocation = currentLocation\n }\n\n // Update the location in memory\n currentLocation = parseHref(destHref, state)\n\n // Keep track of the next location we need to flush to the URL\n next = {\n href,\n state,\n isPush: next?.isPush || type === 'push',\n }\n\n if (!scheduled) {\n // Schedule an update to the browser history\n scheduled = Promise.resolve().then(() => flush())\n }\n }\n\n // NOTE: this function can probably be removed\n const onPushPop = (type: 'PUSH' | 'REPLACE') => {\n currentLocation = parseLocation()\n history.notify({ type })\n }\n\n const onPushPopEvent = async () => {\n if (ignoreNextPop) {\n ignoreNextPop = false\n return\n }\n\n const nextLocation = parseLocation()\n const delta =\n nextLocation.state[stateIndexKey] - currentLocation.state[stateIndexKey]\n const isForward = delta === 1\n const isBack = delta === -1\n const isGo = (!isForward && !isBack) || nextPopIsGo\n nextPopIsGo = false\n\n const action = isGo ? 'GO' : isBack ? 'BACK' : 'FORWARD'\n const notify: SubscriberHistoryAction = isGo\n ? {\n type: 'GO',\n index: delta,\n }\n : {\n type: isBack ? 'BACK' : 'FORWARD',\n }\n\n if (skipBlockerNextPop) {\n skipBlockerNextPop = false\n } else {\n const blockers = _getBlockers()\n if (typeof document !== 'undefined' && blockers.length) {\n for (const blocker of blockers) {\n const isBlocked = await blocker.blockerFn({\n currentLocation,\n nextLocation,\n action,\n })\n if (isBlocked) {\n ignoreNextPop = true\n win.history.go(1)\n history.notify(notify)\n return\n }\n }\n }\n }\n\n currentLocation = parseLocation()\n history.notify(notify)\n }\n\n const onBeforeUnload = (e: BeforeUnloadEvent) => {\n if (ignoreNextBeforeUnload) {\n ignoreNextBeforeUnload = false\n return\n }\n\n let shouldBlock = false\n\n // If one blocker has a non-disabled beforeUnload, we should block\n const blockers = _getBlockers()\n if (typeof document !== 'undefined' && blockers.length) {\n for (const blocker of blockers) {\n const shouldHaveBeforeUnload = blocker.enableBeforeUnload ?? true\n if (shouldHaveBeforeUnload === true) {\n shouldBlock = true\n break\n }\n\n if (\n typeof shouldHaveBeforeUnload === 'function' &&\n shouldHaveBeforeUnload() === true\n ) {\n shouldBlock = true\n break\n }\n }\n }\n\n if (shouldBlock) {\n e.preventDefault()\n return (e.returnValue = '')\n }\n return\n }\n\n const history = createHistory({\n getLocation,\n getLength: () => win.history.length,\n pushState: (href, state) => queueHistoryAction('push', href, state),\n replaceState: (href, state) => queueHistoryAction('replace', href, state),\n back: (ignoreBlocker) => {\n if (ignoreBlocker) skipBlockerNextPop = true\n ignoreNextBeforeUnload = true\n return win.history.back()\n },\n forward: (ignoreBlocker) => {\n if (ignoreBlocker) skipBlockerNextPop = true\n ignoreNextBeforeUnload = true\n win.history.forward()\n },\n go: (n) => {\n nextPopIsGo = true\n win.history.go(n)\n },\n createHref: (href) => createHref(href),\n flush,\n destroy: () => {\n win.history.pushState = originalPushState\n win.history.replaceState = originalReplaceState\n win.removeEventListener(beforeUnloadEvent, onBeforeUnload, {\n capture: true,\n })\n win.removeEventListener(popStateEvent, onPushPopEvent)\n },\n onBlocked: () => {\n // If a navigation is blocked, we need to rollback the location\n // that we optimistically updated in memory.\n if (rollbackLocation && currentLocation !== rollbackLocation) {\n currentLocation = rollbackLocation\n }\n },\n getBlockers: _getBlockers,\n setBlockers: _setBlockers,\n notifyOnIndexChange: false,\n })\n\n win.addEventListener(beforeUnloadEvent, onBeforeUnload, { capture: true })\n win.addEventListener(popStateEvent, onPushPopEvent)\n\n win.history.pushState = function (...args: Array<any>) {\n const res = originalPushState.apply(win.history, args as any)\n if (!history._ignoreSubscribers) onPushPop('PUSH')\n return res\n }\n\n win.history.replaceState = function (...args: Array<any>) {\n const res = originalReplaceState.apply(win.history, args as any)\n if (!history._ignoreSubscribers) onPushPop('REPLACE')\n return res\n }\n\n return history\n}\n\nexport function createHashHistory(opts?: { window?: any }): RouterHistory {\n const win =\n opts?.window ??\n (typeof document !== 'undefined' ? window : (undefined as any))\n return createBrowserHistory({\n window: win,\n parseLocation: () => {\n const hashSplit = win.location.hash.split('#').slice(1)\n const pathPart = hashSplit[0] ?? '/'\n const searchPart = win.location.search\n const hashEntries = hashSplit.slice(1)\n const hashPart =\n hashEntries.length === 0 ? '' : `#${hashEntries.join('#')}`\n const hashHref = `${pathPart}${searchPart}${hashPart}`\n return parseHref(hashHref, win.history.state)\n },\n createHref: (href) =>\n `${win.location.pathname}${win.location.search}#${href}`,\n })\n}\n\nexport function createMemoryHistory(\n opts: {\n initialEntries: Array<string>\n initialIndex?: number\n } = {\n initialEntries: ['/'],\n },\n): RouterHistory {\n const entries = opts.initialEntries\n let index = opts.initialIndex\n ? Math.min(Math.max(opts.initialIndex, 0), entries.length - 1)\n : entries.length - 1\n const states = entries.map((_entry, index) =>\n assignKeyAndIndex(index, undefined),\n )\n\n const getLocation = () => parseHref(entries[index]!, states[index])\n\n 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: ParsedHistoryState | undefined,\n): HistoryLocation {\n const hashIndex = href.indexOf('#')\n const searchIndex = href.indexOf('?')\n\n const addedKey = createRandomKey()\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 || { [stateIndexKey]: 0, key: addedKey, __TSR_key: addedKey },\n }\n}\n\n// Thanks co-pilot!\nfunction createRandomKey() {\n return (Math.random() + 1).toString(36).substring(7)\n}\n"],"names":["_a","blockers","index"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tanstack/history",
3
- "version": "1.121.20",
3
+ "version": "1.121.34",
4
4
  "description": "Modern and scalable routing for React applications",
5
5
  "author": "Tanner Linsley",
6
6
  "license": "MIT",
package/src/index.ts CHANGED
@@ -53,7 +53,8 @@ export interface ParsedPath {
53
53
  export interface HistoryState {}
54
54
 
55
55
  export type ParsedHistoryState = HistoryState & {
56
- key?: string
56
+ key?: string // TODO: Remove in v2 - use __TSR_key instead
57
+ __TSR_key?: string
57
58
  __TSR_index: number
58
59
  }
59
60
 
@@ -252,9 +253,11 @@ function assignKeyAndIndex(index: number, state: HistoryState | undefined) {
252
253
  if (!state) {
253
254
  state = {} as HistoryState
254
255
  }
256
+ const key = createRandomKey()
255
257
  return {
256
258
  ...state,
257
- key: createRandomKey(),
259
+ key, // TODO: Remove in v2 - use __TSR_key instead
260
+ __TSR_key: key,
258
261
  [stateIndexKey]: index,
259
262
  } as ParsedHistoryState
260
263
  }
@@ -302,11 +305,13 @@ export function createBrowserHistory(opts?: {
302
305
  ))
303
306
 
304
307
  // Ensure there is always a key to start
305
- if (!win.history.state?.key) {
308
+ if (!win.history.state?.__TSR_key && !win.history.state?.key) {
309
+ const addedKey = createRandomKey()
306
310
  win.history.replaceState(
307
311
  {
308
312
  [stateIndexKey]: 0,
309
- key: createRandomKey(),
313
+ key: addedKey, // TODO: Remove in v2 - use __TSR_key instead
314
+ __TSR_key: addedKey,
310
315
  },
311
316
  '',
312
317
  )
@@ -615,6 +620,8 @@ export function parseHref(
615
620
  const hashIndex = href.indexOf('#')
616
621
  const searchIndex = href.indexOf('?')
617
622
 
623
+ const addedKey = createRandomKey()
624
+
618
625
  return {
619
626
  href,
620
627
  pathname: href.substring(
@@ -632,7 +639,7 @@ export function parseHref(
632
639
  searchIndex > -1
633
640
  ? href.slice(searchIndex, hashIndex === -1 ? undefined : hashIndex)
634
641
  : '',
635
- state: state || { [stateIndexKey]: 0, key: createRandomKey() },
642
+ state: state || { [stateIndexKey]: 0, key: addedKey, __TSR_key: addedKey },
636
643
  }
637
644
  }
638
645