UNPKG

effect

Version:

The missing standard library for TypeScript, for writing production-grade software.

1,796 lines (1,634 loc) 332 kB
/** * @since 3.10.0 */ import type { StandardSchemaV1 } from "@standard-schema/spec" import type { ArbitraryAnnotation, ArbitraryGenerationContext, LazyArbitrary } from "./Arbitrary.js" import * as array_ from "./Array.js" import * as bigDecimal_ from "./BigDecimal.js" import * as bigInt_ from "./BigInt.js" import * as boolean_ from "./Boolean.js" import type { Brand } from "./Brand.js" import * as cause_ from "./Cause.js" import * as chunk_ from "./Chunk.js" import * as config_ from "./Config.js" import * as configError_ from "./ConfigError.js" import * as data_ from "./Data.js" import * as dateTime from "./DateTime.js" import * as duration_ from "./Duration.js" import * as Effect from "./Effect.js" import * as either_ from "./Either.js" import * as Encoding from "./Encoding.js" import * as Equal from "./Equal.js" import * as Equivalence from "./Equivalence.js" import * as exit_ from "./Exit.js" import * as fastCheck_ from "./FastCheck.js" import * as fiberId_ from "./FiberId.js" import type { LazyArg } from "./Function.js" import { dual, identity } from "./Function.js" import { globalValue } from "./GlobalValue.js" import * as hashMap_ from "./HashMap.js" import * as hashSet_ from "./HashSet.js" import * as internalCause_ from "./internal/cause.js" import * as errors_ from "./internal/schema/errors.js" import * as schemaId_ from "./internal/schema/schemaId.js" import * as util_ from "./internal/schema/util.js" import * as list_ from "./List.js" import * as number_ from "./Number.js" import * as option_ from "./Option.js" import type * as Order from "./Order.js" import * as ParseResult from "./ParseResult.js" import type { Pipeable } from "./Pipeable.js" import { pipeArguments } from "./Pipeable.js" import * as Predicate from "./Predicate.js" import type * as pretty_ from "./Pretty.js" import * as redacted_ from "./Redacted.js" import * as Request from "./Request.js" import * as scheduler_ from "./Scheduler.js" import type { ParseOptions } from "./SchemaAST.js" import * as AST from "./SchemaAST.js" import * as sortedSet_ from "./SortedSet.js" import * as string_ from "./String.js" import * as struct_ from "./Struct.js" import type * as Types from "./Types.js" /** * @since 3.10.0 */ export type Simplify<A> = { [K in keyof A]: A[K] } & {} /** * @since 3.10.0 */ export type SimplifyMutable<A> = { -readonly [K in keyof A]: A[K] } extends infer B ? B : never /** * @since 3.10.0 * @category symbol */ export const TypeId: unique symbol = Symbol.for("effect/Schema") /** * @since 3.10.0 * @category symbol */ export type TypeId = typeof TypeId /** * @category model * @since 3.10.0 */ export interface Schema<in out A, in out I = A, out R = never> extends Schema.Variance<A, I, R>, Pipeable { readonly Type: A readonly Encoded: I readonly Context: R readonly ast: AST.AST /** * Merges a set of new annotations with existing ones, potentially overwriting * any duplicates. */ annotations(annotations: Annotations.GenericSchema<A>): Schema<A, I, R> } /** * @category annotations * @since 3.10.0 */ export interface Annotable<Self extends Schema<A, I, R>, A, I = A, R = never> extends Schema<A, I, R> { annotations(annotations: Annotations.GenericSchema<A>): Self } /** * @category annotations * @since 3.10.0 */ export interface AnnotableClass<Self extends Schema<A, I, R>, A, I = A, R = never> extends Annotable<Self, A, I, R> { new(_: never): Schema.Variance<A, I, R> } /** * @category model * @since 3.10.0 */ export interface SchemaClass<A, I = A, R = never> extends AnnotableClass<SchemaClass<A, I, R>, A, I, R> {} /** * @category constructors * @since 3.10.0 */ export function make<A, I = A, R = never>(ast: AST.AST): SchemaClass<A, I, R> { return class SchemaClass { [TypeId] = variance static ast = ast static annotations(annotations: Annotations.GenericSchema<A>) { return make<A, I, R>(mergeSchemaAnnotations(this.ast, annotations)) } static pipe() { return pipeArguments(this, arguments) } static toString() { return String(ast) } static Type: A static Encoded: I static Context: R static [TypeId] = variance } } const variance = { /* c8 ignore next */ _A: (_: any) => _, /* c8 ignore next */ _I: (_: any) => _, /* c8 ignore next */ _R: (_: never) => _ } const makeStandardResult = <A>(exit: exit_.Exit<StandardSchemaV1.Result<A>>): StandardSchemaV1.Result<A> => exit_.isSuccess(exit) ? exit.value : makeStandardFailureResult(cause_.pretty(exit.cause)) const makeStandardFailureResult = (message: string): StandardSchemaV1.FailureResult => ({ issues: [{ message }] }) const makeStandardFailureFromParseIssue = ( issue: ParseResult.ParseIssue ): Effect.Effect<StandardSchemaV1.FailureResult> => Effect.map(ParseResult.ArrayFormatter.formatIssue(issue), (issues) => ({ issues: issues.map((issue) => ({ path: issue.path, message: issue.message })) })) /** * Returns a "Standard Schema" object conforming to the [Standard Schema * v1](https://standardschema.dev/) specification. * * This function creates a schema whose `validate` method attempts to decode and * validate the provided input synchronously. If the underlying `Schema` * includes any asynchronous components (e.g., asynchronous message resolutions * or checks), then validation will necessarily return a `Promise` instead. * * Any detected defects will be reported via a single issue containing no * `path`. * * @example * ```ts * import { Schema } from "effect" * * const schema = Schema.Struct({ * name: Schema.String * }) * * // ┌─── StandardSchemaV1<{ readonly name: string; }> * // ▼ * const standardSchema = Schema.standardSchemaV1(schema) * ``` * * @category Standard Schema * @since 3.13.0 */ export const standardSchemaV1 = <A, I>(schema: Schema<A, I, never>): StandardSchemaV1<I, A> => { const decodeUnknown = ParseResult.decodeUnknown(schema) return { "~standard": { version: 1, vendor: "effect", validate(value) { const scheduler = new scheduler_.SyncScheduler() const fiber = Effect.runFork( Effect.matchEffect(decodeUnknown(value), { onFailure: makeStandardFailureFromParseIssue, onSuccess: (value) => Effect.succeed({ value }) }), { scheduler } ) scheduler.flush() const exit = fiber.unsafePoll() if (exit) { return makeStandardResult(exit) } return new Promise((resolve) => { fiber.addObserver((exit) => { resolve(makeStandardResult(exit)) }) }) } } } } interface AllAnnotations<A, TypeParameters extends ReadonlyArray<any>> extends Annotations.Schema<A, TypeParameters>, PropertySignature.Annotations<A> {} const builtInAnnotations = { schemaId: AST.SchemaIdAnnotationId, message: AST.MessageAnnotationId, missingMessage: AST.MissingMessageAnnotationId, identifier: AST.IdentifierAnnotationId, title: AST.TitleAnnotationId, description: AST.DescriptionAnnotationId, examples: AST.ExamplesAnnotationId, default: AST.DefaultAnnotationId, documentation: AST.DocumentationAnnotationId, jsonSchema: AST.JSONSchemaAnnotationId, arbitrary: AST.ArbitraryAnnotationId, pretty: AST.PrettyAnnotationId, equivalence: AST.EquivalenceAnnotationId, concurrency: AST.ConcurrencyAnnotationId, batching: AST.BatchingAnnotationId, parseIssueTitle: AST.ParseIssueTitleAnnotationId, parseOptions: AST.ParseOptionsAnnotationId, decodingFallback: AST.DecodingFallbackAnnotationId } const toASTAnnotations = <A, TypeParameters extends ReadonlyArray<any>>( annotations?: AllAnnotations<A, TypeParameters> ): AST.Annotations => { if (!annotations) { return {} } const out: Types.Mutable<AST.Annotations> = { ...annotations } for (const key in builtInAnnotations) { if (key in annotations) { const id = builtInAnnotations[key as keyof typeof builtInAnnotations] out[id] = annotations[key as keyof typeof annotations] delete out[key] } } return out } const mergeSchemaAnnotations = <A>(ast: AST.AST, annotations: Annotations.Schema<A>): AST.AST => AST.annotations(ast, toASTAnnotations(annotations)) /** * @category annotations * @since 3.10.0 */ export declare namespace Annotable { /** * @since 3.10.0 */ export type Self<S extends All> = ReturnType<S["annotations"]> /** * @since 3.10.0 */ export type Any = Annotable<any, any, any, unknown> /** * @since 3.10.0 */ export type All = | Any | Annotable<any, any, never, unknown> | Annotable<any, never, any, unknown> | Annotable<any, never, never, unknown> } /** * @since 3.10.0 */ export function asSchema<S extends Schema.All>( schema: S ): Schema<Schema.Type<S>, Schema.Encoded<S>, Schema.Context<S>> { return schema as any } /** * @category formatting * @since 3.10.0 */ export const format = <S extends Schema.All>(schema: S): string => String(schema.ast) /** * @since 3.10.0 */ export declare namespace Schema { /** * @since 3.10.0 */ export interface Variance<A, I, R> { readonly [TypeId]: { readonly _A: Types.Invariant<A> readonly _I: Types.Invariant<I> readonly _R: Types.Covariant<R> } } /** * @since 3.10.0 */ export type Type<S> = S extends Schema.Variance<infer A, infer _I, infer _R> ? A : never /** * @since 3.10.0 */ export type Encoded<S> = S extends Schema.Variance<infer _A, infer I, infer _R> ? I : never /** * @since 3.10.0 */ export type Context<S> = S extends Schema.Variance<infer _A, infer _I, infer R> ? R : never /** * @since 3.10.0 */ export type ToAsserts<S extends AnyNoContext> = ( input: unknown, options?: AST.ParseOptions ) => asserts input is Schema.Type<S> /** * Any schema, except for `never`. * * @since 3.10.0 */ export type Any = Schema<any, any, unknown> /** * Any schema with `Context = never`, except for `never`. * * @since 3.10.0 */ export type AnyNoContext = Schema<any, any, never> /** * Any schema, including `never`. * * @since 3.10.0 */ export type All = | Any | Schema<any, never, unknown> | Schema<never, any, unknown> | Schema<never, never, unknown> /** * Type-level counterpart of `Schema.asSchema` function. * * @since 3.10.0 */ export type AsSchema<S extends All> = Schema<Type<S>, Encoded<S>, Context<S>> } /** * 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 3.10.0 */ export const encodedSchema = <A, I, R>(schema: Schema<A, I, R>): SchemaClass<I> => 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 3.10.0 */ export const encodedBoundSchema = <A, I, R>(schema: Schema<A, I, R>): SchemaClass<I> => 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 3.10.0 */ export const typeSchema = <A, I, R>(schema: Schema<A, I, R>): SchemaClass<A> => make(AST.typeAST(schema.ast)) /* c8 ignore start */ export { /** * By default the option `exact` is set to `true`. * * @throws `ParseError` * @category validation * @since 3.10.0 */ asserts, /** * @category decoding * @since 3.10.0 */ decodeOption, /** * @throws `ParseError` * @category decoding * @since 3.10.0 */ decodeSync, /** * @category decoding * @since 3.10.0 */ decodeUnknownOption, /** * @throws `ParseError` * @category decoding * @since 3.10.0 */ decodeUnknownSync, /** * @category encoding * @since 3.10.0 */ encodeOption, /** * @throws `ParseError` * @category encoding * @since 3.10.0 */ encodeSync, /** * @category encoding * @since 3.10.0 */ encodeUnknownOption, /** * @throws `ParseError` * @category encoding * @since 3.10.0 */ encodeUnknownSync, /** * By default the option `exact` is set to `true`. * * @category validation * @since 3.10.0 */ is, /** * @category validation * @since 3.10.0 */ validateOption, /** * @throws `ParseError` * @category validation * @since 3.10.0 */ validateSync } from "./ParseResult.js" /* c8 ignore end */ /** * @category encoding * @since 3.10.0 */ export const encodeUnknown = <A, I, R>( schema: Schema<A, I, R>, options?: ParseOptions ) => { const encodeUnknown = ParseResult.encodeUnknown(schema, options) return (u: unknown, overrideOptions?: ParseOptions): Effect.Effect<I, ParseResult.ParseError, R> => ParseResult.mapError(encodeUnknown(u, overrideOptions), ParseResult.parseError) } /** * @category encoding * @since 3.10.0 */ export const encodeUnknownEither = <A, I>( schema: Schema<A, I, never>, options?: ParseOptions ) => { const encodeUnknownEither = ParseResult.encodeUnknownEither(schema, options) return (u: unknown, overrideOptions?: ParseOptions): either_.Either<I, ParseResult.ParseError> => either_.mapLeft(encodeUnknownEither(u, overrideOptions), ParseResult.parseError) } /** * @category encoding * @since 3.10.0 */ export const encodeUnknownPromise = <A, I>( schema: Schema<A, I, never>, options?: ParseOptions ) => { const parser = encodeUnknown(schema, options) return (u: unknown, overrideOptions?: ParseOptions): Promise<I> => Effect.runPromise(parser(u, overrideOptions)) } /** * @category encoding * @since 3.10.0 */ export const encode: <A, I, R>( schema: Schema<A, I, R>, options?: ParseOptions ) => (a: A, overrideOptions?: ParseOptions) => Effect.Effect<I, ParseResult.ParseError, R> = encodeUnknown /** * @category encoding * @since 3.10.0 */ export const encodeEither: <A, I>( schema: Schema<A, I, never>, options?: ParseOptions ) => (a: A, overrideOptions?: ParseOptions) => either_.Either<I, ParseResult.ParseError> = encodeUnknownEither /** * @category encoding * @since 3.10.0 */ export const encodePromise: <A, I>( schema: Schema<A, I, never>, options?: ParseOptions ) => (a: A, overrideOptions?: ParseOptions) => Promise<I> = encodeUnknownPromise /** * @category decoding * @since 3.10.0 */ export const decodeUnknown = <A, I, R>( schema: Schema<A, I, R>, options?: ParseOptions ) => { const decodeUnknown = ParseResult.decodeUnknown(schema, options) return (u: unknown, overrideOptions?: ParseOptions): Effect.Effect<A, ParseResult.ParseError, R> => ParseResult.mapError(decodeUnknown(u, overrideOptions), ParseResult.parseError) } /** * @category decoding * @since 3.10.0 */ export const decodeUnknownEither = <A, I>( schema: Schema<A, I, never>, options?: ParseOptions ) => { const decodeUnknownEither = ParseResult.decodeUnknownEither(schema, options) return (u: unknown, overrideOptions?: ParseOptions): either_.Either<A, ParseResult.ParseError> => either_.mapLeft(decodeUnknownEither(u, overrideOptions), ParseResult.parseError) } /** * @category decoding * @since 3.10.0 */ export const decodeUnknownPromise = <A, I>( schema: Schema<A, I, never>, options?: ParseOptions ) => { const parser = decodeUnknown(schema, options) return (u: unknown, overrideOptions?: ParseOptions): Promise<A> => Effect.runPromise(parser(u, overrideOptions)) } /** * @category decoding * @since 3.10.0 */ export const decode: <A, I, R>( schema: Schema<A, I, R>, options?: ParseOptions ) => (i: I, overrideOptions?: ParseOptions) => Effect.Effect<A, ParseResult.ParseError, R> = decodeUnknown /** * @category decoding * @since 3.10.0 */ export const decodeEither: <A, I>( schema: Schema<A, I, never>, options?: ParseOptions ) => (i: I, overrideOptions?: ParseOptions) => either_.Either<A, ParseResult.ParseError> = decodeUnknownEither /** * @category decoding * @since 3.10.0 */ export const decodePromise: <A, I>( schema: Schema<A, I, never>, options?: ParseOptions ) => (i: I, overrideOptions?: ParseOptions) => Promise<A> = decodeUnknownPromise /** * @category validation * @since 3.10.0 */ export const validate = <A, I, R>( schema: Schema<A, I, R>, options?: ParseOptions ) => { const validate = ParseResult.validate(schema, options) return (u: unknown, overrideOptions?: ParseOptions): Effect.Effect<A, ParseResult.ParseError, R> => ParseResult.mapError(validate(u, overrideOptions), ParseResult.parseError) } /** * @category validation * @since 3.10.0 */ export const validateEither = <A, I, R>( schema: Schema<A, I, R>, options?: ParseOptions ) => { const validateEither = ParseResult.validateEither(schema, options) return (u: unknown, overrideOptions?: ParseOptions): either_.Either<A, ParseResult.ParseError> => either_.mapLeft(validateEither(u, overrideOptions), ParseResult.parseError) } /** * @category validation * @since 3.10.0 */ export const validatePromise = <A, I>( schema: Schema<A, I, never>, options?: ParseOptions ) => { const parser = validate(schema, options) return (u: unknown, overrideOptions?: ParseOptions): Promise<A> => Effect.runPromise(parser(u, overrideOptions)) } /** * Tests if a value is a `Schema`. * * @category guards * @since 3.10.0 */ export const isSchema = (u: unknown): u is Schema.Any => Predicate.hasProperty(u, TypeId) && Predicate.isObject(u[TypeId]) /** * @category api interface * @since 3.10.0 */ export interface Literal<Literals extends array_.NonEmptyReadonlyArray<AST.LiteralValue>> extends AnnotableClass<Literal<Literals>, Literals[number]> { readonly literals: Readonly<Literals> } function getDefaultLiteralAST<Literals extends array_.NonEmptyReadonlyArray<AST.LiteralValue>>( literals: Literals ): AST.AST { return AST.isMembers(literals) ? AST.Union.make(AST.mapMembers(literals, (literal) => new AST.Literal(literal))) : new AST.Literal(literals[0]) } function makeLiteralClass<Literals extends array_.NonEmptyReadonlyArray<AST.LiteralValue>>( literals: Literals, ast: AST.AST = getDefaultLiteralAST(literals) ): Literal<Literals> { return class LiteralClass extends make<Literals[number]>(ast) { static override annotations(annotations: Annotations.Schema<Literals[number]>): Literal<Literals> { return makeLiteralClass(this.literals, mergeSchemaAnnotations(this.ast, annotations)) } static literals = [...literals] as Literals } } /** * @category constructors * @since 3.10.0 */ export function Literal<Literals extends array_.NonEmptyReadonlyArray<AST.LiteralValue>>( ...literals: Literals ): Literal<Literals> export function Literal(): Never export function Literal<Literals extends ReadonlyArray<AST.LiteralValue>>( ...literals: Literals ): SchemaClass<Literals[number]> export function Literal<Literals extends ReadonlyArray<AST.LiteralValue>>( ...literals: Literals ): SchemaClass<Literals[number]> | Never { return array_.isNonEmptyReadonlyArray(literals) ? makeLiteralClass(literals) : Never } /** * Creates a new `Schema` from a literal schema. * * @example * ```ts * import * as Schema from "effect/Schema" * import { Either } from "effect" * * const schema = Schema.Literal("a", "b", "c").pipe(Schema.pickLiteral("a", "b")) * * assert.deepStrictEqual(Schema.decodeSync(schema)("a"), "a") * assert.deepStrictEqual(Schema.decodeSync(schema)("b"), "b") * assert.strictEqual(Either.isLeft(Schema.decodeUnknownEither(schema)("c")), true) * ``` * * @category constructors * @since 3.10.0 */ export const pickLiteral = <A extends AST.LiteralValue, L extends array_.NonEmptyReadonlyArray<A>>(...literals: L) => <I, R>(_schema: Schema<A, I, R>): Literal<[...L]> => Literal(...literals) /** * @category constructors * @since 3.10.0 */ export const UniqueSymbolFromSelf = <S extends symbol>(symbol: S): SchemaClass<S> => make(new AST.UniqueSymbol(symbol)) /** * @category api interface * @since 3.10.0 */ export interface Enums<A extends EnumsDefinition> extends AnnotableClass<Enums<A>, A[keyof A]> { readonly enums: A } /** * @since 3.10.0 */ export type EnumsDefinition = { [x: string]: string | number } const getDefaultEnumsAST = <A extends EnumsDefinition>(enums: A) => new AST.Enums( Object.keys(enums).filter( (key) => typeof enums[enums[key]] !== "number" ).map((key) => [key, enums[key]]) ) const makeEnumsClass = <A extends EnumsDefinition>( enums: A, ast: AST.AST = getDefaultEnumsAST(enums) ): Enums<A> => (class EnumsClass extends make<A[keyof A]>(ast) { static override annotations(annotations: Annotations.Schema<A[keyof A]>) { return makeEnumsClass(this.enums, mergeSchemaAnnotations(this.ast, annotations)) } static enums = { ...enums } }) /** * @category constructors * @since 3.10.0 */ export const Enums = <A extends EnumsDefinition>(enums: A): Enums<A> => makeEnumsClass(enums) type AppendType< Template extends string, Next > = Next extends AST.LiteralValue ? `${Template}${Next}` : Next extends Schema<infer A extends AST.LiteralValue, infer _I, infer _R> ? `${Template}${A}` : never type GetTemplateLiteralType<Params> = Params extends [...infer Init, infer Last] ? AppendType<GetTemplateLiteralType<Init>, Last> : `` /** * @category API interface * @since 3.10.0 */ export interface TemplateLiteral<A> extends SchemaClass<A> {} type TemplateLiteralParameter = Schema.AnyNoContext | AST.LiteralValue /** * @category template literal * @since 3.10.0 */ export const TemplateLiteral = <Params extends array_.NonEmptyReadonlyArray<TemplateLiteralParameter>>( ...[head, ...tail]: Params ): TemplateLiteral<GetTemplateLiteralType<Params>> => { const spans: Array<AST.TemplateLiteralSpan> = [] let h = "" let ts = tail if (isSchema(head)) { if (AST.isLiteral(head.ast)) { h = String(head.ast.literal) } else { ts = [head, ...ts] } } else { h = String(head) } for (let i = 0; i < ts.length; i++) { const item = ts[i] if (isSchema(item)) { if (i < ts.length - 1) { const next = ts[i + 1] if (isSchema(next)) { if (AST.isLiteral(next.ast)) { spans.push(new AST.TemplateLiteralSpan(item.ast, String(next.ast.literal))) i++ continue } } else { spans.push(new AST.TemplateLiteralSpan(item.ast, String(next))) i++ continue } } spans.push(new AST.TemplateLiteralSpan(item.ast, "")) } else { spans.push(new AST.TemplateLiteralSpan(new AST.Literal(item), "")) } } if (array_.isNonEmptyArray(spans)) { return make(new AST.TemplateLiteral(h, spans)) } else { return make(new AST.TemplateLiteral("", [new AST.TemplateLiteralSpan(new AST.Literal(h), "")])) } } type TemplateLiteralParserParameters = Schema.Any | AST.LiteralValue type GetTemplateLiteralParserType<Params> = Params extends [infer Head, ...infer Tail] ? readonly [ Head extends Schema<infer A, infer _I, infer _R> ? A : Head, ...GetTemplateLiteralParserType<Tail> ] : [] type AppendEncoded< Template extends string, Next > = Next extends AST.LiteralValue ? `${Template}${Next}` : Next extends Schema<infer _A, infer I extends AST.LiteralValue, infer _R> ? `${Template}${I}` : never type GetTemplateLiteralParserEncoded<Params> = Params extends [...infer Init, infer Last] ? AppendEncoded<GetTemplateLiteralParserEncoded<Init>, Last> : `` /** * @category API interface * @since 3.10.0 */ export interface TemplateLiteralParser<Params extends array_.NonEmptyReadonlyArray<TemplateLiteralParserParameters>> extends Schema< GetTemplateLiteralParserType<Params>, GetTemplateLiteralParserEncoded<Params>, Schema.Context<Params[number]> > { readonly params: Params } function getTemplateLiteralParserCoercedElement(encoded: Schema.Any, schema: Schema.Any): Schema.Any | undefined { const ast = encoded.ast switch (ast._tag) { case "Literal": { const literal = ast.literal if (!Predicate.isString(literal)) { const s = String(literal) return transform(Literal(s), schema, { strict: true, decode: () => literal, encode: () => s }) } break } case "NumberKeyword": return compose(NumberFromString, schema) case "Union": { const members: Array<Schema.Any> = [] let hasCoercions = false for (const member of ast.types) { const schema = make(member) const encoded = encodedSchema(schema) const coerced = getTemplateLiteralParserCoercedElement(encoded, schema) if (coerced) { hasCoercions = true } members.push(coerced ?? schema) } return hasCoercions ? compose(Union(...members), schema) : schema } } } /** * @category template literal * @since 3.10.0 */ export const TemplateLiteralParser = <Params extends array_.NonEmptyReadonlyArray<TemplateLiteralParserParameters>>( ...params: Params ): TemplateLiteralParser<Params> => { const encodedSchemas: Array<Schema.Any> = [] const elements: Array<Schema.Any> = [] const schemas: Array<Schema.Any> = [] let coerced = false for (let i = 0; i < params.length; i++) { const param = params[i] const schema = isSchema(param) ? param : Literal(param) schemas.push(schema) const encoded = encodedSchema(schema) encodedSchemas.push(encoded) const element = getTemplateLiteralParserCoercedElement(encoded, schema) if (element) { elements.push(element) coerced = true } else { elements.push(schema) } } const from = TemplateLiteral(...encodedSchemas as any) const re = AST.getTemplateLiteralCapturingRegExp(from.ast as AST.TemplateLiteral) let to = Tuple(...elements) if (coerced) { to = to.annotations({ [AST.AutoTitleAnnotationId]: format(Tuple(...schemas)) }) } return class TemplateLiteralParserClass extends transformOrFail(from, to, { strict: false, decode: (i, _, ast) => { const match = re.exec(i) return match ? ParseResult.succeed(match.slice(1, params.length + 1)) : ParseResult.fail(new ParseResult.Type(ast, i, `${re.source}: no match for ${JSON.stringify(i)}`)) }, encode: (tuple) => ParseResult.succeed(tuple.join("")) }) { static params = params.slice() } as any } const declareConstructor = < const TypeParameters extends ReadonlyArray<Schema.Any>, I, A >( typeParameters: TypeParameters, options: { readonly decode: ( ...typeParameters: { readonly [K in keyof TypeParameters]: Schema< Schema.Type<TypeParameters[K]>, Schema.Encoded<TypeParameters[K]>, never > } ) => ( input: unknown, options: ParseOptions, ast: AST.Declaration ) => Effect.Effect<A, ParseResult.ParseIssue, never> readonly encode: ( ...typeParameters: { readonly [K in keyof TypeParameters]: Schema< Schema.Type<TypeParameters[K]>, Schema.Encoded<TypeParameters[K]>, never > } ) => ( input: unknown, options: ParseOptions, ast: AST.Declaration ) => Effect.Effect<I, ParseResult.ParseIssue, never> }, annotations?: Annotations.Schema<A, TypeParameters> ): SchemaClass<A, I, Schema.Context<TypeParameters[number]>> => makeDeclareClass( typeParameters, 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 = <A>( is: (input: unknown) => input is A, annotations?: Annotations.Schema<A> ): SchemaClass<A> => { const decodeUnknown = () => (input: unknown, _: ParseOptions, ast: AST.Declaration) => is(input) ? ParseResult.succeed(input) : ParseResult.fail(new ParseResult.Type(ast, input)) const encodeUnknown = decodeUnknown return makeDeclareClass([], new AST.Declaration([], decodeUnknown, encodeUnknown, toASTAnnotations(annotations))) } /** * @category api interface * @since 3.13.3 */ export interface declare< A, I = A, P extends ReadonlyArray<Schema.All> = readonly [], R = Schema.Context<P[number]> > extends AnnotableClass<declare<A, I, P, R>, A, I, R> { readonly typeParameters: Readonly<P> } /** * @category api interface * @since 3.13.3 */ export interface AnnotableDeclare< Self extends declare<A, I, P, R>, A, I = A, P extends ReadonlyArray<Schema.All> = readonly [], R = Schema.Context<P[number]> > extends declare<A, I, P, R> { annotations(annotations: Annotations.Schema<A>): Self } function makeDeclareClass<P extends ReadonlyArray<Schema.All>, A, I, R>( typeParameters: P, ast: AST.AST ): declare<A, I, P, R> { return class DeclareClass extends make<A, I, R>(ast) { static override annotations(annotations: Annotations.Schema<A>): declare<A, I, P, R> { return makeDeclareClass(this.typeParameters, mergeSchemaAnnotations(this.ast, annotations)) } static typeParameters = [...typeParameters] as any as P } } /** * The constraint `R extends Schema.Context<P[number]>` 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 3.10.0 */ export const declare: { /** * The constraint `R extends Schema.Context<P[number]>` 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 3.10.0 */ <A>(is: (input: unknown) => input is A, annotations?: Annotations.Schema<A>): declare<A> /** * The constraint `R extends Schema.Context<P[number]>` 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 3.10.0 */ <A, I, const P extends ReadonlyArray<Schema.All>>( typeParameters: P, options: { readonly decode: ( ...typeParameters: { readonly [K in keyof P]: Schema<Schema.Type<P[K]>, Schema.Encoded<P[K]>, never> } ) => ( input: unknown, options: ParseOptions, ast: AST.Declaration ) => Effect.Effect<A, ParseResult.ParseIssue, never> readonly encode: ( ...typeParameters: { readonly [K in keyof P]: Schema<Schema.Type<P[K]>, Schema.Encoded<P[K]>, never> } ) => ( input: unknown, options: ParseOptions, ast: AST.Declaration ) => Effect.Effect<I, ParseResult.ParseIssue, never> }, annotations?: Annotations.Schema<A, { readonly [K in keyof P]: Schema.Type<P[K]> }> ): declare<A, I, P> } = 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 schema id * @since 3.10.0 */ export const BrandSchemaId: unique symbol = Symbol.for("effect/SchemaId/Brand") /** * @category constructors * @since 3.10.0 */ export const fromBrand = <C extends Brand<string | symbol>, A extends Brand.Unbranded<C>>( constructor: Brand.Constructor<C>, annotations?: Annotations.Filter<C, A> ) => <I, R>(self: Schema<A, I, R>): BrandSchema<A & C, I, R> => { const out = makeBrandClass( self, new AST.Refinement( self.ast, function predicate(a: A, _: ParseOptions, ast: AST.AST): option_.Option<ParseResult.ParseIssue> { 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({ schemaId: BrandSchemaId, [BrandSchemaId]: { constructor }, ...annotations }) ) ) return out as any } /** * @category schema id * @since 3.10.0 */ export const InstanceOfSchemaId: unique symbol = Symbol.for("effect/SchemaId/InstanceOf") /** * @category api interface * @since 3.10.0 */ export interface instanceOf<A> extends AnnotableDeclare<instanceOf<A>, A> {} /** * @category constructors * @since 3.10.0 */ export const instanceOf = <A extends abstract new(...args: any) => any>( constructor: A, annotations?: Annotations.Schema<InstanceType<A>> ): instanceOf<InstanceType<A>> => declare( (u): u is InstanceType<A> => u instanceof constructor, { title: constructor.name, description: `an instance of ${constructor.name}`, pretty: (): pretty_.Pretty<InstanceType<A>> => String, schemaId: InstanceOfSchemaId, [InstanceOfSchemaId]: { constructor }, ...annotations } ) /** * @category primitives * @since 3.10.0 */ export class Undefined extends make<undefined>(AST.undefinedKeyword) {} /** * @category primitives * @since 3.10.0 */ export class Void extends make<void>(AST.voidKeyword) {} /** * @category primitives * @since 3.10.0 */ export class Null extends make<null>(AST.null) {} /** * @category primitives * @since 3.10.0 */ export class Never extends make<never>(AST.neverKeyword) {} /** * @category primitives * @since 3.10.0 */ export class Unknown extends make<unknown>(AST.unknownKeyword) {} /** * @category primitives * @since 3.10.0 */ export class Any extends make<any>(AST.anyKeyword) {} /** * @category primitives * @since 3.10.0 */ export class BigIntFromSelf extends make<bigint>(AST.bigIntKeyword) {} /** * @category primitives * @since 3.10.0 */ export class SymbolFromSelf extends make<symbol>(AST.symbolKeyword) {} /** @ignore */ class String$ extends make<string>(AST.stringKeyword) {} /** @ignore */ class Number$ extends make<number>(AST.numberKeyword) {} /** @ignore */ class Boolean$ extends make<boolean>(AST.booleanKeyword) {} /** @ignore */ class Object$ extends make<object>(AST.objectKeyword) {} export { /** * @category primitives * @since 3.10.0 */ Boolean$ as Boolean, /** * @category primitives * @since 3.10.0 */ Number$ as Number, /** * @category primitives * @since 3.10.0 */ Object$ as Object, /** * @category primitives * @since 3.10.0 */ String$ as String } /** * @category api interface * @since 3.10.0 */ export interface Union<Members extends ReadonlyArray<Schema.All>> extends AnnotableClass< Union<Members>, Schema.Type<Members[number]>, Schema.Encoded<Members[number]>, Schema.Context<Members[number]> > { readonly members: Readonly<Members> } const getDefaultUnionAST = <Members extends AST.Members<Schema.All>>(members: Members): AST.AST => AST.Union.make(members.map((m) => m.ast)) function makeUnionClass<Members extends AST.Members<Schema.All>>( members: Members, ast: AST.AST = getDefaultUnionAST(members) ): Union<Members> { return class UnionClass extends make< Schema.Type<Members[number]>, Schema.Encoded<Members[number]>, Schema.Context<Members[number]> >(ast) { static override annotations(annotations: Annotations.Schema<Schema.Type<Members[number]>>): Union<Members> { return makeUnionClass(this.members, mergeSchemaAnnotations(this.ast, annotations)) } static members = [...members] } } /** * @category combinators * @since 3.10.0 */ export function Union<Members extends AST.Members<Schema.All>>(...members: Members): Union<Members> export function Union<Member extends Schema.All>(member: Member): Member export function Union(): typeof Never export function Union<Members extends ReadonlyArray<Schema.All>>( ...members: Members ): Schema<Schema.Type<Members[number]>, Schema.Encoded<Members[number]>, Schema.Context<Members[number]>> export function Union<Members extends ReadonlyArray<Schema.All>>( ...members: Members ) { return AST.isMembers(members) ? makeUnionClass(members) : array_.isNonEmptyReadonlyArray(members) ? members[0] : Never } /** * @category api interface * @since 3.10.0 */ export interface NullOr<S extends Schema.All> extends Union<[S, typeof Null]> { annotations(annotations: Annotations.Schema<Schema.Type<S> | null>): NullOr<S> } /** * @category combinators * @since 3.10.0 */ export const NullOr = <S extends Schema.All>(self: S): NullOr<S> => Union(self, Null) /** * @category api interface * @since 3.10.0 */ export interface UndefinedOr<S extends Schema.All> extends Union<[S, typeof Undefined]> { annotations(annotations: Annotations.Schema<Schema.Type<S> | undefined>): UndefinedOr<S> } /** * @category combinators * @since 3.10.0 */ export const UndefinedOr = <S extends Schema.All>(self: S): UndefinedOr<S> => Union(self, Undefined) /** * @category api interface * @since 3.10.0 */ export interface NullishOr<S extends Schema.All> extends Union<[S, typeof Null, typeof Undefined]> { annotations(annotations: Annotations.Schema<Schema.Type<S> | null | undefined>): NullishOr<S> } /** * @category combinators * @since 3.10.0 */ export const NullishOr = <S extends Schema.All>(self: S): NullishOr<S> => Union(self, Null, Undefined) /** * @category combinators * @since 3.10.0 */ export const keyof = <A, I, R>(self: Schema<A, I, R>): SchemaClass<keyof A> => make<keyof A>(AST.keyof(self.ast)) /** * @since 3.10.0 */ export declare namespace Element { /** * @since 3.10.0 */ export interface Annotations<A> extends Annotations.Doc<A> { readonly missingMessage?: AST.MissingMessageAnnotation } /** * @since 3.10.0 */ export type Token = "" | "?" } /** * @category API interface * @since 3.10.0 */ export interface Element<S extends Schema.Any, Token extends Element.Token> extends Schema.Variance<Schema.Type<S>, Schema.Encoded<S>, Schema.Context<S>> { readonly _Token: Token readonly ast: AST.OptionalType readonly from: S annotations(annotations: Element.Annotations<Schema.Type<S>>): Element<S, Token> } /** * @since 3.10.0 */ export const element = <S extends Schema.Any>(self: S): Element<S, ""> => new ElementImpl(new AST.OptionalType(self.ast, false), self) /** * @since 3.10.0 */ export const optionalElement = <S extends Schema.Any>(self: S): Element<S, "?"> => new ElementImpl(new AST.OptionalType(self.ast, true), self) class ElementImpl<S extends Schema.Any, Token extends Element.Token> implements Element<S, Token> { readonly [TypeId]!: Schema.Variance<Schema.Type<S>, Schema.Encoded<S>, Schema.Context<S>>[TypeId] readonly _Token!: Token constructor( readonly ast: AST.OptionalType, readonly from: S ) {} annotations( annotations: Annotations.Schema<Schema.Type<S>> ): ElementImpl<S, Token> { 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 3.10.0 */ export declare namespace TupleType { type ElementsType< Elements, Out extends ReadonlyArray<any> = readonly [] > = Elements extends readonly [infer Head, ...infer Tail] ? Head extends Element<infer T, "?"> ? ElementsType<Tail, readonly [...Out, Schema.Type<T>?]> : ElementsType<Tail, readonly [...Out, Schema.Type<Head>]> : Out type ElementsEncoded< Elements, Out extends ReadonlyArray<any> = readonly [] > = Elements extends readonly [infer Head, ...infer Tail] ? Head extends Element<infer T, "?"> ? ElementsEncoded<Tail, readonly [...Out, Schema.Encoded<T>?]> : ElementsEncoded<Tail, readonly [...Out, Schema.Encoded<Head>]> : Out /** * @since 3.10.0 */ export type Elements = ReadonlyArray<Schema.Any | Element<Schema.Any, Element.Token>> /** * @since 3.10.0 */ export type Rest = ReadonlyArray<Schema.Any | Element<Schema.Any, "">> /** * @since 3.10.0 */ export type Type<Elements extends TupleType.Elements, Rest extends TupleType.Rest> = Rest extends [infer Head, ...infer Tail] ? Readonly<[ ...ElementsType<Elements>, ...ReadonlyArray<Schema.Type<Head>>, ...{ readonly [K in keyof Tail]: Schema.Type<Tail[K]> } ]> : ElementsType<Elements> /** * @since 3.10.0 */ export type Encoded<Elements extends TupleType.Elements, Rest extends TupleType.Rest> = Rest extends [infer Head, ...infer Tail] ? Readonly<[ ...ElementsEncoded<Elements>, ...ReadonlyArray<Schema.Encoded<Head>>, ...{ readonly [K in keyof Tail]: Schema.Encoded<Tail[K]> } ]> : ElementsEncoded<Elements> } /** * @category api interface * @since 3.10.0 */ export interface TupleType<Elements extends TupleType.Elements, Rest extends TupleType.Rest> extends AnnotableClass< TupleType<Elements, Rest>, TupleType.Type<Elements, Rest>, TupleType.Encoded<Elements, Rest>, Schema.Context<Elements[number]> | Schema.Context<Rest[number]> > { readonly elements: Readonly<Elements> readonly rest: Readonly<Rest> } const getDefaultTupleTypeAST = <Elements extends TupleType.Elements, Rest extends TupleType.Rest>( 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 ) function makeTupleTypeClass<Elements extends TupleType.Elements, Rest extends TupleType.Rest>( elements: Elements, rest: Rest, ast: AST.AST = getDefaultTupleTypeAST(elements, rest) ) { return class TupleTypeClass extends make< TupleType.Type<Elements, Rest>, TupleType.Encoded<Elements, Rest>, Schema.Context<Elements[number]> | Schema.Context<Rest[number]> >(ast) { static override annotations( annotations: Annotations.Schema<TupleType.Type<Elements, Rest>> ): TupleType<Elements, Rest> { 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 3.10.0 */ export interface Tuple<Elements extends TupleType.Elements> extends TupleType<Elements, []> { annotations(annotations: Annotations.Schema<TupleType.Type<Elements, []>>): Tuple<Elements> } /** * @category api interface * @since 3.13.3 */ export interface Tuple2<Fst extends Schema.Any, Snd extends Schema.Any> extends AnnotableClass< Tuple2<Fst, Snd>, readonly [Schema.Type<Fst>, Schema.Type<Snd>], readonly [Schema.Encoded<Fst>, Schema.Encoded<Snd>], Schema.Context<Fst> | Schema.Context<Snd> > { readonly elements: readonly [Fst, Snd] readonly rest: readonly [] } /** * @category constructors * @since 3.10.0 */ export function Tuple< const Elements extends TupleType.Elements, Rest extends array_.NonEmptyReadonlyArray<TupleType.Rest[number]> >(elements: Elements, ...rest: Rest): TupleType<Elements, Rest> export function Tuple<Fst extends Schema.Any, Snd extends Schema.Any>(fst: Fst, snd: Snd): Tuple2<Fst, Snd> export function Tuple<Elements extends TupleType.Elements>(...elements: Elements): Tuple<Elements> export function Tuple(...args: ReadonlyArray<any>): any { return Array.isArray(args[0]) ? makeTupleTypeClass(args[0], args.slice(1)) : makeTupleTypeClass(args, []) } /** * @category api interface * @since 3.10.0 */ export interface Array$<Value extends Schema.Any> extends TupleType<[], [Value]> { readonly value: Value annotations(annotations: Annotations.Schema<TupleType.Type<[], [Value]>>): Array$<Value> } function makeArrayClass<Value extends Schema.Any>( value: Value, ast?: AST.AST ): Array$<Value> { return class ArrayClass extends makeTupleTypeClass<[], [Value]>([], [value], ast) { static override annotations(annotations: Annotations.Schema<TupleType.Type<[], [Value]>>) { return makeArrayClass(this.value, mergeSchemaAnnotations(this.ast, annotations)) } static value = value } } const Array$ = <Value extends Schema.Any>(value: Value): Array$<Value> => makeArrayClass(value) export { /** * @category constructors * @since 3.10.0 */ Array$ as Array } /** * @category api interface * @since 3.10.0 */ export interface NonEmptyArray<Value extends Schema.Any> extends AnnotableClass< NonEmptyArray<Value>, array_.NonEmptyReadonlyArray<Schema.Type<Value>>, array_.NonEmptyReadonlyArray<Schema.Encoded<Value>>, Schema.Context<Value> > { readonly elements: readonly [Value] readonly rest: readonly [Value] readonly value: Value } function makeNonEmptyArrayClass<Value extends Schema.Any>( value: Value, ast?: AST.AST ) { return class NonEmptyArrayClass extends makeTupleTypeClass<[Value], [Value]>([value], [value], ast) { static override annotations(annotations: Annotations.Schema<TupleType.Type<[Value], [Value]>>) { return makeNonEmptyArrayClass(this.value, mergeSchemaAnnotations(this.ast, annotations)) } static value = value } } /** * @category constructors * @since 3.10.0 */ export const NonEmptyArray = <Value extends Schema.Any>(value: Value): NonEmptyArray<Value> => makeNonEmptyArrayClass(value) as any /** * @category api interface * @since 3.10.0 */ export interface ArrayEnsure<Value extends Schema.Any> extends transform<Union<[Value, Array$<Value>]>, Array$<SchemaClass<Schema.Type<Value>>>> {} /** * @category constructors * @since 3.10.0 */ export function ArrayEnsure<Value extends Schema.Any>(value: Value): ArrayEnsure<Value> { return transform(Union(value, Array$(value)), Array$(typeSchema(asSchema(value))), { strict: true, decode: (i) => array_.ensure(i), encode: (a) => a.length === 1 ? a[0] : a }) } /** * @category api interface * @since 3.10.0 */ export interface NonEmptyArrayEnsure<Value extends Schema.Any> extends transform<Union<[Value, NonEmptyArray<Value>]>, NonEmptyArray<SchemaClass<Schema.Type<Value>>>> {} /** * @category constructors * @since 3.10.0 */ export function NonEmptyArrayEnsure<Value extends Schema.Any>(value: Value): NonEmptyArrayEnsure<Value> { return transform(Union(value, NonEmptyArray(value)), NonEmptyArray(typeSchema(asSchema(value))), { strict: true, decode: (i) => array_.isNonEmptyReadonlyArray(i) ? i : array_.of(i), encode: (a) => a.length === 1 ? a[0] : a }) } /** * @since 3.10.0 */ export declare namespace PropertySignature { /** * @since 3.10.0 */ export type Token = "?:" | ":" /** * @since 3.10.0 */ export type Any<Key extends PropertyKey = PropertyKey> = PropertySignature< Token, any, Key, Token, any, boolean, unknown > /** * @since 3.10.0 */ export type All<Key extends PropertyKey = PropertyKey> = | Any<Key> | PropertySignature<Token, never, Key, Token, any, boolean, unknown> | PropertySignature<Token, any, Key, Token, never, boolean, unknown> | PropertySignature<Token, never, Key, Token, never, boolean, unknown> /** * @since 3.10.0 */ export type AST = | PropertySignatureDeclaration | PropertySignatureTransformation /** * @since 3.10.0 */ export interface Annotations<A> extends Annotations.Doc<A> { readonly missingMessage?: AST.MissingMessageAnnotation } } const formatPropertySignatureToken = (isOptional: boolean): string => isOptional ? "\"?:\"" : "\":\"" /** * @category PropertySignature * @since 3.10.0 */ export class PropertySignatureDeclaration extends AST.OptionalType { /** * @since 3.10.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 3.10.0 */ toString() { const token = formatPropertySignatureToken(this.isOptional) const type = String(this.type) return `PropertySignature<${token}, ${type}, never, ${token}, ${type}>` } } /** * @category PropertySignature * @since 3.10.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 3.10.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) } /*