-
-
Notifications
You must be signed in to change notification settings - Fork 145
feat(json-schema): smart coercion plugin for any standard schema #748
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
21 commits
Select commit
Hold shift + click to select a range
50ba7f4
init
dinwwwh 6c475c9
wip
dinwwwh a02b7e2
wip
dinwwwh 71c3a8b
wip
dinwwwh 960a881
wip
dinwwwh df3d49e
wip
dinwwwh e630020
tests
dinwwwh cd8fc06
wip
dinwwwh e0af29b
wip
dinwwwh 613f4d4
wip
dinwwwh 9b54413
improve
dinwwwh 91539eb
docs
dinwwwh 0968479
playgrounds
dinwwwh 1f6f932
wip
dinwwwh b68f5cf
wip
dinwwwh 51b6ebb
improve
dinwwwh 8b2b552
wip
dinwwwh c40d3b2
fix
dinwwwh b5b729c
Merge branch 'main' into feat/json-schema/coercer
dinwwwh 4799c75
lint fixed
dinwwwh c5571fc
enhance date coercion handling and add test cases for flexible date f…
dinwwwh File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,155 @@ | ||
| --- | ||
| title: Smart Coercion Plugin | ||
| description: Automatically converts input values to match schema types without manually defining coercion logic. | ||
| --- | ||
|
|
||
| # Smart Coercion Plugin | ||
|
|
||
| Automatically converts input values to match schema types without manually defining coercion logic. | ||
|
|
||
| ::: warning | ||
| This plugin improves developer experience but impacts performance. For high-performance applications or complex schemas, manually defining coercion in your schema validation is more efficient. | ||
| ::: | ||
|
|
||
| ## Installation | ||
|
|
||
| ::: code-group | ||
|
|
||
| ```sh [npm] | ||
| npm install @orpc/json-schema@latest | ||
| ``` | ||
|
|
||
| ```sh [yarn] | ||
| yarn add @orpc/json-schema@latest | ||
| ``` | ||
|
|
||
| ```sh [pnpm] | ||
| pnpm add @orpc/json-schema@latest | ||
| ``` | ||
|
|
||
| ```sh [bun] | ||
| bun add @orpc/json-schema@latest | ||
| ``` | ||
|
|
||
| ```sh [deno] | ||
| deno install npm:@orpc/json-schema@latest | ||
| ``` | ||
|
|
||
| ::: | ||
|
|
||
| ## Setup | ||
|
|
||
| Configure the plugin with [JSON Schema Converters](/docs/openapi/openapi-specification#generating-specifications) for your validation libraries. | ||
|
|
||
| ```ts | ||
| import { OpenAPIHandler } from '@orpc/openapi/fetch' | ||
| import { | ||
| experimental_SmartCoercionPlugin as SmartCoercionPlugin | ||
| } from '@orpc/json-schema' | ||
|
|
||
| const handler = new OpenAPIHandler(router, { | ||
| plugins: [ | ||
| new SmartCoercionPlugin({ | ||
| schemaConverters: [ | ||
| new ZodToJsonSchemaConverter(), | ||
| // Add other schema converters as needed | ||
| ], | ||
| }) | ||
| ] | ||
| }) | ||
| ``` | ||
|
|
||
| ## How It Works | ||
|
|
||
| The plugin converts values **safely** using these rules: | ||
|
|
||
| 1. **Schema-guided:** Only converts when the schema says what type to use | ||
| 2. **Safe only:** Only converts values that make sense (like `'123'` to `123`) | ||
| 3. **Keep original:** If conversion is unsafe, keeps the original value | ||
| 4. **Smart unions:** Picks the best conversion for union types | ||
| 5. **Deep conversion:** Works inside nested objects and arrays | ||
|
|
||
| ::: info | ||
| JavaScript native types such as BigInt, Date, RegExp, URL, Set, and Map are not natively supported by JSON Schema. To enable correct coercion, oRPC relies on the `x-native-type` metadata in your schema: | ||
|
|
||
| - `x-native-type: 'bigint'` for BigInt | ||
| - `x-native-type: 'date'` for Date | ||
| - `x-native-type: 'regexp'` for RegExp | ||
| - `x-native-type: 'url'` for URL | ||
| - `x-native-type: 'set'` for Set | ||
| - `x-native-type: 'map'` for Map | ||
|
|
||
| The built-in [JSON Schema Converters](/docs/openapi/openapi-specification#generating-specifications) handle these cases (except for some experimental converters). Since this approach is not part of the official JSON Schema specification, if you use a custom converter, you may need to add the appropriate `x-native-type` metadata to your schemas to ensure proper coercion. | ||
| ::: | ||
|
|
||
| ## Conversion Rules | ||
|
|
||
| ### String → Boolean | ||
|
|
||
| Support specific string values (case-insensitive): | ||
|
|
||
| - `'true'`, `'on'` → `true` | ||
| - `'false'`, `'off'` → `false` | ||
|
|
||
| ::: info | ||
| HTML `<input type="checkbox">` elements submit `'on'` or `'off'` as values, so this conversion is especially useful for handling checkbox input in forms. | ||
| ::: | ||
|
|
||
| ### String → Number | ||
|
|
||
| Support valid numeric strings: | ||
|
|
||
| - `'123'` → `123` | ||
| - `'3.14'` → `3.14` | ||
|
|
||
| ### String/Number → BigInt | ||
|
|
||
| Support valid numeric strings or numbers: | ||
|
|
||
| - `'12345678901234567890'` → `12345678901234567890n` | ||
| - `12345678901234567890` → `12345678901234567890n` | ||
|
|
||
| ### String → Date | ||
|
|
||
| Support ISO date/datetime strings: | ||
|
|
||
| - `'2023-10-01'` → `new Date('2023-10-01')` | ||
| - `'2020-01-01T06:15'` → `new Date('2020-01-01T06:15')` | ||
| - `'2020-01-01T06:15Z'` → `new Date('2020-01-01T06:15Z')` | ||
| - `'2020-01-01T06:15:00Z'` → `new Date('2020-01-01T06:15:00Z')` | ||
| - `'2020-01-01T06:15:00.123Z'` → `new Date('2020-01-01T06:15:00.123Z')` | ||
|
|
||
| ### String → RegExp | ||
|
|
||
| Support valid regular expression strings: | ||
|
|
||
| - `'/^\\d+$/i'` → `new RegExp('^\\d+$', 'i')` | ||
| - `'/abc/'` → `new RegExp('abc')` | ||
|
|
||
| ### String → URL | ||
|
|
||
| Support valid URL strings: | ||
|
|
||
| - `'https://example.com'` → `new URL('https://example.com')` | ||
|
|
||
| ### Array → Set | ||
|
|
||
| Support arrays of **unique values**: | ||
|
|
||
| - `['apple', 'banana']` → `new Set(['apple', 'banana'])` | ||
|
|
||
| ### Array → Object | ||
|
|
||
| Converts arrays to objects with numeric keys: | ||
|
|
||
| - `['apple', 'banana']` → `{ 0: 'apple', 1: 'banana' }` | ||
|
|
||
| ::: info | ||
| This is particularly useful for [Bracket Notation](/docs/openapi/bracket-notation) when you need objects with numeric keys. | ||
| ::: | ||
|
|
||
| ### Array → Map | ||
|
|
||
| Support arrays of key-value pairs: | ||
|
|
||
| - `[['key1', 'value1'], ['key2', 'value2']]` → `new Map([['key1', 'value1'], ['key2', 'value2']])` |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,26 @@ | ||
| # Hidden folders and files | ||
| .* | ||
| !.gitignore | ||
| !.*.example | ||
|
|
||
| # Common generated folders | ||
| logs/ | ||
| node_modules/ | ||
| out/ | ||
| dist/ | ||
| dist-ssr/ | ||
| build/ | ||
| coverage/ | ||
| temp/ | ||
|
|
||
| # Common generated files | ||
| *.log | ||
| *.log.* | ||
| *.tsbuildinfo | ||
| *.vitest-temp.json | ||
| vite.config.ts.timestamp-* | ||
| vitest.config.ts.timestamp-* | ||
|
|
||
| # Common manual ignore files | ||
| *.local | ||
| *.pem |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,76 @@ | ||
| <div align="center"> | ||
| <image align="center" src="https://orpc.unnoq.com/logo.webp" width=280 alt="oRPC logo" /> | ||
| </div> | ||
|
|
||
| <h1></h1> | ||
|
|
||
| <div align="center"> | ||
| <a href="https://codecov.io/gh/unnoq/orpc"> | ||
| <img alt="codecov" src="https://codecov.io/gh/unnoq/orpc/branch/main/graph/badge.svg"> | ||
| </a> | ||
| <a href="https://www.npmjs.com/package/@orpc/json-schema"> | ||
| <img alt="weekly downloads" src="https://img.shields.io/npm/dw/%40orpc%2Fjson-schema?logo=npm" /> | ||
| </a> | ||
| <a href="https://github.com/unnoq/orpc/blob/main/LICENSE"> | ||
| <img alt="MIT License" src="https://img.shields.io/github/license/unnoq/orpc?logo=open-source-initiative" /> | ||
| </a> | ||
| <a href="https://discord.gg/TXEbwRBvQn"> | ||
| <img alt="Discord" src="https://img.shields.io/discord/1308966753044398161?color=7389D8&label&logo=discord&logoColor=ffffff" /> | ||
| </a> | ||
| </div> | ||
|
|
||
| <h3 align="center">Typesafe APIs Made Simple 🪄</h3> | ||
|
|
||
| **oRPC is a powerful combination of RPC and OpenAPI**, makes it easy to build APIs that are end-to-end type-safe and adhere to OpenAPI standards | ||
|
|
||
| --- | ||
|
|
||
| ## Highlights | ||
|
|
||
| - **🔗 End-to-End Type Safety**: Ensure type-safe inputs, outputs, and errors from client to server. | ||
| - **📘 First-Class OpenAPI**: Built-in support that fully adheres to the OpenAPI standard. | ||
| - **📝 Contract-First Development**: Optionally define your API contract before implementation. | ||
| - **⚙️ Framework Integrations**: Seamlessly integrate with TanStack Query (React, Vue, Solid, Svelte, Angular), Pinia Colada, and more. | ||
| - **🚀 Server Actions**: Fully compatible with React Server Actions on Next.js, TanStack Start, and other platforms. | ||
| - **🔠 Standard Schema Support**: Works out of the box with Zod, Valibot, ArkType, and other schema validators. | ||
| - **🗃️ Native Types**: Supports native types like Date, File, Blob, BigInt, URL, and more. | ||
| - **⏱️ Lazy Router**: Enhance cold start times with our lazy routing feature. | ||
| - **📡 SSE & Streaming**: Enjoy full type-safe support for SSE and streaming. | ||
| - **🌍 Multi-Runtime Support**: Fast and lightweight on Cloudflare, Deno, Bun, Node.js, and beyond. | ||
| - **🔌 Extendability**: Easily extend functionality with plugins, middleware, and interceptors. | ||
| - **🛡️ Reliability**: Well-tested, TypeScript-based, production-ready, and MIT licensed. | ||
|
|
||
| ## Documentation | ||
|
|
||
| You can find the full documentation [here](https://orpc.unnoq.com). | ||
|
|
||
| ## Packages | ||
|
|
||
| - [@orpc/contract](https://www.npmjs.com/package/@orpc/contract): Build your API contract. | ||
| - [@orpc/server](https://www.npmjs.com/package/@orpc/server): Build your API or implement API contract. | ||
| - [@orpc/client](https://www.npmjs.com/package/@orpc/client): Consume your API on the client with type-safety. | ||
| - [@orpc/openapi](https://www.npmjs.com/package/@orpc/openapi): Generate OpenAPI specs and handle OpenAPI requests. | ||
| - [@orpc/nest](https://www.npmjs.com/package/@orpc/nest): Deeply integrate oRPC with [NestJS](https://nestjs.com/). | ||
| - [@orpc/react](https://www.npmjs.com/package/@orpc/react): Utilities for integrating oRPC with React and React Server Actions. | ||
| - [@orpc/tanstack-query](https://www.npmjs.com/package/@orpc/tanstack-query): [TanStack Query](https://tanstack.com/query/latest) integration. | ||
| - [@orpc/vue-colada](https://www.npmjs.com/package/@orpc/vue-colada): Integration with [Pinia Colada](https://pinia-colada.esm.dev/). | ||
| - [@orpc/hey-api](https://www.npmjs.com/package/@orpc/hey-api): [Hey API](https://heyapi.dev/) integration. | ||
| - [@orpc/zod](https://www.npmjs.com/package/@orpc/zod): More schemas that [Zod](https://zod.dev/) doesn't support yet. | ||
| - [@orpc/valibot](https://www.npmjs.com/package/@orpc/valibot): OpenAPI spec generation from [Valibot](https://valibot.dev/). | ||
| - [@orpc/arktype](https://www.npmjs.com/package/@orpc/arktype): OpenAPI spec generation from [ArkType](https://arktype.io/). | ||
|
|
||
| ## `@orpc/json-schema` | ||
|
|
||
| Json Schema related utilities for oRPC. | ||
|
|
||
| ## Sponsors | ||
|
|
||
| <p align="center"> | ||
| <a href="https://cdn.jsdelivr.net/gh/unnoq/unnoq/sponsors.svg"> | ||
| <img src='https://cdn.jsdelivr.net/gh/unnoq/unnoq/sponsors.svg'/> | ||
| </a> | ||
| </p> | ||
|
|
||
| ## License | ||
|
|
||
| Distributed under the MIT License. See [LICENSE](https://github.com/unnoq/orpc/blob/main/LICENSE) for more information. | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,46 @@ | ||
| { | ||
| "name": "@orpc/json-schema", | ||
| "type": "module", | ||
| "version": "0.0.0", | ||
| "license": "MIT", | ||
| "homepage": "https://orpc.unnoq.com", | ||
| "repository": { | ||
| "type": "git", | ||
| "url": "git+https://github.com/unnoq/orpc.git", | ||
| "directory": "packages/json-schema" | ||
| }, | ||
| "keywords": [ | ||
| "unnoq", | ||
| "orpc" | ||
| ], | ||
| "publishConfig": { | ||
| "exports": { | ||
| ".": { | ||
| "types": "./dist/index.d.mts", | ||
| "import": "./dist/index.mjs", | ||
| "default": "./dist/index.mjs" | ||
| } | ||
| } | ||
| }, | ||
| "exports": { | ||
| ".": "./src/index.ts" | ||
| }, | ||
| "files": [ | ||
| "dist" | ||
| ], | ||
| "scripts": { | ||
| "build": "unbuild", | ||
| "build:watch": "pnpm run build --watch", | ||
| "type:check": "tsc -b" | ||
| }, | ||
| "dependencies": { | ||
| "@orpc/contract": "workspace:*", | ||
| "@orpc/openapi": "workspace:*", | ||
| "@orpc/server": "workspace:*", | ||
| "@orpc/shared": "workspace:*", | ||
| "json-schema-typed": "^8.0.1" | ||
| }, | ||
| "devDependencies": { | ||
| "zod": "^3.25.74" | ||
| } | ||
| } |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.