Socket
Book a DemoInstallSign in
Socket

ts-fp-di

Package Overview
Dependencies
Maintainers
0
Versions
32
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

ts-fp-di

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

0.22.0
latest
Source
npmnpm
Version published
Weekly downloads
105
-7.08%
Maintainers
0
Weekly downloads
 
Created
Source

ts-fp-di

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

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

const num = div<number>(); // alias to dis((sum, n: number) => n, void 0)
num(5); // mutate state
num(); // 5

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>()
numberState(5)
const stringState = diMap(n => `string - ${n}`, numberState)
const seState = div<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
  numberState, // optional arg1 for effect function
  stringState) // optional arg2 for effect function

await se()

seState() // "5 string - 5"

// dise function can be overriden for unit tests
diseSet(se, async (n, s) => n + parseInt(s.match(/\d/)))

await se()

seState() // 10

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

Keywords

fp

FAQs

Package last updated on 04 Oct 2024

Did you know?

Socket

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.

Install

Related posts

SocketSocket SOC 2 Logo

Product

About

Packages

Stay in touch

Get open source security insights delivered straight into your inbox.

  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc

U.S. Patent No. 12,346,443 & 12,314,394. Other pending.