Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

composable-functions

Package Overview
Dependencies
Maintainers
2
Versions
24
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

composable-functions - npm Package Compare versions

Comparing version 4.0.0 to 4.0.1

logo.png

12

esm/combinators.js

@@ -144,3 +144,3 @@ import { composable, failure, fromSuccess, success } from './constructors.js';

function map(fn, mapper) {
return (async (...args) => {
return async (...args) => {
const result = await fn(...args);

@@ -150,3 +150,3 @@ if (!result.success)

return composable(mapper)(result.data, ...args);
});
};
}

@@ -212,3 +212,3 @@ /**

function mapErrors(fn, mapper) {
return (async (...args) => {
return async (...args) => {
const res = await fn(...args);

@@ -224,3 +224,3 @@ if (res.success)

}
});
};
}

@@ -246,3 +246,3 @@ /**

function trace(traceFn) {
return (fn) => (async (...args) => {
return (fn) => async (...args) => {
const originalResult = await fn(...args);

@@ -253,3 +253,3 @@ const traceResult = await composable(traceFn)(originalResult, ...args);

return failure(traceResult.errors);
});
};
}

@@ -256,0 +256,0 @@ /**

@@ -61,3 +61,3 @@ import { mapErrors } from './combinators.js';

function fromSuccess(fn, onError = (e) => e) {
return (async (...args) => {
return async (...args) => {
const result = await mapErrors(fn, onError)(...args);

@@ -67,3 +67,3 @@ if (result.success)

throw new ErrorList(result.errors);
});
};
}

@@ -115,3 +115,3 @@ /**

function applySchema(inputSchema, environmentSchema) {
return (fn) => {
return ((fn) => {
return ((input, environment) => {

@@ -121,8 +121,4 @@ const envResult = (environmentSchema ?? alwaysUnknownSchema).safeParse(environment);

if (!result.success || !envResult.success) {
const inputErrors = result.success
? []
: result.error.issues.map((error) => new InputError(error.message, error.path));
const envErrors = envResult.success
? []
: envResult.error.issues.map((error) => new EnvironmentError(error.message, error.path));
const inputErrors = result.success ? [] : result.error.issues.map((error) => new InputError(error.message, error.path));
const envErrors = envResult.success ? [] : envResult.error.issues.map((error) => new EnvironmentError(error.message, error.path));
return Promise.resolve(failure([...inputErrors, ...envErrors]));

@@ -132,3 +128,3 @@ }

});
};
});
}

@@ -135,0 +131,0 @@ const alwaysUnknownSchema = {

@@ -21,3 +21,3 @@ import * as A from '../combinators.js';

* const d = environment.pipe(a, b)
* // ^? Composable<(input?: unknown, environment?: unknown) => { aBoolean: boolean }>
* // ^? ComposableWithSchema<{ aBoolean: boolean }>
* ```

@@ -39,3 +39,3 @@ */

* const aComposable = environment.sequence(a, b)
* // ^? Composable<(input?: unknown, environment?: unknown) => [string, boolean]>
* // ^? ComposableWithSchema<[string, boolean]>
* ```

@@ -42,0 +42,0 @@ */

@@ -13,3 +13,3 @@ # Migrating from domain-functions

- 🛠️ Incremental Migration: Seamlessly migrate your existing codebase incrementally. **Both `domain-functions` and `composable-functions` can coexist**, allowing you to transition module by module.
- 🛟 Enhanced Combinators: New and improved combinators like [`map`](./API.md#map), [`mapParameters`](./API.md#mapparameters), [`mapErrors`](./API.md#maperrors) and [`catchFailure`](./API.md#catchfailure) provide more control over handling and error transformation, making your **code more resilient**.
- 🛟 Enhanced Combinators: New and improved combinators like [`map`](./API.md#map), [`mapParameters`](./API.md#mapparameters), [`mapErrors`](./API.md#maperrors) and [`catchFailure`](./API.md#catchfailure) provide more control over error handling and transformation, making your **code more resilient**.

@@ -39,3 +39,3 @@ # Table of contents

## First steps
The first thing you want to know is that the old `DomainFunction<T>` is equivalent to `Composable<(input?: unknown, environment?: unknwon) => T>`. We brought the arguments to the type signature se we could type check the compositions. A [commonly requested feature](https://github.com/seasonedcc/domain-functions/issues/80) in domain-functions.
The first thing you want to know is that the old `DomainFunction<T>` is equivalent to `Composable<(input?: unknown, environment?: unknown) => T>` (AKA `ComposableWithSchema<T>`). We brought the arguments to the type signature so we could type check the compositions. A [commonly requested feature](https://github.com/seasonedcc/domain-functions/issues/80) in domain-functions.

@@ -241,2 +241,12 @@ A composable does not need a schema, but you can still use one for runtime assertion. What we used to call a Domain Function is now a Composable with [environment](./environments.md) and a schema.

- Elsewhere, collect the inputErrors and environmentErrors with the [`isInputError`](./API.md#isinputerror) and [`isEnvironmentError`](./API.md#isenvironmenterror) functions.
```ts
// replace this
if (result.inputErrors.length > 0) {
return result.inputErrors[0].message
}
// with this
if (result.errors.some(isInputError)) {
return result.errors.find(isInputError).message
}
```

@@ -281,3 +291,3 @@ # Equivalence tables

|---|---|
| `DomainFunction<string>` | `Composable<(input?: unknown, environment?: unknown) => string>` |
| `DomainFunction<string>` | `ComposableWithSchema<string>` |
| `SuccessResult<T>` | `Success<T>` |

@@ -293,1 +303,4 @@ | `ErrorResult` | `Failure` |

| -- | with `serialize`: `{ success: false, errors: [{ message: 'Something went wrong', name: 'Error', path: [] }, { message: 'Required', name: 'InputError', path: ['name'] }, { message: 'Unauthorized', name: 'EnvironmentError', path: ['user'] }] }` |
| `result.inputErrors[0]?.message` | `result.errors.find(isInputError)?.message` |
| `result.environmentErrors[0]?.message` | `result.errors.find(isEnvironmentError)?.message` |
| `result.errors[0]?.exception instanceof CustomError` | `result.errors[0] instanceof CustomError` |
{
"name": "composable-functions",
"version": "4.0.0",
"version": "4.0.1",
"description": "Types and functions to make composition easy and safe",

@@ -5,0 +5,0 @@ "author": "Seasoned",

<p align="center">
<img width="300" src="https://github.com/seasonedcc/composable-functions/assets/566971/b786f157-9fb1-4506-9ddb-e438dcde06c8" alt="Composable Functions" />
<img width="300" src="./logo.png" alt="Composable Functions" />
</p>

@@ -8,3 +8,3 @@

- 🛟 Type-Safe Compositions: Ensure robust type-safety during function composition, preventing incompatible functions from being combined and reducing runtime errors.
- 🔄 Promise and Error Handling: Focus on the happy-path of your functions eliminating the need for verbose try/catch syntax.
- 🔄 Promise and Error Handling: Focus on the happy-path of your functions, eliminating the need for verbose try/catch syntax.
- 🏝️ Isolated Business Logic: Split your code into composable functions, making your code easier to test and maintain.

@@ -131,3 +131,3 @@ - 🔒 End-to-End Type Safety: Achieve end-to-end type safety from the backend to the UI with serializable results, ensuring data integrity across your entire application.

Since pipe will compose from left to right, the only `string` output from `toString` will not fit into the first argument of `add` which is a `number`.
The error message comes in the form of an inferred `FailToCompose` type, this failure type is not callable therefore it will break any attempts to call `addAndReturnString`.
The error message comes in the form of an inferred `FailToCompose` type. This failure type is not callable, therefore it will break any attempts to call `addAndReturnString`.

@@ -168,3 +168,3 @@ ### Using non-composables (mapping)

You can throw anything derived from `Error`. Check [this documentation](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error#custom_error_types) on how to define your custom errors. The library will also wrap anything that does not extends `Error`, just to keep compatibility with code-bases that throw strings or objects.
You can throw anything derived from `Error`. Check [this documentation](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error#custom_error_types) on how to define your custom errors. The library will also wrap anything that does not extend `Error`, just to keep compatibility with code-bases that throw strings or objects.

@@ -189,7 +189,7 @@ ```typescript

The library defines a few custom errors out of the box but these will be more important later on, whend dealing with external input and schemas.
The library defines a few custom errors out of the box but these will be more important later on, when dealing with external input and schemas.
See [the errors module](./src/errors.ts) for more details.
### Catching
You can catch an error in a `Composable`, using `catchFailure` which is similar to `map` but will run whenever the first composable fails:
You can catch an error in a `Composable` using `catchFailure`, which is similar to `map` but will run whenever the first composable fails:

@@ -247,3 +247,3 @@ ```typescript

You can also use it within other composables whenever the composition utilities fall short, the error will be propagated as `ErrorList` and available in the caller `Result`.
You can also use it within other composables whenever the composition utilities fall short. In that case, the error will be propagated as `ErrorList` and available in the caller `Result`.

@@ -275,3 +275,3 @@ ```ts

This documentation will use Node.JS imports by convention, just replace `composable-functions` with `https://deno.land/x/composable_functions/mod.ts` when using [Deno](https://deno.land/).
This documentation will use Node.JS imports by convention. Just replace `composable-functions` with `https://deno.land/x/composable_functions/mod.ts` when using [Deno](https://deno.land/).

@@ -278,0 +278,0 @@

@@ -152,3 +152,3 @@ "use strict";

function map(fn, mapper) {
return (async (...args) => {
return async (...args) => {
const result = await fn(...args);

@@ -158,3 +158,3 @@ if (!result.success)

return (0, constructors_js_1.composable)(mapper)(result.data, ...args);
});
};
}

@@ -223,3 +223,3 @@ exports.map = map;

function mapErrors(fn, mapper) {
return (async (...args) => {
return async (...args) => {
const res = await fn(...args);

@@ -235,3 +235,3 @@ if (res.success)

}
});
};
}

@@ -258,3 +258,3 @@ exports.mapErrors = mapErrors;

function trace(traceFn) {
return (fn) => (async (...args) => {
return (fn) => async (...args) => {
const originalResult = await fn(...args);

@@ -265,3 +265,3 @@ const traceResult = await (0, constructors_js_1.composable)(traceFn)(originalResult, ...args);

return (0, constructors_js_1.failure)(traceResult.errors);
});
};
}

@@ -268,0 +268,0 @@ exports.trace = trace;

@@ -67,3 +67,3 @@ "use strict";

function fromSuccess(fn, onError = (e) => e) {
return (async (...args) => {
return async (...args) => {
const result = await (0, combinators_js_1.mapErrors)(fn, onError)(...args);

@@ -73,3 +73,3 @@ if (result.success)

throw new errors_js_1.ErrorList(result.errors);
});
};
}

@@ -123,3 +123,3 @@ exports.fromSuccess = fromSuccess;

function applySchema(inputSchema, environmentSchema) {
return (fn) => {
return ((fn) => {
return ((input, environment) => {

@@ -129,8 +129,4 @@ const envResult = (environmentSchema ?? alwaysUnknownSchema).safeParse(environment);

if (!result.success || !envResult.success) {
const inputErrors = result.success
? []
: result.error.issues.map((error) => new errors_js_1.InputError(error.message, error.path));
const envErrors = envResult.success
? []
: envResult.error.issues.map((error) => new errors_js_1.EnvironmentError(error.message, error.path));
const inputErrors = result.success ? [] : result.error.issues.map((error) => new errors_js_1.InputError(error.message, error.path));
const envErrors = envResult.success ? [] : envResult.error.issues.map((error) => new errors_js_1.EnvironmentError(error.message, error.path));
return Promise.resolve(failure([...inputErrors, ...envErrors]));

@@ -140,3 +136,3 @@ }

});
};
});
}

@@ -143,0 +139,0 @@ exports.applySchema = applySchema;

@@ -47,3 +47,3 @@ "use strict";

* const d = environment.pipe(a, b)
* // ^? Composable<(input?: unknown, environment?: unknown) => { aBoolean: boolean }>
* // ^? ComposableWithSchema<{ aBoolean: boolean }>
* ```

@@ -66,3 +66,3 @@ */

* const aComposable = environment.sequence(a, b)
* // ^? Composable<(input?: unknown, environment?: unknown) => [string, boolean]>
* // ^? ComposableWithSchema<[string, boolean]>
* ```

@@ -69,0 +69,0 @@ */

@@ -1,3 +0,2 @@

import type { BranchReturn, CanComposeInParallel, CanComposeInSequence, Composable, MergeObjects, PipeReturn, RecordToTuple, Result, SequenceReturn, UnpackData } from './types.js';
import { Internal } from './internal/types.js';
import type { BranchReturn, CanComposeInParallel, CanComposeInSequence, Composable, MapParametersReturn, MergeObjects, PipeReturn, RecordToTuple, Result, SequenceReturn, UnpackData } from './types.js';
/**

@@ -66,3 +65,5 @@ * Merges a list of objects into a single object.

*/
declare function all<Fns extends Composable[]>(...fns: Fns): Composable<(...args: Parameters<NonNullable<CanComposeInParallel<Fns>[0]>>) => { [k in keyof Fns]: UnpackData<Fns[k]>; }>;
declare function all<Fns extends Composable[]>(...fns: Fns): Composable<(...args: Parameters<NonNullable<CanComposeInParallel<Fns>[0]>>) => {
[k in keyof Fns]: UnpackData<Fns[k]>;
}>;
/**

@@ -82,3 +83,5 @@ * Composes functions to run in parallel returning a record with same keys as inputs with respective results when all are successful.

*/
declare function collect<Fns extends Record<string, Composable>>(fns: Fns): Composable<(...args: Parameters<Exclude<CanComposeInParallel<RecordToTuple<Fns>>[0], undefined>>) => { [key in keyof Fns]: UnpackData<Fns[key]>; }>;
declare function collect<Fns extends Record<string, Composable>>(fns: Fns): Composable<(...args: Parameters<Exclude<CanComposeInParallel<RecordToTuple<Fns>>[0], undefined>>) => {
[key in keyof Fns]: UnpackData<Fns[key]>;
}>;
/**

@@ -130,3 +133,3 @@ * Works like `pipe` but it will collect the output of every function in a tuple.

*/
declare function mapParameters<Fn extends Composable, NewParameters extends unknown[], const O extends Parameters<Fn>>(fn: Fn, mapper: (...args: NewParameters) => Promise<O> | O): Composable<(...args: NewParameters) => Internal.IsNever<Awaited<O>> extends true ? never : UnpackData<Fn>>;
declare function mapParameters<Fn extends Composable, NewParameters extends unknown[], const MapperOutput extends Parameters<Fn>>(fn: Fn, mapper: (...args: NewParameters) => Promise<MapperOutput> | MapperOutput): MapParametersReturn<Fn, NewParameters, MapperOutput>;
/**

@@ -161,3 +164,3 @@ * Try to recover from a resulting Failure. When the given function succeeds, its result is returned without changes.

*/
declare function mapErrors<Fn extends Composable>(fn: Fn, mapper: (err: Error[]) => Error[] | Promise<Error[]>): Fn;
declare function mapErrors<P extends unknown[], Output>(fn: Composable<(...args: P) => Output>, mapper: (err: Error[]) => Error[] | Promise<Error[]>): Composable<(...args: P) => Output>;
/**

@@ -181,3 +184,3 @@ * Whenever you need to intercept inputs and a composable result without changing them you can use this function.

*/
declare function trace(traceFn: (result: Result<unknown>, ...originalInput: unknown[]) => Promise<void> | void): <Fn extends Composable>(fn: Fn) => Fn;
declare function trace(traceFn: (result: Result<unknown>, ...originalInput: unknown[]) => Promise<void> | void): <P extends unknown[], Output>(fn: Composable<(...args: P) => Output>) => Composable<(...args: P) => Output>;
/**

@@ -184,0 +187,0 @@ * Compose 2 functions conditionally.

@@ -1,3 +0,2 @@

import type { Composable, Failure, ParserSchema, Success } from './types.js';
import { UnpackData } from './types.js';
import type { ApplySchemaReturn, Composable, ComposableWithSchema, Failure, ParserSchema, Success } from './types.js';
/**

@@ -16,3 +15,3 @@ * It receives any data (T) and returns a Success<T> object.

*/
declare function composable<T extends (...args: any[]) => any>(fn: T): Composable<T>;
declare function composable<T extends Function>(fn: T): Composable<T extends (...args: any[]) => any ? T : never>;
/**

@@ -33,3 +32,3 @@ * It can be used to call a composable from another composable. It will return the output of the given function if it was successfull, otherwise it will throw a `ErrorList` that will bubble up to the parent function.

*/
declare function fromSuccess<O, T extends Composable<(...a: any[]) => O>>(fn: T, onError?: (errors: Error[]) => Error[] | Promise<Error[]>): T extends Composable<(...a: infer P) => infer O> ? (...args: P) => Promise<O> : never;
declare function fromSuccess<O, P extends any[]>(fn: Composable<(...a: P) => O>, onError?: (errors: Error[]) => Error[] | Promise<Error[]>): (...args: P) => Promise<O>;
/**

@@ -54,3 +53,3 @@ * Creates a composable with unknown input and environment that uses schemas to parse them into known types.

*/
declare function withSchema<I, E>(inputSchema?: ParserSchema<I>, environmentSchema?: ParserSchema<E>): <Output>(handler: (input: I, environment: E) => Output) => Composable<(input?: unknown, environment?: unknown) => Awaited<Output>>;
declare function withSchema<I, E>(inputSchema?: ParserSchema<I>, environmentSchema?: ParserSchema<E>): <Output>(hander: (input: I, environment: E) => Output) => ComposableWithSchema<Output>;
/**

@@ -78,3 +77,3 @@ * Takes a composable and creates a composable withSchema that will assert the input and environment types according to the given schemas.

*/
declare function applySchema<I, E>(inputSchema?: ParserSchema<I>, environmentSchema?: ParserSchema<E>): <A extends Composable>(fn: A) => Composable<(input?: unknown, environment?: unknown) => UnpackData<A>>;
declare function applySchema<ParsedInput, ParsedEnvironment>(inputSchema?: ParserSchema<ParsedInput>, environmentSchema?: ParserSchema<ParsedEnvironment>): <R, Input, Environment>(fn: Composable<(input?: Input | undefined, environment?: Environment | undefined) => R>) => ApplySchemaReturn<ParsedInput, ParsedEnvironment, Composable<(input?: Input | undefined, environment?: Environment | undefined) => R>>;
export { applySchema, composable, failure, fromSuccess, success, withSchema };

@@ -18,3 +18,3 @@ import type { Composable, UnpackData } from '../types.js';

* const d = environment.pipe(a, b)
* // ^? Composable<(input?: unknown, environment?: unknown) => { aBoolean: boolean }>
* // ^? ComposableWithSchema<{ aBoolean: boolean }>
* ```

@@ -34,3 +34,3 @@ */

* const aComposable = environment.sequence(a, b)
* // ^? Composable<(input?: unknown, environment?: unknown) => [string, boolean]>
* // ^? ComposableWithSchema<[string, boolean]>
* ```

@@ -37,0 +37,0 @@ */

export { applySchema, composable, failure, fromSuccess, success, withSchema, } from './constructors.js';
export { all, branch, catchFailure, collect, map, mapErrors, mapParameters, mergeObjects, pipe, sequence, trace, } from './combinators.js';
export { inputFromForm, inputFromFormData, inputFromSearch, inputFromUrl, } from './input-resolvers.js';
export type { QueryStringRecord, RequestLike, FormDataLike, } from './input-resolvers.js';
export type { FormDataLike, QueryStringRecord, RequestLike, } from './input-resolvers.js';
export { serialize, serializeError } from './serializer.js';
export { EnvironmentError, ErrorList, InputError, isEnvironmentError, isInputError, } from './errors.js';
export type { BranchReturn, CanComposeInParallel, CanComposeInSequence, Composable, Failure, MergeObjects, ParserSchema, PipeReturn, Result, SequenceReturn, SerializableError, SerializableResult, Success, UnpackAll, UnpackData, } from './types.js';
export type { ApplySchemaReturn, BranchReturn, CanComposeInParallel, CanComposeInSequence, Composable, ComposableWithSchema, FailToCompose, Failure, IncompatibleArguments, MapParametersReturn, MergeObjects, ParserSchema, PipeReturn, Result, SequenceReturn, SerializableError, SerializableResult, Success, UnpackAll, UnpackData, } from './types.js';
export * as environment from './environment/index.js';

@@ -88,3 +88,3 @@ /// <reference types="node" />

declare function inputFromUrl(request: RequestLike): QueryStringRecord;
export type { QueryStringRecord, FormDataLike, RequestLike };
export type { FormDataLike, QueryStringRecord, RequestLike };
export { inputFromForm, inputFromFormData, inputFromSearch, inputFromUrl };

@@ -1,2 +0,2 @@

import { Internal } from './internal/types.js';
import type { Internal } from './internal/types.js';
/**

@@ -43,2 +43,6 @@ * The failure case of a Result.

/**
* A composable async function with schema validation at runtime.
*/
type ComposableWithSchema<O> = Composable<(input?: unknown, environment?: unknown) => O>;
/**
* Extract the type of the returned data when a Composable is successful.

@@ -144,2 +148,18 @@ */

]>[0]>) => null extends Awaited<ReturnType<Resolver>> ? UnpackData<SourceComposable> | UnpackData<Extract<Awaited<ReturnType<Resolver>>, Composable>> : UnpackData<Extract<Awaited<ReturnType<Resolver>>, Composable>>> : CanComposeInSequence<[SourceComposable, Awaited<ReturnType<Resolver>>]> : CanComposeInSequence<[SourceComposable, Composable<Resolver>]>;
export type { BranchReturn, CanComposeInParallel, CanComposeInSequence, Composable, Failure, Last, MergeObjects, ParserSchema, PipeReturn, RecordToTuple, Result, SequenceReturn, SerializableError, SerializableResult, Success, UnpackAll, UnpackData, };
/**
* Ensure that schemas are compatible with composable input and environment otherwise return a FailToCompose.
*/
type ApplySchemaReturn<ParsedInput, ParsedEnvironment, Fn extends Composable> = ParsedInput extends Parameters<Fn>[0] ? ParsedEnvironment extends Parameters<Fn>[1] ? ComposableWithSchema<UnpackData<Fn>> : FailToCompose<ParsedEnvironment, Parameters<Fn>[1]> : FailToCompose<ParsedInput, Parameters<Fn>[0]>;
/**
* The return type of the mapParameters function
*/
type MapParametersReturn<Fn extends Composable, NewParams extends any[], O extends Parameters<Fn>> = Composable<(...args: NewParams) => Internal.IsNever<O> extends true ? never : UnpackData<Fn>>;
/**
* A type that represents an error when composing functions with incompatible arguments.
*/
type IncompatibleArguments = Internal.IncompatibleArguments;
/**
* This is IncompatibleArguments supertype where we include the arguments that caused the incompatibility.
*/
type FailToCompose<A, B> = Internal.FailToCompose<A, B>;
export type { ApplySchemaReturn, BranchReturn, CanComposeInParallel, CanComposeInSequence, Composable, ComposableWithSchema, FailToCompose, Failure, IncompatibleArguments, Last, MapParametersReturn, MergeObjects, ParserSchema, PipeReturn, RecordToTuple, Result, SequenceReturn, SerializableError, SerializableResult, Success, UnpackAll, UnpackData, };
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc