# What changed in rou3

> 47 changes in rou3 0.10 to 0.12 from its official release notes, upgrade guides and advisories: 34 breaking, 2 deprecations, 1 security, 1 new APIs and options. Each one cited. Sources checked 2026-10-20. HTML: https://unstale.sh/npm/rou3/

To see which of these apply to a repository, run `npx @unstale/cli init` in it.

## rou3 0.12

### Breaking changes

- Literal pattern text is percent-encoded once when a route is added; lookup paths are neither decoded nor encoded. (0.12.0) — [release notes](https://github.com/h3js/rou3/releases/tag/v0.12.0)
- Dot segments, including `%2e` forms, are resolved when routes are added and removed; lookup paths are unchanged unless `normalize: true`. (0.12.0) — [release notes](https://github.com/h3js/rou3/releases/tag/v0.12.0)
- The wildcard is now a greedy catch-all that spans segments, so trailing `*` rules apply at every depth. (0.12.0) — [release notes](https://github.com/h3js/rou3/releases/tag/v0.12.0)
- A written `(.*)` is now a greedy catch-all like `*`; `:name(.*)` is keyed by the name, and both match across segments. (0.12.0) — [release notes](https://github.com/h3js/rou3/releases/tag/v0.12.0)
- A bare `**` is now an unnamed capture keyed by its numbered position; a zero-segment match omits the key, and `params._` is deprecated. (0.12.0) — [release notes](https://github.com/h3js/rou3/releases/tag/v0.12.0)
- An optional parameter following a capture in the same segment is matched in place, while the preceding capture is greedy. (0.12.0) — [release notes](https://github.com/h3js/rou3/releases/tag/v0.12.0)
- A regex group after a `{…}` group ending in a parameter is now an unnamed capture next to the parameter, rather than that parameter's constraint. (0.12.0) — [release notes](https://github.com/h3js/rou3/releases/tag/v0.12.0)
- A pattern starting with a `{/…}` group is now absolute and no longer receives an extra `/` in front. (0.12.0) — [release notes](https://github.com/h3js/rou3/releases/tag/v0.12.0)
- `/a/*` and `/a/**` now compare as equal; named catch-all comparisons also have updated equality and subset results. (0.12.0) — [release notes](https://github.com/h3js/rou3/releases/tag/v0.12.0)
- Named catch-alls `:name+`, `:name*` and `**:name` no longer accept empty segments; affected paths fall through to a less specific route or no match. (0.12.0) — [release notes](https://github.com/h3js/rou3/releases/tag/v0.12.0)
- It reads an unnamed catch-all back as `**` while still accepting old `_` groups, and throws for literals or patterns that routes cannot represent. (0.12.0) — [release notes](https://github.com/h3js/rou3/releases/tag/v0.12.0)
- A route can contain only one catch-all among `*`, `**`, `:x+`, `:x*`, `(.*)` or `:x(.*)`; routes with more than one throw. (0.12.0) — [release notes](https://github.com/h3js/rou3/releases/tag/v0.12.0)
- Raw tabs, LF and CR in patterns now throw; `--` and `&&` in a constraint character class also throw. (0.12.0) — [release notes](https://github.com/h3js/rou3/releases/tag/v0.12.0)
- A `*` route node is keyed as `/**`, and parameter segments use numbered keys such as `:_0` and `:_1`. (0.12.0) — [release notes](https://github.com/h3js/rou3/releases/tag/v0.12.0)
- Generated regexes use percent-encoded literal text, name `**` groups `_0`, `_1`, …, and use URLPattern-style named catch-all expressions. (0.12.0) — [release notes](https://github.com/h3js/rou3/releases/tag/v0.12.0)
- On engines without duplicate named groups, patterns requiring an uninlinable group around a trailing `*` now throw. (0.12.0) — [release notes](https://github.com/h3js/rou3/releases/tag/v0.12.0)

### Security

- Generated regexes for parameters sharing a segment no longer backtrack polynomially, closing the stated ReDoS class. (0.12.0) — [release notes](https://github.com/h3js/rou3/releases/tag/v0.12.0)

### Deprecations

- The `_` parameter key for a bare `**` capture is deprecated; read `params["0"] ?? ""` instead or name the capture. (0.12.0) — [release notes](https://github.com/h3js/rou3/releases/tag/v0.12.0)

### Performance

- Segments with several parameters now match in linear time in `findRoute` and compiled matchers. (0.12.0) — [release notes](https://github.com/h3js/rou3/releases/tag/v0.12.0)

### Behavior and default changes

- Constrained and literal in-segment parameters now rank above plain ones on the same node, regardless of registration order. (0.12.0) — [release notes](https://github.com/h3js/rou3/releases/tag/v0.12.0)

### Fixes and other changes

- Each `addRoute` call is listed once; duplicate variants are replaced by the variant `findRoute` picks. (0.12.0) — [release notes](https://github.com/h3js/rou3/releases/tag/v0.12.0)
- Trailing whole-segment catch-alls are typed `string | undefined`; `**<text>` is one key, and the `**` key has a deprecated `_?: string` alternative. (0.12.0) — [release notes](https://github.com/h3js/rou3/releases/tag/v0.12.0)
- With normalization enabled, a trailing `.` or `..` keeps its trailing slash, matching WHATWG URL behavior. (0.12.0) — [release notes](https://github.com/h3js/rou3/releases/tag/v0.12.0)

## rou3 0.11

### Breaking changes

- Generated route data now passes through JSON.stringify, producing valid code for JSON-serializable data and throwing a rou3 error for functions, symbols, or bigints. (0.11.0) — [release notes](https://github.com/h3js/rou3/releases/tag/v0.11.0)
- Method-agnostic routes are always considered alongside method routes; the more specific route wins, with method routes winning ties. findAllRoutes and compiled matchAll return both. (0.11.0) — [release notes](https://github.com/h3js/rou3/releases/tag/v0.11.0)
- The package now declares Node.js >=20.19.0 as its engine requirement. (0.11.0) — [release notes](https://github.com/h3js/rou3/releases/tag/v0.11.0)
- Hand-written regexes must be anchored, and regexes with u or v flags are rejected because they cannot be represented as whole-path routes or equivalent constraints. (0.11.0) — [release notes](https://github.com/h3js/rou3/releases/tag/v0.11.0)
- Required params no longer match empty segments; use *, **, :name*, or a constraint that allows empty when empty values are needed. (0.11.0) — [release notes](https://github.com/h3js/rou3/releases/tag/v0.11.0)
- Falsy route data such as 0, false, and an empty string is returned as-is instead of null; missing data remains null. (0.11.0) — [release notes](https://github.com/h3js/rou3/releases/tag/v0.11.0)
- Regex constraints now throw for anchors, look-arounds, backreferences, and capturing groups because constraint and whole-path matching otherwise disagree. (0.11.0) — [release notes](https://github.com/h3js/rou3/releases/tag/v0.11.0)
- Outside regex constraints, any \x means a literal x; \ is a literal backslash, while \/ and a trailing \ throw. (0.11.0) — [release notes](https://github.com/h3js/rou3/releases/tag/v0.11.0)
- When a segment has several params, the first now matches as little as possible, which can change how values are split. (0.11.0) — [release notes](https://github.com/h3js/rou3/releases/tag/v0.11.0)
- A ? on a param that does not start its segment makes only that param optional; use a group to make the whole segment optional. (0.11.0) — [release notes](https://github.com/h3js/rou3/releases/tag/v0.11.0)
- Param names now follow [A-Za-z_]\w*; -, {, and } end a name. InferRouteParams reads names the same way, so runtime params and inferred types may change. (0.11.0) — [release notes](https://github.com/h3js/rou3/releases/tag/v0.11.0)
- addRoute and routeToRegExp now throw rou3 errors for syntax that previously had surprising or unclear meanings, including invalid modifiers, names, duplicate params, and malformed groups. (0.11.0) — [release notes](https://github.com/h3js/rou3/releases/tag/v0.11.0)

### Deprecations

- RouterCompilerOptions is deprecated in favor of CompileRouterToStringOptions and CompileRouterOptions. (0.11.0) — [release notes](https://github.com/h3js/rou3/releases/tag/v0.11.0)

### New APIs and options

- The compiler now accepts an options object with functionName, matchAll, and serialize; the old positional form still works but is deprecated. (0.11.0) — [release notes](https://github.com/h3js/rou3/releases/tag/v0.11.0)

### Fixes and other changes

- Calling compileRouter with matchAll: true is now typed as returning an array. (0.11.0) — [release notes](https://github.com/h3js/rou3/releases/tag/v0.11.0)
- Each call returns fresh match objects, so mutating one result no longer affects later lookups. (0.11.0) — [release notes](https://github.com/h3js/rou3/releases/tag/v0.11.0)
- Each call returns a fresh { data, params } object, including static matches and params: false results, so mutating one result no longer affects later lookups. (0.11.0) — [release notes](https://github.com/h3js/rou3/releases/tag/v0.11.0)

## rou3 0.10

### Breaking changes

- Wildcards can have following segments, which are matched from the end of the path; only one `**` is allowed, and routes with following segments affect match ranking. (0.10.0) — [release notes](https://github.com/h3js/rou3/releases/tag/v0.10.0)
- Lookup ignores at most one trailing slash; repeated trailing slashes no longer match, and a route with an empty parameter can match `/users//`. (0.10.0) — [release notes](https://github.com/h3js/rou3/releases/tag/v0.10.0)
- With `normalize: true`, path normalization drops `..` segments that would go above the root. (0.10.0) — [release notes](https://github.com/h3js/rou3/releases/tag/v0.10.0)
- Removing a route removes only the given pattern, rather than every route on the same node and method. (0.10.0) — [release notes](https://github.com/h3js/rou3/releases/tag/v0.10.0)
- Generated regexes now match exactly the paths `findRoute` matches, retain segments after `**`, and may have different text; some routes no longer use look-behind. (0.10.0) — [release notes](https://github.com/h3js/rou3/releases/tag/v0.10.0)
- Duplicate param names now produce a `rou3:` error instead of a raw `SyntaxError`; optional groups followed by more route content no longer emit duplicate group names on Node 22. (0.10.0) — [release notes](https://github.com/h3js/rou3/releases/tag/v0.10.0)

### Fixes and other changes

- Optional params and a trailing bare `*` can be `string | undefined`; inferred param names now omit constraints and modifiers. (0.10.0) — [release notes](https://github.com/h3js/rou3/releases/tag/v0.10.0)
