Dependency Injection Container
The Dependency Injection (DI) container provides a centralized way to manage services and dependencies in Flowcraft, enabling loose coupling, easier testing, and better modularity.
DIContainer Class
A lightweight dependency injection container for managing services.
register<T>(token, implementation)
Registers a service instance directly.
tokenServiceToken<T>: A unique token (string or symbol) identifying the service.implementationT: The service instance to register.
registerFactory<T>(token, factory)
Registers a factory function for lazy instantiation of a service.
tokenServiceToken<T>: A unique token identifying the service.factory(container: DIContainer) => T: A function that creates the service instance, receiving the container for dependency resolution.
resolve<T>(token)
Resolves and returns a service instance.
tokenServiceToken<T>: The token of the service to resolve.- Returns
T: The service instance. - Throws
Error: If the service is not registered.
has(token)
Checks if a service is registered.
tokenServiceToken: The token to check.- Returns
boolean:trueif the service is registered, otherwisefalse.
createChild()
Creates a child container that inherits all services from the parent.
- Returns
DIContainer: A new child container.
ServiceTokens
Predefined symbolic tokens for core services:
Logger:Symbol.for('flowcraft:logger')- Logging implementation.Serializer:Symbol.for('flowcraft:serializer')- Context serialization.Evaluator:Symbol.for('flowcraft:evaluator')- Expression evaluation.EventBus:Symbol.for('flowcraft:eventBus')- Event publishing.Orchestrator:Symbol.for('flowcraft:orchestrator')- Workflow orchestration.Middleware:Symbol.for('flowcraft:middleware')- Middleware array.NodeRegistry:Symbol.for('flowcraft:nodeRegistry')- Node implementations.BlueprintRegistry:Symbol.for('flowcraft:blueprintRegistry')- Blueprint registry.Dependencies:Symbol.for('flowcraft:dependencies')- Custom dependencies.
createDefaultContainer(options?)
Creates a pre-configured container with default services. This is the recommended way to set up a container for most use cases, as it provides sensible defaults while allowing customization.
options?ContainerOptions<TDependencies>: Optional overrides for default services.logger?: Custom logger (defaults toNullLogger).serializer?: Custom serializer (defaults toJsonSerializer).evaluator?: Custom evaluator (defaults toPropertyEvaluator).eventBus?: Custom event bus (defaults to no-op).middleware?: Custom middleware array (defaults to empty).registry?: Node registry (defaults to empty).blueprints?: Blueprint registry (defaults to empty).dependencies?: Custom dependencies (defaults to empty).
- Returns
DIContainer: A configured container.
Usage Examples
Basic Usage with Defaults
typescript
import { createDefaultContainer, FlowRuntime } from 'flowcraft'
const container = createDefaultContainer()
const runtime = new FlowRuntime(container)
// Container has NullLogger, JsonSerializer, PropertyEvaluator, etc.Custom Logger and Registry
typescript
import { createDefaultContainer, FlowRuntime, ConsoleLogger } from 'flowcraft'
const container = createDefaultContainer({
registry: {
fetchData: async ({ context }) => {
const data = await apiCall()
return { output: data }
},
processData: async ({ input }) => {
return { output: input.toUpperCase() }
}
},
logger: new ConsoleLogger(),
})
const runtime = new FlowRuntime(container)With Middleware and Dependencies
typescript
import { createDefaultContainer, FlowRuntime } from 'flowcraft'
const container = createDefaultContainer({
middleware: [{
beforeNode: async (ctx, nodeId) => {
console.log(`Starting ${nodeId}`)
}
}],
dependencies: {
database: new DatabaseClient(),
cache: new RedisClient()
}
})
const runtime = new FlowRuntime(container)
// Nodes can access database and cache via context.dependenciesAdvanced: Custom Services
typescript
import { createDefaultContainer, FlowRuntime, ServiceTokens } from 'flowcraft'
import { CustomEvaluator } from './custom-evaluator'
const container = createDefaultContainer({
evaluator: new CustomEvaluator(),
eventBus: {
emit: async (event) => {
await monitoringService.track(event)
}
}
})
// You can also manually register additional services
container.register('customService', new CustomService())
const runtime = new FlowRuntime(container)