@orpc/json-schema 2.0.0-beta.43 → 2.0.0-beta.45

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
@@ -17,7 +17,7 @@
17
17
  <img alt="Discord" src="https://img.shields.io/discord/1308966753044398161?color=7389D8&label&logo=discord&logoColor=ffffff" />
18
18
  </a>
19
19
  <a href="https://deepwiki.com/middleapi/orpc">
20
- <img src="https://deepwiki.com/badge.svg" alt="Ask DeepWiki">
20
+ <img alt="Ask DeepWiki" src="https://img.shields.io/badge/Ask-DeepWiki-blue" />
21
21
  </a>
22
22
  </div>
23
23
 
@@ -60,10 +60,11 @@ You can read the documentation [here](https://orpc.dev).
60
60
  | ------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
61
61
  | [@orpc/next](https://www.npmjs.com/package/@orpc/next) | Integrate with [Next.js Server Functions](https://nextjs.org/docs/app/getting-started/mutating-data). | [Next.js](https://orpc.dev/docs/integrations/next) |
62
62
  | [@orpc/ai-sdk](https://www.npmjs.com/package/@orpc/ai-sdk) | Turn contracts and procedures into [AI SDK](https://ai-sdk.dev/) tools. | [AI SDK](https://orpc.dev/docs/integrations/ai-sdk) |
63
+ | [@orpc/mcp](https://www.npmjs.com/package/@orpc/mcp) | Expose procedures as [MCP](https://modelcontextprotocol.io/) tools. | [MCP](https://orpc.dev/docs/integrations/mcp) |
63
64
  | [@orpc/tanstack-query](https://www.npmjs.com/package/@orpc/tanstack-query) | Integrate with [TanStack Query](https://tanstack.com/query/latest). | [TanStack Query](https://orpc.dev/docs/integrations/tanstack-query) |
64
65
  | [@orpc/pinia-colada](https://www.npmjs.com/package/@orpc/pinia-colada) | Integrate with [Pinia Colada](https://pinia-colada.esm.dev/). | [Pinia Colada](https://orpc.dev/docs/integrations/pinia-colada) |
65
66
  | [@orpc/swr](https://www.npmjs.com/package/@orpc/swr) | Integrate with [SWR](https://swr.vercel.app/). | [SWR](https://orpc.dev/docs/integrations/swr) |
66
- | [@orpc/experimental-msw](https://www.npmjs.com/package/@orpc/experimental-msw) | Mock procedures with [Mock Service Worker](https://mswjs.io/). | [MSW](https://orpc.dev/docs/integrations/msw) |
67
+ | [@orpc/msw](https://www.npmjs.com/package/@orpc/msw) | Mock procedures with [Mock Service Worker](https://mswjs.io/). | [MSW](https://orpc.dev/docs/integrations/msw) |
67
68
  | [@orpc/experimental-effect](https://www.npmjs.com/package/@orpc/experimental-effect) | Integrate with [Effect](https://effect.website/). | [Effect](https://orpc.dev/docs/integrations/effect) |
68
69
  | [@orpc/nest](https://www.npmjs.com/package/@orpc/nest) | Implement your contract with [NestJS](https://nestjs.com/). | [NestJS](https://orpc.dev/docs/integrations/nest) |
69
70
  | [@orpc/node](https://www.npmjs.com/package/@orpc/node) | [Node.js](https://nodejs.org/) plugins for static file serving and large uploads. | [Static File](https://orpc.dev/docs/plugins/static-file), [Tmp File Upload](https://orpc.dev/docs/plugins/tmp-file-upload), [Batch Response Compression](https://orpc.dev/docs/plugins/batch-response-compression) |
@@ -95,27 +96,11 @@ Like what we build over at [middleapi](https://github.com/middleapi)? You can he
95
96
  </tr>
96
97
  </table>
97
98
 
98
- ### Special Sponsors
99
-
100
- <table>
101
- <tr>
102
- <td align="center"><a href="http://twitter.com/rauchg?ref=middleapi&amp;utm_source=middleapi&amp;utm_medium=sponsor" target="_blank" rel="noopener sponsored" title="Guillermo Rauch"><img src="https://avatars.githubusercontent.com/u/13041?u=1ee8d111657cdd02ff6d253df00978d17ee6d722&amp;v=4" width="279" alt="Guillermo Rauch"/><br />Guillermo Rauch</a></td>
103
- </tr>
104
- </table>
105
-
106
- ### Premium Sponsors
107
-
108
- <table>
109
- <tr>
110
- <td align="center"><a href="https://github.com/nexa-ca?ref=middleapi&amp;utm_source=middleapi&amp;utm_medium=sponsor" target="_blank" rel="noopener sponsored" title="Nexa"><img src="https://avatars.githubusercontent.com/u/199146462?v=4" width="209" alt="Nexa"/><br />Nexa</a></td>
111
- </tr>
112
- </table>
113
-
114
99
  ### Organization Sponsors
115
100
 
116
101
  <table>
117
102
  <tr>
118
- <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>
103
+ <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="209" alt="LN Markets"/><br />LN Markets</a></td>
119
104
  </tr>
120
105
  </table>
121
106
 
@@ -123,27 +108,29 @@ Like what we build over at [middleapi](https://github.com/middleapi)? You can he
123
108
 
124
109
  <table>
125
110
  <tr>
126
- <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>
127
- <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>
128
- <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>
129
- <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>
130
- <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>
131
- <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>
111
+ <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="167" alt="Reece McDonald"/><br />Reece McDonald</a></td>
112
+ <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="167" alt="あわわわとーにゅ"/><br />あわわわとーにゅ</a></td>
113
+ <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="167" alt="nk"/><br />nk</a></td>
114
+ <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="167" alt="supastarter"/><br />supastarter</a></td>
115
+ <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="167" alt="herrfugbaum"/><br />herrfugbaum</a></td>
132
116
  </tr>
133
117
  <tr>
134
- <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>
135
- <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>
136
- <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>
137
- <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>
138
- <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>
139
- <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>
118
+ <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="167" alt="Ryota Murakami"/><br />Ryota Murakami</a></td>
119
+ <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="167" alt="David Cramer"/><br />David Cramer</a></td>
120
+ <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="167" alt="Valerii Petryniak"/><br />Valerii Petryniak</a></td>
121
+ <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="167" alt="Valerii Strilets"/><br />Valerii Strilets</a></td>
122
+ <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="167" alt="Kyle Mistele"/><br />Kyle Mistele</a></td>
140
123
  </tr>
141
124
  <tr>
142
- <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>
143
- <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>
144
- <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>
145
- <td align="center"><a href="https://opencollective.com/guest-ac41de3b?ref=middleapi&amp;utm_source=middleapi&amp;utm_medium=sponsor" target="_blank" rel="noopener sponsored" title="Orestis Ioannou"><img src="https://images.opencollective.com/guest-ac41de3b/avatar/460.png" width="139" alt="Orestis Ioannou"/><br />Orestis Ioannou</a></td>
146
- <td align="center"><a href="https://automatio.ai/?ref=middleapi&amp;utm_source=middleapi&amp;utm_medium=sponsor" target="_blank" rel="noopener sponsored" title="Stefan Smiljkovic"><img src="https://avatars.githubusercontent.com/u/1984909?u=b7bf5bc40ed49df3c22f69d2da7b8d78709c49ed&amp;v=4" width="139" alt="Stefan Smiljkovic"/><br />Stefan Smiljkovic</a></td>
125
+ <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="167" alt="christ12938"/><br />christ12938</a></td>
126
+ <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="167" alt="Ryan Soderberg"/><br />Ryan Soderberg</a></td>
127
+ <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="167" alt="shota"/><br />shota</a></td>
128
+ <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="167" alt="Ellis Driscoll"/><br />Ellis Driscoll</a></td>
129
+ <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="167" alt="Hoang Nguyen"/><br />Hoang Nguyen</a></td>
130
+ </tr>
131
+ <tr>
132
+ <td align="center"><a href="https://opencollective.com/guest-ac41de3b?ref=middleapi&amp;utm_source=middleapi&amp;utm_medium=sponsor" target="_blank" rel="noopener sponsored" title="Orestis Ioannou"><img src="https://images.opencollective.com/guest-ac41de3b/avatar/460.png" width="167" alt="Orestis Ioannou"/><br />Orestis Ioannou</a></td>
133
+ <td align="center"><a href="https://automatio.ai/?ref=middleapi&amp;utm_source=middleapi&amp;utm_medium=sponsor" target="_blank" rel="noopener sponsored" title="Stefan Smiljkovic"><img src="https://avatars.githubusercontent.com/u/1984909?u=b7bf5bc40ed49df3c22f69d2da7b8d78709c49ed&amp;v=4" width="167" alt="Stefan Smiljkovic"/><br />Stefan Smiljkovic</a></td>
147
134
  </tr>
148
135
  </table>
149
136
 
@@ -151,34 +138,46 @@ Like what we build over at [middleapi](https://github.com/middleapi)? You can he
151
138
 
152
139
  <table>
153
140
  <tr>
154
- <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>
155
- <td align="center"><a href="https://github.com/IPv4Addr?ref=middleapi&amp;utm_source=middleapi&amp;utm_medium=sponsor" target="_blank" rel="noopener sponsored" title="IPv4Addr"><img src="https://avatars.githubusercontent.com/u/100147665?u=59996b72f69bb53063cb7e9ff8b8f898616cd94d&amp;v=4" width="119" alt="IPv4Addr"/><br />IPv4Addr</a></td>
156
- <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>
157
- <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>
158
- <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>
159
- <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>
160
- <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>
141
+ <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="139" alt="David Walsh"/><br />David Walsh</a></td>
142
+ <td align="center"><a href="https://github.com/IPv4Addr?ref=middleapi&amp;utm_source=middleapi&amp;utm_medium=sponsor" target="_blank" rel="noopener sponsored" title="IPv4Addr"><img src="https://avatars.githubusercontent.com/u/100147665?u=59996b72f69bb53063cb7e9ff8b8f898616cd94d&amp;v=4" width="139" alt="IPv4Addr"/><br />IPv4Addr</a></td>
143
+ <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="139" alt="Robbe Vaes"/><br />Robbe Vaes</a></td>
144
+ <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="139" alt="Aidan Sunbury"/><br />Aidan Sunbury</a></td>
145
+ <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="139" alt="soonoo"/><br />soonoo</a></td>
146
+ <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="139" alt="Kevin Porten"/><br />Kevin Porten</a></td>
147
+ </tr>
148
+ <tr>
149
+ <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="139" alt="Denis"/><br />Denis</a></td>
150
+ <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="139" alt="Christopher Kapic"/><br />Christopher Kapic</a></td>
151
+ <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="139" alt="Tom Ballinger"/><br />Tom Ballinger</a></td>
152
+ <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="139" alt="Sam"/><br />Sam</a></td>
153
+ <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="139" alt="Titoine"/><br />Titoine</a></td>
154
+ <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="139" alt="Igor Makowski"/><br />Igor Makowski</a></td>
161
155
  </tr>
162
156
  <tr>
163
- <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>
164
- <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>
165
- <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>
166
- <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>
167
- <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>
168
- <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>
169
- <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>
157
+ <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="139" alt="hanayashiki"/><br />hanayashiki</a></td>
158
+ <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="139" alt="Lev Dubinets"/><br />Lev Dubinets</a></td>
159
+ <td align="center"><a href="https://kellychan.im/?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="139" alt="Kelly Peilin Chan"/><br />Kelly Peilin Chan</a></td>
160
+ <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="139" alt="Guy Ariely"/><br />Guy Ariely</a></td>
161
+ <td align="center"><a href="https://paulsenon.com/?ref=middleapi&amp;utm_source=middleapi&amp;utm_medium=sponsor" target="_blank" rel="noopener sponsored" title="PaulSenon"><img src="https://avatars.githubusercontent.com/u/19531087?u=6385741eb91d200b8c513ec6045482e301767ca6&amp;v=4" width="139" alt="PaulSenon"/><br />PaulSenon</a></td>
162
+ <td align="center"><a href="https://nakasyou.how/?ref=middleapi&amp;utm_source=middleapi&amp;utm_medium=sponsor" target="_blank" rel="noopener sponsored" title="Shotaro Nakamura"><img src="https://avatars.githubusercontent.com/u/79000684?u=f644df3f29f0e8677a90967115774564f1d9d6ab&amp;v=4" width="139" alt="Shotaro Nakamura"/><br />Shotaro Nakamura</a></td>
170
163
  </tr>
171
164
  <tr>
172
- <td align="center"><a href="https://kellychan.im/?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>
173
- <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>
174
- <td align="center"><a href="https://paulsenon.com/?ref=middleapi&amp;utm_source=middleapi&amp;utm_medium=sponsor" target="_blank" rel="noopener sponsored" title="PaulSenon"><img src="https://avatars.githubusercontent.com/u/19531087?u=6385741eb91d200b8c513ec6045482e301767ca6&amp;v=4" width="119" alt="PaulSenon"/><br />PaulSenon</a></td>
175
- <td align="center"><a href="https://nakasyou.how/?ref=middleapi&amp;utm_source=middleapi&amp;utm_medium=sponsor" target="_blank" rel="noopener sponsored" title="Shotaro Nakamura"><img src="https://avatars.githubusercontent.com/u/79000684?u=f644df3f29f0e8677a90967115774564f1d9d6ab&amp;v=4" width="119" alt="Shotaro Nakamura"/><br />Shotaro Nakamura</a></td>
176
- <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>
177
- <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>
165
+ <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="139" alt="Alex"/><br />Alex</a></td>
166
+ <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="139" alt="Andrey Gubanov"/><br />Andrey Gubanov</a></td>
178
167
  </tr>
179
168
  </table>
180
169
 
181
- With thanks to [38 past sponsors](https://orpc.dev/sponsors#past-sponsors) who helped get oRPC here.
170
+ With thanks to [40 past sponsors](https://orpc.dev/sponsors#past-sponsors) who helped get oRPC here.
171
+
172
+ ## Open Source Programs
173
+
174
+ [middleapi](https://github.com/middleapi) is supported by these open source programs:
175
+
176
+ <table>
177
+ <tr>
178
+ <td align="center"><a href="https://claude.com/contact-sales/claude-for-oss" target="_blank" rel="noopener" title="Claude for Open Source"><img src="https://cdn.simpleicons.org/claude" width="64" alt="Claude"/><br />Claude for Open Source</a></td>
179
+ </tr>
180
+ </table>
182
181
 
183
182
  ## References
184
183
 
package/dist/index.d.mts CHANGED
@@ -60,9 +60,33 @@ declare enum JsonSchemaXNativeType {
60
60
  Map = "map"
61
61
  }
62
62
 
63
+ interface JsonSchemaCoercerOptions {
64
+ /**
65
+ * Maximum digits, excluding a leading `-`, of a string coerced to `bigint`. Longer strings are left unchanged.
66
+ * `Number.POSITIVE_INFINITY` disables it.
67
+ *
68
+ * @default 4300
69
+ */
70
+ maxBigIntDigits?: number | undefined;
71
+ /**
72
+ * Maximum coercion steps per input value, where a step applies one schema to one value.
73
+ * Inputs with fewer than 100 values are budgeted as 100. Past the budget the whole input is left uncoerced,
74
+ * so validation still runs and reports any mismatch.
75
+ *
76
+ * @default 1000
77
+ */
78
+ maxStepsPerValue?: number | undefined;
79
+ }
63
80
  declare class JsonSchemaCoercer {
81
+ private readonly nativeCoerceOptions;
82
+ private readonly maxStepsPerValue;
83
+ /** Reused between calls, since building a context is slow until V8 warms up. */
84
+ private idleContext;
85
+ constructor(options?: JsonSchemaCoercerOptions);
64
86
  coerce([schema, optional]: [schema: JsonSchema, optional: boolean], value: unknown): unknown;
65
87
  private coerceInternal;
88
+ private coerceRef;
89
+ private extendBudget;
66
90
  }
67
91
 
68
92
  /**
@@ -79,6 +103,10 @@ type JsonFileSchema = JsonSchema & object & {
79
103
  * Returns true when the schema is a file-like string schema.
80
104
  */
81
105
  declare function isJsonFileSchema(schema: JsonSchema): schema is JsonFileSchema;
106
+ /**
107
+ * Returns true when a file schema appears anywhere in the schema, such as in a property, a composition, or a def.
108
+ */
109
+ declare function containsJsonFileSchema(schema: JsonSchema): boolean;
82
110
  type JsonObjectSchema = JsonSchema & object & {
83
111
  type: 'object';
84
112
  };
@@ -120,6 +148,12 @@ declare function combineJsonObjectSchemaEntries(entries: JsonObjectSchemaEntry[]
120
148
  * Parses an object schema, or a composition of object schemas, into property entries.
121
149
  */
122
150
  declare function extractJsonObjectSchemaEntries(schema: JsonSchema): JsonObjectSchemaEntry[] | undefined;
151
+ /**
152
+ * Returns true when an object can stand for every value the schema describes: the schema constrains objects,
153
+ * by `type` or object keywords, composes only such schemas, or is unconstrained, since an object satisfies it.
154
+ * Unlike {@link extractJsonObjectSchemaEntries}, an unconstrained schema counts as object-like.
155
+ */
156
+ declare function isJsonObjectLikeSchema(schema: JsonSchema): boolean;
123
157
  /**
124
158
  * Flattens `anyOf` and `oneOf` unions when they are the only active constraints.
125
159
  */
@@ -203,8 +237,8 @@ declare function hoistRecursiveRefToDef(schema: JsonSchema): JsonSchema;
203
237
  */
204
238
  declare function resolveJsonSchemaRootLocalRef(schema: JsonSchema, $defs?: Exclude<JsonSchema, boolean>['$defs']): JsonSchema;
205
239
 
206
- interface SmartCoercionHandlerPluginOptions {
207
- converters?: undefined | JsonSchemaConverter[];
240
+ interface SmartCoercionHandlerPluginOptions extends JsonSchemaCoercerOptions {
241
+ converters?: JsonSchemaConverter[] | undefined;
208
242
  }
209
243
  /**
210
244
  * Coerces incoming request data to match the expected `.input` schema types before validation,
@@ -222,8 +256,8 @@ declare class SmartCoercionHandlerPlugin<T extends Context> implements StandardH
222
256
  private coerceValue;
223
257
  }
224
258
 
225
- interface SmartCoercionLinkPluginOptions {
226
- converters?: undefined | JsonSchemaConverter[];
259
+ interface SmartCoercionLinkPluginOptions extends JsonSchemaCoercerOptions {
260
+ converters?: JsonSchemaConverter[] | undefined;
227
261
  }
228
262
  /**
229
263
  * Coerces server responses to match the expected `.output` or `.errors` schema types before validation,
@@ -259,5 +293,5 @@ declare class StandardJsonSchemaConverter implements JsonSchemaConverter {
259
293
  convertInternal(schema: StandardJSONSchemaV1 & AnySchema, direction: JsonSchemaConverterDirection): [jsonSchema: JsonSchema, optional: boolean];
260
294
  }
261
295
 
262
- export { DelegatingJsonSchemaConverter, JsonSchemaCoercer, JsonSchemaFormat, JsonSchemaType, JsonSchemaXNativeType, SmartCoercionHandlerPlugin, SmartCoercionLinkPlugin, StandardJsonSchemaConverter, combineJsonObjectSchemaEntries, combineJsonSchemasWithComposition, decodeJsonPointerSegment, deduplicateJsonSchemas, encodeJsonPointerSegment, ensureJsonSchemaObject, extractJsonObjectSchemaEntries, flattenJsonUnionSchema, hoistRecursiveRefToDef, isJsonArraySchema, isJsonFileSchema, isJsonObjectSchema, isJsonPrimitiveSchema, isStandardSchemaOptional, isUnconstrainedSchema, mapJsonSchemaRefs, matchArrayableJsonSchema, resolveJsonSchemaRootLocalRef, visitJsonSchemaRefs };
263
- export type { JsonArraySchema, JsonFileSchema, JsonObjectSchema, JsonObjectSchemaEntry, JsonSchema, JsonSchemaConverter, JsonSchemaConverterDirection, JsonSchemaKeywords, SmartCoercionHandlerPluginOptions, SmartCoercionLinkPluginOptions };
296
+ export { DelegatingJsonSchemaConverter, JsonSchemaCoercer, JsonSchemaFormat, JsonSchemaType, JsonSchemaXNativeType, SmartCoercionHandlerPlugin, SmartCoercionLinkPlugin, StandardJsonSchemaConverter, combineJsonObjectSchemaEntries, combineJsonSchemasWithComposition, containsJsonFileSchema, decodeJsonPointerSegment, deduplicateJsonSchemas, encodeJsonPointerSegment, ensureJsonSchemaObject, extractJsonObjectSchemaEntries, flattenJsonUnionSchema, hoistRecursiveRefToDef, isJsonArraySchema, isJsonFileSchema, isJsonObjectLikeSchema, isJsonObjectSchema, isJsonPrimitiveSchema, isStandardSchemaOptional, isUnconstrainedSchema, mapJsonSchemaRefs, matchArrayableJsonSchema, resolveJsonSchemaRootLocalRef, visitJsonSchemaRefs };
297
+ export type { JsonArraySchema, JsonFileSchema, JsonObjectSchema, JsonObjectSchemaEntry, JsonSchema, JsonSchemaCoercerOptions, JsonSchemaConverter, JsonSchemaConverterDirection, JsonSchemaKeywords, SmartCoercionHandlerPluginOptions, SmartCoercionLinkPluginOptions };
package/dist/index.d.ts CHANGED
@@ -60,9 +60,33 @@ declare enum JsonSchemaXNativeType {
60
60
  Map = "map"
61
61
  }
62
62
 
63
+ interface JsonSchemaCoercerOptions {
64
+ /**
65
+ * Maximum digits, excluding a leading `-`, of a string coerced to `bigint`. Longer strings are left unchanged.
66
+ * `Number.POSITIVE_INFINITY` disables it.
67
+ *
68
+ * @default 4300
69
+ */
70
+ maxBigIntDigits?: number | undefined;
71
+ /**
72
+ * Maximum coercion steps per input value, where a step applies one schema to one value.
73
+ * Inputs with fewer than 100 values are budgeted as 100. Past the budget the whole input is left uncoerced,
74
+ * so validation still runs and reports any mismatch.
75
+ *
76
+ * @default 1000
77
+ */
78
+ maxStepsPerValue?: number | undefined;
79
+ }
63
80
  declare class JsonSchemaCoercer {
81
+ private readonly nativeCoerceOptions;
82
+ private readonly maxStepsPerValue;
83
+ /** Reused between calls, since building a context is slow until V8 warms up. */
84
+ private idleContext;
85
+ constructor(options?: JsonSchemaCoercerOptions);
64
86
  coerce([schema, optional]: [schema: JsonSchema, optional: boolean], value: unknown): unknown;
65
87
  private coerceInternal;
88
+ private coerceRef;
89
+ private extendBudget;
66
90
  }
67
91
 
68
92
  /**
@@ -79,6 +103,10 @@ type JsonFileSchema = JsonSchema & object & {
79
103
  * Returns true when the schema is a file-like string schema.
80
104
  */
81
105
  declare function isJsonFileSchema(schema: JsonSchema): schema is JsonFileSchema;
106
+ /**
107
+ * Returns true when a file schema appears anywhere in the schema, such as in a property, a composition, or a def.
108
+ */
109
+ declare function containsJsonFileSchema(schema: JsonSchema): boolean;
82
110
  type JsonObjectSchema = JsonSchema & object & {
83
111
  type: 'object';
84
112
  };
@@ -120,6 +148,12 @@ declare function combineJsonObjectSchemaEntries(entries: JsonObjectSchemaEntry[]
120
148
  * Parses an object schema, or a composition of object schemas, into property entries.
121
149
  */
122
150
  declare function extractJsonObjectSchemaEntries(schema: JsonSchema): JsonObjectSchemaEntry[] | undefined;
151
+ /**
152
+ * Returns true when an object can stand for every value the schema describes: the schema constrains objects,
153
+ * by `type` or object keywords, composes only such schemas, or is unconstrained, since an object satisfies it.
154
+ * Unlike {@link extractJsonObjectSchemaEntries}, an unconstrained schema counts as object-like.
155
+ */
156
+ declare function isJsonObjectLikeSchema(schema: JsonSchema): boolean;
123
157
  /**
124
158
  * Flattens `anyOf` and `oneOf` unions when they are the only active constraints.
125
159
  */
@@ -203,8 +237,8 @@ declare function hoistRecursiveRefToDef(schema: JsonSchema): JsonSchema;
203
237
  */
204
238
  declare function resolveJsonSchemaRootLocalRef(schema: JsonSchema, $defs?: Exclude<JsonSchema, boolean>['$defs']): JsonSchema;
205
239
 
206
- interface SmartCoercionHandlerPluginOptions {
207
- converters?: undefined | JsonSchemaConverter[];
240
+ interface SmartCoercionHandlerPluginOptions extends JsonSchemaCoercerOptions {
241
+ converters?: JsonSchemaConverter[] | undefined;
208
242
  }
209
243
  /**
210
244
  * Coerces incoming request data to match the expected `.input` schema types before validation,
@@ -222,8 +256,8 @@ declare class SmartCoercionHandlerPlugin<T extends Context> implements StandardH
222
256
  private coerceValue;
223
257
  }
224
258
 
225
- interface SmartCoercionLinkPluginOptions {
226
- converters?: undefined | JsonSchemaConverter[];
259
+ interface SmartCoercionLinkPluginOptions extends JsonSchemaCoercerOptions {
260
+ converters?: JsonSchemaConverter[] | undefined;
227
261
  }
228
262
  /**
229
263
  * Coerces server responses to match the expected `.output` or `.errors` schema types before validation,
@@ -259,5 +293,5 @@ declare class StandardJsonSchemaConverter implements JsonSchemaConverter {
259
293
  convertInternal(schema: StandardJSONSchemaV1 & AnySchema, direction: JsonSchemaConverterDirection): [jsonSchema: JsonSchema, optional: boolean];
260
294
  }
261
295
 
262
- export { DelegatingJsonSchemaConverter, JsonSchemaCoercer, JsonSchemaFormat, JsonSchemaType, JsonSchemaXNativeType, SmartCoercionHandlerPlugin, SmartCoercionLinkPlugin, StandardJsonSchemaConverter, combineJsonObjectSchemaEntries, combineJsonSchemasWithComposition, decodeJsonPointerSegment, deduplicateJsonSchemas, encodeJsonPointerSegment, ensureJsonSchemaObject, extractJsonObjectSchemaEntries, flattenJsonUnionSchema, hoistRecursiveRefToDef, isJsonArraySchema, isJsonFileSchema, isJsonObjectSchema, isJsonPrimitiveSchema, isStandardSchemaOptional, isUnconstrainedSchema, mapJsonSchemaRefs, matchArrayableJsonSchema, resolveJsonSchemaRootLocalRef, visitJsonSchemaRefs };
263
- export type { JsonArraySchema, JsonFileSchema, JsonObjectSchema, JsonObjectSchemaEntry, JsonSchema, JsonSchemaConverter, JsonSchemaConverterDirection, JsonSchemaKeywords, SmartCoercionHandlerPluginOptions, SmartCoercionLinkPluginOptions };
296
+ export { DelegatingJsonSchemaConverter, JsonSchemaCoercer, JsonSchemaFormat, JsonSchemaType, JsonSchemaXNativeType, SmartCoercionHandlerPlugin, SmartCoercionLinkPlugin, StandardJsonSchemaConverter, combineJsonObjectSchemaEntries, combineJsonSchemasWithComposition, containsJsonFileSchema, decodeJsonPointerSegment, deduplicateJsonSchemas, encodeJsonPointerSegment, ensureJsonSchemaObject, extractJsonObjectSchemaEntries, flattenJsonUnionSchema, hoistRecursiveRefToDef, isJsonArraySchema, isJsonFileSchema, isJsonObjectLikeSchema, isJsonObjectSchema, isJsonPrimitiveSchema, isStandardSchemaOptional, isUnconstrainedSchema, mapJsonSchemaRefs, matchArrayableJsonSchema, resolveJsonSchemaRootLocalRef, visitJsonSchemaRefs };
297
+ export type { JsonArraySchema, JsonFileSchema, JsonObjectSchema, JsonObjectSchemaEntry, JsonSchema, JsonSchemaCoercerOptions, JsonSchemaConverter, JsonSchemaConverterDirection, JsonSchemaKeywords, SmartCoercionHandlerPluginOptions, SmartCoercionLinkPluginOptions };
package/dist/index.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { setOwn, get, isPlainObject, tryOrUndefined, getOwn, toArray, isDeepEqual, omit, isTypescriptObject } from '@orpc/shared';
1
+ import { setOwn, get, DEFAULT_MAX_BIGINT_DIGITS, isPlainObject, tryOrUndefined, getOwn, toArray, isTypescriptObject, parseBigIntString, findDeepMatches, isDeepEqual, omit } from '@orpc/shared';
2
2
  import { ORPCError, cloneORPCError } from '@orpc/client';
3
3
  import { getProcedureContractOrThrow } from '@orpc/contract';
4
4
 
@@ -215,7 +215,7 @@ const NATIVE_TYPES = /* @__PURE__ */ new Map([
215
215
  [JsonSchemaXNativeType.BigInt, {
216
216
  types: ["string", "integer", "number"],
217
217
  is: (value) => typeof value === "bigint",
218
- coerce: (value) => typeof value === "string" ? stringToBigInt(value) : typeof value === "number" ? numberToBigInt(value) : value
218
+ coerce: (value, { maxBigIntDigits }) => typeof value === "string" ? stringToBigInt(value, maxBigIntDigits) : typeof value === "number" ? numberToBigInt(value) : value
219
219
  }],
220
220
  [JsonSchemaXNativeType.Url, {
221
221
  types: ["string"],
@@ -238,21 +238,58 @@ const NATIVE_TYPES = /* @__PURE__ */ new Map([
238
238
  function minSatisfaction(a, b) {
239
239
  return a < b ? a : b;
240
240
  }
241
+ const MIN_BUDGETED_VALUES = 100;
242
+ function createContext() {
243
+ return { rootSchema: true, memo: void 0, memoize: false, steps: 0, maxSteps: 0, input: void 0 };
244
+ }
245
+ class CoercionBudgetExceededError extends Error {
246
+ }
241
247
  class JsonSchemaCoercer {
248
+ nativeCoerceOptions;
249
+ maxStepsPerValue;
250
+ /** Reused between calls, since building a context is slow until V8 warms up. */
251
+ idleContext = createContext();
252
+ constructor(options = {}) {
253
+ this.nativeCoerceOptions = {
254
+ maxBigIntDigits: options.maxBigIntDigits ?? DEFAULT_MAX_BIGINT_DIGITS
255
+ };
256
+ this.maxStepsPerValue = options.maxStepsPerValue ?? 1e3;
257
+ }
242
258
  coerce([schema, optional], value) {
243
259
  if (optional && value === void 0) {
244
260
  return value;
245
261
  }
246
- const [, coerced] = this.coerceInternal(schema, schema, value);
247
- return coerced;
262
+ const context = this.idleContext ?? createContext();
263
+ this.idleContext = void 0;
264
+ context.rootSchema = schema;
265
+ context.memoize = false;
266
+ context.steps = 0;
267
+ context.maxSteps = this.maxStepsPerValue * MIN_BUDGETED_VALUES;
268
+ context.input = value;
269
+ try {
270
+ const [, coerced] = this.coerceInternal(context, schema, value);
271
+ return coerced;
272
+ } catch (error) {
273
+ if (error instanceof CoercionBudgetExceededError) {
274
+ return value;
275
+ }
276
+ throw error;
277
+ } finally {
278
+ context.memo = void 0;
279
+ context.input = void 0;
280
+ this.idleContext = context;
281
+ }
248
282
  }
249
- coerceInternal(rootSchema, schema, value, appliedRefs) {
283
+ coerceInternal(context, schema, value, appliedRefs) {
284
+ if (++context.steps > context.maxSteps) {
285
+ this.extendBudget(context);
286
+ }
250
287
  if (typeof schema === "boolean") {
251
288
  return [schema ? SATISFIED : UNSATISFIED, value];
252
289
  }
253
290
  if (Array.isArray(schema.type)) {
254
291
  return this.coerceInternal(
255
- rootSchema,
292
+ context,
256
293
  { anyOf: schema.type.map((type) => ({ ...schema, type })) },
257
294
  value,
258
295
  appliedRefs
@@ -261,12 +298,12 @@ class JsonSchemaCoercer {
261
298
  let coerced = value;
262
299
  let satisfied = SATISFIED;
263
300
  if (typeof schema.$ref === "string") {
264
- const resolved = schema.$ref.startsWith("#/") ? get(rootSchema, schema.$ref.slice("#/".length).split("/").map(decodeJsonPointerSegment)) : schema.$ref === "#" ? rootSchema : void 0;
301
+ const resolved = schema.$ref.startsWith("#/") ? get(context.rootSchema, schema.$ref.slice("#/".length).split("/").map(decodeJsonPointerSegment)) : schema.$ref === "#" ? context.rootSchema : void 0;
265
302
  if (resolved !== void 0 && !appliedRefs?.has(resolved)) {
266
- appliedRefs ??= /* @__PURE__ */ new Set();
267
- appliedRefs.add(resolved);
268
- const [subSatisfied, subCoerced] = this.coerceInternal(rootSchema, resolved, coerced, appliedRefs);
269
- appliedRefs.delete(resolved);
303
+ if (schema.type === "object" || schema.type === "array") {
304
+ context.memoize = true;
305
+ }
306
+ const [subSatisfied, subCoerced] = this.coerceRef(context, resolved, coerced, appliedRefs);
270
307
  coerced = subCoerced;
271
308
  satisfied = minSatisfaction(satisfied, subSatisfied);
272
309
  }
@@ -292,7 +329,7 @@ class JsonSchemaCoercer {
292
329
  const declaredNativeType = NATIVE_TYPES.get(schema["x-native-type"]);
293
330
  const nativeType = schema.type === void 0 || declaredNativeType?.types.includes(schema.type) ? declaredNativeType : void 0;
294
331
  if (nativeType && !nativeType.afterTypeCheck) {
295
- coerced = nativeType.coerce(coerced);
332
+ coerced = nativeType.coerce(coerced, this.nativeCoerceOptions);
296
333
  }
297
334
  if (schema.type && !nativeType?.is(coerced)) {
298
335
  switch (schema.type) {
@@ -346,7 +383,7 @@ class JsonSchemaCoercer {
346
383
  satisfied = minSatisfaction(satisfied, LOOSELY_SATISFIED);
347
384
  return item;
348
385
  }
349
- const [subSatisfied, subCoerced] = this.coerceInternal(rootSchema, subSchema, item);
386
+ const [subSatisfied, subCoerced] = this.coerceInternal(context, subSchema, item);
350
387
  satisfied = minSatisfaction(satisfied, subSatisfied);
351
388
  if (subCoerced !== item) {
352
389
  shouldUseCoercedItems = true;
@@ -383,7 +420,7 @@ class JsonSchemaCoercer {
383
420
  if (subSchema === void 0) {
384
421
  satisfied = minSatisfaction(satisfied, LOOSELY_SATISFIED);
385
422
  } else {
386
- const [subSatisfied, subCoerced] = this.coerceInternal(rootSchema, subSchema, value2);
423
+ const [subSatisfied, subCoerced] = this.coerceInternal(context, subSchema, value2);
387
424
  coercedItems[key] = subCoerced;
388
425
  satisfied = minSatisfaction(satisfied, subSatisfied);
389
426
  if (subCoerced !== value2) {
@@ -407,24 +444,26 @@ class JsonSchemaCoercer {
407
444
  }
408
445
  if (nativeType) {
409
446
  if (nativeType.afterTypeCheck) {
410
- coerced = nativeType.coerce(coerced);
447
+ coerced = nativeType.coerce(coerced, this.nativeCoerceOptions);
411
448
  }
412
449
  if (!nativeType.is(coerced)) {
413
450
  satisfied = UNSATISFIED;
414
451
  }
415
452
  }
416
453
  if (schema.allOf) {
454
+ context.memoize = true;
417
455
  for (const subSchema of schema.allOf) {
418
- const [subSatisfied, subCoerced] = this.coerceInternal(rootSchema, subSchema, coerced, appliedRefs);
456
+ const [subSatisfied, subCoerced] = this.coerceInternal(context, subSchema, coerced, appliedRefs);
419
457
  coerced = subCoerced;
420
458
  satisfied = minSatisfaction(satisfied, subSatisfied);
421
459
  }
422
460
  }
423
461
  for (const key of ["anyOf", "oneOf"]) {
424
462
  if (schema[key]) {
463
+ context.memoize = true;
425
464
  let best;
426
465
  for (const subSchema of schema[key]) {
427
- const [subSatisfied, subCoerced] = this.coerceInternal(rootSchema, subSchema, coerced, appliedRefs);
466
+ const [subSatisfied, subCoerced] = this.coerceInternal(context, subSchema, coerced, appliedRefs);
428
467
  if (subSatisfied === UNSATISFIED) {
429
468
  continue;
430
469
  }
@@ -445,13 +484,64 @@ class JsonSchemaCoercer {
445
484
  }
446
485
  }
447
486
  if (typeof schema.not !== "undefined") {
448
- const [notSatisfied] = this.coerceInternal(rootSchema, schema.not, coerced, appliedRefs);
487
+ context.memoize = true;
488
+ const [notSatisfied] = this.coerceInternal(context, schema.not, coerced, appliedRefs);
449
489
  if (notSatisfied !== UNSATISFIED) {
450
490
  satisfied = UNSATISFIED;
451
491
  }
452
492
  }
453
493
  return [satisfied, coerced];
454
494
  }
495
+ coerceRef(context, resolved, value, appliedRefs) {
496
+ let memo;
497
+ if (context.memoize && !appliedRefs?.size && isTypescriptObject(value)) {
498
+ context.memo ??= /* @__PURE__ */ new Map();
499
+ memo = context.memo.get(resolved);
500
+ if (!memo) {
501
+ memo = /* @__PURE__ */ new Map();
502
+ context.memo.set(resolved, memo);
503
+ }
504
+ const cached = memo.get(value);
505
+ if (cached) {
506
+ return cached;
507
+ }
508
+ }
509
+ appliedRefs ??= /* @__PURE__ */ new Set();
510
+ appliedRefs.add(resolved);
511
+ const result = this.coerceInternal(context, resolved, value, appliedRefs);
512
+ appliedRefs.delete(resolved);
513
+ memo?.set(value, result);
514
+ return result;
515
+ }
516
+ extendBudget(context) {
517
+ const limit = 2 * context.steps / this.maxStepsPerValue;
518
+ context.maxSteps = this.maxStepsPerValue * Math.max(countValues(context.input, limit), MIN_BUDGETED_VALUES);
519
+ if (context.steps > context.maxSteps) {
520
+ throw new CoercionBudgetExceededError();
521
+ }
522
+ }
523
+ }
524
+ function countValues(input, limit) {
525
+ const walked = /* @__PURE__ */ new Set();
526
+ const stack = [input];
527
+ let count = 1;
528
+ while (stack.length) {
529
+ const value = stack.pop();
530
+ if ((Array.isArray(value) || isPlainObject(value)) && !walked.has(value)) {
531
+ walked.add(value);
532
+ const items = Array.isArray(value) ? value : Object.values(value);
533
+ for (let i = 0; i < items.length; i++) {
534
+ if (++count >= limit) {
535
+ return count;
536
+ }
537
+ const item = items[i];
538
+ if (isTypescriptObject(item)) {
539
+ stack.push(item);
540
+ }
541
+ }
542
+ }
543
+ }
544
+ return count;
455
545
  }
456
546
  const NUMBER_PATTERN = /^-?(?:0|[1-9]\d*)(?:\.\d+)?$/;
457
547
  function stringToNumber(value) {
@@ -475,11 +565,8 @@ function stringToInteger(value) {
475
565
  }
476
566
  return num;
477
567
  }
478
- function stringToBigInt(value) {
479
- if (!INTEGER_PATTERN.test(value)) {
480
- return value;
481
- }
482
- return BigInt(value);
568
+ function stringToBigInt(value, maxDigits) {
569
+ return parseBigIntString(value, maxDigits) ?? value;
483
570
  }
484
571
  function numberToBigInt(value) {
485
572
  if (!Number.isInteger(value)) {
@@ -497,7 +584,7 @@ function stringToBoolean(value) {
497
584
  }
498
585
  return value;
499
586
  }
500
- const DATE_TIME_PATTERN = /^(?!-0{6})(?:[+-]\d{2})?\d{4}-\d{2}-\d{2}(?:[T ].*)?$/;
587
+ const DATE_TIME_PATTERN = /^(?!-0{6})(?:[+-]\d{2})?\d{4}-\d{2}-\d{2}(?:[T ]\d{2}:\d{2}(?::\d{2}(?:\.\d+)?)?(?:Z|[+-]\d{2}:?\d{2})?)?$/;
501
588
  const DAYS_IN_MONTH = [31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
502
589
  function stringToDate(value) {
503
590
  if (!DATE_TIME_PATTERN.test(value)) {
@@ -560,6 +647,9 @@ function isJsonPrimitiveSchema(schema) {
560
647
  function isJsonFileSchema(schema) {
561
648
  return typeof schema !== "boolean" && schema.type === "string" && (typeof schema.contentMediaType === "string" || schema.format === "binary" || schema.contentEncoding === "binary");
562
649
  }
650
+ function containsJsonFileSchema(schema) {
651
+ return findDeepMatches((v) => isPlainObject(v) && isJsonFileSchema(v), schema).values.length > 0;
652
+ }
563
653
  function isJsonObjectSchema(schema) {
564
654
  return typeof schema !== "boolean" && schema.type === "object";
565
655
  }
@@ -675,6 +765,35 @@ function extractJsonObjectSchemaEntries(schema) {
675
765
  }
676
766
  return result.entries.map(([n, s, ...r]) => [n, withRootDefs(s, schema.$defs), ...r]);
677
767
  }
768
+ const JSON_SCHEMA_OBJECT_KEYWORDS = ["properties", "patternProperties", "additionalProperties", "required", "propertyNames", "minProperties", "maxProperties"];
769
+ function isJsonObjectLikeSchema(schema) {
770
+ return isJsonObjectLikeSchemaInternal(schema, typeof schema === "object" ? schema.$defs : void 0, /* @__PURE__ */ new Set());
771
+ }
772
+ function isJsonObjectLikeSchemaInternal(schema, $defs, followedRefs) {
773
+ if (typeof schema === "object" && typeof schema.$ref === "string") {
774
+ if (followedRefs.has(schema.$ref)) {
775
+ return false;
776
+ }
777
+ followedRefs = new Set(followedRefs).add(schema.$ref);
778
+ }
779
+ const resolved = resolveJsonSchemaRootLocalRef(schema, $defs);
780
+ if (typeof resolved === "boolean") {
781
+ return true;
782
+ }
783
+ if (resolved.type !== void 0) {
784
+ return resolved.type === "object";
785
+ }
786
+ if (JSON_SCHEMA_OBJECT_KEYWORDS.some((keyword) => keyword in resolved)) {
787
+ return true;
788
+ }
789
+ for (const keyword of ["allOf", "anyOf", "oneOf"]) {
790
+ const members = resolved[keyword];
791
+ if (Array.isArray(members) && members.length > 0) {
792
+ return members.every((member) => isJsonObjectLikeSchemaInternal(member, $defs, followedRefs));
793
+ }
794
+ }
795
+ return isUnconstrainedSchema(resolved);
796
+ }
678
797
  function extractJsonObjectSchemaEntriesInternal(schema, $defs, resolvingRefs) {
679
798
  if (typeof schema !== "object") {
680
799
  return { entries: [], objectLike: false };
@@ -902,7 +1021,7 @@ class SmartCoercionHandlerPlugin {
902
1021
  ...toArray(options.converters),
903
1022
  new StandardJsonSchemaConverter()
904
1023
  ]);
905
- this.coercer = new JsonSchemaCoercer();
1024
+ this.coercer = new JsonSchemaCoercer(options);
906
1025
  }
907
1026
  init(options) {
908
1027
  return {
@@ -940,7 +1059,7 @@ class SmartCoercionLinkPlugin {
940
1059
  ...toArray(options.converters),
941
1060
  new StandardJsonSchemaConverter()
942
1061
  ]);
943
- this.coercer = new JsonSchemaCoercer();
1062
+ this.coercer = new JsonSchemaCoercer(options);
944
1063
  }
945
1064
  name = "~smart-coercion";
946
1065
  /**
@@ -995,4 +1114,4 @@ class SmartCoercionLinkPlugin {
995
1114
  }
996
1115
  }
997
1116
 
998
- export { DelegatingJsonSchemaConverter, JsonSchemaCoercer, JsonSchemaFormat, JsonSchemaType, JsonSchemaXNativeType, SmartCoercionHandlerPlugin, SmartCoercionLinkPlugin, StandardJsonSchemaConverter, combineJsonObjectSchemaEntries, combineJsonSchemasWithComposition, decodeJsonPointerSegment, deduplicateJsonSchemas, encodeJsonPointerSegment, ensureJsonSchemaObject, extractJsonObjectSchemaEntries, flattenJsonUnionSchema, hoistRecursiveRefToDef, isJsonArraySchema, isJsonFileSchema, isJsonObjectSchema, isJsonPrimitiveSchema, isStandardSchemaOptional, isUnconstrainedSchema, mapJsonSchemaRefs, matchArrayableJsonSchema, resolveJsonSchemaRootLocalRef, visitJsonSchemaRefs };
1117
+ export { DelegatingJsonSchemaConverter, JsonSchemaCoercer, JsonSchemaFormat, JsonSchemaType, JsonSchemaXNativeType, SmartCoercionHandlerPlugin, SmartCoercionLinkPlugin, StandardJsonSchemaConverter, combineJsonObjectSchemaEntries, combineJsonSchemasWithComposition, containsJsonFileSchema, decodeJsonPointerSegment, deduplicateJsonSchemas, encodeJsonPointerSegment, ensureJsonSchemaObject, extractJsonObjectSchemaEntries, flattenJsonUnionSchema, hoistRecursiveRefToDef, isJsonArraySchema, isJsonFileSchema, isJsonObjectLikeSchema, isJsonObjectSchema, isJsonPrimitiveSchema, isStandardSchemaOptional, isUnconstrainedSchema, mapJsonSchemaRefs, matchArrayableJsonSchema, resolveJsonSchemaRootLocalRef, visitJsonSchemaRefs };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@orpc/json-schema",
3
3
  "type": "module",
4
- "version": "2.0.0-beta.43",
4
+ "version": "2.0.0-beta.45",
5
5
  "description": "JSON Schema conversion and smart request coercion for oRPC and OpenAPI",
6
6
  "license": "MIT",
7
7
  "funding": [
@@ -36,14 +36,14 @@
36
36
  ],
37
37
  "dependencies": {
38
38
  "@openapi-spec/types": "~0.3.0",
39
- "@orpc/client": "2.0.0-beta.43",
40
- "@orpc/contract": "2.0.0-beta.43",
41
- "@orpc/server": "2.0.0-beta.43",
42
- "@orpc/shared": "2.0.0-beta.43",
39
+ "@orpc/client": "2.0.0-beta.45",
40
+ "@orpc/contract": "2.0.0-beta.45",
41
+ "@orpc/server": "2.0.0-beta.45",
42
+ "@orpc/shared": "2.0.0-beta.45",
43
43
  "@standard-schema/spec": "^1.1.0"
44
44
  },
45
45
  "devDependencies": {
46
- "zod": "^4.5.4"
46
+ "zod": "^4.6.7"
47
47
  },
48
48
  "scripts": {
49
49
  "type:check": "tsc -b"