@orpc/json-schema 2.0.0-beta.46 → 2.0.0-beta.48
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 +6 -4
- package/dist/coercer.d.mts +29 -0
- package/dist/coercer.mjs +465 -0
- package/dist/composition-utils.d.mts +41 -0
- package/dist/composition-utils.mjs +357 -0
- package/dist/constants.d.mts +4 -0
- package/dist/constants.mjs +61 -0
- package/dist/convert.d.mts +29 -0
- package/dist/convert.mjs +14 -0
- package/dist/index.d.mts +10 -297
- package/dist/index.mjs +9 -1117
- package/dist/ref-utils.d.mts +44 -0
- package/dist/ref-utils.mjs +117 -0
- package/dist/smart-coercion-handler-plugin.d.mts +22 -0
- package/dist/smart-coercion-handler-plugin.mjs +42 -0
- package/dist/smart-coercion-link-plugin.d.mts +28 -0
- package/dist/smart-coercion-link-plugin.mjs +61 -0
- package/dist/standard-json-schema-converter.d.mts +16 -0
- package/dist/standard-json-schema-converter.mjs +30 -0
- package/dist/types.d.mts +51 -0
- package/dist/types.mjs +40 -0
- package/dist/utils.d.mts +40 -0
- package/dist/utils.mjs +50 -0
- package/package.json +6 -6
- package/dist/index.d.ts +0 -297
package/dist/index.mjs
CHANGED
|
@@ -1,1117 +1,9 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
"allOf",
|
|
11
|
-
"anyOf",
|
|
12
|
-
"const",
|
|
13
|
-
"contains",
|
|
14
|
-
"contentEncoding",
|
|
15
|
-
"contentMediaType",
|
|
16
|
-
"contentSchema",
|
|
17
|
-
"dependencies",
|
|
18
|
-
"dependentRequired",
|
|
19
|
-
"dependentSchemas",
|
|
20
|
-
"else",
|
|
21
|
-
"enum",
|
|
22
|
-
"exclusiveMaximum",
|
|
23
|
-
"exclusiveMinimum",
|
|
24
|
-
"format",
|
|
25
|
-
"if",
|
|
26
|
-
"items",
|
|
27
|
-
"maxContains",
|
|
28
|
-
"maximum",
|
|
29
|
-
"maxItems",
|
|
30
|
-
"maxLength",
|
|
31
|
-
"maxProperties",
|
|
32
|
-
"minContains",
|
|
33
|
-
"minimum",
|
|
34
|
-
"minItems",
|
|
35
|
-
"minLength",
|
|
36
|
-
"minProperties",
|
|
37
|
-
"multipleOf",
|
|
38
|
-
"not",
|
|
39
|
-
"oneOf",
|
|
40
|
-
"pattern",
|
|
41
|
-
"patternProperties",
|
|
42
|
-
"prefixItems",
|
|
43
|
-
"properties",
|
|
44
|
-
"propertyNames",
|
|
45
|
-
"required",
|
|
46
|
-
"then",
|
|
47
|
-
"type",
|
|
48
|
-
"unevaluatedItems",
|
|
49
|
-
"unevaluatedProperties",
|
|
50
|
-
"uniqueItems"
|
|
51
|
-
]);
|
|
52
|
-
const JSON_SCHEMA_PRIMITIVE_TYPES = /* @__PURE__ */ new Set(["string", "number", "integer", "boolean", "null"]);
|
|
53
|
-
const JSON_SCHEMA_RECORD_KEYWORDS = /* @__PURE__ */ new Set(["properties", "patternProperties", "dependentSchemas", "dependencies", "$defs"]);
|
|
54
|
-
|
|
55
|
-
function encodeJsonPointerSegment(segment) {
|
|
56
|
-
return segment.replaceAll("~", "~0").replaceAll("/", "~1");
|
|
57
|
-
}
|
|
58
|
-
function decodeJsonPointerSegment(segment) {
|
|
59
|
-
return segment.replaceAll("~1", "/").replaceAll("~0", "~");
|
|
60
|
-
}
|
|
61
|
-
function visitJsonSchemaRefs(value, visit, schemaLevel = true, seen = /* @__PURE__ */ new Set()) {
|
|
62
|
-
if (!value || typeof value !== "object" || seen.has(value)) {
|
|
63
|
-
return;
|
|
64
|
-
}
|
|
65
|
-
seen.add(value);
|
|
66
|
-
if (Array.isArray(value)) {
|
|
67
|
-
for (const item of value) {
|
|
68
|
-
visitJsonSchemaRefs(item, visit, schemaLevel, seen);
|
|
69
|
-
}
|
|
70
|
-
return;
|
|
71
|
-
}
|
|
72
|
-
for (const key of Object.keys(value)) {
|
|
73
|
-
const val = value[key];
|
|
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 of Object.keys(value)) {
|
|
92
|
-
const val = value[key];
|
|
93
|
-
if (key === "$ref" && typeof val === "string") {
|
|
94
|
-
setOwn(result, key, map(val, [...path, key]));
|
|
95
|
-
} else if (!schemaLevel) {
|
|
96
|
-
setOwn(result, key, mapJsonSchemaRefs(val, map, true, [...path, key]));
|
|
97
|
-
} else if (JSON_SCHEMA_LOGIC_KEYWORDS.has(key) || JSON_SCHEMA_RECORD_KEYWORDS.has(key)) {
|
|
98
|
-
setOwn(result, key, mapJsonSchemaRefs(val, map, !JSON_SCHEMA_RECORD_KEYWORDS.has(key), [...path, key]));
|
|
99
|
-
} else {
|
|
100
|
-
setOwn(result, key, val);
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
return result;
|
|
104
|
-
}
|
|
105
|
-
function hoistRecursiveRefToDef(schema) {
|
|
106
|
-
if (typeof schema !== "object") {
|
|
107
|
-
return schema;
|
|
108
|
-
}
|
|
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);
|
|
113
|
-
});
|
|
114
|
-
if (!hasRootRef) {
|
|
115
|
-
return schema;
|
|
116
|
-
}
|
|
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);
|
|
120
|
-
return {
|
|
121
|
-
$ref: defRef,
|
|
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
|
-
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
|
-
};
|
|
154
|
-
}
|
|
155
|
-
return current;
|
|
156
|
-
}
|
|
157
|
-
function findRecursiveJsonSchemaDefName(defs) {
|
|
158
|
-
let index = 0;
|
|
159
|
-
while (defs?.[`__schema${index}`] !== void 0) {
|
|
160
|
-
index++;
|
|
161
|
-
}
|
|
162
|
-
return `__schema${index}`;
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
var JsonSchemaType = /* @__PURE__ */ ((JsonSchemaType2) => {
|
|
166
|
-
JsonSchemaType2["Array"] = "array";
|
|
167
|
-
JsonSchemaType2["Boolean"] = "boolean";
|
|
168
|
-
JsonSchemaType2["Integer"] = "integer";
|
|
169
|
-
JsonSchemaType2["Null"] = "null";
|
|
170
|
-
JsonSchemaType2["Number"] = "number";
|
|
171
|
-
JsonSchemaType2["Object"] = "object";
|
|
172
|
-
JsonSchemaType2["String"] = "string";
|
|
173
|
-
return JsonSchemaType2;
|
|
174
|
-
})(JsonSchemaType || {});
|
|
175
|
-
var JsonSchemaFormat = /* @__PURE__ */ ((JsonSchemaFormat2) => {
|
|
176
|
-
JsonSchemaFormat2["Date"] = "date";
|
|
177
|
-
JsonSchemaFormat2["DateTime"] = "date-time";
|
|
178
|
-
JsonSchemaFormat2["Duration"] = "duration";
|
|
179
|
-
JsonSchemaFormat2["Email"] = "email";
|
|
180
|
-
JsonSchemaFormat2["Hostname"] = "hostname";
|
|
181
|
-
JsonSchemaFormat2["IDNEmail"] = "idn-email";
|
|
182
|
-
JsonSchemaFormat2["IDNHostname"] = "idn-hostname";
|
|
183
|
-
JsonSchemaFormat2["IPv4"] = "ipv4";
|
|
184
|
-
JsonSchemaFormat2["IPv6"] = "ipv6";
|
|
185
|
-
JsonSchemaFormat2["IRI"] = "iri";
|
|
186
|
-
JsonSchemaFormat2["IRIReference"] = "iri-reference";
|
|
187
|
-
JsonSchemaFormat2["JSONPointer"] = "json-pointer";
|
|
188
|
-
JsonSchemaFormat2["RegEx"] = "regex";
|
|
189
|
-
JsonSchemaFormat2["RelativeJSONPointer"] = "relative-json-pointer";
|
|
190
|
-
JsonSchemaFormat2["Time"] = "time";
|
|
191
|
-
JsonSchemaFormat2["URI"] = "uri";
|
|
192
|
-
JsonSchemaFormat2["URIReference"] = "uri-reference";
|
|
193
|
-
JsonSchemaFormat2["URITemplate"] = "uri-template";
|
|
194
|
-
JsonSchemaFormat2["UUID"] = "uuid";
|
|
195
|
-
return JsonSchemaFormat2;
|
|
196
|
-
})(JsonSchemaFormat || {});
|
|
197
|
-
var JsonSchemaXNativeType = /* @__PURE__ */ ((JsonSchemaXNativeType2) => {
|
|
198
|
-
JsonSchemaXNativeType2["BigInt"] = "bigint";
|
|
199
|
-
JsonSchemaXNativeType2["Date"] = "date";
|
|
200
|
-
JsonSchemaXNativeType2["Url"] = "url";
|
|
201
|
-
JsonSchemaXNativeType2["Set"] = "set";
|
|
202
|
-
JsonSchemaXNativeType2["Map"] = "map";
|
|
203
|
-
return JsonSchemaXNativeType2;
|
|
204
|
-
})(JsonSchemaXNativeType || {});
|
|
205
|
-
|
|
206
|
-
const UNSATISFIED = 0;
|
|
207
|
-
const LOOSELY_SATISFIED = 1;
|
|
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, { maxBigIntDigits }) => typeof value === "string" ? stringToBigInt(value, maxBigIntDigits) : 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
|
-
]);
|
|
238
|
-
function minSatisfaction(a, b) {
|
|
239
|
-
return a < b ? a : b;
|
|
240
|
-
}
|
|
241
|
-
const MIN_BUDGETED_VALUES = 100;
|
|
242
|
-
function createContext() {
|
|
243
|
-
return { rootSchema: true, memo: void 0, memoize: false, steps: 0, maxSteps: 0, input: void 0 };
|
|
244
|
-
}
|
|
245
|
-
class CoercionBudgetExceededError extends Error {
|
|
246
|
-
}
|
|
247
|
-
class JsonSchemaCoercer {
|
|
248
|
-
nativeCoerceOptions;
|
|
249
|
-
maxStepsPerValue;
|
|
250
|
-
/** Reused between calls, since building a context is slow until V8 warms up. */
|
|
251
|
-
idleContext = createContext();
|
|
252
|
-
constructor(options = {}) {
|
|
253
|
-
this.nativeCoerceOptions = {
|
|
254
|
-
maxBigIntDigits: options.maxBigIntDigits ?? DEFAULT_MAX_BIGINT_DIGITS
|
|
255
|
-
};
|
|
256
|
-
this.maxStepsPerValue = options.maxStepsPerValue ?? 1e3;
|
|
257
|
-
}
|
|
258
|
-
coerce([schema, optional], value) {
|
|
259
|
-
if (optional && value === void 0) {
|
|
260
|
-
return value;
|
|
261
|
-
}
|
|
262
|
-
const context = this.idleContext ?? createContext();
|
|
263
|
-
this.idleContext = void 0;
|
|
264
|
-
context.rootSchema = schema;
|
|
265
|
-
context.memoize = false;
|
|
266
|
-
context.steps = 0;
|
|
267
|
-
context.maxSteps = this.maxStepsPerValue * MIN_BUDGETED_VALUES;
|
|
268
|
-
context.input = value;
|
|
269
|
-
try {
|
|
270
|
-
const [, coerced] = this.coerceInternal(context, schema, value);
|
|
271
|
-
return coerced;
|
|
272
|
-
} catch (error) {
|
|
273
|
-
if (error instanceof CoercionBudgetExceededError) {
|
|
274
|
-
return value;
|
|
275
|
-
}
|
|
276
|
-
throw error;
|
|
277
|
-
} finally {
|
|
278
|
-
context.memo = void 0;
|
|
279
|
-
context.input = void 0;
|
|
280
|
-
this.idleContext = context;
|
|
281
|
-
}
|
|
282
|
-
}
|
|
283
|
-
coerceInternal(context, schema, value, appliedRefs) {
|
|
284
|
-
if (++context.steps > context.maxSteps) {
|
|
285
|
-
this.extendBudget(context);
|
|
286
|
-
}
|
|
287
|
-
if (typeof schema === "boolean") {
|
|
288
|
-
return [schema ? SATISFIED : UNSATISFIED, value];
|
|
289
|
-
}
|
|
290
|
-
if (Array.isArray(schema.type)) {
|
|
291
|
-
return this.coerceInternal(
|
|
292
|
-
context,
|
|
293
|
-
{ anyOf: schema.type.map((type) => ({ ...schema, type })) },
|
|
294
|
-
value,
|
|
295
|
-
appliedRefs
|
|
296
|
-
);
|
|
297
|
-
}
|
|
298
|
-
let coerced = value;
|
|
299
|
-
let satisfied = SATISFIED;
|
|
300
|
-
if (typeof schema.$ref === "string") {
|
|
301
|
-
const resolved = schema.$ref.startsWith("#/") ? get(context.rootSchema, schema.$ref.slice("#/".length).split("/").map(decodeJsonPointerSegment)) : schema.$ref === "#" ? context.rootSchema : void 0;
|
|
302
|
-
if (resolved !== void 0 && !appliedRefs?.has(resolved)) {
|
|
303
|
-
if (schema.type === "object" || schema.type === "array") {
|
|
304
|
-
context.memoize = true;
|
|
305
|
-
}
|
|
306
|
-
const [subSatisfied, subCoerced] = this.coerceRef(context, resolved, coerced, appliedRefs);
|
|
307
|
-
coerced = subCoerced;
|
|
308
|
-
satisfied = minSatisfaction(satisfied, subSatisfied);
|
|
309
|
-
}
|
|
310
|
-
}
|
|
311
|
-
const enumValues = schema.const !== void 0 ? [schema.const] : schema.enum;
|
|
312
|
-
if (enumValues !== void 0 && !enumValues.includes(coerced)) {
|
|
313
|
-
if (typeof coerced === "string") {
|
|
314
|
-
const numberValue = stringToNumber(coerced);
|
|
315
|
-
if (enumValues.includes(numberValue)) {
|
|
316
|
-
coerced = numberValue;
|
|
317
|
-
} else {
|
|
318
|
-
const booleanValue = stringToBoolean(coerced);
|
|
319
|
-
if (enumValues.includes(booleanValue)) {
|
|
320
|
-
coerced = booleanValue;
|
|
321
|
-
} else {
|
|
322
|
-
satisfied = UNSATISFIED;
|
|
323
|
-
}
|
|
324
|
-
}
|
|
325
|
-
} else {
|
|
326
|
-
satisfied = UNSATISFIED;
|
|
327
|
-
}
|
|
328
|
-
}
|
|
329
|
-
const declaredNativeType = NATIVE_TYPES.get(schema["x-native-type"]);
|
|
330
|
-
const nativeType = schema.type === void 0 || declaredNativeType?.types.includes(schema.type) ? declaredNativeType : void 0;
|
|
331
|
-
if (nativeType && !nativeType.afterTypeCheck) {
|
|
332
|
-
coerced = nativeType.coerce(coerced, this.nativeCoerceOptions);
|
|
333
|
-
}
|
|
334
|
-
if (schema.type && !nativeType?.is(coerced)) {
|
|
335
|
-
switch (schema.type) {
|
|
336
|
-
case "null": {
|
|
337
|
-
if (coerced !== null) {
|
|
338
|
-
satisfied = UNSATISFIED;
|
|
339
|
-
}
|
|
340
|
-
break;
|
|
341
|
-
}
|
|
342
|
-
case "string": {
|
|
343
|
-
if (typeof coerced !== "string") {
|
|
344
|
-
satisfied = UNSATISFIED;
|
|
345
|
-
}
|
|
346
|
-
break;
|
|
347
|
-
}
|
|
348
|
-
case "number": {
|
|
349
|
-
if (typeof coerced === "string") {
|
|
350
|
-
coerced = stringToNumber(coerced);
|
|
351
|
-
}
|
|
352
|
-
if (typeof coerced !== "number") {
|
|
353
|
-
satisfied = UNSATISFIED;
|
|
354
|
-
}
|
|
355
|
-
break;
|
|
356
|
-
}
|
|
357
|
-
case "integer": {
|
|
358
|
-
if (typeof coerced === "string") {
|
|
359
|
-
coerced = stringToInteger(coerced);
|
|
360
|
-
}
|
|
361
|
-
if (typeof coerced !== "number" || !Number.isInteger(coerced)) {
|
|
362
|
-
satisfied = UNSATISFIED;
|
|
363
|
-
}
|
|
364
|
-
break;
|
|
365
|
-
}
|
|
366
|
-
case "boolean": {
|
|
367
|
-
if (typeof coerced === "string") {
|
|
368
|
-
coerced = stringToBoolean(coerced);
|
|
369
|
-
}
|
|
370
|
-
if (typeof coerced !== "boolean") {
|
|
371
|
-
satisfied = UNSATISFIED;
|
|
372
|
-
}
|
|
373
|
-
break;
|
|
374
|
-
}
|
|
375
|
-
case "array": {
|
|
376
|
-
if (Array.isArray(coerced)) {
|
|
377
|
-
const prefixItemSchemas = "prefixItems" in schema ? toArray(schema.prefixItems) : Array.isArray(schema.items) ? schema.items : [];
|
|
378
|
-
const itemSchema = Array.isArray(schema.items) ? schema.additionalItems : schema.items;
|
|
379
|
-
let shouldUseCoercedItems = false;
|
|
380
|
-
const coercedItems = coerced.map((item, i) => {
|
|
381
|
-
const subSchema = prefixItemSchemas[i] ?? itemSchema;
|
|
382
|
-
if (subSchema === void 0) {
|
|
383
|
-
satisfied = minSatisfaction(satisfied, LOOSELY_SATISFIED);
|
|
384
|
-
return item;
|
|
385
|
-
}
|
|
386
|
-
const [subSatisfied, subCoerced] = this.coerceInternal(context, subSchema, item);
|
|
387
|
-
satisfied = minSatisfaction(satisfied, subSatisfied);
|
|
388
|
-
if (subCoerced !== item) {
|
|
389
|
-
shouldUseCoercedItems = true;
|
|
390
|
-
}
|
|
391
|
-
return subCoerced;
|
|
392
|
-
});
|
|
393
|
-
if (typeof schema.minItems === "number" && coercedItems.length < schema.minItems) {
|
|
394
|
-
satisfied = UNSATISFIED;
|
|
395
|
-
}
|
|
396
|
-
if (shouldUseCoercedItems) {
|
|
397
|
-
coerced = coercedItems;
|
|
398
|
-
}
|
|
399
|
-
} else {
|
|
400
|
-
satisfied = UNSATISFIED;
|
|
401
|
-
}
|
|
402
|
-
break;
|
|
403
|
-
}
|
|
404
|
-
case "object": {
|
|
405
|
-
if (Array.isArray(coerced)) {
|
|
406
|
-
coerced = { ...coerced };
|
|
407
|
-
}
|
|
408
|
-
if (isPlainObject(coerced)) {
|
|
409
|
-
let shouldUseCoercedItems = false;
|
|
410
|
-
const coercedItems = { ...coerced };
|
|
411
|
-
const patternProperties = Object.entries(schema.patternProperties ?? {}).flatMap(([key, value2]) => {
|
|
412
|
-
const pattern = tryOrUndefined(() => new RegExp(key));
|
|
413
|
-
return pattern ? [[pattern, value2]] : [];
|
|
414
|
-
});
|
|
415
|
-
const propertySchemas = schema.properties ?? {};
|
|
416
|
-
for (const key of Object.keys(coerced)) {
|
|
417
|
-
const value2 = coerced[key];
|
|
418
|
-
const subSchema = getOwn(propertySchemas, key) ?? patternProperties.find(([pattern]) => pattern.test(key))?.[1] ?? schema.additionalProperties;
|
|
419
|
-
if (value2 !== void 0 || schema.required?.includes(key)) {
|
|
420
|
-
if (subSchema === void 0) {
|
|
421
|
-
satisfied = minSatisfaction(satisfied, LOOSELY_SATISFIED);
|
|
422
|
-
} else {
|
|
423
|
-
const [subSatisfied, subCoerced] = this.coerceInternal(context, subSchema, value2);
|
|
424
|
-
coercedItems[key] = subCoerced;
|
|
425
|
-
satisfied = minSatisfaction(satisfied, subSatisfied);
|
|
426
|
-
if (subCoerced !== value2) {
|
|
427
|
-
shouldUseCoercedItems = true;
|
|
428
|
-
}
|
|
429
|
-
}
|
|
430
|
-
}
|
|
431
|
-
}
|
|
432
|
-
if (schema.required?.some((key) => !Object.hasOwn(coercedItems, key))) {
|
|
433
|
-
satisfied = UNSATISFIED;
|
|
434
|
-
}
|
|
435
|
-
if (shouldUseCoercedItems) {
|
|
436
|
-
coerced = coercedItems;
|
|
437
|
-
}
|
|
438
|
-
} else {
|
|
439
|
-
satisfied = UNSATISFIED;
|
|
440
|
-
}
|
|
441
|
-
break;
|
|
442
|
-
}
|
|
443
|
-
}
|
|
444
|
-
}
|
|
445
|
-
if (nativeType) {
|
|
446
|
-
if (nativeType.afterTypeCheck) {
|
|
447
|
-
coerced = nativeType.coerce(coerced, this.nativeCoerceOptions);
|
|
448
|
-
}
|
|
449
|
-
if (!nativeType.is(coerced)) {
|
|
450
|
-
satisfied = UNSATISFIED;
|
|
451
|
-
}
|
|
452
|
-
}
|
|
453
|
-
if (schema.allOf) {
|
|
454
|
-
context.memoize = true;
|
|
455
|
-
for (const subSchema of schema.allOf) {
|
|
456
|
-
const [subSatisfied, subCoerced] = this.coerceInternal(context, subSchema, coerced, appliedRefs);
|
|
457
|
-
coerced = subCoerced;
|
|
458
|
-
satisfied = minSatisfaction(satisfied, subSatisfied);
|
|
459
|
-
}
|
|
460
|
-
}
|
|
461
|
-
for (const key of ["anyOf", "oneOf"]) {
|
|
462
|
-
if (schema[key]) {
|
|
463
|
-
context.memoize = true;
|
|
464
|
-
let best;
|
|
465
|
-
for (const subSchema of schema[key]) {
|
|
466
|
-
const [subSatisfied, subCoerced] = this.coerceInternal(context, subSchema, coerced, appliedRefs);
|
|
467
|
-
if (subSatisfied === UNSATISFIED) {
|
|
468
|
-
continue;
|
|
469
|
-
}
|
|
470
|
-
if (subCoerced === coerced) {
|
|
471
|
-
best = { satisfied: subSatisfied, value: subCoerced };
|
|
472
|
-
break;
|
|
473
|
-
}
|
|
474
|
-
if (!best || subSatisfied > best.satisfied) {
|
|
475
|
-
best = { satisfied: subSatisfied, value: subCoerced };
|
|
476
|
-
}
|
|
477
|
-
}
|
|
478
|
-
if (best) {
|
|
479
|
-
coerced = best.value;
|
|
480
|
-
satisfied = minSatisfaction(satisfied, best.satisfied);
|
|
481
|
-
} else {
|
|
482
|
-
satisfied = UNSATISFIED;
|
|
483
|
-
}
|
|
484
|
-
}
|
|
485
|
-
}
|
|
486
|
-
if (typeof schema.not !== "undefined") {
|
|
487
|
-
context.memoize = true;
|
|
488
|
-
const [notSatisfied] = this.coerceInternal(context, schema.not, coerced, appliedRefs);
|
|
489
|
-
if (notSatisfied !== UNSATISFIED) {
|
|
490
|
-
satisfied = UNSATISFIED;
|
|
491
|
-
}
|
|
492
|
-
}
|
|
493
|
-
return [satisfied, coerced];
|
|
494
|
-
}
|
|
495
|
-
coerceRef(context, resolved, value, appliedRefs) {
|
|
496
|
-
let memo;
|
|
497
|
-
if (context.memoize && !appliedRefs?.size && isTypescriptObject(value)) {
|
|
498
|
-
context.memo ??= /* @__PURE__ */ new Map();
|
|
499
|
-
memo = context.memo.get(resolved);
|
|
500
|
-
if (!memo) {
|
|
501
|
-
memo = /* @__PURE__ */ new Map();
|
|
502
|
-
context.memo.set(resolved, memo);
|
|
503
|
-
}
|
|
504
|
-
const cached = memo.get(value);
|
|
505
|
-
if (cached) {
|
|
506
|
-
return cached;
|
|
507
|
-
}
|
|
508
|
-
}
|
|
509
|
-
appliedRefs ??= /* @__PURE__ */ new Set();
|
|
510
|
-
appliedRefs.add(resolved);
|
|
511
|
-
const result = this.coerceInternal(context, resolved, value, appliedRefs);
|
|
512
|
-
appliedRefs.delete(resolved);
|
|
513
|
-
memo?.set(value, result);
|
|
514
|
-
return result;
|
|
515
|
-
}
|
|
516
|
-
extendBudget(context) {
|
|
517
|
-
const limit = 2 * context.steps / this.maxStepsPerValue;
|
|
518
|
-
context.maxSteps = this.maxStepsPerValue * Math.max(countValues(context.input, limit), MIN_BUDGETED_VALUES);
|
|
519
|
-
if (context.steps > context.maxSteps) {
|
|
520
|
-
throw new CoercionBudgetExceededError();
|
|
521
|
-
}
|
|
522
|
-
}
|
|
523
|
-
}
|
|
524
|
-
function countValues(input, limit) {
|
|
525
|
-
const walked = /* @__PURE__ */ new Set();
|
|
526
|
-
const stack = [input];
|
|
527
|
-
let count = 1;
|
|
528
|
-
while (stack.length) {
|
|
529
|
-
const value = stack.pop();
|
|
530
|
-
if ((Array.isArray(value) || isPlainObject(value)) && !walked.has(value)) {
|
|
531
|
-
walked.add(value);
|
|
532
|
-
const items = Array.isArray(value) ? value : Object.values(value);
|
|
533
|
-
for (let i = 0; i < items.length; i++) {
|
|
534
|
-
if (++count >= limit) {
|
|
535
|
-
return count;
|
|
536
|
-
}
|
|
537
|
-
const item = items[i];
|
|
538
|
-
if (isTypescriptObject(item)) {
|
|
539
|
-
stack.push(item);
|
|
540
|
-
}
|
|
541
|
-
}
|
|
542
|
-
}
|
|
543
|
-
}
|
|
544
|
-
return count;
|
|
545
|
-
}
|
|
546
|
-
const NUMBER_PATTERN = /^-?(?:0|[1-9]\d*)(?:\.\d+)?$/;
|
|
547
|
-
function stringToNumber(value) {
|
|
548
|
-
if (!NUMBER_PATTERN.test(value)) {
|
|
549
|
-
return value;
|
|
550
|
-
}
|
|
551
|
-
const num = Number(value);
|
|
552
|
-
if (num < Number.MIN_SAFE_INTEGER || num > Number.MAX_SAFE_INTEGER) {
|
|
553
|
-
return value;
|
|
554
|
-
}
|
|
555
|
-
return num;
|
|
556
|
-
}
|
|
557
|
-
const INTEGER_PATTERN = /^-?(?:0|[1-9]\d*)$/;
|
|
558
|
-
function stringToInteger(value) {
|
|
559
|
-
if (!INTEGER_PATTERN.test(value)) {
|
|
560
|
-
return value;
|
|
561
|
-
}
|
|
562
|
-
const num = Number(value);
|
|
563
|
-
if (!Number.isSafeInteger(num)) {
|
|
564
|
-
return value;
|
|
565
|
-
}
|
|
566
|
-
return num;
|
|
567
|
-
}
|
|
568
|
-
function stringToBigInt(value, maxDigits) {
|
|
569
|
-
return parseBigIntString(value, maxDigits) ?? value;
|
|
570
|
-
}
|
|
571
|
-
function numberToBigInt(value) {
|
|
572
|
-
if (!Number.isInteger(value)) {
|
|
573
|
-
return value;
|
|
574
|
-
}
|
|
575
|
-
return BigInt(value);
|
|
576
|
-
}
|
|
577
|
-
function stringToBoolean(value) {
|
|
578
|
-
const lower = value.toLowerCase();
|
|
579
|
-
if (lower === "false" || lower === "off") {
|
|
580
|
-
return false;
|
|
581
|
-
}
|
|
582
|
-
if (lower === "true" || lower === "on") {
|
|
583
|
-
return true;
|
|
584
|
-
}
|
|
585
|
-
return value;
|
|
586
|
-
}
|
|
587
|
-
const DATE_TIME_PATTERN = /^(?!-0{6})(?:[+-]\d{2})?\d{4}-\d{2}-\d{2}(?:[T ]\d{2}:\d{2}(?::\d{2}(?:\.\d+)?)?(?:Z|[+-]\d{2}:?\d{2})?)?$/;
|
|
588
|
-
const DAYS_IN_MONTH = [31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
|
|
589
|
-
function stringToDate(value) {
|
|
590
|
-
if (!DATE_TIME_PATTERN.test(value)) {
|
|
591
|
-
return value;
|
|
592
|
-
}
|
|
593
|
-
const yearEnd = value.indexOf("-", 1);
|
|
594
|
-
const month = readTwoDigits(value, yearEnd + 1);
|
|
595
|
-
const day = readTwoDigits(value, yearEnd + 4);
|
|
596
|
-
if (day < 1 || day > (DAYS_IN_MONTH[month - 1] ?? 0) || month === 2 && day === 29 && !isLeapYear(Number(value.slice(0, yearEnd)))) {
|
|
597
|
-
return value;
|
|
598
|
-
}
|
|
599
|
-
const date = new Date(value);
|
|
600
|
-
if (Number.isNaN(date.getTime())) {
|
|
601
|
-
return value;
|
|
602
|
-
}
|
|
603
|
-
return date;
|
|
604
|
-
}
|
|
605
|
-
function readTwoDigits(value, index) {
|
|
606
|
-
return (value.charCodeAt(index) - 48) * 10 + value.charCodeAt(index + 1) - 48;
|
|
607
|
-
}
|
|
608
|
-
function isLeapYear(year) {
|
|
609
|
-
return year % 4 === 0 && (year % 100 !== 0 || year % 400 === 0);
|
|
610
|
-
}
|
|
611
|
-
function stringToURL(value) {
|
|
612
|
-
return tryOrUndefined(() => new URL(value)) ?? value;
|
|
613
|
-
}
|
|
614
|
-
function arrayToSet(value) {
|
|
615
|
-
const set = new Set(value);
|
|
616
|
-
if (set.size !== value.length) {
|
|
617
|
-
return value;
|
|
618
|
-
}
|
|
619
|
-
return set;
|
|
620
|
-
}
|
|
621
|
-
function arrayToMap(value) {
|
|
622
|
-
if (value.some((item) => !Array.isArray(item) || item.length !== 2)) {
|
|
623
|
-
return value;
|
|
624
|
-
}
|
|
625
|
-
const result = new Map(value);
|
|
626
|
-
if (result.size !== value.length) {
|
|
627
|
-
return value;
|
|
628
|
-
}
|
|
629
|
-
return result;
|
|
630
|
-
}
|
|
631
|
-
|
|
632
|
-
function isJsonPrimitiveSchema(schema) {
|
|
633
|
-
if (typeof schema === "boolean") {
|
|
634
|
-
return false;
|
|
635
|
-
}
|
|
636
|
-
if (typeof schema.type === "string" && JSON_SCHEMA_PRIMITIVE_TYPES.has(schema.type)) {
|
|
637
|
-
return true;
|
|
638
|
-
}
|
|
639
|
-
if (schema.const !== void 0) {
|
|
640
|
-
return true;
|
|
641
|
-
}
|
|
642
|
-
if (schema.enum !== void 0) {
|
|
643
|
-
return true;
|
|
644
|
-
}
|
|
645
|
-
return false;
|
|
646
|
-
}
|
|
647
|
-
function isJsonFileSchema(schema) {
|
|
648
|
-
return typeof schema !== "boolean" && schema.type === "string" && (typeof schema.contentMediaType === "string" || schema.format === "binary" || schema.contentEncoding === "binary");
|
|
649
|
-
}
|
|
650
|
-
function containsJsonFileSchema(schema) {
|
|
651
|
-
return findDeepMatches((v) => isPlainObject(v) && isJsonFileSchema(v), schema).values.length > 0;
|
|
652
|
-
}
|
|
653
|
-
function isJsonObjectSchema(schema) {
|
|
654
|
-
return typeof schema !== "boolean" && schema.type === "object";
|
|
655
|
-
}
|
|
656
|
-
function isJsonArraySchema(schema) {
|
|
657
|
-
return typeof schema !== "boolean" && schema.type === "array";
|
|
658
|
-
}
|
|
659
|
-
function isUnconstrainedSchema(schema) {
|
|
660
|
-
if (typeof schema === "boolean") {
|
|
661
|
-
return schema;
|
|
662
|
-
}
|
|
663
|
-
if (Object.keys(schema).every((k) => !JSON_SCHEMA_LOGIC_KEYWORDS.has(k))) {
|
|
664
|
-
return true;
|
|
665
|
-
}
|
|
666
|
-
return false;
|
|
667
|
-
}
|
|
668
|
-
function ensureJsonSchemaObject(schema) {
|
|
669
|
-
if (typeof schema === "boolean") {
|
|
670
|
-
return schema ? {} : { not: {} };
|
|
671
|
-
}
|
|
672
|
-
return schema;
|
|
673
|
-
}
|
|
674
|
-
|
|
675
|
-
function promoteJsonSchemaDefs(schema, mergedDefs, basePath) {
|
|
676
|
-
const { $defs, ...rootBody } = schema;
|
|
677
|
-
const renameMap = /* @__PURE__ */ new Map();
|
|
678
|
-
const mapRef = (ref) => {
|
|
679
|
-
if (ref === "#") {
|
|
680
|
-
return basePath;
|
|
681
|
-
}
|
|
682
|
-
if (ref.startsWith("#/$defs/")) {
|
|
683
|
-
const [encodedName, ...segments] = ref.slice("#/$defs/".length).split("/");
|
|
684
|
-
const newName = renameMap.get(decodeJsonPointerSegment(encodedName));
|
|
685
|
-
return newName === void 0 ? ref : ["#/$defs", encodeJsonPointerSegment(newName), ...segments].join("/");
|
|
686
|
-
}
|
|
687
|
-
if (ref.startsWith("#/") && get(rootBody, ref.slice(2).split("/").map(decodeJsonPointerSegment)) !== void 0) {
|
|
688
|
-
return `${basePath}/${ref.slice(2)}`;
|
|
689
|
-
}
|
|
690
|
-
return ref;
|
|
691
|
-
};
|
|
692
|
-
const defs = Object.entries($defs ?? {}).filter(([, def]) => def !== void 0);
|
|
693
|
-
if (defs.length > 0) {
|
|
694
|
-
const promoted = /* @__PURE__ */ new Map();
|
|
695
|
-
const reserved = /* @__PURE__ */ new Set([...mergedDefs.keys(), ...defs.map(([name]) => name)]);
|
|
696
|
-
for (let changed = true; changed; ) {
|
|
697
|
-
changed = false;
|
|
698
|
-
for (const [name, def] of defs) {
|
|
699
|
-
const body = mapJsonSchemaRefs(def, mapRef);
|
|
700
|
-
promoted.set(name, body);
|
|
701
|
-
if (renameMap.has(name) || !mergedDefs.has(name) || isDeepEqual(mergedDefs.get(name), body)) {
|
|
702
|
-
continue;
|
|
703
|
-
}
|
|
704
|
-
let counter = 2;
|
|
705
|
-
while (reserved.has(`${name}${counter}`)) {
|
|
706
|
-
counter++;
|
|
707
|
-
}
|
|
708
|
-
reserved.add(`${name}${counter}`);
|
|
709
|
-
renameMap.set(name, `${name}${counter}`);
|
|
710
|
-
changed = true;
|
|
711
|
-
}
|
|
712
|
-
}
|
|
713
|
-
for (const [name, body] of promoted) {
|
|
714
|
-
mergedDefs.set(renameMap.get(name) ?? name, body);
|
|
715
|
-
}
|
|
716
|
-
}
|
|
717
|
-
return mapJsonSchemaRefs(rootBody, mapRef);
|
|
718
|
-
}
|
|
719
|
-
function combineJsonSchemasWithComposition(keyword, schemas) {
|
|
720
|
-
if (schemas.length <= 1) {
|
|
721
|
-
return schemas[0] ?? true;
|
|
722
|
-
}
|
|
723
|
-
const mergedDefs = /* @__PURE__ */ new Map();
|
|
724
|
-
const compositionBranches = [];
|
|
725
|
-
for (let i = 0; i < schemas.length; i++) {
|
|
726
|
-
const schema = schemas[i];
|
|
727
|
-
compositionBranches.push(typeof schema === "boolean" ? schema : promoteJsonSchemaDefs(schema, mergedDefs, `#/${keyword}/${i}`));
|
|
728
|
-
}
|
|
729
|
-
const result = { [keyword]: compositionBranches };
|
|
730
|
-
if (mergedDefs.size > 0) {
|
|
731
|
-
result.$defs = Object.fromEntries(mergedDefs);
|
|
732
|
-
}
|
|
733
|
-
return result;
|
|
734
|
-
}
|
|
735
|
-
function combineJsonObjectSchemaEntries(entries) {
|
|
736
|
-
const properties = /* @__PURE__ */ new Map();
|
|
737
|
-
const required = [];
|
|
738
|
-
const mergedDefs = /* @__PURE__ */ new Map();
|
|
739
|
-
for (const [name, propertySchema, optional] of entries) {
|
|
740
|
-
if (!optional) {
|
|
741
|
-
required.push(name);
|
|
742
|
-
}
|
|
743
|
-
properties.set(name, typeof propertySchema === "boolean" ? propertySchema : promoteJsonSchemaDefs(propertySchema, mergedDefs, `#/properties/${encodeJsonPointerSegment(name)}`));
|
|
744
|
-
}
|
|
745
|
-
const schema = {
|
|
746
|
-
type: "object",
|
|
747
|
-
properties: Object.fromEntries(properties)
|
|
748
|
-
};
|
|
749
|
-
if (required.length > 0) {
|
|
750
|
-
schema.required = required;
|
|
751
|
-
}
|
|
752
|
-
if (mergedDefs.size > 0) {
|
|
753
|
-
schema.$defs = Object.fromEntries(mergedDefs);
|
|
754
|
-
}
|
|
755
|
-
return schema;
|
|
756
|
-
}
|
|
757
|
-
function extractJsonObjectSchemaEntries(schema) {
|
|
758
|
-
schema = hoistRecursiveRefToDef(schema);
|
|
759
|
-
if (typeof schema !== "object") {
|
|
760
|
-
return void 0;
|
|
761
|
-
}
|
|
762
|
-
const result = extractJsonObjectSchemaEntriesInternal(omit(schema, ["$defs"]), schema.$defs, /* @__PURE__ */ new Set());
|
|
763
|
-
if (!result.objectLike) {
|
|
764
|
-
return void 0;
|
|
765
|
-
}
|
|
766
|
-
return result.entries.map(([n, s, ...r]) => [n, withRootDefs(s, schema.$defs), ...r]);
|
|
767
|
-
}
|
|
768
|
-
const JSON_SCHEMA_OBJECT_KEYWORDS = ["properties", "patternProperties", "additionalProperties", "required", "propertyNames", "minProperties", "maxProperties"];
|
|
769
|
-
function isJsonObjectLikeSchema(schema) {
|
|
770
|
-
return isJsonObjectLikeSchemaInternal(schema, typeof schema === "object" ? schema.$defs : void 0, /* @__PURE__ */ new Set());
|
|
771
|
-
}
|
|
772
|
-
function isJsonObjectLikeSchemaInternal(schema, $defs, followedRefs) {
|
|
773
|
-
if (typeof schema === "object" && typeof schema.$ref === "string") {
|
|
774
|
-
if (followedRefs.has(schema.$ref)) {
|
|
775
|
-
return false;
|
|
776
|
-
}
|
|
777
|
-
followedRefs = new Set(followedRefs).add(schema.$ref);
|
|
778
|
-
}
|
|
779
|
-
const resolved = resolveJsonSchemaRootLocalRef(schema, $defs);
|
|
780
|
-
if (typeof resolved === "boolean") {
|
|
781
|
-
return true;
|
|
782
|
-
}
|
|
783
|
-
if (resolved.type !== void 0) {
|
|
784
|
-
return resolved.type === "object";
|
|
785
|
-
}
|
|
786
|
-
if (JSON_SCHEMA_OBJECT_KEYWORDS.some((keyword) => keyword in resolved)) {
|
|
787
|
-
return true;
|
|
788
|
-
}
|
|
789
|
-
for (const keyword of ["allOf", "anyOf", "oneOf"]) {
|
|
790
|
-
const members = resolved[keyword];
|
|
791
|
-
if (Array.isArray(members) && members.length > 0) {
|
|
792
|
-
return members.every((member) => isJsonObjectLikeSchemaInternal(member, $defs, followedRefs));
|
|
793
|
-
}
|
|
794
|
-
}
|
|
795
|
-
return isUnconstrainedSchema(resolved);
|
|
796
|
-
}
|
|
797
|
-
function extractJsonObjectSchemaEntriesInternal(schema, $defs, resolvingRefs) {
|
|
798
|
-
if (typeof schema !== "object") {
|
|
799
|
-
return { entries: [], objectLike: false };
|
|
800
|
-
}
|
|
801
|
-
if (typeof schema.$ref === "string") {
|
|
802
|
-
if (resolvingRefs.has(schema.$ref)) {
|
|
803
|
-
return { entries: [], objectLike: true };
|
|
804
|
-
}
|
|
805
|
-
const resolved = resolveJsonSchemaRootLocalRef(schema, $defs);
|
|
806
|
-
if (resolved !== schema) {
|
|
807
|
-
return extractJsonObjectSchemaEntriesInternal(resolved, $defs, new Set(resolvingRefs).add(schema.$ref));
|
|
808
|
-
}
|
|
809
|
-
}
|
|
810
|
-
const sources = [];
|
|
811
|
-
if (schema.properties) {
|
|
812
|
-
sources.push({
|
|
813
|
-
entries: Object.entries(schema.properties).map(([name, propertySchema]) => {
|
|
814
|
-
return [
|
|
815
|
-
name,
|
|
816
|
-
propertySchema,
|
|
817
|
-
!schema.required?.includes(name)
|
|
818
|
-
];
|
|
819
|
-
}),
|
|
820
|
-
source: "direct"
|
|
821
|
-
});
|
|
822
|
-
}
|
|
823
|
-
let objectLike = schema.type === "object" || schema.properties !== void 0 || schema.required !== void 0 || schema.additionalProperties !== void 0;
|
|
824
|
-
for (const keyword of ["anyOf", "oneOf", "allOf"]) {
|
|
825
|
-
const branches = schema[keyword];
|
|
826
|
-
if (branches === void 0) {
|
|
827
|
-
continue;
|
|
828
|
-
}
|
|
829
|
-
const branchResults = branches.map((branch) => extractJsonObjectSchemaEntriesInternal(branch, $defs, resolvingRefs));
|
|
830
|
-
if (branchResults.some((result) => !result.objectLike)) {
|
|
831
|
-
return { entries: [], objectLike: false };
|
|
832
|
-
}
|
|
833
|
-
const entriesByName = /* @__PURE__ */ new Map();
|
|
834
|
-
for (const result of branchResults) {
|
|
835
|
-
for (const entry of result.entries) {
|
|
836
|
-
const entries = entriesByName.get(entry[0]);
|
|
837
|
-
if (entries) {
|
|
838
|
-
entries.push(entry);
|
|
839
|
-
} else {
|
|
840
|
-
entriesByName.set(entry[0], [entry]);
|
|
841
|
-
}
|
|
842
|
-
}
|
|
843
|
-
}
|
|
844
|
-
objectLike = true;
|
|
845
|
-
sources.push({
|
|
846
|
-
entries: Array.from(entriesByName.entries()).map(([name, entries]) => {
|
|
847
|
-
const schemas = deduplicateJsonSchemas(entries.map((entry) => entry[1]));
|
|
848
|
-
const required = keyword === "allOf" ? entries.some((entry) => !entry[2]) : branchResults.every((result) => {
|
|
849
|
-
const entry = result.entries.find((item) => item[0] === name);
|
|
850
|
-
return entry !== void 0 && !entry[2];
|
|
851
|
-
});
|
|
852
|
-
return [
|
|
853
|
-
name,
|
|
854
|
-
schemas.length === 1 ? schemas[0] : keyword === "allOf" ? { allOf: schemas } : { anyOf: schemas },
|
|
855
|
-
!required
|
|
856
|
-
];
|
|
857
|
-
}),
|
|
858
|
-
source: keyword
|
|
859
|
-
});
|
|
860
|
-
}
|
|
861
|
-
const sourceEntries = /* @__PURE__ */ new Map();
|
|
862
|
-
for (const { entries, source } of sources) {
|
|
863
|
-
for (const entry of entries) {
|
|
864
|
-
const existing = sourceEntries.get(entry[0]);
|
|
865
|
-
if (existing) {
|
|
866
|
-
existing[source] = entry;
|
|
867
|
-
} else {
|
|
868
|
-
sourceEntries.set(entry[0], { [source]: entry });
|
|
869
|
-
}
|
|
870
|
-
}
|
|
871
|
-
}
|
|
872
|
-
return {
|
|
873
|
-
entries: Array.from(sourceEntries.entries()).map(([name, entries]) => {
|
|
874
|
-
const schemas = deduplicateJsonSchemas([
|
|
875
|
-
entries.direct?.[1],
|
|
876
|
-
entries.allOf?.[1],
|
|
877
|
-
entries.anyOf?.[1],
|
|
878
|
-
entries.oneOf?.[1]
|
|
879
|
-
].filter((schema2) => schema2 !== void 0));
|
|
880
|
-
return [
|
|
881
|
-
name,
|
|
882
|
-
schemas.length === 1 ? schemas[0] : { allOf: schemas },
|
|
883
|
-
!schema.required?.includes(name) && [entries.allOf, entries.anyOf, entries.oneOf].every((entry) => entry === void 0 || entry[2])
|
|
884
|
-
];
|
|
885
|
-
}),
|
|
886
|
-
objectLike
|
|
887
|
-
};
|
|
888
|
-
}
|
|
889
|
-
function flattenJsonUnionSchema(schema) {
|
|
890
|
-
return deduplicateJsonSchemas(flattenJsonUnionSchemaInternal(schema, /* @__PURE__ */ new Set()));
|
|
891
|
-
}
|
|
892
|
-
function flattenJsonUnionSchemaInternal(schema, resolvingRefs) {
|
|
893
|
-
if (typeof schema !== "object") {
|
|
894
|
-
return [schema];
|
|
895
|
-
}
|
|
896
|
-
if (typeof schema.$ref === "string") {
|
|
897
|
-
if (resolvingRefs.has(schema.$ref)) {
|
|
898
|
-
return [];
|
|
899
|
-
}
|
|
900
|
-
const resolved = resolveJsonSchemaRootLocalRef(schema);
|
|
901
|
-
if (resolved !== schema) {
|
|
902
|
-
const result = flattenJsonUnionSchemaInternal(resolved, new Set(resolvingRefs).add(schema.$ref));
|
|
903
|
-
if (result.length > 1) {
|
|
904
|
-
return result;
|
|
905
|
-
}
|
|
906
|
-
}
|
|
907
|
-
}
|
|
908
|
-
const { anyOf: _anyOf, oneOf: _oneOf, ...rest } = schema;
|
|
909
|
-
const entries = Object.entries(rest).filter(([, val]) => val !== void 0);
|
|
910
|
-
for (const keyword of ["anyOf", "oneOf"]) {
|
|
911
|
-
if (schema[keyword]) {
|
|
912
|
-
return schema[keyword].flatMap((s) => {
|
|
913
|
-
s = ensureJsonSchemaObject(s);
|
|
914
|
-
const mergedSchema = {
|
|
915
|
-
...s,
|
|
916
|
-
...Object.fromEntries(entries.filter(([key]) => s[key] === void 0)),
|
|
917
|
-
$defs: schema.$defs
|
|
918
|
-
};
|
|
919
|
-
const conflicts = entries.filter(([key]) => s[key] !== void 0);
|
|
920
|
-
if (conflicts.length) {
|
|
921
|
-
mergedSchema.allOf = [...toArray(mergedSchema.allOf), Object.fromEntries(conflicts)];
|
|
922
|
-
}
|
|
923
|
-
return flattenJsonUnionSchemaInternal(mergedSchema, resolvingRefs);
|
|
924
|
-
});
|
|
925
|
-
}
|
|
926
|
-
}
|
|
927
|
-
return [schema];
|
|
928
|
-
}
|
|
929
|
-
function matchArrayableJsonSchema(schema) {
|
|
930
|
-
const schemas = flattenJsonUnionSchema(schema);
|
|
931
|
-
if (schemas.length !== 2) {
|
|
932
|
-
return void 0;
|
|
933
|
-
}
|
|
934
|
-
const arraySchema = schemas.find(isJsonArraySchema);
|
|
935
|
-
if (arraySchema === void 0) {
|
|
936
|
-
return void 0;
|
|
937
|
-
}
|
|
938
|
-
const items1 = arraySchema.items ?? true;
|
|
939
|
-
const items2 = schemas.find((s) => s !== arraySchema);
|
|
940
|
-
const logicItem1 = Object.fromEntries(
|
|
941
|
-
Object.entries(ensureJsonSchemaObject(items1)).filter(([key]) => JSON_SCHEMA_LOGIC_KEYWORDS.has(key))
|
|
942
|
-
);
|
|
943
|
-
const logicItem2 = Object.fromEntries(
|
|
944
|
-
Object.entries(ensureJsonSchemaObject(items2)).filter(([key]) => JSON_SCHEMA_LOGIC_KEYWORDS.has(key))
|
|
945
|
-
);
|
|
946
|
-
if (!isDeepEqual(logicItem1, logicItem2)) {
|
|
947
|
-
return void 0;
|
|
948
|
-
}
|
|
949
|
-
return [items2, arraySchema];
|
|
950
|
-
}
|
|
951
|
-
function deduplicateJsonSchemas(schemas) {
|
|
952
|
-
const result = [];
|
|
953
|
-
for (const schema of schemas) {
|
|
954
|
-
if (result.some((i) => isDeepEqual(i, schema))) {
|
|
955
|
-
continue;
|
|
956
|
-
}
|
|
957
|
-
result.push(schema);
|
|
958
|
-
}
|
|
959
|
-
return result;
|
|
960
|
-
}
|
|
961
|
-
function withRootDefs(schema, $defs) {
|
|
962
|
-
if (typeof schema === "boolean" || !$defs) {
|
|
963
|
-
return schema;
|
|
964
|
-
}
|
|
965
|
-
return { ...schema, $defs };
|
|
966
|
-
}
|
|
967
|
-
|
|
968
|
-
class DelegatingJsonSchemaConverter {
|
|
969
|
-
constructor(converters = []) {
|
|
970
|
-
this.converters = converters;
|
|
971
|
-
}
|
|
972
|
-
convert(schema, direction) {
|
|
973
|
-
for (const converter of this.converters) {
|
|
974
|
-
if (converter.condition(schema, direction)) {
|
|
975
|
-
return converter.convert(schema, direction);
|
|
976
|
-
}
|
|
977
|
-
}
|
|
978
|
-
return [{}, true];
|
|
979
|
-
}
|
|
980
|
-
}
|
|
981
|
-
|
|
982
|
-
function isStandardSchemaOptional(schema, direction) {
|
|
983
|
-
try {
|
|
984
|
-
const result = schema["~standard"].validate(void 0);
|
|
985
|
-
if (result instanceof Promise) {
|
|
986
|
-
result.catch(() => {
|
|
987
|
-
});
|
|
988
|
-
return false;
|
|
989
|
-
}
|
|
990
|
-
return !result.issues && (direction === "input" || result.value === void 0);
|
|
991
|
-
} catch {
|
|
992
|
-
return false;
|
|
993
|
-
}
|
|
994
|
-
}
|
|
995
|
-
class StandardJsonSchemaConverter {
|
|
996
|
-
condition(schema, _direction) {
|
|
997
|
-
return Boolean(
|
|
998
|
-
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"
|
|
999
|
-
);
|
|
1000
|
-
}
|
|
1001
|
-
convert(schema, direction) {
|
|
1002
|
-
return this.convertInternal(schema, direction);
|
|
1003
|
-
}
|
|
1004
|
-
convertInternal(schema, direction) {
|
|
1005
|
-
try {
|
|
1006
|
-
const jsonSchema = direction === "input" ? schema["~standard"].jsonSchema.input({ target: "draft-2020-12" }) : schema["~standard"].jsonSchema.output({ target: "draft-2020-12" });
|
|
1007
|
-
return [jsonSchema, isStandardSchemaOptional(schema, direction)];
|
|
1008
|
-
} catch {
|
|
1009
|
-
return [{}, true];
|
|
1010
|
-
}
|
|
1011
|
-
}
|
|
1012
|
-
}
|
|
1013
|
-
|
|
1014
|
-
class SmartCoercionHandlerPlugin {
|
|
1015
|
-
name = "~smart-coercion";
|
|
1016
|
-
converter;
|
|
1017
|
-
coercer;
|
|
1018
|
-
cache = /* @__PURE__ */ new WeakMap();
|
|
1019
|
-
constructor(options = {}) {
|
|
1020
|
-
this.converter = new DelegatingJsonSchemaConverter([
|
|
1021
|
-
...toArray(options.converters),
|
|
1022
|
-
new StandardJsonSchemaConverter()
|
|
1023
|
-
]);
|
|
1024
|
-
this.coercer = new JsonSchemaCoercer(options);
|
|
1025
|
-
}
|
|
1026
|
-
init(options) {
|
|
1027
|
-
return {
|
|
1028
|
-
...options,
|
|
1029
|
-
clientInterceptors: [
|
|
1030
|
-
({ next, input, ...interceptorOptions }) => {
|
|
1031
|
-
const inputSchemas = interceptorOptions.procedure["~orpc"].inputSchemas;
|
|
1032
|
-
if (!inputSchemas) {
|
|
1033
|
-
return next();
|
|
1034
|
-
}
|
|
1035
|
-
const coercedInput = this.coerceValue(inputSchemas, input);
|
|
1036
|
-
return next({ ...interceptorOptions, input: coercedInput });
|
|
1037
|
-
},
|
|
1038
|
-
...toArray(options.clientInterceptors)
|
|
1039
|
-
]
|
|
1040
|
-
};
|
|
1041
|
-
}
|
|
1042
|
-
coerceValue(schemas, value) {
|
|
1043
|
-
for (const schema of schemas) {
|
|
1044
|
-
let converted = this.cache.get(schema);
|
|
1045
|
-
if (!converted) {
|
|
1046
|
-
converted = this.converter.convert(schema, "input");
|
|
1047
|
-
this.cache.set(schema, converted);
|
|
1048
|
-
}
|
|
1049
|
-
value = this.coercer.coerce(converted, value);
|
|
1050
|
-
}
|
|
1051
|
-
return value;
|
|
1052
|
-
}
|
|
1053
|
-
}
|
|
1054
|
-
|
|
1055
|
-
class SmartCoercionLinkPlugin {
|
|
1056
|
-
constructor(contract, options = {}) {
|
|
1057
|
-
this.contract = contract;
|
|
1058
|
-
this.converter = new DelegatingJsonSchemaConverter([
|
|
1059
|
-
...toArray(options.converters),
|
|
1060
|
-
new StandardJsonSchemaConverter()
|
|
1061
|
-
]);
|
|
1062
|
-
this.coercer = new JsonSchemaCoercer(options);
|
|
1063
|
-
}
|
|
1064
|
-
name = "~smart-coercion";
|
|
1065
|
-
/**
|
|
1066
|
-
* Output and error values should be coerced before validation.
|
|
1067
|
-
*/
|
|
1068
|
-
after = ["~response-validation"];
|
|
1069
|
-
converter;
|
|
1070
|
-
coercer;
|
|
1071
|
-
cache = /* @__PURE__ */ new WeakMap();
|
|
1072
|
-
init(options) {
|
|
1073
|
-
return {
|
|
1074
|
-
...options,
|
|
1075
|
-
interceptors: [
|
|
1076
|
-
...toArray(options.interceptors),
|
|
1077
|
-
async ({ next, path }) => {
|
|
1078
|
-
const procedure = getProcedureContractOrThrow(this.contract, path);
|
|
1079
|
-
try {
|
|
1080
|
-
const output = await next();
|
|
1081
|
-
const outputSchemas = procedure["~orpc"].outputSchemas;
|
|
1082
|
-
if (!outputSchemas) {
|
|
1083
|
-
return output;
|
|
1084
|
-
}
|
|
1085
|
-
const coercedOutput = this.coerceValue(outputSchemas, output);
|
|
1086
|
-
return coercedOutput;
|
|
1087
|
-
} catch (error) {
|
|
1088
|
-
if (!(error instanceof ORPCError) || !error.defined) {
|
|
1089
|
-
throw error;
|
|
1090
|
-
}
|
|
1091
|
-
const errorMap = procedure["~orpc"].errorMap;
|
|
1092
|
-
const dataSchema = getOwn(errorMap, error.code)?.data;
|
|
1093
|
-
if (!dataSchema) {
|
|
1094
|
-
throw error;
|
|
1095
|
-
}
|
|
1096
|
-
const cloned = cloneORPCError(error);
|
|
1097
|
-
cloned.data = this.coerceValue([dataSchema], cloned.data);
|
|
1098
|
-
throw cloned;
|
|
1099
|
-
}
|
|
1100
|
-
}
|
|
1101
|
-
]
|
|
1102
|
-
};
|
|
1103
|
-
}
|
|
1104
|
-
coerceValue(schemas, value) {
|
|
1105
|
-
for (const schema of schemas) {
|
|
1106
|
-
let converted = this.cache.get(schema);
|
|
1107
|
-
if (!converted) {
|
|
1108
|
-
converted = this.converter.convert(schema, "output");
|
|
1109
|
-
this.cache.set(schema, converted);
|
|
1110
|
-
}
|
|
1111
|
-
value = this.coercer.coerce(converted, value);
|
|
1112
|
-
}
|
|
1113
|
-
return value;
|
|
1114
|
-
}
|
|
1115
|
-
}
|
|
1116
|
-
|
|
1117
|
-
export { DelegatingJsonSchemaConverter, JsonSchemaCoercer, JsonSchemaFormat, JsonSchemaType, JsonSchemaXNativeType, SmartCoercionHandlerPlugin, SmartCoercionLinkPlugin, StandardJsonSchemaConverter, combineJsonObjectSchemaEntries, combineJsonSchemasWithComposition, containsJsonFileSchema, decodeJsonPointerSegment, deduplicateJsonSchemas, encodeJsonPointerSegment, ensureJsonSchemaObject, extractJsonObjectSchemaEntries, flattenJsonUnionSchema, hoistRecursiveRefToDef, isJsonArraySchema, isJsonFileSchema, isJsonObjectLikeSchema, isJsonObjectSchema, isJsonPrimitiveSchema, isStandardSchemaOptional, isUnconstrainedSchema, mapJsonSchemaRefs, matchArrayableJsonSchema, resolveJsonSchemaRootLocalRef, visitJsonSchemaRefs };
|
|
1
|
+
export * from "./coercer.mjs";
|
|
2
|
+
export * from "./composition-utils.mjs";
|
|
3
|
+
export * from "./convert.mjs";
|
|
4
|
+
export * from "./ref-utils.mjs";
|
|
5
|
+
export * from "./smart-coercion-handler-plugin.mjs";
|
|
6
|
+
export * from "./smart-coercion-link-plugin.mjs";
|
|
7
|
+
export * from "./standard-json-schema-converter.mjs";
|
|
8
|
+
export * from "./types.mjs";
|
|
9
|
+
export * from "./utils.mjs";
|