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