Skip to content

Commit

Permalink
v7.0.0 🚀
Browse files Browse the repository at this point in the history
  • Loading branch information
DZakh committed Jun 17, 2024
1 parent a2f6248 commit c0a38f2
Show file tree
Hide file tree
Showing 24 changed files with 1,471 additions and 1,038 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,7 @@ jobs:
uses: actions/upload-artifact@v3
with:
name: rescript-schema-ppx
path: packages/rescript-schema-ppx/rescript-schema-ppx-6.4.0.tgz
path: packages/rescript-schema-ppx/rescript-schema-ppx-7.0.0.tgz

benchmark:
name: Benchmark
Expand Down
56 changes: 31 additions & 25 deletions CHANGELOG_V7.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ let entitySchema = S.object(s => {

> 🧠 While the example with `s.flatten` expect an object with the type `{id: string, name: string, age: int}`, the example above and with `s.nestedField` will expect an object with the type `{id: string, data: {name: string, age: int}}`.
### Extending object field schema
### Extend field with another object schema

You can define object field multiple times to extend it with more fields:

Expand Down Expand Up @@ -114,30 +114,36 @@ Added unsafe mode for `S.json`:
- Changed payload of `Object` and `Tuple` variants in the `tagged` type
- Redesigned `Literal` module to make it more effecient

- The `Literal.t` type was renamed to `literal`, became private and changed structure. Use `S.Literal.parse` to create instances of the type
- `Literal.classify` -> `Literal.parse`
- `Literal.toText` -> `Literal.toString`. Also, started using `.toString` for `Function` literalls and removed spaces for `Dict` and `Array` literals to make them look the same as the `JSON.stringify` output
- The `S.Literal.t` type was renamed to `S.literal`, became private and changed structure. Use `S.Literal.parse` to create instances of the type
- `S.Literal.classify` -> `S.Literal.parse`
- `S.Literal.toText` -> `S.Literal.toString`. Also, started using `.toString` for `Function` literalls and removed spaces for `Dict` and `Array` literals to make them look the same as the `JSON.stringify` output

- Moved built-in refinements from nested modules to improve tree-shaking:

- `Int.min` -> `intMin`
- `Int.max` -> `intMax`
- `Int.port` -> `port`

- `Float.min` -> `floatMin`
- `Float.max` -> `floatMax`

- `Array.min` -> `arrayMin`
- `Array.max` -> `arrayMax`
- `Array.length` -> `arrayLength`

- `String.min` -> `stringMin`
- `String.max` -> `stringMax`
- `String.length` -> `stringLength`
- `String.email` -> `email`
- `String.uuid` -> `uuid`
- `String.cuid` -> `cuid`
- `String.url` -> `url`
- `String.pattern` -> `pattern`
- `String.datetime` -> `datetime`
- `String.trim` -> `trim`
- `S.Int.min` -> `S.intMin`
- `S.Int.max` -> `S.intMax`
- `S.Int.port` -> `S.port`

- `S.Float.min` -> `S.floatMin`
- `S.Float.max` -> `S.floatMax`

- `S.Array.min` -> `S.arrayMinLength`
- `S.Array.max` -> `S.arrayMaxLength`
- `S.Array.length` -> `S.arrayLength`

- `S.String.min` -> `S.stringMinLength`
- `S.String.max` -> `S.stringMaxLength`
- `S.String.length` -> `S.stringLength`
- `S.String.email` -> `S.email`
- `S.String.uuid` -> `S.uuid`
- `S.String.cuid` -> `S.cuid`
- `S.String.url` -> `S.url`
- `S.String.pattern` -> `S.pattern`
- `S.String.datetime` -> `S.datetime`
- `S.String.trim` -> `S.trim`

- `S.Int.min` -> `S.intMin`
- `S.Int.max` -> `S.intMax`

- `S.Number.max` -> `S.numberMax`/`S.integerMax`
- `S.Number.min` -> `S.numberMin`/`S.integerMin`
67 changes: 67 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,73 @@ npm run test:res
npm run test -- --watch
```

## Make comparison

https://bundlejs.com/

`rescript-schema`

```ts
import * as S from "github:DZakh/rescript-schema/artifacts/packages/artifacts/dist/S.mjs";

// Create login schema with email and password
const loginSchema = S.object({
email: S.email(S.string),
password: S.stringMinLength(S.string, 8),
});

// Infer output TypeScript type of login schema
type LoginData = S.Output<typeof loginSchema>; // { email: string; password: string }

// Throws the S.Error(`Failed parsing at ["email"]. Reason: Invalid email address`)
S.parseOrThrow(loginSchema, { email: "", password: "" });

// Returns data as { email: string; password: string }
S.parseOrThrow(loginSchema, {
email: "[email protected]",
password: "12345678",
});
```

valibot

```ts
import * as v from "valibot"; // 1.21 kB

// Create login schema with email and password
const LoginSchema = v.object({
email: v.pipe(v.string(), v.email()),
password: v.pipe(v.string(), v.minLength(8)),
});

// Infer output TypeScript type of login schema
type LoginData = v.InferOutput<typeof LoginSchema>; // { email: string; password: string }

// Throws error for `email` and `password`
v.parse(LoginSchema, { email: "", password: "" });

// Returns data as { email: string; password: string }
v.parse(LoginSchema, { email: "[email protected]", password: "12345678" });
```

zod

```ts
export * as z from "zod"; // 1.21 kB

// Create login schema with email and password
const LoginSchema = z.object({
email: z.string().email(),
password: z.string().min(8),
});

// Throws error for `email` and `password`
LoginSchema.parse({ email: "", password: "" });

// Returns data as { email: string; password: string }
LoginSchema.parse({ email: "[email protected]", password: "12345678" });
```

## License

By contributing your code to the rescript-schema GitHub repository, you agree to license your contribution under the MIT license.
2 changes: 1 addition & 1 deletion IDEAS.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ let trimContract: S.contract<string => string> = S.contract(s => {

- Move S.inline to a separate codegen module

## v7
## v8

- Change operation to include AsyncParse and simplify init functions (throw when asyncTransfor applied for SyncParse)
- Make S.serializeToJsonString super fast
Expand Down
12 changes: 6 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,12 +46,12 @@ Besides the individual bundle size, the overall size of the library is also sign

At the same time **rescript-schema** is the fastest composable validation library in the entire JavaScript ecosystem. This is achieved because of the JIT approach when an ultra optimized validator is created using `eval`.

| | rescript-schema@6.2.0 | [email protected] | Valibot@0.18.0 |
| | rescript-schema@7.0.0 | [email protected] | Valibot@0.32.0 |
| ----------------------------------------- | --------------------- | --------------- | -------------- |
| **Total size** (minified + gzipped) | 9.67 kB | 13.4 kB | 6.73 kB |
| **Example size** (minified + gzipped) | 5.53 kB | 12.8 kB | 965 B |
| **Nested object parsing** | 153,787 ops/ms | 1,177 ops/ms | 3,562 ops/ms |
| **Create schema + Nested object parsing** | 54 ops/ms | 110 ops/ms | 1,937 ops/ms |
| **Total size** (minified + gzipped) | 9.82 kB | 14.6 kB | 9.88 kB |
| **Example size** (minified + gzipped) | 4.98 kB | 12.9 kB | 1.22 B |
| **Nested object parsing** | 156,244 ops/ms | 1,304 ops/ms | 3,822 ops/ms |
| **Create schema + Nested object parsing** | 56 ops/ms | 112 ops/ms | 2,475 ops/ms |
| **Eval-free** ||||
| **Codegen-free** (Doesn't need compiler) ||||
| **Ecosystem** | ⭐️ | ⭐️⭐️⭐️⭐️⭐️ | ⭐️⭐️ |
| **Ecosystem** | ⭐️ | ⭐️⭐️⭐️⭐️⭐️ | ⭐️⭐️⭐️ |
51 changes: 26 additions & 25 deletions docs/js-usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,8 @@ import * as S from "rescript-schema";

// Create login schema with email and password
const loginSchema = S.object({
email: S.String.email(S.string),
password: S.String.min(S.string, 8),
email: S.email(S.string),
password: S.stringMinLength(S.string, 8),
});

// Infer output TypeScript type of login schema
Expand All @@ -86,7 +86,6 @@ S.string;
S.number;
S.integer; // ReScript's S.int
S.boolean;
S.json;

// empty type
S.undefined;
Expand Down Expand Up @@ -125,32 +124,32 @@ const cliArgsSchema = S.literal(["help", "lint"] as const);
**rescript-schema** includes a handful of string-specific refinements and transforms:

```ts
S.String.max(S.string, 5); // String must be 5 or fewer characters long
S.String.min(S.string, 5); // String must be 5 or more characters long
S.String.length(S.string, 5); // String must be exactly 5 characters long
S.String.email(S.string); // Invalid email address
S.String.url(S.string); // Invalid url
S.String.uuid(S.string); // Invalid UUID
S.String.cuid(S.string); // Invalid CUID
S.String.pattern(S.string, %re(`/[0-9]/`)); // Invalid
S.String.datetime(S.string); // Invalid datetime string! Must be UTC
S.stringMaxLength(S.string, 5); // String must be 5 or fewer characters long
S.stringMinLength(S.string, 5); // String must be 5 or more characters long
S.stringLength(S.string, 5); // String must be exactly 5 characters long
S.email(S.string); // Invalid email address
S.url(S.string); // Invalid url
S.uuid(S.string); // Invalid UUID
S.cuid(S.string); // Invalid CUID
S.pattern(S.string, %re(`/[0-9]/`)); // Invalid
S.datetime(S.string); // Invalid datetime string! Must be UTC

S.String.trim(S.string); // trim whitespaces
S.trim(S.string); // trim whitespaces
```

When using built-in refinements, you can provide a custom error message.

```ts
S.String.min(S.string, 1, "String can't be empty");
S.String.length(S.string, 5, "SMS code should be 5 digits long");
S.stringMinLength(S.string, 1, "String can't be empty");
S.stringLength(S.string, 5, "SMS code should be 5 digits long");
```

### ISO datetimes

The `S.String.datetime(S.string)` function has following UTC validation: no timezone offsets with arbitrary sub-second decimal precision.
The `S.datetime(S.string)` function has following UTC validation: no timezone offsets with arbitrary sub-second decimal precision.

```ts
const datetimeSchema = S.String.datetime(S.string);
const datetimeSchema = S.datetime(S.string);
// The datetimeSchema has the type S.Schema<Date, string>
// String is transformed to the Date instance

Expand All @@ -165,14 +164,14 @@ S.parseOrThrow(datetimeSchema, "2020-01-01T00:00:00+02:00"); // fail (no offsets
**rescript-schema** includes some of number-specific refinements:

```ts
S.Number.max(S.number, 5); // Number must be lower than or equal to 5
S.Number.min(S.number 5); // Number must be greater than or equal to 5
S.numberMax(S.number, 5); // Number must be lower than or equal to 5
S.numberMin(S.number 5); // Number must be greater than or equal to 5
```

Optionally, you can pass in a second argument to provide a custom error message.

```ts
S.Number.max(S.number, 5, "this👏is👏too👏big");
S.numberMax(S.number, 5, "this👏is👏too👏big");
```

## NaNs
Expand Down Expand Up @@ -335,9 +334,9 @@ const stringArraySchema = S.array(S.string);
**rescript-schema** includes some of array-specific refinements:

```ts
S.Array.max(S.array(S.string), 5); // Array must be 5 or fewer items long
S.Array.min(S.array(S.string) 5); // Array must be 5 or more items long
S.Array.length(S.array(S.string) 5); // Array must be exactly 5 items long
S.arrayMaxLength(S.array(S.string), 5); // Array must be 5 or fewer items long
S.arrayMinLength(S.array(S.string) 5); // Array must be 5 or more items long
S.arrayLength(S.array(S.string) 5); // Array must be exactly 5 items long
```

## Tuples
Expand Down Expand Up @@ -435,8 +434,10 @@ let circleSchema = S.schema(

The `S.json` schema makes sure that the value is compatible with JSON.

It accepts a boolean as an argument. If it's true, then the value will be validated as valid JSON; otherwise, it unsafely casts it to the `S.Json` type.

```ts
S.parseOrThrow(S.json, "foo"); // passes
S.parseOrThrow(S.json(true), `"foo"`); // passes
```

## JSON string
Expand Down Expand Up @@ -498,7 +499,7 @@ Also, you can have an asynchronous refinement (for parser only):
```ts
const userSchema = S.object({
id: S.asyncParserRefine(S.String.uuid(S.string), async (id, s) => {
id: S.asyncParserRefine(S.uuid(S.string), async (id, s) => {
const isActiveUser = await checkIsActiveUser(id);
if (!isActiveUser) {
s.fail(`The user ${id} is inactive.`);
Expand Down
Loading

1 comment on commit c0a38f2

@github-actions
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Benchmark

Benchmark suite Current: c0a38f2 Previous: f718dde Ratio
Parse string 818959423 ops/sec (±0.14%) 814044167 ops/sec (±0.15%) 0.99
Serialize string 820467046 ops/sec (±0.06%) 814166152 ops/sec (±0.12%) 0.99
Advanced object schema factory 459003 ops/sec (±0.63%) 457405 ops/sec (±0.90%) 1.00
Parse advanced object 45000813 ops/sec (±0.38%) 42985342 ops/sec (±0.72%) 0.96
Create and parse advanced object 34588 ops/sec (±1.15%) 34684 ops/sec (±1.48%) 1.00
Parse advanced strict object 22453639 ops/sec (±0.39%) 21540357 ops/sec (±0.57%) 0.96
Serialize advanced object 803572476 ops/sec (±0.09%) 800158749 ops/sec (±0.09%) 1.00

This comment was automatically generated by workflow using github-action-benchmark.

Please sign in to comment.