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