@effect/schema
Advanced tools
Comparing version 0.2.1 to 0.3.0
@@ -13,2 +13,3 @@ "use strict"; | ||
var I = /*#__PURE__*/_interopRequireWildcard( /*#__PURE__*/require("@effect/schema/internal/common")); | ||
var _ParseResult = /*#__PURE__*/require("@effect/schema/ParseResult"); | ||
function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function (nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); } | ||
@@ -186,10 +187,9 @@ function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; } | ||
case "Refinement": | ||
case "Transform": | ||
{ | ||
const from = go(ast.from); | ||
return (0, _Function.pipe)(getHook(ast), O.match(() => fc => from(fc).filter(a => E.isRight(ast.decode(a))), handler => handler(from))); | ||
const to = go(ast.to); | ||
return (0, _Function.pipe)(getHook(ast), O.match(() => fc => to(fc).filter(a => E.isRight((0, _ParseResult.eitherSync)(ast.decode(a)))), handler => handler(to))); | ||
} | ||
case "Transform": | ||
return go(ast.to); | ||
} | ||
}); | ||
//# sourceMappingURL=Arbitrary.js.map |
55
AST.d.ts
@@ -112,2 +112,12 @@ /** | ||
*/ | ||
export interface HasTransformation { | ||
readonly hasTransformation: boolean; | ||
} | ||
/** | ||
* @since 1.0.0 | ||
*/ | ||
export declare const hasTransformation: (ast: AST) => boolean; | ||
/** | ||
* @since 1.0.0 | ||
*/ | ||
export declare const getAnnotation: <A>(key: PropertyKey) => (annotated: Annotated) => Option<A>; | ||
@@ -118,3 +128,3 @@ /** | ||
*/ | ||
export interface Declaration extends Annotated { | ||
export interface Declaration extends Annotated, HasTransformation { | ||
readonly _tag: "Declaration"; | ||
@@ -212,2 +222,7 @@ readonly typeParameters: ReadonlyArray<AST>; | ||
/** | ||
* @category guards | ||
* @since 1.0.0 | ||
*/ | ||
export declare const isNeverKeyword: (ast: AST) => ast is NeverKeyword; | ||
/** | ||
* @category model | ||
@@ -397,3 +412,3 @@ * @since 1.0.0 | ||
*/ | ||
export interface Tuple extends Annotated { | ||
export interface Tuple extends Annotated, HasTransformation { | ||
readonly _tag: "Tuple"; | ||
@@ -431,3 +446,3 @@ readonly elements: ReadonlyArray<Element>; | ||
export interface IndexSignature { | ||
readonly parameter: StringKeyword | SymbolKeyword | TemplateLiteral | Refinement; | ||
readonly parameter: AST; | ||
readonly type: AST; | ||
@@ -439,3 +454,3 @@ readonly isReadonly: boolean; | ||
*/ | ||
export declare const createIndexSignature: (parameter: StringKeyword | SymbolKeyword | TemplateLiteral | Refinement, type: AST, isReadonly: boolean) => IndexSignature; | ||
export declare const createIndexSignature: (parameter: AST, type: AST, isReadonly: boolean) => IndexSignature; | ||
/** | ||
@@ -445,3 +460,3 @@ * @category model | ||
*/ | ||
export interface TypeLiteral extends Annotated { | ||
export interface TypeLiteral extends Annotated, HasTransformation { | ||
readonly _tag: "TypeLiteral"; | ||
@@ -465,3 +480,3 @@ readonly propertySignatures: ReadonlyArray<PropertySignature>; | ||
*/ | ||
export interface Union extends Annotated { | ||
export interface Union extends Annotated, HasTransformation { | ||
readonly _tag: "Union"; | ||
@@ -484,3 +499,3 @@ readonly types: readonly [AST, AST, ...Array<AST>]; | ||
*/ | ||
export interface Lazy extends Annotated { | ||
export interface Lazy extends Annotated, HasTransformation { | ||
readonly _tag: "Lazy"; | ||
@@ -506,4 +521,5 @@ readonly f: () => AST; | ||
readonly from: AST; | ||
readonly to: AST; | ||
readonly decode: (input: any, options?: ParseOptions) => ParseResult<any>; | ||
readonly isReversed: boolean; | ||
readonly encode: (input: any, options?: ParseOptions) => ParseResult<any>; | ||
} | ||
@@ -514,3 +530,3 @@ /** | ||
*/ | ||
export declare const createRefinement: (from: AST, decode: (input: any, options?: ParseOptions) => ParseResult<any>, isReversed: boolean, annotations?: Annotated["annotations"]) => Refinement; | ||
export declare const createRefinement: (from: AST, to: AST, decode: Refinement["decode"], encode: Refinement["encode"], annotations?: Annotated["annotations"]) => Refinement; | ||
/** | ||
@@ -539,3 +555,2 @@ * @category guards | ||
readonly encode: (input: any, options?: ParseOptions) => ParseResult<any>; | ||
readonly isReversed: boolean; | ||
} | ||
@@ -546,3 +561,3 @@ /** | ||
*/ | ||
export declare const createTransform: (from: AST, to: AST, decode: Transform["decode"], encode: Transform["encode"], isReversed: boolean, annotations?: Annotated["annotations"]) => Transform; | ||
export declare const createTransform: (from: AST, to: AST, decode: Transform["decode"], encode: Transform["encode"], annotations?: Annotated["annotations"]) => Transform; | ||
/** | ||
@@ -567,2 +582,3 @@ * @category guards | ||
decode: (...typeParameters: ReadonlyArray<AST>) => (input: any, options?: ParseOptions) => ParseResult<any>; | ||
hasTransformation: boolean; | ||
} | { | ||
@@ -672,2 +688,3 @@ annotations: { | ||
isReadonly: boolean; | ||
hasTransformation: boolean; | ||
} | { | ||
@@ -681,2 +698,3 @@ annotations: { | ||
indexSignatures: ReadonlyArray<IndexSignature>; | ||
hasTransformation: boolean; | ||
} | { | ||
@@ -689,2 +707,3 @@ annotations: { | ||
types: readonly [AST, AST, ...Array<AST>]; | ||
hasTransformation: boolean; | ||
} | { | ||
@@ -697,2 +716,3 @@ annotations: { | ||
f: () => AST; | ||
hasTransformation: boolean; | ||
} | { | ||
@@ -705,4 +725,5 @@ annotations: { | ||
from: AST; | ||
to: AST; | ||
decode: (input: any, options?: ParseOptions) => ParseResult<any>; | ||
isReversed: boolean; | ||
encode: (input: any, options?: ParseOptions) => ParseResult<any>; | ||
} | { | ||
@@ -718,3 +739,2 @@ annotations: { | ||
encode: (input: any, options?: ParseOptions) => ParseResult<any>; | ||
isReversed: boolean; | ||
}; | ||
@@ -734,2 +754,3 @@ /** | ||
decode: (...typeParameters: ReadonlyArray<AST>) => (input: any, options?: ParseOptions) => ParseResult<any>; | ||
hasTransformation: boolean; | ||
} | { | ||
@@ -823,2 +844,3 @@ annotations: { | ||
isReadonly: boolean; | ||
hasTransformation: boolean; | ||
} | { | ||
@@ -831,2 +853,3 @@ annotations: { | ||
indexSignatures: ReadonlyArray<IndexSignature>; | ||
hasTransformation: boolean; | ||
} | { | ||
@@ -838,2 +861,3 @@ annotations: { | ||
types: readonly [AST, AST, ...Array<AST>]; | ||
hasTransformation: boolean; | ||
} | { | ||
@@ -845,2 +869,3 @@ annotations: { | ||
f: () => AST; | ||
hasTransformation: boolean; | ||
} | { | ||
@@ -852,4 +877,5 @@ annotations: { | ||
from: AST; | ||
to: AST; | ||
decode: (input: any, options?: ParseOptions) => ParseResult<any>; | ||
isReversed: boolean; | ||
encode: (input: any, options?: ParseOptions) => ParseResult<any>; | ||
} | { | ||
@@ -864,3 +890,2 @@ annotations: { | ||
encode: (input: any, options?: ParseOptions) => ParseResult<any>; | ||
isReversed: boolean; | ||
}; | ||
@@ -867,0 +892,0 @@ /** |
224
AST.js
@@ -6,3 +6,3 @@ "use strict"; | ||
}); | ||
exports.voidKeyword = exports.unknownKeyword = exports.undefinedKeyword = exports.symbolKeyword = exports.stringKeyword = exports.sortByWeightDesc = exports.setAnnotation = exports.reverse = exports.pick = exports.partial = exports.omit = exports.objectKeyword = exports.numberKeyword = exports.neverKeyword = exports.mergeAnnotations = exports.keyof = exports.isUnknownKeyword = exports.isUniqueSymbol = exports.isUnion = exports.isTypeLiteral = exports.isTuple = exports.isTransform = exports.isTemplateLiteral = exports.isSymbolKeyword = exports.isStringKeyword = exports.isRefinement = exports.isNumberKeyword = exports.isLiteral = exports.isLazy = exports.isDeclaration = exports.isBooleanKeyword = exports.isBigIntKeyword = exports.isAnyKeyword = exports.getTo = exports.getFrom = exports.getCompiler = exports.getAnnotation = exports.createUniqueSymbol = exports.createUnion = exports.createTypeLiteral = exports.createTuple = exports.createTransform = exports.createTemplateLiteral = exports.createRefinement = exports.createRecord = exports.createPropertySignature = exports.createLiteral = exports.createLazy = exports.createIndexSignature = exports.createEnums = exports.createElement = exports.createDeclaration = exports.booleanKeyword = exports.bigIntKeyword = exports.appendRestElement = exports.appendElement = exports.anyKeyword = exports._getWeight = exports._getPropertySignatures = exports._getParameter = exports._getCardinality = exports.TypeAnnotationId = exports.TitleAnnotationId = exports.MessageAnnotationId = exports.JSONSchemaAnnotationId = exports.IdentifierAnnotationId = exports.ExamplesAnnotationId = exports.DocumentationAnnotationId = exports.DescriptionAnnotationId = exports.BrandAnnotationId = void 0; | ||
exports.voidKeyword = exports.unknownKeyword = exports.undefinedKeyword = exports.symbolKeyword = exports.stringKeyword = exports.sortByWeightDesc = exports.setAnnotation = exports.reverse = exports.pick = exports.partial = exports.omit = exports.objectKeyword = exports.numberKeyword = exports.neverKeyword = exports.mergeAnnotations = exports.keyof = exports.isUnknownKeyword = exports.isUniqueSymbol = exports.isUnion = exports.isTypeLiteral = exports.isTuple = exports.isTransform = exports.isTemplateLiteral = exports.isSymbolKeyword = exports.isStringKeyword = exports.isRefinement = exports.isNumberKeyword = exports.isNeverKeyword = exports.isLiteral = exports.isLazy = exports.isDeclaration = exports.isBooleanKeyword = exports.isBigIntKeyword = exports.isAnyKeyword = exports.hasTransformation = exports.getTo = exports.getFrom = exports.getCompiler = exports.getAnnotation = exports.createUniqueSymbol = exports.createUnion = exports.createTypeLiteral = exports.createTuple = exports.createTransform = exports.createTemplateLiteral = exports.createRefinement = exports.createRecord = exports.createPropertySignature = exports.createLiteral = exports.createLazy = exports.createIndexSignature = exports.createEnums = exports.createElement = exports.createDeclaration = exports.booleanKeyword = exports.bigIntKeyword = exports.appendRestElement = exports.appendElement = exports.anyKeyword = exports._getWeight = exports._getPropertySignatures = exports._getParameterKeyof = exports._getCardinality = exports.TypeAnnotationId = exports.TitleAnnotationId = exports.MessageAnnotationId = exports.JSONSchemaAnnotationId = exports.IdentifierAnnotationId = exports.ExamplesAnnotationId = exports.DocumentationAnnotationId = exports.DescriptionAnnotationId = exports.BrandAnnotationId = void 0; | ||
var _Function = /*#__PURE__*/require("@effect/data/Function"); | ||
@@ -78,2 +78,7 @@ var Number = /*#__PURE__*/_interopRequireWildcard( /*#__PURE__*/require("@effect/data/Number")); | ||
exports.DocumentationAnnotationId = DocumentationAnnotationId; | ||
const hasTransformation = ast => isRefinement(ast) || isTransform(ast) || "hasTransformation" in ast && ast.hasTransformation; | ||
/** | ||
* @since 1.0.0 | ||
*/ | ||
exports.hasTransformation = hasTransformation; | ||
const getAnnotation = key => annotated => Object.prototype.hasOwnProperty.call(annotated.annotations, key) ? O.some(annotated.annotations[key]) : O.none(); | ||
@@ -90,3 +95,4 @@ /** | ||
decode, | ||
annotations | ||
annotations, | ||
hasTransformation: hasTransformation(type) || typeParameters.some(hasTransformation) | ||
}); | ||
@@ -165,6 +171,12 @@ /** | ||
/** | ||
* @category guards | ||
* @since 1.0.0 | ||
*/ | ||
exports.neverKeyword = neverKeyword; | ||
const isNeverKeyword = ast => ast._tag === "NeverKeyword"; | ||
/** | ||
* @category constructors | ||
* @since 1.0.0 | ||
*/ | ||
exports.neverKeyword = neverKeyword; | ||
exports.isNeverKeyword = isNeverKeyword; | ||
const unknownKeyword = { | ||
@@ -340,3 +352,4 @@ _tag: "UnknownKeyword", | ||
isReadonly, | ||
annotations | ||
annotations, | ||
hasTransformation: elements.some(e => hasTransformation(e.type)) || O.isSome(rest) && rest.value.some(hasTransformation) | ||
}); | ||
@@ -364,7 +377,12 @@ /** | ||
exports.createPropertySignature = createPropertySignature; | ||
const createIndexSignature = (parameter, type, isReadonly) => ({ | ||
parameter, | ||
type, | ||
isReadonly | ||
}); | ||
const createIndexSignature = (parameter, type, isReadonly) => { | ||
if (isNeverKeyword(_getParameterKeyof(parameter))) { | ||
throw new Error("An index signature parameter type must be 'string', 'symbol', a template literal type or a refinement of the previous types"); | ||
} | ||
return { | ||
parameter, | ||
type, | ||
isReadonly | ||
}; | ||
}; | ||
/** | ||
@@ -379,3 +397,4 @@ * @category constructors | ||
indexSignatures: sortByCardinalityAsc(indexSignatures), | ||
annotations | ||
annotations, | ||
hasTransformation: propertySignatures.some(p => hasTransformation(p.type)) || indexSignatures.some(is => hasTransformation(is.type)) | ||
}); | ||
@@ -405,3 +424,4 @@ /** | ||
types: sortByWeightDesc(types), | ||
annotations | ||
annotations, | ||
hasTransformation: types.some(hasTransformation) | ||
}; | ||
@@ -425,3 +445,4 @@ } | ||
f, | ||
annotations | ||
annotations, | ||
hasTransformation: true | ||
}); | ||
@@ -439,7 +460,8 @@ /** | ||
exports.isLazy = isLazy; | ||
const createRefinement = (from, decode, isReversed, annotations = {}) => ({ | ||
const createRefinement = (from, to, decode, encode, annotations = {}) => ({ | ||
_tag: "Refinement", | ||
from, | ||
to, | ||
decode, | ||
isReversed, | ||
encode, | ||
annotations | ||
@@ -458,9 +480,8 @@ }); | ||
exports.isRefinement = isRefinement; | ||
const createTransform = (from, to, decode, encode, isReversed, annotations = {}) => ({ | ||
const createTransform = (from, to, decode, encode, annotations = {}) => ({ | ||
_tag: "Transform", | ||
from, | ||
to: getTo(to), | ||
to, | ||
decode, | ||
encode, | ||
isReversed, | ||
annotations | ||
@@ -614,3 +635,2 @@ }); | ||
case "Refinement": | ||
return partial(ast.from); | ||
case "Transform": | ||
@@ -635,26 +655,28 @@ return partial(ast.to); | ||
const getTo = ast => { | ||
switch (ast._tag) { | ||
case "Declaration": | ||
return createDeclaration(ast.typeParameters.map(getTo), ast.type, ast.decode, ast.annotations); | ||
case "Tuple": | ||
return createTuple(ast.elements.map(e => ({ | ||
...e, | ||
type: getTo(e.type) | ||
})), O.map(ast.rest, RA.mapNonEmpty(getTo)), ast.isReadonly, ast.annotations); | ||
case "TypeLiteral": | ||
return createTypeLiteral(ast.propertySignatures.map(p => ({ | ||
...p, | ||
type: getTo(p.type) | ||
})), ast.indexSignatures.map(is => ({ | ||
...is, | ||
type: getTo(is.type) | ||
})), ast.annotations); | ||
case "Union": | ||
return createUnion(ast.types.map(getTo), ast.annotations); | ||
case "Lazy": | ||
return createLazy(() => getTo(ast.f()), ast.annotations); | ||
case "Refinement": | ||
return createRefinement(getTo(ast.from), ast.decode, false, ast.annotations); | ||
case "Transform": | ||
return getTo(ast.to); | ||
if (hasTransformation(ast)) { | ||
switch (ast._tag) { | ||
case "Declaration": | ||
return createDeclaration(ast.typeParameters.map(getTo), ast.type, ast.decode, ast.annotations); | ||
case "Tuple": | ||
return createTuple(ast.elements.map(e => ({ | ||
...e, | ||
type: getTo(e.type) | ||
})), O.map(ast.rest, RA.mapNonEmpty(getTo)), ast.isReadonly, ast.annotations); | ||
case "TypeLiteral": | ||
return createTypeLiteral(ast.propertySignatures.map(p => ({ | ||
...p, | ||
type: getTo(p.type) | ||
})), ast.indexSignatures.map(is => ({ | ||
...is, | ||
type: getTo(is.type) | ||
})), ast.annotations); | ||
case "Union": | ||
return createUnion(ast.types.map(getTo), ast.annotations); | ||
case "Lazy": | ||
return createLazy(() => getTo(ast.f()), ast.annotations); | ||
case "Refinement": | ||
return createRefinement(ast.to, ast.to, ast.decode, ast.decode, ast.annotations); | ||
case "Transform": | ||
return ast.to; | ||
} | ||
} | ||
@@ -668,25 +690,27 @@ return ast; | ||
const getFrom = ast => { | ||
switch (ast._tag) { | ||
case "Declaration": | ||
return createDeclaration(ast.typeParameters.map(getFrom), ast.type, ast.decode, ast.annotations); | ||
case "Tuple": | ||
return createTuple(ast.elements.map(e => ({ | ||
...e, | ||
type: getFrom(e.type) | ||
})), O.map(ast.rest, RA.mapNonEmpty(getFrom)), ast.isReadonly, ast.annotations); | ||
case "TypeLiteral": | ||
return createTypeLiteral(ast.propertySignatures.map(p => ({ | ||
...p, | ||
type: getFrom(p.type) | ||
})), ast.indexSignatures.map(is => ({ | ||
...is, | ||
type: getFrom(is.type) | ||
})), ast.annotations); | ||
case "Union": | ||
return createUnion(ast.types.map(getFrom), ast.annotations); | ||
case "Lazy": | ||
return createLazy(() => getFrom(ast.f()), ast.annotations); | ||
case "Refinement": | ||
case "Transform": | ||
return getFrom(ast.from); | ||
if (hasTransformation(ast)) { | ||
switch (ast._tag) { | ||
case "Declaration": | ||
return createDeclaration(ast.typeParameters.map(getFrom), ast.type, ast.decode, ast.annotations); | ||
case "Tuple": | ||
return createTuple(ast.elements.map(e => ({ | ||
...e, | ||
type: getFrom(e.type) | ||
})), O.map(ast.rest, RA.mapNonEmpty(getFrom)), ast.isReadonly, ast.annotations); | ||
case "TypeLiteral": | ||
return createTypeLiteral(ast.propertySignatures.map(p => ({ | ||
...p, | ||
type: getFrom(p.type) | ||
})), ast.indexSignatures.map(is => ({ | ||
...is, | ||
type: getFrom(is.type) | ||
})), ast.annotations); | ||
case "Union": | ||
return createUnion(ast.types.map(getFrom), ast.annotations); | ||
case "Lazy": | ||
return createLazy(() => getFrom(ast.f()), ast.annotations); | ||
case "Refinement": | ||
case "Transform": | ||
return getFrom(ast.from); | ||
} | ||
} | ||
@@ -700,26 +724,28 @@ return ast; | ||
const reverse = ast => { | ||
switch (ast._tag) { | ||
case "Declaration": | ||
return createDeclaration(ast.typeParameters.map(reverse), ast.type, ast.decode, ast.annotations); | ||
case "Tuple": | ||
return createTuple(ast.elements.map(e => ({ | ||
...e, | ||
type: reverse(e.type) | ||
})), O.map(ast.rest, RA.mapNonEmpty(reverse)), ast.isReadonly, ast.annotations); | ||
case "TypeLiteral": | ||
return createTypeLiteral(ast.propertySignatures.map(p => ({ | ||
...p, | ||
type: reverse(p.type) | ||
})), ast.indexSignatures.map(is => ({ | ||
...is, | ||
type: reverse(is.type) | ||
})), ast.annotations); | ||
case "Union": | ||
return createUnion(ast.types.map(reverse), ast.annotations); | ||
case "Lazy": | ||
return createLazy(() => reverse(ast.f()), ast.annotations); | ||
case "Refinement": | ||
return createRefinement(ast.from, ast.decode, !ast.isReversed, ast.annotations); | ||
case "Transform": | ||
return createTransform(reverse(ast.from), ast.to, ast.decode, ast.encode, !ast.isReversed); | ||
if (hasTransformation(ast)) { | ||
switch (ast._tag) { | ||
case "Declaration": | ||
return createDeclaration(ast.typeParameters.map(reverse), ast.type, ast.decode, ast.annotations); | ||
case "Tuple": | ||
return createTuple(ast.elements.map(e => ({ | ||
...e, | ||
type: reverse(e.type) | ||
})), O.map(ast.rest, RA.mapNonEmpty(reverse)), ast.isReadonly, ast.annotations); | ||
case "TypeLiteral": | ||
return createTypeLiteral(ast.propertySignatures.map(p => ({ | ||
...p, | ||
type: reverse(p.type) | ||
})), ast.indexSignatures.map(is => ({ | ||
...is, | ||
type: reverse(is.type) | ||
})), ast.annotations); | ||
case "Union": | ||
return createUnion(ast.types.map(reverse), ast.annotations); | ||
case "Lazy": | ||
return createLazy(() => reverse(ast.f()), ast.annotations); | ||
case "Refinement": | ||
return createRefinement(reverse(ast.to), reverse(ast.from), ast.encode, ast.decode, ast.annotations); | ||
case "Transform": | ||
return createTransform(reverse(ast.to), reverse(ast.from), ast.encode, ast.decode); | ||
} | ||
} | ||
@@ -757,3 +783,2 @@ return ast; | ||
case "Refinement": | ||
return _getCardinality(ast.from); | ||
case "Transform": | ||
@@ -783,3 +808,2 @@ return _getCardinality(ast.to); | ||
case "Refinement": | ||
return _getWeight(ast.from); | ||
case "Transform": | ||
@@ -830,4 +854,14 @@ return _getWeight(ast.to); | ||
/** @internal */ | ||
const _getParameter = x => isRefinement(x) ? _getParameter(x.from) : x; | ||
exports._getParameter = _getParameter; | ||
const _getParameterKeyof = ast => { | ||
switch (ast._tag) { | ||
case "StringKeyword": | ||
case "SymbolKeyword": | ||
case "TemplateLiteral": | ||
return ast; | ||
case "Refinement": | ||
return _getParameterKeyof(ast.from); | ||
} | ||
return neverKeyword; | ||
}; | ||
exports._getParameterKeyof = _getParameterKeyof; | ||
const _keyof = ast => { | ||
@@ -843,3 +877,3 @@ switch (ast._tag) { | ||
case "TypeLiteral": | ||
return ast.propertySignatures.map(p => typeof p.name === "symbol" ? createUniqueSymbol(p.name) : createLiteral(p.name)).concat(ast.indexSignatures.map(is => _getParameter(is.parameter))); | ||
return ast.propertySignatures.map(p => typeof p.name === "symbol" ? createUniqueSymbol(p.name) : createLiteral(p.name)).concat(ast.indexSignatures.map(is => _getParameterKeyof(is.parameter))); | ||
case "Union": | ||
@@ -852,3 +886,2 @@ { | ||
case "Refinement": | ||
return _keyof(ast.from); | ||
case "Transform": | ||
@@ -885,3 +918,2 @@ return _keyof(ast.to); | ||
case "Refinement": | ||
return _getPropertySignatures(ast.from); | ||
case "Transform": | ||
@@ -888,0 +920,0 @@ return _getPropertySignatures(ast.to); |
@@ -36,4 +36,6 @@ "use strict"; | ||
case "Refinement": | ||
case "Transform": | ||
return getKeysForIndexSignature(input, parameter.from); | ||
} | ||
return []; | ||
}; | ||
@@ -40,0 +42,0 @@ // --------------------------------------------- |
{ | ||
"name": "@effect/schema", | ||
"version": "0.2.1", | ||
"version": "0.3.0", | ||
"license": "MIT", | ||
@@ -10,4 +10,5 @@ "repository": { | ||
"dependencies": { | ||
"@effect/data": "~0.4.1", | ||
"fast-check": "^3.7.0" | ||
"@effect/data": "~0.5.0", | ||
"@effect/io": "^0.10.0", | ||
"fast-check": "^3.7.1" | ||
}, | ||
@@ -14,0 +15,0 @@ "publishConfig": { |
/** | ||
* @since 1.0.0 | ||
*/ | ||
import * as O from "@effect/data/Option"; | ||
import * as E from "@effect/data/Either"; | ||
import type { Option } from "@effect/data/Option"; | ||
import type { ParseOptions } from "@effect/schema/AST"; | ||
import type { ParseResult } from "@effect/schema/ParseResult"; | ||
import * as PR from "@effect/schema/ParseResult"; | ||
import type { ParseResult } from "@effect/schema/ParseResult"; | ||
import type { Schema, To } from "@effect/schema/Schema"; | ||
@@ -14,3 +14,3 @@ /** | ||
*/ | ||
export declare const decodeEither: <I, A>(schema: Schema<I, A>) => (input: unknown, options?: ParseOptions) => PR.ParseResult<A>; | ||
export declare const parse: <_, A>(schema: Schema<_, A>) => (i: unknown, options?: ParseOptions) => A; | ||
/** | ||
@@ -20,3 +20,3 @@ * @category decoding | ||
*/ | ||
export declare const decodeOption: <I, A>(schema: Schema<I, A>) => (input: unknown, options?: ParseOptions) => O.Option<A>; | ||
export declare const parseOption: <_, A>(schema: Schema<_, A>) => (i: unknown, options?: ParseOptions) => Option<A>; | ||
/** | ||
@@ -26,12 +26,48 @@ * @category decoding | ||
*/ | ||
export declare const decode: <I, A>(schema: Schema<I, A>) => (input: unknown, options?: ParseOptions) => A; | ||
export declare const parseEither: <_, A>(schema: Schema<_, A>) => (i: unknown, options?: ParseOptions) => E.Either<PR.ParseError, A>; | ||
/** | ||
* @category decoding | ||
* @since 1.0.0 | ||
*/ | ||
export declare const parsePromise: <_, A>(schema: Schema<_, A>) => (i: unknown, options?: ParseOptions) => Promise<A>; | ||
/** | ||
* @category decoding | ||
* @since 1.0.0 | ||
*/ | ||
export declare const parseEffect: <_, A>(schema: Schema<_, A>) => (i: unknown, options?: ParseOptions) => ParseResult<A>; | ||
/** | ||
* @category decoding | ||
* @since 1.0.0 | ||
*/ | ||
export declare const decode: <I, A>(schema: Schema<I, A>) => (i: I, options?: ParseOptions) => A; | ||
/** | ||
* @category decoding | ||
* @since 1.0.0 | ||
*/ | ||
export declare const decodeOption: <I, A>(schema: Schema<I, A>) => (i: I, options?: ParseOptions) => Option<A>; | ||
/** | ||
* @category decoding | ||
* @since 1.0.0 | ||
*/ | ||
export declare const decodeEither: <I, A>(schema: Schema<I, A>) => (i: I, options?: ParseOptions) => E.Either<PR.ParseError, A>; | ||
/** | ||
* @category decoding | ||
* @since 1.0.0 | ||
*/ | ||
export declare const decodePromise: <I, A>(schema: Schema<I, A>) => (i: I, options?: ParseOptions) => Promise<A>; | ||
/** | ||
* @category decoding | ||
* @since 1.0.0 | ||
*/ | ||
export declare const decodeEffect: <_, A>(schema: Schema<_, A>) => (i: unknown, options?: ParseOptions | undefined) => ParseResult<A>; | ||
/** | ||
* @category validation | ||
* @since 1.0.0 | ||
*/ | ||
export declare const is: <I, A>(schema: Schema<I, A>) => (input: unknown, options?: ParseOptions) => input is A; | ||
export declare const validate: <_, A>(schema: Schema<_, A>) => (a: unknown, options?: ParseOptions) => A; | ||
/** | ||
* @category validation | ||
* @since 1.0.0 | ||
*/ | ||
export type ToAsserts<S extends Schema<any>> = (input: unknown, options?: ParseOptions) => asserts input is To<S>; | ||
export declare const validateOption: <_, A>(schema: Schema<_, A>) => (a: unknown, options?: ParseOptions) => Option<A>; | ||
/** | ||
@@ -41,3 +77,3 @@ * @category validation | ||
*/ | ||
export declare const asserts: <I, A>(schema: Schema<I, A>) => (input: unknown, options?: ParseOptions) => asserts input is A; | ||
export declare const validateEither: <_, A>(schema: Schema<_, A>) => (a: unknown, options?: ParseOptions) => E.Either<PR.ParseError, A>; | ||
/** | ||
@@ -47,3 +83,3 @@ * @category validation | ||
*/ | ||
export declare const validateEither: <I, A>(schema: Schema<I, A>) => (input: unknown, options?: ParseOptions) => PR.ParseResult<A>; | ||
export declare const validatePromise: <_, A>(schema: Schema<_, A>) => (i: unknown, options?: ParseOptions) => Promise<A>; | ||
/** | ||
@@ -53,3 +89,3 @@ * @category validation | ||
*/ | ||
export declare const validateOption: <I, A>(schema: Schema<I, A>) => (input: unknown, options?: ParseOptions) => O.Option<A>; | ||
export declare const validateEffect: <_, A>(schema: Schema<_, A>) => (a: unknown, options?: ParseOptions) => ParseResult<A>; | ||
/** | ||
@@ -59,8 +95,17 @@ * @category validation | ||
*/ | ||
export declare const validate: <I, A>(schema: Schema<I, A>) => (input: unknown, options?: ParseOptions) => A; | ||
export declare const is: <_, A>(schema: Schema<_, A>) => (a: unknown) => a is A; | ||
/** | ||
* @since 1.0.0 | ||
*/ | ||
export type ToAsserts<S extends Schema<any>> = (input: unknown, options?: ParseOptions) => asserts input is To<S>; | ||
/** | ||
* @category validation | ||
* @since 1.0.0 | ||
*/ | ||
export declare const asserts: <_, A>(schema: Schema<_, A>) => (a: unknown, options?: ParseOptions) => asserts a is A; | ||
/** | ||
* @category encoding | ||
* @since 1.0.0 | ||
*/ | ||
export declare const encodeEither: <I, A>(schema: Schema<I, A>) => (a: A, options?: ParseOptions) => PR.ParseResult<I>; | ||
export declare const encode: <I, A>(schema: Schema<I, A>) => (a: A, options?: ParseOptions) => I; | ||
/** | ||
@@ -70,3 +115,3 @@ * @category encoding | ||
*/ | ||
export declare const encodeOption: <I, A>(schema: Schema<I, A>) => (input: A, options?: ParseOptions) => O.Option<I>; | ||
export declare const encodeOption: <I, A>(schema: Schema<I, A>) => (input: A, options?: ParseOptions) => Option<I>; | ||
/** | ||
@@ -76,3 +121,13 @@ * @category encoding | ||
*/ | ||
export declare const encode: <I, A>(schema: Schema<I, A>) => (a: A, options?: ParseOptions) => I; | ||
export declare const encodeEither: <I, A>(schema: Schema<I, A>) => (a: A, options?: ParseOptions) => E.Either<PR.ParseError, I>; | ||
/** | ||
* @category encoding | ||
* @since 1.0.0 | ||
*/ | ||
export declare const encodePromise: <I, A>(schema: Schema<I, A>) => (a: A, options?: ParseOptions) => Promise<I>; | ||
/** | ||
* @category encoding | ||
* @since 1.0.0 | ||
*/ | ||
export declare const encodeEffect: <I, A>(schema: Schema<I, A>) => (a: A, options?: ParseOptions) => ParseResult<I>; | ||
//# sourceMappingURL=Parser.d.ts.map |
664
Parser.js
@@ -6,3 +6,3 @@ "use strict"; | ||
}); | ||
exports.validateOption = exports.validateEither = exports.validate = exports.is = exports.encodeOption = exports.encodeEither = exports.encode = exports.decodeOption = exports.decodeEither = exports.decode = exports.asserts = exports._getSearchTree = exports._getLiterals = void 0; | ||
exports.validatePromise = exports.validateOption = exports.validateEither = exports.validateEffect = exports.validate = exports.parsePromise = exports.parseOption = exports.parseEither = exports.parseEffect = exports.parse = exports.is = exports.encodePromise = exports.encodeOption = exports.encodeEither = exports.encodeEffect = exports.encode = exports.decodePromise = exports.decodeOption = exports.decodeEither = exports.decodeEffect = exports.decode = exports.asserts = exports._getSearchTree = exports._getLiterals = void 0; | ||
var E = /*#__PURE__*/_interopRequireWildcard( /*#__PURE__*/require("@effect/data/Either")); | ||
@@ -13,2 +13,4 @@ var _Function = /*#__PURE__*/require("@effect/data/Function"); | ||
var RA = /*#__PURE__*/_interopRequireWildcard( /*#__PURE__*/require("@effect/data/ReadonlyArray")); | ||
var _Debug = /*#__PURE__*/require("@effect/io/Debug"); | ||
var Effect = /*#__PURE__*/_interopRequireWildcard( /*#__PURE__*/require("@effect/io/Effect")); | ||
var AST = /*#__PURE__*/_interopRequireWildcard( /*#__PURE__*/require("@effect/schema/AST")); | ||
@@ -24,16 +26,25 @@ var I = /*#__PURE__*/_interopRequireWildcard( /*#__PURE__*/require("@effect/schema/internal/common")); | ||
const parse = ast => { | ||
const parse = go(ast); | ||
const get = ast => { | ||
const parser = go(ast); | ||
return (input, options) => { | ||
const t = parse(input, options); | ||
if (PR.isFailure(t)) { | ||
throw new Error((0, _TreeFormatter.formatErrors)(t.left)); | ||
const result = parser(input, options); | ||
const resultComputed = PR.eitherSync(result); | ||
if (E.isLeft(resultComputed)) { | ||
throw new Error((0, _TreeFormatter.formatErrors)(resultComputed.left.errors)); | ||
} | ||
return t.right; | ||
return resultComputed.right; | ||
}; | ||
}; | ||
const parseOption = ast => { | ||
const parse = go(ast); | ||
return (input, options) => O.fromEither(parse(input, options)); | ||
const getOption = ast => { | ||
const parser = go(ast); | ||
return (input, options) => O.fromEither(PR.eitherSync(parser(input, options))); | ||
}; | ||
const getEither = ast => { | ||
const parser = go(ast); | ||
return (input, options) => PR.eitherSync(parser(input, options)); | ||
}; | ||
const getPromise = ast => { | ||
const parser = go(ast); | ||
return (input, options) => Effect.runPromise(parser(input, options)); | ||
}; | ||
/** | ||
@@ -43,3 +54,3 @@ * @category decoding | ||
*/ | ||
const decodeEither = schema => go(schema.ast); | ||
const parse = schema => get(schema.ast); | ||
/** | ||
@@ -49,4 +60,4 @@ * @category decoding | ||
*/ | ||
exports.decodeEither = decodeEither; | ||
const decodeOption = schema => parseOption(schema.ast); | ||
exports.parse = parse; | ||
const parseOption = schema => getOption(schema.ast); | ||
/** | ||
@@ -56,10 +67,52 @@ * @category decoding | ||
*/ | ||
exports.parseOption = parseOption; | ||
const parseEither = schema => getEither(schema.ast); | ||
/** | ||
* @category decoding | ||
* @since 1.0.0 | ||
*/ | ||
exports.parseEither = parseEither; | ||
const parsePromise = schema => getPromise(schema.ast); | ||
/** | ||
* @category decoding | ||
* @since 1.0.0 | ||
*/ | ||
exports.parsePromise = parsePromise; | ||
const parseEffect = schema => go(schema.ast); | ||
/** | ||
* @category decoding | ||
* @since 1.0.0 | ||
*/ | ||
exports.parseEffect = parseEffect; | ||
const decode = parse; | ||
/** | ||
* @category decoding | ||
* @since 1.0.0 | ||
*/ | ||
exports.decode = decode; | ||
const decodeOption = parseOption; | ||
/** | ||
* @category decoding | ||
* @since 1.0.0 | ||
*/ | ||
exports.decodeOption = decodeOption; | ||
const decode = schema => parse(schema.ast); | ||
const decodeEither = parseEither; | ||
/** | ||
* @category decoding | ||
* @since 1.0.0 | ||
*/ | ||
exports.decodeEither = decodeEither; | ||
const decodePromise = parsePromise; | ||
/** | ||
* @category decoding | ||
* @since 1.0.0 | ||
*/ | ||
exports.decodePromise = decodePromise; | ||
const decodeEffect = parseEffect; | ||
/** | ||
* @category validation | ||
* @since 1.0.0 | ||
*/ | ||
exports.decode = decode; | ||
const is = schema => (input, options) => E.isRight(go(AST.getTo(schema.ast))(input, options)); | ||
exports.decodeEffect = decodeEffect; | ||
const validate = schema => get(AST.getTo(schema.ast)); | ||
/** | ||
@@ -69,6 +122,4 @@ * @category validation | ||
*/ | ||
exports.is = is; | ||
const asserts = schema => (input, options) => { | ||
parse(AST.getTo(schema.ast))(input, options); | ||
}; | ||
exports.validate = validate; | ||
const validateOption = schema => getOption(AST.getTo(schema.ast)); | ||
/** | ||
@@ -78,4 +129,4 @@ * @category validation | ||
*/ | ||
exports.asserts = asserts; | ||
const validateEither = schema => go(AST.getTo(schema.ast)); | ||
exports.validateOption = validateOption; | ||
const validateEither = schema => getEither(AST.getTo(schema.ast)); | ||
/** | ||
@@ -86,3 +137,3 @@ * @category validation | ||
exports.validateEither = validateEither; | ||
const validateOption = schema => parseOption(AST.getTo(schema.ast)); | ||
const validatePromise = schema => getPromise(AST.getTo(schema.ast)); | ||
/** | ||
@@ -92,10 +143,30 @@ * @category validation | ||
*/ | ||
exports.validateOption = validateOption; | ||
const validate = schema => parse(AST.getTo(schema.ast)); | ||
exports.validatePromise = validatePromise; | ||
const validateEffect = schema => go(AST.getTo(schema.ast)); | ||
/** | ||
* @category validation | ||
* @since 1.0.0 | ||
*/ | ||
exports.validateEffect = validateEffect; | ||
const is = schema => { | ||
const getEither = validateEither(schema); | ||
return a => E.isRight(getEither(a)); | ||
}; | ||
/** | ||
* @category validation | ||
* @since 1.0.0 | ||
*/ | ||
exports.is = is; | ||
const asserts = schema => { | ||
const get = validate(schema); | ||
return (a, options) => { | ||
get(a, options); | ||
}; | ||
}; | ||
/** | ||
* @category encoding | ||
* @since 1.0.0 | ||
*/ | ||
exports.validate = validate; | ||
const encodeEither = schema => go(AST.reverse(schema.ast)); | ||
exports.asserts = asserts; | ||
const encode = schema => get(AST.reverse(schema.ast)); | ||
/** | ||
@@ -105,4 +176,16 @@ * @category encoding | ||
*/ | ||
exports.encode = encode; | ||
const encodeOption = schema => getOption(AST.reverse(schema.ast)); | ||
/** | ||
* @category encoding | ||
* @since 1.0.0 | ||
*/ | ||
exports.encodeOption = encodeOption; | ||
const encodeEither = schema => getEither(AST.reverse(schema.ast)); | ||
/** | ||
* @category encoding | ||
* @since 1.0.0 | ||
*/ | ||
exports.encodeEither = encodeEither; | ||
const encodeOption = schema => parseOption(AST.reverse(schema.ast)); | ||
const encodePromise = schema => getPromise(AST.reverse(schema.ast)); | ||
/** | ||
@@ -112,6 +195,9 @@ * @category encoding | ||
*/ | ||
exports.encodeOption = encodeOption; | ||
const encode = schema => parse(AST.reverse(schema.ast)); | ||
exports.encode = encode; | ||
const go = /*#__PURE__*/I.memoize(ast => { | ||
exports.encodePromise = encodePromise; | ||
const encodeEffect = schema => { | ||
const parser = go(AST.reverse(schema.ast)); | ||
return (a, options) => parser(a, options); | ||
}; | ||
exports.encodeEffect = encodeEffect; | ||
const go = /*#__PURE__*/I.memoize( /*#__PURE__*/(0, _Debug.untracedMethod)(() => ast => { | ||
switch (ast._tag) { | ||
@@ -156,2 +242,6 @@ case "Declaration": | ||
const rest = (0, _Function.pipe)(ast.rest, O.map(RA.mapNonEmpty(go))); | ||
let requiredLen = ast.elements.filter(e => !e.isOptional).length; | ||
if (O.isSome(ast.rest)) { | ||
requiredLen += ast.rest.value.length - 1; | ||
} | ||
return (input, options) => { | ||
@@ -161,6 +251,36 @@ if (!Array.isArray(input)) { | ||
} | ||
const allErrors = options?.allErrors; | ||
const es = []; | ||
let stepKey = 0; | ||
// --------------------------------------------- | ||
// handle missing indexes | ||
// --------------------------------------------- | ||
const len = input.length; | ||
for (let i = len; i <= requiredLen - 1; i++) { | ||
const e = PR.index(i, [PR.missing]); | ||
if (allErrors) { | ||
es.push([stepKey++, e]); | ||
continue; | ||
} else { | ||
return PR.failure(e); | ||
} | ||
} | ||
// --------------------------------------------- | ||
// handle unexpected indexes | ||
// --------------------------------------------- | ||
const isUnexpectedAllowed = options?.isUnexpectedAllowed; | ||
if (!isUnexpectedAllowed && O.isNone(ast.rest)) { | ||
for (let i = ast.elements.length; i <= len - 1; i++) { | ||
const e = PR.index(i, [PR.unexpected(input[i])]); | ||
if (allErrors) { | ||
es.push([stepKey++, e]); | ||
continue; | ||
} else { | ||
return PR.failures(mutableAppend(sortByIndex(es), e)); | ||
} | ||
} | ||
} | ||
const output = []; | ||
const es = []; | ||
const allErrors = options?.allErrors; | ||
let i = 0; | ||
const queue = []; | ||
// --------------------------------------------- | ||
@@ -170,28 +290,44 @@ // handle elements | ||
for (; i < elements.length; i++) { | ||
if (input.length < i + 1) { | ||
if (len < i + 1) { | ||
// the input element is missing... | ||
if (!ast.elements[i].isOptional) { | ||
// ...but the element is required | ||
const e = PR.index(i, [PR.missing]); | ||
if (allErrors) { | ||
es.push(e); | ||
continue; | ||
} else { | ||
return PR.failure(e); | ||
} | ||
if (ast.elements[i].isOptional) { | ||
continue; | ||
} | ||
} else { | ||
const parser = elements[i]; | ||
const t = parser(input[i], options); | ||
if (PR.isFailure(t)) { | ||
// the input element is present but is not valid | ||
const e = PR.index(i, t.left); | ||
if (allErrors) { | ||
es.push(e); | ||
continue; | ||
} else { | ||
return PR.failures(mutableAppend(es, e)); | ||
const te = parser(input[i], options); | ||
const t = PR.either(te); | ||
if (t) { | ||
if (E.isLeft(t)) { | ||
// the input element is present but is not valid | ||
const e = PR.index(i, t.left.errors); | ||
if (allErrors) { | ||
es.push([stepKey++, e]); | ||
continue; | ||
} else { | ||
return PR.failures(mutableAppend(sortByIndex(es), e)); | ||
} | ||
} | ||
output.push([stepKey++, t.right]); | ||
} else { | ||
const nk = stepKey++; | ||
const index = i; | ||
queue.push((0, _Debug.untracedMethod)(() => ({ | ||
es, | ||
output | ||
}) => Effect.flatMap(Effect.either(te), t => { | ||
if (E.isLeft(t)) { | ||
// the input element is present but is not valid | ||
const e = PR.index(index, t.left.errors); | ||
if (allErrors) { | ||
es.push([nk, e]); | ||
return Effect.unit(); | ||
} else { | ||
return PR.failures(mutableAppend(sortByIndex(es), e)); | ||
} | ||
} | ||
output.push([nk, t.right]); | ||
return Effect.unit(); | ||
}))); | ||
} | ||
output.push(t.right); | ||
} | ||
@@ -205,14 +341,37 @@ } | ||
const tail = RA.tailNonEmpty(rest.value); | ||
for (; i < input.length - tail.length; i++) { | ||
const t = head(input[i], options); | ||
if (PR.isFailure(t)) { | ||
const e = PR.index(i, t.left); | ||
if (allErrors) { | ||
es.push(e); | ||
continue; | ||
for (; i < len - tail.length; i++) { | ||
const te = head(input[i], options); | ||
const t = PR.either(te); | ||
if (t) { | ||
if (E.isLeft(t)) { | ||
const e = PR.index(i, t.left.errors); | ||
if (allErrors) { | ||
es.push([stepKey++, e]); | ||
continue; | ||
} else { | ||
return PR.failures(mutableAppend(sortByIndex(es), e)); | ||
} | ||
} else { | ||
return PR.failures(mutableAppend(es, e)); | ||
output.push([stepKey++, t.right]); | ||
} | ||
} else { | ||
output.push(t.right); | ||
const nk = stepKey++; | ||
const index = i; | ||
queue.push((0, _Debug.untracedMethod)(() => ({ | ||
es, | ||
output | ||
}) => Effect.flatMap(Effect.either(te), t => { | ||
if (E.isLeft(t)) { | ||
const e = PR.index(index, t.left.errors); | ||
if (allErrors) { | ||
es.push([nk, e]); | ||
return Effect.unit(); | ||
} else { | ||
return PR.failures(mutableAppend(sortByIndex(es), e)); | ||
} | ||
} else { | ||
output.push([nk, t.right]); | ||
return Effect.unit(); | ||
} | ||
}))); | ||
} | ||
@@ -225,33 +384,39 @@ } | ||
i += j; | ||
if (input.length < i + 1) { | ||
// the input element is missing and the element is required, bail out | ||
return PR.failures(mutableAppend(es, PR.index(i, [PR.missing]))); | ||
if (len < i + 1) { | ||
continue; | ||
} else { | ||
const t = tail[j](input[i], options); | ||
if (PR.isFailure(t)) { | ||
// the input element is present but is not valid | ||
const e = PR.index(i, t.left); | ||
if (allErrors) { | ||
es.push(e); | ||
continue; | ||
} else { | ||
return PR.failures(mutableAppend(es, e)); | ||
const te = tail[j](input[i], options); | ||
const t = PR.either(te); | ||
if (t) { | ||
if (E.isLeft(t)) { | ||
// the input element is present but is not valid | ||
const e = PR.index(i, t.left.errors); | ||
if (allErrors) { | ||
es.push([stepKey++, e]); | ||
continue; | ||
} else { | ||
return PR.failures(mutableAppend(sortByIndex(es), e)); | ||
} | ||
} | ||
} | ||
output.push(t.right); | ||
} | ||
} | ||
} else { | ||
// --------------------------------------------- | ||
// handle unexpected indexes | ||
// --------------------------------------------- | ||
const isUnexpectedAllowed = options?.isUnexpectedAllowed; | ||
for (; i < input.length; i++) { | ||
const e = PR.index(i, [PR.unexpected(input[i])]); | ||
if (!isUnexpectedAllowed) { | ||
if (allErrors) { | ||
es.push(e); | ||
continue; | ||
output.push([stepKey++, t.right]); | ||
} else { | ||
return PR.failures(mutableAppend(es, e)); | ||
const nk = stepKey++; | ||
const index = i; | ||
queue.push((0, _Debug.untracedMethod)(() => ({ | ||
es, | ||
output | ||
}) => Effect.flatMap(Effect.either(te), t => { | ||
if (E.isLeft(t)) { | ||
// the input element is present but is not valid | ||
const e = PR.index(index, t.left.errors); | ||
if (allErrors) { | ||
es.push([nk, e]); | ||
return Effect.unit(); | ||
} else { | ||
return PR.failures(mutableAppend(sortByIndex(es), e)); | ||
} | ||
} | ||
output.push([nk, t.right]); | ||
return Effect.unit(); | ||
}))); | ||
} | ||
@@ -264,3 +429,16 @@ } | ||
// --------------------------------------------- | ||
return RA.isNonEmptyReadonlyArray(es) ? PR.failures(es) : PR.success(output); | ||
const computeResult = ({ | ||
es, | ||
output | ||
}) => RA.isNonEmptyArray(es) ? PR.failures(sortByIndex(es)) : PR.success(sortByIndex(output)); | ||
return queue.length > 0 ? (0, _Debug.untraced)(() => Effect.suspend(() => { | ||
const state = { | ||
es: Array.from(es), | ||
output: Array.from(output) | ||
}; | ||
return Effect.flatMap(Effect.forEachDiscard(queue, f => f(state)), () => computeResult(state)); | ||
})) : computeResult({ | ||
output, | ||
es | ||
}); | ||
}; | ||
@@ -279,12 +457,11 @@ } | ||
} | ||
const output = {}; | ||
const allErrors = options?.allErrors; | ||
const expectedKeys = {}; | ||
const es = []; | ||
const allErrors = options?.allErrors; | ||
let stepKey = 0; | ||
// --------------------------------------------- | ||
// handle property signatures | ||
// handle missing keys | ||
// --------------------------------------------- | ||
for (let i = 0; i < propertySignaturesTypes.length; i++) { | ||
const ps = ast.propertySignatures[i]; | ||
const parser = propertySignaturesTypes[i]; | ||
const name = ps.name; | ||
@@ -296,3 +473,3 @@ expectedKeys[name] = null; | ||
if (allErrors) { | ||
es.push(e); | ||
es.push([stepKey++, e]); | ||
continue; | ||
@@ -303,18 +480,69 @@ } else { | ||
} | ||
} else { | ||
const t = parser(input[name], options); | ||
if (PR.isFailure(t)) { | ||
// the input key is present but is not valid | ||
const e = PR.key(name, t.left); | ||
} | ||
} | ||
// --------------------------------------------- | ||
// handle unexpected keys | ||
// --------------------------------------------- | ||
const isUnexpectedAllowed = options?.isUnexpectedAllowed; | ||
if (!isUnexpectedAllowed && indexSignatures.length === 0) { | ||
for (const key of Reflect.ownKeys(input)) { | ||
if (!Object.prototype.hasOwnProperty.call(expectedKeys, key)) { | ||
const e = PR.key(key, [PR.unexpected(input[key])]); | ||
if (allErrors) { | ||
es.push(e); | ||
es.push([stepKey++, e]); | ||
continue; | ||
} else { | ||
return PR.failures(mutableAppend(es, e)); | ||
return PR.failures(mutableAppend(sortByIndex(es), e)); | ||
} | ||
} | ||
output[name] = t.right; | ||
} | ||
} | ||
// --------------------------------------------- | ||
// handle property signatures | ||
// --------------------------------------------- | ||
const output = {}; | ||
const queue = []; | ||
for (let i = 0; i < propertySignaturesTypes.length; i++) { | ||
const ps = ast.propertySignatures[i]; | ||
const parser = propertySignaturesTypes[i]; | ||
const name = ps.name; | ||
if (Object.prototype.hasOwnProperty.call(input, name)) { | ||
const te = parser(input[name], options); | ||
const t = PR.either(te); | ||
if (t) { | ||
if (E.isLeft(t)) { | ||
// the input key is present but is not valid | ||
const e = PR.key(name, t.left.errors); | ||
if (allErrors) { | ||
es.push([stepKey++, e]); | ||
continue; | ||
} else { | ||
return PR.failures(mutableAppend(sortByIndex(es), e)); | ||
} | ||
} | ||
output[name] = t.right; | ||
} else { | ||
const nk = stepKey++; | ||
const index = name; | ||
queue.push((0, _Debug.untracedMethod)(() => ({ | ||
es, | ||
output | ||
}) => Effect.flatMap(Effect.either(te), t => { | ||
if (E.isLeft(t)) { | ||
// the input key is present but is not valid | ||
const e = PR.key(index, t.left.errors); | ||
if (allErrors) { | ||
es.push([nk, e]); | ||
return Effect.unit(); | ||
} else { | ||
return PR.failures(mutableAppend(sortByIndex(es), e)); | ||
} | ||
} | ||
output[index] = t.right; | ||
return Effect.unit(); | ||
}))); | ||
} | ||
} | ||
} | ||
// --------------------------------------------- | ||
// handle index signatures | ||
@@ -331,50 +559,59 @@ // --------------------------------------------- | ||
} | ||
const te = parameter(key, options); | ||
// --------------------------------------------- | ||
// handle keys | ||
// --------------------------------------------- | ||
let t = parameter(key, options); | ||
if (PR.isFailure(t)) { | ||
const e = PR.key(key, t.left); | ||
if (allErrors) { | ||
es.push(e); | ||
continue; | ||
} else { | ||
return PR.failures(mutableAppend(es, e)); | ||
const t = PR.either(te); | ||
if (t) { | ||
if (E.isLeft(t)) { | ||
const e = PR.key(key, t.left.errors); | ||
if (allErrors) { | ||
es.push([stepKey++, e]); | ||
continue; | ||
} else { | ||
return PR.failures(mutableAppend(sortByIndex(es), e)); | ||
} | ||
} | ||
} | ||
// there's no else here because index signature parameters can't have transformations | ||
// --------------------------------------------- | ||
// handle values | ||
// --------------------------------------------- | ||
t = type(input[key], options); | ||
if (PR.isFailure(t)) { | ||
const e = PR.key(key, t.left); | ||
if (allErrors) { | ||
es.push(e); | ||
continue; | ||
const tve = type(input[key], options); | ||
const tv = PR.either(tve); | ||
if (tv) { | ||
if (E.isLeft(tv)) { | ||
const e = PR.key(key, tv.left.errors); | ||
if (allErrors) { | ||
es.push([stepKey++, e]); | ||
continue; | ||
} else { | ||
return PR.failures(mutableAppend(sortByIndex(es), e)); | ||
} | ||
} else { | ||
return PR.failures(mutableAppend(es, e)); | ||
output[key] = tv.right; | ||
} | ||
} else { | ||
output[key] = t.right; | ||
const nk = stepKey++; | ||
const index = key; | ||
queue.push((0, _Debug.untracedMethod)(() => ({ | ||
es, | ||
output | ||
}) => Effect.flatMap(Effect.either(tve), tv => { | ||
if (E.isLeft(tv)) { | ||
const e = PR.key(index, tv.left.errors); | ||
if (allErrors) { | ||
es.push([nk, e]); | ||
return Effect.unit(); | ||
} else { | ||
return PR.failures(mutableAppend(sortByIndex(es), e)); | ||
} | ||
} else { | ||
output[key] = tv.right; | ||
return Effect.unit(); | ||
} | ||
}))); | ||
} | ||
} | ||
} | ||
} else { | ||
// --------------------------------------------- | ||
// handle unexpected keys | ||
// --------------------------------------------- | ||
const isUnexpectedAllowed = options?.isUnexpectedAllowed; | ||
for (const key of Reflect.ownKeys(input)) { | ||
if (!Object.prototype.hasOwnProperty.call(expectedKeys, key)) { | ||
const e = PR.key(key, [PR.unexpected(input[key])]); | ||
if (!isUnexpectedAllowed) { | ||
if (allErrors) { | ||
es.push(e); | ||
continue; | ||
} else { | ||
return PR.failures(mutableAppend(es, e)); | ||
} | ||
} | ||
} | ||
} | ||
} | ||
@@ -384,3 +621,16 @@ // --------------------------------------------- | ||
// --------------------------------------------- | ||
return RA.isNonEmptyReadonlyArray(es) ? PR.failures(es) : PR.success(output); | ||
const computeResult = ({ | ||
es, | ||
output | ||
}) => RA.isNonEmptyArray(es) ? PR.failures(sortByIndex(es)) : PR.success(output); | ||
return queue.length > 0 ? (0, _Debug.untraced)(() => Effect.suspend(() => { | ||
const state = { | ||
es: Array.from(es), | ||
output: Object.assign({}, output) | ||
}; | ||
return Effect.flatMap(Effect.forEachDiscard(queue, f => f(state)), () => computeResult(state)); | ||
})) : computeResult({ | ||
es, | ||
output | ||
}); | ||
}; | ||
@@ -393,3 +643,2 @@ } | ||
const len = ownKeys.length; | ||
const otherwise = searchTree.otherwise; | ||
const map = new Map(); | ||
@@ -401,2 +650,4 @@ for (let i = 0; i < ast.types.length; i++) { | ||
const es = []; | ||
let stepKey = 0; | ||
let candidates = []; | ||
if (len > 0) { | ||
@@ -414,30 +665,52 @@ // if there is at least one key then input must be an object | ||
// retrive the minimal set of candidates for decoding | ||
const bucket = buckets[literal]; | ||
for (let i = 0; i < bucket.length; i++) { | ||
const t = map.get(bucket[i])(input, options); | ||
if (PR.isSuccess(t)) { | ||
return t; | ||
} else { | ||
es.push(PR.unionMember(t.left)); | ||
} | ||
} | ||
candidates = candidates.concat(buckets[literal]); | ||
} else { | ||
es.push(PR.key(name, [PR.type(searchTree.keys[name].ast, input[name])])); | ||
es.push([stepKey++, PR.key(name, [PR.type(searchTree.keys[name].ast, input[name])])]); | ||
} | ||
} else { | ||
es.push(PR.key(name, [PR.missing])); | ||
es.push([stepKey++, PR.key(name, [PR.missing])]); | ||
} | ||
} | ||
} else { | ||
es.push(PR.type(unknownRecord, input)); | ||
es.push([stepKey++, PR.type(unknownRecord, input)]); | ||
} | ||
} | ||
// if none of the schemas with at least one property with a literal value succeeded, | ||
// proceed with those that have no literal at all | ||
for (let i = 0; i < otherwise.length; i++) { | ||
const t = map.get(otherwise[i])(input, options); | ||
if (PR.isSuccess(t)) { | ||
return t; | ||
if (searchTree.otherwise.length > 0) { | ||
candidates = candidates.concat(searchTree.otherwise); | ||
} | ||
const queue = []; | ||
const finalResult = { | ||
ref: undefined | ||
}; | ||
for (let i = 0; i < candidates.length; i++) { | ||
const te = map.get(candidates[i])(input, options); | ||
// the members of a union are ordered based on which one should be decoded first, | ||
// therefore if one member has added a task, all subsequent members must | ||
// also add a task to the queue even if they are synchronous | ||
const t = queue.length === 0 ? PR.either(te) : undefined; | ||
if (t) { | ||
if (E.isRight(t)) { | ||
return PR.success(t.right); | ||
} else { | ||
es.push([stepKey++, PR.unionMember(t.left.errors)]); | ||
} | ||
} else { | ||
es.push(PR.unionMember(t.left)); | ||
const nk = stepKey++; | ||
queue.push((0, _Debug.untracedMethod)(() => ({ | ||
es, | ||
finalResult | ||
}) => Effect.suspend(() => { | ||
if (finalResult.ref) { | ||
return Effect.unit(); | ||
} else { | ||
return Effect.flatMap(Effect.either(te), t => { | ||
if (E.isRight(t)) { | ||
finalResult.ref = PR.success(t.right); | ||
} else { | ||
es.push([nk, PR.unionMember(t.left.errors)]); | ||
} | ||
return Effect.unit(); | ||
}); | ||
} | ||
}))); | ||
} | ||
@@ -448,3 +721,24 @@ } | ||
// --------------------------------------------- | ||
return RA.isNonEmptyReadonlyArray(es) ? PR.failures(es) : PR.failure(PR.type(AST.neverKeyword, input)); | ||
const computeResult = ({ | ||
es | ||
}) => RA.isNonEmptyArray(es) ? PR.failures(sortByIndex(es)) : | ||
// this should never happen | ||
PR.failure(PR.type(AST.neverKeyword, input)); | ||
return queue.length > 0 ? (0, _Debug.untraced)(() => Effect.suspend(() => { | ||
const state = { | ||
es: Array.from(es), | ||
finalResult: { | ||
ref: finalResult.ref | ||
} | ||
}; | ||
return Effect.flatMap(Effect.forEachDiscard(queue, f => f(state)), () => { | ||
if (state.finalResult.ref) { | ||
return state.finalResult.ref; | ||
} | ||
return computeResult(state); | ||
}); | ||
})) : computeResult({ | ||
es, | ||
finalResult | ||
}); | ||
}; | ||
@@ -459,33 +753,10 @@ } | ||
case "Refinement": | ||
{ | ||
const from = go(ast.from); | ||
if (ast.isReversed) { | ||
const to = go(AST.getTo(ast.from)); | ||
return (a, options) => (0, _Function.pipe)(to(a, options), | ||
// validate input | ||
E.flatMap(a => ast.decode(a, options)), | ||
// refine | ||
E.flatMap(a => from(a, options)) // encode | ||
); | ||
} | ||
return (u, options) => E.flatMap(from(u, options), a => ast.decode(a, options)); | ||
} | ||
case "Transform": | ||
{ | ||
const from = go(ast.from); | ||
if (ast.isReversed) { | ||
const to = go(ast.to); | ||
return (a, options) => (0, _Function.pipe)(to(a, options), | ||
// validate input | ||
E.flatMap(a => ast.encode(a, options)), | ||
// transform | ||
E.flatMap(a => from(a, options)) // encode | ||
); | ||
} | ||
return (u, options) => E.flatMap(from(u, options), a => ast.decode(a, options)); | ||
const to = go(ast.to); | ||
return (i1, options) => PR.flatMap(from(i1, options), a => PR.flatMap(ast.decode(a, options), i2 => to(i2, options))); | ||
} | ||
} | ||
}); | ||
})); | ||
const fromRefinement = (ast, refinement) => u => refinement(u) ? PR.success(u) : PR.failure(PR.type(ast, u)); | ||
@@ -502,4 +773,5 @@ /** @internal */ | ||
const propertySignature = ast.propertySignatures[i]; | ||
if (AST.isLiteral(propertySignature.type) && !propertySignature.isOptional) { | ||
out.push([propertySignature.name, propertySignature.type]); | ||
const type = AST.getFrom(propertySignature.type); | ||
if (AST.isLiteral(type) && !propertySignature.isOptional) { | ||
out.push([propertySignature.name, type]); | ||
} | ||
@@ -510,5 +782,4 @@ } | ||
case "Refinement": | ||
case "Transform": | ||
return _getLiterals(ast.from); | ||
case "Transform": | ||
return ast.isReversed ? _getLiterals(ast.to) : _getLiterals(AST.getFrom(ast.from)); | ||
} | ||
@@ -569,4 +840,8 @@ return []; | ||
exports._getSearchTree = _getSearchTree; | ||
const unknownArray = /*#__PURE__*/AST.createTuple([], /*#__PURE__*/O.some([AST.unknownKeyword]), true); | ||
const unknownRecord = /*#__PURE__*/AST.createTypeLiteral([], [/*#__PURE__*/AST.createIndexSignature(AST.stringKeyword, AST.unknownKeyword, true), /*#__PURE__*/AST.createIndexSignature(AST.symbolKeyword, AST.unknownKeyword, true)]); | ||
const unknownArray = /*#__PURE__*/AST.createTuple([], /*#__PURE__*/O.some([AST.unknownKeyword]), true, { | ||
[AST.DescriptionAnnotationId]: "a generic array" | ||
}); | ||
const unknownRecord = /*#__PURE__*/AST.createTypeLiteral([], [/*#__PURE__*/AST.createIndexSignature(AST.stringKeyword, AST.unknownKeyword, true), /*#__PURE__*/AST.createIndexSignature(AST.symbolKeyword, AST.unknownKeyword, true)], { | ||
[AST.DescriptionAnnotationId]: "a generic object" | ||
}); | ||
const mutableAppend = (self, a) => { | ||
@@ -589,2 +864,5 @@ self.push(a); | ||
}; | ||
function sortByIndex(es) { | ||
return es.sort(([a], [b]) => a > b ? 1 : a < b ? -1 : 0).map(([_, a]) => a); | ||
} | ||
//# sourceMappingURL=Parser.js.map |
/** | ||
* @since 1.0.0 | ||
*/ | ||
import type { Either, Left, Right } from "@effect/data/Either"; | ||
import * as E from "@effect/data/Either"; | ||
import * as O from "@effect/data/Option"; | ||
import type { NonEmptyReadonlyArray } from "@effect/data/ReadonlyArray"; | ||
import * as Effect from "@effect/io/Effect"; | ||
import type * as AST from "@effect/schema/AST"; | ||
@@ -10,5 +12,17 @@ /** | ||
*/ | ||
export type ParseResult<A> = Either<NonEmptyReadonlyArray<ParseError>, A>; | ||
export interface ParseResult<A> extends Effect.Effect<never, ParseError, A> { | ||
} | ||
/** | ||
* `ParseError` is a type that represents the different types of errors that can occur when decoding a value. | ||
* @since 1.0.0 | ||
*/ | ||
export interface ParseError { | ||
readonly _tag: "ParseError"; | ||
readonly errors: NonEmptyReadonlyArray<ParseErrors>; | ||
} | ||
/** | ||
* @since 1.0.0 | ||
*/ | ||
export declare const parseError: (errors: readonly [ParseErrors, ...ParseErrors[]]) => ParseError; | ||
/** | ||
* `ParseErrors` is a type that represents the different types of errors that can occur when decoding a value. | ||
* | ||
@@ -18,3 +32,3 @@ * @category model | ||
*/ | ||
export type ParseError = Type | Index | Key | Missing | Unexpected | UnionMember; | ||
export type ParseErrors = Type | Index | Key | Missing | Unexpected | UnionMember; | ||
/** | ||
@@ -34,2 +48,3 @@ * The `Type` variant of the `ParseError` type represents an error that occurs when the `actual` value is not of the expected type. | ||
readonly actual: unknown; | ||
readonly message: O.Option<string>; | ||
} | ||
@@ -40,3 +55,3 @@ /** | ||
*/ | ||
export declare const type: (expected: AST.AST, actual: unknown) => Type; | ||
export declare const type: (expected: AST.AST, actual: unknown, message?: string) => Type; | ||
/** | ||
@@ -54,3 +69,3 @@ * The `Index` decode error indicates that there was an error at a specific index in an array or tuple. | ||
readonly index: number; | ||
readonly errors: NonEmptyReadonlyArray<ParseError>; | ||
readonly errors: NonEmptyReadonlyArray<ParseErrors>; | ||
} | ||
@@ -61,3 +76,3 @@ /** | ||
*/ | ||
export declare const index: (index: number, errors: readonly [ParseError, ...ParseError[]]) => Index; | ||
export declare const index: (index: number, errors: readonly [ParseErrors, ...ParseErrors[]]) => Index; | ||
/** | ||
@@ -76,3 +91,3 @@ * The `Key` variant of the `ParseError` type represents an error that occurs when a key in an object is invalid. | ||
readonly key: PropertyKey; | ||
readonly errors: NonEmptyReadonlyArray<ParseError>; | ||
readonly errors: NonEmptyReadonlyArray<ParseErrors>; | ||
} | ||
@@ -83,3 +98,3 @@ /** | ||
*/ | ||
export declare const key: (key: PropertyKey, errors: readonly [ParseError, ...ParseError[]]) => Key; | ||
export declare const key: (key: PropertyKey, errors: readonly [ParseErrors, ...ParseErrors[]]) => Key; | ||
/** | ||
@@ -122,3 +137,3 @@ * Error that occurs when a required key or index is missing. | ||
readonly _tag: "UnionMember"; | ||
readonly errors: NonEmptyReadonlyArray<ParseError>; | ||
readonly errors: NonEmptyReadonlyArray<ParseErrors>; | ||
} | ||
@@ -129,3 +144,3 @@ /** | ||
*/ | ||
export declare const unionMember: (errors: readonly [ParseError, ...ParseError[]]) => UnionMember; | ||
export declare const unionMember: (errors: readonly [ParseErrors, ...ParseErrors[]]) => UnionMember; | ||
/** | ||
@@ -140,3 +155,3 @@ * @category constructors | ||
*/ | ||
export declare const failure: (e: ParseError) => ParseResult<never>; | ||
export declare const failure: (e: ParseErrors) => ParseResult<never>; | ||
/** | ||
@@ -146,13 +161,23 @@ * @category constructors | ||
*/ | ||
export declare const failures: (es: readonly [ParseError, ...ParseError[]]) => ParseResult<never>; | ||
export declare const failures: (es: readonly [ParseErrors, ...ParseErrors[]]) => ParseResult<never>; | ||
/** | ||
* @category guards | ||
* @category optimisation | ||
* @since 1.0.0 | ||
*/ | ||
export declare const isSuccess: <A>(self: ParseResult<A>) => self is Right<A>; | ||
export declare const either: <E, A>(self: Effect.Effect<never, E, A>) => E.Either<E, A> | undefined; | ||
/** | ||
* @category guards | ||
* @category optimisation | ||
* @since 1.0.0 | ||
*/ | ||
export declare const isFailure: <A>(self: ParseResult<A>) => self is Left<NonEmptyReadonlyArray<ParseError>>; | ||
export declare const eitherSync: <E, A>(self: Effect.Effect<never, E, A>) => E.Either<E, A>; | ||
/** | ||
* @category optimisation | ||
* @since 1.0.0 | ||
*/ | ||
export declare const flatMap: <E, E1, A, B>(self: Effect.Effect<never, E, A>, f: (self: A) => Effect.Effect<never, E1, B>) => Effect.Effect<never, E | E1, B>; | ||
/** | ||
* @category optimisation | ||
* @since 1.0.0 | ||
*/ | ||
export declare const map: <E, A, B>(self: Effect.Effect<never, E, A>, f: (self: A) => B) => Effect.Effect<never, E, B>; | ||
//# sourceMappingURL=ParseResult.d.ts.map |
@@ -6,4 +6,9 @@ "use strict"; | ||
}); | ||
exports.unionMember = exports.unexpected = exports.type = exports.success = exports.missing = exports.key = exports.isSuccess = exports.isFailure = exports.index = exports.failures = exports.failure = void 0; | ||
exports.unionMember = exports.unexpected = exports.type = exports.success = exports.parseError = exports.missing = exports.map = exports.key = exports.index = exports.flatMap = exports.failures = exports.failure = exports.eitherSync = exports.either = void 0; | ||
var E = /*#__PURE__*/_interopRequireWildcard( /*#__PURE__*/require("@effect/data/Either")); | ||
var O = /*#__PURE__*/_interopRequireWildcard( /*#__PURE__*/require("@effect/data/Option")); | ||
var _Cause = /*#__PURE__*/require("@effect/io/Cause"); | ||
var Debug = /*#__PURE__*/_interopRequireWildcard( /*#__PURE__*/require("@effect/io/Debug")); | ||
var Effect = /*#__PURE__*/_interopRequireWildcard( /*#__PURE__*/require("@effect/io/Effect")); | ||
var Exit = /*#__PURE__*/_interopRequireWildcard( /*#__PURE__*/require("@effect/io/Exit")); | ||
function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function (nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); } | ||
@@ -16,9 +21,18 @@ function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; } | ||
/** | ||
* @since 1.0.0 | ||
*/ | ||
const parseError = errors => ({ | ||
_tag: "ParseError", | ||
errors | ||
}); | ||
/** | ||
* @category constructors | ||
* @since 1.0.0 | ||
*/ | ||
const type = (expected, actual) => ({ | ||
exports.parseError = parseError; | ||
const type = (expected, actual, message) => ({ | ||
_tag: "Type", | ||
expected, | ||
actual | ||
actual, | ||
message: O.fromNullable(message) | ||
}); | ||
@@ -76,3 +90,3 @@ /** | ||
exports.unionMember = unionMember; | ||
const success = E.right; | ||
const success = a => Exit.succeed(a); | ||
/** | ||
@@ -83,3 +97,3 @@ * @category constructors | ||
exports.success = success; | ||
const failure = e => E.left([e]); | ||
const failure = e => Exit.fail(parseError([e])); | ||
/** | ||
@@ -90,16 +104,78 @@ * @category constructors | ||
exports.failure = failure; | ||
const failures = es => E.left(es); | ||
const failures = es => Exit.fail(parseError(es)); | ||
exports.failures = failures; | ||
const untrace = self => { | ||
// TODO: find a way to detect Traced | ||
const s = self; | ||
return s["_tag"] === "Traced" ? s["i0"] : s; | ||
}; | ||
/** | ||
* @category guards | ||
* @category optimisation | ||
* @since 1.0.0 | ||
*/ | ||
exports.failures = failures; | ||
const isSuccess = E.isRight; | ||
const either = self => { | ||
const untraced = untrace(self); | ||
if ((0, Exit.isExit)(untraced)) { | ||
if (untraced._tag === "Success") { | ||
return E.right(untraced.value); | ||
} else { | ||
const failure = (0, _Cause.failureOption)(untraced.cause); | ||
if (failure._tag === "Some") { | ||
return E.left(failure.value); | ||
} | ||
} | ||
} | ||
}; | ||
/** | ||
* @category guards | ||
* @category optimisation | ||
* @since 1.0.0 | ||
*/ | ||
exports.isSuccess = isSuccess; | ||
const isFailure = E.isLeft; | ||
exports.isFailure = isFailure; | ||
exports.either = either; | ||
const eitherSync = self => { | ||
const untraced = untrace(self); | ||
if ((0, Exit.isExit)(untraced)) { | ||
if (untraced._tag === "Success") { | ||
return E.right(untraced.value); | ||
} else { | ||
const failure = (0, _Cause.failureOption)(untraced.cause); | ||
if (failure._tag === "Some") { | ||
return E.left(failure.value); | ||
} | ||
} | ||
} | ||
return Debug.untraced(() => Effect.runSyncEither(self)); | ||
}; | ||
/** | ||
* @category optimisation | ||
* @since 1.0.0 | ||
*/ | ||
exports.eitherSync = eitherSync; | ||
const flatMap = /*#__PURE__*/Debug.methodWithTrace((trace, restore) => (self, f) => { | ||
const e = either(self); | ||
if (e) { | ||
if (E.isRight(e)) { | ||
return restore(f)(e.right); | ||
} else { | ||
return Exit.fail(e.left); | ||
} | ||
} | ||
return Effect.flatMap(self, restore(f)).traced(trace); | ||
}); | ||
/** | ||
* @category optimisation | ||
* @since 1.0.0 | ||
*/ | ||
exports.flatMap = flatMap; | ||
const map = /*#__PURE__*/Debug.methodWithTrace((trace, restore) => (self, f) => { | ||
const e = either(self); | ||
if (e) { | ||
if (E.isRight(e)) { | ||
return Exit.succeed(restore(f)(e.right)); | ||
} else { | ||
return Exit.fail(e.left); | ||
} | ||
} | ||
return Effect.map(self, restore(f)).traced(trace); | ||
}); | ||
exports.map = map; | ||
//# sourceMappingURL=ParseResult.js.map |
@@ -978,4 +978,18 @@ <h3 align="center"> | ||
To perform these kinds of transformations, the `@effect/schema` library provides the `transform` and `transformEither` combinators. | ||
To perform these kinds of transformations, the `@effect/schema` library provides the `transform` combinator. | ||
**transform** | ||
```ts | ||
<I1, A1, I2, A2>(from: Schema<I1, A1>, to: Schema<I2, A2>, decode: (a1: A1) => I2, encode: (i2: I2) => A1): Schema<I1, A2> | ||
``` | ||
```mermaid | ||
flowchart TD | ||
schema1["from: Schema<I1, A1>"] | ||
schema2["to: Schema<I2, A2>"] | ||
schema1--decode: A1 -> I2-->schema2 | ||
schema2--encode: I2 -> A1-->schema1 | ||
``` | ||
The `transform` combinator takes a target schema, a transformation function from the source type to the target type, and a reverse transformation function from the target type back to the source type. It returns a new schema that applies the transformation function to the output of the original schema before returning it. If the original schema fails to decode a value, the transformed schema will also fail. | ||
@@ -986,8 +1000,2 @@ | ||
// define a schema for the string type | ||
const stringSchema: S.Schema<string> = S.string; | ||
// define a schema for a tuple with one element of type string | ||
const tupleSchema: S.Schema<[string]> = S.tuple(S.string); | ||
// define a function that converts a string into a tuple with one element of type string | ||
@@ -997,6 +1005,6 @@ const decode = (s: string): [string] => [s]; | ||
// define a function that converts a tuple with one element of type string into a string | ||
const encode = ([s]: [string]): string => s; | ||
const encode = ([s]: readonly [string]): string => s; | ||
// use the transform combinator to convert the string schema into the tuple schema | ||
const transformedSchema: S.Schema<string, [string]> = S.transform(stringSchema, tupleSchema, decode, encode); | ||
const transformedSchema: S.Schema<string, readonly [string]> = S.transform(S.string, S.tuple(S.string), decode, encode); | ||
``` | ||
@@ -1011,15 +1019,7 @@ | ||
```ts | ||
import { pipe } from "@effect/data/Function"; | ||
import * as PR from "@effect/schema/ParseResult"; | ||
import * as S from "@effect/schema/Schema"; | ||
import * as AST from "@effect/schema/AST"; | ||
// define a schema for the string type | ||
const stringSchema: S.Schema<string> = S.string; | ||
// define a schema for the boolean type | ||
const booleanSchema: S.Schema<boolean> = S.boolean; | ||
// define a function that converts a string into a boolean | ||
const decode = (s: string): PR.ParseResult<boolean> => | ||
const decode = (s: string) => | ||
s === "true" | ||
@@ -1029,17 +1029,9 @@ ? PR.success(true) | ||
? PR.success(false) | ||
: PR.failure( | ||
PR.type( | ||
AST.createUnion([ | ||
AST.createLiteral("true"), | ||
AST.createLiteral("false"), | ||
]), | ||
s | ||
) | ||
); | ||
: PR.failure(PR.type(S.union(S.literal('true'), S.literal('false')).ast, s)); | ||
// define a function that converts a boolean into a string | ||
const encode = (b: boolean): ParseResult<string> => PR.success(String(b)); | ||
const encode = (b: boolean) => PR.success(String(b)); | ||
// use the transformEither combinator to convert the string schema into the boolean schema | ||
const transformedSchema: S.Schema<string, boolean> = S.transformEither(stringSchema, booleanSchema, decode, encode); | ||
const transformedSchema: S.Schema<string, boolean> = S.transformEither(S.string, S.boolean, decode, encode); | ||
``` | ||
@@ -1139,3 +1131,3 @@ | ||
// const schema: S.Schema<string, Date> | ||
const schema = S.dateFromString(S.string); | ||
const schema = S.dateFromString; | ||
const decode = S.decode(schema); | ||
@@ -1142,0 +1134,0 @@ |
@@ -7,9 +7,10 @@ /** | ||
import * as D from "@effect/data/Data"; | ||
import type { Either } from "@effect/data/Either"; | ||
import * as E from "@effect/data/Either"; | ||
import type { Either } from "@effect/data/Either"; | ||
import type { Option } from "@effect/data/Option"; | ||
import type { Predicate, Refinement } from "@effect/data/Predicate"; | ||
import type { ParseOptions } from "@effect/schema/AST"; | ||
import * as AST from "@effect/schema/AST"; | ||
import type { ParseOptions } from "@effect/schema/AST"; | ||
import type { ParseResult } from "@effect/schema/ParseResult"; | ||
import * as PR from "@effect/schema/ParseResult"; | ||
/** | ||
@@ -44,2 +45,6 @@ * @category model | ||
export declare const to: <I, A>(schema: Schema<I, A>) => Schema<A, A>; | ||
/** | ||
* @since 1.0.0 | ||
*/ | ||
export declare const reverse: <I, A>(schema: Schema<I, A>) => Schema<A, I>; | ||
export { | ||
@@ -57,2 +62,6 @@ /** | ||
*/ | ||
decodeEffect, | ||
/** | ||
* @since 1.0.0 | ||
*/ | ||
decodeEither, | ||
@@ -66,2 +75,6 @@ /** | ||
*/ | ||
decodePromise, | ||
/** | ||
* @since 1.0.0 | ||
*/ | ||
encode, | ||
@@ -71,2 +84,6 @@ /** | ||
*/ | ||
encodeEffect, | ||
/** | ||
* @since 1.0.0 | ||
*/ | ||
encodeEither, | ||
@@ -80,2 +97,6 @@ /** | ||
*/ | ||
encodePromise, | ||
/** | ||
* @since 1.0.0 | ||
*/ | ||
is, | ||
@@ -85,2 +106,22 @@ /** | ||
*/ | ||
parse, | ||
/** | ||
* @since 1.0.0 | ||
*/ | ||
parseEffect, | ||
/** | ||
* @since 1.0.0 | ||
*/ | ||
parseEither, | ||
/** | ||
* @since 1.0.0 | ||
*/ | ||
parseOption, | ||
/** | ||
* @since 1.0.0 | ||
*/ | ||
parsePromise, | ||
/** | ||
* @since 1.0.0 | ||
*/ | ||
validate, | ||
@@ -90,2 +131,6 @@ /** | ||
*/ | ||
validateEffect, | ||
/** | ||
* @since 1.0.0 | ||
*/ | ||
validateEither, | ||
@@ -95,3 +140,7 @@ /** | ||
*/ | ||
validateOption } from "@effect/schema/Parser"; | ||
validateOption, | ||
/** | ||
* @since 1.0.0 | ||
*/ | ||
validatePromise } from "@effect/schema/Parser"; | ||
export type { | ||
@@ -331,5 +380,16 @@ /** | ||
*/ | ||
export declare const transformEffect: { | ||
<I2, A2, A1>(to: Schema<I2, A2>, decode: (a1: A1, options?: ParseOptions) => ParseResult<I2>, encode: (i2: I2, options?: ParseOptions) => ParseResult<A1>): <I1>(self: Schema<I1, A1>) => Schema<I1, A2>; | ||
<I1, A1, I2, A2>(from: Schema<I1, A1>, to: Schema<I2, A2>, decode: (a1: A1, options?: ParseOptions) => ParseResult<I2>, encode: (i2: I2, options?: ParseOptions) => ParseResult<A1>): Schema<I1, A2>; | ||
}; | ||
/** | ||
Create a new `Schema` by transforming the input and output of an existing `Schema` | ||
using the provided decoding functions. | ||
@category combinators | ||
@since 1.0.0 | ||
*/ | ||
export declare const transformEither: { | ||
<B, A>(to: Schema<any, B>, decode: (input: A, options?: ParseOptions) => ParseResult<B>, encode: (input: B, options?: ParseOptions) => ParseResult<A>): <I>(self: Schema<I, A>) => Schema<I, B>; | ||
<I, A, B>(self: Schema<I, A>, to: Schema<any, B>, decode: (input: A, options?: ParseOptions) => ParseResult<B>, encode: (input: B, options?: ParseOptions) => ParseResult<A>): Schema<I, B>; | ||
<I2, A2, A1>(to: Schema<I2, A2>, decode: (a1: A1, options?: ParseOptions) => E.Either<PR.ParseError, I2>, encode: (i2: I2, options?: ParseOptions) => E.Either<PR.ParseError, A1>): <I1>(self: Schema<I1, A1>) => Schema<I1, A2>; | ||
<I1, A1, I2, A2>(from: Schema<I1, A1>, to: Schema<I2, A2>, decode: (a1: A1, options?: ParseOptions) => E.Either<PR.ParseError, I2>, encode: (i2: I2, options?: ParseOptions) => E.Either<PR.ParseError, A1>): Schema<I1, A2>; | ||
}; | ||
@@ -344,4 +404,4 @@ /** | ||
export declare const transform: { | ||
<B, A>(to: Schema<any, B>, ab: (a: A) => B, ba: (b: B) => A): <I>(self: Schema<I, A>) => Schema<I, B>; | ||
<I, A, B>(self: Schema<I, A>, to: Schema<any, B>, ab: (a: A) => B, ba: (b: B) => A): Schema<I, B>; | ||
<I2, A2, A1>(to: Schema<I2, A2>, decode: (a1: A1) => I2, encode: (i2: I2) => A1): <I1>(self: Schema<I1, A1>) => Schema<I1, A2>; | ||
<I1, A1, I2, A2>(from: Schema<I1, A1>, to: Schema<I2, A2>, decode: (a1: A1) => I2, encode: (i2: I2) => A1): Schema<I1, A2>; | ||
}; | ||
@@ -604,3 +664,3 @@ /** | ||
*/ | ||
export declare const dateFromString: <I>(self: Schema<I, string>) => Schema<I, Date>; | ||
export declare const dateFromString: Schema<string, Date>; | ||
/** | ||
@@ -610,3 +670,3 @@ * @category constructors | ||
*/ | ||
export declare const eitherFromSelf: <IE, E, IA, A>(left: Schema<IE, E>, right: Schema<IA, A>) => Schema<E.Either<IE, IA>, E.Either<E, A>>; | ||
export declare const eitherFromSelf: <IE, E, IA, A>(left: Schema<IE, E>, right: Schema<IA, A>) => Schema<Either<IE, IA>, Either<E, A>>; | ||
/** | ||
@@ -622,3 +682,3 @@ * @category parsers | ||
readonly right: IA; | ||
}, E.Either<E, A>>; | ||
}, Either<E, A>>; | ||
/** | ||
@@ -625,0 +685,0 @@ * @since 1.0.0 |
192
Schema.js
@@ -20,2 +20,8 @@ "use strict"; | ||
}); | ||
Object.defineProperty(exports, "decodeEffect", { | ||
enumerable: true, | ||
get: function () { | ||
return P.decodeEffect; | ||
} | ||
}); | ||
Object.defineProperty(exports, "decodeEither", { | ||
@@ -33,2 +39,8 @@ enumerable: true, | ||
}); | ||
Object.defineProperty(exports, "decodePromise", { | ||
enumerable: true, | ||
get: function () { | ||
return P.decodePromise; | ||
} | ||
}); | ||
exports.element = exports.eitherFromSelf = exports.either = exports.documentation = exports.description = void 0; | ||
@@ -41,2 +53,8 @@ Object.defineProperty(exports, "encode", { | ||
}); | ||
Object.defineProperty(exports, "encodeEffect", { | ||
enumerable: true, | ||
get: function () { | ||
return P.encodeEffect; | ||
} | ||
}); | ||
Object.defineProperty(exports, "encodeEither", { | ||
@@ -54,2 +72,8 @@ enumerable: true, | ||
}); | ||
Object.defineProperty(exports, "encodePromise", { | ||
enumerable: true, | ||
get: function () { | ||
return P.encodePromise; | ||
} | ||
}); | ||
exports.extend = exports.examples = exports.enums = exports.endsWith = void 0; | ||
@@ -64,4 +88,35 @@ exports.filter = filter; | ||
}); | ||
exports.multipleOf = exports.minLength = exports.minItems = exports.message = exports.maxLength = exports.maxItems = exports.make = exports.literal = exports.lessThanOrEqualToBigint = exports.lessThanOrEqualTo = exports.lessThanBigint = exports.lessThan = exports.length = exports.lazy = exports.keyof = exports.json = exports.itemsCount = void 0; | ||
exports.unknown = exports.uniqueSymbol = exports.union = exports.undefined = exports.tuple = exports.trimmed = exports.trim = exports.transformEither = exports.transform = exports.to = exports.title = exports.templateLiteral = exports.symbol = exports.struct = exports.string = exports.startsWith = exports.rest = exports.record = exports.readonlySetFromSelf = exports.readonlySet = exports.readonlyMapFromSelf = exports.readonlyMap = exports.positiveBigint = exports.positive = exports.pick = exports.pattern = exports.partial = exports.optionsFromOptionals = exports.optionalElement = exports.optional = exports.optionFromSelf = exports.optionFromNullable = exports.option = exports.omit = exports.object = exports.numberFromString = exports.number = exports.nullable = exports.null = exports.nonPositiveBigint = exports.nonPositive = exports.nonNegativeBigint = exports.nonNegative = exports.nonNaN = exports.nonEmptyArray = exports.nonEmpty = exports.never = exports.negativeBigint = exports.negative = void 0; | ||
exports.maxLength = exports.maxItems = exports.make = exports.literal = exports.lessThanOrEqualToBigint = exports.lessThanOrEqualTo = exports.lessThanBigint = exports.lessThan = exports.length = exports.lazy = exports.keyof = exports.json = exports.itemsCount = void 0; | ||
exports.optionsFromOptionals = exports.optionalElement = exports.optional = exports.optionFromSelf = exports.optionFromNullable = exports.option = exports.omit = exports.object = exports.numberFromString = exports.number = exports.nullable = exports.null = exports.nonPositiveBigint = exports.nonPositive = exports.nonNegativeBigint = exports.nonNegative = exports.nonNaN = exports.nonEmptyArray = exports.nonEmpty = exports.never = exports.negativeBigint = exports.negative = exports.multipleOf = exports.minLength = exports.minItems = exports.message = void 0; | ||
Object.defineProperty(exports, "parse", { | ||
enumerable: true, | ||
get: function () { | ||
return P.parse; | ||
} | ||
}); | ||
Object.defineProperty(exports, "parseEffect", { | ||
enumerable: true, | ||
get: function () { | ||
return P.parseEffect; | ||
} | ||
}); | ||
Object.defineProperty(exports, "parseEither", { | ||
enumerable: true, | ||
get: function () { | ||
return P.parseEither; | ||
} | ||
}); | ||
Object.defineProperty(exports, "parseOption", { | ||
enumerable: true, | ||
get: function () { | ||
return P.parseOption; | ||
} | ||
}); | ||
Object.defineProperty(exports, "parsePromise", { | ||
enumerable: true, | ||
get: function () { | ||
return P.parsePromise; | ||
} | ||
}); | ||
exports.unknown = exports.uniqueSymbol = exports.union = exports.undefined = exports.tuple = exports.trimmed = exports.trim = exports.transformEither = exports.transformEffect = exports.transform = exports.to = exports.title = exports.templateLiteral = exports.symbol = exports.struct = exports.string = exports.startsWith = exports.reverse = exports.rest = exports.record = exports.readonlySetFromSelf = exports.readonlySet = exports.readonlyMapFromSelf = exports.readonlyMap = exports.positiveBigint = exports.positive = exports.pick = exports.pattern = exports.partial = void 0; | ||
Object.defineProperty(exports, "validate", { | ||
@@ -73,2 +128,8 @@ enumerable: true, | ||
}); | ||
Object.defineProperty(exports, "validateEffect", { | ||
enumerable: true, | ||
get: function () { | ||
return P.validateEffect; | ||
} | ||
}); | ||
Object.defineProperty(exports, "validateEither", { | ||
@@ -86,2 +147,8 @@ enumerable: true, | ||
}); | ||
Object.defineProperty(exports, "validatePromise", { | ||
enumerable: true, | ||
get: function () { | ||
return P.validatePromise; | ||
} | ||
}); | ||
exports.void = void 0; | ||
@@ -99,2 +166,4 @@ var B = /*#__PURE__*/_interopRequireWildcard( /*#__PURE__*/require("@effect/data/Bigint")); | ||
var RA = /*#__PURE__*/_interopRequireWildcard( /*#__PURE__*/require("@effect/data/ReadonlyArray")); | ||
var _Debug = /*#__PURE__*/require("@effect/io/Debug"); | ||
var Effect = /*#__PURE__*/_interopRequireWildcard( /*#__PURE__*/require("@effect/io/Effect")); | ||
var AST = /*#__PURE__*/_interopRequireWildcard( /*#__PURE__*/require("@effect/schema/AST")); | ||
@@ -120,4 +189,9 @@ var I = /*#__PURE__*/_interopRequireWildcard( /*#__PURE__*/require("@effect/schema/internal/common")); | ||
const to = schema => make(AST.getTo(schema.ast)); | ||
/** | ||
* @since 1.0.0 | ||
*/ | ||
exports.to = to; | ||
const reverse = schema => make(AST.reverse(schema.ast)); | ||
/* c8 ignore start */ | ||
exports.to = to; | ||
exports.reverse = reverse; | ||
/* c8 ignore end */ | ||
@@ -364,13 +438,13 @@ // --------------------------------------------- | ||
const schema = make(ast); | ||
const decode = P.decode(schema); | ||
const decodeOption = P.decodeOption(schema); | ||
const decodeEither = P.decodeEither(schema); | ||
const validate = P.validate(schema); | ||
const validateOption = P.validateOption(schema); | ||
const validateEither = P.validateEither(schema); | ||
const is = P.is(schema); | ||
const out = Object.assign(input => decode(input), { | ||
const out = Object.assign(input => validate(input), { | ||
[_Brand.RefinedConstructorsTypeId]: _Brand.RefinedConstructorsTypeId, | ||
ast, | ||
option: input => decodeOption(input), | ||
either: input => E.mapLeft(decodeEither(input), errors => [{ | ||
option: input => validateOption(input), | ||
either: input => E.mapLeft(validateEither(input), e => [{ | ||
meta: input, | ||
message: (0, _TreeFormatter.formatErrors)(errors) | ||
message: (0, _TreeFormatter.formatErrors)(e.errors) | ||
}]), | ||
@@ -397,6 +471,6 @@ refine: input => is(input) | ||
const isOverlappingIndexSignatures = (x, y) => x.indexSignatures.some(ix => y.indexSignatures.some(iy => { | ||
const bx = AST._getParameter(ix.parameter); | ||
const by = AST._getParameter(iy.parameter); | ||
const keyofx = AST._getParameterKeyof(ix.parameter); | ||
const keyofy = AST._getParameterKeyof(iy.parameter); | ||
// there cannot be two string index signatures or two symbol index signatures at the same time | ||
return AST.isStringKeyword(bx) && AST.isStringKeyword(by) || AST.isSymbolKeyword(bx) && AST.isSymbolKeyword(by); | ||
return AST.isStringKeyword(keyofx) && AST.isStringKeyword(keyofy) || AST.isSymbolKeyword(keyofx) && AST.isSymbolKeyword(keyofy); | ||
})); | ||
@@ -464,4 +538,5 @@ const intersectUnionMembers = (xs, ys) => { | ||
function filter(predicate, options) { | ||
return from => { | ||
const ast = AST.createRefinement(from.ast, a => predicate(a) ? PR.success(a) : PR.failure(PR.type(ast, a)), false, toAnnotations(options)); | ||
return self => { | ||
const decode = a => predicate(a) ? PR.success(a) : PR.failure(PR.type(ast, a)); | ||
const ast = AST.createRefinement(self.ast, AST.getTo(self.ast), decode, decode, toAnnotations(options)); | ||
return make(ast); | ||
@@ -477,5 +552,14 @@ }; | ||
*/ | ||
const transformEither = /*#__PURE__*/(0, _Function.dual)(4, (self, to, decode, encode) => make(AST.createTransform(self.ast, to.ast, decode, encode, false))); | ||
const transformEffect = /*#__PURE__*/(0, _Function.dual)(4, (from, to, decode, encode) => make(AST.createTransform(from.ast, to.ast, decode, encode))); | ||
/** | ||
Create a new `Schema` by transforming the input and output of an existing `Schema` | ||
using the provided decoding functions. | ||
@category combinators | ||
@since 1.0.0 | ||
*/ | ||
exports.transformEffect = transformEffect; | ||
const transformEither = /*#__PURE__*/(0, _Function.dual)(4, (from, to, decode, encode) => make(AST.createTransform(from.ast, to.ast, (i, o) => Effect.fromEither(decode(i, o)), (i, o) => Effect.fromEither(encode(i, o))))); | ||
/** | ||
Create a new `Schema` by transforming the input and output of an existing `Schema` | ||
using the provided mapping functions. | ||
@@ -487,3 +571,3 @@ | ||
exports.transformEither = transformEither; | ||
const transform = /*#__PURE__*/(0, _Function.dual)(4, (self, to, ab, ba) => transformEither(self, to, a => PR.success(ab(a)), b => PR.success(ba(b)))); | ||
const transform = /*#__PURE__*/(0, _Function.dual)(4, (from, to, decode, encode) => transformEffect(from, to, a => PR.success(decode(a)), b => PR.success(encode(b)))); | ||
/** | ||
@@ -796,3 +880,3 @@ * Attaches a property signature with the specified key and value to the schema. | ||
exports.nonPositiveBigint = nonPositiveBigint; | ||
const clampBigint = (min, max) => self => transform(self, (0, _Function.pipe)(self, betweenBigint(min, max)), self => B.clamp(self, min, max), self => B.clamp(self, min, max)); | ||
const clampBigint = (min, max) => self => transform(self, (0, _Function.pipe)(self, to, betweenBigint(min, max)), self => B.clamp(self, min, max), _Function.identity); | ||
// --------------------------------------------- | ||
@@ -811,7 +895,10 @@ // data/Brand | ||
exports.BrandTypeId = BrandTypeId; | ||
const fromBrand = (constructor, options) => self => (0, _Function.pipe)(self, filter(x => constructor.refine(x), { | ||
typeId: BrandTypeId, | ||
message: a => constructor.either(a).left.map(v => v.message).join(", "), | ||
...options | ||
})); | ||
const fromBrand = (constructor, options) => self => { | ||
const decode = (0, _Debug.untracedMethod)(() => a => Effect.fromEither(E.mapLeft(constructor.either(a), brandErrors => PR.parseError([PR.type(ast, a, brandErrors.map(v => v.message).join(", "))])))); | ||
const ast = AST.createRefinement(self.ast, AST.getTo(self.ast), decode, decode, toAnnotations({ | ||
typeId: BrandTypeId, | ||
...options | ||
})); | ||
return make(ast); | ||
}; | ||
// --------------------------------------------- | ||
@@ -832,4 +919,4 @@ // data/Chunk | ||
}), item => { | ||
const items = P.decodeEither(array(item)); | ||
return (u, options) => !C.isChunk(u) ? PR.failure(PR.type(schema.ast, u)) : E.map(items(C.toReadonlyArray(u), options), C.fromIterable); | ||
const parse = P.parseEffect(array(item)); | ||
return (u, options) => !C.isChunk(u) ? PR.failure(PR.type(schema.ast, u)) : PR.map(parse(C.toReadonlyArray(u), options), C.fromIterable); | ||
}, { | ||
@@ -847,3 +934,3 @@ [AST.IdentifierAnnotationId]: "Chunk", | ||
exports.chunkFromSelf = chunkFromSelf; | ||
const chunk = item => transform(array(item), chunkFromSelf(item), C.fromIterable, C.toReadonlyArray); | ||
const chunk = item => transform(array(item), to(chunkFromSelf(item)), C.fromIterable, C.toReadonlyArray); | ||
// --------------------------------------------- | ||
@@ -862,4 +949,4 @@ // data/Data | ||
const schema = declare([item], item, item => { | ||
const decode = P.decodeEither(item); | ||
return (u, options) => !Equal.isEqual(u) ? PR.failure(PR.type(schema.ast, u)) : E.map(decode(u, options), toData); | ||
const parse = P.parseEffect(item); | ||
return (u, options) => !Equal.isEqual(u) ? PR.failure(PR.type(schema.ast, u)) : PR.map(parse(u, options), toData); | ||
}, { | ||
@@ -877,3 +964,3 @@ [AST.IdentifierAnnotationId]: "Data", | ||
exports.dataFromSelf = dataFromSelf; | ||
const data = item => transform(item, dataFromSelf(item), toData, a => Array.isArray(a) ? Array.from(a) : Object.assign({}, a)); | ||
const data = item => transform(item, to(dataFromSelf(item)), toData, a => Array.isArray(a) ? Array.from(a) : Object.assign({}, a)); | ||
// --------------------------------------------- | ||
@@ -901,9 +988,6 @@ // data/Date | ||
exports.date = date; | ||
const dateFromString = self => { | ||
const schema = transformEither(self, date, s => { | ||
const n = Date.parse(s); | ||
return isNaN(n) ? PR.failure(PR.type(schema.ast, s)) : PR.success(new Date(n)); | ||
}, n => PR.success(n.toISOString())); | ||
return schema; | ||
}; | ||
const dateFromString = /*#__PURE__*/transformEffect(string, date, s => { | ||
const n = Date.parse(s); | ||
return isNaN(n) ? PR.failure(PR.type(dateFromString.ast, s)) : PR.success(new Date(n)); | ||
}, n => PR.success(n.toISOString())); | ||
// --------------------------------------------- | ||
@@ -928,5 +1012,5 @@ // data/Either | ||
const schema = declare([left, right], eitherInline(left, right), (left, right) => { | ||
const decodeLeft = P.decodeEither(left); | ||
const decodeRight = P.decodeEither(right); | ||
return (u, options) => !E.isEither(u) ? PR.failure(PR.type(schema.ast, u)) : E.isLeft(u) ? E.map(decodeLeft(u.left, options), E.left) : E.map(decodeRight(u.right, options), E.right); | ||
const parseLeft = P.parseEffect(left); | ||
const parseRight = P.parseEffect(right); | ||
return (u, options) => !E.isEither(u) ? PR.failure(PR.type(schema.ast, u)) : E.isLeft(u) ? PR.map(parseLeft(u.left, options), E.left) : PR.map(parseRight(u.right, options), E.right); | ||
}, { | ||
@@ -944,3 +1028,3 @@ [AST.IdentifierAnnotationId]: "Either", | ||
exports.eitherFromSelf = eitherFromSelf; | ||
const either = (left, right) => transform(eitherInline(left, right), eitherFromSelf(left, right), a => a._tag === "Left" ? E.left(a.left) : E.right(a.right), E.match(left => ({ | ||
const either = (left, right) => transform(eitherInline(left, right), to(eitherFromSelf(left, right)), a => a._tag === "Left" ? E.left(a.left) : E.right(a.right), E.match(left => ({ | ||
_tag: "Left", | ||
@@ -1188,3 +1272,3 @@ left | ||
exports.nonPositive = nonPositive; | ||
const clamp = (min, max) => self => transform(self, (0, _Function.pipe)(self, between(min, max)), self => N.clamp(self, min, max), self => N.clamp(self, min, max)); | ||
const clamp = (min, max) => self => transform(self, (0, _Function.pipe)(self, to, between(min, max)), self => N.clamp(self, min, max), _Function.identity); | ||
/** | ||
@@ -1200,3 +1284,3 @@ Transforms a `string` into a `number` by parsing the string using `parseFloat`. | ||
const numberFromString = self => { | ||
const schema = transformEither(self, number, s => { | ||
const schema = transformEffect(self, number, s => { | ||
if (s === "NaN") { | ||
@@ -1258,4 +1342,4 @@ return PR.success(NaN); | ||
const schema = declare([value], optionInline(value), value => { | ||
const decodeValue = P.decodeEither(value); | ||
return (u, options) => !O.isOption(u) ? PR.failure(PR.type(schema.ast, u)) : O.isNone(u) ? PR.success(O.none()) : E.map(decodeValue(u.value, options), O.some); | ||
const parse = P.parseEffect(value); | ||
return (u, options) => !O.isOption(u) ? PR.failure(PR.type(schema.ast, u)) : O.isNone(u) ? PR.success(O.none()) : PR.map(parse(u.value, options), O.some); | ||
}, { | ||
@@ -1273,3 +1357,3 @@ [AST.IdentifierAnnotationId]: "Option", | ||
exports.optionFromSelf = optionFromSelf; | ||
const option = value => transform(optionInline(value), optionFromSelf(value), a => a._tag === "None" ? O.none() : O.some(a.value), O.match(() => ({ | ||
const option = value => transform(optionInline(value), to(optionFromSelf(value)), a => a._tag === "None" ? O.none() : O.some(a.value), O.match(() => ({ | ||
_tag: "None" | ||
@@ -1285,3 +1369,3 @@ }), value => ({ | ||
exports.option = option; | ||
const optionFromNullable = value => transform(union(_undefined, _null, value), optionFromSelf(value), O.fromNullable, O.getOrNull); | ||
const optionFromNullable = value => transform(union(_undefined, _null, value), to(optionFromSelf(value)), O.fromNullable, O.getOrNull); | ||
/** | ||
@@ -1318,3 +1402,3 @@ * @category parsers | ||
return PR.success(out); | ||
}, false); | ||
}); | ||
return make(out); | ||
@@ -1397,4 +1481,4 @@ } | ||
}), (key, value) => { | ||
const items = P.decodeEither(array(tuple(key, value))); | ||
return (u, options) => !isMap(u) ? PR.failure(PR.type(schema.ast, u)) : E.map(items(Array.from(u.entries()), options), as => new Map(as)); | ||
const parse = P.parseEffect(array(tuple(key, value))); | ||
return (u, options) => !isMap(u) ? PR.failure(PR.type(schema.ast, u)) : PR.map(parse(Array.from(u.entries()), options), as => new Map(as)); | ||
}, { | ||
@@ -1412,3 +1496,3 @@ [AST.IdentifierAnnotationId]: "ReadonlyMap", | ||
exports.readonlyMapFromSelf = readonlyMapFromSelf; | ||
const readonlyMap = (key, value) => transform(array(tuple(key, value)), readonlyMapFromSelf(key, value), as => new Map(as), map => Array.from(map.entries())); | ||
const readonlyMap = (key, value) => transform(array(tuple(key, value)), to(readonlyMapFromSelf(key, value)), as => new Map(as), map => Array.from(map.entries())); | ||
// --------------------------------------------- | ||
@@ -1429,4 +1513,4 @@ // data/ReadonlySet | ||
}), item => { | ||
const items = P.decodeEither(array(item)); | ||
return (u, options) => !isSet(u) ? PR.failure(PR.type(schema.ast, u)) : E.map(items(Array.from(u.values()), options), as => new Set(as)); | ||
const parse = P.parseEffect(array(item)); | ||
return (u, options) => !isSet(u) ? PR.failure(PR.type(schema.ast, u)) : PR.map(parse(Array.from(u.values()), options), as => new Set(as)); | ||
}, { | ||
@@ -1444,3 +1528,3 @@ [AST.IdentifierAnnotationId]: "ReadonlySet", | ||
exports.readonlySetFromSelf = readonlySetFromSelf; | ||
const readonlySet = item => transform(array(item), readonlySetFromSelf(item), as => new Set(as), set => Array.from(set)); | ||
const readonlySet = item => transform(array(item), to(readonlySetFromSelf(item)), as => new Set(as), set => Array.from(set)); | ||
// --------------------------------------------- | ||
@@ -1604,3 +1688,3 @@ // data/String | ||
exports.includes = includes; | ||
const trim = self => transform(self, (0, _Function.pipe)(self, trimmed()), s => s.trim(), s => s.trim()); | ||
const trim = self => transform(self, (0, _Function.pipe)(to(self), trimmed()), s => s.trim(), _Function.identity); | ||
/** | ||
@@ -1607,0 +1691,0 @@ * @since 1.0.0 |
@@ -11,2 +11,3 @@ /** | ||
import * as I from "@effect/schema/internal/common" | ||
import { eitherSync } from "@effect/schema/ParseResult" | ||
import type { Schema } from "@effect/schema/Schema" | ||
@@ -205,15 +206,14 @@ import type * as FastCheck from "fast-check" | ||
} | ||
case "Refinement": { | ||
const from = go(ast.from) | ||
case "Refinement": | ||
case "Transform": { | ||
const to = go(ast.to) | ||
return pipe( | ||
getHook(ast), | ||
O.match( | ||
() => (fc) => from(fc).filter((a) => E.isRight(ast.decode(a))), | ||
(handler) => handler(from) | ||
() => (fc) => to(fc).filter((a) => E.isRight(eitherSync(ast.decode(a)))), | ||
(handler) => handler(to) | ||
) | ||
) | ||
} | ||
case "Transform": | ||
return go(ast.to) | ||
} | ||
}) |
300
src/AST.ts
@@ -174,2 +174,17 @@ /** | ||
*/ | ||
export interface HasTransformation { | ||
readonly hasTransformation: boolean | ||
} | ||
/** | ||
* @since 1.0.0 | ||
*/ | ||
export const hasTransformation = (ast: AST): boolean => | ||
isRefinement(ast) || isTransform(ast) || ( | ||
"hasTransformation" in ast && ast.hasTransformation | ||
) | ||
/** | ||
* @since 1.0.0 | ||
*/ | ||
export const getAnnotation = <A>(key: PropertyKey) => | ||
@@ -185,3 +200,3 @@ (annotated: Annotated): O.Option<A> => | ||
*/ | ||
export interface Declaration extends Annotated { | ||
export interface Declaration extends Annotated, HasTransformation { | ||
readonly _tag: "Declaration" | ||
@@ -206,3 +221,10 @@ readonly typeParameters: ReadonlyArray<AST> | ||
annotations: Annotated["annotations"] = {} | ||
): Declaration => ({ _tag: "Declaration", typeParameters, type, decode, annotations }) | ||
): Declaration => ({ | ||
_tag: "Declaration", | ||
typeParameters, | ||
type, | ||
decode, | ||
annotations, | ||
hasTransformation: hasTransformation(type) || typeParameters.some(hasTransformation) | ||
}) | ||
@@ -325,2 +347,8 @@ /** | ||
/** | ||
* @category guards | ||
* @since 1.0.0 | ||
*/ | ||
export const isNeverKeyword = (ast: AST): ast is NeverKeyword => ast._tag === "NeverKeyword" | ||
/** | ||
* @category model | ||
@@ -593,3 +621,3 @@ * @since 1.0.0 | ||
*/ | ||
export interface Tuple extends Annotated { | ||
export interface Tuple extends Annotated, HasTransformation { | ||
readonly _tag: "Tuple" | ||
@@ -615,3 +643,5 @@ readonly elements: ReadonlyArray<Element> | ||
isReadonly, | ||
annotations | ||
annotations, | ||
hasTransformation: elements.some((e) => hasTransformation(e.type)) || | ||
(O.isSome(rest) && rest.value.some(hasTransformation)) | ||
}) | ||
@@ -650,3 +680,3 @@ | ||
export interface IndexSignature { | ||
readonly parameter: StringKeyword | SymbolKeyword | TemplateLiteral | Refinement | ||
readonly parameter: AST | ||
readonly type: AST | ||
@@ -660,6 +690,13 @@ readonly isReadonly: boolean | ||
export const createIndexSignature = ( | ||
parameter: StringKeyword | SymbolKeyword | TemplateLiteral | Refinement, | ||
parameter: AST, | ||
type: AST, | ||
isReadonly: boolean | ||
): IndexSignature => ({ parameter, type, isReadonly }) | ||
): IndexSignature => { | ||
if (isNeverKeyword(_getParameterKeyof(parameter))) { | ||
throw new Error( | ||
"An index signature parameter type must be 'string', 'symbol', a template literal type or a refinement of the previous types" | ||
) | ||
} | ||
return ({ parameter, type, isReadonly }) | ||
} | ||
@@ -670,3 +707,3 @@ /** | ||
*/ | ||
export interface TypeLiteral extends Annotated { | ||
export interface TypeLiteral extends Annotated, HasTransformation { | ||
readonly _tag: "TypeLiteral" | ||
@@ -689,3 +726,5 @@ readonly propertySignatures: ReadonlyArray<PropertySignature> | ||
indexSignatures: sortByCardinalityAsc(indexSignatures), | ||
annotations | ||
annotations, | ||
hasTransformation: propertySignatures.some((p) => hasTransformation(p.type)) || | ||
indexSignatures.some((is) => hasTransformation(is.type)) | ||
}) | ||
@@ -703,3 +742,3 @@ | ||
*/ | ||
export interface Union extends Annotated { | ||
export interface Union extends Annotated, HasTransformation { | ||
readonly _tag: "Union" | ||
@@ -724,3 +763,8 @@ readonly types: readonly [AST, AST, ...Array<AST>] | ||
default: { | ||
return { _tag: "Union", types: sortByWeightDesc(types) as any, annotations } | ||
return { | ||
_tag: "Union", | ||
types: sortByWeightDesc(types) as any, | ||
annotations, | ||
hasTransformation: types.some(hasTransformation) | ||
} | ||
} | ||
@@ -740,3 +784,3 @@ } | ||
*/ | ||
export interface Lazy extends Annotated { | ||
export interface Lazy extends Annotated, HasTransformation { | ||
readonly _tag: "Lazy" | ||
@@ -753,3 +797,8 @@ readonly f: () => AST | ||
annotations: Annotated["annotations"] = {} | ||
): Lazy => ({ _tag: "Lazy", f, annotations }) | ||
): Lazy => ({ | ||
_tag: "Lazy", | ||
f, | ||
annotations, | ||
hasTransformation: true | ||
}) | ||
@@ -769,4 +818,5 @@ /** | ||
readonly from: AST | ||
readonly to: AST | ||
readonly decode: (input: any, options?: ParseOptions) => ParseResult<any> | ||
readonly isReversed: boolean | ||
readonly encode: (input: any, options?: ParseOptions) => ParseResult<any> | ||
} | ||
@@ -780,6 +830,7 @@ | ||
from: AST, | ||
decode: (input: any, options?: ParseOptions) => ParseResult<any>, | ||
isReversed: boolean, | ||
to: AST, | ||
decode: Refinement["decode"], | ||
encode: Refinement["encode"], | ||
annotations: Annotated["annotations"] = {} | ||
): Refinement => ({ _tag: "Refinement", from, decode, isReversed, annotations }) | ||
): Refinement => ({ _tag: "Refinement", from, to, decode, encode, annotations }) | ||
@@ -811,3 +862,2 @@ /** | ||
readonly encode: (input: any, options?: ParseOptions) => ParseResult<any> | ||
readonly isReversed: boolean | ||
} | ||
@@ -824,13 +874,4 @@ | ||
encode: Transform["encode"], | ||
isReversed: boolean, | ||
annotations: Annotated["annotations"] = {} | ||
): Transform => ({ | ||
_tag: "Transform", | ||
from, | ||
to: getTo(to), | ||
decode, | ||
encode, | ||
isReversed, | ||
annotations | ||
}) | ||
): Transform => ({ _tag: "Transform", from, to, decode, encode, annotations }) | ||
@@ -1001,3 +1042,2 @@ /** | ||
case "Refinement": | ||
return partial(ast.from) | ||
case "Transform": | ||
@@ -1038,26 +1078,33 @@ return partial(ast.to) | ||
export const getTo = (ast: AST): AST => { | ||
switch (ast._tag) { | ||
case "Declaration": | ||
return createDeclaration(ast.typeParameters.map(getTo), ast.type, ast.decode, ast.annotations) | ||
case "Tuple": | ||
return createTuple( | ||
ast.elements.map((e) => ({ ...e, type: getTo(e.type) })), | ||
O.map(ast.rest, RA.mapNonEmpty(getTo)), | ||
ast.isReadonly, | ||
ast.annotations | ||
) | ||
case "TypeLiteral": | ||
return createTypeLiteral( | ||
ast.propertySignatures.map((p) => ({ ...p, type: getTo(p.type) })), | ||
ast.indexSignatures.map((is) => ({ ...is, type: getTo(is.type) })), | ||
ast.annotations | ||
) | ||
case "Union": | ||
return createUnion(ast.types.map(getTo), ast.annotations) | ||
case "Lazy": | ||
return createLazy(() => getTo(ast.f()), ast.annotations) | ||
case "Refinement": | ||
return createRefinement(getTo(ast.from), ast.decode, false, ast.annotations) | ||
case "Transform": | ||
return getTo(ast.to) | ||
if (hasTransformation(ast)) { | ||
switch (ast._tag) { | ||
case "Declaration": | ||
return createDeclaration( | ||
ast.typeParameters.map(getTo), | ||
ast.type, | ||
ast.decode, | ||
ast.annotations | ||
) | ||
case "Tuple": | ||
return createTuple( | ||
ast.elements.map((e) => ({ ...e, type: getTo(e.type) })), | ||
O.map(ast.rest, RA.mapNonEmpty(getTo)), | ||
ast.isReadonly, | ||
ast.annotations | ||
) | ||
case "TypeLiteral": | ||
return createTypeLiteral( | ||
ast.propertySignatures.map((p) => ({ ...p, type: getTo(p.type) })), | ||
ast.indexSignatures.map((is) => ({ ...is, type: getTo(is.type) })), | ||
ast.annotations | ||
) | ||
case "Union": | ||
return createUnion(ast.types.map(getTo), ast.annotations) | ||
case "Lazy": | ||
return createLazy(() => getTo(ast.f()), ast.annotations) | ||
case "Refinement": | ||
return createRefinement(ast.to, ast.to, ast.decode, ast.decode, ast.annotations) | ||
case "Transform": | ||
return ast.to | ||
} | ||
} | ||
@@ -1071,30 +1118,32 @@ return ast | ||
export const getFrom = (ast: AST): AST => { | ||
switch (ast._tag) { | ||
case "Declaration": | ||
return createDeclaration( | ||
ast.typeParameters.map(getFrom), | ||
ast.type, | ||
ast.decode, | ||
ast.annotations | ||
) | ||
case "Tuple": | ||
return createTuple( | ||
ast.elements.map((e) => ({ ...e, type: getFrom(e.type) })), | ||
O.map(ast.rest, RA.mapNonEmpty(getFrom)), | ||
ast.isReadonly, | ||
ast.annotations | ||
) | ||
case "TypeLiteral": | ||
return createTypeLiteral( | ||
ast.propertySignatures.map((p) => ({ ...p, type: getFrom(p.type) })), | ||
ast.indexSignatures.map((is) => ({ ...is, type: getFrom(is.type) })), | ||
ast.annotations | ||
) | ||
case "Union": | ||
return createUnion(ast.types.map(getFrom), ast.annotations) | ||
case "Lazy": | ||
return createLazy(() => getFrom(ast.f()), ast.annotations) | ||
case "Refinement": | ||
case "Transform": | ||
return getFrom(ast.from) | ||
if (hasTransformation(ast)) { | ||
switch (ast._tag) { | ||
case "Declaration": | ||
return createDeclaration( | ||
ast.typeParameters.map(getFrom), | ||
ast.type, | ||
ast.decode, | ||
ast.annotations | ||
) | ||
case "Tuple": | ||
return createTuple( | ||
ast.elements.map((e) => ({ ...e, type: getFrom(e.type) })), | ||
O.map(ast.rest, RA.mapNonEmpty(getFrom)), | ||
ast.isReadonly, | ||
ast.annotations | ||
) | ||
case "TypeLiteral": | ||
return createTypeLiteral( | ||
ast.propertySignatures.map((p) => ({ ...p, type: getFrom(p.type) })), | ||
ast.indexSignatures.map((is) => ({ ...is, type: getFrom(is.type) })), | ||
ast.annotations | ||
) | ||
case "Union": | ||
return createUnion(ast.types.map(getFrom), ast.annotations) | ||
case "Lazy": | ||
return createLazy(() => getFrom(ast.f()), ast.annotations) | ||
case "Refinement": | ||
case "Transform": | ||
return getFrom(ast.from) | ||
} | ||
} | ||
@@ -1108,31 +1157,39 @@ return ast | ||
export const reverse = (ast: AST): AST => { | ||
switch (ast._tag) { | ||
case "Declaration": | ||
return createDeclaration( | ||
ast.typeParameters.map(reverse), | ||
ast.type, | ||
ast.decode, | ||
ast.annotations | ||
) | ||
case "Tuple": | ||
return createTuple( | ||
ast.elements.map((e) => ({ ...e, type: reverse(e.type) })), | ||
O.map(ast.rest, RA.mapNonEmpty(reverse)), | ||
ast.isReadonly, | ||
ast.annotations | ||
) | ||
case "TypeLiteral": | ||
return createTypeLiteral( | ||
ast.propertySignatures.map((p) => ({ ...p, type: reverse(p.type) })), | ||
ast.indexSignatures.map((is) => ({ ...is, type: reverse(is.type) })), | ||
ast.annotations | ||
) | ||
case "Union": | ||
return createUnion(ast.types.map(reverse), ast.annotations) | ||
case "Lazy": | ||
return createLazy(() => reverse(ast.f()), ast.annotations) | ||
case "Refinement": | ||
return createRefinement(ast.from, ast.decode, !ast.isReversed, ast.annotations) | ||
case "Transform": | ||
return createTransform(reverse(ast.from), ast.to, ast.decode, ast.encode, !ast.isReversed) | ||
if (hasTransformation(ast)) { | ||
switch (ast._tag) { | ||
case "Declaration": | ||
return createDeclaration( | ||
ast.typeParameters.map(reverse), | ||
ast.type, | ||
ast.decode, | ||
ast.annotations | ||
) | ||
case "Tuple": | ||
return createTuple( | ||
ast.elements.map((e) => ({ ...e, type: reverse(e.type) })), | ||
O.map(ast.rest, RA.mapNonEmpty(reverse)), | ||
ast.isReadonly, | ||
ast.annotations | ||
) | ||
case "TypeLiteral": | ||
return createTypeLiteral( | ||
ast.propertySignatures.map((p) => ({ ...p, type: reverse(p.type) })), | ||
ast.indexSignatures.map((is) => ({ ...is, type: reverse(is.type) })), | ||
ast.annotations | ||
) | ||
case "Union": | ||
return createUnion(ast.types.map(reverse), ast.annotations) | ||
case "Lazy": | ||
return createLazy(() => reverse(ast.f()), ast.annotations) | ||
case "Refinement": | ||
return createRefinement( | ||
reverse(ast.to), | ||
reverse(ast.from), | ||
ast.encode, | ||
ast.decode, | ||
ast.annotations | ||
) | ||
case "Transform": | ||
return createTransform(reverse(ast.to), reverse(ast.from), ast.encode, ast.decode) | ||
} | ||
} | ||
@@ -1171,3 +1228,2 @@ return ast | ||
case "Refinement": | ||
return _getCardinality(ast.from) | ||
case "Transform": | ||
@@ -1198,3 +1254,2 @@ return _getCardinality(ast.to) | ||
case "Refinement": | ||
return _getWeight(ast.from) | ||
case "Transform": | ||
@@ -1249,6 +1304,15 @@ return _getWeight(ast.to) | ||
/** @internal */ | ||
export const _getParameter = ( | ||
x: IndexSignature["parameter"] | ||
): StringKeyword | SymbolKeyword | TemplateLiteral => | ||
isRefinement(x) ? _getParameter(x.from as any) : x | ||
export const _getParameterKeyof = ( | ||
ast: AST | ||
): StringKeyword | SymbolKeyword | TemplateLiteral | NeverKeyword => { | ||
switch (ast._tag) { | ||
case "StringKeyword": | ||
case "SymbolKeyword": | ||
case "TemplateLiteral": | ||
return ast | ||
case "Refinement": | ||
return _getParameterKeyof(ast.from) | ||
} | ||
return neverKeyword | ||
} | ||
@@ -1267,3 +1331,3 @@ const _keyof = (ast: AST): ReadonlyArray<AST> => { | ||
typeof p.name === "symbol" ? createUniqueSymbol(p.name) : createLiteral(p.name) | ||
).concat(ast.indexSignatures.map((is) => _getParameter(is.parameter))) | ||
).concat(ast.indexSignatures.map((is) => _getParameterKeyof(is.parameter))) | ||
case "Union": { | ||
@@ -1277,3 +1341,2 @@ return _getPropertySignatures(ast).map((p): AST => | ||
case "Refinement": | ||
return _keyof(ast.from) | ||
case "Transform": | ||
@@ -1323,3 +1386,2 @@ return _keyof(ast.to) | ||
case "Refinement": | ||
return _getPropertySignatures(ast.from) | ||
case "Transform": | ||
@@ -1326,0 +1388,0 @@ return _getPropertySignatures(ast.to) |
@@ -28,3 +28,3 @@ /** | ||
input: { readonly [x: PropertyKey]: unknown }, | ||
parameter: AST.IndexSignature["parameter"] | ||
parameter: AST.AST | ||
): ReadonlyArray<string> | ReadonlyArray<symbol> => { | ||
@@ -38,4 +38,6 @@ switch (parameter._tag) { | ||
case "Refinement": | ||
return getKeysForIndexSignature(input, parameter.from as any) | ||
case "Transform": | ||
return getKeysForIndexSignature(input, parameter.from) | ||
} | ||
return [] | ||
} | ||
@@ -42,0 +44,0 @@ |
1054
src/Parser.ts
@@ -7,32 +7,45 @@ /** | ||
import { pipe } from "@effect/data/Function" | ||
import type { Option } from "@effect/data/Option" | ||
import * as O from "@effect/data/Option" | ||
import type { Option } from "@effect/data/Option" | ||
import * as P from "@effect/data/Predicate" | ||
import type { NonEmptyReadonlyArray } from "@effect/data/ReadonlyArray" | ||
import * as RA from "@effect/data/ReadonlyArray" | ||
import type { NonEmptyReadonlyArray } from "@effect/data/ReadonlyArray" | ||
import { untraced, untracedMethod } from "@effect/io/Debug" | ||
import * as Effect from "@effect/io/Effect" | ||
import type { ParseOptions } from "@effect/schema/AST" | ||
import * as AST from "@effect/schema/AST" | ||
import type { ParseOptions } from "@effect/schema/AST" | ||
import * as I from "@effect/schema/internal/common" | ||
import type { ParseResult } from "@effect/schema/ParseResult" | ||
import * as PR from "@effect/schema/ParseResult" | ||
import type { ParseResult } from "@effect/schema/ParseResult" | ||
import type { Schema, To } from "@effect/schema/Schema" | ||
import { formatErrors } from "@effect/schema/TreeFormatter" | ||
const parse = (ast: AST.AST) => { | ||
const parse = go(ast) | ||
const get = (ast: AST.AST) => { | ||
const parser = go(ast) | ||
return (input: unknown, options?: ParseOptions) => { | ||
const t = parse(input, options) | ||
if (PR.isFailure(t)) { | ||
throw new Error(formatErrors(t.left)) | ||
const result = parser(input, options) | ||
const resultComputed = PR.eitherSync(result) | ||
if (E.isLeft(resultComputed)) { | ||
throw new Error(formatErrors(resultComputed.left.errors)) | ||
} | ||
return t.right | ||
return resultComputed.right | ||
} | ||
} | ||
const parseOption = (ast: AST.AST) => { | ||
const parse = go(ast) | ||
return (input: unknown, options?: ParseOptions): Option<any> => | ||
O.fromEither(parse(input, options)) | ||
const getOption = (ast: AST.AST) => { | ||
const parser = go(ast) | ||
return (input: unknown, options?: ParseOptions) => | ||
O.fromEither(PR.eitherSync(parser(input, options))) | ||
} | ||
const getEither = (ast: AST.AST) => { | ||
const parser = go(ast) | ||
return (input: unknown, options?: ParseOptions) => PR.eitherSync(parser(input, options)) | ||
} | ||
const getPromise = (ast: AST.AST) => { | ||
const parser = go(ast) | ||
return (input: unknown, options?: ParseOptions) => Effect.runPromise(parser(input, options)) | ||
} | ||
/** | ||
@@ -42,5 +55,50 @@ * @category decoding | ||
*/ | ||
export const decodeEither = <I, A>( | ||
export const parse = <_, A>(schema: Schema<_, A>): (i: unknown, options?: ParseOptions) => A => | ||
get(schema.ast) | ||
/** | ||
* @category decoding | ||
* @since 1.0.0 | ||
*/ | ||
export const parseOption = <_, A>( | ||
schema: Schema<_, A> | ||
): (i: unknown, options?: ParseOptions) => Option<A> => getOption(schema.ast) | ||
/** | ||
* @category decoding | ||
* @since 1.0.0 | ||
*/ | ||
export const parseEither = <_, A>( | ||
schema: Schema<_, A> | ||
): (i: unknown, options?: ParseOptions) => E.Either<PR.ParseError, A> => getEither(schema.ast) | ||
/** | ||
* @category decoding | ||
* @since 1.0.0 | ||
*/ | ||
export const parsePromise = <_, A>( | ||
schema: Schema<_, A> | ||
): (i: unknown, options?: ParseOptions) => Promise<A> => getPromise(schema.ast) | ||
/** | ||
* @category decoding | ||
* @since 1.0.0 | ||
*/ | ||
export const parseEffect = <_, A>( | ||
schema: Schema<_, A> | ||
): (i: unknown, options?: ParseOptions) => PR.ParseResult<A> => go(schema.ast) | ||
/** | ||
* @category decoding | ||
* @since 1.0.0 | ||
*/ | ||
export const decode: <I, A>(schema: Schema<I, A>) => (i: I, options?: ParseOptions) => A = parse | ||
/** | ||
* @category decoding | ||
* @since 1.0.0 | ||
*/ | ||
export const decodeOption: <I, A>( | ||
schema: Schema<I, A> | ||
): (input: unknown, options?: ParseOptions) => ParseResult<A> => go(schema.ast) | ||
) => (i: I, options?: ParseOptions) => Option<A> = parseOption | ||
@@ -51,5 +109,5 @@ /** | ||
*/ | ||
export const decodeOption = <I, A>( | ||
export const decodeEither: <I, A>( | ||
schema: Schema<I, A> | ||
): (input: unknown, options?: ParseOptions) => Option<A> => parseOption(schema.ast) | ||
) => (i: I, options?: ParseOptions) => E.Either<PR.ParseError, A> = parseEither | ||
@@ -60,20 +118,29 @@ /** | ||
*/ | ||
export const decode = <I, A>(schema: Schema<I, A>): (input: unknown, options?: ParseOptions) => A => | ||
parse(schema.ast) | ||
export const decodePromise: <I, A>( | ||
schema: Schema<I, A> | ||
) => (i: I, options?: ParseOptions) => Promise<A> = parsePromise | ||
/** | ||
* @category decoding | ||
* @since 1.0.0 | ||
*/ | ||
export const decodeEffect: <_, A>( | ||
schema: Schema<_, A> | ||
) => (i: unknown, options?: ParseOptions | undefined) => ParseResult<A> = parseEffect | ||
/** | ||
* @category validation | ||
* @since 1.0.0 | ||
*/ | ||
export const is = <I, A>(schema: Schema<I, A>) => | ||
(input: unknown, options?: ParseOptions): input is A => | ||
E.isRight(go(AST.getTo(schema.ast))(input, options)) | ||
export const validate = <_, A>( | ||
schema: Schema<_, A> | ||
): (a: unknown, options?: ParseOptions) => A => get(AST.getTo(schema.ast)) | ||
/** | ||
* @category validation | ||
* @since 1.0.0 | ||
*/ | ||
export type ToAsserts<S extends Schema<any>> = ( | ||
input: unknown, | ||
options?: ParseOptions | ||
) => asserts input is To<S> | ||
export const validateOption = <_, A>( | ||
schema: Schema<_, A> | ||
): (a: unknown, options?: ParseOptions) => Option<A> => getOption(AST.getTo(schema.ast)) | ||
@@ -84,6 +151,6 @@ /** | ||
*/ | ||
export const asserts = <I, A>(schema: Schema<I, A>) => | ||
(input: unknown, options?: ParseOptions): asserts input is A => { | ||
parse(AST.getTo(schema.ast))(input, options) | ||
} | ||
export const validateEither = <_, A>( | ||
schema: Schema<_, A> | ||
): (a: unknown, options?: ParseOptions) => E.Either<PR.ParseError, A> => | ||
getEither(AST.getTo(schema.ast)) | ||
@@ -94,5 +161,5 @@ /** | ||
*/ | ||
export const validateEither = <I, A>( | ||
schema: Schema<I, A> | ||
): (input: unknown, options?: ParseOptions) => ParseResult<A> => go(AST.getTo(schema.ast)) | ||
export const validatePromise = <_, A>( | ||
schema: Schema<_, A> | ||
): (i: unknown, options?: ParseOptions) => Promise<A> => getPromise(AST.getTo(schema.ast)) | ||
@@ -103,5 +170,5 @@ /** | ||
*/ | ||
export const validateOption = <I, A>( | ||
schema: Schema<I, A> | ||
): (input: unknown, options?: ParseOptions) => Option<A> => parseOption(AST.getTo(schema.ast)) | ||
export const validateEffect = <_, A>( | ||
schema: Schema<_, A> | ||
): (a: unknown, options?: ParseOptions) => PR.ParseResult<A> => go(AST.getTo(schema.ast)) | ||
@@ -112,5 +179,40 @@ /** | ||
*/ | ||
export const validate = <I, A>( | ||
export const is = <_, A>(schema: Schema<_, A>) => { | ||
const getEither = validateEither(schema) | ||
return (a: unknown): a is A => E.isRight(getEither(a)) | ||
} | ||
/** | ||
* @since 1.0.0 | ||
*/ | ||
export type ToAsserts<S extends Schema<any>> = ( | ||
input: unknown, | ||
options?: ParseOptions | ||
) => asserts input is To<S> | ||
/** | ||
* @category validation | ||
* @since 1.0.0 | ||
*/ | ||
export const asserts = <_, A>(schema: Schema<_, A>) => { | ||
const get = validate(schema) | ||
return (a: unknown, options?: ParseOptions): asserts a is A => { | ||
get(a, options) | ||
} | ||
} | ||
/** | ||
* @category encoding | ||
* @since 1.0.0 | ||
*/ | ||
export const encode = <I, A>(schema: Schema<I, A>): (a: A, options?: ParseOptions) => I => | ||
get(AST.reverse(schema.ast)) | ||
/** | ||
* @category encoding | ||
* @since 1.0.0 | ||
*/ | ||
export const encodeOption = <I, A>( | ||
schema: Schema<I, A> | ||
): (input: unknown, options?: ParseOptions) => A => parse(AST.getTo(schema.ast)) | ||
): (input: A, options?: ParseOptions) => Option<I> => getOption(AST.reverse(schema.ast)) | ||
@@ -123,3 +225,4 @@ /** | ||
schema: Schema<I, A> | ||
): (a: A, options?: ParseOptions) => ParseResult<I> => go(AST.reverse(schema.ast)) | ||
): (a: A, options?: ParseOptions) => E.Either<PR.ParseError, I> => | ||
getEither(AST.reverse(schema.ast)) | ||
@@ -130,5 +233,5 @@ /** | ||
*/ | ||
export const encodeOption = <I, A>( | ||
export const encodePromise = <I, A>( | ||
schema: Schema<I, A> | ||
): (input: A, options?: ParseOptions) => Option<I> => parseOption(AST.reverse(schema.ast)) | ||
): (a: A, options?: ParseOptions) => Promise<I> => getPromise(AST.reverse(schema.ast)) | ||
@@ -139,131 +242,76 @@ /** | ||
*/ | ||
export const encode = <I, A>(schema: Schema<I, A>): (a: A, options?: ParseOptions) => I => | ||
parse(AST.reverse(schema.ast)) | ||
export const encodeEffect = <I, A>( | ||
schema: Schema<I, A> | ||
) => { | ||
const parser = go(AST.reverse(schema.ast)) | ||
return (a: A, options?: ParseOptions): PR.ParseResult<I> => parser(a, options) | ||
} | ||
interface Parser { | ||
(input: unknown, options?: ParseOptions): ParseResult<any> | ||
interface Parser<I, A> { | ||
(i: I, options?: ParseOptions): ParseResult<A> | ||
} | ||
const go = I.memoize((ast: AST.AST): Parser => { | ||
switch (ast._tag) { | ||
case "Declaration": | ||
return ast.decode(...ast.typeParameters) | ||
case "Literal": | ||
return fromRefinement(ast, (u): u is typeof ast.literal => u === ast.literal) | ||
case "UniqueSymbol": | ||
return fromRefinement(ast, (u): u is typeof ast.symbol => u === ast.symbol) | ||
case "UndefinedKeyword": | ||
return fromRefinement(ast, P.isUndefined) | ||
case "VoidKeyword": | ||
return fromRefinement(ast, P.isUndefined) | ||
case "NeverKeyword": | ||
return fromRefinement(ast, P.isNever) | ||
case "UnknownKeyword": | ||
case "AnyKeyword": | ||
return PR.success | ||
case "StringKeyword": | ||
return fromRefinement(ast, P.isString) | ||
case "NumberKeyword": | ||
return fromRefinement(ast, P.isNumber) | ||
case "BooleanKeyword": | ||
return fromRefinement(ast, P.isBoolean) | ||
case "BigIntKeyword": | ||
return fromRefinement(ast, P.isBigint) | ||
case "SymbolKeyword": | ||
return fromRefinement(ast, P.isSymbol) | ||
case "ObjectKeyword": | ||
return fromRefinement(ast, P.isObject) | ||
case "Enums": | ||
return fromRefinement(ast, (u): u is any => ast.enums.some(([_, value]) => value === u)) | ||
case "TemplateLiteral": { | ||
const regex = getTemplateLiteralRegex(ast) | ||
return fromRefinement(ast, (u): u is any => P.isString(u) && regex.test(u)) | ||
} | ||
case "Tuple": { | ||
const elements = ast.elements.map((e) => go(e.type)) | ||
const rest = pipe(ast.rest, O.map(RA.mapNonEmpty(go))) | ||
return (input: unknown, options) => { | ||
if (!Array.isArray(input)) { | ||
return PR.failure(PR.type(unknownArray, input)) | ||
const go = I.memoize(untracedMethod(() => | ||
(ast: AST.AST): Parser<any, any> => { | ||
switch (ast._tag) { | ||
case "Declaration": | ||
return ast.decode(...ast.typeParameters) | ||
case "Literal": | ||
return fromRefinement(ast, (u): u is typeof ast.literal => u === ast.literal) | ||
case "UniqueSymbol": | ||
return fromRefinement(ast, (u): u is typeof ast.symbol => u === ast.symbol) | ||
case "UndefinedKeyword": | ||
return fromRefinement(ast, P.isUndefined) | ||
case "VoidKeyword": | ||
return fromRefinement(ast, P.isUndefined) | ||
case "NeverKeyword": | ||
return fromRefinement(ast, P.isNever) | ||
case "UnknownKeyword": | ||
case "AnyKeyword": | ||
return PR.success | ||
case "StringKeyword": | ||
return fromRefinement(ast, P.isString) | ||
case "NumberKeyword": | ||
return fromRefinement(ast, P.isNumber) | ||
case "BooleanKeyword": | ||
return fromRefinement(ast, P.isBoolean) | ||
case "BigIntKeyword": | ||
return fromRefinement(ast, P.isBigint) | ||
case "SymbolKeyword": | ||
return fromRefinement(ast, P.isSymbol) | ||
case "ObjectKeyword": | ||
return fromRefinement(ast, P.isObject) | ||
case "Enums": | ||
return fromRefinement(ast, (u): u is any => ast.enums.some(([_, value]) => value === u)) | ||
case "TemplateLiteral": { | ||
const regex = getTemplateLiteralRegex(ast) | ||
return fromRefinement(ast, (u): u is any => P.isString(u) && regex.test(u)) | ||
} | ||
case "Tuple": { | ||
const elements = ast.elements.map((e) => go(e.type)) | ||
const rest = pipe(ast.rest, O.map(RA.mapNonEmpty(go))) | ||
let requiredLen = ast.elements.filter((e) => !e.isOptional).length | ||
if (O.isSome(ast.rest)) { | ||
requiredLen += ast.rest.value.length - 1 | ||
} | ||
const output: Array<any> = [] | ||
const es: Array<PR.ParseError> = [] | ||
const allErrors = options?.allErrors | ||
let i = 0 | ||
// --------------------------------------------- | ||
// handle elements | ||
// --------------------------------------------- | ||
for (; i < elements.length; i++) { | ||
if (input.length < i + 1) { | ||
// the input element is missing... | ||
if (!ast.elements[i].isOptional) { | ||
// ...but the element is required | ||
const e = PR.index(i, [PR.missing]) | ||
if (allErrors) { | ||
es.push(e) | ||
continue | ||
} else { | ||
return PR.failure(e) | ||
} | ||
} | ||
} else { | ||
const parser = elements[i] | ||
const t = parser(input[i], options) | ||
if (PR.isFailure(t)) { | ||
// the input element is present but is not valid | ||
const e = PR.index(i, t.left) | ||
if (allErrors) { | ||
es.push(e) | ||
continue | ||
} else { | ||
return PR.failures(mutableAppend(es, e)) | ||
} | ||
} | ||
output.push(t.right) | ||
return (input: unknown, options) => { | ||
if (!Array.isArray(input)) { | ||
return PR.failure(PR.type(unknownArray, input)) | ||
} | ||
} | ||
// --------------------------------------------- | ||
// handle rest element | ||
// --------------------------------------------- | ||
if (O.isSome(rest)) { | ||
const head = RA.headNonEmpty(rest.value) | ||
const tail = RA.tailNonEmpty(rest.value) | ||
for (; i < input.length - tail.length; i++) { | ||
const t = head(input[i], options) | ||
if (PR.isFailure(t)) { | ||
const e = PR.index(i, t.left) | ||
if (allErrors) { | ||
es.push(e) | ||
continue | ||
} else { | ||
return PR.failures(mutableAppend(es, e)) | ||
} | ||
} else { | ||
output.push(t.right) | ||
} | ||
} | ||
const allErrors = options?.allErrors | ||
const es: Array<[number, PR.ParseErrors]> = [] | ||
let stepKey = 0 | ||
// --------------------------------------------- | ||
// handle post rest elements | ||
// handle missing indexes | ||
// --------------------------------------------- | ||
for (let j = 0; j < tail.length; j++) { | ||
i += j | ||
if (input.length < i + 1) { | ||
// the input element is missing and the element is required, bail out | ||
return PR.failures(mutableAppend(es, PR.index(i, [PR.missing]))) | ||
const len = input.length | ||
for (let i = len; i <= requiredLen - 1; i++) { | ||
const e = PR.index(i, [PR.missing]) | ||
if (allErrors) { | ||
es.push([stepKey++, e]) | ||
continue | ||
} else { | ||
const t = tail[j](input[i], options) | ||
if (PR.isFailure(t)) { | ||
// the input element is present but is not valid | ||
const e = PR.index(i, t.left) | ||
if (allErrors) { | ||
es.push(e) | ||
continue | ||
} else { | ||
return PR.failures(mutableAppend(es, e)) | ||
} | ||
} | ||
output.push(t.right) | ||
return PR.failure(e) | ||
} | ||
} | ||
} else { | ||
// --------------------------------------------- | ||
@@ -273,116 +321,224 @@ // handle unexpected indexes | ||
const isUnexpectedAllowed = options?.isUnexpectedAllowed | ||
for (; i < input.length; i++) { | ||
const e = PR.index(i, [PR.unexpected(input[i])]) | ||
if (!isUnexpectedAllowed) { | ||
if (!isUnexpectedAllowed && O.isNone(ast.rest)) { | ||
for (let i = ast.elements.length; i <= len - 1; i++) { | ||
const e = PR.index(i, [PR.unexpected(input[i])]) | ||
if (allErrors) { | ||
es.push(e) | ||
es.push([stepKey++, e]) | ||
continue | ||
} else { | ||
return PR.failures(mutableAppend(es, e)) | ||
return PR.failures(mutableAppend(sortByIndex(es), e)) | ||
} | ||
} | ||
} | ||
} | ||
// --------------------------------------------- | ||
// compute output | ||
// --------------------------------------------- | ||
return RA.isNonEmptyReadonlyArray(es) ? | ||
PR.failures(es) : | ||
PR.success(output) | ||
} | ||
} | ||
case "TypeLiteral": { | ||
if (ast.propertySignatures.length === 0 && ast.indexSignatures.length === 0) { | ||
return fromRefinement(ast, P.isNotNullable) | ||
} | ||
const propertySignaturesTypes = ast.propertySignatures.map((f) => go(f.type)) | ||
const indexSignatures = ast.indexSignatures.map((is) => | ||
[go(is.parameter), go(is.type)] as const | ||
) | ||
return (input: unknown, options) => { | ||
if (!P.isRecord(input)) { | ||
return PR.failure(PR.type(unknownRecord, input)) | ||
} | ||
const output: any = {} | ||
const expectedKeys: any = {} | ||
const es: Array<PR.ParseError> = [] | ||
const allErrors = options?.allErrors | ||
// --------------------------------------------- | ||
// handle property signatures | ||
// --------------------------------------------- | ||
for (let i = 0; i < propertySignaturesTypes.length; i++) { | ||
const ps = ast.propertySignatures[i] | ||
const parser = propertySignaturesTypes[i] | ||
const name = ps.name | ||
expectedKeys[name] = null | ||
if (!Object.prototype.hasOwnProperty.call(input, name)) { | ||
if (!ps.isOptional) { | ||
const e = PR.key(name, [PR.missing]) | ||
if (allErrors) { | ||
es.push(e) | ||
const output: Array<[number, any]> = [] | ||
let i = 0 | ||
type State = { | ||
es: typeof es | ||
output: typeof output | ||
} | ||
const queue: Array<(_: State) => PR.ParseResult<void>> = [] | ||
// --------------------------------------------- | ||
// handle elements | ||
// --------------------------------------------- | ||
for (; i < elements.length; i++) { | ||
if (len < i + 1) { | ||
// the input element is missing... | ||
if (ast.elements[i].isOptional) { | ||
continue | ||
} | ||
} else { | ||
const parser = elements[i] | ||
const te = parser(input[i], options) | ||
const t = PR.either(te) | ||
if (t) { | ||
if (E.isLeft(t)) { | ||
// the input element is present but is not valid | ||
const e = PR.index(i, t.left.errors) | ||
if (allErrors) { | ||
es.push([stepKey++, e]) | ||
continue | ||
} else { | ||
return PR.failures(mutableAppend(sortByIndex(es), e)) | ||
} | ||
} | ||
output.push([stepKey++, t.right]) | ||
} else { | ||
return PR.failure(e) | ||
const nk = stepKey++ | ||
const index = i | ||
queue.push( | ||
untracedMethod(() => | ||
({ es, output }: State) => | ||
Effect.flatMap(Effect.either(te), (t) => { | ||
if (E.isLeft(t)) { | ||
// the input element is present but is not valid | ||
const e = PR.index(index, t.left.errors) | ||
if (allErrors) { | ||
es.push([nk, e]) | ||
return Effect.unit() | ||
} else { | ||
return PR.failures(mutableAppend(sortByIndex(es), e)) | ||
} | ||
} | ||
output.push([nk, t.right]) | ||
return Effect.unit() | ||
}) | ||
) | ||
) | ||
} | ||
} | ||
} else { | ||
const t = parser(input[name], options) | ||
if (PR.isFailure(t)) { | ||
// the input key is present but is not valid | ||
const e = PR.key(name, t.left) | ||
if (allErrors) { | ||
es.push(e) | ||
} | ||
// --------------------------------------------- | ||
// handle rest element | ||
// --------------------------------------------- | ||
if (O.isSome(rest)) { | ||
const head = RA.headNonEmpty(rest.value) | ||
const tail = RA.tailNonEmpty(rest.value) | ||
for (; i < len - tail.length; i++) { | ||
const te = head(input[i], options) | ||
const t = PR.either(te) | ||
if (t) { | ||
if (E.isLeft(t)) { | ||
const e = PR.index(i, t.left.errors) | ||
if (allErrors) { | ||
es.push([stepKey++, e]) | ||
continue | ||
} else { | ||
return PR.failures(mutableAppend(sortByIndex(es), e)) | ||
} | ||
} else { | ||
output.push([stepKey++, t.right]) | ||
} | ||
} else { | ||
const nk = stepKey++ | ||
const index = i | ||
queue.push( | ||
untracedMethod(() => | ||
({ es, output }: State) => | ||
Effect.flatMap(Effect.either(te), (t) => { | ||
if (E.isLeft(t)) { | ||
const e = PR.index(index, t.left.errors) | ||
if (allErrors) { | ||
es.push([nk, e]) | ||
return Effect.unit() | ||
} else { | ||
return PR.failures(mutableAppend(sortByIndex(es), e)) | ||
} | ||
} else { | ||
output.push([nk, t.right]) | ||
return Effect.unit() | ||
} | ||
}) | ||
) | ||
) | ||
} | ||
} | ||
// --------------------------------------------- | ||
// handle post rest elements | ||
// --------------------------------------------- | ||
for (let j = 0; j < tail.length; j++) { | ||
i += j | ||
if (len < i + 1) { | ||
continue | ||
} else { | ||
return PR.failures(mutableAppend(es, e)) | ||
const te = tail[j](input[i], options) | ||
const t = PR.either(te) | ||
if (t) { | ||
if (E.isLeft(t)) { | ||
// the input element is present but is not valid | ||
const e = PR.index(i, t.left.errors) | ||
if (allErrors) { | ||
es.push([stepKey++, e]) | ||
continue | ||
} else { | ||
return PR.failures(mutableAppend(sortByIndex(es), e)) | ||
} | ||
} | ||
output.push([stepKey++, t.right]) | ||
} else { | ||
const nk = stepKey++ | ||
const index = i | ||
queue.push( | ||
untracedMethod(() => | ||
({ es, output }: State) => | ||
Effect.flatMap(Effect.either(te), (t) => { | ||
if (E.isLeft(t)) { | ||
// the input element is present but is not valid | ||
const e = PR.index(index, t.left.errors) | ||
if (allErrors) { | ||
es.push([nk, e]) | ||
return Effect.unit() | ||
} else { | ||
return PR.failures(mutableAppend(sortByIndex(es), e)) | ||
} | ||
} | ||
output.push([nk, t.right]) | ||
return Effect.unit() | ||
}) | ||
) | ||
) | ||
} | ||
} | ||
} | ||
output[name] = t.right | ||
} | ||
} | ||
// --------------------------------------------- | ||
// handle index signatures | ||
// --------------------------------------------- | ||
if (indexSignatures.length > 0) { | ||
for (let i = 0; i < indexSignatures.length; i++) { | ||
const parameter = indexSignatures[i][0] | ||
const type = indexSignatures[i][1] | ||
const keys = I.getKeysForIndexSignature(input, ast.indexSignatures[i].parameter) | ||
for (const key of keys) { | ||
if (Object.prototype.hasOwnProperty.call(expectedKeys, key)) { | ||
continue | ||
} | ||
// --------------------------------------------- | ||
// handle keys | ||
// --------------------------------------------- | ||
let t = parameter(key, options) | ||
if (PR.isFailure(t)) { | ||
const e = PR.key(key, t.left) | ||
if (allErrors) { | ||
es.push(e) | ||
continue | ||
} else { | ||
return PR.failures(mutableAppend(es, e)) | ||
// --------------------------------------------- | ||
// compute output | ||
// --------------------------------------------- | ||
const computeResult = ({ es, output }: State) => | ||
RA.isNonEmptyArray(es) ? | ||
PR.failures(sortByIndex(es)) : | ||
PR.success(sortByIndex(output)) | ||
return queue.length > 0 ? | ||
untraced(() => | ||
Effect.suspend(() => { | ||
const state: State = { | ||
es: Array.from(es), | ||
output: Array.from(output) | ||
} | ||
} | ||
// --------------------------------------------- | ||
// handle values | ||
// --------------------------------------------- | ||
t = type(input[key], options) | ||
if (PR.isFailure(t)) { | ||
const e = PR.key(key, t.left) | ||
return Effect.flatMap( | ||
Effect.forEachDiscard(queue, (f) => f(state)), | ||
() => computeResult(state) | ||
) | ||
}) | ||
) : | ||
computeResult({ output, es }) | ||
} | ||
} | ||
case "TypeLiteral": { | ||
if (ast.propertySignatures.length === 0 && ast.indexSignatures.length === 0) { | ||
return fromRefinement(ast, P.isNotNullable) | ||
} | ||
const propertySignaturesTypes = ast.propertySignatures.map((f) => go(f.type)) | ||
const indexSignatures = ast.indexSignatures.map((is) => | ||
[go(is.parameter), go(is.type)] as const | ||
) | ||
return (input: unknown, options) => { | ||
if (!P.isRecord(input)) { | ||
return PR.failure(PR.type(unknownRecord, input)) | ||
} | ||
const allErrors = options?.allErrors | ||
const expectedKeys: any = {} | ||
const es: Array<[number, PR.ParseErrors]> = [] | ||
let stepKey = 0 | ||
// --------------------------------------------- | ||
// handle missing keys | ||
// --------------------------------------------- | ||
for (let i = 0; i < propertySignaturesTypes.length; i++) { | ||
const ps = ast.propertySignatures[i] | ||
const name = ps.name | ||
expectedKeys[name] = null | ||
if (!Object.prototype.hasOwnProperty.call(input, name)) { | ||
if (!ps.isOptional) { | ||
const e = PR.key(name, [PR.missing]) | ||
if (allErrors) { | ||
es.push(e) | ||
es.push([stepKey++, e]) | ||
continue | ||
} else { | ||
return PR.failures(mutableAppend(es, e)) | ||
return PR.failure(e) | ||
} | ||
} else { | ||
output[key] = t.right | ||
} | ||
} | ||
} | ||
} else { | ||
// --------------------------------------------- | ||
@@ -392,11 +548,11 @@ // handle unexpected keys | ||
const isUnexpectedAllowed = options?.isUnexpectedAllowed | ||
for (const key of Reflect.ownKeys(input)) { | ||
if (!(Object.prototype.hasOwnProperty.call(expectedKeys, key))) { | ||
const e = PR.key(key, [PR.unexpected(input[key])]) | ||
if (!isUnexpectedAllowed) { | ||
if (!isUnexpectedAllowed && indexSignatures.length === 0) { | ||
for (const key of Reflect.ownKeys(input)) { | ||
if (!(Object.prototype.hasOwnProperty.call(expectedKeys, key))) { | ||
const e = PR.key(key, [PR.unexpected(input[key])]) | ||
if (allErrors) { | ||
es.push(e) | ||
es.push([stepKey++, e]) | ||
continue | ||
} else { | ||
return PR.failures(mutableAppend(es, e)) | ||
return PR.failures(mutableAppend(sortByIndex(es), e)) | ||
} | ||
@@ -406,117 +562,297 @@ } | ||
} | ||
} | ||
// --------------------------------------------- | ||
// compute output | ||
// --------------------------------------------- | ||
return RA.isNonEmptyReadonlyArray(es) ? | ||
PR.failures(es) : | ||
PR.success(output) | ||
} | ||
} | ||
case "Union": { | ||
const searchTree = _getSearchTree(ast.types) | ||
const ownKeys = Reflect.ownKeys(searchTree.keys) | ||
const len = ownKeys.length | ||
const otherwise = searchTree.otherwise | ||
const map = new Map() | ||
for (let i = 0; i < ast.types.length; i++) { | ||
map.set(ast.types[i], go(ast.types[i])) | ||
} | ||
return (input, options) => { | ||
const es: Array<PR.ParseError> = [] | ||
// --------------------------------------------- | ||
// handle property signatures | ||
// --------------------------------------------- | ||
const output: any = {} | ||
type State = { | ||
es: typeof es | ||
output: typeof output | ||
} | ||
const queue: Array<(state: State) => PR.ParseResult<void>> = [] | ||
if (len > 0) { | ||
// if there is at least one key then input must be an object | ||
if (P.isRecord(input)) { | ||
for (let i = 0; i < len; i++) { | ||
const name = ownKeys[i] | ||
const buckets = searchTree.keys[name].buckets | ||
// for each property that should contain a literal, check if the input contains that property | ||
if (Object.prototype.hasOwnProperty.call(input, name)) { | ||
const literal = String(input[name]) | ||
// check that the value obtained from the input for the property corresponds to an existing bucket | ||
if (Object.prototype.hasOwnProperty.call(buckets, literal)) { | ||
// retrive the minimal set of candidates for decoding | ||
const bucket = buckets[literal] | ||
for (let i = 0; i < bucket.length; i++) { | ||
const t = map.get(bucket[i])(input, options) | ||
if (PR.isSuccess(t)) { | ||
return t | ||
for (let i = 0; i < propertySignaturesTypes.length; i++) { | ||
const ps = ast.propertySignatures[i] | ||
const parser = propertySignaturesTypes[i] | ||
const name = ps.name | ||
if (Object.prototype.hasOwnProperty.call(input, name)) { | ||
const te = parser(input[name], options) | ||
const t = PR.either(te) | ||
if (t) { | ||
if (E.isLeft(t)) { | ||
// the input key is present but is not valid | ||
const e = PR.key(name, t.left.errors) | ||
if (allErrors) { | ||
es.push([stepKey++, e]) | ||
continue | ||
} else { | ||
return PR.failures(mutableAppend(sortByIndex(es), e)) | ||
} | ||
} | ||
output[name] = t.right | ||
} else { | ||
const nk = stepKey++ | ||
const index = name | ||
queue.push( | ||
untracedMethod(() => | ||
({ es, output }: State) => | ||
Effect.flatMap(Effect.either(te), (t) => { | ||
if (E.isLeft(t)) { | ||
// the input key is present but is not valid | ||
const e = PR.key(index, t.left.errors) | ||
if (allErrors) { | ||
es.push([nk, e]) | ||
return Effect.unit() | ||
} else { | ||
return PR.failures(mutableAppend(sortByIndex(es), e)) | ||
} | ||
} | ||
output[index] = t.right | ||
return Effect.unit() | ||
}) | ||
) | ||
) | ||
} | ||
} | ||
} | ||
// --------------------------------------------- | ||
// handle index signatures | ||
// --------------------------------------------- | ||
if (indexSignatures.length > 0) { | ||
for (let i = 0; i < indexSignatures.length; i++) { | ||
const parameter = indexSignatures[i][0] | ||
const type = indexSignatures[i][1] | ||
const keys = I.getKeysForIndexSignature(input, ast.indexSignatures[i].parameter) | ||
for (const key of keys) { | ||
if (Object.prototype.hasOwnProperty.call(expectedKeys, key)) { | ||
continue | ||
} | ||
const te = parameter(key, options) | ||
// --------------------------------------------- | ||
// handle keys | ||
// --------------------------------------------- | ||
const t = PR.either(te) | ||
if (t) { | ||
if (E.isLeft(t)) { | ||
const e = PR.key(key, t.left.errors) | ||
if (allErrors) { | ||
es.push([stepKey++, e]) | ||
continue | ||
} else { | ||
es.push(PR.unionMember(t.left)) | ||
return PR.failures(mutableAppend(sortByIndex(es), e)) | ||
} | ||
} | ||
} | ||
// there's no else here because index signature parameters can't have transformations | ||
// --------------------------------------------- | ||
// handle values | ||
// --------------------------------------------- | ||
const tve = type(input[key], options) | ||
const tv = PR.either(tve) | ||
if (tv) { | ||
if (E.isLeft(tv)) { | ||
const e = PR.key(key, tv.left.errors) | ||
if (allErrors) { | ||
es.push([stepKey++, e]) | ||
continue | ||
} else { | ||
return PR.failures(mutableAppend(sortByIndex(es), e)) | ||
} | ||
} else { | ||
output[key] = tv.right | ||
} | ||
} else { | ||
es.push( | ||
PR.key(name, [ | ||
PR.type( | ||
const nk = stepKey++ | ||
const index = key | ||
queue.push( | ||
untracedMethod(() => | ||
({ es, output }: State) => | ||
Effect.flatMap( | ||
Effect.either(tve), | ||
(tv) => { | ||
if (E.isLeft(tv)) { | ||
const e = PR.key(index, tv.left.errors) | ||
if (allErrors) { | ||
es.push([nk, e]) | ||
return Effect.unit() | ||
} else { | ||
return PR.failures(mutableAppend(sortByIndex(es), e)) | ||
} | ||
} else { | ||
output[key] = tv.right | ||
return Effect.unit() | ||
} | ||
} | ||
) | ||
) | ||
) | ||
} | ||
} | ||
} | ||
} | ||
// --------------------------------------------- | ||
// compute output | ||
// --------------------------------------------- | ||
const computeResult = ({ es, output }: State) => | ||
RA.isNonEmptyArray(es) ? | ||
PR.failures(sortByIndex(es)) : | ||
PR.success(output) | ||
return queue.length > 0 ? | ||
untraced(() => | ||
Effect.suspend(() => { | ||
const state: State = { | ||
es: Array.from(es), | ||
output: Object.assign({}, output) | ||
} | ||
return Effect.flatMap( | ||
Effect.forEachDiscard(queue, (f) => f(state)), | ||
() => computeResult(state) | ||
) | ||
}) | ||
) : | ||
computeResult({ es, output }) | ||
} | ||
} | ||
case "Union": { | ||
const searchTree = _getSearchTree(ast.types) | ||
const ownKeys = Reflect.ownKeys(searchTree.keys) | ||
const len = ownKeys.length | ||
const map = new Map<any, Parser<any, any>>() | ||
for (let i = 0; i < ast.types.length; i++) { | ||
map.set(ast.types[i], go(ast.types[i])) | ||
} | ||
return (input, options) => { | ||
const es: Array<[number, PR.ParseErrors]> = [] | ||
let stepKey = 0 | ||
let candidates: Array<AST.AST> = [] | ||
if (len > 0) { | ||
// if there is at least one key then input must be an object | ||
if (P.isRecord(input)) { | ||
for (let i = 0; i < len; i++) { | ||
const name = ownKeys[i] | ||
const buckets = searchTree.keys[name].buckets | ||
// for each property that should contain a literal, check if the input contains that property | ||
if (Object.prototype.hasOwnProperty.call(input, name)) { | ||
const literal = String(input[name]) | ||
// check that the value obtained from the input for the property corresponds to an existing bucket | ||
if (Object.prototype.hasOwnProperty.call(buckets, literal)) { | ||
// retrive the minimal set of candidates for decoding | ||
candidates = candidates.concat(buckets[literal]) | ||
} else { | ||
es.push([ | ||
stepKey++, | ||
PR.key(name, [PR.type( | ||
searchTree.keys[name].ast, | ||
input[name] | ||
) | ||
)]) | ||
]) | ||
) | ||
} | ||
} else { | ||
es.push([stepKey++, PR.key(name, [PR.missing])]) | ||
} | ||
} | ||
} else { | ||
es.push([stepKey++, PR.type(unknownRecord, input)]) | ||
} | ||
} | ||
if (searchTree.otherwise.length > 0) { | ||
candidates = candidates.concat(searchTree.otherwise) | ||
} | ||
const queue: Array<(state: State) => PR.ParseResult<unknown>> = [] | ||
const finalResult: { ref: any } = { | ||
ref: undefined | ||
} | ||
type State = { | ||
finalResult: typeof finalResult | ||
es: typeof es | ||
} | ||
for (let i = 0; i < candidates.length; i++) { | ||
const te = map.get(candidates[i])!(input, options) | ||
// the members of a union are ordered based on which one should be decoded first, | ||
// therefore if one member has added a task, all subsequent members must | ||
// also add a task to the queue even if they are synchronous | ||
const t = queue.length === 0 ? PR.either(te) : undefined | ||
if (t) { | ||
if (E.isRight(t)) { | ||
return PR.success(t.right) | ||
} else { | ||
es.push(PR.key(name, [PR.missing])) | ||
es.push([stepKey++, PR.unionMember(t.left.errors)]) | ||
} | ||
} else { | ||
const nk = stepKey++ | ||
queue.push( | ||
untracedMethod(() => | ||
({ es, finalResult }) => | ||
Effect.suspend(() => { | ||
if (finalResult.ref) { | ||
return Effect.unit() | ||
} else { | ||
return Effect.flatMap(Effect.either(te), (t) => { | ||
if (E.isRight(t)) { | ||
finalResult.ref = PR.success(t.right) | ||
} else { | ||
es.push([nk, PR.unionMember(t.left.errors)]) | ||
} | ||
return Effect.unit() | ||
}) | ||
} | ||
}) | ||
) | ||
) | ||
} | ||
} else { | ||
es.push(PR.type(unknownRecord, input)) | ||
} | ||
// --------------------------------------------- | ||
// compute output | ||
// --------------------------------------------- | ||
const computeResult = ({ es }: State) => | ||
RA.isNonEmptyArray(es) ? | ||
PR.failures(sortByIndex(es)) : | ||
// this should never happen | ||
PR.failure(PR.type(AST.neverKeyword, input)) | ||
return queue.length > 0 ? | ||
untraced(() => | ||
Effect.suspend(() => { | ||
const state: State = { | ||
es: Array.from(es), | ||
finalResult: { ref: finalResult.ref } | ||
} | ||
return Effect.flatMap( | ||
Effect.forEachDiscard(queue, (f) => f(state)), | ||
() => { | ||
if (state.finalResult.ref) { | ||
return state.finalResult.ref | ||
} | ||
return computeResult(state) | ||
} | ||
) | ||
}) | ||
) : | ||
computeResult({ es, finalResult }) | ||
} | ||
// if none of the schemas with at least one property with a literal value succeeded, | ||
// proceed with those that have no literal at all | ||
for (let i = 0; i < otherwise.length; i++) { | ||
const t = map.get(otherwise[i])(input, options) | ||
if (PR.isSuccess(t)) { | ||
return t | ||
} else { | ||
es.push(PR.unionMember(t.left)) | ||
} | ||
} | ||
// --------------------------------------------- | ||
// compute output | ||
// --------------------------------------------- | ||
return RA.isNonEmptyReadonlyArray(es) ? | ||
PR.failures(es) : | ||
PR.failure(PR.type(AST.neverKeyword, input)) | ||
} | ||
} | ||
case "Lazy": { | ||
const f = () => go(ast.f()) | ||
const get = I.memoize<typeof f, Parser>(f) | ||
return (a, options) => get(f)(a, options) | ||
} | ||
case "Refinement": { | ||
const from = go(ast.from) | ||
if (ast.isReversed) { | ||
const to = go(AST.getTo(ast.from)) | ||
return (a, options) => | ||
pipe( | ||
to(a, options), // validate input | ||
E.flatMap((a) => ast.decode(a, options)), // refine | ||
E.flatMap((a) => from(a, options)) // encode | ||
) | ||
case "Lazy": { | ||
const f = () => go(ast.f()) | ||
const get = I.memoize<typeof f, Parser<any, any>>(f) | ||
return (a, options) => get(f)(a, options) | ||
} | ||
return (u, options) => E.flatMap(from(u, options), (a) => ast.decode(a, options)) | ||
} | ||
case "Transform": { | ||
const from = go(ast.from) | ||
if (ast.isReversed) { | ||
case "Refinement": | ||
case "Transform": { | ||
const from = go(ast.from) | ||
const to = go(ast.to) | ||
return (a, options) => | ||
pipe( | ||
to(a, options), // validate input | ||
E.flatMap((a) => ast.encode(a, options)), // transform | ||
E.flatMap((a) => from(a, options)) // encode | ||
return (i1, options) => | ||
PR.flatMap( | ||
from(i1, options), | ||
(a) => PR.flatMap(ast.decode(a, options), (i2) => to(i2, options)) | ||
) | ||
} | ||
return (u, options) => E.flatMap(from(u, options), (a) => ast.decode(a, options)) | ||
} | ||
} | ||
}) | ||
)) | ||
const fromRefinement = <A>(ast: AST.AST, refinement: (u: unknown) => u is A): Parser => | ||
const fromRefinement = <A>(ast: AST.AST, refinement: (u: unknown) => u is A): Parser<unknown, A> => | ||
(u) => refinement(u) ? PR.success(u) : PR.failure(PR.type(ast, u)) | ||
@@ -535,4 +871,5 @@ | ||
const propertySignature = ast.propertySignatures[i] | ||
if (AST.isLiteral(propertySignature.type) && !propertySignature.isOptional) { | ||
out.push([propertySignature.name, propertySignature.type]) | ||
const type = AST.getFrom(propertySignature.type) | ||
if (AST.isLiteral(type) && !propertySignature.isOptional) { | ||
out.push([propertySignature.name, type]) | ||
} | ||
@@ -543,5 +880,4 @@ } | ||
case "Refinement": | ||
case "Transform": | ||
return _getLiterals(ast.from) | ||
case "Transform": | ||
return ast.isReversed ? _getLiterals(ast.to) : _getLiterals(AST.getFrom(ast.from)) | ||
} | ||
@@ -611,3 +947,5 @@ return [] | ||
const unknownArray = AST.createTuple([], O.some([AST.unknownKeyword]), true) | ||
const unknownArray = AST.createTuple([], O.some([AST.unknownKeyword]), true, { | ||
[AST.DescriptionAnnotationId]: "a generic array" | ||
}) | ||
@@ -617,3 +955,5 @@ const unknownRecord = AST.createTypeLiteral([], [ | ||
AST.createIndexSignature(AST.symbolKeyword, AST.unknownKeyword, true) | ||
]) | ||
], { | ||
[AST.DescriptionAnnotationId]: "a generic object" | ||
}) | ||
@@ -638,1 +978,7 @@ const mutableAppend = <A>(self: Array<A>, a: A): NonEmptyReadonlyArray<A> => { | ||
} | ||
function sortByIndex<T>(es: RA.NonEmptyArray<[number, T]>): RA.NonEmptyArray<T> | ||
function sortByIndex<T>(es: Array<[number, T]>): Array<T> | ||
function sortByIndex(es: Array<[number, any]>): any { | ||
return es.sort(([a], [b]) => a > b ? 1 : a < b ? -1 : 0).map(([_, a]) => a) | ||
} |
@@ -5,5 +5,10 @@ /** | ||
import type { Either, Left, Right } from "@effect/data/Either" | ||
import * as E from "@effect/data/Either" | ||
import * as O from "@effect/data/Option" | ||
import type { NonEmptyReadonlyArray } from "@effect/data/ReadonlyArray" | ||
import { failureOption } from "@effect/io/Cause" | ||
import * as Debug from "@effect/io/Debug" | ||
import * as Effect from "@effect/io/Effect" | ||
import * as Exit from "@effect/io/Exit" | ||
import { isExit } from "@effect/io/Exit" | ||
import type * as AST from "@effect/schema/AST" | ||
@@ -14,6 +19,22 @@ | ||
*/ | ||
export type ParseResult<A> = Either<NonEmptyReadonlyArray<ParseError>, A> | ||
export interface ParseResult<A> extends Effect.Effect<never, ParseError, A> {} | ||
/** | ||
* `ParseError` is a type that represents the different types of errors that can occur when decoding a value. | ||
* @since 1.0.0 | ||
*/ | ||
export interface ParseError { | ||
readonly _tag: "ParseError" | ||
readonly errors: NonEmptyReadonlyArray<ParseErrors> | ||
} | ||
/** | ||
* @since 1.0.0 | ||
*/ | ||
export const parseError = (errors: NonEmptyReadonlyArray<ParseErrors>): ParseError => ({ | ||
_tag: "ParseError", | ||
errors | ||
}) | ||
/** | ||
* `ParseErrors` is a type that represents the different types of errors that can occur when decoding a value. | ||
* | ||
@@ -23,3 +44,3 @@ * @category model | ||
*/ | ||
export type ParseError = | ||
export type ParseErrors = | ||
| Type | ||
@@ -46,2 +67,3 @@ | Index | ||
readonly actual: unknown | ||
readonly message: O.Option<string> | ||
} | ||
@@ -53,6 +75,7 @@ | ||
*/ | ||
export const type = (expected: AST.AST, actual: unknown): Type => ({ | ||
export const type = (expected: AST.AST, actual: unknown, message?: string): Type => ({ | ||
_tag: "Type", | ||
expected, | ||
actual | ||
actual, | ||
message: O.fromNullable(message) | ||
}) | ||
@@ -72,3 +95,3 @@ | ||
readonly index: number | ||
readonly errors: NonEmptyReadonlyArray<ParseError> | ||
readonly errors: NonEmptyReadonlyArray<ParseErrors> | ||
} | ||
@@ -82,3 +105,3 @@ | ||
index: number, | ||
errors: NonEmptyReadonlyArray<ParseError> | ||
errors: NonEmptyReadonlyArray<ParseErrors> | ||
): Index => ({ | ||
@@ -103,3 +126,3 @@ _tag: "Index", | ||
readonly key: PropertyKey | ||
readonly errors: NonEmptyReadonlyArray<ParseError> | ||
readonly errors: NonEmptyReadonlyArray<ParseErrors> | ||
} | ||
@@ -113,3 +136,3 @@ | ||
key: PropertyKey, | ||
errors: NonEmptyReadonlyArray<ParseError> | ||
errors: NonEmptyReadonlyArray<ParseErrors> | ||
): Key => ({ | ||
@@ -167,3 +190,3 @@ _tag: "Key", | ||
readonly _tag: "UnionMember" | ||
readonly errors: NonEmptyReadonlyArray<ParseError> | ||
readonly errors: NonEmptyReadonlyArray<ParseErrors> | ||
} | ||
@@ -176,3 +199,3 @@ | ||
export const unionMember = ( | ||
errors: NonEmptyReadonlyArray<ParseError> | ||
errors: NonEmptyReadonlyArray<ParseErrors> | ||
): UnionMember => ({ | ||
@@ -187,3 +210,3 @@ _tag: "UnionMember", | ||
*/ | ||
export const success: <A>(a: A) => ParseResult<A> = E.right | ||
export const success: <A>(a: A) => ParseResult<A> = (a) => Exit.succeed(a) | ||
@@ -194,3 +217,3 @@ /** | ||
*/ | ||
export const failure = (e: ParseError): ParseResult<never> => E.left([e]) | ||
export const failure = (e: ParseErrors): ParseResult<never> => Exit.fail(parseError([e])) | ||
@@ -202,17 +225,88 @@ /** | ||
export const failures = ( | ||
es: NonEmptyReadonlyArray<ParseError> | ||
): ParseResult<never> => E.left(es) | ||
es: NonEmptyReadonlyArray<ParseErrors> | ||
): ParseResult<never> => Exit.fail(parseError(es)) | ||
const untrace = <E, A>(self: Effect.Effect<never, E, A>): Effect.Effect<never, E, A> => { | ||
// TODO: find a way to detect Traced | ||
const s: any = self | ||
return s["_tag"] === "Traced" ? s["i0"] : s | ||
} | ||
/** | ||
* @category guards | ||
* @category optimisation | ||
* @since 1.0.0 | ||
*/ | ||
export const isSuccess: <A>(self: ParseResult<A>) => self is Right<A> = E.isRight | ||
export const either = <E, A>(self: Effect.Effect<never, E, A>): E.Either<E, A> | undefined => { | ||
const untraced = untrace(self) | ||
if (isExit(untraced)) { | ||
if (untraced._tag === "Success") { | ||
return E.right(untraced.value as A) | ||
} else { | ||
const failure = failureOption(untraced.cause) | ||
if (failure._tag === "Some") { | ||
return E.left(failure.value as E) | ||
} | ||
} | ||
} | ||
} | ||
/** | ||
* @category guards | ||
* @category optimisation | ||
* @since 1.0.0 | ||
*/ | ||
export const isFailure: <A>( | ||
self: ParseResult<A> | ||
) => self is Left<NonEmptyReadonlyArray<ParseError>> = E.isLeft | ||
export const eitherSync = <E, A>(self: Effect.Effect<never, E, A>): E.Either<E, A> => { | ||
const untraced = untrace(self) | ||
if (isExit(untraced)) { | ||
if (untraced._tag === "Success") { | ||
return E.right(untraced.value as A) | ||
} else { | ||
const failure = failureOption(untraced.cause) | ||
if (failure._tag === "Some") { | ||
return E.left(failure.value as E) | ||
} | ||
} | ||
} | ||
return Debug.untraced(() => Effect.runSyncEither(self)) | ||
} | ||
/** | ||
* @category optimisation | ||
* @since 1.0.0 | ||
*/ | ||
export const flatMap = Debug.methodWithTrace((trace, restore) => | ||
<E, E1, A, B>( | ||
self: Effect.Effect<never, E, A>, | ||
f: (self: A) => Effect.Effect<never, E1, B> | ||
): Effect.Effect<never, E | E1, B> => { | ||
const e = either(self) | ||
if (e) { | ||
if (E.isRight(e)) { | ||
return restore(f)(e.right) | ||
} else { | ||
return Exit.fail(e.left) | ||
} | ||
} | ||
return Effect.flatMap(self, restore(f)).traced(trace) | ||
} | ||
) | ||
/** | ||
* @category optimisation | ||
* @since 1.0.0 | ||
*/ | ||
export const map = Debug.methodWithTrace((trace, restore) => | ||
<E, A, B>( | ||
self: Effect.Effect<never, E, A>, | ||
f: (self: A) => B | ||
): Effect.Effect<never, E, B> => { | ||
const e = either(self) | ||
if (e) { | ||
if (E.isRight(e)) { | ||
return Exit.succeed(restore(f)(e.right)) | ||
} else { | ||
return Exit.fail(e.left) | ||
} | ||
} | ||
return Effect.map(self, restore(f)).traced(trace) | ||
} | ||
) |
@@ -11,15 +11,17 @@ /** | ||
import * as D from "@effect/data/Data" | ||
import type { Either } from "@effect/data/Either" | ||
import * as E from "@effect/data/Either" | ||
import type { Either } from "@effect/data/Either" | ||
import * as Equal from "@effect/data/Equal" | ||
import { dual, pipe } from "@effect/data/Function" | ||
import { dual, identity, pipe } from "@effect/data/Function" | ||
import * as N from "@effect/data/Number" | ||
import type { Option } from "@effect/data/Option" | ||
import * as O from "@effect/data/Option" | ||
import type { Predicate, Refinement } from "@effect/data/Predicate" | ||
import { isDate } from "@effect/data/Predicate" | ||
import type { Predicate, Refinement } from "@effect/data/Predicate" | ||
import * as RA from "@effect/data/ReadonlyArray" | ||
import { untracedMethod } from "@effect/io/Debug" | ||
import * as Effect from "@effect/io/Effect" | ||
import type { Arbitrary } from "@effect/schema/Arbitrary" | ||
import type { ParseOptions } from "@effect/schema/AST" | ||
import * as AST from "@effect/schema/AST" | ||
import type { ParseOptions } from "@effect/schema/AST" | ||
import * as I from "@effect/schema/internal/common" | ||
@@ -62,2 +64,7 @@ import * as P from "@effect/schema/Parser" | ||
/** | ||
* @since 1.0.0 | ||
*/ | ||
export const reverse = <I, A>(schema: Schema<I, A>): Schema<A, I> => make(AST.reverse(schema.ast)) | ||
/* c8 ignore start */ | ||
@@ -76,2 +83,6 @@ export { | ||
*/ | ||
decodeEffect, | ||
/** | ||
* @since 1.0.0 | ||
*/ | ||
decodeEither, | ||
@@ -85,2 +96,6 @@ /** | ||
*/ | ||
decodePromise, | ||
/** | ||
* @since 1.0.0 | ||
*/ | ||
encode, | ||
@@ -90,2 +105,6 @@ /** | ||
*/ | ||
encodeEffect, | ||
/** | ||
* @since 1.0.0 | ||
*/ | ||
encodeEither, | ||
@@ -99,2 +118,6 @@ /** | ||
*/ | ||
encodePromise, | ||
/** | ||
* @since 1.0.0 | ||
*/ | ||
is, | ||
@@ -104,2 +127,22 @@ /** | ||
*/ | ||
parse, | ||
/** | ||
* @since 1.0.0 | ||
*/ | ||
parseEffect, | ||
/** | ||
* @since 1.0.0 | ||
*/ | ||
parseEither, | ||
/** | ||
* @since 1.0.0 | ||
*/ | ||
parseOption, | ||
/** | ||
* @since 1.0.0 | ||
*/ | ||
parsePromise, | ||
/** | ||
* @since 1.0.0 | ||
*/ | ||
validate, | ||
@@ -109,2 +152,6 @@ /** | ||
*/ | ||
validateEffect, | ||
/** | ||
* @since 1.0.0 | ||
*/ | ||
validateEither, | ||
@@ -114,3 +161,7 @@ /** | ||
*/ | ||
validateOption | ||
validateOption, | ||
/** | ||
* @since 1.0.0 | ||
*/ | ||
validatePromise | ||
} from "@effect/schema/Parser" | ||
@@ -525,14 +576,14 @@ | ||
const schema = make(ast) | ||
const decode = P.decode(schema) | ||
const decodeOption = P.decodeOption(schema) | ||
const decodeEither = P.decodeEither(schema) | ||
const validate = P.validate(schema) | ||
const validateOption = P.validateOption(schema) | ||
const validateEither = P.validateEither(schema) | ||
const is = P.is(schema) | ||
const out: any = Object.assign((input: unknown) => decode(input), { | ||
const out: any = Object.assign((input: unknown) => validate(input), { | ||
[RefinedConstructorsTypeId]: RefinedConstructorsTypeId, | ||
ast, | ||
option: (input: unknown) => decodeOption(input), | ||
option: (input: unknown) => validateOption(input), | ||
either: (input: unknown) => | ||
E.mapLeft( | ||
decodeEither(input), | ||
(errors) => [{ meta: input, message: formatErrors(errors) }] | ||
validateEither(input), | ||
(e) => [{ meta: input, message: formatErrors(e.errors) }] | ||
), | ||
@@ -570,7 +621,7 @@ refine: (input: unknown): input is A & Brand<B> => is(input) | ||
y.indexSignatures.some((iy) => { | ||
const bx = AST._getParameter(ix.parameter) | ||
const by = AST._getParameter(iy.parameter) | ||
const keyofx = AST._getParameterKeyof(ix.parameter) | ||
const keyofy = AST._getParameterKeyof(iy.parameter) | ||
// there cannot be two string index signatures or two symbol index signatures at the same time | ||
return (AST.isStringKeyword(bx) && AST.isStringKeyword(by)) || | ||
(AST.isSymbolKeyword(bx) && AST.isSymbolKeyword(by)) | ||
return (AST.isStringKeyword(keyofx) && AST.isStringKeyword(keyofy)) || | ||
(AST.isSymbolKeyword(keyofx) && AST.isSymbolKeyword(keyofy)) | ||
}) | ||
@@ -690,7 +741,9 @@ ) | ||
): <I>(self: Schema<I, A>) => Schema<I, A> { | ||
return (from) => { | ||
return (self) => { | ||
const decode = (a: A) => predicate(a) ? PR.success(a) : PR.failure(PR.type(ast, a)) | ||
const ast = AST.createRefinement( | ||
from.ast, | ||
(a: A) => predicate(a) ? PR.success(a) : PR.failure(PR.type(ast, a)), | ||
false, | ||
self.ast, | ||
AST.getTo(self.ast), | ||
decode, | ||
decode, | ||
toAnnotations(options) | ||
@@ -709,20 +762,66 @@ ) | ||
*/ | ||
export const transformEffect: { | ||
<I2, A2, A1>( | ||
to: Schema<I2, A2>, | ||
decode: (a1: A1, options?: ParseOptions) => ParseResult<I2>, | ||
encode: (i2: I2, options?: ParseOptions) => ParseResult<A1> | ||
): <I1>(self: Schema<I1, A1>) => Schema<I1, A2> | ||
<I1, A1, I2, A2>( | ||
from: Schema<I1, A1>, | ||
to: Schema<I2, A2>, | ||
decode: (a1: A1, options?: ParseOptions) => ParseResult<I2>, | ||
encode: (i2: I2, options?: ParseOptions) => ParseResult<A1> | ||
): Schema<I1, A2> | ||
} = dual(4, <I1, A1, I2, A2>( | ||
from: Schema<I1, A1>, | ||
to: Schema<I2, A2>, | ||
decode: (a1: A1, options?: ParseOptions) => ParseResult<I2>, | ||
encode: (i2: I2, options?: ParseOptions) => ParseResult<A1> | ||
): Schema<I1, A2> => make(AST.createTransform(from.ast, to.ast, decode, encode))) | ||
/** | ||
Create a new `Schema` by transforming the input and output of an existing `Schema` | ||
using the provided decoding functions. | ||
@category combinators | ||
@since 1.0.0 | ||
*/ | ||
export const transformEither: { | ||
<B, A>( | ||
to: Schema<any, B>, | ||
decode: (input: A, options?: ParseOptions) => ParseResult<B>, | ||
encode: (input: B, options?: ParseOptions) => ParseResult<A> | ||
): <I>(self: Schema<I, A>) => Schema<I, B> | ||
<I, A, B>( | ||
self: Schema<I, A>, | ||
to: Schema<any, B>, | ||
decode: (input: A, options?: ParseOptions) => ParseResult<B>, | ||
encode: (input: B, options?: ParseOptions) => ParseResult<A> | ||
): Schema<I, B> | ||
} = dual(4, <I, A, B>( | ||
self: Schema<I, A>, | ||
to: Schema<any, B>, | ||
decode: (input: A, options?: ParseOptions) => ParseResult<B>, | ||
encode: (input: B, options?: ParseOptions) => ParseResult<A> | ||
): Schema<I, B> => make(AST.createTransform(self.ast, to.ast, decode, encode, false))) | ||
<I2, A2, A1>( | ||
to: Schema<I2, A2>, | ||
decode: ( | ||
a1: A1, | ||
options?: ParseOptions | ||
) => E.Either<PR.ParseError, I2>, | ||
encode: ( | ||
i2: I2, | ||
options?: ParseOptions | ||
) => E.Either<PR.ParseError, A1> | ||
): <I1>(self: Schema<I1, A1>) => Schema<I1, A2> | ||
<I1, A1, I2, A2>( | ||
from: Schema<I1, A1>, | ||
to: Schema<I2, A2>, | ||
decode: ( | ||
a1: A1, | ||
options?: ParseOptions | ||
) => E.Either<PR.ParseError, I2>, | ||
encode: ( | ||
i2: I2, | ||
options?: ParseOptions | ||
) => E.Either<PR.ParseError, A1> | ||
): Schema<I1, A2> | ||
} = dual(4, <I1, A1, I2, A2>( | ||
from: Schema<I1, A1>, | ||
to: Schema<I2, A2>, | ||
decode: ( | ||
a1: A1, | ||
options?: ParseOptions | ||
) => E.Either<PR.ParseError, I2>, | ||
encode: (i2: I2, options?: ParseOptions) => E.Either<PR.ParseError, A1> | ||
): Schema<I1, A2> => | ||
make( | ||
AST.createTransform(from.ast, to.ast, (i, o) => | ||
Effect.fromEither(decode(i, o)), (i, o) => | ||
Effect.fromEither(encode(i, o))) | ||
)) | ||
@@ -737,16 +836,22 @@ /** | ||
export const transform: { | ||
<B, A>( | ||
to: Schema<any, B>, | ||
ab: (a: A) => B, | ||
ba: (b: B) => A | ||
): <I>(self: Schema<I, A>) => Schema<I, B> | ||
<I, A, B>(self: Schema<I, A>, to: Schema<any, B>, ab: (a: A) => B, ba: (b: B) => A): Schema<I, B> | ||
<I2, A2, A1>( | ||
to: Schema<I2, A2>, | ||
decode: (a1: A1) => I2, | ||
encode: (i2: I2) => A1 | ||
): <I1>(self: Schema<I1, A1>) => Schema<I1, A2> | ||
<I1, A1, I2, A2>( | ||
from: Schema<I1, A1>, | ||
to: Schema<I2, A2>, | ||
decode: (a1: A1) => I2, | ||
encode: (i2: I2) => A1 | ||
): Schema<I1, A2> | ||
} = dual( | ||
4, | ||
<I, A, B>( | ||
self: Schema<I, A>, | ||
to: Schema<any, B>, | ||
ab: (a: A) => B, | ||
ba: (b: B) => A | ||
): Schema<I, B> => transformEither(self, to, (a) => PR.success(ab(a)), (b) => PR.success(ba(b))) | ||
<I1, A1, I2, A2>( | ||
from: Schema<I1, A1>, | ||
to: Schema<I2, A2>, | ||
decode: (a1: A1) => I2, | ||
encode: (i2: I2) => A1 | ||
): Schema<I1, A2> => | ||
transformEffect(from, to, (a) => PR.success(decode(a)), (b) => PR.success(encode(b))) | ||
) | ||
@@ -787,3 +892,3 @@ | ||
<I, A extends object>(schema: Schema<I, A>): Schema<I, Spread<A & { readonly [k in K]: V }>> => | ||
transform<I, A, any>( | ||
transform<I, A, any, any>( | ||
schema, | ||
@@ -1143,5 +1248,5 @@ pipe(to(schema), extend(struct({ [key]: literal(value) }))), | ||
self, | ||
pipe(self, betweenBigint(min, max)), | ||
pipe(self, to, betweenBigint(min, max)), | ||
(self) => B.clamp(self, min, max) as A, | ||
(self) => B.clamp(self, min, max) as A | ||
identity | ||
) | ||
@@ -1166,16 +1271,22 @@ | ||
) => | ||
<I, A extends Brand.Unbranded<C>>(self: Schema<I, A>): Schema<I, A & C> => | ||
pipe( | ||
self, | ||
filter<A, A & C>( | ||
(x): x is A & C => constructor.refine(x), | ||
{ | ||
typeId: BrandTypeId, | ||
message: (a) => | ||
(constructor.either(a) as E.Left<Brand.BrandErrors>).left.map((v) => v.message) | ||
.join(", "), | ||
...options | ||
} | ||
) | ||
<I, A extends Brand.Unbranded<C>>(self: Schema<I, A>): Schema<I, A & C> => { | ||
const decode = untracedMethod(() => | ||
(a: A): ParseResult<C> => | ||
Effect.fromEither( | ||
E.mapLeft( | ||
constructor.either(a), | ||
(brandErrors) => | ||
PR.parseError([PR.type(ast, a, brandErrors.map((v) => v.message).join(", "))]) | ||
) | ||
) | ||
) | ||
const ast = AST.createRefinement( | ||
self.ast, | ||
AST.getTo(self.ast), | ||
decode, | ||
decode, | ||
toAnnotations({ typeId: BrandTypeId, ...options }) | ||
) | ||
return make(ast) | ||
} | ||
@@ -1204,7 +1315,7 @@ // --------------------------------------------- | ||
<A>(item: Schema<A>) => { | ||
const items = P.decodeEither(array(item)) | ||
const parse = P.parseEffect(array(item)) | ||
return (u, options) => | ||
!C.isChunk(u) ? | ||
PR.failure(PR.type(schema.ast, u)) : | ||
E.map(items(C.toReadonlyArray(u), options), C.fromIterable) | ||
PR.map(parse(C.toReadonlyArray(u), options), C.fromIterable) | ||
}, | ||
@@ -1225,3 +1336,3 @@ { | ||
export const chunk = <I, A>(item: Schema<I, A>): Schema<ReadonlyArray<I>, Chunk<A>> => | ||
transform(array(item), chunkFromSelf(item), C.fromIterable, C.toReadonlyArray) | ||
transform(array(item), to(chunkFromSelf(item)), C.fromIterable, C.toReadonlyArray) | ||
@@ -1259,7 +1370,7 @@ // --------------------------------------------- | ||
) => { | ||
const decode = P.decodeEither(item) | ||
const parse = P.parseEffect(item) | ||
return (u, options) => | ||
!Equal.isEqual(u) ? | ||
PR.failure(PR.type(schema.ast, u)) : | ||
E.map(decode(u, options), toData) | ||
PR.map(parse(u, options), toData) | ||
}, | ||
@@ -1287,3 +1398,3 @@ { | ||
item, | ||
dataFromSelf(item), | ||
to(dataFromSelf(item)), | ||
toData, | ||
@@ -1322,16 +1433,13 @@ (a) => Array.isArray(a) ? Array.from(a) : Object.assign({}, a) as any | ||
*/ | ||
export const dateFromString = <I>(self: Schema<I, string>): Schema<I, Date> => { | ||
const schema: Schema<I, Date> = transformEither( | ||
self, | ||
date, | ||
(s) => { | ||
const n = Date.parse(s) | ||
return isNaN(n) | ||
? PR.failure(PR.type(schema.ast, s)) | ||
: PR.success(new Date(n)) | ||
}, | ||
(n) => PR.success(n.toISOString()) | ||
) | ||
return schema | ||
} | ||
export const dateFromString: Schema<string, Date> = transformEffect( | ||
string, | ||
date, | ||
(s) => { | ||
const n = Date.parse(s) | ||
return isNaN(n) | ||
? PR.failure(PR.type(dateFromString.ast, s)) | ||
: PR.success(new Date(n)) | ||
}, | ||
(n) => PR.success(n.toISOString()) | ||
) | ||
@@ -1380,4 +1488,4 @@ // --------------------------------------------- | ||
) => { | ||
const decodeLeft = P.decodeEither(left) | ||
const decodeRight = P.decodeEither(right) | ||
const parseLeft = P.parseEffect(left) | ||
const parseRight = P.parseEffect(right) | ||
return (u, options) => | ||
@@ -1387,4 +1495,4 @@ !E.isEither(u) ? | ||
E.isLeft(u) ? | ||
E.map(decodeLeft(u.left, options), E.left) : | ||
E.map(decodeRight(u.right, options), E.right) | ||
PR.map(parseLeft(u.left, options), E.left) : | ||
PR.map(parseRight(u.right, options), E.right) | ||
}, | ||
@@ -1413,3 +1521,3 @@ { | ||
eitherInline(left, right), | ||
eitherFromSelf(left, right), | ||
to(eitherFromSelf(left, right)), | ||
(a) => a._tag === "Left" ? E.left(a.left) : E.right(a.right), | ||
@@ -1754,5 +1862,5 @@ E.match( | ||
self, | ||
pipe(self, between<A>(min, max)), | ||
pipe(self, to, between<A>(min, max)), | ||
(self) => N.clamp(self, min, max) as A, | ||
(self) => N.clamp(self, min, max) as A | ||
identity | ||
) | ||
@@ -1769,3 +1877,3 @@ | ||
export const numberFromString = <I>(self: Schema<I, string>): Schema<I, number> => { | ||
const schema: Schema<I, number> = transformEither( | ||
const schema: Schema<I, number> = transformEffect( | ||
self, | ||
@@ -1857,3 +1965,3 @@ number, | ||
<A>(value: Schema<A>) => { | ||
const decodeValue = P.decodeEither(value) | ||
const parse = P.parseEffect(value) | ||
return (u, options) => | ||
@@ -1864,3 +1972,3 @@ !O.isOption(u) ? | ||
PR.success(O.none()) : | ||
E.map(decodeValue(u.value, options), O.some) | ||
PR.map(parse(u.value, options), O.some) | ||
}, | ||
@@ -1885,3 +1993,3 @@ { | ||
optionInline(value), | ||
optionFromSelf(value), | ||
to(optionFromSelf(value)), | ||
(a) => a._tag === "None" ? O.none() : O.some(a.value), | ||
@@ -1898,3 +2006,3 @@ O.match(() => ({ _tag: "None" as const }), (value) => ({ _tag: "Some" as const, value })) | ||
): Schema<I | null | undefined, Option<A>> => | ||
transform(union(_undefined, _null, value), optionFromSelf(value), O.fromNullable, O.getOrNull) | ||
transform(union(_undefined, _null, value), to(optionFromSelf(value)), O.fromNullable, O.getOrNull) | ||
@@ -1959,3 +2067,3 @@ /** | ||
return PR.success(out) | ||
}, false) | ||
}) | ||
return make(out) | ||
@@ -2082,7 +2190,7 @@ } | ||
) => { | ||
const items = P.decodeEither(array(tuple(key, value))) | ||
const parse = P.parseEffect(array(tuple(key, value))) | ||
return (u, options) => | ||
!isMap(u) ? | ||
PR.failure(PR.type(schema.ast, u)) : | ||
E.map(items(Array.from(u.entries()), options), (as) => new Map(as)) | ||
PR.map(parse(Array.from(u.entries()), options), (as) => new Map(as)) | ||
}, | ||
@@ -2108,3 +2216,3 @@ { | ||
array(tuple(key, value)), | ||
readonlyMapFromSelf(key, value), | ||
to(readonlyMapFromSelf(key, value)), | ||
(as) => new Map(as), | ||
@@ -2139,7 +2247,7 @@ (map) => Array.from(map.entries()) | ||
<A>(item: Schema<A>) => { | ||
const items = P.decodeEither(array(item)) | ||
const parse = P.parseEffect(array(item)) | ||
return (u, options) => | ||
!isSet(u) ? | ||
PR.failure(PR.type(schema.ast, u)) : | ||
E.map(items(Array.from(u.values()), options), (as) => new Set(as)) | ||
PR.map(parse(Array.from(u.values()), options), (as) => new Set(as)) | ||
}, | ||
@@ -2162,3 +2270,3 @@ { | ||
array(item), | ||
readonlySetFromSelf(item), | ||
to(readonlySetFromSelf(item)), | ||
(as) => new Set(as), | ||
@@ -2368,5 +2476,5 @@ (set) => Array.from(set) | ||
self, | ||
pipe(self, trimmed()), | ||
pipe(to(self), trimmed()), | ||
(s) => s.trim(), | ||
(s) => s.trim() | ||
identity | ||
) | ||
@@ -2373,0 +2481,0 @@ |
@@ -9,3 +9,3 @@ /** | ||
import * as AST from "@effect/schema/AST" | ||
import type * as PR from "@effect/schema/ParseResult" | ||
import type { ParseErrors } from "@effect/schema/ParseResult" | ||
@@ -15,4 +15,4 @@ interface Forest<A> extends ReadonlyArray<Tree<A>> {} | ||
interface Tree<A> { | ||
value: A | ||
forest: Forest<A> | ||
readonly value: A | ||
readonly forest: Forest<A> | ||
} | ||
@@ -28,3 +28,3 @@ | ||
*/ | ||
export const formatErrors = (errors: NonEmptyReadonlyArray<PR.ParseError>): string => | ||
export const formatErrors = (errors: NonEmptyReadonlyArray<ParseErrors>): string => | ||
drawTree(make(`error(s) found`, errors.map(go))) | ||
@@ -123,4 +123,2 @@ | ||
return ast.types.map(formatExpected).join(" or ") | ||
case "Refinement": | ||
return pipe(getExpected(ast), O.getOrElse(() => "<anonymous refinement schema>")) | ||
case "TemplateLiteral": | ||
@@ -147,8 +145,12 @@ return pipe(getExpected(ast), O.getOrElse(() => formatTemplateLiteral(ast))) | ||
) | ||
case "Refinement": | ||
case "Transform": | ||
return `a parsable value from ${formatExpected(ast.from)} to ${formatExpected(ast.to)}` | ||
return pipe( | ||
getExpected(ast), | ||
O.getOrElse(() => `${formatExpected(ast.from)} -> ${formatExpected(ast.to)}`) | ||
) | ||
} | ||
} | ||
const go = (e: PR.ParseError): Tree<string> => { | ||
const go = (e: ParseErrors): Tree<string> => { | ||
switch (e._tag) { | ||
@@ -160,2 +162,3 @@ case "Type": | ||
O.map((f) => f(e.actual)), | ||
O.orElse(() => e.message), | ||
O.getOrElse(() => | ||
@@ -162,0 +165,0 @@ `Expected ${formatExpected(e.expected)}, actual ${formatActual(e.actual)}` |
/** | ||
* @since 1.0.0 | ||
*/ | ||
import type * as PR from "@effect/schema/ParseResult"; | ||
import type { ParseErrors } from "@effect/schema/ParseResult"; | ||
/** | ||
* @since 1.0.0 | ||
*/ | ||
export declare const formatErrors: (errors: readonly [PR.ParseError, ...PR.ParseError[]]) => string; | ||
export declare const formatErrors: (errors: readonly [ParseErrors, ...ParseErrors[]]) => string; | ||
//# sourceMappingURL=TreeFormatter.d.ts.map |
@@ -97,4 +97,2 @@ "use strict"; | ||
return ast.types.map(formatExpected).join(" or "); | ||
case "Refinement": | ||
return (0, _Function.pipe)(getExpected(ast), O.getOrElse(() => "<anonymous refinement schema>")); | ||
case "TemplateLiteral": | ||
@@ -112,4 +110,5 @@ return (0, _Function.pipe)(getExpected(ast), O.getOrElse(() => formatTemplateLiteral(ast))); | ||
return (0, _Function.pipe)(getExpected(ast), O.getOrElse(() => "<anonymous declaration schema>")); | ||
case "Refinement": | ||
case "Transform": | ||
return `a parsable value from ${formatExpected(ast.from)} to ${formatExpected(ast.to)}`; | ||
return (0, _Function.pipe)(getExpected(ast), O.getOrElse(() => `${formatExpected(ast.from)} -> ${formatExpected(ast.to)}`)); | ||
} | ||
@@ -121,3 +120,3 @@ }; | ||
case "Type": | ||
return make((0, _Function.pipe)(getMessage(e.expected), O.map(f => f(e.actual)), O.getOrElse(() => `Expected ${formatExpected(e.expected)}, actual ${formatActual(e.actual)}`))); | ||
return make((0, _Function.pipe)(getMessage(e.expected), O.map(f => f(e.actual)), O.orElse(() => e.message), O.getOrElse(() => `Expected ${formatExpected(e.expected)}, actual ${formatActual(e.actual)}`))); | ||
case "Index": | ||
@@ -124,0 +123,0 @@ { |
@@ -31,3 +31,2 @@ { | ||
"forceConsistentCasingInFileNames": true, | ||
"suppressImplicitAnyIndexErrors": true, | ||
"stripInternal": true, | ||
@@ -34,0 +33,0 @@ "noImplicitAny": true, |
@@ -7,5 +7,8 @@ { | ||
"tsBuildInfoFile": "build/tsbuildinfo/esm.tsbuildinfo", | ||
"rootDir": "src" | ||
"rootDir": "src", | ||
"target": "ES2022" | ||
}, | ||
"include": ["src/**/*.ts"] | ||
"include": [ | ||
"src/**/*.ts" | ||
] | ||
} |
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 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 not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
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
819791
15621
4
224
3
1443
+ Added@effect/io@^0.10.0
+ Added@effect/data@0.5.1(transitive)
+ Added@effect/io@0.10.0(transitive)
- Removed@effect/data@0.4.1(transitive)
Updated@effect/data@~0.5.0
Updatedfast-check@^3.7.1