@orpc/json-schema 1.14.6 → 2.0.0-beta.10
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 +78 -87
- package/dist/index.d.mts +165 -17
- package/dist/index.d.ts +165 -17
- package/dist/index.mjs +735 -101
- package/package.json +8 -8
package/dist/index.mjs
CHANGED
|
@@ -1,5 +1,142 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
1
|
+
import { get, isPlainObject, toArray, tryOrUndefined, isDeepEqual, omit, isTypescriptObject } from '@orpc/shared';
|
|
2
|
+
export { Format as JsonSchemaFormat, TypeName as JsonSchemaType } from 'json-schema-typed/draft-2020-12';
|
|
3
|
+
import { ORPCError, cloneORPCError } from '@orpc/client';
|
|
4
|
+
import { getProcedureContractOrThrow } from '@orpc/contract';
|
|
5
|
+
|
|
6
|
+
const JSON_SCHEMA_LOGIC_KEYWORDS = /* @__PURE__ */ new Set([
|
|
7
|
+
"$dynamicRef",
|
|
8
|
+
"$ref",
|
|
9
|
+
"additionalItems",
|
|
10
|
+
"additionalProperties",
|
|
11
|
+
"allOf",
|
|
12
|
+
"anyOf",
|
|
13
|
+
"const",
|
|
14
|
+
"contains",
|
|
15
|
+
"contentEncoding",
|
|
16
|
+
"contentMediaType",
|
|
17
|
+
"contentSchema",
|
|
18
|
+
"dependencies",
|
|
19
|
+
"dependentRequired",
|
|
20
|
+
"dependentSchemas",
|
|
21
|
+
"else",
|
|
22
|
+
"enum",
|
|
23
|
+
"exclusiveMaximum",
|
|
24
|
+
"exclusiveMinimum",
|
|
25
|
+
"format",
|
|
26
|
+
"if",
|
|
27
|
+
"items",
|
|
28
|
+
"maxContains",
|
|
29
|
+
"maximum",
|
|
30
|
+
"maxItems",
|
|
31
|
+
"maxLength",
|
|
32
|
+
"maxProperties",
|
|
33
|
+
"minContains",
|
|
34
|
+
"minimum",
|
|
35
|
+
"minItems",
|
|
36
|
+
"minLength",
|
|
37
|
+
"minProperties",
|
|
38
|
+
"multipleOf",
|
|
39
|
+
"not",
|
|
40
|
+
"oneOf",
|
|
41
|
+
"pattern",
|
|
42
|
+
"patternProperties",
|
|
43
|
+
"prefixItems",
|
|
44
|
+
"properties",
|
|
45
|
+
"propertyNames",
|
|
46
|
+
"required",
|
|
47
|
+
"then",
|
|
48
|
+
"type",
|
|
49
|
+
"unevaluatedItems",
|
|
50
|
+
"unevaluatedProperties",
|
|
51
|
+
"uniqueItems"
|
|
52
|
+
]);
|
|
53
|
+
const JSON_SCHEMA_PRIMITIVE_TYPES = /* @__PURE__ */ new Set(["string", "number", "integer", "boolean", "null"]);
|
|
54
|
+
const JSON_SCHEMA_RECORD_KEYWORDS = /* @__PURE__ */ new Set(["properties", "patternProperties", "dependentSchemas", "dependencies", "$defs"]);
|
|
55
|
+
|
|
56
|
+
function encodeJsonPointerSegment(segment) {
|
|
57
|
+
return segment.replaceAll("~", "~0").replaceAll("/", "~1");
|
|
58
|
+
}
|
|
59
|
+
function decodeJsonPointerSegment(segment) {
|
|
60
|
+
return segment.replaceAll("~1", "/").replaceAll("~0", "~");
|
|
61
|
+
}
|
|
62
|
+
function mapJsonSchemaRefs(value, map, schemaLevel = true, path = []) {
|
|
63
|
+
if (!value || typeof value !== "object") {
|
|
64
|
+
return value;
|
|
65
|
+
}
|
|
66
|
+
if (Array.isArray(value)) {
|
|
67
|
+
return value.map((item, index) => mapJsonSchemaRefs(item, map, schemaLevel, [...path, index]));
|
|
68
|
+
}
|
|
69
|
+
const result = {};
|
|
70
|
+
for (const [key, val] of Object.entries(value)) {
|
|
71
|
+
if (key === "$ref" && typeof val === "string") {
|
|
72
|
+
result[key] = map(val, [...path, key]);
|
|
73
|
+
} else if (!schemaLevel) {
|
|
74
|
+
result[key] = mapJsonSchemaRefs(val, map, true, [...path, key]);
|
|
75
|
+
} else if (JSON_SCHEMA_LOGIC_KEYWORDS.has(key) || JSON_SCHEMA_RECORD_KEYWORDS.has(key)) {
|
|
76
|
+
result[key] = mapJsonSchemaRefs(val, map, !JSON_SCHEMA_RECORD_KEYWORDS.has(key), [...path, key]);
|
|
77
|
+
} else {
|
|
78
|
+
result[key] = val;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
return result;
|
|
82
|
+
}
|
|
83
|
+
function hoistRecursiveRefToDef(schema) {
|
|
84
|
+
if (typeof schema !== "object") {
|
|
85
|
+
return schema;
|
|
86
|
+
}
|
|
87
|
+
let defName;
|
|
88
|
+
const rewritten = mapJsonSchemaRefs(schema, (ref) => {
|
|
89
|
+
if (ref === "#" || ref.startsWith("#/") && !ref.startsWith("#/$defs/") && get(schema, ref.slice(2).split("/").map(decodeJsonPointerSegment)) !== void 0) {
|
|
90
|
+
defName ??= findRecursiveJsonSchemaDefName(schema.$defs);
|
|
91
|
+
return `#/$defs/${encodeJsonPointerSegment(defName)}${ref.slice(1)}`;
|
|
92
|
+
}
|
|
93
|
+
return ref;
|
|
94
|
+
});
|
|
95
|
+
if (defName === void 0) {
|
|
96
|
+
return schema;
|
|
97
|
+
}
|
|
98
|
+
const { $defs, ...rest } = rewritten;
|
|
99
|
+
return {
|
|
100
|
+
$ref: `#/$defs/${encodeJsonPointerSegment(defName)}`,
|
|
101
|
+
$defs: {
|
|
102
|
+
...$defs,
|
|
103
|
+
[defName]: rest
|
|
104
|
+
}
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
function resolveJsonSchemaRootLocalRef(schema, $defs) {
|
|
108
|
+
if (typeof schema === "boolean") {
|
|
109
|
+
return schema;
|
|
110
|
+
}
|
|
111
|
+
if (arguments.length === 1) {
|
|
112
|
+
$defs = schema.$defs;
|
|
113
|
+
}
|
|
114
|
+
if (!$defs) {
|
|
115
|
+
return schema;
|
|
116
|
+
}
|
|
117
|
+
if (typeof schema.$ref !== "string" || !schema.$ref.startsWith("#/$defs/")) {
|
|
118
|
+
return schema;
|
|
119
|
+
}
|
|
120
|
+
const resolved = get($defs, schema.$ref.slice("#/$defs/".length).split("/").map(decodeJsonPointerSegment));
|
|
121
|
+
if (resolved === void 0) {
|
|
122
|
+
return schema;
|
|
123
|
+
}
|
|
124
|
+
if (typeof resolved !== "object") {
|
|
125
|
+
return resolved;
|
|
126
|
+
}
|
|
127
|
+
const { $ref: _ref, ...rest } = schema;
|
|
128
|
+
return resolveJsonSchemaRootLocalRef({
|
|
129
|
+
...rest,
|
|
130
|
+
...resolved
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
function findRecursiveJsonSchemaDefName(defs) {
|
|
134
|
+
let index = 0;
|
|
135
|
+
while (defs?.[`__schema${index}`] !== void 0) {
|
|
136
|
+
index++;
|
|
137
|
+
}
|
|
138
|
+
return `__schema${index}`;
|
|
139
|
+
}
|
|
3
140
|
|
|
4
141
|
var JsonSchemaXNativeType = /* @__PURE__ */ ((JsonSchemaXNativeType2) => {
|
|
5
142
|
JsonSchemaXNativeType2["BigInt"] = "bigint";
|
|
@@ -13,25 +150,30 @@ var JsonSchemaXNativeType = /* @__PURE__ */ ((JsonSchemaXNativeType2) => {
|
|
|
13
150
|
|
|
14
151
|
const FLEXIBLE_DATE_FORMAT_REGEX = /^[^-]+-[^-]+-[^-]+$/;
|
|
15
152
|
class JsonSchemaCoercer {
|
|
16
|
-
coerce(schema,
|
|
17
|
-
|
|
153
|
+
coerce([schema, optional], value) {
|
|
154
|
+
if (optional && value === void 0) {
|
|
155
|
+
return value;
|
|
156
|
+
}
|
|
157
|
+
const [, coerced] = this.coerceInternal(schema, schema, value);
|
|
18
158
|
return coerced;
|
|
19
159
|
}
|
|
20
|
-
|
|
160
|
+
coerceInternal(rootSchema, schema, value) {
|
|
21
161
|
if (typeof schema === "boolean") {
|
|
22
|
-
return [schema,
|
|
162
|
+
return [schema, value];
|
|
23
163
|
}
|
|
24
164
|
if (Array.isArray(schema.type)) {
|
|
25
|
-
return this
|
|
26
|
-
|
|
27
|
-
|
|
165
|
+
return this.coerceInternal(
|
|
166
|
+
rootSchema,
|
|
167
|
+
{ anyOf: schema.type.map((type) => ({ ...schema, type })) },
|
|
168
|
+
value
|
|
169
|
+
);
|
|
28
170
|
}
|
|
29
|
-
let coerced =
|
|
171
|
+
let coerced = value;
|
|
30
172
|
let satisfied = true;
|
|
31
173
|
if (typeof schema.$ref === "string") {
|
|
32
|
-
const
|
|
33
|
-
if (
|
|
34
|
-
const [subSatisfied, subCoerced] = this
|
|
174
|
+
const resolved = schema.$ref.startsWith("#/") ? get(rootSchema, schema.$ref.slice("#/".length).split("/").map(decodeJsonPointerSegment)) : schema.$ref === "#" ? rootSchema : void 0;
|
|
175
|
+
if (resolved !== void 0) {
|
|
176
|
+
const [subSatisfied, subCoerced] = this.coerceInternal(rootSchema, resolved, coerced);
|
|
35
177
|
coerced = subCoerced;
|
|
36
178
|
satisfied = subSatisfied;
|
|
37
179
|
}
|
|
@@ -39,11 +181,11 @@ class JsonSchemaCoercer {
|
|
|
39
181
|
const enumValues = schema.const !== void 0 ? [schema.const] : schema.enum;
|
|
40
182
|
if (enumValues !== void 0 && !enumValues.includes(coerced)) {
|
|
41
183
|
if (typeof coerced === "string") {
|
|
42
|
-
const numberValue =
|
|
184
|
+
const numberValue = stringToNumber(coerced);
|
|
43
185
|
if (enumValues.includes(numberValue)) {
|
|
44
186
|
coerced = numberValue;
|
|
45
187
|
} else {
|
|
46
|
-
const booleanValue =
|
|
188
|
+
const booleanValue = stringToBoolean(coerced);
|
|
47
189
|
if (enumValues.includes(booleanValue)) {
|
|
48
190
|
coerced = booleanValue;
|
|
49
191
|
} else {
|
|
@@ -54,7 +196,7 @@ class JsonSchemaCoercer {
|
|
|
54
196
|
satisfied = false;
|
|
55
197
|
}
|
|
56
198
|
}
|
|
57
|
-
if (
|
|
199
|
+
if (schema.type) {
|
|
58
200
|
switch (schema.type) {
|
|
59
201
|
case "null": {
|
|
60
202
|
if (coerced !== null) {
|
|
@@ -70,7 +212,7 @@ class JsonSchemaCoercer {
|
|
|
70
212
|
}
|
|
71
213
|
case "number": {
|
|
72
214
|
if (typeof coerced === "string") {
|
|
73
|
-
coerced =
|
|
215
|
+
coerced = stringToNumber(coerced);
|
|
74
216
|
}
|
|
75
217
|
if (typeof coerced !== "number") {
|
|
76
218
|
satisfied = false;
|
|
@@ -79,7 +221,7 @@ class JsonSchemaCoercer {
|
|
|
79
221
|
}
|
|
80
222
|
case "integer": {
|
|
81
223
|
if (typeof coerced === "string") {
|
|
82
|
-
coerced =
|
|
224
|
+
coerced = stringToInteger(coerced);
|
|
83
225
|
}
|
|
84
226
|
if (typeof coerced !== "number" || !Number.isInteger(coerced)) {
|
|
85
227
|
satisfied = false;
|
|
@@ -88,7 +230,7 @@ class JsonSchemaCoercer {
|
|
|
88
230
|
}
|
|
89
231
|
case "boolean": {
|
|
90
232
|
if (typeof coerced === "string") {
|
|
91
|
-
coerced =
|
|
233
|
+
coerced = stringToBoolean(coerced);
|
|
92
234
|
}
|
|
93
235
|
if (typeof coerced !== "boolean") {
|
|
94
236
|
satisfied = false;
|
|
@@ -106,7 +248,7 @@ class JsonSchemaCoercer {
|
|
|
106
248
|
satisfied = false;
|
|
107
249
|
return item;
|
|
108
250
|
}
|
|
109
|
-
const [subSatisfied, subCoerced] = this
|
|
251
|
+
const [subSatisfied, subCoerced] = this.coerceInternal(rootSchema, subSchema, item);
|
|
110
252
|
if (!subSatisfied) {
|
|
111
253
|
satisfied = false;
|
|
112
254
|
}
|
|
@@ -130,25 +272,25 @@ class JsonSchemaCoercer {
|
|
|
130
272
|
if (Array.isArray(coerced)) {
|
|
131
273
|
coerced = { ...coerced };
|
|
132
274
|
}
|
|
133
|
-
if (
|
|
275
|
+
if (isPlainObject(coerced)) {
|
|
134
276
|
let shouldUseCoercedItems = false;
|
|
135
277
|
const coercedItems = {};
|
|
136
|
-
const patternProperties = Object.entries(schema.patternProperties ?? {}).map(([key,
|
|
278
|
+
const patternProperties = Object.entries(schema.patternProperties ?? {}).map(([key, value2]) => [new RegExp(key), value2]);
|
|
137
279
|
for (const key in coerced) {
|
|
138
|
-
const
|
|
280
|
+
const value2 = coerced[key];
|
|
139
281
|
const subSchema = schema.properties?.[key] ?? patternProperties.find(([pattern]) => pattern.test(key))?.[1] ?? schema.additionalProperties;
|
|
140
|
-
if (
|
|
141
|
-
coercedItems[key] =
|
|
282
|
+
if (value2 === void 0 && !schema.required?.includes(key)) {
|
|
283
|
+
coercedItems[key] = value2;
|
|
142
284
|
} else if (subSchema === void 0) {
|
|
143
|
-
coercedItems[key] =
|
|
285
|
+
coercedItems[key] = value2;
|
|
144
286
|
satisfied = false;
|
|
145
287
|
} else {
|
|
146
|
-
const [subSatisfied, subCoerced] = this
|
|
288
|
+
const [subSatisfied, subCoerced] = this.coerceInternal(rootSchema, subSchema, value2);
|
|
147
289
|
coercedItems[key] = subCoerced;
|
|
148
290
|
if (!subSatisfied) {
|
|
149
291
|
satisfied = false;
|
|
150
292
|
}
|
|
151
|
-
if (subCoerced !==
|
|
293
|
+
if (subCoerced !== value2) {
|
|
152
294
|
shouldUseCoercedItems = true;
|
|
153
295
|
}
|
|
154
296
|
}
|
|
@@ -170,7 +312,7 @@ class JsonSchemaCoercer {
|
|
|
170
312
|
switch (schema["x-native-type"]) {
|
|
171
313
|
case JsonSchemaXNativeType.Date: {
|
|
172
314
|
if (typeof coerced === "string") {
|
|
173
|
-
coerced =
|
|
315
|
+
coerced = stringToDate(coerced);
|
|
174
316
|
}
|
|
175
317
|
if (!(coerced instanceof Date)) {
|
|
176
318
|
satisfied = false;
|
|
@@ -180,10 +322,10 @@ class JsonSchemaCoercer {
|
|
|
180
322
|
case JsonSchemaXNativeType.BigInt: {
|
|
181
323
|
switch (typeof coerced) {
|
|
182
324
|
case "string":
|
|
183
|
-
coerced =
|
|
325
|
+
coerced = stringToBigInt(coerced);
|
|
184
326
|
break;
|
|
185
327
|
case "number":
|
|
186
|
-
coerced =
|
|
328
|
+
coerced = numberToBigInt(coerced);
|
|
187
329
|
break;
|
|
188
330
|
}
|
|
189
331
|
if (typeof coerced !== "bigint") {
|
|
@@ -193,7 +335,7 @@ class JsonSchemaCoercer {
|
|
|
193
335
|
}
|
|
194
336
|
case JsonSchemaXNativeType.RegExp: {
|
|
195
337
|
if (typeof coerced === "string") {
|
|
196
|
-
coerced =
|
|
338
|
+
coerced = stringToRegExp(coerced);
|
|
197
339
|
}
|
|
198
340
|
if (!(coerced instanceof RegExp)) {
|
|
199
341
|
satisfied = false;
|
|
@@ -202,7 +344,7 @@ class JsonSchemaCoercer {
|
|
|
202
344
|
}
|
|
203
345
|
case JsonSchemaXNativeType.Url: {
|
|
204
346
|
if (typeof coerced === "string") {
|
|
205
|
-
coerced =
|
|
347
|
+
coerced = stringToURL(coerced);
|
|
206
348
|
}
|
|
207
349
|
if (!(coerced instanceof URL)) {
|
|
208
350
|
satisfied = false;
|
|
@@ -211,7 +353,7 @@ class JsonSchemaCoercer {
|
|
|
211
353
|
}
|
|
212
354
|
case JsonSchemaXNativeType.Set: {
|
|
213
355
|
if (Array.isArray(coerced)) {
|
|
214
|
-
coerced =
|
|
356
|
+
coerced = arrayToSet(coerced);
|
|
215
357
|
}
|
|
216
358
|
if (!(coerced instanceof Set)) {
|
|
217
359
|
satisfied = false;
|
|
@@ -220,7 +362,7 @@ class JsonSchemaCoercer {
|
|
|
220
362
|
}
|
|
221
363
|
case JsonSchemaXNativeType.Map: {
|
|
222
364
|
if (Array.isArray(coerced)) {
|
|
223
|
-
coerced =
|
|
365
|
+
coerced = arrayToMap(coerced);
|
|
224
366
|
}
|
|
225
367
|
if (!(coerced instanceof Map)) {
|
|
226
368
|
satisfied = false;
|
|
@@ -231,7 +373,7 @@ class JsonSchemaCoercer {
|
|
|
231
373
|
}
|
|
232
374
|
if (schema.allOf) {
|
|
233
375
|
for (const subSchema of schema.allOf) {
|
|
234
|
-
const [subSatisfied, subCoerced] = this
|
|
376
|
+
const [subSatisfied, subCoerced] = this.coerceInternal(rootSchema, subSchema, coerced);
|
|
235
377
|
coerced = subCoerced;
|
|
236
378
|
if (!subSatisfied) {
|
|
237
379
|
satisfied = false;
|
|
@@ -242,7 +384,7 @@ class JsonSchemaCoercer {
|
|
|
242
384
|
if (schema[key]) {
|
|
243
385
|
let bestOptions;
|
|
244
386
|
for (const subSchema of schema[key]) {
|
|
245
|
-
const [subSatisfied, subCoerced] = this
|
|
387
|
+
const [subSatisfied, subCoerced] = this.coerceInternal(rootSchema, subSchema, coerced);
|
|
246
388
|
if (subSatisfied) {
|
|
247
389
|
if (!bestOptions || subCoerced === coerced) {
|
|
248
390
|
bestOptions = { coerced: subCoerced, satisfied: subSatisfied };
|
|
@@ -257,107 +399,599 @@ class JsonSchemaCoercer {
|
|
|
257
399
|
}
|
|
258
400
|
}
|
|
259
401
|
if (typeof schema.not !== "undefined") {
|
|
260
|
-
const [notSatisfied] = this
|
|
402
|
+
const [notSatisfied] = this.coerceInternal(rootSchema, schema.not, coerced);
|
|
261
403
|
if (notSatisfied) {
|
|
262
404
|
satisfied = false;
|
|
263
405
|
}
|
|
264
406
|
}
|
|
265
407
|
return [satisfied, coerced];
|
|
266
408
|
}
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
409
|
+
}
|
|
410
|
+
function stringToNumber(value) {
|
|
411
|
+
const num = Number.parseFloat(value);
|
|
412
|
+
if (Number.isNaN(num) || num !== Number(value)) {
|
|
413
|
+
return value;
|
|
414
|
+
}
|
|
415
|
+
return num;
|
|
416
|
+
}
|
|
417
|
+
function stringToInteger(value) {
|
|
418
|
+
const num = Number.parseInt(value);
|
|
419
|
+
if (Number.isNaN(num) || num !== Number(value)) {
|
|
420
|
+
return value;
|
|
421
|
+
}
|
|
422
|
+
return num;
|
|
423
|
+
}
|
|
424
|
+
function stringToBoolean(value) {
|
|
425
|
+
const lower = value.toLowerCase();
|
|
426
|
+
if (lower === "false" || lower === "off") {
|
|
427
|
+
return false;
|
|
428
|
+
}
|
|
429
|
+
if (lower === "true" || lower === "on") {
|
|
430
|
+
return true;
|
|
431
|
+
}
|
|
432
|
+
return value;
|
|
433
|
+
}
|
|
434
|
+
function stringToBigInt(value) {
|
|
435
|
+
return tryOrUndefined(() => BigInt(value)) ?? value;
|
|
436
|
+
}
|
|
437
|
+
function numberToBigInt(value) {
|
|
438
|
+
return tryOrUndefined(() => BigInt(value)) ?? value;
|
|
439
|
+
}
|
|
440
|
+
function stringToDate(value) {
|
|
441
|
+
const date = new Date(value);
|
|
442
|
+
if (Number.isNaN(date.getTime()) || !FLEXIBLE_DATE_FORMAT_REGEX.test(value)) {
|
|
443
|
+
return value;
|
|
444
|
+
}
|
|
445
|
+
return date;
|
|
446
|
+
}
|
|
447
|
+
function stringToRegExp(value) {
|
|
448
|
+
const match = value.match(/^\/(.*)\/([a-z]*)$/);
|
|
449
|
+
if (match) {
|
|
450
|
+
const [, pattern, flags] = match;
|
|
451
|
+
return tryOrUndefined(() => new RegExp(pattern, flags)) ?? value;
|
|
452
|
+
}
|
|
453
|
+
return value;
|
|
454
|
+
}
|
|
455
|
+
function stringToURL(value) {
|
|
456
|
+
return tryOrUndefined(() => new URL(value)) ?? value;
|
|
457
|
+
}
|
|
458
|
+
function arrayToSet(value) {
|
|
459
|
+
const set = new Set(value);
|
|
460
|
+
if (set.size !== value.length) {
|
|
461
|
+
return value;
|
|
462
|
+
}
|
|
463
|
+
return set;
|
|
464
|
+
}
|
|
465
|
+
function arrayToMap(value) {
|
|
466
|
+
if (value.some((item) => !Array.isArray(item) || item.length !== 2)) {
|
|
467
|
+
return value;
|
|
468
|
+
}
|
|
469
|
+
const result = new Map(value);
|
|
470
|
+
if (result.size !== value.length) {
|
|
471
|
+
return value;
|
|
472
|
+
}
|
|
473
|
+
return result;
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
function isJsonPrimitiveSchema(schema) {
|
|
477
|
+
if (typeof schema === "boolean") {
|
|
478
|
+
return false;
|
|
479
|
+
}
|
|
480
|
+
if (typeof schema.type === "string" && JSON_SCHEMA_PRIMITIVE_TYPES.has(schema.type)) {
|
|
481
|
+
return true;
|
|
482
|
+
}
|
|
483
|
+
if (schema.const !== void 0) {
|
|
484
|
+
return true;
|
|
485
|
+
}
|
|
486
|
+
if (schema.enum !== void 0) {
|
|
487
|
+
return true;
|
|
488
|
+
}
|
|
489
|
+
return false;
|
|
490
|
+
}
|
|
491
|
+
function isJsonFileSchema(schema) {
|
|
492
|
+
return typeof schema !== "boolean" && schema.type === "string" && (typeof schema.contentMediaType === "string" || schema.format === "binary" || schema.contentEncoding === "binary");
|
|
493
|
+
}
|
|
494
|
+
function isJsonObjectSchema(schema) {
|
|
495
|
+
return typeof schema !== "boolean" && schema.type === "object";
|
|
496
|
+
}
|
|
497
|
+
function isJsonArraySchema(schema) {
|
|
498
|
+
return typeof schema !== "boolean" && schema.type === "array";
|
|
499
|
+
}
|
|
500
|
+
function isUnconstrainedSchema(schema) {
|
|
501
|
+
if (typeof schema === "boolean") {
|
|
502
|
+
return schema;
|
|
503
|
+
}
|
|
504
|
+
if (Object.keys(schema).every((k) => !JSON_SCHEMA_LOGIC_KEYWORDS.has(k))) {
|
|
505
|
+
return true;
|
|
506
|
+
}
|
|
507
|
+
return false;
|
|
508
|
+
}
|
|
509
|
+
function ensureJsonSchemaObject(schema) {
|
|
510
|
+
if (typeof schema === "boolean") {
|
|
511
|
+
return schema ? {} : { not: {} };
|
|
512
|
+
}
|
|
513
|
+
return schema;
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
function combineJsonSchemasWithComposition(keyword, schemas) {
|
|
517
|
+
if (schemas.length <= 1) {
|
|
518
|
+
return schemas[0] ?? true;
|
|
519
|
+
}
|
|
520
|
+
const mergedDefs = {};
|
|
521
|
+
const compositionBranches = [];
|
|
522
|
+
for (let i = 0; i < schemas.length; i++) {
|
|
523
|
+
const schema = schemas[i];
|
|
524
|
+
if (typeof schema === "boolean") {
|
|
525
|
+
compositionBranches.push(schema);
|
|
526
|
+
continue;
|
|
271
527
|
}
|
|
272
|
-
|
|
528
|
+
const { $defs, ...rest } = schema;
|
|
529
|
+
const renameMap = {};
|
|
530
|
+
const promotedNames = /* @__PURE__ */ new Set();
|
|
531
|
+
if ($defs) {
|
|
532
|
+
for (const [name, def] of Object.entries($defs)) {
|
|
533
|
+
if (def === void 0)
|
|
534
|
+
continue;
|
|
535
|
+
promotedNames.add(name);
|
|
536
|
+
if (name in mergedDefs) {
|
|
537
|
+
if (isDeepEqual(mergedDefs[name], def)) {
|
|
538
|
+
continue;
|
|
539
|
+
}
|
|
540
|
+
let counter = 2;
|
|
541
|
+
let newName = `${name}${counter}`;
|
|
542
|
+
while (newName in mergedDefs) {
|
|
543
|
+
counter++;
|
|
544
|
+
newName = `${name}${counter}`;
|
|
545
|
+
}
|
|
546
|
+
mergedDefs[newName] = def;
|
|
547
|
+
renameMap[name] = newName;
|
|
548
|
+
} else {
|
|
549
|
+
mergedDefs[name] = def;
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
compositionBranches.push(mapJsonSchemaRefs(
|
|
554
|
+
rest,
|
|
555
|
+
(ref) => {
|
|
556
|
+
if (ref === "#") {
|
|
557
|
+
return `#/${keyword}/${i}`;
|
|
558
|
+
}
|
|
559
|
+
if (ref.startsWith("#/$defs/")) {
|
|
560
|
+
const afterPrefix = ref.slice("#/$defs/".length);
|
|
561
|
+
const slashIdx = afterPrefix.indexOf("/");
|
|
562
|
+
const encodedSegment = slashIdx === -1 ? afterPrefix : afterPrefix.slice(0, slashIdx);
|
|
563
|
+
const rest2 = slashIdx === -1 ? "" : afterPrefix.slice(slashIdx);
|
|
564
|
+
const defName = decodeJsonPointerSegment(encodedSegment);
|
|
565
|
+
if (!promotedNames.has(defName)) {
|
|
566
|
+
return ref;
|
|
567
|
+
}
|
|
568
|
+
if (defName in renameMap) {
|
|
569
|
+
return `#/$defs/${encodeJsonPointerSegment(renameMap[defName])}${rest2}`;
|
|
570
|
+
}
|
|
571
|
+
return ref;
|
|
572
|
+
}
|
|
573
|
+
if (ref.startsWith("#/") && get(rest, ref.slice(2).split("/").map(decodeJsonPointerSegment)) !== void 0) {
|
|
574
|
+
return `#/${keyword}/${i}/${ref.slice(2)}`;
|
|
575
|
+
}
|
|
576
|
+
return ref;
|
|
577
|
+
}
|
|
578
|
+
));
|
|
273
579
|
}
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
580
|
+
const result = { [keyword]: compositionBranches };
|
|
581
|
+
if (Object.keys(mergedDefs).length > 0) {
|
|
582
|
+
result.$defs = mergedDefs;
|
|
583
|
+
}
|
|
584
|
+
return result;
|
|
585
|
+
}
|
|
586
|
+
function combineJsonObjectSchemaEntries(entries) {
|
|
587
|
+
const properties = {};
|
|
588
|
+
const required = [];
|
|
589
|
+
const mergedDefs = {};
|
|
590
|
+
for (const [name, propertySchema, optional] of entries) {
|
|
591
|
+
if (!optional) {
|
|
592
|
+
required.push(name);
|
|
278
593
|
}
|
|
279
|
-
|
|
594
|
+
if (typeof propertySchema === "boolean") {
|
|
595
|
+
properties[name] = propertySchema;
|
|
596
|
+
continue;
|
|
597
|
+
}
|
|
598
|
+
const { $defs, ...rest } = propertySchema;
|
|
599
|
+
const renameMap = {};
|
|
600
|
+
const promotedNames = /* @__PURE__ */ new Set();
|
|
601
|
+
if ($defs) {
|
|
602
|
+
for (const [defName, def] of Object.entries($defs)) {
|
|
603
|
+
if (def === void 0) {
|
|
604
|
+
continue;
|
|
605
|
+
}
|
|
606
|
+
promotedNames.add(defName);
|
|
607
|
+
if (defName in mergedDefs) {
|
|
608
|
+
if (isDeepEqual(mergedDefs[defName], def)) {
|
|
609
|
+
continue;
|
|
610
|
+
}
|
|
611
|
+
let counter = 2;
|
|
612
|
+
let newName = `${defName}${counter}`;
|
|
613
|
+
while (newName in mergedDefs) {
|
|
614
|
+
counter++;
|
|
615
|
+
newName = `${defName}${counter}`;
|
|
616
|
+
}
|
|
617
|
+
mergedDefs[newName] = def;
|
|
618
|
+
renameMap[defName] = newName;
|
|
619
|
+
} else {
|
|
620
|
+
mergedDefs[defName] = def;
|
|
621
|
+
}
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
const propertyPathPrefix = `#/properties/${encodeJsonPointerSegment(name)}`;
|
|
625
|
+
properties[name] = mapJsonSchemaRefs(rest, (ref) => {
|
|
626
|
+
if (ref.startsWith("#/$defs/")) {
|
|
627
|
+
const afterPrefix = ref.slice("#/$defs/".length);
|
|
628
|
+
const slashIdx = afterPrefix.indexOf("/");
|
|
629
|
+
const encodedSegment = slashIdx === -1 ? afterPrefix : afterPrefix.slice(0, slashIdx);
|
|
630
|
+
const refRest = slashIdx === -1 ? "" : afterPrefix.slice(slashIdx);
|
|
631
|
+
const defName = decodeJsonPointerSegment(encodedSegment);
|
|
632
|
+
if (!promotedNames.has(defName)) {
|
|
633
|
+
return ref;
|
|
634
|
+
}
|
|
635
|
+
if (defName in renameMap) {
|
|
636
|
+
return `#/$defs/${encodeJsonPointerSegment(renameMap[defName])}${refRest}`;
|
|
637
|
+
}
|
|
638
|
+
return ref;
|
|
639
|
+
}
|
|
640
|
+
if (ref === "#") {
|
|
641
|
+
return propertyPathPrefix;
|
|
642
|
+
}
|
|
643
|
+
if (ref.startsWith("#/") && get(rest, ref.slice(2).split("/").map(decodeJsonPointerSegment)) !== void 0) {
|
|
644
|
+
return `${propertyPathPrefix}/${ref.slice(2)}`;
|
|
645
|
+
}
|
|
646
|
+
return ref;
|
|
647
|
+
});
|
|
280
648
|
}
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
649
|
+
const schema = {
|
|
650
|
+
type: "object",
|
|
651
|
+
properties
|
|
652
|
+
};
|
|
653
|
+
if (required.length > 0) {
|
|
654
|
+
schema.required = required;
|
|
655
|
+
}
|
|
656
|
+
if (Object.keys(mergedDefs).length > 0) {
|
|
657
|
+
schema.$defs = mergedDefs;
|
|
658
|
+
}
|
|
659
|
+
return schema;
|
|
660
|
+
}
|
|
661
|
+
function extractJsonObjectSchemaEntries(schema) {
|
|
662
|
+
schema = hoistRecursiveRefToDef(schema);
|
|
663
|
+
if (typeof schema !== "object") {
|
|
664
|
+
return void 0;
|
|
665
|
+
}
|
|
666
|
+
const result = extractJsonObjectSchemaEntriesInternal(omit(schema, ["$defs"]), schema.$defs, /* @__PURE__ */ new Set());
|
|
667
|
+
if (!result.objectLike) {
|
|
668
|
+
return void 0;
|
|
669
|
+
}
|
|
670
|
+
return result.entries.map(([n, s, ...r]) => [n, withRootDefs(s, schema.$defs), ...r]);
|
|
671
|
+
}
|
|
672
|
+
function extractJsonObjectSchemaEntriesInternal(schema, $defs, resolvingRefs) {
|
|
673
|
+
if (typeof schema !== "object") {
|
|
674
|
+
return { entries: [], objectLike: false };
|
|
675
|
+
}
|
|
676
|
+
if (typeof schema.$ref === "string") {
|
|
677
|
+
if (resolvingRefs.has(schema.$ref)) {
|
|
678
|
+
return { entries: [], objectLike: true };
|
|
285
679
|
}
|
|
286
|
-
|
|
287
|
-
|
|
680
|
+
const resolved = resolveJsonSchemaRootLocalRef(schema, $defs);
|
|
681
|
+
if (resolved !== schema) {
|
|
682
|
+
return extractJsonObjectSchemaEntriesInternal(resolved, $defs, new Set(resolvingRefs).add(schema.$ref));
|
|
288
683
|
}
|
|
289
|
-
return value;
|
|
290
684
|
}
|
|
291
|
-
|
|
292
|
-
|
|
685
|
+
const sources = [];
|
|
686
|
+
if (schema.properties) {
|
|
687
|
+
sources.push({
|
|
688
|
+
entries: Object.entries(schema.properties).map(([name, propertySchema]) => {
|
|
689
|
+
return [
|
|
690
|
+
name,
|
|
691
|
+
propertySchema,
|
|
692
|
+
!schema.required?.includes(name)
|
|
693
|
+
];
|
|
694
|
+
}),
|
|
695
|
+
source: "direct"
|
|
696
|
+
});
|
|
293
697
|
}
|
|
294
|
-
|
|
295
|
-
|
|
698
|
+
let objectLike = schema.type === "object" || schema.properties !== void 0 || schema.required !== void 0 || schema.additionalProperties !== void 0;
|
|
699
|
+
for (const keyword of ["anyOf", "oneOf", "allOf"]) {
|
|
700
|
+
const branches = schema[keyword];
|
|
701
|
+
if (branches === void 0) {
|
|
702
|
+
continue;
|
|
703
|
+
}
|
|
704
|
+
const branchResults = branches.map((branch) => extractJsonObjectSchemaEntriesInternal(branch, $defs, resolvingRefs));
|
|
705
|
+
if (branchResults.some((result) => !result.objectLike)) {
|
|
706
|
+
return { entries: [], objectLike: false };
|
|
707
|
+
}
|
|
708
|
+
const entriesByName = /* @__PURE__ */ new Map();
|
|
709
|
+
for (const result of branchResults) {
|
|
710
|
+
for (const entry of result.entries) {
|
|
711
|
+
const entries = entriesByName.get(entry[0]);
|
|
712
|
+
if (entries) {
|
|
713
|
+
entries.push(entry);
|
|
714
|
+
} else {
|
|
715
|
+
entriesByName.set(entry[0], [entry]);
|
|
716
|
+
}
|
|
717
|
+
}
|
|
718
|
+
}
|
|
719
|
+
objectLike = true;
|
|
720
|
+
sources.push({
|
|
721
|
+
entries: Array.from(entriesByName.entries()).map(([name, entries]) => {
|
|
722
|
+
const schemas = deduplicateJsonSchemas(entries.map((entry) => entry[1]));
|
|
723
|
+
const required = keyword === "allOf" ? entries.some((entry) => !entry[2]) : branchResults.every((result) => {
|
|
724
|
+
const entry = result.entries.find((item) => item[0] === name);
|
|
725
|
+
return entry !== void 0 && !entry[2];
|
|
726
|
+
});
|
|
727
|
+
return [
|
|
728
|
+
name,
|
|
729
|
+
schemas.length === 1 ? schemas[0] : keyword === "allOf" ? { allOf: schemas } : { anyOf: schemas },
|
|
730
|
+
!required
|
|
731
|
+
];
|
|
732
|
+
}),
|
|
733
|
+
source: keyword
|
|
734
|
+
});
|
|
296
735
|
}
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
736
|
+
const sourceEntries = /* @__PURE__ */ new Map();
|
|
737
|
+
for (const { entries, source } of sources) {
|
|
738
|
+
for (const entry of entries) {
|
|
739
|
+
const existing = sourceEntries.get(entry[0]);
|
|
740
|
+
if (existing) {
|
|
741
|
+
existing[source] = entry;
|
|
742
|
+
} else {
|
|
743
|
+
sourceEntries.set(entry[0], { [source]: entry });
|
|
744
|
+
}
|
|
301
745
|
}
|
|
302
|
-
return date;
|
|
303
746
|
}
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
747
|
+
return {
|
|
748
|
+
entries: Array.from(sourceEntries.entries()).map(([name, entries]) => {
|
|
749
|
+
const schemas = deduplicateJsonSchemas([
|
|
750
|
+
entries.direct?.[1],
|
|
751
|
+
entries.allOf?.[1],
|
|
752
|
+
entries.anyOf?.[1],
|
|
753
|
+
entries.oneOf?.[1]
|
|
754
|
+
].filter((schema2) => schema2 !== void 0));
|
|
755
|
+
return [
|
|
756
|
+
name,
|
|
757
|
+
schemas.length === 1 ? schemas[0] : { allOf: schemas },
|
|
758
|
+
[entries.direct, entries.allOf, entries.anyOf, entries.oneOf].every((entry) => entry === void 0 || entry[2])
|
|
759
|
+
];
|
|
760
|
+
}),
|
|
761
|
+
objectLike
|
|
762
|
+
};
|
|
763
|
+
}
|
|
764
|
+
function flattenJsonUnionSchema(schema) {
|
|
765
|
+
return deduplicateJsonSchemas(flattenJsonUnionSchemaInternal(schema, /* @__PURE__ */ new Set()));
|
|
766
|
+
}
|
|
767
|
+
function flattenJsonUnionSchemaInternal(schema, resolvingRefs) {
|
|
768
|
+
if (typeof schema !== "object") {
|
|
769
|
+
return [schema];
|
|
770
|
+
}
|
|
771
|
+
if (typeof schema.$ref === "string") {
|
|
772
|
+
if (resolvingRefs.has(schema.$ref)) {
|
|
773
|
+
return [];
|
|
774
|
+
}
|
|
775
|
+
const resolved = resolveJsonSchemaRootLocalRef(schema);
|
|
776
|
+
if (resolved !== schema) {
|
|
777
|
+
const result = flattenJsonUnionSchemaInternal(resolved, resolvingRefs.add(schema.$ref));
|
|
778
|
+
if (result.length > 1) {
|
|
779
|
+
return result;
|
|
780
|
+
}
|
|
309
781
|
}
|
|
310
|
-
return value;
|
|
311
782
|
}
|
|
312
|
-
|
|
313
|
-
|
|
783
|
+
const { anyOf: _anyOf, oneOf: _oneOf, ...rest } = schema;
|
|
784
|
+
const entries = Object.entries(rest).filter(([, val]) => val !== void 0);
|
|
785
|
+
for (const keyword of ["anyOf", "oneOf"]) {
|
|
786
|
+
if (schema[keyword]) {
|
|
787
|
+
return schema[keyword].flatMap((s) => {
|
|
788
|
+
s = ensureJsonSchemaObject(s);
|
|
789
|
+
const mergedSchema = {
|
|
790
|
+
...s,
|
|
791
|
+
...Object.fromEntries(entries.filter(([key]) => s[key] === void 0)),
|
|
792
|
+
$defs: schema.$defs
|
|
793
|
+
};
|
|
794
|
+
const conflicts = entries.filter(([key]) => s[key] !== void 0);
|
|
795
|
+
if (conflicts.length) {
|
|
796
|
+
mergedSchema.allOf = [...toArray(mergedSchema.allOf), Object.fromEntries(conflicts)];
|
|
797
|
+
}
|
|
798
|
+
return flattenJsonUnionSchemaInternal(mergedSchema, resolvingRefs);
|
|
799
|
+
});
|
|
800
|
+
}
|
|
314
801
|
}
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
802
|
+
return [schema];
|
|
803
|
+
}
|
|
804
|
+
function matchArrayableJsonSchema(schema) {
|
|
805
|
+
const schemas = flattenJsonUnionSchema(schema);
|
|
806
|
+
if (schemas.length !== 2) {
|
|
807
|
+
return void 0;
|
|
808
|
+
}
|
|
809
|
+
const arraySchema = schemas.find(isJsonArraySchema);
|
|
810
|
+
if (arraySchema === void 0) {
|
|
811
|
+
return void 0;
|
|
812
|
+
}
|
|
813
|
+
const items1 = arraySchema.items ?? true;
|
|
814
|
+
const items2 = schemas.find((s) => s !== arraySchema);
|
|
815
|
+
const logicItem1 = Object.fromEntries(
|
|
816
|
+
Object.entries(ensureJsonSchemaObject(items1)).filter(([key]) => JSON_SCHEMA_LOGIC_KEYWORDS.has(key))
|
|
817
|
+
);
|
|
818
|
+
const logicItem2 = Object.fromEntries(
|
|
819
|
+
Object.entries(ensureJsonSchemaObject(items2)).filter(([key]) => JSON_SCHEMA_LOGIC_KEYWORDS.has(key))
|
|
820
|
+
);
|
|
821
|
+
if (!isDeepEqual(logicItem1, logicItem2)) {
|
|
822
|
+
return void 0;
|
|
823
|
+
}
|
|
824
|
+
return [items2, arraySchema];
|
|
825
|
+
}
|
|
826
|
+
function deduplicateJsonSchemas(schemas) {
|
|
827
|
+
const result = [];
|
|
828
|
+
for (const schema of schemas) {
|
|
829
|
+
if (result.some((i) => isDeepEqual(i, schema))) {
|
|
830
|
+
continue;
|
|
319
831
|
}
|
|
320
|
-
|
|
832
|
+
result.push(schema);
|
|
321
833
|
}
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
834
|
+
return result;
|
|
835
|
+
}
|
|
836
|
+
function withRootDefs(schema, $defs) {
|
|
837
|
+
if (typeof schema === "boolean" || !$defs) {
|
|
838
|
+
return schema;
|
|
839
|
+
}
|
|
840
|
+
return { ...schema, $defs };
|
|
841
|
+
}
|
|
842
|
+
|
|
843
|
+
class DelegatingJsonSchemaConverter {
|
|
844
|
+
constructor(converters = []) {
|
|
845
|
+
this.converters = converters;
|
|
846
|
+
}
|
|
847
|
+
async convert(schema, direction) {
|
|
848
|
+
for (const converter of this.converters) {
|
|
849
|
+
if (await converter.condition(schema, direction)) {
|
|
850
|
+
return converter.convert(schema, direction);
|
|
851
|
+
}
|
|
325
852
|
}
|
|
326
|
-
const
|
|
327
|
-
if (
|
|
328
|
-
|
|
853
|
+
const optional = !(await schema?.["~standard"].validate(void 0))?.issues?.length;
|
|
854
|
+
if (schema && "jsonSchema" in schema["~standard"] && schema["~standard"].jsonSchema) {
|
|
855
|
+
try {
|
|
856
|
+
return [
|
|
857
|
+
schema["~standard"].jsonSchema[direction](),
|
|
858
|
+
optional
|
|
859
|
+
];
|
|
860
|
+
} catch {
|
|
861
|
+
}
|
|
862
|
+
}
|
|
863
|
+
return [{}, optional];
|
|
864
|
+
}
|
|
865
|
+
}
|
|
866
|
+
|
|
867
|
+
class StandardJsonSchemaConverter {
|
|
868
|
+
condition(schema, _direction) {
|
|
869
|
+
return Boolean(
|
|
870
|
+
schema && "jsonSchema" in schema["~standard"] && isTypescriptObject(schema["~standard"].jsonSchema) && "input" in schema["~standard"].jsonSchema && typeof schema["~standard"].jsonSchema.input === "function" && "output" in schema["~standard"].jsonSchema && typeof schema["~standard"].jsonSchema.output === "function"
|
|
871
|
+
);
|
|
872
|
+
}
|
|
873
|
+
convert(schema, direction) {
|
|
874
|
+
return this.convertInternal(schema, direction);
|
|
875
|
+
}
|
|
876
|
+
convertInternal(schema, direction) {
|
|
877
|
+
try {
|
|
878
|
+
const jsonSchema = direction === "input" ? schema["~standard"].jsonSchema.input({ target: "draft-2020-12" }) : schema["~standard"].jsonSchema.output({ target: "draft-2020-12" });
|
|
879
|
+
let optional = false;
|
|
880
|
+
try {
|
|
881
|
+
const result = schema["~standard"].validate(void 0);
|
|
882
|
+
if (!(result instanceof Promise) && !result.issues) {
|
|
883
|
+
optional = direction === "input" ? true : result.value === void 0;
|
|
884
|
+
}
|
|
885
|
+
} catch {
|
|
886
|
+
}
|
|
887
|
+
return [jsonSchema, optional];
|
|
888
|
+
} catch {
|
|
889
|
+
return [{}, true];
|
|
329
890
|
}
|
|
330
|
-
return result;
|
|
331
891
|
}
|
|
332
892
|
}
|
|
333
893
|
|
|
334
|
-
class
|
|
894
|
+
class SmartCoercionHandlerPlugin {
|
|
895
|
+
name = "~smart-coercion";
|
|
335
896
|
converter;
|
|
336
897
|
coercer;
|
|
337
898
|
cache = /* @__PURE__ */ new WeakMap();
|
|
338
899
|
constructor(options = {}) {
|
|
339
|
-
this.converter = new
|
|
900
|
+
this.converter = new DelegatingJsonSchemaConverter([
|
|
901
|
+
...toArray(options.converters),
|
|
902
|
+
new StandardJsonSchemaConverter()
|
|
903
|
+
]);
|
|
340
904
|
this.coercer = new JsonSchemaCoercer();
|
|
341
905
|
}
|
|
342
906
|
init(options) {
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
907
|
+
return {
|
|
908
|
+
...options,
|
|
909
|
+
clientInterceptors: [
|
|
910
|
+
async ({ next, input, ...interceptorOptions }) => {
|
|
911
|
+
const inputSchemas = interceptorOptions.procedure["~orpc"].inputSchemas;
|
|
912
|
+
if (!inputSchemas) {
|
|
913
|
+
return next();
|
|
914
|
+
}
|
|
915
|
+
const coercedInput = await this.coerceValue(inputSchemas, input);
|
|
916
|
+
return next({ ...interceptorOptions, input: coercedInput });
|
|
917
|
+
},
|
|
918
|
+
...toArray(options.clientInterceptors)
|
|
919
|
+
]
|
|
920
|
+
};
|
|
921
|
+
}
|
|
922
|
+
async coerceValue(schemas, value) {
|
|
923
|
+
for (const schema of schemas) {
|
|
924
|
+
let converted = this.cache.get(schema);
|
|
925
|
+
if (!converted) {
|
|
926
|
+
converted = await this.converter.convert(schema, "input");
|
|
927
|
+
this.cache.set(schema, converted);
|
|
348
928
|
}
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
929
|
+
value = this.coercer.coerce(converted, value);
|
|
930
|
+
}
|
|
931
|
+
return value;
|
|
352
932
|
}
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
933
|
+
}
|
|
934
|
+
|
|
935
|
+
class SmartCoercionLinkPlugin {
|
|
936
|
+
constructor(contract, options = {}) {
|
|
937
|
+
this.contract = contract;
|
|
938
|
+
this.converter = new DelegatingJsonSchemaConverter([
|
|
939
|
+
...toArray(options.converters),
|
|
940
|
+
new StandardJsonSchemaConverter()
|
|
941
|
+
]);
|
|
942
|
+
this.coercer = new JsonSchemaCoercer();
|
|
943
|
+
}
|
|
944
|
+
name = "~smart-coercion";
|
|
945
|
+
/**
|
|
946
|
+
* Output and error values should be coerced before validation.
|
|
947
|
+
*/
|
|
948
|
+
after = ["~response-validation"];
|
|
949
|
+
converter;
|
|
950
|
+
coercer;
|
|
951
|
+
cache = /* @__PURE__ */ new WeakMap();
|
|
952
|
+
init(options) {
|
|
953
|
+
return {
|
|
954
|
+
...options,
|
|
955
|
+
interceptors: [
|
|
956
|
+
...toArray(options.interceptors),
|
|
957
|
+
async ({ next, path }) => {
|
|
958
|
+
const procedure = getProcedureContractOrThrow(this.contract, path);
|
|
959
|
+
try {
|
|
960
|
+
const output = await next();
|
|
961
|
+
const outputSchemas = procedure["~orpc"].outputSchemas;
|
|
962
|
+
if (!outputSchemas) {
|
|
963
|
+
return output;
|
|
964
|
+
}
|
|
965
|
+
const coercedOutput = await this.coerceValue(outputSchemas, output);
|
|
966
|
+
return coercedOutput;
|
|
967
|
+
} catch (error) {
|
|
968
|
+
if (!(error instanceof ORPCError) || !error.defined) {
|
|
969
|
+
throw error;
|
|
970
|
+
}
|
|
971
|
+
const errorMap = procedure["~orpc"].errorMap;
|
|
972
|
+
const dataSchema = errorMap[error.code]?.data;
|
|
973
|
+
if (!dataSchema) {
|
|
974
|
+
throw error;
|
|
975
|
+
}
|
|
976
|
+
const cloned = cloneORPCError(error);
|
|
977
|
+
cloned.data = await this.coerceValue([dataSchema], cloned.data);
|
|
978
|
+
throw cloned;
|
|
979
|
+
}
|
|
980
|
+
}
|
|
981
|
+
]
|
|
982
|
+
};
|
|
983
|
+
}
|
|
984
|
+
async coerceValue(schemas, value) {
|
|
985
|
+
for (const schema of schemas) {
|
|
986
|
+
let converted = this.cache.get(schema);
|
|
987
|
+
if (!converted) {
|
|
988
|
+
converted = await this.converter.convert(schema, "output");
|
|
989
|
+
this.cache.set(schema, converted);
|
|
990
|
+
}
|
|
991
|
+
value = this.coercer.coerce(converted, value);
|
|
358
992
|
}
|
|
359
|
-
return
|
|
993
|
+
return value;
|
|
360
994
|
}
|
|
361
995
|
}
|
|
362
996
|
|
|
363
|
-
export { JsonSchemaCoercer, JsonSchemaXNativeType,
|
|
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 };
|