effect
Version:
The missing standard library for TypeScript, for writing production-grade software.
1,549 lines • 193 kB
JavaScript
/**
* @since 3.10.0
*/
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 * 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 { 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 * as ParseResult from "./ParseResult.js";
import { pipeArguments } from "./Pipeable.js";
import * as Predicate from "./Predicate.js";
import * as redacted_ from "./Redacted.js";
import * as Request from "./Request.js";
import * as scheduler_ from "./Scheduler.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";
/**
* @since 3.10.0
* @category symbol
*/
export const TypeId = /*#__PURE__*/Symbol.for("effect/Schema");
/**
* @category constructors
* @since 3.10.0
*/
export function make(ast) {
return class SchemaClass {
[TypeId] = variance;
static ast = ast;
static annotations(annotations) {
return make(mergeSchemaAnnotations(this.ast, annotations));
}
static pipe() {
return pipeArguments(this, arguments);
}
static toString() {
return String(ast);
}
static Type;
static Encoded;
static Context;
static [TypeId] = variance;
};
}
const variance = {
/* c8 ignore next */
_A: _ => _,
/* c8 ignore next */
_I: _ => _,
/* c8 ignore next */
_R: _ => _
};
const makeStandardResult = exit => exit_.isSuccess(exit) ? exit.value : makeStandardFailureResult(cause_.pretty(exit.cause));
const makeStandardFailureResult = message => ({
issues: [{
message
}]
});
const makeStandardFailureFromParseIssue = issue => 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 = (schema, overrideOptions) => {
const decodeUnknown = ParseResult.decodeUnknown(schema, {
errors: "all"
});
return class StandardSchemaV1Class extends make(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));
});
});
}
};
};
};
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 = annotations => {
if (!annotations) {
return {};
}
const out = {
...annotations
};
for (const key in builtInAnnotations) {
if (key in annotations) {
const id = builtInAnnotations[key];
out[id] = annotations[key];
delete out[key];
}
}
return out;
};
const mergeSchemaAnnotations = (ast, annotations) => AST.annotations(ast, toASTAnnotations(annotations));
/**
* @since 3.10.0
*/
export function asSchema(schema) {
return schema;
}
/**
* @category formatting
* @since 3.10.0
*/
export const format = schema => String(schema.ast);
/**
* 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 = schema => 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 = schema => 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 = schema => 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 = (schema, options) => {
const encodeUnknown = ParseResult.encodeUnknown(schema, options);
return (u, overrideOptions) => ParseResult.mapError(encodeUnknown(u, overrideOptions), ParseResult.parseError);
};
/**
* @category encoding
* @since 3.10.0
*/
export const encodeUnknownEither = (schema, options) => {
const encodeUnknownEither = ParseResult.encodeUnknownEither(schema, options);
return (u, overrideOptions) => either_.mapLeft(encodeUnknownEither(u, overrideOptions), ParseResult.parseError);
};
/**
* @category encoding
* @since 3.10.0
*/
export const encodeUnknownPromise = (schema, options) => {
const parser = encodeUnknown(schema, options);
return (u, overrideOptions) => Effect.runPromise(parser(u, overrideOptions));
};
/**
* @category encoding
* @since 3.10.0
*/
export const encode = encodeUnknown;
/**
* @category encoding
* @since 3.10.0
*/
export const encodeEither = encodeUnknownEither;
/**
* @category encoding
* @since 3.10.0
*/
export const encodePromise = encodeUnknownPromise;
/**
* @category decoding
* @since 3.10.0
*/
export const decodeUnknown = (schema, options) => {
const decodeUnknown = ParseResult.decodeUnknown(schema, options);
return (u, overrideOptions) => ParseResult.mapError(decodeUnknown(u, overrideOptions), ParseResult.parseError);
};
/**
* @category decoding
* @since 3.10.0
*/
export const decodeUnknownEither = (schema, options) => {
const decodeUnknownEither = ParseResult.decodeUnknownEither(schema, options);
return (u, overrideOptions) => either_.mapLeft(decodeUnknownEither(u, overrideOptions), ParseResult.parseError);
};
/**
* @category decoding
* @since 3.10.0
*/
export const decodeUnknownPromise = (schema, options) => {
const parser = decodeUnknown(schema, options);
return (u, overrideOptions) => Effect.runPromise(parser(u, overrideOptions));
};
/**
* @category decoding
* @since 3.10.0
*/
export const decode = decodeUnknown;
/**
* @category decoding
* @since 3.10.0
*/
export const decodeEither = decodeUnknownEither;
/**
* @category decoding
* @since 3.10.0
*/
export const decodePromise = decodeUnknownPromise;
/**
* @category validation
* @since 3.10.0
*/
export const validate = (schema, options) => {
const validate = ParseResult.validate(schema, options);
return (u, overrideOptions) => ParseResult.mapError(validate(u, overrideOptions), ParseResult.parseError);
};
/**
* @category validation
* @since 3.10.0
*/
export const validateEither = (schema, options) => {
const validateEither = ParseResult.validateEither(schema, options);
return (u, overrideOptions) => either_.mapLeft(validateEither(u, overrideOptions), ParseResult.parseError);
};
/**
* @category validation
* @since 3.10.0
*/
export const validatePromise = (schema, options) => {
const parser = validate(schema, options);
return (u, overrideOptions) => Effect.runPromise(parser(u, overrideOptions));
};
/**
* Tests if a value is a `Schema`.
*
* @category guards
* @since 3.10.0
*/
export const isSchema = u => Predicate.hasProperty(u, TypeId) && Predicate.isObject(u[TypeId]);
function getDefaultLiteralAST(literals) {
return AST.isMembers(literals) ? AST.Union.make(AST.mapMembers(literals, literal => new AST.Literal(literal))) : new AST.Literal(literals[0]);
}
function makeLiteralClass(literals, ast = getDefaultLiteralAST(literals)) {
return class LiteralClass extends make(ast) {
static annotations(annotations) {
return makeLiteralClass(this.literals, mergeSchemaAnnotations(this.ast, annotations));
}
static literals = [...literals];
};
}
export function Literal(...literals) {
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 = (...literals) => _schema => Literal(...literals);
/**
* @category constructors
* @since 3.10.0
*/
export const UniqueSymbolFromSelf = symbol => make(new AST.UniqueSymbol(symbol));
const getDefaultEnumsAST = enums => new AST.Enums(Object.keys(enums).filter(key => typeof enums[enums[key]] !== "number").map(key => [key, enums[key]]));
const makeEnumsClass = (enums, ast = getDefaultEnumsAST(enums)) => class EnumsClass extends make(ast) {
static annotations(annotations) {
return makeEnumsClass(this.enums, mergeSchemaAnnotations(this.ast, annotations));
}
static enums = {
...enums
};
};
/**
* @category constructors
* @since 3.10.0
*/
export const Enums = enums => makeEnumsClass(enums);
/**
* @category template literal
* @since 3.10.0
*/
export const TemplateLiteral = (...[head, ...tail]) => {
const spans = [];
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), "")]));
}
};
function getTemplateLiteralParserCoercedElement(encoded, schema) {
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 = [];
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) => {
const encodedSchemas = [];
const elements = [];
const schemas = [];
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);
const re = AST.getTemplateLiteralCapturingRegExp(from.ast);
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();
};
};
const declareConstructor = (typeParameters, options, annotations) => makeDeclareClass(typeParameters, new AST.Declaration(typeParameters.map(tp => tp.ast), (...typeParameters) => options.decode(...typeParameters.map(make)), (...typeParameters) => options.encode(...typeParameters.map(make)), toASTAnnotations(annotations)));
const declarePrimitive = (is, annotations) => {
const decodeUnknown = () => (input, _, ast) => is(input) ? ParseResult.succeed(input) : ParseResult.fail(new ParseResult.Type(ast, input));
const encodeUnknown = decodeUnknown;
return makeDeclareClass([], new AST.Declaration([], decodeUnknown, encodeUnknown, toASTAnnotations(annotations)));
};
function makeDeclareClass(typeParameters, ast) {
return class DeclareClass extends make(ast) {
static annotations(annotations) {
return makeDeclareClass(this.typeParameters, mergeSchemaAnnotations(this.ast, annotations));
}
static typeParameters = [...typeParameters];
};
}
/**
* 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 = 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);
};
/**
* @category schema id
* @since 3.10.0
*/
export const BrandSchemaId = /*#__PURE__*/Symbol.for("effect/SchemaId/Brand");
/**
* @category constructors
* @since 3.10.0
*/
export const fromBrand = (constructor, annotations) => self => {
const out = makeBrandClass(self, new AST.Refinement(self.ast, function predicate(a, _, ast) {
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;
};
/**
* @category schema id
* @since 3.10.0
*/
export const InstanceOfSchemaId = /*#__PURE__*/Symbol.for("effect/SchemaId/InstanceOf");
/**
* @category constructors
* @since 3.10.0
*/
export const instanceOf = (constructor, annotations) => declare(u => u instanceof constructor, {
title: constructor.name,
description: `an instance of ${constructor.name}`,
pretty: () => String,
schemaId: InstanceOfSchemaId,
[InstanceOfSchemaId]: {
constructor
},
...annotations
});
/**
* @category primitives
* @since 3.10.0
*/
export class Undefined extends /*#__PURE__*/make(AST.undefinedKeyword) {}
/**
* @category primitives
* @since 3.10.0
*/
export class Void extends /*#__PURE__*/make(AST.voidKeyword) {}
/**
* @category primitives
* @since 3.10.0
*/
export class Null extends /*#__PURE__*/make(AST.null) {}
/**
* @category primitives
* @since 3.10.0
*/
export class Never extends /*#__PURE__*/make(AST.neverKeyword) {}
/**
* @category primitives
* @since 3.10.0
*/
export class Unknown extends /*#__PURE__*/make(AST.unknownKeyword) {}
/**
* @category primitives
* @since 3.10.0
*/
export class Any extends /*#__PURE__*/make(AST.anyKeyword) {}
/**
* @category primitives
* @since 3.10.0
*/
export class BigIntFromSelf extends /*#__PURE__*/make(AST.bigIntKeyword) {}
/**
* @category primitives
* @since 3.10.0
*/
export class SymbolFromSelf extends /*#__PURE__*/make(AST.symbolKeyword) {}
/** @ignore */
class String$ extends /*#__PURE__*/make(AST.stringKeyword) {}
/** @ignore */
class Number$ extends /*#__PURE__*/make(AST.numberKeyword) {}
/** @ignore */
class Boolean$ extends /*#__PURE__*/make(AST.booleanKeyword) {}
/** @ignore */
class Object$ extends /*#__PURE__*/make(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 };
const getDefaultUnionAST = members => AST.Union.make(members.map(m => m.ast));
function makeUnionClass(members, ast = getDefaultUnionAST(members)) {
return class UnionClass extends make(ast) {
static annotations(annotations) {
return makeUnionClass(this.members, mergeSchemaAnnotations(this.ast, annotations));
}
static members = [...members];
};
}
export function Union(...members) {
return AST.isMembers(members) ? makeUnionClass(members) : array_.isNonEmptyReadonlyArray(members) ? members[0] : Never;
}
/**
* @category combinators
* @since 3.10.0
*/
export const NullOr = self => Union(self, Null);
/**
* @category combinators
* @since 3.10.0
*/
export const UndefinedOr = self => Union(self, Undefined);
/**
* @category combinators
* @since 3.10.0
*/
export const NullishOr = self => Union(self, Null, Undefined);
/**
* @category combinators
* @since 3.10.0
*/
export const keyof = self => make(AST.keyof(self.ast));
/**
* @since 3.10.0
*/
export const element = self => new ElementImpl(new AST.OptionalType(self.ast, false), self);
/**
* @since 3.10.0
*/
export const optionalElement = self => new ElementImpl(new AST.OptionalType(self.ast, true), self);
class ElementImpl {
ast;
from;
[TypeId];
_Token;
constructor(ast, from) {
this.ast = ast;
this.from = from;
}
annotations(annotations) {
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 ? "?" : ""}`;
}
}
const getDefaultTupleTypeAST = (elements, 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, rest, ast = getDefaultTupleTypeAST(elements, rest)) {
return class TupleTypeClass extends make(ast) {
static annotations(annotations) {
return makeTupleTypeClass(this.elements, this.rest, mergeSchemaAnnotations(this.ast, annotations));
}
static elements = [...elements];
static rest = [...rest];
};
}
export function Tuple(...args) {
return Array.isArray(args[0]) ? makeTupleTypeClass(args[0], args.slice(1)) : makeTupleTypeClass(args, []);
}
function makeArrayClass(value, ast) {
return class ArrayClass extends makeTupleTypeClass([], [value], ast) {
static annotations(annotations) {
return makeArrayClass(this.value, mergeSchemaAnnotations(this.ast, annotations));
}
static value = value;
};
}
const Array$ = value => makeArrayClass(value);
export {
/**
* @category constructors
* @since 3.10.0
*/
Array$ as Array };
function makeNonEmptyArrayClass(value, ast) {
return class NonEmptyArrayClass extends makeTupleTypeClass([value], [value], ast) {
static annotations(annotations) {
return makeNonEmptyArrayClass(this.value, mergeSchemaAnnotations(this.ast, annotations));
}
static value = value;
};
}
/**
* @category constructors
* @since 3.10.0
*/
export const NonEmptyArray = value => makeNonEmptyArrayClass(value);
/**
* @category constructors
* @since 3.10.0
*/
export function 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 constructors
* @since 3.10.0
*/
export function 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
});
}
const formatPropertySignatureToken = isOptional => isOptional ? "\"?:\"" : "\":\"";
/**
* @category PropertySignature
* @since 3.10.0
*/
export class PropertySignatureDeclaration extends AST.OptionalType {
isReadonly;
defaultValue;
/**
* @since 3.10.0
*/
_tag = "PropertySignatureDeclaration";
constructor(type, isOptional, isReadonly, annotations, defaultValue) {
super(type, isOptional, annotations);
this.isReadonly = isReadonly;
this.defaultValue = defaultValue;
}
/**
* @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 {
isReadonly;
fromKey;
constructor(type, isOptional, isReadonly, annotations, fromKey) {
super(type, isOptional, annotations);
this.isReadonly = isReadonly;
this.fromKey = fromKey;
}
}
/**
* @category PropertySignature
* @since 3.10.0
*/
export class ToPropertySignature extends AST.OptionalType {
isReadonly;
defaultValue;
constructor(type, isOptional, isReadonly, annotations, defaultValue) {
super(type, isOptional, annotations);
this.isReadonly = isReadonly;
this.defaultValue = defaultValue;
}
}
const formatPropertyKey = p => {
if (p === undefined) {
return "never";
}
if (Predicate.isString(p)) {
return JSON.stringify(p);
}
return String(p);
};
/**
* @category PropertySignature
* @since 3.10.0
*/
export class PropertySignatureTransformation {
from;
to;
decode;
encode;
/**
* @since 3.10.0
*/
_tag = "PropertySignatureTransformation";
constructor(from, to, decode, encode) {
this.from = from;
this.to = to;
this.decode = decode;
this.encode = encode;
}
/**
* @since 3.10.0
*/
toString() {
return `PropertySignature<${formatPropertySignatureToken(this.to.isOptional)}, ${this.to.type}, ${formatPropertyKey(this.from.fromKey)}, ${formatPropertySignatureToken(this.from.isOptional)}, ${this.from.type}>`;
}
}
const mergeSignatureAnnotations = (ast, annotations) => {
switch (ast._tag) {
case "PropertySignatureDeclaration":
{
return new PropertySignatureDeclaration(ast.type, ast.isOptional, ast.isReadonly, {
...ast.annotations,
...annotations
}, ast.defaultValue);
}
case "PropertySignatureTransformation":
{
return new PropertySignatureTransformation(ast.from, new ToPropertySignature(ast.to.type, ast.to.isOptional, ast.to.isReadonly, {
...ast.to.annotations,
...annotations
}, ast.to.defaultValue), ast.decode, ast.encode);
}
}
};
/**
* @since 3.10.0
* @category symbol
*/
export const PropertySignatureTypeId = /*#__PURE__*/Symbol.for("effect/PropertySignature");
/**
* @since 3.10.0
* @category guards
*/
export const isPropertySignature = u => Predicate.hasProperty(u, PropertySignatureTypeId);
class PropertySignatureImpl {
ast;
[TypeId];
[PropertySignatureTypeId] = null;
_TypeToken;
_Key;
_EncodedToken;
_HasDefault;
constructor(ast) {
this.ast = ast;
}
pipe() {
return pipeArguments(this, arguments);
}
annotations(annotations) {
return new PropertySignatureImpl(mergeSignatureAnnotations(this.ast, toASTAnnotations(annotations)));
}
toString() {
return String(this.ast);
}
}
/**
* @category PropertySignature
* @since 3.10.0
*/
export const makePropertySignature = ast => new PropertySignatureImpl(ast);
class PropertySignatureWithFromImpl extends PropertySignatureImpl {
from;
constructor(ast, from) {
super(ast);
this.from = from;
}
annotations(annotations) {
return new PropertySignatureWithFromImpl(mergeSignatureAnnotations(this.ast, toASTAnnotations(annotations)), this.from);
}
}
/**
* Lifts a `Schema` into a `PropertySignature`.
*
* @category PropertySignature
* @since 3.10.0
*/
export const propertySignature = self => new PropertySignatureWithFromImpl(new PropertySignatureDeclaration(self.ast, false, true, {}, undefined), self);
/**
* Enhances a property signature with a default constructor value.
*
* @category PropertySignature
* @since 3.10.0
*/
export const withConstructorDefault = /*#__PURE__*/dual(2, (self, defaultValue) => {
const ast = self.ast;
switch (ast._tag) {
case "PropertySignatureDeclaration":
return makePropertySignature(new PropertySignatureDeclaration(ast.type, ast.isOptional, ast.isReadonly, ast.annotations, defaultValue));
case "PropertySignatureTransformation":
return makePropertySignature(new PropertySignatureTransformation(ast.from, new ToPropertySignature(ast.to.type, ast.to.isOptional, ast.to.isReadonly, ast.to.annotations, defaultValue), ast.decode, ast.encode));
}
});
const applyDefaultValue = (o, defaultValue) => option_.match(o, {
onNone: () => option_.some(defaultValue()),
onSome: value => option_.some(value === undefined ? defaultValue() : value)
});
const pruneUndefined = ast => AST.pruneUndefined(ast, pruneUndefined, ast => {
const pruned = pruneUndefined(ast.to);
if (pruned) {
return new AST.Transformation(ast.from, pruned, ast.transformation);
}
});
/**
* Enhances a property signature with a default decoding value.
*
* @category PropertySignature
* @since 3.10.0
*/
export const withDecodingDefault = /*#__PURE__*/dual(2, (self, defaultValue) => {
const ast = self.ast;
switch (ast._tag) {
case "PropertySignatureDeclaration":
{
const to = AST.typeAST(ast.type);
return makePropertySignature(new PropertySignatureTransformation(new FromPropertySignature(ast.type, ast.isOptional, ast.isReadonly, ast.annotations), new ToPropertySignature(pruneUndefined(to) ?? to, false, true, {}, ast.defaultValue), o => applyDefaultValue(o, defaultValue), identity));
}
case "PropertySignatureTransformation":
{
const to = ast.to.type;
return makePropertySignature(new PropertySignatureTransformation(ast.from, new ToPropertySignature(pruneUndefined(to) ?? to, false, ast.to.isReadonly, ast.to.annotations, ast.to.defaultValue), o => applyDefaultValue(ast.decode(o), defaultValue), ast.encode));
}
}
});
/**
* Enhances a property signature with a default decoding value and a default constructor value.
*
* @category PropertySignature
* @since 3.10.0
*/
export const withDefaults = /*#__PURE__*/dual(2, (self, defaults) => self.pipe(withDecodingDefault(defaults.decoding), withConstructorDefault(defaults.constructor)));
/**
* Enhances a property signature by specifying a different key for it in the Encoded type.
*
* @category PropertySignature
* @since 3.10.0
*/
export const fromKey = /*#__PURE__*/dual(2, (self, key) => {
const ast = self.ast;
switch (ast._tag) {
case "PropertySignatureDeclaration":
{
return makePropertySignature(new PropertySignatureTransformation(new FromPropertySignature(ast.type, ast.isOptional, ast.isReadonly, ast.annotations, key), new ToPropertySignature(AST.typeAST(ast.type), ast.isOptional, ast.isReadonly, {}, ast.defaultValue), identity, identity));
}
case "PropertySignatureTransformation":
return makePropertySignature(new PropertySignatureTransformation(new FromPropertySignature(ast.from.type, ast.from.isOptional, ast.from.isReadonly, ast.from.annotations, key), ast.to, ast.decode, ast.encode));
}
});
/**
* Converts an optional property to a required one through a transformation `Option -> Type`.
*
* - `decode`: `none` as argument means the value is missing in the input.
* - `encode`: `none` as return value means the value will be missing in the output.
*
* @category PropertySignature
* @since 3.10.0
*/
export const optionalToRequired = (from, to, options) => makePropertySignature(new PropertySignatureTransformation(new FromPropertySignature(from.ast, true, true, {}, undefined), new ToPropertySignature(to.ast, false, true, {}, undefined), o => option_.some(options.decode(o)), option_.flatMap(options.encode)));
/**
* Converts an optional property to a required one through a transformation `Type -> Option`.
*
* - `decode`: `none` as return value means the value will be missing in the output.
* - `encode`: `none` as argument means the value is missing in the input.
*
* @category PropertySignature
* @since 3.10.0
*/
export const requiredToOptional = (from, to, options) => makePropertySignature(new PropertySignatureTransformation(new FromPropertySignature(from.ast, false, true, {}, undefined), new ToPropertySignature(to.ast, true, true, {}, undefined), option_.flatMap(options.decode), o => option_.some(options.encode(o))));
/**
* Converts an optional property to another optional property through a transformation `Option -> Option`.
*
* - `decode`:
* - `none` as argument means the value is missing in the input.
* - `none` as return value means the value will be missing in the output.
* - `encode`:
* - `none` as argument means the value is missing in the input.
* - `none` as return value means the value will be missing in the output.
*
* @category PropertySignature
* @since 3.10.0
*/
export const optionalToOptional = (from, to, options) => makePropertySignature(new PropertySignatureTransformation(new FromPropertySignature(from.ast, true, true, {}, undefined), new ToPropertySignature(to.ast, true, true, {}, undefined), options.decode, options.encode));
const optionalPropertySignatureAST = (self, options) => {
const isExact = options?.exact;
const defaultValue = options?.default;
const isNullable = options?.nullable;
const asOption = options?.as == "Option";
const asOptionEncode = options?.onNoneEncoding ? option_.orElse(options.onNoneEncoding) : identity;
if (isExact) {
if (defaultValue) {
if (isNullable) {
return withConstructorDefault(optionalToRequired(NullOr(self), typeSchema(self), {
decode: option_.match({
onNone: defaultValue,
onSome: a => a === null ? defaultValue() : a
}),
encode: option_.some
}), defaultValue).ast;
} else {
return withConstructorDefault(optionalToRequired(self, typeSchema(self), {
decode: option_.match({
onNone: defaultValue,
onSome: identity
}),
encode: option_.some
}), defaultValue).ast;
}
} else if (asOption) {
const to = OptionFromSelf_(typeSchema(self));
if (isNullable) {
return optionalToRequired(NullOr(self), to, {
decode: option_.filter(Predicate.isNotNull),
encode: asOptionEncode
}).ast;
} else {
return optionalToRequired(self, to, {
decode: identity,
encode: identity
}).ast;
}
} else {
if (isNullable) {
return optionalToOptional(NullOr(self), typeSchema(self), {
decode: option_.filter(Predicate.isNotNull),
encode: identity
}).ast;
} else {
return new PropertySignatureDeclaration(self.ast, true, true, {}, undefined);
}
}
} else {
if (defaultValue) {
if (isNullable) {
return withConstructorDefault(optionalToRequired(NullishOr(self), typeSchema(self), {
decode: option_.match({
onNone: defaultValue,
onSome: a => a == null ? defaultValue() : a
}),
encode: option_.some
}), defaultValue).ast;
} else {
return withConstructorDefault(optionalToRequired(UndefinedOr(self), typeSchema(self), {
decode: option_.match({
onNone: defaultValue,
onSome: a => a === undefined ? defaultValue() : a
}),
encode: option_.some
}), defaultValue).ast;
}
} else if (asOption) {
const to = OptionFromSelf_(typeSchema(self));
if (isNullable) {
return optionalToRequired(NullishOr(self), to, {
decode: option_.filter(a => a != null),
encode: asOptionEncode
}).ast;
} else {
return optionalToRequired(UndefinedOr(self), to, {
decode: option_.filter(Predicate.isNotUndefined),
encode: asOptionEncode
}).ast;
}
} else {
if (isNullable) {
return optionalToOptional(NullishOr(self), UndefinedOr(typeSchema(self)), {
decode: option_.filter(Predicate.isNotNull),
encode: identity
}).ast;
} else {
return new PropertySignatureDeclaration(UndefinedOr(self).ast, true, true, {}, undefined);
}
}
}
};
/**
* @category PropertySignature
* @since 3.10.0
*/
export const optional = self => {
const ast = self.ast === AST.undefinedKeyword || self.ast === AST.neverKeyword ? AST.undefinedKeyword : UndefinedOr(self).ast;
return new PropertySignatureWithFromImpl(new PropertySignatureDeclaration(ast, true, true, {}, undefined), self);
};
/**
* @category PropertySignature
* @since 3.10.0
*/
export const optionalWith = /*#__PURE__*/dual(args => isSchema(args[0]), (self, options) => {
return new PropertySignatureWithFromImpl(optionalPropertySignatureAST(self, options), self);
});
const preserveMissingMessageAnnotation = /*#__PURE__*/AST.pickAnnotations([AST.MissingMessageAnnotationId]);
const getDefaultTypeLiteralAST = (fields, records) => {
const ownKeys = Reflect.ownKeys(fields);
const pss = [];
if (ownKeys.length > 0) {
const from = [];
const to = [];
const transformations = [];
for (let i = 0; i < ownKeys.length; i++) {
const key = ownKeys[i];
const field = fields[key];
if (isPropertySignature(field)) {
const ast = field.ast;
switch (ast._tag) {
case "PropertySignatureDeclaration":
{
const type = ast.type;
const isOptional = ast.isOptional;
const toAnnotations = ast.annotations;
from.push(new AST.PropertySignature(key, type, isOptional, true, preserveMissingMessageAnnotation(ast)));
to.push(new AST.PropertySignature(key, AST.typeAST(type), isOptional, true, toAnnotations));
pss.push(new AST.PropertySignature(key, type, isOptional, true, toAnnotations));
break;
}
case "PropertySignatureTransformation":
{
const fromKey = ast.from.fromKey ?? key;
from.push(new AST.PropertySignature(fromKey, ast.from.type, ast.from.isOptional, true, ast.from.annotations));
to.push(new AST.PropertySignature(key, ast.to.type, ast.to.isOptional, true, ast.to.annotations));
transformations.push(new AST.PropertySignatureTransformation(fromKey, key, ast.decode, ast.encode));
break;
}
}
} else {
from.push(new AST.PropertySignature(key, field.ast, false, true));
to.push(new AST.PropertySignature(key, AST.typeAST(field.ast), false, true));
pss.push(new AST.PropertySignature(key, field.ast, false, true));
}
}
if (array_.isNonEmptyReadonlyArray(transformations)) {
const issFrom = [];
const issTo = [];
for (const r of records) {
const {
indexSignatures,
propertySignatures
} = AST.record(r.key.ast, r.value.ast);
propertySignatures.forEach(ps => {
from.push(ps);
to.push(new AST.PropertySignature(ps.name, AST.typeAST(ps.type), ps.isOptional, ps.isReadonly, ps.annotations));
});
indexSignatures.forEach(is => {
issFrom.push(is);
issTo.push(new AST.IndexSignature(is.parameter, AST.typeAST(is.type), is.isReadonly));
});
}
return new AST.Transformation(new AST.TypeLiteral(from, issFrom, {
[AST.AutoTitleAnnotationId]: "Struct (Encoded side)"
}), new AST.TypeLiteral(to, issTo, {
[AST.AutoTitleAnnotationId]: "Struct (Type side)"
}), new AST.TypeLiteralTransformation(transformations));
}
}
const iss = [];
for (const r of records) {
const {
indexSignatures,
propertySignatures
} = AST.record(r.key.ast, r.value.ast);
propertySignatures.forEach(ps => pss.push(ps));
indexSignatures.forEach(is => iss.push(is));
}
return new AST.TypeLiteral(pss, iss);
};
const lazilyMergeDefaults = (fields, out) => {
const ownKeys = Reflect.ownKeys(fields);
for (const key of ownKeys) {
const field = fields[key];
if (out[key] === undefined && isPropertySignature(field)) {
const ast = field.ast;
const defaultValue = ast._tag === "PropertySignatureDeclaration" ? ast.defaultValue : ast.to.defaultValue;
if (defaultValue !== undefined) {
out[key] = defaultValue();
}
}
}
return out;
};
function makeTypeLiteralClass(fields, records, ast = getDefaultTypeLiteralAST(fields, records)) {
return class TypeLiteralClass extends make(ast) {
static annotations(annotations) {
return makeTypeLiteralClass(this.fields, this.records, mergeSchemaAnnotations(this.ast, annotations));
}
static fields = {
...fields
};
static records = [...records];
static make = (props, options) => {
const propsWithDefaults = lazilyMergeDefaults(fields, {
...props
});
return getDisableValidationMakeOption(options) ? propsWithDefaults : ParseResult.validateSync(this)(propsWithDefaults);
};
static pick(...keys) {
return Struct(struct_.pick(fields, ...keys));
}
static omit(...keys) {
return Struct(struct_.omit(fields, ...keys));
}
};
}
export function Struct(fields, ...records) {
return makeTypeLiteralClass(fields, records);
}
/**
* Returns a property signature that represents a tag.
* A tag is a literal value that is used to distinguish between different types of objects.
* The tag is optional when using the `make` method.
*
* @example
* ```ts
* import * as assert from "node:assert"
* import { Schema } from "effect"
*
* const User = Schema.Struct({
* _tag: Schema.tag("User"),
* name: Schema.String,
* age: Schema.Number
* })
*
* assert.deepStrictEqual(User.make({ name: "John", age: 44 }), { _tag: "User", name: "John", age: 44 })
* ```
*
* @see {@link TaggedStruct}
*
* @since 3.10.0
*/
export const tag = tag => Literal(tag).pipe(propertySignature, withConstructorDefault(() => tag));
/**
* A tagged struct is a struct that has a tag property that is used to distinguish between different types of objects.
*
* The tag is optional when using the `make` method.
*
* @example
* ```ts
* import * as assert from "node:assert"
* import { Schema } from "effect"
*
* const User = Schema.TaggedStruct("User", {
* name: Schema.String,
* age: Schema.Number
* })
*
* assert.deepStrictEqual(User.make({ name: "John", age: 44 }), { _tag: "User", name: "John", age: 44 })
* ```
*
* @category constructors
* @since 3.10.0
*/
export const TaggedStruct = (value, fields) => Struct({
_tag: tag(value),
...fields
});
function makeRecordClass(key, value, ast) {
return class RecordClass extends makeTypeLiteralClass({}, [{
key,
value
}], ast) {
static annotations(annotations) {
return makeRecordClass(key, value, mergeSchemaAnnotations(this.ast, annotations));
}
static key = key;
static value = value;
};
}
/**
* @category constructors
* @since 3.10.0
*/
export const Record = options => makeRecordClass(options.key, options.value);
/**
* @category struct transformations
* @since 3.10.0
*/
export const pick = (...keys) => self => make(AST.pick(self.ast, keys));
/**
* @category struct transformations
* @since 3.10.0
*/
export const omit = (...keys) => self => make(AST.omit(self.ast, keys));
/**
* Given a schema `Schema<A, I, R>` and a key `key: K`, this function extracts a specific field from the `A` type,
* producing a new schema that represents a transformation from the `{ readonly [key]: I[K] }` type to `A[K]`.
*
* @example
* ```ts
* import * as Schema from "effect/Schema"
*
* // ---------------------------------------------
* // use case: pull out a single field from a
* // struct through a transformation
* // ---------------------------------------------
*
* const mytable = Schema.Struct({
* column1: Schema.NumberFromString,
* column2: Schema.Number
* })
*
* // const pullOutColumn: S.Schema<number, {
* // readonly column1: string;
* // }, never>
* const pullOutColumn = mytable.pipe(Schema.pluck("column1"))
*
* console.log(Schema.decodeUnknownEither(Schema.Array(pullOutColumn))([{ column1: "1", column2: 100 }, { column1: "2", column2: 300 }]))
* // Output: { _id: 'Either', _tag: 'Right', right: [ 1, 2 ] }
* ```
*
* @category struct transformations
* @since 3.10.0
*/
export const pluck = /*#__PURE__*/dual(2, (schema, key) => {
const ps = AST.getPropertyKeyIndexedAccess(AST.typeAST(schema.ast), key);
const value = make(ps.isOptional ? AST.orUndefined(ps.type) : ps.type);
const out = transform(schema.pipe(pick(key)), value, {
strict: true,
decode: i => i[key],
encode: a => ps.isOptional && a === undefined ? {} : {
[key]: a
}
});
return out;
});
function makeBrandClass(from, ast) {
return class BrandClass extends make(ast) {
static annotations(annotations) {
return makeBrandClass(this.from, mergeSchemaAnnotations(this.ast, annotations));
}
static make = (a, options) => {
return getDisableValidationMakeOption(options) ? a : ParseResult.validateSync(this)(a);
};
static from = from;
};
}
/**
* Returns a nominal branded schema by applying a brand to a given schema.
*
* ```
* Schema<A> + B -> Schema<A & Brand<B>>
* ```
*
* @example
* ```ts
* import * as Schema from "effect/Schema"
*
* const Int = Schema.Number.pipe(Schema.int(), Schema.brand("Int"))
* type Int = Schema.Schema.Type<typeof Int> // number & Brand<"Int">
* ```
*
* @category branding
* @since 3.10.0
*/
export const brand = (brand, annotations) => self => {
const annotation = option_.match(AST.getBrandAnnotation(self.ast), {
onNone: () => [brand],
onSome: brands => [...brands, brand]
});
const ast = AST.annotations(self.ast, toASTAnnotations({
[AST.BrandAnnotationId]: annotation,
...annotations
}));
return makeBrandClass(self, ast);
};
/**
* @category combinators
* @since 3.10.0
*/
export const partial = self => make(AST.partial(self.ast));
/**
* @category combinators
* @since 3.10.0
*/
export const partialWith = /*#__PURE__*/dual(args => isSchema(args[0]), (self, options) => make(AST.partial(self.ast, options)));
/**
* @category combinators
* @since 3.10.0
*/
export const required = self => make(AST.required(self.ast));
/**
* Creates a new schema with shallow mutability applied to its properties.
*
* @category combinators
* @since 3.10.0
*/
export const mutable = schema => make(AST.mutable(schema.ast));
const intersectTypeLiterals = (x, y, path) => {
if (AST.isTypeLiteral(x) && AST.isTypeLiteral(y)) {
const propertySignatures = [...x.propertySignatures];
for (const ps of y.propertySignatures) {
const name = ps.name;
const i = propertySignatures.findIndex(ps => ps.name === name);
if (i === -1) {
propertySignatures.push(ps);
} else {
const {
isOptional,
type
} = propertySignatures[i];
propertySignatures[i] = new AST.PropertySignature(name, extendAST(type, ps.type, path.concat(name)), isOptional, true);
}
}
return new AST.TypeLiteral(propertySignatures, x.indexSignatures.concat(y.indexSignatures));
}
throw new Error(errors_.getSchemaExtendErrorMessage(x, y, path));
};
const preserveRefinementAnnotations = /*#__PURE__*/AST.omitAnnotations([AST.IdentifierAnnotationId]);
const addRefinementToMembers = (refinement, asts) => asts.map(ast => new AST.Refinement(ast, refinement.filter, preserveRefinementAnnotations(refinement)));
const extendAST = (x, y, path) => AST.Union.make(intersectUnionMembers([x], [y], path));
const getTypes = ast => AST.isUnion(ast) ? ast.types : [ast];
const intersectUnionMembers = (xs, ys, path) => array_.flatMap(xs, x => array_.flatMap(ys, y => {
switch (y._tag) {
case "Literal":
{
if (Predicate.isString(y.literal) && AST.isStringKeyword(x) || Predicate.isNumber(y.literal) && AST.isNumberKeyword(x) || Predicate.isBoolean(y.literal) && AST.isBooleanKeyword(x)) {
return [y];
}
break;
}
case "StringKeyword":
{
if (y === AST.stringKeyword) {
if (AST.isStringKeyword(x) || AST.isLiteral(x) && Predicate.isString(x.literal)) {
return [x];
} else if (AST.isRefinement(x)) {
return addRefinementToMembers(x, intersectUnionMembers(getTypes(x.from),