@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 +71 -101
- package/dist/index.d.mts +38 -6
- package/dist/index.d.ts +38 -6
- package/dist/index.mjs +143 -90
- package/package.json +16 -6
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://
|
|
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://
|
|
30
|
-
- [@orpc/server](https://
|
|
31
|
-
- [@orpc/client](https://
|
|
32
|
-
- [@orpc/openapi](https://
|
|
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://
|
|
37
|
-
- [@orpc/valibot](https://
|
|
38
|
-
- [@orpc/arktype](https://
|
|
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
|
-
**
|
|
43
|
+
**Built-in features**
|
|
41
44
|
|
|
42
|
-
- [@orpc/
|
|
43
|
-
- [@orpc/
|
|
44
|
-
- [@orpc/
|
|
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
|
-
**
|
|
50
|
+
**Framework & ecosystem integrations**
|
|
47
51
|
|
|
48
|
-
- [@orpc/
|
|
49
|
-
- [@orpc/
|
|
50
|
-
- [@orpc/
|
|
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://
|
|
55
|
-
- [@orpc/pino](https://
|
|
56
|
-
- [@orpc/evlog](https://
|
|
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
|
|
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
|
|
77
|
+
<td width="2000"><a href="https://screenshotone.com/?ref=middleapi&utm_source=middleapi&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
|
|
80
|
+
<td width="2000"><a href="https://misskey.io/?ref=middleapi&utm_source=middleapi&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
|
-
###
|
|
84
|
+
### Organization Sponsors
|
|
79
85
|
|
|
80
86
|
<table>
|
|
81
87
|
<tr>
|
|
82
|
-
<td align="center"><a href="https://
|
|
88
|
+
<td align="center"><a href="https://lnmarkets.com/?ref=middleapi&utm_source=middleapi&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=
|
|
91
|
-
<td align="center"><a href="https://
|
|
92
|
-
<td align="center"><a href="https://github.com/
|
|
93
|
-
<td align="center"><a href="https://
|
|
94
|
-
<td align="center"><a href="https://github.com/
|
|
95
|
-
<td align="center"><a href="https://github.com/
|
|
96
|
+
<td align="center"><a href="https://github.com/hrmcdonald?ref=middleapi&utm_source=middleapi&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&utm_source=middleapi&utm_medium=sponsor" target="_blank" rel="noopener sponsored" title="γγγγγ¨γΌγ«γ
"><img src="https://avatars.githubusercontent.com/u/17376330?u=de3353804be889f009f7e0a1582daf04d0ab292d&v=4" width="139" alt="γγγγγ¨γΌγ«γ
"/><br />γγγγγ¨γΌγ«γ
</a></td>
|
|
98
|
+
<td align="center"><a href="https://github.com/nicognaW?ref=middleapi&utm_source=middleapi&utm_medium=sponsor" target="_blank" rel="noopener sponsored" title="nk"><img src="https://avatars.githubusercontent.com/u/66731869?u=4699bda3a9092d3ec34fbd959450767bcc8b8b6d&v=4" width="139" alt="nk"/><br />nk</a></td>
|
|
99
|
+
<td align="center"><a href="https://supastarter.dev/?ref=middleapi&utm_source=middleapi&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&utm_source=middleapi&utm_medium=sponsor" target="_blank" rel="noopener sponsored" title="Dexter Miguel"><img src="https://avatars.githubusercontent.com/u/5452298?u=645993204be8696c085ecf0d228c3062efe2ed65&v=4" width="139" alt="Dexter Miguel"/><br />Dexter Miguel</a></td>
|
|
101
|
+
<td align="center"><a href="https://github.com/herrfugbaum?ref=middleapi&utm_source=middleapi&utm_medium=sponsor" target="_blank" rel="noopener sponsored" title="herrfugbaum"><img src="https://avatars.githubusercontent.com/u/12859776?u=644dc1666d0220bc0468eb0de3c56b919f635b16&v=4" width="139" alt="herrfugbaum"/><br />herrfugbaum</a></td>
|
|
96
102
|
</tr>
|
|
97
103
|
<tr>
|
|
98
|
-
<td align="center"><a href="https://
|
|
99
|
-
<td align="center"><a href="https://
|
|
100
|
-
<td align="center"><a href="https://github.
|
|
101
|
-
<td align="center"><a href="https://
|
|
102
|
-
<td align="center"><a href="https://
|
|
103
|
-
<td align="center"><a href="https://github.com/
|
|
104
|
+
<td align="center"><a href="https://laststance.io/?ref=middleapi&utm_source=middleapi&utm_medium=sponsor" target="_blank" rel="noopener sponsored" title="Ryota Murakami"><img src="https://avatars.githubusercontent.com/u/5501268?u=599389e03340734325726ca3f8f423c021d47d7f&v=4" width="139" alt="Ryota Murakami"/><br />Ryota Murakami</a></td>
|
|
105
|
+
<td align="center"><a href="https://cra.mr/?ref=middleapi&utm_source=middleapi&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&utm_source=middleapi&utm_medium=sponsor" target="_blank" rel="noopener sponsored" title="Valerii Petryniak"><img src="https://avatars.githubusercontent.com/u/44531564?u=88ac74d9bacd20401518441907acad21063cd397&v=4" width="139" alt="Valerii Petryniak"/><br />Valerii Petryniak</a></td>
|
|
107
|
+
<td align="center"><a href="https://letstri.dev/?ref=middleapi&utm_source=middleapi&utm_medium=sponsor" target="_blank" rel="noopener sponsored" title="Valerii Strilets"><img src="https://avatars.githubusercontent.com/u/13253748?u=c7b10399ccc8f8081e24db94ec32cd9858e86ac3&v=4" width="139" alt="Valerii Strilets"/><br />Valerii Strilets</a></td>
|
|
108
|
+
<td align="center"><a href="https://blacklight.sh/?ref=middleapi&utm_source=middleapi&utm_medium=sponsor" target="_blank" rel="noopener sponsored" title="Kyle Mistele"><img src="https://avatars.githubusercontent.com/u/18430555?u=3afebeb81de666e35aaac3ed46f14159d7603ffb&v=4" width="139" alt="Kyle Mistele"/><br />Kyle Mistele</a></td>
|
|
109
|
+
<td align="center"><a href="https://github.com/christ12938?ref=middleapi&utm_source=middleapi&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/
|
|
107
|
-
<td align="center"><a href="https://github.com/
|
|
108
|
-
<td align="center"><a href="https://github.com/
|
|
109
|
-
<td align="center"><a href="https://github.com/
|
|
112
|
+
<td align="center"><a href="https://github.com/Ryanjso?ref=middleapi&utm_source=middleapi&utm_medium=sponsor" target="_blank" rel="noopener sponsored" title="Ryan Soderberg"><img src="https://avatars.githubusercontent.com/u/39172778?u=5ed913c31d57e7221b75784abcad48c7ebddde27&v=4" width="139" alt="Ryan Soderberg"/><br />Ryan Soderberg</a></td>
|
|
113
|
+
<td align="center"><a href="https://github.com/itigoore01?ref=middleapi&utm_source=middleapi&utm_medium=sponsor" target="_blank" rel="noopener sponsored" title="shota"><img src="https://avatars.githubusercontent.com/u/11831107?u=c976a6dc7e055eb026304c46c99100ed22b0c8e0&v=4" width="139" alt="shota"/><br />shota</a></td>
|
|
114
|
+
<td align="center"><a href="https://github.com/ellis-driscoll?ref=middleapi&utm_source=middleapi&utm_medium=sponsor" target="_blank" rel="noopener sponsored" title="Ellis Driscoll"><img src="https://avatars.githubusercontent.com/u/70685966?u=c5f95bc33b5991d9744abe00052542e4a2ed3cb9&v=4" width="139" alt="Ellis Driscoll"/><br />Ellis Driscoll</a></td>
|
|
115
|
+
<td align="center"><a href="https://github.com/hoangbn?ref=middleapi&utm_source=middleapi&utm_medium=sponsor" target="_blank" rel="noopener sponsored" title="Hoang Nguyen"><img src="https://avatars.githubusercontent.com/u/38968280?u=c90084c6de65c56facabab7ba13a72a49ddbc3e4&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=
|
|
118
|
-
<td align="center"><a href="https://
|
|
119
|
-
<td align="center"><a href="https://github.com/aidansunbury?ref=
|
|
120
|
-
<td align="center"><a href="https://github.com/soonoo?ref=
|
|
121
|
-
<td align="center"><a href="https://
|
|
122
|
-
<td align="center"><a href="https://github.com/pumpkinlink?ref=
|
|
123
|
-
<td align="center"><a href="https://github.com/christopher-kapic?ref=
|
|
123
|
+
<td align="center"><a href="https://github.com/rhinodavid?ref=middleapi&utm_source=middleapi&utm_medium=sponsor" target="_blank" rel="noopener sponsored" title="David Walsh"><img src="https://avatars.githubusercontent.com/u/5778036?u=b5521f07d2f88c3db2a0dae62b5f2f8357214af0&v=4" width="119" alt="David Walsh"/><br />David Walsh</a></td>
|
|
124
|
+
<td align="center"><a href="https://robbevaes.be/?ref=middleapi&utm_source=middleapi&utm_medium=sponsor" target="_blank" rel="noopener sponsored" title="Robbe Vaes"><img src="https://avatars.githubusercontent.com/u/44748019?u=e0232402c045ad4eac7cbd217f1f47e083103b89&v=4" width="119" alt="Robbe Vaes"/><br />Robbe Vaes</a></td>
|
|
125
|
+
<td align="center"><a href="https://github.com/aidansunbury?ref=middleapi&utm_source=middleapi&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&utm_source=middleapi&utm_medium=sponsor" target="_blank" rel="noopener sponsored" title="soonoo"><img src="https://avatars.githubusercontent.com/u/5436405?u=5d0b4aa955c87e30e6bda7f0cccae5402da99528&v=4" width="119" alt="soonoo"/><br />soonoo</a></td>
|
|
127
|
+
<td align="center"><a href="https://kevinporten.dev/?ref=middleapi&utm_source=middleapi&utm_medium=sponsor" target="_blank" rel="noopener sponsored" title="Kevin Porten"><img src="https://avatars.githubusercontent.com/u/1839345?u=dc2263d5cfe0d927ce1a0be04a1d55dd6b55405c&v=4" width="119" alt="Kevin Porten"/><br />Kevin Porten</a></td>
|
|
128
|
+
<td align="center"><a href="https://github.com/pumpkinlink?ref=middleapi&utm_source=middleapi&utm_medium=sponsor" target="_blank" rel="noopener sponsored" title="Denis"><img src="https://avatars.githubusercontent.com/u/11864620?u=5f47bbe6c65d0f6f5cf011021490238e4b0593d0&v=4" width="119" alt="Denis"/><br />Denis</a></td>
|
|
129
|
+
<td align="center"><a href="https://github.com/christopher-kapic?ref=middleapi&utm_source=middleapi&utm_medium=sponsor" target="_blank" rel="noopener sponsored" title="Christopher Kapic"><img src="https://avatars.githubusercontent.com/u/59740769?u=e7ad4b72b5bf6c9eb1644c26dbf3332a8f987377&v=4" width="119" alt="Christopher Kapic"/><br />Christopher Kapic</a></td>
|
|
124
130
|
</tr>
|
|
125
131
|
<tr>
|
|
126
|
-
<td align="center"><a href="
|
|
127
|
-
<td align="center"><a href="https://
|
|
128
|
-
<td align="center"><a href="https://github.com/Titoine?ref=
|
|
129
|
-
<td align="center"><a href="https://
|
|
130
|
-
<td align="center"><a href="https://
|
|
131
|
-
<td align="center"><a href="https://
|
|
132
|
-
<td align="center"><a href="https://
|
|
132
|
+
<td align="center"><a href="http://ballingt.com/?ref=middleapi&utm_source=middleapi&utm_medium=sponsor" target="_blank" rel="noopener sponsored" title="Tom Ballinger"><img src="https://avatars.githubusercontent.com/u/458879?u=4b045ac75d721b6ac2b42a74d7d37f61f0414031&v=4" width="119" alt="Tom Ballinger"/><br />Tom Ballinger</a></td>
|
|
133
|
+
<td align="center"><a href="https://lee-sam.com/?ref=middleapi&utm_source=middleapi&utm_medium=sponsor" target="_blank" rel="noopener sponsored" title="Sam"><img src="https://avatars.githubusercontent.com/u/102863520?u=3c89611f549d5070be232eb4532f690c8f2e7a65&v=4" width="119" alt="Sam"/><br />Sam</a></td>
|
|
134
|
+
<td align="center"><a href="https://github.com/Titoine?ref=middleapi&utm_source=middleapi&utm_medium=sponsor" target="_blank" rel="noopener sponsored" title="Titoine"><img src="https://avatars.githubusercontent.com/u/3514286?u=1bb1e86b0c99c8a1121372e56d51a177eea12191&v=4" width="119" alt="Titoine"/><br />Titoine</a></td>
|
|
135
|
+
<td align="center"><a href="https://rigtch.fm/?ref=middleapi&utm_source=middleapi&utm_medium=sponsor" target="_blank" rel="noopener sponsored" title="Igor Makowski"><img src="https://avatars.githubusercontent.com/u/56691628?u=ee8c879478f7c151b9156aef6c74243fa3e247a8&v=4" width="119" alt="Igor Makowski"/><br />Igor Makowski</a></td>
|
|
136
|
+
<td align="center"><a href="https://blog.cwang.io/?ref=middleapi&utm_source=middleapi&utm_medium=sponsor" target="_blank" rel="noopener sponsored" title="hanayashiki"><img src="https://avatars.githubusercontent.com/u/26056783?u=06c3b9205a16fd41a871e82da1cc2a09306d53f5&v=4" width="119" alt="hanayashiki"/><br />hanayashiki</a></td>
|
|
137
|
+
<td align="center"><a href="https://dubinets.io/?ref=middleapi&utm_source=middleapi&utm_medium=sponsor" target="_blank" rel="noopener sponsored" title="Lev Dubinets"><img src="https://avatars.githubusercontent.com/u/3114081?u=f547f5d5012cab54851f1b1ad72d10e537f78fc2&v=4" width="119" alt="Lev Dubinets"/><br />Lev Dubinets</a></td>
|
|
138
|
+
<td align="center"><a href="https://bika.ai/?ref=middleapi&utm_source=middleapi&utm_medium=sponsor" target="_blank" rel="noopener sponsored" title="Kelly Peilin Chan"><img src="https://avatars.githubusercontent.com/u/520852?u=6b0f7105f694e7b5cacf410a3f04c7044b469dc8&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://
|
|
136
|
-
<td align="center"><a href="https://
|
|
137
|
-
<td align="center"><a href="https://
|
|
141
|
+
<td align="center"><a href="https://guyariely.com/?ref=middleapi&utm_source=middleapi&utm_medium=sponsor" target="_blank" rel="noopener sponsored" title="Guy Ariely"><img src="https://avatars.githubusercontent.com/u/42813496?u=edb6b7f563bf28e160a290832e7da57c0506f8ca&v=4" width="119" alt="Guy Ariely"/><br />Guy Ariely</a></td>
|
|
142
|
+
<td align="center"><a href="https://piscis.dev/?ref=middleapi&utm_source=middleapi&utm_medium=sponsor" target="_blank" rel="noopener sponsored" title="Alex"><img src="https://avatars.githubusercontent.com/u/326163?u=b245f368bd940cf51d08c0b6bf55f8257f359437&v=4" width="119" alt="Alex"/><br />Alex</a></td>
|
|
143
|
+
<td align="center"><a href="https://opensource.gubanov.eu/?ref=middleapi&utm_source=middleapi&utm_medium=sponsor" target="_blank" rel="noopener sponsored" title="Andrey Gubanov"><img src="https://avatars.githubusercontent.com/u/1082083?u=c5f2daf7ebece498e85c83367bb37b4e10e2649d&v=4" width="119" alt="Andrey Gubanov"/><br />Andrey Gubanov</a></td>
|
|
138
144
|
</tr>
|
|
139
145
|
</table>
|
|
140
146
|
|
|
141
|
-
|
|
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&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&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&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&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&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&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&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&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&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&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&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&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&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&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&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&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&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&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&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&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&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&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&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&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&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&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
|
-
|
|
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):
|
|
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):
|
|
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):
|
|
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
|
-
|
|
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):
|
|
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):
|
|
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):
|
|
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,
|
|
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
|
|
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 =
|
|
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
|
-
|
|
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 =
|
|
222
|
+
satisfied = UNSATISFIED;
|
|
193
223
|
}
|
|
194
224
|
}
|
|
195
225
|
} else {
|
|
196
|
-
satisfied =
|
|
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 =
|
|
233
|
+
satisfied = UNSATISFIED;
|
|
204
234
|
}
|
|
205
235
|
break;
|
|
206
236
|
}
|
|
207
237
|
case "string": {
|
|
208
238
|
if (typeof coerced !== "string") {
|
|
209
|
-
satisfied =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
278
|
+
satisfied = minSatisfaction(satisfied, LOOSELY_SATISFIED);
|
|
249
279
|
return item;
|
|
250
280
|
}
|
|
251
281
|
const [subSatisfied, subCoerced] = this.coerceInternal(rootSchema, subSchema, item);
|
|
252
|
-
|
|
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 =
|
|
289
|
+
satisfied = UNSATISFIED;
|
|
262
290
|
}
|
|
263
291
|
if (shouldUseCoercedItems) {
|
|
264
292
|
coerced = coercedItems;
|
|
265
293
|
}
|
|
266
294
|
} else {
|
|
267
|
-
satisfied =
|
|
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 ?? {}).
|
|
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 =
|
|
282
|
-
if (value2
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
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 =
|
|
328
|
+
satisfied = UNSATISFIED;
|
|
300
329
|
}
|
|
301
330
|
if (shouldUseCoercedItems) {
|
|
302
331
|
coerced = coercedItems;
|
|
303
332
|
}
|
|
304
333
|
} else {
|
|
305
|
-
satisfied =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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
|
-
|
|
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
|
|
412
|
+
let best;
|
|
386
413
|
for (const subSchema of schema[key]) {
|
|
387
|
-
const [subSatisfied, subCoerced] = this.coerceInternal(rootSchema, subSchema, coerced);
|
|
388
|
-
if (subSatisfied) {
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
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
|
-
|
|
398
|
-
|
|
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 =
|
|
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
|
-
|
|
412
|
-
|
|
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
|
-
|
|
419
|
-
|
|
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
|
-
|
|
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())
|
|
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(
|
|
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
|
-
|
|
899
|
+
convert(schema, direction) {
|
|
848
900
|
for (const converter of this.converters) {
|
|
849
|
-
if (
|
|
901
|
+
if (converter.condition(schema, direction)) {
|
|
850
902
|
return converter.convert(schema, direction);
|
|
851
903
|
}
|
|
852
904
|
}
|
|
853
|
-
const
|
|
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
|
-
|
|
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 =
|
|
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
|
-
|
|
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 =
|
|
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 =
|
|
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
|
|
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 =
|
|
1030
|
+
cloned.data = this.coerceValue([dataSchema], cloned.data);
|
|
978
1031
|
throw cloned;
|
|
979
1032
|
}
|
|
980
1033
|
}
|
|
981
1034
|
]
|
|
982
1035
|
};
|
|
983
1036
|
}
|
|
984
|
-
|
|
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 =
|
|
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.
|
|
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/
|
|
31
|
-
"@orpc/
|
|
32
|
-
"@orpc/
|
|
33
|
-
"@orpc/
|
|
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"
|