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(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(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(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(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(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(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(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(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(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(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(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(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); });