@orpc/json-schema 2.0.0-beta.2 → 2.0.0-beta.21
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 +32 -21
- package/dist/index.d.mts +11 -10
- package/dist/index.d.ts +11 -10
- package/dist/index.mjs +156 -105
- package/package.json +5 -5
package/README.md
CHANGED
|
@@ -7,6 +7,9 @@
|
|
|
7
7
|
<a href="https://www.npmjs.com/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,10 +29,10 @@ You can read the documentation [here](https://orpc.dev).
|
|
|
26
29
|
|
|
27
30
|
**Core**
|
|
28
31
|
|
|
29
|
-
- [@orpc/contract](https://www.npmjs.com/package/@orpc/contract): Define
|
|
30
|
-
- [@orpc/server](https://www.npmjs.com/package/@orpc/server):
|
|
31
|
-
- [@orpc/client](https://www.npmjs.com/package/@orpc/client): Consume
|
|
32
|
-
- [@orpc/openapi](https://www.npmjs.com/package/@orpc/openapi):
|
|
32
|
+
- [@orpc/contract](https://www.npmjs.com/package/@orpc/contract): Define API contract as the single source of truth.
|
|
33
|
+
- [@orpc/server](https://www.npmjs.com/package/@orpc/server): Build APIs or implement contracts.
|
|
34
|
+
- [@orpc/client](https://www.npmjs.com/package/@orpc/client): Consume APIs with end-to-end type safety.
|
|
35
|
+
- [@orpc/openapi](https://www.npmjs.com/package/@orpc/openapi): Add OpenAPI compatibility to APIs.
|
|
33
36
|
|
|
34
37
|
**Schema validation**
|
|
35
38
|
|
|
@@ -37,23 +40,30 @@ You can read the documentation [here](https://orpc.dev).
|
|
|
37
40
|
- [@orpc/valibot](https://www.npmjs.com/package/@orpc/valibot): Integrate with [Valibot](https://valibot.dev/).
|
|
38
41
|
- [@orpc/arktype](https://www.npmjs.com/package/@orpc/arktype): Integrate with [ArkType](https://arktype.io/).
|
|
39
42
|
|
|
43
|
+
**Built-in features**
|
|
44
|
+
|
|
45
|
+
- [@orpc/publisher](https://www.npmjs.com/package/@orpc/publisher): Pub/Sub with memory, Redis, and Upstash adapters.
|
|
46
|
+
- [@orpc/ratelimit](https://www.npmjs.com/package/@orpc/ratelimit): Rate limiting with memory, Redis, and Upstash adapters.
|
|
47
|
+
- [@orpc/json-schema](https://www.npmjs.com/package/@orpc/json-schema): Smart coercion for OpenAPI requests.
|
|
48
|
+
|
|
40
49
|
**Framework & ecosystem integrations**
|
|
41
50
|
|
|
42
|
-
- [@orpc/next](https://www.npmjs.com/package/@orpc/
|
|
51
|
+
- [@orpc/next](https://www.npmjs.com/package/@orpc/next): Integrate with [Next.js Server Functions](https://nextjs.org/docs/app/getting-started/mutating-data).
|
|
52
|
+
- [@orpc/ai-sdk](https://www.npmjs.com/package/@orpc/ai-sdk): Turn contracts and procedures into [AI SDK](https://ai-sdk.dev/) tools.
|
|
43
53
|
- [@orpc/tanstack-query](https://www.npmjs.com/package/@orpc/tanstack-query): Integrate with [TanStack Query](https://tanstack.com/query/latest).
|
|
54
|
+
- [@orpc/pinia-colada](https://www.npmjs.com/package/@orpc/pinia-colada): Integrate with [Pinia Colada](https://pinia-colada.esm.dev/).
|
|
55
|
+
- [@orpc/swr](https://www.npmjs.com/package/@orpc/swr): Integrate with [SWR](https://swr.vercel.app/).
|
|
44
56
|
- [@orpc/experimental-effect](https://www.npmjs.com/package/@orpc/experimental-effect): Integrate with [Effect](https://effect.website/).
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
- [@orpc/
|
|
49
|
-
- [@orpc/ratelimit](https://www.npmjs.com/package/@orpc/ratelimit): Rate limiting.
|
|
50
|
-
- [@orpc/json-schema](https://www.npmjs.com/package/@orpc/json-schema): Smart coercion for OpenAPI requests.
|
|
57
|
+
- [@orpc/nest](https://www.npmjs.com/package/@orpc/nest): Implement your contract with [NestJS](https://nestjs.com/).
|
|
58
|
+
- [@orpc/bun](https://www.npmjs.com/package/@orpc/bun): Adapters for [Bun's Redis](https://bun.sh/).
|
|
59
|
+
- [@orpc/cloudflare](https://www.npmjs.com/package/@orpc/cloudflare): Adapters for [Cloudflare's RateLimit and Durable Objects](https://developers.cloudflare.com/workers/).
|
|
60
|
+
- [@orpc/trpc](https://www.npmjs.com/package/@orpc/trpc): Reuse existing [tRPC](https://trpc.io/) routers within oRPC.
|
|
51
61
|
|
|
52
62
|
**Observability**
|
|
53
63
|
|
|
54
|
-
- [@orpc/opentelemetry](https://www.npmjs.com/package/@orpc/opentelemetry):
|
|
55
|
-
- [@orpc/pino](https://www.npmjs.com/package/@orpc/pino):
|
|
56
|
-
- [@orpc/evlog](https://www.npmjs.com/package/@orpc/evlog):
|
|
64
|
+
- [@orpc/opentelemetry](https://www.npmjs.com/package/@orpc/opentelemetry): Integrate with [OpenTelemetry](https://opentelemetry.io/) for distributed tracing.
|
|
65
|
+
- [@orpc/pino](https://www.npmjs.com/package/@orpc/pino): Integrate with [Pino](https://getpino.io/) for logging.
|
|
66
|
+
- [@orpc/evlog](https://www.npmjs.com/package/@orpc/evlog): Integrate with [Evlog](https://evlog.dev/) for logging.
|
|
57
67
|
|
|
58
68
|
## Sponsors
|
|
59
69
|
|
|
@@ -99,12 +109,10 @@ Like what we build over at [middleapi](https://github.com/middleapi)? You can he
|
|
|
99
109
|
<td align="center"><a href="https://github.com/valerii15298?ref=orpc" target="_blank" rel="noopener" title="Valerii Petryniak"><img src="https://avatars.githubusercontent.com/u/44531564?u=88ac74d9bacd20401518441907acad21063cd397&v=4" width="139" alt="Valerii Petryniak"/><br />Valerii Petryniak</a></td>
|
|
100
110
|
<td align="center"><a href="https://github.com/letstri?ref=orpc" target="_blank" rel="noopener" title="Valerii Strilets"><img src="https://avatars.githubusercontent.com/u/13253748?u=c7b10399ccc8f8081e24db94ec32cd9858e86ac3&v=4" width="139" alt="Valerii Strilets"/><br />Valerii Strilets</a></td>
|
|
101
111
|
<td align="center"><a href="https://github.com/K-Mistele?ref=orpc" target="_blank" rel="noopener" title="Kyle Mistele"><img src="https://avatars.githubusercontent.com/u/18430555?u=3afebeb81de666e35aaac3ed46f14159d7603ffb&v=4" width="139" alt="Kyle Mistele"/><br />Kyle Mistele</a></td>
|
|
102
|
-
<td align="center"><a href="https://github.com/andrewpeters9?ref=orpc" target="_blank" rel="noopener" title="Andrew Peters"><img src="https://avatars.githubusercontent.com/u/36251325?v=4" width="139" alt="Andrew Peters"/><br />Andrew Peters</a></td>
|
|
103
112
|
<td align="center"><a href="https://github.com/R44VC0RP?ref=orpc" target="_blank" rel="noopener" title="Ryan Vogel"><img src="https://avatars.githubusercontent.com/u/89211796?u=1857347b9787d8d8a7ea5bfc333f96be92d5a683&v=4" width="139" alt="Ryan Vogel"/><br />Ryan Vogel</a></td>
|
|
113
|
+
<td align="center"><a href="https://github.com/christ12938?ref=orpc" target="_blank" rel="noopener" title="christ12938"><img src="https://avatars.githubusercontent.com/u/25758598?v=4" width="139" alt="christ12938"/><br />christ12938</a></td>
|
|
104
114
|
</tr>
|
|
105
115
|
<tr>
|
|
106
|
-
<td align="center"><a href="https://github.com/christ12938?ref=orpc" target="_blank" rel="noopener" title="christ12938"><img src="https://avatars.githubusercontent.com/u/25758598?v=4" width="139" alt="christ12938"/><br />christ12938</a></td>
|
|
107
|
-
<td align="center"><a href="https://github.com/peter-adam-dy?ref=orpc" target="_blank" rel="noopener" title="Peter Adam"><img src="https://avatars.githubusercontent.com/u/132129459?u=4f3dbbb3b443990b56acb7d6a5d11ed2c555f6db&v=4" width="139" alt="Peter Adam"/><br />Peter Adam</a></td>
|
|
108
116
|
<td align="center"><a href="https://github.com/Ryanjso?ref=orpc" target="_blank" rel="noopener" title="Ryan Soderberg"><img src="https://avatars.githubusercontent.com/u/39172778?u=5ed913c31d57e7221b75784abcad48c7ebddde27&v=4" width="139" alt="Ryan Soderberg"/><br />Ryan Soderberg</a></td>
|
|
109
117
|
<td align="center"><a href="https://github.com/itigoore01?ref=orpc" target="_blank" rel="noopener" title="shota"><img src="https://avatars.githubusercontent.com/u/11831107?u=c976a6dc7e055eb026304c46c99100ed22b0c8e0&v=4" width="139" alt="shota"/><br />shota</a></td>
|
|
110
118
|
</tr>
|
|
@@ -115,25 +123,25 @@ Like what we build over at [middleapi](https://github.com/middleapi)? You can he
|
|
|
115
123
|
<table>
|
|
116
124
|
<tr>
|
|
117
125
|
<td align="center"><a href="https://github.com/rhinodavid?ref=orpc" target="_blank" rel="noopener" title="David Walsh"><img src="https://avatars.githubusercontent.com/u/5778036?u=b5521f07d2f88c3db2a0dae62b5f2f8357214af0&v=4" width="119" alt="David Walsh"/><br />David Walsh</a></td>
|
|
126
|
+
<td align="center"><a href="https://github.com/Nic13Gamer?ref=orpc" target="_blank" rel="noopener" title="Nicholas"><img src="https://avatars.githubusercontent.com/u/54724556?u=56a7ab430ce7a80d648ab6eba051d454a818ed0b&v=4" width="119" alt="Nicholas"/><br />Nicholas</a></td>
|
|
118
127
|
<td align="center"><a href="https://github.com/Robbe95?ref=orpc" target="_blank" rel="noopener" title="Robbe Vaes"><img src="https://avatars.githubusercontent.com/u/44748019?u=e0232402c045ad4eac7cbd217f1f47e083103b89&v=4" width="119" alt="Robbe Vaes"/><br />Robbe Vaes</a></td>
|
|
119
128
|
<td align="center"><a href="https://github.com/aidansunbury?ref=orpc" target="_blank" rel="noopener" title="Aidan Sunbury"><img src="https://avatars.githubusercontent.com/u/64103161?v=4" width="119" alt="Aidan Sunbury"/><br />Aidan Sunbury</a></td>
|
|
120
129
|
<td align="center"><a href="https://github.com/soonoo?ref=orpc" target="_blank" rel="noopener" title="soonoo"><img src="https://avatars.githubusercontent.com/u/5436405?u=5d0b4aa955c87e30e6bda7f0cccae5402da99528&v=4" width="119" alt="soonoo"/><br />soonoo</a></td>
|
|
121
130
|
<td align="center"><a href="https://github.com/kporten?ref=orpc" target="_blank" rel="noopener" title="Kevin Porten"><img src="https://avatars.githubusercontent.com/u/1839345?u=dc2263d5cfe0d927ce1a0be04a1d55dd6b55405c&v=4" width="119" alt="Kevin Porten"/><br />Kevin Porten</a></td>
|
|
122
131
|
<td align="center"><a href="https://github.com/pumpkinlink?ref=orpc" target="_blank" rel="noopener" title="Denis"><img src="https://avatars.githubusercontent.com/u/11864620?u=5f47bbe6c65d0f6f5cf011021490238e4b0593d0&v=4" width="119" alt="Denis"/><br />Denis</a></td>
|
|
123
|
-
<td align="center"><a href="https://github.com/christopher-kapic?ref=orpc" target="_blank" rel="noopener" title="Christopher Kapic"><img src="https://avatars.githubusercontent.com/u/59740769?u=e7ad4b72b5bf6c9eb1644c26dbf3332a8f987377&v=4" width="119" alt="Christopher Kapic"/><br />Christopher Kapic</a></td>
|
|
124
132
|
</tr>
|
|
125
133
|
<tr>
|
|
134
|
+
<td align="center"><a href="https://github.com/christopher-kapic?ref=orpc" target="_blank" rel="noopener" title="Christopher Kapic"><img src="https://avatars.githubusercontent.com/u/59740769?u=e7ad4b72b5bf6c9eb1644c26dbf3332a8f987377&v=4" width="119" alt="Christopher Kapic"/><br />Christopher Kapic</a></td>
|
|
126
135
|
<td align="center"><a href="https://github.com/thomasballinger?ref=orpc" target="_blank" rel="noopener" title="Tom Ballinger"><img src="https://avatars.githubusercontent.com/u/458879?u=4b045ac75d721b6ac2b42a74d7d37f61f0414031&v=4" width="119" alt="Tom Ballinger"/><br />Tom Ballinger</a></td>
|
|
127
136
|
<td align="center"><a href="https://github.com/SSam0419?ref=orpc" target="_blank" rel="noopener" title="Sam"><img src="https://avatars.githubusercontent.com/u/102863520?u=3c89611f549d5070be232eb4532f690c8f2e7a65&v=4" width="119" alt="Sam"/><br />Sam</a></td>
|
|
128
137
|
<td align="center"><a href="https://github.com/Titoine?ref=orpc" target="_blank" rel="noopener" title="Titoine"><img src="https://avatars.githubusercontent.com/u/3514286?u=1bb1e86b0c99c8a1121372e56d51a177eea12191&v=4" width="119" alt="Titoine"/><br />Titoine</a></td>
|
|
129
138
|
<td align="center"><a href="https://github.com/Mnigos?ref=orpc" target="_blank" rel="noopener" title="Igor Makowski"><img src="https://avatars.githubusercontent.com/u/56691628?u=ee8c879478f7c151b9156aef6c74243fa3e247a8&v=4" width="119" alt="Igor Makowski"/><br />Igor Makowski</a></td>
|
|
130
139
|
<td align="center"><a href="https://github.com/hanayashiki?ref=orpc" target="_blank" rel="noopener" title="hanayashiki"><img src="https://avatars.githubusercontent.com/u/26056783?u=06c3b9205a16fd41a871e82da1cc2a09306d53f5&v=4" width="119" alt="hanayashiki"/><br />hanayashiki</a></td>
|
|
131
140
|
<td align="center"><a href="https://github.com/ldub?ref=orpc" target="_blank" rel="noopener" title="Lev Dubinets"><img src="https://avatars.githubusercontent.com/u/3114081?u=f547f5d5012cab54851f1b1ad72d10e537f78fc2&v=4" width="119" alt="Lev Dubinets"/><br />Lev Dubinets</a></td>
|
|
132
|
-
<td align="center"><a href="https://github.com/mr-kelly?ref=orpc" target="_blank" rel="noopener" title="Kelly Peilin Chan"><img src="https://avatars.githubusercontent.com/u/520852?u=6b0f7105f694e7b5cacf410a3f04c7044b469dc8&v=4" width="119" alt="Kelly Peilin Chan"/><br />Kelly Peilin Chan</a></td>
|
|
133
141
|
</tr>
|
|
134
142
|
<tr>
|
|
143
|
+
<td align="center"><a href="https://github.com/mr-kelly?ref=orpc" target="_blank" rel="noopener" title="Kelly Peilin Chan"><img src="https://avatars.githubusercontent.com/u/520852?u=6b0f7105f694e7b5cacf410a3f04c7044b469dc8&v=4" width="119" alt="Kelly Peilin Chan"/><br />Kelly Peilin Chan</a></td>
|
|
135
144
|
<td align="center"><a href="https://github.com/piscis?ref=orpc" target="_blank" rel="noopener" title="Alex"><img src="https://avatars.githubusercontent.com/u/326163?u=b245f368bd940cf51d08c0b6bf55f8257f359437&v=4" width="119" alt="Alex"/><br />Alex</a></td>
|
|
136
|
-
<td align="center"><a href="https://github.com/nattstack?ref=orpc" target="_blank" rel="noopener" title="nattstack"><img src="https://avatars.githubusercontent.com/u/31426677?u=fa9dbb8b3e66eb0ea3c88db5dc07f31c8c5418fe&v=4" width="119" alt="nattstack"/><br />nattstack</a></td>
|
|
137
145
|
<td align="center"><a href="https://github.com/finom?ref=orpc" target="_blank" rel="noopener" title="Andrey Gubanov"><img src="https://avatars.githubusercontent.com/u/1082083?u=29e91400dbd4a9c217048a8f59562c4f740498e6&v=4" width="119" alt="Andrey Gubanov"/><br />Andrey Gubanov</a></td>
|
|
138
146
|
</tr>
|
|
139
147
|
</table>
|
|
@@ -150,7 +158,7 @@ Like what we build over at [middleapi](https://github.com/middleapi)? You can he
|
|
|
150
158
|
<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
159
|
<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
160
|
<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=
|
|
161
|
+
<a href="https://github.com/johngerome?ref=orpc" target="_blank" rel="noopener" title="0x4e32"><img src="https://avatars.githubusercontent.com/u/2002000?u=505e54608466ab53754f702973687b04c6424c1f&v=4" width="32" height="32" alt="0x4e32" /></a>
|
|
154
162
|
<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
163
|
<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
164
|
<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>
|
|
@@ -164,7 +172,9 @@ Like what we build over at [middleapi](https://github.com/middleapi)? You can he
|
|
|
164
172
|
<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
173
|
<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
174
|
<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>
|
|
175
|
+
<a href="https://github.com/andrewpeters9?ref=orpc" target="_blank" rel="noopener" title="Andrew Peters"><img src="https://avatars.githubusercontent.com/u/36251325?v=4" width="32" height="32" alt="Andrew Peters" /></a>
|
|
167
176
|
<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>
|
|
177
|
+
<a href="https://github.com/peter-adam-dy?ref=orpc" target="_blank" rel="noopener" title="Peter Adam"><img src="https://avatars.githubusercontent.com/u/132129459?u=4f3dbbb3b443990b56acb7d6a5d11ed2c555f6db&v=4" width="32" height="32" alt="Peter Adam" /></a>
|
|
168
178
|
<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
179
|
<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
180
|
<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>
|
|
@@ -172,6 +182,7 @@ Like what we build over at [middleapi](https://github.com/middleapi)? You can he
|
|
|
172
182
|
<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
183
|
<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
184
|
<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>
|
|
185
|
+
<a href="https://github.com/nattstack?ref=orpc" target="_blank" rel="noopener" title="natt"><img src="https://avatars.githubusercontent.com/u/31426677?u=fa9dbb8b3e66eb0ea3c88db5dc07f31c8c5418fe&v=4" width="32" height="32" alt="natt" /></a>
|
|
175
186
|
<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
187
|
<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
188
|
</p>
|
package/dist/index.d.mts
CHANGED
|
@@ -2,14 +2,13 @@ import * as Draft2020 from 'json-schema-typed/draft-2020-12';
|
|
|
2
2
|
export { Format as JsonSchemaFormat, TypeName as JsonSchemaType } from 'json-schema-typed/draft-2020-12';
|
|
3
3
|
import { AnySchema, RouterContract } from '@orpc/contract';
|
|
4
4
|
export { AnySchema, Schema } from '@orpc/contract';
|
|
5
|
-
import { Promisable } from '@orpc/shared';
|
|
6
5
|
import { Context } from '@orpc/server';
|
|
7
6
|
import { StandardHandlerPlugin, StandardHandlerOptions } from '@orpc/server/standard';
|
|
8
7
|
import { ClientContext } from '@orpc/client';
|
|
9
8
|
import { StandardLinkPlugin, StandardLinkOptions } from '@orpc/client/standard';
|
|
10
9
|
import { StandardJSONSchemaV1 } from '@standard-schema/spec';
|
|
11
10
|
|
|
12
|
-
type JsonSchema = Draft2020.JSONSchema
|
|
11
|
+
type JsonSchema<Value = any> = Draft2020.JSONSchema<Value>;
|
|
13
12
|
type JsonSchemaKeywords = typeof Draft2020.keywords[number];
|
|
14
13
|
declare enum JsonSchemaXNativeType {
|
|
15
14
|
BigInt = "bigint",
|
|
@@ -30,6 +29,7 @@ declare class JsonSchemaCoercer {
|
|
|
30
29
|
* and exclusively use absolute JSON pointers for `$ref` values.
|
|
31
30
|
*/
|
|
32
31
|
|
|
32
|
+
declare function isJsonPrimitiveSchema(schema: JsonSchema): schema is JsonSchema & object;
|
|
33
33
|
type JsonFileSchema = JsonSchema & object & {
|
|
34
34
|
type: 'string';
|
|
35
35
|
contentMediaType?: string;
|
|
@@ -70,10 +70,6 @@ declare function ensureJsonSchemaObject(schema: JsonSchema): Exclude<JsonSchema,
|
|
|
70
70
|
* Combines multiple schemas under the requested composition keyword, promoting branch `$defs` to the root.
|
|
71
71
|
*/
|
|
72
72
|
declare function combineJsonSchemasWithComposition(keyword: 'allOf' | 'anyOf' | 'oneOf', schemas: JsonSchema[]): JsonSchema;
|
|
73
|
-
/**
|
|
74
|
-
* Returns true when every branch in the schema describes a primitive value.
|
|
75
|
-
*/
|
|
76
|
-
declare function isJsonPrimitiveSchema(schema: JsonSchema): boolean;
|
|
77
73
|
type JsonObjectSchemaEntry = [name: string, schema: JsonSchema, optional: boolean];
|
|
78
74
|
/**
|
|
79
75
|
* Combines object property entries back into a single object schema.
|
|
@@ -98,16 +94,16 @@ interface JsonSchemaConverter {
|
|
|
98
94
|
/**
|
|
99
95
|
* Determines whether this converter can handle the given schema.
|
|
100
96
|
*/
|
|
101
|
-
condition(schema: AnySchema | undefined, direction: JsonSchemaConverterDirection):
|
|
97
|
+
condition(schema: AnySchema | undefined, direction: JsonSchemaConverterDirection): boolean;
|
|
102
98
|
/**
|
|
103
99
|
* Converts an ORPC schema to a JSON Schema representation.
|
|
104
100
|
*/
|
|
105
|
-
convert(schema: AnySchema | undefined, direction: JsonSchemaConverterDirection):
|
|
101
|
+
convert(schema: AnySchema | undefined, direction: JsonSchemaConverterDirection): [jsonSchema: JsonSchema, optional: boolean];
|
|
106
102
|
}
|
|
107
103
|
declare class DelegatingJsonSchemaConverter implements Pick<JsonSchemaConverter, 'convert'> {
|
|
108
104
|
private readonly converters;
|
|
109
105
|
constructor(converters?: JsonSchemaConverter[]);
|
|
110
|
-
convert(schema: AnySchema | undefined, direction: JsonSchemaConverterDirection):
|
|
106
|
+
convert(schema: AnySchema | undefined, direction: JsonSchemaConverterDirection): [jsonSchema: JsonSchema, optional: boolean];
|
|
111
107
|
}
|
|
112
108
|
|
|
113
109
|
/**
|
|
@@ -127,6 +123,11 @@ declare function encodeJsonPointerSegment(segment: string): string;
|
|
|
127
123
|
* https://datatracker.ietf.org/doc/html/rfc6901
|
|
128
124
|
*/
|
|
129
125
|
declare function decodeJsonPointerSegment(segment: string): string;
|
|
126
|
+
/**
|
|
127
|
+
* Visits every `$ref` in a schema, traversing the same keywords as {@link mapJsonSchemaRefs}.
|
|
128
|
+
* Shared or cyclic object instances are visited once.
|
|
129
|
+
*/
|
|
130
|
+
declare function visitJsonSchemaRefs(value: JsonSchema, visit: (ref: string) => void, schemaLevel?: boolean, seen?: Set<object>): void;
|
|
130
131
|
declare function mapJsonSchemaRefs(value: JsonSchema, map: (ref: string, path: Array<string | number>) => string, schemaLevel?: boolean, path?: Array<string | number>): JsonSchema;
|
|
131
132
|
/**
|
|
132
133
|
* Rewrites recursive root `#` refs by moving the schema body into `$defs`.
|
|
@@ -186,5 +187,5 @@ declare class StandardJsonSchemaConverter implements JsonSchemaConverter {
|
|
|
186
187
|
convertInternal(schema: StandardJSONSchemaV1 & AnySchema, direction: JsonSchemaConverterDirection): [jsonSchema: JsonSchema, optional: boolean];
|
|
187
188
|
}
|
|
188
189
|
|
|
189
|
-
export { DelegatingJsonSchemaConverter, JsonSchemaCoercer, JsonSchemaXNativeType, SmartCoercionHandlerPlugin, SmartCoercionLinkPlugin, StandardJsonSchemaConverter, combineJsonObjectSchemaEntries, combineJsonSchemasWithComposition, decodeJsonPointerSegment, deduplicateJsonSchemas, encodeJsonPointerSegment, ensureJsonSchemaObject, extractJsonObjectSchemaEntries, flattenJsonUnionSchema, hoistRecursiveRefToDef, isJsonArraySchema, isJsonFileSchema, isJsonObjectSchema, isJsonPrimitiveSchema, isUnconstrainedSchema, mapJsonSchemaRefs, matchArrayableJsonSchema, resolveJsonSchemaRootLocalRef };
|
|
190
|
+
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 };
|
|
190
191
|
export type { JsonArraySchema, JsonFileSchema, JsonObjectSchema, JsonObjectSchemaEntry, JsonSchema, JsonSchemaConverter, JsonSchemaConverterDirection, JsonSchemaKeywords, SmartCoercionHandlerPluginOptions, SmartCoercionLinkPluginOptions };
|
package/dist/index.d.ts
CHANGED
|
@@ -2,14 +2,13 @@ import * as Draft2020 from 'json-schema-typed/draft-2020-12';
|
|
|
2
2
|
export { Format as JsonSchemaFormat, TypeName as JsonSchemaType } from 'json-schema-typed/draft-2020-12';
|
|
3
3
|
import { AnySchema, RouterContract } from '@orpc/contract';
|
|
4
4
|
export { AnySchema, Schema } from '@orpc/contract';
|
|
5
|
-
import { Promisable } from '@orpc/shared';
|
|
6
5
|
import { Context } from '@orpc/server';
|
|
7
6
|
import { StandardHandlerPlugin, StandardHandlerOptions } from '@orpc/server/standard';
|
|
8
7
|
import { ClientContext } from '@orpc/client';
|
|
9
8
|
import { StandardLinkPlugin, StandardLinkOptions } from '@orpc/client/standard';
|
|
10
9
|
import { StandardJSONSchemaV1 } from '@standard-schema/spec';
|
|
11
10
|
|
|
12
|
-
type JsonSchema = Draft2020.JSONSchema
|
|
11
|
+
type JsonSchema<Value = any> = Draft2020.JSONSchema<Value>;
|
|
13
12
|
type JsonSchemaKeywords = typeof Draft2020.keywords[number];
|
|
14
13
|
declare enum JsonSchemaXNativeType {
|
|
15
14
|
BigInt = "bigint",
|
|
@@ -30,6 +29,7 @@ declare class JsonSchemaCoercer {
|
|
|
30
29
|
* and exclusively use absolute JSON pointers for `$ref` values.
|
|
31
30
|
*/
|
|
32
31
|
|
|
32
|
+
declare function isJsonPrimitiveSchema(schema: JsonSchema): schema is JsonSchema & object;
|
|
33
33
|
type JsonFileSchema = JsonSchema & object & {
|
|
34
34
|
type: 'string';
|
|
35
35
|
contentMediaType?: string;
|
|
@@ -70,10 +70,6 @@ declare function ensureJsonSchemaObject(schema: JsonSchema): Exclude<JsonSchema,
|
|
|
70
70
|
* Combines multiple schemas under the requested composition keyword, promoting branch `$defs` to the root.
|
|
71
71
|
*/
|
|
72
72
|
declare function combineJsonSchemasWithComposition(keyword: 'allOf' | 'anyOf' | 'oneOf', schemas: JsonSchema[]): JsonSchema;
|
|
73
|
-
/**
|
|
74
|
-
* Returns true when every branch in the schema describes a primitive value.
|
|
75
|
-
*/
|
|
76
|
-
declare function isJsonPrimitiveSchema(schema: JsonSchema): boolean;
|
|
77
73
|
type JsonObjectSchemaEntry = [name: string, schema: JsonSchema, optional: boolean];
|
|
78
74
|
/**
|
|
79
75
|
* Combines object property entries back into a single object schema.
|
|
@@ -98,16 +94,16 @@ interface JsonSchemaConverter {
|
|
|
98
94
|
/**
|
|
99
95
|
* Determines whether this converter can handle the given schema.
|
|
100
96
|
*/
|
|
101
|
-
condition(schema: AnySchema | undefined, direction: JsonSchemaConverterDirection):
|
|
97
|
+
condition(schema: AnySchema | undefined, direction: JsonSchemaConverterDirection): boolean;
|
|
102
98
|
/**
|
|
103
99
|
* Converts an ORPC schema to a JSON Schema representation.
|
|
104
100
|
*/
|
|
105
|
-
convert(schema: AnySchema | undefined, direction: JsonSchemaConverterDirection):
|
|
101
|
+
convert(schema: AnySchema | undefined, direction: JsonSchemaConverterDirection): [jsonSchema: JsonSchema, optional: boolean];
|
|
106
102
|
}
|
|
107
103
|
declare class DelegatingJsonSchemaConverter implements Pick<JsonSchemaConverter, 'convert'> {
|
|
108
104
|
private readonly converters;
|
|
109
105
|
constructor(converters?: JsonSchemaConverter[]);
|
|
110
|
-
convert(schema: AnySchema | undefined, direction: JsonSchemaConverterDirection):
|
|
106
|
+
convert(schema: AnySchema | undefined, direction: JsonSchemaConverterDirection): [jsonSchema: JsonSchema, optional: boolean];
|
|
111
107
|
}
|
|
112
108
|
|
|
113
109
|
/**
|
|
@@ -127,6 +123,11 @@ declare function encodeJsonPointerSegment(segment: string): string;
|
|
|
127
123
|
* https://datatracker.ietf.org/doc/html/rfc6901
|
|
128
124
|
*/
|
|
129
125
|
declare function decodeJsonPointerSegment(segment: string): string;
|
|
126
|
+
/**
|
|
127
|
+
* Visits every `$ref` in a schema, traversing the same keywords as {@link mapJsonSchemaRefs}.
|
|
128
|
+
* Shared or cyclic object instances are visited once.
|
|
129
|
+
*/
|
|
130
|
+
declare function visitJsonSchemaRefs(value: JsonSchema, visit: (ref: string) => void, schemaLevel?: boolean, seen?: Set<object>): void;
|
|
130
131
|
declare function mapJsonSchemaRefs(value: JsonSchema, map: (ref: string, path: Array<string | number>) => string, schemaLevel?: boolean, path?: Array<string | number>): JsonSchema;
|
|
131
132
|
/**
|
|
132
133
|
* Rewrites recursive root `#` refs by moving the schema body into `$defs`.
|
|
@@ -186,5 +187,5 @@ declare class StandardJsonSchemaConverter implements JsonSchemaConverter {
|
|
|
186
187
|
convertInternal(schema: StandardJSONSchemaV1 & AnySchema, direction: JsonSchemaConverterDirection): [jsonSchema: JsonSchema, optional: boolean];
|
|
187
188
|
}
|
|
188
189
|
|
|
189
|
-
export { DelegatingJsonSchemaConverter, JsonSchemaCoercer, JsonSchemaXNativeType, SmartCoercionHandlerPlugin, SmartCoercionLinkPlugin, StandardJsonSchemaConverter, combineJsonObjectSchemaEntries, combineJsonSchemasWithComposition, decodeJsonPointerSegment, deduplicateJsonSchemas, encodeJsonPointerSegment, ensureJsonSchemaObject, extractJsonObjectSchemaEntries, flattenJsonUnionSchema, hoistRecursiveRefToDef, isJsonArraySchema, isJsonFileSchema, isJsonObjectSchema, isJsonPrimitiveSchema, isUnconstrainedSchema, mapJsonSchemaRefs, matchArrayableJsonSchema, resolveJsonSchemaRootLocalRef };
|
|
190
|
+
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 };
|
|
190
191
|
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 } 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
|
-
bestOptions = { coerced: subCoerced, satisfied: subSatisfied };
|
|
391
|
-
}
|
|
392
|
-
if (subCoerced === coerced) {
|
|
393
|
-
break;
|
|
394
|
-
}
|
|
414
|
+
const [subSatisfied, subCoerced] = this.coerceInternal(rootSchema, subSchema, coerced, appliedRefs);
|
|
415
|
+
if (subSatisfied === UNSATISFIED) {
|
|
416
|
+
continue;
|
|
395
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 };
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
if (best) {
|
|
427
|
+
coerced = best.value;
|
|
428
|
+
satisfied = minSatisfaction(satisfied, best.satisfied);
|
|
429
|
+
} else {
|
|
430
|
+
satisfied = UNSATISFIED;
|
|
396
431
|
}
|
|
397
|
-
coerced = bestOptions ? bestOptions.coerced : coerced;
|
|
398
|
-
satisfied = bestOptions ? bestOptions.satisfied : false;
|
|
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;
|
|
@@ -473,6 +525,21 @@ function arrayToMap(value) {
|
|
|
473
525
|
return result;
|
|
474
526
|
}
|
|
475
527
|
|
|
528
|
+
function isJsonPrimitiveSchema(schema) {
|
|
529
|
+
if (typeof schema === "boolean") {
|
|
530
|
+
return false;
|
|
531
|
+
}
|
|
532
|
+
if (typeof schema.type === "string" && JSON_SCHEMA_PRIMITIVE_TYPES.has(schema.type)) {
|
|
533
|
+
return true;
|
|
534
|
+
}
|
|
535
|
+
if (schema.const !== void 0) {
|
|
536
|
+
return true;
|
|
537
|
+
}
|
|
538
|
+
if (schema.enum !== void 0) {
|
|
539
|
+
return true;
|
|
540
|
+
}
|
|
541
|
+
return false;
|
|
542
|
+
}
|
|
476
543
|
function isJsonFileSchema(schema) {
|
|
477
544
|
return typeof schema !== "boolean" && schema.type === "string" && (typeof schema.contentMediaType === "string" || schema.format === "binary" || schema.contentEncoding === "binary");
|
|
478
545
|
}
|
|
@@ -568,23 +635,6 @@ function combineJsonSchemasWithComposition(keyword, schemas) {
|
|
|
568
635
|
}
|
|
569
636
|
return result;
|
|
570
637
|
}
|
|
571
|
-
function isJsonPrimitiveSchema(schema) {
|
|
572
|
-
return flattenJsonUnionSchema(schema).every((s) => {
|
|
573
|
-
if (typeof s === "boolean") {
|
|
574
|
-
return false;
|
|
575
|
-
}
|
|
576
|
-
if (typeof s.type === "string" && JSON_SCHEMA_PRIMITIVE_TYPES.has(s.type)) {
|
|
577
|
-
return true;
|
|
578
|
-
}
|
|
579
|
-
if (s.const !== void 0) {
|
|
580
|
-
return true;
|
|
581
|
-
}
|
|
582
|
-
if (s.enum !== void 0) {
|
|
583
|
-
return true;
|
|
584
|
-
}
|
|
585
|
-
return false;
|
|
586
|
-
});
|
|
587
|
-
}
|
|
588
638
|
function combineJsonObjectSchemaEntries(entries) {
|
|
589
639
|
const properties = {};
|
|
590
640
|
const required = [];
|
|
@@ -846,13 +896,14 @@ class DelegatingJsonSchemaConverter {
|
|
|
846
896
|
constructor(converters = []) {
|
|
847
897
|
this.converters = converters;
|
|
848
898
|
}
|
|
849
|
-
|
|
899
|
+
convert(schema, direction) {
|
|
850
900
|
for (const converter of this.converters) {
|
|
851
|
-
if (
|
|
901
|
+
if (converter.condition(schema, direction)) {
|
|
852
902
|
return converter.convert(schema, direction);
|
|
853
903
|
}
|
|
854
904
|
}
|
|
855
|
-
const
|
|
905
|
+
const result = schema?.["~standard"].validate(void 0);
|
|
906
|
+
const optional = result instanceof Promise ? false : !result?.issues?.length;
|
|
856
907
|
if (schema && "jsonSchema" in schema["~standard"] && schema["~standard"].jsonSchema) {
|
|
857
908
|
try {
|
|
858
909
|
return [
|
|
@@ -909,23 +960,23 @@ class SmartCoercionHandlerPlugin {
|
|
|
909
960
|
return {
|
|
910
961
|
...options,
|
|
911
962
|
clientInterceptors: [
|
|
912
|
-
|
|
963
|
+
({ next, input, ...interceptorOptions }) => {
|
|
913
964
|
const inputSchemas = interceptorOptions.procedure["~orpc"].inputSchemas;
|
|
914
965
|
if (!inputSchemas) {
|
|
915
966
|
return next();
|
|
916
967
|
}
|
|
917
|
-
const coercedInput =
|
|
968
|
+
const coercedInput = this.coerceValue(inputSchemas, input);
|
|
918
969
|
return next({ ...interceptorOptions, input: coercedInput });
|
|
919
970
|
},
|
|
920
971
|
...toArray(options.clientInterceptors)
|
|
921
972
|
]
|
|
922
973
|
};
|
|
923
974
|
}
|
|
924
|
-
|
|
975
|
+
coerceValue(schemas, value) {
|
|
925
976
|
for (const schema of schemas) {
|
|
926
977
|
let converted = this.cache.get(schema);
|
|
927
978
|
if (!converted) {
|
|
928
|
-
converted =
|
|
979
|
+
converted = this.converter.convert(schema, "input");
|
|
929
980
|
this.cache.set(schema, converted);
|
|
930
981
|
}
|
|
931
982
|
value = this.coercer.coerce(converted, value);
|
|
@@ -964,7 +1015,7 @@ class SmartCoercionLinkPlugin {
|
|
|
964
1015
|
if (!outputSchemas) {
|
|
965
1016
|
return output;
|
|
966
1017
|
}
|
|
967
|
-
const coercedOutput =
|
|
1018
|
+
const coercedOutput = this.coerceValue(outputSchemas, output);
|
|
968
1019
|
return coercedOutput;
|
|
969
1020
|
} catch (error) {
|
|
970
1021
|
if (!(error instanceof ORPCError) || !error.defined) {
|
|
@@ -976,18 +1027,18 @@ class SmartCoercionLinkPlugin {
|
|
|
976
1027
|
throw error;
|
|
977
1028
|
}
|
|
978
1029
|
const cloned = cloneORPCError(error);
|
|
979
|
-
cloned.data =
|
|
1030
|
+
cloned.data = this.coerceValue([dataSchema], cloned.data);
|
|
980
1031
|
throw cloned;
|
|
981
1032
|
}
|
|
982
1033
|
}
|
|
983
1034
|
]
|
|
984
1035
|
};
|
|
985
1036
|
}
|
|
986
|
-
|
|
1037
|
+
coerceValue(schemas, value) {
|
|
987
1038
|
for (const schema of schemas) {
|
|
988
1039
|
let converted = this.cache.get(schema);
|
|
989
1040
|
if (!converted) {
|
|
990
|
-
converted =
|
|
1041
|
+
converted = this.converter.convert(schema, "output");
|
|
991
1042
|
this.cache.set(schema, converted);
|
|
992
1043
|
}
|
|
993
1044
|
value = this.coercer.coerce(converted, value);
|
|
@@ -996,4 +1047,4 @@ class SmartCoercionLinkPlugin {
|
|
|
996
1047
|
}
|
|
997
1048
|
}
|
|
998
1049
|
|
|
999
|
-
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,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@orpc/json-schema",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "2.0.0-beta.
|
|
4
|
+
"version": "2.0.0-beta.21",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"homepage": "https://orpc.dev",
|
|
7
7
|
"repository": {
|
|
@@ -27,10 +27,10 @@
|
|
|
27
27
|
"dependencies": {
|
|
28
28
|
"@standard-schema/spec": "^1.1.0",
|
|
29
29
|
"json-schema-typed": "^8.0.2",
|
|
30
|
-
"@orpc/
|
|
31
|
-
"@orpc/contract": "2.0.0-beta.
|
|
32
|
-
"@orpc/
|
|
33
|
-
"@orpc/shared": "2.0.0-beta.
|
|
30
|
+
"@orpc/client": "2.0.0-beta.21",
|
|
31
|
+
"@orpc/contract": "2.0.0-beta.21",
|
|
32
|
+
"@orpc/server": "2.0.0-beta.21",
|
|
33
|
+
"@orpc/shared": "2.0.0-beta.21"
|
|
34
34
|
},
|
|
35
35
|
"devDependencies": {
|
|
36
36
|
"zod": "^4.4.3"
|