@orpc/json-schema 2.0.0-beta.3 β†’ 2.0.0-beta.31

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.
package/README.md CHANGED
@@ -4,9 +4,12 @@
4
4
  <a href="https://codecov.io/gh/middleapi/orpc">
5
5
  <img alt="codecov" src="https://codecov.io/gh/middleapi/orpc/branch/main/graph/badge.svg">
6
6
  </a>
7
- <a href="https://www.npmjs.com/package/@orpc/json-schema">
7
+ <a href="https://npmx.dev/package/@orpc/json-schema">
8
8
  <img alt="weekly downloads" src="https://img.shields.io/npm/dw/%40orpc%2Fjson-schema?logo=npm" />
9
9
  </a>
10
+ <a href="https://app.codspeed.io/middleapi/orpc?utm_source=badge">
11
+ <img alt="CodSpeed" src="https://img.shields.io/endpoint?url=https://codspeed.io/badge.json" />
12
+ </a>
10
13
  <a href="https://github.com/middleapi/orpc/blob/main/LICENSE">
11
14
  <img alt="MIT License" src="https://img.shields.io/github/license/middleapi/orpc?logo=open-source-initiative" />
12
15
  </a>
@@ -26,60 +29,63 @@ You can read the documentation [here](https://orpc.dev).
26
29
 
27
30
  **Core**
28
31
 
29
- - [@orpc/contract](https://www.npmjs.com/package/@orpc/contract): Define type-safe contracts for your API.
30
- - [@orpc/server](https://www.npmjs.com/package/@orpc/server): Implement your API or contract on the server.
31
- - [@orpc/client](https://www.npmjs.com/package/@orpc/client): Consume your API from the client with full type safety.
32
- - [@orpc/openapi](https://www.npmjs.com/package/@orpc/openapi): Bring OpenAPI compatibility to your APIs.
32
+ - [@orpc/contract](https://npmx.dev/package/@orpc/contract): Define API contract as the single source of truth.
33
+ - [@orpc/server](https://npmx.dev/package/@orpc/server): Build APIs or implement contracts.
34
+ - [@orpc/client](https://npmx.dev/package/@orpc/client): Consume APIs with end-to-end type safety.
35
+ - [@orpc/openapi](https://npmx.dev/package/@orpc/openapi): Add OpenAPI compatibility to APIs.
33
36
 
34
37
  **Schema validation**
35
38
 
36
- - [@orpc/zod](https://www.npmjs.com/package/@orpc/zod): Integrate with [Zod](https://zod.dev/).
37
- - [@orpc/valibot](https://www.npmjs.com/package/@orpc/valibot): Integrate with [Valibot](https://valibot.dev/).
38
- - [@orpc/arktype](https://www.npmjs.com/package/@orpc/arktype): Integrate with [ArkType](https://arktype.io/).
39
+ - [@orpc/zod](https://npmx.dev/package/@orpc/zod): Integrate with [Zod](https://zod.dev/).
40
+ - [@orpc/valibot](https://npmx.dev/package/@orpc/valibot): Integrate with [Valibot](https://valibot.dev/).
41
+ - [@orpc/arktype](https://npmx.dev/package/@orpc/arktype): Integrate with [ArkType](https://arktype.io/).
39
42
 
40
- **Framework & ecosystem integrations**
43
+ **Built-in features**
41
44
 
42
- - [@orpc/next](https://www.npmjs.com/package/@orpc/react): Use oRPC with [Next.js Server Functions](https://nextjs.org/docs/app/getting-started/mutating-data).
43
- - [@orpc/tanstack-query](https://www.npmjs.com/package/@orpc/tanstack-query): Integrate with [TanStack Query](https://tanstack.com/query/latest).
44
- - [@orpc/experimental-effect](https://www.npmjs.com/package/@orpc/experimental-effect): Integrate with [Effect](https://effect.website/).
45
+ - [@orpc/publisher](https://npmx.dev/package/@orpc/publisher): Pub/Sub with memory, Redis, and Upstash adapters.
46
+ - [@orpc/ratelimit](https://npmx.dev/package/@orpc/ratelimit): Rate limiting with memory, Redis, and Upstash adapters.
47
+ - [@orpc/hibernation](https://npmx.dev/package/@orpc/hibernation): Leverage Hibernation APIs like [Cloudflare's Hibernation WebSocket](https://developers.cloudflare.com/durable-objects/best-practices/websockets/#durable-objects-hibernation-websocket-api).
48
+ - [@orpc/json-schema](https://npmx.dev/package/@orpc/json-schema): Smart coercion for OpenAPI requests.
45
49
 
46
- **Built-in features**
50
+ **Framework & ecosystem integrations**
47
51
 
48
- - [@orpc/publisher](https://www.npmjs.com/package/@orpc/publisher): Pub/Sub.
49
- - [@orpc/ratelimit](https://www.npmjs.com/package/@orpc/ratelimit): Rate limiting.
50
- - [@orpc/json-schema](https://www.npmjs.com/package/@orpc/json-schema): Smart coercion for OpenAPI requests.
52
+ - [@orpc/next](https://npmx.dev/package/@orpc/next): Integrate with [Next.js Server Functions](https://nextjs.org/docs/app/getting-started/mutating-data).
53
+ - [@orpc/ai-sdk](https://npmx.dev/package/@orpc/ai-sdk): Turn contracts and procedures into [AI SDK](https://ai-sdk.dev/) tools.
54
+ - [@orpc/tanstack-query](https://npmx.dev/package/@orpc/tanstack-query): Integrate with [TanStack Query](https://tanstack.com/query/latest).
55
+ - [@orpc/pinia-colada](https://npmx.dev/package/@orpc/pinia-colada): Integrate with [Pinia Colada](https://pinia-colada.esm.dev/).
56
+ - [@orpc/swr](https://npmx.dev/package/@orpc/swr): Integrate with [SWR](https://swr.vercel.app/).
57
+ - [@orpc/experimental-msw](https://npmx.dev/package/@orpc/experimental-msw): Mock procedures with [Mock Service Worker](https://mswjs.io/).
58
+ - [@orpc/experimental-effect](https://npmx.dev/package/@orpc/experimental-effect): Integrate with [Effect](https://effect.website/).
59
+ - [@orpc/nest](https://npmx.dev/package/@orpc/nest): Implement your contract with [NestJS](https://nestjs.com/).
60
+ - [@orpc/node](https://npmx.dev/package/@orpc/node): [Node.js](https://nodejs.org/) plugins for static file serving and large uploads.
61
+ - [@orpc/bun](https://npmx.dev/package/@orpc/bun): Adapters for [Bun's Redis](https://bun.sh/).
62
+ - [@orpc/cloudflare](https://npmx.dev/package/@orpc/cloudflare): Adapters for [Cloudflare's RateLimit and Durable Objects](https://developers.cloudflare.com/workers/).
63
+ - [@orpc/trpc](https://npmx.dev/package/@orpc/trpc): Reuse existing [tRPC](https://trpc.io/) routers within oRPC.
51
64
 
52
65
  **Observability**
53
66
 
54
- - [@orpc/opentelemetry](https://www.npmjs.com/package/@orpc/opentelemetry): Instrument your API with [OpenTelemetry](https://opentelemetry.io/).
55
- - [@orpc/pino](https://www.npmjs.com/package/@orpc/pino): Log with [Pino](https://getpino.io/).
56
- - [@orpc/evlog](https://www.npmjs.com/package/@orpc/evlog): Log with [Evlog](https://evlog.dev/).
67
+ - [@orpc/opentelemetry](https://npmx.dev/package/@orpc/opentelemetry): Integrate with [OpenTelemetry](https://opentelemetry.io/) for distributed tracing.
68
+ - [@orpc/pino](https://npmx.dev/package/@orpc/pino): Integrate with [Pino](https://getpino.io/) for logging.
69
+ - [@orpc/evlog](https://npmx.dev/package/@orpc/evlog): Integrate with [Evlog](https://evlog.dev/) for logging.
57
70
 
58
71
  ## Sponsors
59
72
 
60
- Like what we build over at [middleapi](https://github.com/middleapi)? You can help keep it going here: [GitHub Sponsors](https://github.com/sponsors/dinwwwh). Every bit helps! πŸš€
61
-
62
- ### πŸ† Platinum Sponsor
73
+ Like what we build over at [middleapi](https://github.com/middleapi)? You can help keep it going through [GitHub Sponsors](https://github.com/sponsors/dinwwwh) or [Open Collective](https://opencollective.com/middleapi). Every bit helps! πŸš€
63
74
 
64
75
  <table>
65
76
  <tr>
66
- <td align="center"><a href="https://screenshotone.com/?ref=orpc" target="_blank" rel="noopener" title="ScreenshotOne.com"><img src="https://avatars.githubusercontent.com/u/97035603?v=4" width="279" alt="ScreenshotOne.com"/><br />ScreenshotOne.com</a></td>
77
+ <td width="2000"><a href="https://screenshotone.com/?ref=middleapi&amp;utm_source=middleapi&amp;utm_medium=sponsor" target="_blank" rel="noopener" title="The screenshot API for developers"><img src="https://avatars.githubusercontent.com/u/97035603?v=4" width="64" align="left" hspace="12" alt="ScreenshotOne.com"/><b>ScreenshotOne.com</b></a><br /><sub>The screenshot API for developers</sub></td>
67
78
  </tr>
68
- </table>
69
-
70
- ### πŸ₯ˆ Silver Sponsor
71
-
72
- <table>
73
79
  <tr>
74
- <td align="center"><a href="https://misskey.io/?ref=orpc" target="_blank" rel="noopener" title="ζ‘δΈŠγ•γ‚“"><img src="https://avatars.githubusercontent.com/u/37681609?u=0dd4c7e4ba937cbb52b068c55914b1d8164dc0c7&amp;v=4" width="209" alt="ζ‘δΈŠγ•γ‚“"/><br />ζ‘δΈŠγ•γ‚“</a></td>
80
+ <td width="2000"><a href="https://misskey.io/?ref=middleapi&amp;utm_source=middleapi&amp;utm_medium=sponsor" target="_blank" rel="noopener sponsored" title="Decentralized microblogging SNS born on Earth"><img src="https://github.com/MisskeyIO.png" width="64" align="left" hspace="12" alt="MisskeyHQ"/><b>MisskeyHQ</b></a><br /><sub>Decentralized microblogging SNS born on Earth</sub></td>
75
81
  </tr>
76
82
  </table>
77
83
 
78
- ### Generous Sponsors
84
+ ### Organization Sponsors
79
85
 
80
86
  <table>
81
87
  <tr>
82
- <td align="center"><a href="https://github.com/ln-markets?ref=orpc" target="_blank" rel="noopener" title="LN Markets"><img src="https://avatars.githubusercontent.com/u/70597625?v=4" width="167" alt="LN Markets"/><br />LN Markets</a></td>
88
+ <td align="center"><a href="https://lnmarkets.com/?ref=middleapi&amp;utm_source=middleapi&amp;utm_medium=sponsor" target="_blank" rel="noopener sponsored" title="LN Markets"><img src="https://avatars.githubusercontent.com/u/70597625?v=4" width="167" alt="LN Markets"/><br />LN Markets</a></td>
83
89
  </tr>
84
90
  </table>
85
91
 
@@ -87,26 +93,26 @@ Like what we build over at [middleapi](https://github.com/middleapi)? You can he
87
93
 
88
94
  <table>
89
95
  <tr>
90
- <td align="center"><a href="https://github.com/hrmcdonald?ref=orpc" target="_blank" rel="noopener" title="Reece McDonald"><img src="https://avatars.githubusercontent.com/u/39349270?v=4" width="139" alt="Reece McDonald"/><br />Reece McDonald</a></td>
91
- <td align="center"><a href="https://github.com/nicognaW?ref=orpc" target="_blank" rel="noopener" title="nk"><img src="https://avatars.githubusercontent.com/u/66731869?u=4699bda3a9092d3ec34fbd959450767bcc8b8b6d&amp;v=4" width="139" alt="nk"/><br />nk</a></td>
92
- <td align="center"><a href="https://github.com/supastarter?ref=orpc" target="_blank" rel="noopener" title="supastarter"><img src="https://avatars.githubusercontent.com/u/110960143?v=4" width="139" alt="supastarter"/><br />supastarter</a></td>
93
- <td align="center"><a href="https://github.com/divmgl?ref=orpc" target="_blank" rel="noopener" title="Dexter Miguel"><img src="https://avatars.githubusercontent.com/u/5452298?u=645993204be8696c085ecf0d228c3062efe2ed65&amp;v=4" width="139" alt="Dexter Miguel"/><br />Dexter Miguel</a></td>
94
- <td align="center"><a href="https://github.com/herrfugbaum?ref=orpc" target="_blank" rel="noopener" title="herrfugbaum"><img src="https://avatars.githubusercontent.com/u/12859776?u=644dc1666d0220bc0468eb0de3c56b919f635b16&amp;v=4" width="139" alt="herrfugbaum"/><br />herrfugbaum</a></td>
95
- <td align="center"><a href="https://github.com/ryota-murakami?ref=orpc" target="_blank" rel="noopener" title="Ryota Murakami"><img src="https://avatars.githubusercontent.com/u/5501268?u=599389e03340734325726ca3f8f423c021d47d7f&amp;v=4" width="139" alt="Ryota Murakami"/><br />Ryota Murakami</a></td>
96
+ <td align="center"><a href="https://github.com/hrmcdonald?ref=middleapi&amp;utm_source=middleapi&amp;utm_medium=sponsor" target="_blank" rel="noopener sponsored" title="Reece McDonald"><img src="https://avatars.githubusercontent.com/u/39349270?v=4" width="139" alt="Reece McDonald"/><br />Reece McDonald</a></td>
97
+ <td align="center"><a href="https://soymilk.party/?ref=middleapi&amp;utm_source=middleapi&amp;utm_medium=sponsor" target="_blank" rel="noopener sponsored" title="あわわわとーにゅ"><img src="https://avatars.githubusercontent.com/u/17376330?u=de3353804be889f009f7e0a1582daf04d0ab292d&amp;v=4" width="139" alt="あわわわとーにゅ"/><br />あわわわとーにゅ</a></td>
98
+ <td align="center"><a href="https://github.com/nicognaW?ref=middleapi&amp;utm_source=middleapi&amp;utm_medium=sponsor" target="_blank" rel="noopener sponsored" title="nk"><img src="https://avatars.githubusercontent.com/u/66731869?u=4699bda3a9092d3ec34fbd959450767bcc8b8b6d&amp;v=4" width="139" alt="nk"/><br />nk</a></td>
99
+ <td align="center"><a href="https://supastarter.dev/?ref=middleapi&amp;utm_source=middleapi&amp;utm_medium=sponsor" target="_blank" rel="noopener sponsored" title="supastarter"><img src="https://avatars.githubusercontent.com/u/110960143?v=4" width="139" alt="supastarter"/><br />supastarter</a></td>
100
+ <td align="center"><a href="https://github.com/divmgl?ref=middleapi&amp;utm_source=middleapi&amp;utm_medium=sponsor" target="_blank" rel="noopener sponsored" title="Dexter Miguel"><img src="https://avatars.githubusercontent.com/u/5452298?u=645993204be8696c085ecf0d228c3062efe2ed65&amp;v=4" width="139" alt="Dexter Miguel"/><br />Dexter Miguel</a></td>
101
+ <td align="center"><a href="https://github.com/herrfugbaum?ref=middleapi&amp;utm_source=middleapi&amp;utm_medium=sponsor" target="_blank" rel="noopener sponsored" title="herrfugbaum"><img src="https://avatars.githubusercontent.com/u/12859776?u=644dc1666d0220bc0468eb0de3c56b919f635b16&amp;v=4" width="139" alt="herrfugbaum"/><br />herrfugbaum</a></td>
96
102
  </tr>
97
103
  <tr>
98
- <td align="center"><a href="https://github.com/dcramer?ref=orpc" target="_blank" rel="noopener" title="David Cramer"><img src="https://avatars.githubusercontent.com/u/23610?v=4" width="139" alt="David Cramer"/><br />David Cramer</a></td>
99
- <td align="center"><a href="https://github.com/valerii15298?ref=orpc" target="_blank" rel="noopener" title="Valerii Petryniak"><img src="https://avatars.githubusercontent.com/u/44531564?u=88ac74d9bacd20401518441907acad21063cd397&amp;v=4" width="139" alt="Valerii Petryniak"/><br />Valerii Petryniak</a></td>
100
- <td align="center"><a href="https://github.com/letstri?ref=orpc" target="_blank" rel="noopener" title="Valerii Strilets"><img src="https://avatars.githubusercontent.com/u/13253748?u=c7b10399ccc8f8081e24db94ec32cd9858e86ac3&amp;v=4" width="139" alt="Valerii Strilets"/><br />Valerii Strilets</a></td>
101
- <td align="center"><a href="https://github.com/K-Mistele?ref=orpc" target="_blank" rel="noopener" title="Kyle Mistele"><img src="https://avatars.githubusercontent.com/u/18430555?u=3afebeb81de666e35aaac3ed46f14159d7603ffb&amp;v=4" width="139" alt="Kyle Mistele"/><br />Kyle Mistele</a></td>
102
- <td align="center"><a href="https://github.com/andrewpeters9?ref=orpc" target="_blank" rel="noopener" title="Andrew Peters"><img src="https://avatars.githubusercontent.com/u/36251325?v=4" width="139" alt="Andrew Peters"/><br />Andrew Peters</a></td>
103
- <td align="center"><a href="https://github.com/R44VC0RP?ref=orpc" target="_blank" rel="noopener" title="Ryan Vogel"><img src="https://avatars.githubusercontent.com/u/89211796?u=1857347b9787d8d8a7ea5bfc333f96be92d5a683&amp;v=4" width="139" alt="Ryan Vogel"/><br />Ryan Vogel</a></td>
104
+ <td align="center"><a href="https://laststance.io/?ref=middleapi&amp;utm_source=middleapi&amp;utm_medium=sponsor" target="_blank" rel="noopener sponsored" title="Ryota Murakami"><img src="https://avatars.githubusercontent.com/u/5501268?u=599389e03340734325726ca3f8f423c021d47d7f&amp;v=4" width="139" alt="Ryota Murakami"/><br />Ryota Murakami</a></td>
105
+ <td align="center"><a href="https://cra.mr/?ref=middleapi&amp;utm_source=middleapi&amp;utm_medium=sponsor" target="_blank" rel="noopener sponsored" title="David Cramer"><img src="https://avatars.githubusercontent.com/u/23610?v=4" width="139" alt="David Cramer"/><br />David Cramer</a></td>
106
+ <td align="center"><a href="https://valerii15298.github.io/?ref=middleapi&amp;utm_source=middleapi&amp;utm_medium=sponsor" target="_blank" rel="noopener sponsored" title="Valerii Petryniak"><img src="https://avatars.githubusercontent.com/u/44531564?u=88ac74d9bacd20401518441907acad21063cd397&amp;v=4" width="139" alt="Valerii Petryniak"/><br />Valerii Petryniak</a></td>
107
+ <td align="center"><a href="https://letstri.dev/?ref=middleapi&amp;utm_source=middleapi&amp;utm_medium=sponsor" target="_blank" rel="noopener sponsored" title="Valerii Strilets"><img src="https://avatars.githubusercontent.com/u/13253748?u=c7b10399ccc8f8081e24db94ec32cd9858e86ac3&amp;v=4" width="139" alt="Valerii Strilets"/><br />Valerii Strilets</a></td>
108
+ <td align="center"><a href="https://blacklight.sh/?ref=middleapi&amp;utm_source=middleapi&amp;utm_medium=sponsor" target="_blank" rel="noopener sponsored" title="Kyle Mistele"><img src="https://avatars.githubusercontent.com/u/18430555?u=3afebeb81de666e35aaac3ed46f14159d7603ffb&amp;v=4" width="139" alt="Kyle Mistele"/><br />Kyle Mistele</a></td>
109
+ <td align="center"><a href="https://github.com/christ12938?ref=middleapi&amp;utm_source=middleapi&amp;utm_medium=sponsor" target="_blank" rel="noopener sponsored" title="christ12938"><img src="https://avatars.githubusercontent.com/u/25758598?v=4" width="139" alt="christ12938"/><br />christ12938</a></td>
104
110
  </tr>
105
111
  <tr>
106
- <td align="center"><a href="https://github.com/christ12938?ref=orpc" target="_blank" rel="noopener" title="christ12938"><img src="https://avatars.githubusercontent.com/u/25758598?v=4" width="139" alt="christ12938"/><br />christ12938</a></td>
107
- <td align="center"><a href="https://github.com/peter-adam-dy?ref=orpc" target="_blank" rel="noopener" title="Peter Adam"><img src="https://avatars.githubusercontent.com/u/132129459?u=4f3dbbb3b443990b56acb7d6a5d11ed2c555f6db&amp;v=4" width="139" alt="Peter Adam"/><br />Peter Adam</a></td>
108
- <td align="center"><a href="https://github.com/Ryanjso?ref=orpc" target="_blank" rel="noopener" title="Ryan Soderberg"><img src="https://avatars.githubusercontent.com/u/39172778?u=5ed913c31d57e7221b75784abcad48c7ebddde27&amp;v=4" width="139" alt="Ryan Soderberg"/><br />Ryan Soderberg</a></td>
109
- <td align="center"><a href="https://github.com/itigoore01?ref=orpc" target="_blank" rel="noopener" title="shota"><img src="https://avatars.githubusercontent.com/u/11831107?u=c976a6dc7e055eb026304c46c99100ed22b0c8e0&amp;v=4" width="139" alt="shota"/><br />shota</a></td>
112
+ <td align="center"><a href="https://github.com/Ryanjso?ref=middleapi&amp;utm_source=middleapi&amp;utm_medium=sponsor" target="_blank" rel="noopener sponsored" title="Ryan Soderberg"><img src="https://avatars.githubusercontent.com/u/39172778?u=5ed913c31d57e7221b75784abcad48c7ebddde27&amp;v=4" width="139" alt="Ryan Soderberg"/><br />Ryan Soderberg</a></td>
113
+ <td align="center"><a href="https://github.com/itigoore01?ref=middleapi&amp;utm_source=middleapi&amp;utm_medium=sponsor" target="_blank" rel="noopener sponsored" title="shota"><img src="https://avatars.githubusercontent.com/u/11831107?u=c976a6dc7e055eb026304c46c99100ed22b0c8e0&amp;v=4" width="139" alt="shota"/><br />shota</a></td>
114
+ <td align="center"><a href="https://github.com/ellis-driscoll?ref=middleapi&amp;utm_source=middleapi&amp;utm_medium=sponsor" target="_blank" rel="noopener sponsored" title="Ellis Driscoll"><img src="https://avatars.githubusercontent.com/u/70685966?u=c5f95bc33b5991d9744abe00052542e4a2ed3cb9&amp;v=4" width="139" alt="Ellis Driscoll"/><br />Ellis Driscoll</a></td>
115
+ <td align="center"><a href="https://github.com/hoangbn?ref=middleapi&amp;utm_source=middleapi&amp;utm_medium=sponsor" target="_blank" rel="noopener sponsored" title="Hoang Nguyen"><img src="https://avatars.githubusercontent.com/u/38968280?u=c90084c6de65c56facabab7ba13a72a49ddbc3e4&amp;v=4" width="139" alt="Hoang Nguyen"/><br />Hoang Nguyen</a></td>
110
116
  </tr>
111
117
  </table>
112
118
 
@@ -114,67 +120,31 @@ Like what we build over at [middleapi](https://github.com/middleapi)? You can he
114
120
 
115
121
  <table>
116
122
  <tr>
117
- <td align="center"><a href="https://github.com/rhinodavid?ref=orpc" target="_blank" rel="noopener" title="David Walsh"><img src="https://avatars.githubusercontent.com/u/5778036?u=b5521f07d2f88c3db2a0dae62b5f2f8357214af0&amp;v=4" width="119" alt="David Walsh"/><br />David Walsh</a></td>
118
- <td align="center"><a href="https://github.com/Robbe95?ref=orpc" target="_blank" rel="noopener" title="Robbe Vaes"><img src="https://avatars.githubusercontent.com/u/44748019?u=e0232402c045ad4eac7cbd217f1f47e083103b89&amp;v=4" width="119" alt="Robbe Vaes"/><br />Robbe Vaes</a></td>
119
- <td align="center"><a href="https://github.com/aidansunbury?ref=orpc" target="_blank" rel="noopener" title="Aidan Sunbury"><img src="https://avatars.githubusercontent.com/u/64103161?v=4" width="119" alt="Aidan Sunbury"/><br />Aidan Sunbury</a></td>
120
- <td align="center"><a href="https://github.com/soonoo?ref=orpc" target="_blank" rel="noopener" title="soonoo"><img src="https://avatars.githubusercontent.com/u/5436405?u=5d0b4aa955c87e30e6bda7f0cccae5402da99528&amp;v=4" width="119" alt="soonoo"/><br />soonoo</a></td>
121
- <td align="center"><a href="https://github.com/kporten?ref=orpc" target="_blank" rel="noopener" title="Kevin Porten"><img src="https://avatars.githubusercontent.com/u/1839345?u=dc2263d5cfe0d927ce1a0be04a1d55dd6b55405c&amp;v=4" width="119" alt="Kevin Porten"/><br />Kevin Porten</a></td>
122
- <td align="center"><a href="https://github.com/pumpkinlink?ref=orpc" target="_blank" rel="noopener" title="Denis"><img src="https://avatars.githubusercontent.com/u/11864620?u=5f47bbe6c65d0f6f5cf011021490238e4b0593d0&amp;v=4" width="119" alt="Denis"/><br />Denis</a></td>
123
- <td align="center"><a href="https://github.com/christopher-kapic?ref=orpc" target="_blank" rel="noopener" title="Christopher Kapic"><img src="https://avatars.githubusercontent.com/u/59740769?u=e7ad4b72b5bf6c9eb1644c26dbf3332a8f987377&amp;v=4" width="119" alt="Christopher Kapic"/><br />Christopher Kapic</a></td>
123
+ <td align="center"><a href="https://github.com/rhinodavid?ref=middleapi&amp;utm_source=middleapi&amp;utm_medium=sponsor" target="_blank" rel="noopener sponsored" title="David Walsh"><img src="https://avatars.githubusercontent.com/u/5778036?u=b5521f07d2f88c3db2a0dae62b5f2f8357214af0&amp;v=4" width="119" alt="David Walsh"/><br />David Walsh</a></td>
124
+ <td align="center"><a href="https://robbevaes.be/?ref=middleapi&amp;utm_source=middleapi&amp;utm_medium=sponsor" target="_blank" rel="noopener sponsored" title="Robbe Vaes"><img src="https://avatars.githubusercontent.com/u/44748019?u=e0232402c045ad4eac7cbd217f1f47e083103b89&amp;v=4" width="119" alt="Robbe Vaes"/><br />Robbe Vaes</a></td>
125
+ <td align="center"><a href="https://github.com/aidansunbury?ref=middleapi&amp;utm_source=middleapi&amp;utm_medium=sponsor" target="_blank" rel="noopener sponsored" title="Aidan Sunbury"><img src="https://avatars.githubusercontent.com/u/64103161?v=4" width="119" alt="Aidan Sunbury"/><br />Aidan Sunbury</a></td>
126
+ <td align="center"><a href="https://github.com/soonoo?ref=middleapi&amp;utm_source=middleapi&amp;utm_medium=sponsor" target="_blank" rel="noopener sponsored" title="soonoo"><img src="https://avatars.githubusercontent.com/u/5436405?u=5d0b4aa955c87e30e6bda7f0cccae5402da99528&amp;v=4" width="119" alt="soonoo"/><br />soonoo</a></td>
127
+ <td align="center"><a href="https://kevinporten.dev/?ref=middleapi&amp;utm_source=middleapi&amp;utm_medium=sponsor" target="_blank" rel="noopener sponsored" title="Kevin Porten"><img src="https://avatars.githubusercontent.com/u/1839345?u=dc2263d5cfe0d927ce1a0be04a1d55dd6b55405c&amp;v=4" width="119" alt="Kevin Porten"/><br />Kevin Porten</a></td>
128
+ <td align="center"><a href="https://github.com/pumpkinlink?ref=middleapi&amp;utm_source=middleapi&amp;utm_medium=sponsor" target="_blank" rel="noopener sponsored" title="Denis"><img src="https://avatars.githubusercontent.com/u/11864620?u=5f47bbe6c65d0f6f5cf011021490238e4b0593d0&amp;v=4" width="119" alt="Denis"/><br />Denis</a></td>
129
+ <td align="center"><a href="https://github.com/christopher-kapic?ref=middleapi&amp;utm_source=middleapi&amp;utm_medium=sponsor" target="_blank" rel="noopener sponsored" title="Christopher Kapic"><img src="https://avatars.githubusercontent.com/u/59740769?u=e7ad4b72b5bf6c9eb1644c26dbf3332a8f987377&amp;v=4" width="119" alt="Christopher Kapic"/><br />Christopher Kapic</a></td>
124
130
  </tr>
125
131
  <tr>
126
- <td align="center"><a href="https://github.com/thomasballinger?ref=orpc" target="_blank" rel="noopener" title="Tom Ballinger"><img src="https://avatars.githubusercontent.com/u/458879?u=4b045ac75d721b6ac2b42a74d7d37f61f0414031&amp;v=4" width="119" alt="Tom Ballinger"/><br />Tom Ballinger</a></td>
127
- <td align="center"><a href="https://github.com/SSam0419?ref=orpc" target="_blank" rel="noopener" title="Sam"><img src="https://avatars.githubusercontent.com/u/102863520?u=3c89611f549d5070be232eb4532f690c8f2e7a65&amp;v=4" width="119" alt="Sam"/><br />Sam</a></td>
128
- <td align="center"><a href="https://github.com/Titoine?ref=orpc" target="_blank" rel="noopener" title="Titoine"><img src="https://avatars.githubusercontent.com/u/3514286?u=1bb1e86b0c99c8a1121372e56d51a177eea12191&amp;v=4" width="119" alt="Titoine"/><br />Titoine</a></td>
129
- <td align="center"><a href="https://github.com/Mnigos?ref=orpc" target="_blank" rel="noopener" title="Igor Makowski"><img src="https://avatars.githubusercontent.com/u/56691628?u=ee8c879478f7c151b9156aef6c74243fa3e247a8&amp;v=4" width="119" alt="Igor Makowski"/><br />Igor Makowski</a></td>
130
- <td align="center"><a href="https://github.com/hanayashiki?ref=orpc" target="_blank" rel="noopener" title="hanayashiki"><img src="https://avatars.githubusercontent.com/u/26056783?u=06c3b9205a16fd41a871e82da1cc2a09306d53f5&amp;v=4" width="119" alt="hanayashiki"/><br />hanayashiki</a></td>
131
- <td align="center"><a href="https://github.com/ldub?ref=orpc" target="_blank" rel="noopener" title="Lev Dubinets"><img src="https://avatars.githubusercontent.com/u/3114081?u=f547f5d5012cab54851f1b1ad72d10e537f78fc2&amp;v=4" width="119" alt="Lev Dubinets"/><br />Lev Dubinets</a></td>
132
- <td align="center"><a href="https://github.com/mr-kelly?ref=orpc" target="_blank" rel="noopener" title="Kelly Peilin Chan"><img src="https://avatars.githubusercontent.com/u/520852?u=6b0f7105f694e7b5cacf410a3f04c7044b469dc8&amp;v=4" width="119" alt="Kelly Peilin Chan"/><br />Kelly Peilin Chan</a></td>
132
+ <td align="center"><a href="http://ballingt.com/?ref=middleapi&amp;utm_source=middleapi&amp;utm_medium=sponsor" target="_blank" rel="noopener sponsored" title="Tom Ballinger"><img src="https://avatars.githubusercontent.com/u/458879?u=4b045ac75d721b6ac2b42a74d7d37f61f0414031&amp;v=4" width="119" alt="Tom Ballinger"/><br />Tom Ballinger</a></td>
133
+ <td align="center"><a href="https://lee-sam.com/?ref=middleapi&amp;utm_source=middleapi&amp;utm_medium=sponsor" target="_blank" rel="noopener sponsored" title="Sam"><img src="https://avatars.githubusercontent.com/u/102863520?u=3c89611f549d5070be232eb4532f690c8f2e7a65&amp;v=4" width="119" alt="Sam"/><br />Sam</a></td>
134
+ <td align="center"><a href="https://github.com/Titoine?ref=middleapi&amp;utm_source=middleapi&amp;utm_medium=sponsor" target="_blank" rel="noopener sponsored" title="Titoine"><img src="https://avatars.githubusercontent.com/u/3514286?u=1bb1e86b0c99c8a1121372e56d51a177eea12191&amp;v=4" width="119" alt="Titoine"/><br />Titoine</a></td>
135
+ <td align="center"><a href="https://rigtch.fm/?ref=middleapi&amp;utm_source=middleapi&amp;utm_medium=sponsor" target="_blank" rel="noopener sponsored" title="Igor Makowski"><img src="https://avatars.githubusercontent.com/u/56691628?u=ee8c879478f7c151b9156aef6c74243fa3e247a8&amp;v=4" width="119" alt="Igor Makowski"/><br />Igor Makowski</a></td>
136
+ <td align="center"><a href="https://blog.cwang.io/?ref=middleapi&amp;utm_source=middleapi&amp;utm_medium=sponsor" target="_blank" rel="noopener sponsored" title="hanayashiki"><img src="https://avatars.githubusercontent.com/u/26056783?u=06c3b9205a16fd41a871e82da1cc2a09306d53f5&amp;v=4" width="119" alt="hanayashiki"/><br />hanayashiki</a></td>
137
+ <td align="center"><a href="https://dubinets.io/?ref=middleapi&amp;utm_source=middleapi&amp;utm_medium=sponsor" target="_blank" rel="noopener sponsored" title="Lev Dubinets"><img src="https://avatars.githubusercontent.com/u/3114081?u=f547f5d5012cab54851f1b1ad72d10e537f78fc2&amp;v=4" width="119" alt="Lev Dubinets"/><br />Lev Dubinets</a></td>
138
+ <td align="center"><a href="https://bika.ai/?ref=middleapi&amp;utm_source=middleapi&amp;utm_medium=sponsor" target="_blank" rel="noopener sponsored" title="Kelly Peilin Chan"><img src="https://avatars.githubusercontent.com/u/520852?u=6b0f7105f694e7b5cacf410a3f04c7044b469dc8&amp;v=4" width="119" alt="Kelly Peilin Chan"/><br />Kelly Peilin Chan</a></td>
133
139
  </tr>
134
140
  <tr>
135
- <td align="center"><a href="https://github.com/piscis?ref=orpc" target="_blank" rel="noopener" title="Alex"><img src="https://avatars.githubusercontent.com/u/326163?u=b245f368bd940cf51d08c0b6bf55f8257f359437&amp;v=4" width="119" alt="Alex"/><br />Alex</a></td>
136
- <td align="center"><a href="https://github.com/nattstack?ref=orpc" target="_blank" rel="noopener" title="nattstack"><img src="https://avatars.githubusercontent.com/u/31426677?u=fa9dbb8b3e66eb0ea3c88db5dc07f31c8c5418fe&amp;v=4" width="119" alt="nattstack"/><br />nattstack</a></td>
137
- <td align="center"><a href="https://github.com/finom?ref=orpc" target="_blank" rel="noopener" title="Andrey Gubanov"><img src="https://avatars.githubusercontent.com/u/1082083?u=29e91400dbd4a9c217048a8f59562c4f740498e6&amp;v=4" width="119" alt="Andrey Gubanov"/><br />Andrey Gubanov</a></td>
141
+ <td align="center"><a href="https://guyariely.com/?ref=middleapi&amp;utm_source=middleapi&amp;utm_medium=sponsor" target="_blank" rel="noopener sponsored" title="Guy Ariely"><img src="https://avatars.githubusercontent.com/u/42813496?u=edb6b7f563bf28e160a290832e7da57c0506f8ca&amp;v=4" width="119" alt="Guy Ariely"/><br />Guy Ariely</a></td>
142
+ <td align="center"><a href="https://piscis.dev/?ref=middleapi&amp;utm_source=middleapi&amp;utm_medium=sponsor" target="_blank" rel="noopener sponsored" title="Alex"><img src="https://avatars.githubusercontent.com/u/326163?u=b245f368bd940cf51d08c0b6bf55f8257f359437&amp;v=4" width="119" alt="Alex"/><br />Alex</a></td>
143
+ <td align="center"><a href="https://opensource.gubanov.eu/?ref=middleapi&amp;utm_source=middleapi&amp;utm_medium=sponsor" target="_blank" rel="noopener sponsored" title="Andrey Gubanov"><img src="https://avatars.githubusercontent.com/u/1082083?u=c5f2daf7ebece498e85c83367bb37b4e10e2649d&amp;v=4" width="119" alt="Andrey Gubanov"/><br />Andrey Gubanov</a></td>
138
144
  </tr>
139
145
  </table>
140
146
 
141
- ### Past Sponsors
142
-
143
- <p>
144
- <a href="https://github.com/MrMaxie?ref=orpc" target="_blank" rel="noopener" title="Maxie"><img src="https://avatars.githubusercontent.com/u/3857836?u=5e6b57973d4385d655663ffdd836e487856f2984&amp;v=4" width="32" height="32" alt="Maxie" /></a>
145
- <a href="https://github.com/Stijn-Timmer?ref=orpc" target="_blank" rel="noopener" title="Stijn Timmer"><img src="https://avatars.githubusercontent.com/u/100147665?u=106b2c18e9c98a61861b4ee7fc100f5b9906a6c9&amp;v=4" width="32" height="32" alt="Stijn Timmer" /></a>
146
- <a href="https://github.com/u1-liquid?ref=orpc" target="_blank" rel="noopener" title="あわわわとーにゅ"><img src="https://avatars.githubusercontent.com/u/17376330?u=de3353804be889f009f7e0a1582daf04d0ab292d&amp;v=4" width="32" height="32" alt="あわわわとーにゅ" /></a>
147
- <a href="https://github.com/zuplo?ref=orpc" target="_blank" rel="noopener" title="Zuplo"><img src="https://avatars.githubusercontent.com/u/85497839?v=4" width="32" height="32" alt="Zuplo" /></a>
148
- <a href="https://github.com/motopods?ref=orpc" target="_blank" rel="noopener" title="motopods"><img src="https://avatars.githubusercontent.com/u/58200641?v=4" width="32" height="32" alt="motopods" /></a>
149
- <a href="https://github.com/franciscohermida?ref=orpc" target="_blank" rel="noopener" title="Francisco Hermida"><img src="https://avatars.githubusercontent.com/u/483242?u=bbcbc80eb9d8781ff401f7dafc3b59cd7bea0561&amp;v=4" width="32" height="32" alt="Francisco Hermida" /></a>
150
- <a href="https://github.com/theoludwig?ref=orpc" target="_blank" rel="noopener" title="ThΓ©o LUDWIG"><img src="https://avatars.githubusercontent.com/u/25207499?u=a6a9653725a2f574c07893748806668e0598cdbe&amp;v=4" width="32" height="32" alt="ThΓ©o LUDWIG" /></a>
151
- <a href="https://github.com/abhay-ramesh?ref=orpc" target="_blank" rel="noopener" title="Abhay Ramesh"><img src="https://avatars.githubusercontent.com/u/66196314?u=c5c2b0327b26606c2efcfaf17046ab18c3d25c57&amp;v=4" width="32" height="32" alt="Abhay Ramesh" /></a>
152
- <a href="https://github.com/shr-ink?ref=orpc" target="_blank" rel="noopener" title="shr.ink oΓΌ"><img src="https://avatars.githubusercontent.com/u/139700438?v=4" width="32" height="32" alt="shr.ink oΓΌ" /></a>
153
- <a href="https://github.com/johngerome?ref=orpc" target="_blank" rel="noopener" title="0x4e32"><img src="https://avatars.githubusercontent.com/u/2002000?u=24e8dd943cfc862aa284d858a023532c75071ade&amp;v=4" width="32" height="32" alt="0x4e32" /></a>
154
- <a href="https://github.com/yzuyr?ref=orpc" target="_blank" rel="noopener" title="Ryuz"><img src="https://avatars.githubusercontent.com/u/196539378?u=d38374588d219b6748b16406982f6559411466d4&amp;v=4" width="32" height="32" alt="Ryuz" /></a>
155
- <a href="https://github.com/happyboy2022?ref=orpc" target="_blank" rel="noopener" title="happyboy"><img src="https://avatars.githubusercontent.com/u/103669586?u=65b49c4b893ed3703909fbb3a7a22313f3f9c121&amp;v=4" width="32" height="32" alt="happyboy" /></a>
156
- <a href="https://github.com/YiCChi?ref=orpc" target="_blank" rel="noopener" title="yicchi"><img src="https://avatars.githubusercontent.com/u/86967274?u=6c2756f09fe15dd94d572f560e979cd157982852&amp;v=4" width="32" height="32" alt="yicchi" /></a>
157
- <a href="https://github.com/cloudycotton?ref=orpc" target="_blank" rel="noopener" title="Saksham"><img src="https://avatars.githubusercontent.com/u/168998965?u=9b9634a5aed66a51c1b880663272725b00b92b14&amp;v=4" width="32" height="32" alt="Saksham" /></a>
158
- <a href="https://github.com/hrynevychroman?ref=orpc" target="_blank" rel="noopener" title="Roman Hrynevych"><img src="https://avatars.githubusercontent.com/u/82209198?u=1a1d111ab3d589855b9cc8a7fefb1b5c6a4fbbaf&amp;v=4" width="32" height="32" alt="Roman Hrynevych" /></a>
159
- <a href="https://github.com/rokitgg?ref=orpc" target="_blank" rel="noopener" title="rokitg"><img src="https://avatars.githubusercontent.com/u/125133357?u=06c74aefaa2236b06a2e5fba5a5c612339f45912&amp;v=4" width="32" height="32" alt="rokitg" /></a>
160
- <a href="https://github.com/omarkhatibgg?ref=orpc" target="_blank" rel="noopener" title="Omar Khatib"><img src="https://avatars.githubusercontent.com/u/9054278?u=afbba7331b85c51b8eee4130f5fd31b1017dc919&amp;v=4" width="32" height="32" alt="Omar Khatib" /></a>
161
- <a href="https://github.com/YuSabo90002?ref=orpc" target="_blank" rel="noopener" title="Yu-Sabo"><img src="https://avatars.githubusercontent.com/u/13120582?v=4" width="32" height="32" alt="Yu-Sabo" /></a>
162
- <a href="https://github.com/bapspatil?ref=orpc" target="_blank" rel="noopener" title="Bapusaheb Patil"><img src="https://avatars.githubusercontent.com/u/16699418?u=6d9d8e0a64a6f91ca1c4d559c72d931172bdcbbd&amp;v=4" width="32" height="32" alt="Bapusaheb Patil" /></a>
163
- <a href="https://github.com/ripgrim?ref=orpc" target="_blank" rel="noopener" title="grim"><img src="https://avatars.githubusercontent.com/u/75869731?u=b17c42ec2309552fdb822a86b25a2f99146a4d72&amp;v=4" width="32" height="32" alt="grim" /></a>
164
- <a href="https://github.com/nelsonlaidev?ref=orpc" target="_blank" rel="noopener" title="Nelson Lai"><img src="https://avatars.githubusercontent.com/u/75498339?u=2fc0e0b95dd184c5ffb744df977cb15a18b60672&amp;v=4" width="32" height="32" alt="Nelson Lai" /></a>
165
- <a href="https://github.com/nguyenlc1993?ref=orpc" target="_blank" rel="noopener" title="LΓͺ Cao NguyΓͺn"><img src="https://avatars.githubusercontent.com/u/13871971?u=83c8b69d9e35b589c4e1f066cc113b1d9461386f&amp;v=4" width="32" height="32" alt="LΓͺ Cao NguyΓͺn" /></a>
166
- <a href="https://github.com/wobsoriano?ref=orpc" target="_blank" rel="noopener" title="Robert Soriano"><img src="https://avatars.githubusercontent.com/u/13049130?u=6d72104182e7c9ed25934815313fb69107332111&amp;v=4" width="32" height="32" alt="Robert Soriano" /></a>
167
- <a href="https://github.com/SKostyukovich?ref=orpc" target="_blank" rel="noopener" title="SKostyukovich"><img src="https://avatars.githubusercontent.com/u/10700067?v=4" width="32" height="32" alt="SKostyukovich" /></a>
168
- <a href="https://github.com/FabworksHQ?ref=orpc" target="_blank" rel="noopener" title="Fabworks"><img src="https://avatars.githubusercontent.com/u/160179500?v=4" width="32" height="32" alt="Fabworks" /></a>
169
- <a href="https://github.com/NovakAnton?ref=orpc" target="_blank" rel="noopener" title="Novak Antonijevic"><img src="https://avatars.githubusercontent.com/u/157126729?u=ae49fa22292d55c0434ff0ca008206155b18663b&amp;v=4" width="32" height="32" alt="Novak Antonijevic" /></a>
170
- <a href="https://github.com/laduniestu?ref=orpc" target="_blank" rel="noopener" title="Laduni Estu Syalwa"><img src="https://avatars.githubusercontent.com/u/44757637?u=a2fc1ea8f7d827a96721176f79d30592d1c48059&amp;v=4" width="32" height="32" alt="Laduni Estu Syalwa" /></a>
171
- <a href="https://github.com/yukimotochern?ref=orpc" target="_blank" rel="noopener" title="Chen, Zhi-Yuan"><img src="https://avatars.githubusercontent.com/u/20896173?u=945c33fc21725e4d566a0d02afc54b136ca1d67a&amp;v=4" width="32" height="32" alt="Chen, Zhi-Yuan" /></a>
172
- <a href="https://github.com/illarionvk?ref=orpc" target="_blank" rel="noopener" title="Illarion Koperski"><img src="https://avatars.githubusercontent.com/u/5012724?u=7cfa13652f7ac5fb3c56d880e3eb3fbe40c3ea34&amp;v=4" width="32" height="32" alt="Illarion Koperski" /></a>
173
- <a href="https://github.com/steelbrain?ref=orpc" target="_blank" rel="noopener" title="Anees Iqbal"><img src="https://avatars.githubusercontent.com/u/4278113?u=22b80b5399eed68ac76cd58b02961b0481f1db11&amp;v=4" width="32" height="32" alt="Anees Iqbal" /></a>
174
- <a href="https://github.com/Scrumplex?ref=orpc" target="_blank" rel="noopener" title="Sefa Eyeoglu"><img src="https://avatars.githubusercontent.com/u/11587657?u=ab503582165c0bbff0cca47ce31c9450bb1553c9&amp;v=4" width="32" height="32" alt="Sefa Eyeoglu" /></a>
175
- <a href="https://github.com/ChromeGG?ref=orpc" target="_blank" rel="noopener" title="Adam Tkaczyk"><img src="https://avatars.githubusercontent.com/u/39050595?u=a58ca6042a6950e94e6e92442db76ef584279bc0&amp;v=4" width="32" height="32" alt="Adam Tkaczyk" /></a>
176
- <a href="https://github.com/plancraft?ref=orpc" target="_blank" rel="noopener" title="plancraft"><img src="https://avatars.githubusercontent.com/u/46482287?v=4" width="32" height="32" alt="plancraft" /></a>
177
- </p>
147
+ With thanks to [37 past sponsors](https://htmlpreview.github.io/?https://github.com/middleapi/static/blob/main/sponsors.svg) who helped get oRPC here.
178
148
 
179
149
  ## References
180
150
 
package/dist/index.d.mts CHANGED
@@ -2,14 +2,18 @@ import * as Draft2020 from 'json-schema-typed/draft-2020-12';
2
2
  export { Format as JsonSchemaFormat, TypeName as JsonSchemaType } from 'json-schema-typed/draft-2020-12';
3
3
  import { AnySchema, RouterContract } from '@orpc/contract';
4
4
  export { AnySchema, Schema } from '@orpc/contract';
5
- import { Promisable } from '@orpc/shared';
6
5
  import { Context } from '@orpc/server';
7
6
  import { StandardHandlerPlugin, StandardHandlerOptions } from '@orpc/server/standard';
8
7
  import { ClientContext } from '@orpc/client';
9
8
  import { StandardLinkPlugin, StandardLinkOptions } from '@orpc/client/standard';
10
9
  import { StandardJSONSchemaV1 } from '@standard-schema/spec';
11
10
 
12
- type JsonSchema = Draft2020.JSONSchema;
11
+ /**
12
+ * A JSON Schema (draft 2020-12) representation used across oRPC's JSON schema tooling.
13
+ *
14
+ * @see {@link https://orpc.dev/docs/integrations/standard-schema | Standard Schema Integration}
15
+ */
16
+ type JsonSchema<Value = any> = Draft2020.JSONSchema<Value>;
13
17
  type JsonSchemaKeywords = typeof Draft2020.keywords[number];
14
18
  declare enum JsonSchemaXNativeType {
15
19
  BigInt = "bigint",
@@ -90,21 +94,32 @@ declare function flattenJsonUnionSchema(schema: JsonSchema): JsonSchema[];
90
94
  declare function matchArrayableJsonSchema(schema: JsonSchema): undefined | [itemSchema: JsonSchema, arraySchema: JsonArraySchema];
91
95
  declare function deduplicateJsonSchemas(schemas: JsonSchema[]): JsonSchema[];
92
96
 
97
+ /**
98
+ * The conversion direction: `input` targets the schema's input type, `output` targets its output type.
99
+ *
100
+ * @see {@link https://orpc.dev/docs/integrations/standard-schema | Standard Schema Integration}
101
+ */
93
102
  type JsonSchemaConverterDirection = 'input' | 'output';
103
+ /**
104
+ * Interface for converting validation schemas into JSON Schema representations,
105
+ * used by tools such as the OpenAPI Generator and Smart Coercion plugins.
106
+ *
107
+ * @see {@link https://orpc.dev/docs/integrations/standard-schema | Standard Schema Integration}
108
+ */
94
109
  interface JsonSchemaConverter {
95
110
  /**
96
111
  * Determines whether this converter can handle the given schema.
97
112
  */
98
- condition(schema: AnySchema | undefined, direction: JsonSchemaConverterDirection): Promisable<boolean>;
113
+ condition(schema: AnySchema | undefined, direction: JsonSchemaConverterDirection): boolean;
99
114
  /**
100
115
  * Converts an ORPC schema to a JSON Schema representation.
101
116
  */
102
- convert(schema: AnySchema | undefined, direction: JsonSchemaConverterDirection): Promisable<[jsonSchema: JsonSchema, optional: boolean]>;
117
+ convert(schema: AnySchema | undefined, direction: JsonSchemaConverterDirection): [jsonSchema: JsonSchema, optional: boolean];
103
118
  }
104
119
  declare class DelegatingJsonSchemaConverter implements Pick<JsonSchemaConverter, 'convert'> {
105
120
  private readonly converters;
106
121
  constructor(converters?: JsonSchemaConverter[]);
107
- convert(schema: AnySchema | undefined, direction: JsonSchemaConverterDirection): Promise<[jsonSchema: JsonSchema, optional: boolean]>;
122
+ convert(schema: AnySchema | undefined, direction: JsonSchemaConverterDirection): [jsonSchema: JsonSchema, optional: boolean];
108
123
  }
109
124
 
110
125
  /**
@@ -124,6 +139,11 @@ declare function encodeJsonPointerSegment(segment: string): string;
124
139
  * https://datatracker.ietf.org/doc/html/rfc6901
125
140
  */
126
141
  declare function decodeJsonPointerSegment(segment: string): string;
142
+ /**
143
+ * Visits every `$ref` in a schema, traversing the same keywords as {@link mapJsonSchemaRefs}.
144
+ * Shared or cyclic object instances are visited once.
145
+ */
146
+ declare function visitJsonSchemaRefs(value: JsonSchema, visit: (ref: string) => void, schemaLevel?: boolean, seen?: Set<object>): void;
127
147
  declare function mapJsonSchemaRefs(value: JsonSchema, map: (ref: string, path: Array<string | number>) => string, schemaLevel?: boolean, path?: Array<string | number>): JsonSchema;
128
148
  /**
129
149
  * Rewrites recursive root `#` refs by moving the schema body into `$defs`.
@@ -149,6 +169,12 @@ declare function resolveJsonSchemaRootLocalRef(schema: JsonSchema, $defs?: Exclu
149
169
  interface SmartCoercionHandlerPluginOptions {
150
170
  converters?: undefined | JsonSchemaConverter[];
151
171
  }
172
+ /**
173
+ * Coerces incoming request data to match the expected `.input` schema types before validation,
174
+ * based on JSON schemas produced by the configured converters.
175
+ *
176
+ * @see {@link https://orpc.dev/docs/plugins/smart-coercion | Smart Coercion Plugin}
177
+ */
152
178
  declare class SmartCoercionHandlerPlugin<T extends Context> implements StandardHandlerPlugin<T> {
153
179
  name: string;
154
180
  private readonly converter;
@@ -162,6 +188,12 @@ declare class SmartCoercionHandlerPlugin<T extends Context> implements StandardH
162
188
  interface SmartCoercionLinkPluginOptions {
163
189
  converters?: undefined | JsonSchemaConverter[];
164
190
  }
191
+ /**
192
+ * Coerces server responses to match the expected `.output` or `.errors` schema types before validation,
193
+ * based on JSON schemas produced by the configured converters.
194
+ *
195
+ * @see {@link https://orpc.dev/docs/plugins/smart-coercion | Smart Coercion Plugin}
196
+ */
165
197
  declare class SmartCoercionLinkPlugin<T extends ClientContext> implements StandardLinkPlugin<T> {
166
198
  private readonly contract;
167
199
  name: string;
@@ -183,5 +215,5 @@ declare class StandardJsonSchemaConverter implements JsonSchemaConverter {
183
215
  convertInternal(schema: StandardJSONSchemaV1 & AnySchema, direction: JsonSchemaConverterDirection): [jsonSchema: JsonSchema, optional: boolean];
184
216
  }
185
217
 
186
- export { DelegatingJsonSchemaConverter, JsonSchemaCoercer, JsonSchemaXNativeType, SmartCoercionHandlerPlugin, SmartCoercionLinkPlugin, StandardJsonSchemaConverter, combineJsonObjectSchemaEntries, combineJsonSchemasWithComposition, decodeJsonPointerSegment, deduplicateJsonSchemas, encodeJsonPointerSegment, ensureJsonSchemaObject, extractJsonObjectSchemaEntries, flattenJsonUnionSchema, hoistRecursiveRefToDef, isJsonArraySchema, isJsonFileSchema, isJsonObjectSchema, isJsonPrimitiveSchema, isUnconstrainedSchema, mapJsonSchemaRefs, matchArrayableJsonSchema, resolveJsonSchemaRootLocalRef };
218
+ export { DelegatingJsonSchemaConverter, JsonSchemaCoercer, JsonSchemaXNativeType, SmartCoercionHandlerPlugin, SmartCoercionLinkPlugin, StandardJsonSchemaConverter, combineJsonObjectSchemaEntries, combineJsonSchemasWithComposition, decodeJsonPointerSegment, deduplicateJsonSchemas, encodeJsonPointerSegment, ensureJsonSchemaObject, extractJsonObjectSchemaEntries, flattenJsonUnionSchema, hoistRecursiveRefToDef, isJsonArraySchema, isJsonFileSchema, isJsonObjectSchema, isJsonPrimitiveSchema, isUnconstrainedSchema, mapJsonSchemaRefs, matchArrayableJsonSchema, resolveJsonSchemaRootLocalRef, visitJsonSchemaRefs };
187
219
  export type { JsonArraySchema, JsonFileSchema, JsonObjectSchema, JsonObjectSchemaEntry, JsonSchema, JsonSchemaConverter, JsonSchemaConverterDirection, JsonSchemaKeywords, SmartCoercionHandlerPluginOptions, SmartCoercionLinkPluginOptions };
package/dist/index.d.ts CHANGED
@@ -2,14 +2,18 @@ import * as Draft2020 from 'json-schema-typed/draft-2020-12';
2
2
  export { Format as JsonSchemaFormat, TypeName as JsonSchemaType } from 'json-schema-typed/draft-2020-12';
3
3
  import { AnySchema, RouterContract } from '@orpc/contract';
4
4
  export { AnySchema, Schema } from '@orpc/contract';
5
- import { Promisable } from '@orpc/shared';
6
5
  import { Context } from '@orpc/server';
7
6
  import { StandardHandlerPlugin, StandardHandlerOptions } from '@orpc/server/standard';
8
7
  import { ClientContext } from '@orpc/client';
9
8
  import { StandardLinkPlugin, StandardLinkOptions } from '@orpc/client/standard';
10
9
  import { StandardJSONSchemaV1 } from '@standard-schema/spec';
11
10
 
12
- type JsonSchema = Draft2020.JSONSchema;
11
+ /**
12
+ * A JSON Schema (draft 2020-12) representation used across oRPC's JSON schema tooling.
13
+ *
14
+ * @see {@link https://orpc.dev/docs/integrations/standard-schema | Standard Schema Integration}
15
+ */
16
+ type JsonSchema<Value = any> = Draft2020.JSONSchema<Value>;
13
17
  type JsonSchemaKeywords = typeof Draft2020.keywords[number];
14
18
  declare enum JsonSchemaXNativeType {
15
19
  BigInt = "bigint",
@@ -90,21 +94,32 @@ declare function flattenJsonUnionSchema(schema: JsonSchema): JsonSchema[];
90
94
  declare function matchArrayableJsonSchema(schema: JsonSchema): undefined | [itemSchema: JsonSchema, arraySchema: JsonArraySchema];
91
95
  declare function deduplicateJsonSchemas(schemas: JsonSchema[]): JsonSchema[];
92
96
 
97
+ /**
98
+ * The conversion direction: `input` targets the schema's input type, `output` targets its output type.
99
+ *
100
+ * @see {@link https://orpc.dev/docs/integrations/standard-schema | Standard Schema Integration}
101
+ */
93
102
  type JsonSchemaConverterDirection = 'input' | 'output';
103
+ /**
104
+ * Interface for converting validation schemas into JSON Schema representations,
105
+ * used by tools such as the OpenAPI Generator and Smart Coercion plugins.
106
+ *
107
+ * @see {@link https://orpc.dev/docs/integrations/standard-schema | Standard Schema Integration}
108
+ */
94
109
  interface JsonSchemaConverter {
95
110
  /**
96
111
  * Determines whether this converter can handle the given schema.
97
112
  */
98
- condition(schema: AnySchema | undefined, direction: JsonSchemaConverterDirection): Promisable<boolean>;
113
+ condition(schema: AnySchema | undefined, direction: JsonSchemaConverterDirection): boolean;
99
114
  /**
100
115
  * Converts an ORPC schema to a JSON Schema representation.
101
116
  */
102
- convert(schema: AnySchema | undefined, direction: JsonSchemaConverterDirection): Promisable<[jsonSchema: JsonSchema, optional: boolean]>;
117
+ convert(schema: AnySchema | undefined, direction: JsonSchemaConverterDirection): [jsonSchema: JsonSchema, optional: boolean];
103
118
  }
104
119
  declare class DelegatingJsonSchemaConverter implements Pick<JsonSchemaConverter, 'convert'> {
105
120
  private readonly converters;
106
121
  constructor(converters?: JsonSchemaConverter[]);
107
- convert(schema: AnySchema | undefined, direction: JsonSchemaConverterDirection): Promise<[jsonSchema: JsonSchema, optional: boolean]>;
122
+ convert(schema: AnySchema | undefined, direction: JsonSchemaConverterDirection): [jsonSchema: JsonSchema, optional: boolean];
108
123
  }
109
124
 
110
125
  /**
@@ -124,6 +139,11 @@ declare function encodeJsonPointerSegment(segment: string): string;
124
139
  * https://datatracker.ietf.org/doc/html/rfc6901
125
140
  */
126
141
  declare function decodeJsonPointerSegment(segment: string): string;
142
+ /**
143
+ * Visits every `$ref` in a schema, traversing the same keywords as {@link mapJsonSchemaRefs}.
144
+ * Shared or cyclic object instances are visited once.
145
+ */
146
+ declare function visitJsonSchemaRefs(value: JsonSchema, visit: (ref: string) => void, schemaLevel?: boolean, seen?: Set<object>): void;
127
147
  declare function mapJsonSchemaRefs(value: JsonSchema, map: (ref: string, path: Array<string | number>) => string, schemaLevel?: boolean, path?: Array<string | number>): JsonSchema;
128
148
  /**
129
149
  * Rewrites recursive root `#` refs by moving the schema body into `$defs`.
@@ -149,6 +169,12 @@ declare function resolveJsonSchemaRootLocalRef(schema: JsonSchema, $defs?: Exclu
149
169
  interface SmartCoercionHandlerPluginOptions {
150
170
  converters?: undefined | JsonSchemaConverter[];
151
171
  }
172
+ /**
173
+ * Coerces incoming request data to match the expected `.input` schema types before validation,
174
+ * based on JSON schemas produced by the configured converters.
175
+ *
176
+ * @see {@link https://orpc.dev/docs/plugins/smart-coercion | Smart Coercion Plugin}
177
+ */
152
178
  declare class SmartCoercionHandlerPlugin<T extends Context> implements StandardHandlerPlugin<T> {
153
179
  name: string;
154
180
  private readonly converter;
@@ -162,6 +188,12 @@ declare class SmartCoercionHandlerPlugin<T extends Context> implements StandardH
162
188
  interface SmartCoercionLinkPluginOptions {
163
189
  converters?: undefined | JsonSchemaConverter[];
164
190
  }
191
+ /**
192
+ * Coerces server responses to match the expected `.output` or `.errors` schema types before validation,
193
+ * based on JSON schemas produced by the configured converters.
194
+ *
195
+ * @see {@link https://orpc.dev/docs/plugins/smart-coercion | Smart Coercion Plugin}
196
+ */
165
197
  declare class SmartCoercionLinkPlugin<T extends ClientContext> implements StandardLinkPlugin<T> {
166
198
  private readonly contract;
167
199
  name: string;
@@ -183,5 +215,5 @@ declare class StandardJsonSchemaConverter implements JsonSchemaConverter {
183
215
  convertInternal(schema: StandardJSONSchemaV1 & AnySchema, direction: JsonSchemaConverterDirection): [jsonSchema: JsonSchema, optional: boolean];
184
216
  }
185
217
 
186
- export { DelegatingJsonSchemaConverter, JsonSchemaCoercer, JsonSchemaXNativeType, SmartCoercionHandlerPlugin, SmartCoercionLinkPlugin, StandardJsonSchemaConverter, combineJsonObjectSchemaEntries, combineJsonSchemasWithComposition, decodeJsonPointerSegment, deduplicateJsonSchemas, encodeJsonPointerSegment, ensureJsonSchemaObject, extractJsonObjectSchemaEntries, flattenJsonUnionSchema, hoistRecursiveRefToDef, isJsonArraySchema, isJsonFileSchema, isJsonObjectSchema, isJsonPrimitiveSchema, isUnconstrainedSchema, mapJsonSchemaRefs, matchArrayableJsonSchema, resolveJsonSchemaRootLocalRef };
218
+ export { DelegatingJsonSchemaConverter, JsonSchemaCoercer, JsonSchemaXNativeType, SmartCoercionHandlerPlugin, SmartCoercionLinkPlugin, StandardJsonSchemaConverter, combineJsonObjectSchemaEntries, combineJsonSchemasWithComposition, decodeJsonPointerSegment, deduplicateJsonSchemas, encodeJsonPointerSegment, ensureJsonSchemaObject, extractJsonObjectSchemaEntries, flattenJsonUnionSchema, hoistRecursiveRefToDef, isJsonArraySchema, isJsonFileSchema, isJsonObjectSchema, isJsonPrimitiveSchema, isUnconstrainedSchema, mapJsonSchemaRefs, matchArrayableJsonSchema, resolveJsonSchemaRootLocalRef, visitJsonSchemaRefs };
187
219
  export type { JsonArraySchema, JsonFileSchema, JsonObjectSchema, JsonObjectSchemaEntry, JsonSchema, JsonSchemaConverter, JsonSchemaConverterDirection, JsonSchemaKeywords, SmartCoercionHandlerPluginOptions, SmartCoercionLinkPluginOptions };
package/dist/index.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { get, isPlainObject, toArray, tryOrUndefined, isDeepEqual, omit, isTypescriptObject } from '@orpc/shared';
1
+ import { get, isPlainObject, tryOrUndefined, toArray, isDeepEqual, omit, isTypescriptObject, getOwn } from '@orpc/shared';
2
2
  export { Format as JsonSchemaFormat, TypeName as JsonSchemaType } from 'json-schema-typed/draft-2020-12';
3
3
  import { ORPCError, cloneORPCError } from '@orpc/client';
4
4
  import { getProcedureContractOrThrow } from '@orpc/contract';
@@ -59,6 +59,27 @@ function encodeJsonPointerSegment(segment) {
59
59
  function decodeJsonPointerSegment(segment) {
60
60
  return segment.replaceAll("~1", "/").replaceAll("~0", "~");
61
61
  }
62
+ function visitJsonSchemaRefs(value, visit, schemaLevel = true, seen = /* @__PURE__ */ new Set()) {
63
+ if (!value || typeof value !== "object" || seen.has(value)) {
64
+ return;
65
+ }
66
+ seen.add(value);
67
+ if (Array.isArray(value)) {
68
+ for (const item of value) {
69
+ visitJsonSchemaRefs(item, visit, schemaLevel, seen);
70
+ }
71
+ return;
72
+ }
73
+ for (const [key, val] of Object.entries(value)) {
74
+ if (key === "$ref" && typeof val === "string") {
75
+ visit(val);
76
+ } else if (!schemaLevel) {
77
+ visitJsonSchemaRefs(val, visit, true, seen);
78
+ } else if (JSON_SCHEMA_LOGIC_KEYWORDS.has(key) || JSON_SCHEMA_RECORD_KEYWORDS.has(key)) {
79
+ visitJsonSchemaRefs(val, visit, !JSON_SCHEMA_RECORD_KEYWORDS.has(key), seen);
80
+ }
81
+ }
82
+ }
62
83
  function mapJsonSchemaRefs(value, map, schemaLevel = true, path = []) {
63
84
  if (!value || typeof value !== "object") {
64
85
  return value;
@@ -148,7 +169,12 @@ var JsonSchemaXNativeType = /* @__PURE__ */ ((JsonSchemaXNativeType2) => {
148
169
  return JsonSchemaXNativeType2;
149
170
  })(JsonSchemaXNativeType || {});
150
171
 
151
- const FLEXIBLE_DATE_FORMAT_REGEX = /^[^-]+-[^-]+-[^-]+$/;
172
+ const UNSATISFIED = 0;
173
+ const LOOSELY_SATISFIED = 1;
174
+ const SATISFIED = 2;
175
+ function minSatisfaction(a, b) {
176
+ return a < b ? a : b;
177
+ }
152
178
  class JsonSchemaCoercer {
153
179
  coerce([schema, optional], value) {
154
180
  if (optional && value === void 0) {
@@ -157,25 +183,29 @@ class JsonSchemaCoercer {
157
183
  const [, coerced] = this.coerceInternal(schema, schema, value);
158
184
  return coerced;
159
185
  }
160
- coerceInternal(rootSchema, schema, value) {
186
+ coerceInternal(rootSchema, schema, value, appliedRefs) {
161
187
  if (typeof schema === "boolean") {
162
- return [schema, value];
188
+ return [schema ? SATISFIED : UNSATISFIED, value];
163
189
  }
164
190
  if (Array.isArray(schema.type)) {
165
191
  return this.coerceInternal(
166
192
  rootSchema,
167
193
  { anyOf: schema.type.map((type) => ({ ...schema, type })) },
168
- value
194
+ value,
195
+ appliedRefs
169
196
  );
170
197
  }
171
198
  let coerced = value;
172
- let satisfied = true;
199
+ let satisfied = SATISFIED;
173
200
  if (typeof schema.$ref === "string") {
174
201
  const resolved = schema.$ref.startsWith("#/") ? get(rootSchema, schema.$ref.slice("#/".length).split("/").map(decodeJsonPointerSegment)) : schema.$ref === "#" ? rootSchema : void 0;
175
- if (resolved !== void 0) {
176
- const [subSatisfied, subCoerced] = this.coerceInternal(rootSchema, resolved, coerced);
202
+ if (resolved !== void 0 && !appliedRefs?.has(resolved)) {
203
+ appliedRefs ??= /* @__PURE__ */ new Set();
204
+ appliedRefs.add(resolved);
205
+ const [subSatisfied, subCoerced] = this.coerceInternal(rootSchema, resolved, coerced, appliedRefs);
206
+ appliedRefs.delete(resolved);
177
207
  coerced = subCoerced;
178
- satisfied = subSatisfied;
208
+ satisfied = minSatisfaction(satisfied, subSatisfied);
179
209
  }
180
210
  }
181
211
  const enumValues = schema.const !== void 0 ? [schema.const] : schema.enum;
@@ -189,24 +219,24 @@ class JsonSchemaCoercer {
189
219
  if (enumValues.includes(booleanValue)) {
190
220
  coerced = booleanValue;
191
221
  } else {
192
- satisfied = false;
222
+ satisfied = UNSATISFIED;
193
223
  }
194
224
  }
195
225
  } else {
196
- satisfied = false;
226
+ satisfied = UNSATISFIED;
197
227
  }
198
228
  }
199
229
  if (schema.type) {
200
230
  switch (schema.type) {
201
231
  case "null": {
202
232
  if (coerced !== null) {
203
- satisfied = false;
233
+ satisfied = UNSATISFIED;
204
234
  }
205
235
  break;
206
236
  }
207
237
  case "string": {
208
238
  if (typeof coerced !== "string") {
209
- satisfied = false;
239
+ satisfied = UNSATISFIED;
210
240
  }
211
241
  break;
212
242
  }
@@ -215,7 +245,7 @@ class JsonSchemaCoercer {
215
245
  coerced = stringToNumber(coerced);
216
246
  }
217
247
  if (typeof coerced !== "number") {
218
- satisfied = false;
248
+ satisfied = UNSATISFIED;
219
249
  }
220
250
  break;
221
251
  }
@@ -224,7 +254,7 @@ class JsonSchemaCoercer {
224
254
  coerced = stringToInteger(coerced);
225
255
  }
226
256
  if (typeof coerced !== "number" || !Number.isInteger(coerced)) {
227
- satisfied = false;
257
+ satisfied = UNSATISFIED;
228
258
  }
229
259
  break;
230
260
  }
@@ -233,7 +263,7 @@ class JsonSchemaCoercer {
233
263
  coerced = stringToBoolean(coerced);
234
264
  }
235
265
  if (typeof coerced !== "boolean") {
236
- satisfied = false;
266
+ satisfied = UNSATISFIED;
237
267
  }
238
268
  break;
239
269
  }
@@ -245,26 +275,24 @@ class JsonSchemaCoercer {
245
275
  const coercedItems = coerced.map((item, i) => {
246
276
  const subSchema = prefixItemSchemas[i] ?? itemSchema;
247
277
  if (subSchema === void 0) {
248
- satisfied = false;
278
+ satisfied = minSatisfaction(satisfied, LOOSELY_SATISFIED);
249
279
  return item;
250
280
  }
251
281
  const [subSatisfied, subCoerced] = this.coerceInternal(rootSchema, subSchema, item);
252
- if (!subSatisfied) {
253
- satisfied = false;
254
- }
282
+ satisfied = minSatisfaction(satisfied, subSatisfied);
255
283
  if (subCoerced !== item) {
256
284
  shouldUseCoercedItems = true;
257
285
  }
258
286
  return subCoerced;
259
287
  });
260
288
  if (coercedItems.length < prefixItemSchemas.length) {
261
- satisfied = false;
289
+ satisfied = UNSATISFIED;
262
290
  }
263
291
  if (shouldUseCoercedItems) {
264
292
  coerced = coercedItems;
265
293
  }
266
294
  } else {
267
- satisfied = false;
295
+ satisfied = UNSATISFIED;
268
296
  }
269
297
  break;
270
298
  }
@@ -274,35 +302,36 @@ class JsonSchemaCoercer {
274
302
  }
275
303
  if (isPlainObject(coerced)) {
276
304
  let shouldUseCoercedItems = false;
277
- const coercedItems = {};
278
- const patternProperties = Object.entries(schema.patternProperties ?? {}).map(([key, value2]) => [new RegExp(key), value2]);
305
+ const coercedItems = { ...coerced };
306
+ const patternProperties = Object.entries(schema.patternProperties ?? {}).flatMap(([key, value2]) => {
307
+ const pattern = tryOrUndefined(() => new RegExp(key));
308
+ return pattern ? [[pattern, value2]] : [];
309
+ });
310
+ const propertySchemas = schema.properties;
279
311
  for (const key in coerced) {
280
312
  const value2 = coerced[key];
281
- const subSchema = schema.properties?.[key] ?? patternProperties.find(([pattern]) => pattern.test(key))?.[1] ?? schema.additionalProperties;
282
- if (value2 === void 0 && !schema.required?.includes(key)) {
283
- coercedItems[key] = value2;
284
- } else if (subSchema === void 0) {
285
- coercedItems[key] = value2;
286
- satisfied = false;
287
- } else {
288
- const [subSatisfied, subCoerced] = this.coerceInternal(rootSchema, subSchema, value2);
289
- coercedItems[key] = subCoerced;
290
- if (!subSatisfied) {
291
- satisfied = false;
292
- }
293
- if (subCoerced !== value2) {
294
- shouldUseCoercedItems = true;
313
+ const subSchema = (propertySchemas !== void 0 && Object.hasOwn(propertySchemas, key) ? propertySchemas[key] : void 0) ?? patternProperties.find(([pattern]) => pattern.test(key))?.[1] ?? schema.additionalProperties;
314
+ if (value2 !== void 0 || schema.required?.includes(key)) {
315
+ if (subSchema === void 0) {
316
+ satisfied = minSatisfaction(satisfied, LOOSELY_SATISFIED);
317
+ } else {
318
+ const [subSatisfied, subCoerced] = this.coerceInternal(rootSchema, subSchema, value2);
319
+ coercedItems[key] = subCoerced;
320
+ satisfied = minSatisfaction(satisfied, subSatisfied);
321
+ if (subCoerced !== value2) {
322
+ shouldUseCoercedItems = true;
323
+ }
295
324
  }
296
325
  }
297
326
  }
298
327
  if (schema.required?.some((key) => !Object.hasOwn(coercedItems, key))) {
299
- satisfied = false;
328
+ satisfied = UNSATISFIED;
300
329
  }
301
330
  if (shouldUseCoercedItems) {
302
331
  coerced = coercedItems;
303
332
  }
304
333
  } else {
305
- satisfied = false;
334
+ satisfied = UNSATISFIED;
306
335
  }
307
336
  break;
308
337
  }
@@ -315,7 +344,7 @@ class JsonSchemaCoercer {
315
344
  coerced = stringToDate(coerced);
316
345
  }
317
346
  if (!(coerced instanceof Date)) {
318
- satisfied = false;
347
+ satisfied = UNSATISFIED;
319
348
  }
320
349
  break;
321
350
  }
@@ -329,7 +358,7 @@ class JsonSchemaCoercer {
329
358
  break;
330
359
  }
331
360
  if (typeof coerced !== "bigint") {
332
- satisfied = false;
361
+ satisfied = UNSATISFIED;
333
362
  }
334
363
  break;
335
364
  }
@@ -338,7 +367,7 @@ class JsonSchemaCoercer {
338
367
  coerced = stringToRegExp(coerced);
339
368
  }
340
369
  if (!(coerced instanceof RegExp)) {
341
- satisfied = false;
370
+ satisfied = UNSATISFIED;
342
371
  }
343
372
  break;
344
373
  }
@@ -347,7 +376,7 @@ class JsonSchemaCoercer {
347
376
  coerced = stringToURL(coerced);
348
377
  }
349
378
  if (!(coerced instanceof URL)) {
350
- satisfied = false;
379
+ satisfied = UNSATISFIED;
351
380
  }
352
381
  break;
353
382
  }
@@ -356,7 +385,7 @@ class JsonSchemaCoercer {
356
385
  coerced = arrayToSet(coerced);
357
386
  }
358
387
  if (!(coerced instanceof Set)) {
359
- satisfied = false;
388
+ satisfied = UNSATISFIED;
360
389
  }
361
390
  break;
362
391
  }
@@ -365,7 +394,7 @@ class JsonSchemaCoercer {
365
394
  coerced = arrayToMap(coerced);
366
395
  }
367
396
  if (!(coerced instanceof Map)) {
368
- satisfied = false;
397
+ satisfied = UNSATISFIED;
369
398
  }
370
399
  break;
371
400
  }
@@ -373,54 +402,78 @@ class JsonSchemaCoercer {
373
402
  }
374
403
  if (schema.allOf) {
375
404
  for (const subSchema of schema.allOf) {
376
- const [subSatisfied, subCoerced] = this.coerceInternal(rootSchema, subSchema, coerced);
405
+ const [subSatisfied, subCoerced] = this.coerceInternal(rootSchema, subSchema, coerced, appliedRefs);
377
406
  coerced = subCoerced;
378
- if (!subSatisfied) {
379
- satisfied = false;
380
- }
407
+ satisfied = minSatisfaction(satisfied, subSatisfied);
381
408
  }
382
409
  }
383
410
  for (const key of ["anyOf", "oneOf"]) {
384
411
  if (schema[key]) {
385
- let bestOptions;
412
+ let best;
386
413
  for (const subSchema of schema[key]) {
387
- const [subSatisfied, subCoerced] = this.coerceInternal(rootSchema, subSchema, coerced);
388
- if (subSatisfied) {
389
- if (!bestOptions || subCoerced === coerced) {
390
- bestOptions = { coerced: subCoerced, satisfied: subSatisfied };
391
- }
392
- if (subCoerced === coerced) {
393
- break;
394
- }
414
+ const [subSatisfied, subCoerced] = this.coerceInternal(rootSchema, subSchema, coerced, appliedRefs);
415
+ if (subSatisfied === UNSATISFIED) {
416
+ continue;
417
+ }
418
+ if (subCoerced === coerced) {
419
+ best = { satisfied: subSatisfied, value: subCoerced };
420
+ break;
421
+ }
422
+ if (!best || subSatisfied > best.satisfied) {
423
+ best = { satisfied: subSatisfied, value: subCoerced };
395
424
  }
396
425
  }
397
- coerced = bestOptions ? bestOptions.coerced : coerced;
398
- satisfied = bestOptions ? bestOptions.satisfied : false;
426
+ if (best) {
427
+ coerced = best.value;
428
+ satisfied = minSatisfaction(satisfied, best.satisfied);
429
+ } else {
430
+ satisfied = UNSATISFIED;
431
+ }
399
432
  }
400
433
  }
401
434
  if (typeof schema.not !== "undefined") {
402
- const [notSatisfied] = this.coerceInternal(rootSchema, schema.not, coerced);
403
- if (notSatisfied) {
404
- satisfied = false;
435
+ const [notSatisfied] = this.coerceInternal(rootSchema, schema.not, coerced, appliedRefs);
436
+ if (notSatisfied !== UNSATISFIED) {
437
+ satisfied = UNSATISFIED;
405
438
  }
406
439
  }
407
440
  return [satisfied, coerced];
408
441
  }
409
442
  }
443
+ const NUMBER_PATTERN = /^-?(?:0|[1-9]\d*)(?:\.\d+)?$/;
410
444
  function stringToNumber(value) {
411
- const num = Number.parseFloat(value);
412
- if (Number.isNaN(num) || num !== Number(value)) {
445
+ if (!NUMBER_PATTERN.test(value)) {
446
+ return value;
447
+ }
448
+ const num = Number(value);
449
+ if (num < Number.MIN_SAFE_INTEGER || num > Number.MAX_SAFE_INTEGER) {
413
450
  return value;
414
451
  }
415
452
  return num;
416
453
  }
454
+ const INTEGER_PATTERN = /^-?(?:0|[1-9]\d*)$/;
417
455
  function stringToInteger(value) {
418
- const num = Number.parseInt(value);
419
- if (Number.isNaN(num) || num !== Number(value)) {
456
+ if (!INTEGER_PATTERN.test(value)) {
457
+ return value;
458
+ }
459
+ const num = Number(value);
460
+ if (!Number.isSafeInteger(num)) {
420
461
  return value;
421
462
  }
422
463
  return num;
423
464
  }
465
+ function stringToBigInt(value) {
466
+ if (!INTEGER_PATTERN.test(value)) {
467
+ return value;
468
+ }
469
+ return BigInt(value);
470
+ }
471
+ function numberToBigInt(value) {
472
+ if (!Number.isInteger(value)) {
473
+ return value;
474
+ }
475
+ return BigInt(value);
476
+ }
424
477
  function stringToBoolean(value) {
425
478
  const lower = value.toLowerCase();
426
479
  if (lower === "false" || lower === "off") {
@@ -431,21 +484,20 @@ function stringToBoolean(value) {
431
484
  }
432
485
  return value;
433
486
  }
434
- function stringToBigInt(value) {
435
- return tryOrUndefined(() => BigInt(value)) ?? value;
436
- }
437
- function numberToBigInt(value) {
438
- return tryOrUndefined(() => BigInt(value)) ?? value;
439
- }
487
+ const DATE_TIME_PATTERN = /^[+-]?\d{4,6}-\d{1,2}-\d{1,2}(?:[T ].*)?$/;
440
488
  function stringToDate(value) {
489
+ if (!DATE_TIME_PATTERN.test(value)) {
490
+ return value;
491
+ }
441
492
  const date = new Date(value);
442
- if (Number.isNaN(date.getTime()) || !FLEXIBLE_DATE_FORMAT_REGEX.test(value)) {
493
+ if (Number.isNaN(date.getTime())) {
443
494
  return value;
444
495
  }
445
496
  return date;
446
497
  }
498
+ const REGEXP_PATTERN = /^\/([\s\S]*)\/([a-z]*)$/;
447
499
  function stringToRegExp(value) {
448
- const match = value.match(/^\/(.*)\/([a-z]*)$/);
500
+ const match = value.match(REGEXP_PATTERN);
449
501
  if (match) {
450
502
  const [, pattern, flags] = match;
451
503
  return tryOrUndefined(() => new RegExp(pattern, flags)) ?? value;
@@ -774,7 +826,7 @@ function flattenJsonUnionSchemaInternal(schema, resolvingRefs) {
774
826
  }
775
827
  const resolved = resolveJsonSchemaRootLocalRef(schema);
776
828
  if (resolved !== schema) {
777
- const result = flattenJsonUnionSchemaInternal(resolved, resolvingRefs.add(schema.$ref));
829
+ const result = flattenJsonUnionSchemaInternal(resolved, new Set(resolvingRefs).add(schema.$ref));
778
830
  if (result.length > 1) {
779
831
  return result;
780
832
  }
@@ -844,13 +896,14 @@ class DelegatingJsonSchemaConverter {
844
896
  constructor(converters = []) {
845
897
  this.converters = converters;
846
898
  }
847
- async convert(schema, direction) {
899
+ convert(schema, direction) {
848
900
  for (const converter of this.converters) {
849
- if (await converter.condition(schema, direction)) {
901
+ if (converter.condition(schema, direction)) {
850
902
  return converter.convert(schema, direction);
851
903
  }
852
904
  }
853
- const optional = !(await schema?.["~standard"].validate(void 0))?.issues?.length;
905
+ const result = schema?.["~standard"].validate(void 0);
906
+ const optional = result instanceof Promise ? false : !result?.issues?.length;
854
907
  if (schema && "jsonSchema" in schema["~standard"] && schema["~standard"].jsonSchema) {
855
908
  try {
856
909
  return [
@@ -907,23 +960,23 @@ class SmartCoercionHandlerPlugin {
907
960
  return {
908
961
  ...options,
909
962
  clientInterceptors: [
910
- async ({ next, input, ...interceptorOptions }) => {
963
+ ({ next, input, ...interceptorOptions }) => {
911
964
  const inputSchemas = interceptorOptions.procedure["~orpc"].inputSchemas;
912
965
  if (!inputSchemas) {
913
966
  return next();
914
967
  }
915
- const coercedInput = await this.coerceValue(inputSchemas, input);
968
+ const coercedInput = this.coerceValue(inputSchemas, input);
916
969
  return next({ ...interceptorOptions, input: coercedInput });
917
970
  },
918
971
  ...toArray(options.clientInterceptors)
919
972
  ]
920
973
  };
921
974
  }
922
- async coerceValue(schemas, value) {
975
+ coerceValue(schemas, value) {
923
976
  for (const schema of schemas) {
924
977
  let converted = this.cache.get(schema);
925
978
  if (!converted) {
926
- converted = await this.converter.convert(schema, "input");
979
+ converted = this.converter.convert(schema, "input");
927
980
  this.cache.set(schema, converted);
928
981
  }
929
982
  value = this.coercer.coerce(converted, value);
@@ -962,30 +1015,30 @@ class SmartCoercionLinkPlugin {
962
1015
  if (!outputSchemas) {
963
1016
  return output;
964
1017
  }
965
- const coercedOutput = await this.coerceValue(outputSchemas, output);
1018
+ const coercedOutput = this.coerceValue(outputSchemas, output);
966
1019
  return coercedOutput;
967
1020
  } catch (error) {
968
1021
  if (!(error instanceof ORPCError) || !error.defined) {
969
1022
  throw error;
970
1023
  }
971
1024
  const errorMap = procedure["~orpc"].errorMap;
972
- const dataSchema = errorMap[error.code]?.data;
1025
+ const dataSchema = getOwn(errorMap, error.code)?.data;
973
1026
  if (!dataSchema) {
974
1027
  throw error;
975
1028
  }
976
1029
  const cloned = cloneORPCError(error);
977
- cloned.data = await this.coerceValue([dataSchema], cloned.data);
1030
+ cloned.data = this.coerceValue([dataSchema], cloned.data);
978
1031
  throw cloned;
979
1032
  }
980
1033
  }
981
1034
  ]
982
1035
  };
983
1036
  }
984
- async coerceValue(schemas, value) {
1037
+ coerceValue(schemas, value) {
985
1038
  for (const schema of schemas) {
986
1039
  let converted = this.cache.get(schema);
987
1040
  if (!converted) {
988
- converted = await this.converter.convert(schema, "output");
1041
+ converted = this.converter.convert(schema, "output");
989
1042
  this.cache.set(schema, converted);
990
1043
  }
991
1044
  value = this.coercer.coerce(converted, value);
@@ -994,4 +1047,4 @@ class SmartCoercionLinkPlugin {
994
1047
  }
995
1048
  }
996
1049
 
997
- export { DelegatingJsonSchemaConverter, JsonSchemaCoercer, JsonSchemaXNativeType, SmartCoercionHandlerPlugin, SmartCoercionLinkPlugin, StandardJsonSchemaConverter, combineJsonObjectSchemaEntries, combineJsonSchemasWithComposition, decodeJsonPointerSegment, deduplicateJsonSchemas, encodeJsonPointerSegment, ensureJsonSchemaObject, extractJsonObjectSchemaEntries, flattenJsonUnionSchema, hoistRecursiveRefToDef, isJsonArraySchema, isJsonFileSchema, isJsonObjectSchema, isJsonPrimitiveSchema, isUnconstrainedSchema, mapJsonSchemaRefs, matchArrayableJsonSchema, resolveJsonSchemaRootLocalRef };
1050
+ export { DelegatingJsonSchemaConverter, JsonSchemaCoercer, JsonSchemaXNativeType, SmartCoercionHandlerPlugin, SmartCoercionLinkPlugin, StandardJsonSchemaConverter, combineJsonObjectSchemaEntries, combineJsonSchemasWithComposition, decodeJsonPointerSegment, deduplicateJsonSchemas, encodeJsonPointerSegment, ensureJsonSchemaObject, extractJsonObjectSchemaEntries, flattenJsonUnionSchema, hoistRecursiveRefToDef, isJsonArraySchema, isJsonFileSchema, isJsonObjectSchema, isJsonPrimitiveSchema, isUnconstrainedSchema, mapJsonSchemaRefs, matchArrayableJsonSchema, resolveJsonSchemaRootLocalRef, visitJsonSchemaRefs };
package/package.json CHANGED
@@ -1,8 +1,13 @@
1
1
  {
2
2
  "name": "@orpc/json-schema",
3
3
  "type": "module",
4
- "version": "2.0.0-beta.3",
4
+ "version": "2.0.0-beta.31",
5
+ "description": "JSON Schema conversion and smart request coercion for oRPC and OpenAPI",
5
6
  "license": "MIT",
7
+ "funding": [
8
+ "https://github.com/sponsors/dinwwwh",
9
+ "https://opencollective.com/middleapi"
10
+ ],
6
11
  "homepage": "https://orpc.dev",
7
12
  "repository": {
8
13
  "type": "git",
@@ -10,7 +15,12 @@
10
15
  "directory": "packages/json-schema"
11
16
  },
12
17
  "keywords": [
13
- "orpc"
18
+ "orpc",
19
+ "json-schema",
20
+ "openapi",
21
+ "coercion",
22
+ "validation",
23
+ "typescript"
14
24
  ],
15
25
  "sideEffects": false,
16
26
  "exports": {
@@ -27,10 +37,10 @@
27
37
  "dependencies": {
28
38
  "@standard-schema/spec": "^1.1.0",
29
39
  "json-schema-typed": "^8.0.2",
30
- "@orpc/client": "2.0.0-beta.3",
31
- "@orpc/server": "2.0.0-beta.3",
32
- "@orpc/shared": "2.0.0-beta.3",
33
- "@orpc/contract": "2.0.0-beta.3"
40
+ "@orpc/contract": "2.0.0-beta.31",
41
+ "@orpc/client": "2.0.0-beta.31",
42
+ "@orpc/server": "2.0.0-beta.31",
43
+ "@orpc/shared": "2.0.0-beta.31"
34
44
  },
35
45
  "devDependencies": {
36
46
  "zod": "^4.4.3"