diff --git a/CHANGELOG.md b/CHANGELOG.md index d7a3b2c..52220f4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [0.12.2] - 2026-04-01 + +### Fixed +- Route guard property `"?"` is now restricted to function type only, preventing accidental assignment of sub-routes objects (closes #10) + ## [0.12.1] - 2026-04-01 ### Fixed diff --git a/packages/esroute-lit/package.json b/packages/esroute-lit/package.json index 8088d4a..382cbac 100644 --- a/packages/esroute-lit/package.json +++ b/packages/esroute-lit/package.json @@ -1,6 +1,6 @@ { "name": "@esroute/lit", - "version": "0.12.1", + "version": "0.12.2", "description": "A small efficient client-side routing library for lit, written in TypeScript.", "main": "dist/index.js", "sideEffects": false, @@ -22,7 +22,7 @@ "typescript": "^5.4.5" }, "dependencies": { - "esroute": "^0.12.1", + "esroute": "^0.12.2", "lit": "^3.1.1" } } diff --git a/packages/esroute/package.json b/packages/esroute/package.json index 42f450e..f678caa 100644 --- a/packages/esroute/package.json +++ b/packages/esroute/package.json @@ -1,6 +1,6 @@ { "name": "esroute", - "version": "0.12.1", + "version": "0.12.2", "description": "A small efficient framework-agnostic client-side routing library, written in TypeScript.", "types": "dist/index.d.ts", "main": "dist/index.js", diff --git a/packages/esroute/src/route-resolver.spec.ts b/packages/esroute/src/route-resolver.spec.ts index 57ce57c..3e2aab8 100644 --- a/packages/esroute/src/route-resolver.spec.ts +++ b/packages/esroute/src/route-resolver.spec.ts @@ -273,6 +273,17 @@ describe("Resolver", () => { }); }); + describe("types", () => { + it("should not allow a route object as guard", () => { + // @ts-expect-error "?" must be a function, not a sub-routes object + const _routes: Routes = { "?": { foo: () => "foo" } }; + }); + + it("should allow a function as guard", () => { + const _routes: Routes = { "?": () => undefined }; + }); + }); + describe("redirects", () => { it("should resolve a redirect via calling 'go()'", async () => { const navOpts = new NavOpts("/foo"); diff --git a/packages/esroute/src/route-resolver.ts b/packages/esroute/src/route-resolver.ts index b69ec2f..9a63fb6 100644 --- a/packages/esroute/src/route-resolver.ts +++ b/packages/esroute/src/route-resolver.ts @@ -49,13 +49,13 @@ const getResolves = async ( ): Promise => { const { path, params } = opts; const resolves: Resolve[] = []; - let routes: Routes | Resolve | null = root; + let routes: Routes | Resolve | null | undefined = root; for (let i = 0; i < path.length; i++) { const part = path[i]; if (!routes || typeof routes === "function") return null; const redirect = await checkGuard(routes, opts); if (redirect) return [() => redirect]; - const virtual: Routes | Resolve | void = routes[""]; + const virtual: Routes | Resolve | undefined = routes[""]; if (typeof virtual === "function") resolves.unshift(virtual); if (part in routes) routes = routes[part]; else if ("*" in routes) { @@ -73,6 +73,7 @@ const getResolves = async ( resolves.unshift(routes); return resolves; } + if (!routes) return null; const redirect = await checkGuard(routes, opts); if (redirect) return [() => redirect]; } while ((routes = routes[""])); diff --git a/packages/esroute/src/routes.ts b/packages/esroute/src/routes.ts index ca9563f..ad2cdc3 100644 --- a/packages/esroute/src/routes.ts +++ b/packages/esroute/src/routes.ts @@ -10,7 +10,8 @@ export type Resolve = ( ) => T | NavOpts | Promise>; export interface Routes { - [k: string]: Routes | Resolve; + "?"?: Resolve; + [k: string]: Routes | Resolve | undefined; } // Depth counter using a string to track recursion depth (max 10 levels)