diff --git a/src/all.test.ts b/src/all.test.ts index bb815b4d..b406a6c9 100644 --- a/src/all.test.ts +++ b/src/all.test.ts @@ -6,10 +6,11 @@ import { } from './test-prelude.ts' import { z } from './test-prelude.ts' -import { mdf } from './constructor.ts' +import { makeSuccessResult, mdf } from './constructor.ts' import { all } from './domain-functions.ts' import type { DomainFunction } from './types.ts' import type { Equal, Expect } from './types.test.ts' +import { makeErrorResult } from './errors.ts' describe('all', () => { it('should combine two domain functions into one', async () => { @@ -19,13 +20,10 @@ describe('all', () => { const c = all(a, b) type _R = Expect>> - assertEquals(await c({ id: 1 }), { - success: true, - data: [2, 0], - errors: [], - inputErrors: [], - environmentErrors: [], - }) + assertEquals( + await c({ id: 1 }), + makeSuccessResult<[number, number]>([2, 0]), + ) }) it('should combine many domain functions into one', async () => { @@ -36,13 +34,10 @@ describe('all', () => { type _R = Expect>> const results = await d({ id: 1 }) - assertEquals(results, { - success: true, - data: ['1', 2, true], - errors: [], - inputErrors: [], - environmentErrors: [], - }) + assertEquals( + results, + makeSuccessResult<[string, number, boolean]>(['1', 2, true]), + ) }) it('should return error when one of the domain functions has input errors', async () => { @@ -52,17 +47,14 @@ describe('all', () => { const c = all(a, b) type _R = Expect>> - assertEquals(await c({ id: 1 }), { - success: false, - inputErrors: [ - { - message: 'Expected string, received number', - path: ['id'], - }, - ], - errors: [], - environmentErrors: [], - }) + assertEquals( + await c({ id: 1 }), + makeErrorResult({ + inputErrors: [ + { message: 'Expected string, received number', path: ['id'] }, + ], + }), + ) }) it('should return error when one of the domain functions fails', async () => { @@ -74,12 +66,12 @@ describe('all', () => { const c = all(a, b) type _R = Expect>> - assertEquals(await c({ id: 1 }), { - success: false, - errors: [{ message: 'Error', exception: 'Error' }], - inputErrors: [], - environmentErrors: [], - }) + assertEquals( + await c({ id: 1 }), + makeErrorResult({ + errors: [{ message: 'Error', exception: 'Error' }], + }), + ) }) it('should combine the inputError messages of both functions', async () => { @@ -89,21 +81,15 @@ describe('all', () => { const c = all(a, b) type _R = Expect>> - assertEquals(await c({ id: 1 }), { - success: false, - inputErrors: [ - { - message: 'Expected string, received number', - path: ['id'], - }, - { - message: 'Expected string, received number', - path: ['id'], - }, - ], - environmentErrors: [], - errors: [], - }) + assertEquals( + await c({ id: 1 }), + makeErrorResult({ + inputErrors: [ + { message: 'Expected string, received number', path: ['id'] }, + { message: 'Expected string, received number', path: ['id'] }, + ], + }), + ) }) it('should combine the error messages when both functions fail', async () => { @@ -117,11 +103,11 @@ describe('all', () => { const c = all(a, b) type _R = Expect>> - assertObjectMatch(await c({ id: 1 }), { - success: false, - errors: [{ message: 'Error A' }, { message: 'Error B' }], - inputErrors: [], - environmentErrors: [], - }) + assertObjectMatch( + await c({ id: 1 }), + makeErrorResult({ + errors: [{ message: 'Error A' }, { message: 'Error B' }], + }), + ) }) }) diff --git a/src/apply-environment.test.ts b/src/apply-environment.test.ts index c35e363d..f4b57a43 100644 --- a/src/apply-environment.test.ts +++ b/src/apply-environment.test.ts @@ -1,8 +1,9 @@ import { assertEquals, describe, it } from './test-prelude.ts' import { z } from './test-prelude.ts' -import { mdf } from './constructor.ts' +import { makeSuccessResult, mdf } from './constructor.ts' import { applyEnvironment } from './domain-functions.ts' +import { makeErrorResult } from './errors.ts' describe('applyEnvironment', () => { it('fails when environment fails parser', async () => { @@ -13,17 +14,14 @@ describe('applyEnvironment', () => { 'invalid environment', ) - assertEquals(await getEnvWithEnvironment('some input'), { - success: false, - errors: [], - inputErrors: [], - environmentErrors: [ - { - message: 'Expected number, received string', - path: [], - }, - ], - }) + assertEquals( + await getEnvWithEnvironment('some input'), + makeErrorResult({ + environmentErrors: [ + { message: 'Expected number, received string', path: [] }, + ], + }), + ) }) it('should apply environment', async () => { @@ -34,13 +32,9 @@ describe('applyEnvironment', () => { 'constant environment', ) - assertEquals(await getEnvWithEnvironment('some input'), { - success: true, - data: 'constant environment', - errors: [], - inputErrors: [], - environmentErrors: [], - }) + assertEquals( + await getEnvWithEnvironment('some input'), + makeSuccessResult('constant environment'), + ) }) }) - diff --git a/src/branch.test.ts b/src/branch.test.ts index 2d53c1d2..0eede0c4 100644 --- a/src/branch.test.ts +++ b/src/branch.test.ts @@ -6,10 +6,11 @@ import { } from './test-prelude.ts' import { z } from './test-prelude.ts' -import { mdf } from './constructor.ts' +import { makeSuccessResult, mdf } from './constructor.ts' import { branch, pipe, all } from './domain-functions.ts' import type { DomainFunction } from './types.ts' import type { Equal, Expect } from './types.test.ts' +import { makeErrorResult } from './errors.ts' describe('branch', () => { it('should pipe a domain function with a function that returns a DF', async () => { @@ -21,13 +22,7 @@ describe('branch', () => { const c = branch(a, () => Promise.resolve(b)) type _R = Expect>> - assertEquals(await c({ id: 1 }), { - success: true, - data: 2, - errors: [], - inputErrors: [], - environmentErrors: [], - }) + assertEquals(await c({ id: 1 }), makeSuccessResult(2)) }) it('should enable conditionally choosing the next DF with the output of first one', async () => { @@ -40,13 +35,7 @@ describe('branch', () => { const d = branch(a, (output) => (output.next === 'multiply' ? c : b)) type _R = Expect>> - assertEquals(await d({ id: 1 }), { - success: true, - data: 6, - errors: [], - inputErrors: [], - environmentErrors: [], - }) + assertEquals(await d({ id: 1 }), makeSuccessResult(6)) }) it('should not pipe if the predicate returns null', async () => { @@ -60,13 +49,10 @@ describe('branch', () => { Equal> > - assertEquals(await d({ id: 1 }), { - success: true, - data: { id: 3, next: 'multiply' }, - errors: [], - inputErrors: [], - environmentErrors: [], - }) + assertEquals( + await d({ id: 1 }), + makeSuccessResult({ id: 3, next: 'multiply' }), + ) }) it('should use the same environment in all composed functions', async () => { @@ -84,13 +70,7 @@ describe('branch', () => { const c = branch(a, () => b) type _R = Expect>> - assertEquals(await c(undefined, { env: 1 }), { - success: true, - data: 4, - errors: [], - inputErrors: [], - environmentErrors: [], - }) + assertEquals(await c(undefined, { env: 1 }), makeSuccessResult(4)) }) it('should gracefully fail if the first function fails', async () => { @@ -101,17 +81,14 @@ describe('branch', () => { const c = branch(a, () => b) type _R = Expect>> - assertEquals(await c({ id: '1' }), { - success: false, - errors: [], - inputErrors: [ - { - path: ['id'], - message: 'Expected number, received string', - }, - ], - environmentErrors: [], - }) + assertEquals( + await c({ id: '1' }), + makeErrorResult({ + inputErrors: [ + { path: ['id'], message: 'Expected number, received string' }, + ], + }), + ) }) it('should gracefully fail if the second function fails', async () => { @@ -122,17 +99,14 @@ describe('branch', () => { const c = branch(a, () => b) type _R = Expect>> - assertEquals(await c({ id: 1 }), { - success: false, - errors: [], - inputErrors: [ - { - path: ['id'], - message: 'Expected number, received string', - }, - ], - environmentErrors: [], - }) + assertEquals( + await c({ id: 1 }), + makeErrorResult({ + inputErrors: [ + { path: ['id'], message: 'Expected number, received string' }, + ], + }), + ) }) it('should gracefully fail if the condition function fails', async () => { @@ -147,12 +121,12 @@ describe('branch', () => { }) type _R = Expect>> - assertObjectMatch(await c({ id: 1 }), { - success: false, - errors: [{ message: 'condition function failed' }], - inputErrors: [], - environmentErrors: [], - }) + assertObjectMatch( + await c({ id: 1 }), + makeErrorResult({ + errors: [{ message: 'condition function failed' }], + }), + ) }) it('should not break composition with other combinators', async () => { @@ -170,12 +144,9 @@ describe('branch', () => { ) type _R = Expect>> - assertEquals(await d({ id: 1 }), { - data: [4, { id: 3 }], - success: true, - errors: [], - inputErrors: [], - environmentErrors: [], - }) + assertEquals( + await d({ id: 1 }), + makeSuccessResult<[number, { id: number }]>([4, { id: 3 }]), + ) }) }) diff --git a/src/collect-sequence.test.ts b/src/collect-sequence.test.ts index cfb1c883..632b84bb 100644 --- a/src/collect-sequence.test.ts +++ b/src/collect-sequence.test.ts @@ -1,10 +1,11 @@ import { describe, it, assertEquals } from './test-prelude.ts' import { z } from './test-prelude.ts' -import { mdf } from './constructor.ts' +import { makeSuccessResult, mdf } from './constructor.ts' import { collectSequence } from './domain-functions.ts' import type { DomainFunction } from './types.ts' import type { Equal, Expect } from './types.test.ts' +import { makeErrorResult } from './errors.ts' describe('collectSequence', () => { it('should compose domain functions keeping the given order of keys', async () => { @@ -18,13 +19,7 @@ describe('collectSequence', () => { Equal> > - assertEquals(await c({ id: 1 }), { - success: true, - data: { a: { id: 3 }, b: 2 }, - errors: [], - inputErrors: [], - environmentErrors: [], - }) + assertEquals(await c({ id: 1 }), makeSuccessResult({ a: { id: 3 }, b: 2 })) }) it('should use the same environment in all composed functions', async () => { @@ -44,13 +39,10 @@ describe('collectSequence', () => { Equal> > - assertEquals(await c(undefined, { env: 1 }), { - success: true, - data: { a: { inp: 3 }, b: 4 }, - errors: [], - inputErrors: [], - environmentErrors: [], - }) + assertEquals( + await c(undefined, { env: 1 }), + makeSuccessResult({ a: { inp: 3 }, b: 4 }), + ) }) it('should fail on the first environment parser failure', async () => { @@ -71,12 +63,12 @@ describe('collectSequence', () => { Equal> > - assertEquals(await c(undefined, {}), { - success: false, - errors: [], - inputErrors: [], - environmentErrors: [{ message: 'Required', path: ['env'] }], - }) + assertEquals( + await c(undefined, {}), + makeErrorResult({ + environmentErrors: [{ message: 'Required', path: ['env'] }], + }), + ) }) it('should fail on the first input parser failure', async () => { @@ -98,14 +90,14 @@ describe('collectSequence', () => { Equal> > - assertEquals(await c({ inp: 'some invalid input' }, { env: 1 }), { - success: false, - errors: [], - inputErrors: [ - { message: 'Expected undefined, received object', path: [] }, - ], - environmentErrors: [], - }) + assertEquals( + await c({ inp: 'some invalid input' }, { env: 1 }), + makeErrorResult({ + inputErrors: [ + { message: 'Expected undefined, received object', path: [] }, + ], + }), + ) }) it('should fail on the second input parser failure', async () => { @@ -125,14 +117,14 @@ describe('collectSequence', () => { Equal> > - assertEquals(await c(undefined, { env: 1 }), { - success: false, - errors: [], - inputErrors: [ - { message: 'Expected number, received string', path: ['inp'] }, - ], - environmentErrors: [], - }) + assertEquals( + await c(undefined, { env: 1 }), + makeErrorResult({ + inputErrors: [ + { message: 'Expected number, received string', path: ['inp'] }, + ], + }), + ) }) it('should compose more than 2 functions', async () => { @@ -158,12 +150,13 @@ describe('collectSequence', () => { > > - assertEquals(await d({ aNumber: 1 }), { - success: true, - data: { a: { aString: '1' }, b: { aBoolean: true }, c: false }, - errors: [], - inputErrors: [], - environmentErrors: [], - }) + assertEquals( + await d({ aNumber: 1 }), + makeSuccessResult({ + a: { aString: '1' }, + b: { aBoolean: true }, + c: false, + }), + ) }) }) diff --git a/src/collect.test.ts b/src/collect.test.ts index 418017ef..bf6abbab 100644 --- a/src/collect.test.ts +++ b/src/collect.test.ts @@ -6,10 +6,11 @@ import { } from './test-prelude.ts' import { z } from './test-prelude.ts' -import { mdf } from './constructor.ts' +import { makeSuccessResult, mdf } from './constructor.ts' import { collect } from './domain-functions.ts' import type { DomainFunction } from './types.ts' import type { Equal, Expect } from './types.test.ts' +import { makeErrorResult } from './errors.ts' describe('collect', () => { it('should combine an object of domain functions', async () => { @@ -19,13 +20,7 @@ describe('collect', () => { const c = collect({ a, b }) type _R = Expect>> - assertEquals(await c({ id: 1 }), { - success: true, - data: { a: 2, b: 0 }, - errors: [], - inputErrors: [], - environmentErrors: [], - }) + assertEquals(await c({ id: 1 }), makeSuccessResult({ a: 2, b: 0 })) }) it('should return error when one of the domain functions has input errors', async () => { @@ -35,17 +30,14 @@ describe('collect', () => { const c = collect({ a, b }) type _R = Expect>> - assertEquals(await c({ id: 1 }), { - success: false, - inputErrors: [ - { - message: 'Expected string, received number', - path: ['id'], - }, - ], - errors: [], - environmentErrors: [], - }) + assertEquals( + await c({ id: 1 }), + makeErrorResult({ + inputErrors: [ + { message: 'Expected string, received number', path: ['id'] }, + ], + }), + ) }) it('should return error when one of the domain functions fails', async () => { @@ -57,12 +49,12 @@ describe('collect', () => { const c = collect({ a, b }) type _R = Expect>> - assertEquals(await c({ id: 1 }), { - success: false, - errors: [{ message: 'Error', exception: 'Error' }], - inputErrors: [], - environmentErrors: [], - }) + assertEquals( + await c({ id: 1 }), + makeErrorResult({ + errors: [{ message: 'Error', exception: 'Error' }], + }), + ) }) it('should combine the inputError messages of both functions', async () => { @@ -72,21 +64,21 @@ describe('collect', () => { const c = collect({ a, b }) type _R = Expect>> - assertEquals(await c({ id: 1 }), { - success: false, - inputErrors: [ - { - message: 'Expected string, received number', - path: ['id'], - }, - { - message: 'Expected string, received number', - path: ['id'], - }, - ], - environmentErrors: [], - errors: [], - }) + assertEquals( + await c({ id: 1 }), + makeErrorResult({ + inputErrors: [ + { + message: 'Expected string, received number', + path: ['id'], + }, + { + message: 'Expected string, received number', + path: ['id'], + }, + ], + }), + ) }) it('should combine the error messages when both functions fail', async () => { @@ -100,14 +92,14 @@ describe('collect', () => { const c = collect({ a, b }) type _R = Expect>> - assertObjectMatch(await c({ id: 1 }), { - success: false, - errors: [ - { message: 'Error A', exception: { message: 'Error A' } }, - { message: 'Error B', exception: { message: 'Error B' } }, - ], - inputErrors: [], - environmentErrors: [], - }) + assertObjectMatch( + await c({ id: 1 }), + makeErrorResult({ + errors: [ + { message: 'Error A', exception: { message: 'Error A' } }, + { message: 'Error B', exception: { message: 'Error B' } }, + ], + }), + ) }) }) diff --git a/src/constructor.test.ts b/src/constructor.test.ts index 44f60d28..f022d56a 100644 --- a/src/constructor.test.ts +++ b/src/constructor.test.ts @@ -6,12 +6,13 @@ import { } from './test-prelude.ts' import { z } from './test-prelude.ts' -import { mdf, toComposable } from './constructor.ts' +import { makeSuccessResult, mdf, toComposable } from './constructor.ts' import { EnvironmentError, InputError, InputErrors, ResultError, + makeErrorResult, } from './errors.ts' import type { DomainFunction, SuccessResult } from './types.ts' import type { Equal, Expect } from './types.test.ts' @@ -58,25 +59,19 @@ describe('makeDomainFunction', () => { const handler = mdf()(() => 'no input!') type _R = Expect>> - assertEquals(await handler(), { - success: true, - data: 'no input!', - errors: [], - inputErrors: [], - environmentErrors: [], - }) + assertEquals(await handler(), makeSuccessResult('no input!')) }) it('fails gracefully if gets something other than undefined', async () => { const handler = mdf()(() => 'no input!') type _R = Expect>> - assertEquals(await handler('some input'), { - success: false, - errors: [], - inputErrors: [{ path: [], message: 'Expected undefined' }], - environmentErrors: [], - }) + assertEquals( + await handler('some input'), + makeErrorResult({ + inputErrors: [{ path: [], message: 'Expected undefined' }], + }), + ) }) }) @@ -87,25 +82,19 @@ describe('makeDomainFunction', () => { const handler = mdf(parser)(({ id }) => id) type _R = Expect>> - assertEquals(await handler({ id: '1' }), { - success: true, - data: 1, - errors: [], - inputErrors: [], - environmentErrors: [], - }) + assertEquals(await handler({ id: '1' }), makeSuccessResult(1)) }) it('fails gracefully if gets something other than empty record', async () => { const handler = mdf()(() => 'no input!') type _R = Expect>> - assertEquals(await handler(undefined, ''), { - success: false, - errors: [], - inputErrors: [], - environmentErrors: [{ path: [], message: 'Expected an object' }], - }) + assertEquals( + await handler(undefined, ''), + makeErrorResult({ + environmentErrors: [{ path: [], message: 'Expected an object' }], + }), + ) }) it('returns error when parsing fails', async () => { @@ -113,14 +102,14 @@ describe('makeDomainFunction', () => { const handler = mdf(parser)(({ id }) => id) type _R = Expect>> - assertEquals(await handler({ missingId: '1' }), { - success: false, - inputErrors: [ - { message: 'Expected number, received nan', path: ['id'] }, - ], - environmentErrors: [], - errors: [], - }) + assertEquals( + await handler({ missingId: '1' }), + makeErrorResult({ + inputErrors: [ + { message: 'Expected number, received nan', path: ['id'] }, + ], + }), + ) }) }) @@ -136,13 +125,10 @@ describe('makeDomainFunction', () => { Equal> > - assertEquals(await handler({ id: '1' }, { uid: '2' }), { - success: true, - data: [1, 2], - errors: [], - inputErrors: [], - environmentErrors: [], - }) + assertEquals( + await handler({ id: '1' }, { uid: '2' }), + makeSuccessResult([1, 2]), + ) }) it('applies async validations', async () => { @@ -161,12 +147,13 @@ describe('makeDomainFunction', () => { const handler = mdf(parser, envParser)(({ id }, { uid }) => [id, uid]) type _R = Expect>> - assertEquals(await handler({ id: '1' }, { uid: '2' }), { - success: false, - errors: [], - inputErrors: [{ message: 'ID already taken', path: ['id'] }], - environmentErrors: [{ message: 'UID already taken', path: ['uid'] }], - }) + assertEquals( + await handler({ id: '1' }, { uid: '2' }), + makeErrorResult({ + inputErrors: [{ message: 'ID already taken', path: ['id'] }], + environmentErrors: [{ message: 'UID already taken', path: ['uid'] }], + }), + ) }) it('accepts literals as input of domain functions', async () => { @@ -192,14 +179,14 @@ describe('makeDomainFunction', () => { const handler = mdf(parser, envParser)(({ id }, { uid }) => [id, uid]) type _R = Expect>> - assertEquals(await handler({ id: '1' }, {}), { - success: false, - inputErrors: [], - environmentErrors: [ - { message: 'Expected number, received nan', path: ['uid'] }, - ], - errors: [], - }) + assertEquals( + await handler({ id: '1' }, {}), + makeErrorResult({ + environmentErrors: [ + { message: 'Expected number, received nan', path: ['uid'] }, + ], + }), + ) }) it('returns error when the domain function throws an Error', async () => { @@ -208,12 +195,12 @@ describe('makeDomainFunction', () => { }) type _R = Expect>> - assertObjectMatch(await handler({ id: 1 }), { - success: false, - errors: [{ message: 'Error' }], - inputErrors: [], - environmentErrors: [], - }) + assertObjectMatch( + await handler({ id: 1 }), + makeErrorResult({ + errors: [{ message: 'Error' }], + }), + ) }) it('preserves entire original exception when the domain function throws an Error', async () => { @@ -222,20 +209,20 @@ describe('makeDomainFunction', () => { }) type _R = Expect>> - assertObjectMatch(await handler({ id: 1 }), { - success: false, - errors: [ - { - message: 'Some message', - exception: { + assertObjectMatch( + await handler({ id: 1 }), + makeErrorResult({ + errors: [ + { message: 'Some message', - cause: { someUnknownFields: true }, + exception: { + message: 'Some message', + cause: { someUnknownFields: true }, + }, }, - }, - ], - inputErrors: [], - environmentErrors: [], - }) + ], + }), + ) }) it('returns error when the domain function throws a string', async () => { @@ -244,12 +231,12 @@ describe('makeDomainFunction', () => { }) type _R = Expect>> - assertObjectMatch(await handler({ id: 1 }), { - success: false, - errors: [{ message: 'Error', exception: 'Error' }], - inputErrors: [], - environmentErrors: [], - }) + assertObjectMatch( + await handler({ id: 1 }), + makeErrorResult({ + errors: [{ message: 'Error', exception: 'Error' }], + }), + ) }) it('returns error when the domain function throws an object with message', async () => { @@ -258,12 +245,12 @@ describe('makeDomainFunction', () => { }) type _R = Expect>> - assertEquals(await handler({ id: 1 }), { - success: false, - errors: [{ message: 'Error', exception: { message: 'Error' } }], - inputErrors: [], - environmentErrors: [], - }) + assertEquals( + await handler({ id: 1 }), + makeErrorResult({ + errors: [{ message: 'Error', exception: { message: 'Error' } }], + }), + ) }) it('returns inputErrors when the domain function throws an InputError', async () => { @@ -272,12 +259,14 @@ describe('makeDomainFunction', () => { }) type _R = Expect>> - assertEquals(await handler({ id: 1 }), { - success: false, - errors: [], - inputErrors: [{ message: 'Custom input error', path: ['contact', 'id'] }], - environmentErrors: [], - }) + assertEquals( + await handler({ id: 1 }), + makeErrorResult({ + inputErrors: [ + { message: 'Custom input error', path: ['contact', 'id'] }, + ], + }), + ) }) it('returns multiple inputErrors when the domain function throws an InputErrors', async () => { @@ -289,15 +278,15 @@ describe('makeDomainFunction', () => { }) type _R = Expect>> - assertEquals(await handler({ id: 1 }), { - success: false, - errors: [], - inputErrors: [ - { message: 'Custom input error', path: ['contact', 'id'] }, - { message: 'Another input error', path: ['contact', 'id'] }, - ], - environmentErrors: [], - }) + assertEquals( + await handler({ id: 1 }), + makeErrorResult({ + inputErrors: [ + { message: 'Custom input error', path: ['contact', 'id'] }, + { message: 'Another input error', path: ['contact', 'id'] }, + ], + }), + ) }) it('returns environmentErrors when the domain function throws an EnvironmentError', async () => { @@ -306,14 +295,14 @@ describe('makeDomainFunction', () => { }) type _R = Expect>> - assertEquals(await handler({ id: 1 }), { - success: false, - errors: [], - inputErrors: [], - environmentErrors: [ - { message: 'Custom env error', path: ['currentUser', 'role'] }, - ], - }) + assertEquals( + await handler({ id: 1 }), + makeErrorResult({ + environmentErrors: [ + { message: 'Custom env error', path: ['currentUser', 'role'] }, + ], + }), + ) }) it('returns an error result when the domain function throws an ResultError', async () => { @@ -328,12 +317,13 @@ describe('makeDomainFunction', () => { }) type _R = Expect>> - assertEquals(await handler({ id: 1 }), { - success: false, - errors: [], - inputErrors: [{ message: 'Custom input error', path: ['contact', 'id'] }], - environmentErrors: [], - }) + assertEquals( + await handler({ id: 1 }), + makeErrorResult({ + inputErrors: [ + { message: 'Custom input error', path: ['contact', 'id'] }, + ], + }), + ) }) }) - diff --git a/src/constructor.ts b/src/constructor.ts index 3fdf82c9..f38d24df 100644 --- a/src/constructor.ts +++ b/src/constructor.ts @@ -1,20 +1,33 @@ -import { errorResultToFailure, failureToErrorResult } from './errors.ts' +import { + errorResultToFailure, + failureToErrorResult, + makeErrorResult, +} from './errors.ts' import type { DomainFunction, ParserIssue, ParserSchema, SchemaError, + SuccessResult, } from './types.ts' import { Composable } from './composable/index.ts' import { composable } from './composable/composable.ts' +function makeSuccessResult(data: T): SuccessResult { + return { + success: true, + data, + inputErrors: [], + errors: [], + environmentErrors: [], + } +} + function dfResultFromcomposable(fn: T) { return (async (...args) => { const r = await fn(...args) - return r.success - ? { ...r, inputErrors: [], environmentErrors: [] } - : failureToErrorResult(r) + return r.success ? makeSuccessResult(r.data) : failureToErrorResult(r) }) as Composable<(...args: Parameters) => R> } @@ -73,16 +86,14 @@ function fromComposable( const result = await (inputSchema ?? undefinedSchema).safeParseAsync(input) if (!result.success || !envResult.success) { - return { - success: false, - errors: [], + return makeErrorResult({ inputErrors: result.success ? [] : formatSchemaErrors(result.error.issues), environmentErrors: envResult.success ? [] : formatSchemaErrors(envResult.error.issues), - } + }) } return dfResultFromcomposable(fn)( ...([result.data as I, envResult.data as E] as Parameters), @@ -121,5 +132,5 @@ export { makeDomainFunction, makeDomainFunction as mdf, toComposable, + makeSuccessResult, } - diff --git a/src/errors.ts b/src/errors.ts index d06b7d59..a94618fe 100644 --- a/src/errors.ts +++ b/src/errors.ts @@ -88,13 +88,7 @@ class ResultError extends Error { constructor(result: AtLeastOne) { super('ResultError') this.name = 'ResultError' - this.result = { - errors: [], - inputErrors: [], - environmentErrors: [], - ...result, - success: false, - } + this.result = makeErrorResult(result) } } @@ -119,8 +113,7 @@ function errorResultToFailure({ } function failureToErrorResult({ errors }: Failure): ErrorResult { - return { - success: false, + return makeErrorResult({ errors: errors .filter( ({ exception }) => @@ -162,7 +155,17 @@ function failureToErrorResult({ errors }: Failure): ErrorResult { ? exception.result.environmentErrors : [], ), - } + }) +} + +function makeErrorResult(errorData: AtLeastOne) { + return { + success: false, + errors: [], + inputErrors: [], + environmentErrors: [], + ...errorData, + } as ErrorResult } export { @@ -174,5 +177,5 @@ export { InputErrors, ResultError, schemaError, + makeErrorResult, } - diff --git a/src/first.test.ts b/src/first.test.ts index c4b39084..3ac21559 100644 --- a/src/first.test.ts +++ b/src/first.test.ts @@ -1,10 +1,11 @@ import { describe, it, assertEquals } from './test-prelude.ts' import { z } from './test-prelude.ts' -import { mdf } from './constructor.ts' +import { makeSuccessResult, mdf } from './constructor.ts' import { first } from './domain-functions.ts' import type { DomainFunction } from './types.ts' import type { Equal, Expect } from './types.test.ts' +import { makeErrorResult } from './errors.ts' describe('first', () => { it('should return the result of the first successful domain function', async () => { @@ -15,13 +16,7 @@ describe('first', () => { type _R = Expect>> const results = await d({ id: 1 }) - assertEquals(results, { - success: true, - data: '1', - errors: [], - inputErrors: [], - environmentErrors: [], - }) + assertEquals(results, makeSuccessResult('1')) }) it('should return a successful result even if one of the domain functions fails', async () => { @@ -35,13 +30,10 @@ describe('first', () => { const c = first(a, b) type _R = Expect>> - assertEquals(await c({ n: 1, operation: 'increment' }), { - success: true, - data: 2, - inputErrors: [], - errors: [], - environmentErrors: [], - }) + assertEquals( + await c({ n: 1, operation: 'increment' }), + makeSuccessResult(2), + ) }) it('should return error when all of the domain functions fails', async () => { @@ -53,16 +45,14 @@ describe('first', () => { const c = first(a, b) type _R = Expect>> - assertEquals(await c({ id: 1 }), { - success: false, - errors: [{ message: 'Error', exception: 'Error' }], - inputErrors: [ - { - message: 'Expected string, received number', - path: ['id'], - }, - ], - environmentErrors: [], - }) + assertEquals( + await c({ id: 1 }), + makeErrorResult({ + errors: [{ message: 'Error', exception: 'Error' }], + inputErrors: [ + { message: 'Expected string, received number', path: ['id'] }, + ], + }), + ) }) }) diff --git a/src/index.ts b/src/index.ts index 134dd00f..896fa15d 100644 --- a/src/index.ts +++ b/src/index.ts @@ -3,6 +3,7 @@ export { makeDomainFunction, mdf, toComposable, + makeSuccessResult, } from './constructor.ts' export * from './domain-functions.ts' export * from './input-resolvers.ts' @@ -32,4 +33,3 @@ export type { UnpackResult, UnpackSuccess, } from './types.ts' - diff --git a/src/map-error.test.ts b/src/map-error.test.ts index 7f50948f..21e3b3ea 100644 --- a/src/map-error.test.ts +++ b/src/map-error.test.ts @@ -1,10 +1,11 @@ import { describe, it, assertEquals } from './test-prelude.ts' import { z } from './test-prelude.ts' -import { mdf } from './constructor.ts' +import { makeSuccessResult, mdf } from './constructor.ts' import { mapError } from './domain-functions.ts' import type { DomainFunction, ErrorData } from './types.ts' import type { Equal, Expect } from './types.test.ts' +import { makeErrorResult } from './errors.ts' describe('mapError', () => { it('returns the result when the domain function suceeds', async () => { @@ -13,18 +14,12 @@ describe('mapError', () => { ({ errors: [{ message: 'New Error Message' }], inputErrors: [{ message: 'New Input Error Message' }], - }) as ErrorData + } as ErrorData) const c = mapError(a, b) type _R = Expect>> - assertEquals(await c({ id: 1 }), { - success: true, - data: 2, - errors: [], - inputErrors: [], - environmentErrors: [], - }) + assertEquals(await c({ id: 1 }), makeSuccessResult(2)) }) it('returns a domain function function that will apply a function over the error of the first one', async () => { @@ -39,17 +34,18 @@ describe('mapError', () => { path: [], }, ], - }) as ErrorData + } as ErrorData) const c = mapError(a, b) type _R = Expect>> - assertEquals(await c({ invalidInput: '1' }), { - success: false, - errors: [{ message: 'Number of errors: 0' }], - environmentErrors: [], - inputErrors: [{ message: 'Number of input errors: 1', path: [] }], - }) + assertEquals( + await c({ invalidInput: '1' }), + makeErrorResult({ + errors: [{ message: 'Number of errors: 0' }], + inputErrors: [{ message: 'Number of input errors: 1', path: [] }], + }), + ) }) it('returns a domain function function that will apply an async function over the error of the first one', async () => { @@ -69,12 +65,13 @@ describe('mapError', () => { const c = mapError(a, b) type _R = Expect>> - assertEquals(await c({ invalidInput: '1' }), { - success: false, - errors: [{ message: 'Number of errors: 0' }], - environmentErrors: [], - inputErrors: [{ message: 'Number of input errors: 1', path: [] }], - }) + assertEquals( + await c({ invalidInput: '1' }), + makeErrorResult({ + errors: [{ message: 'Number of errors: 0' }], + inputErrors: [{ message: 'Number of input errors: 1', path: [] }], + }), + ) }) it('returns the error when the mapping function fails', async () => { @@ -86,11 +83,11 @@ describe('mapError', () => { const c = mapError(a, b) type _R = Expect>> - assertEquals(await c({ invalidInput: '1' }), { - success: false, - errors: [{ message: 'failed to map', exception: 'failed to map' }], - inputErrors: [], - environmentErrors: [], - }) + assertEquals( + await c({ invalidInput: '1' }), + makeErrorResult({ + errors: [{ message: 'failed to map', exception: 'failed to map' }], + }), + ) }) }) diff --git a/src/map.test.ts b/src/map.test.ts index 9e64986f..ea252afd 100644 --- a/src/map.test.ts +++ b/src/map.test.ts @@ -1,10 +1,11 @@ import { describe, it, assertEquals } from './test-prelude.ts' import { z } from './test-prelude.ts' -import { mdf } from './constructor.ts' +import { makeSuccessResult, mdf } from './constructor.ts' import { map } from './domain-functions.ts' import type { DomainFunction } from './types.ts' import type { Equal, Expect } from './types.test.ts' +import { makeErrorResult } from './errors.ts' describe('map', () => { it('returns a domain function function that will apply a function over the results of the first one', async () => { @@ -14,13 +15,7 @@ describe('map', () => { const c = map(a, b) type _R = Expect>> - assertEquals(await c({ id: 1 }), { - success: true, - data: 3, - errors: [], - inputErrors: [], - environmentErrors: [], - }) + assertEquals(await c({ id: 1 }), makeSuccessResult(3)) }) it('returns a domain function function that will apply an async function over the results of the first one', async () => { @@ -30,13 +25,7 @@ describe('map', () => { const c = map(a, b) type _R = Expect>> - assertEquals(await c({ id: 1 }), { - success: true, - data: 3, - errors: [], - inputErrors: [], - environmentErrors: [], - }) + assertEquals(await c({ id: 1 }), makeSuccessResult(3)) }) it('returns the error when the domain function fails', async () => { @@ -47,12 +36,12 @@ describe('map', () => { const c = map(a, b) type _R = Expect>> - assertEquals(await c({ invalidInput: '1' }), { - success: false, - errors: [], - inputErrors: [{ message: 'Required', path: ['id'] }], - environmentErrors: [], - }) + assertEquals( + await c({ invalidInput: '1' }), + makeErrorResult({ + inputErrors: [{ message: 'Required', path: ['id'] }], + }), + ) }) it('returns the error when the mapping function fails', async () => { @@ -64,11 +53,11 @@ describe('map', () => { const c = map(a, b) type _R = Expect>> - assertEquals(await c({ id: 1 }), { - success: false, - errors: [{ message: 'failed to map', exception: 'failed to map' }], - inputErrors: [], - environmentErrors: [], - }) + assertEquals( + await c({ id: 1 }), + makeErrorResult({ + errors: [{ message: 'failed to map', exception: 'failed to map' }], + }), + ) }) }) diff --git a/src/merge.test.ts b/src/merge.test.ts index be0414d7..90bcd5d5 100644 --- a/src/merge.test.ts +++ b/src/merge.test.ts @@ -6,10 +6,11 @@ import { } from './test-prelude.ts' import { z } from './test-prelude.ts' -import { mdf } from './constructor.ts' +import { makeSuccessResult, mdf } from './constructor.ts' import { merge } from './domain-functions.ts' import type { DomainFunction } from './types.ts' import type { Equal, Expect } from './types.test.ts' +import { makeErrorResult } from './errors.ts' describe('merge', () => { it('should combine two domain functions results into one object', async () => { @@ -25,13 +26,10 @@ describe('merge', () => { Equal> > - assertEquals(await c({ id: 1 }), { - success: true, - data: { resultA: 2, resultB: 0 }, - errors: [], - inputErrors: [], - environmentErrors: [], - }) + assertEquals( + await c({ id: 1 }), + makeSuccessResult({ resultA: 2, resultB: 0 }), + ) }) it('should combine many domain functions into one', async () => { @@ -59,17 +57,10 @@ describe('merge', () => { > const results = await d({ id: 1 }) - assertEquals(results, { - success: true, - data: { - resultA: '1', - resultB: 2, - resultC: true, - }, - errors: [], - inputErrors: [], - environmentErrors: [], - }) + assertEquals( + results, + makeSuccessResult({ resultA: '1', resultB: 2, resultC: true }), + ) }) it('should return error when one of the domain functions has input errors', async () => { @@ -90,17 +81,14 @@ describe('merge', () => { > > - assertEquals(await c({ id: 1 }), { - success: false, - inputErrors: [ - { - message: 'Expected string, received number', - path: ['id'], - }, - ], - errors: [], - environmentErrors: [], - }) + assertEquals( + await c({ id: 1 }), + makeErrorResult({ + inputErrors: [ + { message: 'Expected string, received number', path: ['id'] }, + ], + }), + ) }) it('should return error when one of the domain functions fails', async () => { @@ -114,12 +102,12 @@ describe('merge', () => { const c: DomainFunction = merge(a, b) type _R = Expect>> - assertEquals(await c({ id: 1 }), { - success: false, - errors: [{ message: 'Error', exception: 'Error' }], - inputErrors: [], - environmentErrors: [], - }) + assertEquals( + await c({ id: 1 }), + makeErrorResult({ + errors: [{ message: 'Error', exception: 'Error' }], + }), + ) }) it('should combine the inputError messages of both functions', async () => { @@ -135,21 +123,15 @@ describe('merge', () => { Equal> > - assertEquals(await c({ id: 1 }), { - success: false, - inputErrors: [ - { - message: 'Expected string, received number', - path: ['id'], - }, - { - message: 'Expected string, received number', - path: ['id'], - }, - ], - environmentErrors: [], - errors: [], - }) + assertEquals( + await c({ id: 1 }), + makeErrorResult({ + inputErrors: [ + { message: 'Expected string, received number', path: ['id'] }, + { message: 'Expected string, received number', path: ['id'] }, + ], + }), + ) }) it('should combine the error messages when both functions fail', async () => { @@ -163,14 +145,14 @@ describe('merge', () => { const c = merge(a, b) type _R = Expect>> - assertObjectMatch(await c({ id: 1 }), { - success: false, - errors: [ - { message: 'Error A', exception: { message: 'Error A' } }, - { message: 'Error B', exception: { message: 'Error B' } }, - ], - inputErrors: [], - environmentErrors: [], - }) + assertObjectMatch( + await c({ id: 1 }), + makeErrorResult({ + errors: [ + { message: 'Error A', exception: { message: 'Error A' } }, + { message: 'Error B', exception: { message: 'Error B' } }, + ], + }), + ) }) }) diff --git a/src/pipe.test.ts b/src/pipe.test.ts index 756f0777..d9bfa5ab 100644 --- a/src/pipe.test.ts +++ b/src/pipe.test.ts @@ -1,10 +1,11 @@ import { describe, it, assertEquals } from './test-prelude.ts' import { z } from './test-prelude.ts' -import { mdf } from './constructor.ts' +import { makeSuccessResult, mdf } from './constructor.ts' import { pipe } from './domain-functions.ts' import type { DomainFunction } from './types.ts' import type { Equal, Expect } from './types.test.ts' +import { makeErrorResult } from './errors.ts' describe('pipe', () => { it('should compose domain functions from left-to-right', async () => { @@ -16,13 +17,7 @@ describe('pipe', () => { const c = pipe(a, b) type _R = Expect>> - assertEquals(await c({ id: 1 }), { - success: true, - data: 2, - errors: [], - inputErrors: [], - environmentErrors: [], - }) + assertEquals(await c({ id: 1 }), makeSuccessResult(2)) }) it('should use the same environment in all composed functions', async () => { @@ -40,13 +35,7 @@ describe('pipe', () => { const c = pipe(a, b) type _R = Expect>> - assertEquals(await c(undefined, { env: 1 }), { - success: true, - data: 4, - errors: [], - inputErrors: [], - environmentErrors: [], - }) + assertEquals(await c(undefined, { env: 1 }), makeSuccessResult(4)) }) it('should fail on the first environment parser failure', async () => { @@ -65,12 +54,12 @@ describe('pipe', () => { const c = pipe(a, b) type _R = Expect>> - assertEquals(await c(undefined, {}), { - success: false, - errors: [], - inputErrors: [], - environmentErrors: [{ message: 'Required', path: ['env'] }], - }) + assertEquals( + await c(undefined, {}), + makeErrorResult({ + environmentErrors: [{ message: 'Required', path: ['env'] }], + }), + ) }) it('should fail on the first input parser failure', async () => { @@ -90,14 +79,14 @@ describe('pipe', () => { const c = pipe(a, b) type _R = Expect>> - assertEquals(await c({ inp: 'some invalid input' }, { env: 1 }), { - success: false, - errors: [], - inputErrors: [ - { message: 'Expected undefined, received object', path: [] }, - ], - environmentErrors: [], - }) + assertEquals( + await c({ inp: 'some invalid input' }, { env: 1 }), + makeErrorResult({ + inputErrors: [ + { message: 'Expected undefined, received object', path: [] }, + ], + }), + ) }) it('should fail on the second input parser failure', async () => { @@ -115,14 +104,14 @@ describe('pipe', () => { const c = pipe(a, b) type _R = Expect>> - assertEquals(await c(undefined, { env: 1 }), { - success: false, - errors: [], - inputErrors: [ - { message: 'Expected number, received string', path: ['inp'] }, - ], - environmentErrors: [], - }) + assertEquals( + await c(undefined, { env: 1 }), + makeErrorResult({ + inputErrors: [ + { message: 'Expected number, received string', path: ['inp'] }, + ], + }), + ) }) it('should compose more than 2 functions', async () => { @@ -139,12 +128,6 @@ describe('pipe', () => { const d = pipe(a, b, c) type _R = Expect>> - assertEquals(await d({ aNumber: 1 }), { - success: true, - data: false, - errors: [], - inputErrors: [], - environmentErrors: [], - }) + assertEquals(await d({ aNumber: 1 }), makeSuccessResult(false)) }) }) diff --git a/src/sequence.test.ts b/src/sequence.test.ts index e6357426..3806f259 100644 --- a/src/sequence.test.ts +++ b/src/sequence.test.ts @@ -1,10 +1,11 @@ import { describe, it, assertEquals } from './test-prelude.ts' import { z } from './test-prelude.ts' -import { mdf } from './constructor.ts' +import { makeSuccessResult, mdf } from './constructor.ts' import { sequence } from './domain-functions.ts' import type { DomainFunction } from './types.ts' import type { Equal, Expect } from './types.test.ts' +import { makeErrorResult } from './errors.ts' describe('sequence', () => { it('should compose domain functions from left-to-right saving the results sequentially', async () => { @@ -20,13 +21,13 @@ describe('sequence', () => { Equal> > - assertEquals(await c({ id: 1 }), { - success: true, - data: [{ id: 3 }, { result: 2 }], - errors: [], - inputErrors: [], - environmentErrors: [], - }) + assertEquals( + await c({ id: 1 }), + makeSuccessResult<[{ id: number }, { result: number }]>([ + { id: 3 }, + { result: 2 }, + ]), + ) }) it('should use the same environment in all composed functions', async () => { @@ -46,13 +47,13 @@ describe('sequence', () => { Equal> > - assertEquals(await c(undefined, { env: 1 }), { - success: true, - data: [{ inp: 3 }, { result: 4 }], - errors: [], - inputErrors: [], - environmentErrors: [], - }) + assertEquals( + await c(undefined, { env: 1 }), + makeSuccessResult<[{ inp: number }, { result: number }]>([ + { inp: 3 }, + { result: 4 }, + ]), + ) }) it('should fail on the first environment parser failure', async () => { @@ -71,12 +72,12 @@ describe('sequence', () => { const c = sequence(a, b) type _R = Expect>> - assertEquals(await c(undefined, {}), { - success: false, - errors: [], - inputErrors: [], - environmentErrors: [{ message: 'Required', path: ['env'] }], - }) + assertEquals( + await c(undefined, {}), + makeErrorResult({ + environmentErrors: [{ message: 'Required', path: ['env'] }], + }), + ) }) it('should fail on the first input parser failure', async () => { @@ -96,14 +97,14 @@ describe('sequence', () => { const c = sequence(a, b) type _R = Expect>> - assertEquals(await c({ inp: 'some invalid input' }, { env: 1 }), { - success: false, - errors: [], - inputErrors: [ - { message: 'Expected undefined, received object', path: [] }, - ], - environmentErrors: [], - }) + assertEquals( + await c({ inp: 'some invalid input' }, { env: 1 }), + makeErrorResult({ + inputErrors: [ + { message: 'Expected undefined, received object', path: [] }, + ], + }), + ) }) it('should fail on the second input parser failure', async () => { @@ -121,14 +122,14 @@ describe('sequence', () => { const c = sequence(a, b) type _R = Expect>> - assertEquals(await c(undefined, { env: 1 }), { - success: false, - errors: [], - inputErrors: [ - { message: 'Expected number, received string', path: ['inp'] }, - ], - environmentErrors: [], - }) + assertEquals( + await c(undefined, { env: 1 }), + makeErrorResult({ + inputErrors: [ + { message: 'Expected number, received string', path: ['inp'] }, + ], + }), + ) }) it('should compose more than 2 functions', async () => { @@ -156,12 +157,15 @@ describe('sequence', () => { > > - assertEquals(await d({ aNumber: 1 }), { - success: true, - data: [{ aString: '1' }, { aBoolean: true }, { anotherBoolean: false }], - errors: [], - inputErrors: [], - environmentErrors: [], - }) + assertEquals( + await d({ aNumber: 1 }), + makeSuccessResult< + [ + { aString: string }, + { aBoolean: boolean }, + { anotherBoolean: boolean }, + ] + >([{ aString: '1' }, { aBoolean: true }, { anotherBoolean: false }]), + ) }) }) diff --git a/src/trace.test.ts b/src/trace.test.ts index 61dc90c8..0fb49c80 100644 --- a/src/trace.test.ts +++ b/src/trace.test.ts @@ -10,6 +10,7 @@ import { mdf } from './constructor.ts' import { fromSuccess, trace } from './domain-functions.ts' import type { DomainFunction } from './types.ts' import type { Equal, Expect } from './types.test.ts' +import { makeErrorResult } from './errors.ts' describe('trace', () => { it('converts trace exceptions to df failures', async () => { @@ -22,12 +23,12 @@ describe('trace', () => { const result = await c({ id: 1 }) - assertObjectMatch(result, { - success: false, - errors: [{ message: 'Problem in tracing' }], - inputErrors: [], - environmentErrors: [], - }) + assertObjectMatch( + result, + makeErrorResult({ + errors: [{ message: 'Problem in tracing' }], + }), + ) }) it('intercepts inputs and outputs of a given domain function', async () => { @@ -58,4 +59,3 @@ describe('trace', () => { }) }) }) -