From 8475a2cb7edddee5c7aee61e3c2859c8af9123c2 Mon Sep 17 00:00:00 2001 From: Andrew M Date: Thu, 10 Oct 2024 14:35:57 -0400 Subject: [PATCH 01/12] add TSubscriptionRef (#2725) Co-authored-by: Michael Arnaldi Co-authored-by: Tim --- .changeset/cold-cougars-pretend.md | 5 + .changeset/shiny-squids-sell.md | 5 + packages/effect/src/Stream.ts | 18 ++ packages/effect/src/TPubSub.ts | 9 + packages/effect/src/TQueue.ts | 14 +- packages/effect/src/TRef.ts | 3 +- packages/effect/src/TSubscriptionRef.ts | 192 ++++++++++++ packages/effect/src/index.ts | 5 + packages/effect/src/internal/stm/core.ts | 3 +- packages/effect/src/internal/stm/tPubSub.ts | 1 + packages/effect/src/internal/stm/tQueue.ts | 4 +- packages/effect/src/internal/stm/tRef.ts | 9 +- .../src/internal/stm/tSubscriptionRef.ts | 286 ++++++++++++++++++ packages/effect/src/internal/stream.ts | 28 ++ packages/effect/test/TSubscriptionRef.test.ts | 148 +++++++++ 15 files changed, 716 insertions(+), 14 deletions(-) create mode 100644 .changeset/cold-cougars-pretend.md create mode 100644 .changeset/shiny-squids-sell.md create mode 100644 packages/effect/src/TSubscriptionRef.ts create mode 100644 packages/effect/src/internal/stm/tSubscriptionRef.ts create mode 100644 packages/effect/test/TSubscriptionRef.test.ts diff --git a/.changeset/cold-cougars-pretend.md b/.changeset/cold-cougars-pretend.md new file mode 100644 index 0000000000..df705b7505 --- /dev/null +++ b/.changeset/cold-cougars-pretend.md @@ -0,0 +1,5 @@ +--- +"effect": minor +--- + +add TSubscriptionRef diff --git a/.changeset/shiny-squids-sell.md b/.changeset/shiny-squids-sell.md new file mode 100644 index 0000000000..0e68f8a95f --- /dev/null +++ b/.changeset/shiny-squids-sell.md @@ -0,0 +1,5 @@ +--- +"effect": minor +--- + +add Stream.fromTQueue & Stream.fromTPubSub diff --git a/packages/effect/src/Stream.ts b/packages/effect/src/Stream.ts index 9a4dc08439..bbcf0bd702 100644 --- a/packages/effect/src/Stream.ts +++ b/packages/effect/src/Stream.ts @@ -29,6 +29,8 @@ import type * as Sink from "./Sink.js" import type * as Emit from "./StreamEmit.js" import type * as HaltStrategy from "./StreamHaltStrategy.js" import type * as Take from "./Take.js" +import type { TPubSub } from "./TPubSub.js" +import type { TDequeue } from "./TQueue.js" import type * as Tracer from "./Tracer.js" import type { Covariant, NoInfer, TupleOf } from "./Types.js" import type * as Unify from "./Unify.js" @@ -2013,6 +2015,14 @@ export const fromPubSub: { ): Stream } = internal.fromPubSub +/** + * Creates a stream from a subscription to a `TPubSub`. + * + * @since 3.10.0 + * @category constructors + */ +export const fromTPubSub: (pubsub: TPubSub) => Stream = internal.fromTPubSub + /** * Creates a new `Stream` from an iterable collection of values. * @@ -2094,6 +2104,14 @@ export const fromQueue: ( } ) => Stream = internal.fromQueue +/** + * Creates a stream from a TQueue of values + * + * @since 3.10.0 + * @category constructors + */ +export const fromTQueue: (queue: TDequeue) => Stream = internal.fromTQueue + /** * Creates a stream from a `ReadableStream`. * diff --git a/packages/effect/src/TPubSub.ts b/packages/effect/src/TPubSub.ts index 1a15cfa1b6..f6b7b045ce 100644 --- a/packages/effect/src/TPubSub.ts +++ b/packages/effect/src/TPubSub.ts @@ -107,6 +107,15 @@ export const isEmpty: (self: TPubSub) => STM.STM = internal.isEmp */ export const isFull: (self: TPubSub) => STM.STM = internal.isFull +/** + * Interrupts any fibers that are suspended on `offer` or `take`. Future calls + * to `offer*` and `take*` will be interrupted immediately. + * + * @since 2.0.0 + * @category utils + */ +export const shutdown: (self: TPubSub) => STM.STM = internal.shutdown + /** * Returns `true` if `shutdown` has been called, otherwise returns `false`. * diff --git a/packages/effect/src/TQueue.ts b/packages/effect/src/TQueue.ts index 800555b2c8..e8b9b465fa 100644 --- a/packages/effect/src/TQueue.ts +++ b/packages/effect/src/TQueue.ts @@ -206,7 +206,7 @@ export const isTEnqueue: (u: unknown) => u is TEnqueue = internal.isTEn * @since 2.0.0 * @category mutations */ -export const awaitShutdown: (self: TQueue) => STM.STM = internal.awaitShutdown +export const awaitShutdown: (self: TDequeue | TEnqueue) => STM.STM = internal.awaitShutdown /** * Creates a bounded queue with the back pressure strategy. The queue will @@ -226,7 +226,7 @@ export const bounded: (requestedCapacity: number) => STM.STM> = int * @since 2.0.0 * @category getters */ -export const capacity: (self: TQueue) => number = internal.capacity +export const capacity: (self: TDequeue | TEnqueue) => number = internal.capacity /** * Creates a bounded queue with the dropping strategy. The queue will drop new @@ -245,7 +245,7 @@ export const dropping: (requestedCapacity: number) => STM.STM> = in * @since 2.0.0 * @category getters */ -export const isEmpty: (self: TQueue) => STM.STM = internal.isEmpty +export const isEmpty: (self: TDequeue | TEnqueue) => STM.STM = internal.isEmpty /** * Returns `true` if the `TQueue` contains at least one element, `false` @@ -254,7 +254,7 @@ export const isEmpty: (self: TQueue) => STM.STM = internal.isEmpt * @since 2.0.0 * @category getters */ -export const isFull: (self: TQueue) => STM.STM = internal.isFull +export const isFull: (self: TDequeue | TEnqueue) => STM.STM = internal.isFull /** * Returns `true` if `shutdown` has been called, otherwise returns `false`. @@ -262,7 +262,7 @@ export const isFull: (self: TQueue) => STM.STM = internal.isFull * @since 2.0.0 * @category getters */ -export const isShutdown: (self: TQueue) => STM.STM = internal.isShutdown +export const isShutdown: (self: TDequeue | TEnqueue) => STM.STM = internal.isShutdown /** * Places one value in the queue. @@ -345,7 +345,7 @@ export const seek: { * @since 2.0.0 * @category mutations */ -export const shutdown: (self: TQueue) => STM.STM = internal.shutdown +export const shutdown: (self: TDequeue | TEnqueue) => STM.STM = internal.shutdown /** * Retrieves the size of the queue, which is equal to the number of elements @@ -355,7 +355,7 @@ export const shutdown: (self: TQueue) => STM.STM = internal.shutdown * @since 2.0.0 * @category getters */ -export const size: (self: TQueue) => STM.STM = internal.size +export const size: (self: TDequeue | TEnqueue) => STM.STM = internal.size /** * Creates a bounded queue with the sliding strategy. The queue will add new diff --git a/packages/effect/src/TRef.ts b/packages/effect/src/TRef.ts index 5b98a7c653..1dd83e9c4e 100644 --- a/packages/effect/src/TRef.ts +++ b/packages/effect/src/TRef.ts @@ -7,6 +7,7 @@ import type * as TxnId from "./internal/stm/stm/txnId.js" import type * as Versioned from "./internal/stm/stm/versioned.js" import * as internal from "./internal/stm/tRef.js" import type * as Option from "./Option.js" +import type { Pipeable } from "./Pipeable.js" import type * as STM from "./STM.js" import type * as Types from "./Types.js" @@ -34,7 +35,7 @@ export type TRefTypeId = typeof TRefTypeId * @since 2.0.0 * @category models */ -export interface TRef extends TRef.Variance { +export interface TRef extends TRef.Variance, Pipeable { /** * Note: the method is unbound, exposed only for potential extensions. */ diff --git a/packages/effect/src/TSubscriptionRef.ts b/packages/effect/src/TSubscriptionRef.ts new file mode 100644 index 0000000000..dfc6ccb5ff --- /dev/null +++ b/packages/effect/src/TSubscriptionRef.ts @@ -0,0 +1,192 @@ +/** + * @since 3.10.0 + */ +import type * as Effect from "./Effect.js" +import * as internal from "./internal/stm/tSubscriptionRef.js" +import type * as Option from "./Option.js" +import type * as Scope from "./Scope.js" +import type * as STM from "./STM.js" +import type * as Stream from "./Stream.js" +import type * as TPubSub from "./TPubSub.js" +import type * as TQueue from "./TQueue.js" +import type * as TRef from "./TRef.js" +import type * as Types from "./Types.js" + +/** + * @since 3.10.0 + * @category symbols + */ +export const TSubscriptionRefTypeId: unique symbol = internal.TSubscriptionRefTypeId + +/** + * @since 3.10.0 + * @category symbols + */ +export type TSubscriptionRefTypeId = typeof TSubscriptionRefTypeId + +/** + * A `TSubscriptionRef` is a `TRef` that can be subscribed to in order to + * receive a `TDequeue` of the current value and all committed changes to the value. + * + * @since 3.10.0 + * @category models + */ +export interface TSubscriptionRef extends TSubscriptionRef.Variance, TRef.TRef { + /** @internal */ + readonly ref: TRef.TRef + /** @internal */ + readonly pubsub: TPubSub.TPubSub + /** @internal */ + modify(f: (a: A) => readonly [B, A]): STM.STM + + /** + * A TDequeue containing the current value of the `Ref` as well as all changes + * to that value. + */ + readonly changes: STM.STM> +} + +/** + * @since 3.10.0 + */ +export declare namespace TSubscriptionRef { + /** + * @since 3.10.0 + * @category models + */ + export interface Variance { + readonly [TSubscriptionRefTypeId]: { + readonly _A: Types.Invariant + } + } +} + +/** + * @since 3.10.0 + * @category mutations + */ +export const get: (self: TSubscriptionRef) => STM.STM = internal.get + +/** + * @since 3.10.0 + * @category mutations + */ +export const getAndSet: { + (value: A): (self: TSubscriptionRef) => STM.STM + (self: TSubscriptionRef, value: A): STM.STM +} = internal.getAndSet + +/** + * @since 3.10.0 + * @category mutations + */ +export const getAndUpdate: { + (f: (a: A) => A): (self: TSubscriptionRef) => STM.STM + (self: TSubscriptionRef, f: (a: A) => A): STM.STM +} = internal.getAndUpdate + +/** + * @since 3.10.0 + * @category mutations + */ +export const getAndUpdateSome: { + (f: (a: A) => Option.Option): (self: TSubscriptionRef) => STM.STM + (self: TSubscriptionRef, f: (a: A) => Option.Option): STM.STM +} = internal.getAndUpdateSome + +/** + * @since 3.10.0 + * @category constructors + */ +export const make: (value: A) => STM.STM> = internal.make + +/** + * @since 3.10.0 + * @category mutations + */ +export const modify: { + (f: (a: A) => readonly [B, A]): (self: TSubscriptionRef) => STM.STM + (self: TSubscriptionRef, f: (a: A) => readonly [B, A]): STM.STM +} = internal.modify + +/** + * @since 3.10.0 + * @category mutations + */ +export const modifySome: { + (fallback: B, f: (a: A) => Option.Option): (self: TSubscriptionRef) => STM.STM + (self: TSubscriptionRef, fallback: B, f: (a: A) => Option.Option): STM.STM +} = internal.modifySome + +/** + * @since 3.10.0 + * @category mutations + */ +export const set: { + (value: A): (self: TSubscriptionRef) => STM.STM + (self: TSubscriptionRef, value: A): STM.STM +} = internal.set + +/** + * @since 3.10.0 + * @category mutations + */ +export const setAndGet: { + (value: A): (self: TSubscriptionRef) => STM.STM + (self: TSubscriptionRef, value: A): STM.STM +} = internal.setAndGet + +/** + * @since 3.10.0 + * @category mutations + */ +export const update: { + (f: (a: A) => A): (self: TSubscriptionRef) => STM.STM + (self: TSubscriptionRef, f: (a: A) => A): STM.STM +} = internal.update + +/** + * @since 3.10.0 + * @category mutations + */ +export const updateAndGet: { + (f: (a: A) => A): (self: TSubscriptionRef) => STM.STM + (self: TSubscriptionRef, f: (a: A) => A): STM.STM +} = internal.updateAndGet + +/** + * @since 3.10.0 + * @category mutations + */ +export const updateSome: { + (f: (a: A) => Option.Option): (self: TSubscriptionRef) => STM.STM + (self: TSubscriptionRef, f: (a: A) => Option.Option): STM.STM +} = internal.updateSome + +/** + * @since 3.10.0 + * @category mutations + */ +export const updateSomeAndGet: { + (f: (a: A) => Option.Option): (self: TSubscriptionRef) => STM.STM + (self: TSubscriptionRef, f: (a: A) => Option.Option): STM.STM +} = internal.updateSomeAndGet + +/** + * @since 3.10.0 + * @category mutations + */ +export const changesScoped: (self: TSubscriptionRef) => Effect.Effect, never, Scope.Scope> = + internal.changesScoped + +/** + * @since 3.10.0 + * @category mutations + */ +export const changesStream: (self: TSubscriptionRef) => Stream.Stream = internal.changesStream + +/** + * @since 3.10.0 + * @category mutations + */ +export const changes: (self: TSubscriptionRef) => STM.STM> = (self) => self.changes diff --git a/packages/effect/src/index.ts b/packages/effect/src/index.ts index cb90a4ac72..a57bd513ed 100644 --- a/packages/effect/src/index.ts +++ b/packages/effect/src/index.ts @@ -895,6 +895,11 @@ export * as TSemaphore from "./TSemaphore.js" */ export * as TSet from "./TSet.js" +/** + * @since 3.10.0 + */ +export * as TSubscriptionRef from "./TSubscriptionRef.js" + /** * @since 2.0.0 */ diff --git a/packages/effect/src/internal/stm/core.ts b/packages/effect/src/internal/stm/core.ts index 7c0f558715..1203090752 100644 --- a/packages/effect/src/internal/stm/core.ts +++ b/packages/effect/src/internal/stm/core.ts @@ -15,11 +15,10 @@ import { pipeArguments } from "../../Pipeable.js" import { hasProperty } from "../../Predicate.js" import type * as Scheduler from "../../Scheduler.js" import type * as STM from "../../STM.js" -import { StreamTypeId } from "../../Stream.js" import { YieldWrap } from "../../Utils.js" import { ChannelTypeId } from "../core-stream.js" import { withFiberRuntime } from "../core.js" -import { effectVariance } from "../effectable.js" +import { effectVariance, StreamTypeId } from "../effectable.js" import { OP_COMMIT } from "../opCodes/effect.js" import { SingleShotGen } from "../singleShotGen.js" import { SinkTypeId } from "../sink.js" diff --git a/packages/effect/src/internal/stm/tPubSub.ts b/packages/effect/src/internal/stm/tPubSub.ts index 089be12ce5..b838ddb90a 100644 --- a/packages/effect/src/internal/stm/tPubSub.ts +++ b/packages/effect/src/internal/stm/tPubSub.ts @@ -201,6 +201,7 @@ class TPubSubSubscriptionImpl implements TQueue.TDequeue { capacity(): number { return this.requestedCapacity } + size: STM.STM = core.withSTMRuntime((runtime) => { let currentSubscriberHead = tRef.unsafeGet(this.subscriberHead, runtime.journal) if (currentSubscriberHead === undefined) { diff --git a/packages/effect/src/internal/stm/tQueue.ts b/packages/effect/src/internal/stm/tQueue.ts index 90039374f3..8ca27b9865 100644 --- a/packages/effect/src/internal/stm/tQueue.ts +++ b/packages/effect/src/internal/stm/tQueue.ts @@ -3,7 +3,7 @@ import * as Chunk from "../../Chunk.js" import { dual, pipe } from "../../Function.js" import * as Option from "../../Option.js" import { hasProperty, type Predicate } from "../../Predicate.js" -import * as STM from "../../STM.js" +import type * as STM from "../../STM.js" import type * as TQueue from "../../TQueue.js" import type * as TRef from "../../TRef.js" import * as core from "./core.js" @@ -99,7 +99,7 @@ class TQueueImpl implements TQueue.TQueue { size: STM.STM = core.withSTMRuntime((runtime) => { const queue = tRef.unsafeGet(this.ref, runtime.journal) if (queue === undefined) { - return STM.interruptAs(runtime.fiberId) + return core.interruptAs(runtime.fiberId) } return core.succeed(queue.length) }) diff --git a/packages/effect/src/internal/stm/tRef.ts b/packages/effect/src/internal/stm/tRef.ts index c780509360..3162fc252b 100644 --- a/packages/effect/src/internal/stm/tRef.ts +++ b/packages/effect/src/internal/stm/tRef.ts @@ -1,5 +1,7 @@ import { dual } from "../../Function.js" import * as Option from "../../Option.js" +import type { Pipeable } from "../../Pipeable.js" +import { pipeArguments } from "../../Pipeable.js" import type * as STM from "../../STM.js" import type * as TRef from "../../TRef.js" import * as core from "./core.js" @@ -16,13 +18,13 @@ export const TRefTypeId: TRef.TRefTypeId = Symbol.for( TRefSymbolKey ) as TRef.TRefTypeId -const tRefVariance = { +export const tRefVariance = { /* c8 ignore next */ _A: (_: any) => _ } /** @internal */ -export class TRefImpl implements TRef.TRef { +export class TRefImpl implements TRef.TRef, Pipeable { readonly [TRefTypeId] = tRefVariance /** @internal */ todos: Map @@ -40,6 +42,9 @@ export class TRefImpl implements TRef.TRef { return retValue }) } + pipe() { + return pipeArguments(this, arguments) + } } /** @internal */ diff --git a/packages/effect/src/internal/stm/tSubscriptionRef.ts b/packages/effect/src/internal/stm/tSubscriptionRef.ts new file mode 100644 index 0000000000..94a4924055 --- /dev/null +++ b/packages/effect/src/internal/stm/tSubscriptionRef.ts @@ -0,0 +1,286 @@ +import * as Effect from "../../Effect.js" +import { dual, pipe } from "../../Function.js" +import * as Option from "../../Option.js" +import { pipeArguments } from "../../Pipeable.js" +import * as STM from "../../STM.js" +import * as TPubSub from "../../TPubSub.js" +import * as TQueue from "../../TQueue.js" +import * as TRef from "../../TRef.js" +import type * as TSubscriptionRef from "../../TSubscriptionRef.js" +import * as stream from "../stream.js" +import { tDequeueVariance } from "./tQueue.js" +import { tRefVariance } from "./tRef.js" + +/** @internal */ +const TSubscriptionRefSymbolKey = "effect/TSubscriptionRef" + +/** @internal */ +export const TSubscriptionRefTypeId: TSubscriptionRef.TSubscriptionRefTypeId = Symbol.for( + TSubscriptionRefSymbolKey +) as TSubscriptionRef.TSubscriptionRefTypeId + +const TSubscriptionRefVariance = { + /* c8 ignore next */ + _A: (_: any) => _ +} + +class TDequeueMerge implements TQueue.TDequeue { + [TQueue.TDequeueTypeId] = tDequeueVariance + + constructor( + readonly first: TQueue.TDequeue, + readonly second: TQueue.TDequeue + ) {} + + peek: STM.STM = STM.gen(this, function*() { + const first = yield* this.peekOption + if (first._tag === "Some") { + return first.value + } + return yield* STM.retry + }) + + peekOption: STM.STM> = STM.gen(this, function*() { + const first = yield* this.first.peekOption + if (first._tag === "Some") { + return first + } + const second = yield* this.second.peekOption + if (second._tag === "Some") { + return second + } + return Option.none() + }) + + take: STM.STM = STM.gen(this, function*() { + if (!(yield* this.first.isEmpty)) { + return yield* this.first.take + } + if (!(yield* this.second.isEmpty)) { + return yield* this.second.take + } + return yield* STM.retry + }) + + takeAll: STM.STM> = STM.gen(this, function*() { + return [...yield* this.first.takeAll, ...yield* this.second.takeAll] + }) + + takeUpTo(max: number): STM.STM> { + return STM.gen(this, function*() { + const first = yield* this.first.takeUpTo(max) + if (first.length >= max) { + return first + } + return [...first, ...yield* this.second.takeUpTo(max - first.length)] + }) + } + + capacity(): number { + return this.first.capacity() + this.second.capacity() + } + + size: STM.STM = STM.gen(this, function*() { + return (yield* this.first.size) + (yield* this.second.size) + }) + + isFull: STM.STM = STM.gen(this, function*() { + return (yield* this.first.isFull) && (yield* this.second.isFull) + }) + + isEmpty: STM.STM = STM.gen(this, function*() { + return (yield* this.first.isEmpty) && (yield* this.second.isEmpty) + }) + + shutdown: STM.STM = STM.gen(this, function*() { + yield* this.first.shutdown + yield* this.second.shutdown + }) + + isShutdown: STM.STM = STM.gen(this, function*() { + return (yield* this.first.isShutdown) && (yield* this.second.isShutdown) + }) + + awaitShutdown: STM.STM = STM.gen(this, function*() { + yield* this.first.awaitShutdown + yield* this.second.awaitShutdown + }) +} + +/** @internal */ +class TSubscriptionRefImpl implements TSubscriptionRef.TSubscriptionRef { + readonly [TSubscriptionRefTypeId] = TSubscriptionRefVariance + readonly [TRef.TRefTypeId] = tRefVariance + + constructor( + readonly ref: TRef.TRef, + readonly pubsub: TPubSub.TPubSub + ) {} + + get todos() { + return this.ref.todos + } + + get versioned() { + return this.ref.versioned + } + + pipe() { + return pipeArguments(this, arguments) + } + + get changes(): STM.STM> { + return STM.gen(this, function*() { + const first = yield* TQueue.unbounded() + yield* TQueue.offer(first, yield* TRef.get(this.ref)) + return new TDequeueMerge(first, yield* TPubSub.subscribe(this.pubsub)) + }) + } + + modify(f: (a: A) => readonly [B, A]): STM.STM { + return pipe( + TRef.get(this.ref), + STM.map(f), + STM.flatMap(([b, a]) => + pipe( + TRef.set(this.ref, a), + STM.as(b), + STM.zipLeft(TPubSub.publish(this.pubsub, a)) + ) + ) + ) + } +} + +/** @internal */ +export const make = (value: A): STM.STM> => + pipe( + STM.all([ + TPubSub.unbounded(), + TRef.make(value) + ]), + STM.map(([pubsub, ref]) => new TSubscriptionRefImpl(ref, pubsub)) + ) + +/** @internal */ +export const get = (self: TSubscriptionRef.TSubscriptionRef) => TRef.get(self.ref) + +/** @internal */ +export const set = dual< + (value: A) => (self: TSubscriptionRef.TSubscriptionRef) => STM.STM, + (self: TSubscriptionRef.TSubscriptionRef, value: A) => STM.STM +>( + 2, + (self: TSubscriptionRef.TSubscriptionRef, value: A): STM.STM => + self.modify((): [void, A] => [void 0, value]) +) + +/** @internal */ +export const getAndSet = dual< + (value: A) => (self: TSubscriptionRef.TSubscriptionRef) => STM.STM, + (self: TSubscriptionRef.TSubscriptionRef, value: A) => STM.STM +>(2, (self, value) => self.modify((a) => [a, value])) + +/** @internal */ +export const getAndUpdate = dual< + (f: (a: A) => A) => (self: TSubscriptionRef.TSubscriptionRef) => STM.STM, + (self: TSubscriptionRef.TSubscriptionRef, f: (a: A) => A) => STM.STM +>(2, (self, f) => self.modify((a) => [a, f(a)])) + +/** @internal */ +export const getAndUpdateSome = dual< + (f: (a: A) => Option.Option) => (self: TSubscriptionRef.TSubscriptionRef) => STM.STM, + (self: TSubscriptionRef.TSubscriptionRef, f: (a: A) => Option.Option) => STM.STM +>(2, (self, f) => + self.modify((a) => + Option.match(f(a), { + onNone: () => [a, a], + onSome: (b) => [a, b] + }) + )) + +/** @internal */ +export const setAndGet = dual< + (value: A) => (self: TSubscriptionRef.TSubscriptionRef) => STM.STM, + (self: TSubscriptionRef.TSubscriptionRef, value: A) => STM.STM +>(2, (self, value) => self.modify(() => [value, value])) + +/** @internal */ +export const modify = dual< + (f: (a: A) => readonly [B, A]) => (self: TSubscriptionRef.TSubscriptionRef) => STM.STM, + (self: TSubscriptionRef.TSubscriptionRef, f: (a: A) => readonly [B, A]) => STM.STM +>(2, (self, f) => self.modify(f)) + +/** @internal */ +export const modifySome = dual< + ( + fallback: B, + f: (a: A) => Option.Option + ) => (self: TSubscriptionRef.TSubscriptionRef) => STM.STM, + ( + self: TSubscriptionRef.TSubscriptionRef, + fallback: B, + f: (a: A) => Option.Option + ) => STM.STM +>(3, (self, fallback, f) => + self.modify((a) => + Option.match(f(a), { + onNone: () => [fallback, a], + onSome: (b) => b + }) + )) + +/** @internal */ +export const update = dual< + (f: (a: A) => A) => (self: TSubscriptionRef.TSubscriptionRef) => STM.STM, + (self: TSubscriptionRef.TSubscriptionRef, f: (a: A) => A) => STM.STM +>(2, (self, f) => self.modify((a) => [void 0, f(a)])) + +/** @internal */ +export const updateAndGet = dual< + (f: (a: A) => A) => (self: TSubscriptionRef.TSubscriptionRef) => STM.STM, + (self: TSubscriptionRef.TSubscriptionRef, f: (a: A) => A) => STM.STM +>(2, (self, f) => + self.modify((a) => { + const b = f(a) + return [b, b] + })) + +/** @internal */ +export const updateSome = dual< + (f: (a: A) => Option.Option) => (self: TSubscriptionRef.TSubscriptionRef) => STM.STM, + (self: TSubscriptionRef.TSubscriptionRef, f: (a: A) => Option.Option) => STM.STM +>( + 2, + (self, f) => + self.modify((a) => [ + void 0, + Option.match(f(a), { + onNone: () => a, + onSome: (b) => b + }) + ]) +) + +/** @internal */ +export const updateSomeAndGet = dual< + (f: (a: A) => Option.Option) => (self: TSubscriptionRef.TSubscriptionRef) => STM.STM, + (self: TSubscriptionRef.TSubscriptionRef, f: (a: A) => Option.Option) => STM.STM +>( + 2, + (self, f) => + self.modify((a) => + Option.match(f(a), { + onNone: () => [a, a], + onSome: (b) => [b, b] + }) + ) +) + +/** @internal */ +export const changesScoped = (self: TSubscriptionRef.TSubscriptionRef) => + Effect.acquireRelease(self.changes, TQueue.shutdown) + +/** @internal */ +export const changesStream = (self: TSubscriptionRef.TSubscriptionRef) => + stream.unwrap(Effect.map(self.changes, stream.fromTQueue)) diff --git a/packages/effect/src/internal/stream.ts b/packages/effect/src/internal/stream.ts index 3e677bfac9..7680583983 100644 --- a/packages/effect/src/internal/stream.ts +++ b/packages/effect/src/internal/stream.ts @@ -31,6 +31,8 @@ import type * as Stream from "../Stream.js" import type * as Emit from "../StreamEmit.js" import * as HaltStrategy from "../StreamHaltStrategy.js" import type * as Take from "../Take.js" +import * as TPubSub from "../TPubSub.js" +import * as TQueue from "../TQueue.js" import type * as Tracer from "../Tracer.js" import * as Tuple from "../Tuple.js" import type { NoInfer, TupleOf } from "../Types.js" @@ -3133,6 +3135,14 @@ export const fromPubSub: { return options?.shutdown ? ensuring(stream, PubSub.shutdown(pubsub)) : stream } +/** @internal */ +export const fromTPubSub = (pubsub: TPubSub.TPubSub): Stream.Stream => { + return unwrapScoped(Effect.map( + TPubSub.subscribeScoped(pubsub), + (queue) => fromTQueue(queue) + )) +} + /** @internal */ export const fromIterable = (iterable: Iterable): Stream.Stream => suspend(() => @@ -3224,6 +3234,24 @@ export const fromQueue = ( options?.shutdown ? ensuring(Queue.shutdown(queue)) : identity ) +/** @internal */ +export const fromTQueue = (queue: TQueue.TDequeue): Stream.Stream => + pipe( + TQueue.take(queue), + Effect.map(Chunk.of), + Effect.catchAllCause((cause) => + pipe( + TQueue.isShutdown(queue), + Effect.flatMap((isShutdown) => + isShutdown && Cause.isInterrupted(cause) ? + pull.end() : + pull.failCause(cause) + ) + ) + ), + repeatEffectChunkOption + ) + /** @internal */ export const fromSchedule = (schedule: Schedule.Schedule): Stream.Stream => pipe( diff --git a/packages/effect/test/TSubscriptionRef.test.ts b/packages/effect/test/TSubscriptionRef.test.ts new file mode 100644 index 0000000000..2cc54e89b9 --- /dev/null +++ b/packages/effect/test/TSubscriptionRef.test.ts @@ -0,0 +1,148 @@ +import { Chunk, Deferred, Effect, Equal, Exit, Fiber, pipe, Random, STM, Stream } from "effect" +import * as Number from "effect/Number" +import * as it from "effect/test/utils/extend" +import * as TSubscriptionRef from "effect/TSubscriptionRef" +import { assert, describe } from "vitest" + +describe.concurrent("TSubscriptionRef", () => { + it.effect("only emits comitted values", () => + Effect.gen(function*($) { + const subscriptionRef = yield* $(TSubscriptionRef.make(0)) + + const transaction = pipe( + TSubscriptionRef.update(subscriptionRef, (n) => n + 1), + STM.tap(() => TSubscriptionRef.update(subscriptionRef, (n) => n + 1)) + ) + + const subscriber = yield* $(pipe( + TSubscriptionRef.changesStream(subscriptionRef), + Stream.take(1), + Stream.runCollect, + Effect.fork + )) + // stream doesn't work properly without a yield, it will drop values + yield* $(Effect.yieldNow()) + yield* $(STM.commit(transaction)) + yield* $(Effect.yieldNow()) + const result = yield* $(Fiber.join(subscriber)) + + assert.deepStrictEqual(Array.from(result), [2]) + })) + + it.effect("emits every comitted value", () => + Effect.gen(function*($) { + const subscriptionRef = yield* $(TSubscriptionRef.make(0)) + + const transaction = pipe( + TSubscriptionRef.update(subscriptionRef, (n) => n + 1), + STM.commit, + // stream doesn't work properly without a yield, it will drop the first value without this + Effect.tap(() => Effect.yieldNow()), + Effect.flatMap(() => TSubscriptionRef.update(subscriptionRef, (n) => n + 1)) + ) + + const subscriber = yield* $(pipe( + TSubscriptionRef.changesStream(subscriptionRef), + Stream.take(2), + Stream.runCollect, + Effect.fork + )) + // stream doesn't work properly without a yield, it will drop the first value without this + yield* $(Effect.yieldNow()) + yield* $(transaction) + const result = yield* $(Fiber.join(subscriber)) + + assert.deepStrictEqual(Array.from(result), [1, 2]) + })) + + it.effect("multiple subscribers can receive committed values", () => + Effect.gen(function*($) { + const subscriptionRef = yield* $(TSubscriptionRef.make(0)) + const deferred1 = yield* $(Deferred.make()) + const deferred2 = yield* $(Deferred.make()) + const subscriber1 = yield* $(pipe( + TSubscriptionRef.changesStream(subscriptionRef), + Stream.tap(() => Deferred.succeed(deferred1, void 0)), + Stream.take(3), + Stream.runCollect, + Effect.fork + )) + yield* $(Deferred.await(deferred1)) + yield* $(TSubscriptionRef.update(subscriptionRef, (n) => n + 1)) + const subscriber2 = yield* $(pipe( + TSubscriptionRef.changesStream(subscriptionRef), + Stream.tap(() => Deferred.succeed(deferred2, void 0)), + Stream.take(2), + Stream.runCollect, + Effect.fork + )) + yield* $(Deferred.await(deferred2)) + yield* $(TSubscriptionRef.update(subscriptionRef, (n) => n + 1)) + const result1 = yield* $(Fiber.join(subscriber1)) + const result2 = yield* $(Fiber.join(subscriber2)) + assert.deepStrictEqual(Array.from(result1), [0, 1, 2]) + assert.deepStrictEqual(Array.from(result2), [1, 2]) + })) + + it.effect("subscriptions are interruptible", () => + Effect.gen(function*($) { + const ref = yield* $(TSubscriptionRef.make(0)) + const deferred1 = yield* $(Deferred.make()) + const deferred2 = yield* $(Deferred.make()) + const subscriber1 = yield* $( + TSubscriptionRef.changesStream(ref), + Stream.tap(() => Deferred.succeed(deferred1, void 0)), + Stream.take(5), + Stream.runCollect, + Effect.fork + ) + yield* $(Deferred.await(deferred1)) + yield* $(TSubscriptionRef.update(ref, (n) => n + 1)) + const subscriber2 = yield* $( + TSubscriptionRef.changesStream(ref), + Stream.tap(() => Deferred.succeed(deferred2, void 0)), + Stream.take(2), + Stream.runCollect, + Effect.fork + ) + yield* $(Deferred.await(deferred2)) + yield* $(TSubscriptionRef.update(ref, (n) => n + 1)) + const result1 = yield* $(Fiber.interrupt(subscriber1)) + const result2 = yield* $(Fiber.join(subscriber2)) + assert.isTrue(Exit.isInterrupted(result1)) + assert.deepStrictEqual(Array.from(result2), [1, 2]) + })) + + it.effect("concurrent subscribes and unsubscribes are handled correctly", () => + Effect.gen(function*($) { + const subscriber = (subscriptionRef: TSubscriptionRef.TSubscriptionRef) => + pipe( + Random.nextIntBetween(0, 200), + Effect.flatMap((n) => + pipe( + TSubscriptionRef.changesStream(subscriptionRef), + Stream.take(n), + Stream.runCollect + ) + ) + ) + const ref = yield* $(TSubscriptionRef.make(0)) + const fiber = yield* $( + TSubscriptionRef.update(ref, (n) => n + 1), + Effect.forever, + Effect.fork + ) + const result = yield* $( + Effect.map( + Effect.all( + Array.from({ length: 2 }, () => subscriber(ref)), + { concurrency: 2 } + ), + Chunk.unsafeFromArray + ) + ) + yield* $(Fiber.interrupt(fiber)) + const isSorted = Chunk.every(result, (chunk) => Equal.equals(chunk, Chunk.sort(chunk, Number.Order))) + assert.isTrue(isSorted) + })) +}) From 8162c1d0e524679ff0ddae0fcf0444dc983941af Mon Sep 17 00:00:00 2001 From: Giulio Canti Date: Fri, 11 Oct 2024 17:16:31 +0200 Subject: [PATCH 02/12] Merge Schema into Effect --- .changeset/cyan-sloths-lick.md | 5 + .changeset/six-crabs-itch.md | 41 + packages/cli/src/Args.ts | 2 +- packages/cli/src/Options.ts | 2 +- packages/cli/src/internal/args.ts | 4 +- packages/cli/src/internal/options.ts | 4 +- packages/cli/src/internal/primitive.ts | 2 +- packages/cli/src/internal/prompt/number.ts | 2 +- packages/cli/test/Args.test.ts | 2 +- packages/cli/test/Options.test.ts | 2 +- .../src/RpcBroadcastChannel.ts | 4 +- .../cluster-node/src/ShardManagerProtocol.ts | 2 +- packages/cluster-node/src/ShardingProtocol.ts | 2 +- packages/cluster-node/src/StorageFile.ts | 4 +- packages/cluster-workflow/src/Activity.ts | 2 +- .../src/DurableExecutionEvent.ts | 2 +- .../src/DurableExecutionJournal.ts | 2 +- packages/cluster-workflow/src/Workflow.ts | 2 +- .../test/DurableExecutionJournal.test.ts | 2 +- .../cluster-workflow/test/Workflow.test.ts | 2 +- packages/cluster-workflow/test/utils.ts | 2 +- packages/cluster/src/Message.ts | 4 +- packages/cluster/src/MessageState.ts | 2 +- packages/cluster/src/Pod.ts | 2 +- packages/cluster/src/PodAddress.ts | 2 +- packages/cluster/src/PoisonPill.ts | 2 +- packages/cluster/src/RecipientAddress.ts | 2 +- packages/cluster/src/RecipientType.ts | 2 +- packages/cluster/src/Serialization.ts | 2 +- packages/cluster/src/SerializedEnvelope.ts | 4 +- packages/cluster/src/SerializedMessage.ts | 2 +- packages/cluster/src/ShardId.ts | 2 +- packages/cluster/src/ShardingException.ts | 2 +- .../src/internal/atLeastOnceStorage.ts | 2 +- packages/cluster/src/internal/message.ts | 4 +- packages/cluster/src/internal/messageState.ts | 2 +- .../cluster/src/internal/serialization.ts | 4 +- packages/cluster/src/internal/utils.ts | 2 +- packages/cluster/test/AtLeastOnce.test.ts | 2 +- .../cluster/test/RecipientBehaviour.test.ts | 2 +- packages/cluster/test/RecipientType.test.ts | 2 +- packages/cluster/test/Sharding.test.ts | 2 +- .../benchmark/SchemaArray.ts} | 6 +- .../benchmark/SchemaFilters.ts} | 6 +- .../benchmark/SchemaIndex.ts} | 6 +- .../benchmark/SchemaPropertyOrder.ts} | 4 +- .../benchmark/SchemaStruct.ts} | 4 +- .../benchmark/SchemaToString.ts} | 6 +- .../benchmark/SchemaTreeFormatter.ts} | 6 +- .../benchmark/SchemaUnion.ts} | 6 +- .../benchmark/tsconfig.json | 2 - packages/{schema => effect}/dtslint/Schema.ts | 8 +- .../AST.ts => effect/dtslint/SchemaAST.ts} | 2 +- .../dtslint/SchemaBrand.ts} | 2 +- .../dtslint/SchemaClass.ts} | 2 +- .../dtslint/SchemaContext.ts} | 6 +- .../dtslint/SchemaGeneric.ts} | 3 +- .../dtslint/SchemaParseResult.ts} | 2 +- .../dtslint/SchemaPropertySignature.ts} | 2 +- .../dtslint/SchemaSerializable.ts} | 2 +- .../dtslint/SchemaTaggedError.ts} | 3 +- .../dtslint/SchemaTaggedRequest.ts} | 2 +- .../dtslint/SchemaUserland.ts} | 2 +- packages/effect/package.json | 8 +- packages/effect/src/Arbitrary.ts | 580 ++ packages/effect/src/FastCheck.ts | 9 + packages/effect/src/JSONSchema.ts | 602 ++ packages/effect/src/ParseResult.ts | 1720 +++ packages/effect/src/Pretty.ts | 218 + packages/effect/src/Schema.ts | 9270 ++++++++++++++++ packages/effect/src/SchemaAST.ts | 2744 +++++ packages/effect/src/SchemaArrayFormatter.ts | 82 + packages/effect/src/SchemaEquivalence.ts | 220 + packages/effect/src/SchemaTreeFormatter.ts | 215 + packages/effect/src/Serializable.ts | 385 + packages/effect/src/index.ts | 55 + .../src/internal/schema}/errors.ts | 2 +- .../src/internal/schema}/filters.ts | 38 +- .../src/internal/schema}/serializable.ts | 4 +- .../src/internal/schema}/util.ts | 4 +- .../test/Schema}/Arbitrary/Arbitrary.test.ts | 8 +- .../test/Schema}/Arbitrary/Class.test.ts | 6 +- .../Schema}/Arbitrary/getConstraints.test.ts | 6 +- .../test/Schema}/JSONSchema.test.ts | 16 +- .../test/Schema}/ParseResult.test.ts | 6 +- .../test/Schema}/Pretty.test.ts | 14 +- .../test/Schema}/Schema/Any/Any.test.ts | 4 +- .../test/Schema}/Schema/Array/Array.test.ts | 6 +- .../test/Schema}/Schema/Array/head.test.ts | 4 +- .../Schema}/Schema/Array/headOrElse.test.ts | 4 +- .../Schema}/Schema/Array/itemsCount.test.ts | 4 +- .../Schema}/Schema/Array/maxItems.test.ts | 4 +- .../Schema}/Schema/Array/minItems.test.ts | 4 +- .../test/Schema}/Schema/ArrayEnsure.test.ts | 4 +- .../Schema/BigDecimal/BigDecimal.test.ts | 4 +- .../BigDecimal/BigDecimalFromNumber.test.ts | 4 +- .../BigDecimal/BigDecimalFromSelf.test.ts | 8 +- .../NegativeBigDecimalFromSelf.test.ts | 4 +- .../NonNegativeBigDecimalFromSelf.test.ts | 4 +- .../NonPositiveBigDecimalFromSelf.test.ts | 4 +- .../PositiveBigDecimalFromSelf.test.ts | 4 +- .../BigDecimal/betweenBigDecimal.test.ts | 4 +- .../Schema/BigDecimal/clampBigDecimal.test.ts | 4 +- .../BigDecimal/greaterThanBigDecimal.test.ts | 4 +- .../greaterThanOrEqualToBigDecimal.test.ts | 4 +- .../BigDecimal/lessThanBigDecimal.test.ts | 4 +- .../lessThanOrEqualToBigDecimal.test.ts | 4 +- .../test/Schema}/Schema/BigInt/BigInt.test.ts | 4 +- .../Schema/BigInt/BigIntFromNumber.test.ts | 4 +- .../Schema/BigInt/BigIntFromSelf.test.ts | 4 +- .../BigInt/NegativeBigIntFromSelf.test.ts | 4 +- .../BigInt/NonNegativeBigIntFromSelf.test.ts | 4 +- .../BigInt/NonPositiveBigIntFromSelf.test.ts | 4 +- .../BigInt/PositiveBigIntFromSelf.test.ts | 4 +- .../Schema/BigInt/betweenBigInt.test.ts | 4 +- .../Schema}/Schema/BigInt/clampBigInt.test.ts | 4 +- .../Schema/BigInt/greaterThanBigInt.test.ts | 4 +- .../BigInt/greaterThanOrEqualToBigInt.test.ts | 4 +- .../Schema/BigInt/lessThanBigInt.test.ts | 4 +- .../BigInt/lessThanOrEqualToBigInt.test.ts | 4 +- .../Schema}/Schema/Boolean/Boolean.test.ts | 4 +- .../Schema/Boolean/BooleanFromUnknown.test.ts | 4 +- .../test/Schema}/Schema/Boolean/Not.test.ts | 4 +- .../test/Schema}/Schema/Cause/Cause.test.ts | 4 +- .../Schema/Cause/CauseFromSelf.test.ts | 6 +- .../test/Schema}/Schema/Chunk/Chunk.test.ts | 4 +- .../Schema/Chunk/ChunkFromSelf.test.ts | 10 +- .../Schema/Chunk/NonEmptyChunk.test.ts | 4 +- .../Chunk/NonEmptyChunkFromSelf.test.ts | 12 +- .../test/Schema}/Schema/Class/Class.test.ts | 10 +- .../Schema}/Schema/Class/TaggedClass.test.ts | 6 +- .../Schema}/Schema/Class/TaggedError.test.ts | 7 +- .../Schema/Class/TaggedRequest.test.ts | 8 +- .../test/Schema}/Schema/Class/extend.test.ts | 4 +- .../Schema/Class/transformOrFail.test.ts | 6 +- .../Schema/Class/transformOrFailFrom.test.ts | 6 +- .../test/Schema}/Schema/Config/Config.test.ts | 2 +- .../test/Schema}/Schema/Data/Data.test.ts | 4 +- .../Schema}/Schema/Data/DataFromSelf.test.ts | 8 +- .../test/Schema}/Schema/Date/Date.test.ts | 4 +- .../Schema/Date/DateFromNumber.test.ts | 4 +- .../Schema}/Schema/Date/DateFromSelf.test.ts | 6 +- .../Schema}/Schema/Date/betweenDate.test.ts | 4 +- .../Schema/Date/greaterThanDate.test.ts | 4 +- .../Date/greaterThanOrEqualToDate.test.ts | 4 +- .../Schema}/Schema/Date/lessThanDate.test.ts | 4 +- .../Schema/Date/lessThanOrEqualToDate.test.ts | 4 +- .../Schema}/Schema/DateTime/DateTime.test.ts | 4 +- .../Schema/DecodingFallbackAnnotation.test.ts | 4 +- .../test/Schema}/Schema/Defect/Defect.test.ts | 4 +- .../Schema}/Schema/Duration/Duration.test.ts | 4 +- .../Duration/DurationFromMillis.test.ts | 4 +- .../Schema/Duration/DurationFromNanos.test.ts | 4 +- .../Schema/Duration/DurationFromSelf.test.ts | 6 +- .../Schema/Duration/betweenDuration.test.ts | 4 +- .../Schema/Duration/clampDuration.test.ts | 4 +- .../Duration/greaterThanDuration.test.ts | 4 +- .../greaterThanOrEqualToDuration.test.ts | 4 +- .../Schema/Duration/lessThanDuration.test.ts | 4 +- .../lessThanOrEqualToDuration.test.ts | 4 +- .../test/Schema}/Schema/Either/Either.test.ts | 4 +- .../Schema/Either/EitherFromSelf.test.ts | 8 +- .../Schema/Either/EitherFromUnion.test.ts | 4 +- .../test/Schema}/Schema/Enums/Enums.test.ts | 4 +- .../test/Schema}/Schema/Exit/Exit.test.ts | 4 +- .../Schema}/Schema/Exit/ExitFromSelf.test.ts | 4 +- .../Schema}/Schema/FiberId/FiberId.test.ts | 4 +- .../Schema/FiberId/FiberIdFromSelf.test.ts | 6 +- .../Schema}/Schema/HashMap/HashMap.test.ts | 4 +- .../Schema/HashMap/HashMapFromSelf.test.ts | 8 +- .../Schema}/Schema/HashSet/HashSet.test.ts | 4 +- .../Schema/HashSet/HashSetFromSelf.test.ts | 10 +- .../test/Schema}/Schema/List/List.test.ts | 4 +- .../Schema}/Schema/List/ListFromSelf.test.ts | 10 +- .../Schema}/Schema/Literal/Literal.test.ts | 6 +- .../test/Schema}/Schema/Map/Map.test.ts | 2 +- .../Schema}/Schema/Map/MapFromRecord.test.ts | 4 +- .../Schema}/Schema/Map/MapFromSelf.test.ts | 2 +- .../test/Schema}/Schema/Never/Never.test.ts | 4 +- .../Schema/NonEmptyArrayEnsure.test.ts | 4 +- .../Schema}/Schema/Number/JsonNumber.test.ts | 4 +- .../test/Schema}/Schema/Number/Number.test.ts | 4 +- .../Schema}/Schema/Number/between.test.ts | 4 +- .../test/Schema}/Schema/Number/clamp.test.ts | 4 +- .../test/Schema}/Schema/Number/finite.test.ts | 4 +- .../Schema}/Schema/Number/greaterThan.test.ts | 8 +- .../Number/greaterThanOrEqualTo.test.ts | 8 +- .../test/Schema}/Schema/Number/int.test.ts | 8 +- .../Schema}/Schema/Number/lessThan.test.ts | 8 +- .../Schema/Number/lessThanOrEqualTo.test.ts | 8 +- .../Schema}/Schema/Number/multipleOf.test.ts | 6 +- .../Schema}/Schema/Number/negative.test.ts | 4 +- .../test/Schema}/Schema/Number/nonNaN.test.ts | 8 +- .../Schema}/Schema/Number/nonNegative.test.ts | 4 +- .../Schema}/Schema/Number/nonPositive.test.ts | 4 +- .../Schema/Number/numberFromString.test.ts | 4 +- .../Schema}/Schema/Number/positive.test.ts | 4 +- .../test/Schema}/Schema/Object/Object.test.ts | 4 +- .../test/Schema}/Schema/Option/Option.test.ts | 4 +- .../OptionFromNonEmptyTrimmedString.test.ts | 4 +- .../Schema/Option/OptionFromNullOr.test.ts | 4 +- .../Schema/Option/OptionFromNullishOr.test.ts | 4 +- .../Schema/Option/OptionFromSelf.test.ts | 8 +- .../Option/OptionFromUndefinedOr.test.ts | 4 +- .../Schema/ParseOptions-errors.test.ts | 4 +- .../Schema}/Schema/ParseOptions-exact.test.ts | 4 +- .../ParseOptions-onExcessProperty.test.ts | 16 +- .../ParseOptions-preserveKeyOrder.test.ts | 6 +- .../Schema/ParseOptionsAnnotation.test.ts | 4 +- .../Schema}/Schema/PropertySignature.test.ts | 12 +- .../Schema/ReadonlyMap/ReadonlyMap.test.ts | 4 +- .../ReadonlyMap/ReadonlyMapFromRecord.test.ts | 4 +- .../ReadonlyMap/ReadonlyMapFromSelf.test.ts | 8 +- .../Schema/ReadonlySet/ReadonlySet.test.ts | 4 +- .../ReadonlySet/ReadonlySetFromSelf.test.ts | 8 +- .../test/Schema}/Schema/Record/Record.test.ts | 32 +- .../Schema}/Schema/Redacted/Redacted.test.ts | 6 +- .../Schema/Redacted/RedactedFromSelf.test.ts | 6 +- .../test/Schema}/Schema/Set/Set.test.ts | 2 +- .../Schema}/Schema/Set/SetFromSelf.test.ts | 2 +- .../Schema/SortedSet/SortedSet.test.ts | 4 +- .../SortedSet/SortedSetFromSelf.test.ts | 10 +- .../String/NonEmptyTrimmedString.test.ts | 4 +- .../test/Schema}/Schema/String/String.test.ts | 4 +- .../Schema/String/StringFromBase64.test.ts | 4 +- .../Schema/String/StringFromBase64Url.test.ts | 4 +- .../Schema/String/StringFromHex.test.ts | 4 +- .../Schema}/Schema/String/capitalize.test.ts | 4 +- .../Schema}/Schema/String/endsWith.test.ts | 6 +- .../Schema}/Schema/String/includes.test.ts | 8 +- .../test/Schema}/Schema/String/length.test.ts | 4 +- .../Schema}/Schema/String/lowercase.test.ts | 4 +- .../Schema}/Schema/String/maxLength.test.ts | 8 +- .../Schema}/Schema/String/minLength.test.ts | 8 +- .../Schema/String/nonEmptyString.test.ts | 4 +- .../Schema}/Schema/String/pattern.test.ts | 4 +- .../test/Schema}/Schema/String/split.test.ts | 4 +- .../Schema}/Schema/String/startsWith.test.ts | 6 +- .../test/Schema}/Schema/String/trim.test.ts | 4 +- .../Schema/String/uncapitalize.test.ts | 4 +- .../Schema}/Schema/String/uppercase.test.ts | 4 +- .../test/Schema}/Schema/Struct/Struct.test.ts | 10 +- .../test/Schema}/Schema/Struct/make.test.ts | 4 +- .../test/Schema}/Schema/Struct/omit.test.ts | 2 +- .../test/Schema}/Schema/Struct/pick.test.ts | 2 +- .../test/Schema}/Schema/Symbol/Symbol.test.ts | 4 +- .../Schema/Symbol/SymbolFromSelf.test.ts | 8 +- .../Schema}/Schema/TaggedStruct/make.test.ts | 4 +- .../TemplateLiteral/TemplateLiteral.test.ts | 6 +- .../Schema/TemplateLiteralParser.test.ts | 4 +- .../Schema}/Schema/Trimmed/Trimmed.test.ts | 10 +- .../test/Schema}/Schema/Tuple/Tuple.test.ts | 6 +- .../test/Schema}/Schema/ULID.test.ts | 4 +- .../test/Schema}/Schema/UUID.test.ts | 4 +- .../Schema/Uint8Array/Uint8Array.test.ts | 4 +- .../Uint8Array/Uint8ArrayFromBase64.test.ts | 4 +- .../Uint8ArrayFromBase64Url.test.ts | 4 +- .../Uint8Array/Uint8ArrayFromHex.test.ts | 4 +- .../Uint8Array/Uint8ArrayFromSelf.test.ts | 6 +- .../test/Schema}/Schema/Union/Union.test.ts | 6 +- .../UniqueSymbol/UniqueSymbolFromSelf.test.ts | 17 + .../Schema}/Schema/Unknown/Unknown.test.ts | 4 +- .../test/Schema}/Schema/Void/Void.test.ts | 4 +- .../test/Schema}/Schema/annotations.test.ts | 10 +- .../test/Schema}/Schema/asserts.test.ts | 6 +- .../Schema/attachPropertySignature.test.ts | 18 +- .../test/Schema}/Schema/brand.test.ts | 6 +- .../test/Schema}/Schema/compose.test.ts | 4 +- .../test/Schema}/Schema/decode.test.ts | 4 +- .../test/Schema}/Schema/decodeEither.test.ts | 4 +- .../test/Schema}/Schema/decodeOption.test.ts | 4 +- .../test/Schema}/Schema/decodePromise.test.ts | 4 +- .../test/Schema}/Schema/decodeSync.test.ts | 4 +- .../Schema/decodeUnknownEither.test.ts | 4 +- .../Schema/decodeUnknownOption.test.ts | 4 +- .../Schema}/Schema/decodeUnknownSync.test.ts | 6 +- .../test/Schema}/Schema/encode.test.ts | 4 +- .../test/Schema}/Schema/encodeEither.test.ts | 4 +- .../test/Schema}/Schema/encodeOption.test.ts | 4 +- .../test/Schema}/Schema/encodePromise.test.ts | 4 +- .../test/Schema}/Schema/encodeSync.test.ts | 4 +- .../Schema/encodeUnknownEither.test.ts | 4 +- .../Schema/encodeUnknownOption.test.ts | 4 +- .../Schema}/Schema/encodeUnknownSync.test.ts | 4 +- .../Schema}/Schema/encodedBoundSchema.test.ts | 4 +- .../test/Schema}/Schema/encodedSchema.test.ts | 4 +- .../test/Schema}/Schema/exports.test.ts | 2 +- .../test/Schema}/Schema/extend.test.ts | 10 +- .../test/Schema}/Schema/filter.test.ts | 8 +- .../test/Schema}/Schema/filterEffect.test.ts | 6 +- .../test/Schema}/Schema/format.test.ts | 4 +- .../test/Schema}/Schema/fromBrand.test.ts | 4 +- .../Schema/getNumberIndexedAccess.test.ts | 4 +- .../test/Schema}/Schema/instanceOf.test.ts | 10 +- .../test/Schema}/Schema/is.test.ts | 26 +- .../test/Schema}/Schema/isSchema.test.ts | 2 +- .../test/Schema}/Schema/keyof.test.ts | 12 +- .../test/Schema}/Schema/mutable.test.ts | 4 +- .../test/Schema}/Schema/nonEmptyArray.test.ts | 4 +- .../test/Schema}/Schema/omit.test.ts | 14 +- .../test/Schema}/Schema/optional.test.ts | 4 +- .../Schema}/Schema/optionalToRequired.test.ts | 4 +- .../test/Schema}/Schema/optionalWith.test.ts | 6 +- .../test/Schema}/Schema/parseJson.test.ts | 4 +- .../test/Schema}/Schema/partial.test.ts | 4 +- .../test/Schema}/Schema/partialWith.test.ts | 4 +- .../test/Schema}/Schema/pick.test.ts | 26 +- .../test/Schema}/Schema/pickLiteral.test.ts | 6 +- .../test/Schema}/Schema/pipe.test.ts | 2 +- .../test/Schema}/Schema/pluck.test.ts | 4 +- .../test/Schema}/Schema/rename.test.ts | 12 +- .../test/Schema}/Schema/required.test.ts | 4 +- .../Schema}/Schema/requiredToOptional.test.ts | 4 +- .../test/Schema}/Schema/suspend.test.ts | 4 +- .../test/Schema}/Schema/toString.test.ts | 2 +- .../test/Schema}/Schema/transform.test.ts | 4 +- .../Schema}/Schema/transformLiterals.test.ts | 4 +- .../Schema}/Schema/transformOrFail.test.ts | 6 +- .../test/Schema}/Schema/typeSchema.test.ts | 4 +- .../test/Schema}/Schema/validate.test.ts | 4 +- .../Schema}/Schema/validateEither.test.ts | 4 +- .../Schema}/Schema/validateOption.test.ts | 4 +- .../Schema}/Schema/validatePromise.test.ts | 4 +- .../test/Schema}/Schema/validateSync.test.ts | 4 +- .../Schema/withConstructorDefault.test.ts | 2 +- .../Schema/withDecodingDefault.test.ts | 4 +- .../Schema/SchemaAST}/IndexSignature.test.ts | 2 +- .../test/Schema/SchemaAST}/Tuple.test.ts | 2 +- .../Schema/SchemaAST}/TypeLiteral.test.ts | 4 +- .../TypeLiteralTransformation.test.ts | 2 +- .../test/Schema/SchemaAST}/Union.test.ts | 4 +- .../Schema/SchemaAST}/annotations.test.ts | 2 +- .../test/Schema/SchemaAST}/encodedAST.test.ts | 4 +- .../Schema/SchemaAST}/encodedBoundAST.test.ts | 4 +- .../SchemaAST}/getPropertySignatures.test.ts | 4 +- .../test/Schema/SchemaAST}/guards.test.ts | 6 +- .../test/Schema/SchemaAST}/mutable.test.ts | 4 +- .../test/Schema/SchemaAST}/partial.test.ts | 4 +- .../test/Schema/SchemaAST}/pick.test.ts | 4 +- .../test/Schema/SchemaAST}/record.test.ts | 2 +- .../test/Schema/SchemaAST}/suspend.test.ts | 6 +- .../test/Schema/SchemaAST}/toString.test.ts | 2 +- .../test/Schema/SchemaAST}/typeAST.test.ts | 4 +- .../test/Schema/SchemaAST}/unify.test.ts | 4 +- .../test/Schema/SchemaEquivalence.test.ts} | 22 +- .../test/Schema/SchemaFormatters.test.ts} | 16 +- .../effect/test/Schema/SchemaInternal.test.ts | 39 + .../test/Schema/SchemaUserland.test.ts} | 7 +- .../test/Schema}/Serializable.test.ts | 4 +- .../test => effect/test/Schema}/TestUtils.ts | 14 +- packages/experimental/src/ChannelSchema.ts | 4 +- packages/experimental/src/DevTools/Domain.ts | 2 +- packages/experimental/src/Machine.ts | 6 +- .../experimental/src/Machine/Procedure.ts | 4 +- .../src/Machine/SerializableProcedureList.ts | 4 +- packages/experimental/src/MsgPack.ts | 4 +- packages/experimental/src/Ndjson.ts | 4 +- packages/experimental/src/PersistedCache.ts | 2 +- packages/experimental/src/Persistence.ts | 6 +- packages/experimental/src/RequestResolver.ts | 2 +- packages/experimental/src/VariantSchema.ts | 6 +- .../platform-browser/test/fixtures/schema.ts | 2 +- .../platform-node/test/HttpClient.test.ts | 2 +- .../platform-node/test/HttpServer.test.ts | 2 +- packages/platform/src/Headers.ts | 2 +- packages/platform/src/HttpApi.ts | 4 +- packages/platform/src/HttpApiBuilder.ts | 6 +- packages/platform/src/HttpApiClient.ts | 4 +- packages/platform/src/HttpApiEndpoint.ts | 2 +- packages/platform/src/HttpApiError.ts | 8 +- packages/platform/src/HttpApiGroup.ts | 2 +- packages/platform/src/HttpApiSchema.ts | 4 +- packages/platform/src/HttpBody.ts | 4 +- packages/platform/src/HttpClientRequest.ts | 4 +- packages/platform/src/HttpClientResponse.ts | 6 +- packages/platform/src/HttpIncomingMessage.ts | 6 +- packages/platform/src/HttpRouter.ts | 6 +- packages/platform/src/HttpServerRequest.ts | 6 +- .../platform/src/HttpServerRespondable.ts | 2 +- packages/platform/src/HttpServerResponse.ts | 4 +- packages/platform/src/KeyValueStore.ts | 4 +- packages/platform/src/Multipart.ts | 6 +- packages/platform/src/OpenApi.ts | 4 +- packages/platform/src/OpenApiJsonSchema.ts | 12 +- packages/platform/src/Transferable.ts | 4 +- packages/platform/src/UrlParams.ts | 6 +- packages/platform/src/Worker.ts | 6 +- packages/platform/src/WorkerError.ts | 2 +- packages/platform/src/WorkerRunner.ts | 4 +- packages/platform/src/internal/httpBody.ts | 4 +- .../src/internal/httpClientRequest.ts | 4 +- .../src/internal/httpClientResponse.ts | 6 +- packages/platform/src/internal/httpRouter.ts | 4 +- .../src/internal/httpServerRequest.ts | 6 +- .../src/internal/httpServerResponse.ts | 4 +- .../platform/src/internal/keyValueStore.ts | 2 +- packages/platform/src/internal/multipart.ts | 6 +- packages/platform/src/internal/worker.ts | 4 +- .../platform/src/internal/workerRunner.ts | 4 +- packages/platform/test/HttpClient.test.ts | 2 +- packages/platform/test/KeyValueStore.test.ts | 2 +- .../platform/test/OpenApiJsonSchema.test.ts | 12 +- packages/rpc-http/src/HttpRpcResolver.ts | 2 +- .../rpc-http/src/HttpRpcResolverNoStream.ts | 2 +- .../rpc-http/src/HttpRpcRouterNoStream.ts | 2 +- packages/rpc/src/Rpc.ts | 6 +- packages/rpc/src/RpcResolver.ts | 6 +- packages/rpc/src/RpcResolverNoStream.ts | 4 +- packages/rpc/src/RpcRouter.ts | 6 +- packages/rpc/src/internal/rpc.ts | 4 +- packages/rpc/test/Router.test.ts | 2 +- packages/schema/dtslint/tsconfig.json | 9 - packages/schema/package.json | 6 +- packages/schema/src/AST.ts | 2741 +---- packages/schema/src/Arbitrary.ts | 577 +- packages/schema/src/ArrayFormatter.ts | 79 +- packages/schema/src/Equivalence.ts | 217 +- packages/schema/src/FastCheck.ts | 4 +- packages/schema/src/JSONSchema.ts | 599 +- packages/schema/src/ParseResult.ts | 1717 +-- packages/schema/src/Pretty.ts | 215 +- packages/schema/src/Schema.ts | 9272 +---------------- packages/schema/src/Serializable.ts | 382 +- packages/schema/src/TreeFormatter.ts | 212 +- .../UniqueSymbol/UniqueSymbolFromSelf.test.ts | 17 - packages/schema/test/formatUnknown.test.ts | 27 - packages/schema/test/index.test.ts | 5 + packages/schema/test/util.test.ts | 14 - packages/sql-d1/test/Resolver.test.ts | 2 +- packages/sql-libsql/test/Resolver.test.ts | 2 +- .../sql-sqlite-node/test/Resolver.test.ts | 2 +- packages/sql/src/Model.ts | 4 +- packages/sql/src/SqlResolver.ts | 4 +- packages/sql/src/SqlSchema.ts | 4 +- pnpm-lock.yaml | 23 +- 435 files changed, 17234 insertions(+), 17044 deletions(-) create mode 100644 .changeset/cyan-sloths-lick.md create mode 100644 .changeset/six-crabs-itch.md rename packages/{schema/benchmark/array.ts => effect/benchmark/SchemaArray.ts} (94%) rename packages/{schema/benchmark/filters.ts => effect/benchmark/SchemaFilters.ts} (96%) rename packages/{schema/benchmark/index.ts => effect/benchmark/SchemaIndex.ts} (96%) rename packages/{schema/benchmark/propertyOrder.ts => effect/benchmark/SchemaPropertyOrder.ts} (96%) rename packages/{schema/benchmark/Struct.ts => effect/benchmark/SchemaStruct.ts} (95%) rename packages/{schema/benchmark/toString.ts => effect/benchmark/SchemaToString.ts} (92%) rename packages/{schema/benchmark/TreeFormatter.formatIssueSync.ts => effect/benchmark/SchemaTreeFormatter.ts} (92%) rename packages/{schema/benchmark/union.ts => effect/benchmark/SchemaUnion.ts} (95%) rename packages/{schema => effect}/benchmark/tsconfig.json (69%) rename packages/{schema => effect}/dtslint/Schema.ts (99%) rename packages/{schema/dtslint/AST.ts => effect/dtslint/SchemaAST.ts} (86%) rename packages/{schema/dtslint/Brand.errors.ts => effect/dtslint/SchemaBrand.ts} (90%) rename packages/{schema/dtslint/Class.ts => effect/dtslint/SchemaClass.ts} (99%) rename packages/{schema/dtslint/Context.ts => effect/dtslint/SchemaContext.ts} (98%) rename packages/{schema/dtslint/generic.ts => effect/dtslint/SchemaGeneric.ts} (93%) rename packages/{schema/dtslint/ParseResult.ts => effect/dtslint/SchemaParseResult.ts} (78%) rename packages/{schema/dtslint/PropertySignature.ts => effect/dtslint/SchemaPropertySignature.ts} (95%) rename packages/{schema/dtslint/Serializable.ts => effect/dtslint/SchemaSerializable.ts} (98%) rename packages/{schema/dtslint/TaggedError.ts => effect/dtslint/SchemaTaggedError.ts} (79%) rename packages/{schema/dtslint/TaggedRequest.ts => effect/dtslint/SchemaTaggedRequest.ts} (88%) rename packages/{schema/dtslint/userland.ts => effect/dtslint/SchemaUserland.ts} (95%) create mode 100644 packages/effect/src/Arbitrary.ts create mode 100644 packages/effect/src/FastCheck.ts create mode 100644 packages/effect/src/JSONSchema.ts create mode 100644 packages/effect/src/ParseResult.ts create mode 100644 packages/effect/src/Pretty.ts create mode 100644 packages/effect/src/Schema.ts create mode 100644 packages/effect/src/SchemaAST.ts create mode 100644 packages/effect/src/SchemaArrayFormatter.ts create mode 100644 packages/effect/src/SchemaEquivalence.ts create mode 100644 packages/effect/src/SchemaTreeFormatter.ts create mode 100644 packages/effect/src/Serializable.ts rename packages/{schema/src/internal => effect/src/internal/schema}/errors.ts (99%) rename packages/{schema/src/internal => effect/src/internal/schema}/filters.ts (72%) rename packages/{schema/src/internal => effect/src/internal/schema}/serializable.ts (62%) rename packages/{schema/src/internal => effect/src/internal/schema}/util.ts (96%) rename packages/{schema/test => effect/test/Schema}/Arbitrary/Arbitrary.test.ts (98%) rename packages/{schema/test => effect/test/Schema}/Arbitrary/Class.test.ts (87%) rename packages/{schema/test => effect/test/Schema}/Arbitrary/getConstraints.test.ts (97%) rename packages/{schema/test => effect/test/Schema}/JSONSchema.test.ts (99%) rename packages/{schema/test => effect/test/Schema}/ParseResult.test.ts (99%) rename packages/{schema/test => effect/test/Schema}/Pretty.test.ts (97%) rename packages/{schema/test => effect/test/Schema}/Schema/Any/Any.test.ts (84%) rename packages/{schema/test => effect/test/Schema}/Schema/Array/Array.test.ts (90%) rename packages/{schema/test => effect/test/Schema}/Schema/Array/head.test.ts (90%) rename packages/{schema/test => effect/test/Schema}/Schema/Array/headOrElse.test.ts (95%) rename packages/{schema/test => effect/test/Schema}/Schema/Array/itemsCount.test.ts (89%) rename packages/{schema/test => effect/test/Schema}/Schema/Array/maxItems.test.ts (83%) rename packages/{schema/test => effect/test/Schema}/Schema/Array/minItems.test.ts (88%) rename packages/{schema/test => effect/test/Schema}/Schema/ArrayEnsure.test.ts (96%) rename packages/{schema/test => effect/test/Schema}/Schema/BigDecimal/BigDecimal.test.ts (91%) rename packages/{schema/test => effect/test/Schema}/Schema/BigDecimal/BigDecimalFromNumber.test.ts (91%) rename packages/{schema/test => effect/test/Schema}/Schema/BigDecimal/BigDecimalFromSelf.test.ts (89%) rename packages/{schema/test => effect/test/Schema}/Schema/BigDecimal/NegativeBigDecimalFromSelf.test.ts (90%) rename packages/{schema/test => effect/test/Schema}/Schema/BigDecimal/NonNegativeBigDecimalFromSelf.test.ts (89%) rename packages/{schema/test => effect/test/Schema}/Schema/BigDecimal/NonPositiveBigDecimalFromSelf.test.ts (89%) rename packages/{schema/test => effect/test/Schema}/Schema/BigDecimal/PositiveBigDecimalFromSelf.test.ts (90%) rename packages/{schema/test => effect/test/Schema}/Schema/BigDecimal/betweenBigDecimal.test.ts (92%) rename packages/{schema/test => effect/test/Schema}/Schema/BigDecimal/clampBigDecimal.test.ts (86%) rename packages/{schema/test => effect/test/Schema}/Schema/BigDecimal/greaterThanBigDecimal.test.ts (89%) rename packages/{schema/test => effect/test/Schema}/Schema/BigDecimal/greaterThanOrEqualToBigDecimal.test.ts (88%) rename packages/{schema/test => effect/test/Schema}/Schema/BigDecimal/lessThanBigDecimal.test.ts (89%) rename packages/{schema/test => effect/test/Schema}/Schema/BigDecimal/lessThanOrEqualToBigDecimal.test.ts (88%) rename packages/{schema/test => effect/test/Schema}/Schema/BigInt/BigInt.test.ts (94%) rename packages/{schema/test => effect/test/Schema}/Schema/BigInt/BigIntFromNumber.test.ts (94%) rename packages/{schema/test => effect/test/Schema}/Schema/BigInt/BigIntFromSelf.test.ts (85%) rename packages/{schema/test => effect/test/Schema}/Schema/BigInt/NegativeBigIntFromSelf.test.ts (86%) rename packages/{schema/test => effect/test/Schema}/Schema/BigInt/NonNegativeBigIntFromSelf.test.ts (83%) rename packages/{schema/test => effect/test/Schema}/Schema/BigInt/NonPositiveBigIntFromSelf.test.ts (84%) rename packages/{schema/test => effect/test/Schema}/Schema/BigInt/PositiveBigIntFromSelf.test.ts (86%) rename packages/{schema/test => effect/test/Schema}/Schema/BigInt/betweenBigInt.test.ts (88%) rename packages/{schema/test => effect/test/Schema}/Schema/BigInt/clampBigInt.test.ts (78%) rename packages/{schema/test => effect/test/Schema}/Schema/BigInt/greaterThanBigInt.test.ts (86%) rename packages/{schema/test => effect/test/Schema}/Schema/BigInt/greaterThanOrEqualToBigInt.test.ts (84%) rename packages/{schema/test => effect/test/Schema}/Schema/BigInt/lessThanBigInt.test.ts (86%) rename packages/{schema/test => effect/test/Schema}/Schema/BigInt/lessThanOrEqualToBigInt.test.ts (84%) rename packages/{schema/test => effect/test/Schema}/Schema/Boolean/Boolean.test.ts (83%) rename packages/{schema/test => effect/test/Schema}/Schema/Boolean/BooleanFromUnknown.test.ts (89%) rename packages/{schema/test => effect/test/Schema}/Schema/Boolean/Not.test.ts (80%) rename packages/{schema/test => effect/test/Schema}/Schema/Cause/Cause.test.ts (98%) rename packages/{schema/test => effect/test/Schema}/Schema/Cause/CauseFromSelf.test.ts (95%) rename packages/{schema/test => effect/test/Schema}/Schema/Chunk/Chunk.test.ts (91%) rename packages/{schema/test => effect/test/Schema}/Schema/Chunk/ChunkFromSelf.test.ts (86%) rename packages/{schema/test => effect/test/Schema}/Schema/Chunk/NonEmptyChunk.test.ts (91%) rename packages/{schema/test => effect/test/Schema}/Schema/Chunk/NonEmptyChunkFromSelf.test.ts (87%) rename packages/{schema/test => effect/test/Schema}/Schema/Class/Class.test.ts (98%) rename packages/{schema/test => effect/test/Schema}/Schema/Class/TaggedClass.test.ts (98%) rename packages/{schema/test => effect/test/Schema}/Schema/Class/TaggedError.test.ts (95%) rename packages/{schema/test => effect/test/Schema}/Schema/Class/TaggedRequest.test.ts (96%) rename packages/{schema/test => effect/test/Schema}/Schema/Class/extend.test.ts (97%) rename packages/{schema/test => effect/test/Schema}/Schema/Class/transformOrFail.test.ts (94%) rename packages/{schema/test => effect/test/Schema}/Schema/Class/transformOrFailFrom.test.ts (94%) rename packages/{schema/test => effect/test/Schema}/Schema/Config/Config.test.ts (97%) rename packages/{schema/test => effect/test/Schema}/Schema/Data/Data.test.ts (91%) rename packages/{schema/test => effect/test/Schema}/Schema/Data/DataFromSelf.test.ts (90%) rename packages/{schema/test => effect/test/Schema}/Schema/Date/Date.test.ts (88%) rename packages/{schema/test => effect/test/Schema}/Schema/Date/DateFromNumber.test.ts (91%) rename packages/{schema/test => effect/test/Schema}/Schema/Date/DateFromSelf.test.ts (85%) rename packages/{schema/test => effect/test/Schema}/Schema/Date/betweenDate.test.ts (91%) rename packages/{schema/test => effect/test/Schema}/Schema/Date/greaterThanDate.test.ts (88%) rename packages/{schema/test => effect/test/Schema}/Schema/Date/greaterThanOrEqualToDate.test.ts (86%) rename packages/{schema/test => effect/test/Schema}/Schema/Date/lessThanDate.test.ts (88%) rename packages/{schema/test => effect/test/Schema}/Schema/Date/lessThanOrEqualToDate.test.ts (86%) rename packages/{schema/test => effect/test/Schema}/Schema/DateTime/DateTime.test.ts (94%) rename packages/{schema/test => effect/test/Schema}/Schema/DecodingFallbackAnnotation.test.ts (95%) rename packages/{schema/test => effect/test/Schema}/Schema/Defect/Defect.test.ts (94%) rename packages/{schema/test => effect/test/Schema}/Schema/Duration/Duration.test.ts (95%) rename packages/{schema/test => effect/test/Schema}/Schema/Duration/DurationFromMillis.test.ts (88%) rename packages/{schema/test => effect/test/Schema}/Schema/Duration/DurationFromNanos.test.ts (85%) rename packages/{schema/test => effect/test/Schema}/Schema/Duration/DurationFromSelf.test.ts (88%) rename packages/{schema/test => effect/test/Schema}/Schema/Duration/betweenDuration.test.ts (91%) rename packages/{schema/test => effect/test/Schema}/Schema/Duration/clampDuration.test.ts (86%) rename packages/{schema/test => effect/test/Schema}/Schema/Duration/greaterThanDuration.test.ts (91%) rename packages/{schema/test => effect/test/Schema}/Schema/Duration/greaterThanOrEqualToDuration.test.ts (90%) rename packages/{schema/test => effect/test/Schema}/Schema/Duration/lessThanDuration.test.ts (91%) rename packages/{schema/test => effect/test/Schema}/Schema/Duration/lessThanOrEqualToDuration.test.ts (90%) rename packages/{schema/test => effect/test/Schema}/Schema/Either/Either.test.ts (89%) rename packages/{schema/test => effect/test/Schema}/Schema/Either/EitherFromSelf.test.ts (91%) rename packages/{schema/test => effect/test/Schema}/Schema/Either/EitherFromUnion.test.ts (98%) rename packages/{schema/test => effect/test/Schema}/Schema/Enums/Enums.test.ts (96%) rename packages/{schema/test => effect/test/Schema}/Schema/Exit/Exit.test.ts (94%) rename packages/{schema/test => effect/test/Schema}/Schema/Exit/ExitFromSelf.test.ts (95%) rename packages/{schema/test => effect/test/Schema}/Schema/FiberId/FiberId.test.ts (92%) rename packages/{schema/test => effect/test/Schema}/Schema/FiberId/FiberIdFromSelf.test.ts (88%) rename packages/{schema/test => effect/test/Schema}/Schema/HashMap/HashMap.test.ts (94%) rename packages/{schema/test => effect/test/Schema}/Schema/HashMap/HashMapFromSelf.test.ts (93%) rename packages/{schema/test => effect/test/Schema}/Schema/HashSet/HashSet.test.ts (92%) rename packages/{schema/test => effect/test/Schema}/Schema/HashSet/HashSetFromSelf.test.ts (87%) rename packages/{schema/test => effect/test/Schema}/Schema/List/List.test.ts (91%) rename packages/{schema/test => effect/test/Schema}/Schema/List/ListFromSelf.test.ts (86%) rename packages/{schema/test => effect/test/Schema}/Schema/Literal/Literal.test.ts (93%) rename packages/{schema/test => effect/test/Schema}/Schema/Map/Map.test.ts (86%) rename packages/{schema/test => effect/test/Schema}/Schema/Map/MapFromRecord.test.ts (95%) rename packages/{schema/test => effect/test/Schema}/Schema/Map/MapFromSelf.test.ts (83%) rename packages/{schema/test => effect/test/Schema}/Schema/Never/Never.test.ts (77%) rename packages/{schema/test => effect/test/Schema}/Schema/NonEmptyArrayEnsure.test.ts (97%) rename packages/{schema/test => effect/test/Schema}/Schema/Number/JsonNumber.test.ts (93%) rename packages/{schema/test => effect/test/Schema}/Schema/Number/Number.test.ts (85%) rename packages/{schema/test => effect/test/Schema}/Schema/Number/between.test.ts (87%) rename packages/{schema/test => effect/test/Schema}/Schema/Number/clamp.test.ts (86%) rename packages/{schema/test => effect/test/Schema}/Schema/Number/finite.test.ts (86%) rename packages/{schema/test => effect/test/Schema}/Schema/Number/greaterThan.test.ts (82%) rename packages/{schema/test => effect/test/Schema}/Schema/Number/greaterThanOrEqualTo.test.ts (80%) rename packages/{schema/test => effect/test/Schema}/Schema/Number/int.test.ts (81%) rename packages/{schema/test => effect/test/Schema}/Schema/Number/lessThan.test.ts (83%) rename packages/{schema/test => effect/test/Schema}/Schema/Number/lessThanOrEqualTo.test.ts (81%) rename packages/{schema/test => effect/test/Schema}/Schema/Number/multipleOf.test.ts (89%) rename packages/{schema/test => effect/test/Schema}/Schema/Number/negative.test.ts (84%) rename packages/{schema/test => effect/test/Schema}/Schema/Number/nonNaN.test.ts (77%) rename packages/{schema/test => effect/test/Schema}/Schema/Number/nonNegative.test.ts (82%) rename packages/{schema/test => effect/test/Schema}/Schema/Number/nonPositive.test.ts (82%) rename packages/{schema/test => effect/test/Schema}/Schema/Number/numberFromString.test.ts (95%) rename packages/{schema/test => effect/test/Schema}/Schema/Number/positive.test.ts (84%) rename packages/{schema/test => effect/test/Schema}/Schema/Object/Object.test.ts (90%) rename packages/{schema/test => effect/test/Schema}/Schema/Option/Option.test.ts (87%) rename packages/{schema/test => effect/test/Schema}/Schema/Option/OptionFromNonEmptyTrimmedString.test.ts (93%) rename packages/{schema/test => effect/test/Schema}/Schema/Option/OptionFromNullOr.test.ts (94%) rename packages/{schema/test => effect/test/Schema}/Schema/Option/OptionFromNullishOr.test.ts (94%) rename packages/{schema/test => effect/test/Schema}/Schema/Option/OptionFromSelf.test.ts (86%) rename packages/{schema/test => effect/test/Schema}/Schema/Option/OptionFromUndefinedOr.test.ts (94%) rename packages/{schema/test => effect/test/Schema}/Schema/ParseOptions-errors.test.ts (98%) rename packages/{schema/test => effect/test/Schema}/Schema/ParseOptions-exact.test.ts (92%) rename packages/{schema/test => effect/test/Schema}/Schema/ParseOptions-onExcessProperty.test.ts (94%) rename packages/{schema/test => effect/test/Schema}/Schema/ParseOptions-preserveKeyOrder.test.ts (95%) rename packages/{schema/test => effect/test/Schema}/Schema/ParseOptionsAnnotation.test.ts (94%) rename packages/{schema/test => effect/test/Schema}/Schema/PropertySignature.test.ts (96%) rename packages/{schema/test => effect/test/Schema}/Schema/ReadonlyMap/ReadonlyMap.test.ts (93%) rename packages/{schema/test => effect/test/Schema}/Schema/ReadonlyMap/ReadonlyMapFromRecord.test.ts (95%) rename packages/{schema/test => effect/test/Schema}/Schema/ReadonlyMap/ReadonlyMapFromSelf.test.ts (92%) rename packages/{schema/test => effect/test/Schema}/Schema/ReadonlySet/ReadonlySet.test.ts (91%) rename packages/{schema/test => effect/test/Schema}/Schema/ReadonlySet/ReadonlySetFromSelf.test.ts (90%) rename packages/{schema/test => effect/test/Schema}/Schema/Record/Record.test.ts (93%) rename packages/{schema/test => effect/test/Schema}/Schema/Redacted/Redacted.test.ts (86%) rename packages/{schema/test => effect/test/Schema}/Schema/Redacted/RedactedFromSelf.test.ts (86%) rename packages/{schema/test => effect/test/Schema}/Schema/Set/Set.test.ts (82%) rename packages/{schema/test => effect/test/Schema}/Schema/Set/SetFromSelf.test.ts (81%) rename packages/{schema/test => effect/test/Schema}/Schema/SortedSet/SortedSet.test.ts (93%) rename packages/{schema/test => effect/test/Schema}/Schema/SortedSet/SortedSetFromSelf.test.ts (91%) rename packages/{schema/test => effect/test/Schema}/Schema/String/NonEmptyTrimmedString.test.ts (93%) rename packages/{schema/test => effect/test/Schema}/Schema/String/String.test.ts (78%) rename packages/{schema/test => effect/test/Schema}/Schema/String/StringFromBase64.test.ts (91%) rename packages/{schema/test => effect/test/Schema}/Schema/String/StringFromBase64Url.test.ts (91%) rename packages/{schema/test => effect/test/Schema}/Schema/String/StringFromHex.test.ts (93%) rename packages/{schema/test => effect/test/Schema}/Schema/String/capitalize.test.ts (90%) rename packages/{schema/test => effect/test/Schema}/Schema/String/endsWith.test.ts (86%) rename packages/{schema/test => effect/test/Schema}/Schema/String/includes.test.ts (83%) rename packages/{schema/test => effect/test/Schema}/Schema/String/length.test.ts (96%) rename packages/{schema/test => effect/test/Schema}/Schema/String/lowercase.test.ts (89%) rename packages/{schema/test => effect/test/Schema}/Schema/String/maxLength.test.ts (81%) rename packages/{schema/test => effect/test/Schema}/Schema/String/minLength.test.ts (81%) rename packages/{schema/test => effect/test/Schema}/Schema/String/nonEmptyString.test.ts (87%) rename packages/{schema/test => effect/test/Schema}/Schema/String/pattern.test.ts (92%) rename packages/{schema/test => effect/test/Schema}/Schema/String/split.test.ts (91%) rename packages/{schema/test => effect/test/Schema}/Schema/String/startsWith.test.ts (86%) rename packages/{schema/test => effect/test/Schema}/Schema/String/trim.test.ts (96%) rename packages/{schema/test => effect/test/Schema}/Schema/String/uncapitalize.test.ts (90%) rename packages/{schema/test => effect/test/Schema}/Schema/String/uppercase.test.ts (89%) rename packages/{schema/test => effect/test/Schema}/Schema/Struct/Struct.test.ts (97%) rename packages/{schema/test => effect/test/Schema}/Schema/Struct/make.test.ts (97%) rename packages/{schema/test => effect/test/Schema}/Schema/Struct/omit.test.ts (85%) rename packages/{schema/test => effect/test/Schema}/Schema/Struct/pick.test.ts (86%) rename packages/{schema/test => effect/test/Schema}/Schema/Symbol/Symbol.test.ts (88%) rename packages/{schema/test => effect/test/Schema}/Schema/Symbol/SymbolFromSelf.test.ts (66%) rename packages/{schema/test => effect/test/Schema}/Schema/TaggedStruct/make.test.ts (89%) rename packages/{schema/test => effect/test/Schema}/Schema/TemplateLiteral/TemplateLiteral.test.ts (98%) rename packages/{schema/test => effect/test/Schema}/Schema/TemplateLiteralParser.test.ts (97%) rename packages/{schema/test => effect/test/Schema}/Schema/Trimmed/Trimmed.test.ts (92%) rename packages/{schema/test => effect/test/Schema}/Schema/Tuple/Tuple.test.ts (99%) rename packages/{schema/test => effect/test/Schema}/Schema/ULID.test.ts (81%) rename packages/{schema/test => effect/test/Schema}/Schema/UUID.test.ts (82%) rename packages/{schema/test => effect/test/Schema}/Schema/Uint8Array/Uint8Array.test.ts (90%) rename packages/{schema/test => effect/test/Schema}/Schema/Uint8Array/Uint8ArrayFromBase64.test.ts (91%) rename packages/{schema/test => effect/test/Schema}/Schema/Uint8Array/Uint8ArrayFromBase64Url.test.ts (92%) rename packages/{schema/test => effect/test/Schema}/Schema/Uint8Array/Uint8ArrayFromHex.test.ts (93%) rename packages/{schema/test => effect/test/Schema}/Schema/Uint8Array/Uint8ArrayFromSelf.test.ts (84%) rename packages/{schema/test => effect/test/Schema}/Schema/Union/Union.test.ts (98%) create mode 100644 packages/effect/test/Schema/Schema/UniqueSymbol/UniqueSymbolFromSelf.test.ts rename packages/{schema/test => effect/test/Schema}/Schema/Unknown/Unknown.test.ts (84%) rename packages/{schema/test => effect/test/Schema}/Schema/Void/Void.test.ts (85%) rename packages/{schema/test => effect/test/Schema}/Schema/annotations.test.ts (94%) rename packages/{schema/test => effect/test/Schema}/Schema/asserts.test.ts (93%) rename packages/{schema/test => effect/test/Schema}/Schema/attachPropertySignature.test.ts (91%) rename packages/{schema/test => effect/test/Schema}/Schema/brand.test.ts (97%) rename packages/{schema/test => effect/test/Schema}/Schema/compose.test.ts (96%) rename packages/{schema/test => effect/test/Schema}/Schema/decode.test.ts (92%) rename packages/{schema/test => effect/test/Schema}/Schema/decodeEither.test.ts (93%) rename packages/{schema/test => effect/test/Schema}/Schema/decodeOption.test.ts (89%) rename packages/{schema/test => effect/test/Schema}/Schema/decodePromise.test.ts (92%) rename packages/{schema/test => effect/test/Schema}/Schema/decodeSync.test.ts (93%) rename packages/{schema/test => effect/test/Schema}/Schema/decodeUnknownEither.test.ts (78%) rename packages/{schema/test => effect/test/Schema}/Schema/decodeUnknownOption.test.ts (67%) rename packages/{schema/test => effect/test/Schema}/Schema/decodeUnknownSync.test.ts (84%) rename packages/{schema/test => effect/test/Schema}/Schema/encode.test.ts (92%) rename packages/{schema/test => effect/test/Schema}/Schema/encodeEither.test.ts (93%) rename packages/{schema/test => effect/test/Schema}/Schema/encodeOption.test.ts (89%) rename packages/{schema/test => effect/test/Schema}/Schema/encodePromise.test.ts (92%) rename packages/{schema/test => effect/test/Schema}/Schema/encodeSync.test.ts (93%) rename packages/{schema/test => effect/test/Schema}/Schema/encodeUnknownEither.test.ts (78%) rename packages/{schema/test => effect/test/Schema}/Schema/encodeUnknownOption.test.ts (67%) rename packages/{schema/test => effect/test/Schema}/Schema/encodeUnknownSync.test.ts (79%) rename packages/{schema/test => effect/test/Schema}/Schema/encodedBoundSchema.test.ts (97%) rename packages/{schema/test => effect/test/Schema}/Schema/encodedSchema.test.ts (92%) rename packages/{schema/test => effect/test/Schema}/Schema/exports.test.ts (97%) rename packages/{schema/test => effect/test/Schema}/Schema/extend.test.ts (98%) rename packages/{schema/test => effect/test/Schema}/Schema/filter.test.ts (97%) rename packages/{schema/test => effect/test/Schema}/Schema/filterEffect.test.ts (97%) rename packages/{schema/test => effect/test/Schema}/Schema/format.test.ts (90%) rename packages/{schema/test => effect/test/Schema}/Schema/fromBrand.test.ts (95%) rename packages/{schema/test => effect/test/Schema}/Schema/getNumberIndexedAccess.test.ts (94%) rename packages/{schema/test => effect/test/Schema}/Schema/instanceOf.test.ts (85%) rename packages/{schema/test => effect/test/Schema}/Schema/is.test.ts (96%) rename packages/{schema/test => effect/test/Schema}/Schema/isSchema.test.ts (92%) rename packages/{schema/test => effect/test/Schema}/Schema/keyof.test.ts (93%) rename packages/{schema/test => effect/test/Schema}/Schema/mutable.test.ts (96%) rename packages/{schema/test => effect/test/Schema}/Schema/nonEmptyArray.test.ts (85%) rename packages/{schema/test => effect/test/Schema}/Schema/omit.test.ts (89%) rename packages/{schema/test => effect/test/Schema}/Schema/optional.test.ts (96%) rename packages/{schema/test => effect/test/Schema}/Schema/optionalToRequired.test.ts (87%) rename packages/{schema/test => effect/test/Schema}/Schema/optionalWith.test.ts (99%) rename packages/{schema/test => effect/test/Schema}/Schema/parseJson.test.ts (97%) rename packages/{schema/test => effect/test/Schema}/Schema/partial.test.ts (97%) rename packages/{schema/test => effect/test/Schema}/Schema/partialWith.test.ts (98%) rename packages/{schema/test => effect/test/Schema}/Schema/pick.test.ts (83%) rename packages/{schema/test => effect/test/Schema}/Schema/pickLiteral.test.ts (90%) rename packages/{schema/test => effect/test/Schema}/Schema/pipe.test.ts (92%) rename packages/{schema/test => effect/test/Schema}/Schema/pluck.test.ts (97%) rename packages/{schema/test => effect/test/Schema}/Schema/rename.test.ts (92%) rename packages/{schema/test => effect/test/Schema}/Schema/required.test.ts (98%) rename packages/{schema/test => effect/test/Schema}/Schema/requiredToOptional.test.ts (87%) rename packages/{schema/test => effect/test/Schema}/Schema/suspend.test.ts (97%) rename packages/{schema/test => effect/test/Schema}/Schema/toString.test.ts (95%) rename packages/{schema/test => effect/test/Schema}/Schema/transform.test.ts (86%) rename packages/{schema/test => effect/test/Schema}/Schema/transformLiterals.test.ts (92%) rename packages/{schema/test => effect/test/Schema}/Schema/transformOrFail.test.ts (81%) rename packages/{schema/test => effect/test/Schema}/Schema/typeSchema.test.ts (93%) rename packages/{schema/test => effect/test/Schema}/Schema/validate.test.ts (96%) rename packages/{schema/test => effect/test/Schema}/Schema/validateEither.test.ts (93%) rename packages/{schema/test => effect/test/Schema}/Schema/validateOption.test.ts (89%) rename packages/{schema/test => effect/test/Schema}/Schema/validatePromise.test.ts (92%) rename packages/{schema/test => effect/test/Schema}/Schema/validateSync.test.ts (93%) rename packages/{schema/test => effect/test/Schema}/Schema/withConstructorDefault.test.ts (95%) rename packages/{schema/test => effect/test/Schema}/Schema/withDecodingDefault.test.ts (94%) rename packages/{schema/test/AST => effect/test/Schema/SchemaAST}/IndexSignature.test.ts (92%) rename packages/{schema/test/AST => effect/test/Schema/SchemaAST}/Tuple.test.ts (95%) rename packages/{schema/test/AST => effect/test/Schema/SchemaAST}/TypeLiteral.test.ts (84%) rename packages/{schema/test/AST => effect/test/Schema/SchemaAST}/TypeLiteralTransformation.test.ts (95%) rename packages/{schema/test/AST => effect/test/Schema/SchemaAST}/Union.test.ts (95%) rename packages/{schema/test/AST => effect/test/Schema/SchemaAST}/annotations.test.ts (93%) rename packages/{schema/test/AST => effect/test/Schema/SchemaAST}/encodedAST.test.ts (96%) rename packages/{schema/test/AST => effect/test/Schema/SchemaAST}/encodedBoundAST.test.ts (96%) rename packages/{schema/test/AST => effect/test/Schema/SchemaAST}/getPropertySignatures.test.ts (92%) rename packages/{schema/test/AST => effect/test/Schema/SchemaAST}/guards.test.ts (96%) rename packages/{schema/test/AST => effect/test/Schema/SchemaAST}/mutable.test.ts (92%) rename packages/{schema/test/AST => effect/test/Schema/SchemaAST}/partial.test.ts (97%) rename packages/{schema/test/AST => effect/test/Schema/SchemaAST}/pick.test.ts (96%) rename packages/{schema/test/AST => effect/test/Schema/SchemaAST}/record.test.ts (94%) rename packages/{schema/test/AST => effect/test/Schema/SchemaAST}/suspend.test.ts (85%) rename packages/{schema/test/AST => effect/test/Schema/SchemaAST}/toString.test.ts (95%) rename packages/{schema/test/AST => effect/test/Schema/SchemaAST}/typeAST.test.ts (96%) rename packages/{schema/test/AST => effect/test/Schema/SchemaAST}/unify.test.ts (96%) rename packages/{schema/test/Equivalence.test.ts => effect/test/Schema/SchemaEquivalence.test.ts} (97%) rename packages/{schema/test/Formatter.test.ts => effect/test/Schema/SchemaFormatters.test.ts} (98%) create mode 100644 packages/effect/test/Schema/SchemaInternal.test.ts rename packages/{schema/test/userland.test.ts => effect/test/Schema/SchemaUserland.test.ts} (94%) rename packages/{schema/test => effect/test/Schema}/Serializable.test.ts (96%) rename packages/{schema/test => effect/test/Schema}/TestUtils.ts (96%) delete mode 100644 packages/schema/dtslint/tsconfig.json delete mode 100644 packages/schema/test/Schema/UniqueSymbol/UniqueSymbolFromSelf.test.ts delete mode 100644 packages/schema/test/formatUnknown.test.ts create mode 100644 packages/schema/test/index.test.ts delete mode 100644 packages/schema/test/util.test.ts diff --git a/.changeset/cyan-sloths-lick.md b/.changeset/cyan-sloths-lick.md new file mode 100644 index 0000000000..2af813913e --- /dev/null +++ b/.changeset/cyan-sloths-lick.md @@ -0,0 +1,5 @@ +--- +"@effect/schema": minor +--- + +Re-export modules from `effect` diff --git a/.changeset/six-crabs-itch.md b/.changeset/six-crabs-itch.md new file mode 100644 index 0000000000..433cec855f --- /dev/null +++ b/.changeset/six-crabs-itch.md @@ -0,0 +1,41 @@ +--- +"effect": minor +--- + +Merge Schema into Effect. + +Before + +```ts +import { + Arbitrary, + ArrayFormatter, + AST, + Equivalence, + FastCheck, + JSONSchema, + ParseResult, + Pretty, + Schema, + Serializable, + TreeFormatter +} from "@effect/schema" +``` + +After + +```ts +import { + Arbitrary, + SchemaArrayFormatter, // changed + SchemaAST, // changed + SchemaEquivalence, // changed + FastCheck, + JSONSchema, + ParseResult, + Pretty, + Schema, + Serializable, + SchemaTreeFormatter // changed +} from "effect" +``` diff --git a/packages/cli/src/Args.ts b/packages/cli/src/Args.ts index dd2e7aca7a..28b88652b7 100644 --- a/packages/cli/src/Args.ts +++ b/packages/cli/src/Args.ts @@ -4,13 +4,13 @@ import type { FileSystem } from "@effect/platform/FileSystem" import type { Path } from "@effect/platform/Path" import type { QuitException, Terminal } from "@effect/platform/Terminal" -import type { Schema } from "@effect/schema/Schema" import type { NonEmptyArray } from "effect/Array" import type { Config } from "effect/Config" import type { Effect } from "effect/Effect" import type { Option } from "effect/Option" import type { Pipeable } from "effect/Pipeable" import type { Redacted } from "effect/Redacted" +import type { Schema } from "effect/Schema" import type { Secret } from "effect/Secret" import type { CliConfig } from "./CliConfig.js" import type { HelpDoc } from "./HelpDoc.js" diff --git a/packages/cli/src/Options.ts b/packages/cli/src/Options.ts index 4d68c3bdc5..68f296ef52 100644 --- a/packages/cli/src/Options.ts +++ b/packages/cli/src/Options.ts @@ -4,7 +4,6 @@ import type { FileSystem } from "@effect/platform/FileSystem" import type { Path } from "@effect/platform/Path" import type { QuitException, Terminal } from "@effect/platform/Terminal" -import type { Schema } from "@effect/schema/Schema" import type { NonEmptyArray } from "effect/Array" import type { Config } from "effect/Config" import type { Effect } from "effect/Effect" @@ -13,6 +12,7 @@ import type { HashMap } from "effect/HashMap" import type { Option } from "effect/Option" import type { Pipeable } from "effect/Pipeable" import type { Redacted } from "effect/Redacted" +import type { Schema } from "effect/Schema" import type { Secret } from "effect/Secret" import type { CliConfig } from "./CliConfig.js" import type { HelpDoc } from "./HelpDoc.js" diff --git a/packages/cli/src/internal/args.ts b/packages/cli/src/internal/args.ts index c7f404ee4f..6fa5b41475 100644 --- a/packages/cli/src/internal/args.ts +++ b/packages/cli/src/internal/args.ts @@ -1,8 +1,6 @@ import type * as FileSystem from "@effect/platform/FileSystem" import type * as Path from "@effect/platform/Path" import type * as Terminal from "@effect/platform/Terminal" -import * as Schema from "@effect/schema/Schema" -import * as TreeFormatter from "@effect/schema/TreeFormatter" import * as Arr from "effect/Array" import type * as Config from "effect/Config" import * as Console from "effect/Console" @@ -13,6 +11,8 @@ import * as Option from "effect/Option" import { pipeArguments } from "effect/Pipeable" import type * as Redacted from "effect/Redacted" import * as Ref from "effect/Ref" +import * as Schema from "effect/Schema" +import * as TreeFormatter from "effect/SchemaTreeFormatter" import type * as Secret from "effect/Secret" import type * as Args from "../Args.js" import type * as CliConfig from "../CliConfig.js" diff --git a/packages/cli/src/internal/options.ts b/packages/cli/src/internal/options.ts index 3151ff4371..abf8acbb65 100644 --- a/packages/cli/src/internal/options.ts +++ b/packages/cli/src/internal/options.ts @@ -1,8 +1,6 @@ import type * as FileSystem from "@effect/platform/FileSystem" import type * as Path from "@effect/platform/Path" import type * as Terminal from "@effect/platform/Terminal" -import * as Schema from "@effect/schema/Schema" -import * as TreeFormatter from "@effect/schema/TreeFormatter" import * as Arr from "effect/Array" import * as Config from "effect/Config" import * as Console from "effect/Console" @@ -16,6 +14,8 @@ import { pipeArguments } from "effect/Pipeable" import * as Predicate from "effect/Predicate" import type * as Redacted from "effect/Redacted" import * as Ref from "effect/Ref" +import * as Schema from "effect/Schema" +import * as TreeFormatter from "effect/SchemaTreeFormatter" import type * as Secret from "effect/Secret" import type * as CliConfig from "../CliConfig.js" import type * as HelpDoc from "../HelpDoc.js" diff --git a/packages/cli/src/internal/primitive.ts b/packages/cli/src/internal/primitive.ts index ccd033eb2a..d3fb2f6308 100644 --- a/packages/cli/src/internal/primitive.ts +++ b/packages/cli/src/internal/primitive.ts @@ -1,11 +1,11 @@ import * as FileSystem from "@effect/platform/FileSystem" -import * as Schema from "@effect/schema/Schema" import * as Arr from "effect/Array" import * as Effect from "effect/Effect" import { dual, pipe } from "effect/Function" import * as Option from "effect/Option" import { pipeArguments } from "effect/Pipeable" import * as EffectRedacted from "effect/Redacted" +import * as Schema from "effect/Schema" import * as EffectSecret from "effect/Secret" import type * as CliConfig from "../CliConfig.js" import type * as HelpDoc from "../HelpDoc.js" diff --git a/packages/cli/src/internal/prompt/number.ts b/packages/cli/src/internal/prompt/number.ts index f2d69334bf..8726aaaad3 100644 --- a/packages/cli/src/internal/prompt/number.ts +++ b/packages/cli/src/internal/prompt/number.ts @@ -2,11 +2,11 @@ import * as Terminal from "@effect/platform/Terminal" import * as Ansi from "@effect/printer-ansi/Ansi" import * as Doc from "@effect/printer-ansi/AnsiDoc" import * as Optimize from "@effect/printer/Optimize" -import * as Schema from "@effect/schema/Schema" import * as Arr from "effect/Array" import * as Effect from "effect/Effect" import * as EffectNumber from "effect/Number" import * as Option from "effect/Option" +import * as Schema from "effect/Schema" import type * as Prompt from "../../Prompt.js" import * as InternalPrompt from "../prompt.js" import { Action } from "./action.js" diff --git a/packages/cli/test/Args.test.ts b/packages/cli/test/Args.test.ts index 9fdaf394d2..fd16393f60 100644 --- a/packages/cli/test/Args.test.ts +++ b/packages/cli/test/Args.test.ts @@ -4,10 +4,10 @@ import * as HelpDoc from "@effect/cli/HelpDoc" import * as ValidationError from "@effect/cli/ValidationError" import { FileSystem, Path } from "@effect/platform" import { NodeContext } from "@effect/platform-node" -import * as Schema from "@effect/schema/Schema" import * as Array from "effect/Array" import * as Effect from "effect/Effect" import * as Option from "effect/Option" +import * as Schema from "effect/Schema" import { describe, expect, it } from "vitest" const runEffect = ( diff --git a/packages/cli/test/Options.test.ts b/packages/cli/test/Options.test.ts index f493f2e91a..8402450dc4 100644 --- a/packages/cli/test/Options.test.ts +++ b/packages/cli/test/Options.test.ts @@ -5,7 +5,6 @@ import * as ValidationError from "@effect/cli/ValidationError" import * as NodeContext from "@effect/platform-node/NodeContext" import * as FileSystem from "@effect/platform/FileSystem" import * as Path from "@effect/platform/Path" -import * as Schema from "@effect/schema/Schema" import { BigDecimal } from "effect" import * as Array from "effect/Array" import * as Data from "effect/Data" @@ -14,6 +13,7 @@ import * as Either from "effect/Either" import { identity } from "effect/Function" import * as HashMap from "effect/HashMap" import * as Option from "effect/Option" +import * as Schema from "effect/Schema" import { assert, describe, expect, it } from "vitest" const firstName = Options.text("firstName").pipe(Options.withAlias("f")) diff --git a/packages/cluster-browser/src/RpcBroadcastChannel.ts b/packages/cluster-browser/src/RpcBroadcastChannel.ts index 31b89f091a..94d17de53c 100644 --- a/packages/cluster-browser/src/RpcBroadcastChannel.ts +++ b/packages/cluster-browser/src/RpcBroadcastChannel.ts @@ -4,12 +4,12 @@ import type * as Rpc from "@effect/rpc/Rpc" import * as RpcResolver from "@effect/rpc/RpcResolver" import * as RpcRouter from "@effect/rpc/RpcRouter" -import * as Schema from "@effect/schema/Schema" -import type * as Serializable from "@effect/schema/Serializable" import * as Effect from "effect/Effect" import { pipe } from "effect/Function" import * as Queue from "effect/Queue" import type * as RequestResolver from "effect/RequestResolver" +import * as Schema from "effect/Schema" +import type * as Serializable from "effect/Serializable" import * as Stream from "effect/Stream" class ClientRequest extends Schema.TaggedClass()("ClientRequest", { diff --git a/packages/cluster-node/src/ShardManagerProtocol.ts b/packages/cluster-node/src/ShardManagerProtocol.ts index f5d5d8b8ac..d48edf8846 100644 --- a/packages/cluster-node/src/ShardManagerProtocol.ts +++ b/packages/cluster-node/src/ShardManagerProtocol.ts @@ -4,7 +4,7 @@ import * as Pod from "@effect/cluster/Pod" import * as PodAddress from "@effect/cluster/PodAddress" import * as ShardId from "@effect/cluster/ShardId" -import * as Schema from "@effect/schema/Schema" +import * as Schema from "effect/Schema" /** * @since 1.0.0 diff --git a/packages/cluster-node/src/ShardingProtocol.ts b/packages/cluster-node/src/ShardingProtocol.ts index 684484f78d..8ce4de0c49 100644 --- a/packages/cluster-node/src/ShardingProtocol.ts +++ b/packages/cluster-node/src/ShardingProtocol.ts @@ -6,7 +6,7 @@ import * as SerializedEnvelope from "@effect/cluster/SerializedEnvelope" import * as SerializedMessage from "@effect/cluster/SerializedMessage" import * as ShardId from "@effect/cluster/ShardId" import * as ShardingException from "@effect/cluster/ShardingException" -import * as Schema from "@effect/schema/Schema" +import * as Schema from "effect/Schema" /** * @since 1.0.0 diff --git a/packages/cluster-node/src/StorageFile.ts b/packages/cluster-node/src/StorageFile.ts index 6e9e3fa03e..e330b667eb 100644 --- a/packages/cluster-node/src/StorageFile.ts +++ b/packages/cluster-node/src/StorageFile.ts @@ -6,14 +6,14 @@ import * as PodAddress from "@effect/cluster/PodAddress" import * as ShardId from "@effect/cluster/ShardId" import * as ShardingException from "@effect/cluster/ShardingException" import * as Storage from "@effect/cluster/Storage" -import * as Schema from "@effect/schema/Schema" -import * as TreeFormatter from "@effect/schema/TreeFormatter" import * as Effect from "effect/Effect" import { pipe } from "effect/Function" import * as HashMap from "effect/HashMap" import * as Layer from "effect/Layer" import type * as Option from "effect/Option" import * as Queue from "effect/Queue" +import * as Schema from "effect/Schema" +import * as TreeFormatter from "effect/SchemaTreeFormatter" import * as Stream from "effect/Stream" import * as fs from "node:fs" diff --git a/packages/cluster-workflow/src/Activity.ts b/packages/cluster-workflow/src/Activity.ts index d40b2a4745..229da333ca 100644 --- a/packages/cluster-workflow/src/Activity.ts +++ b/packages/cluster-workflow/src/Activity.ts @@ -1,11 +1,11 @@ /** * @since 1.0.0 */ -import type * as Schema from "@effect/schema/Schema" import * as Effect from "effect/Effect" import type * as Exit from "effect/Exit" import { pipe } from "effect/Function" import * as Option from "effect/Option" +import type * as Schema from "effect/Schema" import * as Stream from "effect/Stream" import * as ActivityContext from "./ActivityContext.js" import * as DurableExecutionEvent from "./DurableExecutionEvent.js" diff --git a/packages/cluster-workflow/src/DurableExecutionEvent.ts b/packages/cluster-workflow/src/DurableExecutionEvent.ts index 7de0b6f2e6..09e816cc27 100644 --- a/packages/cluster-workflow/src/DurableExecutionEvent.ts +++ b/packages/cluster-workflow/src/DurableExecutionEvent.ts @@ -1,8 +1,8 @@ /** * @since 1.0.0 */ -import * as Schema from "@effect/schema/Schema" import type * as Exit from "effect/Exit" +import * as Schema from "effect/Schema" const ATTEMPTED = "@effect/cluster-workflow/DurableExecutionEvent/Attempted" diff --git a/packages/cluster-workflow/src/DurableExecutionJournal.ts b/packages/cluster-workflow/src/DurableExecutionJournal.ts index 8781dd7eae..d96e4a6d6b 100644 --- a/packages/cluster-workflow/src/DurableExecutionJournal.ts +++ b/packages/cluster-workflow/src/DurableExecutionJournal.ts @@ -2,11 +2,11 @@ * @since 1.0.0 */ import * as DurableExecutionEvent from "@effect/cluster-workflow/DurableExecutionEvent" -import * as Schema from "@effect/schema/Schema" import * as SqlClient from "@effect/sql/SqlClient" import * as Context from "effect/Context" import * as Effect from "effect/Effect" import * as Layer from "effect/Layer" +import * as Schema from "effect/Schema" import * as Stream from "effect/Stream" const SymbolKey = "@effect/cluster-workflow/DurableExecutionJournal" diff --git a/packages/cluster-workflow/src/Workflow.ts b/packages/cluster-workflow/src/Workflow.ts index 8e76c670c3..c451a2b88d 100644 --- a/packages/cluster-workflow/src/Workflow.ts +++ b/packages/cluster-workflow/src/Workflow.ts @@ -2,13 +2,13 @@ * @since 1.0.0 */ import type * as Message from "@effect/cluster/Message" -import * as Schema from "@effect/schema/Schema" import * as Array from "effect/Array" import * as Clock from "effect/Clock" import * as Duration from "effect/Duration" import * as Effect from "effect/Effect" import { pipe } from "effect/Function" import * as Option from "effect/Option" +import * as Schema from "effect/Schema" import * as Activity from "./Activity.js" import * as WorkflowContext from "./WorkflowContext.js" diff --git a/packages/cluster-workflow/test/DurableExecutionJournal.test.ts b/packages/cluster-workflow/test/DurableExecutionJournal.test.ts index b3c263b1f0..8fbcd8cfad 100644 --- a/packages/cluster-workflow/test/DurableExecutionJournal.test.ts +++ b/packages/cluster-workflow/test/DurableExecutionJournal.test.ts @@ -2,12 +2,12 @@ import * as DurableExecutionEvent from "@effect/cluster-workflow/DurableExecutio import * as DurableExecutionJournal from "@effect/cluster-workflow/DurableExecutionJournal" import * as NodeFileSystem from "@effect/platform-node/NodeFileSystem" import * as FileSystem from "@effect/platform/FileSystem" -import * as Schema from "@effect/schema/Schema" import * as Sqlite from "@effect/sql-sqlite-node/SqliteClient" import * as SqlClient from "@effect/sql/SqlClient" import * as Chunk from "effect/Chunk" import * as Effect from "effect/Effect" import * as Layer from "effect/Layer" +import * as Schema from "effect/Schema" import * as Stream from "effect/Stream" import { describe, expect, it } from "vitest" diff --git a/packages/cluster-workflow/test/Workflow.test.ts b/packages/cluster-workflow/test/Workflow.test.ts index 31102c9a38..7c018cc577 100644 --- a/packages/cluster-workflow/test/Workflow.test.ts +++ b/packages/cluster-workflow/test/Workflow.test.ts @@ -8,7 +8,6 @@ import * as utils from "@effect/cluster-workflow/test/utils" import * as Workflow from "@effect/cluster-workflow/Workflow" import * as WorkflowEngine from "@effect/cluster-workflow/WorkflowEngine" import * as Message from "@effect/cluster/Message" -import * as Schema from "@effect/schema/Schema" import * as Chunk from "effect/Chunk" import * as Deferred from "effect/Deferred" import * as Effect from "effect/Effect" @@ -17,6 +16,7 @@ import { pipe } from "effect/Function" import * as Logger from "effect/Logger" import * as LogLevel from "effect/LogLevel" import * as Ref from "effect/Ref" +import * as Schema from "effect/Schema" import * as Stream from "effect/Stream" import { describe, expect, it } from "vitest" diff --git a/packages/cluster-workflow/test/utils.ts b/packages/cluster-workflow/test/utils.ts index 0efe645720..4c2183c0b2 100644 --- a/packages/cluster-workflow/test/utils.ts +++ b/packages/cluster-workflow/test/utils.ts @@ -1,8 +1,8 @@ import * as Activity from "@effect/cluster-workflow/Activity" -import type * as Schema from "@effect/schema/Schema" import * as Effect from "effect/Effect" import type * as Exit from "effect/Exit" import { pipe } from "effect/Function" +import type * as Schema from "effect/Schema" import { vi } from "vitest" import type { Mock } from "vitest" diff --git a/packages/cluster/src/Message.ts b/packages/cluster/src/Message.ts index ad67e73162..0df848107c 100644 --- a/packages/cluster/src/Message.ts +++ b/packages/cluster/src/Message.ts @@ -1,10 +1,10 @@ /** * @since 1.0.0 */ -import type * as Schema from "@effect/schema/Schema" -import type * as Serializable from "@effect/schema/Serializable" import type * as Exit_ from "effect/Exit" import type * as PrimaryKey from "effect/PrimaryKey" +import type * as Schema from "effect/Schema" +import type * as Serializable from "effect/Serializable" import type * as Types from "effect/Types" import * as internal from "./internal/message.js" diff --git a/packages/cluster/src/MessageState.ts b/packages/cluster/src/MessageState.ts index 0f0393f629..4d4c16be69 100644 --- a/packages/cluster/src/MessageState.ts +++ b/packages/cluster/src/MessageState.ts @@ -1,8 +1,8 @@ /** * @since 1.0.0 */ -import type * as Schema from "@effect/schema/Schema" import type * as Effect from "effect/Effect" +import type * as Schema from "effect/Schema" import * as internal from "./internal/messageState.js" /** diff --git a/packages/cluster/src/Pod.ts b/packages/cluster/src/Pod.ts index a03146c467..8833b52230 100644 --- a/packages/cluster/src/Pod.ts +++ b/packages/cluster/src/Pod.ts @@ -1,7 +1,7 @@ /** * @since 1.0.0 */ -import * as Schema from "@effect/schema/Schema" +import * as Schema from "effect/Schema" import { TypeIdSchema } from "./internal/utils.js" import * as PodAddress from "./PodAddress.js" diff --git a/packages/cluster/src/PodAddress.ts b/packages/cluster/src/PodAddress.ts index ce49383848..69929b7bf6 100644 --- a/packages/cluster/src/PodAddress.ts +++ b/packages/cluster/src/PodAddress.ts @@ -1,7 +1,7 @@ /** * @since 1.0.0 */ -import * as Schema from "@effect/schema/Schema" +import * as Schema from "effect/Schema" import { TypeIdSchema } from "./internal/utils.js" /** @internal */ diff --git a/packages/cluster/src/PoisonPill.ts b/packages/cluster/src/PoisonPill.ts index d87b71c5a7..81d99cf18a 100644 --- a/packages/cluster/src/PoisonPill.ts +++ b/packages/cluster/src/PoisonPill.ts @@ -1,10 +1,10 @@ /** * @since 1.0.0 */ -import * as Schema from "@effect/schema/Schema" import * as Effect from "effect/Effect" import { pipe } from "effect/Function" import * as Queue from "effect/Queue" +import * as Schema from "effect/Schema" import { TypeIdSchema } from "./internal/utils.js" /** @internal */ diff --git a/packages/cluster/src/RecipientAddress.ts b/packages/cluster/src/RecipientAddress.ts index a5806489ca..36ac78f2e3 100644 --- a/packages/cluster/src/RecipientAddress.ts +++ b/packages/cluster/src/RecipientAddress.ts @@ -1,9 +1,9 @@ /** * @since 1.0.0 */ -import * as Schema from "@effect/schema/Schema" import * as Equal from "effect/Equal" import * as Hash from "effect/Hash" +import * as Schema from "effect/Schema" import { TypeIdSchema } from "./internal/utils.js" const RecipientAddressSymbolKey = "@effect/cluster/RecipientAddress" diff --git a/packages/cluster/src/RecipientType.ts b/packages/cluster/src/RecipientType.ts index cb0c48b443..2b20ac38aa 100644 --- a/packages/cluster/src/RecipientType.ts +++ b/packages/cluster/src/RecipientType.ts @@ -1,10 +1,10 @@ /** * @since 1.0.0 */ -import type * as Schema from "@effect/schema/Schema" import * as Data from "effect/Data" import * as Equal from "effect/Equal" import * as Hash from "effect/Hash" +import type * as Schema from "effect/Schema" import type * as Message from "./Message.js" import * as ShardId from "./ShardId.js" diff --git a/packages/cluster/src/Serialization.ts b/packages/cluster/src/Serialization.ts index 7285a6dbbf..4250f7e31e 100644 --- a/packages/cluster/src/Serialization.ts +++ b/packages/cluster/src/Serialization.ts @@ -1,10 +1,10 @@ /** * @since 1.0.0 */ -import type * as Schema from "@effect/schema/Schema" import type * as Context from "effect/Context" import type * as Effect from "effect/Effect" import type * as Layer from "effect/Layer" +import type * as Schema from "effect/Schema" import * as internal from "./internal/serialization.js" import type * as SerializedMessage from "./SerializedMessage.js" import type * as ShardingException from "./ShardingException.js" diff --git a/packages/cluster/src/SerializedEnvelope.ts b/packages/cluster/src/SerializedEnvelope.ts index 0e3dc294e5..36d12adc81 100644 --- a/packages/cluster/src/SerializedEnvelope.ts +++ b/packages/cluster/src/SerializedEnvelope.ts @@ -1,9 +1,9 @@ /** * @since 1.0.0 */ -import * as Schema from "@effect/schema/Schema" -import * as Serializable from "@effect/schema/Serializable" import * as PrimaryKey from "effect/PrimaryKey" +import * as Schema from "effect/Schema" +import * as Serializable from "effect/Serializable" import { TypeIdSchema } from "./internal/utils.js" import * as RecipientAddress from "./RecipientAddress.js" import * as SerializedMessage from "./SerializedMessage.js" diff --git a/packages/cluster/src/SerializedMessage.ts b/packages/cluster/src/SerializedMessage.ts index 3dcedd83c1..c7e3d797e6 100644 --- a/packages/cluster/src/SerializedMessage.ts +++ b/packages/cluster/src/SerializedMessage.ts @@ -1,7 +1,7 @@ /** * @since 1.0.0 */ -import * as Schema from "@effect/schema/Schema" +import * as Schema from "effect/Schema" import { TypeIdSchema } from "./internal/utils.js" /** @internal */ diff --git a/packages/cluster/src/ShardId.ts b/packages/cluster/src/ShardId.ts index baeb49c625..25902a2866 100644 --- a/packages/cluster/src/ShardId.ts +++ b/packages/cluster/src/ShardId.ts @@ -1,7 +1,7 @@ /** * @since 1.0.0 */ -import * as Schema from "@effect/schema/Schema" +import * as Schema from "effect/Schema" import { TypeIdSchema } from "./internal/utils.js" /** @internal */ diff --git a/packages/cluster/src/ShardingException.ts b/packages/cluster/src/ShardingException.ts index 7f23d95032..656679f06d 100644 --- a/packages/cluster/src/ShardingException.ts +++ b/packages/cluster/src/ShardingException.ts @@ -1,7 +1,7 @@ /** * @since 1.0.0 */ -import * as Schema from "@effect/schema/Schema" +import * as Schema from "effect/Schema" import * as PodAddress from "./PodAddress.js" import * as RecipientAddress from "./RecipientAddress.js" diff --git a/packages/cluster/src/internal/atLeastOnceStorage.ts b/packages/cluster/src/internal/atLeastOnceStorage.ts index 64e434714e..200ad0780b 100644 --- a/packages/cluster/src/internal/atLeastOnceStorage.ts +++ b/packages/cluster/src/internal/atLeastOnceStorage.ts @@ -1,4 +1,3 @@ -import * as Schema from "@effect/schema/Schema" import * as SqlClient from "@effect/sql/SqlClient" import type * as SqlError from "@effect/sql/SqlError" import * as SqlResolver from "@effect/sql/SqlResolver" @@ -6,6 +5,7 @@ import * as Context from "effect/Context" import * as Effect from "effect/Effect" import * as Layer from "effect/Layer" import * as PrimaryKey from "effect/PrimaryKey" +import * as Schema from "effect/Schema" import * as Stream from "effect/Stream" import type * as AtLeastOnceStorage from "../AtLeastOnceStorage.js" import * as RecipientAddress from "../RecipientAddress.js" diff --git a/packages/cluster/src/internal/message.ts b/packages/cluster/src/internal/message.ts index d099076d33..c9f841e375 100644 --- a/packages/cluster/src/internal/message.ts +++ b/packages/cluster/src/internal/message.ts @@ -1,6 +1,6 @@ -import * as Schema from "@effect/schema/Schema" -import * as Serializable from "@effect/schema/Serializable" import * as PrimaryKey from "effect/PrimaryKey" +import * as Schema from "effect/Schema" +import * as Serializable from "effect/Serializable" import type * as Types from "effect/Types" import type * as Message from "../Message.js" diff --git a/packages/cluster/src/internal/messageState.ts b/packages/cluster/src/internal/messageState.ts index 811131386e..0f04f112e1 100644 --- a/packages/cluster/src/internal/messageState.ts +++ b/packages/cluster/src/internal/messageState.ts @@ -1,6 +1,6 @@ -import * as Schema from "@effect/schema/Schema" import * as Effect from "effect/Effect" import { pipe } from "effect/Function" +import * as Schema from "effect/Schema" import type * as MessageState from "../MessageState.js" /** @internal */ diff --git a/packages/cluster/src/internal/serialization.ts b/packages/cluster/src/internal/serialization.ts index 6ff8dc9851..e21f162e12 100644 --- a/packages/cluster/src/internal/serialization.ts +++ b/packages/cluster/src/internal/serialization.ts @@ -1,9 +1,9 @@ -import * as Schema from "@effect/schema/Schema" -import * as TreeFormatter from "@effect/schema/TreeFormatter" import * as Context from "effect/Context" import * as Effect from "effect/Effect" import { pipe } from "effect/Function" import * as Layer from "effect/Layer" +import * as Schema from "effect/Schema" +import * as TreeFormatter from "effect/SchemaTreeFormatter" import type * as Serialization from "../Serialization.js" import * as SerializedMessage from "../SerializedMessage.js" import * as ShardingException from "../ShardingException.js" diff --git a/packages/cluster/src/internal/utils.ts b/packages/cluster/src/internal/utils.ts index f604f3cd56..b7d632da8b 100644 --- a/packages/cluster/src/internal/utils.ts +++ b/packages/cluster/src/internal/utils.ts @@ -1,7 +1,7 @@ -import * as Schema from "@effect/schema/Schema" import * as HashMap from "effect/HashMap" import * as HashSet from "effect/HashSet" import * as Option from "effect/Option" +import * as Schema from "effect/Schema" /** @internal */ export function NotAMessageWithReplierDefect(message: unknown): unknown { diff --git a/packages/cluster/test/AtLeastOnce.test.ts b/packages/cluster/test/AtLeastOnce.test.ts index 404b5fb2d5..72daa85465 100644 --- a/packages/cluster/test/AtLeastOnce.test.ts +++ b/packages/cluster/test/AtLeastOnce.test.ts @@ -12,7 +12,6 @@ import * as ShardManagerClient from "@effect/cluster/ShardManagerClient" import * as Storage from "@effect/cluster/Storage" import * as NodeFileSystem from "@effect/platform-node/NodeFileSystem" import * as FileSystem from "@effect/platform/FileSystem" -import * as Schema from "@effect/schema/Schema" import * as Sqlite from "@effect/sql-sqlite-node/SqliteClient" import * as SqlClient from "@effect/sql/SqlClient" import * as Duration from "effect/Duration" @@ -21,6 +20,7 @@ import * as Exit from "effect/Exit" import { pipe } from "effect/Function" import * as Layer from "effect/Layer" import * as PrimaryKey from "effect/PrimaryKey" +import * as Schema from "effect/Schema" import { describe, expect, it } from "vitest" class SampleMessage extends Schema.TaggedRequest()( diff --git a/packages/cluster/test/RecipientBehaviour.test.ts b/packages/cluster/test/RecipientBehaviour.test.ts index b425431d9a..b34a0ea505 100644 --- a/packages/cluster/test/RecipientBehaviour.test.ts +++ b/packages/cluster/test/RecipientBehaviour.test.ts @@ -5,7 +5,6 @@ import * as RecipientBehaviour from "@effect/cluster/RecipientBehaviour" import * as RecipientBehaviourContext from "@effect/cluster/RecipientBehaviourContext" import * as RecipientType from "@effect/cluster/RecipientType" import * as ShardId from "@effect/cluster/ShardId" -import * as Schema from "@effect/schema/Schema" import * as Deferred from "effect/Deferred" import * as Effect from "effect/Effect" import * as Exit from "effect/Exit" @@ -14,6 +13,7 @@ import { pipe } from "effect/Function" import * as Logger from "effect/Logger" import * as LogLevel from "effect/LogLevel" import * as Queue from "effect/Queue" +import * as Schema from "effect/Schema" import * as Scope from "effect/Scope" import { describe, expect, it } from "vitest" diff --git a/packages/cluster/test/RecipientType.test.ts b/packages/cluster/test/RecipientType.test.ts index 71a522b411..59b11d38a5 100644 --- a/packages/cluster/test/RecipientType.test.ts +++ b/packages/cluster/test/RecipientType.test.ts @@ -1,8 +1,8 @@ import * as Message from "@effect/cluster/Message" import * as RecipientType from "@effect/cluster/RecipientType" -import * as Schema from "@effect/schema/Schema" import { equals } from "effect/Equal" import * as Hash from "effect/Hash" +import * as Schema from "effect/Schema" import { describe, expect, it } from "vitest" class Sample extends Message.TaggedMessage()("Sample", Schema.Never, Schema.Number, { diff --git a/packages/cluster/test/Sharding.test.ts b/packages/cluster/test/Sharding.test.ts index e6735cd6ac..20600c6411 100644 --- a/packages/cluster/test/Sharding.test.ts +++ b/packages/cluster/test/Sharding.test.ts @@ -11,7 +11,6 @@ import * as ShardingConfig from "@effect/cluster/ShardingConfig" import * as ShardingException from "@effect/cluster/ShardingException" import * as ShardManagerClient from "@effect/cluster/ShardManagerClient" import * as Storage from "@effect/cluster/Storage" -import * as Schema from "@effect/schema/Schema" import * as Cause from "effect/Cause" import * as Context from "effect/Context" import * as Deferred from "effect/Deferred" @@ -28,6 +27,7 @@ import * as Option from "effect/Option" import * as PrimaryKey from "effect/PrimaryKey" import * as Queue from "effect/Queue" import * as Ref from "effect/Ref" +import * as Schema from "effect/Schema" import { describe, expect, it } from "vitest" interface SampleService { diff --git a/packages/schema/benchmark/array.ts b/packages/effect/benchmark/SchemaArray.ts similarity index 94% rename from packages/schema/benchmark/array.ts rename to packages/effect/benchmark/SchemaArray.ts index ea57d31de3..8ac4aa4237 100644 --- a/packages/schema/benchmark/array.ts +++ b/packages/effect/benchmark/SchemaArray.ts @@ -1,6 +1,6 @@ -import type { ParseOptions } from "@effect/schema/AST" -import * as ParseResult from "@effect/schema/ParseResult" -import * as S from "@effect/schema/Schema" +import * as ParseResult from "effect/ParseResult" +import * as S from "effect/Schema" +import type { ParseOptions } from "effect/SchemaAST" import { Bench } from "tinybench" /* diff --git a/packages/schema/benchmark/filters.ts b/packages/effect/benchmark/SchemaFilters.ts similarity index 96% rename from packages/schema/benchmark/filters.ts rename to packages/effect/benchmark/SchemaFilters.ts index f1aff6696f..83fe3c795b 100644 --- a/packages/schema/benchmark/filters.ts +++ b/packages/effect/benchmark/SchemaFilters.ts @@ -1,6 +1,6 @@ -import type { ParseOptions } from "@effect/schema/AST" -import * as ParseResult from "@effect/schema/ParseResult" -import * as S from "@effect/schema/Schema" +import * as ParseResult from "effect/ParseResult" +import * as S from "effect/Schema" +import type { ParseOptions } from "effect/SchemaAST" import { Bench } from "tinybench" import { z } from "zod" diff --git a/packages/schema/benchmark/index.ts b/packages/effect/benchmark/SchemaIndex.ts similarity index 96% rename from packages/schema/benchmark/index.ts rename to packages/effect/benchmark/SchemaIndex.ts index fb38103666..a37145866e 100644 --- a/packages/schema/benchmark/index.ts +++ b/packages/effect/benchmark/SchemaIndex.ts @@ -1,6 +1,6 @@ -import type { ParseOptions } from "@effect/schema/AST" -import * as ParseResult from "@effect/schema/ParseResult" -import * as S from "@effect/schema/Schema" +import * as ParseResult from "effect/ParseResult" +import * as S from "effect/Schema" +import type { ParseOptions } from "effect/SchemaAST" import { Bench } from "tinybench" import { z } from "zod" diff --git a/packages/schema/benchmark/propertyOrder.ts b/packages/effect/benchmark/SchemaPropertyOrder.ts similarity index 96% rename from packages/schema/benchmark/propertyOrder.ts rename to packages/effect/benchmark/SchemaPropertyOrder.ts index 37440a9a90..65d9aea4d3 100644 --- a/packages/schema/benchmark/propertyOrder.ts +++ b/packages/effect/benchmark/SchemaPropertyOrder.ts @@ -1,5 +1,5 @@ -import * as ParseResult from "@effect/schema/ParseResult" -import * as S from "@effect/schema/Schema" +import * as ParseResult from "effect/ParseResult" +import * as S from "effect/Schema" import { Bench } from "tinybench" /* diff --git a/packages/schema/benchmark/Struct.ts b/packages/effect/benchmark/SchemaStruct.ts similarity index 95% rename from packages/schema/benchmark/Struct.ts rename to packages/effect/benchmark/SchemaStruct.ts index 20e4b3eb9c..35a86d0740 100644 --- a/packages/schema/benchmark/Struct.ts +++ b/packages/effect/benchmark/SchemaStruct.ts @@ -1,5 +1,5 @@ -import * as ParseResult from "@effect/schema/ParseResult" -import * as S from "@effect/schema/Schema" +import * as ParseResult from "effect/ParseResult" +import * as S from "effect/Schema" import { Bench } from "tinybench" /* diff --git a/packages/schema/benchmark/toString.ts b/packages/effect/benchmark/SchemaToString.ts similarity index 92% rename from packages/schema/benchmark/toString.ts rename to packages/effect/benchmark/SchemaToString.ts index 998927eea7..bfbd1d25f5 100644 --- a/packages/schema/benchmark/toString.ts +++ b/packages/effect/benchmark/SchemaToString.ts @@ -1,6 +1,6 @@ -import * as ParseResult from "@effect/schema/ParseResult" -import * as S from "@effect/schema/Schema" -import * as TreeFormatter from "@effect/schema/TreeFormatter" +import * as ParseResult from "effect/ParseResult" +import * as S from "effect/Schema" +import * as TreeFormatter from "effect/SchemaTreeFormatter" import { Bench } from "tinybench" /* diff --git a/packages/schema/benchmark/TreeFormatter.formatIssueSync.ts b/packages/effect/benchmark/SchemaTreeFormatter.ts similarity index 92% rename from packages/schema/benchmark/TreeFormatter.formatIssueSync.ts rename to packages/effect/benchmark/SchemaTreeFormatter.ts index e22a6d4e7b..38d3eb8a90 100644 --- a/packages/schema/benchmark/TreeFormatter.formatIssueSync.ts +++ b/packages/effect/benchmark/SchemaTreeFormatter.ts @@ -1,7 +1,7 @@ -import type * as ParseResult from "@effect/schema/ParseResult" -import * as S from "@effect/schema/Schema" -import * as TreeFormatter from "@effect/schema/TreeFormatter" import type * as Either from "effect/Either" +import type * as ParseResult from "effect/ParseResult" +import * as S from "effect/Schema" +import * as TreeFormatter from "effect/SchemaTreeFormatter" import { Bench } from "tinybench" /* diff --git a/packages/schema/benchmark/union.ts b/packages/effect/benchmark/SchemaUnion.ts similarity index 95% rename from packages/schema/benchmark/union.ts rename to packages/effect/benchmark/SchemaUnion.ts index 35d2603904..306c8c760f 100644 --- a/packages/schema/benchmark/union.ts +++ b/packages/effect/benchmark/SchemaUnion.ts @@ -1,7 +1,7 @@ -import type { ParseOptions } from "@effect/schema/AST" -import * as ParseResult from "@effect/schema/ParseResult" -import * as S from "@effect/schema/Schema" import * as RA from "effect/Array" +import * as ParseResult from "effect/ParseResult" +import * as S from "effect/Schema" +import type { ParseOptions } from "effect/SchemaAST" import { Bench } from "tinybench" import { z } from "zod" diff --git a/packages/schema/benchmark/tsconfig.json b/packages/effect/benchmark/tsconfig.json similarity index 69% rename from packages/schema/benchmark/tsconfig.json rename to packages/effect/benchmark/tsconfig.json index 095be3dcfe..754e674d82 100644 --- a/packages/schema/benchmark/tsconfig.json +++ b/packages/effect/benchmark/tsconfig.json @@ -5,8 +5,6 @@ "moduleResolution": "NodeNext", "target": "ES2022", "paths": { - "@effect/schema": ["../src/index.js"], - "@effect/schema/*": ["../src/*.js"], "effect/*": ["../../effect/src/*.js"] } } diff --git a/packages/schema/dtslint/Schema.ts b/packages/effect/dtslint/Schema.ts similarity index 99% rename from packages/schema/dtslint/Schema.ts rename to packages/effect/dtslint/Schema.ts index 8c8dd4b97b..cc6143a4df 100644 --- a/packages/schema/dtslint/Schema.ts +++ b/packages/effect/dtslint/Schema.ts @@ -1,8 +1,8 @@ -import type * as AST from "@effect/schema/AST" -import * as ParseResult from "@effect/schema/ParseResult" -import * as S from "@effect/schema/Schema" import { Brand, Context, Effect, Number as N, Option, String as Str } from "effect" import { hole, identity, pipe } from "effect/Function" +import * as ParseResult from "effect/ParseResult" +import * as S from "effect/Schema" +import type * as AST from "effect/SchemaAST" import type { Simplify } from "effect/Types" declare const anyNever: S.Schema @@ -1175,7 +1175,7 @@ S.TemplateLiteral(S.String, null) // $ExpectType TemplateLiteral<`${string}1`> S.TemplateLiteral(S.String, 1n) -// $ExpectType TemplateLiteral<`${string}0` | `${string}a`> +// $ExpectType TemplateLiteral<`${string}a` | `${string}0`> S.TemplateLiteral(S.String, S.Literal("a", 0)) // $ExpectType TemplateLiteral<`a${string}`> diff --git a/packages/schema/dtslint/AST.ts b/packages/effect/dtslint/SchemaAST.ts similarity index 86% rename from packages/schema/dtslint/AST.ts rename to packages/effect/dtslint/SchemaAST.ts index 2b67d9d80e..40b684d46e 100644 --- a/packages/schema/dtslint/AST.ts +++ b/packages/effect/dtslint/SchemaAST.ts @@ -1,4 +1,4 @@ -import * as AST from "@effect/schema/AST" +import * as AST from "effect/SchemaAST" // --------------------------------------------- // annotations diff --git a/packages/schema/dtslint/Brand.errors.ts b/packages/effect/dtslint/SchemaBrand.ts similarity index 90% rename from packages/schema/dtslint/Brand.errors.ts rename to packages/effect/dtslint/SchemaBrand.ts index 9630738374..40f79241eb 100644 --- a/packages/schema/dtslint/Brand.errors.ts +++ b/packages/effect/dtslint/SchemaBrand.ts @@ -1,5 +1,5 @@ -import * as S from "@effect/schema/Schema" import { pipe } from "effect/Function" +import * as S from "effect/Schema" const Int1 = Symbol.for("Int") const Int2 = Symbol.for("Int") diff --git a/packages/schema/dtslint/Class.ts b/packages/effect/dtslint/SchemaClass.ts similarity index 99% rename from packages/schema/dtslint/Class.ts rename to packages/effect/dtslint/SchemaClass.ts index f12de50cd9..6ad1e25526 100644 --- a/packages/schema/dtslint/Class.ts +++ b/packages/effect/dtslint/SchemaClass.ts @@ -1,5 +1,5 @@ -import * as S from "@effect/schema/Schema" import { hole } from "effect/Function" +import * as S from "effect/Schema" // --------------------------------------------- // check that there are no conflicts with the `fields` and `from` fields diff --git a/packages/schema/dtslint/Context.ts b/packages/effect/dtslint/SchemaContext.ts similarity index 98% rename from packages/schema/dtslint/Context.ts rename to packages/effect/dtslint/SchemaContext.ts index f55122c003..a6631f414a 100644 --- a/packages/schema/dtslint/Context.ts +++ b/packages/effect/dtslint/SchemaContext.ts @@ -1,8 +1,8 @@ -import * as ParseResult from "@effect/schema/ParseResult" -import * as S from "@effect/schema/Schema" -import * as Serializable from "@effect/schema/Serializable" import { Context, Effect, Option } from "effect" import { hole } from "effect/Function" +import * as ParseResult from "effect/ParseResult" +import * as S from "effect/Schema" +import * as Serializable from "effect/Serializable" interface aContext extends S.Schema {} interface bContext extends S.Schema {} diff --git a/packages/schema/dtslint/generic.ts b/packages/effect/dtslint/SchemaGeneric.ts similarity index 93% rename from packages/schema/dtslint/generic.ts rename to packages/effect/dtslint/SchemaGeneric.ts index 20700b3f06..d2e593c045 100644 --- a/packages/schema/dtslint/generic.ts +++ b/packages/effect/dtslint/SchemaGeneric.ts @@ -1,5 +1,4 @@ -import { Schema } from "@effect/schema" -import { Either } from "effect" +import { Either, Schema } from "effect" export const f1 = ( resultSchema: Schema.Schema diff --git a/packages/schema/dtslint/ParseResult.ts b/packages/effect/dtslint/SchemaParseResult.ts similarity index 78% rename from packages/schema/dtslint/ParseResult.ts rename to packages/effect/dtslint/SchemaParseResult.ts index 00469d019a..652655181d 100644 --- a/packages/schema/dtslint/ParseResult.ts +++ b/packages/effect/dtslint/SchemaParseResult.ts @@ -1,4 +1,4 @@ -import type * as ParseResult from "@effect/schema/ParseResult" +import type * as ParseResult from "effect/ParseResult" // --------------------------------------------- // a ParseIssue should always have an `actual` field diff --git a/packages/schema/dtslint/PropertySignature.ts b/packages/effect/dtslint/SchemaPropertySignature.ts similarity index 95% rename from packages/schema/dtslint/PropertySignature.ts rename to packages/effect/dtslint/SchemaPropertySignature.ts index 8ae00bab07..6596cae6df 100644 --- a/packages/schema/dtslint/PropertySignature.ts +++ b/packages/effect/dtslint/SchemaPropertySignature.ts @@ -1,4 +1,4 @@ -import { Schema } from "@effect/schema" +import { Schema } from "effect" // $ExpectType propertySignature const A = Schema.propertySignature(Schema.String) diff --git a/packages/schema/dtslint/Serializable.ts b/packages/effect/dtslint/SchemaSerializable.ts similarity index 98% rename from packages/schema/dtslint/Serializable.ts rename to packages/effect/dtslint/SchemaSerializable.ts index 2184b470e2..d9610e1467 100644 --- a/packages/schema/dtslint/Serializable.ts +++ b/packages/effect/dtslint/SchemaSerializable.ts @@ -1,4 +1,4 @@ -import { Schema, Serializable } from "@effect/schema" +import { Schema, Serializable } from "effect" import { hole } from "effect/Function" class TR extends Schema.TaggedRequest()("TR", { diff --git a/packages/schema/dtslint/TaggedError.ts b/packages/effect/dtslint/SchemaTaggedError.ts similarity index 79% rename from packages/schema/dtslint/TaggedError.ts rename to packages/effect/dtslint/SchemaTaggedError.ts index 33f6efb899..2232e69479 100644 --- a/packages/schema/dtslint/TaggedError.ts +++ b/packages/effect/dtslint/SchemaTaggedError.ts @@ -1,6 +1,5 @@ -import { Schema } from "@effect/schema" import type { Unify } from "effect" -import { Effect } from "effect" +import { Effect, Schema } from "effect" class Err extends Schema.TaggedError()("Err", {}) {} diff --git a/packages/schema/dtslint/TaggedRequest.ts b/packages/effect/dtslint/SchemaTaggedRequest.ts similarity index 88% rename from packages/schema/dtslint/TaggedRequest.ts rename to packages/effect/dtslint/SchemaTaggedRequest.ts index ac8829b08e..cf956f8b03 100644 --- a/packages/schema/dtslint/TaggedRequest.ts +++ b/packages/effect/dtslint/SchemaTaggedRequest.ts @@ -1,4 +1,4 @@ -import * as S from "@effect/schema/Schema" +import * as S from "effect/Schema" class TRA extends S.TaggedRequest()("TRA", { failure: S.String, diff --git a/packages/schema/dtslint/userland.ts b/packages/effect/dtslint/SchemaUserland.ts similarity index 95% rename from packages/schema/dtslint/userland.ts rename to packages/effect/dtslint/SchemaUserland.ts index 0b15e3e4f4..3c2f2d74c2 100644 --- a/packages/schema/dtslint/userland.ts +++ b/packages/effect/dtslint/SchemaUserland.ts @@ -1,4 +1,4 @@ -import { Schema as S } from "@effect/schema" +import { Schema as S } from "effect" // Discord: https://discordapp.com/channels/795981131316985866/847382157861060618/1268580485412556883 // goal: pass a Schema Class as a parameter to a function diff --git a/packages/effect/package.json b/packages/effect/package.json index 43970eede9..7931c9da74 100644 --- a/packages/effect/package.json +++ b/packages/effect/package.json @@ -44,7 +44,13 @@ "devDependencies": { "@types/jscodeshift": "^0.11.11", "@types/node": "^20.14.10", + "ajv": "^8.17.1", "ast-types": "^0.14.2", - "jscodeshift": "^0.16.1" + "jscodeshift": "^0.16.1", + "tinybench": "^2.9.0", + "zod": "^3.23.5" + }, + "dependencies": { + "fast-check": "^3.21.0" } } diff --git a/packages/effect/src/Arbitrary.ts b/packages/effect/src/Arbitrary.ts new file mode 100644 index 0000000000..7ca745982d --- /dev/null +++ b/packages/effect/src/Arbitrary.ts @@ -0,0 +1,580 @@ +/** + * @since 3.10.0 + */ + +import * as Arr from "effect/Array" +import * as Option from "effect/Option" +import * as Predicate from "effect/Predicate" +import * as FastCheck from "./FastCheck.js" +import * as errors_ from "./internal/schema/errors.js" +import * as filters_ from "./internal/schema/filters.js" +import * as util_ from "./internal/schema/util.js" +import type * as Schema from "./Schema.js" +import * as AST from "./SchemaAST.js" + +/** + * @category model + * @since 3.10.0 + */ +export interface LazyArbitrary { + (fc: typeof FastCheck): FastCheck.Arbitrary +} + +/** + * @category hooks + * @since 3.10.0 + */ +export const ArbitraryHookId: unique symbol = Symbol.for("effect/Schema/ArbitraryHookId") + +/** + * @category hooks + * @since 3.10.0 + */ +export type ArbitraryHookId = typeof ArbitraryHookId + +/** + * @category hooks + * @since 3.10.0 + */ +export interface GenerationContext { + readonly depthIdentifier?: string + readonly maxDepth: number +} + +/** + * @category hooks + * @since 3.10.0 + */ +export type ArbitraryAnnotation = ( + ...args: [...ReadonlyArray>, GenerationContext] +) => LazyArbitrary + +/** + * @category annotations + * @since 3.10.0 + */ +export const arbitrary = + (annotation: ArbitraryAnnotation) => (self: Schema.Schema): Schema.Schema => + self.annotations({ [ArbitraryHookId]: annotation }) + +/** + * Returns a LazyArbitrary for the `A` type of the provided schema. + * + * @category arbitrary + * @since 3.10.0 + */ +export const makeLazy = (schema: Schema.Schema): LazyArbitrary => + go(schema.ast, { maxDepth: 2 }, []) + +/** + * Returns a fast-check Arbitrary for the `A` type of the provided schema. + * + * @category arbitrary + * @since 3.10.0 + */ +export const make = (schema: Schema.Schema): FastCheck.Arbitrary => makeLazy(schema)(FastCheck) + +const getHook = AST.getAnnotation>(ArbitraryHookId) + +const getRefinementFromArbitrary = ( + ast: AST.Refinement, + ctx: Context, + path: ReadonlyArray +) => { + const constraints = combineConstraints(ctx.constraints, getConstraints(ast)) + return go(ast.from, constraints ? { ...ctx, constraints } : ctx, path) +} + +const getSuspendedContext = ( + ctx: Context, + ast: AST.Suspend +): Context => { + if (ctx.depthIdentifier !== undefined) { + return ctx + } + const depthIdentifier = AST.getIdentifierAnnotation(ast).pipe( + Option.orElse(() => AST.getIdentifierAnnotation(ast.f())), + Option.getOrElse(() => "SuspendDefaultDepthIdentifier") + ) + return { ...ctx, depthIdentifier } +} + +const getSuspendedArray = ( + fc: typeof FastCheck, + depthIdentifier: string, + maxDepth: number, + item: FastCheck.Arbitrary, + constraints?: FastCheck.ArrayConstraints +) => { + let minLength = 1 + let maxLength = 2 + if (constraints && constraints.minLength !== undefined && constraints.minLength > minLength) { + minLength = constraints.minLength + if (minLength > maxLength) { + maxLength = minLength + } + } + return fc.oneof( + { maxDepth, depthIdentifier }, + fc.constant([]), + fc.array(item, { minLength, maxLength }) + ) +} + +interface Context extends GenerationContext { + readonly constraints?: Constraints +} + +const go = ( + ast: AST.AST, + ctx: Context, + path: ReadonlyArray +): LazyArbitrary => { + const hook = getHook(ast) + if (Option.isSome(hook)) { + switch (ast._tag) { + case "Declaration": + return hook.value(...ast.typeParameters.map((p) => go(p, ctx, path)), ctx) + case "Refinement": + return hook.value(getRefinementFromArbitrary(ast, ctx, path), ctx) + default: + return hook.value(ctx) + } + } + switch (ast._tag) { + case "Declaration": { + throw new Error(errors_.getArbitraryMissingAnnotationErrorMessage(path, ast)) + } + case "Literal": + return (fc) => fc.constant(ast.literal) + case "UniqueSymbol": + return (fc) => fc.constant(ast.symbol) + case "UndefinedKeyword": + return (fc) => fc.constant(undefined) + case "NeverKeyword": + return () => { + throw new Error(errors_.getArbitraryUnsupportedErrorMessage(path, ast)) + } + case "UnknownKeyword": + case "AnyKeyword": + case "VoidKeyword": + return (fc) => fc.anything() + case "StringKeyword": + return (fc) => { + if (ctx.constraints) { + switch (ctx.constraints._tag) { + case "StringConstraints": + return fc.string(ctx.constraints.constraints) + } + } + return fc.string() + } + case "NumberKeyword": + return (fc) => { + if (ctx.constraints) { + switch (ctx.constraints._tag) { + case "NumberConstraints": + return fc.float(ctx.constraints.constraints) + case "IntegerConstraints": + return fc.integer(ctx.constraints.constraints) + } + } + return fc.float() + } + case "BooleanKeyword": + return (fc) => fc.boolean() + case "BigIntKeyword": + return (fc) => { + if (ctx.constraints) { + switch (ctx.constraints._tag) { + case "BigIntConstraints": + return fc.bigInt(ctx.constraints.constraints) + } + } + return fc.bigInt() + } + case "SymbolKeyword": + return (fc) => fc.string().map((s) => Symbol.for(s)) + case "ObjectKeyword": + return (fc) => fc.oneof(fc.object(), fc.array(fc.anything())) + case "TemplateLiteral": { + return (fc) => { + const string = fc.string({ maxLength: 5 }) + const number = fc.float({ noDefaultInfinity: true }).filter((n) => !Number.isNaN(n)) + const components: Array> = [fc.constant(ast.head)] + for (const span of ast.spans) { + if (AST.isStringKeyword(span.type)) { + components.push(string) + } else { + components.push(number) + } + components.push(fc.constant(span.literal)) + } + return fc.tuple(...components).map((spans) => spans.join("")) + } + } + case "TupleType": { + const elements: Array> = [] + let hasOptionals = false + let i = 0 + for (const element of ast.elements) { + elements.push(go(element.type, ctx, path.concat(i++))) + if (element.isOptional) { + hasOptionals = true + } + } + const rest = ast.rest.map((annotatedAST) => go(annotatedAST.type, ctx, path)) + return (fc) => { + // --------------------------------------------- + // handle elements + // --------------------------------------------- + let output = fc.tuple(...elements.map((arb) => arb(fc))) + if (hasOptionals) { + const indexes = fc.tuple( + ...ast.elements.map((element) => element.isOptional ? fc.boolean() : fc.constant(true)) + ) + output = output.chain((tuple) => + indexes.map((booleans) => { + for (const [i, b] of booleans.reverse().entries()) { + if (!b) { + tuple.splice(booleans.length - i, 1) + } + } + return tuple + }) + ) + } + + // --------------------------------------------- + // handle rest element + // --------------------------------------------- + if (Arr.isNonEmptyReadonlyArray(rest)) { + const [head, ...tail] = rest + const item = head(fc) + const constraints: FastCheck.ArrayConstraints | undefined = + ctx.constraints && ctx.constraints._tag === "ArrayConstraints" + ? ctx.constraints.constraints + : undefined + output = output.chain((as) => { + return (ctx.depthIdentifier !== undefined + ? getSuspendedArray(fc, ctx.depthIdentifier, ctx.maxDepth, item, constraints) + : fc.array(item, constraints)).map((rest) => [...as, ...rest]) + }) + // --------------------------------------------- + // handle post rest elements + // --------------------------------------------- + for (let j = 0; j < tail.length; j++) { + output = output.chain((as) => tail[j](fc).map((a) => [...as, a])) + } + } + + return output + } + } + case "TypeLiteral": { + const propertySignaturesTypes = ast.propertySignatures.map((ps) => go(ps.type, ctx, path.concat(ps.name))) + const indexSignatures = ast.indexSignatures.map((is) => + [go(is.parameter, ctx, path), go(is.type, ctx, path)] as const + ) + return (fc) => { + const arbs: any = {} + const requiredKeys: Array = [] + // --------------------------------------------- + // handle property signatures + // --------------------------------------------- + for (let i = 0; i < propertySignaturesTypes.length; i++) { + const ps = ast.propertySignatures[i] + const name = ps.name + if (!ps.isOptional) { + requiredKeys.push(name) + } + arbs[name] = propertySignaturesTypes[i](fc) + } + let output = fc.record(arbs, { requiredKeys }) + // --------------------------------------------- + // handle index signatures + // --------------------------------------------- + for (let i = 0; i < indexSignatures.length; i++) { + const key = indexSignatures[i][0](fc) + const value = indexSignatures[i][1](fc) + output = output.chain((o) => { + const item = fc.tuple(key, value) + const arr = ctx.depthIdentifier !== undefined ? + getSuspendedArray(fc, ctx.depthIdentifier, ctx.maxDepth, item) : + fc.array(item) + return arr.map((tuples) => ({ ...Object.fromEntries(tuples), ...o })) + }) + } + + return output + } + } + case "Union": { + const types = ast.types.map((member) => go(member, ctx, path)) + return (fc) => fc.oneof(...types.map((arb) => arb(fc))) + } + case "Enums": { + if (ast.enums.length === 0) { + throw new Error(errors_.getArbitraryEmptyEnumErrorMessage(path)) + } + return (fc) => fc.oneof(...ast.enums.map(([_, value]) => fc.constant(value))) + } + case "Refinement": { + const from = getRefinementFromArbitrary(ast, ctx, path) + return (fc) => from(fc).filter((a) => Option.isNone(ast.filter(a, AST.defaultParseOption, ast))) + } + case "Suspend": { + const get = util_.memoizeThunk(() => { + return go(ast.f(), getSuspendedContext(ctx, ast), path) + }) + return (fc) => fc.constant(null).chain(() => get()(fc)) + } + case "Transformation": + return go(ast.to, ctx, path) + } +} + +/** @internal */ +export class NumberConstraints { + readonly _tag = "NumberConstraints" + readonly constraints: FastCheck.FloatConstraints + constructor(options: { + readonly min?: number | undefined + readonly max?: number | undefined + readonly noNaN?: boolean | undefined + readonly noDefaultInfinity?: boolean | undefined + }) { + this.constraints = {} + if (Predicate.isNumber(options.min)) { + this.constraints.min = Math.fround(options.min) + } + if (Predicate.isNumber(options.max)) { + this.constraints.max = Math.fround(options.max) + } + if (Predicate.isBoolean(options.noNaN)) { + this.constraints.noNaN = options.noNaN + } + if (Predicate.isBoolean(options.noDefaultInfinity)) { + this.constraints.noDefaultInfinity = options.noDefaultInfinity + } + } +} + +/** @internal */ +export class StringConstraints { + readonly _tag = "StringConstraints" + readonly constraints: FastCheck.StringSharedConstraints + constructor(options: { + readonly minLength?: number | undefined + readonly maxLength?: number | undefined + }) { + this.constraints = {} + if (Predicate.isNumber(options.minLength)) { + this.constraints.minLength = options.minLength + } + if (Predicate.isNumber(options.maxLength)) { + this.constraints.maxLength = options.maxLength + } + } +} + +/** @internal */ +export class IntegerConstraints { + readonly _tag = "IntegerConstraints" + readonly constraints: FastCheck.IntegerConstraints + constructor(options: { + readonly min?: number | undefined + readonly max?: number | undefined + }) { + this.constraints = {} + if (Predicate.isNumber(options.min)) { + this.constraints.min = options.min + } + if (Predicate.isNumber(options.max)) { + this.constraints.max = options.max + } + } +} + +/** @internal */ +export class ArrayConstraints { + readonly _tag = "ArrayConstraints" + readonly constraints: FastCheck.ArrayConstraints + constructor(options: { + readonly minLength?: number | undefined + readonly maxLength?: number | undefined + }) { + this.constraints = {} + if (Predicate.isNumber(options.minLength)) { + this.constraints.minLength = options.minLength + } + if (Predicate.isNumber(options.maxLength)) { + this.constraints.maxLength = options.maxLength + } + } +} + +/** @internal */ +export class BigIntConstraints { + readonly _tag = "BigIntConstraints" + readonly constraints: FastCheck.BigIntConstraints + constructor(options: { + readonly min?: bigint | undefined + readonly max?: bigint | undefined + }) { + this.constraints = {} + if (Predicate.isBigInt(options.min)) { + this.constraints.min = options.min + } + if (Predicate.isBigInt(options.max)) { + this.constraints.max = options.max + } + } +} + +/** @internal */ +export type Constraints = + | NumberConstraints + | StringConstraints + | IntegerConstraints + | ArrayConstraints + | BigIntConstraints + +/** @internal */ +export const getConstraints = (ast: AST.Refinement): Constraints | undefined => { + const TypeAnnotationId = ast.annotations[AST.TypeAnnotationId] + const jsonSchema: any = ast.annotations[AST.JSONSchemaAnnotationId] + switch (TypeAnnotationId) { + // int + case filters_.IntTypeId: + return new IntegerConstraints({}) + // number + case filters_.GreaterThanTypeId: + case filters_.GreaterThanOrEqualToTypeId: + case filters_.LessThanTypeId: + case filters_.LessThanOrEqualToTypeId: + case filters_.BetweenTypeId: + return new NumberConstraints({ + min: jsonSchema.exclusiveMinimum ?? jsonSchema.minimum, + max: jsonSchema.exclusiveMaximum ?? jsonSchema.maximum + }) + // bigint + case filters_.GreaterThanBigintTypeId: + case filters_.GreaterThanOrEqualToBigIntTypeId: + case filters_.LessThanBigIntTypeId: + case filters_.LessThanOrEqualToBigIntTypeId: + case filters_.BetweenBigintTypeId: { + const constraints: any = ast.annotations[TypeAnnotationId] + return new BigIntConstraints(constraints) + } + // string + case filters_.MinLengthTypeId: + case filters_.MaxLengthTypeId: + case filters_.LengthTypeId: + return new StringConstraints(jsonSchema) + // array + case filters_.MinItemsTypeId: + case filters_.MaxItemsTypeId: + case filters_.ItemsCountTypeId: + return new ArrayConstraints({ + minLength: jsonSchema.minItems, + maxLength: jsonSchema.maxItems + }) + } +} + +/** @internal */ +export const combineConstraints = ( + c1: Constraints | undefined, + c2: Constraints | undefined +): Constraints | undefined => { + if (c1 === undefined) { + return c2 + } + if (c2 === undefined) { + return c1 + } + switch (c1._tag) { + case "ArrayConstraints": { + switch (c2._tag) { + case "ArrayConstraints": + return new ArrayConstraints({ + minLength: getMax(c1.constraints.minLength, c2.constraints.minLength), + maxLength: getMin(c1.constraints.maxLength, c2.constraints.maxLength) + }) + } + break + } + case "NumberConstraints": { + switch (c2._tag) { + case "NumberConstraints": + return new NumberConstraints({ + min: getMax(c1.constraints.min, c2.constraints.min), + max: getMin(c1.constraints.max, c2.constraints.max), + noNaN: getOr(c1.constraints.noNaN, c2.constraints.noNaN), + noDefaultInfinity: getOr(c1.constraints.noDefaultInfinity, c2.constraints.noDefaultInfinity) + }) + case "IntegerConstraints": + return new IntegerConstraints({ + min: getMax(c1.constraints.min, c2.constraints.min), + max: getMin(c1.constraints.max, c2.constraints.max) + }) + } + break + } + case "BigIntConstraints": { + switch (c2._tag) { + case "BigIntConstraints": + return new BigIntConstraints({ + min: getMax(c1.constraints.min, c2.constraints.min), + max: getMin(c1.constraints.max, c2.constraints.max) + }) + } + break + } + case "StringConstraints": { + switch (c2._tag) { + case "StringConstraints": + return new StringConstraints({ + minLength: getMax(c1.constraints.minLength, c2.constraints.minLength), + maxLength: getMin(c1.constraints.maxLength, c2.constraints.maxLength) + }) + } + break + } + case "IntegerConstraints": { + switch (c2._tag) { + case "NumberConstraints": + case "IntegerConstraints": { + return new IntegerConstraints({ + min: getMax(c1.constraints.min, c2.constraints.min), + max: getMin(c1.constraints.max, c2.constraints.max) + }) + } + } + break + } + } +} + +const getOr = (a: boolean | undefined, b: boolean | undefined): boolean | undefined => { + return a === undefined ? b : b === undefined ? a : a || b +} + +function getMax(n1: bigint | undefined, n2: bigint | undefined): bigint | undefined +function getMax(n1: number | undefined, n2: number | undefined): number | undefined +function getMax( + n1: bigint | number | undefined, + n2: bigint | number | undefined +): bigint | number | undefined { + return n1 === undefined ? n2 : n2 === undefined ? n1 : n1 <= n2 ? n2 : n1 +} + +function getMin(n1: bigint | undefined, n2: bigint | undefined): bigint | undefined +function getMin(n1: number | undefined, n2: number | undefined): number | undefined +function getMin( + n1: bigint | number | undefined, + n2: bigint | number | undefined +): bigint | number | undefined { + return n1 === undefined ? n2 : n2 === undefined ? n1 : n1 <= n2 ? n1 : n2 +} diff --git a/packages/effect/src/FastCheck.ts b/packages/effect/src/FastCheck.ts new file mode 100644 index 0000000000..b5a3803d15 --- /dev/null +++ b/packages/effect/src/FastCheck.ts @@ -0,0 +1,9 @@ +/** + * @since 3.10.0 + */ + +/** + * @category re-exports + * @since 3.10.0 + */ +export * from "fast-check" diff --git a/packages/effect/src/JSONSchema.ts b/packages/effect/src/JSONSchema.ts new file mode 100644 index 0000000000..21c25d2052 --- /dev/null +++ b/packages/effect/src/JSONSchema.ts @@ -0,0 +1,602 @@ +/** + * @since 3.10.0 + */ + +import * as Option from "effect/Option" +import * as Predicate from "effect/Predicate" +import * as Record from "effect/Record" +import * as errors_ from "./internal/schema/errors.js" +import * as filters_ from "./internal/schema/filters.js" +import type * as Schema from "./Schema.js" +import * as AST from "./SchemaAST.js" + +/** + * @category model + * @since 3.10.0 + */ +export interface JsonSchemaAnnotations { + title?: string + description?: string + default?: unknown + examples?: Array +} + +/** + * @category model + * @since 3.10.0 + */ +export interface JsonSchema7Any extends JsonSchemaAnnotations { + $id: "/schemas/any" +} + +/** + * @category model + * @since 3.10.0 + */ +export interface JsonSchema7Unknown extends JsonSchemaAnnotations { + $id: "/schemas/unknown" +} + +/** + * @category model + * @since 3.10.0 + */ +export interface JsonSchema7Void extends JsonSchemaAnnotations { + $id: "/schemas/void" +} + +/** + * @category model + * @since 3.10.0 + */ +export interface JsonSchema7object extends JsonSchemaAnnotations { + $id: "/schemas/object" + anyOf: [ + { type: "object" }, + { type: "array" } + ] +} + +/** + * @category model + * @since 3.10.0 + */ +export interface JsonSchema7empty extends JsonSchemaAnnotations { + $id: "/schemas/{}" + anyOf: [ + { type: "object" }, + { type: "array" } + ] +} + +/** + * @category model + * @since 3.10.0 + */ +export interface JsonSchema7Ref extends JsonSchemaAnnotations { + $ref: string +} + +/** + * @category model + * @since 3.10.0 + */ +export interface JsonSchema7String extends JsonSchemaAnnotations { + type: "string" + minLength?: number + maxLength?: number + pattern?: string +} + +/** + * @category model + * @since 3.10.0 + */ +export interface JsonSchema7Numeric extends JsonSchemaAnnotations { + minimum?: number + exclusiveMinimum?: number + maximum?: number + exclusiveMaximum?: number +} + +/** + * @category model + * @since 3.10.0 + */ +export interface JsonSchema7Number extends JsonSchema7Numeric { + type: "number" +} + +/** + * @category model + * @since 3.10.0 + */ +export interface JsonSchema7Integer extends JsonSchema7Numeric { + type: "integer" +} + +/** + * @category model + * @since 3.10.0 + */ +export interface JsonSchema7Boolean extends JsonSchemaAnnotations { + type: "boolean" +} + +/** + * @category model + * @since 3.10.0 + */ +export interface JsonSchema7Array extends JsonSchemaAnnotations { + type: "array" + items?: JsonSchema7 | Array + minItems?: number + maxItems?: number + additionalItems?: JsonSchema7 | boolean +} + +/** + * @category model + * @since 3.10.0 + */ +export interface JsonSchema7Enum extends JsonSchemaAnnotations { + enum: Array +} + +/** + * @category model + * @since 3.10.0 + */ +export interface JsonSchema7Enums extends JsonSchemaAnnotations { + $comment: "/schemas/enums" + anyOf: Array<{ + title: string + enum: [string | number] + }> +} + +/** + * @category model + * @since 3.10.0 + */ +export interface JsonSchema7AnyOf extends JsonSchemaAnnotations { + anyOf: Array +} + +/** + * @category model + * @since 3.10.0 + */ +export interface JsonSchema7Object extends JsonSchemaAnnotations { + type: "object" + required: Array + properties: Record + additionalProperties?: boolean | JsonSchema7 + patternProperties?: Record + propertyNames?: JsonSchema7 +} + +/** + * @category model + * @since 3.10.0 + */ +export type JsonSchema7 = + | JsonSchema7Any + | JsonSchema7Unknown + | JsonSchema7Void + | JsonSchema7object + | JsonSchema7empty + | JsonSchema7Ref + | JsonSchema7String + | JsonSchema7Number + | JsonSchema7Integer + | JsonSchema7Boolean + | JsonSchema7Array + | JsonSchema7Enum + | JsonSchema7Enums + | JsonSchema7AnyOf + | JsonSchema7Object + +/** + * @category model + * @since 3.10.0 + */ +export type JsonSchema7Root = JsonSchema7 & { + $schema?: string + $defs?: Record +} + +/** + * @category encoding + * @since 3.10.0 + */ +export const make = (schema: Schema.Schema): JsonSchema7Root => { + const $defs: Record = {} + const jsonSchema = go(schema.ast, $defs, true, []) + const out: JsonSchema7Root = { + $schema, + ...jsonSchema + } + // clean up self-referencing entries + for (const id in $defs) { + if ($defs[id]["$ref"] === get$ref(id)) { + delete $defs[id] + } + } + if (!Record.isEmptyRecord($defs)) { + out.$defs = $defs + } + return out +} + +const anyJsonSchema: JsonSchema7 = { $id: "/schemas/any" } + +const unknownJsonSchema: JsonSchema7 = { $id: "/schemas/unknown" } + +const voidJsonSchema: JsonSchema7 = { $id: "/schemas/void" } + +const objectJsonSchema: JsonSchema7 = { + "$id": "/schemas/object", + "anyOf": [ + { "type": "object" }, + { "type": "array" } + ] +} + +const empty = (): JsonSchema7 => ({ + "$id": "/schemas/{}", + "anyOf": [ + { "type": "object" }, + { "type": "array" } + ] +}) + +const $schema = "http://json-schema.org/draft-07/schema#" + +const getJsonSchemaAnnotations = (annotated: AST.Annotated): JsonSchemaAnnotations => + Record.getSomes({ + description: AST.getDescriptionAnnotation(annotated), + title: AST.getTitleAnnotation(annotated), + examples: AST.getExamplesAnnotation(annotated), + default: AST.getDefaultAnnotation(annotated) + }) + +const removeDefaultJsonSchemaAnnotations = ( + jsonSchemaAnnotations: JsonSchemaAnnotations, + ast: AST.AST +): JsonSchemaAnnotations => { + if (jsonSchemaAnnotations["title"] === ast.annotations[AST.TitleAnnotationId]) { + delete jsonSchemaAnnotations["title"] + } + if (jsonSchemaAnnotations["description"] === ast.annotations[AST.DescriptionAnnotationId]) { + delete jsonSchemaAnnotations["description"] + } + return jsonSchemaAnnotations +} + +const getASTJsonSchemaAnnotations = (ast: AST.AST): JsonSchemaAnnotations => { + const jsonSchemaAnnotations = getJsonSchemaAnnotations(ast) + switch (ast._tag) { + case "StringKeyword": + return removeDefaultJsonSchemaAnnotations(jsonSchemaAnnotations, AST.stringKeyword) + case "NumberKeyword": + return removeDefaultJsonSchemaAnnotations(jsonSchemaAnnotations, AST.numberKeyword) + case "BooleanKeyword": + return removeDefaultJsonSchemaAnnotations(jsonSchemaAnnotations, AST.booleanKeyword) + default: + return jsonSchemaAnnotations + } +} + +const pruneUndefinedKeyword = (ps: AST.PropertySignature): AST.AST | undefined => { + const type = ps.type + if (AST.isUnion(type) && Option.isNone(AST.getJSONSchemaAnnotation(type))) { + const types = type.types.filter((type) => !AST.isUndefinedKeyword(type)) + if (types.length < type.types.length) { + return AST.Union.make(types, type.annotations) + } + } +} + +/** @internal */ +export const DEFINITION_PREFIX = "#/$defs/" + +const get$ref = (id: string): string => `${DEFINITION_PREFIX}${id}` + +const getRefinementInnerTransformation = (ast: AST.Refinement): AST.AST | undefined => { + switch (ast.from._tag) { + case "Transformation": + return ast.from + case "Refinement": + return getRefinementInnerTransformation(ast.from) + case "Suspend": { + const from = ast.from.f() + if (AST.isRefinement(from)) { + return getRefinementInnerTransformation(from) + } + } + } +} + +const isParseJsonTransformation = (ast: AST.AST): boolean => + ast.annotations[AST.TypeAnnotationId] === filters_.ParseJsonTypeId + +function merge(a: JsonSchemaAnnotations, b: JsonSchema7): JsonSchema7 +function merge(a: JsonSchema7, b: JsonSchemaAnnotations): JsonSchema7 +function merge(a: JsonSchema7, b: JsonSchema7): JsonSchema7 +function merge(a: object, b: object): object { + return { ...a, ...b } +} + +const isOverrideAnnotation = (jsonSchema: JsonSchema7): boolean => { + return ("type" in jsonSchema) || ("oneOf" in jsonSchema) || ("anyOf" in jsonSchema) || ("const" in jsonSchema) || + ("enum" in jsonSchema) || ("$ref" in jsonSchema) +} + +const go = ( + ast: AST.AST, + $defs: Record, + handleIdentifier: boolean, + path: ReadonlyArray +): JsonSchema7 => { + const hook = AST.getJSONSchemaAnnotation(ast) + if (Option.isSome(hook)) { + const handler = hook.value as JsonSchema7 + if (AST.isRefinement(ast)) { + const t = getRefinementInnerTransformation(ast) + if (t === undefined) { + try { + return { + ...go(ast.from, $defs, true, path), + ...getJsonSchemaAnnotations(ast), + ...handler + } + } catch (e) { + return { + ...getJsonSchemaAnnotations(ast), + ...handler + } + } + } else if (!isOverrideAnnotation(handler)) { + return go(t, $defs, true, path) + } + } + return handler + } + const surrogate = AST.getSurrogateAnnotation(ast) + if (Option.isSome(surrogate)) { + return go(surrogate.value, $defs, handleIdentifier, path) + } + if (handleIdentifier && !AST.isTransformation(ast) && !AST.isRefinement(ast)) { + const identifier = AST.getJSONIdentifier(ast) + if (Option.isSome(identifier)) { + const id = identifier.value + const out = { $ref: get$ref(id) } + if (!Record.has($defs, id)) { + $defs[id] = out + $defs[id] = go(ast, $defs, false, path) + } + return out + } + } + switch (ast._tag) { + case "Declaration": + throw new Error(errors_.getJSONSchemaMissingAnnotationErrorMessage(path, ast)) + case "Literal": { + const literal = ast.literal + if (literal === null) { + return merge({ enum: [null] }, getJsonSchemaAnnotations(ast)) + } else if (Predicate.isString(literal) || Predicate.isNumber(literal) || Predicate.isBoolean(literal)) { + return merge({ enum: [literal] }, getJsonSchemaAnnotations(ast)) + } + throw new Error(errors_.getJSONSchemaMissingAnnotationErrorMessage(path, ast)) + } + case "UniqueSymbol": + throw new Error(errors_.getJSONSchemaMissingAnnotationErrorMessage(path, ast)) + case "UndefinedKeyword": + throw new Error(errors_.getJSONSchemaMissingAnnotationErrorMessage(path, ast)) + case "VoidKeyword": + return merge(voidJsonSchema, getJsonSchemaAnnotations(ast)) + case "NeverKeyword": + throw new Error(errors_.getJSONSchemaMissingAnnotationErrorMessage(path, ast)) + case "UnknownKeyword": + return merge(unknownJsonSchema, getJsonSchemaAnnotations(ast)) + case "AnyKeyword": + return merge(anyJsonSchema, getJsonSchemaAnnotations(ast)) + case "ObjectKeyword": + return merge(objectJsonSchema, getJsonSchemaAnnotations(ast)) + case "StringKeyword": + return { type: "string", ...getASTJsonSchemaAnnotations(ast) } + case "NumberKeyword": + return { type: "number", ...getASTJsonSchemaAnnotations(ast) } + case "BooleanKeyword": + return { type: "boolean", ...getASTJsonSchemaAnnotations(ast) } + case "BigIntKeyword": + throw new Error(errors_.getJSONSchemaMissingAnnotationErrorMessage(path, ast)) + case "SymbolKeyword": + throw new Error(errors_.getJSONSchemaMissingAnnotationErrorMessage(path, ast)) + case "TupleType": { + const elements = ast.elements.map((e, i) => + merge( + go(e.type, $defs, true, path.concat(i)), + getJsonSchemaAnnotations(e) + ) + ) + const rest = ast.rest.map((annotatedAST) => + merge( + go(annotatedAST.type, $defs, true, path), + getJsonSchemaAnnotations(annotatedAST) + ) + ) + const output: JsonSchema7Array = { type: "array" } + // --------------------------------------------- + // handle elements + // --------------------------------------------- + const len = ast.elements.length + if (len > 0) { + output.minItems = len - ast.elements.filter((element) => element.isOptional).length + output.items = elements + } + // --------------------------------------------- + // handle rest element + // --------------------------------------------- + const restLength = rest.length + if (restLength > 0) { + const head = rest[0] + const isHomogeneous = restLength === 1 && ast.elements.every((e) => e.type === ast.rest[0].type) + if (isHomogeneous) { + output.items = head + } else { + output.additionalItems = head + } + + // --------------------------------------------- + // handle post rest elements + // --------------------------------------------- + if (restLength > 1) { + throw new Error(errors_.getJSONSchemaUnsupportedPostRestElementsErrorMessage(path)) + } + } else { + if (len > 0) { + output.additionalItems = false + } else { + output.maxItems = 0 + } + } + + return merge(output, getJsonSchemaAnnotations(ast)) + } + case "TypeLiteral": { + if (ast.propertySignatures.length === 0 && ast.indexSignatures.length === 0) { + return merge(empty(), getJsonSchemaAnnotations(ast)) + } + let patternProperties: JsonSchema7 | undefined = undefined + let propertyNames: JsonSchema7 | undefined = undefined + for (const is of ast.indexSignatures) { + const parameter = is.parameter + switch (parameter._tag) { + case "StringKeyword": { + patternProperties = go(is.type, $defs, true, path) + break + } + case "TemplateLiteral": { + patternProperties = go(is.type, $defs, true, path) + propertyNames = { + type: "string", + pattern: AST.getTemplateLiteralRegExp(parameter).source + } + break + } + case "Refinement": { + patternProperties = go(is.type, $defs, true, path) + propertyNames = go(parameter, $defs, true, path) + break + } + case "SymbolKeyword": + throw new Error(errors_.getJSONSchemaUnsupportedParameterErrorMessage(path, parameter)) + } + } + const output: JsonSchema7Object = { + type: "object", + required: [], + properties: {}, + additionalProperties: false + } + // --------------------------------------------- + // handle property signatures + // --------------------------------------------- + for (let i = 0; i < ast.propertySignatures.length; i++) { + const ps = ast.propertySignatures[i] + const name = ps.name + if (Predicate.isString(name)) { + const pruned = pruneUndefinedKeyword(ps) + output.properties[name] = merge( + go(pruned ? pruned : ps.type, $defs, true, path.concat(ps.name)), + getJsonSchemaAnnotations(ps) + ) + // --------------------------------------------- + // handle optional property signatures + // --------------------------------------------- + if (!ps.isOptional && pruned === undefined) { + output.required.push(name) + } + } else { + throw new Error(errors_.getJSONSchemaUnsupportedKeyErrorMessage(name, path)) + } + } + // --------------------------------------------- + // handle index signatures + // --------------------------------------------- + if (patternProperties !== undefined) { + delete output.additionalProperties + output.patternProperties = { "": patternProperties } + } + if (propertyNames !== undefined) { + output.propertyNames = propertyNames + } + + return merge(output, getJsonSchemaAnnotations(ast)) + } + case "Union": { + const enums: Array = [] + const anyOf: Array = [] + for (const type of ast.types) { + const schema = go(type, $defs, true, path) + if ("enum" in schema) { + if (Object.keys(schema).length > 1) { + anyOf.push(schema) + } else { + for (const e of schema.enum) { + enums.push(e) + } + } + } else { + anyOf.push(schema) + } + } + if (anyOf.length === 0) { + return merge({ enum: enums }, getJsonSchemaAnnotations(ast)) + } else { + if (enums.length >= 1) { + anyOf.push({ enum: enums }) + } + return merge({ anyOf }, getJsonSchemaAnnotations(ast)) + } + } + case "Enums": { + return merge({ + $comment: "/schemas/enums", + anyOf: ast.enums.map((e) => ({ title: e[0], enum: [e[1]] })) + }, getJsonSchemaAnnotations(ast)) + } + case "Refinement": { + if (AST.encodedBoundAST(ast) === ast) { + throw new Error(errors_.getJSONSchemaMissingAnnotationErrorMessage(path, ast)) + } + return go(ast.from, $defs, true, path) + } + case "TemplateLiteral": { + const regex = AST.getTemplateLiteralRegExp(ast) + return merge({ + type: "string", + description: "a template literal", + pattern: regex.source + }, getJsonSchemaAnnotations(ast)) + } + case "Suspend": { + const identifier = Option.orElse(AST.getJSONIdentifier(ast), () => AST.getJSONIdentifier(ast.f())) + if (Option.isNone(identifier)) { + throw new Error(errors_.getJSONSchemaMissingIdentifierAnnotationErrorMessage(path, ast)) + } + return go(ast.f(), $defs, true, path) + } + case "Transformation": { + // Properly handle S.parseJson transformations by focusing on + // the 'to' side of the AST. This approach prevents the generation of useless schemas + // derived from the 'from' side (type: string), ensuring the output matches the intended + // complex schema type. + const next = isParseJsonTransformation(ast.from) ? ast.to : ast.from + return go(next, $defs, true, path) + } + } +} diff --git a/packages/effect/src/ParseResult.ts b/packages/effect/src/ParseResult.ts new file mode 100644 index 0000000000..cb1326a03f --- /dev/null +++ b/packages/effect/src/ParseResult.ts @@ -0,0 +1,1720 @@ +/** + * @since 3.10.0 + */ + +import * as array_ from "effect/Array" +import { TaggedError } from "effect/Data" +import * as Effect from "effect/Effect" +import * as Either from "effect/Either" +import type { LazyArg } from "effect/Function" +import { dual } from "effect/Function" +import { globalValue } from "effect/GlobalValue" +import * as Inspectable from "effect/Inspectable" +import * as Option from "effect/Option" +import * as Predicate from "effect/Predicate" +import type { Concurrency } from "effect/Types" +import * as util_ from "./internal/schema/util.js" +import type * as Schema from "./Schema.js" +import * as AST from "./SchemaAST.js" +import * as TreeFormatter from "./SchemaTreeFormatter.js" + +/** + * `ParseIssue` is a type that represents the different types of errors that can occur when decoding/encoding a value. + * + * @category model + * @since 3.10.0 + */ +export type ParseIssue = + // leaf + | Type + | Missing + | Unexpected + | Forbidden + // composite + | Pointer + | Refinement + | Transformation + | Composite + +/** + * @category model + * @since 3.10.0 + */ +export type SingleOrNonEmpty = A | array_.NonEmptyReadonlyArray + +/** + * @category model + * @since 3.10.0 + */ +export type Path = SingleOrNonEmpty + +/** + * @category model + * @since 3.10.0 + */ +export class Pointer { + /** + * @since 3.10.0 + */ + readonly _tag = "Pointer" + constructor( + readonly path: Path, + readonly actual: unknown, + readonly issue: ParseIssue + ) {} +} + +/** + * Error that occurs when an unexpected key or index is present. + * + * @category model + * @since 3.10.0 + */ +export class Unexpected { + /** + * @since 3.10.0 + */ + readonly _tag = "Unexpected" + constructor( + readonly actual: unknown, + /** + * @since 3.10.0 + */ + readonly message?: string + ) {} +} + +/** + * Error that occurs when a required key or index is missing. + * + * @category model + * @since 3.10.0 + */ +export class Missing { + /** + * @since 3.10.0 + */ + readonly _tag = "Missing" + /** + * @since 3.10.0 + */ + readonly actual = undefined + constructor( + /** + * @since 3.10.0 + */ + readonly ast: AST.Type, + /** + * @since 3.10.0 + */ + readonly message?: string + ) {} +} + +/** + * Error that contains multiple issues. + * + * @category model + * @since 3.10.0 + */ +export class Composite { + /** + * @since 3.10.0 + */ + readonly _tag = "Composite" + constructor( + readonly ast: AST.AST, + readonly actual: unknown, + readonly issues: SingleOrNonEmpty, + readonly output?: unknown + ) {} +} + +/** + * Returns `true` if the value is a `Composite`. + * + * @category guards + * @since 3.10.0 + */ +export const isComposite = (u: unknown): u is Composite => Predicate.hasProperty(u, "_tag") + +/** + * Error that occurs when a refinement has an error. + * + * @category model + * @since 3.10.0 + */ +export class Refinement { + /** + * @since 3.10.0 + */ + readonly _tag = "Refinement" + constructor( + readonly ast: AST.Refinement, + readonly actual: unknown, + readonly kind: "From" | "Predicate", + readonly issue: ParseIssue + ) {} +} + +/** + * Error that occurs when a transformation has an error. + * + * @category model + * @since 3.10.0 + */ +export class Transformation { + /** + * @since 3.10.0 + */ + readonly _tag = "Transformation" + constructor( + readonly ast: AST.Transformation, + readonly actual: unknown, + readonly kind: "Encoded" | "Transformation" | "Type", + readonly issue: ParseIssue + ) {} +} + +/** + * The `Type` variant of the `ParseIssue` type represents an error that occurs when the `actual` value is not of the expected type. + * The `ast` field specifies the expected type, and the `actual` field contains the value that caused the error. + * + * @category model + * @since 3.10.0 + */ +export class Type { + /** + * @since 3.10.0 + */ + readonly _tag = "Type" + constructor( + readonly ast: AST.AST, + readonly actual: unknown, + readonly message?: string + ) {} +} + +/** + * The `Forbidden` variant of the `ParseIssue` type represents a forbidden operation, such as when encountering an Effect that is not allowed to execute (e.g., using `runSync`). + * + * @category model + * @since 3.10.0 + */ +export class Forbidden { + /** + * @since 3.10.0 + */ + readonly _tag = "Forbidden" + constructor( + readonly ast: AST.AST, + readonly actual: unknown, + readonly message?: string + ) {} +} + +/** + * @category type id + * @since 3.10.0 + */ +export const ParseErrorTypeId: unique symbol = Symbol.for("effect/Schema/ParseErrorTypeId") + +/** + * @category type id + * @since 3.10.0 + */ +export type ParseErrorTypeId = typeof ParseErrorTypeId + +/** + * @since 3.10.0 + */ +export const isParseError = (u: unknown): u is ParseError => Predicate.hasProperty(u, ParseErrorTypeId) + +/** + * @since 3.10.0 + */ +export class ParseError extends TaggedError("ParseError")<{ readonly issue: ParseIssue }> { + /** + * @since 3.10.0 + */ + readonly [ParseErrorTypeId] = ParseErrorTypeId + + get message() { + return this.toString() + } + /** + * @since 3.10.0 + */ + toString() { + return TreeFormatter.formatIssueSync(this.issue) + } + /** + * @since 3.10.0 + */ + toJSON() { + return { + _id: "ParseError", + message: this.toString() + } + } + /** + * @since 3.10.0 + */ + [Inspectable.NodeInspectSymbol]() { + return this.toJSON() + } +} + +/** + * @category constructors + * @since 3.10.0 + */ +export const parseError = (issue: ParseIssue): ParseError => new ParseError({ issue }) + +/** + * @category constructors + * @since 3.10.0 + */ +export const succeed: (a: A) => Either.Either = Either.right + +/** + * @category constructors + * @since 3.10.0 + */ +export const fail: (issue: ParseIssue) => Either.Either = Either.left + +const _try: (options: { + try: LazyArg + catch: (e: unknown) => ParseIssue +}) => Either.Either = Either.try + +export { + /** + * @category constructors + * @since 3.10.0 + */ + _try as try +} + +/** + * @category constructors + * @since 3.10.0 + */ +export const fromOption: { + (onNone: () => ParseIssue): (self: Option.Option) => Either.Either + (self: Option.Option, onNone: () => ParseIssue): Either.Either +} = Either.fromOption + +/** + * @category optimisation + * @since 3.10.0 + */ +export const flatMap: { + ( + f: (a: A) => Effect.Effect + ): (self: Effect.Effect) => Effect.Effect + ( + self: Effect.Effect, + f: (a: A) => Effect.Effect + ): Effect.Effect +} = dual(2, ( + self: Effect.Effect, + f: (a: A) => Effect.Effect +): Effect.Effect => { + const s: any = self + if (s["_tag"] === "Left") { + return s + } + if (s["_tag"] === "Right") { + return f(s.right) + } + return Effect.flatMap(self, f) +}) + +/** + * @category optimisation + * @since 3.10.0 + */ +export const map: { + (f: (a: A) => B): (self: Effect.Effect) => Effect.Effect + (self: Effect.Effect, f: (a: A) => B): Effect.Effect +} = dual(2, (self: Effect.Effect, f: (a: A) => B): Effect.Effect => { + const s: any = self + if (s["_tag"] === "Left") { + return s + } + if (s["_tag"] === "Right") { + return Either.right(f(s.right)) + } + return Effect.map(self, f) +}) + +/** + * @category optimisation + * @since 3.10.0 + */ +export const mapError: { + (f: (e: E) => E2): (self: Effect.Effect) => Effect.Effect + (self: Effect.Effect, f: (e: E) => E2): Effect.Effect +} = dual(2, (self: Effect.Effect, f: (e: E) => E2): Effect.Effect => { + const s: any = self + if (s["_tag"] === "Left") { + return Either.left(f(s.left)) + } + if (s["_tag"] === "Right") { + return s + } + return Effect.mapError(self, f) +}) + +/** + * @category optimisation + * @since 3.10.0 + */ +export const eitherOrUndefined = ( + self: Effect.Effect +): Either.Either | undefined => { + const s: any = self + if (s["_tag"] === "Left" || s["_tag"] === "Right") { + return s + } +} + +/** + * @category optimisation + * @since 3.10.0 + */ +export const mapBoth: { + ( + options: { readonly onFailure: (e: E) => E2; readonly onSuccess: (a: A) => A2 } + ): (self: Effect.Effect) => Effect.Effect + ( + self: Effect.Effect, + options: { readonly onFailure: (e: E) => E2; readonly onSuccess: (a: A) => A2 } + ): Effect.Effect +} = dual(2, ( + self: Effect.Effect, + options: { readonly onFailure: (e: E) => E2; readonly onSuccess: (a: A) => A2 } +): Effect.Effect => { + const s: any = self + if (s["_tag"] === "Left") { + return Either.left(options.onFailure(s.left)) + } + if (s["_tag"] === "Right") { + return Either.right(options.onSuccess(s.right)) + } + return Effect.mapBoth(self, options) +}) + +/** + * @category optimisation + * @since 3.10.0 + */ +export const orElse: { + ( + f: (e: E) => Effect.Effect + ): (self: Effect.Effect) => Effect.Effect + ( + self: Effect.Effect, + f: (e: E) => Effect.Effect + ): Effect.Effect +} = dual(2, ( + self: Effect.Effect, + f: (e: E) => Effect.Effect +): Effect.Effect => { + const s: any = self + if (s["_tag"] === "Left") { + return f(s.left) + } + if (s["_tag"] === "Right") { + return s + } + return Effect.catchAll(self, f) +}) + +/** + * @since 3.10.0 + */ +export type DecodeUnknown = (u: unknown, options?: AST.ParseOptions) => Effect.Effect + +/** + * @since 3.10.0 + */ +export type DeclarationDecodeUnknown = ( + u: unknown, + options: AST.ParseOptions, + ast: AST.Declaration +) => Effect.Effect + +/** @internal */ +export const mergeInternalOptions = ( + options: InternalOptions | undefined, + overrideOptions: InternalOptions | number | undefined +): InternalOptions | undefined => { + if (overrideOptions === undefined || Predicate.isNumber(overrideOptions)) { + return options + } + if (options === undefined) { + return overrideOptions + } + return { ...options, ...overrideOptions } +} + +const getEither = (ast: AST.AST, isDecoding: boolean, options?: AST.ParseOptions) => { + const parser = goMemo(ast, isDecoding) + return (u: unknown, overrideOptions?: AST.ParseOptions): Either.Either => + parser(u, mergeInternalOptions(options, overrideOptions)) as any +} + +const getSync = (ast: AST.AST, isDecoding: boolean, options?: AST.ParseOptions) => { + const parser = getEither(ast, isDecoding, options) + return (input: unknown, overrideOptions?: AST.ParseOptions) => + Either.getOrThrowWith(parser(input, overrideOptions), parseError) +} + +const getOption = (ast: AST.AST, isDecoding: boolean, options?: AST.ParseOptions) => { + const parser = getEither(ast, isDecoding, options) + return (input: unknown, overrideOptions?: AST.ParseOptions): Option.Option => + Option.getRight(parser(input, overrideOptions)) +} + +const getEffect = (ast: AST.AST, isDecoding: boolean, options?: AST.ParseOptions) => { + const parser = goMemo(ast, isDecoding) + return (input: unknown, overrideOptions?: AST.ParseOptions): Effect.Effect => + parser(input, { ...mergeInternalOptions(options, overrideOptions), isEffectAllowed: true }) +} + +/** + * @throws `ParseError` + * @category decoding + * @since 3.10.0 + */ +export const decodeUnknownSync = ( + schema: Schema.Schema, + options?: AST.ParseOptions +): (u: unknown, overrideOptions?: AST.ParseOptions) => A => getSync(schema.ast, true, options) + +/** + * @category decoding + * @since 3.10.0 + */ +export const decodeUnknownOption = ( + schema: Schema.Schema, + options?: AST.ParseOptions +): (u: unknown, overrideOptions?: AST.ParseOptions) => Option.Option => getOption(schema.ast, true, options) + +/** + * @category decoding + * @since 3.10.0 + */ +export const decodeUnknownEither = ( + schema: Schema.Schema, + options?: AST.ParseOptions +): (u: unknown, overrideOptions?: AST.ParseOptions) => Either.Either => + getEither(schema.ast, true, options) + +/** + * @category decoding + * @since 3.10.0 + */ +export const decodeUnknownPromise = ( + schema: Schema.Schema, + options?: AST.ParseOptions +) => { + const parser = decodeUnknown(schema, options) + return (u: unknown, overrideOptions?: AST.ParseOptions): Promise => Effect.runPromise(parser(u, overrideOptions)) +} + +/** + * @category decoding + * @since 3.10.0 + */ +export const decodeUnknown = ( + schema: Schema.Schema, + options?: AST.ParseOptions +): (u: unknown, overrideOptions?: AST.ParseOptions) => Effect.Effect => + getEffect(schema.ast, true, options) + +/** + * @throws `ParseError` + * @category encoding + * @since 3.10.0 + */ +export const encodeUnknownSync = ( + schema: Schema.Schema, + options?: AST.ParseOptions +): (u: unknown, overrideOptions?: AST.ParseOptions) => I => getSync(schema.ast, false, options) + +/** + * @category encoding + * @since 3.10.0 + */ +export const encodeUnknownOption = ( + schema: Schema.Schema, + options?: AST.ParseOptions +): (u: unknown, overrideOptions?: AST.ParseOptions) => Option.Option => getOption(schema.ast, false, options) + +/** + * @category encoding + * @since 3.10.0 + */ +export const encodeUnknownEither = ( + schema: Schema.Schema, + options?: AST.ParseOptions +): (u: unknown, overrideOptions?: AST.ParseOptions) => Either.Either => + getEither(schema.ast, false, options) + +/** + * @category encoding + * @since 3.10.0 + */ +export const encodeUnknownPromise = ( + schema: Schema.Schema, + options?: AST.ParseOptions +) => { + const parser = encodeUnknown(schema, options) + return (u: unknown, overrideOptions?: AST.ParseOptions): Promise => Effect.runPromise(parser(u, overrideOptions)) +} + +/** + * @category encoding + * @since 3.10.0 + */ +export const encodeUnknown = ( + schema: Schema.Schema, + options?: AST.ParseOptions +): (u: unknown, overrideOptions?: AST.ParseOptions) => Effect.Effect => + getEffect(schema.ast, false, options) + +/** + * @category decoding + * @since 3.10.0 + */ +export const decodeSync: ( + schema: Schema.Schema, + options?: AST.ParseOptions +) => (i: I, overrideOptions?: AST.ParseOptions) => A = decodeUnknownSync + +/** + * @category decoding + * @since 3.10.0 + */ +export const decodeOption: ( + schema: Schema.Schema, + options?: AST.ParseOptions +) => (i: I, overrideOptions?: AST.ParseOptions) => Option.Option = decodeUnknownOption + +/** + * @category decoding + * @since 3.10.0 + */ +export const decodeEither: ( + schema: Schema.Schema, + options?: AST.ParseOptions +) => (i: I, overrideOptions?: AST.ParseOptions) => Either.Either = decodeUnknownEither + +/** + * @category decoding + * @since 3.10.0 + */ +export const decodePromise: ( + schema: Schema.Schema, + options?: AST.ParseOptions +) => (i: I, overrideOptions?: AST.ParseOptions) => Promise = decodeUnknownPromise + +/** + * @category decoding + * @since 3.10.0 + */ +export const decode: ( + schema: Schema.Schema, + options?: AST.ParseOptions +) => (i: I, overrideOptions?: AST.ParseOptions) => Effect.Effect = decodeUnknown + +/** + * @throws `ParseError` + * @category validation + * @since 3.10.0 + */ +export const validateSync = ( + schema: Schema.Schema, + options?: AST.ParseOptions +): (u: unknown, overrideOptions?: AST.ParseOptions) => A => getSync(AST.typeAST(schema.ast), true, options) + +/** + * @category validation + * @since 3.10.0 + */ +export const validateOption = ( + schema: Schema.Schema, + options?: AST.ParseOptions +): (u: unknown, overrideOptions?: AST.ParseOptions) => Option.Option => + getOption(AST.typeAST(schema.ast), true, options) + +/** + * @category validation + * @since 3.10.0 + */ +export const validateEither = ( + schema: Schema.Schema, + options?: AST.ParseOptions +): (u: unknown, overrideOptions?: AST.ParseOptions) => Either.Either => + getEither(AST.typeAST(schema.ast), true, options) + +/** + * @category validation + * @since 3.10.0 + */ +export const validatePromise = ( + schema: Schema.Schema, + options?: AST.ParseOptions +) => { + const parser = validate(schema, options) + return (u: unknown, overrideOptions?: AST.ParseOptions): Promise => Effect.runPromise(parser(u, overrideOptions)) +} + +/** + * @category validation + * @since 3.10.0 + */ +export const validate = ( + schema: Schema.Schema, + options?: AST.ParseOptions +): (a: unknown, overrideOptions?: AST.ParseOptions) => Effect.Effect => + getEffect(AST.typeAST(schema.ast), true, options) + +/** + * By default the option `exact` is set to `true`. + * + * @category validation + * @since 3.10.0 + */ +export const is = (schema: Schema.Schema, options?: AST.ParseOptions) => { + const parser = goMemo(AST.typeAST(schema.ast), true) + return (u: unknown, overrideOptions?: AST.ParseOptions | number): u is A => + Either.isRight(parser(u, { exact: true, ...mergeInternalOptions(options, overrideOptions) }) as any) +} + +/** + * By default the option `exact` is set to `true`. + * + * @throws `ParseError` + * @category validation + * @since 3.10.0 + */ +export const asserts = (schema: Schema.Schema, options?: AST.ParseOptions) => { + const parser = goMemo(AST.typeAST(schema.ast), true) + return (u: unknown, overrideOptions?: AST.ParseOptions): asserts u is A => { + const result: Either.Either = parser(u, { + exact: true, + ...mergeInternalOptions(options, overrideOptions) + }) as any + if (Either.isLeft(result)) { + throw parseError(result.left) + } + } +} + +/** + * @category encoding + * @since 3.10.0 + */ +export const encodeSync: ( + schema: Schema.Schema, + options?: AST.ParseOptions +) => (a: A, overrideOptions?: AST.ParseOptions) => I = encodeUnknownSync + +/** + * @category encoding + * @since 3.10.0 + */ +export const encodeOption: ( + schema: Schema.Schema, + options?: AST.ParseOptions +) => (input: A, overrideOptions?: AST.ParseOptions) => Option.Option = encodeUnknownOption + +/** + * @category encoding + * @since 3.10.0 + */ +export const encodeEither: ( + schema: Schema.Schema, + options?: AST.ParseOptions +) => (a: A, overrideOptions?: AST.ParseOptions) => Either.Either = encodeUnknownEither + +/** + * @category encoding + * @since 3.10.0 + */ +export const encodePromise: ( + schema: Schema.Schema, + options?: AST.ParseOptions +) => (a: A, overrideOptions?: AST.ParseOptions) => Promise = encodeUnknownPromise + +/** + * @category encoding + * @since 3.10.0 + */ +export const encode: ( + schema: Schema.Schema, + options?: AST.ParseOptions +) => (a: A, overrideOptions?: AST.ParseOptions) => Effect.Effect = encodeUnknown + +interface InternalOptions extends AST.ParseOptions { + readonly isEffectAllowed?: boolean +} + +interface Parser { + (i: any, options?: InternalOptions): Effect.Effect +} + +const decodeMemoMap = globalValue( + Symbol.for("effect/Schema/Parser/decodeMemoMap"), + () => new WeakMap() +) +const encodeMemoMap = globalValue( + Symbol.for("effect/Schema/Parser/encodeMemoMap"), + () => new WeakMap() +) + +const goMemo = (ast: AST.AST, isDecoding: boolean): Parser => { + const memoMap = isDecoding ? decodeMemoMap : encodeMemoMap + const memo = memoMap.get(ast) + if (memo) { + return memo + } + const raw = go(ast, isDecoding) + const parseOptionsAnnotation = AST.getParseOptionsAnnotation(ast) + const parserWithOptions: Parser = Option.isSome(parseOptionsAnnotation) + ? (i, options) => raw(i, mergeInternalOptions(options, parseOptionsAnnotation.value)) + : raw + const decodingFallbackAnnotation = AST.getDecodingFallbackAnnotation(ast) + const parser: Parser = isDecoding && Option.isSome(decodingFallbackAnnotation) + ? (i, options) => + handleForbidden(orElse(parserWithOptions(i, options), decodingFallbackAnnotation.value), ast, i, options) + : parserWithOptions + memoMap.set(ast, parser) + return parser +} + +const getConcurrency = (ast: AST.AST): Concurrency | undefined => + Option.getOrUndefined(AST.getConcurrencyAnnotation(ast)) + +const getBatching = (ast: AST.AST): boolean | "inherit" | undefined => + Option.getOrUndefined(AST.getBatchingAnnotation(ast)) + +const go = (ast: AST.AST, isDecoding: boolean): Parser => { + switch (ast._tag) { + case "Refinement": { + if (isDecoding) { + const from = goMemo(ast.from, true) + return (i, options) => { + options = options ?? AST.defaultParseOption + const allErrors = options?.errors === "all" + const result = flatMap( + orElse(from(i, options), (ef) => { + const issue = new Refinement(ast, i, "From", ef) + if (allErrors && AST.hasStableFilter(ast)) { + return Option.match( + ast.filter(i, options, ast), + { + onNone: () => Either.left(issue), + onSome: (ep) => Either.left(new Composite(ast, i, [issue, new Refinement(ast, i, "Predicate", ep)])) + } + ) + } + return Either.left(issue) + }), + (a) => + Option.match( + ast.filter(a, options, ast), + { + onNone: () => Either.right(a), + onSome: (ep) => Either.left(new Refinement(ast, i, "Predicate", ep)) + } + ) + ) + return handleForbidden(result, ast, i, options) + } + } else { + const from = goMemo(AST.typeAST(ast), true) + const to = goMemo(dropRightRefinement(ast.from), false) + return (i, options) => handleForbidden(flatMap(from(i, options), (a) => to(a, options)), ast, i, options) + } + } + case "Transformation": { + const transform = getFinalTransformation(ast.transformation, isDecoding) + const from = isDecoding ? goMemo(ast.from, true) : goMemo(ast.to, false) + const to = isDecoding ? goMemo(ast.to, true) : goMemo(ast.from, false) + return (i, options) => + handleForbidden( + flatMap( + mapError( + from(i, options), + (e) => new Transformation(ast, i, isDecoding ? "Encoded" : "Type", e) + ), + (a) => + flatMap( + mapError( + transform(a, options ?? AST.defaultParseOption, ast, i), + (e) => new Transformation(ast, i, "Transformation", e) + ), + (i2) => + mapError( + to(i2, options), + (e) => new Transformation(ast, i, isDecoding ? "Type" : "Encoded", e) + ) + ) + ), + ast, + i, + options + ) + } + case "Declaration": { + const parse = isDecoding + ? ast.decodeUnknown(...ast.typeParameters) + : ast.encodeUnknown(...ast.typeParameters) + return (i, options) => handleForbidden(parse(i, options ?? AST.defaultParseOption, ast), ast, i, options) + } + case "Literal": + return fromRefinement(ast, (u): u is typeof ast.literal => u === ast.literal) + case "UniqueSymbol": + return fromRefinement(ast, (u): u is typeof ast.symbol => u === ast.symbol) + case "UndefinedKeyword": + return fromRefinement(ast, Predicate.isUndefined) + case "NeverKeyword": + return fromRefinement(ast, Predicate.isNever) + case "UnknownKeyword": + case "AnyKeyword": + case "VoidKeyword": + return Either.right + case "StringKeyword": + return fromRefinement(ast, Predicate.isString) + case "NumberKeyword": + return fromRefinement(ast, Predicate.isNumber) + case "BooleanKeyword": + return fromRefinement(ast, Predicate.isBoolean) + case "BigIntKeyword": + return fromRefinement(ast, Predicate.isBigInt) + case "SymbolKeyword": + return fromRefinement(ast, Predicate.isSymbol) + case "ObjectKeyword": + return fromRefinement(ast, Predicate.isObject) + case "Enums": + return fromRefinement(ast, (u): u is any => ast.enums.some(([_, value]) => value === u)) + case "TemplateLiteral": { + const regex = AST.getTemplateLiteralRegExp(ast) + return fromRefinement(ast, (u): u is any => Predicate.isString(u) && regex.test(u)) + } + case "TupleType": { + const elements = ast.elements.map((e) => goMemo(e.type, isDecoding)) + const rest = ast.rest.map((annotatedAST) => goMemo(annotatedAST.type, isDecoding)) + let requiredTypes: Array = ast.elements.filter((e) => !e.isOptional) + if (ast.rest.length > 0) { + requiredTypes = requiredTypes.concat(ast.rest.slice(1)) + } + const requiredLen = requiredTypes.length + const expectedIndexes = ast.elements.length > 0 ? ast.elements.map((_, i) => i).join(" | ") : "never" + const concurrency = getConcurrency(ast) + const batching = getBatching(ast) + return (input: unknown, options) => { + if (!array_.isArray(input)) { + return Either.left(new Type(ast, input)) + } + const allErrors = options?.errors === "all" + const es: Array<[number, ParseIssue]> = [] + let stepKey = 0 + const output: Array<[number, any]> = [] + // --------------------------------------------- + // handle missing indexes + // --------------------------------------------- + const len = input.length + for (let i = len; i <= requiredLen - 1; i++) { + const e = new Pointer(i, input, new Missing(requiredTypes[i - len])) + if (allErrors) { + es.push([stepKey++, e]) + continue + } else { + return Either.left(new Composite(ast, input, e, output)) + } + } + + // --------------------------------------------- + // handle excess indexes + // --------------------------------------------- + if (ast.rest.length === 0) { + for (let i = ast.elements.length; i <= len - 1; i++) { + const e = new Pointer(i, input, new Unexpected(input[i], `is unexpected, expected: ${expectedIndexes}`)) + if (allErrors) { + es.push([stepKey++, e]) + continue + } else { + return Either.left(new Composite(ast, input, e, output)) + } + } + } + + let i = 0 + type State = { + es: typeof es + output: typeof output + } + let queue: + | Array<(_: State) => Effect.Effect> + | undefined = undefined + + // --------------------------------------------- + // handle elements + // --------------------------------------------- + for (; i < elements.length; i++) { + if (len < i + 1) { + if (ast.elements[i].isOptional) { + // the input element is missing + continue + } + } else { + const parser = elements[i] + const te = parser(input[i], options) + const eu = eitherOrUndefined(te) + if (eu) { + if (Either.isLeft(eu)) { + // the input element is present but is not valid + const e = new Pointer(i, input, eu.left) + if (allErrors) { + es.push([stepKey++, e]) + continue + } else { + return Either.left(new Composite(ast, input, e, sortByIndex(output))) + } + } + output.push([stepKey++, eu.right]) + } else { + const nk = stepKey++ + const index = i + if (!queue) { + queue = [] + } + queue.push(({ es, output }: State) => + Effect.flatMap(Effect.either(te), (t) => { + if (Either.isLeft(t)) { + // the input element is present but is not valid + const e = new Pointer(index, input, t.left) + if (allErrors) { + es.push([nk, e]) + return Effect.void + } else { + return Either.left(new Composite(ast, input, e, sortByIndex(output))) + } + } + output.push([nk, t.right]) + return Effect.void + }) + ) + } + } + } + // --------------------------------------------- + // handle rest element + // --------------------------------------------- + if (array_.isNonEmptyReadonlyArray(rest)) { + const [head, ...tail] = rest + for (; i < len - tail.length; i++) { + const te = head(input[i], options) + const eu = eitherOrUndefined(te) + if (eu) { + if (Either.isLeft(eu)) { + const e = new Pointer(i, input, eu.left) + if (allErrors) { + es.push([stepKey++, e]) + continue + } else { + return Either.left(new Composite(ast, input, e, sortByIndex(output))) + } + } else { + output.push([stepKey++, eu.right]) + } + } else { + const nk = stepKey++ + const index = i + if (!queue) { + queue = [] + } + queue.push( + ({ es, output }: State) => + Effect.flatMap(Effect.either(te), (t) => { + if (Either.isLeft(t)) { + const e = new Pointer(index, input, t.left) + if (allErrors) { + es.push([nk, e]) + return Effect.void + } else { + return Either.left(new Composite(ast, input, e, sortByIndex(output))) + } + } else { + output.push([nk, t.right]) + return Effect.void + } + }) + ) + } + } + // --------------------------------------------- + // handle post rest elements + // --------------------------------------------- + for (let j = 0; j < tail.length; j++) { + i += j + if (len < i + 1) { + continue + } else { + const te = tail[j](input[i], options) + const eu = eitherOrUndefined(te) + if (eu) { + if (Either.isLeft(eu)) { + // the input element is present but is not valid + const e = new Pointer(i, input, eu.left) + if (allErrors) { + es.push([stepKey++, e]) + continue + } else { + return Either.left(new Composite(ast, input, e, sortByIndex(output))) + } + } + output.push([stepKey++, eu.right]) + } else { + const nk = stepKey++ + const index = i + if (!queue) { + queue = [] + } + queue.push( + ({ es, output }: State) => + Effect.flatMap(Effect.either(te), (t) => { + if (Either.isLeft(t)) { + // the input element is present but is not valid + const e = new Pointer(index, input, t.left) + if (allErrors) { + es.push([nk, e]) + return Effect.void + } else { + return Either.left(new Composite(ast, input, e, sortByIndex(output))) + } + } + output.push([nk, t.right]) + return Effect.void + }) + ) + } + } + } + } + + // --------------------------------------------- + // compute result + // --------------------------------------------- + const computeResult = ({ es, output }: State) => + array_.isNonEmptyArray(es) ? + Either.left(new Composite(ast, input, sortByIndex(es), sortByIndex(output))) : + Either.right(sortByIndex(output)) + if (queue && queue.length > 0) { + const cqueue = queue + return Effect.suspend(() => { + const state: State = { + es: array_.copy(es), + output: array_.copy(output) + } + return Effect.flatMap( + Effect.forEach(cqueue, (f) => f(state), { concurrency, batching, discard: true }), + () => computeResult(state) + ) + }) + } + return computeResult({ output, es }) + } + } + case "TypeLiteral": { + if (ast.propertySignatures.length === 0 && ast.indexSignatures.length === 0) { + return fromRefinement(ast, Predicate.isNotNullable) + } + + const propertySignatures: Array = [] + const expectedKeysMap: Record = {} + const expectedKeys: Array = [] + for (const ps of ast.propertySignatures) { + propertySignatures.push([goMemo(ps.type, isDecoding), ps]) + expectedKeysMap[ps.name] = null + expectedKeys.push(ps.name) + } + + const indexSignatures = ast.indexSignatures.map((is) => + [ + goMemo(is.parameter, isDecoding), + goMemo(is.type, isDecoding), + is.parameter + ] as const + ) + const expectedAST = AST.Union.make( + ast.indexSignatures.map((is): AST.AST => is.parameter).concat( + expectedKeys.map((key) => Predicate.isSymbol(key) ? new AST.UniqueSymbol(key) : new AST.Literal(key)) + ) + ) + const expected = goMemo(expectedAST, isDecoding) + const concurrency = getConcurrency(ast) + const batching = getBatching(ast) + return (input: unknown, options) => { + if (!Predicate.isRecord(input)) { + return Either.left(new Type(ast, input)) + } + const allErrors = options?.errors === "all" + const es: Array<[number, ParseIssue]> = [] + let stepKey = 0 + + // --------------------------------------------- + // handle excess properties + // --------------------------------------------- + const onExcessPropertyError = options?.onExcessProperty === "error" + const onExcessPropertyPreserve = options?.onExcessProperty === "preserve" + const output: Record = {} + let inputKeys: Array | undefined + if (onExcessPropertyError || onExcessPropertyPreserve) { + inputKeys = util_.ownKeys(input) + for (const key of inputKeys) { + const eu = eitherOrUndefined(expected(key, options))! + if (Either.isLeft(eu)) { + // key is unexpected + if (onExcessPropertyError) { + const e = new Pointer( + key, + input, + new Unexpected(input[key], `is unexpected, expected: ${String(expectedAST)}`) + ) + if (allErrors) { + es.push([stepKey++, e]) + continue + } else { + return Either.left(new Composite(ast, input, e, output)) + } + } else { + // preserve key + output[key] = input[key] + } + } + } + } + + // --------------------------------------------- + // handle property signatures + // --------------------------------------------- + type State = { + es: typeof es + output: typeof output + } + let queue: + | Array<(state: State) => Effect.Effect> + | undefined = undefined + + const isExact = options?.exact === true + for (let i = 0; i < propertySignatures.length; i++) { + const ps = propertySignatures[i][1] + const name = ps.name + const hasKey = Object.prototype.hasOwnProperty.call(input, name) + if (!hasKey) { + if (ps.isOptional) { + continue + } else if (isExact) { + const e = new Pointer(name, input, new Missing(ps)) + if (allErrors) { + es.push([stepKey++, e]) + continue + } else { + return Either.left(new Composite(ast, input, e, output)) + } + } + } + const parser = propertySignatures[i][0] + const te = parser(input[name], options) + const eu = eitherOrUndefined(te) + if (eu) { + if (Either.isLeft(eu)) { + const e = new Pointer(name, input, hasKey ? eu.left : new Missing(ps)) + if (allErrors) { + es.push([stepKey++, e]) + continue + } else { + return Either.left(new Composite(ast, input, e, output)) + } + } + output[name] = eu.right + } else { + const nk = stepKey++ + const index = name + if (!queue) { + queue = [] + } + queue.push( + ({ es, output }: State) => + Effect.flatMap(Effect.either(te), (t) => { + if (Either.isLeft(t)) { + const e = new Pointer(index, input, hasKey ? t.left : new Missing(ps)) + if (allErrors) { + es.push([nk, e]) + return Effect.void + } else { + return Either.left(new Composite(ast, input, e, output)) + } + } + output[index] = t.right + return Effect.void + }) + ) + } + } + + // --------------------------------------------- + // handle index signatures + // --------------------------------------------- + for (let i = 0; i < indexSignatures.length; i++) { + const indexSignature = indexSignatures[i] + const parameter = indexSignature[0] + const type = indexSignature[1] + const keys = util_.getKeysForIndexSignature(input, indexSignature[2]) + for (const key of keys) { + // --------------------------------------------- + // handle keys + // --------------------------------------------- + const keu = eitherOrUndefined(parameter(key, options)) + if (keu && Either.isRight(keu)) { + // --------------------------------------------- + // handle values + // --------------------------------------------- + const vpr = type(input[key], options) + const veu = eitherOrUndefined(vpr) + if (veu) { + if (Either.isLeft(veu)) { + const e = new Pointer(key, input, veu.left) + if (allErrors) { + es.push([stepKey++, e]) + continue + } else { + return Either.left(new Composite(ast, input, e, output)) + } + } else { + if (!Object.prototype.hasOwnProperty.call(expectedKeysMap, key)) { + output[key] = veu.right + } + } + } else { + const nk = stepKey++ + const index = key + if (!queue) { + queue = [] + } + queue.push( + ({ es, output }: State) => + Effect.flatMap( + Effect.either(vpr), + (tv) => { + if (Either.isLeft(tv)) { + const e = new Pointer(index, input, tv.left) + if (allErrors) { + es.push([nk, e]) + return Effect.void + } else { + return Either.left(new Composite(ast, input, e, output)) + } + } else { + if (!Object.prototype.hasOwnProperty.call(expectedKeysMap, key)) { + output[key] = tv.right + } + return Effect.void + } + } + ) + ) + } + } + } + } + // --------------------------------------------- + // compute result + // --------------------------------------------- + const computeResult = ({ es, output }: State) => { + if (array_.isNonEmptyArray(es)) { + return Either.left(new Composite(ast, input, sortByIndex(es), output)) + } + if (options?.propertyOrder === "original") { + // preserve input keys order + const keys = inputKeys || util_.ownKeys(input) + for (const name of expectedKeys) { + if (keys.indexOf(name) === -1) { + keys.push(name) + } + } + const out: any = {} + for (const key of keys) { + if (Object.prototype.hasOwnProperty.call(output, key)) { + out[key] = output[key] + } + } + return Either.right(out) + } + return Either.right(output) + } + if (queue && queue.length > 0) { + const cqueue = queue + return Effect.suspend(() => { + const state: State = { + es: array_.copy(es), + output: Object.assign({}, output) + } + return Effect.flatMap( + Effect.forEach(cqueue, (f) => f(state), { concurrency, batching, discard: true }), + () => computeResult(state) + ) + }) + } + return computeResult({ es, output }) + } + } + case "Union": { + const searchTree = getSearchTree(ast.types, isDecoding) + const ownKeys = util_.ownKeys(searchTree.keys) + const len = ownKeys.length + const map = new Map() + for (let i = 0; i < ast.types.length; i++) { + map.set(ast.types[i], goMemo(ast.types[i], isDecoding)) + } + const concurrency = getConcurrency(ast) ?? 1 + const batching = getBatching(ast) + return (input, options) => { + const es: Array<[number, ParseIssue]> = [] + let stepKey = 0 + let candidates: Array = [] + if (len > 0) { + // if there is at least one key then input must be an object + if (isObject(input)) { + for (let i = 0; i < len; i++) { + const name = ownKeys[i] + const buckets = searchTree.keys[name].buckets + // for each property that should contain a literal, check if the input contains that property + if (Object.prototype.hasOwnProperty.call(input, name)) { + const literal = String(input[name]) + // check that the value obtained from the input for the property corresponds to an existing bucket + if (Object.prototype.hasOwnProperty.call(buckets, literal)) { + // retrive the minimal set of candidates for decoding + candidates = candidates.concat(buckets[literal]) + } else { + const literals = AST.Union.make(searchTree.keys[name].literals) + es.push([ + stepKey++, + new Composite( + new AST.TypeLiteral([ + new AST.PropertySignature(name, literals, false, true) + ], []), + input, + new Pointer(name, input, new Type(literals, input[name])) + ) + ]) + } + } else { + const literals = AST.Union.make(searchTree.keys[name].literals) + const fakeps = new AST.PropertySignature(name, literals, false, true) + es.push([ + stepKey++, + new Composite( + new AST.TypeLiteral([fakeps], []), + input, + new Pointer(name, input, new Missing(fakeps)) + ) + ]) + } + } + } else { + es.push([stepKey++, new Type(ast, input)]) + } + } + if (searchTree.otherwise.length > 0) { + candidates = candidates.concat(searchTree.otherwise) + } + + let queue: + | Array<(state: State) => Effect.Effect> + | undefined = undefined + + type State = { + finalResult?: any + es: typeof es + } + + for (let i = 0; i < candidates.length; i++) { + const candidate = candidates[i] + const pr = map.get(candidate)!(input, options) + // the members of a union are ordered based on which one should be decoded first, + // therefore if one member has added a task, all subsequent members must + // also add a task to the queue even if they are synchronous + const eu = !queue || queue.length === 0 ? eitherOrUndefined(pr) : undefined + if (eu) { + if (Either.isRight(eu)) { + return eu + } else { + es.push([stepKey++, eu.left]) + } + } else { + const nk = stepKey++ + if (!queue) { + queue = [] + } + queue.push( + (state) => + Effect.suspend(() => { + if ("finalResult" in state) { + return Effect.void + } else { + return Effect.flatMap(Effect.either(pr), (t) => { + if (Either.isRight(t)) { + state.finalResult = t + } else { + state.es.push([nk, t.left]) + } + return Effect.void + }) + } + }) + ) + } + } + + // --------------------------------------------- + // compute result + // --------------------------------------------- + const computeResult = (es: State["es"]) => + array_.isNonEmptyArray(es) ? + es.length === 1 && es[0][1]._tag === "Type" ? + Either.left(es[0][1]) : + Either.left(new Composite(ast, input, sortByIndex(es))) : + // this should never happen + Either.left(new Type(ast, input)) + + if (queue && queue.length > 0) { + const cqueue = queue + return Effect.suspend(() => { + const state: State = { es: array_.copy(es) } + return Effect.flatMap( + Effect.forEach(cqueue, (f) => f(state), { concurrency, batching, discard: true }), + () => { + if ("finalResult" in state) { + return state.finalResult + } + return computeResult(state.es) + } + ) + }) + } + return computeResult(es) + } + } + case "Suspend": { + const get = util_.memoizeThunk(() => goMemo(AST.annotations(ast.f(), ast.annotations), isDecoding)) + return (a, options) => get()(a, options) + } + } +} + +const isObject = (input: unknown): input is { [x: PropertyKey]: unknown } => typeof input === "object" && input !== null + +const fromRefinement = (ast: AST.AST, refinement: (u: unknown) => u is A): Parser => (u) => + refinement(u) ? Either.right(u) : Either.left(new Type(ast, u)) + +/** @internal */ +export const getLiterals = ( + ast: AST.AST, + isDecoding: boolean +): ReadonlyArray<[PropertyKey, AST.Literal]> => { + switch (ast._tag) { + case "Declaration": { + const annotation = AST.getSurrogateAnnotation(ast) + if (Option.isSome(annotation)) { + return getLiterals(annotation.value, isDecoding) + } + break + } + case "TypeLiteral": { + const out: Array<[PropertyKey, AST.Literal]> = [] + for (let i = 0; i < ast.propertySignatures.length; i++) { + const propertySignature = ast.propertySignatures[i] + const type = isDecoding ? AST.encodedAST(propertySignature.type) : AST.typeAST(propertySignature.type) + if (AST.isLiteral(type) && !propertySignature.isOptional) { + out.push([propertySignature.name, type]) + } + } + return out + } + case "TupleType": { + const out: Array<[PropertyKey, AST.Literal]> = [] + for (let i = 0; i < ast.elements.length; i++) { + const element = ast.elements[i] + const type = isDecoding ? AST.encodedAST(element.type) : AST.typeAST(element.type) + if (AST.isLiteral(type) && !element.isOptional) { + out.push([i, type]) + } + } + return out + } + case "Refinement": + return getLiterals(ast.from, isDecoding) + case "Suspend": + return getLiterals(ast.f(), isDecoding) + case "Transformation": + return getLiterals(isDecoding ? ast.from : ast.to, isDecoding) + } + return [] +} + +/** + * The purpose of the algorithm is to narrow down the pool of possible candidates for decoding as much as possible. + * + * This function separates the schemas into two groups, `keys` and `otherwise`: + * + * - `keys`: the schema has at least one property with a literal value + * - `otherwise`: the schema has no properties with a literal value + * + * If a schema has at least one property with a literal value, so it ends up in `keys`, first a namespace is created for + * the name of the property containing the literal, and then within this namespace a "bucket" is created for the literal + * value in which to store all the schemas that have the same property and literal value. + * + * @internal + */ +export const getSearchTree = ( + members: ReadonlyArray, + isDecoding: boolean +): { + keys: { + readonly [key: PropertyKey]: { + buckets: { [literal: string]: ReadonlyArray } + literals: ReadonlyArray // this is for error messages + } + } + otherwise: ReadonlyArray +} => { + const keys: { + [key: PropertyKey]: { + buckets: { [literal: string]: Array } + literals: Array + } + } = {} + const otherwise: Array = [] + for (let i = 0; i < members.length; i++) { + const member = members[i] + const tags = getLiterals(member, isDecoding) + if (tags.length > 0) { + for (let j = 0; j < tags.length; j++) { + const [key, literal] = tags[j] + const hash = String(literal.literal) + keys[key] = keys[key] || { buckets: {}, literals: [] } + const buckets = keys[key].buckets + if (Object.prototype.hasOwnProperty.call(buckets, hash)) { + if (j < tags.length - 1) { + continue + } + buckets[hash].push(member) + keys[key].literals.push(literal) + } else { + buckets[hash] = [member] + keys[key].literals.push(literal) + break + } + } + } else { + otherwise.push(member) + } + } + return { keys, otherwise } +} + +const dropRightRefinement = (ast: AST.AST): AST.AST => AST.isRefinement(ast) ? dropRightRefinement(ast.from) : ast + +const handleForbidden = ( + effect: Effect.Effect, + ast: AST.AST, + actual: unknown, + options: InternalOptions | undefined +): Effect.Effect => { + const eu = eitherOrUndefined(effect) + if (eu) { + return eu + } + if (options?.isEffectAllowed === true) { + return effect + } + try { + return Effect.runSync(Effect.either(effect as Effect.Effect)) + } catch (e) { + return Either.left( + new Forbidden( + ast, + actual, + "cannot be be resolved synchronously, this is caused by using runSync on an effect that performs async work" + ) + ) + } +} + +const compare = ([a]: [number, ...Array], [b]: [number, ...Array]) => a > b ? 1 : a < b ? -1 : 0 + +function sortByIndex( + es: array_.NonEmptyArray<[number, T]> +): array_.NonEmptyArray +function sortByIndex(es: Array<[number, T]>): Array +function sortByIndex(es: Array<[number, any]>) { + return es.sort(compare).map((t) => t[1]) +} + +// ------------------------------------------------------------------------------------- +// transformations interpreter +// ------------------------------------------------------------------------------------- + +/** @internal */ +export const getFinalTransformation = ( + transformation: AST.TransformationKind, + isDecoding: boolean +): ( + fromA: any, + options: AST.ParseOptions, + self: AST.Transformation, + fromI: any +) => Effect.Effect => { + switch (transformation._tag) { + case "FinalTransformation": + return isDecoding ? transformation.decode : transformation.encode + case "ComposeTransformation": + return Either.right + case "TypeLiteralTransformation": + return (input) => { + let out: Effect.Effect = Either.right(input) + + // --------------------------------------------- + // handle property signature transformations + // --------------------------------------------- + for (const pst of transformation.propertySignatureTransformations) { + const [from, to] = isDecoding ? + [pst.from, pst.to] : + [pst.to, pst.from] + const transformation = isDecoding ? pst.decode : pst.encode + const f = (input: any) => { + const o = transformation( + Object.prototype.hasOwnProperty.call(input, from) ? + Option.some(input[from]) : + Option.none() + ) + delete input[from] + if (Option.isSome(o)) { + input[to] = o.value + } + return input + } + out = map(out, f) + } + return out + } + } +} diff --git a/packages/effect/src/Pretty.ts b/packages/effect/src/Pretty.ts new file mode 100644 index 0000000000..6c31b85fb5 --- /dev/null +++ b/packages/effect/src/Pretty.ts @@ -0,0 +1,218 @@ +/** + * @since 3.10.0 + */ +import * as Arr from "effect/Array" +import * as Option from "effect/Option" +import * as errors_ from "./internal/schema/errors.js" +import * as util_ from "./internal/schema/util.js" +import * as ParseResult from "./ParseResult.js" +import type * as Schema from "./Schema.js" +import * as AST from "./SchemaAST.js" + +/** + * @category model + * @since 3.10.0 + */ +export interface Pretty { + (a: To): string +} + +/** + * @category hooks + * @since 3.10.0 + */ +export const PrettyHookId: unique symbol = Symbol.for("effect/Schema/PrettyHookId") + +/** + * @category hooks + * @since 3.10.0 + */ +export type PrettyHookId = typeof PrettyHookId + +/** + * @category annotations + * @since 3.10.0 + */ +export const pretty = + (handler: (...args: ReadonlyArray>) => Pretty) => + (self: Schema.Schema): Schema.Schema => self.annotations({ [PrettyHookId]: handler }) + +/** + * @category prettify + * @since 3.10.0 + */ +export const make = (schema: Schema.Schema): (a: A) => string => compile(schema.ast, []) + +const getHook = AST.getAnnotation<(...args: ReadonlyArray>) => Pretty>( + PrettyHookId +) + +const getMatcher = (defaultPretty: Pretty) => (ast: AST.AST): Pretty => + Option.match(getHook(ast), { + onNone: () => defaultPretty, + onSome: (handler) => handler() + }) + +const toString = getMatcher((a) => String(a)) + +const stringify = getMatcher((a) => JSON.stringify(a)) + +const formatUnknown = getMatcher(util_.formatUnknown) + +/** + * @since 3.10.0 + */ +export const match: AST.Match> = { + "Declaration": (ast, go, path) => { + const hook = getHook(ast) + if (Option.isSome(hook)) { + return hook.value(...ast.typeParameters.map((tp) => go(tp, path))) + } + throw new Error(errors_.getPrettyMissingAnnotationErrorMessage(path, ast)) + }, + "VoidKeyword": getMatcher(() => "void(0)"), + "NeverKeyword": getMatcher(() => { + throw new Error(errors_.getPrettyNeverErrorMessage) + }), + "Literal": getMatcher((literal: AST.LiteralValue): string => + typeof literal === "bigint" ? + `${String(literal)}n` : + JSON.stringify(literal) + ), + "SymbolKeyword": toString, + "UniqueSymbol": toString, + "TemplateLiteral": stringify, + "UndefinedKeyword": toString, + "UnknownKeyword": formatUnknown, + "AnyKeyword": formatUnknown, + "ObjectKeyword": formatUnknown, + "StringKeyword": stringify, + "NumberKeyword": toString, + "BooleanKeyword": toString, + "BigIntKeyword": getMatcher((a) => `${String(a)}n`), + "Enums": stringify, + "TupleType": (ast, go, path) => { + const hook = getHook(ast) + if (Option.isSome(hook)) { + return hook.value() + } + const elements = ast.elements.map((e, i) => go(e.type, path.concat(i))) + const rest = ast.rest.map((annotatedAST) => go(annotatedAST.type, path)) + return (input: ReadonlyArray) => { + const output: Array = [] + let i = 0 + // --------------------------------------------- + // handle elements + // --------------------------------------------- + for (; i < elements.length; i++) { + if (input.length < i + 1) { + if (ast.elements[i].isOptional) { + continue + } + } else { + output.push(elements[i](input[i])) + } + } + // --------------------------------------------- + // handle rest element + // --------------------------------------------- + if (Arr.isNonEmptyReadonlyArray(rest)) { + const [head, ...tail] = rest + for (; i < input.length - tail.length; i++) { + output.push(head(input[i])) + } + // --------------------------------------------- + // handle post rest elements + // --------------------------------------------- + for (let j = 0; j < tail.length; j++) { + i += j + output.push(tail[j](input[i])) + } + } + + return "[" + output.join(", ") + "]" + } + }, + "TypeLiteral": (ast, go, path) => { + const hook = getHook(ast) + if (Option.isSome(hook)) { + return hook.value() + } + const propertySignaturesTypes = ast.propertySignatures.map((ps) => go(ps.type, path.concat(ps.name))) + const indexSignatureTypes = ast.indexSignatures.map((is) => go(is.type, path)) + const expectedKeys: any = {} + for (let i = 0; i < propertySignaturesTypes.length; i++) { + expectedKeys[ast.propertySignatures[i].name] = null + } + return (input: { readonly [x: PropertyKey]: unknown }) => { + const output: Array = [] + // --------------------------------------------- + // handle property signatures + // --------------------------------------------- + for (let i = 0; i < propertySignaturesTypes.length; i++) { + const ps = ast.propertySignatures[i] + const name = ps.name + if (ps.isOptional && !Object.prototype.hasOwnProperty.call(input, name)) { + continue + } + output.push( + `${util_.formatPropertyKey(name)}: ${propertySignaturesTypes[i](input[name])}` + ) + } + // --------------------------------------------- + // handle index signatures + // --------------------------------------------- + if (indexSignatureTypes.length > 0) { + for (let i = 0; i < indexSignatureTypes.length; i++) { + const type = indexSignatureTypes[i] + const keys = util_.getKeysForIndexSignature(input, ast.indexSignatures[i].parameter) + for (const key of keys) { + if (Object.prototype.hasOwnProperty.call(expectedKeys, key)) { + continue + } + output.push(`${util_.formatPropertyKey(key)}: ${type(input[key])}`) + } + } + } + + return Arr.isNonEmptyReadonlyArray(output) ? "{ " + output.join(", ") + " }" : "{}" + } + }, + "Union": (ast, go, path) => { + const hook = getHook(ast) + if (Option.isSome(hook)) { + return hook.value() + } + const types = ast.types.map((ast) => [ParseResult.is({ ast } as any), go(ast, path)] as const) + return (a) => { + const index = types.findIndex(([is]) => is(a)) + if (index === -1) { + throw new Error(errors_.getPrettyNoMatchingSchemaErrorMessage(a, path, ast)) + } + return types[index][1](a) + } + }, + "Suspend": (ast, go, path) => { + return Option.match(getHook(ast), { + onNone: () => { + const get = util_.memoizeThunk(() => go(ast.f(), path)) + return (a) => get()(a) + }, + onSome: (handler) => handler() + }) + }, + "Refinement": (ast, go, path) => { + return Option.match(getHook(ast), { + onNone: () => go(ast.from, path), + onSome: (handler) => handler() + }) + }, + "Transformation": (ast, go, path) => { + return Option.match(getHook(ast), { + onNone: () => go(ast.to, path), + onSome: (handler) => handler() + }) + } +} + +const compile = AST.getCompiler(match) diff --git a/packages/effect/src/Schema.ts b/packages/effect/src/Schema.ts new file mode 100644 index 0000000000..0a7b1ce29b --- /dev/null +++ b/packages/effect/src/Schema.ts @@ -0,0 +1,9270 @@ +/** + * @since 3.10.0 + */ + +import * as array_ from "effect/Array" +import * as bigDecimal_ from "effect/BigDecimal" +import * as bigInt_ from "effect/BigInt" +import * as boolean_ from "effect/Boolean" +import type { Brand } from "effect/Brand" +import * as cause_ from "effect/Cause" +import * as chunk_ from "effect/Chunk" +import * as config_ from "effect/Config" +import * as configError_ from "effect/ConfigError" +import * as data_ from "effect/Data" +import * as dateTime from "effect/DateTime" +import * as duration_ from "effect/Duration" +import * as Effect from "effect/Effect" +import * as either_ from "effect/Either" +import * as Encoding from "effect/Encoding" +import * as Equal from "effect/Equal" +import * as Equivalence from "effect/Equivalence" +import * as exit_ from "effect/Exit" +import * as fiberId_ from "effect/FiberId" +import type { LazyArg } from "effect/Function" +import { dual, identity } from "effect/Function" +import * as hashMap_ from "effect/HashMap" +import * as hashSet_ from "effect/HashSet" +import * as list_ from "effect/List" +import * as number_ from "effect/Number" +import * as option_ from "effect/Option" +import type * as Order from "effect/Order" +import type { Pipeable } from "effect/Pipeable" +import { pipeArguments } from "effect/Pipeable" +import * as Predicate from "effect/Predicate" +import * as record_ from "effect/Record" +import * as redacted_ from "effect/Redacted" +import * as Request from "effect/Request" +import * as sortedSet_ from "effect/SortedSet" +import * as string_ from "effect/String" +import * as struct_ from "effect/Struct" +import type * as Types from "effect/Types" +import * as arbitrary_ from "./Arbitrary.js" +import type { GenerationContext, LazyArbitrary } from "./Arbitrary.js" +import * as fastCheck_ from "./FastCheck.js" +import * as errors_ from "./internal/schema/errors.js" +import * as filters_ from "./internal/schema/filters.js" +import * as serializable_ from "./internal/schema/serializable.js" +import * as util_ from "./internal/schema/util.js" +import * as ParseResult from "./ParseResult.js" +import * as pretty_ from "./Pretty.js" +import type { ParseOptions } from "./SchemaAST.js" +import * as AST from "./SchemaAST.js" +import * as equivalence_ from "./SchemaEquivalence.js" +import * as TreeFormatter from "./SchemaTreeFormatter.js" +import type * as Serializable from "./Serializable.js" + +/** + * @since 3.10.0 + */ +export type Simplify = { [K in keyof A]: A[K] } & {} + +/** + * @since 3.10.0 + */ +export type SimplifyMutable = { + -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/Schema") + +/** + * @since 3.10.0 + * @category symbol + */ +export type TypeId = typeof TypeId + +/** + * @category model + * @since 3.10.0 + */ +export interface Schema extends Schema.Variance, 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.Schema): Schema +} + +/** + * @category model + * @since 3.10.0 + */ +export interface SchemaClass extends AnnotableClass, A, I, R> {} + +/** + * @category constructors + * @since 3.10.0 + */ +export const make = (ast: AST.AST): SchemaClass => + class SchemaClass { + [TypeId] = variance + static Type: A + static Encoded: I + static Context: R + static [TypeId] = variance + static ast = ast + static annotations(annotations: Annotations.Schema) { + return make(mergeSchemaAnnotations(this.ast, annotations)) + } + static pipe() { + return pipeArguments(this, arguments) + } + static toString() { + return String(ast) + } + } + +const variance = { + /* c8 ignore next */ + _A: (_: any) => _, + /* c8 ignore next */ + _I: (_: any) => _, + /* c8 ignore next */ + _R: (_: never) => _ +} + +interface AllAnnotations> + extends Annotations.Schema, PropertySignature.Annotations +{} + +const toASTAnnotations = >( + annotations?: AllAnnotations +): AST.Annotations => { + if (!annotations) { + return {} + } + const out: Types.Mutable = {} + + // symbols are reserved for custom annotations + const custom = Object.getOwnPropertySymbols(annotations) + for (const sym of custom) { + out[sym] = annotations[sym] + } + + // string keys are reserved as /schema namespace + if (annotations.typeId !== undefined) { + const typeId = annotations.typeId + if (typeof typeId === "object") { + out[AST.TypeAnnotationId] = typeId.id + out[typeId.id] = typeId.annotation + } else { + out[AST.TypeAnnotationId] = typeId + } + } + const move = (from: keyof typeof annotations, to: symbol) => { + if (annotations[from] !== undefined) { + out[to] = annotations[from] + } + } + move("message", AST.MessageAnnotationId) + move("missingMessage", AST.MissingMessageAnnotationId) + move("identifier", AST.IdentifierAnnotationId) + move("title", AST.TitleAnnotationId) + move("description", AST.DescriptionAnnotationId) + move("examples", AST.ExamplesAnnotationId) + move("default", AST.DefaultAnnotationId) + move("documentation", AST.DocumentationAnnotationId) + move("jsonSchema", AST.JSONSchemaAnnotationId) + move("arbitrary", arbitrary_.ArbitraryHookId) + move("pretty", pretty_.PrettyHookId) + move("equivalence", equivalence_.EquivalenceHookId) + move("concurrency", AST.ConcurrencyAnnotationId) + move("batching", AST.BatchingAnnotationId) + move("parseIssueTitle", AST.ParseIssueTitleAnnotationId) + move("parseOptions", AST.ParseOptionsAnnotationId) + move("decodingFallback", AST.DecodingFallbackAnnotationId) + + return out +} + +const mergeSchemaAnnotations = (ast: AST.AST, annotations: Annotations.Schema): AST.AST => + AST.annotations(ast, toASTAnnotations(annotations)) + +/** + * @category annotations + * @since 3.10.0 + */ +export declare namespace Annotable { + /** + * @since 3.10.0 + */ + export type Self = ReturnType + + /** + * @since 3.10.0 + */ + export type Any = Annotable + + /** + * @since 3.10.0 + */ + export type All = + | Any + | Annotable + | Annotable + | Annotable +} + +/** + * @category annotations + * @since 3.10.0 + */ +export interface Annotable, A, I = A, R = never> extends Schema { + annotations(annotations: Annotations.Schema): Self +} + +/** + * @category annotations + * @since 3.10.0 + */ +export interface AnnotableClass, A, I = A, R = never> extends Annotable { + new(_: never): Schema.Variance +} + +/** + * @since 3.10.0 + */ +export const asSchema = ( + schema: S +): Schema, Schema.Encoded, Schema.Context> => schema as any + +/** + * @category formatting + * @since 3.10.0 + */ +export const format = (schema: S): string => String(schema.ast) + +/** + * @since 3.10.0 + */ +export declare namespace Schema { + /** + * @since 3.10.0 + */ + export interface Variance { + readonly [TypeId]: { + readonly _A: Types.Invariant + readonly _I: Types.Invariant + readonly _R: Types.Covariant + } + } + + /** + * @since 3.10.0 + */ + export type Type = S extends Schema.Variance ? A : never + + /** + * @since 3.10.0 + */ + export type Encoded = S extends Schema.Variance ? I : never + + /** + * @since 3.10.0 + */ + export type Context = S extends Schema.Variance ? R : never + + /** + * @since 3.10.0 + */ + export type ToAsserts = ( + input: unknown, + options?: AST.ParseOptions + ) => asserts input is Schema.Type + + /** + * Any schema, except for `never`. + * + * @since 3.10.0 + */ + export type Any = Schema + + /** + * Any schema with `Context = never`, except for `never`. + * + * @since 3.10.0 + */ + export type AnyNoContext = Schema + + /** + * Any schema, including `never`. + * + * @since 3.10.0 + */ + export type All = + | Any + | Schema + | Schema + | Schema + + /** + * Type-level counterpart of `Schema.asSchema` function. + * + * @since 3.10.0 + */ + export type AsSchema = Schema, Encoded, Context> +} + +/** + * The `encodedSchema` function allows you to extract the `Encoded` portion of a + * schema, creating a new schema that conforms to the properties defined in the + * original schema without retaining any refinements or transformations that + * were applied previously. + * + * @since 3.10.0 + */ +export const encodedSchema = (schema: Schema): SchemaClass => make(AST.encodedAST(schema.ast)) + +/** + * The `encodedBoundSchema` function is similar to `encodedSchema` but preserves + * the refinements up to the first transformation point in the original schema. + * + * @since 3.10.0 + */ +export const encodedBoundSchema = (schema: Schema): SchemaClass => + make(AST.encodedBoundAST(schema.ast)) + +/** + * The `typeSchema` function allows you to extract the `Type` portion of a + * schema, creating a new schema that conforms to the properties defined in the + * original schema without considering the initial encoding or transformation + * processes. + * + * @since 3.10.0 + */ +export const typeSchema = (schema: Schema): SchemaClass => make(AST.typeAST(schema.ast)) + +/* c8 ignore start */ +export { + /** + * By default the option `exact` is set to `true`. + * + * @throws `ParseError` + * @category validation + * @since 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: Schema, + options?: ParseOptions +) => { + const encodeUnknown = ParseResult.encodeUnknown(schema, options) + return (u: unknown, overrideOptions?: ParseOptions): Effect.Effect => + ParseResult.mapError(encodeUnknown(u, overrideOptions), ParseResult.parseError) +} + +/** + * @category encoding + * @since 3.10.0 + */ +export const encodeUnknownEither = ( + schema: Schema, + options?: ParseOptions +) => { + const encodeUnknownEither = ParseResult.encodeUnknownEither(schema, options) + return (u: unknown, overrideOptions?: ParseOptions): either_.Either => + either_.mapLeft(encodeUnknownEither(u, overrideOptions), ParseResult.parseError) +} + +/** + * @category encoding + * @since 3.10.0 + */ +export const encodeUnknownPromise = ( + schema: Schema, + options?: ParseOptions +) => { + const parser = encodeUnknown(schema, options) + return (u: unknown, overrideOptions?: ParseOptions): Promise => Effect.runPromise(parser(u, overrideOptions)) +} + +/** + * @category encoding + * @since 3.10.0 + */ +export const encode: ( + schema: Schema, + options?: ParseOptions +) => (a: A, overrideOptions?: ParseOptions) => Effect.Effect = encodeUnknown + +/** + * @category encoding + * @since 3.10.0 + */ +export const encodeEither: ( + schema: Schema, + options?: ParseOptions +) => (a: A, overrideOptions?: ParseOptions) => either_.Either = encodeUnknownEither + +/** + * @category encoding + * @since 3.10.0 + */ +export const encodePromise: ( + schema: Schema, + options?: ParseOptions +) => (a: A, overrideOptions?: ParseOptions) => Promise = encodeUnknownPromise + +/** + * @category decoding + * @since 3.10.0 + */ +export const decodeUnknown = ( + schema: Schema, + options?: ParseOptions +) => { + const decodeUnknown = ParseResult.decodeUnknown(schema, options) + return (u: unknown, overrideOptions?: ParseOptions): Effect.Effect => + ParseResult.mapError(decodeUnknown(u, overrideOptions), ParseResult.parseError) +} + +/** + * @category decoding + * @since 3.10.0 + */ +export const decodeUnknownEither = ( + schema: Schema, + options?: ParseOptions +) => { + const decodeUnknownEither = ParseResult.decodeUnknownEither(schema, options) + return (u: unknown, overrideOptions?: ParseOptions): either_.Either => + either_.mapLeft(decodeUnknownEither(u, overrideOptions), ParseResult.parseError) +} + +/** + * @category decoding + * @since 3.10.0 + */ +export const decodeUnknownPromise = ( + schema: Schema, + options?: ParseOptions +) => { + const parser = decodeUnknown(schema, options) + return (u: unknown, overrideOptions?: ParseOptions): Promise => Effect.runPromise(parser(u, overrideOptions)) +} + +/** + * @category decoding + * @since 3.10.0 + */ +export const decode: ( + schema: Schema, + options?: ParseOptions +) => (i: I, overrideOptions?: ParseOptions) => Effect.Effect = decodeUnknown + +/** + * @category decoding + * @since 3.10.0 + */ +export const decodeEither: ( + schema: Schema, + options?: ParseOptions +) => (i: I, overrideOptions?: ParseOptions) => either_.Either = decodeUnknownEither + +/** + * @category decoding + * @since 3.10.0 + */ +export const decodePromise: ( + schema: Schema, + options?: ParseOptions +) => (i: I, overrideOptions?: ParseOptions) => Promise = decodeUnknownPromise + +/** + * @category validation + * @since 3.10.0 + */ +export const validate = ( + schema: Schema, + options?: ParseOptions +) => { + const validate = ParseResult.validate(schema, options) + return (u: unknown, overrideOptions?: ParseOptions): Effect.Effect => + ParseResult.mapError(validate(u, overrideOptions), ParseResult.parseError) +} + +/** + * @category validation + * @since 3.10.0 + */ +export const validateEither = ( + schema: Schema, + options?: ParseOptions +) => { + const validateEither = ParseResult.validateEither(schema, options) + return (u: unknown, overrideOptions?: ParseOptions): either_.Either => + either_.mapLeft(validateEither(u, overrideOptions), ParseResult.parseError) +} + +/** + * @category validation + * @since 3.10.0 + */ +export const validatePromise = ( + schema: Schema, + options?: ParseOptions +) => { + const parser = validate(schema, options) + return (u: unknown, overrideOptions?: ParseOptions): Promise => Effect.runPromise(parser(u, overrideOptions)) +} + +/** + * Tests if a value is a `Schema`. + * + * @category guards + * @since 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> + extends AnnotableClass, Literals[number]> +{ + readonly literals: Readonly +} + +const getDefaultLiteralAST = >( + literals: Literals +) => + AST.isMembers(literals) + ? AST.Union.make(AST.mapMembers(literals, (literal) => new AST.Literal(literal))) + : new AST.Literal(literals[0]) + +const makeLiteralClass = >( + literals: Literals, + ast: AST.AST = getDefaultLiteralAST(literals) +): Literal => + class LiteralClass extends make(ast) { + static override annotations(annotations: Annotations.Schema): Literal { + return makeLiteralClass(this.literals, mergeSchemaAnnotations(this.ast, annotations)) + } + static literals = [...literals] as Literals + } + +/** + * @category constructors + * @since 3.10.0 + */ +export function Literal>( + ...literals: Literals +): Literal +export function Literal(): Never +export function Literal>( + ...literals: Literals +): Schema +export function Literal>( + ...literals: Literals +): Schema | Never { + return array_.isNonEmptyReadonlyArray(literals) ? makeLiteralClass(literals) : Never +} + +/** + * Creates a new `Schema` from a literal schema. + * + * @example + * import * as S from "effect/Schema" + * import { Either } from "effect" + * + * const schema = S.Literal("a", "b", "c").pipe(S.pickLiteral("a", "b")) + * + * assert.deepStrictEqual(S.decodeSync(schema)("a"), "a") + * assert.deepStrictEqual(S.decodeSync(schema)("b"), "b") + * assert.strictEqual(Either.isLeft(S.decodeUnknownEither(schema)("c")), true) + * + * @category constructors + * @since 3.10.0 + */ +export const pickLiteral = + >(...literals: L) => + (_schema: Schema): Literal<[...L]> => Literal(...literals) + +/** + * @category constructors + * @since 3.10.0 + */ +export const UniqueSymbolFromSelf = (symbol: S): SchemaClass => make(new AST.UniqueSymbol(symbol)) + +/** + * @category api interface + * @since 3.10.0 + */ +export interface Enums extends AnnotableClass, A[keyof A]> { + readonly enums: A +} + +/** + * @since 3.10.0 + */ +export type EnumsDefinition = { [x: string]: string | number } + +const getDefaultEnumsAST = (enums: A) => + new AST.Enums( + Object.keys(enums).filter( + (key) => typeof enums[enums[key]] !== "number" + ).map((key) => [key, enums[key]]) + ) + +const makeEnumsClass = ( + enums: A, + ast: AST.AST = getDefaultEnumsAST(enums) +): Enums => + class EnumsClass extends make(ast) { + static override annotations(annotations: Annotations.Schema) { + return makeEnumsClass(this.enums, mergeSchemaAnnotations(this.ast, annotations)) + } + + static enums = { ...enums } + } + +/** + * @category constructors + * @since 3.10.0 + */ +export const Enums = (enums: A): Enums => makeEnumsClass(enums) + +type Join = Params extends [infer Head, ...infer Tail] ? + `${(Head extends Schema ? A : Head) & (AST.LiteralValue)}${Join}` + : "" + +/** + * @category API interface + * @since 3.10.0 + */ +export interface TemplateLiteral extends SchemaClass {} + +type TemplateLiteralParameter = Schema.AnyNoContext | AST.LiteralValue + +/** + * @category template literal + * @since 3.10.0 + */ +export const TemplateLiteral = >( + ...[head, ...tail]: Params +): TemplateLiteral> => { + let astOrs: ReadonlyArray = getTemplateLiterals( + getTemplateLiteralParameterAST(head) + ) + for (const span of tail) { + astOrs = array_.flatMap( + astOrs, + (a) => getTemplateLiterals(getTemplateLiteralParameterAST(span)).map((b) => combineTemplateLiterals(a, b)) + ) + } + return make(AST.Union.make(astOrs.map((astOr) => Predicate.isString(astOr) ? new AST.Literal(astOr) : astOr))) +} + +const getTemplateLiteralParameterAST = (span: TemplateLiteralParameter): AST.AST => + isSchema(span) ? span.ast : new AST.Literal(String(span)) + +const combineTemplateLiterals = ( + a: AST.TemplateLiteral | string, + b: AST.TemplateLiteral | string +): AST.TemplateLiteral | string => { + if (Predicate.isString(a)) { + return Predicate.isString(b) ? + a + b : + new AST.TemplateLiteral(a + b.head, b.spans) + } + if (Predicate.isString(b)) { + return new AST.TemplateLiteral( + a.head, + array_.modifyNonEmptyLast( + a.spans, + (span) => new AST.TemplateLiteralSpan(span.type, span.literal + b) + ) + ) + } + return new AST.TemplateLiteral( + a.head, + array_.appendAll( + array_.modifyNonEmptyLast( + a.spans, + (span) => new AST.TemplateLiteralSpan(span.type, span.literal + String(b.head)) + ), + b.spans + ) + ) +} + +const getTemplateLiterals = ( + ast: AST.AST +): ReadonlyArray => { + switch (ast._tag) { + case "Literal": + return [String(ast.literal)] + case "NumberKeyword": + case "StringKeyword": + return [new AST.TemplateLiteral("", [new AST.TemplateLiteralSpan(ast, "")])] + case "Union": + return array_.flatMap(ast.types, getTemplateLiterals) + } + throw new Error(errors_.getSchemaUnsupportedLiteralSpanErrorMessage(ast)) +} + +type TemplateLiteralParserParameters = Schema.Any | AST.LiteralValue + +type TemplateLiteralParserParametersType = T extends [infer Head, ...infer Tail] ? + readonly [Head extends Schema ? A : Head, ...TemplateLiteralParserParametersType] + : [] + +type TemplateLiteralParserParametersEncoded = T extends [infer Head, ...infer Tail] ? `${ + & (Head extends Schema ? I : Head) + & (AST.LiteralValue)}${TemplateLiteralParserParametersEncoded}` + : "" + +/** + * @category API interface + * @since 3.10.0 + */ +export interface TemplateLiteralParser> + extends + Schema< + TemplateLiteralParserParametersType, + TemplateLiteralParserParametersEncoded, + Schema.Context + > +{ + readonly params: Params +} + +/** + * @category template literal + * @since 3.10.0 + */ +export const TemplateLiteralParser = >( + ...params: Params +): TemplateLiteralParser => { + const encodedSchemas: Array = [] + const typeSchemas: Array = [] + const numbers: Array = [] + for (let i = 0; i < params.length; i++) { + const p = params[i] + if (isSchema(p)) { + const encoded = encodedSchema(p) + if (AST.isNumberKeyword(encoded.ast)) { + numbers.push(i) + } + encodedSchemas.push(encoded) + typeSchemas.push(p) + } else { + const literal = Literal(p) + encodedSchemas.push(literal) + typeSchemas.push(literal) + } + } + const from = TemplateLiteral(...encodedSchemas as any) + const re = AST.getTemplateLiteralCapturingRegExp(from.ast as AST.TemplateLiteral) + return class TemplateLiteralParserClass extends transform(from, Tuple(...typeSchemas), { + strict: false, + decode: (s) => { + const out: Array = re.exec(s)!.slice(1, params.length + 1) + for (let i = 0; i < numbers.length; i++) { + const index = numbers[i] + out[index] = Number(out[index]) + } + return out + }, + encode: (tuple) => tuple.join("") + }) { + static params = params.slice() + } as any +} + +const declareConstructor = < + const TypeParameters extends ReadonlyArray, + I, + A +>( + typeParameters: TypeParameters, + options: { + readonly decode: ( + ...typeParameters: { + readonly [K in keyof TypeParameters]: Schema< + Schema.Type, + Schema.Encoded, + never + > + } + ) => ( + input: unknown, + options: ParseOptions, + ast: AST.Declaration + ) => Effect.Effect + readonly encode: ( + ...typeParameters: { + readonly [K in keyof TypeParameters]: Schema< + Schema.Type, + Schema.Encoded, + never + > + } + ) => ( + input: unknown, + options: ParseOptions, + ast: AST.Declaration + ) => Effect.Effect + }, + annotations?: Annotations.Schema +): SchemaClass> => + make( + new AST.Declaration( + typeParameters.map((tp) => tp.ast), + (...typeParameters) => options.decode(...typeParameters.map(make) as any), + (...typeParameters) => options.encode(...typeParameters.map(make) as any), + toASTAnnotations(annotations) + ) + ) + +const declarePrimitive = ( + is: (input: unknown) => input is A, + annotations?: Annotations.Schema +): SchemaClass => { + const decodeUnknown = () => (input: unknown, _: ParseOptions, ast: AST.Declaration) => + is(input) ? ParseResult.succeed(input) : ParseResult.fail(new ParseResult.Type(ast, input)) + const encodeUnknown = decodeUnknown + return make(new AST.Declaration([], decodeUnknown, encodeUnknown, toASTAnnotations(annotations))) +} + +/** + * The constraint `R extends Schema.Context` enforces dependencies solely from `typeParameters`. + * This ensures that when you call `Schema.to` or `Schema.from`, you receive a schema with a `never` context. + * + * @category constructors + * @since 3.10.0 + */ +export const declare: { + ( + is: (input: unknown) => input is A, + annotations?: Annotations.Schema + ): SchemaClass + , I, A>( + typeParameters: P, + options: { + readonly decode: ( + ...typeParameters: { readonly [K in keyof P]: Schema, Schema.Encoded, never> } + ) => ( + input: unknown, + options: ParseOptions, + ast: AST.Declaration + ) => Effect.Effect + readonly encode: ( + ...typeParameters: { readonly [K in keyof P]: Schema, Schema.Encoded, never> } + ) => ( + input: unknown, + options: ParseOptions, + ast: AST.Declaration + ) => Effect.Effect + }, + annotations?: Annotations.Schema }> + ): SchemaClass> +} = function() { + if (Array.isArray(arguments[0])) { + const typeParameters = arguments[0] + const options = arguments[1] + const annotations = arguments[2] + return declareConstructor(typeParameters, options, annotations) + } + const is = arguments[0] + const annotations = arguments[1] + return declarePrimitive(is, annotations) +} as any + +/** + * @category type id + * @since 3.10.0 + */ +export const BrandTypeId: unique symbol = Symbol.for("effect/Schema/TypeId/Brand") + +/** + * @category constructors + * @since 3.10.0 + */ +export const fromBrand = , A extends Brand.Unbranded>( + constructor: Brand.Constructor, + annotations?: Annotations.Filter +) => +(self: Schema): BrandSchema => + makeBrandClass, string | symbol>( + new AST.Refinement( + self.ast, + function predicate(a: A, _: ParseOptions, ast: AST.AST): option_.Option { + const either = constructor.either(a) + return either_.isLeft(either) ? + option_.some(new ParseResult.Type(ast, a, either.left.map((v) => v.message).join(", "))) : + option_.none() + }, + toASTAnnotations({ typeId: { id: BrandTypeId, annotation: { constructor } }, ...annotations }) + ) + ) + +/** + * @category type id + * @since 3.10.0 + */ +export const InstanceOfTypeId: unique symbol = Symbol.for("effect/Schema/TypeId/InstanceOf") + +/** + * @category api interface + * @since 3.10.0 + */ +export interface instanceOf extends AnnotableClass, A> {} + +/** + * @category constructors + * @since 3.10.0 + */ +export const instanceOf = any>( + constructor: A, + annotations?: Annotations.Schema> +): instanceOf> => + declare( + (u): u is InstanceType => u instanceof constructor, + { + title: constructor.name, + description: `an instance of ${constructor.name}`, + pretty: (): pretty_.Pretty> => String, + typeId: { id: InstanceOfTypeId, annotation: { constructor } }, + ...annotations + } + ) + +/** + * @category primitives + * @since 3.10.0 + */ +export class Undefined extends make(AST.undefinedKeyword) {} + +/** + * @category primitives + * @since 3.10.0 + */ +export class Void extends make(AST.voidKeyword) {} + +/** + * @category primitives + * @since 3.10.0 + */ +export class Null extends make(AST.null) {} + +/** + * @category primitives + * @since 3.10.0 + */ +export class Never extends make(AST.neverKeyword) {} + +/** + * @category primitives + * @since 3.10.0 + */ +export class Unknown extends make(AST.unknownKeyword) {} + +/** + * @category primitives + * @since 3.10.0 + */ +export class Any extends make(AST.anyKeyword) {} + +/** + * @category primitives + * @since 3.10.0 + */ +export class BigIntFromSelf extends make(AST.bigIntKeyword) {} + +/** + * @category primitives + * @since 3.10.0 + */ +export class SymbolFromSelf extends make(AST.symbolKeyword) {} + +/** @ignore */ +class String$ extends make(AST.stringKeyword) {} + +/** @ignore */ +class Number$ extends make(AST.numberKeyword) {} + +/** @ignore */ +class Boolean$ extends make(AST.booleanKeyword) {} + +/** @ignore */ +class Object$ extends make(AST.objectKeyword) {} + +export { + /** + * @category primitives + * @since 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> extends + AnnotableClass< + Union, + Schema.Type, + Schema.Encoded, + Schema.Context + > +{ + readonly members: Readonly + annotations(annotations: Annotations.Schema>): Union +} + +const getDefaultUnionAST = >(members: Members): AST.AST => + AST.Union.make(members.map((m) => m.ast)) + +const makeUnionClass = >( + members: Members, + ast: AST.AST = getDefaultUnionAST(members) +): Union => + class UnionClass + extends make, Schema.Encoded, Schema.Context>(ast) + { + static override annotations(annotations: Annotations.Schema>): Union { + return makeUnionClass(this.members, mergeSchemaAnnotations(this.ast, annotations)) + } + + static members = [...members] + } + +/** + * @category combinators + * @since 3.10.0 + */ +export function Union>(...members: Members): Union +export function Union(member: Member): Member +export function Union(): typeof Never +export function Union>( + ...members: Members +): Schema, Schema.Encoded, Schema.Context> +export function Union>( + ...members: Members +) { + return AST.isMembers(members) + ? makeUnionClass(members) + : array_.isNonEmptyReadonlyArray(members) + ? members[0] + : Never +} + +/** + * @category api interface + * @since 3.10.0 + */ +export interface NullOr extends Union<[S, typeof Null]> { + annotations(annotations: Annotations.Schema | null>): NullOr +} + +/** + * @category combinators + * @since 3.10.0 + */ +export const NullOr = (self: S): NullOr => Union(self, Null) + +/** + * @category api interface + * @since 3.10.0 + */ +export interface UndefinedOr extends Union<[S, typeof Undefined]> { + annotations(annotations: Annotations.Schema | undefined>): UndefinedOr +} + +/** + * @category combinators + * @since 3.10.0 + */ +export const UndefinedOr = (self: S): UndefinedOr => Union(self, Undefined) + +/** + * @category api interface + * @since 3.10.0 + */ +export interface NullishOr extends Union<[S, typeof Null, typeof Undefined]> { + annotations(annotations: Annotations.Schema | null | undefined>): NullishOr +} + +/** + * @category combinators + * @since 3.10.0 + */ +export const NullishOr = (self: S): NullishOr => Union(self, Null, Undefined) + +/** + * @category combinators + * @since 3.10.0 + */ +export const keyof = (self: Schema): SchemaClass => make(AST.keyof(self.ast)) + +/** + * @since 3.10.0 + */ +export declare namespace Element { + /** + * @since 3.10.0 + */ + export interface Annotations extends Annotations.Doc { + readonly missingMessage?: AST.MissingMessageAnnotation + } + + /** + * @since 3.10.0 + */ + export type Token = "" | "?" +} + +/** + * @category API interface + * @since 3.10.0 + */ +export interface Element + extends Schema.Variance, Schema.Encoded, Schema.Context> +{ + readonly _Token: Token + readonly ast: AST.OptionalType + readonly from: S + annotations(annotations: Element.Annotations>): Element +} + +/** + * @since 3.10.0 + */ +export const element = (self: S): Element => + new ElementImpl(new AST.OptionalType(self.ast, false), self) + +/** + * @since 3.10.0 + */ +export const optionalElement = (self: S): Element => + new ElementImpl(new AST.OptionalType(self.ast, true), self) + +class ElementImpl implements Element { + readonly [TypeId]!: Schema.Variance, Schema.Encoded, Schema.Context>[TypeId] + readonly _Token!: Token + constructor( + readonly ast: AST.OptionalType, + readonly from: S + ) {} + annotations( + annotations: Annotations.Schema> + ): ElementImpl { + return new ElementImpl( + new AST.OptionalType( + this.ast.type, + this.ast.isOptional, + { ...this.ast.annotations, ...toASTAnnotations(annotations) } + ), + this.from + ) + } + toString() { + return `${this.ast.type}${this.ast.isOptional ? "?" : ""}` + } +} + +/** + * @since 3.10.0 + */ +export declare namespace TupleType { + type ElementsType< + Elements, + Out extends ReadonlyArray = readonly [] + > = Elements extends readonly [infer Head, ...infer Tail] ? + Head extends Element ? ElementsType?]> + : ElementsType]> + : Out + + type ElementsEncoded< + Elements, + Out extends ReadonlyArray = readonly [] + > = Elements extends readonly [infer Head, ...infer Tail] ? + Head extends Element ? ElementsEncoded?]> + : ElementsEncoded]> + : Out + + /** + * @since 3.10.0 + */ + export type Elements = ReadonlyArray> + + /** + * @since 3.10.0 + */ + export type Rest = ReadonlyArray> + + /** + * @since 3.10.0 + */ + export type Type = Rest extends + [infer Head, ...infer Tail] ? Readonly<[ + ...ElementsType, + ...ReadonlyArray>, + ...{ readonly [K in keyof Tail]: Schema.Type } + ]> : + ElementsType + + /** + * @since 3.10.0 + */ + export type Encoded = Rest extends + [infer Head, ...infer Tail] ? Readonly<[ + ...ElementsEncoded, + ...ReadonlyArray>, + ...{ readonly [K in keyof Tail]: Schema.Encoded } + ]> : + ElementsEncoded +} + +/** + * @category api interface + * @since 3.10.0 + */ +export interface TupleType extends + AnnotableClass< + TupleType, + TupleType.Type, + TupleType.Encoded, + Schema.Context | Schema.Context + > +{ + readonly elements: Readonly + readonly rest: Readonly +} + +const getDefaultTupleTypeAST = ( + elements: Elements, + rest: Rest +) => + new AST.TupleType( + elements.map((el) => isSchema(el) ? new AST.OptionalType(el.ast, false) : el.ast), + rest.map((el) => isSchema(el) ? new AST.Type(el.ast) : el.ast), + true + ) + +const makeTupleTypeClass = ( + elements: Elements, + rest: Rest, + ast: AST.AST = getDefaultTupleTypeAST(elements, rest) +) => + class TupleTypeClass extends make< + TupleType.Type, + TupleType.Encoded, + Schema.Context | Schema.Context + >(ast) { + static override annotations( + annotations: Annotations.Schema> + ): TupleType { + return makeTupleTypeClass(this.elements, this.rest, mergeSchemaAnnotations(this.ast, annotations)) + } + + static elements = [...elements] as any as Elements + + static rest = [...rest] as any as Rest + } + +/** + * @category api interface + * @since 3.10.0 + */ +export interface Tuple extends TupleType { + annotations(annotations: Annotations.Schema>): Tuple +} + +/** + * @category constructors + * @since 3.10.0 + */ +export function Tuple< + const Elements extends TupleType.Elements, + Rest extends array_.NonEmptyReadonlyArray +>(elements: Elements, ...rest: Rest): TupleType +export function Tuple(...elements: Elements): Tuple +export function Tuple(...args: ReadonlyArray): any { + return Array.isArray(args[0]) + ? makeTupleTypeClass(args[0], args.slice(1)) + : makeTupleTypeClass(args, []) +} + +/** + * @category api interface + * @since 3.10.0 + */ +export interface Array$ extends TupleType<[], [Value]> { + readonly value: Value + annotations(annotations: Annotations.Schema>): Array$ +} + +const makeArrayClass = (value: Value, ast?: AST.AST): Array$ => + class ArrayClass extends makeTupleTypeClass<[], [Value]>([], [value], ast) { + static override annotations(annotations: Annotations.Schema>) { + return makeArrayClass(this.value, mergeSchemaAnnotations(this.ast, annotations)) + } + + static value = value + } + +const Array$ = (value: Value): Array$ => makeArrayClass(value) + +export { + /** + * @category constructors + * @since 3.10.0 + */ + Array$ as Array +} + +/** + * @category api interface + * @since 3.10.0 + */ +export interface NonEmptyArray extends TupleType<[Value], [Value]> { + readonly value: Value + annotations(annotations: Annotations.Schema>): NonEmptyArray +} + +const makeNonEmptyArrayClass = (value: Value, ast?: AST.AST): NonEmptyArray => + class NonEmptyArrayClass extends makeTupleTypeClass<[Value], [Value]>([value], [value], ast) { + static override annotations(annotations: Annotations.Schema>) { + return makeNonEmptyArrayClass(this.value, mergeSchemaAnnotations(this.ast, annotations)) + } + + static value = value + } + +/** + * @category constructors + * @since 3.10.0 + */ +export const NonEmptyArray = (value: Value): NonEmptyArray => + makeNonEmptyArrayClass(value) + +/** + * @category api interface + * @since 3.10.0 + */ +export interface ArrayEnsure extends + AnnotableClass< + ArrayEnsure, + ReadonlyArray>, + Schema.Encoded | ReadonlyArray>, + Schema.Context + > +{} + +/** + * @category constructors + * @since 3.10.0 + */ +export const ArrayEnsure = (value: Value): ArrayEnsure => { + const value_ = asSchema(value) + return class ArrayEnsureClass extends transform(Union(value_, Array$(value_)), Array$(typeSchema(value_)), { + strict: true, + decode: array_.ensure, + encode: (arr) => arr.length === 1 ? arr[0] : arr + }) {} +} + +/** + * @category api interface + * @since 3.10.0 + */ +export interface NonEmptyArrayEnsure extends + AnnotableClass< + NonEmptyArrayEnsure, + array_.NonEmptyReadonlyArray>, + Schema.Encoded | array_.NonEmptyReadonlyArray>, + Schema.Context + > +{} + +/** + * @category constructors + * @since 3.10.0 + */ +export const NonEmptyArrayEnsure = (value: Value): NonEmptyArrayEnsure => { + const value_ = asSchema(value) + return class NonEmptyArrayEnsureClass + extends transform(Union(value_, NonEmptyArray(value_)), NonEmptyArray(typeSchema(value_)), { + strict: true, + decode: array_.ensure as any, + encode: (arr) => arr.length === 1 ? arr[0] : arr + }) + {} +} + +/** + * @since 3.10.0 + */ +export declare namespace PropertySignature { + /** + * @since 3.10.0 + */ + export type Token = "?:" | ":" + + /** + * @since 3.10.0 + */ + export type Any = PropertySignature< + Token, + any, + Key, + Token, + any, + boolean, + unknown + > + + /** + * @since 3.10.0 + */ + export type All = + | Any + | PropertySignature + | PropertySignature + | PropertySignature + + /** + * @since 3.10.0 + */ + export type AST = + | PropertySignatureDeclaration + | PropertySignatureTransformation + + /** + * @since 3.10.0 + */ + export interface Annotations extends Annotations.Doc { + readonly missingMessage?: AST.MissingMessageAnnotation + } +} + +const formatPropertySignatureToken = (isOptional: boolean): string => isOptional ? "\"?:\"" : "\":\"" + +/** + * @category PropertySignature + * @since 3.10.0 + */ +export class PropertySignatureDeclaration extends AST.OptionalType { + /** + * @since 3.10.0 + */ + readonly _tag = "PropertySignatureDeclaration" + constructor( + type: AST.AST, + isOptional: boolean, + readonly isReadonly: boolean, + annotations: AST.Annotations, + readonly defaultValue: (() => unknown) | undefined + ) { + super(type, isOptional, annotations) + } + /** + * @since 3.10.0 + */ + toString() { + const token = formatPropertySignatureToken(this.isOptional) + const type = String(this.type) + return `PropertySignature<${token}, ${type}, never, ${token}, ${type}>` + } +} + +/** + * @category PropertySignature + * @since 3.10.0 + */ +export class FromPropertySignature extends AST.OptionalType { + constructor( + type: AST.AST, + isOptional: boolean, + readonly isReadonly: boolean, + annotations: AST.Annotations, + readonly fromKey?: PropertyKey | undefined + ) { + super(type, isOptional, annotations) + } +} + +/** + * @category PropertySignature + * @since 3.10.0 + */ +export class ToPropertySignature extends AST.OptionalType { + constructor( + type: AST.AST, + isOptional: boolean, + readonly isReadonly: boolean, + annotations: AST.Annotations, + readonly defaultValue: (() => unknown) | undefined + ) { + super(type, isOptional, annotations) + } +} + +const formatPropertyKey = (p: PropertyKey | undefined): string => { + if (p === undefined) { + return "never" + } + if (Predicate.isString(p)) { + return JSON.stringify(p) + } + return String(p) +} + +/** + * @category PropertySignature + * @since 3.10.0 + */ +export class PropertySignatureTransformation { + /** + * @since 3.10.0 + */ + readonly _tag = "PropertySignatureTransformation" + constructor( + readonly from: FromPropertySignature, + readonly to: ToPropertySignature, + readonly decode: AST.PropertySignatureTransformation["decode"], + readonly encode: AST.PropertySignatureTransformation["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: PropertySignature.AST, + annotations: AST.Annotations +): PropertySignature.AST => { + switch (ast._tag) { + case "PropertySignatureDeclaration": { + return new PropertySignatureDeclaration( + ast.type, + ast.isOptional, + ast.isReadonly, + { ...ast.annotations, ...annotations }, + ast.defaultValue + ) + } + case "PropertySignatureTransformation": { + return new PropertySignatureTransformation( + new FromPropertySignature( + ast.from.type, + ast.from.isOptional, + ast.from.isReadonly, + ast.from.annotations + ), + new ToPropertySignature(ast.to.type, ast.to.isOptional, ast.to.isReadonly, { + ...ast.to.annotations, + ...annotations + }, ast.to.defaultValue), + ast.decode, + ast.encode + ) + } + } +} + +/** + * @since 3.10.0 + * @category symbol + */ +export const PropertySignatureTypeId: unique symbol = Symbol.for("effect/Schema/PropertySignature") + +/** + * @since 3.10.0 + * @category symbol + */ +export type PropertySignatureTypeId = typeof PropertySignatureTypeId + +/** + * @since 3.10.0 + * @category guards + */ +export const isPropertySignature = (u: unknown): u is PropertySignature.All => + Predicate.hasProperty(u, PropertySignatureTypeId) + +/** + * @category PropertySignature + * @since 3.10.0 + */ +export interface PropertySignature< + TypeToken extends PropertySignature.Token, + Type, + Key extends PropertyKey, + EncodedToken extends PropertySignature.Token, + Encoded, + HasDefault extends boolean = false, + R = never +> extends Schema.Variance, Pipeable { + readonly [PropertySignatureTypeId]: null + readonly _TypeToken: TypeToken + readonly _EncodedToken: EncodedToken + readonly _HasDefault: HasDefault + readonly _Key: Key + readonly ast: PropertySignature.AST + + annotations( + annotations: PropertySignature.Annotations + ): PropertySignature +} + +class PropertySignatureImpl< + TypeToken extends PropertySignature.Token, + Type, + Key extends PropertyKey, + EncodedToken extends PropertySignature.Token, + Encoded, + HasDefault extends boolean = false, + R = never +> implements PropertySignature { + readonly [TypeId]!: Schema.Variance[TypeId] + readonly [PropertySignatureTypeId] = null + readonly _TypeToken!: TypeToken + readonly _Key!: Key + readonly _EncodedToken!: EncodedToken + readonly _HasDefault!: HasDefault + + constructor( + readonly ast: PropertySignature.AST + ) {} + + pipe() { + return pipeArguments(this, arguments) + } + + annotations( + annotations: PropertySignature.Annotations + ): PropertySignature { + return new PropertySignatureImpl(mergeSignatureAnnotations(this.ast, toASTAnnotations(annotations))) + } + + toString() { + return String(this.ast) + } +} + +/** + * @category PropertySignature + * @since 3.10.0 + */ +export const makePropertySignature = < + TypeToken extends PropertySignature.Token, + Type, + Key extends PropertyKey, + EncodedToken extends PropertySignature.Token, + Encoded, + HasDefault extends boolean = false, + R = never +>(ast: PropertySignature.AST) => + new PropertySignatureImpl(ast) + +class PropertySignatureWithFromImpl< + From extends Schema.All, + TypeToken extends PropertySignature.Token, + Type, + Key extends PropertyKey, + EncodedToken extends PropertySignature.Token, + Encoded, + HasDefault extends boolean = false, + R = never +> extends PropertySignatureImpl { + constructor(ast: PropertySignature.AST, readonly from: From) { + super(ast) + } + annotations( + annotations: PropertySignature.Annotations + ): PropertySignatureWithFromImpl { + return new PropertySignatureWithFromImpl( + mergeSignatureAnnotations(this.ast, toASTAnnotations(annotations)), + this.from + ) + } +} + +/** + * @category API interface + * @since 1.0.0 + */ +export interface propertySignature + extends PropertySignature<":", Schema.Type, never, ":", Schema.Encoded, false, Schema.Context> +{ + readonly from: S + annotations(annotations: PropertySignature.Annotations>): propertySignature +} + +/** + * Lifts a `Schema` into a `PropertySignature`. + * + * @category PropertySignature + * @since 3.10.0 + */ +export const propertySignature = ( + self: S +): propertySignature => + new PropertySignatureWithFromImpl( + new PropertySignatureDeclaration(self.ast, false, true, {}, undefined), + self + ) + +/** + * Enhances a property signature with a default constructor value. + * + * @category PropertySignature + * @since 3.10.0 + */ +export const withConstructorDefault: { + (defaultValue: () => Types.NoInfer): < + TypeToken extends PropertySignature.Token, + Key extends PropertyKey, + EncodedToken extends PropertySignature.Token, + Encoded, + R + >( + self: PropertySignature + ) => PropertySignature + < + TypeToken extends PropertySignature.Token, + Type, + Key extends PropertyKey, + EncodedToken extends PropertySignature.Token, + Encoded, + R + >( + self: PropertySignature, + defaultValue: () => Types.NoInfer + ): PropertySignature +} = dual(2, < + TypeToken extends PropertySignature.Token, + Type, + Key extends PropertyKey, + EncodedToken extends PropertySignature.Token, + Encoded, + R +>( + self: PropertySignature, + defaultValue: () => Types.NoInfer +): PropertySignature => { + const ast = self.ast + switch (ast._tag) { + case "PropertySignatureDeclaration": + return makePropertySignature( + new PropertySignatureDeclaration(ast.type, ast.isOptional, ast.isReadonly, ast.annotations, defaultValue) + ) + case "PropertySignatureTransformation": + return makePropertySignature( + new PropertySignatureTransformation( + ast.from, + new ToPropertySignature(ast.to.type, ast.to.isOptional, ast.to.isReadonly, ast.to.annotations, defaultValue), + ast.decode, + ast.encode + ) + ) + } +}) + +const applyDefaultValue = (o: option_.Option, defaultValue: () => A) => + option_.match(o, { + onNone: () => option_.some(defaultValue()), + onSome: (value) => option_.some(value === undefined ? defaultValue() : value) + }) + +/** + * Enhances a property signature with a default decoding value. + * + * @category PropertySignature + * @since 3.10.0 + */ +export const withDecodingDefault: { + (defaultValue: () => Types.NoInfer): < + Key extends PropertyKey, + Encoded, + HasDefault extends boolean, + R + >( + self: PropertySignature<"?:", Type, Key, "?:", Encoded, HasDefault, R> + ) => PropertySignature<":", Exclude, Key, "?:", Encoded, HasDefault, R> + < + Type, + Key extends PropertyKey, + Encoded, + HasDefault extends boolean, + R + >( + self: PropertySignature<"?:", Type, Key, "?:", Encoded, HasDefault, R>, + defaultValue: () => Types.NoInfer + ): PropertySignature<":", Exclude, Key, "?:", Encoded, HasDefault, R> +} = dual(2, < + Type, + Key extends PropertyKey, + Encoded, + R +>( + self: PropertySignature<"?:", Type, Key, "?:", Encoded, boolean, R>, + defaultValue: () => Types.NoInfer +): PropertySignature<":", Exclude, Key, "?:", Encoded, true, R> => { + const ast = self.ast + switch (ast._tag) { + case "PropertySignatureDeclaration": + return makePropertySignature( + new PropertySignatureTransformation( + ast, + new ToPropertySignature(AST.typeAST(ast.type), false, true, {}, undefined), + (o) => applyDefaultValue(o, defaultValue), + identity + ) + ) + case "PropertySignatureTransformation": + return makePropertySignature( + new PropertySignatureTransformation( + ast.from, + new ToPropertySignature(ast.to.type, false, ast.to.isReadonly, ast.to.annotations, ast.to.defaultValue), + (o) => applyDefaultValue(ast.decode(o), defaultValue), + ast.encode + ) + ) + } +}) + +/** + * Enhances a property signature with a default decoding value and a default constructor value. + * + * @category PropertySignature + * @since 3.10.0 + */ +export const withDefaults: { + (defaults: { + constructor: () => Types.NoInfer> + decoding: () => Types.NoInfer + }): < + Key extends PropertyKey, + Encoded, + R + >( + self: PropertySignature<"?:", Type, Key, "?:", Encoded, boolean, R> + ) => PropertySignature<":", Exclude, Key, "?:", Encoded, true, R> + < + Type, + Key extends PropertyKey, + Encoded, + R + >( + self: PropertySignature<"?:", Type, Key, "?:", Encoded, boolean, R>, + defaults: { + constructor: () => Types.NoInfer> + decoding: () => Types.NoInfer + } + ): PropertySignature<":", Exclude, Key, "?:", Encoded, true, R> +} = dual(2, < + Type, + Key extends PropertyKey, + Encoded, + R +>( + self: PropertySignature<"?:", Type, Key, "?:", Encoded, boolean, R>, + defaults: { + constructor: () => Types.NoInfer> + decoding: () => Types.NoInfer + } +): PropertySignature<":", Exclude, Key, "?:", Encoded, true, R> => + self.pipe(withDecodingDefault(defaults.decoding), withConstructorDefault(defaults.constructor))) + +/** + * Enhances a property signature by specifying a different key for it in the Encoded type. + * + * @category PropertySignature + * @since 3.10.0 + */ +export const fromKey: { + (key: Key): < + TypeToken extends PropertySignature.Token, + Type, + EncodedToken extends PropertySignature.Token, + Encoded, + HasDefault extends boolean, + R + >( + self: PropertySignature + ) => PropertySignature + < + Type, + TypeToken extends PropertySignature.Token, + Encoded, + EncodedToken extends PropertySignature.Token, + HasDefault extends boolean, + R, + Key extends PropertyKey + >( + self: PropertySignature, + key: Key + ): PropertySignature +} = dual(2, < + Type, + TypeToken extends PropertySignature.Token, + Encoded, + EncodedToken extends PropertySignature.Token, + HasDefault extends boolean, + R, + Key extends PropertyKey +>( + self: PropertySignature, + key: Key +): PropertySignature => { + const ast = self.ast + switch (ast._tag) { + case "PropertySignatureDeclaration": { + return makePropertySignature( + new PropertySignatureTransformation( + new FromPropertySignature( + ast.type, + ast.isOptional, + ast.isReadonly, + ast.annotations, + key + ), + new ToPropertySignature(AST.typeAST(ast.type), ast.isOptional, ast.isReadonly, {}, ast.defaultValue), + identity, + identity + ) + ) + } + case "PropertySignatureTransformation": + return makePropertySignature( + new PropertySignatureTransformation( + new FromPropertySignature( + ast.from.type, + ast.from.isOptional, + ast.from.isReadonly, + ast.from.annotations, + key + ), + ast.to, + ast.decode, + ast.encode + ) + ) + } +}) + +/** + * Converts an optional property to a required one through a transformation `Option -> Type`. + * + * - `decode`: `none` as argument means the value is missing in the input. + * - `encode`: `none` as return value means the value will be missing in the output. + * + * @category PropertySignature + * @since 3.10.0 + */ +export const optionalToRequired = ( + from: Schema, + to: Schema, + options: { + readonly decode: (o: option_.Option) => TI + readonly encode: (ti: TI) => option_.Option + } +): PropertySignature<":", TA, never, "?:", FI, false, FR | TR> => + makePropertySignature( + new PropertySignatureTransformation( + new FromPropertySignature(from.ast, true, true, {}, undefined), + new ToPropertySignature(to.ast, false, true, {}, undefined), + (o) => option_.some(options.decode(o)), + option_.flatMap(options.encode) + ) + ) + +/** + * Converts an optional property to a required one through a transformation `Type -> Option`. + * + * - `decode`: `none` as return value means the value will be missing in the output. + * - `encode`: `none` as argument means the value is missing in the input. + * + * @category PropertySignature + * @since 3.10.0 + */ +export const requiredToOptional = ( + from: Schema, + to: Schema, + options: { + readonly decode: (fa: FA) => option_.Option + readonly encode: (o: option_.Option) => FA + } +): PropertySignature<"?:", TA, never, ":", FI, false, FR | TR> => + makePropertySignature( + new PropertySignatureTransformation( + new FromPropertySignature(from.ast, false, true, {}, undefined), + new ToPropertySignature(to.ast, true, true, {}, undefined), + option_.flatMap(options.decode), + (o) => option_.some(options.encode(o)) + ) + ) + +/** + * Converts an optional property to another optional property through a transformation `Option -> Option`. + * + * - `decode`: + * - `none` as argument means the value is missing in the input. + * - `none` as return value means the value will be missing in the output. + * - `encode`: + * - `none` as argument means the value is missing in the input. + * - `none` as return value means the value will be missing in the output. + * + * @category PropertySignature + * @since 3.10.0 + */ +export const optionalToOptional = ( + from: Schema, + to: Schema, + options: { + readonly decode: (o: option_.Option) => option_.Option + readonly encode: (o: option_.Option) => option_.Option + } +): PropertySignature<"?:", TA, never, "?:", FI, false, FR | TR> => + makePropertySignature( + new PropertySignatureTransformation( + new FromPropertySignature(from.ast, true, true, {}, undefined), + new ToPropertySignature(to.ast, true, true, {}, undefined), + options.decode, + options.encode + ) + ) + +/** + * @since 3.10.0 + */ +export type OptionalOptions = { + readonly default?: never + readonly as?: never + readonly exact?: true + readonly nullable?: true +} | { + readonly default: LazyArg + readonly as?: never + readonly exact?: true + readonly nullable?: true +} | { + readonly as: "Option" + readonly default?: never + readonly exact?: never + readonly nullable?: never + readonly onNoneEncoding?: LazyArg> +} | { + readonly as: "Option" + readonly default?: never + readonly exact?: never + readonly nullable: true + readonly onNoneEncoding?: LazyArg> +} | { + readonly as: "Option" + readonly default?: never + readonly exact: true + readonly nullable?: never + readonly onNoneEncoding?: never +} | { + readonly as: "Option" + readonly default?: never + readonly exact: true + readonly nullable: true + readonly onNoneEncoding?: LazyArg> +} | undefined + +/** + * @category api interface + * @since 3.10.0 + */ +export interface optional extends + PropertySignature< + "?:", + Schema.Type | undefined, + never, + "?:", + Schema.Encoded | undefined, + false, + Schema.Context + > +{ + readonly from: S + annotations(annotations: PropertySignature.Annotations | undefined>): optional +} + +/** + * @category api interface + * @since 3.10.0 + */ +export interface optionalWith extends + PropertySignature< + Types.Has extends true ? ":" : "?:", + | (Types.Has extends true ? option_.Option> : Schema.Type) + | (Types.Has extends true ? never : undefined), + never, + "?:", + | Schema.Encoded + | (Types.Has extends true ? null : never) + | (Types.Has extends true ? never : undefined), + Types.Has, + Schema.Context + > +{ + readonly from: S + annotations( + annotations: PropertySignature.Annotations< + | (Types.Has extends true ? option_.Option> : Schema.Type) + | (Types.Has extends true ? never : undefined) + > + ): optionalWith +} + +const optionalPropertySignatureAST = ( + self: Schema, + options?: { + readonly exact?: true + readonly default?: () => A + readonly nullable?: true + readonly as?: "Option" + readonly onNoneEncoding?: () => option_.Option + } +): PropertySignature.AST => { + const isExact = options?.exact + const defaultValue = options?.default + const isNullable = options?.nullable + const asOption = options?.as == "Option" + const asOptionEncode = options?.onNoneEncoding ? option_.orElse(options.onNoneEncoding) : identity + + if (isExact) { + if (defaultValue) { + if (isNullable) { + return withConstructorDefault( + optionalToRequired( + NullOr(self), + typeSchema(self), + { + decode: option_.match({ onNone: defaultValue, onSome: (a) => a === null ? defaultValue() : a }), + encode: option_.some + } + ), + defaultValue + ).ast + } else { + return withConstructorDefault( + optionalToRequired( + self, + typeSchema(self), + { decode: option_.match({ onNone: defaultValue, onSome: identity }), encode: option_.some } + ), + defaultValue + ).ast + } + } else if (asOption) { + if (isNullable) { + return optionalToRequired( + NullOr(self), + OptionFromSelf(typeSchema(self)), + { + decode: option_.filter(Predicate.isNotNull), + encode: asOptionEncode + } + ).ast + } else { + return optionalToRequired( + self, + OptionFromSelf(typeSchema(self)), + { decode: identity, encode: identity } + ).ast + } + } else { + if (isNullable) { + return optionalToOptional( + NullOr(self), + typeSchema(self), + { decode: option_.filter(Predicate.isNotNull), encode: identity } + ).ast + } else { + return new PropertySignatureDeclaration(self.ast, true, true, {}, undefined) + } + } + } else { + if (defaultValue) { + if (isNullable) { + return withConstructorDefault( + optionalToRequired( + NullishOr(self), + typeSchema(self), + { + decode: option_.match({ onNone: defaultValue, onSome: (a) => (a == null ? defaultValue() : a) }), + encode: option_.some + } + ), + defaultValue + ).ast + } else { + return withConstructorDefault( + optionalToRequired( + UndefinedOr(self), + typeSchema(self), + { + decode: option_.match({ onNone: defaultValue, onSome: (a) => (a === undefined ? defaultValue() : a) }), + encode: option_.some + } + ), + defaultValue + ).ast + } + } else if (asOption) { + if (isNullable) { + return optionalToRequired( + NullishOr(self), + OptionFromSelf(typeSchema(self)), + { + decode: option_.filter((a): a is A => a != null), + encode: asOptionEncode + } + ).ast + } else { + return optionalToRequired( + UndefinedOr(self), + OptionFromSelf(typeSchema(self)), + { + decode: option_.filter(Predicate.isNotUndefined), + encode: asOptionEncode + } + ).ast + } + } else { + if (isNullable) { + return optionalToOptional( + NullishOr(self), + UndefinedOr(typeSchema(self)), + { decode: option_.filter(Predicate.isNotNull), encode: identity } + ).ast + } else { + return new PropertySignatureDeclaration(UndefinedOr(self).ast, true, true, {}, undefined) + } + } + } +} + +/** + * @category PropertySignature + * @since 3.10.0 + */ +export const optional = (self: S): optional => { + const ast = self.ast === AST.undefinedKeyword || self.ast === AST.neverKeyword + ? AST.undefinedKeyword + : UndefinedOr(self).ast + return new PropertySignatureWithFromImpl(new PropertySignatureDeclaration(ast, true, true, {}, undefined), self) +} + +/** + * @category PropertySignature + * @since 3.10.0 + */ +export const optionalWith: { + >>( + options: Options + ): (self: S) => optionalWith + >>( + self: S, + options: Options + ): optionalWith +} = dual((args) => isSchema(args[0]), (self, options) => { + return new PropertySignatureWithFromImpl(optionalPropertySignatureAST(self, options), self) +}) + +/** + * @since 3.10.0 + */ +export declare namespace Struct { + /** + * @since 3.10.0 + */ + export type Fields = { + readonly [x: PropertyKey]: + | Schema.All + | PropertySignature.All + } + + type Key = [K] extends [never] ? never : + F[K] extends PropertySignature.All ? [Key] extends [never] ? K : Key : + K + + type EncodedTokenKeys = { + [K in keyof Fields]: Fields[K] extends + | PropertySignature + | PropertySignature + | PropertySignature + | PropertySignature ? K + : never + }[keyof Fields] + + type TypeTokenKeys = { + [K in keyof Fields]: Fields[K] extends OptionalPropertySignature ? K : never + }[keyof Fields] + + type OptionalPropertySignature = + | PropertySignature<"?:", any, PropertyKey, PropertySignature.Token, any, boolean, unknown> + | PropertySignature<"?:", any, PropertyKey, PropertySignature.Token, never, boolean, unknown> + | PropertySignature<"?:", never, PropertyKey, PropertySignature.Token, any, boolean, unknown> + | PropertySignature<"?:", never, PropertyKey, PropertySignature.Token, never, boolean, unknown> + + /** + * @since 3.10.0 + */ + export type Type = Types.UnionToIntersection< + { + [K in keyof F]: F[K] extends OptionalPropertySignature ? { readonly [H in K]?: Schema.Type } : + { readonly [h in K]: Schema.Type } + }[keyof F] + > extends infer Q ? Q : never + + /** + * @since 3.10.0 + */ + export type Encoded = + & { readonly [K in Exclude> as Key]: Schema.Encoded } + & { readonly [K in EncodedTokenKeys as Key]?: Schema.Encoded } + + /** + * @since 3.10.0 + */ + export type Context = Schema.Context + + type PropertySignatureWithDefault = + | PropertySignature + | PropertySignature + | PropertySignature + | PropertySignature + + /** + * @since 3.10.0 + */ + export type Constructor = Types.UnionToIntersection< + { + [K in keyof F]: F[K] extends OptionalPropertySignature ? { readonly [H in K]?: Schema.Type } : + F[K] extends PropertySignatureWithDefault ? { readonly [H in K]?: Schema.Type } : + { readonly [h in K]: Schema.Type } + }[keyof F] + > extends infer Q ? Q : never +} + +/** + * @since 3.10.0 + */ +export declare namespace IndexSignature { + /** + * @since 3.10.0 + */ + export type Record = { readonly key: Schema.All; readonly value: Schema.All } + + /** + * @since 3.10.0 + */ + export type Records = ReadonlyArray + + /** + * @since 3.10.0 + */ + export type NonEmptyRecords = array_.NonEmptyReadonlyArray + + /** + * @since 3.10.0 + */ + export type Type< + Records extends IndexSignature.Records + > = Types.UnionToIntersection< + { + [K in keyof Records]: { + readonly [P in Schema.Type]: Schema.Type + } + }[number] + > + + /** + * @since 3.10.0 + */ + export type Encoded< + Records extends IndexSignature.Records + > = Types.UnionToIntersection< + { + [K in keyof Records]: { + readonly [P in Schema.Encoded]: Schema.Encoded + } + }[number] + > + + /** + * @since 3.10.0 + */ + export type Context = { + [K in keyof Records]: Schema.Context | Schema.Context + }[number] +} + +/** + * @since 3.10.0 + */ +export declare namespace TypeLiteral { + /** + * @since 3.10.0 + */ + export type Type = + & Struct.Type + & IndexSignature.Type + + /** + * @since 3.10.0 + */ + export type Encoded = + & Struct.Encoded + & IndexSignature.Encoded + + /** + * @since 3.10.0 + */ + export type Constructor = + & Struct.Constructor + & IndexSignature.Type +} + +/** + * @category api interface + * @since 3.10.0 + */ +export interface TypeLiteral< + Fields extends Struct.Fields, + Records extends IndexSignature.Records +> extends + AnnotableClass< + TypeLiteral, + Simplify>, + Simplify>, + | Struct.Context + | IndexSignature.Context + > +{ + readonly fields: { readonly [K in keyof Fields]: Fields[K] } + readonly records: Readonly + annotations( + annotations: Annotations.Schema>> + ): TypeLiteral + make( + props: Simplify>, + options?: MakeOptions + ): Simplify> +} + +const getDefaultTypeLiteralAST = < + Fields extends Struct.Fields, + const Records extends IndexSignature.Records +>(fields: Fields, records: Records) => { + const ownKeys = util_.ownKeys(fields) + const pss: Array = [] + if (ownKeys.length > 0) { + const from: Array = [] + const to: Array = [] + const transformations: Array = [] + for (let i = 0; i < ownKeys.length; i++) { + const key = ownKeys[i] + const field = fields[key] + if (isPropertySignature(field)) { + const ast: PropertySignature.AST = field.ast + switch (ast._tag) { + case "PropertySignatureDeclaration": { + const type = ast.type + const isOptional = ast.isOptional + const toAnnotations = ast.annotations + from.push(new AST.PropertySignature(key, type, isOptional, true)) + to.push(new AST.PropertySignature(key, AST.typeAST(type), isOptional, true, toAnnotations)) + pss.push( + new AST.PropertySignature(key, type, isOptional, true, toAnnotations) + ) + break + } + case "PropertySignatureTransformation": { + const fromKey = ast.from.fromKey ?? key + from.push( + new AST.PropertySignature(fromKey, ast.from.type, ast.from.isOptional, true, ast.from.annotations) + ) + to.push( + new AST.PropertySignature(key, ast.to.type, ast.to.isOptional, true, ast.to.annotations) + ) + transformations.push(new AST.PropertySignatureTransformation(fromKey, key, ast.decode, ast.encode)) + break + } + } + } else { + from.push(new AST.PropertySignature(key, field.ast, false, true)) + to.push(new AST.PropertySignature(key, AST.typeAST(field.ast), false, true)) + pss.push(new AST.PropertySignature(key, field.ast, false, true)) + } + } + if (array_.isNonEmptyReadonlyArray(transformations)) { + const issFrom: Array = [] + const issTo: Array = [] + for (const r of records) { + const { indexSignatures, propertySignatures } = AST.record(r.key.ast, r.value.ast) + propertySignatures.forEach((ps) => { + from.push(ps) + to.push( + new AST.PropertySignature(ps.name, AST.typeAST(ps.type), ps.isOptional, ps.isReadonly, ps.annotations) + ) + }) + indexSignatures.forEach((is) => { + issFrom.push(is) + issTo.push(new AST.IndexSignature(is.parameter, AST.typeAST(is.type), is.isReadonly)) + }) + } + return new AST.Transformation( + new AST.TypeLiteral(from, issFrom, { [AST.TitleAnnotationId]: "Struct (Encoded side)" }), + new AST.TypeLiteral(to, issTo, { [AST.TitleAnnotationId]: "Struct (Type side)" }), + new AST.TypeLiteralTransformation(transformations) + ) + } + } + const iss: Array = [] + for (const r of records) { + const { indexSignatures, propertySignatures } = AST.record(r.key.ast, r.value.ast) + propertySignatures.forEach((ps) => pss.push(ps)) + indexSignatures.forEach((is) => iss.push(is)) + } + return new AST.TypeLiteral(pss, iss) +} + +const lazilyMergeDefaults = ( + fields: Struct.Fields, + out: Record +): { [x: string | symbol]: unknown } => { + const ownKeys = util_.ownKeys(fields) + for (const key of ownKeys) { + const field = fields[key] + if (out[key] === undefined && isPropertySignature(field)) { + const ast = field.ast + const defaultValue = ast._tag === "PropertySignatureDeclaration" ? ast.defaultValue : ast.to.defaultValue + if (defaultValue !== undefined) { + out[key] = defaultValue() + } + } + } + return out +} + +const makeTypeLiteralClass = < + Fields extends Struct.Fields, + const Records extends IndexSignature.Records +>( + fields: Fields, + records: Records, + ast: AST.AST = getDefaultTypeLiteralAST(fields, records) +): TypeLiteral => { + return class TypeLiteralClass extends make< + Simplify>, + Simplify>, + | Struct.Context + | IndexSignature.Context + >(ast) { + static override annotations( + annotations: Annotations.Schema>> + ): TypeLiteral { + return makeTypeLiteralClass(this.fields, this.records, mergeSchemaAnnotations(this.ast, annotations)) + } + + static fields = { ...fields } + + static records = [...records] as Records + + static make = ( + props: Simplify>, + options?: MakeOptions + ): Simplify> => { + const propsWithDefaults: any = lazilyMergeDefaults(fields, { ...props as any }) + return getDisableValidationMakeOption(options) + ? propsWithDefaults + : ParseResult.validateSync(this)(propsWithDefaults) + } + + static pick(...keys: Array): Struct>> { + return Struct(struct_.pick(fields, ...keys) as any) + } + + static omit(...keys: Array): Struct>> { + return Struct(struct_.omit(fields, ...keys) as any) + } + } +} + +/** + * @category api interface + * @since 3.10.0 + */ +export interface Struct extends TypeLiteral { + annotations(annotations: Annotations.Schema>>): Struct + pick>(...keys: Keys): Struct>> + omit>(...keys: Keys): Struct>> +} + +/** + * @category constructors + * @since 3.10.0 + */ +export function Struct( + fields: Fields, + ...records: Records +): TypeLiteral +export function Struct(fields: Fields): Struct +export function Struct( + fields: Fields, + ...records: Records +): TypeLiteral { + return makeTypeLiteralClass(fields, records) +} + +/** + * @category api interface + * @since 3.10.0 + */ +export interface tag extends PropertySignature<":", Tag, never, ":", Tag, true, never> {} + +/** + * Returns a property signature that represents a tag. + * A tag is a literal value that is used to distinguish between different types of objects. + * The tag is optional when using the `make` method. + * + * @see {@link TaggedStruct} + * + * @example + * import { Schema } from "effect" + * + * const User = Schema.Struct({ + * _tag: Schema.tag("User"), + * name: Schema.String, + * age: Schema.Number + * }) + * + * assert.deepStrictEqual(User.make({ name: "John", age: 44 }), { _tag: "User", name: "John", age: 44 }) + * + * @since 3.10.0 + */ +export const tag = (tag: Tag): tag => + Literal(tag).pipe(propertySignature, withConstructorDefault(() => tag)) + +/** + * @category api interface + * @since 3.10.0 + */ +export type TaggedStruct = Struct< + { _tag: tag } & Fields +> + +/** + * A tagged struct is a struct that has a tag property that is used to distinguish between different types of objects. + * + * The tag is optional when using the `make` method. + * + * @example + * import { Schema } from "effect" + * + * 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: Tag, + fields: Fields +): TaggedStruct => Struct({ _tag: tag(value), ...fields }) + +/** + * @category api interface + * @since 3.10.0 + */ +export interface Record$ extends TypeLiteral<{}, [{ key: K; value: V }]> { + readonly key: K + readonly value: V + annotations( + annotations: Annotations.Schema>> + ): Record$ +} + +const makeRecordClass = (key: K, value: V, ast?: AST.AST): Record$ => + class RecordClass extends makeTypeLiteralClass({}, [{ key, value }], ast) { + static override annotations( + annotations: Annotations.Schema>> + ) { + return makeRecordClass(key, value, mergeSchemaAnnotations(this.ast, annotations)) + } + + static key = key + + static value = value + } + +/** + * @category constructors + * @since 3.10.0 + */ +export const Record = ( + options: { readonly key: K; readonly value: V } +): Record$ => makeRecordClass(options.key, options.value) + +/** + * @category struct transformations + * @since 3.10.0 + */ +export const pick = >(...keys: Keys) => +( + self: Schema +): SchemaClass>, Simplify>, R> => make(AST.pick(self.ast, keys)) + +/** + * @category struct transformations + * @since 3.10.0 + */ +export const omit = >(...keys: Keys) => +( + self: Schema +): SchemaClass>, Simplify>, R> => make(AST.omit(self.ast, keys)) + +/** + * Given a schema `Schema` and a key `key: K`, this function extracts a specific field from the `A` type, + * producing a new schema that represents a transformation from the `{ readonly [key]: I[K] }` type to `A[K]`. + * + * @example + * import * as S from "effect/Schema" + * + * // --------------------------------------------- + * // use case: pull out a single field from a + * // struct through a transformation + * // --------------------------------------------- + * + * const mytable = S.Struct({ + * column1: S.NumberFromString, + * column2: S.Number + * }) + * + * // const pullOutColumn: S.Schema + * const pullOutColumn = mytable.pipe(S.pluck("column1")) + * + * console.log(S.decodeUnknownEither(S.Array(pullOutColumn))([{ column1: "1", column2: 100 }, { column1: "2", column2: 300 }])) + * // Output: { _id: 'Either', _tag: 'Right', right: [ 1, 2 ] } + * + * @category struct transformations + * @since 3.10.0 + */ +export const pluck: { + ( + key: K + ): (schema: Schema) => Schema + ( + schema: Schema, + key: K + ): Schema +} = dual( + 2, + ( + schema: Schema, + key: K + ): Schema, R> => { + const ps = AST.getPropertyKeyIndexedAccess(AST.typeAST(schema.ast), key) + const value = make(ps.isOptional ? AST.orUndefined(ps.type) : ps.type) + return transform( + schema.pipe(pick(key)), + value, + { + strict: true, + decode: (a: any) => a[key], + encode: (ak) => ps.isOptional && ak === undefined ? {} : { [key]: ak } as any + } + ) + } +) + +/** + * @category branding + * @since 3.10.0 + */ +export interface BrandSchema, I = A, R = never> + extends AnnotableClass, A, I, R> +{ + make(a: Brand.Unbranded, options?: MakeOptions): A +} + +/** + * @category api interface + * @since 3.10.0 + */ +export interface brand + extends BrandSchema & Brand, Schema.Encoded, Schema.Context> +{ + annotations(annotations: Annotations.Schema & Brand>): brand +} + +const makeBrandClass = (ast: AST.AST): brand => + class BrandClass extends make & Brand, Schema.Encoded, Schema.Context>(ast) { + static override annotations(annotations: Annotations.Schema & Brand>): brand { + return makeBrandClass(mergeSchemaAnnotations(this.ast, annotations)) + } + + static make = (a: Brand.Unbranded & Brand>, options?: MakeOptions): Schema.Type & Brand => { + return getDisableValidationMakeOption(options) ? a : ParseResult.validateSync(this)(a) + } + } + +/** + * Returns a nominal branded schema by applying a brand to a given schema. + * + * ``` + * Schema + B -> Schema> + * ``` + * + * @param self - The input schema to be combined with the brand. + * @param brand - The brand to apply. + * + * @example + * import * as Schema from "effect/Schema" + * + * const Int = Schema.Number.pipe(Schema.int(), Schema.brand("Int")) + * type Int = Schema.Schema.Type // number & Brand<"Int"> + * + * @category branding + * @since 3.10.0 + */ +export const brand = ( + brand: B, + annotations?: Annotations.Schema & Brand> +) => +(self: S): brand => { + const annotation: AST.BrandAnnotation = option_.match(AST.getBrandAnnotation(self.ast), { + onNone: () => [brand], + onSome: (brands) => [...brands, brand] + }) + const ast = AST.annotations( + self.ast, + toASTAnnotations({ + // add a default title annotation containing the brand + title: String(self.ast) + ` & Brand<${util_.formatUnknown(brand)}>`, + ...annotations, + [AST.BrandAnnotationId]: annotation + }) + ) + return makeBrandClass(ast) +} + +/** + * @category combinators + * @since 3.10.0 + */ +export const partial = ( + self: Schema +): SchemaClass<{ [K in keyof A]?: A[K] | undefined }, { [K in keyof I]?: I[K] | undefined }, R> => + make(AST.partial(self.ast)) + +/** + * @category combinators + * @since 3.10.0 + */ +export const partialWith: { + (options: Options): ( + self: Schema + ) => SchemaClass<{ [K in keyof A]?: A[K] }, { [K in keyof I]?: I[K] }, R> + ( + self: Schema, + options: Options + ): SchemaClass<{ [K in keyof A]?: A[K] }, { [K in keyof I]?: I[K] }, R> +} = dual((args) => isSchema(args[0]), ( + self: Schema, + options: { readonly exact: true } +): SchemaClass, Partial, R> => make(AST.partial(self.ast, options))) + +/** + * @category combinators + * @since 3.10.0 + */ +export const required = ( + self: Schema +): SchemaClass<{ [K in keyof A]-?: A[K] }, { [K in keyof I]-?: I[K] }, R> => make(AST.required(self.ast)) + +/** + * @category api interface + * @since 3.10.0 + */ +export interface mutable extends + AnnotableClass< + mutable, + SimplifyMutable>, + SimplifyMutable>, + Schema.Context + > +{} + +/** + * Creates a new schema with shallow mutability applied to its properties. + * + * @param schema - The original schema to make properties mutable (shallowly). + * + * @category combinators + * @since 3.10.0 + */ +export const mutable = (schema: S): mutable => make(AST.mutable(schema.ast)) + +const intersectTypeLiterals = ( + x: AST.AST, + y: AST.AST, + path: ReadonlyArray +): AST.TypeLiteral => { + if (AST.isTypeLiteral(x) && AST.isTypeLiteral(y)) { + const propertySignatures = [...x.propertySignatures] + for (const ps of y.propertySignatures) { + const name = ps.name + const i = propertySignatures.findIndex((ps) => ps.name === name) + if (i === -1) { + propertySignatures.push(ps) + } else { + const { isOptional, type } = propertySignatures[i] + propertySignatures[i] = new AST.PropertySignature( + name, + extendAST(type, ps.type, path.concat(name)), + isOptional, + true + ) + } + } + return new AST.TypeLiteral( + propertySignatures, + x.indexSignatures.concat(y.indexSignatures) + ) + } + throw new Error(errors_.getSchemaExtendErrorMessage(x, y, path)) +} + +const preserveRefinementAnnotations = AST.blackListAnnotations([ + AST.IdentifierAnnotationId +]) + +const addRefinementToMembers = (refinement: AST.Refinement, asts: ReadonlyArray): Array => + asts.map((ast) => new AST.Refinement(ast, refinement.filter, preserveRefinementAnnotations(refinement))) + +const extendAST = ( + x: AST.AST, + y: AST.AST, + path: ReadonlyArray +): AST.AST => AST.Union.make(intersectUnionMembers([x], [y], path)) + +const getTypes = (ast: AST.AST): ReadonlyArray => AST.isUnion(ast) ? ast.types : [ast] + +const intersectUnionMembers = ( + xs: ReadonlyArray, + ys: ReadonlyArray, + path: ReadonlyArray +): Array => + array_.flatMap(xs, (x) => + array_.flatMap(ys, (y) => { + switch (y._tag) { + case "Literal": { + if ( + (Predicate.isString(y.literal) && AST.isStringKeyword(x) || + (Predicate.isNumber(y.literal) && AST.isNumberKeyword(x)) || + (Predicate.isBoolean(y.literal) && AST.isBooleanKeyword(x))) + ) { + return [y] + } + break + } + case "StringKeyword": { + if (y === AST.stringKeyword) { + if (AST.isStringKeyword(x) || (AST.isLiteral(x) && Predicate.isString(x.literal))) { + return [x] + } else if (AST.isRefinement(x)) { + return addRefinementToMembers(x, intersectUnionMembers(getTypes(x.from), [y], path)) + } + } else if (x === AST.stringKeyword) { + return [y] + } + break + } + case "NumberKeyword": { + if (y === AST.numberKeyword) { + if (AST.isNumberKeyword(x) || (AST.isLiteral(x) && Predicate.isNumber(x.literal))) { + return [x] + } else if (AST.isRefinement(x)) { + return addRefinementToMembers(x, intersectUnionMembers(getTypes(x.from), [y], path)) + } + } else if (x === AST.numberKeyword) { + return [y] + } + break + } + case "BooleanKeyword": { + if (y === AST.booleanKeyword) { + if (AST.isBooleanKeyword(x) || (AST.isLiteral(x) && Predicate.isBoolean(x.literal))) { + return [x] + } else if (AST.isRefinement(x)) { + return addRefinementToMembers(x, intersectUnionMembers(getTypes(x.from), [y], path)) + } + } else if (x === AST.booleanKeyword) { + return [y] + } + break + } + case "Union": + return intersectUnionMembers(getTypes(x), y.types, path) + case "Suspend": + return [new AST.Suspend(() => extendAST(x, y.f(), path))] + case "Refinement": + return addRefinementToMembers(y, intersectUnionMembers(getTypes(x), getTypes(y.from), path)) + case "TypeLiteral": { + switch (x._tag) { + case "Union": + return intersectUnionMembers(x.types, [y], path) + case "Suspend": + return [new AST.Suspend(() => extendAST(x.f(), y, path))] + case "Refinement": + return addRefinementToMembers(x, intersectUnionMembers(getTypes(x.from), [y], path)) + case "TypeLiteral": + return [intersectTypeLiterals(x, y, path)] + case "Transformation": { + if (AST.isTypeLiteralTransformation(x.transformation)) { + return [ + new AST.Transformation( + intersectTypeLiterals(x.from, y, path), + intersectTypeLiterals(x.to, AST.typeAST(y), path), + new AST.TypeLiteralTransformation( + x.transformation.propertySignatureTransformations + ) + ) + ] + } + break + } + } + break + } + case "Transformation": { + if (AST.isTypeLiteralTransformation(y.transformation)) { + switch (x._tag) { + case "Union": + return intersectUnionMembers(x.types, [y], path) + case "Suspend": + return [new AST.Suspend(() => extendAST(x.f(), y, path))] + case "Refinement": + return addRefinementToMembers(x, intersectUnionMembers(getTypes(x.from), [y], path)) + case "TypeLiteral": + return [ + new AST.Transformation( + intersectTypeLiterals(x, y.from, path), + intersectTypeLiterals(AST.typeAST(x), y.to, path), + new AST.TypeLiteralTransformation( + y.transformation.propertySignatureTransformations + ) + ) + ] + case "Transformation": + { + if (AST.isTypeLiteralTransformation(x.transformation)) { + return [ + new AST.Transformation( + intersectTypeLiterals(x.from, y.from, path), + intersectTypeLiterals(x.to, y.to, path), + new AST.TypeLiteralTransformation( + y.transformation.propertySignatureTransformations.concat( + x.transformation.propertySignatureTransformations + ) + ) + ) + ] + } + } + break + } + } + break + } + } + throw new Error(errors_.getSchemaExtendErrorMessage(x, y, path)) + })) + +/** + * @category api interface + * @since 3.10.0 + */ +export interface extend extends + AnnotableClass< + extend, + Schema.Type & Schema.Type, + Schema.Encoded & Schema.Encoded, + Schema.Context | Schema.Context + > +{} + +/** + * Extends a schema with another schema. + * + * Not all extensions are supported, and their support depends on the nature of the involved schemas. + * + * Possible extensions include: + * - `Schema.String` with another `Schema.String` refinement or a string literal + * - `Schema.Number` with another `Schema.Number` refinement or a number literal + * - `Schema.Boolean` with another `Schema.Boolean` refinement or a boolean literal + * - A struct with another struct where overlapping fields support extension + * - A struct with in index signature + * - A struct with a union of supported schemas + * - A refinement of a struct with a supported schema + * - A suspend of a struct with a supported schema + * + * @example + * import * as Schema from "effect/Schema" + * + * const schema = Schema.Struct({ + * a: Schema.String, + * b: Schema.String + * }) + * + * // const extended: Schema.Schema< + * // { + * // readonly a: string + * // readonly b: string + * // } & { + * // readonly c: string + * // } & { + * // readonly [x: string]: string + * // } + * // > + * const extended = Schema.asSchema(schema.pipe( + * Schema.extend(Schema.Struct({ c: Schema.String })), // <= you can add more fields + * Schema.extend(Schema.Record({ key: Schema.String, value: Schema.String })) // <= you can add index signatures + * )) + * + * @category combinators + * @since 3.10.0 + */ +export const extend: { + (that: That): (self: Self) => extend + (self: Self, that: That): extend +} = dual( + 2, + (self: Self, that: That) => make(extendAST(self.ast, that.ast, [])) +) + +/** + * @category combinators + * @since 3.10.0 + */ +export const compose: { + ( + to: Schema + ): (from: Schema) => SchemaClass + ( + to: Schema + ): (from: Schema) => SchemaClass + ( + to: Schema, + options?: { readonly strict: true } + ): (from: Schema) => SchemaClass + ( + to: Schema, + options: { readonly strict: false } + ): (from: Schema) => SchemaClass + + ( + from: Schema, + to: Schema + ): SchemaClass + ( + from: Schema, + to: Schema + ): SchemaClass + ( + from: Schema, + to: Schema, + options?: { readonly strict: true } + ): SchemaClass + ( + from: Schema, + to: Schema, + options: { readonly strict: false } + ): SchemaClass +} = dual( + (args) => isSchema(args[1]), + (from: Schema, to: Schema): SchemaClass => + make(AST.compose(from.ast, to.ast)) +) + +/** + * @category api interface + * @since 3.10.0 + */ +export interface suspend extends AnnotableClass, A, I, R> {} + +/** + * @category constructors + * @since 3.10.0 + */ +export const suspend = (f: () => Schema): suspend => make(new AST.Suspend(() => f().ast)) + +/** + * @since 3.10.0 + * @category symbol + */ +export const refineTypeId: unique symbol = Symbol.for("effect/Schema/refine") + +/** + * @since 3.10.0 + * @category symbol + */ +export type refineTypeId = typeof refineTypeId + +/** + * @category api interface + * @since 3.10.0 + */ +export interface refine + extends AnnotableClass, A, Schema.Encoded, Schema.Context> +{ + readonly [refineTypeId]: From + readonly from: From + readonly filter: ( + a: Schema.Type, + options: ParseOptions, + self: AST.Refinement + ) => option_.Option + make(a: Schema.Type, options?: MakeOptions): A +} + +const makeRefineClass = ( + from: From, + filter: ( + a: Schema.Type, + options: ParseOptions, + self: AST.Refinement + ) => option_.Option, + ast: AST.AST +): refine => + class RefineClass extends make, Schema.Context>(ast) { + static override annotations(annotations: Annotations.Schema): refine { + return makeRefineClass(this.from, this.filter, mergeSchemaAnnotations(this.ast, annotations)) + } + + static [refineTypeId] = from + + static from = from + + static filter = filter + + static make = (a: Schema.Type, options?: MakeOptions): A => { + return getDisableValidationMakeOption(options) ? a : ParseResult.validateSync(this)(a) + } + } + +/** + * @category api interface + * @since 3.10.0 + */ +export interface filter extends refine, From> {} + +const fromFilterPredicateReturnTypeItem = ( + item: FilterOutput, + ast: AST.Refinement | AST.Transformation, + input: unknown +): option_.Option => { + if (Predicate.isBoolean(item)) { + return item + ? option_.none() + : option_.some(new ParseResult.Type(ast, input)) + } + if (Predicate.isString(item)) { + return option_.some(new ParseResult.Type(ast, input, item)) + } + if (item !== undefined) { + if ("_tag" in item) { + return option_.some(item) + } + const issue = new ParseResult.Type(ast, input, item.message) + return option_.some( + array_.isNonEmptyReadonlyArray(item.path) ? new ParseResult.Pointer(item.path, input, issue) : issue + ) + } + return option_.none() +} + +const toFilterParseIssue = ( + out: FilterReturnType, + ast: AST.Refinement | AST.Transformation, + input: unknown +): option_.Option => { + if (util_.isSingle(out)) { + return fromFilterPredicateReturnTypeItem(out, ast, input) + } + if (array_.isNonEmptyReadonlyArray(out)) { + const issues = array_.filterMap(out, (issue) => fromFilterPredicateReturnTypeItem(issue, ast, input)) + if (array_.isNonEmptyReadonlyArray(issues)) { + return option_.some(issues.length === 1 ? issues[0] : new ParseResult.Composite(ast, input, issues)) + } + } + return option_.none() +} + +/** + * @category filtering + * @since 3.10.0 + */ +export interface FilterIssue { + readonly path: ReadonlyArray + readonly message: string +} + +/** + * @category filtering + * @since 3.10.0 + */ +export type FilterOutput = undefined | boolean | string | ParseResult.ParseIssue | FilterIssue + +type FilterReturnType = FilterOutput | ReadonlyArray + +/** + * @category filtering + * @since 3.10.0 + */ +export function filter( + refinement: (a: A, options: ParseOptions, self: AST.Refinement) => a is B, + annotations?: Annotations.Filter +): (self: Schema) => refine> +export function filter( + refinement: (a: A, options: ParseOptions, self: AST.Refinement) => a is B, + annotations?: Annotations.Filter +): (self: Schema) => refine> +export function filter( + predicate: ( + a: Types.NoInfer>, + options: ParseOptions, + self: AST.Refinement + ) => FilterReturnType, + annotations?: Annotations.Filter>> +): (self: S) => filter +export function filter( + predicate: ( + a: A, + options: ParseOptions, + self: AST.Refinement + ) => FilterReturnType, + annotations?: Annotations.Filter +): (self: Schema) => refine> { + return (self: Schema) => { + function filter(input: A, options: AST.ParseOptions, ast: AST.Refinement) { + return toFilterParseIssue(predicate(input, options, ast), ast, input) + } + const ast = new AST.Refinement( + self.ast, + filter, + toASTAnnotations(annotations) + ) + return makeRefineClass(self, filter, ast) + } +} + +/** + * @category api interface + * @since 3.10.0 + */ +export interface filterEffect + extends transformOrFail>, FD> +{} + +/** + * @category transformations + * @since 3.10.0 + */ +export const filterEffect: { + ( + f: ( + a: Types.NoInfer>, + options: ParseOptions, + self: AST.Transformation + ) => Effect.Effect + ): (self: S) => filterEffect + ( + self: S, + f: ( + a: Types.NoInfer>, + options: ParseOptions, + self: AST.Transformation + ) => Effect.Effect + ): filterEffect +} = dual(2, ( + self: S, + f: ( + a: Types.NoInfer>, + options: ParseOptions, + self: AST.Transformation + ) => Effect.Effect +): filterEffect => + transformOrFail( + self, + typeSchema(self), + { + strict: true, + decode: (a, options, ast) => + ParseResult.flatMap( + f(a, options, ast), + (filterReturnType) => + option_.match(toFilterParseIssue(filterReturnType, ast, a), { + onNone: () => ParseResult.succeed(a), + onSome: ParseResult.fail + }) + ), + encode: ParseResult.succeed + } + )) + +/** + * @category api interface + * @since 3.10.0 + */ +export interface transformOrFail extends + AnnotableClass< + transformOrFail, + Schema.Type, + Schema.Encoded, + Schema.Context | Schema.Context | R + > +{ + readonly from: From + readonly to: To +} + +const makeTransformationClass = ( + from: From, + to: To, + ast: AST.AST +): transformOrFail => + class TransformationClass + extends make, Schema.Encoded, Schema.Context | Schema.Context | R>(ast) + { + static override annotations(annotations: Annotations.Schema>) { + return makeTransformationClass( + this.from, + this.to, + mergeSchemaAnnotations(this.ast, annotations) + ) + } + + static from = from + + static to = to + } + +/** + * Create a new `Schema` by transforming the input and output of an existing `Schema` + * using the provided decoding functions. + * + * @category transformations + * @since 3.10.0 + */ +export const transformOrFail: { + ( + to: To, + options: { + readonly decode: ( + fromA: Schema.Type, + options: ParseOptions, + ast: AST.Transformation, + fromI: Schema.Encoded + ) => Effect.Effect, ParseResult.ParseIssue, RD> + readonly encode: ( + toI: Schema.Encoded, + options: ParseOptions, + ast: AST.Transformation, + toA: Schema.Type + ) => Effect.Effect, ParseResult.ParseIssue, RE> + readonly strict?: true + } | { + readonly decode: ( + fromA: Schema.Type, + options: ParseOptions, + ast: AST.Transformation, + fromI: Schema.Encoded + ) => Effect.Effect + readonly encode: ( + toI: Schema.Encoded, + options: ParseOptions, + ast: AST.Transformation, + toA: Schema.Type + ) => Effect.Effect + readonly strict: false + } + ): (from: From) => transformOrFail + ( + from: From, + to: To, + options: { + readonly decode: ( + fromA: Schema.Type, + options: ParseOptions, + ast: AST.Transformation, + fromI: Schema.Encoded + ) => Effect.Effect, ParseResult.ParseIssue, RD> + readonly encode: ( + toI: Schema.Encoded, + options: ParseOptions, + ast: AST.Transformation, + toA: Schema.Type + ) => Effect.Effect, ParseResult.ParseIssue, RE> + readonly strict?: true + } | { + readonly decode: ( + fromA: Schema.Type, + options: ParseOptions, + ast: AST.Transformation, + fromI: Schema.Encoded + ) => Effect.Effect + readonly encode: ( + toI: Schema.Encoded, + options: ParseOptions, + ast: AST.Transformation, + toA: Schema.Type + ) => Effect.Effect + readonly strict: false + } + ): transformOrFail +} = dual((args) => isSchema(args[0]) && isSchema(args[1]), ( + from: Schema, + to: Schema, + options: { + readonly decode: ( + fromA: FromA, + options: ParseOptions, + ast: AST.Transformation, + fromI: FromI + ) => Effect.Effect + readonly encode: ( + toI: ToI, + options: ParseOptions, + ast: AST.Transformation, + toA: ToA + ) => Effect.Effect + } +): Schema => + makeTransformationClass( + from, + to, + new AST.Transformation( + from.ast, + to.ast, + new AST.FinalTransformation(options.decode, options.encode) + ) + )) + +/** + * @category api interface + * @since 3.10.0 + */ +export interface transform extends transformOrFail { + annotations(annotations: Annotations.Schema>): transform +} + +/** + * Create a new `Schema` by transforming the input and output of an existing `Schema` + * using the provided mapping functions. + * + * @category transformations + * @since 3.10.0 + */ +export const transform: { + ( + to: To, + options: { + readonly decode: (fromA: Schema.Type, fromI: Schema.Encoded) => Schema.Encoded + readonly encode: (toI: Schema.Encoded, toA: Schema.Type) => Schema.Type + readonly strict?: true + } | { + readonly decode: (fromA: Schema.Type, fromI: Schema.Encoded) => unknown + readonly encode: (toI: Schema.Encoded, toA: Schema.Type) => unknown + readonly strict: false + } + ): (from: From) => transform + ( + from: From, + to: To, + options: { + readonly decode: (fromA: Schema.Type, fromI: Schema.Encoded) => Schema.Encoded + readonly encode: (toI: Schema.Encoded, toA: Schema.Type) => Schema.Type + readonly strict?: true + } | { + readonly decode: (fromA: Schema.Type, fromI: Schema.Encoded) => unknown + readonly encode: (toI: Schema.Encoded, toA: Schema.Type) => unknown + readonly strict: false + } + ): transform +} = dual( + (args) => isSchema(args[0]) && isSchema(args[1]), + ( + from: Schema, + to: Schema, + options: { + readonly decode: (fromA: FromA, fromI: FromI) => ToI + readonly encode: (toI: ToI, toA: ToA) => FromA + } + ): Schema => + transformOrFail( + from, + to, + { + strict: true, + decode: (fromA, _options, _ast, toA) => ParseResult.succeed(options.decode(fromA, toA)), + encode: (toI, _options, _ast, toA) => ParseResult.succeed(options.encode(toI, toA)) + } + ) +) + +/** + * @category api interface + * @since 3.10.0 + */ +export interface transformLiteral extends Annotable, Type, Encoded> {} + +/** + * Creates a new `Schema` which transforms literal values. + * + * @example + * import * as S from "effect/Schema" + * + * const schema = S.transformLiteral(0, "a") + * + * assert.deepStrictEqual(S.decodeSync(schema)(0), "a") + * + * @category constructors + * @since 3.10.0 + */ +export const transformLiteral = ( + from: Encoded, + to: Type +): transformLiteral => + transform(Literal(from), Literal(to), { strict: true, decode: () => to, encode: () => from }) + +/** + * Creates a new `Schema` which maps between corresponding literal values. + * + * @example + * import * as S from "effect/Schema" + * + * const Animal = S.transformLiterals( + * [0, "cat"], + * [1, "dog"], + * [2, "cow"] + * ) + * + * assert.deepStrictEqual(S.decodeSync(Animal)(1), "dog") + * + * @category constructors + * @since 3.10.0 + */ +export function transformLiterals>( + ...pairs: A +): Union<{ -readonly [I in keyof A]: transformLiteral }> +export function transformLiterals( + pairs: [Encoded, Type] +): transformLiteral +export function transformLiterals< + const A extends ReadonlyArray +>(...pairs: A): Schema +export function transformLiterals< + const A extends ReadonlyArray +>(...pairs: A): Schema { + return Union(...pairs.map(([from, to]) => transformLiteral(from, to))) +} + +/** + * Attaches a property signature with the specified key and value to the schema. + * This API is useful when you want to add a property to your schema which doesn't describe the shape of the input, + * but rather maps to another schema, for example when you want to add a discriminant to a simple union. + * + * @param self - The input schema. + * @param key - The name of the property to add to the schema. + * @param value - The value of the property to add to the schema. + * + * @example + * import * as S from "effect/Schema" + * import { pipe } from "effect/Function" + * + * const Circle = S.Struct({ radius: S.Number }) + * const Square = S.Struct({ sideLength: S.Number }) + * const Shape = S.Union( + * Circle.pipe(S.attachPropertySignature("kind", "circle")), + * Square.pipe(S.attachPropertySignature("kind", "square")) + * ) + * + * assert.deepStrictEqual(S.decodeSync(Shape)({ radius: 10 }), { + * kind: "circle", + * radius: 10 + * }) + * + * @category combinators + * @since 3.10.0 + */ +export const attachPropertySignature: { + ( + key: K, + value: V, + annotations?: Annotations.Schema> + ): ( + schema: SchemaClass + ) => SchemaClass, I, R> + ( + schema: Schema, + key: K, + value: V, + annotations?: Annotations.Schema> + ): SchemaClass, I, R> +} = dual( + (args) => isSchema(args[0]), + ( + schema: Schema, + key: K, + value: V, + annotations?: Annotations.Schema> + ): SchemaClass, I, R> => { + const ast = extend( + typeSchema(schema), + Struct({ [key]: Predicate.isSymbol(value) ? UniqueSymbolFromSelf(value) : Literal(value) }) + ).ast + return make( + new AST.Transformation( + schema.ast, + annotations ? mergeSchemaAnnotations(ast, annotations) : ast, + new AST.TypeLiteralTransformation( + [ + new AST.PropertySignatureTransformation( + key, + key, + () => option_.some(value), + () => option_.none() + ) + ] + ) + ) + ) + } +) + +/** + * @category annotations + * @since 3.10.0 + */ +export declare namespace Annotations { + /** + * @category annotations + * @since 3.10.0 + */ + export interface Doc extends AST.Annotations { + readonly title?: AST.TitleAnnotation + readonly description?: AST.DescriptionAnnotation + readonly documentation?: AST.DocumentationAnnotation + readonly examples?: AST.ExamplesAnnotation + readonly default?: AST.DefaultAnnotation + } + + /** + * @since 3.10.0 + */ + export interface Schema = readonly []> extends Doc { + readonly identifier?: AST.IdentifierAnnotation + readonly message?: AST.MessageAnnotation + readonly typeId?: AST.TypeAnnotation | { id: AST.TypeAnnotation; annotation: unknown } + readonly jsonSchema?: AST.JSONSchemaAnnotation + readonly arbitrary?: ( + ...arbitraries: [ + ...{ readonly [K in keyof TypeParameters]: LazyArbitrary }, + ctx: GenerationContext + ] + ) => LazyArbitrary + readonly pretty?: ( + ...pretties: { readonly [K in keyof TypeParameters]: pretty_.Pretty } + ) => pretty_.Pretty + readonly equivalence?: ( + ...equivalences: { readonly [K in keyof TypeParameters]: Equivalence.Equivalence } + ) => Equivalence.Equivalence + readonly concurrency?: AST.ConcurrencyAnnotation + readonly batching?: AST.BatchingAnnotation + readonly parseIssueTitle?: AST.ParseIssueTitleAnnotation + readonly parseOptions?: AST.ParseOptions + readonly decodingFallback?: AST.DecodingFallbackAnnotation + } + + /** + * @since 3.10.0 + */ + export interface Filter extends Schema {} +} + +/** + * Merges a set of new annotations with existing ones, potentially overwriting + * any duplicates. + * + * @category annotations + * @since 3.10.0 + */ +export const annotations: { + (annotations: Annotations.Schema>): (self: S) => Annotable.Self + (self: S, annotations: Annotations.Schema>): Annotable.Self +} = dual( + 2, + (self: Schema, annotations: Annotations.Schema): Schema => self.annotations(annotations) +) + +type Rename = { + [ + K in keyof A as K extends keyof M ? M[K] extends PropertyKey ? M[K] + : never + : K + ]: A[K] +} + +/** + * @category renaming + * @since 3.10.0 + */ +export const rename: { + < + A, + const M extends + & { readonly [K in keyof A]?: PropertyKey } + & { readonly [K in Exclude]: never } + >( + mapping: M + ): (self: Schema) => SchemaClass>, I, R> + < + A, + I, + R, + const M extends + & { readonly [K in keyof A]?: PropertyKey } + & { readonly [K in Exclude]: never } + >( + self: Schema, + mapping: M + ): SchemaClass>, I, R> +} = dual( + 2, + < + A, + I, + R, + const M extends + & { readonly [K in keyof A]?: PropertyKey } + & { readonly [K in Exclude]: never } + >( + self: Schema, + mapping: M + ): SchemaClass>, I, R> => make(AST.rename(self.ast, mapping)) +) + +/** + * @category type id + * @since 3.10.0 + */ +export const TrimmedTypeId: unique symbol = Symbol.for("effect/Schema/TypeId/Trimmed") + +/** + * Verifies that a string contains no leading or trailing whitespaces. + * + * Note. This combinator does not make any transformations, it only validates. + * If what you were looking for was a combinator to trim strings, then check out the `trim` combinator. + * + * @category string filters + * @since 3.10.0 + */ +export const trimmed = + (annotations?: Annotations.Filter) => (self: Schema): filter> => + self.pipe( + filter((a) => a === a.trim(), { + typeId: TrimmedTypeId, + description: "a string with no leading or trailing whitespace", + jsonSchema: { pattern: "^\\S[\\s\\S]*\\S$|^\\S$|^$" }, + ...annotations + }) + ) + +/** + * @category type id + * @since 3.10.0 + */ +export const MaxLengthTypeId: unique symbol = filters_.MaxLengthTypeId + +/** + * @category type id + * @since 3.10.0 + */ +export type MaxLengthTypeId = typeof MaxLengthTypeId + +/** + * @category string filters + * @since 3.10.0 + */ +export const maxLength = ( + maxLength: number, + annotations?: Annotations.Filter +) => +(self: Schema): filter> => + self.pipe( + filter( + (a) => a.length <= maxLength, + { + typeId: MaxLengthTypeId, + description: `a string at most ${maxLength} character(s) long`, + jsonSchema: { maxLength }, + ...annotations + } + ) + ) + +/** + * @category type id + * @since 3.10.0 + */ +export const MinLengthTypeId: unique symbol = filters_.MinLengthTypeId + +/** + * @category type id + * @since 3.10.0 + */ +export type MinLengthTypeId = typeof MinLengthTypeId + +/** + * @category string filters + * @since 3.10.0 + */ +export const minLength = ( + minLength: number, + annotations?: Annotations.Filter +) => +(self: Schema): filter> => + self.pipe( + filter( + (a) => a.length >= minLength, + { + typeId: MinLengthTypeId, + description: `a string at least ${minLength} character(s) long`, + jsonSchema: { minLength }, + ...annotations + } + ) + ) + +/** + * @category type id + * @since 3.10.0 + */ +export const PatternTypeId: unique symbol = Symbol.for("effect/Schema/TypeId/Pattern") + +/** + * @category string filters + * @since 3.10.0 + */ +export const pattern = ( + regex: RegExp, + annotations?: Annotations.Filter +) => +(self: Schema): filter> => { + const pattern = regex.source + return self.pipe( + filter( + (a): a is A => { + // The following line ensures that `lastIndex` is reset to `0` in case the user has specified the `g` flag + regex.lastIndex = 0 + return regex.test(a) + }, + { + typeId: { id: PatternTypeId, annotation: { regex } }, + description: `a string matching the pattern ${pattern}`, + jsonSchema: { pattern }, + arbitrary: () => (fc) => fc.stringMatching(regex) as any, + ...annotations + } + ) + ) +} + +/** + * @category type id + * @since 3.10.0 + */ +export const StartsWithTypeId: unique symbol = Symbol.for("effect/Schema/TypeId/StartsWith") + +/** + * @category string filters + * @since 3.10.0 + */ +export const startsWith = ( + startsWith: string, + annotations?: Annotations.Filter +) => +(self: Schema): filter> => + self.pipe( + filter( + (a) => a.startsWith(startsWith), + { + typeId: { id: StartsWithTypeId, annotation: { startsWith } }, + description: `a string starting with ${JSON.stringify(startsWith)}`, + jsonSchema: { pattern: `^${startsWith}` }, + ...annotations + } + ) + ) + +/** + * @category type id + * @since 3.10.0 + */ +export const EndsWithTypeId: unique symbol = Symbol.for("effect/Schema/TypeId/EndsWith") + +/** + * @category string filters + * @since 3.10.0 + */ +export const endsWith = ( + endsWith: string, + annotations?: Annotations.Filter +) => +(self: Schema): filter> => + self.pipe( + filter( + (a) => a.endsWith(endsWith), + { + typeId: { id: EndsWithTypeId, annotation: { endsWith } }, + description: `a string ending with ${JSON.stringify(endsWith)}`, + jsonSchema: { pattern: `^.*${endsWith}$` }, + ...annotations + } + ) + ) + +/** + * @category type id + * @since 3.10.0 + */ +export const IncludesTypeId: unique symbol = Symbol.for("effect/Schema/TypeId/Includes") + +/** + * @category string filters + * @since 3.10.0 + */ +export const includes = ( + searchString: string, + annotations?: Annotations.Filter +) => +(self: Schema): filter> => + self.pipe( + filter( + (a) => a.includes(searchString), + { + typeId: { id: IncludesTypeId, annotation: { includes: searchString } }, + description: `a string including ${JSON.stringify(searchString)}`, + jsonSchema: { pattern: `.*${searchString}.*` }, + ...annotations + } + ) + ) + +/** + * @category type id + * @since 3.10.0 + */ +export const LowercasedTypeId: unique symbol = Symbol.for("effect/Schema/TypeId/Lowercased") + +/** + * Verifies that a string is lowercased. + * + * @category string filters + * @since 3.10.0 + */ +export const lowercased = + (annotations?: Annotations.Filter) => (self: Schema): filter> => + self.pipe( + filter((a) => a === a.toLowerCase(), { + typeId: LowercasedTypeId, + description: "a lowercase string", + ...annotations + }) + ) + +/** + * @category string constructors + * @since 3.10.0 + */ +export class Lowercased extends String$.pipe( + lowercased({ identifier: "Lowercased", title: "Lowercased" }) +) {} + +/** + * @category type id + * @since 3.10.0 + */ +export const CapitalizedTypeId: unique symbol = Symbol.for("effect/Schema/TypeId/Capitalized") + +/** + * Verifies that a string is capitalized. + * + * @category string filters + * @since 3.10.0 + */ +export const capitalized = + (annotations?: Annotations.Filter) => (self: Schema): filter> => + self.pipe( + filter((a) => a[0]?.toUpperCase() === a[0], { + typeId: CapitalizedTypeId, + description: "a capitalized string", + ...annotations + }) + ) + +/** + * @category string constructors + * @since 3.10.0 + */ +export class Capitalized extends String$.pipe( + capitalized({ identifier: "Capitalized", title: "Capitalized" }) +) {} + +/** + * @category type id + * @since 3.10.0 + */ +export const UncapitalizedTypeId: unique symbol = Symbol.for("effect/Schema/TypeId/Uncapitalized") + +/** + * Verifies that a string is uncapitalized. + * + * @category string filters + * @since 3.10.0 + */ +export const uncapitalized = + (annotations?: Annotations.Filter) => (self: Schema): filter> => + self.pipe( + filter((a) => a[0]?.toLowerCase() === a[0], { + typeId: UncapitalizedTypeId, + description: "a uncapitalized string", + ...annotations + }) + ) + +/** + * @category string constructors + * @since 3.10.0 + */ +export class Uncapitalized extends String$.pipe( + uncapitalized({ identifier: "Uncapitalized", title: "Uncapitalized" }) +) {} + +/** + * @category type id + * @since 3.10.0 + */ +export const UppercasedTypeId: unique symbol = Symbol.for("effect/Schema/TypeId/Uppercased") + +/** + * Verifies that a string is uppercased. + * + * @category string filters + * @since 3.10.0 + */ +export const uppercased = + (annotations?: Annotations.Filter) => (self: Schema): filter> => + self.pipe( + filter((a) => a === a.toUpperCase(), { + typeId: UppercasedTypeId, + description: "an uppercase string", + ...annotations + }) + ) + +/** + * @category string constructors + * @since 3.10.0 + */ +export class Uppercased extends String$.pipe( + uppercased({ identifier: "Uppercased", title: "Uppercased" }) +) {} + +/** + * @category type id + * @since 3.10.0 + */ +export const LengthTypeId: unique symbol = filters_.LengthTypeId + +/** + * @category type id + * @since 3.10.0 + */ +export type LengthTypeId = typeof LengthTypeId + +/** + * @category string filters + * @since 3.10.0 + */ +export const length = ( + length: number | { readonly min: number; readonly max: number }, + annotations?: Annotations.Filter +) => +(self: Schema): filter> => { + const minLength = Predicate.isObject(length) ? Math.max(0, Math.floor(length.min)) : Math.max(0, Math.floor(length)) + const maxLength = Predicate.isObject(length) ? Math.max(minLength, Math.floor(length.max)) : minLength + if (minLength !== maxLength) { + return self.pipe( + filter((a) => a.length >= minLength && a.length <= maxLength, { + typeId: LengthTypeId, + description: `a string at least ${minLength} character(s) and at most ${maxLength} character(s) long`, + jsonSchema: { minLength, maxLength }, + ...annotations + }) + ) + } + return self.pipe( + filter((a) => a.length === minLength, { + typeId: LengthTypeId, + description: minLength === 1 ? `a single character` : `a string ${minLength} character(s) long`, + jsonSchema: { minLength, maxLength: minLength }, + ...annotations + }) + ) +} + +/** + * A schema representing a single character. + * + * @category string constructors + * @since 3.10.0 + */ +export class Char extends String$.pipe(length(1, { identifier: "Char" })) {} + +/** + * @category string filters + * @since 3.10.0 + */ +export const nonEmptyString = ( + annotations?: Annotations.Filter +): (self: Schema) => filter> => + minLength(1, { + description: "a non empty string", + ...annotations + }) + +/** + * This schema converts a string to lowercase. + * + * @category string transformations + * @since 3.10.0 + */ +export class Lowercase extends transform( + String$.annotations({ description: "a string that will be converted to lowercase" }), + Lowercased, + { strict: true, decode: (s) => s.toLowerCase(), encode: identity } +).annotations({ identifier: "Lowercase" }) {} + +/** + * This schema converts a string to uppercase. + * + * @category string transformations + * @since 3.10.0 + */ +export class Uppercase extends transform( + String$.annotations({ description: "a string that will be converted to uppercase" }), + Uppercased, + { strict: true, decode: (s) => s.toUpperCase(), encode: identity } +).annotations({ identifier: "Uppercase" }) {} + +/** + * This schema converts a string to capitalized one. + * + * @category string transformations + * @since 3.10.0 + */ +export class Capitalize extends transform( + String$.annotations({ description: "a string that will be converted to a capitalized format" }), + Capitalized, + { strict: true, decode: (s) => string_.capitalize(s), encode: identity } +).annotations({ identifier: "Capitalize" }) {} + +/** + * This schema converts a string to uncapitalized one. + * + * @category string transformations + * @since 3.10.0 + */ +export class Uncapitalize extends transform( + String$.annotations({ description: "a string that will be converted to an uncapitalized format" }), + Uncapitalized, + { strict: true, decode: (s) => string_.uncapitalize(s), encode: identity } +).annotations({ identifier: "Uncapitalize" }) {} + +/** + * @category string constructors + * @since 3.10.0 + */ +export class Trimmed extends String$.pipe( + trimmed({ identifier: "Trimmed", title: "Trimmed" }) +) {} + +/** + * Useful for validating strings that must contain meaningful characters without + * leading or trailing whitespace. + * + * @example + * import { Schema } from "effect" + * + * console.log(Schema.decodeOption(Schema.NonEmptyTrimmedString)("")) // Option.none() + * console.log(Schema.decodeOption(Schema.NonEmptyTrimmedString)(" a ")) // Option.none() + * console.log(Schema.decodeOption(Schema.NonEmptyTrimmedString)("a")) // Option.some("a") + * + * @category string constructors + * @since 3.10.0 + */ +export class NonEmptyTrimmedString extends Trimmed.pipe( + nonEmptyString({ identifier: "NonEmptyTrimmedString", title: "NonEmptyTrimmedString" }) +) {} + +/** + * This schema allows removing whitespaces from the beginning and end of a string. + * + * @category string transformations + * @since 3.10.0 + */ +export class Trim extends transform( + String$.annotations({ description: "a string that will be trimmed" }), + Trimmed, + { strict: true, decode: (s) => s.trim(), encode: identity } +).annotations({ identifier: "Trim" }) {} + +/** + * Returns a schema that allows splitting a string into an array of strings. + * + * @category string transformations + * @since 3.10.0 + */ +export const split = (separator: string): transform> => + transform( + String$.annotations({ description: "a string that will be split" }), + Array$(String$), + { strict: true, decode: string_.split(separator), encode: array_.join(separator) } + ) + +/** + * @since 3.10.0 + */ +export type ParseJsonOptions = { + readonly reviver?: Parameters[1] + readonly replacer?: Parameters[1] + readonly space?: Parameters[2] +} + +const JsonString = String$.annotations({ + [AST.IdentifierAnnotationId]: "JsonString", + [AST.TitleAnnotationId]: "JsonString", + [AST.DescriptionAnnotationId]: "a JSON string" +}) + +const getParseJsonTransformation = (options?: ParseJsonOptions) => + transformOrFail( + JsonString, + Unknown, + { + strict: true, + decode: (s, _, ast) => + ParseResult.try({ + try: () => JSON.parse(s, options?.reviver), + catch: (e: any) => new ParseResult.Type(ast, s, e.message) + }), + encode: (u, _, ast) => + ParseResult.try({ + try: () => JSON.stringify(u, options?.replacer, options?.space), + catch: (e: any) => new ParseResult.Type(ast, u, e.message) + }) + } + ).annotations({ typeId: filters_.ParseJsonTypeId }) + +/** + * The `ParseJson` combinator provides a method to convert JSON strings into the `unknown` type using the underlying + * functionality of `JSON.parse`. It also utilizes `JSON.stringify` for encoding. + * + * You can optionally provide a `ParseJsonOptions` to configure both `JSON.parse` and `JSON.stringify` executions. + * + * Optionally, you can pass a schema `Schema` to obtain an `A` type instead of `unknown`. + * + * @example + * import * as S from "effect/Schema" + * + * assert.deepStrictEqual(S.decodeUnknownSync(S.parseJson())(`{"a":"1"}`), { a: "1" }) + * assert.deepStrictEqual(S.decodeUnknownSync(S.parseJson(S.Struct({ a: S.NumberFromString })))(`{"a":"1"}`), { a: 1 }) + * + * @category string transformations + * @since 3.10.0 + */ +export const parseJson: { + (schema: Schema, options?: ParseJsonOptions): SchemaClass + (options?: ParseJsonOptions): SchemaClass +} = (schema?: Schema | ParseJsonOptions, o?: ParseJsonOptions) => + isSchema(schema) + ? compose(parseJson(o), schema) as any + : getParseJsonTransformation(schema as ParseJsonOptions | undefined) + +/** + * @category string constructors + * @since 3.10.0 + */ +export class NonEmptyString extends String$.pipe( + nonEmptyString({ identifier: "NonEmptyString", title: "NonEmptyString" }) +) {} + +/** + * @category type id + * @since 3.10.0 + */ +export const UUIDTypeId: unique symbol = Symbol.for("effect/Schema/TypeId/UUID") + +const uuidRegexp = /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/i + +/** + * Represents a Universally Unique Identifier (UUID). + * + * This schema ensures that the provided string adheres to the standard UUID format. + * + * @category string constructors + * @since 3.10.0 + */ +export class UUID extends String$.pipe( + pattern(uuidRegexp, { + typeId: UUIDTypeId, + identifier: "UUID", + title: "UUID", + description: "a Universally Unique Identifier", + arbitrary: (): LazyArbitrary => (fc) => fc.uuid() + }) +) {} + +/** + * @category type id + * @since 3.10.0 + */ +export const ULIDTypeId: unique symbol = Symbol.for("effect/Schema/TypeId/ULID") + +const ulidRegexp = /^[0-7][0-9A-HJKMNP-TV-Z]{25}$/i + +/** + * Represents a Universally Unique Lexicographically Sortable Identifier (ULID). + * + * ULIDs are designed to be compact, URL-safe, and ordered, making them suitable for use as identifiers. + * This schema ensures that the provided string adheres to the standard ULID format. + * + * @category string constructors + * @since 3.10.0 + */ +export class ULID extends String$.pipe( + pattern(ulidRegexp, { + typeId: ULIDTypeId, + identifier: "ULID", + title: "ULID", + description: "a Universally Unique Lexicographically Sortable Identifier", + arbitrary: (): LazyArbitrary => (fc) => fc.ulid() + }) +) {} + +/** + * @category type id + * @since 3.10.0 + */ +export const FiniteTypeId: unique symbol = Symbol.for("effect/Schema/TypeId/Finite") + +/** + * Ensures that the provided value is a finite number. + * + * This schema filters out non-finite numeric values, allowing only finite numbers to pass through. + * + * @category number filters + * @since 3.10.0 + */ +export const finite = + (annotations?: Annotations.Filter) => (self: Schema): filter> => + self.pipe( + filter((a) => Number.isFinite(a), { + typeId: FiniteTypeId, + description: "a finite number", + ...annotations + }) + ) + +/** + * @category type id + * @since 3.10.0 + */ +export const GreaterThanTypeId: unique symbol = filters_.GreaterThanTypeId + +/** + * @category type id + * @since 3.10.0 + */ +export type GreaterThanTypeId = typeof GreaterThanTypeId + +/** + * This filter checks whether the provided number is greater than the specified minimum. + * + * @category number filters + * @since 3.10.0 + */ +export const greaterThan = ( + min: number, + annotations?: Annotations.Filter +) => +(self: Schema): filter> => + self.pipe( + filter((a) => a > min, { + typeId: GreaterThanTypeId, + description: min === 0 ? "a positive number" : `a number greater than ${min}`, + jsonSchema: { exclusiveMinimum: min }, + ...annotations + }) + ) + +/** + * @category type id + * @since 3.10.0 + */ +export const GreaterThanOrEqualToTypeId: unique symbol = filters_.GreaterThanOrEqualToTypeId + +/** + * @category type id + * @since 3.10.0 + */ +export type GreaterThanOrEqualToTypeId = typeof GreaterThanOrEqualToTypeId + +/** + * This filter checks whether the provided number is greater than or equal to the specified minimum. + * + * @category number filters + * @since 3.10.0 + */ +export const greaterThanOrEqualTo = ( + min: number, + annotations?: Annotations.Filter +) => +(self: Schema): filter> => + self.pipe( + filter((a) => a >= min, { + typeId: GreaterThanOrEqualToTypeId, + description: min === 0 ? "a non-negative number" : `a number greater than or equal to ${min}`, + jsonSchema: { minimum: min }, + ...annotations + }) + ) + +/** + * @category type id + * @since 3.10.0 + */ +export const MultipleOfTypeId: unique symbol = Symbol.for("effect/Schema/TypeId/MultipleOf") + +/** + * @category number filters + * @since 3.10.0 + */ +export const multipleOf = ( + divisor: number, + annotations?: Annotations.Filter +) => +(self: Schema): filter> => + self.pipe( + filter((a) => number_.remainder(a, divisor) === 0, { + typeId: MultipleOfTypeId, + description: `a number divisible by ${divisor}`, + jsonSchema: { multipleOf: Math.abs(divisor) }, // spec requires positive divisor + ...annotations + }) + ) + +/** + * @category type id + * @since 3.10.0 + */ +export const IntTypeId: unique symbol = filters_.IntTypeId + +/** + * @category type id + * @since 3.10.0 + */ +export type IntTypeId = typeof IntTypeId + +/** + * @category number filters + * @since 3.10.0 + */ +export const int = + (annotations?: Annotations.Filter) => (self: Schema): filter> => + self.pipe( + filter((a) => Number.isSafeInteger(a), { + typeId: IntTypeId, + title: "integer", + description: "an integer", + jsonSchema: { type: "integer" }, + ...annotations + }) + ) + +/** + * @category type id + * @since 3.10.0 + */ +export const LessThanTypeId: unique symbol = filters_.LessThanTypeId + +/** + * @category type id + * @since 3.10.0 + */ +export type LessThanTypeId = typeof LessThanTypeId + +/** + * This filter checks whether the provided number is less than the specified maximum. + * + * @category number filters + * @since 3.10.0 + */ +export const lessThan = + (max: number, annotations?: Annotations.Filter) => + (self: Schema): filter> => + self.pipe( + filter((a) => a < max, { + typeId: LessThanTypeId, + description: max === 0 ? "a negative number" : `a number less than ${max}`, + jsonSchema: { exclusiveMaximum: max }, + ...annotations + }) + ) + +/** + * @category type id + * @since 3.10.0 + */ +export const LessThanOrEqualToTypeId: unique symbol = filters_.LessThanOrEqualToTypeId + +/** + * @category type id + * @since 3.10.0 + */ +export type LessThanOrEqualToTypeId = typeof LessThanOrEqualToTypeId + +/** + * This schema checks whether the provided number is less than or equal to the specified maximum. + * + * @category number filters + * @since 3.10.0 + */ +export const lessThanOrEqualTo = ( + max: number, + annotations?: Annotations.Filter +) => +(self: Schema): filter> => + self.pipe( + filter((a) => a <= max, { + typeId: LessThanOrEqualToTypeId, + description: max === 0 ? "a non-positive number" : `a number less than or equal to ${max}`, + jsonSchema: { maximum: max }, + ...annotations + }) + ) + +/** + * @category type id + * @since 3.10.0 + */ +export const BetweenTypeId: unique symbol = filters_.BetweenTypeId + +/** + * @category type id + * @since 3.10.0 + */ +export type BetweenTypeId = typeof BetweenTypeId + +/** + * This filter checks whether the provided number falls within the specified minimum and maximum values. + * + * @category number filters + * @since 3.10.0 + */ +export const between = ( + min: number, + max: number, + annotations?: Annotations.Filter +) => +(self: Schema): filter> => + self.pipe( + filter((a) => a >= min && a <= max, { + typeId: BetweenTypeId, + description: `a number between ${min} and ${max}`, + jsonSchema: { maximum: max, minimum: min }, + ...annotations + }) + ) + +/** + * @category type id + * @since 3.10.0 + */ +export const NonNaNTypeId: unique symbol = Symbol.for("effect/Schema/TypeId/NonNaN") + +/** + * @category number filters + * @since 3.10.0 + */ +export const nonNaN = + (annotations?: Annotations.Filter) => (self: Schema): filter> => + self.pipe( + filter((a) => !Number.isNaN(a), { + typeId: NonNaNTypeId, + description: "a number excluding NaN", + ...annotations + }) + ) + +/** + * @category number filters + * @since 3.10.0 + */ +export const positive = ( + annotations?: Annotations.Filter +): (self: Schema) => filter> => greaterThan(0, annotations) + +/** + * @category number filters + * @since 3.10.0 + */ +export const negative = ( + annotations?: Annotations.Filter +): (self: Schema) => filter> => lessThan(0, annotations) + +/** + * @category number filters + * @since 3.10.0 + */ +export const nonPositive = ( + annotations?: Annotations.Filter +): (self: Schema) => filter> => lessThanOrEqualTo(0, annotations) + +/** + * @category number filters + * @since 3.10.0 + */ +export const nonNegative = ( + annotations?: Annotations.Filter +): (self: Schema) => filter> => greaterThanOrEqualTo(0, annotations) + +/** + * Clamps a number between a minimum and a maximum value. + * + * @category number transformations + * @since 3.10.0 + */ +export const clamp = + (minimum: number, maximum: number) => + (self: Schema): transform, filter>> => + transform( + self, + self.pipe(typeSchema, between(minimum, maximum)), + { strict: false, decode: (self) => number_.clamp(self, { minimum, maximum }), encode: identity } + ) + +/** + * Transforms a `string` into a `number` by parsing the string using the `parse` function of the `effect/Number` module. + * + * It returns an error if the value can't be converted (for example when non-numeric characters are provided). + * + * The following special string values are supported: "NaN", "Infinity", "-Infinity". + * + * @category number transformations + * @since 3.10.0 + */ +export const parseNumber = ( + self: Schema +): transformOrFail, typeof Number$> => + transformOrFail( + self, + Number$, + { + strict: false, + decode: (s, _, ast) => ParseResult.fromOption(number_.parse(s), () => new ParseResult.Type(ast, s)), + encode: (n) => ParseResult.succeed(String(n)) + } + ) + +/** + * This schema transforms a `string` into a `number` by parsing the string using the `parse` function of the `effect/Number` module. + * + * It returns an error if the value can't be converted (for example when non-numeric characters are provided). + * + * The following special string values are supported: "NaN", "Infinity", "-Infinity". + * + * @category number constructors + * @since 3.10.0 + */ +export class NumberFromString extends parseNumber(String$.annotations({ + description: "a string that will be parsed into a number" +})).annotations({ identifier: "NumberFromString" }) {} + +/** + * @category number constructors + * @since 3.10.0 + */ +export class Finite extends Number$.pipe(finite({ identifier: "Finite", title: "Finite" })) {} + +/** + * @category number constructors + * @since 3.10.0 + */ +export class Int extends Number$.pipe(int({ identifier: "Int", title: "Int" })) {} + +/** + * @category number constructors + * @since 3.10.0 + */ +export class NonNaN extends Number$.pipe(nonNaN({ identifier: "NonNaN", title: "NonNaN" })) {} + +/** + * @category number constructors + * @since 3.10.0 + */ +export class Positive extends Number$.pipe( + positive({ identifier: "Positive", title: "Positive" }) +) {} + +/** + * @category number constructors + * @since 3.10.0 + */ +export class Negative extends Number$.pipe( + negative({ identifier: "Negative", title: "Negative" }) +) {} + +/** + * @category number constructors + * @since 3.10.0 + */ +export class NonPositive extends Number$.pipe( + nonPositive({ identifier: "NonPositive", title: "NonPositive" }) +) {} + +/** + * @category number constructors + * @since 3.10.0 + */ +export class NonNegative extends Number$.pipe( + nonNegative({ identifier: "NonNegative", title: "NonNegative" }) +) {} + +/** + * @category type id + * @since 3.10.0 + */ +export const JsonNumberTypeId: unique symbol = Symbol.for("effect/Schema/TypeId/JsonNumber") + +/** + * The `JsonNumber` is a schema for representing JSON numbers. It ensures that the provided value is a valid + * number by filtering out `NaN` and `(+/-) Infinity`. This is useful when you want to validate and represent numbers in JSON + * format. + * + * @example + * import * as S from "effect/Schema" + * + * const is = S.is(S.JsonNumber) + * + * assert.deepStrictEqual(is(42), true) + * assert.deepStrictEqual(is(Number.NaN), false) + * assert.deepStrictEqual(is(Number.POSITIVE_INFINITY), false) + * assert.deepStrictEqual(is(Number.NEGATIVE_INFINITY), false) + * + * @category number constructors + * @since 3.10.0 + */ +export class JsonNumber extends Number$.pipe( + filter((n) => !Number.isNaN(n) && Number.isFinite(n), { + typeId: JsonNumberTypeId, + identifier: "JsonNumber", + title: "JSON-compatible number", + description: "a JSON-compatible number, excluding NaN, +Infinity, and -Infinity", + jsonSchema: { type: "number" } + }) +) {} + +/** + * @category boolean transformations + * @since 3.10.0 + */ +export class Not extends transform(Boolean$.annotations({ description: "a boolean that will be negated" }), Boolean$, { + strict: true, + decode: boolean_.not, + encode: boolean_.not +}) {} + +/** @ignore */ +class Symbol$ extends transform( + String$.annotations({ description: "a string that will be converted to a symbol" }), + SymbolFromSelf, + { strict: false, decode: (s) => Symbol.for(s), encode: (sym) => sym.description } +).annotations({ identifier: "symbol" }) {} + +export { + /** + * This schema transforms a `string` into a `symbol`. + * + * @category symbol transformations + * @since 3.10.0 + */ + Symbol$ as Symbol +} + +/** + * @category type id + * @since 3.10.0 + */ +export const GreaterThanBigIntTypeId: unique symbol = filters_.GreaterThanBigintTypeId + +/** + * @category type id + * @since 3.10.0 + */ +export type GreaterThanBigIntTypeId = typeof GreaterThanBigIntTypeId + +/** + * @category bigint filters + * @since 3.10.0 + */ +export const greaterThanBigInt = ( + min: bigint, + annotations?: Annotations.Filter +) => +(self: Schema): filter> => + self.pipe( + filter((a) => a > min, { + typeId: { id: GreaterThanBigIntTypeId, annotation: { min } }, + description: min === 0n ? "a positive bigint" : `a bigint greater than ${min}n`, + ...annotations + }) + ) + +/** + * @category type id + * @since 3.10.0 + */ +export const GreaterThanOrEqualToBigIntTypeId: unique symbol = filters_.GreaterThanOrEqualToBigIntTypeId + +/** + * @category type id + * @since 3.10.0 + */ +export type GreaterThanOrEqualToBigIntTypeId = typeof GreaterThanOrEqualToBigIntTypeId + +/** + * @category bigint filters + * @since 3.10.0 + */ +export const greaterThanOrEqualToBigInt = ( + min: bigint, + annotations?: Annotations.Filter +) => +(self: Schema): filter> => + self.pipe( + filter((a) => a >= min, { + typeId: { id: GreaterThanOrEqualToBigIntTypeId, annotation: { min } }, + description: min === 0n + ? "a non-negative bigint" + : `a bigint greater than or equal to ${min}n`, + ...annotations + }) + ) + +/** + * @category type id + * @since 3.10.0 + */ +export const LessThanBigIntTypeId: unique symbol = filters_.LessThanBigIntTypeId + +/** + * @category type id + * @since 3.10.0 + */ +export type LessThanBigIntTypeId = typeof LessThanBigIntTypeId + +/** + * @category bigint filters + * @since 3.10.0 + */ +export const lessThanBigInt = ( + max: bigint, + annotations?: Annotations.Filter +) => +(self: Schema): filter> => + self.pipe( + filter((a) => a < max, { + typeId: { id: LessThanBigIntTypeId, annotation: { max } }, + description: max === 0n ? "a negative bigint" : `a bigint less than ${max}n`, + ...annotations + }) + ) + +/** + * @category type id + * @since 3.10.0 + */ +export const LessThanOrEqualToBigIntTypeId: unique symbol = filters_.LessThanOrEqualToBigIntTypeId + +/** + * @category type id + * @since 3.10.0 + */ +export type LessThanOrEqualToBigIntTypeId = typeof LessThanOrEqualToBigIntTypeId + +/** + * @category bigint filters + * @since 3.10.0 + */ +export const lessThanOrEqualToBigInt = ( + max: bigint, + annotations?: Annotations.Filter +) => +(self: Schema): filter> => + self.pipe( + filter((a) => a <= max, { + typeId: { id: LessThanOrEqualToBigIntTypeId, annotation: { max } }, + description: max === 0n ? "a non-positive bigint" : `a bigint less than or equal to ${max}n`, + ...annotations + }) + ) + +/** + * @category type id + * @since 3.10.0 + */ +export const BetweenBigIntTypeId: unique symbol = filters_.BetweenBigintTypeId + +/** + * @category type id + * @since 3.10.0 + */ +export type BetweenBigIntTypeId = typeof BetweenBigIntTypeId + +/** + * @category bigint filters + * @since 3.10.0 + */ +export const betweenBigInt = ( + min: bigint, + max: bigint, + annotations?: Annotations.Filter +) => +(self: Schema): filter> => + self.pipe( + filter((a) => a >= min && a <= max, { + typeId: { id: BetweenBigIntTypeId, annotation: { max, min } }, + description: `a bigint between ${min}n and ${max}n`, + ...annotations + }) + ) + +/** + * @category bigint filters + * @since 3.10.0 + */ +export const positiveBigInt = ( + annotations?: Annotations.Filter +): (self: Schema) => filter> => greaterThanBigInt(0n, annotations) + +/** + * @category bigint filters + * @since 3.10.0 + */ +export const negativeBigInt = ( + annotations?: Annotations.Filter +): (self: Schema) => filter> => lessThanBigInt(0n, annotations) + +/** + * @category bigint filters + * @since 3.10.0 + */ +export const nonNegativeBigInt = ( + annotations?: Annotations.Filter +): (self: Schema) => filter> => greaterThanOrEqualToBigInt(0n, annotations) + +/** + * @category bigint filters + * @since 3.10.0 + */ +export const nonPositiveBigInt = ( + annotations?: Annotations.Filter +): (self: Schema) => filter> => lessThanOrEqualToBigInt(0n, annotations) + +/** + * Clamps a bigint between a minimum and a maximum value. + * + * @category bigint transformations + * @since 3.10.0 + */ +export const clampBigInt = + (minimum: bigint, maximum: bigint) => + (self: Schema): transform, filter>> => + transform( + self, + self.pipe(typeSchema, betweenBigInt(minimum, maximum)), + { strict: false, decode: (self) => bigInt_.clamp(self, { minimum, maximum }), encode: identity } + ) + +/** @ignore */ +class BigInt$ extends transformOrFail( + String$.annotations({ description: "a string that will be parsed into a bigint" }), + BigIntFromSelf, + { + strict: true, + decode: (s, _, ast) => ParseResult.fromOption(bigInt_.fromString(s), () => new ParseResult.Type(ast, s)), + encode: (n) => ParseResult.succeed(String(n)) + } +).annotations({ identifier: "bigint" }) {} + +export { + /** + * This schema transforms a `string` into a `bigint` by parsing the string using the `BigInt` function. + * + * It returns an error if the value can't be converted (for example when non-numeric characters are provided). + * + * @category bigint transformations + * @since 3.10.0 + */ + BigInt$ as BigInt +} + +/** + * @category bigint constructors + * @since 3.10.0 + */ +export const PositiveBigIntFromSelf: filter> = BigIntFromSelf.pipe( + positiveBigInt({ identifier: "PositiveBigintFromSelf", title: "PositiveBigintFromSelf" }) +) + +/** + * @category bigint constructors + * @since 3.10.0 + */ +export const PositiveBigInt: filter> = BigInt$.pipe( + positiveBigInt({ identifier: "PositiveBigint", title: "PositiveBigint" }) +) + +/** + * @category bigint constructors + * @since 3.10.0 + */ +export const NegativeBigIntFromSelf: filter> = BigIntFromSelf.pipe( + negativeBigInt({ identifier: "NegativeBigintFromSelf", title: "NegativeBigintFromSelf" }) +) + +/** + * @category bigint constructors + * @since 3.10.0 + */ +export const NegativeBigInt: filter> = BigInt$.pipe( + negativeBigInt({ identifier: "NegativeBigint", title: "NegativeBigint" }) +) + +/** + * @category bigint constructors + * @since 3.10.0 + */ +export const NonPositiveBigIntFromSelf: filter> = BigIntFromSelf.pipe( + nonPositiveBigInt({ identifier: "NonPositiveBigintFromSelf", title: "NonPositiveBigintFromSelf" }) +) + +/** + * @category bigint constructors + * @since 3.10.0 + */ +export const NonPositiveBigInt: filter> = BigInt$.pipe( + nonPositiveBigInt({ identifier: "NonPositiveBigint", title: "NonPositiveBigint" }) +) + +/** + * @category bigint constructors + * @since 3.10.0 + */ +export const NonNegativeBigIntFromSelf: filter> = BigIntFromSelf.pipe( + nonNegativeBigInt({ identifier: "NonNegativeBigintFromSelf", title: "NonNegativeBigintFromSelf" }) +) + +/** + * @category bigint constructors + * @since 3.10.0 + */ +export const NonNegativeBigInt: filter> = BigInt$.pipe( + nonNegativeBigInt({ identifier: "NonNegativeBigint", title: "NonNegativeBigint" }) +) + +/** + * This schema transforms a `number` into a `bigint` by parsing the number using the `BigInt` function. + * + * It returns an error if the value can't be safely encoded as a `number` due to being out of range. + * + * @category bigint transformations + * @since 3.10.0 + */ +export class BigIntFromNumber extends transformOrFail( + Number$.annotations({ description: "a number that will be parsed into a bigint" }), + BigIntFromSelf, + { + strict: true, + decode: (n, _, ast) => + ParseResult.fromOption( + bigInt_.fromNumber(n), + () => new ParseResult.Type(ast, n) + ), + encode: (b, _, ast) => ParseResult.fromOption(bigInt_.toNumber(b), () => new ParseResult.Type(ast, b)) + } +).annotations({ identifier: "BigintFromNumber" }) {} + +const redactedArbitrary = (value: LazyArbitrary): LazyArbitrary> => (fc) => + value(fc).map(redacted_.make) + +const toComposite = ( + eff: Effect.Effect, + onSuccess: (a: A) => B, + ast: AST.AST, + actual: unknown +): Effect.Effect => + ParseResult.mapBoth(eff, { + onFailure: (e) => new ParseResult.Composite(ast, actual, e), + onSuccess + }) + +const redactedParse = ( + decodeUnknown: ParseResult.DecodeUnknown +): ParseResult.DeclarationDecodeUnknown, R> => +(u, options, ast) => + redacted_.isRedacted(u) ? + toComposite(decodeUnknown(redacted_.value(u), options), redacted_.make, ast, u) : + ParseResult.fail(new ParseResult.Type(ast, u)) + +/** + * @category api interface + * @since 3.10.0 + */ +export interface RedactedFromSelf extends + AnnotableClass< + RedactedFromSelf, + redacted_.Redacted>, + redacted_.Redacted>, + Schema.Context + > +{} + +/** + * @category Redacted constructors + * @since 3.10.0 + */ +export const RedactedFromSelf = ( + value: Value +): RedactedFromSelf => + declare( + [value], + { + decode: (value) => redactedParse(ParseResult.decodeUnknown(value)), + encode: (value) => redactedParse(ParseResult.encodeUnknown(value)) + }, + { + description: "Redacted()", + pretty: () => () => "Redacted()", + arbitrary: redactedArbitrary, + equivalence: redacted_.getEquivalence + } + ) + +/** + * @category api interface + * @since 3.10.0 + */ +export interface Redacted extends + AnnotableClass< + Redacted, + redacted_.Redacted>, + Schema.Encoded, + Schema.Context + > +{} + +/** + * A schema that transforms any type `A` into a `Redacted`. + * + * @category Redacted transformations + * @since 3.10.0 + */ +export const Redacted = ( + value: Value +): Redacted => { + return transform( + value, + RedactedFromSelf(typeSchema(value)), + { + strict: true, + decode: (value) => redacted_.make(value), + encode: (value) => redacted_.value(value) + } + ) +} + +/** + * @category Duration constructors + * @since 3.10.0 + */ +export class DurationFromSelf extends declare( + duration_.isDuration, + { + identifier: "DurationFromSelf", + pretty: (): pretty_.Pretty => String, + arbitrary: (): LazyArbitrary => (fc) => + fc.oneof( + fc.constant(duration_.infinity), + fc.bigUint().map((_) => duration_.nanos(_)), + fc.bigUint().map((_) => duration_.micros(_)), + fc.maxSafeNat().map((_) => duration_.millis(_)), + fc.maxSafeNat().map((_) => duration_.seconds(_)), + fc.maxSafeNat().map((_) => duration_.minutes(_)), + fc.maxSafeNat().map((_) => duration_.hours(_)), + fc.maxSafeNat().map((_) => duration_.days(_)), + fc.maxSafeNat().map((_) => duration_.weeks(_)) + ), + equivalence: (): Equivalence.Equivalence => duration_.Equivalence + } +) {} + +/** + * A schema that transforms a `bigint` tuple into a `Duration`. + * Treats the value as the number of nanoseconds. + * + * @category Duration transformations + * @since 3.10.0 + */ +export class DurationFromNanos extends transformOrFail( + BigIntFromSelf.annotations({ description: "a bigint that will be parsed into a Duration" }), + DurationFromSelf, + { + strict: true, + decode: (nanos) => ParseResult.succeed(duration_.nanos(nanos)), + encode: (duration, _, ast) => + option_.match(duration_.toNanos(duration), { + onNone: () => ParseResult.fail(new ParseResult.Type(ast, duration)), + onSome: (val) => ParseResult.succeed(val) + }) + } +).annotations({ identifier: "DurationFromNanos" }) {} + +/** + * A schema that transforms a `number` tuple into a `Duration`. + * Treats the value as the number of milliseconds. + * + * @category Duration transformations + * @since 3.10.0 + */ +export class DurationFromMillis extends transform( + Number$.annotations({ description: "a number that will be parsed into a Duration" }), + DurationFromSelf, + { strict: true, decode: (ms) => duration_.millis(ms), encode: (n) => duration_.toMillis(n) } +).annotations({ identifier: "DurationFromMillis" }) {} + +const hrTime: Schema = Tuple( + NonNegative.pipe( + finite({ + [AST.TitleAnnotationId]: "seconds", + [AST.DescriptionAnnotationId]: "seconds" + }) + ), + NonNegative.pipe( + finite({ + [AST.TitleAnnotationId]: "nanos", + [AST.DescriptionAnnotationId]: "nanos" + }) + ) +) + +/** + * A schema that transforms a `[number, number]` tuple into a `Duration`. + * + * @category Duration transformations + * @since 3.10.0 + */ +export class Duration extends transform( + hrTime.annotations({ description: "a tuple of seconds and nanos that will be parsed into a Duration" }), + DurationFromSelf, + { + strict: true, + decode: ([seconds, nanos]) => duration_.nanos(BigInt(seconds) * BigInt(1e9) + BigInt(nanos)), + encode: (duration) => duration_.toHrTime(duration) + } +).annotations({ identifier: "Duration" }) {} + +/** + * Clamps a `Duration` between a minimum and a maximum value. + * + * @category Duration transformations + * @since 3.10.0 + */ +export const clampDuration = + (minimum: duration_.DurationInput, maximum: duration_.DurationInput) => + (self: Schema): transform, filter>> => + transform( + self, + self.pipe(typeSchema, betweenDuration(minimum, maximum)), + { strict: false, decode: (self) => duration_.clamp(self, { minimum, maximum }), encode: identity } + ) + +/** + * @category type id + * @since 3.10.0 + */ +export const LessThanDurationTypeId: unique symbol = Symbol.for("effect/Schema/TypeId/LessThanDuration") + +/** + * @category Duration filters + * @since 3.10.0 + */ +export const lessThanDuration = ( + max: duration_.DurationInput, + annotations?: Annotations.Filter +) => +(self: Schema): filter> => + self.pipe( + filter((a) => duration_.lessThan(a, max), { + typeId: { id: LessThanDurationTypeId, annotation: { max } }, + description: `a Duration less than ${duration_.decode(max)}`, + ...annotations + }) + ) + +/** + * @category type id + * @since 3.10.0 + */ +export const LessThanOrEqualToDurationTypeId: unique symbol = Symbol.for( + "effect/Schema/TypeId/LessThanOrEqualToDuration" +) + +/** + * @category Duration filters + * @since 3.10.0 + */ +export const lessThanOrEqualToDuration = ( + max: duration_.DurationInput, + annotations?: Annotations.Filter +) => +(self: Schema): filter> => + self.pipe( + filter((a) => duration_.lessThanOrEqualTo(a, max), { + typeId: { id: LessThanDurationTypeId, annotation: { max } }, + description: `a Duration less than or equal to ${duration_.decode(max)}`, + ...annotations + }) + ) + +/** + * @category type id + * @since 3.10.0 + */ +export const GreaterThanDurationTypeId: unique symbol = Symbol.for("effect/Schema/TypeId/GreaterThanDuration") + +/** + * @category Duration filters + * @since 3.10.0 + */ +export const greaterThanDuration = ( + min: duration_.DurationInput, + annotations?: Annotations.Filter +) => +(self: Schema): filter> => + self.pipe( + filter((a) => duration_.greaterThan(a, min), { + typeId: { id: GreaterThanDurationTypeId, annotation: { min } }, + description: `a Duration greater than ${duration_.decode(min)}`, + ...annotations + }) + ) + +/** + * @category type id + * @since 3.10.0 + */ +export const GreaterThanOrEqualToDurationTypeId: unique symbol = Symbol.for( + "effect/Schema/TypeId/GreaterThanOrEqualToDuration" +) + +/** + * @category Duration filters + * @since 3.10.0 + */ +export const greaterThanOrEqualToDuration = ( + min: duration_.DurationInput, + annotations?: Annotations.Filter +) => +(self: Schema): filter> => + self.pipe( + filter((a) => duration_.greaterThanOrEqualTo(a, min), { + typeId: { id: GreaterThanOrEqualToDurationTypeId, annotation: { min } }, + description: `a Duration greater than or equal to ${duration_.decode(min)}`, + ...annotations + }) + ) + +/** + * @category type id + * @since 3.10.0 + */ +export const BetweenDurationTypeId: unique symbol = Symbol.for("effect/Schema/TypeId/BetweenDuration") + +/** + * @category Duration filters + * @since 3.10.0 + */ +export const betweenDuration = ( + minimum: duration_.DurationInput, + maximum: duration_.DurationInput, + annotations?: Annotations.Filter +) => +(self: Schema): filter> => + self.pipe( + filter((a) => duration_.between(a, { minimum, maximum }), { + typeId: { id: BetweenDurationTypeId, annotation: { maximum, minimum } }, + description: `a Duration between ${duration_.decode(minimum)} and ${duration_.decode(maximum)}`, + ...annotations + }) + ) + +/** + * @category Uint8Array constructors + * @since 3.10.0 + */ +export const Uint8ArrayFromSelf: Schema = declare( + Predicate.isUint8Array, + { + identifier: "Uint8ArrayFromSelf", + pretty: (): pretty_.Pretty => (u8arr) => `new Uint8Array(${JSON.stringify(Array.from(u8arr))})`, + arbitrary: (): LazyArbitrary => (fc) => fc.uint8Array(), + equivalence: (): Equivalence.Equivalence => array_.getEquivalence(Equal.equals) as any + } +) + +const Uint8Array$: Schema> = transform( + Array$(Number$.pipe( + between(0, 255, { + title: "8-bit unsigned integer", + description: "a 8-bit unsigned integer" + }) + )).annotations({ description: "an array of 8-bit unsigned integers that will be parsed into a Uint8Array" }), + Uint8ArrayFromSelf, + { strict: true, decode: (numbers) => Uint8Array.from(numbers), encode: (uint8Array) => Array.from(uint8Array) } +).annotations({ identifier: "Uint8Array" }) + +export { + /** + * A schema that transforms an array of numbers into a `Uint8Array`. + * + * @category Uint8Array transformations + * @since 3.10.0 + */ + Uint8Array$ as Uint8Array +} + +const makeUint8ArrayTransformation = ( + id: string, + decode: (s: string) => either_.Either, + encode: (u: Uint8Array) => string +) => + transformOrFail( + String$.annotations({ description: "a string that will be parsed into a Uint8Array" }), + Uint8ArrayFromSelf, + { + strict: true, + decode: (s, _, ast) => + either_.mapLeft( + decode(s), + (decodeException) => new ParseResult.Type(ast, s, decodeException.message) + ), + encode: (u) => ParseResult.succeed(encode(u)) + } + ).annotations({ identifier: id }) + +/** + * Decodes a base64 (RFC4648) encoded string into a `Uint8Array`. + * + * @category Uint8Array transformations + * @since 3.10.0 + */ +export const Uint8ArrayFromBase64: Schema = makeUint8ArrayTransformation( + "Uint8ArrayFromBase64", + Encoding.decodeBase64, + Encoding.encodeBase64 +) + +/** + * Decodes a base64 (URL) encoded string into a `Uint8Array`. + * + * @category Uint8Array transformations + * @since 3.10.0 + */ +export const Uint8ArrayFromBase64Url: Schema = makeUint8ArrayTransformation( + "Uint8ArrayFromBase64Url", + Encoding.decodeBase64Url, + Encoding.encodeBase64Url +) + +/** + * Decodes a hex encoded string into a `Uint8Array`. + * + * @category Uint8Array transformations + * @since 3.10.0 + */ +export const Uint8ArrayFromHex: Schema = makeUint8ArrayTransformation( + "Uint8ArrayFromHex", + Encoding.decodeHex, + Encoding.encodeHex +) + +const makeEncodingTransformation = ( + id: string, + decode: (s: string) => either_.Either, + encode: (u: string) => string +) => + transformOrFail( + String$.annotations({ + description: `A string that is interpreted as being ${id}-encoded and will be decoded into a UTF-8 string` + }), + String$, + { + strict: true, + decode: (s, _, ast) => + either_.mapLeft( + decode(s), + (decodeException) => new ParseResult.Type(ast, s, decodeException.message) + ), + encode: (u) => ParseResult.succeed(encode(u)) + } + ).annotations({ identifier: `StringFrom${id}` }) + +/** + * Decodes a base64 (RFC4648) encoded string into a UTF-8 string. + * + * @category string transformations + * @since 3.10.0 + */ +export const StringFromBase64: Schema = makeEncodingTransformation( + "Base64", + Encoding.decodeBase64String, + Encoding.encodeBase64 +) + +/** + * Decodes a base64 (URL) encoded string into a UTF-8 string. + * + * @category string transformations + * @since 3.10.0 + */ +export const StringFromBase64Url: Schema = makeEncodingTransformation( + "Base64Url", + Encoding.decodeBase64UrlString, + Encoding.encodeBase64Url +) + +/** + * Decodes a hex encoded string into a UTF-8 string. + * + * @category string transformations + * @since 3.10.0 + */ +export const StringFromHex: Schema = makeEncodingTransformation( + "Hex", + Encoding.decodeHexString, + Encoding.encodeHex +) + +/** + * @category type id + * @since 3.10.0 + */ +export const MinItemsTypeId: unique symbol = filters_.MinItemsTypeId + +/** + * @category type id + * @since 3.10.0 + */ +export type MinItemsTypeId = typeof MinItemsTypeId + +/** + * @category ReadonlyArray filters + * @since 3.10.0 + */ +export const minItems = ( + n: number, + annotations?: Annotations.Filter> +) => +(self: Schema, I, R>): filter, I, R>> => { + const minItems = Math.floor(n) + if (minItems < 1) { + throw new Error( + errors_.getInvalidArgumentErrorMessage(`Expected an integer greater than or equal to 1, actual ${n}`) + ) + } + return self.pipe( + filter( + (a) => a.length >= minItems, + { + typeId: MinItemsTypeId, + description: `an array of at least ${minItems} items`, + jsonSchema: { minItems }, + [AST.StableFilterAnnotationId]: true, + ...annotations + } + ) + ) +} + +/** + * @category type id + * @since 3.10.0 + */ +export const MaxItemsTypeId: unique symbol = filters_.MaxItemsTypeId + +/** + * @category type id + * @since 3.10.0 + */ +export type MaxItemsTypeId = typeof MaxItemsTypeId + +/** + * @category ReadonlyArray filters + * @since 3.10.0 + */ +export const maxItems = ( + n: number, + annotations?: Annotations.Filter> +) => +(self: Schema, I, R>): filter, I, R>> => + self.pipe( + filter((a) => a.length <= n, { + typeId: MaxItemsTypeId, + description: `an array of at most ${n} items`, + jsonSchema: { maxItems: n }, + [AST.StableFilterAnnotationId]: true, + ...annotations + }) + ) + +/** + * @category type id + * @since 3.10.0 + */ +export const ItemsCountTypeId: unique symbol = filters_.ItemsCountTypeId + +/** + * @category type id + * @since 3.10.0 + */ +export type ItemsCountTypeId = typeof ItemsCountTypeId + +/** + * @category ReadonlyArray filters + * @since 3.10.0 + */ +export const itemsCount = ( + n: number, + annotations?: Annotations.Filter> +) => +(self: Schema, I, R>): filter, I, R>> => + self.pipe( + filter((a) => a.length === n, { + typeId: ItemsCountTypeId, + description: `an array of exactly ${n} item(s)`, + jsonSchema: { minItems: n, maxItems: n }, + [AST.StableFilterAnnotationId]: true, + ...annotations + }) + ) + +/** + * @category ReadonlyArray transformations + * @since 3.10.0 + */ +export const getNumberIndexedAccess = , I extends ReadonlyArray, R>( + self: Schema +): SchemaClass => make(AST.getNumberIndexedAccess(self.ast)) + +/** + * Get the first element of a `ReadonlyArray`, or `None` if the array is empty. + * + * @category ReadonlyArray transformations + * @since 3.10.0 + */ +export const head = (self: Schema, I, R>): SchemaClass, I, R> => + transform( + self, + OptionFromSelf(getNumberIndexedAccess(typeSchema(self))), + { strict: true, decode: array_.head, encode: option_.match({ onNone: () => [], onSome: array_.of }) } + ) + +/** + * Retrieves the first element of a `ReadonlyArray`. + * + * If the array is empty, it returns the `fallback` argument if provided; otherwise, it fails. + * + * @category ReadonlyArray transformations + * @since 3.10.0 + */ +export const headOrElse: { + (fallback?: LazyArg): (self: Schema, I, R>) => SchemaClass + (self: Schema, I, R>, fallback?: LazyArg): SchemaClass +} = dual( + (args) => isSchema(args[0]), + (self: Schema, I, R>, fallback?: LazyArg): SchemaClass => + transformOrFail( + self, + getNumberIndexedAccess(typeSchema(self)), + { + strict: true, + decode: (as, _, ast) => + as.length > 0 + ? ParseResult.succeed(as[0]) + : fallback + ? ParseResult.succeed(fallback()) + : ParseResult.fail(new ParseResult.Type(ast, as)), + encode: (a) => ParseResult.succeed(array_.of(a)) + } + ) +) + +/** + * @category type id + * @since 3.10.0 + */ +export const ValidDateTypeId: unique symbol = Symbol.for("effect/Schema/TypeId/ValidDate") + +/** + * Defines a filter that specifically rejects invalid dates, such as `new + * Date("Invalid Date")`. This filter ensures that only properly formatted and + * valid date objects are accepted, enhancing data integrity by preventing + * erroneous date values from being processed. + * + * @category Date filters + * @since 3.10.0 + */ +export const validDate = + (annotations?: Annotations.Filter) => (self: Schema): filter> => + self.pipe( + filter((a) => !Number.isNaN(a.getTime()), { + typeId: ValidDateTypeId, + description: "a valid Date", + ...annotations + }) + ) + +/** + * @category type id + * @since 3.10.0 + */ +export const LessThanDateTypeId: unique symbol = Symbol.for("effect/Schema/TypeId/LessThanDate") + +/** + * @category Date filters + * @since 3.10.0 + */ +export const lessThanDate = ( + max: Date, + annotations?: Annotations.Filter +) => +(self: Schema): filter> => + self.pipe( + filter((a) => a < max, { + typeId: { id: LessThanDateTypeId, annotation: { max } }, + description: `a date before ${util_.formatDate(max)}`, + ...annotations + }) + ) + +/** + * @category type id + * @since 3.10.0 + */ +export const LessThanOrEqualToDateTypeId: unique symbol = Symbol.for( + "effect/Schema/TypeId/LessThanOrEqualToDate" +) + +/** + * @category Date filters + * @since 3.10.0 + */ +export const lessThanOrEqualToDate = ( + max: Date, + annotations?: Annotations.Filter +) => +(self: Schema): filter> => + self.pipe( + filter((a) => a <= max, { + typeId: { id: LessThanDateTypeId, annotation: { max } }, + description: `a date before or equal to ${util_.formatDate(max)}`, + ...annotations + }) + ) + +/** + * @category type id + * @since 3.10.0 + */ +export const GreaterThanDateTypeId: unique symbol = Symbol.for("effect/Schema/TypeId/GreaterThanDate") + +/** + * @category Date filters + * @since 3.10.0 + */ +export const greaterThanDate = ( + min: Date, + annotations?: Annotations.Filter +) => +(self: Schema): filter> => + self.pipe( + filter((a) => a > min, { + typeId: { id: GreaterThanDateTypeId, annotation: { min } }, + description: `a date after ${util_.formatDate(min)}`, + ...annotations + }) + ) + +/** + * @category type id + * @since 3.10.0 + */ +export const GreaterThanOrEqualToDateTypeId: unique symbol = Symbol.for( + "effect/Schema/TypeId/GreaterThanOrEqualToDate" +) + +/** + * @category Date filters + * @since 3.10.0 + */ +export const greaterThanOrEqualToDate = ( + min: Date, + annotations?: Annotations.Filter +) => +(self: Schema): filter> => + self.pipe( + filter((a) => a >= min, { + typeId: { id: GreaterThanOrEqualToDateTypeId, annotation: { min } }, + description: `a date after or equal to ${util_.formatDate(min)}`, + ...annotations + }) + ) + +/** + * @category type id + * @since 3.10.0 + */ +export const BetweenDateTypeId: unique symbol = Symbol.for("effect/Schema/TypeId/BetweenDate") + +/** + * @category Date filters + * @since 3.10.0 + */ +export const betweenDate = ( + minimum: Date, + maximum: Date, + annotations?: Annotations.Filter +) => +(self: Schema): filter> => + self.pipe( + filter((a) => a <= maximum && a >= minimum, { + typeId: { id: BetweenDateTypeId, annotation: { maximum, minimum } }, + description: `a date between ${util_.formatDate(minimum)} and ${util_.formatDate(maximum)}`, + ...annotations + }) + ) + +/** + * Describes a schema that accommodates potentially invalid `Date` instances, + * such as `new Date("Invalid Date")`, without rejection. + * + * @category Date constructors + * @since 3.10.0 + */ +export class DateFromSelf extends declare( + Predicate.isDate, + { + identifier: "DateFromSelf", + description: "a potentially invalid Date instance", + pretty: (): pretty_.Pretty => (date) => `new Date(${JSON.stringify(date)})`, + arbitrary: (): LazyArbitrary => (fc) => fc.date({ noInvalidDate: false }), + equivalence: () => Equivalence.Date + } +) {} + +/** + * Defines a schema that ensures only valid dates are accepted. This schema + * rejects values like `new Date("Invalid Date")`, which, despite being a `Date` + * instance, represents an invalid date. Such stringent validation ensures that + * all date objects processed through this schema are properly formed and + * represent real dates. + * + * @category Date constructors + * @since 3.10.0 + */ +export class ValidDateFromSelf extends DateFromSelf.pipe( + validDate({ + identifier: "ValidDateFromSelf", + description: "a valid Date instance" + }) +) {} + +/** + * Defines a schema that attempts to convert a `string` to a `Date` object using + * the `new Date` constructor. This conversion is lenient, meaning it does not + * reject strings that do not form valid dates (e.g., using `new Date("Invalid + * Date")` results in a `Date` object, despite being invalid). + * + * @category Date transformations + * @since 3.10.0 + */ +export class DateFromString extends transform( + String$.annotations({ description: "a string that will be parsed into a Date" }), + DateFromSelf, + { strict: true, decode: (s) => new Date(s), encode: (d) => util_.formatDate(d) } +).annotations({ identifier: "DateFromString" }) {} + +/** @ignore */ +class Date$ extends DateFromString.pipe( + validDate({ identifier: "Date" }) +) {} + +export { + /** + * This schema converts a `string` into a `Date` object using the `new Date` + * constructor. It ensures that only valid date strings are accepted, + * rejecting any strings that would result in an invalid date, such as `new + * Date("Invalid Date")`. + * + * @category Date transformations + * @since 3.10.0 + */ + Date$ as Date +} + +/** + * Defines a schema that converts a `number` into a `Date` object using the `new + * Date` constructor. This schema does not validate the numerical input, + * allowing potentially invalid values such as `NaN`, `Infinity`, and + * `-Infinity` to be converted into `Date` objects. During the encoding process, + * any invalid `Date` object will be encoded to `NaN`. + * + * @category Date transformations + * @since 3.10.0 + */ +export class DateFromNumber extends transform( + Number$.annotations({ description: "a number that will be parsed into a Date" }), + DateFromSelf, + { strict: true, decode: (n) => new Date(n), encode: (d) => d.getTime() } +).annotations({ identifier: "DateFromNumber" }) {} + +/** + * Describes a schema that represents a `DateTime.Utc` instance. + * + * @category DateTime.Utc constructors + * @since 3.10.0 + */ +export class DateTimeUtcFromSelf extends declare( + (u) => dateTime.isDateTime(u) && dateTime.isUtc(u), + { + identifier: "DateTimeUtcFromSelf", + description: "a DateTime.Utc instance", + pretty: (): pretty_.Pretty => (dateTime) => dateTime.toString(), + arbitrary: (): LazyArbitrary => (fc) => fc.date().map((date) => dateTime.unsafeFromDate(date)), + equivalence: () => dateTime.Equivalence + } +) {} + +const decodeDateTime = (input: A, _: ParseOptions, ast: AST.AST) => + ParseResult.try({ + try: () => dateTime.unsafeMake(input), + catch: () => new ParseResult.Type(ast, input) + }) + +/** + * Defines a schema that attempts to convert a `number` to a `DateTime.Utc` instance using the `DateTime.unsafeMake` constructor. + * + * @category DateTime.Utc transformations + * @since 3.10.0 + */ +export class DateTimeUtcFromNumber extends transformOrFail( + Number$.annotations({ description: "a number that will be parsed into a DateTime.Utc" }), + DateTimeUtcFromSelf, + { + strict: true, + decode: decodeDateTime, + encode: (dt) => ParseResult.succeed(dateTime.toEpochMillis(dt)) + } +).annotations({ identifier: "DateTimeUtcFromNumber" }) {} + +/** + * Defines a schema that attempts to convert a `string` to a `DateTime.Utc` instance using the `DateTime.unsafeMake` constructor. + * + * @category DateTime.Utc transformations + * @since 3.10.0 + */ +export class DateTimeUtc extends transformOrFail( + String$.annotations({ description: "a string that will be parsed into a DateTime.Utc" }), + DateTimeUtcFromSelf, + { + strict: true, + decode: decodeDateTime, + encode: (dt) => ParseResult.succeed(dateTime.formatIso(dt)) + } +).annotations({ identifier: "DateTimeUtc" }) {} + +const timeZoneOffsetArbitrary = (): LazyArbitrary => (fc) => + fc.integer({ min: -12 * 60 * 60 * 1000, max: 12 * 60 * 60 * 1000 }).map(dateTime.zoneMakeOffset) + +/** + * Describes a schema that represents a `TimeZone.Offset` instance. + * + * @category TimeZone constructors + * @since 3.10.0 + */ +export class TimeZoneOffsetFromSelf extends declare( + dateTime.isTimeZoneOffset, + { + identifier: "TimeZoneOffsetFromSelf", + description: "a TimeZone.Offset instance", + pretty: (): pretty_.Pretty => (zone) => zone.toString(), + arbitrary: timeZoneOffsetArbitrary + } +) {} + +/** + * Defines a schema that converts a `number` to a `TimeZone.Offset` instance using the `DateTime.zoneMakeOffset` constructor. + * + * @category TimeZone transformations + * @since 3.10.0 + */ +export class TimeZoneOffset extends transform( + Number$.annotations({ description: "a number that will be parsed into a TimeZone.Offset" }), + TimeZoneOffsetFromSelf, + { strict: true, decode: dateTime.zoneMakeOffset, encode: (tz) => tz.offset } +).annotations({ identifier: "TimeZoneOffset" }) {} + +const timeZoneNamedArbitrary = (): LazyArbitrary => (fc) => + fc.constantFrom(...Intl.supportedValuesOf("timeZone")).map(dateTime.zoneUnsafeMakeNamed) + +/** + * Describes a schema that represents a `TimeZone.Named` instance. + * + * @category TimeZone constructors + * @since 3.10.0 + */ +export class TimeZoneNamedFromSelf extends declare( + dateTime.isTimeZoneNamed, + { + identifier: "TimeZoneNamedFromSelf", + description: "a TimeZone.Named instance", + pretty: (): pretty_.Pretty => (zone) => zone.toString(), + arbitrary: timeZoneNamedArbitrary + } +) {} + +/** + * Defines a schema that attempts to convert a `string` to a `TimeZone.Named` instance using the `DateTime.zoneUnsafeMakeNamed` constructor. + * + * @category TimeZone transformations + * @since 3.10.0 + */ +export class TimeZoneNamed extends transformOrFail( + String$.annotations({ description: "a string that will be parsed into a TimeZone.Named" }), + TimeZoneNamedFromSelf, + { + strict: true, + decode: (s, _, ast) => + ParseResult.try({ + try: () => dateTime.zoneUnsafeMakeNamed(s), + catch: () => new ParseResult.Type(ast, s) + }), + encode: (tz) => ParseResult.succeed(tz.id) + } +).annotations({ identifier: "TimeZoneNamed" }) {} + +/** + * @category api interface + * @since 3.10.0 + */ +export interface TimeZoneFromSelf extends Union<[typeof TimeZoneOffsetFromSelf, typeof TimeZoneNamedFromSelf]> { + annotations(annotations: Annotations.Schema): TimeZoneFromSelf +} + +/** + * @category TimeZone constructors + * @since 3.10.0 + */ +export const TimeZoneFromSelf: TimeZoneFromSelf = Union(TimeZoneOffsetFromSelf, TimeZoneNamedFromSelf) + +/** + * Defines a schema that attempts to convert a `string` to a `TimeZone` using the `DateTime.zoneFromString` constructor. + * + * @category TimeZone transformations + * @since 3.10.0 + */ +export class TimeZone extends transformOrFail( + String$.annotations({ description: "a string that will be parsed into a TimeZone" }), + TimeZoneFromSelf, + { + strict: true, + decode: (s, _, ast) => + option_.match(dateTime.zoneFromString(s), { + onNone: () => ParseResult.fail(new ParseResult.Type(ast, s)), + onSome: ParseResult.succeed + }), + encode: (tz) => ParseResult.succeed(dateTime.zoneToString(tz)) + } +).annotations({ identifier: "TimeZone" }) {} + +const timeZoneArbitrary: LazyArbitrary = (fc) => + fc.oneof( + timeZoneOffsetArbitrary()(fc), + timeZoneNamedArbitrary()(fc) + ) + +/** + * Describes a schema that represents a `DateTime.Zoned` instance. + * + * @category DateTime.Zoned constructors + * @since 3.10.0 + */ +export class DateTimeZonedFromSelf extends declare( + (u) => dateTime.isDateTime(u) && dateTime.isZoned(u), + { + identifier: "DateTimeZonedFromSelf", + description: "a DateTime.Zoned instance", + pretty: (): pretty_.Pretty => (dateTime) => dateTime.toString(), + arbitrary: (): LazyArbitrary => (fc) => + fc.date().chain((date) => timeZoneArbitrary(fc).map((timeZone) => dateTime.unsafeMakeZoned(date, { timeZone }))), + equivalence: () => dateTime.Equivalence + } +) {} + +/** + * Defines a schema that attempts to convert a `string` to a `DateTime.Zoned` instance. + * + * @category DateTime.Zoned transformations + * @since 3.10.0 + */ +export class DateTimeZoned extends transformOrFail( + String$.annotations({ description: "a string that will be parsed into a DateTime.Zoned" }), + DateTimeZonedFromSelf, + { + strict: true, + decode: (s, _, ast) => + option_.match(dateTime.makeZonedFromString(s), { + onNone: () => ParseResult.fail(new ParseResult.Type(ast, s)), + onSome: ParseResult.succeed + }), + encode: (dt) => ParseResult.succeed(dateTime.formatIsoZoned(dt)) + } +).annotations({ identifier: "DateTimeZoned" }) {} + +/** + * @category Option utils + * @since 3.10.0 + */ +export type OptionEncoded = + | { + readonly _tag: "None" + } + | { + readonly _tag: "Some" + readonly value: I + } + +const OptionNoneEncoded = Struct({ + _tag: Literal("None") +}).annotations({ description: "NoneEncoded" }) + +const optionSomeEncoded = (value: Schema) => + Struct({ + _tag: Literal("Some"), + value + }).annotations({ description: `SomeEncoded<${format(value)}>` }) + +const optionEncoded = (value: Schema) => + Union( + OptionNoneEncoded, + optionSomeEncoded(value) + ).annotations({ + description: `OptionEncoded<${format(value)}>` + }) + +const optionDecode = (input: OptionEncoded): option_.Option => + input._tag === "None" ? option_.none() : option_.some(input.value) + +const optionArbitrary = + (value: LazyArbitrary, ctx: GenerationContext): LazyArbitrary> => (fc) => + fc.oneof( + ctx, + fc.record({ _tag: fc.constant("None" as const) }), + fc.record({ _tag: fc.constant("Some" as const), value: value(fc) }) + ).map(optionDecode) + +const optionPretty = (value: pretty_.Pretty): pretty_.Pretty> => + option_.match({ + onNone: () => "none()", + onSome: (a) => `some(${value(a)})` + }) + +const optionParse = + (decodeUnknown: ParseResult.DecodeUnknown): ParseResult.DeclarationDecodeUnknown, R> => + (u, options, ast) => + option_.isOption(u) ? + option_.isNone(u) ? + ParseResult.succeed(option_.none()) + : toComposite(decodeUnknown(u.value, options), option_.some, ast, u) + : ParseResult.fail(new ParseResult.Type(ast, u)) + +/** + * @category api interface + * @since 3.10.0 + */ +export interface OptionFromSelf extends + AnnotableClass< + OptionFromSelf, + option_.Option>, + option_.Option>, + Schema.Context + > +{} + +/** + * @category Option transformations + * @since 3.10.0 + */ +export const OptionFromSelf = ( + value: Value +): OptionFromSelf => { + return declare( + [value], + { + decode: (value) => optionParse(ParseResult.decodeUnknown(value)), + encode: (value) => optionParse(ParseResult.encodeUnknown(value)) + }, + { + description: `Option<${format(value)}>`, + pretty: optionPretty, + arbitrary: optionArbitrary, + equivalence: option_.getEquivalence + } + ) +} + +const makeNoneEncoded = { + _tag: "None" +} as const +const makeSomeEncoded = (value: A) => ({ + _tag: "Some", + value +} as const) + +/** + * @category api interface + * @since 3.10.0 + */ +export interface Option extends + AnnotableClass< + Option, + option_.Option>, + OptionEncoded>, + Schema.Context + > +{} + +/** + * @category Option transformations + * @since 3.10.0 + */ +export const Option = (value: Value): Option => { + const value_ = asSchema(value) + return transform( + optionEncoded(value_), + OptionFromSelf(typeSchema(value_)), + { + strict: true, + decode: optionDecode, + encode: option_.match({ + onNone: () => makeNoneEncoded, + onSome: makeSomeEncoded + }) + } + ) +} + +/** + * @category api interface + * @since 3.10.0 + */ +export interface OptionFromNullOr extends + AnnotableClass< + OptionFromNullOr, + option_.Option>, + Schema.Encoded | null, + Schema.Context + > +{} + +/** + * @category Option transformations + * @since 3.10.0 + */ +export const OptionFromNullOr = ( + value: Value +): OptionFromNullOr => { + const value_ = asSchema(value) + return transform(NullOr(value_), OptionFromSelf(typeSchema(value_)), { + strict: true, + decode: option_.fromNullable, + encode: option_.getOrNull + }) +} + +/** + * @category api interface + * @since 3.10.0 + */ +export interface OptionFromNullishOr extends + AnnotableClass< + OptionFromNullishOr, + option_.Option>, + Schema.Encoded | null | undefined, + Schema.Context + > +{} + +/** + * @category Option transformations + * @since 3.10.0 + */ +export const OptionFromNullishOr = ( + value: Value, + onNoneEncoding: null | undefined +): OptionFromNullishOr => { + const value_ = asSchema(value) + return transform( + NullishOr(value_), + OptionFromSelf(typeSchema(value_)), + { + strict: true, + decode: option_.fromNullable, + encode: onNoneEncoding === null ? option_.getOrNull : option_.getOrUndefined + } + ) +} + +/** + * @category api interface + * @since 3.10.0 + */ +export interface OptionFromUndefinedOr extends + AnnotableClass< + OptionFromUndefinedOr, + option_.Option>, + Schema.Encoded | undefined, + Schema.Context + > +{} + +/** + * @category Option transformations + * @since 3.10.0 + */ +export const OptionFromUndefinedOr = ( + value: Value +): OptionFromUndefinedOr => { + const value_ = asSchema(value) + return transform(UndefinedOr(value_), OptionFromSelf(typeSchema(value_)), { + strict: true, + decode: option_.fromNullable, + encode: option_.getOrUndefined + }) +} + +/** + * Transforms strings into an Option type, effectively filtering out empty or + * whitespace-only strings by trimming them and checking their length. Returns + * `none` for invalid inputs and `some` for valid non-empty strings. + * + * @example + * import { Schema } from "effect" + * + * console.log(Schema.decodeSync(Schema.OptionFromNonEmptyTrimmedString)("")) // Option.none() + * console.log(Schema.decodeSync(Schema.OptionFromNonEmptyTrimmedString)(" a ")) // Option.some("a") + * console.log(Schema.decodeSync(Schema.OptionFromNonEmptyTrimmedString)("a")) // Option.some("a") + * + * @category Option transformations + * @since 3.10.0 + */ +export const OptionFromNonEmptyTrimmedString = transform(String$, OptionFromSelf(NonEmptyTrimmedString), { + strict: true, + decode: (s) => { + const out = s.trim() + return out.length === 0 ? option_.none() : option_.some(out) + }, + encode: option_.getOrElse(() => "") +}) + +/** + * @category Either utils + * @since 3.10.0 + */ +export type RightEncoded = { + readonly _tag: "Right" + readonly right: IA +} + +/** + * @category Either utils + * @since 3.10.0 + */ +export type LeftEncoded = { + readonly _tag: "Left" + readonly left: IE +} + +/** + * @category Either utils + * @since 3.10.0 + */ +export type EitherEncoded = RightEncoded | LeftEncoded + +const rightEncoded = (right: Schema): Schema, RightEncoded, RR> => + Struct({ + _tag: Literal("Right"), + right + }).annotations({ description: `RightEncoded<${format(right)}>` }) + +const leftEncoded = (left: Schema): Schema, LeftEncoded
  • , LR> => + Struct({ + _tag: Literal("Left"), + left + }).annotations({ description: `LeftEncoded<${format(left)}>` }) + +const eitherEncoded = ( + right: Schema, + left: Schema +) => + Union(rightEncoded(right), leftEncoded(left)).annotations({ + description: `EitherEncoded<${format(left)}, ${format(right)}>` + }) + +const eitherDecode = (input: EitherEncoded): either_.Either => + input._tag === "Left" ? either_.left(input.left) : either_.right(input.right) + +const eitherArbitrary = ( + right: LazyArbitrary, + left: LazyArbitrary +): LazyArbitrary> => +(fc) => + fc.oneof( + fc.record({ _tag: fc.constant("Left" as const), left: left(fc) }), + fc.record({ _tag: fc.constant("Right" as const), right: right(fc) }) + ).map(eitherDecode) + +const eitherPretty = ( + right: pretty_.Pretty, + left: pretty_.Pretty +): pretty_.Pretty> => + either_.match({ + onLeft: (e) => `left(${left(e)})`, + onRight: (a) => `right(${right(a)})` + }) + +const eitherParse = ( + parseRight: ParseResult.DecodeUnknown, + decodeUnknownLeft: ParseResult.DecodeUnknown +): ParseResult.DeclarationDecodeUnknown, LR | RR> => +(u, options, ast) => + either_.isEither(u) ? + either_.match(u, { + onLeft: (left) => toComposite(decodeUnknownLeft(left, options), either_.left, ast, u), + onRight: (right) => toComposite(parseRight(right, options), either_.right, ast, u) + }) + : ParseResult.fail(new ParseResult.Type(ast, u)) + +/** + * @category api interface + * @since 3.10.0 + */ +export interface EitherFromSelf extends + AnnotableClass< + EitherFromSelf, + either_.Either, Schema.Type>, + either_.Either, Schema.Encoded>, + Schema.Context | Schema.Context + > +{} + +/** + * @category Either transformations + * @since 3.10.0 + */ +export const EitherFromSelf = ({ left, right }: { + readonly left: L + readonly right: R +}): EitherFromSelf => { + return declare( + [right, left], + { + decode: (right, left) => eitherParse(ParseResult.decodeUnknown(right), ParseResult.decodeUnknown(left)), + encode: (right, left) => eitherParse(ParseResult.encodeUnknown(right), ParseResult.encodeUnknown(left)) + }, + { + description: `Either<${format(right)}, ${format(left)}>`, + pretty: eitherPretty, + arbitrary: eitherArbitrary, + equivalence: (right, left) => either_.getEquivalence({ left, right }) + } + ) +} + +const makeLeftEncoded = (left: E) => (({ + _tag: "Left", + left +}) as const) +const makeRightEncoded = (right: A) => (({ + _tag: "Right", + right +}) as const) + +/** + * @category api interface + * @since 3.10.0 + */ +export interface Either extends + AnnotableClass< + Either, + either_.Either, Schema.Type>, + EitherEncoded, Schema.Encoded>, + Schema.Context | Schema.Context + > +{} + +/** + * @category Either transformations + * @since 3.10.0 + */ +export const Either = ({ left, right }: { + readonly left: L + readonly right: R +}): Either => { + const right_ = asSchema(right) + const left_ = asSchema(left) + return transform( + eitherEncoded(right_, left_), + EitherFromSelf({ left: typeSchema(left_), right: typeSchema(right_) }), + { + strict: true, + decode: eitherDecode, + encode: either_.match({ onLeft: makeLeftEncoded, onRight: makeRightEncoded }) + } + ) +} + +/** + * @category api interface + * @since 3.10.0 + */ +export interface EitherFromUnion extends + AnnotableClass< + EitherFromUnion, + either_.Either, Schema.Type>, + Schema.Encoded | Schema.Encoded, + Schema.Context | Schema.Context + > +{} + +/** + * @example + * import * as Schema from "effect/Schema" + * + * // Schema> + * Schema.EitherFromUnion({ left: Schema.String, right: Schema.Number }) + * + * @category Either transformations + * @since 3.10.0 + */ +export const EitherFromUnion = ({ left, right }: { + readonly left: L + readonly right: R +}): EitherFromUnion => { + const right_ = asSchema(right) + const left_ = asSchema(left) + const toright = typeSchema(right_) + const toleft = typeSchema(left_) + const fromRight = transform(right_, rightEncoded(toright), { + strict: true, + decode: makeRightEncoded, + encode: (r) => r.right + }) + const fromLeft = transform(left_, leftEncoded(toleft), { + strict: true, + decode: makeLeftEncoded, + encode: (l) => l.left + }) + return transform( + Union(fromRight, fromLeft), + EitherFromSelf({ left: toleft, right: toright }), + { + strict: true, + decode: (from) => from._tag === "Left" ? either_.left(from.left) : either_.right(from.right), + encode: either_.match({ onLeft: makeLeftEncoded, onRight: makeRightEncoded }) + } + ) +} + +const mapArbitrary = ( + key: LazyArbitrary, + value: LazyArbitrary, + ctx: GenerationContext +): LazyArbitrary> => { + return (fc) => { + const items = fc.array(fc.tuple(key(fc), value(fc))) + return (ctx.depthIdentifier !== undefined ? fc.oneof(ctx, fc.constant([]), items) : items).map((as) => new Map(as)) + } +} + +const readonlyMapPretty = ( + key: pretty_.Pretty, + value: pretty_.Pretty +): pretty_.Pretty> => +(map) => + `new Map([${ + Array.from(map.entries()) + .map(([k, v]) => `[${key(k)}, ${value(v)}]`) + .join(", ") + }])` + +const readonlyMapEquivalence = ( + key: Equivalence.Equivalence, + value: Equivalence.Equivalence +): Equivalence.Equivalence> => { + const arrayEquivalence = array_.getEquivalence( + Equivalence.make<[K, V]>(([ka, va], [kb, vb]) => key(ka, kb) && value(va, vb)) + ) + return Equivalence.make((a, b) => arrayEquivalence(Array.from(a.entries()), Array.from(b.entries()))) +} + +const readonlyMapParse = ( + decodeUnknown: ParseResult.DecodeUnknown, R> +): ParseResult.DeclarationDecodeUnknown, R> => +(u, options, ast) => + Predicate.isMap(u) ? + toComposite(decodeUnknown(Array.from(u.entries()), options), (as) => new Map(as), ast, u) + : ParseResult.fail(new ParseResult.Type(ast, u)) + +/** + * @category api interface + * @since 3.10.0 + */ +export interface ReadonlyMapFromSelf extends + AnnotableClass< + ReadonlyMapFromSelf, + ReadonlyMap, Schema.Type>, + ReadonlyMap, Schema.Encoded>, + Schema.Context | Schema.Context + > +{} + +const mapFromSelf_ = ( + key: K, + value: V, + description: string +): ReadonlyMapFromSelf => + declare( + [key, value], + { + decode: (Key, Value) => readonlyMapParse(ParseResult.decodeUnknown(Array$(Tuple(Key, Value)))), + encode: (Key, Value) => readonlyMapParse(ParseResult.encodeUnknown(Array$(Tuple(Key, Value)))) + }, + { + description, + pretty: readonlyMapPretty, + arbitrary: mapArbitrary, + equivalence: readonlyMapEquivalence + } + ) + +/** + * @category ReadonlyMap + * @since 3.10.0 + */ +export const ReadonlyMapFromSelf = ({ key, value }: { + readonly key: K + readonly value: V +}): ReadonlyMapFromSelf => mapFromSelf_(key, value, `ReadonlyMap<${format(key)}, ${format(value)}>`) + +/** + * @category api interface + * @since 3.10.0 + */ +export interface MapFromSelf extends + AnnotableClass< + MapFromSelf, + Map, Schema.Type>, + ReadonlyMap, Schema.Encoded>, + Schema.Context | Schema.Context + > +{} + +/** + * @category Map + * @since 3.10.0 + */ +export const MapFromSelf = ({ key, value }: { + readonly key: K + readonly value: V +}): MapFromSelf => mapFromSelf_(key, value, `Map<${format(key)}, ${format(value)}>`) as any + +/** + * @category api interface + * @since 3.10.0 + */ +export interface ReadonlyMap$ extends + AnnotableClass< + ReadonlyMap$, + ReadonlyMap, Schema.Type>, + ReadonlyArray, Schema.Encoded]>, + Schema.Context | Schema.Context + > +{} + +/** + * @category ReadonlyMap transformations + * @since 3.10.0 + */ +export const ReadonlyMap = ({ key, value }: { + readonly key: K + readonly value: V +}): ReadonlyMap$ => { + const key_ = asSchema(key) + const value_ = asSchema(value) + return transform( + Array$(Tuple(key_, value_)), + ReadonlyMapFromSelf({ key: typeSchema(key_), value: typeSchema(value_) }), + { strict: true, decode: (as) => new Map(as), encode: (map) => Array.from(map.entries()) } + ) +} + +/** + * @category api interface + * @since 3.10.0 + */ +export interface Map$ extends + AnnotableClass< + Map$, + Map, Schema.Type>, + ReadonlyArray, Schema.Encoded]>, + Schema.Context | Schema.Context + > +{} + +const map = ({ key, value }: { + readonly key: K + readonly value: V +}): Map$ => { + const key_ = asSchema(key) + const value_ = asSchema(value) + return transform( + Array$(Tuple(key_, value_)), + MapFromSelf({ key: typeSchema(key_), value: typeSchema(value_) }), + { strict: true, decode: (as) => new Map(as), encode: (map) => Array.from(map.entries()) } + ) +} + +export { + /** + * @category Map transformations + * @since 3.10.0 + */ + map as Map +} + +/** + * @category ReadonlyMap transformations + * @since 3.10.0 + */ +export const ReadonlyMapFromRecord = ({ key, value }: { + key: Schema + value: Schema +}): Schema, { readonly [x: string]: VI }, KR | VR> => + transform( + Record({ key: encodedBoundSchema(key), value }).annotations({ + description: "a record that will be parsed into a ReadonlyMap" + }), + ReadonlyMapFromSelf({ key, value: typeSchema(value) }), + { + strict: true, + decode: (record) => new Map(Object.entries(record)), + encode: record_.fromEntries + } + ) + +/** + * @category Map transformations + * @since 3.10.0 + */ +export const MapFromRecord = ({ key, value }: { + key: Schema + value: Schema +}): Schema, { readonly [x: string]: VI }, KR | VR> => + transform( + Record({ key: encodedBoundSchema(key), value }).annotations({ + description: "a record that will be parsed into a Map" + }), + MapFromSelf({ key, value: typeSchema(value) }), + { + strict: true, + decode: (record) => new Map(Object.entries(record)), + encode: record_.fromEntries + } + ) + +const setArbitrary = (item: LazyArbitrary, ctx: GenerationContext): LazyArbitrary> => (fc) => { + const items = fc.array(item(fc)) + return (ctx.depthIdentifier !== undefined ? fc.oneof(ctx, fc.constant([]), items) : items).map((as) => new Set(as)) +} + +const readonlySetPretty = (item: pretty_.Pretty): pretty_.Pretty> => (set) => + `new Set([${Array.from(set.values()).map((a) => item(a)).join(", ")}])` + +const readonlySetEquivalence = ( + item: Equivalence.Equivalence +): Equivalence.Equivalence> => { + const arrayEquivalence = array_.getEquivalence(item) + return Equivalence.make((a, b) => arrayEquivalence(Array.from(a.values()), Array.from(b.values()))) +} + +const readonlySetParse = ( + decodeUnknown: ParseResult.DecodeUnknown, R> +): ParseResult.DeclarationDecodeUnknown, R> => +(u, options, ast) => + Predicate.isSet(u) ? + toComposite(decodeUnknown(Array.from(u.values()), options), (as) => new Set(as), ast, u) + : ParseResult.fail(new ParseResult.Type(ast, u)) + +/** + * @category api interface + * @since 3.10.0 + */ +export interface ReadonlySetFromSelf extends + AnnotableClass< + ReadonlySetFromSelf, + ReadonlySet>, + ReadonlySet>, + Schema.Context + > +{} + +const setFromSelf_ = (value: Value, description: string): ReadonlySetFromSelf => + declare( + [value], + { + decode: (item) => readonlySetParse(ParseResult.decodeUnknown(Array$(item))), + encode: (item) => readonlySetParse(ParseResult.encodeUnknown(Array$(item))) + }, + { + description, + pretty: readonlySetPretty, + arbitrary: setArbitrary, + equivalence: readonlySetEquivalence + } + ) + +/** + * @category ReadonlySet + * @since 3.10.0 + */ +export const ReadonlySetFromSelf = (value: Value): ReadonlySetFromSelf => + setFromSelf_(value, `ReadonlySet<${format(value)}>`) + +/** + * @category api interface + * @since 3.10.0 + */ +export interface SetFromSelf extends + AnnotableClass< + SetFromSelf, + Set>, + ReadonlySet>, + Schema.Context + > +{} + +/** + * @category Set + * @since 3.10.0 + */ +export const SetFromSelf = (value: Value): SetFromSelf => + setFromSelf_(value, `Set<${format(value)}>`) as any + +/** + * @category api interface + * @since 3.10.0 + */ +export interface ReadonlySet$ extends + AnnotableClass< + ReadonlySet$, + ReadonlySet>, + ReadonlyArray>, + Schema.Context + > +{} + +/** + * @category ReadonlySet transformations + * @since 3.10.0 + */ +export const ReadonlySet = (value: Value): ReadonlySet$ => { + const value_ = asSchema(value) + return transform( + Array$(value_), + ReadonlySetFromSelf(typeSchema(value_)), + { strict: true, decode: (as) => new Set(as), encode: (set) => Array.from(set) } + ) +} + +/** + * @category api interface + * @since 3.10.0 + */ +export interface Set$ extends + AnnotableClass< + Set$, + Set>, + ReadonlyArray>, + Schema.Context + > +{} + +const set = (value: Value): Set$ => { + const value_ = asSchema(value) + return transform( + Array$(value_), + SetFromSelf(typeSchema(value_)), + { strict: true, decode: (as) => new Set(as), encode: (set) => Array.from(set) } + ) +} + +export { + /** + * @category Set transformations + * @since 3.10.0 + */ + set as Set +} + +const bigDecimalPretty = (): pretty_.Pretty => (val) => + `BigDecimal(${bigDecimal_.format(bigDecimal_.normalize(val))})` + +const bigDecimalArbitrary = (): LazyArbitrary => (fc) => + fc.tuple(fc.bigInt(), fc.integer()).map(([value, scale]) => bigDecimal_.make(value, scale)) + +/** + * @category BigDecimal constructors + * @since 3.10.0 + */ +export class BigDecimalFromSelf extends declare( + bigDecimal_.isBigDecimal, + { + identifier: "BigDecimalFromSelf", + pretty: bigDecimalPretty, + arbitrary: bigDecimalArbitrary, + equivalence: () => bigDecimal_.Equivalence + } +) {} + +/** + * @category BigDecimal transformations + * @since 3.10.0 + */ +export class BigDecimal extends transformOrFail( + String$.annotations({ description: "a string that will be parsed into a BigDecimal" }), + BigDecimalFromSelf, + { + strict: true, + decode: (num, _, ast) => + bigDecimal_.fromString(num).pipe(option_.match({ + onNone: () => ParseResult.fail(new ParseResult.Type(ast, num)), + onSome: (val) => ParseResult.succeed(bigDecimal_.normalize(val)) + })), + encode: (val) => ParseResult.succeed(bigDecimal_.format(bigDecimal_.normalize(val))) + } +).annotations({ identifier: "BigDecimal" }) {} + +/** + * A schema that transforms a `number` into a `BigDecimal`. + * When encoding, this Schema will produce incorrect results if the BigDecimal exceeds the 64-bit range of a number. + * + * @category BigDecimal transformations + * @since 3.10.0 + */ +export class BigDecimalFromNumber extends transformOrFail( + Number$.annotations({ description: "a number that will be parsed into a BigDecimal" }), + BigDecimalFromSelf, + { + strict: true, + decode: (num) => ParseResult.succeed(bigDecimal_.fromNumber(num)), + encode: (val) => ParseResult.succeed(bigDecimal_.unsafeToNumber(val)) + } +).annotations({ identifier: "BigDecimalFromNumber" }) {} + +/** + * @category type id + * @since 3.10.0 + */ +export const GreaterThanBigDecimalTypeId: unique symbol = Symbol.for("effect/Schema/TypeId/GreaterThanBigDecimal") + +/** + * @category BigDecimal filters + * @since 3.10.0 + */ +export const greaterThanBigDecimal = ( + min: bigDecimal_.BigDecimal, + annotations?: Annotations.Filter +) => +(self: Schema): filter> => + self.pipe( + filter((a) => bigDecimal_.greaterThan(a, min), { + typeId: { id: GreaterThanBigDecimalTypeId, annotation: { min } }, + description: `a BigDecimal greater than ${bigDecimal_.format(min)}`, + ...annotations + }) + ) + +/** + * @category type id + * @since 3.10.0 + */ +export const GreaterThanOrEqualToBigDecimalTypeId: unique symbol = Symbol.for( + "effect/Schema/TypeId/GreaterThanOrEqualToBigDecimal" +) + +/** + * @category BigDecimal filters + * @since 3.10.0 + */ +export const greaterThanOrEqualToBigDecimal = ( + min: bigDecimal_.BigDecimal, + annotations?: Annotations.Filter +) => +(self: Schema): filter> => + self.pipe( + filter((a) => bigDecimal_.greaterThanOrEqualTo(a, min), { + typeId: { id: GreaterThanOrEqualToBigDecimalTypeId, annotation: { min } }, + description: `a BigDecimal greater than or equal to ${bigDecimal_.format(min)}`, + ...annotations + }) + ) + +/** + * @category type id + * @since 3.10.0 + */ +export const LessThanBigDecimalTypeId: unique symbol = Symbol.for("effect/Schema/TypeId/LessThanBigDecimal") + +/** + * @category BigDecimal filters + * @since 3.10.0 + */ +export const lessThanBigDecimal = ( + max: bigDecimal_.BigDecimal, + annotations?: Annotations.Filter +) => +(self: Schema): filter> => + self.pipe( + filter((a) => bigDecimal_.lessThan(a, max), { + typeId: { id: LessThanBigDecimalTypeId, annotation: { max } }, + description: `a BigDecimal less than ${bigDecimal_.format(max)}`, + ...annotations + }) + ) + +/** + * @category type id + * @since 3.10.0 + */ +export const LessThanOrEqualToBigDecimalTypeId: unique symbol = Symbol.for( + "effect/Schema/TypeId/LessThanOrEqualToBigDecimal" +) + +/** + * @category BigDecimal filters + * @since 3.10.0 + */ +export const lessThanOrEqualToBigDecimal = ( + max: bigDecimal_.BigDecimal, + annotations?: Annotations.Filter +) => +(self: Schema): filter> => + self.pipe( + filter((a) => bigDecimal_.lessThanOrEqualTo(a, max), { + typeId: { id: LessThanOrEqualToBigDecimalTypeId, annotation: { max } }, + description: `a BigDecimal less than or equal to ${bigDecimal_.format(max)}`, + ...annotations + }) + ) + +/** + * @category type id + * @since 3.10.0 + */ +export const PositiveBigDecimalTypeId: unique symbol = Symbol.for( + "effect/Schema/TypeId/PositiveBigDecimal" +) + +/** + * @category BigDecimal filters + * @since 3.10.0 + */ +export const positiveBigDecimal = ( + annotations?: Annotations.Filter +) => +(self: Schema): filter> => + self.pipe( + filter((a) => bigDecimal_.isPositive(a), { + typeId: { id: PositiveBigDecimalTypeId, annotation: {} }, + description: `a positive BigDecimal`, + ...annotations + }) + ) + +/** + * @category BigDecimal constructors + * @since 3.10.0 + */ +export const PositiveBigDecimalFromSelf: filter> = BigDecimalFromSelf.pipe( + positiveBigDecimal({ + identifier: "PositiveBigDecimalFromSelf", + title: "PositiveBigDecimalFromSelf" + }) +) + +/** + * @category type id + * @since 3.10.0 + */ +export const NonNegativeBigDecimalTypeId: unique symbol = Symbol.for( + "effect/Schema/TypeId/NonNegativeBigDecimal" +) + +/** + * @category BigDecimal filters + * @since 3.10.0 + */ +export const nonNegativeBigDecimal = ( + annotations?: Annotations.Filter +) => +(self: Schema): filter> => + self.pipe( + filter((a) => a.value >= 0n, { + typeId: { id: NonNegativeBigDecimalTypeId, annotation: {} }, + description: `a non-negative BigDecimal`, + ...annotations + }) + ) + +/** + * @category BigDecimal constructors + * @since 3.10.0 + */ +export const NonNegativeBigDecimalFromSelf: filter> = BigDecimalFromSelf.pipe( + nonNegativeBigDecimal({ + identifier: "NonNegativeBigDecimalFromSelf", + title: "NonNegativeBigDecimalFromSelf" + }) +) + +/** + * @category type id + * @since 3.10.0 + */ +export const NegativeBigDecimalTypeId: unique symbol = Symbol.for( + "effect/Schema/TypeId/NegativeBigDecimal" +) + +/** + * @category BigDecimal filters + * @since 3.10.0 + */ +export const negativeBigDecimal = ( + annotations?: Annotations.Filter +) => +(self: Schema): filter> => + self.pipe( + filter((a) => bigDecimal_.isNegative(a), { + typeId: { id: NegativeBigDecimalTypeId, annotation: {} }, + description: `a negative BigDecimal`, + ...annotations + }) + ) + +/** + * @category BigDecimal constructors + * @since 3.10.0 + */ +export const NegativeBigDecimalFromSelf: filter> = BigDecimalFromSelf.pipe( + negativeBigDecimal({ + identifier: "NegativeBigDecimalFromSelf", + title: "NegativeBigDecimalFromSelf" + }) +) + +/** + * @category type id + * @since 3.10.0 + */ +export const NonPositiveBigDecimalTypeId: unique symbol = Symbol.for( + "effect/Schema/TypeId/NonPositiveBigDecimal" +) + +/** + * @category BigDecimal filters + * @since 3.10.0 + */ +export const nonPositiveBigDecimal = ( + annotations?: Annotations.Filter +) => +(self: Schema): filter> => + self.pipe( + filter((a) => a.value <= 0n, { + typeId: { id: NonPositiveBigDecimalTypeId, annotation: {} }, + description: `a non-positive BigDecimal`, + ...annotations + }) + ) + +/** + * @category BigDecimal constructors + * @since 3.10.0 + */ +export const NonPositiveBigDecimalFromSelf: filter> = BigDecimalFromSelf.pipe( + nonPositiveBigDecimal({ + identifier: "NonPositiveBigDecimalFromSelf", + title: "NonPositiveBigDecimalFromSelf" + }) +) + +/** + * @category type id + * @since 3.10.0 + */ +export const BetweenBigDecimalTypeId: unique symbol = Symbol.for("effect/Schema/TypeId/BetweenBigDecimal") + +/** + * @category BigDecimal filters + * @since 3.10.0 + */ +export const betweenBigDecimal = ( + minimum: bigDecimal_.BigDecimal, + maximum: bigDecimal_.BigDecimal, + annotations?: Annotations.Filter +) => +(self: Schema): filter> => + self.pipe( + filter((a) => bigDecimal_.between(a, { minimum, maximum }), { + typeId: { id: BetweenBigDecimalTypeId, annotation: { maximum, minimum } }, + description: `a BigDecimal between ${bigDecimal_.format(minimum)} and ${bigDecimal_.format(maximum)}`, + ...annotations + }) + ) + +/** + * Clamps a `BigDecimal` between a minimum and a maximum value. + * + * @category BigDecimal transformations + * @since 3.10.0 + */ +export const clampBigDecimal = + (minimum: bigDecimal_.BigDecimal, maximum: bigDecimal_.BigDecimal) => + (self: Schema): transform, filter>> => + transform( + self, + self.pipe(typeSchema, betweenBigDecimal(minimum, maximum)), + { strict: false, decode: (self) => bigDecimal_.clamp(self, { minimum, maximum }), encode: identity } + ) + +const chunkArbitrary = (item: LazyArbitrary, ctx: GenerationContext): LazyArbitrary> => (fc) => { + const items = fc.array(item(fc)) + return (ctx.depthIdentifier !== undefined ? fc.oneof(ctx, fc.constant([]), items) : items).map(chunk_.fromIterable) +} + +const chunkPretty = (item: pretty_.Pretty): pretty_.Pretty> => (c) => + `Chunk(${chunk_.toReadonlyArray(c).map(item).join(", ")})` + +const chunkParse = ( + decodeUnknown: ParseResult.DecodeUnknown, R> +): ParseResult.DeclarationDecodeUnknown, R> => +(u, options, ast) => + chunk_.isChunk(u) ? + chunk_.isEmpty(u) ? + ParseResult.succeed(chunk_.empty()) + : toComposite(decodeUnknown(chunk_.toReadonlyArray(u), options), chunk_.fromIterable, ast, u) + : ParseResult.fail(new ParseResult.Type(ast, u)) + +/** + * @category api interface + * @since 3.10.0 + */ +export interface ChunkFromSelf extends + AnnotableClass< + ChunkFromSelf, + chunk_.Chunk>, + chunk_.Chunk>, + Schema.Context + > +{} + +/** + * @category Chunk + * @since 3.10.0 + */ +export const ChunkFromSelf = (value: Value): ChunkFromSelf => { + return declare( + [value], + { + decode: (item) => chunkParse(ParseResult.decodeUnknown(Array$(item))), + encode: (item) => chunkParse(ParseResult.encodeUnknown(Array$(item))) + }, + { + description: `Chunk<${format(value)}>`, + pretty: chunkPretty, + arbitrary: chunkArbitrary, + equivalence: chunk_.getEquivalence + } + ) +} + +/** + * @category api interface + * @since 3.10.0 + */ +export interface Chunk extends + AnnotableClass< + Chunk, + chunk_.Chunk>, + ReadonlyArray>, + Schema.Context + > +{} + +/** + * @category Chunk transformations + * @since 3.10.0 + */ +export const Chunk = (value: Value): Chunk => { + const value_ = asSchema(value) + return transform( + Array$(value_), + ChunkFromSelf(typeSchema(value_)), + { + strict: true, + decode: (as) => as.length === 0 ? chunk_.empty() : chunk_.fromIterable(as), + encode: chunk_.toReadonlyArray + } + ) +} + +/** + * @category api interface + * @since 3.10.0 + */ +export interface NonEmptyChunkFromSelf extends + AnnotableClass< + NonEmptyChunkFromSelf, + chunk_.NonEmptyChunk>, + chunk_.NonEmptyChunk>, + Schema.Context + > +{} + +const nonEmptyChunkArbitrary = (item: LazyArbitrary): LazyArbitrary> => (fc) => + fastCheck_.array(item(fc), { minLength: 1 }).map((as) => chunk_.unsafeFromNonEmptyArray(as as any)) + +const nonEmptyChunkPretty = (item: pretty_.Pretty): pretty_.Pretty> => (c) => + `NonEmptyChunk(${chunk_.toReadonlyArray(c).map(item).join(", ")})` + +const nonEmptyChunkParse = ( + decodeUnknown: ParseResult.DecodeUnknown, R> +): ParseResult.DeclarationDecodeUnknown, R> => +(u, options, ast) => + chunk_.isChunk(u) && chunk_.isNonEmpty(u) + ? toComposite(decodeUnknown(chunk_.toReadonlyArray(u), options), chunk_.unsafeFromNonEmptyArray, ast, u) + : ParseResult.fail(new ParseResult.Type(ast, u)) + +/** + * @category Chunk + * @since 3.10.0 + */ +export const NonEmptyChunkFromSelf = (value: Value): NonEmptyChunkFromSelf => { + return declare( + [value], + { + decode: (item) => nonEmptyChunkParse(ParseResult.decodeUnknown(NonEmptyArray(item))), + encode: (item) => nonEmptyChunkParse(ParseResult.encodeUnknown(NonEmptyArray(item))) + }, + { + description: `NonEmptyChunk<${format(value)}>`, + pretty: nonEmptyChunkPretty, + arbitrary: nonEmptyChunkArbitrary, + equivalence: chunk_.getEquivalence + } + ) +} + +/** + * @category api interface + * @since 3.10.0 + */ +export interface NonEmptyChunk extends + AnnotableClass< + NonEmptyChunk, + chunk_.NonEmptyChunk>, + array_.NonEmptyReadonlyArray>, + Schema.Context + > +{} + +/** + * @category Chunk transformations + * @since 3.10.0 + */ +export const NonEmptyChunk = (value: Value): NonEmptyChunk => { + const value_ = asSchema(value) + return transform( + NonEmptyArray(value_), + NonEmptyChunkFromSelf(typeSchema(value_)), + { strict: true, decode: chunk_.unsafeFromNonEmptyArray, encode: chunk_.toReadonlyArray } + ) +} + +const toData = > | ReadonlyArray>(a: A): A => + Array.isArray(a) ? data_.array(a) : data_.struct(a) + +const dataArbitrary = > | ReadonlyArray>( + item: LazyArbitrary +): LazyArbitrary => +(fc) => item(fc).map(toData) + +const dataPretty = > | ReadonlyArray>( + item: pretty_.Pretty +): pretty_.Pretty => +(d) => `Data(${item(d)})` + +const dataParse = > | ReadonlyArray>( + decodeUnknown: ParseResult.DecodeUnknown +): ParseResult.DeclarationDecodeUnknown => +(u, options, ast) => + Equal.isEqual(u) ? + toComposite(decodeUnknown(u, options), toData, ast, u) + : ParseResult.fail(new ParseResult.Type(ast, u)) + +/** + * @category Data transformations + * @since 3.10.0 + */ +export const DataFromSelf = < + R, + I extends Readonly> | ReadonlyArray, + A extends Readonly> | ReadonlyArray +>( + item: Schema +): SchemaClass => + declare( + [item], + { + decode: (item) => dataParse(ParseResult.decodeUnknown(item)), + encode: (item) => dataParse(ParseResult.encodeUnknown(item)) + }, + { + description: `Data<${format(item)}>`, + pretty: dataPretty, + arbitrary: dataArbitrary + } + ) + +/** + * @category Data transformations + * @since 3.10.0 + */ +export const Data = < + R, + I extends Readonly> | ReadonlyArray, + A extends Readonly> | ReadonlyArray +>( + item: Schema +): SchemaClass => + transform( + item, + DataFromSelf(typeSchema(item)), + { strict: false, decode: toData, encode: (a) => Array.isArray(a) ? Array.from(a) : Object.assign({}, a) } + ) + +type MissingSelfGeneric = + `Missing \`Self\` generic - use \`class Self extends ${Usage}()(${Params}{ ... })\`` + +type RequiredKeys = { + [K in keyof T]-?: {} extends Pick ? never : K +}[keyof T] + +/** + * @category api interface + * @since 3.10.0 + */ +export interface Class + extends Schema, R> +{ + new( + props: RequiredKeys extends never ? void | Simplify : Simplify, + options?: MakeOptions + ): Struct.Type & Omit & Proto + + /** @since 3.10.0 */ + readonly ast: AST.Transformation + + make, X>(this: { new(...args: Args): X }, ...args: Args): X + + annotations(annotations: Annotations.Schema): SchemaClass, R> + + readonly fields: { readonly [K in keyof Fields]: Fields[K] } + + readonly identifier: string + + extend(identifier: string): ( + fields: newFields | HasFields, + annotations?: Annotations.Schema + ) => [Extended] extends [never] ? MissingSelfGeneric<"Base.extend"> + : Class< + Extended, + Fields & newFields, + I & Struct.Encoded, + R | Struct.Context, + C & Struct.Constructor, + Self, + Proto + > + + transformOrFail(identifier: string): < + newFields extends Struct.Fields, + R2, + R3 + >( + fields: newFields, + options: { + readonly decode: ( + input: Simplify>, + options: ParseOptions, + ast: AST.Transformation + ) => Effect.Effect>, ParseResult.ParseIssue, R2> + readonly encode: ( + input: Simplify>, + options: ParseOptions, + ast: AST.Transformation + ) => Effect.Effect, ParseResult.ParseIssue, R3> + }, + annotations?: Annotations.Schema + ) => [Transformed] extends [never] ? MissingSelfGeneric<"Base.transformOrFail"> + : Class< + Transformed, + Fields & newFields, + I, + R | Struct.Context | R2 | R3, + C & Struct.Constructor, + Self, + Proto + > + + transformOrFailFrom(identifier: string): < + newFields extends Struct.Fields, + R2, + R3 + >( + fields: newFields, + options: { + readonly decode: ( + input: Simplify, + options: ParseOptions, + ast: AST.Transformation + ) => Effect.Effect>, ParseResult.ParseIssue, R2> + readonly encode: ( + input: Simplify>, + options: ParseOptions, + ast: AST.Transformation + ) => Effect.Effect + }, + annotations?: Annotations.Schema + ) => [Transformed] extends [never] ? MissingSelfGeneric<"Base.transformOrFailFrom"> + : Class< + Transformed, + Fields & newFields, + I, + R | Struct.Context | R2 | R3, + C & Struct.Constructor, + Self, + Proto + > +} + +type HasFields = Struct | { + readonly [refineTypeId]: HasFields +} + +const isField = (u: unknown) => isSchema(u) || isPropertySignature(u) + +const isFields = (fields: object): fields is Fields => + util_.ownKeys(fields).every((key) => isField((fields as any)[key])) + +const getFields = (hasFields: HasFields): Fields => + "fields" in hasFields ? hasFields.fields : getFields(hasFields[refineTypeId]) + +const getSchemaFromFieldsOr = (fieldsOr: Fields | HasFields): Schema.Any => + isFields(fieldsOr) ? Struct(fieldsOr) : isSchema(fieldsOr) ? fieldsOr : Struct(getFields(fieldsOr)) + +const getFieldsFromFieldsOr = (fieldsOr: Fields | HasFields): Fields => + isFields(fieldsOr) ? fieldsOr : getFields(fieldsOr) + +/** + * @category classes + * @since 3.10.0 + */ +export const Class = (identifier: string) => +( + fieldsOr: Fields | HasFields, + annotations?: Annotations.Schema +): [Self] extends [never] ? MissingSelfGeneric<"Class"> + : Class< + Self, + Fields, + Struct.Encoded, + Struct.Context, + Struct.Constructor, + {}, + {} + > => + makeClass({ + kind: "Class", + identifier, + schema: getSchemaFromFieldsOr(fieldsOr), + fields: getFieldsFromFieldsOr(fieldsOr), + Base: data_.Class, + annotations + }) + +/** @internal */ +export const getClassTag = (tag: Tag) => + withConstructorDefault(propertySignature(Literal(tag)), () => tag) + +/** + * @category api interface + * @since 3.10.0 + */ +export interface TaggedClass extends + Class< + Self, + Fields, + Struct.Encoded, + Struct.Context, + Struct.Constructor>, + {}, + {} + > +{ + readonly _tag: Tag +} + +/** + * @category classes + * @since 3.10.0 + */ +export const TaggedClass = (identifier?: string) => +( + tag: Tag, + fieldsOr: Fields | HasFields, + annotations?: Annotations.Schema +): [Self] extends [never] ? MissingSelfGeneric<"TaggedClass", `"Tag", `> + : TaggedClass } & Fields> => +{ + const fields = getFieldsFromFieldsOr(fieldsOr) + const schema = getSchemaFromFieldsOr(fieldsOr) + const newFields = { _tag: getClassTag(tag) } + const taggedFields = extendFields(newFields, fields) + return class TaggedClass extends makeClass({ + kind: "TaggedClass", + identifier: identifier ?? tag, + schema: extend(schema, Struct(newFields)), + fields: taggedFields, + Base: data_.Class, + annotations + }) { + static _tag = tag + } as any +} + +/** + * @category api interface + * @since 3.10.0 + */ +export interface TaggedErrorClass extends + Class< + Self, + Fields, + Struct.Encoded, + Struct.Context, + Struct.Constructor>, + {}, + cause_.YieldableError + > +{ + readonly _tag: Tag +} + +/** + * @category classes + * @since 3.10.0 + */ +export const TaggedError = (identifier?: string) => +( + tag: Tag, + fieldsOr: Fields | HasFields, + annotations?: Annotations.Schema +): [Self] extends [never] ? MissingSelfGeneric<"TaggedError", `"Tag", `> + : TaggedErrorClass< + Self, + Tag, + { readonly _tag: tag } & Fields + > => +{ + class Base extends data_.Error {} + ;(Base.prototype as any).name = tag + const fields = getFieldsFromFieldsOr(fieldsOr) + const schema = getSchemaFromFieldsOr(fieldsOr) + const newFields = { _tag: getClassTag(tag) } + const taggedFields = extendFields(newFields, fields) + return class TaggedErrorClass extends makeClass({ + kind: "TaggedError", + identifier: identifier ?? tag, + schema: extend(schema, Struct(newFields)), + fields: taggedFields, + Base, + annotations, + disableToString: true + }) { + static _tag = tag + get message(): string { + return `{ ${ + util_.ownKeys(fields).map((p: any) => `${util_.formatPropertyKey(p)}: ${util_.formatUnknown(this[p])}`) + .join(", ") + } }` + } + } as any +} + +/** + * @since 3.10.0 + */ +export interface TaggedRequest< + Tag extends string, + A, + I, + R, + SuccessType, + SuccessEncoded, + FailureType, + FailureEncoded, + ResultR +> extends + Request.Request, + Serializable.SerializableWithResult< + A, + I, + R, + SuccessType, + SuccessEncoded, + FailureType, + FailureEncoded, + ResultR + > +{ + readonly _tag: Tag +} + +/** + * @since 3.10.0 + */ +export declare namespace TaggedRequest { + /** + * @since 3.10.0 + */ + export type Any = TaggedRequest + /** + * @since 3.10.0 + */ + export type All = + | Any + | TaggedRequest +} + +/** + * @category api interface + * @since 3.10.0 + */ +export interface TaggedRequestClass< + Self, + Tag extends string, + Payload extends Struct.Fields, + Success extends Schema.All, + Failure extends Schema.All +> extends + Class< + Self, + Payload, + Struct.Encoded, + Struct.Context, + Struct.Constructor>, + TaggedRequest< + Tag, + Self, + Struct.Encoded, + Struct.Context, + Schema.Type, + Schema.Encoded, + Schema.Type, + Schema.Encoded, + Schema.Context | Schema.Context + >, + {} + > +{ + readonly _tag: Tag + readonly success: Success + readonly failure: Failure +} + +/** + * @category classes + * @since 3.10.0 + */ +export const TaggedRequest = + (identifier?: string) => + ( + tag: Tag, + options: { + failure: Failure + success: Success + payload: Payload + }, + annotations?: Annotations.Schema + ): [Self] extends [never] ? MissingSelfGeneric<"TaggedRequest", `"Tag", SuccessSchema, FailureSchema, `> + : TaggedRequestClass< + Self, + Tag, + { readonly _tag: tag } & Payload, + Success, + Failure + > => + { + const taggedFields = extendFields({ _tag: getClassTag(tag) }, options.payload) + return class TaggedRequestClass extends makeClass({ + kind: "TaggedRequest", + identifier: identifier ?? tag, + schema: Struct(taggedFields), + fields: taggedFields, + Base: Request.Class, + annotations + }) { + static _tag = tag + static success = options.success + static failure = options.failure + get [serializable_.symbol]() { + return this.constructor + } + get [serializable_.symbolResult]() { + return { + failure: options.failure, + success: options.success + } + } + } as any + } + +const extendFields = (a: Struct.Fields, b: Struct.Fields): Struct.Fields => { + const out = { ...a } + for (const key of util_.ownKeys(b)) { + if (key in a) { + throw new Error(errors_.getASTDuplicatePropertySignatureErrorMessage(key)) + } + out[key] = b[key] + } + return out +} + +// does not overwrite existing title annotation +const orElseTitleAnnotation = (schema: Schema, title: string): Schema => { + const annotation = AST.getTitleAnnotation(schema.ast) + if (option_.isNone(annotation)) { + return schema.annotations({ title }) + } + return schema +} + +type MakeOptions = boolean | { + readonly disableValidation?: boolean +} + +const getDisableValidationMakeOption = (options: MakeOptions | undefined): boolean => + Predicate.isBoolean(options) ? options : options?.disableValidation ?? false + +const makeClass = ({ Base, annotations, disableToString, fields, identifier, kind, schema }: { + kind: "Class" | "TaggedClass" | "TaggedError" | "TaggedRequest" + identifier: string + schema: Schema.Any + fields: Struct.Fields + Base: new(...args: ReadonlyArray) => any + annotations?: Annotations.Schema | undefined + disableToString?: boolean | undefined +}): any => { + const classSymbol = Symbol.for(`effect/Schema/${kind}/${identifier}`) + const validateSchema = orElseTitleAnnotation(schema, `${identifier} (Constructor)`) + const encodedSide: Schema.Any = orElseTitleAnnotation(schema, `${identifier} (Encoded side)`) + const typeSide = orElseTitleAnnotation(typeSchema(schema), `${identifier} (Type side)`) + const fallbackInstanceOf = (u: unknown) => Predicate.hasProperty(u, classSymbol) && ParseResult.is(typeSide)(u) + const klass = class extends Base { + constructor( + props: { [x: string | symbol]: unknown } = {}, + options: MakeOptions = false + ) { + props = { ...props } + if (kind !== "Class") { + delete props["_tag"] + } + props = lazilyMergeDefaults(fields, props) + if (!getDisableValidationMakeOption(options)) { + props = ParseResult.validateSync(validateSchema)(props) + } + super(props, true) + } + + // ---------------- + // Schema interface + // ---------------- + + static [TypeId] = variance + + static get ast() { + const declaration: Schema.Any = declare( + [typeSide], + { + decode: () => (input, _, ast) => + input instanceof this || fallbackInstanceOf(input) + ? ParseResult.succeed(input) + : ParseResult.fail(new ParseResult.Type(ast, input)), + encode: () => (input, options) => + input instanceof this + ? ParseResult.succeed(input) + : ParseResult.map( + ParseResult.encodeUnknown(typeSide)(input, options), + (props) => new this(props, true) + ) + }, + { + identifier, + title: identifier, + description: `an instance of ${identifier}`, + pretty: (pretty) => (self: any) => `${identifier}(${pretty(self)})`, + // @ts-expect-error + arbitrary: (arb) => (fc) => arb(fc).map((props) => new this(props)), + equivalence: identity, + [AST.SurrogateAnnotationId]: typeSide.ast, + ...annotations + } + ) + const transformation = transform( + encodedSide, + declaration, + { strict: true, decode: (input) => new this(input, true), encode: identity } + ).annotations({ [AST.SurrogateAnnotationId]: schema.ast }) + return transformation.ast + } + + static pipe() { + return pipeArguments(this, arguments) + } + + static annotations(annotations: Annotations.Schema) { + return make(this.ast).annotations(annotations) + } + + static toString() { + return `(${String(encodedSide)} <-> ${identifier})` + } + + // ---------------- + // Class interface + // ---------------- + + static make(...args: Array) { + return new this(...args) + } + + static fields = { ...fields } + + static identifier = identifier + + static extend(identifier: string) { + return (newFieldsOr: Struct.Fields | HasFields, annotations?: Annotations.Schema) => { + const newFields = getFieldsFromFieldsOr(newFieldsOr) + const newSchema = getSchemaFromFieldsOr(newFieldsOr) + const extendedFields = extendFields(fields, newFields) + return makeClass({ + kind, + identifier, + schema: extend(schema, newSchema), + fields: extendedFields, + Base: this, + annotations + }) + } + } + + static transformOrFail(identifier: string) { + return (newFields: Struct.Fields, options: any, annotations?: Annotations.Schema) => { + const transformedFields: Struct.Fields = extendFields(fields, newFields) + return makeClass({ + kind, + identifier, + schema: transformOrFail( + schema, + typeSchema(Struct(transformedFields)), + options + ), + fields: transformedFields, + Base: this, + annotations + }) + } + } + + static transformOrFailFrom(identifier: string) { + return (newFields: Struct.Fields, options: any, annotations?: Annotations.Schema) => { + const transformedFields: Struct.Fields = extendFields(fields, newFields) + return makeClass({ + kind, + identifier, + schema: transformOrFail( + encodedSchema(schema), + Struct(transformedFields), + options + ), + fields: transformedFields, + Base: this, + annotations + }) + } + } + + // ---------------- + // other + // ---------------- + + get [classSymbol]() { + return classSymbol + } + } + if (disableToString !== true) { + Object.defineProperty(klass.prototype, "toString", { + value() { + return `${identifier}({ ${ + util_.ownKeys(fields).map((p: any) => `${util_.formatPropertyKey(p)}: ${util_.formatUnknown(this[p])}`) + .join(", ") + } })` + }, + configurable: true + }) + } + return klass +} + +/** + * @category FiberId + * @since 3.10.0 + */ +export type FiberIdEncoded = + | { + readonly _tag: "Composite" + readonly left: FiberIdEncoded + readonly right: FiberIdEncoded + } + | { + readonly _tag: "None" + } + | { + readonly _tag: "Runtime" + readonly id: number + readonly startTimeMillis: number + } + +const FiberIdNoneEncoded = Struct({ + _tag: Literal("None") +}).annotations({ identifier: "FiberIdNoneEncoded" }) + +const FiberIdRuntimeEncoded = Struct({ + _tag: Literal("Runtime"), + id: Int.annotations({ + title: "id", + description: "id" + }), + startTimeMillis: Int.annotations({ + title: "startTimeMillis", + description: "startTimeMillis" + }) +}).annotations({ identifier: "FiberIdRuntimeEncoded" }) + +const FiberIdCompositeEncoded = Struct({ + _tag: Literal("Composite"), + left: suspend(() => FiberIdEncoded), + right: suspend(() => FiberIdEncoded) +}).annotations({ identifier: "FiberIdCompositeEncoded" }) + +const FiberIdEncoded: Schema = Union( + FiberIdNoneEncoded, + FiberIdRuntimeEncoded, + FiberIdCompositeEncoded +).annotations({ identifier: "FiberIdEncoded" }) + +const fiberIdArbitrary: LazyArbitrary = (fc) => + fc.letrec((tie) => ({ + None: fc.record({ _tag: fc.constant("None" as const) }), + Runtime: fc.record({ _tag: fc.constant("Runtime" as const), id: fc.integer(), startTimeMillis: fc.integer() }), + Composite: fc.record({ _tag: fc.constant("Composite" as const), left: tie("FiberId"), right: tie("FiberId") }), + FiberId: fc.oneof(tie("None"), tie("Runtime"), tie("Composite")) as any as fastCheck_.Arbitrary + })).FiberId.map(fiberIdDecode) + +const fiberIdPretty: pretty_.Pretty = (fiberId) => { + switch (fiberId._tag) { + case "None": + return "FiberId.none" + case "Runtime": + return `FiberId.runtime(${fiberId.id}, ${fiberId.startTimeMillis})` + case "Composite": + return `FiberId.composite(${fiberIdPretty(fiberId.right)}, ${fiberIdPretty(fiberId.left)})` + } +} + +/** + * @category FiberId constructors + * @since 3.10.0 + */ +export class FiberIdFromSelf extends declare( + fiberId_.isFiberId, + { + identifier: "FiberIdFromSelf", + pretty: () => fiberIdPretty, + arbitrary: () => fiberIdArbitrary + } +) {} + +const fiberIdDecode = (input: FiberIdEncoded): fiberId_.FiberId => { + switch (input._tag) { + case "None": + return fiberId_.none + case "Runtime": + return fiberId_.runtime(input.id, input.startTimeMillis) + case "Composite": + return fiberId_.composite(fiberIdDecode(input.left), fiberIdDecode(input.right)) + } +} + +const fiberIdEncode = (input: fiberId_.FiberId): FiberIdEncoded => { + switch (input._tag) { + case "None": + return { _tag: "None" } + case "Runtime": + return { _tag: "Runtime", id: input.id, startTimeMillis: input.startTimeMillis } + case "Composite": + return { + _tag: "Composite", + left: fiberIdEncode(input.left), + right: fiberIdEncode(input.right) + } + } +} + +/** + * @category FiberId transformations + * @since 3.10.0 + */ +export class FiberId extends transform( + FiberIdEncoded, + FiberIdFromSelf, + { strict: true, decode: fiberIdDecode, encode: fiberIdEncode } +).annotations({ identifier: "FiberId" }) {} + +/** + * @category Cause utils + * @since 3.10.0 + */ +export type CauseEncoded = + | { + readonly _tag: "Empty" + } + | { + readonly _tag: "Fail" + readonly error: E + } + | { + readonly _tag: "Die" + readonly defect: D + } + | { + readonly _tag: "Interrupt" + readonly fiberId: FiberIdEncoded + } + | { + readonly _tag: "Sequential" + readonly left: CauseEncoded + readonly right: CauseEncoded + } + | { + readonly _tag: "Parallel" + readonly left: CauseEncoded + readonly right: CauseEncoded + } + +const causeDieEncoded = (defect: Schema) => + Struct({ + _tag: Literal("Die"), + defect + }) + +const CauseEmptyEncoded = Struct({ + _tag: Literal("Empty") +}) + +const causeFailEncoded = (error: Schema) => + Struct({ + _tag: Literal("Fail"), + error + }) + +const CauseInterruptEncoded = Struct({ + _tag: Literal("Interrupt"), + fiberId: FiberIdEncoded +}) + +const causeParallelEncoded = (causeEncoded: Schema, CauseEncoded, R>) => + Struct({ + _tag: Literal("Parallel"), + left: causeEncoded, + right: causeEncoded + }) + +const causeSequentialEncoded = (causeEncoded: Schema, CauseEncoded, R>) => + Struct({ + _tag: Literal("Sequential"), + left: causeEncoded, + right: causeEncoded + }) + +const causeEncoded = ( + error: Schema, + defect: Schema +): Schema, CauseEncoded, R1 | R2> => { + const recur = suspend(() => out) + const out: Schema, CauseEncoded, R1 | R2> = Union( + CauseEmptyEncoded, + causeFailEncoded(error), + causeDieEncoded(defect), + CauseInterruptEncoded, + causeSequentialEncoded(recur), + causeParallelEncoded(recur) + ).annotations({ title: `CauseEncoded<${format(error)}>` }) + return out +} + +const causeArbitrary = ( + error: LazyArbitrary, + defect: LazyArbitrary +): LazyArbitrary> => +(fc) => + fc.letrec((tie) => ({ + Empty: fc.record({ _tag: fc.constant("Empty" as const) }), + Fail: fc.record({ _tag: fc.constant("Fail" as const), error: error(fc) }), + Die: fc.record({ _tag: fc.constant("Die" as const), defect: defect(fc) }), + Interrupt: fc.record({ _tag: fc.constant("Interrupt" as const), fiberId: fiberIdArbitrary(fc) }), + Sequential: fc.record({ _tag: fc.constant("Sequential" as const), left: tie("Cause"), right: tie("Cause") }), + Parallel: fc.record({ _tag: fc.constant("Parallel" as const), left: tie("Cause"), right: tie("Cause") }), + Cause: fc.oneof( + tie("Empty"), + tie("Fail"), + tie("Die"), + tie("Interrupt"), + tie("Sequential"), + tie("Parallel") + ) as any as fastCheck_.Arbitrary> + })).Cause.map(causeDecode) + +const causePretty = (error: pretty_.Pretty): pretty_.Pretty> => (cause) => { + const f = (cause: cause_.Cause): string => { + switch (cause._tag) { + case "Empty": + return "Cause.empty" + case "Fail": + return `Cause.fail(${error(cause.error)})` + case "Die": + return `Cause.die(${cause_.pretty(cause)})` + case "Interrupt": + return `Cause.interrupt(${fiberIdPretty(cause.fiberId)})` + case "Sequential": + return `Cause.sequential(${f(cause.left)}, ${f(cause.right)})` + case "Parallel": + return `Cause.parallel(${f(cause.left)}, ${f(cause.right)})` + } + } + return f(cause) +} + +const causeParse = ( + decodeUnknown: ParseResult.DecodeUnknown, R> +): ParseResult.DeclarationDecodeUnknown, R> => +(u, options, ast) => + cause_.isCause(u) ? + toComposite(decodeUnknown(causeEncode(u), options), causeDecode, ast, u) + : ParseResult.fail(new ParseResult.Type(ast, u)) + +/** + * @category api interface + * @since 3.10.0 + */ +export interface CauseFromSelf extends + AnnotableClass< + CauseFromSelf, + cause_.Cause>, + cause_.Cause>, + Schema.Context | Schema.Context + > +{} + +/** + * @category Cause transformations + * @since 3.10.0 + */ +export const CauseFromSelf = ({ defect, error }: { + readonly error: E + readonly defect: D +}): CauseFromSelf => { + return declare( + [error, defect], + { + decode: (error, defect) => causeParse(ParseResult.decodeUnknown(causeEncoded(error, defect))), + encode: (error, defect) => causeParse(ParseResult.encodeUnknown(causeEncoded(error, defect))) + }, + { + title: `Cause<${error.ast}>`, + pretty: causePretty, + arbitrary: causeArbitrary + } + ) +} + +function causeDecode(cause: CauseEncoded): cause_.Cause { + switch (cause._tag) { + case "Empty": + return cause_.empty + case "Fail": + return cause_.fail(cause.error) + case "Die": + return cause_.die(cause.defect) + case "Interrupt": + return cause_.interrupt(fiberIdDecode(cause.fiberId)) + case "Sequential": + return cause_.sequential(causeDecode(cause.left), causeDecode(cause.right)) + case "Parallel": + return cause_.parallel(causeDecode(cause.left), causeDecode(cause.right)) + } +} + +function causeEncode(cause: cause_.Cause): CauseEncoded { + switch (cause._tag) { + case "Empty": + return { _tag: "Empty" } + case "Fail": + return { _tag: "Fail", error: cause.error } + case "Die": + return { _tag: "Die", defect: cause.defect } + case "Interrupt": + return { _tag: "Interrupt", fiberId: cause.fiberId } + case "Sequential": + return { + _tag: "Sequential", + left: causeEncode(cause.left), + right: causeEncode(cause.right) + } + case "Parallel": + return { + _tag: "Parallel", + left: causeEncode(cause.left), + right: causeEncode(cause.right) + } + } +} + +/** + * @category api interface + * @since 3.10.0 + */ +export interface Cause extends + AnnotableClass< + Cause, + cause_.Cause>, + CauseEncoded, Schema.Encoded>, + Schema.Context | Schema.Context + > +{} + +/** + * @category Cause transformations + * @since 3.10.0 + */ +export const Cause = ({ defect, error }: { + readonly error: E + readonly defect: D +}): Cause => { + const error_ = asSchema(error) + const defect_ = asSchema(defect) + return transform( + causeEncoded(error_, defect_), + CauseFromSelf({ error: typeSchema(error_), defect: Unknown }), + { strict: false, decode: causeDecode, encode: causeEncode } + ) +} + +/** + * @category api interface + * @since 3.10.0 + */ +export interface Defect extends transform {} + +/** + * Defines a schema for handling JavaScript errors (`Error` instances) and other types of defects. + * It decodes objects into Error instances if they match the expected structure (i.e., have a `message` and optionally a `name` and `stack`), + * or converts other values to their string representations. + * + * When encoding, it converts `Error` instances back into plain objects containing only the error's name and message, + * or other values into their string forms. + * + * This is useful for serializing and deserializing errors across network boundaries where error objects do not natively serialize. + * + * @category defect + * @since 3.10.0 + */ +export const Defect: Defect = transform( + Unknown, + Unknown, + { + strict: true, + decode: (u) => { + if (Predicate.isObject(u) && "message" in u && typeof u.message === "string") { + const err = new Error(u.message, { cause: u }) + if ("name" in u && typeof u.name === "string") { + err.name = u.name + } + err.stack = "stack" in u && typeof u.stack === "string" ? u.stack : "" + return err + } + return String(u) + }, + encode: (defect) => { + if (defect instanceof Error) { + return { + name: defect.name, + message: defect.message + // no stack because of security reasons + } + } + return String(defect) + } + } +).annotations({ identifier: "Defect" }) + +/** + * @category Exit utils + * @since 3.10.0 + */ +export type ExitEncoded = + | { + readonly _tag: "Failure" + readonly cause: CauseEncoded + } + | { + readonly _tag: "Success" + readonly value: A + } + +const exitFailureEncoded = ( + error: Schema, + defect: Schema +) => + Struct({ + _tag: Literal("Failure"), + cause: causeEncoded(error, defect) + }) + +const exitSuccessEncoded = ( + value: Schema +) => + Struct({ + _tag: Literal("Success"), + value + }) + +const exitEncoded = ( + value: Schema, + error: Schema, + defect: Schema +): Schema, ExitEncoded, R | ER | DR> => + Union( + exitFailureEncoded(error, defect), + exitSuccessEncoded(value) + ).annotations({ + title: `ExitEncoded<${format(value)}, ${format(error)}, ${format(defect)}>` + }) + +const exitDecode = (input: ExitEncoded): exit_.Exit => { + switch (input._tag) { + case "Failure": + return exit_.failCause(causeDecode(input.cause)) + case "Success": + return exit_.succeed(input.value) + } +} + +const exitArbitrary = ( + value: LazyArbitrary, + error: LazyArbitrary, + defect: LazyArbitrary +): LazyArbitrary> => +(fc) => + fc.oneof( + fc.record({ _tag: fc.constant("Failure" as const), cause: causeArbitrary(error, defect)(fc) }), + fc.record({ _tag: fc.constant("Success" as const), value: value(fc) }) + ).map(exitDecode) + +const exitPretty = + (value: pretty_.Pretty, error: pretty_.Pretty): pretty_.Pretty> => (exit) => + exit._tag === "Failure" + ? `Exit.failCause(${causePretty(error)(exit.cause)})` + : `Exit.succeed(${value(exit.value)})` + +const exitParse = ( + decodeUnknownValue: ParseResult.DecodeUnknown, + decodeUnknownCause: ParseResult.DecodeUnknown, ER> +): ParseResult.DeclarationDecodeUnknown, ER | R> => +(u, options, ast) => + exit_.isExit(u) ? + exit_.match(u, { + onFailure: (cause) => toComposite(decodeUnknownCause(cause, options), exit_.failCause, ast, u), + onSuccess: (value) => toComposite(decodeUnknownValue(value, options), exit_.succeed, ast, u) + }) + : ParseResult.fail(new ParseResult.Type(ast, u)) + +/** + * @category api interface + * @since 3.10.0 + */ +export interface ExitFromSelf extends + AnnotableClass< + ExitFromSelf, + exit_.Exit, Schema.Type>, + exit_.Exit, Schema.Encoded>, + Schema.Context | Schema.Context | Schema.Context + > +{} + +/** + * @category Exit transformations + * @since 3.10.0 + */ +export const ExitFromSelf = ( + { defect, failure, success }: { + readonly failure: E + readonly success: A + readonly defect: D + } +): ExitFromSelf => + declare( + [success, failure, defect], + { + decode: (success, failure, defect) => + exitParse( + ParseResult.decodeUnknown(success), + ParseResult.decodeUnknown(CauseFromSelf({ error: failure, defect })) + ), + encode: (success, failure, defect) => + exitParse( + ParseResult.encodeUnknown(success), + ParseResult.encodeUnknown(CauseFromSelf({ error: failure, defect })) + ) + }, + { + title: `Exit<${success.ast}, ${failure.ast}>`, + pretty: exitPretty, + arbitrary: exitArbitrary + } + ) + +/** + * @category api interface + * @since 3.10.0 + */ +export interface Exit extends + AnnotableClass< + Exit, + exit_.Exit, Schema.Type>, + ExitEncoded, Schema.Encoded, Schema.Encoded>, + Schema.Context | Schema.Context | Schema.Context + > +{} + +/** + * @category Exit transformations + * @since 3.10.0 + */ +export const Exit = ( + { defect, failure, success }: { + readonly failure: E + readonly success: A + readonly defect: D + } +): Exit => { + const success_ = asSchema(success) + const failure_ = asSchema(failure) + const defect_ = asSchema(defect) + return transform( + exitEncoded(success_, failure_, defect_), + ExitFromSelf({ failure: typeSchema(failure_), success: typeSchema(success_), defect: Unknown }), + { + strict: false, + decode: exitDecode, + encode: (exit) => + exit._tag === "Failure" + ? { _tag: "Failure", cause: exit.cause } as const + : { _tag: "Success", value: exit.value } as const + } + ) +} + +const hashSetArbitrary = + (item: LazyArbitrary, ctx: GenerationContext): LazyArbitrary> => (fc) => { + const items = fc.array(item(fc)) + return (ctx.depthIdentifier !== undefined ? fc.oneof(ctx, fc.constant([]), items) : items).map( + hashSet_.fromIterable + ) + } + +const hashSetPretty = (item: pretty_.Pretty): pretty_.Pretty> => (set) => + `HashSet(${Array.from(set).map((a) => item(a)).join(", ")})` + +const hashSetEquivalence = ( + item: Equivalence.Equivalence +): Equivalence.Equivalence> => { + const arrayEquivalence = array_.getEquivalence(item) + return Equivalence.make((a, b) => arrayEquivalence(Array.from(a), Array.from(b))) +} + +const hashSetParse = ( + decodeUnknown: ParseResult.DecodeUnknown, R> +): ParseResult.DeclarationDecodeUnknown, R> => +(u, options, ast) => + hashSet_.isHashSet(u) ? + toComposite(decodeUnknown(Array.from(u), options), hashSet_.fromIterable, ast, u) + : ParseResult.fail(new ParseResult.Type(ast, u)) + +/** + * @category api interface + * @since 3.10.0 + */ +export interface HashSetFromSelf extends + AnnotableClass< + HashSetFromSelf, + hashSet_.HashSet>, + hashSet_.HashSet>, + Schema.Context + > +{} + +/** + * @category HashSet transformations + * @since 3.10.0 + */ +export const HashSetFromSelf = ( + value: Value +): HashSetFromSelf => { + return declare( + [value], + { + decode: (item) => hashSetParse(ParseResult.decodeUnknown(Array$(item))), + encode: (item) => hashSetParse(ParseResult.encodeUnknown(Array$(item))) + }, + { + description: `HashSet<${format(value)}>`, + pretty: hashSetPretty, + arbitrary: hashSetArbitrary, + equivalence: hashSetEquivalence + } + ) +} + +/** + * @category api interface + * @since 3.10.0 + */ +export interface HashSet extends + AnnotableClass< + HashSet, + hashSet_.HashSet>, + ReadonlyArray>, + Schema.Context + > +{} + +/** + * @category HashSet transformations + * @since 3.10.0 + */ +export const HashSet = (value: Value): HashSet => { + const value_ = asSchema(value) + return transform( + Array$(value_), + HashSetFromSelf(typeSchema(value_)), + { strict: true, decode: (as) => hashSet_.fromIterable(as), encode: (set) => Array.from(set) } + ) +} + +const hashMapArbitrary = ( + key: LazyArbitrary, + value: LazyArbitrary, + ctx: GenerationContext +): LazyArbitrary> => +(fc) => { + const items = fc.array(fc.tuple(key(fc), value(fc))) + return (ctx.depthIdentifier !== undefined ? fc.oneof(ctx, fc.constant([]), items) : items).map(hashMap_.fromIterable) +} + +const hashMapPretty = ( + key: pretty_.Pretty, + value: pretty_.Pretty +): pretty_.Pretty> => +(map) => + `HashMap([${ + Array.from(map) + .map(([k, v]) => `[${key(k)}, ${value(v)}]`) + .join(", ") + }])` + +const hashMapEquivalence = ( + key: Equivalence.Equivalence, + value: Equivalence.Equivalence +): Equivalence.Equivalence> => { + const arrayEquivalence = array_.getEquivalence( + Equivalence.make<[K, V]>(([ka, va], [kb, vb]) => key(ka, kb) && value(va, vb)) + ) + return Equivalence.make((a, b) => arrayEquivalence(Array.from(a), Array.from(b))) +} + +const hashMapParse = ( + decodeUnknown: ParseResult.DecodeUnknown, R> +): ParseResult.DeclarationDecodeUnknown, R> => +(u, options, ast) => + hashMap_.isHashMap(u) ? + toComposite(decodeUnknown(Array.from(u), options), hashMap_.fromIterable, ast, u) + : ParseResult.fail(new ParseResult.Type(ast, u)) + +/** + * @category api interface + * @since 3.10.0 + */ +export interface HashMapFromSelf extends + AnnotableClass< + HashMapFromSelf, + hashMap_.HashMap, Schema.Type>, + hashMap_.HashMap, Schema.Encoded>, + Schema.Context | Schema.Context + > +{} + +/** + * @category HashMap transformations + * @since 3.10.0 + */ +export const HashMapFromSelf = ({ key, value }: { + readonly key: K + readonly value: V +}): HashMapFromSelf => { + return declare( + [key, value], + { + decode: (key, value) => hashMapParse(ParseResult.decodeUnknown(Array$(Tuple(key, value)))), + encode: (key, value) => hashMapParse(ParseResult.encodeUnknown(Array$(Tuple(key, value)))) + }, + { + description: `HashMap<${format(key)}, ${format(value)}>`, + pretty: hashMapPretty, + arbitrary: hashMapArbitrary, + equivalence: hashMapEquivalence + } + ) +} + +/** + * @category api interface + * @since 3.10.0 + */ +export interface HashMap extends + AnnotableClass< + HashMap, + hashMap_.HashMap, Schema.Type>, + ReadonlyArray, Schema.Encoded]>, + Schema.Context | Schema.Context + > +{} + +/** + * @category HashMap transformations + * @since 3.10.0 + */ +export const HashMap = ({ key, value }: { + readonly key: K + readonly value: V +}): HashMap => { + const key_ = asSchema(key) + const value_ = asSchema(value) + return transform( + Array$(Tuple(key_, value_)), + HashMapFromSelf({ key: typeSchema(key_), value: typeSchema(value_) }), + { strict: true, decode: (as) => hashMap_.fromIterable(as), encode: (map) => Array.from(map) } + ) +} + +const listArbitrary = (item: LazyArbitrary, ctx: GenerationContext): LazyArbitrary> => (fc) => { + const items = fc.array(item(fc)) + return (ctx.depthIdentifier !== undefined ? fc.oneof(ctx, fc.constant([]), items) : items).map(list_.fromIterable) +} + +const listPretty = (item: pretty_.Pretty): pretty_.Pretty> => (set) => + `List(${Array.from(set).map((a) => item(a)).join(", ")})` + +const listEquivalence = ( + item: Equivalence.Equivalence +): Equivalence.Equivalence> => { + const arrayEquivalence = array_.getEquivalence(item) + return Equivalence.make((a, b) => arrayEquivalence(Array.from(a), Array.from(b))) +} + +const listParse = ( + decodeUnknown: ParseResult.DecodeUnknown, R> +): ParseResult.DeclarationDecodeUnknown, R> => +(u, options, ast) => + list_.isList(u) ? + toComposite(decodeUnknown(Array.from(u), options), list_.fromIterable, ast, u) + : ParseResult.fail(new ParseResult.Type(ast, u)) + +/** + * @category api interface + * @since 3.10.0 + */ +export interface ListFromSelf extends + AnnotableClass< + ListFromSelf, + list_.List>, + list_.List>, + Schema.Context + > +{} + +/** + * @category List transformations + * @since 3.10.0 + */ +export const ListFromSelf = ( + value: Value +): ListFromSelf => { + return declare( + [value], + { + decode: (item) => listParse(ParseResult.decodeUnknown(Array$(item))), + encode: (item) => listParse(ParseResult.encodeUnknown(Array$(item))) + }, + { + description: `List<${format(value)}>`, + pretty: listPretty, + arbitrary: listArbitrary, + equivalence: listEquivalence + } + ) +} + +/** + * @category api interface + * @since 3.10.0 + */ +export interface List extends + AnnotableClass< + List, + list_.List>, + ReadonlyArray>, + Schema.Context + > +{} + +/** + * @category List transformations + * @since 3.10.0 + */ +export const List = (value: Value): List => { + const value_ = asSchema(value) + return transform( + Array$(value_), + ListFromSelf(typeSchema(value_)), + { strict: true, decode: (as) => list_.fromIterable(as), encode: (set) => Array.from(set) } + ) +} + +const sortedSetArbitrary = + (item: LazyArbitrary, ord: Order.Order, ctx: GenerationContext): LazyArbitrary> => + (fc) => { + const items = fc.array(item(fc)) + return (ctx.depthIdentifier !== undefined ? fc.oneof(ctx, fc.constant([]), items) : items).map((as) => + sortedSet_.fromIterable(as, ord) + ) + } + +const sortedSetPretty = (item: pretty_.Pretty): pretty_.Pretty> => (set) => + `new SortedSet([${Array.from(sortedSet_.values(set)).map((a) => item(a)).join(", ")}])` + +const sortedSetParse = ( + decodeUnknown: ParseResult.DecodeUnknown, R>, + ord: Order.Order +): ParseResult.DeclarationDecodeUnknown, R> => +(u, options, ast) => + sortedSet_.isSortedSet(u) ? + toComposite( + decodeUnknown(Array.from(sortedSet_.values(u)), options), + (as): sortedSet_.SortedSet => sortedSet_.fromIterable(as, ord), + ast, + u + ) + : ParseResult.fail(new ParseResult.Type(ast, u)) + +/** + * @category api interface + * @since 3.10.0 + */ +export interface SortedSetFromSelf extends + AnnotableClass< + SortedSetFromSelf, + sortedSet_.SortedSet>, + sortedSet_.SortedSet>, + Schema.Context + > +{} + +/** + * @category SortedSet transformations + * @since 3.10.0 + */ +export const SortedSetFromSelf = ( + value: Value, + ordA: Order.Order>, + ordI: Order.Order> +): SortedSetFromSelf => { + return declare( + [value], + { + decode: (item) => sortedSetParse(ParseResult.decodeUnknown(Array$(item)), ordA), + encode: (item) => sortedSetParse(ParseResult.encodeUnknown(Array$(item)), ordI) + }, + { + description: `SortedSet<${format(value)}>`, + pretty: sortedSetPretty, + arbitrary: (arb, ctx) => sortedSetArbitrary(arb, ordA, ctx), + equivalence: () => sortedSet_.getEquivalence>() + } + ) +} + +/** + * @category api interface + * @since 3.10.0 + */ +export interface SortedSet extends + AnnotableClass< + SortedSet, + sortedSet_.SortedSet>, + ReadonlyArray>, + Schema.Context + > +{} + +/** + * @category SortedSet transformations + * @since 3.10.0 + */ +export const SortedSet = ( + value: Value, + ordA: Order.Order> +): SortedSet => { + const value_ = asSchema(value) + const to = typeSchema(value_) + return transform( + Array$(value_), + SortedSetFromSelf(to, ordA, ordA), + { + strict: true, + decode: (as) => sortedSet_.fromIterable(as, ordA), + encode: (set) => Array.from(sortedSet_.values(set)) + } + ) +} + +/** + * Converts an arbitrary value to a `boolean` by testing whether it is truthy. + * Uses `!!val` to coerce the value to a `boolean`. + * + * @see https://developer.mozilla.org/docs/Glossary/Truthy + * @category boolean constructors + * @since 3.10.0 + */ +export class BooleanFromUnknown extends transform( + Unknown, + Boolean$, + { strict: true, decode: Predicate.isTruthy, encode: identity } +).annotations({ identifier: "BooleanFromUnknown" }) {} + +/** + * @category Config validations + * @since 3.10.0 + */ +export const Config = (name: string, schema: Schema): config_.Config => { + const decodeEither_ = decodeEither(schema) + return config_.string(name).pipe( + config_.mapOrFail((a) => + decodeEither_(a).pipe( + either_.mapLeft((error) => configError_.InvalidData([], TreeFormatter.formatErrorSync(error))) + ) + ) + ) +} diff --git a/packages/effect/src/SchemaAST.ts b/packages/effect/src/SchemaAST.ts new file mode 100644 index 0000000000..d32937d49d --- /dev/null +++ b/packages/effect/src/SchemaAST.ts @@ -0,0 +1,2744 @@ +/** + * @since 3.10.0 + */ + +import * as Arr from "effect/Array" +import type { Effect } from "effect/Effect" +import { dual, identity } from "effect/Function" +import { globalValue } from "effect/GlobalValue" +import * as Number from "effect/Number" +import * as Option from "effect/Option" +import * as Order from "effect/Order" +import * as Predicate from "effect/Predicate" +import * as regexp from "effect/RegExp" +import type { Concurrency } from "effect/Types" +import * as errors_ from "./internal/schema/errors.js" +import * as util_ from "./internal/schema/util.js" +import type { ParseIssue } from "./ParseResult.js" + +/** + * @category model + * @since 3.10.0 + */ +export type AST = + | Declaration + | Literal + | UniqueSymbol + | UndefinedKeyword + | VoidKeyword + | NeverKeyword + | UnknownKeyword + | AnyKeyword + | StringKeyword + | NumberKeyword + | BooleanKeyword + | BigIntKeyword + | SymbolKeyword + | ObjectKeyword + | Enums + | TemplateLiteral + // possible transformations + | Refinement + | TupleType + | TypeLiteral + | Union + | Suspend + // transformations + | Transformation + +// ------------------------------------------------------------------------------------- +// annotations +// ------------------------------------------------------------------------------------- + +/** + * @category annotations + * @since 3.10.0 + */ +export type BrandAnnotation = Arr.NonEmptyReadonlyArray + +/** + * @category annotations + * @since 3.10.0 + */ +export const BrandAnnotationId = Symbol.for("effect/Schema/annotation/Brand") + +/** + * @category annotations + * @since 3.10.0 + */ +export type TypeAnnotation = symbol + +/** + * @category annotations + * @since 3.10.0 + */ +export const TypeAnnotationId = Symbol.for("effect/Schema/annotation/Type") + +/** + * @category annotations + * @since 3.10.0 + */ +export type MessageAnnotation = (issue: ParseIssue) => string | Effect | { + readonly message: string | Effect + readonly override: boolean +} + +/** + * @category annotations + * @since 3.10.0 + */ +export const MessageAnnotationId = Symbol.for("effect/Schema/annotation/Message") + +/** + * @category annotations + * @since 3.10.0 + */ +export type MissingMessageAnnotation = () => string | Effect + +/** + * @category annotations + * @since 3.10.0 + */ +export const MissingMessageAnnotationId = Symbol.for("effect/Schema/annotation/MissingMessage") + +/** + * @category annotations + * @since 3.10.0 + */ +export type IdentifierAnnotation = string + +/** + * @category annotations + * @since 3.10.0 + */ +export const IdentifierAnnotationId = Symbol.for("effect/Schema/annotation/Identifier") + +/** + * @category annotations + * @since 3.10.0 + */ +export type TitleAnnotation = string + +/** + * @category annotations + * @since 3.10.0 + */ +export const TitleAnnotationId = Symbol.for("effect/Schema/annotation/Title") + +/** + * @category annotations + * @since 3.10.0 + */ +export type DescriptionAnnotation = string + +/** + * @category annotations + * @since 3.10.0 + */ +export const DescriptionAnnotationId = Symbol.for("effect/Schema/annotation/Description") + +/** + * @category annotations + * @since 3.10.0 + */ +export type ExamplesAnnotation = Arr.NonEmptyReadonlyArray + +/** + * @category annotations + * @since 3.10.0 + */ +export const ExamplesAnnotationId = Symbol.for("effect/Schema/annotation/Examples") + +/** + * @category annotations + * @since 3.10.0 + */ +export type DefaultAnnotation = A + +/** + * @category annotations + * @since 3.10.0 + */ +export const DefaultAnnotationId = Symbol.for("effect/Schema/annotation/Default") + +/** + * @category annotations + * @since 3.10.0 + */ +export type JSONSchemaAnnotation = object + +/** + * @category annotations + * @since 3.10.0 + */ +export const JSONSchemaAnnotationId = Symbol.for("effect/Schema/annotation/JSONSchema") + +/** + * @category annotations + * @since 3.10.0 + */ +export type DocumentationAnnotation = string + +/** + * @category annotations + * @since 3.10.0 + */ +export const DocumentationAnnotationId = Symbol.for("effect/Schema/annotation/Documentation") + +/** + * @category annotations + * @since 3.10.0 + */ +export type ConcurrencyAnnotation = Concurrency | undefined + +/** + * @category annotations + * @since 3.10.0 + */ +export const ConcurrencyAnnotationId = Symbol.for("effect/Schema/annotation/Concurrency") + +/** + * @category annotations + * @since 3.10.0 + */ +export type BatchingAnnotation = boolean | "inherit" | undefined + +/** + * @category annotations + * @since 3.10.0 + */ +export const BatchingAnnotationId = Symbol.for("effect/Schema/annotation/Batching") + +/** + * @category annotations + * @since 3.10.0 + */ +export type ParseIssueTitleAnnotation = (issue: ParseIssue) => string | undefined + +/** + * @category annotations + * @since 3.10.0 + */ +export const ParseIssueTitleAnnotationId = Symbol.for("effect/Schema/annotation/ParseIssueTitle") + +/** + * @category annotations + * @since 3.10.0 + */ +export const ParseOptionsAnnotationId = Symbol.for("effect/Schema/annotation/ParseOptions") + +/** + * @category annotations + * @since 3.10.0 + */ +export type DecodingFallbackAnnotation = (issue: ParseIssue) => Effect + +/** + * @category annotations + * @since 3.10.0 + */ +export const DecodingFallbackAnnotationId = Symbol.for("effect/Schema/annotation/DecodingFallback") + +/** @internal */ +export const SurrogateAnnotationId = Symbol.for("effect/Schema/annotation/Surrogate") + +/** + * Used by: + * + * - AST.keyof + * - AST.getPropertyKeyIndexedAccess + * - AST.getPropertyKeys + * - AST.getPropertySignatures + * - AST.getWeight + * - Parser.getLiterals + * + * @internal + */ +export type SurrogateAnnotation = AST + +/** @internal */ +export const StableFilterAnnotationId = Symbol.for("effect/Schema/annotation/StableFilter") + +/** + * A stable filter consistently applies fixed validation rules, such as + * 'minItems', 'maxItems', and 'itemsCount', to ensure array length complies + * with set criteria regardless of the input data's content. + * + * @internal + */ +export type StableFilterAnnotation = boolean + +/** + * @category annotations + * @since 3.10.0 + */ +export interface Annotations { + readonly [_: symbol]: unknown +} + +/** + * @category annotations + * @since 3.10.0 + */ +export interface Annotated { + readonly annotations: Annotations +} + +/** + * @category annotations + * @since 3.10.0 + */ +export const getAnnotation: { + (key: symbol): (annotated: Annotated) => Option.Option + (annotated: Annotated, key: symbol): Option.Option +} = dual( + 2, + (annotated: Annotated, key: symbol): Option.Option => + Object.prototype.hasOwnProperty.call(annotated.annotations, key) ? + Option.some(annotated.annotations[key] as any) : + Option.none() +) + +/** + * @category annotations + * @since 3.10.0 + */ +export const getBrandAnnotation = getAnnotation(BrandAnnotationId) + +/** + * @category annotations + * @since 3.10.0 + */ +export const getMessageAnnotation = getAnnotation(MessageAnnotationId) + +/** + * @category annotations + * @since 3.10.0 + */ +export const getMissingMessageAnnotation = getAnnotation(MissingMessageAnnotationId) + +/** + * @category annotations + * @since 3.10.0 + */ +export const getTitleAnnotation = getAnnotation(TitleAnnotationId) + +/** + * @category annotations + * @since 3.10.0 + */ +export const getIdentifierAnnotation = getAnnotation(IdentifierAnnotationId) + +/** + * @category annotations + * @since 3.10.0 + */ +export const getDescriptionAnnotation = getAnnotation(DescriptionAnnotationId) + +/** + * @category annotations + * @since 3.10.0 + */ +export const getExamplesAnnotation = getAnnotation>(ExamplesAnnotationId) + +/** + * @category annotations + * @since 3.10.0 + */ +export const getDefaultAnnotation = getAnnotation>(DefaultAnnotationId) + +/** + * @category annotations + * @since 3.10.0 + */ +export const getJSONSchemaAnnotation = getAnnotation(JSONSchemaAnnotationId) + +/** + * @category annotations + * @since 3.10.0 + */ +export const getDocumentationAnnotation = getAnnotation(DocumentationAnnotationId) + +/** + * @category annotations + * @since 3.10.0 + */ +export const getConcurrencyAnnotation = getAnnotation(ConcurrencyAnnotationId) + +/** + * @category annotations + * @since 3.10.0 + */ +export const getBatchingAnnotation = getAnnotation(BatchingAnnotationId) + +/** + * @category annotations + * @since 3.10.0 + */ +export const getParseIssueTitleAnnotation = getAnnotation(ParseIssueTitleAnnotationId) + +/** + * @category annotations + * @since 3.10.0 + */ +export const getParseOptionsAnnotation = getAnnotation(ParseOptionsAnnotationId) + +/** + * @category annotations + * @since 3.10.0 + */ +export const getDecodingFallbackAnnotation = getAnnotation>( + DecodingFallbackAnnotationId +) + +/** @internal */ +export const getSurrogateAnnotation = getAnnotation(SurrogateAnnotationId) + +const getStableFilterAnnotation = getAnnotation(StableFilterAnnotationId) + +/** @internal */ +export const hasStableFilter = (annotated: Annotated) => + Option.exists(getStableFilterAnnotation(annotated), (b) => b === true) + +const JSONIdentifierAnnotationId = Symbol.for("effect/Schema/annotation/JSONIdentifier") + +/** @internal */ +export const getJSONIdentifierAnnotation = getAnnotation(JSONIdentifierAnnotationId) + +/** + * @category model + * @since 3.10.0 + */ +export class Declaration implements Annotated { + /** + * @since 3.10.0 + */ + readonly _tag = "Declaration" + constructor( + readonly typeParameters: ReadonlyArray, + readonly decodeUnknown: ( + ...typeParameters: ReadonlyArray + ) => (input: unknown, options: ParseOptions, self: Declaration) => Effect, + readonly encodeUnknown: ( + ...typeParameters: ReadonlyArray + ) => (input: unknown, options: ParseOptions, self: Declaration) => Effect, + readonly annotations: Annotations = {} + ) {} + /** + * @since 3.10.0 + */ + toString() { + return Option.getOrElse(getExpected(this), () => "") + } + /** + * @since 3.10.0 + */ + toJSON(): object { + return { + _tag: this._tag, + typeParameters: this.typeParameters.map((ast) => ast.toJSON()), + annotations: toJSONAnnotations(this.annotations) + } + } +} + +const createASTGuard = (tag: T) => (ast: AST): ast is Extract => + ast._tag === tag + +/** + * @category guards + * @since 3.10.0 + */ +export const isDeclaration: (ast: AST) => ast is Declaration = createASTGuard("Declaration") + +/** + * @category model + * @since 3.10.0 + */ +export type LiteralValue = string | number | boolean | null | bigint + +/** + * @category model + * @since 3.10.0 + */ +export class Literal implements Annotated { + /** + * @since 3.10.0 + */ + readonly _tag = "Literal" + constructor(readonly literal: LiteralValue, readonly annotations: Annotations = {}) {} + /** + * @since 3.10.0 + */ + toString() { + return Option.getOrElse(getExpected(this), () => util_.formatUnknown(this.literal)) + } + /** + * @since 3.10.0 + */ + toJSON(): object { + return { + _tag: this._tag, + literal: Predicate.isBigInt(this.literal) ? String(this.literal) : this.literal, + annotations: toJSONAnnotations(this.annotations) + } + } +} + +/** + * @category guards + * @since 3.10.0 + */ +export const isLiteral: (ast: AST) => ast is Literal = createASTGuard("Literal") + +const $null = new Literal(null) + +export { + /** + * @category constructors + * @since 3.10.0 + */ + $null as null +} + +/** + * @category model + * @since 3.10.0 + */ +export class UniqueSymbol implements Annotated { + /** + * @since 3.10.0 + */ + readonly _tag = "UniqueSymbol" + constructor(readonly symbol: symbol, readonly annotations: Annotations = {}) {} + /** + * @since 3.10.0 + */ + toString() { + return Option.getOrElse(getExpected(this), () => util_.formatUnknown(this.symbol)) + } + /** + * @since 3.10.0 + */ + toJSON(): object { + return { + _tag: this._tag, + symbol: String(this.symbol), + annotations: toJSONAnnotations(this.annotations) + } + } +} + +/** + * @category guards + * @since 3.10.0 + */ +export const isUniqueSymbol: (ast: AST) => ast is UniqueSymbol = createASTGuard("UniqueSymbol") + +/** + * @category model + * @since 3.10.0 + */ +export class UndefinedKeyword implements Annotated { + /** + * @since 3.10.0 + */ + readonly _tag = "UndefinedKeyword" + constructor(readonly annotations: Annotations = {}) {} + /** + * @since 3.10.0 + */ + toString() { + return formatKeyword(this) + } + /** + * @since 3.10.0 + */ + toJSON(): object { + return { + _tag: this._tag, + annotations: toJSONAnnotations(this.annotations) + } + } +} + +/** + * @category constructors + * @since 3.10.0 + */ +export const undefinedKeyword: UndefinedKeyword = new UndefinedKeyword({ + [TitleAnnotationId]: "undefined" +}) + +/** + * @category guards + * @since 3.10.0 + */ +export const isUndefinedKeyword: (ast: AST) => ast is UndefinedKeyword = createASTGuard("UndefinedKeyword") + +/** + * @category model + * @since 3.10.0 + */ +export class VoidKeyword implements Annotated { + /** + * @since 3.10.0 + */ + readonly _tag = "VoidKeyword" + constructor(readonly annotations: Annotations = {}) {} + /** + * @since 3.10.0 + */ + toString() { + return formatKeyword(this) + } + /** + * @since 3.10.0 + */ + toJSON(): object { + return { + _tag: this._tag, + annotations: toJSONAnnotations(this.annotations) + } + } +} + +/** + * @category constructors + * @since 3.10.0 + */ +export const voidKeyword: VoidKeyword = new VoidKeyword({ + [TitleAnnotationId]: "void" +}) + +/** + * @category guards + * @since 3.10.0 + */ +export const isVoidKeyword: (ast: AST) => ast is VoidKeyword = createASTGuard("VoidKeyword") + +/** + * @category model + * @since 3.10.0 + */ +export class NeverKeyword implements Annotated { + /** + * @since 3.10.0 + */ + readonly _tag = "NeverKeyword" + constructor(readonly annotations: Annotations = {}) {} + /** + * @since 3.10.0 + */ + toString() { + return formatKeyword(this) + } + /** + * @since 3.10.0 + */ + toJSON(): object { + return { + _tag: this._tag, + annotations: toJSONAnnotations(this.annotations) + } + } +} + +/** + * @category constructors + * @since 3.10.0 + */ +export const neverKeyword: NeverKeyword = new NeverKeyword({ + [TitleAnnotationId]: "never" +}) + +/** + * @category guards + * @since 3.10.0 + */ +export const isNeverKeyword: (ast: AST) => ast is NeverKeyword = createASTGuard("NeverKeyword") + +/** + * @category model + * @since 3.10.0 + */ +export class UnknownKeyword implements Annotated { + /** + * @since 3.10.0 + */ + readonly _tag = "UnknownKeyword" + constructor(readonly annotations: Annotations = {}) {} + /** + * @since 3.10.0 + */ + toString() { + return formatKeyword(this) + } + /** + * @since 3.10.0 + */ + toJSON(): object { + return { + _tag: this._tag, + annotations: toJSONAnnotations(this.annotations) + } + } +} + +/** + * @category constructors + * @since 3.10.0 + */ +export const unknownKeyword: UnknownKeyword = new UnknownKeyword({ + [TitleAnnotationId]: "unknown" +}) + +/** + * @category guards + * @since 3.10.0 + */ +export const isUnknownKeyword: (ast: AST) => ast is UnknownKeyword = createASTGuard("UnknownKeyword") + +/** + * @category model + * @since 3.10.0 + */ +export class AnyKeyword implements Annotated { + /** + * @since 3.10.0 + */ + readonly _tag = "AnyKeyword" + constructor(readonly annotations: Annotations = {}) {} + /** + * @since 3.10.0 + */ + toString() { + return formatKeyword(this) + } + /** + * @since 3.10.0 + */ + toJSON(): object { + return { + _tag: this._tag, + annotations: toJSONAnnotations(this.annotations) + } + } +} + +/** + * @category constructors + * @since 3.10.0 + */ +export const anyKeyword: AnyKeyword = new AnyKeyword({ + [TitleAnnotationId]: "any" +}) + +/** + * @category guards + * @since 3.10.0 + */ +export const isAnyKeyword: (ast: AST) => ast is AnyKeyword = createASTGuard("AnyKeyword") + +/** + * @category model + * @since 3.10.0 + */ +export class StringKeyword implements Annotated { + /** + * @since 3.10.0 + */ + readonly _tag = "StringKeyword" + constructor(readonly annotations: Annotations = {}) {} + /** + * @since 3.10.0 + */ + toString() { + return formatKeyword(this) + } + /** + * @since 3.10.0 + */ + toJSON(): object { + return { + _tag: this._tag, + annotations: toJSONAnnotations(this.annotations) + } + } +} + +/** + * @category constructors + * @since 3.10.0 + */ +export const stringKeyword: StringKeyword = new StringKeyword({ + [TitleAnnotationId]: "string", + [DescriptionAnnotationId]: "a string" +}) + +/** + * @category guards + * @since 3.10.0 + */ +export const isStringKeyword: (ast: AST) => ast is StringKeyword = createASTGuard("StringKeyword") + +/** + * @category model + * @since 3.10.0 + */ +export class NumberKeyword implements Annotated { + /** + * @since 3.10.0 + */ + readonly _tag = "NumberKeyword" + constructor(readonly annotations: Annotations = {}) {} + /** + * @since 3.10.0 + */ + toString() { + return formatKeyword(this) + } + /** + * @since 3.10.0 + */ + toJSON(): object { + return { + _tag: this._tag, + annotations: toJSONAnnotations(this.annotations) + } + } +} + +/** + * @category constructors + * @since 3.10.0 + */ +export const numberKeyword: NumberKeyword = new NumberKeyword({ + [TitleAnnotationId]: "number", + [DescriptionAnnotationId]: "a number" +}) + +/** + * @category guards + * @since 3.10.0 + */ +export const isNumberKeyword: (ast: AST) => ast is NumberKeyword = createASTGuard("NumberKeyword") + +/** + * @category model + * @since 3.10.0 + */ +export class BooleanKeyword implements Annotated { + /** + * @since 3.10.0 + */ + readonly _tag = "BooleanKeyword" + constructor(readonly annotations: Annotations = {}) {} + /** + * @since 3.10.0 + */ + toString() { + return formatKeyword(this) + } + /** + * @since 3.10.0 + */ + toJSON(): object { + return { + _tag: this._tag, + annotations: toJSONAnnotations(this.annotations) + } + } +} + +/** + * @category constructors + * @since 3.10.0 + */ +export const booleanKeyword: BooleanKeyword = new BooleanKeyword({ + [TitleAnnotationId]: "boolean", + [DescriptionAnnotationId]: "a boolean" +}) + +/** + * @category guards + * @since 3.10.0 + */ +export const isBooleanKeyword: (ast: AST) => ast is BooleanKeyword = createASTGuard("BooleanKeyword") + +/** + * @category model + * @since 3.10.0 + */ +export class BigIntKeyword implements Annotated { + /** + * @since 3.10.0 + */ + readonly _tag = "BigIntKeyword" + constructor(readonly annotations: Annotations = {}) {} + /** + * @since 3.10.0 + */ + toString() { + return formatKeyword(this) + } + /** + * @since 3.10.0 + */ + toJSON(): object { + return { + _tag: this._tag, + annotations: toJSONAnnotations(this.annotations) + } + } +} + +/** + * @category constructors + * @since 3.10.0 + */ +export const bigIntKeyword: BigIntKeyword = new BigIntKeyword({ + [TitleAnnotationId]: "bigint", + [DescriptionAnnotationId]: "a bigint" +}) + +/** + * @category guards + * @since 3.10.0 + */ +export const isBigIntKeyword: (ast: AST) => ast is BigIntKeyword = createASTGuard("BigIntKeyword") + +/** + * @category model + * @since 3.10.0 + */ +export class SymbolKeyword implements Annotated { + /** + * @since 3.10.0 + */ + readonly _tag = "SymbolKeyword" + constructor(readonly annotations: Annotations = {}) {} + /** + * @since 3.10.0 + */ + toString() { + return formatKeyword(this) + } + /** + * @since 3.10.0 + */ + toJSON(): object { + return { + _tag: this._tag, + annotations: toJSONAnnotations(this.annotations) + } + } +} + +/** + * @category constructors + * @since 3.10.0 + */ +export const symbolKeyword: SymbolKeyword = new SymbolKeyword({ + [TitleAnnotationId]: "symbol", + [DescriptionAnnotationId]: "a symbol" +}) + +/** + * @category guards + * @since 3.10.0 + */ +export const isSymbolKeyword: (ast: AST) => ast is SymbolKeyword = createASTGuard("SymbolKeyword") + +/** + * @category model + * @since 3.10.0 + */ +export class ObjectKeyword implements Annotated { + /** + * @since 3.10.0 + */ + readonly _tag = "ObjectKeyword" + constructor(readonly annotations: Annotations = {}) {} + /** + * @since 3.10.0 + */ + toString() { + return formatKeyword(this) + } + /** + * @since 3.10.0 + */ + toJSON(): object { + return { + _tag: this._tag, + annotations: toJSONAnnotations(this.annotations) + } + } +} + +/** + * @category constructors + * @since 3.10.0 + */ +export const objectKeyword: ObjectKeyword = new ObjectKeyword({ + [TitleAnnotationId]: "object", + [DescriptionAnnotationId]: "an object in the TypeScript meaning, i.e. the `object` type" +}) + +/** + * @category guards + * @since 3.10.0 + */ +export const isObjectKeyword: (ast: AST) => ast is ObjectKeyword = createASTGuard("ObjectKeyword") + +/** + * @category model + * @since 3.10.0 + */ +export class Enums implements Annotated { + /** + * @since 3.10.0 + */ + readonly _tag = "Enums" + constructor( + readonly enums: ReadonlyArray, + readonly annotations: Annotations = {} + ) {} + /** + * @since 3.10.0 + */ + toString() { + return Option.getOrElse( + getExpected(this), + () => ` JSON.stringify(value)).join(" | ")}>` + ) + } + /** + * @since 3.10.0 + */ + toJSON(): object { + return { + _tag: this._tag, + enums: this.enums, + annotations: toJSONAnnotations(this.annotations) + } + } +} + +/** + * @category guards + * @since 3.10.0 + */ +export const isEnums: (ast: AST) => ast is Enums = createASTGuard("Enums") + +/** + * @category model + * @since 3.10.0 + */ +export class TemplateLiteralSpan { + constructor(readonly type: StringKeyword | NumberKeyword, readonly literal: string) {} + /** + * @since 3.10.0 + */ + toString() { + const type = "${" + String(this.type) + "}" + return type + this.literal + } + /** + * @since 3.10.0 + */ + toJSON(): object { + return { + type: this.type.toJSON(), + literal: this.literal + } + } +} + +/** + * @category model + * @since 3.10.0 + */ +export class TemplateLiteral implements Annotated { + /** + * @since 3.10.0 + */ + readonly _tag = "TemplateLiteral" + constructor( + readonly head: string, + readonly spans: Arr.NonEmptyReadonlyArray, + readonly annotations: Annotations = {} + ) {} + /** + * @since 3.10.0 + */ + toString() { + return Option.getOrElse(getExpected(this), () => formatTemplateLiteral(this)) + } + /** + * @since 3.10.0 + */ + toJSON(): object { + return { + _tag: this._tag, + head: this.head, + spans: this.spans.map((span) => span.toJSON()), + annotations: toJSONAnnotations(this.annotations) + } + } +} + +const formatTemplateLiteral = (ast: TemplateLiteral): string => + "`" + ast.head + ast.spans.map((span) => String(span)).join("") + + "`" + +/** + * @category guards + * @since 3.10.0 + */ +export const isTemplateLiteral: (ast: AST) => ast is TemplateLiteral = createASTGuard("TemplateLiteral") + +/** + * @category model + * @since 3.10.0 + */ +export class Type implements Annotated { + constructor( + readonly type: AST, + readonly annotations: Annotations = {} + ) {} + /** + * @since 3.10.0 + */ + toJSON(): object { + return { + type: this.type.toJSON(), + annotations: toJSONAnnotations(this.annotations) + } + } + /** + * @since 3.10.0 + */ + toString() { + return String(this.type) + } +} + +/** + * @category model + * @since 3.10.0 + */ +export class OptionalType extends Type { + constructor( + type: AST, + readonly isOptional: boolean, + annotations: Annotations = {} + ) { + super(type, annotations) + } + /** + * @since 3.10.0 + */ + toJSON(): object { + return { + type: this.type.toJSON(), + isOptional: this.isOptional, + annotations: toJSONAnnotations(this.annotations) + } + } + /** + * @since 3.10.0 + */ + toString() { + return String(this.type) + (this.isOptional ? "?" : "") + } +} + +const getRestASTs = (rest: ReadonlyArray): ReadonlyArray => rest.map((annotatedAST) => annotatedAST.type) + +/** + * @category model + * @since 3.10.0 + */ +export class TupleType implements Annotated { + /** + * @since 3.10.0 + */ + readonly _tag = "TupleType" + constructor( + readonly elements: ReadonlyArray, + readonly rest: ReadonlyArray, + readonly isReadonly: boolean, + readonly annotations: Annotations = {} + ) { + let hasOptionalElement = false + let hasIllegalRequiredElement = false + for (const e of elements) { + if (e.isOptional) { + hasOptionalElement = true + } else if (hasOptionalElement) { + hasIllegalRequiredElement = true + break + } + } + if (hasIllegalRequiredElement || (hasOptionalElement && rest.length > 1)) { + throw new Error(errors_.getASTRequiredElementFollowinAnOptionalElementErrorMessage) + } + } + /** + * @since 3.10.0 + */ + toString() { + return Option.getOrElse(getExpected(this), () => formatTuple(this)) + } + /** + * @since 3.10.0 + */ + toJSON(): object { + return { + _tag: this._tag, + elements: this.elements.map((e) => e.toJSON()), + rest: this.rest.map((ast) => ast.toJSON()), + isReadonly: this.isReadonly, + annotations: toJSONAnnotations(this.annotations) + } + } +} + +const formatTuple = (ast: TupleType): string => { + const formattedElements = ast.elements.map(String) + .join(", ") + return Arr.matchLeft(ast.rest, { + onEmpty: () => `readonly [${formattedElements}]`, + onNonEmpty: (head, tail) => { + const formattedHead = String(head) + const wrappedHead = formattedHead.includes(" | ") ? `(${formattedHead})` : formattedHead + + if (tail.length > 0) { + const formattedTail = tail.map(String).join(", ") + if (ast.elements.length > 0) { + return `readonly [${formattedElements}, ...${wrappedHead}[], ${formattedTail}]` + } else { + return `readonly [...${wrappedHead}[], ${formattedTail}]` + } + } else { + if (ast.elements.length > 0) { + return `readonly [${formattedElements}, ...${wrappedHead}[]]` + } else { + return `ReadonlyArray<${formattedHead}>` + } + } + } + }) +} + +/** + * @category guards + * @since 3.10.0 + */ +export const isTupleType: (ast: AST) => ast is TupleType = createASTGuard("TupleType") + +/** + * @category model + * @since 3.10.0 + */ +export class PropertySignature extends OptionalType { + constructor( + readonly name: PropertyKey, + type: AST, + isOptional: boolean, + readonly isReadonly: boolean, + annotations?: Annotations + ) { + super(type, isOptional, annotations) + } + /** + * @since 3.10.0 + */ + toString(): string { + return (this.isReadonly ? "readonly " : "") + String(this.name) + (this.isOptional ? "?" : "") + ": " + + this.type + } + /** + * @since 3.10.0 + */ + toJSON(): object { + return { + name: String(this.name), + type: this.type.toJSON(), + isOptional: this.isOptional, + isReadonly: this.isReadonly, + annotations: toJSONAnnotations(this.annotations) + } + } +} + +/** + * @since 3.10.0 + */ +export type Parameter = StringKeyword | SymbolKeyword | TemplateLiteral | Refinement + +/** + * @since 3.10.0 + */ +export const isParameter = (ast: AST): ast is Parameter => { + switch (ast._tag) { + case "StringKeyword": + case "SymbolKeyword": + case "TemplateLiteral": + return true + case "Refinement": + return isParameter(ast.from) + } + return false +} + +/** + * @category model + * @since 3.10.0 + */ +export class IndexSignature { + /** + * @since 3.10.0 + */ + readonly parameter: Parameter + constructor( + parameter: AST, + readonly type: AST, + readonly isReadonly: boolean + ) { + if (isParameter(parameter)) { + this.parameter = parameter + } else { + throw new Error(errors_.getASTIndexSignatureParameterErrorMessage) + } + } + /** + * @since 3.10.0 + */ + toString(): string { + return (this.isReadonly ? "readonly " : "") + `[x: ${this.parameter}]: ${this.type}` + } + /** + * @since 3.10.0 + */ + toJSON(): object { + return { + parameter: this.parameter.toJSON(), + type: this.type.toJSON(), + isReadonly: this.isReadonly + } + } +} + +/** + * @category model + * @since 3.10.0 + */ +export class TypeLiteral implements Annotated { + /** + * @since 3.10.0 + */ + readonly _tag = "TypeLiteral" + /** + * @since 3.10.0 + */ + readonly propertySignatures: ReadonlyArray + /** + * @since 3.10.0 + */ + readonly indexSignatures: ReadonlyArray + constructor( + propertySignatures: ReadonlyArray, + indexSignatures: ReadonlyArray, + readonly annotations: Annotations = {} + ) { + // check for duplicate property signatures + const keys: Record = {} + for (let i = 0; i < propertySignatures.length; i++) { + const name = propertySignatures[i].name + if (Object.prototype.hasOwnProperty.call(keys, name)) { + throw new Error(errors_.getASTDuplicatePropertySignatureErrorMessage(name)) + } + keys[name] = null + } + // check for duplicate index signatures + const parameters = { + string: false, + symbol: false + } + for (let i = 0; i < indexSignatures.length; i++) { + const parameter = getParameterBase(indexSignatures[i].parameter) + if (isStringKeyword(parameter)) { + if (parameters.string) { + throw new Error(errors_.getASTDuplicateIndexSignatureErrorMessage("string")) + } + parameters.string = true + } else if (isSymbolKeyword(parameter)) { + if (parameters.symbol) { + throw new Error(errors_.getASTDuplicateIndexSignatureErrorMessage("symbol")) + } + parameters.symbol = true + } + } + + this.propertySignatures = propertySignatures + this.indexSignatures = indexSignatures + } + /** + * @since 3.10.0 + */ + toString() { + return Option.getOrElse(getExpected(this), () => formatTypeLiteral(this)) + } + /** + * @since 3.10.0 + */ + toJSON(): object { + return { + _tag: this._tag, + propertySignatures: this.propertySignatures.map((ps) => ps.toJSON()), + indexSignatures: this.indexSignatures.map((ps) => ps.toJSON()), + annotations: toJSONAnnotations(this.annotations) + } + } +} + +const formatIndexSignatures = (iss: ReadonlyArray): string => iss.map(String).join("; ") + +const formatTypeLiteral = (ast: TypeLiteral): string => { + if (ast.propertySignatures.length > 0) { + const pss = ast.propertySignatures.map(String).join("; ") + if (ast.indexSignatures.length > 0) { + return `{ ${pss}; ${formatIndexSignatures(ast.indexSignatures)} }` + } else { + return `{ ${pss} }` + } + } else { + if (ast.indexSignatures.length > 0) { + return `{ ${formatIndexSignatures(ast.indexSignatures)} }` + } else { + return "{}" + } + } +} + +/** + * @category guards + * @since 3.10.0 + */ +export const isTypeLiteral: (ast: AST) => ast is TypeLiteral = createASTGuard("TypeLiteral") + +/** + * @since 3.10.0 + */ +export type Members = readonly [A, A, ...Array] + +const sortCandidates = Arr.sort( + Order.mapInput(Number.Order, (ast: AST) => { + switch (ast._tag) { + case "AnyKeyword": + return 0 + case "UnknownKeyword": + return 1 + case "ObjectKeyword": + return 2 + case "StringKeyword": + case "NumberKeyword": + case "BooleanKeyword": + case "BigIntKeyword": + case "SymbolKeyword": + return 3 + } + return 4 + }) +) + +const literalMap = { + string: "StringKeyword", + number: "NumberKeyword", + boolean: "BooleanKeyword", + bigint: "BigIntKeyword" +} as const + +/** @internal */ +export const flatten = (candidates: ReadonlyArray): Array => + Arr.flatMap(candidates, (ast) => isUnion(ast) ? flatten(ast.types) : [ast]) + +/** @internal */ +export const unify = (candidates: ReadonlyArray): Array => { + const cs = sortCandidates(candidates) + const out: Array = [] + const uniques: { [K in AST["_tag"] | "{}"]?: AST } = {} + const literals: Array = [] + for (const ast of cs) { + switch (ast._tag) { + case "NeverKeyword": + break + case "AnyKeyword": + return [anyKeyword] + case "UnknownKeyword": + return [unknownKeyword] + // uniques + case "ObjectKeyword": + case "UndefinedKeyword": + case "VoidKeyword": + case "StringKeyword": + case "NumberKeyword": + case "BooleanKeyword": + case "BigIntKeyword": + case "SymbolKeyword": { + if (!uniques[ast._tag]) { + uniques[ast._tag] = ast + out.push(ast) + } + break + } + case "Literal": { + const type = typeof ast.literal + switch (type) { + case "string": + case "number": + case "bigint": + case "boolean": { + const _tag = literalMap[type] + if (!uniques[_tag] && !literals.includes(ast.literal)) { + literals.push(ast.literal) + out.push(ast) + } + break + } + // null + case "object": { + if (!literals.includes(ast.literal)) { + literals.push(ast.literal) + out.push(ast) + } + break + } + } + break + } + case "UniqueSymbol": { + if (!uniques["SymbolKeyword"] && !literals.includes(ast.symbol)) { + literals.push(ast.symbol) + out.push(ast) + } + break + } + case "TupleType": { + if (!uniques["ObjectKeyword"]) { + out.push(ast) + } + break + } + case "TypeLiteral": { + if (ast.propertySignatures.length === 0 && ast.indexSignatures.length === 0) { + if (!uniques["{}"]) { + uniques["{}"] = ast + out.push(ast) + } + } else if (!uniques["ObjectKeyword"]) { + out.push(ast) + } + break + } + default: + out.push(ast) + } + } + return out +} + +/** + * @category model + * @since 3.10.0 + */ +export class Union implements Annotated { + static make = (types: ReadonlyArray, annotations?: Annotations): AST => { + return isMembers(types) ? new Union(types, annotations) : types.length === 1 ? types[0] : neverKeyword + } + /** @internal */ + static unify = (candidates: ReadonlyArray, annotations?: Annotations): AST => { + return Union.make(unify(flatten(candidates)), annotations) + } + /** + * @since 3.10.0 + */ + readonly _tag = "Union" + private constructor(readonly types: Members, readonly annotations: Annotations = {}) {} + /** + * @since 3.10.0 + */ + toString() { + return Option.getOrElse( + getExpected(this), + () => this.types.map(String).join(" | ") + ) + } + /** + * @since 3.10.0 + */ + toJSON(): object { + return { + _tag: this._tag, + types: this.types.map((ast) => ast.toJSON()), + annotations: toJSONAnnotations(this.annotations) + } + } +} + +/** @internal */ +export const mapMembers = (members: Members, f: (a: A) => B): Members => members.map(f) as any + +/** @internal */ +export const isMembers = (as: ReadonlyArray): as is Members => as.length > 1 + +/** + * @category guards + * @since 3.10.0 + */ +export const isUnion: (ast: AST) => ast is Union = createASTGuard("Union") + +const toJSONMemoMap = globalValue( + Symbol.for("effect/Schema/AST/toJSONMemoMap"), + () => new WeakMap() +) + +/** + * @category model + * @since 3.10.0 + */ +export class Suspend implements Annotated { + /** + * @since 3.10.0 + */ + readonly _tag = "Suspend" + constructor(readonly f: () => AST, readonly annotations: Annotations = {}) { + this.f = util_.memoizeThunk(f) + } + /** + * @since 3.10.0 + */ + toString() { + return getExpected(this).pipe( + Option.orElse(() => + Option.flatMap( + Option.liftThrowable(this.f)(), + (ast) => getExpected(ast) + ) + ), + Option.getOrElse(() => "") + ) + } + /** + * @since 3.10.0 + */ + toJSON(): object { + const ast = this.f() + let out = toJSONMemoMap.get(ast) + if (out) { + return out + } + toJSONMemoMap.set(ast, { _tag: this._tag }) + out = { + _tag: this._tag, + ast: ast.toJSON(), + annotations: toJSONAnnotations(this.annotations) + } + toJSONMemoMap.set(ast, out) + return out + } +} + +/** + * @category guards + * @since 3.10.0 + */ +export const isSuspend: (ast: AST) => ast is Suspend = createASTGuard("Suspend") + +/** + * @category model + * @since 3.10.0 + */ +export class Refinement implements Annotated { + /** + * @since 3.10.0 + */ + readonly _tag = "Refinement" + constructor( + readonly from: From, + readonly filter: ( + input: any, + options: ParseOptions, + self: Refinement + ) => Option.Option, + readonly annotations: Annotations = {} + ) {} + /** + * @since 3.10.0 + */ + toString() { + return Option.getOrElse(getExpected(this), () => `{ ${this.from} | filter }`) + } + /** + * @since 3.10.0 + */ + toJSON(): object { + return { + _tag: this._tag, + from: this.from.toJSON(), + annotations: toJSONAnnotations(this.annotations) + } + } +} + +/** + * @category guards + * @since 3.10.0 + */ +export const isRefinement: (ast: AST) => ast is Refinement = createASTGuard("Refinement") + +/** + * @category model + * @since 3.10.0 + */ +export interface ParseOptions { + /** + * The `errors` option allows you to receive all parsing errors when + * attempting to parse a value using a schema. By default only the first error + * is returned, but by setting the `errors` option to `"all"`, you can receive + * all errors that occurred during the parsing process. This can be useful for + * debugging or for providing more comprehensive error messages to the user. + * + * default: "first" + * + * @since 3.10.0 + */ + readonly errors?: "first" | "all" | undefined + /** + * When using a `Schema` to parse a value, by default any properties that are + * not specified in the `Schema` will be stripped out from the output. This is + * because the `Schema` is expecting a specific shape for the parsed value, + * and any excess properties do not conform to that shape. + * + * However, you can use the `onExcessProperty` option (default value: + * `"ignore"`) to trigger a parsing error. This can be particularly useful in + * cases where you need to detect and handle potential errors or unexpected + * values. + * + * If you want to allow excess properties to remain, you can use + * `onExcessProperty` set to `"preserve"`. + * + * default: "ignore" + * + * @since 3.10.0 + */ + readonly onExcessProperty?: "ignore" | "error" | "preserve" | undefined + /** + * The `propertyOrder` option provides control over the order of object fields + * in the output. This feature is particularly useful when the sequence of + * keys is important for the consuming processes or when maintaining the input + * order enhances readability and usability. + * + * By default, the `propertyOrder` option is set to `"none"`. This means that + * the internal system decides the order of keys to optimize parsing speed. + * The order of keys in this mode should not be considered stable, and it's + * recommended not to rely on key ordering as it may change in future updates + * without notice. + * + * Setting `propertyOrder` to `"original"` ensures that the keys are ordered + * as they appear in the input during the decoding/encoding process. + * + * default: "none" + * + * @since 3.10.0 + */ + readonly propertyOrder?: "none" | "original" | undefined + /** + * Handles missing properties in data structures. By default, missing + * properties are treated as if present with an `undefined` value. To treat + * missing properties as errors, set the `exact` option to `true`. This + * setting is already enabled by default for `is` and `asserts` functions, + * treating absent properties strictly unless overridden. + * + * default: false + * + * @since 3.10.0 + */ + readonly exact?: boolean | undefined +} + +/** + * @since 3.10.0 + */ +export const defaultParseOption: ParseOptions = {} + +/** + * @category model + * @since 3.10.0 + */ +export class Transformation implements Annotated { + /** + * @since 3.10.0 + */ + readonly _tag = "Transformation" + constructor( + readonly from: AST, + readonly to: AST, + readonly transformation: TransformationKind, + readonly annotations: Annotations = {} + ) {} + /** + * @since 3.10.0 + */ + toString() { + return Option.getOrElse( + getExpected(this), + () => `(${String(this.from)} <-> ${String(this.to)})` + ) + } + /** + * @since 3.10.0 + */ + toJSON(): object { + return { + _tag: this._tag, + from: this.from.toJSON(), + to: this.to.toJSON(), + annotations: toJSONAnnotations(this.annotations) + } + } +} + +/** + * @category guards + * @since 3.10.0 + */ +export const isTransformation: (ast: AST) => ast is Transformation = createASTGuard("Transformation") + +/** + * @category model + * @since 3.10.0 + */ +export type TransformationKind = + | FinalTransformation + | ComposeTransformation + | TypeLiteralTransformation + +/** + * @category model + * @since 3.10.0 + */ +export class FinalTransformation { + /** + * @since 3.10.0 + */ + readonly _tag = "FinalTransformation" + constructor( + readonly decode: ( + fromA: any, + options: ParseOptions, + self: Transformation, + fromI: any + ) => Effect, + readonly encode: (toI: any, options: ParseOptions, self: Transformation, toA: any) => Effect + ) {} +} + +const createTransformationGuard = + (tag: T) => + (ast: TransformationKind): ast is Extract => ast._tag === tag + +/** + * @category guards + * @since 3.10.0 + */ +export const isFinalTransformation: (ast: TransformationKind) => ast is FinalTransformation = createTransformationGuard( + "FinalTransformation" +) + +/** + * @category model + * @since 3.10.0 + */ +export class ComposeTransformation { + /** + * @since 3.10.0 + */ + readonly _tag = "ComposeTransformation" +} + +/** + * @category constructors + * @since 3.10.0 + */ +export const composeTransformation: ComposeTransformation = new ComposeTransformation() + +/** + * @category guards + * @since 3.10.0 + */ +export const isComposeTransformation: (ast: TransformationKind) => ast is ComposeTransformation = + createTransformationGuard( + "ComposeTransformation" + ) + +/** + * Represents a `PropertySignature -> PropertySignature` transformation + * + * The semantic of `decode` is: + * - `none()` represents the absence of the key/value pair + * - `some(value)` represents the presence of the key/value pair + * + * The semantic of `encode` is: + * - `none()` you don't want to output the key/value pair + * - `some(value)` you want to output the key/value pair + * + * @category model + * @since 3.10.0 + */ +export class PropertySignatureTransformation { + constructor( + readonly from: PropertyKey, + readonly to: PropertyKey, + readonly decode: (o: Option.Option) => Option.Option, + readonly encode: (o: Option.Option) => Option.Option + ) {} +} + +const isRenamingPropertySignatureTransformation = (t: PropertySignatureTransformation) => + t.decode === identity && t.encode === identity + +/** + * @category model + * @since 3.10.0 + */ +export class TypeLiteralTransformation { + /** + * @since 3.10.0 + */ + readonly _tag = "TypeLiteralTransformation" + constructor( + readonly propertySignatureTransformations: ReadonlyArray< + PropertySignatureTransformation + > + ) { + // check for duplicate property signature transformations + const fromKeys: Record = {} + const toKeys: Record = {} + for (const pst of propertySignatureTransformations) { + const from = pst.from + if (fromKeys[from]) { + throw new Error(errors_.getASTDuplicatePropertySignatureTransformationErrorMessage(from)) + } + fromKeys[from] = true + const to = pst.to + if (toKeys[to]) { + throw new Error(errors_.getASTDuplicatePropertySignatureTransformationErrorMessage(to)) + } + toKeys[to] = true + } + } +} + +/** + * @category guards + * @since 3.10.0 + */ +export const isTypeLiteralTransformation: (ast: TransformationKind) => ast is TypeLiteralTransformation = + createTransformationGuard("TypeLiteralTransformation") + +// ------------------------------------------------------------------------------------- +// API +// ------------------------------------------------------------------------------------- + +/** + * Merges a set of new annotations with existing ones, potentially overwriting + * any duplicates. + * + * @since 3.10.0 + */ +export const annotations = (ast: AST, annotations: Annotations): AST => { + const d = Object.getOwnPropertyDescriptors(ast) + d.annotations.value = { ...ast.annotations, ...annotations } + return Object.create(Object.getPrototypeOf(ast), d) +} + +/** + * Equivalent at runtime to the TypeScript type-level `keyof` operator. + * + * @since 3.10.0 + */ +export const keyof = (ast: AST): AST => Union.unify(_keyof(ast)) + +const STRING_KEYWORD_PATTERN = ".*" +const NUMBER_KEYWORD_PATTERN = "[+-]?\\d*\\.?\\d+(?:[Ee][+-]?\\d+)?" + +/** + * @since 3.10.0 + */ +export const getTemplateLiteralRegExp = (ast: TemplateLiteral): RegExp => { + let pattern = `^${regexp.escape(ast.head)}` + + for (const span of ast.spans) { + if (isStringKeyword(span.type)) { + pattern += STRING_KEYWORD_PATTERN + } else if (isNumberKeyword(span.type)) { + pattern += NUMBER_KEYWORD_PATTERN + } + pattern += regexp.escape(span.literal) + } + + pattern += "$" + return new RegExp(pattern) +} + +/** + * @since 3.10.0 + */ +export const getTemplateLiteralCapturingRegExp = (ast: TemplateLiteral): RegExp => { + let pattern = `^` + if (ast.head !== "") { + pattern += `(${regexp.escape(ast.head)})` + } + + for (const span of ast.spans) { + if (isStringKeyword(span.type)) { + pattern += `(${STRING_KEYWORD_PATTERN})` + } else if (isNumberKeyword(span.type)) { + pattern += `(${NUMBER_KEYWORD_PATTERN})` + } + if (span.literal !== "") { + pattern += `(${regexp.escape(span.literal)})` + } + } + + pattern += "$" + return new RegExp(pattern) +} + +/** + * @since 3.10.0 + */ +export const getPropertySignatures = (ast: AST): Array => { + switch (ast._tag) { + case "Declaration": { + const annotation = getSurrogateAnnotation(ast) + if (Option.isSome(annotation)) { + return getPropertySignatures(annotation.value) + } + break + } + case "TypeLiteral": + return ast.propertySignatures.slice() + case "Suspend": + return getPropertySignatures(ast.f()) + } + return getPropertyKeys(ast).map((name) => getPropertyKeyIndexedAccess(ast, name)) +} + +/** @internal */ +export const getNumberIndexedAccess = (ast: AST): AST => { + switch (ast._tag) { + case "TupleType": { + let hasOptional = false + let out: Array = [] + for (const e of ast.elements) { + if (e.isOptional) { + hasOptional = true + } + out.push(e.type) + } + if (hasOptional) { + out.push(undefinedKeyword) + } + out = out.concat(getRestASTs(ast.rest)) + return Union.make(out) + } + case "Refinement": + return getNumberIndexedAccess(ast.from) + case "Union": + return Union.make(ast.types.map(getNumberIndexedAccess)) + case "Suspend": + return getNumberIndexedAccess(ast.f()) + } + throw new Error(errors_.getASTUnsupportedSchema(ast)) +} + +/** @internal */ +export const getPropertyKeyIndexedAccess = (ast: AST, name: PropertyKey): PropertySignature => { + switch (ast._tag) { + case "Declaration": { + const annotation = getSurrogateAnnotation(ast) + if (Option.isSome(annotation)) { + return getPropertyKeyIndexedAccess(annotation.value, name) + } + break + } + case "TypeLiteral": { + const ops = Arr.findFirst(ast.propertySignatures, (ps) => ps.name === name) + if (Option.isSome(ops)) { + return ops.value + } else { + if (Predicate.isString(name)) { + let out: PropertySignature | undefined = undefined + for (const is of ast.indexSignatures) { + const parameterBase = getParameterBase(is.parameter) + switch (parameterBase._tag) { + case "TemplateLiteral": { + const regex = getTemplateLiteralRegExp(parameterBase) + if (regex.test(name)) { + return new PropertySignature(name, is.type, false, true) + } + break + } + case "StringKeyword": { + if (out === undefined) { + out = new PropertySignature(name, is.type, false, true) + } + } + } + } + if (out) { + return out + } + } else if (Predicate.isSymbol(name)) { + for (const is of ast.indexSignatures) { + const parameterBase = getParameterBase(is.parameter) + if (isSymbolKeyword(parameterBase)) { + return new PropertySignature(name, is.type, false, true) + } + } + } + } + break + } + case "Union": + return new PropertySignature( + name, + Union.make(ast.types.map((ast) => getPropertyKeyIndexedAccess(ast, name).type)), + false, + true + ) + case "Suspend": + return getPropertyKeyIndexedAccess(ast.f(), name) + } + return new PropertySignature(name, neverKeyword, false, true) +} + +const getPropertyKeys = (ast: AST): Array => { + switch (ast._tag) { + case "Declaration": { + const annotation = getSurrogateAnnotation(ast) + if (Option.isSome(annotation)) { + return getPropertyKeys(annotation.value) + } + break + } + case "TypeLiteral": + return ast.propertySignatures.map((ps) => ps.name) + case "Suspend": + return getPropertyKeys(ast.f()) + case "Union": + return ast.types.slice(1).reduce( + (out: Array, ast) => Arr.intersection(out, getPropertyKeys(ast)), + getPropertyKeys(ast.types[0]) + ) + case "Transformation": + return getPropertyKeys(ast.to) + } + return [] +} + +/** @internal */ +export const record = (key: AST, value: AST): { + propertySignatures: Array + indexSignatures: Array +} => { + const propertySignatures: Array = [] + const indexSignatures: Array = [] + const go = (key: AST): void => { + switch (key._tag) { + case "NeverKeyword": + break + case "StringKeyword": + case "SymbolKeyword": + case "TemplateLiteral": + case "Refinement": + indexSignatures.push(new IndexSignature(key, value, true)) + break + case "Literal": + if (Predicate.isString(key.literal) || Predicate.isNumber(key.literal)) { + propertySignatures.push(new PropertySignature(key.literal, value, false, true)) + } else { + throw new Error(errors_.getASTUnsupportedLiteral(key.literal)) + } + break + case "Enums": { + for (const [_, name] of key.enums) { + propertySignatures.push(new PropertySignature(name, value, false, true)) + } + break + } + case "UniqueSymbol": + propertySignatures.push(new PropertySignature(key.symbol, value, false, true)) + break + case "Union": + key.types.forEach(go) + break + default: + throw new Error(errors_.getASTUnsupportedKeySchema(key)) + } + } + go(key) + return { propertySignatures, indexSignatures } +} + +/** + * Equivalent at runtime to the built-in TypeScript utility type `Pick`. + * + * @since 3.10.0 + */ +export const pick = (ast: AST, keys: ReadonlyArray): TypeLiteral | Transformation => { + if (isTransformation(ast)) { + switch (ast.transformation._tag) { + case "ComposeTransformation": + return new Transformation( + pick(ast.from, keys), + pick(ast.to, keys), + composeTransformation + ) + case "TypeLiteralTransformation": { + const ts: Array = [] + const fromKeys: Array = [] + for (const k of keys) { + const t = ast.transformation.propertySignatureTransformations.find((t) => t.to === k) + if (t) { + ts.push(t) + fromKeys.push(t.from) + } else { + fromKeys.push(k) + } + } + return Arr.isNonEmptyReadonlyArray(ts) ? + new Transformation( + pick(ast.from, fromKeys), + pick(ast.to, keys), + new TypeLiteralTransformation(ts) + ) : + pick(ast.from, fromKeys) + } + case "FinalTransformation": { + const annotation = getSurrogateAnnotation(ast) + if (Option.isSome(annotation)) { + return pick(annotation.value, keys) + } + throw new Error(errors_.getASTUnsupportedSchema(ast)) + } + } + } + return new TypeLiteral(keys.map((key) => getPropertyKeyIndexedAccess(ast, key)), []) +} + +/** + * Equivalent at runtime to the built-in TypeScript utility type `Omit`. + * + * @since 3.10.0 + */ +export const omit = (ast: AST, keys: ReadonlyArray): TypeLiteral | Transformation => + pick(ast, getPropertyKeys(ast).filter((name) => !keys.includes(name))) + +/** @internal */ +export const orUndefined = (ast: AST): AST => Union.make([ast, undefinedKeyword]) + +/** + * Equivalent at runtime to the built-in TypeScript utility type `Partial`. + * + * @since 3.10.0 + */ +export const partial = (ast: AST, options?: { readonly exact: true }): AST => { + const exact = options?.exact === true + switch (ast._tag) { + case "TupleType": + return new TupleType( + ast.elements.map((e) => new OptionalType(exact ? e.type : orUndefined(e.type), true)), + Arr.match(ast.rest, { + onEmpty: () => ast.rest, + onNonEmpty: (rest) => [new Type(Union.make([...getRestASTs(rest), undefinedKeyword]))] + }), + ast.isReadonly + ) + case "TypeLiteral": + return new TypeLiteral( + ast.propertySignatures.map((ps) => + new PropertySignature(ps.name, exact ? ps.type : orUndefined(ps.type), true, ps.isReadonly, ps.annotations) + ), + ast.indexSignatures.map((is) => new IndexSignature(is.parameter, orUndefined(is.type), is.isReadonly)) + ) + case "Union": + return Union.make(ast.types.map((member) => partial(member, options))) + case "Suspend": + return new Suspend(() => partial(ast.f(), options)) + case "Declaration": + throw new Error(errors_.getASTUnsupportedSchema(ast)) + case "Refinement": + throw new Error(errors_.getASTUnsupportedSchema(ast)) + case "Transformation": { + if ( + isTypeLiteralTransformation(ast.transformation) && + ast.transformation.propertySignatureTransformations.every(isRenamingPropertySignatureTransformation) + ) { + return new Transformation(partial(ast.from, options), partial(ast.to, options), ast.transformation) + } + throw new Error(errors_.getASTUnsupportedSchema(ast)) + } + } + return ast +} + +/** + * Equivalent at runtime to the built-in TypeScript utility type `Required`. + * + * @since 3.10.0 + */ +export const required = (ast: AST): AST => { + switch (ast._tag) { + case "TupleType": + return new TupleType( + ast.elements.map((e) => new OptionalType(e.type, false)), + ast.rest, + ast.isReadonly + ) + case "TypeLiteral": + return new TypeLiteral( + ast.propertySignatures.map((f) => new PropertySignature(f.name, f.type, false, f.isReadonly, f.annotations)), + ast.indexSignatures + ) + case "Union": + return Union.make(ast.types.map((member) => required(member))) + case "Suspend": + return new Suspend(() => required(ast.f())) + case "Declaration": + throw new Error(errors_.getASTUnsupportedSchema(ast)) + case "Refinement": + throw new Error(errors_.getASTUnsupportedSchema(ast)) + case "Transformation": { + if ( + isTypeLiteralTransformation(ast.transformation) && + ast.transformation.propertySignatureTransformations.every(isRenamingPropertySignatureTransformation) + ) { + return new Transformation(required(ast.from), required(ast.to), ast.transformation) + } + throw new Error(errors_.getASTUnsupportedSchema(ast)) + } + } + return ast +} + +/** + * Creates a new AST with shallow mutability applied to its properties. + * + * @param ast - The original AST to make properties mutable (shallowly). + * + * @since 3.10.0 + */ +export const mutable = (ast: AST): AST => { + switch (ast._tag) { + case "TupleType": + return ast.isReadonly === false ? ast : new TupleType(ast.elements, ast.rest, false, ast.annotations) + case "TypeLiteral": { + const propertySignatures = changeMap( + ast.propertySignatures, + (ps) => + ps.isReadonly === false ? ps : new PropertySignature(ps.name, ps.type, ps.isOptional, false, ps.annotations) + ) + const indexSignatures = changeMap( + ast.indexSignatures, + (is) => is.isReadonly === false ? is : new IndexSignature(is.parameter, is.type, false) + ) + return propertySignatures === ast.propertySignatures && indexSignatures === ast.indexSignatures ? + ast : + new TypeLiteral(propertySignatures, indexSignatures, ast.annotations) + } + case "Union": { + const types = changeMap(ast.types, mutable) + return types === ast.types ? ast : Union.make(types, ast.annotations) + } + case "Suspend": + return new Suspend(() => mutable(ast.f()), ast.annotations) + case "Refinement": { + const from = mutable(ast.from) + return from === ast.from ? ast : new Refinement(from, ast.filter, ast.annotations) + } + case "Transformation": { + const from = mutable(ast.from) + const to = mutable(ast.to) + return from === ast.from && to === ast.to ? + ast : + new Transformation(from, to, ast.transformation, ast.annotations) + } + } + return ast +} + +// ------------------------------------------------------------------------------------- +// compiler harness +// ------------------------------------------------------------------------------------- + +/** + * @since 3.10.0 + */ +export type Compiler = (ast: AST, path: ReadonlyArray) => A + +/** + * @since 3.10.0 + */ +export type Match = { + [K in AST["_tag"]]: (ast: Extract, compile: Compiler, path: ReadonlyArray) => A +} + +/** + * @since 3.10.0 + */ +export const getCompiler = (match: Match): Compiler => { + const compile = (ast: AST, path: ReadonlyArray): A => match[ast._tag](ast as any, compile, path) + return compile +} + +/** + * @since 3.10.0 + */ +export const typeAST = (ast: AST): AST => { + switch (ast._tag) { + case "Declaration": { + const typeParameters = changeMap(ast.typeParameters, typeAST) + return typeParameters === ast.typeParameters ? + ast : + new Declaration(typeParameters, ast.decodeUnknown, ast.encodeUnknown, ast.annotations) + } + case "TupleType": { + const elements = changeMap(ast.elements, (e) => { + const type = typeAST(e.type) + return type === e.type ? e : new OptionalType(type, e.isOptional) + }) + const restASTs = getRestASTs(ast.rest) + const rest = changeMap(restASTs, typeAST) + return elements === ast.elements && rest === restASTs ? + ast : + new TupleType(elements, rest.map((type) => new Type(type)), ast.isReadonly, ast.annotations) + } + case "TypeLiteral": { + const propertySignatures = changeMap(ast.propertySignatures, (p) => { + const type = typeAST(p.type) + return type === p.type ? p : new PropertySignature(p.name, type, p.isOptional, p.isReadonly) + }) + const indexSignatures = changeMap(ast.indexSignatures, (is) => { + const type = typeAST(is.type) + return type === is.type ? is : new IndexSignature(is.parameter, type, is.isReadonly) + }) + return propertySignatures === ast.propertySignatures && indexSignatures === ast.indexSignatures ? + ast : + new TypeLiteral(propertySignatures, indexSignatures, ast.annotations) + } + case "Union": { + const types = changeMap(ast.types, typeAST) + return types === ast.types ? ast : Union.make(types, ast.annotations) + } + case "Suspend": + return new Suspend(() => typeAST(ast.f()), ast.annotations) + case "Refinement": { + const from = typeAST(ast.from) + return from === ast.from ? + ast : + new Refinement(from, ast.filter, ast.annotations) + } + case "Transformation": + return typeAST(ast.to) + } + return ast +} + +/** @internal */ +export const whiteListAnnotations = + (annotationIds: ReadonlyArray) => (annotated: Annotated): Annotations | undefined => { + let out: { [_: symbol]: unknown } | undefined = undefined + for (const id of annotationIds) { + if (Object.prototype.hasOwnProperty.call(annotated.annotations, id)) { + if (out === undefined) { + out = {} + } + out[id] = annotated.annotations[id] + } + } + return out + } + +/** @internal */ +export const blackListAnnotations = + (annotationIds: ReadonlyArray) => (annotated: Annotated): Annotations | undefined => { + const out = { ...annotated.annotations } + for (const id of annotationIds) { + delete out[id] + } + return out + } + +/** @internal */ +export const getJSONIdentifier = (annotated: Annotated) => + Option.orElse(getJSONIdentifierAnnotation(annotated), () => getIdentifierAnnotation(annotated)) + +// To generate a JSON Schema from a recursive schema, an `identifier` annotation +// is required. So, when we calculate the encodedAST, we need to preserve the +// annotation in the form of an internal custom annotation that acts as a +// surrogate for the identifier, which the JSON Schema compiler can then read. +const createJSONIdentifierAnnotation = (annotated: Annotated): Annotations | undefined => + Option.match(getJSONIdentifier(annotated), { + onNone: () => undefined, + onSome: (identifier) => ({ [JSONIdentifierAnnotationId]: identifier }) + }) + +function changeMap( + as: Arr.NonEmptyReadonlyArray, + f: (a: A) => A +): Arr.NonEmptyReadonlyArray +function changeMap(as: ReadonlyArray, f: (a: A) => A): ReadonlyArray +function changeMap(as: ReadonlyArray, f: (a: A) => A): ReadonlyArray { + let changed = false + const out = Arr.allocate(as.length) as Array + for (let i = 0; i < as.length; i++) { + const a = as[i] + const fa = f(a) + if (fa !== a) { + changed = true + } + out[i] = fa + } + return changed ? out : as +} + +const encodedAST_ = (ast: AST, isBound: boolean): AST => { + switch (ast._tag) { + case "Declaration": { + const typeParameters = changeMap(ast.typeParameters, (ast) => encodedAST_(ast, isBound)) + return typeParameters === ast.typeParameters ? + ast : + new Declaration(typeParameters, ast.decodeUnknown, ast.encodeUnknown, ast.annotations) + } + case "TupleType": { + const elements = changeMap(ast.elements, (e) => { + const type = encodedAST_(e.type, isBound) + return type === e.type ? e : new OptionalType(type, e.isOptional) + }) + const restASTs = getRestASTs(ast.rest) + const rest = changeMap(restASTs, (ast) => encodedAST_(ast, isBound)) + return elements === ast.elements && rest === restASTs ? + ast : + new TupleType( + elements, + rest.map((ast) => new Type(ast)), + ast.isReadonly, + createJSONIdentifierAnnotation(ast) + ) + } + case "TypeLiteral": { + const propertySignatures = changeMap(ast.propertySignatures, (ps) => { + const type = encodedAST_(ps.type, isBound) + return type === ps.type + ? ps + : new PropertySignature(ps.name, type, ps.isOptional, ps.isReadonly) + }) + const indexSignatures = changeMap(ast.indexSignatures, (is) => { + const type = encodedAST_(is.type, isBound) + return type === is.type ? is : new IndexSignature(is.parameter, type, is.isReadonly) + }) + return propertySignatures === ast.propertySignatures && indexSignatures === ast.indexSignatures ? + ast : + new TypeLiteral(propertySignatures, indexSignatures, createJSONIdentifierAnnotation(ast)) + } + case "Union": { + const types = changeMap(ast.types, (ast) => encodedAST_(ast, isBound)) + return types === ast.types ? ast : Union.make(types, createJSONIdentifierAnnotation(ast)) + } + case "Suspend": + return new Suspend(() => encodedAST_(ast.f(), isBound), createJSONIdentifierAnnotation(ast)) + case "Refinement": { + const from = encodedAST_(ast.from, isBound) + if (isBound) { + if (from === ast.from) { + return ast + } + if (!isTransformation(ast.from) && hasStableFilter(ast)) { + return new Refinement(from, ast.filter) + } + } + return from + } + case "Transformation": + return encodedAST_(ast.from, isBound) + } + return ast +} + +/** + * @since 3.10.0 + */ +export const encodedAST = (ast: AST): AST => encodedAST_(ast, false) + +/** + * @since 3.10.0 + */ +export const encodedBoundAST = (ast: AST): AST => encodedAST_(ast, true) + +const toJSONAnnotations = (annotations: Annotations): object => { + const out: Record = {} + for (const k of Object.getOwnPropertySymbols(annotations)) { + out[String(k)] = annotations[k] + } + return out +} + +/** @internal */ +export const getParameterBase = ( + ast: Parameter +): StringKeyword | SymbolKeyword | TemplateLiteral => { + switch (ast._tag) { + case "StringKeyword": + case "SymbolKeyword": + case "TemplateLiteral": + return ast + case "Refinement": + return getParameterBase(ast.from) + } +} + +const equalsTemplateLiteralSpan = Arr.getEquivalence((self, that) => + self.type._tag === that.type._tag && self.literal === that.literal +) + +const equalsEnums = Arr.getEquivalence((self, that) => + that[0] === self[0] && that[1] === self[1] +) + +const equals = (self: AST, that: AST) => { + switch (self._tag) { + case "Literal": + return isLiteral(that) && that.literal === self.literal + case "UniqueSymbol": + return isUniqueSymbol(that) && that.symbol === self.symbol + case "UndefinedKeyword": + case "VoidKeyword": + case "NeverKeyword": + case "UnknownKeyword": + case "AnyKeyword": + case "StringKeyword": + case "NumberKeyword": + case "BooleanKeyword": + case "BigIntKeyword": + case "SymbolKeyword": + case "ObjectKeyword": + return that._tag === self._tag + case "TemplateLiteral": + return isTemplateLiteral(that) && that.head === self.head && equalsTemplateLiteralSpan(that.spans, self.spans) + case "Enums": + return isEnums(that) && equalsEnums(that.enums, self.enums) + case "Refinement": + case "TupleType": + case "TypeLiteral": + case "Union": + case "Suspend": + case "Transformation": + case "Declaration": + return self === that + } +} + +const intersection = Arr.intersectionWith(equals) + +const _keyof = (ast: AST): Array => { + switch (ast._tag) { + case "Declaration": { + const annotation = getSurrogateAnnotation(ast) + if (Option.isSome(annotation)) { + return _keyof(annotation.value) + } + break + } + case "TypeLiteral": + return ast.propertySignatures.map((p): AST => + Predicate.isSymbol(p.name) ? new UniqueSymbol(p.name) : new Literal(p.name) + ).concat(ast.indexSignatures.map((is) => getParameterBase(is.parameter))) + case "Suspend": + return _keyof(ast.f()) + case "Union": + return ast.types.slice(1).reduce( + (out: Array, ast) => intersection(out, _keyof(ast)), + _keyof(ast.types[0]) + ) + case "Transformation": + return _keyof(ast.to) + } + throw new Error(errors_.getASTUnsupportedSchema(ast)) +} + +/** @internal */ +export const compose = (ab: AST, cd: AST): AST => new Transformation(ab, cd, composeTransformation) + +/** @internal */ +export const rename = (ast: AST, mapping: { readonly [K in PropertyKey]?: PropertyKey }): AST => { + switch (ast._tag) { + case "TypeLiteral": { + const propertySignatureTransformations: Array = [] + for (const key of util_.ownKeys(mapping)) { + const name = mapping[key] + if (name !== undefined) { + propertySignatureTransformations.push( + new PropertySignatureTransformation( + key, + name, + identity, + identity + ) + ) + } + } + if (propertySignatureTransformations.length === 0) { + return ast + } + return new Transformation( + ast, + new TypeLiteral( + ast.propertySignatures.map((ps) => { + const name = mapping[ps.name] + return new PropertySignature( + name === undefined ? ps.name : name, + typeAST(ps.type), + ps.isOptional, + ps.isReadonly, + ps.annotations + ) + }), + ast.indexSignatures + ), + new TypeLiteralTransformation(propertySignatureTransformations) + ) + } + case "Union": + return Union.make(ast.types.map((ast) => rename(ast, mapping))) + case "Suspend": + return new Suspend(() => rename(ast.f(), mapping)) + case "Transformation": + return compose(ast, rename(typeAST(ast), mapping)) + } + throw new Error(errors_.getASTUnsupportedRenameSchema(ast)) +} + +const formatKeyword = (ast: AST): string => Option.getOrElse(getExpected(ast), () => ast._tag) + +const getExpected = (ast: Annotated): Option.Option => { + return getIdentifierAnnotation(ast).pipe( + Option.orElse(() => getTitleAnnotation(ast)), + Option.orElse(() => getDescriptionAnnotation(ast)) + ) +} diff --git a/packages/effect/src/SchemaArrayFormatter.ts b/packages/effect/src/SchemaArrayFormatter.ts new file mode 100644 index 0000000000..0fad45e13e --- /dev/null +++ b/packages/effect/src/SchemaArrayFormatter.ts @@ -0,0 +1,82 @@ +/** + * @since 3.10.0 + */ + +import * as array_ from "effect/Array" +import * as Effect from "effect/Effect" +import * as util_ from "./internal/schema/util.js" +import type * as ParseResult from "./ParseResult.js" +import * as TreeFormatter from "./SchemaTreeFormatter.js" + +/** + * @category model + * @since 3.10.0 + */ +export interface Issue { + readonly _tag: ParseResult.ParseIssue["_tag"] + readonly path: ReadonlyArray + readonly message: string +} + +/** + * @category formatting + * @since 3.10.0 + */ +export const formatIssue = (issue: ParseResult.ParseIssue): Effect.Effect> => go(issue) + +/** + * @category formatting + * @since 3.10.0 + */ +export const formatIssueSync = (issue: ParseResult.ParseIssue): Array => Effect.runSync(formatIssue(issue)) + +/** + * @category formatting + * @since 3.10.0 + */ +export const formatError = (error: ParseResult.ParseError): Effect.Effect> => formatIssue(error.issue) + +/** + * @category formatting + * @since 3.10.0 + */ +export const formatErrorSync = (error: ParseResult.ParseError): Array => formatIssueSync(error.issue) + +const succeed = (issue: Issue) => Effect.succeed([issue]) + +const getArray = ( + issue: ParseResult.ParseIssue, + path: ReadonlyArray, + onFailure: () => Effect.Effect> +) => + Effect.matchEffect(TreeFormatter.getMessage(issue), { + onFailure, + onSuccess: (message) => succeed({ _tag: issue._tag, path, message }) + }) + +const go = ( + e: ParseResult.ParseIssue | ParseResult.Pointer, + path: ReadonlyArray = [] +): Effect.Effect> => { + const _tag = e._tag + switch (_tag) { + case "Type": + return Effect.map(TreeFormatter.formatTypeMessage(e), (message) => [{ _tag, path, message }]) + case "Forbidden": + return succeed({ _tag, path, message: TreeFormatter.formatForbiddenMessage(e) }) + case "Unexpected": + return succeed({ _tag, path, message: TreeFormatter.formatUnexpectedMessage(e) }) + case "Missing": + return Effect.map(TreeFormatter.formatMissingMessage(e), (message) => [{ _tag, path, message }]) + case "Pointer": + return go(e.issue, path.concat(e.path)) + case "Composite": + return getArray(e, path, () => + util_.isNonEmpty(e.issues) + ? Effect.map(Effect.forEach(e.issues, (issue) => go(issue, path)), array_.flatten) + : go(e.issues, path)) + case "Refinement": + case "Transformation": + return getArray(e, path, () => go(e.issue, path)) + } +} diff --git a/packages/effect/src/SchemaEquivalence.ts b/packages/effect/src/SchemaEquivalence.ts new file mode 100644 index 0000000000..9da0355342 --- /dev/null +++ b/packages/effect/src/SchemaEquivalence.ts @@ -0,0 +1,220 @@ +/** + * @since 3.10.0 + */ + +import * as Arr from "effect/Array" +import * as Equal from "effect/Equal" +import * as Equivalence from "effect/Equivalence" +import * as Option from "effect/Option" +import * as Predicate from "effect/Predicate" +import * as errors_ from "./internal/schema/errors.js" +import * as util_ from "./internal/schema/util.js" +import * as ParseResult from "./ParseResult.js" +import type * as Schema from "./Schema.js" +import * as AST from "./SchemaAST.js" + +/** + * @category hooks + * @since 3.10.0 + */ +export const EquivalenceHookId: unique symbol = Symbol.for("effect/Schema/EquivalenceHookId") + +/** + * @category hooks + * @since 3.10.0 + */ +export type EquivalenceHookId = typeof EquivalenceHookId + +/** + * @category annotations + * @since 3.10.0 + */ +export const equivalence = + (handler: (...args: ReadonlyArray>) => Equivalence.Equivalence) => + (self: Schema.Schema): Schema.Schema => self.annotations({ [EquivalenceHookId]: handler }) + +/** + * @category Equivalence + * @since 3.10.0 + */ +export const make = (schema: Schema.Schema): Equivalence.Equivalence => go(schema.ast, []) + +const getHook = AST.getAnnotation< + (...args: ReadonlyArray>) => Equivalence.Equivalence +>( + EquivalenceHookId +) + +const go = (ast: AST.AST, path: ReadonlyArray): Equivalence.Equivalence => { + const hook = getHook(ast) + if (Option.isSome(hook)) { + switch (ast._tag) { + case "Declaration": + return hook.value(...ast.typeParameters.map((tp) => go(tp, path))) + case "Refinement": + return hook.value(go(ast.from, path)) + default: + return hook.value() + } + } + switch (ast._tag) { + case "NeverKeyword": + throw new Error(errors_.getEquivalenceUnsupportedErrorMessage(ast, path)) + case "Transformation": + return go(ast.to, path) + case "Declaration": + case "Literal": + case "StringKeyword": + case "TemplateLiteral": + case "UniqueSymbol": + case "SymbolKeyword": + case "UnknownKeyword": + case "AnyKeyword": + case "NumberKeyword": + case "BooleanKeyword": + case "BigIntKeyword": + case "UndefinedKeyword": + case "VoidKeyword": + case "Enums": + case "ObjectKeyword": + return Equal.equals + case "Refinement": + return go(ast.from, path) + case "Suspend": { + const get = util_.memoizeThunk(() => go(ast.f(), path)) + return (a, b) => get()(a, b) + } + case "TupleType": { + const elements = ast.elements.map((element, i) => go(element.type, path.concat(i))) + const rest = ast.rest.map((annotatedAST) => go(annotatedAST.type, path)) + return Equivalence.make((a, b) => { + const len = a.length + if (len !== b.length) { + return false + } + // --------------------------------------------- + // handle elements + // --------------------------------------------- + let i = 0 + for (; i < Math.min(len, ast.elements.length); i++) { + if (!elements[i](a[i], b[i])) { + return false + } + } + // --------------------------------------------- + // handle rest element + // --------------------------------------------- + if (Arr.isNonEmptyReadonlyArray(rest)) { + const [head, ...tail] = rest + for (; i < len - tail.length; i++) { + if (!head(a[i], b[i])) { + return false + } + } + // --------------------------------------------- + // handle post rest elements + // --------------------------------------------- + for (let j = 0; j < tail.length; j++) { + i += j + if (!tail[j](a[i], b[i])) { + return false + } + } + } + return true + }) + } + case "TypeLiteral": { + if (ast.propertySignatures.length === 0 && ast.indexSignatures.length === 0) { + return Equal.equals + } + const propertySignatures = ast.propertySignatures.map((ps) => go(ps.type, path.concat(ps.name))) + const indexSignatures = ast.indexSignatures.map((is) => go(is.type, path)) + return Equivalence.make((a, b) => { + const aStringKeys = Object.keys(a) + const aSymbolKeys = Object.getOwnPropertySymbols(a) + // --------------------------------------------- + // handle property signatures + // --------------------------------------------- + for (let i = 0; i < propertySignatures.length; i++) { + const ps = ast.propertySignatures[i] + const name = ps.name + const aHas = Object.prototype.hasOwnProperty.call(a, name) + const bHas = Object.prototype.hasOwnProperty.call(b, name) + if (ps.isOptional) { + if (aHas !== bHas) { + return false + } + } + if (aHas && bHas && !propertySignatures[i](a[name], b[name])) { + return false + } + } + // --------------------------------------------- + // handle index signatures + // --------------------------------------------- + let bSymbolKeys: Array | undefined + let bStringKeys: Array | undefined + for (let i = 0; i < indexSignatures.length; i++) { + const is = ast.indexSignatures[i] + const base = AST.getParameterBase(is.parameter) + const isSymbol = AST.isSymbolKeyword(base) + if (isSymbol) { + bSymbolKeys = bSymbolKeys || Object.getOwnPropertySymbols(b) + if (aSymbolKeys.length !== bSymbolKeys.length) { + return false + } + } else { + bStringKeys = bStringKeys || Object.keys(b) + if (aStringKeys.length !== bStringKeys.length) { + return false + } + } + const aKeys = isSymbol ? aSymbolKeys : aStringKeys + for (let j = 0; j < aKeys.length; j++) { + const key = aKeys[j] + if ( + !Object.prototype.hasOwnProperty.call(b, key) || !indexSignatures[i](a[key], b[key]) + ) { + return false + } + } + } + return true + }) + } + case "Union": { + const searchTree = ParseResult.getSearchTree(ast.types, true) + const ownKeys = util_.ownKeys(searchTree.keys) + const len = ownKeys.length + return Equivalence.make((a, b) => { + let candidates: Array = [] + if (len > 0 && Predicate.isRecord(a)) { + for (let i = 0; i < len; i++) { + const name = ownKeys[i] + const buckets = searchTree.keys[name].buckets + if (Object.prototype.hasOwnProperty.call(a, name)) { + const literal = String(a[name]) + if (Object.prototype.hasOwnProperty.call(buckets, literal)) { + candidates = candidates.concat(buckets[literal]) + } + } + } + } + if (searchTree.otherwise.length > 0) { + candidates = candidates.concat(searchTree.otherwise) + } + const tuples = candidates.map((ast) => [go(ast, path), ParseResult.is({ ast } as any)] as const) + for (let i = 0; i < tuples.length; i++) { + const [equivalence, is] = tuples[i] + if (is(a) && is(b)) { + if (equivalence(a, b)) { + return true + } + } + } + return false + }) + } + } +} diff --git a/packages/effect/src/SchemaTreeFormatter.ts b/packages/effect/src/SchemaTreeFormatter.ts new file mode 100644 index 0000000000..faa720ee89 --- /dev/null +++ b/packages/effect/src/SchemaTreeFormatter.ts @@ -0,0 +1,215 @@ +/** + * @since 3.10.0 + */ + +import type * as Cause from "effect/Cause" +import * as Effect from "effect/Effect" +import * as Option from "effect/Option" +import * as Predicate from "effect/Predicate" +import * as util_ from "./internal/schema/util.js" +import type * as ParseResult from "./ParseResult.js" +import * as AST from "./SchemaAST.js" + +interface Forest extends ReadonlyArray> {} + +interface Tree { + readonly value: A + readonly forest: Forest +} + +const make = (value: A, forest: Forest = []): Tree => ({ + value, + forest +}) + +/** + * @category formatting + * @since 3.10.0 + */ +export const formatIssue = (issue: ParseResult.ParseIssue): Effect.Effect => + Effect.map(go(issue), (tree) => drawTree(tree)) + +/** + * @category formatting + * @since 3.10.0 + */ +export const formatIssueSync = (issue: ParseResult.ParseIssue): string => Effect.runSync(formatIssue(issue)) + +/** + * @category formatting + * @since 3.10.0 + */ +export const formatError = (error: ParseResult.ParseError): Effect.Effect => formatIssue(error.issue) + +/** + * @category formatting + * @since 3.10.0 + */ +export const formatErrorSync = (error: ParseResult.ParseError): string => formatIssueSync(error.issue) + +const drawTree = (tree: Tree): string => tree.value + draw("\n", tree.forest) + +const draw = (indentation: string, forest: Forest): string => { + let r = "" + const len = forest.length + let tree: Tree + for (let i = 0; i < len; i++) { + tree = forest[i] + const isLast = i === len - 1 + r += indentation + (isLast ? "└" : "├") + "─ " + tree.value + r += draw(indentation + (len > 1 && !isLast ? "│ " : " "), tree.forest) + } + return r +} + +const formatTransformationKind = (kind: ParseResult.Transformation["kind"]): string => { + switch (kind) { + case "Encoded": + return "Encoded side transformation failure" + case "Transformation": + return "Transformation process failure" + case "Type": + return "Type side transformation failure" + } +} + +const formatRefinementKind = (kind: ParseResult.Refinement["kind"]): string => { + switch (kind) { + case "From": + return "From side refinement failure" + case "Predicate": + return "Predicate refinement failure" + } +} + +const getAnnotated = (issue: ParseResult.ParseIssue): Option.Option => + "ast" in issue ? Option.some(issue.ast) : Option.none() + +interface CurrentMessage { + readonly message: string + readonly override: boolean +} + +const getCurrentMessage = ( + issue: ParseResult.ParseIssue +): Effect.Effect => + getAnnotated(issue).pipe( + Option.flatMap(AST.getMessageAnnotation), + Effect.flatMap((annotation) => { + const out = annotation(issue) + return Predicate.isString(out) + ? Effect.succeed({ message: out, override: false }) + : Effect.isEffect(out) + ? Effect.map(out, (message) => ({ message, override: false })) + : Predicate.isString(out.message) + ? Effect.succeed({ message: out.message, override: out.override }) + : Effect.map(out.message, (message) => ({ message, override: out.override })) + }) + ) + +const createParseIssueGuard = + (tag: T) => + (issue: ParseResult.ParseIssue): issue is Extract => issue._tag === tag + +const isComposite = createParseIssueGuard("Composite") +const isRefinement = createParseIssueGuard("Refinement") +const isTransformation = createParseIssueGuard("Transformation") + +/** @internal */ +export const getMessage: ( + issue: ParseResult.ParseIssue +) => Effect.Effect = (issue: ParseResult.ParseIssue) => + getCurrentMessage(issue).pipe( + Effect.flatMap((currentMessage) => { + const useInnerMessage = !currentMessage.override && ( + isComposite(issue) || + (isRefinement(issue) && issue.kind === "From") || + (isTransformation(issue) && issue.kind !== "Transformation") + ) + return useInnerMessage + ? isTransformation(issue) || isRefinement(issue) ? getMessage(issue.issue) : Option.none() + : Effect.succeed(currentMessage.message) + }) + ) + +const getParseIssueTitleAnnotation = (issue: ParseResult.ParseIssue): Option.Option => + getAnnotated(issue).pipe( + Option.flatMap(AST.getParseIssueTitleAnnotation), + Option.filterMap( + (annotation) => Option.fromNullable(annotation(issue)) + ) + ) + +/** @internal */ +export const formatTypeMessage = (e: ParseResult.Type): Effect.Effect => + getMessage(e).pipe( + Effect.orElse(() => getParseIssueTitleAnnotation(e)), + Effect.catchAll(() => + Effect.succeed(e.message ?? `Expected ${String(e.ast)}, actual ${util_.formatUnknown(e.actual)}`) + ) + ) + +const getParseIssueTitle = ( + issue: ParseResult.Forbidden | ParseResult.Transformation | ParseResult.Refinement | ParseResult.Composite +): string => Option.getOrElse(getParseIssueTitleAnnotation(issue), () => String(issue.ast)) + +/** @internal */ +export const formatForbiddenMessage = (e: ParseResult.Forbidden): string => e.message ?? "is forbidden" + +/** @internal */ +export const formatUnexpectedMessage = (e: ParseResult.Unexpected): string => e.message ?? "is unexpected" + +/** @internal */ +export const formatMissingMessage = (e: ParseResult.Missing): Effect.Effect => + AST.getMissingMessageAnnotation(e.ast).pipe( + Effect.flatMap((annotation) => { + const out = annotation() + return Predicate.isString(out) ? Effect.succeed(out) : out + }), + Effect.catchAll(() => Effect.succeed(e.message ?? "is missing")) + ) + +const getTree = (issue: ParseResult.ParseIssue, onFailure: () => Effect.Effect>) => + Effect.matchEffect(getMessage(issue), { + onFailure, + onSuccess: (message) => Effect.succeed(make(message)) + }) + +const go = ( + e: ParseResult.ParseIssue | ParseResult.Pointer +): Effect.Effect> => { + switch (e._tag) { + case "Type": + return Effect.map(formatTypeMessage(e), make) + case "Forbidden": + return Effect.succeed(make(getParseIssueTitle(e), [make(formatForbiddenMessage(e))])) + case "Unexpected": + return Effect.succeed(make(formatUnexpectedMessage(e))) + case "Missing": + return Effect.map(formatMissingMessage(e), make) + case "Transformation": + return getTree(e, () => + Effect.map( + go(e.issue), + (tree) => make(getParseIssueTitle(e), [make(formatTransformationKind(e.kind), [tree])]) + )) + case "Refinement": + return getTree( + e, + () => + Effect.map(go(e.issue), (tree) => make(getParseIssueTitle(e), [make(formatRefinementKind(e.kind), [tree])])) + ) + case "Pointer": + return Effect.map(go(e.issue), (tree) => make(util_.formatPath(e.path), [tree])) + case "Composite": { + const parseIssueTitle = getParseIssueTitle(e) + return getTree( + e, + () => + util_.isNonEmpty(e.issues) + ? Effect.map(Effect.forEach(e.issues, go), (forest) => make(parseIssueTitle, forest)) + : Effect.map(go(e.issues), (tree) => make(parseIssueTitle, [tree])) + ) + } + } +} diff --git a/packages/effect/src/Serializable.ts b/packages/effect/src/Serializable.ts new file mode 100644 index 0000000000..eb025e5e0d --- /dev/null +++ b/packages/effect/src/Serializable.ts @@ -0,0 +1,385 @@ +/** + * @since 3.10.0 + */ +import type * as Effect from "effect/Effect" +import type * as Exit from "effect/Exit" +import { dual } from "effect/Function" +import { globalValue } from "effect/GlobalValue" +import * as serializable_ from "./internal/schema/serializable.js" +import type * as ParseResult from "./ParseResult.js" +import * as Schema from "./Schema.js" + +// --------------------------------------------- +// Serializable +// --------------------------------------------- + +/** + * @since 3.10.0 + * @category symbol + */ +export const symbol: unique symbol = serializable_.symbol as any + +/** + * The `Serializable` trait allows objects to define their own schema for + * serialization. + * + * @since 3.10.0 + * @category model + */ +export interface Serializable { + readonly [symbol]: Schema.Schema +} + +/** + * @since 3.10.0 + * @category model + */ +export declare namespace Serializable { + /** + * @since 3.10.0 + */ + export type Type = T extends Serializable ? A : never + /** + * @since 3.10.0 + */ + export type Encoded = T extends Serializable ? I : never + /** + * @since 3.10.0 + */ + export type Context = T extends Serializable ? R : never + /** + * @since 3.10.0 + */ + export type Any = Serializable + /** + * @since 3.10.0 + */ + export type All = + | Any + | Serializable + | Serializable + | Serializable +} + +/** + * @since 3.10.0 + */ +export const asSerializable = ( + serializable: S +): Serializable, Serializable.Encoded, Serializable.Context> => serializable as any + +/** + * @since 3.10.0 + * @category accessor + */ +export const selfSchema = (self: Serializable): Schema.Schema => self[symbol] + +/** + * @since 3.10.0 + * @category encoding + */ +export const serialize = (self: Serializable): Effect.Effect => + Schema.encodeUnknown(self[symbol])(self) + +/** + * @since 3.10.0 + * @category decoding + */ +export const deserialize: { + (value: unknown): (self: Serializable) => Effect.Effect + (self: Serializable, value: unknown): Effect.Effect +} = dual( + 2, + (self: Serializable, value: unknown): Effect.Effect => + Schema.decodeUnknown(self[symbol])(value) +) + +// --------------------------------------------- +// WithResult +// --------------------------------------------- + +/** + * @since 3.10.0 + * @category symbol + */ +export const symbolResult: unique symbol = serializable_.symbolResult as any + +/** + * The `WithResult` trait is designed to encapsulate the outcome of an + * operation, distinguishing between success and failure cases. Each case is + * associated with a schema that defines the structure and types of the success + * or failure data. + * + * @since 3.10.0 + * @category model + */ +export interface WithResult { + readonly [symbolResult]: { + readonly success: Schema.Schema + readonly failure: Schema.Schema + } +} + +/** + * @since 3.10.0 + * @category model + */ +export declare namespace WithResult { + /** + * @since 3.10.0 + */ + export type Success = T extends WithResult ? _A : never + /** + * @since 3.10.0 + */ + export type SuccessEncoded = T extends WithResult ? _I : never + /** + * @since 3.10.0 + */ + export type Failure = T extends WithResult ? _E : never + /** + * @since 3.10.0 + */ + export type FailureEncoded = T extends WithResult ? _EI : never + + /** + * @since 3.10.0 + */ + export type Context = T extends WithResult ? R : never + /** + * @since 3.10.0 + */ + export type Any = WithResult + /** + * @since 3.10.0 + */ + export type All = + | Any + | WithResult +} + +/** + * @since 3.10.0 + */ +export const asWithResult = ( + withExit: WR +): WithResult< + WithResult.Success, + WithResult.SuccessEncoded, + WithResult.Failure, + WithResult.FailureEncoded, + WithResult.Context +> => withExit as any + +/** + * @since 3.10.0 + * @category accessor + */ +export const failureSchema = (self: WithResult): Schema.Schema => + self[symbolResult].failure + +/** + * @since 3.10.0 + * @category accessor + */ +export const successSchema = (self: WithResult): Schema.Schema => + self[symbolResult].success + +const exitSchemaCache = globalValue( + "effect/Schema/Serializable/exitSchemaCache", + () => new WeakMap>() +) + +/** + * @since 3.10.0 + * @category accessor + */ +export const exitSchema = (self: WithResult): Schema.Schema< + Exit.Exit, + Schema.ExitEncoded, + R +> => { + const proto = Object.getPrototypeOf(self) + if (!(symbolResult in proto)) { + return Schema.Exit({ + failure: failureSchema(self), + success: successSchema(self), + defect: Schema.Defect + }) + } + let schema = exitSchemaCache.get(proto) + if (schema === undefined) { + schema = Schema.Exit({ + failure: failureSchema(self), + success: successSchema(self), + defect: Schema.Defect + }) + exitSchemaCache.set(proto, schema) + } + return schema +} + +/** + * @since 3.10.0 + * @category encoding + */ +export const serializeFailure: { + (value: FA): ( + self: WithResult + ) => Effect.Effect + (self: WithResult, value: FA): Effect.Effect +} = dual( + 2, + (self: WithResult, value: FA): Effect.Effect => + Schema.encode(self[symbolResult].failure)(value) +) + +/** + * @since 3.10.0 + * @category decoding + */ +export const deserializeFailure: { + ( + value: unknown + ): (self: WithResult) => Effect.Effect + (self: WithResult, value: unknown): Effect.Effect +} = dual( + 2, + ( + self: WithResult, + value: unknown + ): Effect.Effect => Schema.decodeUnknown(self[symbolResult].failure)(value) +) + +/** + * @since 3.10.0 + * @category encoding + */ +export const serializeSuccess: { + (value: SA): ( + self: WithResult + ) => Effect.Effect + (self: WithResult, value: SA): Effect.Effect +} = dual( + 2, + (self: WithResult, value: SA): Effect.Effect => + Schema.encode(self[symbolResult].success)(value) +) + +/** + * @since 3.10.0 + * @category decoding + */ +export const deserializeSuccess: { + (value: unknown): ( + self: WithResult + ) => Effect.Effect + (self: WithResult, value: unknown): Effect.Effect +} = dual( + 2, + ( + self: WithResult, + value: unknown + ): Effect.Effect => Schema.decodeUnknown(self[symbolResult].success)(value) +) + +/** + * @since 3.10.0 + * @category encoding + */ +export const serializeExit: { + (value: Exit.Exit): ( + self: WithResult + ) => Effect.Effect, ParseResult.ParseError, R> + ( + self: WithResult, + value: Exit.Exit + ): Effect.Effect, ParseResult.ParseError, R> +} = dual(2, ( + self: WithResult, + value: Exit.Exit +): Effect.Effect, ParseResult.ParseError, R> => + Schema.encode(exitSchema(self))(value)) + +/** + * @since 3.10.0 + * @category decoding + */ +export const deserializeExit: { + (value: unknown): ( + self: WithResult + ) => Effect.Effect, ParseResult.ParseError, R> + ( + self: WithResult, + value: unknown + ): Effect.Effect, ParseResult.ParseError, R> +} = dual(2, ( + self: WithResult, + value: unknown +): Effect.Effect, ParseResult.ParseError, R> => Schema.decodeUnknown(exitSchema(self))(value)) + +// --------------------------------------------- +// SerializableWithResult +// --------------------------------------------- + +/** + * The `SerializableWithResult` trait is specifically designed to model remote + * procedures that require serialization of their input and output, managing + * both successful and failed outcomes. + * + * This trait combines functionality from both the `Serializable` and `WithResult` + * traits to handle data serialization and the bifurcation of operation results + * into success or failure categories. + * + * @since 3.10.0 + * @category model + */ +export interface SerializableWithResult< + A, + I, + R, + Success, + SuccessEncoded, + Failure, + FailureEncoded, + ResultR +> extends Serializable, WithResult {} + +/** + * @since 3.10.0 + * @category model + */ +export declare namespace SerializableWithResult { + /** + * @since 3.10.0 + */ + export type Context

    = P extends + SerializableWithResult ? SR | RR + : never + /** + * @since 3.10.0 + */ + export type Any = SerializableWithResult + /** + * @since 3.10.0 + */ + export type All = + | Any + | SerializableWithResult +} + +/** + * @since 3.10.0 + */ +export const asSerializableWithResult = ( + procedure: SWR +): SerializableWithResult< + Serializable.Type, + Serializable.Encoded, + Serializable.Context, + WithResult.Success, + WithResult.SuccessEncoded, + WithResult.Failure, + WithResult.FailureEncoded, + WithResult.Context +> => procedure as any diff --git a/packages/effect/src/index.ts b/packages/effect/src/index.ts index a57bd513ed..8d29c8fa03 100644 --- a/packages/effect/src/index.ts +++ b/packages/effect/src/index.ts @@ -29,6 +29,11 @@ export { unsafeCoerce } from "./Function.js" +/** + * @since 3.10.0 + */ +export * as Arbitrary from "./Arbitrary.js" + /** * This module provides utility functions for working with arrays in TypeScript. * @@ -262,6 +267,11 @@ export * as ExecutionStrategy from "./ExecutionStrategy.js" */ export * as Exit from "./Exit.js" +/** + * @since 3.10.0 + */ +export * as FastCheck from "./FastCheck.js" + /** * @since 2.0.0 */ @@ -354,6 +364,11 @@ export * as Inspectable from "./Inspectable.js" */ export * as Iterable from "./Iterable.js" +/** + * @since 3.10.0 + */ +export * as JSONSchema from "./JSONSchema.js" + /** * @since 2.0.0 */ @@ -574,6 +589,11 @@ export * as Order from "./Order.js" */ export * as Ordering from "./Ordering.js" +/** + * @since 3.10.0 + */ +export * as ParseResult from "./ParseResult.js" + /** * @since 2.0.0 */ @@ -589,6 +609,11 @@ export * as Pool from "./Pool.js" */ export * as Predicate from "./Predicate.js" +/** + * @since 3.10.0 + */ +export * as Pretty from "./Pretty.js" + /** * @since 2.0.0 */ @@ -735,6 +760,31 @@ export * as ScheduleIntervals from "./ScheduleIntervals.js" */ export * as Scheduler from "./Scheduler.js" +/** + * @since 3.10.0 + */ +export * as Schema from "./Schema.js" + +/** + * @since 3.10.0 + */ +export * as SchemaAST from "./SchemaAST.js" + +/** + * @since 3.10.0 + */ +export * as SchemaArrayFormatter from "./SchemaArrayFormatter.js" + +/** + * @since 3.10.0 + */ +export * as SchemaEquivalence from "./SchemaEquivalence.js" + +/** + * @since 3.10.0 + */ +export * as SchemaTreeFormatter from "./SchemaTreeFormatter.js" + /** * @since 2.0.0 */ @@ -756,6 +806,11 @@ export * as ScopedRef from "./ScopedRef.js" */ export * as Secret from "./Secret.js" +/** + * @since 3.10.0 + */ +export * as Serializable from "./Serializable.js" + /** * @since 2.0.0 */ diff --git a/packages/schema/src/internal/errors.ts b/packages/effect/src/internal/schema/errors.ts similarity index 99% rename from packages/schema/src/internal/errors.ts rename to packages/effect/src/internal/schema/errors.ts index 4a8de668fa..628df011c1 100644 --- a/packages/schema/src/internal/errors.ts +++ b/packages/effect/src/internal/schema/errors.ts @@ -1,5 +1,5 @@ import * as array_ from "effect/Array" -import type * as AST from "../AST.js" +import type * as AST from "../../SchemaAST.js" import * as util_ from "./util.js" const getErrorMessage = ( diff --git a/packages/schema/src/internal/filters.ts b/packages/effect/src/internal/schema/filters.ts similarity index 72% rename from packages/schema/src/internal/filters.ts rename to packages/effect/src/internal/schema/filters.ts index c3b1b0ff92..470997f9b7 100644 --- a/packages/schema/src/internal/filters.ts +++ b/packages/effect/src/internal/schema/filters.ts @@ -1,89 +1,89 @@ -import type * as Schema from "../Schema.js" +import type * as Schema from "../../Schema.js" /** @internal */ export const GreaterThanTypeId: Schema.GreaterThanTypeId = Symbol.for( - "@effect/schema/TypeId/GreaterThan" + "effect/Schema/TypeId/GreaterThan" ) as Schema.GreaterThanTypeId /** @internal */ export const GreaterThanOrEqualToTypeId: Schema.GreaterThanOrEqualToTypeId = Symbol.for( - "@effect/schema/TypeId/GreaterThanOrEqualTo" + "effect/Schema/TypeId/GreaterThanOrEqualTo" ) as Schema.GreaterThanOrEqualToTypeId /** @internal */ export const LessThanTypeId: Schema.LessThanTypeId = Symbol.for( - "@effect/schema/TypeId/LessThan" + "effect/Schema/TypeId/LessThan" ) as Schema.LessThanTypeId /** @internal */ export const LessThanOrEqualToTypeId: Schema.LessThanOrEqualToTypeId = Symbol.for( - "@effect/schema/TypeId/LessThanOrEqualTo" + "effect/Schema/TypeId/LessThanOrEqualTo" ) as Schema.LessThanOrEqualToTypeId /** @internal */ export const IntTypeId: Schema.IntTypeId = Symbol.for( - "@effect/schema/TypeId/Int" + "effect/Schema/TypeId/Int" ) as Schema.IntTypeId /** @internal */ export const BetweenTypeId: Schema.BetweenTypeId = Symbol.for( - "@effect/schema/TypeId/Between" + "effect/Schema/TypeId/Between" ) as Schema.BetweenTypeId /** @internal */ export const GreaterThanBigintTypeId: Schema.GreaterThanBigIntTypeId = Symbol.for( - "@effect/schema/TypeId/GreaterThanBigint" + "effect/Schema/TypeId/GreaterThanBigint" ) as Schema.GreaterThanBigIntTypeId /** @internal */ export const GreaterThanOrEqualToBigIntTypeId: Schema.GreaterThanOrEqualToBigIntTypeId = Symbol.for( - "@effect/schema/TypeId/GreaterThanOrEqualToBigint" + "effect/Schema/TypeId/GreaterThanOrEqualToBigint" ) as Schema.GreaterThanOrEqualToBigIntTypeId /** @internal */ export const LessThanBigIntTypeId: Schema.LessThanBigIntTypeId = Symbol.for( - "@effect/schema/TypeId/LessThanBigint" + "effect/Schema/TypeId/LessThanBigint" ) as Schema.LessThanBigIntTypeId /** @internal */ export const LessThanOrEqualToBigIntTypeId: Schema.LessThanOrEqualToBigIntTypeId = Symbol.for( - "@effect/schema/TypeId/LessThanOrEqualToBigint" + "effect/Schema/TypeId/LessThanOrEqualToBigint" ) as Schema.LessThanOrEqualToBigIntTypeId /** @internal */ export const BetweenBigintTypeId: Schema.BetweenBigIntTypeId = Symbol.for( - "@effect/schema/TypeId/BetweenBigint" + "effect/Schema/TypeId/BetweenBigint" ) as Schema.BetweenBigIntTypeId /** @internal */ export const MinLengthTypeId: Schema.MinLengthTypeId = Symbol.for( - "@effect/schema/TypeId/MinLength" + "effect/Schema/TypeId/MinLength" ) as Schema.MinLengthTypeId /** @internal */ export const MaxLengthTypeId: Schema.MaxLengthTypeId = Symbol.for( - "@effect/schema/TypeId/MaxLength" + "effect/Schema/TypeId/MaxLength" ) as Schema.MaxLengthTypeId /** @internal */ export const LengthTypeId: Schema.LengthTypeId = Symbol.for( - "@effect/schema/TypeId/Length" + "effect/Schema/TypeId/Length" ) as Schema.LengthTypeId /** @internal */ export const MinItemsTypeId: Schema.MinItemsTypeId = Symbol.for( - "@effect/schema/TypeId/MinItems" + "effect/Schema/TypeId/MinItems" ) as Schema.MinItemsTypeId /** @internal */ export const MaxItemsTypeId: Schema.MaxItemsTypeId = Symbol.for( - "@effect/schema/TypeId/MaxItems" + "effect/Schema/TypeId/MaxItems" ) as Schema.MaxItemsTypeId /** @internal */ export const ItemsCountTypeId: Schema.ItemsCountTypeId = Symbol.for( - "@effect/schema/TypeId/ItemsCount" + "effect/Schema/TypeId/ItemsCount" ) as Schema.ItemsCountTypeId /** @internal */ -export const ParseJsonTypeId: unique symbol = Symbol.for("@effect/schema/TypeId/ParseJson") +export const ParseJsonTypeId: unique symbol = Symbol.for("effect/Schema/TypeId/ParseJson") diff --git a/packages/schema/src/internal/serializable.ts b/packages/effect/src/internal/schema/serializable.ts similarity index 62% rename from packages/schema/src/internal/serializable.ts rename to packages/effect/src/internal/schema/serializable.ts index eff1d96786..52e0539fac 100644 --- a/packages/schema/src/internal/serializable.ts +++ b/packages/effect/src/internal/schema/serializable.ts @@ -1,9 +1,9 @@ /** @internal */ export const symbol: unique symbol = Symbol.for( - "@effect/schema/Serializable/symbol" + "effect/Schema/Serializable/symbol" ) /** @internal */ export const symbolResult: unique symbol = Symbol.for( - "@effect/schema/Serializable/symbolResult" + "effect/Schema/Serializable/symbolResult" ) diff --git a/packages/schema/src/internal/util.ts b/packages/effect/src/internal/schema/util.ts similarity index 96% rename from packages/schema/src/internal/util.ts rename to packages/effect/src/internal/schema/util.ts index 3e6ef905f0..d7dcfb600a 100644 --- a/packages/schema/src/internal/util.ts +++ b/packages/effect/src/internal/schema/util.ts @@ -1,7 +1,7 @@ import * as array_ from "effect/Array" import * as Predicate from "effect/Predicate" -import type * as AST from "../AST.js" -import type * as ParseResult from "../ParseResult.js" +import type * as ParseResult from "../../ParseResult.js" +import type * as AST from "../../SchemaAST.js" /** @internal */ export const getKeysForIndexSignature = ( diff --git a/packages/schema/test/Arbitrary/Arbitrary.test.ts b/packages/effect/test/Schema/Arbitrary/Arbitrary.test.ts similarity index 98% rename from packages/schema/test/Arbitrary/Arbitrary.test.ts rename to packages/effect/test/Schema/Arbitrary/Arbitrary.test.ts index f8ea825cf4..a1037e52f9 100644 --- a/packages/schema/test/Arbitrary/Arbitrary.test.ts +++ b/packages/effect/test/Schema/Arbitrary/Arbitrary.test.ts @@ -1,8 +1,8 @@ -import * as Arbitrary from "@effect/schema/Arbitrary" -import * as S from "@effect/schema/Schema" -import { expectValidArbitrary } from "@effect/schema/test/TestUtils" +import * as Arbitrary from "effect/Arbitrary" import * as Order from "effect/Order" import { isUnknown } from "effect/Predicate" +import * as S from "effect/Schema" +import { expectValidArbitrary } from "effect/test/Schema/TestUtils" import * as fc from "fast-check" import { describe, expect, it } from "vitest" @@ -139,7 +139,7 @@ schema (NeverKeyword): never`) }) it("uniqueSymbolFromSelf", () => { - const a = Symbol.for("@effect/schema/test/a") + const a = Symbol.for("effect/Schema/test/a") const schema = S.UniqueSymbolFromSelf(a) expectValidArbitrary(schema) }) diff --git a/packages/schema/test/Arbitrary/Class.test.ts b/packages/effect/test/Schema/Arbitrary/Class.test.ts similarity index 87% rename from packages/schema/test/Arbitrary/Class.test.ts rename to packages/effect/test/Schema/Arbitrary/Class.test.ts index b16d995232..92c80ac1bd 100644 --- a/packages/schema/test/Arbitrary/Class.test.ts +++ b/packages/effect/test/Schema/Arbitrary/Class.test.ts @@ -1,8 +1,8 @@ -import * as S from "@effect/schema/Schema" -import { expectValidArbitrary } from "@effect/schema/test/TestUtils" +import * as S from "effect/Schema" +import { expectValidArbitrary } from "effect/test/Schema/TestUtils" import { describe, it } from "vitest" -describe("class", () => { +describe("Class", () => { it("required property signature", () => { class Class extends S.Class("Class")({ a: S.Number diff --git a/packages/schema/test/Arbitrary/getConstraints.test.ts b/packages/effect/test/Schema/Arbitrary/getConstraints.test.ts similarity index 97% rename from packages/schema/test/Arbitrary/getConstraints.test.ts rename to packages/effect/test/Schema/Arbitrary/getConstraints.test.ts index beedcf58b4..c4bce6b119 100644 --- a/packages/schema/test/Arbitrary/getConstraints.test.ts +++ b/packages/effect/test/Schema/Arbitrary/getConstraints.test.ts @@ -1,12 +1,12 @@ -import * as A from "@effect/schema/Arbitrary" -import * as S from "@effect/schema/Schema" +import * as A from "effect/Arbitrary" +import * as S from "effect/Schema" import { describe, expect, it } from "vitest" const expectConstraints = (schema: S.Schema, constraints: A.Constraints) => { expect(A.getConstraints(schema.ast as any)).toEqual(constraints) } -describe("Arbitrary > getConstraints", () => { +describe("getConstraints", () => { describe("number", () => { it("GreaterThanTypeId", () => { expectConstraints(S.Number.pipe(S.greaterThan(0)), new A.NumberConstraints({ min: 0 })) diff --git a/packages/schema/test/JSONSchema.test.ts b/packages/effect/test/Schema/JSONSchema.test.ts similarity index 99% rename from packages/schema/test/JSONSchema.test.ts rename to packages/effect/test/Schema/JSONSchema.test.ts index bb92bdecbe..dfbc21738a 100644 --- a/packages/schema/test/JSONSchema.test.ts +++ b/packages/effect/test/Schema/JSONSchema.test.ts @@ -1,9 +1,9 @@ -import * as A from "@effect/schema/Arbitrary" -import * as AST from "@effect/schema/AST" -import * as JSONSchema from "@effect/schema/JSONSchema" -import * as Schema from "@effect/schema/Schema" import AjvNonEsm from "ajv" +import * as A from "effect/Arbitrary" +import * as JSONSchema from "effect/JSONSchema" import * as Predicate from "effect/Predicate" +import * as Schema from "effect/Schema" +import * as AST from "effect/SchemaAST" import * as fc from "fast-check" import { describe, expect, it } from "vitest" @@ -104,10 +104,10 @@ schema (SymbolKeyword): symbol` it("a unique symbol should raise an error", () => { expectError( - Schema.UniqueSymbolFromSelf(Symbol.for("@effect/schema/test/a")), + Schema.UniqueSymbolFromSelf(Symbol.for("effect/Schema/test/a")), `Missing annotation details: Generating a JSON Schema for this schema requires a "jsonSchema" annotation -schema (UniqueSymbol): Symbol(@effect/schema/test/a)` +schema (UniqueSymbol): Symbol(effect/Schema/test/a)` ) }) @@ -789,11 +789,11 @@ schema (Declaration): DateFromSelf` }) it("should raise an error if there is a property named with a symbol", () => { - const a = Symbol.for("@effect/schema/test/a") + const a = Symbol.for("effect/Schema/test/a") expectError( Schema.Struct({ [a]: Schema.String }), `Unsupported key -details: Cannot encode Symbol(@effect/schema/test/a) key to JSON Schema` +details: Cannot encode Symbol(effect/Schema/test/a) key to JSON Schema` ) }) diff --git a/packages/schema/test/ParseResult.test.ts b/packages/effect/test/Schema/ParseResult.test.ts similarity index 99% rename from packages/schema/test/ParseResult.test.ts rename to packages/effect/test/Schema/ParseResult.test.ts index 2558e5ee3c..7655d6d0a1 100644 --- a/packages/schema/test/ParseResult.test.ts +++ b/packages/effect/test/Schema/ParseResult.test.ts @@ -1,7 +1,7 @@ -import * as AST from "@effect/schema/AST" -import * as P from "@effect/schema/ParseResult" -import * as S from "@effect/schema/Schema" import { Effect, Either, Exit } from "effect" +import * as P from "effect/ParseResult" +import * as S from "effect/Schema" +import * as AST from "effect/SchemaAST" import { inspect } from "node:util" import { describe, expect, it } from "vitest" diff --git a/packages/schema/test/Pretty.test.ts b/packages/effect/test/Schema/Pretty.test.ts similarity index 97% rename from packages/schema/test/Pretty.test.ts rename to packages/effect/test/Schema/Pretty.test.ts index cb13c36955..a4cbff8f7e 100644 --- a/packages/schema/test/Pretty.test.ts +++ b/packages/effect/test/Schema/Pretty.test.ts @@ -1,7 +1,7 @@ -import * as AST from "@effect/schema/AST" -import * as Pretty from "@effect/schema/Pretty" -import * as S from "@effect/schema/Schema" import { isUnknown } from "effect/Predicate" +import * as Pretty from "effect/Pretty" +import * as S from "effect/Schema" +import * as AST from "effect/SchemaAST" import { describe, expect, it } from "vitest" describe("Pretty", () => { @@ -127,10 +127,10 @@ schema (Declaration): `) }) it("uniqueSymbolFromSelf", () => { - const a = Symbol.for("@effect/schema/test/a") + const a = Symbol.for("effect/Schema/test/a") const schema = S.UniqueSymbolFromSelf(a) const pretty = Pretty.make(schema) - expect(pretty(a)).toEqual("Symbol(@effect/schema/test/a)") + expect(pretty(a)).toEqual("Symbol(effect/Schema/test/a)") }) describe("enums", () => { @@ -256,11 +256,11 @@ schema (Declaration): `) }) it("record(symbol, string)", () => { - const a = Symbol.for("@effect/schema/test/a") + const a = Symbol.for("effect/Schema/test/a") const schema = S.Record({ key: S.SymbolFromSelf, value: S.String }) const pretty = Pretty.make(schema) expect(pretty({ [a]: "a" })).toEqual( - `{ Symbol(@effect/schema/test/a): "a" }` + `{ Symbol(effect/Schema/test/a): "a" }` ) }) }) diff --git a/packages/schema/test/Schema/Any/Any.test.ts b/packages/effect/test/Schema/Schema/Any/Any.test.ts similarity index 84% rename from packages/schema/test/Schema/Any/Any.test.ts rename to packages/effect/test/Schema/Schema/Any/Any.test.ts index 9c9c84964b..45271ff5b6 100644 --- a/packages/schema/test/Schema/Any/Any.test.ts +++ b/packages/effect/test/Schema/Schema/Any/Any.test.ts @@ -1,5 +1,5 @@ -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" +import * as S from "effect/Schema" +import * as Util from "effect/test/Schema/TestUtils" import { describe, it } from "vitest" describe("Any", () => { diff --git a/packages/schema/test/Schema/Array/Array.test.ts b/packages/effect/test/Schema/Schema/Array/Array.test.ts similarity index 90% rename from packages/schema/test/Schema/Array/Array.test.ts rename to packages/effect/test/Schema/Schema/Array/Array.test.ts index 75ebbcd413..0c640642af 100644 --- a/packages/schema/test/Schema/Array/Array.test.ts +++ b/packages/effect/test/Schema/Schema/Array/Array.test.ts @@ -1,7 +1,7 @@ -import * as AST from "@effect/schema/AST" -import * as ParseResult from "@effect/schema/ParseResult" -import * as S from "@effect/schema/Schema" import * as Either from "effect/Either" +import * as ParseResult from "effect/ParseResult" +import * as S from "effect/Schema" +import * as AST from "effect/SchemaAST" import { assert, describe, expect, it } from "vitest" describe("Array", () => { diff --git a/packages/schema/test/Schema/Array/head.test.ts b/packages/effect/test/Schema/Schema/Array/head.test.ts similarity index 90% rename from packages/schema/test/Schema/Array/head.test.ts rename to packages/effect/test/Schema/Schema/Array/head.test.ts index 00f867e401..805a8a1744 100644 --- a/packages/schema/test/Schema/Array/head.test.ts +++ b/packages/effect/test/Schema/Schema/Array/head.test.ts @@ -1,6 +1,6 @@ -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" import * as Option from "effect/Option" +import * as S from "effect/Schema" +import * as Util from "effect/test/Schema/TestUtils" import { describe, it } from "vitest" describe("head", () => { diff --git a/packages/schema/test/Schema/Array/headOrElse.test.ts b/packages/effect/test/Schema/Schema/Array/headOrElse.test.ts similarity index 95% rename from packages/schema/test/Schema/Array/headOrElse.test.ts rename to packages/effect/test/Schema/Schema/Array/headOrElse.test.ts index ed5274808a..6e95765fe2 100644 --- a/packages/schema/test/Schema/Array/headOrElse.test.ts +++ b/packages/effect/test/Schema/Schema/Array/headOrElse.test.ts @@ -1,5 +1,5 @@ -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" +import * as S from "effect/Schema" +import * as Util from "effect/test/Schema/TestUtils" import { describe, it } from "vitest" describe("headOrElse", () => { diff --git a/packages/schema/test/Schema/Array/itemsCount.test.ts b/packages/effect/test/Schema/Schema/Array/itemsCount.test.ts similarity index 89% rename from packages/schema/test/Schema/Array/itemsCount.test.ts rename to packages/effect/test/Schema/Schema/Array/itemsCount.test.ts index 650bc8acff..5167fbb64e 100644 --- a/packages/schema/test/Schema/Array/itemsCount.test.ts +++ b/packages/effect/test/Schema/Schema/Array/itemsCount.test.ts @@ -1,5 +1,5 @@ -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" +import * as S from "effect/Schema" +import * as Util from "effect/test/Schema/TestUtils" import { describe, it } from "vitest" describe("itemsCount", () => { diff --git a/packages/schema/test/Schema/Array/maxItems.test.ts b/packages/effect/test/Schema/Schema/Array/maxItems.test.ts similarity index 83% rename from packages/schema/test/Schema/Array/maxItems.test.ts rename to packages/effect/test/Schema/Schema/Array/maxItems.test.ts index b803cf8168..036c7e6869 100644 --- a/packages/schema/test/Schema/Array/maxItems.test.ts +++ b/packages/effect/test/Schema/Schema/Array/maxItems.test.ts @@ -1,5 +1,5 @@ -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" +import * as S from "effect/Schema" +import * as Util from "effect/test/Schema/TestUtils" import { describe, it } from "vitest" describe("maxItems", () => { diff --git a/packages/schema/test/Schema/Array/minItems.test.ts b/packages/effect/test/Schema/Schema/Array/minItems.test.ts similarity index 88% rename from packages/schema/test/Schema/Array/minItems.test.ts rename to packages/effect/test/Schema/Schema/Array/minItems.test.ts index dce6d99e3b..4d6de3d485 100644 --- a/packages/schema/test/Schema/Array/minItems.test.ts +++ b/packages/effect/test/Schema/Schema/Array/minItems.test.ts @@ -1,5 +1,5 @@ -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" +import * as S from "effect/Schema" +import * as Util from "effect/test/Schema/TestUtils" import { describe, expect, it } from "vitest" describe("minItems", () => { diff --git a/packages/schema/test/Schema/ArrayEnsure.test.ts b/packages/effect/test/Schema/Schema/ArrayEnsure.test.ts similarity index 96% rename from packages/schema/test/Schema/ArrayEnsure.test.ts rename to packages/effect/test/Schema/Schema/ArrayEnsure.test.ts index 1567ed127f..c69c6622b9 100644 --- a/packages/schema/test/Schema/ArrayEnsure.test.ts +++ b/packages/effect/test/Schema/Schema/ArrayEnsure.test.ts @@ -1,5 +1,5 @@ -import * as AST from "@effect/schema/AST" -import * as S from "@effect/schema/Schema" +import * as S from "effect/Schema" +import * as AST from "effect/SchemaAST" import { describe, expect, it } from "vitest" import { expectDecodeUnknownFailure, expectDecodeUnknownSuccess, expectEncodeSuccess } from "../TestUtils.js" diff --git a/packages/schema/test/Schema/BigDecimal/BigDecimal.test.ts b/packages/effect/test/Schema/Schema/BigDecimal/BigDecimal.test.ts similarity index 91% rename from packages/schema/test/Schema/BigDecimal/BigDecimal.test.ts rename to packages/effect/test/Schema/Schema/BigDecimal/BigDecimal.test.ts index 2adb7be3a5..6810b88c26 100644 --- a/packages/schema/test/Schema/BigDecimal/BigDecimal.test.ts +++ b/packages/effect/test/Schema/Schema/BigDecimal/BigDecimal.test.ts @@ -1,6 +1,6 @@ -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" import { BigDecimal } from "effect" +import * as S from "effect/Schema" +import * as Util from "effect/test/Schema/TestUtils" import { describe, it } from "vitest" describe("BigDecimal", () => { diff --git a/packages/schema/test/Schema/BigDecimal/BigDecimalFromNumber.test.ts b/packages/effect/test/Schema/Schema/BigDecimal/BigDecimalFromNumber.test.ts similarity index 91% rename from packages/schema/test/Schema/BigDecimal/BigDecimalFromNumber.test.ts rename to packages/effect/test/Schema/Schema/BigDecimal/BigDecimalFromNumber.test.ts index 26b583dc6d..d36a602562 100644 --- a/packages/schema/test/Schema/BigDecimal/BigDecimalFromNumber.test.ts +++ b/packages/effect/test/Schema/Schema/BigDecimal/BigDecimalFromNumber.test.ts @@ -1,6 +1,6 @@ -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" import { BigDecimal } from "effect" +import * as S from "effect/Schema" +import * as Util from "effect/test/Schema/TestUtils" import { describe, it } from "vitest" describe("BigDecimalFromNumber", () => { diff --git a/packages/schema/test/Schema/BigDecimal/BigDecimalFromSelf.test.ts b/packages/effect/test/Schema/Schema/BigDecimal/BigDecimalFromSelf.test.ts similarity index 89% rename from packages/schema/test/Schema/BigDecimal/BigDecimalFromSelf.test.ts rename to packages/effect/test/Schema/Schema/BigDecimal/BigDecimalFromSelf.test.ts index e764008ac5..9092297cbd 100644 --- a/packages/schema/test/Schema/BigDecimal/BigDecimalFromSelf.test.ts +++ b/packages/effect/test/Schema/Schema/BigDecimal/BigDecimalFromSelf.test.ts @@ -1,8 +1,8 @@ -import * as Equivalence from "@effect/schema/Equivalence" -import * as Pretty from "@effect/schema/Pretty" -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" import { BigDecimal } from "effect" +import * as Pretty from "effect/Pretty" +import * as S from "effect/Schema" +import * as Equivalence from "effect/SchemaEquivalence" +import * as Util from "effect/test/Schema/TestUtils" import { describe, expect, it } from "vitest" describe("BigDecimalFromSelf", () => { diff --git a/packages/schema/test/Schema/BigDecimal/NegativeBigDecimalFromSelf.test.ts b/packages/effect/test/Schema/Schema/BigDecimal/NegativeBigDecimalFromSelf.test.ts similarity index 90% rename from packages/schema/test/Schema/BigDecimal/NegativeBigDecimalFromSelf.test.ts rename to packages/effect/test/Schema/Schema/BigDecimal/NegativeBigDecimalFromSelf.test.ts index 10d79c0c92..00e582fd3f 100644 --- a/packages/schema/test/Schema/BigDecimal/NegativeBigDecimalFromSelf.test.ts +++ b/packages/effect/test/Schema/Schema/BigDecimal/NegativeBigDecimalFromSelf.test.ts @@ -1,6 +1,6 @@ -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" import { BigDecimal } from "effect" +import * as S from "effect/Schema" +import * as Util from "effect/test/Schema/TestUtils" import { describe, it } from "vitest" describe("NegativeBigDecimalFromSelf", () => { diff --git a/packages/schema/test/Schema/BigDecimal/NonNegativeBigDecimalFromSelf.test.ts b/packages/effect/test/Schema/Schema/BigDecimal/NonNegativeBigDecimalFromSelf.test.ts similarity index 89% rename from packages/schema/test/Schema/BigDecimal/NonNegativeBigDecimalFromSelf.test.ts rename to packages/effect/test/Schema/Schema/BigDecimal/NonNegativeBigDecimalFromSelf.test.ts index db065c4377..1f325b386c 100644 --- a/packages/schema/test/Schema/BigDecimal/NonNegativeBigDecimalFromSelf.test.ts +++ b/packages/effect/test/Schema/Schema/BigDecimal/NonNegativeBigDecimalFromSelf.test.ts @@ -1,6 +1,6 @@ -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" import { BigDecimal } from "effect" +import * as S from "effect/Schema" +import * as Util from "effect/test/Schema/TestUtils" import { describe, it } from "vitest" describe("NonNegativeBigDecimalFromSelf", () => { diff --git a/packages/schema/test/Schema/BigDecimal/NonPositiveBigDecimalFromSelf.test.ts b/packages/effect/test/Schema/Schema/BigDecimal/NonPositiveBigDecimalFromSelf.test.ts similarity index 89% rename from packages/schema/test/Schema/BigDecimal/NonPositiveBigDecimalFromSelf.test.ts rename to packages/effect/test/Schema/Schema/BigDecimal/NonPositiveBigDecimalFromSelf.test.ts index 81b1ed6138..54df079698 100644 --- a/packages/schema/test/Schema/BigDecimal/NonPositiveBigDecimalFromSelf.test.ts +++ b/packages/effect/test/Schema/Schema/BigDecimal/NonPositiveBigDecimalFromSelf.test.ts @@ -1,6 +1,6 @@ -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" import { BigDecimal } from "effect" +import * as S from "effect/Schema" +import * as Util from "effect/test/Schema/TestUtils" import { describe, it } from "vitest" describe("NonPositiveBigDecimalFromSelf", () => { diff --git a/packages/schema/test/Schema/BigDecimal/PositiveBigDecimalFromSelf.test.ts b/packages/effect/test/Schema/Schema/BigDecimal/PositiveBigDecimalFromSelf.test.ts similarity index 90% rename from packages/schema/test/Schema/BigDecimal/PositiveBigDecimalFromSelf.test.ts rename to packages/effect/test/Schema/Schema/BigDecimal/PositiveBigDecimalFromSelf.test.ts index aab686c7f0..2bc8ba4d6c 100644 --- a/packages/schema/test/Schema/BigDecimal/PositiveBigDecimalFromSelf.test.ts +++ b/packages/effect/test/Schema/Schema/BigDecimal/PositiveBigDecimalFromSelf.test.ts @@ -1,6 +1,6 @@ -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" import { BigDecimal } from "effect" +import * as S from "effect/Schema" +import * as Util from "effect/test/Schema/TestUtils" import { describe, it } from "vitest" describe("PositiveBigDecimalFromSelf", () => { diff --git a/packages/schema/test/Schema/BigDecimal/betweenBigDecimal.test.ts b/packages/effect/test/Schema/Schema/BigDecimal/betweenBigDecimal.test.ts similarity index 92% rename from packages/schema/test/Schema/BigDecimal/betweenBigDecimal.test.ts rename to packages/effect/test/Schema/Schema/BigDecimal/betweenBigDecimal.test.ts index 4d045188e7..86e3e04670 100644 --- a/packages/schema/test/Schema/BigDecimal/betweenBigDecimal.test.ts +++ b/packages/effect/test/Schema/Schema/BigDecimal/betweenBigDecimal.test.ts @@ -1,6 +1,6 @@ -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" import { BigDecimal } from "effect" +import * as S from "effect/Schema" +import * as Util from "effect/test/Schema/TestUtils" import { describe, it } from "vitest" const max = BigDecimal.make(1n, 0) diff --git a/packages/schema/test/Schema/BigDecimal/clampBigDecimal.test.ts b/packages/effect/test/Schema/Schema/BigDecimal/clampBigDecimal.test.ts similarity index 86% rename from packages/schema/test/Schema/BigDecimal/clampBigDecimal.test.ts rename to packages/effect/test/Schema/Schema/BigDecimal/clampBigDecimal.test.ts index b581a16c17..f849de1da0 100644 --- a/packages/schema/test/Schema/BigDecimal/clampBigDecimal.test.ts +++ b/packages/effect/test/Schema/Schema/BigDecimal/clampBigDecimal.test.ts @@ -1,6 +1,6 @@ -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" import { BigDecimal } from "effect" +import * as S from "effect/Schema" +import * as Util from "effect/test/Schema/TestUtils" import { describe, it } from "vitest" describe("clampBigDecimal", () => { diff --git a/packages/schema/test/Schema/BigDecimal/greaterThanBigDecimal.test.ts b/packages/effect/test/Schema/Schema/BigDecimal/greaterThanBigDecimal.test.ts similarity index 89% rename from packages/schema/test/Schema/BigDecimal/greaterThanBigDecimal.test.ts rename to packages/effect/test/Schema/Schema/BigDecimal/greaterThanBigDecimal.test.ts index b5e1135907..d8ba56fec3 100644 --- a/packages/schema/test/Schema/BigDecimal/greaterThanBigDecimal.test.ts +++ b/packages/effect/test/Schema/Schema/BigDecimal/greaterThanBigDecimal.test.ts @@ -1,6 +1,6 @@ -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" import { BigDecimal } from "effect" +import * as S from "effect/Schema" +import * as Util from "effect/test/Schema/TestUtils" import { describe, it } from "vitest" describe("greaterThanBigDecimal", () => { diff --git a/packages/schema/test/Schema/BigDecimal/greaterThanOrEqualToBigDecimal.test.ts b/packages/effect/test/Schema/Schema/BigDecimal/greaterThanOrEqualToBigDecimal.test.ts similarity index 88% rename from packages/schema/test/Schema/BigDecimal/greaterThanOrEqualToBigDecimal.test.ts rename to packages/effect/test/Schema/Schema/BigDecimal/greaterThanOrEqualToBigDecimal.test.ts index 70be1ea007..624b6ab6ee 100644 --- a/packages/schema/test/Schema/BigDecimal/greaterThanOrEqualToBigDecimal.test.ts +++ b/packages/effect/test/Schema/Schema/BigDecimal/greaterThanOrEqualToBigDecimal.test.ts @@ -1,6 +1,6 @@ -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" import { BigDecimal } from "effect" +import * as S from "effect/Schema" +import * as Util from "effect/test/Schema/TestUtils" import { describe, it } from "vitest" describe("greaterThanOrEqualToBigDecimal", () => { diff --git a/packages/schema/test/Schema/BigDecimal/lessThanBigDecimal.test.ts b/packages/effect/test/Schema/Schema/BigDecimal/lessThanBigDecimal.test.ts similarity index 89% rename from packages/schema/test/Schema/BigDecimal/lessThanBigDecimal.test.ts rename to packages/effect/test/Schema/Schema/BigDecimal/lessThanBigDecimal.test.ts index b08eb5a1ad..36c51e0b36 100644 --- a/packages/schema/test/Schema/BigDecimal/lessThanBigDecimal.test.ts +++ b/packages/effect/test/Schema/Schema/BigDecimal/lessThanBigDecimal.test.ts @@ -1,6 +1,6 @@ -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" import { BigDecimal } from "effect" +import * as S from "effect/Schema" +import * as Util from "effect/test/Schema/TestUtils" import { describe, it } from "vitest" describe("lessThanBigDecimal", () => { diff --git a/packages/schema/test/Schema/BigDecimal/lessThanOrEqualToBigDecimal.test.ts b/packages/effect/test/Schema/Schema/BigDecimal/lessThanOrEqualToBigDecimal.test.ts similarity index 88% rename from packages/schema/test/Schema/BigDecimal/lessThanOrEqualToBigDecimal.test.ts rename to packages/effect/test/Schema/Schema/BigDecimal/lessThanOrEqualToBigDecimal.test.ts index 4b5f147c75..9834468dce 100644 --- a/packages/schema/test/Schema/BigDecimal/lessThanOrEqualToBigDecimal.test.ts +++ b/packages/effect/test/Schema/Schema/BigDecimal/lessThanOrEqualToBigDecimal.test.ts @@ -1,6 +1,6 @@ -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" import { BigDecimal } from "effect" +import * as S from "effect/Schema" +import * as Util from "effect/test/Schema/TestUtils" import { describe, it } from "vitest" describe("lessThanOrEqualToBigDecimal", () => { diff --git a/packages/schema/test/Schema/BigInt/BigInt.test.ts b/packages/effect/test/Schema/Schema/BigInt/BigInt.test.ts similarity index 94% rename from packages/schema/test/Schema/BigInt/BigInt.test.ts rename to packages/effect/test/Schema/Schema/BigInt/BigInt.test.ts index e2b4f8555f..824ad81618 100644 --- a/packages/schema/test/Schema/BigInt/BigInt.test.ts +++ b/packages/effect/test/Schema/Schema/BigInt/BigInt.test.ts @@ -1,5 +1,5 @@ -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" +import * as S from "effect/Schema" +import * as Util from "effect/test/Schema/TestUtils" import { describe, it } from "vitest" describe("BigInt", () => { diff --git a/packages/schema/test/Schema/BigInt/BigIntFromNumber.test.ts b/packages/effect/test/Schema/Schema/BigInt/BigIntFromNumber.test.ts similarity index 94% rename from packages/schema/test/Schema/BigInt/BigIntFromNumber.test.ts rename to packages/effect/test/Schema/Schema/BigInt/BigIntFromNumber.test.ts index 423d27c790..a131dea9e2 100644 --- a/packages/schema/test/Schema/BigInt/BigIntFromNumber.test.ts +++ b/packages/effect/test/Schema/Schema/BigInt/BigIntFromNumber.test.ts @@ -1,5 +1,5 @@ -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" +import * as S from "effect/Schema" +import * as Util from "effect/test/Schema/TestUtils" import { describe, it } from "vitest" describe("BigIntFromNumber", () => { diff --git a/packages/schema/test/Schema/BigInt/BigIntFromSelf.test.ts b/packages/effect/test/Schema/Schema/BigInt/BigIntFromSelf.test.ts similarity index 85% rename from packages/schema/test/Schema/BigInt/BigIntFromSelf.test.ts rename to packages/effect/test/Schema/Schema/BigInt/BigIntFromSelf.test.ts index 6c38a8c80c..b7a925b7a5 100644 --- a/packages/schema/test/Schema/BigInt/BigIntFromSelf.test.ts +++ b/packages/effect/test/Schema/Schema/BigInt/BigIntFromSelf.test.ts @@ -1,5 +1,5 @@ -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" +import * as S from "effect/Schema" +import * as Util from "effect/test/Schema/TestUtils" import { describe, it } from "vitest" describe("BigIntFromSelf", () => { diff --git a/packages/schema/test/Schema/BigInt/NegativeBigIntFromSelf.test.ts b/packages/effect/test/Schema/Schema/BigInt/NegativeBigIntFromSelf.test.ts similarity index 86% rename from packages/schema/test/Schema/BigInt/NegativeBigIntFromSelf.test.ts rename to packages/effect/test/Schema/Schema/BigInt/NegativeBigIntFromSelf.test.ts index 22c696631c..7956c3d6a8 100644 --- a/packages/schema/test/Schema/BigInt/NegativeBigIntFromSelf.test.ts +++ b/packages/effect/test/Schema/Schema/BigInt/NegativeBigIntFromSelf.test.ts @@ -1,5 +1,5 @@ -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" +import * as S from "effect/Schema" +import * as Util from "effect/test/Schema/TestUtils" import { describe, it } from "vitest" describe("NegativeBigIntFromSelf", () => { diff --git a/packages/schema/test/Schema/BigInt/NonNegativeBigIntFromSelf.test.ts b/packages/effect/test/Schema/Schema/BigInt/NonNegativeBigIntFromSelf.test.ts similarity index 83% rename from packages/schema/test/Schema/BigInt/NonNegativeBigIntFromSelf.test.ts rename to packages/effect/test/Schema/Schema/BigInt/NonNegativeBigIntFromSelf.test.ts index 845948ba8a..35d533a1c9 100644 --- a/packages/schema/test/Schema/BigInt/NonNegativeBigIntFromSelf.test.ts +++ b/packages/effect/test/Schema/Schema/BigInt/NonNegativeBigIntFromSelf.test.ts @@ -1,5 +1,5 @@ -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" +import * as S from "effect/Schema" +import * as Util from "effect/test/Schema/TestUtils" import { describe, it } from "vitest" describe("NonNegativeBigIntFromSelf", () => { diff --git a/packages/schema/test/Schema/BigInt/NonPositiveBigIntFromSelf.test.ts b/packages/effect/test/Schema/Schema/BigInt/NonPositiveBigIntFromSelf.test.ts similarity index 84% rename from packages/schema/test/Schema/BigInt/NonPositiveBigIntFromSelf.test.ts rename to packages/effect/test/Schema/Schema/BigInt/NonPositiveBigIntFromSelf.test.ts index 6c3b274a30..c9f3f789b1 100644 --- a/packages/schema/test/Schema/BigInt/NonPositiveBigIntFromSelf.test.ts +++ b/packages/effect/test/Schema/Schema/BigInt/NonPositiveBigIntFromSelf.test.ts @@ -1,5 +1,5 @@ -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" +import * as S from "effect/Schema" +import * as Util from "effect/test/Schema/TestUtils" import { describe, it } from "vitest" describe("NonPositiveBigIntFromSelf", () => { diff --git a/packages/schema/test/Schema/BigInt/PositiveBigIntFromSelf.test.ts b/packages/effect/test/Schema/Schema/BigInt/PositiveBigIntFromSelf.test.ts similarity index 86% rename from packages/schema/test/Schema/BigInt/PositiveBigIntFromSelf.test.ts rename to packages/effect/test/Schema/Schema/BigInt/PositiveBigIntFromSelf.test.ts index 40ced3e458..81478046b4 100644 --- a/packages/schema/test/Schema/BigInt/PositiveBigIntFromSelf.test.ts +++ b/packages/effect/test/Schema/Schema/BigInt/PositiveBigIntFromSelf.test.ts @@ -1,5 +1,5 @@ -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" +import * as S from "effect/Schema" +import * as Util from "effect/test/Schema/TestUtils" import { describe, it } from "vitest" describe("PositiveBigIntFromSelf", () => { diff --git a/packages/schema/test/Schema/BigInt/betweenBigInt.test.ts b/packages/effect/test/Schema/Schema/BigInt/betweenBigInt.test.ts similarity index 88% rename from packages/schema/test/Schema/BigInt/betweenBigInt.test.ts rename to packages/effect/test/Schema/Schema/BigInt/betweenBigInt.test.ts index 2b6534212c..4682a1a31b 100644 --- a/packages/schema/test/Schema/BigInt/betweenBigInt.test.ts +++ b/packages/effect/test/Schema/Schema/BigInt/betweenBigInt.test.ts @@ -1,5 +1,5 @@ -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" +import * as S from "effect/Schema" +import * as Util from "effect/test/Schema/TestUtils" import { describe, it } from "vitest" describe("betweenBigInt", () => { diff --git a/packages/schema/test/Schema/BigInt/clampBigInt.test.ts b/packages/effect/test/Schema/Schema/BigInt/clampBigInt.test.ts similarity index 78% rename from packages/schema/test/Schema/BigInt/clampBigInt.test.ts rename to packages/effect/test/Schema/Schema/BigInt/clampBigInt.test.ts index 81bf61e7c7..59ea24aa2f 100644 --- a/packages/schema/test/Schema/BigInt/clampBigInt.test.ts +++ b/packages/effect/test/Schema/Schema/BigInt/clampBigInt.test.ts @@ -1,5 +1,5 @@ -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" +import * as S from "effect/Schema" +import * as Util from "effect/test/Schema/TestUtils" import { describe, it } from "vitest" describe("clampBigInt", () => { diff --git a/packages/schema/test/Schema/BigInt/greaterThanBigInt.test.ts b/packages/effect/test/Schema/Schema/BigInt/greaterThanBigInt.test.ts similarity index 86% rename from packages/schema/test/Schema/BigInt/greaterThanBigInt.test.ts rename to packages/effect/test/Schema/Schema/BigInt/greaterThanBigInt.test.ts index 8253809b61..46b61d9cc3 100644 --- a/packages/schema/test/Schema/BigInt/greaterThanBigInt.test.ts +++ b/packages/effect/test/Schema/Schema/BigInt/greaterThanBigInt.test.ts @@ -1,5 +1,5 @@ -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" +import * as S from "effect/Schema" +import * as Util from "effect/test/Schema/TestUtils" import { describe, it } from "vitest" describe("greaterThanBigInt", () => { diff --git a/packages/schema/test/Schema/BigInt/greaterThanOrEqualToBigInt.test.ts b/packages/effect/test/Schema/Schema/BigInt/greaterThanOrEqualToBigInt.test.ts similarity index 84% rename from packages/schema/test/Schema/BigInt/greaterThanOrEqualToBigInt.test.ts rename to packages/effect/test/Schema/Schema/BigInt/greaterThanOrEqualToBigInt.test.ts index 6e0ee92197..1694c7aa5c 100644 --- a/packages/schema/test/Schema/BigInt/greaterThanOrEqualToBigInt.test.ts +++ b/packages/effect/test/Schema/Schema/BigInt/greaterThanOrEqualToBigInt.test.ts @@ -1,5 +1,5 @@ -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" +import * as S from "effect/Schema" +import * as Util from "effect/test/Schema/TestUtils" import { describe, it } from "vitest" describe("greaterThanOrEqualToBigInt", () => { diff --git a/packages/schema/test/Schema/BigInt/lessThanBigInt.test.ts b/packages/effect/test/Schema/Schema/BigInt/lessThanBigInt.test.ts similarity index 86% rename from packages/schema/test/Schema/BigInt/lessThanBigInt.test.ts rename to packages/effect/test/Schema/Schema/BigInt/lessThanBigInt.test.ts index 1533c17f31..312491807a 100644 --- a/packages/schema/test/Schema/BigInt/lessThanBigInt.test.ts +++ b/packages/effect/test/Schema/Schema/BigInt/lessThanBigInt.test.ts @@ -1,5 +1,5 @@ -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" +import * as S from "effect/Schema" +import * as Util from "effect/test/Schema/TestUtils" import { describe, it } from "vitest" describe("lessThanBigInt", () => { diff --git a/packages/schema/test/Schema/BigInt/lessThanOrEqualToBigInt.test.ts b/packages/effect/test/Schema/Schema/BigInt/lessThanOrEqualToBigInt.test.ts similarity index 84% rename from packages/schema/test/Schema/BigInt/lessThanOrEqualToBigInt.test.ts rename to packages/effect/test/Schema/Schema/BigInt/lessThanOrEqualToBigInt.test.ts index 2e5c410eb7..cec7a7cbcb 100644 --- a/packages/schema/test/Schema/BigInt/lessThanOrEqualToBigInt.test.ts +++ b/packages/effect/test/Schema/Schema/BigInt/lessThanOrEqualToBigInt.test.ts @@ -1,5 +1,5 @@ -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" +import * as S from "effect/Schema" +import * as Util from "effect/test/Schema/TestUtils" import { describe, it } from "vitest" describe("lessThanOrEqualToBigInt", () => { diff --git a/packages/schema/test/Schema/Boolean/Boolean.test.ts b/packages/effect/test/Schema/Schema/Boolean/Boolean.test.ts similarity index 83% rename from packages/schema/test/Schema/Boolean/Boolean.test.ts rename to packages/effect/test/Schema/Schema/Boolean/Boolean.test.ts index 80f3612ea0..36da773376 100644 --- a/packages/schema/test/Schema/Boolean/Boolean.test.ts +++ b/packages/effect/test/Schema/Schema/Boolean/Boolean.test.ts @@ -1,5 +1,5 @@ -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" +import * as S from "effect/Schema" +import * as Util from "effect/test/Schema/TestUtils" import { describe, it } from "vitest" describe("Boolean", () => { diff --git a/packages/schema/test/Schema/Boolean/BooleanFromUnknown.test.ts b/packages/effect/test/Schema/Schema/Boolean/BooleanFromUnknown.test.ts similarity index 89% rename from packages/schema/test/Schema/Boolean/BooleanFromUnknown.test.ts rename to packages/effect/test/Schema/Schema/Boolean/BooleanFromUnknown.test.ts index e3a1294156..cd72e66bbf 100644 --- a/packages/schema/test/Schema/Boolean/BooleanFromUnknown.test.ts +++ b/packages/effect/test/Schema/Schema/Boolean/BooleanFromUnknown.test.ts @@ -1,5 +1,5 @@ -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" +import * as S from "effect/Schema" +import * as Util from "effect/test/Schema/TestUtils" import { describe, it } from "vitest" describe("BooleanFromUnknown", () => { diff --git a/packages/schema/test/Schema/Boolean/Not.test.ts b/packages/effect/test/Schema/Schema/Boolean/Not.test.ts similarity index 80% rename from packages/schema/test/Schema/Boolean/Not.test.ts rename to packages/effect/test/Schema/Schema/Boolean/Not.test.ts index 87300e914a..ef1e6c1275 100644 --- a/packages/schema/test/Schema/Boolean/Not.test.ts +++ b/packages/effect/test/Schema/Schema/Boolean/Not.test.ts @@ -1,5 +1,5 @@ -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" +import * as S from "effect/Schema" +import * as Util from "effect/test/Schema/TestUtils" import { describe, it } from "vitest" describe("Not", () => { diff --git a/packages/schema/test/Schema/Cause/Cause.test.ts b/packages/effect/test/Schema/Schema/Cause/Cause.test.ts similarity index 98% rename from packages/schema/test/Schema/Cause/Cause.test.ts rename to packages/effect/test/Schema/Schema/Cause/Cause.test.ts index 639828e56a..2876be68c1 100644 --- a/packages/schema/test/Schema/Cause/Cause.test.ts +++ b/packages/effect/test/Schema/Schema/Cause/Cause.test.ts @@ -1,6 +1,6 @@ -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" import { Cause, FiberId } from "effect" +import * as S from "effect/Schema" +import * as Util from "effect/test/Schema/TestUtils" import { assert, describe, it } from "vitest" describe("Cause", () => { diff --git a/packages/schema/test/Schema/Cause/CauseFromSelf.test.ts b/packages/effect/test/Schema/Schema/Cause/CauseFromSelf.test.ts similarity index 95% rename from packages/schema/test/Schema/Cause/CauseFromSelf.test.ts rename to packages/effect/test/Schema/Schema/Cause/CauseFromSelf.test.ts index 2a6a4f240f..1290a0d87b 100644 --- a/packages/schema/test/Schema/Cause/CauseFromSelf.test.ts +++ b/packages/effect/test/Schema/Schema/Cause/CauseFromSelf.test.ts @@ -1,8 +1,8 @@ -import * as Pretty from "@effect/schema/Pretty" -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" import * as Cause from "effect/Cause" import * as FiberId from "effect/FiberId" +import * as Pretty from "effect/Pretty" +import * as S from "effect/Schema" +import * as Util from "effect/test/Schema/TestUtils" import { describe, expect, it } from "vitest" describe("CauseFromSelf", () => { diff --git a/packages/schema/test/Schema/Chunk/Chunk.test.ts b/packages/effect/test/Schema/Schema/Chunk/Chunk.test.ts similarity index 91% rename from packages/schema/test/Schema/Chunk/Chunk.test.ts rename to packages/effect/test/Schema/Schema/Chunk/Chunk.test.ts index 519a953fbb..f00e156959 100644 --- a/packages/schema/test/Schema/Chunk/Chunk.test.ts +++ b/packages/effect/test/Schema/Schema/Chunk/Chunk.test.ts @@ -1,6 +1,6 @@ -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" import * as C from "effect/Chunk" +import * as S from "effect/Schema" +import * as Util from "effect/test/Schema/TestUtils" import { describe, it } from "vitest" describe("Chunk", () => { diff --git a/packages/schema/test/Schema/Chunk/ChunkFromSelf.test.ts b/packages/effect/test/Schema/Schema/Chunk/ChunkFromSelf.test.ts similarity index 86% rename from packages/schema/test/Schema/Chunk/ChunkFromSelf.test.ts rename to packages/effect/test/Schema/Schema/Chunk/ChunkFromSelf.test.ts index 2b470ec48d..1d20f5f6d2 100644 --- a/packages/schema/test/Schema/Chunk/ChunkFromSelf.test.ts +++ b/packages/effect/test/Schema/Schema/Chunk/ChunkFromSelf.test.ts @@ -1,8 +1,8 @@ -import * as P from "@effect/schema/ParseResult" -import * as Pretty from "@effect/schema/Pretty" -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" import * as C from "effect/Chunk" +import * as P from "effect/ParseResult" +import * as Pretty from "effect/Pretty" +import * as S from "effect/Schema" +import * as Util from "effect/test/Schema/TestUtils" import { describe, expect, it } from "vitest" describe("ChunkFromSelf", () => { @@ -53,7 +53,7 @@ describe("ChunkFromSelf", () => { expect(is(C.fromIterable(["a", "b", "c"]))).toEqual(true) expect(is(C.fromIterable(["a", "b", 1]))).toEqual(false) - expect(is({ _id: Symbol.for("@effect/schema/test/FakeChunk") })).toEqual(false) + expect(is({ _id: Symbol.for("effect/Schema/test/FakeChunk") })).toEqual(false) }) it("pretty", () => { diff --git a/packages/schema/test/Schema/Chunk/NonEmptyChunk.test.ts b/packages/effect/test/Schema/Schema/Chunk/NonEmptyChunk.test.ts similarity index 91% rename from packages/schema/test/Schema/Chunk/NonEmptyChunk.test.ts rename to packages/effect/test/Schema/Schema/Chunk/NonEmptyChunk.test.ts index 700514feda..a5313fb68f 100644 --- a/packages/schema/test/Schema/Chunk/NonEmptyChunk.test.ts +++ b/packages/effect/test/Schema/Schema/Chunk/NonEmptyChunk.test.ts @@ -1,6 +1,6 @@ -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" import * as C from "effect/Chunk" +import * as S from "effect/Schema" +import * as Util from "effect/test/Schema/TestUtils" import { describe, it } from "vitest" describe("NonEmptyChunk", () => { diff --git a/packages/schema/test/Schema/Chunk/NonEmptyChunkFromSelf.test.ts b/packages/effect/test/Schema/Schema/Chunk/NonEmptyChunkFromSelf.test.ts similarity index 87% rename from packages/schema/test/Schema/Chunk/NonEmptyChunkFromSelf.test.ts rename to packages/effect/test/Schema/Schema/Chunk/NonEmptyChunkFromSelf.test.ts index 0d54017004..8b3d7b1018 100644 --- a/packages/schema/test/Schema/Chunk/NonEmptyChunkFromSelf.test.ts +++ b/packages/effect/test/Schema/Schema/Chunk/NonEmptyChunkFromSelf.test.ts @@ -1,10 +1,10 @@ -import * as Arbitrary from "@effect/schema/Arbitrary" -import * as Equivalence from "@effect/schema/Equivalence" -import * as FastCheck from "@effect/schema/FastCheck" -import * as Pretty from "@effect/schema/Pretty" -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" +import * as Arbitrary from "effect/Arbitrary" import * as C from "effect/Chunk" +import * as FastCheck from "effect/FastCheck" +import * as Pretty from "effect/Pretty" +import * as S from "effect/Schema" +import * as Equivalence from "effect/SchemaEquivalence" +import * as Util from "effect/test/Schema/TestUtils" import { describe, expect, it } from "vitest" describe("NonEmptyChunkFromSelf", () => { diff --git a/packages/schema/test/Schema/Class/Class.test.ts b/packages/effect/test/Schema/Schema/Class/Class.test.ts similarity index 98% rename from packages/schema/test/Schema/Class/Class.test.ts rename to packages/effect/test/Schema/Schema/Class/Class.test.ts index dce446e28c..33f2ded843 100644 --- a/packages/schema/test/Schema/Class/Class.test.ts +++ b/packages/effect/test/Schema/Schema/Class/Class.test.ts @@ -1,11 +1,11 @@ -import * as AST from "@effect/schema/AST" -import * as ParseResult from "@effect/schema/ParseResult" -import * as Pretty from "@effect/schema/Pretty" -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" import { Context, Effect } from "effect" import * as Data from "effect/Data" import * as Equal from "effect/Equal" +import * as ParseResult from "effect/ParseResult" +import * as Pretty from "effect/Pretty" +import * as S from "effect/Schema" +import * as AST from "effect/SchemaAST" +import * as Util from "effect/test/Schema/TestUtils" import { describe, expect, it } from "vitest" class Person extends S.Class("Person")({ diff --git a/packages/schema/test/Schema/Class/TaggedClass.test.ts b/packages/effect/test/Schema/Schema/Class/TaggedClass.test.ts similarity index 98% rename from packages/schema/test/Schema/Class/TaggedClass.test.ts rename to packages/effect/test/Schema/Schema/Class/TaggedClass.test.ts index 57f25ca24e..f561b3cba3 100644 --- a/packages/schema/test/Schema/Class/TaggedClass.test.ts +++ b/packages/effect/test/Schema/Schema/Class/TaggedClass.test.ts @@ -1,7 +1,7 @@ -import * as Equivalence from "@effect/schema/Equivalence" -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" import { pipe, Struct } from "effect" +import * as S from "effect/Schema" +import * as Equivalence from "effect/SchemaEquivalence" +import * as Util from "effect/test/Schema/TestUtils" import { describe, expect, it } from "vitest" describe("TaggedClass", () => { diff --git a/packages/schema/test/Schema/Class/TaggedError.test.ts b/packages/effect/test/Schema/Schema/Class/TaggedError.test.ts similarity index 95% rename from packages/schema/test/Schema/Class/TaggedError.test.ts rename to packages/effect/test/Schema/Schema/Class/TaggedError.test.ts index f14173d099..e1877097e5 100644 --- a/packages/schema/test/Schema/Class/TaggedError.test.ts +++ b/packages/effect/test/Schema/Schema/Class/TaggedError.test.ts @@ -1,7 +1,6 @@ -import { Schema } from "@effect/schema" -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" -import { Cause, Effect, Inspectable } from "effect" +import { Cause, Effect, Inspectable, Schema } from "effect" +import * as S from "effect/Schema" +import * as Util from "effect/test/Schema/TestUtils" import { describe, expect, it } from "vitest" describe("TaggedError", () => { diff --git a/packages/schema/test/Schema/Class/TaggedRequest.test.ts b/packages/effect/test/Schema/Schema/Class/TaggedRequest.test.ts similarity index 96% rename from packages/schema/test/Schema/Class/TaggedRequest.test.ts rename to packages/effect/test/Schema/Schema/Class/TaggedRequest.test.ts index 003cfc3da2..013a05f5dd 100644 --- a/packages/schema/test/Schema/Class/TaggedRequest.test.ts +++ b/packages/effect/test/Schema/Schema/Class/TaggedRequest.test.ts @@ -1,10 +1,10 @@ -import * as ParseResult from "@effect/schema/ParseResult" -import * as S from "@effect/schema/Schema" -import * as Serializable from "@effect/schema/Serializable" -import * as Util from "@effect/schema/test/TestUtils" import { Context, Effect, Exit } from "effect" import * as Equal from "effect/Equal" +import * as ParseResult from "effect/ParseResult" import * as Request from "effect/Request" +import * as S from "effect/Schema" +import * as Serializable from "effect/Serializable" +import * as Util from "effect/test/Schema/TestUtils" import { assert, describe, expect, it } from "vitest" const Name = Context.GenericTag<"Name", string>("Name") diff --git a/packages/schema/test/Schema/Class/extend.test.ts b/packages/effect/test/Schema/Schema/Class/extend.test.ts similarity index 97% rename from packages/schema/test/Schema/Class/extend.test.ts rename to packages/effect/test/Schema/Schema/Class/extend.test.ts index 58507d7ed3..fa2853d501 100644 --- a/packages/schema/test/Schema/Class/extend.test.ts +++ b/packages/effect/test/Schema/Schema/Class/extend.test.ts @@ -1,5 +1,5 @@ -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" +import * as S from "effect/Schema" +import * as Util from "effect/test/Schema/TestUtils" import { describe, expect, it } from "vitest" class Person extends S.Class("Person")({ diff --git a/packages/schema/test/Schema/Class/transformOrFail.test.ts b/packages/effect/test/Schema/Schema/Class/transformOrFail.test.ts similarity index 94% rename from packages/schema/test/Schema/Class/transformOrFail.test.ts rename to packages/effect/test/Schema/Schema/Class/transformOrFail.test.ts index 3146b2ba42..6b5391a0aa 100644 --- a/packages/schema/test/Schema/Class/transformOrFail.test.ts +++ b/packages/effect/test/Schema/Schema/Class/transformOrFail.test.ts @@ -1,7 +1,7 @@ -import * as ParseResult from "@effect/schema/ParseResult" -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" import * as Option from "effect/Option" +import * as ParseResult from "effect/ParseResult" +import * as S from "effect/Schema" +import * as Util from "effect/test/Schema/TestUtils" import { describe, expect, it } from "vitest" class Person extends S.Class("Person")({ diff --git a/packages/schema/test/Schema/Class/transformOrFailFrom.test.ts b/packages/effect/test/Schema/Schema/Class/transformOrFailFrom.test.ts similarity index 94% rename from packages/schema/test/Schema/Class/transformOrFailFrom.test.ts rename to packages/effect/test/Schema/Schema/Class/transformOrFailFrom.test.ts index c795c53913..bf6c52f066 100644 --- a/packages/schema/test/Schema/Class/transformOrFailFrom.test.ts +++ b/packages/effect/test/Schema/Schema/Class/transformOrFailFrom.test.ts @@ -1,7 +1,7 @@ -import * as ParseResult from "@effect/schema/ParseResult" -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" import * as Option from "effect/Option" +import * as ParseResult from "effect/ParseResult" +import * as S from "effect/Schema" +import * as Util from "effect/test/Schema/TestUtils" import { describe, expect, it } from "vitest" class Person extends S.Class("Person")({ diff --git a/packages/schema/test/Schema/Config/Config.test.ts b/packages/effect/test/Schema/Schema/Config/Config.test.ts similarity index 97% rename from packages/schema/test/Schema/Config/Config.test.ts rename to packages/effect/test/Schema/Schema/Config/Config.test.ts index 7aca6f90ca..4196525f79 100644 --- a/packages/schema/test/Schema/Config/Config.test.ts +++ b/packages/effect/test/Schema/Schema/Config/Config.test.ts @@ -1,9 +1,9 @@ -import * as Schema from "@effect/schema/Schema" import type * as Config from "effect/Config" import * as ConfigError from "effect/ConfigError" import * as ConfigProvider from "effect/ConfigProvider" import * as Effect from "effect/Effect" import * as Exit from "effect/Exit" +import * as Schema from "effect/Schema" import { describe, expect, it } from "vitest" /** diff --git a/packages/schema/test/Schema/Data/Data.test.ts b/packages/effect/test/Schema/Schema/Data/Data.test.ts similarity index 91% rename from packages/schema/test/Schema/Data/Data.test.ts rename to packages/effect/test/Schema/Schema/Data/Data.test.ts index 345e92c0c4..2541336109 100644 --- a/packages/schema/test/Schema/Data/Data.test.ts +++ b/packages/effect/test/Schema/Schema/Data/Data.test.ts @@ -1,6 +1,6 @@ -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" import * as Data from "effect/Data" +import * as S from "effect/Schema" +import * as Util from "effect/test/Schema/TestUtils" import { describe, it } from "vitest" describe("Data", () => { diff --git a/packages/schema/test/Schema/Data/DataFromSelf.test.ts b/packages/effect/test/Schema/Schema/Data/DataFromSelf.test.ts similarity index 90% rename from packages/schema/test/Schema/Data/DataFromSelf.test.ts rename to packages/effect/test/Schema/Schema/Data/DataFromSelf.test.ts index bfc7164860..367a406a96 100644 --- a/packages/schema/test/Schema/Data/DataFromSelf.test.ts +++ b/packages/effect/test/Schema/Schema/Data/DataFromSelf.test.ts @@ -1,8 +1,8 @@ -import * as P from "@effect/schema/ParseResult" -import * as Pretty from "@effect/schema/Pretty" -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" import * as Data from "effect/Data" +import * as P from "effect/ParseResult" +import * as Pretty from "effect/Pretty" +import * as S from "effect/Schema" +import * as Util from "effect/test/Schema/TestUtils" import { describe, expect, it } from "vitest" describe("DataFromSelf", () => { diff --git a/packages/schema/test/Schema/Date/Date.test.ts b/packages/effect/test/Schema/Schema/Date/Date.test.ts similarity index 88% rename from packages/schema/test/Schema/Date/Date.test.ts rename to packages/effect/test/Schema/Schema/Date/Date.test.ts index d81e9bc997..e43619d0e1 100644 --- a/packages/schema/test/Schema/Date/Date.test.ts +++ b/packages/effect/test/Schema/Schema/Date/Date.test.ts @@ -1,5 +1,5 @@ -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" +import * as S from "effect/Schema" +import * as Util from "effect/test/Schema/TestUtils" import { describe, it } from "vitest" describe("Date", () => { diff --git a/packages/schema/test/Schema/Date/DateFromNumber.test.ts b/packages/effect/test/Schema/Schema/Date/DateFromNumber.test.ts similarity index 91% rename from packages/schema/test/Schema/Date/DateFromNumber.test.ts rename to packages/effect/test/Schema/Schema/Date/DateFromNumber.test.ts index b040f7e7d3..bd3a69b4eb 100644 --- a/packages/schema/test/Schema/Date/DateFromNumber.test.ts +++ b/packages/effect/test/Schema/Schema/Date/DateFromNumber.test.ts @@ -1,5 +1,5 @@ -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" +import * as S from "effect/Schema" +import * as Util from "effect/test/Schema/TestUtils" import { describe, expect, it } from "vitest" describe("DateFromNumber", () => { diff --git a/packages/schema/test/Schema/Date/DateFromSelf.test.ts b/packages/effect/test/Schema/Schema/Date/DateFromSelf.test.ts similarity index 85% rename from packages/schema/test/Schema/Date/DateFromSelf.test.ts rename to packages/effect/test/Schema/Schema/Date/DateFromSelf.test.ts index 26e1133f21..1f370f39c1 100644 --- a/packages/schema/test/Schema/Date/DateFromSelf.test.ts +++ b/packages/effect/test/Schema/Schema/Date/DateFromSelf.test.ts @@ -1,6 +1,6 @@ -import * as Pretty from "@effect/schema/Pretty" -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" +import * as Pretty from "effect/Pretty" +import * as S from "effect/Schema" +import * as Util from "effect/test/Schema/TestUtils" import { describe, expect, it } from "vitest" describe("DateFromSelf", () => { diff --git a/packages/schema/test/Schema/Date/betweenDate.test.ts b/packages/effect/test/Schema/Schema/Date/betweenDate.test.ts similarity index 91% rename from packages/schema/test/Schema/Date/betweenDate.test.ts rename to packages/effect/test/Schema/Schema/Date/betweenDate.test.ts index af988250d1..7a20928808 100644 --- a/packages/schema/test/Schema/Date/betweenDate.test.ts +++ b/packages/effect/test/Schema/Schema/Date/betweenDate.test.ts @@ -1,5 +1,5 @@ -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" +import * as S from "effect/Schema" +import * as Util from "effect/test/Schema/TestUtils" import { describe, it } from "vitest" describe("betweenDate", () => { diff --git a/packages/schema/test/Schema/Date/greaterThanDate.test.ts b/packages/effect/test/Schema/Schema/Date/greaterThanDate.test.ts similarity index 88% rename from packages/schema/test/Schema/Date/greaterThanDate.test.ts rename to packages/effect/test/Schema/Schema/Date/greaterThanDate.test.ts index a62ca4ec7f..27f350b576 100644 --- a/packages/schema/test/Schema/Date/greaterThanDate.test.ts +++ b/packages/effect/test/Schema/Schema/Date/greaterThanDate.test.ts @@ -1,5 +1,5 @@ -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" +import * as S from "effect/Schema" +import * as Util from "effect/test/Schema/TestUtils" import { describe, it } from "vitest" describe("greaterThanDate", () => { diff --git a/packages/schema/test/Schema/Date/greaterThanOrEqualToDate.test.ts b/packages/effect/test/Schema/Schema/Date/greaterThanOrEqualToDate.test.ts similarity index 86% rename from packages/schema/test/Schema/Date/greaterThanOrEqualToDate.test.ts rename to packages/effect/test/Schema/Schema/Date/greaterThanOrEqualToDate.test.ts index 28943ed5c7..3c6bee0783 100644 --- a/packages/schema/test/Schema/Date/greaterThanOrEqualToDate.test.ts +++ b/packages/effect/test/Schema/Schema/Date/greaterThanOrEqualToDate.test.ts @@ -1,5 +1,5 @@ -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" +import * as S from "effect/Schema" +import * as Util from "effect/test/Schema/TestUtils" import { describe, it } from "vitest" describe("greaterThanOrEqualToDate", () => { diff --git a/packages/schema/test/Schema/Date/lessThanDate.test.ts b/packages/effect/test/Schema/Schema/Date/lessThanDate.test.ts similarity index 88% rename from packages/schema/test/Schema/Date/lessThanDate.test.ts rename to packages/effect/test/Schema/Schema/Date/lessThanDate.test.ts index d7c20cc515..b346dcece9 100644 --- a/packages/schema/test/Schema/Date/lessThanDate.test.ts +++ b/packages/effect/test/Schema/Schema/Date/lessThanDate.test.ts @@ -1,5 +1,5 @@ -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" +import * as S from "effect/Schema" +import * as Util from "effect/test/Schema/TestUtils" import { describe, it } from "vitest" describe("lessThanDate", () => { diff --git a/packages/schema/test/Schema/Date/lessThanOrEqualToDate.test.ts b/packages/effect/test/Schema/Schema/Date/lessThanOrEqualToDate.test.ts similarity index 86% rename from packages/schema/test/Schema/Date/lessThanOrEqualToDate.test.ts rename to packages/effect/test/Schema/Schema/Date/lessThanOrEqualToDate.test.ts index 4e43756e62..c096f6a836 100644 --- a/packages/schema/test/Schema/Date/lessThanOrEqualToDate.test.ts +++ b/packages/effect/test/Schema/Schema/Date/lessThanOrEqualToDate.test.ts @@ -1,5 +1,5 @@ -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" +import * as S from "effect/Schema" +import * as Util from "effect/test/Schema/TestUtils" import { describe, it } from "vitest" describe("lessThanOrEqualToDate", () => { diff --git a/packages/schema/test/Schema/DateTime/DateTime.test.ts b/packages/effect/test/Schema/Schema/DateTime/DateTime.test.ts similarity index 94% rename from packages/schema/test/Schema/DateTime/DateTime.test.ts rename to packages/effect/test/Schema/Schema/DateTime/DateTime.test.ts index 75138c2d2b..71ea3f092d 100644 --- a/packages/schema/test/Schema/DateTime/DateTime.test.ts +++ b/packages/effect/test/Schema/Schema/DateTime/DateTime.test.ts @@ -1,6 +1,6 @@ -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" import { DateTime } from "effect" +import * as S from "effect/Schema" +import * as Util from "effect/test/Schema/TestUtils" import { describe, it } from "vitest" describe("DateTime.Utc", () => { diff --git a/packages/schema/test/Schema/DecodingFallbackAnnotation.test.ts b/packages/effect/test/Schema/Schema/DecodingFallbackAnnotation.test.ts similarity index 95% rename from packages/schema/test/Schema/DecodingFallbackAnnotation.test.ts rename to packages/effect/test/Schema/Schema/DecodingFallbackAnnotation.test.ts index d8e1938553..6beaebea7b 100644 --- a/packages/schema/test/Schema/DecodingFallbackAnnotation.test.ts +++ b/packages/effect/test/Schema/Schema/DecodingFallbackAnnotation.test.ts @@ -1,6 +1,6 @@ -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" import { Effect, Either } from "effect" +import * as S from "effect/Schema" +import * as Util from "effect/test/Schema/TestUtils" import { describe, expect, it } from "vitest" describe("DecodingFallbackAnnotation", () => { diff --git a/packages/schema/test/Schema/Defect/Defect.test.ts b/packages/effect/test/Schema/Schema/Defect/Defect.test.ts similarity index 94% rename from packages/schema/test/Schema/Defect/Defect.test.ts rename to packages/effect/test/Schema/Schema/Defect/Defect.test.ts index b4fa8f5573..e764af6e7b 100644 --- a/packages/schema/test/Schema/Defect/Defect.test.ts +++ b/packages/effect/test/Schema/Schema/Defect/Defect.test.ts @@ -1,5 +1,5 @@ -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" +import * as S from "effect/Schema" +import * as Util from "effect/test/Schema/TestUtils" import { describe, expect, it } from "vitest" describe("Defect", () => { diff --git a/packages/schema/test/Schema/Duration/Duration.test.ts b/packages/effect/test/Schema/Schema/Duration/Duration.test.ts similarity index 95% rename from packages/schema/test/Schema/Duration/Duration.test.ts rename to packages/effect/test/Schema/Schema/Duration/Duration.test.ts index f6d7d6087d..c5cecff6dd 100644 --- a/packages/schema/test/Schema/Duration/Duration.test.ts +++ b/packages/effect/test/Schema/Schema/Duration/Duration.test.ts @@ -1,6 +1,6 @@ -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" import { Duration } from "effect" +import * as S from "effect/Schema" +import * as Util from "effect/test/Schema/TestUtils" import { describe, it } from "vitest" describe("Duration", () => { diff --git a/packages/schema/test/Schema/Duration/DurationFromMillis.test.ts b/packages/effect/test/Schema/Schema/Duration/DurationFromMillis.test.ts similarity index 88% rename from packages/schema/test/Schema/Duration/DurationFromMillis.test.ts rename to packages/effect/test/Schema/Schema/Duration/DurationFromMillis.test.ts index d68eb56933..7528016aba 100644 --- a/packages/schema/test/Schema/Duration/DurationFromMillis.test.ts +++ b/packages/effect/test/Schema/Schema/Duration/DurationFromMillis.test.ts @@ -1,6 +1,6 @@ -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" import { Duration } from "effect" +import * as S from "effect/Schema" +import * as Util from "effect/test/Schema/TestUtils" import { describe, it } from "vitest" describe("DurationFromMillis", () => { diff --git a/packages/schema/test/Schema/Duration/DurationFromNanos.test.ts b/packages/effect/test/Schema/Schema/Duration/DurationFromNanos.test.ts similarity index 85% rename from packages/schema/test/Schema/Duration/DurationFromNanos.test.ts rename to packages/effect/test/Schema/Schema/Duration/DurationFromNanos.test.ts index b2fa196020..c0a7e7dc12 100644 --- a/packages/schema/test/Schema/Duration/DurationFromNanos.test.ts +++ b/packages/effect/test/Schema/Schema/Duration/DurationFromNanos.test.ts @@ -1,6 +1,6 @@ -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" import { Duration } from "effect" +import * as S from "effect/Schema" +import * as Util from "effect/test/Schema/TestUtils" import { describe, it } from "vitest" describe("DurationFromNanos", () => { diff --git a/packages/schema/test/Schema/Duration/DurationFromSelf.test.ts b/packages/effect/test/Schema/Schema/Duration/DurationFromSelf.test.ts similarity index 88% rename from packages/schema/test/Schema/Duration/DurationFromSelf.test.ts rename to packages/effect/test/Schema/Schema/Duration/DurationFromSelf.test.ts index e39fe41286..41501f82ff 100644 --- a/packages/schema/test/Schema/Duration/DurationFromSelf.test.ts +++ b/packages/effect/test/Schema/Schema/Duration/DurationFromSelf.test.ts @@ -1,7 +1,7 @@ -import * as Pretty from "@effect/schema/Pretty" -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" import { Duration } from "effect" +import * as Pretty from "effect/Pretty" +import * as S from "effect/Schema" +import * as Util from "effect/test/Schema/TestUtils" import { describe, expect, it } from "vitest" describe("DurationFromSelf", () => { diff --git a/packages/schema/test/Schema/Duration/betweenDuration.test.ts b/packages/effect/test/Schema/Schema/Duration/betweenDuration.test.ts similarity index 91% rename from packages/schema/test/Schema/Duration/betweenDuration.test.ts rename to packages/effect/test/Schema/Schema/Duration/betweenDuration.test.ts index 472c617aa8..93827985cc 100644 --- a/packages/schema/test/Schema/Duration/betweenDuration.test.ts +++ b/packages/effect/test/Schema/Schema/Duration/betweenDuration.test.ts @@ -1,6 +1,6 @@ -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" import { Duration } from "effect" +import * as S from "effect/Schema" +import * as Util from "effect/test/Schema/TestUtils" import { describe, it } from "vitest" describe("betweenDuration", () => { diff --git a/packages/schema/test/Schema/Duration/clampDuration.test.ts b/packages/effect/test/Schema/Schema/Duration/clampDuration.test.ts similarity index 86% rename from packages/schema/test/Schema/Duration/clampDuration.test.ts rename to packages/effect/test/Schema/Schema/Duration/clampDuration.test.ts index a87e2e034a..0fc3c87474 100644 --- a/packages/schema/test/Schema/Duration/clampDuration.test.ts +++ b/packages/effect/test/Schema/Schema/Duration/clampDuration.test.ts @@ -1,6 +1,6 @@ -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" import { Duration } from "effect" +import * as S from "effect/Schema" +import * as Util from "effect/test/Schema/TestUtils" import { describe, it } from "vitest" describe("clampDuration", () => { diff --git a/packages/schema/test/Schema/Duration/greaterThanDuration.test.ts b/packages/effect/test/Schema/Schema/Duration/greaterThanDuration.test.ts similarity index 91% rename from packages/schema/test/Schema/Duration/greaterThanDuration.test.ts rename to packages/effect/test/Schema/Schema/Duration/greaterThanDuration.test.ts index 81ec4844f5..170a08e8b0 100644 --- a/packages/schema/test/Schema/Duration/greaterThanDuration.test.ts +++ b/packages/effect/test/Schema/Schema/Duration/greaterThanDuration.test.ts @@ -1,6 +1,6 @@ -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" import { Duration } from "effect" +import * as S from "effect/Schema" +import * as Util from "effect/test/Schema/TestUtils" import { describe, it } from "vitest" describe("greaterThanDuration", () => { diff --git a/packages/schema/test/Schema/Duration/greaterThanOrEqualToDuration.test.ts b/packages/effect/test/Schema/Schema/Duration/greaterThanOrEqualToDuration.test.ts similarity index 90% rename from packages/schema/test/Schema/Duration/greaterThanOrEqualToDuration.test.ts rename to packages/effect/test/Schema/Schema/Duration/greaterThanOrEqualToDuration.test.ts index 3258c34afd..37ff0c4373 100644 --- a/packages/schema/test/Schema/Duration/greaterThanOrEqualToDuration.test.ts +++ b/packages/effect/test/Schema/Schema/Duration/greaterThanOrEqualToDuration.test.ts @@ -1,6 +1,6 @@ -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" import { Duration } from "effect" +import * as S from "effect/Schema" +import * as Util from "effect/test/Schema/TestUtils" import { describe, it } from "vitest" describe("greaterThanOrEqualToDuration", () => { diff --git a/packages/schema/test/Schema/Duration/lessThanDuration.test.ts b/packages/effect/test/Schema/Schema/Duration/lessThanDuration.test.ts similarity index 91% rename from packages/schema/test/Schema/Duration/lessThanDuration.test.ts rename to packages/effect/test/Schema/Schema/Duration/lessThanDuration.test.ts index b07825dd57..7b34399312 100644 --- a/packages/schema/test/Schema/Duration/lessThanDuration.test.ts +++ b/packages/effect/test/Schema/Schema/Duration/lessThanDuration.test.ts @@ -1,6 +1,6 @@ -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" import { Duration } from "effect" +import * as S from "effect/Schema" +import * as Util from "effect/test/Schema/TestUtils" import { describe, it } from "vitest" describe("lessThanDuration", () => { diff --git a/packages/schema/test/Schema/Duration/lessThanOrEqualToDuration.test.ts b/packages/effect/test/Schema/Schema/Duration/lessThanOrEqualToDuration.test.ts similarity index 90% rename from packages/schema/test/Schema/Duration/lessThanOrEqualToDuration.test.ts rename to packages/effect/test/Schema/Schema/Duration/lessThanOrEqualToDuration.test.ts index 7dcb1c75cc..2b26498e74 100644 --- a/packages/schema/test/Schema/Duration/lessThanOrEqualToDuration.test.ts +++ b/packages/effect/test/Schema/Schema/Duration/lessThanOrEqualToDuration.test.ts @@ -1,6 +1,6 @@ -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" import { Duration } from "effect" +import * as S from "effect/Schema" +import * as Util from "effect/test/Schema/TestUtils" import { describe, it } from "vitest" describe("lessThanOrEqualToDuration", () => { diff --git a/packages/schema/test/Schema/Either/Either.test.ts b/packages/effect/test/Schema/Schema/Either/Either.test.ts similarity index 89% rename from packages/schema/test/Schema/Either/Either.test.ts rename to packages/effect/test/Schema/Schema/Either/Either.test.ts index 69bff5ac50..a397e96b78 100644 --- a/packages/schema/test/Schema/Either/Either.test.ts +++ b/packages/effect/test/Schema/Schema/Either/Either.test.ts @@ -1,6 +1,6 @@ -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" import * as E from "effect/Either" +import * as S from "effect/Schema" +import * as Util from "effect/test/Schema/TestUtils" import { describe, it } from "vitest" describe("Either", () => { diff --git a/packages/schema/test/Schema/Either/EitherFromSelf.test.ts b/packages/effect/test/Schema/Schema/Either/EitherFromSelf.test.ts similarity index 91% rename from packages/schema/test/Schema/Either/EitherFromSelf.test.ts rename to packages/effect/test/Schema/Schema/Either/EitherFromSelf.test.ts index ca9e5b343b..885e61346a 100644 --- a/packages/schema/test/Schema/Either/EitherFromSelf.test.ts +++ b/packages/effect/test/Schema/Schema/Either/EitherFromSelf.test.ts @@ -1,8 +1,8 @@ -import * as P from "@effect/schema/ParseResult" -import * as Pretty from "@effect/schema/Pretty" -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" import * as E from "effect/Either" +import * as P from "effect/ParseResult" +import * as Pretty from "effect/Pretty" +import * as S from "effect/Schema" +import * as Util from "effect/test/Schema/TestUtils" import { describe, expect, it } from "vitest" describe("EitherFromSelf", () => { diff --git a/packages/schema/test/Schema/Either/EitherFromUnion.test.ts b/packages/effect/test/Schema/Schema/Either/EitherFromUnion.test.ts similarity index 98% rename from packages/schema/test/Schema/Either/EitherFromUnion.test.ts rename to packages/effect/test/Schema/Schema/Either/EitherFromUnion.test.ts index 8a36880520..1368c92e1c 100644 --- a/packages/schema/test/Schema/Either/EitherFromUnion.test.ts +++ b/packages/effect/test/Schema/Schema/Either/EitherFromUnion.test.ts @@ -1,6 +1,6 @@ -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" import * as E from "effect/Either" +import * as S from "effect/Schema" +import * as Util from "effect/test/Schema/TestUtils" import { describe, expect, it } from "vitest" describe("EitherFromUnion", () => { diff --git a/packages/schema/test/Schema/Enums/Enums.test.ts b/packages/effect/test/Schema/Schema/Enums/Enums.test.ts similarity index 96% rename from packages/schema/test/Schema/Enums/Enums.test.ts rename to packages/effect/test/Schema/Schema/Enums/Enums.test.ts index 3a4fc85300..c2abbda15c 100644 --- a/packages/schema/test/Schema/Enums/Enums.test.ts +++ b/packages/effect/test/Schema/Schema/Enums/Enums.test.ts @@ -1,5 +1,5 @@ -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" +import * as S from "effect/Schema" +import * as Util from "effect/test/Schema/TestUtils" import { describe, expect, it } from "vitest" describe("Enums", () => { diff --git a/packages/schema/test/Schema/Exit/Exit.test.ts b/packages/effect/test/Schema/Schema/Exit/Exit.test.ts similarity index 94% rename from packages/schema/test/Schema/Exit/Exit.test.ts rename to packages/effect/test/Schema/Schema/Exit/Exit.test.ts index 1d66b5d397..bcdd537a8c 100644 --- a/packages/schema/test/Schema/Exit/Exit.test.ts +++ b/packages/effect/test/Schema/Schema/Exit/Exit.test.ts @@ -1,6 +1,6 @@ -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" import { Exit } from "effect" +import * as S from "effect/Schema" +import * as Util from "effect/test/Schema/TestUtils" import { describe, it } from "vitest" describe("Exit", () => { diff --git a/packages/schema/test/Schema/Exit/ExitFromSelf.test.ts b/packages/effect/test/Schema/Schema/Exit/ExitFromSelf.test.ts similarity index 95% rename from packages/schema/test/Schema/Exit/ExitFromSelf.test.ts rename to packages/effect/test/Schema/Schema/Exit/ExitFromSelf.test.ts index 48487fc7be..b05e1a7463 100644 --- a/packages/schema/test/Schema/Exit/ExitFromSelf.test.ts +++ b/packages/effect/test/Schema/Schema/Exit/ExitFromSelf.test.ts @@ -1,6 +1,6 @@ -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" import * as Exit from "effect/Exit" +import * as S from "effect/Schema" +import * as Util from "effect/test/Schema/TestUtils" import { describe, it } from "vitest" describe("ExitFromSelf", () => { diff --git a/packages/schema/test/Schema/FiberId/FiberId.test.ts b/packages/effect/test/Schema/Schema/FiberId/FiberId.test.ts similarity index 92% rename from packages/schema/test/Schema/FiberId/FiberId.test.ts rename to packages/effect/test/Schema/Schema/FiberId/FiberId.test.ts index c9d22be199..1b70acf402 100644 --- a/packages/schema/test/Schema/FiberId/FiberId.test.ts +++ b/packages/effect/test/Schema/Schema/FiberId/FiberId.test.ts @@ -1,6 +1,6 @@ -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" import * as FiberId from "effect/FiberId" +import * as S from "effect/Schema" +import * as Util from "effect/test/Schema/TestUtils" import { describe, it } from "vitest" describe("FiberId", () => { diff --git a/packages/schema/test/Schema/FiberId/FiberIdFromSelf.test.ts b/packages/effect/test/Schema/Schema/FiberId/FiberIdFromSelf.test.ts similarity index 88% rename from packages/schema/test/Schema/FiberId/FiberIdFromSelf.test.ts rename to packages/effect/test/Schema/Schema/FiberId/FiberIdFromSelf.test.ts index 2627c9c228..286bd7e3eb 100644 --- a/packages/schema/test/Schema/FiberId/FiberIdFromSelf.test.ts +++ b/packages/effect/test/Schema/Schema/FiberId/FiberIdFromSelf.test.ts @@ -1,7 +1,7 @@ -import * as Pretty from "@effect/schema/Pretty" -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" import * as FiberId from "effect/FiberId" +import * as Pretty from "effect/Pretty" +import * as S from "effect/Schema" +import * as Util from "effect/test/Schema/TestUtils" import { describe, expect, it } from "vitest" describe("FiberIdFromSelf", () => { diff --git a/packages/schema/test/Schema/HashMap/HashMap.test.ts b/packages/effect/test/Schema/Schema/HashMap/HashMap.test.ts similarity index 94% rename from packages/schema/test/Schema/HashMap/HashMap.test.ts rename to packages/effect/test/Schema/Schema/HashMap/HashMap.test.ts index f4d70b85a9..ad64e3d176 100644 --- a/packages/schema/test/Schema/HashMap/HashMap.test.ts +++ b/packages/effect/test/Schema/Schema/HashMap/HashMap.test.ts @@ -1,6 +1,6 @@ -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" import * as HashMap from "effect/HashMap" +import * as S from "effect/Schema" +import * as Util from "effect/test/Schema/TestUtils" import { describe, it } from "vitest" describe("HashMap", () => { diff --git a/packages/schema/test/Schema/HashMap/HashMapFromSelf.test.ts b/packages/effect/test/Schema/Schema/HashMap/HashMapFromSelf.test.ts similarity index 93% rename from packages/schema/test/Schema/HashMap/HashMapFromSelf.test.ts rename to packages/effect/test/Schema/Schema/HashMap/HashMapFromSelf.test.ts index 6637f636a9..aba34425e1 100644 --- a/packages/schema/test/Schema/HashMap/HashMapFromSelf.test.ts +++ b/packages/effect/test/Schema/Schema/HashMap/HashMapFromSelf.test.ts @@ -1,8 +1,8 @@ -import * as P from "@effect/schema/ParseResult" -import * as Pretty from "@effect/schema/Pretty" -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" import * as HashMap from "effect/HashMap" +import * as P from "effect/ParseResult" +import * as Pretty from "effect/Pretty" +import * as S from "effect/Schema" +import * as Util from "effect/test/Schema/TestUtils" import { describe, expect, it } from "vitest" describe("HashMapFromSelf", () => { diff --git a/packages/schema/test/Schema/HashSet/HashSet.test.ts b/packages/effect/test/Schema/Schema/HashSet/HashSet.test.ts similarity index 92% rename from packages/schema/test/Schema/HashSet/HashSet.test.ts rename to packages/effect/test/Schema/Schema/HashSet/HashSet.test.ts index b1516c7fb3..2561b30ae8 100644 --- a/packages/schema/test/Schema/HashSet/HashSet.test.ts +++ b/packages/effect/test/Schema/Schema/HashSet/HashSet.test.ts @@ -1,6 +1,6 @@ -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" import * as HashSet from "effect/HashSet" +import * as S from "effect/Schema" +import * as Util from "effect/test/Schema/TestUtils" import { describe, it } from "vitest" describe("HashSet", () => { diff --git a/packages/schema/test/Schema/HashSet/HashSetFromSelf.test.ts b/packages/effect/test/Schema/Schema/HashSet/HashSetFromSelf.test.ts similarity index 87% rename from packages/schema/test/Schema/HashSet/HashSetFromSelf.test.ts rename to packages/effect/test/Schema/Schema/HashSet/HashSetFromSelf.test.ts index b12d5311d4..82f41b9fac 100644 --- a/packages/schema/test/Schema/HashSet/HashSetFromSelf.test.ts +++ b/packages/effect/test/Schema/Schema/HashSet/HashSetFromSelf.test.ts @@ -1,8 +1,8 @@ -import * as P from "@effect/schema/ParseResult" -import * as Pretty from "@effect/schema/Pretty" -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" import * as HashSet from "effect/HashSet" +import * as P from "effect/ParseResult" +import * as Pretty from "effect/Pretty" +import * as S from "effect/Schema" +import * as Util from "effect/test/Schema/TestUtils" import { describe, expect, it } from "vitest" describe("HashSetFromSelf", () => { @@ -53,7 +53,7 @@ describe("HashSetFromSelf", () => { expect(is(HashSet.fromIterable(["a", "b", "c"]))).toEqual(true) expect(is(HashSet.fromIterable(["a", "b", 1]))).toEqual(false) - expect(is({ _id: Symbol.for("@effect/schema/test/FakeHashSet") })).toEqual(false) + expect(is({ _id: Symbol.for("effect/Schema/test/FakeHashSet") })).toEqual(false) }) it("pretty", () => { diff --git a/packages/schema/test/Schema/List/List.test.ts b/packages/effect/test/Schema/Schema/List/List.test.ts similarity index 91% rename from packages/schema/test/Schema/List/List.test.ts rename to packages/effect/test/Schema/Schema/List/List.test.ts index 6b69a1640e..b0555fa150 100644 --- a/packages/schema/test/Schema/List/List.test.ts +++ b/packages/effect/test/Schema/Schema/List/List.test.ts @@ -1,6 +1,6 @@ -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" import * as List from "effect/List" +import * as S from "effect/Schema" +import * as Util from "effect/test/Schema/TestUtils" import { describe, it } from "vitest" describe("List", () => { diff --git a/packages/schema/test/Schema/List/ListFromSelf.test.ts b/packages/effect/test/Schema/Schema/List/ListFromSelf.test.ts similarity index 86% rename from packages/schema/test/Schema/List/ListFromSelf.test.ts rename to packages/effect/test/Schema/Schema/List/ListFromSelf.test.ts index 55deb9107b..08ee65bcf4 100644 --- a/packages/schema/test/Schema/List/ListFromSelf.test.ts +++ b/packages/effect/test/Schema/Schema/List/ListFromSelf.test.ts @@ -1,8 +1,8 @@ -import * as P from "@effect/schema/ParseResult" -import * as Pretty from "@effect/schema/Pretty" -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" import * as List from "effect/List" +import * as P from "effect/ParseResult" +import * as Pretty from "effect/Pretty" +import * as S from "effect/Schema" +import * as Util from "effect/test/Schema/TestUtils" import { describe, expect, it } from "vitest" describe("ListFromSelf", () => { @@ -53,7 +53,7 @@ describe("ListFromSelf", () => { expect(is(List.fromIterable(["a", "b", "c"]))).toEqual(true) expect(is(List.fromIterable(["a", "b", 1]))).toEqual(false) - expect(is({ _id: Symbol.for("@effect/schema/test/FakeList") })).toEqual(false) + expect(is({ _id: Symbol.for("effect/Schema/test/FakeList") })).toEqual(false) }) it("pretty", () => { diff --git a/packages/schema/test/Schema/Literal/Literal.test.ts b/packages/effect/test/Schema/Schema/Literal/Literal.test.ts similarity index 93% rename from packages/schema/test/Schema/Literal/Literal.test.ts rename to packages/effect/test/Schema/Schema/Literal/Literal.test.ts index e619b4aac6..5f6f39e1be 100644 --- a/packages/schema/test/Schema/Literal/Literal.test.ts +++ b/packages/effect/test/Schema/Schema/Literal/Literal.test.ts @@ -1,6 +1,6 @@ -import * as AST from "@effect/schema/AST" -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" +import * as S from "effect/Schema" +import * as AST from "effect/SchemaAST" +import * as Util from "effect/test/Schema/TestUtils" import { describe, expect, it } from "vitest" describe("Literal", () => { diff --git a/packages/schema/test/Schema/Map/Map.test.ts b/packages/effect/test/Schema/Schema/Map/Map.test.ts similarity index 86% rename from packages/schema/test/Schema/Map/Map.test.ts rename to packages/effect/test/Schema/Schema/Map/Map.test.ts index 4c740103a4..3b2096c5de 100644 --- a/packages/schema/test/Schema/Map/Map.test.ts +++ b/packages/effect/test/Schema/Schema/Map/Map.test.ts @@ -1,4 +1,4 @@ -import * as S from "@effect/schema/Schema" +import * as S from "effect/Schema" import { describe, expect, it } from "vitest" describe("Map", () => { diff --git a/packages/schema/test/Schema/Map/MapFromRecord.test.ts b/packages/effect/test/Schema/Schema/Map/MapFromRecord.test.ts similarity index 95% rename from packages/schema/test/Schema/Map/MapFromRecord.test.ts rename to packages/effect/test/Schema/Schema/Map/MapFromRecord.test.ts index 03d275951e..d1caa17bc5 100644 --- a/packages/schema/test/Schema/Map/MapFromRecord.test.ts +++ b/packages/effect/test/Schema/Schema/Map/MapFromRecord.test.ts @@ -1,5 +1,5 @@ -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" +import * as S from "effect/Schema" +import * as Util from "effect/test/Schema/TestUtils" import { describe, it } from "vitest" describe("MapFromRecord", () => { diff --git a/packages/schema/test/Schema/Map/MapFromSelf.test.ts b/packages/effect/test/Schema/Schema/Map/MapFromSelf.test.ts similarity index 83% rename from packages/schema/test/Schema/Map/MapFromSelf.test.ts rename to packages/effect/test/Schema/Schema/Map/MapFromSelf.test.ts index ef2b5be81f..0239d6850e 100644 --- a/packages/schema/test/Schema/Map/MapFromSelf.test.ts +++ b/packages/effect/test/Schema/Schema/Map/MapFromSelf.test.ts @@ -1,4 +1,4 @@ -import * as S from "@effect/schema/Schema" +import * as S from "effect/Schema" import { describe, expect, it } from "vitest" describe("MapFromSelf", () => { diff --git a/packages/schema/test/Schema/Never/Never.test.ts b/packages/effect/test/Schema/Schema/Never/Never.test.ts similarity index 77% rename from packages/schema/test/Schema/Never/Never.test.ts rename to packages/effect/test/Schema/Schema/Never/Never.test.ts index 4dbc09519f..b78555b6a2 100644 --- a/packages/schema/test/Schema/Never/Never.test.ts +++ b/packages/effect/test/Schema/Schema/Never/Never.test.ts @@ -1,5 +1,5 @@ -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" +import * as S from "effect/Schema" +import * as Util from "effect/test/Schema/TestUtils" import { describe, it } from "vitest" describe("Never", () => { diff --git a/packages/schema/test/Schema/NonEmptyArrayEnsure.test.ts b/packages/effect/test/Schema/Schema/NonEmptyArrayEnsure.test.ts similarity index 97% rename from packages/schema/test/Schema/NonEmptyArrayEnsure.test.ts rename to packages/effect/test/Schema/Schema/NonEmptyArrayEnsure.test.ts index 24d66fecdc..7eb4955840 100644 --- a/packages/schema/test/Schema/NonEmptyArrayEnsure.test.ts +++ b/packages/effect/test/Schema/Schema/NonEmptyArrayEnsure.test.ts @@ -1,5 +1,5 @@ -import * as AST from "@effect/schema/AST" -import * as S from "@effect/schema/Schema" +import * as S from "effect/Schema" +import * as AST from "effect/SchemaAST" import { describe, expect, it } from "vitest" import { expectDecodeUnknownFailure, expectDecodeUnknownSuccess, expectEncodeSuccess } from "../TestUtils.js" diff --git a/packages/schema/test/Schema/Number/JsonNumber.test.ts b/packages/effect/test/Schema/Schema/Number/JsonNumber.test.ts similarity index 93% rename from packages/schema/test/Schema/Number/JsonNumber.test.ts rename to packages/effect/test/Schema/Schema/Number/JsonNumber.test.ts index 6d3e10017e..c4574a39da 100644 --- a/packages/schema/test/Schema/Number/JsonNumber.test.ts +++ b/packages/effect/test/Schema/Schema/Number/JsonNumber.test.ts @@ -1,5 +1,5 @@ -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" +import * as S from "effect/Schema" +import * as Util from "effect/test/Schema/TestUtils" import { describe, it } from "vitest" describe("JsonNumber", () => { diff --git a/packages/schema/test/Schema/Number/Number.test.ts b/packages/effect/test/Schema/Schema/Number/Number.test.ts similarity index 85% rename from packages/schema/test/Schema/Number/Number.test.ts rename to packages/effect/test/Schema/Schema/Number/Number.test.ts index bb5faaccb3..79898fe2cf 100644 --- a/packages/schema/test/Schema/Number/Number.test.ts +++ b/packages/effect/test/Schema/Schema/Number/Number.test.ts @@ -1,5 +1,5 @@ -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" +import * as S from "effect/Schema" +import * as Util from "effect/test/Schema/TestUtils" import { describe, it } from "vitest" describe("Number", () => { diff --git a/packages/schema/test/Schema/Number/between.test.ts b/packages/effect/test/Schema/Schema/Number/between.test.ts similarity index 87% rename from packages/schema/test/Schema/Number/between.test.ts rename to packages/effect/test/Schema/Schema/Number/between.test.ts index e210e73dff..376e38a372 100644 --- a/packages/schema/test/Schema/Number/between.test.ts +++ b/packages/effect/test/Schema/Schema/Number/between.test.ts @@ -1,5 +1,5 @@ -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" +import * as S from "effect/Schema" +import * as Util from "effect/test/Schema/TestUtils" import { describe, it } from "vitest" describe("between", () => { diff --git a/packages/schema/test/Schema/Number/clamp.test.ts b/packages/effect/test/Schema/Schema/Number/clamp.test.ts similarity index 86% rename from packages/schema/test/Schema/Number/clamp.test.ts rename to packages/effect/test/Schema/Schema/Number/clamp.test.ts index 4add28070b..7f1f7d52da 100644 --- a/packages/schema/test/Schema/Number/clamp.test.ts +++ b/packages/effect/test/Schema/Schema/Number/clamp.test.ts @@ -1,5 +1,5 @@ -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" +import * as S from "effect/Schema" +import * as Util from "effect/test/Schema/TestUtils" import { describe, it } from "vitest" describe("clamp", () => { diff --git a/packages/schema/test/Schema/Number/finite.test.ts b/packages/effect/test/Schema/Schema/Number/finite.test.ts similarity index 86% rename from packages/schema/test/Schema/Number/finite.test.ts rename to packages/effect/test/Schema/Schema/Number/finite.test.ts index 08521b07dc..433646d3f4 100644 --- a/packages/schema/test/Schema/Number/finite.test.ts +++ b/packages/effect/test/Schema/Schema/Number/finite.test.ts @@ -1,5 +1,5 @@ -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" +import * as S from "effect/Schema" +import * as Util from "effect/test/Schema/TestUtils" import { describe, it } from "vitest" describe("Finite", () => { diff --git a/packages/schema/test/Schema/Number/greaterThan.test.ts b/packages/effect/test/Schema/Schema/Number/greaterThan.test.ts similarity index 82% rename from packages/schema/test/Schema/Number/greaterThan.test.ts rename to packages/effect/test/Schema/Schema/Number/greaterThan.test.ts index 62013e503c..a403e7f2b3 100644 --- a/packages/schema/test/Schema/Number/greaterThan.test.ts +++ b/packages/effect/test/Schema/Schema/Number/greaterThan.test.ts @@ -1,7 +1,7 @@ -import * as P from "@effect/schema/ParseResult" -import * as Pretty from "@effect/schema/Pretty" -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" +import * as P from "effect/ParseResult" +import * as Pretty from "effect/Pretty" +import * as S from "effect/Schema" +import * as Util from "effect/test/Schema/TestUtils" import { describe, expect, it } from "vitest" describe("greaterThan", () => { diff --git a/packages/schema/test/Schema/Number/greaterThanOrEqualTo.test.ts b/packages/effect/test/Schema/Schema/Number/greaterThanOrEqualTo.test.ts similarity index 80% rename from packages/schema/test/Schema/Number/greaterThanOrEqualTo.test.ts rename to packages/effect/test/Schema/Schema/Number/greaterThanOrEqualTo.test.ts index 9684c87e71..b4a3af9351 100644 --- a/packages/schema/test/Schema/Number/greaterThanOrEqualTo.test.ts +++ b/packages/effect/test/Schema/Schema/Number/greaterThanOrEqualTo.test.ts @@ -1,7 +1,7 @@ -import * as P from "@effect/schema/ParseResult" -import * as Pretty from "@effect/schema/Pretty" -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" +import * as P from "effect/ParseResult" +import * as Pretty from "effect/Pretty" +import * as S from "effect/Schema" +import * as Util from "effect/test/Schema/TestUtils" import { describe, expect, it } from "vitest" describe("greaterThanOrEqualTo", () => { diff --git a/packages/schema/test/Schema/Number/int.test.ts b/packages/effect/test/Schema/Schema/Number/int.test.ts similarity index 81% rename from packages/schema/test/Schema/Number/int.test.ts rename to packages/effect/test/Schema/Schema/Number/int.test.ts index 69e3376287..d74bd8d517 100644 --- a/packages/schema/test/Schema/Number/int.test.ts +++ b/packages/effect/test/Schema/Schema/Number/int.test.ts @@ -1,7 +1,7 @@ -import * as P from "@effect/schema/ParseResult" -import * as Pretty from "@effect/schema/Pretty" -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" +import * as P from "effect/ParseResult" +import * as Pretty from "effect/Pretty" +import * as S from "effect/Schema" +import * as Util from "effect/test/Schema/TestUtils" import { describe, expect, it } from "vitest" describe("Int", () => { diff --git a/packages/schema/test/Schema/Number/lessThan.test.ts b/packages/effect/test/Schema/Schema/Number/lessThan.test.ts similarity index 83% rename from packages/schema/test/Schema/Number/lessThan.test.ts rename to packages/effect/test/Schema/Schema/Number/lessThan.test.ts index 42617cc16c..b95806444f 100644 --- a/packages/schema/test/Schema/Number/lessThan.test.ts +++ b/packages/effect/test/Schema/Schema/Number/lessThan.test.ts @@ -1,7 +1,7 @@ -import * as P from "@effect/schema/ParseResult" -import * as Pretty from "@effect/schema/Pretty" -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" +import * as P from "effect/ParseResult" +import * as Pretty from "effect/Pretty" +import * as S from "effect/Schema" +import * as Util from "effect/test/Schema/TestUtils" import { describe, expect, it } from "vitest" describe("lessThan", () => { diff --git a/packages/schema/test/Schema/Number/lessThanOrEqualTo.test.ts b/packages/effect/test/Schema/Schema/Number/lessThanOrEqualTo.test.ts similarity index 81% rename from packages/schema/test/Schema/Number/lessThanOrEqualTo.test.ts rename to packages/effect/test/Schema/Schema/Number/lessThanOrEqualTo.test.ts index 77cb03ba3d..1b0d1547a0 100644 --- a/packages/schema/test/Schema/Number/lessThanOrEqualTo.test.ts +++ b/packages/effect/test/Schema/Schema/Number/lessThanOrEqualTo.test.ts @@ -1,7 +1,7 @@ -import * as P from "@effect/schema/ParseResult" -import * as Pretty from "@effect/schema/Pretty" -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" +import * as P from "effect/ParseResult" +import * as Pretty from "effect/Pretty" +import * as S from "effect/Schema" +import * as Util from "effect/test/Schema/TestUtils" import { describe, expect, it } from "vitest" describe("lessThanOrEqualTo", () => { diff --git a/packages/schema/test/Schema/Number/multipleOf.test.ts b/packages/effect/test/Schema/Schema/Number/multipleOf.test.ts similarity index 89% rename from packages/schema/test/Schema/Number/multipleOf.test.ts rename to packages/effect/test/Schema/Schema/Number/multipleOf.test.ts index 8ca97c441f..e88e91d050 100644 --- a/packages/schema/test/Schema/Number/multipleOf.test.ts +++ b/packages/effect/test/Schema/Schema/Number/multipleOf.test.ts @@ -1,6 +1,6 @@ -import * as P from "@effect/schema/ParseResult" -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" +import * as P from "effect/ParseResult" +import * as S from "effect/Schema" +import * as Util from "effect/test/Schema/TestUtils" import { describe, expect, it } from "vitest" describe("multipleOf", () => { diff --git a/packages/schema/test/Schema/Number/negative.test.ts b/packages/effect/test/Schema/Schema/Number/negative.test.ts similarity index 84% rename from packages/schema/test/Schema/Number/negative.test.ts rename to packages/effect/test/Schema/Schema/Number/negative.test.ts index 22c8a3e52d..daf55316a8 100644 --- a/packages/schema/test/Schema/Number/negative.test.ts +++ b/packages/effect/test/Schema/Schema/Number/negative.test.ts @@ -1,5 +1,5 @@ -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" +import * as S from "effect/Schema" +import * as Util from "effect/test/Schema/TestUtils" import { describe, it } from "vitest" describe("Negative", () => { diff --git a/packages/schema/test/Schema/Number/nonNaN.test.ts b/packages/effect/test/Schema/Schema/Number/nonNaN.test.ts similarity index 77% rename from packages/schema/test/Schema/Number/nonNaN.test.ts rename to packages/effect/test/Schema/Schema/Number/nonNaN.test.ts index 80a257de30..b6cac9ba8f 100644 --- a/packages/schema/test/Schema/Number/nonNaN.test.ts +++ b/packages/effect/test/Schema/Schema/Number/nonNaN.test.ts @@ -1,7 +1,7 @@ -import * as P from "@effect/schema/ParseResult" -import * as Pretty from "@effect/schema/Pretty" -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" +import * as P from "effect/ParseResult" +import * as Pretty from "effect/Pretty" +import * as S from "effect/Schema" +import * as Util from "effect/test/Schema/TestUtils" import { describe, expect, it } from "vitest" describe("NonNaN", () => { diff --git a/packages/schema/test/Schema/Number/nonNegative.test.ts b/packages/effect/test/Schema/Schema/Number/nonNegative.test.ts similarity index 82% rename from packages/schema/test/Schema/Number/nonNegative.test.ts rename to packages/effect/test/Schema/Schema/Number/nonNegative.test.ts index 4c15616193..1e72ae9208 100644 --- a/packages/schema/test/Schema/Number/nonNegative.test.ts +++ b/packages/effect/test/Schema/Schema/Number/nonNegative.test.ts @@ -1,5 +1,5 @@ -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" +import * as S from "effect/Schema" +import * as Util from "effect/test/Schema/TestUtils" import { describe, it } from "vitest" describe("NonNegative", () => { diff --git a/packages/schema/test/Schema/Number/nonPositive.test.ts b/packages/effect/test/Schema/Schema/Number/nonPositive.test.ts similarity index 82% rename from packages/schema/test/Schema/Number/nonPositive.test.ts rename to packages/effect/test/Schema/Schema/Number/nonPositive.test.ts index 54cdb42953..e1a2f996e3 100644 --- a/packages/schema/test/Schema/Number/nonPositive.test.ts +++ b/packages/effect/test/Schema/Schema/Number/nonPositive.test.ts @@ -1,5 +1,5 @@ -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" +import * as S from "effect/Schema" +import * as Util from "effect/test/Schema/TestUtils" import { describe, it } from "vitest" describe("NonPositive", () => { diff --git a/packages/schema/test/Schema/Number/numberFromString.test.ts b/packages/effect/test/Schema/Schema/Number/numberFromString.test.ts similarity index 95% rename from packages/schema/test/Schema/Number/numberFromString.test.ts rename to packages/effect/test/Schema/Schema/Number/numberFromString.test.ts index f72b9c344b..d729a133fd 100644 --- a/packages/schema/test/Schema/Number/numberFromString.test.ts +++ b/packages/effect/test/Schema/Schema/Number/numberFromString.test.ts @@ -1,5 +1,5 @@ -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" +import * as S from "effect/Schema" +import * as Util from "effect/test/Schema/TestUtils" import { describe, it } from "vitest" describe("NumberFromString", () => { diff --git a/packages/schema/test/Schema/Number/positive.test.ts b/packages/effect/test/Schema/Schema/Number/positive.test.ts similarity index 84% rename from packages/schema/test/Schema/Number/positive.test.ts rename to packages/effect/test/Schema/Schema/Number/positive.test.ts index 09e6dc8cd7..2dc468cadb 100644 --- a/packages/schema/test/Schema/Number/positive.test.ts +++ b/packages/effect/test/Schema/Schema/Number/positive.test.ts @@ -1,5 +1,5 @@ -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" +import * as S from "effect/Schema" +import * as Util from "effect/test/Schema/TestUtils" import { describe, it } from "vitest" describe("Positive", () => { diff --git a/packages/schema/test/Schema/Object/Object.test.ts b/packages/effect/test/Schema/Schema/Object/Object.test.ts similarity index 90% rename from packages/schema/test/Schema/Object/Object.test.ts rename to packages/effect/test/Schema/Schema/Object/Object.test.ts index 6c5ac3cce7..562cd4218c 100644 --- a/packages/schema/test/Schema/Object/Object.test.ts +++ b/packages/effect/test/Schema/Schema/Object/Object.test.ts @@ -1,5 +1,5 @@ -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" +import * as S from "effect/Schema" +import * as Util from "effect/test/Schema/TestUtils" import { describe, it } from "vitest" describe("object", () => { diff --git a/packages/schema/test/Schema/Option/Option.test.ts b/packages/effect/test/Schema/Schema/Option/Option.test.ts similarity index 87% rename from packages/schema/test/Schema/Option/Option.test.ts rename to packages/effect/test/Schema/Schema/Option/Option.test.ts index a23a0683b6..78f1997495 100644 --- a/packages/schema/test/Schema/Option/Option.test.ts +++ b/packages/effect/test/Schema/Schema/Option/Option.test.ts @@ -1,6 +1,6 @@ -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" import * as O from "effect/Option" +import * as S from "effect/Schema" +import * as Util from "effect/test/Schema/TestUtils" import { describe, it } from "vitest" describe("Option", () => { diff --git a/packages/schema/test/Schema/Option/OptionFromNonEmptyTrimmedString.test.ts b/packages/effect/test/Schema/Schema/Option/OptionFromNonEmptyTrimmedString.test.ts similarity index 93% rename from packages/schema/test/Schema/Option/OptionFromNonEmptyTrimmedString.test.ts rename to packages/effect/test/Schema/Schema/Option/OptionFromNonEmptyTrimmedString.test.ts index 25a9339725..f372a66d75 100644 --- a/packages/schema/test/Schema/Option/OptionFromNonEmptyTrimmedString.test.ts +++ b/packages/effect/test/Schema/Schema/Option/OptionFromNonEmptyTrimmedString.test.ts @@ -1,6 +1,6 @@ -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" import * as O from "effect/Option" +import * as S from "effect/Schema" +import * as Util from "effect/test/Schema/TestUtils" import { describe, it } from "vitest" describe("OptionFromNonEmptyTrimmedString", () => { diff --git a/packages/schema/test/Schema/Option/OptionFromNullOr.test.ts b/packages/effect/test/Schema/Schema/Option/OptionFromNullOr.test.ts similarity index 94% rename from packages/schema/test/Schema/Option/OptionFromNullOr.test.ts rename to packages/effect/test/Schema/Schema/Option/OptionFromNullOr.test.ts index d03ed295b8..ce58a65d06 100644 --- a/packages/schema/test/Schema/Option/OptionFromNullOr.test.ts +++ b/packages/effect/test/Schema/Schema/Option/OptionFromNullOr.test.ts @@ -1,6 +1,6 @@ -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" import * as O from "effect/Option" +import * as S from "effect/Schema" +import * as Util from "effect/test/Schema/TestUtils" import { describe, expect, it } from "vitest" describe("OptionFromNullOr", () => { diff --git a/packages/schema/test/Schema/Option/OptionFromNullishOr.test.ts b/packages/effect/test/Schema/Schema/Option/OptionFromNullishOr.test.ts similarity index 94% rename from packages/schema/test/Schema/Option/OptionFromNullishOr.test.ts rename to packages/effect/test/Schema/Schema/Option/OptionFromNullishOr.test.ts index e1bf9c2e40..eedb0550b5 100644 --- a/packages/schema/test/Schema/Option/OptionFromNullishOr.test.ts +++ b/packages/effect/test/Schema/Schema/Option/OptionFromNullishOr.test.ts @@ -1,6 +1,6 @@ -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" import * as O from "effect/Option" +import * as S from "effect/Schema" +import * as Util from "effect/test/Schema/TestUtils" import { describe, expect, it } from "vitest" describe("OptionFromNullishOr", () => { diff --git a/packages/schema/test/Schema/Option/OptionFromSelf.test.ts b/packages/effect/test/Schema/Schema/Option/OptionFromSelf.test.ts similarity index 86% rename from packages/schema/test/Schema/Option/OptionFromSelf.test.ts rename to packages/effect/test/Schema/Schema/Option/OptionFromSelf.test.ts index 207eced194..5b3c023a81 100644 --- a/packages/schema/test/Schema/Option/OptionFromSelf.test.ts +++ b/packages/effect/test/Schema/Schema/Option/OptionFromSelf.test.ts @@ -1,8 +1,8 @@ -import * as P from "@effect/schema/ParseResult" -import * as Pretty from "@effect/schema/Pretty" -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" import * as O from "effect/Option" +import * as P from "effect/ParseResult" +import * as Pretty from "effect/Pretty" +import * as S from "effect/Schema" +import * as Util from "effect/test/Schema/TestUtils" import { describe, expect, it } from "vitest" describe("OptionFromSelf", () => { diff --git a/packages/schema/test/Schema/Option/OptionFromUndefinedOr.test.ts b/packages/effect/test/Schema/Schema/Option/OptionFromUndefinedOr.test.ts similarity index 94% rename from packages/schema/test/Schema/Option/OptionFromUndefinedOr.test.ts rename to packages/effect/test/Schema/Schema/Option/OptionFromUndefinedOr.test.ts index 0bbd44d46f..88f2516a98 100644 --- a/packages/schema/test/Schema/Option/OptionFromUndefinedOr.test.ts +++ b/packages/effect/test/Schema/Schema/Option/OptionFromUndefinedOr.test.ts @@ -1,6 +1,6 @@ -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" import * as O from "effect/Option" +import * as S from "effect/Schema" +import * as Util from "effect/test/Schema/TestUtils" import { describe, expect, it } from "vitest" describe("OptionFromUndefinedOr", () => { diff --git a/packages/schema/test/Schema/ParseOptions-errors.test.ts b/packages/effect/test/Schema/Schema/ParseOptions-errors.test.ts similarity index 98% rename from packages/schema/test/Schema/ParseOptions-errors.test.ts rename to packages/effect/test/Schema/Schema/ParseOptions-errors.test.ts index c869ee8919..797daed627 100644 --- a/packages/schema/test/Schema/ParseOptions-errors.test.ts +++ b/packages/effect/test/Schema/Schema/ParseOptions-errors.test.ts @@ -1,5 +1,5 @@ -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" +import * as S from "effect/Schema" +import * as Util from "effect/test/Schema/TestUtils" import { describe, it } from "vitest" describe("`errors` option", () => { diff --git a/packages/schema/test/Schema/ParseOptions-exact.test.ts b/packages/effect/test/Schema/Schema/ParseOptions-exact.test.ts similarity index 92% rename from packages/schema/test/Schema/ParseOptions-exact.test.ts rename to packages/effect/test/Schema/Schema/ParseOptions-exact.test.ts index 9fcc7c8ff6..45d65e7408 100644 --- a/packages/schema/test/Schema/ParseOptions-exact.test.ts +++ b/packages/effect/test/Schema/Schema/ParseOptions-exact.test.ts @@ -1,5 +1,5 @@ -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" +import * as S from "effect/Schema" +import * as Util from "effect/test/Schema/TestUtils" import { describe, expect, it } from "vitest" describe("`exact` option", () => { diff --git a/packages/schema/test/Schema/ParseOptions-onExcessProperty.test.ts b/packages/effect/test/Schema/Schema/ParseOptions-onExcessProperty.test.ts similarity index 94% rename from packages/schema/test/Schema/ParseOptions-onExcessProperty.test.ts rename to packages/effect/test/Schema/Schema/ParseOptions-onExcessProperty.test.ts index ecc4a6beef..e71c714fa4 100644 --- a/packages/schema/test/Schema/ParseOptions-onExcessProperty.test.ts +++ b/packages/effect/test/Schema/Schema/ParseOptions-onExcessProperty.test.ts @@ -1,7 +1,7 @@ -import * as ParseResult from "@effect/schema/ParseResult" -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" import * as Either from "effect/Either" +import * as ParseResult from "effect/ParseResult" +import * as S from "effect/Schema" +import * as Util from "effect/test/Schema/TestUtils" import { describe, expect, it } from "vitest" describe("`onExcessProperty` option", () => { @@ -186,35 +186,35 @@ describe("`onExcessProperty` option", () => { }) it("struct with string excess keys", async () => { - const c = Symbol.for("@effect/schema/test/c") + const c = Symbol.for("effect/Schema/test/c") const schema = S.Struct({ a: S.String }) const input = { a: "a", b: 1, [c]: true } await Util.expectDecodeUnknownSuccess(schema, input, input, Util.onExcessPropertyPreserve) }) it("struct with symbol excess keys", async () => { - const c = Symbol.for("@effect/schema/test/c") + const c = Symbol.for("effect/Schema/test/c") const schema = S.Struct({ [c]: S.Boolean }) const input = { a: "a", b: 1, [c]: true } await Util.expectDecodeUnknownSuccess(schema, input, input, Util.onExcessPropertyPreserve) }) it("struct with both string and symbol excess keys", async () => { - const c = Symbol.for("@effect/schema/test/c") + const c = Symbol.for("effect/Schema/test/c") const schema = S.Struct({ a: S.String, [c]: S.Boolean }) const input = { a: "a", b: 1, [c]: true } await Util.expectDecodeUnknownSuccess(schema, input, input, Util.onExcessPropertyPreserve) }) it("record(string, string)", async () => { - const c = Symbol.for("@effect/schema/test/c") + const c = Symbol.for("effect/Schema/test/c") const schema = S.Struct({ a: S.String }) const input = { a: "a", [c]: true } await Util.expectDecodeUnknownSuccess(schema, input, input, Util.onExcessPropertyPreserve) }) it("record(symbol, boolean)", async () => { - const c = Symbol.for("@effect/schema/test/c") + const c = Symbol.for("effect/Schema/test/c") const schema = S.Struct({ [c]: S.Boolean }) const input = { a: "a", [c]: true } await Util.expectDecodeUnknownSuccess(schema, input, input, Util.onExcessPropertyPreserve) diff --git a/packages/schema/test/Schema/ParseOptions-preserveKeyOrder.test.ts b/packages/effect/test/Schema/Schema/ParseOptions-preserveKeyOrder.test.ts similarity index 95% rename from packages/schema/test/Schema/ParseOptions-preserveKeyOrder.test.ts rename to packages/effect/test/Schema/Schema/ParseOptions-preserveKeyOrder.test.ts index 76bb3e02f1..7d0dddb021 100644 --- a/packages/schema/test/Schema/ParseOptions-preserveKeyOrder.test.ts +++ b/packages/effect/test/Schema/Schema/ParseOptions-preserveKeyOrder.test.ts @@ -1,11 +1,11 @@ -import * as ParseResult from "@effect/schema/ParseResult" -import * as S from "@effect/schema/Schema" import type { Duration } from "effect" import * as Effect from "effect/Effect" +import * as ParseResult from "effect/ParseResult" +import * as S from "effect/Schema" import { describe, expect, it } from "vitest" describe("`preserveKeyOrder` option", () => { - const b = Symbol.for("@effect/schema/test/b") + const b = Symbol.for("effect/Schema/test/b") const Sync = S.Struct({ a: S.Literal("a"), [b]: S.Array(S.String), diff --git a/packages/schema/test/Schema/ParseOptionsAnnotation.test.ts b/packages/effect/test/Schema/Schema/ParseOptionsAnnotation.test.ts similarity index 94% rename from packages/schema/test/Schema/ParseOptionsAnnotation.test.ts rename to packages/effect/test/Schema/Schema/ParseOptionsAnnotation.test.ts index c70ac310b9..82d2ed4d10 100644 --- a/packages/schema/test/Schema/ParseOptionsAnnotation.test.ts +++ b/packages/effect/test/Schema/Schema/ParseOptionsAnnotation.test.ts @@ -1,5 +1,5 @@ -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" +import * as S from "effect/Schema" +import * as Util from "effect/test/Schema/TestUtils" import { describe, it } from "vitest" const String = S.transform(S.NonEmptyString, S.String, { strict: true, decode: (s) => s, encode: (s) => s }) diff --git a/packages/schema/test/Schema/PropertySignature.test.ts b/packages/effect/test/Schema/Schema/PropertySignature.test.ts similarity index 96% rename from packages/schema/test/Schema/PropertySignature.test.ts rename to packages/effect/test/Schema/Schema/PropertySignature.test.ts index 8b21bb90ca..9f37db82a2 100644 --- a/packages/schema/test/Schema/PropertySignature.test.ts +++ b/packages/effect/test/Schema/Schema/PropertySignature.test.ts @@ -1,8 +1,8 @@ -import * as AST from "@effect/schema/AST" -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" import { identity } from "effect/Function" import * as Option from "effect/Option" +import * as S from "effect/Schema" +import * as AST from "effect/SchemaAST" +import * as Util from "effect/test/Schema/TestUtils" import { describe, expect, it } from "vitest" describe("PropertySignature", () => { @@ -197,14 +197,14 @@ describe("PropertySignature", () => { }) it("symbol key", async () => { - const a = Symbol.for("@effect/schema/test/a") + const a = Symbol.for("effect/Schema/test/a") const ps = S.propertySignature(S.Symbol).pipe(S.fromKey(a)) const transform = S.Struct({ a: ps }) const rename = S.asSchema(transform) const schema = S.Struct({ b: S.Number }).pipe(S.extend(rename)) - await Util.expectDecodeUnknownSuccess(schema, { [a]: "@effect/schema/test/a", b: 1 }, { a, b: 1 }) - await Util.expectEncodeSuccess(schema, { a, b: 1 }, { [a]: "@effect/schema/test/a", b: 1 }) + await Util.expectDecodeUnknownSuccess(schema, { [a]: "effect/Schema/test/a", b: 1 }, { a, b: 1 }) + await Util.expectEncodeSuccess(schema, { a, b: 1 }, { [a]: "effect/Schema/test/a", b: 1 }) }) }) }) diff --git a/packages/schema/test/Schema/ReadonlyMap/ReadonlyMap.test.ts b/packages/effect/test/Schema/Schema/ReadonlyMap/ReadonlyMap.test.ts similarity index 93% rename from packages/schema/test/Schema/ReadonlyMap/ReadonlyMap.test.ts rename to packages/effect/test/Schema/Schema/ReadonlyMap/ReadonlyMap.test.ts index cb27f5b80a..abf898f14b 100644 --- a/packages/schema/test/Schema/ReadonlyMap/ReadonlyMap.test.ts +++ b/packages/effect/test/Schema/Schema/ReadonlyMap/ReadonlyMap.test.ts @@ -1,5 +1,5 @@ -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" +import * as S from "effect/Schema" +import * as Util from "effect/test/Schema/TestUtils" import { describe, it } from "vitest" describe("ReadonlyMap", () => { diff --git a/packages/schema/test/Schema/ReadonlyMap/ReadonlyMapFromRecord.test.ts b/packages/effect/test/Schema/Schema/ReadonlyMap/ReadonlyMapFromRecord.test.ts similarity index 95% rename from packages/schema/test/Schema/ReadonlyMap/ReadonlyMapFromRecord.test.ts rename to packages/effect/test/Schema/Schema/ReadonlyMap/ReadonlyMapFromRecord.test.ts index 74d5d0b00e..632fa48391 100644 --- a/packages/schema/test/Schema/ReadonlyMap/ReadonlyMapFromRecord.test.ts +++ b/packages/effect/test/Schema/Schema/ReadonlyMap/ReadonlyMapFromRecord.test.ts @@ -1,5 +1,5 @@ -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" +import * as S from "effect/Schema" +import * as Util from "effect/test/Schema/TestUtils" import { describe, it } from "vitest" describe("ReadonlyMapFromRecord", () => { diff --git a/packages/schema/test/Schema/ReadonlyMap/ReadonlyMapFromSelf.test.ts b/packages/effect/test/Schema/Schema/ReadonlyMap/ReadonlyMapFromSelf.test.ts similarity index 92% rename from packages/schema/test/Schema/ReadonlyMap/ReadonlyMapFromSelf.test.ts rename to packages/effect/test/Schema/Schema/ReadonlyMap/ReadonlyMapFromSelf.test.ts index c4f74c5d6e..44870dbc36 100644 --- a/packages/schema/test/Schema/ReadonlyMap/ReadonlyMapFromSelf.test.ts +++ b/packages/effect/test/Schema/Schema/ReadonlyMap/ReadonlyMapFromSelf.test.ts @@ -1,7 +1,7 @@ -import * as P from "@effect/schema/ParseResult" -import * as Pretty from "@effect/schema/Pretty" -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" +import * as P from "effect/ParseResult" +import * as Pretty from "effect/Pretty" +import * as S from "effect/Schema" +import * as Util from "effect/test/Schema/TestUtils" import { describe, expect, it } from "vitest" describe("ReadonlyMapFromSelf", () => { diff --git a/packages/schema/test/Schema/ReadonlySet/ReadonlySet.test.ts b/packages/effect/test/Schema/Schema/ReadonlySet/ReadonlySet.test.ts similarity index 91% rename from packages/schema/test/Schema/ReadonlySet/ReadonlySet.test.ts rename to packages/effect/test/Schema/Schema/ReadonlySet/ReadonlySet.test.ts index 9ab3409b65..a251aebcc9 100644 --- a/packages/schema/test/Schema/ReadonlySet/ReadonlySet.test.ts +++ b/packages/effect/test/Schema/Schema/ReadonlySet/ReadonlySet.test.ts @@ -1,5 +1,5 @@ -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" +import * as S from "effect/Schema" +import * as Util from "effect/test/Schema/TestUtils" import { describe, it } from "vitest" describe("ReadonlySet", () => { diff --git a/packages/schema/test/Schema/ReadonlySet/ReadonlySetFromSelf.test.ts b/packages/effect/test/Schema/Schema/ReadonlySet/ReadonlySetFromSelf.test.ts similarity index 90% rename from packages/schema/test/Schema/ReadonlySet/ReadonlySetFromSelf.test.ts rename to packages/effect/test/Schema/Schema/ReadonlySet/ReadonlySetFromSelf.test.ts index 2561faffea..43a82d7400 100644 --- a/packages/schema/test/Schema/ReadonlySet/ReadonlySetFromSelf.test.ts +++ b/packages/effect/test/Schema/Schema/ReadonlySet/ReadonlySetFromSelf.test.ts @@ -1,7 +1,7 @@ -import * as P from "@effect/schema/ParseResult" -import * as Pretty from "@effect/schema/Pretty" -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" +import * as P from "effect/ParseResult" +import * as Pretty from "effect/Pretty" +import * as S from "effect/Schema" +import * as Util from "effect/test/Schema/TestUtils" import { describe, expect, it } from "vitest" describe("ReadonlySetFromSelf", () => { diff --git a/packages/schema/test/Schema/Record/Record.test.ts b/packages/effect/test/Schema/Schema/Record/Record.test.ts similarity index 93% rename from packages/schema/test/Schema/Record/Record.test.ts rename to packages/effect/test/Schema/Schema/Record/Record.test.ts index 940ddc39e4..37757221a3 100644 --- a/packages/schema/test/Schema/Record/Record.test.ts +++ b/packages/effect/test/Schema/Schema/Record/Record.test.ts @@ -1,8 +1,8 @@ -import * as AST from "@effect/schema/AST" -import * as ParseResult from "@effect/schema/ParseResult" -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" import * as Either from "effect/Either" +import * as ParseResult from "effect/ParseResult" +import * as S from "effect/Schema" +import * as AST from "effect/SchemaAST" +import * as Util from "effect/test/Schema/TestUtils" import { assert, describe, expect, it } from "vitest" describe("record", () => { @@ -107,20 +107,20 @@ describe("record", () => { └─ ["a"] └─ Expected number, actual "a"` ) - const b = Symbol.for("@effect/schema/test/b") + const b = Symbol.for("effect/Schema/test/b") await Util.expectDecodeUnknownSuccess(schema, { a: 1, [b]: "b" }, { a: 1 }) await Util.expectDecodeUnknownFailure( schema, { a: 1, [b]: "b" }, `{ readonly [x: string]: number } -└─ [Symbol(@effect/schema/test/b)] +└─ [Symbol(effect/Schema/test/b)] └─ is unexpected, expected: string`, Util.onExcessPropertyError ) }) it("Record(symbol, number)", async () => { - const a = Symbol.for("@effect/schema/test/a") + const a = Symbol.for("effect/Schema/test/a") const schema = S.Record({ key: S.SymbolFromSelf, value: S.Number }) await Util.expectDecodeUnknownSuccess(schema, {}) await Util.expectDecodeUnknownSuccess(schema, { [a]: 1 }) @@ -134,7 +134,7 @@ describe("record", () => { schema, { [a]: "a" }, `{ readonly [x: symbol]: number } -└─ [Symbol(@effect/schema/test/a)] +└─ [Symbol(effect/Schema/test/a)] └─ Expected number, actual "a"` ) await Util.expectDecodeUnknownSuccess( @@ -237,30 +237,30 @@ describe("record", () => { }) it("Record(Symbol('a') | Symbol('b'), number)", async () => { - const a = Symbol.for("@effect/schema/test/a") - const b = Symbol.for("@effect/schema/test/b") + const a = Symbol.for("effect/Schema/test/a") + const b = Symbol.for("effect/Schema/test/b") const schema = S.Record({ key: S.Union(S.UniqueSymbolFromSelf(a), S.UniqueSymbolFromSelf(b)), value: S.Number }) await Util.expectDecodeUnknownSuccess(schema, { [a]: 1, [b]: 2 }) await Util.expectDecodeUnknownFailure( schema, {}, - `{ readonly Symbol(@effect/schema/test/a): number; readonly Symbol(@effect/schema/test/b): number } -└─ [Symbol(@effect/schema/test/a)] + `{ readonly Symbol(effect/Schema/test/a): number; readonly Symbol(effect/Schema/test/b): number } +└─ [Symbol(effect/Schema/test/a)] └─ is missing` ) await Util.expectDecodeUnknownFailure( schema, { [a]: 1 }, - `{ readonly Symbol(@effect/schema/test/a): number; readonly Symbol(@effect/schema/test/b): number } -└─ [Symbol(@effect/schema/test/b)] + `{ readonly Symbol(effect/Schema/test/a): number; readonly Symbol(effect/Schema/test/b): number } +└─ [Symbol(effect/Schema/test/b)] └─ is missing` ) await Util.expectDecodeUnknownFailure( schema, { [b]: 2 }, - `{ readonly Symbol(@effect/schema/test/a): number; readonly Symbol(@effect/schema/test/b): number } -└─ [Symbol(@effect/schema/test/a)] + `{ readonly Symbol(effect/Schema/test/a): number; readonly Symbol(effect/Schema/test/b): number } +└─ [Symbol(effect/Schema/test/a)] └─ is missing` ) }) diff --git a/packages/schema/test/Schema/Redacted/Redacted.test.ts b/packages/effect/test/Schema/Schema/Redacted/Redacted.test.ts similarity index 86% rename from packages/schema/test/Schema/Redacted/Redacted.test.ts rename to packages/effect/test/Schema/Schema/Redacted/Redacted.test.ts index b4b9d471a3..d7eb32de4c 100644 --- a/packages/schema/test/Schema/Redacted/Redacted.test.ts +++ b/packages/effect/test/Schema/Schema/Redacted/Redacted.test.ts @@ -1,7 +1,7 @@ -import * as Pretty from "@effect/schema/Pretty" -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" import { Redacted } from "effect" +import * as Pretty from "effect/Pretty" +import * as S from "effect/Schema" +import * as Util from "effect/test/Schema/TestUtils" import { describe, expect, it } from "vitest" describe("Redacted", () => { diff --git a/packages/schema/test/Schema/Redacted/RedactedFromSelf.test.ts b/packages/effect/test/Schema/Schema/Redacted/RedactedFromSelf.test.ts similarity index 86% rename from packages/schema/test/Schema/Redacted/RedactedFromSelf.test.ts rename to packages/effect/test/Schema/Schema/Redacted/RedactedFromSelf.test.ts index 06d3d88f29..1bb12faff7 100644 --- a/packages/schema/test/Schema/Redacted/RedactedFromSelf.test.ts +++ b/packages/effect/test/Schema/Schema/Redacted/RedactedFromSelf.test.ts @@ -1,7 +1,7 @@ -import * as Pretty from "@effect/schema/Pretty" -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" import { Redacted } from "effect" +import * as Pretty from "effect/Pretty" +import * as S from "effect/Schema" +import * as Util from "effect/test/Schema/TestUtils" import { describe, expect, it } from "vitest" describe("RedactedFromSelf", () => { diff --git a/packages/schema/test/Schema/Set/Set.test.ts b/packages/effect/test/Schema/Schema/Set/Set.test.ts similarity index 82% rename from packages/schema/test/Schema/Set/Set.test.ts rename to packages/effect/test/Schema/Schema/Set/Set.test.ts index d90344212a..7ac771c82d 100644 --- a/packages/schema/test/Schema/Set/Set.test.ts +++ b/packages/effect/test/Schema/Schema/Set/Set.test.ts @@ -1,4 +1,4 @@ -import * as S from "@effect/schema/Schema" +import * as S from "effect/Schema" import { describe, expect, it } from "vitest" describe("Set", () => { diff --git a/packages/schema/test/Schema/Set/SetFromSelf.test.ts b/packages/effect/test/Schema/Schema/Set/SetFromSelf.test.ts similarity index 81% rename from packages/schema/test/Schema/Set/SetFromSelf.test.ts rename to packages/effect/test/Schema/Schema/Set/SetFromSelf.test.ts index 9c28f9e175..3f0abf847a 100644 --- a/packages/schema/test/Schema/Set/SetFromSelf.test.ts +++ b/packages/effect/test/Schema/Schema/Set/SetFromSelf.test.ts @@ -1,4 +1,4 @@ -import * as S from "@effect/schema/Schema" +import * as S from "effect/Schema" import { describe, expect, it } from "vitest" describe("SetFromSelf", () => { diff --git a/packages/schema/test/Schema/SortedSet/SortedSet.test.ts b/packages/effect/test/Schema/Schema/SortedSet/SortedSet.test.ts similarity index 93% rename from packages/schema/test/Schema/SortedSet/SortedSet.test.ts rename to packages/effect/test/Schema/Schema/SortedSet/SortedSet.test.ts index e3955a39de..894217b6bf 100644 --- a/packages/schema/test/Schema/SortedSet/SortedSet.test.ts +++ b/packages/effect/test/Schema/Schema/SortedSet/SortedSet.test.ts @@ -1,7 +1,7 @@ -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" import * as N from "effect/Number" +import * as S from "effect/Schema" import * as SortedSet from "effect/SortedSet" +import * as Util from "effect/test/Schema/TestUtils" import { describe, it } from "vitest" describe("SortedSet", () => { diff --git a/packages/schema/test/Schema/SortedSet/SortedSetFromSelf.test.ts b/packages/effect/test/Schema/Schema/SortedSet/SortedSetFromSelf.test.ts similarity index 91% rename from packages/schema/test/Schema/SortedSet/SortedSetFromSelf.test.ts rename to packages/effect/test/Schema/Schema/SortedSet/SortedSetFromSelf.test.ts index db62237503..61ec5447e6 100644 --- a/packages/schema/test/Schema/SortedSet/SortedSetFromSelf.test.ts +++ b/packages/effect/test/Schema/Schema/SortedSet/SortedSetFromSelf.test.ts @@ -1,11 +1,11 @@ -import * as Equivalence from "@effect/schema/Equivalence" -import * as P from "@effect/schema/ParseResult" -import * as Pretty from "@effect/schema/Pretty" -import * as Schema from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" import * as N from "effect/Number" +import * as P from "effect/ParseResult" +import * as Pretty from "effect/Pretty" +import * as Schema from "effect/Schema" +import * as Equivalence from "effect/SchemaEquivalence" import * as SortedSet from "effect/SortedSet" import * as S from "effect/String" +import * as Util from "effect/test/Schema/TestUtils" import { describe, expect, it } from "vitest" describe("SortedSetFromSelf", () => { diff --git a/packages/schema/test/Schema/String/NonEmptyTrimmedString.test.ts b/packages/effect/test/Schema/Schema/String/NonEmptyTrimmedString.test.ts similarity index 93% rename from packages/schema/test/Schema/String/NonEmptyTrimmedString.test.ts rename to packages/effect/test/Schema/Schema/String/NonEmptyTrimmedString.test.ts index ba9b2d68f4..dc31c6691e 100644 --- a/packages/schema/test/Schema/String/NonEmptyTrimmedString.test.ts +++ b/packages/effect/test/Schema/Schema/String/NonEmptyTrimmedString.test.ts @@ -1,5 +1,5 @@ -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" +import * as S from "effect/Schema" +import * as Util from "effect/test/Schema/TestUtils" import { describe, it } from "vitest" describe("NonEmptyTrimmedString", () => { diff --git a/packages/schema/test/Schema/String/String.test.ts b/packages/effect/test/Schema/Schema/String/String.test.ts similarity index 78% rename from packages/schema/test/Schema/String/String.test.ts rename to packages/effect/test/Schema/Schema/String/String.test.ts index 6722fcc845..8f2d319675 100644 --- a/packages/schema/test/Schema/String/String.test.ts +++ b/packages/effect/test/Schema/Schema/String/String.test.ts @@ -1,5 +1,5 @@ -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" +import * as S from "effect/Schema" +import * as Util from "effect/test/Schema/TestUtils" import { describe, it } from "vitest" describe("String", () => { diff --git a/packages/schema/test/Schema/String/StringFromBase64.test.ts b/packages/effect/test/Schema/Schema/String/StringFromBase64.test.ts similarity index 91% rename from packages/schema/test/Schema/String/StringFromBase64.test.ts rename to packages/effect/test/Schema/Schema/String/StringFromBase64.test.ts index 4a5e3384f9..a783595055 100644 --- a/packages/schema/test/Schema/String/StringFromBase64.test.ts +++ b/packages/effect/test/Schema/Schema/String/StringFromBase64.test.ts @@ -1,5 +1,5 @@ -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" +import * as S from "effect/Schema" +import * as Util from "effect/test/Schema/TestUtils" import { describe, it } from "vitest" describe("StringFromBase64", () => { diff --git a/packages/schema/test/Schema/String/StringFromBase64Url.test.ts b/packages/effect/test/Schema/Schema/String/StringFromBase64Url.test.ts similarity index 91% rename from packages/schema/test/Schema/String/StringFromBase64Url.test.ts rename to packages/effect/test/Schema/Schema/String/StringFromBase64Url.test.ts index e72b188b55..bc9a21afb9 100644 --- a/packages/schema/test/Schema/String/StringFromBase64Url.test.ts +++ b/packages/effect/test/Schema/Schema/String/StringFromBase64Url.test.ts @@ -1,5 +1,5 @@ -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" +import * as S from "effect/Schema" +import * as Util from "effect/test/Schema/TestUtils" import { describe, it } from "vitest" describe("StringFromBase64Url", () => { diff --git a/packages/schema/test/Schema/String/StringFromHex.test.ts b/packages/effect/test/Schema/Schema/String/StringFromHex.test.ts similarity index 93% rename from packages/schema/test/Schema/String/StringFromHex.test.ts rename to packages/effect/test/Schema/Schema/String/StringFromHex.test.ts index c7cd22d656..93bb461d3d 100644 --- a/packages/schema/test/Schema/String/StringFromHex.test.ts +++ b/packages/effect/test/Schema/Schema/String/StringFromHex.test.ts @@ -1,5 +1,5 @@ -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" +import * as S from "effect/Schema" +import * as Util from "effect/test/Schema/TestUtils" import { describe, it } from "vitest" describe("StringFromHex", () => { diff --git a/packages/schema/test/Schema/String/capitalize.test.ts b/packages/effect/test/Schema/Schema/String/capitalize.test.ts similarity index 90% rename from packages/schema/test/Schema/String/capitalize.test.ts rename to packages/effect/test/Schema/Schema/String/capitalize.test.ts index 32043fd0b6..483a5d937f 100644 --- a/packages/schema/test/Schema/String/capitalize.test.ts +++ b/packages/effect/test/Schema/Schema/String/capitalize.test.ts @@ -1,5 +1,5 @@ -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" +import * as S from "effect/Schema" +import * as Util from "effect/test/Schema/TestUtils" import { describe, it } from "vitest" describe("Capitalize", () => { diff --git a/packages/schema/test/Schema/String/endsWith.test.ts b/packages/effect/test/Schema/Schema/String/endsWith.test.ts similarity index 86% rename from packages/schema/test/Schema/String/endsWith.test.ts rename to packages/effect/test/Schema/Schema/String/endsWith.test.ts index 015426fbf4..b10ba13576 100644 --- a/packages/schema/test/Schema/String/endsWith.test.ts +++ b/packages/effect/test/Schema/Schema/String/endsWith.test.ts @@ -1,6 +1,6 @@ -import * as P from "@effect/schema/ParseResult" -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" +import * as P from "effect/ParseResult" +import * as S from "effect/Schema" +import * as Util from "effect/test/Schema/TestUtils" import { describe, expect, it } from "vitest" describe("endsWith", () => { diff --git a/packages/schema/test/Schema/String/includes.test.ts b/packages/effect/test/Schema/Schema/String/includes.test.ts similarity index 83% rename from packages/schema/test/Schema/String/includes.test.ts rename to packages/effect/test/Schema/Schema/String/includes.test.ts index 3d5a9402c5..430f86a2ea 100644 --- a/packages/schema/test/Schema/String/includes.test.ts +++ b/packages/effect/test/Schema/Schema/String/includes.test.ts @@ -1,7 +1,7 @@ -import * as P from "@effect/schema/ParseResult" -import * as Pretty from "@effect/schema/Pretty" -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" +import * as P from "effect/ParseResult" +import * as Pretty from "effect/Pretty" +import * as S from "effect/Schema" +import * as Util from "effect/test/Schema/TestUtils" import { describe, expect, it } from "vitest" describe("includes", () => { diff --git a/packages/schema/test/Schema/String/length.test.ts b/packages/effect/test/Schema/Schema/String/length.test.ts similarity index 96% rename from packages/schema/test/Schema/String/length.test.ts rename to packages/effect/test/Schema/Schema/String/length.test.ts index 6772b60100..6fefb2859c 100644 --- a/packages/schema/test/Schema/String/length.test.ts +++ b/packages/effect/test/Schema/Schema/String/length.test.ts @@ -1,5 +1,5 @@ -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" +import * as S from "effect/Schema" +import * as Util from "effect/test/Schema/TestUtils" import { describe, it } from "vitest" describe("length", () => { diff --git a/packages/schema/test/Schema/String/lowercase.test.ts b/packages/effect/test/Schema/Schema/String/lowercase.test.ts similarity index 89% rename from packages/schema/test/Schema/String/lowercase.test.ts rename to packages/effect/test/Schema/Schema/String/lowercase.test.ts index a765395850..8a523d990d 100644 --- a/packages/schema/test/Schema/String/lowercase.test.ts +++ b/packages/effect/test/Schema/Schema/String/lowercase.test.ts @@ -1,5 +1,5 @@ -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" +import * as S from "effect/Schema" +import * as Util from "effect/test/Schema/TestUtils" import { describe, it } from "vitest" describe("Lowercase", () => { diff --git a/packages/schema/test/Schema/String/maxLength.test.ts b/packages/effect/test/Schema/Schema/String/maxLength.test.ts similarity index 81% rename from packages/schema/test/Schema/String/maxLength.test.ts rename to packages/effect/test/Schema/Schema/String/maxLength.test.ts index 71959f3409..86634ece00 100644 --- a/packages/schema/test/Schema/String/maxLength.test.ts +++ b/packages/effect/test/Schema/Schema/String/maxLength.test.ts @@ -1,7 +1,7 @@ -import * as P from "@effect/schema/ParseResult" -import * as Pretty from "@effect/schema/Pretty" -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" +import * as P from "effect/ParseResult" +import * as Pretty from "effect/Pretty" +import * as S from "effect/Schema" +import * as Util from "effect/test/Schema/TestUtils" import { describe, expect, it } from "vitest" describe("maxLength", () => { diff --git a/packages/schema/test/Schema/String/minLength.test.ts b/packages/effect/test/Schema/Schema/String/minLength.test.ts similarity index 81% rename from packages/schema/test/Schema/String/minLength.test.ts rename to packages/effect/test/Schema/Schema/String/minLength.test.ts index 73e5cd0361..3556a17dc9 100644 --- a/packages/schema/test/Schema/String/minLength.test.ts +++ b/packages/effect/test/Schema/Schema/String/minLength.test.ts @@ -1,7 +1,7 @@ -import * as P from "@effect/schema/ParseResult" -import * as Pretty from "@effect/schema/Pretty" -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" +import * as P from "effect/ParseResult" +import * as Pretty from "effect/Pretty" +import * as S from "effect/Schema" +import * as Util from "effect/test/Schema/TestUtils" import { describe, expect, it } from "vitest" describe("minLength", () => { diff --git a/packages/schema/test/Schema/String/nonEmptyString.test.ts b/packages/effect/test/Schema/Schema/String/nonEmptyString.test.ts similarity index 87% rename from packages/schema/test/Schema/String/nonEmptyString.test.ts rename to packages/effect/test/Schema/Schema/String/nonEmptyString.test.ts index 7f371acc4b..da56c33225 100644 --- a/packages/schema/test/Schema/String/nonEmptyString.test.ts +++ b/packages/effect/test/Schema/Schema/String/nonEmptyString.test.ts @@ -1,5 +1,5 @@ -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" +import * as S from "effect/Schema" +import * as Util from "effect/test/Schema/TestUtils" import { describe, it } from "vitest" describe("nonEmptyString", () => { diff --git a/packages/schema/test/Schema/String/pattern.test.ts b/packages/effect/test/Schema/Schema/String/pattern.test.ts similarity index 92% rename from packages/schema/test/Schema/String/pattern.test.ts rename to packages/effect/test/Schema/Schema/String/pattern.test.ts index 3d1992aa14..46c0c1fbe3 100644 --- a/packages/schema/test/Schema/String/pattern.test.ts +++ b/packages/effect/test/Schema/Schema/String/pattern.test.ts @@ -1,5 +1,5 @@ -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" +import * as S from "effect/Schema" +import * as Util from "effect/test/Schema/TestUtils" import { describe, expect, it } from "vitest" describe("pattern", () => { diff --git a/packages/schema/test/Schema/String/split.test.ts b/packages/effect/test/Schema/Schema/String/split.test.ts similarity index 91% rename from packages/schema/test/Schema/String/split.test.ts rename to packages/effect/test/Schema/Schema/String/split.test.ts index f1c986474a..a514b780b1 100644 --- a/packages/schema/test/Schema/String/split.test.ts +++ b/packages/effect/test/Schema/Schema/String/split.test.ts @@ -1,5 +1,5 @@ -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" +import * as S from "effect/Schema" +import * as Util from "effect/test/Schema/TestUtils" import { describe, it } from "vitest" describe("string/split", () => { diff --git a/packages/schema/test/Schema/String/startsWith.test.ts b/packages/effect/test/Schema/Schema/String/startsWith.test.ts similarity index 86% rename from packages/schema/test/Schema/String/startsWith.test.ts rename to packages/effect/test/Schema/Schema/String/startsWith.test.ts index 51a8681b94..79bb934e76 100644 --- a/packages/schema/test/Schema/String/startsWith.test.ts +++ b/packages/effect/test/Schema/Schema/String/startsWith.test.ts @@ -1,6 +1,6 @@ -import * as P from "@effect/schema/ParseResult" -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" +import * as P from "effect/ParseResult" +import * as S from "effect/Schema" +import * as Util from "effect/test/Schema/TestUtils" import { describe, expect, it } from "vitest" describe("startsWith", () => { diff --git a/packages/schema/test/Schema/String/trim.test.ts b/packages/effect/test/Schema/Schema/String/trim.test.ts similarity index 96% rename from packages/schema/test/Schema/String/trim.test.ts rename to packages/effect/test/Schema/Schema/String/trim.test.ts index d7dbf8b4a6..fd9a1ea993 100644 --- a/packages/schema/test/Schema/String/trim.test.ts +++ b/packages/effect/test/Schema/Schema/String/trim.test.ts @@ -1,5 +1,5 @@ -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" +import * as S from "effect/Schema" +import * as Util from "effect/test/Schema/TestUtils" import { describe, it } from "vitest" describe("trim", () => { diff --git a/packages/schema/test/Schema/String/uncapitalize.test.ts b/packages/effect/test/Schema/Schema/String/uncapitalize.test.ts similarity index 90% rename from packages/schema/test/Schema/String/uncapitalize.test.ts rename to packages/effect/test/Schema/Schema/String/uncapitalize.test.ts index c8ff2a888b..5b908966bd 100644 --- a/packages/schema/test/Schema/String/uncapitalize.test.ts +++ b/packages/effect/test/Schema/Schema/String/uncapitalize.test.ts @@ -1,5 +1,5 @@ -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" +import * as S from "effect/Schema" +import * as Util from "effect/test/Schema/TestUtils" import { describe, it } from "vitest" describe("Uncapitalize", () => { diff --git a/packages/schema/test/Schema/String/uppercase.test.ts b/packages/effect/test/Schema/Schema/String/uppercase.test.ts similarity index 89% rename from packages/schema/test/Schema/String/uppercase.test.ts rename to packages/effect/test/Schema/Schema/String/uppercase.test.ts index 8d821d6d3e..0141859433 100644 --- a/packages/schema/test/Schema/String/uppercase.test.ts +++ b/packages/effect/test/Schema/Schema/String/uppercase.test.ts @@ -1,5 +1,5 @@ -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" +import * as S from "effect/Schema" +import * as Util from "effect/test/Schema/TestUtils" import { describe, it } from "vitest" describe("Uppercase", () => { diff --git a/packages/schema/test/Schema/Struct/Struct.test.ts b/packages/effect/test/Schema/Schema/Struct/Struct.test.ts similarity index 97% rename from packages/schema/test/Schema/Struct/Struct.test.ts rename to packages/effect/test/Schema/Schema/Struct/Struct.test.ts index ee63597192..6b3385db50 100644 --- a/packages/schema/test/Schema/Struct/Struct.test.ts +++ b/packages/effect/test/Schema/Schema/Struct/Struct.test.ts @@ -1,6 +1,6 @@ -import * as AST from "@effect/schema/AST" -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" +import * as S from "effect/Schema" +import * as AST from "effect/SchemaAST" +import * as Util from "effect/test/Schema/TestUtils" import { describe, expect, it } from "vitest" describe("Struct", () => { @@ -199,7 +199,7 @@ describe("Struct", () => { }) it.skip("should preserve the order of properties (sync)", () => { - const b = Symbol.for("@effect/schema/test/b") + const b = Symbol.for("effect/Schema/test/b") const schema = S.Struct({ a: S.Literal("a"), [b]: S.Array(S.String), @@ -286,7 +286,7 @@ describe("Struct", () => { }) it("should handle symbols as keys", async () => { - const a = Symbol.for("@effect/schema/test/a") + const a = Symbol.for("effect/Schema/test/a") const schema = S.Struct({ [a]: S.String }) await Util.expectEncodeSuccess(schema, { [a]: "a" }, { [a]: "a" }) }) diff --git a/packages/schema/test/Schema/Struct/make.test.ts b/packages/effect/test/Schema/Schema/Struct/make.test.ts similarity index 97% rename from packages/schema/test/Schema/Struct/make.test.ts rename to packages/effect/test/Schema/Schema/Struct/make.test.ts index 3def2e308e..3a29f12c10 100644 --- a/packages/schema/test/Schema/Struct/make.test.ts +++ b/packages/effect/test/Schema/Schema/Struct/make.test.ts @@ -1,5 +1,5 @@ -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" +import * as S from "effect/Schema" +import * as Util from "effect/test/Schema/TestUtils" import { describe, expect, it } from "vitest" describe("make", () => { diff --git a/packages/schema/test/Schema/Struct/omit.test.ts b/packages/effect/test/Schema/Schema/Struct/omit.test.ts similarity index 85% rename from packages/schema/test/Schema/Struct/omit.test.ts rename to packages/effect/test/Schema/Schema/Struct/omit.test.ts index b054f83b36..3a24e19154 100644 --- a/packages/schema/test/Schema/Struct/omit.test.ts +++ b/packages/effect/test/Schema/Schema/Struct/omit.test.ts @@ -1,4 +1,4 @@ -import * as S from "@effect/schema/Schema" +import * as S from "effect/Schema" import { describe, expect, it } from "vitest" describe("omit", () => { diff --git a/packages/schema/test/Schema/Struct/pick.test.ts b/packages/effect/test/Schema/Schema/Struct/pick.test.ts similarity index 86% rename from packages/schema/test/Schema/Struct/pick.test.ts rename to packages/effect/test/Schema/Schema/Struct/pick.test.ts index 9860fedb39..2dbd2c0f1e 100644 --- a/packages/schema/test/Schema/Struct/pick.test.ts +++ b/packages/effect/test/Schema/Schema/Struct/pick.test.ts @@ -1,4 +1,4 @@ -import * as S from "@effect/schema/Schema" +import * as S from "effect/Schema" import { describe, expect, it } from "vitest" describe("pick", () => { diff --git a/packages/schema/test/Schema/Symbol/Symbol.test.ts b/packages/effect/test/Schema/Schema/Symbol/Symbol.test.ts similarity index 88% rename from packages/schema/test/Schema/Symbol/Symbol.test.ts rename to packages/effect/test/Schema/Schema/Symbol/Symbol.test.ts index 81742e8f33..e93233335c 100644 --- a/packages/schema/test/Schema/Symbol/Symbol.test.ts +++ b/packages/effect/test/Schema/Schema/Symbol/Symbol.test.ts @@ -1,5 +1,5 @@ -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" +import * as S from "effect/Schema" +import * as Util from "effect/test/Schema/TestUtils" import { describe, it } from "vitest" describe("Symbol", () => { diff --git a/packages/schema/test/Schema/Symbol/SymbolFromSelf.test.ts b/packages/effect/test/Schema/Schema/Symbol/SymbolFromSelf.test.ts similarity index 66% rename from packages/schema/test/Schema/Symbol/SymbolFromSelf.test.ts rename to packages/effect/test/Schema/Schema/Symbol/SymbolFromSelf.test.ts index 51f0dd5f72..2ffd94d361 100644 --- a/packages/schema/test/Schema/Symbol/SymbolFromSelf.test.ts +++ b/packages/effect/test/Schema/Schema/Symbol/SymbolFromSelf.test.ts @@ -1,11 +1,11 @@ -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" +import * as S from "effect/Schema" +import * as Util from "effect/test/Schema/TestUtils" import { describe, it } from "vitest" describe("SymbolFromSelf", () => { const schema = S.SymbolFromSelf it("decoding", async () => { - const a = Symbol.for("@effect/schema/test/a") + const a = Symbol.for("effect/Schema/test/a") await Util.expectDecodeUnknownSuccess(schema, a) await Util.expectDecodeUnknownFailure( schema, @@ -15,7 +15,7 @@ describe("SymbolFromSelf", () => { }) it("encoding", async () => { - const a = Symbol.for("@effect/schema/test/a") + const a = Symbol.for("effect/Schema/test/a") await Util.expectEncodeSuccess(schema, a, a) }) }) diff --git a/packages/schema/test/Schema/TaggedStruct/make.test.ts b/packages/effect/test/Schema/Schema/TaggedStruct/make.test.ts similarity index 89% rename from packages/schema/test/Schema/TaggedStruct/make.test.ts rename to packages/effect/test/Schema/Schema/TaggedStruct/make.test.ts index 5ffe5f07b6..4889c13451 100644 --- a/packages/schema/test/Schema/TaggedStruct/make.test.ts +++ b/packages/effect/test/Schema/Schema/TaggedStruct/make.test.ts @@ -1,5 +1,5 @@ -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" +import * as S from "effect/Schema" +import * as Util from "effect/test/Schema/TestUtils" import { describe, expect, it } from "vitest" describe("make", () => { diff --git a/packages/schema/test/Schema/TemplateLiteral/TemplateLiteral.test.ts b/packages/effect/test/Schema/Schema/TemplateLiteral/TemplateLiteral.test.ts similarity index 98% rename from packages/schema/test/Schema/TemplateLiteral/TemplateLiteral.test.ts rename to packages/effect/test/Schema/Schema/TemplateLiteral/TemplateLiteral.test.ts index f0151f3e08..8c9b0ec77f 100644 --- a/packages/schema/test/Schema/TemplateLiteral/TemplateLiteral.test.ts +++ b/packages/effect/test/Schema/Schema/TemplateLiteral/TemplateLiteral.test.ts @@ -1,6 +1,6 @@ -import * as AST from "@effect/schema/AST" -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" +import * as S from "effect/Schema" +import * as AST from "effect/SchemaAST" +import * as Util from "effect/test/Schema/TestUtils" import { describe, expect, it } from "vitest" describe("TemplateLiteral", () => { diff --git a/packages/schema/test/Schema/TemplateLiteralParser.test.ts b/packages/effect/test/Schema/Schema/TemplateLiteralParser.test.ts similarity index 97% rename from packages/schema/test/Schema/TemplateLiteralParser.test.ts rename to packages/effect/test/Schema/Schema/TemplateLiteralParser.test.ts index fdb8a682bc..876369b776 100644 --- a/packages/schema/test/Schema/TemplateLiteralParser.test.ts +++ b/packages/effect/test/Schema/Schema/TemplateLiteralParser.test.ts @@ -1,5 +1,5 @@ -import * as Schema from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" +import * as Schema from "effect/Schema" +import * as Util from "effect/test/Schema/TestUtils" import { describe, expect, it } from "vitest" describe("TemplateLiteralParser", () => { diff --git a/packages/schema/test/Schema/Trimmed/Trimmed.test.ts b/packages/effect/test/Schema/Schema/Trimmed/Trimmed.test.ts similarity index 92% rename from packages/schema/test/Schema/Trimmed/Trimmed.test.ts rename to packages/effect/test/Schema/Schema/Trimmed/Trimmed.test.ts index f9c1b9fd0c..9420ce7b83 100644 --- a/packages/schema/test/Schema/Trimmed/Trimmed.test.ts +++ b/packages/effect/test/Schema/Schema/Trimmed/Trimmed.test.ts @@ -1,9 +1,9 @@ -import * as AST from "@effect/schema/AST" -import * as P from "@effect/schema/ParseResult" -import * as Pretty from "@effect/schema/Pretty" -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" import { Option, Predicate } from "effect" +import * as P from "effect/ParseResult" +import * as Pretty from "effect/Pretty" +import * as S from "effect/Schema" +import * as AST from "effect/SchemaAST" +import * as Util from "effect/test/Schema/TestUtils" import { describe, expect, it } from "vitest" describe("Trimmed", () => { diff --git a/packages/schema/test/Schema/Tuple/Tuple.test.ts b/packages/effect/test/Schema/Schema/Tuple/Tuple.test.ts similarity index 99% rename from packages/schema/test/Schema/Tuple/Tuple.test.ts rename to packages/effect/test/Schema/Schema/Tuple/Tuple.test.ts index 2ca3e04802..b099a6332f 100644 --- a/packages/schema/test/Schema/Tuple/Tuple.test.ts +++ b/packages/effect/test/Schema/Schema/Tuple/Tuple.test.ts @@ -1,6 +1,6 @@ -import * as AST from "@effect/schema/AST" -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" +import * as S from "effect/Schema" +import * as AST from "effect/SchemaAST" +import * as Util from "effect/test/Schema/TestUtils" import { describe, expect, it } from "vitest" describe("Tuple", () => { diff --git a/packages/schema/test/Schema/ULID.test.ts b/packages/effect/test/Schema/Schema/ULID.test.ts similarity index 81% rename from packages/schema/test/Schema/ULID.test.ts rename to packages/effect/test/Schema/Schema/ULID.test.ts index 79fadc91a5..d2d64bac3f 100644 --- a/packages/schema/test/Schema/ULID.test.ts +++ b/packages/effect/test/Schema/Schema/ULID.test.ts @@ -1,5 +1,5 @@ -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" +import * as S from "effect/Schema" +import * as Util from "effect/test/Schema/TestUtils" import { describe, it } from "vitest" describe("ULID", () => { diff --git a/packages/schema/test/Schema/UUID.test.ts b/packages/effect/test/Schema/Schema/UUID.test.ts similarity index 82% rename from packages/schema/test/Schema/UUID.test.ts rename to packages/effect/test/Schema/Schema/UUID.test.ts index 6cf41c7bf0..78356992fc 100644 --- a/packages/schema/test/Schema/UUID.test.ts +++ b/packages/effect/test/Schema/Schema/UUID.test.ts @@ -1,5 +1,5 @@ -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" +import * as S from "effect/Schema" +import * as Util from "effect/test/Schema/TestUtils" import { describe, it } from "vitest" describe("string/UUID", () => { diff --git a/packages/schema/test/Schema/Uint8Array/Uint8Array.test.ts b/packages/effect/test/Schema/Schema/Uint8Array/Uint8Array.test.ts similarity index 90% rename from packages/schema/test/Schema/Uint8Array/Uint8Array.test.ts rename to packages/effect/test/Schema/Schema/Uint8Array/Uint8Array.test.ts index c4130d8537..da162d0d77 100644 --- a/packages/schema/test/Schema/Uint8Array/Uint8Array.test.ts +++ b/packages/effect/test/Schema/Schema/Uint8Array/Uint8Array.test.ts @@ -1,5 +1,5 @@ -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" +import * as S from "effect/Schema" +import * as Util from "effect/test/Schema/TestUtils" import { describe, expect, it } from "vitest" describe("Uint8Array > Uint8Array", () => { diff --git a/packages/schema/test/Schema/Uint8Array/Uint8ArrayFromBase64.test.ts b/packages/effect/test/Schema/Schema/Uint8Array/Uint8ArrayFromBase64.test.ts similarity index 91% rename from packages/schema/test/Schema/Uint8Array/Uint8ArrayFromBase64.test.ts rename to packages/effect/test/Schema/Schema/Uint8Array/Uint8ArrayFromBase64.test.ts index a6eb8e04ef..92acdd31f4 100644 --- a/packages/schema/test/Schema/Uint8Array/Uint8ArrayFromBase64.test.ts +++ b/packages/effect/test/Schema/Schema/Uint8Array/Uint8ArrayFromBase64.test.ts @@ -1,5 +1,5 @@ -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" +import * as S from "effect/Schema" +import * as Util from "effect/test/Schema/TestUtils" import { describe, it } from "vitest" describe("Uint8ArrayFromBase64", () => { diff --git a/packages/schema/test/Schema/Uint8Array/Uint8ArrayFromBase64Url.test.ts b/packages/effect/test/Schema/Schema/Uint8Array/Uint8ArrayFromBase64Url.test.ts similarity index 92% rename from packages/schema/test/Schema/Uint8Array/Uint8ArrayFromBase64Url.test.ts rename to packages/effect/test/Schema/Schema/Uint8Array/Uint8ArrayFromBase64Url.test.ts index 998a8aa787..6f12f7a7db 100644 --- a/packages/schema/test/Schema/Uint8Array/Uint8ArrayFromBase64Url.test.ts +++ b/packages/effect/test/Schema/Schema/Uint8Array/Uint8ArrayFromBase64Url.test.ts @@ -1,5 +1,5 @@ -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" +import * as S from "effect/Schema" +import * as Util from "effect/test/Schema/TestUtils" import { describe, it } from "vitest" describe("Uint8ArrayFromBase64Url", () => { diff --git a/packages/schema/test/Schema/Uint8Array/Uint8ArrayFromHex.test.ts b/packages/effect/test/Schema/Schema/Uint8Array/Uint8ArrayFromHex.test.ts similarity index 93% rename from packages/schema/test/Schema/Uint8Array/Uint8ArrayFromHex.test.ts rename to packages/effect/test/Schema/Schema/Uint8Array/Uint8ArrayFromHex.test.ts index bebd38cfe0..dd46959fbf 100644 --- a/packages/schema/test/Schema/Uint8Array/Uint8ArrayFromHex.test.ts +++ b/packages/effect/test/Schema/Schema/Uint8Array/Uint8ArrayFromHex.test.ts @@ -1,5 +1,5 @@ -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" +import * as S from "effect/Schema" +import * as Util from "effect/test/Schema/TestUtils" import { describe, it } from "vitest" describe("Uint8ArrayFromHex", () => { diff --git a/packages/schema/test/Schema/Uint8Array/Uint8ArrayFromSelf.test.ts b/packages/effect/test/Schema/Schema/Uint8Array/Uint8ArrayFromSelf.test.ts similarity index 84% rename from packages/schema/test/Schema/Uint8Array/Uint8ArrayFromSelf.test.ts rename to packages/effect/test/Schema/Schema/Uint8Array/Uint8ArrayFromSelf.test.ts index 32c1ec1d0a..e7b3511115 100644 --- a/packages/schema/test/Schema/Uint8Array/Uint8ArrayFromSelf.test.ts +++ b/packages/effect/test/Schema/Schema/Uint8Array/Uint8ArrayFromSelf.test.ts @@ -1,6 +1,6 @@ -import * as Pretty from "@effect/schema/Pretty" -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" +import * as Pretty from "effect/Pretty" +import * as S from "effect/Schema" +import * as Util from "effect/test/Schema/TestUtils" import { describe, expect, it } from "vitest" describe("Uint8Array > Uint8ArrayFromSelf", () => { diff --git a/packages/schema/test/Schema/Union/Union.test.ts b/packages/effect/test/Schema/Schema/Union/Union.test.ts similarity index 98% rename from packages/schema/test/Schema/Union/Union.test.ts rename to packages/effect/test/Schema/Schema/Union/Union.test.ts index 525279b316..dd77590ee4 100644 --- a/packages/schema/test/Schema/Union/Union.test.ts +++ b/packages/effect/test/Schema/Schema/Union/Union.test.ts @@ -1,6 +1,6 @@ -import * as AST from "@effect/schema/AST" -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" +import * as S from "effect/Schema" +import * as AST from "effect/SchemaAST" +import * as Util from "effect/test/Schema/TestUtils" import { describe, expect, it } from "vitest" describe("Union", () => { diff --git a/packages/effect/test/Schema/Schema/UniqueSymbol/UniqueSymbolFromSelf.test.ts b/packages/effect/test/Schema/Schema/UniqueSymbol/UniqueSymbolFromSelf.test.ts new file mode 100644 index 0000000000..eb98685b4a --- /dev/null +++ b/packages/effect/test/Schema/Schema/UniqueSymbol/UniqueSymbolFromSelf.test.ts @@ -0,0 +1,17 @@ +import * as S from "effect/Schema" +import * as Util from "effect/test/Schema/TestUtils" +import { describe, it } from "vitest" + +describe("UniqueSymbolFromSelf", () => { + const a = Symbol.for("effect/Schema/test/a") + const schema = S.UniqueSymbolFromSelf(a) + it("decoding", async () => { + await Util.expectDecodeUnknownSuccess(schema, a) + await Util.expectDecodeUnknownSuccess(schema, Symbol.for("effect/Schema/test/a")) + await Util.expectDecodeUnknownFailure( + schema, + "Symbol(effect/Schema/test/a)", + `Expected Symbol(effect/Schema/test/a), actual "Symbol(effect/Schema/test/a)"` + ) + }) +}) diff --git a/packages/schema/test/Schema/Unknown/Unknown.test.ts b/packages/effect/test/Schema/Schema/Unknown/Unknown.test.ts similarity index 84% rename from packages/schema/test/Schema/Unknown/Unknown.test.ts rename to packages/effect/test/Schema/Schema/Unknown/Unknown.test.ts index d50ff8f052..9646355fb3 100644 --- a/packages/schema/test/Schema/Unknown/Unknown.test.ts +++ b/packages/effect/test/Schema/Schema/Unknown/Unknown.test.ts @@ -1,5 +1,5 @@ -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" +import * as S from "effect/Schema" +import * as Util from "effect/test/Schema/TestUtils" import { describe, it } from "vitest" describe("Unknown", () => { diff --git a/packages/schema/test/Schema/Void/Void.test.ts b/packages/effect/test/Schema/Schema/Void/Void.test.ts similarity index 85% rename from packages/schema/test/Schema/Void/Void.test.ts rename to packages/effect/test/Schema/Schema/Void/Void.test.ts index a253307a14..737873e5df 100644 --- a/packages/schema/test/Schema/Void/Void.test.ts +++ b/packages/effect/test/Schema/Schema/Void/Void.test.ts @@ -1,5 +1,5 @@ -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" +import * as S from "effect/Schema" +import * as Util from "effect/test/Schema/TestUtils" import { describe, it } from "vitest" describe("Void", () => { diff --git a/packages/schema/test/Schema/annotations.test.ts b/packages/effect/test/Schema/Schema/annotations.test.ts similarity index 94% rename from packages/schema/test/Schema/annotations.test.ts rename to packages/effect/test/Schema/Schema/annotations.test.ts index 18f0707246..6fafe96159 100644 --- a/packages/schema/test/Schema/annotations.test.ts +++ b/packages/effect/test/Schema/Schema/annotations.test.ts @@ -1,8 +1,8 @@ -import * as AST from "@effect/schema/AST" -import type * as ParseResult from "@effect/schema/ParseResult" -import * as Pretty from "@effect/schema/Pretty" -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" +import type * as ParseResult from "effect/ParseResult" +import * as Pretty from "effect/Pretty" +import * as S from "effect/Schema" +import * as AST from "effect/SchemaAST" +import * as Util from "effect/test/Schema/TestUtils" import { describe, expect, it } from "vitest" describe(".annotations()", () => { diff --git a/packages/schema/test/Schema/asserts.test.ts b/packages/effect/test/Schema/Schema/asserts.test.ts similarity index 93% rename from packages/schema/test/Schema/asserts.test.ts rename to packages/effect/test/Schema/Schema/asserts.test.ts index 58ad48c9ad..800f109d96 100644 --- a/packages/schema/test/Schema/asserts.test.ts +++ b/packages/effect/test/Schema/Schema/asserts.test.ts @@ -1,6 +1,6 @@ -import * as ParseResult from "@effect/schema/ParseResult" -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" +import * as ParseResult from "effect/ParseResult" +import * as S from "effect/Schema" +import * as Util from "effect/test/Schema/TestUtils" import { describe, expect, it } from "vitest" describe("asserts", () => { diff --git a/packages/schema/test/Schema/attachPropertySignature.test.ts b/packages/effect/test/Schema/Schema/attachPropertySignature.test.ts similarity index 91% rename from packages/schema/test/Schema/attachPropertySignature.test.ts rename to packages/effect/test/Schema/Schema/attachPropertySignature.test.ts index e5dc888741..c821d47408 100644 --- a/packages/schema/test/Schema/attachPropertySignature.test.ts +++ b/packages/effect/test/Schema/Schema/attachPropertySignature.test.ts @@ -1,6 +1,6 @@ -import * as ParseResult from "@effect/schema/ParseResult" -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" +import * as ParseResult from "effect/ParseResult" +import * as S from "effect/Schema" +import * as Util from "effect/test/Schema/TestUtils" import { describe, it } from "vitest" describe("attachPropertySignature", () => { @@ -21,7 +21,7 @@ describe("attachPropertySignature", () => { it("symbol keys literal values", async () => { const Circle = S.Struct({ radius: S.Number }) const Square = S.Struct({ sideLength: S.Number }) - const kind = Symbol.for("@effect/schema/test/kind") + const kind = Symbol.for("effect/Schema/test/kind") const schema = S.Union( Circle.pipe(S.attachPropertySignature(kind, "circle")), Square.pipe(S.attachPropertySignature(kind, "square")) @@ -36,9 +36,9 @@ describe("attachPropertySignature", () => { it("string keys unique symbols", async () => { const Circle = S.Struct({ radius: S.Number }) const Square = S.Struct({ sideLength: S.Number }) - const kind = Symbol.for("@effect/schema/test/kind") - const circle = Symbol.for("@effect/schema/test/circle") - const square = Symbol.for("@effect/schema/test/square") + const kind = Symbol.for("effect/Schema/test/kind") + const circle = Symbol.for("effect/Schema/test/circle") + const square = Symbol.for("effect/Schema/test/square") const schema = S.Union( Circle.pipe(S.attachPropertySignature(kind, circle)), Square.pipe(S.attachPropertySignature(kind, square)) @@ -53,8 +53,8 @@ describe("attachPropertySignature", () => { it("symbol keys unique symbols", async () => { const Circle = S.Struct({ radius: S.Number }) const Square = S.Struct({ sideLength: S.Number }) - const circle = Symbol.for("@effect/schema/test/circle") - const square = Symbol.for("@effect/schema/test/square") + const circle = Symbol.for("effect/Schema/test/circle") + const square = Symbol.for("effect/Schema/test/square") const schema = S.Union( Circle.pipe(S.attachPropertySignature("kind", circle)), Square.pipe(S.attachPropertySignature("kind", square)) diff --git a/packages/schema/test/Schema/brand.test.ts b/packages/effect/test/Schema/Schema/brand.test.ts similarity index 97% rename from packages/schema/test/Schema/brand.test.ts rename to packages/effect/test/Schema/Schema/brand.test.ts index 6232b08caa..3bca1e0572 100644 --- a/packages/schema/test/Schema/brand.test.ts +++ b/packages/effect/test/Schema/Schema/brand.test.ts @@ -1,6 +1,6 @@ -import * as AST from "@effect/schema/AST" -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" +import * as S from "effect/Schema" +import * as AST from "effect/SchemaAST" +import * as Util from "effect/test/Schema/TestUtils" import { describe, expect, it } from "vitest" describe("brand", () => { diff --git a/packages/schema/test/Schema/compose.test.ts b/packages/effect/test/Schema/Schema/compose.test.ts similarity index 96% rename from packages/schema/test/Schema/compose.test.ts rename to packages/effect/test/Schema/Schema/compose.test.ts index bf4dc62ead..624dea76ed 100644 --- a/packages/schema/test/Schema/compose.test.ts +++ b/packages/effect/test/Schema/Schema/compose.test.ts @@ -1,5 +1,5 @@ -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" +import * as S from "effect/Schema" +import * as Util from "effect/test/Schema/TestUtils" import { describe, it } from "vitest" describe("compose", async () => { diff --git a/packages/schema/test/Schema/decode.test.ts b/packages/effect/test/Schema/Schema/decode.test.ts similarity index 92% rename from packages/schema/test/Schema/decode.test.ts rename to packages/effect/test/Schema/Schema/decode.test.ts index 7400cc9cbc..5b59b16e66 100644 --- a/packages/schema/test/Schema/decode.test.ts +++ b/packages/effect/test/Schema/Schema/decode.test.ts @@ -1,5 +1,5 @@ -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" +import * as S from "effect/Schema" +import * as Util from "effect/test/Schema/TestUtils" import { describe, it } from "vitest" describe("decode", () => { diff --git a/packages/schema/test/Schema/decodeEither.test.ts b/packages/effect/test/Schema/Schema/decodeEither.test.ts similarity index 93% rename from packages/schema/test/Schema/decodeEither.test.ts rename to packages/effect/test/Schema/Schema/decodeEither.test.ts index 17266c10ba..f80284f2f7 100644 --- a/packages/schema/test/Schema/decodeEither.test.ts +++ b/packages/effect/test/Schema/Schema/decodeEither.test.ts @@ -1,5 +1,5 @@ -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" +import * as S from "effect/Schema" +import * as Util from "effect/test/Schema/TestUtils" import { describe, it } from "vitest" describe("decodeEither", () => { diff --git a/packages/schema/test/Schema/decodeOption.test.ts b/packages/effect/test/Schema/Schema/decodeOption.test.ts similarity index 89% rename from packages/schema/test/Schema/decodeOption.test.ts rename to packages/effect/test/Schema/Schema/decodeOption.test.ts index 7779795799..b6331f2900 100644 --- a/packages/schema/test/Schema/decodeOption.test.ts +++ b/packages/effect/test/Schema/Schema/decodeOption.test.ts @@ -1,5 +1,5 @@ -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" +import * as S from "effect/Schema" +import * as Util from "effect/test/Schema/TestUtils" import { describe, it } from "vitest" describe("decodeOption", () => { diff --git a/packages/schema/test/Schema/decodePromise.test.ts b/packages/effect/test/Schema/Schema/decodePromise.test.ts similarity index 92% rename from packages/schema/test/Schema/decodePromise.test.ts rename to packages/effect/test/Schema/Schema/decodePromise.test.ts index 1558d3b579..e10727d76b 100644 --- a/packages/schema/test/Schema/decodePromise.test.ts +++ b/packages/effect/test/Schema/Schema/decodePromise.test.ts @@ -1,5 +1,5 @@ -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" +import * as S from "effect/Schema" +import * as Util from "effect/test/Schema/TestUtils" import { describe, it } from "vitest" describe("decodePromise", () => { diff --git a/packages/schema/test/Schema/decodeSync.test.ts b/packages/effect/test/Schema/Schema/decodeSync.test.ts similarity index 93% rename from packages/schema/test/Schema/decodeSync.test.ts rename to packages/effect/test/Schema/Schema/decodeSync.test.ts index 8440bc5f28..996966761f 100644 --- a/packages/schema/test/Schema/decodeSync.test.ts +++ b/packages/effect/test/Schema/Schema/decodeSync.test.ts @@ -1,5 +1,5 @@ -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" +import * as S from "effect/Schema" +import * as Util from "effect/test/Schema/TestUtils" import { describe, expect, it } from "vitest" describe("decodeSync", () => { diff --git a/packages/schema/test/Schema/decodeUnknownEither.test.ts b/packages/effect/test/Schema/Schema/decodeUnknownEither.test.ts similarity index 78% rename from packages/schema/test/Schema/decodeUnknownEither.test.ts rename to packages/effect/test/Schema/Schema/decodeUnknownEither.test.ts index ecc07f6f05..2529cde6db 100644 --- a/packages/schema/test/Schema/decodeUnknownEither.test.ts +++ b/packages/effect/test/Schema/Schema/decodeUnknownEither.test.ts @@ -1,5 +1,5 @@ -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" +import * as S from "effect/Schema" +import * as Util from "effect/test/Schema/TestUtils" import { describe, it } from "vitest" describe("decodeUnknownEither", () => { diff --git a/packages/schema/test/Schema/decodeUnknownOption.test.ts b/packages/effect/test/Schema/Schema/decodeUnknownOption.test.ts similarity index 67% rename from packages/schema/test/Schema/decodeUnknownOption.test.ts rename to packages/effect/test/Schema/Schema/decodeUnknownOption.test.ts index 1f801c9efb..777d94406a 100644 --- a/packages/schema/test/Schema/decodeUnknownOption.test.ts +++ b/packages/effect/test/Schema/Schema/decodeUnknownOption.test.ts @@ -1,5 +1,5 @@ -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" +import * as S from "effect/Schema" +import * as Util from "effect/test/Schema/TestUtils" import { describe, it } from "vitest" describe("decodeUnknownOption", () => { diff --git a/packages/schema/test/Schema/decodeUnknownSync.test.ts b/packages/effect/test/Schema/Schema/decodeUnknownSync.test.ts similarity index 84% rename from packages/schema/test/Schema/decodeUnknownSync.test.ts rename to packages/effect/test/Schema/Schema/decodeUnknownSync.test.ts index 6f84d3823b..a0968fc0a0 100644 --- a/packages/schema/test/Schema/decodeUnknownSync.test.ts +++ b/packages/effect/test/Schema/Schema/decodeUnknownSync.test.ts @@ -1,6 +1,6 @@ -import * as ParseResult from "@effect/schema/ParseResult" -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" +import * as ParseResult from "effect/ParseResult" +import * as S from "effect/Schema" +import * as Util from "effect/test/Schema/TestUtils" import { describe, expect, it } from "vitest" describe("decodeUnknownSync", () => { diff --git a/packages/schema/test/Schema/encode.test.ts b/packages/effect/test/Schema/Schema/encode.test.ts similarity index 92% rename from packages/schema/test/Schema/encode.test.ts rename to packages/effect/test/Schema/Schema/encode.test.ts index c70198b09f..63a90342f0 100644 --- a/packages/schema/test/Schema/encode.test.ts +++ b/packages/effect/test/Schema/Schema/encode.test.ts @@ -1,5 +1,5 @@ -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" +import * as S from "effect/Schema" +import * as Util from "effect/test/Schema/TestUtils" import { describe, it } from "vitest" describe("encode", () => { diff --git a/packages/schema/test/Schema/encodeEither.test.ts b/packages/effect/test/Schema/Schema/encodeEither.test.ts similarity index 93% rename from packages/schema/test/Schema/encodeEither.test.ts rename to packages/effect/test/Schema/Schema/encodeEither.test.ts index bbdfcd0629..eed8a87efa 100644 --- a/packages/schema/test/Schema/encodeEither.test.ts +++ b/packages/effect/test/Schema/Schema/encodeEither.test.ts @@ -1,5 +1,5 @@ -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" +import * as S from "effect/Schema" +import * as Util from "effect/test/Schema/TestUtils" import { describe, it } from "vitest" describe("encodeEither", () => { diff --git a/packages/schema/test/Schema/encodeOption.test.ts b/packages/effect/test/Schema/Schema/encodeOption.test.ts similarity index 89% rename from packages/schema/test/Schema/encodeOption.test.ts rename to packages/effect/test/Schema/Schema/encodeOption.test.ts index 19784ac864..98e9b69063 100644 --- a/packages/schema/test/Schema/encodeOption.test.ts +++ b/packages/effect/test/Schema/Schema/encodeOption.test.ts @@ -1,5 +1,5 @@ -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" +import * as S from "effect/Schema" +import * as Util from "effect/test/Schema/TestUtils" import { describe, it } from "vitest" describe("encodeOption", () => { diff --git a/packages/schema/test/Schema/encodePromise.test.ts b/packages/effect/test/Schema/Schema/encodePromise.test.ts similarity index 92% rename from packages/schema/test/Schema/encodePromise.test.ts rename to packages/effect/test/Schema/Schema/encodePromise.test.ts index d15ed38466..39bc804074 100644 --- a/packages/schema/test/Schema/encodePromise.test.ts +++ b/packages/effect/test/Schema/Schema/encodePromise.test.ts @@ -1,5 +1,5 @@ -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" +import * as S from "effect/Schema" +import * as Util from "effect/test/Schema/TestUtils" import { describe, it } from "vitest" describe("encodePromise", () => { diff --git a/packages/schema/test/Schema/encodeSync.test.ts b/packages/effect/test/Schema/Schema/encodeSync.test.ts similarity index 93% rename from packages/schema/test/Schema/encodeSync.test.ts rename to packages/effect/test/Schema/Schema/encodeSync.test.ts index 6466dafc76..b40c25e80b 100644 --- a/packages/schema/test/Schema/encodeSync.test.ts +++ b/packages/effect/test/Schema/Schema/encodeSync.test.ts @@ -1,5 +1,5 @@ -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" +import * as S from "effect/Schema" +import * as Util from "effect/test/Schema/TestUtils" import { describe, expect, it } from "vitest" describe("encodeSync", () => { diff --git a/packages/schema/test/Schema/encodeUnknownEither.test.ts b/packages/effect/test/Schema/Schema/encodeUnknownEither.test.ts similarity index 78% rename from packages/schema/test/Schema/encodeUnknownEither.test.ts rename to packages/effect/test/Schema/Schema/encodeUnknownEither.test.ts index 50e9d7633e..0fecdd901c 100644 --- a/packages/schema/test/Schema/encodeUnknownEither.test.ts +++ b/packages/effect/test/Schema/Schema/encodeUnknownEither.test.ts @@ -1,5 +1,5 @@ -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" +import * as S from "effect/Schema" +import * as Util from "effect/test/Schema/TestUtils" import { describe, it } from "vitest" describe("encodeUnknownEither", () => { diff --git a/packages/schema/test/Schema/encodeUnknownOption.test.ts b/packages/effect/test/Schema/Schema/encodeUnknownOption.test.ts similarity index 67% rename from packages/schema/test/Schema/encodeUnknownOption.test.ts rename to packages/effect/test/Schema/Schema/encodeUnknownOption.test.ts index f2acc345de..0017712d7f 100644 --- a/packages/schema/test/Schema/encodeUnknownOption.test.ts +++ b/packages/effect/test/Schema/Schema/encodeUnknownOption.test.ts @@ -1,5 +1,5 @@ -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" +import * as S from "effect/Schema" +import * as Util from "effect/test/Schema/TestUtils" import { describe, it } from "vitest" describe("encodeUnknownOption", () => { diff --git a/packages/schema/test/Schema/encodeUnknownSync.test.ts b/packages/effect/test/Schema/Schema/encodeUnknownSync.test.ts similarity index 79% rename from packages/schema/test/Schema/encodeUnknownSync.test.ts rename to packages/effect/test/Schema/Schema/encodeUnknownSync.test.ts index 0f6fe49054..95d39f3106 100644 --- a/packages/schema/test/Schema/encodeUnknownSync.test.ts +++ b/packages/effect/test/Schema/Schema/encodeUnknownSync.test.ts @@ -1,5 +1,5 @@ -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" +import * as S from "effect/Schema" +import * as Util from "effect/test/Schema/TestUtils" import { describe, expect, it } from "vitest" describe("encodeUnknownSync", () => { diff --git a/packages/schema/test/Schema/encodedBoundSchema.test.ts b/packages/effect/test/Schema/Schema/encodedBoundSchema.test.ts similarity index 97% rename from packages/schema/test/Schema/encodedBoundSchema.test.ts rename to packages/effect/test/Schema/Schema/encodedBoundSchema.test.ts index 6a19529b55..cddfa1a9ba 100644 --- a/packages/schema/test/Schema/encodedBoundSchema.test.ts +++ b/packages/effect/test/Schema/Schema/encodedBoundSchema.test.ts @@ -1,5 +1,5 @@ -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" +import * as S from "effect/Schema" +import * as Util from "effect/test/Schema/TestUtils" import { describe, it } from "vitest" describe("encodedBoundSchema", () => { diff --git a/packages/schema/test/Schema/encodedSchema.test.ts b/packages/effect/test/Schema/Schema/encodedSchema.test.ts similarity index 92% rename from packages/schema/test/Schema/encodedSchema.test.ts rename to packages/effect/test/Schema/Schema/encodedSchema.test.ts index 82fe267362..d88325ce35 100644 --- a/packages/schema/test/Schema/encodedSchema.test.ts +++ b/packages/effect/test/Schema/Schema/encodedSchema.test.ts @@ -1,5 +1,5 @@ -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" +import * as S from "effect/Schema" +import * as Util from "effect/test/Schema/TestUtils" import { describe, it } from "vitest" describe("encodedSchema", () => { diff --git a/packages/schema/test/Schema/exports.test.ts b/packages/effect/test/Schema/Schema/exports.test.ts similarity index 97% rename from packages/schema/test/Schema/exports.test.ts rename to packages/effect/test/Schema/Schema/exports.test.ts index 79788108b0..d5e0e60610 100644 --- a/packages/schema/test/Schema/exports.test.ts +++ b/packages/effect/test/Schema/Schema/exports.test.ts @@ -1,4 +1,4 @@ -import * as S from "@effect/schema/Schema" +import * as S from "effect/Schema" import { expect, it } from "vitest" it("exports", () => { diff --git a/packages/schema/test/Schema/extend.test.ts b/packages/effect/test/Schema/Schema/extend.test.ts similarity index 98% rename from packages/schema/test/Schema/extend.test.ts rename to packages/effect/test/Schema/Schema/extend.test.ts index 3b08b556c9..edf6d033aa 100644 --- a/packages/schema/test/Schema/extend.test.ts +++ b/packages/effect/test/Schema/Schema/extend.test.ts @@ -1,8 +1,8 @@ -import * as Arbitrary from "@effect/schema/Arbitrary" -import * as AST from "@effect/schema/AST" -import * as FastCheck from "@effect/schema/FastCheck" -import * as Schema from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" +import * as Arbitrary from "effect/Arbitrary" +import * as FastCheck from "effect/FastCheck" +import * as Schema from "effect/Schema" +import * as AST from "effect/SchemaAST" +import * as Util from "effect/test/Schema/TestUtils" import { describe, expect, it } from "vitest" describe("extend", () => { diff --git a/packages/schema/test/Schema/filter.test.ts b/packages/effect/test/Schema/Schema/filter.test.ts similarity index 97% rename from packages/schema/test/Schema/filter.test.ts rename to packages/effect/test/Schema/Schema/filter.test.ts index 4a76babde6..bc71297250 100644 --- a/packages/schema/test/Schema/filter.test.ts +++ b/packages/effect/test/Schema/Schema/filter.test.ts @@ -1,7 +1,7 @@ -import * as AST from "@effect/schema/AST" -import * as ParseResult from "@effect/schema/ParseResult" -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" +import * as ParseResult from "effect/ParseResult" +import * as S from "effect/Schema" +import * as AST from "effect/SchemaAST" +import * as Util from "effect/test/Schema/TestUtils" import { describe, expect, it } from "vitest" describe("filter", () => { diff --git a/packages/schema/test/Schema/filterEffect.test.ts b/packages/effect/test/Schema/Schema/filterEffect.test.ts similarity index 97% rename from packages/schema/test/Schema/filterEffect.test.ts rename to packages/effect/test/Schema/Schema/filterEffect.test.ts index cd8b961164..3412692174 100644 --- a/packages/schema/test/Schema/filterEffect.test.ts +++ b/packages/effect/test/Schema/Schema/filterEffect.test.ts @@ -1,7 +1,7 @@ -import * as ParseResult from "@effect/schema/ParseResult" -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" import * as Effect from "effect/Effect" +import * as ParseResult from "effect/ParseResult" +import * as S from "effect/Schema" +import * as Util from "effect/test/Schema/TestUtils" import { describe, expect, it } from "vitest" describe("filterEffect", () => { diff --git a/packages/schema/test/Schema/format.test.ts b/packages/effect/test/Schema/Schema/format.test.ts similarity index 90% rename from packages/schema/test/Schema/format.test.ts rename to packages/effect/test/Schema/Schema/format.test.ts index 109a94cbb4..bf4210b27b 100644 --- a/packages/schema/test/Schema/format.test.ts +++ b/packages/effect/test/Schema/Schema/format.test.ts @@ -1,5 +1,5 @@ -import { format } from "@effect/schema/Schema" -import * as S from "@effect/schema/Schema" +import { format } from "effect/Schema" +import * as S from "effect/Schema" import { describe, expect, it } from "vitest" describe("format", () => { diff --git a/packages/schema/test/Schema/fromBrand.test.ts b/packages/effect/test/Schema/Schema/fromBrand.test.ts similarity index 95% rename from packages/schema/test/Schema/fromBrand.test.ts rename to packages/effect/test/Schema/Schema/fromBrand.test.ts index 725e6b942c..b21b087647 100644 --- a/packages/schema/test/Schema/fromBrand.test.ts +++ b/packages/effect/test/Schema/Schema/fromBrand.test.ts @@ -1,6 +1,6 @@ -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" import * as Brand from "effect/Brand" +import * as S from "effect/Schema" +import * as Util from "effect/test/Schema/TestUtils" import { describe, expect, it } from "vitest" type Int = number & Brand.Brand<"Int"> diff --git a/packages/schema/test/Schema/getNumberIndexedAccess.test.ts b/packages/effect/test/Schema/Schema/getNumberIndexedAccess.test.ts similarity index 94% rename from packages/schema/test/Schema/getNumberIndexedAccess.test.ts rename to packages/effect/test/Schema/Schema/getNumberIndexedAccess.test.ts index 5049e567ab..ecb6d39640 100644 --- a/packages/schema/test/Schema/getNumberIndexedAccess.test.ts +++ b/packages/effect/test/Schema/Schema/getNumberIndexedAccess.test.ts @@ -1,6 +1,6 @@ -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" import * as Duration from "effect/Duration" +import * as S from "effect/Schema" +import * as Util from "effect/test/Schema/TestUtils" import { describe, it } from "vitest" describe("getNumberIndexedAccess", () => { diff --git a/packages/schema/test/Schema/instanceOf.test.ts b/packages/effect/test/Schema/Schema/instanceOf.test.ts similarity index 85% rename from packages/schema/test/Schema/instanceOf.test.ts rename to packages/effect/test/Schema/Schema/instanceOf.test.ts index 02607da2ab..3ca32739a3 100644 --- a/packages/schema/test/Schema/instanceOf.test.ts +++ b/packages/effect/test/Schema/Schema/instanceOf.test.ts @@ -1,8 +1,8 @@ -import * as AST from "@effect/schema/AST" -import * as P from "@effect/schema/ParseResult" -import * as Pretty from "@effect/schema/Pretty" -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" +import * as P from "effect/ParseResult" +import * as Pretty from "effect/Pretty" +import * as S from "effect/Schema" +import * as AST from "effect/SchemaAST" +import * as Util from "effect/test/Schema/TestUtils" import { describe, expect, it } from "vitest" describe("instanceOf", () => { diff --git a/packages/schema/test/Schema/is.test.ts b/packages/effect/test/Schema/Schema/is.test.ts similarity index 96% rename from packages/schema/test/Schema/is.test.ts rename to packages/effect/test/Schema/Schema/is.test.ts index bbff33b8da..372d543338 100644 --- a/packages/schema/test/Schema/is.test.ts +++ b/packages/effect/test/Schema/Schema/is.test.ts @@ -1,6 +1,6 @@ -import * as P from "@effect/schema/ParseResult" -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" +import * as P from "effect/ParseResult" +import * as S from "effect/Schema" +import * as Util from "effect/test/Schema/TestUtils" import { describe, expect, it } from "vitest" describe("is", () => { @@ -41,10 +41,10 @@ describe("is", () => { }) it("symbol", () => { - const a = Symbol.for("@effect/schema/test/a") + const a = Symbol.for("effect/Schema/test/a") const is = P.is(S.SymbolFromSelf) expect(is(a)).toEqual(true) - expect(is("@effect/schema/test/a")).toEqual(false) + expect(is("effect/Schema/test/a")).toEqual(false) }) it("object", () => { @@ -74,12 +74,12 @@ describe("is", () => { }) it("uniqueSymbolFromSelf", () => { - const a = Symbol.for("@effect/schema/test/a") + const a = Symbol.for("effect/Schema/test/a") const schema = S.UniqueSymbolFromSelf(a) const is = P.is(schema) expect(is(a)).toEqual(true) - expect(is(Symbol.for("@effect/schema/test/a"))).toEqual(true) - expect(is("Symbol(@effect/schema/test/a)")).toEqual(false) + expect(is(Symbol.for("effect/Schema/test/a"))).toEqual(true) + expect(is("Symbol(effect/Schema/test/a)")).toEqual(false) }) it("Numeric enums", () => { @@ -315,7 +315,7 @@ describe("is", () => { }) it("record(string, string)", () => { - const a = Symbol.for("@effect/schema/test/a") + const a = Symbol.for("effect/Schema/test/a") const schema = S.Record({ key: S.String, value: S.String }) const is = P.is(schema) expect(is(null)).toEqual(false) @@ -329,8 +329,8 @@ describe("is", () => { }) it("record(symbol, string)", () => { - const a = Symbol.for("@effect/schema/test/a") - const b = Symbol.for("@effect/schema/test/b") + const a = Symbol.for("effect/Schema/test/a") + const b = Symbol.for("effect/Schema/test/b") const schema = S.Record({ key: S.SymbolFromSelf, value: S.String }) const is = P.is(schema) expect(is(null)).toEqual(false) @@ -372,8 +372,8 @@ describe("is", () => { }) it("record(Symbol('a') | Symbol('b'), number)", () => { - const a = Symbol.for("@effect/schema/test/a") - const b = Symbol.for("@effect/schema/test/b") + const a = Symbol.for("effect/Schema/test/a") + const b = Symbol.for("effect/Schema/test/b") const schema = S.Record({ key: S.Union(S.UniqueSymbolFromSelf(a), S.UniqueSymbolFromSelf(b)), value: S.Number }) const is = P.is(schema) expect(is({ [a]: 1, [b]: 2 })).toEqual(true) diff --git a/packages/schema/test/Schema/isSchema.test.ts b/packages/effect/test/Schema/Schema/isSchema.test.ts similarity index 92% rename from packages/schema/test/Schema/isSchema.test.ts rename to packages/effect/test/Schema/Schema/isSchema.test.ts index 4c4bc3b6c7..0ed96dcb70 100644 --- a/packages/schema/test/Schema/isSchema.test.ts +++ b/packages/effect/test/Schema/Schema/isSchema.test.ts @@ -1,4 +1,4 @@ -import * as S from "@effect/schema/Schema" +import * as S from "effect/Schema" import { describe, expect, it } from "vitest" describe("isSchema", () => { diff --git a/packages/schema/test/Schema/keyof.test.ts b/packages/effect/test/Schema/Schema/keyof.test.ts similarity index 93% rename from packages/schema/test/Schema/keyof.test.ts rename to packages/effect/test/Schema/Schema/keyof.test.ts index b41493e4a0..5b23669547 100644 --- a/packages/schema/test/Schema/keyof.test.ts +++ b/packages/effect/test/Schema/Schema/keyof.test.ts @@ -1,6 +1,6 @@ -import * as AST from "@effect/schema/AST" -import * as P from "@effect/schema/ParseResult" -import * as S from "@effect/schema/Schema" +import * as P from "effect/ParseResult" +import * as S from "effect/Schema" +import * as AST from "effect/SchemaAST" import { describe, expect, it } from "vitest" describe("keyof", () => { @@ -11,7 +11,7 @@ describe("keyof", () => { }) it("should unify symbol literals with symbol", () => { - const a = Symbol.for("@effect/schema/test/a") + const a = Symbol.for("effect/Schema/test/a") const schema = S.Struct({ [a]: S.String }, S.Record({ key: S.SymbolFromSelf, value: S.String })) const keyof = S.keyof(schema) expect(keyof.ast).toEqual(S.SymbolFromSelf.ast) @@ -32,8 +32,8 @@ describe("keyof", () => { }) it("symbol keys", () => { - const a = Symbol.for("@effect/schema/test/a") - const b = Symbol.for("@effect/schema/test/b") + const a = Symbol.for("effect/Schema/test/a") + const b = Symbol.for("effect/Schema/test/b") const schema = S.Struct({ [a]: S.String, [b]: S.Number diff --git a/packages/schema/test/Schema/mutable.test.ts b/packages/effect/test/Schema/Schema/mutable.test.ts similarity index 96% rename from packages/schema/test/Schema/mutable.test.ts rename to packages/effect/test/Schema/Schema/mutable.test.ts index 03408156bf..ea7c6c5eb1 100644 --- a/packages/schema/test/Schema/mutable.test.ts +++ b/packages/effect/test/Schema/Schema/mutable.test.ts @@ -1,6 +1,6 @@ -import * as AST from "@effect/schema/AST" -import * as S from "@effect/schema/Schema" import { identity } from "effect" +import * as S from "effect/Schema" +import * as AST from "effect/SchemaAST" import { describe, expect, it } from "vitest" describe("mutable", () => { diff --git a/packages/schema/test/Schema/nonEmptyArray.test.ts b/packages/effect/test/Schema/Schema/nonEmptyArray.test.ts similarity index 85% rename from packages/schema/test/Schema/nonEmptyArray.test.ts rename to packages/effect/test/Schema/Schema/nonEmptyArray.test.ts index 6a9aede7d4..f22978eca8 100644 --- a/packages/schema/test/Schema/nonEmptyArray.test.ts +++ b/packages/effect/test/Schema/Schema/nonEmptyArray.test.ts @@ -1,5 +1,5 @@ -import * as AST from "@effect/schema/AST" -import * as S from "@effect/schema/Schema" +import * as S from "effect/Schema" +import * as AST from "effect/SchemaAST" import { describe, expect, it } from "vitest" describe("nonEmptyArray", () => { diff --git a/packages/schema/test/Schema/omit.test.ts b/packages/effect/test/Schema/Schema/omit.test.ts similarity index 89% rename from packages/schema/test/Schema/omit.test.ts rename to packages/effect/test/Schema/Schema/omit.test.ts index a349548f3f..99d4f3675d 100644 --- a/packages/schema/test/Schema/omit.test.ts +++ b/packages/effect/test/Schema/Schema/omit.test.ts @@ -1,10 +1,10 @@ -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" +import * as S from "effect/Schema" +import * as Util from "effect/test/Schema/TestUtils" import { describe, expect, it } from "vitest" describe("omit", () => { it("Struct", async () => { - const a = Symbol.for("@effect/schema/test/a") + const a = Symbol.for("effect/Schema/test/a") const schema = S.Struct({ [a]: S.String, b: S.NumberFromString, c: S.Boolean }).pipe( S.omit("c") ) @@ -13,20 +13,20 @@ describe("omit", () => { await Util.expectDecodeUnknownFailure( schema, null, - "Expected { readonly b: NumberFromString; readonly Symbol(@effect/schema/test/a): string }, actual null" + "Expected { readonly b: NumberFromString; readonly Symbol(effect/Schema/test/a): string }, actual null" ) await Util.expectDecodeUnknownFailure( schema, { [a]: "a" }, - `{ readonly b: NumberFromString; readonly Symbol(@effect/schema/test/a): string } + `{ readonly b: NumberFromString; readonly Symbol(effect/Schema/test/a): string } └─ ["b"] └─ is missing` ) await Util.expectDecodeUnknownFailure( schema, { b: "1" }, - `{ readonly b: NumberFromString; readonly Symbol(@effect/schema/test/a): string } -└─ [Symbol(@effect/schema/test/a)] + `{ readonly b: NumberFromString; readonly Symbol(effect/Schema/test/a): string } +└─ [Symbol(effect/Schema/test/a)] └─ is missing` ) }) diff --git a/packages/schema/test/Schema/optional.test.ts b/packages/effect/test/Schema/Schema/optional.test.ts similarity index 96% rename from packages/schema/test/Schema/optional.test.ts rename to packages/effect/test/Schema/Schema/optional.test.ts index 8444cfe546..f9870a57ee 100644 --- a/packages/schema/test/Schema/optional.test.ts +++ b/packages/effect/test/Schema/Schema/optional.test.ts @@ -1,5 +1,5 @@ -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" +import * as S from "effect/Schema" +import * as Util from "effect/test/Schema/TestUtils" import { describe, expect, it } from "vitest" describe("optional", () => { diff --git a/packages/schema/test/Schema/optionalToRequired.test.ts b/packages/effect/test/Schema/Schema/optionalToRequired.test.ts similarity index 87% rename from packages/schema/test/Schema/optionalToRequired.test.ts rename to packages/effect/test/Schema/Schema/optionalToRequired.test.ts index 300eb60e56..b69c6a03c8 100644 --- a/packages/schema/test/Schema/optionalToRequired.test.ts +++ b/packages/effect/test/Schema/Schema/optionalToRequired.test.ts @@ -1,6 +1,6 @@ -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" import * as Option from "effect/Option" +import * as S from "effect/Schema" +import * as Util from "effect/test/Schema/TestUtils" import { describe, it } from "vitest" describe("optionalToRequired", () => { diff --git a/packages/schema/test/Schema/optionalWith.test.ts b/packages/effect/test/Schema/Schema/optionalWith.test.ts similarity index 99% rename from packages/schema/test/Schema/optionalWith.test.ts rename to packages/effect/test/Schema/Schema/optionalWith.test.ts index b573c69d6b..57cc68572e 100644 --- a/packages/schema/test/Schema/optionalWith.test.ts +++ b/packages/effect/test/Schema/Schema/optionalWith.test.ts @@ -1,7 +1,7 @@ -import * as AST from "@effect/schema/AST" -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" import * as O from "effect/Option" +import * as S from "effect/Schema" +import * as AST from "effect/SchemaAST" +import * as Util from "effect/test/Schema/TestUtils" import { describe, expect, it } from "vitest" describe("optionalWith", () => { diff --git a/packages/schema/test/Schema/parseJson.test.ts b/packages/effect/test/Schema/Schema/parseJson.test.ts similarity index 97% rename from packages/schema/test/Schema/parseJson.test.ts rename to packages/effect/test/Schema/Schema/parseJson.test.ts index 0a936e27bc..cbc910e0ca 100644 --- a/packages/schema/test/Schema/parseJson.test.ts +++ b/packages/effect/test/Schema/Schema/parseJson.test.ts @@ -1,6 +1,6 @@ -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" import * as Exit from "effect/Exit" +import * as S from "effect/Schema" +import * as Util from "effect/test/Schema/TestUtils" import { describe, it } from "vitest" describe("parseJson", () => { diff --git a/packages/schema/test/Schema/partial.test.ts b/packages/effect/test/Schema/Schema/partial.test.ts similarity index 97% rename from packages/schema/test/Schema/partial.test.ts rename to packages/effect/test/Schema/Schema/partial.test.ts index e828442b41..2c60179904 100644 --- a/packages/schema/test/Schema/partial.test.ts +++ b/packages/effect/test/Schema/Schema/partial.test.ts @@ -1,5 +1,5 @@ -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" +import * as S from "effect/Schema" +import * as Util from "effect/test/Schema/TestUtils" import { describe, expect, it } from "vitest" describe("partial", () => { diff --git a/packages/schema/test/Schema/partialWith.test.ts b/packages/effect/test/Schema/Schema/partialWith.test.ts similarity index 98% rename from packages/schema/test/Schema/partialWith.test.ts rename to packages/effect/test/Schema/Schema/partialWith.test.ts index 46fa558e9c..b7502cc06d 100644 --- a/packages/schema/test/Schema/partialWith.test.ts +++ b/packages/effect/test/Schema/Schema/partialWith.test.ts @@ -1,6 +1,6 @@ -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" import { identity } from "effect/Function" +import * as S from "effect/Schema" +import * as Util from "effect/test/Schema/TestUtils" import { describe, expect, it } from "vitest" describe("partialWith", () => { diff --git a/packages/schema/test/Schema/pick.test.ts b/packages/effect/test/Schema/Schema/pick.test.ts similarity index 83% rename from packages/schema/test/Schema/pick.test.ts rename to packages/effect/test/Schema/Schema/pick.test.ts index 7b582020a9..76d0674678 100644 --- a/packages/schema/test/Schema/pick.test.ts +++ b/packages/effect/test/Schema/Schema/pick.test.ts @@ -1,10 +1,10 @@ -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" +import * as S from "effect/Schema" +import * as Util from "effect/test/Schema/TestUtils" import { describe, expect, it } from "vitest" describe("pick", () => { it("Struct", async () => { - const a = Symbol.for("@effect/schema/test/a") + const a = Symbol.for("effect/Schema/test/a") const schema = S.Struct({ [a]: S.String, b: S.NumberFromString, c: S.Boolean }).pipe( S.pick(a, "b") ) @@ -13,20 +13,20 @@ describe("pick", () => { await Util.expectDecodeUnknownFailure( schema, null, - "Expected { readonly Symbol(@effect/schema/test/a): string; readonly b: NumberFromString }, actual null" + "Expected { readonly Symbol(effect/Schema/test/a): string; readonly b: NumberFromString }, actual null" ) await Util.expectDecodeUnknownFailure( schema, { [a]: "a" }, - `{ readonly Symbol(@effect/schema/test/a): string; readonly b: NumberFromString } + `{ readonly Symbol(effect/Schema/test/a): string; readonly b: NumberFromString } └─ ["b"] └─ is missing` ) await Util.expectDecodeUnknownFailure( schema, { b: 1 }, - `{ readonly Symbol(@effect/schema/test/a): string; readonly b: NumberFromString } -└─ [Symbol(@effect/schema/test/a)] + `{ readonly Symbol(effect/Schema/test/a): string; readonly b: NumberFromString } +└─ [Symbol(effect/Schema/test/a)] └─ is missing` ) }) @@ -113,22 +113,22 @@ describe("pick", () => { }) it("Record(symbol, number)", async () => { - const a = Symbol.for("@effect/schema/test/a") - const b = Symbol.for("@effect/schema/test/b") + const a = Symbol.for("effect/Schema/test/a") + const b = Symbol.for("effect/Schema/test/b") const schema = S.Record({ key: S.SymbolFromSelf, value: S.Number }).pipe(S.pick(a, b)) await Util.expectDecodeUnknownSuccess(schema, { [a]: 1, [b]: 2 }) await Util.expectDecodeUnknownFailure( schema, { [a]: "a", [b]: 2 }, - `{ readonly Symbol(@effect/schema/test/a): number; readonly Symbol(@effect/schema/test/b): number } -└─ [Symbol(@effect/schema/test/a)] + `{ readonly Symbol(effect/Schema/test/a): number; readonly Symbol(effect/Schema/test/b): number } +└─ [Symbol(effect/Schema/test/a)] └─ Expected number, actual "a"` ) await Util.expectDecodeUnknownFailure( schema, { [a]: 1, [b]: "b" }, - `{ readonly Symbol(@effect/schema/test/a): number; readonly Symbol(@effect/schema/test/b): number } -└─ [Symbol(@effect/schema/test/b)] + `{ readonly Symbol(effect/Schema/test/a): number; readonly Symbol(effect/Schema/test/b): number } +└─ [Symbol(effect/Schema/test/b)] └─ Expected number, actual "b"` ) }) diff --git a/packages/schema/test/Schema/pickLiteral.test.ts b/packages/effect/test/Schema/Schema/pickLiteral.test.ts similarity index 90% rename from packages/schema/test/Schema/pickLiteral.test.ts rename to packages/effect/test/Schema/Schema/pickLiteral.test.ts index f09e34fef5..df257533fa 100644 --- a/packages/schema/test/Schema/pickLiteral.test.ts +++ b/packages/effect/test/Schema/Schema/pickLiteral.test.ts @@ -1,6 +1,6 @@ -import * as AST from "@effect/schema/AST" -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" +import * as S from "effect/Schema" +import * as AST from "effect/SchemaAST" +import * as Util from "effect/test/Schema/TestUtils" import { describe, expect, it } from "vitest" describe("pickLiteral", () => { diff --git a/packages/schema/test/Schema/pipe.test.ts b/packages/effect/test/Schema/Schema/pipe.test.ts similarity index 92% rename from packages/schema/test/Schema/pipe.test.ts rename to packages/effect/test/Schema/Schema/pipe.test.ts index 5e17642bc1..c430d910a8 100644 --- a/packages/schema/test/Schema/pipe.test.ts +++ b/packages/effect/test/Schema/Schema/pipe.test.ts @@ -1,4 +1,4 @@ -import * as S from "@effect/schema/Schema" +import * as S from "effect/Schema" import { describe, expect, it } from "vitest" describe("pipe", () => { diff --git a/packages/schema/test/Schema/pluck.test.ts b/packages/effect/test/Schema/Schema/pluck.test.ts similarity index 97% rename from packages/schema/test/Schema/pluck.test.ts rename to packages/effect/test/Schema/Schema/pluck.test.ts index bc8d1fca9b..5385452634 100644 --- a/packages/schema/test/Schema/pluck.test.ts +++ b/packages/effect/test/Schema/Schema/pluck.test.ts @@ -1,5 +1,5 @@ -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" +import * as S from "effect/Schema" +import * as Util from "effect/test/Schema/TestUtils" import { describe, it } from "vitest" describe("pluck", () => { diff --git a/packages/schema/test/Schema/rename.test.ts b/packages/effect/test/Schema/Schema/rename.test.ts similarity index 92% rename from packages/schema/test/Schema/rename.test.ts rename to packages/effect/test/Schema/Schema/rename.test.ts index 7de14c6065..bfcb6bccf1 100644 --- a/packages/schema/test/Schema/rename.test.ts +++ b/packages/effect/test/Schema/Schema/rename.test.ts @@ -1,5 +1,5 @@ -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" +import * as S from "effect/Schema" +import * as Util from "effect/test/Schema/TestUtils" import { describe, expect, it } from "vitest" describe("rename", () => { @@ -13,7 +13,7 @@ describe("rename", () => { }) it("from string key to symbol key", async () => { - const c = Symbol.for("@effect/schema/test/c") + const c = Symbol.for("effect/Schema/test/c") const schema = S.Struct({ a: S.String, b: S.Number }) const renamed = S.rename(schema, { a: c }) @@ -22,7 +22,7 @@ describe("rename", () => { }) it("from symbol key to string key", async () => { - const a = Symbol.for("@effect/schema/test/a") + const a = Symbol.for("effect/Schema/test/a") const schema = S.Struct({ [a]: S.String, b: S.Number }) const renamed = S.rename(schema, { [a]: "c" }) @@ -31,8 +31,8 @@ describe("rename", () => { }) it("from symbol key to symbol key", async () => { - const a = Symbol.for("@effect/schema/test/a") - const c = Symbol.for("@effect/schema/test/c") + const a = Symbol.for("effect/Schema/test/a") + const c = Symbol.for("effect/Schema/test/c") const schema = S.Struct({ [a]: S.String, b: S.Number }) const renamed = S.rename(schema, { [a]: c }) diff --git a/packages/schema/test/Schema/required.test.ts b/packages/effect/test/Schema/Schema/required.test.ts similarity index 98% rename from packages/schema/test/Schema/required.test.ts rename to packages/effect/test/Schema/Schema/required.test.ts index 05d3738ab5..d311453422 100644 --- a/packages/schema/test/Schema/required.test.ts +++ b/packages/effect/test/Schema/Schema/required.test.ts @@ -1,6 +1,6 @@ -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" import { identity } from "effect/Function" +import * as S from "effect/Schema" +import * as Util from "effect/test/Schema/TestUtils" import { describe, expect, it } from "vitest" describe("required", () => { diff --git a/packages/schema/test/Schema/requiredToOptional.test.ts b/packages/effect/test/Schema/Schema/requiredToOptional.test.ts similarity index 87% rename from packages/schema/test/Schema/requiredToOptional.test.ts rename to packages/effect/test/Schema/Schema/requiredToOptional.test.ts index 4abdfb336a..f4f69d7bd2 100644 --- a/packages/schema/test/Schema/requiredToOptional.test.ts +++ b/packages/effect/test/Schema/Schema/requiredToOptional.test.ts @@ -1,6 +1,6 @@ -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" import * as Option from "effect/Option" +import * as S from "effect/Schema" +import * as Util from "effect/test/Schema/TestUtils" import { describe, it } from "vitest" describe("requiredToOptional", () => { diff --git a/packages/schema/test/Schema/suspend.test.ts b/packages/effect/test/Schema/Schema/suspend.test.ts similarity index 97% rename from packages/schema/test/Schema/suspend.test.ts rename to packages/effect/test/Schema/Schema/suspend.test.ts index c34afcfa3f..6e7dbe5dd0 100644 --- a/packages/schema/test/Schema/suspend.test.ts +++ b/packages/effect/test/Schema/Schema/suspend.test.ts @@ -1,5 +1,5 @@ -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" +import * as S from "effect/Schema" +import * as Util from "effect/test/Schema/TestUtils" import { describe, it } from "vitest" describe("suspend", () => { diff --git a/packages/schema/test/Schema/toString.test.ts b/packages/effect/test/Schema/Schema/toString.test.ts similarity index 95% rename from packages/schema/test/Schema/toString.test.ts rename to packages/effect/test/Schema/Schema/toString.test.ts index 3c8fa3da58..c5729b5c01 100644 --- a/packages/schema/test/Schema/toString.test.ts +++ b/packages/effect/test/Schema/Schema/toString.test.ts @@ -1,4 +1,4 @@ -import * as S from "@effect/schema/Schema" +import * as S from "effect/Schema" import { describe, expect, it } from "vitest" describe("toString", () => { diff --git a/packages/schema/test/Schema/transform.test.ts b/packages/effect/test/Schema/Schema/transform.test.ts similarity index 86% rename from packages/schema/test/Schema/transform.test.ts rename to packages/effect/test/Schema/Schema/transform.test.ts index f12f2fec01..c563978587 100644 --- a/packages/schema/test/Schema/transform.test.ts +++ b/packages/effect/test/Schema/Schema/transform.test.ts @@ -1,5 +1,5 @@ -import * as Schema from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" +import * as Schema from "effect/Schema" +import * as Util from "effect/test/Schema/TestUtils" import { describe, it } from "vitest" describe("transform", () => { diff --git a/packages/schema/test/Schema/transformLiterals.test.ts b/packages/effect/test/Schema/Schema/transformLiterals.test.ts similarity index 92% rename from packages/schema/test/Schema/transformLiterals.test.ts rename to packages/effect/test/Schema/Schema/transformLiterals.test.ts index b60dd20664..1c8f51ade0 100644 --- a/packages/schema/test/Schema/transformLiterals.test.ts +++ b/packages/effect/test/Schema/Schema/transformLiterals.test.ts @@ -1,5 +1,5 @@ -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" +import * as S from "effect/Schema" +import * as Util from "effect/test/Schema/TestUtils" import { describe, it } from "vitest" describe("transformLiteral", () => { diff --git a/packages/schema/test/Schema/transformOrFail.test.ts b/packages/effect/test/Schema/Schema/transformOrFail.test.ts similarity index 81% rename from packages/schema/test/Schema/transformOrFail.test.ts rename to packages/effect/test/Schema/Schema/transformOrFail.test.ts index 61928723b0..6be641db0e 100644 --- a/packages/schema/test/Schema/transformOrFail.test.ts +++ b/packages/effect/test/Schema/Schema/transformOrFail.test.ts @@ -1,6 +1,6 @@ -import * as ParseResult from "@effect/schema/ParseResult" -import * as Schema from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" +import * as ParseResult from "effect/ParseResult" +import * as Schema from "effect/Schema" +import * as Util from "effect/test/Schema/TestUtils" import { describe, it } from "vitest" describe("transformOrFail", () => { diff --git a/packages/schema/test/Schema/typeSchema.test.ts b/packages/effect/test/Schema/Schema/typeSchema.test.ts similarity index 93% rename from packages/schema/test/Schema/typeSchema.test.ts rename to packages/effect/test/Schema/Schema/typeSchema.test.ts index 4503561aba..50730fdcb9 100644 --- a/packages/schema/test/Schema/typeSchema.test.ts +++ b/packages/effect/test/Schema/Schema/typeSchema.test.ts @@ -1,5 +1,5 @@ -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" +import * as S from "effect/Schema" +import * as Util from "effect/test/Schema/TestUtils" import { describe, expect, it } from "vitest" describe("typeSchema", () => { diff --git a/packages/schema/test/Schema/validate.test.ts b/packages/effect/test/Schema/Schema/validate.test.ts similarity index 96% rename from packages/schema/test/Schema/validate.test.ts rename to packages/effect/test/Schema/Schema/validate.test.ts index d1db486e62..9378256e22 100644 --- a/packages/schema/test/Schema/validate.test.ts +++ b/packages/effect/test/Schema/Schema/validate.test.ts @@ -1,5 +1,5 @@ -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" +import * as S from "effect/Schema" +import * as Util from "effect/test/Schema/TestUtils" import { describe, it } from "vitest" const expectValidateSuccess = async ( diff --git a/packages/schema/test/Schema/validateEither.test.ts b/packages/effect/test/Schema/Schema/validateEither.test.ts similarity index 93% rename from packages/schema/test/Schema/validateEither.test.ts rename to packages/effect/test/Schema/Schema/validateEither.test.ts index bff1e432e4..287aff8fb6 100644 --- a/packages/schema/test/Schema/validateEither.test.ts +++ b/packages/effect/test/Schema/Schema/validateEither.test.ts @@ -1,5 +1,5 @@ -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" +import * as S from "effect/Schema" +import * as Util from "effect/test/Schema/TestUtils" import { describe, it } from "vitest" describe("validateEither", () => { diff --git a/packages/schema/test/Schema/validateOption.test.ts b/packages/effect/test/Schema/Schema/validateOption.test.ts similarity index 89% rename from packages/schema/test/Schema/validateOption.test.ts rename to packages/effect/test/Schema/Schema/validateOption.test.ts index c3a1331789..d601961847 100644 --- a/packages/schema/test/Schema/validateOption.test.ts +++ b/packages/effect/test/Schema/Schema/validateOption.test.ts @@ -1,5 +1,5 @@ -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" +import * as S from "effect/Schema" +import * as Util from "effect/test/Schema/TestUtils" import { describe, it } from "vitest" describe("validateOption", () => { diff --git a/packages/schema/test/Schema/validatePromise.test.ts b/packages/effect/test/Schema/Schema/validatePromise.test.ts similarity index 92% rename from packages/schema/test/Schema/validatePromise.test.ts rename to packages/effect/test/Schema/Schema/validatePromise.test.ts index e2cea9793c..ba32a11f9f 100644 --- a/packages/schema/test/Schema/validatePromise.test.ts +++ b/packages/effect/test/Schema/Schema/validatePromise.test.ts @@ -1,5 +1,5 @@ -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" +import * as S from "effect/Schema" +import * as Util from "effect/test/Schema/TestUtils" import { describe, it } from "vitest" describe("validatePromise", () => { diff --git a/packages/schema/test/Schema/validateSync.test.ts b/packages/effect/test/Schema/Schema/validateSync.test.ts similarity index 93% rename from packages/schema/test/Schema/validateSync.test.ts rename to packages/effect/test/Schema/Schema/validateSync.test.ts index 2d46713226..6fac05c279 100644 --- a/packages/schema/test/Schema/validateSync.test.ts +++ b/packages/effect/test/Schema/Schema/validateSync.test.ts @@ -1,5 +1,5 @@ -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" +import * as S from "effect/Schema" +import * as Util from "effect/test/Schema/TestUtils" import { describe, expect, it } from "vitest" describe("validateSync", () => { diff --git a/packages/schema/test/Schema/withConstructorDefault.test.ts b/packages/effect/test/Schema/Schema/withConstructorDefault.test.ts similarity index 95% rename from packages/schema/test/Schema/withConstructorDefault.test.ts rename to packages/effect/test/Schema/Schema/withConstructorDefault.test.ts index 15a621774e..d94cd59a82 100644 --- a/packages/schema/test/Schema/withConstructorDefault.test.ts +++ b/packages/effect/test/Schema/Schema/withConstructorDefault.test.ts @@ -1,4 +1,4 @@ -import * as S from "@effect/schema/Schema" +import * as S from "effect/Schema" import { describe, expect, it } from "vitest" describe("withConstructorDefault", () => { diff --git a/packages/schema/test/Schema/withDecodingDefault.test.ts b/packages/effect/test/Schema/Schema/withDecodingDefault.test.ts similarity index 94% rename from packages/schema/test/Schema/withDecodingDefault.test.ts rename to packages/effect/test/Schema/Schema/withDecodingDefault.test.ts index e6fb853a7d..2ac911a408 100644 --- a/packages/schema/test/Schema/withDecodingDefault.test.ts +++ b/packages/effect/test/Schema/Schema/withDecodingDefault.test.ts @@ -1,5 +1,5 @@ -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" +import * as S from "effect/Schema" +import * as Util from "effect/test/Schema/TestUtils" import { describe, it } from "vitest" describe("withDecodingDefault", () => { diff --git a/packages/schema/test/AST/IndexSignature.test.ts b/packages/effect/test/Schema/SchemaAST/IndexSignature.test.ts similarity index 92% rename from packages/schema/test/AST/IndexSignature.test.ts rename to packages/effect/test/Schema/SchemaAST/IndexSignature.test.ts index 3d5d00af15..d6d3c8b886 100644 --- a/packages/schema/test/AST/IndexSignature.test.ts +++ b/packages/effect/test/Schema/SchemaAST/IndexSignature.test.ts @@ -1,4 +1,4 @@ -import * as AST from "@effect/schema/AST" +import * as AST from "effect/SchemaAST" import { describe, expect, it } from "vitest" describe("AST.IndexSignature", () => { diff --git a/packages/schema/test/AST/Tuple.test.ts b/packages/effect/test/Schema/SchemaAST/Tuple.test.ts similarity index 95% rename from packages/schema/test/AST/Tuple.test.ts rename to packages/effect/test/Schema/SchemaAST/Tuple.test.ts index c41e928802..6af7a2db6b 100644 --- a/packages/schema/test/AST/Tuple.test.ts +++ b/packages/effect/test/Schema/SchemaAST/Tuple.test.ts @@ -1,4 +1,4 @@ -import * as AST from "@effect/schema/AST" +import * as AST from "effect/SchemaAST" import { describe, expect, it } from "vitest" describe("AST.Tuple", () => { diff --git a/packages/schema/test/AST/TypeLiteral.test.ts b/packages/effect/test/Schema/SchemaAST/TypeLiteral.test.ts similarity index 84% rename from packages/schema/test/AST/TypeLiteral.test.ts rename to packages/effect/test/Schema/SchemaAST/TypeLiteral.test.ts index 643ac9be13..472fbc31cc 100644 --- a/packages/schema/test/AST/TypeLiteral.test.ts +++ b/packages/effect/test/Schema/SchemaAST/TypeLiteral.test.ts @@ -1,5 +1,5 @@ -import * as AST from "@effect/schema/AST" -import * as S from "@effect/schema/Schema" +import * as S from "effect/Schema" +import * as AST from "effect/SchemaAST" import { describe, expect, it } from "vitest" describe("AST.TypeLiteral", () => { diff --git a/packages/schema/test/AST/TypeLiteralTransformation.test.ts b/packages/effect/test/Schema/SchemaAST/TypeLiteralTransformation.test.ts similarity index 95% rename from packages/schema/test/AST/TypeLiteralTransformation.test.ts rename to packages/effect/test/Schema/SchemaAST/TypeLiteralTransformation.test.ts index de4447784b..00407feee3 100644 --- a/packages/schema/test/AST/TypeLiteralTransformation.test.ts +++ b/packages/effect/test/Schema/SchemaAST/TypeLiteralTransformation.test.ts @@ -1,5 +1,5 @@ -import * as AST from "@effect/schema/AST" import { identity } from "effect/Function" +import * as AST from "effect/SchemaAST" import { describe, expect, it } from "vitest" describe("AST.TypeLiteralTransformation", () => { diff --git a/packages/schema/test/AST/Union.test.ts b/packages/effect/test/Schema/SchemaAST/Union.test.ts similarity index 95% rename from packages/schema/test/AST/Union.test.ts rename to packages/effect/test/Schema/SchemaAST/Union.test.ts index 1b0ae8385f..b099a1a974 100644 --- a/packages/schema/test/AST/Union.test.ts +++ b/packages/effect/test/Schema/SchemaAST/Union.test.ts @@ -1,5 +1,5 @@ -import * as AST from "@effect/schema/AST" -import * as S from "@effect/schema/Schema" +import * as S from "effect/Schema" +import * as AST from "effect/SchemaAST" import { describe, expect, it } from "vitest" describe("AST.Union", () => { diff --git a/packages/schema/test/AST/annotations.test.ts b/packages/effect/test/Schema/SchemaAST/annotations.test.ts similarity index 93% rename from packages/schema/test/AST/annotations.test.ts rename to packages/effect/test/Schema/SchemaAST/annotations.test.ts index 73619aee85..b1c71a3fe9 100644 --- a/packages/schema/test/AST/annotations.test.ts +++ b/packages/effect/test/Schema/SchemaAST/annotations.test.ts @@ -1,4 +1,4 @@ -import * as AST from "@effect/schema/AST" +import * as AST from "effect/SchemaAST" import { describe, expect, it } from "vitest" describe("annotations", () => { diff --git a/packages/schema/test/AST/encodedAST.test.ts b/packages/effect/test/Schema/SchemaAST/encodedAST.test.ts similarity index 96% rename from packages/schema/test/AST/encodedAST.test.ts rename to packages/effect/test/Schema/SchemaAST/encodedAST.test.ts index d27c642178..c1d416250a 100644 --- a/packages/schema/test/AST/encodedAST.test.ts +++ b/packages/effect/test/Schema/SchemaAST/encodedAST.test.ts @@ -1,5 +1,5 @@ -import * as AST from "@effect/schema/AST" -import * as S from "@effect/schema/Schema" +import * as S from "effect/Schema" +import * as AST from "effect/SchemaAST" import { describe, expect, it } from "vitest" describe("encodedAST", () => { diff --git a/packages/schema/test/AST/encodedBoundAST.test.ts b/packages/effect/test/Schema/SchemaAST/encodedBoundAST.test.ts similarity index 96% rename from packages/schema/test/AST/encodedBoundAST.test.ts rename to packages/effect/test/Schema/SchemaAST/encodedBoundAST.test.ts index 443b7ec7d8..630a3463e7 100644 --- a/packages/schema/test/AST/encodedBoundAST.test.ts +++ b/packages/effect/test/Schema/SchemaAST/encodedBoundAST.test.ts @@ -1,5 +1,5 @@ -import * as AST from "@effect/schema/AST" -import * as S from "@effect/schema/Schema" +import * as S from "effect/Schema" +import * as AST from "effect/SchemaAST" import { describe, expect, it } from "vitest" describe("encodedBoundAST", () => { diff --git a/packages/schema/test/AST/getPropertySignatures.test.ts b/packages/effect/test/Schema/SchemaAST/getPropertySignatures.test.ts similarity index 92% rename from packages/schema/test/AST/getPropertySignatures.test.ts rename to packages/effect/test/Schema/SchemaAST/getPropertySignatures.test.ts index 301643125e..239b5a4996 100644 --- a/packages/schema/test/AST/getPropertySignatures.test.ts +++ b/packages/effect/test/Schema/SchemaAST/getPropertySignatures.test.ts @@ -1,5 +1,5 @@ -import * as AST from "@effect/schema/AST" -import * as S from "@effect/schema/Schema" +import * as S from "effect/Schema" +import * as AST from "effect/SchemaAST" import { describe, expect, it } from "vitest" describe("getPropertySignatures", () => { diff --git a/packages/schema/test/AST/guards.test.ts b/packages/effect/test/Schema/SchemaAST/guards.test.ts similarity index 96% rename from packages/schema/test/AST/guards.test.ts rename to packages/effect/test/Schema/SchemaAST/guards.test.ts index 9176c83fbd..a59846b08c 100644 --- a/packages/schema/test/AST/guards.test.ts +++ b/packages/effect/test/Schema/SchemaAST/guards.test.ts @@ -1,5 +1,5 @@ -import * as AST from "@effect/schema/AST" -import * as S from "@effect/schema/Schema" +import * as S from "effect/Schema" +import * as AST from "effect/SchemaAST" import { describe, expect, it } from "vitest" describe("guards", () => { @@ -62,7 +62,7 @@ describe("guards", () => { }) it("isUniqueSymbol", () => { - expect(AST.isUniqueSymbol(S.UniqueSymbolFromSelf(Symbol.for("@effect/schema/test/a")).ast)).toEqual( + expect(AST.isUniqueSymbol(S.UniqueSymbolFromSelf(Symbol.for("effect/Schema/test/a")).ast)).toEqual( true ) expect(AST.isUniqueSymbol(S.Unknown.ast)).toEqual(false) diff --git a/packages/schema/test/AST/mutable.test.ts b/packages/effect/test/Schema/SchemaAST/mutable.test.ts similarity index 92% rename from packages/schema/test/AST/mutable.test.ts rename to packages/effect/test/Schema/SchemaAST/mutable.test.ts index e999b88168..43c14e0d4e 100644 --- a/packages/schema/test/AST/mutable.test.ts +++ b/packages/effect/test/Schema/SchemaAST/mutable.test.ts @@ -1,6 +1,6 @@ -import * as AST from "@effect/schema/AST" -import * as S from "@effect/schema/Schema" import { identity } from "effect" +import * as S from "effect/Schema" +import * as AST from "effect/SchemaAST" import { describe, expect, it } from "vitest" const expectSameReference = (schema: S.Schema.Any) => { diff --git a/packages/schema/test/AST/partial.test.ts b/packages/effect/test/Schema/SchemaAST/partial.test.ts similarity index 97% rename from packages/schema/test/AST/partial.test.ts rename to packages/effect/test/Schema/SchemaAST/partial.test.ts index 7a873cfdb8..9f9c8b249e 100644 --- a/packages/schema/test/AST/partial.test.ts +++ b/packages/effect/test/Schema/SchemaAST/partial.test.ts @@ -1,5 +1,5 @@ -import * as AST from "@effect/schema/AST" -import * as S from "@effect/schema/Schema" +import * as S from "effect/Schema" +import * as AST from "effect/SchemaAST" import { describe, expect, it } from "vitest" describe("partial", () => { diff --git a/packages/schema/test/AST/pick.test.ts b/packages/effect/test/Schema/SchemaAST/pick.test.ts similarity index 96% rename from packages/schema/test/AST/pick.test.ts rename to packages/effect/test/Schema/SchemaAST/pick.test.ts index 806e09c1f2..40e9d8d7e7 100644 --- a/packages/schema/test/AST/pick.test.ts +++ b/packages/effect/test/Schema/SchemaAST/pick.test.ts @@ -1,5 +1,5 @@ -import * as AST from "@effect/schema/AST" -import * as S from "@effect/schema/Schema" +import * as S from "effect/Schema" +import * as AST from "effect/SchemaAST" import { describe, expect, it } from "vitest" describe("pick", () => { diff --git a/packages/schema/test/AST/record.test.ts b/packages/effect/test/Schema/SchemaAST/record.test.ts similarity index 94% rename from packages/schema/test/AST/record.test.ts rename to packages/effect/test/Schema/SchemaAST/record.test.ts index 5ad696888e..46647ab3b9 100644 --- a/packages/schema/test/AST/record.test.ts +++ b/packages/effect/test/Schema/SchemaAST/record.test.ts @@ -1,4 +1,4 @@ -import * as AST from "@effect/schema/AST" +import * as AST from "effect/SchemaAST" import { describe, expect, it } from "vitest" describe("record", () => { diff --git a/packages/schema/test/AST/suspend.test.ts b/packages/effect/test/Schema/SchemaAST/suspend.test.ts similarity index 85% rename from packages/schema/test/AST/suspend.test.ts rename to packages/effect/test/Schema/SchemaAST/suspend.test.ts index b96fd968f6..8096266f57 100644 --- a/packages/schema/test/AST/suspend.test.ts +++ b/packages/effect/test/Schema/SchemaAST/suspend.test.ts @@ -1,6 +1,6 @@ -import type * as AST from "@effect/schema/AST" -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" +import * as S from "effect/Schema" +import type * as AST from "effect/SchemaAST" +import * as Util from "effect/test/Schema/TestUtils" import { describe, expect, it } from "vitest" describe("AST.Suspend", () => { diff --git a/packages/schema/test/AST/toString.test.ts b/packages/effect/test/Schema/SchemaAST/toString.test.ts similarity index 95% rename from packages/schema/test/AST/toString.test.ts rename to packages/effect/test/Schema/SchemaAST/toString.test.ts index 809f9d0078..c7971e3809 100644 --- a/packages/schema/test/AST/toString.test.ts +++ b/packages/effect/test/Schema/SchemaAST/toString.test.ts @@ -1,4 +1,4 @@ -import * as S from "@effect/schema/Schema" +import * as S from "effect/Schema" import { describe, expect, it } from "vitest" describe("toString", () => { diff --git a/packages/schema/test/AST/typeAST.test.ts b/packages/effect/test/Schema/SchemaAST/typeAST.test.ts similarity index 96% rename from packages/schema/test/AST/typeAST.test.ts rename to packages/effect/test/Schema/SchemaAST/typeAST.test.ts index e386663da5..937ba1879f 100644 --- a/packages/schema/test/AST/typeAST.test.ts +++ b/packages/effect/test/Schema/SchemaAST/typeAST.test.ts @@ -1,5 +1,5 @@ -import * as AST from "@effect/schema/AST" -import * as S from "@effect/schema/Schema" +import * as S from "effect/Schema" +import * as AST from "effect/SchemaAST" import { describe, expect, it } from "vitest" describe("typeAST", () => { diff --git a/packages/schema/test/AST/unify.test.ts b/packages/effect/test/Schema/SchemaAST/unify.test.ts similarity index 96% rename from packages/schema/test/AST/unify.test.ts rename to packages/effect/test/Schema/SchemaAST/unify.test.ts index 283be60d63..65a5d1a051 100644 --- a/packages/schema/test/AST/unify.test.ts +++ b/packages/effect/test/Schema/SchemaAST/unify.test.ts @@ -1,5 +1,5 @@ -import * as AST from "@effect/schema/AST" -import * as S from "@effect/schema/Schema" +import * as S from "effect/Schema" +import * as AST from "effect/SchemaAST" import { describe, expect, it } from "vitest" const expectUnify = (input: Array, expected: Array) => { diff --git a/packages/schema/test/Equivalence.test.ts b/packages/effect/test/Schema/SchemaEquivalence.test.ts similarity index 97% rename from packages/schema/test/Equivalence.test.ts rename to packages/effect/test/Schema/SchemaEquivalence.test.ts index e8c4c3ab98..e5b2b0c0a9 100644 --- a/packages/schema/test/Equivalence.test.ts +++ b/packages/effect/test/Schema/SchemaEquivalence.test.ts @@ -1,6 +1,4 @@ -import * as A from "@effect/schema/Arbitrary" -import * as E from "@effect/schema/Equivalence" -import * as S from "@effect/schema/Schema" +import * as A from "effect/Arbitrary" import * as Chunk from "effect/Chunk" import * as Data from "effect/Data" import * as Either from "effect/Either" @@ -9,6 +7,8 @@ import * as Equivalence from "effect/Equivalence" import * as Hash from "effect/Hash" import * as Option from "effect/Option" import { isUnknown } from "effect/Predicate" +import * as S from "effect/Schema" +import * as E from "effect/SchemaEquivalence" import * as fc from "fast-check" import { describe, expect, it } from "vitest" @@ -71,7 +71,7 @@ const MySymbol = S.SymbolFromSelf.annotations({ } }) -describe("Equivalence", () => { +describe("SchemaEquivalence", () => { it("the errors should disply a path", () => { expect(() => E.make(S.Tuple(S.Never as any))).toThrow( new Error(`Unsupported schema @@ -460,7 +460,7 @@ schema (NeverKeyword): never`) expect(equivalence({ a: "a", b: 1 }, { a: "a", b: 1 })).toBe(true) // should ignore excess properties - const d = Symbol.for("@effect/schema/test/d") + const d = Symbol.for("effect/Schema/test/d") const excess = { a: "a", b: 1, @@ -476,14 +476,14 @@ schema (NeverKeyword): never`) }) it("symbol keys", () => { - const a = Symbol.for("@effect/schema/test/a") - const b = Symbol.for("@effect/schema/test/b") + const a = Symbol.for("effect/Schema/test/a") + const b = Symbol.for("effect/Schema/test/b") const schema = S.Struct({ [a]: MyString, [b]: MyNumber }) const equivalence = E.make(schema) expect(equivalence({ [a]: "a", [b]: 1 }, { [a]: "a", [b]: 1 })).toBe(true) // should ignore excess properties - const d = Symbol.for("@effect/schema/test/d") + const d = Symbol.for("effect/Schema/test/d") const excess = { [a]: "a", [b]: 1, @@ -540,7 +540,7 @@ schema (NeverKeyword): never`) expect(equivalence({ a: 1 }, { a: 1 })).toBe(true) expect(equivalence({ a: 1, b: 2 }, { a: 1, b: 2 })).toBe(true) // should ignore symbol excess properties - const d = Symbol.for("@effect/schema/test/d") + const d = Symbol.for("effect/Schema/test/d") expect(equivalence({ a: 1, b: 2 }, { a: 1, b: 2, [d]: "d" })).toBe(true) expect(equivalence({ a: 1 }, { a: 2 })).toBe(false) @@ -555,8 +555,8 @@ schema (NeverKeyword): never`) const schema = S.Record({ key: MySymbol, value: MyNumber }) const equivalence = E.make(schema) - const a = Symbol.for("@effect/schema/test/a") - const b = Symbol.for("@effect/schema/test/b") + const a = Symbol.for("effect/Schema/test/a") + const b = Symbol.for("effect/Schema/test/b") expect(equivalence({}, {})).toBe(true) expect(equivalence({ [a]: 1 }, { [a]: 1 })).toBe(true) expect(equivalence({ [a]: 1, [b]: 2 }, { [a]: 1, [b]: 2 })).toBe(true) diff --git a/packages/schema/test/Formatter.test.ts b/packages/effect/test/Schema/SchemaFormatters.test.ts similarity index 98% rename from packages/schema/test/Formatter.test.ts rename to packages/effect/test/Schema/SchemaFormatters.test.ts index 15c62552cc..51d83e9357 100644 --- a/packages/schema/test/Formatter.test.ts +++ b/packages/effect/test/Schema/SchemaFormatters.test.ts @@ -1,15 +1,15 @@ -import * as ArrayFormatter from "@effect/schema/ArrayFormatter" -import type { ParseOptions } from "@effect/schema/AST" -import * as AST from "@effect/schema/AST" -import * as ParseResult from "@effect/schema/ParseResult" -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" -import * as TreeFormatter from "@effect/schema/TreeFormatter" import * as Context from "effect/Context" import * as Effect from "effect/Effect" import * as Either from "effect/Either" import { identity, pipe } from "effect/Function" import * as Option from "effect/Option" +import * as ParseResult from "effect/ParseResult" +import * as S from "effect/Schema" +import * as ArrayFormatter from "effect/SchemaArrayFormatter" +import type { ParseOptions } from "effect/SchemaAST" +import * as AST from "effect/SchemaAST" +import * as TreeFormatter from "effect/SchemaTreeFormatter" +import * as Util from "effect/test/Schema/TestUtils" import { describe, expect, it } from "vitest" const options: ParseOptions = { errors: "all", onExcessProperty: "error" } @@ -21,7 +21,7 @@ const expectIssues = (schema: S.Schema, input: unknown, issues: Arra expect(result).toStrictEqual(Either.left(issues)) } -describe("Formatter", () => { +describe("SchemaFormatters", () => { describe("Forbidden", () => { it("default message", () => { const schema = Util.effectify(S.String) diff --git a/packages/effect/test/Schema/SchemaInternal.test.ts b/packages/effect/test/Schema/SchemaInternal.test.ts new file mode 100644 index 0000000000..612e2e7bd8 --- /dev/null +++ b/packages/effect/test/Schema/SchemaInternal.test.ts @@ -0,0 +1,39 @@ +import * as util from "effect/internal/schema/util" +import { describe, expect, it } from "vitest" + +describe("effect/internal/schema/util", () => { + it("ownKeys", () => { + expect(util.ownKeys({})).toStrictEqual([]) + expect(util.ownKeys({ a: 1 })).toStrictEqual(["a"]) + expect(util.ownKeys({ a: 1, b: 2 })).toStrictEqual(["a", "b"]) + const a = Symbol.for("effect/Schema/test/a") + const b = Symbol.for("effect/Schema/test/b") + expect(util.ownKeys({ [a]: 3, [b]: 4 })).toStrictEqual([a, b]) + expect(util.ownKeys({ a: 1, [a]: 3, b: 2, [b]: 4 })).toStrictEqual(["a", "b", a, b]) + }) + + describe("formatUnknown", () => { + it("should format symbol property signatures", () => { + expect(util.formatUnknown({ [Symbol.for("a")]: 1 })).toEqual("{Symbol(a):1}") + }) + + it("should handle unexpected errors", () => { + const circular: any = { a: null } + circular.a = circular + expect(util.formatUnknown(circular)).toEqual("[object Object]") + }) + + it("should detect data types with a custom `toString` implementation", () => { + const noToString = { a: 1 } + expect(util.formatUnknown(noToString)).toEqual(`{"a":1}`) + const ToString = Object.create({ + toString() { + return "toString custom implementation" + } + }) + expect(util.formatUnknown(ToString)).toEqual("toString custom implementation") + // should not detect arrays + expect(util.formatUnknown([1, 2, 3])).toEqual("[1,2,3]") + }) + }) +}) diff --git a/packages/schema/test/userland.test.ts b/packages/effect/test/Schema/SchemaUserland.test.ts similarity index 94% rename from packages/schema/test/userland.test.ts rename to packages/effect/test/Schema/SchemaUserland.test.ts index ca2ad618d1..17904941a7 100644 --- a/packages/schema/test/userland.test.ts +++ b/packages/effect/test/Schema/SchemaUserland.test.ts @@ -1,9 +1,8 @@ /** * It contains a collection of user-defined APIs to keep track of what might break in the event of breaking changes. */ -import { AST, Schema } from "@effect/schema" -import * as Util from "@effect/schema/test/TestUtils" -import { Record } from "effect" +import { Record, Schema, SchemaAST as AST } from "effect" +import * as Util from "effect/test/Schema/TestUtils" import { describe, expect, it } from "vitest" const structTypeSchema = ( @@ -20,7 +19,7 @@ const structTypeSchema = ( } })) as any -describe("userland", () => { +describe("SchemaUserland", () => { it("structTypeSchema", () => { // Discord: https://discordapp.com/channels/795981131316985866/847382157861060618/1266533881788502096 // goal: `Schema.typeSchema` for structs, retaining the type diff --git a/packages/schema/test/Serializable.test.ts b/packages/effect/test/Schema/Serializable.test.ts similarity index 96% rename from packages/schema/test/Serializable.test.ts rename to packages/effect/test/Schema/Serializable.test.ts index 4c78b2132c..0cf09343f6 100644 --- a/packages/schema/test/Serializable.test.ts +++ b/packages/effect/test/Schema/Serializable.test.ts @@ -1,6 +1,6 @@ -import * as S from "@effect/schema/Schema" -import * as Serializable from "@effect/schema/Serializable" import { Effect, Exit } from "effect" +import * as S from "effect/Schema" +import * as Serializable from "effect/Serializable" import { assert, describe, test } from "vitest" class Person extends S.Class("Person")({ diff --git a/packages/schema/test/TestUtils.ts b/packages/effect/test/Schema/TestUtils.ts similarity index 96% rename from packages/schema/test/TestUtils.ts rename to packages/effect/test/Schema/TestUtils.ts index fcd5da4191..e573ee0791 100644 --- a/packages/schema/test/TestUtils.ts +++ b/packages/effect/test/Schema/TestUtils.ts @@ -1,16 +1,16 @@ -import * as A from "@effect/schema/Arbitrary" -import type { ParseOptions } from "@effect/schema/AST" -import * as AST from "@effect/schema/AST" -import { getFinalTransformation } from "@effect/schema/ParseResult" -import * as ParseResult from "@effect/schema/ParseResult" -import * as S from "@effect/schema/Schema" -import { formatErrorSync } from "@effect/schema/TreeFormatter" +import * as A from "effect/Arbitrary" import * as Context from "effect/Context" import * as Duration from "effect/Duration" import * as Effect from "effect/Effect" import * as Either from "effect/Either" import * as Option from "effect/Option" +import { getFinalTransformation } from "effect/ParseResult" +import * as ParseResult from "effect/ParseResult" import * as Runtime from "effect/Runtime" +import * as S from "effect/Schema" +import type { ParseOptions } from "effect/SchemaAST" +import * as AST from "effect/SchemaAST" +import { formatErrorSync } from "effect/SchemaTreeFormatter" import * as fc from "fast-check" import { assert, expect } from "vitest" diff --git a/packages/experimental/src/ChannelSchema.ts b/packages/experimental/src/ChannelSchema.ts index 988fe5ceed..033d980387 100644 --- a/packages/experimental/src/ChannelSchema.ts +++ b/packages/experimental/src/ChannelSchema.ts @@ -1,12 +1,12 @@ /** * @since 1.0.0 */ -import type { ParseError } from "@effect/schema/ParseResult" -import * as Schema from "@effect/schema/Schema" import type * as Cause from "effect/Cause" import * as Channel from "effect/Channel" import type * as Chunk from "effect/Chunk" import { dual, pipe } from "effect/Function" +import type { ParseError } from "effect/ParseResult" +import * as Schema from "effect/Schema" /** * @since 1.0.0 diff --git a/packages/experimental/src/DevTools/Domain.ts b/packages/experimental/src/DevTools/Domain.ts index 0771bac1f9..7b859b4d59 100644 --- a/packages/experimental/src/DevTools/Domain.ts +++ b/packages/experimental/src/DevTools/Domain.ts @@ -1,8 +1,8 @@ /** * @since 1.0.0 */ -import * as Schema from "@effect/schema/Schema" import type { Option } from "effect/Option" +import * as Schema from "effect/Schema" /** * @since 1.0.0 diff --git a/packages/experimental/src/Machine.ts b/packages/experimental/src/Machine.ts index b61dabd741..1fe3fda6aa 100644 --- a/packages/experimental/src/Machine.ts +++ b/packages/experimental/src/Machine.ts @@ -1,9 +1,6 @@ /** * @since 1.0.0 */ -import type * as ParseResult from "@effect/schema/ParseResult" -import * as Schema from "@effect/schema/Schema" -import * as Serializable from "@effect/schema/Serializable" import * as Arr from "effect/Array" import * as Cause from "effect/Cause" import * as Context from "effect/Context" @@ -17,6 +14,7 @@ import * as FiberSet from "effect/FiberSet" import { dual, identity, pipe } from "effect/Function" import { globalValue } from "effect/GlobalValue" import * as Option from "effect/Option" +import type * as ParseResult from "effect/ParseResult" import type { Pipeable } from "effect/Pipeable" import { pipeArguments } from "effect/Pipeable" import * as PubSub from "effect/PubSub" @@ -24,7 +22,9 @@ import * as Queue from "effect/Queue" import * as Readable from "effect/Readable" import type { Request } from "effect/Request" import type * as Schedule from "effect/Schedule" +import * as Schema from "effect/Schema" import type * as Scope from "effect/Scope" +import * as Serializable from "effect/Serializable" import * as Stream from "effect/Stream" import * as Subscribable from "effect/Subscribable" import * as Tracer from "effect/Tracer" diff --git a/packages/experimental/src/Machine/Procedure.ts b/packages/experimental/src/Machine/Procedure.ts index b85a1daebb..c2b38e4290 100644 --- a/packages/experimental/src/Machine/Procedure.ts +++ b/packages/experimental/src/Machine/Procedure.ts @@ -1,13 +1,13 @@ /** * @since 1.0.0 */ -import type * as Schema from "@effect/schema/Schema" -import type * as Serializable from "@effect/schema/Serializable" import type * as Deferred from "effect/Deferred" import type * as Effect from "effect/Effect" import { type Pipeable, pipeArguments } from "effect/Pipeable" import * as Predicate from "effect/Predicate" import type { Request } from "effect/Request" +import type * as Schema from "effect/Schema" +import type * as Serializable from "effect/Serializable" /** * @since 1.0.0 diff --git a/packages/experimental/src/Machine/SerializableProcedureList.ts b/packages/experimental/src/Machine/SerializableProcedureList.ts index 99ba09b2fd..99a9166d1e 100644 --- a/packages/experimental/src/Machine/SerializableProcedureList.ts +++ b/packages/experimental/src/Machine/SerializableProcedureList.ts @@ -1,10 +1,10 @@ /** * @since 1.0.0 */ -import type * as Schema from "@effect/schema/Schema" -import type * as Serializable from "@effect/schema/Serializable" import type * as Effect from "effect/Effect" import { dual } from "effect/Function" +import type * as Schema from "effect/Schema" +import type * as Serializable from "effect/Serializable" import type * as Types from "effect/Types" import * as Procedure from "./Procedure.js" import * as ProcedureList from "./ProcedureList.js" diff --git a/packages/experimental/src/MsgPack.ts b/packages/experimental/src/MsgPack.ts index 5a1070ff9a..3352e233c8 100644 --- a/packages/experimental/src/MsgPack.ts +++ b/packages/experimental/src/MsgPack.ts @@ -1,13 +1,13 @@ /** * @since 1.0.0 */ -import type { ParseError } from "@effect/schema/ParseResult" -import type * as Schema from "@effect/schema/Schema" import * as Channel from "effect/Channel" import * as Chunk from "effect/Chunk" import * as Data from "effect/Data" import * as Effect from "effect/Effect" import { dual } from "effect/Function" +import type { ParseError } from "effect/ParseResult" +import type * as Schema from "effect/Schema" import { Packr, Unpackr } from "msgpackr" import * as ChannelSchema from "./ChannelSchema.js" diff --git a/packages/experimental/src/Ndjson.ts b/packages/experimental/src/Ndjson.ts index 6bbb9df31d..cd950499ec 100644 --- a/packages/experimental/src/Ndjson.ts +++ b/packages/experimental/src/Ndjson.ts @@ -2,13 +2,13 @@ * @since 1.0.0 */ import { TypeIdError } from "@effect/platform/Error" -import type { ParseError } from "@effect/schema/ParseResult" -import type * as Schema from "@effect/schema/Schema" import type * as Cause from "effect/Cause" import * as Channel from "effect/Channel" import * as Chunk from "effect/Chunk" import * as Effect from "effect/Effect" import { dual, identity } from "effect/Function" +import type { ParseError } from "effect/ParseResult" +import type * as Schema from "effect/Schema" import * as ChannelSchema from "./ChannelSchema.js" /** diff --git a/packages/experimental/src/PersistedCache.ts b/packages/experimental/src/PersistedCache.ts index 63a7a16c6f..8a599181ca 100644 --- a/packages/experimental/src/PersistedCache.ts +++ b/packages/experimental/src/PersistedCache.ts @@ -1,7 +1,6 @@ /** * @since 1.0.0 */ -import type * as Serializable from "@effect/schema/Serializable" import * as Cache from "effect/Cache" import * as Data from "effect/Data" import type * as Duration from "effect/Duration" @@ -11,6 +10,7 @@ import { identity, pipe } from "effect/Function" import * as Hash from "effect/Hash" import * as Option from "effect/Option" import type * as Scope from "effect/Scope" +import type * as Serializable from "effect/Serializable" import * as Tracer from "effect/Tracer" import * as Persistence from "./Persistence.js" diff --git a/packages/experimental/src/Persistence.ts b/packages/experimental/src/Persistence.ts index 109bd073e8..054d7ccae0 100644 --- a/packages/experimental/src/Persistence.ts +++ b/packages/experimental/src/Persistence.ts @@ -3,9 +3,6 @@ */ import { TypeIdError } from "@effect/platform/Error" import * as KeyValueStore from "@effect/platform/KeyValueStore" -import type * as ParseResult from "@effect/schema/ParseResult" -import * as Serializable from "@effect/schema/Serializable" -import * as TreeFormatter from "@effect/schema/TreeFormatter" import type * as Clock from "effect/Clock" import * as Context from "effect/Context" import * as Duration from "effect/Duration" @@ -14,8 +11,11 @@ import type * as Exit from "effect/Exit" import { identity } from "effect/Function" import * as Layer from "effect/Layer" import * as Option from "effect/Option" +import type * as ParseResult from "effect/ParseResult" import * as PrimaryKey from "effect/PrimaryKey" +import * as TreeFormatter from "effect/SchemaTreeFormatter" import type * as Scope from "effect/Scope" +import * as Serializable from "effect/Serializable" /** * @since 1.0.0 diff --git a/packages/experimental/src/RequestResolver.ts b/packages/experimental/src/RequestResolver.ts index 64bc85f9cd..21a1a30b3d 100644 --- a/packages/experimental/src/RequestResolver.ts +++ b/packages/experimental/src/RequestResolver.ts @@ -1,7 +1,6 @@ /** * @since 1.0.0 */ -import type * as Serializable from "@effect/schema/Serializable" import * as Arr from "effect/Array" import * as Deferred from "effect/Deferred" import type * as Duration from "effect/Duration" @@ -13,6 +12,7 @@ import * as Option from "effect/Option" import * as Request from "effect/Request" import * as RequestResolver from "effect/RequestResolver" import type * as Scope from "effect/Scope" +import type * as Serializable from "effect/Serializable" import * as Persistence from "./Persistence.js" interface DataLoaderItem> { diff --git a/packages/experimental/src/VariantSchema.ts b/packages/experimental/src/VariantSchema.ts index baa072dfae..c3bfa43e86 100644 --- a/packages/experimental/src/VariantSchema.ts +++ b/packages/experimental/src/VariantSchema.ts @@ -1,15 +1,15 @@ /** * @since 1.0.0 */ -import type * as AST from "@effect/schema/AST" -import * as ParseResult from "@effect/schema/ParseResult" -import * as Schema from "@effect/schema/Schema" import type { Brand } from "effect/Brand" import type * as Effect from "effect/Effect" import { constUndefined, dual } from "effect/Function" import * as Option from "effect/Option" +import * as ParseResult from "effect/ParseResult" import { type Pipeable, pipeArguments } from "effect/Pipeable" import * as Predicate from "effect/Predicate" +import * as Schema from "effect/Schema" +import type * as AST from "effect/SchemaAST" import * as Struct_ from "effect/Struct" /** diff --git a/packages/platform-browser/test/fixtures/schema.ts b/packages/platform-browser/test/fixtures/schema.ts index 41dc3e098a..b1875d4910 100644 --- a/packages/platform-browser/test/fixtures/schema.ts +++ b/packages/platform-browser/test/fixtures/schema.ts @@ -1,5 +1,5 @@ import * as Transferable from "@effect/platform/Transferable" -import * as Schema from "@effect/schema/Schema" +import * as Schema from "effect/Schema" export class User extends Schema.Class("User")({ id: Schema.Number, diff --git a/packages/platform-node/test/HttpClient.test.ts b/packages/platform-node/test/HttpClient.test.ts index 80e116a31f..29768f8a0b 100644 --- a/packages/platform-node/test/HttpClient.test.ts +++ b/packages/platform-node/test/HttpClient.test.ts @@ -1,11 +1,11 @@ import { HttpClient, HttpClientRequest, HttpClientResponse } from "@effect/platform" import * as NodeClient from "@effect/platform-node/NodeHttpClient" -import * as Schema from "@effect/schema/Schema" import { describe, expect, it } from "@effect/vitest" import { Struct } from "effect" import * as Context from "effect/Context" import * as Effect from "effect/Effect" import * as Layer from "effect/Layer" +import * as Schema from "effect/Schema" import * as Stream from "effect/Stream" const Todo = Schema.Struct({ diff --git a/packages/platform-node/test/HttpServer.test.ts b/packages/platform-node/test/HttpServer.test.ts index ebbd9877f4..69c8bf1927 100644 --- a/packages/platform-node/test/HttpServer.test.ts +++ b/packages/platform-node/test/HttpServer.test.ts @@ -16,11 +16,11 @@ import { UrlParams } from "@effect/platform" import { NodeHttpServer } from "@effect/platform-node" -import * as Schema from "@effect/schema/Schema" import { assert, describe, expect, it } from "@effect/vitest" import { Deferred, Duration, Fiber, Stream } from "effect" import * as Effect from "effect/Effect" import * as Option from "effect/Option" +import * as Schema from "effect/Schema" import * as Tracer from "effect/Tracer" import * as Buffer from "node:buffer" diff --git a/packages/platform/src/Headers.ts b/packages/platform/src/Headers.ts index 844c751552..ec2508e5fd 100644 --- a/packages/platform/src/Headers.ts +++ b/packages/platform/src/Headers.ts @@ -1,7 +1,6 @@ /** * @since 1.0.0 */ -import * as Schema from "@effect/schema/Schema" import * as FiberRef from "effect/FiberRef" import { dual, identity } from "effect/Function" import { globalValue } from "effect/GlobalValue" @@ -9,6 +8,7 @@ import type * as Option from "effect/Option" import * as Predicate from "effect/Predicate" import * as Record from "effect/Record" import * as Redacted from "effect/Redacted" +import * as Schema from "effect/Schema" import * as String from "effect/String" import type { Mutable } from "effect/Types" diff --git a/packages/platform/src/HttpApi.ts b/packages/platform/src/HttpApi.ts index dc361cf102..2a941edf7f 100644 --- a/packages/platform/src/HttpApi.ts +++ b/packages/platform/src/HttpApi.ts @@ -1,8 +1,6 @@ /** * @since 1.0.0 */ -import * as AST from "@effect/schema/AST" -import type * as Schema from "@effect/schema/Schema" import * as Chunk from "effect/Chunk" import * as Context from "effect/Context" import { dual } from "effect/Function" @@ -10,6 +8,8 @@ import * as Option from "effect/Option" import type { Pipeable } from "effect/Pipeable" import { pipeArguments } from "effect/Pipeable" import * as Predicate from "effect/Predicate" +import type * as Schema from "effect/Schema" +import * as AST from "effect/SchemaAST" import * as HttpApiEndpoint from "./HttpApiEndpoint.js" import { HttpApiDecodeError } from "./HttpApiError.js" import * as HttpApiGroup from "./HttpApiGroup.js" diff --git a/packages/platform/src/HttpApiBuilder.ts b/packages/platform/src/HttpApiBuilder.ts index 1ead6c5cad..ca427949a8 100644 --- a/packages/platform/src/HttpApiBuilder.ts +++ b/packages/platform/src/HttpApiBuilder.ts @@ -1,9 +1,6 @@ /** * @since 1.0.0 */ -import * as AST from "@effect/schema/AST" -import * as ParseResult from "@effect/schema/ParseResult" -import * as Schema from "@effect/schema/Schema" import * as Chunk from "effect/Chunk" import * as Context from "effect/Context" import * as Effect from "effect/Effect" @@ -14,9 +11,12 @@ import { globalValue } from "effect/GlobalValue" import * as Layer from "effect/Layer" import type { ManagedRuntime } from "effect/ManagedRuntime" import * as Option from "effect/Option" +import * as ParseResult from "effect/ParseResult" import { type Pipeable, pipeArguments } from "effect/Pipeable" import type { ReadonlyRecord } from "effect/Record" import * as Redacted from "effect/Redacted" +import * as Schema from "effect/Schema" +import * as AST from "effect/SchemaAST" import type { Scope } from "effect/Scope" import type { Covariant, Mutable, NoInfer } from "effect/Types" import { unify } from "effect/Unify" diff --git a/packages/platform/src/HttpApiClient.ts b/packages/platform/src/HttpApiClient.ts index ee98f97d48..48d7cbf241 100644 --- a/packages/platform/src/HttpApiClient.ts +++ b/packages/platform/src/HttpApiClient.ts @@ -1,12 +1,12 @@ /** * @since 1.0.0 */ -import * as ParseResult from "@effect/schema/ParseResult" -import * as Schema from "@effect/schema/Schema" import * as Context from "effect/Context" import * as Effect from "effect/Effect" import { identity } from "effect/Function" import * as Option from "effect/Option" +import * as ParseResult from "effect/ParseResult" +import * as Schema from "effect/Schema" import type { Simplify } from "effect/Types" import * as HttpApi from "./HttpApi.js" import type { HttpApiEndpoint } from "./HttpApiEndpoint.js" diff --git a/packages/platform/src/HttpApiEndpoint.ts b/packages/platform/src/HttpApiEndpoint.ts index 941168537e..0135dbaf6f 100644 --- a/packages/platform/src/HttpApiEndpoint.ts +++ b/packages/platform/src/HttpApiEndpoint.ts @@ -1,7 +1,6 @@ /** * @since 1.0.0 */ -import * as Schema from "@effect/schema/Schema" import type { Brand } from "effect/Brand" import * as Context from "effect/Context" import type { Effect } from "effect/Effect" @@ -10,6 +9,7 @@ import * as Option from "effect/Option" import { type Pipeable, pipeArguments } from "effect/Pipeable" import * as Predicate from "effect/Predicate" import type { Redacted } from "effect/Redacted" +import * as Schema from "effect/Schema" import type * as Types from "effect/Types" import * as HttpApiSchema from "./HttpApiSchema.js" import type { HttpMethod } from "./HttpMethod.js" diff --git a/packages/platform/src/HttpApiError.ts b/packages/platform/src/HttpApiError.ts index 10c414fbdc..dd73bf4b0a 100644 --- a/packages/platform/src/HttpApiError.ts +++ b/packages/platform/src/HttpApiError.ts @@ -1,12 +1,12 @@ /** * @since 1.0.0 */ -import * as ArrayFormatter from "@effect/schema/ArrayFormatter" -import type * as ParseResult from "@effect/schema/ParseResult" -import * as Schema from "@effect/schema/Schema" -import * as TreeFormatter from "@effect/schema/TreeFormatter" import * as Effect from "effect/Effect" import { identity } from "effect/Function" +import type * as ParseResult from "effect/ParseResult" +import * as Schema from "effect/Schema" +import * as ArrayFormatter from "effect/SchemaArrayFormatter" +import * as TreeFormatter from "effect/SchemaTreeFormatter" import * as HttpApiSchema from "./HttpApiSchema.js" /** diff --git a/packages/platform/src/HttpApiGroup.ts b/packages/platform/src/HttpApiGroup.ts index d64550c609..0275037cac 100644 --- a/packages/platform/src/HttpApiGroup.ts +++ b/packages/platform/src/HttpApiGroup.ts @@ -1,12 +1,12 @@ /** * @since 1.0.0 */ -import * as Schema from "@effect/schema/Schema" import * as Chunk from "effect/Chunk" import * as Context from "effect/Context" import { dual } from "effect/Function" import { type Pipeable, pipeArguments } from "effect/Pipeable" import * as Predicate from "effect/Predicate" +import * as Schema from "effect/Schema" import * as HttpApiEndpoint from "./HttpApiEndpoint.js" import type { HttpApiDecodeError } from "./HttpApiError.js" import * as HttpApiSchema from "./HttpApiSchema.js" diff --git a/packages/platform/src/HttpApiSchema.ts b/packages/platform/src/HttpApiSchema.ts index 96774b3656..b471b86ff7 100644 --- a/packages/platform/src/HttpApiSchema.ts +++ b/packages/platform/src/HttpApiSchema.ts @@ -1,11 +1,11 @@ /** * @since 1.0.0 */ -import * as AST from "@effect/schema/AST" -import * as Schema from "@effect/schema/Schema" import type { Brand } from "effect/Brand" import type { LazyArg } from "effect/Function" import { constVoid, dual } from "effect/Function" +import * as Schema from "effect/Schema" +import * as AST from "effect/SchemaAST" import * as Struct from "effect/Struct" /** diff --git a/packages/platform/src/HttpBody.ts b/packages/platform/src/HttpBody.ts index 8aeeabf834..29e89ec427 100644 --- a/packages/platform/src/HttpBody.ts +++ b/packages/platform/src/HttpBody.ts @@ -1,11 +1,11 @@ /** * @since 1.0.0 */ -import type * as ParseResult from "@effect/schema/ParseResult" -import type * as Schema from "@effect/schema/Schema" import type * as Effect from "effect/Effect" import type { Inspectable } from "effect/Inspectable" +import type * as ParseResult from "effect/ParseResult" import * as Predicate from "effect/Predicate" +import type * as Schema from "effect/Schema" import type * as Stream_ from "effect/Stream" import type * as PlatformError from "./Error.js" import type * as FileSystem from "./FileSystem.js" diff --git a/packages/platform/src/HttpClientRequest.ts b/packages/platform/src/HttpClientRequest.ts index 68d511f51a..2a9b188458 100644 --- a/packages/platform/src/HttpClientRequest.ts +++ b/packages/platform/src/HttpClientRequest.ts @@ -1,13 +1,13 @@ /** * @since 1.0.0 */ -import type { ParseOptions } from "@effect/schema/AST" -import type * as Schema from "@effect/schema/Schema" import type * as Effect from "effect/Effect" import type { Inspectable } from "effect/Inspectable" import type * as Option from "effect/Option" import type { Pipeable } from "effect/Pipeable" import type { Redacted } from "effect/Redacted" +import type * as Schema from "effect/Schema" +import type { ParseOptions } from "effect/SchemaAST" import type * as Stream from "effect/Stream" import type * as PlatformError from "./Error.js" import type * as FileSystem from "./FileSystem.js" diff --git a/packages/platform/src/HttpClientResponse.ts b/packages/platform/src/HttpClientResponse.ts index 8455e36a82..6aa820bc18 100644 --- a/packages/platform/src/HttpClientResponse.ts +++ b/packages/platform/src/HttpClientResponse.ts @@ -1,10 +1,10 @@ /** * @since 1.0.0 */ -import type { ParseOptions } from "@effect/schema/AST" -import type * as ParseResult from "@effect/schema/ParseResult" -import type * as Schema from "@effect/schema/Schema" import type * as Effect from "effect/Effect" +import type * as ParseResult from "effect/ParseResult" +import type * as Schema from "effect/Schema" +import type { ParseOptions } from "effect/SchemaAST" import type * as Scope from "effect/Scope" import type * as Stream from "effect/Stream" import type { Unify } from "effect/Unify" diff --git a/packages/platform/src/HttpIncomingMessage.ts b/packages/platform/src/HttpIncomingMessage.ts index e217a94ebc..0422d1ab3f 100644 --- a/packages/platform/src/HttpIncomingMessage.ts +++ b/packages/platform/src/HttpIncomingMessage.ts @@ -1,15 +1,15 @@ /** * @since 1.0.0 */ -import type { ParseOptions } from "@effect/schema/AST" -import type * as ParseResult from "@effect/schema/ParseResult" -import * as Schema from "@effect/schema/Schema" import * as Effect from "effect/Effect" import * as FiberRef from "effect/FiberRef" import { dual } from "effect/Function" import * as Global from "effect/GlobalValue" import type { Inspectable } from "effect/Inspectable" import * as Option from "effect/Option" +import type * as ParseResult from "effect/ParseResult" +import * as Schema from "effect/Schema" +import type { ParseOptions } from "effect/SchemaAST" import type * as Stream from "effect/Stream" import * as FileSystem from "./FileSystem.js" import type * as Headers from "./Headers.js" diff --git a/packages/platform/src/HttpRouter.ts b/packages/platform/src/HttpRouter.ts index 174833405a..565a007c77 100644 --- a/packages/platform/src/HttpRouter.ts +++ b/packages/platform/src/HttpRouter.ts @@ -1,9 +1,6 @@ /** * @since 1.0.0 */ -import type { ParseOptions } from "@effect/schema/AST" -import type * as ParseResult from "@effect/schema/ParseResult" -import type * as Schema from "@effect/schema/Schema" import type * as Cause from "effect/Cause" import type * as Chunk from "effect/Chunk" import type * as Context from "effect/Context" @@ -12,6 +9,9 @@ import type { FiberRef } from "effect/FiberRef" import type { Inspectable } from "effect/Inspectable" import type * as Layer from "effect/Layer" import type * as Option from "effect/Option" +import type * as ParseResult from "effect/ParseResult" +import type * as Schema from "effect/Schema" +import type { ParseOptions } from "effect/SchemaAST" import type * as Scope from "effect/Scope" import type { RouterConfig } from "find-my-way-ts" import type * as Etag from "./Etag.js" diff --git a/packages/platform/src/HttpServerRequest.ts b/packages/platform/src/HttpServerRequest.ts index 3944778ca2..27bc396554 100644 --- a/packages/platform/src/HttpServerRequest.ts +++ b/packages/platform/src/HttpServerRequest.ts @@ -1,15 +1,15 @@ /** * @since 1.0.0 */ -import type { ParseOptions } from "@effect/schema/AST" -import type * as ParseResult from "@effect/schema/ParseResult" -import type * as Schema from "@effect/schema/Schema" import type { Channel } from "effect/Channel" import type { Chunk } from "effect/Chunk" import type * as Context from "effect/Context" import type * as Effect from "effect/Effect" import type { Option } from "effect/Option" +import type * as ParseResult from "effect/ParseResult" import type { ReadonlyRecord } from "effect/Record" +import type * as Schema from "effect/Schema" +import type { ParseOptions } from "effect/SchemaAST" import type * as Scope from "effect/Scope" import type * as Stream from "effect/Stream" import type * as FileSystem from "./FileSystem.js" diff --git a/packages/platform/src/HttpServerRespondable.ts b/packages/platform/src/HttpServerRespondable.ts index b18d0de6e1..d6f809d295 100644 --- a/packages/platform/src/HttpServerRespondable.ts +++ b/packages/platform/src/HttpServerRespondable.ts @@ -1,9 +1,9 @@ /** * @since 1.0.0 */ -import * as ParseResult from "@effect/schema/ParseResult" import * as Cause from "effect/Cause" import * as Effect from "effect/Effect" +import * as ParseResult from "effect/ParseResult" import { hasProperty } from "effect/Predicate" import type { HttpServerResponse } from "./HttpServerResponse.js" import * as ServerResponse from "./HttpServerResponse.js" diff --git a/packages/platform/src/HttpServerResponse.ts b/packages/platform/src/HttpServerResponse.ts index 3a2ed00f8f..283de3adf0 100644 --- a/packages/platform/src/HttpServerResponse.ts +++ b/packages/platform/src/HttpServerResponse.ts @@ -1,11 +1,11 @@ /** * @since 1.0.0 */ -import type { ParseOptions } from "@effect/schema/AST" -import type * as Schema from "@effect/schema/Schema" import type * as Effect from "effect/Effect" import type { Inspectable } from "effect/Inspectable" import type * as Runtime from "effect/Runtime" +import type * as Schema from "effect/Schema" +import type { ParseOptions } from "effect/SchemaAST" import type * as Stream from "effect/Stream" import type { Cookie, Cookies, CookiesError } from "./Cookies.js" import type * as PlatformError from "./Error.js" diff --git a/packages/platform/src/KeyValueStore.ts b/packages/platform/src/KeyValueStore.ts index 113bbcdec0..d92d2a5c87 100644 --- a/packages/platform/src/KeyValueStore.ts +++ b/packages/platform/src/KeyValueStore.ts @@ -1,13 +1,13 @@ /** * @since 1.0.0 */ -import type * as ParseResult from "@effect/schema/ParseResult" -import type * as Schema from "@effect/schema/Schema" import type * as Context from "effect/Context" import type * as Effect from "effect/Effect" import type { LazyArg } from "effect/Function" import type * as Layer from "effect/Layer" import type * as Option from "effect/Option" +import type * as ParseResult from "effect/ParseResult" +import type * as Schema from "effect/Schema" import type * as PlatformError from "./Error.js" import type * as FileSystem from "./FileSystem.js" import * as internal from "./internal/keyValueStore.js" diff --git a/packages/platform/src/Multipart.ts b/packages/platform/src/Multipart.ts index 9dfab4386c..ee0dce2c45 100644 --- a/packages/platform/src/Multipart.ts +++ b/packages/platform/src/Multipart.ts @@ -1,9 +1,6 @@ /** * @since 1.0.0 */ -import type { ParseOptions } from "@effect/schema/AST" -import type * as ParseResult from "@effect/schema/ParseResult" -import type * as Schema from "@effect/schema/Schema" import type { YieldableError } from "effect/Cause" import type * as Channel from "effect/Channel" import type * as Chunk from "effect/Chunk" @@ -11,6 +8,9 @@ import type * as Effect from "effect/Effect" import type * as FiberRef from "effect/FiberRef" import type { Inspectable } from "effect/Inspectable" import type * as Option from "effect/Option" +import type * as ParseResult from "effect/ParseResult" +import type * as Schema from "effect/Schema" +import type { ParseOptions } from "effect/SchemaAST" import type * as Scope from "effect/Scope" import type * as Stream from "effect/Stream" import type * as Multipasta from "multipasta" diff --git a/packages/platform/src/OpenApi.ts b/packages/platform/src/OpenApi.ts index 2e51812423..f447d2a282 100644 --- a/packages/platform/src/OpenApi.ts +++ b/packages/platform/src/OpenApi.ts @@ -1,12 +1,12 @@ /** * @since 1.0.0 */ -import * as AST from "@effect/schema/AST" -import * as Schema from "@effect/schema/Schema" import * as Context from "effect/Context" import { dual } from "effect/Function" import * as Option from "effect/Option" import type { ReadonlyRecord } from "effect/Record" +import * as Schema from "effect/Schema" +import * as AST from "effect/SchemaAST" import type { DeepMutable, Mutable } from "effect/Types" import * as HttpApi from "./HttpApi.js" import * as HttpApiSchema from "./HttpApiSchema.js" diff --git a/packages/platform/src/OpenApiJsonSchema.ts b/packages/platform/src/OpenApiJsonSchema.ts index aa376b821f..4151a69e20 100644 --- a/packages/platform/src/OpenApiJsonSchema.ts +++ b/packages/platform/src/OpenApiJsonSchema.ts @@ -1,13 +1,13 @@ /** * @since 1.0.0 */ -import * as AST from "@effect/schema/AST" -import type * as ParseResult from "@effect/schema/ParseResult" -import type * as Schema from "@effect/schema/Schema" import * as Arr from "effect/Array" import * as Option from "effect/Option" +import type * as ParseResult from "effect/ParseResult" import * as Predicate from "effect/Predicate" import * as Record from "effect/Record" +import type * as Schema from "effect/Schema" +import * as AST from "effect/SchemaAST" /** * @category model @@ -704,9 +704,9 @@ const isNonEmpty = (x: ParseResult.SingleOrNonEmpty): x is Arr.NonEmptyRea const formatPropertyKey = (name: PropertyKey): string => typeof name === "string" ? JSON.stringify(name) : String(name) -const ParseJsonTypeId: unique symbol = Symbol.for("@effect/schema/TypeId/ParseJson") -const SurrogateAnnotationId = Symbol.for("@effect/schema/annotation/Surrogate") -const JSONIdentifierAnnotationId = Symbol.for("@effect/schema/annotation/JSONIdentifier") +const ParseJsonTypeId: unique symbol = Symbol.for("effect/Schema/TypeId/ParseJson") +const SurrogateAnnotationId = Symbol.for("effect/Schema/annotation/Surrogate") +const JSONIdentifierAnnotationId = Symbol.for("effect/Schema/annotation/JSONIdentifier") const getSurrogateAnnotation = AST.getAnnotation(SurrogateAnnotationId) const getJSONIdentifierAnnotation = AST.getAnnotation(JSONIdentifierAnnotationId) diff --git a/packages/platform/src/Transferable.ts b/packages/platform/src/Transferable.ts index 13f4d31466..12fd445b78 100644 --- a/packages/platform/src/Transferable.ts +++ b/packages/platform/src/Transferable.ts @@ -1,12 +1,12 @@ /** * @since 1.0.0 */ -import * as ParseResult from "@effect/schema/ParseResult" -import * as Schema from "@effect/schema/Schema" import * as Context from "effect/Context" import * as Effect from "effect/Effect" import { dual } from "effect/Function" import * as Option from "effect/Option" +import * as ParseResult from "effect/ParseResult" +import * as Schema from "effect/Schema" /** * @since 1.0.0 diff --git a/packages/platform/src/UrlParams.ts b/packages/platform/src/UrlParams.ts index 0777ecaaf1..df69cb0966 100644 --- a/packages/platform/src/UrlParams.ts +++ b/packages/platform/src/UrlParams.ts @@ -1,14 +1,14 @@ /** * @since 1.0.0 */ -import type { ParseOptions } from "@effect/schema/AST" -import type * as ParseResult from "@effect/schema/ParseResult" -import * as Schema from "@effect/schema/Schema" import * as Arr from "effect/Array" import type * as Effect from "effect/Effect" import * as Either from "effect/Either" import { dual } from "effect/Function" import * as Option from "effect/Option" +import type * as ParseResult from "effect/ParseResult" +import * as Schema from "effect/Schema" +import type { ParseOptions } from "effect/SchemaAST" /** * @since 1.0.0 diff --git a/packages/platform/src/Worker.ts b/packages/platform/src/Worker.ts index 683bc751dc..43739cbe10 100644 --- a/packages/platform/src/Worker.ts +++ b/packages/platform/src/Worker.ts @@ -1,17 +1,17 @@ /** * @since 1.0.0 */ -import type * as ParseResult from "@effect/schema/ParseResult" -import type * as Schema from "@effect/schema/Schema" -import type * as Serializable from "@effect/schema/Serializable" import type * as Context from "effect/Context" import type * as Deferred from "effect/Deferred" import type * as Duration from "effect/Duration" import type * as Effect from "effect/Effect" import type { LazyArg } from "effect/Function" import type * as Layer from "effect/Layer" +import type * as ParseResult from "effect/ParseResult" import type * as Pool from "effect/Pool" +import type * as Schema from "effect/Schema" import type * as Scope from "effect/Scope" +import type * as Serializable from "effect/Serializable" import type * as Stream from "effect/Stream" import * as internal from "./internal/worker.js" import type { WorkerError, WorkerErrorFrom } from "./WorkerError.js" diff --git a/packages/platform/src/WorkerError.ts b/packages/platform/src/WorkerError.ts index f30822301e..79c4497898 100644 --- a/packages/platform/src/WorkerError.ts +++ b/packages/platform/src/WorkerError.ts @@ -1,9 +1,9 @@ /** * @since 1.0.0 */ -import * as Schema from "@effect/schema/Schema" import type * as Cause from "effect/Cause" import * as Predicate from "effect/Predicate" +import * as Schema from "effect/Schema" import * as internal from "./internal/workerError.js" /** diff --git a/packages/platform/src/WorkerRunner.ts b/packages/platform/src/WorkerRunner.ts index 07ebf0e70a..48553a438d 100644 --- a/packages/platform/src/WorkerRunner.ts +++ b/packages/platform/src/WorkerRunner.ts @@ -1,12 +1,12 @@ /** * @since 1.0.0 */ -import type * as Schema from "@effect/schema/Schema" -import type * as Serializable from "@effect/schema/Serializable" import type * as Context from "effect/Context" import type * as Effect from "effect/Effect" import type * as Layer from "effect/Layer" +import type * as Schema from "effect/Schema" import type * as Scope from "effect/Scope" +import type * as Serializable from "effect/Serializable" import type * as Stream from "effect/Stream" import * as internal from "./internal/workerRunner.js" import type { WorkerError } from "./WorkerError.js" diff --git a/packages/platform/src/internal/httpBody.ts b/packages/platform/src/internal/httpBody.ts index 4f43140a2a..4096469b6b 100644 --- a/packages/platform/src/internal/httpBody.ts +++ b/packages/platform/src/internal/httpBody.ts @@ -1,9 +1,9 @@ -import type { ParseOptions } from "@effect/schema/AST" -import * as Schema from "@effect/schema/Schema" import * as Data from "effect/Data" import * as Effect from "effect/Effect" import { identity } from "effect/Function" import * as Inspectable from "effect/Inspectable" +import * as Schema from "effect/Schema" +import type { ParseOptions } from "effect/SchemaAST" import * as Stream_ from "effect/Stream" import type * as PlatformError from "../Error.js" import * as FileSystem from "../FileSystem.js" diff --git a/packages/platform/src/internal/httpClientRequest.ts b/packages/platform/src/internal/httpClientRequest.ts index 7f5aa36565..8d57094805 100644 --- a/packages/platform/src/internal/httpClientRequest.ts +++ b/packages/platform/src/internal/httpClientRequest.ts @@ -1,11 +1,11 @@ -import type { ParseOptions } from "@effect/schema/AST" -import type * as Schema from "@effect/schema/Schema" import * as Effect from "effect/Effect" import { dual } from "effect/Function" import * as Inspectable from "effect/Inspectable" import * as Option from "effect/Option" import { pipeArguments } from "effect/Pipeable" import * as Redacted from "effect/Redacted" +import type * as Schema from "effect/Schema" +import type { ParseOptions } from "effect/SchemaAST" import type * as Stream from "effect/Stream" import type * as PlatformError from "../Error.js" import type * as FileSystem from "../FileSystem.js" diff --git a/packages/platform/src/internal/httpClientResponse.ts b/packages/platform/src/internal/httpClientResponse.ts index 540ffd90e6..d5a902a778 100644 --- a/packages/platform/src/internal/httpClientResponse.ts +++ b/packages/platform/src/internal/httpClientResponse.ts @@ -1,10 +1,10 @@ -import type { ParseOptions } from "@effect/schema/AST" -import type * as ParseResult from "@effect/schema/ParseResult" -import * as Schema from "@effect/schema/Schema" import * as Effect from "effect/Effect" import { dual } from "effect/Function" import * as Inspectable from "effect/Inspectable" import * as Option from "effect/Option" +import type * as ParseResult from "effect/ParseResult" +import * as Schema from "effect/Schema" +import type { ParseOptions } from "effect/SchemaAST" import * as Stream from "effect/Stream" import type { Unify } from "effect/Unify" import * as Cookies from "../Cookies.js" diff --git a/packages/platform/src/internal/httpRouter.ts b/packages/platform/src/internal/httpRouter.ts index dd8954bb32..9d53fec2c9 100644 --- a/packages/platform/src/internal/httpRouter.ts +++ b/packages/platform/src/internal/httpRouter.ts @@ -1,5 +1,3 @@ -import type { ParseOptions } from "@effect/schema/AST" -import * as Schema from "@effect/schema/Schema" import type * as Cause from "effect/Cause" import * as Chunk from "effect/Chunk" import * as Context from "effect/Context" @@ -12,6 +10,8 @@ import * as Inspectable from "effect/Inspectable" import * as Layer from "effect/Layer" import * as Option from "effect/Option" import * as Predicate from "effect/Predicate" +import * as Schema from "effect/Schema" +import type { ParseOptions } from "effect/SchemaAST" import * as Tracer from "effect/Tracer" import type { Mutable } from "effect/Types" import * as FindMyWay from "find-my-way-ts" diff --git a/packages/platform/src/internal/httpServerRequest.ts b/packages/platform/src/internal/httpServerRequest.ts index 78e25531c1..56880eac11 100644 --- a/packages/platform/src/internal/httpServerRequest.ts +++ b/packages/platform/src/internal/httpServerRequest.ts @@ -1,12 +1,12 @@ -import type { ParseOptions } from "@effect/schema/AST" -import type * as ParseResult from "@effect/schema/ParseResult" -import * as Schema from "@effect/schema/Schema" import * as Channel from "effect/Channel" import * as Context from "effect/Context" import * as Effect from "effect/Effect" import * as Inspectable from "effect/Inspectable" import * as Option from "effect/Option" +import type * as ParseResult from "effect/ParseResult" import type { ReadonlyRecord } from "effect/Record" +import * as Schema from "effect/Schema" +import type { ParseOptions } from "effect/SchemaAST" import type * as Scope from "effect/Scope" import * as Stream from "effect/Stream" import * as Cookies from "../Cookies.js" diff --git a/packages/platform/src/internal/httpServerResponse.ts b/packages/platform/src/internal/httpServerResponse.ts index 6648e66555..dc1a46601f 100644 --- a/packages/platform/src/internal/httpServerResponse.ts +++ b/packages/platform/src/internal/httpServerResponse.ts @@ -1,11 +1,11 @@ -import type { ParseOptions } from "@effect/schema/AST" -import type * as Schema from "@effect/schema/Schema" import * as Context from "effect/Context" import * as Effect from "effect/Effect" import * as Effectable from "effect/Effectable" import { dual } from "effect/Function" import * as Inspectable from "effect/Inspectable" import * as Runtime from "effect/Runtime" +import type * as Schema from "effect/Schema" +import type { ParseOptions } from "effect/SchemaAST" import * as Stream from "effect/Stream" import * as Cookies from "../Cookies.js" import type * as PlatformError from "../Error.js" diff --git a/packages/platform/src/internal/keyValueStore.ts b/packages/platform/src/internal/keyValueStore.ts index 244e43fc79..76f1ae29c4 100644 --- a/packages/platform/src/internal/keyValueStore.ts +++ b/packages/platform/src/internal/keyValueStore.ts @@ -1,4 +1,3 @@ -import * as Schema from "@effect/schema/Schema" import * as Context from "effect/Context" import * as Effect from "effect/Effect" import * as Either from "effect/Either" @@ -7,6 +6,7 @@ import type { LazyArg } from "effect/Function" import { dual, identity, pipe } from "effect/Function" import * as Layer from "effect/Layer" import * as Option from "effect/Option" +import * as Schema from "effect/Schema" import * as PlatformError from "../Error.js" import * as FileSystem from "../FileSystem.js" import type * as KeyValueStore from "../KeyValueStore.js" diff --git a/packages/platform/src/internal/multipart.ts b/packages/platform/src/internal/multipart.ts index 127a2bba15..4c64926460 100644 --- a/packages/platform/src/internal/multipart.ts +++ b/packages/platform/src/internal/multipart.ts @@ -1,6 +1,3 @@ -import type { ParseOptions } from "@effect/schema/AST" -import type * as ParseResult from "@effect/schema/ParseResult" -import * as Schema from "@effect/schema/Schema" import * as Cause from "effect/Cause" import * as Channel from "effect/Channel" import * as Chunk from "effect/Chunk" @@ -10,8 +7,11 @@ import { dual, flow, pipe } from "effect/Function" import { globalValue } from "effect/GlobalValue" import * as Inspectable from "effect/Inspectable" import * as Option from "effect/Option" +import type * as ParseResult from "effect/ParseResult" import * as Predicate from "effect/Predicate" import * as Queue from "effect/Queue" +import * as Schema from "effect/Schema" +import type { ParseOptions } from "effect/SchemaAST" import type * as Scope from "effect/Scope" import type * as AsyncInput from "effect/SingleProducerAsyncInput" import * as Stream from "effect/Stream" diff --git a/packages/platform/src/internal/worker.ts b/packages/platform/src/internal/worker.ts index 2e89e2d80c..e90c819ebd 100644 --- a/packages/platform/src/internal/worker.ts +++ b/packages/platform/src/internal/worker.ts @@ -1,5 +1,3 @@ -import * as Schema from "@effect/schema/Schema" -import * as Serializable from "@effect/schema/Serializable" import * as Channel from "effect/Channel" import * as Context from "effect/Context" import * as Deferred from "effect/Deferred" @@ -13,7 +11,9 @@ import * as Mailbox from "effect/Mailbox" import * as Option from "effect/Option" import * as Pool from "effect/Pool" import * as Schedule from "effect/Schedule" +import * as Schema from "effect/Schema" import * as Scope from "effect/Scope" +import * as Serializable from "effect/Serializable" import * as Stream from "effect/Stream" import * as Tracer from "effect/Tracer" import * as Transferable from "../Transferable.js" diff --git a/packages/platform/src/internal/workerRunner.ts b/packages/platform/src/internal/workerRunner.ts index 8ab9aa57cc..0861508ab9 100644 --- a/packages/platform/src/internal/workerRunner.ts +++ b/packages/platform/src/internal/workerRunner.ts @@ -1,5 +1,3 @@ -import * as Schema from "@effect/schema/Schema" -import * as Serializable from "@effect/schema/Serializable" import * as Cause from "effect/Cause" import * as Chunk from "effect/Chunk" import * as Context from "effect/Context" @@ -9,7 +7,9 @@ import * as Fiber from "effect/Fiber" import { pipe } from "effect/Function" import * as Layer from "effect/Layer" import * as Schedule from "effect/Schedule" +import * as Schema from "effect/Schema" import type * as Scope from "effect/Scope" +import * as Serializable from "effect/Serializable" import * as Stream from "effect/Stream" import * as Transferable from "../Transferable.js" import type * as Worker from "../Worker.js" diff --git a/packages/platform/test/HttpClient.test.ts b/packages/platform/test/HttpClient.test.ts index a27fa7afa4..3f0ac85494 100644 --- a/packages/platform/test/HttpClient.test.ts +++ b/packages/platform/test/HttpClient.test.ts @@ -6,13 +6,13 @@ import { HttpClientResponse, UrlParams } from "@effect/platform" -import * as Schema from "@effect/schema/Schema" import { assert, describe, expect, it } from "@effect/vitest" import { Either, Ref, Struct } from "effect" import * as Context from "effect/Context" import * as Effect from "effect/Effect" import * as Layer from "effect/Layer" import * as Logger from "effect/Logger" +import * as Schema from "effect/Schema" import * as Stream from "effect/Stream" const Todo = Schema.Struct({ diff --git a/packages/platform/test/KeyValueStore.test.ts b/packages/platform/test/KeyValueStore.test.ts index 8e66a2bce2..9ba1f9fd15 100644 --- a/packages/platform/test/KeyValueStore.test.ts +++ b/packages/platform/test/KeyValueStore.test.ts @@ -1,9 +1,9 @@ import * as KeyValueStore from "@effect/platform/KeyValueStore" -import * as Schema from "@effect/schema/Schema" import * as Effect from "effect/Effect" import { identity } from "effect/Function" import * as Layer from "effect/Layer" import * as Option from "effect/Option" +import * as Schema from "effect/Schema" import { afterEach, describe, expect, it } from "vitest" export const testLayer = (layer: Layer.Layer) => { diff --git a/packages/platform/test/OpenApiJsonSchema.test.ts b/packages/platform/test/OpenApiJsonSchema.test.ts index e40bac81f5..be83d847a9 100644 --- a/packages/platform/test/OpenApiJsonSchema.test.ts +++ b/packages/platform/test/OpenApiJsonSchema.test.ts @@ -1,7 +1,7 @@ import * as JsonSchema from "@effect/platform/OpenApiJsonSchema" -import * as A from "@effect/schema/Arbitrary" -import * as Schema from "@effect/schema/Schema" import AjvNonEsm from "ajv/dist/2019.js" +import * as A from "effect/Arbitrary" +import * as Schema from "effect/Schema" import * as fc from "fast-check" import { describe, expect, it } from "vitest" @@ -102,10 +102,10 @@ schema (SymbolKeyword): symbol` it("a unique symbol should raise an error", () => { expectError( - Schema.UniqueSymbolFromSelf(Symbol.for("@effect/schema/test/a")), + Schema.UniqueSymbolFromSelf(Symbol.for("effect/Schema/test/a")), `Missing annotation details: Generating a JSON Schema for this schema requires a "jsonSchema" annotation -schema (UniqueSymbol): Symbol(@effect/schema/test/a)` +schema (UniqueSymbol): Symbol(effect/Schema/test/a)` ) }) @@ -736,11 +736,11 @@ schema (Declaration): DateFromSelf` }) it("should raise an error if there is a property named with a symbol", () => { - const a = Symbol.for("@effect/schema/test/a") + const a = Symbol.for("effect/Schema/test/a") expectError( Schema.Struct({ [a]: Schema.String }), `Unsupported key -details: Cannot encode Symbol(@effect/schema/test/a) key to JSON Schema` +details: Cannot encode Symbol(effect/Schema/test/a) key to JSON Schema` ) }) diff --git a/packages/rpc-http/src/HttpRpcResolver.ts b/packages/rpc-http/src/HttpRpcResolver.ts index 5fea8629a8..8d2c710ff5 100644 --- a/packages/rpc-http/src/HttpRpcResolver.ts +++ b/packages/rpc-http/src/HttpRpcResolver.ts @@ -7,11 +7,11 @@ import * as ClientRequest from "@effect/platform/HttpClientRequest" import type * as Rpc from "@effect/rpc/Rpc" import * as Resolver from "@effect/rpc/RpcResolver" import type * as Router from "@effect/rpc/RpcRouter" -import type * as Serializable from "@effect/schema/Serializable" import * as Chunk from "effect/Chunk" import * as Effect from "effect/Effect" import type * as RequestResolver from "effect/RequestResolver" import * as Schedule from "effect/Schedule" +import type * as Serializable from "effect/Serializable" import * as Stream from "effect/Stream" /** diff --git a/packages/rpc-http/src/HttpRpcResolverNoStream.ts b/packages/rpc-http/src/HttpRpcResolverNoStream.ts index 6850d52810..f77fe5adad 100644 --- a/packages/rpc-http/src/HttpRpcResolverNoStream.ts +++ b/packages/rpc-http/src/HttpRpcResolverNoStream.ts @@ -8,10 +8,10 @@ import type * as Rpc from "@effect/rpc/Rpc" import * as Resolver from "@effect/rpc/RpcResolver" import * as ResolverNoStream from "@effect/rpc/RpcResolverNoStream" import type * as Router from "@effect/rpc/RpcRouter" -import type * as Serializable from "@effect/schema/Serializable" import * as Effect from "effect/Effect" import type * as RequestResolver from "effect/RequestResolver" import * as Schedule from "effect/Schedule" +import type * as Serializable from "effect/Serializable" /** * @category constructors diff --git a/packages/rpc-http/src/HttpRpcRouterNoStream.ts b/packages/rpc-http/src/HttpRpcRouterNoStream.ts index eee7606500..401255035a 100644 --- a/packages/rpc-http/src/HttpRpcRouterNoStream.ts +++ b/packages/rpc-http/src/HttpRpcRouterNoStream.ts @@ -6,9 +6,9 @@ import type * as ServerError from "@effect/platform/HttpServerError" import * as ServerRequest from "@effect/platform/HttpServerRequest" import * as ServerResponse from "@effect/platform/HttpServerResponse" import * as Router from "@effect/rpc/RpcRouter" -import type { ParseError } from "@effect/schema/ParseResult" import * as Effect from "effect/Effect" import { dual } from "effect/Function" +import type { ParseError } from "effect/ParseResult" /** * @since 1.0.0 diff --git a/packages/rpc/src/Rpc.ts b/packages/rpc/src/Rpc.ts index 36ffbb4ad4..7d462a6843 100644 --- a/packages/rpc/src/Rpc.ts +++ b/packages/rpc/src/Rpc.ts @@ -2,21 +2,21 @@ * @since 1.0.0 */ import * as Headers from "@effect/platform/Headers" -import type * as ParseResult from "@effect/schema/ParseResult" -import * as Schema from "@effect/schema/Schema" -import type * as Serializable from "@effect/schema/Serializable" import type * as Context from "effect/Context" import * as Effect from "effect/Effect" import * as FiberRef from "effect/FiberRef" import { dual, pipe } from "effect/Function" import { globalValue } from "effect/GlobalValue" +import type * as ParseResult from "effect/ParseResult" import { type Pipeable, pipeArguments } from "effect/Pipeable" import * as Predicate from "effect/Predicate" import type * as PrimaryKey from "effect/PrimaryKey" import type * as Record from "effect/Record" import type * as EffectRequest from "effect/Request" import type * as RequestResolver from "effect/RequestResolver" +import * as Schema from "effect/Schema" import type { Scope } from "effect/Scope" +import type * as Serializable from "effect/Serializable" import * as Stream from "effect/Stream" import type * as Types from "effect/Types" import * as Internal from "./internal/rpc.js" diff --git a/packages/rpc/src/RpcResolver.ts b/packages/rpc/src/RpcResolver.ts index 3c38c8e6a5..e362efec67 100644 --- a/packages/rpc/src/RpcResolver.ts +++ b/packages/rpc/src/RpcResolver.ts @@ -2,16 +2,16 @@ * @since 1.0.0 */ import * as Headers from "@effect/platform/Headers" -import type { ParseError } from "@effect/schema/ParseResult" -import * as Schema from "@effect/schema/Schema" -import * as Serializable from "@effect/schema/Serializable" import * as Arr from "effect/Array" import * as Cause from "effect/Cause" import * as Effect from "effect/Effect" import * as Exit from "effect/Exit" import { dual, pipe } from "effect/Function" +import type { ParseError } from "effect/ParseResult" import * as Request from "effect/Request" import * as RequestResolver from "effect/RequestResolver" +import * as Schema from "effect/Schema" +import * as Serializable from "effect/Serializable" import * as Stream from "effect/Stream" import { StreamRequestTypeId, withRequestTag } from "./internal/rpc.js" import * as Rpc from "./Rpc.js" diff --git a/packages/rpc/src/RpcResolverNoStream.ts b/packages/rpc/src/RpcResolverNoStream.ts index 286d6cf131..0225c367b4 100644 --- a/packages/rpc/src/RpcResolverNoStream.ts +++ b/packages/rpc/src/RpcResolverNoStream.ts @@ -1,8 +1,6 @@ /** * @since 1.0.0 */ -import * as Schema from "@effect/schema/Schema" -import * as Serializable from "@effect/schema/Serializable" import type { NonEmptyArray } from "effect/Array" import * as Channel from "effect/Channel" import * as Chunk from "effect/Chunk" @@ -11,6 +9,8 @@ import * as Exit from "effect/Exit" import { pipe } from "effect/Function" import * as Request from "effect/Request" import * as RequestResolver from "effect/RequestResolver" +import * as Schema from "effect/Schema" +import * as Serializable from "effect/Serializable" import * as Stream from "effect/Stream" import { StreamRequestTypeId, withRequestTag } from "./internal/rpc.js" import type * as Rpc from "./Rpc.js" diff --git a/packages/rpc/src/RpcRouter.ts b/packages/rpc/src/RpcRouter.ts index 38d4474c08..d42c73c94e 100644 --- a/packages/rpc/src/RpcRouter.ts +++ b/packages/rpc/src/RpcRouter.ts @@ -1,9 +1,6 @@ /** * @since 1.0.0 */ -import type { ParseError } from "@effect/schema/ParseResult" -import * as Schema from "@effect/schema/Schema" -import * as Serializable from "@effect/schema/Serializable" import * as Cause from "effect/Cause" import * as Channel from "effect/Channel" import * as Chunk from "effect/Chunk" @@ -12,8 +9,11 @@ import * as Effect from "effect/Effect" import * as Exit from "effect/Exit" import { dual, pipe } from "effect/Function" import * as Mailbox from "effect/Mailbox" +import type { ParseError } from "effect/ParseResult" import { type Pipeable, pipeArguments } from "effect/Pipeable" import * as Predicate from "effect/Predicate" +import * as Schema from "effect/Schema" +import * as Serializable from "effect/Serializable" import * as Stream from "effect/Stream" import { StreamRequestTypeId, withRequestTag } from "./internal/rpc.js" import * as Rpc from "./Rpc.js" diff --git a/packages/rpc/src/internal/rpc.ts b/packages/rpc/src/internal/rpc.ts index 89d5cdd18d..57947fa052 100644 --- a/packages/rpc/src/internal/rpc.ts +++ b/packages/rpc/src/internal/rpc.ts @@ -1,10 +1,10 @@ import type * as Headers from "@effect/platform/Headers" -import * as Schema from "@effect/schema/Schema" -import * as Serializable from "@effect/schema/Serializable" import * as Equal from "effect/Equal" import * as Hash from "effect/Hash" import * as PrimaryKey from "effect/PrimaryKey" import * as Request from "effect/Request" +import * as Schema from "effect/Schema" +import * as Serializable from "effect/Serializable" import type * as Rpc from "../Rpc.js" /** @internal */ diff --git a/packages/rpc/test/Router.test.ts b/packages/rpc/test/Router.test.ts index 8bc0354000..01857a1fcb 100644 --- a/packages/rpc/test/Router.test.ts +++ b/packages/rpc/test/Router.test.ts @@ -1,12 +1,12 @@ import { RpcResolver, RpcResolverNoStream, RpcRouter } from "@effect/rpc" import * as Rpc from "@effect/rpc/Rpc" import { Schema } from "@effect/schema" -import * as S from "@effect/schema/Schema" import * as Array from "effect/Array" import * as Chunk from "effect/Chunk" import * as Context from "effect/Context" import * as Effect from "effect/Effect" import { flow, pipe } from "effect/Function" +import * as S from "effect/Schema" import * as Stream from "effect/Stream" import { assert, describe, expect, it, test } from "vitest" diff --git a/packages/schema/dtslint/tsconfig.json b/packages/schema/dtslint/tsconfig.json deleted file mode 100644 index 6b2c70900f..0000000000 --- a/packages/schema/dtslint/tsconfig.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "extends": "../../../tsconfig.base.json", - "include": ["."], - "compilerOptions": { - "incremental": false, - "composite": false, - "noUnusedLocals": false - } -} diff --git a/packages/schema/package.json b/packages/schema/package.json index 21e825f351..d8fd6b390f 100644 --- a/packages/schema/package.json +++ b/packages/schema/package.json @@ -38,7 +38,6 @@ "build-esm": "tsc -b tsconfig.build.json", "build-cjs": "babel build/esm --plugins @babel/transform-export-namespace-from --plugins @babel/transform-modules-commonjs --out-dir build/cjs --source-maps", "build-annotate": "babel build/esm --plugins annotate-pure-calls --out-dir build/esm --source-maps", - "dtslint": "dtslint dtslint", "check": "tsc -b tsconfig.json", "test": "vitest", "coverage": "vitest --coverage", @@ -51,10 +50,7 @@ "effect": "workspace:^" }, "devDependencies": { - "ajv": "^8.17.1", "effect": "workspace:^", - "fast-check": "workspace:^", - "tinybench": "^2.9.0", - "zod": "^3.23.5" + "fast-check": "workspace:^" } } diff --git a/packages/schema/src/AST.ts b/packages/schema/src/AST.ts index 312fbe8f77..ee3b09d03d 100644 --- a/packages/schema/src/AST.ts +++ b/packages/schema/src/AST.ts @@ -2,2743 +2,8 @@ * @since 0.67.0 */ -import * as Arr from "effect/Array" -import type { Effect } from "effect/Effect" -import { dual, identity } from "effect/Function" -import { globalValue } from "effect/GlobalValue" -import * as Number from "effect/Number" -import * as Option from "effect/Option" -import * as Order from "effect/Order" -import * as Predicate from "effect/Predicate" -import * as regexp from "effect/RegExp" -import type { Concurrency } from "effect/Types" -import * as errors_ from "./internal/errors.js" -import * as util_ from "./internal/util.js" -import type { ParseIssue } from "./ParseResult.js" - -/** - * @category model - * @since 0.67.0 - */ -export type AST = - | Declaration - | Literal - | UniqueSymbol - | UndefinedKeyword - | VoidKeyword - | NeverKeyword - | UnknownKeyword - | AnyKeyword - | StringKeyword - | NumberKeyword - | BooleanKeyword - | BigIntKeyword - | SymbolKeyword - | ObjectKeyword - | Enums - | TemplateLiteral - // possible transformations - | Refinement - | TupleType - | TypeLiteral - | Union - | Suspend - // transformations - | Transformation - -// ------------------------------------------------------------------------------------- -// annotations -// ------------------------------------------------------------------------------------- - -/** - * @category annotations - * @since 0.67.0 - */ -export type BrandAnnotation = Arr.NonEmptyReadonlyArray - -/** - * @category annotations - * @since 0.67.0 - */ -export const BrandAnnotationId = Symbol.for("@effect/schema/annotation/Brand") - -/** - * @category annotations - * @since 0.67.0 - */ -export type TypeAnnotation = symbol - -/** - * @category annotations - * @since 0.67.0 - */ -export const TypeAnnotationId = Symbol.for("@effect/schema/annotation/Type") - -/** - * @category annotations - * @since 0.67.0 - */ -export type MessageAnnotation = (issue: ParseIssue) => string | Effect | { - readonly message: string | Effect - readonly override: boolean -} - -/** - * @category annotations - * @since 0.67.0 - */ -export const MessageAnnotationId = Symbol.for("@effect/schema/annotation/Message") - -/** - * @category annotations - * @since 0.67.0 - */ -export type MissingMessageAnnotation = () => string | Effect - -/** - * @category annotations - * @since 0.67.0 - */ -export const MissingMessageAnnotationId = Symbol.for("@effect/schema/annotation/MissingMessage") - -/** - * @category annotations - * @since 0.67.0 - */ -export type IdentifierAnnotation = string - -/** - * @category annotations - * @since 0.67.0 - */ -export const IdentifierAnnotationId = Symbol.for("@effect/schema/annotation/Identifier") - -/** - * @category annotations - * @since 0.67.0 - */ -export type TitleAnnotation = string - -/** - * @category annotations - * @since 0.67.0 - */ -export const TitleAnnotationId = Symbol.for("@effect/schema/annotation/Title") - -/** - * @category annotations - * @since 0.67.0 - */ -export type DescriptionAnnotation = string - -/** - * @category annotations - * @since 0.67.0 - */ -export const DescriptionAnnotationId = Symbol.for("@effect/schema/annotation/Description") - -/** - * @category annotations - * @since 0.67.0 - */ -export type ExamplesAnnotation = Arr.NonEmptyReadonlyArray - -/** - * @category annotations - * @since 0.67.0 - */ -export const ExamplesAnnotationId = Symbol.for("@effect/schema/annotation/Examples") - -/** - * @category annotations - * @since 0.67.0 - */ -export type DefaultAnnotation = A - -/** - * @category annotations - * @since 0.67.0 - */ -export const DefaultAnnotationId = Symbol.for("@effect/schema/annotation/Default") - -/** - * @category annotations - * @since 0.67.0 - */ -export type JSONSchemaAnnotation = object - -/** - * @category annotations - * @since 0.67.0 - */ -export const JSONSchemaAnnotationId = Symbol.for("@effect/schema/annotation/JSONSchema") - -/** - * @category annotations - * @since 0.67.0 - */ -export type DocumentationAnnotation = string - -/** - * @category annotations - * @since 0.67.0 - */ -export const DocumentationAnnotationId = Symbol.for("@effect/schema/annotation/Documentation") - -/** - * @category annotations - * @since 0.67.0 - */ -export type ConcurrencyAnnotation = Concurrency | undefined - -/** - * @category annotations - * @since 0.67.0 - */ -export const ConcurrencyAnnotationId = Symbol.for("@effect/schema/annotation/Concurrency") - -/** - * @category annotations - * @since 0.67.0 - */ -export type BatchingAnnotation = boolean | "inherit" | undefined - -/** - * @category annotations - * @since 0.67.0 - */ -export const BatchingAnnotationId = Symbol.for("@effect/schema/annotation/Batching") - -/** - * @category annotations - * @since 0.67.0 - */ -export type ParseIssueTitleAnnotation = (issue: ParseIssue) => string | undefined - -/** - * @category annotations - * @since 0.67.0 - */ -export const ParseIssueTitleAnnotationId = Symbol.for("@effect/schema/annotation/ParseIssueTitle") - -/** - * @category annotations - * @since 0.68.3 - */ -export const ParseOptionsAnnotationId = Symbol.for("@effect/schema/annotation/ParseOptions") - -/** - * @category annotations - * @since 0.70.1 - */ -export type DecodingFallbackAnnotation = (issue: ParseIssue) => Effect - -/** - * @category annotations - * @since 0.70.1 - */ -export const DecodingFallbackAnnotationId = Symbol.for("@effect/schema/annotation/DecodingFallback") - -/** @internal */ -export const SurrogateAnnotationId = Symbol.for("@effect/schema/annotation/Surrogate") - -/** - * Used by: - * - * - AST.keyof - * - AST.getPropertyKeyIndexedAccess - * - AST.getPropertyKeys - * - AST.getPropertySignatures - * - AST.getWeight - * - Parser.getLiterals - * - * @internal - */ -export type SurrogateAnnotation = AST - -/** @internal */ -export const StableFilterAnnotationId = Symbol.for("@effect/schema/annotation/StableFilter") - -/** - * A stable filter consistently applies fixed validation rules, such as - * 'minItems', 'maxItems', and 'itemsCount', to ensure array length complies - * with set criteria regardless of the input data's content. - * - * @internal - */ -export type StableFilterAnnotation = boolean - -/** - * @category annotations - * @since 0.67.0 - */ -export interface Annotations { - readonly [_: symbol]: unknown -} - -/** - * @category annotations - * @since 0.67.0 - */ -export interface Annotated { - readonly annotations: Annotations -} - -/** - * @category annotations - * @since 0.67.0 - */ -export const getAnnotation: { - (key: symbol): (annotated: Annotated) => Option.Option - (annotated: Annotated, key: symbol): Option.Option -} = dual( - 2, - (annotated: Annotated, key: symbol): Option.Option => - Object.prototype.hasOwnProperty.call(annotated.annotations, key) ? - Option.some(annotated.annotations[key] as any) : - Option.none() -) - -/** - * @category annotations - * @since 0.67.0 - */ -export const getBrandAnnotation = getAnnotation(BrandAnnotationId) - -/** - * @category annotations - * @since 0.67.0 - */ -export const getMessageAnnotation = getAnnotation(MessageAnnotationId) - -/** - * @category annotations - * @since 0.67.0 - */ -export const getMissingMessageAnnotation = getAnnotation(MissingMessageAnnotationId) - -/** - * @category annotations - * @since 0.67.0 - */ -export const getTitleAnnotation = getAnnotation(TitleAnnotationId) - -/** - * @category annotations - * @since 0.67.0 - */ -export const getIdentifierAnnotation = getAnnotation(IdentifierAnnotationId) - -/** - * @category annotations - * @since 0.67.0 - */ -export const getDescriptionAnnotation = getAnnotation(DescriptionAnnotationId) - -/** - * @category annotations - * @since 0.67.0 - */ -export const getExamplesAnnotation = getAnnotation>(ExamplesAnnotationId) - -/** - * @category annotations - * @since 0.67.0 - */ -export const getDefaultAnnotation = getAnnotation>(DefaultAnnotationId) - -/** - * @category annotations - * @since 0.67.0 - */ -export const getJSONSchemaAnnotation = getAnnotation(JSONSchemaAnnotationId) - -/** - * @category annotations - * @since 0.67.0 - */ -export const getDocumentationAnnotation = getAnnotation(DocumentationAnnotationId) - -/** - * @category annotations - * @since 0.67.0 - */ -export const getConcurrencyAnnotation = getAnnotation(ConcurrencyAnnotationId) - -/** - * @category annotations - * @since 0.67.0 - */ -export const getBatchingAnnotation = getAnnotation(BatchingAnnotationId) - -/** - * @category annotations - * @since 0.67.0 - */ -export const getParseIssueTitleAnnotation = getAnnotation(ParseIssueTitleAnnotationId) - -/** - * @category annotations - * @since 0.68.3 - */ -export const getParseOptionsAnnotation = getAnnotation(ParseOptionsAnnotationId) - -/** - * @category annotations - * @since 0.70.1 - */ -export const getDecodingFallbackAnnotation = getAnnotation>( - DecodingFallbackAnnotationId -) - -/** @internal */ -export const getSurrogateAnnotation = getAnnotation(SurrogateAnnotationId) - -const getStableFilterAnnotation = getAnnotation(StableFilterAnnotationId) - -/** @internal */ -export const hasStableFilter = (annotated: Annotated) => - Option.exists(getStableFilterAnnotation(annotated), (b) => b === true) - -const JSONIdentifierAnnotationId = Symbol.for("@effect/schema/annotation/JSONIdentifier") - -/** @internal */ -export const getJSONIdentifierAnnotation = getAnnotation(JSONIdentifierAnnotationId) - -/** - * @category model - * @since 0.67.0 - */ -export class Declaration implements Annotated { - /** - * @since 0.67.0 - */ - readonly _tag = "Declaration" - constructor( - readonly typeParameters: ReadonlyArray, - readonly decodeUnknown: ( - ...typeParameters: ReadonlyArray - ) => (input: unknown, options: ParseOptions, self: Declaration) => Effect, - readonly encodeUnknown: ( - ...typeParameters: ReadonlyArray - ) => (input: unknown, options: ParseOptions, self: Declaration) => Effect, - readonly annotations: Annotations = {} - ) {} - /** - * @since 0.67.0 - */ - toString() { - return Option.getOrElse(getExpected(this), () => "") - } - /** - * @since 0.67.0 - */ - toJSON(): object { - return { - _tag: this._tag, - typeParameters: this.typeParameters.map((ast) => ast.toJSON()), - annotations: toJSONAnnotations(this.annotations) - } - } -} - -const createASTGuard = (tag: T) => (ast: AST): ast is Extract => - ast._tag === tag - -/** - * @category guards - * @since 0.67.0 - */ -export const isDeclaration: (ast: AST) => ast is Declaration = createASTGuard("Declaration") - -/** - * @category model - * @since 0.67.0 - */ -export type LiteralValue = string | number | boolean | null | bigint - -/** - * @category model - * @since 0.67.0 - */ -export class Literal implements Annotated { - /** - * @since 0.67.0 - */ - readonly _tag = "Literal" - constructor(readonly literal: LiteralValue, readonly annotations: Annotations = {}) {} - /** - * @since 0.67.0 - */ - toString() { - return Option.getOrElse(getExpected(this), () => util_.formatUnknown(this.literal)) - } - /** - * @since 0.67.0 - */ - toJSON(): object { - return { - _tag: this._tag, - literal: Predicate.isBigInt(this.literal) ? String(this.literal) : this.literal, - annotations: toJSONAnnotations(this.annotations) - } - } -} - -/** - * @category guards - * @since 0.67.0 - */ -export const isLiteral: (ast: AST) => ast is Literal = createASTGuard("Literal") - -const $null = new Literal(null) - -export { - /** - * @category constructors - * @since 0.67.0 - */ - $null as null -} - -/** - * @category model - * @since 0.67.0 - */ -export class UniqueSymbol implements Annotated { - /** - * @since 0.67.0 - */ - readonly _tag = "UniqueSymbol" - constructor(readonly symbol: symbol, readonly annotations: Annotations = {}) {} - /** - * @since 0.67.0 - */ - toString() { - return Option.getOrElse(getExpected(this), () => util_.formatUnknown(this.symbol)) - } - /** - * @since 0.67.0 - */ - toJSON(): object { - return { - _tag: this._tag, - symbol: String(this.symbol), - annotations: toJSONAnnotations(this.annotations) - } - } -} - -/** - * @category guards - * @since 0.67.0 - */ -export const isUniqueSymbol: (ast: AST) => ast is UniqueSymbol = createASTGuard("UniqueSymbol") - -/** - * @category model - * @since 0.67.0 - */ -export class UndefinedKeyword implements Annotated { - /** - * @since 0.67.0 - */ - readonly _tag = "UndefinedKeyword" - constructor(readonly annotations: Annotations = {}) {} - /** - * @since 0.67.0 - */ - toString() { - return formatKeyword(this) - } - /** - * @since 0.67.0 - */ - toJSON(): object { - return { - _tag: this._tag, - annotations: toJSONAnnotations(this.annotations) - } - } -} - -/** - * @category constructors - * @since 0.67.0 - */ -export const undefinedKeyword: UndefinedKeyword = new UndefinedKeyword({ - [TitleAnnotationId]: "undefined" -}) - -/** - * @category guards - * @since 0.67.0 - */ -export const isUndefinedKeyword: (ast: AST) => ast is UndefinedKeyword = createASTGuard("UndefinedKeyword") - -/** - * @category model - * @since 0.67.0 - */ -export class VoidKeyword implements Annotated { - /** - * @since 0.67.0 - */ - readonly _tag = "VoidKeyword" - constructor(readonly annotations: Annotations = {}) {} - /** - * @since 0.67.0 - */ - toString() { - return formatKeyword(this) - } - /** - * @since 0.67.0 - */ - toJSON(): object { - return { - _tag: this._tag, - annotations: toJSONAnnotations(this.annotations) - } - } -} - -/** - * @category constructors - * @since 0.67.0 - */ -export const voidKeyword: VoidKeyword = new VoidKeyword({ - [TitleAnnotationId]: "void" -}) - -/** - * @category guards - * @since 0.67.0 - */ -export const isVoidKeyword: (ast: AST) => ast is VoidKeyword = createASTGuard("VoidKeyword") - -/** - * @category model - * @since 0.67.0 - */ -export class NeverKeyword implements Annotated { - /** - * @since 0.67.0 - */ - readonly _tag = "NeverKeyword" - constructor(readonly annotations: Annotations = {}) {} - /** - * @since 0.67.0 - */ - toString() { - return formatKeyword(this) - } - /** - * @since 0.67.0 - */ - toJSON(): object { - return { - _tag: this._tag, - annotations: toJSONAnnotations(this.annotations) - } - } -} - -/** - * @category constructors - * @since 0.67.0 - */ -export const neverKeyword: NeverKeyword = new NeverKeyword({ - [TitleAnnotationId]: "never" -}) - -/** - * @category guards - * @since 0.67.0 - */ -export const isNeverKeyword: (ast: AST) => ast is NeverKeyword = createASTGuard("NeverKeyword") - -/** - * @category model - * @since 0.67.0 - */ -export class UnknownKeyword implements Annotated { - /** - * @since 0.67.0 - */ - readonly _tag = "UnknownKeyword" - constructor(readonly annotations: Annotations = {}) {} - /** - * @since 0.67.0 - */ - toString() { - return formatKeyword(this) - } - /** - * @since 0.67.0 - */ - toJSON(): object { - return { - _tag: this._tag, - annotations: toJSONAnnotations(this.annotations) - } - } -} - -/** - * @category constructors - * @since 0.67.0 - */ -export const unknownKeyword: UnknownKeyword = new UnknownKeyword({ - [TitleAnnotationId]: "unknown" -}) - -/** - * @category guards - * @since 0.67.0 - */ -export const isUnknownKeyword: (ast: AST) => ast is UnknownKeyword = createASTGuard("UnknownKeyword") - -/** - * @category model - * @since 0.67.0 - */ -export class AnyKeyword implements Annotated { - /** - * @since 0.67.0 - */ - readonly _tag = "AnyKeyword" - constructor(readonly annotations: Annotations = {}) {} - /** - * @since 0.67.0 - */ - toString() { - return formatKeyword(this) - } - /** - * @since 0.67.0 - */ - toJSON(): object { - return { - _tag: this._tag, - annotations: toJSONAnnotations(this.annotations) - } - } -} - -/** - * @category constructors - * @since 0.67.0 - */ -export const anyKeyword: AnyKeyword = new AnyKeyword({ - [TitleAnnotationId]: "any" -}) - -/** - * @category guards - * @since 0.67.0 - */ -export const isAnyKeyword: (ast: AST) => ast is AnyKeyword = createASTGuard("AnyKeyword") - -/** - * @category model - * @since 0.67.0 - */ -export class StringKeyword implements Annotated { - /** - * @since 0.67.0 - */ - readonly _tag = "StringKeyword" - constructor(readonly annotations: Annotations = {}) {} - /** - * @since 0.67.0 - */ - toString() { - return formatKeyword(this) - } - /** - * @since 0.67.0 - */ - toJSON(): object { - return { - _tag: this._tag, - annotations: toJSONAnnotations(this.annotations) - } - } -} - -/** - * @category constructors - * @since 0.67.0 - */ -export const stringKeyword: StringKeyword = new StringKeyword({ - [TitleAnnotationId]: "string", - [DescriptionAnnotationId]: "a string" -}) - -/** - * @category guards - * @since 0.67.0 - */ -export const isStringKeyword: (ast: AST) => ast is StringKeyword = createASTGuard("StringKeyword") - -/** - * @category model - * @since 0.67.0 - */ -export class NumberKeyword implements Annotated { - /** - * @since 0.67.0 - */ - readonly _tag = "NumberKeyword" - constructor(readonly annotations: Annotations = {}) {} - /** - * @since 0.67.0 - */ - toString() { - return formatKeyword(this) - } - /** - * @since 0.67.0 - */ - toJSON(): object { - return { - _tag: this._tag, - annotations: toJSONAnnotations(this.annotations) - } - } -} - -/** - * @category constructors - * @since 0.67.0 - */ -export const numberKeyword: NumberKeyword = new NumberKeyword({ - [TitleAnnotationId]: "number", - [DescriptionAnnotationId]: "a number" -}) - -/** - * @category guards - * @since 0.67.0 - */ -export const isNumberKeyword: (ast: AST) => ast is NumberKeyword = createASTGuard("NumberKeyword") - -/** - * @category model - * @since 0.67.0 - */ -export class BooleanKeyword implements Annotated { - /** - * @since 0.67.0 - */ - readonly _tag = "BooleanKeyword" - constructor(readonly annotations: Annotations = {}) {} - /** - * @since 0.67.0 - */ - toString() { - return formatKeyword(this) - } - /** - * @since 0.67.0 - */ - toJSON(): object { - return { - _tag: this._tag, - annotations: toJSONAnnotations(this.annotations) - } - } -} - -/** - * @category constructors - * @since 0.67.0 - */ -export const booleanKeyword: BooleanKeyword = new BooleanKeyword({ - [TitleAnnotationId]: "boolean", - [DescriptionAnnotationId]: "a boolean" -}) - -/** - * @category guards - * @since 0.67.0 - */ -export const isBooleanKeyword: (ast: AST) => ast is BooleanKeyword = createASTGuard("BooleanKeyword") - -/** - * @category model - * @since 0.67.0 - */ -export class BigIntKeyword implements Annotated { - /** - * @since 0.67.0 - */ - readonly _tag = "BigIntKeyword" - constructor(readonly annotations: Annotations = {}) {} - /** - * @since 0.67.0 - */ - toString() { - return formatKeyword(this) - } - /** - * @since 0.67.0 - */ - toJSON(): object { - return { - _tag: this._tag, - annotations: toJSONAnnotations(this.annotations) - } - } -} - -/** - * @category constructors - * @since 0.67.0 - */ -export const bigIntKeyword: BigIntKeyword = new BigIntKeyword({ - [TitleAnnotationId]: "bigint", - [DescriptionAnnotationId]: "a bigint" -}) - -/** - * @category guards - * @since 0.67.0 - */ -export const isBigIntKeyword: (ast: AST) => ast is BigIntKeyword = createASTGuard("BigIntKeyword") - -/** - * @category model - * @since 0.67.0 - */ -export class SymbolKeyword implements Annotated { - /** - * @since 0.67.0 - */ - readonly _tag = "SymbolKeyword" - constructor(readonly annotations: Annotations = {}) {} - /** - * @since 0.67.0 - */ - toString() { - return formatKeyword(this) - } - /** - * @since 0.67.0 - */ - toJSON(): object { - return { - _tag: this._tag, - annotations: toJSONAnnotations(this.annotations) - } - } -} - -/** - * @category constructors - * @since 0.67.0 - */ -export const symbolKeyword: SymbolKeyword = new SymbolKeyword({ - [TitleAnnotationId]: "symbol", - [DescriptionAnnotationId]: "a symbol" -}) - -/** - * @category guards - * @since 0.67.0 - */ -export const isSymbolKeyword: (ast: AST) => ast is SymbolKeyword = createASTGuard("SymbolKeyword") - -/** - * @category model - * @since 0.67.0 - */ -export class ObjectKeyword implements Annotated { - /** - * @since 0.67.0 - */ - readonly _tag = "ObjectKeyword" - constructor(readonly annotations: Annotations = {}) {} - /** - * @since 0.67.0 - */ - toString() { - return formatKeyword(this) - } - /** - * @since 0.67.0 - */ - toJSON(): object { - return { - _tag: this._tag, - annotations: toJSONAnnotations(this.annotations) - } - } -} - -/** - * @category constructors - * @since 0.67.0 - */ -export const objectKeyword: ObjectKeyword = new ObjectKeyword({ - [TitleAnnotationId]: "object", - [DescriptionAnnotationId]: "an object in the TypeScript meaning, i.e. the `object` type" -}) - -/** - * @category guards - * @since 0.67.0 - */ -export const isObjectKeyword: (ast: AST) => ast is ObjectKeyword = createASTGuard("ObjectKeyword") - -/** - * @category model - * @since 0.67.0 - */ -export class Enums implements Annotated { - /** - * @since 0.67.0 - */ - readonly _tag = "Enums" - constructor( - readonly enums: ReadonlyArray, - readonly annotations: Annotations = {} - ) {} - /** - * @since 0.67.0 - */ - toString() { - return Option.getOrElse( - getExpected(this), - () => ` JSON.stringify(value)).join(" | ")}>` - ) - } - /** - * @since 0.67.0 - */ - toJSON(): object { - return { - _tag: this._tag, - enums: this.enums, - annotations: toJSONAnnotations(this.annotations) - } - } -} - -/** - * @category guards - * @since 0.67.0 - */ -export const isEnums: (ast: AST) => ast is Enums = createASTGuard("Enums") - -/** - * @category model - * @since 0.67.0 - */ -export class TemplateLiteralSpan { - constructor(readonly type: StringKeyword | NumberKeyword, readonly literal: string) {} - /** - * @since 0.67.0 - */ - toString() { - const type = "${" + String(this.type) + "}" - return type + this.literal - } - /** - * @since 0.67.0 - */ - toJSON(): object { - return { - type: this.type.toJSON(), - literal: this.literal - } - } -} - -/** - * @category model - * @since 0.67.0 - */ -export class TemplateLiteral implements Annotated { - /** - * @since 0.67.0 - */ - readonly _tag = "TemplateLiteral" - constructor( - readonly head: string, - readonly spans: Arr.NonEmptyReadonlyArray, - readonly annotations: Annotations = {} - ) {} - /** - * @since 0.67.0 - */ - toString() { - return Option.getOrElse(getExpected(this), () => formatTemplateLiteral(this)) - } - /** - * @since 0.67.0 - */ - toJSON(): object { - return { - _tag: this._tag, - head: this.head, - spans: this.spans.map((span) => span.toJSON()), - annotations: toJSONAnnotations(this.annotations) - } - } -} - -const formatTemplateLiteral = (ast: TemplateLiteral): string => - "`" + ast.head + ast.spans.map((span) => String(span)).join("") + - "`" - -/** - * @category guards - * @since 0.67.0 - */ -export const isTemplateLiteral: (ast: AST) => ast is TemplateLiteral = createASTGuard("TemplateLiteral") - -/** - * @category model - * @since 0.68.0 - */ -export class Type implements Annotated { - constructor( - readonly type: AST, - readonly annotations: Annotations = {} - ) {} - /** - * @since 0.68.0 - */ - toJSON(): object { - return { - type: this.type.toJSON(), - annotations: toJSONAnnotations(this.annotations) - } - } - /** - * @since 0.68.0 - */ - toString() { - return String(this.type) - } -} - -/** - * @category model - * @since 0.68.0 - */ -export class OptionalType extends Type { - constructor( - type: AST, - readonly isOptional: boolean, - annotations: Annotations = {} - ) { - super(type, annotations) - } - /** - * @since 0.68.0 - */ - toJSON(): object { - return { - type: this.type.toJSON(), - isOptional: this.isOptional, - annotations: toJSONAnnotations(this.annotations) - } - } - /** - * @since 0.68.0 - */ - toString() { - return String(this.type) + (this.isOptional ? "?" : "") - } -} - -const getRestASTs = (rest: ReadonlyArray): ReadonlyArray => rest.map((annotatedAST) => annotatedAST.type) - -/** - * @category model - * @since 0.67.0 - */ -export class TupleType implements Annotated { - /** - * @since 0.67.0 - */ - readonly _tag = "TupleType" - constructor( - readonly elements: ReadonlyArray, - readonly rest: ReadonlyArray, - readonly isReadonly: boolean, - readonly annotations: Annotations = {} - ) { - let hasOptionalElement = false - let hasIllegalRequiredElement = false - for (const e of elements) { - if (e.isOptional) { - hasOptionalElement = true - } else if (hasOptionalElement) { - hasIllegalRequiredElement = true - break - } - } - if (hasIllegalRequiredElement || (hasOptionalElement && rest.length > 1)) { - throw new Error(errors_.getASTRequiredElementFollowinAnOptionalElementErrorMessage) - } - } - /** - * @since 0.67.0 - */ - toString() { - return Option.getOrElse(getExpected(this), () => formatTuple(this)) - } - /** - * @since 0.67.0 - */ - toJSON(): object { - return { - _tag: this._tag, - elements: this.elements.map((e) => e.toJSON()), - rest: this.rest.map((ast) => ast.toJSON()), - isReadonly: this.isReadonly, - annotations: toJSONAnnotations(this.annotations) - } - } -} - -const formatTuple = (ast: TupleType): string => { - const formattedElements = ast.elements.map(String) - .join(", ") - return Arr.matchLeft(ast.rest, { - onEmpty: () => `readonly [${formattedElements}]`, - onNonEmpty: (head, tail) => { - const formattedHead = String(head) - const wrappedHead = formattedHead.includes(" | ") ? `(${formattedHead})` : formattedHead - - if (tail.length > 0) { - const formattedTail = tail.map(String).join(", ") - if (ast.elements.length > 0) { - return `readonly [${formattedElements}, ...${wrappedHead}[], ${formattedTail}]` - } else { - return `readonly [...${wrappedHead}[], ${formattedTail}]` - } - } else { - if (ast.elements.length > 0) { - return `readonly [${formattedElements}, ...${wrappedHead}[]]` - } else { - return `ReadonlyArray<${formattedHead}>` - } - } - } - }) -} - -/** - * @category guards - * @since 0.67.0 - */ -export const isTupleType: (ast: AST) => ast is TupleType = createASTGuard("TupleType") - -/** - * @category model - * @since 0.67.0 - */ -export class PropertySignature extends OptionalType { - constructor( - readonly name: PropertyKey, - type: AST, - isOptional: boolean, - readonly isReadonly: boolean, - annotations?: Annotations - ) { - super(type, isOptional, annotations) - } - /** - * @since 0.68.18 - */ - toString(): string { - return (this.isReadonly ? "readonly " : "") + String(this.name) + (this.isOptional ? "?" : "") + ": " + - this.type - } - /** - * @since 0.67.0 - */ - toJSON(): object { - return { - name: String(this.name), - type: this.type.toJSON(), - isOptional: this.isOptional, - isReadonly: this.isReadonly, - annotations: toJSONAnnotations(this.annotations) - } - } -} - -/** - * @since 0.67.0 - */ -export type Parameter = StringKeyword | SymbolKeyword | TemplateLiteral | Refinement - -/** - * @since 0.67.0 - */ -export const isParameter = (ast: AST): ast is Parameter => { - switch (ast._tag) { - case "StringKeyword": - case "SymbolKeyword": - case "TemplateLiteral": - return true - case "Refinement": - return isParameter(ast.from) - } - return false -} - -/** - * @category model - * @since 0.67.0 - */ -export class IndexSignature { - /** - * @since 0.67.0 - */ - readonly parameter: Parameter - constructor( - parameter: AST, - readonly type: AST, - readonly isReadonly: boolean - ) { - if (isParameter(parameter)) { - this.parameter = parameter - } else { - throw new Error(errors_.getASTIndexSignatureParameterErrorMessage) - } - } - /** - * @since 0.68.18 - */ - toString(): string { - return (this.isReadonly ? "readonly " : "") + `[x: ${this.parameter}]: ${this.type}` - } - /** - * @since 0.67.0 - */ - toJSON(): object { - return { - parameter: this.parameter.toJSON(), - type: this.type.toJSON(), - isReadonly: this.isReadonly - } - } -} - -/** - * @category model - * @since 0.67.0 - */ -export class TypeLiteral implements Annotated { - /** - * @since 0.67.0 - */ - readonly _tag = "TypeLiteral" - /** - * @since 0.67.0 - */ - readonly propertySignatures: ReadonlyArray - /** - * @since 0.67.0 - */ - readonly indexSignatures: ReadonlyArray - constructor( - propertySignatures: ReadonlyArray, - indexSignatures: ReadonlyArray, - readonly annotations: Annotations = {} - ) { - // check for duplicate property signatures - const keys: Record = {} - for (let i = 0; i < propertySignatures.length; i++) { - const name = propertySignatures[i].name - if (Object.prototype.hasOwnProperty.call(keys, name)) { - throw new Error(errors_.getASTDuplicatePropertySignatureErrorMessage(name)) - } - keys[name] = null - } - // check for duplicate index signatures - const parameters = { - string: false, - symbol: false - } - for (let i = 0; i < indexSignatures.length; i++) { - const parameter = getParameterBase(indexSignatures[i].parameter) - if (isStringKeyword(parameter)) { - if (parameters.string) { - throw new Error(errors_.getASTDuplicateIndexSignatureErrorMessage("string")) - } - parameters.string = true - } else if (isSymbolKeyword(parameter)) { - if (parameters.symbol) { - throw new Error(errors_.getASTDuplicateIndexSignatureErrorMessage("symbol")) - } - parameters.symbol = true - } - } - - this.propertySignatures = propertySignatures - this.indexSignatures = indexSignatures - } - /** - * @since 0.67.0 - */ - toString() { - return Option.getOrElse(getExpected(this), () => formatTypeLiteral(this)) - } - /** - * @since 0.67.0 - */ - toJSON(): object { - return { - _tag: this._tag, - propertySignatures: this.propertySignatures.map((ps) => ps.toJSON()), - indexSignatures: this.indexSignatures.map((ps) => ps.toJSON()), - annotations: toJSONAnnotations(this.annotations) - } - } -} - -const formatIndexSignatures = (iss: ReadonlyArray): string => iss.map(String).join("; ") - -const formatTypeLiteral = (ast: TypeLiteral): string => { - if (ast.propertySignatures.length > 0) { - const pss = ast.propertySignatures.map(String).join("; ") - if (ast.indexSignatures.length > 0) { - return `{ ${pss}; ${formatIndexSignatures(ast.indexSignatures)} }` - } else { - return `{ ${pss} }` - } - } else { - if (ast.indexSignatures.length > 0) { - return `{ ${formatIndexSignatures(ast.indexSignatures)} }` - } else { - return "{}" - } - } -} - -/** - * @category guards - * @since 0.67.0 - */ -export const isTypeLiteral: (ast: AST) => ast is TypeLiteral = createASTGuard("TypeLiteral") - -/** - * @since 0.67.0 - */ -export type Members = readonly [A, A, ...Array] - -const sortCandidates = Arr.sort( - Order.mapInput(Number.Order, (ast: AST) => { - switch (ast._tag) { - case "AnyKeyword": - return 0 - case "UnknownKeyword": - return 1 - case "ObjectKeyword": - return 2 - case "StringKeyword": - case "NumberKeyword": - case "BooleanKeyword": - case "BigIntKeyword": - case "SymbolKeyword": - return 3 - } - return 4 - }) -) - -const literalMap = { - string: "StringKeyword", - number: "NumberKeyword", - boolean: "BooleanKeyword", - bigint: "BigIntKeyword" -} as const - -/** @internal */ -export const flatten = (candidates: ReadonlyArray): Array => - Arr.flatMap(candidates, (ast) => isUnion(ast) ? flatten(ast.types) : [ast]) - -/** @internal */ -export const unify = (candidates: ReadonlyArray): Array => { - const cs = sortCandidates(candidates) - const out: Array = [] - const uniques: { [K in AST["_tag"] | "{}"]?: AST } = {} - const literals: Array = [] - for (const ast of cs) { - switch (ast._tag) { - case "NeverKeyword": - break - case "AnyKeyword": - return [anyKeyword] - case "UnknownKeyword": - return [unknownKeyword] - // uniques - case "ObjectKeyword": - case "UndefinedKeyword": - case "VoidKeyword": - case "StringKeyword": - case "NumberKeyword": - case "BooleanKeyword": - case "BigIntKeyword": - case "SymbolKeyword": { - if (!uniques[ast._tag]) { - uniques[ast._tag] = ast - out.push(ast) - } - break - } - case "Literal": { - const type = typeof ast.literal - switch (type) { - case "string": - case "number": - case "bigint": - case "boolean": { - const _tag = literalMap[type] - if (!uniques[_tag] && !literals.includes(ast.literal)) { - literals.push(ast.literal) - out.push(ast) - } - break - } - // null - case "object": { - if (!literals.includes(ast.literal)) { - literals.push(ast.literal) - out.push(ast) - } - break - } - } - break - } - case "UniqueSymbol": { - if (!uniques["SymbolKeyword"] && !literals.includes(ast.symbol)) { - literals.push(ast.symbol) - out.push(ast) - } - break - } - case "TupleType": { - if (!uniques["ObjectKeyword"]) { - out.push(ast) - } - break - } - case "TypeLiteral": { - if (ast.propertySignatures.length === 0 && ast.indexSignatures.length === 0) { - if (!uniques["{}"]) { - uniques["{}"] = ast - out.push(ast) - } - } else if (!uniques["ObjectKeyword"]) { - out.push(ast) - } - break - } - default: - out.push(ast) - } - } - return out -} - -/** - * @category model - * @since 0.67.0 - */ -export class Union implements Annotated { - static make = (types: ReadonlyArray, annotations?: Annotations): AST => { - return isMembers(types) ? new Union(types, annotations) : types.length === 1 ? types[0] : neverKeyword - } - /** @internal */ - static unify = (candidates: ReadonlyArray, annotations?: Annotations): AST => { - return Union.make(unify(flatten(candidates)), annotations) - } - /** - * @since 0.67.0 - */ - readonly _tag = "Union" - private constructor(readonly types: Members, readonly annotations: Annotations = {}) {} - /** - * @since 0.67.0 - */ - toString() { - return Option.getOrElse( - getExpected(this), - () => this.types.map(String).join(" | ") - ) - } - /** - * @since 0.67.0 - */ - toJSON(): object { - return { - _tag: this._tag, - types: this.types.map((ast) => ast.toJSON()), - annotations: toJSONAnnotations(this.annotations) - } - } -} - -/** @internal */ -export const mapMembers = (members: Members, f: (a: A) => B): Members => members.map(f) as any - -/** @internal */ -export const isMembers = (as: ReadonlyArray): as is Members => as.length > 1 - -/** - * @category guards - * @since 0.67.0 - */ -export const isUnion: (ast: AST) => ast is Union = createASTGuard("Union") - -const toJSONMemoMap = globalValue( - Symbol.for("@effect/schema/AST/toJSONMemoMap"), - () => new WeakMap() -) - -/** - * @category model - * @since 0.67.0 - */ -export class Suspend implements Annotated { - /** - * @since 0.67.0 - */ - readonly _tag = "Suspend" - constructor(readonly f: () => AST, readonly annotations: Annotations = {}) { - this.f = util_.memoizeThunk(f) - } - /** - * @since 0.67.0 - */ - toString() { - return getExpected(this).pipe( - Option.orElse(() => - Option.flatMap( - Option.liftThrowable(this.f)(), - (ast) => getExpected(ast) - ) - ), - Option.getOrElse(() => "") - ) - } - /** - * @since 0.67.0 - */ - toJSON(): object { - const ast = this.f() - let out = toJSONMemoMap.get(ast) - if (out) { - return out - } - toJSONMemoMap.set(ast, { _tag: this._tag }) - out = { - _tag: this._tag, - ast: ast.toJSON(), - annotations: toJSONAnnotations(this.annotations) - } - toJSONMemoMap.set(ast, out) - return out - } -} - -/** - * @category guards - * @since 0.67.0 - */ -export const isSuspend: (ast: AST) => ast is Suspend = createASTGuard("Suspend") - -/** - * @category model - * @since 0.67.0 - */ -export class Refinement implements Annotated { - /** - * @since 0.67.0 - */ - readonly _tag = "Refinement" - constructor( - readonly from: From, - readonly filter: ( - input: any, - options: ParseOptions, - self: Refinement - ) => Option.Option, - readonly annotations: Annotations = {} - ) {} - /** - * @since 0.67.0 - */ - toString() { - return Option.getOrElse(getExpected(this), () => `{ ${this.from} | filter }`) - } - /** - * @since 0.67.0 - */ - toJSON(): object { - return { - _tag: this._tag, - from: this.from.toJSON(), - annotations: toJSONAnnotations(this.annotations) - } - } -} - -/** - * @category guards - * @since 0.67.0 - */ -export const isRefinement: (ast: AST) => ast is Refinement = createASTGuard("Refinement") - -/** - * @category model - * @since 0.67.0 - */ -export interface ParseOptions { - /** - * The `errors` option allows you to receive all parsing errors when - * attempting to parse a value using a schema. By default only the first error - * is returned, but by setting the `errors` option to `"all"`, you can receive - * all errors that occurred during the parsing process. This can be useful for - * debugging or for providing more comprehensive error messages to the user. - * - * default: "first" - * - * @since 0.67.0 - */ - readonly errors?: "first" | "all" | undefined - /** - * When using a `Schema` to parse a value, by default any properties that are - * not specified in the `Schema` will be stripped out from the output. This is - * because the `Schema` is expecting a specific shape for the parsed value, - * and any excess properties do not conform to that shape. - * - * However, you can use the `onExcessProperty` option (default value: - * `"ignore"`) to trigger a parsing error. This can be particularly useful in - * cases where you need to detect and handle potential errors or unexpected - * values. - * - * If you want to allow excess properties to remain, you can use - * `onExcessProperty` set to `"preserve"`. - * - * default: "ignore" - * - * @since 0.67.0 - */ - readonly onExcessProperty?: "ignore" | "error" | "preserve" | undefined - /** - * The `propertyOrder` option provides control over the order of object fields - * in the output. This feature is particularly useful when the sequence of - * keys is important for the consuming processes or when maintaining the input - * order enhances readability and usability. - * - * By default, the `propertyOrder` option is set to `"none"`. This means that - * the internal system decides the order of keys to optimize parsing speed. - * The order of keys in this mode should not be considered stable, and it's - * recommended not to rely on key ordering as it may change in future updates - * without notice. - * - * Setting `propertyOrder` to `"original"` ensures that the keys are ordered - * as they appear in the input during the decoding/encoding process. - * - * default: "none" - * - * @since 0.67.20 - */ - readonly propertyOrder?: "none" | "original" | undefined - /** - * Handles missing properties in data structures. By default, missing - * properties are treated as if present with an `undefined` value. To treat - * missing properties as errors, set the `exact` option to `true`. This - * setting is already enabled by default for `is` and `asserts` functions, - * treating absent properties strictly unless overridden. - * - * default: false - * - * @since 0.67.24 - */ - readonly exact?: boolean | undefined -} - -/** - * @since 0.67.0 - */ -export const defaultParseOption: ParseOptions = {} - -/** - * @category model - * @since 0.67.0 - */ -export class Transformation implements Annotated { - /** - * @since 0.67.0 - */ - readonly _tag = "Transformation" - constructor( - readonly from: AST, - readonly to: AST, - readonly transformation: TransformationKind, - readonly annotations: Annotations = {} - ) {} - /** - * @since 0.67.0 - */ - toString() { - return Option.getOrElse( - getExpected(this), - () => `(${String(this.from)} <-> ${String(this.to)})` - ) - } - /** - * @since 0.67.0 - */ - toJSON(): object { - return { - _tag: this._tag, - from: this.from.toJSON(), - to: this.to.toJSON(), - annotations: toJSONAnnotations(this.annotations) - } - } -} - -/** - * @category guards - * @since 0.67.0 - */ -export const isTransformation: (ast: AST) => ast is Transformation = createASTGuard("Transformation") - -/** - * @category model - * @since 0.67.0 - */ -export type TransformationKind = - | FinalTransformation - | ComposeTransformation - | TypeLiteralTransformation - -/** - * @category model - * @since 0.67.0 - */ -export class FinalTransformation { - /** - * @since 0.67.0 - */ - readonly _tag = "FinalTransformation" - constructor( - readonly decode: ( - fromA: any, - options: ParseOptions, - self: Transformation, - fromI: any - ) => Effect, - readonly encode: (toI: any, options: ParseOptions, self: Transformation, toA: any) => Effect - ) {} -} - -const createTransformationGuard = - (tag: T) => - (ast: TransformationKind): ast is Extract => ast._tag === tag - -/** - * @category guards - * @since 0.67.0 - */ -export const isFinalTransformation: (ast: TransformationKind) => ast is FinalTransformation = createTransformationGuard( - "FinalTransformation" -) - -/** - * @category model - * @since 0.67.0 - */ -export class ComposeTransformation { - /** - * @since 0.67.0 - */ - readonly _tag = "ComposeTransformation" -} - -/** - * @category constructors - * @since 0.67.0 - */ -export const composeTransformation: ComposeTransformation = new ComposeTransformation() - -/** - * @category guards - * @since 0.67.0 - */ -export const isComposeTransformation: (ast: TransformationKind) => ast is ComposeTransformation = - createTransformationGuard( - "ComposeTransformation" - ) - -/** - * Represents a `PropertySignature -> PropertySignature` transformation - * - * The semantic of `decode` is: - * - `none()` represents the absence of the key/value pair - * - `some(value)` represents the presence of the key/value pair - * - * The semantic of `encode` is: - * - `none()` you don't want to output the key/value pair - * - `some(value)` you want to output the key/value pair - * - * @category model - * @since 0.67.0 - */ -export class PropertySignatureTransformation { - constructor( - readonly from: PropertyKey, - readonly to: PropertyKey, - readonly decode: (o: Option.Option) => Option.Option, - readonly encode: (o: Option.Option) => Option.Option - ) {} -} - -const isRenamingPropertySignatureTransformation = (t: PropertySignatureTransformation) => - t.decode === identity && t.encode === identity - -/** - * @category model - * @since 0.67.0 - */ -export class TypeLiteralTransformation { - /** - * @since 0.67.0 - */ - readonly _tag = "TypeLiteralTransformation" - constructor( - readonly propertySignatureTransformations: ReadonlyArray< - PropertySignatureTransformation - > - ) { - // check for duplicate property signature transformations - const fromKeys: Record = {} - const toKeys: Record = {} - for (const pst of propertySignatureTransformations) { - const from = pst.from - if (fromKeys[from]) { - throw new Error(errors_.getASTDuplicatePropertySignatureTransformationErrorMessage(from)) - } - fromKeys[from] = true - const to = pst.to - if (toKeys[to]) { - throw new Error(errors_.getASTDuplicatePropertySignatureTransformationErrorMessage(to)) - } - toKeys[to] = true - } - } -} - -/** - * @category guards - * @since 0.67.0 - */ -export const isTypeLiteralTransformation: (ast: TransformationKind) => ast is TypeLiteralTransformation = - createTransformationGuard("TypeLiteralTransformation") - -// ------------------------------------------------------------------------------------- -// API -// ------------------------------------------------------------------------------------- - -/** - * Merges a set of new annotations with existing ones, potentially overwriting - * any duplicates. - * - * @since 0.67.0 - */ -export const annotations = (ast: AST, annotations: Annotations): AST => { - const d = Object.getOwnPropertyDescriptors(ast) - d.annotations.value = { ...ast.annotations, ...annotations } - return Object.create(Object.getPrototypeOf(ast), d) -} - -/** - * Equivalent at runtime to the TypeScript type-level `keyof` operator. - * - * @since 0.67.0 - */ -export const keyof = (ast: AST): AST => Union.unify(_keyof(ast)) - -const STRING_KEYWORD_PATTERN = ".*" -const NUMBER_KEYWORD_PATTERN = "[+-]?\\d*\\.?\\d+(?:[Ee][+-]?\\d+)?" - -/** - * @since 0.67.0 - */ -export const getTemplateLiteralRegExp = (ast: TemplateLiteral): RegExp => { - let pattern = `^${regexp.escape(ast.head)}` - - for (const span of ast.spans) { - if (isStringKeyword(span.type)) { - pattern += STRING_KEYWORD_PATTERN - } else if (isNumberKeyword(span.type)) { - pattern += NUMBER_KEYWORD_PATTERN - } - pattern += regexp.escape(span.literal) - } - - pattern += "$" - return new RegExp(pattern) -} - -/** - * @since 0.70.1 - */ -export const getTemplateLiteralCapturingRegExp = (ast: TemplateLiteral): RegExp => { - let pattern = `^` - if (ast.head !== "") { - pattern += `(${regexp.escape(ast.head)})` - } - - for (const span of ast.spans) { - if (isStringKeyword(span.type)) { - pattern += `(${STRING_KEYWORD_PATTERN})` - } else if (isNumberKeyword(span.type)) { - pattern += `(${NUMBER_KEYWORD_PATTERN})` - } - if (span.literal !== "") { - pattern += `(${regexp.escape(span.literal)})` - } - } - - pattern += "$" - return new RegExp(pattern) -} - -/** - * @since 0.67.0 - */ -export const getPropertySignatures = (ast: AST): Array => { - switch (ast._tag) { - case "Declaration": { - const annotation = getSurrogateAnnotation(ast) - if (Option.isSome(annotation)) { - return getPropertySignatures(annotation.value) - } - break - } - case "TypeLiteral": - return ast.propertySignatures.slice() - case "Suspend": - return getPropertySignatures(ast.f()) - } - return getPropertyKeys(ast).map((name) => getPropertyKeyIndexedAccess(ast, name)) -} - -/** @internal */ -export const getNumberIndexedAccess = (ast: AST): AST => { - switch (ast._tag) { - case "TupleType": { - let hasOptional = false - let out: Array = [] - for (const e of ast.elements) { - if (e.isOptional) { - hasOptional = true - } - out.push(e.type) - } - if (hasOptional) { - out.push(undefinedKeyword) - } - out = out.concat(getRestASTs(ast.rest)) - return Union.make(out) - } - case "Refinement": - return getNumberIndexedAccess(ast.from) - case "Union": - return Union.make(ast.types.map(getNumberIndexedAccess)) - case "Suspend": - return getNumberIndexedAccess(ast.f()) - } - throw new Error(errors_.getASTUnsupportedSchema(ast)) -} - -/** @internal */ -export const getPropertyKeyIndexedAccess = (ast: AST, name: PropertyKey): PropertySignature => { - switch (ast._tag) { - case "Declaration": { - const annotation = getSurrogateAnnotation(ast) - if (Option.isSome(annotation)) { - return getPropertyKeyIndexedAccess(annotation.value, name) - } - break - } - case "TypeLiteral": { - const ops = Arr.findFirst(ast.propertySignatures, (ps) => ps.name === name) - if (Option.isSome(ops)) { - return ops.value - } else { - if (Predicate.isString(name)) { - let out: PropertySignature | undefined = undefined - for (const is of ast.indexSignatures) { - const parameterBase = getParameterBase(is.parameter) - switch (parameterBase._tag) { - case "TemplateLiteral": { - const regex = getTemplateLiteralRegExp(parameterBase) - if (regex.test(name)) { - return new PropertySignature(name, is.type, false, true) - } - break - } - case "StringKeyword": { - if (out === undefined) { - out = new PropertySignature(name, is.type, false, true) - } - } - } - } - if (out) { - return out - } - } else if (Predicate.isSymbol(name)) { - for (const is of ast.indexSignatures) { - const parameterBase = getParameterBase(is.parameter) - if (isSymbolKeyword(parameterBase)) { - return new PropertySignature(name, is.type, false, true) - } - } - } - } - break - } - case "Union": - return new PropertySignature( - name, - Union.make(ast.types.map((ast) => getPropertyKeyIndexedAccess(ast, name).type)), - false, - true - ) - case "Suspend": - return getPropertyKeyIndexedAccess(ast.f(), name) - } - return new PropertySignature(name, neverKeyword, false, true) -} - -const getPropertyKeys = (ast: AST): Array => { - switch (ast._tag) { - case "Declaration": { - const annotation = getSurrogateAnnotation(ast) - if (Option.isSome(annotation)) { - return getPropertyKeys(annotation.value) - } - break - } - case "TypeLiteral": - return ast.propertySignatures.map((ps) => ps.name) - case "Suspend": - return getPropertyKeys(ast.f()) - case "Union": - return ast.types.slice(1).reduce( - (out: Array, ast) => Arr.intersection(out, getPropertyKeys(ast)), - getPropertyKeys(ast.types[0]) - ) - case "Transformation": - return getPropertyKeys(ast.to) - } - return [] -} - -/** @internal */ -export const record = (key: AST, value: AST): { - propertySignatures: Array - indexSignatures: Array -} => { - const propertySignatures: Array = [] - const indexSignatures: Array = [] - const go = (key: AST): void => { - switch (key._tag) { - case "NeverKeyword": - break - case "StringKeyword": - case "SymbolKeyword": - case "TemplateLiteral": - case "Refinement": - indexSignatures.push(new IndexSignature(key, value, true)) - break - case "Literal": - if (Predicate.isString(key.literal) || Predicate.isNumber(key.literal)) { - propertySignatures.push(new PropertySignature(key.literal, value, false, true)) - } else { - throw new Error(errors_.getASTUnsupportedLiteral(key.literal)) - } - break - case "Enums": { - for (const [_, name] of key.enums) { - propertySignatures.push(new PropertySignature(name, value, false, true)) - } - break - } - case "UniqueSymbol": - propertySignatures.push(new PropertySignature(key.symbol, value, false, true)) - break - case "Union": - key.types.forEach(go) - break - default: - throw new Error(errors_.getASTUnsupportedKeySchema(key)) - } - } - go(key) - return { propertySignatures, indexSignatures } -} - -/** - * Equivalent at runtime to the built-in TypeScript utility type `Pick`. - * - * @since 0.67.0 - */ -export const pick = (ast: AST, keys: ReadonlyArray): TypeLiteral | Transformation => { - if (isTransformation(ast)) { - switch (ast.transformation._tag) { - case "ComposeTransformation": - return new Transformation( - pick(ast.from, keys), - pick(ast.to, keys), - composeTransformation - ) - case "TypeLiteralTransformation": { - const ts: Array = [] - const fromKeys: Array = [] - for (const k of keys) { - const t = ast.transformation.propertySignatureTransformations.find((t) => t.to === k) - if (t) { - ts.push(t) - fromKeys.push(t.from) - } else { - fromKeys.push(k) - } - } - return Arr.isNonEmptyReadonlyArray(ts) ? - new Transformation( - pick(ast.from, fromKeys), - pick(ast.to, keys), - new TypeLiteralTransformation(ts) - ) : - pick(ast.from, fromKeys) - } - case "FinalTransformation": { - const annotation = getSurrogateAnnotation(ast) - if (Option.isSome(annotation)) { - return pick(annotation.value, keys) - } - throw new Error(errors_.getASTUnsupportedSchema(ast)) - } - } - } - return new TypeLiteral(keys.map((key) => getPropertyKeyIndexedAccess(ast, key)), []) -} - -/** - * Equivalent at runtime to the built-in TypeScript utility type `Omit`. - * - * @since 0.67.0 - */ -export const omit = (ast: AST, keys: ReadonlyArray): TypeLiteral | Transformation => - pick(ast, getPropertyKeys(ast).filter((name) => !keys.includes(name))) - -/** @internal */ -export const orUndefined = (ast: AST): AST => Union.make([ast, undefinedKeyword]) - -/** - * Equivalent at runtime to the built-in TypeScript utility type `Partial`. - * - * @since 0.67.0 - */ -export const partial = (ast: AST, options?: { readonly exact: true }): AST => { - const exact = options?.exact === true - switch (ast._tag) { - case "TupleType": - return new TupleType( - ast.elements.map((e) => new OptionalType(exact ? e.type : orUndefined(e.type), true)), - Arr.match(ast.rest, { - onEmpty: () => ast.rest, - onNonEmpty: (rest) => [new Type(Union.make([...getRestASTs(rest), undefinedKeyword]))] - }), - ast.isReadonly - ) - case "TypeLiteral": - return new TypeLiteral( - ast.propertySignatures.map((ps) => - new PropertySignature(ps.name, exact ? ps.type : orUndefined(ps.type), true, ps.isReadonly, ps.annotations) - ), - ast.indexSignatures.map((is) => new IndexSignature(is.parameter, orUndefined(is.type), is.isReadonly)) - ) - case "Union": - return Union.make(ast.types.map((member) => partial(member, options))) - case "Suspend": - return new Suspend(() => partial(ast.f(), options)) - case "Declaration": - throw new Error(errors_.getASTUnsupportedSchema(ast)) - case "Refinement": - throw new Error(errors_.getASTUnsupportedSchema(ast)) - case "Transformation": { - if ( - isTypeLiteralTransformation(ast.transformation) && - ast.transformation.propertySignatureTransformations.every(isRenamingPropertySignatureTransformation) - ) { - return new Transformation(partial(ast.from, options), partial(ast.to, options), ast.transformation) - } - throw new Error(errors_.getASTUnsupportedSchema(ast)) - } - } - return ast -} - -/** - * Equivalent at runtime to the built-in TypeScript utility type `Required`. - * - * @since 0.67.0 - */ -export const required = (ast: AST): AST => { - switch (ast._tag) { - case "TupleType": - return new TupleType( - ast.elements.map((e) => new OptionalType(e.type, false)), - ast.rest, - ast.isReadonly - ) - case "TypeLiteral": - return new TypeLiteral( - ast.propertySignatures.map((f) => new PropertySignature(f.name, f.type, false, f.isReadonly, f.annotations)), - ast.indexSignatures - ) - case "Union": - return Union.make(ast.types.map((member) => required(member))) - case "Suspend": - return new Suspend(() => required(ast.f())) - case "Declaration": - throw new Error(errors_.getASTUnsupportedSchema(ast)) - case "Refinement": - throw new Error(errors_.getASTUnsupportedSchema(ast)) - case "Transformation": { - if ( - isTypeLiteralTransformation(ast.transformation) && - ast.transformation.propertySignatureTransformations.every(isRenamingPropertySignatureTransformation) - ) { - return new Transformation(required(ast.from), required(ast.to), ast.transformation) - } - throw new Error(errors_.getASTUnsupportedSchema(ast)) - } - } - return ast -} - -/** - * Creates a new AST with shallow mutability applied to its properties. - * - * @param ast - The original AST to make properties mutable (shallowly). - * - * @since 0.67.0 - */ -export const mutable = (ast: AST): AST => { - switch (ast._tag) { - case "TupleType": - return ast.isReadonly === false ? ast : new TupleType(ast.elements, ast.rest, false, ast.annotations) - case "TypeLiteral": { - const propertySignatures = changeMap( - ast.propertySignatures, - (ps) => - ps.isReadonly === false ? ps : new PropertySignature(ps.name, ps.type, ps.isOptional, false, ps.annotations) - ) - const indexSignatures = changeMap( - ast.indexSignatures, - (is) => is.isReadonly === false ? is : new IndexSignature(is.parameter, is.type, false) - ) - return propertySignatures === ast.propertySignatures && indexSignatures === ast.indexSignatures ? - ast : - new TypeLiteral(propertySignatures, indexSignatures, ast.annotations) - } - case "Union": { - const types = changeMap(ast.types, mutable) - return types === ast.types ? ast : Union.make(types, ast.annotations) - } - case "Suspend": - return new Suspend(() => mutable(ast.f()), ast.annotations) - case "Refinement": { - const from = mutable(ast.from) - return from === ast.from ? ast : new Refinement(from, ast.filter, ast.annotations) - } - case "Transformation": { - const from = mutable(ast.from) - const to = mutable(ast.to) - return from === ast.from && to === ast.to ? - ast : - new Transformation(from, to, ast.transformation, ast.annotations) - } - } - return ast -} - -// ------------------------------------------------------------------------------------- -// compiler harness -// ------------------------------------------------------------------------------------- - -/** - * @since 0.67.0 - */ -export type Compiler = (ast: AST, path: ReadonlyArray) => A - -/** - * @since 0.67.0 - */ -export type Match = { - [K in AST["_tag"]]: (ast: Extract, compile: Compiler, path: ReadonlyArray) => A -} - -/** - * @since 0.67.0 - */ -export const getCompiler = (match: Match): Compiler => { - const compile = (ast: AST, path: ReadonlyArray): A => match[ast._tag](ast as any, compile, path) - return compile -} - -/** - * @since 0.67.0 - */ -export const typeAST = (ast: AST): AST => { - switch (ast._tag) { - case "Declaration": { - const typeParameters = changeMap(ast.typeParameters, typeAST) - return typeParameters === ast.typeParameters ? - ast : - new Declaration(typeParameters, ast.decodeUnknown, ast.encodeUnknown, ast.annotations) - } - case "TupleType": { - const elements = changeMap(ast.elements, (e) => { - const type = typeAST(e.type) - return type === e.type ? e : new OptionalType(type, e.isOptional) - }) - const restASTs = getRestASTs(ast.rest) - const rest = changeMap(restASTs, typeAST) - return elements === ast.elements && rest === restASTs ? - ast : - new TupleType(elements, rest.map((type) => new Type(type)), ast.isReadonly, ast.annotations) - } - case "TypeLiteral": { - const propertySignatures = changeMap(ast.propertySignatures, (p) => { - const type = typeAST(p.type) - return type === p.type ? p : new PropertySignature(p.name, type, p.isOptional, p.isReadonly) - }) - const indexSignatures = changeMap(ast.indexSignatures, (is) => { - const type = typeAST(is.type) - return type === is.type ? is : new IndexSignature(is.parameter, type, is.isReadonly) - }) - return propertySignatures === ast.propertySignatures && indexSignatures === ast.indexSignatures ? - ast : - new TypeLiteral(propertySignatures, indexSignatures, ast.annotations) - } - case "Union": { - const types = changeMap(ast.types, typeAST) - return types === ast.types ? ast : Union.make(types, ast.annotations) - } - case "Suspend": - return new Suspend(() => typeAST(ast.f()), ast.annotations) - case "Refinement": { - const from = typeAST(ast.from) - return from === ast.from ? - ast : - new Refinement(from, ast.filter, ast.annotations) - } - case "Transformation": - return typeAST(ast.to) - } - return ast -} - -/** @internal */ -export const whiteListAnnotations = - (annotationIds: ReadonlyArray) => (annotated: Annotated): Annotations | undefined => { - let out: { [_: symbol]: unknown } | undefined = undefined - for (const id of annotationIds) { - if (Object.prototype.hasOwnProperty.call(annotated.annotations, id)) { - if (out === undefined) { - out = {} - } - out[id] = annotated.annotations[id] - } - } - return out - } - -/** @internal */ -export const blackListAnnotations = - (annotationIds: ReadonlyArray) => (annotated: Annotated): Annotations | undefined => { - const out = { ...annotated.annotations } - for (const id of annotationIds) { - delete out[id] - } - return out - } - -/** @internal */ -export const getJSONIdentifier = (annotated: Annotated) => - Option.orElse(getJSONIdentifierAnnotation(annotated), () => getIdentifierAnnotation(annotated)) - -// To generate a JSON Schema from a recursive schema, an `identifier` annotation -// is required. So, when we calculate the encodedAST, we need to preserve the -// annotation in the form of an internal custom annotation that acts as a -// surrogate for the identifier, which the JSON Schema compiler can then read. -const createJSONIdentifierAnnotation = (annotated: Annotated): Annotations | undefined => - Option.match(getJSONIdentifier(annotated), { - onNone: () => undefined, - onSome: (identifier) => ({ [JSONIdentifierAnnotationId]: identifier }) - }) - -function changeMap( - as: Arr.NonEmptyReadonlyArray, - f: (a: A) => A -): Arr.NonEmptyReadonlyArray -function changeMap(as: ReadonlyArray, f: (a: A) => A): ReadonlyArray -function changeMap(as: ReadonlyArray, f: (a: A) => A): ReadonlyArray { - let changed = false - const out = Arr.allocate(as.length) as Array - for (let i = 0; i < as.length; i++) { - const a = as[i] - const fa = f(a) - if (fa !== a) { - changed = true - } - out[i] = fa - } - return changed ? out : as -} - -const encodedAST_ = (ast: AST, isBound: boolean): AST => { - switch (ast._tag) { - case "Declaration": { - const typeParameters = changeMap(ast.typeParameters, (ast) => encodedAST_(ast, isBound)) - return typeParameters === ast.typeParameters ? - ast : - new Declaration(typeParameters, ast.decodeUnknown, ast.encodeUnknown, ast.annotations) - } - case "TupleType": { - const elements = changeMap(ast.elements, (e) => { - const type = encodedAST_(e.type, isBound) - return type === e.type ? e : new OptionalType(type, e.isOptional) - }) - const restASTs = getRestASTs(ast.rest) - const rest = changeMap(restASTs, (ast) => encodedAST_(ast, isBound)) - return elements === ast.elements && rest === restASTs ? - ast : - new TupleType( - elements, - rest.map((ast) => new Type(ast)), - ast.isReadonly, - createJSONIdentifierAnnotation(ast) - ) - } - case "TypeLiteral": { - const propertySignatures = changeMap(ast.propertySignatures, (ps) => { - const type = encodedAST_(ps.type, isBound) - return type === ps.type - ? ps - : new PropertySignature(ps.name, type, ps.isOptional, ps.isReadonly) - }) - const indexSignatures = changeMap(ast.indexSignatures, (is) => { - const type = encodedAST_(is.type, isBound) - return type === is.type ? is : new IndexSignature(is.parameter, type, is.isReadonly) - }) - return propertySignatures === ast.propertySignatures && indexSignatures === ast.indexSignatures ? - ast : - new TypeLiteral(propertySignatures, indexSignatures, createJSONIdentifierAnnotation(ast)) - } - case "Union": { - const types = changeMap(ast.types, (ast) => encodedAST_(ast, isBound)) - return types === ast.types ? ast : Union.make(types, createJSONIdentifierAnnotation(ast)) - } - case "Suspend": - return new Suspend(() => encodedAST_(ast.f(), isBound), createJSONIdentifierAnnotation(ast)) - case "Refinement": { - const from = encodedAST_(ast.from, isBound) - if (isBound) { - if (from === ast.from) { - return ast - } - if (!isTransformation(ast.from) && hasStableFilter(ast)) { - return new Refinement(from, ast.filter) - } - } - return from - } - case "Transformation": - return encodedAST_(ast.from, isBound) - } - return ast -} - /** - * @since 0.67.0 - */ -export const encodedAST = (ast: AST): AST => encodedAST_(ast, false) - -/** - * @since 0.67.0 + * @category re-exports + * @since 0.76.0 */ -export const encodedBoundAST = (ast: AST): AST => encodedAST_(ast, true) - -const toJSONAnnotations = (annotations: Annotations): object => { - const out: Record = {} - for (const k of Object.getOwnPropertySymbols(annotations)) { - out[String(k)] = annotations[k] - } - return out -} - -/** @internal */ -export const getParameterBase = ( - ast: Parameter -): StringKeyword | SymbolKeyword | TemplateLiteral => { - switch (ast._tag) { - case "StringKeyword": - case "SymbolKeyword": - case "TemplateLiteral": - return ast - case "Refinement": - return getParameterBase(ast.from) - } -} - -const equalsTemplateLiteralSpan = Arr.getEquivalence((self, that) => - self.type._tag === that.type._tag && self.literal === that.literal -) - -const equalsEnums = Arr.getEquivalence((self, that) => - that[0] === self[0] && that[1] === self[1] -) - -const equals = (self: AST, that: AST) => { - switch (self._tag) { - case "Literal": - return isLiteral(that) && that.literal === self.literal - case "UniqueSymbol": - return isUniqueSymbol(that) && that.symbol === self.symbol - case "UndefinedKeyword": - case "VoidKeyword": - case "NeverKeyword": - case "UnknownKeyword": - case "AnyKeyword": - case "StringKeyword": - case "NumberKeyword": - case "BooleanKeyword": - case "BigIntKeyword": - case "SymbolKeyword": - case "ObjectKeyword": - return that._tag === self._tag - case "TemplateLiteral": - return isTemplateLiteral(that) && that.head === self.head && equalsTemplateLiteralSpan(that.spans, self.spans) - case "Enums": - return isEnums(that) && equalsEnums(that.enums, self.enums) - case "Refinement": - case "TupleType": - case "TypeLiteral": - case "Union": - case "Suspend": - case "Transformation": - case "Declaration": - return self === that - } -} - -const intersection = Arr.intersectionWith(equals) - -const _keyof = (ast: AST): Array => { - switch (ast._tag) { - case "Declaration": { - const annotation = getSurrogateAnnotation(ast) - if (Option.isSome(annotation)) { - return _keyof(annotation.value) - } - break - } - case "TypeLiteral": - return ast.propertySignatures.map((p): AST => - Predicate.isSymbol(p.name) ? new UniqueSymbol(p.name) : new Literal(p.name) - ).concat(ast.indexSignatures.map((is) => getParameterBase(is.parameter))) - case "Suspend": - return _keyof(ast.f()) - case "Union": - return ast.types.slice(1).reduce( - (out: Array, ast) => intersection(out, _keyof(ast)), - _keyof(ast.types[0]) - ) - case "Transformation": - return _keyof(ast.to) - } - throw new Error(errors_.getASTUnsupportedSchema(ast)) -} - -/** @internal */ -export const compose = (ab: AST, cd: AST): AST => new Transformation(ab, cd, composeTransformation) - -/** @internal */ -export const rename = (ast: AST, mapping: { readonly [K in PropertyKey]?: PropertyKey }): AST => { - switch (ast._tag) { - case "TypeLiteral": { - const propertySignatureTransformations: Array = [] - for (const key of util_.ownKeys(mapping)) { - const name = mapping[key] - if (name !== undefined) { - propertySignatureTransformations.push( - new PropertySignatureTransformation( - key, - name, - identity, - identity - ) - ) - } - } - if (propertySignatureTransformations.length === 0) { - return ast - } - return new Transformation( - ast, - new TypeLiteral( - ast.propertySignatures.map((ps) => { - const name = mapping[ps.name] - return new PropertySignature( - name === undefined ? ps.name : name, - typeAST(ps.type), - ps.isOptional, - ps.isReadonly, - ps.annotations - ) - }), - ast.indexSignatures - ), - new TypeLiteralTransformation(propertySignatureTransformations) - ) - } - case "Union": - return Union.make(ast.types.map((ast) => rename(ast, mapping))) - case "Suspend": - return new Suspend(() => rename(ast.f(), mapping)) - case "Transformation": - return compose(ast, rename(typeAST(ast), mapping)) - } - throw new Error(errors_.getASTUnsupportedRenameSchema(ast)) -} - -const formatKeyword = (ast: AST): string => Option.getOrElse(getExpected(ast), () => ast._tag) - -const getExpected = (ast: Annotated): Option.Option => { - return getIdentifierAnnotation(ast).pipe( - Option.orElse(() => getTitleAnnotation(ast)), - Option.orElse(() => getDescriptionAnnotation(ast)) - ) -} +export * from "effect/SchemaAST" diff --git a/packages/schema/src/Arbitrary.ts b/packages/schema/src/Arbitrary.ts index 9d9343643c..58ec822031 100644 --- a/packages/schema/src/Arbitrary.ts +++ b/packages/schema/src/Arbitrary.ts @@ -2,579 +2,8 @@ * @since 0.67.0 */ -import * as Arr from "effect/Array" -import * as Option from "effect/Option" -import * as Predicate from "effect/Predicate" -import * as AST from "./AST.js" -import * as FastCheck from "./FastCheck.js" -import * as errors_ from "./internal/errors.js" -import * as filters_ from "./internal/filters.js" -import * as util_ from "./internal/util.js" -import type * as Schema from "./Schema.js" - -/** - * @category model - * @since 0.67.0 - */ -export interface LazyArbitrary { - (fc: typeof FastCheck): FastCheck.Arbitrary -} - -/** - * @category hooks - * @since 0.67.0 - */ -export const ArbitraryHookId: unique symbol = Symbol.for("@effect/schema/ArbitraryHookId") - -/** - * @category hooks - * @since 0.67.0 - */ -export type ArbitraryHookId = typeof ArbitraryHookId - -/** - * @category hooks - * @since 0.72.3 - */ -export interface GenerationContext { - readonly depthIdentifier?: string - readonly maxDepth: number -} - -/** - * @category hooks - * @since 0.72.3 - */ -export type ArbitraryAnnotation = ( - ...args: [...ReadonlyArray>, GenerationContext] -) => LazyArbitrary - -/** - * @category annotations - * @since 0.67.0 - */ -export const arbitrary = - (annotation: ArbitraryAnnotation) => (self: Schema.Schema): Schema.Schema => - self.annotations({ [ArbitraryHookId]: annotation }) - -/** - * Returns a LazyArbitrary for the `A` type of the provided schema. - * - * @category arbitrary - * @since 0.67.0 - */ -export const makeLazy = (schema: Schema.Schema): LazyArbitrary => - go(schema.ast, { maxDepth: 2 }, []) - /** - * Returns a fast-check Arbitrary for the `A` type of the provided schema. - * - * @category arbitrary - * @since 0.67.0 + * @category re-exports + * @since 0.76.0 */ -export const make = (schema: Schema.Schema): FastCheck.Arbitrary => makeLazy(schema)(FastCheck) - -const getHook = AST.getAnnotation>(ArbitraryHookId) - -const getRefinementFromArbitrary = ( - ast: AST.Refinement, - ctx: Context, - path: ReadonlyArray -) => { - const constraints = combineConstraints(ctx.constraints, getConstraints(ast)) - return go(ast.from, constraints ? { ...ctx, constraints } : ctx, path) -} - -const getSuspendedContext = ( - ctx: Context, - ast: AST.Suspend -): Context => { - if (ctx.depthIdentifier !== undefined) { - return ctx - } - const depthIdentifier = AST.getIdentifierAnnotation(ast).pipe( - Option.orElse(() => AST.getIdentifierAnnotation(ast.f())), - Option.getOrElse(() => "SuspendDefaultDepthIdentifier") - ) - return { ...ctx, depthIdentifier } -} - -const getSuspendedArray = ( - fc: typeof FastCheck, - depthIdentifier: string, - maxDepth: number, - item: FastCheck.Arbitrary, - constraints?: FastCheck.ArrayConstraints -) => { - let minLength = 1 - let maxLength = 2 - if (constraints && constraints.minLength !== undefined && constraints.minLength > minLength) { - minLength = constraints.minLength - if (minLength > maxLength) { - maxLength = minLength - } - } - return fc.oneof( - { maxDepth, depthIdentifier }, - fc.constant([]), - fc.array(item, { minLength, maxLength }) - ) -} - -interface Context extends GenerationContext { - readonly constraints?: Constraints -} - -const go = ( - ast: AST.AST, - ctx: Context, - path: ReadonlyArray -): LazyArbitrary => { - const hook = getHook(ast) - if (Option.isSome(hook)) { - switch (ast._tag) { - case "Declaration": - return hook.value(...ast.typeParameters.map((p) => go(p, ctx, path)), ctx) - case "Refinement": - return hook.value(getRefinementFromArbitrary(ast, ctx, path), ctx) - default: - return hook.value(ctx) - } - } - switch (ast._tag) { - case "Declaration": { - throw new Error(errors_.getArbitraryMissingAnnotationErrorMessage(path, ast)) - } - case "Literal": - return (fc) => fc.constant(ast.literal) - case "UniqueSymbol": - return (fc) => fc.constant(ast.symbol) - case "UndefinedKeyword": - return (fc) => fc.constant(undefined) - case "NeverKeyword": - return () => { - throw new Error(errors_.getArbitraryUnsupportedErrorMessage(path, ast)) - } - case "UnknownKeyword": - case "AnyKeyword": - case "VoidKeyword": - return (fc) => fc.anything() - case "StringKeyword": - return (fc) => { - if (ctx.constraints) { - switch (ctx.constraints._tag) { - case "StringConstraints": - return fc.string(ctx.constraints.constraints) - } - } - return fc.string() - } - case "NumberKeyword": - return (fc) => { - if (ctx.constraints) { - switch (ctx.constraints._tag) { - case "NumberConstraints": - return fc.float(ctx.constraints.constraints) - case "IntegerConstraints": - return fc.integer(ctx.constraints.constraints) - } - } - return fc.float() - } - case "BooleanKeyword": - return (fc) => fc.boolean() - case "BigIntKeyword": - return (fc) => { - if (ctx.constraints) { - switch (ctx.constraints._tag) { - case "BigIntConstraints": - return fc.bigInt(ctx.constraints.constraints) - } - } - return fc.bigInt() - } - case "SymbolKeyword": - return (fc) => fc.string().map((s) => Symbol.for(s)) - case "ObjectKeyword": - return (fc) => fc.oneof(fc.object(), fc.array(fc.anything())) - case "TemplateLiteral": { - return (fc) => { - const string = fc.string({ maxLength: 5 }) - const number = fc.float({ noDefaultInfinity: true }).filter((n) => !Number.isNaN(n)) - const components: Array> = [fc.constant(ast.head)] - for (const span of ast.spans) { - if (AST.isStringKeyword(span.type)) { - components.push(string) - } else { - components.push(number) - } - components.push(fc.constant(span.literal)) - } - return fc.tuple(...components).map((spans) => spans.join("")) - } - } - case "TupleType": { - const elements: Array> = [] - let hasOptionals = false - let i = 0 - for (const element of ast.elements) { - elements.push(go(element.type, ctx, path.concat(i++))) - if (element.isOptional) { - hasOptionals = true - } - } - const rest = ast.rest.map((annotatedAST) => go(annotatedAST.type, ctx, path)) - return (fc) => { - // --------------------------------------------- - // handle elements - // --------------------------------------------- - let output = fc.tuple(...elements.map((arb) => arb(fc))) - if (hasOptionals) { - const indexes = fc.tuple( - ...ast.elements.map((element) => element.isOptional ? fc.boolean() : fc.constant(true)) - ) - output = output.chain((tuple) => - indexes.map((booleans) => { - for (const [i, b] of booleans.reverse().entries()) { - if (!b) { - tuple.splice(booleans.length - i, 1) - } - } - return tuple - }) - ) - } - - // --------------------------------------------- - // handle rest element - // --------------------------------------------- - if (Arr.isNonEmptyReadonlyArray(rest)) { - const [head, ...tail] = rest - const item = head(fc) - const constraints: FastCheck.ArrayConstraints | undefined = - ctx.constraints && ctx.constraints._tag === "ArrayConstraints" - ? ctx.constraints.constraints - : undefined - output = output.chain((as) => { - return (ctx.depthIdentifier !== undefined - ? getSuspendedArray(fc, ctx.depthIdentifier, ctx.maxDepth, item, constraints) - : fc.array(item, constraints)).map((rest) => [...as, ...rest]) - }) - // --------------------------------------------- - // handle post rest elements - // --------------------------------------------- - for (let j = 0; j < tail.length; j++) { - output = output.chain((as) => tail[j](fc).map((a) => [...as, a])) - } - } - - return output - } - } - case "TypeLiteral": { - const propertySignaturesTypes = ast.propertySignatures.map((ps) => go(ps.type, ctx, path.concat(ps.name))) - const indexSignatures = ast.indexSignatures.map((is) => - [go(is.parameter, ctx, path), go(is.type, ctx, path)] as const - ) - return (fc) => { - const arbs: any = {} - const requiredKeys: Array = [] - // --------------------------------------------- - // handle property signatures - // --------------------------------------------- - for (let i = 0; i < propertySignaturesTypes.length; i++) { - const ps = ast.propertySignatures[i] - const name = ps.name - if (!ps.isOptional) { - requiredKeys.push(name) - } - arbs[name] = propertySignaturesTypes[i](fc) - } - let output = fc.record(arbs, { requiredKeys }) - // --------------------------------------------- - // handle index signatures - // --------------------------------------------- - for (let i = 0; i < indexSignatures.length; i++) { - const key = indexSignatures[i][0](fc) - const value = indexSignatures[i][1](fc) - output = output.chain((o) => { - const item = fc.tuple(key, value) - const arr = ctx.depthIdentifier !== undefined ? - getSuspendedArray(fc, ctx.depthIdentifier, ctx.maxDepth, item) : - fc.array(item) - return arr.map((tuples) => ({ ...Object.fromEntries(tuples), ...o })) - }) - } - - return output - } - } - case "Union": { - const types = ast.types.map((member) => go(member, ctx, path)) - return (fc) => fc.oneof(...types.map((arb) => arb(fc))) - } - case "Enums": { - if (ast.enums.length === 0) { - throw new Error(errors_.getArbitraryEmptyEnumErrorMessage(path)) - } - return (fc) => fc.oneof(...ast.enums.map(([_, value]) => fc.constant(value))) - } - case "Refinement": { - const from = getRefinementFromArbitrary(ast, ctx, path) - return (fc) => from(fc).filter((a) => Option.isNone(ast.filter(a, AST.defaultParseOption, ast))) - } - case "Suspend": { - const get = util_.memoizeThunk(() => { - return go(ast.f(), getSuspendedContext(ctx, ast), path) - }) - return (fc) => fc.constant(null).chain(() => get()(fc)) - } - case "Transformation": - return go(ast.to, ctx, path) - } -} - -/** @internal */ -export class NumberConstraints { - readonly _tag = "NumberConstraints" - readonly constraints: FastCheck.FloatConstraints - constructor(options: { - readonly min?: number | undefined - readonly max?: number | undefined - readonly noNaN?: boolean | undefined - readonly noDefaultInfinity?: boolean | undefined - }) { - this.constraints = {} - if (Predicate.isNumber(options.min)) { - this.constraints.min = Math.fround(options.min) - } - if (Predicate.isNumber(options.max)) { - this.constraints.max = Math.fround(options.max) - } - if (Predicate.isBoolean(options.noNaN)) { - this.constraints.noNaN = options.noNaN - } - if (Predicate.isBoolean(options.noDefaultInfinity)) { - this.constraints.noDefaultInfinity = options.noDefaultInfinity - } - } -} - -/** @internal */ -export class StringConstraints { - readonly _tag = "StringConstraints" - readonly constraints: FastCheck.StringSharedConstraints - constructor(options: { - readonly minLength?: number | undefined - readonly maxLength?: number | undefined - }) { - this.constraints = {} - if (Predicate.isNumber(options.minLength)) { - this.constraints.minLength = options.minLength - } - if (Predicate.isNumber(options.maxLength)) { - this.constraints.maxLength = options.maxLength - } - } -} - -/** @internal */ -export class IntegerConstraints { - readonly _tag = "IntegerConstraints" - readonly constraints: FastCheck.IntegerConstraints - constructor(options: { - readonly min?: number | undefined - readonly max?: number | undefined - }) { - this.constraints = {} - if (Predicate.isNumber(options.min)) { - this.constraints.min = options.min - } - if (Predicate.isNumber(options.max)) { - this.constraints.max = options.max - } - } -} - -/** @internal */ -export class ArrayConstraints { - readonly _tag = "ArrayConstraints" - readonly constraints: FastCheck.ArrayConstraints - constructor(options: { - readonly minLength?: number | undefined - readonly maxLength?: number | undefined - }) { - this.constraints = {} - if (Predicate.isNumber(options.minLength)) { - this.constraints.minLength = options.minLength - } - if (Predicate.isNumber(options.maxLength)) { - this.constraints.maxLength = options.maxLength - } - } -} - -/** @internal */ -export class BigIntConstraints { - readonly _tag = "BigIntConstraints" - readonly constraints: FastCheck.BigIntConstraints - constructor(options: { - readonly min?: bigint | undefined - readonly max?: bigint | undefined - }) { - this.constraints = {} - if (Predicate.isBigInt(options.min)) { - this.constraints.min = options.min - } - if (Predicate.isBigInt(options.max)) { - this.constraints.max = options.max - } - } -} - -/** @internal */ -export type Constraints = - | NumberConstraints - | StringConstraints - | IntegerConstraints - | ArrayConstraints - | BigIntConstraints - -/** @internal */ -export const getConstraints = (ast: AST.Refinement): Constraints | undefined => { - const TypeAnnotationId = ast.annotations[AST.TypeAnnotationId] - const jsonSchema: any = ast.annotations[AST.JSONSchemaAnnotationId] - switch (TypeAnnotationId) { - // int - case filters_.IntTypeId: - return new IntegerConstraints({}) - // number - case filters_.GreaterThanTypeId: - case filters_.GreaterThanOrEqualToTypeId: - case filters_.LessThanTypeId: - case filters_.LessThanOrEqualToTypeId: - case filters_.BetweenTypeId: - return new NumberConstraints({ - min: jsonSchema.exclusiveMinimum ?? jsonSchema.minimum, - max: jsonSchema.exclusiveMaximum ?? jsonSchema.maximum - }) - // bigint - case filters_.GreaterThanBigintTypeId: - case filters_.GreaterThanOrEqualToBigIntTypeId: - case filters_.LessThanBigIntTypeId: - case filters_.LessThanOrEqualToBigIntTypeId: - case filters_.BetweenBigintTypeId: { - const constraints: any = ast.annotations[TypeAnnotationId] - return new BigIntConstraints(constraints) - } - // string - case filters_.MinLengthTypeId: - case filters_.MaxLengthTypeId: - case filters_.LengthTypeId: - return new StringConstraints(jsonSchema) - // array - case filters_.MinItemsTypeId: - case filters_.MaxItemsTypeId: - case filters_.ItemsCountTypeId: - return new ArrayConstraints({ - minLength: jsonSchema.minItems, - maxLength: jsonSchema.maxItems - }) - } -} - -/** @internal */ -export const combineConstraints = ( - c1: Constraints | undefined, - c2: Constraints | undefined -): Constraints | undefined => { - if (c1 === undefined) { - return c2 - } - if (c2 === undefined) { - return c1 - } - switch (c1._tag) { - case "ArrayConstraints": { - switch (c2._tag) { - case "ArrayConstraints": - return new ArrayConstraints({ - minLength: getMax(c1.constraints.minLength, c2.constraints.minLength), - maxLength: getMin(c1.constraints.maxLength, c2.constraints.maxLength) - }) - } - break - } - case "NumberConstraints": { - switch (c2._tag) { - case "NumberConstraints": - return new NumberConstraints({ - min: getMax(c1.constraints.min, c2.constraints.min), - max: getMin(c1.constraints.max, c2.constraints.max), - noNaN: getOr(c1.constraints.noNaN, c2.constraints.noNaN), - noDefaultInfinity: getOr(c1.constraints.noDefaultInfinity, c2.constraints.noDefaultInfinity) - }) - case "IntegerConstraints": - return new IntegerConstraints({ - min: getMax(c1.constraints.min, c2.constraints.min), - max: getMin(c1.constraints.max, c2.constraints.max) - }) - } - break - } - case "BigIntConstraints": { - switch (c2._tag) { - case "BigIntConstraints": - return new BigIntConstraints({ - min: getMax(c1.constraints.min, c2.constraints.min), - max: getMin(c1.constraints.max, c2.constraints.max) - }) - } - break - } - case "StringConstraints": { - switch (c2._tag) { - case "StringConstraints": - return new StringConstraints({ - minLength: getMax(c1.constraints.minLength, c2.constraints.minLength), - maxLength: getMin(c1.constraints.maxLength, c2.constraints.maxLength) - }) - } - break - } - case "IntegerConstraints": { - switch (c2._tag) { - case "NumberConstraints": - case "IntegerConstraints": { - return new IntegerConstraints({ - min: getMax(c1.constraints.min, c2.constraints.min), - max: getMin(c1.constraints.max, c2.constraints.max) - }) - } - } - break - } - } -} - -const getOr = (a: boolean | undefined, b: boolean | undefined): boolean | undefined => { - return a === undefined ? b : b === undefined ? a : a || b -} - -function getMax(n1: bigint | undefined, n2: bigint | undefined): bigint | undefined -function getMax(n1: number | undefined, n2: number | undefined): number | undefined -function getMax( - n1: bigint | number | undefined, - n2: bigint | number | undefined -): bigint | number | undefined { - return n1 === undefined ? n2 : n2 === undefined ? n1 : n1 <= n2 ? n2 : n1 -} - -function getMin(n1: bigint | undefined, n2: bigint | undefined): bigint | undefined -function getMin(n1: number | undefined, n2: number | undefined): number | undefined -function getMin( - n1: bigint | number | undefined, - n2: bigint | number | undefined -): bigint | number | undefined { - return n1 === undefined ? n2 : n2 === undefined ? n1 : n1 <= n2 ? n1 : n2 -} +export * from "effect/Arbitrary" diff --git a/packages/schema/src/ArrayFormatter.ts b/packages/schema/src/ArrayFormatter.ts index 7c5dfbe783..455b8c98c3 100644 --- a/packages/schema/src/ArrayFormatter.ts +++ b/packages/schema/src/ArrayFormatter.ts @@ -2,81 +2,8 @@ * @since 0.67.0 */ -import * as array_ from "effect/Array" -import * as Effect from "effect/Effect" -import * as util_ from "./internal/util.js" -import type * as ParseResult from "./ParseResult.js" -import * as TreeFormatter from "./TreeFormatter.js" - -/** - * @category model - * @since 0.67.0 - */ -export interface Issue { - readonly _tag: ParseResult.ParseIssue["_tag"] - readonly path: ReadonlyArray - readonly message: string -} - /** - * @category formatting - * @since 0.67.0 + * @category re-exports + * @since 0.76.0 */ -export const formatIssue = (issue: ParseResult.ParseIssue): Effect.Effect> => go(issue) - -/** - * @category formatting - * @since 0.67.0 - */ -export const formatIssueSync = (issue: ParseResult.ParseIssue): Array => Effect.runSync(formatIssue(issue)) - -/** - * @category formatting - * @since 0.67.0 - */ -export const formatError = (error: ParseResult.ParseError): Effect.Effect> => formatIssue(error.issue) - -/** - * @category formatting - * @since 0.67.0 - */ -export const formatErrorSync = (error: ParseResult.ParseError): Array => formatIssueSync(error.issue) - -const succeed = (issue: Issue) => Effect.succeed([issue]) - -const getArray = ( - issue: ParseResult.ParseIssue, - path: ReadonlyArray, - onFailure: () => Effect.Effect> -) => - Effect.matchEffect(TreeFormatter.getMessage(issue), { - onFailure, - onSuccess: (message) => succeed({ _tag: issue._tag, path, message }) - }) - -const go = ( - e: ParseResult.ParseIssue | ParseResult.Pointer, - path: ReadonlyArray = [] -): Effect.Effect> => { - const _tag = e._tag - switch (_tag) { - case "Type": - return Effect.map(TreeFormatter.formatTypeMessage(e), (message) => [{ _tag, path, message }]) - case "Forbidden": - return succeed({ _tag, path, message: TreeFormatter.formatForbiddenMessage(e) }) - case "Unexpected": - return succeed({ _tag, path, message: TreeFormatter.formatUnexpectedMessage(e) }) - case "Missing": - return Effect.map(TreeFormatter.formatMissingMessage(e), (message) => [{ _tag, path, message }]) - case "Pointer": - return go(e.issue, path.concat(e.path)) - case "Composite": - return getArray(e, path, () => - util_.isNonEmpty(e.issues) - ? Effect.map(Effect.forEach(e.issues, (issue) => go(issue, path)), array_.flatten) - : go(e.issues, path)) - case "Refinement": - case "Transformation": - return getArray(e, path, () => go(e.issue, path)) - } -} +export * from "effect/SchemaArrayFormatter" diff --git a/packages/schema/src/Equivalence.ts b/packages/schema/src/Equivalence.ts index 8e74acb24b..edffa47537 100644 --- a/packages/schema/src/Equivalence.ts +++ b/packages/schema/src/Equivalence.ts @@ -2,219 +2,8 @@ * @since 0.67.0 */ -import * as Arr from "effect/Array" -import * as Equal from "effect/Equal" -import * as Equivalence from "effect/Equivalence" -import * as Option from "effect/Option" -import * as Predicate from "effect/Predicate" -import * as AST from "./AST.js" -import * as errors_ from "./internal/errors.js" -import * as util_ from "./internal/util.js" -import * as ParseResult from "./ParseResult.js" -import type * as Schema from "./Schema.js" - -/** - * @category hooks - * @since 0.67.0 - */ -export const EquivalenceHookId: unique symbol = Symbol.for("@effect/schema/EquivalenceHookId") - -/** - * @category hooks - * @since 0.67.0 - */ -export type EquivalenceHookId = typeof EquivalenceHookId - -/** - * @category annotations - * @since 0.67.0 - */ -export const equivalence = - (handler: (...args: ReadonlyArray>) => Equivalence.Equivalence) => - (self: Schema.Schema): Schema.Schema => self.annotations({ [EquivalenceHookId]: handler }) - /** - * @category Equivalence - * @since 0.67.0 + * @category re-exports + * @since 0.76.0 */ -export const make = (schema: Schema.Schema): Equivalence.Equivalence => go(schema.ast, []) - -const getHook = AST.getAnnotation< - (...args: ReadonlyArray>) => Equivalence.Equivalence ->( - EquivalenceHookId -) - -const go = (ast: AST.AST, path: ReadonlyArray): Equivalence.Equivalence => { - const hook = getHook(ast) - if (Option.isSome(hook)) { - switch (ast._tag) { - case "Declaration": - return hook.value(...ast.typeParameters.map((tp) => go(tp, path))) - case "Refinement": - return hook.value(go(ast.from, path)) - default: - return hook.value() - } - } - switch (ast._tag) { - case "NeverKeyword": - throw new Error(errors_.getEquivalenceUnsupportedErrorMessage(ast, path)) - case "Transformation": - return go(ast.to, path) - case "Declaration": - case "Literal": - case "StringKeyword": - case "TemplateLiteral": - case "UniqueSymbol": - case "SymbolKeyword": - case "UnknownKeyword": - case "AnyKeyword": - case "NumberKeyword": - case "BooleanKeyword": - case "BigIntKeyword": - case "UndefinedKeyword": - case "VoidKeyword": - case "Enums": - case "ObjectKeyword": - return Equal.equals - case "Refinement": - return go(ast.from, path) - case "Suspend": { - const get = util_.memoizeThunk(() => go(ast.f(), path)) - return (a, b) => get()(a, b) - } - case "TupleType": { - const elements = ast.elements.map((element, i) => go(element.type, path.concat(i))) - const rest = ast.rest.map((annotatedAST) => go(annotatedAST.type, path)) - return Equivalence.make((a, b) => { - const len = a.length - if (len !== b.length) { - return false - } - // --------------------------------------------- - // handle elements - // --------------------------------------------- - let i = 0 - for (; i < Math.min(len, ast.elements.length); i++) { - if (!elements[i](a[i], b[i])) { - return false - } - } - // --------------------------------------------- - // handle rest element - // --------------------------------------------- - if (Arr.isNonEmptyReadonlyArray(rest)) { - const [head, ...tail] = rest - for (; i < len - tail.length; i++) { - if (!head(a[i], b[i])) { - return false - } - } - // --------------------------------------------- - // handle post rest elements - // --------------------------------------------- - for (let j = 0; j < tail.length; j++) { - i += j - if (!tail[j](a[i], b[i])) { - return false - } - } - } - return true - }) - } - case "TypeLiteral": { - if (ast.propertySignatures.length === 0 && ast.indexSignatures.length === 0) { - return Equal.equals - } - const propertySignatures = ast.propertySignatures.map((ps) => go(ps.type, path.concat(ps.name))) - const indexSignatures = ast.indexSignatures.map((is) => go(is.type, path)) - return Equivalence.make((a, b) => { - const aStringKeys = Object.keys(a) - const aSymbolKeys = Object.getOwnPropertySymbols(a) - // --------------------------------------------- - // handle property signatures - // --------------------------------------------- - for (let i = 0; i < propertySignatures.length; i++) { - const ps = ast.propertySignatures[i] - const name = ps.name - const aHas = Object.prototype.hasOwnProperty.call(a, name) - const bHas = Object.prototype.hasOwnProperty.call(b, name) - if (ps.isOptional) { - if (aHas !== bHas) { - return false - } - } - if (aHas && bHas && !propertySignatures[i](a[name], b[name])) { - return false - } - } - // --------------------------------------------- - // handle index signatures - // --------------------------------------------- - let bSymbolKeys: Array | undefined - let bStringKeys: Array | undefined - for (let i = 0; i < indexSignatures.length; i++) { - const is = ast.indexSignatures[i] - const base = AST.getParameterBase(is.parameter) - const isSymbol = AST.isSymbolKeyword(base) - if (isSymbol) { - bSymbolKeys = bSymbolKeys || Object.getOwnPropertySymbols(b) - if (aSymbolKeys.length !== bSymbolKeys.length) { - return false - } - } else { - bStringKeys = bStringKeys || Object.keys(b) - if (aStringKeys.length !== bStringKeys.length) { - return false - } - } - const aKeys = isSymbol ? aSymbolKeys : aStringKeys - for (let j = 0; j < aKeys.length; j++) { - const key = aKeys[j] - if ( - !Object.prototype.hasOwnProperty.call(b, key) || !indexSignatures[i](a[key], b[key]) - ) { - return false - } - } - } - return true - }) - } - case "Union": { - const searchTree = ParseResult.getSearchTree(ast.types, true) - const ownKeys = util_.ownKeys(searchTree.keys) - const len = ownKeys.length - return Equivalence.make((a, b) => { - let candidates: Array = [] - if (len > 0 && Predicate.isRecord(a)) { - for (let i = 0; i < len; i++) { - const name = ownKeys[i] - const buckets = searchTree.keys[name].buckets - if (Object.prototype.hasOwnProperty.call(a, name)) { - const literal = String(a[name]) - if (Object.prototype.hasOwnProperty.call(buckets, literal)) { - candidates = candidates.concat(buckets[literal]) - } - } - } - } - if (searchTree.otherwise.length > 0) { - candidates = candidates.concat(searchTree.otherwise) - } - const tuples = candidates.map((ast) => [go(ast, path), ParseResult.is({ ast } as any)] as const) - for (let i = 0; i < tuples.length; i++) { - const [equivalence, is] = tuples[i] - if (is(a) && is(b)) { - if (equivalence(a, b)) { - return true - } - } - } - return false - }) - } - } -} +export * from "effect/SchemaEquivalence" diff --git a/packages/schema/src/FastCheck.ts b/packages/schema/src/FastCheck.ts index f11dad7043..42cd555e43 100644 --- a/packages/schema/src/FastCheck.ts +++ b/packages/schema/src/FastCheck.ts @@ -4,6 +4,6 @@ /** * @category re-exports - * @since 0.67.0 + * @since 0.76.0 */ -export * from "fast-check" +export * from "effect/FastCheck" diff --git a/packages/schema/src/JSONSchema.ts b/packages/schema/src/JSONSchema.ts index 4cfa214f09..cfcec21286 100644 --- a/packages/schema/src/JSONSchema.ts +++ b/packages/schema/src/JSONSchema.ts @@ -2,601 +2,8 @@ * @since 0.67.0 */ -import * as Option from "effect/Option" -import * as Predicate from "effect/Predicate" -import * as Record from "effect/Record" -import * as AST from "./AST.js" -import * as errors_ from "./internal/errors.js" -import * as filters_ from "./internal/filters.js" -import type * as Schema from "./Schema.js" - -/** - * @category model - * @since 0.67.0 - */ -export interface JsonSchemaAnnotations { - title?: string - description?: string - default?: unknown - examples?: Array -} - -/** - * @category model - * @since 0.67.0 - */ -export interface JsonSchema7Any extends JsonSchemaAnnotations { - $id: "/schemas/any" -} - -/** - * @category model - * @since 0.67.0 - */ -export interface JsonSchema7Unknown extends JsonSchemaAnnotations { - $id: "/schemas/unknown" -} - -/** - * @category model - * @since 0.69.0 - */ -export interface JsonSchema7Void extends JsonSchemaAnnotations { - $id: "/schemas/void" -} - -/** - * @category model - * @since 0.71.0 - */ -export interface JsonSchema7object extends JsonSchemaAnnotations { - $id: "/schemas/object" - anyOf: [ - { type: "object" }, - { type: "array" } - ] -} - -/** - * @category model - * @since 0.71.0 - */ -export interface JsonSchema7empty extends JsonSchemaAnnotations { - $id: "/schemas/{}" - anyOf: [ - { type: "object" }, - { type: "array" } - ] -} - /** - * @category model - * @since 0.67.0 - */ -export interface JsonSchema7Ref extends JsonSchemaAnnotations { - $ref: string -} - -/** - * @category model - * @since 0.67.0 - */ -export interface JsonSchema7String extends JsonSchemaAnnotations { - type: "string" - minLength?: number - maxLength?: number - pattern?: string -} - -/** - * @category model - * @since 0.67.0 - */ -export interface JsonSchema7Numeric extends JsonSchemaAnnotations { - minimum?: number - exclusiveMinimum?: number - maximum?: number - exclusiveMaximum?: number -} - -/** - * @category model - * @since 0.67.0 - */ -export interface JsonSchema7Number extends JsonSchema7Numeric { - type: "number" -} - -/** - * @category model - * @since 0.67.0 - */ -export interface JsonSchema7Integer extends JsonSchema7Numeric { - type: "integer" -} - -/** - * @category model - * @since 0.67.0 - */ -export interface JsonSchema7Boolean extends JsonSchemaAnnotations { - type: "boolean" -} - -/** - * @category model - * @since 0.67.0 + * @category re-exports + * @since 0.76.0 */ -export interface JsonSchema7Array extends JsonSchemaAnnotations { - type: "array" - items?: JsonSchema7 | Array - minItems?: number - maxItems?: number - additionalItems?: JsonSchema7 | boolean -} - -/** - * @category model - * @since 0.67.0 - */ -export interface JsonSchema7Enum extends JsonSchemaAnnotations { - enum: Array -} - -/** - * @category model - * @since 0.71.0 - */ -export interface JsonSchema7Enums extends JsonSchemaAnnotations { - $comment: "/schemas/enums" - anyOf: Array<{ - title: string - enum: [string | number] - }> -} - -/** - * @category model - * @since 0.67.0 - */ -export interface JsonSchema7AnyOf extends JsonSchemaAnnotations { - anyOf: Array -} - -/** - * @category model - * @since 0.67.0 - */ -export interface JsonSchema7Object extends JsonSchemaAnnotations { - type: "object" - required: Array - properties: Record - additionalProperties?: boolean | JsonSchema7 - patternProperties?: Record - propertyNames?: JsonSchema7 -} - -/** - * @category model - * @since 0.71.0 - */ -export type JsonSchema7 = - | JsonSchema7Any - | JsonSchema7Unknown - | JsonSchema7Void - | JsonSchema7object - | JsonSchema7empty - | JsonSchema7Ref - | JsonSchema7String - | JsonSchema7Number - | JsonSchema7Integer - | JsonSchema7Boolean - | JsonSchema7Array - | JsonSchema7Enum - | JsonSchema7Enums - | JsonSchema7AnyOf - | JsonSchema7Object - -/** - * @category model - * @since 0.67.0 - */ -export type JsonSchema7Root = JsonSchema7 & { - $schema?: string - $defs?: Record -} - -/** - * @category encoding - * @since 0.67.0 - */ -export const make = (schema: Schema.Schema): JsonSchema7Root => { - const $defs: Record = {} - const jsonSchema = go(schema.ast, $defs, true, []) - const out: JsonSchema7Root = { - $schema, - ...jsonSchema - } - // clean up self-referencing entries - for (const id in $defs) { - if ($defs[id]["$ref"] === get$ref(id)) { - delete $defs[id] - } - } - if (!Record.isEmptyRecord($defs)) { - out.$defs = $defs - } - return out -} - -const anyJsonSchema: JsonSchema7 = { $id: "/schemas/any" } - -const unknownJsonSchema: JsonSchema7 = { $id: "/schemas/unknown" } - -const voidJsonSchema: JsonSchema7 = { $id: "/schemas/void" } - -const objectJsonSchema: JsonSchema7 = { - "$id": "/schemas/object", - "anyOf": [ - { "type": "object" }, - { "type": "array" } - ] -} - -const empty = (): JsonSchema7 => ({ - "$id": "/schemas/{}", - "anyOf": [ - { "type": "object" }, - { "type": "array" } - ] -}) - -const $schema = "http://json-schema.org/draft-07/schema#" - -const getJsonSchemaAnnotations = (annotated: AST.Annotated): JsonSchemaAnnotations => - Record.getSomes({ - description: AST.getDescriptionAnnotation(annotated), - title: AST.getTitleAnnotation(annotated), - examples: AST.getExamplesAnnotation(annotated), - default: AST.getDefaultAnnotation(annotated) - }) - -const removeDefaultJsonSchemaAnnotations = ( - jsonSchemaAnnotations: JsonSchemaAnnotations, - ast: AST.AST -): JsonSchemaAnnotations => { - if (jsonSchemaAnnotations["title"] === ast.annotations[AST.TitleAnnotationId]) { - delete jsonSchemaAnnotations["title"] - } - if (jsonSchemaAnnotations["description"] === ast.annotations[AST.DescriptionAnnotationId]) { - delete jsonSchemaAnnotations["description"] - } - return jsonSchemaAnnotations -} - -const getASTJsonSchemaAnnotations = (ast: AST.AST): JsonSchemaAnnotations => { - const jsonSchemaAnnotations = getJsonSchemaAnnotations(ast) - switch (ast._tag) { - case "StringKeyword": - return removeDefaultJsonSchemaAnnotations(jsonSchemaAnnotations, AST.stringKeyword) - case "NumberKeyword": - return removeDefaultJsonSchemaAnnotations(jsonSchemaAnnotations, AST.numberKeyword) - case "BooleanKeyword": - return removeDefaultJsonSchemaAnnotations(jsonSchemaAnnotations, AST.booleanKeyword) - default: - return jsonSchemaAnnotations - } -} - -const pruneUndefinedKeyword = (ps: AST.PropertySignature): AST.AST | undefined => { - const type = ps.type - if (AST.isUnion(type) && Option.isNone(AST.getJSONSchemaAnnotation(type))) { - const types = type.types.filter((type) => !AST.isUndefinedKeyword(type)) - if (types.length < type.types.length) { - return AST.Union.make(types, type.annotations) - } - } -} - -/** @internal */ -export const DEFINITION_PREFIX = "#/$defs/" - -const get$ref = (id: string): string => `${DEFINITION_PREFIX}${id}` - -const getRefinementInnerTransformation = (ast: AST.Refinement): AST.AST | undefined => { - switch (ast.from._tag) { - case "Transformation": - return ast.from - case "Refinement": - return getRefinementInnerTransformation(ast.from) - case "Suspend": { - const from = ast.from.f() - if (AST.isRefinement(from)) { - return getRefinementInnerTransformation(from) - } - } - } -} - -const isParseJsonTransformation = (ast: AST.AST): boolean => - ast.annotations[AST.TypeAnnotationId] === filters_.ParseJsonTypeId - -function merge(a: JsonSchemaAnnotations, b: JsonSchema7): JsonSchema7 -function merge(a: JsonSchema7, b: JsonSchemaAnnotations): JsonSchema7 -function merge(a: JsonSchema7, b: JsonSchema7): JsonSchema7 -function merge(a: object, b: object): object { - return { ...a, ...b } -} - -const isOverrideAnnotation = (jsonSchema: JsonSchema7): boolean => { - return ("type" in jsonSchema) || ("oneOf" in jsonSchema) || ("anyOf" in jsonSchema) || ("const" in jsonSchema) || - ("enum" in jsonSchema) || ("$ref" in jsonSchema) -} - -const go = ( - ast: AST.AST, - $defs: Record, - handleIdentifier: boolean, - path: ReadonlyArray -): JsonSchema7 => { - const hook = AST.getJSONSchemaAnnotation(ast) - if (Option.isSome(hook)) { - const handler = hook.value as JsonSchema7 - if (AST.isRefinement(ast)) { - const t = getRefinementInnerTransformation(ast) - if (t === undefined) { - try { - return { - ...go(ast.from, $defs, true, path), - ...getJsonSchemaAnnotations(ast), - ...handler - } - } catch (e) { - return { - ...getJsonSchemaAnnotations(ast), - ...handler - } - } - } else if (!isOverrideAnnotation(handler)) { - return go(t, $defs, true, path) - } - } - return handler - } - const surrogate = AST.getSurrogateAnnotation(ast) - if (Option.isSome(surrogate)) { - return go(surrogate.value, $defs, handleIdentifier, path) - } - if (handleIdentifier && !AST.isTransformation(ast) && !AST.isRefinement(ast)) { - const identifier = AST.getJSONIdentifier(ast) - if (Option.isSome(identifier)) { - const id = identifier.value - const out = { $ref: get$ref(id) } - if (!Record.has($defs, id)) { - $defs[id] = out - $defs[id] = go(ast, $defs, false, path) - } - return out - } - } - switch (ast._tag) { - case "Declaration": - throw new Error(errors_.getJSONSchemaMissingAnnotationErrorMessage(path, ast)) - case "Literal": { - const literal = ast.literal - if (literal === null) { - return merge({ enum: [null] }, getJsonSchemaAnnotations(ast)) - } else if (Predicate.isString(literal) || Predicate.isNumber(literal) || Predicate.isBoolean(literal)) { - return merge({ enum: [literal] }, getJsonSchemaAnnotations(ast)) - } - throw new Error(errors_.getJSONSchemaMissingAnnotationErrorMessage(path, ast)) - } - case "UniqueSymbol": - throw new Error(errors_.getJSONSchemaMissingAnnotationErrorMessage(path, ast)) - case "UndefinedKeyword": - throw new Error(errors_.getJSONSchemaMissingAnnotationErrorMessage(path, ast)) - case "VoidKeyword": - return merge(voidJsonSchema, getJsonSchemaAnnotations(ast)) - case "NeverKeyword": - throw new Error(errors_.getJSONSchemaMissingAnnotationErrorMessage(path, ast)) - case "UnknownKeyword": - return merge(unknownJsonSchema, getJsonSchemaAnnotations(ast)) - case "AnyKeyword": - return merge(anyJsonSchema, getJsonSchemaAnnotations(ast)) - case "ObjectKeyword": - return merge(objectJsonSchema, getJsonSchemaAnnotations(ast)) - case "StringKeyword": - return { type: "string", ...getASTJsonSchemaAnnotations(ast) } - case "NumberKeyword": - return { type: "number", ...getASTJsonSchemaAnnotations(ast) } - case "BooleanKeyword": - return { type: "boolean", ...getASTJsonSchemaAnnotations(ast) } - case "BigIntKeyword": - throw new Error(errors_.getJSONSchemaMissingAnnotationErrorMessage(path, ast)) - case "SymbolKeyword": - throw new Error(errors_.getJSONSchemaMissingAnnotationErrorMessage(path, ast)) - case "TupleType": { - const elements = ast.elements.map((e, i) => - merge( - go(e.type, $defs, true, path.concat(i)), - getJsonSchemaAnnotations(e) - ) - ) - const rest = ast.rest.map((annotatedAST) => - merge( - go(annotatedAST.type, $defs, true, path), - getJsonSchemaAnnotations(annotatedAST) - ) - ) - const output: JsonSchema7Array = { type: "array" } - // --------------------------------------------- - // handle elements - // --------------------------------------------- - const len = ast.elements.length - if (len > 0) { - output.minItems = len - ast.elements.filter((element) => element.isOptional).length - output.items = elements - } - // --------------------------------------------- - // handle rest element - // --------------------------------------------- - const restLength = rest.length - if (restLength > 0) { - const head = rest[0] - const isHomogeneous = restLength === 1 && ast.elements.every((e) => e.type === ast.rest[0].type) - if (isHomogeneous) { - output.items = head - } else { - output.additionalItems = head - } - - // --------------------------------------------- - // handle post rest elements - // --------------------------------------------- - if (restLength > 1) { - throw new Error(errors_.getJSONSchemaUnsupportedPostRestElementsErrorMessage(path)) - } - } else { - if (len > 0) { - output.additionalItems = false - } else { - output.maxItems = 0 - } - } - - return merge(output, getJsonSchemaAnnotations(ast)) - } - case "TypeLiteral": { - if (ast.propertySignatures.length === 0 && ast.indexSignatures.length === 0) { - return merge(empty(), getJsonSchemaAnnotations(ast)) - } - let patternProperties: JsonSchema7 | undefined = undefined - let propertyNames: JsonSchema7 | undefined = undefined - for (const is of ast.indexSignatures) { - const parameter = is.parameter - switch (parameter._tag) { - case "StringKeyword": { - patternProperties = go(is.type, $defs, true, path) - break - } - case "TemplateLiteral": { - patternProperties = go(is.type, $defs, true, path) - propertyNames = { - type: "string", - pattern: AST.getTemplateLiteralRegExp(parameter).source - } - break - } - case "Refinement": { - patternProperties = go(is.type, $defs, true, path) - propertyNames = go(parameter, $defs, true, path) - break - } - case "SymbolKeyword": - throw new Error(errors_.getJSONSchemaUnsupportedParameterErrorMessage(path, parameter)) - } - } - const output: JsonSchema7Object = { - type: "object", - required: [], - properties: {}, - additionalProperties: false - } - // --------------------------------------------- - // handle property signatures - // --------------------------------------------- - for (let i = 0; i < ast.propertySignatures.length; i++) { - const ps = ast.propertySignatures[i] - const name = ps.name - if (Predicate.isString(name)) { - const pruned = pruneUndefinedKeyword(ps) - output.properties[name] = merge( - go(pruned ? pruned : ps.type, $defs, true, path.concat(ps.name)), - getJsonSchemaAnnotations(ps) - ) - // --------------------------------------------- - // handle optional property signatures - // --------------------------------------------- - if (!ps.isOptional && pruned === undefined) { - output.required.push(name) - } - } else { - throw new Error(errors_.getJSONSchemaUnsupportedKeyErrorMessage(name, path)) - } - } - // --------------------------------------------- - // handle index signatures - // --------------------------------------------- - if (patternProperties !== undefined) { - delete output.additionalProperties - output.patternProperties = { "": patternProperties } - } - if (propertyNames !== undefined) { - output.propertyNames = propertyNames - } - - return merge(output, getJsonSchemaAnnotations(ast)) - } - case "Union": { - const enums: Array = [] - const anyOf: Array = [] - for (const type of ast.types) { - const schema = go(type, $defs, true, path) - if ("enum" in schema) { - if (Object.keys(schema).length > 1) { - anyOf.push(schema) - } else { - for (const e of schema.enum) { - enums.push(e) - } - } - } else { - anyOf.push(schema) - } - } - if (anyOf.length === 0) { - return merge({ enum: enums }, getJsonSchemaAnnotations(ast)) - } else { - if (enums.length >= 1) { - anyOf.push({ enum: enums }) - } - return merge({ anyOf }, getJsonSchemaAnnotations(ast)) - } - } - case "Enums": { - return merge({ - $comment: "/schemas/enums", - anyOf: ast.enums.map((e) => ({ title: e[0], enum: [e[1]] })) - }, getJsonSchemaAnnotations(ast)) - } - case "Refinement": { - if (AST.encodedBoundAST(ast) === ast) { - throw new Error(errors_.getJSONSchemaMissingAnnotationErrorMessage(path, ast)) - } - return go(ast.from, $defs, true, path) - } - case "TemplateLiteral": { - const regex = AST.getTemplateLiteralRegExp(ast) - return merge({ - type: "string", - description: "a template literal", - pattern: regex.source - }, getJsonSchemaAnnotations(ast)) - } - case "Suspend": { - const identifier = Option.orElse(AST.getJSONIdentifier(ast), () => AST.getJSONIdentifier(ast.f())) - if (Option.isNone(identifier)) { - throw new Error(errors_.getJSONSchemaMissingIdentifierAnnotationErrorMessage(path, ast)) - } - return go(ast.f(), $defs, true, path) - } - case "Transformation": { - // Properly handle S.parseJson transformations by focusing on - // the 'to' side of the AST. This approach prevents the generation of useless schemas - // derived from the 'from' side (type: string), ensuring the output matches the intended - // complex schema type. - const next = isParseJsonTransformation(ast.from) ? ast.to : ast.from - return go(next, $defs, true, path) - } - } -} +export * from "effect/JSONSchema" diff --git a/packages/schema/src/ParseResult.ts b/packages/schema/src/ParseResult.ts index 48397c9495..ff365ff5dd 100644 --- a/packages/schema/src/ParseResult.ts +++ b/packages/schema/src/ParseResult.ts @@ -2,1719 +2,8 @@ * @since 0.67.0 */ -import * as array_ from "effect/Array" -import { TaggedError } from "effect/Data" -import * as Effect from "effect/Effect" -import * as Either from "effect/Either" -import type { LazyArg } from "effect/Function" -import { dual } from "effect/Function" -import { globalValue } from "effect/GlobalValue" -import * as Inspectable from "effect/Inspectable" -import * as Option from "effect/Option" -import * as Predicate from "effect/Predicate" -import type { Concurrency } from "effect/Types" -import * as AST from "./AST.js" -import * as util_ from "./internal/util.js" -import type * as Schema from "./Schema.js" -import * as TreeFormatter from "./TreeFormatter.js" - -/** - * `ParseIssue` is a type that represents the different types of errors that can occur when decoding/encoding a value. - * - * @category model - * @since 0.67.0 - */ -export type ParseIssue = - // leaf - | Type - | Missing - | Unexpected - | Forbidden - // composite - | Pointer - | Refinement - | Transformation - | Composite - -/** - * @category model - * @since 0.68.0 - */ -export type SingleOrNonEmpty = A | array_.NonEmptyReadonlyArray - -/** - * @category model - * @since 0.68.0 - */ -export type Path = SingleOrNonEmpty - -/** - * @category model - * @since 0.68.0 - */ -export class Pointer { - /** - * @since 0.68.0 - */ - readonly _tag = "Pointer" - constructor( - readonly path: Path, - readonly actual: unknown, - readonly issue: ParseIssue - ) {} -} - -/** - * Error that occurs when an unexpected key or index is present. - * - * @category model - * @since 0.67.0 - */ -export class Unexpected { - /** - * @since 0.67.0 - */ - readonly _tag = "Unexpected" - constructor( - readonly actual: unknown, - /** - * @since 0.68.0 - */ - readonly message?: string - ) {} -} - -/** - * Error that occurs when a required key or index is missing. - * - * @category model - * @since 0.67.0 - */ -export class Missing { - /** - * @since 0.67.0 - */ - readonly _tag = "Missing" - /** - * @since 0.68.0 - */ - readonly actual = undefined - constructor( - /** - * @since 0.68.0 - */ - readonly ast: AST.Type, - /** - * @since 0.68.0 - */ - readonly message?: string - ) {} -} - -/** - * Error that contains multiple issues. - * - * @category model - * @since 0.68.0 - */ -export class Composite { - /** - * @since 0.68.0 - */ - readonly _tag = "Composite" - constructor( - readonly ast: AST.AST, - readonly actual: unknown, - readonly issues: SingleOrNonEmpty, - readonly output?: unknown - ) {} -} - -/** - * Returns `true` if the value is a `Composite`. - * - * @category guards - * @since 0.68.0 - */ -export const isComposite = (u: unknown): u is Composite => Predicate.hasProperty(u, "_tag") - -/** - * Error that occurs when a refinement has an error. - * - * @category model - * @since 0.67.0 - */ -export class Refinement { - /** - * @since 0.67.0 - */ - readonly _tag = "Refinement" - constructor( - readonly ast: AST.Refinement, - readonly actual: unknown, - readonly kind: "From" | "Predicate", - readonly issue: ParseIssue - ) {} -} - /** - * Error that occurs when a transformation has an error. - * - * @category model - * @since 0.67.0 - */ -export class Transformation { - /** - * @since 0.67.0 - */ - readonly _tag = "Transformation" - constructor( - readonly ast: AST.Transformation, - readonly actual: unknown, - readonly kind: "Encoded" | "Transformation" | "Type", - readonly issue: ParseIssue - ) {} -} - -/** - * The `Type` variant of the `ParseIssue` type represents an error that occurs when the `actual` value is not of the expected type. - * The `ast` field specifies the expected type, and the `actual` field contains the value that caused the error. - * - * @category model - * @since 0.67.0 - */ -export class Type { - /** - * @since 0.67.0 - */ - readonly _tag = "Type" - constructor( - readonly ast: AST.AST, - readonly actual: unknown, - readonly message?: string - ) {} -} - -/** - * The `Forbidden` variant of the `ParseIssue` type represents a forbidden operation, such as when encountering an Effect that is not allowed to execute (e.g., using `runSync`). - * - * @category model - * @since 0.67.0 - */ -export class Forbidden { - /** - * @since 0.67.0 - */ - readonly _tag = "Forbidden" - constructor( - readonly ast: AST.AST, - readonly actual: unknown, - readonly message?: string - ) {} -} - -/** - * @category type id - * @since 0.68.0 - */ -export const ParseErrorTypeId: unique symbol = Symbol.for("@effect/schema/ParseErrorTypeId") - -/** - * @category type id - * @since 0.68.0 - */ -export type ParseErrorTypeId = typeof ParseErrorTypeId - -/** - * @since 0.68.0 - */ -export const isParseError = (u: unknown): u is ParseError => Predicate.hasProperty(u, ParseErrorTypeId) - -/** - * @since 0.67.0 - */ -export class ParseError extends TaggedError("ParseError")<{ readonly issue: ParseIssue }> { - /** - * @since 0.68.0 - */ - readonly [ParseErrorTypeId] = ParseErrorTypeId - - get message() { - return this.toString() - } - /** - * @since 0.67.0 - */ - toString() { - return TreeFormatter.formatIssueSync(this.issue) - } - /** - * @since 0.67.0 - */ - toJSON() { - return { - _id: "ParseError", - message: this.toString() - } - } - /** - * @since 0.67.0 - */ - [Inspectable.NodeInspectSymbol]() { - return this.toJSON() - } -} - -/** - * @category constructors - * @since 0.67.0 - */ -export const parseError = (issue: ParseIssue): ParseError => new ParseError({ issue }) - -/** - * @category constructors - * @since 0.67.0 + * @category re-exports + * @since 0.76.0 */ -export const succeed: (a: A) => Either.Either = Either.right - -/** - * @category constructors - * @since 0.67.0 - */ -export const fail: (issue: ParseIssue) => Either.Either = Either.left - -const _try: (options: { - try: LazyArg - catch: (e: unknown) => ParseIssue -}) => Either.Either = Either.try - -export { - /** - * @category constructors - * @since 0.67.0 - */ - _try as try -} - -/** - * @category constructors - * @since 0.67.0 - */ -export const fromOption: { - (onNone: () => ParseIssue): (self: Option.Option) => Either.Either - (self: Option.Option, onNone: () => ParseIssue): Either.Either -} = Either.fromOption - -/** - * @category optimisation - * @since 0.67.0 - */ -export const flatMap: { - ( - f: (a: A) => Effect.Effect - ): (self: Effect.Effect) => Effect.Effect - ( - self: Effect.Effect, - f: (a: A) => Effect.Effect - ): Effect.Effect -} = dual(2, ( - self: Effect.Effect, - f: (a: A) => Effect.Effect -): Effect.Effect => { - const s: any = self - if (s["_tag"] === "Left") { - return s - } - if (s["_tag"] === "Right") { - return f(s.right) - } - return Effect.flatMap(self, f) -}) - -/** - * @category optimisation - * @since 0.67.0 - */ -export const map: { - (f: (a: A) => B): (self: Effect.Effect) => Effect.Effect - (self: Effect.Effect, f: (a: A) => B): Effect.Effect -} = dual(2, (self: Effect.Effect, f: (a: A) => B): Effect.Effect => { - const s: any = self - if (s["_tag"] === "Left") { - return s - } - if (s["_tag"] === "Right") { - return Either.right(f(s.right)) - } - return Effect.map(self, f) -}) - -/** - * @category optimisation - * @since 0.67.0 - */ -export const mapError: { - (f: (e: E) => E2): (self: Effect.Effect) => Effect.Effect - (self: Effect.Effect, f: (e: E) => E2): Effect.Effect -} = dual(2, (self: Effect.Effect, f: (e: E) => E2): Effect.Effect => { - const s: any = self - if (s["_tag"] === "Left") { - return Either.left(f(s.left)) - } - if (s["_tag"] === "Right") { - return s - } - return Effect.mapError(self, f) -}) - -/** - * @category optimisation - * @since 0.67.0 - */ -export const eitherOrUndefined = ( - self: Effect.Effect -): Either.Either | undefined => { - const s: any = self - if (s["_tag"] === "Left" || s["_tag"] === "Right") { - return s - } -} - -/** - * @category optimisation - * @since 0.67.0 - */ -export const mapBoth: { - ( - options: { readonly onFailure: (e: E) => E2; readonly onSuccess: (a: A) => A2 } - ): (self: Effect.Effect) => Effect.Effect - ( - self: Effect.Effect, - options: { readonly onFailure: (e: E) => E2; readonly onSuccess: (a: A) => A2 } - ): Effect.Effect -} = dual(2, ( - self: Effect.Effect, - options: { readonly onFailure: (e: E) => E2; readonly onSuccess: (a: A) => A2 } -): Effect.Effect => { - const s: any = self - if (s["_tag"] === "Left") { - return Either.left(options.onFailure(s.left)) - } - if (s["_tag"] === "Right") { - return Either.right(options.onSuccess(s.right)) - } - return Effect.mapBoth(self, options) -}) - -/** - * @category optimisation - * @since 0.67.0 - */ -export const orElse: { - ( - f: (e: E) => Effect.Effect - ): (self: Effect.Effect) => Effect.Effect - ( - self: Effect.Effect, - f: (e: E) => Effect.Effect - ): Effect.Effect -} = dual(2, ( - self: Effect.Effect, - f: (e: E) => Effect.Effect -): Effect.Effect => { - const s: any = self - if (s["_tag"] === "Left") { - return f(s.left) - } - if (s["_tag"] === "Right") { - return s - } - return Effect.catchAll(self, f) -}) - -/** - * @since 0.67.0 - */ -export type DecodeUnknown = (u: unknown, options?: AST.ParseOptions) => Effect.Effect - -/** - * @since 0.67.0 - */ -export type DeclarationDecodeUnknown = ( - u: unknown, - options: AST.ParseOptions, - ast: AST.Declaration -) => Effect.Effect - -/** @internal */ -export const mergeInternalOptions = ( - options: InternalOptions | undefined, - overrideOptions: InternalOptions | number | undefined -): InternalOptions | undefined => { - if (overrideOptions === undefined || Predicate.isNumber(overrideOptions)) { - return options - } - if (options === undefined) { - return overrideOptions - } - return { ...options, ...overrideOptions } -} - -const getEither = (ast: AST.AST, isDecoding: boolean, options?: AST.ParseOptions) => { - const parser = goMemo(ast, isDecoding) - return (u: unknown, overrideOptions?: AST.ParseOptions): Either.Either => - parser(u, mergeInternalOptions(options, overrideOptions)) as any -} - -const getSync = (ast: AST.AST, isDecoding: boolean, options?: AST.ParseOptions) => { - const parser = getEither(ast, isDecoding, options) - return (input: unknown, overrideOptions?: AST.ParseOptions) => - Either.getOrThrowWith(parser(input, overrideOptions), parseError) -} - -const getOption = (ast: AST.AST, isDecoding: boolean, options?: AST.ParseOptions) => { - const parser = getEither(ast, isDecoding, options) - return (input: unknown, overrideOptions?: AST.ParseOptions): Option.Option => - Option.getRight(parser(input, overrideOptions)) -} - -const getEffect = (ast: AST.AST, isDecoding: boolean, options?: AST.ParseOptions) => { - const parser = goMemo(ast, isDecoding) - return (input: unknown, overrideOptions?: AST.ParseOptions): Effect.Effect => - parser(input, { ...mergeInternalOptions(options, overrideOptions), isEffectAllowed: true }) -} - -/** - * @throws `ParseError` - * @category decoding - * @since 0.67.0 - */ -export const decodeUnknownSync = ( - schema: Schema.Schema, - options?: AST.ParseOptions -): (u: unknown, overrideOptions?: AST.ParseOptions) => A => getSync(schema.ast, true, options) - -/** - * @category decoding - * @since 0.67.0 - */ -export const decodeUnknownOption = ( - schema: Schema.Schema, - options?: AST.ParseOptions -): (u: unknown, overrideOptions?: AST.ParseOptions) => Option.Option => getOption(schema.ast, true, options) - -/** - * @category decoding - * @since 0.67.0 - */ -export const decodeUnknownEither = ( - schema: Schema.Schema, - options?: AST.ParseOptions -): (u: unknown, overrideOptions?: AST.ParseOptions) => Either.Either => - getEither(schema.ast, true, options) - -/** - * @category decoding - * @since 0.67.0 - */ -export const decodeUnknownPromise = ( - schema: Schema.Schema, - options?: AST.ParseOptions -) => { - const parser = decodeUnknown(schema, options) - return (u: unknown, overrideOptions?: AST.ParseOptions): Promise => Effect.runPromise(parser(u, overrideOptions)) -} - -/** - * @category decoding - * @since 0.67.0 - */ -export const decodeUnknown = ( - schema: Schema.Schema, - options?: AST.ParseOptions -): (u: unknown, overrideOptions?: AST.ParseOptions) => Effect.Effect => - getEffect(schema.ast, true, options) - -/** - * @throws `ParseError` - * @category encoding - * @since 0.67.0 - */ -export const encodeUnknownSync = ( - schema: Schema.Schema, - options?: AST.ParseOptions -): (u: unknown, overrideOptions?: AST.ParseOptions) => I => getSync(schema.ast, false, options) - -/** - * @category encoding - * @since 0.67.0 - */ -export const encodeUnknownOption = ( - schema: Schema.Schema, - options?: AST.ParseOptions -): (u: unknown, overrideOptions?: AST.ParseOptions) => Option.Option => getOption(schema.ast, false, options) - -/** - * @category encoding - * @since 0.67.0 - */ -export const encodeUnknownEither = ( - schema: Schema.Schema, - options?: AST.ParseOptions -): (u: unknown, overrideOptions?: AST.ParseOptions) => Either.Either => - getEither(schema.ast, false, options) - -/** - * @category encoding - * @since 0.67.0 - */ -export const encodeUnknownPromise = ( - schema: Schema.Schema, - options?: AST.ParseOptions -) => { - const parser = encodeUnknown(schema, options) - return (u: unknown, overrideOptions?: AST.ParseOptions): Promise => Effect.runPromise(parser(u, overrideOptions)) -} - -/** - * @category encoding - * @since 0.67.0 - */ -export const encodeUnknown = ( - schema: Schema.Schema, - options?: AST.ParseOptions -): (u: unknown, overrideOptions?: AST.ParseOptions) => Effect.Effect => - getEffect(schema.ast, false, options) - -/** - * @category decoding - * @since 0.67.0 - */ -export const decodeSync: ( - schema: Schema.Schema, - options?: AST.ParseOptions -) => (i: I, overrideOptions?: AST.ParseOptions) => A = decodeUnknownSync - -/** - * @category decoding - * @since 0.67.0 - */ -export const decodeOption: ( - schema: Schema.Schema, - options?: AST.ParseOptions -) => (i: I, overrideOptions?: AST.ParseOptions) => Option.Option = decodeUnknownOption - -/** - * @category decoding - * @since 0.67.0 - */ -export const decodeEither: ( - schema: Schema.Schema, - options?: AST.ParseOptions -) => (i: I, overrideOptions?: AST.ParseOptions) => Either.Either = decodeUnknownEither - -/** - * @category decoding - * @since 0.67.0 - */ -export const decodePromise: ( - schema: Schema.Schema, - options?: AST.ParseOptions -) => (i: I, overrideOptions?: AST.ParseOptions) => Promise = decodeUnknownPromise - -/** - * @category decoding - * @since 0.67.0 - */ -export const decode: ( - schema: Schema.Schema, - options?: AST.ParseOptions -) => (i: I, overrideOptions?: AST.ParseOptions) => Effect.Effect = decodeUnknown - -/** - * @throws `ParseError` - * @category validation - * @since 0.67.0 - */ -export const validateSync = ( - schema: Schema.Schema, - options?: AST.ParseOptions -): (u: unknown, overrideOptions?: AST.ParseOptions) => A => getSync(AST.typeAST(schema.ast), true, options) - -/** - * @category validation - * @since 0.67.0 - */ -export const validateOption = ( - schema: Schema.Schema, - options?: AST.ParseOptions -): (u: unknown, overrideOptions?: AST.ParseOptions) => Option.Option => - getOption(AST.typeAST(schema.ast), true, options) - -/** - * @category validation - * @since 0.67.0 - */ -export const validateEither = ( - schema: Schema.Schema, - options?: AST.ParseOptions -): (u: unknown, overrideOptions?: AST.ParseOptions) => Either.Either => - getEither(AST.typeAST(schema.ast), true, options) - -/** - * @category validation - * @since 0.67.0 - */ -export const validatePromise = ( - schema: Schema.Schema, - options?: AST.ParseOptions -) => { - const parser = validate(schema, options) - return (u: unknown, overrideOptions?: AST.ParseOptions): Promise => Effect.runPromise(parser(u, overrideOptions)) -} - -/** - * @category validation - * @since 0.67.0 - */ -export const validate = ( - schema: Schema.Schema, - options?: AST.ParseOptions -): (a: unknown, overrideOptions?: AST.ParseOptions) => Effect.Effect => - getEffect(AST.typeAST(schema.ast), true, options) - -/** - * By default the option `exact` is set to `true`. - * - * @category validation - * @since 0.67.0 - */ -export const is = (schema: Schema.Schema, options?: AST.ParseOptions) => { - const parser = goMemo(AST.typeAST(schema.ast), true) - return (u: unknown, overrideOptions?: AST.ParseOptions | number): u is A => - Either.isRight(parser(u, { exact: true, ...mergeInternalOptions(options, overrideOptions) }) as any) -} - -/** - * By default the option `exact` is set to `true`. - * - * @throws `ParseError` - * @category validation - * @since 0.67.0 - */ -export const asserts = (schema: Schema.Schema, options?: AST.ParseOptions) => { - const parser = goMemo(AST.typeAST(schema.ast), true) - return (u: unknown, overrideOptions?: AST.ParseOptions): asserts u is A => { - const result: Either.Either = parser(u, { - exact: true, - ...mergeInternalOptions(options, overrideOptions) - }) as any - if (Either.isLeft(result)) { - throw parseError(result.left) - } - } -} - -/** - * @category encoding - * @since 0.67.0 - */ -export const encodeSync: ( - schema: Schema.Schema, - options?: AST.ParseOptions -) => (a: A, overrideOptions?: AST.ParseOptions) => I = encodeUnknownSync - -/** - * @category encoding - * @since 0.67.0 - */ -export const encodeOption: ( - schema: Schema.Schema, - options?: AST.ParseOptions -) => (input: A, overrideOptions?: AST.ParseOptions) => Option.Option = encodeUnknownOption - -/** - * @category encoding - * @since 0.67.0 - */ -export const encodeEither: ( - schema: Schema.Schema, - options?: AST.ParseOptions -) => (a: A, overrideOptions?: AST.ParseOptions) => Either.Either = encodeUnknownEither - -/** - * @category encoding - * @since 0.67.0 - */ -export const encodePromise: ( - schema: Schema.Schema, - options?: AST.ParseOptions -) => (a: A, overrideOptions?: AST.ParseOptions) => Promise = encodeUnknownPromise - -/** - * @category encoding - * @since 0.67.0 - */ -export const encode: ( - schema: Schema.Schema, - options?: AST.ParseOptions -) => (a: A, overrideOptions?: AST.ParseOptions) => Effect.Effect = encodeUnknown - -interface InternalOptions extends AST.ParseOptions { - readonly isEffectAllowed?: boolean -} - -interface Parser { - (i: any, options?: InternalOptions): Effect.Effect -} - -const decodeMemoMap = globalValue( - Symbol.for("@effect/schema/Parser/decodeMemoMap"), - () => new WeakMap() -) -const encodeMemoMap = globalValue( - Symbol.for("@effect/schema/Parser/encodeMemoMap"), - () => new WeakMap() -) - -const goMemo = (ast: AST.AST, isDecoding: boolean): Parser => { - const memoMap = isDecoding ? decodeMemoMap : encodeMemoMap - const memo = memoMap.get(ast) - if (memo) { - return memo - } - const raw = go(ast, isDecoding) - const parseOptionsAnnotation = AST.getParseOptionsAnnotation(ast) - const parserWithOptions: Parser = Option.isSome(parseOptionsAnnotation) - ? (i, options) => raw(i, mergeInternalOptions(options, parseOptionsAnnotation.value)) - : raw - const decodingFallbackAnnotation = AST.getDecodingFallbackAnnotation(ast) - const parser: Parser = isDecoding && Option.isSome(decodingFallbackAnnotation) - ? (i, options) => - handleForbidden(orElse(parserWithOptions(i, options), decodingFallbackAnnotation.value), ast, i, options) - : parserWithOptions - memoMap.set(ast, parser) - return parser -} - -const getConcurrency = (ast: AST.AST): Concurrency | undefined => - Option.getOrUndefined(AST.getConcurrencyAnnotation(ast)) - -const getBatching = (ast: AST.AST): boolean | "inherit" | undefined => - Option.getOrUndefined(AST.getBatchingAnnotation(ast)) - -const go = (ast: AST.AST, isDecoding: boolean): Parser => { - switch (ast._tag) { - case "Refinement": { - if (isDecoding) { - const from = goMemo(ast.from, true) - return (i, options) => { - options = options ?? AST.defaultParseOption - const allErrors = options?.errors === "all" - const result = flatMap( - orElse(from(i, options), (ef) => { - const issue = new Refinement(ast, i, "From", ef) - if (allErrors && AST.hasStableFilter(ast)) { - return Option.match( - ast.filter(i, options, ast), - { - onNone: () => Either.left(issue), - onSome: (ep) => Either.left(new Composite(ast, i, [issue, new Refinement(ast, i, "Predicate", ep)])) - } - ) - } - return Either.left(issue) - }), - (a) => - Option.match( - ast.filter(a, options, ast), - { - onNone: () => Either.right(a), - onSome: (ep) => Either.left(new Refinement(ast, i, "Predicate", ep)) - } - ) - ) - return handleForbidden(result, ast, i, options) - } - } else { - const from = goMemo(AST.typeAST(ast), true) - const to = goMemo(dropRightRefinement(ast.from), false) - return (i, options) => handleForbidden(flatMap(from(i, options), (a) => to(a, options)), ast, i, options) - } - } - case "Transformation": { - const transform = getFinalTransformation(ast.transformation, isDecoding) - const from = isDecoding ? goMemo(ast.from, true) : goMemo(ast.to, false) - const to = isDecoding ? goMemo(ast.to, true) : goMemo(ast.from, false) - return (i, options) => - handleForbidden( - flatMap( - mapError( - from(i, options), - (e) => new Transformation(ast, i, isDecoding ? "Encoded" : "Type", e) - ), - (a) => - flatMap( - mapError( - transform(a, options ?? AST.defaultParseOption, ast, i), - (e) => new Transformation(ast, i, "Transformation", e) - ), - (i2) => - mapError( - to(i2, options), - (e) => new Transformation(ast, i, isDecoding ? "Type" : "Encoded", e) - ) - ) - ), - ast, - i, - options - ) - } - case "Declaration": { - const parse = isDecoding - ? ast.decodeUnknown(...ast.typeParameters) - : ast.encodeUnknown(...ast.typeParameters) - return (i, options) => handleForbidden(parse(i, options ?? AST.defaultParseOption, ast), ast, i, options) - } - case "Literal": - return fromRefinement(ast, (u): u is typeof ast.literal => u === ast.literal) - case "UniqueSymbol": - return fromRefinement(ast, (u): u is typeof ast.symbol => u === ast.symbol) - case "UndefinedKeyword": - return fromRefinement(ast, Predicate.isUndefined) - case "NeverKeyword": - return fromRefinement(ast, Predicate.isNever) - case "UnknownKeyword": - case "AnyKeyword": - case "VoidKeyword": - return Either.right - case "StringKeyword": - return fromRefinement(ast, Predicate.isString) - case "NumberKeyword": - return fromRefinement(ast, Predicate.isNumber) - case "BooleanKeyword": - return fromRefinement(ast, Predicate.isBoolean) - case "BigIntKeyword": - return fromRefinement(ast, Predicate.isBigInt) - case "SymbolKeyword": - return fromRefinement(ast, Predicate.isSymbol) - case "ObjectKeyword": - return fromRefinement(ast, Predicate.isObject) - case "Enums": - return fromRefinement(ast, (u): u is any => ast.enums.some(([_, value]) => value === u)) - case "TemplateLiteral": { - const regex = AST.getTemplateLiteralRegExp(ast) - return fromRefinement(ast, (u): u is any => Predicate.isString(u) && regex.test(u)) - } - case "TupleType": { - const elements = ast.elements.map((e) => goMemo(e.type, isDecoding)) - const rest = ast.rest.map((annotatedAST) => goMemo(annotatedAST.type, isDecoding)) - let requiredTypes: Array = ast.elements.filter((e) => !e.isOptional) - if (ast.rest.length > 0) { - requiredTypes = requiredTypes.concat(ast.rest.slice(1)) - } - const requiredLen = requiredTypes.length - const expectedIndexes = ast.elements.length > 0 ? ast.elements.map((_, i) => i).join(" | ") : "never" - const concurrency = getConcurrency(ast) - const batching = getBatching(ast) - return (input: unknown, options) => { - if (!array_.isArray(input)) { - return Either.left(new Type(ast, input)) - } - const allErrors = options?.errors === "all" - const es: Array<[number, ParseIssue]> = [] - let stepKey = 0 - const output: Array<[number, any]> = [] - // --------------------------------------------- - // handle missing indexes - // --------------------------------------------- - const len = input.length - for (let i = len; i <= requiredLen - 1; i++) { - const e = new Pointer(i, input, new Missing(requiredTypes[i - len])) - if (allErrors) { - es.push([stepKey++, e]) - continue - } else { - return Either.left(new Composite(ast, input, e, output)) - } - } - - // --------------------------------------------- - // handle excess indexes - // --------------------------------------------- - if (ast.rest.length === 0) { - for (let i = ast.elements.length; i <= len - 1; i++) { - const e = new Pointer(i, input, new Unexpected(input[i], `is unexpected, expected: ${expectedIndexes}`)) - if (allErrors) { - es.push([stepKey++, e]) - continue - } else { - return Either.left(new Composite(ast, input, e, output)) - } - } - } - - let i = 0 - type State = { - es: typeof es - output: typeof output - } - let queue: - | Array<(_: State) => Effect.Effect> - | undefined = undefined - - // --------------------------------------------- - // handle elements - // --------------------------------------------- - for (; i < elements.length; i++) { - if (len < i + 1) { - if (ast.elements[i].isOptional) { - // the input element is missing - continue - } - } else { - const parser = elements[i] - const te = parser(input[i], options) - const eu = eitherOrUndefined(te) - if (eu) { - if (Either.isLeft(eu)) { - // the input element is present but is not valid - const e = new Pointer(i, input, eu.left) - if (allErrors) { - es.push([stepKey++, e]) - continue - } else { - return Either.left(new Composite(ast, input, e, sortByIndex(output))) - } - } - output.push([stepKey++, eu.right]) - } else { - const nk = stepKey++ - const index = i - if (!queue) { - queue = [] - } - queue.push(({ es, output }: State) => - Effect.flatMap(Effect.either(te), (t) => { - if (Either.isLeft(t)) { - // the input element is present but is not valid - const e = new Pointer(index, input, t.left) - if (allErrors) { - es.push([nk, e]) - return Effect.void - } else { - return Either.left(new Composite(ast, input, e, sortByIndex(output))) - } - } - output.push([nk, t.right]) - return Effect.void - }) - ) - } - } - } - // --------------------------------------------- - // handle rest element - // --------------------------------------------- - if (array_.isNonEmptyReadonlyArray(rest)) { - const [head, ...tail] = rest - for (; i < len - tail.length; i++) { - const te = head(input[i], options) - const eu = eitherOrUndefined(te) - if (eu) { - if (Either.isLeft(eu)) { - const e = new Pointer(i, input, eu.left) - if (allErrors) { - es.push([stepKey++, e]) - continue - } else { - return Either.left(new Composite(ast, input, e, sortByIndex(output))) - } - } else { - output.push([stepKey++, eu.right]) - } - } else { - const nk = stepKey++ - const index = i - if (!queue) { - queue = [] - } - queue.push( - ({ es, output }: State) => - Effect.flatMap(Effect.either(te), (t) => { - if (Either.isLeft(t)) { - const e = new Pointer(index, input, t.left) - if (allErrors) { - es.push([nk, e]) - return Effect.void - } else { - return Either.left(new Composite(ast, input, e, sortByIndex(output))) - } - } else { - output.push([nk, t.right]) - return Effect.void - } - }) - ) - } - } - // --------------------------------------------- - // handle post rest elements - // --------------------------------------------- - for (let j = 0; j < tail.length; j++) { - i += j - if (len < i + 1) { - continue - } else { - const te = tail[j](input[i], options) - const eu = eitherOrUndefined(te) - if (eu) { - if (Either.isLeft(eu)) { - // the input element is present but is not valid - const e = new Pointer(i, input, eu.left) - if (allErrors) { - es.push([stepKey++, e]) - continue - } else { - return Either.left(new Composite(ast, input, e, sortByIndex(output))) - } - } - output.push([stepKey++, eu.right]) - } else { - const nk = stepKey++ - const index = i - if (!queue) { - queue = [] - } - queue.push( - ({ es, output }: State) => - Effect.flatMap(Effect.either(te), (t) => { - if (Either.isLeft(t)) { - // the input element is present but is not valid - const e = new Pointer(index, input, t.left) - if (allErrors) { - es.push([nk, e]) - return Effect.void - } else { - return Either.left(new Composite(ast, input, e, sortByIndex(output))) - } - } - output.push([nk, t.right]) - return Effect.void - }) - ) - } - } - } - } - - // --------------------------------------------- - // compute result - // --------------------------------------------- - const computeResult = ({ es, output }: State) => - array_.isNonEmptyArray(es) ? - Either.left(new Composite(ast, input, sortByIndex(es), sortByIndex(output))) : - Either.right(sortByIndex(output)) - if (queue && queue.length > 0) { - const cqueue = queue - return Effect.suspend(() => { - const state: State = { - es: array_.copy(es), - output: array_.copy(output) - } - return Effect.flatMap( - Effect.forEach(cqueue, (f) => f(state), { concurrency, batching, discard: true }), - () => computeResult(state) - ) - }) - } - return computeResult({ output, es }) - } - } - case "TypeLiteral": { - if (ast.propertySignatures.length === 0 && ast.indexSignatures.length === 0) { - return fromRefinement(ast, Predicate.isNotNullable) - } - - const propertySignatures: Array = [] - const expectedKeysMap: Record = {} - const expectedKeys: Array = [] - for (const ps of ast.propertySignatures) { - propertySignatures.push([goMemo(ps.type, isDecoding), ps]) - expectedKeysMap[ps.name] = null - expectedKeys.push(ps.name) - } - - const indexSignatures = ast.indexSignatures.map((is) => - [ - goMemo(is.parameter, isDecoding), - goMemo(is.type, isDecoding), - is.parameter - ] as const - ) - const expectedAST = AST.Union.make( - ast.indexSignatures.map((is): AST.AST => is.parameter).concat( - expectedKeys.map((key) => Predicate.isSymbol(key) ? new AST.UniqueSymbol(key) : new AST.Literal(key)) - ) - ) - const expected = goMemo(expectedAST, isDecoding) - const concurrency = getConcurrency(ast) - const batching = getBatching(ast) - return (input: unknown, options) => { - if (!Predicate.isRecord(input)) { - return Either.left(new Type(ast, input)) - } - const allErrors = options?.errors === "all" - const es: Array<[number, ParseIssue]> = [] - let stepKey = 0 - - // --------------------------------------------- - // handle excess properties - // --------------------------------------------- - const onExcessPropertyError = options?.onExcessProperty === "error" - const onExcessPropertyPreserve = options?.onExcessProperty === "preserve" - const output: Record = {} - let inputKeys: Array | undefined - if (onExcessPropertyError || onExcessPropertyPreserve) { - inputKeys = util_.ownKeys(input) - for (const key of inputKeys) { - const eu = eitherOrUndefined(expected(key, options))! - if (Either.isLeft(eu)) { - // key is unexpected - if (onExcessPropertyError) { - const e = new Pointer( - key, - input, - new Unexpected(input[key], `is unexpected, expected: ${String(expectedAST)}`) - ) - if (allErrors) { - es.push([stepKey++, e]) - continue - } else { - return Either.left(new Composite(ast, input, e, output)) - } - } else { - // preserve key - output[key] = input[key] - } - } - } - } - - // --------------------------------------------- - // handle property signatures - // --------------------------------------------- - type State = { - es: typeof es - output: typeof output - } - let queue: - | Array<(state: State) => Effect.Effect> - | undefined = undefined - - const isExact = options?.exact === true - for (let i = 0; i < propertySignatures.length; i++) { - const ps = propertySignatures[i][1] - const name = ps.name - const hasKey = Object.prototype.hasOwnProperty.call(input, name) - if (!hasKey) { - if (ps.isOptional) { - continue - } else if (isExact) { - const e = new Pointer(name, input, new Missing(ps)) - if (allErrors) { - es.push([stepKey++, e]) - continue - } else { - return Either.left(new Composite(ast, input, e, output)) - } - } - } - const parser = propertySignatures[i][0] - const te = parser(input[name], options) - const eu = eitherOrUndefined(te) - if (eu) { - if (Either.isLeft(eu)) { - const e = new Pointer(name, input, hasKey ? eu.left : new Missing(ps)) - if (allErrors) { - es.push([stepKey++, e]) - continue - } else { - return Either.left(new Composite(ast, input, e, output)) - } - } - output[name] = eu.right - } else { - const nk = stepKey++ - const index = name - if (!queue) { - queue = [] - } - queue.push( - ({ es, output }: State) => - Effect.flatMap(Effect.either(te), (t) => { - if (Either.isLeft(t)) { - const e = new Pointer(index, input, hasKey ? t.left : new Missing(ps)) - if (allErrors) { - es.push([nk, e]) - return Effect.void - } else { - return Either.left(new Composite(ast, input, e, output)) - } - } - output[index] = t.right - return Effect.void - }) - ) - } - } - - // --------------------------------------------- - // handle index signatures - // --------------------------------------------- - for (let i = 0; i < indexSignatures.length; i++) { - const indexSignature = indexSignatures[i] - const parameter = indexSignature[0] - const type = indexSignature[1] - const keys = util_.getKeysForIndexSignature(input, indexSignature[2]) - for (const key of keys) { - // --------------------------------------------- - // handle keys - // --------------------------------------------- - const keu = eitherOrUndefined(parameter(key, options)) - if (keu && Either.isRight(keu)) { - // --------------------------------------------- - // handle values - // --------------------------------------------- - const vpr = type(input[key], options) - const veu = eitherOrUndefined(vpr) - if (veu) { - if (Either.isLeft(veu)) { - const e = new Pointer(key, input, veu.left) - if (allErrors) { - es.push([stepKey++, e]) - continue - } else { - return Either.left(new Composite(ast, input, e, output)) - } - } else { - if (!Object.prototype.hasOwnProperty.call(expectedKeysMap, key)) { - output[key] = veu.right - } - } - } else { - const nk = stepKey++ - const index = key - if (!queue) { - queue = [] - } - queue.push( - ({ es, output }: State) => - Effect.flatMap( - Effect.either(vpr), - (tv) => { - if (Either.isLeft(tv)) { - const e = new Pointer(index, input, tv.left) - if (allErrors) { - es.push([nk, e]) - return Effect.void - } else { - return Either.left(new Composite(ast, input, e, output)) - } - } else { - if (!Object.prototype.hasOwnProperty.call(expectedKeysMap, key)) { - output[key] = tv.right - } - return Effect.void - } - } - ) - ) - } - } - } - } - // --------------------------------------------- - // compute result - // --------------------------------------------- - const computeResult = ({ es, output }: State) => { - if (array_.isNonEmptyArray(es)) { - return Either.left(new Composite(ast, input, sortByIndex(es), output)) - } - if (options?.propertyOrder === "original") { - // preserve input keys order - const keys = inputKeys || util_.ownKeys(input) - for (const name of expectedKeys) { - if (keys.indexOf(name) === -1) { - keys.push(name) - } - } - const out: any = {} - for (const key of keys) { - if (Object.prototype.hasOwnProperty.call(output, key)) { - out[key] = output[key] - } - } - return Either.right(out) - } - return Either.right(output) - } - if (queue && queue.length > 0) { - const cqueue = queue - return Effect.suspend(() => { - const state: State = { - es: array_.copy(es), - output: Object.assign({}, output) - } - return Effect.flatMap( - Effect.forEach(cqueue, (f) => f(state), { concurrency, batching, discard: true }), - () => computeResult(state) - ) - }) - } - return computeResult({ es, output }) - } - } - case "Union": { - const searchTree = getSearchTree(ast.types, isDecoding) - const ownKeys = util_.ownKeys(searchTree.keys) - const len = ownKeys.length - const map = new Map() - for (let i = 0; i < ast.types.length; i++) { - map.set(ast.types[i], goMemo(ast.types[i], isDecoding)) - } - const concurrency = getConcurrency(ast) ?? 1 - const batching = getBatching(ast) - return (input, options) => { - const es: Array<[number, ParseIssue]> = [] - let stepKey = 0 - let candidates: Array = [] - if (len > 0) { - // if there is at least one key then input must be an object - if (isObject(input)) { - for (let i = 0; i < len; i++) { - const name = ownKeys[i] - const buckets = searchTree.keys[name].buckets - // for each property that should contain a literal, check if the input contains that property - if (Object.prototype.hasOwnProperty.call(input, name)) { - const literal = String(input[name]) - // check that the value obtained from the input for the property corresponds to an existing bucket - if (Object.prototype.hasOwnProperty.call(buckets, literal)) { - // retrive the minimal set of candidates for decoding - candidates = candidates.concat(buckets[literal]) - } else { - const literals = AST.Union.make(searchTree.keys[name].literals) - es.push([ - stepKey++, - new Composite( - new AST.TypeLiteral([ - new AST.PropertySignature(name, literals, false, true) - ], []), - input, - new Pointer(name, input, new Type(literals, input[name])) - ) - ]) - } - } else { - const literals = AST.Union.make(searchTree.keys[name].literals) - const fakeps = new AST.PropertySignature(name, literals, false, true) - es.push([ - stepKey++, - new Composite( - new AST.TypeLiteral([fakeps], []), - input, - new Pointer(name, input, new Missing(fakeps)) - ) - ]) - } - } - } else { - es.push([stepKey++, new Type(ast, input)]) - } - } - if (searchTree.otherwise.length > 0) { - candidates = candidates.concat(searchTree.otherwise) - } - - let queue: - | Array<(state: State) => Effect.Effect> - | undefined = undefined - - type State = { - finalResult?: any - es: typeof es - } - - for (let i = 0; i < candidates.length; i++) { - const candidate = candidates[i] - const pr = map.get(candidate)!(input, options) - // the members of a union are ordered based on which one should be decoded first, - // therefore if one member has added a task, all subsequent members must - // also add a task to the queue even if they are synchronous - const eu = !queue || queue.length === 0 ? eitherOrUndefined(pr) : undefined - if (eu) { - if (Either.isRight(eu)) { - return eu - } else { - es.push([stepKey++, eu.left]) - } - } else { - const nk = stepKey++ - if (!queue) { - queue = [] - } - queue.push( - (state) => - Effect.suspend(() => { - if ("finalResult" in state) { - return Effect.void - } else { - return Effect.flatMap(Effect.either(pr), (t) => { - if (Either.isRight(t)) { - state.finalResult = t - } else { - state.es.push([nk, t.left]) - } - return Effect.void - }) - } - }) - ) - } - } - - // --------------------------------------------- - // compute result - // --------------------------------------------- - const computeResult = (es: State["es"]) => - array_.isNonEmptyArray(es) ? - es.length === 1 && es[0][1]._tag === "Type" ? - Either.left(es[0][1]) : - Either.left(new Composite(ast, input, sortByIndex(es))) : - // this should never happen - Either.left(new Type(ast, input)) - - if (queue && queue.length > 0) { - const cqueue = queue - return Effect.suspend(() => { - const state: State = { es: array_.copy(es) } - return Effect.flatMap( - Effect.forEach(cqueue, (f) => f(state), { concurrency, batching, discard: true }), - () => { - if ("finalResult" in state) { - return state.finalResult - } - return computeResult(state.es) - } - ) - }) - } - return computeResult(es) - } - } - case "Suspend": { - const get = util_.memoizeThunk(() => goMemo(AST.annotations(ast.f(), ast.annotations), isDecoding)) - return (a, options) => get()(a, options) - } - } -} - -const isObject = (input: unknown): input is { [x: PropertyKey]: unknown } => typeof input === "object" && input !== null - -const fromRefinement = (ast: AST.AST, refinement: (u: unknown) => u is A): Parser => (u) => - refinement(u) ? Either.right(u) : Either.left(new Type(ast, u)) - -/** @internal */ -export const getLiterals = ( - ast: AST.AST, - isDecoding: boolean -): ReadonlyArray<[PropertyKey, AST.Literal]> => { - switch (ast._tag) { - case "Declaration": { - const annotation = AST.getSurrogateAnnotation(ast) - if (Option.isSome(annotation)) { - return getLiterals(annotation.value, isDecoding) - } - break - } - case "TypeLiteral": { - const out: Array<[PropertyKey, AST.Literal]> = [] - for (let i = 0; i < ast.propertySignatures.length; i++) { - const propertySignature = ast.propertySignatures[i] - const type = isDecoding ? AST.encodedAST(propertySignature.type) : AST.typeAST(propertySignature.type) - if (AST.isLiteral(type) && !propertySignature.isOptional) { - out.push([propertySignature.name, type]) - } - } - return out - } - case "TupleType": { - const out: Array<[PropertyKey, AST.Literal]> = [] - for (let i = 0; i < ast.elements.length; i++) { - const element = ast.elements[i] - const type = isDecoding ? AST.encodedAST(element.type) : AST.typeAST(element.type) - if (AST.isLiteral(type) && !element.isOptional) { - out.push([i, type]) - } - } - return out - } - case "Refinement": - return getLiterals(ast.from, isDecoding) - case "Suspend": - return getLiterals(ast.f(), isDecoding) - case "Transformation": - return getLiterals(isDecoding ? ast.from : ast.to, isDecoding) - } - return [] -} - -/** - * The purpose of the algorithm is to narrow down the pool of possible candidates for decoding as much as possible. - * - * This function separates the schemas into two groups, `keys` and `otherwise`: - * - * - `keys`: the schema has at least one property with a literal value - * - `otherwise`: the schema has no properties with a literal value - * - * If a schema has at least one property with a literal value, so it ends up in `keys`, first a namespace is created for - * the name of the property containing the literal, and then within this namespace a "bucket" is created for the literal - * value in which to store all the schemas that have the same property and literal value. - * - * @internal - */ -export const getSearchTree = ( - members: ReadonlyArray, - isDecoding: boolean -): { - keys: { - readonly [key: PropertyKey]: { - buckets: { [literal: string]: ReadonlyArray } - literals: ReadonlyArray // this is for error messages - } - } - otherwise: ReadonlyArray -} => { - const keys: { - [key: PropertyKey]: { - buckets: { [literal: string]: Array } - literals: Array - } - } = {} - const otherwise: Array = [] - for (let i = 0; i < members.length; i++) { - const member = members[i] - const tags = getLiterals(member, isDecoding) - if (tags.length > 0) { - for (let j = 0; j < tags.length; j++) { - const [key, literal] = tags[j] - const hash = String(literal.literal) - keys[key] = keys[key] || { buckets: {}, literals: [] } - const buckets = keys[key].buckets - if (Object.prototype.hasOwnProperty.call(buckets, hash)) { - if (j < tags.length - 1) { - continue - } - buckets[hash].push(member) - keys[key].literals.push(literal) - } else { - buckets[hash] = [member] - keys[key].literals.push(literal) - break - } - } - } else { - otherwise.push(member) - } - } - return { keys, otherwise } -} - -const dropRightRefinement = (ast: AST.AST): AST.AST => AST.isRefinement(ast) ? dropRightRefinement(ast.from) : ast - -const handleForbidden = ( - effect: Effect.Effect, - ast: AST.AST, - actual: unknown, - options: InternalOptions | undefined -): Effect.Effect => { - const eu = eitherOrUndefined(effect) - if (eu) { - return eu - } - if (options?.isEffectAllowed === true) { - return effect - } - try { - return Effect.runSync(Effect.either(effect as Effect.Effect)) - } catch (e) { - return Either.left( - new Forbidden( - ast, - actual, - "cannot be be resolved synchronously, this is caused by using runSync on an effect that performs async work" - ) - ) - } -} - -const compare = ([a]: [number, ...Array], [b]: [number, ...Array]) => a > b ? 1 : a < b ? -1 : 0 - -function sortByIndex( - es: array_.NonEmptyArray<[number, T]> -): array_.NonEmptyArray -function sortByIndex(es: Array<[number, T]>): Array -function sortByIndex(es: Array<[number, any]>) { - return es.sort(compare).map((t) => t[1]) -} - -// ------------------------------------------------------------------------------------- -// transformations interpreter -// ------------------------------------------------------------------------------------- - -/** @internal */ -export const getFinalTransformation = ( - transformation: AST.TransformationKind, - isDecoding: boolean -): ( - fromA: any, - options: AST.ParseOptions, - self: AST.Transformation, - fromI: any -) => Effect.Effect => { - switch (transformation._tag) { - case "FinalTransformation": - return isDecoding ? transformation.decode : transformation.encode - case "ComposeTransformation": - return Either.right - case "TypeLiteralTransformation": - return (input) => { - let out: Effect.Effect = Either.right(input) - - // --------------------------------------------- - // handle property signature transformations - // --------------------------------------------- - for (const pst of transformation.propertySignatureTransformations) { - const [from, to] = isDecoding ? - [pst.from, pst.to] : - [pst.to, pst.from] - const transformation = isDecoding ? pst.decode : pst.encode - const f = (input: any) => { - const o = transformation( - Object.prototype.hasOwnProperty.call(input, from) ? - Option.some(input[from]) : - Option.none() - ) - delete input[from] - if (Option.isSome(o)) { - input[to] = o.value - } - return input - } - out = map(out, f) - } - return out - } - } -} +export * from "effect/ParseResult" diff --git a/packages/schema/src/Pretty.ts b/packages/schema/src/Pretty.ts index bff905d33b..48af116876 100644 --- a/packages/schema/src/Pretty.ts +++ b/packages/schema/src/Pretty.ts @@ -1,218 +1,9 @@ /** * @since 0.67.0 */ -import * as Arr from "effect/Array" -import * as Option from "effect/Option" -import * as AST from "./AST.js" -import * as errors_ from "./internal/errors.js" -import * as util_ from "./internal/util.js" -import * as ParseResult from "./ParseResult.js" -import type * as Schema from "./Schema.js" /** - * @category model - * @since 0.67.0 - */ -export interface Pretty { - (a: To): string -} - -/** - * @category hooks - * @since 0.67.0 + * @category re-exports + * @since 0.76.0 */ -export const PrettyHookId: unique symbol = Symbol.for("@effect/schema/PrettyHookId") - -/** - * @category hooks - * @since 0.67.0 - */ -export type PrettyHookId = typeof PrettyHookId - -/** - * @category annotations - * @since 0.67.0 - */ -export const pretty = - (handler: (...args: ReadonlyArray>) => Pretty) => - (self: Schema.Schema): Schema.Schema => self.annotations({ [PrettyHookId]: handler }) - -/** - * @category prettify - * @since 0.67.0 - */ -export const make = (schema: Schema.Schema): (a: A) => string => compile(schema.ast, []) - -const getHook = AST.getAnnotation<(...args: ReadonlyArray>) => Pretty>( - PrettyHookId -) - -const getMatcher = (defaultPretty: Pretty) => (ast: AST.AST): Pretty => - Option.match(getHook(ast), { - onNone: () => defaultPretty, - onSome: (handler) => handler() - }) - -const toString = getMatcher((a) => String(a)) - -const stringify = getMatcher((a) => JSON.stringify(a)) - -const formatUnknown = getMatcher(util_.formatUnknown) - -/** - * @since 0.67.0 - */ -export const match: AST.Match> = { - "Declaration": (ast, go, path) => { - const hook = getHook(ast) - if (Option.isSome(hook)) { - return hook.value(...ast.typeParameters.map((tp) => go(tp, path))) - } - throw new Error(errors_.getPrettyMissingAnnotationErrorMessage(path, ast)) - }, - "VoidKeyword": getMatcher(() => "void(0)"), - "NeverKeyword": getMatcher(() => { - throw new Error(errors_.getPrettyNeverErrorMessage) - }), - "Literal": getMatcher((literal: AST.LiteralValue): string => - typeof literal === "bigint" ? - `${String(literal)}n` : - JSON.stringify(literal) - ), - "SymbolKeyword": toString, - "UniqueSymbol": toString, - "TemplateLiteral": stringify, - "UndefinedKeyword": toString, - "UnknownKeyword": formatUnknown, - "AnyKeyword": formatUnknown, - "ObjectKeyword": formatUnknown, - "StringKeyword": stringify, - "NumberKeyword": toString, - "BooleanKeyword": toString, - "BigIntKeyword": getMatcher((a) => `${String(a)}n`), - "Enums": stringify, - "TupleType": (ast, go, path) => { - const hook = getHook(ast) - if (Option.isSome(hook)) { - return hook.value() - } - const elements = ast.elements.map((e, i) => go(e.type, path.concat(i))) - const rest = ast.rest.map((annotatedAST) => go(annotatedAST.type, path)) - return (input: ReadonlyArray) => { - const output: Array = [] - let i = 0 - // --------------------------------------------- - // handle elements - // --------------------------------------------- - for (; i < elements.length; i++) { - if (input.length < i + 1) { - if (ast.elements[i].isOptional) { - continue - } - } else { - output.push(elements[i](input[i])) - } - } - // --------------------------------------------- - // handle rest element - // --------------------------------------------- - if (Arr.isNonEmptyReadonlyArray(rest)) { - const [head, ...tail] = rest - for (; i < input.length - tail.length; i++) { - output.push(head(input[i])) - } - // --------------------------------------------- - // handle post rest elements - // --------------------------------------------- - for (let j = 0; j < tail.length; j++) { - i += j - output.push(tail[j](input[i])) - } - } - - return "[" + output.join(", ") + "]" - } - }, - "TypeLiteral": (ast, go, path) => { - const hook = getHook(ast) - if (Option.isSome(hook)) { - return hook.value() - } - const propertySignaturesTypes = ast.propertySignatures.map((ps) => go(ps.type, path.concat(ps.name))) - const indexSignatureTypes = ast.indexSignatures.map((is) => go(is.type, path)) - const expectedKeys: any = {} - for (let i = 0; i < propertySignaturesTypes.length; i++) { - expectedKeys[ast.propertySignatures[i].name] = null - } - return (input: { readonly [x: PropertyKey]: unknown }) => { - const output: Array = [] - // --------------------------------------------- - // handle property signatures - // --------------------------------------------- - for (let i = 0; i < propertySignaturesTypes.length; i++) { - const ps = ast.propertySignatures[i] - const name = ps.name - if (ps.isOptional && !Object.prototype.hasOwnProperty.call(input, name)) { - continue - } - output.push( - `${util_.formatPropertyKey(name)}: ${propertySignaturesTypes[i](input[name])}` - ) - } - // --------------------------------------------- - // handle index signatures - // --------------------------------------------- - if (indexSignatureTypes.length > 0) { - for (let i = 0; i < indexSignatureTypes.length; i++) { - const type = indexSignatureTypes[i] - const keys = util_.getKeysForIndexSignature(input, ast.indexSignatures[i].parameter) - for (const key of keys) { - if (Object.prototype.hasOwnProperty.call(expectedKeys, key)) { - continue - } - output.push(`${util_.formatPropertyKey(key)}: ${type(input[key])}`) - } - } - } - - return Arr.isNonEmptyReadonlyArray(output) ? "{ " + output.join(", ") + " }" : "{}" - } - }, - "Union": (ast, go, path) => { - const hook = getHook(ast) - if (Option.isSome(hook)) { - return hook.value() - } - const types = ast.types.map((ast) => [ParseResult.is({ ast } as any), go(ast, path)] as const) - return (a) => { - const index = types.findIndex(([is]) => is(a)) - if (index === -1) { - throw new Error(errors_.getPrettyNoMatchingSchemaErrorMessage(a, path, ast)) - } - return types[index][1](a) - } - }, - "Suspend": (ast, go, path) => { - return Option.match(getHook(ast), { - onNone: () => { - const get = util_.memoizeThunk(() => go(ast.f(), path)) - return (a) => get()(a) - }, - onSome: (handler) => handler() - }) - }, - "Refinement": (ast, go, path) => { - return Option.match(getHook(ast), { - onNone: () => go(ast.from, path), - onSome: (handler) => handler() - }) - }, - "Transformation": (ast, go, path) => { - return Option.match(getHook(ast), { - onNone: () => go(ast.to, path), - onSome: (handler) => handler() - }) - } -} - -const compile = AST.getCompiler(match) +export * from "effect/Pretty" diff --git a/packages/schema/src/Schema.ts b/packages/schema/src/Schema.ts index 32c7bd1fd9..6d539c56ea 100644 --- a/packages/schema/src/Schema.ts +++ b/packages/schema/src/Schema.ts @@ -2,9274 +2,8 @@ * @since 0.67.0 */ -import * as array_ from "effect/Array" -import * as bigDecimal_ from "effect/BigDecimal" -import * as bigInt_ from "effect/BigInt" -import * as boolean_ from "effect/Boolean" -import type { Brand } from "effect/Brand" -import * as cause_ from "effect/Cause" -import * as chunk_ from "effect/Chunk" -import * as config_ from "effect/Config" -import * as configError_ from "effect/ConfigError" -import * as data_ from "effect/Data" -import * as dateTime from "effect/DateTime" -import * as duration_ from "effect/Duration" -import * as Effect from "effect/Effect" -import * as either_ from "effect/Either" -import * as Encoding from "effect/Encoding" -import * as Equal from "effect/Equal" -import * as Equivalence from "effect/Equivalence" -import * as exit_ from "effect/Exit" -import * as fiberId_ from "effect/FiberId" -import type { LazyArg } from "effect/Function" -import { dual, identity } from "effect/Function" -import * as hashMap_ from "effect/HashMap" -import * as hashSet_ from "effect/HashSet" -import * as list_ from "effect/List" -import * as number_ from "effect/Number" -import * as option_ from "effect/Option" -import type * as Order from "effect/Order" -import type { Pipeable } from "effect/Pipeable" -import { pipeArguments } from "effect/Pipeable" -import * as Predicate from "effect/Predicate" -import * as record_ from "effect/Record" -import * as redacted_ from "effect/Redacted" -import * as Request from "effect/Request" -import * as sortedSet_ from "effect/SortedSet" -import * as string_ from "effect/String" -import * as struct_ from "effect/Struct" -import type * as Types from "effect/Types" -import type { GenerationContext, LazyArbitrary } from "./Arbitrary.js" -import * as arbitrary_ from "./Arbitrary.js" -import type { ParseOptions } from "./AST.js" -import * as AST from "./AST.js" -import * as equivalence_ from "./Equivalence.js" -import * as fastCheck_ from "./FastCheck.js" -import * as errors_ from "./internal/errors.js" -import * as filters_ from "./internal/filters.js" -import * as serializable_ from "./internal/serializable.js" -import * as util_ from "./internal/util.js" -import * as ParseResult from "./ParseResult.js" -import * as pretty_ from "./Pretty.js" -import type * as Serializable from "./Serializable.js" -import * as TreeFormatter from "./TreeFormatter.js" - -/** - * @since 0.68.2 - */ -export type Simplify = { [K in keyof A]: A[K] } & {} - -/** - * @since 0.67.0 - */ -export type SimplifyMutable = { - -readonly [K in keyof A]: A[K] -} extends infer B ? B : never - -/** - * @since 0.67.0 - * @category symbol - */ -export const TypeId: unique symbol = Symbol.for("@effect/schema/Schema") - -/** - * @since 0.67.0 - * @category symbol - */ -export type TypeId = typeof TypeId - -/** - * @category model - * @since 0.67.0 - */ -export interface Schema extends Schema.Variance, Pipeable { - readonly Type: A - readonly Encoded: I - /** @since 0.69.3 */ - readonly Context: R - readonly ast: AST.AST - /** - * Merges a set of new annotations with existing ones, potentially overwriting - * any duplicates. - */ - annotations(annotations: Annotations.Schema): Schema -} - -/** - * @category model - * @since 0.67.0 - */ -export interface SchemaClass extends AnnotableClass, A, I, R> {} - -/** - * @category constructors - * @since 0.67.0 - */ -export const make = (ast: AST.AST): SchemaClass => - class SchemaClass { - [TypeId] = variance - static Type: A - static Encoded: I - static Context: R - static [TypeId] = variance - static ast = ast - static annotations(annotations: Annotations.Schema) { - return make(mergeSchemaAnnotations(this.ast, annotations)) - } - static pipe() { - return pipeArguments(this, arguments) - } - static toString() { - return String(ast) - } - } - -const variance = { - /* c8 ignore next */ - _A: (_: any) => _, - /* c8 ignore next */ - _I: (_: any) => _, - /* c8 ignore next */ - _R: (_: never) => _ -} - -interface AllAnnotations> - extends Annotations.Schema, PropertySignature.Annotations -{} - -const toASTAnnotations = >( - annotations?: AllAnnotations -): AST.Annotations => { - if (!annotations) { - return {} - } - const out: Types.Mutable = {} - - // symbols are reserved for custom annotations - const custom = Object.getOwnPropertySymbols(annotations) - for (const sym of custom) { - out[sym] = annotations[sym] - } - - // string keys are reserved as /schema namespace - if (annotations.typeId !== undefined) { - const typeId = annotations.typeId - if (typeof typeId === "object") { - out[AST.TypeAnnotationId] = typeId.id - out[typeId.id] = typeId.annotation - } else { - out[AST.TypeAnnotationId] = typeId - } - } - const move = (from: keyof typeof annotations, to: symbol) => { - if (annotations[from] !== undefined) { - out[to] = annotations[from] - } - } - move("message", AST.MessageAnnotationId) - move("missingMessage", AST.MissingMessageAnnotationId) - move("identifier", AST.IdentifierAnnotationId) - move("title", AST.TitleAnnotationId) - move("description", AST.DescriptionAnnotationId) - move("examples", AST.ExamplesAnnotationId) - move("default", AST.DefaultAnnotationId) - move("documentation", AST.DocumentationAnnotationId) - move("jsonSchema", AST.JSONSchemaAnnotationId) - move("arbitrary", arbitrary_.ArbitraryHookId) - move("pretty", pretty_.PrettyHookId) - move("equivalence", equivalence_.EquivalenceHookId) - move("concurrency", AST.ConcurrencyAnnotationId) - move("batching", AST.BatchingAnnotationId) - move("parseIssueTitle", AST.ParseIssueTitleAnnotationId) - move("parseOptions", AST.ParseOptionsAnnotationId) - move("decodingFallback", AST.DecodingFallbackAnnotationId) - - return out -} - -const mergeSchemaAnnotations = (ast: AST.AST, annotations: Annotations.Schema): AST.AST => - AST.annotations(ast, toASTAnnotations(annotations)) - -/** - * @category annotations - * @since 0.67.0 - */ -export declare namespace Annotable { - /** - * @since 0.67.0 - */ - export type Self = ReturnType - - /** - * @since 0.67.0 - */ - export type Any = Annotable - - /** - * @since 0.67.0 - */ - export type All = - | Any - | Annotable - | Annotable - | Annotable -} - -/** - * @category annotations - * @since 0.67.0 - */ -export interface Annotable, A, I = A, R = never> extends Schema { - annotations(annotations: Annotations.Schema): Self -} - -/** - * @category annotations - * @since 0.67.0 - */ -export interface AnnotableClass, A, I = A, R = never> extends Annotable { - new(_: never): Schema.Variance -} - -/** - * @since 0.67.0 - */ -export const asSchema = ( - schema: S -): Schema, Schema.Encoded, Schema.Context> => schema as any - -/** - * @category formatting - * @since 0.67.0 - */ -export const format = (schema: S): string => String(schema.ast) - -/** - * @since 0.67.0 - */ -export declare namespace Schema { - /** - * @since 0.67.0 - */ - export interface Variance { - readonly [TypeId]: { - readonly _A: Types.Invariant - readonly _I: Types.Invariant - readonly _R: Types.Covariant - } - } - - /** - * @since 0.67.0 - */ - export type Type = S extends Schema.Variance ? A : never - - /** - * @since 0.67.0 - */ - export type Encoded = S extends Schema.Variance ? I : never - - /** - * @since 0.67.0 - */ - export type Context = S extends Schema.Variance ? R : never - - /** - * @since 0.67.0 - */ - export type ToAsserts = ( - input: unknown, - options?: AST.ParseOptions - ) => asserts input is Schema.Type - - /** - * Any schema, except for `never`. - * - * @since 0.67.0 - */ - export type Any = Schema - - /** - * Any schema with `Context = never`, except for `never`. - * - * @since 0.67.0 - */ - export type AnyNoContext = Schema - - /** - * Any schema, including `never`. - * - * @since 0.67.0 - */ - export type All = - | Any - | Schema - | Schema - | Schema - - /** - * Type-level counterpart of `Schema.asSchema` function. - * - * @since 0.67.0 - */ - export type AsSchema = Schema, Encoded, Context> -} - -/** - * The `encodedSchema` function allows you to extract the `Encoded` portion of a - * schema, creating a new schema that conforms to the properties defined in the - * original schema without retaining any refinements or transformations that - * were applied previously. - * - * @since 0.67.0 - */ -export const encodedSchema = (schema: Schema): SchemaClass => make(AST.encodedAST(schema.ast)) - -/** - * The `encodedBoundSchema` function is similar to `encodedSchema` but preserves - * the refinements up to the first transformation point in the original schema. - * - * @since 0.67.17 - */ -export const encodedBoundSchema = (schema: Schema): SchemaClass => - make(AST.encodedBoundAST(schema.ast)) - -/** - * The `typeSchema` function allows you to extract the `Type` portion of a - * schema, creating a new schema that conforms to the properties defined in the - * original schema without considering the initial encoding or transformation - * processes. - * - * @since 0.67.0 - */ -export const typeSchema = (schema: Schema): SchemaClass => make(AST.typeAST(schema.ast)) - -/* c8 ignore start */ -export { - /** - * By default the option `exact` is set to `true`. - * - * @throws `ParseError` - * @category validation - * @since 0.67.0 - */ - asserts, - /** - * @category decoding - * @since 0.67.0 - */ - decodeOption, - /** - * @throws `ParseError` - * @category decoding - * @since 0.67.0 - */ - decodeSync, - /** - * @category decoding - * @since 0.67.0 - */ - decodeUnknownOption, - /** - * @throws `ParseError` - * @category decoding - * @since 0.67.0 - */ - decodeUnknownSync, - /** - * @category encoding - * @since 0.67.0 - */ - encodeOption, - /** - * @throws `ParseError` - * @category encoding - * @since 0.67.0 - */ - encodeSync, - /** - * @category encoding - * @since 0.67.0 - */ - encodeUnknownOption, - /** - * @throws `ParseError` - * @category encoding - * @since 0.67.0 - */ - encodeUnknownSync, - /** - * By default the option `exact` is set to `true`. - * - * @category validation - * @since 0.67.0 - */ - is, - /** - * @category validation - * @since 0.67.0 - */ - validateOption, - /** - * @throws `ParseError` - * @category validation - * @since 0.67.0 - */ - validateSync -} from "./ParseResult.js" -/* c8 ignore end */ - -/** - * @category encoding - * @since 0.67.0 - */ -export const encodeUnknown = ( - schema: Schema, - options?: ParseOptions -) => { - const encodeUnknown = ParseResult.encodeUnknown(schema, options) - return (u: unknown, overrideOptions?: ParseOptions): Effect.Effect => - ParseResult.mapError(encodeUnknown(u, overrideOptions), ParseResult.parseError) -} - -/** - * @category encoding - * @since 0.67.0 - */ -export const encodeUnknownEither = ( - schema: Schema, - options?: ParseOptions -) => { - const encodeUnknownEither = ParseResult.encodeUnknownEither(schema, options) - return (u: unknown, overrideOptions?: ParseOptions): either_.Either => - either_.mapLeft(encodeUnknownEither(u, overrideOptions), ParseResult.parseError) -} - -/** - * @category encoding - * @since 0.67.0 - */ -export const encodeUnknownPromise = ( - schema: Schema, - options?: ParseOptions -) => { - const parser = encodeUnknown(schema, options) - return (u: unknown, overrideOptions?: ParseOptions): Promise => Effect.runPromise(parser(u, overrideOptions)) -} - -/** - * @category encoding - * @since 0.67.0 - */ -export const encode: ( - schema: Schema, - options?: ParseOptions -) => (a: A, overrideOptions?: ParseOptions) => Effect.Effect = encodeUnknown - -/** - * @category encoding - * @since 0.67.0 - */ -export const encodeEither: ( - schema: Schema, - options?: ParseOptions -) => (a: A, overrideOptions?: ParseOptions) => either_.Either = encodeUnknownEither - -/** - * @category encoding - * @since 0.67.0 - */ -export const encodePromise: ( - schema: Schema, - options?: ParseOptions -) => (a: A, overrideOptions?: ParseOptions) => Promise = encodeUnknownPromise - -/** - * @category decoding - * @since 0.67.0 - */ -export const decodeUnknown = ( - schema: Schema, - options?: ParseOptions -) => { - const decodeUnknown = ParseResult.decodeUnknown(schema, options) - return (u: unknown, overrideOptions?: ParseOptions): Effect.Effect => - ParseResult.mapError(decodeUnknown(u, overrideOptions), ParseResult.parseError) -} - -/** - * @category decoding - * @since 0.67.0 - */ -export const decodeUnknownEither = ( - schema: Schema, - options?: ParseOptions -) => { - const decodeUnknownEither = ParseResult.decodeUnknownEither(schema, options) - return (u: unknown, overrideOptions?: ParseOptions): either_.Either => - either_.mapLeft(decodeUnknownEither(u, overrideOptions), ParseResult.parseError) -} - -/** - * @category decoding - * @since 0.67.0 - */ -export const decodeUnknownPromise = ( - schema: Schema, - options?: ParseOptions -) => { - const parser = decodeUnknown(schema, options) - return (u: unknown, overrideOptions?: ParseOptions): Promise => Effect.runPromise(parser(u, overrideOptions)) -} - -/** - * @category decoding - * @since 0.67.0 - */ -export const decode: ( - schema: Schema, - options?: ParseOptions -) => (i: I, overrideOptions?: ParseOptions) => Effect.Effect = decodeUnknown - -/** - * @category decoding - * @since 0.67.0 - */ -export const decodeEither: ( - schema: Schema, - options?: ParseOptions -) => (i: I, overrideOptions?: ParseOptions) => either_.Either = decodeUnknownEither - -/** - * @category decoding - * @since 0.67.0 - */ -export const decodePromise: ( - schema: Schema, - options?: ParseOptions -) => (i: I, overrideOptions?: ParseOptions) => Promise = decodeUnknownPromise - -/** - * @category validation - * @since 0.67.0 - */ -export const validate = ( - schema: Schema, - options?: ParseOptions -) => { - const validate = ParseResult.validate(schema, options) - return (u: unknown, overrideOptions?: ParseOptions): Effect.Effect => - ParseResult.mapError(validate(u, overrideOptions), ParseResult.parseError) -} - -/** - * @category validation - * @since 0.67.0 - */ -export const validateEither = ( - schema: Schema, - options?: ParseOptions -) => { - const validateEither = ParseResult.validateEither(schema, options) - return (u: unknown, overrideOptions?: ParseOptions): either_.Either => - either_.mapLeft(validateEither(u, overrideOptions), ParseResult.parseError) -} - -/** - * @category validation - * @since 0.67.0 - */ -export const validatePromise = ( - schema: Schema, - options?: ParseOptions -) => { - const parser = validate(schema, options) - return (u: unknown, overrideOptions?: ParseOptions): Promise => Effect.runPromise(parser(u, overrideOptions)) -} - -/** - * Tests if a value is a `Schema`. - * - * @category guards - * @since 0.67.0 - */ -export const isSchema = (u: unknown): u is Schema.Any => - Predicate.hasProperty(u, TypeId) && Predicate.isObject(u[TypeId]) - -/** - * @category api interface - * @since 0.67.0 - */ -export interface Literal> - extends AnnotableClass, Literals[number]> -{ - readonly literals: Readonly -} - -const getDefaultLiteralAST = >( - literals: Literals -) => - AST.isMembers(literals) - ? AST.Union.make(AST.mapMembers(literals, (literal) => new AST.Literal(literal))) - : new AST.Literal(literals[0]) - -const makeLiteralClass = >( - literals: Literals, - ast: AST.AST = getDefaultLiteralAST(literals) -): Literal => - class LiteralClass extends make(ast) { - static override annotations(annotations: Annotations.Schema): Literal { - return makeLiteralClass(this.literals, mergeSchemaAnnotations(this.ast, annotations)) - } - static literals = [...literals] as Literals - } - -/** - * @category constructors - * @since 0.67.0 - */ -export function Literal>( - ...literals: Literals -): Literal -export function Literal(): Never -export function Literal>( - ...literals: Literals -): Schema -export function Literal>( - ...literals: Literals -): Schema | Never { - return array_.isNonEmptyReadonlyArray(literals) ? makeLiteralClass(literals) : Never -} - -/** - * Creates a new `Schema` from a literal schema. - * - * @example - * import * as S from "@effect/schema/Schema" - * import { Either } from "effect" - * - * const schema = S.Literal("a", "b", "c").pipe(S.pickLiteral("a", "b")) - * - * assert.deepStrictEqual(S.decodeSync(schema)("a"), "a") - * assert.deepStrictEqual(S.decodeSync(schema)("b"), "b") - * assert.strictEqual(Either.isLeft(S.decodeUnknownEither(schema)("c")), true) - * - * @category constructors - * @since 0.67.0 - */ -export const pickLiteral = - >(...literals: L) => - (_schema: Schema): Literal<[...L]> => Literal(...literals) - -/** - * @category constructors - * @since 0.67.0 - */ -export const UniqueSymbolFromSelf = (symbol: S): SchemaClass => make(new AST.UniqueSymbol(symbol)) - -/** - * @category api interface - * @since 0.67.0 - */ -export interface Enums extends AnnotableClass, A[keyof A]> { - readonly enums: A -} - -/** - * @since 0.67.0 - */ -export type EnumsDefinition = { [x: string]: string | number } - -const getDefaultEnumsAST = (enums: A) => - new AST.Enums( - Object.keys(enums).filter( - (key) => typeof enums[enums[key]] !== "number" - ).map((key) => [key, enums[key]]) - ) - -const makeEnumsClass = ( - enums: A, - ast: AST.AST = getDefaultEnumsAST(enums) -): Enums => - class EnumsClass extends make(ast) { - static override annotations(annotations: Annotations.Schema) { - return makeEnumsClass(this.enums, mergeSchemaAnnotations(this.ast, annotations)) - } - - static enums = { ...enums } - } - -/** - * @category constructors - * @since 0.67.0 - */ -export const Enums = (enums: A): Enums => makeEnumsClass(enums) - -type Join = Params extends [infer Head, ...infer Tail] ? - `${(Head extends Schema ? A : Head) & (AST.LiteralValue)}${Join}` - : "" - -/** - * @category API interface - * @since 0.67.17 - */ -export interface TemplateLiteral extends SchemaClass {} - -type TemplateLiteralParameter = Schema.AnyNoContext | AST.LiteralValue - -/** - * @category template literal - * @since 0.67.0 - */ -export const TemplateLiteral = >( - ...[head, ...tail]: Params -): TemplateLiteral> => { - let astOrs: ReadonlyArray = getTemplateLiterals( - getTemplateLiteralParameterAST(head) - ) - for (const span of tail) { - astOrs = array_.flatMap( - astOrs, - (a) => getTemplateLiterals(getTemplateLiteralParameterAST(span)).map((b) => combineTemplateLiterals(a, b)) - ) - } - return make(AST.Union.make(astOrs.map((astOr) => Predicate.isString(astOr) ? new AST.Literal(astOr) : astOr))) -} - -const getTemplateLiteralParameterAST = (span: TemplateLiteralParameter): AST.AST => - isSchema(span) ? span.ast : new AST.Literal(String(span)) - -const combineTemplateLiterals = ( - a: AST.TemplateLiteral | string, - b: AST.TemplateLiteral | string -): AST.TemplateLiteral | string => { - if (Predicate.isString(a)) { - return Predicate.isString(b) ? - a + b : - new AST.TemplateLiteral(a + b.head, b.spans) - } - if (Predicate.isString(b)) { - return new AST.TemplateLiteral( - a.head, - array_.modifyNonEmptyLast( - a.spans, - (span) => new AST.TemplateLiteralSpan(span.type, span.literal + b) - ) - ) - } - return new AST.TemplateLiteral( - a.head, - array_.appendAll( - array_.modifyNonEmptyLast( - a.spans, - (span) => new AST.TemplateLiteralSpan(span.type, span.literal + String(b.head)) - ), - b.spans - ) - ) -} - -const getTemplateLiterals = ( - ast: AST.AST -): ReadonlyArray => { - switch (ast._tag) { - case "Literal": - return [String(ast.literal)] - case "NumberKeyword": - case "StringKeyword": - return [new AST.TemplateLiteral("", [new AST.TemplateLiteralSpan(ast, "")])] - case "Union": - return array_.flatMap(ast.types, getTemplateLiterals) - } - throw new Error(errors_.getSchemaUnsupportedLiteralSpanErrorMessage(ast)) -} - -type TemplateLiteralParserParameters = Schema.Any | AST.LiteralValue - -type TemplateLiteralParserParametersType = T extends [infer Head, ...infer Tail] ? - readonly [Head extends Schema ? A : Head, ...TemplateLiteralParserParametersType] - : [] - -type TemplateLiteralParserParametersEncoded = T extends [infer Head, ...infer Tail] ? `${ - & (Head extends Schema ? I : Head) - & (AST.LiteralValue)}${TemplateLiteralParserParametersEncoded}` - : "" - -/** - * @category API interface - * @since 0.70.1 - */ -export interface TemplateLiteralParser> - extends - Schema< - TemplateLiteralParserParametersType, - TemplateLiteralParserParametersEncoded, - Schema.Context - > -{ - readonly params: Params -} - -/** - * @category template literal - * @since 0.70.1 - */ -export const TemplateLiteralParser = >( - ...params: Params -): TemplateLiteralParser => { - const encodedSchemas: Array = [] - const typeSchemas: Array = [] - const numbers: Array = [] - for (let i = 0; i < params.length; i++) { - const p = params[i] - if (isSchema(p)) { - const encoded = encodedSchema(p) - if (AST.isNumberKeyword(encoded.ast)) { - numbers.push(i) - } - encodedSchemas.push(encoded) - typeSchemas.push(p) - } else { - const literal = Literal(p) - encodedSchemas.push(literal) - typeSchemas.push(literal) - } - } - const from = TemplateLiteral(...encodedSchemas as any) - const re = AST.getTemplateLiteralCapturingRegExp(from.ast as AST.TemplateLiteral) - return class TemplateLiteralParserClass extends transform(from, Tuple(...typeSchemas), { - strict: false, - decode: (s) => { - const out: Array = re.exec(s)!.slice(1, params.length + 1) - for (let i = 0; i < numbers.length; i++) { - const index = numbers[i] - out[index] = Number(out[index]) - } - return out - }, - encode: (tuple) => tuple.join("") - }) { - static params = params.slice() - } as any -} - -const declareConstructor = < - const TypeParameters extends ReadonlyArray, - I, - A ->( - typeParameters: TypeParameters, - options: { - readonly decode: ( - ...typeParameters: { - readonly [K in keyof TypeParameters]: Schema< - Schema.Type, - Schema.Encoded, - never - > - } - ) => ( - input: unknown, - options: ParseOptions, - ast: AST.Declaration - ) => Effect.Effect - readonly encode: ( - ...typeParameters: { - readonly [K in keyof TypeParameters]: Schema< - Schema.Type, - Schema.Encoded, - never - > - } - ) => ( - input: unknown, - options: ParseOptions, - ast: AST.Declaration - ) => Effect.Effect - }, - annotations?: Annotations.Schema -): SchemaClass> => - make( - new AST.Declaration( - typeParameters.map((tp) => tp.ast), - (...typeParameters) => options.decode(...typeParameters.map(make) as any), - (...typeParameters) => options.encode(...typeParameters.map(make) as any), - toASTAnnotations(annotations) - ) - ) - -const declarePrimitive = ( - is: (input: unknown) => input is A, - annotations?: Annotations.Schema -): SchemaClass => { - const decodeUnknown = () => (input: unknown, _: ParseOptions, ast: AST.Declaration) => - is(input) ? ParseResult.succeed(input) : ParseResult.fail(new ParseResult.Type(ast, input)) - const encodeUnknown = decodeUnknown - return make(new AST.Declaration([], decodeUnknown, encodeUnknown, toASTAnnotations(annotations))) -} - -/** - * The constraint `R extends Schema.Context` enforces dependencies solely from `typeParameters`. - * This ensures that when you call `Schema.to` or `Schema.from`, you receive a schema with a `never` context. - * - * @category constructors - * @since 0.67.0 - */ -export const declare: { - ( - is: (input: unknown) => input is A, - annotations?: Annotations.Schema - ): SchemaClass - , I, A>( - typeParameters: P, - options: { - readonly decode: ( - ...typeParameters: { readonly [K in keyof P]: Schema, Schema.Encoded, never> } - ) => ( - input: unknown, - options: ParseOptions, - ast: AST.Declaration - ) => Effect.Effect - readonly encode: ( - ...typeParameters: { readonly [K in keyof P]: Schema, Schema.Encoded, never> } - ) => ( - input: unknown, - options: ParseOptions, - ast: AST.Declaration - ) => Effect.Effect - }, - annotations?: Annotations.Schema }> - ): SchemaClass> -} = function() { - if (Array.isArray(arguments[0])) { - const typeParameters = arguments[0] - const options = arguments[1] - const annotations = arguments[2] - return declareConstructor(typeParameters, options, annotations) - } - const is = arguments[0] - const annotations = arguments[1] - return declarePrimitive(is, annotations) -} as any - -/** - * @category type id - * @since 0.67.0 - */ -export const BrandTypeId: unique symbol = Symbol.for("@effect/schema/TypeId/Brand") - -/** - * @category constructors - * @since 0.67.0 - */ -export const fromBrand = , A extends Brand.Unbranded>( - constructor: Brand.Constructor, - annotations?: Annotations.Filter -) => -(self: Schema): BrandSchema => - makeBrandClass, string | symbol>( - new AST.Refinement( - self.ast, - function predicate(a: A, _: ParseOptions, ast: AST.AST): option_.Option { - const either = constructor.either(a) - return either_.isLeft(either) ? - option_.some(new ParseResult.Type(ast, a, either.left.map((v) => v.message).join(", "))) : - option_.none() - }, - toASTAnnotations({ typeId: { id: BrandTypeId, annotation: { constructor } }, ...annotations }) - ) - ) - -/** - * @category type id - * @since 0.67.0 - */ -export const InstanceOfTypeId: unique symbol = Symbol.for("@effect/schema/TypeId/InstanceOf") - -/** - * @category api interface - * @since 0.67.0 - */ -export interface instanceOf extends AnnotableClass, A> {} - -/** - * @category constructors - * @since 0.67.0 - */ -export const instanceOf = any>( - constructor: A, - annotations?: Annotations.Schema> -): instanceOf> => - declare( - (u): u is InstanceType => u instanceof constructor, - { - title: constructor.name, - description: `an instance of ${constructor.name}`, - pretty: (): pretty_.Pretty> => String, - typeId: { id: InstanceOfTypeId, annotation: { constructor } }, - ...annotations - } - ) - -/** - * @category primitives - * @since 0.67.0 - */ -export class Undefined extends make(AST.undefinedKeyword) {} - -/** - * @category primitives - * @since 0.67.0 - */ -export class Void extends make(AST.voidKeyword) {} - -/** - * @category primitives - * @since 0.67.0 - */ -export class Null extends make(AST.null) {} - -/** - * @category primitives - * @since 0.67.0 - */ -export class Never extends make(AST.neverKeyword) {} - -/** - * @category primitives - * @since 0.67.0 - */ -export class Unknown extends make(AST.unknownKeyword) {} - -/** - * @category primitives - * @since 0.67.0 - */ -export class Any extends make(AST.anyKeyword) {} - -/** - * @category primitives - * @since 0.67.0 - */ -export class BigIntFromSelf extends make(AST.bigIntKeyword) {} - -/** - * @category primitives - * @since 0.67.0 - */ -export class SymbolFromSelf extends make(AST.symbolKeyword) {} - -/** @ignore */ -class String$ extends make(AST.stringKeyword) {} - -/** @ignore */ -class Number$ extends make(AST.numberKeyword) {} - -/** @ignore */ -class Boolean$ extends make(AST.booleanKeyword) {} - -/** @ignore */ -class Object$ extends make(AST.objectKeyword) {} - -export { - /** - * @category primitives - * @since 0.67.0 - */ - Boolean$ as Boolean, - /** - * @category primitives - * @since 0.67.0 - */ - Number$ as Number, - /** - * @category primitives - * @since 0.67.0 - */ - Object$ as Object, - /** - * @category primitives - * @since 0.67.0 - */ - String$ as String -} - -/** - * @category api interface - * @since 0.67.0 - */ -export interface Union> extends - AnnotableClass< - Union, - Schema.Type, - Schema.Encoded, - Schema.Context - > -{ - readonly members: Readonly - annotations(annotations: Annotations.Schema>): Union -} - -const getDefaultUnionAST = >(members: Members): AST.AST => - AST.Union.make(members.map((m) => m.ast)) - -const makeUnionClass = >( - members: Members, - ast: AST.AST = getDefaultUnionAST(members) -): Union => - class UnionClass - extends make, Schema.Encoded, Schema.Context>(ast) - { - static override annotations(annotations: Annotations.Schema>): Union { - return makeUnionClass(this.members, mergeSchemaAnnotations(this.ast, annotations)) - } - - static members = [...members] - } - -/** - * @category combinators - * @since 0.67.0 - */ -export function Union>(...members: Members): Union -export function Union(member: Member): Member -export function Union(): typeof Never -export function Union>( - ...members: Members -): Schema, Schema.Encoded, Schema.Context> -export function Union>( - ...members: Members -) { - return AST.isMembers(members) - ? makeUnionClass(members) - : array_.isNonEmptyReadonlyArray(members) - ? members[0] - : Never -} - -/** - * @category api interface - * @since 0.67.0 - */ -export interface NullOr extends Union<[S, typeof Null]> { - annotations(annotations: Annotations.Schema | null>): NullOr -} - -/** - * @category combinators - * @since 0.67.0 - */ -export const NullOr = (self: S): NullOr => Union(self, Null) - -/** - * @category api interface - * @since 0.67.0 - */ -export interface UndefinedOr extends Union<[S, typeof Undefined]> { - annotations(annotations: Annotations.Schema | undefined>): UndefinedOr -} - -/** - * @category combinators - * @since 0.67.0 - */ -export const UndefinedOr = (self: S): UndefinedOr => Union(self, Undefined) - -/** - * @category api interface - * @since 0.67.0 - */ -export interface NullishOr extends Union<[S, typeof Null, typeof Undefined]> { - annotations(annotations: Annotations.Schema | null | undefined>): NullishOr -} - -/** - * @category combinators - * @since 0.67.0 - */ -export const NullishOr = (self: S): NullishOr => Union(self, Null, Undefined) - -/** - * @category combinators - * @since 0.67.0 - */ -export const keyof = (self: Schema): SchemaClass => make(AST.keyof(self.ast)) - -/** - * @since 0.68.0 - */ -export declare namespace Element { - /** - * @since 0.68.0 - */ - export interface Annotations extends Annotations.Doc { - readonly missingMessage?: AST.MissingMessageAnnotation - } - - /** - * @since 0.68.0 - */ - export type Token = "" | "?" -} - -/** - * @category API interface - * @since 0.68.0 - */ -export interface Element - extends Schema.Variance, Schema.Encoded, Schema.Context> -{ - readonly _Token: Token - readonly ast: AST.OptionalType - readonly from: S - annotations(annotations: Element.Annotations>): Element -} - -/** - * @since 0.68.0 - */ -export const element = (self: S): Element => - new ElementImpl(new AST.OptionalType(self.ast, false), self) - -/** - * @since 0.67.0 - */ -export const optionalElement = (self: S): Element => - new ElementImpl(new AST.OptionalType(self.ast, true), self) - -class ElementImpl implements Element { - readonly [TypeId]!: Schema.Variance, Schema.Encoded, Schema.Context>[TypeId] - readonly _Token!: Token - constructor( - readonly ast: AST.OptionalType, - readonly from: S - ) {} - annotations( - annotations: Annotations.Schema> - ): ElementImpl { - return new ElementImpl( - new AST.OptionalType( - this.ast.type, - this.ast.isOptional, - { ...this.ast.annotations, ...toASTAnnotations(annotations) } - ), - this.from - ) - } - toString() { - return `${this.ast.type}${this.ast.isOptional ? "?" : ""}` - } -} - -/** - * @since 0.67.0 - */ -export declare namespace TupleType { - type ElementsType< - Elements, - Out extends ReadonlyArray = readonly [] - > = Elements extends readonly [infer Head, ...infer Tail] ? - Head extends Element ? ElementsType?]> - : ElementsType]> - : Out - - type ElementsEncoded< - Elements, - Out extends ReadonlyArray = readonly [] - > = Elements extends readonly [infer Head, ...infer Tail] ? - Head extends Element ? ElementsEncoded?]> - : ElementsEncoded]> - : Out - - /** - * @since 0.67.0 - */ - export type Elements = ReadonlyArray> - - /** - * @since 0.68.0 - */ - export type Rest = ReadonlyArray> - - /** - * @since 0.67.0 - */ - export type Type = Rest extends - [infer Head, ...infer Tail] ? Readonly<[ - ...ElementsType, - ...ReadonlyArray>, - ...{ readonly [K in keyof Tail]: Schema.Type } - ]> : - ElementsType - - /** - * @since 0.67.0 - */ - export type Encoded = Rest extends - [infer Head, ...infer Tail] ? Readonly<[ - ...ElementsEncoded, - ...ReadonlyArray>, - ...{ readonly [K in keyof Tail]: Schema.Encoded } - ]> : - ElementsEncoded -} - -/** - * @category api interface - * @since 0.67.0 - */ -export interface TupleType extends - AnnotableClass< - TupleType, - TupleType.Type, - TupleType.Encoded, - Schema.Context | Schema.Context - > -{ - readonly elements: Readonly - readonly rest: Readonly -} - -const getDefaultTupleTypeAST = ( - elements: Elements, - rest: Rest -) => - new AST.TupleType( - elements.map((el) => isSchema(el) ? new AST.OptionalType(el.ast, false) : el.ast), - rest.map((el) => isSchema(el) ? new AST.Type(el.ast) : el.ast), - true - ) - -const makeTupleTypeClass = ( - elements: Elements, - rest: Rest, - ast: AST.AST = getDefaultTupleTypeAST(elements, rest) -) => - class TupleTypeClass extends make< - TupleType.Type, - TupleType.Encoded, - Schema.Context | Schema.Context - >(ast) { - static override annotations( - annotations: Annotations.Schema> - ): TupleType { - return makeTupleTypeClass(this.elements, this.rest, mergeSchemaAnnotations(this.ast, annotations)) - } - - static elements = [...elements] as any as Elements - - static rest = [...rest] as any as Rest - } - -/** - * @category api interface - * @since 0.67.0 - */ -export interface Tuple extends TupleType { - annotations(annotations: Annotations.Schema>): Tuple -} - -/** - * @category constructors - * @since 0.67.0 - */ -export function Tuple< - const Elements extends TupleType.Elements, - Rest extends array_.NonEmptyReadonlyArray ->(elements: Elements, ...rest: Rest): TupleType -export function Tuple(...elements: Elements): Tuple -export function Tuple(...args: ReadonlyArray): any { - return Array.isArray(args[0]) - ? makeTupleTypeClass(args[0], args.slice(1)) - : makeTupleTypeClass(args, []) -} - -/** - * @category api interface - * @since 0.67.0 - */ -export interface Array$ extends TupleType<[], [Value]> { - readonly value: Value - annotations(annotations: Annotations.Schema>): Array$ -} - -const makeArrayClass = (value: Value, ast?: AST.AST): Array$ => - class ArrayClass extends makeTupleTypeClass<[], [Value]>([], [value], ast) { - static override annotations(annotations: Annotations.Schema>) { - return makeArrayClass(this.value, mergeSchemaAnnotations(this.ast, annotations)) - } - - static value = value - } - -const Array$ = (value: Value): Array$ => makeArrayClass(value) - -export { - /** - * @category constructors - * @since 0.67.0 - */ - Array$ as Array -} - -/** - * @category api interface - * @since 0.67.0 - */ -export interface NonEmptyArray extends TupleType<[Value], [Value]> { - readonly value: Value - annotations(annotations: Annotations.Schema>): NonEmptyArray -} - -const makeNonEmptyArrayClass = (value: Value, ast?: AST.AST): NonEmptyArray => - class NonEmptyArrayClass extends makeTupleTypeClass<[Value], [Value]>([value], [value], ast) { - static override annotations(annotations: Annotations.Schema>) { - return makeNonEmptyArrayClass(this.value, mergeSchemaAnnotations(this.ast, annotations)) - } - - static value = value - } - -/** - * @category constructors - * @since 0.67.0 - */ -export const NonEmptyArray = (value: Value): NonEmptyArray => - makeNonEmptyArrayClass(value) - -/** - * @category api interface - * @since 0.71.0 - */ -export interface ArrayEnsure extends - AnnotableClass< - ArrayEnsure, - ReadonlyArray>, - Schema.Encoded | ReadonlyArray>, - Schema.Context - > -{} - -/** - * @category constructors - * @since 0.71.0 - */ -export const ArrayEnsure = (value: Value): ArrayEnsure => { - const value_ = asSchema(value) - return class ArrayEnsureClass extends transform(Union(value_, Array$(value_)), Array$(typeSchema(value_)), { - strict: true, - decode: array_.ensure, - encode: (arr) => arr.length === 1 ? arr[0] : arr - }) {} -} - -/** - * @category api interface - * @since 0.71.0 - */ -export interface NonEmptyArrayEnsure extends - AnnotableClass< - NonEmptyArrayEnsure, - array_.NonEmptyReadonlyArray>, - Schema.Encoded | array_.NonEmptyReadonlyArray>, - Schema.Context - > -{} - -/** - * @category constructors - * @since 0.71.0 - */ -export const NonEmptyArrayEnsure = (value: Value): NonEmptyArrayEnsure => { - const value_ = asSchema(value) - return class NonEmptyArrayEnsureClass - extends transform(Union(value_, NonEmptyArray(value_)), NonEmptyArray(typeSchema(value_)), { - strict: true, - decode: array_.ensure as any, - encode: (arr) => arr.length === 1 ? arr[0] : arr - }) - {} -} - -/** - * @since 0.67.0 - */ -export declare namespace PropertySignature { - /** - * @since 0.67.0 - */ - export type Token = "?:" | ":" - - /** - * @since 0.67.0 - */ - export type Any = PropertySignature< - Token, - any, - Key, - Token, - any, - boolean, - unknown - > - - /** - * @since 0.67.0 - */ - export type All = - | Any - | PropertySignature - | PropertySignature - | PropertySignature - - /** - * @since 0.67.0 - */ - export type AST = - | PropertySignatureDeclaration - | PropertySignatureTransformation - - /** - * @since 0.67.0 - */ - export interface Annotations extends Annotations.Doc { - readonly missingMessage?: AST.MissingMessageAnnotation - } -} - -const formatPropertySignatureToken = (isOptional: boolean): string => isOptional ? "\"?:\"" : "\":\"" - -/** - * @category PropertySignature - * @since 0.67.0 - */ -export class PropertySignatureDeclaration extends AST.OptionalType { - /** - * @since 0.67.0 - */ - readonly _tag = "PropertySignatureDeclaration" - constructor( - type: AST.AST, - isOptional: boolean, - readonly isReadonly: boolean, - annotations: AST.Annotations, - readonly defaultValue: (() => unknown) | undefined - ) { - super(type, isOptional, annotations) - } - /** - * @since 0.67.0 - */ - toString() { - const token = formatPropertySignatureToken(this.isOptional) - const type = String(this.type) - return `PropertySignature<${token}, ${type}, never, ${token}, ${type}>` - } -} - -/** - * @category PropertySignature - * @since 0.67.0 - */ -export class FromPropertySignature extends AST.OptionalType { - constructor( - type: AST.AST, - isOptional: boolean, - readonly isReadonly: boolean, - annotations: AST.Annotations, - readonly fromKey?: PropertyKey | undefined - ) { - super(type, isOptional, annotations) - } -} - -/** - * @category PropertySignature - * @since 0.67.0 - */ -export class ToPropertySignature extends AST.OptionalType { - constructor( - type: AST.AST, - isOptional: boolean, - readonly isReadonly: boolean, - annotations: AST.Annotations, - readonly defaultValue: (() => unknown) | undefined - ) { - super(type, isOptional, annotations) - } -} - -const formatPropertyKey = (p: PropertyKey | undefined): string => { - if (p === undefined) { - return "never" - } - if (Predicate.isString(p)) { - return JSON.stringify(p) - } - return String(p) -} - -/** - * @category PropertySignature - * @since 0.67.0 - */ -export class PropertySignatureTransformation { - /** - * @since 0.67.0 - */ - readonly _tag = "PropertySignatureTransformation" - constructor( - readonly from: FromPropertySignature, - readonly to: ToPropertySignature, - readonly decode: AST.PropertySignatureTransformation["decode"], - readonly encode: AST.PropertySignatureTransformation["encode"] - ) {} - /** - * @since 0.67.0 - */ - toString() { - return `PropertySignature<${formatPropertySignatureToken(this.to.isOptional)}, ${this.to.type}, ${ - formatPropertyKey(this.from.fromKey) - }, ${formatPropertySignatureToken(this.from.isOptional)}, ${this.from.type}>` - } -} - -const mergeSignatureAnnotations = ( - ast: PropertySignature.AST, - annotations: AST.Annotations -): PropertySignature.AST => { - switch (ast._tag) { - case "PropertySignatureDeclaration": { - return new PropertySignatureDeclaration( - ast.type, - ast.isOptional, - ast.isReadonly, - { ...ast.annotations, ...annotations }, - ast.defaultValue - ) - } - case "PropertySignatureTransformation": { - return new PropertySignatureTransformation( - new FromPropertySignature( - ast.from.type, - ast.from.isOptional, - ast.from.isReadonly, - ast.from.annotations - ), - new ToPropertySignature(ast.to.type, ast.to.isOptional, ast.to.isReadonly, { - ...ast.to.annotations, - ...annotations - }, ast.to.defaultValue), - ast.decode, - ast.encode - ) - } - } -} - -/** - * @since 0.68.0 - * @category symbol - */ -export const PropertySignatureTypeId: unique symbol = Symbol.for("@effect/schema/PropertySignature") - -/** - * @since 0.68.0 - * @category symbol - */ -export type PropertySignatureTypeId = typeof PropertySignatureTypeId - -/** - * @since 0.69.3 - * @category guards - */ -export const isPropertySignature = (u: unknown): u is PropertySignature.All => - Predicate.hasProperty(u, PropertySignatureTypeId) - -/** - * @category PropertySignature - * @since 0.67.0 - */ -export interface PropertySignature< - TypeToken extends PropertySignature.Token, - Type, - Key extends PropertyKey, - EncodedToken extends PropertySignature.Token, - Encoded, - HasDefault extends boolean = false, - R = never -> extends Schema.Variance, Pipeable { - readonly [PropertySignatureTypeId]: null - readonly _TypeToken: TypeToken - readonly _EncodedToken: EncodedToken - readonly _HasDefault: HasDefault - readonly _Key: Key - readonly ast: PropertySignature.AST - - annotations( - annotations: PropertySignature.Annotations - ): PropertySignature -} - -class PropertySignatureImpl< - TypeToken extends PropertySignature.Token, - Type, - Key extends PropertyKey, - EncodedToken extends PropertySignature.Token, - Encoded, - HasDefault extends boolean = false, - R = never -> implements PropertySignature { - readonly [TypeId]!: Schema.Variance[TypeId] - readonly [PropertySignatureTypeId] = null - readonly _TypeToken!: TypeToken - readonly _Key!: Key - readonly _EncodedToken!: EncodedToken - readonly _HasDefault!: HasDefault - - constructor( - readonly ast: PropertySignature.AST - ) {} - - pipe() { - return pipeArguments(this, arguments) - } - - annotations( - annotations: PropertySignature.Annotations - ): PropertySignature { - return new PropertySignatureImpl(mergeSignatureAnnotations(this.ast, toASTAnnotations(annotations))) - } - - toString() { - return String(this.ast) - } -} - -/** - * @category PropertySignature - * @since 0.67.15 - */ -export const makePropertySignature = < - TypeToken extends PropertySignature.Token, - Type, - Key extends PropertyKey, - EncodedToken extends PropertySignature.Token, - Encoded, - HasDefault extends boolean = false, - R = never ->(ast: PropertySignature.AST) => - new PropertySignatureImpl(ast) - -class PropertySignatureWithFromImpl< - From extends Schema.All, - TypeToken extends PropertySignature.Token, - Type, - Key extends PropertyKey, - EncodedToken extends PropertySignature.Token, - Encoded, - HasDefault extends boolean = false, - R = never -> extends PropertySignatureImpl { - constructor(ast: PropertySignature.AST, readonly from: From) { - super(ast) - } - annotations( - annotations: PropertySignature.Annotations - ): PropertySignatureWithFromImpl { - return new PropertySignatureWithFromImpl( - mergeSignatureAnnotations(this.ast, toASTAnnotations(annotations)), - this.from - ) - } -} - -/** - * @category API interface - * @since 1.0.0 - */ -export interface propertySignature - extends PropertySignature<":", Schema.Type, never, ":", Schema.Encoded, false, Schema.Context> -{ - readonly from: S - annotations(annotations: PropertySignature.Annotations>): propertySignature -} - -/** - * Lifts a `Schema` into a `PropertySignature`. - * - * @category PropertySignature - * @since 0.67.0 - */ -export const propertySignature = ( - self: S -): propertySignature => - new PropertySignatureWithFromImpl( - new PropertySignatureDeclaration(self.ast, false, true, {}, undefined), - self - ) - -/** - * Enhances a property signature with a default constructor value. - * - * @category PropertySignature - * @since 0.67.0 - */ -export const withConstructorDefault: { - (defaultValue: () => Types.NoInfer): < - TypeToken extends PropertySignature.Token, - Key extends PropertyKey, - EncodedToken extends PropertySignature.Token, - Encoded, - R - >( - self: PropertySignature - ) => PropertySignature - < - TypeToken extends PropertySignature.Token, - Type, - Key extends PropertyKey, - EncodedToken extends PropertySignature.Token, - Encoded, - R - >( - self: PropertySignature, - defaultValue: () => Types.NoInfer - ): PropertySignature -} = dual(2, < - TypeToken extends PropertySignature.Token, - Type, - Key extends PropertyKey, - EncodedToken extends PropertySignature.Token, - Encoded, - R ->( - self: PropertySignature, - defaultValue: () => Types.NoInfer -): PropertySignature => { - const ast = self.ast - switch (ast._tag) { - case "PropertySignatureDeclaration": - return makePropertySignature( - new PropertySignatureDeclaration(ast.type, ast.isOptional, ast.isReadonly, ast.annotations, defaultValue) - ) - case "PropertySignatureTransformation": - return makePropertySignature( - new PropertySignatureTransformation( - ast.from, - new ToPropertySignature(ast.to.type, ast.to.isOptional, ast.to.isReadonly, ast.to.annotations, defaultValue), - ast.decode, - ast.encode - ) - ) - } -}) - -const applyDefaultValue = (o: option_.Option, defaultValue: () => A) => - option_.match(o, { - onNone: () => option_.some(defaultValue()), - onSome: (value) => option_.some(value === undefined ? defaultValue() : value) - }) - -/** - * Enhances a property signature with a default decoding value. - * - * @category PropertySignature - * @since 0.67.0 - */ -export const withDecodingDefault: { - (defaultValue: () => Types.NoInfer): < - Key extends PropertyKey, - Encoded, - HasDefault extends boolean, - R - >( - self: PropertySignature<"?:", Type, Key, "?:", Encoded, HasDefault, R> - ) => PropertySignature<":", Exclude, Key, "?:", Encoded, HasDefault, R> - < - Type, - Key extends PropertyKey, - Encoded, - HasDefault extends boolean, - R - >( - self: PropertySignature<"?:", Type, Key, "?:", Encoded, HasDefault, R>, - defaultValue: () => Types.NoInfer - ): PropertySignature<":", Exclude, Key, "?:", Encoded, HasDefault, R> -} = dual(2, < - Type, - Key extends PropertyKey, - Encoded, - R ->( - self: PropertySignature<"?:", Type, Key, "?:", Encoded, boolean, R>, - defaultValue: () => Types.NoInfer -): PropertySignature<":", Exclude, Key, "?:", Encoded, true, R> => { - const ast = self.ast - switch (ast._tag) { - case "PropertySignatureDeclaration": - return makePropertySignature( - new PropertySignatureTransformation( - ast, - new ToPropertySignature(AST.typeAST(ast.type), false, true, {}, undefined), - (o) => applyDefaultValue(o, defaultValue), - identity - ) - ) - case "PropertySignatureTransformation": - return makePropertySignature( - new PropertySignatureTransformation( - ast.from, - new ToPropertySignature(ast.to.type, false, ast.to.isReadonly, ast.to.annotations, ast.to.defaultValue), - (o) => applyDefaultValue(ast.decode(o), defaultValue), - ast.encode - ) - ) - } -}) - -/** - * Enhances a property signature with a default decoding value and a default constructor value. - * - * @category PropertySignature - * @since 0.67.0 - */ -export const withDefaults: { - (defaults: { - constructor: () => Types.NoInfer> - decoding: () => Types.NoInfer - }): < - Key extends PropertyKey, - Encoded, - R - >( - self: PropertySignature<"?:", Type, Key, "?:", Encoded, boolean, R> - ) => PropertySignature<":", Exclude, Key, "?:", Encoded, true, R> - < - Type, - Key extends PropertyKey, - Encoded, - R - >( - self: PropertySignature<"?:", Type, Key, "?:", Encoded, boolean, R>, - defaults: { - constructor: () => Types.NoInfer> - decoding: () => Types.NoInfer - } - ): PropertySignature<":", Exclude, Key, "?:", Encoded, true, R> -} = dual(2, < - Type, - Key extends PropertyKey, - Encoded, - R ->( - self: PropertySignature<"?:", Type, Key, "?:", Encoded, boolean, R>, - defaults: { - constructor: () => Types.NoInfer> - decoding: () => Types.NoInfer - } -): PropertySignature<":", Exclude, Key, "?:", Encoded, true, R> => - self.pipe(withDecodingDefault(defaults.decoding), withConstructorDefault(defaults.constructor))) - -/** - * Enhances a property signature by specifying a different key for it in the Encoded type. - * - * @category PropertySignature - * @since 0.67.0 - */ -export const fromKey: { - (key: Key): < - TypeToken extends PropertySignature.Token, - Type, - EncodedToken extends PropertySignature.Token, - Encoded, - HasDefault extends boolean, - R - >( - self: PropertySignature - ) => PropertySignature - < - Type, - TypeToken extends PropertySignature.Token, - Encoded, - EncodedToken extends PropertySignature.Token, - HasDefault extends boolean, - R, - Key extends PropertyKey - >( - self: PropertySignature, - key: Key - ): PropertySignature -} = dual(2, < - Type, - TypeToken extends PropertySignature.Token, - Encoded, - EncodedToken extends PropertySignature.Token, - HasDefault extends boolean, - R, - Key extends PropertyKey ->( - self: PropertySignature, - key: Key -): PropertySignature => { - const ast = self.ast - switch (ast._tag) { - case "PropertySignatureDeclaration": { - return makePropertySignature( - new PropertySignatureTransformation( - new FromPropertySignature( - ast.type, - ast.isOptional, - ast.isReadonly, - ast.annotations, - key - ), - new ToPropertySignature(AST.typeAST(ast.type), ast.isOptional, ast.isReadonly, {}, ast.defaultValue), - identity, - identity - ) - ) - } - case "PropertySignatureTransformation": - return makePropertySignature( - new PropertySignatureTransformation( - new FromPropertySignature( - ast.from.type, - ast.from.isOptional, - ast.from.isReadonly, - ast.from.annotations, - key - ), - ast.to, - ast.decode, - ast.encode - ) - ) - } -}) - -/** - * Converts an optional property to a required one through a transformation `Option -> Type`. - * - * - `decode`: `none` as argument means the value is missing in the input. - * - `encode`: `none` as return value means the value will be missing in the output. - * - * @category PropertySignature - * @since 0.67.0 - */ -export const optionalToRequired = ( - from: Schema, - to: Schema, - options: { - readonly decode: (o: option_.Option) => TI - readonly encode: (ti: TI) => option_.Option - } -): PropertySignature<":", TA, never, "?:", FI, false, FR | TR> => - makePropertySignature( - new PropertySignatureTransformation( - new FromPropertySignature(from.ast, true, true, {}, undefined), - new ToPropertySignature(to.ast, false, true, {}, undefined), - (o) => option_.some(options.decode(o)), - option_.flatMap(options.encode) - ) - ) - -/** - * Converts an optional property to a required one through a transformation `Type -> Option`. - * - * - `decode`: `none` as return value means the value will be missing in the output. - * - `encode`: `none` as argument means the value is missing in the input. - * - * @category PropertySignature - * @since 0.67.15 - */ -export const requiredToOptional = ( - from: Schema, - to: Schema, - options: { - readonly decode: (fa: FA) => option_.Option - readonly encode: (o: option_.Option) => FA - } -): PropertySignature<"?:", TA, never, ":", FI, false, FR | TR> => - makePropertySignature( - new PropertySignatureTransformation( - new FromPropertySignature(from.ast, false, true, {}, undefined), - new ToPropertySignature(to.ast, true, true, {}, undefined), - option_.flatMap(options.decode), - (o) => option_.some(options.encode(o)) - ) - ) - -/** - * Converts an optional property to another optional property through a transformation `Option -> Option`. - * - * - `decode`: - * - `none` as argument means the value is missing in the input. - * - `none` as return value means the value will be missing in the output. - * - `encode`: - * - `none` as argument means the value is missing in the input. - * - `none` as return value means the value will be missing in the output. - * - * @category PropertySignature - * @since 0.67.0 - */ -export const optionalToOptional = ( - from: Schema, - to: Schema, - options: { - readonly decode: (o: option_.Option) => option_.Option - readonly encode: (o: option_.Option) => option_.Option - } -): PropertySignature<"?:", TA, never, "?:", FI, false, FR | TR> => - makePropertySignature( - new PropertySignatureTransformation( - new FromPropertySignature(from.ast, true, true, {}, undefined), - new ToPropertySignature(to.ast, true, true, {}, undefined), - options.decode, - options.encode - ) - ) - -/** - * @since 0.67.0 - */ -export type OptionalOptions = { - readonly default?: never - readonly as?: never - readonly exact?: true - readonly nullable?: true -} | { - readonly default: LazyArg - readonly as?: never - readonly exact?: true - readonly nullable?: true -} | { - readonly as: "Option" - readonly default?: never - readonly exact?: never - readonly nullable?: never - readonly onNoneEncoding?: LazyArg> -} | { - readonly as: "Option" - readonly default?: never - readonly exact?: never - readonly nullable: true - readonly onNoneEncoding?: LazyArg> -} | { - readonly as: "Option" - readonly default?: never - readonly exact: true - readonly nullable?: never - readonly onNoneEncoding?: never -} | { - readonly as: "Option" - readonly default?: never - readonly exact: true - readonly nullable: true - readonly onNoneEncoding?: LazyArg> -} | undefined - -/** - * @category api interface - * @since 0.67.10 - */ -export interface optional extends - PropertySignature< - "?:", - Schema.Type | undefined, - never, - "?:", - Schema.Encoded | undefined, - false, - Schema.Context - > -{ - readonly from: S - annotations(annotations: PropertySignature.Annotations | undefined>): optional -} - -/** - * @category api interface - * @since 0.69.0 - */ -export interface optionalWith extends - PropertySignature< - Types.Has extends true ? ":" : "?:", - | (Types.Has extends true ? option_.Option> : Schema.Type) - | (Types.Has extends true ? never : undefined), - never, - "?:", - | Schema.Encoded - | (Types.Has extends true ? null : never) - | (Types.Has extends true ? never : undefined), - Types.Has, - Schema.Context - > -{ - readonly from: S - annotations( - annotations: PropertySignature.Annotations< - | (Types.Has extends true ? option_.Option> : Schema.Type) - | (Types.Has extends true ? never : undefined) - > - ): optionalWith -} - -const optionalPropertySignatureAST = ( - self: Schema, - options?: { - readonly exact?: true - readonly default?: () => A - readonly nullable?: true - readonly as?: "Option" - readonly onNoneEncoding?: () => option_.Option - } -): PropertySignature.AST => { - const isExact = options?.exact - const defaultValue = options?.default - const isNullable = options?.nullable - const asOption = options?.as == "Option" - const asOptionEncode = options?.onNoneEncoding ? option_.orElse(options.onNoneEncoding) : identity - - if (isExact) { - if (defaultValue) { - if (isNullable) { - return withConstructorDefault( - optionalToRequired( - NullOr(self), - typeSchema(self), - { - decode: option_.match({ onNone: defaultValue, onSome: (a) => a === null ? defaultValue() : a }), - encode: option_.some - } - ), - defaultValue - ).ast - } else { - return withConstructorDefault( - optionalToRequired( - self, - typeSchema(self), - { decode: option_.match({ onNone: defaultValue, onSome: identity }), encode: option_.some } - ), - defaultValue - ).ast - } - } else if (asOption) { - if (isNullable) { - return optionalToRequired( - NullOr(self), - OptionFromSelf(typeSchema(self)), - { - decode: option_.filter(Predicate.isNotNull), - encode: asOptionEncode - } - ).ast - } else { - return optionalToRequired( - self, - OptionFromSelf(typeSchema(self)), - { decode: identity, encode: identity } - ).ast - } - } else { - if (isNullable) { - return optionalToOptional( - NullOr(self), - typeSchema(self), - { decode: option_.filter(Predicate.isNotNull), encode: identity } - ).ast - } else { - return new PropertySignatureDeclaration(self.ast, true, true, {}, undefined) - } - } - } else { - if (defaultValue) { - if (isNullable) { - return withConstructorDefault( - optionalToRequired( - NullishOr(self), - typeSchema(self), - { - decode: option_.match({ onNone: defaultValue, onSome: (a) => (a == null ? defaultValue() : a) }), - encode: option_.some - } - ), - defaultValue - ).ast - } else { - return withConstructorDefault( - optionalToRequired( - UndefinedOr(self), - typeSchema(self), - { - decode: option_.match({ onNone: defaultValue, onSome: (a) => (a === undefined ? defaultValue() : a) }), - encode: option_.some - } - ), - defaultValue - ).ast - } - } else if (asOption) { - if (isNullable) { - return optionalToRequired( - NullishOr(self), - OptionFromSelf(typeSchema(self)), - { - decode: option_.filter((a): a is A => a != null), - encode: asOptionEncode - } - ).ast - } else { - return optionalToRequired( - UndefinedOr(self), - OptionFromSelf(typeSchema(self)), - { - decode: option_.filter(Predicate.isNotUndefined), - encode: asOptionEncode - } - ).ast - } - } else { - if (isNullable) { - return optionalToOptional( - NullishOr(self), - UndefinedOr(typeSchema(self)), - { decode: option_.filter(Predicate.isNotNull), encode: identity } - ).ast - } else { - return new PropertySignatureDeclaration(UndefinedOr(self).ast, true, true, {}, undefined) - } - } - } -} - -/** - * @category PropertySignature - * @since 0.69.0 - */ -export const optional = (self: S): optional => { - const ast = self.ast === AST.undefinedKeyword || self.ast === AST.neverKeyword - ? AST.undefinedKeyword - : UndefinedOr(self).ast - return new PropertySignatureWithFromImpl(new PropertySignatureDeclaration(ast, true, true, {}, undefined), self) -} - -/** - * @category PropertySignature - * @since 0.69.0 - */ -export const optionalWith: { - >>( - options: Options - ): (self: S) => optionalWith - >>( - self: S, - options: Options - ): optionalWith -} = dual((args) => isSchema(args[0]), (self, options) => { - return new PropertySignatureWithFromImpl(optionalPropertySignatureAST(self, options), self) -}) - -/** - * @since 0.67.0 - */ -export declare namespace Struct { - /** - * @since 0.67.0 - */ - export type Fields = { - readonly [x: PropertyKey]: - | Schema.All - | PropertySignature.All - } - - type Key = [K] extends [never] ? never : - F[K] extends PropertySignature.All ? [Key] extends [never] ? K : Key : - K - - type EncodedTokenKeys = { - [K in keyof Fields]: Fields[K] extends - | PropertySignature - | PropertySignature - | PropertySignature - | PropertySignature ? K - : never - }[keyof Fields] - - type TypeTokenKeys = { - [K in keyof Fields]: Fields[K] extends OptionalPropertySignature ? K : never - }[keyof Fields] - - type OptionalPropertySignature = - | PropertySignature<"?:", any, PropertyKey, PropertySignature.Token, any, boolean, unknown> - | PropertySignature<"?:", any, PropertyKey, PropertySignature.Token, never, boolean, unknown> - | PropertySignature<"?:", never, PropertyKey, PropertySignature.Token, any, boolean, unknown> - | PropertySignature<"?:", never, PropertyKey, PropertySignature.Token, never, boolean, unknown> - - /** - * @since 0.67.0 - */ - export type Type = Types.UnionToIntersection< - { - [K in keyof F]: F[K] extends OptionalPropertySignature ? { readonly [H in K]?: Schema.Type } : - { readonly [h in K]: Schema.Type } - }[keyof F] - > extends infer Q ? Q : never - - /** - * @since 0.67.0 - */ - export type Encoded = - & { readonly [K in Exclude> as Key]: Schema.Encoded } - & { readonly [K in EncodedTokenKeys as Key]?: Schema.Encoded } - - /** - * @since 0.67.0 - */ - export type Context = Schema.Context - - type PropertySignatureWithDefault = - | PropertySignature - | PropertySignature - | PropertySignature - | PropertySignature - - /** - * @since 0.67.0 - */ - export type Constructor = Types.UnionToIntersection< - { - [K in keyof F]: F[K] extends OptionalPropertySignature ? { readonly [H in K]?: Schema.Type } : - F[K] extends PropertySignatureWithDefault ? { readonly [H in K]?: Schema.Type } : - { readonly [h in K]: Schema.Type } - }[keyof F] - > extends infer Q ? Q : never -} - -/** - * @since 0.67.0 - */ -export declare namespace IndexSignature { - /** - * @since 0.67.0 - */ - export type Record = { readonly key: Schema.All; readonly value: Schema.All } - - /** - * @since 0.67.0 - */ - export type Records = ReadonlyArray - - /** - * @since 0.67.0 - */ - export type NonEmptyRecords = array_.NonEmptyReadonlyArray - - /** - * @since 0.67.0 - */ - export type Type< - Records extends IndexSignature.Records - > = Types.UnionToIntersection< - { - [K in keyof Records]: { - readonly [P in Schema.Type]: Schema.Type - } - }[number] - > - - /** - * @since 0.67.0 - */ - export type Encoded< - Records extends IndexSignature.Records - > = Types.UnionToIntersection< - { - [K in keyof Records]: { - readonly [P in Schema.Encoded]: Schema.Encoded - } - }[number] - > - - /** - * @since 0.67.0 - */ - export type Context = { - [K in keyof Records]: Schema.Context | Schema.Context - }[number] -} - -/** - * @since 0.67.0 - */ -export declare namespace TypeLiteral { - /** - * @since 0.67.0 - */ - export type Type = - & Struct.Type - & IndexSignature.Type - - /** - * @since 0.67.0 - */ - export type Encoded = - & Struct.Encoded - & IndexSignature.Encoded - - /** - * @since 0.67.0 - */ - export type Constructor = - & Struct.Constructor - & IndexSignature.Type -} - -/** - * @category api interface - * @since 0.67.0 - */ -export interface TypeLiteral< - Fields extends Struct.Fields, - Records extends IndexSignature.Records -> extends - AnnotableClass< - TypeLiteral, - Simplify>, - Simplify>, - | Struct.Context - | IndexSignature.Context - > -{ - readonly fields: { readonly [K in keyof Fields]: Fields[K] } - readonly records: Readonly - annotations( - annotations: Annotations.Schema>> - ): TypeLiteral - make( - props: Simplify>, - options?: MakeOptions - ): Simplify> -} - -const getDefaultTypeLiteralAST = < - Fields extends Struct.Fields, - const Records extends IndexSignature.Records ->(fields: Fields, records: Records) => { - const ownKeys = util_.ownKeys(fields) - const pss: Array = [] - if (ownKeys.length > 0) { - const from: Array = [] - const to: Array = [] - const transformations: Array = [] - for (let i = 0; i < ownKeys.length; i++) { - const key = ownKeys[i] - const field = fields[key] - if (isPropertySignature(field)) { - const ast: PropertySignature.AST = field.ast - switch (ast._tag) { - case "PropertySignatureDeclaration": { - const type = ast.type - const isOptional = ast.isOptional - const toAnnotations = ast.annotations - from.push(new AST.PropertySignature(key, type, isOptional, true)) - to.push(new AST.PropertySignature(key, AST.typeAST(type), isOptional, true, toAnnotations)) - pss.push( - new AST.PropertySignature(key, type, isOptional, true, toAnnotations) - ) - break - } - case "PropertySignatureTransformation": { - const fromKey = ast.from.fromKey ?? key - from.push( - new AST.PropertySignature(fromKey, ast.from.type, ast.from.isOptional, true, ast.from.annotations) - ) - to.push( - new AST.PropertySignature(key, ast.to.type, ast.to.isOptional, true, ast.to.annotations) - ) - transformations.push(new AST.PropertySignatureTransformation(fromKey, key, ast.decode, ast.encode)) - break - } - } - } else { - from.push(new AST.PropertySignature(key, field.ast, false, true)) - to.push(new AST.PropertySignature(key, AST.typeAST(field.ast), false, true)) - pss.push(new AST.PropertySignature(key, field.ast, false, true)) - } - } - if (array_.isNonEmptyReadonlyArray(transformations)) { - const issFrom: Array = [] - const issTo: Array = [] - for (const r of records) { - const { indexSignatures, propertySignatures } = AST.record(r.key.ast, r.value.ast) - propertySignatures.forEach((ps) => { - from.push(ps) - to.push( - new AST.PropertySignature(ps.name, AST.typeAST(ps.type), ps.isOptional, ps.isReadonly, ps.annotations) - ) - }) - indexSignatures.forEach((is) => { - issFrom.push(is) - issTo.push(new AST.IndexSignature(is.parameter, AST.typeAST(is.type), is.isReadonly)) - }) - } - return new AST.Transformation( - new AST.TypeLiteral(from, issFrom, { [AST.TitleAnnotationId]: "Struct (Encoded side)" }), - new AST.TypeLiteral(to, issTo, { [AST.TitleAnnotationId]: "Struct (Type side)" }), - new AST.TypeLiteralTransformation(transformations) - ) - } - } - const iss: Array = [] - for (const r of records) { - const { indexSignatures, propertySignatures } = AST.record(r.key.ast, r.value.ast) - propertySignatures.forEach((ps) => pss.push(ps)) - indexSignatures.forEach((is) => iss.push(is)) - } - return new AST.TypeLiteral(pss, iss) -} - -const lazilyMergeDefaults = ( - fields: Struct.Fields, - out: Record -): { [x: string | symbol]: unknown } => { - const ownKeys = util_.ownKeys(fields) - for (const key of ownKeys) { - const field = fields[key] - if (out[key] === undefined && isPropertySignature(field)) { - const ast = field.ast - const defaultValue = ast._tag === "PropertySignatureDeclaration" ? ast.defaultValue : ast.to.defaultValue - if (defaultValue !== undefined) { - out[key] = defaultValue() - } - } - } - return out -} - -const makeTypeLiteralClass = < - Fields extends Struct.Fields, - const Records extends IndexSignature.Records ->( - fields: Fields, - records: Records, - ast: AST.AST = getDefaultTypeLiteralAST(fields, records) -): TypeLiteral => { - return class TypeLiteralClass extends make< - Simplify>, - Simplify>, - | Struct.Context - | IndexSignature.Context - >(ast) { - static override annotations( - annotations: Annotations.Schema>> - ): TypeLiteral { - return makeTypeLiteralClass(this.fields, this.records, mergeSchemaAnnotations(this.ast, annotations)) - } - - static fields = { ...fields } - - static records = [...records] as Records - - static make = ( - props: Simplify>, - options?: MakeOptions - ): Simplify> => { - const propsWithDefaults: any = lazilyMergeDefaults(fields, { ...props as any }) - return getDisableValidationMakeOption(options) - ? propsWithDefaults - : ParseResult.validateSync(this)(propsWithDefaults) - } - - static pick(...keys: Array): Struct>> { - return Struct(struct_.pick(fields, ...keys) as any) - } - - static omit(...keys: Array): Struct>> { - return Struct(struct_.omit(fields, ...keys) as any) - } - } -} - -/** - * @category api interface - * @since 0.67.0 - */ -export interface Struct extends TypeLiteral { - annotations(annotations: Annotations.Schema>>): Struct - /** @since 0.68.17 */ - pick>(...keys: Keys): Struct>> - /** @since 0.68.17 */ - omit>(...keys: Keys): Struct>> -} - -/** - * @category constructors - * @since 0.67.0 - */ -export function Struct( - fields: Fields, - ...records: Records -): TypeLiteral -export function Struct(fields: Fields): Struct -export function Struct( - fields: Fields, - ...records: Records -): TypeLiteral { - return makeTypeLiteralClass(fields, records) -} - -/** - * @category api interface - * @since 0.67.14 - */ -export interface tag extends PropertySignature<":", Tag, never, ":", Tag, true, never> {} - -/** - * Returns a property signature that represents a tag. - * A tag is a literal value that is used to distinguish between different types of objects. - * The tag is optional when using the `make` method. - * - * @see {@link TaggedStruct} - * - * @example - * import { Schema } from "@effect/schema" - * - * const User = Schema.Struct({ - * _tag: Schema.tag("User"), - * name: Schema.String, - * age: Schema.Number - * }) - * - * assert.deepStrictEqual(User.make({ name: "John", age: 44 }), { _tag: "User", name: "John", age: 44 }) - * - * @since 0.67.14 - */ -export const tag = (tag: Tag): tag => - Literal(tag).pipe(propertySignature, withConstructorDefault(() => tag)) - -/** - * @category api interface - * @since 0.67.14 - */ -export type TaggedStruct = Struct< - { _tag: tag } & Fields -> - -/** - * A tagged struct is a struct that has a tag property that is used to distinguish between different types of objects. - * - * The tag is optional when using the `make` method. - * - * @example - * import { Schema } from "@effect/schema" - * - * const User = Schema.TaggedStruct("User", { - * name: Schema.String, - * age: Schema.Number - * }) - * - * assert.deepStrictEqual(User.make({ name: "John", age: 44 }), { _tag: "User", name: "John", age: 44 }) - * - * @category constructors - * @since 0.67.14 - */ -export const TaggedStruct = ( - value: Tag, - fields: Fields -): TaggedStruct => Struct({ _tag: tag(value), ...fields }) - -/** - * @category api interface - * @since 0.67.0 - */ -export interface Record$ extends TypeLiteral<{}, [{ key: K; value: V }]> { - readonly key: K - readonly value: V - annotations( - annotations: Annotations.Schema>> - ): Record$ -} - -const makeRecordClass = (key: K, value: V, ast?: AST.AST): Record$ => - class RecordClass extends makeTypeLiteralClass({}, [{ key, value }], ast) { - static override annotations( - annotations: Annotations.Schema>> - ) { - return makeRecordClass(key, value, mergeSchemaAnnotations(this.ast, annotations)) - } - - static key = key - - static value = value - } - -/** - * @category constructors - * @since 0.67.0 - */ -export const Record = ( - options: { readonly key: K; readonly value: V } -): Record$ => makeRecordClass(options.key, options.value) - -/** - * @category struct transformations - * @since 0.67.0 - */ -export const pick = >(...keys: Keys) => -( - self: Schema -): SchemaClass>, Simplify>, R> => make(AST.pick(self.ast, keys)) - -/** - * @category struct transformations - * @since 0.67.0 - */ -export const omit = >(...keys: Keys) => -( - self: Schema -): SchemaClass>, Simplify>, R> => make(AST.omit(self.ast, keys)) - -/** - * Given a schema `Schema` and a key `key: K`, this function extracts a specific field from the `A` type, - * producing a new schema that represents a transformation from the `{ readonly [key]: I[K] }` type to `A[K]`. - * - * @example - * import * as S from "@effect/schema/Schema" - * - * // --------------------------------------------- - * // use case: pull out a single field from a - * // struct through a transformation - * // --------------------------------------------- - * - * const mytable = S.Struct({ - * column1: S.NumberFromString, - * column2: S.Number - * }) - * - * // const pullOutColumn: S.Schema - * const pullOutColumn = mytable.pipe(S.pluck("column1")) - * - * console.log(S.decodeUnknownEither(S.Array(pullOutColumn))([{ column1: "1", column2: 100 }, { column1: "2", column2: 300 }])) - * // Output: { _id: 'Either', _tag: 'Right', right: [ 1, 2 ] } - * - * @category struct transformations - * @since 0.67.0 - */ -export const pluck: { - ( - key: K - ): (schema: Schema) => Schema - ( - schema: Schema, - key: K - ): Schema -} = dual( - 2, - ( - schema: Schema, - key: K - ): Schema, R> => { - const ps = AST.getPropertyKeyIndexedAccess(AST.typeAST(schema.ast), key) - const value = make(ps.isOptional ? AST.orUndefined(ps.type) : ps.type) - return transform( - schema.pipe(pick(key)), - value, - { - strict: true, - decode: (a: any) => a[key], - encode: (ak) => ps.isOptional && ak === undefined ? {} : { [key]: ak } as any - } - ) - } -) - -/** - * @category branding - * @since 0.67.0 - */ -export interface BrandSchema, I = A, R = never> - extends AnnotableClass, A, I, R> -{ - make(a: Brand.Unbranded, options?: MakeOptions): A -} - -/** - * @category api interface - * @since 0.67.0 - */ -export interface brand - extends BrandSchema & Brand, Schema.Encoded, Schema.Context> -{ - annotations(annotations: Annotations.Schema & Brand>): brand -} - -const makeBrandClass = (ast: AST.AST): brand => - class BrandClass extends make & Brand, Schema.Encoded, Schema.Context>(ast) { - static override annotations(annotations: Annotations.Schema & Brand>): brand { - return makeBrandClass(mergeSchemaAnnotations(this.ast, annotations)) - } - - static make = (a: Brand.Unbranded & Brand>, options?: MakeOptions): Schema.Type & Brand => { - return getDisableValidationMakeOption(options) ? a : ParseResult.validateSync(this)(a) - } - } - -/** - * Returns a nominal branded schema by applying a brand to a given schema. - * - * ``` - * Schema + B -> Schema> - * ``` - * - * @param self - The input schema to be combined with the brand. - * @param brand - The brand to apply. - * - * @example - * import * as Schema from "@effect/schema/Schema" - * - * const Int = Schema.Number.pipe(Schema.int(), Schema.brand("Int")) - * type Int = Schema.Schema.Type // number & Brand<"Int"> - * - * @category branding - * @since 0.67.0 - */ -export const brand = ( - brand: B, - annotations?: Annotations.Schema & Brand> -) => -(self: S): brand => { - const annotation: AST.BrandAnnotation = option_.match(AST.getBrandAnnotation(self.ast), { - onNone: () => [brand], - onSome: (brands) => [...brands, brand] - }) - const ast = AST.annotations( - self.ast, - toASTAnnotations({ - // add a default title annotation containing the brand - title: String(self.ast) + ` & Brand<${util_.formatUnknown(brand)}>`, - ...annotations, - [AST.BrandAnnotationId]: annotation - }) - ) - return makeBrandClass(ast) -} - -/** - * @category combinators - * @since 0.69.0 - */ -export const partial = ( - self: Schema -): SchemaClass<{ [K in keyof A]?: A[K] | undefined }, { [K in keyof I]?: I[K] | undefined }, R> => - make(AST.partial(self.ast)) - -/** - * @category combinators - * @since 0.69.0 - */ -export const partialWith: { - (options: Options): ( - self: Schema - ) => SchemaClass<{ [K in keyof A]?: A[K] }, { [K in keyof I]?: I[K] }, R> - ( - self: Schema, - options: Options - ): SchemaClass<{ [K in keyof A]?: A[K] }, { [K in keyof I]?: I[K] }, R> -} = dual((args) => isSchema(args[0]), ( - self: Schema, - options: { readonly exact: true } -): SchemaClass, Partial, R> => make(AST.partial(self.ast, options))) - -/** - * @category combinators - * @since 0.67.0 - */ -export const required = ( - self: Schema -): SchemaClass<{ [K in keyof A]-?: A[K] }, { [K in keyof I]-?: I[K] }, R> => make(AST.required(self.ast)) - -/** - * @category api interface - * @since 0.67.0 - */ -export interface mutable extends - AnnotableClass< - mutable, - SimplifyMutable>, - SimplifyMutable>, - Schema.Context - > -{} - -/** - * Creates a new schema with shallow mutability applied to its properties. - * - * @param schema - The original schema to make properties mutable (shallowly). - * - * @category combinators - * @since 0.67.0 - */ -export const mutable = (schema: S): mutable => make(AST.mutable(schema.ast)) - -const intersectTypeLiterals = ( - x: AST.AST, - y: AST.AST, - path: ReadonlyArray -): AST.TypeLiteral => { - if (AST.isTypeLiteral(x) && AST.isTypeLiteral(y)) { - const propertySignatures = [...x.propertySignatures] - for (const ps of y.propertySignatures) { - const name = ps.name - const i = propertySignatures.findIndex((ps) => ps.name === name) - if (i === -1) { - propertySignatures.push(ps) - } else { - const { isOptional, type } = propertySignatures[i] - propertySignatures[i] = new AST.PropertySignature( - name, - extendAST(type, ps.type, path.concat(name)), - isOptional, - true - ) - } - } - return new AST.TypeLiteral( - propertySignatures, - x.indexSignatures.concat(y.indexSignatures) - ) - } - throw new Error(errors_.getSchemaExtendErrorMessage(x, y, path)) -} - -const preserveRefinementAnnotations = AST.blackListAnnotations([ - AST.IdentifierAnnotationId -]) - -const addRefinementToMembers = (refinement: AST.Refinement, asts: ReadonlyArray): Array => - asts.map((ast) => new AST.Refinement(ast, refinement.filter, preserveRefinementAnnotations(refinement))) - -const extendAST = ( - x: AST.AST, - y: AST.AST, - path: ReadonlyArray -): AST.AST => AST.Union.make(intersectUnionMembers([x], [y], path)) - -const getTypes = (ast: AST.AST): ReadonlyArray => AST.isUnion(ast) ? ast.types : [ast] - -const intersectUnionMembers = ( - xs: ReadonlyArray, - ys: ReadonlyArray, - path: ReadonlyArray -): Array => - array_.flatMap(xs, (x) => - array_.flatMap(ys, (y) => { - switch (y._tag) { - case "Literal": { - if ( - (Predicate.isString(y.literal) && AST.isStringKeyword(x) || - (Predicate.isNumber(y.literal) && AST.isNumberKeyword(x)) || - (Predicate.isBoolean(y.literal) && AST.isBooleanKeyword(x))) - ) { - return [y] - } - break - } - case "StringKeyword": { - if (y === AST.stringKeyword) { - if (AST.isStringKeyword(x) || (AST.isLiteral(x) && Predicate.isString(x.literal))) { - return [x] - } else if (AST.isRefinement(x)) { - return addRefinementToMembers(x, intersectUnionMembers(getTypes(x.from), [y], path)) - } - } else if (x === AST.stringKeyword) { - return [y] - } - break - } - case "NumberKeyword": { - if (y === AST.numberKeyword) { - if (AST.isNumberKeyword(x) || (AST.isLiteral(x) && Predicate.isNumber(x.literal))) { - return [x] - } else if (AST.isRefinement(x)) { - return addRefinementToMembers(x, intersectUnionMembers(getTypes(x.from), [y], path)) - } - } else if (x === AST.numberKeyword) { - return [y] - } - break - } - case "BooleanKeyword": { - if (y === AST.booleanKeyword) { - if (AST.isBooleanKeyword(x) || (AST.isLiteral(x) && Predicate.isBoolean(x.literal))) { - return [x] - } else if (AST.isRefinement(x)) { - return addRefinementToMembers(x, intersectUnionMembers(getTypes(x.from), [y], path)) - } - } else if (x === AST.booleanKeyword) { - return [y] - } - break - } - case "Union": - return intersectUnionMembers(getTypes(x), y.types, path) - case "Suspend": - return [new AST.Suspend(() => extendAST(x, y.f(), path))] - case "Refinement": - return addRefinementToMembers(y, intersectUnionMembers(getTypes(x), getTypes(y.from), path)) - case "TypeLiteral": { - switch (x._tag) { - case "Union": - return intersectUnionMembers(x.types, [y], path) - case "Suspend": - return [new AST.Suspend(() => extendAST(x.f(), y, path))] - case "Refinement": - return addRefinementToMembers(x, intersectUnionMembers(getTypes(x.from), [y], path)) - case "TypeLiteral": - return [intersectTypeLiterals(x, y, path)] - case "Transformation": { - if (AST.isTypeLiteralTransformation(x.transformation)) { - return [ - new AST.Transformation( - intersectTypeLiterals(x.from, y, path), - intersectTypeLiterals(x.to, AST.typeAST(y), path), - new AST.TypeLiteralTransformation( - x.transformation.propertySignatureTransformations - ) - ) - ] - } - break - } - } - break - } - case "Transformation": { - if (AST.isTypeLiteralTransformation(y.transformation)) { - switch (x._tag) { - case "Union": - return intersectUnionMembers(x.types, [y], path) - case "Suspend": - return [new AST.Suspend(() => extendAST(x.f(), y, path))] - case "Refinement": - return addRefinementToMembers(x, intersectUnionMembers(getTypes(x.from), [y], path)) - case "TypeLiteral": - return [ - new AST.Transformation( - intersectTypeLiterals(x, y.from, path), - intersectTypeLiterals(AST.typeAST(x), y.to, path), - new AST.TypeLiteralTransformation( - y.transformation.propertySignatureTransformations - ) - ) - ] - case "Transformation": - { - if (AST.isTypeLiteralTransformation(x.transformation)) { - return [ - new AST.Transformation( - intersectTypeLiterals(x.from, y.from, path), - intersectTypeLiterals(x.to, y.to, path), - new AST.TypeLiteralTransformation( - y.transformation.propertySignatureTransformations.concat( - x.transformation.propertySignatureTransformations - ) - ) - ) - ] - } - } - break - } - } - break - } - } - throw new Error(errors_.getSchemaExtendErrorMessage(x, y, path)) - })) - -/** - * @category api interface - * @since 0.67.0 - */ -export interface extend extends - AnnotableClass< - extend, - Schema.Type & Schema.Type, - Schema.Encoded & Schema.Encoded, - Schema.Context | Schema.Context - > -{} - -/** - * Extends a schema with another schema. - * - * Not all extensions are supported, and their support depends on the nature of the involved schemas. - * - * Possible extensions include: - * - `Schema.String` with another `Schema.String` refinement or a string literal - * - `Schema.Number` with another `Schema.Number` refinement or a number literal - * - `Schema.Boolean` with another `Schema.Boolean` refinement or a boolean literal - * - A struct with another struct where overlapping fields support extension - * - A struct with in index signature - * - A struct with a union of supported schemas - * - A refinement of a struct with a supported schema - * - A suspend of a struct with a supported schema - * - * @example - * import * as Schema from "@effect/schema/Schema" - * - * const schema = Schema.Struct({ - * a: Schema.String, - * b: Schema.String - * }) - * - * // const extended: Schema.Schema< - * // { - * // readonly a: string - * // readonly b: string - * // } & { - * // readonly c: string - * // } & { - * // readonly [x: string]: string - * // } - * // > - * const extended = Schema.asSchema(schema.pipe( - * Schema.extend(Schema.Struct({ c: Schema.String })), // <= you can add more fields - * Schema.extend(Schema.Record({ key: Schema.String, value: Schema.String })) // <= you can add index signatures - * )) - * - * @category combinators - * @since 0.67.0 - */ -export const extend: { - (that: That): (self: Self) => extend - (self: Self, that: That): extend -} = dual( - 2, - (self: Self, that: That) => make(extendAST(self.ast, that.ast, [])) -) - -/** - * @category combinators - * @since 0.67.0 - */ -export const compose: { - ( - to: Schema - ): (from: Schema) => SchemaClass - ( - to: Schema - ): (from: Schema) => SchemaClass - ( - to: Schema, - options?: { readonly strict: true } - ): (from: Schema) => SchemaClass - ( - to: Schema, - options: { readonly strict: false } - ): (from: Schema) => SchemaClass - - ( - from: Schema, - to: Schema - ): SchemaClass - ( - from: Schema, - to: Schema - ): SchemaClass - ( - from: Schema, - to: Schema, - options?: { readonly strict: true } - ): SchemaClass - ( - from: Schema, - to: Schema, - options: { readonly strict: false } - ): SchemaClass -} = dual( - (args) => isSchema(args[1]), - (from: Schema, to: Schema): SchemaClass => - make(AST.compose(from.ast, to.ast)) -) - -/** - * @category api interface - * @since 0.67.0 - */ -export interface suspend extends AnnotableClass, A, I, R> {} - -/** - * @category constructors - * @since 0.67.0 - */ -export const suspend = (f: () => Schema): suspend => make(new AST.Suspend(() => f().ast)) - -/** - * @since 0.68.8 - * @category symbol - */ -export const refineTypeId: unique symbol = Symbol.for("@effect/schema/refine") - -/** - * @since 0.68.8 - * @category symbol - */ -export type refineTypeId = typeof refineTypeId - -/** - * @category api interface - * @since 0.67.0 - */ -export interface refine - extends AnnotableClass, A, Schema.Encoded, Schema.Context> -{ - readonly [refineTypeId]: From - readonly from: From - readonly filter: ( - a: Schema.Type, - options: ParseOptions, - self: AST.Refinement - ) => option_.Option - make(a: Schema.Type, options?: MakeOptions): A -} - -const makeRefineClass = ( - from: From, - filter: ( - a: Schema.Type, - options: ParseOptions, - self: AST.Refinement - ) => option_.Option, - ast: AST.AST -): refine => - class RefineClass extends make, Schema.Context>(ast) { - static override annotations(annotations: Annotations.Schema): refine { - return makeRefineClass(this.from, this.filter, mergeSchemaAnnotations(this.ast, annotations)) - } - - static [refineTypeId] = from - - static from = from - - static filter = filter - - static make = (a: Schema.Type, options?: MakeOptions): A => { - return getDisableValidationMakeOption(options) ? a : ParseResult.validateSync(this)(a) - } - } - -/** - * @category api interface - * @since 0.67.0 - */ -export interface filter extends refine, From> {} - -const fromFilterPredicateReturnTypeItem = ( - item: FilterOutput, - ast: AST.Refinement | AST.Transformation, - input: unknown -): option_.Option => { - if (Predicate.isBoolean(item)) { - return item - ? option_.none() - : option_.some(new ParseResult.Type(ast, input)) - } - if (Predicate.isString(item)) { - return option_.some(new ParseResult.Type(ast, input, item)) - } - if (item !== undefined) { - if ("_tag" in item) { - return option_.some(item) - } - const issue = new ParseResult.Type(ast, input, item.message) - return option_.some( - array_.isNonEmptyReadonlyArray(item.path) ? new ParseResult.Pointer(item.path, input, issue) : issue - ) - } - return option_.none() -} - -const toFilterParseIssue = ( - out: FilterReturnType, - ast: AST.Refinement | AST.Transformation, - input: unknown -): option_.Option => { - if (util_.isSingle(out)) { - return fromFilterPredicateReturnTypeItem(out, ast, input) - } - if (array_.isNonEmptyReadonlyArray(out)) { - const issues = array_.filterMap(out, (issue) => fromFilterPredicateReturnTypeItem(issue, ast, input)) - if (array_.isNonEmptyReadonlyArray(issues)) { - return option_.some(issues.length === 1 ? issues[0] : new ParseResult.Composite(ast, input, issues)) - } - } - return option_.none() -} - -/** - * @category filtering - * @since 0.68.0 - */ -export interface FilterIssue { - readonly path: ReadonlyArray - readonly message: string -} - -/** - * @category filtering - * @since 0.68.0 - */ -export type FilterOutput = undefined | boolean | string | ParseResult.ParseIssue | FilterIssue - -type FilterReturnType = FilterOutput | ReadonlyArray - -/** - * @category filtering - * @since 0.67.0 - */ -export function filter( - refinement: (a: A, options: ParseOptions, self: AST.Refinement) => a is B, - annotations?: Annotations.Filter -): (self: Schema) => refine> -export function filter( - refinement: (a: A, options: ParseOptions, self: AST.Refinement) => a is B, - annotations?: Annotations.Filter -): (self: Schema) => refine> -export function filter( - predicate: ( - a: Types.NoInfer>, - options: ParseOptions, - self: AST.Refinement - ) => FilterReturnType, - annotations?: Annotations.Filter>> -): (self: S) => filter -export function filter( - predicate: ( - a: A, - options: ParseOptions, - self: AST.Refinement - ) => FilterReturnType, - annotations?: Annotations.Filter -): (self: Schema) => refine> { - return (self: Schema) => { - function filter(input: A, options: AST.ParseOptions, ast: AST.Refinement) { - return toFilterParseIssue(predicate(input, options, ast), ast, input) - } - const ast = new AST.Refinement( - self.ast, - filter, - toASTAnnotations(annotations) - ) - return makeRefineClass(self, filter, ast) - } -} - -/** - * @category api interface - * @since 0.68.17 - */ -export interface filterEffect - extends transformOrFail>, FD> -{} - -/** - * @category transformations - * @since 0.68.17 - */ -export const filterEffect: { - ( - f: ( - a: Types.NoInfer>, - options: ParseOptions, - self: AST.Transformation - ) => Effect.Effect - ): (self: S) => filterEffect - ( - self: S, - f: ( - a: Types.NoInfer>, - options: ParseOptions, - self: AST.Transformation - ) => Effect.Effect - ): filterEffect -} = dual(2, ( - self: S, - f: ( - a: Types.NoInfer>, - options: ParseOptions, - self: AST.Transformation - ) => Effect.Effect -): filterEffect => - transformOrFail( - self, - typeSchema(self), - { - strict: true, - decode: (a, options, ast) => - ParseResult.flatMap( - f(a, options, ast), - (filterReturnType) => - option_.match(toFilterParseIssue(filterReturnType, ast, a), { - onNone: () => ParseResult.succeed(a), - onSome: ParseResult.fail - }) - ), - encode: ParseResult.succeed - } - )) - -/** - * @category api interface - * @since 0.67.0 - */ -export interface transformOrFail extends - AnnotableClass< - transformOrFail, - Schema.Type, - Schema.Encoded, - Schema.Context | Schema.Context | R - > -{ - readonly from: From - readonly to: To -} - -const makeTransformationClass = ( - from: From, - to: To, - ast: AST.AST -): transformOrFail => - class TransformationClass - extends make, Schema.Encoded, Schema.Context | Schema.Context | R>(ast) - { - static override annotations(annotations: Annotations.Schema>) { - return makeTransformationClass( - this.from, - this.to, - mergeSchemaAnnotations(this.ast, annotations) - ) - } - - static from = from - - static to = to - } - -/** - * Create a new `Schema` by transforming the input and output of an existing `Schema` - * using the provided decoding functions. - * - * @category transformations - * @since 0.67.0 - */ -export const transformOrFail: { - ( - to: To, - options: { - readonly decode: ( - fromA: Schema.Type, - options: ParseOptions, - ast: AST.Transformation, - fromI: Schema.Encoded - ) => Effect.Effect, ParseResult.ParseIssue, RD> - readonly encode: ( - toI: Schema.Encoded, - options: ParseOptions, - ast: AST.Transformation, - toA: Schema.Type - ) => Effect.Effect, ParseResult.ParseIssue, RE> - readonly strict?: true - } | { - readonly decode: ( - fromA: Schema.Type, - options: ParseOptions, - ast: AST.Transformation, - fromI: Schema.Encoded - ) => Effect.Effect - readonly encode: ( - toI: Schema.Encoded, - options: ParseOptions, - ast: AST.Transformation, - toA: Schema.Type - ) => Effect.Effect - readonly strict: false - } - ): (from: From) => transformOrFail - ( - from: From, - to: To, - options: { - readonly decode: ( - fromA: Schema.Type, - options: ParseOptions, - ast: AST.Transformation, - fromI: Schema.Encoded - ) => Effect.Effect, ParseResult.ParseIssue, RD> - readonly encode: ( - toI: Schema.Encoded, - options: ParseOptions, - ast: AST.Transformation, - toA: Schema.Type - ) => Effect.Effect, ParseResult.ParseIssue, RE> - readonly strict?: true - } | { - readonly decode: ( - fromA: Schema.Type, - options: ParseOptions, - ast: AST.Transformation, - fromI: Schema.Encoded - ) => Effect.Effect - readonly encode: ( - toI: Schema.Encoded, - options: ParseOptions, - ast: AST.Transformation, - toA: Schema.Type - ) => Effect.Effect - readonly strict: false - } - ): transformOrFail -} = dual((args) => isSchema(args[0]) && isSchema(args[1]), ( - from: Schema, - to: Schema, - options: { - readonly decode: ( - fromA: FromA, - options: ParseOptions, - ast: AST.Transformation, - fromI: FromI - ) => Effect.Effect - readonly encode: ( - toI: ToI, - options: ParseOptions, - ast: AST.Transformation, - toA: ToA - ) => Effect.Effect - } -): Schema => - makeTransformationClass( - from, - to, - new AST.Transformation( - from.ast, - to.ast, - new AST.FinalTransformation(options.decode, options.encode) - ) - )) - -/** - * @category api interface - * @since 0.67.0 - */ -export interface transform extends transformOrFail { - annotations(annotations: Annotations.Schema>): transform -} - -/** - * Create a new `Schema` by transforming the input and output of an existing `Schema` - * using the provided mapping functions. - * - * @category transformations - * @since 0.67.0 - */ -export const transform: { - ( - to: To, - options: { - readonly decode: (fromA: Schema.Type, fromI: Schema.Encoded) => Schema.Encoded - readonly encode: (toI: Schema.Encoded, toA: Schema.Type) => Schema.Type - readonly strict?: true - } | { - readonly decode: (fromA: Schema.Type, fromI: Schema.Encoded) => unknown - readonly encode: (toI: Schema.Encoded, toA: Schema.Type) => unknown - readonly strict: false - } - ): (from: From) => transform - ( - from: From, - to: To, - options: { - readonly decode: (fromA: Schema.Type, fromI: Schema.Encoded) => Schema.Encoded - readonly encode: (toI: Schema.Encoded, toA: Schema.Type) => Schema.Type - readonly strict?: true - } | { - readonly decode: (fromA: Schema.Type, fromI: Schema.Encoded) => unknown - readonly encode: (toI: Schema.Encoded, toA: Schema.Type) => unknown - readonly strict: false - } - ): transform -} = dual( - (args) => isSchema(args[0]) && isSchema(args[1]), - ( - from: Schema, - to: Schema, - options: { - readonly decode: (fromA: FromA, fromI: FromI) => ToI - readonly encode: (toI: ToI, toA: ToA) => FromA - } - ): Schema => - transformOrFail( - from, - to, - { - strict: true, - decode: (fromA, _options, _ast, toA) => ParseResult.succeed(options.decode(fromA, toA)), - encode: (toI, _options, _ast, toA) => ParseResult.succeed(options.encode(toI, toA)) - } - ) -) - -/** - * @category api interface - * @since 0.67.0 - */ -export interface transformLiteral extends Annotable, Type, Encoded> {} - -/** - * Creates a new `Schema` which transforms literal values. - * - * @example - * import * as S from "@effect/schema/Schema" - * - * const schema = S.transformLiteral(0, "a") - * - * assert.deepStrictEqual(S.decodeSync(schema)(0), "a") - * - * @category constructors - * @since 0.67.0 - */ -export const transformLiteral = ( - from: Encoded, - to: Type -): transformLiteral => - transform(Literal(from), Literal(to), { strict: true, decode: () => to, encode: () => from }) - -/** - * Creates a new `Schema` which maps between corresponding literal values. - * - * @example - * import * as S from "@effect/schema/Schema" - * - * const Animal = S.transformLiterals( - * [0, "cat"], - * [1, "dog"], - * [2, "cow"] - * ) - * - * assert.deepStrictEqual(S.decodeSync(Animal)(1), "dog") - * - * @category constructors - * @since 0.67.0 - */ -export function transformLiterals>( - ...pairs: A -): Union<{ -readonly [I in keyof A]: transformLiteral }> -export function transformLiterals( - pairs: [Encoded, Type] -): transformLiteral -export function transformLiterals< - const A extends ReadonlyArray ->(...pairs: A): Schema -export function transformLiterals< - const A extends ReadonlyArray ->(...pairs: A): Schema { - return Union(...pairs.map(([from, to]) => transformLiteral(from, to))) -} - -/** - * Attaches a property signature with the specified key and value to the schema. - * This API is useful when you want to add a property to your schema which doesn't describe the shape of the input, - * but rather maps to another schema, for example when you want to add a discriminant to a simple union. - * - * @param self - The input schema. - * @param key - The name of the property to add to the schema. - * @param value - The value of the property to add to the schema. - * - * @example - * import * as S from "@effect/schema/Schema" - * import { pipe } from "effect/Function" - * - * const Circle = S.Struct({ radius: S.Number }) - * const Square = S.Struct({ sideLength: S.Number }) - * const Shape = S.Union( - * Circle.pipe(S.attachPropertySignature("kind", "circle")), - * Square.pipe(S.attachPropertySignature("kind", "square")) - * ) - * - * assert.deepStrictEqual(S.decodeSync(Shape)({ radius: 10 }), { - * kind: "circle", - * radius: 10 - * }) - * - * @category combinators - * @since 0.67.0 - */ -export const attachPropertySignature: { - ( - key: K, - value: V, - annotations?: Annotations.Schema> - ): ( - schema: SchemaClass - ) => SchemaClass, I, R> - ( - schema: Schema, - key: K, - value: V, - annotations?: Annotations.Schema> - ): SchemaClass, I, R> -} = dual( - (args) => isSchema(args[0]), - ( - schema: Schema, - key: K, - value: V, - annotations?: Annotations.Schema> - ): SchemaClass, I, R> => { - const ast = extend( - typeSchema(schema), - Struct({ [key]: Predicate.isSymbol(value) ? UniqueSymbolFromSelf(value) : Literal(value) }) - ).ast - return make( - new AST.Transformation( - schema.ast, - annotations ? mergeSchemaAnnotations(ast, annotations) : ast, - new AST.TypeLiteralTransformation( - [ - new AST.PropertySignatureTransformation( - key, - key, - () => option_.some(value), - () => option_.none() - ) - ] - ) - ) - ) - } -) - -/** - * @category annotations - * @since 0.67.0 - */ -export declare namespace Annotations { - /** - * @category annotations - * @since 0.67.0 - */ - export interface Doc extends AST.Annotations { - readonly title?: AST.TitleAnnotation - readonly description?: AST.DescriptionAnnotation - readonly documentation?: AST.DocumentationAnnotation - readonly examples?: AST.ExamplesAnnotation - readonly default?: AST.DefaultAnnotation - } - - /** - * @since 0.67.0 - */ - export interface Schema = readonly []> extends Doc { - readonly identifier?: AST.IdentifierAnnotation - readonly message?: AST.MessageAnnotation - readonly typeId?: AST.TypeAnnotation | { id: AST.TypeAnnotation; annotation: unknown } - readonly jsonSchema?: AST.JSONSchemaAnnotation - readonly arbitrary?: ( - ...arbitraries: [ - ...{ readonly [K in keyof TypeParameters]: LazyArbitrary }, - ctx: GenerationContext - ] - ) => LazyArbitrary - readonly pretty?: ( - ...pretties: { readonly [K in keyof TypeParameters]: pretty_.Pretty } - ) => pretty_.Pretty - readonly equivalence?: ( - ...equivalences: { readonly [K in keyof TypeParameters]: Equivalence.Equivalence } - ) => Equivalence.Equivalence - readonly concurrency?: AST.ConcurrencyAnnotation - readonly batching?: AST.BatchingAnnotation - readonly parseIssueTitle?: AST.ParseIssueTitleAnnotation - readonly parseOptions?: AST.ParseOptions - readonly decodingFallback?: AST.DecodingFallbackAnnotation - } - - /** - * @since 0.67.0 - */ - export interface Filter extends Schema {} -} - -/** - * Merges a set of new annotations with existing ones, potentially overwriting - * any duplicates. - * - * @category annotations - * @since 0.67.0 - */ -export const annotations: { - (annotations: Annotations.Schema>): (self: S) => Annotable.Self - (self: S, annotations: Annotations.Schema>): Annotable.Self -} = dual( - 2, - (self: Schema, annotations: Annotations.Schema): Schema => self.annotations(annotations) -) - -type Rename = { - [ - K in keyof A as K extends keyof M ? M[K] extends PropertyKey ? M[K] - : never - : K - ]: A[K] -} - -/** - * @category renaming - * @since 0.67.0 - */ -export const rename: { - < - A, - const M extends - & { readonly [K in keyof A]?: PropertyKey } - & { readonly [K in Exclude]: never } - >( - mapping: M - ): (self: Schema) => SchemaClass>, I, R> - < - A, - I, - R, - const M extends - & { readonly [K in keyof A]?: PropertyKey } - & { readonly [K in Exclude]: never } - >( - self: Schema, - mapping: M - ): SchemaClass>, I, R> -} = dual( - 2, - < - A, - I, - R, - const M extends - & { readonly [K in keyof A]?: PropertyKey } - & { readonly [K in Exclude]: never } - >( - self: Schema, - mapping: M - ): SchemaClass>, I, R> => make(AST.rename(self.ast, mapping)) -) - -/** - * @category type id - * @since 0.67.0 - */ -export const TrimmedTypeId: unique symbol = Symbol.for("@effect/schema/TypeId/Trimmed") - -/** - * Verifies that a string contains no leading or trailing whitespaces. - * - * Note. This combinator does not make any transformations, it only validates. - * If what you were looking for was a combinator to trim strings, then check out the `trim` combinator. - * - * @category string filters - * @since 0.67.0 - */ -export const trimmed = - (annotations?: Annotations.Filter) => (self: Schema): filter> => - self.pipe( - filter((a) => a === a.trim(), { - typeId: TrimmedTypeId, - description: "a string with no leading or trailing whitespace", - jsonSchema: { pattern: "^\\S[\\s\\S]*\\S$|^\\S$|^$" }, - ...annotations - }) - ) - -/** - * @category type id - * @since 0.67.0 - */ -export const MaxLengthTypeId: unique symbol = filters_.MaxLengthTypeId - -/** - * @category type id - * @since 0.67.0 - */ -export type MaxLengthTypeId = typeof MaxLengthTypeId - -/** - * @category string filters - * @since 0.67.0 - */ -export const maxLength = ( - maxLength: number, - annotations?: Annotations.Filter -) => -(self: Schema): filter> => - self.pipe( - filter( - (a) => a.length <= maxLength, - { - typeId: MaxLengthTypeId, - description: `a string at most ${maxLength} character(s) long`, - jsonSchema: { maxLength }, - ...annotations - } - ) - ) - -/** - * @category type id - * @since 0.67.0 - */ -export const MinLengthTypeId: unique symbol = filters_.MinLengthTypeId - -/** - * @category type id - * @since 0.67.0 - */ -export type MinLengthTypeId = typeof MinLengthTypeId - -/** - * @category string filters - * @since 0.67.0 - */ -export const minLength = ( - minLength: number, - annotations?: Annotations.Filter -) => -(self: Schema): filter> => - self.pipe( - filter( - (a) => a.length >= minLength, - { - typeId: MinLengthTypeId, - description: `a string at least ${minLength} character(s) long`, - jsonSchema: { minLength }, - ...annotations - } - ) - ) - -/** - * @category type id - * @since 0.67.0 - */ -export const PatternTypeId: unique symbol = Symbol.for("@effect/schema/TypeId/Pattern") - -/** - * @category string filters - * @since 0.67.0 - */ -export const pattern = ( - regex: RegExp, - annotations?: Annotations.Filter -) => -(self: Schema): filter> => { - const pattern = regex.source - return self.pipe( - filter( - (a): a is A => { - // The following line ensures that `lastIndex` is reset to `0` in case the user has specified the `g` flag - regex.lastIndex = 0 - return regex.test(a) - }, - { - typeId: { id: PatternTypeId, annotation: { regex } }, - description: `a string matching the pattern ${pattern}`, - jsonSchema: { pattern }, - arbitrary: () => (fc) => fc.stringMatching(regex) as any, - ...annotations - } - ) - ) -} - -/** - * @category type id - * @since 0.67.0 - */ -export const StartsWithTypeId: unique symbol = Symbol.for("@effect/schema/TypeId/StartsWith") - -/** - * @category string filters - * @since 0.67.0 - */ -export const startsWith = ( - startsWith: string, - annotations?: Annotations.Filter -) => -(self: Schema): filter> => - self.pipe( - filter( - (a) => a.startsWith(startsWith), - { - typeId: { id: StartsWithTypeId, annotation: { startsWith } }, - description: `a string starting with ${JSON.stringify(startsWith)}`, - jsonSchema: { pattern: `^${startsWith}` }, - ...annotations - } - ) - ) - -/** - * @category type id - * @since 0.67.0 - */ -export const EndsWithTypeId: unique symbol = Symbol.for("@effect/schema/TypeId/EndsWith") - -/** - * @category string filters - * @since 0.67.0 - */ -export const endsWith = ( - endsWith: string, - annotations?: Annotations.Filter -) => -(self: Schema): filter> => - self.pipe( - filter( - (a) => a.endsWith(endsWith), - { - typeId: { id: EndsWithTypeId, annotation: { endsWith } }, - description: `a string ending with ${JSON.stringify(endsWith)}`, - jsonSchema: { pattern: `^.*${endsWith}$` }, - ...annotations - } - ) - ) - -/** - * @category type id - * @since 0.67.0 - */ -export const IncludesTypeId: unique symbol = Symbol.for("@effect/schema/TypeId/Includes") - -/** - * @category string filters - * @since 0.67.0 - */ -export const includes = ( - searchString: string, - annotations?: Annotations.Filter -) => -(self: Schema): filter> => - self.pipe( - filter( - (a) => a.includes(searchString), - { - typeId: { id: IncludesTypeId, annotation: { includes: searchString } }, - description: `a string including ${JSON.stringify(searchString)}`, - jsonSchema: { pattern: `.*${searchString}.*` }, - ...annotations - } - ) - ) - -/** - * @category type id - * @since 0.67.0 - */ -export const LowercasedTypeId: unique symbol = Symbol.for("@effect/schema/TypeId/Lowercased") - -/** - * Verifies that a string is lowercased. - * - * @category string filters - * @since 0.67.0 - */ -export const lowercased = - (annotations?: Annotations.Filter) => (self: Schema): filter> => - self.pipe( - filter((a) => a === a.toLowerCase(), { - typeId: LowercasedTypeId, - description: "a lowercase string", - ...annotations - }) - ) - -/** - * @category string constructors - * @since 0.67.0 - */ -export class Lowercased extends String$.pipe( - lowercased({ identifier: "Lowercased", title: "Lowercased" }) -) {} - -/** - * @category type id - * @since 0.68.18 - */ -export const CapitalizedTypeId: unique symbol = Symbol.for("@effect/schema/TypeId/Capitalized") - -/** - * Verifies that a string is capitalized. - * - * @category string filters - * @since 0.68.18 - */ -export const capitalized = - (annotations?: Annotations.Filter) => (self: Schema): filter> => - self.pipe( - filter((a) => a[0]?.toUpperCase() === a[0], { - typeId: CapitalizedTypeId, - description: "a capitalized string", - ...annotations - }) - ) - -/** - * @category string constructors - * @since 0.68.18 - */ -export class Capitalized extends String$.pipe( - capitalized({ identifier: "Capitalized", title: "Capitalized" }) -) {} - -/** - * @category type id - * @since 0.68.18 - */ -export const UncapitalizedTypeId: unique symbol = Symbol.for("@effect/schema/TypeId/Uncapitalized") - -/** - * Verifies that a string is uncapitalized. - * - * @category string filters - * @since 0.68.18 - */ -export const uncapitalized = - (annotations?: Annotations.Filter) => (self: Schema): filter> => - self.pipe( - filter((a) => a[0]?.toLowerCase() === a[0], { - typeId: UncapitalizedTypeId, - description: "a uncapitalized string", - ...annotations - }) - ) - -/** - * @category string constructors - * @since 0.68.18 - */ -export class Uncapitalized extends String$.pipe( - uncapitalized({ identifier: "Uncapitalized", title: "Uncapitalized" }) -) {} - -/** - * @category type id - * @since 0.67.0 - */ -export const UppercasedTypeId: unique symbol = Symbol.for("@effect/schema/TypeId/Uppercased") - -/** - * Verifies that a string is uppercased. - * - * @category string filters - * @since 0.67.0 - */ -export const uppercased = - (annotations?: Annotations.Filter) => (self: Schema): filter> => - self.pipe( - filter((a) => a === a.toUpperCase(), { - typeId: UppercasedTypeId, - description: "an uppercase string", - ...annotations - }) - ) - -/** - * @category string constructors - * @since 0.67.0 - */ -export class Uppercased extends String$.pipe( - uppercased({ identifier: "Uppercased", title: "Uppercased" }) -) {} - -/** - * @category type id - * @since 0.67.0 - */ -export const LengthTypeId: unique symbol = filters_.LengthTypeId - -/** - * @category type id - * @since 0.67.0 - */ -export type LengthTypeId = typeof LengthTypeId - -/** - * @category string filters - * @since 0.67.0 - */ -export const length = ( - length: number | { readonly min: number; readonly max: number }, - annotations?: Annotations.Filter -) => -(self: Schema): filter> => { - const minLength = Predicate.isObject(length) ? Math.max(0, Math.floor(length.min)) : Math.max(0, Math.floor(length)) - const maxLength = Predicate.isObject(length) ? Math.max(minLength, Math.floor(length.max)) : minLength - if (minLength !== maxLength) { - return self.pipe( - filter((a) => a.length >= minLength && a.length <= maxLength, { - typeId: LengthTypeId, - description: `a string at least ${minLength} character(s) and at most ${maxLength} character(s) long`, - jsonSchema: { minLength, maxLength }, - ...annotations - }) - ) - } - return self.pipe( - filter((a) => a.length === minLength, { - typeId: LengthTypeId, - description: minLength === 1 ? `a single character` : `a string ${minLength} character(s) long`, - jsonSchema: { minLength, maxLength: minLength }, - ...annotations - }) - ) -} - -/** - * A schema representing a single character. - * - * @category string constructors - * @since 0.67.0 - */ -export class Char extends String$.pipe(length(1, { identifier: "Char" })) {} - -/** - * @category string filters - * @since 0.69.0 - */ -export const nonEmptyString = ( - annotations?: Annotations.Filter -): (self: Schema) => filter> => - minLength(1, { - description: "a non empty string", - ...annotations - }) - -/** - * This schema converts a string to lowercase. - * - * @category string transformations - * @since 0.67.0 - */ -export class Lowercase extends transform( - String$.annotations({ description: "a string that will be converted to lowercase" }), - Lowercased, - { strict: true, decode: (s) => s.toLowerCase(), encode: identity } -).annotations({ identifier: "Lowercase" }) {} - -/** - * This schema converts a string to uppercase. - * - * @category string transformations - * @since 0.67.0 - */ -export class Uppercase extends transform( - String$.annotations({ description: "a string that will be converted to uppercase" }), - Uppercased, - { strict: true, decode: (s) => s.toUpperCase(), encode: identity } -).annotations({ identifier: "Uppercase" }) {} - -/** - * This schema converts a string to capitalized one. - * - * @category string transformations - * @since 0.68.18 - */ -export class Capitalize extends transform( - String$.annotations({ description: "a string that will be converted to a capitalized format" }), - Capitalized, - { strict: true, decode: (s) => string_.capitalize(s), encode: identity } -).annotations({ identifier: "Capitalize" }) {} - -/** - * This schema converts a string to uncapitalized one. - * - * @category string transformations - * @since 0.68.18 - */ -export class Uncapitalize extends transform( - String$.annotations({ description: "a string that will be converted to an uncapitalized format" }), - Uncapitalized, - { strict: true, decode: (s) => string_.uncapitalize(s), encode: identity } -).annotations({ identifier: "Uncapitalize" }) {} - -/** - * @category string constructors - * @since 0.67.0 - */ -export class Trimmed extends String$.pipe( - trimmed({ identifier: "Trimmed", title: "Trimmed" }) -) {} - -/** - * Useful for validating strings that must contain meaningful characters without - * leading or trailing whitespace. - * - * @example - * import { Schema } from "@effect/schema" - * - * console.log(Schema.decodeOption(Schema.NonEmptyTrimmedString)("")) // Option.none() - * console.log(Schema.decodeOption(Schema.NonEmptyTrimmedString)(" a ")) // Option.none() - * console.log(Schema.decodeOption(Schema.NonEmptyTrimmedString)("a")) // Option.some("a") - * - * @category string constructors - * @since 0.69.3 - */ -export class NonEmptyTrimmedString extends Trimmed.pipe( - nonEmptyString({ identifier: "NonEmptyTrimmedString", title: "NonEmptyTrimmedString" }) -) {} - -/** - * This schema allows removing whitespaces from the beginning and end of a string. - * - * @category string transformations - * @since 0.67.0 - */ -export class Trim extends transform( - String$.annotations({ description: "a string that will be trimmed" }), - Trimmed, - { strict: true, decode: (s) => s.trim(), encode: identity } -).annotations({ identifier: "Trim" }) {} - -/** - * Returns a schema that allows splitting a string into an array of strings. - * - * @category string transformations - * @since 0.67.0 - */ -export const split = (separator: string): transform> => - transform( - String$.annotations({ description: "a string that will be split" }), - Array$(String$), - { strict: true, decode: string_.split(separator), encode: array_.join(separator) } - ) - -/** - * @since 0.67.0 - */ -export type ParseJsonOptions = { - readonly reviver?: Parameters[1] - readonly replacer?: Parameters[1] - readonly space?: Parameters[2] -} - -const JsonString = String$.annotations({ - [AST.IdentifierAnnotationId]: "JsonString", - [AST.TitleAnnotationId]: "JsonString", - [AST.DescriptionAnnotationId]: "a JSON string" -}) - -const getParseJsonTransformation = (options?: ParseJsonOptions) => - transformOrFail( - JsonString, - Unknown, - { - strict: true, - decode: (s, _, ast) => - ParseResult.try({ - try: () => JSON.parse(s, options?.reviver), - catch: (e: any) => new ParseResult.Type(ast, s, e.message) - }), - encode: (u, _, ast) => - ParseResult.try({ - try: () => JSON.stringify(u, options?.replacer, options?.space), - catch: (e: any) => new ParseResult.Type(ast, u, e.message) - }) - } - ).annotations({ typeId: filters_.ParseJsonTypeId }) - -/** - * The `ParseJson` combinator provides a method to convert JSON strings into the `unknown` type using the underlying - * functionality of `JSON.parse`. It also utilizes `JSON.stringify` for encoding. - * - * You can optionally provide a `ParseJsonOptions` to configure both `JSON.parse` and `JSON.stringify` executions. - * - * Optionally, you can pass a schema `Schema` to obtain an `A` type instead of `unknown`. - * - * @example - * import * as S from "@effect/schema/Schema" - * - * assert.deepStrictEqual(S.decodeUnknownSync(S.parseJson())(`{"a":"1"}`), { a: "1" }) - * assert.deepStrictEqual(S.decodeUnknownSync(S.parseJson(S.Struct({ a: S.NumberFromString })))(`{"a":"1"}`), { a: 1 }) - * - * @category string transformations - * @since 0.67.0 - */ -export const parseJson: { - (schema: Schema, options?: ParseJsonOptions): SchemaClass - (options?: ParseJsonOptions): SchemaClass -} = (schema?: Schema | ParseJsonOptions, o?: ParseJsonOptions) => - isSchema(schema) - ? compose(parseJson(o), schema) as any - : getParseJsonTransformation(schema as ParseJsonOptions | undefined) - -/** - * @category string constructors - * @since 0.69.0 - */ -export class NonEmptyString extends String$.pipe( - nonEmptyString({ identifier: "NonEmptyString", title: "NonEmptyString" }) -) {} - -/** - * @category type id - * @since 0.67.0 - */ -export const UUIDTypeId: unique symbol = Symbol.for("@effect/schema/TypeId/UUID") - -const uuidRegexp = /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/i - -/** - * Represents a Universally Unique Identifier (UUID). - * - * This schema ensures that the provided string adheres to the standard UUID format. - * - * @category string constructors - * @since 0.67.0 - */ -export class UUID extends String$.pipe( - pattern(uuidRegexp, { - typeId: UUIDTypeId, - identifier: "UUID", - title: "UUID", - description: "a Universally Unique Identifier", - arbitrary: (): LazyArbitrary => (fc) => fc.uuid() - }) -) {} - -/** - * @category type id - * @since 0.67.0 - */ -export const ULIDTypeId: unique symbol = Symbol.for("@effect/schema/TypeId/ULID") - -const ulidRegexp = /^[0-7][0-9A-HJKMNP-TV-Z]{25}$/i - -/** - * Represents a Universally Unique Lexicographically Sortable Identifier (ULID). - * - * ULIDs are designed to be compact, URL-safe, and ordered, making them suitable for use as identifiers. - * This schema ensures that the provided string adheres to the standard ULID format. - * - * @category string constructors - * @since 0.67.0 - */ -export class ULID extends String$.pipe( - pattern(ulidRegexp, { - typeId: ULIDTypeId, - identifier: "ULID", - title: "ULID", - description: "a Universally Unique Lexicographically Sortable Identifier", - arbitrary: (): LazyArbitrary => (fc) => fc.ulid() - }) -) {} - -/** - * @category type id - * @since 0.67.0 - */ -export const FiniteTypeId: unique symbol = Symbol.for("@effect/schema/TypeId/Finite") - -/** - * Ensures that the provided value is a finite number. - * - * This schema filters out non-finite numeric values, allowing only finite numbers to pass through. - * - * @category number filters - * @since 0.67.0 - */ -export const finite = - (annotations?: Annotations.Filter) => (self: Schema): filter> => - self.pipe( - filter((a) => Number.isFinite(a), { - typeId: FiniteTypeId, - description: "a finite number", - ...annotations - }) - ) - -/** - * @category type id - * @since 0.67.0 - */ -export const GreaterThanTypeId: unique symbol = filters_.GreaterThanTypeId - -/** - * @category type id - * @since 0.67.0 - */ -export type GreaterThanTypeId = typeof GreaterThanTypeId - -/** - * This filter checks whether the provided number is greater than the specified minimum. - * - * @category number filters - * @since 0.67.0 - */ -export const greaterThan = ( - min: number, - annotations?: Annotations.Filter -) => -(self: Schema): filter> => - self.pipe( - filter((a) => a > min, { - typeId: GreaterThanTypeId, - description: min === 0 ? "a positive number" : `a number greater than ${min}`, - jsonSchema: { exclusiveMinimum: min }, - ...annotations - }) - ) - -/** - * @category type id - * @since 0.67.0 - */ -export const GreaterThanOrEqualToTypeId: unique symbol = filters_.GreaterThanOrEqualToTypeId - -/** - * @category type id - * @since 0.67.0 - */ -export type GreaterThanOrEqualToTypeId = typeof GreaterThanOrEqualToTypeId - -/** - * This filter checks whether the provided number is greater than or equal to the specified minimum. - * - * @category number filters - * @since 0.67.0 - */ -export const greaterThanOrEqualTo = ( - min: number, - annotations?: Annotations.Filter -) => -(self: Schema): filter> => - self.pipe( - filter((a) => a >= min, { - typeId: GreaterThanOrEqualToTypeId, - description: min === 0 ? "a non-negative number" : `a number greater than or equal to ${min}`, - jsonSchema: { minimum: min }, - ...annotations - }) - ) - -/** - * @category type id - * @since 0.67.0 - */ -export const MultipleOfTypeId: unique symbol = Symbol.for("@effect/schema/TypeId/MultipleOf") - -/** - * @category number filters - * @since 0.67.0 - */ -export const multipleOf = ( - divisor: number, - annotations?: Annotations.Filter -) => -(self: Schema): filter> => - self.pipe( - filter((a) => number_.remainder(a, divisor) === 0, { - typeId: MultipleOfTypeId, - description: `a number divisible by ${divisor}`, - jsonSchema: { multipleOf: Math.abs(divisor) }, // spec requires positive divisor - ...annotations - }) - ) - -/** - * @category type id - * @since 0.67.0 - */ -export const IntTypeId: unique symbol = filters_.IntTypeId - -/** - * @category type id - * @since 0.67.0 - */ -export type IntTypeId = typeof IntTypeId - -/** - * @category number filters - * @since 0.67.0 - */ -export const int = - (annotations?: Annotations.Filter) => (self: Schema): filter> => - self.pipe( - filter((a) => Number.isSafeInteger(a), { - typeId: IntTypeId, - title: "integer", - description: "an integer", - jsonSchema: { type: "integer" }, - ...annotations - }) - ) - -/** - * @category type id - * @since 0.67.0 - */ -export const LessThanTypeId: unique symbol = filters_.LessThanTypeId - -/** - * @category type id - * @since 0.67.0 - */ -export type LessThanTypeId = typeof LessThanTypeId - -/** - * This filter checks whether the provided number is less than the specified maximum. - * - * @category number filters - * @since 0.67.0 - */ -export const lessThan = - (max: number, annotations?: Annotations.Filter) => - (self: Schema): filter> => - self.pipe( - filter((a) => a < max, { - typeId: LessThanTypeId, - description: max === 0 ? "a negative number" : `a number less than ${max}`, - jsonSchema: { exclusiveMaximum: max }, - ...annotations - }) - ) - -/** - * @category type id - * @since 0.67.0 - */ -export const LessThanOrEqualToTypeId: unique symbol = filters_.LessThanOrEqualToTypeId - -/** - * @category type id - * @since 0.67.0 - */ -export type LessThanOrEqualToTypeId = typeof LessThanOrEqualToTypeId - -/** - * This schema checks whether the provided number is less than or equal to the specified maximum. - * - * @category number filters - * @since 0.67.0 - */ -export const lessThanOrEqualTo = ( - max: number, - annotations?: Annotations.Filter -) => -(self: Schema): filter> => - self.pipe( - filter((a) => a <= max, { - typeId: LessThanOrEqualToTypeId, - description: max === 0 ? "a non-positive number" : `a number less than or equal to ${max}`, - jsonSchema: { maximum: max }, - ...annotations - }) - ) - -/** - * @category type id - * @since 0.67.0 - */ -export const BetweenTypeId: unique symbol = filters_.BetweenTypeId - -/** - * @category type id - * @since 0.67.0 - */ -export type BetweenTypeId = typeof BetweenTypeId - -/** - * This filter checks whether the provided number falls within the specified minimum and maximum values. - * - * @category number filters - * @since 0.67.0 - */ -export const between = ( - min: number, - max: number, - annotations?: Annotations.Filter -) => -(self: Schema): filter> => - self.pipe( - filter((a) => a >= min && a <= max, { - typeId: BetweenTypeId, - description: `a number between ${min} and ${max}`, - jsonSchema: { maximum: max, minimum: min }, - ...annotations - }) - ) - -/** - * @category type id - * @since 0.67.0 - */ -export const NonNaNTypeId: unique symbol = Symbol.for("@effect/schema/TypeId/NonNaN") - -/** - * @category number filters - * @since 0.67.0 - */ -export const nonNaN = - (annotations?: Annotations.Filter) => (self: Schema): filter> => - self.pipe( - filter((a) => !Number.isNaN(a), { - typeId: NonNaNTypeId, - description: "a number excluding NaN", - ...annotations - }) - ) - -/** - * @category number filters - * @since 0.67.0 - */ -export const positive = ( - annotations?: Annotations.Filter -): (self: Schema) => filter> => greaterThan(0, annotations) - -/** - * @category number filters - * @since 0.67.0 - */ -export const negative = ( - annotations?: Annotations.Filter -): (self: Schema) => filter> => lessThan(0, annotations) - -/** - * @category number filters - * @since 0.67.0 - */ -export const nonPositive = ( - annotations?: Annotations.Filter -): (self: Schema) => filter> => lessThanOrEqualTo(0, annotations) - -/** - * @category number filters - * @since 0.67.0 - */ -export const nonNegative = ( - annotations?: Annotations.Filter -): (self: Schema) => filter> => greaterThanOrEqualTo(0, annotations) - -/** - * Clamps a number between a minimum and a maximum value. - * - * @category number transformations - * @since 0.67.0 - */ -export const clamp = - (minimum: number, maximum: number) => - (self: Schema): transform, filter>> => - transform( - self, - self.pipe(typeSchema, between(minimum, maximum)), - { strict: false, decode: (self) => number_.clamp(self, { minimum, maximum }), encode: identity } - ) - -/** - * Transforms a `string` into a `number` by parsing the string using the `parse` function of the `effect/Number` module. - * - * It returns an error if the value can't be converted (for example when non-numeric characters are provided). - * - * The following special string values are supported: "NaN", "Infinity", "-Infinity". - * - * @category number transformations - * @since 0.67.0 - */ -export const parseNumber = ( - self: Schema -): transformOrFail, typeof Number$> => - transformOrFail( - self, - Number$, - { - strict: false, - decode: (s, _, ast) => ParseResult.fromOption(number_.parse(s), () => new ParseResult.Type(ast, s)), - encode: (n) => ParseResult.succeed(String(n)) - } - ) - -/** - * This schema transforms a `string` into a `number` by parsing the string using the `parse` function of the `effect/Number` module. - * - * It returns an error if the value can't be converted (for example when non-numeric characters are provided). - * - * The following special string values are supported: "NaN", "Infinity", "-Infinity". - * - * @category number constructors - * @since 0.67.0 - */ -export class NumberFromString extends parseNumber(String$.annotations({ - description: "a string that will be parsed into a number" -})).annotations({ identifier: "NumberFromString" }) {} - -/** - * @category number constructors - * @since 0.67.0 - */ -export class Finite extends Number$.pipe(finite({ identifier: "Finite", title: "Finite" })) {} - -/** - * @category number constructors - * @since 0.67.0 - */ -export class Int extends Number$.pipe(int({ identifier: "Int", title: "Int" })) {} - -/** - * @category number constructors - * @since 0.67.0 - */ -export class NonNaN extends Number$.pipe(nonNaN({ identifier: "NonNaN", title: "NonNaN" })) {} - -/** - * @category number constructors - * @since 0.67.0 - */ -export class Positive extends Number$.pipe( - positive({ identifier: "Positive", title: "Positive" }) -) {} - -/** - * @category number constructors - * @since 0.67.0 - */ -export class Negative extends Number$.pipe( - negative({ identifier: "Negative", title: "Negative" }) -) {} - -/** - * @category number constructors - * @since 0.67.0 - */ -export class NonPositive extends Number$.pipe( - nonPositive({ identifier: "NonPositive", title: "NonPositive" }) -) {} - -/** - * @category number constructors - * @since 0.67.0 - */ -export class NonNegative extends Number$.pipe( - nonNegative({ identifier: "NonNegative", title: "NonNegative" }) -) {} - -/** - * @category type id - * @since 0.67.0 - */ -export const JsonNumberTypeId: unique symbol = Symbol.for("@effect/schema/TypeId/JsonNumber") - -/** - * The `JsonNumber` is a schema for representing JSON numbers. It ensures that the provided value is a valid - * number by filtering out `NaN` and `(+/-) Infinity`. This is useful when you want to validate and represent numbers in JSON - * format. - * - * @example - * import * as S from "@effect/schema/Schema" - * - * const is = S.is(S.JsonNumber) - * - * assert.deepStrictEqual(is(42), true) - * assert.deepStrictEqual(is(Number.NaN), false) - * assert.deepStrictEqual(is(Number.POSITIVE_INFINITY), false) - * assert.deepStrictEqual(is(Number.NEGATIVE_INFINITY), false) - * - * @category number constructors - * @since 0.67.0 - */ -export class JsonNumber extends Number$.pipe( - filter((n) => !Number.isNaN(n) && Number.isFinite(n), { - typeId: JsonNumberTypeId, - identifier: "JsonNumber", - title: "JSON-compatible number", - description: "a JSON-compatible number, excluding NaN, +Infinity, and -Infinity", - jsonSchema: { type: "number" } - }) -) {} - -/** - * @category boolean transformations - * @since 0.67.0 - */ -export class Not extends transform(Boolean$.annotations({ description: "a boolean that will be negated" }), Boolean$, { - strict: true, - decode: boolean_.not, - encode: boolean_.not -}) {} - -/** @ignore */ -class Symbol$ extends transform( - String$.annotations({ description: "a string that will be converted to a symbol" }), - SymbolFromSelf, - { strict: false, decode: (s) => Symbol.for(s), encode: (sym) => sym.description } -).annotations({ identifier: "symbol" }) {} - -export { - /** - * This schema transforms a `string` into a `symbol`. - * - * @category symbol transformations - * @since 0.67.0 - */ - Symbol$ as Symbol -} - -/** - * @category type id - * @since 0.67.0 - */ -export const GreaterThanBigIntTypeId: unique symbol = filters_.GreaterThanBigintTypeId - -/** - * @category type id - * @since 0.67.0 - */ -export type GreaterThanBigIntTypeId = typeof GreaterThanBigIntTypeId - -/** - * @category bigint filters - * @since 0.67.0 - */ -export const greaterThanBigInt = ( - min: bigint, - annotations?: Annotations.Filter -) => -(self: Schema): filter> => - self.pipe( - filter((a) => a > min, { - typeId: { id: GreaterThanBigIntTypeId, annotation: { min } }, - description: min === 0n ? "a positive bigint" : `a bigint greater than ${min}n`, - ...annotations - }) - ) - -/** - * @category type id - * @since 0.67.0 - */ -export const GreaterThanOrEqualToBigIntTypeId: unique symbol = filters_.GreaterThanOrEqualToBigIntTypeId - -/** - * @category type id - * @since 0.67.0 - */ -export type GreaterThanOrEqualToBigIntTypeId = typeof GreaterThanOrEqualToBigIntTypeId - -/** - * @category bigint filters - * @since 0.67.0 - */ -export const greaterThanOrEqualToBigInt = ( - min: bigint, - annotations?: Annotations.Filter -) => -(self: Schema): filter> => - self.pipe( - filter((a) => a >= min, { - typeId: { id: GreaterThanOrEqualToBigIntTypeId, annotation: { min } }, - description: min === 0n - ? "a non-negative bigint" - : `a bigint greater than or equal to ${min}n`, - ...annotations - }) - ) - -/** - * @category type id - * @since 0.67.0 - */ -export const LessThanBigIntTypeId: unique symbol = filters_.LessThanBigIntTypeId - -/** - * @category type id - * @since 0.67.0 - */ -export type LessThanBigIntTypeId = typeof LessThanBigIntTypeId - -/** - * @category bigint filters - * @since 0.67.0 - */ -export const lessThanBigInt = ( - max: bigint, - annotations?: Annotations.Filter -) => -(self: Schema): filter> => - self.pipe( - filter((a) => a < max, { - typeId: { id: LessThanBigIntTypeId, annotation: { max } }, - description: max === 0n ? "a negative bigint" : `a bigint less than ${max}n`, - ...annotations - }) - ) - -/** - * @category type id - * @since 0.67.0 - */ -export const LessThanOrEqualToBigIntTypeId: unique symbol = filters_.LessThanOrEqualToBigIntTypeId - -/** - * @category type id - * @since 0.67.0 - */ -export type LessThanOrEqualToBigIntTypeId = typeof LessThanOrEqualToBigIntTypeId - -/** - * @category bigint filters - * @since 0.67.0 - */ -export const lessThanOrEqualToBigInt = ( - max: bigint, - annotations?: Annotations.Filter -) => -(self: Schema): filter> => - self.pipe( - filter((a) => a <= max, { - typeId: { id: LessThanOrEqualToBigIntTypeId, annotation: { max } }, - description: max === 0n ? "a non-positive bigint" : `a bigint less than or equal to ${max}n`, - ...annotations - }) - ) - -/** - * @category type id - * @since 0.67.0 - */ -export const BetweenBigIntTypeId: unique symbol = filters_.BetweenBigintTypeId - -/** - * @category type id - * @since 0.67.0 - */ -export type BetweenBigIntTypeId = typeof BetweenBigIntTypeId - -/** - * @category bigint filters - * @since 0.67.0 - */ -export const betweenBigInt = ( - min: bigint, - max: bigint, - annotations?: Annotations.Filter -) => -(self: Schema): filter> => - self.pipe( - filter((a) => a >= min && a <= max, { - typeId: { id: BetweenBigIntTypeId, annotation: { max, min } }, - description: `a bigint between ${min}n and ${max}n`, - ...annotations - }) - ) - -/** - * @category bigint filters - * @since 0.67.0 - */ -export const positiveBigInt = ( - annotations?: Annotations.Filter -): (self: Schema) => filter> => greaterThanBigInt(0n, annotations) - -/** - * @category bigint filters - * @since 0.67.0 - */ -export const negativeBigInt = ( - annotations?: Annotations.Filter -): (self: Schema) => filter> => lessThanBigInt(0n, annotations) - -/** - * @category bigint filters - * @since 0.67.0 - */ -export const nonNegativeBigInt = ( - annotations?: Annotations.Filter -): (self: Schema) => filter> => greaterThanOrEqualToBigInt(0n, annotations) - -/** - * @category bigint filters - * @since 0.67.0 - */ -export const nonPositiveBigInt = ( - annotations?: Annotations.Filter -): (self: Schema) => filter> => lessThanOrEqualToBigInt(0n, annotations) - -/** - * Clamps a bigint between a minimum and a maximum value. - * - * @category bigint transformations - * @since 0.67.0 - */ -export const clampBigInt = - (minimum: bigint, maximum: bigint) => - (self: Schema): transform, filter>> => - transform( - self, - self.pipe(typeSchema, betweenBigInt(minimum, maximum)), - { strict: false, decode: (self) => bigInt_.clamp(self, { minimum, maximum }), encode: identity } - ) - -/** @ignore */ -class BigInt$ extends transformOrFail( - String$.annotations({ description: "a string that will be parsed into a bigint" }), - BigIntFromSelf, - { - strict: true, - decode: (s, _, ast) => ParseResult.fromOption(bigInt_.fromString(s), () => new ParseResult.Type(ast, s)), - encode: (n) => ParseResult.succeed(String(n)) - } -).annotations({ identifier: "bigint" }) {} - -export { - /** - * This schema transforms a `string` into a `bigint` by parsing the string using the `BigInt` function. - * - * It returns an error if the value can't be converted (for example when non-numeric characters are provided). - * - * @category bigint transformations - * @since 0.67.0 - */ - BigInt$ as BigInt -} - -/** - * @category bigint constructors - * @since 0.67.0 - */ -export const PositiveBigIntFromSelf: filter> = BigIntFromSelf.pipe( - positiveBigInt({ identifier: "PositiveBigintFromSelf", title: "PositiveBigintFromSelf" }) -) - -/** - * @category bigint constructors - * @since 0.67.0 - */ -export const PositiveBigInt: filter> = BigInt$.pipe( - positiveBigInt({ identifier: "PositiveBigint", title: "PositiveBigint" }) -) - -/** - * @category bigint constructors - * @since 0.67.0 - */ -export const NegativeBigIntFromSelf: filter> = BigIntFromSelf.pipe( - negativeBigInt({ identifier: "NegativeBigintFromSelf", title: "NegativeBigintFromSelf" }) -) - -/** - * @category bigint constructors - * @since 0.67.0 - */ -export const NegativeBigInt: filter> = BigInt$.pipe( - negativeBigInt({ identifier: "NegativeBigint", title: "NegativeBigint" }) -) - -/** - * @category bigint constructors - * @since 0.67.0 - */ -export const NonPositiveBigIntFromSelf: filter> = BigIntFromSelf.pipe( - nonPositiveBigInt({ identifier: "NonPositiveBigintFromSelf", title: "NonPositiveBigintFromSelf" }) -) - -/** - * @category bigint constructors - * @since 0.67.0 - */ -export const NonPositiveBigInt: filter> = BigInt$.pipe( - nonPositiveBigInt({ identifier: "NonPositiveBigint", title: "NonPositiveBigint" }) -) - -/** - * @category bigint constructors - * @since 0.67.0 - */ -export const NonNegativeBigIntFromSelf: filter> = BigIntFromSelf.pipe( - nonNegativeBigInt({ identifier: "NonNegativeBigintFromSelf", title: "NonNegativeBigintFromSelf" }) -) - -/** - * @category bigint constructors - * @since 0.67.0 - */ -export const NonNegativeBigInt: filter> = BigInt$.pipe( - nonNegativeBigInt({ identifier: "NonNegativeBigint", title: "NonNegativeBigint" }) -) - -/** - * This schema transforms a `number` into a `bigint` by parsing the number using the `BigInt` function. - * - * It returns an error if the value can't be safely encoded as a `number` due to being out of range. - * - * @category bigint transformations - * @since 0.67.0 - */ -export class BigIntFromNumber extends transformOrFail( - Number$.annotations({ description: "a number that will be parsed into a bigint" }), - BigIntFromSelf, - { - strict: true, - decode: (n, _, ast) => - ParseResult.fromOption( - bigInt_.fromNumber(n), - () => new ParseResult.Type(ast, n) - ), - encode: (b, _, ast) => ParseResult.fromOption(bigInt_.toNumber(b), () => new ParseResult.Type(ast, b)) - } -).annotations({ identifier: "BigintFromNumber" }) {} - -const redactedArbitrary = (value: LazyArbitrary): LazyArbitrary> => (fc) => - value(fc).map(redacted_.make) - -const toComposite = ( - eff: Effect.Effect, - onSuccess: (a: A) => B, - ast: AST.AST, - actual: unknown -): Effect.Effect => - ParseResult.mapBoth(eff, { - onFailure: (e) => new ParseResult.Composite(ast, actual, e), - onSuccess - }) - -const redactedParse = ( - decodeUnknown: ParseResult.DecodeUnknown -): ParseResult.DeclarationDecodeUnknown, R> => -(u, options, ast) => - redacted_.isRedacted(u) ? - toComposite(decodeUnknown(redacted_.value(u), options), redacted_.make, ast, u) : - ParseResult.fail(new ParseResult.Type(ast, u)) - -/** - * @category api interface - * @since 0.67.21 - */ -export interface RedactedFromSelf extends - AnnotableClass< - RedactedFromSelf, - redacted_.Redacted>, - redacted_.Redacted>, - Schema.Context - > -{} - -/** - * @category Redacted constructors - * @since 0.67.21 - */ -export const RedactedFromSelf = ( - value: Value -): RedactedFromSelf => - declare( - [value], - { - decode: (value) => redactedParse(ParseResult.decodeUnknown(value)), - encode: (value) => redactedParse(ParseResult.encodeUnknown(value)) - }, - { - description: "Redacted()", - pretty: () => () => "Redacted()", - arbitrary: redactedArbitrary, - equivalence: redacted_.getEquivalence - } - ) - -/** - * @category api interface - * @since 0.67.21 - */ -export interface Redacted extends - AnnotableClass< - Redacted, - redacted_.Redacted>, - Schema.Encoded, - Schema.Context - > -{} - -/** - * A schema that transforms any type `A` into a `Redacted`. - * - * @category Redacted transformations - * @since 0.67.21 - */ -export const Redacted = ( - value: Value -): Redacted => { - return transform( - value, - RedactedFromSelf(typeSchema(value)), - { - strict: true, - decode: (value) => redacted_.make(value), - encode: (value) => redacted_.value(value) - } - ) -} - -/** - * @category Duration constructors - * @since 0.67.0 - */ -export class DurationFromSelf extends declare( - duration_.isDuration, - { - identifier: "DurationFromSelf", - pretty: (): pretty_.Pretty => String, - arbitrary: (): LazyArbitrary => (fc) => - fc.oneof( - fc.constant(duration_.infinity), - fc.bigUint().map((_) => duration_.nanos(_)), - fc.bigUint().map((_) => duration_.micros(_)), - fc.maxSafeNat().map((_) => duration_.millis(_)), - fc.maxSafeNat().map((_) => duration_.seconds(_)), - fc.maxSafeNat().map((_) => duration_.minutes(_)), - fc.maxSafeNat().map((_) => duration_.hours(_)), - fc.maxSafeNat().map((_) => duration_.days(_)), - fc.maxSafeNat().map((_) => duration_.weeks(_)) - ), - equivalence: (): Equivalence.Equivalence => duration_.Equivalence - } -) {} - -/** - * A schema that transforms a `bigint` tuple into a `Duration`. - * Treats the value as the number of nanoseconds. - * - * @category Duration transformations - * @since 0.67.0 - */ -export class DurationFromNanos extends transformOrFail( - BigIntFromSelf.annotations({ description: "a bigint that will be parsed into a Duration" }), - DurationFromSelf, - { - strict: true, - decode: (nanos) => ParseResult.succeed(duration_.nanos(nanos)), - encode: (duration, _, ast) => - option_.match(duration_.toNanos(duration), { - onNone: () => ParseResult.fail(new ParseResult.Type(ast, duration)), - onSome: (val) => ParseResult.succeed(val) - }) - } -).annotations({ identifier: "DurationFromNanos" }) {} - -/** - * A schema that transforms a `number` tuple into a `Duration`. - * Treats the value as the number of milliseconds. - * - * @category Duration transformations - * @since 0.67.0 - */ -export class DurationFromMillis extends transform( - Number$.annotations({ description: "a number that will be parsed into a Duration" }), - DurationFromSelf, - { strict: true, decode: (ms) => duration_.millis(ms), encode: (n) => duration_.toMillis(n) } -).annotations({ identifier: "DurationFromMillis" }) {} - -const hrTime: Schema = Tuple( - NonNegative.pipe( - finite({ - [AST.TitleAnnotationId]: "seconds", - [AST.DescriptionAnnotationId]: "seconds" - }) - ), - NonNegative.pipe( - finite({ - [AST.TitleAnnotationId]: "nanos", - [AST.DescriptionAnnotationId]: "nanos" - }) - ) -) - -/** - * A schema that transforms a `[number, number]` tuple into a `Duration`. - * - * @category Duration transformations - * @since 0.67.0 - */ -export class Duration extends transform( - hrTime.annotations({ description: "a tuple of seconds and nanos that will be parsed into a Duration" }), - DurationFromSelf, - { - strict: true, - decode: ([seconds, nanos]) => duration_.nanos(BigInt(seconds) * BigInt(1e9) + BigInt(nanos)), - encode: (duration) => duration_.toHrTime(duration) - } -).annotations({ identifier: "Duration" }) {} - -/** - * Clamps a `Duration` between a minimum and a maximum value. - * - * @category Duration transformations - * @since 0.67.0 - */ -export const clampDuration = - (minimum: duration_.DurationInput, maximum: duration_.DurationInput) => - (self: Schema): transform, filter>> => - transform( - self, - self.pipe(typeSchema, betweenDuration(minimum, maximum)), - { strict: false, decode: (self) => duration_.clamp(self, { minimum, maximum }), encode: identity } - ) - -/** - * @category type id - * @since 0.67.0 - */ -export const LessThanDurationTypeId: unique symbol = Symbol.for("@effect/schema/TypeId/LessThanDuration") - -/** - * @category Duration filters - * @since 0.67.0 - */ -export const lessThanDuration = ( - max: duration_.DurationInput, - annotations?: Annotations.Filter -) => -(self: Schema): filter> => - self.pipe( - filter((a) => duration_.lessThan(a, max), { - typeId: { id: LessThanDurationTypeId, annotation: { max } }, - description: `a Duration less than ${duration_.decode(max)}`, - ...annotations - }) - ) - -/** - * @category type id - * @since 0.67.0 - */ -export const LessThanOrEqualToDurationTypeId: unique symbol = Symbol.for( - "@effect/schema/TypeId/LessThanOrEqualToDuration" -) - -/** - * @category Duration filters - * @since 0.67.0 - */ -export const lessThanOrEqualToDuration = ( - max: duration_.DurationInput, - annotations?: Annotations.Filter -) => -(self: Schema): filter> => - self.pipe( - filter((a) => duration_.lessThanOrEqualTo(a, max), { - typeId: { id: LessThanDurationTypeId, annotation: { max } }, - description: `a Duration less than or equal to ${duration_.decode(max)}`, - ...annotations - }) - ) - -/** - * @category type id - * @since 0.67.0 - */ -export const GreaterThanDurationTypeId: unique symbol = Symbol.for("@effect/schema/TypeId/GreaterThanDuration") - -/** - * @category Duration filters - * @since 0.67.0 - */ -export const greaterThanDuration = ( - min: duration_.DurationInput, - annotations?: Annotations.Filter -) => -(self: Schema): filter> => - self.pipe( - filter((a) => duration_.greaterThan(a, min), { - typeId: { id: GreaterThanDurationTypeId, annotation: { min } }, - description: `a Duration greater than ${duration_.decode(min)}`, - ...annotations - }) - ) - -/** - * @category type id - * @since 0.67.0 - */ -export const GreaterThanOrEqualToDurationTypeId: unique symbol = Symbol.for( - "@effect/schema/TypeId/GreaterThanOrEqualToDuration" -) - -/** - * @category Duration filters - * @since 0.67.0 - */ -export const greaterThanOrEqualToDuration = ( - min: duration_.DurationInput, - annotations?: Annotations.Filter -) => -(self: Schema): filter> => - self.pipe( - filter((a) => duration_.greaterThanOrEqualTo(a, min), { - typeId: { id: GreaterThanOrEqualToDurationTypeId, annotation: { min } }, - description: `a Duration greater than or equal to ${duration_.decode(min)}`, - ...annotations - }) - ) - -/** - * @category type id - * @since 0.67.0 - */ -export const BetweenDurationTypeId: unique symbol = Symbol.for("@effect/schema/TypeId/BetweenDuration") - -/** - * @category Duration filters - * @since 0.67.0 - */ -export const betweenDuration = ( - minimum: duration_.DurationInput, - maximum: duration_.DurationInput, - annotations?: Annotations.Filter -) => -(self: Schema): filter> => - self.pipe( - filter((a) => duration_.between(a, { minimum, maximum }), { - typeId: { id: BetweenDurationTypeId, annotation: { maximum, minimum } }, - description: `a Duration between ${duration_.decode(minimum)} and ${duration_.decode(maximum)}`, - ...annotations - }) - ) - -/** - * @category Uint8Array constructors - * @since 0.67.0 - */ -export const Uint8ArrayFromSelf: Schema = declare( - Predicate.isUint8Array, - { - identifier: "Uint8ArrayFromSelf", - pretty: (): pretty_.Pretty => (u8arr) => `new Uint8Array(${JSON.stringify(Array.from(u8arr))})`, - arbitrary: (): LazyArbitrary => (fc) => fc.uint8Array(), - equivalence: (): Equivalence.Equivalence => array_.getEquivalence(Equal.equals) as any - } -) - -const Uint8Array$: Schema> = transform( - Array$(Number$.pipe( - between(0, 255, { - title: "8-bit unsigned integer", - description: "a 8-bit unsigned integer" - }) - )).annotations({ description: "an array of 8-bit unsigned integers that will be parsed into a Uint8Array" }), - Uint8ArrayFromSelf, - { strict: true, decode: (numbers) => Uint8Array.from(numbers), encode: (uint8Array) => Array.from(uint8Array) } -).annotations({ identifier: "Uint8Array" }) - -export { - /** - * A schema that transforms an array of numbers into a `Uint8Array`. - * - * @category Uint8Array transformations - * @since 0.67.0 - */ - Uint8Array$ as Uint8Array -} - -const makeUint8ArrayTransformation = ( - id: string, - decode: (s: string) => either_.Either, - encode: (u: Uint8Array) => string -) => - transformOrFail( - String$.annotations({ description: "a string that will be parsed into a Uint8Array" }), - Uint8ArrayFromSelf, - { - strict: true, - decode: (s, _, ast) => - either_.mapLeft( - decode(s), - (decodeException) => new ParseResult.Type(ast, s, decodeException.message) - ), - encode: (u) => ParseResult.succeed(encode(u)) - } - ).annotations({ identifier: id }) - -/** - * Decodes a base64 (RFC4648) encoded string into a `Uint8Array`. - * - * @category Uint8Array transformations - * @since 0.67.0 - */ -export const Uint8ArrayFromBase64: Schema = makeUint8ArrayTransformation( - "Uint8ArrayFromBase64", - Encoding.decodeBase64, - Encoding.encodeBase64 -) - -/** - * Decodes a base64 (URL) encoded string into a `Uint8Array`. - * - * @category Uint8Array transformations - * @since 0.67.0 - */ -export const Uint8ArrayFromBase64Url: Schema = makeUint8ArrayTransformation( - "Uint8ArrayFromBase64Url", - Encoding.decodeBase64Url, - Encoding.encodeBase64Url -) - -/** - * Decodes a hex encoded string into a `Uint8Array`. - * - * @category Uint8Array transformations - * @since 0.67.0 - */ -export const Uint8ArrayFromHex: Schema = makeUint8ArrayTransformation( - "Uint8ArrayFromHex", - Encoding.decodeHex, - Encoding.encodeHex -) - -const makeEncodingTransformation = ( - id: string, - decode: (s: string) => either_.Either, - encode: (u: string) => string -) => - transformOrFail( - String$.annotations({ - description: `A string that is interpreted as being ${id}-encoded and will be decoded into a UTF-8 string` - }), - String$, - { - strict: true, - decode: (s, _, ast) => - either_.mapLeft( - decode(s), - (decodeException) => new ParseResult.Type(ast, s, decodeException.message) - ), - encode: (u) => ParseResult.succeed(encode(u)) - } - ).annotations({ identifier: `StringFrom${id}` }) - -/** - * Decodes a base64 (RFC4648) encoded string into a UTF-8 string. - * - * @category string transformations - * @since 0.67.0 - */ -export const StringFromBase64: Schema = makeEncodingTransformation( - "Base64", - Encoding.decodeBase64String, - Encoding.encodeBase64 -) - -/** - * Decodes a base64 (URL) encoded string into a UTF-8 string. - * - * @category string transformations - * @since 0.67.0 - */ -export const StringFromBase64Url: Schema = makeEncodingTransformation( - "Base64Url", - Encoding.decodeBase64UrlString, - Encoding.encodeBase64Url -) - -/** - * Decodes a hex encoded string into a UTF-8 string. - * - * @category string transformations - * @since 0.67.0 - */ -export const StringFromHex: Schema = makeEncodingTransformation( - "Hex", - Encoding.decodeHexString, - Encoding.encodeHex -) - -/** - * @category type id - * @since 0.67.0 - */ -export const MinItemsTypeId: unique symbol = filters_.MinItemsTypeId - -/** - * @category type id - * @since 0.67.0 - */ -export type MinItemsTypeId = typeof MinItemsTypeId - -/** - * @category ReadonlyArray filters - * @since 0.67.0 - */ -export const minItems = ( - n: number, - annotations?: Annotations.Filter> -) => -(self: Schema, I, R>): filter, I, R>> => { - const minItems = Math.floor(n) - if (minItems < 1) { - throw new Error( - errors_.getInvalidArgumentErrorMessage(`Expected an integer greater than or equal to 1, actual ${n}`) - ) - } - return self.pipe( - filter( - (a) => a.length >= minItems, - { - typeId: MinItemsTypeId, - description: `an array of at least ${minItems} items`, - jsonSchema: { minItems }, - [AST.StableFilterAnnotationId]: true, - ...annotations - } - ) - ) -} - -/** - * @category type id - * @since 0.67.0 - */ -export const MaxItemsTypeId: unique symbol = filters_.MaxItemsTypeId - -/** - * @category type id - * @since 0.67.0 - */ -export type MaxItemsTypeId = typeof MaxItemsTypeId - -/** - * @category ReadonlyArray filters - * @since 0.67.0 - */ -export const maxItems = ( - n: number, - annotations?: Annotations.Filter> -) => -(self: Schema, I, R>): filter, I, R>> => - self.pipe( - filter((a) => a.length <= n, { - typeId: MaxItemsTypeId, - description: `an array of at most ${n} items`, - jsonSchema: { maxItems: n }, - [AST.StableFilterAnnotationId]: true, - ...annotations - }) - ) - -/** - * @category type id - * @since 0.67.0 - */ -export const ItemsCountTypeId: unique symbol = filters_.ItemsCountTypeId - -/** - * @category type id - * @since 0.67.0 - */ -export type ItemsCountTypeId = typeof ItemsCountTypeId - -/** - * @category ReadonlyArray filters - * @since 0.67.0 - */ -export const itemsCount = ( - n: number, - annotations?: Annotations.Filter> -) => -(self: Schema, I, R>): filter, I, R>> => - self.pipe( - filter((a) => a.length === n, { - typeId: ItemsCountTypeId, - description: `an array of exactly ${n} item(s)`, - jsonSchema: { minItems: n, maxItems: n }, - [AST.StableFilterAnnotationId]: true, - ...annotations - }) - ) - -/** - * @category ReadonlyArray transformations - * @since 0.67.0 - */ -export const getNumberIndexedAccess = , I extends ReadonlyArray, R>( - self: Schema -): SchemaClass => make(AST.getNumberIndexedAccess(self.ast)) - -/** - * Get the first element of a `ReadonlyArray`, or `None` if the array is empty. - * - * @category ReadonlyArray transformations - * @since 0.67.0 - */ -export const head = (self: Schema, I, R>): SchemaClass, I, R> => - transform( - self, - OptionFromSelf(getNumberIndexedAccess(typeSchema(self))), - { strict: true, decode: array_.head, encode: option_.match({ onNone: () => [], onSome: array_.of }) } - ) - -/** - * Retrieves the first element of a `ReadonlyArray`. - * - * If the array is empty, it returns the `fallback` argument if provided; otherwise, it fails. - * - * @category ReadonlyArray transformations - * @since 0.67.0 - */ -export const headOrElse: { - (fallback?: LazyArg): (self: Schema, I, R>) => SchemaClass - (self: Schema, I, R>, fallback?: LazyArg): SchemaClass -} = dual( - (args) => isSchema(args[0]), - (self: Schema, I, R>, fallback?: LazyArg): SchemaClass => - transformOrFail( - self, - getNumberIndexedAccess(typeSchema(self)), - { - strict: true, - decode: (as, _, ast) => - as.length > 0 - ? ParseResult.succeed(as[0]) - : fallback - ? ParseResult.succeed(fallback()) - : ParseResult.fail(new ParseResult.Type(ast, as)), - encode: (a) => ParseResult.succeed(array_.of(a)) - } - ) -) - -/** - * @category type id - * @since 0.67.0 - */ -export const ValidDateTypeId: unique symbol = Symbol.for("@effect/schema/TypeId/ValidDate") - -/** - * Defines a filter that specifically rejects invalid dates, such as `new - * Date("Invalid Date")`. This filter ensures that only properly formatted and - * valid date objects are accepted, enhancing data integrity by preventing - * erroneous date values from being processed. - * - * @category Date filters - * @since 0.67.0 - */ -export const validDate = - (annotations?: Annotations.Filter) => (self: Schema): filter> => - self.pipe( - filter((a) => !Number.isNaN(a.getTime()), { - typeId: ValidDateTypeId, - description: "a valid Date", - ...annotations - }) - ) - -/** - * @category type id - * @since 0.73.1 - */ -export const LessThanDateTypeId: unique symbol = Symbol.for("@effect/schema/TypeId/LessThanDate") - -/** - * @category Date filters - * @since 0.73.1 - */ -export const lessThanDate = ( - max: Date, - annotations?: Annotations.Filter -) => -(self: Schema): filter> => - self.pipe( - filter((a) => a < max, { - typeId: { id: LessThanDateTypeId, annotation: { max } }, - description: `a date before ${util_.formatDate(max)}`, - ...annotations - }) - ) - -/** - * @category type id - * @since 0.73.1 - */ -export const LessThanOrEqualToDateTypeId: unique symbol = Symbol.for( - "@effect/schema/TypeId/LessThanOrEqualToDate" -) - -/** - * @category Date filters - * @since 0.73.1 - */ -export const lessThanOrEqualToDate = ( - max: Date, - annotations?: Annotations.Filter -) => -(self: Schema): filter> => - self.pipe( - filter((a) => a <= max, { - typeId: { id: LessThanDateTypeId, annotation: { max } }, - description: `a date before or equal to ${util_.formatDate(max)}`, - ...annotations - }) - ) - -/** - * @category type id - * @since 0.73.1 - */ -export const GreaterThanDateTypeId: unique symbol = Symbol.for("@effect/schema/TypeId/GreaterThanDate") - -/** - * @category Date filters - * @since 0.73.1 - */ -export const greaterThanDate = ( - min: Date, - annotations?: Annotations.Filter -) => -(self: Schema): filter> => - self.pipe( - filter((a) => a > min, { - typeId: { id: GreaterThanDateTypeId, annotation: { min } }, - description: `a date after ${util_.formatDate(min)}`, - ...annotations - }) - ) - -/** - * @category type id - * @since 0.73.1 - */ -export const GreaterThanOrEqualToDateTypeId: unique symbol = Symbol.for( - "@effect/schema/TypeId/GreaterThanOrEqualToDate" -) - -/** - * @category Date filters - * @since 0.73.1 - */ -export const greaterThanOrEqualToDate = ( - min: Date, - annotations?: Annotations.Filter -) => -(self: Schema): filter> => - self.pipe( - filter((a) => a >= min, { - typeId: { id: GreaterThanOrEqualToDateTypeId, annotation: { min } }, - description: `a date after or equal to ${util_.formatDate(min)}`, - ...annotations - }) - ) - -/** - * @category type id - * @since 0.73.1 - */ -export const BetweenDateTypeId: unique symbol = Symbol.for("@effect/schema/TypeId/BetweenDate") - -/** - * @category Date filters - * @since 0.73.1 - */ -export const betweenDate = ( - minimum: Date, - maximum: Date, - annotations?: Annotations.Filter -) => -(self: Schema): filter> => - self.pipe( - filter((a) => a <= maximum && a >= minimum, { - typeId: { id: BetweenDateTypeId, annotation: { maximum, minimum } }, - description: `a date between ${util_.formatDate(minimum)} and ${util_.formatDate(maximum)}`, - ...annotations - }) - ) - -/** - * Describes a schema that accommodates potentially invalid `Date` instances, - * such as `new Date("Invalid Date")`, without rejection. - * - * @category Date constructors - * @since 0.67.0 - */ -export class DateFromSelf extends declare( - Predicate.isDate, - { - identifier: "DateFromSelf", - description: "a potentially invalid Date instance", - pretty: (): pretty_.Pretty => (date) => `new Date(${JSON.stringify(date)})`, - arbitrary: (): LazyArbitrary => (fc) => fc.date({ noInvalidDate: false }), - equivalence: () => Equivalence.Date - } -) {} - -/** - * Defines a schema that ensures only valid dates are accepted. This schema - * rejects values like `new Date("Invalid Date")`, which, despite being a `Date` - * instance, represents an invalid date. Such stringent validation ensures that - * all date objects processed through this schema are properly formed and - * represent real dates. - * - * @category Date constructors - * @since 0.67.0 - */ -export class ValidDateFromSelf extends DateFromSelf.pipe( - validDate({ - identifier: "ValidDateFromSelf", - description: "a valid Date instance" - }) -) {} - -/** - * Defines a schema that attempts to convert a `string` to a `Date` object using - * the `new Date` constructor. This conversion is lenient, meaning it does not - * reject strings that do not form valid dates (e.g., using `new Date("Invalid - * Date")` results in a `Date` object, despite being invalid). - * - * @category Date transformations - * @since 0.67.0 - */ -export class DateFromString extends transform( - String$.annotations({ description: "a string that will be parsed into a Date" }), - DateFromSelf, - { strict: true, decode: (s) => new Date(s), encode: (d) => util_.formatDate(d) } -).annotations({ identifier: "DateFromString" }) {} - -/** @ignore */ -class Date$ extends DateFromString.pipe( - validDate({ identifier: "Date" }) -) {} - -export { - /** - * This schema converts a `string` into a `Date` object using the `new Date` - * constructor. It ensures that only valid date strings are accepted, - * rejecting any strings that would result in an invalid date, such as `new - * Date("Invalid Date")`. - * - * @category Date transformations - * @since 0.67.0 - */ - Date$ as Date -} - -/** - * Defines a schema that converts a `number` into a `Date` object using the `new - * Date` constructor. This schema does not validate the numerical input, - * allowing potentially invalid values such as `NaN`, `Infinity`, and - * `-Infinity` to be converted into `Date` objects. During the encoding process, - * any invalid `Date` object will be encoded to `NaN`. - * - * @category Date transformations - * @since 0.67.0 - */ -export class DateFromNumber extends transform( - Number$.annotations({ description: "a number that will be parsed into a Date" }), - DateFromSelf, - { strict: true, decode: (n) => new Date(n), encode: (d) => d.getTime() } -).annotations({ identifier: "DateFromNumber" }) {} - -/** - * Describes a schema that represents a `DateTime.Utc` instance. - * - * @category DateTime.Utc constructors - * @since 0.70.0 - */ -export class DateTimeUtcFromSelf extends declare( - (u) => dateTime.isDateTime(u) && dateTime.isUtc(u), - { - identifier: "DateTimeUtcFromSelf", - description: "a DateTime.Utc instance", - pretty: (): pretty_.Pretty => (dateTime) => dateTime.toString(), - arbitrary: (): LazyArbitrary => (fc) => fc.date().map((date) => dateTime.unsafeFromDate(date)), - equivalence: () => dateTime.Equivalence - } -) {} - -const decodeDateTime = (input: A, _: ParseOptions, ast: AST.AST) => - ParseResult.try({ - try: () => dateTime.unsafeMake(input), - catch: () => new ParseResult.Type(ast, input) - }) - -/** - * Defines a schema that attempts to convert a `number` to a `DateTime.Utc` instance using the `DateTime.unsafeMake` constructor. - * - * @category DateTime.Utc transformations - * @since 0.70.0 - */ -export class DateTimeUtcFromNumber extends transformOrFail( - Number$.annotations({ description: "a number that will be parsed into a DateTime.Utc" }), - DateTimeUtcFromSelf, - { - strict: true, - decode: decodeDateTime, - encode: (dt) => ParseResult.succeed(dateTime.toEpochMillis(dt)) - } -).annotations({ identifier: "DateTimeUtcFromNumber" }) {} - -/** - * Defines a schema that attempts to convert a `string` to a `DateTime.Utc` instance using the `DateTime.unsafeMake` constructor. - * - * @category DateTime.Utc transformations - * @since 0.70.0 - */ -export class DateTimeUtc extends transformOrFail( - String$.annotations({ description: "a string that will be parsed into a DateTime.Utc" }), - DateTimeUtcFromSelf, - { - strict: true, - decode: decodeDateTime, - encode: (dt) => ParseResult.succeed(dateTime.formatIso(dt)) - } -).annotations({ identifier: "DateTimeUtc" }) {} - -const timeZoneOffsetArbitrary = (): LazyArbitrary => (fc) => - fc.integer({ min: -12 * 60 * 60 * 1000, max: 12 * 60 * 60 * 1000 }).map(dateTime.zoneMakeOffset) - -/** - * Describes a schema that represents a `TimeZone.Offset` instance. - * - * @category TimeZone constructors - * @since 0.70.0 - */ -export class TimeZoneOffsetFromSelf extends declare( - dateTime.isTimeZoneOffset, - { - identifier: "TimeZoneOffsetFromSelf", - description: "a TimeZone.Offset instance", - pretty: (): pretty_.Pretty => (zone) => zone.toString(), - arbitrary: timeZoneOffsetArbitrary - } -) {} - -/** - * Defines a schema that converts a `number` to a `TimeZone.Offset` instance using the `DateTime.zoneMakeOffset` constructor. - * - * @category TimeZone transformations - * @since 0.70.0 - */ -export class TimeZoneOffset extends transform( - Number$.annotations({ description: "a number that will be parsed into a TimeZone.Offset" }), - TimeZoneOffsetFromSelf, - { strict: true, decode: dateTime.zoneMakeOffset, encode: (tz) => tz.offset } -).annotations({ identifier: "TimeZoneOffset" }) {} - -const timeZoneNamedArbitrary = (): LazyArbitrary => (fc) => - fc.constantFrom(...Intl.supportedValuesOf("timeZone")).map(dateTime.zoneUnsafeMakeNamed) - -/** - * Describes a schema that represents a `TimeZone.Named` instance. - * - * @category TimeZone constructors - * @since 0.70.0 - */ -export class TimeZoneNamedFromSelf extends declare( - dateTime.isTimeZoneNamed, - { - identifier: "TimeZoneNamedFromSelf", - description: "a TimeZone.Named instance", - pretty: (): pretty_.Pretty => (zone) => zone.toString(), - arbitrary: timeZoneNamedArbitrary - } -) {} - -/** - * Defines a schema that attempts to convert a `string` to a `TimeZone.Named` instance using the `DateTime.zoneUnsafeMakeNamed` constructor. - * - * @category TimeZone transformations - * @since 0.70.0 - */ -export class TimeZoneNamed extends transformOrFail( - String$.annotations({ description: "a string that will be parsed into a TimeZone.Named" }), - TimeZoneNamedFromSelf, - { - strict: true, - decode: (s, _, ast) => - ParseResult.try({ - try: () => dateTime.zoneUnsafeMakeNamed(s), - catch: () => new ParseResult.Type(ast, s) - }), - encode: (tz) => ParseResult.succeed(tz.id) - } -).annotations({ identifier: "TimeZoneNamed" }) {} - -/** - * @category api interface - * @since 0.70.0 - */ -export interface TimeZoneFromSelf extends Union<[typeof TimeZoneOffsetFromSelf, typeof TimeZoneNamedFromSelf]> { - annotations(annotations: Annotations.Schema): TimeZoneFromSelf -} - -/** - * @category TimeZone constructors - * @since 0.70.0 - */ -export const TimeZoneFromSelf: TimeZoneFromSelf = Union(TimeZoneOffsetFromSelf, TimeZoneNamedFromSelf) - -/** - * Defines a schema that attempts to convert a `string` to a `TimeZone` using the `DateTime.zoneFromString` constructor. - * - * @category TimeZone transformations - * @since 0.70.0 - */ -export class TimeZone extends transformOrFail( - String$.annotations({ description: "a string that will be parsed into a TimeZone" }), - TimeZoneFromSelf, - { - strict: true, - decode: (s, _, ast) => - option_.match(dateTime.zoneFromString(s), { - onNone: () => ParseResult.fail(new ParseResult.Type(ast, s)), - onSome: ParseResult.succeed - }), - encode: (tz) => ParseResult.succeed(dateTime.zoneToString(tz)) - } -).annotations({ identifier: "TimeZone" }) {} - -const timeZoneArbitrary: LazyArbitrary = (fc) => - fc.oneof( - timeZoneOffsetArbitrary()(fc), - timeZoneNamedArbitrary()(fc) - ) - -/** - * Describes a schema that represents a `DateTime.Zoned` instance. - * - * @category DateTime.Zoned constructors - * @since 0.70.0 - */ -export class DateTimeZonedFromSelf extends declare( - (u) => dateTime.isDateTime(u) && dateTime.isZoned(u), - { - identifier: "DateTimeZonedFromSelf", - description: "a DateTime.Zoned instance", - pretty: (): pretty_.Pretty => (dateTime) => dateTime.toString(), - arbitrary: (): LazyArbitrary => (fc) => - fc.date().chain((date) => timeZoneArbitrary(fc).map((timeZone) => dateTime.unsafeMakeZoned(date, { timeZone }))), - equivalence: () => dateTime.Equivalence - } -) {} - -/** - * Defines a schema that attempts to convert a `string` to a `DateTime.Zoned` instance. - * - * @category DateTime.Zoned transformations - * @since 0.70.0 - */ -export class DateTimeZoned extends transformOrFail( - String$.annotations({ description: "a string that will be parsed into a DateTime.Zoned" }), - DateTimeZonedFromSelf, - { - strict: true, - decode: (s, _, ast) => - option_.match(dateTime.makeZonedFromString(s), { - onNone: () => ParseResult.fail(new ParseResult.Type(ast, s)), - onSome: ParseResult.succeed - }), - encode: (dt) => ParseResult.succeed(dateTime.formatIsoZoned(dt)) - } -).annotations({ identifier: "DateTimeZoned" }) {} - -/** - * @category Option utils - * @since 0.67.0 - */ -export type OptionEncoded = - | { - readonly _tag: "None" - } - | { - readonly _tag: "Some" - readonly value: I - } - -const OptionNoneEncoded = Struct({ - _tag: Literal("None") -}).annotations({ description: "NoneEncoded" }) - -const optionSomeEncoded = (value: Schema) => - Struct({ - _tag: Literal("Some"), - value - }).annotations({ description: `SomeEncoded<${format(value)}>` }) - -const optionEncoded = (value: Schema) => - Union( - OptionNoneEncoded, - optionSomeEncoded(value) - ).annotations({ - description: `OptionEncoded<${format(value)}>` - }) - -const optionDecode = (input: OptionEncoded): option_.Option => - input._tag === "None" ? option_.none() : option_.some(input.value) - -const optionArbitrary = - (value: LazyArbitrary, ctx: GenerationContext): LazyArbitrary> => (fc) => - fc.oneof( - ctx, - fc.record({ _tag: fc.constant("None" as const) }), - fc.record({ _tag: fc.constant("Some" as const), value: value(fc) }) - ).map(optionDecode) - -const optionPretty = (value: pretty_.Pretty): pretty_.Pretty> => - option_.match({ - onNone: () => "none()", - onSome: (a) => `some(${value(a)})` - }) - -const optionParse = - (decodeUnknown: ParseResult.DecodeUnknown): ParseResult.DeclarationDecodeUnknown, R> => - (u, options, ast) => - option_.isOption(u) ? - option_.isNone(u) ? - ParseResult.succeed(option_.none()) - : toComposite(decodeUnknown(u.value, options), option_.some, ast, u) - : ParseResult.fail(new ParseResult.Type(ast, u)) - -/** - * @category api interface - * @since 0.67.0 - */ -export interface OptionFromSelf extends - AnnotableClass< - OptionFromSelf, - option_.Option>, - option_.Option>, - Schema.Context - > -{} - -/** - * @category Option transformations - * @since 0.67.0 - */ -export const OptionFromSelf = ( - value: Value -): OptionFromSelf => { - return declare( - [value], - { - decode: (value) => optionParse(ParseResult.decodeUnknown(value)), - encode: (value) => optionParse(ParseResult.encodeUnknown(value)) - }, - { - description: `Option<${format(value)}>`, - pretty: optionPretty, - arbitrary: optionArbitrary, - equivalence: option_.getEquivalence - } - ) -} - -const makeNoneEncoded = { - _tag: "None" -} as const -const makeSomeEncoded = (value: A) => ({ - _tag: "Some", - value -} as const) - -/** - * @category api interface - * @since 0.67.0 - */ -export interface Option extends - AnnotableClass< - Option, - option_.Option>, - OptionEncoded>, - Schema.Context - > -{} - -/** - * @category Option transformations - * @since 0.67.0 - */ -export const Option = (value: Value): Option => { - const value_ = asSchema(value) - return transform( - optionEncoded(value_), - OptionFromSelf(typeSchema(value_)), - { - strict: true, - decode: optionDecode, - encode: option_.match({ - onNone: () => makeNoneEncoded, - onSome: makeSomeEncoded - }) - } - ) -} - -/** - * @category api interface - * @since 0.67.0 - */ -export interface OptionFromNullOr extends - AnnotableClass< - OptionFromNullOr, - option_.Option>, - Schema.Encoded | null, - Schema.Context - > -{} - -/** - * @category Option transformations - * @since 0.67.0 - */ -export const OptionFromNullOr = ( - value: Value -): OptionFromNullOr => { - const value_ = asSchema(value) - return transform(NullOr(value_), OptionFromSelf(typeSchema(value_)), { - strict: true, - decode: option_.fromNullable, - encode: option_.getOrNull - }) -} - -/** - * @category api interface - * @since 0.67.0 - */ -export interface OptionFromNullishOr extends - AnnotableClass< - OptionFromNullishOr, - option_.Option>, - Schema.Encoded | null | undefined, - Schema.Context - > -{} - -/** - * @category Option transformations - * @since 0.67.0 - */ -export const OptionFromNullishOr = ( - value: Value, - onNoneEncoding: null | undefined -): OptionFromNullishOr => { - const value_ = asSchema(value) - return transform( - NullishOr(value_), - OptionFromSelf(typeSchema(value_)), - { - strict: true, - decode: option_.fromNullable, - encode: onNoneEncoding === null ? option_.getOrNull : option_.getOrUndefined - } - ) -} - -/** - * @category api interface - * @since 0.67.0 - */ -export interface OptionFromUndefinedOr extends - AnnotableClass< - OptionFromUndefinedOr, - option_.Option>, - Schema.Encoded | undefined, - Schema.Context - > -{} - -/** - * @category Option transformations - * @since 0.67.0 - */ -export const OptionFromUndefinedOr = ( - value: Value -): OptionFromUndefinedOr => { - const value_ = asSchema(value) - return transform(UndefinedOr(value_), OptionFromSelf(typeSchema(value_)), { - strict: true, - decode: option_.fromNullable, - encode: option_.getOrUndefined - }) -} - -/** - * Transforms strings into an Option type, effectively filtering out empty or - * whitespace-only strings by trimming them and checking their length. Returns - * `none` for invalid inputs and `some` for valid non-empty strings. - * - * @example - * import { Schema } from "@effect/schema" - * - * console.log(Schema.decodeSync(Schema.OptionFromNonEmptyTrimmedString)("")) // Option.none() - * console.log(Schema.decodeSync(Schema.OptionFromNonEmptyTrimmedString)(" a ")) // Option.some("a") - * console.log(Schema.decodeSync(Schema.OptionFromNonEmptyTrimmedString)("a")) // Option.some("a") - * - * @category Option transformations - * @since 0.69.3 - */ -export const OptionFromNonEmptyTrimmedString = transform(String$, OptionFromSelf(NonEmptyTrimmedString), { - strict: true, - decode: (s) => { - const out = s.trim() - return out.length === 0 ? option_.none() : option_.some(out) - }, - encode: option_.getOrElse(() => "") -}) - -/** - * @category Either utils - * @since 0.67.0 - */ -export type RightEncoded = { - readonly _tag: "Right" - readonly right: IA -} - -/** - * @category Either utils - * @since 0.67.0 - */ -export type LeftEncoded = { - readonly _tag: "Left" - readonly left: IE -} - -/** - * @category Either utils - * @since 0.67.0 - */ -export type EitherEncoded = RightEncoded | LeftEncoded - -const rightEncoded = (right: Schema): Schema, RightEncoded, RR> => - Struct({ - _tag: Literal("Right"), - right - }).annotations({ description: `RightEncoded<${format(right)}>` }) - -const leftEncoded = (left: Schema): Schema, LeftEncoded
  • , LR> => - Struct({ - _tag: Literal("Left"), - left - }).annotations({ description: `LeftEncoded<${format(left)}>` }) - -const eitherEncoded = ( - right: Schema, - left: Schema -) => - Union(rightEncoded(right), leftEncoded(left)).annotations({ - description: `EitherEncoded<${format(left)}, ${format(right)}>` - }) - -const eitherDecode = (input: EitherEncoded): either_.Either => - input._tag === "Left" ? either_.left(input.left) : either_.right(input.right) - -const eitherArbitrary = ( - right: LazyArbitrary, - left: LazyArbitrary -): LazyArbitrary> => -(fc) => - fc.oneof( - fc.record({ _tag: fc.constant("Left" as const), left: left(fc) }), - fc.record({ _tag: fc.constant("Right" as const), right: right(fc) }) - ).map(eitherDecode) - -const eitherPretty = ( - right: pretty_.Pretty, - left: pretty_.Pretty -): pretty_.Pretty> => - either_.match({ - onLeft: (e) => `left(${left(e)})`, - onRight: (a) => `right(${right(a)})` - }) - -const eitherParse = ( - parseRight: ParseResult.DecodeUnknown, - decodeUnknownLeft: ParseResult.DecodeUnknown -): ParseResult.DeclarationDecodeUnknown, LR | RR> => -(u, options, ast) => - either_.isEither(u) ? - either_.match(u, { - onLeft: (left) => toComposite(decodeUnknownLeft(left, options), either_.left, ast, u), - onRight: (right) => toComposite(parseRight(right, options), either_.right, ast, u) - }) - : ParseResult.fail(new ParseResult.Type(ast, u)) - -/** - * @category api interface - * @since 0.67.0 - */ -export interface EitherFromSelf extends - AnnotableClass< - EitherFromSelf, - either_.Either, Schema.Type>, - either_.Either, Schema.Encoded>, - Schema.Context | Schema.Context - > -{} - -/** - * @category Either transformations - * @since 0.67.0 - */ -export const EitherFromSelf = ({ left, right }: { - readonly left: L - readonly right: R -}): EitherFromSelf => { - return declare( - [right, left], - { - decode: (right, left) => eitherParse(ParseResult.decodeUnknown(right), ParseResult.decodeUnknown(left)), - encode: (right, left) => eitherParse(ParseResult.encodeUnknown(right), ParseResult.encodeUnknown(left)) - }, - { - description: `Either<${format(right)}, ${format(left)}>`, - pretty: eitherPretty, - arbitrary: eitherArbitrary, - equivalence: (right, left) => either_.getEquivalence({ left, right }) - } - ) -} - -const makeLeftEncoded = (left: E) => (({ - _tag: "Left", - left -}) as const) -const makeRightEncoded = (right: A) => (({ - _tag: "Right", - right -}) as const) - -/** - * @category api interface - * @since 0.67.0 - */ -export interface Either extends - AnnotableClass< - Either, - either_.Either, Schema.Type>, - EitherEncoded, Schema.Encoded>, - Schema.Context | Schema.Context - > -{} - -/** - * @category Either transformations - * @since 0.67.0 - */ -export const Either = ({ left, right }: { - readonly left: L - readonly right: R -}): Either => { - const right_ = asSchema(right) - const left_ = asSchema(left) - return transform( - eitherEncoded(right_, left_), - EitherFromSelf({ left: typeSchema(left_), right: typeSchema(right_) }), - { - strict: true, - decode: eitherDecode, - encode: either_.match({ onLeft: makeLeftEncoded, onRight: makeRightEncoded }) - } - ) -} - -/** - * @category api interface - * @since 0.67.0 - */ -export interface EitherFromUnion extends - AnnotableClass< - EitherFromUnion, - either_.Either, Schema.Type>, - Schema.Encoded | Schema.Encoded, - Schema.Context | Schema.Context - > -{} - -/** - * @example - * import * as Schema from "@effect/schema/Schema" - * - * // Schema> - * Schema.EitherFromUnion({ left: Schema.String, right: Schema.Number }) - * - * @category Either transformations - * @since 0.67.0 - */ -export const EitherFromUnion = ({ left, right }: { - readonly left: L - readonly right: R -}): EitherFromUnion => { - const right_ = asSchema(right) - const left_ = asSchema(left) - const toright = typeSchema(right_) - const toleft = typeSchema(left_) - const fromRight = transform(right_, rightEncoded(toright), { - strict: true, - decode: makeRightEncoded, - encode: (r) => r.right - }) - const fromLeft = transform(left_, leftEncoded(toleft), { - strict: true, - decode: makeLeftEncoded, - encode: (l) => l.left - }) - return transform( - Union(fromRight, fromLeft), - EitherFromSelf({ left: toleft, right: toright }), - { - strict: true, - decode: (from) => from._tag === "Left" ? either_.left(from.left) : either_.right(from.right), - encode: either_.match({ onLeft: makeLeftEncoded, onRight: makeRightEncoded }) - } - ) -} - -const mapArbitrary = ( - key: LazyArbitrary, - value: LazyArbitrary, - ctx: GenerationContext -): LazyArbitrary> => { - return (fc) => { - const items = fc.array(fc.tuple(key(fc), value(fc))) - return (ctx.depthIdentifier !== undefined ? fc.oneof(ctx, fc.constant([]), items) : items).map((as) => new Map(as)) - } -} - -const readonlyMapPretty = ( - key: pretty_.Pretty, - value: pretty_.Pretty -): pretty_.Pretty> => -(map) => - `new Map([${ - Array.from(map.entries()) - .map(([k, v]) => `[${key(k)}, ${value(v)}]`) - .join(", ") - }])` - -const readonlyMapEquivalence = ( - key: Equivalence.Equivalence, - value: Equivalence.Equivalence -): Equivalence.Equivalence> => { - const arrayEquivalence = array_.getEquivalence( - Equivalence.make<[K, V]>(([ka, va], [kb, vb]) => key(ka, kb) && value(va, vb)) - ) - return Equivalence.make((a, b) => arrayEquivalence(Array.from(a.entries()), Array.from(b.entries()))) -} - -const readonlyMapParse = ( - decodeUnknown: ParseResult.DecodeUnknown, R> -): ParseResult.DeclarationDecodeUnknown, R> => -(u, options, ast) => - Predicate.isMap(u) ? - toComposite(decodeUnknown(Array.from(u.entries()), options), (as) => new Map(as), ast, u) - : ParseResult.fail(new ParseResult.Type(ast, u)) - -/** - * @category api interface - * @since 0.67.0 - */ -export interface ReadonlyMapFromSelf extends - AnnotableClass< - ReadonlyMapFromSelf, - ReadonlyMap, Schema.Type>, - ReadonlyMap, Schema.Encoded>, - Schema.Context | Schema.Context - > -{} - -const mapFromSelf_ = ( - key: K, - value: V, - description: string -): ReadonlyMapFromSelf => - declare( - [key, value], - { - decode: (Key, Value) => readonlyMapParse(ParseResult.decodeUnknown(Array$(Tuple(Key, Value)))), - encode: (Key, Value) => readonlyMapParse(ParseResult.encodeUnknown(Array$(Tuple(Key, Value)))) - }, - { - description, - pretty: readonlyMapPretty, - arbitrary: mapArbitrary, - equivalence: readonlyMapEquivalence - } - ) - -/** - * @category ReadonlyMap - * @since 0.67.0 - */ -export const ReadonlyMapFromSelf = ({ key, value }: { - readonly key: K - readonly value: V -}): ReadonlyMapFromSelf => mapFromSelf_(key, value, `ReadonlyMap<${format(key)}, ${format(value)}>`) - -/** - * @category api interface - * @since 0.67.0 - */ -export interface MapFromSelf extends - AnnotableClass< - MapFromSelf, - Map, Schema.Type>, - ReadonlyMap, Schema.Encoded>, - Schema.Context | Schema.Context - > -{} - -/** - * @category Map - * @since 0.67.0 - */ -export const MapFromSelf = ({ key, value }: { - readonly key: K - readonly value: V -}): MapFromSelf => mapFromSelf_(key, value, `Map<${format(key)}, ${format(value)}>`) as any - -/** - * @category api interface - * @since 0.67.0 - */ -export interface ReadonlyMap$ extends - AnnotableClass< - ReadonlyMap$, - ReadonlyMap, Schema.Type>, - ReadonlyArray, Schema.Encoded]>, - Schema.Context | Schema.Context - > -{} - -/** - * @category ReadonlyMap transformations - * @since 0.67.0 - */ -export const ReadonlyMap = ({ key, value }: { - readonly key: K - readonly value: V -}): ReadonlyMap$ => { - const key_ = asSchema(key) - const value_ = asSchema(value) - return transform( - Array$(Tuple(key_, value_)), - ReadonlyMapFromSelf({ key: typeSchema(key_), value: typeSchema(value_) }), - { strict: true, decode: (as) => new Map(as), encode: (map) => Array.from(map.entries()) } - ) -} - -/** - * @category api interface - * @since 0.67.0 - */ -export interface Map$ extends - AnnotableClass< - Map$, - Map, Schema.Type>, - ReadonlyArray, Schema.Encoded]>, - Schema.Context | Schema.Context - > -{} - -const map = ({ key, value }: { - readonly key: K - readonly value: V -}): Map$ => { - const key_ = asSchema(key) - const value_ = asSchema(value) - return transform( - Array$(Tuple(key_, value_)), - MapFromSelf({ key: typeSchema(key_), value: typeSchema(value_) }), - { strict: true, decode: (as) => new Map(as), encode: (map) => Array.from(map.entries()) } - ) -} - -export { - /** - * @category Map transformations - * @since 0.67.0 - */ - map as Map -} - -/** - * @category ReadonlyMap transformations - * @since 0.68.15 - */ -export const ReadonlyMapFromRecord = ({ key, value }: { - key: Schema - value: Schema -}): Schema, { readonly [x: string]: VI }, KR | VR> => - transform( - Record({ key: encodedBoundSchema(key), value }).annotations({ - description: "a record that will be parsed into a ReadonlyMap" - }), - ReadonlyMapFromSelf({ key, value: typeSchema(value) }), - { - strict: true, - decode: (record) => new Map(Object.entries(record)), - encode: record_.fromEntries - } - ) - -/** - * @category Map transformations - * @since 0.68.15 - */ -export const MapFromRecord = ({ key, value }: { - key: Schema - value: Schema -}): Schema, { readonly [x: string]: VI }, KR | VR> => - transform( - Record({ key: encodedBoundSchema(key), value }).annotations({ - description: "a record that will be parsed into a Map" - }), - MapFromSelf({ key, value: typeSchema(value) }), - { - strict: true, - decode: (record) => new Map(Object.entries(record)), - encode: record_.fromEntries - } - ) - -const setArbitrary = (item: LazyArbitrary, ctx: GenerationContext): LazyArbitrary> => (fc) => { - const items = fc.array(item(fc)) - return (ctx.depthIdentifier !== undefined ? fc.oneof(ctx, fc.constant([]), items) : items).map((as) => new Set(as)) -} - -const readonlySetPretty = (item: pretty_.Pretty): pretty_.Pretty> => (set) => - `new Set([${Array.from(set.values()).map((a) => item(a)).join(", ")}])` - -const readonlySetEquivalence = ( - item: Equivalence.Equivalence -): Equivalence.Equivalence> => { - const arrayEquivalence = array_.getEquivalence(item) - return Equivalence.make((a, b) => arrayEquivalence(Array.from(a.values()), Array.from(b.values()))) -} - -const readonlySetParse = ( - decodeUnknown: ParseResult.DecodeUnknown, R> -): ParseResult.DeclarationDecodeUnknown, R> => -(u, options, ast) => - Predicate.isSet(u) ? - toComposite(decodeUnknown(Array.from(u.values()), options), (as) => new Set(as), ast, u) - : ParseResult.fail(new ParseResult.Type(ast, u)) - -/** - * @category api interface - * @since 0.67.0 - */ -export interface ReadonlySetFromSelf extends - AnnotableClass< - ReadonlySetFromSelf, - ReadonlySet>, - ReadonlySet>, - Schema.Context - > -{} - -const setFromSelf_ = (value: Value, description: string): ReadonlySetFromSelf => - declare( - [value], - { - decode: (item) => readonlySetParse(ParseResult.decodeUnknown(Array$(item))), - encode: (item) => readonlySetParse(ParseResult.encodeUnknown(Array$(item))) - }, - { - description, - pretty: readonlySetPretty, - arbitrary: setArbitrary, - equivalence: readonlySetEquivalence - } - ) - -/** - * @category ReadonlySet - * @since 0.67.0 - */ -export const ReadonlySetFromSelf = (value: Value): ReadonlySetFromSelf => - setFromSelf_(value, `ReadonlySet<${format(value)}>`) - -/** - * @category api interface - * @since 0.67.0 - */ -export interface SetFromSelf extends - AnnotableClass< - SetFromSelf, - Set>, - ReadonlySet>, - Schema.Context - > -{} - -/** - * @category Set - * @since 0.67.0 - */ -export const SetFromSelf = (value: Value): SetFromSelf => - setFromSelf_(value, `Set<${format(value)}>`) as any - -/** - * @category api interface - * @since 0.67.0 - */ -export interface ReadonlySet$ extends - AnnotableClass< - ReadonlySet$, - ReadonlySet>, - ReadonlyArray>, - Schema.Context - > -{} - -/** - * @category ReadonlySet transformations - * @since 0.67.0 - */ -export const ReadonlySet = (value: Value): ReadonlySet$ => { - const value_ = asSchema(value) - return transform( - Array$(value_), - ReadonlySetFromSelf(typeSchema(value_)), - { strict: true, decode: (as) => new Set(as), encode: (set) => Array.from(set) } - ) -} - -/** - * @category api interface - * @since 0.67.0 - */ -export interface Set$ extends - AnnotableClass< - Set$, - Set>, - ReadonlyArray>, - Schema.Context - > -{} - -const set = (value: Value): Set$ => { - const value_ = asSchema(value) - return transform( - Array$(value_), - SetFromSelf(typeSchema(value_)), - { strict: true, decode: (as) => new Set(as), encode: (set) => Array.from(set) } - ) -} - -export { - /** - * @category Set transformations - * @since 0.67.0 - */ - set as Set -} - -const bigDecimalPretty = (): pretty_.Pretty => (val) => - `BigDecimal(${bigDecimal_.format(bigDecimal_.normalize(val))})` - -const bigDecimalArbitrary = (): LazyArbitrary => (fc) => - fc.tuple(fc.bigInt(), fc.integer()).map(([value, scale]) => bigDecimal_.make(value, scale)) - -/** - * @category BigDecimal constructors - * @since 0.67.0 - */ -export class BigDecimalFromSelf extends declare( - bigDecimal_.isBigDecimal, - { - identifier: "BigDecimalFromSelf", - pretty: bigDecimalPretty, - arbitrary: bigDecimalArbitrary, - equivalence: () => bigDecimal_.Equivalence - } -) {} - -/** - * @category BigDecimal transformations - * @since 0.67.0 - */ -export class BigDecimal extends transformOrFail( - String$.annotations({ description: "a string that will be parsed into a BigDecimal" }), - BigDecimalFromSelf, - { - strict: true, - decode: (num, _, ast) => - bigDecimal_.fromString(num).pipe(option_.match({ - onNone: () => ParseResult.fail(new ParseResult.Type(ast, num)), - onSome: (val) => ParseResult.succeed(bigDecimal_.normalize(val)) - })), - encode: (val) => ParseResult.succeed(bigDecimal_.format(bigDecimal_.normalize(val))) - } -).annotations({ identifier: "BigDecimal" }) {} - -/** - * A schema that transforms a `number` into a `BigDecimal`. - * When encoding, this Schema will produce incorrect results if the BigDecimal exceeds the 64-bit range of a number. - * - * @category BigDecimal transformations - * @since 0.67.0 - */ -export class BigDecimalFromNumber extends transformOrFail( - Number$.annotations({ description: "a number that will be parsed into a BigDecimal" }), - BigDecimalFromSelf, - { - strict: true, - decode: (num) => ParseResult.succeed(bigDecimal_.fromNumber(num)), - encode: (val) => ParseResult.succeed(bigDecimal_.unsafeToNumber(val)) - } -).annotations({ identifier: "BigDecimalFromNumber" }) {} - -/** - * @category type id - * @since 0.67.0 - */ -export const GreaterThanBigDecimalTypeId: unique symbol = Symbol.for("@effect/schema/TypeId/GreaterThanBigDecimal") - -/** - * @category BigDecimal filters - * @since 0.67.0 - */ -export const greaterThanBigDecimal = ( - min: bigDecimal_.BigDecimal, - annotations?: Annotations.Filter -) => -(self: Schema): filter> => - self.pipe( - filter((a) => bigDecimal_.greaterThan(a, min), { - typeId: { id: GreaterThanBigDecimalTypeId, annotation: { min } }, - description: `a BigDecimal greater than ${bigDecimal_.format(min)}`, - ...annotations - }) - ) - -/** - * @category type id - * @since 0.67.0 - */ -export const GreaterThanOrEqualToBigDecimalTypeId: unique symbol = Symbol.for( - "@effect/schema/TypeId/GreaterThanOrEqualToBigDecimal" -) - -/** - * @category BigDecimal filters - * @since 0.67.0 - */ -export const greaterThanOrEqualToBigDecimal = ( - min: bigDecimal_.BigDecimal, - annotations?: Annotations.Filter -) => -(self: Schema): filter> => - self.pipe( - filter((a) => bigDecimal_.greaterThanOrEqualTo(a, min), { - typeId: { id: GreaterThanOrEqualToBigDecimalTypeId, annotation: { min } }, - description: `a BigDecimal greater than or equal to ${bigDecimal_.format(min)}`, - ...annotations - }) - ) - -/** - * @category type id - * @since 0.67.0 - */ -export const LessThanBigDecimalTypeId: unique symbol = Symbol.for("@effect/schema/TypeId/LessThanBigDecimal") - -/** - * @category BigDecimal filters - * @since 0.67.0 - */ -export const lessThanBigDecimal = ( - max: bigDecimal_.BigDecimal, - annotations?: Annotations.Filter -) => -(self: Schema): filter> => - self.pipe( - filter((a) => bigDecimal_.lessThan(a, max), { - typeId: { id: LessThanBigDecimalTypeId, annotation: { max } }, - description: `a BigDecimal less than ${bigDecimal_.format(max)}`, - ...annotations - }) - ) - -/** - * @category type id - * @since 0.67.0 - */ -export const LessThanOrEqualToBigDecimalTypeId: unique symbol = Symbol.for( - "@effect/schema/TypeId/LessThanOrEqualToBigDecimal" -) - -/** - * @category BigDecimal filters - * @since 0.67.0 - */ -export const lessThanOrEqualToBigDecimal = ( - max: bigDecimal_.BigDecimal, - annotations?: Annotations.Filter -) => -(self: Schema): filter> => - self.pipe( - filter((a) => bigDecimal_.lessThanOrEqualTo(a, max), { - typeId: { id: LessThanOrEqualToBigDecimalTypeId, annotation: { max } }, - description: `a BigDecimal less than or equal to ${bigDecimal_.format(max)}`, - ...annotations - }) - ) - -/** - * @category type id - * @since 0.67.0 - */ -export const PositiveBigDecimalTypeId: unique symbol = Symbol.for( - "@effect/schema/TypeId/PositiveBigDecimal" -) - -/** - * @category BigDecimal filters - * @since 0.67.0 - */ -export const positiveBigDecimal = ( - annotations?: Annotations.Filter -) => -(self: Schema): filter> => - self.pipe( - filter((a) => bigDecimal_.isPositive(a), { - typeId: { id: PositiveBigDecimalTypeId, annotation: {} }, - description: `a positive BigDecimal`, - ...annotations - }) - ) - -/** - * @category BigDecimal constructors - * @since 0.67.0 - */ -export const PositiveBigDecimalFromSelf: filter> = BigDecimalFromSelf.pipe( - positiveBigDecimal({ - identifier: "PositiveBigDecimalFromSelf", - title: "PositiveBigDecimalFromSelf" - }) -) - -/** - * @category type id - * @since 0.67.0 - */ -export const NonNegativeBigDecimalTypeId: unique symbol = Symbol.for( - "@effect/schema/TypeId/NonNegativeBigDecimal" -) - -/** - * @category BigDecimal filters - * @since 0.67.0 - */ -export const nonNegativeBigDecimal = ( - annotations?: Annotations.Filter -) => -(self: Schema): filter> => - self.pipe( - filter((a) => a.value >= 0n, { - typeId: { id: NonNegativeBigDecimalTypeId, annotation: {} }, - description: `a non-negative BigDecimal`, - ...annotations - }) - ) - -/** - * @category BigDecimal constructors - * @since 0.67.0 - */ -export const NonNegativeBigDecimalFromSelf: filter> = BigDecimalFromSelf.pipe( - nonNegativeBigDecimal({ - identifier: "NonNegativeBigDecimalFromSelf", - title: "NonNegativeBigDecimalFromSelf" - }) -) - -/** - * @category type id - * @since 0.67.0 - */ -export const NegativeBigDecimalTypeId: unique symbol = Symbol.for( - "@effect/schema/TypeId/NegativeBigDecimal" -) - -/** - * @category BigDecimal filters - * @since 0.67.0 - */ -export const negativeBigDecimal = ( - annotations?: Annotations.Filter -) => -(self: Schema): filter> => - self.pipe( - filter((a) => bigDecimal_.isNegative(a), { - typeId: { id: NegativeBigDecimalTypeId, annotation: {} }, - description: `a negative BigDecimal`, - ...annotations - }) - ) - -/** - * @category BigDecimal constructors - * @since 0.67.0 - */ -export const NegativeBigDecimalFromSelf: filter> = BigDecimalFromSelf.pipe( - negativeBigDecimal({ - identifier: "NegativeBigDecimalFromSelf", - title: "NegativeBigDecimalFromSelf" - }) -) - -/** - * @category type id - * @since 0.67.0 - */ -export const NonPositiveBigDecimalTypeId: unique symbol = Symbol.for( - "@effect/schema/TypeId/NonPositiveBigDecimal" -) - -/** - * @category BigDecimal filters - * @since 0.67.0 - */ -export const nonPositiveBigDecimal = ( - annotations?: Annotations.Filter -) => -(self: Schema): filter> => - self.pipe( - filter((a) => a.value <= 0n, { - typeId: { id: NonPositiveBigDecimalTypeId, annotation: {} }, - description: `a non-positive BigDecimal`, - ...annotations - }) - ) - -/** - * @category BigDecimal constructors - * @since 0.67.0 - */ -export const NonPositiveBigDecimalFromSelf: filter> = BigDecimalFromSelf.pipe( - nonPositiveBigDecimal({ - identifier: "NonPositiveBigDecimalFromSelf", - title: "NonPositiveBigDecimalFromSelf" - }) -) - -/** - * @category type id - * @since 0.67.0 - */ -export const BetweenBigDecimalTypeId: unique symbol = Symbol.for("@effect/schema/TypeId/BetweenBigDecimal") - -/** - * @category BigDecimal filters - * @since 0.67.0 - */ -export const betweenBigDecimal = ( - minimum: bigDecimal_.BigDecimal, - maximum: bigDecimal_.BigDecimal, - annotations?: Annotations.Filter -) => -(self: Schema): filter> => - self.pipe( - filter((a) => bigDecimal_.between(a, { minimum, maximum }), { - typeId: { id: BetweenBigDecimalTypeId, annotation: { maximum, minimum } }, - description: `a BigDecimal between ${bigDecimal_.format(minimum)} and ${bigDecimal_.format(maximum)}`, - ...annotations - }) - ) - -/** - * Clamps a `BigDecimal` between a minimum and a maximum value. - * - * @category BigDecimal transformations - * @since 0.67.0 - */ -export const clampBigDecimal = - (minimum: bigDecimal_.BigDecimal, maximum: bigDecimal_.BigDecimal) => - (self: Schema): transform, filter>> => - transform( - self, - self.pipe(typeSchema, betweenBigDecimal(minimum, maximum)), - { strict: false, decode: (self) => bigDecimal_.clamp(self, { minimum, maximum }), encode: identity } - ) - -const chunkArbitrary = (item: LazyArbitrary, ctx: GenerationContext): LazyArbitrary> => (fc) => { - const items = fc.array(item(fc)) - return (ctx.depthIdentifier !== undefined ? fc.oneof(ctx, fc.constant([]), items) : items).map(chunk_.fromIterable) -} - -const chunkPretty = (item: pretty_.Pretty): pretty_.Pretty> => (c) => - `Chunk(${chunk_.toReadonlyArray(c).map(item).join(", ")})` - -const chunkParse = ( - decodeUnknown: ParseResult.DecodeUnknown, R> -): ParseResult.DeclarationDecodeUnknown, R> => -(u, options, ast) => - chunk_.isChunk(u) ? - chunk_.isEmpty(u) ? - ParseResult.succeed(chunk_.empty()) - : toComposite(decodeUnknown(chunk_.toReadonlyArray(u), options), chunk_.fromIterable, ast, u) - : ParseResult.fail(new ParseResult.Type(ast, u)) - -/** - * @category api interface - * @since 0.67.0 - */ -export interface ChunkFromSelf extends - AnnotableClass< - ChunkFromSelf, - chunk_.Chunk>, - chunk_.Chunk>, - Schema.Context - > -{} - -/** - * @category Chunk - * @since 0.67.0 - */ -export const ChunkFromSelf = (value: Value): ChunkFromSelf => { - return declare( - [value], - { - decode: (item) => chunkParse(ParseResult.decodeUnknown(Array$(item))), - encode: (item) => chunkParse(ParseResult.encodeUnknown(Array$(item))) - }, - { - description: `Chunk<${format(value)}>`, - pretty: chunkPretty, - arbitrary: chunkArbitrary, - equivalence: chunk_.getEquivalence - } - ) -} - -/** - * @category api interface - * @since 0.67.0 - */ -export interface Chunk extends - AnnotableClass< - Chunk, - chunk_.Chunk>, - ReadonlyArray>, - Schema.Context - > -{} - -/** - * @category Chunk transformations - * @since 0.67.0 - */ -export const Chunk = (value: Value): Chunk => { - const value_ = asSchema(value) - return transform( - Array$(value_), - ChunkFromSelf(typeSchema(value_)), - { - strict: true, - decode: (as) => as.length === 0 ? chunk_.empty() : chunk_.fromIterable(as), - encode: chunk_.toReadonlyArray - } - ) -} - -/** - * @category api interface - * @since 0.67.23 - */ -export interface NonEmptyChunkFromSelf extends - AnnotableClass< - NonEmptyChunkFromSelf, - chunk_.NonEmptyChunk>, - chunk_.NonEmptyChunk>, - Schema.Context - > -{} - -const nonEmptyChunkArbitrary = (item: LazyArbitrary): LazyArbitrary> => (fc) => - fastCheck_.array(item(fc), { minLength: 1 }).map((as) => chunk_.unsafeFromNonEmptyArray(as as any)) - -const nonEmptyChunkPretty = (item: pretty_.Pretty): pretty_.Pretty> => (c) => - `NonEmptyChunk(${chunk_.toReadonlyArray(c).map(item).join(", ")})` - -const nonEmptyChunkParse = ( - decodeUnknown: ParseResult.DecodeUnknown, R> -): ParseResult.DeclarationDecodeUnknown, R> => -(u, options, ast) => - chunk_.isChunk(u) && chunk_.isNonEmpty(u) - ? toComposite(decodeUnknown(chunk_.toReadonlyArray(u), options), chunk_.unsafeFromNonEmptyArray, ast, u) - : ParseResult.fail(new ParseResult.Type(ast, u)) - -/** - * @category Chunk - * @since 0.67.23 - */ -export const NonEmptyChunkFromSelf = (value: Value): NonEmptyChunkFromSelf => { - return declare( - [value], - { - decode: (item) => nonEmptyChunkParse(ParseResult.decodeUnknown(NonEmptyArray(item))), - encode: (item) => nonEmptyChunkParse(ParseResult.encodeUnknown(NonEmptyArray(item))) - }, - { - description: `NonEmptyChunk<${format(value)}>`, - pretty: nonEmptyChunkPretty, - arbitrary: nonEmptyChunkArbitrary, - equivalence: chunk_.getEquivalence - } - ) -} - -/** - * @category api interface - * @since 0.67.23 - */ -export interface NonEmptyChunk extends - AnnotableClass< - NonEmptyChunk, - chunk_.NonEmptyChunk>, - array_.NonEmptyReadonlyArray>, - Schema.Context - > -{} - -/** - * @category Chunk transformations - * @since 0.67.23 - */ -export const NonEmptyChunk = (value: Value): NonEmptyChunk => { - const value_ = asSchema(value) - return transform( - NonEmptyArray(value_), - NonEmptyChunkFromSelf(typeSchema(value_)), - { strict: true, decode: chunk_.unsafeFromNonEmptyArray, encode: chunk_.toReadonlyArray } - ) -} - -const toData = > | ReadonlyArray>(a: A): A => - Array.isArray(a) ? data_.array(a) : data_.struct(a) - -const dataArbitrary = > | ReadonlyArray>( - item: LazyArbitrary -): LazyArbitrary => -(fc) => item(fc).map(toData) - -const dataPretty = > | ReadonlyArray>( - item: pretty_.Pretty -): pretty_.Pretty => -(d) => `Data(${item(d)})` - -const dataParse = > | ReadonlyArray>( - decodeUnknown: ParseResult.DecodeUnknown -): ParseResult.DeclarationDecodeUnknown => -(u, options, ast) => - Equal.isEqual(u) ? - toComposite(decodeUnknown(u, options), toData, ast, u) - : ParseResult.fail(new ParseResult.Type(ast, u)) - -/** - * @category Data transformations - * @since 0.67.0 - */ -export const DataFromSelf = < - R, - I extends Readonly> | ReadonlyArray, - A extends Readonly> | ReadonlyArray ->( - item: Schema -): SchemaClass => - declare( - [item], - { - decode: (item) => dataParse(ParseResult.decodeUnknown(item)), - encode: (item) => dataParse(ParseResult.encodeUnknown(item)) - }, - { - description: `Data<${format(item)}>`, - pretty: dataPretty, - arbitrary: dataArbitrary - } - ) - -/** - * @category Data transformations - * @since 0.67.0 - */ -export const Data = < - R, - I extends Readonly> | ReadonlyArray, - A extends Readonly> | ReadonlyArray ->( - item: Schema -): SchemaClass => - transform( - item, - DataFromSelf(typeSchema(item)), - { strict: false, decode: toData, encode: (a) => Array.isArray(a) ? Array.from(a) : Object.assign({}, a) } - ) - -type MissingSelfGeneric = - `Missing \`Self\` generic - use \`class Self extends ${Usage}()(${Params}{ ... })\`` - -type RequiredKeys = { - [K in keyof T]-?: {} extends Pick ? never : K -}[keyof T] - -/** - * @category api interface - * @since 0.67.0 - */ -export interface Class - extends Schema, R> -{ - new( - props: RequiredKeys extends never ? void | Simplify : Simplify, - options?: MakeOptions - ): Struct.Type & Omit & Proto - - /** @since 0.69.3 */ - readonly ast: AST.Transformation - - make, X>(this: { new(...args: Args): X }, ...args: Args): X - - annotations(annotations: Annotations.Schema): SchemaClass, R> - - readonly fields: { readonly [K in keyof Fields]: Fields[K] } - - readonly identifier: string - - extend(identifier: string): ( - fields: newFields | HasFields, - annotations?: Annotations.Schema - ) => [Extended] extends [never] ? MissingSelfGeneric<"Base.extend"> - : Class< - Extended, - Fields & newFields, - I & Struct.Encoded, - R | Struct.Context, - C & Struct.Constructor, - Self, - Proto - > - - transformOrFail(identifier: string): < - newFields extends Struct.Fields, - R2, - R3 - >( - fields: newFields, - options: { - readonly decode: ( - input: Simplify>, - options: ParseOptions, - ast: AST.Transformation - ) => Effect.Effect>, ParseResult.ParseIssue, R2> - readonly encode: ( - input: Simplify>, - options: ParseOptions, - ast: AST.Transformation - ) => Effect.Effect, ParseResult.ParseIssue, R3> - }, - annotations?: Annotations.Schema - ) => [Transformed] extends [never] ? MissingSelfGeneric<"Base.transformOrFail"> - : Class< - Transformed, - Fields & newFields, - I, - R | Struct.Context | R2 | R3, - C & Struct.Constructor, - Self, - Proto - > - - transformOrFailFrom(identifier: string): < - newFields extends Struct.Fields, - R2, - R3 - >( - fields: newFields, - options: { - readonly decode: ( - input: Simplify, - options: ParseOptions, - ast: AST.Transformation - ) => Effect.Effect>, ParseResult.ParseIssue, R2> - readonly encode: ( - input: Simplify>, - options: ParseOptions, - ast: AST.Transformation - ) => Effect.Effect - }, - annotations?: Annotations.Schema - ) => [Transformed] extends [never] ? MissingSelfGeneric<"Base.transformOrFailFrom"> - : Class< - Transformed, - Fields & newFields, - I, - R | Struct.Context | R2 | R3, - C & Struct.Constructor, - Self, - Proto - > -} - -type HasFields = Struct | { - readonly [refineTypeId]: HasFields -} - -const isField = (u: unknown) => isSchema(u) || isPropertySignature(u) - -const isFields = (fields: object): fields is Fields => - util_.ownKeys(fields).every((key) => isField((fields as any)[key])) - -const getFields = (hasFields: HasFields): Fields => - "fields" in hasFields ? hasFields.fields : getFields(hasFields[refineTypeId]) - -const getSchemaFromFieldsOr = (fieldsOr: Fields | HasFields): Schema.Any => - isFields(fieldsOr) ? Struct(fieldsOr) : isSchema(fieldsOr) ? fieldsOr : Struct(getFields(fieldsOr)) - -const getFieldsFromFieldsOr = (fieldsOr: Fields | HasFields): Fields => - isFields(fieldsOr) ? fieldsOr : getFields(fieldsOr) - -/** - * @category classes - * @since 0.67.0 - */ -export const Class = (identifier: string) => -( - fieldsOr: Fields | HasFields, - annotations?: Annotations.Schema -): [Self] extends [never] ? MissingSelfGeneric<"Class"> - : Class< - Self, - Fields, - Struct.Encoded, - Struct.Context, - Struct.Constructor, - {}, - {} - > => - makeClass({ - kind: "Class", - identifier, - schema: getSchemaFromFieldsOr(fieldsOr), - fields: getFieldsFromFieldsOr(fieldsOr), - Base: data_.Class, - annotations - }) - -/** @internal */ -export const getClassTag = (tag: Tag) => - withConstructorDefault(propertySignature(Literal(tag)), () => tag) - -/** - * @category api interface - * @since 0.67.0 - */ -export interface TaggedClass extends - Class< - Self, - Fields, - Struct.Encoded, - Struct.Context, - Struct.Constructor>, - {}, - {} - > -{ - readonly _tag: Tag -} - -/** - * @category classes - * @since 0.67.0 - */ -export const TaggedClass = (identifier?: string) => -( - tag: Tag, - fieldsOr: Fields | HasFields, - annotations?: Annotations.Schema -): [Self] extends [never] ? MissingSelfGeneric<"TaggedClass", `"Tag", `> - : TaggedClass } & Fields> => -{ - const fields = getFieldsFromFieldsOr(fieldsOr) - const schema = getSchemaFromFieldsOr(fieldsOr) - const newFields = { _tag: getClassTag(tag) } - const taggedFields = extendFields(newFields, fields) - return class TaggedClass extends makeClass({ - kind: "TaggedClass", - identifier: identifier ?? tag, - schema: extend(schema, Struct(newFields)), - fields: taggedFields, - Base: data_.Class, - annotations - }) { - static _tag = tag - } as any -} - -/** - * @category api interface - * @since 0.67.0 - */ -export interface TaggedErrorClass extends - Class< - Self, - Fields, - Struct.Encoded, - Struct.Context, - Struct.Constructor>, - {}, - cause_.YieldableError - > -{ - readonly _tag: Tag -} - -/** - * @category classes - * @since 0.67.0 - */ -export const TaggedError = (identifier?: string) => -( - tag: Tag, - fieldsOr: Fields | HasFields, - annotations?: Annotations.Schema -): [Self] extends [never] ? MissingSelfGeneric<"TaggedError", `"Tag", `> - : TaggedErrorClass< - Self, - Tag, - { readonly _tag: tag } & Fields - > => -{ - class Base extends data_.Error {} - ;(Base.prototype as any).name = tag - const fields = getFieldsFromFieldsOr(fieldsOr) - const schema = getSchemaFromFieldsOr(fieldsOr) - const newFields = { _tag: getClassTag(tag) } - const taggedFields = extendFields(newFields, fields) - return class TaggedErrorClass extends makeClass({ - kind: "TaggedError", - identifier: identifier ?? tag, - schema: extend(schema, Struct(newFields)), - fields: taggedFields, - Base, - annotations, - disableToString: true - }) { - static _tag = tag - get message(): string { - return `{ ${ - util_.ownKeys(fields).map((p: any) => `${util_.formatPropertyKey(p)}: ${util_.formatUnknown(this[p])}`) - .join(", ") - } }` - } - } as any -} - -/** - * @since 0.67.0 - */ -export interface TaggedRequest< - Tag extends string, - A, - I, - R, - SuccessType, - SuccessEncoded, - FailureType, - FailureEncoded, - ResultR -> extends - Request.Request, - Serializable.SerializableWithResult< - A, - I, - R, - SuccessType, - SuccessEncoded, - FailureType, - FailureEncoded, - ResultR - > -{ - readonly _tag: Tag -} - -/** - * @since 0.67.0 - */ -export declare namespace TaggedRequest { - /** - * @since 0.69.0 - */ - export type Any = TaggedRequest - /** - * @since 0.69.0 - */ - export type All = - | Any - | TaggedRequest -} - -/** - * @category api interface - * @since 0.67.0 - */ -export interface TaggedRequestClass< - Self, - Tag extends string, - Payload extends Struct.Fields, - Success extends Schema.All, - Failure extends Schema.All -> extends - Class< - Self, - Payload, - Struct.Encoded, - Struct.Context, - Struct.Constructor>, - TaggedRequest< - Tag, - Self, - Struct.Encoded, - Struct.Context, - Schema.Type, - Schema.Encoded, - Schema.Type, - Schema.Encoded, - Schema.Context | Schema.Context - >, - {} - > -{ - readonly _tag: Tag - /** @since 0.69.1 */ - readonly success: Success - /** @since 0.69.1 */ - readonly failure: Failure -} - -/** - * @category classes - * @since 0.67.0 - */ -export const TaggedRequest = - (identifier?: string) => - ( - tag: Tag, - options: { - failure: Failure - success: Success - payload: Payload - }, - annotations?: Annotations.Schema - ): [Self] extends [never] ? MissingSelfGeneric<"TaggedRequest", `"Tag", SuccessSchema, FailureSchema, `> - : TaggedRequestClass< - Self, - Tag, - { readonly _tag: tag } & Payload, - Success, - Failure - > => - { - const taggedFields = extendFields({ _tag: getClassTag(tag) }, options.payload) - return class TaggedRequestClass extends makeClass({ - kind: "TaggedRequest", - identifier: identifier ?? tag, - schema: Struct(taggedFields), - fields: taggedFields, - Base: Request.Class, - annotations - }) { - static _tag = tag - static success = options.success - static failure = options.failure - get [serializable_.symbol]() { - return this.constructor - } - get [serializable_.symbolResult]() { - return { - failure: options.failure, - success: options.success - } - } - } as any - } - -const extendFields = (a: Struct.Fields, b: Struct.Fields): Struct.Fields => { - const out = { ...a } - for (const key of util_.ownKeys(b)) { - if (key in a) { - throw new Error(errors_.getASTDuplicatePropertySignatureErrorMessage(key)) - } - out[key] = b[key] - } - return out -} - -// does not overwrite existing title annotation -const orElseTitleAnnotation = (schema: Schema, title: string): Schema => { - const annotation = AST.getTitleAnnotation(schema.ast) - if (option_.isNone(annotation)) { - return schema.annotations({ title }) - } - return schema -} - -type MakeOptions = boolean | { - readonly disableValidation?: boolean -} - -const getDisableValidationMakeOption = (options: MakeOptions | undefined): boolean => - Predicate.isBoolean(options) ? options : options?.disableValidation ?? false - -const makeClass = ({ Base, annotations, disableToString, fields, identifier, kind, schema }: { - kind: "Class" | "TaggedClass" | "TaggedError" | "TaggedRequest" - identifier: string - schema: Schema.Any - fields: Struct.Fields - Base: new(...args: ReadonlyArray) => any - annotations?: Annotations.Schema | undefined - disableToString?: boolean | undefined -}): any => { - const classSymbol = Symbol.for(`@effect/schema/${kind}/${identifier}`) - const validateSchema = orElseTitleAnnotation(schema, `${identifier} (Constructor)`) - const encodedSide: Schema.Any = orElseTitleAnnotation(schema, `${identifier} (Encoded side)`) - const typeSide = orElseTitleAnnotation(typeSchema(schema), `${identifier} (Type side)`) - const fallbackInstanceOf = (u: unknown) => Predicate.hasProperty(u, classSymbol) && ParseResult.is(typeSide)(u) - const klass = class extends Base { - constructor( - props: { [x: string | symbol]: unknown } = {}, - options: MakeOptions = false - ) { - props = { ...props } - if (kind !== "Class") { - delete props["_tag"] - } - props = lazilyMergeDefaults(fields, props) - if (!getDisableValidationMakeOption(options)) { - props = ParseResult.validateSync(validateSchema)(props) - } - super(props, true) - } - - // ---------------- - // Schema interface - // ---------------- - - static [TypeId] = variance - - static get ast() { - const declaration: Schema.Any = declare( - [typeSide], - { - decode: () => (input, _, ast) => - input instanceof this || fallbackInstanceOf(input) - ? ParseResult.succeed(input) - : ParseResult.fail(new ParseResult.Type(ast, input)), - encode: () => (input, options) => - input instanceof this - ? ParseResult.succeed(input) - : ParseResult.map( - ParseResult.encodeUnknown(typeSide)(input, options), - (props) => new this(props, true) - ) - }, - { - identifier, - title: identifier, - description: `an instance of ${identifier}`, - pretty: (pretty) => (self: any) => `${identifier}(${pretty(self)})`, - // @ts-expect-error - arbitrary: (arb) => (fc) => arb(fc).map((props) => new this(props)), - equivalence: identity, - [AST.SurrogateAnnotationId]: typeSide.ast, - ...annotations - } - ) - const transformation = transform( - encodedSide, - declaration, - { strict: true, decode: (input) => new this(input, true), encode: identity } - ).annotations({ [AST.SurrogateAnnotationId]: schema.ast }) - return transformation.ast - } - - static pipe() { - return pipeArguments(this, arguments) - } - - static annotations(annotations: Annotations.Schema) { - return make(this.ast).annotations(annotations) - } - - static toString() { - return `(${String(encodedSide)} <-> ${identifier})` - } - - // ---------------- - // Class interface - // ---------------- - - static make(...args: Array) { - return new this(...args) - } - - static fields = { ...fields } - - static identifier = identifier - - static extend(identifier: string) { - return (newFieldsOr: Struct.Fields | HasFields, annotations?: Annotations.Schema) => { - const newFields = getFieldsFromFieldsOr(newFieldsOr) - const newSchema = getSchemaFromFieldsOr(newFieldsOr) - const extendedFields = extendFields(fields, newFields) - return makeClass({ - kind, - identifier, - schema: extend(schema, newSchema), - fields: extendedFields, - Base: this, - annotations - }) - } - } - - static transformOrFail(identifier: string) { - return (newFields: Struct.Fields, options: any, annotations?: Annotations.Schema) => { - const transformedFields: Struct.Fields = extendFields(fields, newFields) - return makeClass({ - kind, - identifier, - schema: transformOrFail( - schema, - typeSchema(Struct(transformedFields)), - options - ), - fields: transformedFields, - Base: this, - annotations - }) - } - } - - static transformOrFailFrom(identifier: string) { - return (newFields: Struct.Fields, options: any, annotations?: Annotations.Schema) => { - const transformedFields: Struct.Fields = extendFields(fields, newFields) - return makeClass({ - kind, - identifier, - schema: transformOrFail( - encodedSchema(schema), - Struct(transformedFields), - options - ), - fields: transformedFields, - Base: this, - annotations - }) - } - } - - // ---------------- - // other - // ---------------- - - get [classSymbol]() { - return classSymbol - } - } - if (disableToString !== true) { - Object.defineProperty(klass.prototype, "toString", { - value() { - return `${identifier}({ ${ - util_.ownKeys(fields).map((p: any) => `${util_.formatPropertyKey(p)}: ${util_.formatUnknown(this[p])}`) - .join(", ") - } })` - }, - configurable: true - }) - } - return klass -} - -/** - * @category FiberId - * @since 0.67.0 - */ -export type FiberIdEncoded = - | { - readonly _tag: "Composite" - readonly left: FiberIdEncoded - readonly right: FiberIdEncoded - } - | { - readonly _tag: "None" - } - | { - readonly _tag: "Runtime" - readonly id: number - readonly startTimeMillis: number - } - -const FiberIdNoneEncoded = Struct({ - _tag: Literal("None") -}).annotations({ identifier: "FiberIdNoneEncoded" }) - -const FiberIdRuntimeEncoded = Struct({ - _tag: Literal("Runtime"), - id: Int.annotations({ - title: "id", - description: "id" - }), - startTimeMillis: Int.annotations({ - title: "startTimeMillis", - description: "startTimeMillis" - }) -}).annotations({ identifier: "FiberIdRuntimeEncoded" }) - -const FiberIdCompositeEncoded = Struct({ - _tag: Literal("Composite"), - left: suspend(() => FiberIdEncoded), - right: suspend(() => FiberIdEncoded) -}).annotations({ identifier: "FiberIdCompositeEncoded" }) - -const FiberIdEncoded: Schema = Union( - FiberIdNoneEncoded, - FiberIdRuntimeEncoded, - FiberIdCompositeEncoded -).annotations({ identifier: "FiberIdEncoded" }) - -const fiberIdArbitrary: LazyArbitrary = (fc) => - fc.letrec((tie) => ({ - None: fc.record({ _tag: fc.constant("None" as const) }), - Runtime: fc.record({ _tag: fc.constant("Runtime" as const), id: fc.integer(), startTimeMillis: fc.integer() }), - Composite: fc.record({ _tag: fc.constant("Composite" as const), left: tie("FiberId"), right: tie("FiberId") }), - FiberId: fc.oneof(tie("None"), tie("Runtime"), tie("Composite")) as any as fastCheck_.Arbitrary - })).FiberId.map(fiberIdDecode) - -const fiberIdPretty: pretty_.Pretty = (fiberId) => { - switch (fiberId._tag) { - case "None": - return "FiberId.none" - case "Runtime": - return `FiberId.runtime(${fiberId.id}, ${fiberId.startTimeMillis})` - case "Composite": - return `FiberId.composite(${fiberIdPretty(fiberId.right)}, ${fiberIdPretty(fiberId.left)})` - } -} - -/** - * @category FiberId constructors - * @since 0.67.0 - */ -export class FiberIdFromSelf extends declare( - fiberId_.isFiberId, - { - identifier: "FiberIdFromSelf", - pretty: () => fiberIdPretty, - arbitrary: () => fiberIdArbitrary - } -) {} - -const fiberIdDecode = (input: FiberIdEncoded): fiberId_.FiberId => { - switch (input._tag) { - case "None": - return fiberId_.none - case "Runtime": - return fiberId_.runtime(input.id, input.startTimeMillis) - case "Composite": - return fiberId_.composite(fiberIdDecode(input.left), fiberIdDecode(input.right)) - } -} - -const fiberIdEncode = (input: fiberId_.FiberId): FiberIdEncoded => { - switch (input._tag) { - case "None": - return { _tag: "None" } - case "Runtime": - return { _tag: "Runtime", id: input.id, startTimeMillis: input.startTimeMillis } - case "Composite": - return { - _tag: "Composite", - left: fiberIdEncode(input.left), - right: fiberIdEncode(input.right) - } - } -} - -/** - * @category FiberId transformations - * @since 0.67.0 - */ -export class FiberId extends transform( - FiberIdEncoded, - FiberIdFromSelf, - { strict: true, decode: fiberIdDecode, encode: fiberIdEncode } -).annotations({ identifier: "FiberId" }) {} - -/** - * @category Cause utils - * @since 0.69.0 - */ -export type CauseEncoded = - | { - readonly _tag: "Empty" - } - | { - readonly _tag: "Fail" - readonly error: E - } - | { - readonly _tag: "Die" - readonly defect: D - } - | { - readonly _tag: "Interrupt" - readonly fiberId: FiberIdEncoded - } - | { - readonly _tag: "Sequential" - readonly left: CauseEncoded - readonly right: CauseEncoded - } - | { - readonly _tag: "Parallel" - readonly left: CauseEncoded - readonly right: CauseEncoded - } - -const causeDieEncoded = (defect: Schema) => - Struct({ - _tag: Literal("Die"), - defect - }) - -const CauseEmptyEncoded = Struct({ - _tag: Literal("Empty") -}) - -const causeFailEncoded = (error: Schema) => - Struct({ - _tag: Literal("Fail"), - error - }) - -const CauseInterruptEncoded = Struct({ - _tag: Literal("Interrupt"), - fiberId: FiberIdEncoded -}) - -const causeParallelEncoded = (causeEncoded: Schema, CauseEncoded, R>) => - Struct({ - _tag: Literal("Parallel"), - left: causeEncoded, - right: causeEncoded - }) - -const causeSequentialEncoded = (causeEncoded: Schema, CauseEncoded, R>) => - Struct({ - _tag: Literal("Sequential"), - left: causeEncoded, - right: causeEncoded - }) - -const causeEncoded = ( - error: Schema, - defect: Schema -): Schema, CauseEncoded, R1 | R2> => { - const recur = suspend(() => out) - const out: Schema, CauseEncoded, R1 | R2> = Union( - CauseEmptyEncoded, - causeFailEncoded(error), - causeDieEncoded(defect), - CauseInterruptEncoded, - causeSequentialEncoded(recur), - causeParallelEncoded(recur) - ).annotations({ title: `CauseEncoded<${format(error)}>` }) - return out -} - -const causeArbitrary = ( - error: LazyArbitrary, - defect: LazyArbitrary -): LazyArbitrary> => -(fc) => - fc.letrec((tie) => ({ - Empty: fc.record({ _tag: fc.constant("Empty" as const) }), - Fail: fc.record({ _tag: fc.constant("Fail" as const), error: error(fc) }), - Die: fc.record({ _tag: fc.constant("Die" as const), defect: defect(fc) }), - Interrupt: fc.record({ _tag: fc.constant("Interrupt" as const), fiberId: fiberIdArbitrary(fc) }), - Sequential: fc.record({ _tag: fc.constant("Sequential" as const), left: tie("Cause"), right: tie("Cause") }), - Parallel: fc.record({ _tag: fc.constant("Parallel" as const), left: tie("Cause"), right: tie("Cause") }), - Cause: fc.oneof( - tie("Empty"), - tie("Fail"), - tie("Die"), - tie("Interrupt"), - tie("Sequential"), - tie("Parallel") - ) as any as fastCheck_.Arbitrary> - })).Cause.map(causeDecode) - -const causePretty = (error: pretty_.Pretty): pretty_.Pretty> => (cause) => { - const f = (cause: cause_.Cause): string => { - switch (cause._tag) { - case "Empty": - return "Cause.empty" - case "Fail": - return `Cause.fail(${error(cause.error)})` - case "Die": - return `Cause.die(${cause_.pretty(cause)})` - case "Interrupt": - return `Cause.interrupt(${fiberIdPretty(cause.fiberId)})` - case "Sequential": - return `Cause.sequential(${f(cause.left)}, ${f(cause.right)})` - case "Parallel": - return `Cause.parallel(${f(cause.left)}, ${f(cause.right)})` - } - } - return f(cause) -} - -const causeParse = ( - decodeUnknown: ParseResult.DecodeUnknown, R> -): ParseResult.DeclarationDecodeUnknown, R> => -(u, options, ast) => - cause_.isCause(u) ? - toComposite(decodeUnknown(causeEncode(u), options), causeDecode, ast, u) - : ParseResult.fail(new ParseResult.Type(ast, u)) - -/** - * @category api interface - * @since 0.69.0 - */ -export interface CauseFromSelf extends - AnnotableClass< - CauseFromSelf, - cause_.Cause>, - cause_.Cause>, - Schema.Context | Schema.Context - > -{} - -/** - * @category Cause transformations - * @since 0.69.0 - */ -export const CauseFromSelf = ({ defect, error }: { - readonly error: E - readonly defect: D -}): CauseFromSelf => { - return declare( - [error, defect], - { - decode: (error, defect) => causeParse(ParseResult.decodeUnknown(causeEncoded(error, defect))), - encode: (error, defect) => causeParse(ParseResult.encodeUnknown(causeEncoded(error, defect))) - }, - { - title: `Cause<${error.ast}>`, - pretty: causePretty, - arbitrary: causeArbitrary - } - ) -} - -function causeDecode(cause: CauseEncoded): cause_.Cause { - switch (cause._tag) { - case "Empty": - return cause_.empty - case "Fail": - return cause_.fail(cause.error) - case "Die": - return cause_.die(cause.defect) - case "Interrupt": - return cause_.interrupt(fiberIdDecode(cause.fiberId)) - case "Sequential": - return cause_.sequential(causeDecode(cause.left), causeDecode(cause.right)) - case "Parallel": - return cause_.parallel(causeDecode(cause.left), causeDecode(cause.right)) - } -} - -function causeEncode(cause: cause_.Cause): CauseEncoded { - switch (cause._tag) { - case "Empty": - return { _tag: "Empty" } - case "Fail": - return { _tag: "Fail", error: cause.error } - case "Die": - return { _tag: "Die", defect: cause.defect } - case "Interrupt": - return { _tag: "Interrupt", fiberId: cause.fiberId } - case "Sequential": - return { - _tag: "Sequential", - left: causeEncode(cause.left), - right: causeEncode(cause.right) - } - case "Parallel": - return { - _tag: "Parallel", - left: causeEncode(cause.left), - right: causeEncode(cause.right) - } - } -} - -/** - * @category api interface - * @since 0.69.0 - */ -export interface Cause extends - AnnotableClass< - Cause, - cause_.Cause>, - CauseEncoded, Schema.Encoded>, - Schema.Context | Schema.Context - > -{} - -/** - * @category Cause transformations - * @since 0.69.0 - */ -export const Cause = ({ defect, error }: { - readonly error: E - readonly defect: D -}): Cause => { - const error_ = asSchema(error) - const defect_ = asSchema(defect) - return transform( - causeEncoded(error_, defect_), - CauseFromSelf({ error: typeSchema(error_), defect: Unknown }), - { strict: false, decode: causeDecode, encode: causeEncode } - ) -} - -/** - * @category api interface - * @since 0.69.0 - */ -export interface Defect extends transform {} - -/** - * Defines a schema for handling JavaScript errors (`Error` instances) and other types of defects. - * It decodes objects into Error instances if they match the expected structure (i.e., have a `message` and optionally a `name` and `stack`), - * or converts other values to their string representations. - * - * When encoding, it converts `Error` instances back into plain objects containing only the error's name and message, - * or other values into their string forms. - * - * This is useful for serializing and deserializing errors across network boundaries where error objects do not natively serialize. - * - * @category defect - * @since 0.69.0 - */ -export const Defect: Defect = transform( - Unknown, - Unknown, - { - strict: true, - decode: (u) => { - if (Predicate.isObject(u) && "message" in u && typeof u.message === "string") { - const err = new Error(u.message, { cause: u }) - if ("name" in u && typeof u.name === "string") { - err.name = u.name - } - err.stack = "stack" in u && typeof u.stack === "string" ? u.stack : "" - return err - } - return String(u) - }, - encode: (defect) => { - if (defect instanceof Error) { - return { - name: defect.name, - message: defect.message - // no stack because of security reasons - } - } - return String(defect) - } - } -).annotations({ identifier: "Defect" }) - -/** - * @category Exit utils - * @since 0.69.0 - */ -export type ExitEncoded = - | { - readonly _tag: "Failure" - readonly cause: CauseEncoded - } - | { - readonly _tag: "Success" - readonly value: A - } - -const exitFailureEncoded = ( - error: Schema, - defect: Schema -) => - Struct({ - _tag: Literal("Failure"), - cause: causeEncoded(error, defect) - }) - -const exitSuccessEncoded = ( - value: Schema -) => - Struct({ - _tag: Literal("Success"), - value - }) - -const exitEncoded = ( - value: Schema, - error: Schema, - defect: Schema -): Schema, ExitEncoded, R | ER | DR> => - Union( - exitFailureEncoded(error, defect), - exitSuccessEncoded(value) - ).annotations({ - title: `ExitEncoded<${format(value)}, ${format(error)}, ${format(defect)}>` - }) - -const exitDecode = (input: ExitEncoded): exit_.Exit => { - switch (input._tag) { - case "Failure": - return exit_.failCause(causeDecode(input.cause)) - case "Success": - return exit_.succeed(input.value) - } -} - -const exitArbitrary = ( - value: LazyArbitrary, - error: LazyArbitrary, - defect: LazyArbitrary -): LazyArbitrary> => -(fc) => - fc.oneof( - fc.record({ _tag: fc.constant("Failure" as const), cause: causeArbitrary(error, defect)(fc) }), - fc.record({ _tag: fc.constant("Success" as const), value: value(fc) }) - ).map(exitDecode) - -const exitPretty = - (value: pretty_.Pretty, error: pretty_.Pretty): pretty_.Pretty> => (exit) => - exit._tag === "Failure" - ? `Exit.failCause(${causePretty(error)(exit.cause)})` - : `Exit.succeed(${value(exit.value)})` - -const exitParse = ( - decodeUnknownValue: ParseResult.DecodeUnknown, - decodeUnknownCause: ParseResult.DecodeUnknown, ER> -): ParseResult.DeclarationDecodeUnknown, ER | R> => -(u, options, ast) => - exit_.isExit(u) ? - exit_.match(u, { - onFailure: (cause) => toComposite(decodeUnknownCause(cause, options), exit_.failCause, ast, u), - onSuccess: (value) => toComposite(decodeUnknownValue(value, options), exit_.succeed, ast, u) - }) - : ParseResult.fail(new ParseResult.Type(ast, u)) - -/** - * @category api interface - * @since 0.69.0 - */ -export interface ExitFromSelf extends - AnnotableClass< - ExitFromSelf, - exit_.Exit, Schema.Type>, - exit_.Exit, Schema.Encoded>, - Schema.Context | Schema.Context | Schema.Context - > -{} - -/** - * @category Exit transformations - * @since 0.69.0 - */ -export const ExitFromSelf = ( - { defect, failure, success }: { - readonly failure: E - readonly success: A - readonly defect: D - } -): ExitFromSelf => - declare( - [success, failure, defect], - { - decode: (success, failure, defect) => - exitParse( - ParseResult.decodeUnknown(success), - ParseResult.decodeUnknown(CauseFromSelf({ error: failure, defect })) - ), - encode: (success, failure, defect) => - exitParse( - ParseResult.encodeUnknown(success), - ParseResult.encodeUnknown(CauseFromSelf({ error: failure, defect })) - ) - }, - { - title: `Exit<${success.ast}, ${failure.ast}>`, - pretty: exitPretty, - arbitrary: exitArbitrary - } - ) - -/** - * @category api interface - * @since 0.69.0 - */ -export interface Exit extends - AnnotableClass< - Exit, - exit_.Exit, Schema.Type>, - ExitEncoded, Schema.Encoded, Schema.Encoded>, - Schema.Context | Schema.Context | Schema.Context - > -{} - -/** - * @category Exit transformations - * @since 0.69.0 - */ -export const Exit = ( - { defect, failure, success }: { - readonly failure: E - readonly success: A - readonly defect: D - } -): Exit => { - const success_ = asSchema(success) - const failure_ = asSchema(failure) - const defect_ = asSchema(defect) - return transform( - exitEncoded(success_, failure_, defect_), - ExitFromSelf({ failure: typeSchema(failure_), success: typeSchema(success_), defect: Unknown }), - { - strict: false, - decode: exitDecode, - encode: (exit) => - exit._tag === "Failure" - ? { _tag: "Failure", cause: exit.cause } as const - : { _tag: "Success", value: exit.value } as const - } - ) -} - -const hashSetArbitrary = - (item: LazyArbitrary, ctx: GenerationContext): LazyArbitrary> => (fc) => { - const items = fc.array(item(fc)) - return (ctx.depthIdentifier !== undefined ? fc.oneof(ctx, fc.constant([]), items) : items).map( - hashSet_.fromIterable - ) - } - -const hashSetPretty = (item: pretty_.Pretty): pretty_.Pretty> => (set) => - `HashSet(${Array.from(set).map((a) => item(a)).join(", ")})` - -const hashSetEquivalence = ( - item: Equivalence.Equivalence -): Equivalence.Equivalence> => { - const arrayEquivalence = array_.getEquivalence(item) - return Equivalence.make((a, b) => arrayEquivalence(Array.from(a), Array.from(b))) -} - -const hashSetParse = ( - decodeUnknown: ParseResult.DecodeUnknown, R> -): ParseResult.DeclarationDecodeUnknown, R> => -(u, options, ast) => - hashSet_.isHashSet(u) ? - toComposite(decodeUnknown(Array.from(u), options), hashSet_.fromIterable, ast, u) - : ParseResult.fail(new ParseResult.Type(ast, u)) - -/** - * @category api interface - * @since 0.67.0 - */ -export interface HashSetFromSelf extends - AnnotableClass< - HashSetFromSelf, - hashSet_.HashSet>, - hashSet_.HashSet>, - Schema.Context - > -{} - -/** - * @category HashSet transformations - * @since 0.67.0 - */ -export const HashSetFromSelf = ( - value: Value -): HashSetFromSelf => { - return declare( - [value], - { - decode: (item) => hashSetParse(ParseResult.decodeUnknown(Array$(item))), - encode: (item) => hashSetParse(ParseResult.encodeUnknown(Array$(item))) - }, - { - description: `HashSet<${format(value)}>`, - pretty: hashSetPretty, - arbitrary: hashSetArbitrary, - equivalence: hashSetEquivalence - } - ) -} - -/** - * @category api interface - * @since 0.67.0 - */ -export interface HashSet extends - AnnotableClass< - HashSet, - hashSet_.HashSet>, - ReadonlyArray>, - Schema.Context - > -{} - -/** - * @category HashSet transformations - * @since 0.67.0 - */ -export const HashSet = (value: Value): HashSet => { - const value_ = asSchema(value) - return transform( - Array$(value_), - HashSetFromSelf(typeSchema(value_)), - { strict: true, decode: (as) => hashSet_.fromIterable(as), encode: (set) => Array.from(set) } - ) -} - -const hashMapArbitrary = ( - key: LazyArbitrary, - value: LazyArbitrary, - ctx: GenerationContext -): LazyArbitrary> => -(fc) => { - const items = fc.array(fc.tuple(key(fc), value(fc))) - return (ctx.depthIdentifier !== undefined ? fc.oneof(ctx, fc.constant([]), items) : items).map(hashMap_.fromIterable) -} - -const hashMapPretty = ( - key: pretty_.Pretty, - value: pretty_.Pretty -): pretty_.Pretty> => -(map) => - `HashMap([${ - Array.from(map) - .map(([k, v]) => `[${key(k)}, ${value(v)}]`) - .join(", ") - }])` - -const hashMapEquivalence = ( - key: Equivalence.Equivalence, - value: Equivalence.Equivalence -): Equivalence.Equivalence> => { - const arrayEquivalence = array_.getEquivalence( - Equivalence.make<[K, V]>(([ka, va], [kb, vb]) => key(ka, kb) && value(va, vb)) - ) - return Equivalence.make((a, b) => arrayEquivalence(Array.from(a), Array.from(b))) -} - -const hashMapParse = ( - decodeUnknown: ParseResult.DecodeUnknown, R> -): ParseResult.DeclarationDecodeUnknown, R> => -(u, options, ast) => - hashMap_.isHashMap(u) ? - toComposite(decodeUnknown(Array.from(u), options), hashMap_.fromIterable, ast, u) - : ParseResult.fail(new ParseResult.Type(ast, u)) - -/** - * @category api interface - * @since 0.67.0 - */ -export interface HashMapFromSelf extends - AnnotableClass< - HashMapFromSelf, - hashMap_.HashMap, Schema.Type>, - hashMap_.HashMap, Schema.Encoded>, - Schema.Context | Schema.Context - > -{} - -/** - * @category HashMap transformations - * @since 0.67.0 - */ -export const HashMapFromSelf = ({ key, value }: { - readonly key: K - readonly value: V -}): HashMapFromSelf => { - return declare( - [key, value], - { - decode: (key, value) => hashMapParse(ParseResult.decodeUnknown(Array$(Tuple(key, value)))), - encode: (key, value) => hashMapParse(ParseResult.encodeUnknown(Array$(Tuple(key, value)))) - }, - { - description: `HashMap<${format(key)}, ${format(value)}>`, - pretty: hashMapPretty, - arbitrary: hashMapArbitrary, - equivalence: hashMapEquivalence - } - ) -} - -/** - * @category api interface - * @since 0.67.0 - */ -export interface HashMap extends - AnnotableClass< - HashMap, - hashMap_.HashMap, Schema.Type>, - ReadonlyArray, Schema.Encoded]>, - Schema.Context | Schema.Context - > -{} - -/** - * @category HashMap transformations - * @since 0.67.0 - */ -export const HashMap = ({ key, value }: { - readonly key: K - readonly value: V -}): HashMap => { - const key_ = asSchema(key) - const value_ = asSchema(value) - return transform( - Array$(Tuple(key_, value_)), - HashMapFromSelf({ key: typeSchema(key_), value: typeSchema(value_) }), - { strict: true, decode: (as) => hashMap_.fromIterable(as), encode: (map) => Array.from(map) } - ) -} - -const listArbitrary = (item: LazyArbitrary, ctx: GenerationContext): LazyArbitrary> => (fc) => { - const items = fc.array(item(fc)) - return (ctx.depthIdentifier !== undefined ? fc.oneof(ctx, fc.constant([]), items) : items).map(list_.fromIterable) -} - -const listPretty = (item: pretty_.Pretty): pretty_.Pretty> => (set) => - `List(${Array.from(set).map((a) => item(a)).join(", ")})` - -const listEquivalence = ( - item: Equivalence.Equivalence -): Equivalence.Equivalence> => { - const arrayEquivalence = array_.getEquivalence(item) - return Equivalence.make((a, b) => arrayEquivalence(Array.from(a), Array.from(b))) -} - -const listParse = ( - decodeUnknown: ParseResult.DecodeUnknown, R> -): ParseResult.DeclarationDecodeUnknown, R> => -(u, options, ast) => - list_.isList(u) ? - toComposite(decodeUnknown(Array.from(u), options), list_.fromIterable, ast, u) - : ParseResult.fail(new ParseResult.Type(ast, u)) - -/** - * @category api interface - * @since 0.67.0 - */ -export interface ListFromSelf extends - AnnotableClass< - ListFromSelf, - list_.List>, - list_.List>, - Schema.Context - > -{} - -/** - * @category List transformations - * @since 0.67.0 - */ -export const ListFromSelf = ( - value: Value -): ListFromSelf => { - return declare( - [value], - { - decode: (item) => listParse(ParseResult.decodeUnknown(Array$(item))), - encode: (item) => listParse(ParseResult.encodeUnknown(Array$(item))) - }, - { - description: `List<${format(value)}>`, - pretty: listPretty, - arbitrary: listArbitrary, - equivalence: listEquivalence - } - ) -} - -/** - * @category api interface - * @since 0.67.0 - */ -export interface List extends - AnnotableClass< - List, - list_.List>, - ReadonlyArray>, - Schema.Context - > -{} - -/** - * @category List transformations - * @since 0.67.0 - */ -export const List = (value: Value): List => { - const value_ = asSchema(value) - return transform( - Array$(value_), - ListFromSelf(typeSchema(value_)), - { strict: true, decode: (as) => list_.fromIterable(as), encode: (set) => Array.from(set) } - ) -} - -const sortedSetArbitrary = - (item: LazyArbitrary, ord: Order.Order, ctx: GenerationContext): LazyArbitrary> => - (fc) => { - const items = fc.array(item(fc)) - return (ctx.depthIdentifier !== undefined ? fc.oneof(ctx, fc.constant([]), items) : items).map((as) => - sortedSet_.fromIterable(as, ord) - ) - } - -const sortedSetPretty = (item: pretty_.Pretty): pretty_.Pretty> => (set) => - `new SortedSet([${Array.from(sortedSet_.values(set)).map((a) => item(a)).join(", ")}])` - -const sortedSetParse = ( - decodeUnknown: ParseResult.DecodeUnknown, R>, - ord: Order.Order -): ParseResult.DeclarationDecodeUnknown, R> => -(u, options, ast) => - sortedSet_.isSortedSet(u) ? - toComposite( - decodeUnknown(Array.from(sortedSet_.values(u)), options), - (as): sortedSet_.SortedSet => sortedSet_.fromIterable(as, ord), - ast, - u - ) - : ParseResult.fail(new ParseResult.Type(ast, u)) - -/** - * @category api interface - * @since 0.67.0 - */ -export interface SortedSetFromSelf extends - AnnotableClass< - SortedSetFromSelf, - sortedSet_.SortedSet>, - sortedSet_.SortedSet>, - Schema.Context - > -{} - -/** - * @category SortedSet transformations - * @since 0.67.0 - */ -export const SortedSetFromSelf = ( - value: Value, - ordA: Order.Order>, - ordI: Order.Order> -): SortedSetFromSelf => { - return declare( - [value], - { - decode: (item) => sortedSetParse(ParseResult.decodeUnknown(Array$(item)), ordA), - encode: (item) => sortedSetParse(ParseResult.encodeUnknown(Array$(item)), ordI) - }, - { - description: `SortedSet<${format(value)}>`, - pretty: sortedSetPretty, - arbitrary: (arb, ctx) => sortedSetArbitrary(arb, ordA, ctx), - equivalence: () => sortedSet_.getEquivalence>() - } - ) -} - -/** - * @category api interface - * @since 0.67.0 - */ -export interface SortedSet extends - AnnotableClass< - SortedSet, - sortedSet_.SortedSet>, - ReadonlyArray>, - Schema.Context - > -{} - -/** - * @category SortedSet transformations - * @since 0.67.0 - */ -export const SortedSet = ( - value: Value, - ordA: Order.Order> -): SortedSet => { - const value_ = asSchema(value) - const to = typeSchema(value_) - return transform( - Array$(value_), - SortedSetFromSelf(to, ordA, ordA), - { - strict: true, - decode: (as) => sortedSet_.fromIterable(as, ordA), - encode: (set) => Array.from(sortedSet_.values(set)) - } - ) -} - -/** - * Converts an arbitrary value to a `boolean` by testing whether it is truthy. - * Uses `!!val` to coerce the value to a `boolean`. - * - * @see https://developer.mozilla.org/docs/Glossary/Truthy - * @category boolean constructors - * @since 0.67.0 - */ -export class BooleanFromUnknown extends transform( - Unknown, - Boolean$, - { strict: true, decode: Predicate.isTruthy, encode: identity } -).annotations({ identifier: "BooleanFromUnknown" }) {} - /** - * @category Config validations - * @since 0.67.12 + * @category re-exports + * @since 0.76.0 */ -export const Config = (name: string, schema: Schema): config_.Config => { - const decodeEither_ = decodeEither(schema) - return config_.string(name).pipe( - config_.mapOrFail((a) => - decodeEither_(a).pipe( - either_.mapLeft((error) => configError_.InvalidData([], TreeFormatter.formatErrorSync(error))) - ) - ) - ) -} +export * from "effect/Schema" diff --git a/packages/schema/src/Serializable.ts b/packages/schema/src/Serializable.ts index fecaaa3b2c..560b9b54fd 100644 --- a/packages/schema/src/Serializable.ts +++ b/packages/schema/src/Serializable.ts @@ -1,385 +1,9 @@ /** * @since 0.67.0 */ -import type * as Effect from "effect/Effect" -import type * as Exit from "effect/Exit" -import { dual } from "effect/Function" -import { globalValue } from "effect/GlobalValue" -import * as serializable_ from "./internal/serializable.js" -import type * as ParseResult from "./ParseResult.js" -import * as Schema from "./Schema.js" - -// --------------------------------------------- -// Serializable -// --------------------------------------------- - -/** - * @since 0.67.0 - * @category symbol - */ -export const symbol: unique symbol = serializable_.symbol as any - -/** - * The `Serializable` trait allows objects to define their own schema for - * serialization. - * - * @since 0.67.0 - * @category model - */ -export interface Serializable { - readonly [symbol]: Schema.Schema -} - -/** - * @since 0.67.0 - * @category model - */ -export declare namespace Serializable { - /** - * @since 0.68.15 - */ - export type Type = T extends Serializable ? A : never - /** - * @since 0.68.15 - */ - export type Encoded = T extends Serializable ? I : never - /** - * @since 0.67.0 - */ - export type Context = T extends Serializable ? R : never - /** - * @since 0.69.0 - */ - export type Any = Serializable - /** - * @since 0.69.0 - */ - export type All = - | Any - | Serializable - | Serializable - | Serializable -} - -/** - * @since 0.69.0 - */ -export const asSerializable = ( - serializable: S -): Serializable, Serializable.Encoded, Serializable.Context> => serializable as any - -/** - * @since 0.67.0 - * @category accessor - */ -export const selfSchema = (self: Serializable): Schema.Schema => self[symbol] - -/** - * @since 0.67.0 - * @category encoding - */ -export const serialize = (self: Serializable): Effect.Effect => - Schema.encodeUnknown(self[symbol])(self) - -/** - * @since 0.67.0 - * @category decoding - */ -export const deserialize: { - (value: unknown): (self: Serializable) => Effect.Effect - (self: Serializable, value: unknown): Effect.Effect -} = dual( - 2, - (self: Serializable, value: unknown): Effect.Effect => - Schema.decodeUnknown(self[symbol])(value) -) - -// --------------------------------------------- -// WithResult -// --------------------------------------------- - -/** - * @since 0.67.0 - * @category symbol - */ -export const symbolResult: unique symbol = serializable_.symbolResult as any - -/** - * The `WithResult` trait is designed to encapsulate the outcome of an - * operation, distinguishing between success and failure cases. Each case is - * associated with a schema that defines the structure and types of the success - * or failure data. - * - * @since 0.67.0 - * @category model - */ -export interface WithResult { - readonly [symbolResult]: { - readonly success: Schema.Schema - readonly failure: Schema.Schema - } -} - -/** - * @since 0.67.0 - * @category model - */ -export declare namespace WithResult { - /** - * @since 0.68.16 - */ - export type Success = T extends WithResult ? _A : never - /** - * @since 0.69.0 - */ - export type SuccessEncoded = T extends WithResult ? _I : never - /** - * @since 0.69.0 - */ - export type Failure = T extends WithResult ? _E : never - /** - * @since 0.69.0 - */ - export type FailureEncoded = T extends WithResult ? _EI : never - - /** - * @since 0.67.0 - */ - export type Context = T extends WithResult ? R : never - /** - * @since 0.69.0 - */ - export type Any = WithResult - /** - * @since 0.69.0 - */ - export type All = - | Any - | WithResult -} - -/** - * @since 0.69.0 - */ -export const asWithResult = ( - withExit: WR -): WithResult< - WithResult.Success, - WithResult.SuccessEncoded, - WithResult.Failure, - WithResult.FailureEncoded, - WithResult.Context -> => withExit as any - -/** - * @since 0.67.0 - * @category accessor - */ -export const failureSchema = (self: WithResult): Schema.Schema => - self[symbolResult].failure - -/** - * @since 0.67.0 - * @category accessor - */ -export const successSchema = (self: WithResult): Schema.Schema => - self[symbolResult].success - -const exitSchemaCache = globalValue( - "@effect/schema/Serializable/exitSchemaCache", - () => new WeakMap>() -) - -/** - * @since 0.67.0 - * @category accessor - */ -export const exitSchema = (self: WithResult): Schema.Schema< - Exit.Exit, - Schema.ExitEncoded, - R -> => { - const proto = Object.getPrototypeOf(self) - if (!(symbolResult in proto)) { - return Schema.Exit({ - failure: failureSchema(self), - success: successSchema(self), - defect: Schema.Defect - }) - } - let schema = exitSchemaCache.get(proto) - if (schema === undefined) { - schema = Schema.Exit({ - failure: failureSchema(self), - success: successSchema(self), - defect: Schema.Defect - }) - exitSchemaCache.set(proto, schema) - } - return schema -} - -/** - * @since 0.67.0 - * @category encoding - */ -export const serializeFailure: { - (value: FA): ( - self: WithResult - ) => Effect.Effect - (self: WithResult, value: FA): Effect.Effect -} = dual( - 2, - (self: WithResult, value: FA): Effect.Effect => - Schema.encode(self[symbolResult].failure)(value) -) - -/** - * @since 0.67.0 - * @category decoding - */ -export const deserializeFailure: { - ( - value: unknown - ): (self: WithResult) => Effect.Effect - (self: WithResult, value: unknown): Effect.Effect -} = dual( - 2, - ( - self: WithResult, - value: unknown - ): Effect.Effect => Schema.decodeUnknown(self[symbolResult].failure)(value) -) - -/** - * @since 0.67.0 - * @category encoding - */ -export const serializeSuccess: { - (value: SA): ( - self: WithResult - ) => Effect.Effect - (self: WithResult, value: SA): Effect.Effect -} = dual( - 2, - (self: WithResult, value: SA): Effect.Effect => - Schema.encode(self[symbolResult].success)(value) -) - -/** - * @since 0.67.0 - * @category decoding - */ -export const deserializeSuccess: { - (value: unknown): ( - self: WithResult - ) => Effect.Effect - (self: WithResult, value: unknown): Effect.Effect -} = dual( - 2, - ( - self: WithResult, - value: unknown - ): Effect.Effect => Schema.decodeUnknown(self[symbolResult].success)(value) -) - -/** - * @since 0.67.0 - * @category encoding - */ -export const serializeExit: { - (value: Exit.Exit): ( - self: WithResult - ) => Effect.Effect, ParseResult.ParseError, R> - ( - self: WithResult, - value: Exit.Exit - ): Effect.Effect, ParseResult.ParseError, R> -} = dual(2, ( - self: WithResult, - value: Exit.Exit -): Effect.Effect, ParseResult.ParseError, R> => - Schema.encode(exitSchema(self))(value)) - -/** - * @since 0.67.0 - * @category decoding - */ -export const deserializeExit: { - (value: unknown): ( - self: WithResult - ) => Effect.Effect, ParseResult.ParseError, R> - ( - self: WithResult, - value: unknown - ): Effect.Effect, ParseResult.ParseError, R> -} = dual(2, ( - self: WithResult, - value: unknown -): Effect.Effect, ParseResult.ParseError, R> => Schema.decodeUnknown(exitSchema(self))(value)) - -// --------------------------------------------- -// SerializableWithResult -// --------------------------------------------- - -/** - * The `SerializableWithResult` trait is specifically designed to model remote - * procedures that require serialization of their input and output, managing - * both successful and failed outcomes. - * - * This trait combines functionality from both the `Serializable` and `WithResult` - * traits to handle data serialization and the bifurcation of operation results - * into success or failure categories. - * - * @since 0.67.0 - * @category model - */ -export interface SerializableWithResult< - A, - I, - R, - Success, - SuccessEncoded, - Failure, - FailureEncoded, - ResultR -> extends Serializable, WithResult {} - -/** - * @since 0.67.0 - * @category model - */ -export declare namespace SerializableWithResult { - /** - * @since 0.69.0 - */ - export type Context

    = P extends + SerializableWithResult ? SR | RR + : never + /** + * @since 3.10.0 + */ + export type Any = SerializableWithResult + /** + * @since 3.10.0 + */ + export type All = + | Any + | SerializableWithResult +} + +/** + * @since 3.10.0 + */ +export const asSerializableWithResult = ( + procedure: SWR +): SerializableWithResult< + Serializable.Type, + Serializable.Encoded, + Serializable.Context, + WithResult.Success, + WithResult.SuccessEncoded, + WithResult.Failure, + WithResult.FailureEncoded, + WithResult.Context +> => procedure as any + +/** + * @since 3.10.0 + */ +export interface TaggedRequest< + Tag extends string, + A, + I, + R, + SuccessType, + SuccessEncoded, + FailureType, + FailureEncoded, + ResultR +> extends + Request.Request, + SerializableWithResult< + A, + I, + R, + SuccessType, + SuccessEncoded, + FailureType, + FailureEncoded, + ResultR + > +{ + readonly _tag: Tag +} + +/** + * @since 3.10.0 + */ +export declare namespace TaggedRequest { + /** + * @since 3.10.0 + */ + export type Any = TaggedRequest + /** + * @since 3.10.0 + */ + export type All = + | Any + | TaggedRequest +} + +/** + * @category api interface + * @since 3.10.0 + */ +export interface TaggedRequestClass< + Self, + Tag extends string, + Payload extends Struct.Fields, + Success extends Schema.All, + Failure extends Schema.All +> extends + Class< + Self, + Payload, + Struct.Encoded, + Struct.Context, + Struct.Constructor>, + TaggedRequest< + Tag, + Self, + Struct.Encoded, + Struct.Context, + Schema.Type, + Schema.Encoded, + Schema.Type, + Schema.Encoded, + Schema.Context | Schema.Context + >, + {} + > +{ + readonly _tag: Tag + readonly success: Success + readonly failure: Failure +} + +/** + * @category classes + * @since 3.10.0 + */ +export const TaggedRequest = + (identifier?: string) => + ( + tag: Tag, + options: { + failure: Failure + success: Success + payload: Payload + }, + annotations?: Annotations.Schema + ): [Self] extends [never] ? MissingSelfGeneric<"TaggedRequest", `"Tag", SuccessSchema, FailureSchema, `> + : TaggedRequestClass< + Self, + Tag, + { readonly _tag: tag } & Payload, + Success, + Failure + > => + { + const taggedFields = extendFields({ _tag: getClassTag(tag) }, options.payload) + return class TaggedRequestClass extends makeClass({ + kind: "TaggedRequest", + identifier: identifier ?? tag, + schema: Struct(taggedFields), + fields: taggedFields, + Base: Request.Class, + annotations + }) { + static _tag = tag + static success = options.success + static failure = options.failure + get [symbolSerializable]() { + return this.constructor + } + get [symbolWithResult]() { + return { + failure: options.failure, + success: options.success + } + } + } as any + } diff --git a/packages/effect/src/Serializable.ts b/packages/effect/src/Serializable.ts deleted file mode 100644 index 060496f90f..0000000000 --- a/packages/effect/src/Serializable.ts +++ /dev/null @@ -1,385 +0,0 @@ -/** - * @since 3.10.0 - */ -import type * as Effect from "./Effect.js" -import type * as Exit from "./Exit.js" -import { dual } from "./Function.js" -import { globalValue } from "./GlobalValue.js" -import * as serializable_ from "./internal/schema/serializable.js" -import type * as ParseResult from "./ParseResult.js" -import * as Schema from "./Schema.js" - -// --------------------------------------------- -// Serializable -// --------------------------------------------- - -/** - * @since 3.10.0 - * @category symbol - */ -export const symbol: unique symbol = serializable_.symbol as any - -/** - * The `Serializable` trait allows objects to define their own schema for - * serialization. - * - * @since 3.10.0 - * @category model - */ -export interface Serializable { - readonly [symbol]: Schema.Schema -} - -/** - * @since 3.10.0 - * @category model - */ -export declare namespace Serializable { - /** - * @since 3.10.0 - */ - export type Type = T extends Serializable ? A : never - /** - * @since 3.10.0 - */ - export type Encoded = T extends Serializable ? I : never - /** - * @since 3.10.0 - */ - export type Context = T extends Serializable ? R : never - /** - * @since 3.10.0 - */ - export type Any = Serializable - /** - * @since 3.10.0 - */ - export type All = - | Any - | Serializable - | Serializable - | Serializable -} - -/** - * @since 3.10.0 - */ -export const asSerializable = ( - serializable: S -): Serializable, Serializable.Encoded, Serializable.Context> => serializable as any - -/** - * @since 3.10.0 - * @category accessor - */ -export const selfSchema = (self: Serializable): Schema.Schema => self[symbol] - -/** - * @since 3.10.0 - * @category encoding - */ -export const serialize = (self: Serializable): Effect.Effect => - Schema.encodeUnknown(self[symbol])(self) - -/** - * @since 3.10.0 - * @category decoding - */ -export const deserialize: { - (value: unknown): (self: Serializable) => Effect.Effect - (self: Serializable, value: unknown): Effect.Effect -} = dual( - 2, - (self: Serializable, value: unknown): Effect.Effect => - Schema.decodeUnknown(self[symbol])(value) -) - -// --------------------------------------------- -// WithResult -// --------------------------------------------- - -/** - * @since 3.10.0 - * @category symbol - */ -export const symbolResult: unique symbol = serializable_.symbolResult as any - -/** - * The `WithResult` trait is designed to encapsulate the outcome of an - * operation, distinguishing between success and failure cases. Each case is - * associated with a schema that defines the structure and types of the success - * or failure data. - * - * @since 3.10.0 - * @category model - */ -export interface WithResult { - readonly [symbolResult]: { - readonly success: Schema.Schema - readonly failure: Schema.Schema - } -} - -/** - * @since 3.10.0 - * @category model - */ -export declare namespace WithResult { - /** - * @since 3.10.0 - */ - export type Success = T extends WithResult ? _A : never - /** - * @since 3.10.0 - */ - export type SuccessEncoded = T extends WithResult ? _I : never - /** - * @since 3.10.0 - */ - export type Failure = T extends WithResult ? _E : never - /** - * @since 3.10.0 - */ - export type FailureEncoded = T extends WithResult ? _EI : never - - /** - * @since 3.10.0 - */ - export type Context = T extends WithResult ? R : never - /** - * @since 3.10.0 - */ - export type Any = WithResult - /** - * @since 3.10.0 - */ - export type All = - | Any - | WithResult -} - -/** - * @since 3.10.0 - */ -export const asWithResult = ( - withExit: WR -): WithResult< - WithResult.Success, - WithResult.SuccessEncoded, - WithResult.Failure, - WithResult.FailureEncoded, - WithResult.Context -> => withExit as any - -/** - * @since 3.10.0 - * @category accessor - */ -export const failureSchema = (self: WithResult): Schema.Schema => - self[symbolResult].failure - -/** - * @since 3.10.0 - * @category accessor - */ -export const successSchema = (self: WithResult): Schema.Schema => - self[symbolResult].success - -const exitSchemaCache = globalValue( - "effect/Schema/Serializable/exitSchemaCache", - () => new WeakMap>() -) - -/** - * @since 3.10.0 - * @category accessor - */ -export const exitSchema = (self: WithResult): Schema.Schema< - Exit.Exit, - Schema.ExitEncoded, - R -> => { - const proto = Object.getPrototypeOf(self) - if (!(symbolResult in proto)) { - return Schema.Exit({ - failure: failureSchema(self), - success: successSchema(self), - defect: Schema.Defect - }) - } - let schema = exitSchemaCache.get(proto) - if (schema === undefined) { - schema = Schema.Exit({ - failure: failureSchema(self), - success: successSchema(self), - defect: Schema.Defect - }) - exitSchemaCache.set(proto, schema) - } - return schema -} - -/** - * @since 3.10.0 - * @category encoding - */ -export const serializeFailure: { - (value: FA): ( - self: WithResult - ) => Effect.Effect - (self: WithResult, value: FA): Effect.Effect -} = dual( - 2, - (self: WithResult, value: FA): Effect.Effect => - Schema.encode(self[symbolResult].failure)(value) -) - -/** - * @since 3.10.0 - * @category decoding - */ -export const deserializeFailure: { - ( - value: unknown - ): (self: WithResult) => Effect.Effect - (self: WithResult, value: unknown): Effect.Effect -} = dual( - 2, - ( - self: WithResult, - value: unknown - ): Effect.Effect => Schema.decodeUnknown(self[symbolResult].failure)(value) -) - -/** - * @since 3.10.0 - * @category encoding - */ -export const serializeSuccess: { - (value: SA): ( - self: WithResult - ) => Effect.Effect - (self: WithResult, value: SA): Effect.Effect -} = dual( - 2, - (self: WithResult, value: SA): Effect.Effect => - Schema.encode(self[symbolResult].success)(value) -) - -/** - * @since 3.10.0 - * @category decoding - */ -export const deserializeSuccess: { - (value: unknown): ( - self: WithResult - ) => Effect.Effect - (self: WithResult, value: unknown): Effect.Effect -} = dual( - 2, - ( - self: WithResult, - value: unknown - ): Effect.Effect => Schema.decodeUnknown(self[symbolResult].success)(value) -) - -/** - * @since 3.10.0 - * @category encoding - */ -export const serializeExit: { - (value: Exit.Exit): ( - self: WithResult - ) => Effect.Effect, ParseResult.ParseError, R> - ( - self: WithResult, - value: Exit.Exit - ): Effect.Effect, ParseResult.ParseError, R> -} = dual(2, ( - self: WithResult, - value: Exit.Exit -): Effect.Effect, ParseResult.ParseError, R> => - Schema.encode(exitSchema(self))(value)) - -/** - * @since 3.10.0 - * @category decoding - */ -export const deserializeExit: { - (value: unknown): ( - self: WithResult - ) => Effect.Effect, ParseResult.ParseError, R> - ( - self: WithResult, - value: unknown - ): Effect.Effect, ParseResult.ParseError, R> -} = dual(2, ( - self: WithResult, - value: unknown -): Effect.Effect, ParseResult.ParseError, R> => Schema.decodeUnknown(exitSchema(self))(value)) - -// --------------------------------------------- -// SerializableWithResult -// --------------------------------------------- - -/** - * The `SerializableWithResult` trait is specifically designed to model remote - * procedures that require serialization of their input and output, managing - * both successful and failed outcomes. - * - * This trait combines functionality from both the `Serializable` and `WithResult` - * traits to handle data serialization and the bifurcation of operation results - * into success or failure categories. - * - * @since 3.10.0 - * @category model - */ -export interface SerializableWithResult< - A, - I, - R, - Success, - SuccessEncoded, - Failure, - FailureEncoded, - ResultR -> extends Serializable, WithResult {} - -/** - * @since 3.10.0 - * @category model - */ -export declare namespace SerializableWithResult { - /** - * @since 3.10.0 - */ - export type Context

    = P extends - SerializableWithResult ? SR | RR - : never - /** - * @since 3.10.0 - */ - export type Any = SerializableWithResult - /** - * @since 3.10.0 - */ - export type All = - | Any - | SerializableWithResult -} - -/** - * @since 3.10.0 - */ -export const asSerializableWithResult = ( - procedure: SWR -): SerializableWithResult< - Serializable.Type, - Serializable.Encoded, - Serializable.Context, - WithResult.Success, - WithResult.SuccessEncoded, - WithResult.Failure, - WithResult.FailureEncoded, - WithResult.Context -> => procedure as any diff --git a/packages/effect/src/index.ts b/packages/effect/src/index.ts index 388a974c13..e38dc4864f 100644 --- a/packages/effect/src/index.ts +++ b/packages/effect/src/index.ts @@ -796,11 +796,6 @@ export * as ScopedRef from "./ScopedRef.js" */ export * as Secret from "./Secret.js" -/** - * @since 3.10.0 - */ -export * as Serializable from "./Serializable.js" - /** * @since 2.0.0 */ diff --git a/packages/effect/src/internal/schema/serializable.ts b/packages/effect/src/internal/schema/serializable.ts deleted file mode 100644 index 52e0539fac..0000000000 --- a/packages/effect/src/internal/schema/serializable.ts +++ /dev/null @@ -1,9 +0,0 @@ -/** @internal */ -export const symbol: unique symbol = Symbol.for( - "effect/Schema/Serializable/symbol" -) - -/** @internal */ -export const symbolResult: unique symbol = Symbol.for( - "effect/Schema/Serializable/symbolResult" -) diff --git a/packages/effect/test/Schema/Schema/Class/TaggedRequest.test.ts b/packages/effect/test/Schema/Schema/Class/TaggedRequest.test.ts index 013a05f5dd..b8a976ee78 100644 --- a/packages/effect/test/Schema/Schema/Class/TaggedRequest.test.ts +++ b/packages/effect/test/Schema/Schema/Class/TaggedRequest.test.ts @@ -3,7 +3,6 @@ import * as Equal from "effect/Equal" import * as ParseResult from "effect/ParseResult" import * as Request from "effect/Request" import * as S from "effect/Schema" -import * as Serializable from "effect/Serializable" import * as Util from "effect/test/Schema/TestUtils" import { assert, describe, expect, it } from "vitest" @@ -112,28 +111,28 @@ describe("TaggedRequest", () => { const req = new MyRequest({ id: 1 }) assert.deepStrictEqual( - Serializable.serialize(req).pipe(Effect.runSync), + S.serialize(req).pipe(Effect.runSync), { _tag: "MyRequest", id: 1 } ) assert(Equal.equals( - Serializable.deserialize(req, { _tag: "MyRequest", id: 1 }).pipe(Effect.runSync), + S.deserialize(req, { _tag: "MyRequest", id: 1 }).pipe(Effect.runSync), req )) assert.deepStrictEqual( - Serializable.serializeExit(req, Exit.fail("fail")).pipe(Effect.runSync), + S.serializeExit(req, Exit.fail("fail")).pipe(Effect.runSync), { _tag: "Failure", cause: { _tag: "Fail", error: "fail" } } ) assert.deepStrictEqual( - Serializable.deserializeExit(req, { _tag: "Failure", cause: { _tag: "Fail", error: "fail" } }) + S.deserializeExit(req, { _tag: "Failure", cause: { _tag: "Fail", error: "fail" } }) .pipe(Effect.runSync), Exit.fail("fail") ) assert.deepStrictEqual( - Serializable.serializeExit(req, Exit.succeed(123)).pipe(Effect.runSync), + S.serializeExit(req, Exit.succeed(123)).pipe(Effect.runSync), { _tag: "Success", value: "123" } ) assert.deepStrictEqual( - Serializable.deserializeExit(req, { _tag: "Success", value: "123" }).pipe(Effect.runSync), + S.deserializeExit(req, { _tag: "Success", value: "123" }).pipe(Effect.runSync), Exit.succeed(123) ) }) @@ -157,28 +156,28 @@ describe("TaggedRequest", () => { expect(String(req)).toEqual(`MyRequest({ "_tag": "MyRequest", "id": 1 })`) assert.deepStrictEqual( - Serializable.serialize(req).pipe( + S.serialize(req).pipe( Effect.provideService(Id, 1), Effect.runSync ), { _tag: "MyRequest", id: 1 } ) assert.deepStrictEqual( - Serializable.deserialize(req, { _tag: "MyRequest", id: 1 }).pipe( + S.deserialize(req, { _tag: "MyRequest", id: 1 }).pipe( Effect.provideService(Id, 1), Effect.runSync ), req ) assert.deepStrictEqual( - Serializable.serializeExit(req, Exit.fail("fail")).pipe( + S.serializeExit(req, Exit.fail("fail")).pipe( Effect.provideService(Name, "fail"), Effect.runSync ), { _tag: "Failure", cause: { _tag: "Fail", error: "fail" } } ) assert.deepStrictEqual( - Serializable.deserializeExit(req, { _tag: "Failure", cause: { _tag: "Fail", error: "fail" } }) + S.deserializeExit(req, { _tag: "Failure", cause: { _tag: "Fail", error: "fail" } }) .pipe( Effect.provideService(Name, "fail"), Effect.runSync diff --git a/packages/effect/test/Schema/Serializable.test.ts b/packages/effect/test/Schema/Serializable.test.ts index 0cf09343f6..df403bc1e3 100644 --- a/packages/effect/test/Schema/Serializable.test.ts +++ b/packages/effect/test/Schema/Serializable.test.ts @@ -1,6 +1,5 @@ import { Effect, Exit } from "effect" import * as S from "effect/Schema" -import * as Serializable from "effect/Serializable" import { assert, describe, test } from "vitest" class Person extends S.Class("Person")({ @@ -11,10 +10,10 @@ class Person extends S.Class("Person")({ class GetPersonById extends S.Class("GetPersonById")({ id: S.Number }) { - get [Serializable.symbol]() { + get [S.symbolSerializable]() { return GetPersonById } - get [Serializable.symbolResult]() { + get [S.symbolWithResult]() { return { success: Person, failure: S.String, @@ -26,7 +25,7 @@ class GetPersonById extends S.Class("GetPersonById")({ describe("Serializable", () => { test("serialize", () => { const req = new GetPersonById({ id: 123 }) - assert.deepStrictEqual(Effect.runSync(Serializable.serialize(req)), { + assert.deepStrictEqual(Effect.runSync(S.serialize(req)), { id: 123 }) }) @@ -34,7 +33,7 @@ describe("Serializable", () => { test("deserialize", () => { const req = new GetPersonById({ id: 123 }) assert.deepStrictEqual( - Effect.runSync(Serializable.deserialize(req, { + Effect.runSync(S.deserialize(req, { id: 456 })), new GetPersonById({ id: 456 }) @@ -45,7 +44,7 @@ describe("Serializable", () => { const req = new GetPersonById({ id: 123 }) assert.deepStrictEqual( Effect.runSync( - Serializable.serializeFailure(req, "fail") + S.serializeFailure(req, "fail") ), "fail" ) @@ -55,7 +54,7 @@ describe("Serializable", () => { const req = new GetPersonById({ id: 123 }) assert.deepStrictEqual( Effect.runSync( - Serializable.serializeSuccess(req, new Person({ id: 123, name: "foo" })) + S.serializeSuccess(req, new Person({ id: 123, name: "foo" })) ), { id: 123, name: "foo" } ) @@ -65,13 +64,13 @@ describe("Serializable", () => { const req = new GetPersonById({ id: 123 }) assert.deepStrictEqual( Effect.runSync( - Serializable.serializeExit(req, Exit.succeed(new Person({ id: 123, name: "foo" }))) + S.serializeExit(req, Exit.succeed(new Person({ id: 123, name: "foo" }))) ), { _tag: "Success", value: { id: 123, name: "foo" } } ) assert.deepStrictEqual( Effect.runSync( - Serializable.serializeExit(req, Exit.fail("fail")) + S.serializeExit(req, Exit.fail("fail")) ), { _tag: "Failure", cause: { _tag: "Fail", error: "fail" } } ) @@ -81,7 +80,7 @@ describe("Serializable", () => { const req = new GetPersonById({ id: 123 }) assert.deepStrictEqual( Effect.runSync( - Serializable.deserializeFailure(req, "fail") + S.deserializeFailure(req, "fail") ), "fail" ) @@ -91,7 +90,7 @@ describe("Serializable", () => { const req = new GetPersonById({ id: 123 }) assert.deepStrictEqual( Effect.runSync( - Serializable.deserializeSuccess(req, { id: 123, name: "foo" }) + S.deserializeSuccess(req, { id: 123, name: "foo" }) ), new Person({ id: 123, name: "foo" }) ) @@ -101,13 +100,13 @@ describe("Serializable", () => { const req = new GetPersonById({ id: 123 }) assert.deepStrictEqual( Effect.runSync( - Serializable.deserializeExit(req, { _tag: "Success", value: { id: 123, name: "foo" } }) + S.deserializeExit(req, { _tag: "Success", value: { id: 123, name: "foo" } }) ), Exit.succeed(new Person({ id: 123, name: "foo" })) ) assert.deepStrictEqual( Effect.runSync( - Serializable.deserializeExit(req, { + S.deserializeExit(req, { _tag: "Failure", cause: { _tag: "Fail", error: "fail" } }) diff --git a/packages/experimental/src/Machine.ts b/packages/experimental/src/Machine.ts index 1fe3fda6aa..ae155e17d8 100644 --- a/packages/experimental/src/Machine.ts +++ b/packages/experimental/src/Machine.ts @@ -24,7 +24,6 @@ import type { Request } from "effect/Request" import type * as Schedule from "effect/Schedule" import * as Schema from "effect/Schema" import type * as Scope from "effect/Scope" -import * as Serializable from "effect/Serializable" import * as Stream from "effect/Stream" import * as Subscribable from "effect/Subscribable" import * as Tracer from "effect/Tracer" @@ -586,7 +585,7 @@ export const boot = < Effect.flatMap((req) => Effect.flatMap( Effect.exit(send(req)), - (exit) => Serializable.serializeExit(req, exit) + (exit) => Schema.serializeExit(req, exit) ) ), Effect.provide(context) diff --git a/packages/experimental/src/Machine/Procedure.ts b/packages/experimental/src/Machine/Procedure.ts index c2b38e4290..3dec66d6b5 100644 --- a/packages/experimental/src/Machine/Procedure.ts +++ b/packages/experimental/src/Machine/Procedure.ts @@ -7,7 +7,6 @@ import { type Pipeable, pipeArguments } from "effect/Pipeable" import * as Predicate from "effect/Predicate" import type { Request } from "effect/Request" import type * as Schema from "effect/Schema" -import type * as Serializable from "effect/Serializable" /** * @since 1.0.0 @@ -243,7 +242,7 @@ export const makeSerializable = < >( schema: Schema.Schema & { readonly _tag: Req["_tag"] }, handler: Handler -): SerializableProcedure> => ({ +): SerializableProcedure> => ({ [TypeId]: TypeId, [SerializableTypeId]: SerializableTypeId, schema: schema as any, diff --git a/packages/experimental/src/Machine/SerializableProcedureList.ts b/packages/experimental/src/Machine/SerializableProcedureList.ts index 99a9166d1e..644d4502d2 100644 --- a/packages/experimental/src/Machine/SerializableProcedureList.ts +++ b/packages/experimental/src/Machine/SerializableProcedureList.ts @@ -4,7 +4,6 @@ import type * as Effect from "effect/Effect" import { dual } from "effect/Function" import type * as Schema from "effect/Schema" -import type * as Serializable from "effect/Serializable" import type * as Types from "effect/Types" import * as Procedure from "./Procedure.js" import * as ProcedureList from "./ProcedureList.js" @@ -59,7 +58,7 @@ export const add: { State, Req | Public, Private, - R | R2 | Serializable.SerializableWithResult.Context + R | R2 | Schema.SerializableWithResult.Context > < State, @@ -74,7 +73,7 @@ export const add: { self: SerializableProcedureList, schema: Schema.Schema & { readonly _tag: Req["_tag"] }, handler: Procedure.Handler, Types.NoInfer | Types.NoInfer, R2> - ): SerializableProcedureList> + ): SerializableProcedureList> } = dual( 3, < @@ -94,7 +93,7 @@ export const add: { State, Req | Public, Private, - R | R2 | Serializable.SerializableWithResult.Context + R | R2 | Schema.SerializableWithResult.Context > => ProcedureList.addProcedure(self, Procedure.makeSerializable()(schema, handler)) as any ) @@ -120,7 +119,7 @@ export const addPrivate: { State, Public, Private | Req, - R | R2 | Serializable.SerializableWithResult.Context + R | R2 | Schema.SerializableWithResult.Context > < State, @@ -135,7 +134,7 @@ export const addPrivate: { self: SerializableProcedureList, schema: Schema.Schema & { readonly _tag: Req["_tag"] }, handler: Procedure.Handler, Types.NoInfer | Types.NoInfer, R2> - ): SerializableProcedureList> + ): SerializableProcedureList> } = dual( 3, < @@ -155,7 +154,7 @@ export const addPrivate: { State, Public, Private | Req, - R | R2 | Serializable.SerializableWithResult.Context + R | R2 | Schema.SerializableWithResult.Context > => ProcedureList.addProcedurePrivate(self, Procedure.makeSerializable()(schema, handler)) as any ) diff --git a/packages/experimental/src/PersistedCache.ts b/packages/experimental/src/PersistedCache.ts index 8a599181ca..e4fe7777d0 100644 --- a/packages/experimental/src/PersistedCache.ts +++ b/packages/experimental/src/PersistedCache.ts @@ -9,8 +9,8 @@ import * as Equal from "effect/Equal" import { identity, pipe } from "effect/Function" import * as Hash from "effect/Hash" import * as Option from "effect/Option" +import type * as Schema from "effect/Schema" import type * as Scope from "effect/Scope" -import type * as Serializable from "effect/Serializable" import * as Tracer from "effect/Tracer" import * as Persistence from "./Persistence.js" @@ -34,8 +34,8 @@ export interface PersistedCache readonly get: ( key: K ) => Effect.Effect< - Serializable.WithResult.Success, - Serializable.WithResult.Failure | Persistence.PersistenceError + Schema.WithResult.Success, + Schema.WithResult.Failure | Persistence.PersistenceError > readonly invalidate: (key: K) => Effect.Effect } @@ -46,14 +46,14 @@ export interface PersistedCache */ export const make = (options: { readonly storeId: string - readonly lookup: (key: K) => Effect.Effect, Serializable.WithResult.Failure, R> + readonly lookup: (key: K) => Effect.Effect, Schema.WithResult.Failure, R> readonly timeToLive: (...args: Persistence.ResultPersistence.TimeToLiveArgs) => Duration.DurationInput readonly inMemoryCapacity?: number | undefined readonly inMemoryTTL?: Duration.DurationInput | undefined }): Effect.Effect< PersistedCache, never, - Serializable.SerializableWithResult.Context | R | Persistence.ResultPersistence | Scope.Scope + Schema.SerializableWithResult.Context | R | Persistence.ResultPersistence | Scope.Scope > => Persistence.ResultPersistence.pipe( Effect.flatMap((_) => @@ -67,9 +67,9 @@ export const make = (options: Cache.make({ lookup: (request: CacheRequest) => { const effect: Effect.Effect< - Serializable.WithResult.Success, - Serializable.WithResult.Failure | Persistence.PersistenceError, - Serializable.SerializableWithResult.Context | R + Schema.WithResult.Success, + Schema.WithResult.Failure | Persistence.PersistenceError, + Schema.SerializableWithResult.Context | R > = pipe( store.get(request.key as any), Effect.flatMap(Option.match({ diff --git a/packages/experimental/src/Persistence.ts b/packages/experimental/src/Persistence.ts index 42f7d5fb2b..6af3103f47 100644 --- a/packages/experimental/src/Persistence.ts +++ b/packages/experimental/src/Persistence.ts @@ -13,8 +13,8 @@ import * as Layer from "effect/Layer" import * as Option from "effect/Option" import * as ParseResult from "effect/ParseResult" import * as PrimaryKey from "effect/PrimaryKey" +import * as Schema from "effect/Schema" import type * as Scope from "effect/Scope" -import * as Serializable from "effect/Serializable" /** * @since 1.0.0 @@ -177,7 +177,7 @@ export declare namespace ResultPersistence { * @since 1.0.0 * @category models */ - export interface Key extends Serializable.WithResult, PrimaryKey.PrimaryKey {} + export interface Key extends Schema.WithResult, PrimaryKey.PrimaryKey {} /** * @since 1.0.0 @@ -225,7 +225,7 @@ export const layerResult = Layer.effect( value: unknown ) => Effect.mapError( - Serializable.deserializeExit(key, value), + Schema.deserializeExit(key, value), (_) => PersistenceParseError.make(method, _.issue) ) const encode = ( @@ -234,7 +234,7 @@ export const layerResult = Layer.effect( value: Exit.Exit ) => Effect.mapError( - Serializable.serializeExit(key, value), + Schema.serializeExit(key, value), (_) => PersistenceParseError.make(method, _.issue) ) const makeKey = ( diff --git a/packages/experimental/src/RequestResolver.ts b/packages/experimental/src/RequestResolver.ts index 21a1a30b3d..de84d47f5f 100644 --- a/packages/experimental/src/RequestResolver.ts +++ b/packages/experimental/src/RequestResolver.ts @@ -11,8 +11,8 @@ import { dual, pipe } from "effect/Function" import * as Option from "effect/Option" import * as Request from "effect/Request" import * as RequestResolver from "effect/RequestResolver" +import type * as Schema from "effect/Schema" import type * as Scope from "effect/Scope" -import type * as Serializable from "effect/Serializable" import * as Persistence from "./Persistence.js" interface DataLoaderItem> { @@ -110,9 +110,7 @@ export const dataLoader = dual< * @since 1.0.0 * @category model */ -export interface PersistedRequest - extends Request.Request, Serializable.WithResult -{} +export interface PersistedRequest extends Request.Request, Schema.WithResult {} /** * @since 1.0.0 @@ -137,7 +135,7 @@ export const persisted: { }): ( self: RequestResolver.RequestResolver ) => Effect.Effect< - RequestResolver.RequestResolver>, + RequestResolver.RequestResolver>, never, Persistence.ResultPersistence | Scope.Scope > @@ -148,7 +146,7 @@ export const persisted: { readonly timeToLive: (...args: Persistence.ResultPersistence.TimeToLiveArgs) => Duration.DurationInput } ): Effect.Effect< - RequestResolver.RequestResolver>, + RequestResolver.RequestResolver>, never, Persistence.ResultPersistence | Scope.Scope > @@ -159,7 +157,7 @@ export const persisted: { readonly timeToLive: (...args: Persistence.ResultPersistence.TimeToLiveArgs) => Duration.DurationInput } ): Effect.Effect< - RequestResolver.RequestResolver>, + RequestResolver.RequestResolver>, never, Persistence.ResultPersistence | Scope.Scope > => diff --git a/packages/platform/src/Worker.ts b/packages/platform/src/Worker.ts index 43739cbe10..1e2cc0e76d 100644 --- a/packages/platform/src/Worker.ts +++ b/packages/platform/src/Worker.ts @@ -11,7 +11,6 @@ import type * as ParseResult from "effect/ParseResult" import type * as Pool from "effect/Pool" import type * as Schema from "effect/Schema" import type * as Scope from "effect/Scope" -import type * as Serializable from "effect/Serializable" import type * as Stream from "effect/Stream" import * as internal from "./internal/worker.js" import type { WorkerError, WorkerErrorFrom } from "./WorkerError.js" @@ -260,12 +259,12 @@ export interface SerializedWorker { readonly id: number readonly execute: ( message: Req - ) => Req extends Serializable.WithResult + ) => Req extends Schema.WithResult ? Stream.Stream : never readonly executeEffect: ( message: Req - ) => Req extends Serializable.WithResult + ) => Req extends Schema.WithResult ? Effect.Effect : never } @@ -295,17 +294,17 @@ export interface SerializedWorkerPool { readonly backing: Pool.Pool, WorkerError> readonly broadcast: ( message: Req - ) => Req extends Serializable.WithResult + ) => Req extends Schema.WithResult ? Effect.Effect : never readonly execute: ( message: Req - ) => Req extends Serializable.WithResult + ) => Req extends Schema.WithResult ? Stream.Stream : never readonly executeEffect: ( message: Req - ) => Req extends Serializable.WithResult + ) => Req extends Schema.WithResult ? Effect.Effect : never } diff --git a/packages/platform/src/WorkerRunner.ts b/packages/platform/src/WorkerRunner.ts index 48553a438d..ae52085a58 100644 --- a/packages/platform/src/WorkerRunner.ts +++ b/packages/platform/src/WorkerRunner.ts @@ -6,7 +6,6 @@ import type * as Effect from "effect/Effect" import type * as Layer from "effect/Layer" import type * as Schema from "effect/Schema" import type * as Scope from "effect/Scope" -import type * as Serializable from "effect/Serializable" import type * as Stream from "effect/Stream" import * as internal from "./internal/workerRunner.js" import type { WorkerError } from "./WorkerError.js" @@ -128,7 +127,7 @@ export declare namespace SerializedRunner { readonly [K in A["_tag"]]: Extract< A, { readonly _tag: K } - > extends Serializable.SerializableWithResult< + > extends Schema.SerializableWithResult< infer S, infer _SI, infer _SR, diff --git a/packages/platform/src/internal/worker.ts b/packages/platform/src/internal/worker.ts index e90c819ebd..0718404793 100644 --- a/packages/platform/src/internal/worker.ts +++ b/packages/platform/src/internal/worker.ts @@ -13,7 +13,6 @@ import * as Pool from "effect/Pool" import * as Schedule from "effect/Schedule" import * as Schema from "effect/Schema" import * as Scope from "effect/Scope" -import * as Serializable from "effect/Serializable" import * as Stream from "effect/Stream" import * as Tracer from "effect/Tracer" import * as Transferable from "../Transferable.js" @@ -298,14 +297,14 @@ export const makeSerialized = < ...options as any, encode(message) { return Effect.mapError( - Serializable.serialize(message as any), + Schema.serialize(message as any), (cause) => new WorkerError({ reason: "encode", cause }) ) } }) const execute = (message: Req) => { - const parseSuccess = Schema.decode(Serializable.successSchema(message as any)) - const parseFailure = Schema.decode(Serializable.failureSchema(message as any)) + const parseSuccess = Schema.decode(Schema.successSchema(message as any)) + const parseFailure = Schema.decode(Schema.failureSchema(message as any)) return pipe( backing.execute(message), Stream.catchAll((error) => Effect.flatMap(parseFailure(error), Effect.fail)), @@ -313,8 +312,8 @@ export const makeSerialized = < ) } const executeEffect = (message: Req) => { - const parseSuccess = Schema.decode(Serializable.successSchema(message as any)) - const parseFailure = Schema.decode(Serializable.failureSchema(message as any)) + const parseSuccess = Schema.decode(Schema.successSchema(message as any)) + const parseFailure = Schema.decode(Schema.failureSchema(message as any)) return Effect.matchEffect(backing.executeEffect(message), { onFailure: (error) => Effect.flatMap(parseFailure(error), Effect.fail), onSuccess: parseSuccess diff --git a/packages/platform/src/internal/workerRunner.ts b/packages/platform/src/internal/workerRunner.ts index 0861508ab9..9a5f4158a0 100644 --- a/packages/platform/src/internal/workerRunner.ts +++ b/packages/platform/src/internal/workerRunner.ts @@ -9,7 +9,6 @@ import * as Layer from "effect/Layer" import * as Schedule from "effect/Schedule" import * as Schema from "effect/Schema" import type * as Scope from "effect/Scope" -import * as Serializable from "effect/Serializable" import * as Stream from "effect/Stream" import * as Transferable from "../Transferable.js" import type * as Worker from "../Worker.js" @@ -189,13 +188,13 @@ export const makeSerialized = < }, encodeError(request, message) { return Effect.mapError( - Serializable.serializeFailure(request as any, message), + Schema.serializeFailure(request as any, message), (cause) => new WorkerError({ reason: "encode", cause }) ) }, encodeOutput(request, message) { return Effect.catchAllCause( - Serializable.serializeSuccess(request as any, message), + Schema.serializeSuccess(request as any, message), (cause) => new WorkerError({ reason: "encode", cause }) ) } diff --git a/packages/rpc-http/src/HttpRpcResolver.ts b/packages/rpc-http/src/HttpRpcResolver.ts index 8d2c710ff5..a39af97235 100644 --- a/packages/rpc-http/src/HttpRpcResolver.ts +++ b/packages/rpc-http/src/HttpRpcResolver.ts @@ -11,7 +11,7 @@ import * as Chunk from "effect/Chunk" import * as Effect from "effect/Effect" import type * as RequestResolver from "effect/RequestResolver" import * as Schedule from "effect/Schedule" -import type * as Serializable from "effect/Serializable" +import type * as Schema from "effect/Schema" import * as Stream from "effect/Stream" /** @@ -22,7 +22,7 @@ export const make = >( client: Client.HttpClient ): RequestResolver.RequestResolver< Rpc.Request>, - Serializable.SerializableWithResult.Context> + Schema.SerializableWithResult.Context> > => Resolver.make((requests) => client.post("", { @@ -46,7 +46,7 @@ export const make = >( */ export const makeClient = >( baseUrl: string -): Serializable.SerializableWithResult.Context> extends never ? Effect.Effect< +): Schema.SerializableWithResult.Context> extends never ? Effect.Effect< Resolver.Client< RequestResolver.RequestResolver< Rpc.Request> diff --git a/packages/rpc-http/src/HttpRpcResolverNoStream.ts b/packages/rpc-http/src/HttpRpcResolverNoStream.ts index f77fe5adad..321429eb31 100644 --- a/packages/rpc-http/src/HttpRpcResolverNoStream.ts +++ b/packages/rpc-http/src/HttpRpcResolverNoStream.ts @@ -11,7 +11,7 @@ import type * as Router from "@effect/rpc/RpcRouter" import * as Effect from "effect/Effect" import type * as RequestResolver from "effect/RequestResolver" import * as Schedule from "effect/Schedule" -import type * as Serializable from "effect/Serializable" +import type * as Schema from "effect/Schema" /** * @category constructors @@ -21,7 +21,7 @@ export const make = >( client: Client.HttpClient ): RequestResolver.RequestResolver< Rpc.Request>, - Serializable.SerializableWithResult.Context> + Schema.SerializableWithResult.Context> > => ResolverNoStream.make((requests) => client.post("", { @@ -38,7 +38,7 @@ export const make = >( */ export const makeClient = >( baseUrl: string -): Serializable.SerializableWithResult.Context> extends never ? Effect.Effect< +): Schema.SerializableWithResult.Context> extends never ? Effect.Effect< Resolver.Client< RequestResolver.RequestResolver< Rpc.Request> diff --git a/packages/rpc/src/Rpc.ts b/packages/rpc/src/Rpc.ts index 7d462a6843..8e096e793b 100644 --- a/packages/rpc/src/Rpc.ts +++ b/packages/rpc/src/Rpc.ts @@ -16,7 +16,6 @@ import type * as EffectRequest from "effect/Request" import type * as RequestResolver from "effect/RequestResolver" import * as Schema from "effect/Schema" import type { Scope } from "effect/Scope" -import type * as Serializable from "effect/Serializable" import * as Stream from "effect/Stream" import type * as Types from "effect/Types" import * as Internal from "./internal/rpc.js" @@ -69,8 +68,8 @@ export interface RpcStream extends Rpc. readonly handler: ( request: Req ) => Stream.Stream< - Req extends Serializable.WithResult ? A : never, - Req extends Serializable.WithResult ? E : never, + Req extends Schema.WithResult ? A : never, + Req extends Schema.WithResult ? E : never, R > } @@ -95,7 +94,7 @@ export declare namespace Rpc { * @category models */ export type Context> = A extends Rpc - ? R | Serializable.SerializableWithResult.Context + ? R | Schema.SerializableWithResult.Context : never /** @@ -117,7 +116,7 @@ export declare namespace Rpc { * @category models */ export type ResultUndecoded = A extends - Serializable.WithResult + Schema.WithResult ? StreamRequestTypeId extends keyof A ? Stream.Stream : Effect.Effect : never @@ -157,9 +156,7 @@ export type StreamRequestTypeId = typeof StreamRequestTypeId * @category schemas */ export interface StreamRequest - extends - EffectRequest.Request>, - Serializable.SerializableWithResult + extends EffectRequest.Request>, Schema.SerializableWithResult { readonly [StreamRequestTypeId]: StreamRequestTypeId readonly _tag: Tag @@ -234,8 +231,8 @@ export const stream = ( handler: ( request: Req ) => Stream.Stream< - Req extends Serializable.WithResult ? A : never, - Req extends Serializable.WithResult ? E : never, + Req extends Schema.WithResult ? A : never, + Req extends Schema.WithResult ? E : never, R > ): Rpc => ({ @@ -258,12 +255,12 @@ export interface Request extends EffectRequest.Request.Error >, PrimaryKey.PrimaryKey, - Serializable.WithResult< - Serializable.WithResult.Context, - Schema.Schema.Encoded, - Schema.Schema.Type, - Schema.Schema.Encoded, - Schema.Schema.Type + Schema.WithResult< + Schema.WithResult.Context, + Schema.Schema.Encoded, + Schema.Schema.Type, + Schema.Schema.Encoded, + Schema.Schema.Type > { readonly request: A diff --git a/packages/rpc/src/RpcResolver.ts b/packages/rpc/src/RpcResolver.ts index e362efec67..fabb0cf829 100644 --- a/packages/rpc/src/RpcResolver.ts +++ b/packages/rpc/src/RpcResolver.ts @@ -11,7 +11,6 @@ import type { ParseError } from "effect/ParseResult" import * as Request from "effect/Request" import * as RequestResolver from "effect/RequestResolver" import * as Schema from "effect/Schema" -import * as Serializable from "effect/Serializable" import * as Stream from "effect/Stream" import { StreamRequestTypeId, withRequestTag } from "./internal/rpc.js" import * as Rpc from "./Rpc.js" @@ -26,10 +25,10 @@ export const make = ( ) => >(): RequestResolver.RequestResolver< Rpc.Request>, - Serializable.SerializableWithResult.Context> | HR + Schema.SerializableWithResult.Context> | HR > => { - const getDecode = withRequestTag((req) => Schema.decodeUnknown(Serializable.exitSchema(req))) - const getDecodeChunk = withRequestTag((req) => Schema.decodeUnknown(Schema.Chunk(Serializable.exitSchema(req)))) + const getDecode = withRequestTag((req) => Schema.decodeUnknown(Schema.exitSchema(req))) + const getDecodeChunk = withRequestTag((req) => Schema.decodeUnknown(Schema.Chunk(Schema.exitSchema(req)))) return RequestResolver.makeBatched( (requests: Arr.NonEmptyArray>) => { @@ -41,7 +40,7 @@ export const make = ( const processEffects = pipe( Effect.forEach(effectRequests, (_) => Effect.map( - Serializable.serialize(_.request), + Schema.serialize(_.request), (request) => ({ ..._, request }) )), Effect.flatMap((payload) => @@ -73,7 +72,7 @@ export const make = ( Effect.forEach(streamRequests, (request) => { const decode = getDecodeChunk(request.request) const stream = pipe( - Serializable.serialize(request.request), + Schema.serialize(request.request), Effect.map((_) => ({ ...request, request: _ })), Effect.map((payload) => pipe( diff --git a/packages/rpc/src/RpcResolverNoStream.ts b/packages/rpc/src/RpcResolverNoStream.ts index 0225c367b4..908f480c92 100644 --- a/packages/rpc/src/RpcResolverNoStream.ts +++ b/packages/rpc/src/RpcResolverNoStream.ts @@ -10,7 +10,6 @@ import { pipe } from "effect/Function" import * as Request from "effect/Request" import * as RequestResolver from "effect/RequestResolver" import * as Schema from "effect/Schema" -import * as Serializable from "effect/Serializable" import * as Stream from "effect/Stream" import { StreamRequestTypeId, withRequestTag } from "./internal/rpc.js" import type * as Rpc from "./Rpc.js" @@ -25,16 +24,16 @@ export const make = ( ) => >(): RequestResolver.RequestResolver< Rpc.Request>, - Serializable.SerializableWithResult.Context> | HR + Schema.SerializableWithResult.Context> | HR > => { - const getDecode = withRequestTag((req) => Schema.decodeUnknown(Serializable.exitSchema(req))) - const getDecodeChunk = withRequestTag((req) => Schema.decodeUnknown(Schema.Chunk(Serializable.exitSchema(req)))) + const getDecode = withRequestTag((req) => Schema.decodeUnknown(Schema.exitSchema(req))) + const getDecodeChunk = withRequestTag((req) => Schema.decodeUnknown(Schema.Chunk(Schema.exitSchema(req)))) return RequestResolver.makeBatched((requests: NonEmptyArray>) => pipe( Effect.forEach(requests, (_) => Effect.map( - Serializable.serialize(_.request), + Schema.serialize(_.request), (request) => ({ ..._, request }) )), Effect.flatMap(handler), diff --git a/packages/rpc/src/RpcRouter.ts b/packages/rpc/src/RpcRouter.ts index d42c73c94e..727d09d445 100644 --- a/packages/rpc/src/RpcRouter.ts +++ b/packages/rpc/src/RpcRouter.ts @@ -13,7 +13,6 @@ import type { ParseError } from "effect/ParseResult" import { type Pipeable, pipeArguments } from "effect/Pipeable" import * as Predicate from "effect/Predicate" import * as Schema from "effect/Schema" -import * as Serializable from "effect/Serializable" import * as Stream from "effect/Stream" import { StreamRequestTypeId, withRequestTag } from "./internal/rpc.js" import * as Rpc from "./Rpc.js" @@ -55,7 +54,7 @@ export declare namespace RpcRouter { * @category models */ export type Context> = A extends RpcRouter - ? R | Serializable.SerializableWithResult.Context + ? R | Schema.SerializableWithResult.Context : never /** @@ -63,7 +62,7 @@ export declare namespace RpcRouter { * @category models */ export type ContextRaw> = A extends RpcRouter - ? R | Serializable.Serializable.Context + ? R | Schema.Serializable.Context : never /** @@ -206,8 +205,8 @@ export const toHandler: { ) const schemaArray = Schema.Array(Rpc.RequestSchema(schema)) const decode = Schema.decodeUnknown(schemaArray) - const getEncode = withRequestTag((req) => Schema.encode(Serializable.exitSchema(req))) - const getEncodeChunk = withRequestTag((req) => Schema.encode(Schema.Chunk(Serializable.exitSchema(req)))) + const getEncode = withRequestTag((req) => Schema.encode(Schema.exitSchema(req))) + const getEncodeChunk = withRequestTag((req) => Schema.encode(Schema.Chunk(Schema.exitSchema(req)))) return (u: unknown): Stream.Stream> => pipe( @@ -316,8 +315,8 @@ export const toHandlerNoStream: { ) const schemaArray = Schema.Array(Rpc.RequestSchema(schema)) const decode = Schema.decodeUnknown(schemaArray) - const getEncode = withRequestTag((req) => Schema.encode(Serializable.exitSchema(req))) - const getEncodeChunk = withRequestTag((req) => Schema.encode(Schema.Chunk(Serializable.exitSchema(req)))) + const getEncode = withRequestTag((req) => Schema.encode(Schema.exitSchema(req))) + const getEncodeChunk = withRequestTag((req) => Schema.encode(Schema.Chunk(Schema.exitSchema(req)))) return (u: unknown): Effect.Effect, ParseError, RpcRouter.Context> => Effect.flatMap( @@ -408,8 +407,8 @@ export const toHandlerRaw = >(router: R) => { */ export const toHandlerUndecoded = >(router: R) => { const handler = toHandlerRaw(router) - const getEncode = withRequestTag((req) => Schema.encode(Serializable.successSchema(req))) - const getEncodeChunk = withRequestTag((req) => Schema.encode(Schema.ChunkFromSelf(Serializable.successSchema(req)))) + const getEncode = withRequestTag((req) => Schema.encode(Schema.successSchema(req))) + const getEncodeChunk = withRequestTag((req) => Schema.encode(Schema.ChunkFromSelf(Schema.successSchema(req)))) return >(request: Req): Rpc.Rpc.ResultUndecoded> => { const result = handler(request) if (Effect.isEffect(result)) { diff --git a/packages/rpc/src/internal/rpc.ts b/packages/rpc/src/internal/rpc.ts index 57947fa052..79e5467f6b 100644 --- a/packages/rpc/src/internal/rpc.ts +++ b/packages/rpc/src/internal/rpc.ts @@ -4,13 +4,12 @@ import * as Hash from "effect/Hash" import * as PrimaryKey from "effect/PrimaryKey" import * as Request from "effect/Request" import * as Schema from "effect/Schema" -import * as Serializable from "effect/Serializable" import type * as Rpc from "../Rpc.js" /** @internal */ export const withRequestTag = ( f: ( - request: Serializable.SerializableWithResult + request: Schema.SerializableWithResult ) => A ) => { const cache = new Map() @@ -46,11 +45,11 @@ export const makeRequest = ( ...options, [Request.RequestTypeId]: undefined as any, [PrimaryKey.symbol]: () => `${options.request._tag}:${hash}`, - [Serializable.symbolResult]: { + [Schema.symbolWithResult]: { success: isStream ? Schema.Never - : Serializable.successSchema(options.request as any), - failure: isStream ? Schema.Never : Serializable.failureSchema(options.request as any) + : Schema.successSchema(options.request as any), + failure: isStream ? Schema.Never : Schema.failureSchema(options.request as any) }, [Equal.symbol](that: Rpc.Request) { return Equal.equals(options.request, that.request) diff --git a/packages/schema/src/Serializable.ts b/packages/schema/src/Serializable.ts deleted file mode 100644 index 560b9b54fd..0000000000 --- a/packages/schema/src/Serializable.ts +++ /dev/null @@ -1,9 +0,0 @@ -/** - * @since 0.67.0 - */ - -/** - * @category re-exports - * @since 0.76.0 - */ -export * from "effect/Serializable" diff --git a/packages/schema/src/index.ts b/packages/schema/src/index.ts index 66da0d1729..1c2004c88d 100644 --- a/packages/schema/src/index.ts +++ b/packages/schema/src/index.ts @@ -37,8 +37,3 @@ export * as Pretty from "./Pretty.js" * @since 0.67.0 */ export * as Schema from "./Schema.js" - -/** - * @since 0.67.0 - */ -export * as Serializable from "./Serializable.js" From faa2cd08ea497f2274fccda8f484d86de1e1a61a Mon Sep 17 00:00:00 2001 From: Giulio Canti Date: Tue, 15 Oct 2024 11:34:50 +0200 Subject: [PATCH 06/12] move compiler annotation ids to SchemaAST module --- packages/effect/src/Arbitrary.ts | 18 ++++++------------ packages/effect/src/Pretty.ts | 8 +------- packages/effect/src/Schema.ts | 15 +++++++-------- packages/effect/src/SchemaAST.ts | 18 ++++++++++++++++++ packages/effect/src/SchemaEquivalence.ts | 8 +------- 5 files changed, 33 insertions(+), 34 deletions(-) diff --git a/packages/effect/src/Arbitrary.ts b/packages/effect/src/Arbitrary.ts index 1e32ad4887..6d0ccbc7d3 100644 --- a/packages/effect/src/Arbitrary.ts +++ b/packages/effect/src/Arbitrary.ts @@ -40,12 +40,6 @@ export type ArbitraryAnnotation = r ] ) => LazyArbitrary -/** - * @category annotations - * @since 3.10.0 - */ -export const ArbitraryAnnotationId: unique symbol = Symbol.for("effect/annotation/Arbitrary") - /** * Returns a LazyArbitrary for the `A` type of the provided schema. * @@ -63,7 +57,7 @@ export const makeLazy = (schema: Schema.Schema): LazyArbitrary */ export const make = (schema: Schema.Schema): FastCheck.Arbitrary => makeLazy(schema)(FastCheck) -const getAnnotation = AST.getAnnotation>(ArbitraryAnnotationId) +const getAnnotation = AST.getAnnotation>(AST.ArbitraryAnnotationId) const getRefinementFromArbitrary = ( ast: AST.Refinement, @@ -119,15 +113,15 @@ const go = ( ctx: Context, path: ReadonlyArray ): LazyArbitrary => { - const annotation = getAnnotation(ast) - if (Option.isSome(annotation)) { + const hook = getAnnotation(ast) + if (Option.isSome(hook)) { switch (ast._tag) { case "Declaration": - return annotation.value(...ast.typeParameters.map((p) => go(p, ctx, path)), ctx) + return hook.value(...ast.typeParameters.map((p) => go(p, ctx, path)), ctx) case "Refinement": - return annotation.value(getRefinementFromArbitrary(ast, ctx, path), ctx) + return hook.value(getRefinementFromArbitrary(ast, ctx, path), ctx) default: - return annotation.value(ctx) + return hook.value(ctx) } } switch (ast._tag) { diff --git a/packages/effect/src/Pretty.ts b/packages/effect/src/Pretty.ts index ca9ba85a84..c769b8437e 100644 --- a/packages/effect/src/Pretty.ts +++ b/packages/effect/src/Pretty.ts @@ -25,19 +25,13 @@ export type PrettyAnnotation = read ...pretties: { readonly [K in keyof TypeParameters]: Pretty } ) => Pretty -/** - * @category annotations - * @since 3.10.0 - */ -export const PrettyAnnotationId: unique symbol = Symbol.for("effect/annotation/Pretty") - /** * @category prettify * @since 3.10.0 */ export const make = (schema: Schema.Schema): (a: A) => string => compile(schema.ast, []) -const getAnnotation = AST.getAnnotation>(PrettyAnnotationId) +const getAnnotation = AST.getAnnotation>(AST.PrettyAnnotationId) const getMatcher = (defaultPretty: Pretty) => (ast: AST.AST): Pretty => Option.match(getAnnotation(ast), { diff --git a/packages/effect/src/Schema.ts b/packages/effect/src/Schema.ts index a4599887cf..1301778a74 100644 --- a/packages/effect/src/Schema.ts +++ b/packages/effect/src/Schema.ts @@ -2,8 +2,7 @@ * @since 3.10.0 */ -import * as arbitrary_ from "./Arbitrary.js" -import type { ArbitraryGenerationContext, LazyArbitrary } from "./Arbitrary.js" +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" @@ -40,13 +39,13 @@ import * as ParseResult from "./ParseResult.js" import type { Pipeable } from "./Pipeable.js" import { pipeArguments } from "./Pipeable.js" import * as Predicate from "./Predicate.js" -import * as pretty_ from "./Pretty.js" +import type * as pretty_ from "./Pretty.js" import * as record_ from "./Record.js" import * as redacted_ from "./Redacted.js" import * as Request from "./Request.js" import type { ParseOptions } from "./SchemaAST.js" import * as AST from "./SchemaAST.js" -import * as equivalence_ from "./SchemaEquivalence.js" +import type * as equivalence_ from "./SchemaEquivalence.js" import * as sortedSet_ from "./SortedSet.js" import * as string_ from "./String.js" import * as struct_ from "./Struct.js" @@ -145,9 +144,9 @@ const builtInAnnotations = { default: AST.DefaultAnnotationId, documentation: AST.DocumentationAnnotationId, jsonSchema: AST.JSONSchemaAnnotationId, - arbitrary: arbitrary_.ArbitraryAnnotationId, - pretty: pretty_.PrettyAnnotationId, - equivalence: equivalence_.EquivalenceAnnotationId, + arbitrary: AST.ArbitraryAnnotationId, + pretty: AST.PrettyAnnotationId, + equivalence: AST.EquivalenceAnnotationId, concurrency: AST.ConcurrencyAnnotationId, batching: AST.BatchingAnnotationId, parseIssueTitle: AST.ParseIssueTitleAnnotationId, @@ -3868,7 +3867,7 @@ export declare namespace Annotations { readonly message?: AST.MessageAnnotation readonly schemaId?: AST.SchemaIdAnnotation readonly jsonSchema?: AST.JSONSchemaAnnotation - readonly arbitrary?: arbitrary_.ArbitraryAnnotation + readonly arbitrary?: ArbitraryAnnotation readonly pretty?: pretty_.PrettyAnnotation readonly equivalence?: equivalence_.EquivalenceAnnotation readonly concurrency?: AST.ConcurrencyAnnotation diff --git a/packages/effect/src/SchemaAST.ts b/packages/effect/src/SchemaAST.ts index 31b6d17c97..d4d0b041e4 100644 --- a/packages/effect/src/SchemaAST.ts +++ b/packages/effect/src/SchemaAST.ts @@ -173,6 +173,24 @@ export type JSONSchemaAnnotation = object */ export const JSONSchemaAnnotationId: unique symbol = Symbol.for("effect/annotation/JSONSchema") +/** + * @category annotations + * @since 3.10.0 + */ +export const ArbitraryAnnotationId: unique symbol = Symbol.for("effect/annotation/Arbitrary") + +/** + * @category annotations + * @since 3.10.0 + */ +export const PrettyAnnotationId: unique symbol = Symbol.for("effect/annotation/Pretty") + +/** + * @category annotations + * @since 3.10.0 + */ +export const EquivalenceAnnotationId: unique symbol = Symbol.for("effect/annotation/Equivalence") + /** * @category annotations * @since 3.10.0 diff --git a/packages/effect/src/SchemaEquivalence.ts b/packages/effect/src/SchemaEquivalence.ts index 4e8ed74472..2dbbcdee89 100644 --- a/packages/effect/src/SchemaEquivalence.ts +++ b/packages/effect/src/SchemaEquivalence.ts @@ -21,19 +21,13 @@ export type EquivalenceAnnotation = ...equivalences: { readonly [K in keyof TypeParameters]: Equivalence.Equivalence } ) => Equivalence.Equivalence -/** - * @category annotations - * @since 3.10.0 - */ -export const EquivalenceAnnotationId: unique symbol = Symbol.for("effect/annotation/Equivalence") - /** * @category Equivalence * @since 3.10.0 */ export const make = (schema: Schema.Schema): Equivalence.Equivalence => go(schema.ast, []) -const getAnnotation = AST.getAnnotation>(EquivalenceAnnotationId) +const getAnnotation = AST.getAnnotation>(AST.EquivalenceAnnotationId) const go = (ast: AST.AST, path: ReadonlyArray): Equivalence.Equivalence => { const hook = getAnnotation(ast) From e7f2f128dcc7dff1c5fdb8096d9e607d28296140 Mon Sep 17 00:00:00 2001 From: Giulio Canti Date: Tue, 15 Oct 2024 12:15:04 +0200 Subject: [PATCH 07/12] merge SchemaEquivalence into Schema --- .changeset/cyan-sloths-lick.md | 2 + .changeset/six-crabs-itch.md | 22 +- packages/effect/src/Arbitrary.ts | 4 +- packages/effect/src/Pretty.ts | 18 +- packages/effect/src/Schema.ts | 191 +++++++++++++++- packages/effect/src/SchemaAST.ts | 9 + packages/effect/src/SchemaEquivalence.ts | 204 ------------------ packages/effect/src/index.ts | 5 - .../BigDecimal/BigDecimalFromSelf.test.ts | 3 +- .../Chunk/NonEmptyChunkFromSelf.test.ts | 3 +- .../Schema/Schema/Class/TaggedClass.test.ts | 5 +- .../SortedSet/SortedSetFromSelf.test.ts | 3 +- .../equivalence.test.ts} | 87 ++++---- packages/schema/src/Equivalence.ts | 9 - packages/schema/src/index.ts | 5 - 15 files changed, 279 insertions(+), 291 deletions(-) delete mode 100644 packages/effect/src/SchemaEquivalence.ts rename packages/effect/test/Schema/{SchemaEquivalence.test.ts => Schema/equivalence.test.ts} (91%) delete mode 100644 packages/schema/src/Equivalence.ts diff --git a/.changeset/cyan-sloths-lick.md b/.changeset/cyan-sloths-lick.md index 22789ec9f9..3a8407dea9 100644 --- a/.changeset/cyan-sloths-lick.md +++ b/.changeset/cyan-sloths-lick.md @@ -7,3 +7,5 @@ Re-export modules from `effect`. `ArrayFormatter` / `TreeFormatter` merged into `ParseResult` module. `Serializable` module merged into `Schema` module. + +`Equivalence` module merged into `Schema` module. diff --git a/.changeset/six-crabs-itch.md b/.changeset/six-crabs-itch.md index 5a49f35cd9..49eb2c240f 100644 --- a/.changeset/six-crabs-itch.md +++ b/.changeset/six-crabs-itch.md @@ -12,7 +12,6 @@ Before import { Arbitrary, AST, - Equivalence, FastCheck, JSONSchema, ParseResult, @@ -27,7 +26,6 @@ After import { Arbitrary, SchemaAST, // changed - SchemaEquivalence, // changed FastCheck, JSONSchema, ParseResult, @@ -55,3 +53,23 @@ import { ArrayFormatter, TreeFormatter } from "effect/ParseResult" ### Serializable Merged into `Schema` module. + +### Equivalence + +Merged into `Schema` module. + +Before + +```ts +import { Equivalence } from "@effect/schema" + +Equivalence.make(myschema) +``` + +After + +```ts +import { Schema } from "@effect/schema" + +Schema.equivalence(myschema) +``` diff --git a/packages/effect/src/Arbitrary.ts b/packages/effect/src/Arbitrary.ts index 6d0ccbc7d3..5f98b93f26 100644 --- a/packages/effect/src/Arbitrary.ts +++ b/packages/effect/src/Arbitrary.ts @@ -57,7 +57,7 @@ export const makeLazy = (schema: Schema.Schema): LazyArbitrary */ export const make = (schema: Schema.Schema): FastCheck.Arbitrary => makeLazy(schema)(FastCheck) -const getAnnotation = AST.getAnnotation>(AST.ArbitraryAnnotationId) +const getArbitraryAnnotation = AST.getAnnotation>(AST.ArbitraryAnnotationId) const getRefinementFromArbitrary = ( ast: AST.Refinement, @@ -113,7 +113,7 @@ const go = ( ctx: Context, path: ReadonlyArray ): LazyArbitrary => { - const hook = getAnnotation(ast) + const hook = getArbitraryAnnotation(ast) if (Option.isSome(hook)) { switch (ast._tag) { case "Declaration": diff --git a/packages/effect/src/Pretty.ts b/packages/effect/src/Pretty.ts index c769b8437e..c301c0c938 100644 --- a/packages/effect/src/Pretty.ts +++ b/packages/effect/src/Pretty.ts @@ -31,10 +31,10 @@ export type PrettyAnnotation = read */ export const make = (schema: Schema.Schema): (a: A) => string => compile(schema.ast, []) -const getAnnotation = AST.getAnnotation>(AST.PrettyAnnotationId) +const getPrettyAnnotation = AST.getAnnotation>(AST.PrettyAnnotationId) const getMatcher = (defaultPretty: Pretty) => (ast: AST.AST): Pretty => - Option.match(getAnnotation(ast), { + Option.match(getPrettyAnnotation(ast), { onNone: () => defaultPretty, onSome: (handler) => handler() }) @@ -50,7 +50,7 @@ const formatUnknown = getMatcher(util_.formatUnknown) */ export const match: AST.Match> = { "Declaration": (ast, go, path) => { - const annotation = getAnnotation(ast) + const annotation = getPrettyAnnotation(ast) if (Option.isSome(annotation)) { return annotation.value(...ast.typeParameters.map((tp) => go(tp, path))) } @@ -78,7 +78,7 @@ export const match: AST.Match> = { "BigIntKeyword": getMatcher((a) => `${String(a)}n`), "Enums": stringify, "TupleType": (ast, go, path) => { - const hook = getAnnotation(ast) + const hook = getPrettyAnnotation(ast) if (Option.isSome(hook)) { return hook.value() } @@ -120,7 +120,7 @@ export const match: AST.Match> = { } }, "TypeLiteral": (ast, go, path) => { - const hook = getAnnotation(ast) + const hook = getPrettyAnnotation(ast) if (Option.isSome(hook)) { return hook.value() } @@ -165,7 +165,7 @@ export const match: AST.Match> = { } }, "Union": (ast, go, path) => { - const hook = getAnnotation(ast) + const hook = getPrettyAnnotation(ast) if (Option.isSome(hook)) { return hook.value() } @@ -179,7 +179,7 @@ export const match: AST.Match> = { } }, "Suspend": (ast, go, path) => { - return Option.match(getAnnotation(ast), { + return Option.match(getPrettyAnnotation(ast), { onNone: () => { const get = util_.memoizeThunk(() => go(ast.f(), path)) return (a) => get()(a) @@ -188,13 +188,13 @@ export const match: AST.Match> = { }) }, "Refinement": (ast, go, path) => { - return Option.match(getAnnotation(ast), { + return Option.match(getPrettyAnnotation(ast), { onNone: () => go(ast.from, path), onSome: (handler) => handler() }) }, "Transformation": (ast, go, path) => { - return Option.match(getAnnotation(ast), { + return Option.match(getPrettyAnnotation(ast), { onNone: () => go(ast.to, path), onSome: (handler) => handler() }) diff --git a/packages/effect/src/Schema.ts b/packages/effect/src/Schema.ts index 1301778a74..512cd08b52 100644 --- a/packages/effect/src/Schema.ts +++ b/packages/effect/src/Schema.ts @@ -45,7 +45,6 @@ import * as redacted_ from "./Redacted.js" import * as Request from "./Request.js" import type { ParseOptions } from "./SchemaAST.js" import * as AST from "./SchemaAST.js" -import type * as equivalence_ from "./SchemaEquivalence.js" import * as sortedSet_ from "./SortedSet.js" import * as string_ from "./String.js" import * as struct_ from "./Struct.js" @@ -3869,7 +3868,7 @@ export declare namespace Annotations { readonly jsonSchema?: AST.JSONSchemaAnnotation readonly arbitrary?: ArbitraryAnnotation readonly pretty?: pretty_.PrettyAnnotation - readonly equivalence?: equivalence_.EquivalenceAnnotation + readonly equivalence?: AST.EquivalenceAnnotation readonly concurrency?: AST.ConcurrencyAnnotation readonly batching?: AST.BatchingAnnotation readonly parseIssueTitle?: AST.ParseIssueTitleAnnotation @@ -9655,3 +9654,191 @@ export const TaggedRequest = } } as any } + +// ------------------------------------------------------------------------------------------------- +// Equivalence compiler +// ------------------------------------------------------------------------------------------------- + +/** + * Given a schema `Schema`, returns an `Equivalence` instance for `A`. + * + * @category Equivalence + * @since 3.10.0 + */ +export const equivalence = (schema: Schema): Equivalence.Equivalence => go(schema.ast, []) + +const getEquivalenceAnnotation = AST.getAnnotation>(AST.EquivalenceAnnotationId) + +const go = (ast: AST.AST, path: ReadonlyArray): Equivalence.Equivalence => { + const hook = getEquivalenceAnnotation(ast) + if (option_.isSome(hook)) { + switch (ast._tag) { + case "Declaration": + return hook.value(...ast.typeParameters.map((tp) => go(tp, path))) + case "Refinement": + return hook.value(go(ast.from, path)) + default: + return hook.value() + } + } + switch (ast._tag) { + case "NeverKeyword": + throw new Error(errors_.getEquivalenceUnsupportedErrorMessage(ast, path)) + case "Transformation": + return go(ast.to, path) + case "Declaration": + case "Literal": + case "StringKeyword": + case "TemplateLiteral": + case "UniqueSymbol": + case "SymbolKeyword": + case "UnknownKeyword": + case "AnyKeyword": + case "NumberKeyword": + case "BooleanKeyword": + case "BigIntKeyword": + case "UndefinedKeyword": + case "VoidKeyword": + case "Enums": + case "ObjectKeyword": + return Equal.equals + case "Refinement": + return go(ast.from, path) + case "Suspend": { + const get = util_.memoizeThunk(() => go(ast.f(), path)) + return (a, b) => get()(a, b) + } + case "TupleType": { + const elements = ast.elements.map((element, i) => go(element.type, path.concat(i))) + const rest = ast.rest.map((annotatedAST) => go(annotatedAST.type, path)) + return Equivalence.make((a, b) => { + const len = a.length + if (len !== b.length) { + return false + } + // --------------------------------------------- + // handle elements + // --------------------------------------------- + let i = 0 + for (; i < Math.min(len, ast.elements.length); i++) { + if (!elements[i](a[i], b[i])) { + return false + } + } + // --------------------------------------------- + // handle rest element + // --------------------------------------------- + if (array_.isNonEmptyReadonlyArray(rest)) { + const [head, ...tail] = rest + for (; i < len - tail.length; i++) { + if (!head(a[i], b[i])) { + return false + } + } + // --------------------------------------------- + // handle post rest elements + // --------------------------------------------- + for (let j = 0; j < tail.length; j++) { + i += j + if (!tail[j](a[i], b[i])) { + return false + } + } + } + return true + }) + } + case "TypeLiteral": { + if (ast.propertySignatures.length === 0 && ast.indexSignatures.length === 0) { + return Equal.equals + } + const propertySignatures = ast.propertySignatures.map((ps) => go(ps.type, path.concat(ps.name))) + const indexSignatures = ast.indexSignatures.map((is) => go(is.type, path)) + return Equivalence.make((a, b) => { + const aStringKeys = Object.keys(a) + const aSymbolKeys = Object.getOwnPropertySymbols(a) + // --------------------------------------------- + // handle property signatures + // --------------------------------------------- + for (let i = 0; i < propertySignatures.length; i++) { + const ps = ast.propertySignatures[i] + const name = ps.name + const aHas = Object.prototype.hasOwnProperty.call(a, name) + const bHas = Object.prototype.hasOwnProperty.call(b, name) + if (ps.isOptional) { + if (aHas !== bHas) { + return false + } + } + if (aHas && bHas && !propertySignatures[i](a[name], b[name])) { + return false + } + } + // --------------------------------------------- + // handle index signatures + // --------------------------------------------- + let bSymbolKeys: Array | undefined + let bStringKeys: Array | undefined + for (let i = 0; i < indexSignatures.length; i++) { + const is = ast.indexSignatures[i] + const base = AST.getParameterBase(is.parameter) + const isSymbol = AST.isSymbolKeyword(base) + if (isSymbol) { + bSymbolKeys = bSymbolKeys || Object.getOwnPropertySymbols(b) + if (aSymbolKeys.length !== bSymbolKeys.length) { + return false + } + } else { + bStringKeys = bStringKeys || Object.keys(b) + if (aStringKeys.length !== bStringKeys.length) { + return false + } + } + const aKeys = isSymbol ? aSymbolKeys : aStringKeys + for (let j = 0; j < aKeys.length; j++) { + const key = aKeys[j] + if ( + !Object.prototype.hasOwnProperty.call(b, key) || !indexSignatures[i](a[key], b[key]) + ) { + return false + } + } + } + return true + }) + } + case "Union": { + const searchTree = ParseResult.getSearchTree(ast.types, true) + const ownKeys = util_.ownKeys(searchTree.keys) + const len = ownKeys.length + return Equivalence.make((a, b) => { + let candidates: Array = [] + if (len > 0 && Predicate.isRecord(a)) { + for (let i = 0; i < len; i++) { + const name = ownKeys[i] + const buckets = searchTree.keys[name].buckets + if (Object.prototype.hasOwnProperty.call(a, name)) { + const literal = String(a[name]) + if (Object.prototype.hasOwnProperty.call(buckets, literal)) { + candidates = candidates.concat(buckets[literal]) + } + } + } + } + if (searchTree.otherwise.length > 0) { + candidates = candidates.concat(searchTree.otherwise) + } + const tuples = candidates.map((ast) => [go(ast, path), ParseResult.is({ ast } as any)] as const) + for (let i = 0; i < tuples.length; i++) { + const [equivalence, is] = tuples[i] + if (is(a) && is(b)) { + if (equivalence(a, b)) { + return true + } + } + } + return false + }) + } + } +} diff --git a/packages/effect/src/SchemaAST.ts b/packages/effect/src/SchemaAST.ts index d4d0b041e4..ddd8679a06 100644 --- a/packages/effect/src/SchemaAST.ts +++ b/packages/effect/src/SchemaAST.ts @@ -4,6 +4,7 @@ import * as Arr from "./Array.js" import type { Effect } from "./Effect.js" +import type { Equivalence } from "./Equivalence.js" import { dual, identity } from "./Function.js" import { globalValue } from "./GlobalValue.js" import * as errors_ from "./internal/schema/errors.js" @@ -185,6 +186,14 @@ export const ArbitraryAnnotationId: unique symbol = Symbol.for("effect/annotatio */ export const PrettyAnnotationId: unique symbol = Symbol.for("effect/annotation/Pretty") +/** + * @category annotations + * @since 3.10.0 + */ +export type EquivalenceAnnotation = readonly []> = ( + ...equivalences: { readonly [K in keyof TypeParameters]: Equivalence } +) => Equivalence + /** * @category annotations * @since 3.10.0 diff --git a/packages/effect/src/SchemaEquivalence.ts b/packages/effect/src/SchemaEquivalence.ts deleted file mode 100644 index 2dbbcdee89..0000000000 --- a/packages/effect/src/SchemaEquivalence.ts +++ /dev/null @@ -1,204 +0,0 @@ -/** - * @since 3.10.0 - */ - -import * as Arr from "./Array.js" -import * as Equal from "./Equal.js" -import * as Equivalence from "./Equivalence.js" -import * as errors_ from "./internal/schema/errors.js" -import * as util_ from "./internal/schema/util.js" -import * as Option from "./Option.js" -import * as ParseResult from "./ParseResult.js" -import * as Predicate from "./Predicate.js" -import type * as Schema from "./Schema.js" -import * as AST from "./SchemaAST.js" - -/** - * @category annotations - * @since 3.10.0 - */ -export type EquivalenceAnnotation = readonly []> = ( - ...equivalences: { readonly [K in keyof TypeParameters]: Equivalence.Equivalence } -) => Equivalence.Equivalence - -/** - * @category Equivalence - * @since 3.10.0 - */ -export const make = (schema: Schema.Schema): Equivalence.Equivalence => go(schema.ast, []) - -const getAnnotation = AST.getAnnotation>(AST.EquivalenceAnnotationId) - -const go = (ast: AST.AST, path: ReadonlyArray): Equivalence.Equivalence => { - const hook = getAnnotation(ast) - if (Option.isSome(hook)) { - switch (ast._tag) { - case "Declaration": - return hook.value(...ast.typeParameters.map((tp) => go(tp, path))) - case "Refinement": - return hook.value(go(ast.from, path)) - default: - return hook.value() - } - } - switch (ast._tag) { - case "NeverKeyword": - throw new Error(errors_.getEquivalenceUnsupportedErrorMessage(ast, path)) - case "Transformation": - return go(ast.to, path) - case "Declaration": - case "Literal": - case "StringKeyword": - case "TemplateLiteral": - case "UniqueSymbol": - case "SymbolKeyword": - case "UnknownKeyword": - case "AnyKeyword": - case "NumberKeyword": - case "BooleanKeyword": - case "BigIntKeyword": - case "UndefinedKeyword": - case "VoidKeyword": - case "Enums": - case "ObjectKeyword": - return Equal.equals - case "Refinement": - return go(ast.from, path) - case "Suspend": { - const get = util_.memoizeThunk(() => go(ast.f(), path)) - return (a, b) => get()(a, b) - } - case "TupleType": { - const elements = ast.elements.map((element, i) => go(element.type, path.concat(i))) - const rest = ast.rest.map((annotatedAST) => go(annotatedAST.type, path)) - return Equivalence.make((a, b) => { - const len = a.length - if (len !== b.length) { - return false - } - // --------------------------------------------- - // handle elements - // --------------------------------------------- - let i = 0 - for (; i < Math.min(len, ast.elements.length); i++) { - if (!elements[i](a[i], b[i])) { - return false - } - } - // --------------------------------------------- - // handle rest element - // --------------------------------------------- - if (Arr.isNonEmptyReadonlyArray(rest)) { - const [head, ...tail] = rest - for (; i < len - tail.length; i++) { - if (!head(a[i], b[i])) { - return false - } - } - // --------------------------------------------- - // handle post rest elements - // --------------------------------------------- - for (let j = 0; j < tail.length; j++) { - i += j - if (!tail[j](a[i], b[i])) { - return false - } - } - } - return true - }) - } - case "TypeLiteral": { - if (ast.propertySignatures.length === 0 && ast.indexSignatures.length === 0) { - return Equal.equals - } - const propertySignatures = ast.propertySignatures.map((ps) => go(ps.type, path.concat(ps.name))) - const indexSignatures = ast.indexSignatures.map((is) => go(is.type, path)) - return Equivalence.make((a, b) => { - const aStringKeys = Object.keys(a) - const aSymbolKeys = Object.getOwnPropertySymbols(a) - // --------------------------------------------- - // handle property signatures - // --------------------------------------------- - for (let i = 0; i < propertySignatures.length; i++) { - const ps = ast.propertySignatures[i] - const name = ps.name - const aHas = Object.prototype.hasOwnProperty.call(a, name) - const bHas = Object.prototype.hasOwnProperty.call(b, name) - if (ps.isOptional) { - if (aHas !== bHas) { - return false - } - } - if (aHas && bHas && !propertySignatures[i](a[name], b[name])) { - return false - } - } - // --------------------------------------------- - // handle index signatures - // --------------------------------------------- - let bSymbolKeys: Array | undefined - let bStringKeys: Array | undefined - for (let i = 0; i < indexSignatures.length; i++) { - const is = ast.indexSignatures[i] - const base = AST.getParameterBase(is.parameter) - const isSymbol = AST.isSymbolKeyword(base) - if (isSymbol) { - bSymbolKeys = bSymbolKeys || Object.getOwnPropertySymbols(b) - if (aSymbolKeys.length !== bSymbolKeys.length) { - return false - } - } else { - bStringKeys = bStringKeys || Object.keys(b) - if (aStringKeys.length !== bStringKeys.length) { - return false - } - } - const aKeys = isSymbol ? aSymbolKeys : aStringKeys - for (let j = 0; j < aKeys.length; j++) { - const key = aKeys[j] - if ( - !Object.prototype.hasOwnProperty.call(b, key) || !indexSignatures[i](a[key], b[key]) - ) { - return false - } - } - } - return true - }) - } - case "Union": { - const searchTree = ParseResult.getSearchTree(ast.types, true) - const ownKeys = util_.ownKeys(searchTree.keys) - const len = ownKeys.length - return Equivalence.make((a, b) => { - let candidates: Array = [] - if (len > 0 && Predicate.isRecord(a)) { - for (let i = 0; i < len; i++) { - const name = ownKeys[i] - const buckets = searchTree.keys[name].buckets - if (Object.prototype.hasOwnProperty.call(a, name)) { - const literal = String(a[name]) - if (Object.prototype.hasOwnProperty.call(buckets, literal)) { - candidates = candidates.concat(buckets[literal]) - } - } - } - } - if (searchTree.otherwise.length > 0) { - candidates = candidates.concat(searchTree.otherwise) - } - const tuples = candidates.map((ast) => [go(ast, path), ParseResult.is({ ast } as any)] as const) - for (let i = 0; i < tuples.length; i++) { - const [equivalence, is] = tuples[i] - if (is(a) && is(b)) { - if (equivalence(a, b)) { - return true - } - } - } - return false - }) - } - } -} diff --git a/packages/effect/src/index.ts b/packages/effect/src/index.ts index e38dc4864f..52f1a01328 100644 --- a/packages/effect/src/index.ts +++ b/packages/effect/src/index.ts @@ -770,11 +770,6 @@ export * as Schema from "./Schema.js" */ export * as SchemaAST from "./SchemaAST.js" -/** - * @since 3.10.0 - */ -export * as SchemaEquivalence from "./SchemaEquivalence.js" - /** * @since 2.0.0 */ diff --git a/packages/effect/test/Schema/Schema/BigDecimal/BigDecimalFromSelf.test.ts b/packages/effect/test/Schema/Schema/BigDecimal/BigDecimalFromSelf.test.ts index 9092297cbd..81967aac85 100644 --- a/packages/effect/test/Schema/Schema/BigDecimal/BigDecimalFromSelf.test.ts +++ b/packages/effect/test/Schema/Schema/BigDecimal/BigDecimalFromSelf.test.ts @@ -1,7 +1,6 @@ import { BigDecimal } from "effect" import * as Pretty from "effect/Pretty" import * as S from "effect/Schema" -import * as Equivalence from "effect/SchemaEquivalence" import * as Util from "effect/test/Schema/TestUtils" import { describe, expect, it } from "vitest" @@ -43,7 +42,7 @@ describe("BigDecimalFromSelf", () => { it("equivalence", () => { const schema = S.BigDecimalFromSelf - const equivalence = Equivalence.make(schema) + const equivalence = S.equivalence(schema) expect(equivalence(BigDecimal.fromNumber(1), BigDecimal.unsafeFromString("1"))).toBe(true) expect(equivalence(BigDecimal.fromNumber(2), BigDecimal.unsafeFromString("1"))).toBe(false) diff --git a/packages/effect/test/Schema/Schema/Chunk/NonEmptyChunkFromSelf.test.ts b/packages/effect/test/Schema/Schema/Chunk/NonEmptyChunkFromSelf.test.ts index 8b3d7b1018..620dff6f5e 100644 --- a/packages/effect/test/Schema/Schema/Chunk/NonEmptyChunkFromSelf.test.ts +++ b/packages/effect/test/Schema/Schema/Chunk/NonEmptyChunkFromSelf.test.ts @@ -3,7 +3,6 @@ import * as C from "effect/Chunk" import * as FastCheck from "effect/FastCheck" import * as Pretty from "effect/Pretty" import * as S from "effect/Schema" -import * as Equivalence from "effect/SchemaEquivalence" import * as Util from "effect/test/Schema/TestUtils" import { describe, expect, it } from "vitest" @@ -64,7 +63,7 @@ describe("NonEmptyChunkFromSelf", () => { it("equivalence", () => { const schema = S.NonEmptyChunkFromSelf(S.String) - const equivalence = Equivalence.make(schema) + const equivalence = S.equivalence(schema) expect(equivalence(C.make("a", "b"), C.make("a", "b"))).toEqual(true) expect(equivalence(C.make("a", "b"), C.make("a", "c"))).toEqual(false) expect(equivalence(C.make("a", "b"), C.make("a"))).toEqual(false) diff --git a/packages/effect/test/Schema/Schema/Class/TaggedClass.test.ts b/packages/effect/test/Schema/Schema/Class/TaggedClass.test.ts index f561b3cba3..31f501f903 100644 --- a/packages/effect/test/Schema/Schema/Class/TaggedClass.test.ts +++ b/packages/effect/test/Schema/Schema/Class/TaggedClass.test.ts @@ -1,6 +1,5 @@ import { pipe, Struct } from "effect" import * as S from "effect/Schema" -import * as Equivalence from "effect/SchemaEquivalence" import * as Util from "effect/test/Schema/TestUtils" import { describe, expect, it } from "vitest" @@ -153,7 +152,7 @@ details: Duplicate key "_tag"`) class A extends S.TaggedClass()("A", { a: S.String }) {} - const eqA = Equivalence.make(A) + const eqA = S.equivalence(A) expect(eqA(new A({ a: "a" }), new A({ a: "a" }))).toBe(true) expect(eqA(new A({ a: "a" }), new A({ a: "b" }))).toBe(false) @@ -161,7 +160,7 @@ details: Duplicate key "_tag"`) b: S.Number, as: S.Array(A) }) {} - const eqB = Equivalence.make(B) + const eqB = S.equivalence(B) expect(eqB(new B({ b: 1, as: [] }), new B({ b: 1, as: [] }))).toBe(true) expect(eqB(new B({ b: 1, as: [] }), new B({ b: 2, as: [] }))).toBe(false) expect(eqB(new B({ b: 1, as: [new A({ a: "a" })] }), new B({ b: 1, as: [new A({ a: "a" })] }))).toBe(true) diff --git a/packages/effect/test/Schema/Schema/SortedSet/SortedSetFromSelf.test.ts b/packages/effect/test/Schema/Schema/SortedSet/SortedSetFromSelf.test.ts index 61ec5447e6..bdb84b5ac1 100644 --- a/packages/effect/test/Schema/Schema/SortedSet/SortedSetFromSelf.test.ts +++ b/packages/effect/test/Schema/Schema/SortedSet/SortedSetFromSelf.test.ts @@ -2,7 +2,6 @@ import * as N from "effect/Number" import * as P from "effect/ParseResult" import * as Pretty from "effect/Pretty" import * as Schema from "effect/Schema" -import * as Equivalence from "effect/SchemaEquivalence" import * as SortedSet from "effect/SortedSet" import * as S from "effect/String" import * as Util from "effect/test/Schema/TestUtils" @@ -79,7 +78,7 @@ describe("SortedSetFromSelf", () => { it("equivalence", () => { const schema = Schema.SortedSetFromSelf(Schema.String, S.Order, S.Order) - const eq = Equivalence.make(schema) + const eq = Schema.equivalence(schema) const a = SortedSet.fromIterable([] as Array, S.Order) const b = SortedSet.fromIterable(["a"] as Array, S.Order) diff --git a/packages/effect/test/Schema/SchemaEquivalence.test.ts b/packages/effect/test/Schema/Schema/equivalence.test.ts similarity index 91% rename from packages/effect/test/Schema/SchemaEquivalence.test.ts rename to packages/effect/test/Schema/Schema/equivalence.test.ts index c372e21e76..9da821514a 100644 --- a/packages/effect/test/Schema/SchemaEquivalence.test.ts +++ b/packages/effect/test/Schema/Schema/equivalence.test.ts @@ -8,7 +8,6 @@ import * as Hash from "effect/Hash" import * as Option from "effect/Option" import { isUnknown } from "effect/Predicate" import * as S from "effect/Schema" -import * as E from "effect/SchemaEquivalence" import * as fc from "fast-check" import { describe, expect, it } from "vitest" @@ -21,7 +20,7 @@ export const propertyType = ( ) => { const arb = A.makeLazy(schema)(fc) // console.log(fc.sample(arb, 10)) - const equivalence = E.make(schema) + const equivalence = S.equivalence(schema) const reflexivity = fc.property(arb, (a) => equivalence(a, a)) const symmetry = fc.property(arb, arb, (a, b) => equivalence(a, b) === equivalence(b, a)) @@ -73,13 +72,13 @@ const MySymbol = S.SymbolFromSelf.annotations({ describe("SchemaEquivalence", () => { it("the errors should disply a path", () => { - expect(() => E.make(S.Tuple(S.Never as any))).toThrow( + expect(() => S.equivalence(S.Tuple(S.Never as any))).toThrow( new Error(`Unsupported schema at path: [0] details: Cannot build an Equivalence schema (NeverKeyword): never`) ) - expect(() => E.make(S.Struct({ a: S.Never as any }))).toThrow( + expect(() => S.equivalence(S.Struct({ a: S.Never as any }))).toThrow( new Error(`Unsupported schema at path: ["a"] details: Cannot build an Equivalence @@ -89,16 +88,16 @@ schema (NeverKeyword): never`) it("transformation", () => { const schema = S.NumberFromString - const equivalence = E.make(schema) + const equivalence = S.equivalence(schema) expect(equivalence(1, 1)).toBe(true) expect(equivalence(1, 2)).toBe(false) }) - it("E.make(S.encodedSchema(schema))", () => { + it("S.equivalence(S.encodedSchema(schema))", () => { const schema = S.NumberFromString - const equivalence = E.make(S.encodedSchema(schema)) + const equivalence = S.equivalence(S.encodedSchema(schema)) expect(equivalence("a", "a")).toBe(true) @@ -106,7 +105,7 @@ schema (NeverKeyword): never`) }) it("never", () => { - expect(() => E.make(S.Never)).toThrow( + expect(() => S.equivalence(S.Never)).toThrow( new Error(`Unsupported schema details: Cannot build an Equivalence schema (NeverKeyword): never`) @@ -115,7 +114,7 @@ schema (NeverKeyword): never`) it("string", () => { const schema = MyString - const equivalence = E.make(schema) + const equivalence = S.equivalence(schema) expect(equivalence("a", "a")).toBe(true) @@ -126,7 +125,7 @@ schema (NeverKeyword): never`) it("Refinement", () => { const schema = S.NonEmptyString - const equivalence = E.make(schema) + const equivalence = S.equivalence(schema) expect(equivalence("a", "a")).toBe(true) @@ -138,7 +137,7 @@ schema (NeverKeyword): never`) describe("declaration", () => { it("should return Equal.equals when an annotation doesn't exist", () => { const schema = S.declare(isUnknown) - const equivalence = E.make(schema) + const equivalence = S.equivalence(schema) expect(equivalence).toStrictEqual(Equal.equals) const make = (id: number, s: string) => { @@ -161,7 +160,7 @@ schema (NeverKeyword): never`) it("Chunk", () => { const schema = S.ChunkFromSelf(MyNumber) - const equivalence = E.make(schema) + const equivalence = S.equivalence(schema) expect(equivalence(Chunk.empty(), Chunk.empty())).toBe(true) expect(equivalence(Chunk.make(1, 2, 3), Chunk.make(1, 2, 3))).toBe(true) @@ -174,7 +173,7 @@ schema (NeverKeyword): never`) it("Date", () => { const schema = S.DateFromSelf - const equivalence = E.make(schema) + const equivalence = S.equivalence(schema) const now = new Date() expect(equivalence(now, now)).toBe(true) @@ -187,7 +186,7 @@ schema (NeverKeyword): never`) it("Data", () => { const schema = S.DataFromSelf(S.Struct({ a: MyString, b: MyNumber })) - const equivalence = E.make(schema) + const equivalence = S.equivalence(schema) expect(equivalence(Data.struct({ a: "ok", b: 0 }), Data.struct({ a: "ok", b: 0 }))).toBe(true) @@ -196,7 +195,7 @@ schema (NeverKeyword): never`) it("Either", () => { const schema = S.EitherFromSelf({ left: MyString, right: MyNumber }) - const equivalence = E.make(schema) + const equivalence = S.equivalence(schema) expect(equivalence(Either.right(1), Either.right(1))).toBe(true) expect(equivalence(Either.left("a"), Either.left("a"))).toBe(true) @@ -209,7 +208,7 @@ schema (NeverKeyword): never`) it("Option", () => { const schema = S.OptionFromSelf(MyNumber) - const equivalence = E.make(schema) + const equivalence = S.equivalence(schema) expect(equivalence(Option.none(), Option.none())).toBe(true) expect(equivalence(Option.some(1), Option.some(1))).toBe(true) @@ -221,7 +220,7 @@ schema (NeverKeyword): never`) it("ReadonlySet", () => { const schema = S.ReadonlySetFromSelf(MyNumber) - const equivalence = E.make(schema) + const equivalence = S.equivalence(schema) expect(equivalence(new Set(), new Set())).toBe(true) expect(equivalence(new Set([1, 2, 3]), new Set([1, 2, 3]))).toBe(true) @@ -233,7 +232,7 @@ schema (NeverKeyword): never`) it("ReadonlyMap", () => { const schema = S.ReadonlyMapFromSelf({ key: MyString, value: MyNumber }) - const equivalence = E.make(schema) + const equivalence = S.equivalence(schema) expect(equivalence(new Map(), new Map())).toBe(true) expect(equivalence(new Map([["a", 1], ["b", 2]]), new Map([["a", 1], ["b", 2]]))).toBe(true) @@ -246,7 +245,7 @@ schema (NeverKeyword): never`) it("Uint8Array", () => { const schema = S.Uint8ArrayFromSelf - const equivalence = E.make(schema) + const equivalence = S.equivalence(schema) expect(equivalence(new Uint8Array(), new Uint8Array())).toBe(true) expect( @@ -264,7 +263,7 @@ schema (NeverKeyword): never`) const schema = S.instanceOf(URL, { equivalence: () => Equivalence.make((a, b) => a.href === b.href) }) - const equivalence = E.make(schema) + const equivalence = S.equivalence(schema) expect(equivalence(new URL("https://example.com/page"), new URL("https://example.com/page"))) .toBe(true) @@ -277,7 +276,7 @@ schema (NeverKeyword): never`) describe("union", () => { it("primitives", () => { const schema = S.Union(MyString, MyNumber) - const equivalence = E.make(schema) + const equivalence = S.equivalence(schema) expect(equivalence("a", "a")).toBe(true) expect(equivalence(1, 1)).toBe(true) @@ -292,7 +291,7 @@ schema (NeverKeyword): never`) const a = S.Struct({ a: MyString }) const ab = S.Struct({ a: MyString, b: S.Number }) const schema = S.Union(a, ab) - const equivalence = E.make(schema) + const equivalence = S.equivalence(schema) expect(equivalence({ a: "a", b: 1 }, { a: "a", b: 1 })).toBe(true) expect(equivalence({ a: "a", b: 1 }, { a: "a", b: 2 })).toBe(true) @@ -307,7 +306,7 @@ schema (NeverKeyword): never`) S.Struct({ tag: S.Literal("a"), a: MyString }), S.Struct({ tag: S.Literal("b"), b: S.Number }) ) - const equivalence = E.make(schema) + const equivalence = S.equivalence(schema) expect(equivalence({ tag: "a", a: "a" }, { tag: "a", a: "a" })).toBe(true) expect(equivalence({ tag: "b", b: 1 }, { tag: "b", b: 1 })).toBe(true) @@ -321,14 +320,14 @@ schema (NeverKeyword): never`) describe("tuple", () => { it("empty", () => { const schema = S.Tuple() - const equivalence = E.make(schema) + const equivalence = S.equivalence(schema) expect(equivalence([], [])).toBe(true) }) it("e", () => { const schema = S.Tuple(MyString, MyNumber) - const equivalence = E.make(schema) + const equivalence = S.equivalence(schema) expect(equivalence(["a", 1], ["a", 1])).toBe(true) @@ -340,7 +339,7 @@ schema (NeverKeyword): never`) it("e r", () => { const schema = S.Tuple([S.String], S.Number) - const equivalence = E.make(schema) + const equivalence = S.equivalence(schema) expect(equivalence(["a"], ["a"])).toBe(true) expect(equivalence(["a", 1], ["a", 1])).toBe(true) @@ -354,7 +353,7 @@ schema (NeverKeyword): never`) it("r", () => { const schema = S.Array(MyNumber) - const equivalence = E.make(schema) + const equivalence = S.equivalence(schema) expect(equivalence([], [])).toBe(true) expect(equivalence([1], [1])).toBe(true) @@ -368,7 +367,7 @@ schema (NeverKeyword): never`) it("r e", () => { const schema = S.Tuple([], MyString, MyNumber) - const equivalence = E.make(schema) + const equivalence = S.equivalence(schema) expect(equivalence([1], [1])).toBe(true) expect(equivalence(["a", 1], ["a", 1])).toBe(true) @@ -384,7 +383,7 @@ schema (NeverKeyword): never`) describe("optional element support", () => { it("e?", () => { const schema = S.Tuple(S.optionalElement(MyString)) - const equivalence = E.make(schema) + const equivalence = S.equivalence(schema) expect(equivalence([], [])).toBe(true) expect(equivalence(["a"], ["a"])).toBe(true) @@ -398,7 +397,7 @@ schema (NeverKeyword): never`) it("e? e?", () => { const schema = S.Tuple(S.optionalElement(MyString), S.optionalElement(MyNumber)) - const equivalence = E.make(schema) + const equivalence = S.equivalence(schema) expect(equivalence([], [])).toBe(true) expect(equivalence(["a"], ["a"])).toBe(true) @@ -416,7 +415,7 @@ schema (NeverKeyword): never`) it("e e?", () => { const schema = S.Tuple(MyString, S.optionalElement(MyNumber)) - const equivalence = E.make(schema) + const equivalence = S.equivalence(schema) expect(equivalence(["a"], ["a"])).toBe(true) expect(equivalence(["a", 1], ["a", 1])).toBe(true) @@ -430,7 +429,7 @@ schema (NeverKeyword): never`) it("e? r", () => { const schema = S.Tuple([S.optionalElement(S.String)], S.Number) - const equivalence = E.make(schema) + const equivalence = S.equivalence(schema) expect(equivalence([], [])).toBe(true) expect(equivalence(["a"], ["a"])).toBe(true) @@ -449,14 +448,14 @@ schema (NeverKeyword): never`) describe("struct", () => { it("empty", () => { const schema = S.Struct({}) - const equivalence = E.make(schema) + const equivalence = S.equivalence(schema) expect(equivalence({}, {})).toBe(false) }) it("string keys", () => { const schema = S.Struct({ a: MyString, b: MyNumber }) - const equivalence = E.make(schema) + const equivalence = S.equivalence(schema) expect(equivalence({ a: "a", b: 1 }, { a: "a", b: 1 })).toBe(true) // should ignore excess properties @@ -479,7 +478,7 @@ schema (NeverKeyword): never`) const a = Symbol.for("effect/Schema/test/a") const b = Symbol.for("effect/Schema/test/b") const schema = S.Struct({ [a]: MyString, [b]: MyNumber }) - const equivalence = E.make(schema) + const equivalence = S.equivalence(schema) expect(equivalence({ [a]: "a", [b]: 1 }, { [a]: "a", [b]: 1 })).toBe(true) // should ignore excess properties @@ -503,7 +502,7 @@ schema (NeverKeyword): never`) a: S.optionalWith(MyString, { exact: true }), b: S.optionalWith(S.Union(MyNumber, S.Undefined), { exact: true }) }) - const equivalence = E.make(schema) + const equivalence = S.equivalence(schema) expect(equivalence({ a: "a", b: 1 }, { a: "a", b: 1 })).toBe(true) expect(equivalence({ b: 1 }, { b: 1 })).toBe(true) @@ -525,7 +524,7 @@ schema (NeverKeyword): never`) describe("record", () => { it("record(never, number)", () => { const schema = S.Record({ key: S.Never, value: MyNumber }) - const equivalence = E.make(schema) + const equivalence = S.equivalence(schema) const input = {} expect(equivalence(input, input)).toBe(true) @@ -534,7 +533,7 @@ schema (NeverKeyword): never`) it("record(string, number)", () => { const schema = S.Record({ key: MyString, value: MyNumber }) - const equivalence = E.make(schema) + const equivalence = S.equivalence(schema) expect(equivalence({}, {})).toBe(true) expect(equivalence({ a: 1 }, { a: 1 })).toBe(true) @@ -553,7 +552,7 @@ schema (NeverKeyword): never`) it("record(symbol, number)", () => { const schema = S.Record({ key: MySymbol, value: MyNumber }) - const equivalence = E.make(schema) + const equivalence = S.equivalence(schema) const a = Symbol.for("effect/Schema/test/a") const b = Symbol.for("effect/Schema/test/b") @@ -574,7 +573,7 @@ schema (NeverKeyword): never`) it("struct record", () => { const schema = S.Struct({ a: MyString, b: MyString }, S.Record({ key: MyString, value: MyString })) - const equivalence = E.make(schema) + const equivalence = S.equivalence(schema) expect(equivalence({ a: "a", b: "b" }, { a: "a", b: "b" })).toBe(true) expect(equivalence({ a: "a", b: "b", c: "c" }, { a: "a", b: "b", c: "c" })).toBe(true) @@ -590,7 +589,7 @@ schema (NeverKeyword): never`) const schema = S.Struct({ a: MyString, b: MyString }).annotations({ equivalence: () => Equivalence.make((x, y) => x.a === y.a) }) - const equivalence = E.make(schema) + const equivalence = S.equivalence(schema) expect(equivalence({ a: "a", b: "b" }, { a: "a", b: "b" })).toBe(true) expect(equivalence({ a: "a", b: "b" }, { a: "a", b: "c" })).toBe(true) @@ -612,7 +611,7 @@ schema (NeverKeyword): never`) as: S.Array(S.suspend((): S.Schema => schema)) }) - const equivalence = E.make(schema) + const equivalence = S.equivalence(schema) const a1: A = { a: "a1", as: [] } expect(equivalence(a1, a1)).toBe(true) @@ -653,7 +652,7 @@ schema (NeverKeyword): never`) right: Expression }) - const equivalence = E.make(Operation) + const equivalence = S.equivalence(Operation) const a1: Operation = { type: "operation", @@ -712,7 +711,7 @@ schema (NeverKeyword): never`) describe("should handle annotations", () => { const expectHook = (source: S.Schema) => { const schema = source.annotations({ equivalence: () => () => true }) - const eq = E.make(schema) + const eq = S.equivalence(schema) expect(eq("a" as any, "b" as any)).toEqual(true) } diff --git a/packages/schema/src/Equivalence.ts b/packages/schema/src/Equivalence.ts deleted file mode 100644 index edffa47537..0000000000 --- a/packages/schema/src/Equivalence.ts +++ /dev/null @@ -1,9 +0,0 @@ -/** - * @since 0.67.0 - */ - -/** - * @category re-exports - * @since 0.76.0 - */ -export * from "effect/SchemaEquivalence" diff --git a/packages/schema/src/index.ts b/packages/schema/src/index.ts index 1c2004c88d..53259a4d3c 100644 --- a/packages/schema/src/index.ts +++ b/packages/schema/src/index.ts @@ -8,11 +8,6 @@ export * as AST from "./AST.js" */ export * as Arbitrary from "./Arbitrary.js" -/** - * @since 0.67.0 - */ -export * as Equivalence from "./Equivalence.js" - /** * @since 0.67.0 */ From 242c898f9c9e19067344f27dc61a6630d6e70810 Mon Sep 17 00:00:00 2001 From: Giulio Canti Date: Tue, 15 Oct 2024 15:19:20 +0200 Subject: [PATCH 08/12] remove schema package --- .changeset/cyan-sloths-lick.md | 11 - .github/CODEOWNERS | 1 - packages/ai/ai/src/AiChat.ts | 4 +- packages/ai/ai/src/AiError.ts | 2 +- packages/ai/ai/src/AiInput.ts | 4 +- packages/ai/ai/src/AiResponse.ts | 2 +- packages/ai/ai/src/AiRole.ts | 2 +- packages/ai/ai/src/AiToolkit.ts | 2 +- packages/ai/ai/src/Completions.ts | 4 +- packages/ai/ai/tsconfig.build.json | 3 +- packages/ai/ai/tsconfig.src.json | 6 +- packages/ai/ai/tsconfig.test.json | 1 - packages/ai/openai/src/Generated.ts | 4 +- packages/ai/openai/tsconfig.build.json | 3 +- packages/ai/openai/tsconfig.src.json | 3 +- packages/ai/openai/tsconfig.test.json | 1 - packages/cli/examples/naval-fate/domain.ts | 2 +- packages/cli/examples/naval-fate/store.ts | 2 +- packages/cli/tsconfig.build.json | 3 +- packages/cli/tsconfig.examples.json | 3 +- packages/cli/tsconfig.src.json | 3 +- packages/cli/tsconfig.test.json | 3 +- packages/cluster-browser/tsconfig.build.json | 3 +- packages/cluster-browser/tsconfig.src.json | 6 +- .../cluster-node/examples/sample-common.ts | 2 +- packages/cluster-node/tsconfig.build.json | 3 +- packages/cluster-node/tsconfig.src.json | 7 +- packages/cluster-workflow/tsconfig.build.json | 3 +- packages/cluster-workflow/tsconfig.src.json | 10 +- packages/cluster/tsconfig.build.json | 1 - packages/cluster/tsconfig.src.json | 5 +- packages/cluster/tsconfig.test.json | 7 +- .../experimental/examples/redis/resolver.ts | 3 +- .../examples/serializable-machine.ts | 3 +- packages/experimental/test/Machine.test.ts | 3 +- .../experimental/test/PersistedCache.test.ts | 3 +- .../experimental/test/RequestResolver.test.ts | 3 +- packages/experimental/tsconfig.build.json | 1 - packages/experimental/tsconfig.src.json | 1 - packages/experimental/tsconfig.test.json | 1 - packages/platform-browser/tsconfig.build.json | 3 +- .../platform-browser/tsconfig.examples.json | 6 +- packages/platform-browser/tsconfig.src.json | 6 +- packages/platform-browser/tsconfig.test.json | 1 - packages/platform-bun/examples/http-client.ts | 4 +- packages/platform-bun/examples/http-router.ts | 3 +- .../platform-node-shared/tsconfig.build.json | 3 +- .../platform-node-shared/tsconfig.src.json | 6 +- .../platform-node-shared/tsconfig.test.json | 3 +- packages/platform-node/examples/api.ts | 3 +- .../platform-node/examples/http-client.ts | 4 +- .../platform-node/examples/http-router.ts | 2 +- packages/platform-node/test/HttpApi.test.ts | 3 +- packages/platform-node/tsconfig.build.json | 3 +- packages/platform-node/tsconfig.src.json | 3 +- packages/platform-node/tsconfig.test.json | 1 - packages/platform/src/HttpApiBuilder.ts | 3 +- packages/platform/test/Transferable.test.ts | 3 +- packages/platform/tsconfig.build.json | 5 +- packages/platform/tsconfig.src.json | 5 +- packages/platform/tsconfig.test.json | 1 - packages/rpc-http/examples/schema.ts | 2 +- packages/rpc-http/tsconfig.build.json | 1 - packages/rpc-http/tsconfig.examples.json | 1 - packages/rpc-http/tsconfig.src.json | 1 - packages/rpc-http/tsconfig.test.json | 1 - packages/rpc/test/Router.test.ts | 79 ++-- packages/rpc/tsconfig.build.json | 3 +- packages/rpc/tsconfig.examples.json | 3 +- packages/rpc/tsconfig.src.json | 6 +- packages/rpc/tsconfig.test.json | 3 +- packages/schema/LICENSE | 21 -- packages/schema/README.md | 271 ------------- packages/schema/docgen.json | 26 -- packages/schema/package.json | 56 --- packages/schema/serializable.md | 355 ------------------ packages/schema/src/AST.ts | 9 - packages/schema/src/Arbitrary.ts | 9 - packages/schema/src/FastCheck.ts | 9 - packages/schema/src/JSONSchema.ts | 9 - packages/schema/src/ParseResult.ts | 9 - packages/schema/src/Pretty.ts | 9 - packages/schema/src/Schema.ts | 9 - packages/schema/src/index.ts | 34 -- packages/schema/test/index.test.ts | 5 - packages/schema/tsconfig.build.json | 12 - packages/schema/tsconfig.json | 8 - packages/schema/tsconfig.src.json | 12 - packages/schema/tsconfig.test.json | 13 - packages/schema/vitest.config.ts | 6 - packages/sql-kysely/test/Sqlite.test.ts | 3 +- packages/sql-mysql2/test/Model.test.ts | 3 +- packages/sql-pg/examples/resolver.ts | 2 +- packages/sql/src/Model.ts | 2 +- packages/sql/tsconfig.build.json | 3 +- packages/sql/tsconfig.examples.json | 3 +- packages/sql/tsconfig.src.json | 3 +- packages/sql/tsconfig.test.json | 3 +- {packages/schema => schema}/CHANGELOG.md | 7 + {packages/schema => schema}/comparisons.md | 0 tsconfig.base.json | 3 - tsconfig.build.json | 1 - tsconfig.json | 2 - vitest.shared.ts | 1 - 104 files changed, 120 insertions(+), 1103 deletions(-) delete mode 100644 .changeset/cyan-sloths-lick.md delete mode 100644 packages/schema/LICENSE delete mode 100644 packages/schema/README.md delete mode 100644 packages/schema/docgen.json delete mode 100644 packages/schema/package.json delete mode 100644 packages/schema/serializable.md delete mode 100644 packages/schema/src/AST.ts delete mode 100644 packages/schema/src/Arbitrary.ts delete mode 100644 packages/schema/src/FastCheck.ts delete mode 100644 packages/schema/src/JSONSchema.ts delete mode 100644 packages/schema/src/ParseResult.ts delete mode 100644 packages/schema/src/Pretty.ts delete mode 100644 packages/schema/src/Schema.ts delete mode 100644 packages/schema/src/index.ts delete mode 100644 packages/schema/test/index.test.ts delete mode 100644 packages/schema/tsconfig.build.json delete mode 100644 packages/schema/tsconfig.json delete mode 100644 packages/schema/tsconfig.src.json delete mode 100644 packages/schema/tsconfig.test.json delete mode 100644 packages/schema/vitest.config.ts rename {packages/schema => schema}/CHANGELOG.md (99%) rename {packages/schema => schema}/comparisons.md (100%) diff --git a/.changeset/cyan-sloths-lick.md b/.changeset/cyan-sloths-lick.md deleted file mode 100644 index 3a8407dea9..0000000000 --- a/.changeset/cyan-sloths-lick.md +++ /dev/null @@ -1,11 +0,0 @@ ---- -"@effect/schema": minor ---- - -Re-export modules from `effect`. - -`ArrayFormatter` / `TreeFormatter` merged into `ParseResult` module. - -`Serializable` module merged into `Schema` module. - -`Equivalence` module merged into `Schema` module. diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 4ee8222af8..6d1efe9bb3 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -15,7 +15,6 @@ /packages/printer-ansi/ @IMax153 /packages/rpc/ @tim-smart /packages/rpc-http/ @tim-smart -/packages/schema/ @gcanti /packages/sql/ @tim-smart /packages/sql-mssql/ @tim-smart /packages/sql-mysql2/ @tim-smart diff --git a/packages/ai/ai/src/AiChat.ts b/packages/ai/ai/src/AiChat.ts index 5a47f4600f..da90dfaabb 100644 --- a/packages/ai/ai/src/AiChat.ts +++ b/packages/ai/ai/src/AiChat.ts @@ -1,12 +1,12 @@ /** * @since 1.0.0 */ -import type { ParseError } from "@effect/schema/ParseResult" -import * as Schema from "@effect/schema/Schema" import * as Channel from "effect/Channel" import * as Chunk from "effect/Chunk" import * as Effect from "effect/Effect" +import type { ParseError } from "effect/ParseResult" import * as Ref from "effect/Ref" +import * as Schema from "effect/Schema" import * as Stream from "effect/Stream" import type { Concurrency } from "effect/Types" import type { AiError } from "./AiError.js" diff --git a/packages/ai/ai/src/AiError.ts b/packages/ai/ai/src/AiError.ts index 83a6e15931..30bf1d77fc 100644 --- a/packages/ai/ai/src/AiError.ts +++ b/packages/ai/ai/src/AiError.ts @@ -1,7 +1,7 @@ /** * @since 1.0.0 */ -import * as Schema from "@effect/schema/Schema" +import * as Schema from "effect/Schema" /** * @since 1.0.0 diff --git a/packages/ai/ai/src/AiInput.ts b/packages/ai/ai/src/AiInput.ts index cf95f00a4f..6cbad1bf87 100644 --- a/packages/ai/ai/src/AiInput.ts +++ b/packages/ai/ai/src/AiInput.ts @@ -4,15 +4,15 @@ import type { PlatformError } from "@effect/platform/Error" import * as FileSystem from "@effect/platform/FileSystem" import * as Path from "@effect/platform/Path" -import * as ParseResult from "@effect/schema/ParseResult" -import * as Schema_ from "@effect/schema/Schema" import * as Chunk from "effect/Chunk" import * as Context from "effect/Context" import * as Effect from "effect/Effect" import * as Encoding from "effect/Encoding" import { dual } from "effect/Function" import * as Option from "effect/Option" +import * as ParseResult from "effect/ParseResult" import * as Predicate from "effect/Predicate" +import * as Schema_ from "effect/Schema" import { AiResponse, ToolCallId, WithResolved } from "./AiResponse.js" import * as AiRole from "./AiRole.js" diff --git a/packages/ai/ai/src/AiResponse.ts b/packages/ai/ai/src/AiResponse.ts index 20a0ef03bb..78bbb311f0 100644 --- a/packages/ai/ai/src/AiResponse.ts +++ b/packages/ai/ai/src/AiResponse.ts @@ -1,13 +1,13 @@ /** * @since 1.0.0 */ -import * as Schema from "@effect/schema/Schema" import * as Chunk from "effect/Chunk" import * as Data from "effect/Data" import * as Effect from "effect/Effect" import * as Iterable from "effect/Iterable" import * as Option from "effect/Option" import * as Predicate from "effect/Predicate" +import * as Schema from "effect/Schema" import { AiError } from "./AiError.js" import * as AiRole from "./AiRole.js" diff --git a/packages/ai/ai/src/AiRole.ts b/packages/ai/ai/src/AiRole.ts index 557a34fefa..af2ec4ae20 100644 --- a/packages/ai/ai/src/AiRole.ts +++ b/packages/ai/ai/src/AiRole.ts @@ -1,8 +1,8 @@ /** * @since 1.0.0 */ -import * as Schema from "@effect/schema/Schema" import * as Option from "effect/Option" +import * as Schema from "effect/Schema" /** * @since 1.0.0 diff --git a/packages/ai/ai/src/AiToolkit.ts b/packages/ai/ai/src/AiToolkit.ts index 1370b66f7f..6d1aaf2e8b 100644 --- a/packages/ai/ai/src/AiToolkit.ts +++ b/packages/ai/ai/src/AiToolkit.ts @@ -1,7 +1,6 @@ /** * @since 1.0.0 */ -import type * as Schema from "@effect/schema/Schema" import * as Context from "effect/Context" import * as Effect from "effect/Effect" import * as Effectable from "effect/Effectable" @@ -10,6 +9,7 @@ import * as HashMap from "effect/HashMap" import * as Inspectable from "effect/Inspectable" import * as Layer from "effect/Layer" import { pipeArguments } from "effect/Pipeable" +import type * as Schema from "effect/Schema" import type { Scope } from "effect/Scope" import type * as Types from "effect/Types" diff --git a/packages/ai/ai/src/Completions.ts b/packages/ai/ai/src/Completions.ts index 31cdf7adc2..0f3e5cfa1b 100644 --- a/packages/ai/ai/src/Completions.ts +++ b/packages/ai/ai/src/Completions.ts @@ -2,13 +2,13 @@ * @since 1.0.0 */ import * as JsonSchema from "@effect/platform/OpenApiJsonSchema" -import * as AST from "@effect/schema/AST" -import * as Schema from "@effect/schema/Schema" import * as Chunk from "effect/Chunk" import * as Context from "effect/Context" import * as Effect from "effect/Effect" import * as HashMap from "effect/HashMap" import * as Option from "effect/Option" +import * as Schema from "effect/Schema" +import * as AST from "effect/SchemaAST" import * as Stream from "effect/Stream" import type { Concurrency } from "effect/Types" import { AiError } from "./AiError.js" diff --git a/packages/ai/ai/tsconfig.build.json b/packages/ai/ai/tsconfig.build.json index be5cca2ee7..e28d885dba 100644 --- a/packages/ai/ai/tsconfig.build.json +++ b/packages/ai/ai/tsconfig.build.json @@ -2,8 +2,7 @@ "extends": "./tsconfig.src.json", "references": [ { "path": "../../effect/tsconfig.build.json" }, - { "path": "../../platform/tsconfig.build.json" }, - { "path": "../../schema/tsconfig.build.json" } + { "path": "../../platform/tsconfig.build.json" } ], "compilerOptions": { "tsBuildInfoFile": ".tsbuildinfo/build.tsbuildinfo", diff --git a/packages/ai/ai/tsconfig.src.json b/packages/ai/ai/tsconfig.src.json index 127e58a359..be27b3befb 100644 --- a/packages/ai/ai/tsconfig.src.json +++ b/packages/ai/ai/tsconfig.src.json @@ -1,11 +1,7 @@ { "extends": "../../../tsconfig.base.json", "include": ["src"], - "references": [ - { "path": "../../effect" }, - { "path": "../../platform" }, - { "path": "../../schema" } - ], + "references": [{ "path": "../../effect" }, { "path": "../../platform" }], "compilerOptions": { "tsBuildInfoFile": ".tsbuildinfo/src.tsbuildinfo", "rootDir": "src", diff --git a/packages/ai/ai/tsconfig.test.json b/packages/ai/ai/tsconfig.test.json index 892b49082e..4781a0a2a5 100644 --- a/packages/ai/ai/tsconfig.test.json +++ b/packages/ai/ai/tsconfig.test.json @@ -5,7 +5,6 @@ { "path": "tsconfig.src.json" }, { "path": "../../effect" }, { "path": "../../platform" }, - { "path": "../../schema" }, { "path": "../../vitest" } ], "compilerOptions": { diff --git a/packages/ai/openai/src/Generated.ts b/packages/ai/openai/src/Generated.ts index 0773e22119..f9d4bc4b12 100644 --- a/packages/ai/openai/src/Generated.ts +++ b/packages/ai/openai/src/Generated.ts @@ -5,9 +5,9 @@ import type * as HttpClient from "@effect/platform/HttpClient" import * as HttpClientError from "@effect/platform/HttpClientError" import * as HttpClientRequest from "@effect/platform/HttpClientRequest" import * as HttpClientResponse from "@effect/platform/HttpClientResponse" -import type { ParseError } from "@effect/schema/ParseResult" -import * as S from "@effect/schema/Schema" import * as Effect from "effect/Effect" +import type { ParseError } from "effect/ParseResult" +import * as S from "effect/Schema" export class ChatCompletionRequestMessageContentPartText extends S.Struct({ "type": S.Literal("text"), diff --git a/packages/ai/openai/tsconfig.build.json b/packages/ai/openai/tsconfig.build.json index 05f1fccc7a..48f83a4721 100644 --- a/packages/ai/openai/tsconfig.build.json +++ b/packages/ai/openai/tsconfig.build.json @@ -4,8 +4,7 @@ { "path": "../../effect/tsconfig.build.json" }, { "path": "../ai/tsconfig.build.json" }, { "path": "../../experimental/tsconfig.build.json" }, - { "path": "../../platform/tsconfig.build.json" }, - { "path": "../../schema/tsconfig.build.json" } + { "path": "../../platform/tsconfig.build.json" } ], "compilerOptions": { "tsBuildInfoFile": ".tsbuildinfo/build.tsbuildinfo", diff --git a/packages/ai/openai/tsconfig.src.json b/packages/ai/openai/tsconfig.src.json index f570f4485b..354a8aec05 100644 --- a/packages/ai/openai/tsconfig.src.json +++ b/packages/ai/openai/tsconfig.src.json @@ -5,8 +5,7 @@ { "path": "../../effect" }, { "path": "../ai" }, { "path": "../../experimental" }, - { "path": "../../platform" }, - { "path": "../../schema" } + { "path": "../../platform" } ], "compilerOptions": { "tsBuildInfoFile": ".tsbuildinfo/src.tsbuildinfo", diff --git a/packages/ai/openai/tsconfig.test.json b/packages/ai/openai/tsconfig.test.json index 7af6a56bea..dba1449e95 100644 --- a/packages/ai/openai/tsconfig.test.json +++ b/packages/ai/openai/tsconfig.test.json @@ -7,7 +7,6 @@ { "path": "../ai" }, { "path": "../../experimental" }, { "path": "../../platform" }, - { "path": "../../schema" }, { "path": "../../vitest" } ], "compilerOptions": { diff --git a/packages/cli/examples/naval-fate/domain.ts b/packages/cli/examples/naval-fate/domain.ts index 27e914dc58..7cec03f100 100644 --- a/packages/cli/examples/naval-fate/domain.ts +++ b/packages/cli/examples/naval-fate/domain.ts @@ -1,5 +1,5 @@ -import * as Schema from "@effect/schema/Schema" import * as Data from "effect/Data" +import * as Schema from "effect/Schema" /** * An error that occurs when attempting to create a Naval Fate ship that already diff --git a/packages/cli/examples/naval-fate/store.ts b/packages/cli/examples/naval-fate/store.ts index b4348374d3..f043a9146f 100644 --- a/packages/cli/examples/naval-fate/store.ts +++ b/packages/cli/examples/naval-fate/store.ts @@ -1,11 +1,11 @@ import * as KeyValueStore from "@effect/platform/KeyValueStore" -import * as Schema from "@effect/schema/Schema" import * as Arr from "effect/Array" import * as Context from "effect/Context" import * as Effect from "effect/Effect" import { pipe } from "effect/Function" import * as Layer from "effect/Layer" import * as Option from "effect/Option" +import * as Schema from "effect/Schema" import { CoordinatesOccupiedError, Mine, Ship, ShipExistsError, ShipNotFoundError } from "./domain.js" /** diff --git a/packages/cli/tsconfig.build.json b/packages/cli/tsconfig.build.json index fadf00be97..1a36eb89df 100644 --- a/packages/cli/tsconfig.build.json +++ b/packages/cli/tsconfig.build.json @@ -2,11 +2,10 @@ "extends": "./tsconfig.src.json", "references": [ { "path": "../effect/tsconfig.build.json" }, - { "path": "../schema/tsconfig.build.json" }, { "path": "../printer/tsconfig.build.json" }, { "path": "../printer-ansi/tsconfig.build.json" }, { "path": "../platform/tsconfig.build.json" }, - { "path": "../platform-node/tsconfig.build.json" }, + { "path": "../platform-node/tsconfig.build.json" } ], "compilerOptions": { "types": ["node"], diff --git a/packages/cli/tsconfig.examples.json b/packages/cli/tsconfig.examples.json index c8ed2bd93a..b244aaebd1 100644 --- a/packages/cli/tsconfig.examples.json +++ b/packages/cli/tsconfig.examples.json @@ -4,11 +4,10 @@ "references": [ { "path": "tsconfig.src.json" }, { "path": "../effect" }, - { "path": "../schema" }, { "path": "../printer" }, { "path": "../printer-ansi" }, { "path": "../platform" }, - { "path": "../platform-node" }, + { "path": "../platform-node" } ], "compilerOptions": { "types": ["node"], diff --git a/packages/cli/tsconfig.src.json b/packages/cli/tsconfig.src.json index 2115a3effb..d6e3c3ae5f 100644 --- a/packages/cli/tsconfig.src.json +++ b/packages/cli/tsconfig.src.json @@ -3,11 +3,10 @@ "include": ["src"], "references": [ { "path": "../effect" }, - { "path": "../schema" }, { "path": "../printer" }, { "path": "../printer-ansi" }, { "path": "../platform" }, - { "path": "../platform-node" }, + { "path": "../platform-node" } ], "compilerOptions": { "types": ["node"], diff --git a/packages/cli/tsconfig.test.json b/packages/cli/tsconfig.test.json index d751ab5910..b0f22e2d30 100644 --- a/packages/cli/tsconfig.test.json +++ b/packages/cli/tsconfig.test.json @@ -4,11 +4,10 @@ "references": [ { "path": "tsconfig.src.json" }, { "path": "../effect" }, - { "path": "../schema" }, { "path": "../printer" }, { "path": "../printer-ansi" }, { "path": "../platform" }, - { "path": "../platform-node" }, + { "path": "../platform-node" } ], "compilerOptions": { "types": ["node"], diff --git a/packages/cluster-browser/tsconfig.build.json b/packages/cluster-browser/tsconfig.build.json index 36692e627e..f0ff2e3e55 100644 --- a/packages/cluster-browser/tsconfig.build.json +++ b/packages/cluster-browser/tsconfig.build.json @@ -2,8 +2,7 @@ "extends": "./tsconfig.src.json", "references": [ { "path": "../effect/tsconfig.build.json" }, - { "path": "../rpc/tsconfig.build.json" }, - { "path": "../schema/tsconfig.build.json" } + { "path": "../rpc/tsconfig.build.json" } ], "compilerOptions": { "tsBuildInfoFile": ".tsbuildinfo/build.tsbuildinfo", diff --git a/packages/cluster-browser/tsconfig.src.json b/packages/cluster-browser/tsconfig.src.json index 9d467a9551..2d72ee5c02 100644 --- a/packages/cluster-browser/tsconfig.src.json +++ b/packages/cluster-browser/tsconfig.src.json @@ -1,11 +1,7 @@ { "extends": "../../tsconfig.base.json", "include": ["src"], - "references": [ - { "path": "../effect" }, - { "path": "../rpc" }, - { "path": "../schema" } - ], + "references": [{ "path": "../effect" }, { "path": "../rpc" }], "compilerOptions": { "tsBuildInfoFile": ".tsbuildinfo/src.tsbuildinfo", "rootDir": "src", diff --git a/packages/cluster-node/examples/sample-common.ts b/packages/cluster-node/examples/sample-common.ts index 828fda62ca..1b2928f94b 100644 --- a/packages/cluster-node/examples/sample-common.ts +++ b/packages/cluster-node/examples/sample-common.ts @@ -1,6 +1,6 @@ import * as Message from "@effect/cluster/Message" import * as RecipientType from "@effect/cluster/RecipientType" -import * as Schema from "@effect/schema/Schema" +import * as Schema from "effect/Schema" export class GetCurrent extends Message.TaggedMessage()("GetCurrent", Schema.Never, Schema.Number, { messageId: Schema.String diff --git a/packages/cluster-node/tsconfig.build.json b/packages/cluster-node/tsconfig.build.json index 8f81d8e387..8b1286c662 100644 --- a/packages/cluster-node/tsconfig.build.json +++ b/packages/cluster-node/tsconfig.build.json @@ -3,8 +3,7 @@ "references": [ { "path": "../effect/tsconfig.build.json" }, { "path": "../cluster/tsconfig.build.json" }, - { "path": "../rpc/tsconfig.build.json" }, - { "path": "../schema/tsconfig.build.json" } + { "path": "../rpc/tsconfig.build.json" } ], "compilerOptions": { "types": ["node"], diff --git a/packages/cluster-node/tsconfig.src.json b/packages/cluster-node/tsconfig.src.json index 9a899d00a6..a0dbf713b3 100644 --- a/packages/cluster-node/tsconfig.src.json +++ b/packages/cluster-node/tsconfig.src.json @@ -1,13 +1,10 @@ { "extends": "../../tsconfig.base.json", - "include": [ - "src" - ], + "include": ["src"], "references": [ { "path": "../effect" }, { "path": "../cluster" }, - { "path": "../rpc" }, - { "path": "../schema" } + { "path": "../rpc" } ], "compilerOptions": { "types": ["node"], diff --git a/packages/cluster-workflow/tsconfig.build.json b/packages/cluster-workflow/tsconfig.build.json index 39e0efb897..8f6e6ec87d 100644 --- a/packages/cluster-workflow/tsconfig.build.json +++ b/packages/cluster-workflow/tsconfig.build.json @@ -2,8 +2,7 @@ "extends": "./tsconfig.src.json", "references": [ { "path": "../effect/tsconfig.build.json" }, - { "path": "../cluster/tsconfig.build.json" }, - { "path": "../schema/tsconfig.build.json" } + { "path": "../cluster/tsconfig.build.json" } ], "compilerOptions": { "tsBuildInfoFile": ".tsbuildinfo/build.tsbuildinfo", diff --git a/packages/cluster-workflow/tsconfig.src.json b/packages/cluster-workflow/tsconfig.src.json index 5f417a08f2..bfe4147871 100644 --- a/packages/cluster-workflow/tsconfig.src.json +++ b/packages/cluster-workflow/tsconfig.src.json @@ -1,13 +1,7 @@ { "extends": "../../tsconfig.base.json", - "include": [ - "src" - ], - "references": [ - { "path": "../effect" }, - { "path": "../cluster" }, - { "path": "../schema" } - ], + "include": ["src"], + "references": [{ "path": "../effect" }, { "path": "../cluster" }], "compilerOptions": { "tsBuildInfoFile": ".tsbuildinfo/src.tsbuildinfo", "rootDir": "src", diff --git a/packages/cluster/tsconfig.build.json b/packages/cluster/tsconfig.build.json index 7d15eaf85f..b5bfec4204 100644 --- a/packages/cluster/tsconfig.build.json +++ b/packages/cluster/tsconfig.build.json @@ -3,7 +3,6 @@ "references": [ { "path": "../effect/tsconfig.build.json" }, { "path": "../platform/tsconfig.build.json" }, - { "path": "../schema/tsconfig.build.json" }, { "path": "../sql/tsconfig.build.json" } ], "compilerOptions": { diff --git a/packages/cluster/tsconfig.src.json b/packages/cluster/tsconfig.src.json index 04ca6812aa..5636abbdfa 100644 --- a/packages/cluster/tsconfig.src.json +++ b/packages/cluster/tsconfig.src.json @@ -1,12 +1,9 @@ { "extends": "../../tsconfig.base.json", - "include": [ - "src" - ], + "include": ["src"], "references": [ { "path": "../effect" }, { "path": "../platform" }, - { "path": "../schema" }, { "path": "../sql" } ], "compilerOptions": { diff --git a/packages/cluster/tsconfig.test.json b/packages/cluster/tsconfig.test.json index 6a3d8ee79d..671911fe3c 100644 --- a/packages/cluster/tsconfig.test.json +++ b/packages/cluster/tsconfig.test.json @@ -1,16 +1,13 @@ { "extends": "../../tsconfig.base.json", - "include": [ - "test", - ], + "include": ["test"], "references": [ { "path": "tsconfig.src.json" }, { "path": "../effect" }, { "path": "../platform" }, { "path": "../platform-node" }, - { "path": "../schema" }, { "path": "../sql" }, - { "path": "../sql-sqlite-node" }, + { "path": "../sql-sqlite-node" } ], "compilerOptions": { "tsBuildInfoFile": ".tsbuildinfo/test.tsbuildinfo", diff --git a/packages/experimental/examples/redis/resolver.ts b/packages/experimental/examples/redis/resolver.ts index e9b08ea2db..ae8190c586 100644 --- a/packages/experimental/examples/redis/resolver.ts +++ b/packages/experimental/examples/redis/resolver.ts @@ -1,8 +1,7 @@ import * as Redis from "@effect/experimental/Persistence/Redis" import { persisted } from "@effect/experimental/RequestResolver" import { runMain } from "@effect/platform-node/NodeRuntime" -import { Schema } from "@effect/schema" -import { Array, Effect, Exit, pipe, PrimaryKey, RequestResolver } from "effect" +import { Array, Effect, Exit, pipe, PrimaryKey, RequestResolver, Schema } from "effect" class User extends Schema.Class("User")({ id: Schema.Number, diff --git a/packages/experimental/examples/serializable-machine.ts b/packages/experimental/examples/serializable-machine.ts index f261bc51de..dcad3a59d5 100644 --- a/packages/experimental/examples/serializable-machine.ts +++ b/packages/experimental/examples/serializable-machine.ts @@ -1,7 +1,6 @@ import { Machine } from "@effect/experimental" import { runMain } from "@effect/platform-node/NodeRuntime" -import { Schema } from "@effect/schema" -import { Effect, List, pipe, Schedule } from "effect" +import { Effect, List, pipe, Schedule, Schema } from "effect" class SendError extends Schema.TaggedError()( "SendError", diff --git a/packages/experimental/test/Machine.test.ts b/packages/experimental/test/Machine.test.ts index 35ad6c528d..d4379b6c94 100644 --- a/packages/experimental/test/Machine.test.ts +++ b/packages/experimental/test/Machine.test.ts @@ -1,7 +1,6 @@ import * as DevTools from "@effect/experimental/DevTools" import * as Machine from "@effect/experimental/Machine" -import { Schema } from "@effect/schema" -import { Cause, Chunk, Context, Deferred, Effect, Exit, Layer, Stream } from "effect" +import { Cause, Chunk, Context, Deferred, Effect, Exit, Layer, Schema, Stream } from "effect" import { assert, describe, test } from "vitest" class Increment diff --git a/packages/experimental/test/PersistedCache.test.ts b/packages/experimental/test/PersistedCache.test.ts index 4932ce7eee..7835031fd5 100644 --- a/packages/experimental/test/PersistedCache.test.ts +++ b/packages/experimental/test/PersistedCache.test.ts @@ -1,9 +1,8 @@ import * as PersistedCache from "@effect/experimental/PersistedCache" import * as Persistence from "@effect/experimental/Persistence" import { KeyValueStore } from "@effect/platform" -import { Schema } from "@effect/schema" import * as it from "@effect/vitest" -import { Effect, Exit, Layer, Option, PrimaryKey } from "effect" +import { Effect, Exit, Layer, Option, PrimaryKey, Schema } from "effect" import { assert, describe } from "vitest" class User extends Schema.Class("User")({ diff --git a/packages/experimental/test/RequestResolver.test.ts b/packages/experimental/test/RequestResolver.test.ts index 3b4c6567ff..4919e661d9 100644 --- a/packages/experimental/test/RequestResolver.test.ts +++ b/packages/experimental/test/RequestResolver.test.ts @@ -1,9 +1,8 @@ import * as Persistence from "@effect/experimental/Persistence" import * as RequestResolverX from "@effect/experimental/RequestResolver" import { KeyValueStore } from "@effect/platform" -import { Schema } from "@effect/schema" import * as it from "@effect/vitest" -import { Array, Effect, Exit, Layer, PrimaryKey, Request, RequestResolver, TestClock } from "effect" +import { Array, Effect, Exit, Layer, PrimaryKey, Request, RequestResolver, Schema, TestClock } from "effect" import type { NonEmptyArray } from "effect/Array" import { assert, describe } from "vitest" diff --git a/packages/experimental/tsconfig.build.json b/packages/experimental/tsconfig.build.json index 91a6b15dd1..7c11a4a9a2 100644 --- a/packages/experimental/tsconfig.build.json +++ b/packages/experimental/tsconfig.build.json @@ -2,7 +2,6 @@ "extends": "./tsconfig.src.json", "references": [ { "path": "../effect/tsconfig.build.json" }, - { "path": "../schema/tsconfig.build.json" }, { "path": "../platform/tsconfig.build.json" }, { "path": "../platform-node/tsconfig.build.json" } ], diff --git a/packages/experimental/tsconfig.src.json b/packages/experimental/tsconfig.src.json index b69f2cb4a2..b9a4b19b34 100644 --- a/packages/experimental/tsconfig.src.json +++ b/packages/experimental/tsconfig.src.json @@ -3,7 +3,6 @@ "include": ["src"], "references": [ { "path": "../effect" }, - { "path": "../schema" }, { "path": "../platform" }, { "path": "../platform-node" } ], diff --git a/packages/experimental/tsconfig.test.json b/packages/experimental/tsconfig.test.json index a08c5c9767..fc764a2b8b 100644 --- a/packages/experimental/tsconfig.test.json +++ b/packages/experimental/tsconfig.test.json @@ -4,7 +4,6 @@ "references": [ { "path": "tsconfig.src.json" }, { "path": "../effect" }, - { "path": "../schema" }, { "path": "../platform-node" }, { "path": "../vitest" } ], diff --git a/packages/platform-browser/tsconfig.build.json b/packages/platform-browser/tsconfig.build.json index 6c869dff9b..29b272747c 100644 --- a/packages/platform-browser/tsconfig.build.json +++ b/packages/platform-browser/tsconfig.build.json @@ -2,8 +2,7 @@ "extends": "./tsconfig.src.json", "references": [ { "path": "../platform/tsconfig.build.json" }, - { "path": "../effect/tsconfig.build.json" }, - { "path": "../schema/tsconfig.build.json" } + { "path": "../effect/tsconfig.build.json" } ], "compilerOptions": { "tsBuildInfoFile": ".tsbuildinfo/build.tsbuildinfo", diff --git a/packages/platform-browser/tsconfig.examples.json b/packages/platform-browser/tsconfig.examples.json index 6381f808cf..5bfd607147 100644 --- a/packages/platform-browser/tsconfig.examples.json +++ b/packages/platform-browser/tsconfig.examples.json @@ -1,11 +1,7 @@ { "extends": "../../tsconfig.base.json", "include": ["examples"], - "references": [ - { "path": "tsconfig.src.json" }, - { "path": "../effect" }, - { "path": "../schema" } - ], + "references": [{ "path": "tsconfig.src.json" }, { "path": "../effect" }], "compilerOptions": { "tsBuildInfoFile": ".tsbuildinfo/examples.tsbuildinfo", "rootDir": "examples", diff --git a/packages/platform-browser/tsconfig.src.json b/packages/platform-browser/tsconfig.src.json index 9f8bba5ff1..3ee0560b5b 100644 --- a/packages/platform-browser/tsconfig.src.json +++ b/packages/platform-browser/tsconfig.src.json @@ -1,11 +1,7 @@ { "extends": "../../tsconfig.base.json", "include": ["src"], - "references": [ - { "path": "../platform" }, - { "path": "../effect" }, - { "path": "../schema" } - ], + "references": [{ "path": "../platform" }, { "path": "../effect" }], "compilerOptions": { "tsBuildInfoFile": ".tsbuildinfo/src.tsbuildinfo", "rootDir": "src", diff --git a/packages/platform-browser/tsconfig.test.json b/packages/platform-browser/tsconfig.test.json index 2cc45b5004..d18d744cb9 100644 --- a/packages/platform-browser/tsconfig.test.json +++ b/packages/platform-browser/tsconfig.test.json @@ -4,7 +4,6 @@ "references": [ { "path": "tsconfig.src.json" }, { "path": "../effect" }, - { "path": "../schema" }, { "path": "../vitest" } ], "compilerOptions": { diff --git a/packages/platform-bun/examples/http-client.ts b/packages/platform-bun/examples/http-client.ts index eeaab80b82..7c27e3e633 100644 --- a/packages/platform-bun/examples/http-client.ts +++ b/packages/platform-bun/examples/http-client.ts @@ -1,9 +1,9 @@ import { FetchHttpClient, HttpClient, HttpClientRequest, HttpClientResponse } from "@effect/platform" import type { HttpBody, HttpClientError } from "@effect/platform" import { BunRuntime } from "@effect/platform-bun" -import type * as ParseResult from "@effect/schema/ParseResult" -import * as Schema from "@effect/schema/Schema" import { Context, Effect, Layer } from "effect" +import type * as ParseResult from "effect/ParseResult" +import * as Schema from "effect/Schema" class Todo extends Schema.Class("Todo")({ userId: Schema.Number, diff --git a/packages/platform-bun/examples/http-router.ts b/packages/platform-bun/examples/http-router.ts index a72d892c56..ffc3636074 100644 --- a/packages/platform-bun/examples/http-router.ts +++ b/packages/platform-bun/examples/http-router.ts @@ -8,8 +8,7 @@ import { Multipart } from "@effect/platform" import { BunHttpServer, BunRuntime } from "@effect/platform-bun" -import { Schema } from "@effect/schema" -import { Effect, Layer, Schedule, Stream } from "effect" +import { Effect, Layer, Schedule, Schema, Stream } from "effect" const ServerLive = BunHttpServer.layer({ port: 3000 }) diff --git a/packages/platform-node-shared/tsconfig.build.json b/packages/platform-node-shared/tsconfig.build.json index 3daf2f819b..8326d06ca2 100644 --- a/packages/platform-node-shared/tsconfig.build.json +++ b/packages/platform-node-shared/tsconfig.build.json @@ -2,8 +2,7 @@ "extends": "./tsconfig.src.json", "references": [ { "path": "../platform/tsconfig.build.json" }, - { "path": "../effect/tsconfig.build.json" }, - { "path": "../schema/tsconfig.build.json" } + { "path": "../effect/tsconfig.build.json" } ], "compilerOptions": { "tsBuildInfoFile": ".tsbuildinfo/build.tsbuildinfo", diff --git a/packages/platform-node-shared/tsconfig.src.json b/packages/platform-node-shared/tsconfig.src.json index 9f8bba5ff1..3ee0560b5b 100644 --- a/packages/platform-node-shared/tsconfig.src.json +++ b/packages/platform-node-shared/tsconfig.src.json @@ -1,11 +1,7 @@ { "extends": "../../tsconfig.base.json", "include": ["src"], - "references": [ - { "path": "../platform" }, - { "path": "../effect" }, - { "path": "../schema" } - ], + "references": [{ "path": "../platform" }, { "path": "../effect" }], "compilerOptions": { "tsBuildInfoFile": ".tsbuildinfo/src.tsbuildinfo", "rootDir": "src", diff --git a/packages/platform-node-shared/tsconfig.test.json b/packages/platform-node-shared/tsconfig.test.json index 83ffe0b19d..5a2ff4b794 100644 --- a/packages/platform-node-shared/tsconfig.test.json +++ b/packages/platform-node-shared/tsconfig.test.json @@ -4,8 +4,7 @@ "references": [ { "path": "tsconfig.src.json" }, { "path": "../platform" }, - { "path": "../effect" }, - { "path": "../schema" } + { "path": "../effect" } ], "compilerOptions": { "tsBuildInfoFile": ".tsbuildinfo/test.tsbuildinfo", diff --git a/packages/platform-node/examples/api.ts b/packages/platform-node/examples/api.ts index 31f9da08fd..a985c091e1 100644 --- a/packages/platform-node/examples/api.ts +++ b/packages/platform-node/examples/api.ts @@ -13,8 +13,7 @@ import { OpenApi } from "@effect/platform" import { NodeHttpServer, NodeRuntime } from "@effect/platform-node" -import { Schema } from "@effect/schema" -import { Context, Effect, Layer, Redacted } from "effect" +import { Context, Effect, Layer, Redacted, Schema } from "effect" import { createServer } from "node:http" class User extends Schema.Class("User")({ diff --git a/packages/platform-node/examples/http-client.ts b/packages/platform-node/examples/http-client.ts index abcaf80891..5c65d4503e 100644 --- a/packages/platform-node/examples/http-client.ts +++ b/packages/platform-node/examples/http-client.ts @@ -2,11 +2,11 @@ import type { HttpBody, HttpClientError } from "@effect/platform" import { HttpClient, HttpClientRequest, HttpClientResponse } from "@effect/platform" import { NodeHttpClient } from "@effect/platform-node" import { runMain } from "@effect/platform-node/NodeRuntime" -import type * as ParseResult from "@effect/schema/ParseResult" -import * as Schema from "@effect/schema/Schema" import * as Context from "effect/Context" import * as Effect from "effect/Effect" import * as Layer from "effect/Layer" +import type * as ParseResult from "effect/ParseResult" +import * as Schema from "effect/Schema" class Todo extends Schema.Class("Todo")({ userId: Schema.Number, diff --git a/packages/platform-node/examples/http-router.ts b/packages/platform-node/examples/http-router.ts index f656e19343..67c0ca34f9 100644 --- a/packages/platform-node/examples/http-router.ts +++ b/packages/platform-node/examples/http-router.ts @@ -7,8 +7,8 @@ import { Multipart } from "@effect/platform" import { NodeHttpServer, NodeRuntime } from "@effect/platform-node" -import * as Schema from "@effect/schema/Schema" import { Effect, Layer, Schedule, Stream } from "effect" +import * as Schema from "effect/Schema" import { createServer } from "node:http" const ServerLive = NodeHttpServer.layer(() => createServer(), { port: 3000 }) diff --git a/packages/platform-node/test/HttpApi.test.ts b/packages/platform-node/test/HttpApi.test.ts index a9a513ccea..8b3b6aaca4 100644 --- a/packages/platform-node/test/HttpApi.test.ts +++ b/packages/platform-node/test/HttpApi.test.ts @@ -15,9 +15,8 @@ import { OpenApi } from "@effect/platform" import { NodeHttpServer } from "@effect/platform-node" -import { Schema } from "@effect/schema" import { assert, describe, it } from "@effect/vitest" -import { Context, DateTime, Effect, Layer, Redacted, Ref, Struct } from "effect" +import { Context, DateTime, Effect, Layer, Redacted, Ref, Schema, Struct } from "effect" import OpenApiFixture from "./fixtures/openapi.json" describe("HttpApi", () => { diff --git a/packages/platform-node/tsconfig.build.json b/packages/platform-node/tsconfig.build.json index adf744ff0b..8c484b04df 100644 --- a/packages/platform-node/tsconfig.build.json +++ b/packages/platform-node/tsconfig.build.json @@ -3,8 +3,7 @@ "references": [ { "path": "../platform/tsconfig.build.json" }, { "path": "../platform-node-shared/tsconfig.build.json" }, - { "path": "../effect/tsconfig.build.json" }, - { "path": "../schema/tsconfig.build.json" } + { "path": "../effect/tsconfig.build.json" } ], "compilerOptions": { "tsBuildInfoFile": ".tsbuildinfo/build.tsbuildinfo", diff --git a/packages/platform-node/tsconfig.src.json b/packages/platform-node/tsconfig.src.json index 230a9a264f..cf9e9e7e52 100644 --- a/packages/platform-node/tsconfig.src.json +++ b/packages/platform-node/tsconfig.src.json @@ -4,8 +4,7 @@ "references": [ { "path": "../platform" }, { "path": "../platform-node-shared" }, - { "path": "../effect" }, - { "path": "../schema" } + { "path": "../effect" } ], "compilerOptions": { "tsBuildInfoFile": ".tsbuildinfo/src.tsbuildinfo", diff --git a/packages/platform-node/tsconfig.test.json b/packages/platform-node/tsconfig.test.json index 5eae292c72..40f0a8ec7d 100644 --- a/packages/platform-node/tsconfig.test.json +++ b/packages/platform-node/tsconfig.test.json @@ -6,7 +6,6 @@ { "path": "../platform" }, { "path": "../platform-node-shared" }, { "path": "../effect" }, - { "path": "../schema" }, { "path": "../vitest" } ], "compilerOptions": { diff --git a/packages/platform/src/HttpApiBuilder.ts b/packages/platform/src/HttpApiBuilder.ts index ca427949a8..79c45b0ac2 100644 --- a/packages/platform/src/HttpApiBuilder.ts +++ b/packages/platform/src/HttpApiBuilder.ts @@ -690,8 +690,7 @@ export const securitySetCookie = ( * @category middleware * @example * import { HttpApiBuilder, HttpApiSecurity } from "@effect/platform" - * import { Schema } from "@effect/schema" - * import { Context, Effect, Redacted } from "effect" + * import { Context, Effect, Redacted, Schema } from "effect" * * class User extends Schema.Class("User")({ * id: Schema.Number diff --git a/packages/platform/test/Transferable.test.ts b/packages/platform/test/Transferable.test.ts index b2e0246ff7..f0bd531736 100644 --- a/packages/platform/test/Transferable.test.ts +++ b/packages/platform/test/Transferable.test.ts @@ -1,6 +1,5 @@ import * as Transferable from "@effect/platform/Transferable" -import { Schema } from "@effect/schema" -import { Effect } from "effect" +import { Effect, Schema } from "effect" import { assert, describe, test } from "vitest" describe("Transferable", () => { diff --git a/packages/platform/tsconfig.build.json b/packages/platform/tsconfig.build.json index 17ad78f02e..058393b854 100644 --- a/packages/platform/tsconfig.build.json +++ b/packages/platform/tsconfig.build.json @@ -1,9 +1,6 @@ { "extends": "./tsconfig.src.json", - "references": [ - { "path": "../effect/tsconfig.build.json" }, - { "path": "../schema/tsconfig.build.json" } - ], + "references": [{ "path": "../effect/tsconfig.build.json" }], "compilerOptions": { "tsBuildInfoFile": ".tsbuildinfo/build.tsbuildinfo", "outDir": "build/esm", diff --git a/packages/platform/tsconfig.src.json b/packages/platform/tsconfig.src.json index 8f1a1eb77f..34700059ad 100644 --- a/packages/platform/tsconfig.src.json +++ b/packages/platform/tsconfig.src.json @@ -1,10 +1,7 @@ { "extends": "../../tsconfig.base.json", "include": ["src"], - "references": [ - { "path": "../effect" }, - { "path": "../schema" } - ], + "references": [{ "path": "../effect" }], "compilerOptions": { "tsBuildInfoFile": ".tsbuildinfo/src.tsbuildinfo", "rootDir": "src", diff --git a/packages/platform/tsconfig.test.json b/packages/platform/tsconfig.test.json index 9c3d8ff20c..3724ddc14c 100644 --- a/packages/platform/tsconfig.test.json +++ b/packages/platform/tsconfig.test.json @@ -4,7 +4,6 @@ "references": [ { "path": "tsconfig.src.json" }, { "path": "../effect" }, - { "path": "../schema" }, { "path": "../vitest" } ], "compilerOptions": { diff --git a/packages/rpc-http/examples/schema.ts b/packages/rpc-http/examples/schema.ts index 246817bb79..a454ae6b77 100644 --- a/packages/rpc-http/examples/schema.ts +++ b/packages/rpc-http/examples/schema.ts @@ -1,6 +1,6 @@ import * as Rpc from "@effect/rpc/Rpc" -import * as S from "@effect/schema/Schema" import { pipe } from "effect/Function" +import * as S from "effect/Schema" export const UserId = pipe(S.Number, S.int(), S.brand("UserId")) export type UserId = S.Schema.Type diff --git a/packages/rpc-http/tsconfig.build.json b/packages/rpc-http/tsconfig.build.json index 0042d5c8c0..e32ea6e718 100644 --- a/packages/rpc-http/tsconfig.build.json +++ b/packages/rpc-http/tsconfig.build.json @@ -2,7 +2,6 @@ "extends": "./tsconfig.src.json", "references": [ { "path": "../effect/tsconfig.build.json" }, - { "path": "../schema/tsconfig.build.json" }, { "path": "../rpc/tsconfig.build.json" }, { "path": "../platform/tsconfig.build.json" } ], diff --git a/packages/rpc-http/tsconfig.examples.json b/packages/rpc-http/tsconfig.examples.json index 55e0c61ba2..149a8d1ae5 100644 --- a/packages/rpc-http/tsconfig.examples.json +++ b/packages/rpc-http/tsconfig.examples.json @@ -4,7 +4,6 @@ "references": [ { "path": "tsconfig.src.json" }, { "path": "../effect" }, - { "path": "../schema" }, { "path": "../rpc" }, { "path": "../platform" }, { "path": "../platform-node" } diff --git a/packages/rpc-http/tsconfig.src.json b/packages/rpc-http/tsconfig.src.json index bde274fd69..bfec71fa85 100644 --- a/packages/rpc-http/tsconfig.src.json +++ b/packages/rpc-http/tsconfig.src.json @@ -3,7 +3,6 @@ "include": ["src"], "references": [ { "path": "../effect" }, - { "path": "../schema" }, { "path": "../rpc" }, { "path": "../platform" } ], diff --git a/packages/rpc-http/tsconfig.test.json b/packages/rpc-http/tsconfig.test.json index 4616200ac8..8ad3bae32d 100644 --- a/packages/rpc-http/tsconfig.test.json +++ b/packages/rpc-http/tsconfig.test.json @@ -4,7 +4,6 @@ "references": [ { "path": "tsconfig.src.json" }, { "path": "../effect" }, - { "path": "../schema" }, { "path": "../rpc" }, { "path": "../platform" }, { "path": "../platform-node" } diff --git a/packages/rpc/test/Router.test.ts b/packages/rpc/test/Router.test.ts index 01857a1fcb..efd3dde82f 100644 --- a/packages/rpc/test/Router.test.ts +++ b/packages/rpc/test/Router.test.ts @@ -1,12 +1,11 @@ import { RpcResolver, RpcResolverNoStream, RpcRouter } from "@effect/rpc" import * as Rpc from "@effect/rpc/Rpc" -import { Schema } from "@effect/schema" import * as Array from "effect/Array" import * as Chunk from "effect/Chunk" import * as Context from "effect/Context" import * as Effect from "effect/Effect" import { flow, pipe } from "effect/Function" -import * as S from "effect/Schema" +import * as Schema from "effect/Schema" import * as Stream from "effect/Stream" import { assert, describe, expect, it, test } from "vitest" @@ -15,20 +14,20 @@ interface Name { } const Name = Context.GenericTag("Name") -class SomeError extends S.TaggedError()("SomeError", { - message: S.String +class SomeError extends Schema.TaggedError()("SomeError", { + message: Schema.String }) {} -class Post extends S.Class("Post")({ - id: S.Number, - body: S.String +class Post extends Schema.Class("Post")({ + id: Schema.Number, + body: Schema.String }) {} -class CreatePost extends S.TaggedRequest()("CreatePost", { - failure: S.Never, +class CreatePost extends Schema.TaggedRequest()("CreatePost", { + failure: Schema.Never, success: Post, payload: { - body: S.String + body: Schema.String } }) {} @@ -36,68 +35,72 @@ const posts = RpcRouter.make( Rpc.effect(CreatePost, ({ body }) => Effect.succeed(new Post({ id: 1, body }))) ) -class Greet extends S.TaggedRequest()("Greet", { - failure: S.Never, - success: S.String, +class Greet extends Schema.TaggedRequest()("Greet", { + failure: Schema.Never, + success: Schema.String, payload: { - name: S.String + name: Schema.String } }) {} -class Fail extends S.TaggedRequest()("Fail", { +class Fail extends Schema.TaggedRequest()("Fail", { failure: SomeError, - success: S.Void, + success: Schema.Void, payload: { - name: S.String + name: Schema.String } }) {} class FailNoInput - extends S.TaggedRequest()("FailNoInput", { failure: SomeError, success: S.Void, payload: {} }) + extends Schema.TaggedRequest()("FailNoInput", { failure: SomeError, success: Schema.Void, payload: {} }) {} -class EncodeInput extends S.TaggedRequest()("EncodeInput", { - failure: S.Never, - success: S.Date, +class EncodeInput extends Schema.TaggedRequest()("EncodeInput", { + failure: Schema.Never, + success: Schema.Date, payload: { - date: S.Date + date: Schema.Date } }) {} -class EncodeDate extends S.TaggedRequest()("EncodeDate", { +class EncodeDate extends Schema.TaggedRequest()("EncodeDate", { failure: SomeError, - success: S.Date, + success: Schema.Date, payload: { - date: S.String + date: Schema.String } }) {} -class Refined extends S.TaggedRequest()("Refined", { - failure: S.Never, - success: S.Number, +class Refined extends Schema.TaggedRequest()("Refined", { + failure: Schema.Never, + success: Schema.Number, payload: { - number: pipe(S.Number, S.int(), S.greaterThan(10)) + number: pipe(Schema.Number, Schema.int(), Schema.greaterThan(10)) } }) {} -class SpanName extends S.TaggedRequest()("SpanName", { failure: S.Never, success: S.String, payload: {} }) {} +class SpanName + extends Schema.TaggedRequest()("SpanName", { failure: Schema.Never, success: Schema.String, payload: {} }) +{} -class GetName extends S.TaggedRequest()("GetName", { failure: S.Never, success: S.String, payload: {} }) {} +class GetName + extends Schema.TaggedRequest()("GetName", { failure: Schema.Never, success: Schema.String, payload: {} }) +{} -class EchoHeaders extends S.TaggedRequest()("EchoHeaders", { - failure: S.Never, - success: S.Record({ key: S.String, value: S.Union(S.String, S.Undefined) }), +class EchoHeaders extends Schema.TaggedRequest()("EchoHeaders", { + failure: Schema.Never, + success: Schema.Record({ key: Schema.String, value: Schema.Union(Schema.String, Schema.Undefined) }), payload: {} }) {} class Counts extends Rpc.StreamRequest()( "Counts", - { failure: S.Never, success: S.Number, payload: {} } + { failure: Schema.Never, success: Schema.Number, payload: {} } ) {} class FailStream extends Rpc.StreamRequest()( "FailStream", - { failure: SomeError, success: S.Number, payload: {} } + { failure: SomeError, success: Schema.Number, payload: {} } ) {} const router = RpcRouter.make( @@ -126,7 +129,7 @@ const router = RpcRouter.make( Stream.tap((_) => Effect.sleep(10)) )), Rpc.effect(EchoHeaders, () => - Rpc.schemaHeaders(S.Struct({ + Rpc.schemaHeaders(Schema.Struct({ foo: Schema.String, baz: Schema.optional(Schema.String) })).pipe(Effect.orDie)), @@ -153,7 +156,7 @@ const handlerArray = (u: ReadonlyArray) => Effect.map(flow( Array.fromIterable, Array.map(([, response]) => response), - Array.filter((_): _ is S.ExitEncoded => Array.isArray(_) === false) + Array.filter((_): _ is Schema.ExitEncoded => Array.isArray(_) === false) )) ) const handlerEffectArray = (u: ReadonlyArray) => diff --git a/packages/rpc/tsconfig.build.json b/packages/rpc/tsconfig.build.json index 94e5fb0383..472aa658e6 100644 --- a/packages/rpc/tsconfig.build.json +++ b/packages/rpc/tsconfig.build.json @@ -2,8 +2,7 @@ "extends": "./tsconfig.src.json", "references": [ { "path": "../effect/tsconfig.build.json" }, - { "path": "../platform/tsconfig.build.json" }, - { "path": "../schema/tsconfig.build.json" } + { "path": "../platform/tsconfig.build.json" } ], "compilerOptions": { "tsBuildInfoFile": ".tsbuildinfo/build.tsbuildinfo", diff --git a/packages/rpc/tsconfig.examples.json b/packages/rpc/tsconfig.examples.json index 718637d3f2..b52b121594 100644 --- a/packages/rpc/tsconfig.examples.json +++ b/packages/rpc/tsconfig.examples.json @@ -4,8 +4,7 @@ "references": [ { "path": "tsconfig.src.json" }, { "path": "../effect" }, - { "path": "../platform" }, - { "path": "../schema" } + { "path": "../platform" } ], "compilerOptions": { "tsBuildInfoFile": ".tsbuildinfo/examples.tsbuildinfo", diff --git a/packages/rpc/tsconfig.src.json b/packages/rpc/tsconfig.src.json index fe0ac8a681..a1ad892129 100644 --- a/packages/rpc/tsconfig.src.json +++ b/packages/rpc/tsconfig.src.json @@ -1,11 +1,7 @@ { "extends": "../../tsconfig.base.json", "include": ["src"], - "references": [ - { "path": "../effect" }, - { "path": "../platform" }, - { "path": "../schema" } - ], + "references": [{ "path": "../effect" }, { "path": "../platform" }], "compilerOptions": { "tsBuildInfoFile": ".tsbuildinfo/src.tsbuildinfo", "rootDir": "src", diff --git a/packages/rpc/tsconfig.test.json b/packages/rpc/tsconfig.test.json index 380524224f..8939416ac7 100644 --- a/packages/rpc/tsconfig.test.json +++ b/packages/rpc/tsconfig.test.json @@ -4,8 +4,7 @@ "references": [ { "path": "tsconfig.src.json" }, { "path": "../effect" }, - { "path": "../platform" }, - { "path": "../schema" } + { "path": "../platform" } ], "compilerOptions": { "tsBuildInfoFile": ".tsbuildinfo/test.tsbuildinfo", diff --git a/packages/schema/LICENSE b/packages/schema/LICENSE deleted file mode 100644 index be1f5c14c7..0000000000 --- a/packages/schema/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2023 Effectful Technologies Inc - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/packages/schema/README.md b/packages/schema/README.md deleted file mode 100644 index 232abe3184..0000000000 --- a/packages/schema/README.md +++ /dev/null @@ -1,271 +0,0 @@ -# Introduction - -Welcome to the documentation for `@effect/schema`, **a library for defining and using schemas** to validate and transform data in TypeScript. - -`@effect/schema` allows you to define a `Schema` that provides a blueprint for describing the structure and data types of your data. Once defined, you can leverage this schema to perform a range of operations, including: - -| Operation | Description | -| --------------- | -------------------------------------------------------------------------------------------------------------- | -| Decoding | Transforming data from an input type `Encoded` to an output type `Type`. | -| Encoding | Converting data from an output type `Type` back to an input type `Encoded`. | -| Asserting | Verifying that a value adheres to the schema's output type `Type`. | -| Arbitraries | Generate arbitraries for [fast-check](https://github.com/dubzzz/fast-check) testing. | -| JSON Schemas | Create JSON Schemas based on defined schemas. | -| Equivalence | Create [Equivalences](https://effect-ts.github.io/effect/schema/Equivalence.ts.html) based on defined schemas. | -| Pretty printing | Support pretty printing for data structures. | - -# Requirements - -- TypeScript **5.0** or newer -- The `strict` flag enabled in your `tsconfig.json` file -- The `exactOptionalPropertyTypes` flag enabled in your `tsconfig.json` file - ```json - { - // ... - "compilerOptions": { - // ... - "strict": true, - "exactOptionalPropertyTypes": true - } - } - ``` -- Additionally, make sure to install the following packages, as they are peer dependencies. Note that some package managers might not install peer dependencies by default, so you need to install them manually: - - `effect` package (peer dependency) - - [fast-check](https://github.com/dubzzz/fast-check) package (peer dependency) - -# Documentation - -- [Website Docs](https://effect.website/docs/guides/schema) -- [API Reference](https://effect-ts.github.io/effect/docs/schema) -- Comparisons - - [zod (v3)](https://github.com/Effect-TS/effect/blob/main/packages/schema/comparisons.md#zod-v3) - -# Getting started - -To install the **beta** version: - -```bash -npm install @effect/schema -``` - -Additionally, make sure to install the `effect` package, as it's peer dependencies. Note that some package managers might not install peer dependencies by default, so you need to install them manually. - -Once you have installed the library, you can import the necessary types and functions from the `@effect/schema/Schema` module. - -**Example** (Namespace Import) - -```ts -import * as Schema from "@effect/schema/Schema" -``` - -**Example** (Named Import) - -```ts -import { Schema } from "@effect/schema" -``` - -# Technical overview - -A schema is a description of a data structure that can be used to generate various artifacts from a single declaration. - -From a technical point of view a schema is just a typed wrapper of an `AST` value: - -```ts -interface Schema { - readonly ast: AST -} -``` - -The `AST` type represents a tiny portion of the TypeScript AST, roughly speaking the part describing ADTs (algebraic data types), -i.e. products (like structs and tuples) and unions, plus a custom transformation node. - -This means that you can define your own schema constructors / combinators as long as you are able to manipulate the `AST` value accordingly, let's see an example. - -Say we want to define a `pair` schema constructor, which takes a `Schema` as input and returns a `Schema` as output. - -First of all we need to define the signature of `pair` - -```ts -import type { Schema } from "@effect/schema" - -declare const pair: ( - schema: Schema.Schema -) => Schema.Schema -``` - -Then we can implement the body using the APIs exported by the `@effect/schema/AST` module: - -```ts -import { AST, Schema } from "@effect/schema" - -const pair = ( - schema: Schema.Schema -): Schema.Schema => { - const element = new AST.OptionalType( - schema.ast, // <= the element type - false // <= is optional? - ) - const tuple = new AST.TupleType( - [element, element], // <= elements definitions - [], // <= rest element - true // <= is readonly? - ) - return Schema.make(tuple) // <= wrap the AST value in a Schema -} -``` - -This example demonstrates the use of the low-level APIs of the `AST` module, however, the same result can be achieved more easily and conveniently by using the high-level APIs provided by the `Schema` module. - -```ts -import { Schema } from "@effect/schema" - -const pair = ( - schema: Schema.Schema -): Schema.Schema => - Schema.Tuple(schema, schema) -``` - -# FAQ - -## Is it Possible to Extend Functionality Beyond Built-in APIs? - -If your needs aren't addressed by the existing built-in APIs, you have the option to craft your own API using the built-in APIs as a foundation. If these still don't suffice, you can delve into the lower-level APIs provided by the `@effect/schema/AST` module. - -To develop a robust custom API, you need to address two primary challenges: - -1. **Type-level challenge**: Can you define the TypeScript signature for your API? -2. **Runtime-level challenge**: Can you implement your API at runtime using either the `Schema` or `AST` module APIs? - -Let's explore a practical example: "Is it possible to make all fields of a struct nullable?" - -**Defining the API Signature in TypeScript** - -First, let's determine if we can define the API's TypeScript signature: - -```ts -import { Schema } from "@effect/schema" - -declare const nullableFields: < - Fields extends { readonly [x: string]: Schema.Schema.Any } ->( - schema: Schema.Struct -) => Schema.Struct<{ [K in keyof Fields]: Schema.NullOr }> - -// Example use - -/* -const schema: Schema.Struct<{ - name: Schema.NullOr; - age: Schema.NullOr; -}> -*/ -const schema = nullableFields( - Schema.Struct({ - name: Schema.String, - age: Schema.Number - }) -) -``` - -You can preliminarily define the signature of `nullableFields` using TypeScript's `declare` keyword, allowing you to immediately test its validity (at the type-level, initially). The example above confirms that the API behaves as expected by inspecting a schema that utilizes this new API. - -```ts -const schema: Schema.Struct<{ - name: Schema.NullOr - age: Schema.NullOr -}> -``` - -**Implementing the API at Runtime** - -```ts -import { Schema } from "@effect/schema" -import { Record } from "effect" - -const nullableFields = < - Fields extends { readonly [x: string]: Schema.Schema.Any } ->( - schema: Schema.Struct -): Schema.Struct<{ [K in keyof Fields]: Schema.NullOr }> => { - return Schema.Struct( - Record.map(schema.fields, (schema) => Schema.NullOr(schema)) as any as { - [K in keyof Fields]: Schema.NullOr - } - ) -} - -const schema = nullableFields( - Schema.Struct({ - name: Schema.String, - age: Schema.Number - }) -) - -console.log(Schema.decodeUnknownSync(schema)({ name: "a", age: null })) -/* -Output: -{ name: 'a', age: null } -*/ -``` - -# Credits - -This library was inspired by the following projects: - -- [io-ts](https://github.com/gcanti/io-ts) -- [zod](https://github.com/colinhacks/zod) -- [zio-schema](https://github.com/zio/zio-schema) - -# License - -By contributing to this project, you agree that your contributions will be licensed under the project's [MIT License](./LICENSE). - -# Contributing Guidelines - -Thank you for considering contributing to our project! Here are some guidelines to help you get started: - -## Reporting Bugs - -If you have found a bug, please open an issue on our [issue tracker](https://github.com/Effect-TS/effect/issues) and provide as much detail as possible. This should include: - -- A clear and concise description of the problem -- Steps to reproduce the problem -- The expected behavior -- The actual behavior -- Any relevant error messages or logs - -## Suggesting Enhancements - -If you have an idea for an enhancement or a new feature, please open an issue on our [issue tracker](https://github.com/Effect-TS/effect/issues) and provide as much detail as possible. This should include: - -- A clear and concise description of the enhancement or feature -- Any potential benefits or use cases -- Any potential drawbacks or trade-offs - -## Pull Requests - -We welcome contributions via pull requests! Here are some guidelines to help you get started: - -1. Fork the repository and clone it to your local machine. -2. Create a new branch for your changes: `git checkout -b my-new-feature` -3. Ensure you have the required dependencies installed by running: `pnpm install` (assuming pnpm version `8.x`). -4. Make your desired changes and, if applicable, include tests to validate your modifications. -5. Run the following commands to ensure the integrity of your changes: - - `pnpm check`: Verify that the code compiles. - - `pnpm test`: Execute the tests. - - `pnpm circular`: Confirm there are no circular imports. - - `pnpm lint`: Check for code style adherence (if you happen to encounter any errors during this process, you can add the `--fix` option to automatically fix some of these style issues). - - `pnpm dtslint`: Run type-level tests. - - `pnpm docgen`: Update the automatically generated documentation. -6. Create a changeset for your changes: before committing your changes, create a changeset to document the modifications. This helps in tracking and communicating the changes effectively. To create a changeset, run the following command: `pnpm changeset`. -7. Commit your changes: after creating the changeset, commit your changes with a descriptive commit message: `git commit -am 'Add some feature'`. -8. Push your changes to your fork: `git push origin my-new-feature`. -9. Open a pull request against our `main` branch. - -### Pull Request Guidelines - -- Please make sure your changes are consistent with the project's existing style and conventions. -- Please write clear commit messages and include a summary of your changes in the pull request description. -- Please make sure all tests pass and add new tests as necessary. -- If your change requires documentation, please update the relevant documentation. -- Please be patient! We will do our best to review your pull request as soon as possible. diff --git a/packages/schema/docgen.json b/packages/schema/docgen.json deleted file mode 100644 index af59e3a42f..0000000000 --- a/packages/schema/docgen.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "$schema": "../../node_modules/@effect/docgen/schema.json", - "exclude": [ - "src/internal/**/*.ts" - ], - "examplesCompilerOptions": { - "noEmit": true, - "strict": true, - "skipLibCheck": true, - "moduleResolution": "Bundler", - "module": "ES2022", - "target": "ES2022", - "lib": [ - "ES2022", - "DOM" - ], - "paths": { - "effect": [ - "../../../effect/src/index.js" - ], - "effect/*": [ - "../../../effect/src/*.js" - ] - } - } -} diff --git a/packages/schema/package.json b/packages/schema/package.json deleted file mode 100644 index d8fd6b390f..0000000000 --- a/packages/schema/package.json +++ /dev/null @@ -1,56 +0,0 @@ -{ - "name": "@effect/schema", - "version": "0.75.3", - "type": "module", - "license": "MIT", - "description": "Modeling the schema of data structures as first-class values", - "homepage": "https://effect.website", - "repository": { - "type": "git", - "url": "https://github.com/Effect-TS/effect.git", - "directory": "packages/schema" - }, - "bugs": { - "url": "https://github.com/Effect-TS/effect/issues" - }, - "tags": [ - "typescript", - "algebraic-data-types", - "functional-programming", - "validation", - "schema" - ], - "keywords": [ - "typescript", - "algebraic-data-types", - "functional-programming", - "validation", - "schema" - ], - "publishConfig": { - "access": "public", - "directory": "dist", - "provenance": true - }, - "scripts": { - "codegen": "build-utils prepare-v2", - "build": "pnpm build-esm && pnpm build-annotate && pnpm build-cjs && build-utils pack-v2", - "build-esm": "tsc -b tsconfig.build.json", - "build-cjs": "babel build/esm --plugins @babel/transform-export-namespace-from --plugins @babel/transform-modules-commonjs --out-dir build/cjs --source-maps", - "build-annotate": "babel build/esm --plugins annotate-pure-calls --out-dir build/esm --source-maps", - "check": "tsc -b tsconfig.json", - "test": "vitest", - "coverage": "vitest --coverage", - "circular": "madge --extensions ts --circular --no-color --no-spinner src" - }, - "dependencies": { - "fast-check": "^3.21.0" - }, - "peerDependencies": { - "effect": "workspace:^" - }, - "devDependencies": { - "effect": "workspace:^", - "fast-check": "workspace:^" - } -} diff --git a/packages/schema/serializable.md b/packages/schema/serializable.md deleted file mode 100644 index b71af084f1..0000000000 --- a/packages/schema/serializable.md +++ /dev/null @@ -1,355 +0,0 @@ -# Serializable - -The `Serializable` module enables objects to have self-contained schema(s) for serialization. This functionality is particularly beneficial in scenarios where objects need to be consistently serialized and deserialized across various runtime environments or sent over network communications. - -## Serializable trait - -The `Serializable` trait equips objects with the capability to define their serialization logic explicitly. - -```ts -interface Serializable { - readonly [symbol]: Schema.Schema -} -``` - -**Example: Implementing the Serializable Trait** - -```ts -import { Schema, Serializable } from "@effect/schema" -import { Effect } from "effect" - -class Person { - constructor( - readonly id: number, - readonly name: string, - readonly createdAt: Date - ) {} - - // Define the schema for serialization - static FromEncoded = Schema.transform( - Schema.Struct({ - id: Schema.Number, - name: Schema.String, - createdAt: Schema.Date - }), - Schema.instanceOf(Person), - { - decode: ({ createdAt, id, name }) => new Person(id, name, createdAt), - encode: ({ createdAt, id, name }) => ({ id, name, createdAt }) - } - ) - - // Implementing the Serializable trait using the static schema - get [Serializable.symbol]() { - return Person.FromEncoded - } -} - -const person = new Person(1, "John", new Date(0)) - -// Example serialization -const serialized = Effect.runSync(Serializable.serialize(person)) -console.log(serialized) -// { id: 1, name: 'John', createdAt: '1970-01-01T00:00:00.000Z' } - -// Deserialization -const deserialized = Schema.decodeUnknownSync(Person.FromEncoded)(serialized) -console.log(deserialized) -// Person { id: 1, name: 'John', createdAt: 1970-01-01T00:00:00.000Z } - -// Deserialization using an instance: -// if you have access to a Person instance you can use `Serializable.deserialize` to deserialize -const deserializedUsingAnInstance = Effect.runSync( - Serializable.deserialize(person, serialized) -) -console.log(deserializedUsingAnInstance) -// Person { id: 1, name: 'John', createdAt: 1970-01-01T00:00:00.000Z } -``` - -## Streamlining Code with Schema.Class - -While the above example provides a comprehensive view of serialization processes, using the `Schema.Class` API can significantly reduce boilerplate and simplify class modeling. - -```ts -import { Schema, Serializable } from "@effect/schema" -import { Effect } from "effect" - -class Person extends Schema.Class("Person")({ - id: Schema.Number, - name: Schema.String, - createdAt: Schema.Date -}) { - get [Serializable.symbol]() { - return Person - } -} - -const person = new Person({ id: 1, name: "John", createdAt: new Date(0) }) - -const serialized = Effect.runSync(Serializable.serialize(person)) -console.log(serialized) -// { id: 1, name: 'John', createdAt: '1970-01-01T00:00:00.000Z' } - -const deserialized = Schema.decodeUnknownSync(Person)(serialized) -console.log(deserialized) -// Person { id: 1, name: 'John', createdAt: 1970-01-01T00:00:00.000Z } - -const deserializedUsingAnInstance = Effect.runSync( - Serializable.deserialize(person, serialized) -) -console.log(deserializedUsingAnInstance) -// Person { id: 1, name: 'John', createdAt: 1970-01-01T00:00:00.000Z } -``` - -## WithResult trait - -The `WithResult` trait is designed to encapsulate the outcome of an operation, distinguishing between success and failure cases. Each case is associated with a schema that defines the structure and types of the success or failure data. - -```ts -interface WithResult< - Success, - SuccessEncoded, - Failure, - FailureEncoded, - ResultR -> { - readonly [symbolResult]: { - readonly success: Schema.Schema - readonly failure: Schema.Schema - } -} -``` - -## SerializableWithResult trait - -The `SerializableWithResult` trait is specifically designed to model remote procedures that require serialization of their input and output, managing both successful and failed outcomes. - -This trait combines functionality from both the `Serializable` and `WithResult` traits to handle data serialization and the bifurcation of operation results into success or failure categories. - -**Definition** - -```ts -interface SerializableWithResult< - A, - I, - R, - Success, - SuccessEncoded, - Failure, - FailureEncoded, - ResultR -> extends Serializable, - WithResult {} -``` - -**Components** - -- **Payload (`A, I, R`)**: The payload is described using the `Serializable` trait, which includes the type of the payload (`A`), its serialized form (`I`), and any relevant runtime context (`R`). -- **Success Case (`Success, SuccessEncoded, ResultR`)**: Defined by `Schema`, this outlines the structure and type of the data upon a successful operation, along with its serialized form. -- **Failure Case (`Failure, FailureEncoded, ResultR`)**: This is analogous to the Success Case but caters to scenarios where the operation fails. It is described by `Schema`. - -**Workflow** - -1. **Initialization**: Begin with data of type `A`. -2. **Serialization**: Convert this data into its serialized format `I`. -3. **Transmission**: Send this serialized data over the network. -4. **Reception and Deserialization**: Upon receiving, convert the data back from type `I` to `A`. -5. **Processing**: The deserialized data is then processed to determine the outcome as either success (`Success`) or failure (`Failure`). -6. **Result Serialization**: Depending on the outcome, serialize the result into `Exit`. -7. **Response Transmission**: Send the serialized outcome back over the network. -8. **Final Deserialization**: Deserialize the received outcome back into `Exit` for final use. - -```mermaid -sequenceDiagram - Sender->>SenderBound: encodes A to I - SenderBound-->>ReceiverBound: send I - ReceiverBound->>Receiver: decodes I to A - Receiver->>ReceiverBound: encodes Exit
    to Exit - ReceiverBound-->>SenderBound: send back
    Exit - SenderBound->>Sender: decodes Exit
    to Exit -``` - -**Example** - -```ts -import type { ParseResult } from "@effect/schema" -import { Schema, Serializable } from "@effect/schema" -import { Effect, Exit } from "effect" - -class Person extends Schema.Class("Person")({ - id: Schema.Number, - name: Schema.String, - createdAt: Schema.Date -}) { - get [Serializable.symbol]() { - return Person - } -} - -class GetPersonById { - constructor(readonly id: number) {} - - static FromEncoded = Schema.transform( - Schema.Number, - Schema.instanceOf(GetPersonById), - { - decode: (id) => new GetPersonById(id), - encode: ({ id }) => id - } - ) - - get [Serializable.symbol]() { - return GetPersonById.FromEncoded - } - - get [Serializable.symbolResult]() { - return { - success: Person, - failure: Schema.String - } - } -} - -function handleGetPersonById( - serializedReq: typeof GetPersonById.FromEncoded.Encoded -) { - return Effect.gen(function* () { - const req = yield* Schema.decodeUnknown(GetPersonById.FromEncoded)( - serializedReq - ) - return yield* Serializable.serializeExit( - req, - req.id === 0 - ? Exit.fail("User not found") - : Exit.succeed( - new Person({ id: req.id, name: "John", createdAt: new Date() }) - ) - ) - }) -} - -const roundtrip = ( - req: GetPersonById -): Effect.Effect, ParseResult.ParseError> => - Effect.gen(function* () { - const serializedReq = yield* Serializable.serialize(req) - const exit = yield* handleGetPersonById(serializedReq) - return yield* Serializable.deserializeExit(req, exit) - }) - -console.log(Effect.runSync(roundtrip(new GetPersonById(1)))) -/* -Output: -{ - _id: 'Exit', - _tag: 'Success', - value: Person { id: 1, name: 'John', createdAt: 2024-07-02T17:40:59.666Z } -} -*/ - -console.log(Effect.runSync(roundtrip(new GetPersonById(0)))) -/* -Output: -{ - _id: 'Exit', - _tag: 'Failure', - cause: { _id: 'Cause', _tag: 'Fail', failure: 'User not found' } -} -*/ -``` - -## Streamlining Code with Schema.TaggedRequest - -While the previous example effectively demonstrates the mechanisms involved, it does require a significant amount of boilerplate code. To streamline development, the `Schema.TaggedRequest` API is specifically designed to reduce complexity and increase readability. - -```ts -import type { ParseResult } from "@effect/schema" -import { Schema, Serializable } from "@effect/schema" -import { Effect, Exit } from "effect" - -// Define a simple person class using Schema.Class for ease of serialization -class Person extends Schema.Class("Person")({ - id: Schema.Number, - name: Schema.String, - createdAt: Schema.Date -}) {} - -// Represents a serializable function: `(payload: { readonly id: number }) => Exit` -class GetPersonById extends Schema.TaggedRequest()( - "GetPersonById", - { - payload: { id: Schema.Number }, // Define the schema for the payload, - success: Person, // Schema for successful outcome - failure: Schema.String // Schema for failure outcome - } -) {} - -// Function to handle the GetPersonById request and process the response -function handleGetPersonById(serializedReq: typeof GetPersonById.Encoded) { - return Effect.gen(function* () { - const req = yield* Schema.decodeUnknown(GetPersonById)(serializedReq) - return yield* Serializable.serializeExit( - req, - req.id === 0 - ? Exit.fail("User not found") - : Exit.succeed( - new Person({ id: req.id, name: "John", createdAt: new Date() }) - ) - ) - }) -} - -// Simulates a roundtrip serialization and deserialization process -const roundtrip = ( - req: GetPersonById -): Effect.Effect, ParseResult.ParseError> => - Effect.gen(function* () { - const serializedReq = yield* Serializable.serialize(req) - const exit = yield* handleGetPersonById(serializedReq) - return yield* Serializable.deserializeExit(req, exit) - }) - -// Example outputs from invoking the roundtrip function -console.log(Effect.runSync(roundtrip(new GetPersonById({ id: 1 })))) -/* -Output: -{ - _id: 'Exit', - _tag: 'Success', - value: Person { id: 1, name: 'John', createdAt: 2024-07-02T17:40:59.666Z } -} -*/ - -console.log(Effect.runSync(roundtrip(new GetPersonById({ id: 0 })))) -/* -Output: -{ - _id: 'Exit', - _tag: 'Failure', - cause: { _id: 'Cause', _tag: 'Fail', failure: 'User not found' } -} -*/ -``` - -## Communication and Serialization with Schema and Serializable Traits - -This section outlines a streamlined client-server interaction using the `Serializable` and `WithResult` traits from the `@effect/schema` library to manage serialization and processing of data objects across network communications. - -**Client-Side Operations:** - -1. **Initialization**: Start with an object of type `A`, which implements `Serializable.SerializableWithResult`. -2. **Serialization**: Serialize the object `A` using `Serializable.serialize`, which employs the schema retrieved from the `Serializable` interface tied to `A`. -3. **Transmission**: Send the serialized data of type `I` to the server and wait for a response. - -**Server-Side Operations:** - -1. **Reception**: Receive the serialized data `I`. -2. **Deserialization**: Convert the serialized data `I` back into an object of type `A` using a predefined union schema `Schema
    `. -3. **Processing**: Handle the message of type `A` to derive an outcome as `Exit`. -4. **Result Serialization**: Serialize the result `Exit` to `Exit` utilizing the schema obtained from `A`'s `WithResult` interface. -5. **Response**: Send the serialized response `Exit` back to the client. - -**Client-Side Response Handling:** - -1. **Reception**: Receive the response `Exit`. -2. **Final Deserialization**: Convert `Exit` back to `Exit` using the original object `A` and the schema from the `WithResult` interface. diff --git a/packages/schema/src/AST.ts b/packages/schema/src/AST.ts deleted file mode 100644 index ee3b09d03d..0000000000 --- a/packages/schema/src/AST.ts +++ /dev/null @@ -1,9 +0,0 @@ -/** - * @since 0.67.0 - */ - -/** - * @category re-exports - * @since 0.76.0 - */ -export * from "effect/SchemaAST" diff --git a/packages/schema/src/Arbitrary.ts b/packages/schema/src/Arbitrary.ts deleted file mode 100644 index 58ec822031..0000000000 --- a/packages/schema/src/Arbitrary.ts +++ /dev/null @@ -1,9 +0,0 @@ -/** - * @since 0.67.0 - */ - -/** - * @category re-exports - * @since 0.76.0 - */ -export * from "effect/Arbitrary" diff --git a/packages/schema/src/FastCheck.ts b/packages/schema/src/FastCheck.ts deleted file mode 100644 index 42cd555e43..0000000000 --- a/packages/schema/src/FastCheck.ts +++ /dev/null @@ -1,9 +0,0 @@ -/** - * @since 0.67.0 - */ - -/** - * @category re-exports - * @since 0.76.0 - */ -export * from "effect/FastCheck" diff --git a/packages/schema/src/JSONSchema.ts b/packages/schema/src/JSONSchema.ts deleted file mode 100644 index cfcec21286..0000000000 --- a/packages/schema/src/JSONSchema.ts +++ /dev/null @@ -1,9 +0,0 @@ -/** - * @since 0.67.0 - */ - -/** - * @category re-exports - * @since 0.76.0 - */ -export * from "effect/JSONSchema" diff --git a/packages/schema/src/ParseResult.ts b/packages/schema/src/ParseResult.ts deleted file mode 100644 index ff365ff5dd..0000000000 --- a/packages/schema/src/ParseResult.ts +++ /dev/null @@ -1,9 +0,0 @@ -/** - * @since 0.67.0 - */ - -/** - * @category re-exports - * @since 0.76.0 - */ -export * from "effect/ParseResult" diff --git a/packages/schema/src/Pretty.ts b/packages/schema/src/Pretty.ts deleted file mode 100644 index 48af116876..0000000000 --- a/packages/schema/src/Pretty.ts +++ /dev/null @@ -1,9 +0,0 @@ -/** - * @since 0.67.0 - */ - -/** - * @category re-exports - * @since 0.76.0 - */ -export * from "effect/Pretty" diff --git a/packages/schema/src/Schema.ts b/packages/schema/src/Schema.ts deleted file mode 100644 index 6d539c56ea..0000000000 --- a/packages/schema/src/Schema.ts +++ /dev/null @@ -1,9 +0,0 @@ -/** - * @since 0.67.0 - */ - -/** - * @category re-exports - * @since 0.76.0 - */ -export * from "effect/Schema" diff --git a/packages/schema/src/index.ts b/packages/schema/src/index.ts deleted file mode 100644 index 53259a4d3c..0000000000 --- a/packages/schema/src/index.ts +++ /dev/null @@ -1,34 +0,0 @@ -/** - * @since 0.67.0 - */ -export * as AST from "./AST.js" - -/** - * @since 0.67.0 - */ -export * as Arbitrary from "./Arbitrary.js" - -/** - * @since 0.67.0 - */ -export * as FastCheck from "./FastCheck.js" - -/** - * @since 0.67.0 - */ -export * as JSONSchema from "./JSONSchema.js" - -/** - * @since 0.67.0 - */ -export * as ParseResult from "./ParseResult.js" - -/** - * @since 0.67.0 - */ -export * as Pretty from "./Pretty.js" - -/** - * @since 0.67.0 - */ -export * as Schema from "./Schema.js" diff --git a/packages/schema/test/index.test.ts b/packages/schema/test/index.test.ts deleted file mode 100644 index b58593296d..0000000000 --- a/packages/schema/test/index.test.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { expect, it } from "vitest" - -it("should work", () => { - expect(1).toBe(1) -}) diff --git a/packages/schema/tsconfig.build.json b/packages/schema/tsconfig.build.json deleted file mode 100644 index 76ee3883a1..0000000000 --- a/packages/schema/tsconfig.build.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "extends": "./tsconfig.src.json", - "references": [ - { "path": "../effect/tsconfig.build.json" } - ], - "compilerOptions": { - "tsBuildInfoFile": ".tsbuildinfo/build.tsbuildinfo", - "outDir": "build/esm", - "declarationDir": "build/dts", - "stripInternal": true - } -} diff --git a/packages/schema/tsconfig.json b/packages/schema/tsconfig.json deleted file mode 100644 index 2c291d2192..0000000000 --- a/packages/schema/tsconfig.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "extends": "../../tsconfig.base.json", - "include": [], - "references": [ - { "path": "tsconfig.src.json" }, - { "path": "tsconfig.test.json" } - ] -} diff --git a/packages/schema/tsconfig.src.json b/packages/schema/tsconfig.src.json deleted file mode 100644 index 272866fcc3..0000000000 --- a/packages/schema/tsconfig.src.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "extends": "../../tsconfig.base.json", - "include": ["src"], - "references": [ - { "path": "../effect" } - ], - "compilerOptions": { - "outDir": "build/src", - "tsBuildInfoFile": ".tsbuildinfo/src.tsbuildinfo", - "rootDir": "src" - } -} diff --git a/packages/schema/tsconfig.test.json b/packages/schema/tsconfig.test.json deleted file mode 100644 index d1c3c57a18..0000000000 --- a/packages/schema/tsconfig.test.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "extends": "../../tsconfig.base.json", - "include": ["test"], - "references": [ - { "path": "tsconfig.src.json" }, - { "path": "../effect" } - ], - "compilerOptions": { - "tsBuildInfoFile": ".tsbuildinfo/test.tsbuildinfo", - "rootDir": "test", - "noEmit": true - } -} diff --git a/packages/schema/vitest.config.ts b/packages/schema/vitest.config.ts deleted file mode 100644 index 0411095f25..0000000000 --- a/packages/schema/vitest.config.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { mergeConfig, type UserConfigExport } from "vitest/config" -import shared from "../../vitest.shared.js" - -const config: UserConfigExport = {} - -export default mergeConfig(shared, config) diff --git a/packages/sql-kysely/test/Sqlite.test.ts b/packages/sql-kysely/test/Sqlite.test.ts index 59eb3a311f..cad5d1ee75 100644 --- a/packages/sql-kysely/test/Sqlite.test.ts +++ b/packages/sql-kysely/test/Sqlite.test.ts @@ -1,9 +1,8 @@ -import { Schema } from "@effect/schema" import { SqlResolver } from "@effect/sql" import * as SqliteKysely from "@effect/sql-kysely/Sqlite" import * as Sqlite from "@effect/sql-sqlite-node" import { assert, describe, it } from "@effect/vitest" -import { Config, Context, Effect, Exit, Layer, Option } from "effect" +import { Config, Context, Effect, Exit, Layer, Option, Schema } from "effect" import type { Generated } from "kysely" export interface User { diff --git a/packages/sql-mysql2/test/Model.test.ts b/packages/sql-mysql2/test/Model.test.ts index 7328bfa9f1..82fe19bd8e 100644 --- a/packages/sql-mysql2/test/Model.test.ts +++ b/packages/sql-mysql2/test/Model.test.ts @@ -1,7 +1,6 @@ -import { Schema } from "@effect/schema" import { Model, SqlClient } from "@effect/sql" import { assert, describe, it } from "@effect/vitest" -import { Effect, Option } from "effect" +import { Effect, Option, Schema } from "effect" import { MysqlContainer } from "./utils.js" class User extends Model.Class("User")({ diff --git a/packages/sql-pg/examples/resolver.ts b/packages/sql-pg/examples/resolver.ts index 9bfe741777..a20ae9817b 100644 --- a/packages/sql-pg/examples/resolver.ts +++ b/packages/sql-pg/examples/resolver.ts @@ -1,8 +1,8 @@ import * as DevTools from "@effect/experimental/DevTools" -import * as Schema from "@effect/schema/Schema" import { SqlClient, SqlResolver } from "@effect/sql" import { PgClient } from "@effect/sql-pg" import { Config, Effect, Layer, String } from "effect" +import * as Schema from "effect/Schema" class Person extends Schema.Class("Person")({ id: Schema.Number, diff --git a/packages/sql/src/Model.ts b/packages/sql/src/Model.ts index d4a04cc162..5b4280a2c3 100644 --- a/packages/sql/src/Model.ts +++ b/packages/sql/src/Model.ts @@ -77,7 +77,7 @@ export { * @since 1.0.0 * @category constructors * @example - * import { Schema } from "@effect/schema" + * import { Schema } from "effect" * import { Model } from "@effect/sql" * * export const GroupId = Schema.Number.pipe(Schema.brand("GroupId")) diff --git a/packages/sql/tsconfig.build.json b/packages/sql/tsconfig.build.json index b0c5d088bf..6b9244b50c 100644 --- a/packages/sql/tsconfig.build.json +++ b/packages/sql/tsconfig.build.json @@ -3,8 +3,7 @@ "references": [ { "path": "../effect/tsconfig.build.json" }, { "path": "../experimental/tsconfig.build.json" }, - { "path": "../platform/tsconfig.build.json" }, - { "path": "../schema/tsconfig.build.json" } + { "path": "../platform/tsconfig.build.json" } ], "compilerOptions": { "tsBuildInfoFile": ".tsbuildinfo/build.tsbuildinfo", diff --git a/packages/sql/tsconfig.examples.json b/packages/sql/tsconfig.examples.json index 718637d3f2..b52b121594 100644 --- a/packages/sql/tsconfig.examples.json +++ b/packages/sql/tsconfig.examples.json @@ -4,8 +4,7 @@ "references": [ { "path": "tsconfig.src.json" }, { "path": "../effect" }, - { "path": "../platform" }, - { "path": "../schema" } + { "path": "../platform" } ], "compilerOptions": { "tsBuildInfoFile": ".tsbuildinfo/examples.tsbuildinfo", diff --git a/packages/sql/tsconfig.src.json b/packages/sql/tsconfig.src.json index 8ed66efe1b..098b4d92f3 100644 --- a/packages/sql/tsconfig.src.json +++ b/packages/sql/tsconfig.src.json @@ -4,8 +4,7 @@ "references": [ { "path": "../effect" }, { "path": "../experimental" }, - { "path": "../platform" }, - { "path": "../schema" } + { "path": "../platform" } ], "compilerOptions": { "tsBuildInfoFile": ".tsbuildinfo/src.tsbuildinfo", diff --git a/packages/sql/tsconfig.test.json b/packages/sql/tsconfig.test.json index 380524224f..8939416ac7 100644 --- a/packages/sql/tsconfig.test.json +++ b/packages/sql/tsconfig.test.json @@ -4,8 +4,7 @@ "references": [ { "path": "tsconfig.src.json" }, { "path": "../effect" }, - { "path": "../platform" }, - { "path": "../schema" } + { "path": "../platform" } ], "compilerOptions": { "tsBuildInfoFile": ".tsbuildinfo/test.tsbuildinfo", diff --git a/packages/schema/CHANGELOG.md b/schema/CHANGELOG.md similarity index 99% rename from packages/schema/CHANGELOG.md rename to schema/CHANGELOG.md index d3426bdb39..29f343fff0 100644 --- a/packages/schema/CHANGELOG.md +++ b/schema/CHANGELOG.md @@ -1,5 +1,12 @@ # @effect/schema +## 0.75.4 + +### Patch Changes + +- Updated dependencies [[`61a99b2`](https://github.com/Effect-TS/effect/commit/61a99b2bf9d757870ef0c2ec9d4c877cdd364a3d)]: + - effect@3.9.2 + ## 0.75.3 ### Patch Changes diff --git a/packages/schema/comparisons.md b/schema/comparisons.md similarity index 100% rename from packages/schema/comparisons.md rename to schema/comparisons.md diff --git a/tsconfig.base.json b/tsconfig.base.json index b37bbf066f..89625c460c 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -106,9 +106,6 @@ "@effect/rpc-http": ["./packages/rpc-http/src/index.js"], "@effect/rpc-http/*": ["./packages/rpc-http/src/*.js"], "@effect/rpc-http/test/*": ["./packages/rpc-http/test/*.js"], - "@effect/schema": ["./packages/schema/src/index.js"], - "@effect/schema/*": ["./packages/schema/src/*.js"], - "@effect/schema/test/*": ["./packages/schema/test/*.js"], "@effect/sql": ["./packages/sql/src/index.js"], "@effect/sql/*": ["./packages/sql/src/*.js"], "@effect/sql/test/*": ["./packages/sql/test/*.js"], diff --git a/tsconfig.build.json b/tsconfig.build.json index 4f24e72513..c9716dc78c 100644 --- a/tsconfig.build.json +++ b/tsconfig.build.json @@ -21,7 +21,6 @@ { "path": "packages/printer-ansi/tsconfig.build.json" }, { "path": "packages/rpc/tsconfig.build.json" }, { "path": "packages/rpc-http/tsconfig.build.json" }, - { "path": "packages/schema/tsconfig.build.json" }, { "path": "packages/sql/tsconfig.build.json" }, { "path": "packages/sql-d1/tsconfig.build.json" }, { "path": "packages/sql-drizzle/tsconfig.build.json" }, diff --git a/tsconfig.json b/tsconfig.json index 69390502fc..bd71cf8688 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -21,8 +21,6 @@ { "path": "packages/printer-ansi" }, { "path": "packages/rpc" }, { "path": "packages/rpc-http" }, - { "path": "packages/schema" }, - { "path": "packages/schema" }, { "path": "packages/sql" }, { "path": "packages/sql-d1" }, { "path": "packages/sql-drizzle" }, diff --git a/vitest.shared.ts b/vitest.shared.ts index 24234a2871..0e8a8a0a41 100644 --- a/vitest.shared.ts +++ b/vitest.shared.ts @@ -47,7 +47,6 @@ const config: UserConfig = { ...alias("printer-ansi"), ...alias("rpc"), ...alias("rpc-http"), - ...alias("schema"), ...alias("sql"), ...alias("sql-d1"), ...alias("sql-drizzle"), From a94383983fb58ed2bb9e1067806fb40ce2214d0d Mon Sep 17 00:00:00 2001 From: Tim Date: Wed, 16 Oct 2024 09:18:41 +1300 Subject: [PATCH 09/12] update package.json's --- packages/ai/ai/docgen.json | 48 ++++------------- packages/ai/ai/package.json | 2 - packages/ai/openai/package.json | 2 - packages/cli/package.json | 2 - packages/cluster-browser/package.json | 2 - packages/cluster-node/package.json | 2 - packages/cluster-workflow/package.json | 2 - packages/cluster/package.json | 2 - packages/experimental/package.json | 1 - packages/platform-browser/package.json | 1 - packages/platform-bun/package.json | 1 - packages/platform-node-shared/package.json | 1 - packages/platform-node/package.json | 1 - packages/platform/package.json | 2 - packages/rpc-http/package.json | 2 - packages/rpc/package.json | 2 - packages/sql/package.json | 2 - pnpm-lock.yaml | 60 ---------------------- 18 files changed, 10 insertions(+), 125 deletions(-) diff --git a/packages/ai/ai/docgen.json b/packages/ai/ai/docgen.json index a728e1818f..2525a36595 100644 --- a/packages/ai/ai/docgen.json +++ b/packages/ai/ai/docgen.json @@ -1,8 +1,6 @@ { "$schema": "../../../node_modules/@effect/docgen/schema.json", - "exclude": [ - "src/internal/**/*.ts" - ], + "exclude": ["src/internal/**/*.ts"], "examplesCompilerOptions": { "noEmit": true, "strict": true, @@ -10,42 +8,16 @@ "moduleResolution": "Bundler", "module": "ES2022", "target": "ES2022", - "lib": [ - "ES2022", - "DOM", - "DOM.Iterable" - ], + "lib": ["ES2022", "DOM", "DOM.Iterable"], "paths": { - "effect": [ - "../../../../effect/src/index.js" - ], - "effect/*": [ - "../../../../effect/src/*.js" - ], - "@effect/platform": [ - "../../../../platform/src/index.js" - ], - "@effect/platform/*": [ - "../../../../platform/src/*.js" - ], - "@effect/schema": [ - "../../../../schema/src/index.js" - ], - "@effect/schema/*": [ - "../../../../schema/src/*.js" - ], - "@effect/ai": [ - "../../../ai/src/index.js" - ], - "@effect/ai/*": [ - "../../../ai/src/*.js" - ], - "@effect/ai-openai": [ - "../../../ai-openai/src/index.js" - ], - "@effect/ai-openai/*": [ - "../../../ai-openai/src/*.js" - ] + "effect": ["../../../../effect/src/index.js"], + "effect/*": ["../../../../effect/src/*.js"], + "@effect/platform": ["../../../../platform/src/index.js"], + "@effect/platform/*": ["../../../../platform/src/*.js"], + "@effect/ai": ["../../../ai/src/index.js"], + "@effect/ai/*": ["../../../ai/src/*.js"], + "@effect/ai-openai": ["../../../ai-openai/src/index.js"], + "@effect/ai-openai/*": ["../../../ai-openai/src/*.js"] } } } diff --git a/packages/ai/ai/package.json b/packages/ai/ai/package.json index 7183ebb0a4..f0c01bc1cf 100644 --- a/packages/ai/ai/package.json +++ b/packages/ai/ai/package.json @@ -41,12 +41,10 @@ "dependencies": {}, "peerDependencies": { "@effect/platform": "workspace:^", - "@effect/schema": "workspace:^", "effect": "workspace:^" }, "devDependencies": { "@effect/platform": "workspace:^", - "@effect/schema": "workspace:^", "effect": "workspace:^" } } diff --git a/packages/ai/openai/package.json b/packages/ai/openai/package.json index 45f6f10f0a..347f970145 100644 --- a/packages/ai/openai/package.json +++ b/packages/ai/openai/package.json @@ -42,7 +42,6 @@ "@effect/ai": "workspace:^", "@effect/experimental": "workspace:^", "@effect/platform": "workspace:^", - "@effect/schema": "workspace:^", "effect": "workspace:^" }, "devDependencies": { @@ -50,7 +49,6 @@ "@effect/experimental": "workspace:^", "@effect/platform": "workspace:^", "@effect/platform-node": "workspace:^", - "@effect/schema": "workspace:^", "@tim-smart/openapi-gen": "^0.2.0", "effect": "workspace:^" }, diff --git a/packages/cli/package.json b/packages/cli/package.json index 17b35c4f76..e6cbee6e93 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -44,7 +44,6 @@ "@effect/platform": "workspace:^", "@effect/printer": "workspace:^", "@effect/printer-ansi": "workspace:^", - "@effect/schema": "workspace:^", "effect": "workspace:^" }, "devDependencies": { @@ -52,7 +51,6 @@ "@effect/platform-node": "workspace:^", "@effect/printer": "workspace:^", "@effect/printer-ansi": "workspace:^", - "@effect/schema": "workspace:^", "@types/ini": "^4.1.1", "@types/node": "^20.14.10", "effect": "workspace:^" diff --git a/packages/cluster-browser/package.json b/packages/cluster-browser/package.json index 5fa3bbea78..13472d63e7 100644 --- a/packages/cluster-browser/package.json +++ b/packages/cluster-browser/package.json @@ -27,12 +27,10 @@ }, "peerDependencies": { "@effect/rpc": "workspace:^", - "@effect/schema": "workspace:^", "effect": "workspace:^" }, "devDependencies": { "@effect/rpc": "workspace:^", - "@effect/schema": "workspace:^", "effect": "workspace:^" } } diff --git a/packages/cluster-node/package.json b/packages/cluster-node/package.json index 3f6bc05cef..497ef4d5ee 100644 --- a/packages/cluster-node/package.json +++ b/packages/cluster-node/package.json @@ -28,7 +28,6 @@ "peerDependencies": { "@effect/cluster": "workspace:^", "@effect/rpc": "workspace:^", - "@effect/schema": "workspace:^", "effect": "workspace:^" }, "devDependencies": { @@ -36,7 +35,6 @@ "@effect/platform-node": "workspace:^", "@effect/rpc": "workspace:^", "@effect/rpc-http": "workspace:^", - "@effect/schema": "workspace:^", "@types/node": "^20.14.10", "effect": "workspace:^" } diff --git a/packages/cluster-workflow/package.json b/packages/cluster-workflow/package.json index 7b057f8a3c..af988f1615 100644 --- a/packages/cluster-workflow/package.json +++ b/packages/cluster-workflow/package.json @@ -27,13 +27,11 @@ }, "peerDependencies": { "@effect/cluster": "workspace:^", - "@effect/schema": "workspace:^", "@effect/sql": "workspace:^", "effect": "workspace:^" }, "devDependencies": { "@effect/cluster": "workspace:^", - "@effect/schema": "workspace:^", "@effect/platform": "workspace:^", "@effect/platform-node": "workspace:^", "@effect/sql": "workspace:^", diff --git a/packages/cluster/package.json b/packages/cluster/package.json index 6cad3bc096..3f140cf6ac 100644 --- a/packages/cluster/package.json +++ b/packages/cluster/package.json @@ -26,14 +26,12 @@ "coverage": "vitest --coverage" }, "peerDependencies": { - "@effect/schema": "workspace:^", "@effect/sql": "workspace:^", "effect": "workspace:^" }, "devDependencies": { "@effect/platform": "workspace:^", "@effect/platform-node": "workspace:^", - "@effect/schema": "workspace:^", "@effect/sql": "workspace:^", "@effect/sql-sqlite-node": "workspace:^", "effect": "workspace:^" diff --git a/packages/experimental/package.json b/packages/experimental/package.json index 3864b539f4..078df23e5a 100644 --- a/packages/experimental/package.json +++ b/packages/experimental/package.json @@ -44,7 +44,6 @@ "peerDependencies": { "@effect/platform": "workspace:^", "@effect/platform-node": "workspace:^", - "@effect/schema": "workspace:^", "effect": "workspace:^", "ioredis": "^5", "lmdb": "^3", diff --git a/packages/platform-browser/package.json b/packages/platform-browser/package.json index 186ce11fab..8c5cd2346a 100644 --- a/packages/platform-browser/package.json +++ b/packages/platform-browser/package.json @@ -46,7 +46,6 @@ }, "devDependencies": { "@effect/platform": "workspace:^", - "@effect/schema": "workspace:^", "effect": "workspace:^", "happy-dom": "^14.12.3", "mock-xmlhttprequest": "^8.3.0" diff --git a/packages/platform-bun/package.json b/packages/platform-bun/package.json index 8c11f8981e..fb8c957fa6 100644 --- a/packages/platform-bun/package.json +++ b/packages/platform-bun/package.json @@ -49,7 +49,6 @@ }, "devDependencies": { "@effect/platform": "workspace:^", - "@effect/schema": "workspace:^", "bun-types": "1.1.22", "effect": "workspace:^" } diff --git a/packages/platform-node-shared/package.json b/packages/platform-node-shared/package.json index 34955ae5b2..cae068b11c 100644 --- a/packages/platform-node-shared/package.json +++ b/packages/platform-node-shared/package.json @@ -50,7 +50,6 @@ }, "devDependencies": { "@effect/platform": "workspace:^", - "@effect/schema": "workspace:^", "@types/node": "^20.14.10", "@types/tar": "^6.1.12", "effect": "workspace:^", diff --git a/packages/platform-node/package.json b/packages/platform-node/package.json index 42be3f6e3a..1759ff6004 100644 --- a/packages/platform-node/package.json +++ b/packages/platform-node/package.json @@ -55,7 +55,6 @@ }, "devDependencies": { "@effect/platform": "workspace:^", - "@effect/schema": "workspace:^", "@types/mime": "^3.0.4", "@types/node": "^20.14.10", "@types/ws": "^8.5.12", diff --git a/packages/platform/package.json b/packages/platform/package.json index 02c5c490f8..b454e55e61 100644 --- a/packages/platform/package.json +++ b/packages/platform/package.json @@ -44,11 +44,9 @@ "multipasta": "^0.2.5" }, "peerDependencies": { - "@effect/schema": "workspace:^", "effect": "workspace:^" }, "devDependencies": { - "@effect/schema": "workspace:^", "ajv": "^8.17.1", "effect": "workspace:^" } diff --git a/packages/rpc-http/package.json b/packages/rpc-http/package.json index e29669d12f..1e6215c334 100644 --- a/packages/rpc-http/package.json +++ b/packages/rpc-http/package.json @@ -44,12 +44,10 @@ "devDependencies": { "@effect/platform": "workspace:^", "@effect/platform-node": "workspace:^", - "@effect/schema": "workspace:^", "effect": "workspace:^" }, "peerDependencies": { "@effect/platform": "workspace:^", - "@effect/schema": "workspace:^", "effect": "workspace:^" } } diff --git a/packages/rpc/package.json b/packages/rpc/package.json index 1ab2ffe52c..75ea49210b 100644 --- a/packages/rpc/package.json +++ b/packages/rpc/package.json @@ -40,12 +40,10 @@ }, "devDependencies": { "@effect/platform": "workspace:^", - "@effect/schema": "workspace:^", "effect": "workspace:^" }, "peerDependencies": { "@effect/platform": "workspace:^", - "@effect/schema": "workspace:^", "effect": "workspace:^" } } diff --git a/packages/sql/package.json b/packages/sql/package.json index 58c31b7654..cbe2954680 100644 --- a/packages/sql/package.json +++ b/packages/sql/package.json @@ -46,13 +46,11 @@ "devDependencies": { "@effect/experimental": "workspace:^", "@effect/platform": "workspace:^", - "@effect/schema": "workspace:^", "effect": "workspace:^" }, "peerDependencies": { "@effect/experimental": "workspace:^", "@effect/platform": "workspace:^", - "@effect/schema": "workspace:^", "effect": "workspace:^" }, "effect": { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 316744737c..51025fd765 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -168,9 +168,6 @@ importers: '@effect/platform': specifier: workspace:^ version: link:../../platform/dist - '@effect/schema': - specifier: workspace:^ - version: link:../../schema/dist effect: specifier: workspace:^ version: link:../../effect/dist @@ -194,9 +191,6 @@ importers: '@effect/platform-node': specifier: workspace:^ version: link:../../platform-node/dist - '@effect/schema': - specifier: workspace:^ - version: link:../../schema/dist '@tim-smart/openapi-gen': specifier: ^0.2.0 version: 0.2.0 @@ -229,9 +223,6 @@ importers: '@effect/printer-ansi': specifier: workspace:^ version: link:../printer-ansi/dist - '@effect/schema': - specifier: workspace:^ - version: link:../schema/dist '@types/ini': specifier: ^4.1.1 version: 4.1.1 @@ -251,9 +242,6 @@ importers: '@effect/platform-node': specifier: workspace:^ version: link:../platform-node/dist - '@effect/schema': - specifier: workspace:^ - version: link:../schema/dist '@effect/sql': specifier: workspace:^ version: link:../sql/dist @@ -270,9 +258,6 @@ importers: '@effect/rpc': specifier: workspace:^ version: link:../rpc/dist - '@effect/schema': - specifier: workspace:^ - version: link:../schema/dist effect: specifier: workspace:^ version: link:../effect/dist @@ -296,9 +281,6 @@ importers: '@effect/rpc-http': specifier: workspace:^ version: link:../rpc-http/dist - '@effect/schema': - specifier: workspace:^ - version: link:../schema/dist '@types/node': specifier: ^22.5.4 version: 22.5.4 @@ -318,9 +300,6 @@ importers: '@effect/platform-node': specifier: workspace:^ version: link:../platform-node/dist - '@effect/schema': - specifier: workspace:^ - version: link:../schema/dist '@effect/sql': specifier: workspace:^ version: link:../sql/dist @@ -369,9 +348,6 @@ importers: '@effect/platform-node': specifier: workspace:^ version: link:../platform-node/dist - '@effect/schema': - specifier: workspace:^ - version: link:../schema/dist effect: specifier: workspace:^ version: link:../effect/dist @@ -449,9 +425,6 @@ importers: specifier: ^0.2.5 version: 0.2.5 devDependencies: - '@effect/schema': - specifier: workspace:^ - version: link:../schema/dist ajv: specifier: ^8.17.1 version: 8.17.1 @@ -469,9 +442,6 @@ importers: '@effect/platform': specifier: workspace:^ version: link:../platform/dist - '@effect/schema': - specifier: workspace:^ - version: link:../schema/dist effect: specifier: workspace:^ version: link:../effect/dist @@ -492,9 +462,6 @@ importers: '@effect/platform': specifier: workspace:^ version: link:../platform/dist - '@effect/schema': - specifier: workspace:^ - version: link:../schema/dist bun-types: specifier: 1.1.22 version: 1.1.22 @@ -521,9 +488,6 @@ importers: '@effect/platform': specifier: workspace:^ version: link:../platform/dist - '@effect/schema': - specifier: workspace:^ - version: link:../schema/dist '@types/mime': specifier: ^3.0.4 version: 3.0.4 @@ -550,9 +514,6 @@ importers: '@effect/platform': specifier: workspace:^ version: link:../platform/dist - '@effect/schema': - specifier: workspace:^ - version: link:../schema/dist '@types/node': specifier: ^22.5.4 version: 22.5.4 @@ -596,9 +557,6 @@ importers: '@effect/platform': specifier: workspace:^ version: link:../platform/dist - '@effect/schema': - specifier: workspace:^ - version: link:../schema/dist effect: specifier: workspace:^ version: link:../effect/dist @@ -616,20 +574,6 @@ importers: '@effect/platform-node': specifier: workspace:^ version: link:../platform-node/dist - '@effect/schema': - specifier: workspace:^ - version: link:../schema/dist - effect: - specifier: workspace:^ - version: link:../effect/dist - publishDirectory: dist - - packages/schema: - dependencies: - fast-check: - specifier: ^3.21.0 - version: 3.21.0 - devDependencies: effect: specifier: workspace:^ version: link:../effect/dist @@ -647,9 +591,6 @@ importers: '@effect/platform': specifier: workspace:^ version: link:../platform/dist - '@effect/schema': - specifier: workspace:^ - version: link:../schema/dist effect: specifier: workspace:^ version: link:../effect/dist @@ -5258,7 +5199,6 @@ packages: libsql@0.4.5: resolution: {integrity: sha512-sorTJV6PNt94Wap27Sai5gtVLIea4Otb2LUiAUyr3p6BPOScGMKGt5F1b5X/XgkNtcsDKeX5qfeBDj+PdShclQ==} - cpu: [x64, arm64, wasm32] os: [darwin, linux, win32] lighthouse-logger@1.4.2: From 72bc2581ba6e50d513e6bfaafdd014165394b873 Mon Sep 17 00:00:00 2001 From: Tim Date: Wed, 16 Oct 2024 09:21:21 +1300 Subject: [PATCH 10/12] rename dtslint symbol --- packages/effect/dtslint/Schema.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/effect/dtslint/Schema.ts b/packages/effect/dtslint/Schema.ts index 99b86fa43d..0910294c33 100644 --- a/packages/effect/dtslint/Schema.ts +++ b/packages/effect/dtslint/Schema.ts @@ -1127,7 +1127,7 @@ S.rename(S.Struct({ a: S.String, b: S.Number }), { a: "c" }) // $ExpectType SchemaClass<{ readonly c: string; readonly d: number; }, { readonly a: string; readonly b: number; }, never> S.rename(S.Struct({ a: S.String, b: S.Number }), { a: "c", b: "d" }) -const a = Symbol.for("@effect/schema/dtslint/a") +const a = Symbol.for("effect/Schema/dtslint/a") // $ExpectType SchemaClass<{ readonly [a]: string; readonly b: number; }, { readonly a: string; readonly b: number; }, never> S.rename(S.Struct({ a: S.String, b: S.Number }), { a }) From 0c4e8e4879d23a8536eaea56cee7f1b42d33928d Mon Sep 17 00:00:00 2001 From: Tim Date: Wed, 16 Oct 2024 09:22:52 +1300 Subject: [PATCH 11/12] update comparisons --- schema/comparisons.md | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/schema/comparisons.md b/schema/comparisons.md index 60dec44be3..9f09afe32e 100644 --- a/schema/comparisons.md +++ b/schema/comparisons.md @@ -33,7 +33,7 @@ mySchema.safeParse(12) // => { success: false; error: ZodError } Schema ```ts -import { Schema as S } from "@effect/schema" +import { Schema as S } from "effect" // creating a schema for strings const mySchema = S.String @@ -68,7 +68,7 @@ type User = z.infer Schema ```ts -import { Schema as S } from "@effect/schema" +import { Schema as S } from "effect" const User = S.Struct({ username: S.String @@ -114,7 +114,7 @@ z.never() Schema ```ts -import { Schema as S } from "@effect/schema" +import { Schema as S } from "effect" // primitive values S.String @@ -163,7 +163,7 @@ tuna.value // "tuna" Schema ```ts -import { Schema as S } from "@effect/schema" +import { Schema as S } from "effect" const tuna = S.Literal("tuna") const twelve = S.Literal(12) @@ -214,7 +214,7 @@ z.string().toUpperCase() // toUpperCase Schema ```ts -import { Schema as S } from "@effect/schema" +import { Schema as S } from "effect" // validations S.String.pipe(S.maxLength(5)) @@ -298,7 +298,7 @@ date.parse("2020-01-32") // fail Schema ```ts -import { Schema as S } from "@effect/schema" +import { Schema as S } from "effect" S.decodeUnknownSync(S.Date)("2020-01-01") // pass S.decodeUnknownSync(S.Date)("2020-1-1") // pass @@ -339,7 +339,7 @@ z.number().safe() // value must be between Number.MIN_SAFE_INTEGER and Number.MA Schema ```ts -import { Schema as S } from "@effect/schema" +import { Schema as S } from "effect" S.Number.pipe(S.greaterThan(5)) S.Number.pipe(S.greaterThanOrEqualTo(5)) @@ -394,7 +394,7 @@ z.bigint().multipleOf(5n) // Evenly divisible by 5n. Schema ```ts -import { Schema as S } from "@effect/schema" +import { Schema as S } from "effect" S.BigInt.pipe(S.greaterThanBigInt(5n)) S.BigInt.pipe(S.greaterThanOrEqualToBigInt(5n)) @@ -782,7 +782,7 @@ console.log(schema.parse("tuna")) // => 42 Schema ```ts -import { Schema } from "@effect/schema" +import { Schema } from "effect" import { Either } from "effect" const schema = Schema.Number.annotations({ @@ -1176,7 +1176,7 @@ documentedString.description // A useful bit of text… Schema ```ts -import { AST, Schema as S } from "@effect/schema" +import { AST, Schema as S } from "effect" const documentedString = S.String.annotations({ description: "A useful bit of text, if you know what to do with it." From 6c369a6c4e7774d4180bb58444e0a89bdf07fec8 Mon Sep 17 00:00:00 2001 From: Tim Date: Wed, 16 Oct 2024 09:33:14 +1300 Subject: [PATCH 12/12] docgen and README's --- packages/ai/openai/docgen.json | 57 ++++++--------------------- packages/cli/docgen.json | 63 +++++++----------------------- packages/effect/docgen.json | 55 ++++++-------------------- packages/platform-node/docgen.json | 4 +- packages/platform/README.md | 26 +++++------- packages/platform/docgen.json | 48 +++++------------------ packages/rpc/README.md | 6 +-- packages/sql/README.md | 16 ++++---- 8 files changed, 69 insertions(+), 206 deletions(-) diff --git a/packages/ai/openai/docgen.json b/packages/ai/openai/docgen.json index 7fdfa91f86..5fa5696a71 100644 --- a/packages/ai/openai/docgen.json +++ b/packages/ai/openai/docgen.json @@ -1,9 +1,6 @@ { "$schema": "../../../node_modules/@effect/docgen/schema.json", - "exclude": [ - "src/Generated.ts", - "src/internal/**/*.ts" - ], + "exclude": ["src/Generated.ts", "src/internal/**/*.ts"], "examplesCompilerOptions": { "noEmit": true, "strict": true, @@ -11,48 +8,18 @@ "moduleResolution": "Bundler", "module": "ES2022", "target": "ES2022", - "lib": [ - "ES2022", - "DOM", - "DOM.Iterable" - ], + "lib": ["ES2022", "DOM", "DOM.Iterable"], "paths": { - "effect": [ - "../../../../effect/src/index.js" - ], - "effect/*": [ - "../../../../effect/src/*.js" - ], - "@effect/experimental": [ - "../../../../experimental/src/index.js" - ], - "@effect/experimental/*": [ - "../../../../experimental/src/*.js" - ], - "@effect/platform": [ - "../../../../platform/src/index.js" - ], - "@effect/platform/*": [ - "../../../../platform/src/*.js" - ], - "@effect/schema": [ - "../../../../schema/src/index.js" - ], - "@effect/schema/*": [ - "../../../../schema/src/*.js" - ], - "@effect/ai": [ - "../../../ai/src/index.js" - ], - "@effect/ai/*": [ - "../../../ai/src/*.js" - ], - "@effect/ai-openai": [ - "../../../ai-openai/src/index.js" - ], - "@effect/ai-openai/*": [ - "../../../ai-openai/src/*.js" - ] + "effect": ["../../../../effect/src/index.js"], + "effect/*": ["../../../../effect/src/*.js"], + "@effect/experimental": ["../../../../experimental/src/index.js"], + "@effect/experimental/*": ["../../../../experimental/src/*.js"], + "@effect/platform": ["../../../../platform/src/index.js"], + "@effect/platform/*": ["../../../../platform/src/*.js"], + "@effect/ai": ["../../../ai/src/index.js"], + "@effect/ai/*": ["../../../ai/src/*.js"], + "@effect/ai-openai": ["../../../ai-openai/src/index.js"], + "@effect/ai-openai/*": ["../../../ai-openai/src/*.js"] } } } diff --git a/packages/cli/docgen.json b/packages/cli/docgen.json index f0bf704bf2..dffc506f60 100644 --- a/packages/cli/docgen.json +++ b/packages/cli/docgen.json @@ -1,8 +1,6 @@ { "$schema": "../../node_modules/@effect/docgen/schema.json", - "exclude": [ - "src/internal/**/*.ts" - ], + "exclude": ["src/internal/**/*.ts"], "examplesCompilerOptions": { "noEmit": true, "strict": true, @@ -10,59 +8,26 @@ "moduleResolution": "Bundler", "module": "ES2022", "target": "ES2022", - "lib": [ - "ES2022", - "DOM" - ], + "lib": ["ES2022", "DOM"], "paths": { - "effect": [ - "../../../effect/src/index.js" - ], - "effect/*": [ - "../../../effect/src/*.js" - ], - "@effect/platform": [ - "../../../platform/src/index.js" - ], - "@effect/platform/*": [ - "../../../platform/src/*.js" - ], - "@effect/platform-node": [ - "../../../platform-node/src/index.js" - ], - "@effect/platform-node/*": [ - "../../../platform-node/src/*.js" - ], + "effect": ["../../../effect/src/index.js"], + "effect/*": ["../../../effect/src/*.js"], + "@effect/platform": ["../../../platform/src/index.js"], + "@effect/platform/*": ["../../../platform/src/*.js"], + "@effect/platform-node": ["../../../platform-node/src/index.js"], + "@effect/platform-node/*": ["../../../platform-node/src/*.js"], "@effect/platform-node-shared": [ "../../../platform-node-shared/src/index.js" ], "@effect/platform-node-shared/*": [ "../../../platform-node-shared/src/*.js" ], - "@effect/printer": [ - "../../../printer/src/index.js" - ], - "@effect/printer/*": [ - "../../../printer/src/*.js" - ], - "@effect/printer-ansi": [ - "../../../printer-ansi/src/index.js" - ], - "@effect/printer-ansi/*": [ - "../../../printer-ansi/src/*.js" - ], - "@effect/schema": [ - "../../../schema/src/index.js" - ], - "@effect/schema/*": [ - "../../../schema/src/*.js" - ], - "@effect/typeclass": [ - "../../../typeclass/src/index.js" - ], - "@effect/typeclass/*": [ - "../../../typeclass/src/*.js" - ] + "@effect/printer": ["../../../printer/src/index.js"], + "@effect/printer/*": ["../../../printer/src/*.js"], + "@effect/printer-ansi": ["../../../printer-ansi/src/index.js"], + "@effect/printer-ansi/*": ["../../../printer-ansi/src/*.js"], + "@effect/typeclass": ["../../../typeclass/src/index.js"], + "@effect/typeclass/*": ["../../../typeclass/src/*.js"] } } } diff --git a/packages/effect/docgen.json b/packages/effect/docgen.json index 341f13106d..5c4b045527 100644 --- a/packages/effect/docgen.json +++ b/packages/effect/docgen.json @@ -1,8 +1,6 @@ { "$schema": "../../node_modules/@effect/docgen/schema.json", - "exclude": [ - "src/internal/**/*.ts" - ], + "exclude": ["src/internal/**/*.ts"], "examplesCompilerOptions": { "noEmit": true, "strict": true, @@ -10,47 +8,18 @@ "moduleResolution": "Bundler", "module": "ES2022", "target": "ES2022", - "lib": [ - "ES2022", - "DOM" - ], + "lib": ["ES2022", "DOM"], "paths": { - "effect": [ - "../../../effect/src/index.js" - ], - "effect/*": [ - "../../../effect/src/*.js" - ], - "@effect/platform": [ - "../../../platform/src/index.js" - ], - "@effect/platform/*": [ - "../../../platform/src/*.js" - ], - "@effect/printer": [ - "../../../printer/src/index.js" - ], - "@effect/printer/*": [ - "../../../printer/src/*.js" - ], - "@effect/printer-ansi": [ - "../../../printer-ansi/src/index.js" - ], - "@effect/printer-ansi/*": [ - "../../../printer-ansi/src/*.js" - ], - "@effect/schema": [ - "../../../schema/src/index.js" - ], - "@effect/schema/*": [ - "../../../schema/src/*.js" - ], - "@effect/typeclass": [ - "../../../typeclass/src/index.js" - ], - "@effect/typeclass/*": [ - "../../../typeclass/src/*.js" - ] + "effect": ["../../../effect/src/index.js"], + "effect/*": ["../../../effect/src/*.js"], + "@effect/platform": ["../../../platform/src/index.js"], + "@effect/platform/*": ["../../../platform/src/*.js"], + "@effect/printer": ["../../../printer/src/index.js"], + "@effect/printer/*": ["../../../printer/src/*.js"], + "@effect/printer-ansi": ["../../../printer-ansi/src/index.js"], + "@effect/printer-ansi/*": ["../../../printer-ansi/src/*.js"], + "@effect/typeclass": ["../../../typeclass/src/index.js"], + "@effect/typeclass/*": ["../../../typeclass/src/*.js"] } } } diff --git a/packages/platform-node/docgen.json b/packages/platform-node/docgen.json index 7eb6dd18b3..800dd65617 100644 --- a/packages/platform-node/docgen.json +++ b/packages/platform-node/docgen.json @@ -21,9 +21,7 @@ ], "@effect/platform-node-shared/*": [ "../../../platform-node-shared/src/*.js" - ], - "@effect/schema": ["../../../schema/src/index.js"], - "@effect/schema/*": ["../../../schema/src/*.js"] + ] } } } diff --git a/packages/platform/README.md b/packages/platform/README.md index 7c960f3362..c03df934ce 100644 --- a/packages/platform/README.md +++ b/packages/platform/README.md @@ -32,7 +32,7 @@ Let's define a simple CRUD API for managing users. First, we need to make an ```ts import { HttpApiEndpoint, HttpApiGroup } from "@effect/platform" -import { Schema } from "@effect/schema" +import { Schema } from "effect" // Our domain "User" Schema class User extends Schema.Class("User")({ @@ -306,8 +306,7 @@ import { HttpApiEndpoint, HttpApiGroup } from "@effect/platform" -import { Schema } from "@effect/schema" -import { DateTime, Effect } from "effect" +import { DateTime, Effect, Schema } from "effect" // here is our api definition class User extends Schema.Class("User")({ @@ -1161,7 +1160,7 @@ string { ### Decoding Data with Schemas -A common use case when fetching data is to validate the received format. For this purpose, the `HttpClientResponse` module is integrated with `@effect/schema`. +A common use case when fetching data is to validate the received format. For this purpose, the `HttpClientResponse` module is integrated with `effect/Schema`. ```ts import { @@ -1170,8 +1169,7 @@ import { HttpClientResponse } from "@effect/platform" import { NodeRuntime } from "@effect/platform-node" -import { Schema } from "@effect/schema" -import { Console, Effect } from "effect" +import { Console, Effect, Schema } from "effect" const Post = Schema.Struct({ id: Schema.Number, @@ -1329,7 +1327,7 @@ Output: ### Decoding Data with Schemas -A common use case when fetching data is to validate the received format. For this purpose, the `HttpClientResponse` module is integrated with `@effect/schema`. +A common use case when fetching data is to validate the received format. For this purpose, the `HttpClientResponse` module is integrated with `effect/Schema`. ```ts import { @@ -1339,8 +1337,7 @@ import { HttpClientResponse } from "@effect/platform" import { NodeRuntime } from "@effect/platform-node" -import { Schema } from "@effect/schema" -import { Console, Effect } from "effect" +import { Console, Effect, Schema } from "effect" const Post = Schema.Struct({ id: Schema.Number, @@ -1717,8 +1714,7 @@ To define routes with parameters, include the parameter names in the path and us ```ts import { HttpRouter, HttpServer, HttpServerResponse } from "@effect/platform" -import { Schema } from "@effect/schema" -import { Effect } from "effect" +import { Effect, Schema } from "effect" import { listen } from "./listen.js" // Define the schema for route parameters @@ -2408,7 +2404,7 @@ curl -i http://localhost:3000/fail ## Validations -Validation is a critical aspect of handling HTTP requests to ensure that the data your server receives is as expected. We'll explore how to validate headers and cookies using the `@effect/platform` and `@effect/schema` libraries, which provide structured and robust methods for these tasks. +Validation is a critical aspect of handling HTTP requests to ensure that the data your server receives is as expected. We'll explore how to validate headers and cookies using the `@effect/platform` and `effect/Schema` libraries, which provide structured and robust methods for these tasks. ### Headers @@ -2421,8 +2417,7 @@ import { HttpServerRequest, HttpServerResponse } from "@effect/platform" -import { Schema } from "@effect/schema" -import { Effect } from "effect" +import { Effect, Schema } from "effect" import { listen } from "./listen.js" const router = HttpRouter.empty.pipe( @@ -2472,8 +2467,7 @@ import { HttpServerRequest, HttpServerResponse } from "@effect/platform" -import { Schema } from "@effect/schema" -import { Effect } from "effect" +import { Effect, Schema } from "effect" import { listen } from "./listen.js" const router = HttpRouter.empty.pipe( diff --git a/packages/platform/docgen.json b/packages/platform/docgen.json index c9859259e4..42c17df2c0 100644 --- a/packages/platform/docgen.json +++ b/packages/platform/docgen.json @@ -1,8 +1,6 @@ { "$schema": "../../node_modules/@effect/docgen/schema.json", - "exclude": [ - "src/internal/**/*.ts" - ], + "exclude": ["src/internal/**/*.ts"], "examplesCompilerOptions": { "noEmit": true, "strict": true, @@ -10,48 +8,22 @@ "moduleResolution": "Bundler", "module": "ES2022", "target": "ES2022", - "lib": [ - "ES2022", - "DOM", - "DOM.Iterable" - ], + "lib": ["ES2022", "DOM", "DOM.Iterable"], "paths": { - "effect": [ - "../../../effect/src/index.js" - ], - "effect/*": [ - "../../../effect/src/*.js" - ], - "@effect/platform": [ - "../../../platform/src/index.js" - ], - "@effect/platform/*": [ - "../../../platform/src/*.js" - ], - "@effect/platform-node": [ - "../../../platform-node/src/index.js" - ], - "@effect/platform-node/*": [ - "../../../platform-node/src/*.js" - ], + "effect": ["../../../effect/src/index.js"], + "effect/*": ["../../../effect/src/*.js"], + "@effect/platform": ["../../../platform/src/index.js"], + "@effect/platform/*": ["../../../platform/src/*.js"], + "@effect/platform-node": ["../../../platform-node/src/index.js"], + "@effect/platform-node/*": ["../../../platform-node/src/*.js"], "@effect/platform-node-shared": [ "../../../platform-node-shared/src/index.js" ], "@effect/platform-node-shared/*": [ "../../../platform-node-shared/src/*.js" ], - "@effect/schema": [ - "../../../schema/src/index.js" - ], - "@effect/schema/*": [ - "../../../schema/src/*.js" - ], - "@effect/typeclass": [ - "../../../typeclass/src/index.js" - ], - "@effect/typeclass/*": [ - "../../../typeclass/src/*.js" - ] + "@effect/typeclass": ["../../../typeclass/src/index.js"], + "@effect/typeclass/*": ["../../../typeclass/src/*.js"] } } } diff --git a/packages/rpc/README.md b/packages/rpc/README.md index 36dce4ae80..7cee42b456 100644 --- a/packages/rpc/README.md +++ b/packages/rpc/README.md @@ -6,7 +6,7 @@ The `@effect/rpc` library facilitates the development of remote procedure call ( ## Declaring Requests -The `TaggedRequest` API in the `@effect/schema` library is designed to facilitate the creation of structured requests that can serialize function signatures involving input arguments, successful outcomes, and potential failures. Essentially, it's a tool for defining a serializable function that can be reliably transported across different systems or network layers. +The `TaggedRequest` API in the `effect/Schema` module is designed to facilitate the creation of structured requests that can serialize function signatures involving input arguments, successful outcomes, and potential failures. Essentially, it's a tool for defining a serializable function that can be reliably transported across different systems or network layers. Here’s a simplified explanation: @@ -32,7 +32,7 @@ sequenceDiagram ```ts filename="request.ts" // request.ts -import { Schema } from "@effect/schema" +import { Schema } from "effect" // Define a user with an ID and name export class User extends Schema.Class("User")({ @@ -209,7 +209,7 @@ Effect.runPromise(program).then(console.log) ```ts filename="request.ts" // request.ts import * as Rpc from "@effect/rpc/Rpc" -import { Schema } from "@effect/schema" +import { Schema } from "effect" export class Counts extends Rpc.StreamRequest()("Counts", { failure: Schema.Never, // Indicates that no errors are expected diff --git a/packages/sql/README.md b/packages/sql/README.md index 124590be52..45dcf4ff3f 100644 --- a/packages/sql/README.md +++ b/packages/sql/README.md @@ -95,8 +95,7 @@ In `sqlfx` you could pass an array to the `sql(array)` function to pass an list ## INSERT resolver ```ts -import { Effect, pipe } from "effect" -import { Schema } from "@effect/schema" +import { Effect, Schema, pipe } from "effect" import { SqlClient } from "@effect/sql" class Person extends Schema.Class("Person")({ @@ -114,15 +113,15 @@ export const makePersonService = Effect.gen(function* () { const sql = yield* SqlClient.SqlClient const InsertPerson = yield* SqlResolver.ordered("InsertPerson", { - Request: InsertPersonSchema, - Result: Person, - execute: (requests) => - sql` + Request: InsertPersonSchema, + Result: Person, + execute: (requests) => + sql` INSERT INTO people ${sql.insert(requests)} RETURNING people.* ` - }) + }) const insert = InsertPerson.execute @@ -133,8 +132,7 @@ export const makePersonService = Effect.gen(function* () { ## SELECT resolver ```ts -import { Effect, pipe } from "effect" -import { Schema } from "@effect/schema" +import { Effect, Schema, pipe } from "effect" import { SqlResolver, SqlClient } from "@effect/sql" class Person extends Schema.Class("Person")({

    = P extends - SerializableWithResult ? SR | RR - : never - /** - * @since 0.69.0 - */ - export type Any = SerializableWithResult - /** - * @since 0.69.0 - */ - export type All = - | Any - | SerializableWithResult -} /** - * @since 0.69.0 + * @category re-exports + * @since 0.76.0 */ -export const asSerializableWithResult = ( - procedure: SWR -): SerializableWithResult< - Serializable.Type, - Serializable.Encoded, - Serializable.Context, - WithResult.Success, - WithResult.SuccessEncoded, - WithResult.Failure, - WithResult.FailureEncoded, - WithResult.Context -> => procedure as any +export * from "effect/Serializable" diff --git a/packages/schema/src/TreeFormatter.ts b/packages/schema/src/TreeFormatter.ts index 05eaf3c960..ca803d16bb 100644 --- a/packages/schema/src/TreeFormatter.ts +++ b/packages/schema/src/TreeFormatter.ts @@ -2,214 +2,8 @@ * @since 0.67.0 */ -import type * as Cause from "effect/Cause" -import * as Effect from "effect/Effect" -import * as Option from "effect/Option" -import * as Predicate from "effect/Predicate" -import * as AST from "./AST.js" -import * as util_ from "./internal/util.js" -import type * as ParseResult from "./ParseResult.js" - -interface Forest extends ReadonlyArray> {} - -interface Tree { - readonly value: A - readonly forest: Forest -} - -const make = (value: A, forest: Forest = []): Tree => ({ - value, - forest -}) - -/** - * @category formatting - * @since 0.67.0 - */ -export const formatIssue = (issue: ParseResult.ParseIssue): Effect.Effect => - Effect.map(go(issue), (tree) => drawTree(tree)) - -/** - * @category formatting - * @since 0.67.0 - */ -export const formatIssueSync = (issue: ParseResult.ParseIssue): string => Effect.runSync(formatIssue(issue)) - /** - * @category formatting - * @since 0.67.0 + * @category re-exports + * @since 0.76.0 */ -export const formatError = (error: ParseResult.ParseError): Effect.Effect => formatIssue(error.issue) - -/** - * @category formatting - * @since 0.67.0 - */ -export const formatErrorSync = (error: ParseResult.ParseError): string => formatIssueSync(error.issue) - -const drawTree = (tree: Tree): string => tree.value + draw("\n", tree.forest) - -const draw = (indentation: string, forest: Forest): string => { - let r = "" - const len = forest.length - let tree: Tree - for (let i = 0; i < len; i++) { - tree = forest[i] - const isLast = i === len - 1 - r += indentation + (isLast ? "└" : "├") + "─ " + tree.value - r += draw(indentation + (len > 1 && !isLast ? "│ " : " "), tree.forest) - } - return r -} - -const formatTransformationKind = (kind: ParseResult.Transformation["kind"]): string => { - switch (kind) { - case "Encoded": - return "Encoded side transformation failure" - case "Transformation": - return "Transformation process failure" - case "Type": - return "Type side transformation failure" - } -} - -const formatRefinementKind = (kind: ParseResult.Refinement["kind"]): string => { - switch (kind) { - case "From": - return "From side refinement failure" - case "Predicate": - return "Predicate refinement failure" - } -} - -const getAnnotated = (issue: ParseResult.ParseIssue): Option.Option => - "ast" in issue ? Option.some(issue.ast) : Option.none() - -interface CurrentMessage { - readonly message: string - readonly override: boolean -} - -const getCurrentMessage = ( - issue: ParseResult.ParseIssue -): Effect.Effect => - getAnnotated(issue).pipe( - Option.flatMap(AST.getMessageAnnotation), - Effect.flatMap((annotation) => { - const out = annotation(issue) - return Predicate.isString(out) - ? Effect.succeed({ message: out, override: false }) - : Effect.isEffect(out) - ? Effect.map(out, (message) => ({ message, override: false })) - : Predicate.isString(out.message) - ? Effect.succeed({ message: out.message, override: out.override }) - : Effect.map(out.message, (message) => ({ message, override: out.override })) - }) - ) - -const createParseIssueGuard = - (tag: T) => - (issue: ParseResult.ParseIssue): issue is Extract => issue._tag === tag - -const isComposite = createParseIssueGuard("Composite") -const isRefinement = createParseIssueGuard("Refinement") -const isTransformation = createParseIssueGuard("Transformation") - -/** @internal */ -export const getMessage: ( - issue: ParseResult.ParseIssue -) => Effect.Effect = (issue: ParseResult.ParseIssue) => - getCurrentMessage(issue).pipe( - Effect.flatMap((currentMessage) => { - const useInnerMessage = !currentMessage.override && ( - isComposite(issue) || - (isRefinement(issue) && issue.kind === "From") || - (isTransformation(issue) && issue.kind !== "Transformation") - ) - return useInnerMessage - ? isTransformation(issue) || isRefinement(issue) ? getMessage(issue.issue) : Option.none() - : Effect.succeed(currentMessage.message) - }) - ) - -const getParseIssueTitleAnnotation = (issue: ParseResult.ParseIssue): Option.Option => - getAnnotated(issue).pipe( - Option.flatMap(AST.getParseIssueTitleAnnotation), - Option.filterMap( - (annotation) => Option.fromNullable(annotation(issue)) - ) - ) - -/** @internal */ -export const formatTypeMessage = (e: ParseResult.Type): Effect.Effect => - getMessage(e).pipe( - Effect.orElse(() => getParseIssueTitleAnnotation(e)), - Effect.catchAll(() => - Effect.succeed(e.message ?? `Expected ${String(e.ast)}, actual ${util_.formatUnknown(e.actual)}`) - ) - ) - -const getParseIssueTitle = ( - issue: ParseResult.Forbidden | ParseResult.Transformation | ParseResult.Refinement | ParseResult.Composite -): string => Option.getOrElse(getParseIssueTitleAnnotation(issue), () => String(issue.ast)) - -/** @internal */ -export const formatForbiddenMessage = (e: ParseResult.Forbidden): string => e.message ?? "is forbidden" - -/** @internal */ -export const formatUnexpectedMessage = (e: ParseResult.Unexpected): string => e.message ?? "is unexpected" - -/** @internal */ -export const formatMissingMessage = (e: ParseResult.Missing): Effect.Effect => - AST.getMissingMessageAnnotation(e.ast).pipe( - Effect.flatMap((annotation) => { - const out = annotation() - return Predicate.isString(out) ? Effect.succeed(out) : out - }), - Effect.catchAll(() => Effect.succeed(e.message ?? "is missing")) - ) - -const getTree = (issue: ParseResult.ParseIssue, onFailure: () => Effect.Effect>) => - Effect.matchEffect(getMessage(issue), { - onFailure, - onSuccess: (message) => Effect.succeed(make(message)) - }) - -const go = ( - e: ParseResult.ParseIssue | ParseResult.Pointer -): Effect.Effect> => { - switch (e._tag) { - case "Type": - return Effect.map(formatTypeMessage(e), make) - case "Forbidden": - return Effect.succeed(make(getParseIssueTitle(e), [make(formatForbiddenMessage(e))])) - case "Unexpected": - return Effect.succeed(make(formatUnexpectedMessage(e))) - case "Missing": - return Effect.map(formatMissingMessage(e), make) - case "Transformation": - return getTree(e, () => - Effect.map( - go(e.issue), - (tree) => make(getParseIssueTitle(e), [make(formatTransformationKind(e.kind), [tree])]) - )) - case "Refinement": - return getTree( - e, - () => - Effect.map(go(e.issue), (tree) => make(getParseIssueTitle(e), [make(formatRefinementKind(e.kind), [tree])])) - ) - case "Pointer": - return Effect.map(go(e.issue), (tree) => make(util_.formatPath(e.path), [tree])) - case "Composite": { - const parseIssueTitle = getParseIssueTitle(e) - return getTree( - e, - () => - util_.isNonEmpty(e.issues) - ? Effect.map(Effect.forEach(e.issues, go), (forest) => make(parseIssueTitle, forest)) - : Effect.map(go(e.issues), (tree) => make(parseIssueTitle, [tree])) - ) - } - } -} +export * from "effect/SchemaTreeFormatter" diff --git a/packages/schema/test/Schema/UniqueSymbol/UniqueSymbolFromSelf.test.ts b/packages/schema/test/Schema/UniqueSymbol/UniqueSymbolFromSelf.test.ts deleted file mode 100644 index b9913dafb2..0000000000 --- a/packages/schema/test/Schema/UniqueSymbol/UniqueSymbolFromSelf.test.ts +++ /dev/null @@ -1,17 +0,0 @@ -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/TestUtils" -import { describe, it } from "vitest" - -describe("UniqueSymbolFromSelf", () => { - const a = Symbol.for("@effect/schema/test/a") - const schema = S.UniqueSymbolFromSelf(a) - it("decoding", async () => { - await Util.expectDecodeUnknownSuccess(schema, a) - await Util.expectDecodeUnknownSuccess(schema, Symbol.for("@effect/schema/test/a")) - await Util.expectDecodeUnknownFailure( - schema, - "Symbol(@effect/schema/test/a)", - `Expected Symbol(@effect/schema/test/a), actual "Symbol(@effect/schema/test/a)"` - ) - }) -}) diff --git a/packages/schema/test/formatUnknown.test.ts b/packages/schema/test/formatUnknown.test.ts deleted file mode 100644 index bc42940305..0000000000 --- a/packages/schema/test/formatUnknown.test.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { describe, expect, it } from "vitest" -import { formatUnknown } from "../src/internal/util.js" - -describe("util > formatUnknown", () => { - it("should format symbol property signatures", () => { - expect(formatUnknown({ [Symbol.for("a")]: 1 })).toEqual("{Symbol(a):1}") - }) - - it("should handle unexpected errors", () => { - const circular: any = { a: null } - circular.a = circular - expect(formatUnknown(circular)).toEqual("[object Object]") - }) - - it("should detect data types with a custom `toString` implementation", () => { - const noToString = { a: 1 } - expect(formatUnknown(noToString)).toEqual(`{"a":1}`) - const ToString = Object.create({ - toString() { - return "toString custom implementation" - } - }) - expect(formatUnknown(ToString)).toEqual("toString custom implementation") - // should not detect arrays - expect(formatUnknown([1, 2, 3])).toEqual("[1,2,3]") - }) -}) diff --git a/packages/schema/test/index.test.ts b/packages/schema/test/index.test.ts new file mode 100644 index 0000000000..b58593296d --- /dev/null +++ b/packages/schema/test/index.test.ts @@ -0,0 +1,5 @@ +import { expect, it } from "vitest" + +it("should work", () => { + expect(1).toBe(1) +}) diff --git a/packages/schema/test/util.test.ts b/packages/schema/test/util.test.ts deleted file mode 100644 index 8ea686e5ba..0000000000 --- a/packages/schema/test/util.test.ts +++ /dev/null @@ -1,14 +0,0 @@ -import * as util from "@effect/schema/internal/util" -import { describe, expect, it } from "vitest" - -describe("util", () => { - it("ownKeys", () => { - expect(util.ownKeys({})).toStrictEqual([]) - expect(util.ownKeys({ a: 1 })).toStrictEqual(["a"]) - expect(util.ownKeys({ a: 1, b: 2 })).toStrictEqual(["a", "b"]) - const a = Symbol.for("@effect/schema/test/a") - const b = Symbol.for("@effect/schema/test/b") - expect(util.ownKeys({ [a]: 3, [b]: 4 })).toStrictEqual([a, b]) - expect(util.ownKeys({ a: 1, [a]: 3, b: 2, [b]: 4 })).toStrictEqual(["a", "b", a, b]) - }) -}) diff --git a/packages/sql-d1/test/Resolver.test.ts b/packages/sql-d1/test/Resolver.test.ts index 96e0a73473..b94ecfc3a0 100644 --- a/packages/sql-d1/test/Resolver.test.ts +++ b/packages/sql-d1/test/Resolver.test.ts @@ -1,8 +1,8 @@ -import * as Schema from "@effect/schema/Schema" import { SqlError, SqlResolver } from "@effect/sql" import { D1Client } from "@effect/sql-d1" import { assert, describe, it } from "@effect/vitest" import { Array, Effect, Option } from "effect" +import * as Schema from "effect/Schema" import { D1Miniflare } from "./utils.js" const seededClient = Effect.gen(function*(_) { diff --git a/packages/sql-libsql/test/Resolver.test.ts b/packages/sql-libsql/test/Resolver.test.ts index 72d9a7a7c9..2c38a46442 100644 --- a/packages/sql-libsql/test/Resolver.test.ts +++ b/packages/sql-libsql/test/Resolver.test.ts @@ -1,8 +1,8 @@ -import * as Schema from "@effect/schema/Schema" import { SqlError, SqlResolver } from "@effect/sql" import { LibsqlClient } from "@effect/sql-libsql" import { assert, describe, layer } from "@effect/vitest" import { Array, Effect, Option } from "effect" +import * as Schema from "effect/Schema" import { LibsqlContainer } from "./util.js" const seededClient = Effect.gen(function*(_) { diff --git a/packages/sql-sqlite-node/test/Resolver.test.ts b/packages/sql-sqlite-node/test/Resolver.test.ts index a945b0f43e..2a6d0d97a5 100644 --- a/packages/sql-sqlite-node/test/Resolver.test.ts +++ b/packages/sql-sqlite-node/test/Resolver.test.ts @@ -1,10 +1,10 @@ import { FileSystem } from "@effect/platform" import { NodeFileSystem } from "@effect/platform-node" -import * as Schema from "@effect/schema/Schema" import { SqlError, SqlResolver } from "@effect/sql" import { SqliteClient } from "@effect/sql-sqlite-node" import { assert, describe, it } from "@effect/vitest" import { Array, Effect, Option } from "effect" +import * as Schema from "effect/Schema" const makeClient = Effect.gen(function*(_) { const fs = yield* _(FileSystem.FileSystem) diff --git a/packages/sql/src/Model.ts b/packages/sql/src/Model.ts index 52b1b9ed89..d4a04cc162 100644 --- a/packages/sql/src/Model.ts +++ b/packages/sql/src/Model.ts @@ -3,13 +3,13 @@ */ import * as RRX from "@effect/experimental/RequestResolver" import * as VariantSchema from "@effect/experimental/VariantSchema" -import * as ParseResult from "@effect/schema/ParseResult" -import * as Schema from "@effect/schema/Schema" import type { Brand } from "effect/Brand" import * as DateTime from "effect/DateTime" import type { DurationInput } from "effect/Duration" import * as Effect from "effect/Effect" import * as Option from "effect/Option" +import * as ParseResult from "effect/ParseResult" +import * as Schema from "effect/Schema" import type { Scope } from "effect/Scope" import { SqlClient } from "./SqlClient.js" import * as SqlResolver from "./SqlResolver.js" diff --git a/packages/sql/src/SqlResolver.ts b/packages/sql/src/SqlResolver.ts index 9c5d1aa2ae..eeb7ca2f50 100644 --- a/packages/sql/src/SqlResolver.ts +++ b/packages/sql/src/SqlResolver.ts @@ -1,8 +1,6 @@ /** * @since 1.0.0 */ -import type { ParseError } from "@effect/schema/ParseResult" -import * as Schema from "@effect/schema/Schema" import type { NonEmptyArray } from "effect/Array" import * as Context from "effect/Context" import * as Effect from "effect/Effect" @@ -12,8 +10,10 @@ import * as FiberRef from "effect/FiberRef" import * as Hash from "effect/Hash" import * as MutableHashMap from "effect/MutableHashMap" import * as Option from "effect/Option" +import type { ParseError } from "effect/ParseResult" import * as Request from "effect/Request" import * as RequestResolver from "effect/RequestResolver" +import * as Schema from "effect/Schema" import * as Tracer from "effect/Tracer" import type * as Types from "effect/Types" import * as internalClient from "./internal/client.js" diff --git a/packages/sql/src/SqlSchema.ts b/packages/sql/src/SqlSchema.ts index b3f6b0c8f5..934d6ea7d4 100644 --- a/packages/sql/src/SqlSchema.ts +++ b/packages/sql/src/SqlSchema.ts @@ -1,11 +1,11 @@ /** * @since 1.0.0 */ -import type { ParseError } from "@effect/schema/ParseResult" -import * as Schema from "@effect/schema/Schema" import * as Cause from "effect/Cause" import * as Effect from "effect/Effect" import type * as Option from "effect/Option" +import type { ParseError } from "effect/ParseResult" +import * as Schema from "effect/Schema" /** * Run a sql query with a request schema and a result schema. diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d686c2fc4d..316744737c 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -333,6 +333,10 @@ importers: publishDirectory: dist packages/effect: + dependencies: + fast-check: + specifier: ^3.21.0 + version: 3.21.0 devDependencies: '@types/jscodeshift': specifier: ^0.11.11 @@ -340,12 +344,21 @@ importers: '@types/node': specifier: ^22.5.4 version: 22.5.4 + ajv: + specifier: ^8.17.1 + version: 8.17.1 ast-types: specifier: ^0.14.2 version: 0.14.2 jscodeshift: specifier: ^0.16.1 version: 0.16.1(@babel/preset-env@7.25.4(@babel/core@7.25.2)) + tinybench: + specifier: ^2.9.0 + version: 2.9.0 + zod: + specifier: ^3.23.5 + version: 3.23.8 publishDirectory: dist packages/experimental: @@ -617,18 +630,9 @@ importers: specifier: ^3.21.0 version: 3.21.0 devDependencies: - ajv: - specifier: ^8.17.1 - version: 8.17.1 effect: specifier: workspace:^ version: link:../effect/dist - tinybench: - specifier: ^2.9.0 - version: 2.9.0 - zod: - specifier: ^3.23.5 - version: 3.23.8 publishDirectory: dist packages/sql: @@ -5254,6 +5258,7 @@ packages: libsql@0.4.5: resolution: {integrity: sha512-sorTJV6PNt94Wap27Sai5gtVLIea4Otb2LUiAUyr3p6BPOScGMKGt5F1b5X/XgkNtcsDKeX5qfeBDj+PdShclQ==} + cpu: [x64, arm64, wasm32] os: [darwin, linux, win32] lighthouse-logger@1.4.2: From 418ae94d188c4415eab7ca29cdd4669061a27118 Mon Sep 17 00:00:00 2001 From: Giulio Canti Date: Sun, 13 Oct 2024 13:05:45 +0200 Subject: [PATCH 03/12] merge ArrayFormatter / TreeFormatter into ParseResult --- .changeset/cyan-sloths-lick.md | 4 +- .changeset/six-crabs-itch.md | 26 +- packages/cli/src/internal/args.ts | 8 +- packages/cli/src/internal/options.ts | 9 +- packages/cluster-node/src/StorageFile.ts | 14 +- .../cluster/src/internal/serialization.ts | 16 +- packages/effect/benchmark/SchemaToString.ts | 3 +- .../effect/benchmark/SchemaTreeFormatter.ts | 5 +- packages/effect/src/Arbitrary.ts | 6 +- packages/effect/src/JSONSchema.ts | 6 +- packages/effect/src/ParseResult.ts | 302 ++++++++++++++++-- packages/effect/src/Pretty.ts | 4 +- packages/effect/src/Schema.ts | 109 ++++--- packages/effect/src/SchemaAST.ts | 20 +- packages/effect/src/SchemaArrayFormatter.ts | 82 ----- packages/effect/src/SchemaEquivalence.ts | 10 +- packages/effect/src/SchemaTreeFormatter.ts | 215 ------------- packages/effect/src/Serializable.ts | 8 +- packages/effect/src/index.ts | 10 - ...s.test.ts => ParseResultFormatter.test.ts} | 14 +- packages/effect/test/Schema/TestUtils.ts | 10 +- packages/experimental/src/Persistence.ts | 5 +- packages/platform/src/HttpApiError.ts | 8 +- packages/schema/src/ArrayFormatter.ts | 9 - packages/schema/src/TreeFormatter.ts | 9 - packages/schema/src/index.ts | 10 - 26 files changed, 433 insertions(+), 489 deletions(-) delete mode 100644 packages/effect/src/SchemaArrayFormatter.ts delete mode 100644 packages/effect/src/SchemaTreeFormatter.ts rename packages/effect/test/Schema/{SchemaFormatters.test.ts => ParseResultFormatter.test.ts} (98%) delete mode 100644 packages/schema/src/ArrayFormatter.ts delete mode 100644 packages/schema/src/TreeFormatter.ts diff --git a/.changeset/cyan-sloths-lick.md b/.changeset/cyan-sloths-lick.md index 2af813913e..b39447ddf5 100644 --- a/.changeset/cyan-sloths-lick.md +++ b/.changeset/cyan-sloths-lick.md @@ -2,4 +2,6 @@ "@effect/schema": minor --- -Re-export modules from `effect` +Re-export modules from `effect`. + +`ArrayFormatter` / `TreeFormatter` merged into `ParseResult` module. diff --git a/.changeset/six-crabs-itch.md b/.changeset/six-crabs-itch.md index 433cec855f..c578e7756a 100644 --- a/.changeset/six-crabs-itch.md +++ b/.changeset/six-crabs-itch.md @@ -4,12 +4,13 @@ Merge Schema into Effect. +### Modules + Before ```ts import { Arbitrary, - ArrayFormatter, AST, Equivalence, FastCheck, @@ -17,8 +18,7 @@ import { ParseResult, Pretty, Schema, - Serializable, - TreeFormatter + Serializable } from "@effect/schema" ``` @@ -27,7 +27,6 @@ After ```ts import { Arbitrary, - SchemaArrayFormatter, // changed SchemaAST, // changed SchemaEquivalence, // changed FastCheck, @@ -35,7 +34,22 @@ import { ParseResult, Pretty, Schema, - Serializable, - SchemaTreeFormatter // changed + Serializable } from "effect" ``` + +### Formatters + +`ArrayFormatter` / `TreeFormatter` merged into `ParseResult` module. + +Before + +```ts +import { ArrayFormatter, TreeFormatter } from "@effect/schema" +``` + +After + +```ts +import { ArrayFormatter, TreeFormatter } from "effect/ParseResult" +``` diff --git a/packages/cli/src/internal/args.ts b/packages/cli/src/internal/args.ts index 6fa5b41475..ace998a793 100644 --- a/packages/cli/src/internal/args.ts +++ b/packages/cli/src/internal/args.ts @@ -8,11 +8,11 @@ import * as Effect from "effect/Effect" import * as Either from "effect/Either" import { dual, pipe } from "effect/Function" import * as Option from "effect/Option" +import * as ParseResult from "effect/ParseResult" import { pipeArguments } from "effect/Pipeable" import type * as Redacted from "effect/Redacted" import * as Ref from "effect/Ref" -import * as Schema from "effect/Schema" -import * as TreeFormatter from "effect/SchemaTreeFormatter" +import type * as Schema from "effect/Schema" import type * as Secret from "effect/Secret" import type * as Args from "../Args.js" import type * as CliConfig from "../CliConfig.js" @@ -436,11 +436,11 @@ export const withSchema = dual< schema: Schema.Schema ) => Args.Args >(2, (self, schema) => { - const decode = Schema.decode(schema) + const decode = ParseResult.decode(schema) return mapEffect(self, (_) => Effect.mapError( decode(_ as any), - (error) => InternalHelpDoc.p(TreeFormatter.formatErrorSync(error)) + (issue) => InternalHelpDoc.p(ParseResult.TreeFormatter.formatIssueSync(issue)) )) }) diff --git a/packages/cli/src/internal/options.ts b/packages/cli/src/internal/options.ts index abf8acbb65..7e47a51ec1 100644 --- a/packages/cli/src/internal/options.ts +++ b/packages/cli/src/internal/options.ts @@ -10,12 +10,12 @@ import { dual, pipe } from "effect/Function" import * as HashMap from "effect/HashMap" import * as Option from "effect/Option" import * as Order from "effect/Order" +import * as ParseResult from "effect/ParseResult" import { pipeArguments } from "effect/Pipeable" import * as Predicate from "effect/Predicate" import type * as Redacted from "effect/Redacted" import * as Ref from "effect/Ref" -import * as Schema from "effect/Schema" -import * as TreeFormatter from "effect/SchemaTreeFormatter" +import type * as Schema from "effect/Schema" import type * as Secret from "effect/Secret" import type * as CliConfig from "../CliConfig.js" import type * as HelpDoc from "../HelpDoc.js" @@ -671,11 +671,12 @@ export const withSchema = dual< schema: Schema.Schema ) => Options.Options >(2, (self, schema) => { - const decode = Schema.decode(schema) + const decode = ParseResult.decode(schema) return mapEffect(self, (_) => Effect.mapError( decode(_ as any), - (error) => InternalValidationError.invalidValue(InternalHelpDoc.p(TreeFormatter.formatErrorSync(error))) + (issue) => + InternalValidationError.invalidValue(InternalHelpDoc.p(ParseResult.TreeFormatter.formatIssueSync(issue))) )) }) diff --git a/packages/cluster-node/src/StorageFile.ts b/packages/cluster-node/src/StorageFile.ts index e330b667eb..67ab625799 100644 --- a/packages/cluster-node/src/StorageFile.ts +++ b/packages/cluster-node/src/StorageFile.ts @@ -11,9 +11,9 @@ import { pipe } from "effect/Function" import * as HashMap from "effect/HashMap" import * as Layer from "effect/Layer" import type * as Option from "effect/Option" +import * as ParseResult from "effect/ParseResult" import * as Queue from "effect/Queue" import * as Schema from "effect/Schema" -import * as TreeFormatter from "effect/SchemaTreeFormatter" import * as Stream from "effect/Stream" import * as fs from "node:fs" @@ -21,8 +21,10 @@ import * as fs from "node:fs" export function jsonStringify(value: A, schema: Schema.Schema) { return pipe( value, - Schema.encode(schema), - Effect.mapError((e) => new ShardingException.SerializationException({ error: TreeFormatter.formatError(e) })), + ParseResult.encode(schema), + Effect.mapError((issue) => + new ShardingException.SerializationException({ error: ParseResult.TreeFormatter.formatIssue(issue) }) + ), Effect.map((_) => JSON.stringify(_)) ) } @@ -31,8 +33,10 @@ export function jsonStringify(value: A, schema: Schema.Schema) { export function jsonParse(value: string, schema: Schema.Schema) { return pipe( Effect.sync(() => JSON.parse(value)), - Effect.flatMap(Schema.decode(schema)), - Effect.mapError((e) => new ShardingException.SerializationException({ error: TreeFormatter.formatError(e) })) + Effect.flatMap(ParseResult.decode(schema)), + Effect.mapError((issue) => + new ShardingException.SerializationException({ error: ParseResult.TreeFormatter.formatIssue(issue) }) + ) ) } diff --git a/packages/cluster/src/internal/serialization.ts b/packages/cluster/src/internal/serialization.ts index e21f162e12..c031a1e957 100644 --- a/packages/cluster/src/internal/serialization.ts +++ b/packages/cluster/src/internal/serialization.ts @@ -2,8 +2,8 @@ import * as Context from "effect/Context" import * as Effect from "effect/Effect" import { pipe } from "effect/Function" import * as Layer from "effect/Layer" -import * as Schema from "effect/Schema" -import * as TreeFormatter from "effect/SchemaTreeFormatter" +import * as ParseResult from "effect/ParseResult" +import type * as Schema from "effect/Schema" import type * as Serialization from "../Serialization.js" import * as SerializedMessage from "../SerializedMessage.js" import * as ShardingException from "../ShardingException.js" @@ -23,8 +23,10 @@ export const serializationTag = Context.GenericTag( function jsonStringify(value: A, schema: Schema.Schema) { return pipe( value, - Schema.encode(schema), - Effect.mapError((e) => new ShardingException.SerializationException({ error: TreeFormatter.formatError(e) })), + ParseResult.encode(schema), + Effect.mapError((issue) => + new ShardingException.SerializationException({ error: ParseResult.TreeFormatter.formatIssue(issue) }) + ), Effect.map((_) => JSON.stringify(_)) ) } @@ -33,8 +35,10 @@ function jsonStringify(value: A, schema: Schema.Schema) { function jsonParse(value: string, schema: Schema.Schema) { return pipe( Effect.sync(() => JSON.parse(value)), - Effect.flatMap(Schema.decode(schema)), - Effect.mapError((e) => new ShardingException.SerializationException({ error: TreeFormatter.formatError(e) })) + Effect.flatMap(ParseResult.decode(schema)), + Effect.mapError((issue) => + new ShardingException.SerializationException({ error: ParseResult.TreeFormatter.formatIssue(issue) }) + ) ) } diff --git a/packages/effect/benchmark/SchemaToString.ts b/packages/effect/benchmark/SchemaToString.ts index bfbd1d25f5..b2f229bf8c 100644 --- a/packages/effect/benchmark/SchemaToString.ts +++ b/packages/effect/benchmark/SchemaToString.ts @@ -1,6 +1,5 @@ import * as ParseResult from "effect/ParseResult" import * as S from "effect/Schema" -import * as TreeFormatter from "effect/SchemaTreeFormatter" import { Bench } from "tinybench" /* @@ -37,7 +36,7 @@ bench schema.ast.toJSON() }) .add("TreeFormatter.formatIssueSync", function() { - TreeFormatter.formatIssueSync(result.left) + ParseResult.TreeFormatter.formatIssueSync(result.left) }) await bench.run() diff --git a/packages/effect/benchmark/SchemaTreeFormatter.ts b/packages/effect/benchmark/SchemaTreeFormatter.ts index 38d3eb8a90..6f65134880 100644 --- a/packages/effect/benchmark/SchemaTreeFormatter.ts +++ b/packages/effect/benchmark/SchemaTreeFormatter.ts @@ -1,7 +1,6 @@ import type * as Either from "effect/Either" -import type * as ParseResult from "effect/ParseResult" +import * as ParseResult from "effect/ParseResult" import * as S from "effect/Schema" -import * as TreeFormatter from "effect/SchemaTreeFormatter" import { Bench } from "tinybench" /* @@ -35,7 +34,7 @@ bench decodeUnknownEither(input) }) .add("TreeFormatter.formatIssueSync(issue)", function() { - TreeFormatter.formatIssueSync(issue) + ParseResult.TreeFormatter.formatIssueSync(issue) }) await bench.run() diff --git a/packages/effect/src/Arbitrary.ts b/packages/effect/src/Arbitrary.ts index 7ca745982d..11ebfa3621 100644 --- a/packages/effect/src/Arbitrary.ts +++ b/packages/effect/src/Arbitrary.ts @@ -2,13 +2,13 @@ * @since 3.10.0 */ -import * as Arr from "effect/Array" -import * as Option from "effect/Option" -import * as Predicate from "effect/Predicate" +import * as Arr from "./Array.js" import * as FastCheck from "./FastCheck.js" import * as errors_ from "./internal/schema/errors.js" import * as filters_ from "./internal/schema/filters.js" import * as util_ from "./internal/schema/util.js" +import * as Option from "./Option.js" +import * as Predicate from "./Predicate.js" import type * as Schema from "./Schema.js" import * as AST from "./SchemaAST.js" diff --git a/packages/effect/src/JSONSchema.ts b/packages/effect/src/JSONSchema.ts index 21c25d2052..fd87635b72 100644 --- a/packages/effect/src/JSONSchema.ts +++ b/packages/effect/src/JSONSchema.ts @@ -2,11 +2,11 @@ * @since 3.10.0 */ -import * as Option from "effect/Option" -import * as Predicate from "effect/Predicate" -import * as Record from "effect/Record" import * as errors_ from "./internal/schema/errors.js" import * as filters_ from "./internal/schema/filters.js" +import * as Option from "./Option.js" +import * as Predicate from "./Predicate.js" +import * as Record from "./Record.js" import type * as Schema from "./Schema.js" import * as AST from "./SchemaAST.js" diff --git a/packages/effect/src/ParseResult.ts b/packages/effect/src/ParseResult.ts index cb1326a03f..1339939f65 100644 --- a/packages/effect/src/ParseResult.ts +++ b/packages/effect/src/ParseResult.ts @@ -2,21 +2,21 @@ * @since 3.10.0 */ -import * as array_ from "effect/Array" -import { TaggedError } from "effect/Data" -import * as Effect from "effect/Effect" -import * as Either from "effect/Either" -import type { LazyArg } from "effect/Function" -import { dual } from "effect/Function" -import { globalValue } from "effect/GlobalValue" -import * as Inspectable from "effect/Inspectable" -import * as Option from "effect/Option" -import * as Predicate from "effect/Predicate" -import type { Concurrency } from "effect/Types" +import * as array_ from "./Array.js" +import type * as cause_ from "./Cause.js" +import { TaggedError } from "./Data.js" +import * as Effect from "./Effect.js" +import * as Either from "./Either.js" +import type { LazyArg } from "./Function.js" +import { dual } from "./Function.js" +import { globalValue } from "./GlobalValue.js" +import * as Inspectable from "./Inspectable.js" import * as util_ from "./internal/schema/util.js" +import * as Option from "./Option.js" +import * as Predicate from "./Predicate.js" import type * as Schema from "./Schema.js" import * as AST from "./SchemaAST.js" -import * as TreeFormatter from "./SchemaTreeFormatter.js" +import type { Concurrency } from "./Types.js" /** * `ParseIssue` is a type that represents the different types of errors that can occur when decoding/encoding a value. @@ -130,14 +130,6 @@ export class Composite { ) {} } -/** - * Returns `true` if the value is a `Composite`. - * - * @category guards - * @since 3.10.0 - */ -export const isComposite = (u: unknown): u is Composite => Predicate.hasProperty(u, "_tag") - /** * Error that occurs when a refinement has an error. * @@ -1718,3 +1710,273 @@ export const getFinalTransformation = ( } } } + +// ---------------- +// Formatters +// ---------------- + +interface Forest extends ReadonlyArray> {} + +interface Tree { + readonly value: A + readonly forest: Forest +} + +const makeTree = (value: A, forest: Forest = []): Tree => ({ + value, + forest +}) + +/** + * @category formatting + * @since 3.10.0 + */ +export interface ParseResultFormatter { + readonly formatIssue: (issue: ParseIssue) => Effect.Effect + readonly formatIssueSync: (issue: ParseIssue) => A + readonly formatError: (error: ParseError) => Effect.Effect + readonly formatErrorSync: (error: ParseError) => A +} + +/** + * @category formatting + * @since 3.10.0 + */ +export const TreeFormatter: ParseResultFormatter = { + formatIssue: (issue) => Effect.map(formatTree(issue), drawTree), + formatIssueSync: (issue) => Effect.runSync(TreeFormatter.formatIssue(issue)), + formatError: (error) => TreeFormatter.formatIssue(error.issue), + formatErrorSync: (error) => TreeFormatter.formatIssueSync(error.issue) +} + +const drawTree = (tree: Tree): string => tree.value + draw("\n", tree.forest) + +const draw = (indentation: string, forest: Forest): string => { + let r = "" + const len = forest.length + let tree: Tree + for (let i = 0; i < len; i++) { + tree = forest[i] + const isLast = i === len - 1 + r += indentation + (isLast ? "└" : "├") + "─ " + tree.value + r += draw(indentation + (len > 1 && !isLast ? "│ " : " "), tree.forest) + } + return r +} + +const formatTransformationKind = (kind: Transformation["kind"]): string => { + switch (kind) { + case "Encoded": + return "Encoded side transformation failure" + case "Transformation": + return "Transformation process failure" + case "Type": + return "Type side transformation failure" + } +} + +const formatRefinementKind = (kind: Refinement["kind"]): string => { + switch (kind) { + case "From": + return "From side refinement failure" + case "Predicate": + return "Predicate refinement failure" + } +} + +const getAnnotated = (issue: ParseIssue): Option.Option => + "ast" in issue ? Option.some(issue.ast) : Option.none() + +interface CurrentMessage { + readonly message: string + readonly override: boolean +} + +const getCurrentMessage = ( + issue: ParseIssue +): Effect.Effect => + getAnnotated(issue).pipe( + Option.flatMap(AST.getMessageAnnotation), + Effect.flatMap((annotation) => { + const out = annotation(issue) + return Predicate.isString(out) + ? Effect.succeed({ message: out, override: false }) + : Effect.isEffect(out) + ? Effect.map(out, (message) => ({ message, override: false })) + : Predicate.isString(out.message) + ? Effect.succeed({ message: out.message, override: out.override }) + : Effect.map(out.message, (message) => ({ message, override: out.override })) + }) + ) + +const createParseIssueGuard = + (tag: T) => (issue: ParseIssue): issue is Extract => + issue._tag === tag + +/** + * Returns `true` if the value is a `Composite`. + * + * @category guards + * @since 3.10.0 + */ +export const isComposite = createParseIssueGuard("Composite") + +const isRefinement = createParseIssueGuard("Refinement") +const isTransformation = createParseIssueGuard("Transformation") + +const getMessage: ( + issue: ParseIssue +) => Effect.Effect = (issue: ParseIssue) => + getCurrentMessage(issue).pipe( + Effect.flatMap((currentMessage) => { + const useInnerMessage = !currentMessage.override && ( + isComposite(issue) || + (isRefinement(issue) && issue.kind === "From") || + (isTransformation(issue) && issue.kind !== "Transformation") + ) + return useInnerMessage + ? isTransformation(issue) || isRefinement(issue) ? getMessage(issue.issue) : Option.none() + : Effect.succeed(currentMessage.message) + }) + ) + +const getParseIssueTitleAnnotation = (issue: ParseIssue): Option.Option => + getAnnotated(issue).pipe( + Option.flatMap(AST.getParseIssueTitleAnnotation), + Option.filterMap( + (annotation) => Option.fromNullable(annotation(issue)) + ) + ) + +const formatTypeMessage = (e: Type): Effect.Effect => + getMessage(e).pipe( + Effect.orElse(() => getParseIssueTitleAnnotation(e)), + Effect.catchAll(() => + Effect.succeed(e.message ?? `Expected ${String(e.ast)}, actual ${util_.formatUnknown(e.actual)}`) + ) + ) + +const getParseIssueTitle = ( + issue: Forbidden | Transformation | Refinement | Composite +): string => Option.getOrElse(getParseIssueTitleAnnotation(issue), () => String(issue.ast)) + +const formatForbiddenMessage = (e: Forbidden): string => e.message ?? "is forbidden" + +const formatUnexpectedMessage = (e: Unexpected): string => e.message ?? "is unexpected" + +const formatMissingMessage = (e: Missing): Effect.Effect => + AST.getMissingMessageAnnotation(e.ast).pipe( + Effect.flatMap((annotation) => { + const out = annotation() + return Predicate.isString(out) ? Effect.succeed(out) : out + }), + Effect.catchAll(() => Effect.succeed(e.message ?? "is missing")) + ) + +const getTree = (issue: ParseIssue, onFailure: () => Effect.Effect>) => + Effect.matchEffect(getMessage(issue), { + onFailure, + onSuccess: (message) => Effect.succeed(makeTree(message)) + }) + +const formatTree = ( + e: ParseIssue | Pointer +): Effect.Effect> => { + switch (e._tag) { + case "Type": + return Effect.map(formatTypeMessage(e), makeTree) + case "Forbidden": + return Effect.succeed(makeTree(getParseIssueTitle(e), [makeTree(formatForbiddenMessage(e))])) + case "Unexpected": + return Effect.succeed(makeTree(formatUnexpectedMessage(e))) + case "Missing": + return Effect.map(formatMissingMessage(e), makeTree) + case "Transformation": + return getTree(e, () => + Effect.map( + formatTree(e.issue), + (tree) => makeTree(getParseIssueTitle(e), [makeTree(formatTransformationKind(e.kind), [tree])]) + )) + case "Refinement": + return getTree( + e, + () => + Effect.map( + formatTree(e.issue), + (tree) => makeTree(getParseIssueTitle(e), [makeTree(formatRefinementKind(e.kind), [tree])]) + ) + ) + case "Pointer": + return Effect.map(formatTree(e.issue), (tree) => makeTree(util_.formatPath(e.path), [tree])) + case "Composite": { + const parseIssueTitle = getParseIssueTitle(e) + return getTree( + e, + () => + util_.isNonEmpty(e.issues) + ? Effect.map(Effect.forEach(e.issues, formatTree), (forest) => makeTree(parseIssueTitle, forest)) + : Effect.map(formatTree(e.issues), (tree) => makeTree(parseIssueTitle, [tree])) + ) + } + } +} + +/** + * @category model + * @since 3.10.0 + */ +export interface ArrayFormatterIssue { + readonly _tag: ParseIssue["_tag"] + readonly path: ReadonlyArray + readonly message: string +} + +/** + * @category formatting + * @since 3.10.0 + */ +export const ArrayFormatter: ParseResultFormatter> = { + formatIssue: (issue) => formatArray(issue), + formatIssueSync: (issue) => Effect.runSync(ArrayFormatter.formatIssue(issue)), + formatError: (error) => ArrayFormatter.formatIssue(error.issue), + formatErrorSync: (error) => ArrayFormatter.formatIssueSync(error.issue) +} + +const succeedArrayFormatterIssue = (issue: ArrayFormatterIssue) => Effect.succeed([issue]) + +const getArray = ( + issue: ParseIssue, + path: ReadonlyArray, + onFailure: () => Effect.Effect> +) => + Effect.matchEffect(getMessage(issue), { + onFailure, + onSuccess: (message) => succeedArrayFormatterIssue({ _tag: issue._tag, path, message }) + }) + +const formatArray = ( + e: ParseIssue | Pointer, + path: ReadonlyArray = [] +): Effect.Effect> => { + const _tag = e._tag + switch (_tag) { + case "Type": + return Effect.map(formatTypeMessage(e), (message) => [{ _tag, path, message }]) + case "Forbidden": + return succeedArrayFormatterIssue({ _tag, path, message: formatForbiddenMessage(e) }) + case "Unexpected": + return succeedArrayFormatterIssue({ _tag, path, message: formatUnexpectedMessage(e) }) + case "Missing": + return Effect.map(formatMissingMessage(e), (message) => [{ _tag, path, message }]) + case "Pointer": + return formatArray(e.issue, path.concat(e.path)) + case "Composite": + return getArray(e, path, () => + util_.isNonEmpty(e.issues) + ? Effect.map(Effect.forEach(e.issues, (issue) => formatArray(issue, path)), array_.flatten) + : formatArray(e.issues, path)) + case "Refinement": + case "Transformation": + return getArray(e, path, () => formatArray(e.issue, path)) + } +} diff --git a/packages/effect/src/Pretty.ts b/packages/effect/src/Pretty.ts index 6c31b85fb5..8da7db6e31 100644 --- a/packages/effect/src/Pretty.ts +++ b/packages/effect/src/Pretty.ts @@ -1,10 +1,10 @@ /** * @since 3.10.0 */ -import * as Arr from "effect/Array" -import * as Option from "effect/Option" +import * as Arr from "./Array.js" import * as errors_ from "./internal/schema/errors.js" import * as util_ from "./internal/schema/util.js" +import * as Option from "./Option.js" import * as ParseResult from "./ParseResult.js" import type * as Schema from "./Schema.js" import * as AST from "./SchemaAST.js" diff --git a/packages/effect/src/Schema.ts b/packages/effect/src/Schema.ts index 0a7b1ce29b..2cf49aa73f 100644 --- a/packages/effect/src/Schema.ts +++ b/packages/effect/src/Schema.ts @@ -2,57 +2,56 @@ * @since 3.10.0 */ -import * as array_ from "effect/Array" -import * as bigDecimal_ from "effect/BigDecimal" -import * as bigInt_ from "effect/BigInt" -import * as boolean_ from "effect/Boolean" -import type { Brand } from "effect/Brand" -import * as cause_ from "effect/Cause" -import * as chunk_ from "effect/Chunk" -import * as config_ from "effect/Config" -import * as configError_ from "effect/ConfigError" -import * as data_ from "effect/Data" -import * as dateTime from "effect/DateTime" -import * as duration_ from "effect/Duration" -import * as Effect from "effect/Effect" -import * as either_ from "effect/Either" -import * as Encoding from "effect/Encoding" -import * as Equal from "effect/Equal" -import * as Equivalence from "effect/Equivalence" -import * as exit_ from "effect/Exit" -import * as fiberId_ from "effect/FiberId" -import type { LazyArg } from "effect/Function" -import { dual, identity } from "effect/Function" -import * as hashMap_ from "effect/HashMap" -import * as hashSet_ from "effect/HashSet" -import * as list_ from "effect/List" -import * as number_ from "effect/Number" -import * as option_ from "effect/Option" -import type * as Order from "effect/Order" -import type { Pipeable } from "effect/Pipeable" -import { pipeArguments } from "effect/Pipeable" -import * as Predicate from "effect/Predicate" -import * as record_ from "effect/Record" -import * as redacted_ from "effect/Redacted" -import * as Request from "effect/Request" -import * as sortedSet_ from "effect/SortedSet" -import * as string_ from "effect/String" -import * as struct_ from "effect/Struct" -import type * as Types from "effect/Types" import * as arbitrary_ from "./Arbitrary.js" import type { GenerationContext, 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 * as hashMap_ from "./HashMap.js" +import * as hashSet_ from "./HashSet.js" import * as errors_ from "./internal/schema/errors.js" import * as filters_ from "./internal/schema/filters.js" import * as serializable_ from "./internal/schema/serializable.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 * as pretty_ from "./Pretty.js" +import * as record_ from "./Record.js" +import * as redacted_ from "./Redacted.js" +import * as Request from "./Request.js" import type { ParseOptions } from "./SchemaAST.js" import * as AST from "./SchemaAST.js" import * as equivalence_ from "./SchemaEquivalence.js" -import * as TreeFormatter from "./SchemaTreeFormatter.js" import type * as Serializable from "./Serializable.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 @@ -644,14 +643,14 @@ export function Literal>( * Creates a new `Schema` from a literal schema. * * @example - * import * as S from "effect/Schema" + * import * as Schema from "effect/Schema" * import { Either } from "effect" * - * const schema = S.Literal("a", "b", "c").pipe(S.pickLiteral("a", "b")) + * const schema = Schema.Literal("a", "b", "c").pipe(Schema.pickLiteral("a", "b")) * - * assert.deepStrictEqual(S.decodeSync(schema)("a"), "a") - * assert.deepStrictEqual(S.decodeSync(schema)("b"), "b") - * assert.strictEqual(Either.isLeft(S.decodeUnknownEither(schema)("c")), true) + * 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 @@ -2833,24 +2832,24 @@ export const omit = >(...key * producing a new schema that represents a transformation from the `{ readonly [key]: I[K] }` type to `A[K]`. * * @example - * import * as S from "effect/Schema" + * import * as Schema from "effect/Schema" * * // --------------------------------------------- * // use case: pull out a single field from a * // struct through a transformation * // --------------------------------------------- * - * const mytable = S.Struct({ - * column1: S.NumberFromString, - * column2: S.Number + * const mytable = Schema.Struct({ + * column1: Schema.NumberFromString, + * column2: Schema.Number * }) * * // const pullOutColumn: S.Schema - * const pullOutColumn = mytable.pipe(S.pluck("column1")) + * const pullOutColumn = mytable.pipe(Schema.pluck("column1")) * - * console.log(S.decodeUnknownEither(S.Array(pullOutColumn))([{ column1: "1", column2: 100 }, { column1: "2", column2: 300 }])) + * 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 @@ -4494,10 +4493,10 @@ const getParseJsonTransformation = (options?: ParseJsonOptions) => * Optionally, you can pass a schema `Schema` to obtain an `A` type instead of `unknown`. * * @example - * import * as S from "effect/Schema" + * import * as Schema from "effect/Schema" * - * assert.deepStrictEqual(S.decodeUnknownSync(S.parseJson())(`{"a":"1"}`), { a: "1" }) - * assert.deepStrictEqual(S.decodeUnknownSync(S.parseJson(S.Struct({ a: S.NumberFromString })))(`{"a":"1"}`), { a: 1 }) + * assert.deepStrictEqual(Schema.decodeUnknownSync(Schema.parseJson())(`{"a":"1"}`), { a: "1" }) + * assert.deepStrictEqual(Schema.decodeUnknownSync(Schema.parseJson(Schema.Struct({ a: Schema.NumberFromString })))(`{"a":"1"}`), { a: 1 }) * * @category string transformations * @since 3.10.0 @@ -4972,9 +4971,9 @@ export const JsonNumberTypeId: unique symbol = Symbol.for("effect/Schema/TypeId/ * format. * * @example - * import * as S from "effect/Schema" + * import * as Schema from "effect/Schema" * - * const is = S.is(S.JsonNumber) + * const is = Schema.is(S.JsonNumber) * * assert.deepStrictEqual(is(42), true) * assert.deepStrictEqual(is(Number.NaN), false) @@ -9263,7 +9262,7 @@ export const Config = (name: string, schema: Schema): config_.Conf return config_.string(name).pipe( config_.mapOrFail((a) => decodeEither_(a).pipe( - either_.mapLeft((error) => configError_.InvalidData([], TreeFormatter.formatErrorSync(error))) + either_.mapLeft((error) => configError_.InvalidData([], ParseResult.TreeFormatter.formatErrorSync(error))) ) ) ) diff --git a/packages/effect/src/SchemaAST.ts b/packages/effect/src/SchemaAST.ts index d32937d49d..51a4baf01d 100644 --- a/packages/effect/src/SchemaAST.ts +++ b/packages/effect/src/SchemaAST.ts @@ -2,19 +2,19 @@ * @since 3.10.0 */ -import * as Arr from "effect/Array" -import type { Effect } from "effect/Effect" -import { dual, identity } from "effect/Function" -import { globalValue } from "effect/GlobalValue" -import * as Number from "effect/Number" -import * as Option from "effect/Option" -import * as Order from "effect/Order" -import * as Predicate from "effect/Predicate" -import * as regexp from "effect/RegExp" -import type { Concurrency } from "effect/Types" +import * as Arr from "./Array.js" +import type { Effect } from "./Effect.js" +import { dual, identity } from "./Function.js" +import { globalValue } from "./GlobalValue.js" import * as errors_ from "./internal/schema/errors.js" import * as util_ from "./internal/schema/util.js" +import * as Number from "./Number.js" +import * as Option from "./Option.js" +import * as Order from "./Order.js" import type { ParseIssue } from "./ParseResult.js" +import * as Predicate from "./Predicate.js" +import * as regexp from "./RegExp.js" +import type { Concurrency } from "./Types.js" /** * @category model diff --git a/packages/effect/src/SchemaArrayFormatter.ts b/packages/effect/src/SchemaArrayFormatter.ts deleted file mode 100644 index 0fad45e13e..0000000000 --- a/packages/effect/src/SchemaArrayFormatter.ts +++ /dev/null @@ -1,82 +0,0 @@ -/** - * @since 3.10.0 - */ - -import * as array_ from "effect/Array" -import * as Effect from "effect/Effect" -import * as util_ from "./internal/schema/util.js" -import type * as ParseResult from "./ParseResult.js" -import * as TreeFormatter from "./SchemaTreeFormatter.js" - -/** - * @category model - * @since 3.10.0 - */ -export interface Issue { - readonly _tag: ParseResult.ParseIssue["_tag"] - readonly path: ReadonlyArray - readonly message: string -} - -/** - * @category formatting - * @since 3.10.0 - */ -export const formatIssue = (issue: ParseResult.ParseIssue): Effect.Effect> => go(issue) - -/** - * @category formatting - * @since 3.10.0 - */ -export const formatIssueSync = (issue: ParseResult.ParseIssue): Array => Effect.runSync(formatIssue(issue)) - -/** - * @category formatting - * @since 3.10.0 - */ -export const formatError = (error: ParseResult.ParseError): Effect.Effect> => formatIssue(error.issue) - -/** - * @category formatting - * @since 3.10.0 - */ -export const formatErrorSync = (error: ParseResult.ParseError): Array => formatIssueSync(error.issue) - -const succeed = (issue: Issue) => Effect.succeed([issue]) - -const getArray = ( - issue: ParseResult.ParseIssue, - path: ReadonlyArray, - onFailure: () => Effect.Effect> -) => - Effect.matchEffect(TreeFormatter.getMessage(issue), { - onFailure, - onSuccess: (message) => succeed({ _tag: issue._tag, path, message }) - }) - -const go = ( - e: ParseResult.ParseIssue | ParseResult.Pointer, - path: ReadonlyArray = [] -): Effect.Effect> => { - const _tag = e._tag - switch (_tag) { - case "Type": - return Effect.map(TreeFormatter.formatTypeMessage(e), (message) => [{ _tag, path, message }]) - case "Forbidden": - return succeed({ _tag, path, message: TreeFormatter.formatForbiddenMessage(e) }) - case "Unexpected": - return succeed({ _tag, path, message: TreeFormatter.formatUnexpectedMessage(e) }) - case "Missing": - return Effect.map(TreeFormatter.formatMissingMessage(e), (message) => [{ _tag, path, message }]) - case "Pointer": - return go(e.issue, path.concat(e.path)) - case "Composite": - return getArray(e, path, () => - util_.isNonEmpty(e.issues) - ? Effect.map(Effect.forEach(e.issues, (issue) => go(issue, path)), array_.flatten) - : go(e.issues, path)) - case "Refinement": - case "Transformation": - return getArray(e, path, () => go(e.issue, path)) - } -} diff --git a/packages/effect/src/SchemaEquivalence.ts b/packages/effect/src/SchemaEquivalence.ts index 9da0355342..e29b4349e7 100644 --- a/packages/effect/src/SchemaEquivalence.ts +++ b/packages/effect/src/SchemaEquivalence.ts @@ -2,14 +2,14 @@ * @since 3.10.0 */ -import * as Arr from "effect/Array" -import * as Equal from "effect/Equal" -import * as Equivalence from "effect/Equivalence" -import * as Option from "effect/Option" -import * as Predicate from "effect/Predicate" +import * as Arr from "./Array.js" +import * as Equal from "./Equal.js" +import * as Equivalence from "./Equivalence.js" import * as errors_ from "./internal/schema/errors.js" import * as util_ from "./internal/schema/util.js" +import * as Option from "./Option.js" import * as ParseResult from "./ParseResult.js" +import * as Predicate from "./Predicate.js" import type * as Schema from "./Schema.js" import * as AST from "./SchemaAST.js" diff --git a/packages/effect/src/SchemaTreeFormatter.ts b/packages/effect/src/SchemaTreeFormatter.ts deleted file mode 100644 index faa720ee89..0000000000 --- a/packages/effect/src/SchemaTreeFormatter.ts +++ /dev/null @@ -1,215 +0,0 @@ -/** - * @since 3.10.0 - */ - -import type * as Cause from "effect/Cause" -import * as Effect from "effect/Effect" -import * as Option from "effect/Option" -import * as Predicate from "effect/Predicate" -import * as util_ from "./internal/schema/util.js" -import type * as ParseResult from "./ParseResult.js" -import * as AST from "./SchemaAST.js" - -interface Forest extends ReadonlyArray> {} - -interface Tree { - readonly value: A - readonly forest: Forest -} - -const make = (value: A, forest: Forest = []): Tree => ({ - value, - forest -}) - -/** - * @category formatting - * @since 3.10.0 - */ -export const formatIssue = (issue: ParseResult.ParseIssue): Effect.Effect => - Effect.map(go(issue), (tree) => drawTree(tree)) - -/** - * @category formatting - * @since 3.10.0 - */ -export const formatIssueSync = (issue: ParseResult.ParseIssue): string => Effect.runSync(formatIssue(issue)) - -/** - * @category formatting - * @since 3.10.0 - */ -export const formatError = (error: ParseResult.ParseError): Effect.Effect => formatIssue(error.issue) - -/** - * @category formatting - * @since 3.10.0 - */ -export const formatErrorSync = (error: ParseResult.ParseError): string => formatIssueSync(error.issue) - -const drawTree = (tree: Tree): string => tree.value + draw("\n", tree.forest) - -const draw = (indentation: string, forest: Forest): string => { - let r = "" - const len = forest.length - let tree: Tree - for (let i = 0; i < len; i++) { - tree = forest[i] - const isLast = i === len - 1 - r += indentation + (isLast ? "└" : "├") + "─ " + tree.value - r += draw(indentation + (len > 1 && !isLast ? "│ " : " "), tree.forest) - } - return r -} - -const formatTransformationKind = (kind: ParseResult.Transformation["kind"]): string => { - switch (kind) { - case "Encoded": - return "Encoded side transformation failure" - case "Transformation": - return "Transformation process failure" - case "Type": - return "Type side transformation failure" - } -} - -const formatRefinementKind = (kind: ParseResult.Refinement["kind"]): string => { - switch (kind) { - case "From": - return "From side refinement failure" - case "Predicate": - return "Predicate refinement failure" - } -} - -const getAnnotated = (issue: ParseResult.ParseIssue): Option.Option => - "ast" in issue ? Option.some(issue.ast) : Option.none() - -interface CurrentMessage { - readonly message: string - readonly override: boolean -} - -const getCurrentMessage = ( - issue: ParseResult.ParseIssue -): Effect.Effect => - getAnnotated(issue).pipe( - Option.flatMap(AST.getMessageAnnotation), - Effect.flatMap((annotation) => { - const out = annotation(issue) - return Predicate.isString(out) - ? Effect.succeed({ message: out, override: false }) - : Effect.isEffect(out) - ? Effect.map(out, (message) => ({ message, override: false })) - : Predicate.isString(out.message) - ? Effect.succeed({ message: out.message, override: out.override }) - : Effect.map(out.message, (message) => ({ message, override: out.override })) - }) - ) - -const createParseIssueGuard = - (tag: T) => - (issue: ParseResult.ParseIssue): issue is Extract => issue._tag === tag - -const isComposite = createParseIssueGuard("Composite") -const isRefinement = createParseIssueGuard("Refinement") -const isTransformation = createParseIssueGuard("Transformation") - -/** @internal */ -export const getMessage: ( - issue: ParseResult.ParseIssue -) => Effect.Effect = (issue: ParseResult.ParseIssue) => - getCurrentMessage(issue).pipe( - Effect.flatMap((currentMessage) => { - const useInnerMessage = !currentMessage.override && ( - isComposite(issue) || - (isRefinement(issue) && issue.kind === "From") || - (isTransformation(issue) && issue.kind !== "Transformation") - ) - return useInnerMessage - ? isTransformation(issue) || isRefinement(issue) ? getMessage(issue.issue) : Option.none() - : Effect.succeed(currentMessage.message) - }) - ) - -const getParseIssueTitleAnnotation = (issue: ParseResult.ParseIssue): Option.Option => - getAnnotated(issue).pipe( - Option.flatMap(AST.getParseIssueTitleAnnotation), - Option.filterMap( - (annotation) => Option.fromNullable(annotation(issue)) - ) - ) - -/** @internal */ -export const formatTypeMessage = (e: ParseResult.Type): Effect.Effect => - getMessage(e).pipe( - Effect.orElse(() => getParseIssueTitleAnnotation(e)), - Effect.catchAll(() => - Effect.succeed(e.message ?? `Expected ${String(e.ast)}, actual ${util_.formatUnknown(e.actual)}`) - ) - ) - -const getParseIssueTitle = ( - issue: ParseResult.Forbidden | ParseResult.Transformation | ParseResult.Refinement | ParseResult.Composite -): string => Option.getOrElse(getParseIssueTitleAnnotation(issue), () => String(issue.ast)) - -/** @internal */ -export const formatForbiddenMessage = (e: ParseResult.Forbidden): string => e.message ?? "is forbidden" - -/** @internal */ -export const formatUnexpectedMessage = (e: ParseResult.Unexpected): string => e.message ?? "is unexpected" - -/** @internal */ -export const formatMissingMessage = (e: ParseResult.Missing): Effect.Effect => - AST.getMissingMessageAnnotation(e.ast).pipe( - Effect.flatMap((annotation) => { - const out = annotation() - return Predicate.isString(out) ? Effect.succeed(out) : out - }), - Effect.catchAll(() => Effect.succeed(e.message ?? "is missing")) - ) - -const getTree = (issue: ParseResult.ParseIssue, onFailure: () => Effect.Effect>) => - Effect.matchEffect(getMessage(issue), { - onFailure, - onSuccess: (message) => Effect.succeed(make(message)) - }) - -const go = ( - e: ParseResult.ParseIssue | ParseResult.Pointer -): Effect.Effect> => { - switch (e._tag) { - case "Type": - return Effect.map(formatTypeMessage(e), make) - case "Forbidden": - return Effect.succeed(make(getParseIssueTitle(e), [make(formatForbiddenMessage(e))])) - case "Unexpected": - return Effect.succeed(make(formatUnexpectedMessage(e))) - case "Missing": - return Effect.map(formatMissingMessage(e), make) - case "Transformation": - return getTree(e, () => - Effect.map( - go(e.issue), - (tree) => make(getParseIssueTitle(e), [make(formatTransformationKind(e.kind), [tree])]) - )) - case "Refinement": - return getTree( - e, - () => - Effect.map(go(e.issue), (tree) => make(getParseIssueTitle(e), [make(formatRefinementKind(e.kind), [tree])])) - ) - case "Pointer": - return Effect.map(go(e.issue), (tree) => make(util_.formatPath(e.path), [tree])) - case "Composite": { - const parseIssueTitle = getParseIssueTitle(e) - return getTree( - e, - () => - util_.isNonEmpty(e.issues) - ? Effect.map(Effect.forEach(e.issues, go), (forest) => make(parseIssueTitle, forest)) - : Effect.map(go(e.issues), (tree) => make(parseIssueTitle, [tree])) - ) - } - } -} diff --git a/packages/effect/src/Serializable.ts b/packages/effect/src/Serializable.ts index eb025e5e0d..060496f90f 100644 --- a/packages/effect/src/Serializable.ts +++ b/packages/effect/src/Serializable.ts @@ -1,10 +1,10 @@ /** * @since 3.10.0 */ -import type * as Effect from "effect/Effect" -import type * as Exit from "effect/Exit" -import { dual } from "effect/Function" -import { globalValue } from "effect/GlobalValue" +import type * as Effect from "./Effect.js" +import type * as Exit from "./Exit.js" +import { dual } from "./Function.js" +import { globalValue } from "./GlobalValue.js" import * as serializable_ from "./internal/schema/serializable.js" import type * as ParseResult from "./ParseResult.js" import * as Schema from "./Schema.js" diff --git a/packages/effect/src/index.ts b/packages/effect/src/index.ts index 8d29c8fa03..388a974c13 100644 --- a/packages/effect/src/index.ts +++ b/packages/effect/src/index.ts @@ -770,21 +770,11 @@ export * as Schema from "./Schema.js" */ export * as SchemaAST from "./SchemaAST.js" -/** - * @since 3.10.0 - */ -export * as SchemaArrayFormatter from "./SchemaArrayFormatter.js" - /** * @since 3.10.0 */ export * as SchemaEquivalence from "./SchemaEquivalence.js" -/** - * @since 3.10.0 - */ -export * as SchemaTreeFormatter from "./SchemaTreeFormatter.js" - /** * @since 2.0.0 */ diff --git a/packages/effect/test/Schema/SchemaFormatters.test.ts b/packages/effect/test/Schema/ParseResultFormatter.test.ts similarity index 98% rename from packages/effect/test/Schema/SchemaFormatters.test.ts rename to packages/effect/test/Schema/ParseResultFormatter.test.ts index 51d83e9357..2864187894 100644 --- a/packages/effect/test/Schema/SchemaFormatters.test.ts +++ b/packages/effect/test/Schema/ParseResultFormatter.test.ts @@ -5,23 +5,21 @@ import { identity, pipe } from "effect/Function" import * as Option from "effect/Option" import * as ParseResult from "effect/ParseResult" import * as S from "effect/Schema" -import * as ArrayFormatter from "effect/SchemaArrayFormatter" import type { ParseOptions } from "effect/SchemaAST" import * as AST from "effect/SchemaAST" -import * as TreeFormatter from "effect/SchemaTreeFormatter" import * as Util from "effect/test/Schema/TestUtils" import { describe, expect, it } from "vitest" const options: ParseOptions = { errors: "all", onExcessProperty: "error" } -const expectIssues = (schema: S.Schema, input: unknown, issues: Array) => { +const expectIssues = (schema: S.Schema, input: unknown, issues: Array) => { const result = S.decodeUnknownEither(schema)(input, options).pipe( - Either.mapLeft((e) => ArrayFormatter.formatIssueSync(e.issue)) + Either.mapLeft((e) => ParseResult.ArrayFormatter.formatIssueSync(e.issue)) ) expect(result).toStrictEqual(Either.left(issues)) } -describe("SchemaFormatters", () => { +describe("ParseResultFormatter", () => { describe("Forbidden", () => { it("default message", () => { const schema = Util.effectify(S.String) @@ -1278,7 +1276,7 @@ it("Effect as message", () => { const result = S.decodeUnknownEither(Name)("") // no service - expect(Either.mapLeft(result, (error) => Effect.runSync(TreeFormatter.formatError(error)))) + expect(Either.mapLeft(result, (error) => Effect.runSync(ParseResult.TreeFormatter.formatError(error)))) .toStrictEqual(Either.left("Invalid string")) // it locale @@ -1287,7 +1285,7 @@ it("Effect as message", () => { result, (error) => Effect.runSync( - TreeFormatter.formatError(error).pipe(Effect.provideService(Translator, { + ParseResult.TreeFormatter.formatError(error).pipe(Effect.provideService(Translator, { locale: "it", translations })) @@ -1301,7 +1299,7 @@ it("Effect as message", () => { result, (error) => Effect.runSync( - TreeFormatter.formatError(error).pipe(Effect.provideService(Translator, { + ParseResult.TreeFormatter.formatError(error).pipe(Effect.provideService(Translator, { locale: "en", translations })) diff --git a/packages/effect/test/Schema/TestUtils.ts b/packages/effect/test/Schema/TestUtils.ts index e573ee0791..ed9a1492df 100644 --- a/packages/effect/test/Schema/TestUtils.ts +++ b/packages/effect/test/Schema/TestUtils.ts @@ -10,7 +10,6 @@ import * as Runtime from "effect/Runtime" import * as S from "effect/Schema" import type { ParseOptions } from "effect/SchemaAST" import * as AST from "effect/SchemaAST" -import { formatErrorSync } from "effect/SchemaTreeFormatter" import * as fc from "fast-check" import { assert, expect } from "vitest" @@ -298,9 +297,10 @@ export const expectEffectFailure = async ( effect: Effect.Effect, message: string ) => { - expect(await Effect.runPromise(Effect.either(Effect.mapError(effect, formatErrorSync)))).toStrictEqual( - Either.left(message) - ) + expect(await Effect.runPromise(Effect.either(Effect.mapError(effect, ParseResult.TreeFormatter.formatErrorSync)))) + .toStrictEqual( + Either.left(message) + ) } export const expectEffectSuccess = async (effect: Effect.Effect, a: A) => { @@ -311,7 +311,7 @@ export const expectEffectSuccess = async (effect: Effect.Effect, a: export const expectEitherLeft = (e: Either.Either, message: string) => { if (Either.isLeft(e)) { - expect(formatErrorSync(e.left)).toStrictEqual(message) + expect(ParseResult.TreeFormatter.formatErrorSync(e.left)).toStrictEqual(message) } else { // eslint-disable-next-line no-console console.log(e.right) diff --git a/packages/experimental/src/Persistence.ts b/packages/experimental/src/Persistence.ts index 054d7ccae0..42f7d5fb2b 100644 --- a/packages/experimental/src/Persistence.ts +++ b/packages/experimental/src/Persistence.ts @@ -11,9 +11,8 @@ import type * as Exit from "effect/Exit" import { identity } from "effect/Function" import * as Layer from "effect/Layer" import * as Option from "effect/Option" -import type * as ParseResult from "effect/ParseResult" +import * as ParseResult from "effect/ParseResult" import * as PrimaryKey from "effect/PrimaryKey" -import * as TreeFormatter from "effect/SchemaTreeFormatter" import type * as Scope from "effect/Scope" import * as Serializable from "effect/Serializable" @@ -52,7 +51,7 @@ export class PersistenceParseError extends TypeIdError(ErrorTypeId, "Persistence } get message() { - return TreeFormatter.formatIssueSync(this.error) + return ParseResult.TreeFormatter.formatIssueSync(this.error) } } diff --git a/packages/platform/src/HttpApiError.ts b/packages/platform/src/HttpApiError.ts index dd73bf4b0a..af486fd5df 100644 --- a/packages/platform/src/HttpApiError.ts +++ b/packages/platform/src/HttpApiError.ts @@ -3,10 +3,8 @@ */ import * as Effect from "effect/Effect" import { identity } from "effect/Function" -import type * as ParseResult from "effect/ParseResult" +import * as ParseResult from "effect/ParseResult" import * as Schema from "effect/Schema" -import * as ArrayFormatter from "effect/SchemaArrayFormatter" -import * as TreeFormatter from "effect/SchemaTreeFormatter" import * as HttpApiSchema from "./HttpApiSchema.js" /** @@ -99,8 +97,8 @@ export class HttpApiDecodeError extends Schema.TaggedError() * @since 1.0.0 */ static fromParseError(error: ParseResult.ParseError): Effect.Effect { - return ArrayFormatter.formatError(error).pipe( - Effect.zip(TreeFormatter.formatError(error)), + return ParseResult.ArrayFormatter.formatError(error).pipe( + Effect.zip(ParseResult.TreeFormatter.formatError(error)), Effect.map(([issues, message]) => new HttpApiDecodeError({ issues, message })) ) } diff --git a/packages/schema/src/ArrayFormatter.ts b/packages/schema/src/ArrayFormatter.ts deleted file mode 100644 index 455b8c98c3..0000000000 --- a/packages/schema/src/ArrayFormatter.ts +++ /dev/null @@ -1,9 +0,0 @@ -/** - * @since 0.67.0 - */ - -/** - * @category re-exports - * @since 0.76.0 - */ -export * from "effect/SchemaArrayFormatter" diff --git a/packages/schema/src/TreeFormatter.ts b/packages/schema/src/TreeFormatter.ts deleted file mode 100644 index ca803d16bb..0000000000 --- a/packages/schema/src/TreeFormatter.ts +++ /dev/null @@ -1,9 +0,0 @@ -/** - * @since 0.67.0 - */ - -/** - * @category re-exports - * @since 0.76.0 - */ -export * from "effect/SchemaTreeFormatter" diff --git a/packages/schema/src/index.ts b/packages/schema/src/index.ts index dec2d54df9..66da0d1729 100644 --- a/packages/schema/src/index.ts +++ b/packages/schema/src/index.ts @@ -8,11 +8,6 @@ export * as AST from "./AST.js" */ export * as Arbitrary from "./Arbitrary.js" -/** - * @since 0.67.0 - */ -export * as ArrayFormatter from "./ArrayFormatter.js" - /** * @since 0.67.0 */ @@ -47,8 +42,3 @@ export * as Schema from "./Schema.js" * @since 0.67.0 */ export * as Serializable from "./Serializable.js" - -/** - * @since 0.67.0 - */ -export * as TreeFormatter from "./TreeFormatter.js" From 06f6d039a2cd3f2795d4fe8cedac91048e66dec0 Mon Sep 17 00:00:00 2001 From: Giulio Canti Date: Mon, 14 Oct 2024 16:32:36 +0200 Subject: [PATCH 04/12] refactor annotations --- packages/effect/dtslint/Schema.ts | 7 +- packages/effect/dtslint/SchemaAST.ts | 4 +- packages/effect/dtslint/SchemaClass.ts | 2 +- packages/effect/src/Arbitrary.ts | 79 +-- packages/effect/src/JSONSchema.ts | 3 +- packages/effect/src/Pretty.ts | 40 +- packages/effect/src/Schema.ts | 606 +++++++++--------- packages/effect/src/SchemaAST.ts | 93 +-- packages/effect/src/SchemaEquivalence.ts | 24 +- .../effect/src/internal/schema/filters.ts | 105 ++- .../test/Schema/Arbitrary/Arbitrary.test.ts | 4 +- packages/effect/test/Schema/Pretty.test.ts | 2 +- .../test/Schema/Schema/annotations.test.ts | 13 + .../effect/test/Schema/Schema/brand.test.ts | 6 +- .../effect/test/Schema/Schema/exports.test.ts | 52 +- .../effect/test/Schema/Schema/filter.test.ts | 4 +- .../test/Schema/Schema/instanceOf.test.ts | 2 +- .../test/Schema/SchemaAST/annotations.test.ts | 2 +- .../test/Schema/SchemaEquivalence.test.ts | 2 +- packages/platform/src/OpenApiJsonSchema.ts | 18 +- 20 files changed, 541 insertions(+), 527 deletions(-) diff --git a/packages/effect/dtslint/Schema.ts b/packages/effect/dtslint/Schema.ts index cc6143a4df..99b86fa43d 100644 --- a/packages/effect/dtslint/Schema.ts +++ b/packages/effect/dtslint/Schema.ts @@ -62,9 +62,14 @@ hole>>() // S.annotations // --------------------------------------------- -// @ts-expect-error +// should allow to add custom string annotations to a schema +// $ExpectType SchemaClass S.String.annotations({ a: 1 }) +// should allow to add custom symbol annotations to a schema +// $ExpectType SchemaClass +S.String.annotations({ [Symbol.for("a")]: 1 }) + /** * @category api interface * @since 1.0.0 diff --git a/packages/effect/dtslint/SchemaAST.ts b/packages/effect/dtslint/SchemaAST.ts index 40b684d46e..794554af31 100644 --- a/packages/effect/dtslint/SchemaAST.ts +++ b/packages/effect/dtslint/SchemaAST.ts @@ -4,8 +4,10 @@ import * as AST from "effect/SchemaAST" // annotations // --------------------------------------------- -// @ts-expect-error +// should allow to add custom string annotations to a schema +// $ExpectType AST AST.annotations(AST.stringKeyword, { a: 1 }) +// should allow to add custom symbol annotations to a schema // $ExpectType AST AST.annotations(AST.stringKeyword, { [Symbol.for("a")]: 1 }) diff --git a/packages/effect/dtslint/SchemaClass.ts b/packages/effect/dtslint/SchemaClass.ts index 6ad1e25526..1198c34d4f 100644 --- a/packages/effect/dtslint/SchemaClass.ts +++ b/packages/effect/dtslint/SchemaClass.ts @@ -6,7 +6,7 @@ import * as S from "effect/Schema" // --------------------------------------------- type HasFields = S.Struct | { - readonly [S.refineTypeId]: HasFields + readonly [S.RefineSchemaId]: HasFields } declare const checkForConflicts: ( diff --git a/packages/effect/src/Arbitrary.ts b/packages/effect/src/Arbitrary.ts index 11ebfa3621..1e32ad4887 100644 --- a/packages/effect/src/Arbitrary.ts +++ b/packages/effect/src/Arbitrary.ts @@ -21,41 +21,30 @@ export interface LazyArbitrary { } /** - * @category hooks - * @since 3.10.0 - */ -export const ArbitraryHookId: unique symbol = Symbol.for("effect/Schema/ArbitraryHookId") - -/** - * @category hooks - * @since 3.10.0 - */ -export type ArbitraryHookId = typeof ArbitraryHookId - -/** - * @category hooks + * @category annotations * @since 3.10.0 */ -export interface GenerationContext { +export interface ArbitraryGenerationContext { readonly depthIdentifier?: string readonly maxDepth: number } /** - * @category hooks + * @category annotations * @since 3.10.0 */ -export type ArbitraryAnnotation = ( - ...args: [...ReadonlyArray>, GenerationContext] +export type ArbitraryAnnotation = readonly []> = ( + ...arbitraries: [ + ...{ readonly [K in keyof TypeParameters]: LazyArbitrary }, + ctx: ArbitraryGenerationContext + ] ) => LazyArbitrary /** * @category annotations * @since 3.10.0 */ -export const arbitrary = - (annotation: ArbitraryAnnotation) => (self: Schema.Schema): Schema.Schema => - self.annotations({ [ArbitraryHookId]: annotation }) +export const ArbitraryAnnotationId: unique symbol = Symbol.for("effect/annotation/Arbitrary") /** * Returns a LazyArbitrary for the `A` type of the provided schema. @@ -74,7 +63,7 @@ export const makeLazy = (schema: Schema.Schema): LazyArbitrary */ export const make = (schema: Schema.Schema): FastCheck.Arbitrary => makeLazy(schema)(FastCheck) -const getHook = AST.getAnnotation>(ArbitraryHookId) +const getAnnotation = AST.getAnnotation>(ArbitraryAnnotationId) const getRefinementFromArbitrary = ( ast: AST.Refinement, @@ -121,7 +110,7 @@ const getSuspendedArray = ( ) } -interface Context extends GenerationContext { +interface Context extends ArbitraryGenerationContext { readonly constraints?: Constraints } @@ -130,15 +119,15 @@ const go = ( ctx: Context, path: ReadonlyArray ): LazyArbitrary => { - const hook = getHook(ast) - if (Option.isSome(hook)) { + const annotation = getAnnotation(ast) + if (Option.isSome(annotation)) { switch (ast._tag) { case "Declaration": - return hook.value(...ast.typeParameters.map((p) => go(p, ctx, path)), ctx) + return annotation.value(...ast.typeParameters.map((p) => go(p, ctx, path)), ctx) case "Refinement": - return hook.value(getRefinementFromArbitrary(ast, ctx, path), ctx) + return annotation.value(getRefinementFromArbitrary(ast, ctx, path), ctx) default: - return hook.value(ctx) + return annotation.value(ctx) } } switch (ast._tag) { @@ -442,40 +431,40 @@ export type Constraints = /** @internal */ export const getConstraints = (ast: AST.Refinement): Constraints | undefined => { - const TypeAnnotationId = ast.annotations[AST.TypeAnnotationId] + const TypeAnnotationId = ast.annotations[AST.SchemaIdAnnotationId] const jsonSchema: any = ast.annotations[AST.JSONSchemaAnnotationId] switch (TypeAnnotationId) { // int - case filters_.IntTypeId: + case filters_.IntSchemaId: return new IntegerConstraints({}) // number - case filters_.GreaterThanTypeId: - case filters_.GreaterThanOrEqualToTypeId: - case filters_.LessThanTypeId: - case filters_.LessThanOrEqualToTypeId: - case filters_.BetweenTypeId: + case filters_.GreaterThanSchemaId: + case filters_.GreaterThanOrEqualToSchemaId: + case filters_.LessThanSchemaId: + case filters_.LessThanOrEqualToSchemaId: + case filters_.BetweenSchemaId: return new NumberConstraints({ min: jsonSchema.exclusiveMinimum ?? jsonSchema.minimum, max: jsonSchema.exclusiveMaximum ?? jsonSchema.maximum }) // bigint - case filters_.GreaterThanBigintTypeId: - case filters_.GreaterThanOrEqualToBigIntTypeId: - case filters_.LessThanBigIntTypeId: - case filters_.LessThanOrEqualToBigIntTypeId: - case filters_.BetweenBigintTypeId: { + case filters_.GreaterThanBigintSchemaId: + case filters_.GreaterThanOrEqualToBigIntSchemaId: + case filters_.LessThanBigIntSchemaId: + case filters_.LessThanOrEqualToBigIntSchemaId: + case filters_.BetweenBigintSchemaId: { const constraints: any = ast.annotations[TypeAnnotationId] return new BigIntConstraints(constraints) } // string - case filters_.MinLengthTypeId: - case filters_.MaxLengthTypeId: - case filters_.LengthTypeId: + case filters_.MinLengthSchemaId: + case filters_.MaxLengthSchemaId: + case filters_.LengthSchemaId: return new StringConstraints(jsonSchema) // array - case filters_.MinItemsTypeId: - case filters_.MaxItemsTypeId: - case filters_.ItemsCountTypeId: + case filters_.MinItemsSchemaId: + case filters_.MaxItemsSchemaId: + case filters_.ItemsCountSchemaId: return new ArrayConstraints({ minLength: jsonSchema.minItems, maxLength: jsonSchema.maxItems diff --git a/packages/effect/src/JSONSchema.ts b/packages/effect/src/JSONSchema.ts index fd87635b72..a6e138db81 100644 --- a/packages/effect/src/JSONSchema.ts +++ b/packages/effect/src/JSONSchema.ts @@ -3,7 +3,6 @@ */ import * as errors_ from "./internal/schema/errors.js" -import * as filters_ from "./internal/schema/filters.js" import * as Option from "./Option.js" import * as Predicate from "./Predicate.js" import * as Record from "./Record.js" @@ -319,7 +318,7 @@ const getRefinementInnerTransformation = (ast: AST.Refinement): AST.AST | undefi } const isParseJsonTransformation = (ast: AST.AST): boolean => - ast.annotations[AST.TypeAnnotationId] === filters_.ParseJsonTypeId + ast.annotations[AST.SchemaIdAnnotationId] === AST.ParseJsonSchemaId function merge(a: JsonSchemaAnnotations, b: JsonSchema7): JsonSchema7 function merge(a: JsonSchema7, b: JsonSchemaAnnotations): JsonSchema7 diff --git a/packages/effect/src/Pretty.ts b/packages/effect/src/Pretty.ts index 8da7db6e31..ca9ba85a84 100644 --- a/packages/effect/src/Pretty.ts +++ b/packages/effect/src/Pretty.ts @@ -18,24 +18,18 @@ export interface Pretty { } /** - * @category hooks - * @since 3.10.0 - */ -export const PrettyHookId: unique symbol = Symbol.for("effect/Schema/PrettyHookId") - -/** - * @category hooks + * @category annotations * @since 3.10.0 */ -export type PrettyHookId = typeof PrettyHookId +export type PrettyAnnotation = readonly []> = ( + ...pretties: { readonly [K in keyof TypeParameters]: Pretty } +) => Pretty /** * @category annotations * @since 3.10.0 */ -export const pretty = - (handler: (...args: ReadonlyArray>) => Pretty) => - (self: Schema.Schema): Schema.Schema => self.annotations({ [PrettyHookId]: handler }) +export const PrettyAnnotationId: unique symbol = Symbol.for("effect/annotation/Pretty") /** * @category prettify @@ -43,12 +37,10 @@ export const pretty = */ export const make = (schema: Schema.Schema): (a: A) => string => compile(schema.ast, []) -const getHook = AST.getAnnotation<(...args: ReadonlyArray>) => Pretty>( - PrettyHookId -) +const getAnnotation = AST.getAnnotation>(PrettyAnnotationId) const getMatcher = (defaultPretty: Pretty) => (ast: AST.AST): Pretty => - Option.match(getHook(ast), { + Option.match(getAnnotation(ast), { onNone: () => defaultPretty, onSome: (handler) => handler() }) @@ -64,9 +56,9 @@ const formatUnknown = getMatcher(util_.formatUnknown) */ export const match: AST.Match> = { "Declaration": (ast, go, path) => { - const hook = getHook(ast) - if (Option.isSome(hook)) { - return hook.value(...ast.typeParameters.map((tp) => go(tp, path))) + const annotation = getAnnotation(ast) + if (Option.isSome(annotation)) { + return annotation.value(...ast.typeParameters.map((tp) => go(tp, path))) } throw new Error(errors_.getPrettyMissingAnnotationErrorMessage(path, ast)) }, @@ -92,7 +84,7 @@ export const match: AST.Match> = { "BigIntKeyword": getMatcher((a) => `${String(a)}n`), "Enums": stringify, "TupleType": (ast, go, path) => { - const hook = getHook(ast) + const hook = getAnnotation(ast) if (Option.isSome(hook)) { return hook.value() } @@ -134,7 +126,7 @@ export const match: AST.Match> = { } }, "TypeLiteral": (ast, go, path) => { - const hook = getHook(ast) + const hook = getAnnotation(ast) if (Option.isSome(hook)) { return hook.value() } @@ -179,7 +171,7 @@ export const match: AST.Match> = { } }, "Union": (ast, go, path) => { - const hook = getHook(ast) + const hook = getAnnotation(ast) if (Option.isSome(hook)) { return hook.value() } @@ -193,7 +185,7 @@ export const match: AST.Match> = { } }, "Suspend": (ast, go, path) => { - return Option.match(getHook(ast), { + return Option.match(getAnnotation(ast), { onNone: () => { const get = util_.memoizeThunk(() => go(ast.f(), path)) return (a) => get()(a) @@ -202,13 +194,13 @@ export const match: AST.Match> = { }) }, "Refinement": (ast, go, path) => { - return Option.match(getHook(ast), { + return Option.match(getAnnotation(ast), { onNone: () => go(ast.from, path), onSome: (handler) => handler() }) }, "Transformation": (ast, go, path) => { - return Option.match(getHook(ast), { + return Option.match(getAnnotation(ast), { onNone: () => go(ast.to, path), onSome: (handler) => handler() }) diff --git a/packages/effect/src/Schema.ts b/packages/effect/src/Schema.ts index 2cf49aa73f..9c1daeda28 100644 --- a/packages/effect/src/Schema.ts +++ b/packages/effect/src/Schema.ts @@ -3,7 +3,7 @@ */ import * as arbitrary_ from "./Arbitrary.js" -import type { GenerationContext, LazyArbitrary } from "./Arbitrary.js" +import type { ArbitraryGenerationContext, LazyArbitrary } from "./Arbitrary.js" import * as array_ from "./Array.js" import * as bigDecimal_ from "./BigDecimal.js" import * as bigInt_ from "./BigInt.js" @@ -69,7 +69,7 @@ export type SimplifyMutable = { * @since 3.10.0 * @category symbol */ -export const TypeId: unique symbol = Symbol.for("effect/Schema/Schema") +export const TypeId: unique symbol = Symbol.for("effect/Schema") /** * @since 3.10.0 @@ -135,52 +135,42 @@ interface AllAnnotations> extends Annotations.Schema, PropertySignature.Annotations {} +const builtInAnnotations = { + schemaId: AST.SchemaIdAnnotationId, + message: AST.MessageAnnotationId, + missingMessage: AST.MissingMessageAnnotationId, + identifier: AST.IdentifierAnnotationId, + title: AST.TitleAnnotationId, + description: AST.DescriptionAnnotationId, + examples: AST.ExamplesAnnotationId, + default: AST.DefaultAnnotationId, + documentation: AST.DocumentationAnnotationId, + jsonSchema: AST.JSONSchemaAnnotationId, + arbitrary: arbitrary_.ArbitraryAnnotationId, + pretty: pretty_.PrettyAnnotationId, + equivalence: equivalence_.EquivalenceAnnotationId, + concurrency: AST.ConcurrencyAnnotationId, + batching: AST.BatchingAnnotationId, + parseIssueTitle: AST.ParseIssueTitleAnnotationId, + parseOptions: AST.ParseOptionsAnnotationId, + decodingFallback: AST.DecodingFallbackAnnotationId +} + const toASTAnnotations = >( annotations?: AllAnnotations ): AST.Annotations => { if (!annotations) { return {} } - const out: Types.Mutable = {} - - // symbols are reserved for custom annotations - const custom = Object.getOwnPropertySymbols(annotations) - for (const sym of custom) { - out[sym] = annotations[sym] - } + const out: Types.Mutable = { ...annotations } - // string keys are reserved as /schema namespace - if (annotations.typeId !== undefined) { - const typeId = annotations.typeId - if (typeof typeId === "object") { - out[AST.TypeAnnotationId] = typeId.id - out[typeId.id] = typeId.annotation - } else { - out[AST.TypeAnnotationId] = typeId - } - } - const move = (from: keyof typeof annotations, to: symbol) => { - if (annotations[from] !== undefined) { - out[to] = annotations[from] + 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] } } - move("message", AST.MessageAnnotationId) - move("missingMessage", AST.MissingMessageAnnotationId) - move("identifier", AST.IdentifierAnnotationId) - move("title", AST.TitleAnnotationId) - move("description", AST.DescriptionAnnotationId) - move("examples", AST.ExamplesAnnotationId) - move("default", AST.DefaultAnnotationId) - move("documentation", AST.DocumentationAnnotationId) - move("jsonSchema", AST.JSONSchemaAnnotationId) - move("arbitrary", arbitrary_.ArbitraryHookId) - move("pretty", pretty_.PrettyHookId) - move("equivalence", equivalence_.EquivalenceHookId) - move("concurrency", AST.ConcurrencyAnnotationId) - move("batching", AST.BatchingAnnotationId) - move("parseIssueTitle", AST.ParseIssueTitleAnnotationId) - move("parseOptions", AST.ParseOptionsAnnotationId) - move("decodingFallback", AST.DecodingFallbackAnnotationId) return out } @@ -951,10 +941,10 @@ export const declare: { } as any /** - * @category type id + * @category schema id * @since 3.10.0 */ -export const BrandTypeId: unique symbol = Symbol.for("effect/Schema/TypeId/Brand") +export const BrandSchemaId: unique symbol = Symbol.for("effect/SchemaId/Brand") /** * @category constructors @@ -974,15 +964,19 @@ export const fromBrand = , A extends Brand.Unbr option_.some(new ParseResult.Type(ast, a, either.left.map((v) => v.message).join(", "))) : option_.none() }, - toASTAnnotations({ typeId: { id: BrandTypeId, annotation: { constructor } }, ...annotations }) + toASTAnnotations({ + schemaId: BrandSchemaId, + [BrandSchemaId]: { constructor }, + ...annotations + }) ) ) /** - * @category type id + * @category schema id * @since 3.10.0 */ -export const InstanceOfTypeId: unique symbol = Symbol.for("effect/Schema/TypeId/InstanceOf") +export const InstanceOfSchemaId: unique symbol = Symbol.for("effect/SchemaId/InstanceOf") /** * @category api interface @@ -1004,7 +998,8 @@ export const instanceOf = any>( title: constructor.name, description: `an instance of ${constructor.name}`, pretty: (): pretty_.Pretty> => String, - typeId: { id: InstanceOfTypeId, annotation: { constructor } }, + schemaId: InstanceOfSchemaId, + [InstanceOfSchemaId]: { constructor }, ...annotations } ) @@ -1669,7 +1664,7 @@ const mergeSignatureAnnotations = ( * @since 3.10.0 * @category symbol */ -export const PropertySignatureTypeId: unique symbol = Symbol.for("effect/Schema/PropertySignature") +export const PropertySignatureTypeId: unique symbol = Symbol.for("effect/PropertySignature") /** * @since 3.10.0 @@ -3309,13 +3304,13 @@ export const suspend = (f: () => Schema): suspend => * @since 3.10.0 * @category symbol */ -export const refineTypeId: unique symbol = Symbol.for("effect/Schema/refine") +export const RefineSchemaId: unique symbol = Symbol.for("effect/SchemaId/Refine") /** * @since 3.10.0 * @category symbol */ -export type refineTypeId = typeof refineTypeId +export type RefineSchemaId = typeof RefineSchemaId /** * @category api interface @@ -3324,7 +3319,7 @@ export type refineTypeId = typeof refineTypeId export interface refine extends AnnotableClass, A, Schema.Encoded, Schema.Context> { - readonly [refineTypeId]: From + readonly [RefineSchemaId]: From // required for `type HasFields = ...` readonly from: From readonly filter: ( a: Schema.Type, @@ -3348,7 +3343,7 @@ const makeRefineClass = ( return makeRefineClass(this.from, this.filter, mergeSchemaAnnotations(this.ast, annotations)) } - static [refineTypeId] = from + static [RefineSchemaId] = from static from = from @@ -3872,20 +3867,11 @@ export declare namespace Annotations { export interface Schema = readonly []> extends Doc { readonly identifier?: AST.IdentifierAnnotation readonly message?: AST.MessageAnnotation - readonly typeId?: AST.TypeAnnotation | { id: AST.TypeAnnotation; annotation: unknown } + readonly schemaId?: AST.SchemaIdAnnotation readonly jsonSchema?: AST.JSONSchemaAnnotation - readonly arbitrary?: ( - ...arbitraries: [ - ...{ readonly [K in keyof TypeParameters]: LazyArbitrary }, - ctx: GenerationContext - ] - ) => LazyArbitrary - readonly pretty?: ( - ...pretties: { readonly [K in keyof TypeParameters]: pretty_.Pretty } - ) => pretty_.Pretty - readonly equivalence?: ( - ...equivalences: { readonly [K in keyof TypeParameters]: Equivalence.Equivalence } - ) => Equivalence.Equivalence + readonly arbitrary?: arbitrary_.ArbitraryAnnotation + readonly pretty?: pretty_.PrettyAnnotation + readonly equivalence?: equivalence_.EquivalenceAnnotation readonly concurrency?: AST.ConcurrencyAnnotation readonly batching?: AST.BatchingAnnotation readonly parseIssueTitle?: AST.ParseIssueTitleAnnotation @@ -3962,10 +3948,10 @@ export const rename: { ) /** - * @category type id + * @category schema id * @since 3.10.0 */ -export const TrimmedTypeId: unique symbol = Symbol.for("effect/Schema/TypeId/Trimmed") +export const TrimmedSchemaId: unique symbol = Symbol.for("effect/SchemaId/Trimmed") /** * Verifies that a string contains no leading or trailing whitespaces. @@ -3980,7 +3966,7 @@ export const trimmed = (annotations?: Annotations.Filter) => (self: Schema): filter> => self.pipe( filter((a) => a === a.trim(), { - typeId: TrimmedTypeId, + schemaId: TrimmedSchemaId, description: "a string with no leading or trailing whitespace", jsonSchema: { pattern: "^\\S[\\s\\S]*\\S$|^\\S$|^$" }, ...annotations @@ -3988,16 +3974,16 @@ export const trimmed = ) /** - * @category type id + * @category schema id * @since 3.10.0 */ -export const MaxLengthTypeId: unique symbol = filters_.MaxLengthTypeId +export const MaxLengthSchemaId: unique symbol = filters_.MaxLengthSchemaId /** - * @category type id + * @category schema id * @since 3.10.0 */ -export type MaxLengthTypeId = typeof MaxLengthTypeId +export type MaxLengthSchemaId = typeof MaxLengthSchemaId /** * @category string filters @@ -4012,7 +3998,7 @@ export const maxLength = ( filter( (a) => a.length <= maxLength, { - typeId: MaxLengthTypeId, + schemaId: MaxLengthSchemaId, description: `a string at most ${maxLength} character(s) long`, jsonSchema: { maxLength }, ...annotations @@ -4021,16 +4007,16 @@ export const maxLength = ( ) /** - * @category type id + * @category schema id * @since 3.10.0 */ -export const MinLengthTypeId: unique symbol = filters_.MinLengthTypeId +export const MinLengthSchemaId: unique symbol = filters_.MinLengthSchemaId /** - * @category type id + * @category schema id * @since 3.10.0 */ -export type MinLengthTypeId = typeof MinLengthTypeId +export type MinLengthSchemaId = typeof MinLengthSchemaId /** * @category string filters @@ -4045,7 +4031,7 @@ export const minLength = ( filter( (a) => a.length >= minLength, { - typeId: MinLengthTypeId, + schemaId: MinLengthSchemaId, description: `a string at least ${minLength} character(s) long`, jsonSchema: { minLength }, ...annotations @@ -4054,10 +4040,10 @@ export const minLength = ( ) /** - * @category type id + * @category schema id * @since 3.10.0 */ -export const PatternTypeId: unique symbol = Symbol.for("effect/Schema/TypeId/Pattern") +export const PatternSchemaId: unique symbol = Symbol.for("effect/SchemaId/Pattern") /** * @category string filters @@ -4077,7 +4063,8 @@ export const pattern = ( return regex.test(a) }, { - typeId: { id: PatternTypeId, annotation: { regex } }, + schemaId: PatternSchemaId, + [PatternSchemaId]: { regex }, description: `a string matching the pattern ${pattern}`, jsonSchema: { pattern }, arbitrary: () => (fc) => fc.stringMatching(regex) as any, @@ -4088,10 +4075,10 @@ export const pattern = ( } /** - * @category type id + * @category schema id * @since 3.10.0 */ -export const StartsWithTypeId: unique symbol = Symbol.for("effect/Schema/TypeId/StartsWith") +export const StartsWithSchemaId: unique symbol = Symbol.for("effect/SchemaId/StartsWith") /** * @category string filters @@ -4106,7 +4093,8 @@ export const startsWith = ( filter( (a) => a.startsWith(startsWith), { - typeId: { id: StartsWithTypeId, annotation: { startsWith } }, + schemaId: StartsWithSchemaId, + [StartsWithSchemaId]: { startsWith }, description: `a string starting with ${JSON.stringify(startsWith)}`, jsonSchema: { pattern: `^${startsWith}` }, ...annotations @@ -4115,10 +4103,10 @@ export const startsWith = ( ) /** - * @category type id + * @category schema id * @since 3.10.0 */ -export const EndsWithTypeId: unique symbol = Symbol.for("effect/Schema/TypeId/EndsWith") +export const EndsWithSchemaId: unique symbol = Symbol.for("effect/SchemaId/EndsWith") /** * @category string filters @@ -4133,7 +4121,8 @@ export const endsWith = ( filter( (a) => a.endsWith(endsWith), { - typeId: { id: EndsWithTypeId, annotation: { endsWith } }, + schemaId: EndsWithSchemaId, + [EndsWithSchemaId]: { endsWith }, description: `a string ending with ${JSON.stringify(endsWith)}`, jsonSchema: { pattern: `^.*${endsWith}$` }, ...annotations @@ -4142,10 +4131,10 @@ export const endsWith = ( ) /** - * @category type id + * @category schema id * @since 3.10.0 */ -export const IncludesTypeId: unique symbol = Symbol.for("effect/Schema/TypeId/Includes") +export const IncludesSchemaId: unique symbol = Symbol.for("effect/SchemaId/Includes") /** * @category string filters @@ -4160,7 +4149,8 @@ export const includes = ( filter( (a) => a.includes(searchString), { - typeId: { id: IncludesTypeId, annotation: { includes: searchString } }, + schemaId: IncludesSchemaId, + [IncludesSchemaId]: { includes: searchString }, description: `a string including ${JSON.stringify(searchString)}`, jsonSchema: { pattern: `.*${searchString}.*` }, ...annotations @@ -4169,10 +4159,10 @@ export const includes = ( ) /** - * @category type id + * @category schema id * @since 3.10.0 */ -export const LowercasedTypeId: unique symbol = Symbol.for("effect/Schema/TypeId/Lowercased") +export const LowercasedSchemaId: unique symbol = Symbol.for("effect/SchemaId/Lowercased") /** * Verifies that a string is lowercased. @@ -4184,7 +4174,7 @@ export const lowercased = (annotations?: Annotations.Filter) => (self: Schema): filter> => self.pipe( filter((a) => a === a.toLowerCase(), { - typeId: LowercasedTypeId, + schemaId: LowercasedSchemaId, description: "a lowercase string", ...annotations }) @@ -4199,10 +4189,10 @@ export class Lowercased extends String$.pipe( ) {} /** - * @category type id + * @category schema id * @since 3.10.0 */ -export const CapitalizedTypeId: unique symbol = Symbol.for("effect/Schema/TypeId/Capitalized") +export const CapitalizedSchemaId: unique symbol = Symbol.for("effect/SchemaId/Capitalized") /** * Verifies that a string is capitalized. @@ -4214,7 +4204,7 @@ export const capitalized = (annotations?: Annotations.Filter) => (self: Schema): filter> => self.pipe( filter((a) => a[0]?.toUpperCase() === a[0], { - typeId: CapitalizedTypeId, + schemaId: CapitalizedSchemaId, description: "a capitalized string", ...annotations }) @@ -4229,10 +4219,10 @@ export class Capitalized extends String$.pipe( ) {} /** - * @category type id + * @category schema id * @since 3.10.0 */ -export const UncapitalizedTypeId: unique symbol = Symbol.for("effect/Schema/TypeId/Uncapitalized") +export const UncapitalizedSchemaId: unique symbol = Symbol.for("effect/SchemaId/Uncapitalized") /** * Verifies that a string is uncapitalized. @@ -4244,7 +4234,7 @@ export const uncapitalized = (annotations?: Annotations.Filter) => (self: Schema): filter> => self.pipe( filter((a) => a[0]?.toLowerCase() === a[0], { - typeId: UncapitalizedTypeId, + schemaId: UncapitalizedSchemaId, description: "a uncapitalized string", ...annotations }) @@ -4259,10 +4249,10 @@ export class Uncapitalized extends String$.pipe( ) {} /** - * @category type id + * @category schema id * @since 3.10.0 */ -export const UppercasedTypeId: unique symbol = Symbol.for("effect/Schema/TypeId/Uppercased") +export const UppercasedSchemaId: unique symbol = Symbol.for("effect/SchemaId/Uppercased") /** * Verifies that a string is uppercased. @@ -4274,7 +4264,7 @@ export const uppercased = (annotations?: Annotations.Filter) => (self: Schema): filter> => self.pipe( filter((a) => a === a.toUpperCase(), { - typeId: UppercasedTypeId, + schemaId: UppercasedSchemaId, description: "an uppercase string", ...annotations }) @@ -4289,16 +4279,16 @@ export class Uppercased extends String$.pipe( ) {} /** - * @category type id + * @category schema id * @since 3.10.0 */ -export const LengthTypeId: unique symbol = filters_.LengthTypeId +export const LengthSchemaId: unique symbol = filters_.LengthSchemaId /** - * @category type id + * @category schema id * @since 3.10.0 */ -export type LengthTypeId = typeof LengthTypeId +export type LengthSchemaId = typeof LengthSchemaId /** * @category string filters @@ -4314,7 +4304,7 @@ export const length = ( if (minLength !== maxLength) { return self.pipe( filter((a) => a.length >= minLength && a.length <= maxLength, { - typeId: LengthTypeId, + schemaId: LengthSchemaId, description: `a string at least ${minLength} character(s) and at most ${maxLength} character(s) long`, jsonSchema: { minLength, maxLength }, ...annotations @@ -4323,7 +4313,7 @@ export const length = ( } return self.pipe( filter((a) => a.length === minLength, { - typeId: LengthTypeId, + schemaId: LengthSchemaId, description: minLength === 1 ? `a single character` : `a string ${minLength} character(s) long`, jsonSchema: { minLength, maxLength: minLength }, ...annotations @@ -4482,7 +4472,7 @@ const getParseJsonTransformation = (options?: ParseJsonOptions) => catch: (e: any) => new ParseResult.Type(ast, u, e.message) }) } - ).annotations({ typeId: filters_.ParseJsonTypeId }) + ).annotations({ schemaId: AST.ParseJsonSchemaId }) /** * The `ParseJson` combinator provides a method to convert JSON strings into the `unknown` type using the underlying @@ -4504,10 +4494,10 @@ const getParseJsonTransformation = (options?: ParseJsonOptions) => export const parseJson: { (schema: Schema, options?: ParseJsonOptions): SchemaClass (options?: ParseJsonOptions): SchemaClass -} = (schema?: Schema | ParseJsonOptions, o?: ParseJsonOptions) => - isSchema(schema) - ? compose(parseJson(o), schema) as any - : getParseJsonTransformation(schema as ParseJsonOptions | undefined) +} = (schemaOrOptions?: Schema | ParseJsonOptions, o?: ParseJsonOptions) => + isSchema(schemaOrOptions) + ? compose(parseJson(o), schemaOrOptions) as any + : getParseJsonTransformation(schemaOrOptions as ParseJsonOptions | undefined) /** * @category string constructors @@ -4518,10 +4508,10 @@ export class NonEmptyString extends String$.pipe( ) {} /** - * @category type id + * @category schema id * @since 3.10.0 */ -export const UUIDTypeId: unique symbol = Symbol.for("effect/Schema/TypeId/UUID") +export const UUIDSchemaId: unique symbol = Symbol.for("effect/SchemaId/UUID") const uuidRegexp = /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/i @@ -4535,7 +4525,7 @@ const uuidRegexp = /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4} */ export class UUID extends String$.pipe( pattern(uuidRegexp, { - typeId: UUIDTypeId, + schemaId: UUIDSchemaId, identifier: "UUID", title: "UUID", description: "a Universally Unique Identifier", @@ -4544,10 +4534,10 @@ export class UUID extends String$.pipe( ) {} /** - * @category type id + * @category schema id * @since 3.10.0 */ -export const ULIDTypeId: unique symbol = Symbol.for("effect/Schema/TypeId/ULID") +export const ULIDSchemaId: unique symbol = Symbol.for("effect/SchemaId/ULID") const ulidRegexp = /^[0-7][0-9A-HJKMNP-TV-Z]{25}$/i @@ -4562,7 +4552,7 @@ const ulidRegexp = /^[0-7][0-9A-HJKMNP-TV-Z]{25}$/i */ export class ULID extends String$.pipe( pattern(ulidRegexp, { - typeId: ULIDTypeId, + schemaId: ULIDSchemaId, identifier: "ULID", title: "ULID", description: "a Universally Unique Lexicographically Sortable Identifier", @@ -4571,10 +4561,10 @@ export class ULID extends String$.pipe( ) {} /** - * @category type id + * @category schema id * @since 3.10.0 */ -export const FiniteTypeId: unique symbol = Symbol.for("effect/Schema/TypeId/Finite") +export const FiniteSchemaId: unique symbol = Symbol.for("effect/SchemaId/Finite") /** * Ensures that the provided value is a finite number. @@ -4588,23 +4578,23 @@ export const finite = (annotations?: Annotations.Filter) => (self: Schema): filter> => self.pipe( filter((a) => Number.isFinite(a), { - typeId: FiniteTypeId, + schemaId: FiniteSchemaId, description: "a finite number", ...annotations }) ) /** - * @category type id + * @category schema id * @since 3.10.0 */ -export const GreaterThanTypeId: unique symbol = filters_.GreaterThanTypeId +export const GreaterThanSchemaId: unique symbol = filters_.GreaterThanSchemaId /** - * @category type id + * @category schema id * @since 3.10.0 */ -export type GreaterThanTypeId = typeof GreaterThanTypeId +export type GreaterThanSchemaId = typeof GreaterThanSchemaId /** * This filter checks whether the provided number is greater than the specified minimum. @@ -4619,7 +4609,7 @@ export const greaterThan = ( (self: Schema): filter> => self.pipe( filter((a) => a > min, { - typeId: GreaterThanTypeId, + schemaId: GreaterThanSchemaId, description: min === 0 ? "a positive number" : `a number greater than ${min}`, jsonSchema: { exclusiveMinimum: min }, ...annotations @@ -4627,16 +4617,16 @@ export const greaterThan = ( ) /** - * @category type id + * @category schema id * @since 3.10.0 */ -export const GreaterThanOrEqualToTypeId: unique symbol = filters_.GreaterThanOrEqualToTypeId +export const GreaterThanOrEqualToSchemaId: unique symbol = filters_.GreaterThanOrEqualToSchemaId /** - * @category type id + * @category schema id * @since 3.10.0 */ -export type GreaterThanOrEqualToTypeId = typeof GreaterThanOrEqualToTypeId +export type GreaterThanOrEqualToSchemaId = typeof GreaterThanOrEqualToSchemaId /** * This filter checks whether the provided number is greater than or equal to the specified minimum. @@ -4651,7 +4641,7 @@ export const greaterThanOrEqualTo = ( (self: Schema): filter> => self.pipe( filter((a) => a >= min, { - typeId: GreaterThanOrEqualToTypeId, + schemaId: GreaterThanOrEqualToSchemaId, description: min === 0 ? "a non-negative number" : `a number greater than or equal to ${min}`, jsonSchema: { minimum: min }, ...annotations @@ -4659,10 +4649,10 @@ export const greaterThanOrEqualTo = ( ) /** - * @category type id + * @category schema id * @since 3.10.0 */ -export const MultipleOfTypeId: unique symbol = Symbol.for("effect/Schema/TypeId/MultipleOf") +export const MultipleOfSchemaId: unique symbol = Symbol.for("effect/SchemaId/MultipleOf") /** * @category number filters @@ -4675,7 +4665,7 @@ export const multipleOf = ( (self: Schema): filter> => self.pipe( filter((a) => number_.remainder(a, divisor) === 0, { - typeId: MultipleOfTypeId, + schemaId: MultipleOfSchemaId, description: `a number divisible by ${divisor}`, jsonSchema: { multipleOf: Math.abs(divisor) }, // spec requires positive divisor ...annotations @@ -4683,16 +4673,16 @@ export const multipleOf = ( ) /** - * @category type id + * @category schema id * @since 3.10.0 */ -export const IntTypeId: unique symbol = filters_.IntTypeId +export const IntSchemaId: unique symbol = filters_.IntSchemaId /** - * @category type id + * @category schema id * @since 3.10.0 */ -export type IntTypeId = typeof IntTypeId +export type IntSchemaId = typeof IntSchemaId /** * @category number filters @@ -4702,7 +4692,7 @@ export const int = (annotations?: Annotations.Filter) => (self: Schema): filter> => self.pipe( filter((a) => Number.isSafeInteger(a), { - typeId: IntTypeId, + schemaId: IntSchemaId, title: "integer", description: "an integer", jsonSchema: { type: "integer" }, @@ -4711,16 +4701,16 @@ export const int = ) /** - * @category type id + * @category schema id * @since 3.10.0 */ -export const LessThanTypeId: unique symbol = filters_.LessThanTypeId +export const LessThanSchemaId: unique symbol = filters_.LessThanSchemaId /** - * @category type id + * @category schema id * @since 3.10.0 */ -export type LessThanTypeId = typeof LessThanTypeId +export type LessThanSchemaId = typeof LessThanSchemaId /** * This filter checks whether the provided number is less than the specified maximum. @@ -4733,7 +4723,7 @@ export const lessThan = (self: Schema): filter> => self.pipe( filter((a) => a < max, { - typeId: LessThanTypeId, + schemaId: LessThanSchemaId, description: max === 0 ? "a negative number" : `a number less than ${max}`, jsonSchema: { exclusiveMaximum: max }, ...annotations @@ -4741,16 +4731,16 @@ export const lessThan = ) /** - * @category type id + * @category schema id * @since 3.10.0 */ -export const LessThanOrEqualToTypeId: unique symbol = filters_.LessThanOrEqualToTypeId +export const LessThanOrEqualToSchemaId: unique symbol = filters_.LessThanOrEqualToSchemaId /** - * @category type id + * @category schema id * @since 3.10.0 */ -export type LessThanOrEqualToTypeId = typeof LessThanOrEqualToTypeId +export type LessThanOrEqualToSchemaId = typeof LessThanOrEqualToSchemaId /** * This schema checks whether the provided number is less than or equal to the specified maximum. @@ -4765,7 +4755,7 @@ export const lessThanOrEqualTo = ( (self: Schema): filter> => self.pipe( filter((a) => a <= max, { - typeId: LessThanOrEqualToTypeId, + schemaId: LessThanOrEqualToSchemaId, description: max === 0 ? "a non-positive number" : `a number less than or equal to ${max}`, jsonSchema: { maximum: max }, ...annotations @@ -4773,16 +4763,16 @@ export const lessThanOrEqualTo = ( ) /** - * @category type id + * @category schema id * @since 3.10.0 */ -export const BetweenTypeId: unique symbol = filters_.BetweenTypeId +export const BetweenSchemaId: unique symbol = filters_.BetweenSchemaId /** - * @category type id + * @category schema id * @since 3.10.0 */ -export type BetweenTypeId = typeof BetweenTypeId +export type BetweenSchemaId = typeof BetweenSchemaId /** * This filter checks whether the provided number falls within the specified minimum and maximum values. @@ -4798,7 +4788,7 @@ export const between = ( (self: Schema): filter> => self.pipe( filter((a) => a >= min && a <= max, { - typeId: BetweenTypeId, + schemaId: BetweenSchemaId, description: `a number between ${min} and ${max}`, jsonSchema: { maximum: max, minimum: min }, ...annotations @@ -4806,10 +4796,10 @@ export const between = ( ) /** - * @category type id + * @category schema id * @since 3.10.0 */ -export const NonNaNTypeId: unique symbol = Symbol.for("effect/Schema/TypeId/NonNaN") +export const NonNaNSchemaId: unique symbol = Symbol.for("effect/SchemaId/NonNaN") /** * @category number filters @@ -4819,7 +4809,7 @@ export const nonNaN = (annotations?: Annotations.Filter) => (self: Schema): filter> => self.pipe( filter((a) => !Number.isNaN(a), { - typeId: NonNaNTypeId, + schemaId: NonNaNSchemaId, description: "a number excluding NaN", ...annotations }) @@ -4960,10 +4950,10 @@ export class NonNegative extends Number$.pipe( ) {} /** - * @category type id + * @category schema id * @since 3.10.0 */ -export const JsonNumberTypeId: unique symbol = Symbol.for("effect/Schema/TypeId/JsonNumber") +export const JsonNumberSchemaId: unique symbol = Symbol.for("effect/SchemaId/JsonNumber") /** * The `JsonNumber` is a schema for representing JSON numbers. It ensures that the provided value is a valid @@ -4985,7 +4975,7 @@ export const JsonNumberTypeId: unique symbol = Symbol.for("effect/Schema/TypeId/ */ export class JsonNumber extends Number$.pipe( filter((n) => !Number.isNaN(n) && Number.isFinite(n), { - typeId: JsonNumberTypeId, + schemaId: JsonNumberSchemaId, identifier: "JsonNumber", title: "JSON-compatible number", description: "a JSON-compatible number, excluding NaN, +Infinity, and -Infinity", @@ -5021,16 +5011,16 @@ export { } /** - * @category type id + * @category schema id * @since 3.10.0 */ -export const GreaterThanBigIntTypeId: unique symbol = filters_.GreaterThanBigintTypeId +export const GreaterThanBigIntSchemaId: unique symbol = filters_.GreaterThanBigintSchemaId /** - * @category type id + * @category schema id * @since 3.10.0 */ -export type GreaterThanBigIntTypeId = typeof GreaterThanBigIntTypeId +export type GreaterThanBigIntSchemaId = typeof GreaterThanBigIntSchemaId /** * @category bigint filters @@ -5043,23 +5033,24 @@ export const greaterThanBigInt = ( (self: Schema): filter> => self.pipe( filter((a) => a > min, { - typeId: { id: GreaterThanBigIntTypeId, annotation: { min } }, + schemaId: GreaterThanBigIntSchemaId, + [GreaterThanBigIntSchemaId]: { min }, description: min === 0n ? "a positive bigint" : `a bigint greater than ${min}n`, ...annotations }) ) /** - * @category type id + * @category schema id * @since 3.10.0 */ -export const GreaterThanOrEqualToBigIntTypeId: unique symbol = filters_.GreaterThanOrEqualToBigIntTypeId +export const GreaterThanOrEqualToBigIntSchemaId: unique symbol = filters_.GreaterThanOrEqualToBigIntSchemaId /** - * @category type id + * @category schema id * @since 3.10.0 */ -export type GreaterThanOrEqualToBigIntTypeId = typeof GreaterThanOrEqualToBigIntTypeId +export type GreaterThanOrEqualToBigIntSchemaId = typeof GreaterThanOrEqualToBigIntSchemaId /** * @category bigint filters @@ -5072,7 +5063,8 @@ export const greaterThanOrEqualToBigInt = ( (self: Schema): filter> => self.pipe( filter((a) => a >= min, { - typeId: { id: GreaterThanOrEqualToBigIntTypeId, annotation: { min } }, + schemaId: GreaterThanOrEqualToBigIntSchemaId, + [GreaterThanOrEqualToBigIntSchemaId]: { min }, description: min === 0n ? "a non-negative bigint" : `a bigint greater than or equal to ${min}n`, @@ -5081,16 +5073,16 @@ export const greaterThanOrEqualToBigInt = ( ) /** - * @category type id + * @category schema id * @since 3.10.0 */ -export const LessThanBigIntTypeId: unique symbol = filters_.LessThanBigIntTypeId +export const LessThanBigIntSchemaId: unique symbol = filters_.LessThanBigIntSchemaId /** - * @category type id + * @category schema id * @since 3.10.0 */ -export type LessThanBigIntTypeId = typeof LessThanBigIntTypeId +export type LessThanBigIntSchemaId = typeof LessThanBigIntSchemaId /** * @category bigint filters @@ -5103,23 +5095,24 @@ export const lessThanBigInt = ( (self: Schema): filter> => self.pipe( filter((a) => a < max, { - typeId: { id: LessThanBigIntTypeId, annotation: { max } }, + schemaId: LessThanBigIntSchemaId, + [LessThanBigIntSchemaId]: { max }, description: max === 0n ? "a negative bigint" : `a bigint less than ${max}n`, ...annotations }) ) /** - * @category type id + * @category schema id * @since 3.10.0 */ -export const LessThanOrEqualToBigIntTypeId: unique symbol = filters_.LessThanOrEqualToBigIntTypeId +export const LessThanOrEqualToBigIntSchemaId: unique symbol = filters_.LessThanOrEqualToBigIntSchemaId /** - * @category type id + * @category schema id * @since 3.10.0 */ -export type LessThanOrEqualToBigIntTypeId = typeof LessThanOrEqualToBigIntTypeId +export type LessThanOrEqualToBigIntSchemaId = typeof LessThanOrEqualToBigIntSchemaId /** * @category bigint filters @@ -5132,23 +5125,24 @@ export const lessThanOrEqualToBigInt = ( (self: Schema): filter> => self.pipe( filter((a) => a <= max, { - typeId: { id: LessThanOrEqualToBigIntTypeId, annotation: { max } }, + schemaId: LessThanOrEqualToBigIntSchemaId, + [LessThanOrEqualToBigIntSchemaId]: { max }, description: max === 0n ? "a non-positive bigint" : `a bigint less than or equal to ${max}n`, ...annotations }) ) /** - * @category type id + * @category schema id * @since 3.10.0 */ -export const BetweenBigIntTypeId: unique symbol = filters_.BetweenBigintTypeId +export const BetweenBigIntSchemaId: unique symbol = filters_.BetweenBigintSchemaId /** - * @category type id + * @category schema id * @since 3.10.0 */ -export type BetweenBigIntTypeId = typeof BetweenBigIntTypeId +export type BetweenBigIntSchemaId = typeof BetweenBigIntSchemaId /** * @category bigint filters @@ -5162,7 +5156,8 @@ export const betweenBigInt = ( (self: Schema): filter> => self.pipe( filter((a) => a >= min && a <= max, { - typeId: { id: BetweenBigIntTypeId, annotation: { max, min } }, + schemaId: BetweenBigIntSchemaId, + [BetweenBigIntSchemaId]: { max, min }, description: `a bigint between ${min}n and ${max}n`, ...annotations }) @@ -5519,10 +5514,10 @@ export const clampDuration = ) /** - * @category type id + * @category schema id * @since 3.10.0 */ -export const LessThanDurationTypeId: unique symbol = Symbol.for("effect/Schema/TypeId/LessThanDuration") +export const LessThanDurationSchemaId: unique symbol = Symbol.for("effect/SchemaId/LessThanDuration") /** * @category Duration filters @@ -5535,18 +5530,19 @@ export const lessThanDuration = ( (self: Schema): filter> => self.pipe( filter((a) => duration_.lessThan(a, max), { - typeId: { id: LessThanDurationTypeId, annotation: { max } }, + schemaId: LessThanDurationSchemaId, + [LessThanDurationSchemaId]: { max }, description: `a Duration less than ${duration_.decode(max)}`, ...annotations }) ) /** - * @category type id + * @category schema id * @since 3.10.0 */ -export const LessThanOrEqualToDurationTypeId: unique symbol = Symbol.for( - "effect/Schema/TypeId/LessThanOrEqualToDuration" +export const LessThanOrEqualToDurationSchemaId: unique symbol = Symbol.for( + "effect/schema/LessThanOrEqualToDuration" ) /** @@ -5560,17 +5556,18 @@ export const lessThanOrEqualToDuration = ( (self: Schema): filter> => self.pipe( filter((a) => duration_.lessThanOrEqualTo(a, max), { - typeId: { id: LessThanDurationTypeId, annotation: { max } }, + schemaId: LessThanDurationSchemaId, + [LessThanDurationSchemaId]: { max }, description: `a Duration less than or equal to ${duration_.decode(max)}`, ...annotations }) ) /** - * @category type id + * @category schema id * @since 3.10.0 */ -export const GreaterThanDurationTypeId: unique symbol = Symbol.for("effect/Schema/TypeId/GreaterThanDuration") +export const GreaterThanDurationSchemaId: unique symbol = Symbol.for("effect/SchemaId/GreaterThanDuration") /** * @category Duration filters @@ -5583,18 +5580,19 @@ export const greaterThanDuration = ( (self: Schema): filter> => self.pipe( filter((a) => duration_.greaterThan(a, min), { - typeId: { id: GreaterThanDurationTypeId, annotation: { min } }, + schemaId: GreaterThanDurationSchemaId, + [GreaterThanDurationSchemaId]: { min }, description: `a Duration greater than ${duration_.decode(min)}`, ...annotations }) ) /** - * @category type id + * @category schema id * @since 3.10.0 */ -export const GreaterThanOrEqualToDurationTypeId: unique symbol = Symbol.for( - "effect/Schema/TypeId/GreaterThanOrEqualToDuration" +export const GreaterThanOrEqualToDurationSchemaId: unique symbol = Symbol.for( + "effect/schema/GreaterThanOrEqualToDuration" ) /** @@ -5608,17 +5606,18 @@ export const greaterThanOrEqualToDuration = ( (self: Schema): filter> => self.pipe( filter((a) => duration_.greaterThanOrEqualTo(a, min), { - typeId: { id: GreaterThanOrEqualToDurationTypeId, annotation: { min } }, + schemaId: GreaterThanOrEqualToDurationSchemaId, + [GreaterThanOrEqualToDurationSchemaId]: { min }, description: `a Duration greater than or equal to ${duration_.decode(min)}`, ...annotations }) ) /** - * @category type id + * @category schema id * @since 3.10.0 */ -export const BetweenDurationTypeId: unique symbol = Symbol.for("effect/Schema/TypeId/BetweenDuration") +export const BetweenDurationSchemaId: unique symbol = Symbol.for("effect/SchemaId/BetweenDuration") /** * @category Duration filters @@ -5632,7 +5631,8 @@ export const betweenDuration = ( (self: Schema): filter> => self.pipe( filter((a) => duration_.between(a, { minimum, maximum }), { - typeId: { id: BetweenDurationTypeId, annotation: { maximum, minimum } }, + schemaId: BetweenDurationSchemaId, + [BetweenDurationSchemaId]: { maximum, minimum }, description: `a Duration between ${duration_.decode(minimum)} and ${duration_.decode(maximum)}`, ...annotations }) @@ -5786,16 +5786,16 @@ export const StringFromHex: Schema = makeEncodingTransformation( ) /** - * @category type id + * @category schema id * @since 3.10.0 */ -export const MinItemsTypeId: unique symbol = filters_.MinItemsTypeId +export const MinItemsSchemaId: unique symbol = filters_.MinItemsSchemaId /** - * @category type id + * @category schema id * @since 3.10.0 */ -export type MinItemsTypeId = typeof MinItemsTypeId +export type MinItemsSchemaId = typeof MinItemsSchemaId /** * @category ReadonlyArray filters @@ -5816,7 +5816,7 @@ export const minItems = ( filter( (a) => a.length >= minItems, { - typeId: MinItemsTypeId, + schemaId: MinItemsSchemaId, description: `an array of at least ${minItems} items`, jsonSchema: { minItems }, [AST.StableFilterAnnotationId]: true, @@ -5827,16 +5827,16 @@ export const minItems = ( } /** - * @category type id + * @category schema id * @since 3.10.0 */ -export const MaxItemsTypeId: unique symbol = filters_.MaxItemsTypeId +export const MaxItemsSchemaId: unique symbol = filters_.MaxItemsSchemaId /** - * @category type id + * @category schema id * @since 3.10.0 */ -export type MaxItemsTypeId = typeof MaxItemsTypeId +export type MaxItemsSchemaId = typeof MaxItemsSchemaId /** * @category ReadonlyArray filters @@ -5849,7 +5849,7 @@ export const maxItems = ( (self: Schema, I, R>): filter, I, R>> => self.pipe( filter((a) => a.length <= n, { - typeId: MaxItemsTypeId, + schemaId: MaxItemsSchemaId, description: `an array of at most ${n} items`, jsonSchema: { maxItems: n }, [AST.StableFilterAnnotationId]: true, @@ -5858,16 +5858,16 @@ export const maxItems = ( ) /** - * @category type id + * @category schema id * @since 3.10.0 */ -export const ItemsCountTypeId: unique symbol = filters_.ItemsCountTypeId +export const ItemsCountSchemaId: unique symbol = filters_.ItemsCountSchemaId /** - * @category type id + * @category schema id * @since 3.10.0 */ -export type ItemsCountTypeId = typeof ItemsCountTypeId +export type ItemsCountSchemaId = typeof ItemsCountSchemaId /** * @category ReadonlyArray filters @@ -5880,7 +5880,7 @@ export const itemsCount = ( (self: Schema, I, R>): filter, I, R>> => self.pipe( filter((a) => a.length === n, { - typeId: ItemsCountTypeId, + schemaId: ItemsCountSchemaId, description: `an array of exactly ${n} item(s)`, jsonSchema: { minItems: n, maxItems: n }, [AST.StableFilterAnnotationId]: true, @@ -5940,10 +5940,10 @@ export const headOrElse: { ) /** - * @category type id + * @category schema id * @since 3.10.0 */ -export const ValidDateTypeId: unique symbol = Symbol.for("effect/Schema/TypeId/ValidDate") +export const ValidDateSchemaId: unique symbol = Symbol.for("effect/SchemaId/ValidDate") /** * Defines a filter that specifically rejects invalid dates, such as `new @@ -5958,17 +5958,17 @@ export const validDate = (annotations?: Annotations.Filter) => (self: Schema): filter> => self.pipe( filter((a) => !Number.isNaN(a.getTime()), { - typeId: ValidDateTypeId, + schemaId: ValidDateSchemaId, description: "a valid Date", ...annotations }) ) /** - * @category type id + * @category schema id * @since 3.10.0 */ -export const LessThanDateTypeId: unique symbol = Symbol.for("effect/Schema/TypeId/LessThanDate") +export const LessThanDateSchemaId: unique symbol = Symbol.for("effect/SchemaId/LessThanDate") /** * @category Date filters @@ -5981,18 +5981,19 @@ export const lessThanDate = ( (self: Schema): filter> => self.pipe( filter((a) => a < max, { - typeId: { id: LessThanDateTypeId, annotation: { max } }, + schemaId: LessThanDateSchemaId, + [LessThanDateSchemaId]: { max }, description: `a date before ${util_.formatDate(max)}`, ...annotations }) ) /** - * @category type id + * @category schema id * @since 3.10.0 */ -export const LessThanOrEqualToDateTypeId: unique symbol = Symbol.for( - "effect/Schema/TypeId/LessThanOrEqualToDate" +export const LessThanOrEqualToDateSchemaId: unique symbol = Symbol.for( + "effect/schema/LessThanOrEqualToDate" ) /** @@ -6006,17 +6007,18 @@ export const lessThanOrEqualToDate = ( (self: Schema): filter> => self.pipe( filter((a) => a <= max, { - typeId: { id: LessThanDateTypeId, annotation: { max } }, + schemaId: LessThanDateSchemaId, + [LessThanDateSchemaId]: { max }, description: `a date before or equal to ${util_.formatDate(max)}`, ...annotations }) ) /** - * @category type id + * @category schema id * @since 3.10.0 */ -export const GreaterThanDateTypeId: unique symbol = Symbol.for("effect/Schema/TypeId/GreaterThanDate") +export const GreaterThanDateSchemaId: unique symbol = Symbol.for("effect/SchemaId/GreaterThanDate") /** * @category Date filters @@ -6029,18 +6031,19 @@ export const greaterThanDate = ( (self: Schema): filter> => self.pipe( filter((a) => a > min, { - typeId: { id: GreaterThanDateTypeId, annotation: { min } }, + schemaId: GreaterThanDateSchemaId, + [GreaterThanDateSchemaId]: { min }, description: `a date after ${util_.formatDate(min)}`, ...annotations }) ) /** - * @category type id + * @category schema id * @since 3.10.0 */ -export const GreaterThanOrEqualToDateTypeId: unique symbol = Symbol.for( - "effect/Schema/TypeId/GreaterThanOrEqualToDate" +export const GreaterThanOrEqualToDateSchemaId: unique symbol = Symbol.for( + "effect/schema/GreaterThanOrEqualToDate" ) /** @@ -6054,17 +6057,18 @@ export const greaterThanOrEqualToDate = ( (self: Schema): filter> => self.pipe( filter((a) => a >= min, { - typeId: { id: GreaterThanOrEqualToDateTypeId, annotation: { min } }, + schemaId: GreaterThanOrEqualToDateSchemaId, + [GreaterThanOrEqualToDateSchemaId]: { min }, description: `a date after or equal to ${util_.formatDate(min)}`, ...annotations }) ) /** - * @category type id + * @category schema id * @since 3.10.0 */ -export const BetweenDateTypeId: unique symbol = Symbol.for("effect/Schema/TypeId/BetweenDate") +export const BetweenDateSchemaId: unique symbol = Symbol.for("effect/SchemaId/BetweenDate") /** * @category Date filters @@ -6078,7 +6082,8 @@ export const betweenDate = ( (self: Schema): filter> => self.pipe( filter((a) => a <= maximum && a >= minimum, { - typeId: { id: BetweenDateTypeId, annotation: { maximum, minimum } }, + schemaId: BetweenDateSchemaId, + [BetweenDateSchemaId]: { maximum, minimum }, description: `a date between ${util_.formatDate(minimum)} and ${util_.formatDate(maximum)}`, ...annotations }) @@ -6406,7 +6411,7 @@ const optionDecode = (input: OptionEncoded): option_.Option => input._tag === "None" ? option_.none() : option_.some(input.value) const optionArbitrary = - (value: LazyArbitrary, ctx: GenerationContext): LazyArbitrary> => (fc) => + (value: LazyArbitrary, ctx: ArbitraryGenerationContext): LazyArbitrary> => (fc) => fc.oneof( ctx, fc.record({ _tag: fc.constant("None" as const) }), @@ -6829,7 +6834,7 @@ export const EitherFromUnion = ({ le const mapArbitrary = ( key: LazyArbitrary, value: LazyArbitrary, - ctx: GenerationContext + ctx: ArbitraryGenerationContext ): LazyArbitrary> => { return (fc) => { const items = fc.array(fc.tuple(key(fc), value(fc))) @@ -7033,10 +7038,11 @@ export const MapFromRecord = ({ key, value }: { } ) -const setArbitrary = (item: LazyArbitrary, ctx: GenerationContext): LazyArbitrary> => (fc) => { - const items = fc.array(item(fc)) - return (ctx.depthIdentifier !== undefined ? fc.oneof(ctx, fc.constant([]), items) : items).map((as) => new Set(as)) -} +const setArbitrary = + (item: LazyArbitrary, ctx: ArbitraryGenerationContext): LazyArbitrary> => (fc) => { + const items = fc.array(item(fc)) + return (ctx.depthIdentifier !== undefined ? fc.oneof(ctx, fc.constant([]), items) : items).map((as) => new Set(as)) + } const readonlySetPretty = (item: pretty_.Pretty): pretty_.Pretty> => (set) => `new Set([${Array.from(set.values()).map((a) => item(a)).join(", ")}])` @@ -7223,10 +7229,10 @@ export class BigDecimalFromNumber extends transformOrFail( ).annotations({ identifier: "BigDecimalFromNumber" }) {} /** - * @category type id + * @category schema id * @since 3.10.0 */ -export const GreaterThanBigDecimalTypeId: unique symbol = Symbol.for("effect/Schema/TypeId/GreaterThanBigDecimal") +export const GreaterThanBigDecimalSchemaId: unique symbol = Symbol.for("effect/SchemaId/GreaterThanBigDecimal") /** * @category BigDecimal filters @@ -7239,18 +7245,19 @@ export const greaterThanBigDecimal = ( (self: Schema): filter> => self.pipe( filter((a) => bigDecimal_.greaterThan(a, min), { - typeId: { id: GreaterThanBigDecimalTypeId, annotation: { min } }, + schemaId: GreaterThanBigDecimalSchemaId, + [GreaterThanBigDecimalSchemaId]: { min }, description: `a BigDecimal greater than ${bigDecimal_.format(min)}`, ...annotations }) ) /** - * @category type id + * @category schema id * @since 3.10.0 */ -export const GreaterThanOrEqualToBigDecimalTypeId: unique symbol = Symbol.for( - "effect/Schema/TypeId/GreaterThanOrEqualToBigDecimal" +export const GreaterThanOrEqualToBigDecimalSchemaId: unique symbol = Symbol.for( + "effect/schema/GreaterThanOrEqualToBigDecimal" ) /** @@ -7264,17 +7271,18 @@ export const greaterThanOrEqualToBigDecimal = (self: Schema): filter> => self.pipe( filter((a) => bigDecimal_.greaterThanOrEqualTo(a, min), { - typeId: { id: GreaterThanOrEqualToBigDecimalTypeId, annotation: { min } }, + schemaId: GreaterThanOrEqualToBigDecimalSchemaId, + [GreaterThanOrEqualToBigDecimalSchemaId]: { min }, description: `a BigDecimal greater than or equal to ${bigDecimal_.format(min)}`, ...annotations }) ) /** - * @category type id + * @category schema id * @since 3.10.0 */ -export const LessThanBigDecimalTypeId: unique symbol = Symbol.for("effect/Schema/TypeId/LessThanBigDecimal") +export const LessThanBigDecimalSchemaId: unique symbol = Symbol.for("effect/SchemaId/LessThanBigDecimal") /** * @category BigDecimal filters @@ -7287,18 +7295,19 @@ export const lessThanBigDecimal = ( (self: Schema): filter> => self.pipe( filter((a) => bigDecimal_.lessThan(a, max), { - typeId: { id: LessThanBigDecimalTypeId, annotation: { max } }, + schemaId: LessThanBigDecimalSchemaId, + [LessThanBigDecimalSchemaId]: { max }, description: `a BigDecimal less than ${bigDecimal_.format(max)}`, ...annotations }) ) /** - * @category type id + * @category schema id * @since 3.10.0 */ -export const LessThanOrEqualToBigDecimalTypeId: unique symbol = Symbol.for( - "effect/Schema/TypeId/LessThanOrEqualToBigDecimal" +export const LessThanOrEqualToBigDecimalSchemaId: unique symbol = Symbol.for( + "effect/schema/LessThanOrEqualToBigDecimal" ) /** @@ -7312,18 +7321,19 @@ export const lessThanOrEqualToBigDecimal = ( (self: Schema): filter> => self.pipe( filter((a) => bigDecimal_.lessThanOrEqualTo(a, max), { - typeId: { id: LessThanOrEqualToBigDecimalTypeId, annotation: { max } }, + schemaId: LessThanOrEqualToBigDecimalSchemaId, + [LessThanOrEqualToBigDecimalSchemaId]: { max }, description: `a BigDecimal less than or equal to ${bigDecimal_.format(max)}`, ...annotations }) ) /** - * @category type id + * @category schema id * @since 3.10.0 */ -export const PositiveBigDecimalTypeId: unique symbol = Symbol.for( - "effect/Schema/TypeId/PositiveBigDecimal" +export const PositiveBigDecimalSchemaId: unique symbol = Symbol.for( + "effect/schema/PositiveBigDecimal" ) /** @@ -7336,7 +7346,7 @@ export const positiveBigDecimal = ( (self: Schema): filter> => self.pipe( filter((a) => bigDecimal_.isPositive(a), { - typeId: { id: PositiveBigDecimalTypeId, annotation: {} }, + schemaId: PositiveBigDecimalSchemaId, description: `a positive BigDecimal`, ...annotations }) @@ -7354,11 +7364,11 @@ export const PositiveBigDecimalFromSelf: filter> ) /** - * @category type id + * @category schema id * @since 3.10.0 */ -export const NonNegativeBigDecimalTypeId: unique symbol = Symbol.for( - "effect/Schema/TypeId/NonNegativeBigDecimal" +export const NonNegativeBigDecimalSchemaId: unique symbol = Symbol.for( + "effect/schema/NonNegativeBigDecimal" ) /** @@ -7371,7 +7381,7 @@ export const nonNegativeBigDecimal = ( (self: Schema): filter> => self.pipe( filter((a) => a.value >= 0n, { - typeId: { id: NonNegativeBigDecimalTypeId, annotation: {} }, + schemaId: NonNegativeBigDecimalSchemaId, description: `a non-negative BigDecimal`, ...annotations }) @@ -7389,11 +7399,11 @@ export const NonNegativeBigDecimalFromSelf: filter( (self: Schema): filter> => self.pipe( filter((a) => bigDecimal_.isNegative(a), { - typeId: { id: NegativeBigDecimalTypeId, annotation: {} }, + schemaId: NegativeBigDecimalSchemaId, description: `a negative BigDecimal`, ...annotations }) @@ -7424,11 +7434,11 @@ export const NegativeBigDecimalFromSelf: filter> ) /** - * @category type id + * @category schema id * @since 3.10.0 */ -export const NonPositiveBigDecimalTypeId: unique symbol = Symbol.for( - "effect/Schema/TypeId/NonPositiveBigDecimal" +export const NonPositiveBigDecimalSchemaId: unique symbol = Symbol.for( + "effect/schema/NonPositiveBigDecimal" ) /** @@ -7441,7 +7451,7 @@ export const nonPositiveBigDecimal = ( (self: Schema): filter> => self.pipe( filter((a) => a.value <= 0n, { - typeId: { id: NonPositiveBigDecimalTypeId, annotation: {} }, + schemaId: NonPositiveBigDecimalSchemaId, description: `a non-positive BigDecimal`, ...annotations }) @@ -7459,10 +7469,10 @@ export const NonPositiveBigDecimalFromSelf: filter( (self: Schema): filter> => self.pipe( filter((a) => bigDecimal_.between(a, { minimum, maximum }), { - typeId: { id: BetweenBigDecimalTypeId, annotation: { maximum, minimum } }, + schemaId: BetweenBigDecimalSchemaId, + [BetweenBigDecimalSchemaId]: { maximum, minimum }, description: `a BigDecimal between ${bigDecimal_.format(minimum)} and ${bigDecimal_.format(maximum)}`, ...annotations }) @@ -7497,10 +7508,11 @@ export const clampBigDecimal = { strict: false, decode: (self) => bigDecimal_.clamp(self, { minimum, maximum }), encode: identity } ) -const chunkArbitrary = (item: LazyArbitrary, ctx: GenerationContext): LazyArbitrary> => (fc) => { - const items = fc.array(item(fc)) - return (ctx.depthIdentifier !== undefined ? fc.oneof(ctx, fc.constant([]), items) : items).map(chunk_.fromIterable) -} +const chunkArbitrary = + (item: LazyArbitrary, ctx: ArbitraryGenerationContext): LazyArbitrary> => (fc) => { + const items = fc.array(item(fc)) + return (ctx.depthIdentifier !== undefined ? fc.oneof(ctx, fc.constant([]), items) : items).map(chunk_.fromIterable) + } const chunkPretty = (item: pretty_.Pretty): pretty_.Pretty> => (c) => `Chunk(${chunk_.toReadonlyArray(c).map(item).join(", ")})` @@ -7819,7 +7831,7 @@ export interface Class = Struct | { - readonly [refineTypeId]: HasFields + readonly [RefineSchemaId]: HasFields } const isField = (u: unknown) => isSchema(u) || isPropertySignature(u) @@ -7828,7 +7840,7 @@ const isFields = (fields: object): fields is Field util_.ownKeys(fields).every((key) => isField((fields as any)[key])) const getFields = (hasFields: HasFields): Fields => - "fields" in hasFields ? hasFields.fields : getFields(hasFields[refineTypeId]) + "fields" in hasFields ? hasFields.fields : getFields(hasFields[RefineSchemaId]) const getSchemaFromFieldsOr = (fieldsOr: Fields | HasFields): Schema.Any => isFields(fieldsOr) ? Struct(fieldsOr) : isSchema(fieldsOr) ? fieldsOr : Struct(getFields(fieldsOr)) @@ -8867,7 +8879,7 @@ export const Exit = (item: LazyArbitrary, ctx: GenerationContext): LazyArbitrary> => (fc) => { + (item: LazyArbitrary, ctx: ArbitraryGenerationContext): LazyArbitrary> => (fc) => { const items = fc.array(item(fc)) return (ctx.depthIdentifier !== undefined ? fc.oneof(ctx, fc.constant([]), items) : items).map( hashSet_.fromIterable @@ -8956,7 +8968,7 @@ export const HashSet = (value: Value): HashSet const hashMapArbitrary = ( key: LazyArbitrary, value: LazyArbitrary, - ctx: GenerationContext + ctx: ArbitraryGenerationContext ): LazyArbitrary> => (fc) => { const items = fc.array(fc.tuple(key(fc), value(fc))) @@ -9058,10 +9070,11 @@ export const HashMap = ({ key, value ) } -const listArbitrary = (item: LazyArbitrary, ctx: GenerationContext): LazyArbitrary> => (fc) => { - const items = fc.array(item(fc)) - return (ctx.depthIdentifier !== undefined ? fc.oneof(ctx, fc.constant([]), items) : items).map(list_.fromIterable) -} +const listArbitrary = + (item: LazyArbitrary, ctx: ArbitraryGenerationContext): LazyArbitrary> => (fc) => { + const items = fc.array(item(fc)) + return (ctx.depthIdentifier !== undefined ? fc.oneof(ctx, fc.constant([]), items) : items).map(list_.fromIterable) + } const listPretty = (item: pretty_.Pretty): pretty_.Pretty> => (set) => `List(${Array.from(set).map((a) => item(a)).join(", ")})` @@ -9142,14 +9155,17 @@ export const List = (value: Value): List => { ) } -const sortedSetArbitrary = - (item: LazyArbitrary, ord: Order.Order, ctx: GenerationContext): LazyArbitrary> => - (fc) => { - const items = fc.array(item(fc)) - return (ctx.depthIdentifier !== undefined ? fc.oneof(ctx, fc.constant([]), items) : items).map((as) => - sortedSet_.fromIterable(as, ord) - ) - } +const sortedSetArbitrary = ( + item: LazyArbitrary, + ord: Order.Order, + ctx: ArbitraryGenerationContext +): LazyArbitrary> => +(fc) => { + const items = fc.array(item(fc)) + return (ctx.depthIdentifier !== undefined ? fc.oneof(ctx, fc.constant([]), items) : items).map((as) => + sortedSet_.fromIterable(as, ord) + ) +} const sortedSetPretty = (item: pretty_.Pretty): pretty_.Pretty> => (set) => `new SortedSet([${Array.from(sortedSet_.values(set)).map((a) => item(a)).join(", ")}])` diff --git a/packages/effect/src/SchemaAST.ts b/packages/effect/src/SchemaAST.ts index 51a4baf01d..31b6d17c97 100644 --- a/packages/effect/src/SchemaAST.ts +++ b/packages/effect/src/SchemaAST.ts @@ -60,19 +60,19 @@ export type BrandAnnotation = Arr.NonEmptyReadonlyArray * @category annotations * @since 3.10.0 */ -export const BrandAnnotationId = Symbol.for("effect/Schema/annotation/Brand") +export const BrandAnnotationId: unique symbol = Symbol.for("effect/annotation/Brand") /** * @category annotations * @since 3.10.0 */ -export type TypeAnnotation = symbol +export type SchemaIdAnnotation = string | symbol /** * @category annotations * @since 3.10.0 */ -export const TypeAnnotationId = Symbol.for("effect/Schema/annotation/Type") +export const SchemaIdAnnotationId: unique symbol = Symbol.for("effect/annotation/SchemaId") /** * @category annotations @@ -87,7 +87,7 @@ export type MessageAnnotation = (issue: ParseIssue) => string | Effect | * @category annotations * @since 3.10.0 */ -export const MessageAnnotationId = Symbol.for("effect/Schema/annotation/Message") +export const MessageAnnotationId: unique symbol = Symbol.for("effect/annotation/Message") /** * @category annotations @@ -99,7 +99,7 @@ export type MissingMessageAnnotation = () => string | Effect * @category annotations * @since 3.10.0 */ -export const MissingMessageAnnotationId = Symbol.for("effect/Schema/annotation/MissingMessage") +export const MissingMessageAnnotationId: unique symbol = Symbol.for("effect/annotation/MissingMessage") /** * @category annotations @@ -111,7 +111,7 @@ export type IdentifierAnnotation = string * @category annotations * @since 3.10.0 */ -export const IdentifierAnnotationId = Symbol.for("effect/Schema/annotation/Identifier") +export const IdentifierAnnotationId: unique symbol = Symbol.for("effect/annotation/Identifier") /** * @category annotations @@ -123,7 +123,7 @@ export type TitleAnnotation = string * @category annotations * @since 3.10.0 */ -export const TitleAnnotationId = Symbol.for("effect/Schema/annotation/Title") +export const TitleAnnotationId: unique symbol = Symbol.for("effect/annotation/Title") /** * @category annotations @@ -135,7 +135,7 @@ export type DescriptionAnnotation = string * @category annotations * @since 3.10.0 */ -export const DescriptionAnnotationId = Symbol.for("effect/Schema/annotation/Description") +export const DescriptionAnnotationId: unique symbol = Symbol.for("effect/annotation/Description") /** * @category annotations @@ -147,7 +147,7 @@ export type ExamplesAnnotation = Arr.NonEmptyReadonlyArray * @category annotations * @since 3.10.0 */ -export const ExamplesAnnotationId = Symbol.for("effect/Schema/annotation/Examples") +export const ExamplesAnnotationId: unique symbol = Symbol.for("effect/annotation/Examples") /** * @category annotations @@ -159,7 +159,7 @@ export type DefaultAnnotation = A * @category annotations * @since 3.10.0 */ -export const DefaultAnnotationId = Symbol.for("effect/Schema/annotation/Default") +export const DefaultAnnotationId: unique symbol = Symbol.for("effect/annotation/Default") /** * @category annotations @@ -171,7 +171,7 @@ export type JSONSchemaAnnotation = object * @category annotations * @since 3.10.0 */ -export const JSONSchemaAnnotationId = Symbol.for("effect/Schema/annotation/JSONSchema") +export const JSONSchemaAnnotationId: unique symbol = Symbol.for("effect/annotation/JSONSchema") /** * @category annotations @@ -183,7 +183,7 @@ export type DocumentationAnnotation = string * @category annotations * @since 3.10.0 */ -export const DocumentationAnnotationId = Symbol.for("effect/Schema/annotation/Documentation") +export const DocumentationAnnotationId: unique symbol = Symbol.for("effect/annotation/Documentation") /** * @category annotations @@ -195,7 +195,7 @@ export type ConcurrencyAnnotation = Concurrency | undefined * @category annotations * @since 3.10.0 */ -export const ConcurrencyAnnotationId = Symbol.for("effect/Schema/annotation/Concurrency") +export const ConcurrencyAnnotationId: unique symbol = Symbol.for("effect/annotation/Concurrency") /** * @category annotations @@ -207,7 +207,7 @@ export type BatchingAnnotation = boolean | "inherit" | undefined * @category annotations * @since 3.10.0 */ -export const BatchingAnnotationId = Symbol.for("effect/Schema/annotation/Batching") +export const BatchingAnnotationId: unique symbol = Symbol.for("effect/annotation/Batching") /** * @category annotations @@ -219,13 +219,13 @@ export type ParseIssueTitleAnnotation = (issue: ParseIssue) => string | undefine * @category annotations * @since 3.10.0 */ -export const ParseIssueTitleAnnotationId = Symbol.for("effect/Schema/annotation/ParseIssueTitle") +export const ParseIssueTitleAnnotationId: unique symbol = Symbol.for("effect/annotation/ParseIssueTitle") /** * @category annotations * @since 3.10.0 */ -export const ParseOptionsAnnotationId = Symbol.for("effect/Schema/annotation/ParseOptions") +export const ParseOptionsAnnotationId: unique symbol = Symbol.for("effect/annotation/ParseOptions") /** * @category annotations @@ -237,27 +237,22 @@ export type DecodingFallbackAnnotation = (issue: ParseIssue) => Effect(SurrogateAnnotationId) const getStableFilterAnnotation = getAnnotation(StableFilterAnnotationId) @@ -400,11 +399,35 @@ const getStableFilterAnnotation = getAnnotation(StableFi export const hasStableFilter = (annotated: Annotated) => Option.exists(getStableFilterAnnotation(annotated), (b) => b === true) -const JSONIdentifierAnnotationId = Symbol.for("effect/Schema/annotation/JSONIdentifier") +/** + * @category annotations + * @since 3.10.0 + */ +export const JSONIdentifierAnnotationId: unique symbol = Symbol.for("effect/annotation/JSONIdentifier") -/** @internal */ +/** + * @category annotations + * @since 3.10.0 + */ export const getJSONIdentifierAnnotation = getAnnotation(JSONIdentifierAnnotationId) +/** + * @category annotations + * @since 3.10.0 + */ +export const getJSONIdentifier = (annotated: Annotated) => + Option.orElse(getJSONIdentifierAnnotation(annotated), () => getIdentifierAnnotation(annotated)) + +// ------------------------------------------------------------------------------------- +// schema ids +// ------------------------------------------------------------------------------------- + +/** + * @category schema id + * @since 3.10.0 + */ +export const ParseJsonSchemaId: unique symbol = Symbol.for("effect/schema/ParseJson") + /** * @category model * @since 3.10.0 @@ -2485,10 +2508,6 @@ export const blackListAnnotations = return out } -/** @internal */ -export const getJSONIdentifier = (annotated: Annotated) => - Option.orElse(getJSONIdentifierAnnotation(annotated), () => getIdentifierAnnotation(annotated)) - // To generate a JSON Schema from a recursive schema, an `identifier` annotation // is required. So, when we calculate the encodedAST, we need to preserve the // annotation in the form of an internal custom annotation that acts as a diff --git a/packages/effect/src/SchemaEquivalence.ts b/packages/effect/src/SchemaEquivalence.ts index e29b4349e7..4e8ed74472 100644 --- a/packages/effect/src/SchemaEquivalence.ts +++ b/packages/effect/src/SchemaEquivalence.ts @@ -14,24 +14,18 @@ import type * as Schema from "./Schema.js" import * as AST from "./SchemaAST.js" /** - * @category hooks - * @since 3.10.0 - */ -export const EquivalenceHookId: unique symbol = Symbol.for("effect/Schema/EquivalenceHookId") - -/** - * @category hooks + * @category annotations * @since 3.10.0 */ -export type EquivalenceHookId = typeof EquivalenceHookId +export type EquivalenceAnnotation = readonly []> = ( + ...equivalences: { readonly [K in keyof TypeParameters]: Equivalence.Equivalence } +) => Equivalence.Equivalence /** * @category annotations * @since 3.10.0 */ -export const equivalence = - (handler: (...args: ReadonlyArray>) => Equivalence.Equivalence) => - (self: Schema.Schema): Schema.Schema => self.annotations({ [EquivalenceHookId]: handler }) +export const EquivalenceAnnotationId: unique symbol = Symbol.for("effect/annotation/Equivalence") /** * @category Equivalence @@ -39,14 +33,10 @@ export const equivalence = */ export const make = (schema: Schema.Schema): Equivalence.Equivalence => go(schema.ast, []) -const getHook = AST.getAnnotation< - (...args: ReadonlyArray>) => Equivalence.Equivalence ->( - EquivalenceHookId -) +const getAnnotation = AST.getAnnotation>(EquivalenceAnnotationId) const go = (ast: AST.AST, path: ReadonlyArray): Equivalence.Equivalence => { - const hook = getHook(ast) + const hook = getAnnotation(ast) if (Option.isSome(hook)) { switch (ast._tag) { case "Declaration": diff --git a/packages/effect/src/internal/schema/filters.ts b/packages/effect/src/internal/schema/filters.ts index 470997f9b7..710373d7b4 100644 --- a/packages/effect/src/internal/schema/filters.ts +++ b/packages/effect/src/internal/schema/filters.ts @@ -1,89 +1,86 @@ import type * as Schema from "../../Schema.js" /** @internal */ -export const GreaterThanTypeId: Schema.GreaterThanTypeId = Symbol.for( - "effect/Schema/TypeId/GreaterThan" -) as Schema.GreaterThanTypeId +export const GreaterThanSchemaId: Schema.GreaterThanSchemaId = Symbol.for( + "effect/schema/GreaterThan" +) as Schema.GreaterThanSchemaId /** @internal */ -export const GreaterThanOrEqualToTypeId: Schema.GreaterThanOrEqualToTypeId = Symbol.for( - "effect/Schema/TypeId/GreaterThanOrEqualTo" -) as Schema.GreaterThanOrEqualToTypeId +export const GreaterThanOrEqualToSchemaId: Schema.GreaterThanOrEqualToSchemaId = Symbol.for( + "effect/schema/GreaterThanOrEqualTo" +) as Schema.GreaterThanOrEqualToSchemaId /** @internal */ -export const LessThanTypeId: Schema.LessThanTypeId = Symbol.for( - "effect/Schema/TypeId/LessThan" -) as Schema.LessThanTypeId +export const LessThanSchemaId: Schema.LessThanSchemaId = Symbol.for( + "effect/schema/LessThan" +) as Schema.LessThanSchemaId /** @internal */ -export const LessThanOrEqualToTypeId: Schema.LessThanOrEqualToTypeId = Symbol.for( - "effect/Schema/TypeId/LessThanOrEqualTo" -) as Schema.LessThanOrEqualToTypeId +export const LessThanOrEqualToSchemaId: Schema.LessThanOrEqualToSchemaId = Symbol.for( + "effect/schema/LessThanOrEqualTo" +) as Schema.LessThanOrEqualToSchemaId /** @internal */ -export const IntTypeId: Schema.IntTypeId = Symbol.for( - "effect/Schema/TypeId/Int" -) as Schema.IntTypeId +export const IntSchemaId: Schema.IntSchemaId = Symbol.for( + "effect/schema/Int" +) as Schema.IntSchemaId /** @internal */ -export const BetweenTypeId: Schema.BetweenTypeId = Symbol.for( - "effect/Schema/TypeId/Between" -) as Schema.BetweenTypeId +export const BetweenSchemaId: Schema.BetweenSchemaId = Symbol.for( + "effect/schema/Between" +) as Schema.BetweenSchemaId /** @internal */ -export const GreaterThanBigintTypeId: Schema.GreaterThanBigIntTypeId = Symbol.for( - "effect/Schema/TypeId/GreaterThanBigint" -) as Schema.GreaterThanBigIntTypeId +export const GreaterThanBigintSchemaId: Schema.GreaterThanBigIntSchemaId = Symbol.for( + "effect/schema/GreaterThanBigint" +) as Schema.GreaterThanBigIntSchemaId /** @internal */ -export const GreaterThanOrEqualToBigIntTypeId: Schema.GreaterThanOrEqualToBigIntTypeId = Symbol.for( - "effect/Schema/TypeId/GreaterThanOrEqualToBigint" -) as Schema.GreaterThanOrEqualToBigIntTypeId +export const GreaterThanOrEqualToBigIntSchemaId: Schema.GreaterThanOrEqualToBigIntSchemaId = Symbol.for( + "effect/schema/GreaterThanOrEqualToBigint" +) as Schema.GreaterThanOrEqualToBigIntSchemaId /** @internal */ -export const LessThanBigIntTypeId: Schema.LessThanBigIntTypeId = Symbol.for( - "effect/Schema/TypeId/LessThanBigint" -) as Schema.LessThanBigIntTypeId +export const LessThanBigIntSchemaId: Schema.LessThanBigIntSchemaId = Symbol.for( + "effect/schema/LessThanBigint" +) as Schema.LessThanBigIntSchemaId /** @internal */ -export const LessThanOrEqualToBigIntTypeId: Schema.LessThanOrEqualToBigIntTypeId = Symbol.for( - "effect/Schema/TypeId/LessThanOrEqualToBigint" -) as Schema.LessThanOrEqualToBigIntTypeId +export const LessThanOrEqualToBigIntSchemaId: Schema.LessThanOrEqualToBigIntSchemaId = Symbol.for( + "effect/schema/LessThanOrEqualToBigint" +) as Schema.LessThanOrEqualToBigIntSchemaId /** @internal */ -export const BetweenBigintTypeId: Schema.BetweenBigIntTypeId = Symbol.for( - "effect/Schema/TypeId/BetweenBigint" -) as Schema.BetweenBigIntTypeId +export const BetweenBigintSchemaId: Schema.BetweenBigIntSchemaId = Symbol.for( + "effect/schema/BetweenBigint" +) as Schema.BetweenBigIntSchemaId /** @internal */ -export const MinLengthTypeId: Schema.MinLengthTypeId = Symbol.for( - "effect/Schema/TypeId/MinLength" -) as Schema.MinLengthTypeId +export const MinLengthSchemaId: Schema.MinLengthSchemaId = Symbol.for( + "effect/schema/MinLength" +) as Schema.MinLengthSchemaId /** @internal */ -export const MaxLengthTypeId: Schema.MaxLengthTypeId = Symbol.for( - "effect/Schema/TypeId/MaxLength" -) as Schema.MaxLengthTypeId +export const MaxLengthSchemaId: Schema.MaxLengthSchemaId = Symbol.for( + "effect/schema/MaxLength" +) as Schema.MaxLengthSchemaId /** @internal */ -export const LengthTypeId: Schema.LengthTypeId = Symbol.for( - "effect/Schema/TypeId/Length" -) as Schema.LengthTypeId +export const LengthSchemaId: Schema.LengthSchemaId = Symbol.for( + "effect/schema/Length" +) as Schema.LengthSchemaId /** @internal */ -export const MinItemsTypeId: Schema.MinItemsTypeId = Symbol.for( - "effect/Schema/TypeId/MinItems" -) as Schema.MinItemsTypeId +export const MinItemsSchemaId: Schema.MinItemsSchemaId = Symbol.for( + "effect/schema/MinItems" +) as Schema.MinItemsSchemaId /** @internal */ -export const MaxItemsTypeId: Schema.MaxItemsTypeId = Symbol.for( - "effect/Schema/TypeId/MaxItems" -) as Schema.MaxItemsTypeId +export const MaxItemsSchemaId: Schema.MaxItemsSchemaId = Symbol.for( + "effect/schema/MaxItems" +) as Schema.MaxItemsSchemaId /** @internal */ -export const ItemsCountTypeId: Schema.ItemsCountTypeId = Symbol.for( - "effect/Schema/TypeId/ItemsCount" -) as Schema.ItemsCountTypeId - -/** @internal */ -export const ParseJsonTypeId: unique symbol = Symbol.for("effect/Schema/TypeId/ParseJson") +export const ItemsCountSchemaId: Schema.ItemsCountSchemaId = Symbol.for( + "effect/schema/ItemsCount" +) as Schema.ItemsCountSchemaId diff --git a/packages/effect/test/Schema/Arbitrary/Arbitrary.test.ts b/packages/effect/test/Schema/Arbitrary/Arbitrary.test.ts index a1037e52f9..1c4f7c4ef5 100644 --- a/packages/effect/test/Schema/Arbitrary/Arbitrary.test.ts +++ b/packages/effect/test/Schema/Arbitrary/Arbitrary.test.ts @@ -256,7 +256,7 @@ details: Generating an Arbitrary for this schema requires at least one enum`) const schema = S.Struct({ a: S.String, as: S.Array( - S.suspend((): S.Schema => schema).pipe(Arbitrary.arbitrary(() => () => arb)) + S.suspend((): S.Schema => schema).annotations({ arbitrary: () => () => arb }) ) }) expectValidArbitrary(schema) @@ -620,7 +620,7 @@ details: Generating an Arbitrary for this schema requires at least one enum`) describe("should handle annotations", () => { const expectHook = (source: S.Schema) => { - const schema = source.pipe(Arbitrary.arbitrary(() => (fc) => fc.constant("custom arbitrary") as any)) + const schema = source.annotations({ arbitrary: () => (fc) => fc.constant("custom arbitrary") as any }) const arb = Arbitrary.makeLazy(schema)(fc) expect(fc.sample(arb, 1)[0]).toEqual("custom arbitrary") } diff --git a/packages/effect/test/Schema/Pretty.test.ts b/packages/effect/test/Schema/Pretty.test.ts index a4cbff8f7e..1a25058339 100644 --- a/packages/effect/test/Schema/Pretty.test.ts +++ b/packages/effect/test/Schema/Pretty.test.ts @@ -434,7 +434,7 @@ schema (Declaration): `) describe("should handle annotations", () => { const expectHook = (source: S.Schema) => { - const schema = source.pipe(Pretty.pretty(() => () => "custom pretty")) + const schema = source.annotations({ pretty: () => () => "custom pretty" }) const pretty = Pretty.make(schema) expect(pretty(null as any)).toEqual("custom pretty") } diff --git a/packages/effect/test/Schema/Schema/annotations.test.ts b/packages/effect/test/Schema/Schema/annotations.test.ts index 6fafe96159..29de6d2d77 100644 --- a/packages/effect/test/Schema/Schema/annotations.test.ts +++ b/packages/effect/test/Schema/Schema/annotations.test.ts @@ -142,3 +142,16 @@ describe(".annotations()", () => { expect(Pretty.make(AFromSelf)(new A("value"))).toEqual(`new A("value")`) }) }) + +declare module "effect/Schema" { + namespace Annotations { + interface Schema extends Doc { + formName?: string + } + } +} + +it("should support custom annotations", () => { + const schema = S.String.annotations({ formName: "a" }) + expect(schema.ast.annotations["formName"]).toEqual("a") +}) diff --git a/packages/effect/test/Schema/Schema/brand.test.ts b/packages/effect/test/Schema/Schema/brand.test.ts index 3bca1e0572..541fabeee8 100644 --- a/packages/effect/test/Schema/Schema/brand.test.ts +++ b/packages/effect/test/Schema/Schema/brand.test.ts @@ -61,7 +61,7 @@ describe("brand", () => { ) expect(schema.ast.annotations).toEqual({ - [AST.TypeAnnotationId]: S.IntTypeId, + [AST.SchemaIdAnnotationId]: S.IntSchemaId, [AST.BrandAnnotationId]: ["A"], [AST.TitleAnnotationId]: `integer & Brand<"A">`, [AST.DescriptionAnnotationId]: "an A brand", @@ -79,7 +79,7 @@ describe("brand", () => { ) expect(schema.ast.annotations).toEqual({ - [AST.TypeAnnotationId]: S.IntTypeId, + [AST.SchemaIdAnnotationId]: S.IntSchemaId, [AST.BrandAnnotationId]: ["A", "B"], [AST.TitleAnnotationId]: `integer & Brand<"A"> & Brand<"B">`, [AST.DescriptionAnnotationId]: "a B brand", @@ -98,7 +98,7 @@ describe("brand", () => { }) ) expect(schema.ast.annotations).toEqual({ - [AST.TypeAnnotationId]: S.IntTypeId, + [AST.SchemaIdAnnotationId]: S.IntSchemaId, [AST.BrandAnnotationId]: [A, B], [AST.TitleAnnotationId]: "integer & Brand & Brand", [AST.DescriptionAnnotationId]: "a B brand", diff --git a/packages/effect/test/Schema/Schema/exports.test.ts b/packages/effect/test/Schema/Schema/exports.test.ts index d5e0e60610..c982f96eda 100644 --- a/packages/effect/test/Schema/Schema/exports.test.ts +++ b/packages/effect/test/Schema/Schema/exports.test.ts @@ -22,30 +22,30 @@ it("exports", () => { expect(S.validateOption).exist expect(S.validateEither).exist - expect(S.GreaterThanBigIntTypeId).exist - expect(S.GreaterThanOrEqualToBigIntTypeId).exist - expect(S.LessThanBigIntTypeId).exist - expect(S.LessThanOrEqualToBigIntTypeId).exist - expect(S.BetweenBigIntTypeId).exist - expect(S.BrandTypeId).exist - expect(S.FiniteTypeId).exist - expect(S.GreaterThanTypeId).exist - expect(S.GreaterThanOrEqualToTypeId).exist - expect(S.MultipleOfTypeId).exist - expect(S.IntTypeId).exist - expect(S.LessThanTypeId).exist - expect(S.LessThanOrEqualToTypeId).exist - expect(S.BetweenTypeId).exist - expect(S.NonNaNTypeId).exist - expect(S.InstanceOfTypeId).exist - expect(S.MinItemsTypeId).exist - expect(S.MaxItemsTypeId).exist - expect(S.ItemsCountTypeId).exist - expect(S.TrimmedTypeId).exist - expect(S.PatternTypeId).exist - expect(S.StartsWithTypeId).exist - expect(S.EndsWithTypeId).exist - expect(S.IncludesTypeId).exist - expect(S.UUIDTypeId).exist - expect(S.ULIDTypeId).exist + expect(S.GreaterThanBigIntSchemaId).exist + expect(S.GreaterThanOrEqualToBigIntSchemaId).exist + expect(S.LessThanBigIntSchemaId).exist + expect(S.LessThanOrEqualToBigIntSchemaId).exist + expect(S.BetweenBigIntSchemaId).exist + expect(S.BrandSchemaId).exist + expect(S.FiniteSchemaId).exist + expect(S.GreaterThanSchemaId).exist + expect(S.GreaterThanOrEqualToSchemaId).exist + expect(S.MultipleOfSchemaId).exist + expect(S.IntSchemaId).exist + expect(S.LessThanSchemaId).exist + expect(S.LessThanOrEqualToSchemaId).exist + expect(S.BetweenSchemaId).exist + expect(S.NonNaNSchemaId).exist + expect(S.InstanceOfSchemaId).exist + expect(S.MinItemsSchemaId).exist + expect(S.MaxItemsSchemaId).exist + expect(S.ItemsCountSchemaId).exist + expect(S.TrimmedSchemaId).exist + expect(S.PatternSchemaId).exist + expect(S.StartsWithSchemaId).exist + expect(S.EndsWithSchemaId).exist + expect(S.IncludesSchemaId).exist + expect(S.UUIDSchemaId).exist + expect(S.ULIDSchemaId).exist }) diff --git a/packages/effect/test/Schema/Schema/filter.test.ts b/packages/effect/test/Schema/Schema/filter.test.ts index bc71297250..5d3e627e8e 100644 --- a/packages/effect/test/Schema/Schema/filter.test.ts +++ b/packages/effect/test/Schema/Schema/filter.test.ts @@ -8,7 +8,7 @@ describe("filter", () => { it("annotation options", () => { const schema = S.String.pipe( S.filter((s): s is string => s.length === 1, { - typeId: Symbol.for("Char"), + schemaId: Symbol.for("Char"), description: "description", documentation: "documentation", examples: ["examples"], @@ -18,7 +18,7 @@ describe("filter", () => { }) ) expect(schema.ast.annotations).toEqual({ - [AST.TypeAnnotationId]: Symbol.for("Char"), + [AST.SchemaIdAnnotationId]: Symbol.for("Char"), [AST.DescriptionAnnotationId]: "description", [AST.DocumentationAnnotationId]: "documentation", [AST.ExamplesAnnotationId]: [ diff --git a/packages/effect/test/Schema/Schema/instanceOf.test.ts b/packages/effect/test/Schema/Schema/instanceOf.test.ts index 3ca32739a3..5de4eb4a0d 100644 --- a/packages/effect/test/Schema/Schema/instanceOf.test.ts +++ b/packages/effect/test/Schema/Schema/instanceOf.test.ts @@ -17,7 +17,7 @@ describe("instanceOf", () => { it("annotations", () => { const schema = S.instanceOf(Set, { description: "my description" }) expect(schema.ast.annotations[AST.DescriptionAnnotationId]).toEqual("my description") - expect(schema.ast.annotations[S.InstanceOfTypeId]).toEqual({ constructor: Set }) + expect(schema.ast.annotations[S.InstanceOfSchemaId]).toEqual({ constructor: Set }) }) it("decoding", async () => { diff --git a/packages/effect/test/Schema/SchemaAST/annotations.test.ts b/packages/effect/test/Schema/SchemaAST/annotations.test.ts index b1c71a3fe9..6506f24bbe 100644 --- a/packages/effect/test/Schema/SchemaAST/annotations.test.ts +++ b/packages/effect/test/Schema/SchemaAST/annotations.test.ts @@ -2,7 +2,7 @@ import * as AST from "effect/SchemaAST" import { describe, expect, it } from "vitest" describe("annotations", () => { - it("should ad annotations", () => { + it("should add annotations", () => { const symA = Symbol.for("a") const ast = AST.annotations(AST.stringKeyword, { [symA]: "A" }) expect(ast instanceof AST.StringKeyword).toBe(true) diff --git a/packages/effect/test/Schema/SchemaEquivalence.test.ts b/packages/effect/test/Schema/SchemaEquivalence.test.ts index e5b2b0c0a9..c372e21e76 100644 --- a/packages/effect/test/Schema/SchemaEquivalence.test.ts +++ b/packages/effect/test/Schema/SchemaEquivalence.test.ts @@ -711,7 +711,7 @@ schema (NeverKeyword): never`) describe("should handle annotations", () => { const expectHook = (source: S.Schema) => { - const schema = source.pipe(E.equivalence(() => () => true)) + const schema = source.annotations({ equivalence: () => () => true }) const eq = E.make(schema) expect(eq("a" as any, "b" as any)).toEqual(true) } diff --git a/packages/platform/src/OpenApiJsonSchema.ts b/packages/platform/src/OpenApiJsonSchema.ts index 4151a69e20..8a7e2487a5 100644 --- a/packages/platform/src/OpenApiJsonSchema.ts +++ b/packages/platform/src/OpenApiJsonSchema.ts @@ -312,7 +312,8 @@ const getRefinementInnerTransformation = (ast: AST.Refinement): AST.AST | undefi } } -const isParseJsonTransformation = (ast: AST.AST): boolean => ast.annotations[AST.TypeAnnotationId] === ParseJsonTypeId +const isParseJsonTransformation = (ast: AST.AST): boolean => + ast.annotations[AST.SchemaIdAnnotationId] === AST.ParseJsonSchemaId const isOverrideAnnotation = (jsonSchema: JsonSchema): boolean => { return ("type" in jsonSchema) || ("oneOf" in jsonSchema) || ("anyOf" in jsonSchema) || ("const" in jsonSchema) || @@ -352,7 +353,7 @@ const go = ( } return handler } - const surrogate = getSurrogateAnnotation(ast) + const surrogate = AST.getSurrogateAnnotation(ast) if (Option.isSome(surrogate)) { return { ...(ast._tag === "Transformation" ? getJsonSchemaAnnotations(ast.to) : {}), @@ -361,7 +362,7 @@ const go = ( } } if (handleIdentifier && !AST.isTransformation(ast) && !AST.isRefinement(ast)) { - const identifier = getJSONIdentifier(ast) + const identifier = AST.getJSONIdentifier(ast) if (Option.isSome(identifier)) { const id = identifier.value const out = { $ref: get$ref(id) } @@ -603,7 +604,7 @@ const go = ( } } case "Suspend": { - const identifier = Option.orElse(getJSONIdentifier(ast), () => getJSONIdentifier(ast.f())) + const identifier = Option.orElse(AST.getJSONIdentifier(ast), () => AST.getJSONIdentifier(ast.f())) if (Option.isNone(identifier)) { throw new Error(getJSONSchemaMissingIdentifierAnnotationErrorMessage(path, ast)) } @@ -703,12 +704,3 @@ const formatPath = (path: ParseResult.Path): string => const isNonEmpty = (x: ParseResult.SingleOrNonEmpty): x is Arr.NonEmptyReadonlyArray => Array.isArray(x) const formatPropertyKey = (name: PropertyKey): string => typeof name === "string" ? JSON.stringify(name) : String(name) - -const ParseJsonTypeId: unique symbol = Symbol.for("effect/Schema/TypeId/ParseJson") -const SurrogateAnnotationId = Symbol.for("effect/Schema/annotation/Surrogate") -const JSONIdentifierAnnotationId = Symbol.for("effect/Schema/annotation/JSONIdentifier") - -const getSurrogateAnnotation = AST.getAnnotation(SurrogateAnnotationId) -const getJSONIdentifierAnnotation = AST.getAnnotation(JSONIdentifierAnnotationId) -const getJSONIdentifier = (annotated: AST.Annotated) => - Option.orElse(getJSONIdentifierAnnotation(annotated), () => AST.getIdentifierAnnotation(annotated)) From 0a2b0d79929bc846b9d1d651d067ec142ce72752 Mon Sep 17 00:00:00 2001 From: Giulio Canti Date: Tue, 15 Oct 2024 10:56:44 +0200 Subject: [PATCH 05/12] merge Serializable into Schema --- .changeset/cyan-sloths-lick.md | 2 + .changeset/six-crabs-itch.md | 10 +- packages/ai/ai/src/AiToolkit.ts | 9 +- .../src/RpcBroadcastChannel.ts | 3 +- packages/cluster/src/Message.ts | 6 +- packages/cluster/src/SerializedEnvelope.ts | 5 +- packages/cluster/src/internal/message.ts | 9 +- packages/effect/dtslint/SchemaContext.ts | 3 +- packages/effect/dtslint/SchemaSerializable.ts | 28 +- packages/effect/src/Schema.ts | 635 ++++++++++++++---- packages/effect/src/Serializable.ts | 385 ----------- packages/effect/src/index.ts | 5 - .../src/internal/schema/serializable.ts | 9 - .../Schema/Schema/Class/TaggedRequest.test.ts | 21 +- .../effect/test/Schema/Serializable.test.ts | 25 +- packages/experimental/src/Machine.ts | 3 +- .../experimental/src/Machine/Procedure.ts | 3 +- .../src/Machine/SerializableProcedureList.ts | 13 +- packages/experimental/src/PersistedCache.ts | 16 +- packages/experimental/src/Persistence.ts | 8 +- packages/experimental/src/RequestResolver.ts | 12 +- packages/platform/src/Worker.ts | 11 +- packages/platform/src/WorkerRunner.ts | 3 +- packages/platform/src/internal/worker.ts | 11 +- .../platform/src/internal/workerRunner.ts | 5 +- packages/rpc-http/src/HttpRpcResolver.ts | 6 +- .../rpc-http/src/HttpRpcResolverNoStream.ts | 6 +- packages/rpc/src/Rpc.ts | 29 +- packages/rpc/src/RpcResolver.ts | 11 +- packages/rpc/src/RpcResolverNoStream.ts | 9 +- packages/rpc/src/RpcRouter.ts | 17 +- packages/rpc/src/internal/rpc.ts | 9 +- packages/schema/src/Serializable.ts | 9 - packages/schema/src/index.ts | 5 - 34 files changed, 639 insertions(+), 702 deletions(-) delete mode 100644 packages/effect/src/Serializable.ts delete mode 100644 packages/effect/src/internal/schema/serializable.ts delete mode 100644 packages/schema/src/Serializable.ts diff --git a/.changeset/cyan-sloths-lick.md b/.changeset/cyan-sloths-lick.md index b39447ddf5..22789ec9f9 100644 --- a/.changeset/cyan-sloths-lick.md +++ b/.changeset/cyan-sloths-lick.md @@ -5,3 +5,5 @@ Re-export modules from `effect`. `ArrayFormatter` / `TreeFormatter` merged into `ParseResult` module. + +`Serializable` module merged into `Schema` module. diff --git a/.changeset/six-crabs-itch.md b/.changeset/six-crabs-itch.md index c578e7756a..5a49f35cd9 100644 --- a/.changeset/six-crabs-itch.md +++ b/.changeset/six-crabs-itch.md @@ -17,8 +17,7 @@ import { JSONSchema, ParseResult, Pretty, - Schema, - Serializable + Schema } from "@effect/schema" ``` @@ -33,8 +32,7 @@ import { JSONSchema, ParseResult, Pretty, - Schema, - Serializable + Schema } from "effect" ``` @@ -53,3 +51,7 @@ After ```ts import { ArrayFormatter, TreeFormatter } from "effect/ParseResult" ``` + +### Serializable + +Merged into `Schema` module. diff --git a/packages/ai/ai/src/AiToolkit.ts b/packages/ai/ai/src/AiToolkit.ts index a4f3387319..1370b66f7f 100644 --- a/packages/ai/ai/src/AiToolkit.ts +++ b/packages/ai/ai/src/AiToolkit.ts @@ -2,7 +2,6 @@ * @since 1.0.0 */ import type * as Schema from "@effect/schema/Schema" -import type * as Serializable from "@effect/schema/Serializable" import * as Context from "effect/Context" import * as Effect from "effect/Effect" import * as Effectable from "effect/Effectable" @@ -82,7 +81,7 @@ export declare namespace Tool { export interface AnySchema { readonly [Schema.TypeId]: any readonly _tag: string - readonly Type: Serializable.SerializableWithResult.All + readonly Type: Schema.SerializableWithResult.All readonly success: Schema.Schema.Any } @@ -90,19 +89,19 @@ export declare namespace Tool { * @since 1.0.0 * @category tool */ - export type Success = Serializable.WithResult.Success + export type Success = Schema.WithResult.Success /** * @since 1.0.0 * @category tool */ - export type Failure = Serializable.WithResult.Failure + export type Failure = Schema.WithResult.Failure /** * @since 1.0.0 * @category tool */ - export type Context = Serializable.WithResult.Context + export type Context = Schema.WithResult.Context /** * @since 1.0.0 diff --git a/packages/cluster-browser/src/RpcBroadcastChannel.ts b/packages/cluster-browser/src/RpcBroadcastChannel.ts index 94d17de53c..6fa78d3ae8 100644 --- a/packages/cluster-browser/src/RpcBroadcastChannel.ts +++ b/packages/cluster-browser/src/RpcBroadcastChannel.ts @@ -9,7 +9,6 @@ import { pipe } from "effect/Function" import * as Queue from "effect/Queue" import type * as RequestResolver from "effect/RequestResolver" import * as Schema from "effect/Schema" -import type * as Serializable from "effect/Serializable" import * as Stream from "effect/Stream" class ClientRequest extends Schema.TaggedClass()("ClientRequest", { @@ -73,7 +72,7 @@ export const make = >( channelId: string ): RequestResolver.RequestResolver< Rpc.Request>, - Serializable.SerializableWithResult.Context> + Schema.SerializableWithResult.Context> > => RpcResolver.make((requests) => { return Effect.gen(function*($) { diff --git a/packages/cluster/src/Message.ts b/packages/cluster/src/Message.ts index 0df848107c..8767e9d539 100644 --- a/packages/cluster/src/Message.ts +++ b/packages/cluster/src/Message.ts @@ -4,7 +4,6 @@ import type * as Exit_ from "effect/Exit" import type * as PrimaryKey from "effect/PrimaryKey" import type * as Schema from "effect/Schema" -import type * as Serializable from "effect/Serializable" import type * as Types from "effect/Types" import * as internal from "./internal/message.js" @@ -16,7 +15,7 @@ import * as internal from "./internal/message.js" * @category models */ export interface Message - extends Serializable.SerializableWithResult, PrimaryKey.PrimaryKey + extends Schema.SerializableWithResult, PrimaryKey.PrimaryKey {} /** @@ -70,8 +69,7 @@ export namespace Message { * @since 1.0.0 * @category utils */ - export type Exit = S extends Serializable.WithResult ? - Exit_.Exit + export type Exit = S extends Schema.WithResult ? Exit_.Exit : never } diff --git a/packages/cluster/src/SerializedEnvelope.ts b/packages/cluster/src/SerializedEnvelope.ts index 36d12adc81..41c605ee36 100644 --- a/packages/cluster/src/SerializedEnvelope.ts +++ b/packages/cluster/src/SerializedEnvelope.ts @@ -3,7 +3,6 @@ */ import * as PrimaryKey from "effect/PrimaryKey" import * as Schema from "effect/Schema" -import * as Serializable from "effect/Serializable" import { TypeIdSchema } from "./internal/utils.js" import * as RecipientAddress from "./RecipientAddress.js" import * as SerializedMessage from "./SerializedMessage.js" @@ -41,10 +40,10 @@ export class SerializedEnvelope extends Schema.Class(Seriali messageId: Schema.String, body: SerializedMessage.schema }) { - get [Serializable.symbol]() { + get [Schema.symbolSerializable]() { return this.constructor } - get [Serializable.symbolResult]() { + get [Schema.symbolWithResult]() { return { Success: Schema.Void, Failure: Schema.Never } } get [PrimaryKey.symbol]() { diff --git a/packages/cluster/src/internal/message.ts b/packages/cluster/src/internal/message.ts index c9f841e375..f662f72e08 100644 --- a/packages/cluster/src/internal/message.ts +++ b/packages/cluster/src/internal/message.ts @@ -1,6 +1,5 @@ import * as PrimaryKey from "effect/PrimaryKey" import * as Schema from "effect/Schema" -import * as Serializable from "effect/Serializable" import type * as Types from "effect/Types" import type * as Message from "../Message.js" @@ -8,7 +7,7 @@ import type * as Message from "../Message.js" export function isMessageWithResult(value: unknown): value is Message.Message { return ( typeof value === "object" && value !== null && - Serializable.symbolResult in value + Schema.symbolWithResult in value ) } @@ -16,21 +15,21 @@ export function isMessageWithResult(value: unknown): value is Message.Message( message: A ): Schema.Schema, unknown> { - return Serializable.exitSchema(message as any) as any + return Schema.exitSchema(message as any) as any } /** @internal */ export function successSchema( message: A ): Schema.Schema, unknown> { - return Serializable.successSchema(message as any) as any + return Schema.successSchema(message as any) as any } /** @internal */ export function failureSchema( message: A ): Schema.Schema, unknown> { - return Serializable.failureSchema(message as any) as any + return Schema.failureSchema(message as any) as any } /** diff --git a/packages/effect/dtslint/SchemaContext.ts b/packages/effect/dtslint/SchemaContext.ts index a6631f414a..a8dee05306 100644 --- a/packages/effect/dtslint/SchemaContext.ts +++ b/packages/effect/dtslint/SchemaContext.ts @@ -2,7 +2,6 @@ import { Context, Effect, Option } from "effect" import { hole } from "effect/Function" import * as ParseResult from "effect/ParseResult" import * as S from "effect/Schema" -import * as Serializable from "effect/Serializable" interface aContext extends S.Schema {} interface bContext extends S.Schema {} @@ -414,7 +413,7 @@ MyRequest.fields declare const myRequest: MyRequest // $ExpectType Schema, ExitEncoded, "bContext" | "cContext"> -Serializable.exitSchema(myRequest) +S.exitSchema(myRequest) // --------------------------------------------- // TemplateLiteralParser diff --git a/packages/effect/dtslint/SchemaSerializable.ts b/packages/effect/dtslint/SchemaSerializable.ts index d9610e1467..7e12e3fd59 100644 --- a/packages/effect/dtslint/SchemaSerializable.ts +++ b/packages/effect/dtslint/SchemaSerializable.ts @@ -1,4 +1,4 @@ -import { Schema, Serializable } from "effect" +import { Schema } from "effect" import { hole } from "effect/Function" class TR extends Schema.TaggedRequest()("TR", { @@ -9,20 +9,18 @@ class TR extends Schema.TaggedRequest()("TR", { } }) {} -const successSchema = (req: Req) => - Serializable.successSchema(Serializable.asWithResult(req)) +const successSchema = (req: Req) => Schema.successSchema(Schema.asWithResult(req)) // $ExpectType Schema successSchema(new TR({ id: 1 })) -const failureSchema = (req: Req) => - Serializable.failureSchema(Serializable.asWithResult(req)) +const failureSchema = (req: Req) => Schema.failureSchema(Schema.asWithResult(req)) // $ExpectType Schema failureSchema(new TR({ id: 1 })) const selfSchema = (req: Req) => - Serializable.selfSchema(Serializable.asSerializable(req)) + Schema.serializableSchema(Schema.asSerializable(req)) // $ExpectType Schema; } & { id: typeof NumberFromString; }>, never> selfSchema(new TR({ id: 1 })) @@ -44,36 +42,36 @@ class Foo extends Schema.TaggedRequest()("A", { // --------------------------------------------- // $ExpectType Foo -hole>>() +hole>>() // $ExpectType Encoded<{ readonly _tag: tag<"A">; } & { a: Schema<"payload", "payload-encoded", "payload-context">; }> -hole>>() +hole>>() // $ExpectType "payload-context" -hole>>() +hole>>() // --------------------------------------------- // WithResult type-level helpers // --------------------------------------------- // $ExpectType "success" -hole>>() +hole>>() // $ExpectType "success-encoded" -hole>>() +hole>>() // $ExpectType "failure" -hole>>() +hole>>() // $ExpectType "failure-encoded" -hole>>() +hole>>() // $ExpectType "failure-context" | "success-context" -hole>>() +hole>>() // --------------------------------------------- // SerializableWithResult type-level helpers // --------------------------------------------- // $ExpectType "failure-context" | "success-context" | "payload-context" -hole>>() +hole>>() diff --git a/packages/effect/src/Schema.ts b/packages/effect/src/Schema.ts index 9c1daeda28..a4599887cf 100644 --- a/packages/effect/src/Schema.ts +++ b/packages/effect/src/Schema.ts @@ -26,11 +26,11 @@ 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 errors_ from "./internal/schema/errors.js" import * as filters_ from "./internal/schema/filters.js" -import * as serializable_ from "./internal/schema/serializable.js" import * as util_ from "./internal/schema/util.js" import * as list_ from "./List.js" import * as number_ from "./Number.js" @@ -47,7 +47,6 @@ import * as Request from "./Request.js" import type { ParseOptions } from "./SchemaAST.js" import * as AST from "./SchemaAST.js" import * as equivalence_ from "./SchemaEquivalence.js" -import type * as Serializable from "./Serializable.js" import * as sortedSet_ from "./SortedSet.js" import * as string_ from "./String.js" import * as struct_ from "./Struct.js" @@ -3218,7 +3217,7 @@ export interface extend extend * b: Schema.String * }) * - * // const extended: Schema.Schema< + * // const extended: Schema< * // { * // readonly a: string * // readonly b: string @@ -7984,134 +7983,6 @@ export const TaggedError = (identifier?: string) => } as any } -/** - * @since 3.10.0 - */ -export interface TaggedRequest< - Tag extends string, - A, - I, - R, - SuccessType, - SuccessEncoded, - FailureType, - FailureEncoded, - ResultR -> extends - Request.Request, - Serializable.SerializableWithResult< - A, - I, - R, - SuccessType, - SuccessEncoded, - FailureType, - FailureEncoded, - ResultR - > -{ - readonly _tag: Tag -} - -/** - * @since 3.10.0 - */ -export declare namespace TaggedRequest { - /** - * @since 3.10.0 - */ - export type Any = TaggedRequest - /** - * @since 3.10.0 - */ - export type All = - | Any - | TaggedRequest -} - -/** - * @category api interface - * @since 3.10.0 - */ -export interface TaggedRequestClass< - Self, - Tag extends string, - Payload extends Struct.Fields, - Success extends Schema.All, - Failure extends Schema.All -> extends - Class< - Self, - Payload, - Struct.Encoded, - Struct.Context, - Struct.Constructor>, - TaggedRequest< - Tag, - Self, - Struct.Encoded, - Struct.Context, - Schema.Type, - Schema.Encoded, - Schema.Type, - Schema.Encoded, - Schema.Context | Schema.Context - >, - {} - > -{ - readonly _tag: Tag - readonly success: Success - readonly failure: Failure -} - -/** - * @category classes - * @since 3.10.0 - */ -export const TaggedRequest = - (identifier?: string) => - ( - tag: Tag, - options: { - failure: Failure - success: Success - payload: Payload - }, - annotations?: Annotations.Schema - ): [Self] extends [never] ? MissingSelfGeneric<"TaggedRequest", `"Tag", SuccessSchema, FailureSchema, `> - : TaggedRequestClass< - Self, - Tag, - { readonly _tag: tag } & Payload, - Success, - Failure - > => - { - const taggedFields = extendFields({ _tag: getClassTag(tag) }, options.payload) - return class TaggedRequestClass extends makeClass({ - kind: "TaggedRequest", - identifier: identifier ?? tag, - schema: Struct(taggedFields), - fields: taggedFields, - Base: Request.Class, - annotations - }) { - static _tag = tag - static success = options.success - static failure = options.failure - get [serializable_.symbol]() { - return this.constructor - } - get [serializable_.symbolResult]() { - return { - failure: options.failure, - success: options.success - } - } - } as any - } - const extendFields = (a: Struct.Fields, b: Struct.Fields): Struct.Fields => { const out = { ...a } for (const key of util_.ownKeys(b)) { @@ -9283,3 +9154,505 @@ export const Config = (name: string, schema: Schema): config_.Conf ) ) } + +// --------------------------------------------- +// Serializable +// --------------------------------------------- + +/** + * @since 3.10.0 + * @category symbol + */ +export const symbolSerializable: unique symbol = Symbol.for( + "effect/Schema/Serializable/symbol" +) + +/** + * The `Serializable` trait allows objects to define their own schema for + * serialization. + * + * @since 3.10.0 + * @category model + */ +export interface Serializable { + readonly [symbolSerializable]: Schema +} + +/** + * @since 3.10.0 + * @category model + */ +export declare namespace Serializable { + /** + * @since 3.10.0 + */ + export type Type = T extends Serializable ? A : never + /** + * @since 3.10.0 + */ + export type Encoded = T extends Serializable ? I : never + /** + * @since 3.10.0 + */ + export type Context = T extends Serializable ? R : never + /** + * @since 3.10.0 + */ + export type Any = Serializable + /** + * @since 3.10.0 + */ + export type All = + | Any + | Serializable + | Serializable + | Serializable +} + +/** + * @since 3.10.0 + */ +export const asSerializable = ( + serializable: S +): Serializable, Serializable.Encoded, Serializable.Context> => serializable as any + +/** + * @since 3.10.0 + * @category accessor + */ +export const serializableSchema = (self: Serializable): Schema => self[symbolSerializable] + +/** + * @since 3.10.0 + * @category encoding + */ +export const serialize = (self: Serializable): Effect.Effect => + encodeUnknown(self[symbolSerializable])(self) + +/** + * @since 3.10.0 + * @category decoding + */ +export const deserialize: { + (value: unknown): (self: Serializable) => Effect.Effect + (self: Serializable, value: unknown): Effect.Effect +} = dual( + 2, + (self: Serializable, value: unknown): Effect.Effect => + decodeUnknown(self[symbolSerializable])(value) +) + +/** + * @since 3.10.0 + * @category symbol + */ +export const symbolWithResult: unique symbol = Symbol.for( + "effect/Schema/Serializable/symbolResult" +) + +/** + * The `WithResult` trait is designed to encapsulate the outcome of an + * operation, distinguishing between success and failure cases. Each case is + * associated with a schema that defines the structure and types of the success + * or failure data. + * + * @since 3.10.0 + * @category model + */ +export interface WithResult { + readonly [symbolWithResult]: { + readonly success: Schema + readonly failure: Schema + } +} + +/** + * @since 3.10.0 + * @category model + */ +export declare namespace WithResult { + /** + * @since 3.10.0 + */ + export type Success = T extends WithResult ? _A : never + /** + * @since 3.10.0 + */ + export type SuccessEncoded = T extends WithResult ? _I : never + /** + * @since 3.10.0 + */ + export type Failure = T extends WithResult ? _E : never + /** + * @since 3.10.0 + */ + export type FailureEncoded = T extends WithResult ? _EI : never + + /** + * @since 3.10.0 + */ + export type Context = T extends WithResult ? R : never + /** + * @since 3.10.0 + */ + export type Any = WithResult + /** + * @since 3.10.0 + */ + export type All = + | Any + | WithResult +} + +/** + * @since 3.10.0 + */ +export const asWithResult = ( + withExit: WR +): WithResult< + WithResult.Success, + WithResult.SuccessEncoded, + WithResult.Failure, + WithResult.FailureEncoded, + WithResult.Context +> => withExit as any + +/** + * @since 3.10.0 + * @category accessor + */ +export const failureSchema = (self: WithResult): Schema => + self[symbolWithResult].failure + +/** + * @since 3.10.0 + * @category accessor + */ +export const successSchema = (self: WithResult): Schema => + self[symbolWithResult].success + +const exitSchemaCache = globalValue( + "effect/Schema/Serializable/exitSchemaCache", + () => new WeakMap>() +) + +/** + * @since 3.10.0 + * @category accessor + */ +export const exitSchema = (self: WithResult): Schema< + exit_.Exit, + ExitEncoded, + R +> => { + const proto = Object.getPrototypeOf(self) + if (!(symbolWithResult in proto)) { + return Exit({ + failure: failureSchema(self), + success: successSchema(self), + defect: Defect + }) + } + let schema = exitSchemaCache.get(proto) + if (schema === undefined) { + schema = Exit({ + failure: failureSchema(self), + success: successSchema(self), + defect: Defect + }) + exitSchemaCache.set(proto, schema) + } + return schema +} + +/** + * @since 3.10.0 + * @category encoding + */ +export const serializeFailure: { + (value: FA): ( + self: WithResult + ) => Effect.Effect + (self: WithResult, value: FA): Effect.Effect +} = dual( + 2, + (self: WithResult, value: FA): Effect.Effect => + encode(self[symbolWithResult].failure)(value) +) + +/** + * @since 3.10.0 + * @category decoding + */ +export const deserializeFailure: { + ( + value: unknown + ): (self: WithResult) => Effect.Effect + (self: WithResult, value: unknown): Effect.Effect +} = dual( + 2, + ( + self: WithResult, + value: unknown + ): Effect.Effect => decodeUnknown(self[symbolWithResult].failure)(value) +) + +/** + * @since 3.10.0 + * @category encoding + */ +export const serializeSuccess: { + (value: SA): ( + self: WithResult + ) => Effect.Effect + (self: WithResult, value: SA): Effect.Effect +} = dual( + 2, + (self: WithResult, value: SA): Effect.Effect => + encode(self[symbolWithResult].success)(value) +) + +/** + * @since 3.10.0 + * @category decoding + */ +export const deserializeSuccess: { + (value: unknown): ( + self: WithResult + ) => Effect.Effect + (self: WithResult, value: unknown): Effect.Effect +} = dual( + 2, + ( + self: WithResult, + value: unknown + ): Effect.Effect => decodeUnknown(self[symbolWithResult].success)(value) +) + +/** + * @since 3.10.0 + * @category encoding + */ +export const serializeExit: { + (value: exit_.Exit): ( + self: WithResult + ) => Effect.Effect, ParseResult.ParseError, R> + ( + self: WithResult, + value: exit_.Exit + ): Effect.Effect, ParseResult.ParseError, R> +} = dual(2, ( + self: WithResult, + value: exit_.Exit +): Effect.Effect, ParseResult.ParseError, R> => encode(exitSchema(self))(value)) + +/** + * @since 3.10.0 + * @category decoding + */ +export const deserializeExit: { + (value: unknown): ( + self: WithResult + ) => Effect.Effect, ParseResult.ParseError, R> + ( + self: WithResult, + value: unknown + ): Effect.Effect, ParseResult.ParseError, R> +} = dual(2, ( + self: WithResult, + value: unknown +): Effect.Effect, ParseResult.ParseError, R> => decodeUnknown(exitSchema(self))(value)) + +// --------------------------------------------- +// SerializableWithResult +// --------------------------------------------- + +/** + * The `SerializableWithResult` trait is specifically designed to model remote + * procedures that require serialization of their input and output, managing + * both successful and failed outcomes. + * + * This trait combines functionality from both the `Serializable` and `WithResult` + * traits to handle data serialization and the bifurcation of operation results + * into success or failure categories. + * + * @since 3.10.0 + * @category model + */ +export interface SerializableWithResult< + A, + I, + R, + Success, + SuccessEncoded, + Failure, + FailureEncoded, + ResultR +> extends Serializable, WithResult {} + +/** + * @since 3.10.0 + * @category model + */ +export declare namespace SerializableWithResult { + /** + * @since 3.10.0 + */ + export type Context