fix: queue pushpop actions
When running push/pop actions like back, forward or go in quick succession the ignoreBlocker functionality does not work since the event which flips back the ignore flag does not run right away and the second call to back for example runs before the event meaning the flag is not reset correctly when the second pushpop event is called. This PR fixes this by implementing a promise based queue system
It also ensure that ignoreBlocker also works with go
Summary by CodeRabbit
-
Improvements
- Enhanced scheduling of back/forward/go navigation to make transitions more reliable and to avoid races.
- Improved coordination with navigation blockers and state synchronization for consistent history behavior.
- Added an option to bypass blockers for certain navigations when needed.
-
Breaking Changes
- Public navigation API signature updated; create-memory-history options signature adjusted.
βοΈ Tip: You can customize this high-level summary in your review settings.
View your CI Pipeline Execution β for commit d8e1933750cad5c0d52d78e343d2bad183659407
| Command | Status | Duration | Result |
|---|---|---|---|
nx affected --targets=test:eslint,test:unit,tes... |
β Succeeded | 8m 31s | View β |
nx run-many --target=build --exclude=examples/*... |
β Succeeded | 1m 27s | View β |
βοΈ Nx Cloud last updated this comment at 2025-12-04 11:50:39 UTC
Walkthrough
Adds an optional ignoreBlocker boolean to the public go method, introduces a scheduler to serialize/backpressure BACK/FORWARD/GO browser actions, reworks pop handling to coordinate blockers and synthetic recovery navigations, and inlines the createMemoryHistory opts type.
Changes
| Cohort / File(s) | Summary |
|---|---|
History core β scheduling & blockers packages/history/src/index.ts |
Public go(n, ignoreBlocker) signature added. Introduces schedulePushPopAction to serialize/coalesce back/forward/go browser actions; adds state: scheduledPushPopAction, resolveScheduledPushPopAction, nextOnPushPop. Reworks onPushPopEvent to consult scheduled state, evaluate blockers, perform synthetic corrective history.go(...) when blocked, and ensure scheduling state is reset in a finally block. |
API surface / types packages/history/src/index.ts |
createMemoryHistory opts type changed to an inline { initialEntries: Array<string>; initialIndex?: number }. Public history input type for createHistory updates go signature to accept ignoreBlocker. Minor refactor/formatting adjustments and local type tightening. |
Sequence Diagram
sequenceDiagram
participant App as Application
participant History as History API
participant Scheduler as Scheduler
participant Browser as Browser History
participant Blockers as Blocker Chain
participant Subscribers as Subscribers
App->>History: call go/back/forward (maybe ignoreBlocker)
History->>Scheduler: schedulePushPopAction(action, delta, ignoreBlocker)
Scheduler->>Browser: perform browser.history call (push/pop/go) [serialized]
Browser->>History: popstate -> onPushPopEvent()
History->>Scheduler: consult nextOnPushPop / scheduled state
alt skip blocker (ignoreBlocker / skip flag)
History->>Subscribers: notify subscribers (no blocker evaluation)
else
History->>Blockers: evaluate blocker chain
alt blockers allow
History->>Subscribers: notify subscribers
else blockers block
History->>Browser: synthetic history.go(recoveryDelta) to revert
History->>History: set ignoreNextPop and update state
History->>Subscribers: notify of blocked outcome
end
end
Scheduler->>Scheduler: finally: reset scheduledPushPopAction / resolveScheduledPushPopAction / nextOnPushPop
Estimated code review effort
π― 4 (Complex) | β±οΈ ~45 minutes
- Review scheduling/state machine correctness:
scheduledPushPopAction,resolveScheduledPushPopAction,nextOnPushPop,ignoreNextPop. - Verify blocker evaluation logic and synthetic recovery
history.go(...)avoid event loops and maintain consistent indices. - Confirm public API/type change (
gosignature,createMemoryHistoryopts) is compatible with external callers and documentation.
Poem
π° I hopped a step and timed the tide,
I queued my moves and let blockers hide.
A gentle nudge when paths were stuck,
Schedules hum and subscribers luck.
Hop on β history's neat and spry.
Pre-merge checks and finishing touches
β Failed checks (1 warning)
| Check name | Status | Explanation | Resolution |
|---|---|---|---|
| Docstring Coverage | β οΈ Warning | Docstring coverage is 66.67% which is insufficient. The required threshold is 80.00%. | You can run @coderabbitai generate docstrings to improve docstring coverage. |
β Passed checks (2 passed)
| Check name | Status | Explanation |
|---|---|---|
| Description Check | β Passed | Check skipped - CodeRabbitβs high-level summary is enabled. |
| Title check | β Passed | The title 'fix: queue pushpop actions' directly addresses the main change: implementing a queue system for serializing push/pop navigation actions to fix the ignoreBlocker bypass bug. |
β¨ Finishing touches
- [ ] π Generate docstrings
π§ͺ Generate unit tests (beta)
- [ ] Create PR with unit tests
- [ ] Post copyable unit tests in a comment
π Recent review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
π₯ Commits
Reviewing files that changed from the base of the PR and between f1723d15b2075fccfc8c09c2bfef587485e468b8 and d8e1933750cad5c0d52d78e343d2bad183659407.
π Files selected for processing (1)
-
packages/history/src/index.ts(5 hunks)
π§° Additional context used
π Path-based instructions (1)
**/*.{ts,tsx}
π CodeRabbit inference engine (AGENTS.md)
Use TypeScript strict mode with extensive type safety throughout the codebase
Files:
-
packages/history/src/index.ts
π Additional comments (6)
packages/history/src/index.ts (6)
99-115: LGTM - Signature updates for ignoreBlocker propagation.The
ignoreBlockerparameter is now consistently required acrossgo,back, andforwardin the internalcreateHistoryopts type, enabling proper blocker bypass semantics throughout navigation paths.
204-213: LGTM - ignoreBlocker correctly propagated to opts.go.The
ignoreBlockeroption is properly extracted and passed through to the underlying history implementation.
396-428: Queue serialization looks correct after the race condition fix.The chained action path (lines 416-426) now properly updates
scheduledPushPopActionwith a new promise, ensuring subsequent callers see the correct queued state. Thefinallyblock cleanup at lines 484-491 will correctly reset state after each action completes.Minor observation:
ignoreNextBeforeUnloadis set immediately (line 406) even when an action is queued, which means a later-queued action'signoreBlockervalue will overwrite an earlier one. This seems acceptable sincebeforeUnloadevents are edge cases and the final state reflects the latest user intent.
535-549: LGTM - Consistent integration with the scheduling layer.The
back,forward, andgomethods correctly integrate withschedulePushPopAction, properly distinguishingisGofor accurate action type detection inonPushPopEvent.
484-491: LGTM - Finally block safely resets scheduling state.The conditional check on
resolveScheduledPushPopActionensures the cleanup only runs when there's an active scheduled action. This correctly handles the case where the corrective navigation's popstate is ignored viaignoreNextPop.
472-476: Verify the notification behavior when navigation is blocked.After blocking navigation with
go(-delta), the code callshistory.notify(notify)with the original action type. Subscribers need to verify this notification is interpreted correctlyβif subscribers treat this as "navigation completed," it could cause state inconsistencies. Clarify whether this notification is intentional for blocker UI updates or if it should indicate a blocked/failed navigation instead.
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.
Comment @coderabbitai help to get the list of available commands and usage tips.
More templates
- tanstack-router-react-example-authenticated-routes
- tanstack-router-react-example-authenticated-routes-firebase
- tanstack-router-react-example-basic
- tanstack-router-react-example-basic-default-search-params
- tanstack-router-react-example-basic-devtools-panel
- tanstack-router-react-example-basic-file-based
- tanstack-router-react-example-basic-non-nested-devtools
- tanstack-router-react-example-react-query
- tanstack-router-react-example-basic-react-query-file-based
- tanstack-router-react-example-basic-ssr-file-based
- tanstack-router-react-example-basic-ssr-streaming-file-based
- tanstack-router-react-example-basic-virtual-file-based
- tanstack-router-react-example-basic-virtual-inside-file-based
- tanstack-router-react-example-deferred-data
- tanstack-router-i18n-paraglide
- tanstack-router-react-example-kitchen-sink
- tanstack-router-react-example-kitchen-sink-file-based
- tanstack-router-react-example-kitchen-sink-react-query
- tanstack-router-react-example-kitchen-sink-react-query-file-based
- tanstack-router-react-example-large-file-based
- tanstack-router-react-example-location-masking
- tanstack-router-react-example-navigation-blocking
- tanstack-router-react-example-quickstart
- tanstack-router-react-example-quickstart-esbuild-file-based
- tanstack-router-react-example-quickstart-file-based
- tanstack-router-react-example-quickstart-rspack-file-based
- tanstack-router-react-example-quickstart-webpack-file-based
- router-monorepo-react-query
- router-mono-simple
- router-mono-simple-lazy
- tanstack-router-react-example-scroll-restoration
- tanstack-search-validator-adapters
- tanstack-start-example-bare
- tanstack-start-example-basic
- tanstack-start-example-basic-auth
- tanstack-router-react-example-basic-authjs
- tanstack-start-example-basic-cloudflare
- tanstack-start-example-basic-react-query
- tanstack-start-example-basic-static
- tanstack-start-bun-hosting
- tanstack-start-example-clerk-basic
- tanstack-start-example-convex-trellaux
- tanstack-start-example-counter
- tanstack-start-i18n-paraglide
- tanstack-start-example-large
- tanstack-start-example-material-ui
- tanstack-start-streaming-data-from-server-functions
- tanstack-start-example-supabase-basic
- tanstack-start-tailwind-v4
- tanstack-start-example-trellaux
- tanstack-start-example-workos
- tanstack-router-react-example-view-transitions
- tanstack-router-react-example-with-framer-motion
- tanstack-router-react-example-with-trpc
- tanstack-router-react-example-with-trpc-react-query
- tanstack-router-solid-example-authenticated-routes
- tanstack-router-solid-example-authenticated-routes-firebase
- tanstack-router-solid-example-basic
- tanstack-router-solid-example-basic-default-search-params
- tanstack-router-solid-example-basic-devtools-panel
- tanstack-router-solid-example-basic-file-based
- tanstack-router-solid-example-basic-non-nested-devtools
- tanstack-router-solid-example-basic-solid-query
- tanstack-router-solid-example-basic-solid-query-file-based
- tanstack-router-solid-example-basic-ssr-file-based
- tanstack-router-solid-example-basic-ssr-streaming-file-based
- tanstack-router-solid-example-basic-virtual-file-based
- tanstack-router-solid-example-basic-virtual-inside-file-based
- tanstack-router-solid-example-deferred-data
- tanstack-router-solid-i18n-paraglide
- tanstack-router-solid-example-kitchen-sink
- tanstack-router-solid-example-kitchen-sink-file-based
- tanstack-router-solid-example-kitchen-sink-solid-query
- tanstack-router-solid-example-kitchen-sink-solid-query-file-based
- tanstack-router-solid-example-large-file-based
- tanstack-router-solid-example-location-masking
- tanstack-router-solid-example-navigation-blocking
- tanstack-router-solid-example-quickstart
- tanstack-router-solid-example-quickstart-esbuild-file-based
- tanstack-router-solid-example-quickstart-file-based
- tanstack-router-solid-example-quickstart-rspack-file-based
- tanstack-router-solid-example-quickstart-webpack-file-based
- router-solid-mono-simple
- router-solid-mono-simple-lazy
- router-solid-monorepo-solid-query
- tanstack-router-solid-example-scroll-restoration
- tanstack-solid-router-search-validator-adapters
- tanstack-solid-start-example-basic
- tanstack-solid-start-example-basic-auth
- tanstack-solid-start-example-basic-authjs
- tanstack-solid-start-example-basic-cloudflare
- tanstack-solid-start-example-basic-netlify
- tanstack-solid-start-example-basic-nitro
- tanstack-start-example-basic-solid-query
- tanstack-solid-start-example-basic-static
- tanstack-solid-start-bun-hosting
- tanstack-solid-start-example-convex-better-auth
- tanstack-solid-start-example-counter
- tanstack-solid-start-i18n-paraglide
- tanstack-solid-start-example-large
- tanstack-solid-start-streaming-data-from-server-functions
- tanstack-solid-start-example-supabase-basic
- tanstack-solid-start-tailwind-v4
- tanstack-router-solid-example-view-transitions
- tanstack-router-solid-example-with-framer-motion
- tanstack-router-solid-example-with-trpc
@tanstack/arktype-adapter
npm i https://pkg.pr.new/TanStack/router/@tanstack/arktype-adapter@6019
@tanstack/directive-functions-plugin
npm i https://pkg.pr.new/TanStack/router/@tanstack/directive-functions-plugin@6019
@tanstack/eslint-plugin-router
npm i https://pkg.pr.new/TanStack/router/@tanstack/eslint-plugin-router@6019
@tanstack/history
npm i https://pkg.pr.new/TanStack/router/@tanstack/history@6019
@tanstack/nitro-v2-vite-plugin
npm i https://pkg.pr.new/TanStack/router/@tanstack/nitro-v2-vite-plugin@6019
@tanstack/react-router
npm i https://pkg.pr.new/TanStack/router/@tanstack/react-router@6019
@tanstack/react-router-devtools
npm i https://pkg.pr.new/TanStack/router/@tanstack/react-router-devtools@6019
@tanstack/react-router-ssr-query
npm i https://pkg.pr.new/TanStack/router/@tanstack/react-router-ssr-query@6019
@tanstack/react-start
npm i https://pkg.pr.new/TanStack/router/@tanstack/react-start@6019
@tanstack/react-start-client
npm i https://pkg.pr.new/TanStack/router/@tanstack/react-start-client@6019
@tanstack/react-start-server
npm i https://pkg.pr.new/TanStack/router/@tanstack/react-start-server@6019
@tanstack/router-cli
npm i https://pkg.pr.new/TanStack/router/@tanstack/router-cli@6019
@tanstack/router-core
npm i https://pkg.pr.new/TanStack/router/@tanstack/router-core@6019
@tanstack/router-devtools
npm i https://pkg.pr.new/TanStack/router/@tanstack/router-devtools@6019
@tanstack/router-devtools-core
npm i https://pkg.pr.new/TanStack/router/@tanstack/router-devtools-core@6019
@tanstack/router-generator
npm i https://pkg.pr.new/TanStack/router/@tanstack/router-generator@6019
@tanstack/router-plugin
npm i https://pkg.pr.new/TanStack/router/@tanstack/router-plugin@6019
@tanstack/router-ssr-query-core
npm i https://pkg.pr.new/TanStack/router/@tanstack/router-ssr-query-core@6019
@tanstack/router-utils
npm i https://pkg.pr.new/TanStack/router/@tanstack/router-utils@6019
@tanstack/router-vite-plugin
npm i https://pkg.pr.new/TanStack/router/@tanstack/router-vite-plugin@6019
@tanstack/server-functions-plugin
npm i https://pkg.pr.new/TanStack/router/@tanstack/server-functions-plugin@6019
@tanstack/solid-router
npm i https://pkg.pr.new/TanStack/router/@tanstack/solid-router@6019
@tanstack/solid-router-devtools
npm i https://pkg.pr.new/TanStack/router/@tanstack/solid-router-devtools@6019
@tanstack/solid-router-ssr-query
npm i https://pkg.pr.new/TanStack/router/@tanstack/solid-router-ssr-query@6019
@tanstack/solid-start
npm i https://pkg.pr.new/TanStack/router/@tanstack/solid-start@6019
@tanstack/solid-start-client
npm i https://pkg.pr.new/TanStack/router/@tanstack/solid-start-client@6019
@tanstack/solid-start-server
npm i https://pkg.pr.new/TanStack/router/@tanstack/solid-start-server@6019
@tanstack/start-client-core
npm i https://pkg.pr.new/TanStack/router/@tanstack/start-client-core@6019
@tanstack/start-plugin-core
npm i https://pkg.pr.new/TanStack/router/@tanstack/start-plugin-core@6019
@tanstack/start-server-core
npm i https://pkg.pr.new/TanStack/router/@tanstack/start-server-core@6019
@tanstack/start-static-server-functions
npm i https://pkg.pr.new/TanStack/router/@tanstack/start-static-server-functions@6019
@tanstack/start-storage-context
npm i https://pkg.pr.new/TanStack/router/@tanstack/start-storage-context@6019
@tanstack/valibot-adapter
npm i https://pkg.pr.new/TanStack/router/@tanstack/valibot-adapter@6019
@tanstack/virtual-file-routes
npm i https://pkg.pr.new/TanStack/router/@tanstack/virtual-file-routes@6019
@tanstack/zod-adapter
npm i https://pkg.pr.new/TanStack/router/@tanstack/zod-adapter@6019
commit: d8e1933