@real-router/helpers
v0.1.33
Published
Helper utilities for comparing and checking routes
Downloads
2,847
Readme
@real-router/helpers
Route segment testing utilities for Real-Router. Useful for navigation menus, breadcrumbs, conditional rendering, and route guards.
Installation
npm install @real-router/helpers
# or
pnpm add @real-router/helpers
# or
yarn add @real-router/helpers
# or
bun add @real-router/helpersQuick Start
import {
startsWithSegment,
endsWithSegment,
includesSegment,
areRoutesRelated,
} from "@real-router/helpers";
startsWithSegment("users.profile.edit", "users"); // true
endsWithSegment("users.profile.edit", "edit"); // true
includesSegment("users.profile.edit", "profile"); // true
areRoutesRelated("users", "users.profile"); // trueAPI
startsWithSegment(route: State | string, segment?: string | null): boolean | ((segment: string) => boolean)
Tests if route name starts with segment.route: State | string — route state object or route name stringsegment?: string | null — segment to test (optional for curried form)
Returns: boolean (true if starts with segment, false if segment is null/empty) or (segment: string) => boolean (tester function if segment omitted)
// Direct usage
startsWithSegment("users.list", "users"); // true
startsWithSegment("users.profile", "users.profile"); // true (multi-segment)
// With State object
startsWithSegment({ name: "users.list", params: {}, path: "/" }, "users"); // true
// Curried form
const tester = startsWithSegment("users.profile.edit");
tester("users"); // true
tester("admin"); // falseendsWithSegment(route: State | string, segment?: string | null): boolean | ((segment: string) => boolean)
Tests if route name ends with segment.route: State | string — route state object or route name stringsegment?: string | null — segment to test (optional for curried form)
Returns: boolean (true if ends with segment, false if segment is null/empty) or (segment: string) => boolean (tester function if segment omitted)
endsWithSegment("users.profile.edit", "edit"); // true
endsWithSegment("a.b.c.d", "c.d"); // true (multi-segment)
// Curried form
const tester = endsWithSegment("users.list");
tester("list"); // trueincludesSegment(route: State | string, segment?: string | null): boolean | ((segment: string) => boolean)
Tests if route name includes segment anywhere.route: State | string — route state object or route name stringsegment?: string | null — segment to test (optional for curried form)
Returns: boolean (true if includes segment, false if segment is null/empty) or (segment: string) => boolean (tester function if segment omitted)
includesSegment("admin.users.profile", "users"); // true
includesSegment("a.b.c.d", "b.c"); // true (contiguous)
includesSegment("a.b.c.d", "a.c"); // false (not contiguous)
// Curried form
const tester = includesSegment("admin.users.profile");
tester("users"); // true
tester("settings"); // falseareRoutesRelated(route1: string, route2: string): boolean
Tests if routes are in same hierarchy (parent-child, child-parent, or same).route1: string — first route nameroute2: string — second route name
Returns: boolean — true if routes are related (same, parent-child, or child-parent)
// Parent-child relationship
areRoutesRelated("users", "users.list"); // true
areRoutesRelated("users", "users.profile.edit"); // true
// Child-parent relationship
areRoutesRelated("users.list", "users"); // true
areRoutesRelated("users.profile.edit", "users"); // true
// Same route
areRoutesRelated("users", "users"); // true
// Siblings (not related)
areRoutesRelated("users.list", "users.view"); // false
// Different branches (not related)
areRoutesRelated("users", "admin"); // falseUsage Examples
Navigation Menu
function NavigationMenu({ currentRoute }) {
const items = [
{ name: "Dashboard", route: "dashboard" },
{ name: "Users", route: "users" },
];
return (
<nav>
{items.map((item) => (
<MenuItem
key={item.route}
active={startsWithSegment(currentRoute, item.route)}
>
{item.name}
</MenuItem>
))}
</nav>
);
}Route Guard
const adminGuard = (router) => (toState, fromState, done) => {
if (startsWithSegment(toState, "admin") && !isAdmin()) {
done({ redirect: { name: "unauthorized" } });
} else {
done();
}
};
router.useMiddleware(adminGuard);Conditional Rendering
function Layout({ route, children }) {
return (
<div>
{startsWithSegment(route, "admin") && <AdminSidebar />}
{endsWithSegment(route, "edit") && <EditToolbar />}
<main>{children}</main>
</div>
);
}Validation
Segments are validated for security:
- Allowed:
a-z,A-Z,0-9,.,-,_ - Max length: 10,000 characters
- Empty/null: Returns
false - Invalid chars: Throws
TypeError
startsWithSegment("route", "valid-segment_v2"); // OK
startsWithSegment("route", "invalid!char"); // Throws TypeError
startsWithSegment("route", ""); // falseMigration from router5-helpers
- import { startsWithSegment, redirect } from 'router5-helpers';
+ import { startsWithSegment } from '@real-router/helpers';
// Segment testing — unchanged
startsWithSegment(route, 'admin');
// redirect removed — use guards:
- router.addActivateGuard('old', () => redirect('new'));
+ router.addActivateGuard('old', (to, from, done) => {
+ done({ redirect: { name: 'new' } });
+ });New: areRoutesRelated() for hierarchy checks.
Related Packages
- @real-router/core — Core router
- @real-router/react — React integration
License
MIT © Oleg Ivanov
