diff --git a/.changeset/old-rats-remember.md b/.changeset/old-rats-remember.md new file mode 100644 index 0000000000..06f34129ab --- /dev/null +++ b/.changeset/old-rats-remember.md @@ -0,0 +1,34 @@ +--- +"@effect/schema": minor +--- + +AST + +- rename `isTransform` to `isTransformation` + +Schema + +- consolidate `transform` and `transformOrFail` parameters into an `options` object, #2434 +- consolidate `Class.transformOrFail` and `Class.transformOrFailFrom` parameters into an `options` object +- consolidate `declare` parameters into an `options` object +- consolidate `optionalToRequired` parameters into an `options` object +- consolidate `optionalToOptional` parameters into an `options` object +- Removed `negateBigDecimal` function (This cleanup was prompted by the realization that numerous functions can be derived from transformations such as negation, Math.abs, increment, etc. However, including all of them in the library is not feasible. Therefore, `negateBigDecimal` was removed) + +ParseResult + +- rename `Tuple` to `TupleType` + +TreeFormatter + +- rename `formatIssue` to `formatIssueSync` (This change was made to maintain consistency in API naming across all decoding and encoding APIs.) +- rename `formatIssueEffect` to `formatIssue` +- rename `formatError` to `formatErrorSync` +- rename `formatErrorEffect` to `formatError` + +ArrayFormatter + +- rename `formatIssue` to `formatIssueSync` +- rename `formatIssueEffect` to `formatIssue` +- rename `formatError` to `formatErrorSync` +- rename `formatErrorEffect` to `formatError` diff --git a/packages/cli/src/internal/args.ts b/packages/cli/src/internal/args.ts index 0edefd3130..c16ace610a 100644 --- a/packages/cli/src/internal/args.ts +++ b/packages/cli/src/internal/args.ts @@ -434,7 +434,7 @@ export const withSchema = dual< return mapEffect(self, (_) => Effect.mapError( decode(_ as any), - (error) => InternalHelpDoc.p(TreeFormatter.formatIssue(error.error)) + (error) => InternalHelpDoc.p(TreeFormatter.formatIssueSync(error.error)) )) }) diff --git a/packages/cli/src/internal/options.ts b/packages/cli/src/internal/options.ts index 6b6886bc1d..3e3fe82db7 100644 --- a/packages/cli/src/internal/options.ts +++ b/packages/cli/src/internal/options.ts @@ -650,7 +650,7 @@ export const withSchema = dual< return mapEffect(self, (_) => Effect.mapError( decode(_ as any), - (error) => InternalValidationError.invalidValue(InternalHelpDoc.p(TreeFormatter.formatIssue(error.error))) + (error) => InternalValidationError.invalidValue(InternalHelpDoc.p(TreeFormatter.formatIssueSync(error.error))) )) }) diff --git a/packages/experimental/src/DevTools/Domain.ts b/packages/experimental/src/DevTools/Domain.ts index 6886b8553e..2165597208 100644 --- a/packages/experimental/src/DevTools/Domain.ts +++ b/packages/experimental/src/DevTools/Domain.ts @@ -247,8 +247,7 @@ export const Gauge = metric( const numberOrInfinity = Schema.transform( Schema.union(Schema.number, Schema.null), Schema.number, - (i) => i === null ? Number.POSITIVE_INFINITY : i, - (i) => Number.isFinite(i) ? i : null + { decode: (i) => i === null ? Number.POSITIVE_INFINITY : i, encode: (i) => Number.isFinite(i) ? i : null } ) /** diff --git a/packages/experimental/src/Persistence.ts b/packages/experimental/src/Persistence.ts index 2f7b8bbc47..c45087eef2 100644 --- a/packages/experimental/src/Persistence.ts +++ b/packages/experimental/src/Persistence.ts @@ -52,7 +52,7 @@ export class PersistenceParseError extends TypeIdError(ErrorTypeId, "Persistence } get message() { - return TreeFormatter.formatIssue(this.error) + return TreeFormatter.formatIssueSync(this.error) } } diff --git a/packages/platform/src/Http/Headers.ts b/packages/platform/src/Http/Headers.ts index 9e4a843e0c..b928ad2410 100644 --- a/packages/platform/src/Http/Headers.ts +++ b/packages/platform/src/Http/Headers.ts @@ -54,8 +54,7 @@ export const schema: Schema.Schema fromInput(record), - identity + { decode: (record) => fromInput(record), encode: identity } ) /** diff --git a/packages/platform/src/Transferable.ts b/packages/platform/src/Transferable.ts index a59977b957..293d52a354 100644 --- a/packages/platform/src/Transferable.ts +++ b/packages/platform/src/Transferable.ts @@ -93,8 +93,7 @@ export const schema: { Schema.transformOrFail( Schema.encodedSchema(self), self, - ParseResult.succeed, - (i) => Effect.as(addAll(f(i)), i) + { decode: ParseResult.succeed, encode: (i) => Effect.as(addAll(f(i)), i) } )) /** diff --git a/packages/platform/src/WorkerError.ts b/packages/platform/src/WorkerError.ts index f502cc8224..bc7d7c9b1b 100644 --- a/packages/platform/src/WorkerError.ts +++ b/packages/platform/src/WorkerError.ts @@ -28,12 +28,14 @@ export const isWorkerError = (u: unknown): u is WorkerError => Predicate.hasProp const causeDefectPretty: Schema.Schema = Schema.transform( Schema.unknown, Schema.unknown, - identity, - (defect) => { - if (Predicate.isObject(defect)) { - return Cause.pretty(Cause.die(defect)) + { + decode: identity, + encode: (defect) => { + if (Predicate.isObject(defect)) { + return Cause.pretty(Cause.die(defect)) + } + return String(defect) } - return String(defect) } ) diff --git a/packages/rpc/src/Router.ts b/packages/rpc/src/Router.ts index 588486f21a..b0132cbc0f 100644 --- a/packages/rpc/src/Router.ts +++ b/packages/rpc/src/Router.ts @@ -204,8 +204,7 @@ export const toHandler = >(router: R, options?: { Schema.transform( rpc.schema, Schema.typeSchema(Schema.tuple(rpc.schema, Schema.any)), - (request) => [request, rpc] as const, - ([request]) => request + { decode: (request) => [request, rpc] as const, encode: ([request]) => request } ) ) ) @@ -302,8 +301,7 @@ export const toHandlerEffect = >(router: R, options?: Schema.transform( rpc.schema, Schema.typeSchema(Schema.tuple(rpc.schema, Schema.any)), - (request) => [request, rpc] as const, - ([request]) => request + { decode: (request) => [request, rpc] as const, encode: ([request]) => request } ) ) ) @@ -370,8 +368,7 @@ export const toHandlerRaw = >(router: R) => { Schema.transform( Schema.typeSchema(rpc.schema), Schema.typeSchema(Schema.tuple(rpc.schema, Schema.any)), - (request) => [request, rpc] as const, - ([request]) => request + { decode: (request) => [request, rpc] as const, encode: ([request]) => request } ) )) const parse = Schema.decode(schema) diff --git a/packages/schema/README.md b/packages/schema/README.md index f7f246ac4a..8ccfe19338 100644 --- a/packages/schema/README.md +++ b/packages/schema/README.md @@ -458,16 +458,14 @@ const Person = S.struct({ age: S.number, }); -const asyncSchema = S.transformOrFail( - PersonId, - Person, +const asyncSchema = S.transformOrFail(PersonId, Person, { // Simulate an async transformation - (id) => + decode: (id) => Effect.succeed({ id, name: "name", age: 18 }).pipe( Effect.delay("10 millis") ), - (person) => Effect.succeed(person.id).pipe(Effect.delay("10 millis")) -); + encode: (person) => Effect.succeed(person.id).pipe(Effect.delay("10 millis")), +}); const syncParsePersonId = S.decodeUnknownEither(asyncSchema); @@ -2132,15 +2130,19 @@ const DiscriminatedShape = S.union( Circle.pipe( S.transform( S.struct({ ...Circle.fields, kind: S.literal("circle") }), // Add a "kind" property with the literal value "circle" to Circle - (circle) => ({ ...circle, kind: "circle" as const }), // Add the discriminant property to Circle - ({ kind: _kind, ...rest }) => rest // Remove the discriminant property + { + decode: (circle) => ({ ...circle, kind: "circle" as const }), // Add the discriminant property to Circle + encode: ({ kind: _kind, ...rest }) => rest, // Remove the discriminant property + } ) ), Square.pipe( S.transform( S.struct({ ...Square.fields, kind: S.literal("square") }), // Add a "kind" property with the literal value "square" to Square - (square) => ({ ...square, kind: "square" as const }), // Add the discriminant property to Square - ({ kind: _kind, ...rest }) => rest // Remove the discriminant property + { + decode: (square) => ({ ...square, kind: "square" as const }), // Add the discriminant property to Square + encode: ({ kind: _kind, ...rest }) => rest, // Remove the discriminant property + } ) ) ); @@ -3526,13 +3528,16 @@ export class PersonWithTransform extends Person.transformOrFail - Effect.mapBoth(getAge(input.id), { - onFailure: (e) => new ParseResult.Type(S.string.ast, input.id, e.message), - // must return { age: Option } - onSuccess: (age) => ({ ...input, age: Option.some(age) }), - }), - ParseResult.succeed + { + decode: (input) => + Effect.mapBoth(getAge(input.id), { + onFailure: (e) => + new ParseResult.Type(S.string.ast, input.id, e.message), + // must return { age: Option } + onSuccess: (age) => ({ ...input, age: Option.some(age) }), + }), + encode: ParseResult.succeed, + } ) {} S.decodeUnknownPromise(PersonWithTransform)({ id: 1, name: "name" }).then( @@ -3553,13 +3558,15 @@ export class PersonWithTransformFrom extends Person.transformOrFailFrom - Effect.mapBoth(getAge(input.id), { - onFailure: (e) => new ParseResult.Type(S.string.ast, input, e.message), - // must return { age?: number } - onSuccess: (age) => (age > 18 ? { ...input, age } : { ...input }), - }), - ParseResult.succeed + { + decode: (input) => + Effect.mapBoth(getAge(input.id), { + onFailure: (e) => new ParseResult.Type(S.string.ast, input, e.message), + // must return { age?: number } + onSuccess: (age) => (age > 18 ? { ...input, age } : { ...input }), + }), + encode: ParseResult.succeed, + } ) {} S.decodeUnknownPromise(PersonWithTransformFrom)({ id: 1, name: "name" }).then( @@ -3575,15 +3582,15 @@ PersonWithTransformFrom { */ ``` -The decision of which API to use, either `transform` or `transformFrom`, depends on when you wish to execute the transformation: +The decision of which API to use, either `transformOrFail` or `transformOrFailFrom`, depends on when you wish to execute the transformation: -1. Using `transform`: +1. Using `transformOrFail`: - The transformation occurs at the end of the process. - It expects you to provide a value of type `{ age: Option }`. - After processing the initial input, the new transformation comes into play, and you need to ensure the final output adheres to the specified structure. -2. Using `transformFrom`: +2. Using `transformOrFailFrom`: - The new transformation starts as soon as the initial input is handled. - You should provide a value `{ age?: number }`. - Based on this fresh input, the subsequent transformation `{ age: S.optionalToOption(S.number, { exact: true }) }` is executed. @@ -3598,12 +3605,19 @@ To perform these kinds of transformations, the `@effect/schema` library provides ## transform ```ts -declare const transform: ( - from: Schema, - to: Schema, - decode: (b: B) => C, - encode: (c: C) => B -) => Schema; +declare const transform: ( + from: From, + to: To, + options: { + readonly decode: (fromA: Schema.Type) => Schema.Encoded + readonly encode: (toI: Schema.Encoded) => Schema.Type + readonly strict?: true + } | { + readonly decode: (fromA: Schema.Type) => unknown + readonly encode: (toI: Schema.Encoded) => unknown + readonly strict: false + } + ): transform ``` ```mermaid @@ -3621,14 +3635,12 @@ import * as S from "@effect/schema/Schema"; // use the transform combinator to convert the string schema into the tuple schema export const transformedSchema: S.Schema = - S.transform( - S.string, - S.tuple(S.string), + S.transform(S.string, S.tuple(S.string), { // define a function that converts a string into a tuple with one element of type string - (s) => [s] as const, + decode: (s) => [s] as const, // define a function that converts a tuple with one element of type string into a string - ([s]) => s - ); + encode: ([s]) => s, + }); ``` In the example above, we defined a schema for the `string` type and a schema for the tuple type `[string]`. We also defined the functions `decode` and `encode` that convert a `string` into a tuple and a tuple into a `string`, respectively. Then, we used the `transform` combinator to convert the string schema into a schema for the tuple type `[string]`. The resulting schema can be used to parse values of type `string` into values of type `[string]`. @@ -3638,20 +3650,60 @@ In the example above, we defined a schema for the `string` type and a schema for If you need to be less restrictive in your `decode` and `encode` functions, you can make use of the `{ strict: false }` option: ```ts -declare const transform: ( - from: Schema, - to: Schema, - decode: (b: B) => unknown, // Less strict constraint - encode: (c: C) => unknown, // Less strict constraint - options: { strict: false } -) => Schema; +( + from: From, + to: To, + options: { + readonly decode: (fromA: Schema.Type) => Schema.Encoded + readonly encode: (toI: Schema.Encoded) => Schema.Type + readonly strict?: true + } | { + readonly decode: (fromA: Schema.Type) => unknown // Less strict constraint + readonly encode: (toI: Schema.Encoded) => unknown // Less strict constraint + readonly strict: false + } +): transform ``` This is useful when you want to relax the type constraints imposed by the `decode` and `encode` functions, making them more permissive. ## transformOrFail -The `transformOrFail` combinator works in a similar way, but allows the transformation function to return an `Effect( + from: From, + to: To, + options: { + readonly decode: ( + fromA: Schema.Type, + options: ParseOptions, + ast: AST.Transformation + ) => Effect.Effect, ParseResult.ParseIssue, RD> + readonly encode: ( + toI: Schema.Encoded, + options: ParseOptions, + ast: AST.Transformation + ) => Effect.Effect, ParseResult.ParseIssue, RE> + readonly strict?: true + } | { + readonly decode: ( + fromA: Schema.Type, + options: ParseOptions, + ast: AST.Transformation + ) => Effect.Effect + readonly encode: ( + toI: Schema.Encoded, + options: ParseOptions, + ast: AST.Transformation + ) => Effect.Effect + readonly strict: false + } +): transformOrFail +``` + +Example ```ts import * as ParseResult from "@effect/schema/ParseResult"; @@ -3660,17 +3712,19 @@ import * as S from "@effect/schema/Schema"; export const transformedSchema: S.Schema = S.transformOrFail( S.string, S.boolean, - // define a function that converts a string into a boolean - (s) => - s === "true" - ? ParseResult.succeed(true) - : s === "false" - ? ParseResult.succeed(false) - : ParseResult.fail( - new ParseResult.Type(S.literal("true", "false").ast, s) - ), - // define a function that converts a boolean into a string - (b) => ParseResult.succeed(String(b)) + { + // define a function that converts a string into a boolean + decode: (s) => + s === "true" + ? ParseResult.succeed(true) + : s === "false" + ? ParseResult.succeed(false) + : ParseResult.fail( + new ParseResult.Type(S.literal("true", "false").ast, s) + ), + // define a function that converts a boolean into a string + encode: (b) => ParseResult.succeed(String(b)), + } ); ``` @@ -3696,16 +3750,14 @@ const api = (url: string): Effect.Effect => const PeopleId = S.string.pipe(S.brand("PeopleId")); -const PeopleIdFromString = S.transformOrFail( - S.string, - PeopleId, - (s, _, ast) => +const PeopleIdFromString = S.transformOrFail(S.string, PeopleId, { + decode: (s, _, ast) => Effect.mapBoth(api(`https://swapi.dev/api/people/${s}`), { onFailure: (e) => new ParseResult.Type(ast, s, e.message), onSuccess: () => s, }), - ParseResult.succeed -); + encode: ParseResult.succeed, +}); const decode = (id: string) => Effect.mapError(S.decodeUnknown(PeopleIdFromString)(id), (e) => @@ -3763,16 +3815,14 @@ const api = (url: string): Effect.Effect => const PeopleId = S.string.pipe(S.brand("PeopleId")); -const PeopleIdFromString = S.transformOrFail( - S.string, - PeopleId, - (s, _, ast) => +const PeopleIdFromString = S.transformOrFail(S.string, PeopleId, { + decode: (s, _, ast) => Effect.mapBoth(api(`https://swapi.dev/api/people/${s}`), { onFailure: (e) => new ParseResult.Type(ast, s, e.message), onSuccess: () => s, }), - ParseResult.succeed -); + encode: ParseResult.succeed, +}); const decode = (id: string) => Effect.mapError(S.decodeUnknown(PeopleIdFromString)(id), (e) => @@ -5004,17 +5054,19 @@ const NormalizedUrlString: S.Schema = S.string.pipe( const NormalizeUrlString: S.Schema = S.transformOrFail( S.string, NormalizedUrlString, - (value, _, ast) => - ParseResult.try({ - try: () => new URL(value).toString(), - catch: (err) => - new ParseResult.Type( - ast, - value, - err instanceof Error ? err.message : undefined - ), - }), - ParseResult.succeed + { + decode: (value, _, ast) => + ParseResult.try({ + try: () => new URL(value).toString(), + catch: (err) => + new ParseResult.Type( + ast, + value, + err instanceof Error ? err.message : undefined + ), + }), + encode: ParseResult.succeed, + } ); const decode = S.decodeUnknownSync(NormalizeUrlString); @@ -5389,17 +5441,15 @@ When a transformation encounters an error, the default error message provides in import * as ParseResult from "@effect/schema/ParseResult"; import * as S from "@effect/schema/Schema"; -const IntFromString = S.transformOrFail( - S.string, - S.Int, - (s, _, ast) => { +const IntFromString = S.transformOrFail(S.string, S.Int, { + decode: (s, _, ast) => { const n = Number(s); return Number.isNaN(n) ? ParseResult.fail(new ParseResult.Type(ast, s)) : ParseResult.succeed(n); }, - (n) => ParseResult.succeed(String(n)) -).annotations({ identifier: "IntFromString" }); + encode: (n) => ParseResult.succeed(String(n)), +}).annotations({ identifier: "IntFromString" }); const schema = S.struct({ name: S.NonEmpty, @@ -5453,13 +5503,15 @@ import * as S from "@effect/schema/Schema"; const IntFromString = S.transformOrFail( S.string.annotations({ message: () => "please enter a string" }), S.Int.annotations({ message: () => "please enter an integer" }), - (s, _, ast) => { - const n = Number(s); - return Number.isNaN(n) - ? ParseResult.fail(new ParseResult.Type(ast, s)) - : ParseResult.succeed(n); - }, - (n) => ParseResult.succeed(String(n)) + { + decode: (s, _, ast) => { + const n = Number(s); + return Number.isNaN(n) + ? ParseResult.fail(new ParseResult.Type(ast, s)) + : ParseResult.succeed(n); + }, + encode: (n) => ParseResult.succeed(String(n)), + } ).annotations({ identifier: "IntFromString", message: () => "please enter a parseable string", diff --git a/packages/schema/dtslint/Context.ts b/packages/schema/dtslint/Context.ts index ed5b70f727..e3fb0eecbe 100644 --- a/packages/schema/dtslint/Context.ts +++ b/packages/schema/dtslint/Context.ts @@ -27,8 +27,7 @@ S.declare((u): u is string => typeof u === "string") // $ExpectType Schema S.declare( [aContext, bContext], - (_a, _b) => () => ParseResult.succeed("a"), - (_a, _b) => () => ParseResult.succeed(1), + { decode: (_a, _b) => () => ParseResult.succeed("a"), encode: (_a, _b) => () => ParseResult.succeed(1) }, { arbitrary: ( _a, // $ExpectType Arbitrary @@ -50,46 +49,55 @@ S.declare( } ) +// @ts-expect-error S.declare( [aContext, bContext], - // @ts-expect-error - (_a, _b) => () => Taga.pipe(Effect.flatMap(ParseResult.succeed)), - (_a, _b) => () => ParseResult.succeed(1) + { + decode: (_a, _b) => () => Taga.pipe(Effect.flatMap(ParseResult.succeed)), + encode: (_a, _b) => () => ParseResult.succeed(1) + } ) +// @ts-expect-error S.declare( [aContext, bContext], - (_a, _b) => () => ParseResult.succeed("a"), - // @ts-expect-error - (_a, _b) => () => Tagb.pipe(Effect.flatMap(ParseResult.succeed)) + { + decode: (_a, _b) => () => ParseResult.succeed("a"), + encode: (_a, _b) => () => Tagb.pipe(Effect.flatMap(ParseResult.succeed)) + } ) S.declare( - [aContext, bContext], // @ts-expect-error - (_a, _b) => () => Taga.pipe(Effect.flatMap(ParseResult.succeed)), - (_a, _b) => () => Tagb.pipe(Effect.flatMap(ParseResult.succeed)) + [aContext, bContext], + { + decode: (_a, _b) => () => Taga.pipe(Effect.flatMap(ParseResult.succeed)), + encode: (_a, _b) => () => Tagb.pipe(Effect.flatMap(ParseResult.succeed)) + } ) +// @ts-expect-error S.declare( [], - // @ts-expect-error - () => () => Tag1.pipe(Effect.flatMap(ParseResult.succeed)), - () => () => ParseResult.succeed(1) + { decode: () => () => Tag1.pipe(Effect.flatMap(ParseResult.succeed)), encode: () => () => ParseResult.succeed(1) } ) +// @ts-expect-error S.declare( [aContext, bContext], - // @ts-expect-error - (_a, _b) => () => Tag1.pipe(Effect.flatMap(ParseResult.succeed)), - (_a, _b) => () => ParseResult.succeed(1) + { + decode: (_a, _b) => () => Tag1.pipe(Effect.flatMap(ParseResult.succeed)), + encode: (_a, _b) => () => ParseResult.succeed(1) + } ) +// @ts-expect-error S.declare( [aContext, bContext], - (_a, _b) => () => ParseResult.succeed("a"), - // @ts-expect-error - (_a, _b) => () => Tag2.pipe(Effect.flatMap(ParseResult.succeed)) + { + decode: (_a, _b) => () => ParseResult.succeed("a"), + encode: (_a, _b) => () => Tag2.pipe(Effect.flatMap(ParseResult.succeed)) + } ) // --------------------------------------------- @@ -164,7 +172,7 @@ S.propertySignature(aContext) // --------------------------------------------- // $ExpectType PropertySignature<":", string, never, "?:", string, "aContext"> -S.optionalToRequired(aContext, S.string, Option.getOrElse(() => ""), Option.some) +S.optionalToRequired(aContext, S.string, { decode: Option.getOrElse(() => ""), encode: Option.some }) // --------------------------------------------- // optional @@ -274,20 +282,22 @@ aContext.pipe(S.filter(() => false)) // --------------------------------------------- // $ExpectType Schema -S.asSchema(S.transformOrFail(aContext, bContext, () => ParseResult.succeed(1), () => ParseResult.succeed(""))) +S.asSchema( + S.transformOrFail(aContext, bContext, { decode: () => ParseResult.succeed(1), encode: () => ParseResult.succeed("") }) +) // $ExpectType transformOrFail -S.transformOrFail(aContext, bContext, () => ParseResult.succeed(1), () => ParseResult.succeed("")) +S.transformOrFail(aContext, bContext, { decode: () => ParseResult.succeed(1), encode: () => ParseResult.succeed("") }) // --------------------------------------------- // transform // --------------------------------------------- // $ExpectType Schema -S.asSchema(S.transform(aContext, bContext, () => 1, () => "")) +S.asSchema(S.transform(aContext, bContext, { decode: () => 1, encode: () => "" })) // $ExpectType transform -S.transform(aContext, bContext, () => 1, () => "") +S.transform(aContext, bContext, { decode: () => 1, encode: () => "" }) // --------------------------------------------- // attachPropertySignature @@ -385,8 +395,10 @@ export class MyClassWithTransform extends MyClass.transformOrFail Tag1.pipe(Effect.flatMap((a) => ParseResult.succeed(i.a === a ? { ...i, b: 1 } : { ...i, b: 2 }))), - (a) => Tag2.pipe(Effect.flatMap((b) => ParseResult.succeed(a.b === b ? { a: "a1" } : { a: "a2" }))) + { + decode: (i) => Tag1.pipe(Effect.flatMap((a) => ParseResult.succeed(i.a === a ? { ...i, b: 1 } : { ...i, b: 2 }))), + encode: (a) => Tag2.pipe(Effect.flatMap((b) => ParseResult.succeed(a.b === b ? { a: "a1" } : { a: "a2" }))) + } ) {} // $ExpectType "aContext" | "bContext" | "Tag1" | "Tag2" @@ -404,8 +416,10 @@ export class MyClassWithTransformFrom { b: bContext }, - (i) => Tag1.pipe(Effect.flatMap((a) => ParseResult.succeed(i.a === a ? { ...i, b: 1 } : { ...i, b: 2 }))), - (a) => Tag2.pipe(Effect.flatMap((b) => ParseResult.succeed(a.b === b ? { a: "a1" } : { a: "a2" }))) + { + decode: (i) => Tag1.pipe(Effect.flatMap((a) => ParseResult.succeed(i.a === a ? { ...i, b: 1 } : { ...i, b: 2 }))), + encode: (a) => Tag2.pipe(Effect.flatMap((b) => ParseResult.succeed(a.b === b ? { a: "a1" } : { a: "a2" }))) + } ) {} diff --git a/packages/schema/dtslint/Schema.ts b/packages/schema/dtslint/Schema.ts index 296b7f9248..face5e9186 100644 --- a/packages/schema/dtslint/Schema.ts +++ b/packages/schema/dtslint/Schema.ts @@ -1332,10 +1332,10 @@ S.asSchema(S.mutable(S.suspend(() => S.array(S.string)))) S.mutable(S.suspend(() => S.array(S.string))) // $ExpectType Schema -S.asSchema(S.mutable(S.transform(S.array(S.string), S.array(S.string), identity, identity))) +S.asSchema(S.mutable(S.transform(S.array(S.string), S.array(S.string), { decode: identity, encode: identity }))) // $ExpectType mutable, array<$string>>> -S.mutable(S.transform(S.array(S.string), S.array(S.string), identity, identity)) +S.mutable(S.transform(S.array(S.string), S.array(S.string), { decode: identity, encode: identity })) // $ExpectType Schema<{ a: string; b: number; }, { a: string; b: number; }, never> S.asSchema(S.extend(S.mutable(S.struct({ a: S.string })), S.mutable(S.struct({ b: S.number })))) @@ -1355,7 +1355,7 @@ S.asSchema(S.extend(S.mutable(S.struct({ a: S.string })), S.mutable(S.record(S.s // --------------------------------------------- // $ExpectType transform<$string, $number> -const transform1 = S.string.pipe(S.transform(S.number, (s) => s.length, (n) => String(n))) +const transform1 = S.string.pipe(S.transform(S.number, { decode: (s) => s.length, encode: (n) => String(n) })) // $ExpectType $string transform1.from @@ -1370,10 +1370,10 @@ transform1.annotations({}) S.asSchema(transform1) // $ExpectType Schema -S.asSchema(S.string.pipe(S.transform(S.number, (s) => s, (n) => n, { strict: false }))) +S.asSchema(S.string.pipe(S.transform(S.number, { strict: false, decode: (s) => s, encode: (n) => n }))) // $ExpectType transform<$string, $number> -S.string.pipe(S.transform(S.number, (s) => s, (n) => n, { strict: false })) +S.string.pipe(S.transform(S.number, { strict: false, decode: (s) => s, encode: (n) => n })) // @ts-expect-error S.string.pipe(S.transform(S.number, (s) => s, (n) => String(n))) @@ -1389,8 +1389,7 @@ S.string.pipe(S.transform(S.number, (s) => s.length, (n) => n)) const transformOrFail1 = S.string.pipe( S.transformOrFail( S.number, - (s) => ParseResult.succeed(s.length), - (n) => ParseResult.succeed(String(n)) + { decode: (s) => ParseResult.succeed(s.length), encode: (n) => ParseResult.succeed(String(n)) } ) ) @@ -1410,9 +1409,7 @@ S.asSchema(transformOrFail1) S.asSchema(S.string.pipe( S.transformOrFail( S.number, - (s) => ParseResult.succeed(s), - (n) => ParseResult.succeed(String(n)), - { strict: false } + { strict: false, decode: (s) => ParseResult.succeed(s), encode: (n) => ParseResult.succeed(String(n)) } ) )) @@ -1420,9 +1417,7 @@ S.asSchema(S.string.pipe( S.string.pipe( S.transformOrFail( S.number, - (s) => ParseResult.succeed(s), - (n) => ParseResult.succeed(String(n)), - { strict: false } + { strict: false, decode: (s) => ParseResult.succeed(s), encode: (n) => ParseResult.succeed(String(n)) } ) ) @@ -1959,7 +1954,7 @@ S.asSchema(S.mapFromSelf({ key: S.NumberFromString, value: S.string })) // $ExpectType mapFromSelf S.mapFromSelf({ key: S.NumberFromString, value: S.string }) -// --------------------------------------------- +// ---------------- ----------------------------- // readonlyMap // --------------------------------------------- diff --git a/packages/schema/src/AST.ts b/packages/schema/src/AST.ts index 0eb4db9a6b..5acdb713a0 100644 --- a/packages/schema/src/AST.ts +++ b/packages/schema/src/AST.ts @@ -1683,7 +1683,7 @@ export class Transformation implements Annotated { * @category guards * @since 1.0.0 */ -export const isTransform: (ast: AST) => ast is Transformation = createASTGuard("Transformation") +export const isTransformation: (ast: AST) => ast is Transformation = createASTGuard("Transformation") /** * @category model @@ -2033,7 +2033,7 @@ export const record = (key: AST, value: AST): { * @since 1.0.0 */ export const pick = (ast: AST, keys: ReadonlyArray): TypeLiteral | Transformation => { - if (isTransform(ast)) { + if (isTransformation(ast)) { switch (ast.transformation._tag) { case "ComposeTransformation": return new Transformation( diff --git a/packages/schema/src/ArrayFormatter.ts b/packages/schema/src/ArrayFormatter.ts index 34cc22b8ac..5f22326938 100644 --- a/packages/schema/src/ArrayFormatter.ts +++ b/packages/schema/src/ArrayFormatter.ts @@ -21,26 +21,25 @@ export interface Issue { * @category formatting * @since 1.0.0 */ -export const formatIssueEffect = (issue: ParseResult.ParseIssue): Effect.Effect> => go(issue) +export const formatIssue = (issue: ParseResult.ParseIssue): Effect.Effect> => go(issue) /** * @category formatting * @since 1.0.0 */ -export const formatIssue = (issue: ParseResult.ParseIssue): Array => Effect.runSync(formatIssueEffect(issue)) +export const formatIssueSync = (issue: ParseResult.ParseIssue): Array => Effect.runSync(formatIssue(issue)) /** * @category formatting * @since 1.0.0 */ -export const formatErrorEffect = (error: ParseResult.ParseError): Effect.Effect> => - formatIssueEffect(error.error) +export const formatError = (error: ParseResult.ParseError): Effect.Effect> => formatIssue(error.error) /** * @category formatting * @since 1.0.0 */ -export const formatError = (error: ParseResult.ParseError): Array => formatIssue(error.error) +export const formatErrorSync = (error: ParseResult.ParseError): Array => formatIssueSync(error.error) const getArray = ( issue: ParseResult.ParseIssue, diff --git a/packages/schema/src/ParseResult.ts b/packages/schema/src/ParseResult.ts index e82d780ee1..f97342c511 100644 --- a/packages/schema/src/ParseResult.ts +++ b/packages/schema/src/ParseResult.ts @@ -27,7 +27,7 @@ import * as TreeFormatter from "./TreeFormatter.js" export type ParseIssue = | Declaration | Refinement - | Tuple + | TupleType | TypeLiteral | Union | Transformation @@ -73,7 +73,7 @@ export class Refinement { * @category model * @since 1.0.0 */ -export class Tuple { +export class TupleType { /** * @since 1.0.0 */ @@ -269,7 +269,7 @@ export class ParseError extends TaggedError("ParseError")<{ readonly error: Pars * @since 1.0.0 */ toString() { - return TreeFormatter.formatIssue(this.error) + return TreeFormatter.formatIssueSync(this.error) } /** * @since 1.0.0 @@ -495,7 +495,7 @@ const getEither = (ast: AST.AST, isDecoding: boolean, options?: AST.ParseOptions 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), (e) => new Error(TreeFormatter.formatIssue(e))) + Either.getOrThrowWith(parser(input, overrideOptions), (e) => new Error(TreeFormatter.formatIssueSync(e))) } const getOption = (ast: AST.AST, isDecoding: boolean, options?: AST.ParseOptions) => { @@ -728,7 +728,7 @@ export const asserts = (schema: Schema.Schema, options?: AST.P isExact: true }) as any if (Either.isLeft(result)) { - throw new Error(TreeFormatter.formatIssue(result.left)) + throw new Error(TreeFormatter.formatIssueSync(result.left)) } } } @@ -941,7 +941,7 @@ const go = (ast: AST.AST, isDecoding: boolean): Parser => { es.push([stepKey++, e]) continue } else { - return Either.left(new Tuple(ast, input, [e])) + return Either.left(new TupleType(ast, input, [e])) } } @@ -955,7 +955,7 @@ const go = (ast: AST.AST, isDecoding: boolean): Parser => { es.push([stepKey++, e]) continue } else { - return Either.left(new Tuple(ast, input, [e])) + return Either.left(new TupleType(ast, input, [e])) } } } @@ -991,7 +991,7 @@ const go = (ast: AST.AST, isDecoding: boolean): Parser => { es.push([stepKey++, e]) continue } else { - return Either.left(new Tuple(ast, input, [e], sortByIndex(output))) + return Either.left(new TupleType(ast, input, [e], sortByIndex(output))) } } output.push([stepKey++, eu.right]) @@ -1010,7 +1010,7 @@ const go = (ast: AST.AST, isDecoding: boolean): Parser => { es.push([nk, e]) return Effect.unit } else { - return Either.left(new Tuple(ast, input, [e], sortByIndex(output))) + return Either.left(new TupleType(ast, input, [e], sortByIndex(output))) } } output.push([nk, t.right]) @@ -1035,7 +1035,7 @@ const go = (ast: AST.AST, isDecoding: boolean): Parser => { es.push([stepKey++, e]) continue } else { - return Either.left(new Tuple(ast, input, [e], sortByIndex(output))) + return Either.left(new TupleType(ast, input, [e], sortByIndex(output))) } } else { output.push([stepKey++, eu.right]) @@ -1055,7 +1055,7 @@ const go = (ast: AST.AST, isDecoding: boolean): Parser => { es.push([nk, e]) return Effect.unit } else { - return Either.left(new Tuple(ast, input, [e], sortByIndex(output))) + return Either.left(new TupleType(ast, input, [e], sortByIndex(output))) } } else { output.push([nk, t.right]) @@ -1083,7 +1083,7 @@ const go = (ast: AST.AST, isDecoding: boolean): Parser => { es.push([stepKey++, e]) continue } else { - return Either.left(new Tuple(ast, input, [e], sortByIndex(output))) + return Either.left(new TupleType(ast, input, [e], sortByIndex(output))) } } output.push([stepKey++, eu.right]) @@ -1103,7 +1103,7 @@ const go = (ast: AST.AST, isDecoding: boolean): Parser => { es.push([nk, e]) return Effect.unit } else { - return Either.left(new Tuple(ast, input, [e], sortByIndex(output))) + return Either.left(new TupleType(ast, input, [e], sortByIndex(output))) } } output.push([nk, t.right]) @@ -1120,7 +1120,7 @@ const go = (ast: AST.AST, isDecoding: boolean): Parser => { // --------------------------------------------- const computeResult = ({ es, output }: State) => ReadonlyArray.isNonEmptyArray(es) ? - Either.left(new Tuple(ast, input, sortByIndex(es), sortByIndex(output))) : + Either.left(new TupleType(ast, input, sortByIndex(es), sortByIndex(output))) : Either.right(sortByIndex(output)) if (queue && queue.length > 0) { const cqueue = queue diff --git a/packages/schema/src/Schema.ts b/packages/schema/src/Schema.ts index c60f02560d..ba99724c96 100644 --- a/packages/schema/src/Schema.ts +++ b/packages/schema/src/Schema.ts @@ -708,31 +708,41 @@ const declareConstructor = < A >( typeParameters: TypeParameters, - decodeUnknown: ( - ...typeParameters: { - readonly [K in keyof TypeParameters]: Schema< - Schema.Type, - Schema.Encoded, - never - > - } - ) => (input: unknown, options: ParseOptions, ast: AST.Declaration) => Effect.Effect, - encodeUnknown: ( - ...typeParameters: { - readonly [K in keyof TypeParameters]: Schema< - Schema.Type, - Schema.Encoded, - never - > - } - ) => (input: unknown, options: ParseOptions, ast: AST.Declaration) => Effect.Effect, + 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 ): Schema> => make( new AST.Declaration( typeParameters.map((tp) => tp.ast), - (...typeParameters) => decodeUnknown(...typeParameters.map(make) as any), - (...typeParameters) => encodeUnknown(...typeParameters.map(make) as any), + (...typeParameters) => options.decode(...typeParameters.map(make) as any), + (...typeParameters) => options.encode(...typeParameters.map(make) as any), toASTAnnotations(annotations) ) ) @@ -761,29 +771,30 @@ export const declare: { ): Schema , I, A>( typeParameters: P, - decodeUnknown: ( - ...typeParameters: { readonly [K in keyof P]: Schema, Schema.Encoded, never> } - ) => ( - input: unknown, - options: ParseOptions, - ast: AST.Declaration - ) => Effect.Effect, - encodeUnknown: ( - ...typeParameters: { readonly [K in keyof P]: Schema, Schema.Encoded, never> } - ) => ( - input: unknown, - options: ParseOptions, - ast: AST.Declaration - ) => Effect.Effect, + 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 }> ): Schema> } = function() { if (Array.isArray(arguments[0])) { const typeParameters = arguments[0] - const decodeUnknown = arguments[1] - const encodeUnknown = arguments[2] - const annotations = arguments[3] - return declareConstructor(typeParameters, decodeUnknown, encodeUnknown, annotations) + const options = arguments[1] + const annotations = arguments[2] + return declareConstructor(typeParameters, options, annotations) } const is = arguments[0] const annotations = arguments[1] @@ -1630,15 +1641,17 @@ export const fromKey: { export const optionalToRequired = ( from: Schema, to: Schema, - decode: (o: Option.Option) => TI, - encode: (ti: TI) => Option.Option + options: { + readonly decode: (o: Option.Option) => TI + readonly encode: (ti: TI) => Option.Option + } ): PropertySignature<":", TA, never, "?:", FI, FR | TR> => new PropertySignatureImpl( new PropertySignatureTransformation( new FromPropertySignature(from.ast, true, true, {}, undefined), new ToPropertySignature(to.ast, false, true, {}), - (o) => Option.some(decode(o)), - Option.flatMap(encode) + (o) => Option.some(options.decode(o)), + Option.flatMap(options.encode) ) ) @@ -1656,15 +1669,17 @@ export const optionalToRequired = ( export const optionalToOptional = ( from: Schema, to: Schema, - decode: (o: Option.Option) => Option.Option, - encode: (o: Option.Option) => Option.Option + options: { + readonly decode: (o: Option.Option) => Option.Option + readonly encode: (o: Option.Option) => Option.Option + } ): PropertySignature<"?:", TA, never, "?:", FI, FR | TR> => new PropertySignatureImpl( new PropertySignatureTransformation( new FromPropertySignature(from.ast, true, true, {}, undefined), new ToPropertySignature(to.ast, true, true, {}), - decode, - encode + options.decode, + options.encode ) ) @@ -1774,15 +1789,16 @@ export const optional: { return optionalToRequired( nullable(schema), typeSchema(schema), - Option.match({ onNone: defaultValue, onSome: (a) => a === null ? defaultValue() : a }), - Option.some + { + decode: Option.match({ onNone: defaultValue, onSome: (a) => a === null ? defaultValue() : a }), + encode: Option.some + } ) } else { return optionalToRequired( schema, typeSchema(schema), - Option.match({ onNone: defaultValue, onSome: identity }), - Option.some + { decode: Option.match({ onNone: defaultValue, onSome: identity }), encode: Option.some } ) } } else if (asOption) { @@ -1790,15 +1806,13 @@ export const optional: { return optionalToRequired( nullable(schema), optionFromSelf(typeSchema(schema)), - Option.filter(Predicate.isNotNull), - identity + { decode: Option.filter(Predicate.isNotNull), encode: identity } ) } else { return optionalToRequired( schema, optionFromSelf(typeSchema(schema)), - identity, - identity + { decode: identity, encode: identity } ) } } else { @@ -1806,8 +1820,7 @@ export const optional: { return optionalToOptional( nullable(schema), typeSchema(schema), - Option.filter(Predicate.isNotNull), - identity + { decode: Option.filter(Predicate.isNotNull), encode: identity } ) } else { return new PropertySignatureImpl(new PropertySignatureDeclaration(schema.ast, true, true, {})) @@ -1819,15 +1832,19 @@ export const optional: { return optionalToRequired( nullish(schema), typeSchema(schema), - Option.match({ onNone: defaultValue, onSome: (a) => (a == null ? defaultValue() : a) }), - Option.some + { + decode: Option.match({ onNone: defaultValue, onSome: (a) => (a == null ? defaultValue() : a) }), + encode: Option.some + } ) } else { return optionalToRequired( orUndefined(schema), typeSchema(schema), - Option.match({ onNone: defaultValue, onSome: (a) => (a === undefined ? defaultValue() : a) }), - Option.some + { + decode: Option.match({ onNone: defaultValue, onSome: (a) => (a === undefined ? defaultValue() : a) }), + encode: Option.some + } ) } } else if (asOption) { @@ -1835,15 +1852,13 @@ export const optional: { return optionalToRequired( nullish(schema), optionFromSelf(typeSchema(schema)), - Option.filter((a): a is A => a != null), - identity + { decode: Option.filter((a): a is A => a != null), encode: identity } ) } else { return optionalToRequired( orUndefined(schema), optionFromSelf(typeSchema(schema)), - Option.filter(Predicate.isNotUndefined), - identity + { decode: Option.filter(Predicate.isNotUndefined), encode: identity } ) } } else { @@ -1851,8 +1866,7 @@ export const optional: { return optionalToOptional( nullish(schema), orUndefined(typeSchema(schema)), - Option.filter(Predicate.isNotNull), - identity + { decode: Option.filter(Predicate.isNotNull), encode: identity } ) } else { return new PropertySignatureImpl( @@ -2244,8 +2258,10 @@ export const pluck: { return transform( schema.pipe(pick(key)), value, - (a: any) => a[key], - (ak) => ps.isOptional && ak === undefined ? {} : { [key]: ak } as any + { + decode: (a: any) => a[key], + encode: (ak) => ps.isOptional && ak === undefined ? {} : { [key]: ak } as any + } ) } ) @@ -2260,7 +2276,7 @@ const makeBrandSchema = Either.match(_validateEither(unbranded), { - onLeft: (e) => Option.some(Brand.error(TreeFormatter.formatError(e), e)), + onLeft: (e) => Option.some(Brand.error(TreeFormatter.formatErrorSync(e), e)), onRight: () => Option.none() }) ) @@ -2438,7 +2454,7 @@ const intersectUnionMembers = ( if (AST.isTypeLiteral(y)) { return [intersectTypeLiterals(x, y, path)] } else if ( - AST.isTransform(y) && AST.isTypeLiteralTransformation(y.transformation) + AST.isTransformation(y) && AST.isTypeLiteralTransformation(y.transformation) ) { return [ new AST.Transformation( @@ -2451,7 +2467,7 @@ const intersectUnionMembers = ( ] } } else if ( - AST.isTransform(x) && AST.isTypeLiteralTransformation(x.transformation) + AST.isTransformation(x) && AST.isTypeLiteralTransformation(x.transformation) ) { if (AST.isTypeLiteral(y)) { return [ @@ -2464,7 +2480,7 @@ const intersectUnionMembers = ( ) ] } else if ( - AST.isTransform(y) && AST.isTypeLiteralTransformation(y.transformation) + AST.isTransformation(y) && AST.isTypeLiteralTransformation(y.transformation) ) { return [ new AST.Transformation( @@ -2622,77 +2638,84 @@ class transformOrFailImpl export const transformOrFail: { ( to: To, - decode: ( - fromA: Schema.Type, - options: ParseOptions, - ast: AST.Transformation - ) => Effect.Effect, ParseResult.ParseIssue, RD>, - encode: ( - toI: Schema.Encoded, - options: ParseOptions, - ast: AST.Transformation - ) => Effect.Effect, ParseResult.ParseIssue, RE> - ): (from: From) => transformOrFail - ( - to: To, - decode: ( - fromA: Schema.Type, - options: ParseOptions, - ast: AST.Transformation - ) => Effect.Effect, - encode: ( - toI: Schema.Encoded, - options: ParseOptions, - ast: AST.Transformation - ) => Effect.Effect, - options: { strict: false } + options: { + readonly decode: ( + fromA: Schema.Type, + options: ParseOptions, + ast: AST.Transformation + ) => Effect.Effect, ParseResult.ParseIssue, RD> + readonly encode: ( + toI: Schema.Encoded, + options: ParseOptions, + ast: AST.Transformation + ) => Effect.Effect, ParseResult.ParseIssue, RE> + readonly strict?: true + } | { + readonly decode: ( + fromA: Schema.Type, + options: ParseOptions, + ast: AST.Transformation + ) => Effect.Effect + readonly encode: ( + toI: Schema.Encoded, + options: ParseOptions, + ast: AST.Transformation + ) => Effect.Effect + readonly strict: false + } ): (from: From) => transformOrFail - ( - from: From, - to: To, - decode: ( - fromA: Schema.Type, - options: ParseOptions, - ast: AST.Transformation - ) => Effect.Effect, ParseResult.ParseIssue, RD>, - encode: ( - toI: Schema.Encoded, - options: ParseOptions, - ast: AST.Transformation - ) => Effect.Effect, ParseResult.ParseIssue, R4> - ): transformOrFail ( from: From, to: To, - decode: ( - fromA: Schema.Type, + options: { + readonly decode: ( + fromA: Schema.Type, + options: ParseOptions, + ast: AST.Transformation + ) => Effect.Effect, ParseResult.ParseIssue, RD> + readonly encode: ( + toI: Schema.Encoded, + options: ParseOptions, + ast: AST.Transformation + ) => Effect.Effect, ParseResult.ParseIssue, RE> + readonly strict?: true + } | { + readonly decode: ( + fromA: Schema.Type, + options: ParseOptions, + ast: AST.Transformation + ) => Effect.Effect + readonly encode: ( + toI: Schema.Encoded, + options: ParseOptions, + ast: AST.Transformation + ) => 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 - ) => Effect.Effect, - encode: ( - toI: Schema.Encoded, + ) => Effect.Effect + readonly encode: ( + toI: ToI, options: ParseOptions, ast: AST.Transformation - ) => Effect.Effect, - options: { strict: false } - ): transformOrFail -} = dual((args) => isSchema(args[0]) && isSchema(args[1]), ( - from: Schema, - to: Schema, - decode: ( - fromA: FromA, - options: ParseOptions, - ast: AST.Transformation - ) => Effect.Effect, - encode: (toI: ToI, options: ParseOptions, ast: AST.Transformation) => Effect.Effect -): Schema => + ) => Effect.Effect + } +): Schema => new transformOrFailImpl( from, to, new AST.Transformation( from.ast, to.ast, - new AST.FinalTransformation(decode, encode) + new AST.FinalTransformation(options.decode, options.encode) ) )) @@ -2714,37 +2737,47 @@ export interface transform exten export const transform: { ( to: To, - decode: (fromA: Schema.Type) => Schema.Encoded, - encode: (toI: Schema.Encoded) => Schema.Type - ): (from: From) => transform - ( - to: To, - decode: (fromA: Schema.Type) => unknown, - encode: (toI: Schema.Encoded) => unknown, - options: { strict: false } + options: { + readonly decode: (fromA: Schema.Type) => Schema.Encoded + readonly encode: (toI: Schema.Encoded) => Schema.Type + readonly strict?: true + } | { + readonly decode: (fromA: Schema.Type) => unknown + readonly encode: (toI: Schema.Encoded) => unknown + readonly strict: false + } ): (from: From) => transform ( from: From, to: To, - decode: (fromA: Schema.Type) => Schema.Encoded, - encode: (toI: Schema.Encoded) => Schema.Type - ): transform - ( - from: From, - to: To, - decode: (fromA: Schema.Type) => unknown, - encode: (toI: Schema.Encoded) => unknown, - options: { strict: false } + options: { + readonly decode: (fromA: Schema.Type) => Schema.Encoded + readonly encode: (toI: Schema.Encoded) => Schema.Type + readonly strict?: true + } | { + readonly decode: (fromA: Schema.Type) => unknown + readonly encode: (toI: Schema.Encoded) => unknown + readonly strict: false + } ): transform } = dual( (args) => isSchema(args[0]) && isSchema(args[1]), ( from: Schema, to: Schema, - decode: (fromA: FromA) => ToI, - encode: (toI: ToI) => FromA + options: { + readonly decode: (fromA: FromA) => ToI + readonly encode: (toI: ToI) => FromA + } ): Schema => - transformOrFail(from, to, (fromA) => ParseResult.succeed(decode(fromA)), (toI) => ParseResult.succeed(encode(toI))) + transformOrFail( + from, + to, + { + decode: (fromA) => ParseResult.succeed(options.decode(fromA)), + encode: (toI) => ParseResult.succeed(options.encode(toI)) + } + ) ) /** @@ -2769,7 +2802,7 @@ export interface transformLiteral extends Annotable( from: Encoded, to: Type -): transformLiteral => transform(literal(from), literal(to), () => to, () => from) +): transformLiteral => transform(literal(from), literal(to), { decode: () => to, encode: () => from }) /** * Creates a new `Schema` which maps between corresponding literal values. @@ -3426,8 +3459,7 @@ export interface Lowercase extends Annotable {} export const Lowercase: Lowercase = transform( string, Lowercased, - (s) => s.toLowerCase(), - identity + { decode: (s) => s.toLowerCase(), encode: identity } ).annotations({ identifier: "Lowercase" }) /** @@ -3445,8 +3477,7 @@ export interface Uppercase extends Annotable {} export const Uppercase: Uppercase = transform( string, Uppercased, - (s) => s.toUpperCase(), - identity + { decode: (s) => s.toUpperCase(), encode: identity } ).annotations({ identifier: "Uppercase" }) /** @@ -3472,8 +3503,7 @@ export interface Trim extends Annotable {} export const Trim: Trim = transform( string, Trimmed, - (s) => s.trim(), - identity + { decode: (s) => s.trim(), encode: identity } ).annotations({ identifier: "Trim" }) /** @@ -3486,8 +3516,7 @@ export const split = (separator: string): Schema, string> transform( string, array(string), - S.split(separator), - ReadonlyArray.join(separator) + { decode: S.split(separator), encode: ReadonlyArray.join(separator) } ) /** @@ -3533,16 +3562,18 @@ export const parseJson: { return transformOrFail( JsonString, unknown, - (s, _, ast) => - ParseResult.try({ - try: () => JSON.parse(s, options?.reviver), - catch: (e: any) => new ParseResult.Type(ast, s, e.message) - }), - (u, _, ast) => - ParseResult.try({ - try: () => JSON.stringify(u, options?.replacer, options?.space), - catch: (e: any) => new ParseResult.Type(ast, u, e.message) - }) + { + 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) + }) + } ) } @@ -3905,9 +3936,7 @@ export const clamp = transform( self, self.pipe(typeSchema, between(minimum, maximum)), - (self) => N.clamp(self, { minimum, maximum }), - identity, - { strict: false } + { strict: false, decode: (self) => N.clamp(self, { minimum, maximum }), encode: identity } ) /** @@ -3929,8 +3958,10 @@ export interface NumberFromString extends Annotable ParseResult.fromOption(N.parse(s), () => new ParseResult.Type(ast, s)), - (n) => ParseResult.succeed(String(n)) + { + decode: (s, _, ast) => ParseResult.fromOption(N.parse(s), () => new ParseResult.Type(ast, s)), + encode: (n) => ParseResult.succeed(String(n)) + } ).annotations({ identifier: "NumberFromString" }) /** @@ -4027,7 +4058,7 @@ export interface Not extends Annotable {} * @category boolean transformations * @since 1.0.0 */ -export const Not: Not = transform(boolean, boolean, boolean_.not, boolean_.not) +export const Not: Not = transform(boolean, boolean, { decode: boolean_.not, encode: boolean_.not }) /** * @category api interface @@ -4044,9 +4075,7 @@ export interface $symbol extends Annotable<$symbol, symbol, string> {} export const symbol: $symbol = transform( string, symbolFromSelf, - (s) => Symbol.for(s), - (sym) => sym.description, - { strict: false } + { strict: false, decode: (s) => Symbol.for(s), encode: (sym) => sym.description } ).annotations({ identifier: "symbol" }) /** @@ -4240,9 +4269,7 @@ export const clampBigint = transform( self, self.pipe(typeSchema, betweenBigint(minimum, maximum)), - (self) => bigInt_.clamp(self, { minimum, maximum }), - identity, - { strict: false } + { strict: false, decode: (self) => bigInt_.clamp(self, { minimum, maximum }), encode: identity } ) /** @@ -4262,8 +4289,10 @@ export interface $bigint extends Annotable<$bigint, bigint, string> {} export const bigint: $bigint = transformOrFail( string, bigintFromSelf, - (s, _, ast) => ParseResult.fromOption(bigInt_.fromString(s), () => new ParseResult.Type(ast, s)), - (n) => ParseResult.succeed(String(n)) + { + decode: (s, _, ast) => ParseResult.fromOption(bigInt_.fromString(s), () => new ParseResult.Type(ast, s)), + encode: (n) => ParseResult.succeed(String(n)) + } ).annotations({ identifier: "bigint" }) /** @@ -4347,12 +4376,14 @@ export interface BigintFromNumber extends Annotable - ParseResult.fromOption( - bigInt_.fromNumber(n), - () => new ParseResult.Type(ast, n) - ), - (b, _, ast) => ParseResult.fromOption(bigInt_.toNumber(b), () => new ParseResult.Type(ast, b)) + { + 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" }) /** @@ -4389,9 +4420,7 @@ export interface Secret extends Annotable {} export const Secret: Secret = transform( string, SecretFromSelf, - (str) => secret_.fromString(str), - (secret) => secret_.value(secret), - { strict: false } + { strict: false, decode: (str) => secret_.fromString(str), encode: (secret) => secret_.value(secret) } ).annotations({ identifier: "Secret" }) /** @@ -4441,12 +4470,14 @@ export interface DurationFromNanos extends Annotable ParseResult.succeed(duration_.nanos(nanos)), - (duration, _, ast) => - Option.match(duration_.toNanos(duration), { - onNone: () => ParseResult.fail(new ParseResult.Type(ast, duration)), - onSome: (val) => ParseResult.succeed(val) - }) + { + 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" }) /** @@ -4465,8 +4496,7 @@ export interface DurationFromMillis extends Annotable duration_.millis(ms), - (n) => duration_.toMillis(n) + { decode: (ms) => duration_.millis(ms), encode: (n) => duration_.toMillis(n) } ).annotations({ identifier: "DurationFromMillis" }) const hrTime: Schema = tuple( @@ -4499,8 +4529,10 @@ export interface Duration extends Annotable duration_.nanos(BigInt(seconds) * BigInt(1e9) + BigInt(nanos)), - (duration) => duration_.toHrTime(duration) + { + decode: ([seconds, nanos]) => duration_.nanos(BigInt(seconds) * BigInt(1e9) + BigInt(nanos)), + encode: (duration) => duration_.toHrTime(duration) + } ).annotations({ identifier: "Duration" }) /** @@ -4515,9 +4547,7 @@ export const clampDuration = transform( self, self.pipe(typeSchema, betweenDuration(minimum, maximum)), - (self) => duration_.clamp(self, { minimum, maximum }), - identity, - { strict: false } + { strict: false, decode: (self) => duration_.clamp(self, { minimum, maximum }), encode: identity } ) /** @@ -4662,8 +4692,7 @@ const _Uint8Array: Schema> = transform( }) )).annotations({ description: "an array of 8-bit unsigned integers" }), Uint8ArrayFromSelf, - (a) => Uint8Array.from(a), - (arr) => Array.from(arr) + { decode: (numbers) => Uint8Array.from(numbers), encode: (uint8Array) => Array.from(uint8Array) } ).annotations({ identifier: "Uint8Array" }) export { @@ -4684,13 +4713,15 @@ const makeEncodingTransformation = ( transformOrFail( string, Uint8ArrayFromSelf, - (s, _, ast) => - Either.mapLeft( - decode(s), - (decodeException) => new ParseResult.Type(ast, s, decodeException.message) - ), - (u) => ParseResult.succeed(encode(u)), - { strict: false } + { + strict: false, + decode: (s, _, ast) => + Either.mapLeft( + decode(s), + (decodeException) => new ParseResult.Type(ast, s, decodeException.message) + ), + encode: (u) => ParseResult.succeed(encode(u)) + } ).annotations({ identifier: id }) /** @@ -4831,8 +4862,7 @@ export const head = (self: Schema, I, R>): Schema [], onSome: ReadonlyArray.of }) + { decode: ReadonlyArray.head, encode: Option.match({ onNone: () => [], onSome: ReadonlyArray.of }) } ) /** @@ -4852,13 +4882,15 @@ export const headOrElse: { transformOrFail( self, getNumberIndexedAccess(typeSchema(self)), - (as, _, ast) => - as.length > 0 - ? ParseResult.succeed(as[0]) - : fallback - ? ParseResult.succeed(fallback()) - : ParseResult.fail(new ParseResult.Type(ast, as)), - (a) => ParseResult.succeed(ReadonlyArray.of(a)) + { + 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(ReadonlyArray.of(a)) + } ) ) @@ -4941,8 +4973,7 @@ export interface DateFromString extends Annotable export const DateFromString: DateFromString = transform( string, DateFromSelf, - (s) => new Date(s), - (n) => n.toISOString() + { decode: (s) => new Date(s), encode: (n) => n.toISOString() } ).annotations({ identifier: "DateFromString" }) /** @@ -5041,8 +5072,10 @@ export const optionFromSelf = ( ): optionFromSelf => { return declare( [value], - (value) => optionParse(ParseResult.decodeUnknown(value)), - (value) => optionParse(ParseResult.encodeUnknown(value)), + { + decode: (value) => optionParse(ParseResult.decodeUnknown(value)), + encode: (value) => optionParse(ParseResult.encodeUnknown(value)) + }, { description: `Option<${format(value)}>`, pretty: optionPretty, @@ -5082,11 +5115,13 @@ export const option = (value: Value): option => return transform( optionEncoded(_value), optionFromSelf(typeSchema(_value)), - optionDecode, - Option.match({ - onNone: () => makeNoneEncoded, - onSome: makeSomeEncoded - }) + { + decode: optionDecode, + encode: Option.match({ + onNone: () => makeNoneEncoded, + onSome: makeSomeEncoded + }) + } ) } @@ -5111,7 +5146,10 @@ export const optionFromNullable = ( value: Value ): optionFromNullable => { const _value = asSchema(value) - return transform(nullable(_value), optionFromSelf(typeSchema(_value)), Option.fromNullable, Option.getOrNull) + return transform(nullable(_value), optionFromSelf(typeSchema(_value)), { + decode: Option.fromNullable, + encode: Option.getOrNull + }) } /** @@ -5139,8 +5177,7 @@ export const optionFromNullish = ( return transform( nullish(_value), optionFromSelf(typeSchema(_value)), - Option.fromNullable, - onNoneEncoding === null ? Option.getOrNull : Option.getOrUndefined + { decode: Option.fromNullable, encode: onNoneEncoding === null ? Option.getOrNull : Option.getOrUndefined } ) } @@ -5165,7 +5202,10 @@ export const optionFromOrUndefined = ( value: Value ): optionFromOrUndefined => { const _value = asSchema(value) - return transform(orUndefined(_value), optionFromSelf(typeSchema(_value)), Option.fromNullable, Option.getOrUndefined) + return transform(orUndefined(_value), optionFromSelf(typeSchema(_value)), { + decode: Option.fromNullable, + encode: Option.getOrUndefined + }) } /** @@ -5267,8 +5307,10 @@ export const eitherFromSelf = ({ lef }): eitherFromSelf => { return declare( [right, left], - (right, left) => eitherParse(ParseResult.decodeUnknown(right), ParseResult.decodeUnknown(left)), - (right, left) => eitherParse(ParseResult.encodeUnknown(right), ParseResult.encodeUnknown(left)), + { + decode: (right, left) => eitherParse(ParseResult.decodeUnknown(right), ParseResult.decodeUnknown(left)), + encode: (right, left) => eitherParse(ParseResult.encodeUnknown(right), ParseResult.encodeUnknown(left)) + }, { description: `Either<${format(left)}, ${format(right)}>`, pretty: eitherPretty, @@ -5313,8 +5355,7 @@ export const either = ({ left, right return transform( eitherEncoded(_right, _left), eitherFromSelf({ left: typeSchema(_left), right: typeSchema(_right) }), - eitherDecode, - Either.match({ onLeft: makeLeftEncoded, onRight: makeRightEncoded }) + { decode: eitherDecode, encode: Either.match({ onLeft: makeLeftEncoded, onRight: makeRightEncoded }) } ) } @@ -5349,13 +5390,15 @@ export const eitherFromUnion = ({ le const _left = asSchema(left) const toright = typeSchema(_right) const toleft = typeSchema(_left) - const fromRight = transform(_right, rightEncoded(toright), makeRightEncoded, (r) => r.right) - const fromLeft = transform(_left, leftEncoded(toleft), makeLeftEncoded, (l) => l.left) + const fromRight = transform(_right, rightEncoded(toright), { decode: makeRightEncoded, encode: (r) => r.right }) + const fromLeft = transform(_left, leftEncoded(toleft), { decode: makeLeftEncoded, encode: (l) => l.left }) return transform( union(fromRight, fromLeft), eitherFromSelf({ left: toleft, right: toright }), - (from) => from._tag === "Left" ? Either.left(from.left) : Either.right(from.right), - Either.match({ onLeft: makeLeftEncoded, onRight: makeRightEncoded }) + { + decode: (from) => from._tag === "Left" ? Either.left(from.left) : Either.right(from.right), + encode: Either.match({ onLeft: makeLeftEncoded, onRight: makeRightEncoded }) + } ) } @@ -5414,8 +5457,10 @@ const _mapFromSelf = ( ): readonlyMapFromSelf => declare( [key, value], - (key, value) => readonlyMapParse(ParseResult.decodeUnknown(array(tuple(key, value)))), - (key, value) => readonlyMapParse(ParseResult.encodeUnknown(array(tuple(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, @@ -5481,8 +5526,7 @@ export const readonlyMap = ({ key, v return transform( array(tuple(_key, _value)), readonlyMapFromSelf({ key: typeSchema(_key), value: typeSchema(_value) }), - (as) => new Map(as), - (map) => Array.from(map.entries()) + { decode: (as) => new Map(as), encode: (map) => Array.from(map.entries()) } ) } @@ -5512,8 +5556,7 @@ export const map = ({ key, value }: return transform( array(tuple(_key, _value)), mapFromSelf({ key: typeSchema(_key), value: typeSchema(_value) }), - (as) => new Map(as), - (map) => Array.from(map.entries()) + { decode: (as) => new Map(as), encode: (map) => Array.from(map.entries()) } ) } @@ -5554,8 +5597,10 @@ export interface readonlySetFromSelf extends const _setFromSelf = (value: Value, description: string): readonlySetFromSelf => declare( [value], - (item) => readonlySetParse(ParseResult.decodeUnknown(array(item))), - (item) => readonlySetParse(ParseResult.encodeUnknown(array(item))), + { + decode: (item) => readonlySetParse(ParseResult.decodeUnknown(array(item))), + encode: (item) => readonlySetParse(ParseResult.encodeUnknown(array(item))) + }, { description, pretty: readonlySetPretty, @@ -5613,8 +5658,7 @@ export const readonlySet = (value: Value): readonlySet return transform( array(_value), readonlySetFromSelf(typeSchema(_value)), - (as) => new Set(as), - (set) => Array.from(set) + { decode: (as) => new Set(as), encode: (set) => Array.from(set) } ) } @@ -5640,8 +5684,7 @@ export const set = (value: Value): set => { return transform( array(_value), setFromSelf(typeSchema(_value)), - (as) => new Set(as), - (set) => Array.from(set) + { decode: (as) => new Set(as), encode: (set) => Array.from(set) } ) } @@ -5684,12 +5727,14 @@ export interface BigDecimal extends Annotable - bigDecimal_.fromString(num).pipe(Option.match({ - onNone: () => ParseResult.fail(new ParseResult.Type(ast, num)), - onSome: (val) => ParseResult.succeed(bigDecimal_.normalize(val)) - })), - (val) => ParseResult.succeed(bigDecimal_.format(bigDecimal_.normalize(val))) + { + 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" }) /** @@ -5708,8 +5753,10 @@ export interface BigDecimalFromNumber extends Annotable ParseResult.succeed(bigDecimal_.fromNumber(num)), - (val) => ParseResult.succeed(bigDecimal_.unsafeToNumber(val)) + { + decode: (num) => ParseResult.succeed(bigDecimal_.fromNumber(num)), + encode: (val) => ParseResult.succeed(bigDecimal_.unsafeToNumber(val)) + } ).annotations({ identifier: "BigDecimalFromNumber" }) /** @@ -5984,21 +6031,9 @@ export const clampBigDecimal = transform( self, self.pipe(typeSchema, betweenBigDecimal(minimum, maximum)), - (self) => bigDecimal_.clamp(self, { minimum, maximum }), - identity, - { strict: false } + { strict: false, decode: (self) => bigDecimal_.clamp(self, { minimum, maximum }), encode: identity } ) -/** - * Negates a `BigDecimal`. - * - * @category BigDecimal transformations - * @since 1.0.0 - */ -export const negateBigDecimal = ( - self: Schema -): Schema => transform(self, typeSchema(self), bigDecimal_.negate, bigDecimal_.negate, { strict: false }) - const chunkArbitrary = (item: Arbitrary): Arbitrary> => (fc) => fc.array(item(fc)).map(Chunk.fromIterable) @@ -6035,8 +6070,10 @@ export interface chunkFromSelf extends export const chunkFromSelf = (value: Value): chunkFromSelf => { return declare( [value], - (item) => chunkParse(ParseResult.decodeUnknown(array(item))), - (item) => chunkParse(ParseResult.encodeUnknown(array(item))), + { + decode: (item) => chunkParse(ParseResult.decodeUnknown(array(item))), + encode: (item) => chunkParse(ParseResult.encodeUnknown(array(item))) + }, { description: `Chunk<${format(value)}>`, pretty: chunkPretty, @@ -6068,8 +6105,7 @@ export const chunk = (value: Value): chunk => { return transform( array(_value), chunkFromSelf(typeSchema(_value)), - (as) => as.length === 0 ? Chunk.empty() : Chunk.fromIterable(as), - Chunk.toReadonlyArray + { decode: (as) => as.length === 0 ? Chunk.empty() : Chunk.fromIterable(as), encode: Chunk.toReadonlyArray } ) } @@ -6107,8 +6143,10 @@ export const dataFromSelf = < ): Schema => { return declare( [item], - (item) => dataParse(ParseResult.decodeUnknown(item)), - (item) => dataParse(ParseResult.encodeUnknown(item)), + { + decode: (item) => dataParse(ParseResult.decodeUnknown(item)), + encode: (item) => dataParse(ParseResult.encodeUnknown(item)) + }, { description: `Data<${format(item)}>`, pretty: dataPretty, @@ -6131,9 +6169,7 @@ export const data = < transform( item, dataFromSelf(typeSchema(item)), - toData, - (a) => Array.isArray(a) ? Array.from(a) : Object.assign({}, a), - { strict: false } + { strict: false, decode: toData, encode: (a) => Array.isArray(a) ? Array.from(a) : Object.assign({}, a) } ) type MissingSelfGeneric = @@ -6174,16 +6210,18 @@ export interface Class( fields: newFields, - decode: ( - input: A, - options: ParseOptions, - ast: AST.Transformation - ) => Effect.Effect>, ParseResult.ParseIssue, R2>, - encode: ( - input: Types.Simplify>, - options: ParseOptions, - ast: AST.Transformation - ) => Effect.Effect + options: { + readonly decode: ( + input: A, + options: ParseOptions, + ast: AST.Transformation + ) => Effect.Effect>, ParseResult.ParseIssue, R2> + readonly encode: ( + input: Types.Simplify>, + options: ParseOptions, + ast: AST.Transformation + ) => Effect.Effect + } ) => [Transformed] extends [never] ? MissingSelfGeneric<"Base.transform"> : Class< Transformed, @@ -6202,16 +6240,18 @@ export interface Class( fields: newFields, - decode: ( - input: I, - options: ParseOptions, - ast: AST.Transformation - ) => Effect.Effect>, ParseResult.ParseIssue, R2>, - encode: ( - input: Types.Simplify>, - options: ParseOptions, - ast: AST.Transformation - ) => Effect.Effect + options: { + readonly decode: ( + input: I, + options: ParseOptions, + ast: AST.Transformation + ) => Effect.Effect>, ParseResult.ParseIssue, R2> + readonly encode: ( + input: Types.Simplify>, + options: ParseOptions, + ast: AST.Transformation + ) => Effect.Effect + } ) => [Transformed] extends [never] ? MissingSelfGeneric<"Base.transformFrom"> : Class< Transformed, @@ -6464,17 +6504,19 @@ const makeClass = ({ Base, annotations, fields, fromSchema, identifier, kind, ta const equivalence = equivalence_.make(toSchema) const declaration: Schema.Any = declare( [], - () => (input, _, ast) => - input instanceof this || fallbackInstanceOf(input) - ? ParseResult.succeed(input) - : ParseResult.fail(new ParseResult.Type(ast, input)), - () => (input, options) => - input instanceof this - ? ParseResult.succeed(input) - : ParseResult.map( - encode(input, options), - (props) => new this(props, true) - ), + { + 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( + encode(input, options), + (props) => new this(props, true) + ) + }, { identifier, title: identifier, @@ -6493,8 +6535,7 @@ const makeClass = ({ Base, annotations, fields, fromSchema, identifier, kind, ta const transformation = transform( from, declaration, - (input) => new this(input, true), - identity + { decode: (input) => new this(input, true), encode: identity } ).annotations({ [AST.SurrogateAnnotationId]: schema.ast }) return transformation.ast } @@ -6514,7 +6555,7 @@ const makeClass = ({ Base, annotations, fields, fromSchema, identifier, kind, ta } static transformOrFail(identifier: string) { - return (newFields: Struct.Fields, decode: any, encode: any, annotations?: Annotations.Schema) => { + return (newFields: Struct.Fields, options: any, annotations?: Annotations.Schema) => { const transformedFields: Struct.Fields = extendFields(fields, newFields) return makeClass({ kind, @@ -6522,8 +6563,7 @@ const makeClass = ({ Base, annotations, fields, fromSchema, identifier, kind, ta fromSchema: transformOrFail( schema, typeSchema(struct(transformedFields)), - decode, - encode + options ), fields: transformedFields, Base: this, @@ -6534,7 +6574,7 @@ const makeClass = ({ Base, annotations, fields, fromSchema, identifier, kind, ta } static transformOrFailFrom(identifier: string) { - return (newFields: Struct.Fields, decode: any, encode: any, annotations?: Annotations.Schema) => { + return (newFields: Struct.Fields, options: any, annotations?: Annotations.Schema) => { const transformedFields: Struct.Fields = extendFields(fields, newFields) return makeClass({ kind, @@ -6542,8 +6582,7 @@ const makeClass = ({ Base, annotations, fields, fromSchema, identifier, kind, ta fromSchema: transformOrFail( encodedSchema(schema), struct(transformedFields), - decode, - encode + options ), fields: transformedFields, Base: this, @@ -6675,8 +6714,7 @@ export interface FiberId extends Annotable({ defect = unkno }): causeFromSelf => { return declare( [error, defect], - (error, defect) => causeParse(ParseResult.decodeUnknown(causeEncoded(error, defect))), - (error, defect) => causeParse(ParseResult.encodeUnknown(causeEncoded(error, defect))), + { + decode: (error, defect) => causeParse(ParseResult.decodeUnknown(causeEncoded(error, defect))), + encode: (error, defect) => causeParse(ParseResult.encodeUnknown(causeEncoded(error, defect))) + }, { description: `Cause<${format(error)}>`, pretty: causePretty, @@ -6879,25 +6919,27 @@ function causeEncode(cause: Cause.Cause): CauseEncoded { export const causeDefectUnknown: $unknown = transform( unknown, unknown, - (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 + { + 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 } - err.stack = "stack" in u && typeof u.stack === "string" ? u.stack : "" - return err - } - return String(u) - }, - (defect) => { - if (defect instanceof Error) { - return { - name: defect.name, - message: defect.message + return String(u) + }, + encode: (defect) => { + if (defect instanceof Error) { + return { + name: defect.name, + message: defect.message + } } + return String(defect) } - return String(defect) } ) @@ -6926,8 +6968,7 @@ export const cause = ({ defect = causeDefectUn return transform( causeEncoded(_error, defect), causeFromSelf({ error: typeSchema(_error), defect: typeSchema(defect) }), - causeDecode, - causeEncode + { decode: causeDecode, encode: causeEncode } ) } @@ -7036,16 +7077,18 @@ export const exitFromSelf = => declare( [success, failure, defect], - (success, failure, defect) => - exitParse( - ParseResult.decodeUnknown(success), - ParseResult.decodeUnknown(causeFromSelf({ error: failure, defect })) - ), - (success, failure, defect) => - exitParse( - ParseResult.encodeUnknown(success), - ParseResult.encodeUnknown(causeFromSelf({ error: 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 })) + ) + }, { description: `Exit<${format(failure)}, ${format(success)}>`, pretty: exitPretty, @@ -7082,11 +7125,13 @@ export const exit = ( return transform( exitEncoded(_success, _failure, defect), exitFromSelf({ failure: typeSchema(_failure), success: typeSchema(_success), defect: typeSchema(defect) }), - exitDecode, - (exit) => - exit._tag === "Failure" - ? { _tag: "Failure", cause: exit.cause } as const - : { _tag: "Success", value: exit.value } as const + { + decode: exitDecode, + encode: (exit) => + exit._tag === "Failure" + ? { _tag: "Failure", cause: exit.cause } as const + : { _tag: "Success", value: exit.value } as const + } ) } @@ -7136,8 +7181,10 @@ export const hashSetFromSelf = ( ): hashSetFromSelf => { return declare( [value], - (item) => hashSetParse(ParseResult.decodeUnknown(array(item))), - (item) => hashSetParse(ParseResult.encodeUnknown(array(item))), + { + decode: (item) => hashSetParse(ParseResult.decodeUnknown(array(item))), + encode: (item) => hashSetParse(ParseResult.encodeUnknown(array(item))) + }, { description: `HashSet<${format(value)}>`, pretty: hashSetPretty, @@ -7169,8 +7216,7 @@ export const hashSet = (value: Value): hashSet return transform( array(_value), hashSetFromSelf(typeSchema(_value)), - (as) => HashSet.fromIterable(as), - (set) => Array.from(set) + { decode: (as) => HashSet.fromIterable(as), encode: (set) => Array.from(set) } ) } @@ -7232,8 +7278,10 @@ export const hashMapFromSelf = ({ ke }): hashMapFromSelf => { return declare( [key, value], - (key, value) => hashMapParse(ParseResult.decodeUnknown(array(tuple(key, value)))), - (key, value) => hashMapParse(ParseResult.encodeUnknown(array(tuple(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, @@ -7269,8 +7317,7 @@ export const hashMap = ({ key, value return transform( array(tuple(_key, _value)), hashMapFromSelf({ key: typeSchema(_key), value: typeSchema(_value) }), - (as) => HashMap.fromIterable(as), - (map) => Array.from(map) + { decode: (as) => HashMap.fromIterable(as), encode: (map) => Array.from(map) } ) } @@ -7320,8 +7367,10 @@ export const listFromSelf = ( ): listFromSelf => { return declare( [value], - (item) => listParse(ParseResult.decodeUnknown(array(item))), - (item) => listParse(ParseResult.encodeUnknown(array(item))), + { + decode: (item) => listParse(ParseResult.decodeUnknown(array(item))), + encode: (item) => listParse(ParseResult.encodeUnknown(array(item))) + }, { description: `List<${format(value)}>`, pretty: listPretty, @@ -7353,8 +7402,7 @@ export const list = (value: Value): list => { return transform( array(_value), listFromSelf(typeSchema(_value)), - (as) => List.fromIterable(as), - (set) => Array.from(set) + { decode: (as) => List.fromIterable(as), encode: (set) => Array.from(set) } ) } @@ -7398,8 +7446,10 @@ export const sortedSetFromSelf = ( ): sortedSetFromSelf => { return declare( [value], - (item) => sortedSetParse(ParseResult.decodeUnknown(array(item)), ordA), - (item) => sortedSetParse(ParseResult.encodeUnknown(array(item)), ordI), + { + decode: (item) => sortedSetParse(ParseResult.decodeUnknown(array(item)), ordA), + encode: (item) => sortedSetParse(ParseResult.encodeUnknown(array(item)), ordI) + }, { description: `SortedSet<${format(value)}>`, pretty: sortedSetPretty, @@ -7435,8 +7485,7 @@ export const sortedSet = ( return transform( array(_value), sortedSetFromSelf(to, ordA, ordA), - (as) => SortedSet.fromIterable(as, ordA), - (set) => Array.from(SortedSet.values(set)) + { decode: (as) => SortedSet.fromIterable(as, ordA), encode: (set) => Array.from(SortedSet.values(set)) } ) } @@ -7462,6 +7511,5 @@ export interface BooleanFromUnknown extends Annotable(value: A, forest: Forest = []): Tree => ({ * @category formatting * @since 1.0.0 */ -export const formatIssueEffect = (issue: ParseResult.ParseIssue): Effect.Effect => +export const formatIssue = (issue: ParseResult.ParseIssue): Effect.Effect => Effect.map(go(issue), (tree) => drawTree(tree)) /** * @category formatting * @since 1.0.0 */ -export const formatIssue = (issue: ParseResult.ParseIssue): string => Effect.runSync(formatIssueEffect(issue)) +export const formatIssueSync = (issue: ParseResult.ParseIssue): string => Effect.runSync(formatIssue(issue)) /** * @category formatting * @since 1.0.0 */ -export const formatErrorEffect = (error: ParseResult.ParseError): Effect.Effect => - formatIssueEffect(error.error) +export const formatError = (error: ParseResult.ParseError): Effect.Effect => formatIssue(error.error) /** * @category formatting * @since 1.0.0 */ -export const formatError = (error: ParseResult.ParseError): string => formatIssue(error.error) +export const formatErrorSync = (error: ParseResult.ParseError): string => formatIssueSync(error.error) const drawTree = (tree: Tree): string => tree.value + draw("\n", tree.forest) diff --git a/packages/schema/test/AST/guards.test.ts b/packages/schema/test/AST/guards.test.ts index 6d571a7382..f6c9ef8950 100644 --- a/packages/schema/test/AST/guards.test.ts +++ b/packages/schema/test/AST/guards.test.ts @@ -23,8 +23,8 @@ describe("AST > guards", () => { }) it("isTransform", () => { - expect(AST.isTransform(S.Trim.ast)).toEqual(true) - expect(AST.isTransform(S.number.ast)).toEqual(false) + expect(AST.isTransformation(S.Trim.ast)).toEqual(true) + expect(AST.isTransformation(S.number.ast)).toEqual(false) }) it("isUndefinedKeyword", () => { diff --git a/packages/schema/test/AST/mutable.test.ts b/packages/schema/test/AST/mutable.test.ts index 48d6ab59a6..20675ec3de 100644 --- a/packages/schema/test/AST/mutable.test.ts +++ b/packages/schema/test/AST/mutable.test.ts @@ -31,6 +31,6 @@ describe("AST > mutable", () => { }) it("transformation", () => { - expectSameReference(S.transform(S.array(S.string), S.array(S.string), identity, identity)) + expectSameReference(S.transform(S.array(S.string), S.array(S.string), { decode: identity, encode: identity })) }) }) diff --git a/packages/schema/test/ArrayFormatter.test.ts b/packages/schema/test/ArrayFormatter.test.ts index f7e95b47d0..050808ecab 100644 --- a/packages/schema/test/ArrayFormatter.test.ts +++ b/packages/schema/test/ArrayFormatter.test.ts @@ -12,7 +12,7 @@ const options: ParseOptions = { errors: "all", onExcessProperty: "error" } const expectIssues = (schema: S.Schema, input: unknown, issues: Array) => { const result = S.decodeUnknownEither(schema)(input, options).pipe( Either.mapLeft((e) => - ArrayFormatter.formatIssue(e.error).map((issue) => ({ ...issue, message: String(issue.message) })) + ArrayFormatter.formatIssueSync(e.error).map((issue) => ({ ...issue, message: String(issue.message) })) ) ) expect(result).toStrictEqual(Either.left(issues)) @@ -47,8 +47,10 @@ describe("ArrayFormatter", () => { const schema = S.string.pipe( S.transformOrFail( S.string, - (s, _, ast) => ParseResult.fail(new ParseResult.Type(ast, s, "my custom message")), - ParseResult.succeed + { + decode: (s, _, ast) => ParseResult.fail(new ParseResult.Type(ast, s, "my custom message")), + encode: ParseResult.succeed + } ) ) expectIssues(schema, "", [{ @@ -360,13 +362,15 @@ describe("ArrayFormatter", () => { const schema = S.transformOrFail( S.string.annotations({ message: () => "please enter a string" }), S.Int.annotations({ message: () => "please enter an integer" }), - (s, _, ast) => { - const n = Number(s) - return Number.isNaN(n) - ? ParseResult.fail(new ParseResult.Type(ast, s)) - : ParseResult.succeed(n) - }, - (n) => ParseResult.succeed(String(n)) + { + decode: (s, _, ast) => { + const n = Number(s) + return Number.isNaN(n) + ? ParseResult.fail(new ParseResult.Type(ast, s)) + : ParseResult.succeed(n) + }, + encode: (n) => ParseResult.succeed(String(n)) + } ).annotations({ identifier: "IntFromString", message: () => "please enter a decodeUnknownable string" diff --git a/packages/schema/test/BigDecimal/negateBigDecimal.test.ts b/packages/schema/test/BigDecimal/negateBigDecimal.test.ts deleted file mode 100644 index 725aeab2f9..0000000000 --- a/packages/schema/test/BigDecimal/negateBigDecimal.test.ts +++ /dev/null @@ -1,22 +0,0 @@ -import * as S from "@effect/schema/Schema" -import * as Util from "@effect/schema/test/util" -import { BigDecimal } from "effect" -import { describe, it } from "vitest" - -describe("BigDecimal/negateBigDecimal", () => { - it("decoding", async () => { - const schema = S.BigDecimalFromSelf.pipe(S.negateBigDecimal) - - await Util.expectDecodeUnknownSuccess(schema, BigDecimal.make(3n, 0), BigDecimal.make(-3n, 0)) - await Util.expectDecodeUnknownSuccess(schema, BigDecimal.make(0n, 0), BigDecimal.make(0n, 0)) - await Util.expectDecodeUnknownSuccess(schema, BigDecimal.make(-3n, 0), BigDecimal.make(3n, 0)) - }) - - it("encoding", async () => { - const schema = S.BigDecimalFromSelf.pipe(S.negateBigDecimal) - - await Util.expectEncodeSuccess(schema, BigDecimal.make(3n, 0), BigDecimal.make(-3n, 0)) - await Util.expectEncodeSuccess(schema, BigDecimal.make(0n, 0), BigDecimal.make(0n, 0)) - await Util.expectEncodeSuccess(schema, BigDecimal.make(-3n, 0), BigDecimal.make(3n, 0)) - }) -}) diff --git a/packages/schema/test/ParseResult.test.ts b/packages/schema/test/ParseResult.test.ts index 1c3bc38b7b..fdd947ab65 100644 --- a/packages/schema/test/ParseResult.test.ts +++ b/packages/schema/test/ParseResult.test.ts @@ -159,8 +159,7 @@ describe("ParseIssue.actual", () => { const result = S.decodeEither(S.transformOrFail( S.NumberFromString, S.boolean, - (n, _, ast) => P.fail(new P.Type(ast, n)), - (b, _, ast) => P.fail(new P.Type(ast, b)) + { decode: (n, _, ast) => P.fail(new P.Type(ast, n)), encode: (b, _, ast) => P.fail(new P.Type(ast, b)) } ))("1") if (Either.isRight(result)) throw new Error("Expected failure") expect(result.left.error.actual).toEqual("1") @@ -171,8 +170,7 @@ describe("ParseIssue.actual", () => { const result = S.encodeEither(S.transformOrFail( S.boolean, S.NumberFromString, - (n, _, ast) => P.fail(new P.Type(ast, n)), - (b, _, ast) => P.fail(new P.Type(ast, b)) + { decode: (n, _, ast) => P.fail(new P.Type(ast, n)), encode: (b, _, ast) => P.fail(new P.Type(ast, b)) } ))(1) if (Either.isRight(result)) throw new Error("Expected failure") expect(result.left.error.actual).toEqual(1) @@ -271,8 +269,7 @@ describe("ParseIssue.actual", () => { _tag: S.transform( S.literal("a"), S.literal("b"), - () => "b" as const, - () => "a" as const + { decode: () => "b" as const, encode: () => "a" as const } ) }) .ast, diff --git a/packages/schema/test/Schema/Class.test.ts b/packages/schema/test/Schema/Class.test.ts index f53416f4b9..07cdd77b20 100644 --- a/packages/schema/test/Schema/Class.test.ts +++ b/packages/schema/test/Schema/Class.test.ts @@ -26,14 +26,16 @@ const NameString = S.string.pipe( S.nonEmpty(), S.transformOrFail( S.string, - (_, _opts, ast) => - Name.pipe( - Effect.filterOrFail( - (name) => _ === name, - () => new ParseResult.Type(ast, _, "Does not match Name") - ) - ), - (_) => ParseResult.succeed(_) + { + decode: (_, _opts, ast) => + Name.pipe( + Effect.filterOrFail( + (name) => _ === name, + () => new ParseResult.Type(ast, _, "Does not match Name") + ) + ), + encode: (_) => ParseResult.succeed(_) + } ) ) @@ -41,13 +43,15 @@ const Id = Context.GenericTag<"Id", number>("Name") const IdNumber = S.number.pipe( S.transformOrFail( S.number, - (_, _opts, ast) => - Effect.filterOrFail( - Id, - (id) => _ === id, - () => new ParseResult.Type(ast, _, "Does not match Id") - ), - (_) => ParseResult.succeed(_) + { + decode: (_, _opts, ast) => + Effect.filterOrFail( + Id, + (id) => _ === id, + () => new ParseResult.Type(ast, _, "Does not match Id") + ), + encode: (_) => ParseResult.succeed(_) + } ) ) @@ -86,34 +90,38 @@ class PersonWithTransform extends Person.transformOrFail("P { thing: Thing }, - (input, _, ast) => - input.id === 2 ? - ParseResult.fail(new ParseResult.Type(ast, input)) : - ParseResult.succeed({ - ...input, - thing: O.some({ id: 123 }) - }), - (input, _, ast) => - input.id === 2 ? - ParseResult.fail(new ParseResult.Type(ast, input)) : - ParseResult.succeed(input) + { + decode: (input, _, ast) => + input.id === 2 ? + ParseResult.fail(new ParseResult.Type(ast, input)) : + ParseResult.succeed({ + ...input, + thing: O.some({ id: 123 }) + }), + encode: (input, _, ast) => + input.id === 2 ? + ParseResult.fail(new ParseResult.Type(ast, input)) : + ParseResult.succeed(input) + } ) {} class PersonWithTransformFrom extends Person.transformOrFailFrom("PersonWithTransformFrom")( { thing: Thing }, - (input, _, ast) => - input.id === 2 ? - ParseResult.fail(new ParseResult.Type(ast, input)) : - ParseResult.succeed({ - ...input, - thing: { id: 123 } - }), - (input, _, ast) => - input.id === 2 ? - ParseResult.fail(new ParseResult.Type(ast, input)) : - ParseResult.succeed(input) + { + decode: (input, _, ast) => + input.id === 2 ? + ParseResult.fail(new ParseResult.Type(ast, input)) : + ParseResult.succeed({ + ...input, + thing: { id: 123 } + }), + encode: (input, _, ast) => + input.id === 2 ? + ParseResult.fail(new ParseResult.Type(ast, input)) : + ParseResult.succeed(input) + } ) {} describe("Schema > Class APIs", () => { diff --git a/packages/schema/test/Schema/attachPropertySignature.test.ts b/packages/schema/test/Schema/attachPropertySignature.test.ts index 2fd46af8c8..27ea557a1e 100644 --- a/packages/schema/test/Schema/attachPropertySignature.test.ts +++ b/packages/schema/test/Schema/attachPropertySignature.test.ts @@ -82,8 +82,10 @@ describe("Schema/attachPropertySignature", () => { const schema = S.transformOrFail( From, To, - (input) => ParseResult.mapError(S.decodeUnknown(To)(input), (e) => e.error), - ({ _isVisible, ...rest }) => ParseResult.succeed(rest) + { + decode: (input) => ParseResult.mapError(S.decodeUnknown(To)(input), (e) => e.error), + encode: ({ _isVisible, ...rest }) => ParseResult.succeed(rest) + } ).pipe( S.attachPropertySignature("_tag", "Circle") ) diff --git a/packages/schema/test/Schema/extend.test.ts b/packages/schema/test/Schema/extend.test.ts index 6daf7a40c3..c929f6956e 100644 --- a/packages/schema/test/Schema/extend.test.ts +++ b/packages/schema/test/Schema/extend.test.ts @@ -226,8 +226,7 @@ describe("Schema > extend", () => { const BoolFromString = S.transform( S.string, S.boolean, - (x) => !!x, - (x) => "" + x + { decode: (x) => !!x, encode: (x) => "" + x } ) it("optional, transformation", async () => { diff --git a/packages/schema/test/Schema/filter.test.ts b/packages/schema/test/Schema/filter.test.ts index ac8bc55714..d1881003dc 100644 --- a/packages/schema/test/Schema/filter.test.ts +++ b/packages/schema/test/Schema/filter.test.ts @@ -72,7 +72,7 @@ describe("Schema > filter", () => { S.filter((s): s is string => s.length === 1, { message: (issue) => { if (issue._tag === "Refinement" && issue.kind === "From") { - return TreeFormatter.formatIssue(issue.error) + return TreeFormatter.formatIssueSync(issue.error) } return `invalid ${issue.actual}` } diff --git a/packages/schema/test/Schema/mutable.test.ts b/packages/schema/test/Schema/mutable.test.ts index 6048ded9c5..018539a1b6 100644 --- a/packages/schema/test/Schema/mutable.test.ts +++ b/packages/schema/test/Schema/mutable.test.ts @@ -64,8 +64,8 @@ describe("Schema > mutable", () => { }) it("transformation", () => { - const schema = S.mutable(S.transform(S.array(S.string), S.array(S.string), identity, identity)) - if (AST.isTransform(schema.ast)) { + const schema = S.mutable(S.transform(S.array(S.string), S.array(S.string), { decode: identity, encode: identity })) + if (AST.isTransformation(schema.ast)) { expect(schema.ast.from).toEqual( new AST.TupleType([], [S.string.ast], false) ) diff --git a/packages/schema/test/Schema/optionalToRequired.test.ts b/packages/schema/test/Schema/optionalToRequired.test.ts index f7811599b5..cf312a5b53 100644 --- a/packages/schema/test/Schema/optionalToRequired.test.ts +++ b/packages/schema/test/Schema/optionalToRequired.test.ts @@ -8,8 +8,7 @@ describe("Schema > optionalToRequired", () => { const ps = S.optionalToRequired( S.NumberFromString, S.BigintFromNumber, - Option.getOrElse(() => 0), - Option.some + { decode: Option.getOrElse(() => 0), encode: Option.some } ) const schema = S.struct({ a: ps }) await Util.expectDecodeUnknownSuccess(schema, { a: "1" }, { a: 1n }) diff --git a/packages/schema/test/Schema/partial.test.ts b/packages/schema/test/Schema/partial.test.ts index 31144211c4..e642cd62f4 100644 --- a/packages/schema/test/Schema/partial.test.ts +++ b/packages/schema/test/Schema/partial.test.ts @@ -213,9 +213,10 @@ describe("Schema > partial", () => { }) it("transformations should throw", async () => { - expect(() => S.partial(S.transform(S.string, S.string, identity, identity), { exact: true })).toThrow( - new Error("`partial` cannot handle transformations") - ) + expect(() => S.partial(S.transform(S.string, S.string, { decode: identity, encode: identity }), { exact: true })) + .toThrow( + new Error("`partial` cannot handle transformations") + ) }) }) }) diff --git a/packages/schema/test/Schema/required.test.ts b/packages/schema/test/Schema/required.test.ts index d15d33fede..33a38ab016 100644 --- a/packages/schema/test/Schema/required.test.ts +++ b/packages/schema/test/Schema/required.test.ts @@ -204,7 +204,7 @@ describe("Schema > required", () => { }) it("transformations should throw", async () => { - expect(() => S.required(S.transform(S.string, S.string, identity, identity))).toThrow( + expect(() => S.required(S.transform(S.string, S.string, { decode: identity, encode: identity }))).toThrow( new Error("`required` cannot handle transformations") ) }) diff --git a/packages/schema/test/Schema/typeSchema.test.ts b/packages/schema/test/Schema/typeSchema.test.ts index 43d9068443..5e5c5b7b73 100644 --- a/packages/schema/test/Schema/typeSchema.test.ts +++ b/packages/schema/test/Schema/typeSchema.test.ts @@ -7,8 +7,7 @@ describe("Schema > typeSchema", () => { const schema = S.string.pipe( S.transform( S.tuple(S.NumberFromString, S.NumberFromString), - (s) => [s, s] as const, - ([s]) => s + { decode: (s) => [s, s] as const, encode: ([s]) => s } ), S.typeSchema ) diff --git a/packages/schema/test/TreeFormatter.test.ts b/packages/schema/test/TreeFormatter.test.ts index bfffda7198..f4cf998b90 100644 --- a/packages/schema/test/TreeFormatter.test.ts +++ b/packages/schema/test/TreeFormatter.test.ts @@ -478,13 +478,15 @@ describe("TreeFormatter", () => { const schema = S.transformOrFail( S.string.annotations({ message: () => "please enter a string" }), S.Int.annotations({ message: () => "please enter an integer" }), - (s, _, ast) => { - const n = Number(s) - return Number.isNaN(n) - ? ParseResult.fail(new ParseResult.Type(ast, s)) - : ParseResult.succeed(n) - }, - (n) => ParseResult.succeed(String(n)) + { + decode: (s, _, ast) => { + const n = Number(s) + return Number.isNaN(n) + ? ParseResult.fail(new ParseResult.Type(ast, s)) + : ParseResult.succeed(n) + }, + encode: (n) => ParseResult.succeed(String(n)) + } ).annotations({ identifier: "IntFromString", message: () => "please enter a decodeUnknownable string" @@ -635,7 +637,7 @@ describe("TreeFormatter", () => { const result = S.decodeUnknownEither(Name)("") // no service - expect(Either.mapLeft(result, (error) => Effect.runSync(TreeFormatter.formatErrorEffect(error)))) + expect(Either.mapLeft(result, (error) => Effect.runSync(TreeFormatter.formatError(error)))) .toStrictEqual(Either.left("Invalid string")) // it locale @@ -644,7 +646,7 @@ describe("TreeFormatter", () => { result, (error) => Effect.runSync( - TreeFormatter.formatErrorEffect(error).pipe(Effect.provideService(Translator, { + TreeFormatter.formatError(error).pipe(Effect.provideService(Translator, { locale: "it", translations })) @@ -658,7 +660,7 @@ describe("TreeFormatter", () => { result, (error) => Effect.runSync( - TreeFormatter.formatErrorEffect(error).pipe(Effect.provideService(Translator, { + TreeFormatter.formatError(error).pipe(Effect.provideService(Translator, { locale: "en", translations })) diff --git a/packages/schema/test/util.ts b/packages/schema/test/util.ts index 397138a567..de8ce2680f 100644 --- a/packages/schema/test/util.ts +++ b/packages/schema/test/util.ts @@ -4,7 +4,7 @@ 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 { formatError } from "@effect/schema/TreeFormatter" +import { formatErrorSync } from "@effect/schema/TreeFormatter" import * as Context from "effect/Context" import * as Duration from "effect/Duration" import * as Effect from "effect/Effect" @@ -171,15 +171,13 @@ export const identityTransform = (schema: S.Schema): S.Schema => schema export const X2 = S.transform( S.string, S.string, - (s) => s + s, - (s) => s.substring(0, s.length / 2) + { decode: (s) => s + s, encode: (s) => s.substring(0, s.length / 2) } ) export const X3 = S.transform( S.string, S.string, - (s) => s + s + s, - (s) => s.substring(0, s.length / 3) + { decode: (s) => s + s + s, encode: (s) => s.substring(0, s.length / 3) } ) const doProperty = true @@ -253,7 +251,7 @@ export const expectEffectFailure = async ( effect: Effect.Effect, message: string ) => { - expect(await Effect.runPromise(Effect.either(Effect.mapError(effect, formatError)))).toStrictEqual( + expect(await Effect.runPromise(Effect.either(Effect.mapError(effect, formatErrorSync)))).toStrictEqual( Either.left(message) ) } @@ -265,7 +263,7 @@ export const expectEffectSuccess = async (effect: Effect.Effect, a: } export const expectEitherLeft = (e: Either.Either, message: string) => { - expect(Either.mapLeft(e, formatError)).toStrictEqual(Either.left(message)) + expect(Either.mapLeft(e, formatErrorSync)).toStrictEqual(Either.left(message)) } export const expectEitherRight = (e: Either.Either, a: A) => { @@ -282,8 +280,10 @@ export const expectSome = (o: Option.Option, a: A) => { export const AsyncDeclaration = S.declare( [], - () => (u) => Effect.andThen(Effect.sleep("10 millis"), Effect.succeed(u)), - () => (u) => Effect.andThen(Effect.sleep("10 millis"), Effect.succeed(u)), + { + decode: () => (u) => Effect.andThen(Effect.sleep("10 millis"), Effect.succeed(u)), + encode: () => (u) => Effect.andThen(Effect.sleep("10 millis"), Effect.succeed(u)) + }, { identifier: "AsyncDeclaration" } @@ -296,6 +296,5 @@ const Name = Context.GenericTag<"Name", string>("Name") export const DependencyString = S.transformOrFail( S.string, S.string, - (s) => Effect.andThen(Name, s), - (s) => Effect.andThen(Name, s) + { decode: (s) => Effect.andThen(Name, s), encode: (s) => Effect.andThen(Name, s) } ).annotations({ identifier: "DependencyString" })