diff --git a/README.md b/README.md index 3086b372..be782d0b 100644 --- a/README.md +++ b/README.md @@ -103,7 +103,7 @@ This documentation will use Node.JS imports by convention, just replace `domain- ## Create your first action with Remix ```tsx -import type { ActionFunction } from 'remix' +import type { DataFunctionArgs } from 'remix' import { useActionData, redirect } from 'remix' // You can also use the short version of makeDomainFunction: mdf import { mdf, inputFromForm } from 'domain-functions' @@ -111,7 +111,7 @@ import * as z from 'zod' const schema = z.object({ number: z.coerce.number() }) -export const action: ActionFunction = async ({ request }) => { +export async function action({ request }: DataFunctionArgs) { const increment = mdf(schema)(({ number }) => number + 1) const result = await increment(await inputFromForm(request)) @@ -121,7 +121,8 @@ export const action: ActionFunction = async ({ request }) => { } export default function Index() { - const actionData = useActionData() + const actionData = useActionData() + // ^? ErrorResult | null return (
@@ -237,6 +238,7 @@ const alwaysFails = mdf(input, environment)(async () => { }) const failedResult = await alwaysFails(someInput) +// ^? Result /* failedResult = { success: false, @@ -255,6 +257,7 @@ const alwaysFails = mdf(input, environment)(async () => { }) const failedResult = await alwaysFails(someInput) +// ^? Result /* failedResult = { success: false, @@ -333,9 +336,10 @@ 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 }) +// ^? Result<[string, number, boolean]> ``` -For the example above, the result type will be `Result<[string, number, boolean]>`: +For the example above, the result will be: ```ts { @@ -358,6 +362,7 @@ const b = mdf(z.object({ id: z.number() }))(() => { }) const results = await all(a, b)({ id: 1 }) +// ^? Result<[never, never]> /*{ success: false, @@ -382,9 +387,10 @@ const b = mdf(z.object({}))(() => 2) const c = mdf(z.object({}))(() => true) const results = await collect({ a, b, c })({}) +// ^? Result<{ a: string, b: number, c: boolean }> ``` -For the example above, the result type will be `Result<{ a: string, b: number, c: boolean }>`: +For the example above, the result will be: ```ts { @@ -420,10 +426,10 @@ const b = mdf(z.object({}))(() => ({ resultB: 2 })) const c = mdf(z.object({}))(async () => ({ resultC: true })) const results = await merge(a, b, c)({}) +// ^? Result<{ resultA: string, resultB: number, resultC: boolean }> ``` -For the example above, the result type will be `Result<{ resultA: string, resultB: number, resultC: boolean }>`: - +For the example above, the result will be: ```ts { success: true, @@ -434,17 +440,6 @@ For the example above, the result type will be `Result<{ resultA: string, result } ``` -__Be mindful of__ each constituent domain function's return type. If any domain function returns something other than an object, the composite domain function will return an `ErrorResult`: - -```ts -{ - success: false, - errors: [{ message: 'Invalid data format returned from some domain functions' }], - inputErrors: [], - environmentErrors: [], -} -``` - ### first `first` will create a composite domain function that will return the result of the first successful constituent domain function. It handles inputs and environments like the `all` function. @@ -459,9 +454,10 @@ const b = mdf( )(({ n }) => n - 1) const result = await first(a, b)({ n: 1, operation: 'increment' }) +// ^? Result ``` -For the example above, the result type will be `Result`: +For the example above, the result will be: ```ts { @@ -484,7 +480,8 @@ const b = mdf(z.object({ operation: z.literal('B') }))(() => ({ })) const result = await first(a, b)({ operation: 'A' }) -// ^-- Result<{ resultA: string } | { resultB: string }> +// ^? Result<{ resultA: string } | { resultB: string }> + if (!result.success) return console.log('No function was successful') if ('resultA' in result.data) return console.log('function A succeeded') return console.log('function B succeeded') @@ -501,6 +498,7 @@ const b = mdf(z.object({ id: z.number() }))(() => { }) const result = await first(a, b)({ id: 1 }) +// ^? Result /*{ success: false, @@ -539,9 +537,10 @@ const c = mdf(z.object({ aBoolean: z.boolean() }))( const d = pipe(a, b, c) const result = await d({ aNumber: 1 }) +// ^? Result ``` -For the example above, the result type will be `Result`: +For the example above, the result will be: ```ts { @@ -567,9 +566,10 @@ const b = mdf(z.string())((aString) => aString === '1') const c = sequence(a, b) const result = await c(1) +// ^? Result<[string, boolean]> ``` -For the example above, the result type will be `Result<[string, boolean]>`: +For the example above, the result will be: ```ts { @@ -596,10 +596,9 @@ const b = mdf(z.object({ aString: z.string() }))( const c = map(sequence(a, b), mergeObjects) const result = await c(1) +// ^? Result<{ aString: string, aBoolean: boolean }> ``` -For the example above, the result type will be `Result<{ aString: string, aBoolean: boolean }>`. - ### collectSequence `collectSequence` is very similar to the `collect` function, except __it runs in the sequence of the keys' order like a `pipe`__. @@ -618,9 +617,10 @@ const b = mdf(z.string())((aString) => aString === '1') const c = collectSequence({ a, b }) const result = await c(1) +// ^? Result<{ a: string, b: boolean }> ``` -For the example above, the result type will be `Result<{ a: string, b: boolean }>`: +For the example above, the result will be: ```ts { @@ -632,26 +632,6 @@ For the example above, the result type will be `Result<{ a: string, b: boolean } } ``` -If you'd rather have an object instead of a tuple (similar to the `merge` function), you can use the `map` and `mergeObjects` functions like so: - -```ts -import { mergeObjects } from 'domain-functions' - -const a = mdf(z.number())((aNumber) => ({ - aString: String(aNumber) -})) -const b = mdf(z.object({ aString: z.string() }))( - ({ aString }) => ({ aBoolean: aString === '1' }) -) - -const c = map(sequence(a, b), mergeObjects) - -const result = await c(1) -``` - -For the example above, the result type will be `Result<{ aString: string, aBoolean: boolean }>`. - - ### branch Use `branch` to add conditional logic to your domain functions' compositions. @@ -673,8 +653,9 @@ const findUserByIdOrEmail = branch( (output) => (typeof output === "number" ? findUserById : findUserByEmail), ) const result = await findUserByIdOrEmail({ id: 1 }) +// ^? Result ``` -For the example above, the result type will be `Result`: +For the example above, the result will be: ```ts { success: true, @@ -701,6 +682,7 @@ const findUserByIdOrEmail = branch( throw new Error("Invalid input") }, ) +// ^? DomainFunction ``` For the example above, the result type will be `ErrorResult`: ```ts @@ -738,9 +720,10 @@ const fetchFullName = pipe( ) const result = fetchFullName({ userId: 2 }) +// ^? Result ``` -For the example above, the result type will be `Result` and its value something like this: +For the example above, the result will be something like this: ```ts { @@ -804,10 +787,7 @@ const domainFunctionA = mdf( )(async ({ id }) => { const valueB = await fromSuccess(domainFunctionB)({ userId: id }) // do something else - return { - valueA, - valueB, - } + return { valueA, valueB } }) ``` @@ -822,11 +802,11 @@ Object properties from the rightmost object will take precedence over the leftmo const a = { a: 1, b: 2 } const b = { b: '3', c: '4' } const result = mergeObjects([a, b]) +// ^? { a: number, b: string, c: string } ``` The resulting object will be: ```ts { a: 1, b: '3', c: '4' } -// inferred as { a: number, b: string, c: string } ``` @@ -841,7 +821,7 @@ The resulting object will be: const fn = mdf()(async () => '') type Data = UnpackData -// Data = string +// ^? string ``` ### UnpackSuccess @@ -852,8 +832,8 @@ type Data = UnpackData const fn = mdf()(async () => '') type Success = UnpackSuccess -// Success = { success: true, data: string, errors: [], inputErrors: [], environmentErrors: [] } -// Which is the same as: SuccessResult +// ^? SuccessResult +// Which is the same as: { success: true, data: string, errors: [], inputErrors: [], environmentErrors: [] } ``` ### UnpackResult @@ -863,19 +843,11 @@ type Success = UnpackSuccess const fn = mdf()(async () => '') type Result = UnpackResult -/* -Result = - | { success: true, data: string, errors: [], inputErrors: [], environmentErrors: [], } - | { success: false, errors: { message: string }[], inputErrors: SchemaError[], environmentErrors: SchemaError[] } - -* Which is the same as: -Result -* Which is the same as: -SuccessResult | ErrorResult -*/ +// ^? Result +// Which is the same as: { success: true, data: string, errors: [], inputErrors: [], environmentErrors: [], } | { success: false, errors: { message: string }[], inputErrors: SchemaError[], environmentErrors: SchemaError[] } +// Or the same as: SuccessResult | ErrorResult ``` - ## Extracting input values for domain functions We export some functions to help you extract values out of your requests before sending them as user input.