API card
Every runtime call of di-bag: its summary, the codes it can raise, and an example that type-checks. An example without an import line uses import { DiBag } from 'di-bag';. The rules are in AGENTS.md; each code links to its section in errors.md.
One way per task
DiBag facade
DiBag.createProvider(factory, options)
Create a provider from a named-dependency factory.
const config = DiBag.createProvider(() => ({ url: 'memory:' }), { factoryReturnKind: 'sync-value' });DiBag.createProviderFromFunction(options)
Create a provider whose factory receives positional dependency values.
const portSymbol = Symbol('port');
const port = DiBag.createToken(portSymbol).forService<number>();
const client = DiBag.createProviderFromFunction({ dependencies: [port], factoryFunction: value => ({ port: value }) });DiBag.createProviderFromClass(options)
Create a provider that constructs a class from positional dependencies.
const portSymbol = Symbol('port');
const port = DiBag.createToken(portSymbol).forService<number>();
class Client { constructor(readonly port: number) {} }
const client = DiBag.createProviderFromClass({ dependencies: [port], serviceClass: Client });DiBag.createProviderFromPlugin(options)
Create a provider from a versioned plugin descriptor.
const pluginDescriptor = { apiVersion: 1 as const, create: () => ({ run() {} }) };
const plugin = DiBag.createProviderFromPlugin({ dependencies: [], pluginDescriptor, factoryReturnKind: 'uninspected', isValidPluginOutput: (value): value is { run(): void } => typeof value === 'object' && value !== null });DiBag.createToken(symbol)
Create a nominal token from a symbol.
const clockSymbol = Symbol('clock');
const clock = DiBag.createToken(clockSymbol).forService<{ now(): number }>();DiBag.withConfiguration(options)
Return a facade with inherited runtime settings and appended observers. Throws: DI_BAG_INVALID_ARGUMENT.
const Observed = DiBag.withConfiguration({
lifecycleObservers: [{ onLifecycleEvent: event => console.log(event.kind), onObserverFailure: failure => console.error(failure.error) }],
});DiBag.optional(token)
Create a positional dependency that yields undefined only when the token is unregistered. Throws: DI_BAG_INVALID_TOKEN, DI_BAG_WRONG_TOKEN_KIND.
const clockKey = Symbol('clock');
const clock = DiBag.createToken(clockKey).forService<{ now(): number }>();
const stamp = DiBag.createProviderFromFunction({ dependencies: [DiBag.optional(clock)], factoryFunction: source => source?.now() ?? 0 });DiBag.lazy(token)
Create a positional dependency supplied as a function that resolves the token when called. Throws: DI_BAG_INVALID_TOKEN.
const clockKey = Symbol('clock');
const clock = DiBag.createToken(clockKey).forService<{ now(): number }>();
const stamp = DiBag.createProviderFromFunction({ dependencies: [DiBag.lazy(clock)], factoryFunction: getClock => () => getClock().now() });DiBag.createBuilder()
Begin an empty immutable graph; buildContainer creates its owning container, buildModule seals a reusable module.
const container = DiBag.createBuilder().withServices({ greeting: () => 'hello' }).buildContainer();DiBag.providerWithDisposal(options)
Add an ownership stage to a provider input. Throws: DI_BAG_INVALID_ARGUMENT, DI_BAG_INVALID_PROVIDER.
const owned = DiBag.providerWithDisposal({ provider: () => ({ close() {} }), disposeService: service => service.close() });DiBag.providerWithLifetime(options)
Return a provider with singleton, scoped, or transient caching. Providers are scoped per container by default; mark shared clients singleton when none of their dependencies are scoped. Throws: DI_BAG_INVALID_ARGUMENT, DI_BAG_INVALID_PROVIDER.
const createClient = () => ({ close() {} });
const client = DiBag.providerWithLifetime({ provider: DiBag.createProvider(() => createClient()), lifetime: 'singleton:one-per-container-tree' });DiBag.providerWithRegistrationMetadata(options)
Add noncolliding registration metadata without acquiring the service. Throws: DI_BAG_INVALID_ARGUMENT, DI_BAG_DUPLICATE_METADATA_KEY, DI_BAG_INVALID_PROVIDER.
const registered = DiBag.providerWithRegistrationMetadata({ provider: () => 1, registrationMetadata: { owner: 'platform' } });DiBag.providerWithAcquisitionMetadata(options)
Append one synchronous acquisition-metadata frame using the selected callback input. Throws: DI_BAG_INVALID_ARGUMENT, DI_BAG_INVALID_ACQUISITION_METADATA, DI_BAG_INVALID_PROVIDER.
const observed = DiBag.providerWithAcquisitionMetadata({ provider: () => 1, callbackReceives: 'exposed-service', describeAcquisition: value => ({ value }) });DiBag.providerWithTransformedService(options)
Transform the selected callback input while retaining dependencies, metadata, lifetime and ownership stages. Throws: DI_BAG_INVALID_ARGUMENT, DI_BAG_INVALID_PROVIDER.
const mapped = DiBag.providerWithTransformedService({ provider: () => 1, callbackReceives: 'exposed-service', transformService: value => String(value) });Builder
builder.withServices(providersByName)
Add new string-named services. Throws: DI_BAG_INVALID_ARGUMENT, DI_BAG_INVALID_PROVIDER, DI_BAG_DUPLICATE_SERVICE_KEY, DI_BAG_WRONG_TOKEN_KIND.
type Clock = { now(): number };
const builder = DiBag.createBuilder().withServices({ clock: (): Clock => ({ now: () => Date.now() }) }).withServices({ stamp: ({ clock }: { clock: Clock }) => clock.now() });builder.withTokenService(token, provider)
Add the single service of a typed token. Throws: DI_BAG_INVALID_TOKEN, DI_BAG_WRONG_TOKEN_KIND, DI_BAG_DUPLICATE_SERVICE_KEY, DI_BAG_INVALID_PROVIDER.
const clockKey = Symbol('clock');
const clock = DiBag.createToken(clockKey).forService<{ now(): number }>();
const builder = DiBag.createBuilder().withTokenService(clock, () => ({ now: () => Date.now() }));builder.withServiceAlias(options)
Add another lookup name for an existing service. Throws: DI_BAG_INVALID_ARGUMENT, DI_BAG_INVALID_TOKEN, DI_BAG_WRONG_TOKEN_KIND, DI_BAG_DUPLICATE_SERVICE_KEY, DI_BAG_UNKNOWN_SERVICE_KEY.
const builder = DiBag.createBuilder().withServices({ clock: () => Date.now() }).withServiceAlias({ aliasKey: 'now', targetServiceKey: 'clock' });builder.withCollectionContribution(options)
Append a provider to the list of a collection token. Throws: DI_BAG_INVALID_ARGUMENT, DI_BAG_INVALID_TOKEN, DI_BAG_WRONG_TOKEN_KIND, DI_BAG_INVALID_PROVIDER.
const toolsKey = Symbol('tools');
const tools = DiBag.createToken(toolsKey).forCollectionOf<string>();
const builder = DiBag.createBuilder().withCollectionContribution({ collectionToken: tools, provider: () => 'search' }).withCollectionContribution({ collectionToken: tools, provider: () => 'fetch' });builder.withReplacedService(serviceKey, provider)
Replace an existing binding with a compatible provider, selecting it by name, service token, collection token. Throws: DI_BAG_UNKNOWN_SERVICE_KEY, DI_BAG_INVALID_PROVIDER, DI_BAG_WRONG_TOKEN_KIND.
const builder = DiBag.createBuilder().withServices({ clock: () => Date.now() }).withReplacedService('clock', () => 0);builder.withInstalledModules(modules)
Install sealed modules in list order, allocating fresh private bindings for each installation. Throws: DI_BAG_INVALID_ARGUMENT, DI_BAG_INVALID_MODULE, DI_BAG_DUPLICATE_SERVICE_KEY, DI_BAG_WRONG_TOKEN_KIND.
const greeting = DiBag.createBuilder().withServices({ greet: ({ name }: { name: string }) => `hello, ${name}` }).buildModule({ exportedServiceKeys: ['greet'] });
const app = DiBag.createBuilder().withInstalledModules([greeting]).withServices({ name: () => 'Ada' }).buildContainer();builder.verifyGraphAtCompileTime()
Report at the type level why this graph would not build; the runtime call does nothing.
const builder = DiBag.createBuilder().withServices({ greeting: () => 'hello' });
builder.verifyGraphAtCompileTime() satisfies void;builder.buildModule(options)
Seal this graph as a reusable module and select its public names and typed tokens. Throws: DI_BAG_INVALID_ARGUMENT, DI_BAG_UNKNOWN_SERVICE_KEY, DI_BAG_INVALID_TOKEN, DI_BAG_WRONG_TOKEN_KIND.
const orders = DiBag.createBuilder()
.withServices({ repository: () => new Map<string, number>() })
.withServices({ placeOrder: ({ repository }: { repository: Map<string, number> }) => (id: string) => repository.set(id, 1) })
.buildModule({ exportedServiceKeys: ['placeOrder'], moduleLabel: 'orders' });
// Errors and graphSnapshot() name the private binding 'orders/repository'.
const app = DiBag.createBuilder().withInstalledModules([orders]).buildContainer();builder.buildContainer()
Finish a complete graph as a lazy container. Throws: DI_BAG_CLASSIFIER_REQUIRED.
const app = DiBag.createBuilder().withServices({ greeting: () => 'hello' }).buildContainer();
await app.close();Module
module.withRenamedRequirement(options)
Return a module view that asks its host for a requirement under a new name. Throws: DI_BAG_INVALID_ARGUMENT, DI_BAG_UNKNOWN_SERVICE_KEY, DI_BAG_DUPLICATE_SERVICE_KEY.
const feature = DiBag.createBuilder()
.withServices({ answer: ({ config }: { config: number }) => config })
.buildModule({ exportedServiceKeys: ['answer'] });
const app = DiBag.createBuilder()
.withInstalledModules([feature.withRenamedRequirement({ currentRequirementKey: 'config', newRequirementKey: 'featureConfig' })])
.withServices({ featureConfig: () => 42 }).buildContainer();
console.log(app.resolve('answer'));
await app.close();Container
container.resolve(token)
Resolve a registered service, acquiring it lazily when needed. Throws: DI_BAG_CLOSING, DI_BAG_CLOSED, DI_BAG_INVALID_TOKEN, DI_BAG_WRONG_TOKEN_KIND, DI_BAG_UNKNOWN_SERVICE_KEY, DI_BAG_MISSING_DEPENDENCY, DI_BAG_DEPENDENCY_CYCLE, DI_BAG_LIFETIME_DEPENDENCY, DI_BAG_INVALID_DEPENDENCY_ACCESS, DI_BAG_STRUCTURAL_THENABLE, DI_BAG_INVALID_CLASSIFIER_RESULT, DI_BAG_INVALID_ACQUISITION_METADATA, DI_BAG_PLUGIN_VALIDATION.
const container = DiBag.createBuilder().withServices({ greeting: () => 'hello' }).buildContainer();
const greeting: string = container.resolve('greeting');container.resolveCollection(token)
Resolve every contribution for a collection token as a fresh frozen list. Throws: DI_BAG_INVALID_TOKEN, DI_BAG_WRONG_TOKEN_KIND, DI_BAG_CLOSING, DI_BAG_CLOSED.
const toolsKey = Symbol('tools');
const tools = DiBag.createToken(toolsKey).forCollectionOf<string>();
const container = DiBag.createBuilder().buildContainer();
const names: readonly string[] = container.resolveCollection(tools);container.serviceSnapshot(serviceKey)
Inspect a service binding through any supported public key without resolving it. Throws: DI_BAG_INVALID_TOKEN, DI_BAG_WRONG_TOKEN_KIND.
const container = DiBag.createBuilder().withServices({ greeting: () => 'hello' }).buildContainer();
const snapshot = container.serviceSnapshot('greeting');
await container.close();container.graphSnapshot()
Describe every resolvable binding and the dependency edges observed so far.
const container = DiBag.createBuilder().withServices({ greeting: () => 'hello' }).buildContainer();
const labels = container.graphSnapshot().bindings.map(binding => binding.bindingLabel);
await container.close();container.createChildContainer(options?)
Create a tracked child container with fresh ownership for unshared services. Throws: DI_BAG_INVALID_ARGUMENT, DI_BAG_CONFLICTING_SERVICE_SELECTION, DI_BAG_MISSING_REPLACEMENT_PROVIDER, DI_BAG_UNKNOWN_SERVICE_KEY, DI_BAG_SINGLETON_REPLACEMENT, DI_BAG_INVALID_TOKEN, DI_BAG_WRONG_TOKEN_KIND.
const parent = DiBag.createBuilder().withServices({ request: DiBag.providerWithLifetime({
provider: () => ({ id: 'initial' }), lifetime: 'scoped:one-per-container',
}) }).buildContainer();
const child = parent.createChildContainer(['request'], { request: () => ({ id: 'child' }) });
const request = child.resolve('request');
await child.close();
await parent.close();container.createIndependentContainer(replacedServiceKeys, replacementProviders)
Create an independent container with fresh instances and checked replacements. Throws: DI_BAG_INVALID_ARGUMENT, DI_BAG_MISSING_REPLACEMENT_PROVIDER, DI_BAG_UNKNOWN_SERVICE_KEY, DI_BAG_INVALID_PROVIDER, DI_BAG_INVALID_TOKEN, DI_BAG_WRONG_TOKEN_KIND.
const parent = DiBag.createBuilder().withServices({ clock: () => Date.now() }).buildContainer();
const independent = parent.createIndependentContainer(['clock'], { clock: () => 0 });
const now = independent.resolve('clock');
await independent.close();
await parent.close();container.ensureServicesReady(serviceKeys, options?)
Make the listed services ready before continuing, then resolve to this same container. Throws: DI_BAG_SERVICE_READINESS_FAILED, DI_BAG_SERVICE_READINESS_CANCELLED, DI_BAG_INVALID_ARGUMENT, DI_BAG_UNKNOWN_SERVICE_KEY, DI_BAG_INVALID_TOKEN, DI_BAG_WRONG_TOKEN_KIND, DI_BAG_CLOSING, DI_BAG_CLOSED.
const container = await DiBag.createBuilder()
.withServices({ db: async () => ({ ping: () => true }) })
.buildContainer()
.ensureServicesReady(['db'], { totalTimeoutMs: 5_000 });container.close(options?)
Close this container, drain in-flight work, and dispose owned resources once. Throws: DI_BAG_DISPOSAL_FAILED, DI_BAG_CLOSE_FAILED, DI_BAG_CLOSE_TIMEOUT, DI_BAG_CLOSE_ABORTED, DI_BAG_INVALID_ARGUMENT.
const container = DiBag.createBuilder().withServices({ value: () => 1 }).buildContainer();
await container.close({ waitTimeoutMs: 10_000, abortSignal: AbortSignal.timeout(15_000) });Errors
DiBagPluginValidationError
A plugin descriptor or its acquired output failed validation at the checked plugin boundary. Code: DI_BAG_PLUGIN_VALIDATION.
import { DiBag, DiBagPluginValidationError } from 'di-bag';
try {
DiBag.createProviderFromPlugin({ dependencies: [], pluginDescriptor: { apiVersion: 2 }, factoryReturnKind: 'uninspected', isValidPluginOutput: (pluginOutput): pluginOutput is string => typeof pluginOutput === 'string' });
} catch (error) {
if (error instanceof DiBagPluginValidationError) console.error(error.phase, error.reason);
}DiBagDisposalError
One or more disposers failed during close(); every disposal was still attempted. Code: DI_BAG_DISPOSAL_FAILED.
import { DiBag, DiBagDisposalError } from 'di-bag';
const container = DiBag.createBuilder().withServices({ value: () => 1 }).buildContainer();
await container.close().catch((error: unknown) => {
if (error instanceof DiBagDisposalError) for (const failure of error.failures) console.error(failure.bindingLabel, failure.error);
});DiBagServiceReadinessError
ensureServicesReady could not make a listed service ready, and this container is now closed. Code: DI_BAG_SERVICE_READINESS_FAILED.
import { DiBag, DiBagServiceReadinessError } from 'di-bag';
const container = DiBag.createBuilder().withServices({ db: async (): Promise<number> => { throw new Error('offline'); } }).buildContainer();
try {
await container.ensureServicesReady(['db']);
} catch (error) {
if (error instanceof DiBagServiceReadinessError) console.error(error.cause, error.disposalFailures);
}DiBagServiceReadinessCancelledError
ensureServicesReady stopped waiting on abort or timeout; this container is closing and disposalPromise settles when it has closed. Code: DI_BAG_SERVICE_READINESS_CANCELLED.
import { DiBag, DiBagServiceReadinessCancelledError } from 'di-bag';
const container = DiBag.createBuilder().withServices({ db: () => new Promise<number>(() => {}) }).buildContainer();
try {
await container.ensureServicesReady(['db'], { totalTimeoutMs: 1_000 });
} catch (error) {
if (error instanceof DiBagServiceReadinessCancelledError) console.error(error.details.acquisitionsStillPending);
}DiBagCloseCancelledError
A close({ waitTimeoutMs, abortSignal }) wait stopped before disposal finished; disposal keeps running. Code: DI_BAG_CLOSE_TIMEOUT, DI_BAG_CLOSE_ABORTED.
import { DiBag, DiBagCloseCancelledError } from 'di-bag';
const container = DiBag.createBuilder().withServices({ value: () => 1 }).buildContainer();
try {
await container.close({ waitTimeoutMs: 5_000 });
} catch (error) {
if (error instanceof DiBagCloseCancelledError) console.error(error.details.disposersStillRunning);
throw error;
}