Pure functional programming based dependency injection library inspired by go.uber.org/fig and go.uber.org/dig.
npm install injecaPure functional programming (means no class and extra wrappers and containers) based dependency injection with application lifecycle management.
> Heavily inspired by uber/fig and uber/dig in Golang ecosystem.
[![npm version][npm-version-src]][npm-version-href]
[![npm downloads][npm-downloads-src]][npm-downloads-href]
[![bundle][bundle-src]][bundle-href]
[![JSDocs][jsdocs-src]][jsdocs-href]
[![License][license-src]][license-href]
``sh`
npm install injeca
pnpm add injeca
bun add injeca
ni injeca
yarn add injeca
injeca ships with a global singleton container exposed as injeca. You can register
providers, declare their dependencies, and run invocations once everything is ready.
`ts
import { createServer } from 'node:http'
import { injeca, lifecycle } from 'injeca'
// Providers return a value once and are cached inside the container.
const config = injeca.provide('config', () => ({ port: 3000 }))
const server = injeca.provide({
dependsOn: { config, lifecycle },
async build({ dependsOn }) {
const { config, lifecycle } = dependsOn
const app = createServer()
lifecycle.appHooks.onStart(() => app.listen(config.port))
lifecycle.appHooks.onStop(() => app.close())
return app
},
})
injeca.invoke({
dependsOn: { server },
async callback({ server }) {
// eslint-disable-next-line no-console
console.log('HTTP server ready:', await server.address())
},
})
await injeca.start()
process.once('SIGINT', async () => {
await injeca.stop()
})
`
> [!WARNING]
>
> injeca implements singleton / cached dependencies by default
>
> This means every value returned by build will be stored and build will onlybuild
> got called once.
>
> If returns a function, that function is cached (not the result of callingbuild
> it). This is useful for lazy dependencies, "getters", or wrappers for singleton,
> but it also means any side effects inside the returned function will run on every call.
>
> If you want the returned function to be reusable (e.g. a single window), create
> the reusable closure once inside , and return a wrapper that calls it:`
>
> ts`
> const settingsWindow = injeca.provide('windows:settings', {
> dependsOn: { widgetsManager },
> build: ({ dependsOn }) => {
> const getWindow = setupReusableSettingsWindow(dependsOn) // created once
> return async () => await getWindow() // can be called many times
> },
> })
>
Avoid recreating the reusable function inside the returned function, otherwise
each call starts from a fresh cache and can create duplicate resources.
If you need multiple containers (for tests or per-request scopes), create them
explicitly and pass the container reference into provide, invoke, start, andstop.
`ts
import { createContainer, invoke, provide, start, stop } from 'injeca'
const container = createContainer()
const token = provide(container, 'token', () => crypto.randomUUID())
invoke(container, {
dependsOn: { token },
// eslint-disable-next-line no-console
callback: ({ token }) => console.log('token', token),
})
await start(container)
await stop(container)
``
MIT
[npm-version-src]: https://img.shields.io/npm/v/injeca?style=flat&colorA=080f12&colorB=1fa669
[npm-version-href]: https://npmjs.com/package/injeca
[npm-downloads-src]: https://img.shields.io/npm/dm/injeca?style=flat&colorA=080f12&colorB=1fa669
[npm-downloads-href]: https://npmjs.com/package/injeca
[bundle-src]: https://img.shields.io/bundlephobia/minzip/injeca?style=flat&colorA=080f12&colorB=1fa669&label=minzip
[bundle-href]: https://bundlephobia.com/result?p=injeca
[license-src]: https://img.shields.io/github/license/moeru-ai/injeca.svg?style=flat&colorA=080f12&colorB=1fa669
[license-href]: https://github.com/moeru-ai/injeca/blob/main/LICENSE
[jsdocs-src]: https://img.shields.io/badge/jsdocs-reference-080f12?style=flat&colorA=080f12&colorB=1fa669
[jsdocs-href]: https://www.jsdocs.io/package/injeca