effect 3.17.9 → 3.17.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/dist/cjs/Arbitrary.js +5 -2
- package/dist/cjs/Arbitrary.js.map +1 -1
- package/dist/cjs/Either.js.map +1 -1
- package/dist/cjs/JSONSchema.js +307 -364
- package/dist/cjs/JSONSchema.js.map +1 -1
- package/dist/cjs/ParseResult.js +1 -1
- package/dist/cjs/ParseResult.js.map +1 -1
- package/dist/cjs/Schema.js +17 -14
- package/dist/cjs/Schema.js.map +1 -1
- package/dist/cjs/SchemaAST.js +27 -25
- package/dist/cjs/SchemaAST.js.map +1 -1
- package/dist/cjs/internal/core-effect.js +6 -6
- package/dist/cjs/internal/core-effect.js.map +1 -1
- package/dist/cjs/internal/version.js +1 -1
- package/dist/cjs/internal/version.js.map +1 -1
- package/dist/dts/Effect.d.ts +4 -4
- package/dist/dts/Either.d.ts +96 -96
- package/dist/dts/Either.d.ts.map +1 -1
- package/dist/dts/JSONSchema.d.ts.map +1 -1
- package/dist/dts/STM.d.ts +2 -2
- package/dist/dts/Schema.d.ts.map +1 -1
- package/dist/dts/SchemaAST.d.ts +2 -0
- package/dist/dts/SchemaAST.d.ts.map +1 -1
- package/dist/esm/Arbitrary.js +5 -2
- package/dist/esm/Arbitrary.js.map +1 -1
- package/dist/esm/Either.js.map +1 -1
- package/dist/esm/JSONSchema.js +307 -364
- package/dist/esm/JSONSchema.js.map +1 -1
- package/dist/esm/ParseResult.js +1 -1
- package/dist/esm/ParseResult.js.map +1 -1
- package/dist/esm/Schema.js +17 -14
- package/dist/esm/Schema.js.map +1 -1
- package/dist/esm/SchemaAST.js +27 -25
- package/dist/esm/SchemaAST.js.map +1 -1
- package/dist/esm/internal/core-effect.js +6 -6
- package/dist/esm/internal/core-effect.js.map +1 -1
- package/dist/esm/internal/version.js +1 -1
- package/dist/esm/internal/version.js.map +1 -1
- package/package.json +1 -1
- package/src/Arbitrary.ts +4 -2
- package/src/Effect.ts +4 -4
- package/src/Either.ts +132 -132
- package/src/JSONSchema.ts +373 -332
- package/src/ParseResult.ts +1 -1
- package/src/STM.ts +2 -2
- package/src/Schema.ts +19 -12
- package/src/SchemaAST.ts +24 -31
- package/src/internal/core-effect.ts +6 -6
- package/src/internal/version.ts +1 -1
package/dist/cjs/JSONSchema.js
CHANGED
|
@@ -6,6 +6,7 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
6
6
|
exports.make = exports.fromAST = void 0;
|
|
7
7
|
var Arr = _interopRequireWildcard(require("./Array.js"));
|
|
8
8
|
var errors_ = _interopRequireWildcard(require("./internal/schema/errors.js"));
|
|
9
|
+
var schemaId_ = _interopRequireWildcard(require("./internal/schema/schemaId.js"));
|
|
9
10
|
var Option = _interopRequireWildcard(require("./Option.js"));
|
|
10
11
|
var ParseResult = _interopRequireWildcard(require("./ParseResult.js"));
|
|
11
12
|
var Predicate = _interopRequireWildcard(require("./Predicate.js"));
|
|
@@ -73,38 +74,38 @@ const fromAST = (ast, options) => {
|
|
|
73
74
|
const definitionPath = options.definitionPath ?? "#/$defs/";
|
|
74
75
|
const getRef = id => definitionPath + id;
|
|
75
76
|
const target = options.target ?? "jsonSchema7";
|
|
76
|
-
const handleIdentifier = options.topLevelReferenceStrategy !== "skip";
|
|
77
|
+
const handleIdentifier = options.topLevelReferenceStrategy !== "skip" ? "handle-identifier" : "ignore-identifier";
|
|
77
78
|
const additionalPropertiesStrategy = options.additionalPropertiesStrategy ?? "strict";
|
|
78
79
|
return go(ast, options.definitions, handleIdentifier, [], {
|
|
79
80
|
getRef,
|
|
80
81
|
target,
|
|
81
82
|
additionalPropertiesStrategy
|
|
82
|
-
});
|
|
83
|
+
}, "handle-annotation", "handle-errors");
|
|
83
84
|
};
|
|
84
85
|
exports.fromAST = fromAST;
|
|
85
86
|
const constNever = {
|
|
86
|
-
|
|
87
|
-
|
|
87
|
+
$id: "/schemas/never",
|
|
88
|
+
not: {}
|
|
88
89
|
};
|
|
89
90
|
const constAny = {
|
|
90
|
-
|
|
91
|
+
$id: "/schemas/any"
|
|
91
92
|
};
|
|
92
93
|
const constUnknown = {
|
|
93
|
-
|
|
94
|
+
$id: "/schemas/unknown"
|
|
94
95
|
};
|
|
95
96
|
const constVoid = {
|
|
96
|
-
|
|
97
|
+
$id: "/schemas/void"
|
|
97
98
|
};
|
|
98
|
-
const
|
|
99
|
-
|
|
99
|
+
const constObject = {
|
|
100
|
+
$id: "/schemas/object",
|
|
100
101
|
"anyOf": [{
|
|
101
102
|
"type": "object"
|
|
102
103
|
}, {
|
|
103
104
|
"type": "array"
|
|
104
105
|
}]
|
|
105
106
|
};
|
|
106
|
-
const
|
|
107
|
-
|
|
107
|
+
const constEmptyStruct = {
|
|
108
|
+
$id: "/schemas/%7B%7D",
|
|
108
109
|
"anyOf": [{
|
|
109
110
|
"type": "object"
|
|
110
111
|
}, {
|
|
@@ -112,79 +113,93 @@ const constEmpty = {
|
|
|
112
113
|
}]
|
|
113
114
|
};
|
|
114
115
|
const $schema = "http://json-schema.org/draft-07/schema#";
|
|
115
|
-
|
|
116
|
-
annotated
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
116
|
+
function getRawDescription(annotated) {
|
|
117
|
+
if (annotated !== undefined) return Option.getOrUndefined(AST.getDescriptionAnnotation(annotated));
|
|
118
|
+
}
|
|
119
|
+
function getRawTitle(annotated) {
|
|
120
|
+
if (annotated !== undefined) return Option.getOrUndefined(AST.getTitleAnnotation(annotated));
|
|
121
|
+
}
|
|
122
|
+
function getRawDefault(annotated) {
|
|
123
|
+
if (annotated !== undefined) return AST.getDefaultAnnotation(annotated);
|
|
124
|
+
return Option.none();
|
|
125
|
+
}
|
|
126
|
+
function getRawExamples(annotated) {
|
|
127
|
+
if (annotated !== undefined) return Option.getOrUndefined(AST.getExamplesAnnotation(annotated));
|
|
128
|
+
}
|
|
129
|
+
function encodeExamples(ast, examples) {
|
|
130
|
+
const getOption = ParseResult.getOption(ast, false);
|
|
131
|
+
const out = Arr.filterMap(examples, e => getOption(e));
|
|
132
|
+
return out.length > 0 ? out : undefined;
|
|
133
|
+
}
|
|
134
|
+
function filterBuiltIn(ast, annotation, key) {
|
|
135
|
+
if (annotation !== undefined) {
|
|
136
|
+
switch (ast._tag) {
|
|
137
|
+
case "StringKeyword":
|
|
138
|
+
return annotation !== AST.stringKeyword.annotations[key] ? annotation : undefined;
|
|
139
|
+
case "NumberKeyword":
|
|
140
|
+
return annotation !== AST.numberKeyword.annotations[key] ? annotation : undefined;
|
|
141
|
+
case "BooleanKeyword":
|
|
142
|
+
return annotation !== AST.booleanKeyword.annotations[key] ? annotation : undefined;
|
|
128
143
|
}
|
|
129
144
|
}
|
|
130
|
-
return
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
145
|
+
return annotation;
|
|
146
|
+
}
|
|
147
|
+
function pruneJsonSchemaAnnotations(ast, description, title, def, examples) {
|
|
148
|
+
const out = {};
|
|
149
|
+
if (description !== undefined) out.description = description;
|
|
150
|
+
if (title !== undefined) out.title = title;
|
|
151
|
+
if (Option.isSome(def)) out.default = def.value;
|
|
152
|
+
if (examples !== undefined) {
|
|
153
|
+
const encodedExamples = encodeExamples(ast, examples);
|
|
154
|
+
if (encodedExamples !== undefined) {
|
|
155
|
+
out.examples = encodedExamples;
|
|
156
|
+
}
|
|
135
157
|
}
|
|
136
|
-
if (
|
|
137
|
-
|
|
158
|
+
if (Object.keys(out).length === 0) {
|
|
159
|
+
return undefined;
|
|
138
160
|
}
|
|
139
|
-
return
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
161
|
+
return out;
|
|
162
|
+
}
|
|
163
|
+
function getContextJsonSchemaAnnotations(ast, annotated) {
|
|
164
|
+
return pruneJsonSchemaAnnotations(ast, getRawDescription(annotated), getRawTitle(annotated), getRawDefault(annotated), getRawExamples(annotated));
|
|
165
|
+
}
|
|
166
|
+
function getJsonSchemaAnnotations(ast) {
|
|
167
|
+
return pruneJsonSchemaAnnotations(ast, filterBuiltIn(ast, getRawDescription(ast), AST.DescriptionAnnotationId), filterBuiltIn(ast, getRawTitle(ast), AST.TitleAnnotationId), getRawDefault(ast), getRawExamples(ast));
|
|
168
|
+
}
|
|
169
|
+
function mergeJsonSchemaAnnotations(jsonSchema, jsonSchemaAnnotations) {
|
|
170
|
+
if (jsonSchemaAnnotations) {
|
|
171
|
+
if ("$ref" in jsonSchema) {
|
|
172
|
+
return {
|
|
173
|
+
allOf: [jsonSchema],
|
|
174
|
+
...jsonSchemaAnnotations
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
return {
|
|
178
|
+
...jsonSchema,
|
|
179
|
+
...jsonSchemaAnnotations
|
|
180
|
+
};
|
|
152
181
|
}
|
|
153
|
-
|
|
182
|
+
return jsonSchema;
|
|
183
|
+
}
|
|
154
184
|
const pruneUndefined = ast => {
|
|
155
185
|
if (Option.isNone(AST.getJSONSchemaAnnotation(ast))) {
|
|
156
186
|
return AST.pruneUndefined(ast, pruneUndefined, ast => pruneUndefined(ast.from));
|
|
157
187
|
}
|
|
158
188
|
};
|
|
159
189
|
const isParseJsonTransformation = ast => ast.annotations[AST.SchemaIdAnnotationId] === AST.ParseJsonSchemaId;
|
|
160
|
-
const isOverrideAnnotation = jsonSchema => {
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
const isMergeableEnum = jsonSchema => {
|
|
166
|
-
const len = Object.keys(jsonSchema).length;
|
|
167
|
-
return "enum" in jsonSchema && (len === 1 || "type" in jsonSchema && len === 2);
|
|
168
|
-
};
|
|
169
|
-
// Some validators do not support enums without a type keyword. This function
|
|
170
|
-
// adds a type keyword to the schema if it is missing and the enum values are
|
|
171
|
-
// homogeneous.
|
|
172
|
-
const addEnumType = jsonSchema => {
|
|
173
|
-
if ("enum" in jsonSchema && !("type" in jsonSchema)) {
|
|
174
|
-
const type = jsonSchema.enum.every(Predicate.isString) ? "string" : jsonSchema.enum.every(Predicate.isNumber) ? "number" : jsonSchema.enum.every(Predicate.isBoolean) ? "boolean" : undefined;
|
|
175
|
-
if (type !== undefined) {
|
|
176
|
-
return {
|
|
177
|
-
type,
|
|
178
|
-
...jsonSchema
|
|
179
|
-
};
|
|
190
|
+
const isOverrideAnnotation = (ast, jsonSchema) => {
|
|
191
|
+
if (AST.isRefinement(ast)) {
|
|
192
|
+
const schemaId = ast.annotations[AST.SchemaIdAnnotationId];
|
|
193
|
+
if (schemaId === schemaId_.IntSchemaId) {
|
|
194
|
+
return "type" in jsonSchema && jsonSchema.type !== "integer";
|
|
180
195
|
}
|
|
181
196
|
}
|
|
182
|
-
return jsonSchema;
|
|
197
|
+
return "type" in jsonSchema || "oneOf" in jsonSchema || "anyOf" in jsonSchema || "$ref" in jsonSchema;
|
|
183
198
|
};
|
|
184
|
-
const mergeRefinements = (from, jsonSchema,
|
|
199
|
+
const mergeRefinements = (from, jsonSchema, ast) => {
|
|
185
200
|
const out = {
|
|
186
201
|
...from,
|
|
187
|
-
...
|
|
202
|
+
...getJsonSchemaAnnotations(ast),
|
|
188
203
|
...jsonSchema
|
|
189
204
|
};
|
|
190
205
|
out.allOf ??= [];
|
|
@@ -220,25 +235,6 @@ function isContentSchemaSupported(options) {
|
|
|
220
235
|
return true;
|
|
221
236
|
}
|
|
222
237
|
}
|
|
223
|
-
function isNullTypeKeywordSupported(options) {
|
|
224
|
-
switch (options.target) {
|
|
225
|
-
case "jsonSchema7":
|
|
226
|
-
case "jsonSchema2019-09":
|
|
227
|
-
return true;
|
|
228
|
-
case "openApi3.1":
|
|
229
|
-
return false;
|
|
230
|
-
}
|
|
231
|
-
}
|
|
232
|
-
// https://swagger.io/docs/specification/v3_0/data-models/data-types/#null
|
|
233
|
-
function isNullableKeywordSupported(options) {
|
|
234
|
-
switch (options.target) {
|
|
235
|
-
case "jsonSchema7":
|
|
236
|
-
case "jsonSchema2019-09":
|
|
237
|
-
return false;
|
|
238
|
-
case "openApi3.1":
|
|
239
|
-
return true;
|
|
240
|
-
}
|
|
241
|
-
}
|
|
242
238
|
function getAdditionalProperties(options) {
|
|
243
239
|
switch (options.additionalPropertiesStrategy) {
|
|
244
240
|
case "allow":
|
|
@@ -247,166 +243,177 @@ function getAdditionalProperties(options) {
|
|
|
247
243
|
return false;
|
|
248
244
|
}
|
|
249
245
|
}
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
if (i !== -1) {
|
|
257
|
-
members = [members[i]];
|
|
246
|
+
function addASTAnnotations(jsonSchema, ast) {
|
|
247
|
+
return addAnnotations(jsonSchema, getJsonSchemaAnnotations(ast));
|
|
248
|
+
}
|
|
249
|
+
function addAnnotations(jsonSchema, annotations) {
|
|
250
|
+
if (annotations === undefined || Object.keys(annotations).length === 0) {
|
|
251
|
+
return jsonSchema;
|
|
258
252
|
}
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
253
|
+
if ("$ref" in jsonSchema) {
|
|
254
|
+
return {
|
|
255
|
+
allOf: [jsonSchema],
|
|
256
|
+
...annotations
|
|
257
|
+
};
|
|
262
258
|
}
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
259
|
+
return {
|
|
260
|
+
...jsonSchema,
|
|
261
|
+
...annotations
|
|
262
|
+
};
|
|
263
|
+
}
|
|
264
|
+
function getIdentifierAnnotation(ast) {
|
|
265
|
+
const identifier = Option.getOrUndefined(AST.getJSONIdentifier(ast));
|
|
266
|
+
if (identifier === undefined) {
|
|
267
|
+
if (AST.isSuspend(ast)) {
|
|
268
|
+
return getIdentifierAnnotation(ast.f());
|
|
269
|
+
}
|
|
270
|
+
if (AST.isTransformation(ast) && AST.isTypeLiteral(ast.from) && AST.isDeclaration(ast.to)) {
|
|
271
|
+
const to = ast.to;
|
|
272
|
+
const surrogate = AST.getSurrogateAnnotation(to);
|
|
273
|
+
if (Option.isSome(surrogate)) {
|
|
274
|
+
return getIdentifierAnnotation(to);
|
|
275
|
+
}
|
|
276
|
+
}
|
|
266
277
|
}
|
|
267
|
-
return
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
if (
|
|
271
|
-
const
|
|
272
|
-
if (
|
|
273
|
-
const id = identifier.value;
|
|
278
|
+
return identifier;
|
|
279
|
+
}
|
|
280
|
+
function go(ast, $defs, identifier, path, options, annotation, errors) {
|
|
281
|
+
if (identifier === "handle-identifier") {
|
|
282
|
+
const id = getIdentifierAnnotation(ast);
|
|
283
|
+
if (id !== undefined) {
|
|
274
284
|
const escapedId = id.replace(/~/ig, "~0").replace(/\//ig, "~1");
|
|
275
285
|
const out = {
|
|
276
286
|
$ref: options.getRef(escapedId)
|
|
277
287
|
};
|
|
278
288
|
if (!Record.has($defs, id)) {
|
|
279
289
|
$defs[id] = out;
|
|
280
|
-
$defs[id] = go(ast, $defs,
|
|
290
|
+
$defs[id] = go(ast, $defs, "ignore-identifier", path, options, "handle-annotation", errors);
|
|
281
291
|
}
|
|
282
292
|
return out;
|
|
283
293
|
}
|
|
284
294
|
}
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
295
|
+
if (annotation === "handle-annotation") {
|
|
296
|
+
const hook = AST.getJSONSchemaAnnotation(ast);
|
|
297
|
+
if (Option.isSome(hook)) {
|
|
298
|
+
const handler = hook.value;
|
|
299
|
+
if (isOverrideAnnotation(ast, handler)) {
|
|
300
|
+
switch (ast._tag) {
|
|
301
|
+
case "Declaration":
|
|
302
|
+
return addASTAnnotations(handler, ast);
|
|
303
|
+
default:
|
|
304
|
+
return handler;
|
|
305
|
+
}
|
|
306
|
+
} else {
|
|
307
|
+
switch (ast._tag) {
|
|
308
|
+
case "Refinement":
|
|
309
|
+
{
|
|
310
|
+
const t = AST.getTransformationFrom(ast);
|
|
311
|
+
if (t === undefined) {
|
|
312
|
+
return mergeRefinements(go(ast.from, $defs, identifier, path, options, "handle-annotation", errors), handler, ast);
|
|
313
|
+
} else {
|
|
314
|
+
return go(t, $defs, identifier, path, options, "handle-annotation", errors);
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
default:
|
|
318
|
+
return {
|
|
319
|
+
...go(ast, $defs, identifier, path, options, "ignore-annotation", errors),
|
|
320
|
+
...handler
|
|
321
|
+
};
|
|
322
|
+
}
|
|
294
323
|
}
|
|
295
324
|
}
|
|
296
|
-
if (AST.isDeclaration(ast)) {
|
|
297
|
-
return {
|
|
298
|
-
...handler,
|
|
299
|
-
...getJsonSchemaAnnotations(ast)
|
|
300
|
-
};
|
|
301
|
-
}
|
|
302
|
-
return handler;
|
|
303
325
|
}
|
|
304
326
|
const surrogate = AST.getSurrogateAnnotation(ast);
|
|
305
327
|
if (Option.isSome(surrogate)) {
|
|
306
|
-
return go(surrogate.value, $defs,
|
|
328
|
+
return go(surrogate.value, $defs, identifier, path, options, "handle-annotation", errors);
|
|
307
329
|
}
|
|
308
330
|
switch (ast._tag) {
|
|
331
|
+
// Unsupported
|
|
309
332
|
case "Declaration":
|
|
310
|
-
|
|
333
|
+
case "UndefinedKeyword":
|
|
334
|
+
case "BigIntKeyword":
|
|
335
|
+
case "UniqueSymbol":
|
|
336
|
+
case "SymbolKeyword":
|
|
337
|
+
{
|
|
338
|
+
if (errors === "ignore-errors") return addASTAnnotations(constAny, ast);
|
|
339
|
+
throw new Error(errors_.getJSONSchemaMissingAnnotationErrorMessage(path, ast));
|
|
340
|
+
}
|
|
341
|
+
case "Suspend":
|
|
342
|
+
{
|
|
343
|
+
if (identifier === "handle-identifier") {
|
|
344
|
+
if (errors === "ignore-errors") return addASTAnnotations(constAny, ast);
|
|
345
|
+
throw new Error(errors_.getJSONSchemaMissingIdentifierAnnotationErrorMessage(path, ast));
|
|
346
|
+
}
|
|
347
|
+
return go(ast.f(), $defs, "ignore-identifier", path, options, "handle-annotation", errors);
|
|
348
|
+
}
|
|
349
|
+
// Primitives
|
|
350
|
+
case "NeverKeyword":
|
|
351
|
+
return addASTAnnotations(constNever, ast);
|
|
352
|
+
case "VoidKeyword":
|
|
353
|
+
return addASTAnnotations(constVoid, ast);
|
|
354
|
+
case "UnknownKeyword":
|
|
355
|
+
return addASTAnnotations(constUnknown, ast);
|
|
356
|
+
case "AnyKeyword":
|
|
357
|
+
return addASTAnnotations(constAny, ast);
|
|
358
|
+
case "ObjectKeyword":
|
|
359
|
+
return addASTAnnotations(constObject, ast);
|
|
360
|
+
case "StringKeyword":
|
|
361
|
+
return addASTAnnotations({
|
|
362
|
+
type: "string"
|
|
363
|
+
}, ast);
|
|
364
|
+
case "NumberKeyword":
|
|
365
|
+
return addASTAnnotations({
|
|
366
|
+
type: "number"
|
|
367
|
+
}, ast);
|
|
368
|
+
case "BooleanKeyword":
|
|
369
|
+
return addASTAnnotations({
|
|
370
|
+
type: "boolean"
|
|
371
|
+
}, ast);
|
|
311
372
|
case "Literal":
|
|
312
373
|
{
|
|
313
374
|
const literal = ast.literal;
|
|
314
375
|
if (literal === null) {
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
return {
|
|
319
|
-
type: "null",
|
|
320
|
-
...getJsonSchemaAnnotations(ast)
|
|
321
|
-
};
|
|
322
|
-
} else {
|
|
323
|
-
// OpenAPI 3.1 does not support the "null" type keyword
|
|
324
|
-
// https://swagger.io/docs/specification/v3_0/data-models/data-types/#null
|
|
325
|
-
return {
|
|
326
|
-
// @ts-expect-error
|
|
327
|
-
enum: [null],
|
|
328
|
-
...getJsonSchemaAnnotations(ast)
|
|
329
|
-
};
|
|
330
|
-
}
|
|
376
|
+
return addASTAnnotations({
|
|
377
|
+
type: "null"
|
|
378
|
+
}, ast);
|
|
331
379
|
} else if (Predicate.isString(literal)) {
|
|
332
|
-
return {
|
|
380
|
+
return addASTAnnotations({
|
|
333
381
|
type: "string",
|
|
334
|
-
enum: [literal]
|
|
335
|
-
|
|
336
|
-
};
|
|
382
|
+
enum: [literal]
|
|
383
|
+
}, ast);
|
|
337
384
|
} else if (Predicate.isNumber(literal)) {
|
|
338
|
-
return {
|
|
385
|
+
return addASTAnnotations({
|
|
339
386
|
type: "number",
|
|
340
|
-
enum: [literal]
|
|
341
|
-
|
|
342
|
-
};
|
|
387
|
+
enum: [literal]
|
|
388
|
+
}, ast);
|
|
343
389
|
} else if (Predicate.isBoolean(literal)) {
|
|
344
|
-
return {
|
|
390
|
+
return addASTAnnotations({
|
|
345
391
|
type: "boolean",
|
|
346
|
-
enum: [literal]
|
|
347
|
-
|
|
348
|
-
};
|
|
392
|
+
enum: [literal]
|
|
393
|
+
}, ast);
|
|
349
394
|
}
|
|
395
|
+
if (errors === "ignore-errors") return addASTAnnotations(constAny, ast);
|
|
350
396
|
throw new Error(errors_.getJSONSchemaMissingAnnotationErrorMessage(path, ast));
|
|
351
397
|
}
|
|
352
|
-
case "
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
return {
|
|
368
|
-
...constUnknown,
|
|
369
|
-
...getJsonSchemaAnnotations(ast)
|
|
370
|
-
};
|
|
371
|
-
case "AnyKeyword":
|
|
372
|
-
return {
|
|
373
|
-
...constAny,
|
|
374
|
-
...getJsonSchemaAnnotations(ast)
|
|
375
|
-
};
|
|
376
|
-
case "ObjectKeyword":
|
|
377
|
-
return {
|
|
378
|
-
...constAnyObject,
|
|
379
|
-
...getJsonSchemaAnnotations(ast)
|
|
380
|
-
};
|
|
381
|
-
case "StringKeyword":
|
|
382
|
-
return {
|
|
383
|
-
type: "string",
|
|
384
|
-
...getASTJsonSchemaAnnotations(ast)
|
|
385
|
-
};
|
|
386
|
-
case "NumberKeyword":
|
|
387
|
-
return {
|
|
388
|
-
type: "number",
|
|
389
|
-
...getASTJsonSchemaAnnotations(ast)
|
|
390
|
-
};
|
|
391
|
-
case "BooleanKeyword":
|
|
392
|
-
return {
|
|
393
|
-
type: "boolean",
|
|
394
|
-
...getASTJsonSchemaAnnotations(ast)
|
|
395
|
-
};
|
|
396
|
-
case "BigIntKeyword":
|
|
397
|
-
throw new Error(errors_.getJSONSchemaMissingAnnotationErrorMessage(path, ast));
|
|
398
|
-
case "SymbolKeyword":
|
|
399
|
-
throw new Error(errors_.getJSONSchemaMissingAnnotationErrorMessage(path, ast));
|
|
398
|
+
case "Enums":
|
|
399
|
+
{
|
|
400
|
+
const anyOf = ast.enums.map(e => {
|
|
401
|
+
const type = Predicate.isNumber(e[1]) ? "number" : "string";
|
|
402
|
+
return {
|
|
403
|
+
type,
|
|
404
|
+
title: e[0],
|
|
405
|
+
enum: [e[1]]
|
|
406
|
+
};
|
|
407
|
+
});
|
|
408
|
+
return anyOf.length >= 1 ? addASTAnnotations({
|
|
409
|
+
$comment: "/schemas/enums",
|
|
410
|
+
anyOf
|
|
411
|
+
}, ast) : addASTAnnotations(constNever, ast);
|
|
412
|
+
}
|
|
400
413
|
case "TupleType":
|
|
401
414
|
{
|
|
402
|
-
const elements = ast.elements.map((e, i) => (
|
|
403
|
-
|
|
404
|
-
...getJsonSchemaAnnotations(e.type, e)
|
|
405
|
-
}));
|
|
406
|
-
const rest = ast.rest.map(annotatedAST => ({
|
|
407
|
-
...go(annotatedAST.type, $defs, true, path, options),
|
|
408
|
-
...getJsonSchemaAnnotations(annotatedAST.type, annotatedAST)
|
|
409
|
-
}));
|
|
415
|
+
const elements = ast.elements.map((e, i) => mergeJsonSchemaAnnotations(go(e.type, $defs, "handle-identifier", path.concat(i), options, "handle-annotation", errors), getContextJsonSchemaAnnotations(e.type, e)));
|
|
416
|
+
const rest = ast.rest.map(type => mergeJsonSchemaAnnotations(go(type.type, $defs, "handle-identifier", path, options, "handle-annotation", errors), getContextJsonSchemaAnnotations(type.type, type)));
|
|
410
417
|
const output = {
|
|
411
418
|
type: "array"
|
|
412
419
|
};
|
|
@@ -434,6 +441,7 @@ const go = (ast, $defs, handleIdentifier, path, options) => {
|
|
|
434
441
|
// handle post rest elements
|
|
435
442
|
// ---------------------------------------------
|
|
436
443
|
if (restLength > 1) {
|
|
444
|
+
if (errors === "ignore-errors") return addASTAnnotations(constAny, ast);
|
|
437
445
|
throw new Error(errors_.getJSONSchemaUnsupportedPostRestElementsErrorMessage(path));
|
|
438
446
|
}
|
|
439
447
|
} else {
|
|
@@ -443,18 +451,12 @@ const go = (ast, $defs, handleIdentifier, path, options) => {
|
|
|
443
451
|
output.maxItems = 0;
|
|
444
452
|
}
|
|
445
453
|
}
|
|
446
|
-
return
|
|
447
|
-
...output,
|
|
448
|
-
...getJsonSchemaAnnotations(ast)
|
|
449
|
-
};
|
|
454
|
+
return addASTAnnotations(output, ast);
|
|
450
455
|
}
|
|
451
456
|
case "TypeLiteral":
|
|
452
457
|
{
|
|
453
458
|
if (ast.propertySignatures.length === 0 && ast.indexSignatures.length === 0) {
|
|
454
|
-
return
|
|
455
|
-
...constEmpty,
|
|
456
|
-
...getJsonSchemaAnnotations(ast)
|
|
457
|
-
};
|
|
459
|
+
return addASTAnnotations(constEmptyStruct, ast);
|
|
458
460
|
}
|
|
459
461
|
const output = {
|
|
460
462
|
type: "object",
|
|
@@ -470,12 +472,12 @@ const go = (ast, $defs, handleIdentifier, path, options) => {
|
|
|
470
472
|
switch (parameter._tag) {
|
|
471
473
|
case "StringKeyword":
|
|
472
474
|
{
|
|
473
|
-
output.additionalProperties = go(pruned, $defs,
|
|
475
|
+
output.additionalProperties = go(pruned, $defs, "handle-identifier", path, options, "handle-annotation", errors);
|
|
474
476
|
break;
|
|
475
477
|
}
|
|
476
478
|
case "TemplateLiteral":
|
|
477
479
|
{
|
|
478
|
-
patternProperties = go(pruned, $defs,
|
|
480
|
+
patternProperties = go(pruned, $defs, "handle-identifier", path, options, "handle-annotation", errors);
|
|
479
481
|
propertyNames = {
|
|
480
482
|
type: "string",
|
|
481
483
|
pattern: AST.getTemplateLiteralRegExp(parameter).source
|
|
@@ -484,15 +486,15 @@ const go = (ast, $defs, handleIdentifier, path, options) => {
|
|
|
484
486
|
}
|
|
485
487
|
case "Refinement":
|
|
486
488
|
{
|
|
487
|
-
patternProperties = go(pruned, $defs,
|
|
488
|
-
propertyNames = go(parameter, $defs,
|
|
489
|
+
patternProperties = go(pruned, $defs, "handle-identifier", path, options, "handle-annotation", errors);
|
|
490
|
+
propertyNames = go(parameter, $defs, "handle-identifier", path, options, "handle-annotation", errors);
|
|
489
491
|
break;
|
|
490
492
|
}
|
|
491
493
|
case "SymbolKeyword":
|
|
492
494
|
{
|
|
493
495
|
const indexSignaturePath = path.concat("[symbol]");
|
|
494
|
-
output.additionalProperties = go(pruned, $defs,
|
|
495
|
-
propertyNames = go(parameter, $defs,
|
|
496
|
+
output.additionalProperties = go(pruned, $defs, "handle-identifier", indexSignaturePath, options, "handle-annotation", errors);
|
|
497
|
+
propertyNames = go(parameter, $defs, "handle-identifier", indexSignaturePath, options, "handle-annotation", errors);
|
|
496
498
|
break;
|
|
497
499
|
}
|
|
498
500
|
}
|
|
@@ -506,10 +508,7 @@ const go = (ast, $defs, handleIdentifier, path, options) => {
|
|
|
506
508
|
if (Predicate.isString(name)) {
|
|
507
509
|
const pruned = pruneUndefined(ps.type);
|
|
508
510
|
const type = pruned ?? ps.type;
|
|
509
|
-
output.properties[name] =
|
|
510
|
-
...go(type, $defs, true, path.concat(ps.name), options),
|
|
511
|
-
...getJsonSchemaAnnotations(type, ps)
|
|
512
|
-
};
|
|
511
|
+
output.properties[name] = mergeJsonSchemaAnnotations(go(type, $defs, "handle-identifier", path.concat(ps.name), options, "handle-annotation", errors), getContextJsonSchemaAnnotations(type, ps));
|
|
513
512
|
// ---------------------------------------------
|
|
514
513
|
// handle optional property signatures
|
|
515
514
|
// ---------------------------------------------
|
|
@@ -517,6 +516,7 @@ const go = (ast, $defs, handleIdentifier, path, options) => {
|
|
|
517
516
|
output.required.push(name);
|
|
518
517
|
}
|
|
519
518
|
} else {
|
|
519
|
+
if (errors === "ignore-errors") return addASTAnnotations(constAny, ast);
|
|
520
520
|
throw new Error(errors_.getJSONSchemaUnsupportedKeyErrorMessage(name, path));
|
|
521
521
|
}
|
|
522
522
|
}
|
|
@@ -532,129 +532,34 @@ const go = (ast, $defs, handleIdentifier, path, options) => {
|
|
|
532
532
|
if (propertyNames !== undefined) {
|
|
533
533
|
output.propertyNames = propertyNames;
|
|
534
534
|
}
|
|
535
|
-
return
|
|
536
|
-
...output,
|
|
537
|
-
...getJsonSchemaAnnotations(ast)
|
|
538
|
-
};
|
|
535
|
+
return addASTAnnotations(output, ast);
|
|
539
536
|
}
|
|
540
537
|
case "Union":
|
|
541
538
|
{
|
|
542
|
-
const members =
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
}
|
|
554
|
-
}
|
|
539
|
+
const members = ast.types.map(t => go(t, $defs, "handle-identifier", path, options, "handle-annotation", errors));
|
|
540
|
+
const anyOf = compactUnion(members);
|
|
541
|
+
switch (anyOf.length) {
|
|
542
|
+
case 0:
|
|
543
|
+
return constNever;
|
|
544
|
+
case 1:
|
|
545
|
+
return addASTAnnotations(anyOf[0], ast);
|
|
546
|
+
default:
|
|
547
|
+
return addASTAnnotations({
|
|
548
|
+
anyOf
|
|
549
|
+
}, ast);
|
|
555
550
|
}
|
|
556
|
-
const anyOf = shrink(members);
|
|
557
|
-
const finalize = anyOf => {
|
|
558
|
-
switch (anyOf.length) {
|
|
559
|
-
case 0:
|
|
560
|
-
return {
|
|
561
|
-
...constNever,
|
|
562
|
-
...getJsonSchemaAnnotations(ast)
|
|
563
|
-
};
|
|
564
|
-
case 1:
|
|
565
|
-
{
|
|
566
|
-
return {
|
|
567
|
-
...addEnumType(anyOf[0]),
|
|
568
|
-
...getJsonSchemaAnnotations(ast)
|
|
569
|
-
};
|
|
570
|
-
}
|
|
571
|
-
default:
|
|
572
|
-
return {
|
|
573
|
-
anyOf: anyOf.map(addEnumType),
|
|
574
|
-
...getJsonSchemaAnnotations(ast)
|
|
575
|
-
};
|
|
576
|
-
}
|
|
577
|
-
};
|
|
578
|
-
if (isNullableKeywordSupported(options)) {
|
|
579
|
-
let nullable = false;
|
|
580
|
-
const nonNullables = [];
|
|
581
|
-
for (const s of anyOf) {
|
|
582
|
-
if ("nullable" in s) {
|
|
583
|
-
nullable = true;
|
|
584
|
-
const nn = {
|
|
585
|
-
...s
|
|
586
|
-
};
|
|
587
|
-
delete nn.nullable;
|
|
588
|
-
nonNullables.push(nn);
|
|
589
|
-
} else if (isMergeableEnum(s)) {
|
|
590
|
-
const nnes = s.enum.filter(e => e !== null);
|
|
591
|
-
if (nnes.length < s.enum.length) {
|
|
592
|
-
nullable = true;
|
|
593
|
-
if (nnes.length === 0) {
|
|
594
|
-
continue;
|
|
595
|
-
}
|
|
596
|
-
const nn = {
|
|
597
|
-
...s
|
|
598
|
-
};
|
|
599
|
-
nn.enum = nnes;
|
|
600
|
-
nonNullables.push(nn);
|
|
601
|
-
}
|
|
602
|
-
} else {
|
|
603
|
-
nonNullables.push(s);
|
|
604
|
-
}
|
|
605
|
-
}
|
|
606
|
-
if (nullable) {
|
|
607
|
-
const out = finalize(nonNullables);
|
|
608
|
-
if (!isAnyJSONSchema(out) && !isUnknownJSONSchema(out)) {
|
|
609
|
-
// @ts-expect-error
|
|
610
|
-
out.nullable = nullable;
|
|
611
|
-
}
|
|
612
|
-
return out;
|
|
613
|
-
}
|
|
614
|
-
}
|
|
615
|
-
return finalize(anyOf);
|
|
616
|
-
}
|
|
617
|
-
case "Enums":
|
|
618
|
-
{
|
|
619
|
-
const anyOf = ast.enums.map(e => addEnumType({
|
|
620
|
-
title: e[0],
|
|
621
|
-
enum: [e[1]]
|
|
622
|
-
}));
|
|
623
|
-
return anyOf.length >= 1 ? {
|
|
624
|
-
$comment: "/schemas/enums",
|
|
625
|
-
anyOf,
|
|
626
|
-
...getJsonSchemaAnnotations(ast)
|
|
627
|
-
} : {
|
|
628
|
-
...constNever,
|
|
629
|
-
...getJsonSchemaAnnotations(ast)
|
|
630
|
-
};
|
|
631
551
|
}
|
|
632
552
|
case "Refinement":
|
|
633
|
-
|
|
634
|
-
// The jsonSchema annotation is required only if the refinement does not have a transformation
|
|
635
|
-
if (AST.getTransformationFrom(ast) === undefined) {
|
|
636
|
-
throw new Error(errors_.getJSONSchemaMissingAnnotationErrorMessage(path, ast));
|
|
637
|
-
}
|
|
638
|
-
return go(ast.from, $defs, handleIdentifier, path, options);
|
|
639
|
-
}
|
|
553
|
+
return go(ast.from, $defs, identifier, path, options, "handle-annotation", errors);
|
|
640
554
|
case "TemplateLiteral":
|
|
641
555
|
{
|
|
642
556
|
const regex = AST.getTemplateLiteralRegExp(ast);
|
|
643
|
-
return {
|
|
557
|
+
return addASTAnnotations({
|
|
644
558
|
type: "string",
|
|
645
559
|
title: String(ast),
|
|
646
560
|
description: "a template literal",
|
|
647
|
-
pattern: regex.source
|
|
648
|
-
|
|
649
|
-
};
|
|
650
|
-
}
|
|
651
|
-
case "Suspend":
|
|
652
|
-
{
|
|
653
|
-
const identifier = Option.orElse(AST.getJSONIdentifier(ast), () => AST.getJSONIdentifier(ast.f()));
|
|
654
|
-
if (Option.isNone(identifier)) {
|
|
655
|
-
throw new Error(errors_.getJSONSchemaMissingIdentifierAnnotationErrorMessage(path, ast));
|
|
656
|
-
}
|
|
657
|
-
return go(ast.f(), $defs, handleIdentifier, path, options);
|
|
561
|
+
pattern: regex.source
|
|
562
|
+
}, ast);
|
|
658
563
|
}
|
|
659
564
|
case "Transformation":
|
|
660
565
|
{
|
|
@@ -664,35 +569,73 @@ const go = (ast, $defs, handleIdentifier, path, options) => {
|
|
|
664
569
|
"contentMediaType": "application/json"
|
|
665
570
|
};
|
|
666
571
|
if (isContentSchemaSupported(options)) {
|
|
667
|
-
out["contentSchema"] = go(ast.to, $defs,
|
|
572
|
+
out["contentSchema"] = go(ast.to, $defs, identifier, path, options, "handle-annotation", errors);
|
|
668
573
|
}
|
|
669
574
|
return out;
|
|
670
575
|
}
|
|
671
|
-
|
|
672
|
-
if (
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
576
|
+
const from = go(ast.from, $defs, identifier, path, options, "handle-annotation", errors);
|
|
577
|
+
if (ast.transformation._tag === "TypeLiteralTransformation" && isJsonSchema7Object(from)) {
|
|
578
|
+
const to = go(ast.to, {}, "ignore-identifier", path, options, "handle-annotation", "ignore-errors");
|
|
579
|
+
if (isJsonSchema7Object(to)) {
|
|
580
|
+
for (const t of ast.transformation.propertySignatureTransformations) {
|
|
581
|
+
const toKey = t.to;
|
|
582
|
+
const fromKey = t.from;
|
|
583
|
+
if (Predicate.isString(toKey) && Predicate.isString(fromKey)) {
|
|
584
|
+
const toProperty = to.properties[toKey];
|
|
585
|
+
if (Predicate.isRecord(toProperty)) {
|
|
586
|
+
const fromProperty = from.properties[fromKey];
|
|
587
|
+
if (Predicate.isRecord(fromProperty)) {
|
|
588
|
+
const annotations = {};
|
|
589
|
+
if (Predicate.isString(toProperty.title)) annotations.title = toProperty.title;
|
|
590
|
+
if (Predicate.isString(toProperty.description)) annotations.description = toProperty.description;
|
|
591
|
+
if (Array.isArray(toProperty.examples)) annotations.examples = toProperty.examples;
|
|
592
|
+
if (Object.hasOwn(toProperty, "default")) annotations.default = toProperty.default;
|
|
593
|
+
from.properties[fromKey] = addAnnotations(fromProperty, annotations);
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
}
|
|
692
598
|
}
|
|
693
599
|
}
|
|
694
|
-
return
|
|
600
|
+
return addASTAnnotations(from, ast);
|
|
695
601
|
}
|
|
696
602
|
}
|
|
697
|
-
}
|
|
603
|
+
}
|
|
604
|
+
function isJsonSchema7Object(jsonSchema) {
|
|
605
|
+
return Predicate.isRecord(jsonSchema) && jsonSchema.type === "object" && Predicate.isRecord(jsonSchema.properties);
|
|
606
|
+
}
|
|
607
|
+
function isNeverWithoutCustomAnnotations(jsonSchema) {
|
|
608
|
+
return jsonSchema === constNever || Predicate.hasProperty(jsonSchema, "$id") && jsonSchema.$id === constNever.$id && Object.keys(jsonSchema).length === 3 && jsonSchema.title === AST.neverKeyword.annotations[AST.TitleAnnotationId];
|
|
609
|
+
}
|
|
610
|
+
function isAny(jsonSchema) {
|
|
611
|
+
return "$id" in jsonSchema && jsonSchema.$id === constAny.$id;
|
|
612
|
+
}
|
|
613
|
+
function isUnknown(jsonSchema) {
|
|
614
|
+
return "$id" in jsonSchema && jsonSchema.$id === constUnknown.$id;
|
|
615
|
+
}
|
|
616
|
+
function isVoid(jsonSchema) {
|
|
617
|
+
return "$id" in jsonSchema && jsonSchema.$id === constVoid.$id;
|
|
618
|
+
}
|
|
619
|
+
function isCompactableLiteral(jsonSchema) {
|
|
620
|
+
return Predicate.hasProperty(jsonSchema, "enum") && "type" in jsonSchema && Object.keys(jsonSchema).length === 2;
|
|
621
|
+
}
|
|
622
|
+
function compactUnion(members) {
|
|
623
|
+
const out = [];
|
|
624
|
+
for (const m of members) {
|
|
625
|
+
if (isNeverWithoutCustomAnnotations(m)) continue;
|
|
626
|
+
if (isAny(m) || isUnknown(m) || isVoid(m)) return [m];
|
|
627
|
+
if (isCompactableLiteral(m) && out.length > 0) {
|
|
628
|
+
const last = out[out.length - 1];
|
|
629
|
+
if (isCompactableLiteral(last) && last.type === m.type) {
|
|
630
|
+
out[out.length - 1] = {
|
|
631
|
+
type: last.type,
|
|
632
|
+
enum: [...last.enum, ...m.enum]
|
|
633
|
+
};
|
|
634
|
+
continue;
|
|
635
|
+
}
|
|
636
|
+
}
|
|
637
|
+
out.push(m);
|
|
638
|
+
}
|
|
639
|
+
return out;
|
|
640
|
+
}
|
|
698
641
|
//# sourceMappingURL=JSONSchema.js.map
|