149 lines
6.0 KiB
TypeScript
149 lines
6.0 KiB
TypeScript
import { bench, describe } from 'vitest';
|
|
import { STANDARD } from '../helpers/bench-options.js';
|
|
import {
|
|
ok,
|
|
err,
|
|
ntOk,
|
|
ntErr,
|
|
TsOk,
|
|
TsErr,
|
|
OxOk,
|
|
OxErr,
|
|
E,
|
|
pipe,
|
|
TmResult,
|
|
} from '../helpers/imports.js';
|
|
|
|
interface User {
|
|
name: string;
|
|
email: string;
|
|
age: number;
|
|
}
|
|
|
|
const validUser: User = { name: 'Alice', email: 'alice@example.com', age: 30 };
|
|
const invalidUser: User = { name: '', email: 'alice@example.com', age: 30 };
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// 5-check validation — all pass
|
|
// ---------------------------------------------------------------------------
|
|
|
|
describe('Scenario: 5-check validation pipeline — all pass', () => {
|
|
bench('native-monad', () => {
|
|
ok<User, string>(validUser)
|
|
.flatMap(u => (u.name ? ok(u) : err('name required')))
|
|
.flatMap(u => (u.email.includes('@') ? ok(u) : err('invalid email')))
|
|
.flatMap(u => (u.age >= 18 ? ok(u) : err('must be 18+')))
|
|
.flatMap(u => (u.age <= 120 ? ok(u) : err('unrealistic age')))
|
|
.map(u => ({ ...u, validated: true }));
|
|
}, STANDARD);
|
|
|
|
bench('neverthrow', () => {
|
|
ntOk<User, string>(validUser)
|
|
.andThen(u => (u.name ? ntOk(u) : ntErr('name required')))
|
|
.andThen(u => (u.email.includes('@') ? ntOk(u) : ntErr('invalid email')))
|
|
.andThen(u => (u.age >= 18 ? ntOk(u) : ntErr('must be 18+')))
|
|
.andThen(u => (u.age <= 120 ? ntOk(u) : ntErr('unrealistic age')))
|
|
.map(u => ({ ...u, validated: true }));
|
|
}, STANDARD);
|
|
|
|
bench('ts-results-es', () => {
|
|
TsOk<User, string>(validUser)
|
|
.andThen(u => (u.name ? TsOk(u) : TsErr('name required')))
|
|
.andThen(u => (u.email.includes('@') ? TsOk(u) : TsErr('invalid email')))
|
|
.andThen(u => (u.age >= 18 ? TsOk(u) : TsErr('must be 18+')))
|
|
.andThen(u => (u.age <= 120 ? TsOk(u) : TsErr('unrealistic age')))
|
|
.map(u => ({ ...u, validated: true }));
|
|
}, STANDARD);
|
|
|
|
bench('oxide.ts', () => {
|
|
OxOk<User, string>(validUser)
|
|
.andThen(u => (u.name ? OxOk(u) : OxErr('name required')))
|
|
.andThen(u => (u.email.includes('@') ? OxOk(u) : OxErr('invalid email')))
|
|
.andThen(u => (u.age >= 18 ? OxOk(u) : OxErr('must be 18+')))
|
|
.andThen(u => (u.age <= 120 ? OxOk(u) : OxErr('unrealistic age')))
|
|
.map(u => ({ ...u, validated: true }));
|
|
}, STANDARD);
|
|
|
|
bench('fp-ts', () => {
|
|
pipe(
|
|
E.right<string, User>(validUser),
|
|
E.flatMap(u => (u.name ? E.right(u) : E.left('name required'))),
|
|
E.flatMap(u => (u.email.includes('@') ? E.right(u) : E.left('invalid email'))),
|
|
E.flatMap(u => (u.age >= 18 ? E.right(u) : E.left('must be 18+'))),
|
|
E.flatMap(u => (u.age <= 120 ? E.right(u) : E.left('unrealistic age'))),
|
|
E.map(u => ({ ...u, validated: true })),
|
|
);
|
|
}, STANDARD);
|
|
|
|
bench('true-myth', () => {
|
|
TmResult.ok<User, string>(validUser)
|
|
.andThen(u => (u.name ? TmResult.ok(u) : TmResult.err('name required')))
|
|
.andThen(u => (u.email.includes('@') ? TmResult.ok(u) : TmResult.err('invalid email')))
|
|
.andThen(u => (u.age >= 18 ? TmResult.ok(u) : TmResult.err('must be 18+')))
|
|
.andThen(u => (u.age <= 120 ? TmResult.ok(u) : TmResult.err('unrealistic age')))
|
|
.map(u => ({ ...u, validated: true }));
|
|
}, STANDARD);
|
|
});
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// 5-check validation — fails at step 1 (early exit)
|
|
// ---------------------------------------------------------------------------
|
|
|
|
describe('Scenario: 5-check validation pipeline — fails at step 1', () => {
|
|
bench('native-monad', () => {
|
|
ok<User, string>(invalidUser)
|
|
.flatMap(u => (u.name ? ok(u) : err('name required')))
|
|
.flatMap(u => (u.email.includes('@') ? ok(u) : err('invalid email')))
|
|
.flatMap(u => (u.age >= 18 ? ok(u) : err('must be 18+')))
|
|
.flatMap(u => (u.age <= 120 ? ok(u) : err('unrealistic age')))
|
|
.map(u => ({ ...u, validated: true }));
|
|
}, STANDARD);
|
|
|
|
bench('neverthrow', () => {
|
|
ntOk<User, string>(invalidUser)
|
|
.andThen(u => (u.name ? ntOk(u) : ntErr('name required')))
|
|
.andThen(u => (u.email.includes('@') ? ntOk(u) : ntErr('invalid email')))
|
|
.andThen(u => (u.age >= 18 ? ntOk(u) : ntErr('must be 18+')))
|
|
.andThen(u => (u.age <= 120 ? ntOk(u) : ntErr('unrealistic age')))
|
|
.map(u => ({ ...u, validated: true }));
|
|
}, STANDARD);
|
|
|
|
bench('ts-results-es', () => {
|
|
TsOk<User, string>(invalidUser)
|
|
.andThen(u => (u.name ? TsOk(u) : TsErr('name required')))
|
|
.andThen(u => (u.email.includes('@') ? TsOk(u) : TsErr('invalid email')))
|
|
.andThen(u => (u.age >= 18 ? TsOk(u) : TsErr('must be 18+')))
|
|
.andThen(u => (u.age <= 120 ? TsOk(u) : TsErr('unrealistic age')))
|
|
.map(u => ({ ...u, validated: true }));
|
|
}, STANDARD);
|
|
|
|
bench('oxide.ts', () => {
|
|
OxOk<User, string>(invalidUser)
|
|
.andThen(u => (u.name ? OxOk(u) : OxErr('name required')))
|
|
.andThen(u => (u.email.includes('@') ? OxOk(u) : OxErr('invalid email')))
|
|
.andThen(u => (u.age >= 18 ? OxOk(u) : OxErr('must be 18+')))
|
|
.andThen(u => (u.age <= 120 ? OxOk(u) : OxErr('unrealistic age')))
|
|
.map(u => ({ ...u, validated: true }));
|
|
}, STANDARD);
|
|
|
|
bench('fp-ts', () => {
|
|
pipe(
|
|
E.right<string, User>(invalidUser),
|
|
E.flatMap(u => (u.name ? E.right(u) : E.left('name required'))),
|
|
E.flatMap(u => (u.email.includes('@') ? E.right(u) : E.left('invalid email'))),
|
|
E.flatMap(u => (u.age >= 18 ? E.right(u) : E.left('must be 18+'))),
|
|
E.flatMap(u => (u.age <= 120 ? E.right(u) : E.left('unrealistic age'))),
|
|
E.map(u => ({ ...u, validated: true })),
|
|
);
|
|
}, STANDARD);
|
|
|
|
bench('true-myth', () => {
|
|
TmResult.ok<User, string>(invalidUser)
|
|
.andThen(u => (u.name ? TmResult.ok(u) : TmResult.err('name required')))
|
|
.andThen(u => (u.email.includes('@') ? TmResult.ok(u) : TmResult.err('invalid email')))
|
|
.andThen(u => (u.age >= 18 ? TmResult.ok(u) : TmResult.err('must be 18+')))
|
|
.andThen(u => (u.age <= 120 ? TmResult.ok(u) : TmResult.err('unrealistic age')))
|
|
.map(u => ({ ...u, validated: true }));
|
|
}, STANDARD);
|
|
});
|