[{"name":"App","comment":" Top-level application wiring.\n\nTakes routes, pages, shared state configuration, and produces a\n`Browser.application` Program.\n\n## Usage\n\n    main : Program Flags Model Msg\n    main =\n        App.create\n            { init = init\n            , router = router\n            , toRoute = toRoute\n            , toPage = toPage\n            , notFound = notFoundView\n            , shared =\n                { init = sharedInit\n                , update = sharedUpdate\n                , subscriptions = sharedSubscriptions\n                }\n            , layout = \\_ _ -> Layout.none\n            }\n\n\n## Types\n\n@docs Config, Model, Msg, PageView\n\n\n## Creating an App\n\n@docs create, page\n\n","unions":[{"name":"Model","comment":" The application model.\n","args":["shared","pageModel"],"cases":[]},{"name":"Msg","comment":" The application message type.\n","args":["sharedMsg","pageMsg"],"cases":[["UrlRequested",["Browser.UrlRequest"]],["UrlChanged",["Url.Url"]],["SharedMsg",["sharedMsg"]],["PageMsg",["pageMsg"]]]},{"name":"PageView","comment":" A page view bundles the page's init/update/view/subscriptions in a\nuniform type. Created via `App.page`.\n","args":["shared","pageModel","pageMsg"],"cases":[]}],"aliases":[{"name":"Config","comment":" Configuration for creating an App.\n\n  - `router`: the Route.Router for URL parsing\n  - `toRoute`: function that determines the current route from a URL, with a fallback\n  - `toPage`: given a route and shared state, produces the page view\n  - `notFound`: view shown for unmatched URLs\n  - `shared`: shared state init/update/subscriptions\n\n","args":["flags","shared","sharedMsg","route","pageModel","pageMsg"],"type":"{ router : Route.Router route, toRoute : Url.Url -> route, toPage : route -> shared -> App.PageView shared pageModel pageMsg, shared : { init : flags -> ( shared, Platform.Cmd.Cmd sharedMsg ), update : sharedMsg -> shared -> ( shared, Platform.Cmd.Cmd sharedMsg ), subscriptions : shared -> Platform.Sub.Sub sharedMsg } }"}],"values":[{"name":"create","comment":" Create a `Browser.application` from the config.\n\nThis is the main entry point. It wires together routing, page lifecycle,\nshared state, and the Elm runtime.\n\n    main =\n        App.create { ... }\n\n","type":"App.Config flags shared sharedMsg route pageModel pageMsg -> Platform.Program flags (App.Model shared pageModel) (App.Msg sharedMsg pageMsg)"},{"name":"page","comment":" Create a PageView from a page's functions and model/msg wrappers.\n\n    toPage : Route -> Shared -> PageView Shared PageModel PageMsg\n    toPage route shared =\n        case route of\n            Home ->\n                App.page\n                    { init = \\_ -> ( (), Cmd.none )\n                    , update = \\_ m -> ( m, Cmd.none )\n                    , view = \\_ _ -> Html.text \"Home\"\n                    , subscriptions = \\_ _ -> Sub.none\n                    }\n\n","type":"{ init : shared -> ( pageModel, Platform.Cmd.Cmd pageMsg ), update : pageMsg -> pageModel -> ( pageModel, Platform.Cmd.Cmd pageMsg ), view : shared -> pageModel -> Html.Html pageMsg, subscriptions : shared -> pageModel -> Platform.Sub.Sub pageMsg } -> App.PageView shared pageModel pageMsg"}],"binops":[]},{"name":"App.Effect","comment":" The Effect type wraps Cmd with shared state updates and navigation.\n\nPages return `Effect msg` instead of `Cmd msg` from their update functions\nwhen they need to update shared state or navigate. The App module interprets\nEffects into actual Cmds.\n\n    update : Msg -> Model -> ( Model, Effect Msg )\n    update msg model =\n        case msg of\n            LoggedIn user ->\n                ( model\n                , Effect.batch\n                    [ Effect.updateShared (\\s -> { s | user = Just user })\n                    , Effect.pushUrl \"/\"\n                    ]\n                )\n\n            FetchData ->\n                ( model\n                , Effect.sendCmd (Http.get { ... })\n                )\n\n## Types\n\n@docs Effect\n\n## Primitives\n\n@docs none, sendCmd, batch\n\n## Shared State\n\n@docs updateShared\n\n## Navigation\n\n@docs pushUrl, replaceUrl, back, loadExternalUrl\n\n## Transforming Effects\n\n@docs map\n\n","unions":[{"name":"Effect","comment":" An effect that the App module interprets.\n\n  - `None` : no effect\n  - `Batch` : combine multiple effects\n  - `SendCmd` : a regular Elm Cmd\n  - `UpdateShared` : modify the shared state\n  - `PushUrl` : navigate to a new URL (adds to history)\n  - `ReplaceUrl` : navigate without adding history\n  - `Back` : go back N steps in history\n  - `LoadExternalUrl` : navigate to an external URL (full page load)\n\n","args":["shared","msg"],"cases":[["None",[]],["Batch",["List.List (App.Effect.Effect shared msg)"]],["SendCmd",["Platform.Cmd.Cmd msg"]],["UpdateShared",["shared -> shared"]],["PushUrl",["String.String"]],["ReplaceUrl",["String.String"]],["Back",["Basics.Int"]],["LoadExternalUrl",["String.String"]]]}],"aliases":[],"values":[{"name":"back","comment":" Go back N steps in browser history.\n","type":"Basics.Int -> App.Effect.Effect shared msg"},{"name":"batch","comment":" Combine multiple effects.\n","type":"List.List (App.Effect.Effect shared msg) -> App.Effect.Effect shared msg"},{"name":"loadExternalUrl","comment":" Navigate to an external URL. This triggers a full page load,\nleaving the SPA.\n","type":"String.String -> App.Effect.Effect shared msg"},{"name":"map","comment":" Map over the message type of an Effect.\n","type":"(a -> b) -> App.Effect.Effect shared a -> App.Effect.Effect shared b"},{"name":"none","comment":" No effect.\n","type":"App.Effect.Effect shared msg"},{"name":"pushUrl","comment":" Navigate to a URL (adds to browser history).\n\n    Effect.pushUrl \"/blog/42\"\n\n","type":"String.String -> App.Effect.Effect shared msg"},{"name":"replaceUrl","comment":" Navigate to a URL without adding to browser history.\n","type":"String.String -> App.Effect.Effect shared msg"},{"name":"sendCmd","comment":" Wrap a regular Cmd as an Effect.\n","type":"Platform.Cmd.Cmd msg -> App.Effect.Effect shared msg"},{"name":"updateShared","comment":" Update the shared state with a transformation function.\n\n    Effect.updateShared (\\s -> { s | user = Just user })\n\n","type":"(shared -> shared) -> App.Effect.Effect shared msg"}],"binops":[]},{"name":"App.Navigation","comment":" Type-safe navigation helpers.\n\nThese work with the `Route.Router` and `Effect` types to provide\nnavigation using route values instead of raw strings.\n\n    -- In an update function:\n    ( model, Navigation.navigateTo router (BlogPost 42) )\n\n    -- In a view function:\n    Navigation.link router (BlogPost 42) [ class \"blog-link\" ] [ text \"Read\" ]\n\n\n## Navigation Effects\n\n@docs navigateTo, replaceRoute\n\n\n## View Helpers\n\n@docs link\n\n","unions":[],"aliases":[],"values":[{"name":"link","comment":" Create a link element that navigates via the SPA router.\n\n    Navigation.link router (BlogPost 42) [ class \"blog-link\" ] [ text \"Read post\" ]\n    -- Renders: <a href=\"/blog/42\">Read post</a>\n    -- With click handler that prevents default navigation\n\n","type":"Route.Router route -> route -> List.List (Html.Attribute msg) -> List.List (Html.Html msg) -> Html.Html msg"},{"name":"navigateTo","comment":" Navigate to a route (adds to browser history).\n\n    Navigation.navigateTo router (BlogPost 42)\n    -- Equivalent to: Effect.pushUrl \"/blog/42\"\n\n","type":"Route.Router route -> route -> App.Effect.Effect shared msg"},{"name":"replaceRoute","comment":" Navigate to a route without adding to browser history.\n","type":"Route.Router route -> route -> App.Effect.Effect shared msg"}],"binops":[]},{"name":"App.Shared","comment":" Cross-cutting shared state management.\n\nThe shared state is global state accessible to all pages. Pages read it\nthrough their function signatures (it's passed to init, view, subscriptions)\nand update it through Effects.\n\nUsers define their own shared type and provide init/update/subscriptions:\n\n    type alias Shared =\n        { user : Maybe User\n        , theme : Theme\n        , apiUrl : String\n        }\n\n    sharedConfig : SharedConfig Flags Shared SharedMsg\n    sharedConfig =\n        App.Shared.sharedConfig\n            { init = \\flags -> ( { user = Nothing, theme = Light, apiUrl = flags.apiUrl }, Cmd.none )\n            , update = \\msg shared -> ( shared, Cmd.none )\n            , subscriptions = \\shared -> Sub.none\n            }\n\n## Types\n\n@docs SharedConfig\n\n## Configuration\n\n@docs sharedConfig\n\n## Runtime\n\n@docs initShared, updateShared, sharedSubscriptions\n\n","unions":[{"name":"SharedConfig","comment":" Configuration for shared state management. Opaque type that bundles\ninit, update, and subscriptions for the shared state.\n","args":["flags","shared","sharedMsg"],"cases":[]}],"aliases":[],"values":[{"name":"initShared","comment":" Initialize the shared state from flags.\n","type":"App.Shared.SharedConfig flags shared sharedMsg -> flags -> ( shared, Platform.Cmd.Cmd sharedMsg )"},{"name":"sharedConfig","comment":" Create a shared state configuration.\n","type":"{ init : flags -> ( shared, Platform.Cmd.Cmd sharedMsg ), update : sharedMsg -> shared -> ( shared, Platform.Cmd.Cmd sharedMsg ), subscriptions : shared -> Platform.Sub.Sub sharedMsg } -> App.Shared.SharedConfig flags shared sharedMsg"},{"name":"sharedSubscriptions","comment":" Get shared state subscriptions.\n","type":"App.Shared.SharedConfig flags shared sharedMsg -> shared -> Platform.Sub.Sub sharedMsg"},{"name":"updateShared","comment":" Update the shared state with a message.\n","type":"App.Shared.SharedConfig flags shared sharedMsg -> sharedMsg -> shared -> ( shared, Platform.Cmd.Cmd sharedMsg )"}],"binops":[]},{"name":"Layout","comment":" Composable layout wrappers.\n\nLayouts wrap page views with surrounding UI (navigation, sidebar, footer, etc).\nThey can be stateless or stateful.\n\n## Stateless layout\n\n    sidebarLayout : Layout shared () Never msg\n    sidebarLayout =\n        Layout.static\n            { key = \"sidebar\"\n            , view = \\shared pageView ->\n                Html.div []\n                    [ sidebar shared\n                    , Html.div [ class \"content\" ] [ pageView ]\n                    ]\n            }\n\n## Stateful layout\n\n    collapsibleSidebar : Layout shared SidebarModel SidebarMsg msg\n    collapsibleSidebar =\n        Layout.stateful\n            { key = \"sidebar\"\n            , init = \\shared -> ( { open = True }, Cmd.none )\n            , update = \\msg model -> ( { model | open = not model.open }, Cmd.none )\n            , view = \\shared model pageView ->\n                Html.div []\n                    [ if model.open then sidebar shared else Html.text \"\"\n                    , pageView\n                    ]\n            , subscriptions = \\shared model -> Sub.none\n            }\n\n## Types\n\n@docs Layout\n\n## Constructors\n\n@docs none, static, stateful\n\n## Wiring\n\n@docs withLayout, getLayoutKey, applyLayout\n\n## Internal Types\n\n@docs Msg, LayoutState\n\n## Runtime\n\n@docs initLayoutState, updateLayoutState, viewWithLayout, layoutSubscriptions\n\n","unions":[{"name":"Layout","comment":" An opaque layout type.\n\nType parameters:\n  - `shared` : the shared state type\n  - `layoutModel` : this layout's own model (use `()` for stateless)\n  - `layoutMsg` : this layout's own message type (use `Never` for stateless)\n  - `pageMsg` : the page message type that flows through\n\n","args":["shared","layoutModel","layoutMsg","pageMsg"],"cases":[]},{"name":"LayoutState","comment":" Runtime state for a layout instance.\n","args":["layoutModel"],"cases":[["NoState",[]],["HasState",["String.String","layoutModel"]]]},{"name":"Msg","comment":" Combined message type for stateful layouts. Pages produce `PageMsg`,\nthe layout itself produces `LayoutMsg`.\n","args":["layoutMsg","pageMsg"],"cases":[["LayoutMsg",["layoutMsg"]],["PageMsg",["pageMsg"]]]}],"aliases":[],"values":[{"name":"applyLayout","comment":" Apply a stateless layout to page content.\n","type":"Layout.Layout shared () Basics.Never pageMsg -> shared -> Html.Html pageMsg -> Html.Html pageMsg"},{"name":"getLayoutKey","comment":" Get the key that identifies this layout.\n","type":"Layout.Layout shared layoutModel layoutMsg pageMsg -> String.String"},{"name":"initLayoutState","comment":" Initialize layout state.\n","type":"Layout.Layout shared layoutModel layoutMsg pageMsg -> shared -> ( Layout.LayoutState layoutModel, Platform.Cmd.Cmd layoutMsg )"},{"name":"layoutSubscriptions","comment":" Get subscriptions from a stateful layout.\n","type":"Layout.Layout shared layoutModel layoutMsg pageMsg -> shared -> Layout.LayoutState layoutModel -> Platform.Sub.Sub layoutMsg"},{"name":"none","comment":" No layout -- the page renders bare.\n","type":"Layout.Layout shared () Basics.Never msg"},{"name":"stateful","comment":" Create a stateful layout with its own model, messages, and lifecycle.\n\nLayout state persists across page transitions when pages share the same\nlayout key. The layout is only re-initialized when navigating to a page\nwith a different layout key.\n\n","type":"{ key : String.String, init : shared -> ( layoutModel, Platform.Cmd.Cmd layoutMsg ), update : layoutMsg -> layoutModel -> ( layoutModel, Platform.Cmd.Cmd layoutMsg ), view : shared -> layoutModel -> Html.Html pageMsg -> Html.Html (Layout.Msg layoutMsg pageMsg), subscriptions : shared -> layoutModel -> Platform.Sub.Sub layoutMsg } -> Layout.Layout shared layoutModel layoutMsg pageMsg"},{"name":"static","comment":" Create a stateless layout.\n\nThe `key` is used to determine layout identity: pages sharing the same\nlayout key share layout state (relevant for stateful layouts) and don't\ntrigger layout re-initialization during navigation.\n\n","type":"{ key : String.String, view : shared -> Html.Html pageMsg -> Html.Html pageMsg } -> Layout.Layout shared () Basics.Never pageMsg"},{"name":"updateLayoutState","comment":" Update layout state with a layout message.\n","type":"Layout.Layout shared layoutModel layoutMsg pageMsg -> layoutMsg -> Layout.LayoutState layoutModel -> ( Layout.LayoutState layoutModel, Platform.Cmd.Cmd layoutMsg )"},{"name":"viewWithLayout","comment":" Render a stateful layout with its state and page content.\n","type":"Layout.Layout shared layoutModel layoutMsg pageMsg -> shared -> Layout.LayoutState layoutModel -> Html.Html pageMsg -> Html.Html (Layout.Msg layoutMsg pageMsg)"},{"name":"withLayout","comment":" Placeholder: associate a layout with a page.\nReturns the layout key for use by the App module.\n","type":"Layout.Layout shared lm lmsg pageMsg -> String.String"}],"binops":[]},{"name":"Page","comment":" Page lifecycle abstraction.\n\nPages are values, not modules. You can create them with different complexity\nlevels, pass them around, and compose them.\n\n    -- Simplest: view-only, no state\n    aboutPage =\n        Page.static\n            { view = \\shared -> Html.text \"About\"\n            }\n\n    -- With local state:\n    counterPage =\n        Page.sandbox\n            { init = \\shared -> 0\n            , update = \\msg model -> model + 1\n            , view = \\shared model -> Html.text (String.fromInt model)\n            }\n\n    -- Full page with commands:\n    blogPage =\n        Page.element\n            { init = \\shared -> ( Loading, fetchPosts shared.apiUrl )\n            , update = \\msg model -> ( model, Cmd.none )\n            , view = \\shared model -> Html.text \"Blog\"\n            , subscriptions = \\shared model -> Sub.none\n            }\n\n## Types\n\n@docs Page, PageState\n\n## Constructors\n\n@docs static, sandbox, element\n\n## Transformations\n\n@docs mapMsg, mapModel, mapBoth\n\n## Accessors\n\n@docs init, update, view, subscriptions\n\n","unions":[{"name":"Page","comment":" An opaque page type. Internally normalizes all page variants to the\nfull init/update/view/subscriptions shape.\n\nType parameters:\n  - `shared` : the shared state type passed to all page functions\n  - `model` : this page's local model\n  - `msg` : this page's message type\n\n","args":["shared","model","msg"],"cases":[]},{"name":"PageState","comment":" The loading state of a page, used for transition support.\n","args":["model"],"cases":[["Loading",[]],["Loaded",["model"]],["Failed",["String.String"]]]}],"aliases":[],"values":[{"name":"element","comment":" Create a full page with commands and subscriptions.\n\n    blogPage =\n        Page.element\n            { init = \\shared -> ( initialModel, loadPosts )\n            , update = updateBlog\n            , view = viewBlog\n            , subscriptions = \\shared model -> Time.every 60000 Tick\n            }\n\n","type":"{ init : shared -> ( model, Platform.Cmd.Cmd msg ), update : msg -> model -> ( model, Platform.Cmd.Cmd msg ), view : shared -> model -> Html.Html msg, subscriptions : shared -> model -> Platform.Sub.Sub msg } -> Page.Page shared model msg"},{"name":"init","comment":" Get the init function from a page.\n","type":"Page.Page shared model msg -> shared -> ( model, Platform.Cmd.Cmd msg )"},{"name":"mapBoth","comment":" Map over both a page's model and message types. This is the primary\nway to wire a page into the application's unified model/msg types.\n\n    App.page homeSpec =\n        Page.mapBoth HomeModel HomeMsg (\\(HomeMsg m) -> m) (\\(HomeModel m) -> m) homePage\n\nMore commonly used through the `App.page` helper.\n\n","type":"(model -> mappedModel) -> (msg -> mappedMsg) -> (mappedMsg -> Maybe.Maybe msg) -> (mappedModel -> Maybe.Maybe model) -> Page.Page shared model msg -> Page.Page shared mappedModel mappedMsg"},{"name":"mapModel","comment":" Map over a page's model type.\n","type":"(model -> mappedModel) -> (mappedModel -> model) -> Page.Page shared model msg -> Page.Page shared mappedModel msg"},{"name":"mapMsg","comment":" Map over a page's message type.\n","type":"(msg -> mappedMsg) -> Page.Page shared model msg -> Page.Page shared model mappedMsg"},{"name":"sandbox","comment":" Create a sandbox page with local state but no commands.\n\n    counterPage =\n        Page.sandbox\n            { init = \\shared -> 0\n            , update = \\msg model -> model + 1\n            , view = \\shared model -> Html.text (String.fromInt model)\n            }\n\n","type":"{ init : shared -> model, update : msg -> model -> model, view : shared -> model -> Html.Html msg } -> Page.Page shared model msg"},{"name":"static","comment":" Create a static page with only a view function.\nNo model, no messages, no update logic.\n\n    aboutPage =\n        Page.static\n            { view = \\shared -> Html.div [] [ Html.text \"About us\" ]\n            }\n\n","type":"{ view : shared -> Html.Html msg } -> Page.Page shared () msg"},{"name":"subscriptions","comment":" Get the subscriptions function from a page.\n","type":"Page.Page shared model msg -> shared -> model -> Platform.Sub.Sub msg"},{"name":"update","comment":" Get the update function from a page.\n","type":"Page.Page shared model msg -> msg -> model -> ( model, Platform.Cmd.Cmd msg )"},{"name":"view","comment":" Get the view function from a page.\n","type":"Page.Page shared model msg -> shared -> model -> Html.Html msg"}],"binops":[]},{"name":"Page.Element","comment":" Convenience module for creating element pages (full TEA).\n\n    import Page.Element as Element\n\n    blogPage =\n        Element.page\n            { init = \\shared -> ( Loading, fetchPosts )\n            , update = updateBlog\n            , view = viewBlog\n            , subscriptions = \\shared model -> Sub.none\n            }\n\n@docs page\n\n","unions":[],"aliases":[],"values":[{"name":"page","comment":" Create a full page with commands and subscriptions.\nAlias for `Page.element`.\n","type":"{ init : shared -> ( model, Platform.Cmd.Cmd msg ), update : msg -> model -> ( model, Platform.Cmd.Cmd msg ), view : shared -> model -> Html.Html msg, subscriptions : shared -> model -> Platform.Sub.Sub msg } -> Page.Page shared model msg"}],"binops":[]},{"name":"Page.Sandbox","comment":" Convenience module for creating sandbox pages (state, no commands).\n\n    import Page.Sandbox as Sandbox\n\n    counterPage =\n        Sandbox.page\n            { init = \\shared -> 0\n            , update = \\msg model -> model + 1\n            , view = \\shared model -> Html.text (String.fromInt model)\n            }\n\n@docs page\n\n","unions":[],"aliases":[],"values":[{"name":"page","comment":" Create a sandbox page with local state but no commands.\nAlias for `Page.sandbox`.\n","type":"{ init : shared -> model, update : msg -> model -> model, view : shared -> model -> Html.Html msg } -> Page.Page shared model msg"}],"binops":[]},{"name":"Page.Static","comment":" Convenience module for creating static pages.\n\n    import Page.Static as Static\n\n    aboutPage =\n        Static.page\n            { view = \\shared -> Html.text \"About\"\n            }\n\n@docs page\n\n","unions":[],"aliases":[],"values":[{"name":"page","comment":" Create a static page with only a view function.\nAlias for `Page.static`.\n","type":"{ view : shared -> Html.Html msg } -> Page.Page shared () msg"}],"binops":[]},{"name":"Page.Transition","comment":" Page lifecycle hooks for enter/exit transitions and loading states.\n\nThese are composable modifiers applied via pipeline to any page:\n\n    blogPage =\n        Page.element { ... }\n            |> Transition.onEnter (\\shared -> loadPosts shared.apiUrl)\n            |> Transition.onLeave (\\shared model -> saveDraft model.draft)\n            |> Transition.withLoading loadingView\n            |> Transition.keepAlive\n\n## Types\n\n@docs PageState, Lifecycle\n\n## Lifecycle Constructors and Modifiers\n\n@docs lifecycle, onEnter, onLeave, withLoading, keepAlive\n\n## Lifecycle Accessors\n\n@docs getOnEnter, getOnLeave, isKeepAlive\n\n","unions":[{"name":"Lifecycle","comment":" Lifecycle metadata for a page. Stored alongside the page, not inside it,\nbecause the App module needs access to manage page transitions.\n","args":["shared","model","msg"],"cases":[]},{"name":"PageState","comment":" The loading state of a page.\n","args":["model"],"cases":[["Loading",[]],["Loaded",["model"]],["Failed",["String.String"]]]}],"aliases":[],"values":[{"name":"getOnEnter","comment":" Get the onEnter command producer, if any.\n","type":"Page.Transition.Lifecycle shared model msg -> Maybe.Maybe (shared -> Platform.Cmd.Cmd msg)"},{"name":"getOnLeave","comment":" Get the onLeave command producer, if any.\n","type":"Page.Transition.Lifecycle shared model msg -> Maybe.Maybe (shared -> model -> Platform.Cmd.Cmd msg)"},{"name":"isKeepAlive","comment":" Check if the page is marked as keep-alive.\n","type":"Page.Transition.Lifecycle shared model msg -> Basics.Bool"},{"name":"keepAlive","comment":" Mark the page as keep-alive. When navigating away, the model is preserved\ninstead of being destroyed. When returning to this page, the preserved model\nis reused instead of running init again.\n\n    myLifecycle =\n        Transition.lifecycle\n            |> Transition.keepAlive\n\n","type":"Page.Transition.Lifecycle shared model msg -> Page.Transition.Lifecycle shared model msg"},{"name":"lifecycle","comment":" Create a default lifecycle with no hooks.\n","type":"Page.Transition.Lifecycle shared model msg"},{"name":"onEnter","comment":" Add an onEnter hook. The command is fired when the page becomes active.\n\n    myLifecycle =\n        Transition.lifecycle\n            |> Transition.onEnter (\\shared -> loadData shared.apiUrl)\n\n","type":"(shared -> Platform.Cmd.Cmd msg) -> Page.Transition.Lifecycle shared model msg -> Page.Transition.Lifecycle shared model msg"},{"name":"onLeave","comment":" Add an onLeave hook. The command is fired when navigating away from the page.\n\n    myLifecycle =\n        Transition.lifecycle\n            |> Transition.onLeave (\\shared model -> saveDraft model.draft)\n\n","type":"(shared -> model -> Platform.Cmd.Cmd msg) -> Page.Transition.Lifecycle shared model msg -> Page.Transition.Lifecycle shared model msg"},{"name":"withLoading","comment":" Set a loading view shown while onEnter commands are in-flight.\n\n    myLifecycle =\n        Transition.lifecycle\n            |> Transition.withLoading (\\shared -> Html.text \"Loading...\")\n\n","type":"(shared -> Html.Html msg) -> Page.Transition.Lifecycle shared model msg -> Page.Transition.Lifecycle shared model msg"}],"binops":[]},{"name":"Route","comment":" Bidirectional route definition combinators.\n\nDefine a route once, get both URL parsing and URL building from the same\ndefinition. No code generation required.\n\n## Approach\n\nThe core idea is a `Codec` that threads both a parser and a builder through\na pipeline. Each segment combinator advances both sides simultaneously.\n\nThe `codec` function takes two arguments:\n\n  1. The route constructor (for parsing: `BlogPost : Int -> Route`)\n  2. An extractor (for building: `\\r -> case r of BlogPost id -> Just (id, ()) ; _ -> Nothing`)\n\nThe extractor returns a tuple-chain that mirrors the constructor's\narguments. The compiler checks that the constructor and extractor agree.\n\n## Example\n\n    type Route\n        = Home\n        | Blog\n        | BlogPost Int\n        | UserProfile String\n        | UserRepo String String\n\n    router : Router Route\n    router =\n        oneOf\n            [ top Home\n            , codec Blog (\\r -> case r of Blog -> Just () ; _ -> Nothing)\n                |> literal \"blog\"\n            , codec BlogPost (\\r -> case r of BlogPost id -> Just ( id, () ) ; _ -> Nothing)\n                |> literal \"blog\"\n                |> int\n            , codec UserProfile (\\r -> case r of UserProfile name -> Just ( name, () ) ; _ -> Nothing)\n                |> literal \"user\"\n                |> string\n            , codec UserRepo (\\r -> case r of UserRepo owner repo -> Just ( owner, ( repo, () ) ) ; _ -> Nothing)\n                |> literal \"user\"\n                |> string\n                |> string\n            ]\n\n    parse router \"/blog/42\"          --> Just (BlogPost 42)\n    toPath router (BlogPost 42)      --> Just \"/blog/42\"\n    toPath router (UserRepo \"a\" \"b\") --> Just \"/user/a/b\"\n\n\n## Types\n\n@docs Codec, Router\n\n\n## Defining Routes\n\n@docs codec, top\n\n\n## Segment Combinators\n\n@docs literal, literalCi, int, string, custom\n\n\n## Additional Segment Primitives\n\n@docs float, nonEmptyString, uuid\n\n\n## Constrained Segment Primitives\n\n@docs intRange, slug, enum\n\n\n## Splat Routes\n\n@docs splat\n\n\n## Nesting\n\n@docs nest\n\n\n## Query Parameters\n\n@docs withQuery\n\n\n## Router\n\n@docs oneOf\n\n\n## Base Path\n\n@docs withBasePath\n\n\n## Parsing and Building\n\n@docs parse, matches, toPath, toSegments, fromUrl, parseFull, toFullPath\n\n\n## Canonicalization\n\nUseful when paired with `literalCi`, `intRange`, `slug`, `enum`, or\nnested routes whose canonical form differs from accepted input.\n\n@docs canonicalize, redirectTo\n\n\n## Hash Routing\n\nFor SPAs hosted where you can't control server rewrites (GitHub Pages,\nplain static hosts), put the route in the URL fragment. These functions\nread/write the fragment and ignore any router base path.\n\n@docs parseHash, toHashPath, fromUrlHash\n\n\n## Query String Utilities\n\nCombinators for splicing raw query strings onto built paths. Useful when\nthe query portion is computed imperatively (overlay tracking params,\nmutate one key, etc.) rather than declared in the codec.\n\n@docs appendQuery, replaceQuery\n\n\n## Active Link Helpers\n\n@docs startsWith\n\n","unions":[{"name":"Codec","comment":" A bidirectional route codec.\n\nType parameters:\n\n  - `parseState` — what the parser still needs to produce (starts as the\n    constructor type, e.g., `Int -> Route`, consumed down to `Route`)\n  - `buildState` — what the builder still needs to consume (starts as the\n    extractor result type, e.g., `( Int, () )`, consumed down to `()`)\n  - `route` — the final route type\n\n","args":["parseState","buildState","route"],"cases":[]},{"name":"Router","comment":" A router holds finalized codecs with uniform type and an optional\nbase path that prefixes every URL.\n","args":["route"],"cases":[]}],"aliases":[],"values":[{"name":"appendQuery","comment":" Append a raw query string to a path. An empty query is dropped, so\nno trailing `?` is produced. A leading `?` on the query is tolerated.\n\n    appendQuery \"page=2\" \"/search\"  --> \"/search?page=2\"\n    appendQuery \"\"        \"/search\" --> \"/search\"\n    appendQuery \"?page=2\" \"/search\" --> \"/search?page=2\"\n\nUseful in combination with `toPath` and `Route.Query.fromList` /\n`Route.Query.merge` for building URLs whose query portion is computed\nimperatively (overlay tracking params, mutate one key, etc.).\n\n    toPath router (Search \"elm\")\n        |> Maybe.map (Route.appendQuery \"utm_source=twitter\")\n\n","type":"String.String -> String.String -> String.String"},{"name":"canonicalize","comment":" Parse a path then rebuild it as the canonical form. Returns\n`Nothing` if the path does not parse.\n\nPairs with `literalCi`, `intRange`, `slug`, and `enum`, all of which\naccept inputs whose canonical form differs from what the user typed.\nAlso collapses consecutive slashes and trailing slashes since the\nparser already tolerates them.\n\n    canonicalize router \"/Blog\"        --> Just \"/blog\"      -- with literalCi\n    canonicalize router \"/blog/42/\"    --> Just \"/blog/42\"\n    canonicalize router \"//blog//42//\" --> Just \"/blog/42\"\n    canonicalize router \"/unknown\"     --> Nothing\n\nOperates on the path portion only. If your input includes a query\nstring or fragment, split them off first and reattach with\n`appendQuery` / `replaceQuery`.\n\n","type":"Route.Router route -> String.String -> Maybe.Maybe String.String"},{"name":"codec","comment":" Begin a route codec definition.\n\nThe first argument is the route constructor used for parsing.\nThe second argument extracts parameters from a route value for building.\n\nThe extractor should return `Just (params)` if the route matches, where\nparams is a right-nested tuple chain ending in `()`:\n\n  - 0 params: `Just ()`\n  - 1 param:  `Just ( a, () )`\n  - 2 params: `Just ( a, ( b, () ) )`\n\nExample:\n\n    codec BlogPost (\\r -> case r of BlogPost id -> Just ( id, () ) ; _ -> Nothing)\n        |> literal \"blog\"\n        |> int\n\n","type":"constructor -> (route -> Maybe.Maybe buildTuple) -> Route.Codec constructor buildTuple route"},{"name":"custom","comment":" Capture a path segment with a custom parser and builder.\n\n    custom Slug.fromString Slug.toString\n        : Codec (Slug -> parse) ( Slug, build ) route\n        -> Codec parse build route\n\n","type":"(String.String -> Maybe.Maybe a) -> (a -> String.String) -> Route.Codec (a -> parse) ( a, build ) route -> Route.Codec parse build route"},{"name":"enum","comment":" Capture a path segment that must be one of a finite set of strings,\nmapped to values of a custom type.\n\nMirrors `Query.enum` on the path side. Variants are tried in list order\non both parse and build. Building a route value not present in the\nvariant list returns `Nothing` so a malformed URL is never produced.\n\nThe variant value type must support structural equality (no functions\ninside the type).\n\n    type SortOrder\n        = Asc\n        | Desc\n\n    sortVariants : List ( String, SortOrder )\n    sortVariants =\n        [ ( \"asc\", Asc ), ( \"desc\", Desc ) ]\n\n    codec Sort (\\r -> case r of Sort o -> Just ( o, () ) ; _ -> Nothing)\n        |> literal \"sort\"\n        |> enum sortVariants\n    -- matches \"/sort/asc\"  -> Sort Asc\n    -- matches \"/sort/desc\" -> Sort Desc\n    -- \"/sort/sideways\"     -> Nothing\n\n","type":"List.List ( String.String, a ) -> Route.Codec (a -> parse) ( a, build ) route -> Route.Codec parse build route"},{"name":"float","comment":" Capture a Float path segment.\n\n    codec Plot (\\r -> case r of Plot v -> Just ( v, () ) ; _ -> Nothing)\n        |> literal \"plot\"\n        |> float\n\n","type":"Route.Codec (Basics.Float -> parse) ( Basics.Float, build ) route -> Route.Codec parse build route"},{"name":"fromUrl","comment":" Parse a `Url` (from elm/url) into a route value.\n","type":"Route.Router route -> Url.Url -> Maybe.Maybe route"},{"name":"fromUrlHash","comment":" Parse a `Url`'s fragment as a route.\n\nUseful in `Browser.application`'s `onUrlChange` when using hash-style\nrouting. An absent fragment is treated as the root.\n\n","type":"Route.Router route -> Url.Url -> Maybe.Maybe route"},{"name":"int","comment":" Capture an Int path segment.\n\n    codec BlogPost (\\r -> case r of BlogPost id -> Just ( id, () ) ; _ -> Nothing)\n        |> literal \"blog\"\n        |> int\n\n","type":"Route.Codec (Basics.Int -> parse) ( Basics.Int, build ) route -> Route.Codec parse build route"},{"name":"intRange","comment":" Capture an Int path segment that must lie within `[min, max]`\ninclusive. Out-of-range values fail to parse, and `toPath` returns\n`Nothing` for out-of-range route values, so a route like\n`Page 999` will not silently produce a URL that cannot round-trip.\n\n    codec Page (\\r -> case r of Page n -> Just ( n, () ) ; _ -> Nothing)\n        |> literal \"page\"\n        |> intRange 1 100\n\n","type":"Basics.Int -> Basics.Int -> Route.Codec (Basics.Int -> parse) ( Basics.Int, build ) route -> Route.Codec parse build route"},{"name":"literal","comment":" Add a literal path segment that must match exactly. Does not consume\nany type parameter from the constructor or extractor.\n\n    codec Blog (\\r -> case r of Blog -> Just () ; _ -> Nothing)\n        |> literal \"blog\"\n\n","type":"String.String -> Route.Codec parse build route -> Route.Codec parse build route"},{"name":"literalCi","comment":" Like `literal`, but matches case-insensitively on parse. Always\nbuilds the canonical (original-cased) form, so URLs the library\ngenerates remain consistent.\n\nUseful when migrating from frameworks that match paths case-\ninsensitively (Express, Phoenix), or when you want `/Blog` and\n`/BLOG` to resolve to the same route as `/blog` for SEO redirect\nhygiene without writing every variant by hand.\n\n    codec Blog (\\r -> case r of Blog -> Just () ; _ -> Nothing)\n        |> literalCi \"blog\"\n\n    -- parses \"/blog\", \"/Blog\", \"/BLOG\" all as Blog\n    -- builds Blog as \"/blog\"\n\n","type":"String.String -> Route.Codec parse build route -> Route.Codec parse build route"},{"name":"matches","comment":" Boolean check for whether a URL path matches any route in a router.\nA thin wrapper over `parse` for cases where you only need to know\n\"does this path match\" without the route value itself.\n\n    matches router \"/blog/42\"  --> True\n    matches router \"/unknown\"  --> False\n\n","type":"Route.Router route -> String.String -> Basics.Bool"},{"name":"nest","comment":" Group routes under a common path prefix.\n\n    router =\n        oneOf\n            (List.concat\n                [ [ top Home ]\n                , nest \"blog\"\n                    [ codec BlogList (\\r -> case r of BlogList -> Just () ; _ -> Nothing)\n                    , codec BlogPost (\\r -> case r of BlogPost id -> Just ( id, () ) ; _ -> Nothing)\n                        |> int\n                    ]\n                , nest \"user\"\n                    [ codec UserProfile (\\r -> case r of UserProfile n -> Just ( n, () ) ; _ -> Nothing)\n                        |> string\n                    ]\n                ]\n            )\n\n    -- Nesting can be nested:\n    nest \"api\" (nest \"v1\" [ ... ])\n\n","type":"String.String -> List.List (Route.Codec route () route) -> List.List (Route.Codec route () route)"},{"name":"nonEmptyString","comment":" Capture a non-empty String path segment. Fails to build (returns\n`Nothing` from `toPath`) if the value is empty, which is safer than\nsilently producing a malformed URL like `/user//repo`.\n\n    codec UserProfile (\\r -> case r of UserProfile name -> Just ( name, () ) ; _ -> Nothing)\n        |> literal \"user\"\n        |> nonEmptyString\n\n","type":"Route.Codec (String.String -> parse) ( String.String, build ) route -> Route.Codec parse build route"},{"name":"oneOf","comment":" Combine finalized route codecs into a router. Routes are tried in\ndefinition order for both parsing and building.\n\n    router =\n        oneOf\n            [ top Home\n            , codec Blog (\\r -> case r of Blog -> Just () ; _ -> Nothing)\n                |> literal \"blog\"\n            , codec BlogPost (\\r -> case r of BlogPost id -> Just ( id, () ) ; _ -> Nothing)\n                |> literal \"blog\"\n                |> int\n            ]\n\nNote: every codec must be fully applied (all parameters consumed) before\nbeing passed to `oneOf`. The compiler enforces this — a codec with\nunconsumed parameters will have the wrong type.\n\n","type":"List.List (Route.Codec route () route) -> Route.Router route"},{"name":"parse","comment":" Parse a URL path string into a route value.\n\n    parse router \"/blog/42\" --> Just (BlogPost 42)\n    parse router \"/unknown\" --> Nothing\n\n","type":"Route.Router route -> String.String -> Maybe.Maybe route"},{"name":"parseFull","comment":" Parse a full URL path (with optional query string) into a route and\nquery parameters.\n\n    parseFull router searchQuery \"/search?q=elm&page=2\"\n    --> Just ( SearchResults, { query = Just \"elm\", page = Just 2 } )\n\n","type":"Route.Router route -> Route.Query.QueryCodec query -> String.String -> Maybe.Maybe ( route, query )"},{"name":"parseHash","comment":" Parse a fragment string into a route. Accepts the fragment with or\nwithout a leading `#`. An empty fragment is treated as `/`.\n\n    parseHash router \"#/blog/42\"  --> Just (BlogPost 42)\n    parseHash router \"/blog/42\"   --> Just (BlogPost 42)\n    parseHash router \"\"           --> Just Home\n\nThis bypasses any base path set on the router.\n\n","type":"Route.Router route -> String.String -> Maybe.Maybe route"},{"name":"redirectTo","comment":" Like `canonicalize`, but returns `Just` only when the canonical\nform differs from the input. Returns `Nothing` when the input is\nalready canonical or fails to parse, which collapses both \"no\nredirect needed\" and \"404\" into one branch the caller can handle as\n\"continue with normal routing.\"\n\n    redirectTo router \"/Blog\"     --> Just \"/blog\"\n    redirectTo router \"/blog\"     --> Nothing\n    redirectTo router \"/unknown\"  --> Nothing\n\nUseful in `Browser.application`'s `onUrlChange` to emit a\n`Browser.Navigation.replaceUrl` when the URL is non-canonical:\n\n    case Route.redirectTo router url.path of\n        Just canonical ->\n            ( model, Nav.replaceUrl key canonical )\n\n        Nothing ->\n            -- normal routing\n            ...\n\n","type":"Route.Router route -> String.String -> Maybe.Maybe String.String"},{"name":"replaceQuery","comment":" Replace the query portion of a path. Strips any existing query\nfirst, then appends the new one. An empty query removes any existing\nquery without leaving a trailing `?`.\n\n    replaceQuery \"page=3\" \"/search?page=2\"     --> \"/search?page=3\"\n    replaceQuery \"\"       \"/search?page=2\"     --> \"/search\"\n    replaceQuery \"page=3\" \"/search\"            --> \"/search?page=3\"\n\n","type":"String.String -> String.String -> String.String"},{"name":"slug","comment":" Capture a kebab-case slug path segment.\n\nA slug is a non-empty string of lowercase ASCII letters, digits, and\nhyphens, with no leading or trailing hyphen and no consecutive hyphens.\nOut-of-shape values fail to parse, and `toPath` returns `Nothing` for\nslug-shaped fields whose value would not parse back.\n\n    codec Article (\\r -> case r of Article s -> Just ( s, () ) ; _ -> Nothing)\n        |> literal \"articles\"\n        |> slug\n    -- matches \"/articles/getting-started\", parses to Article \"getting-started\"\n    -- \"/articles/Bad_Slug\" -> Nothing\n\n","type":"Route.Codec (String.String -> parse) ( String.String, build ) route -> Route.Codec parse build route"},{"name":"splat","comment":" Capture all remaining path segments as a `List String`. This must be\nthe final segment in a route definition, since it consumes everything\nthat follows.\n\n    type Route\n        = Docs (List String)\n\n    router =\n        oneOf\n            [ codec Docs (\\r -> case r of Docs path -> Just ( path, () ))\n                |> literal \"docs\"\n                |> splat\n            ]\n\n    parse router \"/docs/intro/setup\"  --> Just (Docs [\"intro\", \"setup\"])\n    parse router \"/docs\"              --> Just (Docs [])\n    toPath router (Docs [\"a\",\"b\",\"c\"]) --> Just \"/docs/a/b/c\"\n\n","type":"Route.Codec (List.List String.String -> parse) ( List.List String.String, build ) route -> Route.Codec parse build route"},{"name":"startsWith","comment":" Test whether one route's path is a prefix of another's. Useful for\nhighlighting nav links when the current route is under a section.\n\n    startsWith router Blog (BlogPost 42)         --> True\n    startsWith router (BlogPost 42) Blog         --> False\n    startsWith router Home anyOtherRoute         --> True\n    startsWith router Blog Blog                  --> True\n\nReturns `False` if either route fails to build.\n\n","type":"Route.Router route -> route -> route -> Basics.Bool"},{"name":"string","comment":" Capture a String path segment. Values are percent-encoded when\nbuilding and percent-decoded when parsing, so a value containing `/`,\nspaces, or other URL-significant characters round-trips cleanly.\n\n    codec UserProfile (\\r -> case r of UserProfile name -> Just ( name, () ) ; _ -> Nothing)\n        |> literal \"user\"\n        |> string\n\n","type":"Route.Codec (String.String -> parse) ( String.String, build ) route -> Route.Codec parse build route"},{"name":"toFullPath","comment":" Build a full URL path with query string from a route and query parameters.\n\n    toFullPath router searchQuery SearchResults\n        { query = Just \"elm\", page = Just 2 }\n    --> Just \"/search?q=elm&page=2\"\n\n","type":"Route.Router route -> Route.Query.QueryCodec query -> route -> query -> Maybe.Maybe String.String"},{"name":"toHashPath","comment":" Build a route as a fragment with a leading `#`.\n\n    toHashPath router (BlogPost 42) --> Just \"#/blog/42\"\n    toHashPath router Home          --> Just \"#/\"\n\nThis bypasses any base path set on the router.\n\n","type":"Route.Router route -> route -> Maybe.Maybe String.String"},{"name":"toPath","comment":" Build a URL path string from a route value.\n\n    toPath router (BlogPost 42) --> Just \"/blog/42\"\n    toPath router Home          --> Just \"/\"\n\n","type":"Route.Router route -> route -> Maybe.Maybe String.String"},{"name":"toSegments","comment":" Build the path segments of a route as a list, without the leading\nslash and without joining. Each segment is percent-decoded, so the\nvalues match what was originally captured. Useful for breadcrumbs and\ncustom rendering that needs each segment in isolation.\n\n    toSegments router (BlogPost 42) --> Just [ \"blog\", \"42\" ]\n    toSegments router Home          --> Just []\n\nA router mounted at a base path includes the base segments first:\n\n    toSegments (router |> withBasePath \"/admin\") (BlogPost 42)\n    --> Just [ \"admin\", \"blog\", \"42\" ]\n\nFor the joined string form, use `toPath`.\n\n","type":"Route.Router route -> route -> Maybe.Maybe (List.List String.String)"},{"name":"top","comment":" A route that matches the root path \"/\" with no parameters.\n\n    top Home  -- matches \"/\"\n\n","type":"route -> Route.Codec route () route"},{"name":"uuid","comment":" Capture a UUID path segment in `8-4-4-4-12` hex format. The string is\nreturned as-is (lowercased on the way out for consistency); use `custom`\nwith your own UUID type if you have one.\n\n    codec ItemPage (\\r -> case r of ItemPage id -> Just ( id, () ) ; _ -> Nothing)\n        |> literal \"items\"\n        |> uuid\n\n","type":"Route.Codec (String.String -> parse) ( String.String, build ) route -> Route.Codec parse build route"},{"name":"withBasePath","comment":" Mount a router at a path prefix. All `parse` and `toPath` operations\ngo through this prefix; routes that don't match the prefix fail to\nparse.\n\n    router =\n        oneOf [ top Home, ... ]\n            |> withBasePath \"/admin\"\n\n    parse router \"/admin\"          --> Just Home\n    parse router \"/admin/blog\"     --> Just Blog\n    parse router \"/blog\"           --> Nothing\n    toPath router Home             --> Just \"/admin\"\n    toPath router Blog             --> Just \"/admin/blog\"\n\nThe base path is normalized: leading slash is added if missing, trailing\nslash is stripped. Calling `withBasePath` again replaces the previous\nvalue (it does not stack).\n\nHash-routing functions (`parseHash`, `toHashPath`, `fromUrlHash`) ignore\nthe base path on purpose — the fragment is independent of the\nserver-served URL path.\n\n","type":"String.String -> Route.Router route -> Route.Router route"},{"name":"withQuery","comment":" Attach a query parameter codec to a finalized route codec.\n\nThis is used with `parseFull` and `toFullPath` for routes that include\nquery parameters.\n\n","type":"Route.Query.QueryCodec query -> Route.Codec route () route -> ( Route.Codec route () route, Route.Query.QueryCodec query )"}],"binops":[]},{"name":"Route.Query","comment":" Typed, bidirectional query parameter handling.\n\nQuery parameters are defined as codecs that can both parse query strings\nand build them back. They compose independently from path codecs.\n\n## Example (pipeline form, recommended)\n\n    type alias SearchParams =\n        { query : Maybe String\n        , page : Int\n        , tags : List String\n        }\n\n    searchQuery : QueryCodec SearchParams\n    searchQuery =\n        Query.succeed SearchParams\n            |> Query.with .query (Query.string \"q\")\n            |> Query.with .page (Query.int \"page\" |> Query.withDefault 1)\n            |> Query.with .tags (Query.list \"tag\")\n            |> Query.build\n\n    Query.parseQuery searchQuery \"q=elm&page=2&tag=fp&tag=web\"\n    --> { query = Just \"elm\", page = 2, tags = [ \"fp\", \"web\" ] }\n\n    Query.buildQuery searchQuery\n        { query = Just \"elm\", page = 2, tags = [ \"fp\", \"web\" ] }\n    --> \"q=elm&page=2&tag=fp&tag=web\"\n\n## Types\n\n@docs QueryCodec\n\n## Primitive Codecs\n\nAll primitives that may be absent return `Maybe a`. Lift to a non-Maybe\ntype with a default via `withDefault`.\n\n@docs string, int, float, bool, flag\n\n## Collection and Enum Codecs\n\n@docs enum, list, csv\n\n## Modifiers\n\n@docs withDefault, required\n\n## Pipeline\n\nBuild composite query codecs in a record-friendly pipeline. This is the\nrecommended way to compose more than one or two parameters.\n\n@docs Pipeline, succeed, with, build\n\n## Tuple Combinators\n\nThe original `mapN` combinators. They remain exported for backwards\ncompatibility, but new code should prefer the pipeline.\n\n@docs map, map2, map3, map4\n\n## Parsing and Building\n\n@docs parseQuery, parseQueryStrict, parseQueryResult, buildQuery\n\n## Url Parsing\n\nConvenience wrappers over the `parseQuery*` family that take a `Url`\ndirectly and read its query field, removing\n`url.query |> Maybe.withDefault \"\"` boilerplate at every call site.\n\n@docs parseUrl, parseUrlStrict, parseUrlResult\n\n## Raw Query Manipulation\n\nFor the common \"change one key, keep all the others\" pattern in\npagination and filter UIs, work directly on the raw query string.\n\n@docs update, drop\n\n## Raw Query Conversion\n\nEscape hatches that expose the parsed `List ( String, String )`\nrepresentation, plus targeted lookup and merging. Use these when\nyou need a query operation that does not fit the typed pipeline.\n\n@docs toList, fromList, lookup, merge, preserve, except, canonicalString\n\n","unions":[{"name":"Pipeline","comment":" An intermediate builder for a pipelined query codec. Construct one\nwith `succeed`, add fields with `with`, finalize with `build`.\n\nThe two type parameters are:\n\n  - `state` — the parser state, which evolves from the constructor type\n    (e.g., `String -> Int -> SearchParams`) to the final result type as\n    fields are added.\n  - `result` — the final record type. Stays constant across the pipeline\n    so each `with` step can extract the field value from a record value.\n\n","args":["state","result"],"cases":[]},{"name":"QueryCodec","comment":" A bidirectional codec for a single query parameter or a composed set\nof query parameters.\n\nThe internal `requiredKeys` field lists keys flagged with\n`Query.required`. It is consulted only by `parseQueryStrict`; lenient\n`parseQuery` ignores it.\n","args":["a"],"cases":[]}],"aliases":[],"values":[{"name":"bool","comment":" An optional boolean query parameter. Parses \"true\"/\"1\" as True,\n\"false\"/\"0\" as False.\n\n    Query.bool \"active\"\n    -- parses \"?active=true\" -> Just True\n    -- parses \"?active=0\"    -> Just False\n    -- parses \"?\"            -> Nothing\n\n","type":"String.String -> Route.Query.QueryCodec (Maybe.Maybe Basics.Bool)"},{"name":"build","comment":" Finalize a pipeline into a `QueryCodec`.\n\nThe `state` and `result` types must be equal — i.e., every field has\nbeen provided to satisfy the constructor passed to `succeed`.\n\n","type":"Route.Query.Pipeline result result -> Route.Query.QueryCodec result"},{"name":"buildQuery","comment":" Build a query string from a value using a codec.\nReturns the query string without a leading \"?\". Returns empty string\nif no parameters are produced.\n\n    buildQuery codec { page = Just 3, sort = Just DateAsc }\n    --> \"page=3&sort=date-asc\"\n\n","type":"Route.Query.QueryCodec a -> a -> String.String"},{"name":"canonicalString","comment":" Normalize a raw query string to a canonical form by sorting its\nentries lexicographically (by key, then by value). Percent-encoding\nis also normalized as a side effect of round-tripping through\n`toList` / `fromList`.\n\nUseful as a stable cache key, deduplication discriminator, or\nanalytics input where two semantically-equivalent URLs should hash\nto the same string.\n\n    Query.canonicalString \"page=2&q=elm\"\n    --> \"page=2&q=elm\"\n\n    Query.canonicalString \"q=elm&page=2\"\n    --> \"page=2&q=elm\"\n\n    Query.canonicalString \"tag=web&q=elm&tag=fp\"\n    --> \"q=elm&tag=fp&tag=web\"\n\nTolerates a leading `?`; the result never includes one.\n\n","type":"String.String -> String.String"},{"name":"csv","comment":" A comma-separated list-valued query parameter. Parses\n`?tag=fp,web,clojure` into `[ \"fp\", \"web\", \"clojure\" ]`.\n\n    Query.csv \"tag\"\n    -- parses \"?tag=fp,web\" -> [ \"fp\", \"web\" ]\n    -- parses \"?tag=\"       -> []\n    -- parses \"?\"           -> []\n\nDistinct from `Query.list`, which collects repeated keys\n(`?tag=fp&tag=web`). Both styles are common in the wild; choose the\none your URLs already use.\n\nThe values must not contain commas. If they might, use `Query.list`\ninstead, since this codec uses `,` as the delimiter and would treat\nan embedded comma as a separator. Empty values produced by adjacent\nor trailing commas are filtered out, so `Query.csv` and `Query.list`\nagree on whether a key contributes any values.\n\n","type":"String.String -> Route.Query.QueryCodec (List.List String.String)"},{"name":"drop","comment":" Remove every occurrence of a key from a raw query string. Equivalent\nto `update key Nothing`.\n\n    Query.drop \"tag\" \"q=elm&tag=fp&tag=web\"\n    --> \"q=elm\"\n\n","type":"String.String -> String.String -> String.String"},{"name":"enum","comment":" An enum query parameter that maps between strings and custom types.\n\n    type SortOrder\n        = DateAsc\n        | DateDesc\n        | Title\n\n    sortCodec : QueryCodec (Maybe SortOrder)\n    sortCodec =\n        Query.enum \"sort\"\n            [ ( \"date-asc\", DateAsc )\n            , ( \"date-desc\", DateDesc )\n            , ( \"title\", Title )\n            ]\n\n","type":"String.String -> List.List ( String.String, a ) -> Route.Query.QueryCodec (Maybe.Maybe a)"},{"name":"except","comment":" Drop the listed keys from a raw query string, keeping everything\nelse. The list-based complement to `Query.drop`. Tolerates a leading\n`?`.\n\n    Query.except [ \"utm_source\", \"utm_campaign\" ]\n        \"q=elm&page=2&utm_source=tw&utm_campaign=launch\"\n    --> \"q=elm&page=2\"\n\n    Query.except [ \"page\" ] \"q=elm&page=2\"\n    --> \"q=elm\"\n\n    Query.except [] \"q=elm&page=2\"\n    --> \"q=elm&page=2\"\n\nUseful when you have a denylist of keys to strip (sensitive params\nbefore sharing a URL, navigation-only keys before persisting state)\nwithout having to list out every key you do want to keep.\n\n","type":"List.List String.String -> String.String -> String.String"},{"name":"flag","comment":" A flag query parameter. Present = True, absent = False.\nThe value is ignored; only presence matters.\n\n    Query.flag \"verbose\"\n    -- parses \"?verbose\"           -> True\n    -- parses \"?verbose=anything\"  -> True\n    -- parses \"?\"                  -> False\n\n","type":"String.String -> Route.Query.QueryCodec Basics.Bool"},{"name":"float","comment":" An optional Float query parameter. Mirrors `Route.float` on the path\nside and pairs with `Query.int` for non-integer numeric values.\n\n    Query.float \"ratio\"\n    -- parses \"?ratio=0.5\"  -> Just 0.5\n    -- parses \"?ratio=abc\"  -> Nothing\n    -- parses \"?\"           -> Nothing\n\n","type":"String.String -> Route.Query.QueryCodec (Maybe.Maybe Basics.Float)"},{"name":"fromList","comment":" Build a raw query string from a list of key-value pairs.\n\nKeys and values are percent-encoded. The order of pairs is preserved.\nA pair with an empty value is rendered as a bare key (`flag` rather\nthan `flag=`), matching the behavior of `Query.flag`.\n\n    Query.fromList [ ( \"q\", \"elm\" ), ( \"tag\", \"fp\" ), ( \"tag\", \"web\" ) ]\n    --> \"q=elm&tag=fp&tag=web\"\n\n    Query.fromList []\n    --> \"\"\n\n","type":"List.List ( String.String, String.String ) -> String.String"},{"name":"int","comment":" An optional Int query parameter.\n\n    Query.int \"page\"\n    -- parses \"?page=3\"   -> Just 3\n    -- parses \"?page=abc\" -> Nothing\n    -- parses \"?\"         -> Nothing\n\n","type":"String.String -> Route.Query.QueryCodec (Maybe.Maybe Basics.Int)"},{"name":"list","comment":" A list-valued query parameter. Repeated keys are collected into a list.\n\n    Query.list \"tag\"\n    -- parses \"?tag=elm&tag=fp\" -> [ \"elm\", \"fp\" ]\n    -- parses \"?\"               -> []\n\n","type":"String.String -> Route.Query.QueryCodec (List.List String.String)"},{"name":"lookup","comment":" Look up the first value for a key in a raw query string.\n\nTolerates a leading `?`. Returns `Nothing` if the key is absent.\nFor multi-valued keys, only the first value is returned, matching\nhow the typed primitives like `Query.string` behave.\n\n    Query.lookup \"q\" \"q=elm&page=2\"\n    --> Just \"elm\"\n\n    Query.lookup \"missing\" \"q=elm\"\n    --> Nothing\n\n    Query.lookup \"tag\" \"tag=fp&tag=web\"\n    --> Just \"fp\"\n\n","type":"String.String -> String.String -> Maybe.Maybe String.String"},{"name":"map","comment":" Map over a query codec's parsed value.\n","type":"(a -> b) -> (b -> a) -> Route.Query.QueryCodec a -> Route.Query.QueryCodec b"},{"name":"map2","comment":" Combine two query codecs using a constructor and a decomposer.\n\nFor three or more fields prefer the `succeed`/`with`/`build` pipeline.\n\n    map2 SearchQuery\n        (\\s -> ( s.query, s.page ))\n        (Query.string \"q\")\n        (Query.int \"page\")\n\n","type":"(a -> b -> result) -> (result -> ( a, b )) -> Route.Query.QueryCodec a -> Route.Query.QueryCodec b -> Route.Query.QueryCodec result"},{"name":"map3","comment":" Combine three query codecs.\n\nFor three or more fields prefer the `succeed`/`with`/`build` pipeline.\n\n","type":"(a -> b -> c -> result) -> (result -> ( a, b, c )) -> Route.Query.QueryCodec a -> Route.Query.QueryCodec b -> Route.Query.QueryCodec c -> Route.Query.QueryCodec result"},{"name":"map4","comment":" Combine four query codecs.\n\nFor three or more fields prefer the `succeed`/`with`/`build` pipeline.\n\n","type":"(a -> b -> c -> d -> result) -> (result -> ( ( a, b ), ( c, d ) )) -> Route.Query.QueryCodec a -> Route.Query.QueryCodec b -> Route.Query.QueryCodec c -> Route.Query.QueryCodec d -> Route.Query.QueryCodec result"},{"name":"merge","comment":" Combine two raw query strings.\n\nOn key conflict every entry from the first argument is kept and\nevery entry from the second argument with that key is dropped.\nKeys present only in the second argument are appended in their\noriginal order. Tolerates a leading `?` on either side.\n\n    Query.merge \"q=elm\" \"utm_source=twitter\"\n    --> \"q=elm&utm_source=twitter\"\n\n    Query.merge \"q=elm\" \"q=haskell&utm_source=twitter\"\n    --> \"q=elm&utm_source=twitter\"\n\n    Query.merge \"\" \"utm_source=twitter\"\n    --> \"utm_source=twitter\"\n\nUseful for the \"overlay tracking parameters on a route's natural\nquery\" pattern, where the route owns its own keys but other layers\n(analytics, A/B testing, deep links) need to add their own without\nclobbering them.\n\n","type":"String.String -> String.String -> String.String"},{"name":"parseQuery","comment":" Parse a query string into a value using a codec.\nThe query string should not include the leading \"?\".\n\n    parseQuery codec \"page=3&sort=date\"\n\n","type":"Route.Query.QueryCodec a -> String.String -> a"},{"name":"parseQueryResult","comment":" Result-typed variant of `parseQueryStrict`. Returns\n`Err missingKeys` listing every required key absent from the input,\nor `Ok` of the parsed value when all required keys are present.\n\n    paramsCodec =\n        Query.succeed Pair\n            |> Query.with .q (Query.string \"q\" |> Query.required \"q\")\n            |> Query.with .page (Query.int \"page\" |> Query.required \"page\")\n            |> Query.build\n\n    Query.parseQueryResult paramsCodec \"q=elm&page=2\"\n    --> Ok (Pair (Just \"elm\") (Just 2))\n\n    Query.parseQueryResult paramsCodec \"q=elm\"\n    --> Err [ \"page\" ]\n\n    Query.parseQueryResult paramsCodec \"\"\n    --> Err [ \"q\", \"page\" ]\n\nUseful when missing keys should produce a meaningful error message\n(form validation feedback, structured logs, an HTTP 400 response).\n\n","type":"Route.Query.QueryCodec a -> String.String -> Result.Result (List.List String.String) a"},{"name":"parseQueryStrict","comment":" Strict variant of `parseQuery`. Returns `Nothing` if any key\nflagged with `Query.required` is absent from the input. Otherwise\nreturns `Just` of the same value `parseQuery` would have produced.\n\n    paramsCodec =\n        Query.succeed identity\n            |> Query.with identity (Query.string \"q\" |> Query.required \"q\")\n            |> Query.build\n\n    Query.parseQueryStrict paramsCodec \"q=elm\"   --> Just (Just \"elm\")\n    Query.parseQueryStrict paramsCodec \"\"        --> Nothing\n\nUseful when a route's query parameter is not actually optional and\na missing value should fail the route match rather than silently\nfalling back to a default.\n\n","type":"Route.Query.QueryCodec a -> String.String -> Maybe.Maybe a"},{"name":"parseUrl","comment":" Convenience wrapper over `parseQuery` that takes a `Url` directly\nand reads its query field. Saves the\n`url.query |> Maybe.withDefault \"\" |> Query.parseQuery codec`\nboilerplate at every call site, common in `Browser.application` page\ninit functions.\n\n    Query.parseUrl searchQuery url\n    --> { query = Just \"elm\", page = 1, tags = [] }\n\nAn absent query (`url.query == Nothing`) is treated as empty.\n\n","type":"Route.Query.QueryCodec a -> Url.Url -> a"},{"name":"parseUrlResult","comment":" Result-typed variant of `parseUrl`. Returns `Err missingKeys`\nlisting every required key absent from the URL's query, or `Ok` of\nthe parsed value when all required keys are present.\n\n    Query.parseUrlResult requiredQuery url\n    --> Ok { q = \"elm\", page = 2 }   -- both present\n    --> Err [ \"page\" ]               -- only q present\n    --> Err [ \"q\", \"page\" ]          -- both missing\n\n","type":"Route.Query.QueryCodec a -> Url.Url -> Result.Result (List.List String.String) a"},{"name":"parseUrlStrict","comment":" Strict variant of `parseUrl`. Returns `Nothing` if any key flagged\nwith `Query.required` is absent from the URL's query. Otherwise returns\n`Just` of the same value `parseUrl` would have produced.\n\n    Query.parseUrlStrict requiredQuery url\n    --> Just { q = \"elm\" }   -- when q is present\n    --> Nothing              -- when q is absent\n\n","type":"Route.Query.QueryCodec a -> Url.Url -> Maybe.Maybe a"},{"name":"preserve","comment":" Keep only the listed keys from a raw query string, dropping\neverything else. Order of the surviving entries is preserved.\nTolerates a leading `?`.\n\n    Query.preserve [ \"q\", \"page\" ] \"q=elm&page=2&utm_source=tw\"\n    --> \"q=elm&page=2\"\n\n    Query.preserve [ \"utm_source\", \"utm_campaign\" ]\n        \"q=elm&utm_source=tw&utm_campaign=launch\"\n    --> \"utm_source=tw&utm_campaign=launch\"\n\n    Query.preserve [] \"q=elm&page=2\"\n    --> \"\"\n\nUseful for the \"carry these tracking parameters across navigation\"\npattern, or for stripping a query string down to a known-safe\nallowlist before logging or caching.\n\n","type":"List.List String.String -> String.String -> String.String"},{"name":"required","comment":" Mark a query parameter as required. The key is added to the\ncodec's required-key list, which is consulted only by\n`parseQueryStrict`. Lenient `parseQuery` ignores the marker, so this\nmodifier never changes a value's parsed type.\n\n    Query.string \"q\" |> Query.required \"q\"\n    -- still QueryCodec (Maybe String)\n    -- but parseQueryStrict fails when \"q\" is absent\n\nThe key is repeated explicitly so that `required` composes with any\ncodec, including ones built from `map` or `withDefault` whose own\nkey is not externally inspectable. If the key is already marked\nrequired, this is a no-op.\n\n","type":"String.String -> Route.Query.QueryCodec a -> Route.Query.QueryCodec a"},{"name":"string","comment":" An optional string query parameter.\n\n    Query.string \"q\"\n    -- parses \"?q=hello\" -> Just \"hello\"\n    -- parses \"?q=\"      -> Nothing  (empty values are treated as absent)\n    -- parses \"?\"        -> Nothing\n\n    -- builds Just \"hello\" -> \"q=hello\"\n    -- builds Nothing      -> \"\"\n    -- builds (Just \"\")    -> \"\"\n\nFor the older \"missing means empty string\" behavior, use:\n\n    Query.string \"q\" |> Query.withDefault \"\"\n\n","type":"String.String -> Route.Query.QueryCodec (Maybe.Maybe String.String)"},{"name":"succeed","comment":" Begin a pipelined query codec from a record constructor.\n\n    Query.succeed SearchParams\n        |> Query.with .q (Query.string \"q\")\n        |> Query.with .page (Query.int \"page\" |> Query.withDefault 1)\n        |> Query.build\n\n","type":"constructor -> Route.Query.Pipeline constructor result"},{"name":"toList","comment":" Parse a raw query string into a list of key-value pairs.\n\nTolerates a leading `?`. Preserves the original order and any\nduplicate keys. Keys and values are percent-decoded, so the round\ntrip with `Query.fromList` is clean even for keys or values that\ncontain URL-significant characters.\n\n    Query.toList \"?q=elm&tag=fp&tag=web\"\n    --> [ ( \"q\", \"elm\" ), ( \"tag\", \"fp\" ), ( \"tag\", \"web\" ) ]\n\n    Query.toList \"\"\n    --> []\n\n","type":"String.String -> List.List ( String.String, String.String )"},{"name":"update","comment":" Set or remove a single key in a raw query string, leaving every\nother key untouched.\n\nPass `Just value` to set the key (replacing all existing occurrences and\nappending if absent), or `Nothing` to remove it (equivalent to `drop`).\nA leading `?` on the input is accepted and stripped. The result is\nreturned without a leading `?`.\n\n    Query.update \"page\" (Just \"5\") \"q=elm&page=2&tag=fp\"\n    --> \"q=elm&tag=fp&page=5\"\n\n    Query.update \"page\" Nothing \"q=elm&page=2\"\n    --> \"q=elm\"\n\n    Query.update \"sort\" (Just \"date\") \"\"\n    --> \"sort=date\"\n\nThis is intended for \"navigate to the same page with `page=` changed\"\npatterns where rebuilding the full typed record would be needlessly\nverbose.\n\n","type":"String.String -> Maybe.Maybe String.String -> String.String -> String.String"},{"name":"with","comment":" Add a field to a pipelined query codec. The first argument is a\nrecord-field accessor used when building. The second is the codec for\nthat field.\n\n    Query.succeed SearchParams\n        |> Query.with .q (Query.string \"q\")\n\n","type":"(result -> a) -> Route.Query.QueryCodec a -> Route.Query.Pipeline (a -> next) result -> Route.Query.Pipeline next result"},{"name":"withDefault","comment":" Provide a default value for a `Maybe`-shaped codec, lifting it to a\nnon-Maybe type. The default is omitted from the built query string, so\nround-tripping still works.\n\n    Query.int \"page\" |> Query.withDefault 1\n    -- parses \"?\"        -> 1\n    -- parses \"?page=2\"  -> 2\n    -- builds 1          -> \"\"\n    -- builds 2          -> \"page=2\"\n\nThe value type must support structural equality (no functions inside).\n\n","type":"a -> Route.Query.QueryCodec (Maybe.Maybe a) -> Route.Query.QueryCodec a"}],"binops":[]}]