var __addDisposableResource = (this && this.__addDisposableResource) || function (env, value, async) {
    if (value !== null && value !== void 0) {
        if (typeof value !== "object" && typeof value !== "function") throw new TypeError("Object expected.");
        var dispose, inner;
        if (async) {
            if (!Symbol.asyncDispose) throw new TypeError("Symbol.asyncDispose is not defined.");
            dispose = value[Symbol.asyncDispose];
        }
        if (dispose === void 0) {
            if (!Symbol.dispose) throw new TypeError("Symbol.dispose is not defined.");
            dispose = value[Symbol.dispose];
            if (async) inner = dispose;
        }
        if (typeof dispose !== "function") throw new TypeError("Object not disposable.");
        if (inner) dispose = function() { try { inner.call(this); } catch (e) { return Promise.reject(e); } };
        env.stack.push({ value: value, dispose: dispose, async: async });
    }
    else if (async) {
        env.stack.push({ async: true });
    }
    return value;
};
var __disposeResources = (this && this.__disposeResources) || (function (SuppressedError) {
    return function (env) {
        function fail(e) {
            env.error = env.hasError ? new SuppressedError(e, env.error, "An error was suppressed during disposal.") : e;
            env.hasError = true;
        }
        var r, s = 0;
        function next() {
            while (r = env.stack.pop()) {
                try {
                    if (!r.async && s === 1) return s = 0, env.stack.push(r), Promise.resolve().then(next);
                    if (r.dispose) {
                        var result = r.dispose.call(r.value);
                        if (r.async) return s |= 2, Promise.resolve(result).then(next, function(e) { fail(e); return next(); });
                    }
                    else s |= 1;
                }
                catch (e) {
                    fail(e);
                }
            }
            if (s === 1) return env.hasError ? Promise.reject(env.error) : Promise.resolve();
            if (env.hasError) throw env.error;
        }
        return next();
    };
})(typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
    var e = new Error(message);
    return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
});
import { isLocationStateEntry, resolveLocationStateEntries, } from "./react/location-state-shared.js";
/**
 * Check if state is from typed LocationStateEntry[] (has __rsc_ls_ keys)
 */
function isTypedLocationState(state) {
    if (state === null || typeof state !== "object")
        return false;
    return Object.keys(state).some((key) => key.startsWith("__rsc_ls_"));
}
/**
 * Resolve navigation state - handles both LocationStateEntry[] and legacy formats
 */
function resolveNavigationState(state) {
    // Check if it's an array of LocationStateEntry
    if (Array.isArray(state) &&
        state.length > 0 &&
        isLocationStateEntry(state[0])) {
        return resolveLocationStateEntries(state);
    }
    // Return as-is for legacy formats
    return state;
}
/**
 * Build history state object from user state
 * - Typed state: spread directly into history.state
 * - Legacy state: store in history.state.state
 */
function buildHistoryState(userState, routerState) {
    const result = {};
    // Add router internal state
    if (routerState?.intercept) {
        result.intercept = true;
        if (routerState.sourceUrl) {
            result.sourceUrl = routerState.sourceUrl;
        }
    }
    // Add user state
    if (userState !== undefined) {
        if (isTypedLocationState(userState)) {
            // Typed state: spread directly
            Object.assign(result, userState);
        }
        else {
            // Legacy state: store in .state
            result.state = userState;
        }
    }
    return Object.keys(result).length > 0 ? result : null;
}
import { setupLinkInterception } from "./link-interceptor.js";
import { createPartialUpdater } from "./partial-update.js";
import { generateHistoryKey } from "./navigation-store.js";
import { handleNavigationStart, handleNavigationEnd, ensureHistoryKey, } from "./scroll-restoration.js";
import { NetworkError, isNetworkError } from "../errors.js";
import { NetworkErrorThrower } from "../network-error-thrower.js";
import { createElement, startTransition } from "react";
// Polyfill Symbol.dispose for Safari and older browsers
if (typeof Symbol.dispose === "undefined") {
    Symbol.dispose = Symbol("Symbol.dispose");
}
/**
 * Check if a segment is an intercept segment
 * Intercept segments have namespace starting with "intercept:" or ID containing .@
 */
function isInterceptSegment(s) {
    return (s.namespace?.startsWith("intercept:") ||
        (s.type === "parallel" && s.id.includes(".@")));
}
/**
 * Check if cached segments are intercept-only (no main route segments)
 * Intercept responses shouldn't be used for optimistic rendering since
 * whether interception happens depends on the current page context
 */
function isInterceptOnlyCache(segments) {
    return segments.some(isInterceptSegment);
}
/**
 * Creates a navigation transaction that coordinates with the event controller.
 * Handles loading state transitions and cleanup on completion/abort.
 *
 * Supports optimistic navigation: render from cache immediately,
 * then revalidate in background and reconcile if data changed.
 */
function createNavigationTransaction(store, eventController, url, options) {
    let committed = false;
    let optimisticallyCommitted = false;
    let earlyStatePushed = false;
    const currentUrl = window.location.href;
    // Start navigation in event controller (this sets loading state)
    const handle = eventController.startNavigation(url, options);
    // If state is provided, push it to history immediately so loading UI can access it
    // This enables "optimistic state" - showing product names in skeletons etc.
    if (options?.state !== undefined && !options?.replace) {
        const earlyHistoryState = buildHistoryState(options.state);
        window.history.pushState(earlyHistoryState, "", url);
        earlyStatePushed = true;
    }
    /**
     * Optimistically commit from cache - renders immediately before revalidation
     * Sets optimisticallyCommitted flag so final commit() knows to reconcile
     */
    function optimisticCommit(opts) {
        optimisticallyCommitted = true;
        const { url, segmentIds, segments, replace, scroll } = opts;
        const parsedUrl = new URL(url, window.location.origin);
        // Save current scroll position before navigating
        handleNavigationStart();
        // Update segment state
        store.setSegmentIds(segmentIds);
        store.setCurrentUrl(url);
        store.setPath(parsedUrl.pathname);
        // Generate history key from URL
        const historyKey = generateHistoryKey(url);
        store.setHistoryKey(historyKey);
        // Cache segments with current handleData (will be overwritten by fresh data on final commit)
        const currentHandleData = eventController.getHandleState().data;
        store.cacheSegmentsForHistory(historyKey, segments, currentHandleData);
        // Build history state with user state if provided
        const historyState = buildHistoryState(opts.state);
        // Update browser URL
        // Use replaceState if we already pushed early (for optimistic state access)
        if (replace || earlyStatePushed) {
            window.history.replaceState(historyState, "", url);
        }
        else {
            window.history.pushState(historyState, "", url);
        }
        // Ensure new history entry has a scroll restoration key
        ensureHistoryKey();
        // Complete the navigation in event controller (sets idle state)
        handle.complete(parsedUrl);
        // Handle scroll after navigation
        handleNavigationEnd({ scroll });
        console.log("[Browser] Optimistic commit from cache, historyKey:", historyKey);
    }
    /**
     * Commit the navigation - updates store and URL atomically
     * If optimisticCommit was called, this becomes a reconciliation
     */
    function commit(opts) {
        committed = true;
        // If optimistic commit already done, adjust options for reconciliation
        const isReconciliation = optimisticallyCommitted;
        const { url, segmentIds, segments, storeOnly, intercept, interceptSourceUrl, cacheOnly, } = opts;
        // For reconciliation: always replace (URL already pushed), no scroll
        const replace = isReconciliation ? true : opts.replace;
        const scroll = isReconciliation ? false : opts.scroll;
        const parsedUrl = new URL(url, window.location.origin);
        // Generate history key from URL (with intercept suffix for separate caching)
        const historyKey = generateHistoryKey(url, { intercept });
        // For cache-only commits (stale revalidation), only update cache and return
        // Don't touch store state or history - user may have navigated elsewhere
        if (cacheOnly) {
            const currentHandleData = eventController.getHandleState().data;
            store.cacheSegmentsForHistory(historyKey, segments, currentHandleData);
            console.log("[Browser] Cache-only commit, historyKey:", historyKey);
            return;
        }
        // Save current scroll position before navigating (only for non-reconciliation)
        if (!isReconciliation) {
            handleNavigationStart();
        }
        // Update segment state atomically
        store.setSegmentIds(segmentIds);
        store.setCurrentUrl(url);
        store.setPath(parsedUrl.pathname);
        store.setHistoryKey(historyKey);
        // Cache segments with current handleData for this history entry (fresh data overwrites optimistic)
        const currentHandleData = eventController.getHandleState().data;
        store.cacheSegmentsForHistory(historyKey, segments, currentHandleData);
        // For server actions, skip URL/history updates but still complete navigation
        if (storeOnly) {
            console.log("[Browser] Store updated (action)");
            // Complete navigation to clear loading state
            handle.complete(parsedUrl);
            return;
        }
        // Build history state - include user state and intercept info for popstate handling
        const historyState = buildHistoryState(opts.state, {
            intercept,
            sourceUrl: interceptSourceUrl,
        });
        // Update browser URL (skip if reconciliation - already done in optimisticCommit)
        if (!isReconciliation) {
            // Use replaceState if we already pushed early (for optimistic state access) or replace requested
            if (replace || earlyStatePushed) {
                window.history.replaceState(historyState, "", url);
            }
            else {
                window.history.pushState(historyState, "", url);
            }
            // Ensure new history entry has a scroll restoration key
            ensureHistoryKey();
        }
        // Complete the navigation in event controller (sets idle state, updates location)
        handle.complete(parsedUrl);
        // Handle scroll after navigation (skip if reconciliation)
        if (!isReconciliation) {
            handleNavigationEnd({ scroll });
        }
        if (isReconciliation) {
            console.log("[Browser] Reconciliation commit, historyKey:", historyKey);
        }
        else {
            console.log("[Browser] Navigation committed, historyKey:", historyKey, intercept ? "(intercept)" : "");
        }
    }
    return {
        handle,
        optimisticCommit,
        commit,
        /**
         * Create a bound transaction with pre-configured URL options
         * segmentIds and segments provided at commit time (after they're resolved)
         */
        with(opts) {
            return {
                get currentUrl() {
                    return currentUrl;
                },
                startStreaming() {
                    return handle.startStreaming();
                },
                commit: (segmentIds, segments, overrides) => {
                    // Allow overrides to disable scroll (e.g., for intercepts)
                    const finalScroll = overrides?.scroll !== undefined ? overrides.scroll : opts.scroll;
                    // Allow overrides to force replace (e.g., for intercepts)
                    const finalReplace = overrides?.replace !== undefined ? overrides.replace : opts.replace;
                    // Intercept info: overrides take precedence, fallback to opts
                    const intercept = overrides?.intercept !== undefined
                        ? overrides.intercept
                        : opts.intercept;
                    const interceptSourceUrl = overrides?.interceptSourceUrl !== undefined
                        ? overrides.interceptSourceUrl
                        : opts.interceptSourceUrl;
                    // Cache-only mode: overrides take precedence, fallback to opts
                    const cacheOnly = overrides?.cacheOnly !== undefined
                        ? overrides.cacheOnly
                        : opts.cacheOnly;
                    // User state: overrides take precedence, fallback to opts
                    const state = overrides?.state !== undefined ? overrides.state : opts.state;
                    commit({
                        ...opts,
                        segmentIds,
                        segments,
                        scroll: finalScroll,
                        replace: finalReplace,
                        state,
                        intercept,
                        interceptSourceUrl,
                        cacheOnly,
                    });
                },
            };
        },
        [Symbol.dispose]() {
            // If aborted, another navigation took over - don't touch state
            if (handle.signal.aborted)
                return;
            // If not committed (and not optimistically committed), the handle's dispose
            // will reset state to idle via the event controller
            if (!committed && !optimisticallyCommitted) {
                handle[Symbol.dispose]();
                // The NavigationHandle's [Symbol.dispose] handles this
            }
        },
    };
}
// Export for use by server-action-bridge
export { createNavigationTransaction };
/**
 * Create a navigation bridge for handling client-side navigation
 *
 * The bridge coordinates all navigation operations:
 * - Link click interception
 * - Browser back/forward (popstate)
 * - Programmatic navigation
 *
 * Uses the event controller for reactive state management.
 *
 * @param config - Bridge configuration
 * @returns NavigationBridge instance
 */
export function createNavigationBridge(config) {
    const { store, client, eventController, onUpdate, renderSegments, version } = config;
    // Create shared partial updater
    const fetchPartialUpdate = createPartialUpdater({
        store,
        client,
        onUpdate,
        renderSegments,
        version,
    });
    return {
        /**
         * Navigate to a URL
         * Uses optimistic rendering from cache when available (SWR pattern)
         */
        async navigate(url, options) {
            const env_1 = { stack: [], error: void 0, hasError: false };
            try {
                // Resolve LocationStateEntry[] to flat object if needed
                const resolvedState = options?.state !== undefined
                    ? resolveNavigationState(options.state)
                    : undefined;
                // Only abort pending requests when navigating to a different route
                // Same-route navigation (e.g., /todos -> /todos) should not cancel in-flight actions
                const currentPath = new URL(window.location.href).pathname;
                const targetPath = new URL(url, window.location.origin).pathname;
                if (currentPath !== targetPath) {
                    eventController.abortNavigation();
                }
                // Check if we're "leaving intercept" - navigating from intercept to same URL without intercept
                // This happens when clicking "View Full Details" in an intercept modal
                const currentHistoryState = window.history.state;
                const isCurrentlyIntercept = currentHistoryState?.intercept === true;
                const isSamePathNavigation = currentPath === targetPath;
                const isLeavingIntercept = isCurrentlyIntercept && isSamePathNavigation;
                if (isLeavingIntercept) {
                    console.log(`[Browser] Leaving intercept - same URL navigation from intercept`);
                    // Clear intercept source URL to ensure server doesn't treat this as intercept
                    store.setInterceptSourceUrl(null);
                }
                // Before navigating away, update the source page's cache with the latest handleData.
                // This ensures the cache has correct handleData even if handles were streaming.
                const sourceHistoryKey = store.getHistoryKey();
                const sourceCached = store.getCachedSegments(sourceHistoryKey);
                if (sourceCached?.segments && sourceCached.segments.length > 0) {
                    const currentHandleData = eventController.getHandleState().data;
                    store.cacheSegmentsForHistory(sourceHistoryKey, sourceCached.segments, currentHandleData);
                }
                // Check if we have cached segments for target URL
                const historyKey = generateHistoryKey(url);
                const cached = store.getCachedSegments(historyKey);
                // For shared segments (same ID on current and target), use current page's version
                // since it may have fresher data after an action revalidation.
                // This avoids unnecessary server round-trips for shared layout loaders.
                let cachedSegments = cached?.segments;
                const cachedHandleData = cached?.handleData;
                if (cachedSegments && sourceCached?.segments) {
                    const sourceSegmentMap = new Map(sourceCached.segments.map((s) => [s.id, s]));
                    cachedSegments = cachedSegments.map((targetSeg) => {
                        const sourceSeg = sourceSegmentMap.get(targetSeg.id);
                        // Use source (current page) version for shared segments - it's fresher
                        return sourceSeg || targetSeg;
                    });
                }
                // Also check if there's an intercept cache entry for this URL
                // If so, this URL CAN be intercepted, and we shouldn't use the non-intercept cache
                // because the navigation might result in an intercept (depending on source URL)
                const interceptHistoryKey = generateHistoryKey(url, { intercept: true });
                const hasInterceptCache = store.hasHistoryCache(interceptHistoryKey);
                // Skip optimistic rendering for:
                // 1. intercept caches - interception depends on source page context
                // 2. routes that CAN be intercepted - we don't know if this navigation will intercept
                // 3. when leaving intercept - we need fresh non-intercept segments from server
                const hasUsableCache = cachedSegments &&
                    cachedSegments.length > 0 &&
                    !isInterceptOnlyCache(cachedSegments) &&
                    !hasInterceptCache &&
                    !isLeavingIntercept;
                const tx = __addDisposableResource(env_1, createNavigationTransaction(store, eventController, url, {
                    ...options,
                    state: resolvedState,
                    skipLoadingState: hasUsableCache,
                }), false);
                // REVALIDATE: Fetch fresh data from server
                try {
                    await fetchPartialUpdate(url, hasUsableCache ? cachedSegments.map((s) => s.id) : undefined, false, tx.handle.signal, tx.with({
                        url,
                        replace: options?.replace,
                        scroll: options?.scroll,
                        state: resolvedState,
                    }), 
                    // Pass cached segments (merged with current page's fresh segments for shared IDs)
                    // so the segment map is consistent with what we tell the server we have.
                    // Server decides what needs revalidation based on route matching and custom functions.
                    // No need for staleRevalidation flag - we're sending the freshest segments we have.
                    // Also pass cached handle data for restoring breadcrumbs when server returns empty diff.
                    // When leaving intercept, pass the flag so fetchPartialUpdate knows to filter segments.
                    hasUsableCache
                        ? { targetCacheSegments: cachedSegments, targetCacheHandleData: cachedHandleData }
                        : isLeavingIntercept
                            ? { leavingIntercept: true }
                            : undefined);
                }
                catch (error) {
                    // Ignore AbortError - navigation was cancelled by a newer navigation
                    if (error instanceof DOMException && error.name === "AbortError") {
                        console.log("[Browser] Navigation aborted by newer navigation");
                        return;
                    }
                    // Handle network errors by triggering root error boundary
                    if (error instanceof NetworkError || isNetworkError(error)) {
                        const networkError = error instanceof NetworkError
                            ? error
                            : new NetworkError("Unable to connect to server. Please check your connection.", { cause: error, url, operation: "navigation" });
                        console.error("[Browser] Network error during navigation:", networkError);
                        // Emit update with NetworkErrorThrower to trigger root error boundary
                        startTransition(() => {
                            onUpdate({
                                root: createElement(NetworkErrorThrower, { error: networkError }),
                                metadata: {
                                    pathname: url,
                                    segments: [],
                                    isError: true,
                                },
                            });
                        });
                        return;
                    }
                    throw error;
                }
            }
            catch (e_1) {
                env_1.error = e_1;
                env_1.hasError = true;
            }
            finally {
                __disposeResources(env_1);
            }
        },
        /**
         * Refresh current route
         */
        async refresh() {
            const env_2 = { stack: [], error: void 0, hasError: false };
            try {
                eventController.abortNavigation();
                const tx = __addDisposableResource(env_2, createNavigationTransaction(store, eventController, window.location.href, { replace: true }), false);
                try {
                    // Refetch with empty segments to get everything fresh
                    await fetchPartialUpdate(window.location.href, [], false, tx.handle.signal, tx.with({ url: window.location.href, replace: true, scroll: false }));
                }
                catch (error) {
                    // Handle network errors by triggering root error boundary
                    if (error instanceof NetworkError || isNetworkError(error)) {
                        const networkError = error instanceof NetworkError
                            ? error
                            : new NetworkError("Unable to connect to server. Please check your connection.", {
                                cause: error,
                                url: window.location.href,
                                operation: "revalidation",
                            });
                        console.error("[Browser] Network error during refresh:", networkError);
                        startTransition(() => {
                            onUpdate({
                                root: createElement(NetworkErrorThrower, { error: networkError }),
                                metadata: {
                                    pathname: window.location.href,
                                    segments: [],
                                    isError: true,
                                },
                            });
                        });
                        return;
                    }
                    throw error;
                }
            }
            catch (e_2) {
                env_2.error = e_2;
                env_2.hasError = true;
            }
            finally {
                __disposeResources(env_2);
            }
        },
        /**
         * Handle browser back/forward navigation
         * Uses cached segments when available for instant restoration
         */
        async handlePopstate() {
            const env_3 = { stack: [], error: void 0, hasError: false };
            try {
                // Abort any pending navigation to prevent race conditions
                eventController.abortNavigation();
                const url = window.location.href;
                // Check if this history entry is an intercept
                const historyState = window.history.state;
                const isIntercept = historyState?.intercept === true;
                const interceptSourceUrl = historyState?.sourceUrl;
                // Check if intercept context is changing (same URL, different intercept state)
                // If so, abort in-flight actions - their results would be for wrong context
                const currentInterceptSource = store.getInterceptSourceUrl();
                const newInterceptSource = interceptSourceUrl ?? null;
                if (currentInterceptSource !== newInterceptSource) {
                    console.log(`[Browser] Intercept context changing (${currentInterceptSource} -> ${newInterceptSource}), aborting in-flight actions`);
                    eventController.abortAllActions();
                }
                // Compute history key from URL (with intercept suffix if applicable)
                const historyKey = generateHistoryKey(url, { intercept: isIntercept });
                console.log("[Browser] Popstate -", isIntercept ? "intercept" : "normal", "key:", historyKey);
                // Update location in event controller
                eventController.setLocation(new URL(url));
                // If this is an intercept, restore the intercept context
                if (isIntercept && interceptSourceUrl) {
                    store.setInterceptSourceUrl(interceptSourceUrl);
                }
                else {
                    store.setInterceptSourceUrl(null);
                }
                // Helper to check if streaming is in progress
                const isStreaming = () => eventController.getState().isStreaming;
                // Check if we can restore from history cache
                const cached = store.getCachedSegments(historyKey);
                const cachedSegments = cached?.segments;
                const cachedHandleData = cached?.handleData;
                const isStale = cached?.stale ?? false;
                if (cachedSegments && cachedSegments.length > 0) {
                    // Update store to point to this history entry
                    store.setHistoryKey(historyKey);
                    store.setSegmentIds(cachedSegments.map((s) => s.id));
                    store.setCurrentUrl(url);
                    store.setPath(new URL(url).pathname);
                    // Render from cache - force await to skip loading fallbacks
                    try {
                        const root = await renderSegments(cachedSegments, {
                            forceAwait: true,
                        });
                        onUpdate({
                            root,
                            metadata: {
                                pathname: new URL(url).pathname,
                                segments: cachedSegments,
                                isPartial: true,
                                matched: cachedSegments.map((s) => s.id),
                                diff: [],
                                cachedHandleData,
                            },
                        });
                        // Restore scroll position for back/forward navigation
                        handleNavigationEnd({ restore: true, isStreaming });
                        // SWR: If stale, trigger background revalidation
                        if (isStale) {
                            console.log("[Browser] Cache is stale, background revalidating...");
                            // Background revalidation - don't await, just fire and forget
                            const segmentIds = cachedSegments.map((s) => s.id);
                            const tx = createNavigationTransaction(store, eventController, url, { skipLoadingState: true, replace: true });
                            fetchPartialUpdate(url, segmentIds, false, tx.handle.signal, tx.with({
                                url,
                                replace: true,
                                scroll: false,
                                intercept: isIntercept,
                                interceptSourceUrl,
                                cacheOnly: true,
                            }), { staleRevalidation: true, interceptSourceUrl }).catch((error) => {
                                if (error instanceof DOMException &&
                                    error.name === "AbortError") {
                                    console.log("[Browser] Background revalidation aborted");
                                    return;
                                }
                                if (error instanceof NetworkError || isNetworkError(error)) {
                                    console.warn("[Browser] Background revalidation network error (cached content preserved):", error.message);
                                    return;
                                }
                                console.error("[Browser] Background revalidation failed:", error);
                            }).finally(() => {
                                tx[Symbol.dispose]();
                            });
                        }
                        return;
                    }
                    catch (error) {
                        console.warn("[Browser] Failed to render from cache, fetching:", error);
                        // Fall through to fetch
                    }
                }
                else {
                    console.log("[Browser] History cache miss for key:", historyKey);
                }
                // Fetch if not cached
                const tx = __addDisposableResource(env_3, createNavigationTransaction(store, eventController, url, {
                    replace: true,
                }), false);
                try {
                    await fetchPartialUpdate(url, undefined, false, tx.handle.signal, tx.with({ url, replace: true, scroll: false }));
                    // Restore scroll position after fetch completes
                    handleNavigationEnd({ restore: true, isStreaming });
                }
                catch (error) {
                    if (error instanceof DOMException && error.name === "AbortError") {
                        console.log("[Browser] Popstate navigation aborted");
                        return;
                    }
                    // Handle network errors by triggering root error boundary
                    if (error instanceof NetworkError || isNetworkError(error)) {
                        const networkError = error instanceof NetworkError
                            ? error
                            : new NetworkError("Unable to connect to server. Please check your connection.", { cause: error, url, operation: "navigation" });
                        console.error("[Browser] Network error during popstate navigation:", networkError);
                        startTransition(() => {
                            onUpdate({
                                root: createElement(NetworkErrorThrower, { error: networkError }),
                                metadata: {
                                    pathname: url,
                                    segments: [],
                                    isError: true,
                                },
                            });
                        });
                        return;
                    }
                    throw error;
                }
            }
            catch (e_3) {
                env_3.error = e_3;
                env_3.hasError = true;
            }
            finally {
                __disposeResources(env_3);
            }
        },
        /**
         * Register link interception
         * @returns Cleanup function
         */
        registerLinkInterception() {
            const cleanupLinks = setupLinkInterception((url, options) => {
                this.navigate(url, options);
            });
            const handlePopstate = () => {
                this.handlePopstate();
            };
            // When the browser restores a page from bfcache (back-forward cache),
            // any in-flight navigation state is stale. This happens when:
            // 1. A navigation triggers X-RSC-Reload (e.g., response route hit via SPA)
            // 2. window.location.href does a hard navigation
            // 3. The user presses back and the browser restores from bfcache
            // At that point, currentNavigation is still set from step 1, so
            // getState() returns "loading" and the progress bar shows.
            // Abort the stale navigation to reset state to idle.
            const handlePageShow = (event) => {
                if (event.persisted) {
                    console.log("[Browser] Page restored from bfcache, resetting navigation state");
                    eventController.abortNavigation();
                }
            };
            // Register cross-tab refresh callback with the store
            store.setCrossTabRefreshCallback(() => {
                this.refresh();
            });
            window.addEventListener("popstate", handlePopstate);
            window.addEventListener("pageshow", handlePageShow);
            console.log("[Browser] Navigation bridge ready");
            return () => {
                cleanupLinks();
                window.removeEventListener("popstate", handlePopstate);
                window.removeEventListener("pageshow", handlePageShow);
            };
        },
    };
}
export { createNavigationBridge as default };
//# sourceMappingURL=navigation-bridge.js.map