BRAIDGROUP
RESEARCH & DEV
61. Framework Docs

Link SPA Router

Link is Braid's client-side SPA router (braid-lang/lib/frameworks/link/link.br, 336 lines). It provides route matching with :param patterns, route guards, lazy loading, nested routes, path validators, history stack integration, and a full navigation API.

Core Architecture

The LinkRouter struct holds the route configuration, current route state, extracted params, guards, transition hooks, lazy component cache, history stack reference, and path validators.

Router Setup

import link;

let router = link.create_router({
    routes: [
        { path: "/", component: Home, guards: [], loader: nil,
          lazy_load: nil, meta: {}, param_validators: {}, children: [] },
        { path: "/users", component: Users, guards: [], ... },
        { path: "/users/:id", component: UserProfile, ... }
    ],
    history: window.history,  // optional history adapter
    fallback: { path: "/404", component: NotFound }
});

Route Matching

router.navigate("/users/42");

// Internal matching (router.match_route(path)):
// - Exact match: "/users" == "/users"
// - Param match: "/users/:id" matches "/users/42"
// - Nested match: parent path + child path
// - Falls back to config.fallback

// Extracted params:
let params = router.get_current_params(); // { id: "42" }
let meta = router.get_current_route_meta();

Path Matching Algorithm

fn path_matches(self, pattern, path) {
    // 1. Exact string match
    // 2. Split both into segments by "/"
    // 3. Compare segment-by-segment
    // 4. If pattern segment starts with ":", treat as param
    // 5. Run param_validator if configured
    // 6. All segments must match
}

Route Factories

// Standard route
fn route_with_meta(path, component, meta) -> object

// Guarded route — requires passing guard function
fn guarded_route(path, component, guard_fn) -> object

// Route with param validators
fn route_with_validator(path, component, validators) -> object

// Lazy-loaded route
fn lazy_route(path, loader_fn, component_fn) -> object

Route Guard Example

let auth_guard = fn(route, path) {
    let is_authenticated = check_auth();
    if !is_authenticated {
        return "/login";  // redirect path
    }
    return nil;  // allow navigation
};

let protected_route = link.guarded_route("/dashboard", Dashboard, auth_guard);
router.add_route(protected_route);
router.add_guard(auth_guard);  // or add globally

Lazy Route Example

let admin_route = link.lazy_route(
    "/admin",
    fn(params) { return fetch("/api/admin/data"); },   // loader
    fn() { return AdminPanel; }                         // lazy component resolver
);
router.add_route(admin_route);

fn is_lazy_route(route) -> bool  // check if route is lazy-loaded

Navigation API

// Navigate to a path
fn link_push(router, path)

// Replace current history entry
fn link_replace(router, path)

// Navigate back
fn link_back(router)

// Router method
router.navigate(path)

// Generate an anchor element with SPA navigation
let spa_link = router.link_to(path, "Click me", { class: "nav-link" });
// Returns: <a href="..." onClick={preventDefault + navigate}>Click me</a>

Route Guards (beforeEnter / beforeLeave)

router.add_before_enter_hook(fn(route) {
    track_analytics(route);
});

router.add_before_leave_hook(fn(current_route) {
    save_form_draft(current_route);
});

// Guards can return a redirect path (string) to cancel and redirect,
// or nil to allow navigation through.
// Guards are checked in order: global beforeLeave -&gt; guards -> beforeEnter

Nested Routes

let parent_route = {
    path: "/settings",
    component: SettingsLayout,
    children: [
        { path: "/profile", component: Profile },
        { path: "/security", component: Security }
    ]
};
// Matches: /settings/profile, /settings/security
// Router walks children and concatenates parent + child paths

Path Validators

let route = link.route_with_validator("/users/:id", UserProfile, {
    id: fn(value) {
        let id = number(value);
        return id > 0 && id <= 99999;  // only valid IDs
    }
});

History Stack Integration

// When history adapter is provided:
// - navigate() calls history_stack.push_state(path, { route: path })
// - link_replace() calls history_stack.replace_state(path, {})
// - link_back() calls history_stack.back()
// - Router detects URL changes (popstate) and re-runs matching

Route Data Loading

// When route.loader is defined, the router calls it before rendering
// and injects result into route.component.props.__route_data
route.loader = fn(params) {
    return fetch("/api/users/" + params.id);
};

// After navigation:
component.props.__route_data  // contains loaded data

SPA Navigation Example

import bond;
import link;

let router = link.create_router({
    routes: [
        link.route_with_meta("/", Home, { title: "Home" }),
        link.route_with_meta("/about", About, { title: "About" }),
        link.route_with_meta("/contact", Contact, { title: "Contact" })
    ],
    history: window.history,
    fallback: link.route_with_meta("/404", NotFound, { title: "Not Found" })
});

fn NavBar() {
    return bond.nav({ class: "navbar" }, [
        router.link_to("/", "Home"),
        router.link_to("/about", "About"),
        router.link_to("/contact", "Contact")
    ]);
}

fn App() {
    return bond.div({}, [
        bond.createComponent(NavBar, {}),
        bond.createComponent(router.current_route.component, {
            __route_data: router.current_route.loader != nil
                ? router.current_route.loader(router.params)
                : nil
        })
    ]);
}

Auth Guard Example

import bond;
import link;

let session = { is_authenticated: false };

fn require_auth(route, path) {
    if !session.is_authenticated {
        return "/login?redirect=" + path;
    }
    return nil;
}

let router = link.create_router({
    routes: [
        link.route_with_meta("/login", LoginPage, { title: "Login" }),
        link.guarded_route("/dashboard", Dashboard, require_auth),
        link.guarded_route("/admin", AdminPanel, require_auth),
    ],
    history: window.history
});

fn LogoutButton() {
    return bond.button("Logout", {
        onClick: fn() {
            session.is_authenticated = false;
            link.link_push(router, "/login");
        }
    });
}

Low-Level Helpers

// Split a path into segments
fn split_path(self, path) -&gt; list   // "/users/42" -> ["users", "42"]

// Extract param values from matched path
fn extract_params(self, pattern, path) -> object

// Validate a single param value against registered validator
fn validate_param(self, param_name, value) -> bool

// Check if path matches pattern (including param segments)
fn path_matches(self, pattern, path) -> bool

// Add a route after construction
router.add_route(route)

// Add a global guard
router.add_guard(guard_fn)