/** * @since 0.67.0 */ import * as array_ from "effect/Array" import * as bigDecimal_ from "effect/BigDecimal" import * as bigInt_ from "effect/BigInt" import * as boolean_ from "effect/Boolean" import type { Brand } from "effect/Brand" import * as cause_ from "effect/Cause" import * as chunk_ from "effect/Chunk" import * as config_ from "effect/Config" import * as configError_ from "effect/ConfigError" import * as data_ from "effect/Data" import * as dateTime from "effect/DateTime" import * as duration_ from "effect/Duration" import * as Effect from "effect/Effect" import * as either_ from "effect/Either" import * as Encoding from "effect/Encoding" import * as Equal from "effect/Equal" import * as Equivalence from "effect/Equivalence" import * as exit_ from "effect/Exit" import * as fiberId_ from "effect/FiberId" import type { LazyArg } from "effect/Function" import { dual, identity } from "effect/Function" import * as hashMap_ from "effect/HashMap" import * as hashSet_ from "effect/HashSet" import * as list_ from "effect/List" import * as number_ from "effect/Number" import * as option_ from "effect/Option" import type * as Order from "effect/Order" import type { Pipeable } from "effect/Pipeable" import { pipeArguments } from "effect/Pipeable" import * as Predicate from "effect/Predicate" import * as record_ from "effect/Record" import * as redacted_ from "effect/Redacted" import * as Request from "effect/Request" import * as sortedSet_ from "effect/SortedSet" import * as string_ from "effect/String" import * as struct_ from "effect/Struct" import type * as Types from "effect/Types" import type { GenerationContext, LazyArbitrary } from "./Arbitrary.js" import * as arbitrary_ from "./Arbitrary.js" import type { ParseOptions } from "./AST.js" import * as AST from "./AST.js" import * as equivalence_ from "./Equivalence.js" import * as fastCheck_ from "./FastCheck.js" import * as errors_ from "./internal/errors.js" import * as filters_ from "./internal/filters.js" import * as serializable_ from "./internal/serializable.js" import * as util_ from "./internal/util.js" import * as ParseResult from "./ParseResult.js" import * as pretty_ from "./Pretty.js" import type * as Serializable from "./Serializable.js" import * as TreeFormatter from "./TreeFormatter.js" /** * @since 0.68.2 */ export type Simplify = { [K in keyof A]: A[K] } & {} /** * @since 0.67.0 */ export type SimplifyMutable = { -readonly [K in keyof A]: A[K] } extends infer B ? B : never /** * @since 0.67.0 * @category symbol */ export const TypeId: unique symbol = Symbol.for("@effect/schema/Schema") /** * @since 0.67.0 * @category symbol */ export type TypeId = typeof TypeId /** * @category model * @since 0.67.0 */ export interface Schema extends Schema.Variance, Pipeable { readonly Type: A readonly Encoded: I /** @since 0.69.3 */ readonly Context: R readonly ast: AST.AST /** * Merges a set of new annotations with existing ones, potentially overwriting * any duplicates. */ annotations(annotations: Annotations.Schema): Schema } /** * @category model * @since 0.67.0 */ export interface SchemaClass extends AnnotableClass, A, I, R> {} /** * @category constructors * @since 0.67.0 */ export const make = (ast: AST.AST): SchemaClass => (class SchemaClass { [TypeId] = variance static Type: A static Encoded: I static Context: R static [TypeId] = variance static ast = ast static annotations(annotations: Annotations.Schema) { return make(mergeSchemaAnnotations(this.ast, annotations)) } static pipe() { return pipeArguments(this, arguments) } static toString() { return String(ast) } }) const variance = { /* c8 ignore next */ _A: (_: any) => _, /* c8 ignore next */ _I: (_: any) => _, /* c8 ignore next */ _R: (_: never) => _ } interface AllAnnotations> extends Annotations.Schema, PropertySignature.Annotations {} const toASTAnnotations = >( annotations?: AllAnnotations ): AST.Annotations => { if (!annotations) { return {} } const out: Types.Mutable = {} // symbols are reserved for custom annotations const custom = Object.getOwnPropertySymbols(annotations) for (const sym of custom) { out[sym] = annotations[sym] } // string keys are reserved as /schema namespace if (annotations.typeId !== undefined) { const typeId = annotations.typeId if (typeof typeId === "object") { out[AST.TypeAnnotationId] = typeId.id out[typeId.id] = typeId.annotation } else { out[AST.TypeAnnotationId] = typeId } } const move = (from: keyof typeof annotations, to: symbol) => { if (annotations[from] !== undefined) { out[to] = annotations[from] } } move("message", AST.MessageAnnotationId) move("missingMessage", AST.MissingMessageAnnotationId) move("identifier", AST.IdentifierAnnotationId) move("title", AST.TitleAnnotationId) move("description", AST.DescriptionAnnotationId) move("examples", AST.ExamplesAnnotationId) move("default", AST.DefaultAnnotationId) move("documentation", AST.DocumentationAnnotationId) move("jsonSchema", AST.JSONSchemaAnnotationId) move("arbitrary", arbitrary_.ArbitraryHookId) move("pretty", pretty_.PrettyHookId) move("equivalence", equivalence_.EquivalenceHookId) move("concurrency", AST.ConcurrencyAnnotationId) move("batching", AST.BatchingAnnotationId) move("parseIssueTitle", AST.ParseIssueTitleAnnotationId) move("parseOptions", AST.ParseOptionsAnnotationId) move("decodingFallback", AST.DecodingFallbackAnnotationId) return out } const mergeSchemaAnnotations = (ast: AST.AST, annotations: Annotations.Schema): AST.AST => AST.annotations(ast, toASTAnnotations(annotations)) /** * @category annotations * @since 0.67.0 */ export declare namespace Annotable { /** * @since 0.67.0 */ export type Self = ReturnType /** * @since 0.67.0 */ export type Any = Annotable /** * @since 0.67.0 */ export type All = | Any | Annotable | Annotable | Annotable } /** * @category annotations * @since 0.67.0 */ export interface Annotable, A, I = A, R = never> extends Schema { annotations(annotations: Annotations.Schema): Self } /** * @category annotations * @since 0.67.0 */ export interface AnnotableClass, A, I = A, R = never> extends Annotable { new(_: never): Schema.Variance } /** * @since 0.67.0 */ export const asSchema = ( schema: S ): Schema, Schema.Encoded, Schema.Context> => schema as any /** * @category formatting * @since 0.67.0 */ export const format = (schema: S): string => String(schema.ast) /** * @since 0.67.0 */ export declare namespace Schema { /** * @since 0.67.0 */ export interface Variance { readonly [TypeId]: { readonly _A: Types.Invariant readonly _I: Types.Invariant readonly _R: Types.Covariant } } /** * @since 0.67.0 */ export type Type = S extends Schema.Variance ? A : never /** * @since 0.67.0 */ export type Encoded = S extends Schema.Variance ? I : never /** * @since 0.67.0 */ export type Context = S extends Schema.Variance ? R : never /** * @since 0.67.0 */ export type ToAsserts = ( input: unknown, options?: AST.ParseOptions ) => asserts input is Schema.Type /** * Any schema, except for `never`. * * @since 0.67.0 */ export type Any = Schema /** * Any schema with `Context = never`, except for `never`. * * @since 0.67.0 */ export type AnyNoContext = Schema /** * Any schema, including `never`. * * @since 0.67.0 */ export type All = | Any | Schema | Schema | Schema /** * Type-level counterpart of `Schema.asSchema` function. * * @since 0.67.0 */ export type AsSchema = Schema, Encoded, Context> } /** * The `encodedSchema` function allows you to extract the `Encoded` portion of a * schema, creating a new schema that conforms to the properties defined in the * original schema without retaining any refinements or transformations that * were applied previously. * * @since 0.67.0 */ export const encodedSchema = (schema: Schema): SchemaClass => make(AST.encodedAST(schema.ast)) /** * The `encodedBoundSchema` function is similar to `encodedSchema` but preserves * the refinements up to the first transformation point in the original schema. * * @since 0.67.17 */ export const encodedBoundSchema = (schema: Schema): SchemaClass => make(AST.encodedBoundAST(schema.ast)) /** * The `typeSchema` function allows you to extract the `Type` portion of a * schema, creating a new schema that conforms to the properties defined in the * original schema without considering the initial encoding or transformation * processes. * * @since 0.67.0 */ export const typeSchema = (schema: Schema): SchemaClass => make(AST.typeAST(schema.ast)) /* c8 ignore start */ export { /** * By default the option `exact` is set to `true`. * * @throws `ParseError` * @category validation * @since 0.67.0 */ asserts, /** * @category decoding * @since 0.67.0 */ decodeOption, /** * @throws `ParseError` * @category decoding * @since 0.67.0 */ decodeSync, /** * @category decoding * @since 0.67.0 */ decodeUnknownOption, /** * @throws `ParseError` * @category decoding * @since 0.67.0 */ decodeUnknownSync, /** * @category encoding * @since 0.67.0 */ encodeOption, /** * @throws `ParseError` * @category encoding * @since 0.67.0 */ encodeSync, /** * @category encoding * @since 0.67.0 */ encodeUnknownOption, /** * @throws `ParseError` * @category encoding * @since 0.67.0 */ encodeUnknownSync, /** * By default the option `exact` is set to `true`. * * @category validation * @since 0.67.0 */ is, /** * @category validation * @since 0.67.0 */ validateOption, /** * @throws `ParseError` * @category validation * @since 0.67.0 */ validateSync } from "./ParseResult.js" /* c8 ignore end */ /** * @category encoding * @since 0.67.0 */ export const encodeUnknown = ( schema: Schema, options?: ParseOptions ) => { const encodeUnknown = ParseResult.encodeUnknown(schema, options) return (u: unknown, overrideOptions?: ParseOptions): Effect.Effect => ParseResult.mapError(encodeUnknown(u, overrideOptions), ParseResult.parseError) } /** * @category encoding * @since 0.67.0 */ export const encodeUnknownEither = ( schema: Schema, options?: ParseOptions ) => { const encodeUnknownEither = ParseResult.encodeUnknownEither(schema, options) return (u: unknown, overrideOptions?: ParseOptions): either_.Either => either_.mapLeft(encodeUnknownEither(u, overrideOptions), ParseResult.parseError) } /** * @category encoding * @since 0.67.0 */ export const encodeUnknownPromise = ( schema: Schema, options?: ParseOptions ) => { const parser = encodeUnknown(schema, options) return (u: unknown, overrideOptions?: ParseOptions): Promise => Effect.runPromise(parser(u, overrideOptions)) } /** * @category encoding * @since 0.67.0 */ export const encode: ( schema: Schema, options?: ParseOptions ) => (a: A, overrideOptions?: ParseOptions) => Effect.Effect = encodeUnknown /** * @category encoding * @since 0.67.0 */ export const encodeEither: ( schema: Schema, options?: ParseOptions ) => (a: A, overrideOptions?: ParseOptions) => either_.Either = encodeUnknownEither /** * @category encoding * @since 0.67.0 */ export const encodePromise: ( schema: Schema, options?: ParseOptions ) => (a: A, overrideOptions?: ParseOptions) => Promise = encodeUnknownPromise /** * @category decoding * @since 0.67.0 */ export const decodeUnknown = ( schema: Schema, options?: ParseOptions ) => { const decodeUnknown = ParseResult.decodeUnknown(schema, options) return (u: unknown, overrideOptions?: ParseOptions): Effect.Effect => ParseResult.mapError(decodeUnknown(u, overrideOptions), ParseResult.parseError) } /** * @category decoding * @since 0.67.0 */ export const decodeUnknownEither = ( schema: Schema, options?: ParseOptions ) => { const decodeUnknownEither = ParseResult.decodeUnknownEither(schema, options) return (u: unknown, overrideOptions?: ParseOptions): either_.Either => either_.mapLeft(decodeUnknownEither(u, overrideOptions), ParseResult.parseError) } /** * @category decoding * @since 0.67.0 */ export const decodeUnknownPromise = ( schema: Schema, options?: ParseOptions ) => { const parser = decodeUnknown(schema, options) return (u: unknown, overrideOptions?: ParseOptions): Promise => Effect.runPromise(parser(u, overrideOptions)) } /** * @category decoding * @since 0.67.0 */ export const decode: ( schema: Schema, options?: ParseOptions ) => (i: I, overrideOptions?: ParseOptions) => Effect.Effect = decodeUnknown /** * @category decoding * @since 0.67.0 */ export const decodeEither: ( schema: Schema, options?: ParseOptions ) => (i: I, overrideOptions?: ParseOptions) => either_.Either = decodeUnknownEither /** * @category decoding * @since 0.67.0 */ export const decodePromise: ( schema: Schema, options?: ParseOptions ) => (i: I, overrideOptions?: ParseOptions) => Promise = decodeUnknownPromise /** * @category validation * @since 0.67.0 */ export const validate = ( schema: Schema, options?: ParseOptions ) => { const validate = ParseResult.validate(schema, options) return (u: unknown, overrideOptions?: ParseOptions): Effect.Effect => ParseResult.mapError(validate(u, overrideOptions), ParseResult.parseError) } /** * @category validation * @since 0.67.0 */ export const validateEither = ( schema: Schema, options?: ParseOptions ) => { const validateEither = ParseResult.validateEither(schema, options) return (u: unknown, overrideOptions?: ParseOptions): either_.Either => either_.mapLeft(validateEither(u, overrideOptions), ParseResult.parseError) } /** * @category validation * @since 0.67.0 */ export const validatePromise = ( schema: Schema, options?: ParseOptions ) => { const parser = validate(schema, options) return (u: unknown, overrideOptions?: ParseOptions): Promise => Effect.runPromise(parser(u, overrideOptions)) } /** * Tests if a value is a `Schema`. * * @category guards * @since 0.67.0 */ export const isSchema = (u: unknown): u is Schema.Any => Predicate.hasProperty(u, TypeId) && Predicate.isObject(u[TypeId]) /** * @category api interface * @since 0.67.0 */ export interface Literal> extends AnnotableClass, Literals[number]> { readonly literals: Readonly } const getDefaultLiteralAST = >( literals: Literals ) => AST.isMembers(literals) ? AST.Union.make(AST.mapMembers(literals, (literal) => new AST.Literal(literal))) : new AST.Literal(literals[0]) const makeLiteralClass = >( literals: Literals, ast: AST.AST = getDefaultLiteralAST(literals) ): Literal => (class LiteralClass extends make(ast) { static override annotations(annotations: Annotations.Schema): Literal { return makeLiteralClass(this.literals, mergeSchemaAnnotations(this.ast, annotations)) } static literals = [...literals] as Literals }) /** * @category constructors * @since 0.67.0 */ export function Literal>( ...literals: Literals ): Literal export function Literal(): Never export function Literal>( ...literals: Literals ): Schema export function Literal>( ...literals: Literals ): Schema | Never { return array_.isNonEmptyReadonlyArray(literals) ? makeLiteralClass(literals) : Never } /** * Creates a new `Schema` from a literal schema. * * @example * import * as S from "@effect/schema/Schema" * import { Either } from "effect" * * const schema = S.Literal("a", "b", "c").pipe(S.pickLiteral("a", "b")) * * assert.deepStrictEqual(S.decodeSync(schema)("a"), "a") * assert.deepStrictEqual(S.decodeSync(schema)("b"), "b") * assert.strictEqual(Either.isLeft(S.decodeUnknownEither(schema)("c")), true) * * @category constructors * @since 0.67.0 */ export const pickLiteral = >(...literals: L) => (_schema: Schema): Literal<[...L]> => Literal(...literals) /** * @category constructors * @since 0.67.0 */ export const UniqueSymbolFromSelf = (symbol: S): SchemaClass => make(new AST.UniqueSymbol(symbol)) /** * @category api interface * @since 0.67.0 */ export interface Enums extends AnnotableClass, A[keyof A]> { readonly enums: A } /** * @since 0.67.0 */ export type EnumsDefinition = { [x: string]: string | number } const getDefaultEnumsAST = (enums: A) => new AST.Enums( Object.keys(enums).filter( (key) => typeof enums[enums[key]] !== "number" ).map((key) => [key, enums[key]]) ) const makeEnumsClass = ( enums: A, ast: AST.AST = getDefaultEnumsAST(enums) ): Enums => (class EnumsClass extends make(ast) { static override annotations(annotations: Annotations.Schema) { return makeEnumsClass(this.enums, mergeSchemaAnnotations(this.ast, annotations)) } static enums = { ...enums } }) /** * @category constructors * @since 0.67.0 */ export const Enums = (enums: A): Enums => makeEnumsClass(enums) type Join = Params extends [infer Head, ...infer Tail] ? `${(Head extends Schema ? A : Head) & (AST.LiteralValue)}${Join}` : "" /** * @category API interface * @since 0.67.17 */ export interface TemplateLiteral extends SchemaClass {} type TemplateLiteralParameter = Schema.AnyNoContext | AST.LiteralValue /** * @category template literal * @since 0.67.0 */ export const TemplateLiteral = >( ...[head, ...tail]: Params ): TemplateLiteral> => { let astOrs: ReadonlyArray = getTemplateLiterals( getTemplateLiteralParameterAST(head) ) for (const span of tail) { astOrs = array_.flatMap( astOrs, (a) => getTemplateLiterals(getTemplateLiteralParameterAST(span)).map((b) => combineTemplateLiterals(a, b)) ) } return make(AST.Union.make(astOrs.map((astOr) => Predicate.isString(astOr) ? new AST.Literal(astOr) : astOr))) } const getTemplateLiteralParameterAST = (span: TemplateLiteralParameter): AST.AST => isSchema(span) ? span.ast : new AST.Literal(String(span)) const combineTemplateLiterals = ( a: AST.TemplateLiteral | string, b: AST.TemplateLiteral | string ): AST.TemplateLiteral | string => { if (Predicate.isString(a)) { return Predicate.isString(b) ? a + b : new AST.TemplateLiteral(a + b.head, b.spans) } if (Predicate.isString(b)) { return new AST.TemplateLiteral( a.head, array_.modifyNonEmptyLast( a.spans, (span) => new AST.TemplateLiteralSpan(span.type, span.literal + b) ) ) } return new AST.TemplateLiteral( a.head, array_.appendAll( array_.modifyNonEmptyLast( a.spans, (span) => new AST.TemplateLiteralSpan(span.type, span.literal + String(b.head)) ), b.spans ) ) } const getTemplateLiterals = ( ast: AST.AST ): ReadonlyArray => { switch (ast._tag) { case "Literal": return [String(ast.literal)] case "NumberKeyword": case "StringKeyword": return [new AST.TemplateLiteral("", [new AST.TemplateLiteralSpan(ast, "")])] case "Union": return array_.flatMap(ast.types, getTemplateLiterals) } throw new Error(errors_.getSchemaUnsupportedLiteralSpanErrorMessage(ast)) } type TemplateLiteralParserParameters = Schema.Any | AST.LiteralValue type TemplateLiteralParserParametersType = T extends [infer Head, ...infer Tail] ? readonly [Head extends Schema ? A : Head, ...TemplateLiteralParserParametersType] : [] type TemplateLiteralParserParametersEncoded = T extends [infer Head, ...infer Tail] ? `${ & (Head extends Schema ? I : Head) & (AST.LiteralValue)}${TemplateLiteralParserParametersEncoded}` : "" /** * @category API interface * @since 0.70.1 */ export interface TemplateLiteralParser> extends Schema< TemplateLiteralParserParametersType, TemplateLiteralParserParametersEncoded, Schema.Context > { readonly params: Params } /** * @category template literal * @since 0.70.1 */ export const TemplateLiteralParser = >( ...params: Params ): TemplateLiteralParser => { const encodedSchemas: Array = [] const typeSchemas: Array = [] const numbers: Array = [] for (let i = 0; i < params.length; i++) { const p = params[i] if (isSchema(p)) { const encoded = encodedSchema(p) if (AST.isNumberKeyword(encoded.ast)) { numbers.push(i) } encodedSchemas.push(encoded) typeSchemas.push(p) } else { const literal = Literal(p) encodedSchemas.push(literal) typeSchemas.push(literal) } } const from = TemplateLiteral(...encodedSchemas as any) const re = AST.getTemplateLiteralCapturingRegExp(from.ast as AST.TemplateLiteral) return class TemplateLiteralParserClass extends transform(from, Tuple(...typeSchemas), { strict: false, decode: (s) => { const out: Array = re.exec(s)!.slice(1, params.length + 1) for (let i = 0; i < numbers.length; i++) { const index = numbers[i] out[index] = Number(out[index]) } return out }, encode: (tuple) => tuple.join("") }) { static params = params.slice() } as any } const declareConstructor = < const TypeParameters extends ReadonlyArray, I, A >( typeParameters: TypeParameters, options: { readonly decode: ( ...typeParameters: { readonly [K in keyof TypeParameters]: Schema< Schema.Type, Schema.Encoded, never > } ) => ( input: unknown, options: ParseOptions, ast: AST.Declaration ) => Effect.Effect readonly encode: ( ...typeParameters: { readonly [K in keyof TypeParameters]: Schema< Schema.Type, Schema.Encoded, never > } ) => ( input: unknown, options: ParseOptions, ast: AST.Declaration ) => Effect.Effect }, annotations?: Annotations.Schema ): SchemaClass> => make( new AST.Declaration( typeParameters.map((tp) => tp.ast), (...typeParameters) => options.decode(...typeParameters.map(make) as any), (...typeParameters) => options.encode(...typeParameters.map(make) as any), toASTAnnotations(annotations) ) ) const declarePrimitive = ( is: (input: unknown) => input is A, annotations?: Annotations.Schema ): SchemaClass => { const decodeUnknown = () => (input: unknown, _: ParseOptions, ast: AST.Declaration) => is(input) ? ParseResult.succeed(input) : ParseResult.fail(new ParseResult.Type(ast, input)) const encodeUnknown = decodeUnknown return make(new AST.Declaration([], decodeUnknown, encodeUnknown, toASTAnnotations(annotations))) } /** * The constraint `R extends Schema.Context` enforces dependencies solely from `typeParameters`. * This ensures that when you call `Schema.to` or `Schema.from`, you receive a schema with a `never` context. * * @category constructors * @since 0.67.0 */ export const declare: { /** * The constraint `R extends Schema.Context` enforces dependencies solely from `typeParameters`. * This ensures that when you call `Schema.to` or `Schema.from`, you receive a schema with a `never` context. * * @category constructors * @since 0.67.0 */ (is: (input: unknown) => input is A, annotations?: Annotations.Schema): SchemaClass /** * The constraint `R extends Schema.Context` enforces dependencies solely from `typeParameters`. * This ensures that when you call `Schema.to` or `Schema.from`, you receive a schema with a `never` context. * * @category constructors * @since 0.67.0 */ , I, A>( typeParameters: P, options: { readonly decode: ( ...typeParameters: { readonly [K in keyof P]: Schema, Schema.Encoded, never> } ) => ( input: unknown, options: ParseOptions, ast: AST.Declaration ) => Effect.Effect readonly encode: ( ...typeParameters: { readonly [K in keyof P]: Schema, Schema.Encoded, never> } ) => ( input: unknown, options: ParseOptions, ast: AST.Declaration ) => Effect.Effect }, annotations?: Annotations.Schema }> ): SchemaClass> } = function() { if (Array.isArray(arguments[0])) { const typeParameters = arguments[0] const options = arguments[1] const annotations = arguments[2] return declareConstructor(typeParameters, options, annotations) } const is = arguments[0] const annotations = arguments[1] return declarePrimitive(is, annotations) } as any /** * @category type id * @since 0.67.0 */ export const BrandTypeId: unique symbol = Symbol.for("@effect/schema/TypeId/Brand") /** * @category constructors * @since 0.67.0 */ export const fromBrand = , A extends Brand.Unbranded>( constructor: Brand.Constructor, annotations?: Annotations.Filter ) => (self: Schema): BrandSchema => makeBrandClass, string | symbol>( new AST.Refinement( self.ast, function predicate(a: A, _: ParseOptions, ast: AST.AST): option_.Option { const either = constructor.either(a) return either_.isLeft(either) ? option_.some(new ParseResult.Type(ast, a, either.left.map((v) => v.message).join(", "))) : option_.none() }, toASTAnnotations({ typeId: { id: BrandTypeId, annotation: { constructor } }, ...annotations }) ) ) /** * @category type id * @since 0.67.0 */ export const InstanceOfTypeId: unique symbol = Symbol.for("@effect/schema/TypeId/InstanceOf") /** * @category api interface * @since 0.67.0 */ export interface instanceOf extends AnnotableClass, A> {} /** * @category constructors * @since 0.67.0 */ export const instanceOf = any>( constructor: A, annotations?: Annotations.Schema> ): instanceOf> => declare( (u): u is InstanceType => u instanceof constructor, { title: constructor.name, description: `an instance of ${constructor.name}`, pretty: (): pretty_.Pretty> => String, typeId: { id: InstanceOfTypeId, annotation: { constructor } }, ...annotations } ) /** * @category primitives * @since 0.67.0 */ export class Undefined extends make(AST.undefinedKeyword) {} /** * @category primitives * @since 0.67.0 */ export class Void extends make(AST.voidKeyword) {} /** * @category primitives * @since 0.67.0 */ export class Null extends make(AST.null) {} /** * @category primitives * @since 0.67.0 */ export class Never extends make(AST.neverKeyword) {} /** * @category primitives * @since 0.67.0 */ export class Unknown extends make(AST.unknownKeyword) {} /** * @category primitives * @since 0.67.0 */ export class Any extends make(AST.anyKeyword) {} /** * @category primitives * @since 0.67.0 */ export class BigIntFromSelf extends make(AST.bigIntKeyword) {} /** * @category primitives * @since 0.67.0 */ export class SymbolFromSelf extends make(AST.symbolKeyword) {} /** @ignore */ class String$ extends make(AST.stringKeyword) {} /** @ignore */ class Number$ extends make(AST.numberKeyword) {} /** @ignore */ class Boolean$ extends make(AST.booleanKeyword) {} /** @ignore */ class Object$ extends make(AST.objectKeyword) {} export { /** * @category primitives * @since 0.67.0 */ Boolean$ as Boolean, /** * @category primitives * @since 0.67.0 */ Number$ as Number, /** * @category primitives * @since 0.67.0 */ Object$ as Object, /** * @category primitives * @since 0.67.0 */ String$ as String } /** * @category api interface * @since 0.67.0 */ export interface Union> extends AnnotableClass< Union, Schema.Type, Schema.Encoded, Schema.Context > { readonly members: Readonly annotations(annotations: Annotations.Schema>): Union } const getDefaultUnionAST = >(members: Members): AST.AST => AST.Union.make(members.map((m) => m.ast)) const makeUnionClass = >( members: Members, ast: AST.AST = getDefaultUnionAST(members) ): Union< Members > => (class UnionClass extends make, Schema.Encoded, Schema.Context>(ast) { static override annotations(annotations: Annotations.Schema>): Union { return makeUnionClass(this.members, mergeSchemaAnnotations(this.ast, annotations)) } static members = [...members] }) /** * @category combinators * @since 0.67.0 */ export function Union>(...members: Members): Union export function Union(member: Member): Member export function Union(): typeof Never export function Union>( ...members: Members ): Schema, Schema.Encoded, Schema.Context> export function Union>( ...members: Members ) { return AST.isMembers(members) ? makeUnionClass(members) : array_.isNonEmptyReadonlyArray(members) ? members[0] : Never } /** * @category api interface * @since 0.67.0 */ export interface NullOr extends Union<[S, typeof Null]> { annotations(annotations: Annotations.Schema | null>): NullOr } /** * @category combinators * @since 0.67.0 */ export const NullOr = (self: S): NullOr => Union(self, Null) /** * @category api interface * @since 0.67.0 */ export interface UndefinedOr extends Union<[S, typeof Undefined]> { annotations(annotations: Annotations.Schema | undefined>): UndefinedOr } /** * @category combinators * @since 0.67.0 */ export const UndefinedOr = (self: S): UndefinedOr => Union(self, Undefined) /** * @category api interface * @since 0.67.0 */ export interface NullishOr extends Union<[S, typeof Null, typeof Undefined]> { annotations(annotations: Annotations.Schema | null | undefined>): NullishOr } /** * @category combinators * @since 0.67.0 */ export const NullishOr = (self: S): NullishOr => Union(self, Null, Undefined) /** * @category combinators * @since 0.67.0 */ export const keyof = (self: Schema): SchemaClass => make(AST.keyof(self.ast)) /** * @since 0.68.0 */ export declare namespace Element { /** * @since 0.68.0 */ export interface Annotations extends Annotations.Doc { readonly missingMessage?: AST.MissingMessageAnnotation } /** * @since 0.68.0 */ export type Token = "" | "?" } /** * @category API interface * @since 0.68.0 */ export interface Element extends Schema.Variance, Schema.Encoded, Schema.Context> { readonly _Token: Token readonly ast: AST.OptionalType readonly from: S annotations(annotations: Element.Annotations>): Element } /** * @since 0.68.0 */ export const element = (self: S): Element => new ElementImpl(new AST.OptionalType(self.ast, false), self) /** * @since 0.67.0 */ export const optionalElement = (self: S): Element => new ElementImpl(new AST.OptionalType(self.ast, true), self) class ElementImpl implements Element { readonly [TypeId]!: Schema.Variance, Schema.Encoded, Schema.Context>[TypeId] readonly _Token!: Token constructor( readonly ast: AST.OptionalType, readonly from: S ) {} annotations( annotations: Annotations.Schema> ): ElementImpl { return new ElementImpl( new AST.OptionalType( this.ast.type, this.ast.isOptional, { ...this.ast.annotations, ...toASTAnnotations(annotations) } ), this.from ) } toString() { return `${this.ast.type}${this.ast.isOptional ? "?" : ""}` } } /** * @since 0.67.0 */ export declare namespace TupleType { type ElementsType< Elements, Out extends ReadonlyArray = readonly [] > = Elements extends readonly [infer Head, ...infer Tail] ? Head extends Element ? ElementsType?]> : ElementsType]> : Out type ElementsEncoded< Elements, Out extends ReadonlyArray = readonly [] > = Elements extends readonly [infer Head, ...infer Tail] ? Head extends Element ? ElementsEncoded?]> : ElementsEncoded]> : Out /** * @since 0.67.0 */ export type Elements = ReadonlyArray> /** * @since 0.68.0 */ export type Rest = ReadonlyArray> /** * @since 0.67.0 */ export type Type = Rest extends [infer Head, ...infer Tail] ? Readonly<[ ...ElementsType, ...ReadonlyArray>, ...{ readonly [K in keyof Tail]: Schema.Type } ]> : ElementsType /** * @since 0.67.0 */ export type Encoded = Rest extends [infer Head, ...infer Tail] ? Readonly<[ ...ElementsEncoded, ...ReadonlyArray>, ...{ readonly [K in keyof Tail]: Schema.Encoded } ]> : ElementsEncoded } /** * @category api interface * @since 0.67.0 */ export interface TupleType extends AnnotableClass< TupleType, TupleType.Type, TupleType.Encoded, Schema.Context | Schema.Context > { readonly elements: Readonly readonly rest: Readonly } const getDefaultTupleTypeAST = ( elements: Elements, rest: Rest ) => new AST.TupleType( elements.map((el) => isSchema(el) ? new AST.OptionalType(el.ast, false) : el.ast), rest.map((el) => isSchema(el) ? new AST.Type(el.ast) : el.ast), true ) const makeTupleTypeClass = ( elements: Elements, rest: Rest, ast: AST.AST = getDefaultTupleTypeAST(elements, rest) ) => (class TupleTypeClass extends make< TupleType.Type, TupleType.Encoded, Schema.Context | Schema.Context >(ast) { static override annotations( annotations: Annotations.Schema> ): TupleType { return makeTupleTypeClass(this.elements, this.rest, mergeSchemaAnnotations(this.ast, annotations)) } static elements = [...elements] as any as Elements static rest = [...rest] as any as Rest }) /** * @category api interface * @since 0.67.0 */ export interface Tuple extends TupleType { annotations(annotations: Annotations.Schema>): Tuple } /** * @category constructors * @since 0.67.0 */ export function Tuple< const Elements extends TupleType.Elements, Rest extends array_.NonEmptyReadonlyArray >(elements: Elements, ...rest: Rest): TupleType export function Tuple(...elements: Elements): Tuple export function Tuple(...args: ReadonlyArray): any { return Array.isArray(args[0]) ? makeTupleTypeClass(args[0], args.slice(1)) : makeTupleTypeClass(args, []) } /** * @category api interface * @since 0.67.0 */ export interface Array$ extends TupleType<[], [Value]> { readonly value: Value annotations(annotations: Annotations.Schema>): Array$ } const makeArrayClass = ( value: Value, ast?: AST.AST ): Array$ => (class ArrayClass extends makeTupleTypeClass<[], [Value]>([], [value], ast) { static override annotations(annotations: Annotations.Schema>) { return makeArrayClass(this.value, mergeSchemaAnnotations(this.ast, annotations)) } static value = value }) const Array$ = (value: Value): Array$ => makeArrayClass(value) export { /** * @category constructors * @since 0.67.0 */ Array$ as Array } /** * @category api interface * @since 0.67.0 */ export interface NonEmptyArray extends TupleType<[Value], [Value]> { readonly value: Value annotations(annotations: Annotations.Schema>): NonEmptyArray } const makeNonEmptyArrayClass = ( value: Value, ast?: AST.AST ): NonEmptyArray< Value > => (class NonEmptyArrayClass extends makeTupleTypeClass<[Value], [Value]>([value], [value], ast) { static override annotations(annotations: Annotations.Schema>) { return makeNonEmptyArrayClass(this.value, mergeSchemaAnnotations(this.ast, annotations)) } static value = value }) /** * @category constructors * @since 0.67.0 */ export const NonEmptyArray = (value: Value): NonEmptyArray => makeNonEmptyArrayClass(value) /** * @category api interface * @since 0.71.0 */ export interface ArrayEnsure extends AnnotableClass< ArrayEnsure, ReadonlyArray>, Schema.Encoded | ReadonlyArray>, Schema.Context > {} /** * @category constructors * @since 0.71.0 */ export const ArrayEnsure = (value: Value): ArrayEnsure => { const value_ = asSchema(value) return class ArrayEnsureClass extends transform(Union(value_, Array$(value_)), Array$(typeSchema(value_)), { strict: true, decode: array_.ensure, encode: (arr) => arr.length === 1 ? arr[0] : arr }) {} } /** * @category api interface * @since 0.71.0 */ export interface NonEmptyArrayEnsure extends AnnotableClass< NonEmptyArrayEnsure, array_.NonEmptyReadonlyArray>, Schema.Encoded | array_.NonEmptyReadonlyArray>, Schema.Context > {} /** * @category constructors * @since 0.71.0 */ export const NonEmptyArrayEnsure = (value: Value): NonEmptyArrayEnsure => { const value_ = asSchema(value) return class NonEmptyArrayEnsureClass extends transform(Union(value_, NonEmptyArray(value_)), NonEmptyArray(typeSchema(value_)), { strict: true, decode: array_.ensure as any, encode: (arr) => arr.length === 1 ? arr[0] : arr }) {} } /** * @since 0.67.0 */ export declare namespace PropertySignature { /** * @since 0.67.0 */ export type Token = "?:" | ":" /** * @since 0.67.0 */ export type Any = PropertySignature< Token, any, Key, Token, any, boolean, unknown > /** * @since 0.67.0 */ export type All = | Any | PropertySignature | PropertySignature | PropertySignature /** * @since 0.67.0 */ export type AST = | PropertySignatureDeclaration | PropertySignatureTransformation /** * @since 0.67.0 */ export interface Annotations extends Annotations.Doc { readonly missingMessage?: AST.MissingMessageAnnotation } } const formatPropertySignatureToken = (isOptional: boolean): string => isOptional ? "\"?:\"" : "\":\"" /** * @category PropertySignature * @since 0.67.0 */ export class PropertySignatureDeclaration extends AST.OptionalType { /** * @since 0.67.0 */ readonly _tag = "PropertySignatureDeclaration" constructor( type: AST.AST, isOptional: boolean, readonly isReadonly: boolean, annotations: AST.Annotations, readonly defaultValue: (() => unknown) | undefined ) { super(type, isOptional, annotations) } /** * @since 0.67.0 */ toString() { const token = formatPropertySignatureToken(this.isOptional) const type = String(this.type) return `PropertySignature<${token}, ${type}, never, ${token}, ${type}>` } } /** * @category PropertySignature * @since 0.67.0 */ export class FromPropertySignature extends AST.OptionalType { constructor( type: AST.AST, isOptional: boolean, readonly isReadonly: boolean, annotations: AST.Annotations, readonly fromKey?: PropertyKey | undefined ) { super(type, isOptional, annotations) } } /** * @category PropertySignature * @since 0.67.0 */ export class ToPropertySignature extends AST.OptionalType { constructor( type: AST.AST, isOptional: boolean, readonly isReadonly: boolean, annotations: AST.Annotations, readonly defaultValue: (() => unknown) | undefined ) { super(type, isOptional, annotations) } } const formatPropertyKey = (p: PropertyKey | undefined): string => { if (p === undefined) { return "never" } if (Predicate.isString(p)) { return JSON.stringify(p) } return String(p) } /** * @category PropertySignature * @since 0.67.0 */ export class PropertySignatureTransformation { /** * @since 0.67.0 */ readonly _tag = "PropertySignatureTransformation" constructor( readonly from: FromPropertySignature, readonly to: ToPropertySignature, readonly decode: AST.PropertySignatureTransformation["decode"], readonly encode: AST.PropertySignatureTransformation["encode"] ) {} /** * @since 0.67.0 */ toString() { return `PropertySignature<${formatPropertySignatureToken(this.to.isOptional)}, ${this.to.type}, ${ formatPropertyKey(this.from.fromKey) }, ${formatPropertySignatureToken(this.from.isOptional)}, ${this.from.type}>` } } const mergeSignatureAnnotations = ( ast: PropertySignature.AST, annotations: AST.Annotations ): PropertySignature.AST => { switch (ast._tag) { case "PropertySignatureDeclaration": { return new PropertySignatureDeclaration( ast.type, ast.isOptional, ast.isReadonly, { ...ast.annotations, ...annotations }, ast.defaultValue ) } case "PropertySignatureTransformation": { return new PropertySignatureTransformation( new FromPropertySignature( ast.from.type, ast.from.isOptional, ast.from.isReadonly, ast.from.annotations ), new ToPropertySignature(ast.to.type, ast.to.isOptional, ast.to.isReadonly, { ...ast.to.annotations, ...annotations }, ast.to.defaultValue), ast.decode, ast.encode ) } } } /** * @since 0.68.0 * @category symbol */ export const PropertySignatureTypeId: unique symbol = Symbol.for("@effect/schema/PropertySignature") /** * @since 0.68.0 * @category symbol */ export type PropertySignatureTypeId = typeof PropertySignatureTypeId /** * @since 0.69.3 * @category guards */ export const isPropertySignature = (u: unknown): u is PropertySignature.All => Predicate.hasProperty(u, PropertySignatureTypeId) /** * @category PropertySignature * @since 0.67.0 */ export interface PropertySignature< TypeToken extends PropertySignature.Token, Type, Key extends PropertyKey, EncodedToken extends PropertySignature.Token, Encoded, HasDefault extends boolean = false, R = never > extends Schema.Variance, Pipeable { readonly [PropertySignatureTypeId]: null readonly _TypeToken: TypeToken readonly _EncodedToken: EncodedToken readonly _HasDefault: HasDefault readonly _Key: Key readonly ast: PropertySignature.AST annotations( annotations: PropertySignature.Annotations ): PropertySignature } class PropertySignatureImpl< TypeToken extends PropertySignature.Token, Type, Key extends PropertyKey, EncodedToken extends PropertySignature.Token, Encoded, HasDefault extends boolean = false, R = never > implements PropertySignature { readonly [TypeId]!: Schema.Variance[TypeId] readonly [PropertySignatureTypeId] = null readonly _TypeToken!: TypeToken readonly _Key!: Key readonly _EncodedToken!: EncodedToken readonly _HasDefault!: HasDefault constructor( readonly ast: PropertySignature.AST ) {} pipe() { return pipeArguments(this, arguments) } annotations( annotations: PropertySignature.Annotations ): PropertySignature { return new PropertySignatureImpl(mergeSignatureAnnotations(this.ast, toASTAnnotations(annotations))) } toString() { return String(this.ast) } } /** * @category PropertySignature * @since 0.67.15 */ export const makePropertySignature = < TypeToken extends PropertySignature.Token, Type, Key extends PropertyKey, EncodedToken extends PropertySignature.Token, Encoded, HasDefault extends boolean = false, R = never >(ast: PropertySignature.AST) => new PropertySignatureImpl(ast) class PropertySignatureWithFromImpl< From extends Schema.All, TypeToken extends PropertySignature.Token, Type, Key extends PropertyKey, EncodedToken extends PropertySignature.Token, Encoded, HasDefault extends boolean = false, R = never > extends PropertySignatureImpl { constructor(ast: PropertySignature.AST, readonly from: From) { super(ast) } annotations( annotations: PropertySignature.Annotations ): PropertySignatureWithFromImpl { return new PropertySignatureWithFromImpl( mergeSignatureAnnotations(this.ast, toASTAnnotations(annotations)), this.from ) } } /** * @category API interface * @since 1.0.0 */ export interface propertySignature extends PropertySignature<":", Schema.Type, never, ":", Schema.Encoded, false, Schema.Context> { readonly from: S annotations(annotations: PropertySignature.Annotations>): propertySignature } /** * Lifts a `Schema` into a `PropertySignature`. * * @category PropertySignature * @since 0.67.0 */ export const propertySignature = ( self: S ): propertySignature => new PropertySignatureWithFromImpl( new PropertySignatureDeclaration(self.ast, false, true, {}, undefined), self ) /** * Enhances a property signature with a default constructor value. * * @category PropertySignature * @since 0.67.0 */ export const withConstructorDefault: { /** * Enhances a property signature with a default constructor value. * * @category PropertySignature * @since 0.67.0 */ (defaultValue: () => Types.NoInfer): < TypeToken extends PropertySignature.Token, Key extends PropertyKey, EncodedToken extends PropertySignature.Token, Encoded, R >( self: PropertySignature ) => PropertySignature /** * Enhances a property signature with a default constructor value. * * @category PropertySignature * @since 0.67.0 */ < TypeToken extends PropertySignature.Token, Type, Key extends PropertyKey, EncodedToken extends PropertySignature.Token, Encoded, R >( self: PropertySignature, defaultValue: () => Types.NoInfer ): PropertySignature } = dual(2, < TypeToken extends PropertySignature.Token, Type, Key extends PropertyKey, EncodedToken extends PropertySignature.Token, Encoded, R >( self: PropertySignature, defaultValue: () => Types.NoInfer ): PropertySignature => { const ast = self.ast switch (ast._tag) { case "PropertySignatureDeclaration": return makePropertySignature( new PropertySignatureDeclaration(ast.type, ast.isOptional, ast.isReadonly, ast.annotations, defaultValue) ) case "PropertySignatureTransformation": return makePropertySignature( new PropertySignatureTransformation( ast.from, new ToPropertySignature(ast.to.type, ast.to.isOptional, ast.to.isReadonly, ast.to.annotations, defaultValue), ast.decode, ast.encode ) ) } }) const applyDefaultValue = (o: option_.Option, defaultValue: () => A) => option_.match(o, { onNone: () => option_.some(defaultValue()), onSome: (value) => option_.some(value === undefined ? defaultValue() : value) }) /** * Enhances a property signature with a default decoding value. * * @category PropertySignature * @since 0.67.0 */ export const withDecodingDefault: { /** * Enhances a property signature with a default decoding value. * * @category PropertySignature * @since 0.67.0 */ (defaultValue: () => Types.NoInfer): < Key extends PropertyKey, Encoded, HasDefault extends boolean, R >( self: PropertySignature<"?:", Type, Key, "?:", Encoded, HasDefault, R> ) => PropertySignature<":", Exclude, Key, "?:", Encoded, HasDefault, R> /** * Enhances a property signature with a default decoding value. * * @category PropertySignature * @since 0.67.0 */ < Type, Key extends PropertyKey, Encoded, HasDefault extends boolean, R >( self: PropertySignature<"?:", Type, Key, "?:", Encoded, HasDefault, R>, defaultValue: () => Types.NoInfer ): PropertySignature<":", Exclude, Key, "?:", Encoded, HasDefault, R> } = dual(2, < Type, Key extends PropertyKey, Encoded, R >( self: PropertySignature<"?:", Type, Key, "?:", Encoded, boolean, R>, defaultValue: () => Types.NoInfer ): PropertySignature<":", Exclude, Key, "?:", Encoded, true, R> => { const ast = self.ast switch (ast._tag) { case "PropertySignatureDeclaration": return makePropertySignature( new PropertySignatureTransformation( ast, new ToPropertySignature(AST.typeAST(ast.type), false, true, {}, undefined), (o) => applyDefaultValue(o, defaultValue), identity ) ) case "PropertySignatureTransformation": return makePropertySignature( new PropertySignatureTransformation( ast.from, new ToPropertySignature(ast.to.type, false, ast.to.isReadonly, ast.to.annotations, ast.to.defaultValue), (o) => applyDefaultValue(ast.decode(o), defaultValue), ast.encode ) ) } }) /** * Enhances a property signature with a default decoding value and a default constructor value. * * @category PropertySignature * @since 0.67.0 */ export const withDefaults: { /** * Enhances a property signature with a default decoding value and a default constructor value. * * @category PropertySignature * @since 0.67.0 */ ( defaults: { constructor: () => Types.NoInfer> decoding: () => Types.NoInfer } ): < Key extends PropertyKey, Encoded, R >( self: PropertySignature<"?:", Type, Key, "?:", Encoded, boolean, R> ) => PropertySignature<":", Exclude, Key, "?:", Encoded, true, R> /** * Enhances a property signature with a default decoding value and a default constructor value. * * @category PropertySignature * @since 0.67.0 */ < Type, Key extends PropertyKey, Encoded, R >( self: PropertySignature<"?:", Type, Key, "?:", Encoded, boolean, R>, defaults: { constructor: () => Types.NoInfer> decoding: () => Types.NoInfer } ): PropertySignature<":", Exclude, Key, "?:", Encoded, true, R> } = dual(2, < Type, Key extends PropertyKey, Encoded, R >( self: PropertySignature<"?:", Type, Key, "?:", Encoded, boolean, R>, defaults: { constructor: () => Types.NoInfer> decoding: () => Types.NoInfer } ): PropertySignature<":", Exclude, Key, "?:", Encoded, true, R> => self.pipe(withDecodingDefault(defaults.decoding), withConstructorDefault(defaults.constructor))) /** * Enhances a property signature by specifying a different key for it in the Encoded type. * * @category PropertySignature * @since 0.67.0 */ export const fromKey: { /** * Enhances a property signature by specifying a different key for it in the Encoded type. * * @category PropertySignature * @since 0.67.0 */ (key: Key): < TypeToken extends PropertySignature.Token, Type, EncodedToken extends PropertySignature.Token, Encoded, HasDefault extends boolean, R >( self: PropertySignature ) => PropertySignature /** * Enhances a property signature by specifying a different key for it in the Encoded type. * * @category PropertySignature * @since 0.67.0 */ < Type, TypeToken extends PropertySignature.Token, Encoded, EncodedToken extends PropertySignature.Token, HasDefault extends boolean, R, Key extends PropertyKey >( self: PropertySignature, key: Key ): PropertySignature } = dual(2, < Type, TypeToken extends PropertySignature.Token, Encoded, EncodedToken extends PropertySignature.Token, HasDefault extends boolean, R, Key extends PropertyKey >( self: PropertySignature, key: Key ): PropertySignature => { const ast = self.ast switch (ast._tag) { case "PropertySignatureDeclaration": { return makePropertySignature( new PropertySignatureTransformation( new FromPropertySignature( ast.type, ast.isOptional, ast.isReadonly, ast.annotations, key ), new ToPropertySignature(AST.typeAST(ast.type), ast.isOptional, ast.isReadonly, {}, ast.defaultValue), identity, identity ) ) } case "PropertySignatureTransformation": return makePropertySignature( new PropertySignatureTransformation( new FromPropertySignature( ast.from.type, ast.from.isOptional, ast.from.isReadonly, ast.from.annotations, key ), ast.to, ast.decode, ast.encode ) ) } }) /** * Converts an optional property to a required one through a transformation `Option -> Type`. * * - `decode`: `none` as argument means the value is missing in the input. * - `encode`: `none` as return value means the value will be missing in the output. * * @category PropertySignature * @since 0.67.0 */ export const optionalToRequired = ( from: Schema, to: Schema, options: { readonly decode: (o: option_.Option) => TI readonly encode: (ti: TI) => option_.Option } ): PropertySignature<":", TA, never, "?:", FI, false, FR | TR> => makePropertySignature( new PropertySignatureTransformation( new FromPropertySignature(from.ast, true, true, {}, undefined), new ToPropertySignature(to.ast, false, true, {}, undefined), (o) => option_.some(options.decode(o)), option_.flatMap(options.encode) ) ) /** * Converts an optional property to a required one through a transformation `Type -> Option`. * * - `decode`: `none` as return value means the value will be missing in the output. * - `encode`: `none` as argument means the value is missing in the input. * * @category PropertySignature * @since 0.67.15 */ export const requiredToOptional = ( from: Schema, to: Schema, options: { readonly decode: (fa: FA) => option_.Option readonly encode: (o: option_.Option) => FA } ): PropertySignature<"?:", TA, never, ":", FI, false, FR | TR> => makePropertySignature( new PropertySignatureTransformation( new FromPropertySignature(from.ast, false, true, {}, undefined), new ToPropertySignature(to.ast, true, true, {}, undefined), option_.flatMap(options.decode), (o) => option_.some(options.encode(o)) ) ) /** * Converts an optional property to another optional property through a transformation `Option -> Option`. * * - `decode`: * - `none` as argument means the value is missing in the input. * - `none` as return value means the value will be missing in the output. * - `encode`: * - `none` as argument means the value is missing in the input. * - `none` as return value means the value will be missing in the output. * * @category PropertySignature * @since 0.67.0 */ export const optionalToOptional = ( from: Schema, to: Schema, options: { readonly decode: (o: option_.Option) => option_.Option readonly encode: (o: option_.Option) => option_.Option } ): PropertySignature<"?:", TA, never, "?:", FI, false, FR | TR> => makePropertySignature( new PropertySignatureTransformation( new FromPropertySignature(from.ast, true, true, {}, undefined), new ToPropertySignature(to.ast, true, true, {}, undefined), options.decode, options.encode ) ) /** * @since 0.67.0 */ export type OptionalOptions = { readonly default?: never readonly as?: never readonly exact?: true readonly nullable?: true } | { readonly default: LazyArg readonly as?: never readonly exact?: true readonly nullable?: true } | { readonly as: "Option" readonly default?: never readonly exact?: never readonly nullable?: never readonly onNoneEncoding?: LazyArg> } | { readonly as: "Option" readonly default?: never readonly exact?: never readonly nullable: true readonly onNoneEncoding?: LazyArg> } | { readonly as: "Option" readonly default?: never readonly exact: true readonly nullable?: never readonly onNoneEncoding?: never } | { readonly as: "Option" readonly default?: never readonly exact: true readonly nullable: true readonly onNoneEncoding?: LazyArg> } | undefined /** * @category api interface * @since 0.67.10 */ export interface optional extends PropertySignature< "?:", Schema.Type | undefined, never, "?:", Schema.Encoded | undefined, false, Schema.Context > { readonly from: S annotations(annotations: PropertySignature.Annotations | undefined>): optional } /** * @category api interface * @since 0.69.0 */ export interface optionalWith extends PropertySignature< Types.Has extends true ? ":" : "?:", | (Types.Has extends true ? option_.Option> : Schema.Type) | (Types.Has extends true ? never : undefined), never, "?:", | Schema.Encoded | (Types.Has extends true ? null : never) | (Types.Has extends true ? never : undefined), Types.Has, Schema.Context > { readonly from: S annotations( annotations: PropertySignature.Annotations< | (Types.Has extends true ? option_.Option> : Schema.Type) | (Types.Has extends true ? never : undefined) > ): optionalWith } const optionalPropertySignatureAST = ( self: Schema, options?: { readonly exact?: true readonly default?: () => A readonly nullable?: true readonly as?: "Option" readonly onNoneEncoding?: () => option_.Option } ): PropertySignature.AST => { const isExact = options?.exact const defaultValue = options?.default const isNullable = options?.nullable const asOption = options?.as == "Option" const asOptionEncode = options?.onNoneEncoding ? option_.orElse(options.onNoneEncoding) : identity if (isExact) { if (defaultValue) { if (isNullable) { return withConstructorDefault( optionalToRequired( NullOr(self), typeSchema(self), { decode: option_.match({ onNone: defaultValue, onSome: (a) => a === null ? defaultValue() : a }), encode: option_.some } ), defaultValue ).ast } else { return withConstructorDefault( optionalToRequired( self, typeSchema(self), { decode: option_.match({ onNone: defaultValue, onSome: identity }), encode: option_.some } ), defaultValue ).ast } } else if (asOption) { if (isNullable) { return optionalToRequired( NullOr(self), OptionFromSelf(typeSchema(self)), { decode: option_.filter(Predicate.isNotNull), encode: asOptionEncode } ).ast } else { return optionalToRequired( self, OptionFromSelf(typeSchema(self)), { decode: identity, encode: identity } ).ast } } else { if (isNullable) { return optionalToOptional( NullOr(self), typeSchema(self), { decode: option_.filter(Predicate.isNotNull), encode: identity } ).ast } else { return new PropertySignatureDeclaration(self.ast, true, true, {}, undefined) } } } else { if (defaultValue) { if (isNullable) { return withConstructorDefault( optionalToRequired( NullishOr(self), typeSchema(self), { decode: option_.match({ onNone: defaultValue, onSome: (a) => (a == null ? defaultValue() : a) }), encode: option_.some } ), defaultValue ).ast } else { return withConstructorDefault( optionalToRequired( UndefinedOr(self), typeSchema(self), { decode: option_.match({ onNone: defaultValue, onSome: (a) => (a === undefined ? defaultValue() : a) }), encode: option_.some } ), defaultValue ).ast } } else if (asOption) { if (isNullable) { return optionalToRequired( NullishOr(self), OptionFromSelf(typeSchema(self)), { decode: option_.filter((a): a is A => a != null), encode: asOptionEncode } ).ast } else { return optionalToRequired( UndefinedOr(self), OptionFromSelf(typeSchema(self)), { decode: option_.filter(Predicate.isNotUndefined), encode: asOptionEncode } ).ast } } else { if (isNullable) { return optionalToOptional( NullishOr(self), UndefinedOr(typeSchema(self)), { decode: option_.filter(Predicate.isNotNull), encode: identity } ).ast } else { return new PropertySignatureDeclaration(UndefinedOr(self).ast, true, true, {}, undefined) } } } } /** * @category PropertySignature * @since 0.69.0 */ export const optional = (self: S): optional => { const ast = self.ast === AST.undefinedKeyword || self.ast === AST.neverKeyword ? AST.undefinedKeyword : UndefinedOr(self).ast return new PropertySignatureWithFromImpl(new PropertySignatureDeclaration(ast, true, true, {}, undefined), self) } /** * @category PropertySignature * @since 0.69.0 */ export const optionalWith: { /** * @category PropertySignature * @since 0.69.0 */ >>( options: Options ): (self: S) => optionalWith /** * @category PropertySignature * @since 0.69.0 */ >>( self: S, options: Options ): optionalWith } = dual((args) => isSchema(args[0]), (self, options) => { return new PropertySignatureWithFromImpl(optionalPropertySignatureAST(self, options), self) }) /** * @since 0.67.0 */ export declare namespace Struct { /** * @since 0.67.0 */ export type Fields = { readonly [x: PropertyKey]: | Schema.All | PropertySignature.All } type Key = [K] extends [never] ? never : F[K] extends PropertySignature.All ? [Key] extends [never] ? K : Key : K type EncodedTokenKeys = { [K in keyof Fields]: Fields[K] extends | PropertySignature | PropertySignature | PropertySignature | PropertySignature ? K : never }[keyof Fields] type TypeTokenKeys = { [K in keyof Fields]: Fields[K] extends OptionalPropertySignature ? K : never }[keyof Fields] type OptionalPropertySignature = | PropertySignature<"?:", any, PropertyKey, PropertySignature.Token, any, boolean, unknown> | PropertySignature<"?:", any, PropertyKey, PropertySignature.Token, never, boolean, unknown> | PropertySignature<"?:", never, PropertyKey, PropertySignature.Token, any, boolean, unknown> | PropertySignature<"?:", never, PropertyKey, PropertySignature.Token, never, boolean, unknown> /** * @since 0.67.0 */ export type Type = Types.UnionToIntersection< { [K in keyof F]: F[K] extends OptionalPropertySignature ? { readonly [H in K]?: Schema.Type } : { readonly [h in K]: Schema.Type } }[keyof F] > extends infer Q ? Q : never /** * @since 0.67.0 */ export type Encoded = & { readonly [K in Exclude> as Key]: Schema.Encoded } & { readonly [K in EncodedTokenKeys as Key]?: Schema.Encoded } /** * @since 0.67.0 */ export type Context = Schema.Context type PropertySignatureWithDefault = | PropertySignature | PropertySignature | PropertySignature | PropertySignature /** * @since 0.67.0 */ export type Constructor = Types.UnionToIntersection< { [K in keyof F]: F[K] extends OptionalPropertySignature ? { readonly [H in K]?: Schema.Type } : F[K] extends PropertySignatureWithDefault ? { readonly [H in K]?: Schema.Type } : { readonly [h in K]: Schema.Type } }[keyof F] > extends infer Q ? Q : never } /** * @since 0.67.0 */ export declare namespace IndexSignature { /** * @since 0.67.0 */ export type Record = { readonly key: Schema.All; readonly value: Schema.All } /** * @since 0.67.0 */ export type Records = ReadonlyArray /** * @since 0.67.0 */ export type NonEmptyRecords = array_.NonEmptyReadonlyArray /** * @since 0.67.0 */ export type Type< Records extends IndexSignature.Records > = Types.UnionToIntersection< { [K in keyof Records]: { readonly [P in Schema.Type]: Schema.Type } }[number] > /** * @since 0.67.0 */ export type Encoded< Records extends IndexSignature.Records > = Types.UnionToIntersection< { [K in keyof Records]: { readonly [P in Schema.Encoded]: Schema.Encoded } }[number] > /** * @since 0.67.0 */ export type Context = { [K in keyof Records]: Schema.Context | Schema.Context }[number] } /** * @since 0.67.0 */ export declare namespace TypeLiteral { /** * @since 0.67.0 */ export type Type = & Struct.Type & IndexSignature.Type /** * @since 0.67.0 */ export type Encoded = & Struct.Encoded & IndexSignature.Encoded /** * @since 0.67.0 */ export type Constructor = & Struct.Constructor & IndexSignature.Type } /** * @category api interface * @since 0.67.0 */ export interface TypeLiteral< Fields extends Struct.Fields, Records extends IndexSignature.Records > extends AnnotableClass< TypeLiteral, Simplify>, Simplify>, | Struct.Context | IndexSignature.Context > { readonly fields: { readonly [K in keyof Fields]: Fields[K] } readonly records: Readonly annotations( annotations: Annotations.Schema>> ): TypeLiteral make( props: Simplify>, options?: MakeOptions ): Simplify> } const getDefaultTypeLiteralAST = < Fields extends Struct.Fields, const Records extends IndexSignature.Records >(fields: Fields, records: Records) => { const ownKeys = util_.ownKeys(fields) const pss: Array = [] if (ownKeys.length > 0) { const from: Array = [] const to: Array = [] const transformations: Array = [] for (let i = 0; i < ownKeys.length; i++) { const key = ownKeys[i] const field = fields[key] if (isPropertySignature(field)) { const ast: PropertySignature.AST = field.ast switch (ast._tag) { case "PropertySignatureDeclaration": { const type = ast.type const isOptional = ast.isOptional const toAnnotations = ast.annotations from.push(new AST.PropertySignature(key, type, isOptional, true)) to.push(new AST.PropertySignature(key, AST.typeAST(type), isOptional, true, toAnnotations)) pss.push( new AST.PropertySignature(key, type, isOptional, true, toAnnotations) ) break } case "PropertySignatureTransformation": { const fromKey = ast.from.fromKey ?? key from.push( new AST.PropertySignature(fromKey, ast.from.type, ast.from.isOptional, true, ast.from.annotations) ) to.push( new AST.PropertySignature(key, ast.to.type, ast.to.isOptional, true, ast.to.annotations) ) transformations.push(new AST.PropertySignatureTransformation(fromKey, key, ast.decode, ast.encode)) break } } } else { from.push(new AST.PropertySignature(key, field.ast, false, true)) to.push(new AST.PropertySignature(key, AST.typeAST(field.ast), false, true)) pss.push(new AST.PropertySignature(key, field.ast, false, true)) } } if (array_.isNonEmptyReadonlyArray(transformations)) { const issFrom: Array = [] const issTo: Array = [] for (const r of records) { const { indexSignatures, propertySignatures } = AST.record(r.key.ast, r.value.ast) propertySignatures.forEach((ps) => { from.push(ps) to.push( new AST.PropertySignature(ps.name, AST.typeAST(ps.type), ps.isOptional, ps.isReadonly, ps.annotations) ) }) indexSignatures.forEach((is) => { issFrom.push(is) issTo.push(new AST.IndexSignature(is.parameter, AST.typeAST(is.type), is.isReadonly)) }) } return new AST.Transformation( new AST.TypeLiteral(from, issFrom, { [AST.TitleAnnotationId]: "Struct (Encoded side)" }), new AST.TypeLiteral(to, issTo, { [AST.TitleAnnotationId]: "Struct (Type side)" }), new AST.TypeLiteralTransformation(transformations) ) } } const iss: Array = [] for (const r of records) { const { indexSignatures, propertySignatures } = AST.record(r.key.ast, r.value.ast) propertySignatures.forEach((ps) => pss.push(ps)) indexSignatures.forEach((is) => iss.push(is)) } return new AST.TypeLiteral(pss, iss) } const lazilyMergeDefaults = ( fields: Struct.Fields, out: Record ): { [x: string | symbol]: unknown } => { const ownKeys = util_.ownKeys(fields) for (const key of ownKeys) { const field = fields[key] if (out[key] === undefined && isPropertySignature(field)) { const ast = field.ast const defaultValue = ast._tag === "PropertySignatureDeclaration" ? ast.defaultValue : ast.to.defaultValue if (defaultValue !== undefined) { out[key] = defaultValue() } } } return out } const makeTypeLiteralClass = < Fields extends Struct.Fields, const Records extends IndexSignature.Records >( fields: Fields, records: Records, ast: AST.AST = getDefaultTypeLiteralAST(fields, records) ): TypeLiteral => { return class TypeLiteralClass extends make< Simplify>, Simplify>, | Struct.Context | IndexSignature.Context >(ast) { static override annotations( annotations: Annotations.Schema>> ): TypeLiteral { return makeTypeLiteralClass(this.fields, this.records, mergeSchemaAnnotations(this.ast, annotations)) } static fields = { ...fields } static records = [...records] as Records static make = ( props: Simplify>, options?: MakeOptions ): Simplify> => { const propsWithDefaults: any = lazilyMergeDefaults(fields, { ...props as any }) return getDisableValidationMakeOption(options) ? propsWithDefaults : ParseResult.validateSync(this)(propsWithDefaults) } static pick(...keys: Array): Struct>> { return Struct(struct_.pick(fields, ...keys) as any) } static omit(...keys: Array): Struct>> { return Struct(struct_.omit(fields, ...keys) as any) } } } /** * @category api interface * @since 0.67.0 */ export interface Struct extends TypeLiteral { annotations(annotations: Annotations.Schema>>): Struct /** @since 0.68.17 */ pick>(...keys: Keys): Struct>> /** @since 0.68.17 */ omit>(...keys: Keys): Struct>> } /** * @category constructors * @since 0.67.0 */ export function Struct( fields: Fields, ...records: Records ): TypeLiteral export function Struct(fields: Fields): Struct export function Struct( fields: Fields, ...records: Records ): TypeLiteral { return makeTypeLiteralClass(fields, records) } /** * @category api interface * @since 0.67.14 */ export interface tag extends PropertySignature<":", Tag, never, ":", Tag, true, never> {} /** * Returns a property signature that represents a tag. * A tag is a literal value that is used to distinguish between different types of objects. * The tag is optional when using the `make` method. * * @see {@link TaggedStruct} * * @example * import { Schema } from "@effect/schema" * * const User = Schema.Struct({ * _tag: Schema.tag("User"), * name: Schema.String, * age: Schema.Number * }) * * assert.deepStrictEqual(User.make({ name: "John", age: 44 }), { _tag: "User", name: "John", age: 44 }) * * @since 0.67.14 */ export const tag = (tag: Tag): tag => Literal(tag).pipe(propertySignature, withConstructorDefault(() => tag)) /** * @category api interface * @since 0.67.14 */ export type TaggedStruct = Struct< { _tag: tag } & Fields > /** * A tagged struct is a struct that has a tag property that is used to distinguish between different types of objects. * * The tag is optional when using the `make` method. * * @example * import { Schema } from "@effect/schema" * * const User = Schema.TaggedStruct("User", { * name: Schema.String, * age: Schema.Number * }) * * assert.deepStrictEqual(User.make({ name: "John", age: 44 }), { _tag: "User", name: "John", age: 44 }) * * @category constructors * @since 0.67.14 */ export const TaggedStruct = ( value: Tag, fields: Fields ): TaggedStruct => Struct({ _tag: tag(value), ...fields }) /** * @category api interface * @since 0.67.0 */ export interface Record$ extends TypeLiteral<{}, [{ key: K; value: V }]> { readonly key: K readonly value: V annotations( annotations: Annotations.Schema>> ): Record$ } const makeRecordClass = ( key: K, value: V, ast?: AST.AST ): Record$ => (class RecordClass extends makeTypeLiteralClass({}, [{ key, value }], ast) { static override annotations( annotations: Annotations.Schema>> ) { return makeRecordClass(key, value, mergeSchemaAnnotations(this.ast, annotations)) } static key = key static value = value }) /** * @category constructors * @since 0.67.0 */ export const Record = ( options: { readonly key: K; readonly value: V } ): Record$ => makeRecordClass(options.key, options.value) /** * @category struct transformations * @since 0.67.0 */ export const pick = >(...keys: Keys) => ( self: Schema ): SchemaClass>, Simplify>, R> => make(AST.pick(self.ast, keys)) /** * @category struct transformations * @since 0.67.0 */ export const omit = >(...keys: Keys) => ( self: Schema ): SchemaClass>, Simplify>, R> => make(AST.omit(self.ast, keys)) /** * Given a schema `Schema` and a key `key: K`, this function extracts a specific field from the `A` type, * producing a new schema that represents a transformation from the `{ readonly [key]: I[K] }` type to `A[K]`. * * @example * import * as S from "@effect/schema/Schema" * * // --------------------------------------------- * // use case: pull out a single field from a * // struct through a transformation * // --------------------------------------------- * * const mytable = S.Struct({ * column1: S.NumberFromString, * column2: S.Number * }) * * // const pullOutColumn: S.Schema * const pullOutColumn = mytable.pipe(S.pluck("column1")) * * console.log(S.decodeUnknownEither(S.Array(pullOutColumn))([{ column1: "1", column2: 100 }, { column1: "2", column2: 300 }])) * // Output: { _id: 'Either', _tag: 'Right', right: [ 1, 2 ] } * * @category struct transformations * @since 0.67.0 */ export const pluck: { /** * Given a schema `Schema` and a key `key: K`, this function extracts a specific field from the `A` type, * producing a new schema that represents a transformation from the `{ readonly [key]: I[K] }` type to `A[K]`. * * @example * import * as S from "@effect/schema/Schema" * * // --------------------------------------------- * // use case: pull out a single field from a * // struct through a transformation * // --------------------------------------------- * * const mytable = S.Struct({ * column1: S.NumberFromString, * column2: S.Number * }) * * // const pullOutColumn: S.Schema * const pullOutColumn = mytable.pipe(S.pluck("column1")) * * console.log(S.decodeUnknownEither(S.Array(pullOutColumn))([{ column1: "1", column2: 100 }, { column1: "2", column2: 300 }])) * // Output: { _id: 'Either', _tag: 'Right', right: [ 1, 2 ] } * * @category struct transformations * @since 0.67.0 */ ( key: K ): (schema: Schema) => Schema /** * Given a schema `Schema` and a key `key: K`, this function extracts a specific field from the `A` type, * producing a new schema that represents a transformation from the `{ readonly [key]: I[K] }` type to `A[K]`. * * @example * import * as S from "@effect/schema/Schema" * * // --------------------------------------------- * // use case: pull out a single field from a * // struct through a transformation * // --------------------------------------------- * * const mytable = S.Struct({ * column1: S.NumberFromString, * column2: S.Number * }) * * // const pullOutColumn: S.Schema * const pullOutColumn = mytable.pipe(S.pluck("column1")) * * console.log(S.decodeUnknownEither(S.Array(pullOutColumn))([{ column1: "1", column2: 100 }, { column1: "2", column2: 300 }])) * // Output: { _id: 'Either', _tag: 'Right', right: [ 1, 2 ] } * * @category struct transformations * @since 0.67.0 */ (schema: Schema, key: K): Schema } = dual( 2, ( schema: Schema, key: K ): Schema, R> => { const ps = AST.getPropertyKeyIndexedAccess(AST.typeAST(schema.ast), key) const value = make< /** * Given a schema `Schema` and a key `key: K`, this function extracts a specific field from the `A` type, * producing a new schema that represents a transformation from the `{ readonly [key]: I[K] }` type to `A[K]`. * * @example * import * as S from "@effect/schema/Schema" * * // --------------------------------------------- * // use case: pull out a single field from a * // struct through a transformation * // --------------------------------------------- * * const mytable = S.Struct({ * column1: S.NumberFromString, * column2: S.Number * }) * * // const pullOutColumn: S.Schema * const pullOutColumn = mytable.pipe(S.pluck("column1")) * * console.log(S.decodeUnknownEither(S.Array(pullOutColumn))([{ column1: "1", column2: 100 }, { column1: "2", column2: 300 }])) * // Output: { _id: 'Either', _tag: 'Right', right: [ 1, 2 ] } * * @category struct transformations * @since 0.67.0 */ A[K], /** * Given a schema `Schema` and a key `key: K`, this function extracts a specific field from the `A` type, * producing a new schema that represents a transformation from the `{ readonly [key]: I[K] }` type to `A[K]`. * * @example * import * as S from "@effect/schema/Schema" * * // --------------------------------------------- * // use case: pull out a single field from a * // struct through a transformation * // --------------------------------------------- * * const mytable = S.Struct({ * column1: S.NumberFromString, * column2: S.Number * }) * * // const pullOutColumn: S.Schema * const pullOutColumn = mytable.pipe(S.pluck("column1")) * * console.log(S.decodeUnknownEither(S.Array(pullOutColumn))([{ column1: "1", column2: 100 }, { column1: "2", column2: 300 }])) * // Output: { _id: 'Either', _tag: 'Right', right: [ 1, 2 ] } * * @category struct transformations * @since 0.67.0 */ A[K], /** * Given a schema `Schema` and a key `key: K`, this function extracts a specific field from the `A` type, * producing a new schema that represents a transformation from the `{ readonly [key]: I[K] }` type to `A[K]`. * * @example * import * as S from "@effect/schema/Schema" * * // --------------------------------------------- * // use case: pull out a single field from a * // struct through a transformation * // --------------------------------------------- * * const mytable = S.Struct({ * column1: S.NumberFromString, * column2: S.Number * }) * * // const pullOutColumn: S.Schema * const pullOutColumn = mytable.pipe(S.pluck("column1")) * * console.log(S.decodeUnknownEither(S.Array(pullOutColumn))([{ column1: "1", column2: 100 }, { column1: "2", column2: 300 }])) * // Output: { _id: 'Either', _tag: 'Right', right: [ 1, 2 ] } * * @category struct transformations * @since 0.67.0 */ R >(ps.isOptional ? AST.orUndefined(ps.type) : ps.type) return transform( schema.pipe(pick(key)), value, { strict: true, decode: (a: any) => a[key], encode: (ak) => ps.isOptional && ak === undefined ? {} : { [key]: ak } as any } ) } ) /** * @category branding * @since 0.67.0 */ export interface BrandSchema, I = A, R = never> extends AnnotableClass, A, I, R> { make(a: Brand.Unbranded, options?: MakeOptions): A } /** * @category api interface * @since 0.67.0 */ export interface brand extends BrandSchema & Brand, Schema.Encoded, Schema.Context> { annotations(annotations: Annotations.Schema & Brand>): brand } const makeBrandClass = ( ast: AST.AST ): brand => (class BrandClass extends make & Brand, Schema.Encoded, Schema.Context>(ast) { static override annotations(annotations: Annotations.Schema & Brand>): brand { return makeBrandClass(mergeSchemaAnnotations(this.ast, annotations)) } static make = (a: Brand.Unbranded & Brand>, options?: MakeOptions): Schema.Type & Brand => { return getDisableValidationMakeOption(options) ? a : ParseResult.validateSync(this)(a) } }) /** * Returns a nominal branded schema by applying a brand to a given schema. * * ``` * Schema + B -> Schema> * ``` * * @param self - The input schema to be combined with the brand. * @param brand - The brand to apply. * * @example * import * as Schema from "@effect/schema/Schema" * * const Int = Schema.Number.pipe(Schema.int(), Schema.brand("Int")) * type Int = Schema.Schema.Type // number & Brand<"Int"> * * @category branding * @since 0.67.0 */ export const brand = ( brand: B, annotations?: Annotations.Schema & Brand> ) => (self: S): brand => { const annotation: AST.BrandAnnotation = option_.match(AST.getBrandAnnotation(self.ast), { onNone: () => [brand], onSome: (brands) => [...brands, brand] }) const ast = AST.annotations( self.ast, toASTAnnotations({ // add a default title annotation containing the brand title: String(self.ast) + ` & Brand<${util_.formatUnknown(brand)}>`, ...annotations, [AST.BrandAnnotationId]: annotation }) ) return makeBrandClass(ast) } /** * @category combinators * @since 0.69.0 */ export const partial = ( self: Schema ): SchemaClass<{ [K in keyof A]?: A[K] | undefined }, { [K in keyof I]?: I[K] | undefined }, R> => make(AST.partial(self.ast)) /** * @category combinators * @since 0.69.0 */ export const partialWith: { /** * @category combinators * @since 0.69.0 */ (options: Options): ( self: Schema ) => SchemaClass<{ [K in keyof A]?: A[K] }, { [K in keyof I]?: I[K] }, R> /** * @category combinators * @since 0.69.0 */ ( self: Schema, options: Options ): SchemaClass<{ [K in keyof A]?: A[K] }, { [K in keyof I]?: I[K] }, R> } = dual((args) => isSchema(args[0]), ( self: Schema, options: { readonly exact: true } ): SchemaClass, Partial, R> => make(AST.partial(self.ast, options))) /** * @category combinators * @since 0.67.0 */ export const required = ( self: Schema ): SchemaClass<{ [K in keyof A]-?: A[K] }, { [K in keyof I]-?: I[K] }, R> => make(AST.required(self.ast)) /** * @category api interface * @since 0.67.0 */ export interface mutable extends AnnotableClass< mutable, SimplifyMutable>, SimplifyMutable>, Schema.Context > {} /** * Creates a new schema with shallow mutability applied to its properties. * * @param schema - The original schema to make properties mutable (shallowly). * * @category combinators * @since 0.67.0 */ export const mutable = (schema: S): mutable => make(AST.mutable(schema.ast)) const intersectTypeLiterals = ( x: AST.AST, y: AST.AST, path: ReadonlyArray ): AST.TypeLiteral => { if (AST.isTypeLiteral(x) && AST.isTypeLiteral(y)) { const propertySignatures = [...x.propertySignatures] for (const ps of y.propertySignatures) { const name = ps.name const i = propertySignatures.findIndex((ps) => ps.name === name) if (i === -1) { propertySignatures.push(ps) } else { const { isOptional, type } = propertySignatures[i] propertySignatures[i] = new AST.PropertySignature( name, extendAST(type, ps.type, path.concat(name)), isOptional, true ) } } return new AST.TypeLiteral( propertySignatures, x.indexSignatures.concat(y.indexSignatures) ) } throw new Error(errors_.getSchemaExtendErrorMessage(x, y, path)) } const preserveRefinementAnnotations = AST.blackListAnnotations([ AST.IdentifierAnnotationId ]) const addRefinementToMembers = (refinement: AST.Refinement, asts: ReadonlyArray): Array => asts.map((ast) => new AST.Refinement(ast, refinement.filter, preserveRefinementAnnotations(refinement))) const extendAST = ( x: AST.AST, y: AST.AST, path: ReadonlyArray ): AST.AST => AST.Union.make(intersectUnionMembers([x], [y], path)) const getTypes = (ast: AST.AST): ReadonlyArray => AST.isUnion(ast) ? ast.types : [ast] const intersectUnionMembers = ( xs: ReadonlyArray, ys: ReadonlyArray, path: ReadonlyArray ): Array => array_.flatMap(xs, (x) => array_.flatMap(ys, (y) => { switch (y._tag) { case "Literal": { if ( (Predicate.isString(y.literal) && AST.isStringKeyword(x) || (Predicate.isNumber(y.literal) && AST.isNumberKeyword(x)) || (Predicate.isBoolean(y.literal) && AST.isBooleanKeyword(x))) ) { return [y] } break } case "StringKeyword": { if (y === AST.stringKeyword) { if (AST.isStringKeyword(x) || (AST.isLiteral(x) && Predicate.isString(x.literal))) { return [x] } else if (AST.isRefinement(x)) { return addRefinementToMembers(x, intersectUnionMembers(getTypes(x.from), [y], path)) } } else if (x === AST.stringKeyword) { return [y] } break } case "NumberKeyword": { if (y === AST.numberKeyword) { if (AST.isNumberKeyword(x) || (AST.isLiteral(x) && Predicate.isNumber(x.literal))) { return [x] } else if (AST.isRefinement(x)) { return addRefinementToMembers(x, intersectUnionMembers(getTypes(x.from), [y], path)) } } else if (x === AST.numberKeyword) { return [y] } break } case "BooleanKeyword": { if (y === AST.booleanKeyword) { if (AST.isBooleanKeyword(x) || (AST.isLiteral(x) && Predicate.isBoolean(x.literal))) { return [x] } else if (AST.isRefinement(x)) { return addRefinementToMembers(x, intersectUnionMembers(getTypes(x.from), [y], path)) } } else if (x === AST.booleanKeyword) { return [y] } break } case "Union": return intersectUnionMembers(getTypes(x), y.types, path) case "Suspend": return [new AST.Suspend(() => extendAST(x, y.f(), path))] case "Refinement": return addRefinementToMembers(y, intersectUnionMembers(getTypes(x), getTypes(y.from), path)) case "TypeLiteral": { switch (x._tag) { case "Union": return intersectUnionMembers(x.types, [y], path) case "Suspend": return [new AST.Suspend(() => extendAST(x.f(), y, path))] case "Refinement": return addRefinementToMembers(x, intersectUnionMembers(getTypes(x.from), [y], path)) case "TypeLiteral": return [intersectTypeLiterals(x, y, path)] case "Transformation": { if (AST.isTypeLiteralTransformation(x.transformation)) { return [ new AST.Transformation( intersectTypeLiterals(x.from, y, path), intersectTypeLiterals(x.to, AST.typeAST(y), path), new AST.TypeLiteralTransformation( x.transformation.propertySignatureTransformations ) ) ] } break } } break } case "Transformation": { if (AST.isTypeLiteralTransformation(y.transformation)) { switch (x._tag) { case "Union": return intersectUnionMembers(x.types, [y], path) case "Suspend": return [new AST.Suspend(() => extendAST(x.f(), y, path))] case "Refinement": return addRefinementToMembers(x, intersectUnionMembers(getTypes(x.from), [y], path)) case "TypeLiteral": return [ new AST.Transformation( intersectTypeLiterals(x, y.from, path), intersectTypeLiterals(AST.typeAST(x), y.to, path), new AST.TypeLiteralTransformation( y.transformation.propertySignatureTransformations ) ) ] case "Transformation": { if (AST.isTypeLiteralTransformation(x.transformation)) { return [ new AST.Transformation( intersectTypeLiterals(x.from, y.from, path), intersectTypeLiterals(x.to, y.to, path), new AST.TypeLiteralTransformation( y.transformation.propertySignatureTransformations.concat( x.transformation.propertySignatureTransformations ) ) ) ] } } break } } break } } throw new Error(errors_.getSchemaExtendErrorMessage(x, y, path)) })) /** * @category api interface * @since 0.67.0 */ export interface extend extends AnnotableClass< extend, Schema.Type & Schema.Type, Schema.Encoded & Schema.Encoded, Schema.Context | Schema.Context > {} /** * Extends a schema with another schema. * * Not all extensions are supported, and their support depends on the nature of the involved schemas. * * Possible extensions include: * - `Schema.String` with another `Schema.String` refinement or a string literal * - `Schema.Number` with another `Schema.Number` refinement or a number literal * - `Schema.Boolean` with another `Schema.Boolean` refinement or a boolean literal * - A struct with another struct where overlapping fields support extension * - A struct with in index signature * - A struct with a union of supported schemas * - A refinement of a struct with a supported schema * - A suspend of a struct with a supported schema * * @example * import * as Schema from "@effect/schema/Schema" * * const schema = Schema.Struct({ * a: Schema.String, * b: Schema.String * }) * * // const extended: Schema.Schema< * // { * // readonly a: string * // readonly b: string * // } & { * // readonly c: string * // } & { * // readonly [x: string]: string * // } * // > * const extended = Schema.asSchema(schema.pipe( * Schema.extend(Schema.Struct({ c: Schema.String })), // <= you can add more fields * Schema.extend(Schema.Record({ key: Schema.String, value: Schema.String })) // <= you can add index signatures * )) * * @category combinators * @since 0.67.0 */ export const extend: { /** * Extends a schema with another schema. * * Not all extensions are supported, and their support depends on the nature of the involved schemas. * * Possible extensions include: * - `Schema.String` with another `Schema.String` refinement or a string literal * - `Schema.Number` with another `Schema.Number` refinement or a number literal * - `Schema.Boolean` with another `Schema.Boolean` refinement or a boolean literal * - A struct with another struct where overlapping fields support extension * - A struct with in index signature * - A struct with a union of supported schemas * - A refinement of a struct with a supported schema * - A suspend of a struct with a supported schema * * @example * import * as Schema from "@effect/schema/Schema" * * const schema = Schema.Struct({ * a: Schema.String, * b: Schema.String * }) * * // const extended: Schema.Schema< * // { * // readonly a: string * // readonly b: string * // } & { * // readonly c: string * // } & { * // readonly [x: string]: string * // } * // > * const extended = Schema.asSchema(schema.pipe( * Schema.extend(Schema.Struct({ c: Schema.String })), // <= you can add more fields * Schema.extend(Schema.Record({ key: Schema.String, value: Schema.String })) // <= you can add index signatures * )) * * @category combinators * @since 0.67.0 */ (that: That): (self: Self) => extend /** * Extends a schema with another schema. * * Not all extensions are supported, and their support depends on the nature of the involved schemas. * * Possible extensions include: * - `Schema.String` with another `Schema.String` refinement or a string literal * - `Schema.Number` with another `Schema.Number` refinement or a number literal * - `Schema.Boolean` with another `Schema.Boolean` refinement or a boolean literal * - A struct with another struct where overlapping fields support extension * - A struct with in index signature * - A struct with a union of supported schemas * - A refinement of a struct with a supported schema * - A suspend of a struct with a supported schema * * @example * import * as Schema from "@effect/schema/Schema" * * const schema = Schema.Struct({ * a: Schema.String, * b: Schema.String * }) * * // const extended: Schema.Schema< * // { * // readonly a: string * // readonly b: string * // } & { * // readonly c: string * // } & { * // readonly [x: string]: string * // } * // > * const extended = Schema.asSchema(schema.pipe( * Schema.extend(Schema.Struct({ c: Schema.String })), // <= you can add more fields * Schema.extend(Schema.Record({ key: Schema.String, value: Schema.String })) // <= you can add index signatures * )) * * @category combinators * @since 0.67.0 */ (self: Self, that: That): extend } = dual( 2, (self: Self, that: That) => make(extendAST(self.ast, that.ast, [])) ) /** * @category combinators * @since 0.67.0 */ export const compose: { /** * @category combinators * @since 0.67.0 */ (to: Schema): (from: Schema) => SchemaClass /** * @category combinators * @since 0.67.0 */ (to: Schema): (from: Schema) => SchemaClass /** * @category combinators * @since 0.67.0 */ ( to: Schema, options?: { readonly strict: true } ): (from: Schema) => SchemaClass /** * @category combinators * @since 0.67.0 */ ( to: Schema, options: { readonly strict: false } ): (from: Schema) => SchemaClass /** * @category combinators * @since 0.67.0 */ (from: Schema, to: Schema): SchemaClass /** * @category combinators * @since 0.67.0 */ (from: Schema, to: Schema): SchemaClass /** * @category combinators * @since 0.67.0 */ ( from: Schema, to: Schema, options?: { readonly strict: true } ): SchemaClass /** * @category combinators * @since 0.67.0 */ ( from: Schema, to: Schema, options: { readonly strict: false } ): SchemaClass } = dual( (args) => isSchema(args[1]), (from: Schema, to: Schema): SchemaClass => make(AST.compose(from.ast, to.ast)) ) /** * @category api interface * @since 0.67.0 */ export interface suspend extends AnnotableClass, A, I, R> {} /** * @category constructors * @since 0.67.0 */ export const suspend = (f: () => Schema): suspend => make(new AST.Suspend(() => f().ast)) /** * @since 0.68.8 * @category symbol */ export const refineTypeId: unique symbol = Symbol.for("@effect/schema/refine") /** * @since 0.68.8 * @category symbol */ export type refineTypeId = typeof refineTypeId /** * @category api interface * @since 0.67.0 */ export interface refine extends AnnotableClass, A, Schema.Encoded, Schema.Context> { readonly [refineTypeId]: From readonly from: From readonly filter: ( a: Schema.Type, options: ParseOptions, self: AST.Refinement ) => option_.Option make(a: Schema.Type, options?: MakeOptions): A } const makeRefineClass = ( from: From, filter: ( a: Schema.Type, options: ParseOptions, self: AST.Refinement ) => option_.Option, ast: AST.AST ): refine => (class RefineClass extends make, Schema.Context>(ast) { static override annotations(annotations: Annotations.Schema): refine { return makeRefineClass(this.from, this.filter, mergeSchemaAnnotations(this.ast, annotations)) } static [refineTypeId] = from static from = from static filter = filter static make = (a: Schema.Type, options?: MakeOptions): A => { return getDisableValidationMakeOption(options) ? a : ParseResult.validateSync(this)(a) } }) /** * @category api interface * @since 0.67.0 */ export interface filter extends refine, From> {} const fromFilterPredicateReturnTypeItem = ( item: FilterOutput, ast: AST.Refinement | AST.Transformation, input: unknown ): option_.Option => { if (Predicate.isBoolean(item)) { return item ? option_.none() : option_.some(new ParseResult.Type(ast, input)) } if (Predicate.isString(item)) { return option_.some(new ParseResult.Type(ast, input, item)) } if (item !== undefined) { if ("_tag" in item) { return option_.some(item) } const issue = new ParseResult.Type(ast, input, item.message) return option_.some( array_.isNonEmptyReadonlyArray(item.path) ? new ParseResult.Pointer(item.path, input, issue) : issue ) } return option_.none() } const toFilterParseIssue = ( out: FilterReturnType, ast: AST.Refinement | AST.Transformation, input: unknown ): option_.Option => { if (util_.isSingle(out)) { return fromFilterPredicateReturnTypeItem(out, ast, input) } if (array_.isNonEmptyReadonlyArray(out)) { const issues = array_.filterMap(out, (issue) => fromFilterPredicateReturnTypeItem(issue, ast, input)) if (array_.isNonEmptyReadonlyArray(issues)) { return option_.some(issues.length === 1 ? issues[0] : new ParseResult.Composite(ast, input, issues)) } } return option_.none() } /** * @category filtering * @since 0.68.0 */ export interface FilterIssue { readonly path: ReadonlyArray readonly message: string } /** * @category filtering * @since 0.68.0 */ export type FilterOutput = undefined | boolean | string | ParseResult.ParseIssue | FilterIssue type FilterReturnType = FilterOutput | ReadonlyArray /** * @category filtering * @since 0.67.0 */ export function filter( refinement: (a: A, options: ParseOptions, self: AST.Refinement) => a is B, annotations?: Annotations.Filter ): (self: Schema) => refine> export function filter( refinement: (a: A, options: ParseOptions, self: AST.Refinement) => a is B, annotations?: Annotations.Filter ): (self: Schema) => refine> export function filter( predicate: ( a: Types.NoInfer>, options: ParseOptions, self: AST.Refinement ) => FilterReturnType, annotations?: Annotations.Filter>> ): (self: S) => filter export function filter( predicate: ( a: A, options: ParseOptions, self: AST.Refinement ) => FilterReturnType, annotations?: Annotations.Filter ): (self: Schema) => refine> { return (self: Schema) => { function filter(input: A, options: AST.ParseOptions, ast: AST.Refinement) { return toFilterParseIssue(predicate(input, options, ast), ast, input) } const ast = new AST.Refinement( self.ast, filter, toASTAnnotations(annotations) ) return makeRefineClass(self, filter, ast) } } /** * @category api interface * @since 0.68.17 */ export interface filterEffect extends transformOrFail>, FD> {} /** * @category transformations * @since 0.68.17 */ export const filterEffect: { /** * @category transformations * @since 0.68.17 */ ( f: ( a: Types.NoInfer>, options: ParseOptions, self: AST.Transformation ) => Effect.Effect ): (self: S) => filterEffect /** * @category transformations * @since 0.68.17 */ ( self: S, f: ( a: Types.NoInfer>, options: ParseOptions, self: AST.Transformation ) => Effect.Effect ): filterEffect } = dual(2, ( self: S, f: ( a: Types.NoInfer>, options: ParseOptions, self: AST.Transformation ) => Effect.Effect ): filterEffect => transformOrFail( self, typeSchema(self), { strict: true, decode: (a, options, ast) => ParseResult.flatMap( f(a, options, ast), (filterReturnType) => option_.match(toFilterParseIssue(filterReturnType, ast, a), { onNone: () => ParseResult.succeed(a), onSome: ParseResult.fail }) ), encode: ParseResult.succeed } )) /** * @category api interface * @since 0.67.0 */ export interface transformOrFail extends AnnotableClass< transformOrFail, Schema.Type, Schema.Encoded, Schema.Context | Schema.Context | R > { readonly from: From readonly to: To } const makeTransformationClass = ( from: From, to: To, ast: AST.AST ): transformOrFail< From, To, R > => (class TransformationClass extends make, Schema.Encoded, Schema.Context | Schema.Context | R>(ast) { static override annotations(annotations: Annotations.Schema>) { return makeTransformationClass( this.from, this.to, mergeSchemaAnnotations(this.ast, annotations) ) } static from = from static to = to }) /** * Create a new `Schema` by transforming the input and output of an existing `Schema` * using the provided decoding functions. * * @category transformations * @since 0.67.0 */ export const transformOrFail: { /** * Create a new `Schema` by transforming the input and output of an existing `Schema` * using the provided decoding functions. * * @category transformations * @since 0.67.0 */ ( to: To, options: { readonly decode: ( fromA: Schema.Type, options: ParseOptions, ast: AST.Transformation, fromI: Schema.Encoded ) => Effect.Effect, ParseResult.ParseIssue, RD> readonly encode: ( toI: Schema.Encoded, options: ParseOptions, ast: AST.Transformation, toA: Schema.Type ) => Effect.Effect, ParseResult.ParseIssue, RE> readonly strict?: true } | { readonly decode: ( fromA: Schema.Type, options: ParseOptions, ast: AST.Transformation, fromI: Schema.Encoded ) => Effect.Effect readonly encode: ( toI: Schema.Encoded, options: ParseOptions, ast: AST.Transformation, toA: Schema.Type ) => Effect.Effect readonly strict: false } ): (from: From) => transformOrFail /** * Create a new `Schema` by transforming the input and output of an existing `Schema` * using the provided decoding functions. * * @category transformations * @since 0.67.0 */ ( from: From, to: To, options: { readonly decode: ( fromA: Schema.Type, options: ParseOptions, ast: AST.Transformation, fromI: Schema.Encoded ) => Effect.Effect, ParseResult.ParseIssue, RD> readonly encode: ( toI: Schema.Encoded, options: ParseOptions, ast: AST.Transformation, toA: Schema.Type ) => Effect.Effect, ParseResult.ParseIssue, RE> readonly strict?: true } | { readonly decode: ( fromA: Schema.Type, options: ParseOptions, ast: AST.Transformation, fromI: Schema.Encoded ) => Effect.Effect readonly encode: ( toI: Schema.Encoded, options: ParseOptions, ast: AST.Transformation, toA: Schema.Type ) => Effect.Effect readonly strict: false } ): transformOrFail } = dual((args) => isSchema(args[0]) && isSchema(args[1]), ( from: Schema, to: Schema, options: { readonly decode: ( fromA: FromA, options: ParseOptions, ast: AST.Transformation, fromI: FromI ) => Effect.Effect readonly encode: ( toI: ToI, options: ParseOptions, ast: AST.Transformation, toA: ToA ) => Effect.Effect } ): Schema => makeTransformationClass( from, to, new AST.Transformation( from.ast, to.ast, new AST.FinalTransformation(options.decode, options.encode) ) )) /** * @category api interface * @since 0.67.0 */ export interface transform extends transformOrFail { annotations(annotations: Annotations.Schema>): transform } /** * Create a new `Schema` by transforming the input and output of an existing `Schema` * using the provided mapping functions. * * @category transformations * @since 0.67.0 */ export const transform: { /** * Create a new `Schema` by transforming the input and output of an existing `Schema` * using the provided mapping functions. * * @category transformations * @since 0.67.0 */ ( to: To, options: { readonly decode: (fromA: Schema.Type, fromI: Schema.Encoded) => Schema.Encoded readonly encode: (toI: Schema.Encoded, toA: Schema.Type) => Schema.Type readonly strict?: true } | { readonly decode: (fromA: Schema.Type, fromI: Schema.Encoded) => unknown readonly encode: (toI: Schema.Encoded, toA: Schema.Type) => unknown readonly strict: false } ): (from: From) => transform /** * Create a new `Schema` by transforming the input and output of an existing `Schema` * using the provided mapping functions. * * @category transformations * @since 0.67.0 */ ( from: From, to: To, options: { readonly decode: (fromA: Schema.Type, fromI: Schema.Encoded) => Schema.Encoded readonly encode: (toI: Schema.Encoded, toA: Schema.Type) => Schema.Type readonly strict?: true } | { readonly decode: (fromA: Schema.Type, fromI: Schema.Encoded) => unknown readonly encode: (toI: Schema.Encoded, toA: Schema.Type) => unknown readonly strict: false } ): transform } = dual( (args) => isSchema(args[0]) && isSchema(args[1]), ( from: Schema, to: Schema, options: { readonly decode: (fromA: FromA, fromI: FromI) => ToI readonly encode: (toI: ToI, toA: ToA) => FromA } ): Schema => transformOrFail( from, to, { strict: true, decode: (fromA, _options, _ast, toA) => ParseResult.succeed(options.decode(fromA, toA)), encode: (toI, _options, _ast, toA) => ParseResult.succeed(options.encode(toI, toA)) } ) ) /** * @category api interface * @since 0.67.0 */ export interface transformLiteral extends Annotable, Type, Encoded> {} /** * Creates a new `Schema` which transforms literal values. * * @example * import * as S from "@effect/schema/Schema" * * const schema = S.transformLiteral(0, "a") * * assert.deepStrictEqual(S.decodeSync(schema)(0), "a") * * @category constructors * @since 0.67.0 */ export const transformLiteral = ( from: Encoded, to: Type ): transformLiteral => transform(Literal(from), Literal(to), { strict: true, decode: () => to, encode: () => from }) /** * Creates a new `Schema` which maps between corresponding literal values. * * @example * import * as S from "@effect/schema/Schema" * * const Animal = S.transformLiterals( * [0, "cat"], * [1, "dog"], * [2, "cow"] * ) * * assert.deepStrictEqual(S.decodeSync(Animal)(1), "dog") * * @category constructors * @since 0.67.0 */ export function transformLiterals>( ...pairs: A ): Union<{ -readonly [I in keyof A]: transformLiteral }> export function transformLiterals( pairs: [Encoded, Type] ): transformLiteral export function transformLiterals< const A extends ReadonlyArray >(...pairs: A): Schema export function transformLiterals< const A extends ReadonlyArray >(...pairs: A): Schema { return Union(...pairs.map(([from, to]) => transformLiteral(from, to))) } /** * Attaches a property signature with the specified key and value to the schema. * This API is useful when you want to add a property to your schema which doesn't describe the shape of the input, * but rather maps to another schema, for example when you want to add a discriminant to a simple union. * * @param self - The input schema. * @param key - The name of the property to add to the schema. * @param value - The value of the property to add to the schema. * * @example * import * as S from "@effect/schema/Schema" * import { pipe } from "effect/Function" * * const Circle = S.Struct({ radius: S.Number }) * const Square = S.Struct({ sideLength: S.Number }) * const Shape = S.Union( * Circle.pipe(S.attachPropertySignature("kind", "circle")), * Square.pipe(S.attachPropertySignature("kind", "square")) * ) * * assert.deepStrictEqual(S.decodeSync(Shape)({ radius: 10 }), { * kind: "circle", * radius: 10 * }) * * @category combinators * @since 0.67.0 */ export const attachPropertySignature: { /** * Attaches a property signature with the specified key and value to the schema. * This API is useful when you want to add a property to your schema which doesn't describe the shape of the input, * but rather maps to another schema, for example when you want to add a discriminant to a simple union. * * @param self - The input schema. * @param key - The name of the property to add to the schema. * @param value - The value of the property to add to the schema. * * @example * import * as S from "@effect/schema/Schema" * import { pipe } from "effect/Function" * * const Circle = S.Struct({ radius: S.Number }) * const Square = S.Struct({ sideLength: S.Number }) * const Shape = S.Union( * Circle.pipe(S.attachPropertySignature("kind", "circle")), * Square.pipe(S.attachPropertySignature("kind", "square")) * ) * * assert.deepStrictEqual(S.decodeSync(Shape)({ radius: 10 }), { * kind: "circle", * radius: 10 * }) * * @category combinators * @since 0.67.0 */ ( key: K, value: V, annotations?: Annotations.Schema> ): ( schema: SchemaClass ) => SchemaClass, I, R> /** * Attaches a property signature with the specified key and value to the schema. * This API is useful when you want to add a property to your schema which doesn't describe the shape of the input, * but rather maps to another schema, for example when you want to add a discriminant to a simple union. * * @param self - The input schema. * @param key - The name of the property to add to the schema. * @param value - The value of the property to add to the schema. * * @example * import * as S from "@effect/schema/Schema" * import { pipe } from "effect/Function" * * const Circle = S.Struct({ radius: S.Number }) * const Square = S.Struct({ sideLength: S.Number }) * const Shape = S.Union( * Circle.pipe(S.attachPropertySignature("kind", "circle")), * Square.pipe(S.attachPropertySignature("kind", "square")) * ) * * assert.deepStrictEqual(S.decodeSync(Shape)({ radius: 10 }), { * kind: "circle", * radius: 10 * }) * * @category combinators * @since 0.67.0 */ ( schema: Schema, key: K, value: V, annotations?: Annotations.Schema> ): SchemaClass, I, R> } = dual( (args) => isSchema(args[0]), ( schema: Schema, key: K, value: V, annotations?: Annotations.Schema> ): SchemaClass, I, R> => { const ast = extend( typeSchema(schema), Struct({ [key]: Predicate.isSymbol(value) ? UniqueSymbolFromSelf(value) : Literal(value) }) ).ast return make( new AST.Transformation( schema.ast, annotations ? mergeSchemaAnnotations(ast, annotations) : ast, new AST.TypeLiteralTransformation( [ new AST.PropertySignatureTransformation( key, key, () => option_.some(value), () => option_.none() ) ] ) ) ) } ) /** * @category annotations * @since 0.67.0 */ export declare namespace Annotations { /** * @category annotations * @since 0.67.0 */ export interface Doc extends AST.Annotations { readonly title?: AST.TitleAnnotation readonly description?: AST.DescriptionAnnotation readonly documentation?: AST.DocumentationAnnotation readonly examples?: AST.ExamplesAnnotation readonly default?: AST.DefaultAnnotation } /** * @since 0.67.0 */ export interface Schema = readonly []> extends Doc { readonly identifier?: AST.IdentifierAnnotation readonly message?: AST.MessageAnnotation readonly typeId?: AST.TypeAnnotation | { id: AST.TypeAnnotation; annotation: unknown } readonly jsonSchema?: AST.JSONSchemaAnnotation readonly arbitrary?: ( ...arbitraries: [ ...{ readonly [K in keyof TypeParameters]: LazyArbitrary }, ctx: GenerationContext ] ) => LazyArbitrary readonly pretty?: ( ...pretties: { readonly [K in keyof TypeParameters]: pretty_.Pretty } ) => pretty_.Pretty readonly equivalence?: ( ...equivalences: { readonly [K in keyof TypeParameters]: Equivalence.Equivalence } ) => Equivalence.Equivalence readonly concurrency?: AST.ConcurrencyAnnotation readonly batching?: AST.BatchingAnnotation readonly parseIssueTitle?: AST.ParseIssueTitleAnnotation readonly parseOptions?: AST.ParseOptions readonly decodingFallback?: AST.DecodingFallbackAnnotation } /** * @since 0.67.0 */ export interface Filter extends Schema {} } /** * Merges a set of new annotations with existing ones, potentially overwriting * any duplicates. * * @category annotations * @since 0.67.0 */ export const annotations: { /** * Merges a set of new annotations with existing ones, potentially overwriting * any duplicates. * * @category annotations * @since 0.67.0 */ (annotations: Annotations.Schema>): (self: S) => Annotable.Self /** * Merges a set of new annotations with existing ones, potentially overwriting * any duplicates. * * @category annotations * @since 0.67.0 */ (self: S, annotations: Annotations.Schema>): Annotable.Self } = dual( 2, (self: Schema, annotations: Annotations.Schema): Schema => self.annotations(annotations) ) type Rename = { [ K in keyof A as K extends keyof M ? M[K] extends PropertyKey ? M[K] : never : K ]: A[K] } /** * @category renaming * @since 0.67.0 */ export const rename: { /** * @category renaming * @since 0.67.0 */ < A, const M extends & { readonly [K in keyof A]?: PropertyKey } & { readonly [K in Exclude]: never } >(mapping: M): (self: Schema) => SchemaClass>, I, R> /** * @category renaming * @since 0.67.0 */ < A, I, R, const M extends & { readonly [K in keyof A]?: PropertyKey } & { readonly [K in Exclude]: never } >(self: Schema, mapping: M): SchemaClass>, I, R> } = dual( 2, < A, I, R, const M extends & { readonly [K in keyof A]?: PropertyKey } & { readonly [K in Exclude]: never } >( self: Schema, mapping: M ): SchemaClass>, I, R> => make(AST.rename(self.ast, mapping)) ) /** * @category type id * @since 0.67.0 */ export const TrimmedTypeId: unique symbol = Symbol.for("@effect/schema/TypeId/Trimmed") /** * Verifies that a string contains no leading or trailing whitespaces. * * Note. This combinator does not make any transformations, it only validates. * If what you were looking for was a combinator to trim strings, then check out the `trim` combinator. * * @category string filters * @since 0.67.0 */ export const trimmed = (annotations?: Annotations.Filter) => (self: Schema): filter> => self.pipe( filter((a) => a === a.trim(), { typeId: TrimmedTypeId, description: "a string with no leading or trailing whitespace", jsonSchema: { pattern: "^\\S[\\s\\S]*\\S$|^\\S$|^$" }, ...annotations }) ) /** * @category type id * @since 0.67.0 */ export const MaxLengthTypeId: unique symbol = filters_.MaxLengthTypeId /** * @category type id * @since 0.67.0 */ export type MaxLengthTypeId = typeof MaxLengthTypeId /** * @category string filters * @since 0.67.0 */ export const maxLength = ( maxLength: number, annotations?: Annotations.Filter ) => (self: Schema): filter> => self.pipe( filter( (a) => a.length <= maxLength, { typeId: MaxLengthTypeId, description: `a string at most ${maxLength} character(s) long`, jsonSchema: { maxLength }, ...annotations } ) ) /** * @category type id * @since 0.67.0 */ export const MinLengthTypeId: unique symbol = filters_.MinLengthTypeId /** * @category type id * @since 0.67.0 */ export type MinLengthTypeId = typeof MinLengthTypeId /** * @category string filters * @since 0.67.0 */ export const minLength = ( minLength: number, annotations?: Annotations.Filter ) => (self: Schema): filter> => self.pipe( filter( (a) => a.length >= minLength, { typeId: MinLengthTypeId, description: `a string at least ${minLength} character(s) long`, jsonSchema: { minLength }, ...annotations } ) ) /** * @category type id * @since 0.67.0 */ export const PatternTypeId: unique symbol = Symbol.for("@effect/schema/TypeId/Pattern") /** * @category string filters * @since 0.67.0 */ export const pattern = ( regex: RegExp, annotations?: Annotations.Filter ) => (self: Schema): filter> => { const pattern = regex.source return self.pipe( filter( (a): a is A => { // The following line ensures that `lastIndex` is reset to `0` in case the user has specified the `g` flag regex.lastIndex = 0 return regex.test(a) }, { typeId: { id: PatternTypeId, annotation: { regex } }, description: `a string matching the pattern ${pattern}`, jsonSchema: { pattern }, arbitrary: () => (fc) => fc.stringMatching(regex) as any, ...annotations } ) ) } /** * @category type id * @since 0.67.0 */ export const StartsWithTypeId: unique symbol = Symbol.for("@effect/schema/TypeId/StartsWith") /** * @category string filters * @since 0.67.0 */ export const startsWith = ( startsWith: string, annotations?: Annotations.Filter ) => (self: Schema): filter> => self.pipe( filter( (a) => a.startsWith(startsWith), { typeId: { id: StartsWithTypeId, annotation: { startsWith } }, description: `a string starting with ${JSON.stringify(startsWith)}`, jsonSchema: { pattern: `^${startsWith}` }, ...annotations } ) ) /** * @category type id * @since 0.67.0 */ export const EndsWithTypeId: unique symbol = Symbol.for("@effect/schema/TypeId/EndsWith") /** * @category string filters * @since 0.67.0 */ export const endsWith = ( endsWith: string, annotations?: Annotations.Filter ) => (self: Schema): filter> => self.pipe( filter( (a) => a.endsWith(endsWith), { typeId: { id: EndsWithTypeId, annotation: { endsWith } }, description: `a string ending with ${JSON.stringify(endsWith)}`, jsonSchema: { pattern: `^.*${endsWith}$` }, ...annotations } ) ) /** * @category type id * @since 0.67.0 */ export const IncludesTypeId: unique symbol = Symbol.for("@effect/schema/TypeId/Includes") /** * @category string filters * @since 0.67.0 */ export const includes = ( searchString: string, annotations?: Annotations.Filter ) => (self: Schema): filter> => self.pipe( filter( (a) => a.includes(searchString), { typeId: { id: IncludesTypeId, annotation: { includes: searchString } }, description: `a string including ${JSON.stringify(searchString)}`, jsonSchema: { pattern: `.*${searchString}.*` }, ...annotations } ) ) /** * @category type id * @since 0.67.0 */ export const LowercasedTypeId: unique symbol = Symbol.for("@effect/schema/TypeId/Lowercased") /** * Verifies that a string is lowercased. * * @category string filters * @since 0.67.0 */ export const lowercased = (annotations?: Annotations.Filter) => (self: Schema): filter> => self.pipe( filter((a) => a === a.toLowerCase(), { typeId: LowercasedTypeId, description: "a lowercase string", ...annotations }) ) /** * @category string constructors * @since 0.67.0 */ export class Lowercased extends String$.pipe( lowercased({ identifier: "Lowercased", title: "Lowercased" }) ) {} /** * @category type id * @since 0.68.18 */ export const CapitalizedTypeId: unique symbol = Symbol.for("@effect/schema/TypeId/Capitalized") /** * Verifies that a string is capitalized. * * @category string filters * @since 0.68.18 */ export const capitalized = (annotations?: Annotations.Filter) => (self: Schema): filter> => self.pipe( filter((a) => a[0]?.toUpperCase() === a[0], { typeId: CapitalizedTypeId, description: "a capitalized string", ...annotations }) ) /** * @category string constructors * @since 0.68.18 */ export class Capitalized extends String$.pipe( capitalized({ identifier: "Capitalized", title: "Capitalized" }) ) {} /** * @category type id * @since 0.68.18 */ export const UncapitalizedTypeId: unique symbol = Symbol.for("@effect/schema/TypeId/Uncapitalized") /** * Verifies that a string is uncapitalized. * * @category string filters * @since 0.68.18 */ export const uncapitalized = (annotations?: Annotations.Filter) => (self: Schema): filter> => self.pipe( filter((a) => a[0]?.toLowerCase() === a[0], { typeId: UncapitalizedTypeId, description: "a uncapitalized string", ...annotations }) ) /** * @category string constructors * @since 0.68.18 */ export class Uncapitalized extends String$.pipe( uncapitalized({ identifier: "Uncapitalized", title: "Uncapitalized" }) ) {} /** * @category type id * @since 0.67.0 */ export const UppercasedTypeId: unique symbol = Symbol.for("@effect/schema/TypeId/Uppercased") /** * Verifies that a string is uppercased. * * @category string filters * @since 0.67.0 */ export const uppercased = (annotations?: Annotations.Filter) => (self: Schema): filter> => self.pipe( filter((a) => a === a.toUpperCase(), { typeId: UppercasedTypeId, description: "an uppercase string", ...annotations }) ) /** * @category string constructors * @since 0.67.0 */ export class Uppercased extends String$.pipe( uppercased({ identifier: "Uppercased", title: "Uppercased" }) ) {} /** * @category type id * @since 0.67.0 */ export const LengthTypeId: unique symbol = filters_.LengthTypeId /** * @category type id * @since 0.67.0 */ export type LengthTypeId = typeof LengthTypeId /** * @category string filters * @since 0.67.0 */ export const length = ( length: number | { readonly min: number; readonly max: number }, annotations?: Annotations.Filter ) => (self: Schema): filter> => { const minLength = Predicate.isObject(length) ? Math.max(0, Math.floor(length.min)) : Math.max(0, Math.floor(length)) const maxLength = Predicate.isObject(length) ? Math.max(minLength, Math.floor(length.max)) : minLength if (minLength !== maxLength) { return self.pipe( filter((a) => a.length >= minLength && a.length <= maxLength, { typeId: LengthTypeId, description: `a string at least ${minLength} character(s) and at most ${maxLength} character(s) long`, jsonSchema: { minLength, maxLength }, ...annotations }) ) } return self.pipe( filter((a) => a.length === minLength, { typeId: LengthTypeId, description: minLength === 1 ? `a single character` : `a string ${minLength} character(s) long`, jsonSchema: { minLength, maxLength: minLength }, ...annotations }) ) } /** * A schema representing a single character. * * @category string constructors * @since 0.67.0 */ export class Char extends String$.pipe(length(1, { identifier: "Char" })) {} /** * @category string filters * @since 0.69.0 */ export const nonEmptyString = ( annotations?: Annotations.Filter ): (self: Schema) => filter> => minLength(1, { description: "a non empty string", ...annotations }) /** * This schema converts a string to lowercase. * * @category string transformations * @since 0.67.0 */ export class Lowercase extends transform( String$.annotations({ description: "a string that will be converted to lowercase" }), Lowercased, { strict: true, decode: (s) => s.toLowerCase(), encode: identity } ).annotations({ identifier: "Lowercase" }) {} /** * This schema converts a string to uppercase. * * @category string transformations * @since 0.67.0 */ export class Uppercase extends transform( String$.annotations({ description: "a string that will be converted to uppercase" }), Uppercased, { strict: true, decode: (s) => s.toUpperCase(), encode: identity } ).annotations({ identifier: "Uppercase" }) {} /** * This schema converts a string to capitalized one. * * @category string transformations * @since 0.68.18 */ export class Capitalize extends transform( String$.annotations({ description: "a string that will be converted to a capitalized format" }), Capitalized, { strict: true, decode: (s) => string_.capitalize(s), encode: identity } ).annotations({ identifier: "Capitalize" }) {} /** * This schema converts a string to uncapitalized one. * * @category string transformations * @since 0.68.18 */ export class Uncapitalize extends transform( String$.annotations({ description: "a string that will be converted to an uncapitalized format" }), Uncapitalized, { strict: true, decode: (s) => string_.uncapitalize(s), encode: identity } ).annotations({ identifier: "Uncapitalize" }) {} /** * @category string constructors * @since 0.67.0 */ export class Trimmed extends String$.pipe( trimmed({ identifier: "Trimmed", title: "Trimmed" }) ) {} /** * Useful for validating strings that must contain meaningful characters without * leading or trailing whitespace. * * @example * import { Schema } from "@effect/schema" * * console.log(Schema.decodeOption(Schema.NonEmptyTrimmedString)("")) // Option.none() * console.log(Schema.decodeOption(Schema.NonEmptyTrimmedString)(" a ")) // Option.none() * console.log(Schema.decodeOption(Schema.NonEmptyTrimmedString)("a")) // Option.some("a") * * @category string constructors * @since 0.69.3 */ export class NonEmptyTrimmedString extends Trimmed.pipe( nonEmptyString({ identifier: "NonEmptyTrimmedString", title: "NonEmptyTrimmedString" }) ) {} /** * This schema allows removing whitespaces from the beginning and end of a string. * * @category string transformations * @since 0.67.0 */ export class Trim extends transform( String$.annotations({ description: "a string that will be trimmed" }), Trimmed, { strict: true, decode: (s) => s.trim(), encode: identity } ).annotations({ identifier: "Trim" }) {} /** * Returns a schema that allows splitting a string into an array of strings. * * @category string transformations * @since 0.67.0 */ export const split = (separator: string): transform> => transform( String$.annotations({ description: "a string that will be split" }), Array$(String$), { strict: true, decode: string_.split(separator), encode: array_.join(separator) } ) /** * @since 0.67.0 */ export type ParseJsonOptions = { readonly reviver?: Parameters[1] readonly replacer?: Parameters[1] readonly space?: Parameters[2] } const JsonString = String$.annotations({ [AST.IdentifierAnnotationId]: "JsonString", [AST.TitleAnnotationId]: "JsonString", [AST.DescriptionAnnotationId]: "a JSON string" }) const getParseJsonTransformation = (options?: ParseJsonOptions) => transformOrFail( JsonString, Unknown, { strict: true, decode: (s, _, ast) => ParseResult.try({ try: () => JSON.parse(s, options?.reviver), catch: (e: any) => new ParseResult.Type(ast, s, e.message) }), encode: (u, _, ast) => ParseResult.try({ try: () => JSON.stringify(u, options?.replacer, options?.space), catch: (e: any) => new ParseResult.Type(ast, u, e.message) }) } ).annotations({ typeId: filters_.ParseJsonTypeId }) /** * The `ParseJson` combinator provides a method to convert JSON strings into the `unknown` type using the underlying * functionality of `JSON.parse`. It also utilizes `JSON.stringify` for encoding. * * You can optionally provide a `ParseJsonOptions` to configure both `JSON.parse` and `JSON.stringify` executions. * * Optionally, you can pass a schema `Schema` to obtain an `A` type instead of `unknown`. * * @example * import * as S from "@effect/schema/Schema" * * assert.deepStrictEqual(S.decodeUnknownSync(S.parseJson())(`{"a":"1"}`), { a: "1" }) * assert.deepStrictEqual(S.decodeUnknownSync(S.parseJson(S.Struct({ a: S.NumberFromString })))(`{"a":"1"}`), { a: 1 }) * * @category string transformations * @since 0.67.0 */ export const parseJson: { /** * The `ParseJson` combinator provides a method to convert JSON strings into the `unknown` type using the underlying * functionality of `JSON.parse`. It also utilizes `JSON.stringify` for encoding. * * You can optionally provide a `ParseJsonOptions` to configure both `JSON.parse` and `JSON.stringify` executions. * * Optionally, you can pass a schema `Schema` to obtain an `A` type instead of `unknown`. * * @example * import * as S from "@effect/schema/Schema" * * assert.deepStrictEqual(S.decodeUnknownSync(S.parseJson())(`{"a":"1"}`), { a: "1" }) * assert.deepStrictEqual(S.decodeUnknownSync(S.parseJson(S.Struct({ a: S.NumberFromString })))(`{"a":"1"}`), { a: 1 }) * * @category string transformations * @since 0.67.0 */ (schema: Schema, options?: ParseJsonOptions): SchemaClass /** * The `ParseJson` combinator provides a method to convert JSON strings into the `unknown` type using the underlying * functionality of `JSON.parse`. It also utilizes `JSON.stringify` for encoding. * * You can optionally provide a `ParseJsonOptions` to configure both `JSON.parse` and `JSON.stringify` executions. * * Optionally, you can pass a schema `Schema` to obtain an `A` type instead of `unknown`. * * @example * import * as S from "@effect/schema/Schema" * * assert.deepStrictEqual(S.decodeUnknownSync(S.parseJson())(`{"a":"1"}`), { a: "1" }) * assert.deepStrictEqual(S.decodeUnknownSync(S.parseJson(S.Struct({ a: S.NumberFromString })))(`{"a":"1"}`), { a: 1 }) * * @category string transformations * @since 0.67.0 */ (options?: ParseJsonOptions): SchemaClass } = (schema?: Schema | ParseJsonOptions, o?: ParseJsonOptions) => isSchema(schema) ? compose(parseJson(o), schema) as any : getParseJsonTransformation(schema as ParseJsonOptions | undefined) /** * @category string constructors * @since 0.69.0 */ export class NonEmptyString extends String$.pipe( nonEmptyString({ identifier: "NonEmptyString", title: "NonEmptyString" }) ) {} /** * @category type id * @since 0.67.0 */ export const UUIDTypeId: unique symbol = Symbol.for("@effect/schema/TypeId/UUID") const uuidRegexp = /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/i /** * Represents a Universally Unique Identifier (UUID). * * This schema ensures that the provided string adheres to the standard UUID format. * * @category string constructors * @since 0.67.0 */ export class UUID extends String$.pipe( pattern(uuidRegexp, { typeId: UUIDTypeId, identifier: "UUID", title: "UUID", description: "a Universally Unique Identifier", arbitrary: (): LazyArbitrary => (fc) => fc.uuid() }) ) {} /** * @category type id * @since 0.67.0 */ export const ULIDTypeId: unique symbol = Symbol.for("@effect/schema/TypeId/ULID") const ulidRegexp = /^[0-7][0-9A-HJKMNP-TV-Z]{25}$/i /** * Represents a Universally Unique Lexicographically Sortable Identifier (ULID). * * ULIDs are designed to be compact, URL-safe, and ordered, making them suitable for use as identifiers. * This schema ensures that the provided string adheres to the standard ULID format. * * @category string constructors * @since 0.67.0 */ export class ULID extends String$.pipe( pattern(ulidRegexp, { typeId: ULIDTypeId, identifier: "ULID", title: "ULID", description: "a Universally Unique Lexicographically Sortable Identifier", arbitrary: (): LazyArbitrary => (fc) => fc.ulid() }) ) {} /** * @category type id * @since 0.67.0 */ export const FiniteTypeId: unique symbol = Symbol.for("@effect/schema/TypeId/Finite") /** * Ensures that the provided value is a finite number. * * This schema filters out non-finite numeric values, allowing only finite numbers to pass through. * * @category number filters * @since 0.67.0 */ export const finite = (annotations?: Annotations.Filter) => (self: Schema): filter> => self.pipe( filter((a) => Number.isFinite(a), { typeId: FiniteTypeId, description: "a finite number", ...annotations }) ) /** * @category type id * @since 0.67.0 */ export const GreaterThanTypeId: unique symbol = filters_.GreaterThanTypeId /** * @category type id * @since 0.67.0 */ export type GreaterThanTypeId = typeof GreaterThanTypeId /** * This filter checks whether the provided number is greater than the specified minimum. * * @category number filters * @since 0.67.0 */ export const greaterThan = ( min: number, annotations?: Annotations.Filter ) => (self: Schema): filter> => self.pipe( filter((a) => a > min, { typeId: GreaterThanTypeId, description: min === 0 ? "a positive number" : `a number greater than ${min}`, jsonSchema: { exclusiveMinimum: min }, ...annotations }) ) /** * @category type id * @since 0.67.0 */ export const GreaterThanOrEqualToTypeId: unique symbol = filters_.GreaterThanOrEqualToTypeId /** * @category type id * @since 0.67.0 */ export type GreaterThanOrEqualToTypeId = typeof GreaterThanOrEqualToTypeId /** * This filter checks whether the provided number is greater than or equal to the specified minimum. * * @category number filters * @since 0.67.0 */ export const greaterThanOrEqualTo = ( min: number, annotations?: Annotations.Filter ) => (self: Schema): filter> => self.pipe( filter((a) => a >= min, { typeId: GreaterThanOrEqualToTypeId, description: min === 0 ? "a non-negative number" : `a number greater than or equal to ${min}`, jsonSchema: { minimum: min }, ...annotations }) ) /** * @category type id * @since 0.67.0 */ export const MultipleOfTypeId: unique symbol = Symbol.for("@effect/schema/TypeId/MultipleOf") /** * @category number filters * @since 0.67.0 */ export const multipleOf = ( divisor: number, annotations?: Annotations.Filter ) => (self: Schema): filter> => self.pipe( filter((a) => number_.remainder(a, divisor) === 0, { typeId: MultipleOfTypeId, description: `a number divisible by ${divisor}`, jsonSchema: { multipleOf: Math.abs(divisor) }, // spec requires positive divisor ...annotations }) ) /** * @category type id * @since 0.67.0 */ export const IntTypeId: unique symbol = filters_.IntTypeId /** * @category type id * @since 0.67.0 */ export type IntTypeId = typeof IntTypeId /** * @category number filters * @since 0.67.0 */ export const int = (annotations?: Annotations.Filter) => (self: Schema): filter> => self.pipe( filter((a) => Number.isSafeInteger(a), { typeId: IntTypeId, title: "integer", description: "an integer", jsonSchema: { type: "integer" }, ...annotations }) ) /** * @category type id * @since 0.67.0 */ export const LessThanTypeId: unique symbol = filters_.LessThanTypeId /** * @category type id * @since 0.67.0 */ export type LessThanTypeId = typeof LessThanTypeId /** * This filter checks whether the provided number is less than the specified maximum. * * @category number filters * @since 0.67.0 */ export const lessThan = (max: number, annotations?: Annotations.Filter) => (self: Schema): filter> => self.pipe( filter((a) => a < max, { typeId: LessThanTypeId, description: max === 0 ? "a negative number" : `a number less than ${max}`, jsonSchema: { exclusiveMaximum: max }, ...annotations }) ) /** * @category type id * @since 0.67.0 */ export const LessThanOrEqualToTypeId: unique symbol = filters_.LessThanOrEqualToTypeId /** * @category type id * @since 0.67.0 */ export type LessThanOrEqualToTypeId = typeof LessThanOrEqualToTypeId /** * This schema checks whether the provided number is less than or equal to the specified maximum. * * @category number filters * @since 0.67.0 */ export const lessThanOrEqualTo = ( max: number, annotations?: Annotations.Filter ) => (self: Schema): filter> => self.pipe( filter((a) => a <= max, { typeId: LessThanOrEqualToTypeId, description: max === 0 ? "a non-positive number" : `a number less than or equal to ${max}`, jsonSchema: { maximum: max }, ...annotations }) ) /** * @category type id * @since 0.67.0 */ export const BetweenTypeId: unique symbol = filters_.BetweenTypeId /** * @category type id * @since 0.67.0 */ export type BetweenTypeId = typeof BetweenTypeId /** * This filter checks whether the provided number falls within the specified minimum and maximum values. * * @category number filters * @since 0.67.0 */ export const between = ( min: number, max: number, annotations?: Annotations.Filter ) => (self: Schema): filter> => self.pipe( filter((a) => a >= min && a <= max, { typeId: BetweenTypeId, description: `a number between ${min} and ${max}`, jsonSchema: { maximum: max, minimum: min }, ...annotations }) ) /** * @category type id * @since 0.67.0 */ export const NonNaNTypeId: unique symbol = Symbol.for("@effect/schema/TypeId/NonNaN") /** * @category number filters * @since 0.67.0 */ export const nonNaN = (annotations?: Annotations.Filter) => (self: Schema): filter> => self.pipe( filter((a) => !Number.isNaN(a), { typeId: NonNaNTypeId, description: "a number excluding NaN", ...annotations }) ) /** * @category number filters * @since 0.67.0 */ export const positive = ( annotations?: Annotations.Filter ): (self: Schema) => filter> => greaterThan(0, annotations) /** * @category number filters * @since 0.67.0 */ export const negative = ( annotations?: Annotations.Filter ): (self: Schema) => filter> => lessThan(0, annotations) /** * @category number filters * @since 0.67.0 */ export const nonPositive = ( annotations?: Annotations.Filter ): (self: Schema) => filter> => lessThanOrEqualTo(0, annotations) /** * @category number filters * @since 0.67.0 */ export const nonNegative = ( annotations?: Annotations.Filter ): (self: Schema) => filter> => greaterThanOrEqualTo(0, annotations) /** * Clamps a number between a minimum and a maximum value. * * @category number transformations * @since 0.67.0 */ export const clamp = (minimum: number, maximum: number) => (self: Schema): transform, filter>> => transform( self, self.pipe(typeSchema, between(minimum, maximum)), { strict: false, decode: (self) => number_.clamp(self, { minimum, maximum }), encode: identity } ) /** * Transforms a `string` into a `number` by parsing the string using the `parse` function of the `effect/Number` module. * * It returns an error if the value can't be converted (for example when non-numeric characters are provided). * * The following special string values are supported: "NaN", "Infinity", "-Infinity". * * @category number transformations * @since 0.67.0 */ export const parseNumber = ( self: Schema ): transformOrFail, typeof Number$> => transformOrFail( self, Number$, { strict: false, decode: (s, _, ast) => ParseResult.fromOption(number_.parse(s), () => new ParseResult.Type(ast, s)), encode: (n) => ParseResult.succeed(String(n)) } ) /** * This schema transforms a `string` into a `number` by parsing the string using the `parse` function of the `effect/Number` module. * * It returns an error if the value can't be converted (for example when non-numeric characters are provided). * * The following special string values are supported: "NaN", "Infinity", "-Infinity". * * @category number constructors * @since 0.67.0 */ export class NumberFromString extends parseNumber(String$.annotations({ description: "a string that will be parsed into a number" })).annotations({ identifier: "NumberFromString" }) {} /** * @category number constructors * @since 0.67.0 */ export class Finite extends Number$.pipe(finite({ identifier: "Finite", title: "Finite" })) {} /** * @category number constructors * @since 0.67.0 */ export class Int extends Number$.pipe(int({ identifier: "Int", title: "Int" })) {} /** * @category number constructors * @since 0.67.0 */ export class NonNaN extends Number$.pipe(nonNaN({ identifier: "NonNaN", title: "NonNaN" })) {} /** * @category number constructors * @since 0.67.0 */ export class Positive extends Number$.pipe( positive({ identifier: "Positive", title: "Positive" }) ) {} /** * @category number constructors * @since 0.67.0 */ export class Negative extends Number$.pipe( negative({ identifier: "Negative", title: "Negative" }) ) {} /** * @category number constructors * @since 0.67.0 */ export class NonPositive extends Number$.pipe( nonPositive({ identifier: "NonPositive", title: "NonPositive" }) ) {} /** * @category number constructors * @since 0.67.0 */ export class NonNegative extends Number$.pipe( nonNegative({ identifier: "NonNegative", title: "NonNegative" }) ) {} /** * @category type id * @since 0.67.0 */ export const JsonNumberTypeId: unique symbol = Symbol.for("@effect/schema/TypeId/JsonNumber") /** * The `JsonNumber` is a schema for representing JSON numbers. It ensures that the provided value is a valid * number by filtering out `NaN` and `(+/-) Infinity`. This is useful when you want to validate and represent numbers in JSON * format. * * @example * import * as S from "@effect/schema/Schema" * * const is = S.is(S.JsonNumber) * * assert.deepStrictEqual(is(42), true) * assert.deepStrictEqual(is(Number.NaN), false) * assert.deepStrictEqual(is(Number.POSITIVE_INFINITY), false) * assert.deepStrictEqual(is(Number.NEGATIVE_INFINITY), false) * * @category number constructors * @since 0.67.0 */ export class JsonNumber extends Number$.pipe( filter((n) => !Number.isNaN(n) && Number.isFinite(n), { typeId: JsonNumberTypeId, identifier: "JsonNumber", title: "JSON-compatible number", description: "a JSON-compatible number, excluding NaN, +Infinity, and -Infinity", jsonSchema: { type: "number" } }) ) {} /** * @category boolean transformations * @since 0.67.0 */ export class Not extends transform(Boolean$.annotations({ description: "a boolean that will be negated" }), Boolean$, { strict: true, decode: boolean_.not, encode: boolean_.not }) {} /** @ignore */ class Symbol$ extends transform( String$.annotations({ description: "a string that will be converted to a symbol" }), SymbolFromSelf, { strict: false, decode: (s) => Symbol.for(s), encode: (sym) => sym.description } ).annotations({ identifier: "symbol" }) {} export { /** * This schema transforms a `string` into a `symbol`. * * @category symbol transformations * @since 0.67.0 */ Symbol$ as Symbol } /** * @category type id * @since 0.67.0 */ export const GreaterThanBigIntTypeId: unique symbol = filters_.GreaterThanBigintTypeId /** * @category type id * @since 0.67.0 */ export type GreaterThanBigIntTypeId = typeof GreaterThanBigIntTypeId /** * @category bigint filters * @since 0.67.0 */ export const greaterThanBigInt = ( min: bigint, annotations?: Annotations.Filter ) => (self: Schema): filter> => self.pipe( filter((a) => a > min, { typeId: { id: GreaterThanBigIntTypeId, annotation: { min } }, description: min === 0n ? "a positive bigint" : `a bigint greater than ${min}n`, ...annotations }) ) /** * @category type id * @since 0.67.0 */ export const GreaterThanOrEqualToBigIntTypeId: unique symbol = filters_.GreaterThanOrEqualToBigIntTypeId /** * @category type id * @since 0.67.0 */ export type GreaterThanOrEqualToBigIntTypeId = typeof GreaterThanOrEqualToBigIntTypeId /** * @category bigint filters * @since 0.67.0 */ export const greaterThanOrEqualToBigInt = ( min: bigint, annotations?: Annotations.Filter ) => (self: Schema): filter> => self.pipe( filter((a) => a >= min, { typeId: { id: GreaterThanOrEqualToBigIntTypeId, annotation: { min } }, description: min === 0n ? "a non-negative bigint" : `a bigint greater than or equal to ${min}n`, ...annotations }) ) /** * @category type id * @since 0.67.0 */ export const LessThanBigIntTypeId: unique symbol = filters_.LessThanBigIntTypeId /** * @category type id * @since 0.67.0 */ export type LessThanBigIntTypeId = typeof LessThanBigIntTypeId /** * @category bigint filters * @since 0.67.0 */ export const lessThanBigInt = ( max: bigint, annotations?: Annotations.Filter ) => (self: Schema): filter> => self.pipe( filter((a) => a < max, { typeId: { id: LessThanBigIntTypeId, annotation: { max } }, description: max === 0n ? "a negative bigint" : `a bigint less than ${max}n`, ...annotations }) ) /** * @category type id * @since 0.67.0 */ export const LessThanOrEqualToBigIntTypeId: unique symbol = filters_.LessThanOrEqualToBigIntTypeId /** * @category type id * @since 0.67.0 */ export type LessThanOrEqualToBigIntTypeId = typeof LessThanOrEqualToBigIntTypeId /** * @category bigint filters * @since 0.67.0 */ export const lessThanOrEqualToBigInt = ( max: bigint, annotations?: Annotations.Filter ) => (self: Schema): filter> => self.pipe( filter((a) => a <= max, { typeId: { id: LessThanOrEqualToBigIntTypeId, annotation: { max } }, description: max === 0n ? "a non-positive bigint" : `a bigint less than or equal to ${max}n`, ...annotations }) ) /** * @category type id * @since 0.67.0 */ export const BetweenBigIntTypeId: unique symbol = filters_.BetweenBigintTypeId /** * @category type id * @since 0.67.0 */ export type BetweenBigIntTypeId = typeof BetweenBigIntTypeId /** * @category bigint filters * @since 0.67.0 */ export const betweenBigInt = ( min: bigint, max: bigint, annotations?: Annotations.Filter ) => (self: Schema): filter> => self.pipe( filter((a) => a >= min && a <= max, { typeId: { id: BetweenBigIntTypeId, annotation: { max, min } }, description: `a bigint between ${min}n and ${max}n`, ...annotations }) ) /** * @category bigint filters * @since 0.67.0 */ export const positiveBigInt = ( annotations?: Annotations.Filter ): (self: Schema) => filter> => greaterThanBigInt(0n, annotations) /** * @category bigint filters * @since 0.67.0 */ export const negativeBigInt = ( annotations?: Annotations.Filter ): (self: Schema) => filter> => lessThanBigInt(0n, annotations) /** * @category bigint filters * @since 0.67.0 */ export const nonNegativeBigInt = ( annotations?: Annotations.Filter ): (self: Schema) => filter> => greaterThanOrEqualToBigInt(0n, annotations) /** * @category bigint filters * @since 0.67.0 */ export const nonPositiveBigInt = ( annotations?: Annotations.Filter ): (self: Schema) => filter> => lessThanOrEqualToBigInt(0n, annotations) /** * Clamps a bigint between a minimum and a maximum value. * * @category bigint transformations * @since 0.67.0 */ export const clampBigInt = (minimum: bigint, maximum: bigint) => (self: Schema): transform, filter>> => transform( self, self.pipe(typeSchema, betweenBigInt(minimum, maximum)), { strict: false, decode: (self) => bigInt_.clamp(self, { minimum, maximum }), encode: identity } ) /** @ignore */ class BigInt$ extends transformOrFail( String$.annotations({ description: "a string that will be parsed into a bigint" }), BigIntFromSelf, { strict: true, decode: (s, _, ast) => ParseResult.fromOption(bigInt_.fromString(s), () => new ParseResult.Type(ast, s)), encode: (n) => ParseResult.succeed(String(n)) } ).annotations({ identifier: "bigint" }) {} export { /** * This schema transforms a `string` into a `bigint` by parsing the string using the `BigInt` function. * * It returns an error if the value can't be converted (for example when non-numeric characters are provided). * * @category bigint transformations * @since 0.67.0 */ BigInt$ as BigInt } /** * @category bigint constructors * @since 0.67.0 */ export const PositiveBigIntFromSelf: filter> = BigIntFromSelf.pipe( positiveBigInt({ identifier: "PositiveBigintFromSelf", title: "PositiveBigintFromSelf" }) ) /** * @category bigint constructors * @since 0.67.0 */ export const PositiveBigInt: filter> = BigInt$.pipe( positiveBigInt({ identifier: "PositiveBigint", title: "PositiveBigint" }) ) /** * @category bigint constructors * @since 0.67.0 */ export const NegativeBigIntFromSelf: filter> = BigIntFromSelf.pipe( negativeBigInt({ identifier: "NegativeBigintFromSelf", title: "NegativeBigintFromSelf" }) ) /** * @category bigint constructors * @since 0.67.0 */ export const NegativeBigInt: filter> = BigInt$.pipe( negativeBigInt({ identifier: "NegativeBigint", title: "NegativeBigint" }) ) /** * @category bigint constructors * @since 0.67.0 */ export const NonPositiveBigIntFromSelf: filter> = BigIntFromSelf.pipe( nonPositiveBigInt({ identifier: "NonPositiveBigintFromSelf", title: "NonPositiveBigintFromSelf" }) ) /** * @category bigint constructors * @since 0.67.0 */ export const NonPositiveBigInt: filter> = BigInt$.pipe( nonPositiveBigInt({ identifier: "NonPositiveBigint", title: "NonPositiveBigint" }) ) /** * @category bigint constructors * @since 0.67.0 */ export const NonNegativeBigIntFromSelf: filter> = BigIntFromSelf.pipe( nonNegativeBigInt({ identifier: "NonNegativeBigintFromSelf", title: "NonNegativeBigintFromSelf" }) ) /** * @category bigint constructors * @since 0.67.0 */ export const NonNegativeBigInt: filter> = BigInt$.pipe( nonNegativeBigInt({ identifier: "NonNegativeBigint", title: "NonNegativeBigint" }) ) /** * This schema transforms a `number` into a `bigint` by parsing the number using the `BigInt` function. * * It returns an error if the value can't be safely encoded as a `number` due to being out of range. * * @category bigint transformations * @since 0.67.0 */ export class BigIntFromNumber extends transformOrFail( Number$.annotations({ description: "a number that will be parsed into a bigint" }), BigIntFromSelf, { strict: true, decode: (n, _, ast) => ParseResult.fromOption( bigInt_.fromNumber(n), () => new ParseResult.Type(ast, n) ), encode: (b, _, ast) => ParseResult.fromOption(bigInt_.toNumber(b), () => new ParseResult.Type(ast, b)) } ).annotations({ identifier: "BigintFromNumber" }) {} const redactedArbitrary = (value: LazyArbitrary): LazyArbitrary> => (fc) => value(fc).map(redacted_.make) const toComposite = ( eff: Effect.Effect, onSuccess: (a: A) => B, ast: AST.AST, actual: unknown ): Effect.Effect => ParseResult.mapBoth(eff, { onFailure: (e) => new ParseResult.Composite(ast, actual, e), onSuccess }) const redactedParse = ( decodeUnknown: ParseResult.DecodeUnknown ): ParseResult.DeclarationDecodeUnknown, R> => (u, options, ast) => redacted_.isRedacted(u) ? toComposite(decodeUnknown(redacted_.value(u), options), redacted_.make, ast, u) : ParseResult.fail(new ParseResult.Type(ast, u)) /** * @category api interface * @since 0.67.21 */ export interface RedactedFromSelf extends AnnotableClass< RedactedFromSelf, redacted_.Redacted>, redacted_.Redacted>, Schema.Context > {} /** * @category Redacted constructors * @since 0.67.21 */ export const RedactedFromSelf = ( value: Value ): RedactedFromSelf => declare( [value], { decode: (value) => redactedParse(ParseResult.decodeUnknown(value)), encode: (value) => redactedParse(ParseResult.encodeUnknown(value)) }, { description: "Redacted()", pretty: () => () => "Redacted()", arbitrary: redactedArbitrary, equivalence: redacted_.getEquivalence } ) /** * @category api interface * @since 0.67.21 */ export interface Redacted extends AnnotableClass< Redacted, redacted_.Redacted>, Schema.Encoded, Schema.Context > {} /** * A schema that transforms any type `A` into a `Redacted`. * * @category Redacted transformations * @since 0.67.21 */ export const Redacted = ( value: Value ): Redacted => { return transform( value, RedactedFromSelf(typeSchema(value)), { strict: true, decode: (value) => redacted_.make(value), encode: (value) => redacted_.value(value) } ) } /** * @category Duration constructors * @since 0.67.0 */ export class DurationFromSelf extends declare( duration_.isDuration, { identifier: "DurationFromSelf", pretty: (): pretty_.Pretty => String, arbitrary: (): LazyArbitrary => (fc) => fc.oneof( fc.constant(duration_.infinity), fc.bigUint().map((_) => duration_.nanos(_)), fc.bigUint().map((_) => duration_.micros(_)), fc.maxSafeNat().map((_) => duration_.millis(_)), fc.maxSafeNat().map((_) => duration_.seconds(_)), fc.maxSafeNat().map((_) => duration_.minutes(_)), fc.maxSafeNat().map((_) => duration_.hours(_)), fc.maxSafeNat().map((_) => duration_.days(_)), fc.maxSafeNat().map((_) => duration_.weeks(_)) ), equivalence: (): Equivalence.Equivalence => duration_.Equivalence } ) {} /** * A schema that transforms a `bigint` tuple into a `Duration`. * Treats the value as the number of nanoseconds. * * @category Duration transformations * @since 0.67.0 */ export class DurationFromNanos extends transformOrFail( BigIntFromSelf.annotations({ description: "a bigint that will be parsed into a Duration" }), DurationFromSelf, { strict: true, decode: (nanos) => ParseResult.succeed(duration_.nanos(nanos)), encode: (duration, _, ast) => option_.match(duration_.toNanos(duration), { onNone: () => ParseResult.fail(new ParseResult.Type(ast, duration)), onSome: (val) => ParseResult.succeed(val) }) } ).annotations({ identifier: "DurationFromNanos" }) {} /** * A schema that transforms a `number` tuple into a `Duration`. * Treats the value as the number of milliseconds. * * @category Duration transformations * @since 0.67.0 */ export class DurationFromMillis extends transform( Number$.annotations({ description: "a number that will be parsed into a Duration" }), DurationFromSelf, { strict: true, decode: (ms) => duration_.millis(ms), encode: (n) => duration_.toMillis(n) } ).annotations({ identifier: "DurationFromMillis" }) {} const hrTime: Schema = Tuple( NonNegative.pipe( finite({ [AST.TitleAnnotationId]: "seconds", [AST.DescriptionAnnotationId]: "seconds" }) ), NonNegative.pipe( finite({ [AST.TitleAnnotationId]: "nanos", [AST.DescriptionAnnotationId]: "nanos" }) ) ) /** * A schema that transforms a `[number, number]` tuple into a `Duration`. * * @category Duration transformations * @since 0.67.0 */ export class Duration extends transform( hrTime.annotations({ description: "a tuple of seconds and nanos that will be parsed into a Duration" }), DurationFromSelf, { strict: true, decode: ([seconds, nanos]) => duration_.nanos(BigInt(seconds) * BigInt(1e9) + BigInt(nanos)), encode: (duration) => duration_.toHrTime(duration) } ).annotations({ identifier: "Duration" }) {} /** * Clamps a `Duration` between a minimum and a maximum value. * * @category Duration transformations * @since 0.67.0 */ export const clampDuration = (minimum: duration_.DurationInput, maximum: duration_.DurationInput) => (self: Schema): transform, filter>> => transform( self, self.pipe(typeSchema, betweenDuration(minimum, maximum)), { strict: false, decode: (self) => duration_.clamp(self, { minimum, maximum }), encode: identity } ) /** * @category type id * @since 0.67.0 */ export const LessThanDurationTypeId: unique symbol = Symbol.for("@effect/schema/TypeId/LessThanDuration") /** * @category Duration filters * @since 0.67.0 */ export const lessThanDuration = ( max: duration_.DurationInput, annotations?: Annotations.Filter ) => (self: Schema): filter> => self.pipe( filter((a) => duration_.lessThan(a, max), { typeId: { id: LessThanDurationTypeId, annotation: { max } }, description: `a Duration less than ${duration_.decode(max)}`, ...annotations }) ) /** * @category type id * @since 0.67.0 */ export const LessThanOrEqualToDurationTypeId: unique symbol = Symbol.for( "@effect/schema/TypeId/LessThanOrEqualToDuration" ) /** * @category Duration filters * @since 0.67.0 */ export const lessThanOrEqualToDuration = ( max: duration_.DurationInput, annotations?: Annotations.Filter ) => (self: Schema): filter> => self.pipe( filter((a) => duration_.lessThanOrEqualTo(a, max), { typeId: { id: LessThanDurationTypeId, annotation: { max } }, description: `a Duration less than or equal to ${duration_.decode(max)}`, ...annotations }) ) /** * @category type id * @since 0.67.0 */ export const GreaterThanDurationTypeId: unique symbol = Symbol.for("@effect/schema/TypeId/GreaterThanDuration") /** * @category Duration filters * @since 0.67.0 */ export const greaterThanDuration = ( min: duration_.DurationInput, annotations?: Annotations.Filter ) => (self: Schema): filter> => self.pipe( filter((a) => duration_.greaterThan(a, min), { typeId: { id: GreaterThanDurationTypeId, annotation: { min } }, description: `a Duration greater than ${duration_.decode(min)}`, ...annotations }) ) /** * @category type id * @since 0.67.0 */ export const GreaterThanOrEqualToDurationTypeId: unique symbol = Symbol.for( "@effect/schema/TypeId/GreaterThanOrEqualToDuration" ) /** * @category Duration filters * @since 0.67.0 */ export const greaterThanOrEqualToDuration = ( min: duration_.DurationInput, annotations?: Annotations.Filter ) => (self: Schema): filter> => self.pipe( filter((a) => duration_.greaterThanOrEqualTo(a, min), { typeId: { id: GreaterThanOrEqualToDurationTypeId, annotation: { min } }, description: `a Duration greater than or equal to ${duration_.decode(min)}`, ...annotations }) ) /** * @category type id * @since 0.67.0 */ export const BetweenDurationTypeId: unique symbol = Symbol.for("@effect/schema/TypeId/BetweenDuration") /** * @category Duration filters * @since 0.67.0 */ export const betweenDuration = ( minimum: duration_.DurationInput, maximum: duration_.DurationInput, annotations?: Annotations.Filter ) => (self: Schema): filter> => self.pipe( filter((a) => duration_.between(a, { minimum, maximum }), { typeId: { id: BetweenDurationTypeId, annotation: { maximum, minimum } }, description: `a Duration between ${duration_.decode(minimum)} and ${duration_.decode(maximum)}`, ...annotations }) ) /** * @category Uint8Array constructors * @since 0.67.0 */ export const Uint8ArrayFromSelf: Schema = declare( Predicate.isUint8Array, { identifier: "Uint8ArrayFromSelf", pretty: (): pretty_.Pretty => (u8arr) => `new Uint8Array(${JSON.stringify(Array.from(u8arr))})`, arbitrary: (): LazyArbitrary => (fc) => fc.uint8Array(), equivalence: (): Equivalence.Equivalence => array_.getEquivalence(Equal.equals) as any } ) const Uint8Array$: Schema> = transform( Array$(Number$.pipe( between(0, 255, { title: "8-bit unsigned integer", description: "a 8-bit unsigned integer" }) )).annotations({ description: "an array of 8-bit unsigned integers that will be parsed into a Uint8Array" }), Uint8ArrayFromSelf, { strict: true, decode: (numbers) => Uint8Array.from(numbers), encode: (uint8Array) => Array.from(uint8Array) } ).annotations({ identifier: "Uint8Array" }) export { /** * A schema that transforms an array of numbers into a `Uint8Array`. * * @category Uint8Array transformations * @since 0.67.0 */ Uint8Array$ as Uint8Array } const makeUint8ArrayTransformation = ( id: string, decode: (s: string) => either_.Either, encode: (u: Uint8Array) => string ) => transformOrFail( String$.annotations({ description: "a string that will be parsed into a Uint8Array" }), Uint8ArrayFromSelf, { strict: true, decode: (s, _, ast) => either_.mapLeft( decode(s), (decodeException) => new ParseResult.Type(ast, s, decodeException.message) ), encode: (u) => ParseResult.succeed(encode(u)) } ).annotations({ identifier: id }) /** * Decodes a base64 (RFC4648) encoded string into a `Uint8Array`. * * @category Uint8Array transformations * @since 0.67.0 */ export const Uint8ArrayFromBase64: Schema = makeUint8ArrayTransformation( "Uint8ArrayFromBase64", Encoding.decodeBase64, Encoding.encodeBase64 ) /** * Decodes a base64 (URL) encoded string into a `Uint8Array`. * * @category Uint8Array transformations * @since 0.67.0 */ export const Uint8ArrayFromBase64Url: Schema = makeUint8ArrayTransformation( "Uint8ArrayFromBase64Url", Encoding.decodeBase64Url, Encoding.encodeBase64Url ) /** * Decodes a hex encoded string into a `Uint8Array`. * * @category Uint8Array transformations * @since 0.67.0 */ export const Uint8ArrayFromHex: Schema = makeUint8ArrayTransformation( "Uint8ArrayFromHex", Encoding.decodeHex, Encoding.encodeHex ) const makeEncodingTransformation = ( id: string, decode: (s: string) => either_.Either, encode: (u: string) => string ) => transformOrFail( String$.annotations({ description: `A string that is interpreted as being ${id}-encoded and will be decoded into a UTF-8 string` }), String$, { strict: true, decode: (s, _, ast) => either_.mapLeft( decode(s), (decodeException) => new ParseResult.Type(ast, s, decodeException.message) ), encode: (u) => ParseResult.succeed(encode(u)) } ).annotations({ identifier: `StringFrom${id}` }) /** * Decodes a base64 (RFC4648) encoded string into a UTF-8 string. * * @category string transformations * @since 0.67.0 */ export const StringFromBase64: Schema = makeEncodingTransformation( "Base64", Encoding.decodeBase64String, Encoding.encodeBase64 ) /** * Decodes a base64 (URL) encoded string into a UTF-8 string. * * @category string transformations * @since 0.67.0 */ export const StringFromBase64Url: Schema = makeEncodingTransformation( "Base64Url", Encoding.decodeBase64UrlString, Encoding.encodeBase64Url ) /** * Decodes a hex encoded string into a UTF-8 string. * * @category string transformations * @since 0.67.0 */ export const StringFromHex: Schema = makeEncodingTransformation( "Hex", Encoding.decodeHexString, Encoding.encodeHex ) /** * @category type id * @since 0.67.0 */ export const MinItemsTypeId: unique symbol = filters_.MinItemsTypeId /** * @category type id * @since 0.67.0 */ export type MinItemsTypeId = typeof MinItemsTypeId /** * @category ReadonlyArray filters * @since 0.67.0 */ export const minItems = ( n: number, annotations?: Annotations.Filter> ) => (self: Schema, I, R>): filter, I, R>> => { const minItems = Math.floor(n) if (minItems < 1) { throw new Error( errors_.getInvalidArgumentErrorMessage(`Expected an integer greater than or equal to 1, actual ${n}`) ) } return self.pipe( filter( (a) => a.length >= minItems, { typeId: MinItemsTypeId, description: `an array of at least ${minItems} items`, jsonSchema: { minItems }, [AST.StableFilterAnnotationId]: true, ...annotations } ) ) } /** * @category type id * @since 0.67.0 */ export const MaxItemsTypeId: unique symbol = filters_.MaxItemsTypeId /** * @category type id * @since 0.67.0 */ export type MaxItemsTypeId = typeof MaxItemsTypeId /** * @category ReadonlyArray filters * @since 0.67.0 */ export const maxItems = ( n: number, annotations?: Annotations.Filter> ) => (self: Schema, I, R>): filter, I, R>> => self.pipe( filter((a) => a.length <= n, { typeId: MaxItemsTypeId, description: `an array of at most ${n} items`, jsonSchema: { maxItems: n }, [AST.StableFilterAnnotationId]: true, ...annotations }) ) /** * @category type id * @since 0.67.0 */ export const ItemsCountTypeId: unique symbol = filters_.ItemsCountTypeId /** * @category type id * @since 0.67.0 */ export type ItemsCountTypeId = typeof ItemsCountTypeId /** * @category ReadonlyArray filters * @since 0.67.0 */ export const itemsCount = ( n: number, annotations?: Annotations.Filter> ) => (self: Schema, I, R>): filter, I, R>> => self.pipe( filter((a) => a.length === n, { typeId: ItemsCountTypeId, description: `an array of exactly ${n} item(s)`, jsonSchema: { minItems: n, maxItems: n }, [AST.StableFilterAnnotationId]: true, ...annotations }) ) /** * @category ReadonlyArray transformations * @since 0.67.0 */ export const getNumberIndexedAccess = , I extends ReadonlyArray, R>( self: Schema ): SchemaClass => make(AST.getNumberIndexedAccess(self.ast)) /** * Get the first element of a `ReadonlyArray`, or `None` if the array is empty. * * @category ReadonlyArray transformations * @since 0.67.0 */ export const head = (self: Schema, I, R>): SchemaClass, I, R> => transform( self, OptionFromSelf(getNumberIndexedAccess(typeSchema(self))), { strict: true, decode: array_.head, encode: option_.match({ onNone: () => [], onSome: array_.of }) } ) /** * Retrieves the first element of a `ReadonlyArray`. * * If the array is empty, it returns the `fallback` argument if provided; otherwise, it fails. * * @category ReadonlyArray transformations * @since 0.67.0 */ export const headOrElse: { /** * Retrieves the first element of a `ReadonlyArray`. * * If the array is empty, it returns the `fallback` argument if provided; otherwise, it fails. * * @category ReadonlyArray transformations * @since 0.67.0 */ (fallback?: LazyArg): (self: Schema, I, R>) => SchemaClass /** * Retrieves the first element of a `ReadonlyArray`. * * If the array is empty, it returns the `fallback` argument if provided; otherwise, it fails. * * @category ReadonlyArray transformations * @since 0.67.0 */ (self: Schema, I, R>, fallback?: LazyArg): SchemaClass } = dual( (args) => isSchema(args[0]), (self: Schema, I, R>, fallback?: LazyArg): SchemaClass => transformOrFail( self, getNumberIndexedAccess(typeSchema(self)), { strict: true, decode: (as, _, ast) => as.length > 0 ? ParseResult.succeed(as[0]) : fallback ? ParseResult.succeed(fallback()) : ParseResult.fail(new ParseResult.Type(ast, as)), encode: (a) => ParseResult.succeed(array_.of(a)) } ) ) /** * @category type id * @since 0.67.0 */ export const ValidDateTypeId: unique symbol = Symbol.for("@effect/schema/TypeId/ValidDate") /** * Defines a filter that specifically rejects invalid dates, such as `new * Date("Invalid Date")`. This filter ensures that only properly formatted and * valid date objects are accepted, enhancing data integrity by preventing * erroneous date values from being processed. * * @category Date filters * @since 0.67.0 */ export const validDate = (annotations?: Annotations.Filter) => (self: Schema): filter> => self.pipe( filter((a) => !Number.isNaN(a.getTime()), { typeId: ValidDateTypeId, description: "a valid Date", ...annotations }) ) /** * @category type id * @since 0.73.1 */ export const LessThanDateTypeId: unique symbol = Symbol.for("@effect/schema/TypeId/LessThanDate") /** * @category Date filters * @since 0.73.1 */ export const lessThanDate = ( max: Date, annotations?: Annotations.Filter ) => (self: Schema): filter> => self.pipe( filter((a) => a < max, { typeId: { id: LessThanDateTypeId, annotation: { max } }, description: `a date before ${util_.formatDate(max)}`, ...annotations }) ) /** * @category type id * @since 0.73.1 */ export const LessThanOrEqualToDateTypeId: unique symbol = Symbol.for( "@effect/schema/TypeId/LessThanOrEqualToDate" ) /** * @category Date filters * @since 0.73.1 */ export const lessThanOrEqualToDate = ( max: Date, annotations?: Annotations.Filter ) => (self: Schema): filter> => self.pipe( filter((a) => a <= max, { typeId: { id: LessThanDateTypeId, annotation: { max } }, description: `a date before or equal to ${util_.formatDate(max)}`, ...annotations }) ) /** * @category type id * @since 0.73.1 */ export const GreaterThanDateTypeId: unique symbol = Symbol.for("@effect/schema/TypeId/GreaterThanDate") /** * @category Date filters * @since 0.73.1 */ export const greaterThanDate = ( min: Date, annotations?: Annotations.Filter ) => (self: Schema): filter> => self.pipe( filter((a) => a > min, { typeId: { id: GreaterThanDateTypeId, annotation: { min } }, description: `a date after ${util_.formatDate(min)}`, ...annotations }) ) /** * @category type id * @since 0.73.1 */ export const GreaterThanOrEqualToDateTypeId: unique symbol = Symbol.for( "@effect/schema/TypeId/GreaterThanOrEqualToDate" ) /** * @category Date filters * @since 0.73.1 */ export const greaterThanOrEqualToDate = ( min: Date, annotations?: Annotations.Filter ) => (self: Schema): filter> => self.pipe( filter((a) => a >= min, { typeId: { id: GreaterThanOrEqualToDateTypeId, annotation: { min } }, description: `a date after or equal to ${util_.formatDate(min)}`, ...annotations }) ) /** * @category type id * @since 0.73.1 */ export const BetweenDateTypeId: unique symbol = Symbol.for("@effect/schema/TypeId/BetweenDate") /** * @category Date filters * @since 0.73.1 */ export const betweenDate = ( minimum: Date, maximum: Date, annotations?: Annotations.Filter ) => (self: Schema): filter> => self.pipe( filter((a) => a <= maximum && a >= minimum, { typeId: { id: BetweenDateTypeId, annotation: { maximum, minimum } }, description: `a date between ${util_.formatDate(minimum)} and ${util_.formatDate(maximum)}`, ...annotations }) ) /** * Describes a schema that accommodates potentially invalid `Date` instances, * such as `new Date("Invalid Date")`, without rejection. * * @category Date constructors * @since 0.67.0 */ export class DateFromSelf extends declare( Predicate.isDate, { identifier: "DateFromSelf", description: "a potentially invalid Date instance", pretty: (): pretty_.Pretty => (date) => `new Date(${JSON.stringify(date)})`, arbitrary: (): LazyArbitrary => (fc) => fc.date({ noInvalidDate: false }), equivalence: () => Equivalence.Date } ) {} /** * Defines a schema that ensures only valid dates are accepted. This schema * rejects values like `new Date("Invalid Date")`, which, despite being a `Date` * instance, represents an invalid date. Such stringent validation ensures that * all date objects processed through this schema are properly formed and * represent real dates. * * @category Date constructors * @since 0.67.0 */ export class ValidDateFromSelf extends DateFromSelf.pipe( validDate({ identifier: "ValidDateFromSelf", description: "a valid Date instance" }) ) {} /** * Defines a schema that attempts to convert a `string` to a `Date` object using * the `new Date` constructor. This conversion is lenient, meaning it does not * reject strings that do not form valid dates (e.g., using `new Date("Invalid * Date")` results in a `Date` object, despite being invalid). * * @category Date transformations * @since 0.67.0 */ export class DateFromString extends transform( String$.annotations({ description: "a string that will be parsed into a Date" }), DateFromSelf, { strict: true, decode: (s) => new Date(s), encode: (d) => util_.formatDate(d) } ).annotations({ identifier: "DateFromString" }) {} /** @ignore */ class Date$ extends DateFromString.pipe( validDate({ identifier: "Date" }) ) {} export { /** * This schema converts a `string` into a `Date` object using the `new Date` * constructor. It ensures that only valid date strings are accepted, * rejecting any strings that would result in an invalid date, such as `new * Date("Invalid Date")`. * * @category Date transformations * @since 0.67.0 */ Date$ as Date } /** * Defines a schema that converts a `number` into a `Date` object using the `new * Date` constructor. This schema does not validate the numerical input, * allowing potentially invalid values such as `NaN`, `Infinity`, and * `-Infinity` to be converted into `Date` objects. During the encoding process, * any invalid `Date` object will be encoded to `NaN`. * * @category Date transformations * @since 0.67.0 */ export class DateFromNumber extends transform( Number$.annotations({ description: "a number that will be parsed into a Date" }), DateFromSelf, { strict: true, decode: (n) => new Date(n), encode: (d) => d.getTime() } ).annotations({ identifier: "DateFromNumber" }) {} /** * Describes a schema that represents a `DateTime.Utc` instance. * * @category DateTime.Utc constructors * @since 0.70.0 */ export class DateTimeUtcFromSelf extends declare( (u) => dateTime.isDateTime(u) && dateTime.isUtc(u), { identifier: "DateTimeUtcFromSelf", description: "a DateTime.Utc instance", pretty: (): pretty_.Pretty => (dateTime) => dateTime.toString(), arbitrary: (): LazyArbitrary => (fc) => fc.date().map((date) => dateTime.unsafeFromDate(date)), equivalence: () => dateTime.Equivalence } ) {} const decodeDateTime = (input: A, _: ParseOptions, ast: AST.AST) => ParseResult.try({ try: () => dateTime.unsafeMake(input), catch: () => new ParseResult.Type(ast, input) }) /** * Defines a schema that attempts to convert a `number` to a `DateTime.Utc` instance using the `DateTime.unsafeMake` constructor. * * @category DateTime.Utc transformations * @since 0.70.0 */ export class DateTimeUtcFromNumber extends transformOrFail( Number$.annotations({ description: "a number that will be parsed into a DateTime.Utc" }), DateTimeUtcFromSelf, { strict: true, decode: decodeDateTime, encode: (dt) => ParseResult.succeed(dateTime.toEpochMillis(dt)) } ).annotations({ identifier: "DateTimeUtcFromNumber" }) {} /** * Defines a schema that attempts to convert a `string` to a `DateTime.Utc` instance using the `DateTime.unsafeMake` constructor. * * @category DateTime.Utc transformations * @since 0.70.0 */ export class DateTimeUtc extends transformOrFail( String$.annotations({ description: "a string that will be parsed into a DateTime.Utc" }), DateTimeUtcFromSelf, { strict: true, decode: decodeDateTime, encode: (dt) => ParseResult.succeed(dateTime.formatIso(dt)) } ).annotations({ identifier: "DateTimeUtc" }) {} const timeZoneOffsetArbitrary = (): LazyArbitrary => (fc) => fc.integer({ min: -12 * 60 * 60 * 1000, max: 12 * 60 * 60 * 1000 }).map(dateTime.zoneMakeOffset) /** * Describes a schema that represents a `TimeZone.Offset` instance. * * @category TimeZone constructors * @since 0.70.0 */ export class TimeZoneOffsetFromSelf extends declare( dateTime.isTimeZoneOffset, { identifier: "TimeZoneOffsetFromSelf", description: "a TimeZone.Offset instance", pretty: (): pretty_.Pretty => (zone) => zone.toString(), arbitrary: timeZoneOffsetArbitrary } ) {} /** * Defines a schema that converts a `number` to a `TimeZone.Offset` instance using the `DateTime.zoneMakeOffset` constructor. * * @category TimeZone transformations * @since 0.70.0 */ export class TimeZoneOffset extends transform( Number$.annotations({ description: "a number that will be parsed into a TimeZone.Offset" }), TimeZoneOffsetFromSelf, { strict: true, decode: dateTime.zoneMakeOffset, encode: (tz) => tz.offset } ).annotations({ identifier: "TimeZoneOffset" }) {} const timeZoneNamedArbitrary = (): LazyArbitrary => (fc) => fc.constantFrom(...Intl.supportedValuesOf("timeZone")).map(dateTime.zoneUnsafeMakeNamed) /** * Describes a schema that represents a `TimeZone.Named` instance. * * @category TimeZone constructors * @since 0.70.0 */ export class TimeZoneNamedFromSelf extends declare( dateTime.isTimeZoneNamed, { identifier: "TimeZoneNamedFromSelf", description: "a TimeZone.Named instance", pretty: (): pretty_.Pretty => (zone) => zone.toString(), arbitrary: timeZoneNamedArbitrary } ) {} /** * Defines a schema that attempts to convert a `string` to a `TimeZone.Named` instance using the `DateTime.zoneUnsafeMakeNamed` constructor. * * @category TimeZone transformations * @since 0.70.0 */ export class TimeZoneNamed extends transformOrFail( String$.annotations({ description: "a string that will be parsed into a TimeZone.Named" }), TimeZoneNamedFromSelf, { strict: true, decode: (s, _, ast) => ParseResult.try({ try: () => dateTime.zoneUnsafeMakeNamed(s), catch: () => new ParseResult.Type(ast, s) }), encode: (tz) => ParseResult.succeed(tz.id) } ).annotations({ identifier: "TimeZoneNamed" }) {} /** * @category api interface * @since 0.70.0 */ export interface TimeZoneFromSelf extends Union<[typeof TimeZoneOffsetFromSelf, typeof TimeZoneNamedFromSelf]> { annotations(annotations: Annotations.Schema): TimeZoneFromSelf } /** * @category TimeZone constructors * @since 0.70.0 */ export const TimeZoneFromSelf: TimeZoneFromSelf = Union(TimeZoneOffsetFromSelf, TimeZoneNamedFromSelf) /** * Defines a schema that attempts to convert a `string` to a `TimeZone` using the `DateTime.zoneFromString` constructor. * * @category TimeZone transformations * @since 0.70.0 */ export class TimeZone extends transformOrFail( String$.annotations({ description: "a string that will be parsed into a TimeZone" }), TimeZoneFromSelf, { strict: true, decode: (s, _, ast) => option_.match(dateTime.zoneFromString(s), { onNone: () => ParseResult.fail(new ParseResult.Type(ast, s)), onSome: ParseResult.succeed }), encode: (tz) => ParseResult.succeed(dateTime.zoneToString(tz)) } ).annotations({ identifier: "TimeZone" }) {} const timeZoneArbitrary: LazyArbitrary = (fc) => fc.oneof( timeZoneOffsetArbitrary()(fc), timeZoneNamedArbitrary()(fc) ) /** * Describes a schema that represents a `DateTime.Zoned` instance. * * @category DateTime.Zoned constructors * @since 0.70.0 */ export class DateTimeZonedFromSelf extends declare( (u) => dateTime.isDateTime(u) && dateTime.isZoned(u), { identifier: "DateTimeZonedFromSelf", description: "a DateTime.Zoned instance", pretty: (): pretty_.Pretty => (dateTime) => dateTime.toString(), arbitrary: (): LazyArbitrary => (fc) => fc.date().chain((date) => timeZoneArbitrary(fc).map((timeZone) => dateTime.unsafeMakeZoned(date, { timeZone }))), equivalence: () => dateTime.Equivalence } ) {} /** * Defines a schema that attempts to convert a `string` to a `DateTime.Zoned` instance. * * @category DateTime.Zoned transformations * @since 0.70.0 */ export class DateTimeZoned extends transformOrFail( String$.annotations({ description: "a string that will be parsed into a DateTime.Zoned" }), DateTimeZonedFromSelf, { strict: true, decode: (s, _, ast) => option_.match(dateTime.makeZonedFromString(s), { onNone: () => ParseResult.fail(new ParseResult.Type(ast, s)), onSome: ParseResult.succeed }), encode: (dt) => ParseResult.succeed(dateTime.formatIsoZoned(dt)) } ).annotations({ identifier: "DateTimeZoned" }) {} /** * @category Option utils * @since 0.67.0 */ export type OptionEncoded = | { readonly _tag: "None" } | { readonly _tag: "Some" readonly value: I } const OptionNoneEncoded = Struct({ _tag: Literal("None") }).annotations({ description: "NoneEncoded" }) const optionSomeEncoded = (value: Schema) => Struct({ _tag: Literal("Some"), value }).annotations({ description: `SomeEncoded<${format(value)}>` }) const optionEncoded = (value: Schema) => Union( OptionNoneEncoded, optionSomeEncoded(value) ).annotations({ description: `OptionEncoded<${format(value)}>` }) const optionDecode = (input: OptionEncoded): option_.Option => input._tag === "None" ? option_.none() : option_.some(input.value) const optionArbitrary = (value: LazyArbitrary, ctx: GenerationContext): LazyArbitrary> => (fc) => fc.oneof( ctx, fc.record({ _tag: fc.constant("None" as const) }), fc.record({ _tag: fc.constant("Some" as const), value: value(fc) }) ).map(optionDecode) const optionPretty = (value: pretty_.Pretty): pretty_.Pretty> => option_.match({ onNone: () => "none()", onSome: (a) => `some(${value(a)})` }) const optionParse = (decodeUnknown: ParseResult.DecodeUnknown): ParseResult.DeclarationDecodeUnknown, R> => (u, options, ast) => option_.isOption(u) ? option_.isNone(u) ? ParseResult.succeed(option_.none()) : toComposite(decodeUnknown(u.value, options), option_.some, ast, u) : ParseResult.fail(new ParseResult.Type(ast, u)) /** * @category api interface * @since 0.67.0 */ export interface OptionFromSelf extends AnnotableClass< OptionFromSelf, option_.Option>, option_.Option>, Schema.Context > {} /** * @category Option transformations * @since 0.67.0 */ export const OptionFromSelf = ( value: Value ): OptionFromSelf => { return declare( [value], { decode: (value) => optionParse(ParseResult.decodeUnknown(value)), encode: (value) => optionParse(ParseResult.encodeUnknown(value)) }, { description: `Option<${format(value)}>`, pretty: optionPretty, arbitrary: optionArbitrary, equivalence: option_.getEquivalence } ) } const makeNoneEncoded = { _tag: "None" } as const const makeSomeEncoded = (value: A) => ({ _tag: "Some", value } as const) /** * @category api interface * @since 0.67.0 */ export interface Option extends AnnotableClass< Option, option_.Option>, OptionEncoded>, Schema.Context > {} /** * @category Option transformations * @since 0.67.0 */ export const Option = (value: Value): Option => { const value_ = asSchema(value) return transform( optionEncoded(value_), OptionFromSelf(typeSchema(value_)), { strict: true, decode: optionDecode, encode: option_.match({ onNone: () => makeNoneEncoded, onSome: makeSomeEncoded }) } ) } /** * @category api interface * @since 0.67.0 */ export interface OptionFromNullOr extends AnnotableClass< OptionFromNullOr, option_.Option>, Schema.Encoded | null, Schema.Context > {} /** * @category Option transformations * @since 0.67.0 */ export const OptionFromNullOr = ( value: Value ): OptionFromNullOr => { const value_ = asSchema(value) return transform(NullOr(value_), OptionFromSelf(typeSchema(value_)), { strict: true, decode: option_.fromNullable, encode: option_.getOrNull }) } /** * @category api interface * @since 0.67.0 */ export interface OptionFromNullishOr extends AnnotableClass< OptionFromNullishOr, option_.Option>, Schema.Encoded | null | undefined, Schema.Context > {} /** * @category Option transformations * @since 0.67.0 */ export const OptionFromNullishOr = ( value: Value, onNoneEncoding: null | undefined ): OptionFromNullishOr => { const value_ = asSchema(value) return transform( NullishOr(value_), OptionFromSelf(typeSchema(value_)), { strict: true, decode: option_.fromNullable, encode: onNoneEncoding === null ? option_.getOrNull : option_.getOrUndefined } ) } /** * @category api interface * @since 0.67.0 */ export interface OptionFromUndefinedOr extends AnnotableClass< OptionFromUndefinedOr, option_.Option>, Schema.Encoded | undefined, Schema.Context > {} /** * @category Option transformations * @since 0.67.0 */ export const OptionFromUndefinedOr = ( value: Value ): OptionFromUndefinedOr => { const value_ = asSchema(value) return transform(UndefinedOr(value_), OptionFromSelf(typeSchema(value_)), { strict: true, decode: option_.fromNullable, encode: option_.getOrUndefined }) } /** * Transforms strings into an Option type, effectively filtering out empty or * whitespace-only strings by trimming them and checking their length. Returns * `none` for invalid inputs and `some` for valid non-empty strings. * * @example * import { Schema } from "@effect/schema" * * console.log(Schema.decodeSync(Schema.OptionFromNonEmptyTrimmedString)("")) // Option.none() * console.log(Schema.decodeSync(Schema.OptionFromNonEmptyTrimmedString)(" a ")) // Option.some("a") * console.log(Schema.decodeSync(Schema.OptionFromNonEmptyTrimmedString)("a")) // Option.some("a") * * @category Option transformations * @since 0.69.3 */ export const OptionFromNonEmptyTrimmedString = transform(String$, OptionFromSelf(NonEmptyTrimmedString), { strict: true, decode: (s) => { const out = s.trim() return out.length === 0 ? option_.none() : option_.some(out) }, encode: option_.getOrElse(() => "") }) /** * @category Either utils * @since 0.67.0 */ export type RightEncoded = { readonly _tag: "Right" readonly right: IA } /** * @category Either utils * @since 0.67.0 */ export type LeftEncoded = { readonly _tag: "Left" readonly left: IE } /** * @category Either utils * @since 0.67.0 */ export type EitherEncoded = RightEncoded | LeftEncoded const rightEncoded = (right: Schema): Schema, RightEncoded, RR> => Struct({ _tag: Literal("Right"), right }).annotations({ description: `RightEncoded<${format(right)}>` }) const leftEncoded = (left: Schema): Schema, LeftEncoded
  • , LR> => Struct({ _tag: Literal("Left"), left }).annotations({ description: `LeftEncoded<${format(left)}>` }) const eitherEncoded = ( right: Schema, left: Schema ) => Union(rightEncoded(right), leftEncoded(left)).annotations({ description: `EitherEncoded<${format(left)}, ${format(right)}>` }) const eitherDecode = (input: EitherEncoded): either_.Either => input._tag === "Left" ? either_.left(input.left) : either_.right(input.right) const eitherArbitrary = ( right: LazyArbitrary, left: LazyArbitrary ): LazyArbitrary> => (fc) => fc.oneof( fc.record({ _tag: fc.constant("Left" as const), left: left(fc) }), fc.record({ _tag: fc.constant("Right" as const), right: right(fc) }) ).map(eitherDecode) const eitherPretty = ( right: pretty_.Pretty, left: pretty_.Pretty ): pretty_.Pretty> => either_.match({ onLeft: (e) => `left(${left(e)})`, onRight: (a) => `right(${right(a)})` }) const eitherParse = ( parseRight: ParseResult.DecodeUnknown, decodeUnknownLeft: ParseResult.DecodeUnknown ): ParseResult.DeclarationDecodeUnknown, LR | RR> => (u, options, ast) => either_.isEither(u) ? either_.match(u, { onLeft: (left) => toComposite(decodeUnknownLeft(left, options), either_.left, ast, u), onRight: (right) => toComposite(parseRight(right, options), either_.right, ast, u) }) : ParseResult.fail(new ParseResult.Type(ast, u)) /** * @category api interface * @since 0.67.0 */ export interface EitherFromSelf extends AnnotableClass< EitherFromSelf, either_.Either, Schema.Type>, either_.Either, Schema.Encoded>, Schema.Context | Schema.Context > {} /** * @category Either transformations * @since 0.67.0 */ export const EitherFromSelf = ({ left, right }: { readonly left: L readonly right: R }): EitherFromSelf => { return declare( [right, left], { decode: (right, left) => eitherParse(ParseResult.decodeUnknown(right), ParseResult.decodeUnknown(left)), encode: (right, left) => eitherParse(ParseResult.encodeUnknown(right), ParseResult.encodeUnknown(left)) }, { description: `Either<${format(right)}, ${format(left)}>`, pretty: eitherPretty, arbitrary: eitherArbitrary, equivalence: (right, left) => either_.getEquivalence({ left, right }) } ) } const makeLeftEncoded = (left: E) => (({ _tag: "Left", left }) as const) const makeRightEncoded = (right: A) => (({ _tag: "Right", right }) as const) /** * @category api interface * @since 0.67.0 */ export interface Either extends AnnotableClass< Either, either_.Either, Schema.Type>, EitherEncoded, Schema.Encoded>, Schema.Context | Schema.Context > {} /** * @category Either transformations * @since 0.67.0 */ export const Either = ({ left, right }: { readonly left: L readonly right: R }): Either => { const right_ = asSchema(right) const left_ = asSchema(left) return transform( eitherEncoded(right_, left_), EitherFromSelf({ left: typeSchema(left_), right: typeSchema(right_) }), { strict: true, decode: eitherDecode, encode: either_.match({ onLeft: makeLeftEncoded, onRight: makeRightEncoded }) } ) } /** * @category api interface * @since 0.67.0 */ export interface EitherFromUnion extends AnnotableClass< EitherFromUnion, either_.Either, Schema.Type>, Schema.Encoded | Schema.Encoded, Schema.Context | Schema.Context > {} /** * @example * import * as Schema from "@effect/schema/Schema" * * // Schema> * Schema.EitherFromUnion({ left: Schema.String, right: Schema.Number }) * * @category Either transformations * @since 0.67.0 */ export const EitherFromUnion = ({ left, right }: { readonly left: L readonly right: R }): EitherFromUnion => { const right_ = asSchema(right) const left_ = asSchema(left) const toright = typeSchema(right_) const toleft = typeSchema(left_) const fromRight = transform(right_, rightEncoded(toright), { strict: true, decode: makeRightEncoded, encode: (r) => r.right }) const fromLeft = transform(left_, leftEncoded(toleft), { strict: true, decode: makeLeftEncoded, encode: (l) => l.left }) return transform( Union(fromRight, fromLeft), EitherFromSelf({ left: toleft, right: toright }), { strict: true, decode: (from) => from._tag === "Left" ? either_.left(from.left) : either_.right(from.right), encode: either_.match({ onLeft: makeLeftEncoded, onRight: makeRightEncoded }) } ) } const mapArbitrary = ( key: LazyArbitrary, value: LazyArbitrary, ctx: GenerationContext ): LazyArbitrary> => { return (fc) => { const items = fc.array(fc.tuple(key(fc), value(fc))) return (ctx.depthIdentifier !== undefined ? fc.oneof(ctx, fc.constant([]), items) : items).map((as) => new Map(as)) } } const readonlyMapPretty = ( key: pretty_.Pretty, value: pretty_.Pretty ): pretty_.Pretty> => (map) => `new Map([${ Array.from(map.entries()) .map(([k, v]) => `[${key(k)}, ${value(v)}]`) .join(", ") }])` const readonlyMapEquivalence = ( key: Equivalence.Equivalence, value: Equivalence.Equivalence ): Equivalence.Equivalence> => { const arrayEquivalence = array_.getEquivalence( Equivalence.make<[K, V]>(([ka, va], [kb, vb]) => key(ka, kb) && value(va, vb)) ) return Equivalence.make((a, b) => arrayEquivalence(Array.from(a.entries()), Array.from(b.entries()))) } const readonlyMapParse = ( decodeUnknown: ParseResult.DecodeUnknown, R> ): ParseResult.DeclarationDecodeUnknown, R> => (u, options, ast) => Predicate.isMap(u) ? toComposite(decodeUnknown(Array.from(u.entries()), options), (as) => new Map(as), ast, u) : ParseResult.fail(new ParseResult.Type(ast, u)) /** * @category api interface * @since 0.67.0 */ export interface ReadonlyMapFromSelf extends AnnotableClass< ReadonlyMapFromSelf, ReadonlyMap, Schema.Type>, ReadonlyMap, Schema.Encoded>, Schema.Context | Schema.Context > {} const mapFromSelf_ = ( key: K, value: V, description: string ): ReadonlyMapFromSelf => declare( [key, value], { decode: (Key, Value) => readonlyMapParse(ParseResult.decodeUnknown(Array$(Tuple(Key, Value)))), encode: (Key, Value) => readonlyMapParse(ParseResult.encodeUnknown(Array$(Tuple(Key, Value)))) }, { description, pretty: readonlyMapPretty, arbitrary: mapArbitrary, equivalence: readonlyMapEquivalence } ) /** * @category ReadonlyMap * @since 0.67.0 */ export const ReadonlyMapFromSelf = ({ key, value }: { readonly key: K readonly value: V }): ReadonlyMapFromSelf => mapFromSelf_(key, value, `ReadonlyMap<${format(key)}, ${format(value)}>`) /** * @category api interface * @since 0.67.0 */ export interface MapFromSelf extends AnnotableClass< MapFromSelf, Map, Schema.Type>, ReadonlyMap, Schema.Encoded>, Schema.Context | Schema.Context > {} /** * @category Map * @since 0.67.0 */ export const MapFromSelf = ({ key, value }: { readonly key: K readonly value: V }): MapFromSelf => mapFromSelf_(key, value, `Map<${format(key)}, ${format(value)}>`) as any /** * @category api interface * @since 0.67.0 */ export interface ReadonlyMap$ extends AnnotableClass< ReadonlyMap$, ReadonlyMap, Schema.Type>, ReadonlyArray, Schema.Encoded]>, Schema.Context | Schema.Context > {} /** * @category ReadonlyMap transformations * @since 0.67.0 */ export const ReadonlyMap = ({ key, value }: { readonly key: K readonly value: V }): ReadonlyMap$ => { const key_ = asSchema(key) const value_ = asSchema(value) return transform( Array$(Tuple(key_, value_)), ReadonlyMapFromSelf({ key: typeSchema(key_), value: typeSchema(value_) }), { strict: true, decode: (as) => new Map(as), encode: (map) => Array.from(map.entries()) } ) } /** * @category api interface * @since 0.67.0 */ export interface Map$ extends AnnotableClass< Map$, Map, Schema.Type>, ReadonlyArray, Schema.Encoded]>, Schema.Context | Schema.Context > {} const map = ({ key, value }: { readonly key: K readonly value: V }): Map$ => { const key_ = asSchema(key) const value_ = asSchema(value) return transform( Array$(Tuple(key_, value_)), MapFromSelf({ key: typeSchema(key_), value: typeSchema(value_) }), { strict: true, decode: (as) => new Map(as), encode: (map) => Array.from(map.entries()) } ) } export { /** * @category Map transformations * @since 0.67.0 */ map as Map } /** * @category ReadonlyMap transformations * @since 0.68.15 */ export const ReadonlyMapFromRecord = ({ key, value }: { key: Schema value: Schema }): Schema, { readonly [x: string]: VI }, KR | VR> => transform( Record({ key: encodedBoundSchema(key), value }).annotations({ description: "a record that will be parsed into a ReadonlyMap" }), ReadonlyMapFromSelf({ key, value: typeSchema(value) }), { strict: true, decode: (record) => new Map(Object.entries(record)), encode: record_.fromEntries } ) /** * @category Map transformations * @since 0.68.15 */ export const MapFromRecord = ({ key, value }: { key: Schema value: Schema }): Schema, { readonly [x: string]: VI }, KR | VR> => transform( Record({ key: encodedBoundSchema(key), value }).annotations({ description: "a record that will be parsed into a Map" }), MapFromSelf({ key, value: typeSchema(value) }), { strict: true, decode: (record) => new Map(Object.entries(record)), encode: record_.fromEntries } ) const setArbitrary = (item: LazyArbitrary, ctx: GenerationContext): LazyArbitrary> => (fc) => { const items = fc.array(item(fc)) return (ctx.depthIdentifier !== undefined ? fc.oneof(ctx, fc.constant([]), items) : items).map((as) => new Set(as)) } const readonlySetPretty = (item: pretty_.Pretty): pretty_.Pretty> => (set) => `new Set([${Array.from(set.values()).map((a) => item(a)).join(", ")}])` const readonlySetEquivalence = ( item: Equivalence.Equivalence ): Equivalence.Equivalence> => { const arrayEquivalence = array_.getEquivalence(item) return Equivalence.make((a, b) => arrayEquivalence(Array.from(a.values()), Array.from(b.values()))) } const readonlySetParse = ( decodeUnknown: ParseResult.DecodeUnknown, R> ): ParseResult.DeclarationDecodeUnknown, R> => (u, options, ast) => Predicate.isSet(u) ? toComposite(decodeUnknown(Array.from(u.values()), options), (as) => new Set(as), ast, u) : ParseResult.fail(new ParseResult.Type(ast, u)) /** * @category api interface * @since 0.67.0 */ export interface ReadonlySetFromSelf extends AnnotableClass< ReadonlySetFromSelf, ReadonlySet>, ReadonlySet>, Schema.Context > {} const setFromSelf_ = (value: Value, description: string): ReadonlySetFromSelf => declare( [value], { decode: (item) => readonlySetParse(ParseResult.decodeUnknown(Array$(item))), encode: (item) => readonlySetParse(ParseResult.encodeUnknown(Array$(item))) }, { description, pretty: readonlySetPretty, arbitrary: setArbitrary, equivalence: readonlySetEquivalence } ) /** * @category ReadonlySet * @since 0.67.0 */ export const ReadonlySetFromSelf = (value: Value): ReadonlySetFromSelf => setFromSelf_(value, `ReadonlySet<${format(value)}>`) /** * @category api interface * @since 0.67.0 */ export interface SetFromSelf extends AnnotableClass< SetFromSelf, Set>, ReadonlySet>, Schema.Context > {} /** * @category Set * @since 0.67.0 */ export const SetFromSelf = (value: Value): SetFromSelf => setFromSelf_(value, `Set<${format(value)}>`) as any /** * @category api interface * @since 0.67.0 */ export interface ReadonlySet$ extends AnnotableClass< ReadonlySet$, ReadonlySet>, ReadonlyArray>, Schema.Context > {} /** * @category ReadonlySet transformations * @since 0.67.0 */ export const ReadonlySet = (value: Value): ReadonlySet$ => { const value_ = asSchema(value) return transform( Array$(value_), ReadonlySetFromSelf(typeSchema(value_)), { strict: true, decode: (as) => new Set(as), encode: (set) => Array.from(set) } ) } /** * @category api interface * @since 0.67.0 */ export interface Set$ extends AnnotableClass< Set$, Set>, ReadonlyArray>, Schema.Context > {} const set = (value: Value): Set$ => { const value_ = asSchema(value) return transform( Array$(value_), SetFromSelf(typeSchema(value_)), { strict: true, decode: (as) => new Set(as), encode: (set) => Array.from(set) } ) } export { /** * @category Set transformations * @since 0.67.0 */ set as Set } const bigDecimalPretty = (): pretty_.Pretty => (val) => `BigDecimal(${bigDecimal_.format(bigDecimal_.normalize(val))})` const bigDecimalArbitrary = (): LazyArbitrary => (fc) => fc.tuple(fc.bigInt(), fc.integer()).map(([value, scale]) => bigDecimal_.make(value, scale)) /** * @category BigDecimal constructors * @since 0.67.0 */ export class BigDecimalFromSelf extends declare( bigDecimal_.isBigDecimal, { identifier: "BigDecimalFromSelf", pretty: bigDecimalPretty, arbitrary: bigDecimalArbitrary, equivalence: () => bigDecimal_.Equivalence } ) {} /** * @category BigDecimal transformations * @since 0.67.0 */ export class BigDecimal extends transformOrFail( String$.annotations({ description: "a string that will be parsed into a BigDecimal" }), BigDecimalFromSelf, { strict: true, decode: (num, _, ast) => bigDecimal_.fromString(num).pipe(option_.match({ onNone: () => ParseResult.fail(new ParseResult.Type(ast, num)), onSome: (val) => ParseResult.succeed(bigDecimal_.normalize(val)) })), encode: (val) => ParseResult.succeed(bigDecimal_.format(bigDecimal_.normalize(val))) } ).annotations({ identifier: "BigDecimal" }) {} /** * A schema that transforms a `number` into a `BigDecimal`. * When encoding, this Schema will produce incorrect results if the BigDecimal exceeds the 64-bit range of a number. * * @category BigDecimal transformations * @since 0.67.0 */ export class BigDecimalFromNumber extends transformOrFail( Number$.annotations({ description: "a number that will be parsed into a BigDecimal" }), BigDecimalFromSelf, { strict: true, decode: (num) => ParseResult.succeed(bigDecimal_.fromNumber(num)), encode: (val) => ParseResult.succeed(bigDecimal_.unsafeToNumber(val)) } ).annotations({ identifier: "BigDecimalFromNumber" }) {} /** * @category type id * @since 0.67.0 */ export const GreaterThanBigDecimalTypeId: unique symbol = Symbol.for("@effect/schema/TypeId/GreaterThanBigDecimal") /** * @category BigDecimal filters * @since 0.67.0 */ export const greaterThanBigDecimal = ( min: bigDecimal_.BigDecimal, annotations?: Annotations.Filter ) => (self: Schema): filter> => self.pipe( filter((a) => bigDecimal_.greaterThan(a, min), { typeId: { id: GreaterThanBigDecimalTypeId, annotation: { min } }, description: `a BigDecimal greater than ${bigDecimal_.format(min)}`, ...annotations }) ) /** * @category type id * @since 0.67.0 */ export const GreaterThanOrEqualToBigDecimalTypeId: unique symbol = Symbol.for( "@effect/schema/TypeId/GreaterThanOrEqualToBigDecimal" ) /** * @category BigDecimal filters * @since 0.67.0 */ export const greaterThanOrEqualToBigDecimal = ( min: bigDecimal_.BigDecimal, annotations?: Annotations.Filter ) => (self: Schema): filter> => self.pipe( filter((a) => bigDecimal_.greaterThanOrEqualTo(a, min), { typeId: { id: GreaterThanOrEqualToBigDecimalTypeId, annotation: { min } }, description: `a BigDecimal greater than or equal to ${bigDecimal_.format(min)}`, ...annotations }) ) /** * @category type id * @since 0.67.0 */ export const LessThanBigDecimalTypeId: unique symbol = Symbol.for("@effect/schema/TypeId/LessThanBigDecimal") /** * @category BigDecimal filters * @since 0.67.0 */ export const lessThanBigDecimal = ( max: bigDecimal_.BigDecimal, annotations?: Annotations.Filter ) => (self: Schema): filter> => self.pipe( filter((a) => bigDecimal_.lessThan(a, max), { typeId: { id: LessThanBigDecimalTypeId, annotation: { max } }, description: `a BigDecimal less than ${bigDecimal_.format(max)}`, ...annotations }) ) /** * @category type id * @since 0.67.0 */ export const LessThanOrEqualToBigDecimalTypeId: unique symbol = Symbol.for( "@effect/schema/TypeId/LessThanOrEqualToBigDecimal" ) /** * @category BigDecimal filters * @since 0.67.0 */ export const lessThanOrEqualToBigDecimal = ( max: bigDecimal_.BigDecimal, annotations?: Annotations.Filter ) => (self: Schema): filter> => self.pipe( filter((a) => bigDecimal_.lessThanOrEqualTo(a, max), { typeId: { id: LessThanOrEqualToBigDecimalTypeId, annotation: { max } }, description: `a BigDecimal less than or equal to ${bigDecimal_.format(max)}`, ...annotations }) ) /** * @category type id * @since 0.67.0 */ export const PositiveBigDecimalTypeId: unique symbol = Symbol.for( "@effect/schema/TypeId/PositiveBigDecimal" ) /** * @category BigDecimal filters * @since 0.67.0 */ export const positiveBigDecimal = ( annotations?: Annotations.Filter ) => (self: Schema): filter> => self.pipe( filter((a) => bigDecimal_.isPositive(a), { typeId: { id: PositiveBigDecimalTypeId, annotation: {} }, description: `a positive BigDecimal`, ...annotations }) ) /** * @category BigDecimal constructors * @since 0.67.0 */ export const PositiveBigDecimalFromSelf: filter> = BigDecimalFromSelf.pipe( positiveBigDecimal({ identifier: "PositiveBigDecimalFromSelf", title: "PositiveBigDecimalFromSelf" }) ) /** * @category type id * @since 0.67.0 */ export const NonNegativeBigDecimalTypeId: unique symbol = Symbol.for( "@effect/schema/TypeId/NonNegativeBigDecimal" ) /** * @category BigDecimal filters * @since 0.67.0 */ export const nonNegativeBigDecimal = ( annotations?: Annotations.Filter ) => (self: Schema): filter> => self.pipe( filter((a) => a.value >= 0n, { typeId: { id: NonNegativeBigDecimalTypeId, annotation: {} }, description: `a non-negative BigDecimal`, ...annotations }) ) /** * @category BigDecimal constructors * @since 0.67.0 */ export const NonNegativeBigDecimalFromSelf: filter> = BigDecimalFromSelf.pipe( nonNegativeBigDecimal({ identifier: "NonNegativeBigDecimalFromSelf", title: "NonNegativeBigDecimalFromSelf" }) ) /** * @category type id * @since 0.67.0 */ export const NegativeBigDecimalTypeId: unique symbol = Symbol.for( "@effect/schema/TypeId/NegativeBigDecimal" ) /** * @category BigDecimal filters * @since 0.67.0 */ export const negativeBigDecimal = ( annotations?: Annotations.Filter ) => (self: Schema): filter> => self.pipe( filter((a) => bigDecimal_.isNegative(a), { typeId: { id: NegativeBigDecimalTypeId, annotation: {} }, description: `a negative BigDecimal`, ...annotations }) ) /** * @category BigDecimal constructors * @since 0.67.0 */ export const NegativeBigDecimalFromSelf: filter> = BigDecimalFromSelf.pipe( negativeBigDecimal({ identifier: "NegativeBigDecimalFromSelf", title: "NegativeBigDecimalFromSelf" }) ) /** * @category type id * @since 0.67.0 */ export const NonPositiveBigDecimalTypeId: unique symbol = Symbol.for( "@effect/schema/TypeId/NonPositiveBigDecimal" ) /** * @category BigDecimal filters * @since 0.67.0 */ export const nonPositiveBigDecimal = ( annotations?: Annotations.Filter ) => (self: Schema): filter> => self.pipe( filter((a) => a.value <= 0n, { typeId: { id: NonPositiveBigDecimalTypeId, annotation: {} }, description: `a non-positive BigDecimal`, ...annotations }) ) /** * @category BigDecimal constructors * @since 0.67.0 */ export const NonPositiveBigDecimalFromSelf: filter> = BigDecimalFromSelf.pipe( nonPositiveBigDecimal({ identifier: "NonPositiveBigDecimalFromSelf", title: "NonPositiveBigDecimalFromSelf" }) ) /** * @category type id * @since 0.67.0 */ export const BetweenBigDecimalTypeId: unique symbol = Symbol.for("@effect/schema/TypeId/BetweenBigDecimal") /** * @category BigDecimal filters * @since 0.67.0 */ export const betweenBigDecimal = ( minimum: bigDecimal_.BigDecimal, maximum: bigDecimal_.BigDecimal, annotations?: Annotations.Filter ) => (self: Schema): filter> => self.pipe( filter((a) => bigDecimal_.between(a, { minimum, maximum }), { typeId: { id: BetweenBigDecimalTypeId, annotation: { maximum, minimum } }, description: `a BigDecimal between ${bigDecimal_.format(minimum)} and ${bigDecimal_.format(maximum)}`, ...annotations }) ) /** * Clamps a `BigDecimal` between a minimum and a maximum value. * * @category BigDecimal transformations * @since 0.67.0 */ export const clampBigDecimal = (minimum: bigDecimal_.BigDecimal, maximum: bigDecimal_.BigDecimal) => (self: Schema): transform, filter>> => transform( self, self.pipe(typeSchema, betweenBigDecimal(minimum, maximum)), { strict: false, decode: (self) => bigDecimal_.clamp(self, { minimum, maximum }), encode: identity } ) const chunkArbitrary = (item: LazyArbitrary, ctx: GenerationContext): LazyArbitrary> => (fc) => { const items = fc.array(item(fc)) return (ctx.depthIdentifier !== undefined ? fc.oneof(ctx, fc.constant([]), items) : items).map(chunk_.fromIterable) } const chunkPretty = (item: pretty_.Pretty): pretty_.Pretty> => (c) => `Chunk(${chunk_.toReadonlyArray(c).map(item).join(", ")})` const chunkParse = ( decodeUnknown: ParseResult.DecodeUnknown, R> ): ParseResult.DeclarationDecodeUnknown, R> => (u, options, ast) => chunk_.isChunk(u) ? chunk_.isEmpty(u) ? ParseResult.succeed(chunk_.empty()) : toComposite(decodeUnknown(chunk_.toReadonlyArray(u), options), chunk_.fromIterable, ast, u) : ParseResult.fail(new ParseResult.Type(ast, u)) /** * @category api interface * @since 0.67.0 */ export interface ChunkFromSelf extends AnnotableClass< ChunkFromSelf, chunk_.Chunk>, chunk_.Chunk>, Schema.Context > {} /** * @category Chunk * @since 0.67.0 */ export const ChunkFromSelf = (value: Value): ChunkFromSelf => { return declare( [value], { decode: (item) => chunkParse(ParseResult.decodeUnknown(Array$(item))), encode: (item) => chunkParse(ParseResult.encodeUnknown(Array$(item))) }, { description: `Chunk<${format(value)}>`, pretty: chunkPretty, arbitrary: chunkArbitrary, equivalence: chunk_.getEquivalence } ) } /** * @category api interface * @since 0.67.0 */ export interface Chunk extends AnnotableClass< Chunk, chunk_.Chunk>, ReadonlyArray>, Schema.Context > {} /** * @category Chunk transformations * @since 0.67.0 */ export const Chunk = (value: Value): Chunk => { const value_ = asSchema(value) return transform( Array$(value_), ChunkFromSelf(typeSchema(value_)), { strict: true, decode: (as) => as.length === 0 ? chunk_.empty() : chunk_.fromIterable(as), encode: chunk_.toReadonlyArray } ) } /** * @category api interface * @since 0.67.23 */ export interface NonEmptyChunkFromSelf extends AnnotableClass< NonEmptyChunkFromSelf, chunk_.NonEmptyChunk>, chunk_.NonEmptyChunk>, Schema.Context > {} const nonEmptyChunkArbitrary = (item: LazyArbitrary): LazyArbitrary> => (fc) => fastCheck_.array(item(fc), { minLength: 1 }).map((as) => chunk_.unsafeFromNonEmptyArray(as as any)) const nonEmptyChunkPretty = (item: pretty_.Pretty): pretty_.Pretty> => (c) => `NonEmptyChunk(${chunk_.toReadonlyArray(c).map(item).join(", ")})` const nonEmptyChunkParse = ( decodeUnknown: ParseResult.DecodeUnknown, R> ): ParseResult.DeclarationDecodeUnknown, R> => (u, options, ast) => chunk_.isChunk(u) && chunk_.isNonEmpty(u) ? toComposite(decodeUnknown(chunk_.toReadonlyArray(u), options), chunk_.unsafeFromNonEmptyArray, ast, u) : ParseResult.fail(new ParseResult.Type(ast, u)) /** * @category Chunk * @since 0.67.23 */ export const NonEmptyChunkFromSelf = (value: Value): NonEmptyChunkFromSelf => { return declare( [value], { decode: (item) => nonEmptyChunkParse(ParseResult.decodeUnknown(NonEmptyArray(item))), encode: (item) => nonEmptyChunkParse(ParseResult.encodeUnknown(NonEmptyArray(item))) }, { description: `NonEmptyChunk<${format(value)}>`, pretty: nonEmptyChunkPretty, arbitrary: nonEmptyChunkArbitrary, equivalence: chunk_.getEquivalence } ) } /** * @category api interface * @since 0.67.23 */ export interface NonEmptyChunk extends AnnotableClass< NonEmptyChunk, chunk_.NonEmptyChunk>, array_.NonEmptyReadonlyArray>, Schema.Context > {} /** * @category Chunk transformations * @since 0.67.23 */ export const NonEmptyChunk = (value: Value): NonEmptyChunk => { const value_ = asSchema(value) return transform( NonEmptyArray(value_), NonEmptyChunkFromSelf(typeSchema(value_)), { strict: true, decode: chunk_.unsafeFromNonEmptyArray, encode: chunk_.toReadonlyArray } ) } const toData = > | ReadonlyArray>(a: A): A => Array.isArray(a) ? data_.array(a) : data_.struct(a) const dataArbitrary = > | ReadonlyArray>( item: LazyArbitrary ): LazyArbitrary => (fc) => item(fc).map(toData) const dataPretty = > | ReadonlyArray>( item: pretty_.Pretty ): pretty_.Pretty => (d) => `Data(${item(d)})` const dataParse = > | ReadonlyArray>( decodeUnknown: ParseResult.DecodeUnknown ): ParseResult.DeclarationDecodeUnknown => (u, options, ast) => Equal.isEqual(u) ? toComposite(decodeUnknown(u, options), toData, ast, u) : ParseResult.fail(new ParseResult.Type(ast, u)) /** * @category Data transformations * @since 0.67.0 */ export const DataFromSelf = < R, I extends Readonly> | ReadonlyArray, A extends Readonly> | ReadonlyArray >( item: Schema ): SchemaClass => declare( [item], { decode: (item) => dataParse(ParseResult.decodeUnknown(item)), encode: (item) => dataParse(ParseResult.encodeUnknown(item)) }, { description: `Data<${format(item)}>`, pretty: dataPretty, arbitrary: dataArbitrary } ) /** * @category Data transformations * @since 0.67.0 */ export const Data = < R, I extends Readonly> | ReadonlyArray, A extends Readonly> | ReadonlyArray >( item: Schema ): SchemaClass => transform( item, DataFromSelf(typeSchema(item)), { strict: false, decode: toData, encode: (a) => Array.isArray(a) ? Array.from(a) : Object.assign({}, a) } ) type MissingSelfGeneric = `Missing \`Self\` generic - use \`class Self extends ${Usage}()(${Params}{ ... })\`` type RequiredKeys = { [K in keyof T]-?: {} extends Pick ? never : K }[keyof T] /** * @category api interface * @since 0.67.0 */ export interface Class extends Schema, R> { new( props: RequiredKeys extends never ? void | Simplify : Simplify, options?: MakeOptions ): Struct.Type & Omit & Proto /** @since 0.69.3 */ readonly ast: AST.Transformation make, X>(this: { new(...args: Args): X }, ...args: Args): X annotations(annotations: Annotations.Schema): SchemaClass, R> readonly fields: { readonly [K in keyof Fields]: Fields[K] } readonly identifier: string extend(identifier: string): ( fields: newFields | HasFields, annotations?: Annotations.Schema ) => [Extended] extends [never] ? MissingSelfGeneric<"Base.extend"> : Class< Extended, Fields & newFields, I & Struct.Encoded, R | Struct.Context, C & Struct.Constructor, Self, Proto > transformOrFail(identifier: string): < newFields extends Struct.Fields, R2, R3 >( fields: newFields, options: { readonly decode: ( input: Simplify>, options: ParseOptions, ast: AST.Transformation ) => Effect.Effect>, ParseResult.ParseIssue, R2> readonly encode: ( input: Simplify>, options: ParseOptions, ast: AST.Transformation ) => Effect.Effect, ParseResult.ParseIssue, R3> }, annotations?: Annotations.Schema ) => [Transformed] extends [never] ? MissingSelfGeneric<"Base.transformOrFail"> : Class< Transformed, Fields & newFields, I, R | Struct.Context | R2 | R3, C & Struct.Constructor, Self, Proto > transformOrFailFrom(identifier: string): < newFields extends Struct.Fields, R2, R3 >( fields: newFields, options: { readonly decode: ( input: Simplify, options: ParseOptions, ast: AST.Transformation ) => Effect.Effect>, ParseResult.ParseIssue, R2> readonly encode: ( input: Simplify>, options: ParseOptions, ast: AST.Transformation ) => Effect.Effect }, annotations?: Annotations.Schema ) => [Transformed] extends [never] ? MissingSelfGeneric<"Base.transformOrFailFrom"> : Class< Transformed, Fields & newFields, I, R | Struct.Context | R2 | R3, C & Struct.Constructor, Self, Proto > } type HasFields = Struct | { readonly [refineTypeId]: HasFields } const isField = (u: unknown) => isSchema(u) || isPropertySignature(u) const isFields = (fields: object): fields is Fields => util_.ownKeys(fields).every((key) => isField((fields as any)[key])) const getFields = (hasFields: HasFields): Fields => "fields" in hasFields ? hasFields.fields : getFields(hasFields[refineTypeId]) const getSchemaFromFieldsOr = (fieldsOr: Fields | HasFields): Schema.Any => isFields(fieldsOr) ? Struct(fieldsOr) : isSchema(fieldsOr) ? fieldsOr : Struct(getFields(fieldsOr)) const getFieldsFromFieldsOr = (fieldsOr: Fields | HasFields): Fields => isFields(fieldsOr) ? fieldsOr : getFields(fieldsOr) /** * @category classes * @since 0.67.0 */ export const Class = (identifier: string) => ( fieldsOr: Fields | HasFields, annotations?: Annotations.Schema ): [Self] extends [never] ? MissingSelfGeneric<"Class"> : Class< Self, Fields, Struct.Encoded, Struct.Context, Struct.Constructor, {}, {} > => makeClass({ kind: "Class", identifier, schema: getSchemaFromFieldsOr(fieldsOr), fields: getFieldsFromFieldsOr(fieldsOr), Base: data_.Class, annotations }) /** @internal */ export const getClassTag = (tag: Tag) => withConstructorDefault(propertySignature(Literal(tag)), () => tag) /** * @category api interface * @since 0.67.0 */ export interface TaggedClass extends Class< Self, Fields, Struct.Encoded, Struct.Context, Struct.Constructor>, {}, {} > { readonly _tag: Tag } /** * @category classes * @since 0.67.0 */ export const TaggedClass = (identifier?: string) => ( tag: Tag, fieldsOr: Fields | HasFields, annotations?: Annotations.Schema ): [Self] extends [never] ? MissingSelfGeneric<"TaggedClass", `"Tag", `> : TaggedClass } & Fields> => { const fields = getFieldsFromFieldsOr(fieldsOr) const schema = getSchemaFromFieldsOr(fieldsOr) const newFields = { _tag: getClassTag(tag) } const taggedFields = extendFields(newFields, fields) return class TaggedClass extends makeClass({ kind: "TaggedClass", identifier: identifier ?? tag, schema: extend(schema, Struct(newFields)), fields: taggedFields, Base: data_.Class, annotations }) { static _tag = tag } as any } /** * @category api interface * @since 0.67.0 */ export interface TaggedErrorClass extends Class< Self, Fields, Struct.Encoded, Struct.Context, Struct.Constructor>, {}, cause_.YieldableError > { readonly _tag: Tag } /** * @category classes * @since 0.67.0 */ export const TaggedError = (identifier?: string) => ( tag: Tag, fieldsOr: Fields | HasFields, annotations?: Annotations.Schema ): [Self] extends [never] ? MissingSelfGeneric<"TaggedError", `"Tag", `> : TaggedErrorClass< Self, Tag, { readonly _tag: tag } & Fields > => { class Base extends data_.Error {} ;(Base.prototype as any).name = tag const fields = getFieldsFromFieldsOr(fieldsOr) const schema = getSchemaFromFieldsOr(fieldsOr) const newFields = { _tag: getClassTag(tag) } const taggedFields = extendFields(newFields, fields) return class TaggedErrorClass extends makeClass({ kind: "TaggedError", identifier: identifier ?? tag, schema: extend(schema, Struct(newFields)), fields: taggedFields, Base, annotations, disableToString: true }) { static _tag = tag get message(): string { return `{ ${ util_.ownKeys(fields).map((p: any) => `${util_.formatPropertyKey(p)}: ${util_.formatUnknown(this[p])}`) .join(", ") } }` } } as any } /** * @since 0.67.0 */ export interface TaggedRequest< Tag extends string, A, I, R, SuccessType, SuccessEncoded, FailureType, FailureEncoded, ResultR > extends Request.Request, Serializable.SerializableWithResult< A, I, R, SuccessType, SuccessEncoded, FailureType, FailureEncoded, ResultR > { readonly _tag: Tag } /** * @since 0.67.0 */ export declare namespace TaggedRequest { /** * @since 0.69.0 */ export type Any = TaggedRequest /** * @since 0.69.0 */ export type All = | Any | TaggedRequest } /** * @category api interface * @since 0.67.0 */ export interface TaggedRequestClass< Self, Tag extends string, Payload extends Struct.Fields, Success extends Schema.All, Failure extends Schema.All > extends Class< Self, Payload, Struct.Encoded, Struct.Context, Struct.Constructor>, TaggedRequest< Tag, Self, Struct.Encoded, Struct.Context, Schema.Type, Schema.Encoded, Schema.Type, Schema.Encoded, Schema.Context | Schema.Context >, {} > { readonly _tag: Tag /** @since 0.69.1 */ readonly success: Success /** @since 0.69.1 */ readonly failure: Failure } /** * @category classes * @since 0.67.0 */ export const TaggedRequest = (identifier?: string) => ( tag: Tag, options: { failure: Failure success: Success payload: Payload }, annotations?: Annotations.Schema ): [Self] extends [never] ? MissingSelfGeneric<"TaggedRequest", `"Tag", SuccessSchema, FailureSchema, `> : TaggedRequestClass< Self, Tag, { readonly _tag: tag } & Payload, Success, Failure > => { const taggedFields = extendFields({ _tag: getClassTag(tag) }, options.payload) return class TaggedRequestClass extends makeClass({ kind: "TaggedRequest", identifier: identifier ?? tag, schema: Struct(taggedFields), fields: taggedFields, Base: Request.Class, annotations }) { static _tag = tag static success = options.success static failure = options.failure get [serializable_.symbol]() { return this.constructor } get [serializable_.symbolResult]() { return { failure: options.failure, success: options.success } } } as any } const extendFields = (a: Struct.Fields, b: Struct.Fields): Struct.Fields => { const out = { ...a } for (const key of util_.ownKeys(b)) { if (key in a) { throw new Error(errors_.getASTDuplicatePropertySignatureErrorMessage(key)) } out[key] = b[key] } return out } // does not overwrite existing title annotation const orElseTitleAnnotation = (schema: Schema, title: string): Schema => { const annotation = AST.getTitleAnnotation(schema.ast) if (option_.isNone(annotation)) { return schema.annotations({ title }) } return schema } type MakeOptions = boolean | { readonly disableValidation?: boolean } const getDisableValidationMakeOption = (options: MakeOptions | undefined): boolean => Predicate.isBoolean(options) ? options : options?.disableValidation ?? false const makeClass = ({ Base, annotations, disableToString, fields, identifier, kind, schema }: { kind: "Class" | "TaggedClass" | "TaggedError" | "TaggedRequest" identifier: string schema: Schema.Any fields: Struct.Fields Base: new(...args: ReadonlyArray) => any annotations?: Annotations.Schema | undefined disableToString?: boolean | undefined }): any => { const classSymbol = Symbol.for(`@effect/schema/${kind}/${identifier}`) const validateSchema = orElseTitleAnnotation(schema, `${identifier} (Constructor)`) const encodedSide: Schema.Any = orElseTitleAnnotation(schema, `${identifier} (Encoded side)`) const typeSide = orElseTitleAnnotation(typeSchema(schema), `${identifier} (Type side)`) const fallbackInstanceOf = (u: unknown) => Predicate.hasProperty(u, classSymbol) && ParseResult.is(typeSide)(u) const klass = class extends Base { constructor( props: { [x: string | symbol]: unknown } = {}, options: MakeOptions = false ) { props = { ...props } if (kind !== "Class") { delete props["_tag"] } props = lazilyMergeDefaults(fields, props) if (!getDisableValidationMakeOption(options)) { props = ParseResult.validateSync(validateSchema)(props) } super(props, true) } // ---------------- // Schema interface // ---------------- static [TypeId] = variance static get ast() { const declaration: Schema.Any = declare( [typeSide], { decode: () => (input, _, ast) => input instanceof this || fallbackInstanceOf(input) ? ParseResult.succeed(input) : ParseResult.fail(new ParseResult.Type(ast, input)), encode: () => (input, options) => input instanceof this ? ParseResult.succeed(input) : ParseResult.map( ParseResult.encodeUnknown(typeSide)(input, options), (props) => new this(props, true) ) }, { identifier, title: identifier, description: `an instance of ${identifier}`, pretty: (pretty) => (self: any) => `${identifier}(${pretty(self)})`, // @ts-expect-error arbitrary: (arb) => (fc) => arb(fc).map((props) => new this(props)), equivalence: identity, [AST.SurrogateAnnotationId]: typeSide.ast, ...annotations } ) const transformation = transform( encodedSide, declaration, { strict: true, decode: (input) => new this(input, true), encode: identity } ).annotations({ [AST.SurrogateAnnotationId]: schema.ast }) return transformation.ast } static pipe() { return pipeArguments(this, arguments) } static annotations(annotations: Annotations.Schema) { return make(this.ast).annotations(annotations) } static toString() { return `(${String(encodedSide)} <-> ${identifier})` } // ---------------- // Class interface // ---------------- static make(...args: Array) { return new this(...args) } static fields = { ...fields } static identifier = identifier static extend(identifier: string) { return (newFieldsOr: Struct.Fields | HasFields, annotations?: Annotations.Schema) => { const newFields = getFieldsFromFieldsOr(newFieldsOr) const newSchema = getSchemaFromFieldsOr(newFieldsOr) const extendedFields = extendFields(fields, newFields) return makeClass({ kind, identifier, schema: extend(schema, newSchema), fields: extendedFields, Base: this, annotations }) } } static transformOrFail(identifier: string) { return (newFields: Struct.Fields, options: any, annotations?: Annotations.Schema) => { const transformedFields: Struct.Fields = extendFields(fields, newFields) return makeClass({ kind, identifier, schema: transformOrFail( schema, typeSchema(Struct(transformedFields)), options ), fields: transformedFields, Base: this, annotations }) } } static transformOrFailFrom(identifier: string) { return (newFields: Struct.Fields, options: any, annotations?: Annotations.Schema) => { const transformedFields: Struct.Fields = extendFields(fields, newFields) return makeClass({ kind, identifier, schema: transformOrFail( encodedSchema(schema), Struct(transformedFields), options ), fields: transformedFields, Base: this, annotations }) } } // ---------------- // other // ---------------- get [classSymbol]() { return classSymbol } } if (disableToString !== true) { Object.defineProperty(klass.prototype, "toString", { value() { return `${identifier}({ ${ util_.ownKeys(fields).map((p: any) => `${util_.formatPropertyKey(p)}: ${util_.formatUnknown(this[p])}`) .join(", ") } })` }, configurable: true }) } return klass } /** * @category FiberId * @since 0.67.0 */ export type FiberIdEncoded = | { readonly _tag: "Composite" readonly left: FiberIdEncoded readonly right: FiberIdEncoded } | { readonly _tag: "None" } | { readonly _tag: "Runtime" readonly id: number readonly startTimeMillis: number } const FiberIdNoneEncoded = Struct({ _tag: Literal("None") }).annotations({ identifier: "FiberIdNoneEncoded" }) const FiberIdRuntimeEncoded = Struct({ _tag: Literal("Runtime"), id: Int.annotations({ title: "id", description: "id" }), startTimeMillis: Int.annotations({ title: "startTimeMillis", description: "startTimeMillis" }) }).annotations({ identifier: "FiberIdRuntimeEncoded" }) const FiberIdCompositeEncoded = Struct({ _tag: Literal("Composite"), left: suspend(() => FiberIdEncoded), right: suspend(() => FiberIdEncoded) }).annotations({ identifier: "FiberIdCompositeEncoded" }) const FiberIdEncoded: Schema = Union( FiberIdNoneEncoded, FiberIdRuntimeEncoded, FiberIdCompositeEncoded ).annotations({ identifier: "FiberIdEncoded" }) const fiberIdArbitrary: LazyArbitrary = (fc) => fc.letrec((tie) => ({ None: fc.record({ _tag: fc.constant("None" as const) }), Runtime: fc.record({ _tag: fc.constant("Runtime" as const), id: fc.integer(), startTimeMillis: fc.integer() }), Composite: fc.record({ _tag: fc.constant("Composite" as const), left: tie("FiberId"), right: tie("FiberId") }), FiberId: fc.oneof(tie("None"), tie("Runtime"), tie("Composite")) as any as fastCheck_.Arbitrary })).FiberId.map(fiberIdDecode) const fiberIdPretty: pretty_.Pretty = (fiberId) => { switch (fiberId._tag) { case "None": return "FiberId.none" case "Runtime": return `FiberId.runtime(${fiberId.id}, ${fiberId.startTimeMillis})` case "Composite": return `FiberId.composite(${fiberIdPretty(fiberId.right)}, ${fiberIdPretty(fiberId.left)})` } } /** * @category FiberId constructors * @since 0.67.0 */ export class FiberIdFromSelf extends declare( fiberId_.isFiberId, { identifier: "FiberIdFromSelf", pretty: () => fiberIdPretty, arbitrary: () => fiberIdArbitrary } ) {} const fiberIdDecode = (input: FiberIdEncoded): fiberId_.FiberId => { switch (input._tag) { case "None": return fiberId_.none case "Runtime": return fiberId_.runtime(input.id, input.startTimeMillis) case "Composite": return fiberId_.composite(fiberIdDecode(input.left), fiberIdDecode(input.right)) } } const fiberIdEncode = (input: fiberId_.FiberId): FiberIdEncoded => { switch (input._tag) { case "None": return { _tag: "None" } case "Runtime": return { _tag: "Runtime", id: input.id, startTimeMillis: input.startTimeMillis } case "Composite": return { _tag: "Composite", left: fiberIdEncode(input.left), right: fiberIdEncode(input.right) } } } /** * @category FiberId transformations * @since 0.67.0 */ export class FiberId extends transform( FiberIdEncoded, FiberIdFromSelf, { strict: true, decode: fiberIdDecode, encode: fiberIdEncode } ).annotations({ identifier: "FiberId" }) {} /** * @category Cause utils * @since 0.69.0 */ export type CauseEncoded = | { readonly _tag: "Empty" } | { readonly _tag: "Fail" readonly error: E } | { readonly _tag: "Die" readonly defect: D } | { readonly _tag: "Interrupt" readonly fiberId: FiberIdEncoded } | { readonly _tag: "Sequential" readonly left: CauseEncoded readonly right: CauseEncoded } | { readonly _tag: "Parallel" readonly left: CauseEncoded readonly right: CauseEncoded } const causeDieEncoded = (defect: Schema) => Struct({ _tag: Literal("Die"), defect }) const CauseEmptyEncoded = Struct({ _tag: Literal("Empty") }) const causeFailEncoded = (error: Schema) => Struct({ _tag: Literal("Fail"), error }) const CauseInterruptEncoded = Struct({ _tag: Literal("Interrupt"), fiberId: FiberIdEncoded }) const causeParallelEncoded = (causeEncoded: Schema, CauseEncoded, R>) => Struct({ _tag: Literal("Parallel"), left: causeEncoded, right: causeEncoded }) const causeSequentialEncoded = (causeEncoded: Schema, CauseEncoded, R>) => Struct({ _tag: Literal("Sequential"), left: causeEncoded, right: causeEncoded }) const causeEncoded = ( error: Schema, defect: Schema ): Schema, CauseEncoded, R1 | R2> => { const recur = suspend(() => out) const out: Schema, CauseEncoded, R1 | R2> = Union( CauseEmptyEncoded, causeFailEncoded(error), causeDieEncoded(defect), CauseInterruptEncoded, causeSequentialEncoded(recur), causeParallelEncoded(recur) ).annotations({ title: `CauseEncoded<${format(error)}>` }) return out } const causeArbitrary = ( error: LazyArbitrary, defect: LazyArbitrary ): LazyArbitrary> => (fc) => fc.letrec((tie) => ({ Empty: fc.record({ _tag: fc.constant("Empty" as const) }), Fail: fc.record({ _tag: fc.constant("Fail" as const), error: error(fc) }), Die: fc.record({ _tag: fc.constant("Die" as const), defect: defect(fc) }), Interrupt: fc.record({ _tag: fc.constant("Interrupt" as const), fiberId: fiberIdArbitrary(fc) }), Sequential: fc.record({ _tag: fc.constant("Sequential" as const), left: tie("Cause"), right: tie("Cause") }), Parallel: fc.record({ _tag: fc.constant("Parallel" as const), left: tie("Cause"), right: tie("Cause") }), Cause: fc.oneof( tie("Empty"), tie("Fail"), tie("Die"), tie("Interrupt"), tie("Sequential"), tie("Parallel") ) as any as fastCheck_.Arbitrary> })).Cause.map(causeDecode) const causePretty = (error: pretty_.Pretty): pretty_.Pretty> => (cause) => { const f = (cause: cause_.Cause): string => { switch (cause._tag) { case "Empty": return "Cause.empty" case "Fail": return `Cause.fail(${error(cause.error)})` case "Die": return `Cause.die(${cause_.pretty(cause)})` case "Interrupt": return `Cause.interrupt(${fiberIdPretty(cause.fiberId)})` case "Sequential": return `Cause.sequential(${f(cause.left)}, ${f(cause.right)})` case "Parallel": return `Cause.parallel(${f(cause.left)}, ${f(cause.right)})` } } return f(cause) } const causeParse = ( decodeUnknown: ParseResult.DecodeUnknown, R> ): ParseResult.DeclarationDecodeUnknown, R> => (u, options, ast) => cause_.isCause(u) ? toComposite(decodeUnknown(causeEncode(u), options), causeDecode, ast, u) : ParseResult.fail(new ParseResult.Type(ast, u)) /** * @category api interface * @since 0.69.0 */ export interface CauseFromSelf extends AnnotableClass< CauseFromSelf, cause_.Cause>, cause_.Cause>, Schema.Context | Schema.Context > {} /** * @category Cause transformations * @since 0.69.0 */ export const CauseFromSelf = ({ defect, error }: { readonly error: E readonly defect: D }): CauseFromSelf => { return declare( [error, defect], { decode: (error, defect) => causeParse(ParseResult.decodeUnknown(causeEncoded(error, defect))), encode: (error, defect) => causeParse(ParseResult.encodeUnknown(causeEncoded(error, defect))) }, { title: `Cause<${error.ast}>`, pretty: causePretty, arbitrary: causeArbitrary } ) } function causeDecode(cause: CauseEncoded): cause_.Cause { switch (cause._tag) { case "Empty": return cause_.empty case "Fail": return cause_.fail(cause.error) case "Die": return cause_.die(cause.defect) case "Interrupt": return cause_.interrupt(fiberIdDecode(cause.fiberId)) case "Sequential": return cause_.sequential(causeDecode(cause.left), causeDecode(cause.right)) case "Parallel": return cause_.parallel(causeDecode(cause.left), causeDecode(cause.right)) } } function causeEncode(cause: cause_.Cause): CauseEncoded { switch (cause._tag) { case "Empty": return { _tag: "Empty" } case "Fail": return { _tag: "Fail", error: cause.error } case "Die": return { _tag: "Die", defect: cause.defect } case "Interrupt": return { _tag: "Interrupt", fiberId: cause.fiberId } case "Sequential": return { _tag: "Sequential", left: causeEncode(cause.left), right: causeEncode(cause.right) } case "Parallel": return { _tag: "Parallel", left: causeEncode(cause.left), right: causeEncode(cause.right) } } } /** * @category api interface * @since 0.69.0 */ export interface Cause extends AnnotableClass< Cause, cause_.Cause>, CauseEncoded, Schema.Encoded>, Schema.Context | Schema.Context > {} /** * @category Cause transformations * @since 0.69.0 */ export const Cause = ({ defect, error }: { readonly error: E readonly defect: D }): Cause => { const error_ = asSchema(error) const defect_ = asSchema(defect) return transform( causeEncoded(error_, defect_), CauseFromSelf({ error: typeSchema(error_), defect: Unknown }), { strict: false, decode: causeDecode, encode: causeEncode } ) } /** * @category api interface * @since 0.69.0 */ export interface Defect extends transform {} /** * Defines a schema for handling JavaScript errors (`Error` instances) and other types of defects. * It decodes objects into Error instances if they match the expected structure (i.e., have a `message` and optionally a `name` and `stack`), * or converts other values to their string representations. * * When encoding, it converts `Error` instances back into plain objects containing only the error's name and message, * or other values into their string forms. * * This is useful for serializing and deserializing errors across network boundaries where error objects do not natively serialize. * * @category defect * @since 0.69.0 */ export const Defect: Defect = transform( Unknown, Unknown, { strict: true, decode: (u) => { if (Predicate.isObject(u) && "message" in u && typeof u.message === "string") { const err = new Error(u.message, { cause: u }) if ("name" in u && typeof u.name === "string") { err.name = u.name } err.stack = "stack" in u && typeof u.stack === "string" ? u.stack : "" return err } return String(u) }, encode: (defect) => { if (defect instanceof Error) { return { name: defect.name, message: defect.message // no stack because of security reasons } } return String(defect) } } ).annotations({ identifier: "Defect" }) /** * @category Exit utils * @since 0.69.0 */ export type ExitEncoded = | { readonly _tag: "Failure" readonly cause: CauseEncoded } | { readonly _tag: "Success" readonly value: A } const exitFailureEncoded = ( error: Schema, defect: Schema ) => Struct({ _tag: Literal("Failure"), cause: causeEncoded(error, defect) }) const exitSuccessEncoded = ( value: Schema ) => Struct({ _tag: Literal("Success"), value }) const exitEncoded = ( value: Schema, error: Schema, defect: Schema ): Schema, ExitEncoded, R | ER | DR> => Union( exitFailureEncoded(error, defect), exitSuccessEncoded(value) ).annotations({ title: `ExitEncoded<${format(value)}, ${format(error)}, ${format(defect)}>` }) const exitDecode = (input: ExitEncoded): exit_.Exit => { switch (input._tag) { case "Failure": return exit_.failCause(causeDecode(input.cause)) case "Success": return exit_.succeed(input.value) } } const exitArbitrary = ( value: LazyArbitrary, error: LazyArbitrary, defect: LazyArbitrary ): LazyArbitrary> => (fc) => fc.oneof( fc.record({ _tag: fc.constant("Failure" as const), cause: causeArbitrary(error, defect)(fc) }), fc.record({ _tag: fc.constant("Success" as const), value: value(fc) }) ).map(exitDecode) const exitPretty = (value: pretty_.Pretty, error: pretty_.Pretty): pretty_.Pretty> => (exit) => exit._tag === "Failure" ? `Exit.failCause(${causePretty(error)(exit.cause)})` : `Exit.succeed(${value(exit.value)})` const exitParse = ( decodeUnknownValue: ParseResult.DecodeUnknown, decodeUnknownCause: ParseResult.DecodeUnknown, ER> ): ParseResult.DeclarationDecodeUnknown, ER | R> => (u, options, ast) => exit_.isExit(u) ? exit_.match(u, { onFailure: (cause) => toComposite(decodeUnknownCause(cause, options), exit_.failCause, ast, u), onSuccess: (value) => toComposite(decodeUnknownValue(value, options), exit_.succeed, ast, u) }) : ParseResult.fail(new ParseResult.Type(ast, u)) /** * @category api interface * @since 0.69.0 */ export interface ExitFromSelf extends AnnotableClass< ExitFromSelf, exit_.Exit, Schema.Type>, exit_.Exit, Schema.Encoded>, Schema.Context | Schema.Context | Schema.Context > {} /** * @category Exit transformations * @since 0.69.0 */ export const ExitFromSelf = ( { defect, failure, success }: { readonly failure: E readonly success: A readonly defect: D } ): ExitFromSelf => declare( [success, failure, defect], { decode: (success, failure, defect) => exitParse( ParseResult.decodeUnknown(success), ParseResult.decodeUnknown(CauseFromSelf({ error: failure, defect })) ), encode: (success, failure, defect) => exitParse( ParseResult.encodeUnknown(success), ParseResult.encodeUnknown(CauseFromSelf({ error: failure, defect })) ) }, { title: `Exit<${success.ast}, ${failure.ast}>`, pretty: exitPretty, arbitrary: exitArbitrary } ) /** * @category api interface * @since 0.69.0 */ export interface Exit extends AnnotableClass< Exit, exit_.Exit, Schema.Type>, ExitEncoded, Schema.Encoded, Schema.Encoded>, Schema.Context | Schema.Context | Schema.Context > {} /** * @category Exit transformations * @since 0.69.0 */ export const Exit = ( { defect, failure, success }: { readonly failure: E readonly success: A readonly defect: D } ): Exit => { const success_ = asSchema(success) const failure_ = asSchema(failure) const defect_ = asSchema(defect) return transform( exitEncoded(success_, failure_, defect_), ExitFromSelf({ failure: typeSchema(failure_), success: typeSchema(success_), defect: Unknown }), { strict: false, decode: exitDecode, encode: (exit) => exit._tag === "Failure" ? { _tag: "Failure", cause: exit.cause } as const : { _tag: "Success", value: exit.value } as const } ) } const hashSetArbitrary = (item: LazyArbitrary, ctx: GenerationContext): LazyArbitrary> => (fc) => { const items = fc.array(item(fc)) return (ctx.depthIdentifier !== undefined ? fc.oneof(ctx, fc.constant([]), items) : items).map( hashSet_.fromIterable ) } const hashSetPretty = (item: pretty_.Pretty): pretty_.Pretty> => (set) => `HashSet(${Array.from(set).map((a) => item(a)).join(", ")})` const hashSetEquivalence = ( item: Equivalence.Equivalence ): Equivalence.Equivalence> => { const arrayEquivalence = array_.getEquivalence(item) return Equivalence.make((a, b) => arrayEquivalence(Array.from(a), Array.from(b))) } const hashSetParse = ( decodeUnknown: ParseResult.DecodeUnknown, R> ): ParseResult.DeclarationDecodeUnknown, R> => (u, options, ast) => hashSet_.isHashSet(u) ? toComposite(decodeUnknown(Array.from(u), options), hashSet_.fromIterable, ast, u) : ParseResult.fail(new ParseResult.Type(ast, u)) /** * @category api interface * @since 0.67.0 */ export interface HashSetFromSelf extends AnnotableClass< HashSetFromSelf, hashSet_.HashSet>, hashSet_.HashSet>, Schema.Context > {} /** * @category HashSet transformations * @since 0.67.0 */ export const HashSetFromSelf = ( value: Value ): HashSetFromSelf => { return declare( [value], { decode: (item) => hashSetParse(ParseResult.decodeUnknown(Array$(item))), encode: (item) => hashSetParse(ParseResult.encodeUnknown(Array$(item))) }, { description: `HashSet<${format(value)}>`, pretty: hashSetPretty, arbitrary: hashSetArbitrary, equivalence: hashSetEquivalence } ) } /** * @category api interface * @since 0.67.0 */ export interface HashSet extends AnnotableClass< HashSet, hashSet_.HashSet>, ReadonlyArray>, Schema.Context > {} /** * @category HashSet transformations * @since 0.67.0 */ export const HashSet = (value: Value): HashSet => { const value_ = asSchema(value) return transform( Array$(value_), HashSetFromSelf(typeSchema(value_)), { strict: true, decode: (as) => hashSet_.fromIterable(as), encode: (set) => Array.from(set) } ) } const hashMapArbitrary = ( key: LazyArbitrary, value: LazyArbitrary, ctx: GenerationContext ): LazyArbitrary> => (fc) => { const items = fc.array(fc.tuple(key(fc), value(fc))) return (ctx.depthIdentifier !== undefined ? fc.oneof(ctx, fc.constant([]), items) : items).map(hashMap_.fromIterable) } const hashMapPretty = ( key: pretty_.Pretty, value: pretty_.Pretty ): pretty_.Pretty> => (map) => `HashMap([${ Array.from(map) .map(([k, v]) => `[${key(k)}, ${value(v)}]`) .join(", ") }])` const hashMapEquivalence = ( key: Equivalence.Equivalence, value: Equivalence.Equivalence ): Equivalence.Equivalence> => { const arrayEquivalence = array_.getEquivalence( Equivalence.make<[K, V]>(([ka, va], [kb, vb]) => key(ka, kb) && value(va, vb)) ) return Equivalence.make((a, b) => arrayEquivalence(Array.from(a), Array.from(b))) } const hashMapParse = ( decodeUnknown: ParseResult.DecodeUnknown, R> ): ParseResult.DeclarationDecodeUnknown, R> => (u, options, ast) => hashMap_.isHashMap(u) ? toComposite(decodeUnknown(Array.from(u), options), hashMap_.fromIterable, ast, u) : ParseResult.fail(new ParseResult.Type(ast, u)) /** * @category api interface * @since 0.67.0 */ export interface HashMapFromSelf extends AnnotableClass< HashMapFromSelf, hashMap_.HashMap, Schema.Type>, hashMap_.HashMap, Schema.Encoded>, Schema.Context | Schema.Context > {} /** * @category HashMap transformations * @since 0.67.0 */ export const HashMapFromSelf = ({ key, value }: { readonly key: K readonly value: V }): HashMapFromSelf => { return declare( [key, value], { decode: (key, value) => hashMapParse(ParseResult.decodeUnknown(Array$(Tuple(key, value)))), encode: (key, value) => hashMapParse(ParseResult.encodeUnknown(Array$(Tuple(key, value)))) }, { description: `HashMap<${format(key)}, ${format(value)}>`, pretty: hashMapPretty, arbitrary: hashMapArbitrary, equivalence: hashMapEquivalence } ) } /** * @category api interface * @since 0.67.0 */ export interface HashMap extends AnnotableClass< HashMap, hashMap_.HashMap, Schema.Type>, ReadonlyArray, Schema.Encoded]>, Schema.Context | Schema.Context > {} /** * @category HashMap transformations * @since 0.67.0 */ export const HashMap = ({ key, value }: { readonly key: K readonly value: V }): HashMap => { const key_ = asSchema(key) const value_ = asSchema(value) return transform( Array$(Tuple(key_, value_)), HashMapFromSelf({ key: typeSchema(key_), value: typeSchema(value_) }), { strict: true, decode: (as) => hashMap_.fromIterable(as), encode: (map) => Array.from(map) } ) } const listArbitrary = (item: LazyArbitrary, ctx: GenerationContext): LazyArbitrary> => (fc) => { const items = fc.array(item(fc)) return (ctx.depthIdentifier !== undefined ? fc.oneof(ctx, fc.constant([]), items) : items).map(list_.fromIterable) } const listPretty = (item: pretty_.Pretty): pretty_.Pretty> => (set) => `List(${Array.from(set).map((a) => item(a)).join(", ")})` const listEquivalence = ( item: Equivalence.Equivalence ): Equivalence.Equivalence> => { const arrayEquivalence = array_.getEquivalence(item) return Equivalence.make((a, b) => arrayEquivalence(Array.from(a), Array.from(b))) } const listParse = ( decodeUnknown: ParseResult.DecodeUnknown, R> ): ParseResult.DeclarationDecodeUnknown, R> => (u, options, ast) => list_.isList(u) ? toComposite(decodeUnknown(Array.from(u), options), list_.fromIterable, ast, u) : ParseResult.fail(new ParseResult.Type(ast, u)) /** * @category api interface * @since 0.67.0 */ export interface ListFromSelf extends AnnotableClass< ListFromSelf, list_.List>, list_.List>, Schema.Context > {} /** * @category List transformations * @since 0.67.0 */ export const ListFromSelf = ( value: Value ): ListFromSelf => { return declare( [value], { decode: (item) => listParse(ParseResult.decodeUnknown(Array$(item))), encode: (item) => listParse(ParseResult.encodeUnknown(Array$(item))) }, { description: `List<${format(value)}>`, pretty: listPretty, arbitrary: listArbitrary, equivalence: listEquivalence } ) } /** * @category api interface * @since 0.67.0 */ export interface List extends AnnotableClass< List, list_.List>, ReadonlyArray>, Schema.Context > {} /** * @category List transformations * @since 0.67.0 */ export const List = (value: Value): List => { const value_ = asSchema(value) return transform( Array$(value_), ListFromSelf(typeSchema(value_)), { strict: true, decode: (as) => list_.fromIterable(as), encode: (set) => Array.from(set) } ) } const sortedSetArbitrary = (item: LazyArbitrary, ord: Order.Order, ctx: GenerationContext): LazyArbitrary> => (fc) => { const items = fc.array(item(fc)) return (ctx.depthIdentifier !== undefined ? fc.oneof(ctx, fc.constant([]), items) : items).map((as) => sortedSet_.fromIterable(as, ord) ) } const sortedSetPretty = (item: pretty_.Pretty): pretty_.Pretty> => (set) => `new SortedSet([${Array.from(sortedSet_.values(set)).map((a) => item(a)).join(", ")}])` const sortedSetParse = ( decodeUnknown: ParseResult.DecodeUnknown, R>, ord: Order.Order ): ParseResult.DeclarationDecodeUnknown, R> => (u, options, ast) => sortedSet_.isSortedSet(u) ? toComposite( decodeUnknown(Array.from(sortedSet_.values(u)), options), (as): sortedSet_.SortedSet => sortedSet_.fromIterable(as, ord), ast, u ) : ParseResult.fail(new ParseResult.Type(ast, u)) /** * @category api interface * @since 0.67.0 */ export interface SortedSetFromSelf extends AnnotableClass< SortedSetFromSelf, sortedSet_.SortedSet>, sortedSet_.SortedSet>, Schema.Context > {} /** * @category SortedSet transformations * @since 0.67.0 */ export const SortedSetFromSelf = ( value: Value, ordA: Order.Order>, ordI: Order.Order> ): SortedSetFromSelf => { return declare( [value], { decode: (item) => sortedSetParse(ParseResult.decodeUnknown(Array$(item)), ordA), encode: (item) => sortedSetParse(ParseResult.encodeUnknown(Array$(item)), ordI) }, { description: `SortedSet<${format(value)}>`, pretty: sortedSetPretty, arbitrary: (arb, ctx) => sortedSetArbitrary(arb, ordA, ctx), equivalence: () => sortedSet_.getEquivalence>() } ) } /** * @category api interface * @since 0.67.0 */ export interface SortedSet extends AnnotableClass< SortedSet, sortedSet_.SortedSet>, ReadonlyArray>, Schema.Context > {} /** * @category SortedSet transformations * @since 0.67.0 */ export const SortedSet = ( value: Value, ordA: Order.Order> ): SortedSet => { const value_ = asSchema(value) const to = typeSchema(value_) return transform( Array$(value_), SortedSetFromSelf(to, ordA, ordA), { strict: true, decode: (as) => sortedSet_.fromIterable(as, ordA), encode: (set) => Array.from(sortedSet_.values(set)) } ) } /** * Converts an arbitrary value to a `boolean` by testing whether it is truthy. * Uses `!!val` to coerce the value to a `boolean`. * * @see https://developer.mozilla.org/docs/Glossary/Truthy * @category boolean constructors * @since 0.67.0 */ export class BooleanFromUnknown extends transform( Unknown, Boolean$, { strict: true, decode: Predicate.isTruthy, encode: identity } ).annotations({ identifier: "BooleanFromUnknown" }) {} /** * @category Config validations * @since 0.67.12 */ export const Config = (name: string, schema: Schema): config_.Config => { const decodeEither_ = decodeEither(schema) return config_.string(name).pipe( config_.mapOrFail((a) => decodeEither_(a).pipe( either_.mapLeft((error) => configError_.InvalidData([], TreeFormatter.formatErrorSync(error))) ) ) ) }