| /** | |
| * Describes data shapes and how unknown input becomes trusted values. | |
| * | |
| * A schema can validate input, decode it into an application type, and encode | |
| * that value back to another representation. This module contains the main | |
| * schema, codec, decoder, and encoder APIs, together with schemas for common | |
| * JavaScript values and Effect data types. It also supports refinements, | |
| * transformations, defaults, classes, JSON Schema generation, test data | |
| * generation, formatting, equivalence, optics, and differs derived from schema | |
| * definitions. | |
| * | |
| * @since 4.0.0 | |
| */ | |
| /** @effect-diagnostics schemaStructWithTag:skip-file */ | |
| import type { StandardJSONSchemaV1, StandardSchemaV1 } from "@standard-schema/spec" | |
| import * as Arr from "./Array.ts" | |
| import * as BigDecimal_ from "./BigDecimal.ts" | |
| import type * as Brand from "./Brand.ts" | |
| import * as Cause_ from "./Cause.ts" | |
| import * as Chunk_ from "./Chunk.ts" | |
| import type * as Combiner from "./Combiner.ts" | |
| import * as Data from "./Data.ts" | |
| import * as DateTime from "./DateTime.ts" | |
| import type { Differ } from "./Differ.ts" | |
| import * as Duration_ from "./Duration.ts" | |
| import * as Effect from "./Effect.ts" | |
| import * as Encoding from "./Encoding.ts" | |
| import * as Equal from "./Equal.ts" | |
| import * as Equivalence from "./Equivalence.ts" | |
| import * as Exit_ from "./Exit.ts" | |
| import type { Formatter } from "./Formatter.ts" | |
| import { format, formatPropertyKey } from "./Formatter.ts" | |
| import { identity, memoize } from "./Function.ts" | |
| import * as HashMap_ from "./HashMap.ts" | |
| import * as HashSet_ from "./HashSet.ts" | |
| import * as core from "./internal/core.ts" | |
| import * as InternalAnnotations from "./internal/schema/annotations.ts" | |
| import * as InternalArbitrary from "./internal/schema/arbitrary.ts" | |
| import * as InternalEquivalence from "./internal/schema/equivalence.ts" | |
| import * as InternalStandard from "./internal/schema/representation.ts" | |
| import * as InternalSchema from "./internal/schema/schema.ts" | |
| import { SchemaError } from "./internal/schema/schema.ts" | |
| import * as JsonPatch from "./JsonPatch.ts" | |
| import * as JsonSchema from "./JsonSchema.ts" | |
| import { remainder } from "./Number.ts" | |
| import * as Optic_ from "./Optic.ts" | |
| import * as Option_ from "./Option.ts" | |
| import * as Order from "./Order.ts" | |
| import * as Pipeable from "./Pipeable.ts" | |
| import * as Predicate from "./Predicate.ts" | |
| import * as Record_ from "./Record.ts" | |
| import * as Redacted_ from "./Redacted.ts" | |
| import * as Result_ from "./Result.ts" | |
| import * as Scheduler from "./Scheduler.ts" | |
| import * as SchemaAST from "./SchemaAST.ts" | |
| import * as SchemaGetter from "./SchemaGetter.ts" | |
| import * as SchemaIssue from "./SchemaIssue.ts" | |
| import * as SchemaParser from "./SchemaParser.ts" | |
| import type * as SchemaRepresentation from "./SchemaRepresentation.ts" | |
| import * as SchemaTransformation from "./SchemaTransformation.ts" | |
| import type { Assign, Lambda, Mutable, Simplify } from "./Struct.ts" | |
| import * as Struct_ from "./Struct.ts" | |
| import * as FastCheck from "./testing/FastCheck.ts" | |
| import type { RequiredKeys, UnionToIntersection } from "./Types.ts" | |
| import type { Unify } from "./Unify.ts" | |
| const TypeId = InternalSchema.TypeId | |
| /** | |
| * Whether a schema field is required or optional within a struct. | |
| * | |
| * @see {@link optionalKey} — mark a struct field as optional | |
| * @see {@link optional} — mark a struct field as optional with `| undefined` | |
| * | |
| * @category models | |
| * @since 4.0.0 | |
| */ | |
| export type Optionality = "required" | "optional" | |
| /** | |
| * Whether a schema field is readonly or mutable within a struct. | |
| * | |
| * @see {@link mutableKey} — mark a struct field as mutable | |
| * | |
| * @category models | |
| * @since 4.0.0 | |
| */ | |
| export type Mutability = "readonly" | "mutable" | |
| /** | |
| * Whether a schema field has a constructor default value. | |
| * | |
| * @see {@link withConstructorDefault} — add a default to a schema field | |
| * @see {@link tag} — creates a literal field with a constructor default | |
| * | |
| * @category models | |
| * @since 4.0.0 | |
| */ | |
| export type ConstructorDefault = "no-default" | "with-default" | |
| /** | |
| * Options for `makeEffect`, `make`, and Class constructors. | |
| * | |
| * **When to use** | |
| * | |
| * Use when passing `disableChecks: true` to skip validation when you trust the data. | |
| * - Pass `parseOptions` to control error reporting behavior. | |
| * | |
| * @see {@link Bottom.makeEffect} | |
| * @see {@link Bottom.make} | |
| * | |
| * @category options | |
| * @since 3.13.4 | |
| */ | |
| export interface MakeOptions { | |
| /** | |
| * The parse options to use for the schema. | |
| */ | |
| readonly parseOptions?: SchemaAST.ParseOptions | undefined | |
| /** | |
| * Whether to disable validation for the schema. | |
| */ | |
| readonly disableChecks?: boolean | undefined | |
| } | |
| /** | |
| * The fully-parameterized base interface for all schemas. Exposes all 14 type | |
| * parameters controlling type inference, mutability, optionality, services, | |
| * and transformation behavior. | |
| * | |
| * **When to use** | |
| * | |
| * Use when you are writing advanced generic schema utilities or performing schema | |
| * introspection. | |
| * - In user code, prefer {@link Schema}, {@link Codec}, {@link Decoder}, or | |
| * {@link Encoder} instead. | |
| * | |
| * @see {@link Top} — the existential "any schema" type (erased type params) | |
| * @see {@link Schema} — tracks only the decoded Type | |
| * @see {@link Codec} — tracks Type + Encoded | |
| * | |
| * @category models | |
| * @since 4.0.0 | |
| */ | |
| export interface Bottom< | |
| out T, | |
| out E, | |
| out RD, | |
| out RE, | |
| out Ast extends SchemaAST.AST, | |
| out Rebuild extends Top, | |
| out TypeMakeIn = T, | |
| out Iso = T, | |
| in out TypeParameters extends ReadonlyArray<Top> = readonly [], | |
| out TypeMake = TypeMakeIn, | |
| out TypeMutability extends Mutability = "readonly", | |
| out TypeOptionality extends Optionality = "required", | |
| out TypeConstructorDefault extends ConstructorDefault = "no-default", | |
| out EncodedMutability extends Mutability = "readonly", | |
| out EncodedOptionality extends Optionality = "required" | |
| > extends Pipeable.Pipeable { | |
| readonly [TypeId]: typeof TypeId | |
| readonly "ast": Ast | |
| readonly "Rebuild": Rebuild | |
| readonly "~type.parameters": TypeParameters | |
| readonly "Type": T | |
| readonly "Encoded": E | |
| readonly "DecodingServices": RD | |
| readonly "EncodingServices": RE | |
| readonly "~type.make.in": TypeMakeIn | |
| readonly "~type.make": TypeMake // useful to type the `refine` interface | |
| readonly "~type.constructor.default": TypeConstructorDefault | |
| readonly "Iso": Iso | |
| readonly "~type.mutability": TypeMutability | |
| readonly "~type.optionality": TypeOptionality | |
| readonly "~encoded.mutability": EncodedMutability | |
| readonly "~encoded.optionality": EncodedOptionality | |
| annotate(annotations: Annotations.Bottom<this["Type"], this["~type.parameters"]>): this["Rebuild"] | |
| annotateKey(annotations: Annotations.Key<this["Type"]>): this["Rebuild"] | |
| check(...checks: readonly [SchemaAST.Check<this["Type"]>, ...Array<SchemaAST.Check<this["Type"]>>]): this["Rebuild"] | |
| rebuild(ast: this["ast"]): this["Rebuild"] | |
| /** | |
| * Constructs a value from the make input representation synchronously. | |
| * | |
| * **When to use** | |
| * | |
| * Use when constructor input is trusted or when validation failure | |
| * should abort with a thrown `Error`. | |
| * | |
| * **Details** | |
| * | |
| * Applies constructor defaults and type-side validation according to | |
| * `MakeOptions`. | |
| * | |
| * **Gotchas** | |
| * | |
| * Throws an `Error` with the schema issue in its `cause` when validation | |
| * fails. | |
| * | |
| * @see {@link Bottom.makeOption} — construct synchronously and discard validation details | |
| * @see {@link Bottom.makeEffect} — construct through `Effect` when validation failure should stay in the error channel | |
| */ | |
| make(input: this["~type.make.in"], options?: MakeOptions): this["Type"] | |
| /** | |
| * Constructs a value from the make input representation, returning `Option.none` | |
| * when validation fails. | |
| * | |
| * **When to use** | |
| * | |
| * Use when you only need to know whether construction succeeds | |
| * and do not need validation details. | |
| * | |
| * **Details** | |
| * | |
| * Applies constructor defaults and type-side validation according to | |
| * `MakeOptions`. | |
| * | |
| * @see {@link Bottom.make} — construct synchronously when validation failure should throw | |
| * @see {@link Bottom.makeEffect} — construct through `Effect` when validation details should stay in the error channel | |
| */ | |
| makeOption(input: this["~type.make.in"], options?: MakeOptions): Option_.Option<this["Type"]> | |
| /** | |
| * Constructs a value from the make input representation, returning validation | |
| * failures in the `Effect` error channel. | |
| * | |
| * **When to use** | |
| * | |
| * Use when constructor input may fail validation and you want to | |
| * compose that failure with other `Effect` operations instead of throwing. | |
| * | |
| * @see {@link Bottom.make} — construct synchronously when validation failure should throw | |
| * @see {@link Bottom.makeOption} — construct synchronously and discard validation details | |
| */ | |
| makeEffect(input: this["~type.make.in"], options?: MakeOptions): Effect.Effect<this["Type"], SchemaError> | |
| } | |
| /** | |
| * Type-level representation returned by {@link declareConstructor}. | |
| * | |
| * @category constructors | |
| * @since 4.0.0 | |
| */ | |
| export interface declareConstructor<T, E, TypeParameters extends ReadonlyArray<Top>, Iso = T> extends | |
| Bottom< | |
| T, | |
| E, | |
| TypeParameters[number]["DecodingServices"], | |
| TypeParameters[number]["EncodingServices"], | |
| SchemaAST.Declaration, | |
| declareConstructor<T, E, TypeParameters, Iso>, | |
| T, | |
| Iso, | |
| TypeParameters | |
| > | |
| {} | |
| /** | |
| * Creates a schema for a **parametric** type (a generic container such as | |
| * `Array<A>`, `Option<A>`, etc.) by accepting a list of type-parameter schemas | |
| * and a decoder factory. | |
| * | |
| * **When to use** | |
| * | |
| * Use when you are defining a schema for a generic container whose validation | |
| * depends on one or more type-parameter schemas. | |
| * | |
| * **Details** | |
| * | |
| * The outer call `declareConstructor<T, E, Iso>()` fixes the decoded type `T`, | |
| * the encoded type `E`, and the optional iso type. The inner call receives: | |
| * - `typeParameters` — the concrete schemas for each type variable | |
| * - `run` — a factory that, given resolved codecs for each type parameter, | |
| * returns a parsing function `(u, ast, options) => Effect<T, Issue>` | |
| * - `annotations` — optional metadata | |
| * | |
| * @see {@link declare} for creating schemas for non-parametric types. | |
| * | |
| * **Example** (Schema for a parametric `Box<A>` type) | |
| * | |
| * ```ts | |
| * import { Effect, Option, Schema, SchemaIssue as Issue, SchemaParser } from "effect" | |
| * | |
| * interface Box<A> { | |
| * readonly value: A | |
| * } | |
| * | |
| * const isBox = (u: unknown): u is Box<unknown> => | |
| * typeof u === "object" && u !== null && "value" in u | |
| * | |
| * const Box = <A extends Schema.Top>(item: A) => | |
| * Schema.declareConstructor<Box<A["Type"]>, Box<A["Encoded"]>>()( | |
| * [item], | |
| * ([itemCodec]) => | |
| * (u, ast, options) => { | |
| * if (!isBox(u)) { | |
| * return Effect.fail(new SchemaIssue.InvalidType(ast, Option.some(u))) | |
| * } | |
| * return Effect.map( | |
| * SchemaParser.decodeUnknownEffect(itemCodec)(u.value, options), | |
| * (value) => ({ value }) | |
| * ) | |
| * } | |
| * ) | |
| * | |
| * const schema = Box(Schema.Number) | |
| * ``` | |
| * | |
| * @category constructors | |
| * @since 4.0.0 | |
| */ | |
| export function declareConstructor<T, E = T, Iso = T>() { | |
| return <const TypeParameters extends ReadonlyArray<Top>>( | |
| typeParameters: TypeParameters, | |
| run: ( | |
| typeParameters: { | |
| readonly [K in keyof TypeParameters]: Codec<TypeParameters[K]["Type"], TypeParameters[K]["Encoded"]> | |
| } | |
| ) => ( | |
| u: unknown, | |
| self: SchemaAST.Declaration, | |
| options: SchemaAST.ParseOptions | |
| ) => Effect.Effect<T, SchemaIssue.Issue>, | |
| annotations?: Annotations.Declaration<T, TypeParameters> | |
| ): declareConstructor<T, E, TypeParameters, Iso> => { | |
| return make( | |
| new SchemaAST.Declaration( | |
| typeParameters.map(SchemaAST.getAST), | |
| (typeParameters) => run(typeParameters.map((ast) => make(ast)) as any), | |
| annotations | |
| ) | |
| ) | |
| } | |
| } | |
| /** | |
| * Type-level representation returned by {@link declare}. | |
| * | |
| * @category constructors | |
| * @since 3.13.3 | |
| */ | |
| export interface declare<T, Iso = T> extends declareConstructor<T, T, readonly [], Iso> { | |
| readonly "Rebuild": declare<T, Iso> | |
| } | |
| /** | |
| * Creates a schema for a **non-parametric** opaque type using a type-guard | |
| * function. The schema accepts any unknown value and succeeds when `is` returns | |
| * `true`, failing with an `InvalidType` issue otherwise. | |
| * | |
| * **When to use** | |
| * | |
| * Use when you are defining a schema for an opaque type with no type parameters | |
| * and validation can be expressed as a type guard. | |
| * | |
| * **Example** (Schema for a custom `UserId` branded type) | |
| * | |
| * ```ts | |
| * import { Schema } from "effect" | |
| * | |
| * type UserId = string & { readonly _tag: "UserId" } | |
| * | |
| * const isUserId = (u: unknown): u is UserId => | |
| * typeof u === "string" && u.startsWith("user_") | |
| * | |
| * const UserId = Schema.declare<UserId>(isUserId, { | |
| * title: "UserId", | |
| * description: "A user identifier starting with 'user_'" | |
| * }) | |
| * ``` | |
| * | |
| * @see {@link declareConstructor} for creating schemas for parametric types. | |
| * | |
| * @category constructors | |
| * @since 3.10.0 | |
| */ | |
| export function declare<T, Iso = T>( | |
| is: (u: unknown) => u is T, | |
| annotations?: Annotations.Declaration<T> | undefined | |
| ): declare<T, Iso> { | |
| return declareConstructor<T, T, Iso>()( | |
| [], | |
| () => (input, ast) => | |
| is(input) ? | |
| Effect.succeed(input) : | |
| Effect.fail(new SchemaIssue.InvalidType(ast, Option_.some(input))), | |
| annotations | |
| ) | |
| } | |
| /** | |
| * Returns a schema widened to the fully-parameterized {@link Bottom} interface, | |
| * making all 14 type parameters visible to TypeScript. | |
| * | |
| * **Details** | |
| * | |
| * Normally, concrete schema interfaces (e.g. `Schema<string>`) hide most type | |
| * parameters. `revealBottom` is useful when writing generic utilities that need | |
| * to inspect or propagate the complete set of type parameters. | |
| * | |
| * **Example** (Inspecting all type parameters of a schema) | |
| * | |
| * ```ts | |
| * import { Schema } from "effect" | |
| * | |
| * const schema = Schema.String | |
| * | |
| * // Widen to Bottom to access all 14 type parameters | |
| * const bottom = Schema.revealBottom(schema) | |
| * | |
| * // `bottom` now exposes Type, Encoded, DecodingServices, EncodingServices, | |
| * // ast, Rebuild, ~type.make.in, Iso, ~type.parameters, etc. | |
| * type T = typeof bottom["Type"] // string | |
| * type E = typeof bottom["Encoded"] // string | |
| * ``` | |
| * | |
| * @category utility types | |
| * @since 4.0.0 | |
| */ | |
| export function revealBottom<S extends Top>( | |
| bottom: S | |
| ): Bottom< | |
| S["Type"], | |
| S["Encoded"], | |
| S["DecodingServices"], | |
| S["EncodingServices"], | |
| S["ast"], | |
| S["Rebuild"], | |
| S["~type.make.in"], | |
| S["Iso"], | |
| S["~type.parameters"], | |
| S["~type.make"], | |
| S["~type.mutability"], | |
| S["~type.optionality"], | |
| S["~type.constructor.default"], | |
| S["~encoded.mutability"], | |
| S["~encoded.optionality"] | |
| > { | |
| return bottom | |
| } | |
| /** | |
| * Adds metadata annotations to a schema without changing its runtime behavior. | |
| * This is the pipeable (curried) counterpart of the `.annotate` method. | |
| * | |
| * **Details** | |
| * | |
| * Annotations provide extra context used by documentation generators, JSON | |
| * Schema converters, error formatters, and other tooling. Common keys include | |
| * `title`, `description`, `examples`, `message`, and `identifier`. | |
| * | |
| * **Example** (Adding a title and description) | |
| * | |
| * ```ts | |
| * import { Schema } from "effect" | |
| * | |
| * const Age = Schema.Number.pipe( | |
| * Schema.annotate({ | |
| * title: "Age", | |
| * description: "A non-negative integer representing age in years" | |
| * }) | |
| * ) | |
| * ``` | |
| * | |
| * @see {@link annotateEncoded} to annotate the encoded side instead. | |
| * | |
| * @category annotations | |
| * @since 4.0.0 | |
| */ | |
| export function annotate<S extends Top>(annotations: Annotations.Bottom<S["Type"], S["~type.parameters"]>) { | |
| return (self: S) => self.annotate(annotations) | |
| } | |
| /** | |
| * Adds metadata annotations to the **encoded** side of a schema without | |
| * changing its runtime behavior. This is the encoded-side counterpart of | |
| * `annotate`, which targets the decoded (Type) side. | |
| * | |
| * **Details** | |
| * | |
| * Internally the schema is flipped so that `Encoded` becomes `Type`, | |
| * annotated, and then flipped back. | |
| * | |
| * **Example** (Adding a title to the encoded representation) | |
| * | |
| * ```ts | |
| * import { Schema } from "effect" | |
| * | |
| * const schema = Schema.NumberFromString.pipe( | |
| * Schema.annotateEncoded({ | |
| * title: "my title" | |
| * }) | |
| * ) | |
| * | |
| * console.log(Schema.toEncoded(schema).ast.annotations?.title) | |
| * // "my title" | |
| * ``` | |
| * | |
| * @see {@link annotate} to annotate the type side instead. | |
| * | |
| * @category annotations | |
| * @since 4.0.0 | |
| */ | |
| export function annotateEncoded<S extends Top>(annotations: Annotations.Bottom<S["Encoded"], readonly []>) { | |
| return (self: S): S["Rebuild"] => flip(flip(self).annotate(annotations)) | |
| } | |
| /** | |
| * Adds key-level annotations to a schema field. This is the pipeable | |
| * (curried) counterpart of the `.annotateKey` method. | |
| * | |
| * **Details** | |
| * | |
| * Key annotations apply to a field's position inside a `Struct` or `Tuple` | |
| * rather than to the field's value type. They can carry a | |
| * `messageMissingKey` to customise the error shown when the field is absent, | |
| * as well as standard documentation fields such as `title`, `description`, | |
| * and `examples`. | |
| * | |
| * **Example** (Custom missing-key message for a required field) | |
| * | |
| * ```ts | |
| * import { Schema } from "effect" | |
| * | |
| * const schema = Schema.Struct({ | |
| * username: Schema.String.pipe( | |
| * Schema.annotateKey({ | |
| * description: "The username used to log in", | |
| * messageMissingKey: "Username is required" | |
| * }) | |
| * ) | |
| * }) | |
| * ``` | |
| * | |
| * @category annotations | |
| * @since 4.0.0 | |
| */ | |
| export function annotateKey<S extends Top>(annotations: Annotations.Key<S["Type"]>) { | |
| return (self: S): S["Rebuild"] => { | |
| return self.rebuild(SchemaAST.annotateKey(self.ast, annotations)) | |
| } | |
| } | |
| /** | |
| * The existential "any schema" type — all type parameters are erased to `unknown`. | |
| * | |
| * **Details** | |
| * | |
| * Use `Top` as a constraint when writing generic utilities that must accept *any* | |
| * schema regardless of its `Type`, `Encoded`, or service requirements. It is the | |
| * widest possible schema type and therefore gives you the least static information. | |
| * | |
| * In user code prefer the narrower interfaces: | |
| * - {@link Schema}`<T>` — when you only care about the decoded type | |
| * - {@link Codec}`<T, E, RD, RE>` — when you need the encoded type and service requirements | |
| * - {@link Decoder}`<T, RD>` — for decode-only APIs | |
| * - {@link Encoder}`<E, RE>` — for encode-only APIs | |
| * | |
| * @category models | |
| * @since 4.0.0 | |
| */ | |
| export interface Top extends | |
| Bottom< | |
| unknown, | |
| unknown, | |
| unknown, | |
| unknown, | |
| SchemaAST.AST, | |
| Top, | |
| unknown, | |
| unknown, | |
| any, // this is because TypeParameters is invariant | |
| unknown, | |
| Mutability, | |
| Optionality, | |
| ConstructorDefault, | |
| Mutability, | |
| Optionality | |
| > | |
| {} | |
| /** | |
| * Namespace of type-level helpers for {@link Schema}. | |
| * | |
| * @since 3.10.0 | |
| */ | |
| export declare namespace Schema { | |
| /** | |
| * Extracts the decoded `Type` from a schema. | |
| * | |
| * **Example** (Extracting the decoded type) | |
| * | |
| * ```ts | |
| * import { Schema } from "effect" | |
| * | |
| * const Person = Schema.Struct({ name: Schema.String, age: Schema.Number }) | |
| * type Person = Schema.Schema.Type<typeof Person> | |
| * // { readonly name: string; readonly age: number } | |
| * ``` | |
| * | |
| * @category utility types | |
| * @since 3.10.0 | |
| */ | |
| export type Type<S> = S extends Top ? S["Type"] : never | |
| } | |
| /** | |
| * A typed view of a schema that tracks only the decoded (output) type `T`. | |
| * | |
| * **Details** | |
| * | |
| * Use `Schema<T>` as a constraint when you want to accept "any schema that | |
| * decodes to `T`" and do not need to know or constrain the encoded | |
| * representation, required services, or any other type parameters. | |
| * | |
| * This is a structural interface — concrete schema values are produced by the | |
| * constructors in this module (e.g. {@link Struct}, {@link String}, {@link Number}). | |
| * When you also need the encoded type or service requirements, use {@link Codec}. | |
| * | |
| * **Example** (Function that accepts any schema decoding to `string`) | |
| * | |
| * ```ts | |
| * import { Schema } from "effect" | |
| * | |
| * declare function print(schema: Schema.Schema<string>): void | |
| * | |
| * print(Schema.String) // ok | |
| * print(Schema.NonEmptyString) // ok | |
| * ``` | |
| * | |
| * @see {@link Codec} — also tracks Encoded, DecodingServices, EncodingServices | |
| * @see {@link Schema.Type} — extract the decoded type at the type level | |
| * | |
| * @category models | |
| * @since 3.10.0 | |
| */ | |
| export interface Schema<out T> extends Top { | |
| readonly "Type": T | |
| readonly "Rebuild": Schema<T> | |
| } | |
| /** | |
| * Namespace of type-level helpers for {@link Codec}. | |
| * | |
| * @since 4.0.0 | |
| */ | |
| export declare namespace Codec { | |
| /** | |
| * Extracts the encoded (`Encoded`) type from a schema. | |
| * | |
| * **Example** (Extracting the encoded type) | |
| * | |
| * ```ts | |
| * import { Schema } from "effect" | |
| * | |
| * const schema = Schema.NumberFromString | |
| * type Enc = Schema.Codec.Encoded<typeof schema> | |
| * // string | |
| * ``` | |
| * | |
| * @category utility types | |
| * @since 3.10.0 | |
| */ | |
| export type Encoded<S> = S extends Top ? S["Encoded"] : never | |
| /** | |
| * Extracts the Effect services required during *decoding* from a schema. | |
| * | |
| * **Example** (Checking decoding service requirements) | |
| * | |
| * ```ts | |
| * import { Schema } from "effect" | |
| * | |
| * const schema = Schema.String | |
| * type RD = Schema.Codec.DecodingServices<typeof schema> | |
| * // never | |
| * ``` | |
| * | |
| * @category utility types | |
| * @since 4.0.0 | |
| */ | |
| export type DecodingServices<S> = S extends Top ? S["DecodingServices"] : never | |
| /** | |
| * Extracts the Effect services required during *encoding* from a schema. | |
| * | |
| * **Example** (Checking encoding service requirements) | |
| * | |
| * ```ts | |
| * import { Schema } from "effect" | |
| * | |
| * const schema = Schema.String | |
| * type RE = Schema.Codec.EncodingServices<typeof schema> | |
| * // never | |
| * ``` | |
| * | |
| * @category utility types | |
| * @since 4.0.0 | |
| */ | |
| export type EncodingServices<S> = S extends Top ? S["EncodingServices"] : never | |
| } | |
| /** | |
| * A schema that additionally supports optic (lens/prism) operations. | |
| * | |
| * **Details** | |
| * | |
| * `Optic<T, Iso>` extends {@link Schema}`<T>` with an `Iso` type that | |
| * describes the isomorphic counterpart used by the optic layer. Crucially, | |
| * decoding and encoding require *no* Effect services (`DecodingServices` and | |
| * `EncodingServices` are both `never`), which means the optic can operate | |
| * purely without an Effect runtime. | |
| * | |
| * Most primitive schemas (e.g. `Schema.String`, `Schema.Number`) implement | |
| * `Optic` automatically. You normally interact with this interface through | |
| * {@link Optic_} utilities rather than constructing it directly. | |
| * | |
| * @category models | |
| * @since 4.0.0 | |
| */ | |
| export interface Optic<out T, out Iso> extends Schema<T> { | |
| readonly "Iso": Iso | |
| readonly "DecodingServices": never | |
| readonly "EncodingServices": never | |
| readonly "Rebuild": Optic<T, Iso> | |
| } | |
| /** | |
| * A schema that tracks the decoded type `T`, the encoded type `E`, and the | |
| * Effect services required during decoding (`RD`) and encoding (`RE`). | |
| * | |
| * **Details** | |
| * | |
| * Use `Codec<T, E, RD, RE>` when you need to preserve full type information | |
| * about a schema — both what it decodes to and what it serializes from/to. | |
| * Most concrete schemas produced by this module implement `Codec`. | |
| * | |
| * For APIs that only need one direction, prefer the narrower views: | |
| * - {@link Decoder}`<T, RD>` — decode-only | |
| * - {@link Encoder}`<E, RE>` — encode-only | |
| * - {@link Schema}`<T>` — type-only (no encoded representation) | |
| * | |
| * **Example** (Accepting a codec that decodes to `number` from `string`) | |
| * | |
| * ```ts | |
| * import { Schema } from "effect" | |
| * | |
| * declare function serialize<T>(codec: Schema.Codec<T, string>): string | |
| * | |
| * serialize(Schema.NumberFromString) // ok — decodes number, encoded as string | |
| * ``` | |
| * | |
| * @see {@link Codec.Encoded} — extract the encoded type | |
| * @see {@link Codec.DecodingServices} — extract required decoding services | |
| * @see {@link Codec.EncodingServices} — extract required encoding services | |
| * @see {@link revealCodec} — helper to make TypeScript infer the full Codec type | |
| * | |
| * @category models | |
| * @since 4.0.0 | |
| */ | |
| export interface Codec<out T, out E = T, out RD = never, out RE = never> extends Schema<T> { | |
| readonly "Encoded": E | |
| readonly "DecodingServices": RD | |
| readonly "EncodingServices": RE | |
| readonly "Rebuild": Codec<T, E, RD, RE> | |
| } | |
| /** | |
| * A {@link Codec} view for APIs that only *decode* (parse/validate) values. | |
| * | |
| * **Details** | |
| * | |
| * Use `Decoder<T, RD>` to accept "any schema that can decode to `T`" without | |
| * constraining or depending on the encoded representation (`Encoded` is | |
| * `unknown`) or encoding services. | |
| * | |
| * **Example** (Function that only needs to decode) | |
| * | |
| * ```ts | |
| * import { Schema } from "effect" | |
| * | |
| * declare function validate<T>(decoder: Schema.Decoder<T>): (input: unknown) => T | |
| * | |
| * validate(Schema.String) // ok | |
| * validate(Schema.NumberFromString) // ok | |
| * ``` | |
| * | |
| * @category models | |
| * @since 4.0.0 | |
| */ | |
| export interface Decoder<out T, out RD = never> extends Codec<T, unknown, RD, unknown> { | |
| readonly "Rebuild": Decoder<T, RD> | |
| } | |
| /** | |
| * A {@link Codec} view for APIs that only *encode* values. | |
| * | |
| * **Details** | |
| * | |
| * Use `Encoder<E, RE>` to accept "any schema that can encode to `E`" without | |
| * constraining or depending on the decoded `Type` (`Type` is `unknown`) or | |
| * decoding services. | |
| * | |
| * **Example** (Function that only needs to encode) | |
| * | |
| * ```ts | |
| * import { Schema } from "effect" | |
| * | |
| * declare function serialize<E>(encoder: Schema.Encoder<E>): (value: unknown) => E | |
| * | |
| * serialize(Schema.String) // ok — encodes to string | |
| * serialize(Schema.NumberFromString) // ok — encodes number to string | |
| * ``` | |
| * | |
| * @category models | |
| * @since 4.0.0 | |
| */ | |
| export interface Encoder<out E, out RE = never> extends Codec<unknown, E, unknown, RE> { | |
| readonly "Rebuild": Encoder<E, RE> | |
| } | |
| /** | |
| * Returns a codec widened to the full {@link Codec} interface, prompting | |
| * TypeScript to infer all four type parameters (`T`, `E`, `RD`, `RE`). | |
| * | |
| * **Details** | |
| * | |
| * When a schema is stored in a variable typed as `Schema<T>` or `Top`, the | |
| * encoded type and service requirements are erased. Passing the value through | |
| * `revealCodec` recovers those parameters without any runtime cost. | |
| * | |
| * **Example** (Recovering encoded type from a schema variable) | |
| * | |
| * ```ts | |
| * import { Schema } from "effect" | |
| * | |
| * const schema: Schema.Schema<number> = Schema.NumberFromString | |
| * | |
| * // Without revealCodec, Encoded is unknown | |
| * const codec = Schema.revealCodec(schema) | |
| * type Enc = typeof codec["Encoded"] // string | |
| * ``` | |
| * | |
| * @category utility types | |
| * @since 4.0.0 | |
| */ | |
| export function revealCodec<T, E, RD, RE>(codec: Codec<T, E, RD, RE>) { | |
| return codec | |
| } | |
| export { | |
| /** | |
| * Error thrown (or returned as the error channel value) when schema decoding | |
| * or encoding fails. | |
| * | |
| * **Details** | |
| * | |
| * The `issue` field contains a structured {@link SchemaIssue.Issue} tree describing | |
| * every validation failure, including the path to the problematic value, | |
| * expected types, and actual values received. `message` renders the issue tree | |
| * as a human-readable string. | |
| * | |
| * Use {@link isSchemaError} to narrow an unknown value to `SchemaError`. | |
| * | |
| * **Example** (Catching a SchemaError) | |
| * | |
| * ```ts | |
| * import { Schema } from "effect" | |
| * | |
| * try { | |
| * Schema.decodeUnknownSync(Schema.Number)("not a number") | |
| * } catch (err) { | |
| * if (Schema.isSchemaError(err)) { | |
| * console.log(err.message) | |
| * // Expected number, actual "not a number" | |
| * } | |
| * } | |
| * ``` | |
| * | |
| * @category errors | |
| * @since 4.0.0 | |
| */ | |
| SchemaError | |
| } | |
| /** | |
| * Returns `true` if `u` is a {@link SchemaError}. | |
| * | |
| * **Example** (Type guard in a catch block) | |
| * | |
| * ```ts | |
| * import { Schema } from "effect" | |
| * | |
| * try { | |
| * Schema.decodeUnknownSync(Schema.Number)("oops") | |
| * } catch (err) { | |
| * if (Schema.isSchemaError(err)) { | |
| * console.log(err._tag) // "SchemaError" | |
| * } | |
| * } | |
| * ``` | |
| * | |
| * @category guards | |
| * @since 4.0.0 | |
| */ | |
| export function isSchemaError(u: unknown): u is SchemaError { | |
| return Predicate.hasProperty(u, InternalSchema.SchemaErrorTypeId) | |
| } | |
| function makeStandardResult<A>(exit: Exit_.Exit<StandardSchemaV1.Result<A>>): StandardSchemaV1.Result<A> { | |
| return Exit_.isSuccess(exit) ? exit.value : { | |
| issues: [{ message: Cause_.pretty(exit.cause) }] | |
| } | |
| } | |
| /** | |
| * Returns a "Standard Schema" object conforming to the [Standard Schema | |
| * v1](https://standardschema.dev/) specification. | |
| * | |
| * **Details** | |
| * | |
| * 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. | |
| * | |
| * **Example** (Creating a standard schema from a regular schema) | |
| * | |
| * ```ts | |
| * import { Schema } from "effect" | |
| * | |
| * // Define custom hook functions for error formatting | |
| * const leafHook = (issue: any) => { | |
| * switch (issue._tag) { | |
| * case "InvalidType": | |
| * return "Expected different type" | |
| * case "InvalidValue": | |
| * return "Invalid value provided" | |
| * case "MissingKey": | |
| * return "Required property missing" | |
| * case "UnexpectedKey": | |
| * return "Unexpected property found" | |
| * case "Forbidden": | |
| * return "Operation not allowed" | |
| * case "OneOf": | |
| * return "Multiple valid options available" | |
| * default: | |
| * return "Validation error" | |
| * } | |
| * } | |
| * | |
| * // Create a standard schema from a regular schema | |
| * const PersonSchema = Schema.Struct({ | |
| * name: Schema.NonEmptyString, | |
| * age: Schema.Number.check(Schema.isBetween({ minimum: 0, maximum: 150 })) | |
| * }) | |
| * | |
| * const standardSchema = Schema.toStandardSchemaV1(PersonSchema, { | |
| * leafHook | |
| * }) | |
| * | |
| * // The standard schema can be used with any Standard Schema v1 compatible library | |
| * const validResult = standardSchema["~standard"].validate({ | |
| * name: "Alice", | |
| * age: 30 | |
| * }) | |
| * console.log(validResult) // { value: { name: "Alice", age: 30 } } | |
| * | |
| * const invalidResult = standardSchema["~standard"].validate({ | |
| * name: "", | |
| * age: 200 | |
| * }) | |
| * console.log(invalidResult) // { issues: [{ path: ["name"], message: "..." }, { path: ["age"], message: "..." }] } | |
| * ``` | |
| * | |
| * @category Standard Schema | |
| * @since 4.0.0 | |
| */ | |
| export function toStandardSchemaV1<S extends Decoder<unknown>>( | |
| self: S, | |
| options?: { | |
| readonly leafHook?: SchemaIssue.LeafHook | undefined | |
| readonly checkHook?: SchemaIssue.CheckHook | undefined | |
| readonly parseOptions?: SchemaAST.ParseOptions | undefined | |
| } | |
| ): StandardSchemaV1<S["Encoded"], S["Type"]> & S { | |
| const decodeUnknownEffect = SchemaParser.decodeUnknownEffect(self) as ( | |
| input: unknown, | |
| options?: SchemaAST.ParseOptions | |
| ) => Effect.Effect<S["Type"], SchemaIssue.Issue> | |
| const parseOptions: SchemaAST.ParseOptions = { errors: "all", ...options?.parseOptions } | |
| const formatter = SchemaIssue.makeFormatterStandardSchemaV1(options) | |
| const validate: StandardSchemaV1<S["Encoded"], S["Type"]>["~standard"]["validate"] = (value: unknown) => { | |
| const scheduler = new Scheduler.MixedScheduler() | |
| const fiber = Effect.runFork( | |
| Effect.match(decodeUnknownEffect(value, parseOptions), { | |
| onFailure: formatter, | |
| onSuccess: (value): StandardSchemaV1.Result<S["Type"]> => ({ value }) | |
| }), | |
| { scheduler } | |
| ) | |
| fiber.currentDispatcher?.flush() | |
| const exit = fiber.pollUnsafe() | |
| if (exit) { | |
| return makeStandardResult(exit) | |
| } | |
| return new Promise((resolve) => { | |
| fiber.addObserver((exit) => { | |
| resolve(makeStandardResult(exit)) | |
| }) | |
| }) | |
| } | |
| if ("~standard" in self) { | |
| const out = self as any | |
| if ("validate" in out["~standard"]) return out | |
| Object.assign(out["~standard"], { validate }) | |
| return out | |
| } else { | |
| return Object.assign(self, { | |
| "~standard": { | |
| version: 1, | |
| vendor: "effect", | |
| validate | |
| } as const | |
| }) | |
| } | |
| } | |
| function toBaseStandardJSONSchemaV1(self: Top, target: StandardJSONSchemaV1.Target): JsonSchema.JsonSchema { | |
| const doc2020_12 = toJsonSchemaDocument(self) | |
| if (target === "draft-2020-12") { | |
| const schema = doc2020_12.schema | |
| if (Object.keys(doc2020_12.definitions).length > 0) { | |
| schema.$defs = doc2020_12.definitions | |
| } | |
| return schema | |
| } else if (target === "draft-07") { | |
| const doc07 = JsonSchema.toDocumentDraft07(doc2020_12) | |
| const schema = doc07.schema | |
| if (Object.keys(doc07.definitions).length > 0) { | |
| schema.definitions = doc07.definitions | |
| } | |
| return schema | |
| } | |
| throw new globalThis.Error(`Unsupported target: ${target}`) | |
| } | |
| /** | |
| * Converts a schema to an experimental Standard JSON Schema V1 representation. | |
| * | |
| * **Details** | |
| * | |
| * https://github.com/standard-schema/standard-schema/pull/134 | |
| * | |
| * @category Standard Schema | |
| * @since 4.0.0 | |
| */ | |
| export function toStandardJSONSchemaV1<S extends Top>(self: S): StandardJSONSchemaV1<S["Encoded"], S["Type"]> & S { | |
| const jsonSchema: StandardJSONSchemaV1.Props<S["Encoded"], S["Type"]>["jsonSchema"] = { | |
| input(options) { | |
| return toBaseStandardJSONSchemaV1(self, options.target) | |
| }, | |
| output(options) { | |
| return toBaseStandardJSONSchemaV1(toType(self), options.target) | |
| } | |
| } | |
| if ("~standard" in self) { | |
| const out = self as any | |
| if ("jsonSchema" in out["~standard"]) return out | |
| Object.assign(out["~standard"], { jsonSchema }) | |
| return out | |
| } else { | |
| return Object.assign(self, { | |
| "~standard": { | |
| version: 1, | |
| vendor: "effect", | |
| jsonSchema | |
| } as const | |
| }) | |
| } | |
| } | |
| /** | |
| * Creates a type guard function that checks if a value conforms to a given | |
| * schema. | |
| * | |
| * **Details** | |
| * | |
| * This function returns a predicate that performs a type-safe check, narrowing | |
| * the type of the input value if the check passes. It's particularly useful for | |
| * runtime type validation and TypeScript type narrowing. | |
| * | |
| * **Example** (Basic Type Guard) | |
| * | |
| * ```ts | |
| * import { Schema } from "effect" | |
| * | |
| * const isString = Schema.is(Schema.String) | |
| * | |
| * console.log(isString("hello")) // true | |
| * console.log(isString(42)) // false | |
| * | |
| * // Type narrowing in action | |
| * const value: unknown = "hello" | |
| * if (isString(value)) { | |
| * // value is now typed as string | |
| * console.log(value.toUpperCase()) // "HELLO" | |
| * } | |
| * ``` | |
| * | |
| * @category guards | |
| * @since 3.10.0 | |
| */ | |
| export const is = SchemaParser.is | |
| /** | |
| * Creates an assertion function that throws an error if the input doesn't match | |
| * the schema. | |
| * | |
| * **When to use** | |
| * | |
| * Use to validate unknown input at runtime while narrowing the value with a | |
| * TypeScript assertion signature. | |
| * | |
| * **Details** | |
| * | |
| * The input is narrowed if the assertion succeeds. If validation fails, the | |
| * assertion throws. | |
| * | |
| * **Example** (Basic Usage) | |
| * | |
| * ```ts | |
| * import { Schema } from "effect" | |
| * | |
| * const input: unknown = "hello" | |
| * | |
| * // This will pass silently (no return value) and narrow input to string | |
| * Schema.asserts(Schema.String, input) | |
| * console.log(input.toUpperCase()) | |
| * | |
| * // This will throw an error | |
| * try { | |
| * const invalid: unknown = 123 | |
| * Schema.asserts(Schema.String, invalid) | |
| * } catch (error) { | |
| * console.log("Non-string assertion failed as expected") | |
| * } | |
| * ``` | |
| * | |
| * @category guards | |
| * @since 4.0.0 | |
| */ | |
| export const asserts: <S extends Top, I>(schema: S, input: I) => asserts input is I & S["Type"] = SchemaParser.asserts | |
| /** | |
| * Decodes an `unknown` input against a schema, returning an `Effect` that | |
| * succeeds with the decoded value or fails with a {@link SchemaError}. | |
| * | |
| * **When to use** | |
| * | |
| * Use when you need to decode unknown input in an `Effect` whose failure | |
| * channel is `SchemaError`. | |
| * | |
| * **Details** | |
| * | |
| * Prefer {@link decodeEffect} when the input is already typed as the schema's | |
| * `Encoded` type. | |
| * Options may be provided either when creating the decoder or when applying it; | |
| * application options override creation options. | |
| * | |
| * @see {@link SchemaParser.decodeUnknownEffect} for the adapter that fails with `SchemaIssue.Issue` directly | |
| * | |
| * @category decoding | |
| * @since 4.0.0 | |
| */ | |
| export function decodeUnknownEffect<S extends Top>(schema: S, options?: SchemaAST.ParseOptions) { | |
| const parser = SchemaParser.decodeUnknownEffect(schema, options) | |
| return ( | |
| input: unknown, | |
| options?: SchemaAST.ParseOptions | |
| ): Effect.Effect<S["Type"], SchemaError, S["DecodingServices"]> => { | |
| return Effect.mapErrorEager(parser(input, options), (issue) => new SchemaError(issue)) | |
| } | |
| } | |
| /** | |
| * Decodes a typed input (the schema's `Encoded` type) against a schema, | |
| * returning an `Effect` that succeeds with the decoded value or fails with a | |
| * {@link SchemaError}. | |
| * | |
| * **When to use** | |
| * | |
| * Use when you need to decode input already typed as the schema's `Encoded` | |
| * type in an `Effect` whose failure channel is `SchemaError`. | |
| * | |
| * **Details** | |
| * | |
| * For `unknown` input use {@link decodeUnknownEffect}. | |
| * Options may be provided either when creating the decoder or when applying it; | |
| * application options override creation options. | |
| * | |
| * @see {@link SchemaParser.decodeEffect} for the adapter that fails with `SchemaIssue.Issue` directly | |
| * | |
| * @category decoding | |
| * @since 4.0.0 | |
| */ | |
| export const decodeEffect: <S extends Top>( | |
| schema: S, | |
| options?: SchemaAST.ParseOptions | |
| ) => ( | |
| input: S["Encoded"], | |
| options?: SchemaAST.ParseOptions | |
| ) => Effect.Effect<S["Type"], SchemaError, S["DecodingServices"]> = decodeUnknownEffect | |
| /** | |
| * Decodes an `unknown` input against a schema synchronously, returning an | |
| * `Exit` that is either a `Success` with the decoded value or a `Failure` with | |
| * a {@link SchemaError}. | |
| * | |
| * **When to use** | |
| * | |
| * Use when you need to decode unknown input into an `Exit` whose failure | |
| * contains `SchemaError`. | |
| * | |
| * **Details** | |
| * | |
| * Only usable with schemas that have no `DecodingServices` requirement. Prefer | |
| * {@link decodeExit} when the input is already typed as the schema's `Encoded` | |
| * type. | |
| * Options may be provided either when creating the decoder or when applying it; | |
| * application options override creation options. | |
| * | |
| * @see {@link SchemaParser.decodeUnknownExit} for the adapter whose failure contains `SchemaIssue.Issue` directly | |
| * | |
| * @category decoding | |
| * @since 4.0.0 | |
| */ | |
| export function decodeUnknownExit<S extends Decoder<unknown>>(schema: S, options?: SchemaAST.ParseOptions) { | |
| const parser = SchemaParser.decodeUnknownExit(schema, options) | |
| return (input: unknown, options?: SchemaAST.ParseOptions): Exit_.Exit<S["Type"], SchemaError> => { | |
| return Exit_.mapError(parser(input, options), (issue) => new SchemaError(issue)) | |
| } | |
| } | |
| /** | |
| * Decodes a typed input (the schema's `Encoded` type) against a schema | |
| * synchronously, returning an `Exit` that is either a `Success` with the | |
| * decoded value or a `Failure` with a {@link SchemaError}. | |
| * | |
| * **When to use** | |
| * | |
| * Use when you need to decode already typed `Encoded` input into an `Exit` | |
| * whose failure contains `SchemaError`. | |
| * | |
| * **Details** | |
| * | |
| * Only usable with schemas that have no `DecodingServices` requirement. For | |
| * `unknown` input use {@link decodeUnknownExit}. | |
| * Options may be provided either when creating the decoder or when applying it; | |
| * application options override creation options. | |
| * | |
| * @see {@link SchemaParser.decodeExit} for the adapter whose failure contains `SchemaIssue.Issue` directly | |
| * | |
| * @category decoding | |
| * @since 4.0.0 | |
| */ | |
| export const decodeExit: <S extends Decoder<unknown>>( | |
| schema: S, | |
| options?: SchemaAST.ParseOptions | |
| ) => (input: S["Encoded"], options?: SchemaAST.ParseOptions) => Exit_.Exit<S["Type"], SchemaError> = decodeUnknownExit | |
| /** | |
| * Decodes an `unknown` input against a schema, returning an `Option` that is | |
| * `Some` with the decoded value on success or `None` on failure. | |
| * | |
| * **When to use** | |
| * | |
| * Use when you do not know the input type statically and only need to know | |
| * whether decoding succeeded. | |
| * | |
| * **Details** | |
| * | |
| * Prefer this over {@link decodeUnknownExit} or {@link decodeUnknownEffect} | |
| * when you don't need error details. For input already typed as the schema's | |
| * `Encoded` type use {@link decodeOption}. | |
| * Options may be provided either when creating the decoder or when applying it; | |
| * application options override creation options. | |
| * | |
| * @category decoding | |
| * @since 3.10.0 | |
| */ | |
| export const decodeUnknownOption: <S extends Decoder<unknown>>( | |
| schema: S, | |
| options?: SchemaAST.ParseOptions | |
| ) => (input: unknown, options?: SchemaAST.ParseOptions) => Option_.Option<S["Type"]> = SchemaParser.decodeUnknownOption | |
| /** | |
| * Decodes a typed input (the schema's `Encoded` type) against a schema, | |
| * returning an `Option` that is `Some` with the decoded value on success or | |
| * `None` on failure. | |
| * | |
| * **When to use** | |
| * | |
| * Use when you already have input typed as the schema's `Encoded` type and | |
| * only need to know whether decoding succeeded. | |
| * | |
| * **Details** | |
| * | |
| * For `unknown` input use {@link decodeUnknownOption}. | |
| * Options may be provided either when creating the decoder or when applying it; | |
| * application options override creation options. | |
| * | |
| * @category decoding | |
| * @since 3.10.0 | |
| */ | |
| export const decodeOption: <S extends Decoder<unknown>>( | |
| schema: S, | |
| options?: SchemaAST.ParseOptions | |
| ) => (input: S["Encoded"], options?: SchemaAST.ParseOptions) => Option_.Option<S["Type"]> = decodeUnknownOption | |
| /** | |
| * Decodes an `unknown` input against a schema, returning a `Result` that | |
| * succeeds with the decoded value or fails with a {@link SchemaError}. | |
| * | |
| * **When to use** | |
| * | |
| * Use when you do not know the input type statically and want decoding to | |
| * return a `Result` with `SchemaError` failure data. | |
| * | |
| * **Details** | |
| * | |
| * For input already typed as the schema's `Encoded` type use | |
| * {@link decodeResult}. | |
| * Options may be provided either when creating the decoder or when applying it; | |
| * application options override creation options. | |
| * | |
| * @see {@link SchemaParser.decodeUnknownResult} for the adapter that fails with `SchemaIssue.Issue` directly | |
| * | |
| * @category decoding | |
| * @since 4.0.0 | |
| */ | |
| export function decodeUnknownResult<S extends Decoder<unknown>>(schema: S, options?: SchemaAST.ParseOptions) { | |
| const parser = SchemaParser.decodeUnknownResult(schema, options) | |
| return (input: unknown, options?: SchemaAST.ParseOptions): Result_.Result<S["Type"], SchemaError> => { | |
| return Result_.mapError(parser(input, options), (issue) => new SchemaError(issue)) | |
| } | |
| } | |
| /** | |
| * Decodes a typed input (the schema's `Encoded` type) against a schema, | |
| * returning a `Result` that succeeds with the decoded value or fails with a | |
| * {@link SchemaError}. | |
| * | |
| * **When to use** | |
| * | |
| * Use when you already have input typed as the schema's `Encoded` type and | |
| * want decoding to return a `Result` with `SchemaError` failure data. | |
| * | |
| * **Details** | |
| * | |
| * For `unknown` input use {@link decodeUnknownResult}. | |
| * Options may be provided either when creating the decoder or when applying it; | |
| * application options override creation options. | |
| * | |
| * @see {@link SchemaParser.decodeResult} for the adapter that fails with `SchemaIssue.Issue` directly | |
| * | |
| * @category decoding | |
| * @since 4.0.0 | |
| */ | |
| export const decodeResult: <S extends Decoder<unknown>>( | |
| schema: S, | |
| options?: SchemaAST.ParseOptions | |
| ) => (input: S["Encoded"], options?: SchemaAST.ParseOptions) => Result_.Result<S["Type"], SchemaError> = | |
| decodeUnknownResult | |
| /** | |
| * Decodes an `unknown` input against a schema, returning a `Promise` that | |
| * resolves with the decoded value or rejects with a {@link SchemaError}. | |
| * | |
| * **When to use** | |
| * | |
| * Use when you need decoding of unknown input to return a JavaScript `Promise` | |
| * that rejects with `SchemaError`. | |
| * | |
| * **Details** | |
| * | |
| * For input already typed as the schema's `Encoded` type use | |
| * {@link decodePromise}. | |
| * Options may be provided either when creating the decoder or when applying it; | |
| * application options override creation options. | |
| * | |
| * @see {@link SchemaParser.decodeUnknownPromise} for the adapter that rejects with `SchemaIssue.Issue` directly | |
| * | |
| * @category decoding | |
| * @since 3.10.0 | |
| */ | |
| export function decodeUnknownPromise<S extends Decoder<unknown>>(schema: S, options?: SchemaAST.ParseOptions) { | |
| const parser = decodeUnknownEffect(schema, options) | |
| return (input: unknown, options?: SchemaAST.ParseOptions): Promise<S["Type"]> => { | |
| return Effect.runPromise(parser(input, options)) | |
| } | |
| } | |
| /** | |
| * Decodes a typed input (the schema's `Encoded` type) against a schema, | |
| * returning a `Promise` that resolves with the decoded value or rejects with a | |
| * {@link SchemaError}. | |
| * | |
| * **When to use** | |
| * | |
| * Use when you already have input typed as the schema's `Encoded` type and | |
| * need decoding to return a JavaScript `Promise` that rejects with | |
| * `SchemaError`. | |
| * | |
| * **Details** | |
| * | |
| * For `unknown` input use `decodeUnknownPromise`. | |
| * Options may be provided either when creating the decoder or when applying it; | |
| * application options override creation options. | |
| * | |
| * @see {@link SchemaParser.decodePromise} for the adapter that rejects with `SchemaIssue.Issue` directly | |
| * | |
| * @category decoding | |
| * @since 3.10.0 | |
| */ | |
| export const decodePromise: <S extends Decoder<unknown>>( | |
| schema: S, | |
| options?: SchemaAST.ParseOptions | |
| ) => (input: S["Encoded"], options?: SchemaAST.ParseOptions) => Promise<S["Type"]> = decodeUnknownPromise | |
| /** | |
| * Decodes an `unknown` input against a schema synchronously, returning the | |
| * decoded value or throwing a {@link SchemaError}. | |
| * | |
| * **When to use** | |
| * | |
| * Use when you need to validate unknown data at a synchronous boundary and want | |
| * schema mismatches to throw `SchemaError`. | |
| * | |
| * **Details** | |
| * | |
| * For input already typed as the schema's `Encoded` type use `decodeSync`. | |
| * Only service-free schemas can be decoded synchronously. For non-throwing | |
| * alternatives see `decodeUnknownOption`, `decodeUnknownExit`, or | |
| * `decodeUnknownEffect`. Options may be provided either when creating the | |
| * decoder or when applying it; application options override creation options. | |
| * | |
| * **Example** (Decoding with a transformation schema) | |
| * | |
| * ```ts | |
| * import { Schema } from "effect" | |
| * | |
| * const NumberFromString = Schema.NumberFromString | |
| * | |
| * console.log(Schema.decodeUnknownSync(NumberFromString)("42")) | |
| * // Output: 42 | |
| * | |
| * Schema.decodeUnknownSync(NumberFromString)("not a number") | |
| * // throws SchemaError: NumberFromString | |
| * // └─ Encoded side transformation failure | |
| * // └─ NumberFromString | |
| * // └─ Expected a numeric string, actual "not a number" | |
| * ``` | |
| * | |
| * @see {@link SchemaParser.decodeUnknownSync} for the adapter that throws an `Error` whose cause is `SchemaIssue.Issue` | |
| * | |
| * @category decoding | |
| * @since 4.0.0 | |
| */ | |
| export function decodeUnknownSync<S extends Decoder<unknown>>(schema: S, options?: SchemaAST.ParseOptions) { | |
| const parser = decodeUnknownEffect(schema, options) | |
| return (input: unknown, options?: SchemaAST.ParseOptions): S["Type"] => { | |
| return Effect.runSync(parser(input, options) as Effect.Effect<S["Type"], SchemaError>) | |
| } | |
| } | |
| /** | |
| * Decodes a typed input (the schema's `Encoded` type) against a schema | |
| * synchronously, returning the decoded value or throwing a {@link SchemaError}. | |
| * | |
| * **When to use** | |
| * | |
| * Use when you already have input typed as the schema's `Encoded` type and | |
| * want schema mismatches to throw `SchemaError` synchronously. | |
| * | |
| * **Details** | |
| * | |
| * For `unknown` input use `decodeUnknownSync`. | |
| * Only service-free schemas can be decoded synchronously. Options may be | |
| * provided either when creating the decoder or when applying it; application | |
| * options override creation options. | |
| * | |
| * @see {@link SchemaParser.decodeSync} for the adapter that throws an `Error` whose cause is `SchemaIssue.Issue` | |
| * | |
| * @category decoding | |
| * @since 4.0.0 | |
| */ | |
| export const decodeSync: <S extends Decoder<unknown>>( | |
| schema: S, | |
| options?: SchemaAST.ParseOptions | |
| ) => (input: S["Encoded"], options?: SchemaAST.ParseOptions) => S["Type"] = decodeUnknownSync | |
| /** | |
| * Encodes an `unknown` input against a schema, returning an `Effect` that | |
| * succeeds with the encoded value or fails with a {@link SchemaError}. | |
| * | |
| * **When to use** | |
| * | |
| * Use when you need to encode unknown input in an `Effect` whose failure | |
| * channel is `SchemaError`. | |
| * | |
| * **Details** | |
| * | |
| * Prefer {@link encodeEffect} when the value is already typed as the schema's | |
| * `Type`. | |
| * Options may be provided either when creating the encoder or when applying it; | |
| * application options override creation options. | |
| * | |
| * **Example** (Encoding a value to a string) | |
| * | |
| * ```ts | |
| * import { Effect, Schema } from "effect" | |
| * | |
| * const NumberFromString = Schema.NumberFromString | |
| * | |
| * Effect.runPromise(Schema.encodeUnknownEffect(NumberFromString)(42)).then(console.log) | |
| * // Output: "42" | |
| * ``` | |
| * | |
| * @see {@link SchemaParser.encodeUnknownEffect} for the adapter that fails with `SchemaIssue.Issue` directly | |
| * | |
| * @category encoding | |
| * @since 4.0.0 | |
| */ | |
| export function encodeUnknownEffect<S extends Top>(schema: S, options?: SchemaAST.ParseOptions) { | |
| const parser = SchemaParser.encodeUnknownEffect(schema, options) | |
| return ( | |
| input: unknown, | |
| options?: SchemaAST.ParseOptions | |
| ): Effect.Effect<S["Encoded"], SchemaError, S["EncodingServices"]> => { | |
| return Effect.mapErrorEager(parser(input, options), (issue) => new SchemaError(issue)) | |
| } | |
| } | |
| /** | |
| * Encodes a typed input (the schema's `Type`) against a schema, returning an | |
| * `Effect` that succeeds with the encoded value or fails with a | |
| * {@link SchemaError}. | |
| * | |
| * **When to use** | |
| * | |
| * Use when you need to encode input already typed as the schema's `Type` in | |
| * an `Effect` whose failure channel is `SchemaError`. | |
| * | |
| * **Details** | |
| * | |
| * For `unknown` input use {@link encodeUnknownEffect}. | |
| * Options may be provided either when creating the encoder or when applying it; | |
| * application options override creation options. | |
| * | |
| * @see {@link SchemaParser.encodeEffect} for the adapter that fails with `SchemaIssue.Issue` directly | |
| * | |
| * @category encoding | |
| * @since 4.0.0 | |
| */ | |
| export const encodeEffect: <S extends Top>( | |
| schema: S, | |
| options?: SchemaAST.ParseOptions | |
| ) => ( | |
| input: S["Type"], | |
| options?: SchemaAST.ParseOptions | |
| ) => Effect.Effect<S["Encoded"], SchemaError, S["EncodingServices"]> = encodeUnknownEffect | |
| /** | |
| * Encodes an `unknown` input against a schema synchronously, returning an | |
| * `Exit` that is either a `Success` with the encoded value or a `Failure` with | |
| * a {@link SchemaError}. | |
| * | |
| * **When to use** | |
| * | |
| * Use when you need to encode unknown input into an `Exit` whose failure | |
| * contains `SchemaError`. | |
| * | |
| * **Details** | |
| * | |
| * Only usable with schemas that have no `EncodingServices` requirement. Prefer | |
| * {@link encodeExit} when the value is already typed as the schema's `Type`. | |
| * Options may be provided either when creating the encoder or when applying it; | |
| * application options override creation options. | |
| * | |
| * @see {@link SchemaParser.encodeUnknownExit} for the adapter whose failure contains `SchemaIssue.Issue` directly | |
| * | |
| * @category encoding | |
| * @since 4.0.0 | |
| */ | |
| export function encodeUnknownExit<S extends Encoder<unknown>>(schema: S, options?: SchemaAST.ParseOptions) { | |
| const parser = SchemaParser.encodeUnknownExit(schema, options) | |
| return (input: unknown, options?: SchemaAST.ParseOptions): Exit_.Exit<S["Encoded"], SchemaError> => { | |
| return Exit_.mapError(parser(input, options), (issue) => new SchemaError(issue)) | |
| } | |
| } | |
| /** | |
| * Encodes a typed input (the schema's `Type`) against a schema synchronously, | |
| * returning an `Exit` that is either a `Success` with the encoded value or a | |
| * `Failure` with a {@link SchemaError}. | |
| * | |
| * **When to use** | |
| * | |
| * Use when you need to encode already typed schema values into an `Exit` whose | |
| * failure contains `SchemaError`. | |
| * | |
| * **Details** | |
| * | |
| * Only usable with schemas that have no `EncodingServices` requirement. For | |
| * `unknown` input use {@link encodeUnknownExit}. | |
| * Options may be provided either when creating the encoder or when applying it; | |
| * application options override creation options. | |
| * | |
| * @see {@link SchemaParser.encodeExit} for the adapter whose failure contains `SchemaIssue.Issue` directly | |
| * | |
| * @category encoding | |
| * @since 4.0.0 | |
| */ | |
| export const encodeExit: <S extends Encoder<unknown>>( | |
| schema: S, | |
| options?: SchemaAST.ParseOptions | |
| ) => (input: S["Type"], options?: SchemaAST.ParseOptions) => Exit_.Exit<S["Encoded"], SchemaError> = encodeUnknownExit | |
| /** | |
| * Encodes an `unknown` input against a schema, returning an `Option` that is | |
| * `Some` with the encoded value on success or `None` on failure. | |
| * | |
| * **When to use** | |
| * | |
| * Use when you do not know the input type statically and only need to know | |
| * whether encoding succeeded. | |
| * | |
| * **Details** | |
| * | |
| * Prefer this over {@link encodeUnknownExit} or {@link encodeUnknownEffect} | |
| * when you don't need error details. For values already typed as the schema's | |
| * `Type` use {@link encodeOption}. | |
| * Options may be provided either when creating the encoder or when applying it; | |
| * application options override creation options. | |
| * | |
| * @category encoding | |
| * @since 3.10.0 | |
| */ | |
| export const encodeUnknownOption: <S extends Encoder<unknown>>( | |
| schema: S, | |
| options?: SchemaAST.ParseOptions | |
| ) => (input: unknown, options?: SchemaAST.ParseOptions) => Option_.Option<S["Encoded"]> = | |
| SchemaParser.encodeUnknownOption | |
| /** | |
| * Encodes a typed input (the schema's `Type`) against a schema, returning an | |
| * `Option` that is `Some` with the encoded value on success or `None` on | |
| * failure. | |
| * | |
| * **When to use** | |
| * | |
| * Use when you already have a value typed as the schema's `Type` and only need | |
| * to know whether encoding succeeded. | |
| * | |
| * **Details** | |
| * | |
| * For `unknown` input use {@link encodeUnknownOption}. | |
| * Options may be provided either when creating the encoder or when applying it; | |
| * application options override creation options. | |
| * | |
| * @category encoding | |
| * @since 3.10.0 | |
| */ | |
| export const encodeOption: <S extends Encoder<unknown>>( | |
| schema: S, | |
| options?: SchemaAST.ParseOptions | |
| ) => (input: S["Type"], options?: SchemaAST.ParseOptions) => Option_.Option<S["Encoded"]> = encodeUnknownOption | |
| /** | |
| * Encodes an `unknown` input against a schema, returning a `Result` that | |
| * succeeds with the encoded value or fails with a {@link SchemaError}. | |
| * | |
| * **When to use** | |
| * | |
| * Use when you do not know the input type statically and want encoding to | |
| * return a `Result` with `SchemaError` failure data. | |
| * | |
| * **Details** | |
| * | |
| * For values already typed as the schema's `Type` use {@link encodeResult}. | |
| * Options may be provided either when creating the encoder or when applying it; | |
| * application options override creation options. | |
| * | |
| * @see {@link SchemaParser.encodeUnknownResult} for the adapter that fails with `SchemaIssue.Issue` directly | |
| * | |
| * @category encoding | |
| * @since 4.0.0 | |
| */ | |
| export function encodeUnknownResult<S extends Encoder<unknown>>(schema: S, options?: SchemaAST.ParseOptions) { | |
| const parser = SchemaParser.encodeUnknownResult(schema, options) | |
| return (input: unknown, options?: SchemaAST.ParseOptions): Result_.Result<S["Encoded"], SchemaError> => { | |
| return Result_.mapError(parser(input, options), (issue) => new SchemaError(issue)) | |
| } | |
| } | |
| /** | |
| * Encodes a typed input (the schema's `Type`) against a schema, returning a | |
| * `Result` that succeeds with the encoded value or fails with a | |
| * {@link SchemaError}. | |
| * | |
| * **When to use** | |
| * | |
| * Use when you already have a value typed as the schema's `Type` and want | |
| * encoding to return a `Result` with `SchemaError` failure data. | |
| * | |
| * **Details** | |
| * | |
| * For `unknown` input use {@link encodeUnknownResult}. | |
| * Options may be provided either when creating the encoder or when applying it; | |
| * application options override creation options. | |
| * | |
| * @see {@link SchemaParser.encodeResult} for the adapter that fails with `SchemaIssue.Issue` directly | |
| * | |
| * @category encoding | |
| * @since 4.0.0 | |
| */ | |
| export const encodeResult: <S extends Encoder<unknown>>( | |
| schema: S, | |
| options?: SchemaAST.ParseOptions | |
| ) => (input: S["Type"], options?: SchemaAST.ParseOptions) => Result_.Result<S["Encoded"], SchemaError> = | |
| encodeUnknownResult | |
| /** | |
| * Encodes an `unknown` input against a schema, returning a `Promise` that | |
| * resolves with the encoded value or rejects with a {@link SchemaError}. | |
| * | |
| * **When to use** | |
| * | |
| * Use when you need encoding of unknown input to return a JavaScript `Promise` | |
| * that rejects with `SchemaError`. | |
| * | |
| * **Details** | |
| * | |
| * For values already typed as the schema's `Type` use {@link encodePromise}. | |
| * Options may be provided either when creating the encoder or when applying it; | |
| * application options override creation options. | |
| * | |
| * @see {@link SchemaParser.encodeUnknownPromise} for the adapter that rejects with `SchemaIssue.Issue` directly | |
| * | |
| * @category encoding | |
| * @since 3.10.0 | |
| */ | |
| export function encodeUnknownPromise<S extends Encoder<unknown>>(schema: S, options?: SchemaAST.ParseOptions) { | |
| const parser = encodeUnknownEffect(schema, options) | |
| return (input: unknown, options?: SchemaAST.ParseOptions): Promise<S["Encoded"]> => { | |
| return Effect.runPromise(parser(input, options)) | |
| } | |
| } | |
| /** | |
| * Encodes a typed input (the schema's `Type`) against a schema, returning a | |
| * `Promise` that resolves with the encoded value or rejects with a | |
| * {@link SchemaError}. | |
| * | |
| * **When to use** | |
| * | |
| * Use when you already have a value typed as the schema's `Type` and need | |
| * encoding to return a JavaScript `Promise` that rejects with `SchemaError`. | |
| * | |
| * **Details** | |
| * | |
| * For `unknown` input use {@link encodeUnknownPromise}. | |
| * Options may be provided either when creating the encoder or when applying it; | |
| * application options override creation options. | |
| * | |
| * @see {@link SchemaParser.encodePromise} for the adapter that rejects with `SchemaIssue.Issue` directly | |
| * | |
| * @category encoding | |
| * @since 3.10.0 | |
| */ | |
| export const encodePromise: <S extends Encoder<unknown>>( | |
| schema: S, | |
| options?: SchemaAST.ParseOptions | |
| ) => (input: S["Type"], options?: SchemaAST.ParseOptions) => Promise<S["Encoded"]> = encodeUnknownPromise | |
| /** | |
| * Encodes an `unknown` input against a schema synchronously, throwing a | |
| * {@link SchemaError} on failure. | |
| * | |
| * **When to use** | |
| * | |
| * Use when you need to serialize unknown data at a synchronous boundary and | |
| * want schema mismatches to throw `SchemaError`. | |
| * | |
| * **Details** | |
| * | |
| * For non-throwing alternatives see {@link encodeUnknownOption}, | |
| * {@link encodeUnknownExit}, or {@link encodeUnknownEffect}. For values | |
| * already typed as the schema's `Type` use {@link encodeSync}. | |
| * Options may be provided either when creating the encoder or when applying it; | |
| * application options override creation options. | |
| * | |
| * @see {@link SchemaParser.encodeUnknownSync} for the adapter that throws an `Error` whose cause is `SchemaIssue.Issue` | |
| * | |
| * @category encoding | |
| * @since 4.0.0 | |
| */ | |
| export function encodeUnknownSync<S extends Encoder<unknown>>(schema: S, options?: SchemaAST.ParseOptions) { | |
| const parser = encodeUnknownEffect(schema, options) | |
| return (input: unknown, options?: SchemaAST.ParseOptions): S["Encoded"] => { | |
| return Effect.runSync(parser(input, options) as Effect.Effect<S["Encoded"], SchemaError>) | |
| } | |
| } | |
| /** | |
| * Encodes a typed input (the schema's `Type`) against a schema synchronously, | |
| * throwing a {@link SchemaError} on failure. | |
| * | |
| * **When to use** | |
| * | |
| * Use when you already have a value typed as the schema's `Type` and want | |
| * schema mismatches to throw `SchemaError` synchronously. | |
| * | |
| * **Details** | |
| * | |
| * For `unknown` input use {@link encodeUnknownSync}. | |
| * Options may be provided either when creating the encoder or when applying it; | |
| * application options override creation options. | |
| * | |
| * @see {@link SchemaParser.encodeSync} for the adapter that throws an `Error` whose cause is `SchemaIssue.Issue` | |
| * | |
| * @category encoding | |
| * @since 4.0.0 | |
| */ | |
| export const encodeSync: <S extends Encoder<unknown>>( | |
| schema: S, | |
| options?: SchemaAST.ParseOptions | |
| ) => (input: S["Type"], options?: SchemaAST.ParseOptions) => S["Encoded"] = encodeUnknownSync | |
| /** | |
| * Creates a schema from an AST (Abstract Syntax Tree) node. | |
| * | |
| * **Details** | |
| * | |
| * This is the fundamental constructor for all schemas in the Effect Schema | |
| * library. It takes an AST node and wraps it in a fully-typed schema that | |
| * preserves all type information and provides the complete schema API. | |
| * | |
| * The `make` function is used internally to create all primitive schemas like | |
| * `String`, `Number`, `Boolean`, etc., as well as more complex schemas. It's | |
| * the bridge between the untyped AST representation and the strongly-typed | |
| * schema. | |
| * | |
| * @category constructors | |
| * @since 3.10.0 | |
| */ | |
| export const make: <S extends Top>(ast: S["ast"], options?: object) => S = InternalSchema.make | |
| /** | |
| * Transforms a schema into a class that can be extended with `extends`. The | |
| * resulting class inherits the full schema API (e.g. `annotate`) and can define | |
| * static methods that reference `this`. | |
| * | |
| * **Example** (Wrapping a primitive schema) | |
| * | |
| * ```ts | |
| * import { Schema } from "effect" | |
| * | |
| * class MyString extends Schema.asClass(Schema.String) { | |
| * static readonly decodeUnknownSync = Schema.decodeUnknownSync(this) | |
| * } | |
| * | |
| * console.log(MyString.decodeUnknownSync("a")) | |
| * // "a" | |
| * ``` | |
| * | |
| * @category constructors | |
| * @since 4.0.0 | |
| */ | |
| export function asClass<S extends Top>(schema: S): S & { new(_: never): {} } { | |
| // oxlint-disable-next-line @typescript-eslint/no-extraneous-class | |
| class Class {} | |
| return Object.setPrototypeOf(Class, schema) | |
| } | |
| /** | |
| * Checks whether a value is a `Schema`. | |
| * | |
| * @category guards | |
| * @since 3.10.0 | |
| */ | |
| export function isSchema(u: unknown): u is Top { | |
| return Predicate.hasProperty(u, TypeId) && u[TypeId] === TypeId | |
| } | |
| /** | |
| * Type-level representation returned by {@link optionalKey}. | |
| * | |
| * @category models | |
| * @since 4.0.0 | |
| */ | |
| export interface optionalKey<S extends Top> extends | |
| Bottom< | |
| S["Type"], | |
| S["Encoded"], | |
| S["DecodingServices"], | |
| S["EncodingServices"], | |
| S["ast"], | |
| optionalKey<S>, | |
| S["~type.make.in"], | |
| S["Iso"], | |
| S["~type.parameters"], | |
| S["~type.make"], | |
| S["~type.mutability"], | |
| "optional", | |
| S["~type.constructor.default"], | |
| S["~encoded.mutability"], | |
| "optional" | |
| > | |
| { | |
| readonly schema: S | |
| } | |
| interface optionalKeyLambda extends Lambda { | |
| <S extends Top>(self: S): optionalKey<S> | |
| readonly "~lambda.out": this["~lambda.in"] extends Top ? optionalKey<this["~lambda.in"]> : never | |
| } | |
| /** | |
| * Creates an exact optional key schema for struct fields. Unlike `optional`, | |
| * this creates exact optional properties (not `| undefined`) that can be | |
| * completely omitted from the object. | |
| * | |
| * **Example** (Creating a struct with optional key) | |
| * | |
| * ```ts | |
| * import { Schema } from "effect" | |
| * | |
| * const schema = Schema.Struct({ | |
| * name: Schema.String, | |
| * age: Schema.optionalKey(Schema.Number) | |
| * }) | |
| * | |
| * // Type: { readonly name: string; readonly age?: number } | |
| * type Person = typeof schema["Type"] | |
| * ``` | |
| * | |
| * @category combinators | |
| * @since 4.0.0 | |
| */ | |
| export const optionalKey = Struct_.lambda<optionalKeyLambda>((schema) => | |
| make(SchemaAST.optionalKey(schema.ast), { schema }) | |
| ) | |
| interface requiredKeyLambda extends Lambda { | |
| <S extends Top>(self: optionalKey<S>): S | |
| readonly "~lambda.out": this["~lambda.in"] extends optionalKey<Top> ? this["~lambda.in"]["schema"] | |
| : "Error: schema not eligible for requiredKey" | |
| } | |
| /** | |
| * Reverses `optionalKey` and returns the inner required schema. | |
| * | |
| * **When to use** | |
| * | |
| * Use to remove optional-key wrapping from a schema field that was previously | |
| * wrapped with {@link optionalKey}. | |
| * | |
| * @category combinators | |
| * @since 4.0.0 | |
| */ | |
| export const requiredKey = Struct_.lambda<requiredKeyLambda>((self) => self.schema) | |
| /** | |
| * Type-level representation returned by {@link optional}. | |
| * | |
| * @category models | |
| * @since 3.10.0 | |
| */ | |
| export interface optional<S extends Top> extends optionalKey<UndefinedOr<S>> { | |
| readonly "Rebuild": optional<S> | |
| } | |
| interface optionalLambda extends Lambda { | |
| <S extends Top>(self: S): optional<S> | |
| readonly "~lambda.out": this["~lambda.in"] extends Top ? optional<this["~lambda.in"]> : never | |
| } | |
| /** | |
| * Marks a struct field as optional, allowing the key to be absent or | |
| * `undefined`. | |
| * | |
| * **Details** | |
| * | |
| * The resulting property may be absent or explicitly set to `undefined`. | |
| * Equivalent to `optionalKey(UndefinedOr(S))`. | |
| * | |
| * Use {@link optionalKey} instead if you want exact optional semantics (absent | |
| * only, not `undefined`). | |
| * | |
| * **Example** (Optional field accepting undefined) | |
| * | |
| * ```ts | |
| * import { Schema } from "effect" | |
| * | |
| * const schema = Schema.Struct({ | |
| * name: Schema.String, | |
| * age: Schema.optional(Schema.Number) | |
| * }) | |
| * | |
| * // { readonly name: string; readonly age?: number | undefined } | |
| * type Person = typeof schema.Type | |
| * ``` | |
| * | |
| * @category combinators | |
| * @since 3.10.0 | |
| */ | |
| export const optional = Struct_.lambda<optionalLambda>((self) => optionalKey(UndefinedOr(self))) | |
| interface requiredLambda extends Lambda { | |
| <S extends Top>(self: optional<S>): S | |
| readonly "~lambda.out": this["~lambda.in"] extends optional<Top> ? this["~lambda.in"]["schema"]["members"][0] | |
| : "Error: schema not eligible for required" | |
| } | |
| /** | |
| * Reverses `optional` and returns the inner schema. | |
| * | |
| * **When to use** | |
| * | |
| * Use to remove optional wrapping from a schema field that was previously | |
| * wrapped with {@link optional}. | |
| * | |
| * **Details** | |
| * | |
| * This also unwraps the `UndefinedOr` member added by `optional`. | |
| * | |
| * @category combinators | |
| * @since 3.10.0 | |
| */ | |
| export const required = Struct_.lambda<requiredLambda>((self) => self.schema.members[0]) | |
| /** | |
| * Type-level representation returned by {@link mutableKey}. | |
| * | |
| * @category models | |
| * @since 4.0.0 | |
| */ | |
| export interface mutableKey<S extends Top> extends | |
| Bottom< | |
| S["Type"], | |
| S["Encoded"], | |
| S["DecodingServices"], | |
| S["EncodingServices"], | |
| S["ast"], | |
| mutableKey<S>, | |
| S["~type.make.in"], | |
| S["Iso"], | |
| S["~type.parameters"], | |
| S["~type.make"], | |
| "mutable", | |
| S["~type.optionality"], | |
| S["~type.constructor.default"], | |
| "mutable", | |
| S["~encoded.optionality"] | |
| > | |
| { | |
| readonly schema: S | |
| } | |
| interface mutableKeyLambda extends Lambda { | |
| <S extends Top>(self: S): mutableKey<S> | |
| readonly "~lambda.out": this["~lambda.in"] extends Top ? mutableKey<this["~lambda.in"]> : never | |
| } | |
| /** | |
| * Makes a struct field mutable (removes the `readonly` modifier on the property). | |
| * Use {@link readonlyKey} to reverse. | |
| * | |
| * @category combinators | |
| * @since 4.0.0 | |
| */ | |
| export const mutableKey = Struct_.lambda<mutableKeyLambda>((schema) => | |
| make(SchemaAST.mutableKey(schema.ast), { schema }) | |
| ) | |
| interface readonlyKeyLambda extends Lambda { | |
| <S extends Top>(self: mutableKey<S>): S | |
| readonly "~lambda.out": this["~lambda.in"] extends mutableKey<Top> ? this["~lambda.in"]["schema"] | |
| : "Error: schema not eligible for readonlyKey" | |
| } | |
| /** | |
| * Reverses `mutableKey` and returns the inner readonly schema. | |
| * | |
| * **When to use** | |
| * | |
| * Use to remove mutable-key wrapping from a schema field that was previously | |
| * wrapped with {@link mutableKey}. | |
| * | |
| * @category combinators | |
| * @since 4.0.0 | |
| */ | |
| export const readonlyKey = Struct_.lambda<readonlyKeyLambda>((self) => self.schema) | |
| /** | |
| * Type-level representation returned by {@link toType}. | |
| * | |
| * @category transforming | |
| * @since 4.0.0 | |
| */ | |
| export interface toType<S extends Top> extends | |
| Bottom< | |
| S["Type"], | |
| S["Type"], | |
| never, | |
| never, | |
| S["ast"], | |
| toType<S>, | |
| S["~type.make.in"], | |
| S["Iso"], | |
| S["~type.parameters"], | |
| S["~type.make"], | |
| S["~type.mutability"], | |
| S["~type.optionality"], | |
| S["~type.constructor.default"], | |
| S["~encoded.mutability"], | |
| S["~encoded.optionality"] | |
| > | |
| {} | |
| interface toTypeLambda extends Lambda { | |
| <S extends Top>(self: S): toType<S> | |
| readonly "~lambda.out": this["~lambda.in"] extends Top ? toType<this["~lambda.in"]> : never | |
| } | |
| /** | |
| * Extracts the type-side schema: sets `Encoded` to equal the decoded `Type`, | |
| * discarding the encoding transformation path. | |
| * | |
| * @category transforming | |
| * @since 4.0.0 | |
| */ | |
| export const toType = Struct_.lambda<toTypeLambda>((schema) => make(SchemaAST.toType(schema.ast), { schema })) | |
| /** | |
| * Type-level representation returned by {@link toEncoded}. | |
| * | |
| * @category transforming | |
| * @since 4.0.0 | |
| */ | |
| export interface toEncoded<S extends Top> extends | |
| Bottom< | |
| S["Encoded"], | |
| S["Encoded"], | |
| never, | |
| never, | |
| SchemaAST.AST, | |
| toEncoded<S>, | |
| S["Encoded"], | |
| S["Encoded"], | |
| ReadonlyArray<Top>, | |
| S["Encoded"], | |
| S["~type.mutability"], | |
| S["~type.optionality"], | |
| S["~type.constructor.default"], | |
| S["~encoded.mutability"], | |
| S["~encoded.optionality"] | |
| > | |
| {} | |
| interface toEncodedLambda extends Lambda { | |
| <S extends Top>(self: S): toEncoded<S> | |
| readonly "~lambda.out": this["~lambda.in"] extends Top ? toEncoded<this["~lambda.in"]> : never | |
| } | |
| /** | |
| * Extracts the encoded-side schema: sets `Type` to equal the `Encoded`, | |
| * discarding the decoding transformation path. | |
| * | |
| * @category transforming | |
| * @since 4.0.0 | |
| */ | |
| export const toEncoded = Struct_.lambda<toEncodedLambda>((schema) => make(SchemaAST.toEncoded(schema.ast), { schema })) | |
| const FlipTypeId = "~effect/Schema/flip" | |
| /** | |
| * Type-level representation returned by {@link flip}. | |
| * | |
| * @category transforming | |
| * @since 4.0.0 | |
| */ | |
| export interface flip<S extends Top> extends | |
| Bottom< | |
| S["Encoded"], | |
| S["Type"], | |
| S["EncodingServices"], | |
| S["DecodingServices"], | |
| SchemaAST.AST, | |
| flip<S>, | |
| S["Encoded"], | |
| S["Encoded"], | |
| ReadonlyArray<Top>, | |
| S["Encoded"], | |
| S["~encoded.mutability"], | |
| S["~encoded.optionality"], | |
| ConstructorDefault, | |
| S["~type.mutability"], | |
| S["~type.optionality"] | |
| > | |
| { | |
| readonly [FlipTypeId]: typeof FlipTypeId | |
| readonly schema: S | |
| } | |
| function isFlip$(schema: Top): schema is flip<any> { | |
| return Predicate.hasProperty(schema, FlipTypeId) && schema[FlipTypeId] === FlipTypeId | |
| } | |
| /** | |
| * Swaps the decoded and encoded sides of a schema. | |
| * | |
| * **When to use** | |
| * | |
| * Use to invert a schema transformation direction. | |
| * | |
| * **Details** | |
| * | |
| * Calling `flip` twice returns the original schema. | |
| * | |
| * **Example** (Flip a number-from-string schema) | |
| * | |
| * ```ts | |
| * import { Schema } from "effect" | |
| * | |
| * // NumberFromString: decodes string → number | |
| * const flipped = Schema.flip(Schema.NumberFromString) | |
| * // flipped: decodes number → string | |
| * ``` | |
| * | |
| * @category transforming | |
| * @since 4.0.0 | |
| */ | |
| export function flip<S extends Top>(schema: S): S extends flip<infer F> ? F["Rebuild"] : flip<S> | |
| export function flip<S extends Top>(schema: S): flip<S> { | |
| if (isFlip$(schema)) { | |
| return schema.schema.rebuild(SchemaAST.flip(schema.ast)) | |
| } | |
| return make(SchemaAST.flip(schema.ast), { [FlipTypeId]: FlipTypeId, schema }) | |
| } | |
| /** | |
| * Type-level representation returned by {@link Literal}. | |
| * | |
| * @category models | |
| * @since 3.10.0 | |
| */ | |
| export interface Literal<L extends SchemaAST.LiteralValue> | |
| extends Bottom<L, L, never, never, SchemaAST.Literal, Literal<L>> | |
| { | |
| readonly literal: L | |
| transform<L2 extends SchemaAST.LiteralValue>(to: L2): decodeTo<Literal<L2>, Literal<L>> | |
| } | |
| /** | |
| * Creates a schema for a single literal value (string, number, bigint, boolean, or null). | |
| * | |
| * **Example** (String literal) | |
| * | |
| * ```ts | |
| * import { Schema } from "effect" | |
| * | |
| * const schema = Schema.Literal("hello") | |
| * // Type: Schema.Literal<"hello"> | |
| * ``` | |
| * | |
| * @see {@link Literals} for a schema that represents a union of literals. | |
| * @see {@link tag} for a schema that represents a literal value that can be | |
| * used as a discriminator field in tagged unions and has a constructor default. | |
| * @category constructors | |
| * @since 3.10.0 | |
| */ | |
| export function Literal<L extends SchemaAST.LiteralValue>(literal: L): Literal<L> { | |
| const out = make<Literal<L>>(new SchemaAST.Literal(literal), { | |
| literal, | |
| transform<L2 extends SchemaAST.LiteralValue>(to: L2): decodeTo<Literal<L2>, Literal<L>> { | |
| return out.pipe(decodeTo(Literal(to), { | |
| decode: SchemaGetter.transform(() => to), | |
| encode: SchemaGetter.transform(() => literal) | |
| })) | |
| } | |
| }) | |
| return out | |
| } | |
| /** | |
| * Namespace for {@link TemplateLiteral} helper types. | |
| * | |
| * @since 3.10.0 | |
| */ | |
| export declare namespace TemplateLiteral { | |
| /** | |
| * Constraint for schema parts that can appear inside a `TemplateLiteral`. | |
| * | |
| * **Details** | |
| * | |
| * The schema's encoded value must be a `string`, `number`, or `bigint` so it can | |
| * be converted into a template literal string segment. | |
| * | |
| * @category utility types | |
| * @since 4.0.0 | |
| */ | |
| export interface SchemaPart extends Top { | |
| readonly Encoded: string | number | bigint | |
| } | |
| /** | |
| * Literal value that can be used directly as a part of a `TemplateLiteral`. | |
| * | |
| * @category utility types | |
| * @since 4.0.0 | |
| */ | |
| export type LiteralPart = string | number | bigint | |
| /** | |
| * A single part of a `TemplateLiteral`, either an interpolated schema part or a | |
| * literal `string`, `number`, or `bigint`. | |
| * | |
| * @category utility types | |
| * @since 4.0.0 | |
| */ | |
| export type Part = SchemaPart | LiteralPart | |
| /** | |
| * Ordered list of parts used to construct a `TemplateLiteral` schema. | |
| * | |
| * @category utility types | |
| * @since 4.0.0 | |
| */ | |
| export type Parts = ReadonlyArray<Part> | |
| type AppendType< | |
| Template extends string, | |
| Next | |
| > = Next extends LiteralPart ? `${Template}${Next}` | |
| : Next extends Codec<unknown, infer E extends LiteralPart, unknown, unknown> ? `${Template}${E}` | |
| : never | |
| /** | |
| * Computes the encoded string literal type produced by concatenating the encoded | |
| * forms of all template literal parts. | |
| * | |
| * @category utility types | |
| * @since 3.10.0 | |
| */ | |
| export type Encoded<Parts> = Parts extends readonly [...infer Init, infer Last] ? AppendType<Encoded<Init>, Last> | |
| : `` | |
| } | |
| /** | |
| * Type-level representation returned by {@link TemplateLiteral}. | |
| * | |
| * @category models | |
| * @since 3.10.0 | |
| */ | |
| export interface TemplateLiteral<Parts extends TemplateLiteral.Parts> extends | |
| Bottom< | |
| TemplateLiteral.Encoded<Parts>, | |
| TemplateLiteral.Encoded<Parts>, | |
| never, | |
| never, | |
| SchemaAST.TemplateLiteral, | |
| TemplateLiteral<Parts> | |
| > | |
| { | |
| readonly parts: Parts | |
| } | |
| function templateLiteralFromParts<Parts extends TemplateLiteral.Parts>(parts: Parts) { | |
| return new SchemaAST.TemplateLiteral(parts.map((part) => isSchema(part) ? part.ast : new SchemaAST.Literal(part))) | |
| } | |
| /** | |
| * Creates a schema that validates strings matching a template literal pattern. Each part can be | |
| * a literal string/number/bigint or a schema whose encoded type is a string, number, or bigint. | |
| * | |
| * **When to use** | |
| * | |
| * Use when the decoded value should remain the matched string and you do not | |
| * need the individual template parts parsed into a tuple. | |
| * | |
| * **Example** (URL path pattern) | |
| * | |
| * ```ts | |
| * import { Schema } from "effect" | |
| * | |
| * const schema = Schema.TemplateLiteral(["/user/", Schema.Number]) | |
| * // matches strings like "/user/123", "/user/42", etc. | |
| * ``` | |
| * | |
| * @see {@link TemplateLiteralParser} for a schema that also parses matched parts into a tuple. | |
| * @category constructors | |
| * @since 3.10.0 | |
| */ | |
| export function TemplateLiteral<const Parts extends TemplateLiteral.Parts>(parts: Parts): TemplateLiteral<Parts> { | |
| return make(templateLiteralFromParts(parts), { parts }) | |
| } | |
| /** | |
| * Namespace for {@link TemplateLiteralParser} helper types. | |
| * | |
| * @since 3.10.0 | |
| */ | |
| export declare namespace TemplateLiteralParser { | |
| /** | |
| * Computes the decoded tuple type produced by `TemplateLiteralParser`. | |
| * | |
| * **Details** | |
| * | |
| * Literal parts contribute their literal value to the tuple. Schema parts | |
| * contribute their decoded `Type`. | |
| * | |
| * @category utility types | |
| * @since 3.10.0 | |
| */ | |
| export type Type<Parts> = Parts extends readonly [infer Head, ...infer Tail] ? readonly [ | |
| Head extends TemplateLiteral.LiteralPart ? Head : | |
| Head extends Codec<infer T, unknown, unknown, unknown> ? T | |
| : never, | |
| ...Type<Tail> | |
| ] | |
| : [] | |
| } | |
| /** | |
| * Type-level representation returned by {@link TemplateLiteralParser}. | |
| * | |
| * @category models | |
| * @since 3.10.0 | |
| */ | |
| export interface TemplateLiteralParser<Parts extends TemplateLiteral.Parts> extends | |
| Bottom< | |
| TemplateLiteralParser.Type<Parts>, | |
| TemplateLiteral.Encoded<Parts>, | |
| never, | |
| never, | |
| SchemaAST.Arrays, | |
| TemplateLiteralParser<Parts> | |
| > | |
| { | |
| readonly parts: Parts | |
| } | |
| /** | |
| * Schema for parsing template literal matches into typed tuple parts. | |
| * | |
| * **When to use** | |
| * | |
| * Use to validate a template literal string and decode the matched parts into | |
| * typed values. | |
| * | |
| * **Details** | |
| * | |
| * Unlike {@link TemplateLiteral}, this schema decodes the matched string into a | |
| * readonly tuple with one element per schema part. | |
| * | |
| * **Example** (Parse path parameters) | |
| * | |
| * ```ts | |
| * import { Schema } from "effect" | |
| * | |
| * const schema = Schema.TemplateLiteralParser(["/user/", Schema.NumberFromString]) | |
| * // decodes "/user/42" => readonly ["/user/", 42] | |
| * ``` | |
| * | |
| * @see {@link TemplateLiteral} for a validation-only version that keeps the string encoded. | |
| * @category constructors | |
| * @since 3.10.0 | |
| */ | |
| export function TemplateLiteralParser<const Parts extends TemplateLiteral.Parts>( | |
| parts: Parts | |
| ): TemplateLiteralParser<Parts> { | |
| return make(templateLiteralFromParts(parts).asTemplateLiteralParser(), { parts }) | |
| } | |
| /** | |
| * Type-level representation returned by {@link Enum}. | |
| * | |
| * @category models | |
| * @since 4.0.0 | |
| */ | |
| export interface Enum<A extends { [x: string]: string | number }> | |
| extends Bottom<A[keyof A], A[keyof A], never, never, SchemaAST.Enum, Enum<A>> | |
| { | |
| readonly enums: A | |
| } | |
| /** | |
| * Creates a schema from a TypeScript enum object. Validates that the input is one of the enum's values. | |
| * | |
| * **Example** (Direction enum) | |
| * | |
| * ```ts | |
| * import { Schema } from "effect" | |
| * | |
| * enum Direction { | |
| * Up = "Up", | |
| * Down = "Down" | |
| * } | |
| * | |
| * const schema = Schema.Enum(Direction) | |
| * // accepts "Up" or "Down" | |
| * ``` | |
| * | |
| * @category constructors | |
| * @since 4.0.0 | |
| */ | |
| export function Enum<A extends { [x: string]: string | number }>(enums: A): Enum<A> { | |
| return make( | |
| new SchemaAST.Enum( | |
| Object.keys(enums).filter( | |
| (key) => typeof enums[enums[key]] !== "number" | |
| ).map((key) => [key, enums[key]]) | |
| ), | |
| { enums } | |
| ) | |
| } | |
| /** | |
| * Type-level representation of {@link Never}. | |
| * | |
| * @category models | |
| * @since 3.10.0 | |
| */ | |
| export interface Never extends Bottom<never, never, never, never, SchemaAST.Never, Never> {} | |
| /** | |
| * Schema for the `never` type. Always fails validation — no value satisfies it. | |
| * | |
| * @category schemas | |
| * @since 3.10.0 | |
| */ | |
| export const Never: Never = make(SchemaAST.never) | |
| /** | |
| * Type-level representation of {@link Any}. | |
| * | |
| * @category models | |
| * @since 3.10.0 | |
| */ | |
| export interface Any extends Bottom<any, any, never, never, SchemaAST.Any, Any> {} | |
| /** | |
| * Schema for the `any` type. Accepts any value without validation. | |
| * | |
| * @see {@link Unknown} for a safer alternative that uses `unknown`. | |
| * @category schemas | |
| * @since 3.10.0 | |
| */ | |
| export const Any: Any = make(SchemaAST.any) | |
| /** | |
| * Type-level representation of {@link Unknown}. | |
| * | |
| * @category models | |
| * @since 3.10.0 | |
| */ | |
| export interface Unknown extends Bottom<unknown, unknown, never, never, SchemaAST.Unknown, Unknown> {} | |
| /** | |
| * Schema for the `unknown` type. Accepts any value without validation. | |
| * | |
| * **When to use** | |
| * | |
| * Use as a top schema when you need to accept any input while preserving | |
| * TypeScript's `unknown` safety at use sites. | |
| * | |
| * @see {@link Any} for the `any` variant. | |
| * @category schemas | |
| * @since 3.10.0 | |
| */ | |
| export const Unknown: Unknown = make(SchemaAST.unknown) | |
| /** | |
| * Type-level representation of {@link Null}. | |
| * | |
| * @category models | |
| * @since 3.10.0 | |
| */ | |
| export interface Null extends Bottom<null, null, never, never, SchemaAST.Null, Null> {} | |
| /** | |
| * Schema for the `null` literal. Validates that the input is strictly `null`. | |
| * | |
| * @see {@link NullOr} for a union with another schema. | |
| * @category schemas | |
| * @since 3.10.0 | |
| */ | |
| export const Null: Null = make(SchemaAST.null) | |
| /** | |
| * Type-level representation of {@link Undefined}. | |
| * | |
| * @category models | |
| * @since 3.10.0 | |
| */ | |
| export interface Undefined extends Bottom<undefined, undefined, never, never, SchemaAST.Undefined, Undefined> {} | |
| /** | |
| * Schema for the `undefined` literal. Validates that the input is strictly `undefined`. | |
| * | |
| * @see {@link UndefinedOr} for a union with another schema. | |
| * @category schemas | |
| * @since 3.10.0 | |
| */ | |
| export const Undefined: Undefined = make(SchemaAST.undefined) | |
| /** | |
| * Type-level representation of {@link String}. | |
| * | |
| * @category models | |
| * @since 4.0.0 | |
| */ | |
| export interface String extends Bottom<string, string, never, never, SchemaAST.String, String> {} | |
| /** | |
| * Schema for `string` values. Validates that the input is `typeof` `"string"`. | |
| * | |
| * @category schemas | |
| * @since 4.0.0 | |
| */ | |
| export const String: String = make(SchemaAST.string) | |
| /** | |
| * Type-level representation of {@link Number}. | |
| * | |
| * @category models | |
| * @since 4.0.0 | |
| */ | |
| export interface Number extends Bottom<number, number, never, never, SchemaAST.Number, Number> {} | |
| /** | |
| * Schema for `number` values, including `NaN`, `Infinity`, and `-Infinity`. | |
| * | |
| * **Details** | |
| * | |
| * Default JSON serializer: | |
| * | |
| * - Finite numbers are serialized as numbers. | |
| * - Non-finite values are serialized as strings (`"NaN"`, `"Infinity"`, `"-Infinity"`). | |
| * | |
| * @see {@link Finite} for a schema that excludes non-finite values. | |
| * @category schemas | |
| * @since 4.0.0 | |
| */ | |
| export const Number: Number = make(SchemaAST.number) | |
| /** | |
| * Type-level representation of {@link Boolean}. | |
| * | |
| * @category models | |
| * @since 4.0.0 | |
| */ | |
| export interface Boolean extends Bottom<boolean, boolean, never, never, SchemaAST.Boolean, Boolean> {} | |
| /** | |
| * Schema for `boolean` values. Validates that the input is `typeof` `"boolean"`. | |
| * | |
| * **When to use** | |
| * | |
| * Use to validate values that are already JavaScript booleans. | |
| * | |
| * @see {@link BooleanFromBit} for a schema that decodes bit literals `0` or `1` into a boolean | |
| * | |
| * @category boolean | |
| * @since 4.0.0 | |
| */ | |
| export const Boolean: Boolean = make(SchemaAST.boolean) | |
| /** | |
| * Type-level representation of {@link Symbol}. | |
| * | |
| * @category models | |
| * @since 4.0.0 | |
| */ | |
| export interface Symbol extends Bottom<symbol, symbol, never, never, SchemaAST.Symbol, Symbol> {} | |
| /** | |
| * Schema for `symbol` values. Validates that the input is `typeof` `"symbol"`. | |
| * | |
| * @see {@link UniqueSymbol} for a schema that matches a specific symbol. | |
| * @category schemas | |
| * @since 4.0.0 | |
| */ | |
| export const Symbol: Symbol = make(SchemaAST.symbol) | |
| /** | |
| * Type-level representation of {@link BigInt}. | |
| * | |
| * @category models | |
| * @since 4.0.0 | |
| */ | |
| export interface BigInt extends Bottom<bigint, bigint, never, never, SchemaAST.BigInt, BigInt> {} | |
| /** | |
| * Schema for `bigint` values. Validates that the input is `typeof` `"bigint"`. | |
| * | |
| * **When to use** | |
| * | |
| * Use when the input is already a bigint and the schema should validate and | |
| * preserve bigint values without parsing from another representation. | |
| * | |
| * @see {@link BigIntFromString} for parsing string input into a bigint | |
| * | |
| * @category schemas | |
| * @since 4.0.0 | |
| */ | |
| export const BigInt: BigInt = make(SchemaAST.bigInt) | |
| /** | |
| * Type-level representation of {@link Void}. | |
| * | |
| * @category models | |
| * @since 3.10.0 | |
| */ | |
| export interface Void extends Bottom<void, void, never, never, SchemaAST.Void, Void> {} | |
| /** | |
| * Schema for the `void` type. Accepts `undefined` as the encoded value. | |
| * | |
| * @category schemas | |
| * @since 3.10.0 | |
| */ | |
| export const Void: Void = make(SchemaAST.void) | |
| /** | |
| * Type-level representation of {@link ObjectKeyword}. | |
| * | |
| * @category models | |
| * @since 4.0.0 | |
| */ | |
| export interface ObjectKeyword extends Bottom<object, object, never, never, SchemaAST.ObjectKeyword, ObjectKeyword> {} | |
| /** | |
| * Schema for the `object` type. Validates that the input is a non-null object or function | |
| * (i.e. `typeof value === "object" && value !== null || typeof value === "function"`). | |
| * | |
| * @category schemas | |
| * @since 4.0.0 | |
| */ | |
| export const ObjectKeyword: ObjectKeyword = make(SchemaAST.objectKeyword) | |
| /** | |
| * Type-level representation returned by {@link UniqueSymbol}. | |
| * | |
| * @category models | |
| * @since 4.0.0 | |
| */ | |
| export interface UniqueSymbol<sym extends symbol> | |
| extends Bottom<sym, sym, never, never, SchemaAST.UniqueSymbol, UniqueSymbol<sym>> | |
| {} | |
| /** | |
| * Creates a schema for a specific symbol. Only that exact symbol satisfies the schema. | |
| * | |
| * **Example** (Specific symbol) | |
| * | |
| * ```ts | |
| * import { Schema } from "effect" | |
| * | |
| * const mySymbol = Symbol.for("mySymbol") | |
| * const schema = Schema.UniqueSymbol(mySymbol) | |
| * ``` | |
| * | |
| * @see {@link Symbol} for a schema that accepts any symbol. | |
| * @category constructors | |
| * @since 4.0.0 | |
| */ | |
| export function UniqueSymbol<const sym extends symbol>(symbol: sym): UniqueSymbol<sym> { | |
| return make(new SchemaAST.UniqueSymbol(symbol)) | |
| } | |
| /** | |
| * Namespace for struct field type utilities. | |
| * | |
| * **Details** | |
| * | |
| * These types compute the decoded `Type`, encoded `Encoded`, and constructor | |
| * input `MakeIn` of a {@link Struct} from its field map, handling optional, | |
| * mutable, and other field modifiers automatically. | |
| * | |
| * - `Struct.Fields` — constraint for the field map object | |
| * - `Struct.Type<F>` — decoded type of the struct | |
| * - `Struct.Encoded<F>` — encoded type of the struct | |
| * - `Struct.MakeIn<F>` — constructor input (optional/defaulted fields may be omitted) | |
| * - `Struct.DecodingServices<F>` / `Struct.EncodingServices<F>` — required services | |
| * | |
| * @since 3.10.0 | |
| */ | |
| export declare namespace Struct { | |
| /** | |
| * Constraint for a struct field map: an object whose values are schemas. | |
| * | |
| * @category utility types | |
| * @since 3.10.0 | |
| */ | |
| export type Fields = { readonly [x: PropertyKey]: Top } | |
| type TypeOptionalKeys<Fields extends Struct.Fields> = { | |
| [K in keyof Fields]: Fields[K] extends { readonly "~type.optionality": "optional" } ? K | |
| : never | |
| }[keyof Fields] | |
| type TypeMutableKeys<Fields extends Struct.Fields> = { | |
| [K in keyof Fields]: Fields[K] extends { readonly "~type.mutability": "mutable" } ? K | |
| : never | |
| }[keyof Fields] | |
| type Type_< | |
| F extends Fields, | |
| O extends keyof F = TypeOptionalKeys<F>, | |
| M extends keyof F = TypeMutableKeys<F> | |
| > = | |
| & { readonly [K in keyof F as K extends M | O ? never : K]: F[K]["Type"] } | |
| & { readonly [K in keyof F as K extends O ? K extends M ? never : K : never]?: F[K]["Type"] } | |
| & { -readonly [K in keyof F as K extends M ? K extends O ? never : K : never]: F[K]["Type"] } | |
| & { -readonly [K in keyof F as K extends M & O ? K : never]?: F[K]["Type"] } | |
| /** | |
| * Computes the decoded object type for a struct field map. | |
| * | |
| * **Details** | |
| * | |
| * Field schemas contribute their decoded `Type`. `optionalKey` and `optional` | |
| * produce optional properties, while `mutableKey` produces writable properties. | |
| * | |
| * @category utility types | |
| * @since 3.10.0 | |
| */ | |
| export type Type<F extends Fields> = Simplify<Type_<F>> | |
| type Iso_< | |
| F extends Fields, | |
| O extends keyof F = TypeOptionalKeys<F>, | |
| M extends keyof F = TypeMutableKeys<F> | |
| > = | |
| & { readonly [K in keyof F as K extends M | O ? never : K]: F[K]["Iso"] } | |
| & { readonly [K in keyof F as K extends O ? K extends M ? never : K : never]?: F[K]["Iso"] } | |
| & { -readonly [K in keyof F as K extends M ? K extends O ? never : K : never]: F[K]["Iso"] } | |
| & { -readonly [K in keyof F as K extends M & O ? K : never]?: F[K]["Iso"] } | |
| /** | |
| * Computes the iso object type for a struct field map from each field schema's | |
| * `Iso` type. | |
| * | |
| * **Details** | |
| * | |
| * The resulting property optionality and mutability follow the same field | |
| * modifiers used by `Struct.Type`. | |
| * | |
| * @category utility types | |
| * @since 4.0.0 | |
| */ | |
| export type Iso<F extends Fields> = Simplify<Iso_<F>> | |
| type EncodedOptionalKeys<Fields extends Struct.Fields> = { | |
| [K in keyof Fields]: Fields[K] extends { readonly "~encoded.optionality": "optional" } ? K | |
| : never | |
| }[keyof Fields] | |
| type EncodedMutableKeys<Fields extends Struct.Fields> = { | |
| [K in keyof Fields]: Fields[K] extends { readonly "~encoded.mutability": "mutable" } ? K | |
| : never | |
| }[keyof Fields] | |
| type Encoded_< | |
| F extends Fields, | |
| O extends keyof F = EncodedOptionalKeys<F>, | |
| M extends keyof F = EncodedMutableKeys<F> | |
| > = | |
| & { readonly [K in keyof F as K extends M | O ? never : K]: F[K]["Encoded"] } | |
| & { readonly [K in keyof F as K extends O ? K extends M ? never : K : never]?: F[K]["Encoded"] } | |
| & { -readonly [K in keyof F as K extends M ? K extends O ? never : K : never]: F[K]["Encoded"] } | |
| & { -readonly [K in keyof F as K extends M & O ? K : never]?: F[K]["Encoded"] } | |
| /** | |
| * Computes the encoded object type for a struct field map. | |
| * | |
| * **Details** | |
| * | |
| * Field schemas contribute their `Encoded` type. Encoded-side optionality and | |
| * mutability modifiers determine whether properties are optional or writable in | |
| * the encoded shape. | |
| * | |
| * @category utility types | |
| * @since 3.10.0 | |
| */ | |
| export type Encoded<F extends Fields> = Simplify<Encoded_<F>> | |
| /** | |
| * Union of all decoding service requirements needed by the schemas in a struct | |
| * field map. | |
| * | |
| * @category utility types | |
| * @since 4.0.0 | |
| */ | |
| export type DecodingServices<F extends Fields> = { readonly [K in keyof F]: F[K]["DecodingServices"] }[keyof F] | |
| /** | |
| * Union of all encoding service requirements needed by the schemas in a struct | |
| * field map. | |
| * | |
| * @category utility types | |
| * @since 4.0.0 | |
| */ | |
| export type EncodingServices<F extends Fields> = { readonly [K in keyof F]: F[K]["EncodingServices"] }[keyof F] | |
| type TypeConstructorDefaultedKeys<Fields extends Struct.Fields> = { | |
| [K in keyof Fields]: Fields[K] extends { readonly "~type.constructor.default": "with-default" } ? K | |
| : never | |
| }[keyof Fields] | |
| type MakeIn_< | |
| F extends Fields, | |
| O = TypeOptionalKeys<F> | TypeConstructorDefaultedKeys<F> | |
| > = | |
| & { readonly [K in keyof F as K extends O ? never : K]: F[K]["~type.make"] } | |
| & { readonly [K in keyof F as K extends O ? K : never]?: F[K]["~type.make"] } | |
| /** | |
| * Computes the input object type accepted when constructing a struct value. | |
| * | |
| * **Details** | |
| * | |
| * Required fields use each field schema's `~type.make` input. Fields marked | |
| * optional or with a constructor default may be omitted. | |
| * | |
| * @category utility types | |
| * @since 4.0.0 | |
| */ | |
| export type MakeIn<F extends Fields> = Simplify<MakeIn_<F>> | |
| } | |
| /** | |
| * Type-level representation returned by {@link Struct}. | |
| * | |
| * @category models | |
| * @since 3.10.0 | |
| */ | |
| export interface Struct<Fields extends Struct.Fields> extends | |
| Bottom< | |
| Struct.Type<Fields>, | |
| Struct.Encoded<Fields>, | |
| Struct.DecodingServices<Fields>, | |
| Struct.EncodingServices<Fields>, | |
| SchemaAST.Objects, | |
| Struct<Fields>, | |
| Struct.MakeIn<Fields>, | |
| Struct.Iso<Fields> | |
| > | |
| { | |
| /** | |
| * The field definitions of this struct. Spread them into a new struct to | |
| * reuse fields across schemas. | |
| * | |
| * **Example** (Reusing fields across structs) | |
| * | |
| * ```ts | |
| * import { Schema } from "effect" | |
| * | |
| * const Timestamped = Schema.Struct({ | |
| * createdAt: Schema.Date, | |
| * updatedAt: Schema.Date | |
| * }) | |
| * | |
| * const User = Schema.Struct({ | |
| * ...Timestamped.fields, | |
| * name: Schema.String, | |
| * email: Schema.String | |
| * }) | |
| * ``` | |
| */ | |
| readonly fields: Fields | |
| /** | |
| * Returns a new struct with the fields modified by the provided function. | |
| * | |
| * **Details** | |
| * | |
| * Options: | |
| * | |
| * - `unsafePreserveChecks` - if `true`, keep any `.check(...)` constraints | |
| * that were attached to the original union. Defaults to `false`. | |
| * | |
| * **Warning**: This is an unsafe operation. Since `mapFields` | |
| * transformations change the schema type, the original refinement functions | |
| * may no longer be valid or safe to apply to the transformed schema. Only | |
| * use this option if you have verified that your refinements remain correct | |
| * after the transformation. | |
| */ | |
| mapFields<To extends Struct.Fields>( | |
| f: (fields: Fields) => To, | |
| options?: { | |
| readonly unsafePreserveChecks?: boolean | undefined | |
| } | undefined | |
| ): Struct<Simplify<Readonly<To>>> | |
| } | |
| function makeStruct<const Fields extends Struct.Fields>(ast: SchemaAST.Objects, fields: Fields): Struct<Fields> { | |
| return make(ast, { | |
| fields, | |
| mapFields<To extends Struct.Fields>( | |
| this: Struct<Fields>, | |
| f: (fields: Fields) => To, | |
| options?: { | |
| readonly unsafePreserveChecks?: boolean | undefined | |
| } | undefined | |
| ): Struct<To> { | |
| const fields = f(this.fields) | |
| return makeStruct(SchemaAST.struct(fields, options?.unsafePreserveChecks ? this.ast.checks : undefined), fields) | |
| } | |
| }) | |
| } | |
| /** | |
| * Defines a struct schema from a map of field schemas. | |
| * | |
| * **Details** | |
| * | |
| * Each field value is a schema. Use {@link optionalKey} or {@link optional} to | |
| * mark fields as optional, and {@link mutableKey} to mark them as mutable. | |
| * | |
| * The resulting schema's `Type` is a readonly object type with the fields' | |
| * decoded types. The `Encoded` form mirrors the field schemas' encoded types. | |
| * | |
| * **Example** (Basic struct) | |
| * | |
| * ```ts | |
| * import { Schema } from "effect" | |
| * | |
| * const Person = Schema.Struct({ | |
| * name: Schema.String, | |
| * age: Schema.Number, | |
| * email: Schema.optionalKey(Schema.String) | |
| * }) | |
| * | |
| * // { readonly name: string; readonly age: number; readonly email?: string } | |
| * type Person = typeof Person.Type | |
| * | |
| * const alice = Schema.decodeUnknownSync(Person)({ name: "Alice", age: 30 }) | |
| * console.log(alice) | |
| * // { name: 'Alice', age: 30 } | |
| * ``` | |
| * | |
| * @category constructors | |
| * @since 3.10.0 | |
| */ | |
| export function Struct<const Fields extends Struct.Fields>(fields: Fields): Struct<Fields> { | |
| return makeStruct(SchemaAST.struct(fields, undefined), fields) | |
| } | |
| interface fieldsAssign<NewFields extends Struct.Fields> extends Lambda { | |
| <Fields extends Struct.Fields>( | |
| struct: Struct<Fields> | |
| ): Struct<Struct_.Simplify<Struct_.Assign<Fields, NewFields>>> | |
| readonly "~lambda.out": this["~lambda.in"] extends Struct<Struct.Fields> | |
| ? Struct<Struct_.Simplify<Struct_.Assign<this["~lambda.in"]["fields"], NewFields>>> | |
| : "Error: schema not eligible for fieldsAssign" | |
| } | |
| /** | |
| * Adds fields to a struct schema through a struct-mapping lambda. | |
| * | |
| * **When to use** | |
| * | |
| * Use to add the same fields to an existing struct or every struct member of a | |
| * union. | |
| * | |
| * **Details** | |
| * | |
| * This is a shortcut for `MyStruct.mapFields(Struct.assign(fields))`. | |
| * | |
| * **Example** (Adding fields to a union of structs) | |
| * | |
| * ```ts | |
| * import { Schema, Tuple } from "effect" | |
| * | |
| * // Add a new field to all members of a union of structs | |
| * const schema = Schema.Union([ | |
| * Schema.Struct({ a: Schema.String }), | |
| * Schema.Struct({ b: Schema.Number }) | |
| * ]).mapMembers(Tuple.map(Schema.fieldsAssign({ c: Schema.Number }))) | |
| * ``` | |
| * | |
| * @category combinators | |
| * @since 4.0.0 | |
| */ | |
| export function fieldsAssign<const NewFields extends Struct.Fields>(fields: NewFields) { | |
| return Struct_.lambda<fieldsAssign<NewFields>>((struct) => struct.mapFields(Struct_.assign(fields))) | |
| } | |
| /** | |
| * Type-level representation returned by {@link encodeKeys}. | |
| * | |
| * @category transforming | |
| * @since 4.0.0 | |
| */ | |
| export interface encodeKeys< | |
| S extends Top & { readonly fields: Struct.Fields }, | |
| M extends { readonly [K in keyof S["fields"]]?: PropertyKey } | |
| > extends | |
| decodeTo< | |
| S, | |
| Struct< | |
| { | |
| [ | |
| K in keyof S["fields"] as K extends keyof M ? M[K] extends PropertyKey ? M[K] : K : K | |
| ]: toEncoded<S["fields"][K]> | |
| } | |
| > | |
| > | |
| {} | |
| const canonicalPropertyKey = (key: PropertyKey): string | symbol => | |
| typeof key === "symbol" ? key : globalThis.String(key) | |
| /** | |
| * Renames struct keys in the encoded form without changing the decoded type. | |
| * | |
| * **Details** | |
| * | |
| * Takes a partial mapping `{ decodedKey: encodedKey }` and produces a | |
| * transformation schema that decodes from the renamed keys and encodes back to | |
| * the renamed keys. Keys not present in the mapping are left unchanged. | |
| * If two existing fields would produce the same encoded key, construction | |
| * fails. | |
| * | |
| * **Example** (Rename `name` to `full_name` in the encoded form) | |
| * | |
| * ```ts | |
| * import { Schema } from "effect" | |
| * | |
| * const Person = Schema.Struct({ name: Schema.String, age: Schema.Number }) | |
| * const Encoded = Person.pipe(Schema.encodeKeys({ name: "full_name" })) | |
| * | |
| * // Decodes { full_name: "Alice", age: 30 } → { name: "Alice", age: 30 } | |
| * const alice = Schema.decodeUnknownSync(Encoded)({ full_name: "Alice", age: 30 }) | |
| * console.log(alice) | |
| * // { name: 'Alice', age: 30 } | |
| * ``` | |
| * | |
| * @category transforming | |
| * @since 4.0.0 | |
| */ | |
| export function encodeKeys< | |
| S extends Top & { readonly fields: Struct.Fields }, | |
| const M extends { readonly [K in keyof S["fields"]]?: PropertyKey } | |
| >(mapping: M) { | |
| return function(self: S): encodeKeys<S, M> { | |
| const fields: any = {} | |
| const appliedMapping: any = {} | |
| const reverseMapping: any = {} | |
| const seenEncodedKeys = new Set<string | symbol>() | |
| for (const k of Reflect.ownKeys(self.fields)) { | |
| const encoded = toEncoded(self.fields[k]) | |
| const hasMapping = Object.hasOwn(mapping, k) | |
| const encodedKey = hasMapping ? (mapping as any)[k] as PropertyKey : k | |
| const canonical = canonicalPropertyKey(encodedKey) | |
| if (seenEncodedKeys.has(canonical)) { | |
| throw new globalThis.Error(`Duplicate encoded keys: ${formatPropertyKey(encodedKey)}`) | |
| } | |
| seenEncodedKeys.add(canonical) | |
| fields[encodedKey] = encoded | |
| if (hasMapping) { | |
| appliedMapping[k] = encodedKey | |
| reverseMapping[encodedKey] = k | |
| } | |
| } | |
| return Struct(fields).pipe(decodeTo( | |
| self, | |
| SchemaTransformation.transform<any, any>({ | |
| decode: Struct_.renameKeys(reverseMapping), | |
| encode: Struct_.renameKeys(appliedMapping) | |
| }) | |
| )) as any | |
| } | |
| } | |
| /** | |
| * Adds derived fields to a struct schema during decoding. | |
| * | |
| * **Details** | |
| * | |
| * Each new field is derived from the decoded struct value via a function that | |
| * returns `Option`. On encoding the derived fields are stripped. This allows | |
| * computed or enriched fields to live in the decoded type without appearing in | |
| * the encoded form. | |
| * | |
| * **Example** (Add a computed `fullName` field) | |
| * | |
| * ```ts | |
| * import { Option, Schema } from "effect" | |
| * | |
| * const Person = Schema.Struct({ first: Schema.String, last: Schema.String }) | |
| * const Extended = Person.pipe( | |
| * Schema.extendTo( | |
| * { fullName: Schema.String }, | |
| * { fullName: (p) => Option.some(`${p.first} ${p.last}`) } | |
| * ) | |
| * ) | |
| * | |
| * const alice = Schema.decodeUnknownSync(Extended)({ first: "Alice", last: "Smith" }) | |
| * console.log(alice.fullName) | |
| * // Alice Smith | |
| * ``` | |
| * | |
| * @category transforming | |
| * @since 4.0.0 | |
| */ | |
| export function extendTo<S extends Struct<Struct.Fields>, const Fields extends Struct.Fields>( | |
| /** The new fields to add */ | |
| fields: Fields, | |
| /** A function per field to derive its value from the original input */ | |
| derive: { readonly [K in keyof Fields]: (s: S["Type"]) => Option_.Option<Fields[K]["Type"]> } | |
| ) { | |
| return ( | |
| self: S | |
| ): decodeTo<Struct<Simplify<{ [K in keyof S["fields"]]: toType<S["fields"][K]> } & Fields>>, S> => { | |
| const f = Record_.map(self.fields, toType) | |
| const to = Struct({ ...f, ...fields }) | |
| return self.pipe(decodeTo( | |
| to, | |
| SchemaTransformation.transform({ | |
| decode: (input) => { | |
| const out: any = { ...input } | |
| for (const k in fields) { | |
| const f = derive[k] | |
| const o = f(input) | |
| if (Option_.isSome(o)) { | |
| out[k] = o.value | |
| } | |
| } | |
| return out | |
| }, | |
| encode: (input) => { | |
| const out = { ...input } | |
| for (const k in fields) { | |
| delete out[k] | |
| } | |
| return out | |
| } | |
| }) | |
| )) as any | |
| } | |
| } | |
| /** | |
| * Namespace for `Record` type utilities. | |
| * | |
| * **Details** | |
| * | |
| * - `Record.Key` — constraint for the key schema (must encode to `PropertyKey`) | |
| * - `Record.Type<K, V>` — decoded type of the record | |
| * - `Record.Encoded<K, V>` — encoded type of the record | |
| * | |
| * @since 3.10.0 | |
| */ | |
| export declare namespace Record { | |
| /** | |
| * Constraint for schemas that can be used as record keys. | |
| * | |
| * **Details** | |
| * | |
| * The key schema must decode and encode property keys (`string`, `number`, or | |
| * `symbol`) so it can describe object property names. | |
| * | |
| * @category utility types | |
| * @since 4.0.0 | |
| */ | |
| export interface Key extends Codec<PropertyKey, PropertyKey, unknown, unknown> { | |
| readonly "~type.make": PropertyKey | |
| readonly "Iso": PropertyKey | |
| } | |
| /** | |
| * Computes the decoded object type for a record schema from its key and value | |
| * schemas. | |
| * | |
| * **Details** | |
| * | |
| * The key schema supplies the property keys and the value schema supplies each | |
| * property's decoded `Type`. Optional and mutable value schemas affect the | |
| * resulting property optionality and writability. | |
| * | |
| * @category utility types | |
| * @since 3.10.0 | |
| */ | |
| export type Type<Key extends Record.Key, Value extends Top> = Value extends | |
| { readonly "~type.optionality": "optional" } ? | |
| Value extends { readonly "~type.mutability": "mutable" } ? { [P in Key["Type"]]?: Value["Type"] } | |
| : { readonly [P in Key["Type"]]?: Value["Type"] } | |
| : Value extends { readonly "~type.mutability": "mutable" } ? { [P in Key["Type"]]: Value["Type"] } | |
| : { readonly [P in Key["Type"]]: Value["Type"] } | |
| /** | |
| * Computes the iso object type for a record schema from the key schema's `Iso` | |
| * keys and the value schema's `Iso` values. | |
| * | |
| * @category utility types | |
| * @since 4.0.0 | |
| */ | |
| export type Iso<Key extends Record.Key, Value extends Top> = Value extends | |
| { readonly "~type.optionality": "optional" } ? | |
| Value extends { readonly "~type.mutability": "mutable" } ? { [P in Key["Iso"]]?: Value["Iso"] } | |
| : { readonly [P in Key["Iso"]]?: Value["Iso"] } | |
| : Value extends { readonly "~type.mutability": "mutable" } ? { [P in Key["Iso"]]: Value["Iso"] } | |
| : { readonly [P in Key["Iso"]]: Value["Iso"] } | |
| /** | |
| * Computes the encoded object type for a record schema from the key and value | |
| * schemas' encoded types. | |
| * | |
| * **Details** | |
| * | |
| * Encoded-side optionality and mutability on the value schema determine whether | |
| * the encoded record properties are optional or writable. | |
| * | |
| * @category utility types | |
| * @since 3.10.0 | |
| */ | |
| export type Encoded<Key extends Record.Key, Value extends Top> = Value extends | |
| { readonly "~encoded.optionality": "optional" } ? | |
| Value extends { readonly "~encoded.mutability": "mutable" } ? { [P in Key["Encoded"]]?: Value["Encoded"] } | |
| : { readonly [P in Key["Encoded"]]?: Value["Encoded"] } | |
| : Value extends { readonly "~encoded.mutability": "mutable" } ? { [P in Key["Encoded"]]: Value["Encoded"] } | |
| : { readonly [P in Key["Encoded"]]: Value["Encoded"] } | |
| /** | |
| * Union of the decoding service requirements of a record's key schema and value | |
| * schema. | |
| * | |
| * @category utility types | |
| * @since 4.0.0 | |
| */ | |
| export type DecodingServices<Key extends Record.Key, Value extends Top> = | |
| | Key["DecodingServices"] | |
| | Value["DecodingServices"] | |
| /** | |
| * Union of the encoding service requirements of a record's key schema and value | |
| * schema. | |
| * | |
| * @category utility types | |
| * @since 4.0.0 | |
| */ | |
| export type EncodingServices<Key extends Record.Key, Value extends Top> = | |
| | Key["EncodingServices"] | |
| | Value["EncodingServices"] | |
| /** | |
| * Computes the input object type accepted when constructing a record value. | |
| * | |
| * **Details** | |
| * | |
| * Keys use the key schema's `~type.make` type and values use the value schema's | |
| * `~type.make` type. Value optionality and mutability determine whether | |
| * properties are optional or writable. | |
| * | |
| * @category utility types | |
| * @since 4.0.0 | |
| */ | |
| export type MakeIn<Key extends Record.Key, Value extends Top> = Value extends | |
| { readonly "~encoded.optionality": "optional" } ? | |
| Value extends { readonly "~encoded.mutability": "mutable" } ? { [P in Key["~type.make"]]?: Value["~type.make"] } | |
| : { readonly [P in Key["~type.make"]]?: Value["~type.make"] } | |
| : Value extends { readonly "~encoded.mutability": "mutable" } ? { [P in Key["~type.make"]]: Value["~type.make"] } | |
| : { readonly [P in Key["~type.make"]]: Value["~type.make"] } | |
| } | |
| /** | |
| * Type-level representation returned by {@link Record}. | |
| * | |
| * @category models | |
| * @since 4.0.0 | |
| */ | |
| export interface $Record<Key extends Record.Key, Value extends Top> extends | |
| Bottom< | |
| Record.Type<Key, Value>, | |
| Record.Encoded<Key, Value>, | |
| Record.DecodingServices<Key, Value>, | |
| Record.EncodingServices<Key, Value>, | |
| SchemaAST.Objects, | |
| $Record<Key, Value>, | |
| Simplify<Record.MakeIn<Key, Value>>, | |
| Record.Iso<Key, Value> | |
| > | |
| { | |
| readonly key: Key | |
| readonly value: Value | |
| } | |
| /** | |
| * Defines a record (dictionary) schema with typed keys and values. | |
| * | |
| * **Example** (String-keyed record of numbers) | |
| * | |
| * ```ts | |
| * import { Schema } from "effect" | |
| * | |
| * const schema = Schema.Record(Schema.String, Schema.Number) | |
| * | |
| * // { readonly [x: string]: number } | |
| * type R = typeof schema.Type | |
| * | |
| * const result = Schema.decodeUnknownSync(schema)({ a: 1, b: 2 }) | |
| * console.log(result) | |
| * // { a: 1, b: 2 } | |
| * ``` | |
| * | |
| * @category constructors | |
| * @since 3.10.0 | |
| */ | |
| export function Record<Key extends Record.Key, Value extends Top>( | |
| key: Key, | |
| value: Value, | |
| options?: { | |
| readonly keyValueCombiner: { | |
| readonly decode?: Combiner.Combiner<readonly [Key["Type"], Value["Type"]]> | undefined | |
| readonly encode?: Combiner.Combiner<readonly [Key["Encoded"], Value["Encoded"]]> | undefined | |
| } | |
| } | |
| ): $Record<Key, Value> { | |
| const keyValueCombiner = options?.keyValueCombiner?.decode || options?.keyValueCombiner?.encode | |
| ? new SchemaAST.KeyValueCombiner(options.keyValueCombiner.decode, options.keyValueCombiner.encode) | |
| : undefined | |
| return make(SchemaAST.record(key.ast, value.ast, keyValueCombiner), { key, value }) | |
| } | |
| /** | |
| * Namespace for `StructWithRest` type utilities. | |
| * | |
| * **Details** | |
| * | |
| * - `StructWithRest.Type<S, R>` — decoded type (struct type intersected with record types) | |
| * - `StructWithRest.Encoded<S, R>` — encoded type | |
| * | |
| * @since 4.0.0 | |
| */ | |
| export declare namespace StructWithRest { | |
| /** | |
| * Constraint for object-like schemas that can be used as the fixed portion of a | |
| * `StructWithRest` schema. | |
| * | |
| * @category utility types | |
| * @since 4.0.0 | |
| */ | |
| export type Objects = Top & { readonly ast: SchemaAST.Objects } | |
| /** | |
| * Readonly list of record schemas that provide the additional index signatures | |
| * for a `StructWithRest` schema. | |
| * | |
| * @category utility types | |
| * @since 3.10.0 | |
| */ | |
| export type Records = ReadonlyArray<$Record<Record.Key, Top>> | |
| type MergeTuple<T extends ReadonlyArray<unknown>> = T extends readonly [infer Head, ...infer Tail] ? | |
| Head & MergeTuple<Tail> | |
| : {} | |
| type Intersect< | |
| S extends Objects, | |
| Records extends StructWithRest.Records, | |
| Side extends "Type" | "Iso" | "Encoded" | "~type.make" | |
| > = | |
| & S[Side] | |
| & MergeTuple<{ readonly [K in keyof Records]: Records[K][Side] }> | |
| /** | |
| * Computes the decoded type for `StructWithRest` by intersecting the base object | |
| * schema's decoded `Type` with the decoded types of all rest record schemas. | |
| * | |
| * @category utility types | |
| * @since 3.10.0 | |
| */ | |
| export type Type<S extends Objects, Records extends StructWithRest.Records> = Intersect<S, Records, "Type"> | |
| /** | |
| * Computes the iso type for `StructWithRest` by intersecting the base object | |
| * schema's `Iso` type with the `Iso` types of all rest record schemas. | |
| * | |
| * @category utility types | |
| * @since 4.0.0 | |
| */ | |
| export type Iso<S extends Objects, Records extends StructWithRest.Records> = Intersect<S, Records, "Iso"> | |
| /** | |
| * Computes the encoded type for `StructWithRest` by intersecting the base object | |
| * schema's encoded type with the encoded types of all rest record schemas. | |
| * | |
| * @category utility types | |
| * @since 3.10.0 | |
| */ | |
| export type Encoded<S extends Objects, Records extends StructWithRest.Records> = Intersect<S, Records, "Encoded"> | |
| /** | |
| * Computes the input type accepted when constructing a `StructWithRest` value by | |
| * intersecting the base object's make input with the make inputs of all rest | |
| * record schemas. | |
| * | |
| * @category utility types | |
| * @since 4.0.0 | |
| */ | |
| export type MakeIn<S extends Objects, Records extends StructWithRest.Records> = Intersect<S, Records, "~type.make"> | |
| type Services< | |
| S extends Objects, | |
| Records extends StructWithRest.Records, | |
| Side extends "DecodingServices" | "EncodingServices" | |
| > = | |
| | S[Side] | |
| | { [K in keyof Records]: Records[K][Side] }[number] | |
| /** | |
| * Union of the decoding service requirements of the base object schema and all | |
| * rest record schemas. | |
| * | |
| * @category utility types | |
| * @since 4.0.0 | |
| */ | |
| export type DecodingServices<S extends Objects, Records extends StructWithRest.Records> = Services< | |
| S, | |
| Records, | |
| "DecodingServices" | |
| > | |
| /** | |
| * Union of the encoding service requirements of the base object schema and all | |
| * rest record schemas. | |
| * | |
| * @category utility types | |
| * @since 4.0.0 | |
| */ | |
| export type EncodingServices<S extends Objects, Records extends StructWithRest.Records> = Services< | |
| S, | |
| Records, | |
| "EncodingServices" | |
| > | |
| type IncompatibleKeys<A, B, OK extends (keyof A & keyof B) = Extract<keyof A, keyof B>> = { | |
| [K in OK]: Required<Pick<A, K>>[K] extends B[K] ? never : K | |
| }[OK] | |
| type IncompatibleSideKeys< | |
| S extends Objects, | |
| Records extends StructWithRest.Records, | |
| Side extends "Type" | "Encoded" | "Iso" | "~type.make" | |
| > = { | |
| [I in keyof Records]: Records[I][Side] extends object ? IncompatibleKeys<S[Side], Records[I][Side]> : never | |
| }[number] | |
| /** | |
| * Validates that the records are compatible with the struct. | |
| * | |
| * @category utility types | |
| * @since 4.0.0 | |
| */ | |
| export type ValidateRecords<S extends Objects, Records extends StructWithRest.Records> = [ | |
| | IncompatibleSideKeys<S, Records, "Type"> | |
| | IncompatibleSideKeys<S, Records, "Encoded"> | |
| | IncompatibleSideKeys<S, Records, "Iso"> | |
| | IncompatibleSideKeys<S, Records, "~type.make"> | |
| ] extends [never] ? unknown | |
| : { | |
| "incompatible index signatures": | |
| | IncompatibleSideKeys<S, Records, "Type"> | |
| | IncompatibleSideKeys<S, Records, "Encoded"> | |
| | IncompatibleSideKeys<S, Records, "Iso"> | |
| | IncompatibleSideKeys<S, Records, "~type.make"> | |
| } | |
| } | |
| /** | |
| * Type-level representation returned by {@link StructWithRest}. | |
| * | |
| * @category models | |
| * @since 4.0.0 | |
| */ | |
| export interface StructWithRest< | |
| S extends StructWithRest.Objects, | |
| Records extends StructWithRest.Records | |
| > extends | |
| Bottom< | |
| Simplify<StructWithRest.Type<S, Records>>, | |
| Simplify<StructWithRest.Encoded<S, Records>>, | |
| StructWithRest.DecodingServices<S, Records>, | |
| StructWithRest.EncodingServices<S, Records>, | |
| SchemaAST.Objects, | |
| StructWithRest<S, Records>, | |
| Simplify<StructWithRest.MakeIn<S, Records>>, | |
| Simplify<StructWithRest.Iso<S, Records>> | |
| > | |
| { | |
| readonly schema: S | |
| readonly records: Records | |
| } | |
| /** | |
| * Extends a struct schema with one or more record (index-signature) schemas, | |
| * producing a schema whose decoded type intersects the struct and all records. | |
| * | |
| * **Example** (Struct with string-indexed extra keys) | |
| * | |
| * ```ts | |
| * import { Schema } from "effect" | |
| * | |
| * const schema = Schema.StructWithRest( | |
| * Schema.Struct({ id: Schema.Number }), | |
| * [Schema.Record(Schema.String, Schema.Number)] | |
| * ) | |
| * | |
| * // { readonly id: number, readonly [x: string]: number } | |
| * type T = typeof schema.Type | |
| * ``` | |
| * | |
| * @category constructors | |
| * @since 4.0.0 | |
| */ | |
| export function StructWithRest< | |
| const S extends StructWithRest.Objects, | |
| const Records extends StructWithRest.Records | |
| >( | |
| schema: S, | |
| records: Records & StructWithRest.ValidateRecords<S, Records> | |
| ): StructWithRest<S, Records> { | |
| return make(SchemaAST.structWithRest(schema.ast, records.map(SchemaAST.getAST)), { schema, records }) | |
| } | |
| /** | |
| * Namespace for `Tuple` type utilities. | |
| * | |
| * **Details** | |
| * | |
| * - `Tuple.Elements` — constraint for the element schema array | |
| * - `Tuple.Type<E>` — decoded tuple type | |
| * - `Tuple.Encoded<E>` — encoded tuple type | |
| * - `Tuple.MakeIn<E>` — constructor input tuple | |
| * | |
| * @since 3.10.0 | |
| */ | |
| export declare namespace Tuple { | |
| /** | |
| * Constraint for the readonly array of element schemas used to define a | |
| * fixed-length `Tuple` schema. | |
| * | |
| * @category utility types | |
| * @since 3.10.0 | |
| */ | |
| export type Elements = ReadonlyArray<Top> | |
| type Type_< | |
| Elements, | |
| Out extends ReadonlyArray<any> = readonly [] | |
| > = Elements extends readonly [infer Head, ...infer Tail] ? | |
| Head extends { readonly "Type": infer T } ? | |
| Head extends { readonly "~type.optionality": "optional" } ? Type_<Tail, readonly [...Out, T?]> | |
| : Type_<Tail, readonly [...Out, T]> | |
| : Out | |
| : Out | |
| /** | |
| * Computes the decoded tuple type for a tuple element schema array. | |
| * | |
| * **Details** | |
| * | |
| * Each element contributes its decoded `Type`; optional element schemas produce | |
| * optional tuple positions. | |
| * | |
| * @category utility types | |
| * @since 3.10.0 | |
| */ | |
| export type Type<E extends Elements> = Type_<E> | |
| type Iso_< | |
| Elements, | |
| Out extends ReadonlyArray<any> = readonly [] | |
| > = Elements extends readonly [infer Head, ...infer Tail] ? | |
| Head extends { readonly "Iso": infer T } ? | |
| Head extends { readonly "~type.optionality": "optional" } ? Iso_<Tail, readonly [...Out, T?]> | |
| : Iso_<Tail, readonly [...Out, T]> | |
| : Out | |
| : Out | |
| /** | |
| * Computes the iso tuple type for a tuple element schema array from each | |
| * element schema's `Iso` type. | |
| * | |
| * @category utility types | |
| * @since 4.0.0 | |
| */ | |
| export type Iso<E extends Elements> = Iso_<E> | |
| type Encoded_< | |
| Elements, | |
| Out extends ReadonlyArray<any> = readonly [] | |
| > = Elements extends readonly [infer Head, ...infer Tail] ? | |
| Head extends { readonly "Encoded": infer T } ? | |
| Head extends { readonly "~encoded.optionality": "optional" } ? Encoded_<Tail, readonly [...Out, T?]> | |
| : Encoded_<Tail, readonly [...Out, T]> | |
| : Out | |
| : Out | |
| /** | |
| * Computes the encoded tuple type for a tuple element schema array. | |
| * | |
| * **Details** | |
| * | |
| * Each element contributes its `Encoded` type; encoded-side optional element | |
| * schemas produce optional tuple positions. | |
| * | |
| * @category utility types | |
| * @since 3.10.0 | |
| */ | |
| export type Encoded<E extends Elements> = Encoded_<E> | |
| /** | |
| * Union of all decoding service requirements needed by the tuple element | |
| * schemas. | |
| * | |
| * @category utility types | |
| * @since 4.0.0 | |
| */ | |
| export type DecodingServices<E extends Elements> = E[number]["DecodingServices"] | |
| /** | |
| * Union of all encoding service requirements needed by the tuple element | |
| * schemas. | |
| * | |
| * @category utility types | |
| * @since 4.0.0 | |
| */ | |
| export type EncodingServices<E extends Elements> = E[number]["EncodingServices"] | |
| type MakeIn_< | |
| E, | |
| Out extends ReadonlyArray<any> = readonly [] | |
| > = E extends readonly [infer Head, ...infer Tail] ? | |
| Head extends { "~type.make": infer T } ? | |
| Head extends | |
| { readonly "~type.optionality": "optional" } | { readonly "~type.constructor.default": "with-default" } ? | |
| MakeIn_<Tail, readonly [...Out, T?]> : | |
| MakeIn_<Tail, readonly [...Out, T]> | |
| : Out : | |
| Out | |
| /** | |
| * Computes the input tuple type accepted when constructing a tuple value. | |
| * | |
| * **Details** | |
| * | |
| * Each element uses its `~type.make` input type. Optional elements and elements | |
| * with constructor defaults produce optional tuple positions. | |
| * | |
| * @category utility types | |
| * @since 4.0.0 | |
| */ | |
| export type MakeIn<E extends Elements> = MakeIn_<E> | |
| } | |
| /** | |
| * Type-level representation returned by {@link Tuple}. | |
| * | |
| * @category models | |
| * @since 3.10.0 | |
| */ | |
| export interface Tuple<Elements extends Tuple.Elements> extends | |
| Bottom< | |
| Tuple.Type<Elements>, | |
| Tuple.Encoded<Elements>, | |
| Tuple.DecodingServices<Elements>, | |
| Tuple.EncodingServices<Elements>, | |
| SchemaAST.Arrays, | |
| Tuple<Elements>, | |
| Tuple.MakeIn<Elements>, | |
| Tuple.Iso<Elements> | |
| > | |
| { | |
| readonly elements: Elements | |
| /** | |
| * Returns a new tuple with the elements modified by the provided function. | |
| * | |
| * **Details** | |
| * | |
| * Options: | |
| * | |
| * - `unsafePreserveChecks` - if `true`, keep any `.check(...)` constraints | |
| * that were attached to the original union. Defaults to `false`. | |
| * | |
| * **Warning**: This is an unsafe operation. Since `mapFields` | |
| * transformations change the schema type, the original refinement functions | |
| * may no longer be valid or safe to apply to the transformed schema. Only | |
| * use this option if you have verified that your refinements remain correct | |
| * after the transformation. | |
| */ | |
| mapElements<To extends Tuple.Elements>( | |
| f: (elements: Elements) => To, | |
| options?: { | |
| readonly unsafePreserveChecks?: boolean | undefined | |
| } | undefined | |
| ): Tuple<Simplify<Readonly<To>>> | |
| } | |
| function makeTuple<Elements extends Tuple.Elements>(ast: SchemaAST.Arrays, elements: Elements): Tuple<Elements> { | |
| return make(ast, { | |
| elements, | |
| mapElements<To extends Tuple.Elements>( | |
| this: Tuple<Elements>, | |
| f: (elements: Elements) => To, | |
| options?: { | |
| readonly unsafePreserveChecks?: boolean | undefined | |
| } | undefined | |
| ): Tuple<Simplify<Readonly<To>>> { | |
| const elements = f(this.elements) | |
| return makeTuple(SchemaAST.tuple(elements, options?.unsafePreserveChecks ? this.ast.checks : undefined), elements) | |
| } | |
| }) | |
| } | |
| /** | |
| * Defines a fixed-length tuple schema from an array of element schemas. | |
| * | |
| * **Example** (Pair of string and number) | |
| * | |
| * ```ts | |
| * import { Schema } from "effect" | |
| * | |
| * const schema = Schema.Tuple([Schema.String, Schema.Number]) | |
| * | |
| * const pair = Schema.decodeUnknownSync(schema)(["hello", 42]) | |
| * console.log(pair) | |
| * // [ 'hello', 42 ] | |
| * ``` | |
| * | |
| * @category constructors | |
| * @since 3.10.0 | |
| */ | |
| export function Tuple<const Elements extends ReadonlyArray<Top>>(elements: Elements): Tuple<Elements> { | |
| return makeTuple(SchemaAST.tuple(elements), elements) | |
| } | |
| /** | |
| * Namespace for `TupleWithRest` type utilities. | |
| * | |
| * **Details** | |
| * | |
| * - `TupleWithRest.TupleType` — constraint for the leading tuple schema | |
| * - `TupleWithRest.Rest` — the rest element schema(s) | |
| * - `TupleWithRest.Type<T, R>` — decoded type (fixed elements + rest) | |
| * - `TupleWithRest.Encoded<T, R>` — encoded type | |
| * | |
| * @since 4.0.0 | |
| */ | |
| export declare namespace TupleWithRest { | |
| /** | |
| * Constraint for tuple-like schemas that can be used as the fixed leading | |
| * portion of a `TupleWithRest` schema. | |
| * | |
| * @category utility types | |
| * @since 3.10.0 | |
| */ | |
| export type TupleType = Top & { | |
| readonly Type: ReadonlyArray<unknown> | |
| readonly Encoded: ReadonlyArray<unknown> | |
| readonly ast: SchemaAST.Arrays | |
| readonly "~type.make": ReadonlyArray<unknown> | |
| readonly "Iso": ReadonlyArray<unknown> | |
| } | |
| /** | |
| * Non-empty list of schemas used for the rest portion of a `TupleWithRest`. | |
| * | |
| * **Details** | |
| * | |
| * The first schema describes the repeated rest element. Additional schemas, when | |
| * present, describe trailing tuple elements after the repeated rest segment. | |
| * | |
| * @category utility types | |
| * @since 3.10.0 | |
| */ | |
| export type Rest = readonly [Top, ...Array<Top>] | |
| /** | |
| * Computes the decoded tuple type for a `TupleWithRest`. | |
| * | |
| * **Details** | |
| * | |
| * The output starts with the fixed tuple elements, continues with zero or more | |
| * values decoded by the first rest schema, and includes any trailing rest schemas | |
| * as fixed tuple positions. | |
| * | |
| * @category utility types | |
| * @since 3.10.0 | |
| */ | |
| export type Type<T extends ReadonlyArray<unknown>, Rest extends TupleWithRest.Rest> = Rest extends | |
| readonly [infer Head extends Top, ...infer Tail extends ReadonlyArray<Top>] ? Readonly<[ | |
| ...T, | |
| ...Array<Head["Type"]>, | |
| ...{ readonly [K in keyof Tail]: Tail[K]["Type"] } | |
| ]> : | |
| T | |
| /** | |
| * Computes the iso tuple type for a `TupleWithRest`. | |
| * | |
| * **Details** | |
| * | |
| * The output starts with the fixed tuple's `Iso` elements, continues with zero | |
| * or more values using the first rest schema's `Iso`, and includes any trailing | |
| * rest schemas as fixed tuple positions. | |
| * | |
| * @category utility types | |
| * @since 4.0.0 | |
| */ | |
| export type Iso<T extends ReadonlyArray<unknown>, Rest extends TupleWithRest.Rest> = Rest extends | |
| readonly [infer Head extends Top, ...infer Tail extends ReadonlyArray<Top>] ? Readonly<[ | |
| ...T, | |
| ...Array<Head["Iso"]>, | |
| ...{ readonly [K in keyof Tail]: Tail[K]["Iso"] } | |
| ]> : | |
| T | |
| /** | |
| * Computes the encoded tuple type for `TupleWithRest`. | |
| * | |
| * **Details** | |
| * | |
| * The leading tuple's encoded elements are kept first. The encoded type of the | |
| * first rest schema may repeat zero or more times, and the encoded types of any | |
| * additional rest schemas become required trailing tuple elements. | |
| * | |
| * @category utility types | |
| * @since 3.10.0 | |
| */ | |
| export type Encoded<E extends ReadonlyArray<unknown>, Rest extends TupleWithRest.Rest> = Rest extends | |
| readonly [infer Head extends Top, ...infer Tail extends ReadonlyArray<Top>] ? readonly [ | |
| ...E, | |
| ...Array<Head["Encoded"]>, | |
| ...{ readonly [K in keyof Tail]: Tail[K]["Encoded"] } | |
| ] : | |
| E | |
| /** | |
| * Computes the constructor input tuple type for `TupleWithRest`. | |
| * | |
| * **Details** | |
| * | |
| * The leading tuple's make input elements are kept first. The make input type of | |
| * the first rest schema may repeat zero or more times, and the make input types | |
| * of any additional rest schemas become required trailing tuple elements. | |
| * | |
| * @category utility types | |
| * @since 4.0.0 | |
| */ | |
| export type MakeIn<M extends ReadonlyArray<unknown>, Rest extends TupleWithRest.Rest> = Rest extends | |
| readonly [infer Head extends Top, ...infer Tail extends ReadonlyArray<Top>] ? readonly [ | |
| ...M, | |
| ...Array<Head["~type.make"]>, | |
| ...{ readonly [K in keyof Tail]: Tail[K]["~type.make"] } | |
| ] : | |
| M | |
| } | |
| /** | |
| * Type-level representation returned by {@link TupleWithRest}. | |
| * | |
| * @category models | |
| * @since 4.0.0 | |
| */ | |
| export interface TupleWithRest< | |
| S extends TupleWithRest.TupleType, | |
| Rest extends TupleWithRest.Rest | |
| > extends | |
| Bottom< | |
| TupleWithRest.Type<S["Type"], Rest>, | |
| TupleWithRest.Encoded<S["Encoded"], Rest>, | |
| S["DecodingServices"] | Rest[number]["DecodingServices"], | |
| S["EncodingServices"] | Rest[number]["EncodingServices"], | |
| SchemaAST.Arrays, | |
| TupleWithRest<S, Rest>, | |
| TupleWithRest.MakeIn<S["~type.make"], Rest>, | |
| TupleWithRest.Iso<S["Iso"], Rest> | |
| > | |
| { | |
| readonly schema: S | |
| readonly rest: Rest | |
| } | |
| /** | |
| * Extends a fixed-length tuple schema with a variadic rest segment. | |
| * | |
| * **Details** | |
| * | |
| * The resulting tuple starts with the fixed elements from `schema`. The first | |
| * schema in `rest` is the repeatable element schema, and any additional schemas | |
| * in `rest` are required trailing tuple elements after the variadic segment. For | |
| * example, `[Schema.Boolean, Schema.String]` represents zero or more booleans | |
| * followed by a final string. | |
| * | |
| * **Example** (Tuple with rest) | |
| * | |
| * ```ts | |
| * import { Schema } from "effect" | |
| * | |
| * // [string, number, ...boolean[]] | |
| * const schema = Schema.TupleWithRest( | |
| * Schema.Tuple([Schema.String, Schema.Number]), | |
| * [Schema.Boolean] | |
| * ) | |
| * | |
| * const result = Schema.decodeUnknownSync(schema)(["hello", 1, true, false]) | |
| * console.log(result) | |
| * // [ 'hello', 1, true, false ] | |
| * ``` | |
| * | |
| * @category constructors | |
| * @since 4.0.0 | |
| */ | |
| export function TupleWithRest<S extends Tuple<Tuple.Elements>, const Rest extends TupleWithRest.Rest>( | |
| schema: S, | |
| rest: Rest | |
| ): TupleWithRest<S, Rest> { | |
| return make(SchemaAST.tupleWithRest(schema.ast, rest.map(SchemaAST.getAST)), { schema, rest }) | |
| } | |
| /** | |
| * Type-level representation returned by {@link Array}. | |
| * | |
| * @category models | |
| * @since 4.0.0 | |
| */ | |
| export interface $Array<S extends Top> extends | |
| Bottom< | |
| ReadonlyArray<S["Type"]>, | |
| ReadonlyArray<S["Encoded"]>, | |
| S["DecodingServices"], | |
| S["EncodingServices"], | |
| SchemaAST.Arrays, | |
| $Array<S>, | |
| ReadonlyArray<S["~type.make"]>, | |
| ReadonlyArray<S["Iso"]> | |
| > | |
| { | |
| readonly value: S | |
| } | |
| interface ArrayLambda extends Lambda { | |
| <S extends Top>(self: S): $Array<S> | |
| readonly "~lambda.out": this["~lambda.in"] extends Top ? $Array<this["~lambda.in"]> : never | |
| } | |
| /** | |
| * @category constructors | |
| * @since 4.0.0 | |
| */ | |
| const ArraySchema = Struct_.lambda<ArrayLambda>((schema) => | |
| make(new SchemaAST.Arrays(false, [], [schema.ast]), { value: schema }) | |
| ) | |
| export { | |
| /** | |
| * Defines a `ReadonlyArray` schema for a given element schema. | |
| * | |
| * **Example** (Array of strings) | |
| * | |
| * ```ts | |
| * import { Schema } from "effect" | |
| * | |
| * const schema = Schema.Array(Schema.String) | |
| * | |
| * const result = Schema.decodeUnknownSync(schema)(["a", "b", "c"]) | |
| * console.log(result) | |
| * // [ 'a', 'b', 'c' ] | |
| * ``` | |
| * | |
| * @category constructors | |
| * @since 4.0.0 | |
| */ | |
| ArraySchema as Array | |
| } | |
| /** | |
| * Type-level representation returned by {@link NonEmptyArray}. | |
| * | |
| * @category models | |
| * @since 3.10.0 | |
| */ | |
| export interface NonEmptyArray<S extends Top> extends | |
| Bottom< | |
| readonly [S["Type"], ...Array<S["Type"]>], | |
| readonly [S["Encoded"], ...Array<S["Encoded"]>], | |
| S["DecodingServices"], | |
| S["EncodingServices"], | |
| SchemaAST.Arrays, | |
| NonEmptyArray<S>, | |
| readonly [S["~type.make"], ...Array<S["~type.make"]>], | |
| readonly [S["Iso"], ...Array<S["Iso"]>] | |
| > | |
| { | |
| readonly value: S | |
| } | |
| interface NonEmptyArrayLambda extends Lambda { | |
| <S extends Top>(self: S): NonEmptyArray<S> | |
| readonly "~lambda.out": this["~lambda.in"] extends Top ? NonEmptyArray<this["~lambda.in"]> : never | |
| } | |
| /** | |
| * Defines a non-empty `ReadonlyArray` schema — at least one element required. | |
| * Type is `readonly [T, ...T[]]`. | |
| * | |
| * **Example** (Non-empty array of numbers) | |
| * | |
| * ```ts | |
| * import { Schema } from "effect" | |
| * | |
| * const schema = Schema.NonEmptyArray(Schema.Number) | |
| * | |
| * Schema.decodeUnknownSync(schema)([1, 2, 3]) // ok | |
| * Schema.decodeUnknownSync(schema)([]) // throws | |
| * ``` | |
| * | |
| * @category constructors | |
| * @since 3.10.0 | |
| */ | |
| export const NonEmptyArray = Struct_.lambda<NonEmptyArrayLambda>((schema) => | |
| make(new SchemaAST.Arrays(false, [schema.ast], [schema.ast]), { value: schema }) | |
| ) | |
| /** | |
| * Type-level representation returned by {@link ArrayEnsure}. | |
| * | |
| * @category constructors | |
| * @since 3.10.0 | |
| */ | |
| export interface ArrayEnsure<S extends Top> extends decodeTo<$Array<toType<S>>, Union<readonly [S, $Array<S>]>> { | |
| readonly "Rebuild": ArrayEnsure<S> | |
| } | |
| /** | |
| * Creates a schema that accepts either a value decoded by `schema` or an array | |
| * decoded by `Schema.Array(schema)`, then returns an array. | |
| * | |
| * **When to use** | |
| * | |
| * Use to accept input that may be provided either as one item or as an array, | |
| * while normalizing decoded values to a readonly array. | |
| * | |
| * **Details** | |
| * | |
| * During encoding, one-element arrays are encoded as the single element. Empty | |
| * arrays and arrays with two or more elements are encoded as arrays. | |
| * | |
| * **Gotchas** | |
| * | |
| * The single-value branch is tried before the array branch. If `schema` itself | |
| * accepts arrays, an array input can be treated as one value and wrapped in a | |
| * one-element array. | |
| * | |
| * @see {@link Array} for accepting only array input | |
| * @see {@link NonEmptyArray} for requiring at least one decoded element | |
| * | |
| * @category constructors | |
| * @since 3.10.0 | |
| */ | |
| export function ArrayEnsure<S extends Top>(schema: S): ArrayEnsure<S> { | |
| return Union([schema, ArraySchema(schema)]).pipe(decodeTo( | |
| ArraySchema(toType(schema)), | |
| SchemaTransformation.transform({ | |
| decode: Arr.ensure, | |
| encode: (array) => array.length === 1 ? array[0] : array | |
| }) | |
| )) | |
| } | |
| /** | |
| * Type-level representation returned by {@link UniqueArray}. | |
| * | |
| * @category models | |
| * @since 4.0.0 | |
| */ | |
| export interface UniqueArray<S extends Top> extends $Array<S> { | |
| readonly "Rebuild": UniqueArray<S> | |
| } | |
| /** | |
| * Returns a new array schema that ensures all elements are unique. | |
| * | |
| * **Details** | |
| * | |
| * The equivalence used to determine uniqueness is the one provided by | |
| * `Schema.toEquivalence(item)`. | |
| * | |
| * @category constructors | |
| * @since 4.0.0 | |
| */ | |
| export function UniqueArray<S extends Top>(item: S): UniqueArray<S> { | |
| return ArraySchema(item).check(isUnique()) | |
| } | |
| /** | |
| * Type-level representation returned by {@link mutable}. | |
| * | |
| * @category transforming | |
| * @since 3.10.0 | |
| */ | |
| export interface mutable<S extends Top & { readonly "ast": SchemaAST.Arrays }> extends | |
| Bottom< | |
| Mutable<S["Type"]>, | |
| Mutable<S["Encoded"]>, | |
| S["DecodingServices"], | |
| S["EncodingServices"], | |
| S["ast"], | |
| mutable<S>, | |
| // "~type.make" and "~type.make.in" as they are because they are contravariant | |
| S["~type.make.in"], | |
| S["Iso"], | |
| S["~type.parameters"], | |
| S["~type.make"], | |
| S["~type.mutability"], | |
| S["~type.optionality"], | |
| S["~type.constructor.default"], | |
| S["~encoded.mutability"], | |
| S["~encoded.optionality"] | |
| > | |
| { | |
| readonly schema: S | |
| } | |
| interface mutableLambda extends Lambda { | |
| <S extends Top & { readonly "ast": SchemaAST.Arrays }>(self: S): mutable<S> | |
| readonly "~lambda.out": this["~lambda.in"] extends Top & { readonly "ast": SchemaAST.Arrays } ? | |
| mutable<this["~lambda.in"]> | |
| : "Error: schema not eligible for mutable" | |
| } | |
| /** | |
| * Makes an array or tuple schema mutable, removing the `readonly` modifier. | |
| * | |
| * **Example** (Mutable array) | |
| * | |
| * ```ts | |
| * import { Schema } from "effect" | |
| * | |
| * const schema = Schema.mutable(Schema.Array(Schema.Number)) | |
| * | |
| * // number[] (mutable) | |
| * type T = typeof schema.Type | |
| * ``` | |
| * | |
| * @category transforming | |
| * @since 3.10.0 | |
| */ | |
| export const mutable = Struct_.lambda<mutableLambda>((schema) => { | |
| return make(new SchemaAST.Arrays(true, schema.ast.elements, schema.ast.rest), { schema }) | |
| }) | |
| /** | |
| * Type-level representation returned by {@link Union}. | |
| * | |
| * @category models | |
| * @since 3.10.0 | |
| */ | |
| export interface Union<Members extends ReadonlyArray<Top>> extends | |
| Bottom< | |
| { [K in keyof Members]: Members[K]["Type"] }[number], | |
| { [K in keyof Members]: Members[K]["Encoded"] }[number], | |
| { [K in keyof Members]: Members[K]["DecodingServices"] }[number], | |
| { [K in keyof Members]: Members[K]["EncodingServices"] }[number], | |
| SchemaAST.Union<{ [K in keyof Members]: Members[K]["ast"] }[number]>, | |
| Union<Members>, | |
| { [K in keyof Members]: Members[K]["~type.make"] }[number], | |
| { [K in keyof Members]: Members[K]["Iso"] }[number] | |
| > | |
| { | |
| readonly members: Members | |
| /** | |
| * Returns a new union with the members modified by the provided function. | |
| * | |
| * **Details** | |
| * | |
| * Options: | |
| * | |
| * - `unsafePreserveChecks` - if `true`, keep any `.check(...)` constraints | |
| * that were attached to the original union. Defaults to `false`. | |
| * | |
| * **Warning**: This is an unsafe operation. Since `mapFields` | |
| * transformations change the schema type, the original refinement functions | |
| * may no longer be valid or safe to apply to the transformed schema. Only | |
| * use this option if you have verified that your refinements remain correct | |
| * after the transformation. | |
| */ | |
| mapMembers<To extends ReadonlyArray<Top>>( | |
| f: (members: Members) => To, | |
| options?: { | |
| readonly unsafePreserveChecks?: boolean | undefined | |
| } | undefined | |
| ): Union<Simplify<Readonly<To>>> | |
| } | |
| function makeUnion<Members extends ReadonlyArray<Top>>( | |
| ast: SchemaAST.Union<Members[number]["ast"]>, | |
| members: Members | |
| ): Union<Members> { | |
| return make(ast, { | |
| members, | |
| mapMembers<To extends ReadonlyArray<Top>>( | |
| this: Union<Members>, | |
| f: (members: Members) => To, | |
| options?: { | |
| readonly unsafePreserveChecks?: boolean | undefined | |
| } | undefined | |
| ): Union<Simplify<Readonly<To>>> { | |
| const members = f(this.members) | |
| return makeUnion( | |
| SchemaAST.union(members, this.ast.mode, options?.unsafePreserveChecks ? this.ast.checks : undefined), | |
| members | |
| ) | |
| } | |
| }) | |
| } | |
| /** | |
| * Creates a union schema from an array of member schemas. Members are tested in | |
| * order; the first match is returned. | |
| * | |
| * **Details** | |
| * | |
| * Optionally, specify `mode`: | |
| * - `"anyOf"` (default) — matches if any member matches. | |
| * - `"oneOf"` — matches if exactly one member matches. | |
| * | |
| * **Example** (String or number union) | |
| * | |
| * ```ts | |
| * import { Schema } from "effect" | |
| * | |
| * const schema = Schema.Union([Schema.String, Schema.Number]) | |
| * | |
| * Schema.decodeUnknownSync(schema)("hello") // "hello" | |
| * Schema.decodeUnknownSync(schema)(42) // 42 | |
| * ``` | |
| * | |
| * @category constructors | |
| * @since 3.10.0 | |
| */ | |
| export function Union<const Members extends ReadonlyArray<Top>>( | |
| members: Members, | |
| options?: { mode?: "anyOf" | "oneOf" } | |
| ): Union<Members> { | |
| return makeUnion(SchemaAST.union(members, options?.mode ?? "anyOf", undefined), members) | |
| } | |
| /** | |
| * Type-level representation returned by {@link Literals}. | |
| * | |
| * @category models | |
| * @since 4.0.0 | |
| */ | |
| export interface Literals<L extends ReadonlyArray<SchemaAST.LiteralValue>> | |
| extends Bottom<L[number], L[number], never, never, SchemaAST.Union<SchemaAST.Literal>, Literals<L>> | |
| { | |
| readonly literals: L | |
| readonly members: { readonly [K in keyof L]: Literal<L[K]> } | |
| /** | |
| * Map over the members of the union. | |
| */ | |
| mapMembers<To extends ReadonlyArray<Top>>(f: (members: this["members"]) => To): Union<Simplify<Readonly<To>>> | |
| pick<const L2 extends ReadonlyArray<L[number]>>(literals: L2): Literals<L2> | |
| transform<const L2 extends { readonly [I in keyof L]: SchemaAST.LiteralValue }>( | |
| to: L2 | |
| ): Union<{ [I in keyof L]: decodeTo<Literal<L2[I]>, Literal<L[I]>> }> | |
| } | |
| /** | |
| * Creates a union schema from an array of literal values. | |
| * | |
| * **Example** (Status codes) | |
| * | |
| * ```ts | |
| * import { Schema } from "effect" | |
| * | |
| * const schema = Schema.Literals(["active", "inactive", "pending"]) | |
| * // accepts "active", "inactive", or "pending" | |
| * ``` | |
| * | |
| * @see {@link Literal} for a schema that represents a single literal. | |
| * @category constructors | |
| * @since 4.0.0 | |
| */ | |
| export function Literals<const L extends ReadonlyArray<SchemaAST.LiteralValue>>(literals: L): Literals<L> { | |
| const members = literals.map(Literal) as { readonly [K in keyof L]: Literal<L[K]> } | |
| return make(SchemaAST.union(members, "anyOf", undefined), { | |
| literals, | |
| members, | |
| mapMembers<To extends ReadonlyArray<Top>>( | |
| this: Literals<L>, | |
| f: (members: Literals<L>["members"]) => To | |
| ): Union<Simplify<Readonly<To>>> { | |
| return Union(f(this.members)) | |
| }, | |
| pick<const L2 extends ReadonlyArray<L[number]>>(literals: L2): Literals<L2> { | |
| return Literals(literals) | |
| }, | |
| transform<const L2 extends { readonly [I in keyof L]: SchemaAST.LiteralValue }>( | |
| to: L2 | |
| ): Union<{ [I in keyof L]: decodeTo<Literal<L2[I]>, Literal<L[I]>> }> { | |
| return Union(members.map((member, index) => member.transform(to[index]))) as any | |
| } | |
| }) | |
| } | |
| /** | |
| * Type-level representation returned by {@link NullOr}. | |
| * | |
| * @category models | |
| * @since 3.10.0 | |
| */ | |
| export interface NullOr<S extends Top> extends Union<readonly [S, Null]> { | |
| readonly "Rebuild": NullOr<S> | |
| } | |
| interface NullOrLambda extends Lambda { | |
| <S extends Top>(self: S): NullOr<S> | |
| readonly "~lambda.out": this["~lambda.in"] extends Top ? NullOr<this["~lambda.in"]> : never | |
| } | |
| /** | |
| * Creates a union schema of `S | null`. | |
| * | |
| * @category constructors | |
| * @since 3.10.0 | |
| */ | |
| export const NullOr = Struct_.lambda<NullOrLambda>((self) => Union([self, Null])) | |
| /** | |
| * Type-level representation returned by {@link UndefinedOr}. | |
| * | |
| * @category models | |
| * @since 3.10.0 | |
| */ | |
| export interface UndefinedOr<S extends Top> extends Union<readonly [S, Undefined]> { | |
| readonly "Rebuild": UndefinedOr<S> | |
| } | |
| interface UndefinedOrLambda extends Lambda { | |
| <S extends Top>(self: S): UndefinedOr<S> | |
| readonly "~lambda.out": this["~lambda.in"] extends Top ? UndefinedOr<this["~lambda.in"]> : never | |
| } | |
| /** | |
| * Creates a union schema of `S | undefined`. | |
| * | |
| * @category constructors | |
| * @since 3.10.0 | |
| */ | |
| export const UndefinedOr = Struct_.lambda<UndefinedOrLambda>((self) => Union([self, Undefined])) | |
| /** | |
| * Type-level representation returned by {@link NullishOr}. | |
| * | |
| * @category models | |
| * @since 3.10.0 | |
| */ | |
| export interface NullishOr<S extends Top> extends Union<readonly [S, Null, Undefined]> { | |
| readonly "Rebuild": NullishOr<S> | |
| } | |
| interface NullishOrLambda extends Lambda { | |
| <S extends Top>(self: S): NullishOr<S> | |
| readonly "~lambda.out": this["~lambda.in"] extends Top ? NullishOr<this["~lambda.in"]> : never | |
| } | |
| /** | |
| * Creates a union schema of `S | null | undefined`. | |
| * | |
| * @category constructors | |
| * @since 3.10.0 | |
| */ | |
| export const NullishOr = Struct_.lambda<NullishOrLambda>((self) => Union([self, Null, Undefined])) | |
| /** | |
| * Type-level representation returned by {@link suspend}. | |
| * | |
| * @category models | |
| * @since 3.10.0 | |
| */ | |
| export interface suspend<S extends Top> extends | |
| Bottom< | |
| S["Type"], | |
| S["Encoded"], | |
| S["DecodingServices"], | |
| S["EncodingServices"], | |
| SchemaAST.Suspend, | |
| suspend<S>, | |
| S["~type.make.in"], | |
| S["Iso"], | |
| S["~type.parameters"], | |
| S["~type.make"], | |
| S["~type.mutability"], | |
| S["~type.optionality"], | |
| S["~type.constructor.default"], | |
| S["~encoded.mutability"], | |
| S["~encoded.optionality"] | |
| > | |
| {} | |
| /** | |
| * Creates a suspended schema that defers evaluation until needed. This is | |
| * essential for creating recursive schemas where a schema references itself, | |
| * preventing infinite recursion during schema definition. | |
| * | |
| * **Example** (Recursive tree schema) | |
| * | |
| * ```ts | |
| * import { Schema } from "effect" | |
| * | |
| * interface Tree { | |
| * readonly value: number | |
| * readonly children: ReadonlyArray<Tree> | |
| * } | |
| * | |
| * const Tree = Schema.Struct({ | |
| * value: Schema.Number, | |
| * children: Schema.Array(Schema.suspend((): Schema.Codec<Tree> => Tree)) | |
| * }) | |
| * ``` | |
| * | |
| * @category constructors | |
| * @since 3.10.0 | |
| */ | |
| export function suspend<S extends Top>(f: () => S): suspend<S> { | |
| return make(new SchemaAST.Suspend(() => f().ast)) | |
| } | |
| /** | |
| * Attaches one or more filter checks to a schema without changing the | |
| * TypeScript type. | |
| * | |
| * **Example** (Adding checks to a schema) | |
| * | |
| * ```ts | |
| * import { Schema } from "effect" | |
| * | |
| * const AgeSchema = Schema.Number.pipe( | |
| * Schema.check(Schema.isGreaterThanOrEqualTo(0), Schema.isLessThanOrEqualTo(120)) | |
| * ) | |
| * ``` | |
| * | |
| * @category filtering | |
| * @since 4.0.0 | |
| */ | |
| export function check<S extends Top>( | |
| ...checks: readonly [SchemaAST.Check<S["Type"]>, ...Array<SchemaAST.Check<S["Type"]>>] | |
| ) { | |
| return (self: S): S["Rebuild"] => self.check(...checks) | |
| } | |
| /** | |
| * Type-level representation returned by {@link refine}. | |
| * | |
| * @category filtering | |
| * @since 3.10.0 | |
| */ | |
| export interface refine<T extends S["Type"], S extends Top> extends | |
| Bottom< | |
| T, | |
| S["Encoded"], | |
| S["DecodingServices"], | |
| S["EncodingServices"], | |
| S["ast"], | |
| refine<T, S>, | |
| S["~type.make.in"], | |
| T, | |
| S["~type.parameters"], | |
| T, | |
| S["~type.mutability"], | |
| S["~type.optionality"], | |
| S["~type.constructor.default"], | |
| S["~encoded.mutability"], | |
| S["~encoded.optionality"] | |
| > | |
| { | |
| readonly schema: S | |
| } | |
| /** | |
| * Narrows the TypeScript type of a schema's output via a type guard predicate, | |
| * attaching the guard as a runtime filter check. | |
| * | |
| * **Details** | |
| * | |
| * The `annotations` parameter annotates the filter created by the refinement. | |
| * With the default formatter, failed refinements use `message` first, | |
| * `expected` second, and `<filter>` when neither is provided. `identifier` | |
| * names type-level failures before the refinement runs; it does not name the | |
| * failed refinement itself. | |
| * | |
| * @category filtering | |
| * @since 3.10.0 | |
| */ | |
| export function refine<S extends Top, T extends S["Type"]>( | |
| refinement: (value: S["Type"]) => value is T, | |
| annotations?: Annotations.Filter | |
| ) { | |
| return (schema: S): refine<T, S> => | |
| make(SchemaAST.appendChecks(schema.ast, [SchemaAST.makeFilterByGuard(refinement, annotations)]), { schema }) | |
| } | |
| type DistributeBrands<B> = UnionToIntersection<B extends infer U extends string ? Brand.Brand<U> : never> | |
| /** | |
| * Type-level representation returned by {@link brand}. | |
| * | |
| * @category branding | |
| * @since 3.10.0 | |
| */ | |
| export interface brand<S extends Top, B> extends | |
| Bottom< | |
| S["Type"] & DistributeBrands<B>, | |
| S["Encoded"], | |
| S["DecodingServices"], | |
| S["EncodingServices"], | |
| S["ast"], | |
| brand<S, B>, | |
| S["~type.make.in"], | |
| S["Type"] & DistributeBrands<B>, | |
| S["~type.parameters"], | |
| S["Type"] & DistributeBrands<B>, | |
| S["~type.mutability"], | |
| S["~type.optionality"], | |
| S["~type.constructor.default"], | |
| S["~encoded.mutability"], | |
| S["~encoded.optionality"] | |
| > | |
| { | |
| readonly schema: S | |
| readonly identifier: string | |
| } | |
| /** | |
| * Adds a nominal brand to a schema, intersecting the output type with | |
| * `Brand.Brand<B>` to prevent accidental mixing of structurally identical types. | |
| * | |
| * **When to use** | |
| * | |
| * Use to make values decoded by an existing schema nominally distinct when the | |
| * schema already carries the runtime validation you need. | |
| * | |
| * **Gotchas** | |
| * | |
| * `brand` adds brand metadata and narrows the TypeScript output type, but it | |
| * does not add runtime checks. | |
| * | |
| * @see {@link fromBrand} for applying a Brand constructor's checks along with the brand tag | |
| * | |
| * @category branding | |
| * @since 3.10.0 | |
| */ | |
| export function brand<B extends string>(identifier: B) { | |
| return <S extends Top>(schema: S): brand<S["Rebuild"], B> => | |
| make(SchemaAST.brand(schema.ast, identifier), { schema, identifier }) | |
| } | |
| /** | |
| * Creates a branded schema from a {@link Brand.Constructor}, applying the | |
| * constructor's checks and brand tag to the underlying schema. | |
| * | |
| * @category branding | |
| * @since 3.10.0 | |
| */ | |
| export function fromBrand<A extends Brand.Brand<any>>(identifier: string, ctor: Brand.Constructor<A>) { | |
| return <S extends Top & { readonly "Type": Brand.Brand.Unbranded<A> }>( | |
| self: S | |
| ): brand<S["Rebuild"], Brand.Brand.Keys<A>> => { | |
| return (ctor.checks ? self.check(...ctor.checks) : self).pipe(brand(identifier)) | |
| } | |
| } | |
| /** | |
| * Type-level representation returned by {@link middlewareDecoding}. | |
| * | |
| * @category decoding | |
| * @since 4.0.0 | |
| */ | |
| export interface middlewareDecoding<S extends Top, RD> extends | |
| Bottom< | |
| S["Type"], | |
| S["Encoded"], | |
| RD, | |
| S["EncodingServices"], | |
| S["ast"], | |
| middlewareDecoding<S, RD>, | |
| S["~type.make.in"], | |
| S["Iso"], | |
| S["~type.parameters"], | |
| S["~type.make"], | |
| S["~type.mutability"], | |
| S["~type.optionality"], | |
| S["~type.constructor.default"], | |
| S["~encoded.mutability"], | |
| S["~encoded.optionality"] | |
| > | |
| { | |
| readonly schema: S | |
| } | |
| /** | |
| * Intercepts the decoding pipeline of a schema. | |
| * | |
| * **Details** | |
| * | |
| * The provided function receives the current decoding `Effect` and `ParseOptions`, | |
| * and returns a new `Effect` — potentially adding service requirements (`RD`), | |
| * recovering from errors, or augmenting the result. | |
| * | |
| * **Example** (Logging decode failures) | |
| * | |
| * ```ts | |
| * import { Effect, Schema } from "effect" | |
| * | |
| * const Logged = Schema.String.pipe( | |
| * Schema.middlewareDecoding((effect) => | |
| * Effect.tapError(effect, (issue) => Effect.log("decode failed", issue)) | |
| * ) | |
| * ) | |
| * ``` | |
| * | |
| * @see {@link catchDecoding} for a simpler error-recovery variant | |
| * @category decoding | |
| * @since 4.0.0 | |
| */ | |
| export function middlewareDecoding<S extends Top, RD>( | |
| decode: ( | |
| effect: Effect.Effect<Option_.Option<S["Type"]>, SchemaIssue.Issue, S["DecodingServices"]>, | |
| options: SchemaAST.ParseOptions | |
| ) => Effect.Effect<Option_.Option<S["Type"]>, SchemaIssue.Issue, RD> | |
| ) { | |
| return (schema: S): middlewareDecoding<S, RD> => | |
| make( | |
| SchemaAST.middlewareDecoding(schema.ast, new SchemaTransformation.Middleware(decode, identity)), | |
| { schema } | |
| ) | |
| } | |
| /** | |
| * Type-level representation returned by {@link middlewareEncoding}. | |
| * | |
| * @category encoding | |
| * @since 4.0.0 | |
| */ | |
| export interface middlewareEncoding<S extends Top, RE> extends | |
| Bottom< | |
| S["Type"], | |
| S["Encoded"], | |
| S["DecodingServices"], | |
| RE, | |
| S["ast"], | |
| middlewareEncoding<S, RE>, | |
| S["~type.make.in"], | |
| S["Iso"], | |
| S["~type.parameters"], | |
| S["~type.make"], | |
| S["~type.mutability"], | |
| S["~type.optionality"], | |
| S["~type.constructor.default"], | |
| S["~encoded.mutability"], | |
| S["~encoded.optionality"] | |
| > | |
| { | |
| readonly schema: S | |
| } | |
| /** | |
| * Intercepts the encoding pipeline of a schema. | |
| * | |
| * **Details** | |
| * | |
| * The provided function receives the current encoding `Effect` and `ParseOptions`, | |
| * and returns a new `Effect` — potentially adding service requirements (`RE`), | |
| * recovering from errors, or augmenting the result. | |
| * | |
| * **Example** (Logging encode failures) | |
| * | |
| * ```ts | |
| * import { Effect, Schema } from "effect" | |
| * | |
| * const Logged = Schema.String.pipe( | |
| * Schema.middlewareEncoding((effect) => | |
| * Effect.tapError(effect, (issue) => Effect.log("encode failed", issue)) | |
| * ) | |
| * ) | |
| * ``` | |
| * | |
| * @see {@link catchEncoding} for a simpler error-recovery variant | |
| * @category encoding | |
| * @since 4.0.0 | |
| */ | |
| export function middlewareEncoding<S extends Top, RE>( | |
| encode: ( | |
| effect: Effect.Effect<Option_.Option<S["Encoded"]>, SchemaIssue.Issue, S["EncodingServices"]>, | |
| options: SchemaAST.ParseOptions | |
| ) => Effect.Effect<Option_.Option<S["Encoded"]>, SchemaIssue.Issue, RE> | |
| ) { | |
| return (schema: S): middlewareEncoding<S, RE> => | |
| make( | |
| SchemaAST.middlewareEncoding(schema.ast, new SchemaTransformation.Middleware(identity, encode)), | |
| { schema } | |
| ) | |
| } | |
| /** | |
| * Recovers from a decoding error by providing a fallback value. | |
| * | |
| * **Details** | |
| * | |
| * The handler receives the `Issue` and returns an `Effect` that either | |
| * succeeds with a fallback value or re-fails with a (possibly different) issue. | |
| * | |
| * **Example** (Returning a default on decode failure) | |
| * | |
| * ```ts | |
| * import { Effect, Option, Schema } from "effect" | |
| * | |
| * const schema = Schema.Number.pipe( | |
| * Schema.catchDecoding((_issue) => Effect.succeed(Option.some(0))) | |
| * ) | |
| * ``` | |
| * | |
| * @see {@link catchDecodingWithContext} to add service requirements to the handler | |
| * @category error handling | |
| * @since 4.0.0 | |
| */ | |
| export function catchDecoding<S extends Top>( | |
| f: (issue: SchemaIssue.Issue) => Effect.Effect<Option_.Option<S["Type"]>, SchemaIssue.Issue> | |
| ): (self: S) => S["Rebuild"] { | |
| return catchDecodingWithContext(f) | |
| } | |
| /** | |
| * Recovers from a decoding error with a handler that may require Effect services. | |
| * | |
| * **When to use** | |
| * | |
| * Use when you need decoding fallback logic to require services from the Effect | |
| * context. | |
| * | |
| * **Details** | |
| * | |
| * The handler receives the `Issue` and returns an `Effect` that either succeeds | |
| * with a fallback value or re-fails with a (possibly different) issue. The | |
| * handler's services are added to the schema's decoding services. | |
| * | |
| * @see {@link catchDecoding} for recovery handlers that do not require services | |
| * @see {@link middlewareDecoding} for intercepting or replacing the full decoding pipeline | |
| * | |
| * @category error handling | |
| * @since 4.0.0 | |
| */ | |
| export function catchDecodingWithContext<S extends Top, R = never>( | |
| f: (issue: SchemaIssue.Issue) => Effect.Effect<Option_.Option<S["Type"]>, SchemaIssue.Issue, R> | |
| ) { | |
| return (self: S): middlewareDecoding<S, S["DecodingServices"] | R> => | |
| self.pipe(middlewareDecoding(Effect.catchEager(f))) | |
| } | |
| /** | |
| * Recovers from an encoding error by providing a fallback value. | |
| * | |
| * **Details** | |
| * | |
| * The handler receives the `Issue` and returns an `Effect` that either | |
| * succeeds with a fallback value or re-fails with a (possibly different) issue. | |
| * | |
| * @see {@link catchEncodingWithContext} to add service requirements to the handler | |
| * @category error handling | |
| * @since 4.0.0 | |
| */ | |
| export function catchEncoding<S extends Top>( | |
| f: (issue: SchemaIssue.Issue) => Effect.Effect<Option_.Option<S["Encoded"]>, SchemaIssue.Issue> | |
| ): (self: S) => S["Rebuild"] { | |
| return catchEncodingWithContext(f) | |
| } | |
| /** | |
| * Recovers from an encoding error with a handler that may require Effect services. | |
| * | |
| * **When to use** | |
| * | |
| * Use when you need encoding fallback logic to require services from the Effect | |
| * context. | |
| * | |
| * **Details** | |
| * | |
| * The handler receives the `Issue` and returns an `Effect` that either succeeds | |
| * with a fallback encoded value or re-fails with a (possibly different) issue. | |
| * The handler's services are added to the schema's encoding services. | |
| * | |
| * @see {@link catchEncoding} for recovery handlers that do not require services | |
| * @see {@link middlewareEncoding} for intercepting or replacing the full encoding pipeline | |
| * | |
| * @category error handling | |
| * @since 4.0.0 | |
| */ | |
| export function catchEncodingWithContext<S extends Top, R = never>( | |
| f: (issue: SchemaIssue.Issue) => Effect.Effect<Option_.Option<S["Encoded"]>, SchemaIssue.Issue, R> | |
| ) { | |
| return (self: S): middlewareEncoding<S, S["EncodingServices"] | R> => | |
| self.pipe(middlewareEncoding(Effect.catchEager(f))) | |
| } | |
| /** | |
| * Type-level representation returned by {@link decodeTo}. | |
| * | |
| * @category transforming | |
| * @since 4.0.0 | |
| */ | |
| export interface decodeTo<To extends Top, From extends Top, RD = never, RE = never> extends | |
| Bottom< | |
| To["Type"], | |
| From["Encoded"], | |
| To["DecodingServices"] | From["DecodingServices"] | RD, | |
| To["EncodingServices"] | From["EncodingServices"] | RE, | |
| To["ast"], | |
| decodeTo<To, From, RD, RE>, | |
| To["~type.make.in"], | |
| To["Iso"], | |
| To["~type.parameters"], | |
| To["~type.make"], | |
| To["~type.mutability"], | |
| To["~type.optionality"], | |
| To["~type.constructor.default"], | |
| From["~encoded.mutability"], | |
| From["~encoded.optionality"] | |
| > | |
| { | |
| readonly from: From | |
| readonly to: To | |
| } | |
| /** | |
| * Type-level representation returned by {@link decodeTo} without a custom transformation. | |
| * | |
| * @category transforming | |
| * @since 3.10.0 | |
| */ | |
| export interface compose<To extends Top, From extends Top> extends decodeTo<To, From> {} | |
| /** | |
| * Creates a schema that transforms from a source schema to a target schema. | |
| * | |
| * **When to use** | |
| * | |
| * Use when decoding should change the schema's decoded type or encoded shape, | |
| * with an optional custom bidirectional transformation. | |
| * | |
| * **Details** | |
| * | |
| * Call it with the target schema `to` and then pipe the source schema `from` | |
| * into the returned function. The resulting schema decodes from | |
| * `From["Encoded"]` to `To["Type"]` and encodes from `To["Type"]` back to | |
| * `From["Encoded"]`. | |
| * | |
| * When no transformation is provided, `SchemaTransformation.passthrough()` is | |
| * used, so `From["Type"]` must already be compatible with `To["Encoded"]`. | |
| * The resulting schema combines decoding and encoding services from both | |
| * schemas and any custom transformation. | |
| * | |
| * **Gotchas** | |
| * | |
| * In a custom transformation, `decode` maps `From["Type"]` to `To["Encoded"]` | |
| * and is used on the encoding path, while `encode` maps `To["Encoded"]` to | |
| * `From["Type"]` and is used on the decoding path. | |
| * | |
| * **Example** (String to Number with transformation) | |
| * | |
| * ```ts | |
| * import { Schema, SchemaGetter } from "effect" | |
| * | |
| * const NumberFromString = Schema.String.pipe( | |
| * Schema.decodeTo( | |
| * Schema.Number, | |
| * { | |
| * decode: SchemaGetter.transform((s) => Number(s)), | |
| * encode: SchemaGetter.transform((n) => String(n)) | |
| * } | |
| * ) | |
| * ) | |
| * | |
| * const result = Schema.decodeUnknownSync(NumberFromString)("123") | |
| * // result: 123 | |
| * ``` | |
| * | |
| * @category transforming | |
| * @since 4.0.0 | |
| */ | |
| export function decodeTo<To extends Top>(to: To): <From extends Top>(from: From) => compose<To, From> | |
| export function decodeTo<To extends Top, From extends Top, RD = never, RE = never>( | |
| to: To, | |
| transformation: { | |
| readonly decode: SchemaGetter.Getter<NoInfer<To["Encoded"]>, NoInfer<From["Type"]>, RD> | |
| readonly encode: SchemaGetter.Getter<NoInfer<From["Type"]>, NoInfer<To["Encoded"]>, RE> | |
| } | |
| ): (from: From) => decodeTo<To, From, RD, RE> | |
| export function decodeTo<To extends Top, From extends Top, RD = never, RE = never>( | |
| to: To, | |
| transformation?: { | |
| readonly decode: SchemaGetter.Getter<To["Encoded"], From["Type"], RD> | |
| readonly encode: SchemaGetter.Getter<From["Type"], To["Encoded"], RE> | |
| } | undefined | |
| ) { | |
| return (from: From) => { | |
| return make( | |
| SchemaAST.decodeTo( | |
| from.ast, | |
| to.ast, | |
| transformation ? SchemaTransformation.make(transformation) : SchemaTransformation.passthrough() | |
| ), | |
| { | |
| from, | |
| to | |
| } | |
| ) | |
| } | |
| } | |
| /** | |
| * Applies a transformation to a schema, creating a new schema with the same type but transformed encoding/decoding. | |
| * | |
| * **When to use** | |
| * | |
| * Use when the decoded type stays the same and the transformation only | |
| * normalizes values during encoding and decoding. | |
| * | |
| * **Details** | |
| * | |
| * Call it with a transformation object and then pipe a schema into the returned | |
| * function. The resulting schema keeps the same `Type` and `Encoded` types as | |
| * the source schema, while applying the transformation during both decoding and | |
| * encoding. | |
| * | |
| * Internally this uses `toType(self)` as the target schema and combines service | |
| * requirements from the source schema and the transformation. | |
| * | |
| * **Gotchas** | |
| * | |
| * Use {@link decodeTo} instead when the transformation should change the | |
| * decoded type. For this helper, both transformation getters operate on | |
| * `S["Type"]` values. | |
| * | |
| * **Example** (Trimming string values during encoding/decoding) | |
| * | |
| * ```ts | |
| * import { Schema, SchemaGetter } from "effect" | |
| * | |
| * const Trimmed = Schema.String.pipe( | |
| * Schema.decode({ | |
| * decode: SchemaGetter.transform((s) => s.trim()), | |
| * encode: SchemaGetter.transform((s) => s.trim()) | |
| * }) | |
| * ) | |
| * | |
| * const result = Schema.decodeUnknownSync(Trimmed)(" hello ") | |
| * // result: "hello" | |
| * ``` | |
| * | |
| * @category transforming | |
| * @since 3.10.0 | |
| */ | |
| export function decode<S extends Top, RD = never, RE = never>(transformation: { | |
| readonly decode: SchemaGetter.Getter<S["Type"], S["Type"], RD> | |
| readonly encode: SchemaGetter.Getter<S["Type"], S["Type"], RE> | |
| }) { | |
| return (self: S): decodeTo<toType<S>, S, RD, RE> => { | |
| return self.pipe(decodeTo(toType(self), transformation)) | |
| } | |
| } | |
| /** | |
| * Reverses a schema transformation so the encoded schema is supplied first. | |
| * | |
| * **When to use** | |
| * | |
| * Use to define a transformation by naming the encoded schema before the | |
| * decoded schema. | |
| * | |
| * **Details** | |
| * | |
| * `encodeTo(to)(from)` is equivalent to `to.pipe(decodeTo(from))`. The `from` | |
| * schema acts as the target decoded schema and `to` acts as the encoded source. | |
| * | |
| * **Example** (Encode a number back to string) | |
| * | |
| * ```ts | |
| * import { Schema, SchemaGetter } from "effect" | |
| * | |
| * const NumberFromString = Schema.Number.pipe( | |
| * Schema.encodeTo(Schema.String, { | |
| * decode: SchemaGetter.transform((s: string) => Number(s)), | |
| * encode: SchemaGetter.transform((n: number) => String(n)) | |
| * }) | |
| * ) | |
| * ``` | |
| * | |
| * @category transforming | |
| * @since 4.0.0 | |
| */ | |
| export function encodeTo<To extends Top>( | |
| to: To | |
| ): <From extends Top>(from: From) => decodeTo<From, To> | |
| export function encodeTo<To extends Top, From extends Top, RD = never, RE = never>( | |
| to: To, | |
| transformation: { | |
| readonly decode: SchemaGetter.Getter<NoInfer<From["Encoded"]>, NoInfer<To["Type"]>, RD> | |
| readonly encode: SchemaGetter.Getter<NoInfer<To["Type"]>, NoInfer<From["Encoded"]>, RE> | |
| } | |
| ): (from: From) => decodeTo<From, To, RD, RE> | |
| export function encodeTo<To extends Top, From extends Top, RD = never, RE = never>( | |
| to: To, | |
| transformation?: { | |
| readonly decode: SchemaGetter.Getter<From["Encoded"], To["Type"], RD> | |
| readonly encode: SchemaGetter.Getter<To["Type"], From["Encoded"], RE> | |
| } | |
| ) { | |
| return (from: From): decodeTo<From, To, RD, RE> => { | |
| return transformation ? | |
| to.pipe(decodeTo(from, transformation)) : | |
| to.pipe(decodeTo(from)) | |
| } | |
| } | |
| /** | |
| * Applies a transformation to a schema's encoded type, creating a new schema where encoding/decoding | |
| * operate on `S["Encoded"]` rather than `S["Type"]`. | |
| * | |
| * **Details** | |
| * | |
| * The `decode` getter maps `S["Encoded"]` → `S["Encoded"]` (applied during decoding), | |
| * and the `encode` getter maps `S["Encoded"]` → `S["Encoded"]` (applied during encoding). | |
| * | |
| * **Example** (Upper-casing encoded strings) | |
| * | |
| * ```ts | |
| * import { Schema, SchemaGetter } from "effect" | |
| * | |
| * const UpperFromLower = Schema.String.pipe( | |
| * Schema.encode({ | |
| * decode: SchemaGetter.transform((s: string) => s.toLowerCase()), | |
| * encode: SchemaGetter.transform((s: string) => s.toUpperCase()) | |
| * }) | |
| * ) | |
| * ``` | |
| * | |
| * @category transforming | |
| * @since 3.10.0 | |
| */ | |
| export function encode<S extends Top, RD = never, RE = never>(transformation: { | |
| readonly decode: SchemaGetter.Getter<S["Encoded"], S["Encoded"], RD> | |
| readonly encode: SchemaGetter.Getter<S["Encoded"], S["Encoded"], RE> | |
| }) { | |
| return (self: S): decodeTo<S, toEncoded<S>, RD, RE> => { | |
| return toEncoded(self).pipe(decodeTo(self, transformation)) | |
| } | |
| } | |
| /** | |
| * Constraint used to ensure a schema field does not already have a constructor default. | |
| * | |
| * **Details** | |
| * | |
| * Only schemas that satisfy this constraint can be passed to {@link withConstructorDefault}. | |
| * | |
| * @category models | |
| * @since 4.0.0 | |
| */ | |
| export interface WithoutConstructorDefault { | |
| readonly "~type.constructor.default": "no-default" | |
| } | |
| /** | |
| * Type-level representation returned by {@link withConstructorDefault}. | |
| * | |
| * @category constructors | |
| * @since 3.10.0 | |
| */ | |
| export interface withConstructorDefault<S extends Top & WithoutConstructorDefault> extends | |
| Bottom< | |
| S["Type"], | |
| S["Encoded"], | |
| S["DecodingServices"], | |
| S["EncodingServices"], | |
| S["ast"], | |
| withConstructorDefault<S>, | |
| S["~type.make.in"], | |
| S["Iso"], | |
| S["~type.parameters"], | |
| S["~type.make"], | |
| S["~type.mutability"], | |
| S["~type.optionality"], | |
| "with-default", | |
| S["~encoded.mutability"], | |
| S["~encoded.optionality"] | |
| > | |
| { | |
| readonly schema: S | |
| } | |
| /** | |
| * Attaches a constructor default value to a schema field. | |
| * | |
| * **Details** | |
| * | |
| * Constructor defaults are applied only during `make*`, not during decoding or | |
| * encoding. | |
| * | |
| * **Example** (Optional field with a static default) | |
| * | |
| * ```ts | |
| * import { Effect, Schema } from "effect" | |
| * | |
| * const MySchema = Schema.Struct({ | |
| * name: Schema.String.pipe( | |
| * Schema.optionalKey, | |
| * Schema.withConstructorDefault(Effect.succeed("anonymous")) | |
| * ) | |
| * }) | |
| * | |
| * const value = MySchema.make({}) | |
| * // value: { name: "anonymous" } | |
| * ``` | |
| * | |
| * @category constructors | |
| * @since 3.10.0 | |
| */ | |
| export function withConstructorDefault<S extends Top & WithoutConstructorDefault>( | |
| // `S["~type.make.in"]` instead of `S["Type"]` is intentional here because | |
| // it makes easier to define the default value if there are nested defaults | |
| defaultValue: Effect.Effect<S["~type.make.in"], SchemaError> | |
| ) { | |
| return (schema: S): withConstructorDefault<S> => | |
| make(SchemaAST.withConstructorDefault(schema.ast, Effect.mapErrorEager(defaultValue, (e) => e.issue)), { schema }) | |
| } | |
| /** | |
| * Type-level representation returned by {@link withDecodingDefaultKey}. | |
| * | |
| * @category decoding | |
| * @since 4.0.0 | |
| */ | |
| export interface withDecodingDefaultKey<S extends Top, R = never> extends decodeTo<S, optionalKey<toEncoded<S>>, R> { | |
| readonly "Rebuild": withDecodingDefaultKey<S, R> | |
| } | |
| /** | |
| * Options for {@link withDecodingDefaultKey} and {@link withDecodingDefault}. | |
| * | |
| * **Details** | |
| * | |
| * - `encodingStrategy`: | |
| * - `"passthrough"` (default): pass the value through during encoding | |
| * - `"omit"`: omit the key from the encoded output | |
| * | |
| * @category options | |
| * @since 4.0.0 | |
| */ | |
| export type DecodingDefaultOptions = { | |
| readonly encodingStrategy?: "omit" | "passthrough" | undefined | |
| } | |
| /** | |
| * Makes a struct key optional on the `Encoded` side and provides a default | |
| * `Encoded` value when the key is missing during decoding. | |
| * | |
| * **Details** | |
| * | |
| * The key uses `optionalKey` on the encoded side, so it may be absent from the | |
| * input object but **not** `undefined`. The default value is specified in terms | |
| * of the `Encoded` type (before any decoding transformations). | |
| * | |
| * Options: | |
| * | |
| * - `encodingStrategy`: | |
| * - `"passthrough"` (default): include the value in the encoded output. | |
| * - `"omit"`: omit the key from the encoded output. | |
| * | |
| * **Example** (Default for a missing struct key) | |
| * | |
| * ```ts | |
| * import { Effect, Schema } from "effect" | |
| * | |
| * const MySchema = Schema.Struct({ | |
| * name: Schema.String.pipe(Schema.withDecodingDefaultKey(Effect.succeed("anonymous"))) | |
| * }) | |
| * | |
| * const result = Schema.decodeUnknownSync(MySchema)({}) | |
| * // result: { name: "anonymous" } | |
| * ``` | |
| * | |
| * @see {@link withDecodingDefault} for the value-level variant (key absent **or** `undefined`) | |
| * @see {@link withDecodingDefaultTypeKey} for the variant where the default is a `Type` value | |
| * @category decoding | |
| * @since 4.0.0 | |
| */ | |
| export function withDecodingDefaultKey<S extends Top, R = never>( | |
| defaultValue: Effect.Effect<S["Encoded"], SchemaError, R>, | |
| options?: DecodingDefaultOptions | |
| ) { | |
| const encode = options?.encodingStrategy === "omit" ? SchemaGetter.omit() : SchemaGetter.passthrough() | |
| return (self: S): withDecodingDefaultKey<S, R> => { | |
| return optionalKey(toEncoded(self)).pipe(decodeTo(self, { | |
| decode: SchemaGetter.withDefault(Effect.mapErrorEager(defaultValue, (e) => e.issue)), | |
| encode | |
| })) | |
| } | |
| } | |
| /** | |
| * Type-level representation returned by {@link withDecodingDefaultTypeKey}. | |
| * | |
| * @category decoding | |
| * @since 4.0.0 | |
| */ | |
| export interface withDecodingDefaultTypeKey<S extends Top, R = never> | |
| extends decodeTo<withDecodingDefaultKey<toType<S>, R>, optionalKey<S>> | |
| { | |
| readonly "Rebuild": withDecodingDefaultTypeKey<S, R> | |
| } | |
| /** | |
| * Makes a struct key optional on the `Encoded` side (`optionalKey`, so the | |
| * key may be absent but **not** `undefined`) and provides a default `Type` | |
| * value when the key is missing during decoding. | |
| * | |
| * **Details** | |
| * | |
| * Unlike {@link withDecodingDefaultKey}, the default value is specified in | |
| * terms of the `Type` (decoded) representation, so it does not need to go | |
| * through the decoding transformation. | |
| * | |
| * Options: | |
| * | |
| * - `encodingStrategy`: | |
| * - `"passthrough"` (default): include the value in the encoded output. | |
| * - `"omit"`: omit the key from the encoded output. | |
| * | |
| * @see {@link withDecodingDefaultKey} for the variant where the default is an `Encoded` value | |
| * @see {@link withDecodingDefaultType} for the value-level variant | |
| * @category decoding | |
| * @since 4.0.0 | |
| */ | |
| export function withDecodingDefaultTypeKey<S extends Top, R = never>( | |
| defaultValue: Effect.Effect<S["Type"], SchemaError, R>, | |
| options?: DecodingDefaultOptions | |
| ) { | |
| return (self: S): withDecodingDefaultTypeKey<S, R> => { | |
| return toType(self).pipe( | |
| withDecodingDefaultKey<toType<S>, R>(defaultValue, options), | |
| encodeTo(optionalKey(self)) | |
| ) | |
| } | |
| } | |
| /** | |
| * Type-level representation returned by {@link withDecodingDefault}. | |
| * | |
| * @category decoding | |
| * @since 3.10.0 | |
| */ | |
| export interface withDecodingDefault<S extends Top, R = never> extends decodeTo<S, optional<toEncoded<S>>, R> { | |
| readonly "Rebuild": withDecodingDefault<S, R> | |
| } | |
| /** | |
| * Wraps the `Encoded` side with `optional` (key absent **or** `undefined`) | |
| * and provides a default `Encoded` value when the field is missing or | |
| * `undefined` during decoding. | |
| * | |
| * **When to use** | |
| * | |
| * Use when the default is expressed in the encoded representation, before the | |
| * field's decoding transformation runs. | |
| * | |
| * **Details** | |
| * | |
| * The default value is specified in terms of the `Encoded` type (before any | |
| * decoding transformations). | |
| * | |
| * Options: | |
| * | |
| * - `encodingStrategy`: | |
| * - `"passthrough"` (default): include the value in the encoded output. | |
| * - `"omit"`: omit the key from the encoded output. | |
| * | |
| * **Example** (Default for an optional field value) | |
| * | |
| * ```ts | |
| * import { Effect, Schema } from "effect" | |
| * | |
| * const MySchema = Schema.Struct({ | |
| * name: Schema.String.pipe(Schema.optional, Schema.withDecodingDefault(Effect.succeed("anonymous"))) | |
| * }) | |
| * | |
| * const result = Schema.decodeUnknownSync(MySchema)({ name: undefined }) | |
| * // result: { name: "anonymous" } | |
| * ``` | |
| * | |
| * @see {@link withDecodingDefaultKey} for the key-level variant (key absent only, not `undefined`) | |
| * @see {@link withDecodingDefaultType} for the variant where the default is a `Type` value | |
| * @category decoding | |
| * @since 3.10.0 | |
| */ | |
| export function withDecodingDefault<S extends Top, R = never>( | |
| defaultValue: Effect.Effect<S["Encoded"], SchemaError, R>, | |
| options?: DecodingDefaultOptions | |
| ) { | |
| const encode = options?.encodingStrategy === "omit" ? SchemaGetter.omit() : SchemaGetter.passthrough() | |
| return (self: S): withDecodingDefault<S, R> => { | |
| return optional(toEncoded(self)).pipe(decodeTo(self, { | |
| decode: SchemaGetter.withDefault(Effect.mapErrorEager(defaultValue, (e) => e.issue)), | |
| encode | |
| })) | |
| } | |
| } | |
| /** | |
| * Type-level representation returned by {@link withDecodingDefaultType}. | |
| * | |
| * @category decoding | |
| * @since 4.0.0 | |
| */ | |
| export interface withDecodingDefaultType<S extends Top, R = never> | |
| extends decodeTo<withDecodingDefault<toType<S>, R>, optional<S>> | |
| { | |
| readonly "Rebuild": withDecodingDefaultType<S, R> | |
| } | |
| /** | |
| * Wraps the `Encoded` side with `optional` (key absent **or** `undefined`) | |
| * and provides a default `Type` value when the field is missing or | |
| * `undefined` during decoding. | |
| * | |
| * **When to use** | |
| * | |
| * Use when the default is already in the decoded representation and should not | |
| * pass through the field's decoding transformation. | |
| * | |
| * **Details** | |
| * | |
| * Unlike {@link withDecodingDefault}, the default value is specified in terms | |
| * of the `Type` (decoded) representation, so it does not need to go through | |
| * the decoding transformation. | |
| * | |
| * Options: | |
| * | |
| * - `encodingStrategy`: | |
| * - `"passthrough"` (default): include the value in the encoded output. | |
| * - `"omit"`: omit the key from the encoded output. | |
| * | |
| * @see {@link withDecodingDefault} for the variant where the default is an `Encoded` value | |
| * @see {@link withDecodingDefaultTypeKey} for the key-level variant | |
| * @category decoding | |
| * @since 4.0.0 | |
| */ | |
| export function withDecodingDefaultType<S extends Top, R = never>( | |
| defaultValue: Effect.Effect<S["Type"], SchemaError, R>, | |
| options?: DecodingDefaultOptions | |
| ) { | |
| return (self: S): withDecodingDefaultType<S, R> => { | |
| return toType(self).pipe( | |
| withDecodingDefault<toType<S>, R>(defaultValue, options), | |
| encodeTo(optional(self)) | |
| ) | |
| } | |
| } | |
| /** | |
| * Type-level representation returned by {@link tag}. | |
| * | |
| * @category constructors | |
| * @since 3.10.0 | |
| */ | |
| export interface tag<Tag extends SchemaAST.LiteralValue> extends withConstructorDefault<Literal<Tag>> {} | |
| /** | |
| * Combines a {@link Literal} schema with {@link withConstructorDefault}, making it ideal | |
| * for discriminator fields in tagged unions. When constructing via `make`, the | |
| * `_tag` field can be omitted and will be filled automatically. | |
| * | |
| * **Example** (Discriminated union tag) | |
| * | |
| * ```ts | |
| * import { Schema } from "effect" | |
| * | |
| * const A = Schema.Struct({ _tag: Schema.tag("A"), value: Schema.Number }) | |
| * | |
| * // _tag is optional in make, auto-filled to "A" | |
| * const a = A.make({ value: 42 }) | |
| * // a: { _tag: "A", value: 42 } | |
| * ``` | |
| * | |
| * @see {@link tagDefaultOmit} to also omit the tag during encoding | |
| * @see {@link TaggedStruct} for a shorthand that adds `_tag` automatically | |
| * @category constructors | |
| * @since 3.10.0 | |
| */ | |
| export function tag<Tag extends SchemaAST.LiteralValue>(literal: Tag): tag<Tag> { | |
| return Literal(literal).pipe(withConstructorDefault(Effect.succeed(literal))) | |
| } | |
| /** | |
| * Creates a literal `_tag` schema that is omitted from encoded output. | |
| * | |
| * **When to use** | |
| * | |
| * Use to decode data that omits the discriminator field while still constructing | |
| * values with a `_tag` for tagged union matching. | |
| * | |
| * **Details** | |
| * | |
| * The tag is filled during decoding and construction, like {@link tag}, but is | |
| * omitted when encoding. | |
| * | |
| * **Example** (Tag omitted during encoding) | |
| * | |
| * ```ts | |
| * import { Schema } from "effect" | |
| * | |
| * const A = Schema.Struct({ | |
| * _tag: Schema.tagDefaultOmit("A"), | |
| * value: Schema.Number | |
| * }) | |
| * | |
| * // Encode strips the _tag field | |
| * const encoded = Schema.encodeUnknownSync(A)({ _tag: "A", value: 1 }) | |
| * // encoded: { value: 1 } | |
| * ``` | |
| * | |
| * @see {@link tag} for the variant that keeps the tag during encoding | |
| * @category constructors | |
| * @since 4.0.0 | |
| */ | |
| export function tagDefaultOmit<Tag extends SchemaAST.LiteralValue>(literal: Tag) { | |
| return tag(literal).pipe(withDecodingDefaultKey(Effect.succeed(literal), { encodingStrategy: "omit" })) | |
| } | |
| /** | |
| * Type-level representation returned by {@link TaggedStruct}. | |
| * | |
| * @category models | |
| * @since 3.10.0 | |
| */ | |
| export type TaggedStruct<Tag extends SchemaAST.LiteralValue, Fields extends Struct.Fields> = Struct< | |
| Simplify<{ readonly _tag: tag<Tag> } & Fields> | |
| > | |
| /** | |
| * Creates a struct schema with an automatically populated `_tag` field. | |
| * | |
| * **When to use** | |
| * | |
| * Use to define a tagged union case from a literal tag and a set of fields. | |
| * | |
| * **Details** | |
| * | |
| * When using the `make` method, the `_tag` field is optional and will be | |
| * added automatically. However, when decoding or encoding, the `_tag` field | |
| * must be present in the input. | |
| * | |
| * **Example** (Tagged struct as a shorthand for a struct with a `_tag` field) | |
| * | |
| * ```ts | |
| * import { Schema } from "effect" | |
| * | |
| * // Defines a struct with a fixed `_tag` field | |
| * const tagged = Schema.TaggedStruct("A", { | |
| * a: Schema.String | |
| * }) | |
| * | |
| * // This is the same as writing: | |
| * const equivalent = Schema.Struct({ | |
| * _tag: Schema.tag("A"), | |
| * a: Schema.String | |
| * }) | |
| * ``` | |
| * | |
| * **Example** (Accessing the literal value of the tag) | |
| * | |
| * ```ts | |
| * import { Schema } from "effect" | |
| * | |
| * const tagged = Schema.TaggedStruct("A", { | |
| * a: Schema.String | |
| * }) | |
| * | |
| * // literal: "A" | |
| * const literal = tagged.fields._tag.schema.literal | |
| * ``` | |
| * | |
| * @category constructors | |
| * @since 3.10.0 | |
| */ | |
| export function TaggedStruct<const Tag extends SchemaAST.LiteralValue, const Fields extends Struct.Fields>( | |
| value: Tag, | |
| fields: Fields | |
| ): TaggedStruct<Tag, Fields> { | |
| return Struct({ _tag: tag(value), ...fields }) | |
| } | |
| /** | |
| * Recursively flatten any nested Schema.Union members into a single tuple of leaf schemas. | |
| */ | |
| type Flatten<Schemas> = Schemas extends readonly [infer Head, ...infer Tail] | |
| ? Head extends Union<infer Inner> ? [...Flatten<Inner>, ...Flatten<Tail>] | |
| : [Head, ...Flatten<Tail>] | |
| : [] | |
| type TaggedUnionUtils< | |
| Tag extends PropertyKey, | |
| Members extends ReadonlyArray<Top & { readonly Type: { readonly [K in Tag]: PropertyKey } }>, | |
| Flattened extends ReadonlyArray<Top & { readonly Type: { readonly [K in Tag]: PropertyKey } }> = Flatten< | |
| Members | |
| > | |
| > = { | |
| readonly cases: Simplify<{ [M in Flattened[number] as M["Type"][Tag]]: M }> | |
| readonly isAnyOf: <const Keys>( | |
| keys: ReadonlyArray<Keys> | |
| ) => (value: Members[number]["Type"]) => value is Extract<Members[number]["Type"], { readonly [K in Tag]: Keys }> | |
| readonly guards: { [M in Flattened[number] as M["Type"][Tag]]: (u: unknown) => u is M["Type"] } | |
| readonly match: { | |
| < | |
| Cases extends { [M in Flattened[number] as M["Type"][Tag]]: (value: M["Type"]) => any } | |
| >( | |
| value: Members[number]["Type"], | |
| cases: Cases | |
| ): Cases[keyof Cases] extends (value: any) => infer R ? Unify<R> | |
| : never | |
| < | |
| Cases extends { [M in Flattened[number] as M["Type"][Tag]]: (value: M["Type"]) => any } | |
| >( | |
| cases: Cases | |
| ): (value: Members[number]["Type"]) => Cases[keyof Cases] extends (value: any) => infer R ? Unify<R> | |
| : never | |
| } | |
| } | |
| /** | |
| * Type-level representation returned by {@link toTaggedUnion}. | |
| * | |
| * @category combinators | |
| * @since 4.0.0 | |
| */ | |
| export type toTaggedUnion< | |
| Tag extends PropertyKey, | |
| Members extends ReadonlyArray<Top & { readonly Type: { readonly [K in Tag]: PropertyKey } }> | |
| > = Union<Members> & TaggedUnionUtils<Tag, Members> | |
| /** | |
| * Augments an existing {@link Union} of tagged structs with utility methods keyed by the discriminant field. | |
| * | |
| * **Example** (Adding tagged-union utilities to an existing union) | |
| * | |
| * ```ts | |
| * import { Schema } from "effect" | |
| * | |
| * const A = Schema.TaggedStruct("A", { value: Schema.Number }) | |
| * const B = Schema.TaggedStruct("B", { name: Schema.String }) | |
| * | |
| * const MyUnion = Schema.Union([A, B]).pipe(Schema.toTaggedUnion("_tag")) | |
| * | |
| * // Pattern-match on the union | |
| * const result = MyUnion.match({ _tag: "A", value: 1 }, { | |
| * A: (a) => `number: ${a.value}`, | |
| * B: (b) => `name: ${b.name}` | |
| * }) | |
| * ``` | |
| * | |
| * @see {@link TaggedUnion} for a shorthand that builds the union from scratch | |
| * @category combinators | |
| * @since 4.0.0 | |
| */ | |
| export function toTaggedUnion<const Tag extends PropertyKey>(tag: Tag) { | |
| return <const Members extends ReadonlyArray<Top & { readonly Type: { readonly [K in Tag]: PropertyKey } }>>( | |
| self: Union<Members> | |
| ): toTaggedUnion<Tag, Members> => { | |
| const cases: Record<PropertyKey, unknown> = {} | |
| const guards: Record<PropertyKey, (u: unknown) => boolean> = {} | |
| const isAnyOf = (keys: ReadonlyArray<PropertyKey>) => (value: Members[number]["Type"]) => keys.includes(value[tag]) | |
| walk(self) | |
| return Object.assign(self, { cases, isAnyOf, guards, match }) as any | |
| function walk(schema: Top) { | |
| const ast = schema.ast | |
| if ( | |
| SchemaAST.isUnion(ast) && "members" in schema && globalThis.Array.isArray(schema.members) && | |
| schema.members.every(isSchema) | |
| ) { | |
| return schema.members.forEach(walk) | |
| } | |
| const sentinels = SchemaAST.collectSentinels(ast) | |
| if (sentinels.length > 0) { | |
| const literal = sentinels.find((s) => s.key === tag)?.literal | |
| if (Predicate.isPropertyKey(literal)) { | |
| cases[literal] = schema | |
| guards[literal] = is(toType(schema)) | |
| return | |
| } | |
| } | |
| throw new globalThis.Error("No literal or unique symbol found") | |
| } | |
| function match() { | |
| if (arguments.length === 1) { | |
| const cases = arguments[0] | |
| return function(value: any) { | |
| return cases[value[tag]](value) | |
| } | |
| } | |
| const value = arguments[0] | |
| const cases = arguments[1] | |
| return cases[value[tag]](value) | |
| } | |
| } | |
| } | |
| /** | |
| * Type-level representation returned by {@link TaggedUnion}. | |
| * | |
| * @category models | |
| * @since 4.0.0 | |
| */ | |
| export interface TaggedUnion<Cases extends Record<string, Top>> extends | |
| Bottom< | |
| { [K in keyof Cases]: Cases[K]["Type"] }[keyof Cases], | |
| { [K in keyof Cases]: Cases[K]["Encoded"] }[keyof Cases], | |
| { [K in keyof Cases]: Cases[K]["DecodingServices"] }[keyof Cases], | |
| { [K in keyof Cases]: Cases[K]["EncodingServices"] }[keyof Cases], | |
| SchemaAST.Union<SchemaAST.Objects>, | |
| TaggedUnion<Cases>, | |
| { [K in keyof Cases]: Cases[K]["~type.make"] }[keyof Cases] | |
| > | |
| { | |
| readonly cases: Cases | |
| readonly isAnyOf: <const Keys>( | |
| keys: ReadonlyArray<Keys> | |
| ) => (value: Cases[keyof Cases]["Type"]) => value is Extract<Cases[keyof Cases]["Type"], { _tag: Keys }> | |
| readonly guards: { [K in keyof Cases]: (u: unknown) => u is Cases[K]["Type"] } | |
| readonly match: { | |
| <Output>( | |
| cases: { [K in keyof Cases]: (value: Cases[K]["Type"]) => Output } | |
| ): (value: Cases[keyof Cases]["Type"]) => Output | |
| <Output>( | |
| value: Cases[keyof Cases]["Type"], | |
| cases: { [K in keyof Cases]: (value: Cases[K]["Type"]) => Output } | |
| ): Output | |
| } | |
| } | |
| /** | |
| * Builds a discriminated union from a record of field sets, one per variant. | |
| * Each key becomes the `_tag` literal and the value is passed to {@link TaggedStruct}. | |
| * The result includes `cases`, `guards`, `isAnyOf`, and `match` utilities. | |
| * | |
| * **Example** (Discriminated union with pattern matching) | |
| * | |
| * ```ts | |
| * import { Schema } from "effect" | |
| * | |
| * const Shape = Schema.TaggedUnion({ | |
| * Circle: { radius: Schema.Number }, | |
| * Rectangle: { width: Schema.Number, height: Schema.Number } | |
| * }) | |
| * | |
| * // Pattern-match on a decoded value | |
| * const area = Shape.match({ _tag: "Circle", radius: 5 }, { | |
| * Circle: (c) => Math.PI * c.radius ** 2, | |
| * Rectangle: (r) => r.width * r.height | |
| * }) | |
| * ``` | |
| * | |
| * @see {@link toTaggedUnion} to augment an existing union instead | |
| * @category constructors | |
| * @since 4.0.0 | |
| */ | |
| export function TaggedUnion<const CasesByTag extends Record<string, Struct.Fields>>( | |
| casesByTag: CasesByTag | |
| ): TaggedUnion<{ readonly [K in keyof CasesByTag & string]: TaggedStruct<K, CasesByTag[K]> }> { | |
| const cases: any = {} | |
| const members: any = [] | |
| for (const key of Object.keys(casesByTag)) { | |
| members.push(cases[key] = TaggedStruct(key, casesByTag[key])) | |
| } | |
| const union = Union(members) | |
| const { guards, isAnyOf, match } = toTaggedUnion("_tag")(union) | |
| return make(union.ast, { cases, isAnyOf, guards, match }) | |
| } | |
| /** | |
| * Type-level representation returned by {@link Opaque}. | |
| * | |
| * @category models | |
| * @since 4.0.0 | |
| */ | |
| export interface Opaque<Self, S extends Top, Brand> extends | |
| Bottom< | |
| Self, | |
| S["Encoded"], | |
| S["DecodingServices"], | |
| S["EncodingServices"], | |
| S["ast"], | |
| S["Rebuild"], | |
| S["~type.make.in"], | |
| S["Iso"], | |
| S["~type.parameters"], | |
| S["~type.make"], | |
| S["~type.mutability"], | |
| S["~type.optionality"], | |
| S["~type.constructor.default"], | |
| S["~encoded.mutability"], | |
| S["~encoded.optionality"] | |
| > | |
| { | |
| new(_: never): S["Type"] & Brand | |
| } | |
| /** | |
| * Wraps a struct schema so that its decoded `Type` becomes a nominally distinct type `Self`. | |
| * Useful for creating opaque types that are structurally identical to a base struct | |
| * but type-incompatible with it. | |
| * | |
| * **Example** (Opaque struct) | |
| * | |
| * ```ts | |
| * import { Schema } from "effect" | |
| * | |
| * class Person extends Schema.Opaque<Person>()( | |
| * Schema.Struct({ | |
| * name: Schema.String | |
| * }) | |
| * ) {} | |
| * | |
| * // Decoded value is Person, not { name: string } | |
| * const person = Schema.decodeUnknownSync(Person)({ name: "Alice" }) | |
| * // person: Person | |
| * ``` | |
| * | |
| * @category constructors | |
| * @since 4.0.0 | |
| */ | |
| export function Opaque<Self, Brand = {}>() { | |
| return <S extends Top>(schema: S): Opaque<Self, S, Brand> & Omit<S, keyof Top> => { | |
| // oxlint-disable-next-line @typescript-eslint/no-extraneous-class | |
| class Opaque {} | |
| return Object.setPrototypeOf(Opaque, schema) | |
| } | |
| } | |
| /** | |
| * Type-level representation returned by {@link instanceOf}. | |
| * | |
| * @category models | |
| * @since 3.10.0 | |
| */ | |
| export interface instanceOf<T, Iso = T> extends declare<T, Iso> { | |
| readonly "Rebuild": instanceOf<T, Iso> | |
| } | |
| /** | |
| * Creates a schema that validates values using `instanceof`. | |
| * Decoding and encoding pass the value through unchanged. | |
| * | |
| * **Example** (Schema for a built-in class) | |
| * | |
| * ```ts | |
| * import { Schema } from "effect" | |
| * | |
| * const DateSchema = Schema.instanceOf(Date) | |
| * | |
| * const decoded = Schema.decodeUnknownSync(DateSchema)(new Date("2024-01-01")) | |
| * // decoded: Date | |
| * ``` | |
| * | |
| * @category constructors | |
| * @since 3.10.0 | |
| */ | |
| export function instanceOf<C extends abstract new(...args: any) => any, Iso = InstanceType<C>>( | |
| constructor: C, | |
| annotations?: Annotations.Declaration<InstanceType<C>> | undefined | |
| ): instanceOf<InstanceType<C>, Iso> { | |
| return declare((u): u is InstanceType<C> => u instanceof constructor, annotations) | |
| } | |
| /** | |
| * Constructs an `SchemaAST.Link` that describes how a value of type `T` encodes to and decodes from a `To` schema. | |
| * Used when building low-level AST transformations that bridge two schema types. | |
| * | |
| * @category transforming | |
| * @since 4.0.0 | |
| */ | |
| export function link<T>() { | |
| return <To extends Top>( | |
| encodeTo: To, | |
| transformation: { | |
| readonly decode: SchemaGetter.Getter<T, NoInfer<To["Type"]>> | |
| readonly encode: SchemaGetter.Getter<NoInfer<To["Type"]>, T> | |
| } | |
| ): SchemaAST.Link => { | |
| return new SchemaAST.Link(encodeTo.ast, SchemaTransformation.make(transformation)) | |
| } | |
| } | |
| // ----------------------------------------------------------------------------- | |
| // Checks | |
| // ----------------------------------------------------------------------------- | |
| /** | |
| * Creates a custom validation filter from a predicate function. | |
| * | |
| * **Details** | |
| * | |
| * The predicate receives the decoded input value, the schema AST, and parse | |
| * options, and returns a `FilterOutput`. Non-success outputs are normalized into | |
| * schema issues. The `annotations` parameter annotates the filter itself; with | |
| * the default formatter, failures use `message` first, `expected` second, and | |
| * `<filter>` when neither is provided. | |
| * | |
| * When `abort` is `true`, parsing stops after this filter fails instead of | |
| * collecting later check failures. | |
| * | |
| * **Example** (Failure at a nested path) | |
| * | |
| * ```ts | |
| * import { Schema } from "effect" | |
| * | |
| * const schema = Schema.Struct({ password: Schema.String, confirmPassword: Schema.String }).check( | |
| * Schema.makeFilter((o) => | |
| * o.password === o.confirmPassword | |
| * ? undefined | |
| * : { path: ["password"], issue: "password and confirmPassword must match" } | |
| * ) | |
| * ) | |
| * | |
| * console.log(String(Schema.decodeUnknownExit(schema)({ password: "123456", confirmPassword: "1234567" }))) | |
| * // Failure(Cause([Fail(SchemaError: password and confirmPassword must match | |
| * // at ["password"])])) | |
| * ``` | |
| * | |
| * **Example** (Reporting multiple failures at once) | |
| * | |
| * ```ts | |
| * import { Schema } from "effect" | |
| * | |
| * const schema = Schema.Struct({ a: Schema.Finite, b: Schema.Finite, c: Schema.Finite }).check( | |
| * Schema.makeFilter((o) => { | |
| * const issues: Array<Schema.FilterIssue> = [] | |
| * if (o.a > 0) { | |
| * if (o.b <= 0) issues.push({ path: ["b"], issue: "b must be greater than 0" }) | |
| * if (o.c <= 0) issues.push({ path: ["c"], issue: "c must be greater than 0" }) | |
| * } | |
| * return issues | |
| * }) | |
| * ) | |
| * | |
| * console.log(String(Schema.decodeUnknownExit(schema)({ a: 1, b: 0, c: 0 }))) | |
| * // Failure(Cause([Fail(SchemaError: b must be greater than 0 | |
| * // at ["b"] | |
| * // c must be greater than 0 | |
| * // at ["c"])])) | |
| * ``` | |
| * | |
| * @category constructors | |
| * @since 4.0.0 | |
| */ | |
| export const makeFilter: <T>( | |
| filter: (input: T, ast: SchemaAST.AST, options: SchemaAST.ParseOptions) => FilterOutput, | |
| annotations?: Annotations.Filter | undefined, | |
| abort?: boolean | |
| ) => SchemaAST.Filter<T> = SchemaAST.makeFilter | |
| /** | |
| * A single failure reported by a filter predicate. Used as the element type | |
| * of the array arm of {@link FilterOutput}, and also accepted on its own. | |
| * | |
| * **Details** | |
| * | |
| * - `string`: failure with that string as the message. Produces an | |
| * {@link SchemaIssue.InvalidValue} wrapping the input, with the string used as | |
| * the issue's `message` annotation. | |
| * - {@link SchemaIssue.Issue}: a fully-formed issue, returned as-is. | |
| * - `{ path, issue }`: failure attached to a nested path. `issue` is either | |
| * a `string` (wrapped in an {@link SchemaIssue.InvalidValue}) or a full | |
| * {@link SchemaIssue.Issue}; the result is wrapped in an {@link SchemaIssue.Pointer} | |
| * at the given `path`. | |
| * | |
| * @category models | |
| * @since 3.10.0 | |
| */ | |
| export type FilterIssue = string | SchemaIssue.Issue | { | |
| readonly path: ReadonlyArray<PropertyKey> | |
| readonly issue: string | SchemaIssue.Issue | |
| } | |
| /** | |
| * The value a filter predicate (see {@link makeFilter}) may return. | |
| * | |
| * **Details** | |
| * | |
| * Each shape is normalized into an {@link SchemaIssue.Issue} (or `undefined` for | |
| * success) before being attached to the parse result: | |
| * | |
| * - `undefined`: success. The input satisfies the filter. | |
| * - `true`: success. Equivalent to `undefined`, useful when the predicate is | |
| * a plain boolean expression. | |
| * - `false`: generic failure. Produces an {@link SchemaIssue.InvalidValue} wrapping | |
| * the input, with no custom message. | |
| * - {@link FilterIssue}: a single failure. See {@link FilterIssue} for the | |
| * shapes (`string`, {@link SchemaIssue.Issue}, or `{ path, issue }`). | |
| * - `ReadonlyArray<FilterIssue>`: several failures reported together. An | |
| * empty array is treated as success; a single-element array is equivalent | |
| * to returning that element directly; otherwise the entries are grouped | |
| * into an {@link SchemaIssue.Composite}. | |
| * | |
| * @category models | |
| * @since 3.10.0 | |
| */ | |
| export type FilterOutput = | |
| | undefined | |
| | boolean | |
| | FilterIssue | |
| | ReadonlyArray<FilterIssue> | |
| /** | |
| * Groups multiple checks into a single {@link SchemaAST.FilterGroup}, applying | |
| * optional shared annotations to the group as a whole. | |
| * | |
| * @category constructors | |
| * @since 4.0.0 | |
| */ | |
| export function makeFilterGroup<T>( | |
| checks: readonly [SchemaAST.Check<T>, ...Array<SchemaAST.Check<T>>], | |
| annotations: Annotations.Filter | undefined = undefined | |
| ): SchemaAST.FilterGroup<T> { | |
| return new SchemaAST.FilterGroup(checks, annotations) | |
| } | |
| const TRIMMED_PATTERN = "^\\S[\\s\\S]*\\S$|^\\S$|^$" | |
| /** | |
| * Validates that a string has no leading or trailing whitespace. | |
| * | |
| * **Details** | |
| * | |
| * JSON Schema: | |
| * | |
| * This check corresponds to a `pattern` constraint in JSON Schema that | |
| * matches strings without leading or trailing whitespace. | |
| * | |
| * Arbitrary: | |
| * | |
| * When generating test data with fast-check, this applies a `patterns` | |
| * constraint to ensure generated strings match the trimmed pattern. | |
| * | |
| * @category String checks | |
| * @since 4.0.0 | |
| */ | |
| export function isTrimmed(annotations?: Annotations.Filter) { | |
| return makeFilter( | |
| (s: string) => s.trim() === s, | |
| { | |
| expected: "a string with no leading or trailing whitespace", | |
| meta: { | |
| _tag: "isTrimmed", | |
| regExp: new globalThis.RegExp(TRIMMED_PATTERN) | |
| }, | |
| arbitrary: { | |
| constraint: { | |
| patterns: [TRIMMED_PATTERN] | |
| } | |
| }, | |
| ...annotations | |
| } | |
| ) | |
| } | |
| /** | |
| * Validates that a string matches the specified regular expression pattern. | |
| * | |
| * **Details** | |
| * | |
| * JSON Schema: | |
| * | |
| * This check corresponds to the `pattern` constraint in JSON Schema. | |
| * | |
| * Arbitrary: | |
| * | |
| * When generating test data with fast-check, this applies a `patterns` | |
| * constraint to ensure generated strings match the specified RegExp pattern. | |
| * | |
| * @category String checks | |
| * @since 4.0.0 | |
| */ | |
| export const isPattern: (regExp: globalThis.RegExp, annotations?: Annotations.Filter) => SchemaAST.Filter<string> = | |
| SchemaAST.isPattern | |
| /** | |
| * Validates that a string represents a finite number. | |
| * | |
| * **Details** | |
| * | |
| * JSON Schema: | |
| * | |
| * This check corresponds to a `pattern` constraint in JSON Schema that matches | |
| * strings representing finite numbers. | |
| * | |
| * Arbitrary: | |
| * | |
| * When generating test data with fast-check, this applies a `patterns` | |
| * constraint to ensure generated strings match the number string pattern. | |
| * | |
| * @category String checks | |
| * @since 4.0.0 | |
| */ | |
| export const isStringFinite: (annotations?: Annotations.Filter) => SchemaAST.Filter<string> = SchemaAST.isStringFinite | |
| /** | |
| * Validates that a string is a signed base-10 integer literal for Effect's | |
| * BigInt string encoding. | |
| * | |
| * **Details** | |
| * | |
| * The check uses the pattern `^-?\d+$`. It does not accept leading `+`, decimal | |
| * points, exponent notation, separators, or non-decimal inputs such as | |
| * hexadecimal strings. | |
| * | |
| * JSON Schema: | |
| * This check corresponds to a `pattern` constraint with the same signed | |
| * base-10 integer pattern. | |
| * | |
| * @category String checks | |
| * @since 4.0.0 | |
| */ | |
| export const isStringBigInt: (annotations?: Annotations.Filter) => SchemaAST.Filter<string> = SchemaAST.isStringBigInt | |
| /** | |
| * Validates that a string has the `Symbol(description)` format used by Effect's | |
| * symbol string encoding. | |
| * | |
| * **Details** | |
| * | |
| * The check uses the pattern `^Symbol\((.*)\)$`. It is not a general test for | |
| * whether a string can be passed to JavaScript's `Symbol()` function. | |
| * | |
| * @category String checks | |
| * @since 4.0.0 | |
| */ | |
| export const isStringSymbol: (annotations?: Annotations.Filter) => SchemaAST.Filter<string> = SchemaAST.isStringSymbol | |
| /** | |
| * Returns a RegExp for validating an RFC 9562 / RFC 4122 UUID. | |
| * | |
| * Optionally specify a version 1-8. If no version is specified (`undefined`), all versions are supported. | |
| */ | |
| const getUUIDRegExp = (version?: 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8): globalThis.RegExp => { | |
| if (version) { | |
| return new globalThis.RegExp( | |
| `^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-${version}[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$` | |
| ) | |
| } | |
| return /^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|[fF]{8}-[fF]{4}-[fF]{4}-[fF]{4}-[fF]{12})$/; | |
| } | |
| /** | |
| * Validates that a string is a strict Universally Unique Identifier (UUID). | |
| * | |
| * **When to use** | |
| * | |
| * Use when you need UUID semantics, including version and RFC variant bits, | |
| * rather than only the dashed hexadecimal shape. | |
| * | |
| * **Details** | |
| * | |
| * Without a version argument, this accepts UUID versions 1 through 8, the nil | |
| * UUID (`00000000-0000-0000-0000-000000000000`), and the max UUID | |
| * (`ffffffff-ffff-ffff-ffff-ffffffffffff`). With a version argument, this | |
| * accepts only UUIDs with that version and RFC variant bits; nil and max UUIDs | |
| * are not versioned UUIDs and do not match version-specific checks. | |
| * | |
| * JSON Schema: | |
| * | |
| * This check corresponds to a `pattern` constraint in JSON Schema that matches | |
| * UUID format, and includes a `format: "uuid"` annotation. | |
| * | |
| * Arbitrary: | |
| * | |
| * When generating test data with fast-check, this applies a `patterns` | |
| * constraint to ensure generated strings match the UUID pattern. | |
| * | |
| * @see {@link isGUID} for shape-only GUID validation. | |
| * @category String checks | |
| * @since 4.0.0 | |
| */ | |
| export function isUUID(version?: 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8, annotations?: Annotations.Filter) { | |
| const regExp = getUUIDRegExp(version) | |
| return isPattern( | |
| regExp, | |
| { | |
| expected: version ? `a UUID v${version}` : "a UUID", | |
| meta: { | |
| _tag: "isUUID", | |
| regExp, | |
| version | |
| }, | |
| ...annotations | |
| } | |
| ) | |
| } | |
| const GUID_REGEXP = /^([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})$/ | |
| /** | |
| * Validates that a string has the GUID / UUID textual shape. | |
| * | |
| * **When to use** | |
| * | |
| * Use when you need to accept dashed hexadecimal identifiers without enforcing | |
| * UUID version or variant bits. | |
| * | |
| * **Details** | |
| * | |
| * This check accepts strings in the `8-4-4-4-12` hexadecimal form. JSON Schema | |
| * output includes the corresponding `pattern` constraint and intentionally does | |
| * not include `format: "uuid"` because GUID validation is looser than UUID | |
| * validation. | |
| * | |
| * Arbitrary: | |
| * | |
| * When generating test data with fast-check, this applies a `patterns` | |
| * constraint to ensure generated strings match the GUID pattern. | |
| * | |
| * @see {@link isUUID} for strict UUID validation. | |
| * @category String checks | |
| * @since 4.0.0 | |
| */ | |
| export function isGUID(annotations?: Annotations.Filter) { | |
| return isPattern( | |
| GUID_REGEXP, | |
| { | |
| expected: "a GUID", | |
| meta: { | |
| _tag: "isGUID", | |
| regExp: GUID_REGEXP | |
| }, | |
| ...annotations | |
| } | |
| ) | |
| } | |
| /** | |
| * Validates that a string is a valid ULID (Universally Unique Lexicographically | |
| * Sortable Identifier). | |
| * | |
| * **Details** | |
| * | |
| * JSON Schema: | |
| * | |
| * This check corresponds to a `pattern` constraint in JSON Schema that matches | |
| * the ULID format. | |
| * | |
| * Arbitrary: | |
| * | |
| * When generating test data with fast-check, this applies a `patterns` | |
| * constraint to ensure generated strings match the ULID pattern. | |
| * | |
| * @category String checks | |
| * @since 4.0.0 | |
| */ | |
| export function isULID(annotations?: Annotations.Filter) { | |
| const regExp = /^[0-9A-HJKMNP-TV-Za-hjkmnp-tv-z]{26}$/ | |
| return isPattern( | |
| regExp, | |
| { | |
| meta: { | |
| _tag: "isULID", | |
| regExp | |
| }, | |
| ...annotations | |
| } | |
| ) | |
| } | |
| /** | |
| * Validates that a string is valid Base64 encoded data. | |
| * | |
| * **Details** | |
| * | |
| * JSON Schema: | |
| * | |
| * This check corresponds to a `pattern` constraint in JSON Schema that matches | |
| * Base64 format. | |
| * | |
| * Arbitrary: | |
| * | |
| * When generating test data with fast-check, this applies a `patterns` | |
| * constraint to ensure generated strings match the Base64 pattern. | |
| * | |
| * @category String checks | |
| * @since 4.0.0 | |
| */ | |
| export function isBase64(annotations?: Annotations.Filter) { | |
| const regExp = /^([0-9a-zA-Z+/]{4})*(([0-9a-zA-Z+/]{2}==)|([0-9a-zA-Z+/]{3}=))?$/ | |
| return isPattern( | |
| regExp, | |
| { | |
| expected: "a base64 encoded string", | |
| meta: { | |
| _tag: "isBase64", | |
| regExp | |
| }, | |
| ...annotations | |
| } | |
| ) | |
| } | |
| /** | |
| * Validates that a string is valid Base64URL encoded data (Base64 with URL-safe | |
| * characters). | |
| * | |
| * **Details** | |
| * | |
| * JSON Schema: | |
| * | |
| * This check corresponds to a `pattern` constraint in JSON Schema that matches | |
| * Base64URL format. | |
| * | |
| * Arbitrary: | |
| * | |
| * When generating test data with fast-check, this applies a `patterns` | |
| * constraint to ensure generated strings match the Base64URL pattern. | |
| * | |
| * @category String checks | |
| * @since 4.0.0 | |
| */ | |
| export function isBase64Url(annotations?: Annotations.Filter) { | |
| const regExp = /^([0-9a-zA-Z-_]{4})*(([0-9a-zA-Z-_]{2}(==)?)|([0-9a-zA-Z-_]{3}(=)?))?$/ | |
| return isPattern( | |
| regExp, | |
| { | |
| expected: "a base64url encoded string", | |
| meta: { | |
| _tag: "isBase64Url", | |
| regExp | |
| }, | |
| ...annotations | |
| } | |
| ) | |
| } | |
| /** | |
| * Validates at runtime that a string starts with the specified literal prefix. | |
| * | |
| * **Details** | |
| * | |
| * Notes: | |
| * The JSON Schema and arbitrary metadata are built from `^${startsWith}` without | |
| * escaping regexp metacharacters. If the prefix contains regexp syntax, generated | |
| * patterns may not be equivalent to the runtime `startsWith` check. | |
| * | |
| * @category String checks | |
| * @since 4.0.0 | |
| */ | |
| export function isStartsWith(startsWith: string, annotations?: Annotations.Filter) { | |
| const formatted = JSON.stringify(startsWith) | |
| return makeFilter( | |
| (s: string) => s.startsWith(startsWith), | |
| { | |
| expected: `a string starting with ${formatted}`, | |
| meta: { | |
| _tag: "isStartsWith", | |
| startsWith, | |
| regExp: new globalThis.RegExp(`^${startsWith}`) | |
| }, | |
| arbitrary: { | |
| constraint: { | |
| patterns: [`^${startsWith}`] | |
| } | |
| }, | |
| ...annotations | |
| } | |
| ) | |
| } | |
| /** | |
| * Validates at runtime that a string ends with the specified literal suffix. | |
| * | |
| * **Details** | |
| * | |
| * Notes: | |
| * The JSON Schema and arbitrary metadata are built from `${endsWith}$` without | |
| * escaping regexp metacharacters. If the suffix contains regexp syntax, generated | |
| * patterns may not be equivalent to the runtime `endsWith` check. | |
| * | |
| * @category String checks | |
| * @since 4.0.0 | |
| */ | |
| export function isEndsWith(endsWith: string, annotations?: Annotations.Filter) { | |
| const formatted = JSON.stringify(endsWith) | |
| return makeFilter( | |
| (s: string) => s.endsWith(endsWith), | |
| { | |
| expected: `a string ending with ${formatted}`, | |
| meta: { | |
| _tag: "isEndsWith", | |
| endsWith, | |
| regExp: new globalThis.RegExp(`${endsWith}$`) | |
| }, | |
| arbitrary: { | |
| constraint: { | |
| patterns: [`${endsWith}$`] | |
| } | |
| }, | |
| ...annotations | |
| } | |
| ) | |
| } | |
| /** | |
| * Validates at runtime that a string contains the specified literal substring. | |
| * | |
| * **Details** | |
| * | |
| * Notes: | |
| * The JSON Schema and arbitrary metadata use the substring as a raw regexp | |
| * pattern. If the substring contains regexp syntax, generated patterns may not be | |
| * equivalent to the runtime `includes` check. | |
| * | |
| * @category String checks | |
| * @since 4.0.0 | |
| */ | |
| export function isIncludes(includes: string, annotations?: Annotations.Filter) { | |
| const formatted = JSON.stringify(includes) | |
| return makeFilter( | |
| (s: string) => s.includes(includes), | |
| { | |
| expected: `a string including ${formatted}`, | |
| meta: { | |
| _tag: "isIncludes", | |
| includes, | |
| regExp: new globalThis.RegExp(includes) | |
| }, | |
| arbitrary: { | |
| constraint: { | |
| patterns: [includes] | |
| } | |
| }, | |
| ...annotations | |
| } | |
| ) | |
| } | |
| const UPPERCASED_PATTERN = "^[^a-z]*$" | |
| /** | |
| * Validates that a string is unchanged by JavaScript's `toUpperCase()`. | |
| * | |
| * **Details** | |
| * | |
| * This accepts empty strings and characters that do not have lowercase forms, | |
| * such as digits, punctuation, and whitespace. It rejects strings that would | |
| * change when uppercased. | |
| * | |
| * @category String checks | |
| * @since 4.0.0 | |
| */ | |
| export function isUppercased(annotations?: Annotations.Filter) { | |
| return makeFilter( | |
| (s: string) => s.toUpperCase() === s, | |
| { | |
| expected: "a string with all characters in uppercase", | |
| meta: { | |
| _tag: "isUppercased", | |
| regExp: new globalThis.RegExp(UPPERCASED_PATTERN) | |
| }, | |
| arbitrary: { | |
| constraint: { | |
| patterns: [UPPERCASED_PATTERN] | |
| } | |
| }, | |
| ...annotations | |
| } | |
| ) | |
| } | |
| const LOWERCASED_PATTERN = "^[^A-Z]*$" | |
| /** | |
| * Validates that a string is unchanged by JavaScript's `toLowerCase()`. | |
| * | |
| * **Details** | |
| * | |
| * This accepts empty strings and characters that do not have uppercase forms, | |
| * such as digits, punctuation, and whitespace. It rejects strings that would | |
| * change when lowercased. | |
| * | |
| * @category String checks | |
| * @since 4.0.0 | |
| */ | |
| export function isLowercased(annotations?: Annotations.Filter) { | |
| return makeFilter( | |
| (s: string) => s.toLowerCase() === s, | |
| { | |
| expected: "a string with all characters in lowercase", | |
| meta: { | |
| _tag: "isLowercased", | |
| regExp: new globalThis.RegExp(LOWERCASED_PATTERN) | |
| }, | |
| arbitrary: { | |
| constraint: { | |
| patterns: [LOWERCASED_PATTERN] | |
| } | |
| }, | |
| ...annotations | |
| } | |
| ) | |
| } | |
| const CAPITALIZED_PATTERN = "^[^a-z]?.*$" | |
| /** | |
| * Validates that the first character of a string is unchanged by | |
| * `toUpperCase()`. | |
| * | |
| * **Details** | |
| * | |
| * Empty strings pass. Strings whose first character has no lowercase form, such | |
| * as a digit, punctuation mark, or whitespace, also pass. | |
| * | |
| * @category String checks | |
| * @since 4.0.0 | |
| */ | |
| export function isCapitalized(annotations?: Annotations.Filter) { | |
| return makeFilter( | |
| (s: string) => s.charAt(0).toUpperCase() === s.charAt(0), | |
| { | |
| expected: "a string with the first character in uppercase", | |
| meta: { | |
| _tag: "isCapitalized", | |
| regExp: new globalThis.RegExp(CAPITALIZED_PATTERN) | |
| }, | |
| arbitrary: { | |
| constraint: { | |
| patterns: [CAPITALIZED_PATTERN] | |
| } | |
| }, | |
| ...annotations | |
| } | |
| ) | |
| } | |
| const UNCAPITALIZED_PATTERN = "^[^A-Z]?.*$" | |
| /** | |
| * Validates that the first character of a string is unchanged by | |
| * `toLowerCase()`. | |
| * | |
| * **Details** | |
| * | |
| * Empty strings pass. Strings whose first character has no uppercase form, such | |
| * as a digit, punctuation mark, or whitespace, also pass. | |
| * | |
| * @category String checks | |
| * @since 4.0.0 | |
| */ | |
| export function isUncapitalized(annotations?: Annotations.Filter) { | |
| return makeFilter( | |
| (s: string) => s.charAt(0).toLowerCase() === s.charAt(0), | |
| { | |
| expected: "a string with the first character in lowercase", | |
| meta: { | |
| _tag: "isUncapitalized", | |
| regExp: new globalThis.RegExp(UNCAPITALIZED_PATTERN) | |
| }, | |
| arbitrary: { | |
| constraint: { | |
| patterns: [UNCAPITALIZED_PATTERN] | |
| } | |
| }, | |
| ...annotations | |
| } | |
| ) | |
| } | |
| /** | |
| * Validates that a number is finite (not `Infinity`, `-Infinity`, or `NaN`). | |
| * | |
| * **Details** | |
| * | |
| * JSON Schema: | |
| * | |
| * This check does not have a direct JSON Schema equivalent, but ensures the | |
| * number is valid and finite. | |
| * | |
| * Arbitrary: | |
| * | |
| * When generating test data with fast-check, this applies `noNaN: true` and | |
| * `noInfinity: true` constraints to ensure generated numbers are finite. | |
| * | |
| * @category Number checks | |
| * @since 4.0.0 | |
| */ | |
| export function isFinite(annotations?: Annotations.Filter) { | |
| return makeFilter( | |
| (n: number) => globalThis.Number.isFinite(n), | |
| { | |
| expected: "a finite number", | |
| meta: { | |
| _tag: "isFinite" | |
| }, | |
| arbitrary: { | |
| constraint: { | |
| noInfinity: true, | |
| noNaN: true | |
| } | |
| }, | |
| ...annotations | |
| } | |
| ) | |
| } | |
| /** | |
| * Creates a greater-than (`>`) check for any ordered type from an | |
| * `Order.Order` instance. | |
| * | |
| * @category Order checks | |
| * @since 4.0.0 | |
| */ | |
| export function makeIsGreaterThan<T>(options: { | |
| readonly order: Order.Order<T> | |
| readonly annotate?: ((exclusiveMinimum: T) => Annotations.Filter) | undefined | |
| readonly formatter?: Formatter<T> | undefined | |
| }) { | |
| const gt = Order.isGreaterThan(options.order) | |
| const formatter = options.formatter ?? format | |
| return (exclusiveMinimum: T, annotations?: Annotations.Filter) => { | |
| return makeFilter<T>( | |
| (input) => gt(input, exclusiveMinimum), | |
| { | |
| expected: `a value greater than ${formatter(exclusiveMinimum)}`, | |
| arbitrary: { | |
| constraint: { | |
| ordered: { | |
| order: options.order, | |
| minimum: exclusiveMinimum, | |
| exclusiveMinimum: true | |
| } | |
| } | |
| }, | |
| ...options.annotate?.(exclusiveMinimum), | |
| ...annotations | |
| } | |
| ) | |
| } | |
| } | |
| /** | |
| * Creates a greater-than-or-equal-to (`>=`) check for any ordered type from an | |
| * `Order.Order` instance. | |
| * | |
| * @category Order checks | |
| * @since 4.0.0 | |
| */ | |
| export function makeIsGreaterThanOrEqualTo<T>(options: { | |
| readonly order: Order.Order<T> | |
| readonly annotate?: ((exclusiveMinimum: T) => Annotations.Filter) | undefined | |
| readonly formatter?: Formatter<T> | undefined | |
| }) { | |
| const gte = Order.isGreaterThanOrEqualTo(options.order) | |
| const formatter = options.formatter ?? format | |
| return (minimum: T, annotations?: Annotations.Filter) => { | |
| return makeFilter<T>( | |
| (input) => gte(input, minimum), | |
| { | |
| expected: `a value greater than or equal to ${formatter(minimum)}`, | |
| arbitrary: { | |
| constraint: { | |
| ordered: { | |
| order: options.order, | |
| minimum | |
| } | |
| } | |
| }, | |
| ...options.annotate?.(minimum), | |
| ...annotations | |
| } | |
| ) | |
| } | |
| } | |
| /** | |
| * Creates a less-than (`<`) check for any ordered type from an `Order.Order` | |
| * instance. | |
| * | |
| * @category Order checks | |
| * @since 4.0.0 | |
| */ | |
| export function makeIsLessThan<T>(options: { | |
| readonly order: Order.Order<T> | |
| readonly annotate?: ((exclusiveMaximum: T) => Annotations.Filter) | undefined | |
| readonly formatter?: Formatter<T> | undefined | |
| }) { | |
| const lt = Order.isLessThan(options.order) | |
| const formatter = options.formatter ?? format | |
| return (exclusiveMaximum: T, annotations?: Annotations.Filter) => { | |
| return makeFilter<T>( | |
| (input) => lt(input, exclusiveMaximum), | |
| { | |
| expected: `a value less than ${formatter(exclusiveMaximum)}`, | |
| arbitrary: { | |
| constraint: { | |
| ordered: { | |
| order: options.order, | |
| maximum: exclusiveMaximum, | |
| exclusiveMaximum: true | |
| } | |
| } | |
| }, | |
| ...options.annotate?.(exclusiveMaximum), | |
| ...annotations | |
| } | |
| ) | |
| } | |
| } | |
| /** | |
| * Creates a less-than-or-equal-to (`<=`) check for any ordered type from an | |
| * `Order.Order` instance. | |
| * | |
| * @category Order checks | |
| * @since 4.0.0 | |
| */ | |
| export function makeIsLessThanOrEqualTo<T>(options: { | |
| readonly order: Order.Order<T> | |
| readonly annotate?: ((exclusiveMaximum: T) => Annotations.Filter) | undefined | |
| readonly formatter?: Formatter<T> | undefined | |
| }) { | |
| const lte = Order.isLessThanOrEqualTo(options.order) | |
| const formatter = options.formatter ?? format | |
| return (maximum: T, annotations?: Annotations.Filter) => { | |
| return makeFilter<T>( | |
| (input) => lte(input, maximum), | |
| { | |
| expected: `a value less than or equal to ${formatter(maximum)}`, | |
| arbitrary: { | |
| constraint: { | |
| ordered: { | |
| order: options.order, | |
| maximum | |
| } | |
| } | |
| }, | |
| ...options.annotate?.(maximum), | |
| ...annotations | |
| } | |
| ) | |
| } | |
| } | |
| /** | |
| * Creates an inclusive or exclusive range check for any ordered type from an | |
| * `Order.Order` instance. | |
| * | |
| * @category Order checks | |
| * @since 4.0.0 | |
| */ | |
| export function makeIsBetween<T>(deriveOptions: { | |
| readonly order: Order.Order<T> | |
| readonly annotate?: | |
| | ((options: { | |
| readonly minimum: T | |
| readonly maximum: T | |
| readonly exclusiveMinimum?: boolean | undefined | |
| readonly exclusiveMaximum?: boolean | undefined | |
| }) => Annotations.Filter) | |
| | undefined | |
| readonly formatter?: Formatter<T> | undefined | |
| }) { | |
| const greaterThanOrEqualTo = Order.isGreaterThanOrEqualTo(deriveOptions.order) | |
| const greaterThan = Order.isGreaterThan(deriveOptions.order) | |
| const lessThanOrEqualTo = Order.isLessThanOrEqualTo(deriveOptions.order) | |
| const lessThan = Order.isLessThan(deriveOptions.order) | |
| const formatter = deriveOptions.formatter ?? format | |
| return (options: { | |
| readonly minimum: T | |
| readonly maximum: T | |
| readonly exclusiveMinimum?: boolean | undefined | |
| readonly exclusiveMaximum?: boolean | undefined | |
| }, annotations?: Annotations.Filter) => { | |
| const gte = options.exclusiveMinimum ? greaterThan : greaterThanOrEqualTo | |
| const lte = options.exclusiveMaximum ? lessThan : lessThanOrEqualTo | |
| return makeFilter<T>( | |
| (input) => gte(input, options.minimum) && lte(input, options.maximum), | |
| { | |
| expected: `a value between ${formatter(options.minimum)}${options.exclusiveMinimum ? " (excluded)" : ""} and ${ | |
| formatter(options.maximum) | |
| }${options.exclusiveMaximum ? " (excluded)" : ""}`, | |
| arbitrary: { | |
| constraint: { | |
| ordered: { | |
| order: deriveOptions.order, | |
| minimum: options.minimum, | |
| maximum: options.maximum, | |
| ...(options.exclusiveMinimum && { exclusiveMinimum: true }), | |
| ...(options.exclusiveMaximum && { exclusiveMaximum: true }) | |
| } | |
| } | |
| }, | |
| ...deriveOptions.annotate?.(options), | |
| ...annotations | |
| } | |
| ) | |
| } | |
| } | |
| /** | |
| * Creates a divisibility check for any numeric type from a remainder function | |
| * and a zero value. | |
| * | |
| * @category Numeric checks | |
| * @since 4.0.0 | |
| */ | |
| export function makeIsMultipleOf<T>(options: { | |
| readonly remainder: (input: T, divisor: T) => T | |
| readonly zero: NoInfer<T> | |
| readonly annotate?: ((divisor: T) => Annotations.Filter) | undefined | |
| readonly formatter?: Formatter<T> | undefined | |
| }) { | |
| return (divisor: T, annotations?: Annotations.Filter) => { | |
| const formatter = options.formatter ?? format | |
| return makeFilter<T>( | |
| (input) => options.remainder(input, divisor) === options.zero, | |
| { | |
| expected: `a value that is a multiple of ${formatter(divisor)}`, | |
| ...options.annotate?.(divisor), | |
| ...annotations | |
| } | |
| ) | |
| } | |
| } | |
| /** | |
| * Validates that a number is greater than the specified value (exclusive). | |
| * | |
| * **Details** | |
| * | |
| * JSON Schema: | |
| * | |
| * This check corresponds to the `exclusiveMinimum` constraint in JSON Schema. | |
| * | |
| * Arbitrary: | |
| * | |
| * When generating test data with fast-check, this applies an | |
| * `exclusiveMinimum` constraint to ensure generated numbers are greater than | |
| * the specified value. | |
| * | |
| * @category Number checks | |
| * @since 4.0.0 | |
| */ | |
| export const isGreaterThan = makeIsGreaterThan({ | |
| order: Order.Number, | |
| annotate: (exclusiveMinimum) => ({ | |
| meta: { | |
| _tag: "isGreaterThan", | |
| exclusiveMinimum | |
| } | |
| }) | |
| }) | |
| /** | |
| * Validates that a number is greater than or equal to the specified value | |
| * (inclusive). | |
| * | |
| * **Details** | |
| * | |
| * JSON Schema: | |
| * | |
| * This check corresponds to the `minimum` constraint in JSON Schema. | |
| * | |
| * Arbitrary: | |
| * | |
| * When generating test data with fast-check, this applies a `minimum` constraint | |
| * to ensure generated numbers are greater than or equal to the specified value. | |
| * | |
| * @category Number checks | |
| * @since 4.0.0 | |
| */ | |
| export const isGreaterThanOrEqualTo = makeIsGreaterThanOrEqualTo({ | |
| order: Order.Number, | |
| annotate: (minimum) => ({ | |
| meta: { | |
| _tag: "isGreaterThanOrEqualTo", | |
| minimum | |
| } | |
| }) | |
| }) | |
| /** | |
| * Validates that a number is less than the specified value (exclusive). | |
| * | |
| * **Details** | |
| * | |
| * JSON Schema: | |
| * | |
| * This check corresponds to the `exclusiveMaximum` constraint in JSON Schema. | |
| * | |
| * Arbitrary: | |
| * | |
| * When generating test data with fast-check, this applies an | |
| * `exclusiveMaximum` constraint to ensure generated numbers are less than the | |
| * specified value. | |
| * | |
| * @category Number checks | |
| * @since 4.0.0 | |
| */ | |
| export const isLessThan = makeIsLessThan({ | |
| order: Order.Number, | |
| annotate: (exclusiveMaximum) => ({ | |
| meta: { | |
| _tag: "isLessThan", | |
| exclusiveMaximum | |
| } | |
| }) | |
| }) | |
| /** | |
| * Validates that a number is less than or equal to the specified value | |
| * (inclusive). | |
| * | |
| * **Details** | |
| * | |
| * JSON Schema: | |
| * | |
| * This check corresponds to the `maximum` constraint in JSON Schema. | |
| * | |
| * Arbitrary: | |
| * | |
| * When generating test data with fast-check, this applies a `maximum` constraint | |
| * to ensure generated numbers are less than or equal to the specified value. | |
| * | |
| * @category Number checks | |
| * @since 4.0.0 | |
| */ | |
| export const isLessThanOrEqualTo = makeIsLessThanOrEqualTo({ | |
| order: Order.Number, | |
| annotate: (maximum) => ({ | |
| meta: { | |
| _tag: "isLessThanOrEqualTo", | |
| maximum | |
| } | |
| }) | |
| }) | |
| /** | |
| * Validates that a number is within a specified range. The range boundaries can | |
| * be inclusive or exclusive based on the provided options. | |
| * | |
| * **Details** | |
| * | |
| * JSON Schema: | |
| * | |
| * This check corresponds to `minimum`/`maximum` or `exclusiveMinimum`/`exclusiveMaximum` | |
| * constraints in JSON Schema, depending on the options provided. | |
| * | |
| * Arbitrary: | |
| * | |
| * When generating test data with fast-check, this applies `minimum` and | |
| * `maximum` constraints with optional `exclusiveMinimum` and | |
| * `exclusiveMaximum` flags to ensure generated numbers fall within the | |
| * specified range. | |
| * | |
| * @category Number checks | |
| * @since 4.0.0 | |
| */ | |
| export const isBetween = makeIsBetween({ | |
| order: Order.Number, | |
| annotate: (options) => { | |
| return { | |
| meta: { | |
| _tag: "isBetween", | |
| ...options | |
| } | |
| } | |
| } | |
| }) | |
| /** | |
| * Validates that a number is a multiple of the specified divisor. | |
| * | |
| * **Details** | |
| * | |
| * JSON Schema: | |
| * | |
| * This check corresponds to the `multipleOf` constraint in JSON Schema. | |
| * | |
| * Arbitrary: | |
| * | |
| * When generating test data with fast-check, this applies constraints to ensure | |
| * generated numbers are multiples of the specified divisor. | |
| * | |
| * @category Number checks | |
| * @since 4.0.0 | |
| */ | |
| export const isMultipleOf = makeIsMultipleOf({ | |
| remainder, | |
| zero: 0, | |
| annotate: (divisor) => ({ | |
| expected: `a value that is a multiple of ${divisor}`, | |
| meta: { | |
| _tag: "isMultipleOf", | |
| divisor | |
| } | |
| }) | |
| }) | |
| /** | |
| * Validates that a number is a safe integer (within the safe integer range | |
| * that can be exactly represented in JavaScript). | |
| * | |
| * **Details** | |
| * | |
| * JSON Schema: | |
| * | |
| * This check corresponds to the `type: "integer"` constraint in JSON Schema. | |
| * | |
| * Arbitrary: | |
| * | |
| * When generating test data with fast-check, this applies an `integer: true` | |
| * constraint to ensure generated numbers are integers. | |
| * | |
| * @category Integer checks | |
| * @since 4.0.0 | |
| */ | |
| export function isInt(annotations?: Annotations.Filter) { | |
| return makeFilter( | |
| (n: number) => globalThis.Number.isSafeInteger(n), | |
| { | |
| expected: "an integer", | |
| meta: { | |
| _tag: "isInt" | |
| }, | |
| arbitrary: { | |
| constraint: { | |
| integer: true | |
| } | |
| }, | |
| ...annotations | |
| } | |
| ) | |
| } | |
| /** | |
| * Validates that a number is a 32-bit signed integer (range: -2,147,483,648 to | |
| * 2,147,483,647). | |
| * | |
| * **Details** | |
| * | |
| * JSON Schema: | |
| * | |
| * This check corresponds to the `format: "int32"` constraint in OpenAPI 3.1, | |
| * or `minimum`/`maximum` constraints in other JSON Schema targets. | |
| * | |
| * Arbitrary: | |
| * | |
| * When generating test data with fast-check, this applies integer and range | |
| * constraints to ensure generated numbers are 32-bit signed integers. | |
| * | |
| * @category Integer checks | |
| * @since 4.0.0 | |
| */ | |
| export function isInt32(annotations?: Annotations.Filter) { | |
| return new SchemaAST.FilterGroup( | |
| [ | |
| isInt(annotations), | |
| isBetween({ minimum: -2147483648, maximum: 2147483647 }) | |
| ], | |
| { | |
| expected: "a 32-bit integer", | |
| ...annotations | |
| } | |
| ) | |
| } | |
| /** | |
| * Validates that a number is a 32-bit unsigned integer (range: 0 to | |
| * 4,294,967,295). | |
| * | |
| * **Details** | |
| * | |
| * JSON Schema: | |
| * | |
| * This check corresponds to the `format: "uint32"` constraint in OpenAPI 3.1, | |
| * or `minimum`/`maximum` constraints in other JSON Schema targets. | |
| * | |
| * Arbitrary: | |
| * | |
| * When generating test data with fast-check, this applies integer and range | |
| * constraints to ensure generated numbers are 32-bit unsigned integers. | |
| * | |
| * @category Integer checks | |
| * @since 4.0.0 | |
| */ | |
| export function isUint32(annotations?: Annotations.Filter) { | |
| return new SchemaAST.FilterGroup( | |
| [ | |
| isInt(), | |
| isBetween({ minimum: 0, maximum: 4294967295 }) | |
| ], | |
| { | |
| expected: "a 32-bit unsigned integer", | |
| ...annotations | |
| } | |
| ) | |
| } | |
| /** | |
| * Validates that a Date object represents a valid date (not an invalid date | |
| * like `new Date("invalid")`). | |
| * | |
| * **Details** | |
| * | |
| * JSON Schema: | |
| * | |
| * This check does not have a direct JSON Schema equivalent, as JSON Schema | |
| * validates date strings, not Date objects. | |
| * | |
| * Arbitrary: | |
| * | |
| * When generating test data with fast-check, this applies a `valid: true` | |
| * constraint to ensure generated Date objects are valid. | |
| * | |
| * @category Date checks | |
| * @since 4.0.0 | |
| */ | |
| export function isDateValid(annotations?: Annotations.Filter) { | |
| return makeFilter<globalThis.Date>( | |
| (date) => !isNaN(date.getTime()), | |
| { | |
| expected: "a valid date", | |
| meta: { | |
| _tag: "isDateValid" | |
| }, | |
| arbitrary: { | |
| constraint: { | |
| valid: true | |
| } | |
| }, | |
| ...annotations | |
| } | |
| ) | |
| } | |
| /** | |
| * Validates that a Date is greater than the specified value (exclusive). | |
| * | |
| * **Details** | |
| * | |
| * Arbitrary: | |
| * | |
| * When generating test data with fast-check, this applies a `min` constraint of | |
| * one millisecond after the specified value to ensure generated Date objects are | |
| * greater than it. | |
| * | |
| * @category Date checks | |
| * @since 4.0.0 | |
| */ | |
| export const isGreaterThanDate = makeIsGreaterThan({ | |
| order: Order.Date, | |
| annotate: (exclusiveMinimum) => ({ | |
| meta: { | |
| _tag: "isGreaterThanDate", | |
| exclusiveMinimum | |
| } | |
| }) | |
| }) | |
| /** | |
| * Validates that a Date is greater than or equal to the specified date | |
| * (inclusive). | |
| * | |
| * **Details** | |
| * | |
| * JSON Schema: | |
| * | |
| * This check does not have a direct JSON Schema equivalent, as JSON Schema | |
| * validates date strings, not Date objects. | |
| * | |
| * Arbitrary: | |
| * | |
| * When generating test data with fast-check, this applies a `min` constraint | |
| * to ensure generated Date objects are greater than or equal to the specified | |
| * date. | |
| * | |
| * @category Date checks | |
| * @since 4.0.0 | |
| */ | |
| export const isGreaterThanOrEqualToDate = makeIsGreaterThanOrEqualTo({ | |
| order: Order.Date, | |
| annotate: (minimum) => ({ | |
| meta: { | |
| _tag: "isGreaterThanOrEqualToDate", | |
| minimum | |
| } | |
| }) | |
| }) | |
| /** | |
| * Validates that a Date is less than the specified value (exclusive). | |
| * | |
| * **Details** | |
| * | |
| * Arbitrary: | |
| * | |
| * When generating test data with fast-check, this applies a `max` constraint of | |
| * one millisecond before the specified value to ensure generated Date objects | |
| * are less than it. | |
| * | |
| * @category Date checks | |
| * @since 4.0.0 | |
| */ | |
| export const isLessThanDate = makeIsLessThan({ | |
| order: Order.Date, | |
| annotate: (exclusiveMaximum) => ({ | |
| meta: { | |
| _tag: "isLessThanDate", | |
| exclusiveMaximum | |
| } | |
| }) | |
| }) | |
| /** | |
| * Validates that a Date is less than or equal to the specified date | |
| * (inclusive). | |
| * | |
| * **Details** | |
| * | |
| * JSON Schema: | |
| * | |
| * This check does not have a direct JSON Schema equivalent, as JSON Schema | |
| * validates date strings, not Date objects. | |
| * | |
| * Arbitrary: | |
| * | |
| * When generating test data with fast-check, this applies a `max` constraint | |
| * to ensure generated Date objects are less than or equal to the specified | |
| * date. | |
| * | |
| * @category Date checks | |
| * @since 4.0.0 | |
| */ | |
| export const isLessThanOrEqualToDate = makeIsLessThanOrEqualTo({ | |
| order: Order.Date, | |
| annotate: (maximum) => ({ | |
| meta: { | |
| _tag: "isLessThanOrEqualToDate", | |
| maximum | |
| } | |
| }) | |
| }) | |
| /** | |
| * Validates that a Date is within a specified range. The range boundaries can | |
| * be inclusive or exclusive based on the provided options. | |
| * | |
| * **Details** | |
| * | |
| * JSON Schema: | |
| * | |
| * This check does not have a direct JSON Schema equivalent, as JSON Schema | |
| * validates date strings, not Date objects. | |
| * | |
| * Arbitrary: | |
| * | |
| * When generating test data with fast-check, this applies `min` and `max` | |
| * constraints to ensure generated Date objects fall within the specified range, | |
| * shifting exclusive bounds by one millisecond. | |
| * | |
| * @category Date checks | |
| * @since 4.0.0 | |
| */ | |
| export const isBetweenDate = makeIsBetween({ | |
| order: Order.Date, | |
| annotate: (options) => ({ | |
| meta: { | |
| _tag: "isBetweenDate", | |
| ...options | |
| } | |
| }) | |
| }) | |
| /** | |
| * Validates that a BigInt is greater than the specified value (exclusive). | |
| * | |
| * **Details** | |
| * | |
| * Arbitrary: | |
| * | |
| * When generating test data with fast-check, this applies a `min` constraint of | |
| * `exclusiveMinimum + 1n` to ensure generated BigInts are greater than the | |
| * specified value. | |
| * | |
| * @category BigInt checks | |
| * @since 4.0.0 | |
| */ | |
| export const isGreaterThanBigInt = makeIsGreaterThan({ | |
| order: Order.BigInt, | |
| annotate: (exclusiveMinimum) => ({ | |
| meta: { | |
| _tag: "isGreaterThanBigInt", | |
| exclusiveMinimum | |
| } | |
| }) | |
| }) | |
| /** | |
| * Validates that a BigInt is greater than or equal to the specified value | |
| * (inclusive). | |
| * | |
| * **Details** | |
| * | |
| * Arbitrary: | |
| * | |
| * When generating test data with fast-check, this applies a `min` constraint | |
| * to ensure generated BigInt values are greater than or equal to the specified | |
| * value. | |
| * | |
| * @category BigInt checks | |
| * @since 4.0.0 | |
| */ | |
| export const isGreaterThanOrEqualToBigInt = makeIsGreaterThanOrEqualTo({ | |
| order: Order.BigInt, | |
| annotate: (minimum) => ({ | |
| meta: { | |
| _tag: "isGreaterThanOrEqualToBigInt", | |
| minimum | |
| } | |
| }) | |
| }) | |
| /** | |
| * Validates that a BigInt is less than the specified value (exclusive). | |
| * | |
| * **Details** | |
| * | |
| * Arbitrary: | |
| * | |
| * When generating test data with fast-check, this applies a `max` constraint of | |
| * `exclusiveMaximum - 1n` to ensure generated BigInts are less than the | |
| * specified value. | |
| * | |
| * @category BigInt checks | |
| * @since 4.0.0 | |
| */ | |
| export const isLessThanBigInt = makeIsLessThan({ | |
| order: Order.BigInt, | |
| annotate: (exclusiveMaximum) => ({ | |
| meta: { | |
| _tag: "isLessThanBigInt", | |
| exclusiveMaximum | |
| } | |
| }) | |
| }) | |
| /** | |
| * Validates that a BigInt is less than or equal to the specified value | |
| * (inclusive). | |
| * | |
| * **Details** | |
| * | |
| * Arbitrary: | |
| * | |
| * When generating test data with fast-check, this applies a `max` constraint | |
| * to ensure generated BigInt values are less than or equal to the specified | |
| * value. | |
| * | |
| * @category BigInt checks | |
| * @since 4.0.0 | |
| */ | |
| export const isLessThanOrEqualToBigInt = makeIsLessThanOrEqualTo({ | |
| order: Order.BigInt, | |
| annotate: (maximum) => ({ | |
| meta: { | |
| _tag: "isLessThanOrEqualToBigInt", | |
| maximum | |
| } | |
| }) | |
| }) | |
| /** | |
| * Validates that a BigInt is within a specified range. The range boundaries can | |
| * be inclusive or exclusive based on the provided options. | |
| * | |
| * **Details** | |
| * | |
| * Arbitrary: | |
| * | |
| * When generating test data with fast-check, this applies `min` and `max` | |
| * constraints to ensure generated BigInt values fall within the specified | |
| * range. | |
| * | |
| * @category BigInt checks | |
| * @since 4.0.0 | |
| */ | |
| export const isBetweenBigInt = makeIsBetween({ | |
| order: Order.BigInt, | |
| annotate: (options) => ({ | |
| meta: { | |
| _tag: "isBetweenBigInt", | |
| ...options | |
| } | |
| }) | |
| }) | |
| /** | |
| * Validates that a BigDecimal is greater than the specified value (exclusive). | |
| * | |
| * @category BigDecimal checks | |
| * @since 4.0.0 | |
| */ | |
| export const isGreaterThanBigDecimal = makeIsGreaterThan({ | |
| order: BigDecimal_.Order, | |
| formatter: (bd) => BigDecimal_.format(bd) | |
| }) | |
| /** | |
| * Validates that a BigDecimal is greater than or equal to the specified value | |
| * (inclusive). | |
| * | |
| * @category BigDecimal checks | |
| * @since 4.0.0 | |
| */ | |
| export const isGreaterThanOrEqualToBigDecimal = makeIsGreaterThanOrEqualTo({ | |
| order: BigDecimal_.Order, | |
| formatter: (bd) => BigDecimal_.format(bd) | |
| }) | |
| /** | |
| * Validates that a BigDecimal is less than the specified value (exclusive). | |
| * | |
| * @category BigDecimal checks | |
| * @since 4.0.0 | |
| */ | |
| export const isLessThanBigDecimal = makeIsLessThan({ | |
| order: BigDecimal_.Order, | |
| formatter: (bd) => BigDecimal_.format(bd) | |
| }) | |
| /** | |
| * Validates that a BigDecimal is less than or equal to the specified value | |
| * (inclusive). | |
| * | |
| * @category BigDecimal checks | |
| * @since 4.0.0 | |
| */ | |
| export const isLessThanOrEqualToBigDecimal = makeIsLessThanOrEqualTo({ | |
| order: BigDecimal_.Order, | |
| formatter: (bd) => BigDecimal_.format(bd) | |
| }) | |
| /** | |
| * Validates that a `BigDecimal` is within a specified range. | |
| * | |
| * **Details** | |
| * | |
| * The minimum and maximum boundaries are inclusive by default. Pass | |
| * `exclusiveMinimum` or `exclusiveMaximum` to exclude either boundary. | |
| * | |
| * @category BigDecimal checks | |
| * @since 4.0.0 | |
| */ | |
| export const isBetweenBigDecimal = makeIsBetween({ | |
| order: BigDecimal_.Order, | |
| formatter: (bd) => BigDecimal_.format(bd) | |
| }) | |
| /** | |
| * Validates that a value has at least the specified length. Works with strings | |
| * and arrays. | |
| * | |
| * **Details** | |
| * | |
| * JSON Schema: | |
| * | |
| * This check corresponds to the `minLength` constraint for strings or the | |
| * `minItems` constraint for arrays in JSON Schema. | |
| * | |
| * Arbitrary: | |
| * | |
| * When generating test data with fast-check, this applies a `minLength` | |
| * constraint to ensure generated strings or arrays have at least the required | |
| * length. | |
| * | |
| * **Example** (Minimum length check) | |
| * | |
| * ```ts | |
| * import { Schema } from "effect" | |
| * | |
| * const NonEmptyStringSchema = Schema.String.check(Schema.isMinLength(1)) | |
| * const NonEmptyArraySchema = Schema.Array(Schema.Number).check(Schema.isMinLength(1)) | |
| * ``` | |
| * | |
| * @category Length checks | |
| * @since 4.0.0 | |
| */ | |
| export function isMinLength(minLength: number, annotations?: Annotations.Filter) { | |
| minLength = Math.max(0, Math.floor(minLength)) | |
| return makeFilter<{ readonly length: number }>( | |
| (input) => input.length >= minLength, | |
| { | |
| expected: `a value with a length of at least ${minLength}`, | |
| meta: { | |
| _tag: "isMinLength", | |
| minLength | |
| }, | |
| [SchemaAST.STRUCTURAL_ANNOTATION_KEY]: true, | |
| arbitrary: { | |
| constraint: { | |
| minLength | |
| } | |
| }, | |
| ...annotations | |
| } | |
| ) | |
| } | |
| /** | |
| * Validates that a value has at least one element. Works with strings and arrays. | |
| * This is equivalent to `isMinLength(1)`. | |
| * | |
| * **Details** | |
| * | |
| * JSON Schema: | |
| * | |
| * This check corresponds to the `minLength: 1` constraint for strings or the | |
| * `minItems: 1` constraint for arrays in JSON Schema. | |
| * | |
| * Arbitrary: | |
| * | |
| * When generating test data with fast-check, this applies a `minLength: 1` | |
| * constraint to ensure generated strings or arrays are non-empty. | |
| * | |
| * @category Length checks | |
| * @since 4.0.0 | |
| */ | |
| export function isNonEmpty(annotations?: Annotations.Filter) { | |
| return isMinLength(1, annotations) | |
| } | |
| /** | |
| * Validates that a value has at most the specified length. Works with strings | |
| * and arrays. | |
| * | |
| * **Details** | |
| * | |
| * JSON Schema: | |
| * | |
| * This check corresponds to the `maxLength` constraint for strings or the | |
| * `maxItems` constraint for arrays in JSON Schema. | |
| * | |
| * Arbitrary: | |
| * | |
| * When generating test data with fast-check, this applies a `maxLength` | |
| * constraint to ensure generated strings or arrays have at most the required | |
| * length. | |
| * | |
| * @category Length checks | |
| * @since 4.0.0 | |
| */ | |
| export function isMaxLength(maxLength: number, annotations?: Annotations.Filter) { | |
| maxLength = Math.max(0, Math.floor(maxLength)) | |
| return makeFilter<{ readonly length: number }>( | |
| (input) => input.length <= maxLength, | |
| { | |
| expected: `a value with a length of at most ${maxLength}`, | |
| meta: { | |
| _tag: "isMaxLength", | |
| maxLength | |
| }, | |
| [SchemaAST.STRUCTURAL_ANNOTATION_KEY]: true, | |
| arbitrary: { | |
| constraint: { | |
| maxLength | |
| } | |
| }, | |
| ...annotations | |
| } | |
| ) | |
| } | |
| /** | |
| * Validates that a value's length is within the specified range. Works with | |
| * strings and arrays. | |
| * | |
| * **Details** | |
| * | |
| * JSON Schema: | |
| * | |
| * This check corresponds to `minLength`/`maxLength` constraints for strings | |
| * or `minItems`/`maxItems` constraints for arrays in JSON Schema. | |
| * | |
| * Arbitrary: | |
| * | |
| * When generating test data with fast-check, this applies `minLength` and | |
| * `maxLength` constraints to ensure generated strings or arrays have a length | |
| * within the specified range. | |
| * | |
| * @category Length checks | |
| * @since 4.0.0 | |
| */ | |
| export function isLengthBetween(minimum: number, maximum: number, annotations?: Annotations.Filter) { | |
| minimum = Math.max(0, Math.floor(minimum)) | |
| maximum = Math.max(0, Math.floor(maximum)) | |
| return makeFilter<{ readonly length: number }>( | |
| (input) => input.length >= minimum && input.length <= maximum, | |
| { | |
| expected: minimum === maximum | |
| ? `a value with a length of ${minimum}` | |
| : `a value with a length between ${minimum} and ${maximum}`, | |
| meta: { | |
| _tag: "isLengthBetween", | |
| minimum, | |
| maximum | |
| }, | |
| [SchemaAST.STRUCTURAL_ANNOTATION_KEY]: true, | |
| arbitrary: { | |
| constraint: { | |
| minLength: minimum, | |
| maxLength: maximum | |
| } | |
| }, | |
| ...annotations | |
| } | |
| ) | |
| } | |
| /** | |
| * Validates that a value has at least the specified size. Works with values | |
| * that have a `size` property, such as `Set` or `Map`. | |
| * | |
| * **Details** | |
| * | |
| * JSON Schema: | |
| * | |
| * This check does not have a direct JSON Schema equivalent, as it applies to | |
| * values with a `size` property rather than standard JSON Schema types. | |
| * | |
| * Arbitrary: | |
| * | |
| * When generating test data with fast-check, this applies a node-local | |
| * `minLength` constraint. Generators for values with a final `.size`, such as | |
| * sets and maps, interpret it as final cardinality. | |
| * | |
| * @category Size checks | |
| * @since 4.0.0 | |
| */ | |
| export function isMinSize(minSize: number, annotations?: Annotations.Filter) { | |
| minSize = Math.max(0, Math.floor(minSize)) | |
| return makeFilter<{ readonly size: number }>( | |
| (input) => input.size >= minSize, | |
| { | |
| expected: `a value with a size of at least ${minSize}`, | |
| meta: { | |
| _tag: "isMinSize", | |
| minSize | |
| }, | |
| [SchemaAST.STRUCTURAL_ANNOTATION_KEY]: true, | |
| arbitrary: { | |
| constraint: { | |
| minLength: minSize | |
| } | |
| }, | |
| ...annotations | |
| } | |
| ) | |
| } | |
| /** | |
| * Validates that a value has at most the specified size. Works with values | |
| * that have a `size` property, such as `Set` or `Map`. | |
| * | |
| * **Details** | |
| * | |
| * JSON Schema: | |
| * | |
| * This check does not have a direct JSON Schema equivalent, as it applies to | |
| * values with a `size` property rather than standard JSON Schema types. | |
| * | |
| * Arbitrary: | |
| * | |
| * When generating test data with fast-check, this applies a node-local | |
| * `maxLength` constraint. Generators for values with a final `.size`, such as | |
| * sets and maps, interpret it as final cardinality. | |
| * | |
| * @category Size checks | |
| * @since 4.0.0 | |
| */ | |
| export function isMaxSize(maxSize: number, annotations?: Annotations.Filter) { | |
| maxSize = Math.max(0, Math.floor(maxSize)) | |
| return makeFilter<{ readonly size: number }>( | |
| (input) => input.size <= maxSize, | |
| { | |
| expected: `a value with a size of at most ${maxSize}`, | |
| meta: { | |
| _tag: "isMaxSize", | |
| maxSize | |
| }, | |
| [SchemaAST.STRUCTURAL_ANNOTATION_KEY]: true, | |
| arbitrary: { | |
| constraint: { | |
| maxLength: maxSize | |
| } | |
| }, | |
| ...annotations | |
| } | |
| ) | |
| } | |
| /** | |
| * Validates that a value's size is within the specified range. Works with | |
| * values that have a `size` property, such as `Set` or `Map`. | |
| * | |
| * **Details** | |
| * | |
| * JSON Schema: | |
| * | |
| * This check does not have a direct JSON Schema equivalent, as it applies to | |
| * values with a `size` property rather than standard JSON Schema types. | |
| * | |
| * Arbitrary: | |
| * | |
| * When generating test data with fast-check, this applies node-local | |
| * `minLength` and `maxLength` constraints. Generators for values with a final | |
| * `.size`, such as sets and maps, interpret them as final cardinality. | |
| * | |
| * @category Size checks | |
| * @since 4.0.0 | |
| */ | |
| export function isSizeBetween(minimum: number, maximum: number, annotations?: Annotations.Filter) { | |
| minimum = Math.max(0, Math.floor(minimum)) | |
| maximum = Math.max(0, Math.floor(maximum)) | |
| return makeFilter<{ readonly size: number }>( | |
| (input) => input.size >= minimum && input.size <= maximum, | |
| { | |
| expected: minimum === maximum | |
| ? `a value with a size of ${minimum}` | |
| : `a value with a size between ${minimum} and ${maximum}`, | |
| meta: { | |
| _tag: "isSizeBetween", | |
| minimum, | |
| maximum | |
| }, | |
| [SchemaAST.STRUCTURAL_ANNOTATION_KEY]: true, | |
| arbitrary: { | |
| constraint: { | |
| minLength: minimum, | |
| maxLength: maximum | |
| } | |
| }, | |
| ...annotations | |
| } | |
| ) | |
| } | |
| /** | |
| * Validates that an object contains at least the specified number of | |
| * properties. This includes both string and symbol keys when counting | |
| * properties. | |
| * | |
| * **Details** | |
| * | |
| * JSON Schema: | |
| * | |
| * This check corresponds to the `minProperties` constraint in JSON Schema. | |
| * | |
| * Arbitrary: | |
| * | |
| * When generating test data with fast-check, this applies a node-local | |
| * `minLength` constraint. Object generators interpret it as the final number | |
| * of own properties. | |
| * | |
| * @category Object checks | |
| * @since 4.0.0 | |
| */ | |
| export function isMinProperties(minProperties: number, annotations?: Annotations.Filter) { | |
| minProperties = Math.max(0, Math.floor(minProperties)) | |
| return makeFilter<object>( | |
| (input) => Reflect.ownKeys(input).length >= minProperties, | |
| { | |
| expected: `a value with at least ${minProperties === 1 ? "1 entry" : `${minProperties} entries`}`, | |
| meta: { | |
| _tag: "isMinProperties", | |
| minProperties | |
| }, | |
| [SchemaAST.STRUCTURAL_ANNOTATION_KEY]: true, | |
| arbitrary: { | |
| constraint: { | |
| minLength: minProperties | |
| } | |
| }, | |
| ...annotations | |
| } | |
| ) | |
| } | |
| /** | |
| * Validates that an object contains at most the specified number of properties. | |
| * This includes both string and symbol keys when counting properties. | |
| * | |
| * **Details** | |
| * | |
| * JSON Schema: | |
| * | |
| * This check corresponds to the `maxProperties` constraint in JSON Schema. | |
| * | |
| * Arbitrary: | |
| * | |
| * When generating test data with fast-check, this applies a node-local | |
| * `maxLength` constraint. Object generators interpret it as the final number | |
| * of own properties. | |
| * | |
| * @category Object checks | |
| * @since 4.0.0 | |
| */ | |
| export function isMaxProperties(maxProperties: number, annotations?: Annotations.Filter) { | |
| maxProperties = Math.max(0, Math.floor(maxProperties)) | |
| return makeFilter<object>( | |
| (input) => Reflect.ownKeys(input).length <= maxProperties, | |
| { | |
| expected: `a value with at most ${maxProperties === 1 ? "1 entry" : `${maxProperties} entries`}`, | |
| meta: { | |
| _tag: "isMaxProperties", | |
| maxProperties | |
| }, | |
| [SchemaAST.STRUCTURAL_ANNOTATION_KEY]: true, | |
| arbitrary: { | |
| constraint: { | |
| maxLength: maxProperties | |
| } | |
| }, | |
| ...annotations | |
| } | |
| ) | |
| } | |
| /** | |
| * Validates that an object contains between `minimum` and `maximum` properties (inclusive). | |
| * This includes both string and symbol keys when counting properties. | |
| * | |
| * **Details** | |
| * | |
| * JSON Schema: | |
| * | |
| * This check corresponds to `minProperties` and `maxProperties` | |
| * constraints in JSON Schema. | |
| * | |
| * Arbitrary: | |
| * | |
| * When generating test data with fast-check, this applies node-local | |
| * `minLength` and `maxLength` constraints. Object generators interpret them as | |
| * the final number of own properties. | |
| * | |
| * @category Object checks | |
| * @since 4.0.0 | |
| */ | |
| export function isPropertiesLengthBetween(minimum: number, maximum: number, annotations?: Annotations.Filter) { | |
| minimum = Math.max(0, Math.floor(minimum)) | |
| maximum = Math.max(0, Math.floor(maximum)) | |
| return makeFilter<object>( | |
| (input) => Reflect.ownKeys(input).length >= minimum && Reflect.ownKeys(input).length <= maximum, | |
| { | |
| expected: minimum === maximum | |
| ? `a value with exactly ${minimum === 1 ? "1 entry" : `${minimum} entries`}` | |
| : `a value with between ${minimum} and ${maximum} entries`, | |
| meta: { | |
| _tag: "isPropertiesLengthBetween", | |
| minimum, | |
| maximum | |
| }, | |
| [SchemaAST.STRUCTURAL_ANNOTATION_KEY]: true, | |
| arbitrary: { | |
| constraint: { | |
| minLength: minimum, | |
| maxLength: maximum | |
| } | |
| }, | |
| ...annotations | |
| } | |
| ) | |
| } | |
| /** | |
| * Validates that every own property key of an object satisfies the encoded side | |
| * of the provided key schema. | |
| * | |
| * **Details** | |
| * | |
| * This check uses `Reflect.ownKeys`, so symbol keys are validated in addition to | |
| * string property names. | |
| * | |
| * JSON Schema: | |
| * For string property names, this corresponds to the `propertyNames` constraint | |
| * in JSON Schema. | |
| * | |
| * @category Object checks | |
| * @since 4.0.0 | |
| */ | |
| export function isPropertyNames(keySchema: Top, annotations?: Annotations.Filter) { | |
| const propertyNames = toEncoded(keySchema) | |
| const parser = SchemaParser._issue(propertyNames.ast) | |
| return makeFilter<object>( | |
| (input, ast, options) => { | |
| const keys = Reflect.ownKeys(input) | |
| const issues: Array<SchemaIssue.Issue> = [] | |
| for (const key of keys) { | |
| const issue = parser(key, options) | |
| if (issue !== undefined) { | |
| issues.push(new SchemaIssue.Pointer([key], issue)) | |
| if (options.errors === "first") break | |
| } | |
| } | |
| if (Arr.isArrayNonEmpty(issues)) { | |
| return new SchemaIssue.Composite(ast, Option_.some(input), issues) | |
| } | |
| return true | |
| }, | |
| { | |
| expected: "an object with property names matching the schema", | |
| meta: { | |
| _tag: "isPropertyNames", | |
| propertyNames: propertyNames.ast | |
| }, | |
| [SchemaAST.STRUCTURAL_ANNOTATION_KEY]: true, | |
| ...annotations | |
| } | |
| ) | |
| } | |
| /** | |
| * Validates that all items in an array are unique according to Effect equality. | |
| * | |
| * **Details** | |
| * | |
| * JSON Schema: | |
| * This check corresponds to the `uniqueItems: true` constraint in JSON Schema. | |
| * | |
| * Arbitrary: | |
| * When generating test data with fast-check, this applies a node-local | |
| * `unique: true` constraint. Array generators translate it to `fast-check` | |
| * `uniqueArray` using Effect equality. | |
| * | |
| * @category Array checks | |
| * @since 4.0.0 | |
| */ | |
| export function isUnique<T>(annotations?: Annotations.Filter) { | |
| const equivalence = Equal.asEquivalence<T>() | |
| return makeFilter<ReadonlyArray<T>>( | |
| (input) => Arr.dedupeWith(input, equivalence).length === input.length, | |
| { | |
| expected: "an array with unique items", | |
| meta: { | |
| _tag: "isUnique" | |
| }, | |
| arbitrary: { | |
| constraint: { | |
| unique: true | |
| } | |
| }, | |
| ...annotations | |
| } | |
| ) | |
| } | |
| // ----------------------------------------------------------------------------- | |
| // Built-in Schemas | |
| // ----------------------------------------------------------------------------- | |
| /** | |
| * Type-level representation of {@link NonEmptyString}. | |
| * | |
| * @category string | |
| * @since 3.10.0 | |
| */ | |
| export interface NonEmptyString extends String { | |
| readonly "Rebuild": NonEmptyString | |
| } | |
| /** | |
| * Schema for non-empty strings. Validates that a string has at least one | |
| * character. | |
| * | |
| * @category string | |
| * @since 3.10.0 | |
| */ | |
| export const NonEmptyString: NonEmptyString = String.check(isNonEmpty()) | |
| /** | |
| * Type-level representation of {@link Char}. | |
| * | |
| * @category string | |
| * @since 3.10.0 | |
| */ | |
| export interface Char extends String { | |
| readonly "Rebuild": Char | |
| } | |
| /** | |
| * Schema for strings whose JavaScript `length` is exactly `1`. | |
| * | |
| * **When to use** | |
| * | |
| * Use to validate string values that must have `length === 1`. | |
| * | |
| * **Gotchas** | |
| * | |
| * This schema uses JavaScript `String.length`, so visible characters made from | |
| * multiple UTF-16 code units do not satisfy `length === 1`. | |
| * | |
| * @see {@link String} for unconstrained string values | |
| * @see {@link NonEmptyString} for strings with length greater than zero | |
| * @see {@link isLengthBetween} for the underlying length check | |
| * | |
| * @category string | |
| * @since 3.10.0 | |
| */ | |
| export const Char: Char = String.check(isLengthBetween(1, 1)) | |
| /** | |
| * Type-level representation returned by {@link Option}. | |
| * | |
| * @category Option | |
| * @since 3.10.0 | |
| */ | |
| export interface Option<A extends Top> extends | |
| declareConstructor< | |
| Option_.Option<A["Type"]>, | |
| Option_.Option<A["Encoded"]>, | |
| readonly [A], | |
| OptionIso<A> | |
| > | |
| { | |
| readonly "Rebuild": Option<A> | |
| readonly value: A | |
| } | |
| /** | |
| * Iso representation used for `Option` schemas. | |
| * | |
| * **Details** | |
| * | |
| * `None` is represented as `{ _tag: "None" }`, while `Some` is represented as | |
| * `{ _tag: "Some", value }` using the wrapped schema's `Iso` type. | |
| * | |
| * @category Option | |
| * @since 4.0.0 | |
| */ | |
| export type OptionIso<A extends Top> = | |
| | { readonly _tag: "None" } | |
| | { readonly _tag: "Some"; readonly value: A["Iso"] } | |
| /** | |
| * Schema for `Option<A>` values. | |
| * | |
| * @category Option | |
| * @since 3.10.0 | |
| */ | |
| export function Option<A extends Top>(value: A): Option<A> { | |
| const schema = declareConstructor< | |
| Option_.Option<A["Type"]>, | |
| Option_.Option<A["Encoded"]>, | |
| OptionIso<A> | |
| >()( | |
| [value], | |
| ([value]) => (input, ast, options) => { | |
| if (Option_.isOption(input)) { | |
| if (Option_.isNone(input)) { | |
| return Effect.succeedNone | |
| } | |
| return Effect.mapBothEager( | |
| SchemaParser.decodeUnknownEffect(value)(input.value, options), | |
| { | |
| onSuccess: Option_.some, | |
| onFailure: (issue) => | |
| new SchemaIssue.Composite(ast, Option_.some(input), [new SchemaIssue.Pointer(["value"], issue)]) | |
| } | |
| ) | |
| } | |
| return Effect.fail(new SchemaIssue.InvalidType(ast, Option_.some(input))) | |
| }, | |
| { | |
| typeConstructor: { | |
| _tag: "effect/Option" | |
| }, | |
| generation: { | |
| runtime: `Schema.Option(?)`, | |
| Type: `Option.Option<?>`, | |
| importDeclaration: `import * as Option from "effect/Option"` | |
| }, | |
| expected: "Option", | |
| toCodec: ([value]) => | |
| link<Option_.Option<A["Encoded"]>>()( | |
| Union([ | |
| Struct({ _tag: Literal("Some"), value }), | |
| Struct({ _tag: Literal("None") }) | |
| ]), | |
| SchemaTransformation.transform({ | |
| decode: (e) => e._tag === "None" ? Option_.none() : Option_.some(e.value), | |
| encode: (o) => (Option_.isSome(o) ? { _tag: "Some", value: o.value } as const : { _tag: "None" } as const) | |
| }) | |
| ), | |
| toArbitrary: ([value]) => (fc, ctx) => { | |
| const terminal = fc.constant(Option_.none()) | |
| const arbitrary = fc.oneof( | |
| terminal, | |
| value.arbitrary.map(Option_.some) | |
| ) | |
| return withRecursion(fc, ctx, terminal, arbitrary) | |
| }, | |
| toEquivalence: ([value]) => Option_.makeEquivalence(value), | |
| toFormatter: ([value]) => | |
| Option_.match({ | |
| onNone: () => "none()", | |
| onSome: (t) => `some(${value(t)})` | |
| }) | |
| } | |
| ) | |
| return make(schema.ast, { value }) | |
| } | |
| /** | |
| * Type-level representation returned by {@link OptionFromNullOr}. | |
| * | |
| * @category Option | |
| * @since 3.10.0 | |
| */ | |
| export interface OptionFromNullOr<S extends Top> extends decodeTo<Option<toType<S>>, NullOr<S>> { | |
| readonly "Rebuild": OptionFromNullOr<S> | |
| } | |
| /** | |
| * Decodes a nullable, required value `T` to a required `Option<T>` value. | |
| * | |
| * **Details** | |
| * | |
| * Decoding maps `null` to `None` and all other values to `Some`. Encoding maps | |
| * `None` to `null` and maps `Some` to its value. | |
| * | |
| * @category Option | |
| * @since 3.10.0 | |
| */ | |
| export function OptionFromNullOr<S extends Top>(schema: S): OptionFromNullOr<S> { | |
| return NullOr(schema).pipe(decodeTo( | |
| Option(toType(schema)), | |
| SchemaTransformation.optionFromNullOr() | |
| )) | |
| } | |
| /** | |
| * Type-level representation returned by {@link OptionFromUndefinedOr}. | |
| * | |
| * @category Option | |
| * @since 3.10.0 | |
| */ | |
| export interface OptionFromUndefinedOr<S extends Top> extends decodeTo<Option<toType<S>>, UndefinedOr<S>> { | |
| readonly "Rebuild": OptionFromUndefinedOr<S> | |
| } | |
| /** | |
| * Decodes a required value that may be `undefined` to a required `Option<T>` | |
| * value. | |
| * | |
| * **Details** | |
| * | |
| * Decoding maps `undefined` to `None` and all other values to `Some`. Encoding | |
| * maps `None` to `undefined` and maps `Some` to its value. | |
| * | |
| * @category Option | |
| * @since 3.10.0 | |
| */ | |
| export function OptionFromUndefinedOr<S extends Top>(schema: S): OptionFromUndefinedOr<S> { | |
| return UndefinedOr(schema).pipe(decodeTo( | |
| Option(toType(schema)), | |
| SchemaTransformation.optionFromUndefinedOr() | |
| )) | |
| } | |
| /** | |
| * Type-level representation returned by {@link OptionFromNullishOr}. | |
| * | |
| * @category Option | |
| * @since 3.10.0 | |
| */ | |
| export interface OptionFromNullishOr<S extends Top> extends decodeTo<Option<toType<S>>, NullishOr<S>> { | |
| readonly "Rebuild": OptionFromNullishOr<S> | |
| } | |
| /** | |
| * Decodes a nullish value `T` to a required `Option<T>` value. | |
| * | |
| * **Details** | |
| * | |
| * Decoding maps `null` and `undefined` to `None` and all other values to | |
| * `Some`. Encoding maps `None` to `null` or `undefined` depending on | |
| * `options.onNoneEncoding`, which defaults to `undefined`, and maps `Some` to | |
| * its value. | |
| * | |
| * @category Option | |
| * @since 3.10.0 | |
| */ | |
| export function OptionFromNullishOr<S extends Top>( | |
| schema: S, | |
| options?: { | |
| onNoneEncoding: null | undefined | |
| } | |
| ): OptionFromNullishOr<S> { | |
| return NullishOr(schema).pipe(decodeTo( | |
| Option(toType(schema)), | |
| SchemaTransformation.optionFromNullishOr(options) | |
| )) | |
| } | |
| /** | |
| * Type-level representation returned by {@link OptionFromOptionalKey}. | |
| * | |
| * @category Option | |
| * @since 4.0.0 | |
| */ | |
| export interface OptionFromOptionalKey<S extends Top> extends decodeTo<Option<toType<S>>, optionalKey<S>> { | |
| readonly "Rebuild": OptionFromOptionalKey<S> | |
| } | |
| /** | |
| * Decodes an optional value `A` to a required `Option<A>` value. | |
| * | |
| * **Details** | |
| * | |
| * Decoding maps a missing key to `None` and a present value to `Some`. | |
| * Encoding maps `None` to a missing key and maps `Some` to its value. | |
| * | |
| * @category Option | |
| * @since 4.0.0 | |
| */ | |
| export function OptionFromOptionalKey<S extends Top>(schema: S): OptionFromOptionalKey<S> { | |
| return optionalKey(schema).pipe(decodeTo( | |
| Option(toType(schema)), | |
| SchemaTransformation.optionFromOptionalKey() | |
| )) | |
| } | |
| /** | |
| * Type-level representation returned by {@link OptionFromOptional}. | |
| * | |
| * @category Option | |
| * @since 4.0.0 | |
| */ | |
| export interface OptionFromOptional<S extends Top> extends decodeTo<Option<toType<S>>, optional<S>> { | |
| readonly "Rebuild": OptionFromOptional<S> | |
| } | |
| /** | |
| * Decodes an optional or `undefined` value `A` to a required `Option<A>` | |
| * value. | |
| * | |
| * **Details** | |
| * | |
| * Decoding maps a missing key or a present `undefined` value to `None`, and | |
| * maps all other values to `Some`. Encoding maps `None` to a missing key and | |
| * maps `Some` to its value. | |
| * | |
| * @category Option | |
| * @since 4.0.0 | |
| */ | |
| export function OptionFromOptional<S extends Top>(schema: S): OptionFromOptional<S> { | |
| return optional(schema).pipe(decodeTo( | |
| Option(toType(schema)), | |
| SchemaTransformation.optionFromOptional<any>() | |
| )) | |
| } | |
| /** | |
| * Type-level representation returned by {@link OptionFromOptionalNullOr}. | |
| * | |
| * @category Option | |
| * @since 4.0.0 | |
| */ | |
| export interface OptionFromOptionalNullOr<S extends Top> extends decodeTo<Option<toType<S>>, optional<NullOr<S>>> { | |
| readonly "Rebuild": OptionFromOptionalNullOr<S> | |
| } | |
| /** | |
| * Decodes an optional or `null` or `undefined` value `A` to a required `Option<A>` | |
| * value. | |
| * | |
| * **Details** | |
| * | |
| * Decoding maps a missing key, `undefined`, or `null` to `None`, and maps all | |
| * other values to `Some`. Encoding maps `Some` to its value. `None` is encoded | |
| * according to `options.onNoneEncoding`: `"omit"` encodes a missing key, | |
| * `null` encodes `null`, and `undefined` encodes `undefined`. | |
| * | |
| * @category Option | |
| * @since 4.0.0 | |
| */ | |
| export function OptionFromOptionalNullOr<S extends Top>( | |
| schema: S, | |
| options?: { | |
| readonly onNoneEncoding: "omit" | null | undefined | |
| } | |
| ): OptionFromOptionalNullOr<S> { | |
| const onNoneEncoding = options === undefined ? "omit" : options.onNoneEncoding | |
| const noneValue = onNoneEncoding === null | |
| ? null as S["Type"] | null | undefined | |
| : undefined as S["Type"] | null | undefined | |
| return optional(NullOr(schema)).pipe(decodeTo( | |
| Option(toType(schema)), | |
| SchemaTransformation.transformOptional<Option_.Option<S["Type"]>, S["Type"] | null | undefined>({ | |
| decode: (oe) => oe.pipe(Option_.filter(Predicate.isNotNullish), Option_.some), | |
| encode: onNoneEncoding === "omit" | |
| ? Option_.flatten | |
| : (ot) => Option_.some(Option_.getOrElse(Option_.flatten(ot), () => noneValue)) | |
| }) | |
| )) | |
| } | |
| /** | |
| * Type-level representation returned by {@link Result}. | |
| * | |
| * @category schemas | |
| * @since 4.0.0 | |
| */ | |
| export interface Result<A extends Top, E extends Top> extends | |
| declareConstructor< | |
| Result_.Result<A["Type"], E["Type"]>, | |
| Result_.Result<A["Encoded"], E["Encoded"]>, | |
| readonly [A, E], | |
| ResultIso<A, E> | |
| > | |
| { | |
| readonly "Rebuild": Result<A, E> | |
| readonly success: A | |
| readonly failure: E | |
| } | |
| /** | |
| * Iso representation used for `Result` schemas. | |
| * | |
| * **Details** | |
| * | |
| * Successful results are represented as `{ _tag: "Success", success }`, while | |
| * failed results are represented as `{ _tag: "Failure", failure }`. | |
| * | |
| * @category schemas | |
| * @since 4.0.0 | |
| */ | |
| export type ResultIso<A extends Top, E extends Top> = | |
| | { readonly _tag: "Success"; readonly success: A["Iso"] } | |
| | { readonly _tag: "Failure"; readonly failure: E["Iso"] } | |
| /** | |
| * Schema for `Result<A, E>` values. | |
| * | |
| * @category schemas | |
| * @since 4.0.0 | |
| */ | |
| export function Result<A extends Top, E extends Top>( | |
| success: A, | |
| failure: E | |
| ): Result<A, E> { | |
| const schema = declareConstructor< | |
| Result_.Result<A["Type"], E["Type"]>, | |
| Result_.Result<A["Encoded"], E["Encoded"]>, | |
| ResultIso<A, E> | |
| >()( | |
| [success, failure], | |
| ([success, failure]) => (input, ast, options) => { | |
| if (!Result_.isResult(input)) { | |
| return Effect.fail(new SchemaIssue.InvalidType(ast, Option_.some(input))) | |
| } | |
| switch (input._tag) { | |
| case "Success": | |
| return Effect.mapBothEager(SchemaParser.decodeEffect(success)(input.success, options), { | |
| onSuccess: Result_.succeed, | |
| onFailure: (issue) => | |
| new SchemaIssue.Composite(ast, Option_.some(input), [new SchemaIssue.Pointer(["success"], issue)]) | |
| }) | |
| case "Failure": | |
| return Effect.mapBothEager(SchemaParser.decodeEffect(failure)(input.failure, options), { | |
| onSuccess: Result_.fail, | |
| onFailure: (issue) => | |
| new SchemaIssue.Composite(ast, Option_.some(input), [new SchemaIssue.Pointer(["failure"], issue)]) | |
| }) | |
| } | |
| }, | |
| { | |
| typeConstructor: { | |
| _tag: "effect/Result" | |
| }, | |
| generation: { | |
| runtime: `Schema.Result(?, ?)`, | |
| Type: `Result.Result<?, ?>`, | |
| importDeclaration: `import * as Result from "effect/Result"` | |
| }, | |
| expected: "Result", | |
| toCodec: ([success, failure]) => | |
| link<Result_.Result<A["Encoded"], E["Encoded"]>>()( | |
| Union([ | |
| Struct({ _tag: Literal("Success"), success }), | |
| Struct({ _tag: Literal("Failure"), failure }) | |
| ]), | |
| SchemaTransformation.transform({ | |
| decode: (e): Result_.Result<A["Encoded"], E["Encoded"]> => | |
| e._tag === "Success" ? Result_.succeed(e.success) : Result_.fail(e.failure), | |
| encode: (r) => | |
| Result_.isSuccess(r) | |
| ? { _tag: "Success", success: r.success } as const | |
| : { _tag: "Failure", failure: r.failure } as const | |
| }) | |
| ), | |
| toArbitrary: ([success, failure]) => (fc, ctx) => { | |
| const terminal = oneOfArbitraries( | |
| fc, | |
| success.terminal?.map((a): Result_.Result<A["Type"], E["Type"]> => Result_.succeed(a)), | |
| failure.terminal?.map((e): Result_.Result<A["Type"], E["Type"]> => Result_.fail(e)) | |
| ) | |
| const arbitrary = fc.oneof( | |
| success.arbitrary.map((a): Result_.Result<A["Type"], E["Type"]> => Result_.succeed(a)), | |
| failure.arbitrary.map((e): Result_.Result<A["Type"], E["Type"]> => Result_.fail(e)) | |
| ) | |
| return withRecursion(fc, ctx, terminal, arbitrary) | |
| }, | |
| toEquivalence: ([success, failure]) => Result_.makeEquivalence(success, failure), | |
| toFormatter: ([success, failure]) => | |
| Result_.match({ | |
| onSuccess: (t) => `success(${success(t)})`, | |
| onFailure: (t) => `failure(${failure(t)})` | |
| }) | |
| } | |
| ) | |
| return make(schema.ast, { success, failure }) | |
| } | |
| /** | |
| * Type-level representation returned by {@link Redacted}. | |
| * | |
| * @category Redacted | |
| * @since 3.10.0 | |
| */ | |
| export interface Redacted<S extends Top> extends | |
| declareConstructor< | |
| Redacted_.Redacted<S["Type"]>, | |
| Redacted_.Redacted<S["Encoded"]>, | |
| readonly [S] | |
| > | |
| { | |
| readonly "Rebuild": Redacted<S> | |
| readonly value: S | |
| } | |
| /** | |
| * Schema for values that hide sensitive information from error output and | |
| * inspection. | |
| * | |
| * **Details** | |
| * | |
| * If the wrapped schema fails, the issue will be redacted to prevent both | |
| * the actual value and the schema details from being exposed. | |
| * | |
| * Options: | |
| * | |
| * - `label`: When provided, the schema will behave as follows: | |
| * - Values will be validated against the label in addition to the wrapped schema | |
| * - The default JSON serializer will deserialize into a `Redacted` instance with the label | |
| * - The arbitrary generator will produce a `Redacted` instance with the label | |
| * - The formatter will return the label | |
| * - `disallowJsonEncode`: When set to `true`, when attempting to encode a `Redacted` instance | |
| * into JSON, it will fail with an error. This is useful when the wrapped schema is | |
| * sensitive and should not be exposed in JSON. | |
| * | |
| * @category Redacted | |
| * @since 3.10.0 | |
| */ | |
| export function Redacted<S extends Top>(value: S, options?: { | |
| readonly label?: string | undefined | |
| readonly disallowJsonEncode?: boolean | undefined | |
| }): Redacted<S> { | |
| const decodeLabel = typeof options?.label === "string" | |
| ? SchemaParser.decodeUnknownEffect(Literal(options.label)) | |
| : undefined | |
| const schema = declareConstructor<Redacted_.Redacted<S["Type"]>, Redacted_.Redacted<S["Encoded"]>>()( | |
| [value], | |
| ([value]) => (input, ast, poptions) => { | |
| if (Redacted_.isRedacted(input)) { | |
| const label: Effect.Effect<void, SchemaIssue.Issue, never> = decodeLabel !== undefined | |
| ? Effect.mapErrorEager( | |
| decodeLabel(input.label, poptions), | |
| (issue) => new SchemaIssue.Pointer(["label"], issue) | |
| ) | |
| : Effect.void | |
| return Effect.flatMapEager( | |
| label, | |
| () => | |
| Effect.mapBothEager( | |
| SchemaParser.decodeUnknownEffect(value)(Redacted_.value(input), poptions), | |
| { | |
| onSuccess: () => input, | |
| onFailure: (/** ignore the actual issue because of security reasons */) => { | |
| const oinput = Option_.some(input) | |
| return new SchemaIssue.Composite(ast, oinput, [ | |
| new SchemaIssue.Pointer(["value"], new SchemaIssue.InvalidValue(oinput)) | |
| ]) | |
| } | |
| } | |
| ) | |
| ) | |
| } | |
| return Effect.fail(new SchemaIssue.InvalidType(ast, Option_.some(input))) | |
| }, | |
| { | |
| typeConstructor: { | |
| _tag: "effect/Redacted", | |
| options | |
| }, | |
| generation: { | |
| runtime: options !== undefined ? `Schema.Redacted(?, ${format(options)})` : `Schema.Redacted(?)`, | |
| Type: `Redacted.Redacted<?>`, | |
| importDeclaration: `import * as Redacted from "effect/Redacted"` | |
| }, | |
| expected: "Redacted", | |
| toCodecJson: ([value]) => | |
| link<Redacted_.Redacted<S["Encoded"]>>()( | |
| redact(value), | |
| { | |
| decode: SchemaGetter.transform((e) => Redacted_.make(e, { label: options?.label })), | |
| encode: options?.disallowJsonEncode ? | |
| SchemaGetter.forbidden((oe) => | |
| "Cannot serialize Redacted" + | |
| (Option_.isSome(oe) && typeof oe.value.label === "string" ? ` with label: "${oe.value.label}"` : "") | |
| ) : | |
| SchemaGetter.transform(Redacted_.value) | |
| } | |
| ), | |
| toArbitrary: ([value]) => () => ({ | |
| arbitrary: value.arbitrary.map((a) => Redacted_.make(a, { label: options?.label })), | |
| terminal: value.terminal?.map((a) => Redacted_.make(a, { label: options?.label })) | |
| }), | |
| toFormatter: () => globalThis.String, | |
| toEquivalence: ([value]) => Redacted_.makeEquivalence(value) | |
| } | |
| ) | |
| return make(schema.ast, { value }) | |
| } | |
| /** | |
| * Type-level representation returned by {@link RedactedFromValue}. | |
| * | |
| * @category Redacted | |
| * @since 4.0.0 | |
| */ | |
| export interface RedactedFromValue<S extends Top> | |
| extends decodeTo<Redacted<toType<S>>, middlewareDecoding<S, S["DecodingServices"]>> | |
| { | |
| readonly "Rebuild": RedactedFromValue<S> | |
| } | |
| /** | |
| * Middleware that wraps decoded errors in `Redacted`, preventing sensitive | |
| * schema details from leaking in error messages. | |
| * | |
| * @category Redacted | |
| * @since 4.0.0 | |
| */ | |
| export function redact<S extends Top>(schema: S): middlewareDecoding<S, S["DecodingServices"]> { | |
| return schema.pipe(middlewareDecoding(Effect.mapErrorEager(SchemaIssue.redact))) | |
| } | |
| /** | |
| * Decodes a value and wraps it in `Redacted<A>`. Unlike {@link Redacted} which | |
| * expects the input to already be a `Redacted` instance, this schema decodes | |
| * the raw value and wraps it. | |
| * | |
| * @category Redacted | |
| * @since 4.0.0 | |
| */ | |
| export function RedactedFromValue<S extends Top>(value: S, options?: { | |
| readonly label?: string | undefined | |
| readonly disallowEncode?: boolean | undefined | |
| }): RedactedFromValue<S> { | |
| return redact(value).pipe( | |
| decodeTo( | |
| Redacted(toType(value), { | |
| label: options?.label, | |
| disallowJsonEncode: options?.disallowEncode | |
| }), | |
| { | |
| decode: SchemaGetter.transform((t) => Redacted_.make(t, { label: options?.label })), | |
| encode: options?.disallowEncode ? | |
| SchemaGetter.forbidden((oe) => | |
| "Cannot encode Redacted" + | |
| (Option_.isSome(oe) && typeof oe.value.label === "string" ? ` with label: "${oe.value.label}"` : "") | |
| ) : | |
| SchemaGetter.transform(Redacted_.value) | |
| } | |
| ) | |
| ) | |
| } | |
| /** | |
| * Type-level representation returned by {@link CauseReason}. | |
| * | |
| * @category CauseReason | |
| * @since 4.0.0 | |
| */ | |
| export interface CauseReason<E extends Top, D extends Top> extends | |
| declareConstructor< | |
| Cause_.Reason<E["Type"]>, | |
| Cause_.Reason<E["Encoded"]>, | |
| readonly [E, D], | |
| CauseReasonIso<E, D> | |
| > | |
| { | |
| readonly "Rebuild": CauseReason<E, D> | |
| readonly error: E | |
| readonly defect: D | |
| } | |
| /** | |
| * Iso representation used for `CauseReason` schemas. | |
| * | |
| * **Details** | |
| * | |
| * Failures are represented with a `Fail` tag and encoded error, defects with a | |
| * `Die` tag and encoded defect, and interrupts with an optional `fiberId`. | |
| * | |
| * @category CauseReason | |
| * @since 4.0.0 | |
| */ | |
| export type CauseReasonIso<E extends Top, D extends Top> = { | |
| readonly _tag: "Fail" | |
| readonly error: E["Iso"] | |
| } | { | |
| readonly _tag: "Die" | |
| readonly error: D["Iso"] | |
| } | { | |
| readonly _tag: "Interrupt" | |
| readonly fiberId: number | undefined | |
| } | |
| /** | |
| * Creates a schema for `Cause.Reason` values using separate schemas for typed | |
| * failures and unexpected defects. | |
| * | |
| * **When to use** | |
| * | |
| * Use when serializing or decoding individual cause reasons separately from a | |
| * full failure cause, with distinct schemas for typed errors and defects. | |
| * | |
| * **Details** | |
| * | |
| * `Fail` reasons use the `error` schema, `Die` reasons use the `defect` schema, | |
| * and `Interrupt` reasons carry only an optional fiber id. | |
| * | |
| * @see {@link Cause} for constructing schemas for full Cause values | |
| * @see {@link CauseReasonIso} for the ISO shape of each cause reason | |
| * | |
| * @category CauseReason | |
| * @since 4.0.0 | |
| */ | |
| export function CauseReason<E extends Top, D extends Top>(error: E, defect: D): CauseReason<E, D> { | |
| const schema = declareConstructor<Cause_.Reason<E["Type"]>, Cause_.Reason<E["Encoded"]>, CauseReasonIso<E, D>>()( | |
| [error, defect], | |
| ([error, defect]) => (input, ast, options) => { | |
| if (!Cause_.isReason(input)) { | |
| return Effect.fail(new SchemaIssue.InvalidType(ast, Option_.some(input))) | |
| } | |
| switch (input._tag) { | |
| case "Fail": | |
| return Effect.mapBothEager( | |
| SchemaParser.decodeUnknownEffect(error)(input.error, options), | |
| { | |
| onSuccess: Cause_.makeFailReason, | |
| onFailure: (issue) => | |
| new SchemaIssue.Composite(ast, Option_.some(input), [new SchemaIssue.Pointer(["error"], issue)]) | |
| } | |
| ) | |
| case "Die": | |
| return Effect.mapBothEager( | |
| SchemaParser.decodeUnknownEffect(defect)(input.defect, options), | |
| { | |
| onSuccess: Cause_.makeDieReason, | |
| onFailure: (issue) => | |
| new SchemaIssue.Composite(ast, Option_.some(input), [new SchemaIssue.Pointer(["defect"], issue)]) | |
| } | |
| ) | |
| case "Interrupt": | |
| return Effect.succeed(input) | |
| } | |
| }, | |
| { | |
| typeConstructor: { | |
| _tag: "effect/Cause/Failure" | |
| }, | |
| generation: { | |
| runtime: `Schema.CauseReason(?, ?)`, | |
| Type: `Cause.Failure<?, ?>`, | |
| importDeclaration: `import * as Cause from "effect/Cause"` | |
| }, | |
| expected: "Cause.Failure", | |
| toCodec: ([error, defect]) => | |
| link<Cause_.Reason<E["Encoded"]>>()( | |
| Union([ | |
| Struct({ _tag: Literal("Fail"), error }), | |
| Struct({ _tag: Literal("Die"), defect }), | |
| Struct({ _tag: Literal("Interrupt"), fiberId: UndefinedOr(Finite) }) | |
| ]), | |
| SchemaTransformation.transform({ | |
| decode: (e) => { | |
| switch (e._tag) { | |
| case "Fail": | |
| return Cause_.makeFailReason(e.error) | |
| case "Die": | |
| return Cause_.makeDieReason(e.defect) | |
| case "Interrupt": | |
| return Cause_.makeInterruptReason(e.fiberId) | |
| } | |
| }, | |
| encode: identity | |
| }) | |
| ), | |
| toArbitrary: ([error, defect]) => causeReasonToArbitrary(error, defect), | |
| toEquivalence: ([error, defect]) => causeReasonToEquivalence(error, defect), | |
| toFormatter: ([error, defect]) => causeReasonToFormatter(error, defect) | |
| } | |
| ) | |
| return make(schema.ast, { error, defect }) | |
| } | |
| function causeReasonToArbitrary<E, D>( | |
| error: Annotations.ToArbitrary.TypeParameter<E>, | |
| defect: Annotations.ToArbitrary.TypeParameter<D> | |
| ) { | |
| return (fc: typeof FastCheck, ctx: Annotations.ToArbitrary.Context) => { | |
| const terminal = fc.constant(Cause_.makeInterruptReason()) | |
| const arbitrary = fc.oneof( | |
| terminal, | |
| fc.integer({ min: 1 }).map(Cause_.makeInterruptReason), | |
| error.arbitrary.map((e) => Cause_.makeFailReason(e)), | |
| defect.arbitrary.map((d) => Cause_.makeDieReason(d)) | |
| ) | |
| return withRecursion(fc, ctx, terminal, arbitrary) | |
| } | |
| } | |
| function causeReasonToEquivalence<E>(error: Equivalence.Equivalence<E>, defect: Equivalence.Equivalence<unknown>) { | |
| return (a: Cause_.Reason<E>, b: Cause_.Reason<E>) => { | |
| if (a._tag !== b._tag) return false | |
| switch (a._tag) { | |
| case "Fail": | |
| return error(a.error, (b as Cause_.Fail<E>).error) | |
| case "Die": | |
| return defect(a.defect, (b as Cause_.Die).defect) | |
| case "Interrupt": | |
| return a.fiberId === (b as Cause_.Interrupt).fiberId | |
| } | |
| } | |
| } | |
| function causeReasonToFormatter<E>(error: Formatter<E>, defect: Formatter<unknown>) { | |
| return (t: Cause_.Reason<E>) => { | |
| switch (t._tag) { | |
| case "Fail": | |
| return `Fail(${error(t.error)})` | |
| case "Die": | |
| return `Die(${defect(t.defect)})` | |
| case "Interrupt": | |
| return "Interrupt" | |
| } | |
| } | |
| } | |
| /** | |
| * Type-level representation returned by {@link Cause}. | |
| * | |
| * @category Cause | |
| * @since 3.10.0 | |
| */ | |
| export interface Cause<E extends Top, D extends Top> extends | |
| declareConstructor< | |
| Cause_.Cause<E["Type"]>, | |
| Cause_.Cause<E["Encoded"]>, | |
| readonly [E, D], | |
| CauseIso<E, D> | |
| > | |
| { | |
| readonly "Rebuild": Cause<E, D> | |
| readonly error: E | |
| readonly defect: D | |
| } | |
| /** | |
| * Iso representation used for `Cause` schemas: an ordered array of | |
| * `CauseReasonIso` values. | |
| * | |
| * **When to use** | |
| * | |
| * Use when working with the ISO shape of a `Cause` schema, such as `toIso` | |
| * optics or codecs that expose a cause as its ordered array of encoded reasons. | |
| * | |
| * @see {@link Cause} for constructing schemas for full Cause values | |
| * @see {@link CauseReasonIso} for the ISO shape of each array element | |
| * | |
| * @category Cause | |
| * @since 4.0.0 | |
| */ | |
| export type CauseIso<E extends Top, D extends Top> = ReadonlyArray<CauseReasonIso<E, D>> | |
| /** | |
| * Creates a schema for `Cause` values using separate schemas for typed failures | |
| * and unexpected defects. | |
| * | |
| * **When to use** | |
| * | |
| * Use to validate, transform, or serialize Effect failure causes when typed | |
| * failures and unexpected defects need separate schemas. | |
| * | |
| * **Details** | |
| * | |
| * The `error` schema is applied to `Fail` reasons and the `defect` schema is | |
| * applied to `Die` reasons. Interrupt reasons do not use either schema and | |
| * carry only an optional fiber id. | |
| * | |
| * @see {@link CauseReason} for the schema used by each individual cause reason | |
| * @see {@link CauseIso} for the ordered array representation used by the schema ISO | |
| * | |
| * @category Cause | |
| * @since 3.10.0 | |
| */ | |
| export function Cause<E extends Top, D extends Top>(error: E, defect: D): Cause<E, D> { | |
| const schema = declareConstructor<Cause_.Cause<E["Type"]>, Cause_.Cause<E["Encoded"]>, CauseIso<E, D>>()( | |
| [error, defect], | |
| ([error, defect]) => { | |
| const failures = ArraySchema(CauseReason(error, defect)) | |
| return (input, ast, options) => { | |
| if (!Cause_.isCause(input)) { | |
| return Effect.fail(new SchemaIssue.InvalidType(ast, Option_.some(input))) | |
| } | |
| return Effect.mapBothEager(SchemaParser.decodeUnknownEffect(failures)(input.reasons, options), { | |
| onSuccess: Cause_.fromReasons, | |
| onFailure: (issue) => | |
| new SchemaIssue.Composite(ast, Option_.some(input), [new SchemaIssue.Pointer(["failures"], issue)]) | |
| }) | |
| } | |
| }, | |
| { | |
| typeConstructor: { | |
| _tag: "effect/Cause" | |
| }, | |
| generation: { | |
| runtime: `Schema.Cause(?, ?)`, | |
| Type: `Cause.Cause<?, ?>`, | |
| importDeclaration: `import * as Cause from "effect/Cause"` | |
| }, | |
| expected: "Cause", | |
| toCodec: ([error, defect]) => | |
| link<Cause_.Cause<E["Encoded"]>>()( | |
| ArraySchema(CauseReason(error, defect)), | |
| SchemaTransformation.transform({ | |
| decode: Cause_.fromReasons, | |
| encode: ({ reasons: failures }) => failures | |
| }) | |
| ), | |
| toArbitrary: ([error, defect]) => causeToArbitrary(error, defect), | |
| toEquivalence: ([error, defect]) => causeToEquivalence(error, defect), | |
| toFormatter: ([error, defect]) => causeToFormatter(error, defect) | |
| } | |
| ) | |
| return make(schema.ast, { error, defect }) | |
| } | |
| function causeToArbitrary<E, D>( | |
| error: Annotations.ToArbitrary.TypeParameter<E>, | |
| defect: Annotations.ToArbitrary.TypeParameter<D> | |
| ) { | |
| return (fc: typeof FastCheck, ctx: Annotations.ToArbitrary.Context) => { | |
| const reason = causeReasonToArbitrary(error, defect)(fc, ctx) | |
| const terminal = fc.constant(Cause_.empty) | |
| const arbitrary = fc.array(reason.arbitrary).map(Cause_.fromReasons) | |
| return withRecursion(fc, ctx, terminal, arbitrary) | |
| } | |
| } | |
| function causeToEquivalence<E>(error: Equivalence.Equivalence<E>, defect: Equivalence.Equivalence<unknown>) { | |
| const failures = Equivalence.Array(causeReasonToEquivalence(error, defect)) | |
| return (a: Cause_.Cause<E>, b: Cause_.Cause<E>) => failures(a.reasons, b.reasons) | |
| } | |
| function causeToFormatter<E>(error: Formatter<E>, defect: Formatter<unknown>) { | |
| const causeReason = causeReasonToFormatter(error, defect) | |
| return (t: Cause_.Cause<E>) => `Cause([${t.reasons.map(causeReason).join(", ")}])` | |
| } | |
| /** | |
| * Type-level representation of {@link Error}. | |
| * | |
| * @category Error | |
| * @since 4.0.0 | |
| */ | |
| export interface Error extends instanceOf<globalThis.Error> { | |
| readonly "Rebuild": Error | |
| } | |
| /** | |
| * Options for {@link Error} and {@link Defect}. | |
| * | |
| * @category options | |
| * @since 4.0.0 | |
| */ | |
| export interface ErrorOptions { | |
| /** | |
| * Includes string stack traces in encoded `Error` values when set to `true`. | |
| * | |
| * @default false | |
| */ | |
| readonly includeStack?: boolean | undefined | |
| /** | |
| * Excludes `Error.cause` values from encoded `Error` values when set to | |
| * `true`. | |
| * | |
| * @default false | |
| */ | |
| readonly excludeCause?: boolean | undefined | |
| } | |
| type ErrorOptionsKey = 0 | 1 | 2 | 3 | |
| const getErrorOptionsKey = (options?: ErrorOptions): ErrorOptionsKey => | |
| ((options?.includeStack === true ? 1 : 0) | | |
| (options?.excludeCause === true ? 2 : 0)) as ErrorOptionsKey | |
| const getErrorOptions = (key: ErrorOptionsKey): ErrorOptions | undefined => { | |
| switch (key) { | |
| case 0: | |
| return undefined | |
| case 1: | |
| return { includeStack: true } | |
| case 2: | |
| return { excludeCause: true } | |
| case 3: | |
| return { includeStack: true, excludeCause: true } | |
| } | |
| } | |
| const errorSchemaCache: Array<Error | undefined> = [] | |
| /** | |
| * Schema for JavaScript `Error` objects. | |
| * | |
| * **Details** | |
| * | |
| * Default JSON serializer: | |
| * | |
| * Encodes an `Error` as an object with `message`, optional `name`, and optional | |
| * `cause` properties, and decodes that object back into an `Error`. Stack | |
| * traces are omitted by default for security. Pass `{ includeStack: true }` to | |
| * include stack traces, or `{ excludeCause: true }` to omit causes. | |
| * | |
| * @category constructors | |
| * @since 4.0.0 | |
| */ | |
| export function Error(options?: ErrorOptions): Error { | |
| const key = getErrorOptionsKey(options) | |
| const cached = errorSchemaCache[key] | |
| if (cached !== undefined) { | |
| return cached | |
| } | |
| const normalizedOptions = getErrorOptions(key) | |
| const schema = instanceOf(globalThis.Error, { | |
| typeConstructor: { | |
| _tag: "Error", | |
| ...(normalizedOptions === undefined ? {} : { options: normalizedOptions }) | |
| }, | |
| generation: { | |
| runtime: normalizedOptions !== undefined ? `Schema.Error(${format(normalizedOptions)})` : `Schema.Error()`, | |
| Type: `globalThis.Error` | |
| }, | |
| expected: "Error", | |
| toCodecJson: () => link<globalThis.Error>()(JsonError, SchemaTransformation.errorFromJsonError(normalizedOptions)), | |
| toArbitrary: () => (fc) => fc.string().map((message) => new globalThis.Error(message)) | |
| }) | |
| errorSchemaCache[key] = schema | |
| return schema | |
| } | |
| /** | |
| * Type-level representation of {@link Defect}. | |
| * | |
| * @category Defect | |
| * @since 3.10.0 | |
| */ | |
| export interface Defect extends decodeTo<Unknown, typeof Json> { | |
| readonly "Rebuild": Defect | |
| } | |
| const defectSchemaCache: Array<Defect | undefined> = [] | |
| /** | |
| * Schema for unexpected defect values represented as `unknown` with a JSON | |
| * encoded form. | |
| * | |
| * **When to use** | |
| * | |
| * Use when you need a schema for `Cause` defects or other unexpected failures | |
| * whose runtime value may be any value. | |
| * | |
| * **Details** | |
| * | |
| * The encoded side is {@link Json}. During decoding, JSON objects with a string | |
| * `message` property are decoded into JavaScript `Error` values, preserving a | |
| * non-default `name` and any string `stack`. Other JSON values decode | |
| * unchanged. | |
| * | |
| * During encoding, JavaScript `Error` values encode to JSON objects with | |
| * `name`, `message`, and optional `cause` properties. Pass | |
| * `{ includeStack: true }` to include string stack traces in encoded `Error` | |
| * defects, or `{ excludeCause: true }` to omit causes. Other values are | |
| * serialized through Effect's JSON formatter and then parsed back into JSON | |
| * when possible. | |
| * | |
| * **Gotchas** | |
| * | |
| * This schema is for carrying defects across JSON boundaries, not for | |
| * preserving every JavaScript value exactly. Some values cannot round-trip | |
| * unchanged: | |
| * | |
| * - A non-`Error` object such as `{ message: "boom" }` encodes as an | |
| * error-shaped JSON object and decodes back as an `Error`. | |
| * - JSON serialization normalizes unsupported values. For example, | |
| * `undefined` array elements encode as `null`, unsupported object properties | |
| * are omitted, and circular references are dropped. | |
| * - Values that cannot be represented as JSON fall back to Effect's formatted | |
| * string representation. | |
| * | |
| * @see {@link Error} for a schema that only accepts JavaScript `Error` values. | |
| * @category constructors | |
| * @since 4.0.0 | |
| */ | |
| export function Defect(options?: ErrorOptions): Defect { | |
| const key = getErrorOptionsKey(options) | |
| const cached = defectSchemaCache[key] | |
| if (cached !== undefined) { | |
| return cached | |
| } | |
| const schema = Json.pipe(decodeTo(Unknown, SchemaTransformation.defectFromJson(getErrorOptions(key)))) | |
| defectSchemaCache[key] = schema | |
| return schema | |
| } | |
| /** | |
| * Type-level representation returned by {@link Exit}. | |
| * | |
| * @category Exit | |
| * @since 3.10.0 | |
| */ | |
| export interface Exit<A extends Top, E extends Top, D extends Top> extends | |
| declareConstructor< | |
| Exit_.Exit<A["Type"], E["Type"]>, | |
| Exit_.Exit<A["Encoded"], E["Encoded"]>, | |
| readonly [A, E, D], | |
| ExitIso<A, E, D> | |
| > | |
| { | |
| readonly "Rebuild": Exit<A, E, D> | |
| readonly value: A | |
| readonly error: E | |
| readonly defect: D | |
| } | |
| /** | |
| * Iso representation used for `Exit` schemas. | |
| * | |
| * **Details** | |
| * | |
| * Successful exits are represented as `{ _tag: "Success", value }`, while failed | |
| * exits are represented as `{ _tag: "Failure", cause }`. | |
| * | |
| * @category Exit | |
| * @since 4.0.0 | |
| */ | |
| export type ExitIso<A extends Top, E extends Top, D extends Top> = { | |
| readonly _tag: "Success" | |
| readonly value: A["Iso"] | |
| } | { | |
| readonly _tag: "Failure" | |
| readonly cause: CauseIso<E, D> | |
| } | |
| /** | |
| * Creates a schema for `Exit` values using schemas for the success value, typed | |
| * failure, and unexpected defect channels. | |
| * | |
| * **When to use** | |
| * | |
| * Use when serializing or validating an effect outcome where success, typed | |
| * failure, and defects each need their own schema. | |
| * | |
| * @category Exit | |
| * @since 3.10.0 | |
| */ | |
| export function Exit<A extends Top, E extends Top, D extends Top>(value: A, error: E, defect: D): Exit<A, E, D> { | |
| const schema = declareConstructor< | |
| Exit_.Exit<A["Type"], E["Type"]>, | |
| Exit_.Exit<A["Encoded"], E["Encoded"]>, | |
| ExitIso<A, E, D> | |
| >()( | |
| [value, error, defect], | |
| ([value, error, defect]) => { | |
| const cause = Cause(error, defect) | |
| return (input, ast, options) => { | |
| if (!Exit_.isExit(input)) { | |
| return Effect.fail(new SchemaIssue.InvalidType(ast, Option_.some(input))) | |
| } | |
| switch (input._tag) { | |
| case "Success": | |
| return Effect.mapBothEager( | |
| SchemaParser.decodeUnknownEffect(value)(input.value, options), | |
| { | |
| onSuccess: Exit_.succeed, | |
| onFailure: (issue) => | |
| new SchemaIssue.Composite(ast, Option_.some(input), [new SchemaIssue.Pointer(["value"], issue)]) | |
| } | |
| ) | |
| case "Failure": | |
| return Effect.mapBothEager( | |
| SchemaParser.decodeUnknownEffect(cause)(input.cause, options), | |
| { | |
| onSuccess: Exit_.failCause, | |
| onFailure: (issue) => | |
| new SchemaIssue.Composite(ast, Option_.some(input), [new SchemaIssue.Pointer(["cause"], issue)]) | |
| } | |
| ) | |
| } | |
| } | |
| }, | |
| { | |
| typeConstructor: { | |
| _tag: "effect/Exit" | |
| }, | |
| generation: { | |
| runtime: `Schema.Exit(?, ?, ?)`, | |
| Type: `Exit.Exit<?, ?, ?>`, | |
| importDeclaration: `import * as Exit from "effect/Exit"` | |
| }, | |
| expected: "Exit", | |
| toCodec: ([value, error, defect]) => | |
| link<Exit_.Exit<A["Encoded"], E["Encoded"]>>()( | |
| Union([ | |
| Struct({ _tag: Literal("Success"), value }), | |
| Struct({ _tag: Literal("Failure"), cause: Cause(error, defect) }) | |
| ]), | |
| SchemaTransformation.transform({ | |
| decode: (e): Exit_.Exit<A["Encoded"], E["Encoded"]> => | |
| e._tag === "Success" ? Exit_.succeed(e.value) : Exit_.failCause(e.cause), | |
| encode: (exit) => | |
| Exit_.isSuccess(exit) | |
| ? { _tag: "Success", value: exit.value } as const | |
| : { _tag: "Failure", cause: exit.cause } as const | |
| }) | |
| ), | |
| toArbitrary: ([value, error, defect]) => (fc, ctx) => { | |
| const cause = causeToArbitrary(error, defect)(fc, ctx) | |
| const terminal = oneOfArbitraries( | |
| fc, | |
| value.terminal?.map((v): Exit_.Exit<A["Type"], E["Type"]> => Exit_.succeed(v)), | |
| cause.terminal?.map((cause): Exit_.Exit<A["Type"], E["Type"]> => Exit_.failCause(cause)) | |
| ) | |
| const arbitrary = fc.oneof( | |
| value.arbitrary.map((v): Exit_.Exit<A["Type"], E["Type"]> => Exit_.succeed(v)), | |
| cause.arbitrary.map((cause): Exit_.Exit<A["Type"], E["Type"]> => Exit_.failCause(cause)) | |
| ) | |
| return withRecursion(fc, ctx, terminal, arbitrary) | |
| }, | |
| toEquivalence: ([value, error, defect]) => { | |
| const cause = causeToEquivalence(error, defect) | |
| return (a, b) => { | |
| if (a._tag !== b._tag) return false | |
| switch (a._tag) { | |
| case "Success": | |
| return value(a.value, (b as Exit_.Success<A["Type"]>).value) | |
| case "Failure": | |
| return cause(a.cause, (b as Exit_.Failure<E["Type"], D["Type"]>).cause) | |
| } | |
| } | |
| }, | |
| toFormatter: ([value, error, defect]) => { | |
| const cause = causeToFormatter(error, defect) | |
| return (t) => { | |
| switch (t._tag) { | |
| case "Success": | |
| return `Exit.Success(${value(t.value)})` | |
| case "Failure": | |
| return `Exit.Failure(${cause(t.cause)})` | |
| } | |
| } | |
| } | |
| } | |
| ) | |
| return make(schema.ast, { value, error, defect }) | |
| } | |
| /** | |
| * Type-level representation returned by {@link ReadonlyMap}. | |
| * | |
| * @category ReadonlyMap | |
| * @since 4.0.0 | |
| */ | |
| export interface $ReadonlyMap<Key extends Top, Value extends Top> extends | |
| declareConstructor< | |
| globalThis.ReadonlyMap<Key["Type"], Value["Type"]>, | |
| globalThis.ReadonlyMap<Key["Encoded"], Value["Encoded"]>, | |
| readonly [Key, Value], | |
| ReadonlyMapIso<Key, Value> | |
| > | |
| { | |
| readonly "Rebuild": $ReadonlyMap<Key, Value> | |
| readonly key: Key | |
| readonly value: Value | |
| } | |
| /** | |
| * Iso representation used for `ReadonlyMap` schemas: an array of readonly | |
| * `[key, value]` tuples using each entry schema's `Iso` type. | |
| * | |
| * @category ReadonlyMap | |
| * @since 4.0.0 | |
| */ | |
| export type ReadonlyMapIso<Key extends Top, Value extends Top> = ReadonlyArray<readonly [Key["Iso"], Value["Iso"]]> | |
| function oneOfArbitraries<T>( | |
| fc: typeof FastCheck, | |
| a: FastCheck.Arbitrary<T> | undefined, | |
| b: FastCheck.Arbitrary<T> | undefined | |
| ) { | |
| return a === undefined ? b : b === undefined ? a : fc.oneof(a, b) | |
| } | |
| function withRecursion<T>( | |
| fc: typeof FastCheck, | |
| ctx: Annotations.ToArbitrary.Context, | |
| terminal: FastCheck.Arbitrary<T> | undefined, | |
| arbitrary: FastCheck.Arbitrary<T> | |
| ) { | |
| return { | |
| arbitrary: terminal === undefined || ctx.recursion === undefined | |
| ? arbitrary | |
| : fc.oneof(ctx.recursion, terminal, arbitrary), | |
| terminal | |
| } | |
| } | |
| function arrayFromItems<T>( | |
| fc: typeof FastCheck, | |
| item: FastCheck.Arbitrary<T>, | |
| constraints: FastCheck.ArrayConstraints | undefined, | |
| comparator?: ((a: T, b: T) => boolean) | undefined | |
| ) { | |
| return comparator === undefined | |
| ? fc.array(item, constraints) | |
| : fc.uniqueArray(item, { ...constraints, comparator }) | |
| } | |
| function collectionArbitrary<T, Out>( | |
| fc: typeof FastCheck, | |
| ctx: Annotations.ToArbitrary.Context, | |
| item: FastCheck.Arbitrary<T>, | |
| terminalItem: FastCheck.Arbitrary<T> | undefined, | |
| fromIterable: (items: Array<T>) => Out, | |
| comparator?: ((a: T, b: T) => boolean) | undefined | |
| ) { | |
| const constraint = ctx.constraint | |
| const constraints = constraint === undefined || | |
| (constraint.minLength === undefined && constraint.maxLength === undefined) | |
| ? undefined | |
| : { | |
| ...(constraint.minLength !== undefined ? { minLength: constraint.minLength } : {}), | |
| ...(constraint.maxLength !== undefined ? { maxLength: constraint.maxLength } : {}) | |
| } | |
| if ( | |
| constraints?.minLength !== undefined && constraints.maxLength !== undefined && | |
| constraints.minLength > constraints.maxLength | |
| ) { | |
| throw new globalThis.Error("Unable to derive an arbitrary for size constraints") | |
| } | |
| const minLength = constraints?.minLength ?? 0 | |
| const terminal = minLength === 0 | |
| ? fc.constant<Array<T>>([]) | |
| : terminalItem === undefined | |
| ? undefined | |
| : arrayFromItems(fc, terminalItem, { ...constraints, maxLength: minLength }, comparator) | |
| const arrays = withRecursion( | |
| fc, | |
| ctx, | |
| terminal, | |
| arrayFromItems(fc, item, constraints, comparator) | |
| ) | |
| return { | |
| arbitrary: arrays.arbitrary.map(fromIterable), | |
| terminal: arrays.terminal?.map(fromIterable) | |
| } | |
| } | |
| function entriesArbitrary<K, V, Out>( | |
| fc: typeof FastCheck, | |
| ctx: Annotations.ToArbitrary.Context, | |
| key: Annotations.ToArbitrary.TypeParameter<K>, | |
| value: Annotations.ToArbitrary.TypeParameter<V>, | |
| fromIterable: (items: Array<[K, V]>) => Out | |
| ) { | |
| return collectionArbitrary( | |
| fc, | |
| ctx, | |
| fc.tuple(key.arbitrary, value.arbitrary), | |
| key.terminal === undefined || value.terminal === undefined ? undefined : fc.tuple(key.terminal, value.terminal), | |
| fromIterable, | |
| ([a], [b]) => Equal.equals(a, b) | |
| ) | |
| } | |
| /** | |
| * Schema for readonly maps whose keys and values conform to the provided | |
| * schemas. | |
| * | |
| * @category ReadonlyMap | |
| * @since 3.10.0 | |
| */ | |
| export function ReadonlyMap<Key extends Top, Value extends Top>(key: Key, value: Value): $ReadonlyMap<Key, Value> { | |
| const schema = declareConstructor< | |
| globalThis.ReadonlyMap<Key["Type"], Value["Type"]>, | |
| globalThis.ReadonlyMap<Key["Encoded"], Value["Encoded"]>, | |
| ReadonlyMapIso<Key, Value> | |
| >()( | |
| [key, value], | |
| ([key, value]) => { | |
| const array = ArraySchema(Tuple([key, value])) | |
| return (input, ast, options) => { | |
| if (input instanceof globalThis.Map) { | |
| return Effect.mapBothEager( | |
| SchemaParser.decodeUnknownEffect(array)([...input], options), | |
| { | |
| onSuccess: (array: ReadonlyArray<readonly [Key["Type"], Value["Type"]]>) => new globalThis.Map(array), | |
| onFailure: (issue) => | |
| new SchemaIssue.Composite(ast, Option_.some(input), [new SchemaIssue.Pointer(["entries"], issue)]) | |
| } | |
| ) | |
| } | |
| return Effect.fail(new SchemaIssue.InvalidType(ast, Option_.some(input))) | |
| } | |
| }, | |
| { | |
| typeConstructor: { | |
| _tag: "ReadonlyMap" | |
| }, | |
| generation: { | |
| runtime: `Schema.ReadonlyMap(?, ?)`, | |
| Type: `globalThis.ReadonlyMap<?, ?>` | |
| }, | |
| expected: "ReadonlyMap", | |
| toCodec: ([key, value]) => | |
| link<globalThis.Map<Key["Encoded"], Value["Encoded"]>>()( | |
| ArraySchema(Tuple([key, value])), | |
| SchemaTransformation.transform({ | |
| decode: (e) => new globalThis.Map(e), | |
| encode: (map) => [...map.entries()] | |
| }) | |
| ), | |
| toArbitrary: ([key, value]) => (fc, ctx) => entriesArbitrary(fc, ctx, key, value, (as) => new globalThis.Map(as)), | |
| toEquivalence: ([key, value]) => Equal.makeCompareMap(key, value), | |
| toFormatter: ([key, value]) => (t) => { | |
| const size = t.size | |
| if (size === 0) { | |
| return "ReadonlyMap(0) {}" | |
| } | |
| const entries = globalThis.Array.from(t.entries()).sort().map(([k, v]) => `${key(k)} => ${value(v)}`) | |
| return `ReadonlyMap(${size}) { ${entries.join(", ")} }` | |
| } | |
| } | |
| ) | |
| return make(schema.ast, { key, value }) | |
| } | |
| /** | |
| * Type-level representation returned by {@link HashMap}. | |
| * | |
| * @category HashMap | |
| * @since 3.10.0 | |
| */ | |
| export interface HashMap<Key extends Top, Value extends Top> extends | |
| declareConstructor< | |
| HashMap_.HashMap<Key["Type"], Value["Type"]>, | |
| HashMap_.HashMap<Key["Encoded"], Value["Encoded"]>, | |
| readonly [Key, Value], | |
| HashMapIso<Key, Value> | |
| > | |
| { | |
| readonly "Rebuild": HashMap<Key, Value> | |
| readonly key: Key | |
| readonly value: Value | |
| } | |
| /** | |
| * Iso representation used for `HashMap` schemas: an array of readonly | |
| * `[key, value]` tuples using each entry schema's `Iso` type. | |
| * | |
| * @category HashMap | |
| * @since 4.0.0 | |
| */ | |
| export type HashMapIso<Key extends Top, Value extends Top> = ReadonlyArray<readonly [Key["Iso"], Value["Iso"]]> | |
| /** | |
| * Schema for hash maps whose keys and values conform to the provided schemas. | |
| * | |
| * @category HashMap | |
| * @since 3.10.0 | |
| */ | |
| export function HashMap<Key extends Top, Value extends Top>(key: Key, value: Value): HashMap<Key, Value> { | |
| const schema = declareConstructor< | |
| HashMap_.HashMap<Key["Type"], Value["Type"]>, | |
| HashMap_.HashMap<Key["Encoded"], Value["Encoded"]>, | |
| HashMapIso<Key, Value> | |
| >()( | |
| [key, value], | |
| ([key, value]) => { | |
| const entries = ArraySchema(Tuple([key, value])) | |
| return (input, ast, options) => { | |
| if (HashMap_.isHashMap(input)) { | |
| return Effect.mapBothEager( | |
| SchemaParser.decodeUnknownEffect(entries)(HashMap_.toEntries(input), options), | |
| { | |
| onSuccess: HashMap_.fromIterable, | |
| onFailure: (issue) => | |
| new SchemaIssue.Composite(ast, Option_.some(input), [new SchemaIssue.Pointer(["entries"], issue)]) | |
| } | |
| ) | |
| } | |
| return Effect.fail(new SchemaIssue.InvalidType(ast, Option_.some(input))) | |
| } | |
| }, | |
| { | |
| typeConstructor: { | |
| _tag: "effect/HashMap" | |
| }, | |
| generation: { | |
| runtime: `Schema.HashMap(?, ?)`, | |
| Type: `HashMap.HashMap<?, ?>`, | |
| importDeclaration: `import * as HashMap from "effect/HashMap"` | |
| }, | |
| expected: "HashMap", | |
| toCodec: ([key, value]) => | |
| link<HashMap_.HashMap<Key["Encoded"], Value["Encoded"]>>()( | |
| ArraySchema(Tuple([key, value])), | |
| SchemaTransformation.transform({ | |
| decode: HashMap_.fromIterable, | |
| encode: HashMap_.toEntries | |
| }) | |
| ), | |
| toArbitrary: ([key, value]) => (fc, ctx) => entriesArbitrary(fc, ctx, key, value, HashMap_.fromIterable), | |
| toEquivalence: ([key, value]) => Equal.makeCompareMap(key, value), | |
| toFormatter: ([key, value]) => (t) => { | |
| const size = HashMap_.size(t) | |
| if (size === 0) { | |
| return "HashMap(0) {}" | |
| } | |
| const entries = HashMap_.toEntries(t).sort().map(([k, v]) => `${key(k)} => ${value(v)}`) | |
| return `HashMap(${size}) { ${entries.join(", ")} }` | |
| } | |
| } | |
| ) | |
| return make(schema.ast, { key, value }) | |
| } | |
| /** | |
| * Type-level representation returned by {@link ReadonlySet}. | |
| * | |
| * @category ReadonlySet | |
| * @since 4.0.0 | |
| */ | |
| export interface $ReadonlySet<Value extends Top> extends | |
| declareConstructor< | |
| globalThis.ReadonlySet<Value["Type"]>, | |
| globalThis.ReadonlySet<Value["Encoded"]>, | |
| readonly [Value], | |
| ReadonlySetIso<Value> | |
| > | |
| { | |
| readonly "Rebuild": $ReadonlySet<Value> | |
| readonly value: Value | |
| } | |
| /** | |
| * Iso representation used for `ReadonlySet` schemas: an array of element values | |
| * using the element schema's `Iso` type. | |
| * | |
| * @category ReadonlySet | |
| * @since 4.0.0 | |
| */ | |
| export type ReadonlySetIso<Value extends Top> = ReadonlyArray<Value["Iso"]> | |
| /** | |
| * Schema for readonly sets whose values conform to the provided element schema. | |
| * | |
| * @category ReadonlySet | |
| * @since 3.10.0 | |
| */ | |
| export function ReadonlySet<Value extends Top>(value: Value): $ReadonlySet<Value> { | |
| const schema = declareConstructor< | |
| globalThis.ReadonlySet<Value["Type"]>, | |
| globalThis.ReadonlySet<Value["Encoded"]>, | |
| ReadonlySetIso<Value> | |
| >()( | |
| [value], | |
| ([value]) => { | |
| const array = ArraySchema(value) | |
| return (input, ast, options) => { | |
| if (input instanceof globalThis.Set) { | |
| return Effect.mapBothEager( | |
| SchemaParser.decodeUnknownEffect(array)([...input], options), | |
| { | |
| onSuccess: (array: ReadonlyArray<Value["Type"]>) => new globalThis.Set(array), | |
| onFailure: (issue) => | |
| new SchemaIssue.Composite(ast, Option_.some(input), [new SchemaIssue.Pointer(["values"], issue)]) | |
| } | |
| ) | |
| } | |
| return Effect.fail(new SchemaIssue.InvalidType(ast, Option_.some(input))) | |
| } | |
| }, | |
| { | |
| typeConstructor: { | |
| _tag: "ReadonlySet" | |
| }, | |
| generation: { | |
| runtime: `Schema.ReadonlySet(?)`, | |
| Type: `globalThis.ReadonlySet<?>` | |
| }, | |
| expected: "ReadonlySet", | |
| toCodec: ([value]) => | |
| link<globalThis.Set<Value["Encoded"]>>()( | |
| ArraySchema(value), | |
| SchemaTransformation.transform({ | |
| decode: (e) => new globalThis.Set(e), | |
| encode: (set) => [...set.values()] | |
| }) | |
| ), | |
| toArbitrary: ([value]) => (fc, ctx) => | |
| collectionArbitrary(fc, ctx, value.arbitrary, value.terminal, (as) => new globalThis.Set(as), Equal.equals), | |
| toEquivalence: ([value]) => Equal.makeCompareSet(value), | |
| toFormatter: ([value]) => (t) => { | |
| const size = t.size | |
| if (size === 0) { | |
| return "ReadonlySet(0) {}" | |
| } | |
| const values = globalThis.Array.from(t.values()).sort().map((v) => `${value(v)}`) | |
| return `ReadonlySet(${size}) { ${values.join(", ")} }` | |
| } | |
| } | |
| ) | |
| return make(schema.ast, { value }) | |
| } | |
| /** | |
| * Type-level representation returned by {@link HashSet}. | |
| * | |
| * @category HashSet | |
| * @since 3.10.0 | |
| */ | |
| export interface HashSet<Value extends Top> extends | |
| declareConstructor< | |
| HashSet_.HashSet<Value["Type"]>, | |
| HashSet_.HashSet<Value["Encoded"]>, | |
| readonly [Value], | |
| HashSetIso<Value> | |
| > | |
| { | |
| readonly "Rebuild": HashSet<Value> | |
| readonly value: Value | |
| } | |
| /** | |
| * Iso representation used for `HashSet` schemas: an array of element values | |
| * using the element schema's `Iso` type. | |
| * | |
| * @category HashSet | |
| * @since 4.0.0 | |
| */ | |
| export type HashSetIso<Value extends Top> = ReadonlyArray<Value["Iso"]> | |
| /** | |
| * Schema for hash sets whose values conform to the provided element schema. | |
| * | |
| * @category HashSet | |
| * @since 3.10.0 | |
| */ | |
| export function HashSet<Value extends Top>(value: Value): HashSet<Value> { | |
| const schema = declareConstructor< | |
| HashSet_.HashSet<Value["Type"]>, | |
| HashSet_.HashSet<Value["Encoded"]>, | |
| HashSetIso<Value> | |
| >()( | |
| [value], | |
| ([value]) => { | |
| const values = ArraySchema(value) | |
| return (input, ast, options) => { | |
| if (HashSet_.isHashSet(input)) { | |
| return Effect.mapBothEager( | |
| SchemaParser.decodeUnknownEffect(values)(Arr.fromIterable(input), options), | |
| { | |
| onSuccess: HashSet_.fromIterable, | |
| onFailure: (issue) => | |
| new SchemaIssue.Composite(ast, Option_.some(input), [new SchemaIssue.Pointer(["values"], issue)]) | |
| } | |
| ) | |
| } | |
| return Effect.fail(new SchemaIssue.InvalidType(ast, Option_.some(input))) | |
| } | |
| }, | |
| { | |
| typeConstructor: { | |
| _tag: "effect/HashSet" | |
| }, | |
| generation: { | |
| runtime: `Schema.HashSet(?)`, | |
| Type: `HashSet.HashSet<?>` | |
| }, | |
| expected: "HashSet", | |
| toCodec: ([value]) => | |
| link<HashSet_.HashSet<Value["Encoded"]>>()( | |
| ArraySchema(value), | |
| SchemaTransformation.transform({ | |
| decode: HashSet_.fromIterable, | |
| encode: Arr.fromIterable | |
| }) | |
| ), | |
| toArbitrary: ([value]) => (fc, ctx) => | |
| collectionArbitrary(fc, ctx, value.arbitrary, value.terminal, HashSet_.fromIterable, Equal.equals), | |
| toEquivalence: ([value]) => Equal.makeCompareSet(value), | |
| toFormatter: ([value]) => (t) => { | |
| const size = HashSet_.size(t) | |
| if (size === 0) { | |
| return "HashSet(0) {}" | |
| } | |
| const values = globalThis.Array.from(t).sort().map((v) => `${value(v)}`) | |
| return `HashSet(${size}) { ${values.join(", ")} }` | |
| } | |
| } | |
| ) | |
| return make(schema.ast, { value }) | |
| } | |
| /** | |
| * Type-level representation returned by {@link Chunk}. | |
| * | |
| * @category Chunk | |
| * @since 3.10.0 | |
| */ | |
| export interface Chunk<Value extends Top> extends | |
| declareConstructor< | |
| Chunk_.Chunk<Value["Type"]>, | |
| Chunk_.Chunk<Value["Encoded"]>, | |
| readonly [Value], | |
| ChunkIso<Value> | |
| > | |
| { | |
| readonly "Rebuild": Chunk<Value> | |
| readonly value: Value | |
| } | |
| /** | |
| * Iso representation used for `Chunk` schemas: an array of element values using | |
| * the element schema's `Iso` type. | |
| * | |
| * **When to use** | |
| * | |
| * Use when annotating type-level helpers that work with the readonly-array ISO | |
| * shape of a `Chunk` schema. | |
| * | |
| * @see {@link Chunk} for the schema interface and constructor that use this ISO representation | |
| * | |
| * @category Chunk | |
| * @since 4.0.0 | |
| */ | |
| export type ChunkIso<Value extends Top> = ReadonlyArray<Value["Iso"]> | |
| /** | |
| * Schema for chunks whose values conform to the provided element schema. | |
| * | |
| * @category Chunk | |
| * @since 3.10.0 | |
| */ | |
| export function Chunk<Value extends Top>(value: Value): Chunk<Value> { | |
| const schema = declareConstructor< | |
| Chunk_.Chunk<Value["Type"]>, | |
| Chunk_.Chunk<Value["Encoded"]>, | |
| ChunkIso<Value> | |
| >()( | |
| [value], | |
| ([value]) => { | |
| const values = ArraySchema(value) | |
| return (input, ast, options) => { | |
| if (Chunk_.isChunk(input)) { | |
| return Effect.mapBothEager( | |
| SchemaParser.decodeUnknownEffect(values)(Arr.fromIterable(input), options), | |
| { | |
| onSuccess: Chunk_.fromIterable, | |
| onFailure: (issue) => | |
| new SchemaIssue.Composite(ast, Option_.some(input), [new SchemaIssue.Pointer(["values"], issue)]) | |
| } | |
| ) | |
| } | |
| return Effect.fail(new SchemaIssue.InvalidType(ast, Option_.some(input))) | |
| } | |
| }, | |
| { | |
| typeConstructor: { | |
| _tag: "effect/Chunk" | |
| }, | |
| generation: { | |
| runtime: `Schema.Chunk(?)`, | |
| Type: `Chunk.Chunk<?>` | |
| }, | |
| expected: "Chunk", | |
| toCodec: ([value]) => | |
| link<Chunk_.Chunk<Value["Encoded"]>>()( | |
| ArraySchema(value), | |
| SchemaTransformation.transform({ | |
| decode: Chunk_.fromIterable, | |
| encode: Arr.fromIterable | |
| }) | |
| ), | |
| toArbitrary: ([value]) => (fc, ctx) => | |
| collectionArbitrary(fc, ctx, value.arbitrary, value.terminal, Chunk_.fromIterable), | |
| toEquivalence: ([value]) => Chunk_.makeEquivalence(value), | |
| toFormatter: ([value]) => (t) => { | |
| const size = Chunk_.size(t) | |
| if (size === 0) { | |
| return "Chunk(0) {}" | |
| } | |
| const values = globalThis.Array.from(t).sort().map((v) => `${value(v)}`) | |
| return `Chunk(${size}) { ${values.join(", ")} }` | |
| } | |
| } | |
| ) | |
| return make(schema.ast, { value }) | |
| } | |
| /** | |
| * Type-level representation of {@link RegExp}. | |
| * | |
| * @category RegExp | |
| * @since 4.0.0 | |
| */ | |
| export interface RegExp extends instanceOf<globalThis.RegExp> { | |
| readonly "Rebuild": RegExp | |
| } | |
| /** | |
| * Schema for JavaScript `RegExp` objects. | |
| * | |
| * **Details** | |
| * | |
| * The default JSON serializer encodes a `RegExp` as `{ source, flags }`. | |
| * | |
| * @category RegExp | |
| * @since 4.0.0 | |
| */ | |
| export const RegExp: RegExp = instanceOf( | |
| globalThis.RegExp, | |
| { | |
| typeConstructor: { | |
| _tag: "RegExp" | |
| }, | |
| generation: { | |
| runtime: `Schema.RegExp`, | |
| Type: `globalThis.RegExp` | |
| }, | |
| expected: "RegExp", | |
| toCodecJson: () => | |
| link<globalThis.RegExp>()( | |
| Struct({ | |
| source: String, | |
| flags: String | |
| }), | |
| SchemaTransformation.transformOrFail({ | |
| decode: (e) => | |
| Effect.try({ | |
| try: () => new globalThis.RegExp(e.source, e.flags), | |
| catch: (e) => new SchemaIssue.InvalidValue(Option_.some(e), { message: globalThis.String(e) }) | |
| }), | |
| encode: (regExp) => | |
| Effect.succeed({ | |
| source: regExp.source, | |
| flags: regExp.flags | |
| }) | |
| }) | |
| ), | |
| toArbitrary: () => (fc) => | |
| fc | |
| .tuple( | |
| fc.constantFrom( | |
| ".", | |
| ".*", | |
| "\\d+", | |
| "\\w+", | |
| "[a-z]+", | |
| "[A-Z]+", | |
| "[0-9]+", | |
| "^[a-zA-Z0-9]+$", | |
| "^\\d{4}-\\d{2}-\\d{2}$" // date pattern | |
| ), | |
| fc | |
| .uniqueArray(fc.constantFrom("g", "i", "m", "s", "u", "y"), { | |
| minLength: 0, | |
| maxLength: 6 | |
| }) | |
| .map((flags) => flags.join("")) | |
| ) | |
| .map(([source, flags]) => new globalThis.RegExp(source, flags)), | |
| toEquivalence: () => (a, b) => a.source === b.source && a.flags === b.flags | |
| } | |
| ) | |
| /** | |
| * Type-level representation of {@link URL}. | |
| * | |
| * @category URL | |
| * @since 4.0.0 | |
| */ | |
| export interface URL extends instanceOf<globalThis.URL> { | |
| readonly "Rebuild": URL | |
| } | |
| const URLString = String.annotate({ expected: "a string that will be decoded as a URL" }) | |
| /** | |
| * Schema for JavaScript `URL` objects. | |
| * | |
| * **Details** | |
| * | |
| * Default JSON serializer: | |
| * | |
| * - encodes `URL` as a `string` | |
| * | |
| * @category URL | |
| * @since 4.0.0 | |
| */ | |
| export const URL: URL = instanceOf( | |
| globalThis.URL, | |
| { | |
| typeConstructor: { | |
| _tag: "URL" | |
| }, | |
| generation: { | |
| runtime: `Schema.URL`, | |
| Type: `globalThis.URL` | |
| }, | |
| expected: "URL", | |
| toCodecJson: () => | |
| link<globalThis.URL>()( | |
| URLString, | |
| SchemaTransformation.urlFromString | |
| ), | |
| toArbitrary: () => (fc) => fc.webUrl().map((s) => new globalThis.URL(s)), | |
| toEquivalence: () => (a, b) => a.toString() === b.toString() | |
| } | |
| ) | |
| /** | |
| * Type-level representation of {@link URLFromString}. | |
| * | |
| * @category URL | |
| * @since 4.0.0 | |
| */ | |
| export interface URLFromString extends decodeTo<URL, String> { | |
| readonly "Rebuild": URLFromString | |
| } | |
| /** | |
| * Schema that decodes a `string` into a `URL`. | |
| * | |
| * **Details** | |
| * | |
| * Decoding: | |
| * - A **valid** URL `string` is decoded as a `URL` | |
| * | |
| * Encoding: | |
| * - A `URL` is encoded as a `string` | |
| * | |
| * @category URL | |
| * @since 4.0.0 | |
| */ | |
| export const URLFromString: URLFromString = URLString.pipe(decodeTo(URL, SchemaTransformation.urlFromString)) | |
| /** | |
| * Type-level representation of {@link Date}. | |
| * | |
| * @category Date | |
| * @since 4.0.0 | |
| */ | |
| export interface Date extends instanceOf<globalThis.Date> { | |
| readonly "Rebuild": Date | |
| } | |
| type DateArbitraryConstraints = FastCheck.DateConstraints & { | |
| readonly valid?: boolean | undefined | |
| } | |
| function dateArbitraryConstraints<T = globalThis.Date>( | |
| constraint: Annotations.ToArbitrary.Constraint | undefined, | |
| ordered: Annotations.ToArbitrary.OrderedConstraint<T> | undefined, | |
| base?: DateArbitraryConstraints | undefined, | |
| toDate?: (value: T) => globalThis.Date | |
| ): FastCheck.DateConstraints { | |
| const out: FastCheck.DateConstraints = { ...base } | |
| delete (out as any).valid | |
| if (base?.valid || constraint?.valid) { | |
| out.noInvalidDate = true | |
| } | |
| if (ordered?.minimum !== undefined) { | |
| const minimum = toDate === undefined ? ordered.minimum as globalThis.Date : toDate(ordered.minimum) | |
| const nextMin = ordered.exclusiveMinimum ? new globalThis.Date(minimum.getTime() + 1) : minimum | |
| if (out.min === undefined || nextMin.getTime() > out.min.getTime()) { | |
| out.min = nextMin | |
| } | |
| } | |
| if (ordered?.maximum !== undefined) { | |
| const maximum = toDate === undefined ? ordered.maximum as globalThis.Date : toDate(ordered.maximum) | |
| const nextMax = ordered.exclusiveMaximum ? new globalThis.Date(maximum.getTime() - 1) : maximum | |
| if (out.max === undefined || nextMax.getTime() < out.max.getTime()) { | |
| out.max = nextMax | |
| } | |
| } | |
| return out | |
| } | |
| const DateString = String.annotate({ expected: "a string in ISO 8601 format that will be decoded as a Date" }) | |
| /** | |
| * Schema for JavaScript `Date` objects. | |
| * | |
| * **When to use** | |
| * | |
| * Use to validate in-memory values that must already be JavaScript date | |
| * objects. | |
| * | |
| * **Details** | |
| * | |
| * This schema accepts any `Date` instance, including invalid dates. The default | |
| * JSON serializer encodes valid dates as ISO 8601 strings; invalid dates encode | |
| * as `"Invalid Date"`. | |
| * | |
| * **Example** (Date schema) | |
| * | |
| * ```ts | |
| * import { Schema } from "effect" | |
| * | |
| * Schema.decodeUnknownSync(Schema.Date)(new Date("2024-01-01")) | |
| * // => Date { 2024-01-01T00:00:00.000Z } | |
| * ``` | |
| * | |
| * @see {@link DateValid} for accepting only valid Date instances | |
| * | |
| * @category Date | |
| * @since 4.0.0 | |
| */ | |
| export const Date: Date = instanceOf( | |
| globalThis.Date, | |
| { | |
| typeConstructor: { | |
| _tag: "Date" | |
| }, | |
| generation: { | |
| runtime: `Schema.Date`, | |
| Type: `globalThis.Date` | |
| }, | |
| expected: "Date", | |
| toCodecJson: () => | |
| link<globalThis.Date>()( | |
| DateString, | |
| SchemaTransformation.dateFromString | |
| ), | |
| toArbitrary: () => (fc, ctx) => | |
| fc.date(dateArbitraryConstraints( | |
| ctx?.constraint, | |
| ctx?.constraint?.ordered?.order === Order.Date ? ctx.constraint.ordered : undefined | |
| )) | |
| } | |
| ) | |
| /** | |
| * Type-level representation of {@link DateFromString}. | |
| * | |
| * @category Date | |
| * @since 3.10.0 | |
| */ | |
| export interface DateFromString extends decodeTo<Date, String> { | |
| readonly "Rebuild": DateFromString | |
| } | |
| /** | |
| * Schema that decodes a string into a JavaScript `Date`. | |
| * | |
| * **When to use** | |
| * | |
| * Use to model string-encoded dates that decode to JavaScript `Date` objects | |
| * and encode back to strings. | |
| * | |
| * **Details** | |
| * | |
| * Decoding: | |
| * The string is passed to JavaScript `Date` construction. | |
| * | |
| * Encoding: | |
| * A valid `Date` is encoded as an ISO string; an invalid `Date` is encoded as | |
| * `"Invalid Date"`. | |
| * | |
| * **Gotchas** | |
| * | |
| * Invalid date strings can decode to invalid `Date` instances. | |
| * | |
| * @see {@link Date} for accepting Date instances directly | |
| * @see {@link DateValid} for rejecting invalid Date instances | |
| * | |
| * @category Date | |
| * @since 3.10.0 | |
| */ | |
| export const DateFromString: DateFromString = DateString.pipe(decodeTo(Date, SchemaTransformation.dateFromString)) | |
| /** | |
| * Type-level representation of {@link DateValid}. | |
| * | |
| * @category Date | |
| * @since 4.0.0 | |
| */ | |
| export interface DateValid extends Date { | |
| readonly "Rebuild": DateValid | |
| } | |
| /** | |
| * Schema for **valid** JavaScript `Date` objects. | |
| * | |
| * **Details** | |
| * | |
| * This schema accepts `Date` instances but rejects invalid dates (such as `new | |
| * Date("invalid")`). | |
| * | |
| * @category Date | |
| * @since 4.0.0 | |
| */ | |
| export const DateValid: DateValid = Date.check(isDateValid()) | |
| /** | |
| * Type-level representation of {@link Duration}. | |
| * | |
| * @category Duration | |
| * @since 3.10.0 | |
| */ | |
| export interface Duration extends declare<Duration_.Duration> { | |
| readonly "Rebuild": Duration | |
| } | |
| /** | |
| * Schema for `Duration` values. | |
| * | |
| * **Details** | |
| * | |
| * The default JSON serializer encodes `Duration` as a tagged object with the | |
| * duration type and value. | |
| * | |
| * **Example** (Duration schema) | |
| * | |
| * ```ts | |
| * import { Duration, Schema } from "effect" | |
| * | |
| * Schema.decodeUnknownSync(Schema.Duration)(Duration.seconds(5)) | |
| * // => Duration(5s) | |
| * ``` | |
| * | |
| * @category Duration | |
| * | |
| * @since 3.10.0 | |
| */ | |
| export const Duration: Duration = declare( | |
| Duration_.isDuration, | |
| { | |
| typeConstructor: { | |
| _tag: "effect/Duration" | |
| }, | |
| generation: { | |
| runtime: `Schema.Duration`, | |
| Type: `Duration.Duration`, | |
| importDeclaration: `import * as Duration from "effect/Duration"` | |
| }, | |
| expected: "Duration", | |
| toCodecJson: () => | |
| link<Duration_.Duration>()( | |
| Union([ | |
| Struct({ _tag: Literal("Infinity") }), | |
| Struct({ _tag: Literal("NegativeInfinity") }), | |
| Struct({ _tag: Literal("Nanos"), value: BigInt }), | |
| Struct({ _tag: Literal("Millis"), value: Int }) | |
| ]), | |
| SchemaTransformation.transform({ | |
| decode: (e) => { | |
| switch (e._tag) { | |
| case "Infinity": | |
| return Duration_.infinity | |
| case "NegativeInfinity": | |
| return Duration_.negativeInfinity | |
| case "Nanos": | |
| return Duration_.nanos(e.value) | |
| case "Millis": | |
| return Duration_.millis(e.value) | |
| } | |
| }, | |
| encode: (duration) => { | |
| switch (duration.value._tag) { | |
| case "Infinity": | |
| return { _tag: "Infinity" } as const | |
| case "NegativeInfinity": | |
| return { _tag: "NegativeInfinity" } as const | |
| case "Nanos": | |
| return { _tag: "Nanos", value: duration.value.nanos } as const | |
| case "Millis": | |
| return { _tag: "Millis", value: duration.value.millis } as const | |
| } | |
| } | |
| }) | |
| ), | |
| toArbitrary: () => (fc) => | |
| fc.oneof( | |
| fc.constant(Duration_.infinity), | |
| fc.constant(Duration_.negativeInfinity), | |
| fc.bigInt().map(Duration_.nanos), | |
| fc.maxSafeInteger().map(Duration_.millis) | |
| ), | |
| toFormatter: () => globalThis.String, | |
| toEquivalence: () => Duration_.Equivalence | |
| } | |
| ) | |
| const DurationString = String.annotate({ expected: "a string that will be decoded as a Duration" }) | |
| /** | |
| * Type-level representation of {@link DurationFromString}. | |
| * | |
| * @category Duration | |
| * @since 4.0.0 | |
| */ | |
| export interface DurationFromString extends decodeTo<Duration, String> { | |
| readonly "Rebuild": DurationFromString | |
| } | |
| /** | |
| * Schema that parses a string into a `Duration`. | |
| * | |
| * **Details** | |
| * | |
| * Decoding: | |
| * - A `string` is decoded as a `Duration`, accepting any format that | |
| * `Duration.fromInput` can parse. | |
| * | |
| * Encoding: | |
| * - A `Duration` is encoded as a parseable `string`. | |
| * | |
| * @category Duration | |
| * @since 4.0.0 | |
| */ | |
| export const DurationFromString: DurationFromString = DurationString.pipe( | |
| decodeTo(Duration, SchemaTransformation.durationFromString) | |
| ) | |
| /** | |
| * Type-level representation of {@link DurationFromNanos}. | |
| * | |
| * @category Duration | |
| * @since 3.10.0 | |
| */ | |
| export interface DurationFromNanos extends decodeTo<Duration, BigInt> { | |
| readonly "Rebuild": DurationFromNanos | |
| } | |
| const bigint0 = globalThis.BigInt(0) | |
| /** | |
| * Schema that decodes a non-negative `bigint` into a | |
| * `Duration`, treating the bigint as nanoseconds. | |
| * | |
| * **Details** | |
| * | |
| * Decoding: | |
| * A non-negative `bigint` representing nanoseconds is decoded as a `Duration`. | |
| * | |
| * Encoding: | |
| * Finite durations are encoded as a non-negative `bigint` number of nanoseconds. | |
| * Encoding fails when the duration cannot be represented as nanoseconds, such as | |
| * `Duration.infinity`. | |
| * | |
| * @category Duration | |
| * @since 3.10.0 | |
| */ | |
| export const DurationFromNanos: DurationFromNanos = BigInt.check(isGreaterThanOrEqualToBigInt(bigint0)).pipe( | |
| decodeTo(Duration, SchemaTransformation.durationFromNanos) | |
| ) | |
| /** | |
| * Type-level representation of {@link DurationFromMillis}. | |
| * | |
| * @category Duration | |
| * @since 3.10.0 | |
| */ | |
| export interface DurationFromMillis extends decodeTo<Duration, Number> { | |
| readonly "Rebuild": DurationFromMillis | |
| } | |
| /** | |
| * Schema that decodes a non-negative (possibly infinite) | |
| * integer into a `Duration`, treating the integer value as the duration in | |
| * milliseconds. | |
| * | |
| * **Details** | |
| * | |
| * Decoding: | |
| * - A non-negative (possibly infinite) integer representing milliseconds is | |
| * decoded as a `Duration` | |
| * | |
| * Encoding: | |
| * - A `Duration` is encoded to a non-negative (possibly infinite) integer | |
| * representing milliseconds | |
| * | |
| * @category Duration | |
| * @since 3.10.0 | |
| */ | |
| export const DurationFromMillis: DurationFromMillis = Number.check(isGreaterThanOrEqualTo(0)).pipe( | |
| decodeTo(Duration, SchemaTransformation.durationFromMillis) | |
| ) | |
| /** | |
| * Type-level representation of {@link BigDecimal}. | |
| * | |
| * @category BigDecimal | |
| * @since 3.10.0 | |
| */ | |
| export interface BigDecimal extends declare<BigDecimal_.BigDecimal> { | |
| readonly "Rebuild": BigDecimal | |
| } | |
| const BigDecimalString = String.annotate({ expected: "a string that will be decoded as a BigDecimal" }) | |
| const bigDecimalDefaultMaxScale = 20 | |
| const bigDecimalInvalidOrderedConstraintsError = "Unable to derive an arbitrary for the ordered BigDecimal constraints" | |
| function bigDecimalScaleValueAtScale(bd: BigDecimal_.BigDecimal, scale: number): bigint { | |
| return BigDecimal_.scale(bd, scale).value | |
| } | |
| function bigDecimalMinValueAtScale( | |
| minimum: BigDecimal_.BigDecimal, | |
| scale: number, | |
| excluded: boolean | |
| ): bigint { | |
| return excluded | |
| ? bigDecimalScaleValueAtScale(BigDecimal_.floor(minimum, scale), scale) + globalThis.BigInt(1) | |
| : bigDecimalScaleValueAtScale(BigDecimal_.ceil(minimum, scale), scale) | |
| } | |
| function bigDecimalMaxValueAtScale( | |
| maximum: BigDecimal_.BigDecimal, | |
| scale: number, | |
| excluded: boolean | |
| ): bigint { | |
| return excluded | |
| ? bigDecimalScaleValueAtScale(BigDecimal_.ceil(maximum, scale), scale) - globalThis.BigInt(1) | |
| : bigDecimalScaleValueAtScale(BigDecimal_.floor(maximum, scale), scale) | |
| } | |
| function bigDecimalMaxScale(ordered: Annotations.ToArbitrary.OrderedConstraint<BigDecimal_.BigDecimal>): number { | |
| return Math.max( | |
| bigDecimalDefaultMaxScale, | |
| ordered.minimum?.scale ?? 0, | |
| ordered.maximum?.scale ?? 0, | |
| ordered.exclusiveMinimum && ordered.minimum !== undefined ? ordered.minimum.scale + 1 : 0, | |
| ordered.exclusiveMaximum && ordered.maximum !== undefined ? ordered.maximum.scale + 1 : 0 | |
| ) | |
| } | |
| function bigDecimalValueConstraintsAtScale( | |
| ordered: Annotations.ToArbitrary.OrderedConstraint<BigDecimal_.BigDecimal>, | |
| scale: number | |
| ): FastCheck.BigIntConstraints | undefined { | |
| const constraints: FastCheck.BigIntConstraints = {} | |
| if (ordered.minimum !== undefined) { | |
| constraints.min = bigDecimalMinValueAtScale(ordered.minimum, scale, ordered.exclusiveMinimum === true) | |
| } | |
| if (ordered.maximum !== undefined) { | |
| constraints.max = bigDecimalMaxValueAtScale(ordered.maximum, scale, ordered.exclusiveMaximum === true) | |
| } | |
| if (constraints.min !== undefined && constraints.max !== undefined && constraints.min > constraints.max) { | |
| return undefined | |
| } | |
| return constraints | |
| } | |
| function bigDecimalScaleConstraints( | |
| ordered: Annotations.ToArbitrary.OrderedConstraint<BigDecimal_.BigDecimal> | |
| ): FastCheck.IntegerConstraints { | |
| const max = bigDecimalMaxScale(ordered) | |
| if (bigDecimalValueConstraintsAtScale(ordered, max) === undefined) { | |
| throw new globalThis.Error(bigDecimalInvalidOrderedConstraintsError) | |
| } | |
| let min = 0 | |
| let high = max | |
| while (min < high) { | |
| const scale = min + Math.floor((high - min) / 2) | |
| if (bigDecimalValueConstraintsAtScale(ordered, scale) === undefined) { | |
| min = scale + 1 | |
| } else { | |
| high = scale | |
| } | |
| } | |
| return { min, max } | |
| } | |
| /** | |
| * Schema for `BigDecimal` values. | |
| * | |
| * **When to use** | |
| * | |
| * Use when you already have Effect decimal instances and need schema | |
| * validation, formatting, equivalence, and JSON string serialization. | |
| * | |
| * **Details** | |
| * | |
| * Default JSON serializer: | |
| * | |
| * - encodes `BigDecimal` as a `string` | |
| * | |
| * @see {@link BigDecimalFromString} for parsing string input into a BigDecimal | |
| * | |
| * @category BigDecimal | |
| * @since 3.10.0 | |
| */ | |
| export const BigDecimal: BigDecimal = declare( | |
| BigDecimal_.isBigDecimal, | |
| { | |
| typeConstructor: { | |
| _tag: "effect/BigDecimal" | |
| }, | |
| generation: { | |
| runtime: `Schema.BigDecimal`, | |
| Type: `BigDecimal.BigDecimal`, | |
| importDeclaration: `import * as BigDecimal from "effect/BigDecimal"` | |
| }, | |
| expected: "BigDecimal", | |
| toCodecJson: () => | |
| link<BigDecimal_.BigDecimal>()( | |
| BigDecimalString, | |
| SchemaTransformation.bigDecimalFromString | |
| ), | |
| toArbitrary: () => (fc, ctx) => { | |
| const ordered = ctx.constraint?.ordered?.order === BigDecimal_.Order | |
| ? ctx.constraint.ordered as Annotations.ToArbitrary.OrderedConstraint<BigDecimal_.BigDecimal> | |
| : undefined | |
| if (ordered === undefined) { | |
| return fc.tuple(fc.bigInt(), fc.integer({ min: 0, max: bigDecimalDefaultMaxScale })) | |
| .map(([value, scale]) => BigDecimal_.make(value, scale)) | |
| } | |
| return fc.integer(bigDecimalScaleConstraints(ordered)).chain((scale) => { | |
| const constraints = bigDecimalValueConstraintsAtScale(ordered, scale) | |
| if (constraints === undefined) { | |
| throw new globalThis.Error(bigDecimalInvalidOrderedConstraintsError) | |
| } | |
| return fc.bigInt(constraints).map((value) => BigDecimal_.make(value, scale)) | |
| }) | |
| }, | |
| toFormatter: () => (bd) => BigDecimal_.format(bd), | |
| toEquivalence: () => BigDecimal_.Equivalence | |
| } | |
| ) | |
| /** | |
| * Type-level representation of {@link BigDecimalFromString}. | |
| * | |
| * @category BigDecimal | |
| * @since 4.0.0 | |
| */ | |
| export interface BigDecimalFromString extends decodeTo<BigDecimal, String> { | |
| readonly "Rebuild": BigDecimalFromString | |
| } | |
| /** | |
| * Schema that parses a string into a `BigDecimal`. | |
| * | |
| * **When to use** | |
| * | |
| * Use to parse decimal or exponent-notation strings into arbitrary-precision | |
| * BigDecimal values while encoding them back to strings. | |
| * | |
| * **Details** | |
| * | |
| * Decoding: | |
| * - A `string` is decoded with `BigDecimal.fromString`. | |
| * | |
| * Encoding: | |
| * - A `BigDecimal` is encoded with `BigDecimal.format`. | |
| * | |
| * **Gotchas** | |
| * | |
| * An empty string decodes as zero. | |
| * | |
| * @see {@link BigDecimal} for validating values that are already BigDecimal values | |
| * @see {@link BigIntFromString} for parsing base-10 integer strings into bigint values | |
| * @see {@link NumberFromString} for parsing JavaScript number strings | |
| * | |
| * @category BigDecimal | |
| * @since 4.0.0 | |
| */ | |
| export const BigDecimalFromString: BigDecimalFromString = BigDecimalString.pipe( | |
| decodeTo(BigDecimal, SchemaTransformation.bigDecimalFromString) | |
| ) | |
| /** | |
| * Type-level representation of {@link UnknownFromJsonString}. | |
| * | |
| * @category models | |
| * @since 4.0.0 | |
| */ | |
| export interface UnknownFromJsonString extends fromJsonString<Unknown> { | |
| readonly "Rebuild": UnknownFromJsonString | |
| } | |
| /** | |
| * Schema that decodes a JSON-encoded string into an `unknown` value. | |
| * | |
| * **Details** | |
| * | |
| * Decoding: | |
| * - A `string` is decoded as an `unknown` value. | |
| * - If the string is not valid JSON, decoding fails. | |
| * | |
| * Encoding: | |
| * - Any value is encoded as a JSON string using `JSON.stringify`. | |
| * - If the value is not a valid JSON value, encoding fails. | |
| * | |
| * **Example** (Decoding unknown JSON strings) | |
| * | |
| * ```ts | |
| * import { Schema } from "effect" | |
| * | |
| * Schema.decodeUnknownSync(Schema.UnknownFromJsonString)(`{"a":1,"b":2}`) | |
| * // => { a: 1, b: 2 } | |
| * ``` | |
| * | |
| * @category schemas | |
| * @since 4.0.0 | |
| */ | |
| export const UnknownFromJsonString: UnknownFromJsonString = fromJsonString(Unknown) | |
| /** | |
| * Type-level representation returned by {@link fromJsonString}. | |
| * | |
| * @category models | |
| * @since 4.0.0 | |
| */ | |
| export interface fromJsonString<S extends Top> extends decodeTo<S, String> { | |
| readonly "Rebuild": fromJsonString<S> | |
| } | |
| /** | |
| * Returns a schema that decodes a JSON string and then decodes the parsed value | |
| * using the given schema. | |
| * | |
| * **Details** | |
| * | |
| * This is useful when working with JSON-encoded strings where the actual | |
| * structure of the value is known and described by an existing schema. | |
| * | |
| * The resulting schema first parses the input string as JSON, and then runs the | |
| * provided schema on the parsed result. | |
| * | |
| * JSON Schema generation: | |
| * | |
| * When using `fromJsonString` with `draft-2020-12` or `openApi3.1`, the | |
| * resulting schema will be a JSON Schema with a `contentSchema` property that | |
| * contains the JSON Schema for the given schema. | |
| * | |
| * **Example** (Decoding JSON strings with a schema) | |
| * | |
| * ```ts | |
| * import { Schema } from "effect" | |
| * | |
| * const schema = Schema.Struct({ a: Schema.Number }) | |
| * const schemaFromJsonString = Schema.fromJsonString(schema) | |
| * | |
| * Schema.decodeUnknownSync(schemaFromJsonString)(`{"a":1,"b":2}`) | |
| * // => { a: 1 } | |
| * ``` | |
| * | |
| * **Example** (Emitting JSON Schema for a JSON string decoder) | |
| * | |
| * ```ts | |
| * import { Schema } from "effect" | |
| * | |
| * const original = Schema.Struct({ a: Schema.String }) | |
| * const schema = Schema.fromJsonString(original) | |
| * | |
| * const document = Schema.toJsonSchemaDocument(schema) | |
| * | |
| * console.log(JSON.stringify(document, null, 2)) | |
| * // { | |
| * // "source": "draft-2020-12", | |
| * // "schema": { | |
| * // "type": "string", | |
| * // "contentMediaType": "application/json", | |
| * // "contentSchema": { | |
| * // "type": "object", | |
| * // "properties": { | |
| * // "a": { | |
| * // "type": "string" | |
| * // } | |
| * // }, | |
| * // "required": [ | |
| * // "a" | |
| * // ], | |
| * // "additionalProperties": false | |
| * // } | |
| * // }, | |
| * // "definitions": {} | |
| * // } | |
| * ``` | |
| * | |
| * @category constructors | |
| * @since 4.0.0 | |
| */ | |
| export function fromJsonString<S extends Top>(schema: S): fromJsonString<S> { | |
| return String.annotate({ | |
| expected: "a string that will be decoded as JSON", | |
| contentMediaType: "application/json", | |
| contentSchema: SchemaAST.toEncoded(schema.ast) | |
| }).pipe(decodeTo(schema, SchemaTransformation.fromJsonString)) | |
| } | |
| /** | |
| * Type-level representation of {@link File}. | |
| * | |
| * @category file | |
| * @since 4.0.0 | |
| */ | |
| export interface File extends instanceOf<globalThis.File> { | |
| readonly "Rebuild": File | |
| } | |
| /** | |
| * Schema for JavaScript `File` objects. | |
| * | |
| * **Details** | |
| * | |
| * The default JSON serializer encodes a `File` as `{ data, type, name, lastModified }` | |
| * where `data` is base64-encoded. | |
| * | |
| * @category file | |
| * @since 4.0.0 | |
| */ | |
| export const File: File = instanceOf(globalThis.File, { | |
| typeConstructor: { | |
| _tag: "File" | |
| }, | |
| generation: { | |
| runtime: `Schema.File`, | |
| Type: `globalThis.File` | |
| }, | |
| expected: "File", | |
| toCodecJson: () => | |
| link<globalThis.File>()( | |
| Struct({ | |
| data: String.check(isBase64()), | |
| type: String, | |
| name: String, | |
| lastModified: Number | |
| }), | |
| SchemaTransformation.transformOrFail({ | |
| decode: (e) => | |
| Result_.match(Encoding.decodeBase64(e.data), { | |
| onFailure: (error) => | |
| Effect.fail( | |
| new SchemaIssue.InvalidValue(Option_.some(e.data), { | |
| message: error.message | |
| }) | |
| ), | |
| onSuccess: (bytes) => { | |
| const buffer = new globalThis.Uint8Array(bytes) | |
| return Effect.succeed( | |
| new globalThis.File([buffer], e.name, { type: e.type, lastModified: e.lastModified }) | |
| ) | |
| } | |
| }), | |
| encode: (file) => | |
| Effect.tryPromise({ | |
| try: async () => { | |
| const bytes = new globalThis.Uint8Array(await file.arrayBuffer()) | |
| return { | |
| data: Encoding.encodeBase64(bytes), | |
| type: file.type, | |
| name: file.name, | |
| lastModified: file.lastModified | |
| } | |
| }, | |
| catch: (e) => | |
| new SchemaIssue.InvalidValue(Option_.some(file), { | |
| message: globalThis.String(e) | |
| }) | |
| }) | |
| }) | |
| ) | |
| }) | |
| /** | |
| * Type-level representation of {@link FormData}. | |
| * | |
| * @category FormData | |
| * @since 4.0.0 | |
| */ | |
| export interface FormData extends instanceOf<globalThis.FormData> { | |
| readonly "Rebuild": FormData | |
| } | |
| /** | |
| * Schema for JavaScript `FormData` objects. | |
| * | |
| * **Details** | |
| * | |
| * The default JSON serializer encodes a `FormData` as an array of `[key, entry]` | |
| * pairs where each entry is tagged as `"String"` or `"File"`. | |
| * | |
| * @category FormData | |
| * @since 4.0.0 | |
| */ | |
| export const FormData: FormData = instanceOf(globalThis.FormData, { | |
| typeConstructor: { | |
| _tag: "FormData" | |
| }, | |
| generation: { | |
| runtime: `Schema.FormData`, | |
| Type: `globalThis.FormData` | |
| }, | |
| expected: "FormData", | |
| toCodecJson: () => | |
| link<globalThis.FormData>()( | |
| ArraySchema( | |
| Tuple([ | |
| String, | |
| Union([ | |
| Struct({ _tag: tag("String"), value: String }), | |
| Struct({ _tag: tag("File"), value: File }) | |
| ]) | |
| ]) | |
| ), | |
| SchemaTransformation.transformOrFail({ | |
| decode: (e) => { | |
| const out = new globalThis.FormData() | |
| for (const [key, entry] of e) { | |
| out.append(key, entry.value) | |
| } | |
| return Effect.succeed(out) | |
| }, | |
| encode: (formData) => { | |
| return Effect.succeed( | |
| globalThis.Array.from(formData.entries()).map(([key, value]) => { | |
| if (typeof value === "string") { | |
| return [key, { _tag: "String", value }] as const | |
| } else { | |
| return [key, { _tag: "File", value }] as const | |
| } | |
| }) | |
| ) | |
| } | |
| }) | |
| ) | |
| }) | |
| /** | |
| * Type-level representation returned by {@link fromFormData}. | |
| * | |
| * @category FormData | |
| * @since 4.0.0 | |
| */ | |
| export interface fromFormData<S extends Top> extends decodeTo<S, FormData> { | |
| readonly "Rebuild": fromFormData<S> | |
| } | |
| /** | |
| * Schema for decoding `FormData` through a bracket-notation tree. | |
| * | |
| * **When to use** | |
| * | |
| * Use to decode browser or multipart form data into a structured schema value. | |
| * | |
| * **Details** | |
| * | |
| * The decoding process has two steps: | |
| * | |
| * 1. Parse `FormData` into a nested tree record. | |
| * 2. Decode the parsed value with the given schema. | |
| * | |
| * You can express nested values using bracket notation. | |
| * | |
| * If you want to decode values that are not strings, use | |
| * `Schema.toCodecStringTree` with the `keepDeclarations: true` option. | |
| * This serializer preserves values such as numbers and `Blob` objects when | |
| * compatible with the schema. | |
| * | |
| * **Example** (Decoding a flat structure) | |
| * | |
| * ```ts | |
| * import { Schema } from "effect" | |
| * | |
| * const schema = Schema.fromFormData( | |
| * Schema.Struct({ | |
| * a: Schema.String | |
| * }) | |
| * ) | |
| * | |
| * const formData = new FormData() | |
| * formData.append("a", "1") | |
| * formData.append("b", "2") | |
| * | |
| * console.log(String(Schema.decodeUnknownExit(schema)(formData))) | |
| * // Success({"a":"1"}) | |
| * ``` | |
| * | |
| * **Example** (Nested fields) | |
| * | |
| * ```ts | |
| * import { Schema } from "effect" | |
| * | |
| * const schema = Schema.fromFormData( | |
| * Schema.Struct({ | |
| * a: Schema.String, | |
| * b: Schema.Struct({ | |
| * c: Schema.String, | |
| * d: Schema.String | |
| * }) | |
| * }) | |
| * ) | |
| * | |
| * const formData = new FormData() | |
| * formData.append("a", "1") | |
| * formData.append("b[c]", "2") | |
| * formData.append("b[d]", "3") | |
| * | |
| * console.log(String(Schema.decodeUnknownExit(schema)(formData))) | |
| * // Success({"a":"1","b":{"c":"2","d":"3"}}) | |
| * ``` | |
| * | |
| * **Example** (Parsing non-string values) | |
| * | |
| * ```ts | |
| * import { Schema } from "effect" | |
| * | |
| * const schema = Schema.fromFormData( | |
| * Schema.toCodecStringTree( | |
| * Schema.Struct({ | |
| * a: Schema.Int | |
| * }), | |
| * { keepDeclarations: true } | |
| * ) | |
| * ) | |
| * | |
| * const formData = new FormData() | |
| * formData.append("a", "1") | |
| * | |
| * console.log(String(Schema.decodeUnknownExit(schema)(formData))) | |
| * // Success({"a":1}) // Note: the value is a number | |
| * ``` | |
| * | |
| * @category decoding | |
| * @since 4.0.0 | |
| */ | |
| export function fromFormData<S extends Top>(schema: S): fromFormData<S> { | |
| return FormData.pipe(decodeTo(schema, SchemaTransformation.fromFormData)) | |
| } | |
| /** | |
| * Type-level representation of {@link URLSearchParams}. | |
| * | |
| * @category search params | |
| * @since 4.0.0 | |
| */ | |
| export interface URLSearchParams extends instanceOf<globalThis.URLSearchParams> { | |
| readonly "Rebuild": URLSearchParams | |
| } | |
| /** | |
| * Schema for JavaScript `URLSearchParams` objects. | |
| * | |
| * **Details** | |
| * | |
| * The default JSON serializer encodes a `URLSearchParams` as a query string. | |
| * | |
| * @category search params | |
| * @since 4.0.0 | |
| */ | |
| export const URLSearchParams: URLSearchParams = instanceOf(globalThis.URLSearchParams, { | |
| typeConstructor: { | |
| _tag: "URLSearchParams" | |
| }, | |
| generation: { | |
| runtime: `Schema.URLSearchParams`, | |
| Type: `globalThis.URLSearchParams` | |
| }, | |
| expected: "URLSearchParams", | |
| toCodecJson: () => | |
| link<globalThis.URLSearchParams>()( | |
| String.annotate({ expected: "a query string that will be decoded as URLSearchParams" }), | |
| SchemaTransformation.transform({ | |
| decode: (e) => new globalThis.URLSearchParams(e), | |
| encode: (params) => params.toString() | |
| }) | |
| ) | |
| }) | |
| /** | |
| * Type-level representation returned by {@link fromURLSearchParams}. | |
| * | |
| * @category search params | |
| * @since 4.0.0 | |
| */ | |
| export interface fromURLSearchParams<S extends Top> extends decodeTo<S, URLSearchParams> { | |
| readonly "Rebuild": fromURLSearchParams<S> | |
| } | |
| /** | |
| * Schema for decoding `URLSearchParams` through a bracket-notation tree. | |
| * | |
| * **When to use** | |
| * | |
| * Use to decode query parameters into a structured schema value. | |
| * | |
| * **Details** | |
| * | |
| * The decoding process has two steps: | |
| * | |
| * 1. Parse `URLSearchParams` into a nested tree record. | |
| * 2. Decode the parsed value with the given schema. | |
| * | |
| * You can express nested values using bracket notation. | |
| * | |
| * If you want to decode values that are not strings, use | |
| * `Schema.toCodecStringTree`. This serializer preserves values such as | |
| * numbers when compatible with the schema. | |
| * | |
| * **Example** (Decoding a flat structure) | |
| * | |
| * ```ts | |
| * import { Schema } from "effect" | |
| * | |
| * const schema = Schema.fromURLSearchParams( | |
| * Schema.Struct({ | |
| * a: Schema.String | |
| * }) | |
| * ) | |
| * | |
| * const urlSearchParams = new URLSearchParams("a=1&b=2") | |
| * | |
| * console.log(String(Schema.decodeUnknownExit(schema)(urlSearchParams))) | |
| * // Success({"a":"1"}) | |
| * ``` | |
| * | |
| * **Example** (Nested fields) | |
| * | |
| * ```ts | |
| * import { Schema } from "effect" | |
| * | |
| * const schema = Schema.fromURLSearchParams( | |
| * Schema.Struct({ | |
| * a: Schema.String, | |
| * b: Schema.Struct({ | |
| * c: Schema.String, | |
| * d: Schema.String | |
| * }) | |
| * }) | |
| * ) | |
| * | |
| * const urlSearchParams = new URLSearchParams("a=1&b[c]=2&b[d]=3") | |
| * | |
| * console.log(String(Schema.decodeUnknownExit(schema)(urlSearchParams))) | |
| * // Success({"a":"1","b":{"c":"2","d":"3"}}) | |
| * ``` | |
| * | |
| * **Example** (Parsing non-string values) | |
| * | |
| * ```ts | |
| * import { Schema } from "effect" | |
| * | |
| * const schema = Schema.fromURLSearchParams( | |
| * Schema.toCodecStringTree( | |
| * Schema.Struct({ | |
| * a: Schema.Int | |
| * }) | |
| * ) | |
| * ) | |
| * | |
| * const urlSearchParams = new URLSearchParams("a=1&b=2") | |
| * | |
| * console.log(String(Schema.decodeUnknownExit(schema)(urlSearchParams))) | |
| * // Success({"a":1}) // Note: the value is a number | |
| * ``` | |
| * | |
| * @category decoding | |
| * @since 4.0.0 | |
| */ | |
| export function fromURLSearchParams<S extends Top>(schema: S): fromURLSearchParams<S> { | |
| return URLSearchParams.pipe(decodeTo(schema, SchemaTransformation.fromURLSearchParams)) | |
| } | |
| /** | |
| * Type-level representation of {@link Finite}. | |
| * | |
| * @category Number | |
| * @since 3.10.0 | |
| */ | |
| export interface Finite extends Number { | |
| readonly "Rebuild": Finite | |
| } | |
| /** | |
| * Schema for finite numbers, rejecting `NaN`, `Infinity`, and `-Infinity`. | |
| * | |
| * @category Number | |
| * @since 3.10.0 | |
| */ | |
| export const Finite: Finite = Number.check(isFinite()) | |
| /** | |
| * Type-level representation of {@link Int}. | |
| * | |
| * @category Number | |
| * @since 3.10.0 | |
| */ | |
| export interface Int extends Number { | |
| readonly "Rebuild": Int | |
| } | |
| /** | |
| * Schema for integers, rejecting `NaN`, `Infinity`, and `-Infinity`. | |
| * | |
| * @category Number | |
| * @since 3.10.0 | |
| */ | |
| export const Int: Int = Number.check(isInt()) | |
| /** | |
| * Type-level representation of {@link NumberFromString}. | |
| * | |
| * @category Number | |
| * @since 3.10.0 | |
| */ | |
| export interface NumberFromString extends decodeTo<Finite, String> { | |
| readonly "Rebuild": NumberFromString | |
| } | |
| /** | |
| * Schema that parses a string into a `number` using JavaScript | |
| * number coercion. | |
| * | |
| * **Details** | |
| * | |
| * Decoding: | |
| * A `string` is decoded as a number, including possible non-finite values such as | |
| * `NaN`, `Infinity`, and `-Infinity`. Use `FiniteFromString` to reject non-finite | |
| * numbers. | |
| * | |
| * Encoding: | |
| * A number is encoded as a `string`. | |
| * | |
| * @category Number | |
| * @since 3.10.0 | |
| */ | |
| export const NumberFromString: NumberFromString = String.annotate({ | |
| expected: "a string that will be decoded as a number" | |
| }).pipe(decodeTo(Number, SchemaTransformation.numberFromString)) | |
| /** | |
| * Type-level representation of {@link FiniteFromString}. | |
| * | |
| * @category Number | |
| * @since 4.0.0 | |
| */ | |
| export interface FiniteFromString extends decodeTo<Finite, String> { | |
| readonly "Rebuild": FiniteFromString | |
| } | |
| /** | |
| * Schema that parses a string into a finite number. | |
| * | |
| * **Details** | |
| * | |
| * Decoding: | |
| * - A `string` is decoded as a finite number, rejecting `NaN`, `Infinity`, and | |
| * `-Infinity` values. | |
| * | |
| * Encoding: | |
| * - A finite number is encoded as a `string`. | |
| * | |
| * @category Number | |
| * @since 4.0.0 | |
| */ | |
| export const FiniteFromString: FiniteFromString = String.annotate({ | |
| expected: "a string that will be decoded as a finite number" | |
| }).pipe(decodeTo(Finite, SchemaTransformation.numberFromString)) | |
| /** | |
| * Type-level representation of {@link BigIntFromString}. | |
| * | |
| * @category BigInt | |
| * @since 4.0.0 | |
| */ | |
| export interface BigIntFromString extends decodeTo<BigInt, String> { | |
| readonly "Rebuild": BigIntFromString | |
| } | |
| /** | |
| * Schema that parses a string into a `bigint`. | |
| * | |
| * **When to use** | |
| * | |
| * Use to parse signed base-10 integer strings into bigint values while encoding | |
| * bigint values back to decimal strings. | |
| * | |
| * **Details** | |
| * | |
| * Decoding: | |
| * - A `string` is decoded as a `bigint`. | |
| * | |
| * Encoding: | |
| * - A `bigint` is encoded as a `string`. | |
| * | |
| * **Gotchas** | |
| * | |
| * Decoding accepts only strings matching `^-?\d+$`. | |
| * | |
| * @see {@link isStringBigInt} for the string predicate used by this schema | |
| * @see {@link BigInt} for validating values that are already bigint values | |
| * @see {@link NumberFromString} for parsing JavaScript number strings, including non-finite values | |
| * @see {@link BigDecimalFromString} for parsing decimal number strings | |
| * | |
| * @category BigInt | |
| * @since 4.0.0 | |
| */ | |
| export const BigIntFromString: BigIntFromString = make<String>(SchemaAST.bigIntString).pipe( | |
| decodeTo(BigInt, SchemaTransformation.bigintFromString) | |
| ) | |
| /** | |
| * Type-level representation of {@link Trimmed}. | |
| * | |
| * @category string | |
| * @since 3.10.0 | |
| */ | |
| export interface Trimmed extends String { | |
| readonly "Rebuild": Trimmed | |
| } | |
| /** | |
| * Schema for strings that contains no leading or trailing whitespaces. | |
| * | |
| * @category string | |
| * @since 3.10.0 | |
| */ | |
| export const Trimmed: Trimmed = String.check(isTrimmed()) | |
| /** | |
| * Type-level representation of {@link Trim}. | |
| * | |
| * @category string | |
| * @since 3.10.0 | |
| */ | |
| export interface Trim extends decodeTo<Trimmed, String> { | |
| readonly "Rebuild": Trim | |
| } | |
| /** | |
| * Schema that trims whitespace from a string. | |
| * | |
| * **Details** | |
| * | |
| * Decoding: | |
| * - A `string` is decoded as a string with no leading or trailing whitespaces. | |
| * | |
| * Encoding: | |
| * - The trimmed string is encoded as is. | |
| * | |
| * @category string | |
| * @since 3.10.0 | |
| */ | |
| export const Trim: Trim = String.annotate({ | |
| expected: "a string that will be decoded as a trimmed string" | |
| }).pipe(decodeTo(Trimmed, SchemaTransformation.trim())) | |
| /** | |
| * Type-level representation of {@link StringFromBase64}. | |
| * | |
| * @category string | |
| * @since 3.10.0 | |
| */ | |
| export interface StringFromBase64 extends decodeTo<String, String> { | |
| readonly "Rebuild": StringFromBase64 | |
| } | |
| /** | |
| * Decodes a base64 (RFC4648) encoded string into a UTF-8 string. | |
| * | |
| * **Details** | |
| * | |
| * Decoding: | |
| * - A **valid** base64 encoded string is decoded as a UTF-8 `string`. | |
| * | |
| * Encoding: | |
| * - A `string` is encoded as a base64-encoded string. | |
| * | |
| * @category string | |
| * @since 3.10.0 | |
| */ | |
| export const StringFromBase64: StringFromBase64 = String.annotate({ | |
| expected: "a base64 encoded string that will be decoded as a UTF-8 string" | |
| }).pipe( | |
| decodeTo(String, SchemaTransformation.stringFromBase64String) | |
| ) | |
| /** | |
| * Type-level representation of {@link StringFromBase64Url}. | |
| * | |
| * @category string | |
| * @since 3.10.0 | |
| */ | |
| export interface StringFromBase64Url extends decodeTo<String, String> { | |
| readonly "Rebuild": StringFromBase64Url | |
| } | |
| /** | |
| * Decodes a base64 (URL) encoded string into a UTF-8 string. | |
| * | |
| * **Details** | |
| * | |
| * Decoding: | |
| * - A **valid** base64 (URL) encoded string is decoded as a UTF-8 `string`. | |
| * | |
| * Encoding: | |
| * - A `string` is encoded as a base64 (URL) encoded string. | |
| * | |
| * @category string | |
| * @since 3.10.0 | |
| */ | |
| export const StringFromBase64Url: StringFromBase64Url = String.annotate({ | |
| expected: "a base64 (URL) encoded string that will be decoded as a UTF-8 string" | |
| }).pipe( | |
| decodeTo(String, SchemaTransformation.stringFromBase64UrlString) | |
| ) | |
| /** | |
| * Type-level representation of {@link StringFromHex}. | |
| * | |
| * @category string | |
| * @since 3.10.0 | |
| */ | |
| export interface StringFromHex extends decodeTo<String, String> { | |
| readonly "Rebuild": StringFromHex | |
| } | |
| /** | |
| * Decodes a hex encoded string into a UTF-8 string. | |
| * | |
| * **Details** | |
| * | |
| * Decoding: | |
| * - A **valid** hex encoded string is decoded as a UTF-8 `string`. | |
| * | |
| * Encoding: | |
| * - A `string` is encoded as a hex string. | |
| * | |
| * @category string | |
| * @since 3.10.0 | |
| */ | |
| export const StringFromHex: StringFromHex = String.annotate({ | |
| expected: "a hex encoded string that will be decoded as a UTF-8 string" | |
| }).pipe( | |
| decodeTo(String, SchemaTransformation.stringFromHexString) | |
| ) | |
| /** | |
| * Type-level representation of {@link StringFromUriComponent}. | |
| * | |
| * @category string | |
| * @since 3.12.0 | |
| */ | |
| export interface StringFromUriComponent extends decodeTo<String, String> { | |
| readonly "Rebuild": StringFromUriComponent | |
| } | |
| /** | |
| * Decodes a URI component encoded string into a UTF-8 string. | |
| * Can be used to store data in a URL. | |
| * | |
| * **Details** | |
| * | |
| * Decoding: | |
| * - A **valid** URI component encoded string is decoded as a UTF-8 `string`. | |
| * | |
| * Encoding: | |
| * - A `string` is encoded as a URI component encoded string. | |
| * | |
| * **Example** (Decoding URI component strings) | |
| * | |
| * ```ts | |
| * import { Schema } from "effect" | |
| * | |
| * const PaginationSchema = Schema.Struct({ | |
| * maxItemPerPage: Schema.Number, | |
| * page: Schema.Number | |
| * }) | |
| * | |
| * const UrlSchema = Schema.StringFromUriComponent.pipe( | |
| * Schema.decodeTo(Schema.fromJsonString(PaginationSchema)) | |
| * ) | |
| * | |
| * console.log(Schema.encodeSync(UrlSchema)({ maxItemPerPage: 10, page: 1 })) | |
| * // %7B%22maxItemPerPage%22%3A10%2C%22page%22%3A1%7D | |
| * ``` | |
| * | |
| * @category string | |
| * @since 3.12.0 | |
| */ | |
| export const StringFromUriComponent: StringFromUriComponent = String.annotate({ | |
| expected: "a URI component encoded string that will be decoded as a UTF-8 string" | |
| }).pipe( | |
| decodeTo(String, SchemaTransformation.stringFromUriComponent) | |
| ) | |
| /** | |
| * Schema for property keys accepted by Effect schemas: finite `number`, | |
| * `symbol`, or `string`. | |
| * | |
| * @category PropertyKey | |
| * @since 4.0.0 | |
| */ | |
| export const PropertyKey = Union([Finite, Symbol, String]) | |
| /** | |
| * Schema for a Standard Schema v1 failure result. | |
| * | |
| * **Details** | |
| * | |
| * The result contains an `issues` array where each issue has a message and an | |
| * optional path made of property keys or keyed path segments. | |
| * | |
| * @category Standard Schema | |
| * @since 4.0.0 | |
| */ | |
| export const StandardSchemaV1FailureResult = Struct({ | |
| issues: ArraySchema(Struct({ | |
| message: String, | |
| path: optional(ArraySchema(Union([PropertyKey, Struct({ key: PropertyKey })]))) | |
| })) | |
| }) | |
| /** | |
| * Type-level representation of {@link BooleanFromBit}. | |
| * | |
| * @category boolean | |
| * @since 4.0.0 | |
| */ | |
| export interface BooleanFromBit extends decodeTo<Boolean, Literals<readonly [0, 1]>> { | |
| readonly "Rebuild": BooleanFromBit | |
| } | |
| /** | |
| * Schema for a boolean parsed from 0 or 1. | |
| * | |
| * **When to use** | |
| * | |
| * Use when decoding data sources that represent booleans as `0 | 1` while | |
| * keeping boolean values in the decoded model. | |
| * | |
| * **Details** | |
| * | |
| * Decoding accepts only `0 | 1`, maps `1` to `true`, and maps `0` to `false`. | |
| * Encoding maps `true` to `1` and `false` to `0`. | |
| * | |
| * @see {@link Boolean} for validating values that are already booleans | |
| * @see {@link Literals} for keeping bit literals instead of decoding them | |
| * | |
| * @category boolean | |
| * @since 4.0.0 | |
| */ | |
| export const BooleanFromBit: BooleanFromBit = Literals([0, 1]).pipe( | |
| decodeTo( | |
| Boolean, | |
| SchemaTransformation.transform({ | |
| decode: (bit) => bit === 1, | |
| encode: (bool) => bool ? 1 : 0 | |
| }) | |
| ) | |
| ) | |
| /** | |
| * Type-level representation of {@link Uint8Array}. | |
| * | |
| * @category Uint8Array | |
| * @since 4.0.0 | |
| */ | |
| export interface Uint8Array extends instanceOf<globalThis.Uint8Array<ArrayBufferLike>> { | |
| readonly "Rebuild": Uint8Array | |
| } | |
| const Base64String = String.annotate({ | |
| expected: "a base64 encoded string that will be decoded as Uint8Array", | |
| format: "byte", | |
| contentEncoding: "base64" | |
| }) | |
| /** | |
| * Schema for JavaScript `Uint8Array` objects. | |
| * | |
| * **Details** | |
| * | |
| * Default JSON serializer: | |
| * | |
| * The default JSON serializer encodes Uint8Array as a Base64 encoded string. | |
| * | |
| * @category Uint8Array | |
| * @since 4.0.0 | |
| */ | |
| export const Uint8Array: Uint8Array = instanceOf(globalThis.Uint8Array<ArrayBufferLike>, { | |
| typeConstructor: { | |
| _tag: "Uint8Array" | |
| }, | |
| generation: { | |
| runtime: `Schema.Uint8Array`, | |
| Type: `globalThis.Uint8Array` | |
| }, | |
| expected: "Uint8Array", | |
| toCodecJson: () => | |
| link<globalThis.Uint8Array<ArrayBufferLike>>()( | |
| Base64String, | |
| SchemaTransformation.uint8ArrayFromBase64String | |
| ), | |
| toArbitrary: () => (fc) => fc.uint8Array() | |
| }) | |
| /** | |
| * Type-level representation of {@link Uint8ArrayFromBase64}. | |
| * | |
| * @category Uint8Array | |
| * @since 3.10.0 | |
| */ | |
| export interface Uint8ArrayFromBase64 extends decodeTo<Uint8Array, String> { | |
| readonly "Rebuild": Uint8ArrayFromBase64 | |
| } | |
| /** | |
| * Schema that decodes a base64 encoded string into a | |
| * `Uint8Array`. | |
| * | |
| * **Details** | |
| * | |
| * Decoding: | |
| * - A **valid** base64 encoded string is decoded as a `Uint8Array`. | |
| * | |
| * Encoding: | |
| * - A `Uint8Array` is encoded as a base64-encoded string. | |
| * | |
| * @category Uint8Array | |
| * @since 3.10.0 | |
| */ | |
| export const Uint8ArrayFromBase64: Uint8ArrayFromBase64 = Base64String.pipe( | |
| decodeTo(Uint8Array, SchemaTransformation.uint8ArrayFromBase64String) | |
| ) | |
| /** | |
| * Type-level representation of {@link Uint8ArrayFromBase64Url}. | |
| * | |
| * @category Uint8Array | |
| * @since 3.10.0 | |
| */ | |
| export interface Uint8ArrayFromBase64Url extends decodeTo<Uint8Array, String> { | |
| readonly "Rebuild": Uint8ArrayFromBase64Url | |
| } | |
| /** | |
| * Schema that decodes a base64 (URL) encoded string into a | |
| * `Uint8Array`. | |
| * | |
| * **Details** | |
| * | |
| * Decoding: | |
| * - A **valid** base64 (URL) encoded string is decoded as a `Uint8Array`. | |
| * | |
| * Encoding: | |
| * - A `Uint8Array` is encoded as a base64 (URL) encoded string. | |
| * | |
| * @category Uint8Array | |
| * @since 3.10.0 | |
| */ | |
| export const Uint8ArrayFromBase64Url: Uint8ArrayFromBase64Url = String.annotate({ | |
| expected: "a base64 (URL) encoded string that will be decoded as a Uint8Array" | |
| }).pipe( | |
| decodeTo(Uint8Array, { | |
| decode: SchemaGetter.decodeBase64Url(), | |
| encode: SchemaGetter.encodeBase64Url() | |
| }) | |
| ) | |
| /** | |
| * Type-level representation of {@link Uint8ArrayFromHex}. | |
| * | |
| * @category Uint8Array | |
| * @since 3.10.0 | |
| */ | |
| export interface Uint8ArrayFromHex extends decodeTo<Uint8Array, String> { | |
| readonly "Rebuild": Uint8ArrayFromHex | |
| } | |
| /** | |
| * Schema that decodes a hex encoded string into a | |
| * `Uint8Array`. | |
| * | |
| * **Details** | |
| * | |
| * Decoding: | |
| * - A **valid** hex encoded string is decoded as a `Uint8Array`. | |
| * | |
| * Encoding: | |
| * - A `Uint8Array` is encoded as a hex encoded string. | |
| * | |
| * @category Uint8Array | |
| * @since 3.10.0 | |
| */ | |
| export const Uint8ArrayFromHex: Uint8ArrayFromHex = String.annotate({ | |
| expected: "a hex encoded string that will be decoded as a Uint8Array" | |
| }).pipe( | |
| decodeTo(Uint8Array, { | |
| decode: SchemaGetter.decodeHex(), | |
| encode: SchemaGetter.encodeHex() | |
| }) | |
| ) | |
| /** | |
| * Type-level representation of {@link DateTimeUtc}. | |
| * | |
| * @category DateTime | |
| * @since 3.10.0 | |
| */ | |
| export interface DateTimeUtc extends declare<DateTime.Utc> { | |
| readonly "Rebuild": DateTimeUtc | |
| } | |
| /** | |
| * Schema for `DateTime.Utc` values. | |
| * | |
| * **When to use** | |
| * | |
| * Use to validate existing `DateTime.Utc` schema values and use the default JSON | |
| * codec that represents them as UTC ISO strings. | |
| * | |
| * **Details** | |
| * | |
| * The default JSON codec decodes UTC ISO strings into `DateTime.Utc` values and | |
| * encodes `DateTime.Utc` values as UTC ISO strings. | |
| * | |
| * @see {@link DateTimeUtcFromString} for decoding date-time strings into UTC values | |
| * @see {@link DateTimeUtcFromDate} for decoding JavaScript Date values into UTC values | |
| * @see {@link DateTimeUtcFromMillis} for decoding epoch milliseconds into UTC values | |
| * @see {@link DateTimeZoned} for preserving zoned DateTime values | |
| * | |
| * @category DateTime | |
| * @since 3.10.0 | |
| */ | |
| export const DateTimeUtc: DateTimeUtc = declare( | |
| (u) => DateTime.isDateTime(u) && DateTime.isUtc(u), | |
| { | |
| typeConstructor: { | |
| _tag: "effect/DateTime.Utc" | |
| }, | |
| generation: { | |
| runtime: `Schema.DateTimeUtc`, | |
| Type: `DateTime.Utc`, | |
| importDeclaration: `import * as DateTime from "effect/DateTime"` | |
| }, | |
| expected: "DateTime.Utc", | |
| toCodecJson: () => | |
| link<DateTime.Utc>()( | |
| String, | |
| SchemaTransformation.dateTimeUtcFromString | |
| ), | |
| toArbitrary: () => (fc, ctx) => | |
| fc.date(dateArbitraryConstraints( | |
| ctx?.constraint, | |
| ctx?.constraint?.ordered?.order === DateTime.Order ? ctx.constraint.ordered : undefined, | |
| { valid: true }, | |
| DateTime.toDateUtc | |
| )) | |
| .map((date) => DateTime.fromDateUnsafe(date)), | |
| toFormatter: () => (utc) => utc.toString(), | |
| toEquivalence: () => DateTime.Equivalence | |
| } | |
| ) | |
| /** | |
| * Type-level representation of {@link DateTimeUtcFromDate}. | |
| * | |
| * @category DateTime | |
| * @since 3.12.0 | |
| */ | |
| export interface DateTimeUtcFromDate extends decodeTo<DateTimeUtc, Date> { | |
| readonly "Rebuild": DateTimeUtcFromDate | |
| } | |
| /** | |
| * Schema that decodes a `Date` into a `DateTime.Utc`. | |
| * | |
| * **When to use** | |
| * | |
| * Use when you need to decode valid JavaScript `Date` objects into | |
| * `DateTime.Utc` values. | |
| * | |
| * **Details** | |
| * | |
| * Decoding: | |
| * - A **valid** `Date` is decoded as a `DateTime.Utc` | |
| * | |
| * Encoding: | |
| * - A `DateTime.Utc` is encoded as a `Date` | |
| * | |
| * @see {@link DateTimeUtc} for validating values that are already `DateTime.Utc` | |
| * @see {@link DateTimeUtcFromString} for decoding date-time strings into UTC values | |
| * @see {@link DateTimeUtcFromMillis} for decoding epoch milliseconds into UTC values | |
| * @see {@link DateValid} for validating Date instances without converting them | |
| * | |
| * @category DateTime | |
| * @since 3.12.0 | |
| */ | |
| export const DateTimeUtcFromDate: DateTimeUtcFromDate = DateValid.pipe( | |
| decodeTo(DateTimeUtc, { | |
| decode: SchemaGetter.dateTimeUtcFromInput(), | |
| encode: SchemaGetter.transform(DateTime.toDateUtc) | |
| }) | |
| ) | |
| /** | |
| * Type-level representation of {@link DateTimeUtcFromString}. | |
| * | |
| * @category DateTime | |
| * @since 4.0.0 | |
| */ | |
| export interface DateTimeUtcFromString extends decodeTo<DateTimeUtc, String> { | |
| readonly "Rebuild": DateTimeUtcFromString | |
| } | |
| /** | |
| * Schema that decodes a date-time string into a `DateTime.Utc`. | |
| * | |
| * **Details** | |
| * | |
| * Decoding: | |
| * | |
| * - A string accepted by `DateTime.make` is parsed and normalized to UTC. Strings | |
| * without an explicit zone are interpreted as UTC. | |
| * | |
| * Encoding: | |
| * | |
| * - A `DateTime.Utc` is encoded as a UTC ISO 8601 string. | |
| * | |
| * @category DateTime | |
| * @since 4.0.0 | |
| */ | |
| export const DateTimeUtcFromString: DateTimeUtcFromString = String.annotate({ | |
| expected: "a string that will be decoded as a DateTime.Utc" | |
| }).pipe( | |
| decodeTo( | |
| DateTimeUtc, | |
| SchemaTransformation.dateTimeUtcFromString | |
| ) | |
| ) | |
| /** | |
| * Type-level representation of {@link DateTimeUtcFromMillis}. | |
| * | |
| * @category DateTime | |
| * @since 4.0.0 | |
| */ | |
| export interface DateTimeUtcFromMillis extends decodeTo<instanceOf<DateTime.Utc>, Number> { | |
| readonly "Rebuild": DateTimeUtcFromMillis | |
| } | |
| /** | |
| * Schema that decodes a number into a `DateTime.Utc`. | |
| * | |
| * **Details** | |
| * | |
| * Decoding: | |
| * - A number of milliseconds since the Unix epoch is decoded as a `DateTime.Utc` | |
| * | |
| * Encoding: | |
| * - A `DateTime.Utc` is encoded as a number of milliseconds since the Unix epoch. | |
| * | |
| * @category DateTime | |
| * @since 4.0.0 | |
| */ | |
| export const DateTimeUtcFromMillis: DateTimeUtcFromMillis = Number.pipe( | |
| decodeTo(DateTimeUtc, { | |
| decode: SchemaGetter.dateTimeUtcFromInput(), | |
| encode: SchemaGetter.transform(DateTime.toEpochMillis) | |
| }) | |
| ) | |
| /** | |
| * Type-level representation of {@link TimeZoneOffset}. | |
| * | |
| * @category DateTime | |
| * @since 3.10.0 | |
| */ | |
| export interface TimeZoneOffset extends declare<DateTime.TimeZone.Offset> { | |
| readonly "Rebuild": TimeZoneOffset | |
| } | |
| /** | |
| * Schema for `DateTime.TimeZone.Offset` values. | |
| * | |
| * **Details** | |
| * | |
| * Default JSON serializer: | |
| * | |
| * - encodes `DateTime.TimeZone.Offset` as a number (offset in milliseconds) | |
| * | |
| * @category DateTime | |
| * @since 3.10.0 | |
| */ | |
| export const TimeZoneOffset: TimeZoneOffset = declare( | |
| DateTime.isTimeZoneOffset, | |
| { | |
| typeConstructor: { | |
| _tag: "effect/DateTime.TimeZone.Offset" | |
| }, | |
| generation: { | |
| runtime: `Schema.TimeZoneOffset`, | |
| Type: `DateTime.TimeZone.Offset`, | |
| importDeclaration: `import * as DateTime from "effect/DateTime"` | |
| }, | |
| expected: "DateTime.TimeZone.Offset", | |
| toCodecJson: () => | |
| link<DateTime.TimeZone.Offset>()( | |
| Number, | |
| SchemaTransformation.timeZoneOffsetFromNumber | |
| ), | |
| toArbitrary: () => (fc) => | |
| fc.integer({ min: -12 * 60 * 60 * 1000, max: 14 * 60 * 60 * 1000 }).map((n) => DateTime.zoneMakeOffset(n)), | |
| toFormatter: () => (tz) => DateTime.zoneToString(tz), | |
| toEquivalence: () => (a, b) => a.offset === b.offset | |
| } | |
| ) | |
| /** | |
| * Type-level representation of {@link TimeZoneNamed}. | |
| * | |
| * @category DateTime | |
| * @since 3.10.0 | |
| */ | |
| export interface TimeZoneNamed extends declare<DateTime.TimeZone.Named> { | |
| readonly "Rebuild": TimeZoneNamed | |
| } | |
| const TimeZoneNamedString = String.annotate({ expected: "an IANA time zone identifier" }) | |
| /** | |
| * Schema for `DateTime.TimeZone.Named` values. | |
| * | |
| * **Details** | |
| * | |
| * Default JSON serializer: | |
| * | |
| * - encodes `DateTime.TimeZone.Named` as a string (IANA time zone identifier) | |
| * | |
| * @category DateTime | |
| * @since 3.10.0 | |
| */ | |
| export const TimeZoneNamed: TimeZoneNamed = declare( | |
| DateTime.isTimeZoneNamed, | |
| { | |
| typeConstructor: { | |
| _tag: "effect/DateTime.TimeZone.Named" | |
| }, | |
| generation: { | |
| runtime: `Schema.TimeZoneNamed`, | |
| Type: `DateTime.TimeZone.Named`, | |
| importDeclaration: `import * as DateTime from "effect/DateTime"` | |
| }, | |
| expected: "DateTime.TimeZone.Named", | |
| toCodecJson: () => | |
| link<DateTime.TimeZone.Named>()( | |
| TimeZoneNamedString, | |
| SchemaTransformation.timeZoneNamedFromString | |
| ), | |
| toArbitrary: () => (fc) => | |
| fc.constantFrom( | |
| ...["UTC", "Europe/London", "America/New_York", "Asia/Tokyo", "Australia/Sydney"].map( | |
| DateTime.zoneMakeNamedUnsafe | |
| ) | |
| ), | |
| toFormatter: () => (tz) => DateTime.zoneToString(tz), | |
| toEquivalence: () => (a, b) => a.id === b.id | |
| } | |
| ) | |
| /** | |
| * Type-level representation of {@link TimeZoneNamedFromString}. | |
| * | |
| * @category DateTime | |
| * @since 4.0.0 | |
| */ | |
| export interface TimeZoneNamedFromString extends decodeTo<TimeZoneNamed, String> { | |
| readonly "Rebuild": TimeZoneNamedFromString | |
| } | |
| /** | |
| * Schema that parses an IANA time zone identifier string into a `DateTime.TimeZone.Named`. | |
| * | |
| * **Details** | |
| * | |
| * Decoding: | |
| * - A `string` is decoded as a `DateTime.TimeZone.Named`. | |
| * | |
| * Encoding: | |
| * - A `DateTime.TimeZone.Named` is encoded as a `string`. | |
| * | |
| * @category DateTime | |
| * @since 4.0.0 | |
| */ | |
| export const TimeZoneNamedFromString: TimeZoneNamedFromString = TimeZoneNamedString.pipe( | |
| decodeTo(TimeZoneNamed, SchemaTransformation.timeZoneNamedFromString) | |
| ) | |
| /** | |
| * Type-level representation of {@link TimeZone}. | |
| * | |
| * @category DateTime | |
| * @since 3.10.0 | |
| */ | |
| export interface TimeZone extends declare<DateTime.TimeZone> { | |
| readonly "Rebuild": TimeZone | |
| } | |
| const TimeZoneString = String.annotate({ | |
| expected: "a time zone string (IANA identifier or offset like +03:00)" | |
| }) | |
| /** | |
| * Schema for `DateTime.TimeZone` values. | |
| * | |
| * **Details** | |
| * | |
| * Default JSON serializer: | |
| * | |
| * - encodes `DateTime.TimeZone` as a string (IANA identifier or offset like | |
| * `+03:00`) | |
| * | |
| * @category DateTime | |
| * @since 3.10.0 | |
| */ | |
| export const TimeZone: TimeZone = declare( | |
| DateTime.isTimeZone, | |
| { | |
| typeConstructor: { | |
| _tag: "effect/DateTime.TimeZone" | |
| }, | |
| generation: { | |
| runtime: `Schema.TimeZone`, | |
| Type: `DateTime.TimeZone`, | |
| importDeclaration: `import * as DateTime from "effect/DateTime"` | |
| }, | |
| expected: "DateTime.TimeZone", | |
| toCodecJson: () => | |
| link<DateTime.TimeZone>()( | |
| TimeZoneString, | |
| SchemaTransformation.timeZoneFromString | |
| ), | |
| toArbitrary: () => (fc) => | |
| fc.oneof( | |
| fc.integer({ min: -12 * 60 * 60 * 1000, max: 14 * 60 * 60 * 1000 }).map((n) => DateTime.zoneMakeOffset(n)), | |
| fc.constantFrom( | |
| ...["UTC", "Europe/London", "America/New_York", "Asia/Tokyo", "Australia/Sydney"].map( | |
| DateTime.zoneMakeNamedUnsafe | |
| ) | |
| ) | |
| ), | |
| toFormatter: () => (tz) => DateTime.zoneToString(tz), | |
| toEquivalence: () => (a, b) => DateTime.zoneToString(a) === DateTime.zoneToString(b) | |
| } | |
| ) | |
| /** | |
| * Type-level representation of {@link TimeZoneFromString}. | |
| * | |
| * @category DateTime | |
| * @since 4.0.0 | |
| */ | |
| export interface TimeZoneFromString extends decodeTo<TimeZone, String> { | |
| readonly "Rebuild": TimeZoneFromString | |
| } | |
| /** | |
| * Schema that parses a time zone string into a `DateTime.TimeZone`. | |
| * | |
| * **Details** | |
| * | |
| * Decoding: | |
| * - A `string` (IANA identifier or offset like `+03:00`) is decoded as a `DateTime.TimeZone`. | |
| * | |
| * Encoding: | |
| * - A `DateTime.TimeZone` is encoded as a `string`. | |
| * | |
| * @category DateTime | |
| * @since 4.0.0 | |
| */ | |
| export const TimeZoneFromString: TimeZoneFromString = TimeZoneString.pipe( | |
| decodeTo(TimeZone, SchemaTransformation.timeZoneFromString) | |
| ) | |
| /** | |
| * Type-level representation of {@link DateTimeZoned}. | |
| * | |
| * @category DateTime | |
| * @since 3.10.0 | |
| */ | |
| export interface DateTimeZoned extends declare<DateTime.Zoned> { | |
| readonly "Rebuild": DateTimeZoned | |
| } | |
| const DateTimeZonedString = String.annotate({ | |
| expected: "a zoned DateTime string (e.g. 2024-01-01T00:00:00.000+00:00[Europe/London])" | |
| }) | |
| /** | |
| * Schema for `DateTime.Zoned` values. | |
| * | |
| * **Details** | |
| * | |
| * Default JSON serializer: | |
| * | |
| * - encodes offset zones as an ISO date-time with a numeric offset, such as | |
| * `YYYY-MM-DDTHH:mm:ss.sss+HH:MM` | |
| * - encodes named zones by appending the IANA identifier in brackets, such as | |
| * `YYYY-MM-DDTHH:mm:ss.sss+HH:MM[Time/Zone]` | |
| * | |
| * @category DateTime | |
| * @since 3.10.0 | |
| */ | |
| export const DateTimeZoned: DateTimeZoned = declare( | |
| (u) => DateTime.isDateTime(u) && DateTime.isZoned(u), | |
| { | |
| typeConstructor: { | |
| _tag: "effect/DateTime.Zoned" | |
| }, | |
| generation: { | |
| runtime: `Schema.DateTimeZoned`, | |
| Type: `DateTime.Zoned`, | |
| importDeclaration: `import * as DateTime from "effect/DateTime"` | |
| }, | |
| expected: "DateTime.Zoned", | |
| toCodecJson: () => | |
| link<DateTime.Zoned>()( | |
| DateTimeZonedString, | |
| SchemaTransformation.dateTimeZonedFromString | |
| ), | |
| toArbitrary: () => (fc, ctx) => | |
| fc.tuple( | |
| fc.date(dateArbitraryConstraints( | |
| ctx?.constraint, | |
| ctx?.constraint?.ordered?.order === DateTime.Order ? ctx.constraint.ordered : undefined, | |
| { | |
| max: new globalThis.Date(8640000000000000 - 14 * 60 * 60 * 1000), | |
| min: new globalThis.Date(-8640000000000000 + 14 * 60 * 60 * 1000), | |
| valid: true | |
| }, | |
| DateTime.toDateUtc | |
| )), | |
| fc.constantFrom("UTC", "Europe/London", "America/New_York", "Asia/Tokyo", "Australia/Sydney") | |
| ).map(([date, zone]) => DateTime.makeZonedUnsafe(date, { timeZone: zone })), | |
| toFormatter: () => (zoned) => DateTime.formatIsoZoned(zoned), | |
| toEquivalence: () => DateTime.Equivalence | |
| } | |
| ) | |
| /** | |
| * Type-level representation of {@link DateTimeZonedFromString}. | |
| * | |
| * @category DateTime | |
| * @since 4.0.0 | |
| */ | |
| export interface DateTimeZonedFromString extends decodeTo<DateTimeZoned, String> { | |
| readonly "Rebuild": DateTimeZonedFromString | |
| } | |
| /** | |
| * Schema that parses a zoned DateTime string into a `DateTime.Zoned`. | |
| * | |
| * **Details** | |
| * | |
| * Decoding: | |
| * - A `string` (e.g. `2024-01-01T00:00:00.000+00:00[Europe/London]`) is decoded as a `DateTime.Zoned`. | |
| * | |
| * Encoding: | |
| * - A `DateTime.Zoned` is encoded as a `string`. | |
| * | |
| * @category DateTime | |
| * @since 4.0.0 | |
| */ | |
| export const DateTimeZonedFromString: DateTimeZonedFromString = DateTimeZonedString.pipe( | |
| decodeTo(DateTimeZoned, SchemaTransformation.dateTimeZonedFromString) | |
| ) | |
| // ----------------------------------------------------------------------------- | |
| // Class | |
| // ----------------------------------------------------------------------------- | |
| /** | |
| * Type-level representation returned by {@link Class}. | |
| * | |
| * @category models | |
| * @since 3.10.0 | |
| */ | |
| export interface Class<Self, S extends Top & { readonly fields: Struct.Fields }, Inherited> extends | |
| Bottom< | |
| Self, | |
| S["Encoded"], | |
| S["DecodingServices"], | |
| S["EncodingServices"], | |
| SchemaAST.Declaration, | |
| decodeTo<declareConstructor<Self, S["Encoded"], readonly [S], S["Iso"]>, S>, | |
| RequiredKeys<S["~type.make.in"]> extends never ? void | S["~type.make.in"] : S["~type.make.in"], | |
| S["Iso"], | |
| readonly [S], | |
| Self, | |
| S["~type.mutability"], | |
| S["~type.optionality"], | |
| S["~type.constructor.default"], | |
| S["~encoded.mutability"], | |
| S["~encoded.optionality"] | |
| > | |
| { | |
| new( | |
| ...args: {} extends S["~type.make.in"] ? [props?: S["~type.make.in"], options?: MakeOptions] | |
| : [props: S["~type.make.in"], options?: MakeOptions] | |
| ): S["Type"] & Inherited | |
| readonly identifier: string | |
| readonly fields: S["fields"] | |
| /** | |
| * Returns a new struct with the fields modified by the provided function. | |
| * | |
| * **Details** | |
| * | |
| * Options: | |
| * | |
| * - `unsafePreserveChecks` - if `true`, keep any `.check(...)` constraints | |
| * that were attached to the original struct. Defaults to `false`. | |
| * | |
| * **Warning**: This is an unsafe operation. Since `mapFields` | |
| * transformations change the schema type, the original refinement functions | |
| * may no longer be valid or safe to apply to the transformed schema. Only | |
| * use this option if you have verified that your refinements remain correct | |
| * after the transformation. | |
| */ | |
| mapFields<To extends Struct.Fields>( | |
| f: (fields: S["fields"]) => To, | |
| options?: { | |
| readonly unsafePreserveChecks?: boolean | undefined | |
| } | undefined | |
| ): Struct<Simplify<Readonly<To>>> | |
| extend<Extended = never, Static = {}, Brand = {}>( | |
| identifier: string | |
| ): <NewFields extends Struct.Fields>( | |
| fields: NewFields, | |
| annotations?: Annotations.Declaration<Extended, readonly [Struct<Simplify<Assign<S["fields"], NewFields>>>]> | |
| ) => [Extended] extends [never] ? MissingSelfGeneric<"Base.extend"> : InheritStaticMembers< | |
| Class<Extended, Struct<Simplify<Assign<S["fields"], NewFields>>>, Self & Brand>, | |
| Static | |
| > | |
| } | |
| // Merges custom static members from a parent class onto the extended class, | |
| // giving priority to the extended class's own members (e.g. schema-generated statics). | |
| type InheritStaticMembers<C, Static> = C & Pick<Static, Exclude<keyof Static, keyof C>> | |
| const immerable: unique symbol = globalThis.Symbol.for("immer-draftable") as any | |
| function makeClass< | |
| Self, | |
| S extends Struct<Struct.Fields>, | |
| Inherited extends new(...args: ReadonlyArray<any>) => any | |
| >( | |
| Inherited: Inherited, | |
| identifier: string, | |
| struct: S, | |
| annotations: Annotations.Declaration<Self, readonly [S]> | undefined, | |
| proto: ((identifier: string) => object) | undefined | |
| ): any { | |
| const getClassSchema = getClassSchemaFactory(struct, identifier, annotations) | |
| const ClassTypeId = getClassTypeId(identifier) // HMR support | |
| const out = class extends Inherited { | |
| constructor(...[input, options]: ReadonlyArray<any>) { | |
| input = input ?? {} | |
| const validated = struct.make(input, options) | |
| super({ ...input, ...validated }, { ...options, disableChecks: true }) | |
| } | |
| static readonly [TypeId] = TypeId | |
| get [ClassTypeId]() { | |
| return ClassTypeId | |
| } | |
| static readonly [immerable] = true | |
| static readonly identifier = identifier | |
| static readonly fields = struct.fields | |
| static get ast(): SchemaAST.Declaration { | |
| return getClassSchema(this).ast | |
| } | |
| static pipe() { | |
| return Pipeable.pipeArguments(this, arguments) | |
| } | |
| static rebuild(ast: SchemaAST.Declaration) { | |
| return getClassSchema(this).rebuild(ast) | |
| } | |
| static make(input: S["~type.make.in"], options?: MakeOptions): Self { | |
| return new this(input, options) | |
| } | |
| static makeOption(input: S["~type.make.in"], options?: MakeOptions): Option_.Option<Self> { | |
| return SchemaParser.makeOption(getClassSchema(this) as any)(input ?? {}, options) as any | |
| } | |
| static makeEffect(input: S["~type.make.in"], options?: MakeOptions): Effect.Effect<Self, SchemaError> { | |
| return (getClassSchema(this) as any).makeEffect(input ?? {}, options) | |
| } | |
| static annotate(annotations: Annotations.Declaration<Self, readonly [S]>) { | |
| return this.rebuild(SchemaAST.annotate(this.ast, annotations)) | |
| } | |
| static annotateKey(annotations: Annotations.Key<Self>) { | |
| return this.rebuild(SchemaAST.annotateKey(this.ast, annotations)) | |
| } | |
| static check(...checks: readonly [SchemaAST.Check<Self>, ...Array<SchemaAST.Check<Self>>]) { | |
| return this.rebuild(SchemaAST.appendChecks(this.ast, checks)) | |
| } | |
| static extend<Extended>( | |
| identifier: string | |
| ): <NewFields extends Struct.Fields>( | |
| fields: NewFields, | |
| annotations?: Annotations.Declaration<Extended, readonly [Struct<Simplify<Assign<S["fields"], NewFields>>>]> | |
| ) => Class<Extended, Struct<Simplify<Assign<S["fields"], NewFields>>>, Self> { | |
| return (newFields, annotations) => { | |
| const fields = { ...struct.fields, ...newFields } | |
| return makeClass( | |
| this, | |
| identifier, | |
| makeStruct(SchemaAST.struct(fields, struct.ast.checks, { identifier }), fields), | |
| annotations, | |
| proto | |
| ) | |
| } | |
| } | |
| static mapFields<To extends Struct.Fields>( | |
| f: (fields: S["fields"]) => To, | |
| options?: { | |
| readonly unsafePreserveChecks?: boolean | undefined | |
| } | undefined | |
| ): Struct<Simplify<Readonly<To>>> { | |
| return struct.mapFields(f, options) | |
| } | |
| } | |
| if (proto !== undefined) { | |
| Object.assign(out.prototype, proto(identifier)) | |
| } | |
| return out | |
| } | |
| function getClassTransformation(self: new(...args: ReadonlyArray<any>) => any) { | |
| return new SchemaTransformation.Transformation<any, any, never, never>( | |
| SchemaGetter.transform((input) => new self(input)), | |
| SchemaGetter.passthrough() | |
| ) | |
| } | |
| function getClassTypeId(identifier: string) { | |
| return `~effect/Schema/Class/${identifier}` | |
| } | |
| function getClassSchemaFactory<S extends Top>( | |
| from: S, | |
| identifier: string, | |
| annotations: Annotations.Declaration<any, readonly [S]> | undefined | |
| ) { | |
| let memo: decodeTo<declareConstructor<any, S["Encoded"], readonly [S]>, S> | undefined | |
| return <Self extends (new(...args: ReadonlyArray<any>) => any) & { readonly identifier: string }>( | |
| self: Self | |
| ): decodeTo<declareConstructor<Self, S["Encoded"], readonly [S]>, S> => { | |
| if (memo === undefined) { | |
| const transformation = getClassTransformation(self) | |
| const to = make<declareConstructor<Self, S["Encoded"], readonly [S]>>( | |
| new SchemaAST.Declaration( | |
| [from.ast], | |
| () => (input, ast) => { | |
| return input instanceof self || | |
| Predicate.hasProperty(input, getClassTypeId(identifier)) ? | |
| Effect.succeed(input) : | |
| Effect.fail(new SchemaIssue.InvalidType(ast, Option_.some(input))) | |
| }, | |
| { | |
| identifier, | |
| [SchemaAST.ClassTypeId]: ([from]: readonly [SchemaAST.AST]) => new SchemaAST.Link(from, transformation), | |
| toCodec: ([from]: readonly [Codec<S["Encoded"]>]) => new SchemaAST.Link(from.ast, transformation), | |
| toArbitrary: ([from]: readonly [Annotations.ToArbitrary.TypeParameter<S["Type"]>]) => () => ({ | |
| arbitrary: from.arbitrary.map((args: S["Type"]) => new self(args)), | |
| terminal: from.terminal?.map((args: S["Type"]) => new self(args)) | |
| }), | |
| toFormatter: ([from]: readonly [Formatter<S["Type"]>]) => (t: Self) => `${self.identifier}(${from(t)})`, | |
| "~sentinels": SchemaAST.collectSentinels(from.ast), | |
| ...annotations | |
| } | |
| ) | |
| ) | |
| memo = from.pipe(decodeTo(to, transformation)) | |
| } | |
| return memo | |
| } | |
| } | |
| function isStruct(schema: Struct.Fields | Struct<Struct.Fields>): schema is Struct<Struct.Fields> { | |
| return isSchema(schema) | |
| } | |
| type MissingSelfGeneric<Usage extends string> = | |
| `Missing \`Self\` generic - use \`class Self extends ${Usage}<Self>(...)\`` | |
| /** | |
| * Creates a schema-backed class whose constructor validates input against a | |
| * {@link Struct} schema. Construction throws a {@link SchemaError} on invalid | |
| * input. | |
| * | |
| * **When to use** | |
| * | |
| * Use when you need a schema-backed data class with validated construction, | |
| * schema-derived decoding/encoding, and class-style methods or inheritance. | |
| * | |
| * **Details** | |
| * | |
| * Pass the desired class type as the first type parameter. The second optional | |
| * type parameter can be used to add nominal brands. | |
| * | |
| * **Gotchas** | |
| * | |
| * Passing `disableChecks` in the options skips constructor validation. | |
| * | |
| * **Example** (Basic class) | |
| * | |
| * ```ts | |
| * import { Schema } from "effect" | |
| * | |
| * class Person extends Schema.Class<Person>("Person")({ | |
| * name: Schema.String, | |
| * age: Schema.Number | |
| * }) {} | |
| * | |
| * const alice = new Person({ name: "Alice", age: 30 }) | |
| * console.log(alice.name) // "Alice" | |
| * console.log(`${alice}`) // "Person({ name: Alice, age: 30 })" | |
| * ``` | |
| * | |
| * **Example** (Extending a class) | |
| * | |
| * ```ts | |
| * import { Schema } from "effect" | |
| * | |
| * class Animal extends Schema.Class<Animal>("Animal")({ | |
| * name: Schema.String | |
| * }) {} | |
| * | |
| * class Dog extends Animal.extend<Dog>("Dog")({ | |
| * breed: Schema.String | |
| * }) {} | |
| * | |
| * const dog = new Dog({ name: "Rex", breed: "Labrador" }) | |
| * console.log(dog.name) // "Rex" | |
| * console.log(dog.breed) // "Labrador" | |
| * ``` | |
| * | |
| * @see {@link TaggedClass} for adding a `_tag` literal field to the class schema | |
| * @see {@link ErrorClass} for defining schema-backed error classes | |
| * @see {@link TaggedErrorClass} for defining tagged schema-backed error classes | |
| * | |
| * @category constructors | |
| * @since 3.10.0 | |
| */ | |
| export const Class: { | |
| /** | |
| * Creates a schema-backed class whose constructor validates input against a | |
| * {@link Struct} schema. Construction throws a {@link SchemaError} on invalid | |
| * input. | |
| * | |
| * **When to use** | |
| * | |
| * Use when you need a schema-backed data class with validated construction, | |
| * schema-derived decoding/encoding, and class-style methods or inheritance. | |
| * | |
| * **Details** | |
| * | |
| * Pass the desired class type as the first type parameter. The second optional | |
| * type parameter can be used to add nominal brands. | |
| * | |
| * **Gotchas** | |
| * | |
| * Passing `disableChecks` in the options skips constructor validation. | |
| * | |
| * **Example** (Basic class) | |
| * | |
| * ```ts | |
| * import { Schema } from "effect" | |
| * | |
| * class Person extends Schema.Class<Person>("Person")({ | |
| * name: Schema.String, | |
| * age: Schema.Number | |
| * }) {} | |
| * | |
| * const alice = new Person({ name: "Alice", age: 30 }) | |
| * console.log(alice.name) // "Alice" | |
| * console.log(`${alice}`) // "Person({ name: Alice, age: 30 })" | |
| * ``` | |
| * | |
| * **Example** (Extending a class) | |
| * | |
| * ```ts | |
| * import { Schema } from "effect" | |
| * | |
| * class Animal extends Schema.Class<Animal>("Animal")({ | |
| * name: Schema.String | |
| * }) {} | |
| * | |
| * class Dog extends Animal.extend<Dog>("Dog")({ | |
| * breed: Schema.String | |
| * }) {} | |
| * | |
| * const dog = new Dog({ name: "Rex", breed: "Labrador" }) | |
| * console.log(dog.name) // "Rex" | |
| * console.log(dog.breed) // "Labrador" | |
| * ``` | |
| * | |
| * @see {@link TaggedClass} for adding a `_tag` literal field to the class schema | |
| * @see {@link ErrorClass} for defining schema-backed error classes | |
| * @see {@link TaggedErrorClass} for defining tagged schema-backed error classes | |
| * | |
| * @category constructors | |
| * @since 3.10.0 | |
| */ | |
| <Self = never, Brand = {}>(identifier: string): { | |
| /** | |
| * Creates a schema-backed class whose constructor validates input against a | |
| * {@link Struct} schema. Construction throws a {@link SchemaError} on invalid | |
| * input. | |
| * | |
| * **When to use** | |
| * | |
| * Use when you need a schema-backed data class with validated construction, | |
| * schema-derived decoding/encoding, and class-style methods or inheritance. | |
| * | |
| * **Details** | |
| * | |
| * Pass the desired class type as the first type parameter. The second optional | |
| * type parameter can be used to add nominal brands. | |
| * | |
| * **Gotchas** | |
| * | |
| * Passing `disableChecks` in the options skips constructor validation. | |
| * | |
| * **Example** (Basic class) | |
| * | |
| * ```ts | |
| * import { Schema } from "effect" | |
| * | |
| * class Person extends Schema.Class<Person>("Person")({ | |
| * name: Schema.String, | |
| * age: Schema.Number | |
| * }) {} | |
| * | |
| * const alice = new Person({ name: "Alice", age: 30 }) | |
| * console.log(alice.name) // "Alice" | |
| * console.log(`${alice}`) // "Person({ name: Alice, age: 30 })" | |
| * ``` | |
| * | |
| * **Example** (Extending a class) | |
| * | |
| * ```ts | |
| * import { Schema } from "effect" | |
| * | |
| * class Animal extends Schema.Class<Animal>("Animal")({ | |
| * name: Schema.String | |
| * }) {} | |
| * | |
| * class Dog extends Animal.extend<Dog>("Dog")({ | |
| * breed: Schema.String | |
| * }) {} | |
| * | |
| * const dog = new Dog({ name: "Rex", breed: "Labrador" }) | |
| * console.log(dog.name) // "Rex" | |
| * console.log(dog.breed) // "Labrador" | |
| * ``` | |
| * | |
| * @see {@link TaggedClass} for adding a `_tag` literal field to the class schema | |
| * @see {@link ErrorClass} for defining schema-backed error classes | |
| * @see {@link TaggedErrorClass} for defining tagged schema-backed error classes | |
| * | |
| * @category constructors | |
| * @since 3.10.0 | |
| */ | |
| <const Fields extends Struct.Fields>( | |
| fields: Fields, | |
| annotations?: Annotations.Declaration<Self, readonly [Struct<Fields>]> | |
| ): [Self] extends [never] ? MissingSelfGeneric<"Schema.Class"> : Class<Self, Struct<Fields>, Brand> | |
| /** | |
| * Creates a schema-backed class whose constructor validates input against a | |
| * {@link Struct} schema. Construction throws a {@link SchemaError} on invalid | |
| * input. | |
| * | |
| * **When to use** | |
| * | |
| * Use when you need a schema-backed data class with validated construction, | |
| * schema-derived decoding/encoding, and class-style methods or inheritance. | |
| * | |
| * **Details** | |
| * | |
| * Pass the desired class type as the first type parameter. The second optional | |
| * type parameter can be used to add nominal brands. | |
| * | |
| * **Gotchas** | |
| * | |
| * Passing `disableChecks` in the options skips constructor validation. | |
| * | |
| * **Example** (Basic class) | |
| * | |
| * ```ts | |
| * import { Schema } from "effect" | |
| * | |
| * class Person extends Schema.Class<Person>("Person")({ | |
| * name: Schema.String, | |
| * age: Schema.Number | |
| * }) {} | |
| * | |
| * const alice = new Person({ name: "Alice", age: 30 }) | |
| * console.log(alice.name) // "Alice" | |
| * console.log(`${alice}`) // "Person({ name: Alice, age: 30 })" | |
| * ``` | |
| * | |
| * **Example** (Extending a class) | |
| * | |
| * ```ts | |
| * import { Schema } from "effect" | |
| * | |
| * class Animal extends Schema.Class<Animal>("Animal")({ | |
| * name: Schema.String | |
| * }) {} | |
| * | |
| * class Dog extends Animal.extend<Dog>("Dog")({ | |
| * breed: Schema.String | |
| * }) {} | |
| * | |
| * const dog = new Dog({ name: "Rex", breed: "Labrador" }) | |
| * console.log(dog.name) // "Rex" | |
| * console.log(dog.breed) // "Labrador" | |
| * ``` | |
| * | |
| * @see {@link TaggedClass} for adding a `_tag` literal field to the class schema | |
| * @see {@link ErrorClass} for defining schema-backed error classes | |
| * @see {@link TaggedErrorClass} for defining tagged schema-backed error classes | |
| * | |
| * @category constructors | |
| * @since 3.10.0 | |
| */ | |
| <S extends Struct<Struct.Fields>>(schema: S, annotations?: Annotations.Declaration<Self, readonly [S]>): [Self] extends [never] ? MissingSelfGeneric<"Schema.Class"> : Class<Self, S, Brand> | |
| } | |
| } = <Self, Brand = {}>(identifier: string) => | |
| ( | |
| schema: Struct.Fields | Struct<Struct.Fields>, | |
| annotations?: Annotations.Declaration<Self, readonly [Struct<Struct.Fields>]> | |
| ): [Self] extends [never] ? MissingSelfGeneric<"Schema.Class"> : Class<Self, Struct<Struct.Fields>, Brand> => { | |
| const struct = isStruct(schema) ? schema : Struct(schema) | |
| return makeClass( | |
| Data.Class, | |
| identifier, | |
| struct, | |
| annotations, | |
| (identifier) => ({ | |
| toString() { | |
| return `${identifier}(${format({ ...this })})` | |
| } | |
| }) | |
| ) | |
| } | |
| /** | |
| * Defines a schema-backed class with an automatically populated `_tag` field. | |
| * | |
| * **When to use** | |
| * | |
| * Use to define class instances that are validated by a schema and participate | |
| * in tagged union matching. | |
| * | |
| * **Details** | |
| * | |
| * The optional `identifier` parameter overrides the schema identifier; | |
| * it defaults to the `tag` value. | |
| * | |
| * **Example** (Tagged class) | |
| * | |
| * ```ts | |
| * import { Schema } from "effect" | |
| * | |
| * class Circle extends Schema.TaggedClass<Circle>()("Circle", { | |
| * radius: Schema.Number | |
| * }) {} | |
| * | |
| * const c = new Circle({ radius: 5 }) | |
| * console.log(c._tag) // "Circle" | |
| * console.log(c.radius) // 5 | |
| * ``` | |
| * | |
| * @category constructors | |
| * @since 3.10.0 | |
| */ | |
| export const TaggedClass: { | |
| /** | |
| * Defines a schema-backed class with an automatically populated `_tag` field. | |
| * | |
| * **When to use** | |
| * | |
| * Use to define class instances that are validated by a schema and participate | |
| * in tagged union matching. | |
| * | |
| * **Details** | |
| * | |
| * The optional `identifier` parameter overrides the schema identifier; | |
| * it defaults to the `tag` value. | |
| * | |
| * **Example** (Tagged class) | |
| * | |
| * ```ts | |
| * import { Schema } from "effect" | |
| * | |
| * class Circle extends Schema.TaggedClass<Circle>()("Circle", { | |
| * radius: Schema.Number | |
| * }) {} | |
| * | |
| * const c = new Circle({ radius: 5 }) | |
| * console.log(c._tag) // "Circle" | |
| * console.log(c.radius) // 5 | |
| * ``` | |
| * | |
| * @category constructors | |
| * @since 3.10.0 | |
| */ | |
| <Self = never, Brand = {}>(identifier?: string): { | |
| /** | |
| * Defines a schema-backed class with an automatically populated `_tag` field. | |
| * | |
| * **When to use** | |
| * | |
| * Use to define class instances that are validated by a schema and participate | |
| * in tagged union matching. | |
| * | |
| * **Details** | |
| * | |
| * The optional `identifier` parameter overrides the schema identifier; | |
| * it defaults to the `tag` value. | |
| * | |
| * **Example** (Tagged class) | |
| * | |
| * ```ts | |
| * import { Schema } from "effect" | |
| * | |
| * class Circle extends Schema.TaggedClass<Circle>()("Circle", { | |
| * radius: Schema.Number | |
| * }) {} | |
| * | |
| * const c = new Circle({ radius: 5 }) | |
| * console.log(c._tag) // "Circle" | |
| * console.log(c.radius) // 5 | |
| * ``` | |
| * | |
| * @category constructors | |
| * @since 3.10.0 | |
| */ | |
| <Tag extends string, const Fields extends Struct.Fields>( | |
| tag: Tag, | |
| fields: Fields, | |
| annotations?: Annotations.Declaration<Self, readonly [TaggedStruct<Tag, Fields>]> | |
| ): [Self] extends [never] ? MissingSelfGeneric<"Schema.TaggedClass"> : Class<Self, TaggedStruct<Tag, Fields>, Brand> | |
| /** | |
| * Defines a schema-backed class with an automatically populated `_tag` field. | |
| * | |
| * **When to use** | |
| * | |
| * Use to define class instances that are validated by a schema and participate | |
| * in tagged union matching. | |
| * | |
| * **Details** | |
| * | |
| * The optional `identifier` parameter overrides the schema identifier; | |
| * it defaults to the `tag` value. | |
| * | |
| * **Example** (Tagged class) | |
| * | |
| * ```ts | |
| * import { Schema } from "effect" | |
| * | |
| * class Circle extends Schema.TaggedClass<Circle>()("Circle", { | |
| * radius: Schema.Number | |
| * }) {} | |
| * | |
| * const c = new Circle({ radius: 5 }) | |
| * console.log(c._tag) // "Circle" | |
| * console.log(c.radius) // 5 | |
| * ``` | |
| * | |
| * @category constructors | |
| * @since 3.10.0 | |
| */ | |
| <Tag extends string, S extends Struct<Struct.Fields>>( | |
| tag: Tag, | |
| schema: S, | |
| annotations?: Annotations.Declaration< | |
| Self, | |
| readonly [Struct<Simplify<{ readonly _tag: tag<Tag> } & S["fields"]>>] | |
| > | |
| ): [Self] extends [never] ? MissingSelfGeneric<"Schema.TaggedClass"> | |
| : Class<Self, Struct<Simplify<{ readonly _tag: tag<Tag> } & S["fields"]>>, Brand> | |
| } | |
| } = (identifier?: string) => { | |
| return ( | |
| tagValue: string, | |
| schema: Struct.Fields | Struct<Struct.Fields>, | |
| annotations?: Annotations.Declaration<any, readonly [Struct<Struct.Fields>]> | |
| ): any => { | |
| const struct = isStruct(schema) ? | |
| schema.mapFields((fields) => ({ _tag: tag(tagValue), ...fields }), { | |
| unsafePreserveChecks: true | |
| }) : | |
| TaggedStruct(tagValue, schema) | |
| return Class<any, {}>(identifier ?? tagValue)( | |
| struct, | |
| annotations as Annotations.Declaration<any, readonly [typeof struct]> | |
| ) | |
| } | |
| } | |
| /** | |
| * Creates a schema-backed error class that can be used as a typed, | |
| * yieldable error in Effect programs. Combines {@link Class} validation with | |
| * the `YieldableError` interface so instances can be yielded directly inside | |
| * `Effect.gen`. | |
| * | |
| * **Example** (Schema-backed error) | |
| * | |
| * ```ts | |
| * import { Effect, Schema } from "effect" | |
| * | |
| * class NotFound extends Schema.ErrorClass<NotFound>("NotFound")({ | |
| * id: Schema.Number | |
| * }) {} | |
| * | |
| * const program = Effect.gen(function*() { | |
| * yield* new NotFound({ id: 1 }) | |
| * }) | |
| * ``` | |
| * | |
| * @category constructors | |
| * @since 4.0.0 | |
| */ | |
| export const ErrorClass: { | |
| /** | |
| * Creates a schema-backed error class that can be used as a typed, | |
| * yieldable error in Effect programs. Combines {@link Class} validation with | |
| * the `YieldableError` interface so instances can be yielded directly inside | |
| * `Effect.gen`. | |
| * | |
| * **Example** (Schema-backed error) | |
| * | |
| * ```ts | |
| * import { Effect, Schema } from "effect" | |
| * | |
| * class NotFound extends Schema.ErrorClass<NotFound>("NotFound")({ | |
| * id: Schema.Number | |
| * }) {} | |
| * | |
| * const program = Effect.gen(function*() { | |
| * yield* new NotFound({ id: 1 }) | |
| * }) | |
| * ``` | |
| * | |
| * @category constructors | |
| * @since 4.0.0 | |
| */ | |
| <Self = never, Brand = {}>(identifier: string): { | |
| /** | |
| * Creates a schema-backed error class that can be used as a typed, | |
| * yieldable error in Effect programs. Combines {@link Class} validation with | |
| * the `YieldableError` interface so instances can be yielded directly inside | |
| * `Effect.gen`. | |
| * | |
| * **Example** (Schema-backed error) | |
| * | |
| * ```ts | |
| * import { Effect, Schema } from "effect" | |
| * | |
| * class NotFound extends Schema.ErrorClass<NotFound>("NotFound")({ | |
| * id: Schema.Number | |
| * }) {} | |
| * | |
| * const program = Effect.gen(function*() { | |
| * yield* new NotFound({ id: 1 }) | |
| * }) | |
| * ``` | |
| * | |
| * @category constructors | |
| * @since 4.0.0 | |
| */ | |
| <const Fields extends Struct.Fields>( | |
| fields: Fields, | |
| annotations?: Annotations.Declaration<Self, readonly [Struct<Fields>]> | |
| ): [Self] extends [never] ? MissingSelfGeneric<"Schema.ErrorClass"> | |
| : Class<Self, Struct<Fields>, Cause_.YieldableError & Brand> | |
| /** | |
| * Creates a schema-backed error class that can be used as a typed, | |
| * yieldable error in Effect programs. Combines {@link Class} validation with | |
| * the `YieldableError` interface so instances can be yielded directly inside | |
| * `Effect.gen`. | |
| * | |
| * **Example** (Schema-backed error) | |
| * | |
| * ```ts | |
| * import { Effect, Schema } from "effect" | |
| * | |
| * class NotFound extends Schema.ErrorClass<NotFound>("NotFound")({ | |
| * id: Schema.Number | |
| * }) {} | |
| * | |
| * const program = Effect.gen(function*() { | |
| * yield* new NotFound({ id: 1 }) | |
| * }) | |
| * ``` | |
| * | |
| * @category constructors | |
| * @since 4.0.0 | |
| */ | |
| <S extends Struct<Struct.Fields>>(schema: S, annotations?: Annotations.Declaration<Self, readonly [S]>): [Self] extends [never] ? MissingSelfGeneric<"Schema.ErrorClass"> : Class<Self, S, Cause_.YieldableError & Brand> | |
| } | |
| } = <Self, Brand = {}>(identifier: string) => | |
| ( | |
| schema: Struct.Fields | Struct<Struct.Fields>, | |
| annotations?: Annotations.Declaration<Self, readonly [Struct<Struct.Fields>]> | |
| ): [Self] extends [never] ? MissingSelfGeneric<"Schema.ErrorClass"> | |
| : Class<Self, Struct<Struct.Fields>, Cause_.YieldableError & Brand> => | |
| { | |
| const struct = isStruct(schema) ? schema : Struct(schema) | |
| const self = makeClass( | |
| core.Error, | |
| identifier, | |
| struct, | |
| annotations, | |
| (identifier) => ({ | |
| name: identifier | |
| }) | |
| ) | |
| return self | |
| } | |
| /** | |
| * Defines a schema-backed yieldable error class with an automatically populated | |
| * `_tag` field. | |
| * | |
| * **When to use** | |
| * | |
| * Use to define typed errors that are schema validated, yielded in `Effect.gen`, | |
| * and matched as tagged union members. | |
| * | |
| * **Example** (Tagged error class) | |
| * | |
| * ```ts | |
| * import { Effect, Schema } from "effect" | |
| * | |
| * class NotFound extends Schema.TaggedErrorClass<NotFound>()("NotFound", { | |
| * id: Schema.Number | |
| * }) {} | |
| * | |
| * const program = Effect.gen(function*() { | |
| * yield* new NotFound({ id: 42 }) | |
| * }) | |
| * ``` | |
| * | |
| * @category constructors | |
| * @since 3.10.0 | |
| */ | |
| export const TaggedErrorClass: { | |
| /** | |
| * Defines a schema-backed yieldable error class with an automatically populated | |
| * `_tag` field. | |
| * | |
| * **When to use** | |
| * | |
| * Use to define typed errors that are schema validated, yielded in `Effect.gen`, | |
| * and matched as tagged union members. | |
| * | |
| * **Example** (Tagged error class) | |
| * | |
| * ```ts | |
| * import { Effect, Schema } from "effect" | |
| * | |
| * class NotFound extends Schema.TaggedErrorClass<NotFound>()("NotFound", { | |
| * id: Schema.Number | |
| * }) {} | |
| * | |
| * const program = Effect.gen(function*() { | |
| * yield* new NotFound({ id: 42 }) | |
| * }) | |
| * ``` | |
| * | |
| * @category constructors | |
| * @since 3.10.0 | |
| */ | |
| <Self = never, Brand = {}>(identifier?: string): { | |
| /** | |
| * Defines a schema-backed yieldable error class with an automatically populated | |
| * `_tag` field. | |
| * | |
| * **When to use** | |
| * | |
| * Use to define typed errors that are schema validated, yielded in `Effect.gen`, | |
| * and matched as tagged union members. | |
| * | |
| * **Example** (Tagged error class) | |
| * | |
| * ```ts | |
| * import { Effect, Schema } from "effect" | |
| * | |
| * class NotFound extends Schema.TaggedErrorClass<NotFound>()("NotFound", { | |
| * id: Schema.Number | |
| * }) {} | |
| * | |
| * const program = Effect.gen(function*() { | |
| * yield* new NotFound({ id: 42 }) | |
| * }) | |
| * ``` | |
| * | |
| * @category constructors | |
| * @since 3.10.0 | |
| */ | |
| <Tag extends string, const Fields extends Struct.Fields>( | |
| tag: Tag, | |
| fields: Fields, | |
| annotations?: Annotations.Declaration<Self, readonly [TaggedStruct<Tag, Fields>]> | |
| ): [Self] extends [never] ? MissingSelfGeneric<"Schema.TaggedErrorClass"> | |
| : Class<Self, TaggedStruct<Tag, Fields>, Cause_.YieldableError & Brand> | |
| /** | |
| * Defines a schema-backed yieldable error class with an automatically populated | |
| * `_tag` field. | |
| * | |
| * **When to use** | |
| * | |
| * Use to define typed errors that are schema validated, yielded in `Effect.gen`, | |
| * and matched as tagged union members. | |
| * | |
| * **Example** (Tagged error class) | |
| * | |
| * ```ts | |
| * import { Effect, Schema } from "effect" | |
| * | |
| * class NotFound extends Schema.TaggedErrorClass<NotFound>()("NotFound", { | |
| * id: Schema.Number | |
| * }) {} | |
| * | |
| * const program = Effect.gen(function*() { | |
| * yield* new NotFound({ id: 42 }) | |
| * }) | |
| * ``` | |
| * | |
| * @category constructors | |
| * @since 3.10.0 | |
| */ | |
| <Tag extends string, S extends Struct<Struct.Fields>>( | |
| tag: Tag, | |
| schema: S, | |
| annotations?: Annotations.Declaration< | |
| Self, | |
| readonly [Struct<Simplify<{ readonly _tag: tag<Tag> } & S["fields"]>>] | |
| > | |
| ): [Self] extends [never] ? MissingSelfGeneric<"Schema.TaggedErrorClass"> | |
| : Class<Self, Struct<Simplify<{ readonly _tag: tag<Tag> } & S["fields"]>>, Cause_.YieldableError & Brand> | |
| } | |
| } = (identifier?: string) => { | |
| return ( | |
| tagValue: string, | |
| schema: Struct.Fields | Struct<Struct.Fields>, | |
| annotations?: Annotations.Declaration<any, readonly [Struct<Struct.Fields>]> | |
| ): any => { | |
| const struct = isStruct(schema) ? | |
| schema.mapFields((fields) => ({ _tag: tag(tagValue), ...fields }), { | |
| unsafePreserveChecks: true | |
| }) : | |
| TaggedStruct(tagValue, schema) | |
| return ErrorClass<any, {}>(identifier ?? tagValue)( | |
| struct, | |
| annotations as Annotations.Declaration<any, readonly [typeof struct]> | |
| ) | |
| } | |
| } | |
| // ----------------------------------------------------------------------------- | |
| // Arbitrary | |
| // ----------------------------------------------------------------------------- | |
| /** | |
| * A thunk that, given the `fast-check` module, returns an `Arbitrary<T>`. | |
| * Use this type when you need to defer instantiation of the arbitrary, for | |
| * example to support recursive schemas. | |
| * | |
| * @category Arbitrary | |
| * @since 4.0.0 | |
| */ | |
| export type LazyArbitrary<T> = (fc: typeof FastCheck) => FastCheck.Arbitrary<T> | |
| /** | |
| * Derives a {@link LazyArbitrary} from a schema. The result is memoized so | |
| * repeated calls with the same schema are cheap. | |
| * | |
| * **Details** | |
| * | |
| * Prefer {@link toArbitrary} when you need the arbitrary directly, or when you | |
| * want derivation diagnostics via `{ report: true }`. Unsupported schema | |
| * nodes, impossible constraints, invalid candidates, and recursive schemas | |
| * without a finite terminal path fail immediately. | |
| * | |
| * @category Arbitrary | |
| * @since 4.0.0 | |
| */ | |
| export function toArbitraryLazy<S extends Top>(schema: S): LazyArbitrary<S["Type"]> { | |
| const lawc = InternalArbitrary.memoized(schema.ast) | |
| return (fc) => lawc(fc, {}) | |
| } | |
| /** | |
| * Derives a `fast-check` `Arbitrary` from a schema for property-based | |
| * testing. The derived arbitrary generates values that satisfy the schema. | |
| * | |
| * **Details** | |
| * | |
| * Constraints refine base generators; candidates add weighted sources while | |
| * filters still validate every value. `{ report: true }` returns warnings such | |
| * as `OpaqueFilter`, while derivation errors remain fail-fast. Recursive | |
| * schemas use terminal branches and fail when no finite terminal path exists. | |
| * | |
| * **Example** (Generating arbitrary values) | |
| * | |
| * ```ts | |
| * import { Schema } from "effect" | |
| * import * as FastCheck from "fast-check" | |
| * | |
| * const PersonArb = Schema.toArbitrary( | |
| * Schema.Struct({ name: Schema.String, age: Schema.Number }) | |
| * ) | |
| * | |
| * // Sample a random value | |
| * const sample = FastCheck.sample(PersonArb, 1)[0] | |
| * console.log(typeof sample.name) // "string" | |
| * ``` | |
| * | |
| * @category Arbitrary | |
| * @since 4.0.0 | |
| */ | |
| export function toArbitrary<S extends Top>(schema: S): FastCheck.Arbitrary<S["Type"]> | |
| export function toArbitrary<S extends Top>( | |
| schema: S, | |
| options: { readonly report: true } | |
| ): Annotations.ToArbitrary.WithReport<FastCheck.Arbitrary<S["Type"]>> | |
| export function toArbitrary<S extends Top>( | |
| schema: S, | |
| options?: { readonly report?: boolean } | |
| ): FastCheck.Arbitrary<S["Type"]> | Annotations.ToArbitrary.WithReport<FastCheck.Arbitrary<S["Type"]>> { | |
| if (options?.report === true) { | |
| const lawc = InternalArbitrary.memoized(schema.ast) | |
| const report = InternalArbitrary.makeReport() | |
| InternalArbitrary.collectReport(schema.ast, report) | |
| return { | |
| value: lawc(FastCheck, {}), | |
| report: InternalArbitrary.toReport(report) | |
| } | |
| } | |
| return toArbitraryLazy(schema)(FastCheck) | |
| } | |
| // ----------------------------------------------------------------------------- | |
| // Formatter | |
| // ----------------------------------------------------------------------------- | |
| /** | |
| * Attaches a custom formatter used by `toFormatter`. | |
| * | |
| * **Details** | |
| * | |
| * Use this when the formatter derived from the schema structure is not suitable. | |
| * The annotation is applied through this helper because adding it directly to | |
| * `Annotations.Bottom` would make schemas invariant. | |
| * | |
| * @category Formatter | |
| * @since 4.0.0 | |
| */ | |
| export function overrideToFormatter<S extends Top>(toFormatter: () => Formatter<S["Type"]>) { | |
| return (self: S): S["Rebuild"] => { | |
| return self.annotate({ toFormatter }) | |
| } | |
| } | |
| /** | |
| * Derives a string formatter function from a schema. The formatter converts | |
| * a value to its human-readable string representation, recursing into structs, | |
| * arrays, and unions. | |
| * | |
| * **Details** | |
| * | |
| * The optional `onBefore` hook lets you intercept specific AST nodes before | |
| * the default formatting logic runs. | |
| * | |
| * @category Formatter | |
| * @since 4.0.0 | |
| */ | |
| export function toFormatter<T>(schema: Schema<T>, options?: { | |
| readonly onBefore?: | |
| | ((ast: SchemaAST.AST, recur: (ast: SchemaAST.AST) => Formatter<any>) => Formatter<any> | undefined) | |
| | undefined | |
| }): Formatter<T> { | |
| return recur(schema.ast) | |
| function recur(ast: SchemaAST.AST): Formatter<T> { | |
| // --------------------------------------------- | |
| // handle annotation | |
| // --------------------------------------------- | |
| const annotation = InternalAnnotations.resolve(ast)?.["toFormatter"] | |
| if (typeof annotation === "function") { | |
| return annotation(SchemaAST.isDeclaration(ast) ? ast.typeParameters.map(recur) : []) | |
| } | |
| // --------------------------------------------- | |
| // handle onBefore | |
| // --------------------------------------------- | |
| if (options?.onBefore) { | |
| const onBefore = options.onBefore(ast, recur) | |
| if (onBefore !== undefined) { | |
| return onBefore | |
| } | |
| } | |
| // --------------------------------------------- | |
| // handle base case | |
| // --------------------------------------------- | |
| return on(ast) | |
| } | |
| function on(ast: SchemaAST.AST): Formatter<any> { | |
| switch (ast._tag) { | |
| default: | |
| return format | |
| case "Never": | |
| return () => "never" | |
| case "Void": | |
| return () => "void" | |
| case "Arrays": { | |
| const elements = ast.elements.map(recur) | |
| const rest = ast.rest.map(recur) | |
| return (t) => { | |
| const out: Array<string> = [] | |
| let i = 0 | |
| // --------------------------------------------- | |
| // handle elements | |
| // --------------------------------------------- | |
| for (; i < elements.length; i++) { | |
| if (t.length < i + 1) { | |
| if (SchemaAST.isOptional(ast.elements[i])) { | |
| continue | |
| } | |
| } else { | |
| out.push(elements[i](t[i])) | |
| } | |
| } | |
| // --------------------------------------------- | |
| // handle rest element | |
| // --------------------------------------------- | |
| if (rest.length > 0) { | |
| const [head, ...tail] = rest | |
| for (; i < t.length - tail.length; i++) { | |
| out.push(head(t[i])) | |
| } | |
| // --------------------------------------------- | |
| // handle post rest elements | |
| // --------------------------------------------- | |
| for (let j = 0; j < tail.length; j++) { | |
| i += j | |
| out.push(tail[j](t[i])) | |
| } | |
| } | |
| return "[" + out.join(", ") + "]" | |
| } | |
| } | |
| case "Objects": { | |
| const propertySignatures = ast.propertySignatures.map((ps) => recur(ps.type)) | |
| const indexSignatures = ast.indexSignatures.map((is) => recur(is.type)) | |
| if (ast.propertySignatures.length === 0 && ast.indexSignatures.length === 0) { | |
| return format | |
| } | |
| return (t) => { | |
| const out: Array<string> = [] | |
| const visited = new Set<PropertyKey>() | |
| // --------------------------------------------- | |
| // handle property signatures | |
| // --------------------------------------------- | |
| for (let i = 0; i < propertySignatures.length; i++) { | |
| const ps = ast.propertySignatures[i] | |
| const name = ps.name | |
| visited.add(name) | |
| if (SchemaAST.isOptional(ps.type) && !Object.hasOwn(t, name)) { | |
| continue | |
| } | |
| out.push(`${formatPropertyKey(name)}: ${propertySignatures[i](t[name])}`) | |
| } | |
| // --------------------------------------------- | |
| // handle index signatures | |
| // --------------------------------------------- | |
| for (let i = 0; i < indexSignatures.length; i++) { | |
| const keys = SchemaAST.getIndexSignatureKeys(t, ast.indexSignatures[i].parameter) | |
| for (const key of keys) { | |
| if (visited.has(key)) { | |
| continue | |
| } | |
| visited.add(key) | |
| out.push(`${formatPropertyKey(key)}: ${indexSignatures[i](t[key])}`) | |
| } | |
| } | |
| return out.length > 0 ? "{ " + out.join(", ") + " }" : "{}" | |
| } | |
| } | |
| case "Union": { | |
| const getCandidates = (t: any) => SchemaAST.getCandidates(t, ast.types) | |
| return (t) => { | |
| const candidates = getCandidates(t) | |
| const refinements = candidates.map(SchemaParser._is) | |
| for (let i = 0; i < candidates.length; i++) { | |
| const is = refinements[i] | |
| if (is(t)) { | |
| return recur(candidates[i])(t) | |
| } | |
| } | |
| return format(t) | |
| } | |
| } | |
| case "Suspend": { | |
| const get = SchemaAST.memoizeThunk(() => recur(ast.thunk())) | |
| return (t) => get()(t) | |
| } | |
| } | |
| } | |
| } | |
| // ----------------------------------------------------------------------------- | |
| // Equivalence | |
| // ----------------------------------------------------------------------------- | |
| /** | |
| * Overrides the equivalence derivation for a schema by supplying a custom | |
| * `Equivalence`. | |
| * | |
| * **When to use** | |
| * | |
| * Use when you need a custom equivalence instead of the default structural | |
| * equivalence derived by {@link toEquivalence}. | |
| * | |
| * @category instances | |
| * @since 4.0.0 | |
| */ | |
| export function overrideToEquivalence<S extends Top>(toEquivalence: () => Equivalence.Equivalence<S["Type"]>) { | |
| return (self: S): S["Rebuild"] => self.annotate({ toEquivalence }) | |
| } | |
| /** | |
| * Derives an `Equivalence` from a schema. Two values are considered equal when | |
| * every field (and nested field) compares equal according to the schema | |
| * structure. | |
| * | |
| * **Example** (Struct equivalence) | |
| * | |
| * ```ts | |
| * import { Schema } from "effect" | |
| * | |
| * const eq = Schema.toEquivalence(Schema.Struct({ id: Schema.Number, name: Schema.String })) | |
| * | |
| * console.log(eq({ id: 1, name: "Alice" }, { id: 1, name: "Alice" })) // true | |
| * console.log(eq({ id: 1, name: "Alice" }, { id: 2, name: "Alice" })) // false | |
| * ``` | |
| * | |
| * @category instances | |
| * @since 4.0.0 | |
| */ | |
| export function toEquivalence<T>(schema: Schema<T>): Equivalence.Equivalence<T> { | |
| return InternalEquivalence.toEquivalence(schema.ast) | |
| } | |
| // ----------------------------------------------------------------------------- | |
| // Representation | |
| // ----------------------------------------------------------------------------- | |
| /** | |
| * Derives an intermediate `SchemaRepresentation.Document` from a schema. This | |
| * document is used internally by {@link toJsonSchemaDocument} and related | |
| * functions to produce JSON Schema output. | |
| * | |
| * @category Representation | |
| * @since 4.0.0 | |
| */ | |
| export function toRepresentation(schema: Top): SchemaRepresentation.Document { | |
| return InternalStandard.fromAST(schema.ast) | |
| } | |
| // ----------------------------------------------------------------------------- | |
| // JsonSchema | |
| // ----------------------------------------------------------------------------- | |
| /** | |
| * Options for {@link toJsonSchemaDocument}. | |
| * | |
| * @category options | |
| * @since 4.0.0 | |
| */ | |
| export interface ToJsonSchemaOptions { | |
| /** | |
| * Controls how additional properties are handled while resolving the JSON | |
| * schema. | |
| * | |
| * **Details** | |
| * | |
| * Possible values include: | |
| * - `false`: Disallow additional properties (default) | |
| * - `true`: Allow additional properties | |
| * - `JsonSchema`: Use the provided JSON Schema for additional properties | |
| */ | |
| readonly additionalProperties?: boolean | JsonSchema.JsonSchema | undefined | |
| /** | |
| * Controls whether to generate descriptions for checks (if the user has not | |
| * provided them) based on the `expected` annotation of the check. | |
| */ | |
| readonly generateDescriptions?: boolean | undefined | |
| /** | |
| * A predicate that controls which additional annotation keys (beyond the | |
| * standard JSON Schema keys) are included in the generated output. | |
| * | |
| * **When to use** | |
| * | |
| * Use when you need to include non-standard annotation keys in the generated | |
| * JSON Schema, such as Monaco Editor properties (`markdownDescription`, | |
| * `defaultSnippets`) or vendor extensions (`x-*`). | |
| * | |
| * **Details** | |
| * | |
| * Standard JSON Schema keys (`title`, `description`, `default`, `examples`, | |
| * `readOnly`, `writeOnly`, `format`, `contentEncoding`, `contentMediaType`, | |
| * `contentSchema`) are always included. This predicate is checked for any | |
| * *other* annotation key. | |
| * | |
| * **Gotchas** | |
| * | |
| * Prefer whitelisting the custom annotation keys you want to emit instead of | |
| * using a broad predicate such as `() => true`, because broad predicates can | |
| * include Effect-specific annotations that are preserved for internal schema | |
| * generation. | |
| * | |
| * **Example** (Including custom annotations) | |
| * | |
| * ```ts | |
| * import { Schema } from "effect" | |
| * | |
| * const schema = Schema.String.annotate({ | |
| * description: "A name", | |
| * markdownDescription: "The **name** field" | |
| * }) | |
| * | |
| * const doc = Schema.toJsonSchemaDocument(schema, { | |
| * includeAnnotationKey: (key) => | |
| * key === "markdownDescription" || key.startsWith("x-") | |
| * }) | |
| * | |
| * console.log(doc.schema) | |
| * // { | |
| * // type: "string", | |
| * // description: "A name", | |
| * // markdownDescription: "The **name** field" | |
| * // } | |
| * ``` | |
| */ | |
| readonly includeAnnotationKey?: ((key: string) => boolean) | undefined | |
| } | |
| /** | |
| * Returns a JSON Schema document using draft 2020-12. | |
| * | |
| * **Details** | |
| * | |
| * The `options` parameter controls generation details such as additional | |
| * properties and synthesized check descriptions; it does not change the draft | |
| * target. | |
| * | |
| * **Gotchas** | |
| * | |
| * JSON Schema generation is best-effort. Some Effect schema semantics cannot | |
| * be represented exactly in JSON Schema, and importing an emitted JSON Schema | |
| * may produce an equivalent approximation rather than the original schema | |
| * shape. | |
| * | |
| * @category converting | |
| * @since 4.0.0 | |
| */ | |
| export function toJsonSchemaDocument(schema: Top, options?: ToJsonSchemaOptions): JsonSchema.Document<"draft-2020-12"> { | |
| const sd = toRepresentation(schema) | |
| const jd = InternalStandard.toJsonSchemaDocument(sd, options) | |
| return { | |
| dialect: "draft-2020-12", | |
| schema: jd.schema, | |
| definitions: jd.definitions | |
| } | |
| } | |
| // ----------------------------------------------------------------------------- | |
| // Canonical Codecs | |
| // ----------------------------------------------------------------------------- | |
| /** | |
| * Derives a canonical JSON codec from a schema. The encoded form is `Json`, and | |
| * decoding produces the schema's `Type`. | |
| * | |
| * @category Canonical Codecs | |
| * @since 4.0.0 | |
| */ | |
| export function toCodecJson<T, E, RD, RE>(schema: Codec<T, E, RD, RE>): Codec<T, Json, RD, RE> { | |
| return make(toCodecJsonTop(schema.ast)) | |
| } | |
| const toCodecJsonTop = SchemaAST.toCodec((ast) => { | |
| const out = toCodecJsonBase(ast, toCodecJsonTop) | |
| return out !== ast && SchemaAST.isOptional(ast) ? SchemaAST.optionalKeyLastLink(out) : out | |
| }) | |
| function toCodecJsonBase(ast: SchemaAST.AST, recur: (ast: SchemaAST.AST) => SchemaAST.AST): SchemaAST.AST { | |
| switch (ast._tag) { | |
| case "Declaration": { | |
| const getLink = ast.annotations?.toCodecJson ?? ast.annotations?.toCodec | |
| if (Predicate.isFunction(getLink)) { | |
| const tps = SchemaAST.isDeclaration(ast) | |
| ? ast.typeParameters.map((tp) => InternalSchema.make(SchemaAST.toEncoded(tp))) | |
| : [] | |
| const link = getLink(tps) | |
| const to = recur(link.to) | |
| return SchemaAST.replaceEncoding(ast, to === link.to ? [link] : [new SchemaAST.Link(to, link.transformation)]) | |
| } | |
| return SchemaAST.replaceEncoding(ast, [SchemaAST.unknownToNull]) | |
| } | |
| case "Unknown": | |
| case "ObjectKeyword": | |
| return SchemaAST.replaceEncoding(ast, [SchemaAST.unknownToJson]) | |
| case "Undefined": | |
| case "Void": | |
| case "Literal": | |
| case "Number": | |
| return ast.toCodecJson() | |
| case "UniqueSymbol": | |
| case "Symbol": | |
| case "BigInt": | |
| return ast.toCodecStringTree() | |
| case "Objects": { | |
| if (ast.propertySignatures.some((ps) => typeof ps.name !== "string")) { | |
| throw new globalThis.Error("Objects property names must be strings", { cause: ast }) | |
| } | |
| return ast.recur(recur) | |
| } | |
| case "Union": { | |
| const sortedTypes = InternalSchema.jsonReorder(ast.types) | |
| if (sortedTypes !== ast.types) { | |
| return new SchemaAST.Union( | |
| sortedTypes, | |
| ast.mode, | |
| ast.annotations, | |
| ast.checks, | |
| ast.encoding, | |
| ast.context, | |
| ast.encodingChecks | |
| ).recur(recur) | |
| } | |
| return ast.recur(recur) | |
| } | |
| case "Arrays": | |
| case "Suspend": | |
| return ast.recur(recur) | |
| } | |
| // `Schema.Any` is used as an escape hatch | |
| return ast | |
| } | |
| /** | |
| * Derives an isomorphism codec from a schema. The encoded form is the | |
| * schema's `Iso` type — the intermediate representation used for round-tripping. | |
| * | |
| * @category Canonical Codecs | |
| * @since 4.0.0 | |
| */ | |
| export function toCodecIso<S extends Top>(schema: S): Codec<S["Type"], S["Iso"]> { | |
| return make(toCodecIsoTop(SchemaAST.toType(schema.ast))) | |
| } | |
| const toCodecIsoTop = memoize((ast: SchemaAST.AST): SchemaAST.AST => { | |
| const out = toCodecIsoBase(ast, toCodecIsoTop) | |
| return out !== ast && SchemaAST.isOptional(ast) ? SchemaAST.optionalKeyLastLink(out) : out | |
| }) | |
| function toCodecIsoBase(ast: SchemaAST.AST, recur: (ast: SchemaAST.AST) => SchemaAST.AST): SchemaAST.AST { | |
| switch (ast._tag) { | |
| case "Declaration": { | |
| const getLink = ast.annotations?.toCodecIso ?? ast.annotations?.toCodec | |
| if (Predicate.isFunction(getLink)) { | |
| const link = getLink(ast.typeParameters.map((tp) => InternalSchema.make(tp))) | |
| const to = recur(link.to) | |
| return SchemaAST.replaceEncoding(ast, to === link.to ? [link] : [new SchemaAST.Link(to, link.transformation)]) | |
| } | |
| return ast | |
| } | |
| case "Arrays": | |
| case "Objects": | |
| case "Union": | |
| case "Suspend": | |
| return ast.recur(recur) | |
| } | |
| return ast | |
| } | |
| /** | |
| * A {@link Tree} of `string | undefined` nodes. Leaf values are either a | |
| * string representation or `undefined` for opaque/declaration types. | |
| * | |
| * @category Canonical Codecs | |
| * @since 4.0.0 | |
| */ | |
| export type StringTree = Tree<string | undefined> | |
| /** | |
| * Converts a schema to the StringTree canonical codec, where every leaf value | |
| * becomes a string while preserving the original structure. | |
| * | |
| * **Details** | |
| * | |
| * Declarations are converted to `undefined` (unless they have a | |
| * `toCodecJson` or `toCodec` annotation). | |
| * | |
| * Options: | |
| * | |
| * - `keepDeclarations`: if `true`, it **does not** convert declarations to | |
| * `undefined` but instead keeps them as they are (unless they have a | |
| * `toCodecJson` or `toCodec` annotation). | |
| * | |
| * Defaults to `false`. | |
| * | |
| * @category Canonical Codecs | |
| * @since 4.0.0 | |
| */ | |
| export function toCodecStringTree<T, E, RD, RE>(schema: Codec<T, E, RD, RE>): Codec<T, StringTree, RD, RE> | |
| export function toCodecStringTree<T, E, RD, RE>( | |
| schema: Codec<T, E, RD, RE>, | |
| options: { readonly keepDeclarations: true } // Used in FormData | |
| ): Codec<T, unknown, RD, RE> | |
| export function toCodecStringTree<T, E, RD, RE>( | |
| schema: Codec<T, E, RD, RE>, | |
| options?: { readonly keepDeclarations?: boolean | undefined } | |
| ): Codec<T, unknown, RD, RE> { | |
| return make( | |
| toCodecEnsureArray( | |
| options?.keepDeclarations === true | |
| ? serializerStringTreeKeepDeclarations(schema.ast) | |
| : serializerStringTree(schema.ast) | |
| ) | |
| ) | |
| } | |
| type XmlEncoderOptions = { | |
| /** Root element name for the returned XML string. Default: "root" */ | |
| readonly rootName?: string | undefined | |
| /** When an array doesn't have a natural item name, use this. Default: "item" */ | |
| readonly arrayItemName?: string | undefined | |
| /** Pretty-print output. Default: true */ | |
| readonly pretty?: boolean | undefined | |
| /** Indentation used when pretty-printing. Default: " " (two spaces) */ | |
| readonly indent?: string | undefined | |
| /** Sort object keys for stable output. Default: true */ | |
| readonly sortKeys?: boolean | undefined | |
| } | |
| /** | |
| * Derives an XML encoder from a codec. | |
| * | |
| * **Details** | |
| * | |
| * The returned function encodes a value through `toCodecStringTree` and returns | |
| * an `Effect` that succeeds with the XML string or fails with `SchemaError` if | |
| * codec encoding fails. | |
| * | |
| * @category Canonical Codecs | |
| * @since 4.0.0 | |
| */ | |
| export function toEncoderXml<T, E, RD, RE>( | |
| codec: Codec<T, E, RD, RE>, | |
| options?: XmlEncoderOptions | |
| ) { | |
| const rootName = InternalAnnotations.resolveIdentifier(codec.ast) ?? InternalAnnotations.resolveTitle(codec.ast) | |
| const serialize = encodeEffect(toCodecStringTree(codec)) | |
| return (t: T): Effect.Effect<string, SchemaError, RE> => | |
| serialize(t).pipe(Effect.map((stringTree) => stringTreeToXml(stringTree, { rootName, ...options }))) | |
| } | |
| function stringTreeToXml(value: StringTree, options: XmlEncoderOptions): string { | |
| const rootName = options.rootName ?? "root" | |
| const arrayItemName = options.arrayItemName ?? "item" | |
| const pretty = options.pretty ?? true | |
| const indent = options.indent ?? " " | |
| const sortKeys = options.sortKeys ?? true | |
| const seen = new Set<object>() | |
| const lines: Array<string> = [] | |
| recur(rootName, value, 0) | |
| return lines.join(pretty ? "\n" : "") | |
| function push(depth: number, text: string): void { | |
| lines.push(pretty ? indent.repeat(depth) + text : text) | |
| } | |
| function recur(tagName: string, node: StringTree, depth: number, originalNameForMeta?: string): void { | |
| const { attrs, safe } = xml.tagInfo(tagName, originalNameForMeta) | |
| if (node === undefined) { | |
| push(depth, `<${safe}${attrs}/>`) | |
| } else if (typeof node === "string") { | |
| push(depth, `<${safe}${attrs}>${xml.escapeText(node)}</${safe}>`) | |
| } else if (typeof node !== "object" || node === null) { | |
| push(depth, `<${safe}${attrs}>${xml.escapeText(format(node))}</${safe}>`) | |
| } else { | |
| if (seen.has(node)) throw new globalThis.Error("Cycle detected while serializing to XML.", { cause: node }) | |
| seen.add(node) | |
| try { | |
| if (globalThis.globalThis.Array.isArray(node)) { | |
| if (node.length === 0) { | |
| push(depth, `<${safe}${attrs}/>`) | |
| return | |
| } | |
| push(depth, `<${safe}${attrs}>`) | |
| for (const item of node) recur(arrayItemName, item, depth + 1) | |
| push(depth, `</${safe}>`) | |
| return | |
| } | |
| const obj = node as Record<string, StringTree> | |
| const keys = Object.keys(obj) | |
| if (sortKeys) keys.sort() | |
| if (keys.length === 0) { | |
| push(depth, `<${safe}${attrs}/>`) | |
| return | |
| } | |
| push(depth, `<${safe}${attrs}>`) | |
| for (const k of keys) { | |
| recur(xml.parseTagName(k).safe, obj[k], depth + 1, k) | |
| } | |
| push(depth, `</${safe}>`) | |
| } finally { | |
| seen.delete(node) | |
| } | |
| } | |
| } | |
| } | |
| const xml = { | |
| escapeText(s: string): string { | |
| return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">"); | |
| }, | |
| escapeAttribute(s: string): string { | |
| return s.replace(/&/g, "&").replace(/"/g, """).replace(/</g, "<").replace(/>/g, ">"); | |
| }, | |
| parseTagName(name: string): { safe: string; changed: boolean } { | |
| const original = name | |
| let safe = name | |
| if (!/^[A-Za-z_]/.test(safe)) safe = "_" + safe | |
| safe = safe.replace(/[^A-Za-z0-9._-]/g, "_") | |
| if (/^xml/i.test(safe)) safe = "_" + safe | |
| return { safe, changed: safe !== original } | |
| }, | |
| tagInfo(name: string, original?: string): { safe: string; attrs: string } { | |
| const { changed, safe } = xml.parseTagName(name) | |
| const needsMeta = changed || (original && original !== name) | |
| const attrs = needsMeta ? ` data-name="${xml.escapeAttribute(original ?? name)}"` : "" | |
| return { safe, attrs } | |
| } | |
| } | |
| function getStringTreePriority(ast: SchemaAST.AST): number { | |
| switch (ast._tag) { | |
| case "Null": | |
| case "Boolean": | |
| case "Number": | |
| case "BigInt": | |
| case "Symbol": | |
| case "UniqueSymbol": | |
| return 0 | |
| default: | |
| return 1 | |
| } | |
| } | |
| const treeReorder = InternalSchema.makeReorder(getStringTreePriority) | |
| function serializerTree( | |
| ast: SchemaAST.AST, | |
| recur: (ast: SchemaAST.AST) => SchemaAST.AST, | |
| onMissingAnnotation: (ast: SchemaAST.AST) => SchemaAST.AST | |
| ): SchemaAST.AST { | |
| switch (ast._tag) { | |
| case "Declaration": { | |
| const getLink = ast.annotations?.toCodecJson ?? ast.annotations?.toCodec | |
| if (Predicate.isFunction(getLink)) { | |
| const tps = SchemaAST.isDeclaration(ast) | |
| ? ast.typeParameters.map((tp) => make(recur(SchemaAST.toEncoded(tp)))) | |
| : [] | |
| const link = getLink(tps) | |
| const to = recur(link.to) | |
| return SchemaAST.replaceEncoding(ast, to === link.to ? [link] : [new SchemaAST.Link(to, link.transformation)]) | |
| } | |
| return onMissingAnnotation(ast) | |
| } | |
| case "Null": | |
| return SchemaAST.replaceEncoding(ast, [nullToString]) | |
| case "Boolean": | |
| return SchemaAST.replaceEncoding(ast, [booleanToString]) | |
| case "Unknown": | |
| case "ObjectKeyword": | |
| return SchemaAST.replaceEncoding(ast, [SchemaAST.unknownToStringTree]) | |
| case "Enum": | |
| case "Number": | |
| case "Literal": | |
| case "UniqueSymbol": | |
| case "Symbol": | |
| case "BigInt": | |
| return ast.toCodecStringTree() | |
| case "Objects": { | |
| if (ast.propertySignatures.some((ps) => typeof ps.name !== "string")) { | |
| throw new globalThis.Error("Objects property names must be strings", { cause: ast }) | |
| } | |
| return ast.recur(recur) | |
| } | |
| case "Union": { | |
| const sortedTypes = treeReorder(ast.types) | |
| if (sortedTypes !== ast.types) { | |
| return new SchemaAST.Union( | |
| sortedTypes, | |
| ast.mode, | |
| ast.annotations, | |
| ast.checks, | |
| ast.encoding, | |
| ast.context, | |
| ast.encodingChecks | |
| ).recur(recur) | |
| } | |
| return ast.recur(recur) | |
| } | |
| case "Arrays": | |
| case "Suspend": | |
| return ast.recur(recur) | |
| } | |
| // `Schema.Any` is used as an escape hatch | |
| return ast | |
| } | |
| const nullToString = new SchemaAST.Link( | |
| new SchemaAST.Literal("null"), | |
| new SchemaTransformation.Transformation( | |
| SchemaGetter.transform(() => null), | |
| SchemaGetter.transform(() => "null") | |
| ) | |
| ) | |
| const booleanToString = new SchemaAST.Link( | |
| new SchemaAST.Union([new SchemaAST.Literal("true"), new SchemaAST.Literal("false")], "anyOf"), | |
| new SchemaTransformation.Transformation( | |
| SchemaGetter.transform((s) => s === "true"), | |
| SchemaGetter.String() | |
| ) | |
| ) | |
| const serializerStringTree = SchemaAST.toCodec((ast) => { | |
| const out = serializerTree(ast, serializerStringTree, (ast) => SchemaAST.replaceEncoding(ast, [unknownToUndefined])) | |
| if (out !== ast && SchemaAST.isOptional(ast)) { | |
| return SchemaAST.optionalKeyLastLink(out) | |
| } | |
| return out | |
| }) | |
| const unknownToUndefined = new SchemaAST.Link( | |
| SchemaAST.undefined, | |
| new SchemaTransformation.Transformation( | |
| SchemaGetter.passthrough(), | |
| SchemaGetter.transform(() => undefined) | |
| ) | |
| ) | |
| const serializerStringTreeKeepDeclarations = SchemaAST.toCodec((ast) => { | |
| const out = serializerTree(ast, serializerStringTreeKeepDeclarations, identity) | |
| if (out !== ast && SchemaAST.isOptional(ast)) { | |
| return SchemaAST.optionalKeyLastLink(out) | |
| } | |
| return out | |
| }) | |
| const SERIALIZER_ENSURE_ARRAY = "~effect/Schema/SERIALIZER_ENSURE_ARRAY" | |
| const toCodecEnsureArray = SchemaAST.toCodec((ast) => { | |
| if (SchemaAST.isUnion(ast) && ast.annotations?.[SERIALIZER_ENSURE_ARRAY]) { | |
| return ast | |
| } | |
| const out = onSerializerEnsureArray(ast) | |
| if (SchemaAST.isArrays(out)) { | |
| const ensure = new SchemaAST.Union( | |
| [ | |
| out, | |
| SchemaAST.decodeTo( | |
| SchemaAST.string, | |
| out, | |
| new SchemaTransformation.Transformation( | |
| SchemaGetter.split(), | |
| SchemaGetter.passthrough() | |
| ) | |
| ) | |
| ], | |
| "anyOf", | |
| { [SERIALIZER_ENSURE_ARRAY]: true } | |
| ) | |
| return SchemaAST.isOptional(ast) ? SchemaAST.optionalKey(ensure) : ensure | |
| } | |
| return out | |
| }) | |
| function onSerializerEnsureArray(ast: SchemaAST.AST): SchemaAST.AST { | |
| switch (ast._tag) { | |
| default: | |
| return ast | |
| case "Declaration": | |
| case "Arrays": | |
| case "Objects": | |
| case "Union": | |
| case "Suspend": | |
| return ast.recur(toCodecEnsureArray) | |
| } | |
| } | |
| // ----------------------------------------------------------------------------- | |
| // Optic APIs | |
| // ----------------------------------------------------------------------------- | |
| /** | |
| * Derives an `Iso` optic from a schema that isomorphically converts between | |
| * the schema's `Type` and its `Iso` (intermediate / serialized form). | |
| * | |
| * @category Optic | |
| * @since 4.0.0 | |
| */ | |
| export function toIso<S extends Top>(schema: S): Optic_.Iso<S["Type"], S["Iso"]> { | |
| const serializer = toCodecIso(schema) | |
| return Optic_.makeIso(SchemaParser.encodeSync(serializer), SchemaParser.decodeSync(serializer)) | |
| } | |
| /** | |
| * Returns an identity `Iso` over the schema's source (`Type`) side. | |
| * | |
| * @category Optic | |
| * @since 4.0.0 | |
| */ | |
| export function toIsoSource<S extends Top>(_: S): Optic_.Iso<S["Type"], S["Type"]> { | |
| return Optic_.id() | |
| } | |
| /** | |
| * Returns an identity `Iso` over the schema's focus (`Iso`) side. | |
| * | |
| * @category Optic | |
| * @since 4.0.0 | |
| */ | |
| export function toIsoFocus<S extends Top>(_: S): Optic_.Iso<S["Iso"], S["Iso"]> { | |
| return Optic_.id() | |
| } | |
| /** | |
| * Type-level representation returned by {@link overrideToCodecIso}. | |
| * | |
| * @category Optic | |
| * @since 4.0.0 | |
| */ | |
| export interface overrideToCodecIso<S extends Top, Iso> extends | |
| Bottom< | |
| S["Type"], | |
| S["Encoded"], | |
| S["DecodingServices"], | |
| S["EncodingServices"], | |
| S["ast"], | |
| overrideToCodecIso<S, Iso>, | |
| S["~type.make.in"], | |
| Iso, | |
| S["~type.parameters"], | |
| S["~type.make"], | |
| S["~type.mutability"], | |
| S["~type.optionality"], | |
| S["~type.constructor.default"], | |
| S["~encoded.mutability"], | |
| S["~encoded.optionality"] | |
| > | |
| { | |
| readonly schema: S | |
| } | |
| /** | |
| * Overrides a schema's derived ISO codec with an explicit target codec. | |
| * | |
| * **When to use** | |
| * | |
| * Use to provide a custom ISO transformation when the default derivation is not | |
| * appropriate. | |
| * | |
| * **Details** | |
| * | |
| * The resulting schema carries a custom `Iso` type parameter and uses the | |
| * provided `decode` and `encode` getters to transform between the schema type | |
| * and the target codec. | |
| * | |
| * @category Optic | |
| * @since 4.0.0 | |
| */ | |
| export function overrideToCodecIso<S extends Top, Iso>( | |
| to: Codec<Iso>, | |
| transformation: { | |
| readonly decode: SchemaGetter.Getter<S["Type"], Iso> | |
| readonly encode: SchemaGetter.Getter<Iso, S["Type"]> | |
| } | |
| ) { | |
| return (schema: S): overrideToCodecIso<S, Iso> => { | |
| return make( | |
| SchemaAST.annotate(schema.ast, { | |
| toCodecIso: () => new SchemaAST.Link(to.ast, SchemaTransformation.make(transformation)) | |
| }), | |
| { schema } | |
| ) | |
| } | |
| } | |
| // ----------------------------------------------------------------------------- | |
| // Differ APIs | |
| // ----------------------------------------------------------------------------- | |
| /** | |
| * Derives a JSON Patch differ from a codec. Serializes values to JSON (via | |
| * {@link toCodecJson}), computes RFC 6902 JSON Patch operations between old | |
| * and new values, and can apply patches back to the typed value. | |
| * | |
| * @category converting | |
| * @since 4.0.0 | |
| */ | |
| export function toDifferJsonPatch<T, E>(schema: Codec<T, E>): Differ<T, JsonPatch.JsonPatch> { | |
| const serializer = toCodecJson(schema) | |
| const get = SchemaParser.encodeSync(serializer) | |
| const set = SchemaParser.decodeSync(serializer) | |
| return { | |
| empty: [], | |
| diff: (oldValue, newValue) => JsonPatch.get(get(oldValue), get(newValue)), | |
| combine: (first, second) => [...first, ...second], | |
| patch: (oldValue, patch) => { | |
| const value = get(oldValue) | |
| const patched = JsonPatch.apply(patch, value) | |
| return Object.is(patched, value) ? oldValue : set(patched) | |
| } | |
| } | |
| } | |
| /** | |
| * Recursive tree type whose leaves are `Node` values and whose branches are | |
| * readonly arrays or string-keyed records of child trees. | |
| * | |
| * @category Tree | |
| * @since 4.0.0 | |
| */ | |
| export type Tree<Node> = Node | TreeRecord<Node> | ReadonlyArray<Tree<Node>> | |
| /** | |
| * A record node in a {@link Tree}: an object mapping string keys to child | |
| * `Tree` nodes. | |
| * | |
| * @category Tree | |
| * @since 4.0.0 | |
| */ | |
| export interface TreeRecord<A> { | |
| readonly [x: string]: Tree<A> | |
| } | |
| /** | |
| * Creates a recursive schema for a {@link Tree} of values described by `node`. | |
| * The resulting schema accepts a single node value, an array of trees, or an | |
| * object whose values are trees. | |
| * | |
| * @category Tree | |
| * @since 4.0.0 | |
| */ | |
| export function Tree<S extends Top>(node: S) { | |
| const Tree$ref = suspend((): Codec< | |
| Tree<S["Type"]>, | |
| Tree<S["Encoded"]>, | |
| S["DecodingServices"], | |
| S["EncodingServices"] | |
| > => Tree) | |
| const Tree = Union([ | |
| node, | |
| ArraySchema(Tree$ref), | |
| Record(String, Tree$ref) | |
| ]) | |
| return Tree | |
| } | |
| /** | |
| * Recursive TypeScript type for any valid immutable JSON value: `null`, | |
| * `number`, `boolean`, `string`, a readonly array of `Json` values, or a | |
| * readonly record of `string → Json`. For the corresponding schema, see the | |
| * {@link Json} const. | |
| * | |
| * @category models | |
| * @since 4.0.0 | |
| */ | |
| export type Json = null | number | boolean | string | JsonArray | JsonObject | |
| /** | |
| * A readonly array of {@link Json} values. | |
| * | |
| * @category models | |
| * @since 4.0.0 | |
| */ | |
| export interface JsonArray extends ReadonlyArray<Json> {} | |
| /** | |
| * A readonly record whose values are {@link Json} values. | |
| * | |
| * @category models | |
| * @since 4.0.0 | |
| */ | |
| export interface JsonObject { | |
| readonly [x: string]: Json | |
| } | |
| /** | |
| * Schema that accepts and validates any immutable JSON-compatible value. | |
| * | |
| * **Example** (Validating a JSON value) | |
| * | |
| * ```ts | |
| * import { Schema } from "effect" | |
| * | |
| * const result = Schema.decodeUnknownOption(Schema.Json)({ key: [1, true, null] }) | |
| * console.log(result._tag) // "Some" | |
| * ``` | |
| * | |
| * @category schemas | |
| * @since 4.0.0 | |
| */ | |
| export const Json: Codec<Json> = make(SchemaAST.Json) | |
| const JsonError = Struct({ | |
| message: String, | |
| name: optionalKey(String), | |
| stack: optionalKey(String), | |
| cause: optionalKey(Json) | |
| }) | |
| /** | |
| * Recursive TypeScript type for mutable JSON values: `null`, `number`, | |
| * `boolean`, `string`, mutable arrays, or mutable string-keyed records. | |
| * | |
| * @category models | |
| * @since 4.0.0 | |
| */ | |
| export type MutableJson = null | number | boolean | string | MutableJsonArray | MutableJsonObject | |
| /** | |
| * A mutable array of {@link MutableJson} values. | |
| * | |
| * @category models | |
| * @since 4.0.0 | |
| */ | |
| export interface MutableJsonArray extends Array<MutableJson> {} | |
| /** | |
| * A mutable record whose values are {@link MutableJson} values. | |
| * | |
| * @category models | |
| * @since 4.0.0 | |
| */ | |
| export interface MutableJsonObject { | |
| [x: string]: MutableJson | |
| } | |
| /** | |
| * Schema that accepts any mutable JSON-compatible value. See {@link Json} for | |
| * the immutable variant. | |
| * | |
| * @category schemas | |
| * @since 4.0.0 | |
| */ | |
| export const MutableJson: Codec<MutableJson> = make(SchemaAST.MutableJson) | |
| // ----------------------------------------------------------------------------- | |
| // Annotations | |
| // ----------------------------------------------------------------------------- | |
| /** | |
| * Resolves the typed annotations from a schema. The term "resolve" (rather | |
| * than "get") reflects the lookup strategy: if the schema has checks, the | |
| * annotations are taken from the last check; otherwise they are taken from | |
| * the base schema instance. | |
| * | |
| * @category Schema Resolvers | |
| * @since 4.0.0 | |
| */ | |
| export function resolveAnnotations<S extends Top>( | |
| schema: S | |
| ): Annotations.Bottom<S["Type"], S["~type.parameters"]> | undefined { | |
| return InternalAnnotations.resolve(schema.ast) | |
| } | |
| /** | |
| * Resolves the context (key-level) annotations from a schema. Context | |
| * annotations are those attached via `annotateKey` and live on the AST's | |
| * `context` rather than on the schema node itself. | |
| * | |
| * @category Schema Resolvers | |
| * @since 4.0.0 | |
| */ | |
| export function resolveAnnotationsKey<S extends Top>(schema: S): Annotations.Key<S["Type"]> | undefined { | |
| return schema.ast.context?.annotations | |
| } | |
| /** | |
| * The `Annotations` namespace groups all annotation interfaces used to attach | |
| * metadata to schemas. Annotations control documentation, validation messages, | |
| * JSON Schema generation, equivalence, arbitrary generation, and more. | |
| * | |
| * **Details** | |
| * | |
| * Use {@link resolveAnnotations} to read the annotations attached to a schema at | |
| * runtime. | |
| * | |
| * @since 4.0.0 | |
| */ | |
| export declare namespace Annotations { | |
| /** | |
| * This interface is used to define the annotations that can be attached to a | |
| * schema. You can extend this interface to define your own annotations. | |
| * | |
| * **Details** | |
| * | |
| * Note that both a missing key or `undefined` is used to indicate that the | |
| * annotation is not present. | |
| * | |
| * This means that can remove any annotation by setting it to `undefined`. | |
| * | |
| * **Example** (Defining your own annotations) | |
| * | |
| * ```ts | |
| * import { Schema } from "effect" | |
| * | |
| * // Extend the Annotations interface with a custom `version` annotation | |
| * declare module "effect/Schema" { | |
| * namespace Annotations { | |
| * interface Annotations { | |
| * readonly version?: | |
| * | readonly [major: number, minor: number, patch: number] | |
| * | undefined | |
| * } | |
| * } | |
| * } | |
| * | |
| * // The `version` annotation is now recognized by the TypeScript compiler | |
| * const schema = Schema.String.annotate({ version: [1, 2, 0] }) | |
| * | |
| * // const version: readonly [major: number, minor: number, patch: number] | undefined | |
| * const version = Schema.resolveAnnotations(schema)?.["version"] | |
| * | |
| * if (version) { | |
| * // Access individual parts of the version | |
| * console.log(version[1]) | |
| * // Output: 2 | |
| * } | |
| * ``` | |
| * | |
| * @category models | |
| * @since 4.0.0 | |
| */ | |
| export interface Annotations { | |
| readonly [x: string]: unknown | |
| } | |
| /** | |
| * Annotations shared by all schema nodes. These map to common JSON Schema / | |
| * OpenAPI fields: `title`, `description`, `format`, etc. | |
| * | |
| * @category models | |
| * @since 4.0.0 | |
| */ | |
| export interface Augment extends Annotations { | |
| /** | |
| * Human-readable description of what a value is expected to satisfy. | |
| * | |
| * **Details** | |
| * | |
| * For filter and refinement failures, the default formatter uses | |
| * `message` first, then `expected`, and finally falls back to `<filter>`. | |
| * | |
| * Use this to name a failed filter in the default message: | |
| * `Expected <expected>, got <actual>`. | |
| */ | |
| readonly expected?: string | undefined | |
| readonly title?: string | undefined | |
| readonly description?: string | undefined | |
| readonly documentation?: string | undefined | |
| readonly readOnly?: boolean | undefined | |
| readonly writeOnly?: boolean | undefined | |
| readonly format?: string | undefined | |
| readonly contentEncoding?: string | undefined | |
| readonly contentMediaType?: string | undefined | |
| } | |
| /** | |
| * Extends {@link Augment} with type-parametric `default` and `examples` fields. | |
| * | |
| * @category models | |
| * @since 4.0.0 | |
| */ | |
| export interface Documentation<T> extends Augment { | |
| readonly default?: T | undefined | |
| readonly examples?: ReadonlyArray<T> | undefined | |
| } | |
| /** | |
| * Annotations for struct property schemas. Extends {@link Documentation} | |
| * with an optional `messageMissingKey` to override the error message when | |
| * the property key is absent during decoding. | |
| * | |
| * @category models | |
| * @since 4.0.0 | |
| */ | |
| export interface Key<T> extends Documentation<T> { | |
| /** | |
| * The message to use when a key is missing. | |
| */ | |
| readonly messageMissingKey?: string | undefined | |
| } | |
| /** | |
| * Base annotations shared by all composite schema nodes. Extends | |
| * {@link Documentation} with error messages, branding, parse options, and | |
| * arbitrary generation hooks. {@link Declaration} and other annotation | |
| * interfaces build on top of this. | |
| * | |
| * @category models | |
| * @since 4.0.0 | |
| */ | |
| export interface Bottom<T, TypeParameters extends ReadonlyArray<Top>> extends Documentation<T> { | |
| /** | |
| * Complete message to use when this schema node reports an issue. | |
| * | |
| * **Details** | |
| * | |
| * This replaces the default message for matching issue types instead of | |
| * only changing the expected label. For a filter or refinement failure, | |
| * annotate the filter with `message` to replace the whole filter failure | |
| * message, or `expected` to keep the default | |
| * `Expected <expected>, got <actual>` shape. | |
| */ | |
| readonly message?: string | undefined | |
| /** | |
| * The message to use when a key is unexpected. | |
| */ | |
| readonly messageUnexpectedKey?: string | undefined | |
| /** | |
| * Stable identifier for this schema node. | |
| * | |
| * **Details** | |
| * | |
| * Identifiers are used by schema tooling, including JSON Schema | |
| * generation, to name references. The default formatter also uses | |
| * `identifier` as the expected label for type-level failures, such as | |
| * `Expected UserId, got null`. | |
| * | |
| * `identifier` does not name a failed filter or refinement. If the base | |
| * type matches and a filter fails, put `expected` or `message` on the | |
| * filter/refinement instead. | |
| */ | |
| readonly identifier?: string | undefined | |
| readonly parseOptions?: SchemaAST.ParseOptions | undefined | |
| /** | |
| * Optional metadata used to identify or extend the filter with custom data. | |
| */ | |
| readonly meta?: Meta | undefined | |
| /** | |
| * Accumulated brands when multiple brands are added with `Schema.brand`. | |
| */ | |
| readonly brands?: ReadonlyArray<string> | undefined | |
| readonly toArbitrary?: | |
| | ToArbitrary.Declaration<T, TypeParameters> | |
| | undefined | |
| } | |
| /** | |
| * Helpers for projecting declaration type-parameter schemas into decoded or | |
| * encoded codec arrays used by annotation hooks. | |
| * | |
| * @since 4.0.0 | |
| */ | |
| export namespace TypeParameters { | |
| /** | |
| * Maps declaration type-parameter schemas to codecs for their decoded `Type` | |
| * values. | |
| * | |
| * @category utility types | |
| * @since 4.0.0 | |
| */ | |
| export type Type<TypeParameters extends ReadonlyArray<Top>> = { | |
| readonly [K in keyof TypeParameters]: Codec<TypeParameters[K]["Type"]> | |
| } | |
| /** | |
| * Maps declaration type-parameter schemas to codecs for their `Encoded` values. | |
| * | |
| * @category utility types | |
| * @since 4.0.0 | |
| */ | |
| export type Encoded<TypeParameters extends ReadonlyArray<Top>> = { | |
| readonly [K in keyof TypeParameters]: Codec<TypeParameters[K]["Encoded"]> | |
| } | |
| } | |
| /** | |
| * Full annotation set for `Declaration` schema nodes — used when defining | |
| * custom, opaque schema types via `Schema.declare`. Extends {@link Bottom} | |
| * with optional codec, arbitrary, equivalence, and formatter hooks so that | |
| * derived capabilities (JSON encoding, property testing, etc.) can be | |
| * provided for the custom type. | |
| * | |
| * @category models | |
| * @since 4.0.0 | |
| */ | |
| export interface Declaration<T, TypeParameters extends ReadonlyArray<Top> = readonly []> | |
| extends Bottom<T, TypeParameters> | |
| { | |
| readonly toCodec?: | |
| | ((typeParameters: TypeParameters.Encoded<TypeParameters>) => SchemaAST.Link) | |
| | undefined | |
| readonly toCodecJson?: | |
| | ((typeParameters: TypeParameters.Encoded<TypeParameters>) => SchemaAST.Link) | |
| | undefined | |
| readonly toCodecIso?: | |
| | ((typeParameters: TypeParameters.Type<TypeParameters>) => SchemaAST.Link) | |
| | undefined | |
| readonly toArbitrary?: ToArbitrary.Declaration<T, TypeParameters> | undefined | |
| readonly toEquivalence?: ToEquivalence.Declaration<T, TypeParameters> | undefined | |
| readonly toFormatter?: ToFormatter.Declaration<T, TypeParameters> | undefined | |
| readonly typeConstructor?: { | |
| readonly _tag: string | |
| readonly [key: string]: unknown | |
| } | undefined | |
| readonly generation?: { | |
| readonly runtime: string | |
| readonly Type: string | |
| readonly Encoded?: string | undefined | |
| readonly importDeclaration?: string | undefined | |
| } | undefined | |
| /** | |
| * Used to collect sentinels from a Declaration SchemaAST. | |
| * | |
| * @internal | |
| */ | |
| readonly "~sentinels"?: ReadonlyArray<SchemaAST.Sentinel> | undefined | |
| } | |
| /** | |
| * Annotations for filter schema nodes (created via `Schema.filter`). Extends | |
| * {@link Augment} with an optional error message, identifier, and metadata. | |
| * Filters are intentionally non-parametric to keep them covariant. | |
| * | |
| * @category models | |
| * @since 4.0.0 | |
| */ | |
| export interface Filter extends Augment { | |
| /** | |
| * Complete message to use when this filter or refinement fails. | |
| * | |
| * **Details** | |
| * | |
| * The default formatter checks filter annotations in this order: | |
| * `message`, then `expected`, then `<filter>`. | |
| */ | |
| readonly message?: string | undefined | |
| /** | |
| * Stable identifier for the schema after this filter is attached. | |
| * | |
| * **Details** | |
| * | |
| * This can affect schema tooling such as JSON Schema generation and | |
| * type-level failures before the filter runs, but it does not name the | |
| * failed filter itself. For filter failure messages, use `expected` or | |
| * `message`. | |
| */ | |
| readonly identifier?: string | undefined | |
| /** | |
| * Optional metadata used to identify or extend the filter with custom data. | |
| */ | |
| readonly meta?: Meta | undefined | |
| /** | |
| * Optional hints used by arbitrary derivation for this filter. | |
| * | |
| * **Details** | |
| * | |
| * The same annotation can be attached to a single filter or a | |
| * `FilterGroup`. Group hints apply to the same schema node while child | |
| * filters are still collected and checked normally. | |
| */ | |
| readonly arbitrary?: | |
| | ToArbitrary.Filter | |
| | undefined | |
| /** | |
| * Marks the filter as *structural*, meaning it applies to the shape or | |
| * structure of the container (e.g., array length, object keys) rather than | |
| * the contents. | |
| * | |
| * **Details** | |
| * | |
| * Example: `minLength` on an array is a structural filter. | |
| */ | |
| readonly "~structural"?: boolean | undefined | |
| } | |
| /** | |
| * Types used by arbitrary-derivation annotations to configure `toArbitrary` | |
| * hooks, filter hints, candidate sources, diagnostics, and merged generation | |
| * constraints. | |
| * | |
| * @since 4.0.0 | |
| */ | |
| export namespace ToArbitrary { | |
| /** | |
| * Arbitrary-generation hints attached to a filter or filter group. | |
| * | |
| * **Details** | |
| * | |
| * `constraint` refines the schema node's base generator. `candidate` adds a | |
| * weighted source before all filters run. If neither hint is provided, the | |
| * filter does not guide generation; generated values are still checked by | |
| * the filter predicate. With `{ report: true }`, this is reported as | |
| * `OpaqueFilter`. | |
| * | |
| * @category models | |
| * @since 4.0.0 | |
| */ | |
| export interface Filter { | |
| readonly constraint?: Constraint | undefined | |
| readonly candidate?: Candidate | undefined | |
| } | |
| /** | |
| * Additional arbitrary source used before final filter checks run. | |
| * | |
| * **Details** | |
| * | |
| * The base generator keeps weight `1`; candidates default to weight `1` | |
| * and must use a positive integer weight. `make` receives the merged | |
| * constraint for the current node and may return `undefined` to opt out, | |
| * including for recursive terminal branches. Candidate values are still | |
| * checked by every schema filter, so invalid candidates affect efficiency but | |
| * not validity. | |
| * | |
| * @category models | |
| * @since 4.0.0 | |
| */ | |
| export interface Candidate { | |
| readonly weight?: number | undefined | |
| readonly make: ( | |
| fc: typeof FastCheck, | |
| context: Context | |
| ) => FastCheck.Arbitrary<unknown> | undefined | |
| } | |
| /** | |
| * Ordered constraint accumulated from range checks. | |
| * | |
| * **Details** | |
| * | |
| * Generators consume these constraints only when they recognize `order`, | |
| * such as `Order.Number`, `Order.BigInt`, DateTime, or BigDecimal. Merging | |
| * constraints with different `Order` instances fails fast. | |
| * | |
| * @category models | |
| * @since 4.0.0 | |
| */ | |
| export interface OrderedConstraint<T> { | |
| readonly order: Order.Order<T> | |
| readonly minimum?: T | undefined | |
| readonly exclusiveMinimum?: boolean | undefined | |
| readonly maximum?: T | undefined | |
| readonly exclusiveMaximum?: boolean | undefined | |
| } | |
| /** | |
| * Node-local arbitrary-generation constraint accumulated from schema checks. | |
| * | |
| * **Details** | |
| * | |
| * `Constraint` is a generation hint for the current schema AST node, not a | |
| * self-describing validation contract. Each generator consumes the fields it | |
| * understands for the current node and ignores the rest; final schema | |
| * filters still validate every generated value. | |
| * | |
| * `minLength` and `maxLength` represent node-local cardinality: string | |
| * length for strings, array length for arrays, final own-property count for | |
| * objects, and final size/cardinality for sets, maps, hash collections, and | |
| * chunks. `patterns` are concatenated and used by string generators. | |
| * `integer`, `noNaN`, `noInfinity`, `valid`, and `unique` are true when any | |
| * contributing filter sets them. Range bounds live in `ordered` so ordered | |
| * values can share the same representation. | |
| * | |
| * @category models | |
| * @since 4.0.0 | |
| */ | |
| export interface Constraint { | |
| readonly minLength?: number | undefined | |
| readonly maxLength?: number | undefined | |
| readonly patterns?: readonly [string, ...Array<string>] | |
| readonly integer?: boolean | undefined | |
| readonly noInfinity?: boolean | undefined | |
| readonly noNaN?: boolean | undefined | |
| readonly valid?: boolean | undefined | |
| readonly unique?: boolean | undefined | |
| readonly ordered?: OrderedConstraint<any> | undefined | |
| } | |
| /** | |
| * Recursion budget passed to arbitrary-derivation hooks. | |
| * | |
| * **Details** | |
| * | |
| * Pass this object to `fc.oneof` when combining terminal and recursive | |
| * branches. Put the terminal branch first because fast-check uses only the | |
| * first branch once `maxDepth` is reached for `depthIdentifier`. | |
| * | |
| * @category models | |
| * @since 4.0.0 | |
| */ | |
| export interface Recursion { | |
| readonly maxDepth: number | |
| readonly depthIdentifier: FastCheck.DepthIdentifier | string | |
| } | |
| /** | |
| * Context passed to arbitrary-derivation hooks and candidate factories. | |
| * | |
| * **Details** | |
| * | |
| * `constraint` contains the merged constraint for the current schema | |
| * node. `recursion` is present while deriving through a suspended schema; | |
| * hooks that build recursive alternatives should pass it to `fc.oneof` with | |
| * the finite branch first. | |
| * | |
| * @category models | |
| * @since 4.0.0 | |
| */ | |
| export interface Context { | |
| readonly constraint?: ToArbitrary.Constraint | undefined | |
| readonly recursion?: ToArbitrary.Recursion | undefined | |
| } | |
| /** | |
| * Arbitrary generators derived for a declaration type parameter. | |
| * | |
| * **Details** | |
| * | |
| * `arbitrary` is the normal generator. `terminal` is the finite generator | |
| * used while building recursive terminal branches and is `undefined` when | |
| * no finite path is known. Optional containers can ignore it; non-empty | |
| * containers need it for their terminal branch. | |
| * | |
| * @category models | |
| * @since 4.0.0 | |
| */ | |
| export interface TypeParameter<T> { | |
| readonly arbitrary: FastCheck.Arbitrary<T> | |
| readonly terminal: FastCheck.Arbitrary<T> | undefined | |
| } | |
| /** | |
| * Arbitrary derivation returned by declaration hooks. | |
| * | |
| * **Details** | |
| * | |
| * `arbitrary` is the normal generator. `terminal` is an optional finite | |
| * branch for recursive schemas. If omitted, it defaults to `arbitrary` only | |
| * for declarations without type parameters. | |
| * | |
| * @category models | |
| * @since 4.0.0 | |
| */ | |
| export interface Derivation<T> { | |
| readonly arbitrary: FastCheck.Arbitrary<T> | |
| readonly terminal?: FastCheck.Arbitrary<T> | undefined | |
| } | |
| /** | |
| * Output accepted from declaration arbitrary hooks. | |
| * | |
| * **Details** | |
| * | |
| * A bare fast-check arbitrary is shorthand for `{ arbitrary }`, useful for | |
| * atomic declarations such as URLs. Generic declarations that need precise | |
| * recursive behavior should return a {@link Derivation} with `terminal`. | |
| * | |
| * @category models | |
| * @since 4.0.0 | |
| */ | |
| export type Output<T> = FastCheck.Arbitrary<T> | Derivation<T> | |
| /** | |
| * Hook signature for declaration schema arbitrary annotations. | |
| * | |
| * **Details** | |
| * | |
| * Type parameters expose normal and terminal generators. A declaration with | |
| * no type parameters can return a bare arbitrary; a generic declaration | |
| * must return `terminal` explicitly when it has a finite branch depending on | |
| * parameters. | |
| * | |
| * @category models | |
| * @since 4.0.0 | |
| */ | |
| export interface Declaration<T, TypeParameters extends ReadonlyArray<Top>> { | |
| ( | |
| /* Arbitrary derivations for any type parameters of the schema (if present) */ | |
| typeParameters: { readonly [K in keyof TypeParameters]: TypeParameter<TypeParameters[K]["Type"]> } | |
| ): (fc: typeof FastCheck, context: Context) => Output<T> | |
| } | |
| /** | |
| * Wraps a derived value together with arbitrary-derivation diagnostics. | |
| * | |
| * @category models | |
| * @since 4.0.0 | |
| */ | |
| export interface WithReport<A> { | |
| readonly value: A | |
| readonly report: Report | |
| } | |
| /** | |
| * Diagnostics collected while deriving an arbitrary. | |
| * | |
| * **Details** | |
| * | |
| * Reports contain warnings only. Unsupported schema nodes, impossible | |
| * constraints, invalid candidate weights, and throwing candidate factories | |
| * fail immediately. | |
| * | |
| * @category models | |
| * @since 4.0.0 | |
| */ | |
| export interface Report { | |
| readonly warnings: ReadonlyArray<Warning> | |
| } | |
| /** | |
| * Non-fatal arbitrary-derivation warning. | |
| * | |
| * @category models | |
| * @since 4.0.0 | |
| */ | |
| export type Warning = OpaqueFilterWarning | |
| /** | |
| * Warning emitted when a filter is handled only by the final `.filter`. | |
| * | |
| * **Details** | |
| * | |
| * The filter is still enforced. The warning means it did not contribute | |
| * a constraint or candidate, so generation may rely on fast-check discards. | |
| * | |
| * @category models | |
| * @since 4.0.0 | |
| */ | |
| export interface OpaqueFilterWarning { | |
| readonly _tag: "OpaqueFilter" | |
| readonly path: ReadonlyArray<PropertyKey> | |
| readonly description?: string | undefined | |
| } | |
| } | |
| /** | |
| * Types used by formatter annotations to customize formatter derivation for | |
| * declaration schemas. | |
| * | |
| * @since 4.0.0 | |
| */ | |
| export namespace ToFormatter { | |
| /** | |
| * Hook signature for declaration schema formatter annotations. | |
| * | |
| * **Details** | |
| * | |
| * Given formatters for any type parameters, returns a formatter for `T`. | |
| * | |
| * @category models | |
| * @since 4.0.0 | |
| */ | |
| export interface Declaration<T, TypeParameters extends ReadonlyArray<Top>> { | |
| ( | |
| /* Formatters for any type parameters of the schema (if present) */ | |
| typeParameters: { readonly [K in keyof TypeParameters]: Formatter<TypeParameters[K]["Type"]> } | |
| ): Formatter<T> | |
| } | |
| } | |
| /** | |
| * Types used by equivalence annotations to customize equivalence derivation for | |
| * declaration schemas. | |
| * | |
| * @since 4.0.0 | |
| */ | |
| export namespace ToEquivalence { | |
| /** | |
| * Hook signature for declaration schema equivalence annotations. | |
| * | |
| * **Details** | |
| * | |
| * Given equivalences for any type parameters, returns an `Equivalence` for `T`. | |
| * | |
| * @category models | |
| * @since 4.0.0 | |
| */ | |
| export interface Declaration<T, TypeParameters extends ReadonlyArray<Top>> { | |
| ( | |
| /* Equivalences for any type parameters of the schema (if present) */ | |
| typeParameters: { readonly [K in keyof TypeParameters]: Equivalence.Equivalence<TypeParameters[K]["Type"]> } | |
| ): Equivalence.Equivalence<T> | |
| } | |
| } | |
| /** | |
| * Annotations that can be attached to schema issues. | |
| * | |
| * **Details** | |
| * | |
| * The optional `message` field overrides the default issue message. | |
| * | |
| * @category models | |
| * @since 4.0.0 | |
| */ | |
| export interface Issue extends Annotations { | |
| readonly message?: string | undefined | |
| } | |
| /** | |
| * Registry of metadata payloads emitted by built-in schema filters and checks. | |
| * | |
| * **Details** | |
| * | |
| * Do not augment this interface with custom metadata; extend `MetaDefinitions` | |
| * instead. | |
| * | |
| * @category models | |
| * @since 4.0.0 | |
| */ | |
| export interface BuiltInMetaDefinitions { | |
| // String Meta | |
| readonly isStringFinite: { | |
| readonly _tag: "isStringFinite" | |
| readonly regExp: globalThis.RegExp | |
| } | |
| readonly isStringBigInt: { | |
| readonly _tag: "isStringBigInt" | |
| readonly regExp: globalThis.RegExp | |
| } | |
| readonly isStringSymbol: { | |
| readonly _tag: "isStringSymbol" | |
| readonly regExp: globalThis.RegExp | |
| } | |
| readonly isMinLength: { | |
| readonly _tag: "isMinLength" | |
| readonly minLength: number | |
| } | |
| readonly isMaxLength: { | |
| readonly _tag: "isMaxLength" | |
| readonly maxLength: number | |
| } | |
| readonly isLengthBetween: { | |
| readonly _tag: "isLengthBetween" | |
| readonly minimum: number | |
| readonly maximum: number | |
| } | |
| readonly isPattern: { | |
| readonly _tag: "isPattern" | |
| readonly regExp: globalThis.RegExp | |
| } | |
| readonly isTrimmed: { | |
| readonly _tag: "isTrimmed" | |
| readonly regExp: globalThis.RegExp | |
| } | |
| readonly isUUID: { | |
| readonly _tag: "isUUID" | |
| readonly regExp: globalThis.RegExp | |
| readonly version: 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | undefined | |
| } | |
| readonly isGUID: { | |
| readonly _tag: "isGUID" | |
| readonly regExp: globalThis.RegExp | |
| } | |
| readonly isULID: { | |
| readonly _tag: "isULID" | |
| readonly regExp: globalThis.RegExp | |
| } | |
| readonly isBase64: { | |
| readonly _tag: "isBase64" | |
| readonly regExp: globalThis.RegExp | |
| } | |
| readonly isBase64Url: { | |
| readonly _tag: "isBase64Url" | |
| readonly regExp: globalThis.RegExp | |
| } | |
| readonly isStartsWith: { | |
| readonly _tag: "isStartsWith" | |
| readonly startsWith: string | |
| readonly regExp: globalThis.RegExp | |
| } | |
| readonly isEndsWith: { | |
| readonly _tag: "isEndsWith" | |
| readonly endsWith: string | |
| readonly regExp: globalThis.RegExp | |
| } | |
| readonly isIncludes: { | |
| readonly _tag: "isIncludes" | |
| readonly includes: string | |
| readonly regExp: globalThis.RegExp | |
| } | |
| readonly isUppercased: { | |
| readonly _tag: "isUppercased" | |
| readonly regExp: globalThis.RegExp | |
| } | |
| readonly isLowercased: { | |
| readonly _tag: "isLowercased" | |
| readonly regExp: globalThis.RegExp | |
| } | |
| readonly isCapitalized: { | |
| readonly _tag: "isCapitalized" | |
| readonly regExp: globalThis.RegExp | |
| } | |
| readonly isUncapitalized: { | |
| readonly _tag: "isUncapitalized" | |
| readonly regExp: globalThis.RegExp | |
| } | |
| // Number Meta | |
| readonly isFinite: { | |
| readonly _tag: "isFinite" | |
| } | |
| readonly isInt: { | |
| readonly _tag: "isInt" | |
| } | |
| readonly isMultipleOf: { | |
| readonly _tag: "isMultipleOf" | |
| readonly divisor: number | |
| } | |
| readonly isGreaterThan: { | |
| readonly _tag: "isGreaterThan" | |
| readonly exclusiveMinimum: number | |
| } | |
| readonly isGreaterThanOrEqualTo: { | |
| readonly _tag: "isGreaterThanOrEqualTo" | |
| readonly minimum: number | |
| } | |
| readonly isLessThan: { | |
| readonly _tag: "isLessThan" | |
| readonly exclusiveMaximum: number | |
| } | |
| readonly isLessThanOrEqualTo: { | |
| readonly _tag: "isLessThanOrEqualTo" | |
| readonly maximum: number | |
| } | |
| readonly isBetween: { | |
| readonly _tag: "isBetween" | |
| readonly minimum: number | |
| readonly maximum: number | |
| readonly exclusiveMinimum?: boolean | undefined | |
| readonly exclusiveMaximum?: boolean | undefined | |
| } | |
| // BigInt Meta | |
| readonly isGreaterThanBigInt: { | |
| readonly _tag: "isGreaterThanBigInt" | |
| readonly exclusiveMinimum: bigint | |
| } | |
| readonly isGreaterThanOrEqualToBigInt: { | |
| readonly _tag: "isGreaterThanOrEqualToBigInt" | |
| readonly minimum: bigint | |
| } | |
| readonly isLessThanBigInt: { | |
| readonly _tag: "isLessThanBigInt" | |
| readonly exclusiveMaximum: bigint | |
| } | |
| readonly isLessThanOrEqualToBigInt: { | |
| readonly _tag: "isLessThanOrEqualToBigInt" | |
| readonly maximum: bigint | |
| } | |
| readonly isBetweenBigInt: { | |
| readonly _tag: "isBetweenBigInt" | |
| readonly minimum: bigint | |
| readonly maximum: bigint | |
| readonly exclusiveMinimum?: boolean | undefined | |
| readonly exclusiveMaximum?: boolean | undefined | |
| } | |
| // Date Meta | |
| readonly isDateValid: { | |
| readonly _tag: "isDateValid" | |
| } | |
| readonly isGreaterThanDate: { | |
| readonly _tag: "isGreaterThanDate" | |
| readonly exclusiveMinimum: globalThis.Date | |
| } | |
| readonly isGreaterThanOrEqualToDate: { | |
| readonly _tag: "isGreaterThanOrEqualToDate" | |
| readonly minimum: globalThis.Date | |
| } | |
| readonly isLessThanDate: { | |
| readonly _tag: "isLessThanDate" | |
| readonly exclusiveMaximum: globalThis.Date | |
| } | |
| readonly isLessThanOrEqualToDate: { | |
| readonly _tag: "isLessThanOrEqualToDate" | |
| readonly maximum: globalThis.Date | |
| } | |
| readonly isBetweenDate: { | |
| readonly _tag: "isBetweenDate" | |
| readonly minimum: globalThis.Date | |
| readonly maximum: globalThis.Date | |
| readonly exclusiveMinimum?: boolean | undefined | |
| readonly exclusiveMaximum?: boolean | undefined | |
| } | |
| // Objects Meta | |
| readonly isMinProperties: { | |
| readonly _tag: "isMinProperties" | |
| readonly minProperties: number | |
| } | |
| readonly isMaxProperties: { | |
| readonly _tag: "isMaxProperties" | |
| readonly maxProperties: number | |
| } | |
| readonly isPropertiesLengthBetween: { | |
| readonly _tag: "isPropertiesLengthBetween" | |
| readonly minimum: number | |
| readonly maximum: number | |
| } | |
| readonly isPropertyNames: { | |
| readonly _tag: "isPropertyNames" | |
| readonly propertyNames: SchemaAST.AST | |
| } | |
| // Arrays Meta | |
| readonly isUnique: { | |
| readonly _tag: "isUnique" | |
| } | |
| // Declaration Meta | |
| readonly isMinSize: { | |
| readonly _tag: "isMinSize" | |
| readonly minSize: number | |
| } | |
| readonly isMaxSize: { | |
| readonly _tag: "isMaxSize" | |
| readonly maxSize: number | |
| } | |
| readonly isSizeBetween: { | |
| readonly _tag: "isSizeBetween" | |
| readonly minimum: number | |
| readonly maximum: number | |
| } | |
| } | |
| /** | |
| * Union of all metadata payloads defined by `BuiltInMetaDefinitions`. | |
| * | |
| * @category utility types | |
| * @since 4.0.0 | |
| */ | |
| export type BuiltInMeta = BuiltInMetaDefinitions[keyof BuiltInMetaDefinitions] | |
| /** | |
| * Augmentable registry of schema filter metadata payloads. | |
| * | |
| * **Details** | |
| * | |
| * Extend this interface to add custom values accepted by annotation `meta` | |
| * fields. | |
| * | |
| * @category models | |
| * @since 4.0.0 | |
| */ | |
| export interface MetaDefinitions extends BuiltInMetaDefinitions {} | |
| /** | |
| * Union of built-in and user-augmented schema filter metadata payloads. | |
| * | |
| * @category utility types | |
| * @since 4.0.0 | |
| */ | |
| export type Meta = MetaDefinitions[keyof MetaDefinitions] | |
| } | |
Xet Storage Details
- Size:
- 441 kB
- Xet hash:
- 3c15c81d010c10856d652e0de0bdfe9b036ec3477d156a62fddcbf24218d9eab
·
Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.