@effect/schema
Advanced tools
Comparing version 0.0.0-snapshot-78b767c2b1625186e17131761a0edbac25d21850 to 0.0.0-snapshot-79dc882eafd6ddb60780764e2f663a6087b52622
@@ -7,3 +7,3 @@ "use strict"; | ||
exports.makeLazy = exports.make = exports.getConstraints = exports.combineConstraints = exports.arbitrary = exports.StringConstraints = exports.NumberConstraints = exports.IntegerConstraints = exports.BigIntConstraints = exports.ArrayConstraints = exports.ArbitraryHookId = void 0; | ||
var Array = /*#__PURE__*/_interopRequireWildcard( /*#__PURE__*/require("effect/Array")); | ||
var Arr = /*#__PURE__*/_interopRequireWildcard( /*#__PURE__*/require("effect/Array")); | ||
var Option = /*#__PURE__*/_interopRequireWildcard( /*#__PURE__*/require("effect/Option")); | ||
@@ -64,3 +64,3 @@ var Predicate = /*#__PURE__*/_interopRequireWildcard( /*#__PURE__*/require("effect/Predicate")); | ||
exports.arbitrary = arbitrary; | ||
const makeLazy = schema => go(schema.ast, {}); | ||
const makeLazy = schema => go(schema.ast, {}, []); | ||
/** | ||
@@ -91,3 +91,3 @@ * Returns a fast-check Arbitrary for the `A` type of the provided schema. | ||
const getHook = /*#__PURE__*/AST.getAnnotation(ArbitraryHookId); | ||
const getRefinementFromArbitrary = (ast, options) => { | ||
const getRefinementFromArbitrary = (ast, options, path) => { | ||
const constraints = combineConstraints(options.constraints, getConstraints(ast)); | ||
@@ -97,5 +97,6 @@ return go(ast.from, constraints ? { | ||
constraints | ||
} : options); | ||
} : options, path); | ||
}; | ||
const go = (ast, options) => { | ||
const getArbitraryErrorMessage = (message, path) => errors_.getErrorMessageWithPath(`cannot build an Arbitrary for ${message}`, path); | ||
const go = (ast, options, path) => { | ||
const hook = getHook(ast); | ||
@@ -105,5 +106,5 @@ if (Option.isSome(hook)) { | ||
case "Declaration": | ||
return hook.value(...ast.typeParameters.map(p => go(p, options))); | ||
return hook.value(...ast.typeParameters.map(p => go(p, options, path))); | ||
case "Refinement": | ||
return hook.value(getRefinementFromArbitrary(ast, options)); | ||
return hook.value(getRefinementFromArbitrary(ast, options, path)); | ||
default: | ||
@@ -116,3 +117,3 @@ return hook.value(); | ||
{ | ||
throw new Error(errors_.getArbitraryErrorMessage(`a declaration without annotations (${ast})`)); | ||
throw new Error(getArbitraryErrorMessage(`a declaration without annotations (${ast})`, path)); | ||
} | ||
@@ -128,3 +129,3 @@ case "Literal": | ||
return () => { | ||
throw new Error(errors_.getArbitraryErrorMessage("`never`")); | ||
throw new Error(getArbitraryErrorMessage("`never`", path)); | ||
}; | ||
@@ -197,4 +198,5 @@ case "UnknownKeyword": | ||
let hasOptionals = false; | ||
let i = 0; | ||
for (const element of ast.elements) { | ||
elements.push(go(element.type, options)); | ||
elements.push(go(element.type, options, path.concat(i++))); | ||
if (element.isOptional) { | ||
@@ -204,3 +206,3 @@ hasOptionals = true; | ||
} | ||
const rest = ast.rest.map(e => go(e, options)); | ||
const rest = ast.rest.map(e => go(e, options, path)); | ||
return fc => { | ||
@@ -225,3 +227,3 @@ // --------------------------------------------- | ||
// --------------------------------------------- | ||
if (Array.isNonEmptyReadonlyArray(rest)) { | ||
if (Arr.isNonEmptyReadonlyArray(rest)) { | ||
const [head, ...tail] = rest; | ||
@@ -256,4 +258,4 @@ const arb = head(fc); | ||
{ | ||
const propertySignaturesTypes = ast.propertySignatures.map(f => go(f.type, options)); | ||
const indexSignatures = ast.indexSignatures.map(is => [go(is.parameter, options), go(is.type, options)]); | ||
const propertySignaturesTypes = ast.propertySignatures.map(ps => go(ps.type, options, path.concat(ps.name))); | ||
const indexSignatures = ast.indexSignatures.map(is => [go(is.parameter, options, path), go(is.type, options, path)]); | ||
return fc => { | ||
@@ -294,3 +296,3 @@ const arbs = {}; | ||
{ | ||
const types = ast.types.map(t => go(t, options)); | ||
const types = ast.types.map(t => go(t, options, path)); | ||
return fc => fc.oneof({ | ||
@@ -303,3 +305,3 @@ depthSize | ||
if (ast.enums.length === 0) { | ||
throw new Error(errors_.getArbitraryErrorMessage("an empty enum")); | ||
throw new Error(getArbitraryErrorMessage("an empty enum", path)); | ||
} | ||
@@ -310,3 +312,3 @@ return fc => fc.oneof(...ast.enums.map(([_, value]) => fc.constant(value))); | ||
{ | ||
const from = getRefinementFromArbitrary(ast, options); | ||
const from = getRefinementFromArbitrary(ast, options, path); | ||
return fc => from(fc).filter(a => Option.isNone(ast.filter(a, AST.defaultParseOption, ast))); | ||
@@ -319,7 +321,7 @@ } | ||
isSuspend: true | ||
})); | ||
}, path)); | ||
return fc => fc.constant(null).chain(() => get()(fc)); | ||
} | ||
case "Transformation": | ||
return go(ast.to, options); | ||
return go(ast.to, options, path); | ||
} | ||
@@ -326,0 +328,0 @@ }; |
@@ -7,3 +7,3 @@ "use strict"; | ||
exports.formatIssueSync = exports.formatIssue = exports.formatErrorSync = exports.formatError = void 0; | ||
var Array = /*#__PURE__*/_interopRequireWildcard( /*#__PURE__*/require("effect/Array")); | ||
var Arr = /*#__PURE__*/_interopRequireWildcard( /*#__PURE__*/require("effect/Array")); | ||
var Effect = /*#__PURE__*/_interopRequireWildcard( /*#__PURE__*/require("effect/Effect")); | ||
@@ -64,10 +64,12 @@ var TreeFormatter = /*#__PURE__*/_interopRequireWildcard( /*#__PURE__*/require("./TreeFormatter.js")); | ||
exports.formatErrorSync = formatErrorSync; | ||
const succeed = issue => Effect.succeed([issue]); | ||
const getArray = (issue, path, onFailure) => Effect.matchEffect(TreeFormatter.getMessage(issue), { | ||
onFailure, | ||
onSuccess: message => Effect.succeed([{ | ||
onSuccess: message => succeed({ | ||
_tag: issue._tag, | ||
path, | ||
message | ||
}]) | ||
}) | ||
}); | ||
const flatten = eff => Effect.map(eff, Arr.flatten); | ||
const go = (e, path = []) => { | ||
@@ -83,21 +85,21 @@ const _tag = e._tag; | ||
case "Forbidden": | ||
return Effect.succeed([{ | ||
return succeed({ | ||
_tag, | ||
path, | ||
message: TreeFormatter.formatForbiddenMessage(e) | ||
}]); | ||
}); | ||
case "Unexpected": | ||
return Effect.succeed([{ | ||
return succeed({ | ||
_tag, | ||
path, | ||
message: `is unexpected, expected ${e.ast.toString(true)}` | ||
}]); | ||
}); | ||
case "Missing": | ||
return Effect.succeed([{ | ||
return succeed({ | ||
_tag, | ||
path, | ||
message: "is missing" | ||
}]); | ||
}); | ||
case "Union": | ||
return getArray(e, path, () => Effect.map(Effect.forEach(e.errors, e => { | ||
return getArray(e, path, () => flatten(Effect.forEach(e.errors, e => { | ||
switch (e._tag) { | ||
@@ -109,10 +111,10 @@ case "Member": | ||
} | ||
}), Array.flatten)); | ||
}))); | ||
case "TupleType": | ||
return getArray(e, path, () => Effect.map(Effect.forEach(e.errors, index => go(index.error, [...path, index.index])), Array.flatten)); | ||
return getArray(e, path, () => flatten(Effect.forEach(e.errors, index => go(index.error, path.concat(index.index))))); | ||
case "TypeLiteral": | ||
return getArray(e, path, () => Effect.map(Effect.forEach(e.errors, key => go(key.error, [...path, key.key])), Array.flatten)); | ||
return getArray(e, path, () => flatten(Effect.forEach(e.errors, key => go(key.error, path.concat(key.key))))); | ||
case "Declaration": | ||
case "Refinement": | ||
case "Transformation": | ||
case "Refinement": | ||
case "Declaration": | ||
return getArray(e, path, () => go(e.error, path)); | ||
@@ -119,0 +121,0 @@ } |
@@ -7,3 +7,3 @@ "use strict"; | ||
exports.make = exports.equivalence = exports.EquivalenceHookId = void 0; | ||
var Array = /*#__PURE__*/_interopRequireWildcard( /*#__PURE__*/require("effect/Array")); | ||
var Arr = /*#__PURE__*/_interopRequireWildcard( /*#__PURE__*/require("effect/Array")); | ||
var Equal = /*#__PURE__*/_interopRequireWildcard( /*#__PURE__*/require("effect/Equal")); | ||
@@ -63,6 +63,7 @@ var Equivalence = /*#__PURE__*/_interopRequireWildcard( /*#__PURE__*/require("effect/Equivalence")); | ||
exports.equivalence = equivalence; | ||
const make = schema => go(schema.ast); | ||
const make = schema => go(schema.ast, []); | ||
exports.make = make; | ||
const getHook = /*#__PURE__*/AST.getAnnotation(EquivalenceHookId); | ||
const go = ast => { | ||
const getEquivalenceErrorMessage = (message, path) => errors_.getErrorMessageWithPath(`cannot build an Equivalence for ${message}`, path); | ||
const go = (ast, path) => { | ||
const hook = getHook(ast); | ||
@@ -72,5 +73,5 @@ if (Option.isSome(hook)) { | ||
case "Declaration": | ||
return hook.value(...ast.typeParameters.map(go)); | ||
return hook.value(...ast.typeParameters.map(tp => go(tp, path))); | ||
case "Refinement": | ||
return hook.value(go(ast.from)); | ||
return hook.value(go(ast.from, path)); | ||
default: | ||
@@ -82,5 +83,5 @@ return hook.value(); | ||
case "NeverKeyword": | ||
throw new Error(errors_.getEquivalenceErrorMessage("`never`")); | ||
throw new Error(getEquivalenceErrorMessage("`never`", path)); | ||
case "Transformation": | ||
return go(ast.to); | ||
return go(ast.to, path); | ||
case "Declaration": | ||
@@ -103,6 +104,6 @@ case "Literal": | ||
case "Refinement": | ||
return go(ast.from); | ||
return go(ast.from, path); | ||
case "Suspend": | ||
{ | ||
const get = util_.memoizeThunk(() => go(ast.f())); | ||
const get = util_.memoizeThunk(() => go(ast.f(), path)); | ||
return (a, b) => get()(a, b); | ||
@@ -112,4 +113,4 @@ } | ||
{ | ||
const elements = ast.elements.map(element => go(element.type)); | ||
const rest = ast.rest.map(go); | ||
const elements = ast.elements.map((element, i) => go(element.type, path.concat(i))); | ||
const rest = ast.rest.map(ast => go(ast, path)); | ||
return Equivalence.make((a, b) => { | ||
@@ -132,3 +133,3 @@ const len = a.length; | ||
// --------------------------------------------- | ||
if (Array.isNonEmptyReadonlyArray(rest)) { | ||
if (Arr.isNonEmptyReadonlyArray(rest)) { | ||
const [head, ...tail] = rest; | ||
@@ -158,4 +159,4 @@ for (; i < len - tail.length; i++) { | ||
} | ||
const propertySignatures = ast.propertySignatures.map(ps => go(ps.type)); | ||
const indexSignatures = ast.indexSignatures.map(is => go(is.type)); | ||
const propertySignatures = ast.propertySignatures.map(ps => go(ps.type, path.concat(ps.name))); | ||
const indexSignatures = ast.indexSignatures.map(is => go(is.type, path)); | ||
return Equivalence.make((a, b) => { | ||
@@ -234,3 +235,3 @@ const aStringKeys = Object.keys(a); | ||
} | ||
const tuples = candidates.map(ast => [go(ast), ParseResult.is({ | ||
const tuples = candidates.map(ast => [go(ast, path), ParseResult.is({ | ||
ast | ||
@@ -237,0 +238,0 @@ })]); |
@@ -6,3 +6,3 @@ "use strict"; | ||
}); | ||
exports.getRequiredElementFollowinAnOptionalElementErrorMessage = exports.getPrettyErrorMessage = exports.getIndexSignatureParameterErrorMessage = exports.getEquivalenceErrorMessage = exports.getDuplicatePropertySignatureTransformationErrorMessage = exports.getDuplicatePropertySignatureErrorMessage = exports.getDuplicateIndexSignatureErrorMessage = exports.getArbitraryErrorMessage = exports.getAPIErrorMessage = void 0; | ||
exports.getErrorMessageWithPath = exports.getErrorMessage = exports.getDuplicatePropertySignatureErrorMessage = void 0; | ||
var util_ = /*#__PURE__*/_interopRequireWildcard( /*#__PURE__*/require("./util.js")); | ||
@@ -38,23 +38,13 @@ function _getRequireWildcardCache(e) { | ||
exports.getDuplicatePropertySignatureErrorMessage = getDuplicatePropertySignatureErrorMessage; | ||
const getDuplicateIndexSignatureErrorMessage = name => `Duplicate index signature for type \`${name}\``; | ||
const getErrorMessage = (api, message) => `${api}: ${message}`; | ||
/** @internal */ | ||
exports.getDuplicateIndexSignatureErrorMessage = getDuplicateIndexSignatureErrorMessage; | ||
const getIndexSignatureParameterErrorMessage = exports.getIndexSignatureParameterErrorMessage = "An index signature parameter type must be `string`, `symbol`, a template literal type or a refinement of the previous types"; | ||
/** @internal */ | ||
const getRequiredElementFollowinAnOptionalElementErrorMessage = exports.getRequiredElementFollowinAnOptionalElementErrorMessage = "A required element cannot follow an optional element. ts(1257)"; | ||
/** @internal */ | ||
const getDuplicatePropertySignatureTransformationErrorMessage = name => `Duplicate property signature transformation ${util_.formatUnknown(name)}`; | ||
/** @internal */ | ||
exports.getDuplicatePropertySignatureTransformationErrorMessage = getDuplicatePropertySignatureTransformationErrorMessage; | ||
const getArbitraryErrorMessage = message => `cannot build an Arbitrary for ${message}`; | ||
/** @internal */ | ||
exports.getArbitraryErrorMessage = getArbitraryErrorMessage; | ||
const getPrettyErrorMessage = message => `cannot build a Pretty for ${message}`; | ||
/** @internal */ | ||
exports.getPrettyErrorMessage = getPrettyErrorMessage; | ||
const getEquivalenceErrorMessage = message => `cannot build an Equivalence for ${message}`; | ||
/** @internal */ | ||
exports.getEquivalenceErrorMessage = getEquivalenceErrorMessage; | ||
const getAPIErrorMessage = (api, message) => `${api}: ${message}`; | ||
exports.getAPIErrorMessage = getAPIErrorMessage; | ||
exports.getErrorMessage = getErrorMessage; | ||
const getErrorMessageWithPath = (message, path) => { | ||
let out = message; | ||
if (path.length > 0) { | ||
out += ` (path [${path.map(util_.formatPropertyKey).join(", ")}])`; | ||
} | ||
return out; | ||
}; | ||
exports.getErrorMessageWithPath = getErrorMessageWithPath; | ||
//# sourceMappingURL=errors.js.map |
@@ -6,3 +6,3 @@ "use strict"; | ||
}); | ||
exports.ownKeys = exports.memoizeThunk = exports.getKeysForIndexSignature = exports.formatUnknown = void 0; | ||
exports.ownKeys = exports.memoizeThunk = exports.getKeysForIndexSignature = exports.formatUnknown = exports.formatPropertyKey = void 0; | ||
var Predicate = /*#__PURE__*/_interopRequireWildcard( /*#__PURE__*/require("effect/Predicate")); | ||
@@ -86,3 +86,6 @@ function _getRequireWildcardCache(e) { | ||
}; | ||
/** @internal */ | ||
exports.formatUnknown = formatUnknown; | ||
const formatPropertyKey = name => typeof name === "string" ? JSON.stringify(name) : String(name); | ||
exports.formatPropertyKey = formatPropertyKey; | ||
//# sourceMappingURL=util.js.map |
@@ -11,2 +11,3 @@ "use strict"; | ||
var AST = /*#__PURE__*/_interopRequireWildcard( /*#__PURE__*/require("./AST.js")); | ||
var errors_ = /*#__PURE__*/_interopRequireWildcard( /*#__PURE__*/require("./internal/errors.js")); | ||
function _getRequireWildcardCache(e) { | ||
@@ -47,3 +48,3 @@ if ("function" != typeof WeakMap) return null; | ||
const $defs = {}; | ||
const jsonSchema = go(schema.ast, $defs); | ||
const jsonSchema = go(schema.ast, $defs, true, []); | ||
const out = { | ||
@@ -88,3 +89,3 @@ $schema, | ||
const $schema = "http://json-schema.org/draft-07/schema#"; | ||
const getMeta = annotated => Record.getSomes({ | ||
const getJsonSchemaAnnotations = annotated => Record.getSomes({ | ||
description: AST.getDescriptionAnnotation(annotated), | ||
@@ -102,36 +103,49 @@ title: AST.getTitleAnnotation(annotated), | ||
}; | ||
const getMissingAnnotationError = name => { | ||
const out = new Error(`cannot build a JSON Schema for ${name} without a JSON Schema annotation`); | ||
out.name = "MissingAnnotation"; | ||
return out; | ||
}; | ||
const getUnsupportedIndexSignatureParameterErrorMessage = parameter => `Unsupported index signature parameter (${parameter})`; | ||
const getMissingAnnotationErrorMessage = (name, path) => errors_.getErrorMessageWithPath(`cannot build a JSON Schema for ${name} without a JSON Schema annotation`, path); | ||
const getUnsupportedIndexSignatureParameterErrorMessage = (parameter, path) => errors_.getErrorMessageWithPath(`unsupported index signature parameter (${parameter})`, path); | ||
/** @internal */ | ||
const DEFINITION_PREFIX = exports.DEFINITION_PREFIX = "#/$defs/"; | ||
const get$ref = id => `${DEFINITION_PREFIX}${id}`; | ||
const go = (ast, $defs, handleIdentifier = true) => { | ||
const hasTransformation = ast => { | ||
switch (ast.from._tag) { | ||
case "Transformation": | ||
return true; | ||
case "Refinement": | ||
return hasTransformation(ast.from); | ||
case "Suspend": | ||
{ | ||
const from = ast.from.f(); | ||
if (AST.isRefinement(from)) { | ||
return hasTransformation(from); | ||
} | ||
} | ||
break; | ||
} | ||
return false; | ||
}; | ||
const go = (ast, $defs, handleIdentifier, path) => { | ||
const hook = AST.getJSONSchemaAnnotation(ast); | ||
if (Option.isSome(hook)) { | ||
const handler = hook.value; | ||
switch (ast._tag) { | ||
case "Refinement": | ||
try { | ||
return { | ||
...go(ast.from, $defs), | ||
...getMeta(ast), | ||
...handler | ||
}; | ||
} catch (e) { | ||
if (e instanceof Error && e.name === "MissingAnnotation") { | ||
return { | ||
...getMeta(ast), | ||
...handler | ||
}; | ||
} | ||
throw e; | ||
} | ||
if (AST.isRefinement(ast) && !hasTransformation(ast)) { | ||
try { | ||
return { | ||
...go(ast.from, $defs, true, path), | ||
...getJsonSchemaAnnotations(ast), | ||
...handler | ||
}; | ||
} catch (e) { | ||
return { | ||
...getJsonSchemaAnnotations(ast), | ||
...handler | ||
}; | ||
} | ||
} | ||
return handler; | ||
} | ||
if (handleIdentifier) { | ||
const surrogate = AST.getSurrogateAnnotation(ast); | ||
if (Option.isSome(surrogate)) { | ||
return go(surrogate.value, $defs, handleIdentifier, path); | ||
} | ||
if (handleIdentifier && !AST.isTransformation(ast)) { | ||
const identifier = AST.getJSONIdentifier(ast); | ||
@@ -145,3 +159,3 @@ if (Option.isSome(identifier)) { | ||
$defs[id] = out; | ||
$defs[id] = go(ast, $defs, false); | ||
$defs[id] = go(ast, $defs, false, path); | ||
} | ||
@@ -153,3 +167,3 @@ return out; | ||
case "Declaration": | ||
throw getMissingAnnotationError("a declaration"); | ||
throw new Error(getMissingAnnotationErrorMessage("a declaration", path)); | ||
case "Literal": | ||
@@ -161,3 +175,3 @@ { | ||
const: null, | ||
...getMeta(ast) | ||
...getJsonSchemaAnnotations(ast) | ||
}; | ||
@@ -167,3 +181,3 @@ } else if (Predicate.isString(literal)) { | ||
const: literal, | ||
...getMeta(ast) | ||
...getJsonSchemaAnnotations(ast) | ||
}; | ||
@@ -173,3 +187,3 @@ } else if (Predicate.isNumber(literal)) { | ||
const: literal, | ||
...getMeta(ast) | ||
...getJsonSchemaAnnotations(ast) | ||
}; | ||
@@ -179,19 +193,19 @@ } else if (Predicate.isBoolean(literal)) { | ||
const: literal, | ||
...getMeta(ast) | ||
...getJsonSchemaAnnotations(ast) | ||
}; | ||
} | ||
throw getMissingAnnotationError("a bigint literal"); | ||
throw new Error(getMissingAnnotationErrorMessage("a bigint literal", path)); | ||
} | ||
case "UniqueSymbol": | ||
throw getMissingAnnotationError("a unique symbol"); | ||
throw new Error(getMissingAnnotationErrorMessage("a unique symbol", path)); | ||
case "UndefinedKeyword": | ||
throw getMissingAnnotationError("`undefined`"); | ||
throw new Error(getMissingAnnotationErrorMessage("`undefined`", path)); | ||
case "VoidKeyword": | ||
throw getMissingAnnotationError("`void`"); | ||
throw new Error(getMissingAnnotationErrorMessage("`void`", path)); | ||
case "NeverKeyword": | ||
throw getMissingAnnotationError("`never`"); | ||
throw new Error(getMissingAnnotationErrorMessage("`never`", path)); | ||
case "UnknownKeyword": | ||
return { | ||
...unknownJsonSchema, | ||
...getMeta(ast) | ||
...getJsonSchemaAnnotations(ast) | ||
}; | ||
@@ -201,3 +215,3 @@ case "AnyKeyword": | ||
...anyJsonSchema, | ||
...getMeta(ast) | ||
...getJsonSchemaAnnotations(ast) | ||
}; | ||
@@ -207,3 +221,3 @@ case "ObjectKeyword": | ||
...objectJsonSchema, | ||
...getMeta(ast) | ||
...getJsonSchemaAnnotations(ast) | ||
}; | ||
@@ -213,3 +227,3 @@ case "StringKeyword": | ||
type: "string", | ||
...getMeta(ast) | ||
...getJsonSchemaAnnotations(ast) | ||
}; | ||
@@ -219,3 +233,3 @@ case "NumberKeyword": | ||
type: "number", | ||
...getMeta(ast) | ||
...getJsonSchemaAnnotations(ast) | ||
}; | ||
@@ -225,12 +239,13 @@ case "BooleanKeyword": | ||
type: "boolean", | ||
...getMeta(ast) | ||
...getJsonSchemaAnnotations(ast) | ||
}; | ||
case "BigIntKeyword": | ||
throw getMissingAnnotationError("`bigint`"); | ||
throw new Error(getMissingAnnotationErrorMessage("`bigint`", path)); | ||
case "SymbolKeyword": | ||
throw getMissingAnnotationError("`symbol`"); | ||
throw new Error(getMissingAnnotationErrorMessage("`symbol`", path)); | ||
case "TupleType": | ||
{ | ||
const elements = ast.elements.map(e => go(e.type, $defs)); | ||
const rest = ast.rest.map(ast => go(ast, $defs)); | ||
const len = ast.elements.length; | ||
const elements = ast.elements.map((e, i) => go(e.type, $defs, true, path.concat(i))); | ||
const rest = ast.rest.map(ast => go(ast, $defs, true, path)); | ||
const output = { | ||
@@ -242,3 +257,2 @@ type: "array" | ||
// --------------------------------------------- | ||
const len = elements.length; | ||
if (len > 0) { | ||
@@ -262,3 +276,3 @@ output.minItems = len - ast.elements.filter(element => element.isOptional).length; | ||
if (rest.length > 1) { | ||
throw new Error("Generating a JSON Schema for post-rest elements is not currently supported. You're welcome to contribute by submitting a Pull Request."); | ||
throw new Error(errors_.getErrorMessageWithPath("Generating a JSON Schema for post-rest elements is not currently supported. You're welcome to contribute by submitting a Pull Request.", path)); | ||
} | ||
@@ -274,3 +288,3 @@ } else { | ||
...output, | ||
...getMeta(ast) | ||
...getJsonSchemaAnnotations(ast) | ||
}; | ||
@@ -283,3 +297,3 @@ } | ||
...empty(), | ||
...getMeta(ast) | ||
...getJsonSchemaAnnotations(ast) | ||
}; | ||
@@ -294,3 +308,3 @@ } | ||
{ | ||
additionalProperties = go(is.type, $defs); | ||
additionalProperties = go(is.type, $defs, true, path); | ||
break; | ||
@@ -301,3 +315,3 @@ } | ||
patternProperties = { | ||
[AST.getTemplateLiteralRegExp(parameter).source]: go(is.type, $defs) | ||
[AST.getTemplateLiteralRegExp(parameter).source]: go(is.type, $defs, true, path) | ||
}; | ||
@@ -311,10 +325,10 @@ break; | ||
patternProperties = { | ||
[hook.value.pattern]: go(is.type, $defs) | ||
[hook.value.pattern]: go(is.type, $defs, true, path) | ||
}; | ||
break; | ||
} | ||
throw new Error(getUnsupportedIndexSignatureParameterErrorMessage(parameter)); | ||
throw new Error(getUnsupportedIndexSignatureParameterErrorMessage(parameter, path)); | ||
} | ||
case "SymbolKeyword": | ||
throw new Error(getUnsupportedIndexSignatureParameterErrorMessage(parameter)); | ||
throw new Error(getUnsupportedIndexSignatureParameterErrorMessage(parameter, path)); | ||
} | ||
@@ -324,4 +338,4 @@ } | ||
return { | ||
...go(pruneUndefinedKeyword(ps), $defs), | ||
...getMeta(ps) | ||
...go(pruneUndefinedKeyword(ps), $defs, true, path.concat(ps.name)), | ||
...getJsonSchemaAnnotations(ps) | ||
}; | ||
@@ -349,3 +363,3 @@ }); | ||
} else { | ||
throw new Error(`cannot encode ${String(name)} key to JSON Schema`); | ||
throw new Error(errors_.getErrorMessageWithPath(`cannot encode ${String(name)} key to JSON Schema`, path)); | ||
} | ||
@@ -364,3 +378,3 @@ } | ||
...output, | ||
...getMeta(ast) | ||
...getJsonSchemaAnnotations(ast) | ||
}; | ||
@@ -373,3 +387,3 @@ } | ||
for (const type of ast.types) { | ||
const schema = go(type, $defs); | ||
const schema = go(type, $defs, true, path); | ||
if ("const" in schema) { | ||
@@ -389,3 +403,3 @@ if (Object.keys(schema).length > 1) { | ||
const: enums[0], | ||
...getMeta(ast) | ||
...getJsonSchemaAnnotations(ast) | ||
}; | ||
@@ -395,3 +409,3 @@ } else { | ||
enum: enums, | ||
...getMeta(ast) | ||
...getJsonSchemaAnnotations(ast) | ||
}; | ||
@@ -411,3 +425,3 @@ } | ||
anyOf, | ||
...getMeta(ast) | ||
...getJsonSchemaAnnotations(ast) | ||
}; | ||
@@ -424,3 +438,3 @@ } | ||
})), | ||
...getMeta(ast) | ||
...getJsonSchemaAnnotations(ast) | ||
}; | ||
@@ -430,3 +444,3 @@ } | ||
{ | ||
throw new Error("cannot build a JSON Schema for a refinement without a JSON Schema annotation"); | ||
throw new Error(errors_.getErrorMessageWithPath("cannot build a JSON Schema for a refinement without a JSON Schema annotation", path)); | ||
} | ||
@@ -440,3 +454,3 @@ case "TemplateLiteral": | ||
pattern: regex.source, | ||
...getMeta(ast) | ||
...getJsonSchemaAnnotations(ast) | ||
}; | ||
@@ -448,10 +462,10 @@ } | ||
if (Option.isNone(identifier)) { | ||
throw new Error("Generating a JSON Schema for suspended schemas requires an identifier annotation"); | ||
throw new Error(errors_.getErrorMessageWithPath("Generating a JSON Schema for suspended schemas requires an identifier annotation", path)); | ||
} | ||
return go(ast.f(), $defs); | ||
return go(ast.f(), $defs, true, path); | ||
} | ||
case "Transformation": | ||
return go(ast.to, $defs); | ||
return go(ast.from, $defs, true, path); | ||
} | ||
}; | ||
//# sourceMappingURL=JSONSchema.js.map |
@@ -7,3 +7,3 @@ "use strict"; | ||
exports.validateSync = exports.validatePromise = exports.validateOption = exports.validateEither = exports.validate = exports.try = exports.succeed = exports.parseError = exports.orElse = exports.missing = exports.mergeParseOptions = exports.mapError = exports.mapBoth = exports.map = exports.is = exports.getSearchTree = exports.getLiterals = exports.getFinalTransformation = exports.fromOption = exports.flatMap = exports.fail = exports.encodeUnknownSync = exports.encodeUnknownPromise = exports.encodeUnknownOption = exports.encodeUnknownEither = exports.encodeUnknown = exports.encodeSync = exports.encodePromise = exports.encodeOption = exports.encodeEither = exports.encode = exports.eitherOrUndefined = exports.decodeUnknownSync = exports.decodeUnknownPromise = exports.decodeUnknownOption = exports.decodeUnknownEither = exports.decodeUnknown = exports.decodeSync = exports.decodePromise = exports.decodeOption = exports.decodeEither = exports.decode = exports.asserts = exports.Union = exports.Unexpected = exports.TypeLiteral = exports.Type = exports.TupleType = exports.Transformation = exports.Refinement = exports.ParseError = exports.Missing = exports.Member = exports.Key = exports.Index = exports.Forbidden = exports.Declaration = void 0; | ||
var Array = /*#__PURE__*/_interopRequireWildcard( /*#__PURE__*/require("effect/Array")); | ||
var Arr = /*#__PURE__*/_interopRequireWildcard( /*#__PURE__*/require("effect/Array")); | ||
var _Data = /*#__PURE__*/require("effect/Data"); | ||
@@ -460,12 +460,12 @@ var Effect = /*#__PURE__*/_interopRequireWildcard( /*#__PURE__*/require("effect/Effect")); | ||
/** @internal */ | ||
const mergeParseOptions = (a, b) => { | ||
if (a === undefined) { | ||
return b; | ||
const mergeParseOptions = (options, overrideOptions) => { | ||
if (overrideOptions === undefined || Predicate.isNumber(overrideOptions)) { | ||
return options; | ||
} | ||
if (b === undefined) { | ||
return a; | ||
if (options === undefined) { | ||
return overrideOptions; | ||
} | ||
const out = {}; | ||
out.errors = b.errors ?? a.errors; | ||
out.onExcessProperty = b.onExcessProperty ?? a.onExcessProperty; | ||
out.errors = overrideOptions.errors ?? options.errors; | ||
out.onExcessProperty = overrideOptions.onExcessProperty ?? options.onExcessProperty; | ||
return out; | ||
@@ -480,3 +480,5 @@ }; | ||
const parser = getEither(ast, isDecoding, options); | ||
return (input, overrideOptions) => Either.getOrThrowWith(parser(input, overrideOptions), e => new Error(TreeFormatter.formatIssueSync(e))); | ||
return (input, overrideOptions) => Either.getOrThrowWith(parser(input, overrideOptions), issue => new Error(TreeFormatter.formatIssueSync(issue), { | ||
cause: issue | ||
})); | ||
}; | ||
@@ -642,3 +644,5 @@ const getOption = (ast, isDecoding, options) => { | ||
if (Either.isLeft(result)) { | ||
throw new Error(TreeFormatter.formatIssueSync(result.left)); | ||
throw new Error(TreeFormatter.formatIssueSync(result.left), { | ||
cause: result.left | ||
}); | ||
} | ||
@@ -759,3 +763,3 @@ }; | ||
return (input, options) => { | ||
if (!Array.isArray(input)) { | ||
if (!Arr.isArray(input)) { | ||
return Either.left(new Type(ast, input)); | ||
@@ -850,3 +854,3 @@ } | ||
// --------------------------------------------- | ||
if (Array.isNonEmptyReadonlyArray(rest)) { | ||
if (Arr.isNonEmptyReadonlyArray(rest)) { | ||
const [head, ...tail] = rest; | ||
@@ -948,3 +952,3 @@ for (; i < len - tail.length; i++) { | ||
output | ||
}) => Array.isNonEmptyArray(es) ? Either.left(new TupleType(ast, input, sortByIndex(es), sortByIndex(output))) : Either.right(sortByIndex(output)); | ||
}) => Arr.isNonEmptyArray(es) ? Either.left(new TupleType(ast, input, sortByIndex(es), sortByIndex(output))) : Either.right(sortByIndex(output)); | ||
if (queue && queue.length > 0) { | ||
@@ -954,4 +958,4 @@ const cqueue = queue; | ||
const state = { | ||
es: Array.copy(es), | ||
output: Array.copy(output) | ||
es: Arr.copy(es), | ||
output: Arr.copy(output) | ||
}; | ||
@@ -1145,3 +1149,3 @@ return Effect.flatMap(Effect.forEach(cqueue, f => f(state), { | ||
output | ||
}) => Array.isNonEmptyArray(es) ? Either.left(new TypeLiteral(ast, input, sortByIndex(es), output)) : Either.right(output); | ||
}) => Arr.isNonEmptyArray(es) ? Either.left(new TypeLiteral(ast, input, sortByIndex(es), output)) : Either.right(output); | ||
if (queue && queue.length > 0) { | ||
@@ -1151,3 +1155,3 @@ const cqueue = queue; | ||
const state = { | ||
es: Array.copy(es), | ||
es: Arr.copy(es), | ||
output: Object.assign({}, output) | ||
@@ -1250,3 +1254,3 @@ }; | ||
// --------------------------------------------- | ||
const computeResult = es => Array.isNonEmptyArray(es) ? es.length === 1 && es[0][1]._tag === "Type" ? Either.left(es[0][1]) : Either.left(new Union(ast, input, sortByIndex(es))) : | ||
const computeResult = es => Arr.isNonEmptyArray(es) ? es.length === 1 && es[0][1]._tag === "Type" ? Either.left(es[0][1]) : Either.left(new Union(ast, input, sortByIndex(es))) : | ||
// this should never happen | ||
@@ -1258,3 +1262,3 @@ Either.left(new Type(AST.neverKeyword, input)); | ||
const state = { | ||
es: Array.copy(es) | ||
es: Arr.copy(es) | ||
}; | ||
@@ -1261,0 +1265,0 @@ return Effect.flatMap(Effect.forEach(cqueue, f => f(state), { |
@@ -7,3 +7,3 @@ "use strict"; | ||
exports.pretty = exports.match = exports.make = exports.PrettyHookId = void 0; | ||
var Array = /*#__PURE__*/_interopRequireWildcard( /*#__PURE__*/require("effect/Array")); | ||
var Arr = /*#__PURE__*/_interopRequireWildcard( /*#__PURE__*/require("effect/Array")); | ||
var Option = /*#__PURE__*/_interopRequireWildcard( /*#__PURE__*/require("effect/Option")); | ||
@@ -60,3 +60,3 @@ var AST = /*#__PURE__*/_interopRequireWildcard( /*#__PURE__*/require("./AST.js")); | ||
exports.pretty = pretty; | ||
const make = schema => compile(schema.ast); | ||
const make = schema => compile(schema.ast, []); | ||
exports.make = make; | ||
@@ -71,2 +71,3 @@ const getHook = /*#__PURE__*/AST.getAnnotation(PrettyHookId); | ||
const formatUnknown = /*#__PURE__*/getMatcher(util_.formatUnknown); | ||
const getPrettyErrorMessage = (message, path) => errors_.getErrorMessageWithPath(`cannot build a Pretty for ${message}`, path); | ||
/** | ||
@@ -76,8 +77,8 @@ * @since 1.0.0 | ||
const match = exports.match = { | ||
"Declaration": (ast, go) => { | ||
"Declaration": (ast, go, path) => { | ||
const hook = getHook(ast); | ||
if (Option.isSome(hook)) { | ||
return hook.value(...ast.typeParameters.map(go)); | ||
return hook.value(...ast.typeParameters.map(tp => go(tp, path))); | ||
} | ||
throw new Error(errors_.getPrettyErrorMessage(`a declaration without annotations (${ast})`)); | ||
throw new Error(getPrettyErrorMessage(`a declaration without annotations (${ast})`, path)); | ||
}, | ||
@@ -101,3 +102,3 @@ "VoidKeyword": /*#__PURE__*/getMatcher(() => "void(0)"), | ||
"Enums": stringify, | ||
"TupleType": (ast, go) => { | ||
"TupleType": (ast, go, path) => { | ||
const hook = getHook(ast); | ||
@@ -107,4 +108,4 @@ if (Option.isSome(hook)) { | ||
} | ||
const elements = ast.elements.map(e => go(e.type)); | ||
const rest = ast.rest.map(go); | ||
const elements = ast.elements.map((e, i) => go(e.type, path.concat(i))); | ||
const rest = ast.rest.map(ast => go(ast, path)); | ||
return input => { | ||
@@ -128,3 +129,3 @@ const output = []; | ||
// --------------------------------------------- | ||
if (Array.isNonEmptyReadonlyArray(rest)) { | ||
if (Arr.isNonEmptyReadonlyArray(rest)) { | ||
const [head, ...tail] = rest; | ||
@@ -145,3 +146,3 @@ for (; i < input.length - tail.length; i++) { | ||
}, | ||
"TypeLiteral": (ast, go) => { | ||
"TypeLiteral": (ast, go, path) => { | ||
const hook = getHook(ast); | ||
@@ -151,4 +152,4 @@ if (Option.isSome(hook)) { | ||
} | ||
const propertySignaturesTypes = ast.propertySignatures.map(f => go(f.type)); | ||
const indexSignatureTypes = ast.indexSignatures.map(is => go(is.type)); | ||
const propertySignaturesTypes = ast.propertySignatures.map(ps => go(ps.type, path.concat(ps.name))); | ||
const indexSignatureTypes = ast.indexSignatures.map(is => go(is.type, path)); | ||
const expectedKeys = {}; | ||
@@ -169,3 +170,3 @@ for (let i = 0; i < propertySignaturesTypes.length; i++) { | ||
} | ||
output.push(`${getPrettyPropertyKey(name)}: ${propertySignaturesTypes[i](input[name])}`); | ||
output.push(`${util_.formatPropertyKey(name)}: ${propertySignaturesTypes[i](input[name])}`); | ||
} | ||
@@ -183,10 +184,10 @@ // --------------------------------------------- | ||
} | ||
output.push(`${getPrettyPropertyKey(key)}: ${type(input[key])}`); | ||
output.push(`${util_.formatPropertyKey(key)}: ${type(input[key])}`); | ||
} | ||
} | ||
} | ||
return Array.isNonEmptyReadonlyArray(output) ? "{ " + output.join(", ") + " }" : "{}"; | ||
return Arr.isNonEmptyReadonlyArray(output) ? "{ " + output.join(", ") + " }" : "{}"; | ||
}; | ||
}, | ||
"Union": (ast, go) => { | ||
"Union": (ast, go, path) => { | ||
const hook = getHook(ast); | ||
@@ -198,3 +199,3 @@ if (Option.isSome(hook)) { | ||
ast | ||
}), go(ast)]); | ||
}), go(ast, path)]); | ||
return a => { | ||
@@ -205,6 +206,6 @@ const index = types.findIndex(([is]) => is(a)); | ||
}, | ||
"Suspend": (ast, go) => { | ||
"Suspend": (ast, go, path) => { | ||
return Option.match(getHook(ast), { | ||
onNone: () => { | ||
const get = util_.memoizeThunk(() => go(ast.f())); | ||
const get = util_.memoizeThunk(() => go(ast.f(), path)); | ||
return a => get()(a); | ||
@@ -215,11 +216,11 @@ }, | ||
}, | ||
"Refinement": (ast, go) => { | ||
"Refinement": (ast, go, path) => { | ||
return Option.match(getHook(ast), { | ||
onNone: () => go(ast.from), | ||
onNone: () => go(ast.from, path), | ||
onSome: handler => handler() | ||
}); | ||
}, | ||
"Transformation": (ast, go) => { | ||
"Transformation": (ast, go, path) => { | ||
return Option.match(getHook(ast), { | ||
onNone: () => go(ast.to), | ||
onNone: () => go(ast.to, path), | ||
onSome: handler => handler() | ||
@@ -230,3 +231,2 @@ }); | ||
const compile = /*#__PURE__*/AST.getCompiler(match); | ||
const getPrettyPropertyKey = name => typeof name === "string" ? JSON.stringify(name) : String(name); | ||
//# sourceMappingURL=Pretty.js.map |
@@ -100,3 +100,3 @@ "use strict"; | ||
}; | ||
const getPrevMessage = issue => { | ||
const getInnerMessage = issue => { | ||
switch (issue._tag) { | ||
@@ -111,3 +111,5 @@ case "Refinement": | ||
case "Transformation": | ||
return getMessage(issue.error); | ||
{ | ||
return getMessage(issue.error); | ||
} | ||
} | ||
@@ -118,6 +120,26 @@ return Option.none(); | ||
const out = annotation(issue); | ||
return Predicate.isString(out) ? Effect.succeed(out) : out; | ||
return Predicate.isString(out) ? Effect.succeed({ | ||
message: out, | ||
override: false | ||
}) : Effect.isEffect(out) ? Effect.map(out, message => ({ | ||
message, | ||
override: false | ||
})) : Predicate.isString(out.message) ? Effect.succeed({ | ||
message: out.message, | ||
override: out.override | ||
}) : Effect.map(out.message, message => ({ | ||
message, | ||
override: out.override | ||
})); | ||
})); | ||
/** @internal */ | ||
const getMessage = issue => Effect.catchAll(getPrevMessage(issue), () => getCurrentMessage(issue)); | ||
const getMessage = issue => { | ||
const current = getCurrentMessage(issue); | ||
return getInnerMessage(issue).pipe(Effect.flatMap(inner => Effect.map(current, current => current.override ? current.message : inner)), Effect.catchAll(() => Effect.flatMap(current, current => { | ||
if (!current.override && (issue._tag === "Refinement" && issue.kind !== "Predicate" || issue._tag === "Transformation" && issue.kind !== "Transformation")) { | ||
return Option.none(); | ||
} | ||
return Effect.succeed(current.message); | ||
}))); | ||
}; | ||
exports.getMessage = getMessage; | ||
@@ -156,5 +178,5 @@ const getParseIssueTitleAnnotation = issue => Option.filterMap(AST.getParseIssueTitleAnnotation(issue.ast), annotation => Option.fromNullable(annotation(issue))); | ||
case "TupleType": | ||
return getTree(e, () => Effect.map(Effect.forEach(e.errors, index => Effect.map(go(index.error), tree => make(`[${index.index}]`, [tree]))), forest => make(getParseIssueTitle(e), forest))); | ||
return getTree(e, () => Effect.map(Effect.forEach(e.errors, index => Effect.map(go(index.error), tree => make(`[${util_.formatPropertyKey(index.index)}]`, [tree]))), forest => make(getParseIssueTitle(e), forest))); | ||
case "TypeLiteral": | ||
return getTree(e, () => Effect.map(Effect.forEach(e.errors, key => Effect.map(go(key.error), tree => make(`[${util_.formatUnknown(key.key)}]`, [tree]))), forest => make(getParseIssueTitle(e), forest))); | ||
return getTree(e, () => Effect.map(Effect.forEach(e.errors, key => Effect.map(go(key.error), tree => make(`[${util_.formatPropertyKey(key.key)}]`, [tree]))), forest => make(getParseIssueTitle(e), forest))); | ||
case "Transformation": | ||
@@ -161,0 +183,0 @@ return getTree(e, () => Effect.map(go(e.error), tree => make(getParseIssueTitle(e), [make(formatTransformationKind(e.kind), [tree])]))); |
/** | ||
* @since 1.0.0 | ||
*/ | ||
import * as Array from "effect/Array"; | ||
import * as Effect from "effect/Effect"; | ||
@@ -12,3 +11,3 @@ import type * as ParseResult from "./ParseResult.js"; | ||
export interface Issue { | ||
readonly _tag: ParseResult.ParseIssue["_tag"] | ParseResult.Missing["_tag"] | ParseResult.Unexpected["_tag"]; | ||
readonly _tag: "Transformation" | "Type" | "Declaration" | "Refinement" | "TupleType" | "TypeLiteral" | "Union" | "Forbidden" | "Missing" | "Unexpected"; | ||
readonly path: ReadonlyArray<PropertyKey>; | ||
@@ -15,0 +14,0 @@ readonly message: string; |
/** | ||
* @since 1.0.0 | ||
*/ | ||
import * as Array from "effect/Array"; | ||
import * as Arr from "effect/Array"; | ||
import type { Effect } from "effect/Effect"; | ||
@@ -18,3 +18,3 @@ import * as Option from "effect/Option"; | ||
*/ | ||
export type BrandAnnotation = Array.NonEmptyReadonlyArray<string | symbol>; | ||
export type BrandAnnotation = Arr.NonEmptyReadonlyArray<string | symbol>; | ||
/** | ||
@@ -39,3 +39,6 @@ * @category annotations | ||
*/ | ||
export type MessageAnnotation = (issue: ParseIssue) => string | Effect<string>; | ||
export type MessageAnnotation = (issue: ParseIssue) => string | Effect<string> | { | ||
readonly message: string | Effect<string>; | ||
readonly override: boolean; | ||
}; | ||
/** | ||
@@ -80,3 +83,3 @@ * @category annotations | ||
*/ | ||
export type ExamplesAnnotation<A> = Array.NonEmptyReadonlyArray<A>; | ||
export type ExamplesAnnotation<A> = Arr.NonEmptyReadonlyArray<A>; | ||
/** | ||
@@ -700,3 +703,3 @@ * @category annotations | ||
readonly head: string; | ||
readonly spans: Array.NonEmptyReadonlyArray<TemplateLiteralSpan>; | ||
readonly spans: Arr.NonEmptyReadonlyArray<TemplateLiteralSpan>; | ||
readonly annotations: Annotations; | ||
@@ -1106,3 +1109,3 @@ static make: (head: string, spans: ReadonlyArray<TemplateLiteralSpan>, annotations?: Annotations) => TemplateLiteral | Literal; | ||
*/ | ||
export type Compiler<A> = (ast: AST) => A; | ||
export type Compiler<A> = (ast: AST, path: ReadonlyArray<PropertyKey>) => A; | ||
/** | ||
@@ -1114,3 +1117,3 @@ * @since 1.0.0 | ||
_tag: K; | ||
}>, compile: Compiler<A>) => A; | ||
}>, compile: Compiler<A>, path: ReadonlyArray<PropertyKey>) => A; | ||
}; | ||
@@ -1129,6 +1132,2 @@ /** | ||
export declare const encodedAST: (ast: AST) => AST; | ||
/** | ||
* @since 1.0.0 | ||
*/ | ||
export declare const hash: (ast: AST) => number; | ||
//# sourceMappingURL=AST.d.ts.map |
@@ -10,3 +10,13 @@ /** | ||
*/ | ||
export interface JsonSchema7Any { | ||
export interface JsonSchemaAnnotations { | ||
title?: string; | ||
description?: string; | ||
default?: unknown; | ||
examples?: Array<unknown>; | ||
} | ||
/** | ||
* @category model | ||
* @since 1.0.0 | ||
*/ | ||
export interface JsonSchema7Any extends JsonSchemaAnnotations { | ||
$id: "/schemas/any"; | ||
@@ -18,3 +28,3 @@ } | ||
*/ | ||
export interface JsonSchema7Unknown { | ||
export interface JsonSchema7Unknown extends JsonSchemaAnnotations { | ||
$id: "/schemas/unknown"; | ||
@@ -26,3 +36,3 @@ } | ||
*/ | ||
export interface JsonSchema7object { | ||
export interface JsonSchema7object extends JsonSchemaAnnotations { | ||
$id: "/schemas/object"; | ||
@@ -42,3 +52,3 @@ oneOf: [ | ||
*/ | ||
export interface JsonSchema7empty { | ||
export interface JsonSchema7empty extends JsonSchemaAnnotations { | ||
$id: "/schemas/{}"; | ||
@@ -58,3 +68,3 @@ oneOf: [ | ||
*/ | ||
export interface JsonSchema7Ref { | ||
export interface JsonSchema7Ref extends JsonSchemaAnnotations { | ||
$ref: string; | ||
@@ -66,3 +76,3 @@ } | ||
*/ | ||
export interface JsonSchema7Const { | ||
export interface JsonSchema7Const extends JsonSchemaAnnotations { | ||
const: AST.LiteralValue; | ||
@@ -74,3 +84,3 @@ } | ||
*/ | ||
export interface JsonSchema7String { | ||
export interface JsonSchema7String extends JsonSchemaAnnotations { | ||
type: "string"; | ||
@@ -80,3 +90,2 @@ minLength?: number; | ||
pattern?: string; | ||
description?: string; | ||
} | ||
@@ -87,3 +96,3 @@ /** | ||
*/ | ||
export interface JsonSchema7Numeric { | ||
export interface JsonSchema7Numeric extends JsonSchemaAnnotations { | ||
minimum?: number; | ||
@@ -112,3 +121,3 @@ exclusiveMinimum?: number; | ||
*/ | ||
export interface JsonSchema7Boolean { | ||
export interface JsonSchema7Boolean extends JsonSchemaAnnotations { | ||
type: "boolean"; | ||
@@ -120,3 +129,3 @@ } | ||
*/ | ||
export interface JsonSchema7Array { | ||
export interface JsonSchema7Array extends JsonSchemaAnnotations { | ||
type: "array"; | ||
@@ -132,3 +141,3 @@ items?: JsonSchema7 | Array<JsonSchema7>; | ||
*/ | ||
export interface JsonSchema7OneOf { | ||
export interface JsonSchema7OneOf extends JsonSchemaAnnotations { | ||
oneOf: Array<JsonSchema7>; | ||
@@ -140,3 +149,3 @@ } | ||
*/ | ||
export interface JsonSchema7Enum { | ||
export interface JsonSchema7Enum extends JsonSchemaAnnotations { | ||
enum: Array<AST.LiteralValue>; | ||
@@ -148,3 +157,3 @@ } | ||
*/ | ||
export interface JsonSchema7Enums { | ||
export interface JsonSchema7Enums extends JsonSchemaAnnotations { | ||
$comment: "/schemas/enums"; | ||
@@ -160,3 +169,3 @@ oneOf: Array<{ | ||
*/ | ||
export interface JsonSchema7AnyOf { | ||
export interface JsonSchema7AnyOf extends JsonSchemaAnnotations { | ||
anyOf: Array<JsonSchema7>; | ||
@@ -168,3 +177,3 @@ } | ||
*/ | ||
export interface JsonSchema7Object { | ||
export interface JsonSchema7Object extends JsonSchemaAnnotations { | ||
type: "object"; | ||
@@ -171,0 +180,0 @@ required: Array<string>; |
/** | ||
* @since 1.0.0 | ||
*/ | ||
import * as Array from "effect/Array"; | ||
import * as Arr from "effect/Array"; | ||
import * as Effect from "effect/Effect"; | ||
@@ -61,3 +61,3 @@ import * as Either from "effect/Either"; | ||
readonly actual: unknown; | ||
readonly errors: Array.NonEmptyReadonlyArray<Index>; | ||
readonly errors: Arr.NonEmptyReadonlyArray<Index>; | ||
readonly output: ReadonlyArray<unknown>; | ||
@@ -68,3 +68,3 @@ /** | ||
readonly _tag = "TupleType"; | ||
constructor(ast: AST.TupleType, actual: unknown, errors: Array.NonEmptyReadonlyArray<Index>, output?: ReadonlyArray<unknown>); | ||
constructor(ast: AST.TupleType, actual: unknown, errors: Arr.NonEmptyReadonlyArray<Index>, output?: ReadonlyArray<unknown>); | ||
} | ||
@@ -95,3 +95,3 @@ /** | ||
readonly actual: unknown; | ||
readonly errors: Array.NonEmptyReadonlyArray<Key>; | ||
readonly errors: Arr.NonEmptyReadonlyArray<Key>; | ||
readonly output: { | ||
@@ -104,3 +104,3 @@ readonly [x: string]: unknown; | ||
readonly _tag = "TypeLiteral"; | ||
constructor(ast: AST.TypeLiteral, actual: unknown, errors: Array.NonEmptyReadonlyArray<Key>, output?: { | ||
constructor(ast: AST.TypeLiteral, actual: unknown, errors: Arr.NonEmptyReadonlyArray<Key>, output?: { | ||
readonly [x: string]: unknown; | ||
@@ -235,3 +235,3 @@ }); | ||
readonly actual: unknown; | ||
readonly errors: Array.NonEmptyReadonlyArray<Type | TypeLiteral | Member>; | ||
readonly errors: Arr.NonEmptyReadonlyArray<Type | TypeLiteral | Member>; | ||
/** | ||
@@ -241,3 +241,3 @@ * @since 1.0.0 | ||
readonly _tag = "Union"; | ||
constructor(ast: AST.Union, actual: unknown, errors: Array.NonEmptyReadonlyArray<Type | TypeLiteral | Member>); | ||
constructor(ast: AST.Union, actual: unknown, errors: Arr.NonEmptyReadonlyArray<Type | TypeLiteral | Member>); | ||
} | ||
@@ -469,3 +469,3 @@ declare const ParseError_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").Equals<A, {}> extends true ? void : { readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }) => import("effect/Cause").YieldableError & { | ||
*/ | ||
export declare const is: <A, I, R>(schema: Schema.Schema<A, I, R>, options?: AST.ParseOptions) => (u: unknown, overrideOptions?: AST.ParseOptions) => u is A; | ||
export declare const is: <A, I, R>(schema: Schema.Schema<A, I, R>, options?: AST.ParseOptions) => (u: unknown, overrideOptions?: AST.ParseOptions | number) => u is A; | ||
/** | ||
@@ -472,0 +472,0 @@ * @category validation |
@@ -46,6 +46,6 @@ /** | ||
*/ | ||
export interface WithResult<A, I, E, EI, R> { | ||
export interface WithResult<SuccessA, SuccessI, FailureA, FailureI, SuccessAndFailureR> { | ||
readonly [symbolResult]: { | ||
readonly Success: Schema.Schema<A, I, R>; | ||
readonly Failure: Schema.Schema<E, EI, R>; | ||
readonly Success: Schema.Schema<SuccessA, SuccessI, SuccessAndFailureR>; | ||
readonly Failure: Schema.Schema<FailureA, FailureI, SuccessAndFailureR>; | ||
}; | ||
@@ -82,3 +82,3 @@ } | ||
*/ | ||
export interface SerializableWithResult<S, SI, SR, A, AI, E, EI, RR> extends Serializable<S, SI, SR>, WithResult<A, AI, E, EI, RR> { | ||
export interface SerializableWithResult<Self, FieldsI, FieldsR, SuccessA, SuccessI, FailureA, FailureI, SuccessAndFailureR> extends Serializable<Self, FieldsI, FieldsR>, WithResult<SuccessA, SuccessI, FailureA, FailureI, SuccessAndFailureR> { | ||
} | ||
@@ -85,0 +85,0 @@ /** |
/** | ||
* @since 1.0.0 | ||
*/ | ||
import * as Array from "effect/Array"; | ||
import * as Arr from "effect/Array"; | ||
import * as Option from "effect/Option"; | ||
@@ -30,3 +30,3 @@ import * as Predicate from "effect/Predicate"; | ||
*/ | ||
export const makeLazy = schema => go(schema.ast, {}); | ||
export const makeLazy = schema => go(schema.ast, {}, []); | ||
/** | ||
@@ -55,3 +55,3 @@ * Returns a fast-check Arbitrary for the `A` type of the provided schema. | ||
const getHook = /*#__PURE__*/AST.getAnnotation(ArbitraryHookId); | ||
const getRefinementFromArbitrary = (ast, options) => { | ||
const getRefinementFromArbitrary = (ast, options, path) => { | ||
const constraints = combineConstraints(options.constraints, getConstraints(ast)); | ||
@@ -61,5 +61,6 @@ return go(ast.from, constraints ? { | ||
constraints | ||
} : options); | ||
} : options, path); | ||
}; | ||
const go = (ast, options) => { | ||
const getArbitraryErrorMessage = (message, path) => errors_.getErrorMessageWithPath(`cannot build an Arbitrary for ${message}`, path); | ||
const go = (ast, options, path) => { | ||
const hook = getHook(ast); | ||
@@ -69,5 +70,5 @@ if (Option.isSome(hook)) { | ||
case "Declaration": | ||
return hook.value(...ast.typeParameters.map(p => go(p, options))); | ||
return hook.value(...ast.typeParameters.map(p => go(p, options, path))); | ||
case "Refinement": | ||
return hook.value(getRefinementFromArbitrary(ast, options)); | ||
return hook.value(getRefinementFromArbitrary(ast, options, path)); | ||
default: | ||
@@ -80,3 +81,3 @@ return hook.value(); | ||
{ | ||
throw new Error(errors_.getArbitraryErrorMessage(`a declaration without annotations (${ast})`)); | ||
throw new Error(getArbitraryErrorMessage(`a declaration without annotations (${ast})`, path)); | ||
} | ||
@@ -92,3 +93,3 @@ case "Literal": | ||
return () => { | ||
throw new Error(errors_.getArbitraryErrorMessage("`never`")); | ||
throw new Error(getArbitraryErrorMessage("`never`", path)); | ||
}; | ||
@@ -161,4 +162,5 @@ case "UnknownKeyword": | ||
let hasOptionals = false; | ||
let i = 0; | ||
for (const element of ast.elements) { | ||
elements.push(go(element.type, options)); | ||
elements.push(go(element.type, options, path.concat(i++))); | ||
if (element.isOptional) { | ||
@@ -168,3 +170,3 @@ hasOptionals = true; | ||
} | ||
const rest = ast.rest.map(e => go(e, options)); | ||
const rest = ast.rest.map(e => go(e, options, path)); | ||
return fc => { | ||
@@ -189,3 +191,3 @@ // --------------------------------------------- | ||
// --------------------------------------------- | ||
if (Array.isNonEmptyReadonlyArray(rest)) { | ||
if (Arr.isNonEmptyReadonlyArray(rest)) { | ||
const [head, ...tail] = rest; | ||
@@ -220,4 +222,4 @@ const arb = head(fc); | ||
{ | ||
const propertySignaturesTypes = ast.propertySignatures.map(f => go(f.type, options)); | ||
const indexSignatures = ast.indexSignatures.map(is => [go(is.parameter, options), go(is.type, options)]); | ||
const propertySignaturesTypes = ast.propertySignatures.map(ps => go(ps.type, options, path.concat(ps.name))); | ||
const indexSignatures = ast.indexSignatures.map(is => [go(is.parameter, options, path), go(is.type, options, path)]); | ||
return fc => { | ||
@@ -258,3 +260,3 @@ const arbs = {}; | ||
{ | ||
const types = ast.types.map(t => go(t, options)); | ||
const types = ast.types.map(t => go(t, options, path)); | ||
return fc => fc.oneof({ | ||
@@ -267,3 +269,3 @@ depthSize | ||
if (ast.enums.length === 0) { | ||
throw new Error(errors_.getArbitraryErrorMessage("an empty enum")); | ||
throw new Error(getArbitraryErrorMessage("an empty enum", path)); | ||
} | ||
@@ -274,3 +276,3 @@ return fc => fc.oneof(...ast.enums.map(([_, value]) => fc.constant(value))); | ||
{ | ||
const from = getRefinementFromArbitrary(ast, options); | ||
const from = getRefinementFromArbitrary(ast, options, path); | ||
return fc => from(fc).filter(a => Option.isNone(ast.filter(a, AST.defaultParseOption, ast))); | ||
@@ -283,7 +285,7 @@ } | ||
isSuspend: true | ||
})); | ||
}, path)); | ||
return fc => fc.constant(null).chain(() => get()(fc)); | ||
} | ||
case "Transformation": | ||
return go(ast.to, options); | ||
return go(ast.to, options, path); | ||
} | ||
@@ -290,0 +292,0 @@ }; |
/** | ||
* @since 1.0.0 | ||
*/ | ||
import * as Array from "effect/Array"; | ||
import * as Arr from "effect/Array"; | ||
import * as Effect from "effect/Effect"; | ||
@@ -27,10 +27,12 @@ import * as TreeFormatter from "./TreeFormatter.js"; | ||
export const formatErrorSync = error => formatIssueSync(error.error); | ||
const succeed = issue => Effect.succeed([issue]); | ||
const getArray = (issue, path, onFailure) => Effect.matchEffect(TreeFormatter.getMessage(issue), { | ||
onFailure, | ||
onSuccess: message => Effect.succeed([{ | ||
onSuccess: message => succeed({ | ||
_tag: issue._tag, | ||
path, | ||
message | ||
}]) | ||
}) | ||
}); | ||
const flatten = eff => Effect.map(eff, Arr.flatten); | ||
const go = (e, path = []) => { | ||
@@ -46,21 +48,21 @@ const _tag = e._tag; | ||
case "Forbidden": | ||
return Effect.succeed([{ | ||
return succeed({ | ||
_tag, | ||
path, | ||
message: TreeFormatter.formatForbiddenMessage(e) | ||
}]); | ||
}); | ||
case "Unexpected": | ||
return Effect.succeed([{ | ||
return succeed({ | ||
_tag, | ||
path, | ||
message: `is unexpected, expected ${e.ast.toString(true)}` | ||
}]); | ||
}); | ||
case "Missing": | ||
return Effect.succeed([{ | ||
return succeed({ | ||
_tag, | ||
path, | ||
message: "is missing" | ||
}]); | ||
}); | ||
case "Union": | ||
return getArray(e, path, () => Effect.map(Effect.forEach(e.errors, e => { | ||
return getArray(e, path, () => flatten(Effect.forEach(e.errors, e => { | ||
switch (e._tag) { | ||
@@ -72,10 +74,10 @@ case "Member": | ||
} | ||
}), Array.flatten)); | ||
}))); | ||
case "TupleType": | ||
return getArray(e, path, () => Effect.map(Effect.forEach(e.errors, index => go(index.error, [...path, index.index])), Array.flatten)); | ||
return getArray(e, path, () => flatten(Effect.forEach(e.errors, index => go(index.error, path.concat(index.index))))); | ||
case "TypeLiteral": | ||
return getArray(e, path, () => Effect.map(Effect.forEach(e.errors, key => go(key.error, [...path, key.key])), Array.flatten)); | ||
return getArray(e, path, () => flatten(Effect.forEach(e.errors, key => go(key.error, path.concat(key.key))))); | ||
case "Declaration": | ||
case "Refinement": | ||
case "Transformation": | ||
case "Refinement": | ||
case "Declaration": | ||
return getArray(e, path, () => go(e.error, path)); | ||
@@ -82,0 +84,0 @@ } |
/** | ||
* @since 1.0.0 | ||
*/ | ||
import * as Array from "effect/Array"; | ||
import * as Arr from "effect/Array"; | ||
import { dual, identity } from "effect/Function"; | ||
import { globalValue } from "effect/GlobalValue"; | ||
import * as Hash from "effect/Hash"; | ||
import * as Number from "effect/Number"; | ||
@@ -812,3 +811,3 @@ import * as Option from "effect/Option"; | ||
annotations; | ||
static make = (head, spans, annotations = {}) => Array.isNonEmptyReadonlyArray(spans) ? new TemplateLiteral(head, spans, annotations) : new Literal(head); | ||
static make = (head, spans, annotations = {}) => Arr.isNonEmptyReadonlyArray(spans) ? new TemplateLiteral(head, spans, annotations) : new Literal(head); | ||
/** | ||
@@ -903,3 +902,3 @@ * @since 1.0.0 | ||
if (hasIllegalRequiredElement || hasOptionalElement && rest.length > 1) { | ||
throw new Error(errors_.getRequiredElementFollowinAnOptionalElementErrorMessage); | ||
throw new Error(getRequiredElementFollowinAnOptionalElementErrorMessage); | ||
} | ||
@@ -928,3 +927,3 @@ } | ||
const formattedElements = ast.elements.map(String).join(", "); | ||
return Array.matchLeft(ast.rest, { | ||
return Arr.matchLeft(ast.rest, { | ||
onEmpty: () => `readonly [${formattedElements}]`, | ||
@@ -1017,3 +1016,3 @@ onNonEmpty: (head, tail) => { | ||
} else { | ||
throw new Error(errors_.getIndexSignatureParameterErrorMessage); | ||
throw new Error(getIndexSignatureParameterErrorMessage); | ||
} | ||
@@ -1070,3 +1069,3 @@ } | ||
if (parameters.string) { | ||
throw new Error(errors_.getDuplicateIndexSignatureErrorMessage("string")); | ||
throw new Error(getDuplicateIndexSignatureErrorMessage("string")); | ||
} | ||
@@ -1076,3 +1075,3 @@ parameters.string = true; | ||
if (parameters.symbol) { | ||
throw new Error(errors_.getDuplicateIndexSignatureErrorMessage("symbol")); | ||
throw new Error(getDuplicateIndexSignatureErrorMessage("symbol")); | ||
} | ||
@@ -1104,5 +1103,5 @@ parameters.symbol = true; | ||
const formatTypeLiteral = ast => { | ||
const formattedPropertySignatures = ast.propertySignatures.map(ps => String(ps.name) + (ps.isOptional ? "?" : "") + ": " + ps.type).join("; "); | ||
const formattedPropertySignatures = ast.propertySignatures.map(ps => (ps.isReadonly ? "readonly " : "") + String(ps.name) + (ps.isOptional ? "?" : "") + ": " + ps.type).join("; "); | ||
if (ast.indexSignatures.length > 0) { | ||
const formattedIndexSignatures = ast.indexSignatures.map(is => `[x: ${getParameterBase(is.parameter)}]: ${is.type}`).join("; "); | ||
const formattedIndexSignatures = ast.indexSignatures.map(is => (is.isReadonly ? "readonly " : "") + `[x: ${getParameterBase(is.parameter)}]: ${is.type}`).join("; "); | ||
if (ast.propertySignatures.length > 0) { | ||
@@ -1127,3 +1126,3 @@ return `{ ${formattedPropertySignatures}; ${formattedIndexSignatures} }`; | ||
const removeNevers = candidates => candidates.filter(ast => !(ast === neverKeyword)); | ||
const sortCandidates = /*#__PURE__*/Array.sort( /*#__PURE__*/Order.mapInput(Number.Order, ast => { | ||
const sortCandidates = /*#__PURE__*/Arr.sort( /*#__PURE__*/Order.mapInput(Number.Order, ast => { | ||
switch (ast._tag) { | ||
@@ -1152,3 +1151,3 @@ case "AnyKeyword": | ||
/** @internal */ | ||
export const flatten = candidates => Array.flatMap(candidates, ast => isUnion(ast) ? flatten(ast.types) : [ast]); | ||
export const flatten = candidates => Arr.flatMap(candidates, ast => isUnion(ast) ? flatten(ast.types) : [ast]); | ||
/** @internal */ | ||
@@ -1381,3 +1380,3 @@ export const unify = candidates => { | ||
toString(verbose = false) { | ||
return Option.getOrElse(getExpected(this, verbose), () => "<refinement schema>"); | ||
return Option.getOrElse(getExpected(this, verbose), () => `{ ${this.from} | filter }`); | ||
} | ||
@@ -1514,2 +1513,3 @@ /** | ||
} | ||
const isRenamingPropertySignatureTransformation = t => t.decode === identity && t.encode === identity; | ||
/** | ||
@@ -1533,3 +1533,3 @@ * @category model | ||
if (fromKeys[from]) { | ||
throw new Error(errors_.getDuplicatePropertySignatureTransformationErrorMessage(from)); | ||
throw new Error(getDuplicatePropertySignatureTransformationErrorMessage(from)); | ||
} | ||
@@ -1539,3 +1539,3 @@ fromKeys[from] = true; | ||
if (toKeys[to]) { | ||
throw new Error(errors_.getDuplicatePropertySignatureTransformationErrorMessage(to)); | ||
throw new Error(getDuplicatePropertySignatureTransformationErrorMessage(to)); | ||
} | ||
@@ -1637,3 +1637,3 @@ toKeys[to] = true; | ||
} | ||
throw new Error(errors_.getAPIErrorMessage("NumberIndexedAccess", `unsupported schema (${ast})`)); | ||
throw new Error(errors_.getErrorMessage("getNumberIndexedAccess", `unsupported schema (${ast})`)); | ||
}; | ||
@@ -1653,3 +1653,3 @@ /** @internal */ | ||
{ | ||
const ops = Array.findFirst(ast.propertySignatures, ps => ps.name === name); | ||
const ops = Arr.findFirst(ast.propertySignatures, ps => ps.name === name); | ||
if (Option.isSome(ops)) { | ||
@@ -1666,3 +1666,3 @@ return ops.value; | ||
if (regex.test(name)) { | ||
return new PropertySignature(name, is.type, false, false); | ||
return new PropertySignature(name, is.type, false, true); | ||
} | ||
@@ -1672,3 +1672,3 @@ break; | ||
case "StringKeyword": | ||
return new PropertySignature(name, is.type, false, false); | ||
return new PropertySignature(name, is.type, false, true); | ||
} | ||
@@ -1680,3 +1680,3 @@ } | ||
if (isSymbolKeyword(parameterBase)) { | ||
return new PropertySignature(name, is.type, false, false); | ||
return new PropertySignature(name, is.type, false, true); | ||
} | ||
@@ -1710,3 +1710,3 @@ } | ||
case "Union": | ||
return ast.types.slice(1).reduce((out, ast) => Array.intersection(out, getPropertyKeys(ast)), getPropertyKeys(ast.types[0])); | ||
return ast.types.slice(1).reduce((out, ast) => Arr.intersection(out, getPropertyKeys(ast)), getPropertyKeys(ast.types[0])); | ||
case "Transformation": | ||
@@ -1735,5 +1735,12 @@ return getPropertyKeys(ast.to); | ||
} else { | ||
throw new Error(errors_.getAPIErrorMessage("Record", `unsupported literal (${util_.formatUnknown(key.literal)})`)); | ||
throw new Error(errors_.getErrorMessage("record", `unsupported literal (${util_.formatUnknown(key.literal)})`)); | ||
} | ||
break; | ||
case "Enums": | ||
{ | ||
for (const [_, name] of key.enums) { | ||
propertySignatures.push(new PropertySignature(name, value, false, true)); | ||
} | ||
break; | ||
} | ||
case "UniqueSymbol": | ||
@@ -1746,3 +1753,3 @@ propertySignatures.push(new PropertySignature(key.symbol, value, false, true)); | ||
default: | ||
throw new Error(errors_.getAPIErrorMessage("Record", `unsupported key schema (${key})`)); | ||
throw new Error(errors_.getErrorMessage("record", `unsupported key schema (${key})`)); | ||
} | ||
@@ -1779,3 +1786,3 @@ }; | ||
} | ||
return new Transformation(pick(ast.from, fromKeys), pick(ast.to, keys), Array.isNonEmptyReadonlyArray(ts) ? new TypeLiteralTransformation(ts) : composeTransformation); | ||
return Arr.isNonEmptyReadonlyArray(ts) ? new Transformation(pick(ast.from, fromKeys), pick(ast.to, keys), new TypeLiteralTransformation(ts)) : pick(ast.from, fromKeys); | ||
} | ||
@@ -1788,3 +1795,3 @@ case "FinalTransformation": | ||
} | ||
throw new Error(errors_.getAPIErrorMessage("Pick", "cannot handle this kind of transformation")); | ||
throw new Error(errors_.getErrorMessage("pick", "cannot handle this kind of transformation")); | ||
} | ||
@@ -1812,3 +1819,3 @@ } | ||
case "TupleType": | ||
return new TupleType(ast.elements.map(e => new Element(exact ? e.type : orUndefined(e.type), true)), Array.match(ast.rest, { | ||
return new TupleType(ast.elements.map(e => new Element(exact ? e.type : orUndefined(e.type), true)), Arr.match(ast.rest, { | ||
onEmpty: () => ast.rest, | ||
@@ -1824,7 +1831,12 @@ onNonEmpty: rest => [Union.make([...rest, undefinedKeyword])] | ||
case "Declaration": | ||
throw new Error(errors_.getAPIErrorMessage("Partial", "cannot handle declarations")); | ||
throw new Error(errors_.getErrorMessage("partial", "cannot handle declarations")); | ||
case "Refinement": | ||
throw new Error(errors_.getAPIErrorMessage("Partial", "cannot handle refinements")); | ||
throw new Error(errors_.getErrorMessage("partial", "cannot handle refinements")); | ||
case "Transformation": | ||
throw new Error(errors_.getAPIErrorMessage("Partial", "cannot handle transformations")); | ||
{ | ||
if (isTypeLiteralTransformation(ast.transformation) && ast.transformation.propertySignatureTransformations.every(isRenamingPropertySignatureTransformation)) { | ||
return new Transformation(partial(ast.from, options), partial(ast.to, options), ast.transformation); | ||
} | ||
throw new Error(errors_.getErrorMessage("partial", "cannot handle transformations")); | ||
} | ||
} | ||
@@ -1849,7 +1861,12 @@ return ast; | ||
case "Declaration": | ||
throw new Error(errors_.getAPIErrorMessage("Required", "cannot handle declarations")); | ||
throw new Error(errors_.getErrorMessage("required", "cannot handle declarations")); | ||
case "Refinement": | ||
throw new Error(errors_.getAPIErrorMessage("Required", "cannot handle refinements")); | ||
throw new Error(errors_.getErrorMessage("required", "cannot handle refinements")); | ||
case "Transformation": | ||
throw new Error(errors_.getAPIErrorMessage("Required", "cannot handle transformations")); | ||
{ | ||
if (isTypeLiteralTransformation(ast.transformation) && ast.transformation.propertySignatureTransformations.every(isRenamingPropertySignatureTransformation)) { | ||
return new Transformation(required(ast.from), required(ast.to), ast.transformation); | ||
} | ||
throw new Error(errors_.getErrorMessage("required", "cannot handle transformations")); | ||
} | ||
} | ||
@@ -1900,9 +1917,5 @@ return ast; | ||
export const getCompiler = match => { | ||
const compile = ast => match[ast._tag](ast, compile); | ||
const compile = (ast, path) => match[ast._tag](ast, compile, path); | ||
return compile; | ||
}; | ||
/** @internal */ | ||
export const getToPropertySignatures = ps => ps.map(p => new PropertySignature(p.name, typeAST(p.type), p.isOptional, p.isReadonly, p.annotations)); | ||
/** @internal */ | ||
export const getToIndexSignatures = ps => ps.map(is => new IndexSignature(is.parameter, typeAST(is.type), is.isReadonly)); | ||
/** | ||
@@ -1966,3 +1979,3 @@ * @since 1.0.0 | ||
let changed = false; | ||
const out = Array.allocate(as.length); | ||
const out = Arr.allocate(as.length); | ||
for (let i = 0; i < as.length; i++) { | ||
@@ -2029,6 +2042,2 @@ const a = as[i]; | ||
}; | ||
/** | ||
* @since 1.0.0 | ||
*/ | ||
export const hash = ast => Hash.string(JSON.stringify(ast, null, 2)); | ||
/** @internal */ | ||
@@ -2060,4 +2069,4 @@ export const getCardinality = ast => { | ||
}; | ||
const sortPropertySignatures = /*#__PURE__*/Array.sort( /*#__PURE__*/Order.mapInput(Number.Order, ps => getCardinality(ps.type))); | ||
const sortIndexSignatures = /*#__PURE__*/Array.sort( /*#__PURE__*/Order.mapInput(Number.Order, is => { | ||
const sortPropertySignatures = /*#__PURE__*/Arr.sort( /*#__PURE__*/Order.mapInput(Number.Order, ps => getCardinality(ps.type))); | ||
const sortIndexSignatures = /*#__PURE__*/Arr.sort( /*#__PURE__*/Order.mapInput(Number.Order, is => { | ||
switch (getParameterBase(is.parameter)._tag) { | ||
@@ -2129,4 +2138,4 @@ case "StringKeyword": | ||
}; | ||
const equalsTemplateLiteralSpan = /*#__PURE__*/Array.getEquivalence((self, that) => self.type._tag === that.type._tag && self.literal === that.literal); | ||
const equalsEnums = /*#__PURE__*/Array.getEquivalence((self, that) => that[0] === self[0] && that[1] === self[1]); | ||
const equalsTemplateLiteralSpan = /*#__PURE__*/Arr.getEquivalence((self, that) => self.type._tag === that.type._tag && self.literal === that.literal); | ||
const equalsEnums = /*#__PURE__*/Arr.getEquivalence((self, that) => that[0] === self[0] && that[1] === self[1]); | ||
const equals = (self, that) => { | ||
@@ -2164,3 +2173,3 @@ switch (self._tag) { | ||
}; | ||
const intersection = /*#__PURE__*/Array.intersectionWith(equals); | ||
const intersection = /*#__PURE__*/Arr.intersectionWith(equals); | ||
const _keyof = ast => { | ||
@@ -2185,3 +2194,3 @@ switch (ast._tag) { | ||
} | ||
throw new Error(errors_.getAPIErrorMessage("KeyOf", `unsupported schema (${ast})`)); | ||
throw new Error(errors_.getErrorMessage("keyof", `unsupported schema (${ast})`)); | ||
}; | ||
@@ -2232,2 +2241,6 @@ /** @internal */ | ||
}; | ||
const getDuplicateIndexSignatureErrorMessage = name => `Duplicate index signature for type \`${name}\``; | ||
const getIndexSignatureParameterErrorMessage = "An index signature parameter type must be `string`, `symbol`, a template literal type or a refinement of the previous types"; | ||
const getRequiredElementFollowinAnOptionalElementErrorMessage = "A required element cannot follow an optional element. ts(1257)"; | ||
const getDuplicatePropertySignatureTransformationErrorMessage = name => `Duplicate property signature transformation ${util_.formatUnknown(name)}`; | ||
//# sourceMappingURL=AST.js.map |
/** | ||
* @since 1.0.0 | ||
*/ | ||
import * as Array from "effect/Array"; | ||
import * as Arr from "effect/Array"; | ||
import * as Equal from "effect/Equal"; | ||
@@ -29,5 +29,6 @@ import * as Equivalence from "effect/Equivalence"; | ||
*/ | ||
export const make = schema => go(schema.ast); | ||
export const make = schema => go(schema.ast, []); | ||
const getHook = /*#__PURE__*/AST.getAnnotation(EquivalenceHookId); | ||
const go = ast => { | ||
const getEquivalenceErrorMessage = (message, path) => errors_.getErrorMessageWithPath(`cannot build an Equivalence for ${message}`, path); | ||
const go = (ast, path) => { | ||
const hook = getHook(ast); | ||
@@ -37,5 +38,5 @@ if (Option.isSome(hook)) { | ||
case "Declaration": | ||
return hook.value(...ast.typeParameters.map(go)); | ||
return hook.value(...ast.typeParameters.map(tp => go(tp, path))); | ||
case "Refinement": | ||
return hook.value(go(ast.from)); | ||
return hook.value(go(ast.from, path)); | ||
default: | ||
@@ -47,5 +48,5 @@ return hook.value(); | ||
case "NeverKeyword": | ||
throw new Error(errors_.getEquivalenceErrorMessage("`never`")); | ||
throw new Error(getEquivalenceErrorMessage("`never`", path)); | ||
case "Transformation": | ||
return go(ast.to); | ||
return go(ast.to, path); | ||
case "Declaration": | ||
@@ -68,6 +69,6 @@ case "Literal": | ||
case "Refinement": | ||
return go(ast.from); | ||
return go(ast.from, path); | ||
case "Suspend": | ||
{ | ||
const get = util_.memoizeThunk(() => go(ast.f())); | ||
const get = util_.memoizeThunk(() => go(ast.f(), path)); | ||
return (a, b) => get()(a, b); | ||
@@ -77,4 +78,4 @@ } | ||
{ | ||
const elements = ast.elements.map(element => go(element.type)); | ||
const rest = ast.rest.map(go); | ||
const elements = ast.elements.map((element, i) => go(element.type, path.concat(i))); | ||
const rest = ast.rest.map(ast => go(ast, path)); | ||
return Equivalence.make((a, b) => { | ||
@@ -97,3 +98,3 @@ const len = a.length; | ||
// --------------------------------------------- | ||
if (Array.isNonEmptyReadonlyArray(rest)) { | ||
if (Arr.isNonEmptyReadonlyArray(rest)) { | ||
const [head, ...tail] = rest; | ||
@@ -123,4 +124,4 @@ for (; i < len - tail.length; i++) { | ||
} | ||
const propertySignatures = ast.propertySignatures.map(ps => go(ps.type)); | ||
const indexSignatures = ast.indexSignatures.map(is => go(is.type)); | ||
const propertySignatures = ast.propertySignatures.map(ps => go(ps.type, path.concat(ps.name))); | ||
const indexSignatures = ast.indexSignatures.map(is => go(is.type, path)); | ||
return Equivalence.make((a, b) => { | ||
@@ -199,3 +200,3 @@ const aStringKeys = Object.keys(a); | ||
} | ||
const tuples = candidates.map(ast => [go(ast), ParseResult.is({ | ||
const tuples = candidates.map(ast => [go(ast, path), ParseResult.is({ | ||
ast | ||
@@ -202,0 +203,0 @@ })]); |
@@ -5,17 +5,11 @@ import * as util_ from "./util.js"; | ||
/** @internal */ | ||
export const getDuplicateIndexSignatureErrorMessage = name => `Duplicate index signature for type \`${name}\``; | ||
export const getErrorMessage = (api, message) => `${api}: ${message}`; | ||
/** @internal */ | ||
export const getIndexSignatureParameterErrorMessage = "An index signature parameter type must be `string`, `symbol`, a template literal type or a refinement of the previous types"; | ||
/** @internal */ | ||
export const getRequiredElementFollowinAnOptionalElementErrorMessage = "A required element cannot follow an optional element. ts(1257)"; | ||
/** @internal */ | ||
export const getDuplicatePropertySignatureTransformationErrorMessage = name => `Duplicate property signature transformation ${util_.formatUnknown(name)}`; | ||
/** @internal */ | ||
export const getArbitraryErrorMessage = message => `cannot build an Arbitrary for ${message}`; | ||
/** @internal */ | ||
export const getPrettyErrorMessage = message => `cannot build a Pretty for ${message}`; | ||
/** @internal */ | ||
export const getEquivalenceErrorMessage = message => `cannot build an Equivalence for ${message}`; | ||
/** @internal */ | ||
export const getAPIErrorMessage = (api, message) => `${api}: ${message}`; | ||
export const getErrorMessageWithPath = (message, path) => { | ||
let out = message; | ||
if (path.length > 0) { | ||
out += ` (path [${path.map(util_.formatPropertyKey).join(", ")}])`; | ||
} | ||
return out; | ||
}; | ||
//# sourceMappingURL=errors.js.map |
@@ -51,2 +51,4 @@ import * as Predicate from "effect/Predicate"; | ||
}; | ||
/** @internal */ | ||
export const formatPropertyKey = name => typeof name === "string" ? JSON.stringify(name) : String(name); | ||
//# sourceMappingURL=util.js.map |
@@ -8,2 +8,3 @@ /** | ||
import * as AST from "./AST.js"; | ||
import * as errors_ from "./internal/errors.js"; | ||
/** | ||
@@ -15,3 +16,3 @@ * @category encoding | ||
const $defs = {}; | ||
const jsonSchema = go(schema.ast, $defs); | ||
const jsonSchema = go(schema.ast, $defs, true, []); | ||
const out = { | ||
@@ -55,3 +56,3 @@ $schema, | ||
const $schema = "http://json-schema.org/draft-07/schema#"; | ||
const getMeta = annotated => Record.getSomes({ | ||
const getJsonSchemaAnnotations = annotated => Record.getSomes({ | ||
description: AST.getDescriptionAnnotation(annotated), | ||
@@ -69,36 +70,49 @@ title: AST.getTitleAnnotation(annotated), | ||
}; | ||
const getMissingAnnotationError = name => { | ||
const out = new Error(`cannot build a JSON Schema for ${name} without a JSON Schema annotation`); | ||
out.name = "MissingAnnotation"; | ||
return out; | ||
}; | ||
const getUnsupportedIndexSignatureParameterErrorMessage = parameter => `Unsupported index signature parameter (${parameter})`; | ||
const getMissingAnnotationErrorMessage = (name, path) => errors_.getErrorMessageWithPath(`cannot build a JSON Schema for ${name} without a JSON Schema annotation`, path); | ||
const getUnsupportedIndexSignatureParameterErrorMessage = (parameter, path) => errors_.getErrorMessageWithPath(`unsupported index signature parameter (${parameter})`, path); | ||
/** @internal */ | ||
export const DEFINITION_PREFIX = "#/$defs/"; | ||
const get$ref = id => `${DEFINITION_PREFIX}${id}`; | ||
const go = (ast, $defs, handleIdentifier = true) => { | ||
const hasTransformation = ast => { | ||
switch (ast.from._tag) { | ||
case "Transformation": | ||
return true; | ||
case "Refinement": | ||
return hasTransformation(ast.from); | ||
case "Suspend": | ||
{ | ||
const from = ast.from.f(); | ||
if (AST.isRefinement(from)) { | ||
return hasTransformation(from); | ||
} | ||
} | ||
break; | ||
} | ||
return false; | ||
}; | ||
const go = (ast, $defs, handleIdentifier, path) => { | ||
const hook = AST.getJSONSchemaAnnotation(ast); | ||
if (Option.isSome(hook)) { | ||
const handler = hook.value; | ||
switch (ast._tag) { | ||
case "Refinement": | ||
try { | ||
return { | ||
...go(ast.from, $defs), | ||
...getMeta(ast), | ||
...handler | ||
}; | ||
} catch (e) { | ||
if (e instanceof Error && e.name === "MissingAnnotation") { | ||
return { | ||
...getMeta(ast), | ||
...handler | ||
}; | ||
} | ||
throw e; | ||
} | ||
if (AST.isRefinement(ast) && !hasTransformation(ast)) { | ||
try { | ||
return { | ||
...go(ast.from, $defs, true, path), | ||
...getJsonSchemaAnnotations(ast), | ||
...handler | ||
}; | ||
} catch (e) { | ||
return { | ||
...getJsonSchemaAnnotations(ast), | ||
...handler | ||
}; | ||
} | ||
} | ||
return handler; | ||
} | ||
if (handleIdentifier) { | ||
const surrogate = AST.getSurrogateAnnotation(ast); | ||
if (Option.isSome(surrogate)) { | ||
return go(surrogate.value, $defs, handleIdentifier, path); | ||
} | ||
if (handleIdentifier && !AST.isTransformation(ast)) { | ||
const identifier = AST.getJSONIdentifier(ast); | ||
@@ -112,3 +126,3 @@ if (Option.isSome(identifier)) { | ||
$defs[id] = out; | ||
$defs[id] = go(ast, $defs, false); | ||
$defs[id] = go(ast, $defs, false, path); | ||
} | ||
@@ -120,3 +134,3 @@ return out; | ||
case "Declaration": | ||
throw getMissingAnnotationError("a declaration"); | ||
throw new Error(getMissingAnnotationErrorMessage("a declaration", path)); | ||
case "Literal": | ||
@@ -128,3 +142,3 @@ { | ||
const: null, | ||
...getMeta(ast) | ||
...getJsonSchemaAnnotations(ast) | ||
}; | ||
@@ -134,3 +148,3 @@ } else if (Predicate.isString(literal)) { | ||
const: literal, | ||
...getMeta(ast) | ||
...getJsonSchemaAnnotations(ast) | ||
}; | ||
@@ -140,3 +154,3 @@ } else if (Predicate.isNumber(literal)) { | ||
const: literal, | ||
...getMeta(ast) | ||
...getJsonSchemaAnnotations(ast) | ||
}; | ||
@@ -146,19 +160,19 @@ } else if (Predicate.isBoolean(literal)) { | ||
const: literal, | ||
...getMeta(ast) | ||
...getJsonSchemaAnnotations(ast) | ||
}; | ||
} | ||
throw getMissingAnnotationError("a bigint literal"); | ||
throw new Error(getMissingAnnotationErrorMessage("a bigint literal", path)); | ||
} | ||
case "UniqueSymbol": | ||
throw getMissingAnnotationError("a unique symbol"); | ||
throw new Error(getMissingAnnotationErrorMessage("a unique symbol", path)); | ||
case "UndefinedKeyword": | ||
throw getMissingAnnotationError("`undefined`"); | ||
throw new Error(getMissingAnnotationErrorMessage("`undefined`", path)); | ||
case "VoidKeyword": | ||
throw getMissingAnnotationError("`void`"); | ||
throw new Error(getMissingAnnotationErrorMessage("`void`", path)); | ||
case "NeverKeyword": | ||
throw getMissingAnnotationError("`never`"); | ||
throw new Error(getMissingAnnotationErrorMessage("`never`", path)); | ||
case "UnknownKeyword": | ||
return { | ||
...unknownJsonSchema, | ||
...getMeta(ast) | ||
...getJsonSchemaAnnotations(ast) | ||
}; | ||
@@ -168,3 +182,3 @@ case "AnyKeyword": | ||
...anyJsonSchema, | ||
...getMeta(ast) | ||
...getJsonSchemaAnnotations(ast) | ||
}; | ||
@@ -174,3 +188,3 @@ case "ObjectKeyword": | ||
...objectJsonSchema, | ||
...getMeta(ast) | ||
...getJsonSchemaAnnotations(ast) | ||
}; | ||
@@ -180,3 +194,3 @@ case "StringKeyword": | ||
type: "string", | ||
...getMeta(ast) | ||
...getJsonSchemaAnnotations(ast) | ||
}; | ||
@@ -186,3 +200,3 @@ case "NumberKeyword": | ||
type: "number", | ||
...getMeta(ast) | ||
...getJsonSchemaAnnotations(ast) | ||
}; | ||
@@ -192,12 +206,13 @@ case "BooleanKeyword": | ||
type: "boolean", | ||
...getMeta(ast) | ||
...getJsonSchemaAnnotations(ast) | ||
}; | ||
case "BigIntKeyword": | ||
throw getMissingAnnotationError("`bigint`"); | ||
throw new Error(getMissingAnnotationErrorMessage("`bigint`", path)); | ||
case "SymbolKeyword": | ||
throw getMissingAnnotationError("`symbol`"); | ||
throw new Error(getMissingAnnotationErrorMessage("`symbol`", path)); | ||
case "TupleType": | ||
{ | ||
const elements = ast.elements.map(e => go(e.type, $defs)); | ||
const rest = ast.rest.map(ast => go(ast, $defs)); | ||
const len = ast.elements.length; | ||
const elements = ast.elements.map((e, i) => go(e.type, $defs, true, path.concat(i))); | ||
const rest = ast.rest.map(ast => go(ast, $defs, true, path)); | ||
const output = { | ||
@@ -209,3 +224,2 @@ type: "array" | ||
// --------------------------------------------- | ||
const len = elements.length; | ||
if (len > 0) { | ||
@@ -229,3 +243,3 @@ output.minItems = len - ast.elements.filter(element => element.isOptional).length; | ||
if (rest.length > 1) { | ||
throw new Error("Generating a JSON Schema for post-rest elements is not currently supported. You're welcome to contribute by submitting a Pull Request."); | ||
throw new Error(errors_.getErrorMessageWithPath("Generating a JSON Schema for post-rest elements is not currently supported. You're welcome to contribute by submitting a Pull Request.", path)); | ||
} | ||
@@ -241,3 +255,3 @@ } else { | ||
...output, | ||
...getMeta(ast) | ||
...getJsonSchemaAnnotations(ast) | ||
}; | ||
@@ -250,3 +264,3 @@ } | ||
...empty(), | ||
...getMeta(ast) | ||
...getJsonSchemaAnnotations(ast) | ||
}; | ||
@@ -261,3 +275,3 @@ } | ||
{ | ||
additionalProperties = go(is.type, $defs); | ||
additionalProperties = go(is.type, $defs, true, path); | ||
break; | ||
@@ -268,3 +282,3 @@ } | ||
patternProperties = { | ||
[AST.getTemplateLiteralRegExp(parameter).source]: go(is.type, $defs) | ||
[AST.getTemplateLiteralRegExp(parameter).source]: go(is.type, $defs, true, path) | ||
}; | ||
@@ -278,10 +292,10 @@ break; | ||
patternProperties = { | ||
[hook.value.pattern]: go(is.type, $defs) | ||
[hook.value.pattern]: go(is.type, $defs, true, path) | ||
}; | ||
break; | ||
} | ||
throw new Error(getUnsupportedIndexSignatureParameterErrorMessage(parameter)); | ||
throw new Error(getUnsupportedIndexSignatureParameterErrorMessage(parameter, path)); | ||
} | ||
case "SymbolKeyword": | ||
throw new Error(getUnsupportedIndexSignatureParameterErrorMessage(parameter)); | ||
throw new Error(getUnsupportedIndexSignatureParameterErrorMessage(parameter, path)); | ||
} | ||
@@ -291,4 +305,4 @@ } | ||
return { | ||
...go(pruneUndefinedKeyword(ps), $defs), | ||
...getMeta(ps) | ||
...go(pruneUndefinedKeyword(ps), $defs, true, path.concat(ps.name)), | ||
...getJsonSchemaAnnotations(ps) | ||
}; | ||
@@ -316,3 +330,3 @@ }); | ||
} else { | ||
throw new Error(`cannot encode ${String(name)} key to JSON Schema`); | ||
throw new Error(errors_.getErrorMessageWithPath(`cannot encode ${String(name)} key to JSON Schema`, path)); | ||
} | ||
@@ -331,3 +345,3 @@ } | ||
...output, | ||
...getMeta(ast) | ||
...getJsonSchemaAnnotations(ast) | ||
}; | ||
@@ -340,3 +354,3 @@ } | ||
for (const type of ast.types) { | ||
const schema = go(type, $defs); | ||
const schema = go(type, $defs, true, path); | ||
if ("const" in schema) { | ||
@@ -356,3 +370,3 @@ if (Object.keys(schema).length > 1) { | ||
const: enums[0], | ||
...getMeta(ast) | ||
...getJsonSchemaAnnotations(ast) | ||
}; | ||
@@ -362,3 +376,3 @@ } else { | ||
enum: enums, | ||
...getMeta(ast) | ||
...getJsonSchemaAnnotations(ast) | ||
}; | ||
@@ -378,3 +392,3 @@ } | ||
anyOf, | ||
...getMeta(ast) | ||
...getJsonSchemaAnnotations(ast) | ||
}; | ||
@@ -391,3 +405,3 @@ } | ||
})), | ||
...getMeta(ast) | ||
...getJsonSchemaAnnotations(ast) | ||
}; | ||
@@ -397,3 +411,3 @@ } | ||
{ | ||
throw new Error("cannot build a JSON Schema for a refinement without a JSON Schema annotation"); | ||
throw new Error(errors_.getErrorMessageWithPath("cannot build a JSON Schema for a refinement without a JSON Schema annotation", path)); | ||
} | ||
@@ -407,3 +421,3 @@ case "TemplateLiteral": | ||
pattern: regex.source, | ||
...getMeta(ast) | ||
...getJsonSchemaAnnotations(ast) | ||
}; | ||
@@ -415,10 +429,10 @@ } | ||
if (Option.isNone(identifier)) { | ||
throw new Error("Generating a JSON Schema for suspended schemas requires an identifier annotation"); | ||
throw new Error(errors_.getErrorMessageWithPath("Generating a JSON Schema for suspended schemas requires an identifier annotation", path)); | ||
} | ||
return go(ast.f(), $defs); | ||
return go(ast.f(), $defs, true, path); | ||
} | ||
case "Transformation": | ||
return go(ast.to, $defs); | ||
return go(ast.from, $defs, true, path); | ||
} | ||
}; | ||
//# sourceMappingURL=JSONSchema.js.map |
/** | ||
* @since 1.0.0 | ||
*/ | ||
import * as Array from "effect/Array"; | ||
import * as Arr from "effect/Array"; | ||
import { TaggedError } from "effect/Data"; | ||
@@ -417,12 +417,12 @@ import * as Effect from "effect/Effect"; | ||
/** @internal */ | ||
export const mergeParseOptions = (a, b) => { | ||
if (a === undefined) { | ||
return b; | ||
export const mergeParseOptions = (options, overrideOptions) => { | ||
if (overrideOptions === undefined || Predicate.isNumber(overrideOptions)) { | ||
return options; | ||
} | ||
if (b === undefined) { | ||
return a; | ||
if (options === undefined) { | ||
return overrideOptions; | ||
} | ||
const out = {}; | ||
out.errors = b.errors ?? a.errors; | ||
out.onExcessProperty = b.onExcessProperty ?? a.onExcessProperty; | ||
out.errors = overrideOptions.errors ?? options.errors; | ||
out.onExcessProperty = overrideOptions.onExcessProperty ?? options.onExcessProperty; | ||
return out; | ||
@@ -436,3 +436,5 @@ }; | ||
const parser = getEither(ast, isDecoding, options); | ||
return (input, overrideOptions) => Either.getOrThrowWith(parser(input, overrideOptions), e => new Error(TreeFormatter.formatIssueSync(e))); | ||
return (input, overrideOptions) => Either.getOrThrowWith(parser(input, overrideOptions), issue => new Error(TreeFormatter.formatIssueSync(issue), { | ||
cause: issue | ||
})); | ||
}; | ||
@@ -582,3 +584,5 @@ const getOption = (ast, isDecoding, options) => { | ||
if (Either.isLeft(result)) { | ||
throw new Error(TreeFormatter.formatIssueSync(result.left)); | ||
throw new Error(TreeFormatter.formatIssueSync(result.left), { | ||
cause: result.left | ||
}); | ||
} | ||
@@ -698,3 +702,3 @@ }; | ||
return (input, options) => { | ||
if (!Array.isArray(input)) { | ||
if (!Arr.isArray(input)) { | ||
return Either.left(new Type(ast, input)); | ||
@@ -789,3 +793,3 @@ } | ||
// --------------------------------------------- | ||
if (Array.isNonEmptyReadonlyArray(rest)) { | ||
if (Arr.isNonEmptyReadonlyArray(rest)) { | ||
const [head, ...tail] = rest; | ||
@@ -887,3 +891,3 @@ for (; i < len - tail.length; i++) { | ||
output | ||
}) => Array.isNonEmptyArray(es) ? Either.left(new TupleType(ast, input, sortByIndex(es), sortByIndex(output))) : Either.right(sortByIndex(output)); | ||
}) => Arr.isNonEmptyArray(es) ? Either.left(new TupleType(ast, input, sortByIndex(es), sortByIndex(output))) : Either.right(sortByIndex(output)); | ||
if (queue && queue.length > 0) { | ||
@@ -893,4 +897,4 @@ const cqueue = queue; | ||
const state = { | ||
es: Array.copy(es), | ||
output: Array.copy(output) | ||
es: Arr.copy(es), | ||
output: Arr.copy(output) | ||
}; | ||
@@ -1084,3 +1088,3 @@ return Effect.flatMap(Effect.forEach(cqueue, f => f(state), { | ||
output | ||
}) => Array.isNonEmptyArray(es) ? Either.left(new TypeLiteral(ast, input, sortByIndex(es), output)) : Either.right(output); | ||
}) => Arr.isNonEmptyArray(es) ? Either.left(new TypeLiteral(ast, input, sortByIndex(es), output)) : Either.right(output); | ||
if (queue && queue.length > 0) { | ||
@@ -1090,3 +1094,3 @@ const cqueue = queue; | ||
const state = { | ||
es: Array.copy(es), | ||
es: Arr.copy(es), | ||
output: Object.assign({}, output) | ||
@@ -1189,3 +1193,3 @@ }; | ||
// --------------------------------------------- | ||
const computeResult = es => Array.isNonEmptyArray(es) ? es.length === 1 && es[0][1]._tag === "Type" ? Either.left(es[0][1]) : Either.left(new Union(ast, input, sortByIndex(es))) : | ||
const computeResult = es => Arr.isNonEmptyArray(es) ? es.length === 1 && es[0][1]._tag === "Type" ? Either.left(es[0][1]) : Either.left(new Union(ast, input, sortByIndex(es))) : | ||
// this should never happen | ||
@@ -1197,3 +1201,3 @@ Either.left(new Type(AST.neverKeyword, input)); | ||
const state = { | ||
es: Array.copy(es) | ||
es: Arr.copy(es) | ||
}; | ||
@@ -1200,0 +1204,0 @@ return Effect.flatMap(Effect.forEach(cqueue, f => f(state), { |
/** | ||
* @since 1.0.0 | ||
*/ | ||
import * as Array from "effect/Array"; | ||
import * as Arr from "effect/Array"; | ||
import * as Option from "effect/Option"; | ||
@@ -26,3 +26,3 @@ import * as AST from "./AST.js"; | ||
*/ | ||
export const make = schema => compile(schema.ast); | ||
export const make = schema => compile(schema.ast, []); | ||
const getHook = /*#__PURE__*/AST.getAnnotation(PrettyHookId); | ||
@@ -36,2 +36,3 @@ const getMatcher = defaultPretty => ast => Option.match(getHook(ast), { | ||
const formatUnknown = /*#__PURE__*/getMatcher(util_.formatUnknown); | ||
const getPrettyErrorMessage = (message, path) => errors_.getErrorMessageWithPath(`cannot build a Pretty for ${message}`, path); | ||
/** | ||
@@ -41,8 +42,8 @@ * @since 1.0.0 | ||
export const match = { | ||
"Declaration": (ast, go) => { | ||
"Declaration": (ast, go, path) => { | ||
const hook = getHook(ast); | ||
if (Option.isSome(hook)) { | ||
return hook.value(...ast.typeParameters.map(go)); | ||
return hook.value(...ast.typeParameters.map(tp => go(tp, path))); | ||
} | ||
throw new Error(errors_.getPrettyErrorMessage(`a declaration without annotations (${ast})`)); | ||
throw new Error(getPrettyErrorMessage(`a declaration without annotations (${ast})`, path)); | ||
}, | ||
@@ -66,3 +67,3 @@ "VoidKeyword": /*#__PURE__*/getMatcher(() => "void(0)"), | ||
"Enums": stringify, | ||
"TupleType": (ast, go) => { | ||
"TupleType": (ast, go, path) => { | ||
const hook = getHook(ast); | ||
@@ -72,4 +73,4 @@ if (Option.isSome(hook)) { | ||
} | ||
const elements = ast.elements.map(e => go(e.type)); | ||
const rest = ast.rest.map(go); | ||
const elements = ast.elements.map((e, i) => go(e.type, path.concat(i))); | ||
const rest = ast.rest.map(ast => go(ast, path)); | ||
return input => { | ||
@@ -93,3 +94,3 @@ const output = []; | ||
// --------------------------------------------- | ||
if (Array.isNonEmptyReadonlyArray(rest)) { | ||
if (Arr.isNonEmptyReadonlyArray(rest)) { | ||
const [head, ...tail] = rest; | ||
@@ -110,3 +111,3 @@ for (; i < input.length - tail.length; i++) { | ||
}, | ||
"TypeLiteral": (ast, go) => { | ||
"TypeLiteral": (ast, go, path) => { | ||
const hook = getHook(ast); | ||
@@ -116,4 +117,4 @@ if (Option.isSome(hook)) { | ||
} | ||
const propertySignaturesTypes = ast.propertySignatures.map(f => go(f.type)); | ||
const indexSignatureTypes = ast.indexSignatures.map(is => go(is.type)); | ||
const propertySignaturesTypes = ast.propertySignatures.map(ps => go(ps.type, path.concat(ps.name))); | ||
const indexSignatureTypes = ast.indexSignatures.map(is => go(is.type, path)); | ||
const expectedKeys = {}; | ||
@@ -134,3 +135,3 @@ for (let i = 0; i < propertySignaturesTypes.length; i++) { | ||
} | ||
output.push(`${getPrettyPropertyKey(name)}: ${propertySignaturesTypes[i](input[name])}`); | ||
output.push(`${util_.formatPropertyKey(name)}: ${propertySignaturesTypes[i](input[name])}`); | ||
} | ||
@@ -148,10 +149,10 @@ // --------------------------------------------- | ||
} | ||
output.push(`${getPrettyPropertyKey(key)}: ${type(input[key])}`); | ||
output.push(`${util_.formatPropertyKey(key)}: ${type(input[key])}`); | ||
} | ||
} | ||
} | ||
return Array.isNonEmptyReadonlyArray(output) ? "{ " + output.join(", ") + " }" : "{}"; | ||
return Arr.isNonEmptyReadonlyArray(output) ? "{ " + output.join(", ") + " }" : "{}"; | ||
}; | ||
}, | ||
"Union": (ast, go) => { | ||
"Union": (ast, go, path) => { | ||
const hook = getHook(ast); | ||
@@ -163,3 +164,3 @@ if (Option.isSome(hook)) { | ||
ast | ||
}), go(ast)]); | ||
}), go(ast, path)]); | ||
return a => { | ||
@@ -170,6 +171,6 @@ const index = types.findIndex(([is]) => is(a)); | ||
}, | ||
"Suspend": (ast, go) => { | ||
"Suspend": (ast, go, path) => { | ||
return Option.match(getHook(ast), { | ||
onNone: () => { | ||
const get = util_.memoizeThunk(() => go(ast.f())); | ||
const get = util_.memoizeThunk(() => go(ast.f(), path)); | ||
return a => get()(a); | ||
@@ -180,11 +181,11 @@ }, | ||
}, | ||
"Refinement": (ast, go) => { | ||
"Refinement": (ast, go, path) => { | ||
return Option.match(getHook(ast), { | ||
onNone: () => go(ast.from), | ||
onNone: () => go(ast.from, path), | ||
onSome: handler => handler() | ||
}); | ||
}, | ||
"Transformation": (ast, go) => { | ||
"Transformation": (ast, go, path) => { | ||
return Option.match(getHook(ast), { | ||
onNone: () => go(ast.to), | ||
onNone: () => go(ast.to, path), | ||
onSome: handler => handler() | ||
@@ -195,3 +196,2 @@ }); | ||
const compile = /*#__PURE__*/AST.getCompiler(match); | ||
const getPrettyPropertyKey = name => typeof name === "string" ? JSON.stringify(name) : String(name); | ||
//# sourceMappingURL=Pretty.js.map |
@@ -64,3 +64,3 @@ /** | ||
}; | ||
const getPrevMessage = issue => { | ||
const getInnerMessage = issue => { | ||
switch (issue._tag) { | ||
@@ -75,3 +75,5 @@ case "Refinement": | ||
case "Transformation": | ||
return getMessage(issue.error); | ||
{ | ||
return getMessage(issue.error); | ||
} | ||
} | ||
@@ -82,6 +84,26 @@ return Option.none(); | ||
const out = annotation(issue); | ||
return Predicate.isString(out) ? Effect.succeed(out) : out; | ||
return Predicate.isString(out) ? Effect.succeed({ | ||
message: out, | ||
override: false | ||
}) : Effect.isEffect(out) ? Effect.map(out, message => ({ | ||
message, | ||
override: false | ||
})) : Predicate.isString(out.message) ? Effect.succeed({ | ||
message: out.message, | ||
override: out.override | ||
}) : Effect.map(out.message, message => ({ | ||
message, | ||
override: out.override | ||
})); | ||
})); | ||
/** @internal */ | ||
export const getMessage = issue => Effect.catchAll(getPrevMessage(issue), () => getCurrentMessage(issue)); | ||
export const getMessage = issue => { | ||
const current = getCurrentMessage(issue); | ||
return getInnerMessage(issue).pipe(Effect.flatMap(inner => Effect.map(current, current => current.override ? current.message : inner)), Effect.catchAll(() => Effect.flatMap(current, current => { | ||
if (!current.override && (issue._tag === "Refinement" && issue.kind !== "Predicate" || issue._tag === "Transformation" && issue.kind !== "Transformation")) { | ||
return Option.none(); | ||
} | ||
return Effect.succeed(current.message); | ||
}))); | ||
}; | ||
const getParseIssueTitleAnnotation = issue => Option.filterMap(AST.getParseIssueTitleAnnotation(issue.ast), annotation => Option.fromNullable(annotation(issue))); | ||
@@ -117,5 +139,5 @@ /** @internal */ | ||
case "TupleType": | ||
return getTree(e, () => Effect.map(Effect.forEach(e.errors, index => Effect.map(go(index.error), tree => make(`[${index.index}]`, [tree]))), forest => make(getParseIssueTitle(e), forest))); | ||
return getTree(e, () => Effect.map(Effect.forEach(e.errors, index => Effect.map(go(index.error), tree => make(`[${util_.formatPropertyKey(index.index)}]`, [tree]))), forest => make(getParseIssueTitle(e), forest))); | ||
case "TypeLiteral": | ||
return getTree(e, () => Effect.map(Effect.forEach(e.errors, key => Effect.map(go(key.error), tree => make(`[${util_.formatUnknown(key.key)}]`, [tree]))), forest => make(getParseIssueTitle(e), forest))); | ||
return getTree(e, () => Effect.map(Effect.forEach(e.errors, key => Effect.map(go(key.error), tree => make(`[${util_.formatPropertyKey(key.key)}]`, [tree]))), forest => make(getParseIssueTitle(e), forest))); | ||
case "Transformation": | ||
@@ -122,0 +144,0 @@ return getTree(e, () => Effect.map(go(e.error), tree => make(getParseIssueTitle(e), [make(formatTransformationKind(e.kind), [tree])]))); |
{ | ||
"name": "@effect/schema", | ||
"version": "0.0.0-snapshot-78b767c2b1625186e17131761a0edbac25d21850", | ||
"version": "0.0.0-snapshot-79dc882eafd6ddb60780764e2f663a6087b52622", | ||
"description": "Modeling the schema of data structures as first-class values", | ||
@@ -8,10 +8,15 @@ "license": "MIT", | ||
"type": "git", | ||
"url": "https://github.com/effect-ts/effect.git", | ||
"url": "https://github.com/Effect-TS/effect.git", | ||
"directory": "packages/schema" | ||
}, | ||
"sideEffects": [], | ||
"dependencies": { | ||
"fast-check": "^3.17.2" | ||
}, | ||
"peerDependencies": { | ||
"fast-check": "^3.13.2", | ||
"effect": "^0.0.0-snapshot-78b767c2b1625186e17131761a0edbac25d21850" | ||
"effect": "^0.0.0-snapshot-79dc882eafd6ddb60780764e2f663a6087b52622" | ||
}, | ||
"publishConfig": { | ||
"provenance": true | ||
}, | ||
"main": "./dist/cjs/index.js", | ||
@@ -18,0 +23,0 @@ "module": "./dist/esm/index.js", |
@@ -5,3 +5,3 @@ /** | ||
import * as Array from "effect/Array" | ||
import * as Arr from "effect/Array" | ||
import * as Option from "effect/Option" | ||
@@ -50,3 +50,3 @@ import * as Predicate from "effect/Predicate" | ||
*/ | ||
export const makeLazy = <A, I, R>(schema: Schema.Schema<A, I, R>): LazyArbitrary<A> => go(schema.ast, {}) | ||
export const makeLazy = <A, I, R>(schema: Schema.Schema<A, I, R>): LazyArbitrary<A> => go(schema.ast, {}, []) | ||
@@ -93,8 +93,11 @@ /** | ||
const getRefinementFromArbitrary = (ast: AST.Refinement, options: Options) => { | ||
const getRefinementFromArbitrary = (ast: AST.Refinement, options: Options, path: ReadonlyArray<PropertyKey>) => { | ||
const constraints = combineConstraints(options.constraints, getConstraints(ast)) | ||
return go(ast.from, constraints ? { ...options, constraints } : options) | ||
return go(ast.from, constraints ? { ...options, constraints } : options, path) | ||
} | ||
const go = (ast: AST.AST, options: Options): LazyArbitrary<any> => { | ||
const getArbitraryErrorMessage = (message: string, path: ReadonlyArray<PropertyKey>) => | ||
errors_.getErrorMessageWithPath(`cannot build an Arbitrary for ${message}`, path) | ||
const go = (ast: AST.AST, options: Options, path: ReadonlyArray<PropertyKey>): LazyArbitrary<any> => { | ||
const hook = getHook(ast) | ||
@@ -104,5 +107,5 @@ if (Option.isSome(hook)) { | ||
case "Declaration": | ||
return hook.value(...ast.typeParameters.map((p) => go(p, options))) | ||
return hook.value(...ast.typeParameters.map((p) => go(p, options, path))) | ||
case "Refinement": | ||
return hook.value(getRefinementFromArbitrary(ast, options)) | ||
return hook.value(getRefinementFromArbitrary(ast, options, path)) | ||
default: | ||
@@ -114,3 +117,3 @@ return hook.value() | ||
case "Declaration": { | ||
throw new Error(errors_.getArbitraryErrorMessage(`a declaration without annotations (${ast})`)) | ||
throw new Error(getArbitraryErrorMessage(`a declaration without annotations (${ast})`, path)) | ||
} | ||
@@ -126,3 +129,3 @@ case "Literal": | ||
return () => { | ||
throw new Error(errors_.getArbitraryErrorMessage("`never`")) | ||
throw new Error(getArbitraryErrorMessage("`never`", path)) | ||
} | ||
@@ -189,4 +192,5 @@ case "UnknownKeyword": | ||
let hasOptionals = false | ||
let i = 0 | ||
for (const element of ast.elements) { | ||
elements.push(go(element.type, options)) | ||
elements.push(go(element.type, options, path.concat(i++))) | ||
if (element.isOptional) { | ||
@@ -196,3 +200,3 @@ hasOptionals = true | ||
} | ||
const rest = ast.rest.map((e) => go(e, options)) | ||
const rest = ast.rest.map((e) => go(e, options, path)) | ||
return (fc) => { | ||
@@ -222,3 +226,3 @@ // --------------------------------------------- | ||
// --------------------------------------------- | ||
if (Array.isNonEmptyReadonlyArray(rest)) { | ||
if (Arr.isNonEmptyReadonlyArray(rest)) { | ||
const [head, ...tail] = rest | ||
@@ -252,5 +256,5 @@ const arb = head(fc) | ||
case "TypeLiteral": { | ||
const propertySignaturesTypes = ast.propertySignatures.map((f) => go(f.type, options)) | ||
const propertySignaturesTypes = ast.propertySignatures.map((ps) => go(ps.type, options, path.concat(ps.name))) | ||
const indexSignatures = ast.indexSignatures.map((is) => | ||
[go(is.parameter, options), go(is.type, options)] as const | ||
[go(is.parameter, options, path), go(is.type, options, path)] as const | ||
) | ||
@@ -287,3 +291,3 @@ return (fc) => { | ||
case "Union": { | ||
const types = ast.types.map((t) => go(t, options)) | ||
const types = ast.types.map((t) => go(t, options, path)) | ||
return (fc) => fc.oneof({ depthSize }, ...types.map((arb) => arb(fc))) | ||
@@ -293,3 +297,3 @@ } | ||
if (ast.enums.length === 0) { | ||
throw new Error(errors_.getArbitraryErrorMessage("an empty enum")) | ||
throw new Error(getArbitraryErrorMessage("an empty enum", path)) | ||
} | ||
@@ -299,11 +303,11 @@ return (fc) => fc.oneof(...ast.enums.map(([_, value]) => fc.constant(value))) | ||
case "Refinement": { | ||
const from = getRefinementFromArbitrary(ast, options) | ||
const from = getRefinementFromArbitrary(ast, options, path) | ||
return (fc) => from(fc).filter((a) => Option.isNone(ast.filter(a, AST.defaultParseOption, ast))) | ||
} | ||
case "Suspend": { | ||
const get = util_.memoizeThunk(() => go(ast.f(), { ...options, isSuspend: true })) | ||
const get = util_.memoizeThunk(() => go(ast.f(), { ...options, isSuspend: true }, path)) | ||
return (fc) => fc.constant(null).chain(() => get()(fc)) | ||
} | ||
case "Transformation": | ||
return go(ast.to, options) | ||
return go(ast.to, options, path) | ||
} | ||
@@ -310,0 +314,0 @@ } |
@@ -5,3 +5,3 @@ /** | ||
import * as Array from "effect/Array" | ||
import * as Arr from "effect/Array" | ||
import * as Effect from "effect/Effect" | ||
@@ -16,3 +16,13 @@ import type * as ParseResult from "./ParseResult.js" | ||
export interface Issue { | ||
readonly _tag: ParseResult.ParseIssue["_tag"] | ParseResult.Missing["_tag"] | ParseResult.Unexpected["_tag"] | ||
readonly _tag: | ||
| "Transformation" | ||
| "Type" | ||
| "Declaration" | ||
| "Refinement" | ||
| "TupleType" | ||
| "TypeLiteral" | ||
| "Union" | ||
| "Forbidden" | ||
| "Missing" | ||
| "Unexpected" | ||
readonly path: ReadonlyArray<PropertyKey> | ||
@@ -46,2 +56,4 @@ readonly message: string | ||
const succeed = (issue: Issue) => Effect.succeed([issue]) | ||
const getArray = ( | ||
@@ -54,5 +66,7 @@ issue: ParseResult.ParseIssue, | ||
onFailure, | ||
onSuccess: (message) => Effect.succeed<Array<Issue>>([{ _tag: issue._tag, path, message }]) | ||
onSuccess: (message) => succeed({ _tag: issue._tag, path, message }) | ||
}) | ||
const flatten = (eff: Effect.Effect<Array<Array<Issue>>>): Effect.Effect<Array<Issue>> => Effect.map(eff, Arr.flatten) | ||
const go = ( | ||
@@ -67,10 +81,10 @@ e: ParseResult.ParseIssue | ParseResult.Missing | ParseResult.Unexpected, | ||
case "Forbidden": | ||
return Effect.succeed([{ _tag, path, message: TreeFormatter.formatForbiddenMessage(e) }]) | ||
return succeed({ _tag, path, message: TreeFormatter.formatForbiddenMessage(e) }) | ||
case "Unexpected": | ||
return Effect.succeed([{ _tag, path, message: `is unexpected, expected ${e.ast.toString(true)}` }]) | ||
return succeed({ _tag, path, message: `is unexpected, expected ${e.ast.toString(true)}` }) | ||
case "Missing": | ||
return Effect.succeed([{ _tag, path, message: "is missing" }]) | ||
return succeed({ _tag, path, message: "is missing" }) | ||
case "Union": | ||
return getArray(e, path, () => | ||
Effect.map( | ||
flatten( | ||
Effect.forEach(e.errors, (e) => { | ||
@@ -83,22 +97,21 @@ switch (e._tag) { | ||
} | ||
}), | ||
Array.flatten | ||
}) | ||
)) | ||
case "TupleType": | ||
return getArray(e, path, () => | ||
Effect.map( | ||
Effect.forEach(e.errors, (index) => go(index.error, [...path, index.index])), | ||
Array.flatten | ||
)) | ||
return getArray( | ||
e, | ||
path, | ||
() => flatten(Effect.forEach(e.errors, (index) => go(index.error, path.concat(index.index)))) | ||
) | ||
case "TypeLiteral": | ||
return getArray(e, path, () => | ||
Effect.map( | ||
Effect.forEach(e.errors, (key) => go(key.error, [...path, key.key])), | ||
Array.flatten | ||
)) | ||
return getArray( | ||
e, | ||
path, | ||
() => flatten(Effect.forEach(e.errors, (key) => go(key.error, path.concat(key.key)))) | ||
) | ||
case "Declaration": | ||
case "Refinement": | ||
case "Transformation": | ||
case "Refinement": | ||
case "Declaration": | ||
return getArray(e, path, () => go(e.error, path)) | ||
} | ||
} |
@@ -5,3 +5,3 @@ /** | ||
import * as Array from "effect/Array" | ||
import * as Arr from "effect/Array" | ||
import * as Equal from "effect/Equal" | ||
@@ -41,3 +41,3 @@ import * as Equivalence from "effect/Equivalence" | ||
*/ | ||
export const make = <A, I, R>(schema: Schema.Schema<A, I, R>): Equivalence.Equivalence<A> => go(schema.ast) | ||
export const make = <A, I, R>(schema: Schema.Schema<A, I, R>): Equivalence.Equivalence<A> => go(schema.ast, []) | ||
@@ -50,3 +50,6 @@ const getHook = AST.getAnnotation< | ||
const go = (ast: AST.AST): Equivalence.Equivalence<any> => { | ||
const getEquivalenceErrorMessage = (message: string, path: ReadonlyArray<PropertyKey>) => | ||
errors_.getErrorMessageWithPath(`cannot build an Equivalence for ${message}`, path) | ||
const go = (ast: AST.AST, path: ReadonlyArray<PropertyKey>): Equivalence.Equivalence<any> => { | ||
const hook = getHook(ast) | ||
@@ -56,5 +59,5 @@ if (Option.isSome(hook)) { | ||
case "Declaration": | ||
return hook.value(...ast.typeParameters.map(go)) | ||
return hook.value(...ast.typeParameters.map((tp) => go(tp, path))) | ||
case "Refinement": | ||
return hook.value(go(ast.from)) | ||
return hook.value(go(ast.from, path)) | ||
default: | ||
@@ -66,5 +69,5 @@ return hook.value() | ||
case "NeverKeyword": | ||
throw new Error(errors_.getEquivalenceErrorMessage("`never`")) | ||
throw new Error(getEquivalenceErrorMessage("`never`", path)) | ||
case "Transformation": | ||
return go(ast.to) | ||
return go(ast.to, path) | ||
case "Declaration": | ||
@@ -87,10 +90,10 @@ case "Literal": | ||
case "Refinement": | ||
return go(ast.from) | ||
return go(ast.from, path) | ||
case "Suspend": { | ||
const get = util_.memoizeThunk(() => go(ast.f())) | ||
const get = util_.memoizeThunk(() => go(ast.f(), path)) | ||
return (a, b) => get()(a, b) | ||
} | ||
case "TupleType": { | ||
const elements = ast.elements.map((element) => go(element.type)) | ||
const rest = ast.rest.map(go) | ||
const elements = ast.elements.map((element, i) => go(element.type, path.concat(i))) | ||
const rest = ast.rest.map((ast) => go(ast, path)) | ||
return Equivalence.make((a, b) => { | ||
@@ -113,3 +116,3 @@ const len = a.length | ||
// --------------------------------------------- | ||
if (Array.isNonEmptyReadonlyArray(rest)) { | ||
if (Arr.isNonEmptyReadonlyArray(rest)) { | ||
const [head, ...tail] = rest | ||
@@ -138,4 +141,4 @@ for (; i < len - tail.length; i++) { | ||
} | ||
const propertySignatures = ast.propertySignatures.map((ps) => go(ps.type)) | ||
const indexSignatures = ast.indexSignatures.map((is) => go(is.type)) | ||
const propertySignatures = ast.propertySignatures.map((ps) => go(ps.type, path.concat(ps.name))) | ||
const indexSignatures = ast.indexSignatures.map((is) => go(is.type, path)) | ||
return Equivalence.make((a, b) => { | ||
@@ -215,3 +218,3 @@ const aStringKeys = Object.keys(a) | ||
} | ||
const tuples = candidates.map((ast) => [go(ast), ParseResult.is({ ast } as any)] as const) | ||
const tuples = candidates.map((ast) => [go(ast, path), ParseResult.is({ ast } as any)] as const) | ||
for (let i = 0; i < tuples.length; i++) { | ||
@@ -218,0 +221,0 @@ const [equivalence, is] = tuples[i] |
@@ -8,27 +8,11 @@ import * as util_ from "./util.js" | ||
/** @internal */ | ||
export const getDuplicateIndexSignatureErrorMessage = (name: "string" | "symbol"): string => | ||
`Duplicate index signature for type \`${name}\`` | ||
export const getErrorMessage = (api: string, message: string) => `${api}: ${message}` | ||
/** @internal */ | ||
export const getIndexSignatureParameterErrorMessage = | ||
"An index signature parameter type must be `string`, `symbol`, a template literal type or a refinement of the previous types" | ||
/** @internal */ | ||
export const getRequiredElementFollowinAnOptionalElementErrorMessage = | ||
"A required element cannot follow an optional element. ts(1257)" | ||
/** @internal */ | ||
export const getDuplicatePropertySignatureTransformationErrorMessage = (name: PropertyKey): string => | ||
`Duplicate property signature transformation ${util_.formatUnknown(name)}` | ||
/** @internal */ | ||
export const getArbitraryErrorMessage = (message: string) => `cannot build an Arbitrary for ${message}` | ||
/** @internal */ | ||
export const getPrettyErrorMessage = (message: string) => `cannot build a Pretty for ${message}` | ||
/** @internal */ | ||
export const getEquivalenceErrorMessage = (message: string) => `cannot build an Equivalence for ${message}` | ||
/** @internal */ | ||
export const getAPIErrorMessage = (api: string, message: string) => `${api}: ${message}` | ||
export const getErrorMessageWithPath = (message: string, path: ReadonlyArray<PropertyKey>) => { | ||
let out = message | ||
if (path.length > 0) { | ||
out += ` (path [${path.map(util_.formatPropertyKey).join(", ")}])` | ||
} | ||
return out | ||
} |
@@ -75,1 +75,5 @@ import * as Predicate from "effect/Predicate" | ||
} | ||
/** @internal */ | ||
export const formatPropertyKey = (name: PropertyKey): string => | ||
typeof name === "string" ? JSON.stringify(name) : String(name) |
@@ -9,2 +9,3 @@ /** | ||
import * as AST from "./AST.js" | ||
import * as errors_ from "./internal/errors.js" | ||
import type * as Schema from "./Schema.js" | ||
@@ -16,3 +17,14 @@ | ||
*/ | ||
export interface JsonSchema7Any { | ||
export interface JsonSchemaAnnotations { | ||
title?: string | ||
description?: string | ||
default?: unknown | ||
examples?: Array<unknown> | ||
} | ||
/** | ||
* @category model | ||
* @since 1.0.0 | ||
*/ | ||
export interface JsonSchema7Any extends JsonSchemaAnnotations { | ||
$id: "/schemas/any" | ||
@@ -25,3 +37,3 @@ } | ||
*/ | ||
export interface JsonSchema7Unknown { | ||
export interface JsonSchema7Unknown extends JsonSchemaAnnotations { | ||
$id: "/schemas/unknown" | ||
@@ -34,3 +46,3 @@ } | ||
*/ | ||
export interface JsonSchema7object { | ||
export interface JsonSchema7object extends JsonSchemaAnnotations { | ||
$id: "/schemas/object" | ||
@@ -47,3 +59,3 @@ oneOf: [ | ||
*/ | ||
export interface JsonSchema7empty { | ||
export interface JsonSchema7empty extends JsonSchemaAnnotations { | ||
$id: "/schemas/{}" | ||
@@ -60,3 +72,3 @@ oneOf: [ | ||
*/ | ||
export interface JsonSchema7Ref { | ||
export interface JsonSchema7Ref extends JsonSchemaAnnotations { | ||
$ref: string | ||
@@ -69,3 +81,3 @@ } | ||
*/ | ||
export interface JsonSchema7Const { | ||
export interface JsonSchema7Const extends JsonSchemaAnnotations { | ||
const: AST.LiteralValue | ||
@@ -78,3 +90,3 @@ } | ||
*/ | ||
export interface JsonSchema7String { | ||
export interface JsonSchema7String extends JsonSchemaAnnotations { | ||
type: "string" | ||
@@ -84,3 +96,2 @@ minLength?: number | ||
pattern?: string | ||
description?: string | ||
} | ||
@@ -92,3 +103,3 @@ | ||
*/ | ||
export interface JsonSchema7Numeric { | ||
export interface JsonSchema7Numeric extends JsonSchemaAnnotations { | ||
minimum?: number | ||
@@ -120,3 +131,3 @@ exclusiveMinimum?: number | ||
*/ | ||
export interface JsonSchema7Boolean { | ||
export interface JsonSchema7Boolean extends JsonSchemaAnnotations { | ||
type: "boolean" | ||
@@ -129,3 +140,3 @@ } | ||
*/ | ||
export interface JsonSchema7Array { | ||
export interface JsonSchema7Array extends JsonSchemaAnnotations { | ||
type: "array" | ||
@@ -142,3 +153,3 @@ items?: JsonSchema7 | Array<JsonSchema7> | ||
*/ | ||
export interface JsonSchema7OneOf { | ||
export interface JsonSchema7OneOf extends JsonSchemaAnnotations { | ||
oneOf: Array<JsonSchema7> | ||
@@ -151,3 +162,3 @@ } | ||
*/ | ||
export interface JsonSchema7Enum { | ||
export interface JsonSchema7Enum extends JsonSchemaAnnotations { | ||
enum: Array<AST.LiteralValue> | ||
@@ -160,3 +171,3 @@ } | ||
*/ | ||
export interface JsonSchema7Enums { | ||
export interface JsonSchema7Enums extends JsonSchemaAnnotations { | ||
$comment: "/schemas/enums" | ||
@@ -173,3 +184,3 @@ oneOf: Array<{ | ||
*/ | ||
export interface JsonSchema7AnyOf { | ||
export interface JsonSchema7AnyOf extends JsonSchemaAnnotations { | ||
anyOf: Array<JsonSchema7> | ||
@@ -182,3 +193,3 @@ } | ||
*/ | ||
export interface JsonSchema7Object { | ||
export interface JsonSchema7Object extends JsonSchemaAnnotations { | ||
type: "object" | ||
@@ -228,3 +239,3 @@ required: Array<string> | ||
const $defs: Record<string, any> = {} | ||
const jsonSchema = go(schema.ast, $defs) | ||
const jsonSchema = go(schema.ast, $defs, true, []) | ||
const out: JsonSchema7Root = { | ||
@@ -268,3 +279,3 @@ $schema, | ||
const getMeta = (annotated: AST.Annotated) => | ||
const getJsonSchemaAnnotations = (annotated: AST.Annotated): JsonSchemaAnnotations => | ||
Record.getSomes({ | ||
@@ -285,10 +296,9 @@ description: AST.getDescriptionAnnotation(annotated), | ||
const getMissingAnnotationError = (name: string) => { | ||
const out = new Error(`cannot build a JSON Schema for ${name} without a JSON Schema annotation`) | ||
out.name = "MissingAnnotation" | ||
return out | ||
} | ||
const getMissingAnnotationErrorMessage = (name: string, path: ReadonlyArray<PropertyKey>): string => | ||
errors_.getErrorMessageWithPath(`cannot build a JSON Schema for ${name} without a JSON Schema annotation`, path) | ||
const getUnsupportedIndexSignatureParameterErrorMessage = (parameter: AST.AST): string => | ||
`Unsupported index signature parameter (${parameter})` | ||
const getUnsupportedIndexSignatureParameterErrorMessage = ( | ||
parameter: AST.AST, | ||
path: ReadonlyArray<PropertyKey> | ||
): string => errors_.getErrorMessageWithPath(`unsupported index signature parameter (${parameter})`, path) | ||
@@ -300,20 +310,43 @@ /** @internal */ | ||
const go = (ast: AST.AST, $defs: Record<string, JsonSchema7>, handleIdentifier: boolean = true): JsonSchema7 => { | ||
const hasTransformation = (ast: AST.Refinement): boolean => { | ||
switch (ast.from._tag) { | ||
case "Transformation": | ||
return true | ||
case "Refinement": | ||
return hasTransformation(ast.from) | ||
case "Suspend": | ||
{ | ||
const from = ast.from.f() | ||
if (AST.isRefinement(from)) { | ||
return hasTransformation(from) | ||
} | ||
} | ||
break | ||
} | ||
return false | ||
} | ||
const go = ( | ||
ast: AST.AST, | ||
$defs: Record<string, JsonSchema7>, | ||
handleIdentifier: boolean, | ||
path: ReadonlyArray<PropertyKey> | ||
): JsonSchema7 => { | ||
const hook = AST.getJSONSchemaAnnotation(ast) | ||
if (Option.isSome(hook)) { | ||
const handler = hook.value as JsonSchema7 | ||
switch (ast._tag) { | ||
case "Refinement": | ||
try { | ||
return { ...go(ast.from, $defs), ...getMeta(ast), ...handler } | ||
} catch (e) { | ||
if (e instanceof Error && e.name === "MissingAnnotation") { | ||
return { ...getMeta(ast), ...handler } | ||
} | ||
throw e | ||
} | ||
if (AST.isRefinement(ast) && !hasTransformation(ast)) { | ||
try { | ||
return { ...go(ast.from, $defs, true, path), ...getJsonSchemaAnnotations(ast), ...handler } | ||
} catch (e) { | ||
return { ...getJsonSchemaAnnotations(ast), ...handler } | ||
} | ||
} | ||
return handler | ||
} | ||
if (handleIdentifier) { | ||
const surrogate = AST.getSurrogateAnnotation(ast) | ||
if (Option.isSome(surrogate)) { | ||
return go(surrogate.value, $defs, handleIdentifier, path) | ||
} | ||
if (handleIdentifier && !AST.isTransformation(ast)) { | ||
const identifier = AST.getJSONIdentifier(ast) | ||
@@ -325,3 +358,3 @@ if (Option.isSome(identifier)) { | ||
$defs[id] = out | ||
$defs[id] = go(ast, $defs, false) | ||
$defs[id] = go(ast, $defs, false, path) | ||
} | ||
@@ -333,43 +366,44 @@ return out | ||
case "Declaration": | ||
throw getMissingAnnotationError("a declaration") | ||
throw new Error(getMissingAnnotationErrorMessage("a declaration", path)) | ||
case "Literal": { | ||
const literal = ast.literal | ||
if (literal === null) { | ||
return { const: null, ...getMeta(ast) } | ||
return { const: null, ...getJsonSchemaAnnotations(ast) } | ||
} else if (Predicate.isString(literal)) { | ||
return { const: literal, ...getMeta(ast) } | ||
return { const: literal, ...getJsonSchemaAnnotations(ast) } | ||
} else if (Predicate.isNumber(literal)) { | ||
return { const: literal, ...getMeta(ast) } | ||
return { const: literal, ...getJsonSchemaAnnotations(ast) } | ||
} else if (Predicate.isBoolean(literal)) { | ||
return { const: literal, ...getMeta(ast) } | ||
return { const: literal, ...getJsonSchemaAnnotations(ast) } | ||
} | ||
throw getMissingAnnotationError("a bigint literal") | ||
throw new Error(getMissingAnnotationErrorMessage("a bigint literal", path)) | ||
} | ||
case "UniqueSymbol": | ||
throw getMissingAnnotationError("a unique symbol") | ||
throw new Error(getMissingAnnotationErrorMessage("a unique symbol", path)) | ||
case "UndefinedKeyword": | ||
throw getMissingAnnotationError("`undefined`") | ||
throw new Error(getMissingAnnotationErrorMessage("`undefined`", path)) | ||
case "VoidKeyword": | ||
throw getMissingAnnotationError("`void`") | ||
throw new Error(getMissingAnnotationErrorMessage("`void`", path)) | ||
case "NeverKeyword": | ||
throw getMissingAnnotationError("`never`") | ||
throw new Error(getMissingAnnotationErrorMessage("`never`", path)) | ||
case "UnknownKeyword": | ||
return { ...unknownJsonSchema, ...getMeta(ast) } | ||
return { ...unknownJsonSchema, ...getJsonSchemaAnnotations(ast) } | ||
case "AnyKeyword": | ||
return { ...anyJsonSchema, ...getMeta(ast) } | ||
return { ...anyJsonSchema, ...getJsonSchemaAnnotations(ast) } | ||
case "ObjectKeyword": | ||
return { ...objectJsonSchema, ...getMeta(ast) } | ||
return { ...objectJsonSchema, ...getJsonSchemaAnnotations(ast) } | ||
case "StringKeyword": | ||
return { type: "string", ...getMeta(ast) } | ||
return { type: "string", ...getJsonSchemaAnnotations(ast) } | ||
case "NumberKeyword": | ||
return { type: "number", ...getMeta(ast) } | ||
return { type: "number", ...getJsonSchemaAnnotations(ast) } | ||
case "BooleanKeyword": | ||
return { type: "boolean", ...getMeta(ast) } | ||
return { type: "boolean", ...getJsonSchemaAnnotations(ast) } | ||
case "BigIntKeyword": | ||
throw getMissingAnnotationError("`bigint`") | ||
throw new Error(getMissingAnnotationErrorMessage("`bigint`", path)) | ||
case "SymbolKeyword": | ||
throw getMissingAnnotationError("`symbol`") | ||
throw new Error(getMissingAnnotationErrorMessage("`symbol`", path)) | ||
case "TupleType": { | ||
const elements = ast.elements.map((e) => go(e.type, $defs)) | ||
const rest = ast.rest.map((ast) => go(ast, $defs)) | ||
const len = ast.elements.length | ||
const elements = ast.elements.map((e, i) => go(e.type, $defs, true, path.concat(i))) | ||
const rest = ast.rest.map((ast) => go(ast, $defs, true, path)) | ||
const output: JsonSchema7Array = { type: "array" } | ||
@@ -379,3 +413,2 @@ // --------------------------------------------- | ||
// --------------------------------------------- | ||
const len = elements.length | ||
if (len > 0) { | ||
@@ -401,3 +434,6 @@ output.minItems = len - ast.elements.filter((element) => element.isOptional).length | ||
throw new Error( | ||
"Generating a JSON Schema for post-rest elements is not currently supported. You're welcome to contribute by submitting a Pull Request." | ||
errors_.getErrorMessageWithPath( | ||
"Generating a JSON Schema for post-rest elements is not currently supported. You're welcome to contribute by submitting a Pull Request.", | ||
path | ||
) | ||
) | ||
@@ -413,7 +449,7 @@ } | ||
return { ...output, ...getMeta(ast) } | ||
return { ...output, ...getJsonSchemaAnnotations(ast) } | ||
} | ||
case "TypeLiteral": { | ||
if (ast.propertySignatures.length === 0 && ast.indexSignatures.length === 0) { | ||
return { ...empty(), ...getMeta(ast) } | ||
return { ...empty(), ...getJsonSchemaAnnotations(ast) } | ||
} | ||
@@ -426,3 +462,3 @@ let additionalProperties: JsonSchema7 | undefined = undefined | ||
case "StringKeyword": { | ||
additionalProperties = go(is.type, $defs) | ||
additionalProperties = go(is.type, $defs, true, path) | ||
break | ||
@@ -432,6 +468,3 @@ } | ||
patternProperties = { | ||
[AST.getTemplateLiteralRegExp(parameter).source]: go( | ||
is.type, | ||
$defs | ||
) | ||
[AST.getTemplateLiteralRegExp(parameter).source]: go(is.type, $defs, true, path) | ||
} | ||
@@ -447,17 +480,17 @@ break | ||
patternProperties = { | ||
[hook.value.pattern]: go( | ||
is.type, | ||
$defs | ||
) | ||
[hook.value.pattern]: go(is.type, $defs, true, path) | ||
} | ||
break | ||
} | ||
throw new Error(getUnsupportedIndexSignatureParameterErrorMessage(parameter)) | ||
throw new Error(getUnsupportedIndexSignatureParameterErrorMessage(parameter, path)) | ||
} | ||
case "SymbolKeyword": | ||
throw new Error(getUnsupportedIndexSignatureParameterErrorMessage(parameter)) | ||
throw new Error(getUnsupportedIndexSignatureParameterErrorMessage(parameter, path)) | ||
} | ||
} | ||
const propertySignatures = ast.propertySignatures.map((ps) => { | ||
return { ...go(pruneUndefinedKeyword(ps), $defs), ...getMeta(ps) } | ||
return { | ||
...go(pruneUndefinedKeyword(ps), $defs, true, path.concat(ps.name)), | ||
...getJsonSchemaAnnotations(ps) | ||
} | ||
}) | ||
@@ -484,3 +517,3 @@ const output: JsonSchema7Object = { | ||
} else { | ||
throw new Error(`cannot encode ${String(name)} key to JSON Schema`) | ||
throw new Error(errors_.getErrorMessageWithPath(`cannot encode ${String(name)} key to JSON Schema`, path)) | ||
} | ||
@@ -498,3 +531,3 @@ } | ||
return { ...output, ...getMeta(ast) } | ||
return { ...output, ...getJsonSchemaAnnotations(ast) } | ||
} | ||
@@ -505,3 +538,3 @@ case "Union": { | ||
for (const type of ast.types) { | ||
const schema = go(type, $defs) | ||
const schema = go(type, $defs, true, path) | ||
if ("const" in schema) { | ||
@@ -519,5 +552,5 @@ if (Object.keys(schema).length > 1) { | ||
if (enums.length === 1) { | ||
return { const: enums[0], ...getMeta(ast) } | ||
return { const: enums[0], ...getJsonSchemaAnnotations(ast) } | ||
} else { | ||
return { enum: enums, ...getMeta(ast) } | ||
return { enum: enums, ...getJsonSchemaAnnotations(ast) } | ||
} | ||
@@ -530,3 +563,3 @@ } else { | ||
} | ||
return { anyOf, ...getMeta(ast) } | ||
return { anyOf, ...getJsonSchemaAnnotations(ast) } | ||
} | ||
@@ -538,7 +571,12 @@ } | ||
oneOf: ast.enums.map((e) => ({ title: e[0], const: e[1] })), | ||
...getMeta(ast) | ||
...getJsonSchemaAnnotations(ast) | ||
} | ||
} | ||
case "Refinement": { | ||
throw new Error("cannot build a JSON Schema for a refinement without a JSON Schema annotation") | ||
throw new Error( | ||
errors_.getErrorMessageWithPath( | ||
"cannot build a JSON Schema for a refinement without a JSON Schema annotation", | ||
path | ||
) | ||
) | ||
} | ||
@@ -551,3 +589,3 @@ case "TemplateLiteral": { | ||
pattern: regex.source, | ||
...getMeta(ast) | ||
...getJsonSchemaAnnotations(ast) | ||
} | ||
@@ -559,10 +597,13 @@ } | ||
throw new Error( | ||
"Generating a JSON Schema for suspended schemas requires an identifier annotation" | ||
errors_.getErrorMessageWithPath( | ||
"Generating a JSON Schema for suspended schemas requires an identifier annotation", | ||
path | ||
) | ||
) | ||
} | ||
return go(ast.f(), $defs) | ||
return go(ast.f(), $defs, true, path) | ||
} | ||
case "Transformation": | ||
return go(ast.to, $defs) | ||
return go(ast.from, $defs, true, path) | ||
} | ||
} |
@@ -5,3 +5,3 @@ /** | ||
import * as Array from "effect/Array" | ||
import * as Arr from "effect/Array" | ||
import { TaggedError } from "effect/Data" | ||
@@ -85,3 +85,3 @@ import * as Effect from "effect/Effect" | ||
readonly actual: unknown, | ||
readonly errors: Array.NonEmptyReadonlyArray<Index>, | ||
readonly errors: Arr.NonEmptyReadonlyArray<Index>, | ||
readonly output: ReadonlyArray<unknown> = [] | ||
@@ -119,3 +119,3 @@ ) {} | ||
readonly actual: unknown, | ||
readonly errors: Array.NonEmptyReadonlyArray<Key>, | ||
readonly errors: Arr.NonEmptyReadonlyArray<Key>, | ||
readonly output: { readonly [x: string]: unknown } = {} | ||
@@ -260,3 +260,3 @@ ) {} | ||
readonly actual: unknown, | ||
readonly errors: Array.NonEmptyReadonlyArray<Type | TypeLiteral | Member> | ||
readonly errors: Arr.NonEmptyReadonlyArray<Type | TypeLiteral | Member> | ||
) {} | ||
@@ -478,14 +478,14 @@ } | ||
export const mergeParseOptions = ( | ||
a: AST.ParseOptions | undefined, | ||
b: AST.ParseOptions | undefined | ||
options: AST.ParseOptions | undefined, | ||
overrideOptions: AST.ParseOptions | number | undefined | ||
): AST.ParseOptions | undefined => { | ||
if (a === undefined) { | ||
return b | ||
if (overrideOptions === undefined || Predicate.isNumber(overrideOptions)) { | ||
return options | ||
} | ||
if (b === undefined) { | ||
return a | ||
if (options === undefined) { | ||
return overrideOptions | ||
} | ||
const out: Mutable<AST.ParseOptions> = {} | ||
out.errors = b.errors ?? a.errors | ||
out.onExcessProperty = b.onExcessProperty ?? a.onExcessProperty | ||
out.errors = overrideOptions.errors ?? options.errors | ||
out.onExcessProperty = overrideOptions.onExcessProperty ?? options.onExcessProperty | ||
return out | ||
@@ -503,3 +503,6 @@ } | ||
return (input: unknown, overrideOptions?: AST.ParseOptions) => | ||
Either.getOrThrowWith(parser(input, overrideOptions), (e) => new Error(TreeFormatter.formatIssueSync(e))) | ||
Either.getOrThrowWith( | ||
parser(input, overrideOptions), | ||
(issue) => new Error(TreeFormatter.formatIssueSync(issue), { cause: issue }) | ||
) | ||
} | ||
@@ -721,3 +724,3 @@ | ||
const parser = goMemo(AST.typeAST(schema.ast), true) | ||
return (u: unknown, overrideOptions?: AST.ParseOptions): u is A => | ||
return (u: unknown, overrideOptions?: AST.ParseOptions | number): u is A => | ||
Either.isRight(parser(u, { ...mergeParseOptions(options, overrideOptions), isExact: true }) as any) | ||
@@ -738,3 +741,3 @@ } | ||
if (Either.isLeft(result)) { | ||
throw new Error(TreeFormatter.formatIssueSync(result.left)) | ||
throw new Error(TreeFormatter.formatIssueSync(result.left), { cause: result.left }) | ||
} | ||
@@ -936,3 +939,3 @@ } | ||
return (input: unknown, options) => { | ||
if (!Array.isArray(input)) { | ||
if (!Arr.isArray(input)) { | ||
return Either.left(new Type(ast, input)) | ||
@@ -1035,3 +1038,3 @@ } | ||
// --------------------------------------------- | ||
if (Array.isNonEmptyReadonlyArray(rest)) { | ||
if (Arr.isNonEmptyReadonlyArray(rest)) { | ||
const [head, ...tail] = rest | ||
@@ -1132,3 +1135,3 @@ for (; i < len - tail.length; i++) { | ||
const computeResult = ({ es, output }: State) => | ||
Array.isNonEmptyArray(es) ? | ||
Arr.isNonEmptyArray(es) ? | ||
Either.left(new TupleType(ast, input, sortByIndex(es), sortByIndex(output))) : | ||
@@ -1140,4 +1143,4 @@ Either.right(sortByIndex(output)) | ||
const state: State = { | ||
es: Array.copy(es), | ||
output: Array.copy(output) | ||
es: Arr.copy(es), | ||
output: Arr.copy(output) | ||
} | ||
@@ -1354,3 +1357,3 @@ return Effect.flatMap( | ||
const computeResult = ({ es, output }: State) => | ||
Array.isNonEmptyArray(es) ? | ||
Arr.isNonEmptyArray(es) ? | ||
Either.left(new TypeLiteral(ast, input, sortByIndex(es), output)) : | ||
@@ -1362,3 +1365,3 @@ Either.right(output) | ||
const state: State = { | ||
es: Array.copy(es), | ||
es: Arr.copy(es), | ||
output: Object.assign({}, output) | ||
@@ -1488,3 +1491,3 @@ } | ||
const computeResult = (es: State["es"]) => | ||
Array.isNonEmptyArray(es) ? | ||
Arr.isNonEmptyArray(es) ? | ||
es.length === 1 && es[0][1]._tag === "Type" ? | ||
@@ -1499,3 +1502,3 @@ Either.left(es[0][1]) : | ||
return Effect.suspend(() => { | ||
const state: State = { es: Array.copy(es) } | ||
const state: State = { es: Arr.copy(es) } | ||
return Effect.flatMap( | ||
@@ -1649,4 +1652,4 @@ Effect.forEach(cqueue, (f) => f(state), { concurrency, batching, discard: true }), | ||
function sortByIndex<T>( | ||
es: Array.NonEmptyArray<[number, T]> | ||
): Array.NonEmptyArray<T> | ||
es: Arr.NonEmptyArray<[number, T]> | ||
): Arr.NonEmptyArray<T> | ||
function sortByIndex<T>(es: Array<[number, T]>): Array<T> | ||
@@ -1653,0 +1656,0 @@ function sortByIndex(es: Array<[number, any]>): any { |
/** | ||
* @since 1.0.0 | ||
*/ | ||
import * as Array from "effect/Array" | ||
import * as Arr from "effect/Array" | ||
import * as Option from "effect/Option" | ||
@@ -44,3 +44,3 @@ import * as AST from "./AST.js" | ||
*/ | ||
export const make = <A, I, R>(schema: Schema.Schema<A, I, R>): (a: A) => string => compile(schema.ast) | ||
export const make = <A, I, R>(schema: Schema.Schema<A, I, R>): (a: A) => string => compile(schema.ast, []) | ||
@@ -63,2 +63,5 @@ const getHook = AST.getAnnotation<(...args: ReadonlyArray<Pretty<any>>) => Pretty<any>>( | ||
const getPrettyErrorMessage = (message: string, path: ReadonlyArray<PropertyKey>) => | ||
errors_.getErrorMessageWithPath(`cannot build a Pretty for ${message}`, path) | ||
/** | ||
@@ -68,8 +71,8 @@ * @since 1.0.0 | ||
export const match: AST.Match<Pretty<any>> = { | ||
"Declaration": (ast, go) => { | ||
"Declaration": (ast, go, path) => { | ||
const hook = getHook(ast) | ||
if (Option.isSome(hook)) { | ||
return hook.value(...ast.typeParameters.map(go)) | ||
return hook.value(...ast.typeParameters.map((tp) => go(tp, path))) | ||
} | ||
throw new Error(errors_.getPrettyErrorMessage(`a declaration without annotations (${ast})`)) | ||
throw new Error(getPrettyErrorMessage(`a declaration without annotations (${ast})`, path)) | ||
}, | ||
@@ -97,3 +100,3 @@ "VoidKeyword": getMatcher(() => "void(0)"), | ||
"Enums": stringify, | ||
"TupleType": (ast, go) => { | ||
"TupleType": (ast, go, path) => { | ||
const hook = getHook(ast) | ||
@@ -103,4 +106,4 @@ if (Option.isSome(hook)) { | ||
} | ||
const elements = ast.elements.map((e) => go(e.type)) | ||
const rest = ast.rest.map(go) | ||
const elements = ast.elements.map((e, i) => go(e.type, path.concat(i))) | ||
const rest = ast.rest.map((ast) => go(ast, path)) | ||
return (input: ReadonlyArray<unknown>) => { | ||
@@ -124,3 +127,3 @@ const output: Array<string> = [] | ||
// --------------------------------------------- | ||
if (Array.isNonEmptyReadonlyArray(rest)) { | ||
if (Arr.isNonEmptyReadonlyArray(rest)) { | ||
const [head, ...tail] = rest | ||
@@ -142,3 +145,3 @@ for (; i < input.length - tail.length; i++) { | ||
}, | ||
"TypeLiteral": (ast, go) => { | ||
"TypeLiteral": (ast, go, path) => { | ||
const hook = getHook(ast) | ||
@@ -148,4 +151,4 @@ if (Option.isSome(hook)) { | ||
} | ||
const propertySignaturesTypes = ast.propertySignatures.map((f) => go(f.type)) | ||
const indexSignatureTypes = ast.indexSignatures.map((is) => go(is.type)) | ||
const propertySignaturesTypes = ast.propertySignatures.map((ps) => go(ps.type, path.concat(ps.name))) | ||
const indexSignatureTypes = ast.indexSignatures.map((is) => go(is.type, path)) | ||
const expectedKeys: any = {} | ||
@@ -167,3 +170,3 @@ for (let i = 0; i < propertySignaturesTypes.length; i++) { | ||
output.push( | ||
`${getPrettyPropertyKey(name)}: ${propertySignaturesTypes[i](input[name])}` | ||
`${util_.formatPropertyKey(name)}: ${propertySignaturesTypes[i](input[name])}` | ||
) | ||
@@ -182,3 +185,3 @@ } | ||
} | ||
output.push(`${getPrettyPropertyKey(key)}: ${type(input[key])}`) | ||
output.push(`${util_.formatPropertyKey(key)}: ${type(input[key])}`) | ||
} | ||
@@ -188,6 +191,6 @@ } | ||
return Array.isNonEmptyReadonlyArray(output) ? "{ " + output.join(", ") + " }" : "{}" | ||
return Arr.isNonEmptyReadonlyArray(output) ? "{ " + output.join(", ") + " }" : "{}" | ||
} | ||
}, | ||
"Union": (ast, go) => { | ||
"Union": (ast, go, path) => { | ||
const hook = getHook(ast) | ||
@@ -197,3 +200,3 @@ if (Option.isSome(hook)) { | ||
} | ||
const types = ast.types.map((ast) => [ParseResult.is({ ast } as any), go(ast)] as const) | ||
const types = ast.types.map((ast) => [ParseResult.is({ ast } as any), go(ast, path)] as const) | ||
return (a) => { | ||
@@ -204,6 +207,6 @@ const index = types.findIndex(([is]) => is(a)) | ||
}, | ||
"Suspend": (ast, go) => { | ||
"Suspend": (ast, go, path) => { | ||
return Option.match(getHook(ast), { | ||
onNone: () => { | ||
const get = util_.memoizeThunk(() => go(ast.f())) | ||
const get = util_.memoizeThunk(() => go(ast.f(), path)) | ||
return (a) => get()(a) | ||
@@ -214,11 +217,11 @@ }, | ||
}, | ||
"Refinement": (ast, go) => { | ||
"Refinement": (ast, go, path) => { | ||
return Option.match(getHook(ast), { | ||
onNone: () => go(ast.from), | ||
onNone: () => go(ast.from, path), | ||
onSome: (handler) => handler() | ||
}) | ||
}, | ||
"Transformation": (ast, go) => { | ||
"Transformation": (ast, go, path) => { | ||
return Option.match(getHook(ast), { | ||
onNone: () => go(ast.to), | ||
onNone: () => go(ast.to, path), | ||
onSome: (handler) => handler() | ||
@@ -230,4 +233,1 @@ }) | ||
const compile = AST.getCompiler(match) | ||
const getPrettyPropertyKey = (name: PropertyKey): string => | ||
typeof name === "string" ? JSON.stringify(name) : String(name) |
@@ -55,6 +55,6 @@ /** | ||
*/ | ||
export interface WithResult<A, I, E, EI, R> { | ||
export interface WithResult<SuccessA, SuccessI, FailureA, FailureI, SuccessAndFailureR> { | ||
readonly [symbolResult]: { | ||
readonly Success: Schema.Schema<A, I, R> | ||
readonly Failure: Schema.Schema<E, EI, R> | ||
readonly Success: Schema.Schema<SuccessA, SuccessI, SuccessAndFailureR> | ||
readonly Failure: Schema.Schema<FailureA, FailureI, SuccessAndFailureR> | ||
} | ||
@@ -118,4 +118,14 @@ } | ||
*/ | ||
export interface SerializableWithResult<S, SI, SR, A, AI, E, EI, RR> | ||
extends Serializable<S, SI, SR>, WithResult<A, AI, E, EI, RR> | ||
export interface SerializableWithResult< | ||
Self, | ||
FieldsI, | ||
FieldsR, | ||
SuccessA, | ||
SuccessI, | ||
FailureA, | ||
FailureI, | ||
SuccessAndFailureR | ||
> extends | ||
Serializable<Self, FieldsI, FieldsR>, | ||
WithResult<SuccessA, SuccessI, FailureA, FailureI, SuccessAndFailureR> | ||
{} | ||
@@ -122,0 +132,0 @@ |
@@ -85,3 +85,3 @@ /** | ||
const getPrevMessage = ( | ||
const getInnerMessage = ( | ||
issue: ParseResult.ParseIssue | ||
@@ -96,4 +96,5 @@ ): Effect.Effect<string, Cause.NoSuchElementException> => { | ||
} | ||
case "Transformation": | ||
case "Transformation": { | ||
return getMessage(issue.error) | ||
} | ||
} | ||
@@ -105,6 +106,14 @@ return Option.none() | ||
issue: ParseResult.ParseIssue | ||
) => Effect.Effect<string, Cause.NoSuchElementException> = (issue: ParseResult.ParseIssue) => | ||
) => Effect.Effect<{ message: string; override: boolean }, Cause.NoSuchElementException> = ( | ||
issue: ParseResult.ParseIssue | ||
) => | ||
AST.getMessageAnnotation(issue.ast).pipe(Effect.flatMap((annotation) => { | ||
const out = annotation(issue) | ||
return Predicate.isString(out) ? Effect.succeed(out) : out | ||
return Predicate.isString(out) | ||
? Effect.succeed({ message: out, override: false }) | ||
: Effect.isEffect(out) | ||
? Effect.map(out, (message) => ({ message, override: false })) | ||
: Predicate.isString(out.message) | ||
? Effect.succeed({ message: out.message, override: out.override }) | ||
: Effect.map(out.message, (message) => ({ message, override: out.override })) | ||
})) | ||
@@ -115,4 +124,21 @@ | ||
issue: ParseResult.ParseIssue | ||
) => Effect.Effect<string, Cause.NoSuchElementException> = (issue: ParseResult.ParseIssue) => | ||
Effect.catchAll(getPrevMessage(issue), () => getCurrentMessage(issue)) | ||
) => Effect.Effect<string, Cause.NoSuchElementException> = (issue: ParseResult.ParseIssue) => { | ||
const current = getCurrentMessage(issue) | ||
return getInnerMessage(issue).pipe( | ||
Effect.flatMap((inner) => Effect.map(current, (current) => current.override ? current.message : inner)), | ||
Effect.catchAll(() => | ||
Effect.flatMap(current, (current) => { | ||
if ( | ||
!current.override && ( | ||
(issue._tag === "Refinement" && issue.kind !== "Predicate") || | ||
(issue._tag === "Transformation" && issue.kind !== "Transformation") | ||
) | ||
) { | ||
return Option.none() | ||
} | ||
return Effect.succeed(current.message) | ||
}) | ||
) | ||
) | ||
} | ||
@@ -174,3 +200,3 @@ const getParseIssueTitleAnnotation = (issue: ParseResult.ParseIssue): Option.Option<string> => | ||
e.errors, | ||
(index) => Effect.map(go(index.error), (tree) => make(`[${index.index}]`, [tree])) | ||
(index) => Effect.map(go(index.error), (tree) => make(`[${util_.formatPropertyKey(index.index)}]`, [tree])) | ||
), | ||
@@ -183,3 +209,3 @@ (forest) => make(getParseIssueTitle(e), forest) | ||
Effect.forEach(e.errors, (key) => | ||
Effect.map(go(key.error), (tree) => make(`[${util_.formatUnknown(key.key)}]`, [tree]))), | ||
Effect.map(go(key.error), (tree) => make(`[${util_.formatPropertyKey(key.key)}]`, [tree]))), | ||
(forest) => | ||
@@ -186,0 +212,0 @@ make(getParseIssueTitle(e), forest) |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
2321335
39802
8036
+ Addedfast-check@^3.17.2