@@ -75,29 +75,30 @@ export class BrowserNavigationInstrumentation extends InstrumentationBase<Browse
7575 */
7676 private _onSoftNavigation ( changeState : string | null | undefined , navigationEvent ?: any ) {
7777 const referrer = this . oldUrl ;
78- // For Navigation API events, always process as they may contain important state changes
79- // For other events, skip only if URL hasn't changed AND it's not a hash-only change
80- if ( changeState !== 'navigate' && referrer === location . href ) {
81- // Still allow hash changes even if URLs appear the same (normalization issues)
82- const isHashChange = this . _isHashChange ( referrer , location . href ) || changeState === 'hashchange' ;
83- if ( ! isHashChange ) {
84- return ;
85- }
78+ const currentUrl = ( changeState === 'navigate' && navigationEvent ?. destination ?. url )
79+ ? navigationEvent . destination . url
80+ : location . href ;
81+
82+ if ( referrer === currentUrl ) {
83+ return ;
8684 }
87- const navType = this . _mapChangeStateToType ( changeState ) ;
88- const sameDocument = this . _determineSameDocument ( changeState , navigationEvent , referrer , window . location . href ) ;
89- const hashChange = this . _determineHashChange ( changeState , navigationEvent , referrer , window . location . href ) ;
85+
86+ const navType = this . _mapChangeStateToType ( changeState , navigationEvent ) ;
87+ const sameDocument = this . _determineSameDocument ( changeState , navigationEvent , referrer , currentUrl ) ;
88+ const hashChange = this . _determineHashChange ( changeState , navigationEvent , referrer , currentUrl ) ;
9089 const navLogRecord : LogRecord = {
9190 eventName : EVENT_NAME ,
9291 attributes : {
93- [ URL_FULL_ATTRIBUTE ] : this . _sanitizeUrl ( window . location . href ) ,
92+ [ URL_FULL_ATTRIBUTE ] : this . _sanitizeUrl ( currentUrl ) ,
9493 [ NAV_SAME_DOCUMENT ] : sameDocument ,
9594 [ NAV_HASH_CHANGE ] : hashChange ,
9695 ...( navType ? { [ NAV_TYPE ] : navType } : { } ) ,
9796 } ,
9897 } ;
9998 this . _applyCustomLogRecordData ( navLogRecord , this . applyCustomLogRecordData ) ;
10099 this . eventLogger ?. emit ( navLogRecord ) ;
100+
101+ this . oldUrl = currentUrl ;
101102 }
102103
103104 public _setLogger ( eventLogger : Logger ) {
@@ -122,52 +123,41 @@ export class BrowserNavigationInstrumentation extends InstrumentationBase<Browse
122123 override enable ( ) {
123124 const cfg = this . getConfig ( ) as BrowserNavigationInstrumentationConfig ;
124125 const useNavigationApiIfAvailable = ! ! cfg . useNavigationApiIfAvailable ;
126+ const hasNavigationApi = useNavigationApiIfAvailable && ( window as any ) . navigation ;
125127
126- // Always patch history API
127- this . _patchHistoryApi ( ) ;
128+ // Only patch history API if Navigation API is not available
129+ if ( ! hasNavigationApi ) {
130+ this . _patchHistoryApi ( ) ;
131+ }
128132
129133 // Always listen for page load
130134 this . _waitForPageLoad ( ) ;
131135
132- // Always listen for popstate (back/forward)
133- if ( this . _onPopStateHandler ) {
134- window . removeEventListener ( 'popstate' , this . _onPopStateHandler ) ;
135- this . _onPopStateHandler = undefined ;
136- }
137- this . _onPopStateHandler = ( ) => {
138- this . _onSoftNavigation ( 'popstate' ) ;
139- this . oldUrl = location . href ;
140- } ;
141- window . addEventListener ( 'popstate' , this . _onPopStateHandler ) ;
142-
143- // Always listen for hashchange
144- if ( this . _onHashChangeHandler ) {
145- window . removeEventListener ( 'hashchange' , this . _onHashChangeHandler ) ;
146- this . _onHashChangeHandler = undefined ;
147- }
148- this . _onHashChangeHandler = ( ) => {
149- this . _onSoftNavigation ( 'hashchange' ) ;
150- this . oldUrl = location . href ;
151- } ;
152- window . addEventListener ( 'hashchange' , this . _onHashChangeHandler ) ;
153-
154- // Navigation API listener (experimental)
155- if ( this . _onNavigateHandler ) {
156- const nav = ( window as any ) . navigation ;
157- if ( nav ) {
158- nav . removeEventListener ( 'navigate' , this . _onNavigateHandler ) ;
136+ if ( hasNavigationApi ) {
137+ if ( this . _onNavigateHandler ) {
138+ const nav = ( window as any ) . navigation ;
139+ if ( nav ) {
140+ nav . removeEventListener ( 'navigate' , this . _onNavigateHandler ) ;
141+ }
142+ this . _onNavigateHandler = undefined ;
159143 }
160- this . _onNavigateHandler = undefined ;
161- }
162- if ( useNavigationApiIfAvailable && ( window as any ) . navigation ) {
144+
163145 this . _onNavigateHandler = ( event : any ) => {
164146 this . _onSoftNavigation ( 'navigate' , event ) ;
165- this . oldUrl = location . href ;
166147 } ;
167148 ( window as any ) . navigation . addEventListener (
168149 'navigate' ,
169150 this . _onNavigateHandler
170151 ) ;
152+ } else {
153+ if ( this . _onPopStateHandler ) {
154+ window . removeEventListener ( 'popstate' , this . _onPopStateHandler ) ;
155+ this . _onPopStateHandler = undefined ;
156+ }
157+ this . _onPopStateHandler = ( ) => {
158+ this . _onSoftNavigation ( 'popstate' ) ;
159+ } ;
160+ window . addEventListener ( 'popstate' , this . _onPopStateHandler ) ;
171161 }
172162 }
173163
@@ -211,7 +201,6 @@ export class BrowserNavigationInstrumentation extends InstrumentationBase<Browse
211201 const oldUrl = plugin . oldUrl ;
212202 if ( url !== oldUrl ) {
213203 plugin . _onSoftNavigation ( changeState ) ;
214- plugin . oldUrl = location . href ;
215204 }
216205 return result ;
217206 } ;
@@ -255,6 +244,8 @@ export class BrowserNavigationInstrumentation extends InstrumentationBase<Browse
255244 try {
256245 const a = new URL ( fromUrl , window . location . origin ) ;
257246 const b = new URL ( toUrl , window . location . origin ) ;
247+ // Only consider it a hash change if the base URL (origin + pathname + search) is identical
248+ // and only the hash portion differs
258249 return (
259250 a . origin === b . origin &&
260251 a . pathname === b . pathname &&
@@ -367,7 +358,34 @@ export class BrowserNavigationInstrumentation extends InstrumentationBase<Browse
367358 }
368359 }
369360
370- private _mapChangeStateToType ( changeState ?: string | null ) : NavigationType | undefined {
361+ private _mapChangeStateToType ( changeState ?: string | null , navigationEvent ?: any ) : NavigationType | undefined {
362+ // For Navigation API events, check if it's a hash change first
363+ if ( changeState === 'navigate' && navigationEvent ?. hashChange ) {
364+ // Hash changes are always considered 'push' operations semantically
365+ return 'push' ;
366+ }
367+
368+ // For Navigation API events, determine type based on event properties
369+ if ( changeState === 'navigate' ) {
370+ // Check if this is a back/forward navigation (traverse)
371+ if ( navigationEvent ?. navigationType === 'traverse' ) {
372+ return 'traverse' ;
373+ }
374+
375+ // Check if this is a replace operation
376+ if ( navigationEvent ?. navigationType === 'replace' ) {
377+ return 'replace' ;
378+ }
379+
380+ // Check if this is a reload
381+ if ( navigationEvent ?. navigationType === 'reload' ) {
382+ return 'reload' ;
383+ }
384+
385+ // Default to 'push' for new navigations (link clicks, programmatic navigation)
386+ return 'push' ;
387+ }
388+
371389 switch ( changeState ) {
372390 case 'pushState' :
373391 return 'push' ;
0 commit comments