@sirexelite/typedpaths
v1.0.1
Published
Type-safe URL paths builder + parser with schema validation for path params and query params.
Maintainers
Readme
TypedPaths
Type-safe URL paths builder + parser with schema validation for path params and query params.
- ✅ Only
/:name,/:name?,/:name*are variables (everything else is literal) - ✅ Path params + Query params validation (enum/int/slug/custom)
- ✅
modify()/modifyQuery()/clearQuery()/removeQuery() - ✅
read()binds params from a URL (defaults tolocation.href) - ✅
readQueryOnly()validates only query string - ✅
isValid()checks if URL matches schema - ✅ Optional segments (
?) and rest segments (*) - ✅ Vitest tests + vitest bench
Install
npm i @sirexelite/typedpaths
# or
pnpm add @sirexelite/typedpaths
# or
yarn add @sirexelite/typedpathsQuick start
import { TypedPaths, T } from "@sirexelite/typedpaths";
const scheme = TypedPaths.create({
"/:view/:subject_id?": {
vars: [
{ name: "view", type: T.enum(["users", "posts"] as const) },
{ name: "subject_id", type: T.string() }
],
queryParams: [
{ name: "tab", type: T.enum(["a", "b"] as const) },
{ name: "page", type: T.int() },
{ name: "q", type: T.string() }
],
queryMode: "strict"
},
"/files/:path*": {
vars: [{ name: "path", type: T.string() }],
queryMode: "strict"
},
"/:page": {
vars: [{ name: "page", type: T.slug(), validator: (v) => v !== "view" }],
queryMode: "strict"
}
} as const);Build URLs
Path only
scheme.view.modify({ view: "users" }).get(); // "/users"
scheme.subject_id.modify({ view: "users", subject_id: "1" }).get(); // "/users/1"With query
scheme.view
.modify({ view: "users" })
.modifyQuery({ tab: "a", page: 2 })
.get(); // "/users?tab=a&page=2"Clear / remove query params
const node = scheme.view
.modify({ view: "users" })
.modifyQuery({ q: "hello", page: 1, tab: "b" });
node.clearQuery().get(); // "/users"
node.clearQuery(["q"]).get(); // "/users?tab=b&page=1"
node.removeQuery("page").get(); // "/users?tab=b&q=hello"Optional ? and rest *
// optional
scheme.subject_id.modify({ view: "users" }).get(); // "/users"
// rest
scheme.path.modify({ path: ["a", "b", "c"] }).get(); // "/files/a/b/c"
scheme.path.modify({ path: [] }).get(); // "/files"Read from URL
read(path?: string = location.href)
- Finds the best matching route (priority + order)
- Parses & validates path + query
- Returns a bound scheme where .get() keeps current values
const bound = scheme.read("https://x/users/abc?tab=b&page=3");
bound.match; // { pattern: "/:view/:subject_id?" }
bound.vars; // { view: "users", subject_id: "abc", tab: "b", page: 3 }
bound.view.get(); // "/users?tab=b&page=3"
bound.subject_id.get(); // "/users/abc?tab=b&page=3"
// override bound values
bound.view.modify({ view: "posts" }).get(); // "/posts?tab=b&page=3"readQueryOnly(path?: string = location.href)
Validates only querystring using the first route whose query rules match.
const q = scheme.readQueryOnly("https://x/whatever?tab=a&page=2");
q.query; // { tab: "a", page: 2 }
q.match; // { pattern: "/:view/:subject_id?" }isValid(path?: string = location.href)
scheme.isValid("https://x/users/abc?tab=a&page=2"); // true
scheme.isValid("https://x/users/abc?unknown=1"); // false (strict)Validation types
Built-in helpers:
- T.string()
- T.int()
- T.slug()
- T.enum([...])
Custom validator:
{ name: "id", type: T.string(), validator: (s) => s.length > 5 }Route options
queryMode
- "strict": unknown query keys are rejected
- "loose": unknown query keys are ignored
prio
Higher prio matches earlier in read() / isValid().
"/:page": { prio: -100 }Benchmark
name hz min max mean p75 p99 p995 p999 rme samples
· isValid() hit 342,256.05 0.0023 2.7455 0.0029 0.0029 0.0062 0.0073 0.0116 ±1.21% 171150
· isValid() reject unknown query 216,793.35 0.0038 0.5789 0.0046 0.0047 0.0093 0.0110 0.0145 ±0.73% 108397
· read() bind 102,927.77 0.0084 0.5754 0.0097 0.0096 0.0171 0.0195 0.0258 ±0.69% 51464
· build: path only 594,850.22 0.0014 0.4339 0.0017 0.0016 0.0030 0.0036 0.0074 ±0.74% 297426
· build: path + query 417,404.50 0.0020 4.8481 0.0024 0.0023 0.0042 0.0053 0.0097 ±2.02% 208703
· readQueryOnly() 507,398.38 0.0016 0.5202 0.0020 0.0019 0.0039 0.0047 0.0085 ±0.72% 253700
· rest build 710,507.88 0.0012 0.6027 0.0014 0.0014 0.0026 0.0029 0.0058 ±0.84% 355256