@vielzeug/wayfinder 2.0.0 → 2.1.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.
Files changed (51) hide show
  1. package/README.md +2 -2
  2. package/dist/_dev.cjs.map +1 -1
  3. package/dist/_dev.js.map +1 -1
  4. package/dist/compile.cjs.map +1 -1
  5. package/dist/compile.d.ts.map +1 -1
  6. package/dist/compile.js.map +1 -1
  7. package/dist/context.cjs +1 -1
  8. package/dist/context.cjs.map +1 -1
  9. package/dist/context.d.ts +4 -4
  10. package/dist/context.d.ts.map +1 -1
  11. package/dist/context.js +1 -1
  12. package/dist/context.js.map +1 -1
  13. package/dist/devtools.cjs.map +1 -1
  14. package/dist/devtools.d.ts.map +1 -1
  15. package/dist/devtools.js.map +1 -1
  16. package/dist/errors.cjs +1 -1
  17. package/dist/errors.cjs.map +1 -1
  18. package/dist/errors.d.ts +0 -1
  19. package/dist/errors.d.ts.map +1 -1
  20. package/dist/errors.js +1 -4
  21. package/dist/errors.js.map +1 -1
  22. package/dist/guards.cjs.map +1 -1
  23. package/dist/guards.d.ts +1 -1
  24. package/dist/guards.js.map +1 -1
  25. package/dist/history.cjs +1 -1
  26. package/dist/history.cjs.map +1 -1
  27. package/dist/history.d.ts.map +1 -1
  28. package/dist/history.js +3 -1
  29. package/dist/history.js.map +1 -1
  30. package/dist/index.d.ts +3 -3
  31. package/dist/index.d.ts.map +1 -1
  32. package/dist/path.cjs +1 -1
  33. package/dist/path.cjs.map +1 -1
  34. package/dist/path.d.ts.map +1 -1
  35. package/dist/path.js +6 -2
  36. package/dist/path.js.map +1 -1
  37. package/dist/router.cjs +1 -1
  38. package/dist/router.cjs.map +1 -1
  39. package/dist/router.d.ts +3 -4
  40. package/dist/router.d.ts.map +1 -1
  41. package/dist/router.js +37 -33
  42. package/dist/router.js.map +1 -1
  43. package/dist/types.d.ts +13 -14
  44. package/dist/types.d.ts.map +1 -1
  45. package/dist/wayfinder.cjs +1 -1
  46. package/dist/wayfinder.cjs.map +1 -1
  47. package/dist/wayfinder.iife.js +1 -1
  48. package/dist/wayfinder.iife.js.map +1 -1
  49. package/dist/wayfinder.js +1 -1
  50. package/dist/wayfinder.js.map +1 -1
  51. package/package.json +33 -28
package/README.md CHANGED
@@ -31,8 +31,8 @@
31
31
  - Declarative `notFound` fallback route
32
32
  - Browser and memory history drivers
33
33
  - `redirectTo()` middleware helper
34
- - SSR-compatible `loadPath()` for data prefetching without side effects
35
- - `matchPath()` for synchronous route inspection
34
+ - SSR-compatible `load()` for data prefetching without side effects
35
+ - `match()` for synchronous route inspection
36
36
  - `ready` and `waitFor(name)` for lifecycle coordination
37
37
 
38
38
  ## Installation
package/dist/_dev.cjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"_dev.cjs","names":[],"sources":["../src/_dev.ts"],"sourcesContent":["const isDev = !(globalThis as { __WAYFINDER_PROD__?: boolean }).__WAYFINDER_PROD__;\n\n/** @internal @security Messages may include user-supplied data. */\nexport function warn(msg: string): void {\n if (isDev) console.warn(`[@vielzeug/wayfinder] ${msg}`);\n}\n\n/** @internal */\nexport function error(msg: string, ...args: unknown[]): void {\n if (isDev) console.error(`[@vielzeug/wayfinder] ${msg}`, ...args);\n}\n\n/** @internal — Run fn only in dev builds. Use when dev-only logic goes beyond a single warn() / error() call. */\nexport function devOnly(fn: () => void): void {\n if (isDev) fn();\n}\n"],"mappings":"AAGA,SAAgB,EAAK,EAAmB,CAExC,CAGA,SAAgB,EAAM,EAAa,GAAG,EAAuB,CAE7D"}
1
+ {"version":3,"file":"_dev.cjs","names":[],"sources":["../src/_dev.ts"],"sourcesContent":["const isDev = !(globalThis as { __WAYFINDER_PROD__?: boolean }).__WAYFINDER_PROD__;\n\n/** @internal @security Messages may include user-supplied data. */\nexport function warn(msg: string): void {\n if (isDev) console.warn(`[@vielzeug/wayfinder] ${msg}`);\n}\n\n/** @internal */\nexport function error(msg: string, ...args: unknown[]): void {\n if (isDev) console.error(`[@vielzeug/wayfinder] ${msg}`, ...args);\n}\n"],"mappings":"AAGA,SAAgB,EAAK,EAAmB,CAExC,CAGA,SAAgB,EAAM,EAAa,GAAG,EAAuB,CAE7D"}
package/dist/_dev.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"_dev.js","names":[],"sources":["../src/_dev.ts"],"sourcesContent":["const isDev = !(globalThis as { __WAYFINDER_PROD__?: boolean }).__WAYFINDER_PROD__;\n\n/** @internal @security Messages may include user-supplied data. */\nexport function warn(msg: string): void {\n if (isDev) console.warn(`[@vielzeug/wayfinder] ${msg}`);\n}\n\n/** @internal */\nexport function error(msg: string, ...args: unknown[]): void {\n if (isDev) console.error(`[@vielzeug/wayfinder] ${msg}`, ...args);\n}\n\n/** @internal — Run fn only in dev builds. Use when dev-only logic goes beyond a single warn() / error() call. */\nexport function devOnly(fn: () => void): void {\n if (isDev) fn();\n}\n"],"mappings":";AAGA,SAAgB,EAAK,GAAmB,CAExC;AAGA,SAAgB,EAAM,GAAa,GAAG,GAAuB,CAE7D"}
1
+ {"version":3,"file":"_dev.js","names":[],"sources":["../src/_dev.ts"],"sourcesContent":["const isDev = !(globalThis as { __WAYFINDER_PROD__?: boolean }).__WAYFINDER_PROD__;\n\n/** @internal @security Messages may include user-supplied data. */\nexport function warn(msg: string): void {\n if (isDev) console.warn(`[@vielzeug/wayfinder] ${msg}`);\n}\n\n/** @internal */\nexport function error(msg: string, ...args: unknown[]): void {\n if (isDev) console.error(`[@vielzeug/wayfinder] ${msg}`, ...args);\n}\n"],"mappings":";AAGA,SAAgB,EAAK,GAAmB,CAExC;AAGA,SAAgB,EAAM,GAAa,GAAG,GAAuB,CAE7D"}
@@ -1 +1 @@
1
- {"version":3,"file":"compile.cjs","names":[],"sources":["../src/compile.ts"],"sourcesContent":["import type { Middleware, RouteBranchDef, RouteDefinition, RouteRecord, RouterOptions, RouteTable } from './types';\n\nimport { warn } from './_dev';\nimport { WayfinderRouteError } from './errors';\nimport { compilePathMatcher, joinPaths, normalizePath } from './path';\n\nexport type CompiledRoutes<TMeta = unknown, TComponent = unknown> = {\n records: readonly RouteRecord<TMeta, TComponent>[];\n routesByName: ReadonlyMap<string, RouteRecord<TMeta, TComponent>>;\n};\n\nexport function compileRoutes<TRoutes extends RouteTable, TMeta, TComponent>(\n options: RouterOptions<TRoutes, TMeta, TComponent>,\n): CompiledRoutes<TMeta, TComponent> {\n const records: RouteRecord<TMeta, TComponent>[] = [];\n\n const compile = (\n name: string,\n route: RouteDefinition,\n ancestorPath: string,\n ancestorBranchDefs: RouteBranchDef<TMeta, TComponent>[],\n ancestorMiddleware: RouteRecord<TMeta, TComponent>['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<TMeta, TComponent>[] = [\n ...ancestorBranchDefs,\n {\n component: route.component as TComponent | undefined,\n dataFn: route.data,\n lazy: route.lazy as RouteBranchDef<TMeta, TComponent>['lazy'],\n meta: route.meta as TMeta | undefined,\n name,\n onError: route.onError as RouteBranchDef<TMeta, TComponent>['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 // R9: emit a record for leaf routes (no children), redirects, lazy routes, and routes with data.\n // A bare parent-only route (children but no data/lazy/redirect) is not emitted as a leaf.\n if (route.lazy !== undefined || 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":"8EAWA,SAAgB,EACd,EACmC,CACnC,IAAM,EAA4C,CAAC,EAE7C,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,EAAkD,CACtD,GAAG,EACH,CACE,UAAW,EAAM,UACjB,OAAQ,EAAM,KACd,KAAM,EAAM,KACZ,KAAM,EAAM,KACZ,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,OAAS,IAAA,IAAa,EAAM,WAAa,IAAA,IAAa,CAAC,EAAM,UAAY,EAAM,OAAS,IAAA,GAAW,CAC3G,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"}
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<TMeta = unknown, TComponent = unknown> = {\n records: readonly RouteRecord<TMeta, TComponent>[];\n routesByName: ReadonlyMap<string, RouteRecord<TMeta, TComponent>>;\n};\n\nexport function compileRoutes<TRoutes extends RouteTable, TMeta, TComponent>(\n options: RouterOptions<TRoutes, TMeta, TComponent>,\n): CompiledRoutes<TMeta, TComponent> {\n const records: RouteRecord<TMeta, TComponent>[] = [];\n\n const compile = (\n name: string,\n route: RouteDefinition,\n ancestorPath: string,\n ancestorBranchDefs: RouteBranchDef<TMeta, TComponent>[],\n ancestorMiddleware: RouteRecord<TMeta, TComponent>['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<TMeta, TComponent>[] = [\n ...ancestorBranchDefs,\n {\n component: route.component as TComponent | undefined,\n dataFn: route.data,\n lazy: route.lazy as RouteBranchDef<TMeta, TComponent>['lazy'],\n meta: route.meta as TMeta | undefined,\n name,\n onError: route.onError as RouteBranchDef<TMeta, TComponent>['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), redirects, lazy routes, and routes with data.\n // A bare parent-only route (children but no data/lazy/redirect) is not emitted as a leaf.\n if (route.lazy !== undefined || 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":"8EAUA,SAAgB,EACd,EACmC,CACnC,IAAM,EAA4C,CAAC,EAE7C,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,EAAkD,CACtD,GAAG,EACH,CACE,UAAW,EAAM,UACjB,OAAQ,EAAM,KACd,KAAM,EAAM,KACZ,KAAM,EAAM,KACZ,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,OAAS,IAAA,IAAa,EAAM,WAAa,IAAA,IAAa,CAAC,EAAM,UAAY,EAAM,OAAS,IAAA,GAAW,CAC3G,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"}
@@ -1 +1 @@
1
- {"version":3,"file":"compile.d.ts","sourceRoot":"","sources":["../src/compile.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAA+C,WAAW,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAMnH,MAAM,MAAM,cAAc,CAAC,KAAK,GAAG,OAAO,EAAE,UAAU,GAAG,OAAO,IAAI;IAClE,OAAO,EAAE,SAAS,WAAW,CAAC,KAAK,EAAE,UAAU,CAAC,EAAE,CAAC;IACnD,YAAY,EAAE,WAAW,CAAC,MAAM,EAAE,WAAW,CAAC,KAAK,EAAE,UAAU,CAAC,CAAC,CAAC;CACnE,CAAC;AAEF,wBAAgB,aAAa,CAAC,OAAO,SAAS,UAAU,EAAE,KAAK,EAAE,UAAU,EACzE,OAAO,EAAE,aAAa,CAAC,OAAO,EAAE,KAAK,EAAE,UAAU,CAAC,GACjD,cAAc,CAAC,KAAK,EAAE,UAAU,CAAC,CAoGnC"}
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,CAAC,KAAK,GAAG,OAAO,EAAE,UAAU,GAAG,OAAO,IAAI;IAClE,OAAO,EAAE,SAAS,WAAW,CAAC,KAAK,EAAE,UAAU,CAAC,EAAE,CAAC;IACnD,YAAY,EAAE,WAAW,CAAC,MAAM,EAAE,WAAW,CAAC,KAAK,EAAE,UAAU,CAAC,CAAC,CAAC;CACnE,CAAC;AAEF,wBAAgB,aAAa,CAAC,OAAO,SAAS,UAAU,EAAE,KAAK,EAAE,UAAU,EACzE,OAAO,EAAE,aAAa,CAAC,OAAO,EAAE,KAAK,EAAE,UAAU,CAAC,GACjD,cAAc,CAAC,KAAK,EAAE,UAAU,CAAC,CAoGnC"}
@@ -1 +1 @@
1
- {"version":3,"file":"compile.js","names":[],"sources":["../src/compile.ts"],"sourcesContent":["import type { Middleware, RouteBranchDef, RouteDefinition, RouteRecord, RouterOptions, RouteTable } from './types';\n\nimport { warn } from './_dev';\nimport { WayfinderRouteError } from './errors';\nimport { compilePathMatcher, joinPaths, normalizePath } from './path';\n\nexport type CompiledRoutes<TMeta = unknown, TComponent = unknown> = {\n records: readonly RouteRecord<TMeta, TComponent>[];\n routesByName: ReadonlyMap<string, RouteRecord<TMeta, TComponent>>;\n};\n\nexport function compileRoutes<TRoutes extends RouteTable, TMeta, TComponent>(\n options: RouterOptions<TRoutes, TMeta, TComponent>,\n): CompiledRoutes<TMeta, TComponent> {\n const records: RouteRecord<TMeta, TComponent>[] = [];\n\n const compile = (\n name: string,\n route: RouteDefinition,\n ancestorPath: string,\n ancestorBranchDefs: RouteBranchDef<TMeta, TComponent>[],\n ancestorMiddleware: RouteRecord<TMeta, TComponent>['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<TMeta, TComponent>[] = [\n ...ancestorBranchDefs,\n {\n component: route.component as TComponent | undefined,\n dataFn: route.data,\n lazy: route.lazy as RouteBranchDef<TMeta, TComponent>['lazy'],\n meta: route.meta as TMeta | undefined,\n name,\n onError: route.onError as RouteBranchDef<TMeta, TComponent>['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 // R9: emit a record for leaf routes (no children), redirects, lazy routes, and routes with data.\n // A bare parent-only route (children but no data/lazy/redirect) is not emitted as a leaf.\n if (route.lazy !== undefined || 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":";;;;AAWA,SAAgB,EACd,GACmC;CACnC,IAAM,IAA4C,CAAC,GAE7C,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,IAAkD,CACtD,GAAG,GACH;GACE,WAAW,EAAM;GACjB,QAAQ,EAAM;GACd,MAAM,EAAM;GACZ,MAAM,EAAM;GACZ;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,SAAS,KAAA,KAAa,EAAM,aAAa,KAAA,KAAa,CAAC,EAAM,YAAY,EAAM,SAAS,KAAA,GAAW;GAC3G,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"}
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<TMeta = unknown, TComponent = unknown> = {\n records: readonly RouteRecord<TMeta, TComponent>[];\n routesByName: ReadonlyMap<string, RouteRecord<TMeta, TComponent>>;\n};\n\nexport function compileRoutes<TRoutes extends RouteTable, TMeta, TComponent>(\n options: RouterOptions<TRoutes, TMeta, TComponent>,\n): CompiledRoutes<TMeta, TComponent> {\n const records: RouteRecord<TMeta, TComponent>[] = [];\n\n const compile = (\n name: string,\n route: RouteDefinition,\n ancestorPath: string,\n ancestorBranchDefs: RouteBranchDef<TMeta, TComponent>[],\n ancestorMiddleware: RouteRecord<TMeta, TComponent>['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<TMeta, TComponent>[] = [\n ...ancestorBranchDefs,\n {\n component: route.component as TComponent | undefined,\n dataFn: route.data,\n lazy: route.lazy as RouteBranchDef<TMeta, TComponent>['lazy'],\n meta: route.meta as TMeta | undefined,\n name,\n onError: route.onError as RouteBranchDef<TMeta, TComponent>['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), redirects, lazy routes, and routes with data.\n // A bare parent-only route (children but no data/lazy/redirect) is not emitted as a leaf.\n if (route.lazy !== undefined || 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,EACd,GACmC;CACnC,IAAM,IAA4C,CAAC,GAE7C,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,IAAkD,CACtD,GAAG,GACH;GACE,WAAW,EAAM;GACjB,QAAQ,EAAM;GACd,MAAM,EAAM;GACZ,MAAM,EAAM;GACZ;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,SAAS,KAAA,KAAa,EAAM,aAAa,KAAA,KAAa,CAAC,EAAM,YAAY,EAAM,SAAS,KAAA,GAAW;GAC3G,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
- require("./_dev.cjs");const e=require("./errors.cjs");function t(e){return{error:e.error,location:e.location,matches:e.matches,status:e.status}}function n(e,t,n,r,i){return e.map((e,a)=>({component:e.component,data:r[a],meta:e.meta,name:e.name,params:{...t},pathname:n,status:i?.[a]??`idle`}))}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){let i=!1;async function a(o){if(o<n.length){let r=!1;await n[o](t,async()=>{if(r)throw new e.WayfinderApiError(`next() called multiple times`);r=!0,await a(o+1)});return}i=!0,await r()}return await a(0),i}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;
1
+ require("./_dev.cjs");const e=require("./errors.cjs");function t(e){return{error:e.error,location:e.location,matches:e.matches,status:e.status}}function n(e,t,n,r,i){return e.map((e,a)=>({component:e.component,data:r[a],meta:e.meta,name:e.name,params:{...t},pathname:n,status:i?.[a]??`idle`}))}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){let i=!1;async function a(o){if(o<n.length){let r=!1;await n[o]?.(t,async()=>{if(r)throw new e.WayfinderApiError(`next() called multiple times`);r=!0,await a(o+1)});return}i=!0,await r()}return await a(0),i}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
@@ -1 +1 @@
1
- {"version":3,"file":"context.cjs","names":[],"sources":["../src/context.ts"],"sourcesContent":["import type {\n MatchStatus,\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\nimport { error as logError } from './_dev';\nimport { WayfinderApiError } from './errors';\n\n// ─── Route state ──────────────────────────────────────────────────────────────\n\n/** R5: No defensive deep-copy — RouteState is typed readonly; callers always pass fresh objects. */\nexport function createRouteState<TMeta = unknown, TComponent = unknown>(input: {\n error?: unknown;\n location: RouteLocation;\n matches: RouteMatchBranch<TMeta, TComponent>;\n status: NavigationStatus;\n}): RouteState<TMeta, TComponent> {\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, data results, and optional per-node statuses.\n * F1: Each node carries its own `status` for fine-grained nested layout feedback.\n */\nexport function buildMatchBranch<TMeta = unknown, TComponent = unknown>(\n branchDefs: readonly RouteBranchDef<TMeta, TComponent>[],\n params: RouteParams,\n pathname: string,\n dataResults: unknown[],\n statuses?: readonly MatchStatus[],\n): RouteMatchBranch<TMeta, TComponent> {\n return branchDefs.map((def, i): RouteMatchBranch<TMeta, TComponent>[number] => ({\n component: def.component as TComponent,\n data: dataResults[i],\n meta: def.meta as TMeta,\n name: def.name,\n params: { ...params },\n pathname,\n status: statuses?.[i] ?? 'idle',\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): 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 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\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":"sDAqBA,SAAgB,EAAwD,EAKtC,CAChC,MAAO,CACL,MAAO,EAAM,MACb,SAAU,EAAM,SAChB,QAAS,EAAM,QACf,OAAQ,EAAM,MAChB,CACF,CAMA,SAAgB,EACd,EACA,EACA,EACA,EACA,EACqC,CACrC,OAAO,EAAW,KAAK,EAAK,KAAoD,CAC9E,UAAW,EAAI,UACf,KAAM,EAAY,GAClB,KAAM,EAAI,KACV,KAAM,EAAI,KACV,OAAQ,CAAE,GAAG,CAAO,EACpB,WACA,OAAQ,IAAW,IAAM,MAC3B,EAAE,CACJ,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,EACkB,CAClB,IAAI,EAAc,GAElB,eAAe,EAAS,EAA8B,CACpD,GAAI,EAAQ,EAAW,OAAQ,CAC7B,IAAI,EAAS,GAEb,MAAM,EAAW,EAAM,CAAE,EAAS,SAAY,CAC5C,GAAI,EAAQ,MAAM,IAAI,EAAA,kBAAkB,8BAA8B,EAEtE,EAAS,GACT,MAAM,EAAS,EAAQ,CAAC,CAC1B,CAAC,EAED,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"}
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<TMeta = unknown, TComponent = unknown>(input: {\n error?: unknown;\n location: RouteLocation;\n matches: RouteMatchBranch<TMeta, TComponent>;\n status: NavigationStatus;\n}): RouteState<TMeta, TComponent> {\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, data results, and optional per-node statuses.\n * Each node carries its own `status` for fine-grained nested layout feedback.\n */\nexport function buildMatchBranch<TMeta = unknown, TComponent = unknown>(\n branchDefs: readonly RouteBranchDef<TMeta, TComponent>[],\n params: RouteParams,\n pathname: string,\n dataResults: unknown[],\n statuses?: readonly NavigationStatus[],\n): RouteMatchBranch<TMeta, TComponent> {\n return branchDefs.map((def, i): RouteMatchBranch<TMeta, TComponent>[number] => ({\n component: def.component as TComponent,\n data: dataResults[i],\n meta: def.meta as TMeta,\n name: def.name,\n params: { ...params },\n pathname,\n status: statuses?.[i] ?? 'idle',\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): 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 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\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":"sDAmBA,SAAgB,EAAwD,EAKtC,CAChC,MAAO,CACL,MAAO,EAAM,MACb,SAAU,EAAM,SAChB,QAAS,EAAM,QACf,OAAQ,EAAM,MAChB,CACF,CAMA,SAAgB,EACd,EACA,EACA,EACA,EACA,EACqC,CACrC,OAAO,EAAW,KAAK,EAAK,KAAoD,CAC9E,UAAW,EAAI,UACf,KAAM,EAAY,GAClB,KAAM,EAAI,KACV,KAAM,EAAI,KACV,OAAQ,CAAE,GAAG,CAAO,EACpB,WACA,OAAQ,IAAW,IAAM,MAC3B,EAAE,CACJ,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,EACkB,CAClB,IAAI,EAAc,GAElB,eAAe,EAAS,EAA8B,CACpD,GAAI,EAAQ,EAAW,OAAQ,CAC7B,IAAI,EAAS,GAEb,MAAM,EAAW,EAAM,GAAG,EAAS,SAAY,CAC7C,GAAI,EAAQ,MAAM,IAAI,EAAA,kBAAkB,8BAA8B,EAEtE,EAAS,GACT,MAAM,EAAS,EAAQ,CAAC,CAC1B,CAAC,EAED,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,5 +1,5 @@
1
- import type { MatchStatus, Middleware, NavigationStatus, ResolvedQueryParams, RouteBranchDef, RouteContext, RouteLocation, RouteMatchBranch, RouteParams, RouterErrorContext, RouteState, RouteTable } from './types';
2
- /** R5: No defensive deep-copy — RouteState is typed readonly; callers always pass fresh objects. */
1
+ import type { Middleware, NavigationStatus, ResolvedQueryParams, RouteBranchDef, RouteContext, RouteLocation, RouteMatchBranch, RouteParams, RouterErrorContext, RouteState, RouteTable } from './types';
2
+ /** No defensive deep-copy — RouteState is typed readonly; callers always pass fresh objects. */
3
3
  export declare function createRouteState<TMeta = unknown, TComponent = unknown>(input: {
4
4
  error?: unknown;
5
5
  location: RouteLocation;
@@ -8,9 +8,9 @@ export declare function createRouteState<TMeta = unknown, TComponent = unknown>(
8
8
  }): RouteState<TMeta, TComponent>;
9
9
  /**
10
10
  * Build a RouteMatchBranch from compiled defs, data results, and optional per-node statuses.
11
- * F1: Each node carries its own `status` for fine-grained nested layout feedback.
11
+ * Each node carries its own `status` for fine-grained nested layout feedback.
12
12
  */
13
- export declare function buildMatchBranch<TMeta = unknown, TComponent = unknown>(branchDefs: readonly RouteBranchDef<TMeta, TComponent>[], params: RouteParams, pathname: string, dataResults: unknown[], statuses?: readonly MatchStatus[]): RouteMatchBranch<TMeta, TComponent>;
13
+ export declare function buildMatchBranch<TMeta = unknown, TComponent = unknown>(branchDefs: readonly RouteBranchDef<TMeta, TComponent>[], params: RouteParams, pathname: string, dataResults: unknown[], statuses?: readonly NavigationStatus[]): RouteMatchBranch<TMeta, TComponent>;
14
14
  export declare function createRouteContext<TRoutes extends RouteTable>(location: RouteLocation, resolvedQuery: ResolvedQueryParams, params: RouteParams, matches: RouteMatchBranch, navigate: RouteContext<RouteParams, TRoutes>['navigate']): RouteContext<RouteParams, TRoutes>;
15
15
  export declare function executeMiddlewarePipeline<TRoutes extends RouteTable>(context: RouteContext<RouteParams, TRoutes>, middleware: readonly Middleware<TRoutes>[], terminal: () => Promise<void>): Promise<boolean>;
16
16
  export declare function reportError(error: unknown, context: RouterErrorContext, onError?: (e: unknown, ctx: RouterErrorContext) => void): void;
@@ -1 +1 @@
1
- {"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../src/context.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,WAAW,EACX,UAAU,EACV,gBAAgB,EAChB,mBAAmB,EACnB,cAAc,EACd,YAAY,EACZ,aAAa,EACb,gBAAgB,EAChB,WAAW,EACX,kBAAkB,EAClB,UAAU,EACV,UAAU,EACX,MAAM,SAAS,CAAC;AAOjB,oGAAoG;AACpG,wBAAgB,gBAAgB,CAAC,KAAK,GAAG,OAAO,EAAE,UAAU,GAAG,OAAO,EAAE,KAAK,EAAE;IAC7E,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,QAAQ,EAAE,aAAa,CAAC;IACxB,OAAO,EAAE,gBAAgB,CAAC,KAAK,EAAE,UAAU,CAAC,CAAC;IAC7C,MAAM,EAAE,gBAAgB,CAAC;CAC1B,GAAG,UAAU,CAAC,KAAK,EAAE,UAAU,CAAC,CAOhC;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,GAAG,OAAO,EAAE,UAAU,GAAG,OAAO,EACpE,UAAU,EAAE,SAAS,cAAc,CAAC,KAAK,EAAE,UAAU,CAAC,EAAE,EACxD,MAAM,EAAE,WAAW,EACnB,QAAQ,EAAE,MAAM,EAChB,WAAW,EAAE,OAAO,EAAE,EACtB,QAAQ,CAAC,EAAE,SAAS,WAAW,EAAE,GAChC,gBAAgB,CAAC,KAAK,EAAE,UAAU,CAAC,CAUrC;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,GAC5B,OAAO,CAAC,OAAO,CAAC,CAwBlB;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"}
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,EACb,gBAAgB,EAChB,WAAW,EACX,kBAAkB,EAClB,UAAU,EACV,UAAU,EACX,MAAM,SAAS,CAAC;AAIjB,gGAAgG;AAChG,wBAAgB,gBAAgB,CAAC,KAAK,GAAG,OAAO,EAAE,UAAU,GAAG,OAAO,EAAE,KAAK,EAAE;IAC7E,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,QAAQ,EAAE,aAAa,CAAC;IACxB,OAAO,EAAE,gBAAgB,CAAC,KAAK,EAAE,UAAU,CAAC,CAAC;IAC7C,MAAM,EAAE,gBAAgB,CAAC;CAC1B,GAAG,UAAU,CAAC,KAAK,EAAE,UAAU,CAAC,CAOhC;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,GAAG,OAAO,EAAE,UAAU,GAAG,OAAO,EACpE,UAAU,EAAE,SAAS,cAAc,CAAC,KAAK,EAAE,UAAU,CAAC,EAAE,EACxD,MAAM,EAAE,WAAW,EACnB,QAAQ,EAAE,MAAM,EAChB,WAAW,EAAE,OAAO,EAAE,EACtB,QAAQ,CAAC,EAAE,SAAS,gBAAgB,EAAE,GACrC,gBAAgB,CAAC,KAAK,EAAE,UAAU,CAAC,CAUrC;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,GAC5B,OAAO,CAAC,OAAO,CAAC,CAwBlB;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
@@ -37,7 +37,7 @@ async function i(t, n, r) {
37
37
  async function a(o) {
38
38
  if (o < n.length) {
39
39
  let r = !1;
40
- await n[o](t, async () => {
40
+ await n[o]?.(t, async () => {
41
41
  if (r) throw new e("next() called multiple times");
42
42
  r = !0, await a(o + 1);
43
43
  });
@@ -1 +1 @@
1
- {"version":3,"file":"context.js","names":[],"sources":["../src/context.ts"],"sourcesContent":["import type {\n MatchStatus,\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\nimport { error as logError } from './_dev';\nimport { WayfinderApiError } from './errors';\n\n// ─── Route state ──────────────────────────────────────────────────────────────\n\n/** R5: No defensive deep-copy — RouteState is typed readonly; callers always pass fresh objects. */\nexport function createRouteState<TMeta = unknown, TComponent = unknown>(input: {\n error?: unknown;\n location: RouteLocation;\n matches: RouteMatchBranch<TMeta, TComponent>;\n status: NavigationStatus;\n}): RouteState<TMeta, TComponent> {\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, data results, and optional per-node statuses.\n * F1: Each node carries its own `status` for fine-grained nested layout feedback.\n */\nexport function buildMatchBranch<TMeta = unknown, TComponent = unknown>(\n branchDefs: readonly RouteBranchDef<TMeta, TComponent>[],\n params: RouteParams,\n pathname: string,\n dataResults: unknown[],\n statuses?: readonly MatchStatus[],\n): RouteMatchBranch<TMeta, TComponent> {\n return branchDefs.map((def, i): RouteMatchBranch<TMeta, TComponent>[number] => ({\n component: def.component as TComponent,\n data: dataResults[i],\n meta: def.meta as TMeta,\n name: def.name,\n params: { ...params },\n pathname,\n status: statuses?.[i] ?? 'idle',\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): 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 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\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":";;;AAqBA,SAAgB,EAAwD,GAKtC;CAChC,OAAO;EACL,OAAO,EAAM;EACb,UAAU,EAAM;EAChB,SAAS,EAAM;EACf,QAAQ,EAAM;CAChB;AACF;AAMA,SAAgB,EACd,GACA,GACA,GACA,GACA,GACqC;CACrC,OAAO,EAAW,KAAK,GAAK,OAAoD;EAC9E,WAAW,EAAI;EACf,MAAM,EAAY;EAClB,MAAM,EAAI;EACV,MAAM,EAAI;EACV,QAAQ,EAAE,GAAG,EAAO;EACpB;EACA,QAAQ,IAAW,MAAM;CAC3B,EAAE;AACJ;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,GACkB;CAClB,IAAI,IAAc;CAElB,eAAe,EAAS,GAA8B;EACpD,IAAI,IAAQ,EAAW,QAAQ;GAC7B,IAAI,IAAS;GAEb,MAAM,EAAW,EAAM,CAAE,GAAS,YAAY;IAC5C,IAAI,GAAQ,MAAM,IAAI,EAAkB,8BAA8B;IAGtE,AADA,IAAS,IACT,MAAM,EAAS,IAAQ,CAAC;GAC1B,CAAC;GAED;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"}
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<TMeta = unknown, TComponent = unknown>(input: {\n error?: unknown;\n location: RouteLocation;\n matches: RouteMatchBranch<TMeta, TComponent>;\n status: NavigationStatus;\n}): RouteState<TMeta, TComponent> {\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, data results, and optional per-node statuses.\n * Each node carries its own `status` for fine-grained nested layout feedback.\n */\nexport function buildMatchBranch<TMeta = unknown, TComponent = unknown>(\n branchDefs: readonly RouteBranchDef<TMeta, TComponent>[],\n params: RouteParams,\n pathname: string,\n dataResults: unknown[],\n statuses?: readonly NavigationStatus[],\n): RouteMatchBranch<TMeta, TComponent> {\n return branchDefs.map((def, i): RouteMatchBranch<TMeta, TComponent>[number] => ({\n component: def.component as TComponent,\n data: dataResults[i],\n meta: def.meta as TMeta,\n name: def.name,\n params: { ...params },\n pathname,\n status: statuses?.[i] ?? 'idle',\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): 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 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\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":";;;AAmBA,SAAgB,EAAwD,GAKtC;CAChC,OAAO;EACL,OAAO,EAAM;EACb,UAAU,EAAM;EAChB,SAAS,EAAM;EACf,QAAQ,EAAM;CAChB;AACF;AAMA,SAAgB,EACd,GACA,GACA,GACA,GACA,GACqC;CACrC,OAAO,EAAW,KAAK,GAAK,OAAoD;EAC9E,WAAW,EAAI;EACf,MAAM,EAAY;EAClB,MAAM,EAAI;EACV,MAAM,EAAI;EACV,QAAQ,EAAE,GAAG,EAAO;EACpB;EACA,QAAQ,IAAW,MAAM;CAC3B,EAAE;AACJ;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,GACkB;CAClB,IAAI,IAAc;CAElB,eAAe,EAAS,GAA8B;EACpD,IAAI,IAAQ,EAAW,QAAQ;GAC7B,IAAI,IAAS;GAEb,MAAM,EAAW,EAAM,GAAG,GAAS,YAAY;IAC7C,IAAI,GAAQ,MAAM,IAAI,EAAkB,8BAA8B;IAGtE,AADA,IAAS,IACT,MAAM,EAAS,IAAQ,CAAC;GAC1B,CAAC;GAED;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"}
@@ -1 +1 @@
1
- {"version":3,"file":"devtools.cjs","names":[],"sources":["../src/devtools.ts"],"sourcesContent":["/**\n * @vielzeug/wayfinder — debug utilities for router navigation visualisation.\n *\n * Import from the dedicated sub-path so it is tree-shaken from production bundles:\n * ```ts\n * import { debugRouter } from '@vielzeug/wayfinder/devtools';\n * ```\n */\n\nimport type { Router, RouterOptions, RouteState, RouteTable } from './index';\n\nimport { createRouter } from './router';\n\n/** Options for {@link debugRouter}. Extends {@link RouterOptions} with debug-specific settings. */\nexport interface DebugRouterOptions<\n TRoutes extends RouteTable,\n TMeta = unknown,\n TComponent = unknown,\n> extends RouterOptions<TRoutes, TMeta, TComponent> {\n /**\n * Label used in log prefixes. Defaults to `'nav'`, producing `[wayfinder:nav]`.\n * Useful when running multiple routers (e.g. a main router and a modal router)\n * so their log output can be distinguished:\n * ```ts\n * debugRouter({ label: 'modal', routes });\n * // [wayfinder:modal] loading /confirm\n * ```\n */\n label?: string;\n}\n\n/**\n * Creates a {@link Router} with navigation lifecycle logging pre-wired to `console.debug`.\n *\n * Equivalent to `createRouter(options)` with a `subscribe` listener attached, but imported\n * from a dedicated sub-path so `console.debug` references are tree-shaken from production\n * bundles when this sub-path is not imported.\n *\n * Logs every navigation state change with `[wayfinder:<label>]` prefixes showing the pathname,\n * status (`loading` → `idle` / `error`), and matched route names.\n *\n * @example\n * ```ts\n * import { debugRouter } from '@vielzeug/wayfinder/devtools';\n *\n * const router = debugRouter({ routes });\n * // [wayfinder:nav] loading /users\n * // [wayfinder:nav] idle /users [users]\n *\n * // Multi-router setup:\n * const modal = debugRouter({ label: 'modal', routes: modalRoutes });\n * // [wayfinder:modal] loading /confirm\n * ```\n */\nexport function debugRouter<const TRoutes extends RouteTable, TMeta = unknown, TComponent = unknown>(\n options: DebugRouterOptions<TRoutes, TMeta, TComponent>,\n): Router<TRoutes, TMeta, TComponent> {\n const { label = 'nav', ...routerOptions } = options;\n const router = createRouter<TRoutes, TMeta, TComponent>(routerOptions);\n const prefix = `[wayfinder:${label}]`;\n\n const log = (state: RouteState<TMeta, TComponent>): void => {\n const names = state.matches\n .map((m) => m.name)\n .filter(Boolean)\n .join(', ');\n const suffix = names ? ` [${names}]` : '';\n\n if (state.status === 'error') {\n console.debug(`${prefix} error ${state.location.pathname}${suffix}`, state.error);\n } else {\n console.debug(`${prefix} ${state.status.padEnd(8)} ${state.location.pathname}${suffix}`);\n }\n };\n\n router.subscribe(log);\n\n return router;\n}\n"],"mappings":"mGAsDA,SAAgB,EACd,EACoC,CACpC,GAAM,CAAE,QAAQ,MAAO,GAAG,GAAkB,EACtC,EAAS,EAAA,aAAyC,CAAa,EAC/D,EAAS,cAAc,EAAM,GAkBnC,OAFA,EAAO,UAdM,GAA+C,CAC1D,IAAM,EAAQ,EAAM,QACjB,IAAK,GAAM,EAAE,IAAI,CAAC,CAClB,OAAO,OAAO,CAAC,CACf,KAAK,IAAI,EACN,EAAS,EAAQ,MAAM,EAAM,GAAK,GAEpC,EAAM,SAAW,QACnB,QAAQ,MAAM,GAAG,EAAO,YAAY,EAAM,SAAS,WAAW,IAAU,EAAM,KAAK,EAEnF,QAAQ,MAAM,GAAG,EAAO,GAAG,EAAM,OAAO,OAAO,CAAC,EAAE,GAAG,EAAM,SAAS,WAAW,GAAQ,CAE3F,CAEoB,EAEb,CACT"}
1
+ {"version":3,"file":"devtools.cjs","names":[],"sources":["../src/devtools.ts"],"sourcesContent":["/**\n * @vielzeug/wayfinder — debug utilities for router navigation visualisation.\n *\n * Import from the dedicated sub-path so it is tree-shaken from production bundles:\n * ```ts\n * import { debugRouter } from '@vielzeug/wayfinder/devtools';\n * ```\n */\n\nimport type { Router, RouterOptions, RouteState, RouteTable } from './index';\n\nimport { createRouter } from './router';\n\n/** Options for {@link debugRouter}. Extends {@link RouterOptions} with debug-specific settings. */\nexport interface DebugRouterOptions<TRoutes extends RouteTable, TMeta = unknown, TComponent = unknown>\n extends RouterOptions<TRoutes, TMeta, TComponent> {\n /**\n * Label used in log prefixes. Defaults to `'nav'`, producing `[wayfinder:nav]`.\n * Useful when running multiple routers (e.g. a main router and a modal router)\n * so their log output can be distinguished:\n * ```ts\n * debugRouter({ label: 'modal', routes });\n * // [wayfinder:modal] loading /confirm\n * ```\n */\n label?: string;\n}\n\n/**\n * Creates a {@link Router} with navigation lifecycle logging pre-wired to `console.debug`.\n *\n * Equivalent to `createRouter(options)` with a `subscribe` listener attached, but imported\n * from a dedicated sub-path so `console.debug` references are tree-shaken from production\n * bundles when this sub-path is not imported.\n *\n * Logs every navigation state change with `[wayfinder:<label>]` prefixes showing the pathname,\n * status (`loading` → `idle` / `error`), and matched route names.\n *\n * @example\n * ```ts\n * import { debugRouter } from '@vielzeug/wayfinder/devtools';\n *\n * const router = debugRouter({ routes });\n * // [wayfinder:nav] loading /users\n * // [wayfinder:nav] idle /users [users]\n *\n * // Multi-router setup:\n * const modal = debugRouter({ label: 'modal', routes: modalRoutes });\n * // [wayfinder:modal] loading /confirm\n * ```\n */\nexport function debugRouter<const TRoutes extends RouteTable, TMeta = unknown, TComponent = unknown>(\n options: DebugRouterOptions<TRoutes, TMeta, TComponent>,\n): Router<TRoutes, TMeta, TComponent> {\n const { label = 'nav', ...routerOptions } = options;\n const router = createRouter<TRoutes, TMeta, TComponent>(routerOptions);\n const prefix = `[wayfinder:${label}]`;\n\n const log = (state: RouteState<TMeta, TComponent>): void => {\n const names = state.matches\n .map((m) => m.name)\n .filter(Boolean)\n .join(', ');\n const suffix = names ? ` [${names}]` : '';\n\n if (state.status === 'error') {\n console.debug(`${prefix} error ${state.location.pathname}${suffix}`, state.error);\n } else {\n console.debug(`${prefix} ${state.status.padEnd(8)} ${state.location.pathname}${suffix}`);\n }\n };\n\n router.subscribe(log);\n\n return router;\n}\n"],"mappings":"mGAmDA,SAAgB,EACd,EACoC,CACpC,GAAM,CAAE,QAAQ,MAAO,GAAG,GAAkB,EACtC,EAAS,EAAA,aAAyC,CAAa,EAC/D,EAAS,cAAc,EAAM,GAkBnC,OAFA,EAAO,UAdM,GAA+C,CAC1D,IAAM,EAAQ,EAAM,QACjB,IAAK,GAAM,EAAE,IAAI,CAAC,CAClB,OAAO,OAAO,CAAC,CACf,KAAK,IAAI,EACN,EAAS,EAAQ,MAAM,EAAM,GAAK,GAEpC,EAAM,SAAW,QACnB,QAAQ,MAAM,GAAG,EAAO,YAAY,EAAM,SAAS,WAAW,IAAU,EAAM,KAAK,EAEnF,QAAQ,MAAM,GAAG,EAAO,GAAG,EAAM,OAAO,OAAO,CAAC,EAAE,GAAG,EAAM,SAAS,WAAW,GAAQ,CAE3F,CAEoB,EAEb,CACT"}
@@ -1 +1 @@
1
- {"version":3,"file":"devtools.d.ts","sourceRoot":"","sources":["../src/devtools.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,aAAa,EAAc,UAAU,EAAE,MAAM,SAAS,CAAC;AAI7E,mGAAmG;AACnG,MAAM,WAAW,kBAAkB,CACjC,OAAO,SAAS,UAAU,EAC1B,KAAK,GAAG,OAAO,EACf,UAAU,GAAG,OAAO,CACpB,SAAQ,aAAa,CAAC,OAAO,EAAE,KAAK,EAAE,UAAU,CAAC;IACjD;;;;;;;;OAQG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,WAAW,CAAC,KAAK,CAAC,OAAO,SAAS,UAAU,EAAE,KAAK,GAAG,OAAO,EAAE,UAAU,GAAG,OAAO,EACjG,OAAO,EAAE,kBAAkB,CAAC,OAAO,EAAE,KAAK,EAAE,UAAU,CAAC,GACtD,MAAM,CAAC,OAAO,EAAE,KAAK,EAAE,UAAU,CAAC,CAsBpC"}
1
+ {"version":3,"file":"devtools.d.ts","sourceRoot":"","sources":["../src/devtools.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,aAAa,EAAc,UAAU,EAAE,MAAM,SAAS,CAAC;AAI7E,mGAAmG;AACnG,MAAM,WAAW,kBAAkB,CAAC,OAAO,SAAS,UAAU,EAAE,KAAK,GAAG,OAAO,EAAE,UAAU,GAAG,OAAO,CACnG,SAAQ,aAAa,CAAC,OAAO,EAAE,KAAK,EAAE,UAAU,CAAC;IACjD;;;;;;;;OAQG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,WAAW,CAAC,KAAK,CAAC,OAAO,SAAS,UAAU,EAAE,KAAK,GAAG,OAAO,EAAE,UAAU,GAAG,OAAO,EACjG,OAAO,EAAE,kBAAkB,CAAC,OAAO,EAAE,KAAK,EAAE,UAAU,CAAC,GACtD,MAAM,CAAC,OAAO,EAAE,KAAK,EAAE,UAAU,CAAC,CAsBpC"}
@@ -1 +1 @@
1
- {"version":3,"file":"devtools.js","names":[],"sources":["../src/devtools.ts"],"sourcesContent":["/**\n * @vielzeug/wayfinder — debug utilities for router navigation visualisation.\n *\n * Import from the dedicated sub-path so it is tree-shaken from production bundles:\n * ```ts\n * import { debugRouter } from '@vielzeug/wayfinder/devtools';\n * ```\n */\n\nimport type { Router, RouterOptions, RouteState, RouteTable } from './index';\n\nimport { createRouter } from './router';\n\n/** Options for {@link debugRouter}. Extends {@link RouterOptions} with debug-specific settings. */\nexport interface DebugRouterOptions<\n TRoutes extends RouteTable,\n TMeta = unknown,\n TComponent = unknown,\n> extends RouterOptions<TRoutes, TMeta, TComponent> {\n /**\n * Label used in log prefixes. Defaults to `'nav'`, producing `[wayfinder:nav]`.\n * Useful when running multiple routers (e.g. a main router and a modal router)\n * so their log output can be distinguished:\n * ```ts\n * debugRouter({ label: 'modal', routes });\n * // [wayfinder:modal] loading /confirm\n * ```\n */\n label?: string;\n}\n\n/**\n * Creates a {@link Router} with navigation lifecycle logging pre-wired to `console.debug`.\n *\n * Equivalent to `createRouter(options)` with a `subscribe` listener attached, but imported\n * from a dedicated sub-path so `console.debug` references are tree-shaken from production\n * bundles when this sub-path is not imported.\n *\n * Logs every navigation state change with `[wayfinder:<label>]` prefixes showing the pathname,\n * status (`loading` → `idle` / `error`), and matched route names.\n *\n * @example\n * ```ts\n * import { debugRouter } from '@vielzeug/wayfinder/devtools';\n *\n * const router = debugRouter({ routes });\n * // [wayfinder:nav] loading /users\n * // [wayfinder:nav] idle /users [users]\n *\n * // Multi-router setup:\n * const modal = debugRouter({ label: 'modal', routes: modalRoutes });\n * // [wayfinder:modal] loading /confirm\n * ```\n */\nexport function debugRouter<const TRoutes extends RouteTable, TMeta = unknown, TComponent = unknown>(\n options: DebugRouterOptions<TRoutes, TMeta, TComponent>,\n): Router<TRoutes, TMeta, TComponent> {\n const { label = 'nav', ...routerOptions } = options;\n const router = createRouter<TRoutes, TMeta, TComponent>(routerOptions);\n const prefix = `[wayfinder:${label}]`;\n\n const log = (state: RouteState<TMeta, TComponent>): void => {\n const names = state.matches\n .map((m) => m.name)\n .filter(Boolean)\n .join(', ');\n const suffix = names ? ` [${names}]` : '';\n\n if (state.status === 'error') {\n console.debug(`${prefix} error ${state.location.pathname}${suffix}`, state.error);\n } else {\n console.debug(`${prefix} ${state.status.padEnd(8)} ${state.location.pathname}${suffix}`);\n }\n };\n\n router.subscribe(log);\n\n return router;\n}\n"],"mappings":";;AAsDA,SAAgB,EACd,GACoC;CACpC,IAAM,EAAE,WAAQ,OAAO,GAAG,MAAkB,GACtC,IAAS,EAAyC,CAAa,GAC/D,IAAS,cAAc,EAAM;CAkBnC,OAFA,EAAO,WAdM,MAA+C;EAC1D,IAAM,IAAQ,EAAM,QACjB,KAAK,MAAM,EAAE,IAAI,CAAC,CAClB,OAAO,OAAO,CAAC,CACf,KAAK,IAAI,GACN,IAAS,IAAQ,MAAM,EAAM,KAAK;EAExC,AAAI,EAAM,WAAW,UACnB,QAAQ,MAAM,GAAG,EAAO,YAAY,EAAM,SAAS,WAAW,KAAU,EAAM,KAAK,IAEnF,QAAQ,MAAM,GAAG,EAAO,GAAG,EAAM,OAAO,OAAO,CAAC,EAAE,GAAG,EAAM,SAAS,WAAW,GAAQ;CAE3F,CAEoB,GAEb;AACT"}
1
+ {"version":3,"file":"devtools.js","names":[],"sources":["../src/devtools.ts"],"sourcesContent":["/**\n * @vielzeug/wayfinder — debug utilities for router navigation visualisation.\n *\n * Import from the dedicated sub-path so it is tree-shaken from production bundles:\n * ```ts\n * import { debugRouter } from '@vielzeug/wayfinder/devtools';\n * ```\n */\n\nimport type { Router, RouterOptions, RouteState, RouteTable } from './index';\n\nimport { createRouter } from './router';\n\n/** Options for {@link debugRouter}. Extends {@link RouterOptions} with debug-specific settings. */\nexport interface DebugRouterOptions<TRoutes extends RouteTable, TMeta = unknown, TComponent = unknown>\n extends RouterOptions<TRoutes, TMeta, TComponent> {\n /**\n * Label used in log prefixes. Defaults to `'nav'`, producing `[wayfinder:nav]`.\n * Useful when running multiple routers (e.g. a main router and a modal router)\n * so their log output can be distinguished:\n * ```ts\n * debugRouter({ label: 'modal', routes });\n * // [wayfinder:modal] loading /confirm\n * ```\n */\n label?: string;\n}\n\n/**\n * Creates a {@link Router} with navigation lifecycle logging pre-wired to `console.debug`.\n *\n * Equivalent to `createRouter(options)` with a `subscribe` listener attached, but imported\n * from a dedicated sub-path so `console.debug` references are tree-shaken from production\n * bundles when this sub-path is not imported.\n *\n * Logs every navigation state change with `[wayfinder:<label>]` prefixes showing the pathname,\n * status (`loading` → `idle` / `error`), and matched route names.\n *\n * @example\n * ```ts\n * import { debugRouter } from '@vielzeug/wayfinder/devtools';\n *\n * const router = debugRouter({ routes });\n * // [wayfinder:nav] loading /users\n * // [wayfinder:nav] idle /users [users]\n *\n * // Multi-router setup:\n * const modal = debugRouter({ label: 'modal', routes: modalRoutes });\n * // [wayfinder:modal] loading /confirm\n * ```\n */\nexport function debugRouter<const TRoutes extends RouteTable, TMeta = unknown, TComponent = unknown>(\n options: DebugRouterOptions<TRoutes, TMeta, TComponent>,\n): Router<TRoutes, TMeta, TComponent> {\n const { label = 'nav', ...routerOptions } = options;\n const router = createRouter<TRoutes, TMeta, TComponent>(routerOptions);\n const prefix = `[wayfinder:${label}]`;\n\n const log = (state: RouteState<TMeta, TComponent>): void => {\n const names = state.matches\n .map((m) => m.name)\n .filter(Boolean)\n .join(', ');\n const suffix = names ? ` [${names}]` : '';\n\n if (state.status === 'error') {\n console.debug(`${prefix} error ${state.location.pathname}${suffix}`, state.error);\n } else {\n console.debug(`${prefix} ${state.status.padEnd(8)} ${state.location.pathname}${suffix}`);\n }\n };\n\n router.subscribe(log);\n\n return router;\n}\n"],"mappings":";;AAmDA,SAAgB,EACd,GACoC;CACpC,IAAM,EAAE,WAAQ,OAAO,GAAG,MAAkB,GACtC,IAAS,EAAyC,CAAa,GAC/D,IAAS,cAAc,EAAM;CAkBnC,OAFA,EAAO,WAdM,MAA+C;EAC1D,IAAM,IAAQ,EAAM,QACjB,KAAK,MAAM,EAAE,IAAI,CAAC,CAClB,OAAO,OAAO,CAAC,CACf,KAAK,IAAI,GACN,IAAS,IAAQ,MAAM,EAAM,KAAK;EAExC,AAAI,EAAM,WAAW,UACnB,QAAQ,MAAM,GAAG,EAAO,YAAY,EAAM,SAAS,WAAW,KAAU,EAAM,KAAK,IAEnF,QAAQ,MAAM,GAAG,EAAO,GAAG,EAAM,OAAO,OAAO,CAAC,EAAE,GAAG,EAAM,SAAS,WAAW,GAAQ;CAE3F,CAEoB,GAEb;AACT"}
package/dist/errors.cjs CHANGED
@@ -1,2 +1,2 @@
1
- var e=class e extends Error{constructor(e,t){super(e,t),this.name=new.target.name,Object.setPrototypeOf(this,new.target.prototype)}static is(t){return t instanceof e}},t=class extends e{constructor(){super(`Router is disposed`)}},n=class extends e{},r=class extends e{constructor(){super(`Redirect loop detected`)}},i=class extends e{};exports.WayfinderApiError=i,exports.WayfinderDisposedError=t,exports.WayfinderError=e,exports.WayfinderRedirectLoopError=r,exports.WayfinderRouteError=n;
1
+ var e=class extends Error{constructor(e,t){super(e,t),this.name=new.target.name,Object.setPrototypeOf(this,new.target.prototype)}},t=class extends e{constructor(){super(`Router is disposed`)}},n=class extends e{},r=class extends e{constructor(){super(`Redirect loop detected`)}},i=class extends e{};exports.WayfinderApiError=i,exports.WayfinderDisposedError=t,exports.WayfinderError=e,exports.WayfinderRedirectLoopError=r,exports.WayfinderRouteError=n;
2
2
  //# sourceMappingURL=errors.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"errors.cjs","names":[],"sources":["../src/errors.ts"],"sourcesContent":["/** Base class for all wayfinder errors. Use `instanceof WayfinderError` to catch any router-originated error. */\nexport class WayfinderError extends Error {\n constructor(message: string, opts?: ErrorOptions) {\n super(message, opts);\n this.name = new.target.name;\n Object.setPrototypeOf(this, new.target.prototype);\n }\n\n static is(err: unknown): err is WayfinderError {\n return err instanceof WayfinderError;\n }\n}\n\n/** Thrown when any router method is called after the router has been disposed. */\nexport class WayfinderDisposedError extends WayfinderError {\n constructor() {\n super('Router is disposed');\n }\n}\n\n/** Thrown when a route or path definition is malformed. */\nexport class WayfinderRouteError extends WayfinderError {}\n\n/** Thrown when the router detects an infinite redirect loop. */\nexport class WayfinderRedirectLoopError extends WayfinderError {\n constructor() {\n super('Redirect loop detected');\n }\n}\n\n/** Thrown on API misuse (e.g. calling next() multiple times). */\nexport class WayfinderApiError extends WayfinderError {}\n"],"mappings":"AACA,IAAa,EAAb,MAAa,UAAuB,KAAM,CACxC,YAAY,EAAiB,EAAqB,CAChD,MAAM,EAAS,CAAI,EACnB,KAAK,KAAO,WAAW,KACvB,OAAO,eAAe,KAAM,WAAW,SAAS,CAClD,CAEA,OAAO,GAAG,EAAqC,CAC7C,OAAO,aAAe,CACxB,CACF,EAGa,EAAb,cAA4C,CAAe,CACzD,aAAc,CACZ,MAAM,oBAAoB,CAC5B,CACF,EAGa,EAAb,cAAyC,CAAe,CAAC,EAG5C,EAAb,cAAgD,CAAe,CAC7D,aAAc,CACZ,MAAM,wBAAwB,CAChC,CACF,EAGa,EAAb,cAAuC,CAAe,CAAC"}
1
+ {"version":3,"file":"errors.cjs","names":[],"sources":["../src/errors.ts"],"sourcesContent":["/** Base class for all wayfinder errors. Use `instanceof WayfinderError` to catch any router-originated error. */\nexport class WayfinderError extends Error {\n constructor(message: string, opts?: ErrorOptions) {\n super(message, opts);\n this.name = new.target.name;\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\n/** Thrown when any router method is called after the router has been disposed. */\nexport class WayfinderDisposedError extends WayfinderError {\n constructor() {\n super('Router is disposed');\n }\n}\n\n/** Thrown when a route or path definition is malformed. */\nexport class WayfinderRouteError extends WayfinderError {}\n\n/** Thrown when the router detects an infinite redirect loop. */\nexport class WayfinderRedirectLoopError extends WayfinderError {\n constructor() {\n super('Redirect loop detected');\n }\n}\n\n/** Thrown on API misuse (e.g. calling next() multiple times). */\nexport class WayfinderApiError extends WayfinderError {}\n"],"mappings":"AACA,IAAa,EAAb,cAAoC,KAAM,CACxC,YAAY,EAAiB,EAAqB,CAChD,MAAM,EAAS,CAAI,EACnB,KAAK,KAAO,WAAW,KACvB,OAAO,eAAe,KAAM,WAAW,SAAS,CAClD,CACF,EAGa,EAAb,cAA4C,CAAe,CACzD,aAAc,CACZ,MAAM,oBAAoB,CAC5B,CACF,EAGa,EAAb,cAAyC,CAAe,CAAC,EAG5C,EAAb,cAAgD,CAAe,CAC7D,aAAc,CACZ,MAAM,wBAAwB,CAChC,CACF,EAGa,EAAb,cAAuC,CAAe,CAAC"}
package/dist/errors.d.ts CHANGED
@@ -1,7 +1,6 @@
1
1
  /** Base class for all wayfinder errors. Use `instanceof WayfinderError` to catch any router-originated error. */
2
2
  export declare class WayfinderError extends Error {
3
3
  constructor(message: string, opts?: ErrorOptions);
4
- static is(err: unknown): err is WayfinderError;
5
4
  }
6
5
  /** Thrown when any router method is called after the router has been disposed. */
7
6
  export declare class WayfinderDisposedError extends WayfinderError {
@@ -1 +1 @@
1
- {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,iHAAiH;AACjH,qBAAa,cAAe,SAAQ,KAAK;gBAC3B,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,YAAY;IAMhD,MAAM,CAAC,EAAE,CAAC,GAAG,EAAE,OAAO,GAAG,GAAG,IAAI,cAAc;CAG/C;AAED,kFAAkF;AAClF,qBAAa,sBAAuB,SAAQ,cAAc;;CAIzD;AAED,2DAA2D;AAC3D,qBAAa,mBAAoB,SAAQ,cAAc;CAAG;AAE1D,gEAAgE;AAChE,qBAAa,0BAA2B,SAAQ,cAAc;;CAI7D;AAED,iEAAiE;AACjE,qBAAa,iBAAkB,SAAQ,cAAc;CAAG"}
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,iHAAiH;AACjH,qBAAa,cAAe,SAAQ,KAAK;gBAC3B,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,YAAY;CAKjD;AAED,kFAAkF;AAClF,qBAAa,sBAAuB,SAAQ,cAAc;;CAIzD;AAED,2DAA2D;AAC3D,qBAAa,mBAAoB,SAAQ,cAAc;CAAG;AAE1D,gEAAgE;AAChE,qBAAa,0BAA2B,SAAQ,cAAc;;CAI7D;AAED,iEAAiE;AACjE,qBAAa,iBAAkB,SAAQ,cAAc;CAAG"}
package/dist/errors.js CHANGED
@@ -1,11 +1,8 @@
1
1
  //#region src/errors.ts
2
- var e = class e extends Error {
2
+ var e = class extends Error {
3
3
  constructor(e, t) {
4
4
  super(e, t), this.name = new.target.name, Object.setPrototypeOf(this, new.target.prototype);
5
5
  }
6
- static is(t) {
7
- return t instanceof e;
8
- }
9
6
  }, t = class extends e {
10
7
  constructor() {
11
8
  super("Router is disposed");
@@ -1 +1 @@
1
- {"version":3,"file":"errors.js","names":[],"sources":["../src/errors.ts"],"sourcesContent":["/** Base class for all wayfinder errors. Use `instanceof WayfinderError` to catch any router-originated error. */\nexport class WayfinderError extends Error {\n constructor(message: string, opts?: ErrorOptions) {\n super(message, opts);\n this.name = new.target.name;\n Object.setPrototypeOf(this, new.target.prototype);\n }\n\n static is(err: unknown): err is WayfinderError {\n return err instanceof WayfinderError;\n }\n}\n\n/** Thrown when any router method is called after the router has been disposed. */\nexport class WayfinderDisposedError extends WayfinderError {\n constructor() {\n super('Router is disposed');\n }\n}\n\n/** Thrown when a route or path definition is malformed. */\nexport class WayfinderRouteError extends WayfinderError {}\n\n/** Thrown when the router detects an infinite redirect loop. */\nexport class WayfinderRedirectLoopError extends WayfinderError {\n constructor() {\n super('Redirect loop detected');\n }\n}\n\n/** Thrown on API misuse (e.g. calling next() multiple times). */\nexport class WayfinderApiError extends WayfinderError {}\n"],"mappings":";AACA,IAAa,IAAb,MAAa,UAAuB,MAAM;CACxC,YAAY,GAAiB,GAAqB;EAGhD,AAFA,MAAM,GAAS,CAAI,GACnB,KAAK,OAAO,WAAW,MACvB,OAAO,eAAe,MAAM,WAAW,SAAS;CAClD;CAEA,OAAO,GAAG,GAAqC;EAC7C,OAAO,aAAe;CACxB;AACF,GAGa,IAAb,cAA4C,EAAe;CACzD,cAAc;EACZ,MAAM,oBAAoB;CAC5B;AACF,GAGa,IAAb,cAAyC,EAAe,CAAC,GAG5C,IAAb,cAAgD,EAAe;CAC7D,cAAc;EACZ,MAAM,wBAAwB;CAChC;AACF,GAGa,IAAb,cAAuC,EAAe,CAAC"}
1
+ {"version":3,"file":"errors.js","names":[],"sources":["../src/errors.ts"],"sourcesContent":["/** Base class for all wayfinder errors. Use `instanceof WayfinderError` to catch any router-originated error. */\nexport class WayfinderError extends Error {\n constructor(message: string, opts?: ErrorOptions) {\n super(message, opts);\n this.name = new.target.name;\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\n/** Thrown when any router method is called after the router has been disposed. */\nexport class WayfinderDisposedError extends WayfinderError {\n constructor() {\n super('Router is disposed');\n }\n}\n\n/** Thrown when a route or path definition is malformed. */\nexport class WayfinderRouteError extends WayfinderError {}\n\n/** Thrown when the router detects an infinite redirect loop. */\nexport class WayfinderRedirectLoopError extends WayfinderError {\n constructor() {\n super('Redirect loop detected');\n }\n}\n\n/** Thrown on API misuse (e.g. calling next() multiple times). */\nexport class WayfinderApiError extends WayfinderError {}\n"],"mappings":";AACA,IAAa,IAAb,cAAoC,MAAM;CACxC,YAAY,GAAiB,GAAqB;EAGhD,AAFA,MAAM,GAAS,CAAI,GACnB,KAAK,OAAO,WAAW,MACvB,OAAO,eAAe,MAAM,WAAW,SAAS;CAClD;AACF,GAGa,IAAb,cAA4C,EAAe;CACzD,cAAc;EACZ,MAAM,oBAAoB;CAC5B;AACF,GAGa,IAAb,cAAyC,EAAe,CAAC,GAG5C,IAAb,cAAgD,EAAe;CAC7D,cAAc;EACZ,MAAM,wBAAwB;CAChC;AACF,GAGa,IAAb,cAAuC,EAAe,CAAC"}
@@ -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 * R4: `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,SAAW,IAAA,IAAa,CAAC,EAAO,KAAM,GAAS,EAAY,IAAI,CAAI,CAAC,IAIpE,CAAC,MAFiB,EAAQ,CAAW,EAE3B,MAAO,GAGvB,MAAO,EACT"}
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,SAAW,IAAA,IAAa,CAAC,EAAO,KAAM,GAAS,EAAY,IAAI,CAAI,CAAC,IAIpE,CAAC,MAFiB,EAAQ,CAAW,EAE3B,MAAO,GAGvB,MAAO,EACT"}
package/dist/guards.d.ts CHANGED
@@ -16,7 +16,7 @@ export type RegisteredBlocker = {
16
16
  * A snapshot of the blocker set is taken before iteration so that guards added or removed
17
17
  * during execution do not affect the current run.
18
18
  *
19
- * R4: `destination` is passed to each blocker so guards can make context-aware decisions.
19
+ * `destination` is passed to each blocker so guards can make context-aware decisions.
20
20
  */
21
21
  export declare function runLeaveBlockers(blockers: ReadonlySet<RegisteredBlocker>, activeMatchNames: readonly string[], destination: NavigationDestination): Promise<boolean>;
22
22
  //# sourceMappingURL=guards.d.ts.map
@@ -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 * R4: `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,YAAW,KAAA,KAAa,CAAC,EAAO,MAAM,MAAS,EAAY,IAAI,CAAI,CAAC,MAIpE,CAAC,MAFiB,EAAQ,CAAW,GAE3B,OAAO;CAGvB,OAAO;AACT"}
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,YAAW,KAAA,KAAa,CAAC,EAAO,MAAM,MAAS,EAAY,IAAI,CAAI,CAAC,MAIpE,CAAC,MAFiB,EAAQ,CAAW,GAE3B,OAAO;CAGvB,OAAO;AACT"}
package/dist/history.cjs CHANGED
@@ -1,2 +1,2 @@
1
- function e(){return{back(){window.history.back()},get location(){return{hash:window.location.hash,pathname:window.location.pathname,search:window.location.search,state:window.history.state}},onPopstate(e){return window.addEventListener(`popstate`,e),()=>window.removeEventListener(`popstate`,e)},push(e,t){window.history.pushState(t,``,e)},replace(e,t){window.history.replaceState(t,``,e)}}}function t(e=`/`){let t=new URL(e,`http://localhost`),n=[{hash:t.hash,pathname:t.pathname,search:t.search,state:null}],r=0,i=new Set;return{back(){r<=0||(--r,i.forEach(e=>e()))},get location(){return n[r]},onPopstate(e){return i.add(e),()=>i.delete(e)},push(e,t=null){let i=new URL(e,`http://localhost`);n.splice(r+1),n.push({hash:i.hash,pathname:i.pathname,search:i.search,state:t}),r=n.length-1},replace(e,t=null){let i=new URL(e,`http://localhost`);n[r]={hash:i.hash,pathname:i.pathname,search:i.search,state:t}}}}exports.createBrowserHistory=e,exports.createMemoryHistory=t;
1
+ function e(){return{back(){window.history.back()},get location(){return{hash:window.location.hash,pathname:window.location.pathname,search:window.location.search,state:window.history.state}},onPopstate(e){return window.addEventListener(`popstate`,e),()=>window.removeEventListener(`popstate`,e)},push(e,t){window.history.pushState(t,``,e)},replace(e,t){window.history.replaceState(t,``,e)}}}function t(e=`/`){let t=new URL(e,`http://localhost`),n=[{hash:t.hash,pathname:t.pathname,search:t.search,state:null}],r=0,i=new Set;return{back(){r<=0||(--r,i.forEach(e=>{e()}))},get location(){return n[r]},onPopstate(e){return i.add(e),()=>i.delete(e)},push(e,t=null){let i=new URL(e,`http://localhost`);n.splice(r+1),n.push({hash:i.hash,pathname:i.pathname,search:i.search,state:t}),r=n.length-1},replace(e,t=null){let i=new URL(e,`http://localhost`);n[r]={hash:i.hash,pathname:i.pathname,search:i.search,state:t}}}}exports.createBrowserHistory=e,exports.createMemoryHistory=t;
2
2
  //# sourceMappingURL=history.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"history.cjs","names":[],"sources":["../src/history.ts"],"sourcesContent":["import type { HistoryDriver } from './types';\n\n/** Creates a history driver backed by the browser History API. */\nexport function createBrowserHistory(): HistoryDriver {\n return {\n back() {\n window.history.back();\n },\n get location() {\n return {\n hash: window.location.hash,\n pathname: window.location.pathname,\n search: window.location.search,\n // State lives on window.history.state, not window.location.\n state: window.history.state,\n };\n },\n onPopstate(listener) {\n window.addEventListener('popstate', listener);\n\n return () => window.removeEventListener('popstate', listener);\n },\n push(url, state) {\n window.history.pushState(state, '', url);\n },\n replace(url, state) {\n window.history.replaceState(state, '', url);\n },\n };\n}\n\ntype MemoryLocation = { hash: string; pathname: string; search: string; state: unknown };\n\n/**\n * Creates an in-memory history driver. Suitable for SSR, tests, and non-browser environments.\n *\n * Semantics mirror the browser History API:\n * - `push()` and `replace()` update the location silently (no listener notification),\n * matching `pushState`/`replaceState` which do not fire `popstate`.\n * - `back()` moves one entry back and notifies subscribers, matching a browser back-button press.\n */\nexport function createMemoryHistory(initialPath = '/'): HistoryDriver {\n const parsed = new URL(initialPath, 'http://localhost');\n const stack: MemoryLocation[] = [\n { hash: parsed.hash, pathname: parsed.pathname, search: parsed.search, state: null },\n ];\n let cursor = 0;\n const listeners = new Set<() => void>();\n\n return {\n back() {\n if (cursor <= 0) return;\n\n cursor -= 1;\n listeners.forEach((l) => l());\n },\n get location(): MemoryLocation {\n return stack[cursor]!;\n },\n onPopstate(listener) {\n listeners.add(listener);\n\n return () => listeners.delete(listener);\n },\n push(url, state = null) {\n const p = new URL(url, 'http://localhost');\n\n stack.splice(cursor + 1);\n stack.push({ hash: p.hash, pathname: p.pathname, search: p.search, state });\n cursor = stack.length - 1;\n // Silent — mirrors browser pushState semantics. Router drives navigation via #handleRoute directly.\n },\n replace(url, state = null) {\n const p = new URL(url, 'http://localhost');\n\n stack[cursor] = { hash: p.hash, pathname: p.pathname, search: p.search, state };\n // Silent — mirrors browser replaceState semantics.\n },\n };\n}\n"],"mappings":"AAGA,SAAgB,GAAsC,CACpD,MAAO,CACL,MAAO,CACL,OAAO,QAAQ,KAAK,CACtB,EACA,IAAI,UAAW,CACb,MAAO,CACL,KAAM,OAAO,SAAS,KACtB,SAAU,OAAO,SAAS,SAC1B,OAAQ,OAAO,SAAS,OAExB,MAAO,OAAO,QAAQ,KACxB,CACF,EACA,WAAW,EAAU,CAGnB,OAFA,OAAO,iBAAiB,WAAY,CAAQ,MAE/B,OAAO,oBAAoB,WAAY,CAAQ,CAC9D,EACA,KAAK,EAAK,EAAO,CACf,OAAO,QAAQ,UAAU,EAAO,GAAI,CAAG,CACzC,EACA,QAAQ,EAAK,EAAO,CAClB,OAAO,QAAQ,aAAa,EAAO,GAAI,CAAG,CAC5C,CACF,CACF,CAYA,SAAgB,EAAoB,EAAc,IAAoB,CACpE,IAAM,EAAS,IAAI,IAAI,EAAa,kBAAkB,EAChD,EAA0B,CAC9B,CAAE,KAAM,EAAO,KAAM,SAAU,EAAO,SAAU,OAAQ,EAAO,OAAQ,MAAO,IAAK,CACrF,EACI,EAAS,EACP,EAAY,IAAI,IAEtB,MAAO,CACL,MAAO,CACD,GAAU,IAEd,IACA,EAAU,QAAS,GAAM,EAAE,CAAC,EAC9B,EACA,IAAI,UAA2B,CAC7B,OAAO,EAAM,EACf,EACA,WAAW,EAAU,CAGnB,OAFA,EAAU,IAAI,CAAQ,MAET,EAAU,OAAO,CAAQ,CACxC,EACA,KAAK,EAAK,EAAQ,KAAM,CACtB,IAAM,EAAI,IAAI,IAAI,EAAK,kBAAkB,EAEzC,EAAM,OAAO,EAAS,CAAC,EACvB,EAAM,KAAK,CAAE,KAAM,EAAE,KAAM,SAAU,EAAE,SAAU,OAAQ,EAAE,OAAQ,OAAM,CAAC,EAC1E,EAAS,EAAM,OAAS,CAE1B,EACA,QAAQ,EAAK,EAAQ,KAAM,CACzB,IAAM,EAAI,IAAI,IAAI,EAAK,kBAAkB,EAEzC,EAAM,GAAU,CAAE,KAAM,EAAE,KAAM,SAAU,EAAE,SAAU,OAAQ,EAAE,OAAQ,OAAM,CAEhF,CACF,CACF"}
1
+ {"version":3,"file":"history.cjs","names":[],"sources":["../src/history.ts"],"sourcesContent":["import type { HistoryDriver } from './types';\n\n/** Creates a history driver backed by the browser History API. */\nexport function createBrowserHistory(): HistoryDriver {\n return {\n back() {\n window.history.back();\n },\n get location() {\n return {\n hash: window.location.hash,\n pathname: window.location.pathname,\n search: window.location.search,\n // State lives on window.history.state, not window.location.\n state: window.history.state,\n };\n },\n onPopstate(listener) {\n window.addEventListener('popstate', listener);\n\n return () => window.removeEventListener('popstate', listener);\n },\n push(url, state) {\n window.history.pushState(state, '', url);\n },\n replace(url, state) {\n window.history.replaceState(state, '', url);\n },\n };\n}\n\ntype MemoryLocation = { hash: string; pathname: string; search: string; state: unknown };\n\n/**\n * Creates an in-memory history driver. Suitable for SSR, tests, and non-browser environments.\n *\n * Semantics mirror the browser History API:\n * - `push()` and `replace()` update the location silently (no listener notification),\n * matching `pushState`/`replaceState` which do not fire `popstate`.\n * - `back()` moves one entry back and notifies subscribers, matching a browser back-button press.\n */\nexport function createMemoryHistory(initialPath = '/'): HistoryDriver {\n const parsed = new URL(initialPath, 'http://localhost');\n const stack: MemoryLocation[] = [\n { hash: parsed.hash, pathname: parsed.pathname, search: parsed.search, state: null },\n ];\n let cursor = 0;\n const listeners = new Set<() => void>();\n\n return {\n back() {\n if (cursor <= 0) return;\n\n cursor -= 1;\n listeners.forEach((l) => {\n l();\n });\n },\n get location(): MemoryLocation {\n return stack[cursor]!;\n },\n onPopstate(listener) {\n listeners.add(listener);\n\n return () => listeners.delete(listener);\n },\n push(url, state = null) {\n const p = new URL(url, 'http://localhost');\n\n stack.splice(cursor + 1);\n stack.push({ hash: p.hash, pathname: p.pathname, search: p.search, state });\n cursor = stack.length - 1;\n // Silent — mirrors browser pushState semantics. Router drives navigation via #handleRoute directly.\n },\n replace(url, state = null) {\n const p = new URL(url, 'http://localhost');\n\n stack[cursor] = { hash: p.hash, pathname: p.pathname, search: p.search, state };\n // Silent — mirrors browser replaceState semantics.\n },\n };\n}\n"],"mappings":"AAGA,SAAgB,GAAsC,CACpD,MAAO,CACL,MAAO,CACL,OAAO,QAAQ,KAAK,CACtB,EACA,IAAI,UAAW,CACb,MAAO,CACL,KAAM,OAAO,SAAS,KACtB,SAAU,OAAO,SAAS,SAC1B,OAAQ,OAAO,SAAS,OAExB,MAAO,OAAO,QAAQ,KACxB,CACF,EACA,WAAW,EAAU,CAGnB,OAFA,OAAO,iBAAiB,WAAY,CAAQ,MAE/B,OAAO,oBAAoB,WAAY,CAAQ,CAC9D,EACA,KAAK,EAAK,EAAO,CACf,OAAO,QAAQ,UAAU,EAAO,GAAI,CAAG,CACzC,EACA,QAAQ,EAAK,EAAO,CAClB,OAAO,QAAQ,aAAa,EAAO,GAAI,CAAG,CAC5C,CACF,CACF,CAYA,SAAgB,EAAoB,EAAc,IAAoB,CACpE,IAAM,EAAS,IAAI,IAAI,EAAa,kBAAkB,EAChD,EAA0B,CAC9B,CAAE,KAAM,EAAO,KAAM,SAAU,EAAO,SAAU,OAAQ,EAAO,OAAQ,MAAO,IAAK,CACrF,EACI,EAAS,EACP,EAAY,IAAI,IAEtB,MAAO,CACL,MAAO,CACD,GAAU,IAEd,IACA,EAAU,QAAS,GAAM,CACvB,EAAE,CACJ,CAAC,EACH,EACA,IAAI,UAA2B,CAC7B,OAAO,EAAM,EACf,EACA,WAAW,EAAU,CAGnB,OAFA,EAAU,IAAI,CAAQ,MAET,EAAU,OAAO,CAAQ,CACxC,EACA,KAAK,EAAK,EAAQ,KAAM,CACtB,IAAM,EAAI,IAAI,IAAI,EAAK,kBAAkB,EAEzC,EAAM,OAAO,EAAS,CAAC,EACvB,EAAM,KAAK,CAAE,KAAM,EAAE,KAAM,SAAU,EAAE,SAAU,OAAQ,EAAE,OAAQ,OAAM,CAAC,EAC1E,EAAS,EAAM,OAAS,CAE1B,EACA,QAAQ,EAAK,EAAQ,KAAM,CACzB,IAAM,EAAI,IAAI,IAAI,EAAK,kBAAkB,EAEzC,EAAM,GAAU,CAAE,KAAM,EAAE,KAAM,SAAU,EAAE,SAAU,OAAQ,EAAE,OAAQ,OAAM,CAEhF,CACF,CACF"}
@@ -1 +1 @@
1
- {"version":3,"file":"history.d.ts","sourceRoot":"","sources":["../src/history.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAE7C,kEAAkE;AAClE,wBAAgB,oBAAoB,IAAI,aAAa,CA0BpD;AAID;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CAAC,WAAW,SAAM,GAAG,aAAa,CAsCpE"}
1
+ {"version":3,"file":"history.d.ts","sourceRoot":"","sources":["../src/history.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAE7C,kEAAkE;AAClE,wBAAgB,oBAAoB,IAAI,aAAa,CA0BpD;AAID;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CAAC,WAAW,SAAM,GAAG,aAAa,CAwCpE"}
package/dist/history.js CHANGED
@@ -32,7 +32,9 @@ function t(e = "/") {
32
32
  }], r = 0, i = /* @__PURE__ */ new Set();
33
33
  return {
34
34
  back() {
35
- r <= 0 || (--r, i.forEach((e) => e()));
35
+ r <= 0 || (--r, i.forEach((e) => {
36
+ e();
37
+ }));
36
38
  },
37
39
  get location() {
38
40
  return n[r];
@@ -1 +1 @@
1
- {"version":3,"file":"history.js","names":[],"sources":["../src/history.ts"],"sourcesContent":["import type { HistoryDriver } from './types';\n\n/** Creates a history driver backed by the browser History API. */\nexport function createBrowserHistory(): HistoryDriver {\n return {\n back() {\n window.history.back();\n },\n get location() {\n return {\n hash: window.location.hash,\n pathname: window.location.pathname,\n search: window.location.search,\n // State lives on window.history.state, not window.location.\n state: window.history.state,\n };\n },\n onPopstate(listener) {\n window.addEventListener('popstate', listener);\n\n return () => window.removeEventListener('popstate', listener);\n },\n push(url, state) {\n window.history.pushState(state, '', url);\n },\n replace(url, state) {\n window.history.replaceState(state, '', url);\n },\n };\n}\n\ntype MemoryLocation = { hash: string; pathname: string; search: string; state: unknown };\n\n/**\n * Creates an in-memory history driver. Suitable for SSR, tests, and non-browser environments.\n *\n * Semantics mirror the browser History API:\n * - `push()` and `replace()` update the location silently (no listener notification),\n * matching `pushState`/`replaceState` which do not fire `popstate`.\n * - `back()` moves one entry back and notifies subscribers, matching a browser back-button press.\n */\nexport function createMemoryHistory(initialPath = '/'): HistoryDriver {\n const parsed = new URL(initialPath, 'http://localhost');\n const stack: MemoryLocation[] = [\n { hash: parsed.hash, pathname: parsed.pathname, search: parsed.search, state: null },\n ];\n let cursor = 0;\n const listeners = new Set<() => void>();\n\n return {\n back() {\n if (cursor <= 0) return;\n\n cursor -= 1;\n listeners.forEach((l) => l());\n },\n get location(): MemoryLocation {\n return stack[cursor]!;\n },\n onPopstate(listener) {\n listeners.add(listener);\n\n return () => listeners.delete(listener);\n },\n push(url, state = null) {\n const p = new URL(url, 'http://localhost');\n\n stack.splice(cursor + 1);\n stack.push({ hash: p.hash, pathname: p.pathname, search: p.search, state });\n cursor = stack.length - 1;\n // Silent — mirrors browser pushState semantics. Router drives navigation via #handleRoute directly.\n },\n replace(url, state = null) {\n const p = new URL(url, 'http://localhost');\n\n stack[cursor] = { hash: p.hash, pathname: p.pathname, search: p.search, state };\n // Silent — mirrors browser replaceState semantics.\n },\n };\n}\n"],"mappings":";AAGA,SAAgB,IAAsC;CACpD,OAAO;EACL,OAAO;GACL,OAAO,QAAQ,KAAK;EACtB;EACA,IAAI,WAAW;GACb,OAAO;IACL,MAAM,OAAO,SAAS;IACtB,UAAU,OAAO,SAAS;IAC1B,QAAQ,OAAO,SAAS;IAExB,OAAO,OAAO,QAAQ;GACxB;EACF;EACA,WAAW,GAAU;GAGnB,OAFA,OAAO,iBAAiB,YAAY,CAAQ,SAE/B,OAAO,oBAAoB,YAAY,CAAQ;EAC9D;EACA,KAAK,GAAK,GAAO;GACf,OAAO,QAAQ,UAAU,GAAO,IAAI,CAAG;EACzC;EACA,QAAQ,GAAK,GAAO;GAClB,OAAO,QAAQ,aAAa,GAAO,IAAI,CAAG;EAC5C;CACF;AACF;AAYA,SAAgB,EAAoB,IAAc,KAAoB;CACpE,IAAM,IAAS,IAAI,IAAI,GAAa,kBAAkB,GAChD,IAA0B,CAC9B;EAAE,MAAM,EAAO;EAAM,UAAU,EAAO;EAAU,QAAQ,EAAO;EAAQ,OAAO;CAAK,CACrF,GACI,IAAS,GACP,oBAAY,IAAI,IAAgB;CAEtC,OAAO;EACL,OAAO;GACD,KAAU,MAEd,KACA,EAAU,SAAS,MAAM,EAAE,CAAC;EAC9B;EACA,IAAI,WAA2B;GAC7B,OAAO,EAAM;EACf;EACA,WAAW,GAAU;GAGnB,OAFA,EAAU,IAAI,CAAQ,SAET,EAAU,OAAO,CAAQ;EACxC;EACA,KAAK,GAAK,IAAQ,MAAM;GACtB,IAAM,IAAI,IAAI,IAAI,GAAK,kBAAkB;GAIzC,AAFA,EAAM,OAAO,IAAS,CAAC,GACvB,EAAM,KAAK;IAAE,MAAM,EAAE;IAAM,UAAU,EAAE;IAAU,QAAQ,EAAE;IAAQ;GAAM,CAAC,GAC1E,IAAS,EAAM,SAAS;EAE1B;EACA,QAAQ,GAAK,IAAQ,MAAM;GACzB,IAAM,IAAI,IAAI,IAAI,GAAK,kBAAkB;GAEzC,EAAM,KAAU;IAAE,MAAM,EAAE;IAAM,UAAU,EAAE;IAAU,QAAQ,EAAE;IAAQ;GAAM;EAEhF;CACF;AACF"}
1
+ {"version":3,"file":"history.js","names":[],"sources":["../src/history.ts"],"sourcesContent":["import type { HistoryDriver } from './types';\n\n/** Creates a history driver backed by the browser History API. */\nexport function createBrowserHistory(): HistoryDriver {\n return {\n back() {\n window.history.back();\n },\n get location() {\n return {\n hash: window.location.hash,\n pathname: window.location.pathname,\n search: window.location.search,\n // State lives on window.history.state, not window.location.\n state: window.history.state,\n };\n },\n onPopstate(listener) {\n window.addEventListener('popstate', listener);\n\n return () => window.removeEventListener('popstate', listener);\n },\n push(url, state) {\n window.history.pushState(state, '', url);\n },\n replace(url, state) {\n window.history.replaceState(state, '', url);\n },\n };\n}\n\ntype MemoryLocation = { hash: string; pathname: string; search: string; state: unknown };\n\n/**\n * Creates an in-memory history driver. Suitable for SSR, tests, and non-browser environments.\n *\n * Semantics mirror the browser History API:\n * - `push()` and `replace()` update the location silently (no listener notification),\n * matching `pushState`/`replaceState` which do not fire `popstate`.\n * - `back()` moves one entry back and notifies subscribers, matching a browser back-button press.\n */\nexport function createMemoryHistory(initialPath = '/'): HistoryDriver {\n const parsed = new URL(initialPath, 'http://localhost');\n const stack: MemoryLocation[] = [\n { hash: parsed.hash, pathname: parsed.pathname, search: parsed.search, state: null },\n ];\n let cursor = 0;\n const listeners = new Set<() => void>();\n\n return {\n back() {\n if (cursor <= 0) return;\n\n cursor -= 1;\n listeners.forEach((l) => {\n l();\n });\n },\n get location(): MemoryLocation {\n return stack[cursor]!;\n },\n onPopstate(listener) {\n listeners.add(listener);\n\n return () => listeners.delete(listener);\n },\n push(url, state = null) {\n const p = new URL(url, 'http://localhost');\n\n stack.splice(cursor + 1);\n stack.push({ hash: p.hash, pathname: p.pathname, search: p.search, state });\n cursor = stack.length - 1;\n // Silent — mirrors browser pushState semantics. Router drives navigation via #handleRoute directly.\n },\n replace(url, state = null) {\n const p = new URL(url, 'http://localhost');\n\n stack[cursor] = { hash: p.hash, pathname: p.pathname, search: p.search, state };\n // Silent — mirrors browser replaceState semantics.\n },\n };\n}\n"],"mappings":";AAGA,SAAgB,IAAsC;CACpD,OAAO;EACL,OAAO;GACL,OAAO,QAAQ,KAAK;EACtB;EACA,IAAI,WAAW;GACb,OAAO;IACL,MAAM,OAAO,SAAS;IACtB,UAAU,OAAO,SAAS;IAC1B,QAAQ,OAAO,SAAS;IAExB,OAAO,OAAO,QAAQ;GACxB;EACF;EACA,WAAW,GAAU;GAGnB,OAFA,OAAO,iBAAiB,YAAY,CAAQ,SAE/B,OAAO,oBAAoB,YAAY,CAAQ;EAC9D;EACA,KAAK,GAAK,GAAO;GACf,OAAO,QAAQ,UAAU,GAAO,IAAI,CAAG;EACzC;EACA,QAAQ,GAAK,GAAO;GAClB,OAAO,QAAQ,aAAa,GAAO,IAAI,CAAG;EAC5C;CACF;AACF;AAYA,SAAgB,EAAoB,IAAc,KAAoB;CACpE,IAAM,IAAS,IAAI,IAAI,GAAa,kBAAkB,GAChD,IAA0B,CAC9B;EAAE,MAAM,EAAO;EAAM,UAAU,EAAO;EAAU,QAAQ,EAAO;EAAQ,OAAO;CAAK,CACrF,GACI,IAAS,GACP,oBAAY,IAAI,IAAgB;CAEtC,OAAO;EACL,OAAO;GACD,KAAU,MAEd,KACA,EAAU,SAAS,MAAM;IACvB,EAAE;GACJ,CAAC;EACH;EACA,IAAI,WAA2B;GAC7B,OAAO,EAAM;EACf;EACA,WAAW,GAAU;GAGnB,OAFA,EAAU,IAAI,CAAQ,SAET,EAAU,OAAO,CAAQ;EACxC;EACA,KAAK,GAAK,IAAQ,MAAM;GACtB,IAAM,IAAI,IAAI,IAAI,GAAK,kBAAkB;GAIzC,AAFA,EAAM,OAAO,IAAS,CAAC,GACvB,EAAM,KAAK;IAAE,MAAM,EAAE;IAAM,UAAU,EAAE;IAAU,QAAQ,EAAE;IAAQ;GAAM,CAAC,GAC1E,IAAS,EAAM,SAAS;EAE1B;EACA,QAAQ,GAAK,IAAQ,MAAM;GACzB,IAAM,IAAI,IAAI,IAAI,GAAK,kBAAkB;GAEzC,EAAM,KAAU;IAAE,MAAM,EAAE;IAAM,UAAU,EAAE;IAAU,QAAQ,EAAE;IAAQ;GAAM;EAEhF;CACF;AACF"}
package/dist/index.d.ts CHANGED
@@ -1,7 +1,7 @@
1
- export type { BeforeLeaveBlocker, BeforeLeaveOptions, CoerceSearchFn, DataContext, DataFn, DataStream, HistoryDriver, IsActiveOptions, MatchStatus, MaybePromise, Middleware, NamedNavigationTarget, NavigateOptions, NavigationDestination, NavigationStatus, NavigationTarget, PathParams, QueryParams, RawNavigationTarget, ResolvedQueryParams, ResolvedQueryValue, RouteChildren, RouteContext, RouteDefinition, RouteLocation, RouteMatch, RouteMatchBranch, RouteMiddleware, RouteName, RouteParams, RoutePathByName, RouteState, RouteTable, RouterErrorContext, RouterErrorSource, RouterOptions, ScrollDecision, ScrollPosition, Unsubscribe, UntypedNamedNavigationTarget, } from './types';
2
1
  export { WayfinderApiError, WayfinderDisposedError, WayfinderError, WayfinderRedirectLoopError, WayfinderRouteError, } from './errors';
3
- export { createRouter } from './router';
4
- export type { Router } from './router';
5
2
  export { createBrowserHistory, createMemoryHistory } from './history';
6
3
  export { redirectTo } from './middleware';
4
+ export type { Router } from './router';
5
+ export { createRouter } from './router';
6
+ export type { BeforeLeaveBlocker, BeforeLeaveOptions, CoerceSearchFn, DataContext, DataFn, DataStream, 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, ScrollDecision, ScrollPosition, Unsubscribe, UntypedNamedNavigationTarget, } from './types';
7
7
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EACV,kBAAkB,EAClB,kBAAkB,EAClB,cAAc,EACd,WAAW,EACX,MAAM,EACN,UAAU,EACV,aAAa,EACb,eAAe,EACf,WAAW,EACX,YAAY,EACZ,UAAU,EACV,qBAAqB,EACrB,eAAe,EACf,qBAAqB,EACrB,gBAAgB,EAChB,gBAAgB,EAChB,UAAU,EACV,WAAW,EACX,mBAAmB,EACnB,mBAAmB,EACnB,kBAAkB,EAClB,aAAa,EACb,YAAY,EACZ,eAAe,EACf,aAAa,EACb,UAAU,EACV,gBAAgB,EAChB,eAAe,EACf,SAAS,EACT,WAAW,EACX,eAAe,EACf,UAAU,EACV,UAAU,EACV,kBAAkB,EAClB,iBAAiB,EACjB,aAAa,EACb,cAAc,EACd,cAAc,EACd,WAAW,EACX,4BAA4B,GAC7B,MAAM,SAAS,CAAC;AACjB,OAAO,EACL,iBAAiB,EACjB,sBAAsB,EACtB,cAAc,EACd,0BAA0B,EAC1B,mBAAmB,GACpB,MAAM,UAAU,CAAC;AAClB,OAAO,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AACxC,YAAY,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AACvC,OAAO,EAAE,oBAAoB,EAAE,mBAAmB,EAAE,MAAM,WAAW,CAAC;AACtE,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC"}
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,UAAU,EACV,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,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||``)}}function f(e,t,n,r){let i=l(e,t,n);if(!r||Object.keys(r).length===0)return i;let a=new URLSearchParams;for(let[e,t]of Object.entries(r))Array.isArray(t)?t.forEach(t=>a.append(e,t)):a.set(e,t);return`${i}?${a.toString()}`}exports.buildPreloadKey=f,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;
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||``)}}function f(e,t,n,r){let i=l(e,t,n);if(!r||Object.keys(r).length===0)return i;let a=new URLSearchParams;for(let[e,t]of Object.entries(r))Array.isArray(t)?t.forEach(t=>{a.append(e,t)}):a.set(e,t);return`${i}?${a.toString()}`}exports.buildPreloadKey=f,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 type { QueryParams, ResolvedQueryParams, RouteLocation, RouteMatcher, RouteParams, RouteRecord } from './types';\n\nimport { WayfinderRouteError } from './errors';\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)) value.forEach((item) => search.append(key, String(item)));\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) => search.append(key, v));\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<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 +1 @@
1
- {"version":3,"file":"path.d.ts","sourceRoot":"","sources":["../src/path.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,mBAAmB,EAAE,aAAa,EAAE,YAAY,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAIvH,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,KAAK,GAAG,OAAO,EAAE,UAAU,GAAG,OAAO,EAC/D,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,WAAW,CAAC,KAAK,EAAE,UAAU,CAAC,GACrC,WAAW,GAAG,IAAI,CAkBpB;AAED,wBAAgB,aAAa,CAAC,KAAK,GAAG,OAAO,EAAE,UAAU,GAAG,OAAO,EACjE,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,SAAS,WAAW,CAAC,KAAK,EAAE,UAAU,CAAC,EAAE,GACjD;IAAE,MAAM,EAAE,WAAW,CAAC;IAAC,MAAM,CAAC,EAAE,WAAW,CAAC,KAAK,EAAE,UAAU,CAAC,CAAA;CAAE,CAQlE;AAED,wBAAgB,aAAa,CAAC,KAAK,GAAG,OAAO,EAAE,UAAU,GAAG,OAAO,EACjE,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,WAAW,CAAC,KAAK,EAAE,UAAU,CAAC,GACrC,OAAO,CAET;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,CA4BrH;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;AAED,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,KAAK,CAAC,EAAE,WAAW,GAAG,MAAM,CAgB5G"}
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,KAAK,GAAG,OAAO,EAAE,UAAU,GAAG,OAAO,EAC/D,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,WAAW,CAAC,KAAK,EAAE,UAAU,CAAC,GACrC,WAAW,GAAG,IAAI,CAkBpB;AAED,wBAAgB,aAAa,CAAC,KAAK,GAAG,OAAO,EAAE,UAAU,GAAG,OAAO,EACjE,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,SAAS,WAAW,CAAC,KAAK,EAAE,UAAU,CAAC,EAAE,GACjD;IAAE,MAAM,EAAE,WAAW,CAAC;IAAC,MAAM,CAAC,EAAE,WAAW,CAAC,KAAK,EAAE,UAAU,CAAC,CAAA;CAAE,CAQlE;AAED,wBAAgB,aAAa,CAAC,KAAK,GAAG,OAAO,EAAE,UAAU,GAAG,OAAO,EACjE,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,WAAW,CAAC,KAAK,EAAE,UAAU,CAAC,GACrC,OAAO,CAET;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;AAED,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,KAAK,CAAC,EAAE,WAAW,GAAG,MAAM,CAkB5G"}
package/dist/path.js CHANGED
@@ -105,7 +105,9 @@ function l(t, r, i = {}, a) {
105
105
  if (a && Object.keys(a).length) {
106
106
  let e = new URLSearchParams();
107
107
  Object.entries(a).forEach(([t, n]) => {
108
- Array.isArray(n) ? n.forEach((n) => e.append(t, String(n))) : e.set(t, String(n));
108
+ Array.isArray(n) ? n.forEach((n) => {
109
+ e.append(t, String(n));
110
+ }) : e.set(t, String(n));
109
111
  }), o += `?${e.toString()}`;
110
112
  }
111
113
  return n(t, o);
@@ -129,7 +131,9 @@ function f(e, t, n, r) {
129
131
  let i = l(e, t, n);
130
132
  if (!r || Object.keys(r).length === 0) return i;
131
133
  let a = new URLSearchParams();
132
- for (let [e, t] of Object.entries(r)) Array.isArray(t) ? t.forEach((t) => a.append(e, t)) : a.set(e, t);
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);
133
137
  return `${i}?${a.toString()}`;
134
138
  }
135
139
  //#endregion