Security News
Opengrep Emerges as Open Source Alternative Amid Semgrep Licensing Controversy
Opengrep forks Semgrep to preserve open source SAST in response to controversial licensing changes.
fp-ts-bootstrap
Advanced tools
This is a module aimed at application bootstrapping using types from fp-ts. Its ideas and most of the code were ported from the fluture-hooks library.
This module mainly provides a Bracket type with accompanying type
class instances. The Bracket type is a drop-in replacement for the Cont type
from fp-ts-cont, but specialized in returning TaskEither
. This solves the
problem stipulated at the end of application bootstrapping with fp-ts by
allowing the return type to be threaded through the program. Furthermore, it
makes the ApplicativePar
instance possible, which allows for parallel
composition of bracketed resources.
Besides the Bracket type, this module also provides a Service type which is a small layer on top for managing dependencies through the Reader monad.
Define your service. See the full example in
./example/services/server.ts
.
export const withServer: Service.Service<Error, Dependencies, HTTP.Server> = (
({port, app}) => Bracket.bracket(
() => new Promise(resolve => {
const server = HTTP.createServer(app);
server.listen(port, () => resolve(E.right(server)));
}),
server => () => new Promise(resolve => {
server.close((e: unknown) => resolve(
e instanceof Error ? E.left(e) : E.right(undefined)
));
}),
)
);
Combine multiple such services with ease using Do notation. See the full example
in ./example/services/index.ts
.
export const withServices = pipe(
withEnv,
Bracket.bindTo('env'),
Bracket.bind('logger', ({env}) => withLogger({level: env.LOG_LEVEL})),
Bracket.bind('database', ({env, logger}) => withDatabase({
url: env.DATABASE_URL,
logger: logger
})),
Bracket.bind('app', ({database}) => withApp({database})),
Bracket.bind('server', ({env, app}) => withServer({
port: env.PORT,
app: app,
})),
);
Consume your service. See the full example in ./example/index.ts
.
const program = withServices(({server, logger}) => pipe(
TE.fromIO(logger.info(`Server listening on ${JSON.stringify(server.address())}`)),
TE.apSecond(TE.fromTask(() => new Promise(resolve => {
process.once('SIGINT', resolve);
}))),
TE.chain(() => TE.fromIO(logger.info('Shutting down app'))),
));
And finally, run your program:
program().then(E.fold(console.error, console.log), console.error);
import {Bracket} from 'fp-ts-bootstrap';
type Bracket<E, R> = (
<T>(consume: (resource: R) => TaskEither<E, T>) => TaskEither<E, T>
);
The Bracket type aliases the structure that's encountered when using a curried
variant of fp-ts' TaskEither.bracket
function. This curried variant is
also exported from the Bracket module as bracket
. It models a bracketed
resource for which the consumption hasn't been specified yet.
The Bracket module defines various type class instances for Bracket
that allow
you to compose and combine multiple bracketed resources. From most instances,
some derivative functions are exported as well.
of
, Do
map
, flap
, bindTo
, let
ap
, apFirst
, apSecond
, apS
, getApplySemigroup
, sequenceT
, sequenceS
chain
, chainFirst
, bind
apPar
, apFirstPar
, apSecondPar
, apSPar
, getApplySemigroupPar
, sequenceTPar
, sequenceSPar
import {Service} from 'fp-ts-bootstrap';
type Service<E, D, S> = Reader<D, Bracket<E, S>>;
The Service type is a small layer on top of Reader that formalizes the
type of a Bracket with dependencies. The Service type can also be composed and
combined using the utilities provided by ReaderT<Bracket>
. These utilities
are re-exported from the Service module.
import * as FS from 'fs/promises';
import * as TE from 'fp-ts/TaskEither';
import * as E from 'fp-ts/Either';
import {Bracket} from 'fp-ts-bootstrap';
const acquireFileHandle = (url: string) => (
TE.tryCatch(() => FS.open(url, 'a'), E.toError)
);
const disposeFileHandle = (file: FS.FileHandle) => (
TE.tryCatch(() => file.close(), E.toError)
);
const withMyFile = Bracket.bracket(
acquireFileHandle('/tmp/my-file.txt'),
disposeFileHandle,
);
This recipe builds on the previous one by adding dependencies to the service.
import {Service} from 'fp-ts-bootstrap/lib/Service';
type Dependencies = {
url: string;
};
const withMyFile: Service<Error, Dependencies, FS.FileHandle> = (
({url}) => Bracket.bracket(
acquireFileHandle(url),
disposeFileHandle,
)
);
The Bracket type has a sequential Applicative
instance that it uses by
default, but there's also a parallel ApplicativePar
instance that you can use
to combine services in parallel*. Two very useful derivative function using
ApplicativePar
are
sequenceSPar
for building a Struct of resources from a Struct of Brackets; andapSPar
for adding another property to an existing Struct of services:import {pipe} from 'fp-ts/function';
import {Bracket} from 'fp-ts-bootstrap';
const withServices = pipe(
Bracket.sequenceSPar({
env: withEnv,
logger: withLogger({level: 'info'}),
}),
Bracket.apSPar('database', withDatabase({url: 'postgres://localhost:5432'}))
);
const program = withServices(({env, logger, database}) => pipe(
// ...
));
* By "in parallel" we mean that the services are acquired in parallel, but
disposed in sequence. This is a technical limitation that exists to ensure that
the ApplyPar
instance is lawful.
import {pipe} from 'fp-ts/function';
import {Bracket} from 'fp-ts-bootstrap';
const withServices = pipe(
withEnv,
Bracket.bindTo('env'),
Bracket.bind('logger', ({env}) => withLogger({level: env.LOG_LEVEL})),
Bracket.bind('database', ({env, logger}) => withDatabase({
url: env.DATABASE_URL,
logger: logger
})),
Bracket.bind('server', ({env, database}) => withServer({
port: env.PORT,
app: app,
database: database,
})),
);
There's a fully working example app in the ./example
directory.
To run it, clone this repo and run the following commands:
$ npm install
$ ./node_modules/.bin/ts-node ./example/index.ts
You should now be able to visit http://localhost:3000/arbitrary/path,
which should give you a Hello World response, and log your request URL
to ./database.txt
.
FAQs
Application bootstrapping utilities for fp-ts
The npm package fp-ts-bootstrap receives a total of 7,701 weekly downloads. As such, fp-ts-bootstrap popularity was classified as popular.
We found that fp-ts-bootstrap demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
Opengrep forks Semgrep to preserve open source SAST in response to controversial licensing changes.
Security News
Critics call the Node.js EOL CVE a misuse of the system, sparking debate over CVE standards and the growing noise in vulnerability databases.
Security News
cURL and Go security teams are publicly rejecting CVSS as flawed for assessing vulnerabilities and are calling for more accurate, context-aware approaches.