Skip to content

Commit 94a7645

Browse files
committed
TS: Powerful S.to instead of S.transform
1 parent 58c2e5c commit 94a7645

File tree

11 files changed

+375
-277
lines changed

11 files changed

+375
-277
lines changed

IDEAS.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
- Add `S.brand` for TS API
88
- Update Standard Schema error message to only include reason part
99
- Fix refinement on union schema which also uses `S.to`
10+
- TS API: Removed `S.transform` in favor of `S.to`
1011

1112
## v11
1213

packages/sury/scripts/pack/Pack.res

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -155,15 +155,14 @@ let filesMapping = [
155155
("deepStrict", "S.deepStrict"),
156156
("strip", "S.strip"),
157157
("deepStrip", "S.deepStrip"),
158-
("to", "S.to"),
158+
("to", "S.js_to"),
159159
("toJSONSchema", "S.toJSONSchema"),
160160
("fromJSONSchema", "S.fromJSONSchema"),
161161
("extendJSONSchema", "S.extendJSONSchema"),
162162
("shape", "S.shape"),
163163
("tuple", "S.tuple"),
164164
("asyncParserRefine", "S.js_asyncParserRefine"),
165165
("refine", "S.js_refine"),
166-
("transform", "S.js_transform"),
167166
("meta", "S.meta"),
168167
("toExpression", "S.toExpression"),
169168
("noValidation", "S.noValidation"),

packages/sury/scripts/pack/Pack.res.mjs

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -227,7 +227,7 @@ let filesMapping = [
227227
],
228228
[
229229
"to",
230-
"S.to"
230+
"S.js_to"
231231
],
232232
[
233233
"toJSONSchema",
@@ -257,10 +257,6 @@ let filesMapping = [
257257
"refine",
258258
"S.js_refine"
259259
],
260-
[
261-
"transform",
262-
"S.js_transform"
263-
],
264260
[
265261
"meta",
266262
"S.meta"

packages/sury/scripts/pack/Prepack.res.mjs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ var filesMapping = [
100100
["tuple", "S.tuple"],
101101
["asyncParserRefine", "S.js_asyncParserRefine"],
102102
["refine", "S.js_refine"],
103-
["transform", "S.js_transform"],
103+
["transform", "S.js_to"],
104104
["meta", "S.meta"],
105105
["toExpression", "S.toExpression"],
106106
["noValidation", "S.noValidation"],

packages/sury/src/S.d.ts

Lines changed: 28 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -96,26 +96,29 @@ export type JSON =
9696
| JSON[];
9797

9898
export type Schema<Output, Input = unknown> = {
99-
with<Transformed>(
100-
transform: (
99+
with<TargetOutput = unknown, TargetInput = unknown>(
100+
to: (
101101
schema: Schema<unknown, unknown>,
102-
parser:
103-
| ((value: unknown, s: EffectCtx<unknown, unknown>) => unknown)
104-
| undefined,
105-
serializer?: (value: unknown, s: EffectCtx<unknown, unknown>) => Input
102+
target: Schema<unknown, unknown>,
103+
decode?: ((value: unknown) => unknown) | undefined,
104+
encode?: (value: unknown) => Output
106105
) => Schema<unknown, unknown>,
107-
parser:
108-
| ((value: Output, s: EffectCtx<unknown, unknown>) => Transformed)
109-
| undefined,
110-
serializer?: (value: Transformed, s: EffectCtx<unknown, unknown>) => Input
111-
): Schema<Transformed, Input>;
112-
with(
106+
target: Schema<TargetOutput, TargetInput>,
107+
decode?: ((value: Output) => TargetInput) | undefined,
108+
encode?: (value: TargetInput) => Output
109+
): Schema<TargetOutput, Input>;
110+
// I don't know how, but it makes both S.refine and S.shape work
111+
with<Shape>(
113112
refine: (
114113
schema: Schema<unknown, unknown>,
115-
refiner: (value: unknown, s: EffectCtx<unknown, unknown>) => Promise<void>
114+
refiner:
115+
| ((value: unknown, s: EffectCtx<unknown, unknown>) => unknown)
116+
| undefined
116117
) => Schema<unknown, unknown>,
117-
refiner: (value: Output, s: EffectCtx<Output, Input>) => Promise<void>
118-
): Schema<Output, Input>;
118+
refiner:
119+
| ((value: Output, s: EffectCtx<unknown, unknown>) => Shape)
120+
| undefined
121+
): Schema<Shape, Input>;
119122
// with(message: string): t<Output, Input>; TODO: implement
120123
with<O, I>(fn: (schema: Schema<Output, Input>) => Schema<O, I>): Schema<O, I>;
121124
with<O, I, A1 extends string>(
@@ -619,14 +622,6 @@ export function refine<Output, Input>(
619622
refiner: (value: Output, s: EffectCtx<Output, Input>) => void
620623
): Schema<Output, Input>;
621624

622-
export function transform<Transformed, Output = unknown, Input = unknown>(
623-
schema: Schema<Output, Input>,
624-
parser:
625-
| ((value: Output, s: EffectCtx<unknown, unknown>) => Transformed)
626-
| undefined,
627-
serializer?: (value: Transformed, s: EffectCtx<unknown, unknown>) => Output
628-
): Schema<Transformed, Input>;
629-
630625
export const min: <Output extends string | number | unknown[], Input>(
631626
schema: Schema<Output, Input>,
632627
length: number,
@@ -726,20 +721,22 @@ export function compile<
726721
? Promise<CompileOutputMappings<Input, Output>[OutputOption]>
727722
: never;
728723

729-
export function shape<Output, Input, Shape>(
724+
export function shape<Shape = unknown, Output = unknown, Input = unknown>(
730725
schema: Schema<Output, Input>,
731726
shaper: (value: Output) => Shape
732727
): Schema<Shape, Input>;
733728

734729
export function to<
735-
FromInput,
736-
ToOutput,
737-
FromOutput = FromInput,
738-
ToInput = ToOutput
730+
Output = unknown,
731+
Input = unknown,
732+
TargetInput = unknown,
733+
TargetOutput = unknown
739734
>(
740-
from: Schema<FromOutput, FromInput>,
741-
to: Schema<ToOutput, ToInput>
742-
): Schema<ToOutput, FromInput>;
735+
schema: Schema<Output, Input>,
736+
target: Schema<TargetOutput, TargetInput>,
737+
decode?: ((value: Output) => TargetInput) | undefined,
738+
encode?: (value: TargetOutput) => Output
739+
): Schema<TargetOutput, Input>;
743740

744741
export function toJSONSchema<Output, Input>(
745742
schema: Schema<Output, Input>

packages/sury/src/S.js

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,15 +44,14 @@ export var strict = S.strict
4444
export var deepStrict = S.deepStrict
4545
export var strip = S.strip
4646
export var deepStrip = S.deepStrip
47-
export var to = S.to
47+
export var to = S.js_to
4848
export var toJSONSchema = S.toJSONSchema
4949
export var fromJSONSchema = S.fromJSONSchema
5050
export var extendJSONSchema = S.extendJSONSchema
5151
export var shape = S.shape
5252
export var tuple = S.tuple
5353
export var asyncParserRefine = S.js_asyncParserRefine
5454
export var refine = S.js_refine
55-
export var transform = S.js_transform
5655
export var meta = S.meta
5756
export var toExpression = S.toExpression
5857
export var noValidation = S.noValidation

packages/sury/src/S.resi

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -257,6 +257,7 @@ and errorCode =
257257
| OperationFailed(string)
258258
| InvalidOperation({description: string})
259259
| InvalidType({expected: schema<unknown>, received: unknown, unionErrors?: array<error>})
260+
| FailedTransformation({from: schema<unknown>, to: schema<unknown>, error: exn})
260261
| UnsupportedTransformation({from: schema<unknown>, to: schema<unknown>})
261262
| ExcessField(string)
262263
| UnexpectedAsync

packages/sury/src/Sury.res

Lines changed: 51 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -573,6 +573,7 @@ and errorCode =
573573
| OperationFailed(string)
574574
| InvalidOperation({description: string})
575575
| InvalidType({expected: schema<unknown>, received: unknown, unionErrors?: array<error>})
576+
| FailedTransformation({from: schema<unknown>, to: schema<unknown>, error: exn})
576577
| UnsupportedTransformation({from: schema<unknown>, to: schema<unknown>})
577578
| ExcessField(string)
578579
| UnexpectedAsync
@@ -829,6 +830,14 @@ Schema.prototype = sp;
829830
`Unsupported transformation from ${from->toExpression} to ${to->toExpression}`
830831
| UnexpectedAsync => "Encountered unexpected async transform or refine. Use parseAsyncOrThrow operation instead"
831832
| ExcessField(fieldName) => `Unrecognized key "${fieldName}"`
833+
| FailedTransformation(_) => {
834+
let text = %raw(`"" + reason$1.error`)
835+
if text->String.startsWith("Error: ") {
836+
text->String.slice(~start=7)
837+
} else {
838+
text
839+
}
840+
}
832841
| InvalidType({expected: schema, received, ?unionErrors}) =>
833842
let m = ref(`Expected ${schema->toExpression}, received ${received->stringify}`)
834843
switch unionErrors {
@@ -5431,19 +5440,48 @@ let js_union = values =>
54315440
values->Js.Array2.map(Schema.definitionToSchema)->(Obj.magic: array<internal> => array<'a>),
54325441
)
54335442

5434-
let js_transform = (schema, ~parser as maybeParser=?, ~serializer as maybeSerializer=?) => {
5435-
schema->transform(s => {
5436-
{
5437-
parser: ?switch maybeParser {
5438-
| Some(parser) => Some(v => parser(v, s))
5439-
| None => None
5440-
},
5441-
serializer: ?switch maybeSerializer {
5442-
| Some(serializer) => Some(v => serializer(v, s))
5443-
| None => None
5444-
},
5445-
}
5446-
})
5443+
let js_to = {
5444+
// FIXME: Test how it'll work if we have async var as input
5445+
let customBuilder = (~from, ~target, ~fn) => {
5446+
Builder.make((b, ~input, ~selfSchema as _, ~path) => {
5447+
let output = b->B.allocateVal(~schema=target)
5448+
b.code = `try{${output.inline}=${b->B.embed(fn)}(${input.inline})}catch(x){${b->B.failWithArg(
5449+
~path,
5450+
e => FailedTransformation({
5451+
from: from->castToPublic,
5452+
to: target->castToPublic,
5453+
error: e,
5454+
}),
5455+
`x`,
5456+
)}}`
5457+
output
5458+
})
5459+
}
5460+
5461+
(
5462+
schema,
5463+
target,
5464+
~decoder as maybeDecoder: option<'value => 'target>=?,
5465+
~encoder as maybeEncoder: option<'target => 'value>=?,
5466+
) => {
5467+
updateOutput(schema->castToInternal, mut => {
5468+
let target = target->castToInternal
5469+
let target = switch maybeEncoder {
5470+
| Some(fn) =>
5471+
let targetMut = target->copySchema
5472+
targetMut.serializer = Some(
5473+
customBuilder(~from=target, ~target=schema->castToInternal, ~fn),
5474+
)
5475+
targetMut
5476+
| None => target
5477+
}
5478+
mut.to = Some(target)
5479+
switch maybeDecoder {
5480+
| Some(fn) => mut.parser = Some(customBuilder(~from=schema->castToInternal, ~target, ~fn))
5481+
| None => ()
5482+
}
5483+
})
5484+
}
54475485
}
54485486

54495487
let js_refine = (schema, refiner) => {

0 commit comments

Comments
 (0)