import { History , Path , LocationDescriptorObject , TransitionPromptHook , UnregisterCallback , LocationListener , Location , Href , Action , } from 'history' ; /**
 * A wrapper around a `History` instance that is scoped to a particular base path of the history stack. Behaves
 * similarly to the `basename` option except that this wrapper hides any history stack entries from outside the scope
 * of this base path.
 *
 * This wrapper also allows Core and Plugins to share a single underlying global `History` instance without exposing
 * the history of other applications.
 *
 * The  ScopedHistory . createSubHistory method is particularly useful for applications that
 * contain any number of "sub-apps" which should not have access to the main application's history or basePath.
 *
 *  public export class ScopedHistory < HistoryLocationState = unknown > implements History < HistoryLocationState > { /**
     * Tracks whether or not the user has left this history's scope. All methods throw errors if called after scope has
     * been left.
     */ private isActive = true ; /**
     * All active listeners on this history instance.
     */ private listeners = new Set < LocationListener < HistoryLocationState > > ( ) ; /**
     * Array of the local history stack. Only stores  Location . key to use tracking an index of the current
     * position of the window in the history stack.
      private locationKeys : Array < string | undefined > = [ ] ; /**
     * The key of the current position of the window in the history stack.
     */ private currentLocationKeyIndex : number = 0 ; constructor ( private readonly parentHistory , private readonly basePath ) { const parentPath = this . parentHistory . location . pathname ; if ( ! parentPath . startsWith ( basePath ) ) { throw new Error ( `Browser location [${ parentPath }] is not currently in expected basePath [${ basePath }]` ) ; } this . locationKeys . push ( this . parentHistory . location . key ) ; this . setupHistoryListener ( ) ; } param basePath public createSubHistory = < SubHistoryLocationState = unknown > ( basePath ) : ScopedHistory < SubHistoryLocationState > => { return new ScopedHistory < SubHistoryLocationState > ( this , basePath ) ; } ; /**
     * The number of entries in the history stack, including all entries forwards and backwards from the current location.
     */ public get length ( ) { this . verifyActive ( ) ; return this . locationKeys . length ; } /**
     * The current location of the history stack.
     */ public get location ( ) { this . verifyActive ( ) ; return this . stripBasePath ( this . parentHistory . location ) ; } /**
     * The last action dispatched on the history stack.
     */ public get action ( ) { this . verifyActive ( ) ; return this . parentHistory . action ; } param pathOrLocation param state public push = ( pathOrLocation , state ? ) : void => { this . verifyActive ( ) ; if ( typeof pathOrLocation === 'string' ) { this . parentHistory . push ( this . prependBasePath ( pathOrLocation ) , state ) ; } else { this . parentHistory . push ( this . prependBasePath ( pathOrLocation ) ) ; } } ; param pathOrLocation param state public replace = ( pathOrLocation , state ? ) : void => { this . verifyActive ( ) ; if ( typeof pathOrLocation === 'string' ) { this . parentHistory . replace ( this . prependBasePath ( pathOrLocation ) , state ) ; } else { this . parentHistory . replace ( this . prependBasePath ( pathOrLocation ) ) ; } } ; param n public go = ( n ) : void => { this . verifyActive ( ) ; if ( n === 0 ) { this . parentHistory . go ( n ) ; } else if ( n < 0 ) { if ( this . currentLocationKeyIndex + 1 + n >= 1 ) { this . parentHistory . go ( n ) ; } } else if ( n <= this . currentLocationKeyIndex + this . locationKeys . length - 1 ) { this . parentHistory . go ( n ) ; } } ; /**
     * Send the user one location back in the history stack. Equivalent to calling
     *  ScopedHistory . go . If no more entries are available backwards, this is a no-op.
      public goBack = ( ) => { this . verifyActive ( ) ; this . go ( - 1 ) ; } ; /**
     * Send the user one location forward in the history stack. Equivalent to calling
     *  ScopedHistory . go . If no more entries are available forwards, this is a no-op.
      public goForward = ( ) => { this . verifyActive ( ) ; this . go ( 1 ) ; } ; /**
     * Not supported. Use  AppMountParameters . onAppLeave .
     *
     *  remarks public block = ( prompt ? ) : UnregisterCallback => { throw new Error ( `history.block is not supported. Please use the AppMountParameters.onAppLeave API.` ) ; } ; param listener returns public listen = ( listener ) : UnregisterCallback => { this . verifyActive ( ) ; this . listeners . add ( listener ) ; return ( ) => { this . listeners . delete ( listener ) ; } ; } ; param location param prependBasePath public createHref = ( location , { prependBasePath = true } = { } ) : Href => { this . verifyActive ( ) ; if ( prependBasePath ) { location = this . prependBasePath ( location ) ; if ( location . pathname === undefined ) { location . pathname = this . basePath ; } } return this . parentHistory . createHref ( location ) ; } ; private prependBasePath ( path ) ; private prependBasePath ( location ) ; /**
     * Prepends the scoped base path to the Path or Location
     */ private prependBasePath ( pathOrLocation ) { if ( typeof pathOrLocation === 'string' ) { return this . prependBasePathToString ( pathOrLocation ) ; } else { return { ... pathOrLocation , pathname : pathOrLocation . pathname !== undefined ? this . prependBasePathToString ( pathOrLocation . pathname ) : undefined , } ; } } /**
     * Prepends the base path to string.
     */ private prependBasePathToString ( path ) { return path . length ? `${ this . basePath }/${ path }` . replace ( /\/{2,}/g , '/' ) : this . basePath ; } /**
     * Removes the base path from a location.
     */ private stripBasePath ( location ) { return { ... location , pathname : location . pathname . replace ( new RegExp ( `^${ this . basePath }` ) , '' ) , } ; } /** Called on each public method to ensure that we have not fallen out of scope yet. */ private verifyActive ( ) { if ( ! this . isActive ) { throw new Error ( `ScopedHistory instance has fell out of navigation scope for basePath: ${ this . basePath }` ) ; } } /**
     * Sets up the listener on the parent history instance used to follow navigation updates and track our internal
     * state. Also forwards events to child listeners with the base path stripped from the location.
     */ private setupHistoryListener ( ) { const unlisten = this . parentHistory . listen ( ( location , action ) => { if ( ! location . pathname . startsWith ( this . basePath ) ) { unlisten ( ) ; this . isActive = false ; return ; } /**
             * Track location keys using the same algorithm the browser uses internally.
             * - On PUSH, remove all items that came after the current location and append the new location.
             * - On POP, set the current location, but do not change the entries.
             * - On REPLACE, override the location for the current index with the new location.
             */ if ( action === 'PUSH' ) { this . locationKeys = [ ... this . locationKeys . slice ( 0 , this . currentLocationKeyIndex + 1 ) , location . key , ] ; this . currentLocationKeyIndex = this . locationKeys . indexOf ( location . key ) ; } else if ( action === 'POP' ) { this . currentLocationKeyIndex = this . locationKeys . indexOf ( location . key ) ; } else if ( action === 'REPLACE' ) { this . locationKeys [ this . currentLocationKeyIndex ] = location . key ; } else { throw new Error ( `Unrecognized history action: ${ action }` ) ; } [ ... this . listeners ] . forEach ( ( listener ) => { listener ( this . stripBasePath ( location ) , action ) ; } ) ; } ) ; } }