import { bench, describe } from 'vitest'; import { STANDARD } from '../helpers/bench-options.js'; import { ok, err, Result, ntOk, ntErr, ntFromThrowable, TsOk, TsErr, OxOk, OxErr, E, pipe, TmResult, } from '../helpers/imports.js'; const validJson = '{"name":"Alice","age":30,"email":"alice@example.com"}'; const invalidJson = 'not json {{{'; // --------------------------------------------------------------------------- // JSON parsing — valid input // --------------------------------------------------------------------------- describe('Scenario: JSON parse + extract field — valid input', () => { bench('native-monad', () => { Result.from(() => JSON.parse(validJson), () => 'parse error') .map(obj => obj.name as string) .flatMap(name => (name.length > 0 ? ok(name) : err('empty name'))); }, STANDARD); bench('neverthrow', () => { ntFromThrowable(() => JSON.parse(validJson), () => 'parse error')() .map(obj => obj.name as string) .andThen(name => (name.length > 0 ? ntOk(name) : ntErr('empty name'))); }, STANDARD); bench('ts-results-es', () => { let r; try { r = TsOk(JSON.parse(validJson)); } catch { r = TsErr('parse error'); } r.map((obj: any) => obj.name as string) .andThen((name: string) => (name.length > 0 ? TsOk(name) : TsErr('empty name'))); }, STANDARD); bench('oxide.ts', () => { let r; try { r = OxOk(JSON.parse(validJson)); } catch { r = OxErr('parse error'); } r.map((obj: any) => obj.name as string) .andThen((name: string) => (name.length > 0 ? OxOk(name) : OxErr('parse error'))); }, STANDARD); bench('fp-ts', () => { pipe( E.tryCatch(() => JSON.parse(validJson), () => 'parse error'), E.map(obj => obj.name as string), E.flatMap(name => (name.length > 0 ? E.right(name) : E.left('empty name'))), ); }, STANDARD); bench('true-myth', () => { TmResult.tryOr('parse error', () => JSON.parse(validJson)) .map(obj => obj.name as string) .andThen(name => (name.length > 0 ? TmResult.ok(name) : TmResult.err('empty name'))); }, STANDARD); }); // --------------------------------------------------------------------------- // JSON parsing — invalid input // --------------------------------------------------------------------------- describe('Scenario: JSON parse + extract field — invalid input', () => { bench('native-monad', () => { Result.from(() => JSON.parse(invalidJson), () => 'parse error') .map(obj => obj.name as string) .flatMap(name => (name.length > 0 ? ok(name) : err('empty name'))); }, STANDARD); bench('neverthrow', () => { ntFromThrowable(() => JSON.parse(invalidJson), () => 'parse error')() .map(obj => obj.name as string) .andThen(name => (name.length > 0 ? ntOk(name) : ntErr('empty name'))); }, STANDARD); bench('fp-ts', () => { pipe( E.tryCatch(() => JSON.parse(invalidJson), () => 'parse error'), E.map(obj => obj.name as string), E.flatMap(name => (name.length > 0 ? E.right(name) : E.left('empty name'))), ); }, STANDARD); bench('true-myth', () => { TmResult.tryOr('parse error', () => JSON.parse(invalidJson)) .map(obj => obj.name as string) .andThen(name => (name.length > 0 ? TmResult.ok(name) : TmResult.err('empty name'))); }, STANDARD); }); // --------------------------------------------------------------------------- // Number parsing — valid // --------------------------------------------------------------------------- describe('Scenario: number parsing — valid', () => { const input = '42.5'; bench('native-monad', () => { Result.from(() => { const n = Number(input); if (Number.isNaN(n)) throw new Error('NaN'); return n; }).flatMap(n => (n > 0 ? ok(n) : err('not positive'))); }, STANDARD); bench('fp-ts', () => { pipe( E.tryCatch(() => { const n = Number(input); if (Number.isNaN(n)) throw new Error('NaN'); return n; }, () => 'parse error'), E.flatMap(n => (n > 0 ? E.right(n) : E.left('not positive'))), ); }, STANDARD); });