| name | injeca |
| description | Guide for using injeca — a pure functional dependency injection library with application lifecycle management, inspired by go.uber.org/dig and go.uber.org/fig. Use this skill whenever the user imports from 'injeca', mentions injeca, needs dependency injection in TypeScript/JavaScript without classes, wants to wire up services with lifecycle hooks (start/stop), needs to manage application startup/shutdown order, or discusses functional DI patterns. Also use when the user asks about alternatives to tsyringe, inversify, or awilix that avoid class-based decorators. |
| license | MIT |
| metadata | {"author":"moeru-ai","version":"0.1.0"} |
injeca
Pure functional dependency injection with application lifecycle management.
Core Concepts
- No classes, no decorators — everything is plain functions and objects
- Singleton by default —
build runs once, the result is cached
- Lifecycle hooks —
onStart / onStop for ordered startup and graceful shutdown
- Topological resolution — dependencies are resolved in correct order, circular deps are detected
API Quick Reference
Global Container (Singleton)
The simplest way — injeca is a pre-created global container:
import { injeca, lifecycle } from 'injeca'
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 }) {
console.log('Server ready:', server.address())
},
})
await injeca.start()
process.once('SIGINT', () => injeca.stop())
Scoped Container (for tests, per-request, or multiple instances)
import { createContainer, invoke, lifecycle, provide, start, stop } from 'injeca'
const container = createContainer()
const token = provide(container, 'token', () => crypto.randomUUID())
invoke(container, {
dependsOn: { token },
callback: ({ token }) => console.log('token', token),
})
await start(container)
await stop(container)
Provider Patterns
Simple value provider
const config = injeca.provide('config', () => ({ port: 3000 }))
const config = provide(container, 'config', () => ({ port: 3000 }))
Provider with dependencies
const db = injeca.provide({
dependsOn: { config },
build: ({ dependsOn }) => createDatabase(dependsOn.config.connectionString),
})
Provider with lifecycle hooks
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
},
})
Lazy / reusable provider
If build returns a function, that function itself is cached (not its return value). Useful for factory patterns:
const createWindow = injeca.provide('createWindow', {
dependsOn: { config },
build: ({ dependsOn }) => {
const getWindow = setupReusableWindow(dependsOn.config)
return async () => await getWindow()
},
})
Lifecycle
Import lifecycle from injeca to declare start/stop hooks inside providers:
import { lifecycle } from 'injeca'
const service = injeca.provide({
dependsOn: { lifecycle },
build({ dependsOn }) {
dependsOn.lifecycle.appHooks.onStart(() => { })
dependsOn.lifecycle.appHooks.onStop(() => { })
return myService
},
})
onStart hooks run in topological order (dependencies first)
onStop hooks run in reverse topological order (dependents first)
- Both are async-safe
Manual Resolution
import { resolve } from 'injeca'
const resolved = await resolve(container, { config, db })
Logger Configuration
import { createContainer } from 'injeca'
const container = createContainer({ enabled: false })
import { createLoggLogger } from 'injeca'
const container = createContainer({ logger: createLoggLogger() })
Key Rules
- Singletons:
build only runs once — the return value is cached. If you return a function, the function is cached, not its result.
- Dependency declaration: Use
dependsOn with typed keys from provide() for full type safety.
- Lifecycle: Always import
lifecycle from injeca and declare it in dependsOn to get onStart/onStop hooks.
- Start order: Call
start(container) or injeca.start() after all providers and invocations are registered.
- Circular deps: Injeca detects circular dependencies at resolution time and throws an error.
- No
any: Provider types are fully inferred from build return type and dependsOn declarations.
Documentation
For the latest API reference, use context7 to query injeca documentation.