Containers and scopes
Containers
Section titled “Containers”A container owns provider registrations and resolves dependency graphs on demand.
const container = createContainer(providers);
container.register(provideValue(PORT, 3000));container.has(PORT); // truecontainer.get(PORT); // 3000Registering the same token twice throws DuplicateProviderError. For an
intentional test or environment override, pass { allowOverride: true }.
container.register(provideValue(API, fakeApi), { allowOverride: true });Scopes
Section titled “Scopes”| Scope | Lifetime |
|---|---|
singleton |
One cached instance in the container that owns the provider |
scoped |
One cached instance in each resolving container |
transient |
A fresh instance on every resolution |
Use the Scopes constants for autocompletion:
provideFactory(REQUEST_STATE, { scope: Scopes.Scoped, deps: { request: REQUEST }, useFactory: ({ request }) => new RequestState(request),});A longer-lived service cannot capture a shorter-lived dependency. For example,
a singleton cannot depend on a scoped provider. Invalid lifetime graphs fail
during resolution with InvalidProviderError.
Child containers
Section titled “Child containers”Child containers inherit providers from a parent while adding or overriding request-local values. Singleton instances remain owned by the provider’s container; scoped instances belong to the child performing resolution.
import { createChildContainer, createContainer, createToken, provideFactory, provideValue, Scopes,} from "di-craft";
type Request = { readonly id: string;};
class RequestState { readonly request: Request;
constructor(request: Request) { this.request = request; }}
const REQUEST = createToken<Request>("REQUEST");const REQUEST_STATE = createToken<RequestState>("REQUEST_STATE");
const root = createContainer([ provideFactory(REQUEST_STATE, { scope: Scopes.Scoped, deps: { request: REQUEST }, useFactory: ({ request }) => new RequestState(request), }),]);
const requestA = createChildContainer(root, [ provideValue(REQUEST, { id: "request-a" }),]);const requestB = createChildContainer(root, [ provideValue(REQUEST, { id: "request-b" }),]);
const firstA = requestA.get(REQUEST_STATE);const secondA = requestA.get(REQUEST_STATE);const firstB = requestB.get(REQUEST_STATE);
firstA === secondA; // truefirstA === firstB; // falseThis is the core primitive used by the Node.js and Next.js request adapters.
Resolution errors
Section titled “Resolution errors”All library errors extend DiError:
| Error | Meaning |
|---|---|
MissingProviderError |
No provider exists for a requested token |
DuplicateProviderError |
A token was registered more than once |
CircularDependencyError |
The provider graph contains a cycle |
InvalidDependencyError |
A dependency declaration is missing or invalid |
InvalidProviderError |
A scope, disposal hook, or override is invalid |
Cached resources are released through deterministic disposal.