class / @typed/navigation/Navigation

Navigation

Provides backend-neutral reactive navigation state and Effectful history operations.

Package version
1.0.0-beta.13
Category
Navigation service
Since
1.0.0

Import

import { Navigation } from "@typed/navigation/Navigation";

Signatures

export declare class Navigation extends Navigation_base {
    static readonly origin: Effect.Effect<string, never, Navigation>;
    static readonly base: Effect.Effect<string, never, Navigation>;
    static readonly currentEntry: RefSubject.Computed<{
        readonly id: string;
        readonly key: string;
        readonly url: URL;
        readonly state: unknown;
        readonly sameDocument: boolean;
    }, never, Navigation>;
    static readonly entries: RefSubject.Computed<readonly {
        readonly id: string;
        readonly key: string;
        readonly url: URL;
        readonly state: unknown;
        readonly sameDocument: boolean;
    }[], never, Navigation>;
    static readonly transition: RefSubject.Filtered<{
        readonly type: "push" | "replace" | "reload" | "traverse";
        readonly from: {
            readonly id: string;
            readonly key: string;
            readonly url: URL;
            readonly state: unknown;
            readonly sameDocument: boolean;
        };
        readonly to: {
            readonly url: URL;
            readonly state: unknown;
            readonly sameDocument: boolean;
            readonly key?: string | undefined;
        };
        readonly info?: unknown;
    }, never, Navigation>;
    static readonly canGoBack: RefSubject.Computed<boolean, never, Navigation>;
    static readonly canGoForward: RefSubject.Computed<boolean, never, Navigation>;
    static navigate<S>(url: string | URL, options: NavigationNavigateOptions & {
        readonly state: S;
    }): Effect.Effect<DestinationState<S>, NavigationError, Navigation>;
    static navigate(url: string | URL, options?: NavigationNavigateOptions): Effect.Effect<Destination, NavigationError, Navigation>;
    static readonly back: (options?: NavigationInfoOptions) => Effect.Effect<{
        readonly id: string;
        readonly key: string;
        readonly url: URL;
        readonly state: unknown;
        readonly sameDocument: boolean;
    }, NavigationError, Navigation>;
    static readonly forward: (options?: NavigationInfoOptions) => Effect.Effect<{
        readonly id: string;
        readonly key: string;
        readonly url: URL;
        readonly state: unknown;
        readonly sameDocument: boolean;
    }, NavigationError, Navigation>;
    static readonly traverseTo: (key: Destination["key"], options?: NavigationInfoOptions) => Effect.Effect<{
        readonly id: string;
        readonly key: string;
        readonly url: URL;
        readonly state: unknown;
        readonly sameDocument: boolean;
    }, NavigationError, Navigation>;
    static updateCurrentEntry<S>(options: {
        readonly state: S;
    }): Effect.Effect<DestinationState<S>, NavigationError, Navigation>;
    static updateCurrentEntry(options: {
        readonly state: unknown;
    }): Effect.Effect<Destination, NavigationError, Navigation>;
    static reload<S>(options: NavigationReloadOptions & {
        readonly state: S;
    }): Effect.Effect<DestinationState<S>, NavigationError, Navigation>;
    static reload(options?: NavigationReloadOptions): Effect.Effect<Destination, NavigationError, Navigation>;
    static readonly onBeforeNavigation: <R = never, R2 = never>(handler: BeforeNavigationHandler<R, R2>) => Effect.Effect<void, never, Navigation | Scope.Scope | R | R2>;
    static readonly onNavigation: <R = never, R2 = never>(handler: NavigationHandler<R, R2>) => Effect.Effect<void, never, Navigation | Scope.Scope | R | R2>;
}

Why

Routers, renderers, and application code can depend on one service whether history comes from a browser, SSR memory, or a test. Current entry, entries, and transition are RefSubjects so state remains renderer-independent.

Ownership and lifetime

A provider Layer owns the service, its backend listeners, reactive state, and handler registry. Handler registration requires a Scope and unregisters through that scope’s finalizer. Each navigation Effect publishes a transition, runs before handlers sequentially, commits, clears the transition, and then runs post-commit handlers. Interruption follows Effect’s scoped cleanup.

Property: back

Traverses to the preceding retained entry when one exists.

Property: back: Why

Bounds behavior stays provider-neutral and preserves the destination’s key, id, and state.

Property: back: Ownership and lifetime

The provider computes the preceding index and owns the resulting traversal transition. At the lower bound it returns the current Destination without creating backend work.

Property: base

The structural base path exposed by the active provider.

Property: base: Why

Routers can establish a stable mount root separately from the changing current URL.

Property: base: Ownership and lifetime

Navigation.base reads the active service synchronously; the provider owns the stored string for its Layer lifetime.

Property: canGoBack

Whether the retained history contains a preceding entry.

Property: canGoBack: Why

Callers can disable back actions without duplicating provider bounds logic.

Property: canGoBack: Ownership and lifetime

Navigation.canGoBack is a provider-backed RefSubject view. The provider owns its state; each consumer Scope owns and releases its observation.

Property: canGoForward

Whether the retained history contains a following entry.

Property: canGoForward: Why

Callers can disable forward actions after pushes truncate the forward branch.

Property: canGoForward: Ownership and lifetime

Navigation.canGoForward is a provider-backed RefSubject view. The provider owns its state; each consumer Scope owns and releases its observation.

Property: currentEntry

The reactive committed destination at the active history index.

Property: currentEntry: Why

Readers observe committed history. A before-navigation decision can still cancel or redirect the separate proposed transition without changing this value. A committed destination does not imply that the selected page has finished loading data or mounting its DOM.

Property: currentEntry: Ownership and lifetime

Navigation.currentEntry is a provider-backed RefSubject view. The provider owns its state; each consumer Scope owns and releases its observation.

Property: entries

The reactive retained history entries in traversal order.

Property: entries: Why

Back, forward, and keyed traversal need an explicit bounded history model in every provider.

Property: entries: Ownership and lifetime

Navigation.entries is a provider-backed RefSubject view. The provider owns its state; each consumer Scope owns and releases its observation.

Property: forward

Traverses to the following retained entry when one exists.

Property: forward: Why

Bounds behavior stays provider-neutral and preserves forward-history identity.

Property: forward: Ownership and lifetime

The provider computes the following index and owns the resulting traversal transition. At the upper bound it returns the current Destination without creating backend work.

Method: navigate

Starts a push or replacement transition to a URL.

Method: navigate: Why

All backends use the same ordered guard, redirect, commit, and post-commit protocol.

Method: navigate: Ownership and lifetime

The provider owns the proposed transition until it commits, redirects, cancels, fails, or is interrupted. Once its backend mutation commits, later interruption cannot undo that mutation.

Property: onBeforeNavigation

Registers a scoped pre-commit navigation handler.

Property: onBeforeNavigation: Why

Handlers can allow, redirect, or cancel before the backend mutates history.

Property: onBeforeNavigation: Ownership and lifetime

Registration captures the current Effect context and requires Scope. Closing that Scope unregisters the handler and releases retained references.

Property: onNavigation

Registers a scoped post-commit navigation handler.

Property: onNavigation: Why

Follow-up synchronization runs only after history and current-entry state agree.

Property: onNavigation: Ownership and lifetime

Registration captures the current Effect context and requires Scope. Closing that Scope unregisters the handler and releases retained references.

Property: origin

The absolute origin used to resolve relative destinations.

Property: origin: Why

A single origin makes same-document classification and URL normalization consistent across providers.

Property: origin: Ownership and lifetime

Navigation.origin reads the active service synchronously; the provider owns the stored string for its Layer lifetime.

Method: reload

Reloads the current destination through the active backend.

Method: reload: Why

Reload behavior remains explicit without pretending it is a push or keyed traversal.

Method: reload: Ownership and lifetime

The active provider owns reload work and decides whether the platform can remain in-process. Browser reload may leave the current JavaScript lifetime once the History API action commits.

Property: transition

The reactive transition while navigation is pending.

Property: transition: Why

Renderers can show pending work without treating a proposal as the committed destination.

Property: transition: Ownership and lifetime

Navigation.transition is a provider-backed RefSubject view. The provider owns its state; each consumer Scope owns and releases its observation.

Use transition.asComputed() when pending UI must observe Option.none() as the transition ends. The Filtered observation itself emits only present transitions, so it cannot signal absence by publishing another Transition. Reading it while absent has the usual Filtered NoSuchElementError behavior.

Property: traverseTo

Traverses to a retained entry by stable key.

Property: traverseTo: Why

A key addresses history identity even when several entries share the same URL.

Property: traverseTo: Ownership and lifetime

The provider owns keyed lookup and the resulting backend traversal. The current key is a no-op; an unknown key fails with NavigationError before any backend commit.

Method: updateCurrentEntry

Replaces application state on the current committed entry.

Method: updateCurrentEntry: Why

State updates should not create a new navigation position or remount the selected route.

Method: updateCurrentEntry: Ownership and lifetime

The provider owns the replacement transition and retains the supplied state only if it commits. The existing Destination key and history position remain current.

Examples

import { Navigation } from "@typed/navigation/Navigation"
import * as Effect from "effect/Effect"

const goToAccount = Navigation.navigate("/account", { history: "push" })
const current = Navigation.currentEntry

Effect service and Scope concepts follow the Effect v4 model documented at https://effect.website/docs/requirements-management/services/ and https://effect.website/docs/resource-management/scope/.

Other public imports

These import paths expose the same declaration. Each page retains its own public name and signature.

Source