@vielzeug/wayfinder 2.2.0 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -41
- package/dist/compile.cjs +1 -1
- package/dist/compile.cjs.map +1 -1
- package/dist/compile.d.ts +4 -4
- package/dist/compile.d.ts.map +1 -1
- package/dist/compile.js +1 -4
- package/dist/compile.js.map +1 -1
- package/dist/context.cjs +1 -1
- package/dist/context.cjs.map +1 -1
- package/dist/context.d.ts +6 -7
- package/dist/context.d.ts.map +1 -1
- package/dist/context.js +18 -17
- package/dist/context.js.map +1 -1
- package/dist/guards.cjs +1 -1
- package/dist/guards.cjs.map +1 -1
- package/dist/guards.js +1 -1
- package/dist/guards.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/path.cjs +1 -1
- package/dist/path.cjs.map +1 -1
- package/dist/path.d.ts +4 -5
- package/dist/path.d.ts.map +1 -1
- package/dist/path.js +1 -10
- package/dist/path.js.map +1 -1
- package/dist/router.cjs +1 -1
- package/dist/router.cjs.map +1 -1
- package/dist/router.d.ts +14 -22
- package/dist/router.d.ts.map +1 -1
- package/dist/router.js +190 -238
- package/dist/router.js.map +1 -1
- package/dist/types.d.ts +27 -39
- package/dist/types.d.ts.map +1 -1
- package/dist/wayfinder.cjs +1 -1
- package/dist/wayfinder.cjs.map +1 -1
- package/dist/wayfinder.iife.js +1 -1
- package/dist/wayfinder.iife.js.map +1 -1
- package/dist/wayfinder.js +1 -1
- package/dist/wayfinder.js.map +1 -1
- package/package.json +6 -6
- package/dist/hydration.cjs +0 -2
- package/dist/hydration.cjs.map +0 -1
- package/dist/hydration.d.ts +0 -7
- package/dist/hydration.d.ts.map +0 -1
- package/dist/hydration.js +0 -55
- package/dist/hydration.js.map +0 -1
- package/dist/preload.cjs +0 -2
- package/dist/preload.cjs.map +0 -1
- package/dist/preload.d.ts +0 -18
- package/dist/preload.d.ts.map +0 -1
- package/dist/preload.js +0 -30
- package/dist/preload.js.map +0 -1
package/README.md
CHANGED
|
@@ -1,39 +1,6 @@
|
|
|
1
1
|
# @vielzeug/wayfinder
|
|
2
2
|
|
|
3
|
-
>
|
|
4
|
-
|
|
5
|
-
[](https://www.npmjs.com/package/@vielzeug/wayfinder) [](https://opensource.org/licenses/MIT)
|
|
6
|
-
|
|
7
|
-
<details>
|
|
8
|
-
<summary>Quick Reference</summary>
|
|
9
|
-
|
|
10
|
-
**Package:** `@vielzeug/wayfinder` **Category:** Routing
|
|
11
|
-
|
|
12
|
-
**Key exports:** `createRouter`, `createBrowserHistory`, `createMemoryHistory`, `redirectTo`
|
|
13
|
-
|
|
14
|
-
**When to use:** Framework-agnostic client-side router with typed params, async data loading, middleware, leave guards, and View Transitions support.
|
|
15
|
-
|
|
16
|
-
**Related:** [@vielzeug/ripple](https://vielzeug.dev/ripple/) · [@vielzeug/ward](https://vielzeug.dev/ward/) · [@vielzeug/herald](https://vielzeug.dev/herald/)
|
|
17
|
-
|
|
18
|
-
</details>
|
|
19
|
-
|
|
20
|
-
`@vielzeug/wayfinder` is part of Vielzeug and ships as a zero-dependency TypeScript package with ESM+CJS output.
|
|
21
|
-
|
|
22
|
-
## What You Get
|
|
23
|
-
|
|
24
|
-
- Declarative route tables with nested names (`dashboard.settings`)
|
|
25
|
-
- Named and raw-path navigation through one `navigate()` API
|
|
26
|
-
- Route middleware, data loaders, and lazy route modules
|
|
27
|
-
- Global `beforeLeave` leave guards with optional route scoping
|
|
28
|
-
- `component` + `meta` + `data` payloads on matched branch state
|
|
29
|
-
- Per-match `status` for granular loading/streaming feedback in nested layouts
|
|
30
|
-
- Streaming data loaders via async generators
|
|
31
|
-
- Declarative `notFound` fallback route
|
|
32
|
-
- Browser and memory history drivers
|
|
33
|
-
- `redirectTo()` middleware helper
|
|
34
|
-
- SSR-compatible `load()` for data prefetching without side effects
|
|
35
|
-
- `match()` for synchronous route inspection
|
|
36
|
-
- `ready` and `waitFor(name)` for lifecycle coordination
|
|
3
|
+
> Client-side router with middleware and guards
|
|
37
4
|
|
|
38
5
|
## Installation
|
|
39
6
|
|
|
@@ -45,8 +12,6 @@ yarn add @vielzeug/wayfinder
|
|
|
45
12
|
|
|
46
13
|
## Quick Start
|
|
47
14
|
|
|
48
|
-
Create a memory-backed router, await initial routing, then navigate by route name.
|
|
49
|
-
|
|
50
15
|
```ts
|
|
51
16
|
import { createRouter } from '@vielzeug/wayfinder';
|
|
52
17
|
|
|
@@ -65,7 +30,7 @@ const router = createRouter({
|
|
|
65
30
|
},
|
|
66
31
|
},
|
|
67
32
|
notFound: {
|
|
68
|
-
|
|
33
|
+
data: () => ({ message: 'Not found' }),
|
|
69
34
|
},
|
|
70
35
|
});
|
|
71
36
|
|
|
@@ -74,7 +39,7 @@ await router.ready;
|
|
|
74
39
|
// React to state changes:
|
|
75
40
|
router.subscribe((state) => {
|
|
76
41
|
const leaf = state.matches.at(-1);
|
|
77
|
-
render(leaf?.
|
|
42
|
+
render(leaf?.name, leaf?.data);
|
|
78
43
|
});
|
|
79
44
|
|
|
80
45
|
await router.navigate({ name: 'dashboard.settings' });
|
|
@@ -87,9 +52,6 @@ await router.navigate({ name: 'dashboard.settings' });
|
|
|
87
52
|
- [API Reference](https://vielzeug.dev/wayfinder/api)
|
|
88
53
|
- [Examples](https://vielzeug.dev/wayfinder/examples)
|
|
89
54
|
- [Migration Guide](https://vielzeug.dev/wayfinder/migration)
|
|
90
|
-
- [React Integration Example](https://vielzeug.dev/wayfinder/examples/react-integration)
|
|
91
|
-
- [Vue Integration Example](https://vielzeug.dev/wayfinder/examples/vue-integration)
|
|
92
|
-
- [Svelte Integration Example](https://vielzeug.dev/wayfinder/examples/svelte-integration)
|
|
93
55
|
|
|
94
56
|
## License
|
|
95
57
|
|
package/dist/compile.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
const e=require("./errors.cjs");require("./_dev.cjs");const t=require("./path.cjs");function n(n){let r=[],i=(n,a,o,s,c)=>{if(a.index&&a.path!==void 0)throw new e.WayfinderRouteError(`Route "${n}" cannot define both index and path`);if(!a.index&&a.path===void 0)throw new e.WayfinderRouteError(`Route "${n}" must define path or set index: true`);let l=a.index?o:t.normalizePath(a.path?t.joinPaths(o,a.path):o),u=[...s,{
|
|
1
|
+
const e=require("./errors.cjs");require("./_dev.cjs");const t=require("./path.cjs");function n(n){let r=[],i=(n,a,o,s,c)=>{if(a.index&&a.path!==void 0)throw new e.WayfinderRouteError(`Route "${n}" cannot define both index and path`);if(!a.index&&a.path===void 0)throw new e.WayfinderRouteError(`Route "${n}" must define path or set index: true`);let l=a.index?o:t.normalizePath(a.path?t.joinPaths(o,a.path):o),u=[...s,{dataFn:a.data,name:n,onError:a.onError}],d=[...c,...a.middleware??[]];if(a.children)for(let[e,t]of Object.entries(a.children))i(`${n}.${e}`,t,l,u,d);if(a.redirect!==void 0||!a.children||a.data!==void 0){let e=u[u.length-1];r.push({branchDefs:u,coerceSearch:a.coerceSearch,leaf:e,matcher:t.compilePathMatcher(l),ownMiddleware:d,path:l,redirect:a.redirect})}};for(let[e,t]of Object.entries(n.routes))i(e,t,`/`,[],[]);let a=new Set;for(let t of r){if(a.has(t.leaf.name))throw new e.WayfinderRouteError(`Duplicate route name: "${t.leaf.name}". A top-level route key must not coincide with a nested route's dot-notation name.`);a.add(t.leaf.name)}let o=!1;for(let e of r){let t=e.path===`/*`||e.path.endsWith(`/*`)||e.path.includes(`:`)&&e.path.endsWith(`*`);o&&!t&&`${e.leaf.name}${e.path}`,t&&(o=!0)}return{records:r,routesByName:new Map(r.map(e=>[e.leaf.name,e]))}}exports.compileRoutes=n;
|
|
2
2
|
//# sourceMappingURL=compile.cjs.map
|
package/dist/compile.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"compile.cjs","names":[],"sources":["../src/compile.ts"],"sourcesContent":["import { warn } from './_dev';\nimport { WayfinderRouteError } from './errors';\nimport { compilePathMatcher, joinPaths, normalizePath } from './path';\nimport type { Middleware, RouteBranchDef, RouteDefinition, RouteRecord, RouterOptions, RouteTable } from './types';\n\nexport type CompiledRoutes
|
|
1
|
+
{"version":3,"file":"compile.cjs","names":[],"sources":["../src/compile.ts"],"sourcesContent":["import { warn } from './_dev';\nimport { WayfinderRouteError } from './errors';\nimport { compilePathMatcher, joinPaths, normalizePath } from './path';\nimport type { Middleware, RouteBranchDef, RouteDefinition, RouteRecord, RouterOptions, RouteTable } from './types';\n\nexport type CompiledRoutes = {\n records: readonly RouteRecord[];\n routesByName: ReadonlyMap<string, RouteRecord>;\n};\n\nexport function compileRoutes<TRoutes extends RouteTable>(options: RouterOptions<TRoutes>): CompiledRoutes {\n const records: RouteRecord[] = [];\n\n const compile = (\n name: string,\n route: RouteDefinition,\n ancestorPath: string,\n ancestorBranchDefs: RouteBranchDef[],\n ancestorMiddleware: RouteRecord['ownMiddleware'],\n ): void => {\n if (route.index && route.path !== undefined) {\n throw new WayfinderRouteError(`Route \"${name}\" cannot define both index and path`);\n }\n\n if (!route.index && route.path === undefined) {\n throw new WayfinderRouteError(`Route \"${name}\" must define path or set index: true`);\n }\n\n const ownPath = route.index\n ? ancestorPath\n : normalizePath(route.path ? joinPaths(ancestorPath, route.path) : ancestorPath);\n\n const branchDefs: RouteBranchDef[] = [\n ...ancestorBranchDefs,\n {\n dataFn: route.data,\n name,\n onError: route.onError as RouteBranchDef['onError'],\n },\n ];\n\n const ownMiddleware: Middleware[] = [\n ...ancestorMiddleware,\n ...((route.middleware ?? []) as unknown as Middleware[]),\n ];\n\n if (route.children) {\n for (const [childName, childRoute] of Object.entries(route.children)) {\n compile(`${name}.${childName}`, childRoute, ownPath, branchDefs, ownMiddleware);\n }\n }\n\n // Emit a record for leaf routes (no children) and routes with data or redirect.\n // A bare parent-only route (children but no data/redirect) is not emitted as a leaf.\n if (route.redirect !== undefined || !route.children || route.data !== undefined) {\n const leaf = branchDefs[branchDefs.length - 1]!;\n\n records.push({\n branchDefs,\n coerceSearch: route.coerceSearch,\n leaf,\n matcher: compilePathMatcher(ownPath),\n ownMiddleware,\n path: ownPath,\n redirect: route.redirect,\n });\n }\n };\n\n for (const [name, route] of Object.entries(options.routes)) {\n compile(name, route, '/', [], []);\n }\n\n // Validate duplicate names.\n const namesSeen = new Set<string>();\n\n for (const record of records) {\n if (namesSeen.has(record.leaf.name)) {\n throw new WayfinderRouteError(\n `Duplicate route name: \"${record.leaf.name}\". A top-level route key must not coincide with a nested route's dot-notation name.`,\n );\n }\n\n namesSeen.add(record.leaf.name);\n }\n\n // Warn when a catch-all or wildcard-param route precedes more specific routes.\n // Routes match in array order (object key order), so a wildcard placed too early silently shadows later routes.\n let wildcardSeen = false;\n\n for (const record of records) {\n const isCatchAll =\n record.path === '/*' || record.path.endsWith('/*') || (record.path.includes(':') && record.path.endsWith('*'));\n\n if (wildcardSeen && !isCatchAll) {\n warn(\n `Route \"${record.leaf.name}\" (${record.path}) is defined after a wildcard/catch-all route and will never match. Move specific routes before wildcards.`,\n );\n }\n\n if (isCatchAll) wildcardSeen = true;\n }\n\n return {\n records,\n routesByName: new Map(records.map((r) => [r.leaf.name, r])),\n };\n}\n"],"mappings":"oFAUA,SAAgB,EAA0C,EAAiD,CACzG,IAAM,EAAyB,CAAC,EAE1B,GACJ,EACA,EACA,EACA,EACA,IACS,CACT,GAAI,EAAM,OAAS,EAAM,OAAS,IAAA,GAChC,MAAM,IAAI,EAAA,oBAAoB,UAAU,EAAK,oCAAoC,EAGnF,GAAI,CAAC,EAAM,OAAS,EAAM,OAAS,IAAA,GACjC,MAAM,IAAI,EAAA,oBAAoB,UAAU,EAAK,sCAAsC,EAGrF,IAAM,EAAU,EAAM,MAClB,EACA,EAAA,cAAc,EAAM,KAAO,EAAA,UAAU,EAAc,EAAM,IAAI,EAAI,CAAY,EAE3E,EAA+B,CACnC,GAAG,EACH,CACE,OAAQ,EAAM,KACd,OACA,QAAS,EAAM,OACjB,CACF,EAEM,EAA8B,CAClC,GAAG,EACH,GAAK,EAAM,YAAc,CAAC,CAC5B,EAEA,GAAI,EAAM,SACR,IAAK,GAAM,CAAC,EAAW,KAAe,OAAO,QAAQ,EAAM,QAAQ,EACjE,EAAQ,GAAG,EAAK,GAAG,IAAa,EAAY,EAAS,EAAY,CAAa,EAMlF,GAAI,EAAM,WAAa,IAAA,IAAa,CAAC,EAAM,UAAY,EAAM,OAAS,IAAA,GAAW,CAC/E,IAAM,EAAO,EAAW,EAAW,OAAS,GAE5C,EAAQ,KAAK,CACX,aACA,aAAc,EAAM,aACpB,OACA,QAAS,EAAA,mBAAmB,CAAO,EACnC,gBACA,KAAM,EACN,SAAU,EAAM,QAClB,CAAC,CACH,CACF,EAEA,IAAK,GAAM,CAAC,EAAM,KAAU,OAAO,QAAQ,EAAQ,MAAM,EACvD,EAAQ,EAAM,EAAO,IAAK,CAAC,EAAG,CAAC,CAAC,EAIlC,IAAM,EAAY,IAAI,IAEtB,IAAK,IAAM,KAAU,EAAS,CAC5B,GAAI,EAAU,IAAI,EAAO,KAAK,IAAI,EAChC,MAAM,IAAI,EAAA,oBACR,0BAA0B,EAAO,KAAK,KAAK,oFAC7C,EAGF,EAAU,IAAI,EAAO,KAAK,IAAI,CAChC,CAIA,IAAI,EAAe,GAEnB,IAAK,IAAM,KAAU,EAAS,CAC5B,IAAM,EACJ,EAAO,OAAS,MAAQ,EAAO,KAAK,SAAS,IAAI,GAAM,EAAO,KAAK,SAAS,GAAG,GAAK,EAAO,KAAK,SAAS,GAAG,EAE1G,GAAgB,CAAC,GAEjB,GAAU,EAAO,KAAK,KAAtB,EAAgC,EAAO,KAAvC,EAIA,IAAY,EAAe,GACjC,CAEA,MAAO,CACL,UACA,aAAc,IAAI,IAAI,EAAQ,IAAK,GAAM,CAAC,EAAE,KAAK,KAAM,CAAC,CAAC,CAAC,CAC5D,CACF"}
|
package/dist/compile.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { RouteRecord, RouterOptions, RouteTable } from './types';
|
|
2
|
-
export type CompiledRoutes
|
|
3
|
-
records: readonly RouteRecord
|
|
4
|
-
routesByName: ReadonlyMap<string, RouteRecord
|
|
2
|
+
export type CompiledRoutes = {
|
|
3
|
+
records: readonly RouteRecord[];
|
|
4
|
+
routesByName: ReadonlyMap<string, RouteRecord>;
|
|
5
5
|
};
|
|
6
|
-
export declare function compileRoutes<TRoutes extends RouteTable
|
|
6
|
+
export declare function compileRoutes<TRoutes extends RouteTable>(options: RouterOptions<TRoutes>): CompiledRoutes;
|
|
7
7
|
//# sourceMappingURL=compile.d.ts.map
|
package/dist/compile.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"compile.d.ts","sourceRoot":"","sources":["../src/compile.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAA+C,WAAW,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAEnH,MAAM,MAAM,cAAc,
|
|
1
|
+
{"version":3,"file":"compile.d.ts","sourceRoot":"","sources":["../src/compile.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAA+C,WAAW,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAEnH,MAAM,MAAM,cAAc,GAAG;IAC3B,OAAO,EAAE,SAAS,WAAW,EAAE,CAAC;IAChC,YAAY,EAAE,WAAW,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;CAChD,CAAC;AAEF,wBAAgB,aAAa,CAAC,OAAO,SAAS,UAAU,EAAE,OAAO,EAAE,aAAa,CAAC,OAAO,CAAC,GAAG,cAAc,CAiGzG"}
|
package/dist/compile.js
CHANGED
|
@@ -7,15 +7,12 @@ function i(i) {
|
|
|
7
7
|
if (s.index && s.path !== void 0) throw new e(`Route "${i}" cannot define both index and path`);
|
|
8
8
|
if (!s.index && s.path === void 0) throw new e(`Route "${i}" must define path or set index: true`);
|
|
9
9
|
let d = s.index ? c : r(s.path ? n(c, s.path) : c), f = [...l, {
|
|
10
|
-
component: s.component,
|
|
11
10
|
dataFn: s.data,
|
|
12
|
-
lazy: s.lazy,
|
|
13
|
-
meta: s.meta,
|
|
14
11
|
name: i,
|
|
15
12
|
onError: s.onError
|
|
16
13
|
}], p = [...u, ...s.middleware ?? []];
|
|
17
14
|
if (s.children) for (let [e, t] of Object.entries(s.children)) o(`${i}.${e}`, t, d, f, p);
|
|
18
|
-
if (s.
|
|
15
|
+
if (s.redirect !== void 0 || !s.children || s.data !== void 0) {
|
|
19
16
|
let e = f[f.length - 1];
|
|
20
17
|
a.push({
|
|
21
18
|
branchDefs: f,
|
package/dist/compile.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"compile.js","names":[],"sources":["../src/compile.ts"],"sourcesContent":["import { warn } from './_dev';\nimport { WayfinderRouteError } from './errors';\nimport { compilePathMatcher, joinPaths, normalizePath } from './path';\nimport type { Middleware, RouteBranchDef, RouteDefinition, RouteRecord, RouterOptions, RouteTable } from './types';\n\nexport type CompiledRoutes
|
|
1
|
+
{"version":3,"file":"compile.js","names":[],"sources":["../src/compile.ts"],"sourcesContent":["import { warn } from './_dev';\nimport { WayfinderRouteError } from './errors';\nimport { compilePathMatcher, joinPaths, normalizePath } from './path';\nimport type { Middleware, RouteBranchDef, RouteDefinition, RouteRecord, RouterOptions, RouteTable } from './types';\n\nexport type CompiledRoutes = {\n records: readonly RouteRecord[];\n routesByName: ReadonlyMap<string, RouteRecord>;\n};\n\nexport function compileRoutes<TRoutes extends RouteTable>(options: RouterOptions<TRoutes>): CompiledRoutes {\n const records: RouteRecord[] = [];\n\n const compile = (\n name: string,\n route: RouteDefinition,\n ancestorPath: string,\n ancestorBranchDefs: RouteBranchDef[],\n ancestorMiddleware: RouteRecord['ownMiddleware'],\n ): void => {\n if (route.index && route.path !== undefined) {\n throw new WayfinderRouteError(`Route \"${name}\" cannot define both index and path`);\n }\n\n if (!route.index && route.path === undefined) {\n throw new WayfinderRouteError(`Route \"${name}\" must define path or set index: true`);\n }\n\n const ownPath = route.index\n ? ancestorPath\n : normalizePath(route.path ? joinPaths(ancestorPath, route.path) : ancestorPath);\n\n const branchDefs: RouteBranchDef[] = [\n ...ancestorBranchDefs,\n {\n dataFn: route.data,\n name,\n onError: route.onError as RouteBranchDef['onError'],\n },\n ];\n\n const ownMiddleware: Middleware[] = [\n ...ancestorMiddleware,\n ...((route.middleware ?? []) as unknown as Middleware[]),\n ];\n\n if (route.children) {\n for (const [childName, childRoute] of Object.entries(route.children)) {\n compile(`${name}.${childName}`, childRoute, ownPath, branchDefs, ownMiddleware);\n }\n }\n\n // Emit a record for leaf routes (no children) and routes with data or redirect.\n // A bare parent-only route (children but no data/redirect) is not emitted as a leaf.\n if (route.redirect !== undefined || !route.children || route.data !== undefined) {\n const leaf = branchDefs[branchDefs.length - 1]!;\n\n records.push({\n branchDefs,\n coerceSearch: route.coerceSearch,\n leaf,\n matcher: compilePathMatcher(ownPath),\n ownMiddleware,\n path: ownPath,\n redirect: route.redirect,\n });\n }\n };\n\n for (const [name, route] of Object.entries(options.routes)) {\n compile(name, route, '/', [], []);\n }\n\n // Validate duplicate names.\n const namesSeen = new Set<string>();\n\n for (const record of records) {\n if (namesSeen.has(record.leaf.name)) {\n throw new WayfinderRouteError(\n `Duplicate route name: \"${record.leaf.name}\". A top-level route key must not coincide with a nested route's dot-notation name.`,\n );\n }\n\n namesSeen.add(record.leaf.name);\n }\n\n // Warn when a catch-all or wildcard-param route precedes more specific routes.\n // Routes match in array order (object key order), so a wildcard placed too early silently shadows later routes.\n let wildcardSeen = false;\n\n for (const record of records) {\n const isCatchAll =\n record.path === '/*' || record.path.endsWith('/*') || (record.path.includes(':') && record.path.endsWith('*'));\n\n if (wildcardSeen && !isCatchAll) {\n warn(\n `Route \"${record.leaf.name}\" (${record.path}) is defined after a wildcard/catch-all route and will never match. Move specific routes before wildcards.`,\n );\n }\n\n if (isCatchAll) wildcardSeen = true;\n }\n\n return {\n records,\n routesByName: new Map(records.map((r) => [r.leaf.name, r])),\n };\n}\n"],"mappings":";;;;AAUA,SAAgB,EAA0C,GAAiD;CACzG,IAAM,IAAyB,CAAC,GAE1B,KACJ,GACA,GACA,GACA,GACA,MACS;EACT,IAAI,EAAM,SAAS,EAAM,SAAS,KAAA,GAChC,MAAM,IAAI,EAAoB,UAAU,EAAK,oCAAoC;EAGnF,IAAI,CAAC,EAAM,SAAS,EAAM,SAAS,KAAA,GACjC,MAAM,IAAI,EAAoB,UAAU,EAAK,sCAAsC;EAGrF,IAAM,IAAU,EAAM,QAClB,IACA,EAAc,EAAM,OAAO,EAAU,GAAc,EAAM,IAAI,IAAI,CAAY,GAE3E,IAA+B,CACnC,GAAG,GACH;GACE,QAAQ,EAAM;GACd;GACA,SAAS,EAAM;EACjB,CACF,GAEM,IAA8B,CAClC,GAAG,GACH,GAAK,EAAM,cAAc,CAAC,CAC5B;EAEA,IAAI,EAAM,UACR,KAAK,IAAM,CAAC,GAAW,MAAe,OAAO,QAAQ,EAAM,QAAQ,GACjE,EAAQ,GAAG,EAAK,GAAG,KAAa,GAAY,GAAS,GAAY,CAAa;EAMlF,IAAI,EAAM,aAAa,KAAA,KAAa,CAAC,EAAM,YAAY,EAAM,SAAS,KAAA,GAAW;GAC/E,IAAM,IAAO,EAAW,EAAW,SAAS;GAE5C,EAAQ,KAAK;IACX;IACA,cAAc,EAAM;IACpB;IACA,SAAS,EAAmB,CAAO;IACnC;IACA,MAAM;IACN,UAAU,EAAM;GAClB,CAAC;EACH;CACF;CAEA,KAAK,IAAM,CAAC,GAAM,MAAU,OAAO,QAAQ,EAAQ,MAAM,GACvD,EAAQ,GAAM,GAAO,KAAK,CAAC,GAAG,CAAC,CAAC;CAIlC,IAAM,oBAAY,IAAI,IAAY;CAElC,KAAK,IAAM,KAAU,GAAS;EAC5B,IAAI,EAAU,IAAI,EAAO,KAAK,IAAI,GAChC,MAAM,IAAI,EACR,0BAA0B,EAAO,KAAK,KAAK,oFAC7C;EAGF,EAAU,IAAI,EAAO,KAAK,IAAI;CAChC;CAIA,IAAI,IAAe;CAEnB,KAAK,IAAM,KAAU,GAAS;EAC5B,IAAM,IACJ,EAAO,SAAS,QAAQ,EAAO,KAAK,SAAS,IAAI,KAAM,EAAO,KAAK,SAAS,GAAG,KAAK,EAAO,KAAK,SAAS,GAAG;EAQ9G,AANI,KAAgB,CAAC,KAEjB,GAAU,EAAO,KAAK,KAAtB,EAAgC,EAAO,KAAvC,GAIA,MAAY,IAAe;CACjC;CAEA,OAAO;EACL;EACA,cAAc,IAAI,IAAI,EAAQ,KAAK,MAAM,CAAC,EAAE,KAAK,MAAM,CAAC,CAAC,CAAC;CAC5D;AACF"}
|
package/dist/context.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
const e=require("./errors.cjs");require("./_dev.cjs");function t(e){return{error:e.error,location:e.location,matches:e.matches,status:e.status}}function n(e,t,n,r
|
|
1
|
+
const e=require("./errors.cjs");require("./_dev.cjs");function t(e){return{error:e.error,location:e.location,matches:e.matches,status:e.status}}function n(e,t,n,r){return e.map((e,i)=>({data:r[i],name:e.name,params:{...t},pathname:n}))}function r(e,t,n,r,i){return{hash:e.hash,historyState:e.historyState,locals:{},matches:r,navigate:i,params:n,pathname:e.pathname,query:t}}async function i(t,n,r,i){let a=!1;async function o(s){if(s<n.length){let r=!1;try{await n[s]?.(t,async()=>{if(r)throw new e.WayfinderApiError(`next() called multiple times`);r=!0,await o(s+1)})}catch(e){throw i?.(e)??e}return}a=!0,await r()}return await o(0),a}function a(e,t,n){if(n){n(e,t);return}}exports.buildMatchBranch=n,exports.createRouteContext=r,exports.createRouteState=t,exports.executeMiddlewarePipeline=i,exports.reportError=a;
|
|
2
2
|
//# sourceMappingURL=context.cjs.map
|
package/dist/context.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"context.cjs","names":[],"sources":["../src/context.ts"],"sourcesContent":["import { error as logError } from './_dev';\nimport { WayfinderApiError } from './errors';\nimport type {\n Middleware,\n NavigationStatus,\n ResolvedQueryParams,\n RouteBranchDef,\n RouteContext,\n RouteLocation,\n RouteMatchBranch,\n RouteParams,\n RouterErrorContext,\n RouteState,\n RouteTable,\n} from './types';\n\n// ─── Route state ──────────────────────────────────────────────────────────────\n\n/** No defensive deep-copy — RouteState is typed readonly; callers always pass fresh objects. */\nexport function createRouteState
|
|
1
|
+
{"version":3,"file":"context.cjs","names":[],"sources":["../src/context.ts"],"sourcesContent":["import { error as logError } from './_dev';\nimport { WayfinderApiError } from './errors';\nimport type {\n Middleware,\n NavigationStatus,\n ResolvedQueryParams,\n RouteBranchDef,\n RouteContext,\n RouteLocation,\n RouteMatch,\n RouteMatchBranch,\n RouteParams,\n RouterErrorContext,\n RouteState,\n RouteTable,\n} from './types';\n\n// ─── Route state ──────────────────────────────────────────────────────────────\n\n/** No defensive deep-copy — RouteState is typed readonly; callers always pass fresh objects. */\nexport function createRouteState(input: {\n error?: unknown;\n location: RouteLocation;\n matches: RouteMatchBranch;\n status: NavigationStatus;\n}): RouteState {\n return {\n error: input.error,\n location: input.location,\n matches: input.matches,\n status: input.status,\n };\n}\n\n/**\n * Build a RouteMatchBranch from compiled defs and data results.\n */\nexport function buildMatchBranch(\n branchDefs: readonly RouteBranchDef[],\n params: RouteParams,\n pathname: string,\n dataResults: unknown[],\n): RouteMatchBranch {\n return branchDefs.map(\n (def, i): RouteMatch => ({\n data: dataResults[i],\n name: def.name,\n params: { ...params },\n pathname,\n }),\n );\n}\n\n// ─── Context factory ──────────────────────────────────────────────────────────\n\nexport function createRouteContext<TRoutes extends RouteTable>(\n location: RouteLocation,\n resolvedQuery: ResolvedQueryParams,\n params: RouteParams,\n matches: RouteMatchBranch,\n navigate: RouteContext<RouteParams, TRoutes>['navigate'],\n): RouteContext<RouteParams, TRoutes> {\n return {\n hash: location.hash,\n historyState: location.historyState,\n locals: {},\n matches,\n navigate,\n params,\n pathname: location.pathname,\n query: resolvedQuery,\n };\n}\n\n// ─── Middleware pipeline ──────────────────────────────────────────────────────\n\nexport async function executeMiddlewarePipeline<TRoutes extends RouteTable>(\n context: RouteContext<RouteParams, TRoutes>,\n middleware: readonly Middleware<TRoutes>[],\n terminal: () => Promise<void>,\n wrapMiddlewareError?: (error: unknown) => unknown,\n): Promise<boolean> {\n let terminalRan = false;\n\n async function dispatch(index: number): Promise<void> {\n if (index < middleware.length) {\n let called = false;\n\n try {\n await middleware[index]?.(context, async () => {\n if (called) throw new WayfinderApiError('next() called multiple times');\n\n called = true;\n await dispatch(index + 1);\n });\n } catch (error) {\n throw wrapMiddlewareError?.(error) ?? error;\n }\n\n return;\n }\n\n terminalRan = true;\n await terminal();\n }\n\n await dispatch(0);\n\n return terminalRan;\n}\n\n// ─── Error reporting ──────────────────────────────────────────────────────────\n\nexport function reportError(\n error: unknown,\n context: RouterErrorContext,\n onError?: (e: unknown, ctx: RouterErrorContext) => void,\n): void {\n if (onError) {\n onError(error, context);\n\n return;\n }\n\n logError('Unhandled router error. Provide an onError callback to handle errors explicitly:', error);\n}\n"],"mappings":"sDAoBA,SAAgB,EAAiB,EAKlB,CACb,MAAO,CACL,MAAO,EAAM,MACb,SAAU,EAAM,SAChB,QAAS,EAAM,QACf,OAAQ,EAAM,MAChB,CACF,CAKA,SAAgB,EACd,EACA,EACA,EACA,EACkB,CAClB,OAAO,EAAW,KACf,EAAK,KAAmB,CACvB,KAAM,EAAY,GAClB,KAAM,EAAI,KACV,OAAQ,CAAE,GAAG,CAAO,EACpB,UACF,EACF,CACF,CAIA,SAAgB,EACd,EACA,EACA,EACA,EACA,EACoC,CACpC,MAAO,CACL,KAAM,EAAS,KACf,aAAc,EAAS,aACvB,OAAQ,CAAC,EACT,UACA,WACA,SACA,SAAU,EAAS,SACnB,MAAO,CACT,CACF,CAIA,eAAsB,EACpB,EACA,EACA,EACA,EACkB,CAClB,IAAI,EAAc,GAElB,eAAe,EAAS,EAA8B,CACpD,GAAI,EAAQ,EAAW,OAAQ,CAC7B,IAAI,EAAS,GAEb,GAAI,CACF,MAAM,EAAW,EAAM,GAAG,EAAS,SAAY,CAC7C,GAAI,EAAQ,MAAM,IAAI,EAAA,kBAAkB,8BAA8B,EAEtE,EAAS,GACT,MAAM,EAAS,EAAQ,CAAC,CAC1B,CAAC,CACH,OAAS,EAAO,CACd,MAAM,IAAsB,CAAK,GAAK,CACxC,CAEA,MACF,CAEA,EAAc,GACd,MAAM,EAAS,CACjB,CAIA,OAFA,MAAM,EAAS,CAAC,EAET,CACT,CAIA,SAAgB,EACd,EACA,EACA,EACM,CACN,GAAI,EAAS,CACX,EAAQ,EAAO,CAAO,EAEtB,MACF,CAGF"}
|
package/dist/context.d.ts
CHANGED
|
@@ -1,17 +1,16 @@
|
|
|
1
1
|
import type { Middleware, NavigationStatus, ResolvedQueryParams, RouteBranchDef, RouteContext, RouteLocation, RouteMatchBranch, RouteParams, RouterErrorContext, RouteState, RouteTable } from './types';
|
|
2
2
|
/** No defensive deep-copy — RouteState is typed readonly; callers always pass fresh objects. */
|
|
3
|
-
export declare function createRouteState
|
|
3
|
+
export declare function createRouteState(input: {
|
|
4
4
|
error?: unknown;
|
|
5
5
|
location: RouteLocation;
|
|
6
|
-
matches: RouteMatchBranch
|
|
6
|
+
matches: RouteMatchBranch;
|
|
7
7
|
status: NavigationStatus;
|
|
8
|
-
}): RouteState
|
|
8
|
+
}): RouteState;
|
|
9
9
|
/**
|
|
10
|
-
* Build a RouteMatchBranch from compiled defs
|
|
11
|
-
* Each node carries its own `status` for fine-grained nested layout feedback.
|
|
10
|
+
* Build a RouteMatchBranch from compiled defs and data results.
|
|
12
11
|
*/
|
|
13
|
-
export declare function buildMatchBranch
|
|
12
|
+
export declare function buildMatchBranch(branchDefs: readonly RouteBranchDef[], params: RouteParams, pathname: string, dataResults: unknown[]): RouteMatchBranch;
|
|
14
13
|
export declare function createRouteContext<TRoutes extends RouteTable>(location: RouteLocation, resolvedQuery: ResolvedQueryParams, params: RouteParams, matches: RouteMatchBranch, navigate: RouteContext<RouteParams, TRoutes>['navigate']): RouteContext<RouteParams, TRoutes>;
|
|
15
|
-
export declare function executeMiddlewarePipeline<TRoutes extends RouteTable>(context: RouteContext<RouteParams, TRoutes>, middleware: readonly Middleware<TRoutes>[], terminal: () => Promise<void
|
|
14
|
+
export declare function executeMiddlewarePipeline<TRoutes extends RouteTable>(context: RouteContext<RouteParams, TRoutes>, middleware: readonly Middleware<TRoutes>[], terminal: () => Promise<void>, wrapMiddlewareError?: (error: unknown) => unknown): Promise<boolean>;
|
|
16
15
|
export declare function reportError(error: unknown, context: RouterErrorContext, onError?: (e: unknown, ctx: RouterErrorContext) => void): void;
|
|
17
16
|
//# sourceMappingURL=context.d.ts.map
|
package/dist/context.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../src/context.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,UAAU,EACV,gBAAgB,EAChB,mBAAmB,EACnB,cAAc,EACd,YAAY,EACZ,aAAa,
|
|
1
|
+
{"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../src/context.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,UAAU,EACV,gBAAgB,EAChB,mBAAmB,EACnB,cAAc,EACd,YAAY,EACZ,aAAa,EAEb,gBAAgB,EAChB,WAAW,EACX,kBAAkB,EAClB,UAAU,EACV,UAAU,EACX,MAAM,SAAS,CAAC;AAIjB,gGAAgG;AAChG,wBAAgB,gBAAgB,CAAC,KAAK,EAAE;IACtC,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,QAAQ,EAAE,aAAa,CAAC;IACxB,OAAO,EAAE,gBAAgB,CAAC;IAC1B,MAAM,EAAE,gBAAgB,CAAC;CAC1B,GAAG,UAAU,CAOb;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAC9B,UAAU,EAAE,SAAS,cAAc,EAAE,EACrC,MAAM,EAAE,WAAW,EACnB,QAAQ,EAAE,MAAM,EAChB,WAAW,EAAE,OAAO,EAAE,GACrB,gBAAgB,CASlB;AAID,wBAAgB,kBAAkB,CAAC,OAAO,SAAS,UAAU,EAC3D,QAAQ,EAAE,aAAa,EACvB,aAAa,EAAE,mBAAmB,EAClC,MAAM,EAAE,WAAW,EACnB,OAAO,EAAE,gBAAgB,EACzB,QAAQ,EAAE,YAAY,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC,UAAU,CAAC,GACvD,YAAY,CAAC,WAAW,EAAE,OAAO,CAAC,CAWpC;AAID,wBAAsB,yBAAyB,CAAC,OAAO,SAAS,UAAU,EACxE,OAAO,EAAE,YAAY,CAAC,WAAW,EAAE,OAAO,CAAC,EAC3C,UAAU,EAAE,SAAS,UAAU,CAAC,OAAO,CAAC,EAAE,EAC1C,QAAQ,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,EAC7B,mBAAmB,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,OAAO,GAChD,OAAO,CAAC,OAAO,CAAC,CA4BlB;AAID,wBAAgB,WAAW,CACzB,KAAK,EAAE,OAAO,EACd,OAAO,EAAE,kBAAkB,EAC3B,OAAO,CAAC,EAAE,CAAC,CAAC,EAAE,OAAO,EAAE,GAAG,EAAE,kBAAkB,KAAK,IAAI,GACtD,IAAI,CAQN"}
|
package/dist/context.js
CHANGED
|
@@ -9,15 +9,12 @@ function t(e) {
|
|
|
9
9
|
status: e.status
|
|
10
10
|
};
|
|
11
11
|
}
|
|
12
|
-
function n(e, t, n, r
|
|
13
|
-
return e.map((e,
|
|
14
|
-
|
|
15
|
-
data: r[a],
|
|
16
|
-
meta: e.meta,
|
|
12
|
+
function n(e, t, n, r) {
|
|
13
|
+
return e.map((e, i) => ({
|
|
14
|
+
data: r[i],
|
|
17
15
|
name: e.name,
|
|
18
16
|
params: { ...t },
|
|
19
|
-
pathname: n
|
|
20
|
-
status: i?.[a] ?? "idle"
|
|
17
|
+
pathname: n
|
|
21
18
|
}));
|
|
22
19
|
}
|
|
23
20
|
function r(e, t, n, r, i) {
|
|
@@ -32,20 +29,24 @@ function r(e, t, n, r, i) {
|
|
|
32
29
|
query: t
|
|
33
30
|
};
|
|
34
31
|
}
|
|
35
|
-
async function i(t, n, r) {
|
|
36
|
-
let
|
|
37
|
-
async function
|
|
38
|
-
if (
|
|
32
|
+
async function i(t, n, r, i) {
|
|
33
|
+
let a = !1;
|
|
34
|
+
async function o(s) {
|
|
35
|
+
if (s < n.length) {
|
|
39
36
|
let r = !1;
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
37
|
+
try {
|
|
38
|
+
await n[s]?.(t, async () => {
|
|
39
|
+
if (r) throw new e("next() called multiple times");
|
|
40
|
+
r = !0, await o(s + 1);
|
|
41
|
+
});
|
|
42
|
+
} catch (e) {
|
|
43
|
+
throw i?.(e) ?? e;
|
|
44
|
+
}
|
|
44
45
|
return;
|
|
45
46
|
}
|
|
46
|
-
|
|
47
|
+
a = !0, await r();
|
|
47
48
|
}
|
|
48
|
-
return await
|
|
49
|
+
return await o(0), a;
|
|
49
50
|
}
|
|
50
51
|
function a(e, t, n) {
|
|
51
52
|
if (n) {
|
package/dist/context.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"context.js","names":[],"sources":["../src/context.ts"],"sourcesContent":["import { error as logError } from './_dev';\nimport { WayfinderApiError } from './errors';\nimport type {\n Middleware,\n NavigationStatus,\n ResolvedQueryParams,\n RouteBranchDef,\n RouteContext,\n RouteLocation,\n RouteMatchBranch,\n RouteParams,\n RouterErrorContext,\n RouteState,\n RouteTable,\n} from './types';\n\n// ─── Route state ──────────────────────────────────────────────────────────────\n\n/** No defensive deep-copy — RouteState is typed readonly; callers always pass fresh objects. */\nexport function createRouteState
|
|
1
|
+
{"version":3,"file":"context.js","names":[],"sources":["../src/context.ts"],"sourcesContent":["import { error as logError } from './_dev';\nimport { WayfinderApiError } from './errors';\nimport type {\n Middleware,\n NavigationStatus,\n ResolvedQueryParams,\n RouteBranchDef,\n RouteContext,\n RouteLocation,\n RouteMatch,\n RouteMatchBranch,\n RouteParams,\n RouterErrorContext,\n RouteState,\n RouteTable,\n} from './types';\n\n// ─── Route state ──────────────────────────────────────────────────────────────\n\n/** No defensive deep-copy — RouteState is typed readonly; callers always pass fresh objects. */\nexport function createRouteState(input: {\n error?: unknown;\n location: RouteLocation;\n matches: RouteMatchBranch;\n status: NavigationStatus;\n}): RouteState {\n return {\n error: input.error,\n location: input.location,\n matches: input.matches,\n status: input.status,\n };\n}\n\n/**\n * Build a RouteMatchBranch from compiled defs and data results.\n */\nexport function buildMatchBranch(\n branchDefs: readonly RouteBranchDef[],\n params: RouteParams,\n pathname: string,\n dataResults: unknown[],\n): RouteMatchBranch {\n return branchDefs.map(\n (def, i): RouteMatch => ({\n data: dataResults[i],\n name: def.name,\n params: { ...params },\n pathname,\n }),\n );\n}\n\n// ─── Context factory ──────────────────────────────────────────────────────────\n\nexport function createRouteContext<TRoutes extends RouteTable>(\n location: RouteLocation,\n resolvedQuery: ResolvedQueryParams,\n params: RouteParams,\n matches: RouteMatchBranch,\n navigate: RouteContext<RouteParams, TRoutes>['navigate'],\n): RouteContext<RouteParams, TRoutes> {\n return {\n hash: location.hash,\n historyState: location.historyState,\n locals: {},\n matches,\n navigate,\n params,\n pathname: location.pathname,\n query: resolvedQuery,\n };\n}\n\n// ─── Middleware pipeline ──────────────────────────────────────────────────────\n\nexport async function executeMiddlewarePipeline<TRoutes extends RouteTable>(\n context: RouteContext<RouteParams, TRoutes>,\n middleware: readonly Middleware<TRoutes>[],\n terminal: () => Promise<void>,\n wrapMiddlewareError?: (error: unknown) => unknown,\n): Promise<boolean> {\n let terminalRan = false;\n\n async function dispatch(index: number): Promise<void> {\n if (index < middleware.length) {\n let called = false;\n\n try {\n await middleware[index]?.(context, async () => {\n if (called) throw new WayfinderApiError('next() called multiple times');\n\n called = true;\n await dispatch(index + 1);\n });\n } catch (error) {\n throw wrapMiddlewareError?.(error) ?? error;\n }\n\n return;\n }\n\n terminalRan = true;\n await terminal();\n }\n\n await dispatch(0);\n\n return terminalRan;\n}\n\n// ─── Error reporting ──────────────────────────────────────────────────────────\n\nexport function reportError(\n error: unknown,\n context: RouterErrorContext,\n onError?: (e: unknown, ctx: RouterErrorContext) => void,\n): void {\n if (onError) {\n onError(error, context);\n\n return;\n }\n\n logError('Unhandled router error. Provide an onError callback to handle errors explicitly:', error);\n}\n"],"mappings":";;;AAoBA,SAAgB,EAAiB,GAKlB;CACb,OAAO;EACL,OAAO,EAAM;EACb,UAAU,EAAM;EAChB,SAAS,EAAM;EACf,QAAQ,EAAM;CAChB;AACF;AAKA,SAAgB,EACd,GACA,GACA,GACA,GACkB;CAClB,OAAO,EAAW,KACf,GAAK,OAAmB;EACvB,MAAM,EAAY;EAClB,MAAM,EAAI;EACV,QAAQ,EAAE,GAAG,EAAO;EACpB;CACF,EACF;AACF;AAIA,SAAgB,EACd,GACA,GACA,GACA,GACA,GACoC;CACpC,OAAO;EACL,MAAM,EAAS;EACf,cAAc,EAAS;EACvB,QAAQ,CAAC;EACT;EACA;EACA;EACA,UAAU,EAAS;EACnB,OAAO;CACT;AACF;AAIA,eAAsB,EACpB,GACA,GACA,GACA,GACkB;CAClB,IAAI,IAAc;CAElB,eAAe,EAAS,GAA8B;EACpD,IAAI,IAAQ,EAAW,QAAQ;GAC7B,IAAI,IAAS;GAEb,IAAI;IACF,MAAM,EAAW,EAAM,GAAG,GAAS,YAAY;KAC7C,IAAI,GAAQ,MAAM,IAAI,EAAkB,8BAA8B;KAGtE,AADA,IAAS,IACT,MAAM,EAAS,IAAQ,CAAC;IAC1B,CAAC;GACH,SAAS,GAAO;IACd,MAAM,IAAsB,CAAK,KAAK;GACxC;GAEA;EACF;EAGA,AADA,IAAc,IACd,MAAM,EAAS;CACjB;CAIA,OAFA,MAAM,EAAS,CAAC,GAET;AACT;AAIA,SAAgB,EACd,GACA,GACA,GACM;CACN,IAAI,GAAS;EACX,EAAQ,GAAO,CAAO;EAEtB;CACF;AAGF"}
|
package/dist/guards.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
async function e(e,t,n){let r=new Set(t),i=[...e];for(let{handler:e,routes:t}of i)if(
|
|
1
|
+
async function e(e,t,n){let r=new Set(t),i=[...e];for(let{handler:e,routes:t}of i)if((t===void 0||t.some(e=>r.has(e)))&&!await e(n))return!1;return!0}exports.runLeaveBlockers=e;
|
|
2
2
|
//# sourceMappingURL=guards.cjs.map
|
package/dist/guards.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"guards.cjs","names":[],"sources":["../src/guards.ts"],"sourcesContent":["import type { BeforeLeaveBlocker, NavigationDestination } from './types';\n\n/**\n * A registered leave guard. `routes` scopes the guard to navigations that depart from\n * any route whose name appears in the array (matched against any node in the active branch,\n * not just the leaf). When `routes` is omitted the guard fires on every navigation.\n */\nexport type RegisteredBlocker = {\n handler: BeforeLeaveBlocker;\n routes?: string[];\n};\n\n/**\n * Run all registered leave blockers in registration order.\n * Guards whose `routes` filter does not overlap the current active match names are skipped.\n * Returns `false` as soon as any blocker returns `false`; returns `true` if all pass.\n *\n * A snapshot of the blocker set is taken before iteration so that guards added or removed\n * during execution do not affect the current run.\n *\n * `destination` is passed to each blocker so guards can make context-aware decisions.\n */\nexport async function runLeaveBlockers(\n blockers: ReadonlySet<RegisteredBlocker>,\n activeMatchNames: readonly string[],\n destination: NavigationDestination,\n): Promise<boolean> {\n const activeNames = new Set(activeMatchNames);\n // Snapshot so that removals during iteration don't skip entries.\n const snapshot = [...blockers];\n\n for (const { handler, routes } of snapshot) {\n if (routes !== undefined && !routes.some((name) => activeNames.has(name))) continue;\n\n const allowed = await handler(destination);\n\n if (!allowed) return false;\n }\n\n return true;\n}\n"],"mappings":"AAsBA,eAAsB,EACpB,EACA,EACA,EACkB,CAClB,IAAM,EAAc,IAAI,IAAI,CAAgB,EAEtC,EAAW,CAAC,GAAG,CAAQ,EAE7B,IAAK,GAAM,CAAE,UAAS,YAAY,EAC5B,
|
|
1
|
+
{"version":3,"file":"guards.cjs","names":[],"sources":["../src/guards.ts"],"sourcesContent":["import type { BeforeLeaveBlocker, NavigationDestination } from './types';\n\n/**\n * A registered leave guard. `routes` scopes the guard to navigations that depart from\n * any route whose name appears in the array (matched against any node in the active branch,\n * not just the leaf). When `routes` is omitted the guard fires on every navigation.\n */\nexport type RegisteredBlocker = {\n handler: BeforeLeaveBlocker;\n routes?: string[];\n};\n\n/**\n * Run all registered leave blockers in registration order.\n * Guards whose `routes` filter does not overlap the current active match names are skipped.\n * Returns `false` as soon as any blocker returns `false`; returns `true` if all pass.\n *\n * A snapshot of the blocker set is taken before iteration so that guards added or removed\n * during execution do not affect the current run.\n *\n * `destination` is passed to each blocker so guards can make context-aware decisions.\n */\nexport async function runLeaveBlockers(\n blockers: ReadonlySet<RegisteredBlocker>,\n activeMatchNames: readonly string[],\n destination: NavigationDestination,\n): Promise<boolean> {\n const activeNames = new Set(activeMatchNames);\n // Snapshot so that removals during iteration don't skip entries.\n const snapshot = [...blockers];\n\n for (const { handler, routes } of snapshot) {\n if (routes !== undefined && !routes.some((name) => activeNames.has(name))) continue;\n\n const allowed = await handler(destination);\n\n if (!allowed) return false;\n }\n\n return true;\n}\n"],"mappings":"AAsBA,eAAsB,EACpB,EACA,EACA,EACkB,CAClB,IAAM,EAAc,IAAI,IAAI,CAAgB,EAEtC,EAAW,CAAC,GAAG,CAAQ,EAE7B,IAAK,GAAM,CAAE,UAAS,YAAY,EAC5B,QAAW,IAAA,IAAc,EAAO,KAAM,GAAS,EAAY,IAAI,CAAI,CAAC,IAIpE,CAAC,MAFiB,EAAQ,CAAW,EAE3B,MAAO,GAGvB,MAAO,EACT"}
|
package/dist/guards.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
//#region src/guards.ts
|
|
2
2
|
async function e(e, t, n) {
|
|
3
3
|
let r = new Set(t), i = [...e];
|
|
4
|
-
for (let { handler: e, routes: t } of i) if (
|
|
4
|
+
for (let { handler: e, routes: t } of i) if ((t === void 0 || t.some((e) => r.has(e))) && !await e(n)) return !1;
|
|
5
5
|
return !0;
|
|
6
6
|
}
|
|
7
7
|
//#endregion
|
package/dist/guards.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"guards.js","names":[],"sources":["../src/guards.ts"],"sourcesContent":["import type { BeforeLeaveBlocker, NavigationDestination } from './types';\n\n/**\n * A registered leave guard. `routes` scopes the guard to navigations that depart from\n * any route whose name appears in the array (matched against any node in the active branch,\n * not just the leaf). When `routes` is omitted the guard fires on every navigation.\n */\nexport type RegisteredBlocker = {\n handler: BeforeLeaveBlocker;\n routes?: string[];\n};\n\n/**\n * Run all registered leave blockers in registration order.\n * Guards whose `routes` filter does not overlap the current active match names are skipped.\n * Returns `false` as soon as any blocker returns `false`; returns `true` if all pass.\n *\n * A snapshot of the blocker set is taken before iteration so that guards added or removed\n * during execution do not affect the current run.\n *\n * `destination` is passed to each blocker so guards can make context-aware decisions.\n */\nexport async function runLeaveBlockers(\n blockers: ReadonlySet<RegisteredBlocker>,\n activeMatchNames: readonly string[],\n destination: NavigationDestination,\n): Promise<boolean> {\n const activeNames = new Set(activeMatchNames);\n // Snapshot so that removals during iteration don't skip entries.\n const snapshot = [...blockers];\n\n for (const { handler, routes } of snapshot) {\n if (routes !== undefined && !routes.some((name) => activeNames.has(name))) continue;\n\n const allowed = await handler(destination);\n\n if (!allowed) return false;\n }\n\n return true;\n}\n"],"mappings":";AAsBA,eAAsB,EACpB,GACA,GACA,GACkB;CAClB,IAAM,IAAc,IAAI,IAAI,CAAgB,GAEtC,IAAW,CAAC,GAAG,CAAQ;CAE7B,KAAK,IAAM,EAAE,YAAS,eAAY,GAC5B,
|
|
1
|
+
{"version":3,"file":"guards.js","names":[],"sources":["../src/guards.ts"],"sourcesContent":["import type { BeforeLeaveBlocker, NavigationDestination } from './types';\n\n/**\n * A registered leave guard. `routes` scopes the guard to navigations that depart from\n * any route whose name appears in the array (matched against any node in the active branch,\n * not just the leaf). When `routes` is omitted the guard fires on every navigation.\n */\nexport type RegisteredBlocker = {\n handler: BeforeLeaveBlocker;\n routes?: string[];\n};\n\n/**\n * Run all registered leave blockers in registration order.\n * Guards whose `routes` filter does not overlap the current active match names are skipped.\n * Returns `false` as soon as any blocker returns `false`; returns `true` if all pass.\n *\n * A snapshot of the blocker set is taken before iteration so that guards added or removed\n * during execution do not affect the current run.\n *\n * `destination` is passed to each blocker so guards can make context-aware decisions.\n */\nexport async function runLeaveBlockers(\n blockers: ReadonlySet<RegisteredBlocker>,\n activeMatchNames: readonly string[],\n destination: NavigationDestination,\n): Promise<boolean> {\n const activeNames = new Set(activeMatchNames);\n // Snapshot so that removals during iteration don't skip entries.\n const snapshot = [...blockers];\n\n for (const { handler, routes } of snapshot) {\n if (routes !== undefined && !routes.some((name) => activeNames.has(name))) continue;\n\n const allowed = await handler(destination);\n\n if (!allowed) return false;\n }\n\n return true;\n}\n"],"mappings":";AAsBA,eAAsB,EACpB,GACA,GACA,GACkB;CAClB,IAAM,IAAc,IAAI,IAAI,CAAgB,GAEtC,IAAW,CAAC,GAAG,CAAQ;CAE7B,KAAK,IAAM,EAAE,YAAS,eAAY,GAC5B,WAAW,KAAA,KAAc,EAAO,MAAM,MAAS,EAAY,IAAI,CAAI,CAAC,MAIpE,CAAC,MAFiB,EAAQ,CAAW,GAE3B,OAAO;CAGvB,OAAO;AACT"}
|
package/dist/index.d.ts
CHANGED
|
@@ -3,5 +3,5 @@ export { createBrowserHistory, createMemoryHistory } from './history';
|
|
|
3
3
|
export { redirectTo } from './middleware';
|
|
4
4
|
export type { Router } from './router';
|
|
5
5
|
export { createRouter } from './router';
|
|
6
|
-
export type { BeforeLeaveBlocker, BeforeLeaveOptions, CoerceSearchFn, DataContext, DataFn,
|
|
6
|
+
export type { BeforeLeaveBlocker, BeforeLeaveOptions, CoerceSearchFn, DataContext, DataFn, HistoryDriver, IsActiveOptions, MaybePromise, Middleware, NamedNavigationTarget, NavigateOptions, NavigationDestination, NavigationStatus, NavigationTarget, PathParams, QueryParams, RawNavigationTarget, ResolvedQueryParams, ResolvedQueryValue, RouteContext, RouteDefinition, RouteLocation, RouteMatch, RouteMatchBranch, RouteMiddleware, RouteName, RouteParams, RoutePathByName, RouterErrorContext, RouterOptions, RouteState, RouteTable, RouteViewMap, RouteViewName, RouteViewRegistry, ScrollDecision, ScrollPosition, Unsubscribe, UntypedNamedNavigationTarget, } from './types';
|
|
7
7
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,iBAAiB,EACjB,sBAAsB,EACtB,cAAc,EACd,0BAA0B,EAC1B,mBAAmB,GACpB,MAAM,UAAU,CAAC;AAClB,OAAO,EAAE,oBAAoB,EAAE,mBAAmB,EAAE,MAAM,WAAW,CAAC;AACtE,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,YAAY,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AACvC,OAAO,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AACxC,YAAY,EACV,kBAAkB,EAClB,kBAAkB,EAClB,cAAc,EACd,WAAW,EACX,MAAM,EACN,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,iBAAiB,EACjB,sBAAsB,EACtB,cAAc,EACd,0BAA0B,EAC1B,mBAAmB,GACpB,MAAM,UAAU,CAAC;AAClB,OAAO,EAAE,oBAAoB,EAAE,mBAAmB,EAAE,MAAM,WAAW,CAAC;AACtE,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,YAAY,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AACvC,OAAO,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AACxC,YAAY,EACV,kBAAkB,EAClB,kBAAkB,EAClB,cAAc,EACd,WAAW,EACX,MAAM,EACN,aAAa,EACb,eAAe,EACf,YAAY,EACZ,UAAU,EACV,qBAAqB,EACrB,eAAe,EACf,qBAAqB,EACrB,gBAAgB,EAChB,gBAAgB,EAChB,UAAU,EACV,WAAW,EACX,mBAAmB,EACnB,mBAAmB,EACnB,kBAAkB,EAClB,YAAY,EACZ,eAAe,EACf,aAAa,EACb,UAAU,EACV,gBAAgB,EAChB,eAAe,EACf,SAAS,EACT,WAAW,EACX,eAAe,EACf,kBAAkB,EAClB,aAAa,EACb,UAAU,EACV,UAAU,EACV,YAAY,EACZ,aAAa,EACb,iBAAiB,EACjB,cAAc,EACd,cAAc,EACd,WAAW,EACX,4BAA4B,GAC7B,MAAM,SAAS,CAAC"}
|
package/dist/path.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
const e=require("./errors.cjs");function t(e){let t=`/${e}`.replace(/\/+/g,`/`);return t!==`/`&&t.endsWith(`/`)?t.slice(0,-1):t}function n(e,n){return t(`${e}/${n}`)}function r(e){return e.replace(/[.*+?^${}()|[\]\\]/g,`\\$&`)}function i(n){let i=t(n);if(i===`/`)return{paramNames:[],pattern:/^\/$/,prefixPattern:/^\/$/};let a=[],o=i.slice(1).split(`/`),s=[`^`],c=[`^`];for(let t=0;t<o.length;t+=1){let i=o[t];if(i===`*`){if(t!==o.length-1)throw new e.WayfinderRouteError(`Wildcard "*" must be the final segment in path: ${n}`);s.push(`(?:/.*)?`);break}if(i.startsWith(`:`)&&i.endsWith(`*`)){if(t!==o.length-1)throw new e.WayfinderRouteError(`Wildcard param must be final segment in path: ${n}`);let r=i.slice(1,-1);if(!/^\w+$/.test(r))throw new e.WayfinderRouteError(`Invalid param name ":${r}" in path "${n}". Param names must only contain word characters (a–z, A–Z, 0–9, _).`);a.push(r),s.push(t===o.length-1?`(?:/(.*))?`:`/(.*)`),c.push(`/.*`);break}if(i.startsWith(`:`)){let t=i.slice(1);if(!/^\w+$/.test(t))throw new e.WayfinderRouteError(`Invalid param name ":${t}" in path "${n}". Param names must only contain word characters (a–z, A–Z, 0–9, _).`);a.push(t),s.push(`/([^/]+)`),c.push(`/[^/]+`);continue}let l=r(i);s.push(`/${l}`),c.push(`/${l}`)}return s.push(`$`),c.push(`(?:/.*)?$`),{paramNames:a,pattern:new RegExp(s.join(``)),prefixPattern:new RegExp(c.join(``))}}function a(e,t){let n=t.matcher.pattern.exec(e);if(!n)return null;let r={};return t.matcher.paramNames.forEach((e,t)=>{let i=n[t+1]??``;try{r[e]=decodeURIComponent(i)}catch{r[e]=i}}),r}function o(e,t){for(let n of t){let t=a(e,n);if(t)return{params:t,record:n}}return{params:{}}}function s(e,t){return t.matcher.prefixPattern.test(e)}function c(e){let t=new URLSearchParams(e),n={},r=(e,t)=>{Object.defineProperty(n,e,{configurable:!0,enumerable:!0,value:t,writable:!0})};for(let[e,i]of t.entries()){let t=Object.hasOwn(n,e)?n[e]:void 0;if(t===void 0){r(e,i);continue}if(Array.isArray(t)){t.push(i);continue}r(e,[t,i])}return n}function l(t,r,i={},a){let o=r.replace(/:(\w+)(\*)?/g,(t,n,r)=>{let a=i[n];if(a==null)throw new e.WayfinderRouteError(`Missing path param: ${n}`);return r?a.split(`/`).map(e=>encodeURIComponent(e)).join(`/`):encodeURIComponent(a)});if(a&&Object.keys(a).length){let e=new URLSearchParams;Object.entries(a).forEach(([t,n])=>{Array.isArray(n)?n.forEach(n=>{e.append(t,String(n))}):e.set(t,String(n))}),o+=`?${e.toString()}`}return n(t,o)}function u(e,n=`/`){let r=t(e),i=t(n);if(i===`/`)return r;if(r===i)return`/`;let a=`${i}/`;return r.startsWith(a)?t(r.slice(i.length)):r}function d(e,t){return{hash:t.location.hash.replace(/^#/,``),historyState:t.location.state,pathname:u(t.location.pathname||`/`,e),query:c(t.location.search||``)}}
|
|
1
|
+
const e=require("./errors.cjs");function t(e){let t=`/${e}`.replace(/\/+/g,`/`);return t!==`/`&&t.endsWith(`/`)?t.slice(0,-1):t}function n(e,n){return t(`${e}/${n}`)}function r(e){return e.replace(/[.*+?^${}()|[\]\\]/g,`\\$&`)}function i(n){let i=t(n);if(i===`/`)return{paramNames:[],pattern:/^\/$/,prefixPattern:/^\/$/};let a=[],o=i.slice(1).split(`/`),s=[`^`],c=[`^`];for(let t=0;t<o.length;t+=1){let i=o[t];if(i===`*`){if(t!==o.length-1)throw new e.WayfinderRouteError(`Wildcard "*" must be the final segment in path: ${n}`);s.push(`(?:/.*)?`);break}if(i.startsWith(`:`)&&i.endsWith(`*`)){if(t!==o.length-1)throw new e.WayfinderRouteError(`Wildcard param must be final segment in path: ${n}`);let r=i.slice(1,-1);if(!/^\w+$/.test(r))throw new e.WayfinderRouteError(`Invalid param name ":${r}" in path "${n}". Param names must only contain word characters (a–z, A–Z, 0–9, _).`);a.push(r),s.push(t===o.length-1?`(?:/(.*))?`:`/(.*)`),c.push(`/.*`);break}if(i.startsWith(`:`)){let t=i.slice(1);if(!/^\w+$/.test(t))throw new e.WayfinderRouteError(`Invalid param name ":${t}" in path "${n}". Param names must only contain word characters (a–z, A–Z, 0–9, _).`);a.push(t),s.push(`/([^/]+)`),c.push(`/[^/]+`);continue}let l=r(i);s.push(`/${l}`),c.push(`/${l}`)}return s.push(`$`),c.push(`(?:/.*)?$`),{paramNames:a,pattern:new RegExp(s.join(``)),prefixPattern:new RegExp(c.join(``))}}function a(e,t){let n=t.matcher.pattern.exec(e);if(!n)return null;let r={};return t.matcher.paramNames.forEach((e,t)=>{let i=n[t+1]??``;try{r[e]=decodeURIComponent(i)}catch{r[e]=i}}),r}function o(e,t){for(let n of t){let t=a(e,n);if(t)return{params:t,record:n}}return{params:{}}}function s(e,t){return t.matcher.prefixPattern.test(e)}function c(e){let t=new URLSearchParams(e),n={},r=(e,t)=>{Object.defineProperty(n,e,{configurable:!0,enumerable:!0,value:t,writable:!0})};for(let[e,i]of t.entries()){let t=Object.hasOwn(n,e)?n[e]:void 0;if(t===void 0){r(e,i);continue}if(Array.isArray(t)){t.push(i);continue}r(e,[t,i])}return n}function l(t,r,i={},a){let o=r.replace(/:(\w+)(\*)?/g,(t,n,r)=>{let a=i[n];if(a==null)throw new e.WayfinderRouteError(`Missing path param: ${n}`);return r?a.split(`/`).map(e=>encodeURIComponent(e)).join(`/`):encodeURIComponent(a)});if(a&&Object.keys(a).length){let e=new URLSearchParams;Object.entries(a).forEach(([t,n])=>{Array.isArray(n)?n.forEach(n=>{e.append(t,String(n))}):e.set(t,String(n))}),o+=`?${e.toString()}`}return n(t,o)}function u(e,n=`/`){let r=t(e),i=t(n);if(i===`/`)return r;if(r===i)return`/`;let a=`${i}/`;return r.startsWith(a)?t(r.slice(i.length)):r}function d(e,t){return{hash:t.location.hash.replace(/^#/,``),historyState:t.location.state,pathname:u(t.location.pathname||`/`,e),query:c(t.location.search||``)}}exports.buildUrl=l,exports.compilePathMatcher=i,exports.joinPaths=n,exports.matchRecord=a,exports.matchRouteFor=o,exports.matchesPrefix=s,exports.normalizePath=t,exports.parseQuery=c,exports.readLocation=d,exports.stripBase=u;
|
|
2
2
|
//# sourceMappingURL=path.cjs.map
|
package/dist/path.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"path.cjs","names":[],"sources":["../src/path.ts"],"sourcesContent":["import { WayfinderRouteError } from './errors';\nimport type { QueryParams, ResolvedQueryParams, RouteLocation, RouteMatcher, RouteParams, RouteRecord } from './types';\n\n/** Ensure leading slash, collapse duplicate slashes, preserve root. */\nexport function normalizePath(path: string): string {\n const normalized = `/${path}`.replace(/\\/+/g, '/');\n\n return normalized !== '/' && normalized.endsWith('/') ? normalized.slice(0, -1) : normalized;\n}\n\nexport function joinPaths(base: string, path: string): string {\n return normalizePath(`${base}/${path}`);\n}\n\nfunction escapeRegex(input: string): string {\n return input.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n}\n\nexport function compilePathMatcher(path: string): RouteMatcher {\n const normalized = normalizePath(path);\n\n if (normalized === '/') {\n return {\n paramNames: [],\n pattern: /^\\/$/,\n prefixPattern: /^\\/$/,\n };\n }\n\n const paramNames: string[] = [];\n const segments = normalized.slice(1).split('/');\n const regexParts: string[] = ['^'];\n const prefixParts: string[] = ['^'];\n\n for (let i = 0; i < segments.length; i += 1) {\n const segment = segments[i]!;\n\n if (segment === '*') {\n if (i !== segments.length - 1) {\n throw new WayfinderRouteError(`Wildcard \"*\" must be the final segment in path: ${path}`);\n }\n\n regexParts.push('(?:/.*)?');\n break;\n }\n\n if (segment.startsWith(':') && segment.endsWith('*')) {\n if (i !== segments.length - 1) {\n throw new WayfinderRouteError(`Wildcard param must be final segment in path: ${path}`);\n }\n\n const name = segment.slice(1, -1);\n\n if (!/^\\w+$/.test(name)) {\n throw new WayfinderRouteError(\n `Invalid param name \":${name}\" in path \"${path}\". Param names must only contain word characters (a–z, A–Z, 0–9, _).`,\n );\n }\n\n paramNames.push(name);\n regexParts.push(i === segments.length - 1 ? '(?:/(.*))?' : '/(.*)');\n prefixParts.push('/.*');\n break;\n }\n\n if (segment.startsWith(':')) {\n const name = segment.slice(1);\n\n if (!/^\\w+$/.test(name)) {\n throw new WayfinderRouteError(\n `Invalid param name \":${name}\" in path \"${path}\". Param names must only contain word characters (a–z, A–Z, 0–9, _).`,\n );\n }\n\n paramNames.push(name);\n regexParts.push('/([^/]+)');\n prefixParts.push('/[^/]+');\n continue;\n }\n\n const escaped = escapeRegex(segment);\n\n regexParts.push(`/${escaped}`);\n prefixParts.push(`/${escaped}`);\n }\n\n regexParts.push('$');\n prefixParts.push('(?:/.*)?$');\n\n return {\n paramNames,\n pattern: new RegExp(regexParts.join('')),\n prefixPattern: new RegExp(prefixParts.join('')),\n };\n}\n\nexport function matchRecord<TMeta = unknown, TComponent = unknown>(\n pathname: string,\n record: RouteRecord<TMeta, TComponent>,\n): RouteParams | null {\n const match = record.matcher.pattern.exec(pathname);\n\n if (!match) return null;\n\n const params: RouteParams = {};\n\n record.matcher.paramNames.forEach((name, index) => {\n const value = match[index + 1] ?? '';\n\n try {\n params[name] = decodeURIComponent(value);\n } catch {\n params[name] = value;\n }\n });\n\n return params;\n}\n\nexport function matchRouteFor<TMeta = unknown, TComponent = unknown>(\n pathname: string,\n records: readonly RouteRecord<TMeta, TComponent>[],\n): { params: RouteParams; record?: RouteRecord<TMeta, TComponent> } {\n for (const record of records) {\n const params = matchRecord(pathname, record);\n\n if (params) return { params, record };\n }\n\n return { params: {} };\n}\n\nexport function matchesPrefix<TMeta = unknown, TComponent = unknown>(\n pathname: string,\n record: RouteRecord<TMeta, TComponent>,\n): boolean {\n return record.matcher.prefixPattern.test(pathname);\n}\n\n/**\n * Parse `?foo=a&foo=b&bar=c` into `{ foo: ['a', 'b'], bar: 'c' }`.\n *\n * @security Query keys come directly from the (attacker-controlled) URL. Plain bracket\n * assignment (`out[key] = value`) would let a key like `__proto__` reach the inherited\n * `Object.prototype.__proto__` setter and silently rewrite `out`'s own prototype instead of\n * storing a value — `setQueryValue` uses `Object.defineProperty` so every key, including\n * `__proto__`/`constructor`/`prototype`, is always stored as a plain own data property.\n */\nexport function parseQuery(queryString: string): QueryParams {\n const search = new URLSearchParams(queryString);\n const out: QueryParams = {};\n\n const setQueryValue = (key: string, value: QueryParams[string]): void => {\n Object.defineProperty(out, key, { configurable: true, enumerable: true, value, writable: true });\n };\n\n for (const [key, value] of search.entries()) {\n const existing = Object.hasOwn(out, key) ? out[key] : undefined;\n\n if (existing === undefined) {\n setQueryValue(key, value);\n continue;\n }\n\n if (Array.isArray(existing)) {\n existing.push(value);\n continue;\n }\n\n setQueryValue(key, [existing, value]);\n }\n\n return out;\n}\n\n/** Build a URL from a path pattern, params, and query, respecting the router base. */\nexport function buildUrl(base: string, pattern: string, params: RouteParams = {}, query?: ResolvedQueryParams): string {\n let path = pattern.replace(/:(\\w+)(\\*)?/g, (_match, key: string, isWildcard: string) => {\n const value = params[key];\n\n if (value == null) throw new WayfinderRouteError(`Missing path param: ${key}`);\n\n if (isWildcard) {\n return value\n .split('/')\n .map((segment) => encodeURIComponent(segment))\n .join('/');\n }\n\n return encodeURIComponent(value);\n });\n\n if (query && Object.keys(query).length) {\n const search = new URLSearchParams();\n\n Object.entries(query).forEach(([key, value]) => {\n if (Array.isArray(value))\n value.forEach((item) => {\n search.append(key, String(item));\n });\n else search.set(key, String(value));\n });\n\n path += `?${search.toString()}`;\n }\n\n return joinPaths(base, path);\n}\n\n// ─── Location helpers (formerly in resolve.ts) ────────────────────────────────\n\nexport function stripBase(pathname: string, base = '/'): string {\n const normalizedPath = normalizePath(pathname);\n const normalizedBase = normalizePath(base);\n\n if (normalizedBase === '/') return normalizedPath;\n\n if (normalizedPath === normalizedBase) return '/';\n\n const prefix = `${normalizedBase}/`;\n\n return normalizedPath.startsWith(prefix)\n ? normalizePath(normalizedPath.slice(normalizedBase.length))\n : normalizedPath;\n}\n\nexport function readLocation(\n base: string,\n history: { location: { hash: string; pathname: string; search: string; state: unknown } },\n): RouteLocation {\n return {\n hash: history.location.hash.replace(/^#/, ''),\n historyState: history.location.state,\n pathname: stripBase(history.location.pathname || '/', base),\n query: parseQuery(history.location.search || ''),\n };\n}\n\nexport function buildPreloadKey(base: string, path: string, params: RouteParams, query?: QueryParams): string {\n const url = buildUrl(base, path, params);\n\n if (!query || Object.keys(query).length === 0) return url;\n\n const search = new URLSearchParams();\n\n for (const [key, value] of Object.entries(query)) {\n if (Array.isArray(value)) {\n value.forEach((v) => {\n search.append(key, v);\n });\n } else {\n search.set(key, value);\n }\n }\n\n return `${url}?${search.toString()}`;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"path.cjs","names":[],"sources":["../src/path.ts"],"sourcesContent":["import { WayfinderRouteError } from './errors';\nimport type { QueryParams, ResolvedQueryParams, RouteLocation, RouteMatcher, RouteParams, RouteRecord } from './types';\n\n/** Ensure leading slash, collapse duplicate slashes, preserve root. */\nexport function normalizePath(path: string): string {\n const normalized = `/${path}`.replace(/\\/+/g, '/');\n\n return normalized !== '/' && normalized.endsWith('/') ? normalized.slice(0, -1) : normalized;\n}\n\nexport function joinPaths(base: string, path: string): string {\n return normalizePath(`${base}/${path}`);\n}\n\nfunction escapeRegex(input: string): string {\n return input.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n}\n\nexport function compilePathMatcher(path: string): RouteMatcher {\n const normalized = normalizePath(path);\n\n if (normalized === '/') {\n return {\n paramNames: [],\n pattern: /^\\/$/,\n prefixPattern: /^\\/$/,\n };\n }\n\n const paramNames: string[] = [];\n const segments = normalized.slice(1).split('/');\n const regexParts: string[] = ['^'];\n const prefixParts: string[] = ['^'];\n\n for (let i = 0; i < segments.length; i += 1) {\n const segment = segments[i]!;\n\n if (segment === '*') {\n if (i !== segments.length - 1) {\n throw new WayfinderRouteError(`Wildcard \"*\" must be the final segment in path: ${path}`);\n }\n\n regexParts.push('(?:/.*)?');\n break;\n }\n\n if (segment.startsWith(':') && segment.endsWith('*')) {\n if (i !== segments.length - 1) {\n throw new WayfinderRouteError(`Wildcard param must be final segment in path: ${path}`);\n }\n\n const name = segment.slice(1, -1);\n\n if (!/^\\w+$/.test(name)) {\n throw new WayfinderRouteError(\n `Invalid param name \":${name}\" in path \"${path}\". Param names must only contain word characters (a–z, A–Z, 0–9, _).`,\n );\n }\n\n paramNames.push(name);\n regexParts.push(i === segments.length - 1 ? '(?:/(.*))?' : '/(.*)');\n prefixParts.push('/.*');\n break;\n }\n\n if (segment.startsWith(':')) {\n const name = segment.slice(1);\n\n if (!/^\\w+$/.test(name)) {\n throw new WayfinderRouteError(\n `Invalid param name \":${name}\" in path \"${path}\". Param names must only contain word characters (a–z, A–Z, 0–9, _).`,\n );\n }\n\n paramNames.push(name);\n regexParts.push('/([^/]+)');\n prefixParts.push('/[^/]+');\n continue;\n }\n\n const escaped = escapeRegex(segment);\n\n regexParts.push(`/${escaped}`);\n prefixParts.push(`/${escaped}`);\n }\n\n regexParts.push('$');\n prefixParts.push('(?:/.*)?$');\n\n return {\n paramNames,\n pattern: new RegExp(regexParts.join('')),\n prefixPattern: new RegExp(prefixParts.join('')),\n };\n}\n\nexport function matchRecord(pathname: string, record: RouteRecord): RouteParams | null {\n const match = record.matcher.pattern.exec(pathname);\n\n if (!match) return null;\n\n const params: RouteParams = {};\n\n record.matcher.paramNames.forEach((name: string, index: number) => {\n const value = match[index + 1] ?? '';\n\n try {\n params[name] = decodeURIComponent(value);\n } catch {\n params[name] = value;\n }\n });\n\n return params;\n}\n\nexport function matchRouteFor(\n pathname: string,\n records: readonly RouteRecord[],\n): { params: RouteParams; record?: RouteRecord } {\n for (const record of records) {\n const params = matchRecord(pathname, record);\n\n if (params) return { params, record };\n }\n\n return { params: {} };\n}\n\nexport function matchesPrefix(pathname: string, record: RouteRecord): boolean {\n return record.matcher.prefixPattern.test(pathname);\n}\n\n/**\n * Parse `?foo=a&foo=b&bar=c` into `{ foo: ['a', 'b'], bar: 'c' }`.\n *\n * @security Query keys come directly from the (attacker-controlled) URL. Plain bracket\n * assignment (`out[key] = value`) would let a key like `__proto__` reach the inherited\n * `Object.prototype.__proto__` setter and silently rewrite `out`'s own prototype instead of\n * storing a value — `setQueryValue` uses `Object.defineProperty` so every key, including\n * `__proto__`/`constructor`/`prototype`, is always stored as a plain own data property.\n */\nexport function parseQuery(queryString: string): QueryParams {\n const search = new URLSearchParams(queryString);\n const out: QueryParams = {};\n\n const setQueryValue = (key: string, value: QueryParams[string]): void => {\n Object.defineProperty(out, key, { configurable: true, enumerable: true, value, writable: true });\n };\n\n for (const [key, value] of search.entries()) {\n const existing = Object.hasOwn(out, key) ? out[key] : undefined;\n\n if (existing === undefined) {\n setQueryValue(key, value);\n continue;\n }\n\n if (Array.isArray(existing)) {\n existing.push(value);\n continue;\n }\n\n setQueryValue(key, [existing, value]);\n }\n\n return out;\n}\n\n/** Build a URL from a path pattern, params, and query, respecting the router base. */\nexport function buildUrl(base: string, pattern: string, params: RouteParams = {}, query?: ResolvedQueryParams): string {\n let path = pattern.replace(/:(\\w+)(\\*)?/g, (_match, key: string, isWildcard: string) => {\n const value = params[key];\n\n if (value == null) throw new WayfinderRouteError(`Missing path param: ${key}`);\n\n if (isWildcard) {\n return value\n .split('/')\n .map((segment) => encodeURIComponent(segment))\n .join('/');\n }\n\n return encodeURIComponent(value);\n });\n\n if (query && Object.keys(query).length) {\n const search = new URLSearchParams();\n\n Object.entries(query).forEach(([key, value]) => {\n if (Array.isArray(value))\n value.forEach((item) => {\n search.append(key, String(item));\n });\n else search.set(key, String(value));\n });\n\n path += `?${search.toString()}`;\n }\n\n return joinPaths(base, path);\n}\n\n// ─── Location helpers (formerly in resolve.ts) ────────────────────────────────\n\nexport function stripBase(pathname: string, base = '/'): string {\n const normalizedPath = normalizePath(pathname);\n const normalizedBase = normalizePath(base);\n\n if (normalizedBase === '/') return normalizedPath;\n\n if (normalizedPath === normalizedBase) return '/';\n\n const prefix = `${normalizedBase}/`;\n\n return normalizedPath.startsWith(prefix)\n ? normalizePath(normalizedPath.slice(normalizedBase.length))\n : normalizedPath;\n}\n\nexport function readLocation(\n base: string,\n history: { location: { hash: string; pathname: string; search: string; state: unknown } },\n): RouteLocation {\n return {\n hash: history.location.hash.replace(/^#/, ''),\n historyState: history.location.state,\n pathname: stripBase(history.location.pathname || '/', base),\n query: parseQuery(history.location.search || ''),\n };\n}\n"],"mappings":"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"}
|
package/dist/path.d.ts
CHANGED
|
@@ -3,12 +3,12 @@ import type { QueryParams, ResolvedQueryParams, RouteLocation, RouteMatcher, Rou
|
|
|
3
3
|
export declare function normalizePath(path: string): string;
|
|
4
4
|
export declare function joinPaths(base: string, path: string): string;
|
|
5
5
|
export declare function compilePathMatcher(path: string): RouteMatcher;
|
|
6
|
-
export declare function matchRecord
|
|
7
|
-
export declare function matchRouteFor
|
|
6
|
+
export declare function matchRecord(pathname: string, record: RouteRecord): RouteParams | null;
|
|
7
|
+
export declare function matchRouteFor(pathname: string, records: readonly RouteRecord[]): {
|
|
8
8
|
params: RouteParams;
|
|
9
|
-
record?: RouteRecord
|
|
9
|
+
record?: RouteRecord;
|
|
10
10
|
};
|
|
11
|
-
export declare function matchesPrefix
|
|
11
|
+
export declare function matchesPrefix(pathname: string, record: RouteRecord): boolean;
|
|
12
12
|
/**
|
|
13
13
|
* Parse `?foo=a&foo=b&bar=c` into `{ foo: ['a', 'b'], bar: 'c' }`.
|
|
14
14
|
*
|
|
@@ -30,5 +30,4 @@ export declare function readLocation(base: string, history: {
|
|
|
30
30
|
state: unknown;
|
|
31
31
|
};
|
|
32
32
|
}): RouteLocation;
|
|
33
|
-
export declare function buildPreloadKey(base: string, path: string, params: RouteParams, query?: QueryParams): string;
|
|
34
33
|
//# sourceMappingURL=path.d.ts.map
|
package/dist/path.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"path.d.ts","sourceRoot":"","sources":["../src/path.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,mBAAmB,EAAE,aAAa,EAAE,YAAY,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAEvH,uEAAuE;AACvE,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAIlD;AAED,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAE5D;AAMD,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,YAAY,CA4E7D;AAED,wBAAgB,WAAW,CAAC,
|
|
1
|
+
{"version":3,"file":"path.d.ts","sourceRoot":"","sources":["../src/path.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,mBAAmB,EAAE,aAAa,EAAE,YAAY,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAEvH,uEAAuE;AACvE,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAIlD;AAED,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAE5D;AAMD,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,YAAY,CA4E7D;AAED,wBAAgB,WAAW,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,GAAG,WAAW,GAAG,IAAI,CAkBrF;AAED,wBAAgB,aAAa,CAC3B,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,SAAS,WAAW,EAAE,GAC9B;IAAE,MAAM,EAAE,WAAW,CAAC;IAAC,MAAM,CAAC,EAAE,WAAW,CAAA;CAAE,CAQ/C;AAED,wBAAgB,aAAa,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,GAAG,OAAO,CAE5E;AAED;;;;;;;;GAQG;AACH,wBAAgB,UAAU,CAAC,WAAW,EAAE,MAAM,GAAG,WAAW,CAyB3D;AAED,sFAAsF;AACtF,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,GAAE,WAAgB,EAAE,KAAK,CAAC,EAAE,mBAAmB,GAAG,MAAM,CA+BrH;AAID,wBAAgB,SAAS,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,SAAM,GAAG,MAAM,CAa9D;AAED,wBAAgB,YAAY,CAC1B,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE;IAAE,QAAQ,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,OAAO,CAAA;KAAE,CAAA;CAAE,GACxF,aAAa,CAOf"}
|
package/dist/path.js
CHANGED
|
@@ -127,16 +127,7 @@ function d(e, t) {
|
|
|
127
127
|
query: c(t.location.search || "")
|
|
128
128
|
};
|
|
129
129
|
}
|
|
130
|
-
function f(e, t, n, r) {
|
|
131
|
-
let i = l(e, t, n);
|
|
132
|
-
if (!r || Object.keys(r).length === 0) return i;
|
|
133
|
-
let a = new URLSearchParams();
|
|
134
|
-
for (let [e, t] of Object.entries(r)) Array.isArray(t) ? t.forEach((t) => {
|
|
135
|
-
a.append(e, t);
|
|
136
|
-
}) : a.set(e, t);
|
|
137
|
-
return `${i}?${a.toString()}`;
|
|
138
|
-
}
|
|
139
130
|
//#endregion
|
|
140
|
-
export {
|
|
131
|
+
export { l as buildUrl, i as compilePathMatcher, n as joinPaths, a as matchRecord, o as matchRouteFor, s as matchesPrefix, t as normalizePath, c as parseQuery, d as readLocation, u as stripBase };
|
|
141
132
|
|
|
142
133
|
//# sourceMappingURL=path.js.map
|
package/dist/path.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"path.js","names":[],"sources":["../src/path.ts"],"sourcesContent":["import { WayfinderRouteError } from './errors';\nimport type { QueryParams, ResolvedQueryParams, RouteLocation, RouteMatcher, RouteParams, RouteRecord } from './types';\n\n/** Ensure leading slash, collapse duplicate slashes, preserve root. */\nexport function normalizePath(path: string): string {\n const normalized = `/${path}`.replace(/\\/+/g, '/');\n\n return normalized !== '/' && normalized.endsWith('/') ? normalized.slice(0, -1) : normalized;\n}\n\nexport function joinPaths(base: string, path: string): string {\n return normalizePath(`${base}/${path}`);\n}\n\nfunction escapeRegex(input: string): string {\n return input.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n}\n\nexport function compilePathMatcher(path: string): RouteMatcher {\n const normalized = normalizePath(path);\n\n if (normalized === '/') {\n return {\n paramNames: [],\n pattern: /^\\/$/,\n prefixPattern: /^\\/$/,\n };\n }\n\n const paramNames: string[] = [];\n const segments = normalized.slice(1).split('/');\n const regexParts: string[] = ['^'];\n const prefixParts: string[] = ['^'];\n\n for (let i = 0; i < segments.length; i += 1) {\n const segment = segments[i]!;\n\n if (segment === '*') {\n if (i !== segments.length - 1) {\n throw new WayfinderRouteError(`Wildcard \"*\" must be the final segment in path: ${path}`);\n }\n\n regexParts.push('(?:/.*)?');\n break;\n }\n\n if (segment.startsWith(':') && segment.endsWith('*')) {\n if (i !== segments.length - 1) {\n throw new WayfinderRouteError(`Wildcard param must be final segment in path: ${path}`);\n }\n\n const name = segment.slice(1, -1);\n\n if (!/^\\w+$/.test(name)) {\n throw new WayfinderRouteError(\n `Invalid param name \":${name}\" in path \"${path}\". Param names must only contain word characters (a–z, A–Z, 0–9, _).`,\n );\n }\n\n paramNames.push(name);\n regexParts.push(i === segments.length - 1 ? '(?:/(.*))?' : '/(.*)');\n prefixParts.push('/.*');\n break;\n }\n\n if (segment.startsWith(':')) {\n const name = segment.slice(1);\n\n if (!/^\\w+$/.test(name)) {\n throw new WayfinderRouteError(\n `Invalid param name \":${name}\" in path \"${path}\". Param names must only contain word characters (a–z, A–Z, 0–9, _).`,\n );\n }\n\n paramNames.push(name);\n regexParts.push('/([^/]+)');\n prefixParts.push('/[^/]+');\n continue;\n }\n\n const escaped = escapeRegex(segment);\n\n regexParts.push(`/${escaped}`);\n prefixParts.push(`/${escaped}`);\n }\n\n regexParts.push('$');\n prefixParts.push('(?:/.*)?$');\n\n return {\n paramNames,\n pattern: new RegExp(regexParts.join('')),\n prefixPattern: new RegExp(prefixParts.join('')),\n };\n}\n\nexport function matchRecord<TMeta = unknown, TComponent = unknown>(\n pathname: string,\n record: RouteRecord<TMeta, TComponent>,\n): RouteParams | null {\n const match = record.matcher.pattern.exec(pathname);\n\n if (!match) return null;\n\n const params: RouteParams = {};\n\n record.matcher.paramNames.forEach((name, index) => {\n const value = match[index + 1] ?? '';\n\n try {\n params[name] = decodeURIComponent(value);\n } catch {\n params[name] = value;\n }\n });\n\n return params;\n}\n\nexport function matchRouteFor<TMeta = unknown, TComponent = unknown>(\n pathname: string,\n records: readonly RouteRecord<TMeta, TComponent>[],\n): { params: RouteParams; record?: RouteRecord<TMeta, TComponent> } {\n for (const record of records) {\n const params = matchRecord(pathname, record);\n\n if (params) return { params, record };\n }\n\n return { params: {} };\n}\n\nexport function matchesPrefix<TMeta = unknown, TComponent = unknown>(\n pathname: string,\n record: RouteRecord<TMeta, TComponent>,\n): boolean {\n return record.matcher.prefixPattern.test(pathname);\n}\n\n/**\n * Parse `?foo=a&foo=b&bar=c` into `{ foo: ['a', 'b'], bar: 'c' }`.\n *\n * @security Query keys come directly from the (attacker-controlled) URL. Plain bracket\n * assignment (`out[key] = value`) would let a key like `__proto__` reach the inherited\n * `Object.prototype.__proto__` setter and silently rewrite `out`'s own prototype instead of\n * storing a value — `setQueryValue` uses `Object.defineProperty` so every key, including\n * `__proto__`/`constructor`/`prototype`, is always stored as a plain own data property.\n */\nexport function parseQuery(queryString: string): QueryParams {\n const search = new URLSearchParams(queryString);\n const out: QueryParams = {};\n\n const setQueryValue = (key: string, value: QueryParams[string]): void => {\n Object.defineProperty(out, key, { configurable: true, enumerable: true, value, writable: true });\n };\n\n for (const [key, value] of search.entries()) {\n const existing = Object.hasOwn(out, key) ? out[key] : undefined;\n\n if (existing === undefined) {\n setQueryValue(key, value);\n continue;\n }\n\n if (Array.isArray(existing)) {\n existing.push(value);\n continue;\n }\n\n setQueryValue(key, [existing, value]);\n }\n\n return out;\n}\n\n/** Build a URL from a path pattern, params, and query, respecting the router base. */\nexport function buildUrl(base: string, pattern: string, params: RouteParams = {}, query?: ResolvedQueryParams): string {\n let path = pattern.replace(/:(\\w+)(\\*)?/g, (_match, key: string, isWildcard: string) => {\n const value = params[key];\n\n if (value == null) throw new WayfinderRouteError(`Missing path param: ${key}`);\n\n if (isWildcard) {\n return value\n .split('/')\n .map((segment) => encodeURIComponent(segment))\n .join('/');\n }\n\n return encodeURIComponent(value);\n });\n\n if (query && Object.keys(query).length) {\n const search = new URLSearchParams();\n\n Object.entries(query).forEach(([key, value]) => {\n if (Array.isArray(value))\n value.forEach((item) => {\n search.append(key, String(item));\n });\n else search.set(key, String(value));\n });\n\n path += `?${search.toString()}`;\n }\n\n return joinPaths(base, path);\n}\n\n// ─── Location helpers (formerly in resolve.ts) ────────────────────────────────\n\nexport function stripBase(pathname: string, base = '/'): string {\n const normalizedPath = normalizePath(pathname);\n const normalizedBase = normalizePath(base);\n\n if (normalizedBase === '/') return normalizedPath;\n\n if (normalizedPath === normalizedBase) return '/';\n\n const prefix = `${normalizedBase}/`;\n\n return normalizedPath.startsWith(prefix)\n ? normalizePath(normalizedPath.slice(normalizedBase.length))\n : normalizedPath;\n}\n\nexport function readLocation(\n base: string,\n history: { location: { hash: string; pathname: string; search: string; state: unknown } },\n): RouteLocation {\n return {\n hash: history.location.hash.replace(/^#/, ''),\n historyState: history.location.state,\n pathname: stripBase(history.location.pathname || '/', base),\n query: parseQuery(history.location.search || ''),\n };\n}\n\nexport function buildPreloadKey(base: string, path: string, params: RouteParams, query?: QueryParams): string {\n const url = buildUrl(base, path, params);\n\n if (!query || Object.keys(query).length === 0) return url;\n\n const search = new URLSearchParams();\n\n for (const [key, value] of Object.entries(query)) {\n if (Array.isArray(value)) {\n value.forEach((v) => {\n search.append(key, v);\n });\n } else {\n search.set(key, value);\n }\n }\n\n return `${url}?${search.toString()}`;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"path.js","names":[],"sources":["../src/path.ts"],"sourcesContent":["import { WayfinderRouteError } from './errors';\nimport type { QueryParams, ResolvedQueryParams, RouteLocation, RouteMatcher, RouteParams, RouteRecord } from './types';\n\n/** Ensure leading slash, collapse duplicate slashes, preserve root. */\nexport function normalizePath(path: string): string {\n const normalized = `/${path}`.replace(/\\/+/g, '/');\n\n return normalized !== '/' && normalized.endsWith('/') ? normalized.slice(0, -1) : normalized;\n}\n\nexport function joinPaths(base: string, path: string): string {\n return normalizePath(`${base}/${path}`);\n}\n\nfunction escapeRegex(input: string): string {\n return input.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n}\n\nexport function compilePathMatcher(path: string): RouteMatcher {\n const normalized = normalizePath(path);\n\n if (normalized === '/') {\n return {\n paramNames: [],\n pattern: /^\\/$/,\n prefixPattern: /^\\/$/,\n };\n }\n\n const paramNames: string[] = [];\n const segments = normalized.slice(1).split('/');\n const regexParts: string[] = ['^'];\n const prefixParts: string[] = ['^'];\n\n for (let i = 0; i < segments.length; i += 1) {\n const segment = segments[i]!;\n\n if (segment === '*') {\n if (i !== segments.length - 1) {\n throw new WayfinderRouteError(`Wildcard \"*\" must be the final segment in path: ${path}`);\n }\n\n regexParts.push('(?:/.*)?');\n break;\n }\n\n if (segment.startsWith(':') && segment.endsWith('*')) {\n if (i !== segments.length - 1) {\n throw new WayfinderRouteError(`Wildcard param must be final segment in path: ${path}`);\n }\n\n const name = segment.slice(1, -1);\n\n if (!/^\\w+$/.test(name)) {\n throw new WayfinderRouteError(\n `Invalid param name \":${name}\" in path \"${path}\". Param names must only contain word characters (a–z, A–Z, 0–9, _).`,\n );\n }\n\n paramNames.push(name);\n regexParts.push(i === segments.length - 1 ? '(?:/(.*))?' : '/(.*)');\n prefixParts.push('/.*');\n break;\n }\n\n if (segment.startsWith(':')) {\n const name = segment.slice(1);\n\n if (!/^\\w+$/.test(name)) {\n throw new WayfinderRouteError(\n `Invalid param name \":${name}\" in path \"${path}\". Param names must only contain word characters (a–z, A–Z, 0–9, _).`,\n );\n }\n\n paramNames.push(name);\n regexParts.push('/([^/]+)');\n prefixParts.push('/[^/]+');\n continue;\n }\n\n const escaped = escapeRegex(segment);\n\n regexParts.push(`/${escaped}`);\n prefixParts.push(`/${escaped}`);\n }\n\n regexParts.push('$');\n prefixParts.push('(?:/.*)?$');\n\n return {\n paramNames,\n pattern: new RegExp(regexParts.join('')),\n prefixPattern: new RegExp(prefixParts.join('')),\n };\n}\n\nexport function matchRecord(pathname: string, record: RouteRecord): RouteParams | null {\n const match = record.matcher.pattern.exec(pathname);\n\n if (!match) return null;\n\n const params: RouteParams = {};\n\n record.matcher.paramNames.forEach((name: string, index: number) => {\n const value = match[index + 1] ?? '';\n\n try {\n params[name] = decodeURIComponent(value);\n } catch {\n params[name] = value;\n }\n });\n\n return params;\n}\n\nexport function matchRouteFor(\n pathname: string,\n records: readonly RouteRecord[],\n): { params: RouteParams; record?: RouteRecord } {\n for (const record of records) {\n const params = matchRecord(pathname, record);\n\n if (params) return { params, record };\n }\n\n return { params: {} };\n}\n\nexport function matchesPrefix(pathname: string, record: RouteRecord): boolean {\n return record.matcher.prefixPattern.test(pathname);\n}\n\n/**\n * Parse `?foo=a&foo=b&bar=c` into `{ foo: ['a', 'b'], bar: 'c' }`.\n *\n * @security Query keys come directly from the (attacker-controlled) URL. Plain bracket\n * assignment (`out[key] = value`) would let a key like `__proto__` reach the inherited\n * `Object.prototype.__proto__` setter and silently rewrite `out`'s own prototype instead of\n * storing a value — `setQueryValue` uses `Object.defineProperty` so every key, including\n * `__proto__`/`constructor`/`prototype`, is always stored as a plain own data property.\n */\nexport function parseQuery(queryString: string): QueryParams {\n const search = new URLSearchParams(queryString);\n const out: QueryParams = {};\n\n const setQueryValue = (key: string, value: QueryParams[string]): void => {\n Object.defineProperty(out, key, { configurable: true, enumerable: true, value, writable: true });\n };\n\n for (const [key, value] of search.entries()) {\n const existing = Object.hasOwn(out, key) ? out[key] : undefined;\n\n if (existing === undefined) {\n setQueryValue(key, value);\n continue;\n }\n\n if (Array.isArray(existing)) {\n existing.push(value);\n continue;\n }\n\n setQueryValue(key, [existing, value]);\n }\n\n return out;\n}\n\n/** Build a URL from a path pattern, params, and query, respecting the router base. */\nexport function buildUrl(base: string, pattern: string, params: RouteParams = {}, query?: ResolvedQueryParams): string {\n let path = pattern.replace(/:(\\w+)(\\*)?/g, (_match, key: string, isWildcard: string) => {\n const value = params[key];\n\n if (value == null) throw new WayfinderRouteError(`Missing path param: ${key}`);\n\n if (isWildcard) {\n return value\n .split('/')\n .map((segment) => encodeURIComponent(segment))\n .join('/');\n }\n\n return encodeURIComponent(value);\n });\n\n if (query && Object.keys(query).length) {\n const search = new URLSearchParams();\n\n Object.entries(query).forEach(([key, value]) => {\n if (Array.isArray(value))\n value.forEach((item) => {\n search.append(key, String(item));\n });\n else search.set(key, String(value));\n });\n\n path += `?${search.toString()}`;\n }\n\n return joinPaths(base, path);\n}\n\n// ─── Location helpers (formerly in resolve.ts) ────────────────────────────────\n\nexport function stripBase(pathname: string, base = '/'): string {\n const normalizedPath = normalizePath(pathname);\n const normalizedBase = normalizePath(base);\n\n if (normalizedBase === '/') return normalizedPath;\n\n if (normalizedPath === normalizedBase) return '/';\n\n const prefix = `${normalizedBase}/`;\n\n return normalizedPath.startsWith(prefix)\n ? normalizePath(normalizedPath.slice(normalizedBase.length))\n : normalizedPath;\n}\n\nexport function readLocation(\n base: string,\n history: { location: { hash: string; pathname: string; search: string; state: unknown } },\n): RouteLocation {\n return {\n hash: history.location.hash.replace(/^#/, ''),\n historyState: history.location.state,\n pathname: stripBase(history.location.pathname || '/', base),\n query: parseQuery(history.location.search || ''),\n };\n}\n"],"mappings":"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"}
|