New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

@tanstack/history

Package Overview
Dependencies
Maintainers
0
Versions
199
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@tanstack/history - npm Package Compare versions

Comparing version 1.87.6 to 1.90.0

40

dist/esm/index.d.ts
export interface NavigateOptions {
ignoreBlocker?: boolean;
}
type SubscriberHistoryAction = {
type: HistoryAction | 'ROLLBACK';
} | {
type: 'GO';
index: number;
};
type SubscriberArgs = {
location: HistoryLocation;
action: SubscriberHistoryAction;
};
export interface RouterHistory {
location: HistoryLocation;
length: number;
subscribers: Set<(opts: {
location: HistoryLocation;
}) => void>;
subscribe: (cb: (opts: {
location: HistoryLocation;
}) => void) => () => void;
subscribers: Set<(opts: SubscriberArgs) => void>;
subscribe: (cb: (opts: SubscriberArgs) => void) => () => void;
push: (path: string, state?: any, navigateOpts?: NavigateOptions) => void;

@@ -19,6 +25,6 @@ replace: (path: string, state?: any, navigateOpts?: NavigateOptions) => void;

createHref: (href: string) => string;
block: (blocker: BlockerFn) => () => void;
block: (blocker: NavigationBlocker) => () => void;
flush: () => void;
destroy: () => void;
notify: () => void;
notify: (action: SubscriberHistoryAction) => void;
_ignoreSubscribers?: boolean;

@@ -39,3 +45,13 @@ }

type ShouldAllowNavigation = any;
export type BlockerFn = () => Promise<ShouldAllowNavigation> | ShouldAllowNavigation;
export type HistoryAction = 'PUSH' | 'POP' | 'REPLACE' | 'FORWARD' | 'BACK' | 'GO';
export type BlockerFnArgs = {
currentLocation: HistoryLocation;
nextLocation: HistoryLocation;
action: HistoryAction;
};
export type BlockerFn = (args: BlockerFnArgs) => Promise<ShouldAllowNavigation> | ShouldAllowNavigation;
export type NavigationBlocker = {
blockerFn: BlockerFn;
enableBeforeUnload?: (() => boolean) | boolean;
};
export declare function createHistory(opts: {

@@ -47,4 +63,4 @@ getLocation: () => HistoryLocation;

go: (n: number) => void;
back: () => void;
forward: () => void;
back: (ignoreBlocker: boolean) => void;
forward: (ignoreBlocker: boolean) => void;
createHref: (path: string) => string;

@@ -54,2 +70,4 @@ flush?: () => void;

onBlocked?: (onUpdate: () => void) => void;
getBlockers?: () => Array<NavigationBlocker>;
setBlockers?: (blockers: Array<NavigationBlocker>) => void;
}): RouterHistory;

@@ -56,0 +74,0 @@ /**

@@ -1,29 +0,39 @@

const pushStateEvent = "pushstate";
const popStateEvent = "popstate";
const beforeUnloadEvent = "beforeunload";
const beforeUnloadListener = (event) => {
event.preventDefault();
return event.returnValue = "";
};
const stopBlocking = () => {
removeEventListener(beforeUnloadEvent, beforeUnloadListener, {
capture: true
});
};
function createHistory(opts) {
let location = opts.getLocation();
const subscribers = /* @__PURE__ */ new Set();
let blockers = [];
const notify = () => {
const notify = (action) => {
location = opts.getLocation();
subscribers.forEach((subscriber) => subscriber({ location }));
subscribers.forEach((subscriber) => subscriber({ location, action }));
};
const tryNavigation = async (task, navigateOpts) => {
var _a;
const _notifyRollback = () => {
location = opts.getLocation();
subscribers.forEach(
(subscriber) => subscriber({ location, action: { type: "ROLLBACK" } })
);
};
const tryNavigation = async ({
task,
navigateOpts,
...actionInfo
}) => {
var _a, _b;
const ignoreBlocker = (navigateOpts == null ? void 0 : navigateOpts.ignoreBlocker) ?? false;
if (!ignoreBlocker && typeof document !== "undefined" && blockers.length) {
if (ignoreBlocker) {
task();
return;
}
const blockers = ((_a = opts.getBlockers) == null ? void 0 : _a.call(opts)) ?? [];
const isPushOrReplace = actionInfo.type === "PUSH" || actionInfo.type === "REPLACE";
if (typeof document !== "undefined" && blockers.length && isPushOrReplace) {
for (const blocker of blockers) {
const allowed = await blocker();
if (!allowed) {
(_a = opts.onBlocked) == null ? void 0 : _a.call(opts, notify);
const nextLocation = parseHref(actionInfo.path, actionInfo.state);
const isBlocked = await blocker.blockerFn({
currentLocation: location,
nextLocation,
action: actionInfo.type
});
if (isBlocked) {
(_b = opts.onBlocked) == null ? void 0 : _b.call(opts, _notifyRollback);
return;

@@ -51,45 +61,67 @@ }

state = assignKey(state);
tryNavigation(() => {
opts.pushState(path, state);
notify();
}, navigateOpts);
tryNavigation({
task: () => {
opts.pushState(path, state);
notify({ type: "PUSH" });
},
navigateOpts,
type: "PUSH",
path,
state
});
},
replace: (path, state, navigateOpts) => {
state = assignKey(state);
tryNavigation(() => {
opts.replaceState(path, state);
notify();
}, navigateOpts);
tryNavigation({
task: () => {
opts.replaceState(path, state);
notify({ type: "REPLACE" });
},
navigateOpts,
type: "REPLACE",
path,
state
});
},
go: (index, navigateOpts) => {
tryNavigation(() => {
opts.go(index);
notify();
}, navigateOpts);
tryNavigation({
task: () => {
opts.go(index);
notify({ type: "GO", index });
},
navigateOpts,
type: "GO"
});
},
back: (navigateOpts) => {
tryNavigation(() => {
opts.back();
notify();
}, navigateOpts);
tryNavigation({
task: () => {
opts.back((navigateOpts == null ? void 0 : navigateOpts.ignoreBlocker) ?? false);
notify({ type: "BACK" });
},
navigateOpts,
type: "BACK"
});
},
forward: (navigateOpts) => {
tryNavigation(() => {
opts.forward();
notify();
}, navigateOpts);
tryNavigation({
task: () => {
opts.forward((navigateOpts == null ? void 0 : navigateOpts.ignoreBlocker) ?? false);
notify({ type: "FORWARD" });
},
navigateOpts,
type: "FORWARD"
});
},
createHref: (str) => opts.createHref(str),
block: (blocker) => {
blockers.push(blocker);
if (blockers.length === 1) {
addEventListener(beforeUnloadEvent, beforeUnloadListener, {
capture: true
});
}
var _a;
if (!opts.setBlockers) return () => {
};
const blockers = ((_a = opts.getBlockers) == null ? void 0 : _a.call(opts)) ?? [];
opts.setBlockers([...blockers, blocker]);
return () => {
blockers = blockers.filter((b) => b !== blocker);
if (!blockers.length) {
stopBlocking();
}
var _a2, _b;
const blockers2 = ((_a2 = opts.getBlockers) == null ? void 0 : _a2.call(opts)) ?? [];
(_b = opts.setBlockers) == null ? void 0 : _b.call(opts, blockers2.filter((b) => b !== blocker));
};

@@ -121,2 +153,5 @@ },

const originalReplaceState = win.history.replaceState;
let blockers = [];
const _getBlockers = () => blockers;
const _setBlockers = (newBlockers) => blockers = newBlockers;
const createHref = (opts == null ? void 0 : opts.createHref) ?? ((path) => path);

@@ -129,2 +164,5 @@ const parseLocation = (opts == null ? void 0 : opts.parseLocation) ?? (() => parseHref(

let rollbackLocation;
let ignoreNextPop = false;
let skipBlockerNextPop = false;
let ignoreNextBeforeUnload = false;
const getLocation = () => currentLocation;

@@ -165,4 +203,59 @@ let next;

currentLocation = parseLocation();
history.notify();
history.notify({ type: "POP" });
};
const onPushPopEvent = async () => {
if (ignoreNextPop) {
ignoreNextPop = false;
return;
}
if (skipBlockerNextPop) {
skipBlockerNextPop = false;
} else {
const blockers2 = _getBlockers();
if (typeof document !== "undefined" && blockers2.length) {
for (const blocker of blockers2) {
const nextLocation = parseLocation();
const isBlocked = await blocker.blockerFn({
currentLocation,
nextLocation,
action: "POP"
});
if (isBlocked) {
ignoreNextPop = true;
win.history.go(1);
history.notify({ type: "POP" });
return;
}
}
}
}
currentLocation = parseLocation();
history.notify({ type: "POP" });
};
const onBeforeUnload = (e) => {
if (ignoreNextBeforeUnload) {
ignoreNextBeforeUnload = false;
return;
}
let shouldBlock = false;
const blockers2 = _getBlockers();
if (typeof document !== "undefined" && blockers2.length) {
for (const blocker of blockers2) {
const shouldHaveBeforeUnload = blocker.enableBeforeUnload ?? true;
if (shouldHaveBeforeUnload === true) {
shouldBlock = true;
break;
}
if (typeof shouldHaveBeforeUnload === "function" && shouldHaveBeforeUnload() === true) {
shouldBlock = true;
break;
}
}
}
if (shouldBlock) {
e.preventDefault();
return e.returnValue = "";
}
return;
};
const history = createHistory({

@@ -173,4 +266,12 @@ getLocation,

replaceState: (href, state) => queueHistoryAction("replace", href, state),
back: () => win.history.back(),
forward: () => win.history.forward(),
back: (ignoreBlocker) => {
if (ignoreBlocker) skipBlockerNextPop = true;
ignoreNextBeforeUnload = true;
return win.history.back();
},
forward: (ignoreBlocker) => {
if (ignoreBlocker) skipBlockerNextPop = true;
ignoreNextBeforeUnload = true;
win.history.forward();
},
go: (n) => win.history.go(n),

@@ -182,4 +283,6 @@ createHref: (href) => createHref(href),

win.history.replaceState = originalReplaceState;
win.removeEventListener(pushStateEvent, onPushPop);
win.removeEventListener(popStateEvent, onPushPop);
win.removeEventListener(beforeUnloadEvent, onBeforeUnload, {
capture: true
});
win.removeEventListener(popStateEvent, onPushPopEvent);
},

@@ -191,6 +294,8 @@ onBlocked: (onUpdate) => {

}
}
},
getBlockers: _getBlockers,
setBlockers: _setBlockers
});
win.addEventListener(pushStateEvent, onPushPop);
win.addEventListener(popStateEvent, onPushPop);
win.addEventListener(beforeUnloadEvent, onBeforeUnload, { capture: true });
win.addEventListener(popStateEvent, onPushPopEvent);
win.history.pushState = function(...args) {

@@ -197,0 +302,0 @@ const res = originalPushState.apply(win.history, args);

{
"name": "@tanstack/history",
"version": "1.87.6",
"version": "1.90.0",
"description": "Modern and scalable routing for React applications",

@@ -5,0 +5,0 @@ "author": "Tanner Linsley",

@@ -8,7 +8,22 @@ // While the public API was clearly inspired by the "history" npm package,

}
type SubscriberHistoryAction =
| {
type: HistoryAction | 'ROLLBACK'
}
| {
type: 'GO'
index: number
}
type SubscriberArgs = {
location: HistoryLocation
action: SubscriberHistoryAction
}
export interface RouterHistory {
location: HistoryLocation
length: number
subscribers: Set<(opts: { location: HistoryLocation }) => void>
subscribe: (cb: (opts: { location: HistoryLocation }) => void) => () => void
subscribers: Set<(opts: SubscriberArgs) => void>
subscribe: (cb: (opts: SubscriberArgs) => void) => () => void
push: (path: string, state?: any, navigateOpts?: NavigateOptions) => void

@@ -20,6 +35,6 @@ replace: (path: string, state?: any, navigateOpts?: NavigateOptions) => void

createHref: (href: string) => string
block: (blocker: BlockerFn) => () => void
block: (blocker: NavigationBlocker) => () => void
flush: () => void
destroy: () => void
notify: () => void
notify: (action: SubscriberHistoryAction) => void
_ignoreSubscribers?: boolean

@@ -45,22 +60,43 @@ }

export type BlockerFn = () =>
| Promise<ShouldAllowNavigation>
| ShouldAllowNavigation
export type HistoryAction =
| 'PUSH'
| 'POP'
| 'REPLACE'
| 'FORWARD'
| 'BACK'
| 'GO'
const pushStateEvent = 'pushstate'
const popStateEvent = 'popstate'
const beforeUnloadEvent = 'beforeunload'
const beforeUnloadListener = (event: Event) => {
event.preventDefault()
// @ts-expect-error
return (event.returnValue = '')
export type BlockerFnArgs = {
currentLocation: HistoryLocation
nextLocation: HistoryLocation
action: HistoryAction
}
const stopBlocking = () => {
removeEventListener(beforeUnloadEvent, beforeUnloadListener, {
capture: true,
})
export type BlockerFn = (
args: BlockerFnArgs,
) => Promise<ShouldAllowNavigation> | ShouldAllowNavigation
export type NavigationBlocker = {
blockerFn: BlockerFn
enableBeforeUnload?: (() => boolean) | boolean
}
type TryNavigateArgs = {
task: () => void
type: 'PUSH' | 'REPLACE' | 'BACK' | 'FORWARD' | 'GO'
navigateOpts?: NavigateOptions
} & (
| {
type: 'PUSH' | 'REPLACE'
path: string
state: any
}
| {
type: 'BACK' | 'FORWARD' | 'GO'
}
)
const popStateEvent = 'popstate'
const beforeUnloadEvent = 'beforeunload'
export function createHistory(opts: {

@@ -72,4 +108,4 @@ getLocation: () => HistoryLocation

go: (n: number) => void
back: () => void
forward: () => void
back: (ignoreBlocker: boolean) => void
forward: (ignoreBlocker: boolean) => void
createHref: (path: string) => string

@@ -79,22 +115,44 @@ flush?: () => void

onBlocked?: (onUpdate: () => void) => void
getBlockers?: () => Array<NavigationBlocker>
setBlockers?: (blockers: Array<NavigationBlocker>) => void
}): RouterHistory {
let location = opts.getLocation()
const subscribers = new Set<(opts: { location: HistoryLocation }) => void>()
let blockers: Array<BlockerFn> = []
const subscribers = new Set<(opts: SubscriberArgs) => void>()
const notify = () => {
const notify = (action: SubscriberHistoryAction) => {
location = opts.getLocation()
subscribers.forEach((subscriber) => subscriber({ location }))
subscribers.forEach((subscriber) => subscriber({ location, action }))
}
const tryNavigation = async (
task: () => void,
navigateOpts?: NavigateOptions,
) => {
const _notifyRollback = () => {
location = opts.getLocation()
subscribers.forEach((subscriber) =>
subscriber({ location, action: { type: 'ROLLBACK' } }),
)
}
const tryNavigation = async ({
task,
navigateOpts,
...actionInfo
}: TryNavigateArgs) => {
const ignoreBlocker = navigateOpts?.ignoreBlocker ?? false
if (!ignoreBlocker && typeof document !== 'undefined' && blockers.length) {
if (ignoreBlocker) {
task()
return
}
const blockers = opts.getBlockers?.() ?? []
const isPushOrReplace =
actionInfo.type === 'PUSH' || actionInfo.type === 'REPLACE'
if (typeof document !== 'undefined' && blockers.length && isPushOrReplace) {
for (const blocker of blockers) {
const allowed = await blocker()
if (!allowed) {
opts.onBlocked?.(notify)
const nextLocation = parseHref(actionInfo.path, actionInfo.state)
const isBlocked = await blocker.blockerFn({
currentLocation: location,
nextLocation,
action: actionInfo.type,
})
if (isBlocked) {
opts.onBlocked?.(_notifyRollback)
return

@@ -116,3 +174,3 @@ }

subscribers,
subscribe: (cb: (opts: { location: HistoryLocation }) => void) => {
subscribe: (cb: (opts: SubscriberArgs) => void) => {
subscribers.add(cb)

@@ -126,48 +184,65 @@

state = assignKey(state)
tryNavigation(() => {
opts.pushState(path, state)
notify()
}, navigateOpts)
tryNavigation({
task: () => {
opts.pushState(path, state)
notify({ type: 'PUSH' })
},
navigateOpts,
type: 'PUSH',
path,
state,
})
},
replace: (path, state, navigateOpts) => {
state = assignKey(state)
tryNavigation(() => {
opts.replaceState(path, state)
notify()
}, navigateOpts)
tryNavigation({
task: () => {
opts.replaceState(path, state)
notify({ type: 'REPLACE' })
},
navigateOpts,
type: 'REPLACE',
path,
state,
})
},
go: (index, navigateOpts) => {
tryNavigation(() => {
opts.go(index)
notify()
}, navigateOpts)
tryNavigation({
task: () => {
opts.go(index)
notify({ type: 'GO', index })
},
navigateOpts,
type: 'GO',
})
},
back: (navigateOpts) => {
tryNavigation(() => {
opts.back()
notify()
}, navigateOpts)
tryNavigation({
task: () => {
opts.back(navigateOpts?.ignoreBlocker ?? false)
notify({ type: 'BACK' })
},
navigateOpts,
type: 'BACK',
})
},
forward: (navigateOpts) => {
tryNavigation(() => {
opts.forward()
notify()
}, navigateOpts)
tryNavigation({
task: () => {
opts.forward(navigateOpts?.ignoreBlocker ?? false)
notify({ type: 'FORWARD' })
},
navigateOpts,
type: 'FORWARD',
})
},
createHref: (str) => opts.createHref(str),
block: (blocker) => {
blockers.push(blocker)
if (!opts.setBlockers) return () => {}
const blockers = opts.getBlockers?.() ?? []
opts.setBlockers([...blockers, blocker])
if (blockers.length === 1) {
addEventListener(beforeUnloadEvent, beforeUnloadListener, {
capture: true,
})
}
return () => {
blockers = blockers.filter((b) => b !== blocker)
if (!blockers.length) {
stopBlocking()
}
const blockers = opts.getBlockers?.() ?? []
opts.setBlockers?.(blockers.filter((b) => b !== blocker))
}

@@ -219,2 +294,7 @@ },

let blockers: Array<NavigationBlocker> = []
const _getBlockers = () => blockers
const _setBlockers = (newBlockers: Array<NavigationBlocker>) =>
(blockers = newBlockers)
const createHref = opts?.createHref ?? ((path) => path)

@@ -232,2 +312,6 @@ const parseLocation =

let ignoreNextPop = false
let skipBlockerNextPop = false
let ignoreNextBeforeUnload = false
const getLocation = () => currentLocation

@@ -305,5 +389,72 @@

currentLocation = parseLocation()
history.notify()
history.notify({ type: 'POP' })
}
const onPushPopEvent = async () => {
if (ignoreNextPop) {
ignoreNextPop = false
return
}
if (skipBlockerNextPop) {
skipBlockerNextPop = false
} else {
const blockers = _getBlockers()
if (typeof document !== 'undefined' && blockers.length) {
for (const blocker of blockers) {
const nextLocation = parseLocation()
const isBlocked = await blocker.blockerFn({
currentLocation,
nextLocation,
action: 'POP',
})
if (isBlocked) {
ignoreNextPop = true
win.history.go(1)
history.notify({ type: 'POP' })
return
}
}
}
}
currentLocation = parseLocation()
history.notify({ type: 'POP' })
}
const onBeforeUnload = (e: BeforeUnloadEvent) => {
if (ignoreNextBeforeUnload) {
ignoreNextBeforeUnload = false
return
}
let shouldBlock = false
// If one blocker has a non-disabled beforeUnload, we should block
const blockers = _getBlockers()
if (typeof document !== 'undefined' && blockers.length) {
for (const blocker of blockers) {
const shouldHaveBeforeUnload = blocker.enableBeforeUnload ?? true
if (shouldHaveBeforeUnload === true) {
shouldBlock = true
break
}
if (
typeof shouldHaveBeforeUnload === 'function' &&
shouldHaveBeforeUnload() === true
) {
shouldBlock = true
break
}
}
}
if (shouldBlock) {
e.preventDefault()
return (e.returnValue = '')
}
return
}
const history = createHistory({

@@ -314,4 +465,12 @@ getLocation,

replaceState: (href, state) => queueHistoryAction('replace', href, state),
back: () => win.history.back(),
forward: () => win.history.forward(),
back: (ignoreBlocker) => {
if (ignoreBlocker) skipBlockerNextPop = true
ignoreNextBeforeUnload = true
return win.history.back()
},
forward: (ignoreBlocker) => {
if (ignoreBlocker) skipBlockerNextPop = true
ignoreNextBeforeUnload = true
win.history.forward()
},
go: (n) => win.history.go(n),

@@ -323,4 +482,6 @@ createHref: (href) => createHref(href),

win.history.replaceState = originalReplaceState
win.removeEventListener(pushStateEvent, onPushPop)
win.removeEventListener(popStateEvent, onPushPop)
win.removeEventListener(beforeUnloadEvent, onBeforeUnload, {
capture: true,
})
win.removeEventListener(popStateEvent, onPushPopEvent)
},

@@ -336,9 +497,11 @@ onBlocked: (onUpdate) => {

},
getBlockers: _getBlockers,
setBlockers: _setBlockers,
})
win.addEventListener(pushStateEvent, onPushPop)
win.addEventListener(popStateEvent, onPushPop)
win.addEventListener(beforeUnloadEvent, onBeforeUnload, { capture: true })
win.addEventListener(popStateEvent, onPushPopEvent)
win.history.pushState = function (...args: Array<any>) {
const res = originalPushState.apply(win.history, args)
const res = originalPushState.apply(win.history, args as any)
if (!history._ignoreSubscribers) onPushPop()

@@ -349,3 +512,3 @@ return res

win.history.replaceState = function (...args: Array<any>) {
const res = originalReplaceState.apply(win.history, args)
const res = originalReplaceState.apply(win.history, args as any)
if (!history._ignoreSubscribers) onPushPop()

@@ -352,0 +515,0 @@ return res

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc