npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

ts-fp-di

v0.20.0

Published

Tiny TypeScript functional dependency injection, based on Node.js AsyncLocalStorage

Downloads

806

Readme

ts-fp-di

Tiny TypeScript functional dependency injection, based on Node.js AsyncLocalStorage.

Get started

Firstly, need init DI container for each life cycle of your backend application (each HTTP request/response, handle MQ message, ...).

Example of middleware for typical Koa application, where on each HTTP request will be created particular DI container:

app.use(async (ctx, next) => {
  await diInit(async () => return await next());
});

Further, simply use ts-fp-di API "as is" in code, it will consider particular DI scope.

Examples

Basic

const fn = di(() => 1);
fn() // call `fn` function inside DI scope, it's return 1

Override dependency (method 1)

const fn = di(() => 1);
diSet(fn, () => 2); // Override `fn` function inside DI scope. Useful for unit tests.
fn() // returns 2, because it rewriten.

Override dependency (method 2)

const fn = () => 1;
diSet(fn, () => 2); // Override `fn` function inside DI scope. Useful for unit tests.
diDep(fn)() // returns 2, because it rewriten.

Dependency by string key

diSet('user', {login: 'xxx'}); // Useful to setup current user in DI scope
diDep<User>('user') // Extract current user from anywhere

State managment in DI scope

// setup Redux like state with reducer in DI scope
const inc = dis((sum, n: number) => sum + n, 0); 
inc(1); // mutate state
inc(); // 1, "inc" without argument returns current state

State managment in global scope

// setup Redux like state with reducer in global scope (pass true as isGlobal flag)
const inc = dis((sum, n: number) => sum + n, 0, true); 
inc(1); // mutate state
inc(); // 1, "inc" without argument returns current state

clearGlobalState(); // you can clear global state (useful in tests)
inc() // 0, "inc" returns default value now

Singleton for DI scope

let i = 0;
const fn = diOnce(() => { // <- setup Singleton function for DI scope
  i += 1;
  return i;
});

fn(); // 1
fn(); // also 1, because fn is singleton for DI scope

Singleton constant for DI scope

const cache = dic<number>()

cache(1)
cache() // 1

Override Singleton for DI scope

const fn = diOnce((n: number) => { // <- setup Singleton function for DI scope
  return n + 1;
});

diOnceSet(fn, -1); // Override diOnceSet. For example, use this in your unit tests
fn(4) // -1 instead 5, because -1 set on prev line

Check that runtime in DI scope

diExists() // false

diInit(() => {
  diExists() // true
});

Share DI context

const ctx = diContext()

diInit(() => {
  // ctx will be considered here
}, ctx)

diInit(() => {
  // same ctx will be considered here too
}, ctx)

DI Scope (OOP incapsulation alternative)

const inc = dis((resp: number, n: number) => resp + n, 0)

const scope = diScope({ inc }, () => {
  // optional "constructor" function
  // some `diSet` can be used here
})

scope.inc(5) // this mutation occur only inside this scope
scope.inc() // 5 

Functional reactive programming, mapping

const cacheNumber = dic<number>()
const calcString = diMap(n => `string - ${n}`, cacheNumber)

cacheNumber(5)
calcString() // "string - 5"

const onceNumber = diOnce((n: number) => {
  return n;
});
const calcString = diMap(n => `string - ${n}`, onceNumber)

onceNumber(5)
calcString() // "string - 5"

const inc = dis((sum, n: number) => sum + n, 0);
const calcString = diMap(s => `string - ${s}`, inc)
inc(1);
inc(4);
calcString() // "string - 5"

calcString.raw(1) // direct call of function, useful for unit tests

const cacheNumber = dic<number>()
let i = 0
const onceString = diMapOnce(n => ((i = i + 1), `string - ${n}`), cacheNumber)

cacheNumber(5)
onceString() // "string - 5"
onceString() // i = 1, because onceString is singleton for DI scope

Attach async effect to State

const numberState = dic<number>()
const stringState = diMap(n => `string - ${n}`, numberState)
const seState = dic<string>() // will be populated via side effect
const se = dise(
  async (n, s) => `${n} ${s}`, // side effect async function
  seState, // this state will be populated via async response
  n, // optional arg1 for effect function
  s) // optional arg2 for effect function

await se()

seState() // "5 string - 5"

Plugins

Internal AsyncLocalStorage instance exposed as als property. You can implement your own plugin around it.

‼️ If you use ts-fp-di with plugins on your project, please consider, that you have only one ts-fp-di node_module For example you can freeze as singleton you dependency via package.json overrides

{
  "overrides": {
    "ts-fp-di": "^x.x.x"
  }
}

  • ts-fp-di-mikroorm - Use MikroORM Entities inside ts-fp-di State and achieve auto persistence in DB
  • ts-fp-di-rxjs - Utils for RxJS for working with ts-fp-di
  • rxjs-wait-next - Wait RxJS Subject.next emition for all subscribers