Install

Works with Drupal: ^10.3 || ^11

Using Composer to manage Drupal site dependencies

Alternative installation files

Download tar.gz 683.23 KB
MD5: 293d220c6f583dc1fdb78913ac4439aa
SHA-1: b2fe7b838d3c76df12a70c7fa9119d8b9c5ffb7b
SHA-256: 9f84d34a7a6783cf2cdd5e90e645aa3e4f079502487b4cf924d9f64233042b41
Download zip 1.17 MB
MD5: c6a34ba7abc0fa807c472a57f5e02c40
SHA-1: 82d6fcda80a6fa18835186f300e52709be03e7ac
SHA-256: eacf4fcac1b20bb092b0ff1e3a5d0f3db7fa33e6bef1ee58af972e8f84ab0064

Release notes

Changed

  • Core docblocks and type definitions now describe collaborators by their contract role rather than naming specific downstream themes, modules, or sites. Pure documentation change; no runtime behaviour is affected.

Fixed

  • Cross-route Back navigation no longer renders an empty page. classifyScope(target) previously compared target.pathname against location.pathname, but on popstate the browser has already moved location to the target by the time the comparison runs - the function compared the URL to itself and always returned 'content', telling the server "no route change, return content only" when the user had actually crossed routes. The auto-classify path in NavigationController.start now passes the navigation state store's last committed URL as the source so the diff captures the actual route transition. Click callers default to location.href, which is still correct for them. computeStaleKeys got the same source-URL treatment so the X-SDX-Stale header reflects the prior page on popstate as well.
  • installPopstateListener no longer traps the browser back button. The handler used to call history.pushState(location.href) unconditionally on every popstate, intended as an address-bar mask while the unsaved-changes confirm dialog awaited a decision. On the no-dirty-page path - which is the vast majority of navigations - confirmNavigation() short-circuits to a pre-resolved true, but the trap pushState fired anyway. The trap entry sat at currentIndex+1 carrying the same URL as the entry the browser had just navigated to, so the next Back press consumed the trap right back into itself. Users found themselves oscillating between two history entries pointing at the same URL, unable to navigate further back than one step regardless of stack depth. The trap is now gated behind hasUnsavedChanges(). The fast path dispatches the SPA fetch with no history manipulation; the slow path keeps the trap-push but adds a closure-scoped rewind via history.back() on cancel, with a reentry guard that swallows the synthetic popstate so the rewind does not re-dispatch the SPA fetch. A second guard drops additional back presses while a confirm dialog is open, since stacking dialogs would be ambiguous about which decision targets which destination.
Created by: vzsigmond
Created on: 5 May 2026 at 08:43 UTC
Last updated: 5 May 2026 at 08:43 UTC
Bug fixes

Other releases