fix: never throw when encoding or decoding URI components - #2011
Conversation
encodeURIComponent throws a URIError on a lone surrogate, so a path param or router key containing one crashed the OpenAPI link, pathToHttpPath and the static file redirect. @orpc/shared now exports safeEncodeURIComponent and safeDecodeURIComponent (replacing tryDecodeURIComponent): lone surrogates are encoded as U+FFFD like URLSearchParams does, malformed percent escapes decode to the raw input, and input without a percent sign skips the decoder entirely. The raw globals are now rejected by no-restricted-globals in package sources. The previous ban/ban entry only checked call expressions, so a bare reference such as path.map(encodeURIComponent) was never flagged.
More templates
@orpc/ai-sdk
@orpc/arktype
@orpc/bun
@orpc/client
@orpc/cloudflare
@orpc/contract
@orpc/experimental-effect
@orpc/evlog
@orpc/hibernation
@orpc/json-schema
@orpc/experimental-msw
@orpc/nest
@orpc/next
@orpc/node
@orpc/openapi
@orpc/opentelemetry
@orpc/pinia-colada
@orpc/pino
@orpc/publisher
@orpc/ratelimit
@orpc/server
@orpc/shared
@orpc/swr
@orpc/tanstack-query
@orpc/trpc
@orpc/valibot
@orpc/zod
commit: |
Merging this PR will degrade performance by 11.2%
|
| Benchmark | BASE |
HEAD |
Efficiency | |
|---|---|---|---|---|
| ❌ | serve file |
3.5 ms | 3.9 ms | -11.2% |
Tip
Investigate this regression by commenting @codspeedbot fix this regression on this PR, or directly use the CodSpeed MCP with your agent.
Comparing dinwwwh:claude/safe-uri-encode-decode-ed2e03 (8c08b41) with main (9ad5039)
There was a problem hiding this comment.
✅ No new issues found.
Reviewed changes — a single commit replacing tryDecodeURIComponent with never-throwing URI helpers, updating every package call site, and swapping the lint guard.
- New
safeEncodeURIComponent/safeDecodeURIComponentinpackages/shared/src/uri.ts(replacingtryDecodeURIComponent): encode falls back to replacing lone surrogates with U+FFFD when the native call throws; decode early-returns inputs without%and returns malformed input unchanged. - Call sites routed through the safe helpers in
shared/src/http.ts, the OpenAPI link codec and matcher, the client batch plugin, and the node static-file handler — behavior is unchanged for well-formed input. - Lint guard swapped from
ban/bantono-restricted-globals(scoped topackages/*/src/**, tests exempt), which also catches bare references likemap(encodeURIComponent). - Tests added for well-formed encode parity (incl. a full non-surrogate BMP sweep), lone-surrogate encode, and malformed/valid decode.
Verified locally: the affected shared/openapi/node/client suites pass (261 tests), no-restricted-globals fires on bare references, and a fuzz comparison shows safeEncodeURIComponent is exactly encodeURIComponent(value.toWellFormed()) over exhaustive surrogate combinations and 200k random inputs. The lookbehind regex is supported by every runtime in the documented requirements.mdx matrix (Safari 16.4 is both the floor and lookbehind's first version).
openrouter/deepseek/deepseek-v4.1-flash (free via Pullfrog for OSS) | 𝕏
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |

encodeURIComponentthrows aURIErroron a lone surrogate, so a path param or router key containing one crashed the OpenAPI link,pathToHttpPath, and the static file redirect.@orpc/sharednow exportssafeEncodeURIComponentandsafeDecodeURIComponent(replacingtryDecodeURIComponent), every package call site uses them, and ESLint rejects the raw globals in package sources.Fixes
URLSearchParamsandString.prototype.toWellFormedapply.no-restricted-globalsreplaces theban/banentry: the ban plugin only inspects call expressions, so a bare reference likepath.map(encodeURIComponent)was never flagged. Test files stay exempt.Performance
%, 2-8x faster on typical path segments.Testing