From ddd7e763aa3068eaf5f2c1397f4681da60048721 Mon Sep 17 00:00:00 2001 From: Guga Guichard Date: Tue, 12 Sep 2023 15:28:54 -0300 Subject: [PATCH] feat: Export mdf alias for makeDomainFunction --- README.md | 95 ++++++++++++++++++------------------ src/all.test.ts | 45 ++++++++--------- src/branch.test.ts | 55 +++++++++------------ src/collect-sequence.test.ts | 42 +++++++--------- src/collect.test.ts | 33 +++++++------ src/constructor.test.ts | 47 +++++++++--------- src/constructor.ts | 2 +- src/domain-functions.ts | 24 ++++----- src/errors.ts | 6 +-- src/first.test.ts | 22 +++------ src/from-success.test.ts | 10 ++-- src/index.ts | 2 +- src/map-error.test.ts | 14 ++---- src/map.test.ts | 12 ++--- src/merge.test.ts | 35 +++++++------ src/pipe.test.ts | 42 +++++++--------- src/sequence.test.ts | 44 ++++++++--------- src/trace.test.ts | 6 +-- src/types.test.ts | 12 ++--- 19 files changed, 251 insertions(+), 297 deletions(-) diff --git a/README.md b/README.md index 6367fbfa..02c275a0 100644 --- a/README.md +++ b/README.md @@ -105,13 +105,14 @@ This documentation will use Node.JS imports by convention, just replace `domain- ```tsx import type { ActionFunction } from 'remix' import { useActionData, redirect } from 'remix' -import { makeDomainFunction, inputFromForm } from 'domain-functions' +// You can also use the short version of makeDomainFunction: mdf +import { mdf, inputFromForm } from 'domain-functions' import * as z from 'zod' const schema = z.object({ number: z.coerce.number() }) export const action: ActionFunction = async ({ request }) => { - const increment = makeDomainFunction(schema)(({ number }) => number + 1) + const increment = mdf(schema)(({ number }) => number + 1) const result = await increment(await inputFromForm(request)) if (!result.success) return result @@ -142,7 +143,7 @@ Sometimes you want to ensure the safety of certain values that weren't explicitl ```tsx // In some app/domain/*.server.ts file -const sendEmail = makeDomainFunction( +const sendEmail = mdf( z.object({ email: z.string().email() }), // user input schema z.object({ origin: z.string() }) // environment schema )( @@ -171,7 +172,7 @@ We usually use the environment for ensuring authenticated requests. In this case, assume you have a `currentUser` function that returns the authenticated user: ```tsx -const dangerousFunction = makeDomainFunction( +const dangerousFunction = mdf( someInputSchema, z.object({ user: z.object({ id: z.string(), admin: z.literal(true) }) }) )(async (input, { user }) => { @@ -195,7 +196,7 @@ type ErrorResult = { The `inputErrors` and `environmentErrors` fields will be the errors from parsing the corresponding Zod schemas, and the `errors` field will be for any exceptions thrown inside the domain function (in which case we keep a reference to the original exception): ```ts -const alwaysFails = makeDomainFunction(input, environment)(async () => { +const alwaysFails = mdf(input, environment)(async () => { throw new Error('Some error') }) @@ -217,7 +218,7 @@ failedResult = { Whenever you want more control over the domain function's `ErrorResult`, you can throw a `ResultError` from the domain function's handler. You will then be able to add multiple error messages to the structure: ```ts -const alwaysFails = makeDomainFunction(inputSchema)(async () => { +const alwaysFails = mdf(inputSchema)(async () => { throw new ResultError({ errors: [{ message: 'Some error' }], inputErrors: [{ path: ['number'], message: 'Expected number, received nan' }], @@ -231,7 +232,7 @@ const alwaysFails = makeDomainFunction(inputSchema)(async () => { You can also throw an `InputError` whenever you want a custom input error that cannot be generated by your schema. ```ts -const alwaysFails = makeDomainFunction(input, environment)(async () => { +const alwaysFails = mdf(input, environment)(async () => { throw new InputError('Email already taken', 'email') }) @@ -249,7 +250,7 @@ failedResult = { To throw several input errors at once, you can use the pluralized version `InputErrors` like this: ```ts -const alwaysFails = makeDomainFunction(input, environment)(async () => { +const alwaysFails = mdf(input, environment)(async () => { throw new InputErrors([{message: 'Email already taken', path: 'email'}, {message: 'Password too short', path: 'password'}]) }) @@ -327,9 +328,9 @@ It will pass the same input and environment to each provided function. If __all constituent functions__ are successful, The `data` field (on the composite domain function's result) will be a tuple containing each function's output. ```ts -const a = makeDomainFunction(z.object({ id: z.number() }))(({ id }) => String(id)) -const b = makeDomainFunction(z.object({ id: z.number() }))(({ id }) => id + 1) -const c = makeDomainFunction(z.object({ id: z.number() }))(({ id }) => Boolean(id)) +const a = mdf(z.object({ id: z.number() }))(({ id }) => String(id)) +const b = mdf(z.object({ id: z.number() }))(({ id }) => id + 1) +const c = mdf(z.object({ id: z.number() }))(({ id }) => Boolean(id)) const results = await all(a, b, c)({ id: 1 }) ``` @@ -349,10 +350,10 @@ For the example above, the result type will be `Result<[string, number, boolean] If any of the constituent functions fail, the `errors` field (on the composite domain function's result) will be an array of the concatenated errors from each failing function: ```ts -const a = makeDomainFunction(z.object({ id: z.number() }))(() => { +const a = mdf(z.object({ id: z.number() }))(() => { throw new Error('Error A') }) -const b = makeDomainFunction(z.object({ id: z.number() }))(() => { +const b = mdf(z.object({ id: z.number() }))(() => { throw new Error('Error B') }) @@ -376,9 +377,9 @@ const results = await all(a, b)({ id: 1 }) The motivation for this is that an object with named fields is often preferable to long tuples, when composing many domain functions. ```ts -const a = makeDomainFunction(z.object({}))(() => '1') -const b = makeDomainFunction(z.object({}))(() => 2) -const c = makeDomainFunction(z.object({}))(() => true) +const a = mdf(z.object({}))(() => '1') +const b = mdf(z.object({}))(() => 2) +const c = mdf(z.object({}))(() => true) const results = await collect({ a, b, c })({}) ``` @@ -410,13 +411,13 @@ map(all(a, b, c), mergeObjects) The resulting data of every domain function will be merged into one object. __This could potentially lead to values of the leftmost functions being overwritten by the rightmost ones__. ```ts -const a = makeDomainFunction(z.object({}))(() => ({ +const a = mdf(z.object({}))(() => ({ resultA: 'string', resultB: 'string', resultC: 'string', })) -const b = makeDomainFunction(z.object({}))(() => ({ resultB: 2 })) -const c = makeDomainFunction(z.object({}))(async () => ({ resultC: true })) +const b = mdf(z.object({}))(() => ({ resultB: 2 })) +const c = mdf(z.object({}))(async () => ({ resultC: true })) const results = await merge(a, b, c)({}) ``` @@ -450,10 +451,10 @@ __Be mindful of__ each constituent domain function's return type. If any domain __It is important to notice__ that all constituent domain functions will be executed in parallel, so be mindful of the side effects. ```ts -const a = makeDomainFunction( +const a = mdf( z.object({ n: z.number(), operation: z.literal('increment') }), )(({ n }) => n + 1) -const b = makeDomainFunction( +const b = mdf( z.object({ n: z.number(), operation: z.literal('decrement') }), )(({ n }) => n - 1) @@ -475,10 +476,10 @@ For the example above, the result type will be `Result`: The composite domain function's result type will be a union of each constituent domain function's result type. ```ts -const a = makeDomainFunction(z.object({ operation: z.literal('A') }))(() => ({ +const a = mdf(z.object({ operation: z.literal('A') }))(() => ({ resultA: 'A', })) -const b = makeDomainFunction(z.object({ operation: z.literal('B') }))(() => ({ +const b = mdf(z.object({ operation: z.literal('B') }))(() => ({ resultB: 'B', })) @@ -492,10 +493,10 @@ return console.log('function B succeeded') If every constituent domain function fails, the `errors` field will contain the concatenated errors from each failing function's result: ```ts -const a = makeDomainFunction(z.object({ id: z.number() }))(() => { +const a = mdf(z.object({ id: z.number() }))(() => { throw new Error('Error A') }) -const b = makeDomainFunction(z.object({ id: z.number() }))(() => { +const b = mdf(z.object({ id: z.number() }))(() => { throw new Error('Error B') }) @@ -521,17 +522,17 @@ The resulting data will be the output of the rightmost function. Note that there is no type-level assurance that a function's output will align with and be succesfully parsed by the next function in the pipeline. ```ts -const a = makeDomainFunction(z.object({ aNumber: z.number() }))( +const a = mdf(z.object({ aNumber: z.number() }))( ({ aNumber }) => ({ aString: String(aNumber), }), ) -const b = makeDomainFunction(z.object({ aString: z.string() }))( +const b = mdf(z.object({ aString: z.string() }))( ({ aString }) => ({ aBoolean: aString == '1', }), ) -const c = makeDomainFunction(z.object({ aBoolean: z.boolean() }))( +const c = mdf(z.object({ aBoolean: z.boolean() }))( async ({ aBoolean }) => !aBoolean, ) @@ -560,8 +561,8 @@ If one functions fails, execution halts and the error is returned. Instead of the `data` field being the output of the last domain function, it will be a tuple containing each intermediate output (similar to the `all` function). ```ts -const a = makeDomainFunction(z.number())((aNumber) => String(aNumber)) -const b = makeDomainFunction(z.string())((aString) => aString === '1') +const a = mdf(z.number())((aNumber) => String(aNumber)) +const b = mdf(z.string())((aString) => aString === '1') const c = sequence(a, b) @@ -585,10 +586,10 @@ If you'd rather have an object instead of a tuple (similar to the `merge` functi ```ts import { mergeObjects } from 'domain-functions' -const a = makeDomainFunction(z.number())((aNumber) => ({ +const a = mdf(z.number())((aNumber) => ({ aString: String(aNumber) })) -const b = makeDomainFunction(z.object({ aString: z.string() }))( +const b = mdf(z.object({ aString: z.string() }))( ({ aString }) => ({ aBoolean: aString === '1' }) ) @@ -603,7 +604,7 @@ For the example above, the result type will be `Result<{ aString: string, aBoole `collectSequence` is very similar to the `collect` function, except __it runs in the sequence of the keys' order like a `pipe`__. -It receives its constituent functions inside a record with string keys that identify each one. +It receives its constituent functions inside a record with string keys that identify each one. The shape of this record will be preserved for the `data` property in successful results. This feature relies on JS's order of objects' keys (guaranteed since ECMAScript2015). @@ -611,8 +612,8 @@ This feature relies on JS's order of objects' keys (guaranteed since ECMAScript2 **NOTE :** For number-like object keys (eg: { 2: dfA, 1: dfB }) JS will follow ascendent order. ```ts -const a = makeDomainFunction(z.number())((aNumber) => String(aNumber)) -const b = makeDomainFunction(z.string())((aString) => aString === '1') +const a = mdf(z.number())((aNumber) => String(aNumber)) +const b = mdf(z.string())((aString) => aString === '1') const c = collectSequence({ a, b }) @@ -636,10 +637,10 @@ If you'd rather have an object instead of a tuple (similar to the `merge` functi ```ts import { mergeObjects } from 'domain-functions' -const a = makeDomainFunction(z.number())((aNumber) => ({ +const a = mdf(z.number())((aNumber) => ({ aString: String(aNumber) })) -const b = makeDomainFunction(z.object({ aString: z.string() }))( +const b = mdf(z.object({ aString: z.string() }))( ({ aString }) => ({ aBoolean: aString === '1' }) ) @@ -658,13 +659,13 @@ Use `branch` to add conditional logic to your domain functions' compositions. It receives a domain function and a predicate function that should return the next domain function to be executed based on the previous domain function's output. ```ts -const getIdOrEmail = makeDomainFunction(z.object({ id: z.number().optional, email: z.string().optional() }))((data) => { +const getIdOrEmail = mdf(z.object({ id: z.number().optional, email: z.string().optional() }))((data) => { return data.id ?? data.email }) -const findUserById = makeDomainFunction(z.number())((id) => { +const findUserById = mdf(z.number())((id) => { return db.users.find({ id }) }) -const findUserByEmail = makeDomainFunction(z.string().email())((email) => { +const findUserByEmail = mdf(z.string().email())((email) => { return db.users.find({ email }) }) const findUserByIdOrEmail = branch( @@ -712,14 +713,14 @@ If successful, the `data` field will contain the output of the first function ar This can be useful when composing domain functions. For example, you might need to align input/output types in a pipeline: ```ts -const fetchAsText = makeDomainFunction(z.object({ userId: z.number() }))( +const fetchAsText = mdf(z.object({ userId: z.number() }))( ({ userId }) => fetch(`https://reqres.in/api/users/${String(userId)}`).then((r) => r.json(), ), ) -const fullName = makeDomainFunction( +const fullName = mdf( z.object({ first_name: z.string(), last_name: z.string() }), )(({ first_name, last_name }) => `${first_name} ${last_name}`) @@ -752,7 +753,7 @@ This could be useful when adding any layer of error handling. In the example below, we are counting the errors but disregarding the contents: ```ts -const increment = makeDomainFunction(z.object({ id: z.number() }))( +const increment = mdf(z.object({ id: z.number() }))( ({ id }) => id + 1, ) @@ -790,7 +791,7 @@ For the example above, the `result` will be: Whenever the composition utilities fall short, and you want to call other domain functions from inside your current one, you can use the `fromSuccess` function to create a domain function that is expected to always succeed. ```ts -const domainFunctionA = makeDomainFunction( +const domainFunctionA = mdf( z.object({ id: z.string() }), )(async ({ id }) => { const valueB = await fromSuccess(domainFunctionB)({ userId: id }) @@ -829,7 +830,7 @@ The resulting object will be: `UnpackData` infers the returned data of a successful domain function: ```ts -const fn = makeDomainFunction()(async () => '') +const fn = mdf()(async () => '') type Data = UnpackData // Data = string @@ -840,7 +841,7 @@ type Data = UnpackData `UnpackSuccess` infers the success result of a domain function: ```ts -const fn = makeDomainFunction()(async () => '') +const fn = mdf()(async () => '') type Success = UnpackSuccess // Success = { success: true, data: string, errors: [], inputErrors: [], environmentErrors: [] } @@ -851,7 +852,7 @@ type Success = UnpackSuccess `UnpackResult` infers the result of a domain function: ```ts -const fn = makeDomainFunction()(async () => '') +const fn = mdf()(async () => '') type Result = UnpackResult /* diff --git a/src/all.test.ts b/src/all.test.ts index d1033b7c..6386c4da 100644 --- a/src/all.test.ts +++ b/src/all.test.ts @@ -1,19 +1,20 @@ -import { describe, it, assertEquals, assertObjectMatch } from './test-prelude.ts' +import { + describe, + it, + assertEquals, + assertObjectMatch, +} from './test-prelude.ts' import { z } from 'https://deno.land/x/zod@v3.21.4/mod.ts' -import { makeDomainFunction } from './constructor.ts' +import { mdf } from './constructor.ts' import { all } from './domain-functions.ts' import type { DomainFunction } from './types.ts' import type { Equal, Expect } from './types.test.ts' describe('all', () => { it('should combine two domain functions into one', async () => { - const a = makeDomainFunction(z.object({ id: z.number() }))( - ({ id }) => id + 1, - ) - const b = makeDomainFunction(z.object({ id: z.number() }))( - ({ id }) => id - 1, - ) + const a = mdf(z.object({ id: z.number() }))(({ id }) => id + 1) + const b = mdf(z.object({ id: z.number() }))(({ id }) => id - 1) const c = all(a, b) type _R = Expect>> @@ -28,15 +29,9 @@ describe('all', () => { }) it('should combine many domain functions into one', async () => { - const a = makeDomainFunction(z.object({ id: z.number() }))(({ id }) => - String(id), - ) - const b = makeDomainFunction(z.object({ id: z.number() }))( - ({ id }) => id + 1, - ) - const c = makeDomainFunction(z.object({ id: z.number() }))(({ id }) => - Boolean(id), - ) + const a = mdf(z.object({ id: z.number() }))(({ id }) => String(id)) + const b = mdf(z.object({ id: z.number() }))(({ id }) => id + 1) + const c = mdf(z.object({ id: z.number() }))(({ id }) => Boolean(id)) const d = all(a, b, c) type _R = Expect>> @@ -51,8 +46,8 @@ describe('all', () => { }) it('should return error when one of the domain functions has input errors', async () => { - const a = makeDomainFunction(z.object({ id: z.number() }))(({ id }) => id) - const b = makeDomainFunction(z.object({ id: z.string() }))(({ id }) => id) + const a = mdf(z.object({ id: z.number() }))(({ id }) => id) + const b = mdf(z.object({ id: z.string() }))(({ id }) => id) const c = all(a, b) type _R = Expect>> @@ -71,8 +66,8 @@ describe('all', () => { }) it('should return error when one of the domain functions fails', async () => { - const a = makeDomainFunction(z.object({ id: z.number() }))(({ id }) => id) - const b = makeDomainFunction(z.object({ id: z.number() }))(() => { + const a = mdf(z.object({ id: z.number() }))(({ id }) => id) + const b = mdf(z.object({ id: z.number() }))(() => { throw 'Error' }) @@ -88,8 +83,8 @@ describe('all', () => { }) it('should combine the inputError messages of both functions', async () => { - const a = makeDomainFunction(z.object({ id: z.string() }))(({ id }) => id) - const b = makeDomainFunction(z.object({ id: z.string() }))(({ id }) => id) + const a = mdf(z.object({ id: z.string() }))(({ id }) => id) + const b = mdf(z.object({ id: z.string() }))(({ id }) => id) const c = all(a, b) type _R = Expect>> @@ -112,10 +107,10 @@ describe('all', () => { }) it('should combine the error messages when both functions fail', async () => { - const a = makeDomainFunction(z.object({ id: z.number() }))(() => { + const a = mdf(z.object({ id: z.number() }))(() => { throw new Error('Error A') }) - const b = makeDomainFunction(z.object({ id: z.number() }))(() => { + const b = mdf(z.object({ id: z.number() }))(() => { throw new Error('Error B') }) diff --git a/src/branch.test.ts b/src/branch.test.ts index f65544b5..24295334 100644 --- a/src/branch.test.ts +++ b/src/branch.test.ts @@ -1,19 +1,22 @@ -import { describe, it, assertEquals, assertObjectMatch } from './test-prelude.ts' +import { + describe, + it, + assertEquals, + assertObjectMatch, +} from './test-prelude.ts' import { z } from 'https://deno.land/x/zod@v3.21.4/mod.ts' -import { makeDomainFunction } from './constructor.ts' +import { 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' describe('branch', () => { it('should pipe a domain function with a function that returns a DF', async () => { - const a = makeDomainFunction(z.object({ id: z.number() }))(({ id }) => ({ + const a = mdf(z.object({ id: z.number() }))(({ id }) => ({ id: id + 2, })) - const b = makeDomainFunction(z.object({ id: z.number() }))( - ({ id }) => id - 1, - ) + const b = mdf(z.object({ id: z.number() }))(({ id }) => id - 1) const c = branch(a, () => Promise.resolve(b)) type _R = Expect>> @@ -28,16 +31,12 @@ describe('branch', () => { }) it('should enable conditionally choosing the next DF with the output of first one', async () => { - const a = makeDomainFunction(z.object({ id: z.number() }))(({ id }) => ({ + const a = mdf(z.object({ id: z.number() }))(({ id }) => ({ id: id + 2, next: 'multiply', })) - const b = makeDomainFunction(z.object({ id: z.number() }))(({ id }) => - String(id), - ) - const c = makeDomainFunction(z.object({ id: z.number() }))( - ({ id }) => id * 2, - ) + const b = mdf(z.object({ id: z.number() }))(({ id }) => String(id)) + const c = mdf(z.object({ id: z.number() }))(({ id }) => id * 2) const d = branch(a, (output) => (output.next === 'multiply' ? c : b)) type _R = Expect>> @@ -51,13 +50,13 @@ describe('branch', () => { }) it('should use the same environment in all composed functions', async () => { - const a = makeDomainFunction( + const a = mdf( z.undefined(), z.object({ env: z.number() }), )((_input, { env }) => ({ inp: env + 2, })) - const b = makeDomainFunction( + const b = mdf( z.object({ inp: z.number() }), z.object({ env: z.number() }), )(({ inp }, { env }) => inp + env) @@ -75,12 +74,10 @@ describe('branch', () => { }) it('should gracefully fail if the first function fails', async () => { - const a = makeDomainFunction(z.object({ id: z.number() }))(({ id }) => ({ + const a = mdf(z.object({ id: z.number() }))(({ id }) => ({ id: id + 2, })) - const b = makeDomainFunction(z.object({ id: z.number() }))( - ({ id }) => id - 1, - ) + const b = mdf(z.object({ id: z.number() }))(({ id }) => id - 1) const c = branch(a, () => b) type _R = Expect>> @@ -98,12 +95,10 @@ describe('branch', () => { }) it('should gracefully fail if the second function fails', async () => { - const a = makeDomainFunction(z.object({ id: z.number() }))(({ id }) => ({ + const a = mdf(z.object({ id: z.number() }))(({ id }) => ({ id: String(id), })) - const b = makeDomainFunction(z.object({ id: z.number() }))( - ({ id }) => id - 1, - ) + const b = mdf(z.object({ id: z.number() }))(({ id }) => id - 1) const c = branch(a, () => b) type _R = Expect>> @@ -121,12 +116,10 @@ describe('branch', () => { }) it('should gracefully fail if the condition function fails', async () => { - const a = makeDomainFunction(z.object({ id: z.number() }))(({ id }) => ({ + const a = mdf(z.object({ id: z.number() }))(({ id }) => ({ id: id + 2, })) - const b = makeDomainFunction(z.object({ id: z.number() }))( - ({ id }) => id - 1, - ) + const b = mdf(z.object({ id: z.number() }))(({ id }) => id - 1) const c = branch(a, (_) => { throw new Error('condition function failed') return b @@ -142,13 +135,11 @@ describe('branch', () => { }) it('should not break composition with other combinators', async () => { - const a = makeDomainFunction(z.object({ id: z.number() }))(({ id }) => ({ + const a = mdf(z.object({ id: z.number() }))(({ id }) => ({ id: id + 2, })) - const b = makeDomainFunction(z.object({ id: z.number() }))( - ({ id }) => id - 1, - ) - const c = makeDomainFunction(z.number())((n) => n * 2) + const b = mdf(z.object({ id: z.number() }))(({ id }) => id - 1) + const c = mdf(z.number())((n) => n * 2) const d = all( pipe( branch(a, () => b), diff --git a/src/collect-sequence.test.ts b/src/collect-sequence.test.ts index 62af8a98..b0fb6c19 100644 --- a/src/collect-sequence.test.ts +++ b/src/collect-sequence.test.ts @@ -1,19 +1,17 @@ import { describe, it, assertEquals } from './test-prelude.ts' import { z } from 'https://deno.land/x/zod@v3.21.4/mod.ts' -import { makeDomainFunction } from './constructor.ts' +import { mdf } from './constructor.ts' import { collectSequence } from './domain-functions.ts' import type { DomainFunction } from './types.ts' import type { Equal, Expect } from './types.test.ts' describe('collectSequence', () => { it('should compose domain functions keeping the given order of keys', async () => { - const a = makeDomainFunction(z.object({ id: z.number() }))(({ id }) => ({ + const a = mdf(z.object({ id: z.number() }))(({ id }) => ({ id: id + 2, })) - const b = makeDomainFunction(z.object({ id: z.number() }))( - ({ id }) => id - 1, - ) + const b = mdf(z.object({ id: z.number() }))(({ id }) => id - 1) const c = collectSequence({ a, b }) type _R = Expect< @@ -30,13 +28,13 @@ describe('collectSequence', () => { }) it('should use the same environment in all composed functions', async () => { - const a = makeDomainFunction( + const a = mdf( z.undefined(), z.object({ env: z.number() }), )((_input, { env }) => ({ inp: env + 2, })) - const b = makeDomainFunction( + const b = mdf( z.object({ inp: z.number() }), z.object({ env: z.number() }), )(({ inp }, { env }) => inp + env) @@ -57,13 +55,13 @@ describe('collectSequence', () => { it('should fail on the first environment parser failure', async () => { const envParser = z.object({ env: z.number() }) - const a = makeDomainFunction( + const a = mdf( z.undefined(), envParser, )((_input, { env }) => ({ inp: env + 2, })) - const b = makeDomainFunction( + const b = mdf( z.object({ inp: z.number() }), envParser, )(({ inp }, { env }) => inp + env) @@ -84,13 +82,13 @@ describe('collectSequence', () => { it('should fail on the first input parser failure', async () => { const firstInputParser = z.undefined() - const a = makeDomainFunction( + const a = mdf( firstInputParser, z.object({ env: z.number() }), )((_input, { env }) => ({ inp: env + 2, })) - const b = makeDomainFunction( + const b = mdf( z.object({ inp: z.number() }), z.object({ env: z.number() }), )(({ inp }, { env }) => inp + env) @@ -111,13 +109,13 @@ describe('collectSequence', () => { }) it('should fail on the second input parser failure', async () => { - const a = makeDomainFunction( + const a = mdf( z.undefined(), z.object({ env: z.number() }), )(() => ({ inp: 'some invalid input', })) - const b = makeDomainFunction( + const b = mdf( z.object({ inp: z.number() }), z.object({ env: z.number() }), )(({ inp }, { env }) => inp + env) @@ -138,17 +136,13 @@ describe('collectSequence', () => { }) it('should compose more than 2 functions', async () => { - const a = makeDomainFunction(z.object({ aNumber: z.number() }))( - ({ aNumber }) => ({ - aString: String(aNumber), - }), - ) - const b = makeDomainFunction(z.object({ aString: z.string() }))( - ({ aString }) => ({ - aBoolean: aString == '1', - }), - ) - const c = makeDomainFunction(z.object({ aBoolean: z.boolean() }))( + const a = mdf(z.object({ aNumber: z.number() }))(({ aNumber }) => ({ + aString: String(aNumber), + })) + const b = mdf(z.object({ aString: z.string() }))(({ aString }) => ({ + aBoolean: aString == '1', + })) + const c = mdf(z.object({ aBoolean: z.boolean() }))( ({ aBoolean }) => !aBoolean, ) diff --git a/src/collect.test.ts b/src/collect.test.ts index e7125593..0fd1428e 100644 --- a/src/collect.test.ts +++ b/src/collect.test.ts @@ -1,19 +1,20 @@ -import { describe, it, assertObjectMatch, assertEquals } from './test-prelude.ts' +import { + describe, + it, + assertObjectMatch, + assertEquals, +} from './test-prelude.ts' import { z } from 'https://deno.land/x/zod@v3.21.4/mod.ts' -import { makeDomainFunction } from './constructor.ts' +import { mdf } from './constructor.ts' import { collect } from './domain-functions.ts' import type { DomainFunction } from './types.ts' import type { Equal, Expect } from './types.test.ts' describe('collect', () => { it('should combine an object of domain functions', async () => { - const a = makeDomainFunction(z.object({ id: z.number() }))( - ({ id }) => id + 1, - ) - const b = makeDomainFunction(z.object({ id: z.number() }))( - ({ id }) => id - 1, - ) + const a = mdf(z.object({ id: z.number() }))(({ id }) => id + 1) + const b = mdf(z.object({ id: z.number() }))(({ id }) => id - 1) const c = collect({ a, b }) type _R = Expect>> @@ -28,8 +29,8 @@ describe('collect', () => { }) it('should return error when one of the domain functions has input errors', async () => { - const a = makeDomainFunction(z.object({ id: z.number() }))(({ id }) => id) - const b = makeDomainFunction(z.object({ id: z.string() }))(({ id }) => id) + const a = mdf(z.object({ id: z.number() }))(({ id }) => id) + const b = mdf(z.object({ id: z.string() }))(({ id }) => id) const c = collect({ a, b }) type _R = Expect>> @@ -48,8 +49,8 @@ describe('collect', () => { }) it('should return error when one of the domain functions fails', async () => { - const a = makeDomainFunction(z.object({ id: z.number() }))(({ id }) => id) - const b = makeDomainFunction(z.object({ id: z.number() }))(() => { + const a = mdf(z.object({ id: z.number() }))(({ id }) => id) + const b = mdf(z.object({ id: z.number() }))(() => { throw 'Error' }) @@ -65,8 +66,8 @@ describe('collect', () => { }) it('should combine the inputError messages of both functions', async () => { - const a = makeDomainFunction(z.object({ id: z.string() }))(({ id }) => id) - const b = makeDomainFunction(z.object({ id: z.string() }))(({ id }) => id) + const a = mdf(z.object({ id: z.string() }))(({ id }) => id) + const b = mdf(z.object({ id: z.string() }))(({ id }) => id) const c = collect({ a, b }) type _R = Expect>> @@ -89,10 +90,10 @@ describe('collect', () => { }) it('should combine the error messages when both functions fail', async () => { - const a = makeDomainFunction(z.object({ id: z.number() }))(() => { + const a = mdf(z.object({ id: z.number() }))(() => { throw new Error('Error A') }) - const b = makeDomainFunction(z.object({ id: z.number() }))(() => { + const b = mdf(z.object({ id: z.number() }))(() => { throw new Error('Error B') }) diff --git a/src/constructor.test.ts b/src/constructor.test.ts index 4b597bcc..4cbed9a9 100644 --- a/src/constructor.test.ts +++ b/src/constructor.test.ts @@ -1,7 +1,12 @@ -import { describe, it, assertEquals, assertObjectMatch } from './test-prelude.ts' +import { + describe, + it, + assertEquals, + assertObjectMatch, +} from './test-prelude.ts' import { z } from 'https://deno.land/x/zod@v3.21.4/mod.ts' -import { makeDomainFunction } from './constructor.ts' +import { mdf } from './constructor.ts' import { EnvironmentError, ResultError, @@ -13,7 +18,7 @@ import type { Equal, Expect } from './types.test.ts' describe('makeDomainFunction', () => { describe('when it has no input', async () => { - const handler = makeDomainFunction()(() => 'no input!') + const handler = mdf()(() => 'no input!') type _R = Expect>> assertEquals(await handler(), { @@ -29,7 +34,7 @@ describe('makeDomainFunction', () => { it('uses zod parser to create parse the input and call the domain function', async () => { const parser = z.object({ id: z.preprocess(Number, z.number()) }) - const handler = makeDomainFunction(parser)(({ id }) => id) + const handler = mdf(parser)(({ id }) => id) type _R = Expect>> assertEquals(await handler({ id: '1' }), { @@ -43,7 +48,7 @@ describe('makeDomainFunction', () => { it('returns error when parsing fails', async () => { const parser = z.object({ id: z.preprocess(Number, z.number()) }) - const handler = makeDomainFunction(parser)(({ id }) => id) + const handler = mdf(parser)(({ id }) => id) type _R = Expect>> assertEquals(await handler({ missingId: '1' }), { @@ -61,7 +66,7 @@ describe('makeDomainFunction', () => { const parser = z.object({ id: z.preprocess(Number, z.number()) }) const envParser = z.object({ uid: z.preprocess(Number, z.number()) }) - const handler = makeDomainFunction( + const handler = mdf( parser, envParser, )(({ id }, { uid }) => [id, uid] as const) @@ -91,10 +96,7 @@ describe('makeDomainFunction', () => { .refine((value) => value !== 2, { message: 'UID already taken' }), }) - const handler = makeDomainFunction( - parser, - envParser, - )(({ id }, { uid }) => [id, uid]) + const handler = mdf(parser, envParser)(({ id }, { uid }) => [id, uid]) type _R = Expect>> assertEquals(await handler({ id: '1' }, { uid: '2' }), { @@ -106,7 +108,7 @@ describe('makeDomainFunction', () => { }) it('accepts literals as input of domain functions', async () => { - const handler = makeDomainFunction(z.number(), z.string())((n) => n + 1) + const handler = mdf(z.number(), z.string())((n) => n + 1) type _R = Expect>> const result = await handler(1, 'not going to be used') @@ -114,7 +116,7 @@ describe('makeDomainFunction', () => { }) it('accepts sync functions', async () => { - const handler = makeDomainFunction(z.number())((n) => n + 1) + const handler = mdf(z.number())((n) => n + 1) type _R = Expect>> const result = await handler(1) @@ -125,10 +127,7 @@ describe('makeDomainFunction', () => { const parser = z.object({ id: z.preprocess(Number, z.number()) }) const envParser = z.object({ uid: z.preprocess(Number, z.number()) }) - const handler = makeDomainFunction( - parser, - envParser, - )(({ id }, { uid }) => [id, uid]) + const handler = mdf(parser, envParser)(({ id }, { uid }) => [id, uid]) type _R = Expect>> assertEquals(await handler({ id: '1' }, {}), { @@ -142,7 +141,7 @@ describe('makeDomainFunction', () => { }) it('returns error when the domain function throws an Error', async () => { - const handler = makeDomainFunction(z.object({ id: z.number() }))(() => { + const handler = mdf(z.object({ id: z.number() }))(() => { throw new Error('Error') }) type _R = Expect>> @@ -156,7 +155,7 @@ describe('makeDomainFunction', () => { }) it('preserves entire original exception when the domain function throws an Error', async () => { - const handler = makeDomainFunction(z.object({ id: z.number() }))(() => { + const handler = mdf(z.object({ id: z.number() }))(() => { throw new Error('Some message', { cause: { someUnknownFields: true } }) }) type _R = Expect>> @@ -178,7 +177,7 @@ describe('makeDomainFunction', () => { }) it('returns error when the domain function throws a string', async () => { - const handler = makeDomainFunction(z.object({ id: z.number() }))(() => { + const handler = mdf(z.object({ id: z.number() }))(() => { throw 'Error' }) type _R = Expect>> @@ -192,7 +191,7 @@ describe('makeDomainFunction', () => { }) it('returns error when the domain function throws an object with message', async () => { - const handler = makeDomainFunction(z.object({ id: z.number() }))(() => { + const handler = mdf(z.object({ id: z.number() }))(() => { throw { message: 'Error' } }) type _R = Expect>> @@ -206,7 +205,7 @@ describe('makeDomainFunction', () => { }) it('returns inputErrors when the domain function throws an InputError', async () => { - const handler = makeDomainFunction(z.object({ id: z.number() }))(() => { + const handler = mdf(z.object({ id: z.number() }))(() => { throw new InputError('Custom input error', 'contact.id') }) type _R = Expect>> @@ -220,7 +219,7 @@ describe('makeDomainFunction', () => { }) it('returns multiple inputErrors when the domain function throws an InputErrors', async () => { - const handler = makeDomainFunction(z.object({ id: z.number() }))(() => { + const handler = mdf(z.object({ id: z.number() }))(() => { throw new InputErrors([ { message: 'Custom input error', path: 'contact.id' }, { message: 'Another input error', path: 'contact.id' }, @@ -240,7 +239,7 @@ describe('makeDomainFunction', () => { }) it('returns environmentErrors when the domain function throws an EnvironmentError', async () => { - const handler = makeDomainFunction(z.object({ id: z.number() }))(() => { + const handler = mdf(z.object({ id: z.number() }))(() => { throw new EnvironmentError('Custom env error', 'currentUser.role') }) type _R = Expect>> @@ -256,7 +255,7 @@ describe('makeDomainFunction', () => { }) it('returns an error result when the domain function throws an ResultError', async () => { - const handler = makeDomainFunction(z.object({ id: z.number() }))(() => { + const handler = mdf(z.object({ id: z.number() }))(() => { throw new ResultError({ errors: [], inputErrors: [ diff --git a/src/constructor.ts b/src/constructor.ts index 4605b697..61aec71c 100644 --- a/src/constructor.ts +++ b/src/constructor.ts @@ -124,4 +124,4 @@ function makeDomainFunction< } } -export { safeResult, makeDomainFunction } +export { safeResult, makeDomainFunction, makeDomainFunction as mdf } diff --git a/src/domain-functions.ts b/src/domain-functions.ts index 234671d7..7421416c 100644 --- a/src/domain-functions.ts +++ b/src/domain-functions.ts @@ -18,7 +18,7 @@ import { safeResult } from './constructor.ts' /** * Creates a single domain function out of multiple domain functions. It will pass the same input and environment to each provided function. The functions will run in parallel. If all constituent functions are successful, The data field will be a tuple containing each function's output. * @example - * import { makeDomainFunction as mdf, all } from 'domain-functions' + * import { mdf, all } from 'domain-functions' * * const a = mdf(z.object({ id: z.number() }))(({ id }) => String(id)) const b = mdf(z.object({ id: z.number() }))(({ id }) => id + 1) @@ -53,7 +53,7 @@ function all( /** * Receives a Record of domain functions, runs them all in parallel and preserves the shape of this record for the data property in successful results. * @example - * import { makeDomainFunction as mdf, collect } from 'domain-functions' + * import { mdf, collect } from 'domain-functions' * * const a = mdf(z.object({}))(() => '1') const b = mdf(z.object({}))(() => 2) @@ -74,7 +74,7 @@ function collect>( /** * Creates a composite domain function that will return the result of the first successful constituent domain function. **It is important to notice** that all constituent domain functions will be executed in parallel, so be mindful of the side effects. * @example - * import { makeDomainFunction as mdf, first } from 'domain-functions' + * import { mdf, first } from 'domain-functions' * * const a = mdf(z.object({ n: z.number() }))(({ n }) => n + 1) const b = mdf(z.object({ n: z.number() }))(({ n }) => String(n)) @@ -109,7 +109,7 @@ function first( /** * **NOTE :** Try to use [collect](collect) instead wherever possible since it is much safer. `merge` can create domain functions that will always fail in run-time or even overwrite data from successful constituent functions application. The `collect` function does not have these issues and serves a similar purpose. * @example - * import { makeDomainFunction as mdf, merge } from 'domain-functions' + * import { mdf, merge } from 'domain-functions' * * const a = mdf(z.object({}))(() => ({ a: 'a' })) * const b = mdf(z.object({}))(() => ({ b: 2 })) @@ -125,7 +125,7 @@ function merge>[]>( /** * Creates a single domain function out of a chain of multiple domain functions. It will pass the same environment to all given functions, and it will pass the output of a function as the next function's input in left-to-right order. The resulting data will be the output of the rightmost function. * @example - * import { makeDomainFunction as mdf, pipe } from 'domain-functions' + * import { mdf, pipe } from 'domain-functions' * * const a = mdf(z.object({ aNumber: z.number() }))( * ({ aNumber }) => ({ aString: String(aNumber) }), @@ -149,7 +149,7 @@ function pipe( * * **NOTE :** After ECMAScript2015 JS is able to keep the order of keys in an object, we are relying on that. However, number-like keys such as { 1: 'foo' } will be ordered and may break the given order. * @example - * import { makeDomainFunction as mdf, collectSequence } from 'domain-functions' + * import { mdf, collectSequence } from 'domain-functions' * * const a = mdf(z.object({}))(() => '1') const b = mdf(z.number())((n) => n + 2) @@ -174,7 +174,7 @@ function collectSequence>( /** * Works like `pipe` but it will collect the output of every function in a tuple, similar to `all`. * @example - * import { makeDomainFunction as mdf, sequence } from 'domain-functions' + * import { mdf, sequence } from 'domain-functions' * * const a = mdf(z.number())((aNumber) => String(aNumber)) * const b = mdf(z.string())((aString) => aString === '1') @@ -206,7 +206,7 @@ function sequence( /** * It takes a domain function and a predicate to apply a transformation over the result.data of that function. It only runs if the function was successfull. When the given domain function fails, its error is returned wihout changes. * @example - * import { makeDomainFunction as mdf, map } from 'domain-functions' + * import { mdf, map } from 'domain-functions' * * const a = mdf(z.object({ n: z.number() }))(({ n }) => n + 1) * const df = map(a, (n) => String(n)) @@ -228,7 +228,7 @@ function map( * Use it to add conditional logic to your domain functions' compositions. * It receives a domain function and a predicate function that should return the next domain function to be executed based on the previous domain function's output. * @example - * import { makeDomainFunction as mdf, branch } from 'domain-functions' + * import { mdf, branch } from 'domain-functions' * * const getIdOrEmail = mdf(z.object({ id: z.number().optional(), email: z.string().optional() }))((data) => data.id ?? data.email) * const findUserById = mdf(z.number())((id) => db.users.find({ id })) @@ -258,7 +258,7 @@ function branch( * It can be used to call a domain function from another domain function. It will return the output of the given domain function if it was successfull, otherwise it will throw a `ResultError` that will bubble up to the parent function. * Also good to use it in successfull test cases. * @example - * import { makeDomainFunction as mdf, fromSuccess } from 'domain-functions' + * import { mdf, fromSuccess } from 'domain-functions' * * const add1 = mdf(z.number())((n) => n + 1) * const result = await add1(1) @@ -281,7 +281,7 @@ function fromSuccess( /** * Creates a single domain function that will apply a transformation over the ErrorResult of a failed DomainFunction. When the given domain function succeeds, its result is returned without changes. * @example - * import { makeDomainFunction as mdf, mapError } from 'domain-functions' + * import { mdf, mapError } from 'domain-functions' * * const increment = mdf(z.object({ id: z.number() }))(({ id }) => id + 1) * const summarizeErrors = (result: ErrorData) => @@ -317,7 +317,7 @@ type TraceData = { * The most common use case is to log failures to the console or to an external service. * @param traceFn A function that receives the input, environment and result of a domain function. * @example - * import { makeDomainFunction as mdf, trace } from 'domain-functions' + * import { mdf, trace } from 'domain-functions' * * const trackErrors = trace(({ input, output, result }) => { * if(!result.success) sendToExternalService({ input, output, result }) diff --git a/src/errors.ts b/src/errors.ts index 68349d91..8b7a76aa 100644 --- a/src/errors.ts +++ b/src/errors.ts @@ -61,7 +61,7 @@ function errorMessagesFor(errors: SchemaError[], name: string) { /** * A custom error class for input errors. * @example - * const df = makeDomainFunction()(() => { + * const df = mdf()(() => { * throw new InputError('Invalid input', 'user.name') * }) */ @@ -87,7 +87,7 @@ class InputErrors extends Error { /** * A custom error class for environment errors. * @example - * const df = makeDomainFunction()(() => { + * const df = mdf()(() => { * throw new EnvironmentError('Invalid environment', 'user.name') * }) */ @@ -104,7 +104,7 @@ class EnvironmentError extends Error { /** * A custom error class for creating ErrorResult. * @example - * const df = makeDomainFunction()(() => { + * const df = mdf()(() => { * throw new ResultError({ * errors: [{ message: 'Some error' }], * inputErrors: [{ message: 'Some input error', path: 'user.name' }], diff --git a/src/first.test.ts b/src/first.test.ts index 77365a76..4815782a 100644 --- a/src/first.test.ts +++ b/src/first.test.ts @@ -1,22 +1,16 @@ import { describe, it, assertEquals } from './test-prelude.ts' import { z } from 'https://deno.land/x/zod@v3.21.4/mod.ts' -import { makeDomainFunction } from './constructor.ts' +import { mdf } from './constructor.ts' import { first } from './domain-functions.ts' import type { DomainFunction } from './types.ts' import type { Equal, Expect } from './types.test.ts' describe('first', () => { it('should return the result of the first successful domain function', async () => { - const a = makeDomainFunction(z.object({ id: z.number() }))(({ id }) => - String(id), - ) - const b = makeDomainFunction(z.object({ id: z.number() }))( - ({ id }) => id + 1, - ) - const c = makeDomainFunction(z.object({ id: z.number() }))(({ id }) => - Boolean(id), - ) + const a = mdf(z.object({ id: z.number() }))(({ id }) => String(id)) + const b = mdf(z.object({ id: z.number() }))(({ id }) => id + 1) + const c = mdf(z.object({ id: z.number() }))(({ id }) => Boolean(id)) const d = first(a, b, c) type _R = Expect>> @@ -31,10 +25,10 @@ describe('first', () => { }) it('should return a successful result even if one of the domain functions fails', async () => { - const a = makeDomainFunction( + const a = mdf( z.object({ n: z.number(), operation: z.literal('increment') }), )(({ n }) => n + 1) - const b = makeDomainFunction( + const b = mdf( z.object({ n: z.number(), operation: z.literal('decrement') }), )(({ n }) => n - 1) @@ -51,8 +45,8 @@ describe('first', () => { }) it('should return error when all of the domain functions fails', async () => { - const a = makeDomainFunction(z.object({ id: z.string() }))(({ id }) => id) - const b = makeDomainFunction(z.object({ id: z.number() }))(() => { + const a = mdf(z.object({ id: z.string() }))(({ id }) => id) + const b = mdf(z.object({ id: z.number() }))(() => { throw 'Error' }) diff --git a/src/from-success.test.ts b/src/from-success.test.ts index 3407b282..4253fd9d 100644 --- a/src/from-success.test.ts +++ b/src/from-success.test.ts @@ -1,16 +1,14 @@ import { describe, it, assertEquals, assertRejects } from './test-prelude.ts' import { z } from 'https://deno.land/x/zod@v3.21.4/mod.ts' -import { makeDomainFunction } from './constructor.ts' +import { mdf } from './constructor.ts' import { fromSuccess } from './domain-functions.ts' import { ResultError } from './errors.ts' import type { Equal, Expect } from './types.test.ts' describe('fromSuccess', () => { it('returns the result.data when the domain function suceeds', async () => { - const a = makeDomainFunction(z.object({ id: z.number() }))( - ({ id }) => id + 1, - ) + const a = mdf(z.object({ id: z.number() }))(({ id }) => id + 1) const c = fromSuccess(a) type _R = Expect< @@ -24,9 +22,7 @@ describe('fromSuccess', () => { }) it('throws an exception when the domain function fails', () => { - const a = makeDomainFunction(z.object({ id: z.number() }))( - ({ id }) => id + 1, - ) + const a = mdf(z.object({ id: z.number() }))(({ id }) => id + 1) const c = fromSuccess(a) type _R = Expect< diff --git a/src/index.ts b/src/index.ts index 1fbe324c..224a0ac6 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,4 +1,4 @@ -export { makeDomainFunction, safeResult } from './constructor.ts' +export { makeDomainFunction, mdf, safeResult } from './constructor.ts' export * from './domain-functions.ts' export * from './input-resolvers.ts' export * from './errors.ts' diff --git a/src/map-error.test.ts b/src/map-error.test.ts index b174f63a..6471a2cf 100644 --- a/src/map-error.test.ts +++ b/src/map-error.test.ts @@ -1,16 +1,14 @@ import { describe, it, assertEquals } from './test-prelude.ts' import { z } from 'https://deno.land/x/zod@v3.21.4/mod.ts' -import { makeDomainFunction } from './constructor.ts' +import { 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' describe('mapError', () => { it('returns the result when the domain function suceeds', async () => { - const a = makeDomainFunction(z.object({ id: z.number() }))( - ({ id }) => id + 1, - ) + const a = mdf(z.object({ id: z.number() }))(({ id }) => id + 1) const b = () => ({ errors: [{ message: 'New Error Message' }], @@ -30,9 +28,7 @@ describe('mapError', () => { }) it('returns a domain function function that will apply a function over the error of the first one', async () => { - const a = makeDomainFunction(z.object({ id: z.number() }))( - ({ id }) => id + 1, - ) + const a = mdf(z.object({ id: z.number() }))(({ id }) => id + 1) const b = (result: ErrorData) => ({ errors: [{ message: 'Number of errors: ' + result.errors.length }], @@ -57,9 +53,7 @@ describe('mapError', () => { }) it('returns the error when the mapping function fails', async () => { - const a = makeDomainFunction(z.object({ id: z.number() }))( - ({ id }) => id + 1, - ) + const a = mdf(z.object({ id: z.number() }))(({ id }) => id + 1) const b = () => { throw 'failed to map' } diff --git a/src/map.test.ts b/src/map.test.ts index ae699179..71f77012 100644 --- a/src/map.test.ts +++ b/src/map.test.ts @@ -1,16 +1,14 @@ import { describe, it, assertEquals } from './test-prelude.ts' import { z } from 'https://deno.land/x/zod@v3.21.4/mod.ts' -import { makeDomainFunction } from './constructor.ts' +import { mdf } from './constructor.ts' import { map } from './domain-functions.ts' import type { DomainFunction } from './types.ts' import type { Equal, Expect } from './types.test.ts' describe('map', () => { it('returns a domain function function that will apply a function over the results of the first one', async () => { - const a = makeDomainFunction(z.object({ id: z.number() }))( - ({ id }) => id + 1, - ) + const a = mdf(z.object({ id: z.number() }))(({ id }) => id + 1) const b = (id: number) => id + 1 const c = map(a, b) @@ -27,7 +25,7 @@ describe('map', () => { it('returns the error when the domain function fails', async () => { const firstInputParser = z.object({ id: z.number() }) - const a = makeDomainFunction(firstInputParser)(({ id }) => id + 1) + const a = mdf(firstInputParser)(({ id }) => id + 1) const b = (id: number) => id + 1 const c = map(a, b) @@ -42,9 +40,7 @@ describe('map', () => { }) it('returns the error when the mapping function fails', async () => { - const a = makeDomainFunction(z.object({ id: z.number() }))( - ({ id }) => id + 1, - ) + const a = mdf(z.object({ id: z.number() }))(({ id }) => id + 1) const b = () => { throw 'failed to map' } diff --git a/src/merge.test.ts b/src/merge.test.ts index 4f8a0973..b5e625f2 100644 --- a/src/merge.test.ts +++ b/src/merge.test.ts @@ -1,17 +1,22 @@ -import { describe, it, assertEquals, assertObjectMatch } from './test-prelude.ts' +import { + describe, + it, + assertEquals, + assertObjectMatch, +} from './test-prelude.ts' import { z } from 'https://deno.land/x/zod@v3.21.4/mod.ts' -import { makeDomainFunction } from './constructor.ts' +import { mdf } from './constructor.ts' import { merge } from './domain-functions.ts' import type { DomainFunction } from './types.ts' import type { Equal, Expect } from './types.test.ts' describe('merge', () => { it('should combine two domain functions results into one object', async () => { - const a = makeDomainFunction(z.object({ id: z.number() }))(({ id }) => ({ + const a = mdf(z.object({ id: z.number() }))(({ id }) => ({ resultA: id + 1, })) - const b = makeDomainFunction(z.object({ id: z.number() }))(({ id }) => ({ + const b = mdf(z.object({ id: z.number() }))(({ id }) => ({ resultB: id - 1, })) @@ -30,15 +35,15 @@ describe('merge', () => { }) it('should combine many domain functions into one', async () => { - const a = makeDomainFunction(z.object({ id: z.number() }))(({ id }) => ({ + const a = mdf(z.object({ id: z.number() }))(({ id }) => ({ resultA: String(id), resultB: String(id), resultC: String(id), })) - const b = makeDomainFunction(z.object({ id: z.number() }))(({ id }) => ({ + const b = mdf(z.object({ id: z.number() }))(({ id }) => ({ resultB: id + 1, })) - const c = makeDomainFunction(z.object({ id: z.number() }))(({ id }) => ({ + const c = mdf(z.object({ id: z.number() }))(({ id }) => ({ resultC: Boolean(id), })) const d = merge(a, b, c) @@ -68,10 +73,10 @@ describe('merge', () => { }) it('should return error when one of the domain functions has input errors', async () => { - const a = makeDomainFunction(z.object({ id: z.number() }))(({ id }) => ({ + const a = mdf(z.object({ id: z.number() }))(({ id }) => ({ id, })) - const b = makeDomainFunction(z.object({ id: z.string() }))(({ id }) => ({ + const b = mdf(z.object({ id: z.string() }))(({ id }) => ({ id, })) @@ -99,10 +104,10 @@ describe('merge', () => { }) it('should return error when one of the domain functions fails', async () => { - const a = makeDomainFunction(z.object({ id: z.number() }))(({ id }) => ({ + const a = mdf(z.object({ id: z.number() }))(({ id }) => ({ id, })) - const b = makeDomainFunction(z.object({ id: z.number() }))(() => { + const b = mdf(z.object({ id: z.number() }))(() => { throw 'Error' }) @@ -118,10 +123,10 @@ describe('merge', () => { }) it('should combine the inputError messages of both functions', async () => { - const a = makeDomainFunction(z.object({ id: z.string() }))(({ id }) => ({ + const a = mdf(z.object({ id: z.string() }))(({ id }) => ({ resultA: id, })) - const b = makeDomainFunction(z.object({ id: z.string() }))(({ id }) => ({ + const b = mdf(z.object({ id: z.string() }))(({ id }) => ({ resultB: id, })) @@ -148,10 +153,10 @@ describe('merge', () => { }) it('should combine the error messages when both functions fail', async () => { - const a = makeDomainFunction(z.object({ id: z.number() }))(() => { + const a = mdf(z.object({ id: z.number() }))(() => { throw new Error('Error A') }) - const b = makeDomainFunction(z.object({ id: z.number() }))(() => { + const b = mdf(z.object({ id: z.number() }))(() => { throw new Error('Error B') }) diff --git a/src/pipe.test.ts b/src/pipe.test.ts index 277e0fbf..e11dd13e 100644 --- a/src/pipe.test.ts +++ b/src/pipe.test.ts @@ -1,19 +1,17 @@ import { describe, it, assertEquals } from './test-prelude.ts' import { z } from 'https://deno.land/x/zod@v3.21.4/mod.ts' -import { makeDomainFunction } from './constructor.ts' +import { mdf } from './constructor.ts' import { pipe } from './domain-functions.ts' import type { DomainFunction } from './types.ts' import type { Equal, Expect } from './types.test.ts' describe('pipe', () => { it('should compose domain functions from left-to-right', async () => { - const a = makeDomainFunction(z.object({ id: z.number() }))(({ id }) => ({ + const a = mdf(z.object({ id: z.number() }))(({ id }) => ({ id: id + 2, })) - const b = makeDomainFunction(z.object({ id: z.number() }))( - ({ id }) => id - 1, - ) + const b = mdf(z.object({ id: z.number() }))(({ id }) => id - 1) const c = pipe(a, b) type _R = Expect>> @@ -28,13 +26,13 @@ describe('pipe', () => { }) it('should use the same environment in all composed functions', async () => { - const a = makeDomainFunction( + const a = mdf( z.undefined(), z.object({ env: z.number() }), )((_input, { env }) => ({ inp: env + 2, })) - const b = makeDomainFunction( + const b = mdf( z.object({ inp: z.number() }), z.object({ env: z.number() }), )(({ inp }, { env }) => inp + env) @@ -53,13 +51,13 @@ describe('pipe', () => { it('should fail on the first environment parser failure', async () => { const envParser = z.object({ env: z.number() }) - const a = makeDomainFunction( + const a = mdf( z.undefined(), envParser, )((_input, { env }) => ({ inp: env + 2, })) - const b = makeDomainFunction( + const b = mdf( z.object({ inp: z.number() }), envParser, )(({ inp }, { env }) => inp + env) @@ -78,13 +76,13 @@ describe('pipe', () => { it('should fail on the first input parser failure', async () => { const firstInputParser = z.undefined() - const a = makeDomainFunction( + const a = mdf( firstInputParser, z.object({ env: z.number() }), )((_input, { env }) => ({ inp: env + 2, })) - const b = makeDomainFunction( + const b = mdf( z.object({ inp: z.number() }), z.object({ env: z.number() }), )(({ inp }, { env }) => inp + env) @@ -103,13 +101,13 @@ describe('pipe', () => { }) it('should fail on the second input parser failure', async () => { - const a = makeDomainFunction( + const a = mdf( z.undefined(), z.object({ env: z.number() }), )(() => ({ inp: 'some invalid input', })) - const b = makeDomainFunction( + const b = mdf( z.object({ inp: z.number() }), z.object({ env: z.number() }), )(({ inp }, { env }) => inp + env) @@ -128,17 +126,13 @@ describe('pipe', () => { }) it('should compose more than 2 functions', async () => { - const a = makeDomainFunction(z.object({ aNumber: z.number() }))( - ({ aNumber }) => ({ - aString: String(aNumber), - }), - ) - const b = makeDomainFunction(z.object({ aString: z.string() }))( - ({ aString }) => ({ - aBoolean: aString == '1', - }), - ) - const c = makeDomainFunction(z.object({ aBoolean: z.boolean() }))( + const a = mdf(z.object({ aNumber: z.number() }))(({ aNumber }) => ({ + aString: String(aNumber), + })) + const b = mdf(z.object({ aString: z.string() }))(({ aString }) => ({ + aBoolean: aString == '1', + })) + const c = mdf(z.object({ aBoolean: z.boolean() }))( ({ aBoolean }) => !aBoolean, ) diff --git a/src/sequence.test.ts b/src/sequence.test.ts index d13f2533..ea6148b7 100644 --- a/src/sequence.test.ts +++ b/src/sequence.test.ts @@ -1,17 +1,17 @@ import { describe, it, assertEquals } from './test-prelude.ts' import { z } from 'https://deno.land/x/zod@v3.21.4/mod.ts' -import { makeDomainFunction } from './constructor.ts' +import { mdf } from './constructor.ts' import { sequence } from './domain-functions.ts' import type { DomainFunction } from './types.ts' import type { Equal, Expect } from './types.test.ts' describe('sequence', () => { it('should compose domain functions from left-to-right saving the results sequentially', async () => { - const a = makeDomainFunction(z.object({ id: z.number() }))(({ id }) => ({ + const a = mdf(z.object({ id: z.number() }))(({ id }) => ({ id: id + 2, })) - const b = makeDomainFunction(z.object({ id: z.number() }))(({ id }) => ({ + const b = mdf(z.object({ id: z.number() }))(({ id }) => ({ result: id - 1, })) @@ -30,13 +30,13 @@ describe('sequence', () => { }) it('should use the same environment in all composed functions', async () => { - const a = makeDomainFunction( + const a = mdf( z.undefined(), z.object({ env: z.number() }), )((_input, { env }) => ({ inp: env + 2, })) - const b = makeDomainFunction( + const b = mdf( z.object({ inp: z.number() }), z.object({ env: z.number() }), )(({ inp }, { env }) => ({ result: inp + env })) @@ -57,13 +57,13 @@ describe('sequence', () => { it('should fail on the first environment parser failure', async () => { const envParser = z.object({ env: z.number() }) - const a = makeDomainFunction( + const a = mdf( z.undefined(), envParser, )((_input, { env }) => ({ inp: env + 2, })) - const b = makeDomainFunction( + const b = mdf( z.object({ inp: z.number() }), envParser, )(({ inp }, { env }) => inp + env) @@ -82,13 +82,13 @@ describe('sequence', () => { it('should fail on the first input parser failure', async () => { const firstInputParser = z.undefined() - const a = makeDomainFunction( + const a = mdf( firstInputParser, z.object({ env: z.number() }), )((_input, { env }) => ({ inp: env + 2, })) - const b = makeDomainFunction( + const b = mdf( z.object({ inp: z.number() }), z.object({ env: z.number() }), )(({ inp }, { env }) => inp + env) @@ -107,13 +107,13 @@ describe('sequence', () => { }) it('should fail on the second input parser failure', async () => { - const a = makeDomainFunction( + const a = mdf( z.undefined(), z.object({ env: z.number() }), )(() => ({ inp: 'some invalid input', })) - const b = makeDomainFunction( + const b = mdf( z.object({ inp: z.number() }), z.object({ env: z.number() }), )(({ inp }, { env }) => inp + env) @@ -132,19 +132,15 @@ describe('sequence', () => { }) it('should compose more than 2 functions', async () => { - const a = makeDomainFunction(z.object({ aNumber: z.number() }))( - ({ aNumber }) => ({ - aString: String(aNumber), - }), - ) - const b = makeDomainFunction(z.object({ aString: z.string() }))( - ({ aString }) => ({ - aBoolean: aString == '1', - }), - ) - const c = makeDomainFunction(z.object({ aBoolean: z.boolean() }))( - ({ aBoolean }) => ({ anotherBoolean: !aBoolean }), - ) + const a = mdf(z.object({ aNumber: z.number() }))(({ aNumber }) => ({ + aString: String(aNumber), + })) + const b = mdf(z.object({ aString: z.string() }))(({ aString }) => ({ + aBoolean: aString == '1', + })) + const c = mdf(z.object({ aBoolean: z.boolean() }))(({ aBoolean }) => ({ + anotherBoolean: !aBoolean, + })) const d = sequence(a, b, c) type _R = Expect< diff --git a/src/trace.test.ts b/src/trace.test.ts index d950b386..a06397ab 100644 --- a/src/trace.test.ts +++ b/src/trace.test.ts @@ -1,16 +1,14 @@ import { describe, it, assertEquals } from './test-prelude.ts' import { z } from 'https://deno.land/x/zod@v3.21.4/mod.ts' -import { makeDomainFunction } from './constructor.ts' +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' describe('trace', () => { it('intercepts inputs and outputs of a given domain function', async () => { - const a = makeDomainFunction(z.object({ id: z.number() }))( - ({ id }) => id + 1, - ) + const a = mdf(z.object({ id: z.number() }))(({ id }) => id + 1) let contextFromFunctionA: { input: unknown diff --git a/src/types.test.ts b/src/types.test.ts index 5078d234..8360faac 100644 --- a/src/types.test.ts +++ b/src/types.test.ts @@ -1,5 +1,5 @@ // deno-lint-ignore-file ban-ts-comment no-namespace no-unused-vars -import { makeDomainFunction } from './constructor.ts' +import { mdf } from './constructor.ts' import { describe, it, assertEquals } from './test-prelude.ts' import * as Subject from './types.ts' @@ -12,7 +12,7 @@ export type Equal = : false namespace UnpackData { - const result = makeDomainFunction()(() => ({ name: 'foo' } as const)) + const result = mdf()(() => ({ name: 'foo' } as const)) type test = Expect< Equal, { readonly name: 'foo' }> @@ -24,7 +24,7 @@ namespace UnpackData { } namespace UnpackResult { - const result = makeDomainFunction()(() => ({ name: 'foo' })) + const result = mdf()(() => ({ name: 'foo' })) type test = Expect< Equal, Subject.Result<{ name: string }>> @@ -32,7 +32,7 @@ namespace UnpackResult { } namespace UnpackSuccess { - const result = makeDomainFunction()(() => ({ name: 'foo' })) + const result = mdf()(() => ({ name: 'foo' })) type test = Expect< Equal< @@ -78,8 +78,8 @@ namespace AtLeastOne { } namespace UnpackAll { - const dfA = makeDomainFunction()(() => ({ a: 1 } as const)) - const dfB = makeDomainFunction()(() => ({ b: 2 } as const)) + const dfA = mdf()(() => ({ a: 1 } as const)) + const dfB = mdf()(() => ({ b: 2 } as const)) type Result = Subject.UnpackAll<[typeof dfA, typeof dfB]>