effect 3.17.9 → 3.17.11

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.
Files changed (67) hide show
  1. package/dist/cjs/Arbitrary.js +5 -2
  2. package/dist/cjs/Arbitrary.js.map +1 -1
  3. package/dist/cjs/Either.js.map +1 -1
  4. package/dist/cjs/JSONSchema.js +307 -364
  5. package/dist/cjs/JSONSchema.js.map +1 -1
  6. package/dist/cjs/ParseResult.js +1 -1
  7. package/dist/cjs/ParseResult.js.map +1 -1
  8. package/dist/cjs/Predicate.js +1 -1
  9. package/dist/cjs/Predicate.js.map +1 -1
  10. package/dist/cjs/Schema.js +17 -14
  11. package/dist/cjs/Schema.js.map +1 -1
  12. package/dist/cjs/SchemaAST.js +27 -25
  13. package/dist/cjs/SchemaAST.js.map +1 -1
  14. package/dist/cjs/internal/core-effect.js +6 -6
  15. package/dist/cjs/internal/core-effect.js.map +1 -1
  16. package/dist/cjs/internal/fiber.js +15 -1
  17. package/dist/cjs/internal/fiber.js.map +1 -1
  18. package/dist/cjs/internal/fiberRuntime.js +40 -16
  19. package/dist/cjs/internal/fiberRuntime.js.map +1 -1
  20. package/dist/cjs/internal/version.js +1 -1
  21. package/dist/cjs/internal/version.js.map +1 -1
  22. package/dist/dts/Effect.d.ts +4 -4
  23. package/dist/dts/Either.d.ts +96 -96
  24. package/dist/dts/Either.d.ts.map +1 -1
  25. package/dist/dts/JSONSchema.d.ts.map +1 -1
  26. package/dist/dts/Predicate.d.ts.map +1 -1
  27. package/dist/dts/STM.d.ts +2 -2
  28. package/dist/dts/Schema.d.ts.map +1 -1
  29. package/dist/dts/SchemaAST.d.ts +2 -0
  30. package/dist/dts/SchemaAST.d.ts.map +1 -1
  31. package/dist/dts/internal/fiber.d.ts.map +1 -1
  32. package/dist/dts/internal/fiberRuntime.d.ts.map +1 -1
  33. package/dist/esm/Arbitrary.js +5 -2
  34. package/dist/esm/Arbitrary.js.map +1 -1
  35. package/dist/esm/Either.js.map +1 -1
  36. package/dist/esm/JSONSchema.js +307 -364
  37. package/dist/esm/JSONSchema.js.map +1 -1
  38. package/dist/esm/ParseResult.js +1 -1
  39. package/dist/esm/ParseResult.js.map +1 -1
  40. package/dist/esm/Predicate.js +1 -1
  41. package/dist/esm/Predicate.js.map +1 -1
  42. package/dist/esm/Schema.js +17 -14
  43. package/dist/esm/Schema.js.map +1 -1
  44. package/dist/esm/SchemaAST.js +27 -25
  45. package/dist/esm/SchemaAST.js.map +1 -1
  46. package/dist/esm/internal/core-effect.js +6 -6
  47. package/dist/esm/internal/core-effect.js.map +1 -1
  48. package/dist/esm/internal/fiber.js +15 -1
  49. package/dist/esm/internal/fiber.js.map +1 -1
  50. package/dist/esm/internal/fiberRuntime.js +39 -15
  51. package/dist/esm/internal/fiberRuntime.js.map +1 -1
  52. package/dist/esm/internal/version.js +1 -1
  53. package/dist/esm/internal/version.js.map +1 -1
  54. package/package.json +1 -1
  55. package/src/Arbitrary.ts +4 -2
  56. package/src/Effect.ts +4 -4
  57. package/src/Either.ts +132 -132
  58. package/src/JSONSchema.ts +373 -332
  59. package/src/ParseResult.ts +1 -1
  60. package/src/Predicate.ts +2 -1
  61. package/src/STM.ts +2 -2
  62. package/src/Schema.ts +19 -12
  63. package/src/SchemaAST.ts +24 -31
  64. package/src/internal/core-effect.ts +6 -6
  65. package/src/internal/fiber.ts +18 -5
  66. package/src/internal/fiberRuntime.ts +46 -105
  67. package/src/internal/version.ts +1 -1
@@ -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
- "$id": "/schemas/never",
87
- "not": {}
87
+ $id: "/schemas/never",
88
+ not: {}
88
89
  };
89
90
  const constAny = {
90
- "$id": "/schemas/any"
91
+ $id: "/schemas/any"
91
92
  };
92
93
  const constUnknown = {
93
- "$id": "/schemas/unknown"
94
+ $id: "/schemas/unknown"
94
95
  };
95
96
  const constVoid = {
96
- "$id": "/schemas/void"
97
+ $id: "/schemas/void"
97
98
  };
98
- const constAnyObject = {
99
- "$id": "/schemas/object",
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 constEmpty = {
107
- "$id": "/schemas/%7B%7D",
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
- const getJsonSchemaAnnotations = (ast, annotated) => {
116
- annotated ??= ast;
117
- const out = Record.getSomes({
118
- description: AST.getDescriptionAnnotation(annotated),
119
- title: AST.getTitleAnnotation(annotated),
120
- default: AST.getDefaultAnnotation(annotated)
121
- });
122
- const oexamples = AST.getExamplesAnnotation(annotated);
123
- if (Option.isSome(oexamples) && oexamples.value.length > 0) {
124
- const getOption = ParseResult.getOption(ast, false);
125
- const examples = Arr.filterMap(oexamples.value, e => getOption(e));
126
- if (examples.length > 0) {
127
- out.examples = examples;
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 out;
131
- };
132
- const removeDefaultJsonSchemaAnnotations = (jsonSchemaAnnotations, ast) => {
133
- if (jsonSchemaAnnotations["title"] === ast.annotations[AST.TitleAnnotationId]) {
134
- delete jsonSchemaAnnotations["title"];
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 (jsonSchemaAnnotations["description"] === ast.annotations[AST.DescriptionAnnotationId]) {
137
- delete jsonSchemaAnnotations["description"];
158
+ if (Object.keys(out).length === 0) {
159
+ return undefined;
138
160
  }
139
- return jsonSchemaAnnotations;
140
- };
141
- const getASTJsonSchemaAnnotations = ast => {
142
- const jsonSchemaAnnotations = getJsonSchemaAnnotations(ast);
143
- switch (ast._tag) {
144
- case "StringKeyword":
145
- return removeDefaultJsonSchemaAnnotations(jsonSchemaAnnotations, AST.stringKeyword);
146
- case "NumberKeyword":
147
- return removeDefaultJsonSchemaAnnotations(jsonSchemaAnnotations, AST.numberKeyword);
148
- case "BooleanKeyword":
149
- return removeDefaultJsonSchemaAnnotations(jsonSchemaAnnotations, AST.booleanKeyword);
150
- default:
151
- return jsonSchemaAnnotations;
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
- return "type" in jsonSchema || "oneOf" in jsonSchema || "anyOf" in jsonSchema || "const" in jsonSchema || "enum" in jsonSchema || "$ref" in jsonSchema;
162
- };
163
- // Returns true if the schema is an enum with no other properties other than the
164
- // optional "type". This is used to merge enums together.
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, annotations) => {
199
+ const mergeRefinements = (from, jsonSchema, ast) => {
185
200
  const out = {
186
201
  ...from,
187
- ...annotations,
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
- const isNeverJSONSchema = jsonSchema => "$id" in jsonSchema && jsonSchema.$id === "/schemas/never";
251
- const isAnyJSONSchema = jsonSchema => "$id" in jsonSchema && jsonSchema.$id === "/schemas/any";
252
- const isUnknownJSONSchema = jsonSchema => "$id" in jsonSchema && jsonSchema.$id === "/schemas/unknown";
253
- const isVoidJSONSchema = jsonSchema => "$id" in jsonSchema && jsonSchema.$id === "/schemas/void";
254
- const shrink = members => {
255
- let i = members.findIndex(isAnyJSONSchema);
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
- i = members.findIndex(isUnknownJSONSchema);
260
- if (i !== -1) {
261
- members = [members[i]];
253
+ if ("$ref" in jsonSchema) {
254
+ return {
255
+ allOf: [jsonSchema],
256
+ ...annotations
257
+ };
262
258
  }
263
- i = members.findIndex(isVoidJSONSchema);
264
- if (i !== -1) {
265
- members = [members[i]];
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 members;
268
- };
269
- const go = (ast, $defs, handleIdentifier, path, options) => {
270
- if (handleIdentifier) {
271
- const identifier = AST.getJSONIdentifier(ast);
272
- if (Option.isSome(identifier)) {
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, false, path, options);
290
+ $defs[id] = go(ast, $defs, "ignore-identifier", path, options, "handle-annotation", errors);
281
291
  }
282
292
  return out;
283
293
  }
284
294
  }
285
- const hook = AST.getJSONSchemaAnnotation(ast);
286
- if (Option.isSome(hook)) {
287
- const handler = hook.value;
288
- if (AST.isRefinement(ast)) {
289
- const t = AST.getTransformationFrom(ast);
290
- if (t === undefined) {
291
- return mergeRefinements(go(ast.from, $defs, handleIdentifier, path, options), handler, getJsonSchemaAnnotations(ast));
292
- } else if (!isOverrideAnnotation(handler)) {
293
- return go(t, $defs, handleIdentifier, path, options);
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, handleIdentifier, path, options);
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
- throw new Error(errors_.getJSONSchemaMissingAnnotationErrorMessage(path, ast));
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
- if (isNullTypeKeywordSupported(options)) {
316
- // https://json-schema.org/draft-07/draft-handrews-json-schema-validation-00.pdf
317
- // Section 6.1.1
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
- ...getJsonSchemaAnnotations(ast)
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
- ...getJsonSchemaAnnotations(ast)
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
- ...getJsonSchemaAnnotations(ast)
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 "UniqueSymbol":
353
- throw new Error(errors_.getJSONSchemaMissingAnnotationErrorMessage(path, ast));
354
- case "UndefinedKeyword":
355
- throw new Error(errors_.getJSONSchemaMissingAnnotationErrorMessage(path, ast));
356
- case "VoidKeyword":
357
- return {
358
- ...constVoid,
359
- ...getJsonSchemaAnnotations(ast)
360
- };
361
- case "NeverKeyword":
362
- return {
363
- ...constNever,
364
- ...getJsonSchemaAnnotations(ast)
365
- };
366
- case "UnknownKeyword":
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
- ...go(e.type, $defs, true, path.concat(i), options),
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, true, path, options);
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, true, path, options);
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, true, path, options);
488
- propertyNames = go(parameter, $defs, true, path, options);
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, true, indexSignaturePath, options);
495
- propertyNames = go(parameter, $defs, true, indexSignaturePath, options);
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
- for (const type of ast.types) {
544
- const jsonSchema = go(type, $defs, true, path, options);
545
- if (!isNeverJSONSchema(jsonSchema)) {
546
- const last = members[members.length - 1];
547
- if (isMergeableEnum(jsonSchema) && last !== undefined && isMergeableEnum(last)) {
548
- members[members.length - 1] = {
549
- enum: last.enum.concat(jsonSchema.enum)
550
- };
551
- } else {
552
- members.push(jsonSchema);
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
- ...getJsonSchemaAnnotations(ast)
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, handleIdentifier, path, options);
572
+ out["contentSchema"] = go(ast.to, $defs, identifier, path, options, "handle-annotation", errors);
668
573
  }
669
574
  return out;
670
575
  }
671
- let next = ast.from;
672
- if (AST.isTypeLiteralTransformation(ast.transformation)) {
673
- // Annotations from the transformation are applied unless there are user-defined annotations on the form side,
674
- // ensuring that the user's intended annotations are included in the generated schema.
675
- const identifier = AST.getIdentifierAnnotation(ast);
676
- if (Option.isSome(identifier) && Option.isNone(AST.getIdentifierAnnotation(next))) {
677
- next = AST.annotations(next, {
678
- [AST.IdentifierAnnotationId]: identifier.value
679
- });
680
- }
681
- const title = AST.getTitleAnnotation(ast);
682
- if (Option.isSome(title) && Option.isNone(AST.getTitleAnnotation(next))) {
683
- next = AST.annotations(next, {
684
- [AST.TitleAnnotationId]: title.value
685
- });
686
- }
687
- const description = AST.getDescriptionAnnotation(ast);
688
- if (Option.isSome(description) && Option.isNone(AST.getDescriptionAnnotation(next))) {
689
- next = AST.annotations(next, {
690
- [AST.DescriptionAnnotationId]: description.value
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 go(next, $defs, handleIdentifier, path, options);
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