@orpc/json-schema 2.0.0-beta.40 → 2.0.0-beta.41
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 +5 -5
- package/dist/index.d.mts +10 -2
- package/dist/index.d.ts +10 -2
- package/dist/index.mjs +104 -107
- package/package.json +5 -5
package/README.md
CHANGED
|
@@ -85,7 +85,7 @@ Like what we build over at [middleapi](https://github.com/middleapi)? You can he
|
|
|
85
85
|
|
|
86
86
|
<table>
|
|
87
87
|
<tr>
|
|
88
|
-
<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>
|
|
88
|
+
<td width="2000"><a href="https://screenshotone.com/?ref=middleapi&utm_source=middleapi&utm_medium=sponsor" target="_blank" rel="noopener sponsored" 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>
|
|
89
89
|
</tr>
|
|
90
90
|
<tr>
|
|
91
91
|
<td width="2000"><a href="https://yuzu.health/careers?ref=middleapi&utm_source=middleapi&utm_medium=sponsor" target="_blank" rel="noopener sponsored" title="We're hiring NYC based engineers"><img src="https://avatars.githubusercontent.com/u/102488956?v=4" width="64" align="left" hspace="12" alt="Yuzu"/><b>Yuzu</b></a><br /><sub>We're hiring NYC based engineers</sub></td>
|
|
@@ -127,19 +127,18 @@ Like what we build over at [middleapi](https://github.com/middleapi)? You can he
|
|
|
127
127
|
<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>
|
|
128
128
|
<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>
|
|
129
129
|
<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>
|
|
130
|
-
<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>
|
|
131
130
|
<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>
|
|
131
|
+
<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>
|
|
132
132
|
</tr>
|
|
133
133
|
<tr>
|
|
134
|
-
<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>
|
|
135
134
|
<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>
|
|
136
135
|
<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>
|
|
137
136
|
<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>
|
|
138
137
|
<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>
|
|
139
138
|
<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>
|
|
139
|
+
<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>
|
|
140
140
|
</tr>
|
|
141
141
|
<tr>
|
|
142
|
-
<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>
|
|
143
142
|
<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>
|
|
144
143
|
<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>
|
|
145
144
|
<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>
|
|
@@ -173,12 +172,13 @@ Like what we build over at [middleapi](https://github.com/middleapi)? You can he
|
|
|
173
172
|
<td align="center"><a href="https://kellychan.im/?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>
|
|
174
173
|
<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>
|
|
175
174
|
<td align="center"><a href="https://paulsenon.com/?ref=middleapi&utm_source=middleapi&utm_medium=sponsor" target="_blank" rel="noopener sponsored" title="PaulSenon"><img src="https://avatars.githubusercontent.com/u/19531087?u=6385741eb91d200b8c513ec6045482e301767ca6&v=4" width="119" alt="PaulSenon"/><br />PaulSenon</a></td>
|
|
175
|
+
<td align="center"><a href="https://nakasyou.how/?ref=middleapi&utm_source=middleapi&utm_medium=sponsor" target="_blank" rel="noopener sponsored" title="Shotaro Nakamura"><img src="https://avatars.githubusercontent.com/u/79000684?u=f644df3f29f0e8677a90967115774564f1d9d6ab&v=4" width="119" alt="Shotaro Nakamura"/><br />Shotaro Nakamura</a></td>
|
|
176
176
|
<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>
|
|
177
177
|
<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>
|
|
178
178
|
</tr>
|
|
179
179
|
</table>
|
|
180
180
|
|
|
181
|
-
With thanks to [
|
|
181
|
+
With thanks to [38 past sponsors](https://orpc.dev/sponsors#past-sponsors) who helped get oRPC here.
|
|
182
182
|
|
|
183
183
|
## References
|
|
184
184
|
|
package/dist/index.d.mts
CHANGED
|
@@ -193,7 +193,8 @@ declare function hoistRecursiveRefToDef(schema: JsonSchema): JsonSchema;
|
|
|
193
193
|
* intentionally left untouched.
|
|
194
194
|
*
|
|
195
195
|
* If the ref cannot be resolved (missing `$defs`, unknown key, etc.) the
|
|
196
|
-
* schema is returned as-is.
|
|
196
|
+
* schema is returned as-is. Chained refs are followed until one repeats,
|
|
197
|
+
* which is kept.
|
|
197
198
|
*
|
|
198
199
|
* @param schema - The schema whose root-level `$ref` should be resolved.
|
|
199
200
|
* @param $defs - Definition map to resolve against. If omitted, falls back to
|
|
@@ -245,11 +246,18 @@ declare class SmartCoercionLinkPlugin<T extends ClientContext> implements Standa
|
|
|
245
246
|
private coerceValue;
|
|
246
247
|
}
|
|
247
248
|
|
|
249
|
+
/**
|
|
250
|
+
* Returns true when the schema accepts `undefined` in the given direction,
|
|
251
|
+
* found by validating `undefined` against it. For `output`, the validated value must also be `undefined`.
|
|
252
|
+
*
|
|
253
|
+
* Returns false when validation throws or is async. An async result is never left as an unhandled rejection.
|
|
254
|
+
*/
|
|
255
|
+
declare function isStandardSchemaOptional(schema: AnySchema, direction: JsonSchemaConverterDirection): boolean;
|
|
248
256
|
declare class StandardJsonSchemaConverter implements JsonSchemaConverter {
|
|
249
257
|
condition(schema: AnySchema | undefined, _direction: JsonSchemaConverterDirection): boolean;
|
|
250
258
|
convert(schema: AnySchema | undefined, direction: JsonSchemaConverterDirection): [jsonSchema: JsonSchema, optional: boolean];
|
|
251
259
|
convertInternal(schema: StandardJSONSchemaV1 & AnySchema, direction: JsonSchemaConverterDirection): [jsonSchema: JsonSchema, optional: boolean];
|
|
252
260
|
}
|
|
253
261
|
|
|
254
|
-
export { DelegatingJsonSchemaConverter, JsonSchemaCoercer, JsonSchemaFormat, JsonSchemaType, JsonSchemaXNativeType, SmartCoercionHandlerPlugin, SmartCoercionLinkPlugin, StandardJsonSchemaConverter, combineJsonObjectSchemaEntries, combineJsonSchemasWithComposition, decodeJsonPointerSegment, deduplicateJsonSchemas, encodeJsonPointerSegment, ensureJsonSchemaObject, extractJsonObjectSchemaEntries, flattenJsonUnionSchema, hoistRecursiveRefToDef, isJsonArraySchema, isJsonFileSchema, isJsonObjectSchema, isJsonPrimitiveSchema, isUnconstrainedSchema, mapJsonSchemaRefs, matchArrayableJsonSchema, resolveJsonSchemaRootLocalRef, visitJsonSchemaRefs };
|
|
262
|
+
export { DelegatingJsonSchemaConverter, JsonSchemaCoercer, JsonSchemaFormat, JsonSchemaType, JsonSchemaXNativeType, SmartCoercionHandlerPlugin, SmartCoercionLinkPlugin, StandardJsonSchemaConverter, combineJsonObjectSchemaEntries, combineJsonSchemasWithComposition, decodeJsonPointerSegment, deduplicateJsonSchemas, encodeJsonPointerSegment, ensureJsonSchemaObject, extractJsonObjectSchemaEntries, flattenJsonUnionSchema, hoistRecursiveRefToDef, isJsonArraySchema, isJsonFileSchema, isJsonObjectSchema, isJsonPrimitiveSchema, isStandardSchemaOptional, isUnconstrainedSchema, mapJsonSchemaRefs, matchArrayableJsonSchema, resolveJsonSchemaRootLocalRef, visitJsonSchemaRefs };
|
|
255
263
|
export type { JsonArraySchema, JsonFileSchema, JsonObjectSchema, JsonObjectSchemaEntry, JsonSchema, JsonSchemaConverter, JsonSchemaConverterDirection, JsonSchemaKeywords, SmartCoercionHandlerPluginOptions, SmartCoercionLinkPluginOptions };
|
package/dist/index.d.ts
CHANGED
|
@@ -193,7 +193,8 @@ declare function hoistRecursiveRefToDef(schema: JsonSchema): JsonSchema;
|
|
|
193
193
|
* intentionally left untouched.
|
|
194
194
|
*
|
|
195
195
|
* If the ref cannot be resolved (missing `$defs`, unknown key, etc.) the
|
|
196
|
-
* schema is returned as-is.
|
|
196
|
+
* schema is returned as-is. Chained refs are followed until one repeats,
|
|
197
|
+
* which is kept.
|
|
197
198
|
*
|
|
198
199
|
* @param schema - The schema whose root-level `$ref` should be resolved.
|
|
199
200
|
* @param $defs - Definition map to resolve against. If omitted, falls back to
|
|
@@ -245,11 +246,18 @@ declare class SmartCoercionLinkPlugin<T extends ClientContext> implements Standa
|
|
|
245
246
|
private coerceValue;
|
|
246
247
|
}
|
|
247
248
|
|
|
249
|
+
/**
|
|
250
|
+
* Returns true when the schema accepts `undefined` in the given direction,
|
|
251
|
+
* found by validating `undefined` against it. For `output`, the validated value must also be `undefined`.
|
|
252
|
+
*
|
|
253
|
+
* Returns false when validation throws or is async. An async result is never left as an unhandled rejection.
|
|
254
|
+
*/
|
|
255
|
+
declare function isStandardSchemaOptional(schema: AnySchema, direction: JsonSchemaConverterDirection): boolean;
|
|
248
256
|
declare class StandardJsonSchemaConverter implements JsonSchemaConverter {
|
|
249
257
|
condition(schema: AnySchema | undefined, _direction: JsonSchemaConverterDirection): boolean;
|
|
250
258
|
convert(schema: AnySchema | undefined, direction: JsonSchemaConverterDirection): [jsonSchema: JsonSchema, optional: boolean];
|
|
251
259
|
convertInternal(schema: StandardJSONSchemaV1 & AnySchema, direction: JsonSchemaConverterDirection): [jsonSchema: JsonSchema, optional: boolean];
|
|
252
260
|
}
|
|
253
261
|
|
|
254
|
-
export { DelegatingJsonSchemaConverter, JsonSchemaCoercer, JsonSchemaFormat, JsonSchemaType, JsonSchemaXNativeType, SmartCoercionHandlerPlugin, SmartCoercionLinkPlugin, StandardJsonSchemaConverter, combineJsonObjectSchemaEntries, combineJsonSchemasWithComposition, decodeJsonPointerSegment, deduplicateJsonSchemas, encodeJsonPointerSegment, ensureJsonSchemaObject, extractJsonObjectSchemaEntries, flattenJsonUnionSchema, hoistRecursiveRefToDef, isJsonArraySchema, isJsonFileSchema, isJsonObjectSchema, isJsonPrimitiveSchema, isUnconstrainedSchema, mapJsonSchemaRefs, matchArrayableJsonSchema, resolveJsonSchemaRootLocalRef, visitJsonSchemaRefs };
|
|
262
|
+
export { DelegatingJsonSchemaConverter, JsonSchemaCoercer, JsonSchemaFormat, JsonSchemaType, JsonSchemaXNativeType, SmartCoercionHandlerPlugin, SmartCoercionLinkPlugin, StandardJsonSchemaConverter, combineJsonObjectSchemaEntries, combineJsonSchemasWithComposition, decodeJsonPointerSegment, deduplicateJsonSchemas, encodeJsonPointerSegment, ensureJsonSchemaObject, extractJsonObjectSchemaEntries, flattenJsonUnionSchema, hoistRecursiveRefToDef, isJsonArraySchema, isJsonFileSchema, isJsonObjectSchema, isJsonPrimitiveSchema, isStandardSchemaOptional, isUnconstrainedSchema, mapJsonSchemaRefs, matchArrayableJsonSchema, resolveJsonSchemaRootLocalRef, visitJsonSchemaRefs };
|
|
255
263
|
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, tryOrUndefined, getOwn, toArray, isDeepEqual, omit, isTypescriptObject } from '@orpc/shared';
|
|
1
|
+
import { setOwn, get, isPlainObject, tryOrUndefined, getOwn, toArray, isDeepEqual, omit, isTypescriptObject } from '@orpc/shared';
|
|
2
2
|
import { ORPCError, cloneORPCError } from '@orpc/client';
|
|
3
3
|
import { getProcedureContractOrThrow } from '@orpc/contract';
|
|
4
4
|
|
|
@@ -91,13 +91,13 @@ function mapJsonSchemaRefs(value, map, schemaLevel = true, path = []) {
|
|
|
91
91
|
for (const key of Object.keys(value)) {
|
|
92
92
|
const val = value[key];
|
|
93
93
|
if (key === "$ref" && typeof val === "string") {
|
|
94
|
-
result
|
|
94
|
+
setOwn(result, key, map(val, [...path, key]));
|
|
95
95
|
} else if (!schemaLevel) {
|
|
96
|
-
result
|
|
96
|
+
setOwn(result, key, mapJsonSchemaRefs(val, map, true, [...path, key]));
|
|
97
97
|
} else if (JSON_SCHEMA_LOGIC_KEYWORDS.has(key) || JSON_SCHEMA_RECORD_KEYWORDS.has(key)) {
|
|
98
|
-
result
|
|
98
|
+
setOwn(result, key, mapJsonSchemaRefs(val, map, !JSON_SCHEMA_RECORD_KEYWORDS.has(key), [...path, key]));
|
|
99
99
|
} else {
|
|
100
|
-
result
|
|
100
|
+
setOwn(result, key, val);
|
|
101
101
|
}
|
|
102
102
|
}
|
|
103
103
|
return result;
|
|
@@ -106,20 +106,19 @@ function hoistRecursiveRefToDef(schema) {
|
|
|
106
106
|
if (typeof schema !== "object") {
|
|
107
107
|
return schema;
|
|
108
108
|
}
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
return `#/$defs/${encodeJsonPointerSegment(defName)}${ref.slice(1)}`;
|
|
114
|
-
}
|
|
115
|
-
return ref;
|
|
109
|
+
const isRootRef = (ref) => ref === "#" || ref.startsWith("#/") && !ref.startsWith("#/$defs/") && get(schema, ref.slice(2).split("/").map(decodeJsonPointerSegment)) !== void 0;
|
|
110
|
+
let hasRootRef = false;
|
|
111
|
+
visitJsonSchemaRefs(schema, (ref) => {
|
|
112
|
+
hasRootRef ||= isRootRef(ref);
|
|
116
113
|
});
|
|
117
|
-
if (
|
|
114
|
+
if (!hasRootRef) {
|
|
118
115
|
return schema;
|
|
119
116
|
}
|
|
120
|
-
const
|
|
117
|
+
const defName = findRecursiveJsonSchemaDefName(schema.$defs);
|
|
118
|
+
const defRef = `#/$defs/${encodeJsonPointerSegment(defName)}`;
|
|
119
|
+
const { $defs, ...rest } = mapJsonSchemaRefs(schema, (ref) => isRootRef(ref) ? `${defRef}${ref.slice(1)}` : ref);
|
|
121
120
|
return {
|
|
122
|
-
$ref:
|
|
121
|
+
$ref: defRef,
|
|
123
122
|
$defs: {
|
|
124
123
|
...$defs,
|
|
125
124
|
[defName]: rest
|
|
@@ -136,21 +135,24 @@ function resolveJsonSchemaRootLocalRef(schema, $defs) {
|
|
|
136
135
|
if (!$defs) {
|
|
137
136
|
return schema;
|
|
138
137
|
}
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
138
|
+
const followedRefs = /* @__PURE__ */ new Set();
|
|
139
|
+
let current = schema;
|
|
140
|
+
while (typeof current.$ref === "string" && current.$ref.startsWith("#/$defs/") && !followedRefs.has(current.$ref)) {
|
|
141
|
+
followedRefs.add(current.$ref);
|
|
142
|
+
const resolved = get($defs, current.$ref.slice("#/$defs/".length).split("/").map(decodeJsonPointerSegment));
|
|
143
|
+
if (resolved === void 0) {
|
|
144
|
+
return current;
|
|
145
|
+
}
|
|
146
|
+
if (typeof resolved !== "object") {
|
|
147
|
+
return resolved;
|
|
148
|
+
}
|
|
149
|
+
const { $ref: _ref, ...rest } = current;
|
|
150
|
+
current = {
|
|
151
|
+
...rest,
|
|
152
|
+
...resolved
|
|
153
|
+
};
|
|
148
154
|
}
|
|
149
|
-
|
|
150
|
-
return resolveJsonSchemaRootLocalRef({
|
|
151
|
-
...rest,
|
|
152
|
-
...resolved
|
|
153
|
-
});
|
|
155
|
+
return current;
|
|
154
156
|
}
|
|
155
157
|
function findRecursiveJsonSchemaDefName(defs) {
|
|
156
158
|
let index = 0;
|
|
@@ -204,6 +206,35 @@ var JsonSchemaXNativeType = /* @__PURE__ */ ((JsonSchemaXNativeType2) => {
|
|
|
204
206
|
const UNSATISFIED = 0;
|
|
205
207
|
const LOOSELY_SATISFIED = 1;
|
|
206
208
|
const SATISFIED = 2;
|
|
209
|
+
const NATIVE_TYPES = /* @__PURE__ */ new Map([
|
|
210
|
+
[JsonSchemaXNativeType.Date, {
|
|
211
|
+
types: ["string"],
|
|
212
|
+
is: (value) => value instanceof Date,
|
|
213
|
+
coerce: (value) => typeof value === "string" ? stringToDate(value) : value
|
|
214
|
+
}],
|
|
215
|
+
[JsonSchemaXNativeType.BigInt, {
|
|
216
|
+
types: ["string", "integer", "number"],
|
|
217
|
+
is: (value) => typeof value === "bigint",
|
|
218
|
+
coerce: (value) => typeof value === "string" ? stringToBigInt(value) : typeof value === "number" ? numberToBigInt(value) : value
|
|
219
|
+
}],
|
|
220
|
+
[JsonSchemaXNativeType.Url, {
|
|
221
|
+
types: ["string"],
|
|
222
|
+
is: (value) => value instanceof URL,
|
|
223
|
+
coerce: (value) => typeof value === "string" ? stringToURL(value) : value
|
|
224
|
+
}],
|
|
225
|
+
[JsonSchemaXNativeType.Set, {
|
|
226
|
+
types: ["array"],
|
|
227
|
+
is: (value) => value instanceof Set,
|
|
228
|
+
coerce: (value) => Array.isArray(value) ? arrayToSet(value) : value,
|
|
229
|
+
afterTypeCheck: true
|
|
230
|
+
}],
|
|
231
|
+
[JsonSchemaXNativeType.Map, {
|
|
232
|
+
types: ["array"],
|
|
233
|
+
is: (value) => value instanceof Map,
|
|
234
|
+
coerce: (value) => Array.isArray(value) ? arrayToMap(value) : value,
|
|
235
|
+
afterTypeCheck: true
|
|
236
|
+
}]
|
|
237
|
+
]);
|
|
207
238
|
function minSatisfaction(a, b) {
|
|
208
239
|
return a < b ? a : b;
|
|
209
240
|
}
|
|
@@ -258,7 +289,12 @@ class JsonSchemaCoercer {
|
|
|
258
289
|
satisfied = UNSATISFIED;
|
|
259
290
|
}
|
|
260
291
|
}
|
|
261
|
-
|
|
292
|
+
const declaredNativeType = NATIVE_TYPES.get(schema["x-native-type"]);
|
|
293
|
+
const nativeType = schema.type === void 0 || declaredNativeType?.types.includes(schema.type) ? declaredNativeType : void 0;
|
|
294
|
+
if (nativeType && !nativeType.afterTypeCheck) {
|
|
295
|
+
coerced = nativeType.coerce(coerced);
|
|
296
|
+
}
|
|
297
|
+
if (schema.type && !nativeType?.is(coerced)) {
|
|
262
298
|
switch (schema.type) {
|
|
263
299
|
case "null": {
|
|
264
300
|
if (coerced !== null) {
|
|
@@ -317,7 +353,7 @@ class JsonSchemaCoercer {
|
|
|
317
353
|
}
|
|
318
354
|
return subCoerced;
|
|
319
355
|
});
|
|
320
|
-
if (coercedItems.length <
|
|
356
|
+
if (typeof schema.minItems === "number" && coercedItems.length < schema.minItems) {
|
|
321
357
|
satisfied = UNSATISFIED;
|
|
322
358
|
}
|
|
323
359
|
if (shouldUseCoercedItems) {
|
|
@@ -369,58 +405,12 @@ class JsonSchemaCoercer {
|
|
|
369
405
|
}
|
|
370
406
|
}
|
|
371
407
|
}
|
|
372
|
-
if (
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
if (!(coerced instanceof Date)) {
|
|
379
|
-
satisfied = UNSATISFIED;
|
|
380
|
-
}
|
|
381
|
-
break;
|
|
382
|
-
}
|
|
383
|
-
case JsonSchemaXNativeType.BigInt: {
|
|
384
|
-
switch (typeof coerced) {
|
|
385
|
-
case "string":
|
|
386
|
-
coerced = stringToBigInt(coerced);
|
|
387
|
-
break;
|
|
388
|
-
case "number":
|
|
389
|
-
coerced = numberToBigInt(coerced);
|
|
390
|
-
break;
|
|
391
|
-
}
|
|
392
|
-
if (typeof coerced !== "bigint") {
|
|
393
|
-
satisfied = UNSATISFIED;
|
|
394
|
-
}
|
|
395
|
-
break;
|
|
396
|
-
}
|
|
397
|
-
case JsonSchemaXNativeType.Url: {
|
|
398
|
-
if (typeof coerced === "string") {
|
|
399
|
-
coerced = stringToURL(coerced);
|
|
400
|
-
}
|
|
401
|
-
if (!(coerced instanceof URL)) {
|
|
402
|
-
satisfied = UNSATISFIED;
|
|
403
|
-
}
|
|
404
|
-
break;
|
|
405
|
-
}
|
|
406
|
-
case JsonSchemaXNativeType.Set: {
|
|
407
|
-
if (Array.isArray(coerced)) {
|
|
408
|
-
coerced = arrayToSet(coerced);
|
|
409
|
-
}
|
|
410
|
-
if (!(coerced instanceof Set)) {
|
|
411
|
-
satisfied = UNSATISFIED;
|
|
412
|
-
}
|
|
413
|
-
break;
|
|
414
|
-
}
|
|
415
|
-
case JsonSchemaXNativeType.Map: {
|
|
416
|
-
if (Array.isArray(coerced)) {
|
|
417
|
-
coerced = arrayToMap(coerced);
|
|
418
|
-
}
|
|
419
|
-
if (!(coerced instanceof Map)) {
|
|
420
|
-
satisfied = UNSATISFIED;
|
|
421
|
-
}
|
|
422
|
-
break;
|
|
423
|
-
}
|
|
408
|
+
if (nativeType) {
|
|
409
|
+
if (nativeType.afterTypeCheck) {
|
|
410
|
+
coerced = nativeType.coerce(coerced);
|
|
411
|
+
}
|
|
412
|
+
if (!nativeType.is(coerced)) {
|
|
413
|
+
satisfied = UNSATISFIED;
|
|
424
414
|
}
|
|
425
415
|
}
|
|
426
416
|
if (schema.allOf) {
|
|
@@ -507,17 +497,30 @@ function stringToBoolean(value) {
|
|
|
507
497
|
}
|
|
508
498
|
return value;
|
|
509
499
|
}
|
|
510
|
-
const DATE_TIME_PATTERN = /^[+-]?\d{4
|
|
500
|
+
const DATE_TIME_PATTERN = /^(?!-0{6})(?:[+-]\d{2})?\d{4}-\d{2}-\d{2}(?:[T ].*)?$/;
|
|
501
|
+
const DAYS_IN_MONTH = [31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
|
|
511
502
|
function stringToDate(value) {
|
|
512
503
|
if (!DATE_TIME_PATTERN.test(value)) {
|
|
513
504
|
return value;
|
|
514
505
|
}
|
|
506
|
+
const yearEnd = value.indexOf("-", 1);
|
|
507
|
+
const month = readTwoDigits(value, yearEnd + 1);
|
|
508
|
+
const day = readTwoDigits(value, yearEnd + 4);
|
|
509
|
+
if (day < 1 || day > (DAYS_IN_MONTH[month - 1] ?? 0) || month === 2 && day === 29 && !isLeapYear(Number(value.slice(0, yearEnd)))) {
|
|
510
|
+
return value;
|
|
511
|
+
}
|
|
515
512
|
const date = new Date(value);
|
|
516
513
|
if (Number.isNaN(date.getTime())) {
|
|
517
514
|
return value;
|
|
518
515
|
}
|
|
519
516
|
return date;
|
|
520
517
|
}
|
|
518
|
+
function readTwoDigits(value, index) {
|
|
519
|
+
return (value.charCodeAt(index) - 48) * 10 + value.charCodeAt(index + 1) - 48;
|
|
520
|
+
}
|
|
521
|
+
function isLeapYear(year) {
|
|
522
|
+
return year % 4 === 0 && (year % 100 !== 0 || year % 400 === 0);
|
|
523
|
+
}
|
|
521
524
|
function stringToURL(value) {
|
|
522
525
|
return tryOrUndefined(() => new URL(value)) ?? value;
|
|
523
526
|
}
|
|
@@ -758,7 +761,7 @@ function extractJsonObjectSchemaEntriesInternal(schema, $defs, resolvingRefs) {
|
|
|
758
761
|
return [
|
|
759
762
|
name,
|
|
760
763
|
schemas.length === 1 ? schemas[0] : { allOf: schemas },
|
|
761
|
-
|
|
764
|
+
!schema.required?.includes(name) && [entries.allOf, entries.anyOf, entries.oneOf].every((entry) => entry === void 0 || entry[2])
|
|
762
765
|
];
|
|
763
766
|
}),
|
|
764
767
|
objectLike
|
|
@@ -853,21 +856,23 @@ class DelegatingJsonSchemaConverter {
|
|
|
853
856
|
return converter.convert(schema, direction);
|
|
854
857
|
}
|
|
855
858
|
}
|
|
856
|
-
|
|
857
|
-
const optional = result instanceof Promise ? false : !result?.issues?.length;
|
|
858
|
-
if (schema && "jsonSchema" in schema["~standard"] && schema["~standard"].jsonSchema) {
|
|
859
|
-
try {
|
|
860
|
-
return [
|
|
861
|
-
schema["~standard"].jsonSchema[direction](),
|
|
862
|
-
optional
|
|
863
|
-
];
|
|
864
|
-
} catch {
|
|
865
|
-
}
|
|
866
|
-
}
|
|
867
|
-
return [{}, optional];
|
|
859
|
+
return [{}, true];
|
|
868
860
|
}
|
|
869
861
|
}
|
|
870
862
|
|
|
863
|
+
function isStandardSchemaOptional(schema, direction) {
|
|
864
|
+
try {
|
|
865
|
+
const result = schema["~standard"].validate(void 0);
|
|
866
|
+
if (result instanceof Promise) {
|
|
867
|
+
result.catch(() => {
|
|
868
|
+
});
|
|
869
|
+
return false;
|
|
870
|
+
}
|
|
871
|
+
return !result.issues && (direction === "input" || result.value === void 0);
|
|
872
|
+
} catch {
|
|
873
|
+
return false;
|
|
874
|
+
}
|
|
875
|
+
}
|
|
871
876
|
class StandardJsonSchemaConverter {
|
|
872
877
|
condition(schema, _direction) {
|
|
873
878
|
return Boolean(
|
|
@@ -880,15 +885,7 @@ class StandardJsonSchemaConverter {
|
|
|
880
885
|
convertInternal(schema, direction) {
|
|
881
886
|
try {
|
|
882
887
|
const jsonSchema = direction === "input" ? schema["~standard"].jsonSchema.input({ target: "draft-2020-12" }) : schema["~standard"].jsonSchema.output({ target: "draft-2020-12" });
|
|
883
|
-
|
|
884
|
-
try {
|
|
885
|
-
const result = schema["~standard"].validate(void 0);
|
|
886
|
-
if (!(result instanceof Promise) && !result.issues) {
|
|
887
|
-
optional = direction === "input" ? true : result.value === void 0;
|
|
888
|
-
}
|
|
889
|
-
} catch {
|
|
890
|
-
}
|
|
891
|
-
return [jsonSchema, optional];
|
|
888
|
+
return [jsonSchema, isStandardSchemaOptional(schema, direction)];
|
|
892
889
|
} catch {
|
|
893
890
|
return [{}, true];
|
|
894
891
|
}
|
|
@@ -998,4 +995,4 @@ class SmartCoercionLinkPlugin {
|
|
|
998
995
|
}
|
|
999
996
|
}
|
|
1000
997
|
|
|
1001
|
-
export { DelegatingJsonSchemaConverter, JsonSchemaCoercer, JsonSchemaFormat, JsonSchemaType, JsonSchemaXNativeType, SmartCoercionHandlerPlugin, SmartCoercionLinkPlugin, StandardJsonSchemaConverter, combineJsonObjectSchemaEntries, combineJsonSchemasWithComposition, decodeJsonPointerSegment, deduplicateJsonSchemas, encodeJsonPointerSegment, ensureJsonSchemaObject, extractJsonObjectSchemaEntries, flattenJsonUnionSchema, hoistRecursiveRefToDef, isJsonArraySchema, isJsonFileSchema, isJsonObjectSchema, isJsonPrimitiveSchema, isUnconstrainedSchema, mapJsonSchemaRefs, matchArrayableJsonSchema, resolveJsonSchemaRootLocalRef, visitJsonSchemaRefs };
|
|
998
|
+
export { DelegatingJsonSchemaConverter, JsonSchemaCoercer, JsonSchemaFormat, JsonSchemaType, JsonSchemaXNativeType, SmartCoercionHandlerPlugin, SmartCoercionLinkPlugin, StandardJsonSchemaConverter, combineJsonObjectSchemaEntries, combineJsonSchemasWithComposition, decodeJsonPointerSegment, deduplicateJsonSchemas, encodeJsonPointerSegment, ensureJsonSchemaObject, extractJsonObjectSchemaEntries, flattenJsonUnionSchema, hoistRecursiveRefToDef, isJsonArraySchema, isJsonFileSchema, isJsonObjectSchema, isJsonPrimitiveSchema, isStandardSchemaOptional, isUnconstrainedSchema, mapJsonSchemaRefs, matchArrayableJsonSchema, resolveJsonSchemaRootLocalRef, visitJsonSchemaRefs };
|
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.41",
|
|
5
5
|
"description": "JSON Schema conversion and smart request coercion for oRPC and OpenAPI",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"funding": [
|
|
@@ -36,10 +36,10 @@
|
|
|
36
36
|
],
|
|
37
37
|
"dependencies": {
|
|
38
38
|
"@openapi-spec/types": "~0.1.0",
|
|
39
|
-
"@orpc/client": "2.0.0-beta.
|
|
40
|
-
"@orpc/contract": "2.0.0-beta.
|
|
41
|
-
"@orpc/server": "2.0.0-beta.
|
|
42
|
-
"@orpc/shared": "2.0.0-beta.
|
|
39
|
+
"@orpc/client": "2.0.0-beta.41",
|
|
40
|
+
"@orpc/contract": "2.0.0-beta.41",
|
|
41
|
+
"@orpc/server": "2.0.0-beta.41",
|
|
42
|
+
"@orpc/shared": "2.0.0-beta.41",
|
|
43
43
|
"@standard-schema/spec": "^1.1.0"
|
|
44
44
|
},
|
|
45
45
|
"devDependencies": {
|