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
| Task | Call |
|---|---|
| Start a graph | DiBag.createBuilder() |
| Register a service | builder.register(more) |
| Register a synchronous factory for a browser or worker | DiBag.fromSyncFactory(callback, options) |
| Register an async factory for a browser or worker | DiBag.fromAsyncFactory(callback, options) |
| Attach cleanup | DiBag.withDisposal(create, dispose) |
| Choose a lifetime | DiBag.withLifetime(registration, lifetime) |
| Seal a module | builder.buildModule(keys, options?) |
| Install a module | builder.installModule(module) |
| Check the graph on its own line | builder.verifyGraph() |
| Build a bag | builder.build() |
| Replace for a test | bag.fork(keys, overrides) |
| Open a scope | bag.createScope() |
| Resolve | bag.resolve(token) |
| Close | bag.close(options?) |
DiBag facade
DiBag.withConfiguration(options)
Return a facade with inherited runtime settings and appended observers. Throws: DI_BAG_INVALID_CONFIGURATION.
const Observed = DiBag.withConfiguration({
observers: [{ onEvent: event => console.log(event.kind), onError: failure => console.error(failure.error) }],
});DiBag.fromFactory(callback, options)
Describe a named-dependency factory with an explicit acquisition mode or the acquisition's abort signal. Throws: DI_BAG_INVALID_FACTORY, DI_BAG_INVALID_ACQUISITION_MODE.
type Query = { then(done: (rows: string[]) => void): void };
const query = DiBag.fromFactory((): Query => ({ then: done => done([]) }), { acquisitionMode: 'raw' });DiBag.fromSyncFactory(callback, options)
Describe a synchronous factory that runs on every host: the exact return value is the service and then is never read. Throws: DI_BAG_INVALID_FACTORY.
const config = DiBag.fromSyncFactory(() => ({ url: 'memory:' }));DiBag.fromAsyncFactory(callback, options)
Describe an asynchronous factory that runs on every host: the service is the returned native Promise and withDisposal receives its fulfilled value. Throws: DI_BAG_INVALID_FACTORY.
const db = DiBag.withDisposal(
DiBag.fromAsyncFactory(async ({ config }: { config: { url: string } }) => ({ url: config.url, end: async () => {} })),
db => db.end(),
);DiBag.token(key)
Create a typed token from a unique symbol; .of<Service>() fixes its service type. Throws: DI_BAG_INVALID_TOKEN.
const clockKey = Symbol('clock');
const clock = DiBag.token(clockKey).of<{ now(): number }>();DiBag.optional(token)
Create a positional dependency that yields undefined only when the token is unregistered. Throws: DI_BAG_INVALID_TOKEN.
const clockKey = Symbol('clock');
const clock = DiBag.token(clockKey).of<{ now(): number }>();
const stamp = DiBag.fromFunction([DiBag.optional(clock)], 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.token(clockKey).of<{ now(): number }>();
const stamp = DiBag.fromFunction([DiBag.lazy(clock)], getClock => () => getClock().now());DiBag.all(token)
Create a positional dependency containing every contribution to a collection token, in order. Throws: DI_BAG_INVALID_TOKEN.
const toolsKey = Symbol('tools');
const tools = DiBag.token(toolsKey).of<string>();
const menu = DiBag.fromFunction([DiBag.all(tools)], names => names.join(', '));DiBag.fromPlugin(dependencies, plugin, options)
Validate an unknown plugin descriptor now and its acquired output at acquisition. Throws: DI_BAG_INVALID_TOKEN, DI_BAG_INVALID_PLUGIN_OPTIONS, DI_BAG_PLUGIN_VALIDATION.
declare const descriptor: unknown;
const greeter = DiBag.fromPlugin([], descriptor, {
acquisitionMode: 'raw',
validate: (value): value is () => string => typeof value === 'function',
});DiBag.fromFunction(tokens, callback, ...modeOptions)
Adapt a positional function whose parameters receive the listed tokens' services. Throws: DI_BAG_INVALID_TOKEN, DI_BAG_INVALID_FUNCTION, DI_BAG_INVALID_ACQUISITION_MODE.
const clockKey = Symbol('clock');
const clock = DiBag.token(clockKey).of<{ now(): number }>();
const stamp = DiBag.fromFunction([clock], source => new Date(source.now()).toISOString());DiBag.fromClass(tokens, constructor, ...modeOptions)
Adapt a class whose constructor parameters receive the listed tokens' services. Throws: DI_BAG_INVALID_TOKEN, DI_BAG_INVALID_CONSTRUCTOR, DI_BAG_INVALID_ACQUISITION_MODE.
class Greeter { constructor(readonly greeting: string) {} }
const greetingKey = Symbol('greeting');
const greeter = DiBag.fromClass([DiBag.token(greetingKey).of<string>()], Greeter);DiBag.createBuilder()
Begin an empty immutable graph; build creates its owning bag, buildModule seals a reusable module.
const bag = DiBag.createBuilder().register({ greeting: () => 'hello' }).build();DiBag.withDisposal(create, dispose)
Make the bag own a factory's value and run dispose on it when the bag closes. Throws: DI_BAG_INVALID_REGISTRATION.
const bag = DiBag.createBuilder()
.register({ controller: DiBag.withDisposal(() => new AbortController(), controller => controller.abort()) })
.build();
await bag.close();DiBag.withLifetime(registration, lifetime)
Select root, scoped (the default), or transient caching for a registration. Throws: DI_BAG_INVALID_LIFETIME, DI_BAG_INVALID_REGISTRATION.
const bag = DiBag.createBuilder()
.register({ cache: DiBag.withLifetime(() => new Map<string, string>(), 'root') })
.build();DiBag.withMetadata(registration, options)
Attach static registration metadata, or per-acquisition metadata in direct or awaited mode. Throws: DI_BAG_INVALID_METADATA, DI_BAG_DUPLICATE_METADATA, DI_BAG_INVALID_REGISTRATION.
const greeting = DiBag.withMetadata(() => 'hello', { static: { owner: 'greeting' } });DiBag.transformService(registration, options)
Transform the exposed service while retaining dependencies, metadata, lifetime, and existing ownership. Throws: DI_BAG_INVALID_TRANSFORM, DI_BAG_INVALID_ACQUISITION_MODE, DI_BAG_INVALID_REGISTRATION.
const shout = DiBag.transformService(() => 'hello', { mode: 'direct', transform: text => text.toUpperCase() });Builder
builder.register(more)
Add new string-named registrations. Throws: DI_BAG_INVALID_REGISTRATION, DI_BAG_DUPLICATE_REGISTRATION, DI_BAG_INVALID_TOKEN.
type Clock = { now(): number };
const builder = DiBag.createBuilder()
.register({ clock: (): Clock => ({ now: () => Date.now() }) })
.register({ stamp: ({ clock }: { clock: Clock }) => clock.now() });builder.alias(destination, target)
Add another lookup name or token for an existing service. Throws: DI_BAG_INVALID_TOKEN, DI_BAG_DUPLICATE_REGISTRATION, DI_BAG_INVALID_ALIAS.
const builder = DiBag.createBuilder().register({ clock: () => Date.now() }).alias('now', 'clock');builder.contribute(token, registration)
Append a provider to a typed-token collection. Throws: DI_BAG_INVALID_TOKEN, DI_BAG_INVALID_REGISTRATION.
const toolsKey = Symbol('tools');
const tools = DiBag.token(toolsKey).of<string>();
const builder = DiBag.createBuilder().contribute(tools, () => 'search').contribute(tools, () => 'fetch');builder.replace(key, registration)
Replace an existing string-named registration with a dependency-free factory. Throws: DI_BAG_INVALID_REPLACEMENT, DI_BAG_INVALID_REGISTRATION, DI_BAG_INVALID_TOKEN.
const builder = DiBag.createBuilder().register({ clock: () => Date.now() }).replace('clock', () => 0);builder.installModule(module)
Install a sealed module, allocating fresh private bindings for this installation. Throws: DI_BAG_INVALID_MODULE, DI_BAG_DUPLICATE_REGISTRATION.
const greeting = DiBag.createBuilder()
.register({ greet: ({ name }: { name: string }) => `hello, ${name}` })
.buildModule(['greet']);
const bag = DiBag.createBuilder().installModule(greeting).register({ name: () => 'Ada' }).build();builder.verifyGraph()
Report at the type level why this graph would not build; the runtime call does nothing.
const builder = DiBag.createBuilder().register({ greeting: () => 'hello' });
builder.verifyGraph() satisfies void;builder.buildModule(keys, options?)
Seal this graph as a reusable module and select its public names and typed tokens. Throws: DI_BAG_INVALID_EXPORT, DI_BAG_INVALID_TOKEN.
const orders = DiBag.createBuilder()
.register({ repository: () => new Map<string, number>() })
.register({ placeOrder: ({ repository }: { repository: Map<string, number> }) => (id: string) => repository.set(id, 1) })
.buildModule(['placeOrder'], { label: 'orders' });
// Errors and inspectGraph() name the private binding 'orders/repository'.
const app = DiBag.createBuilder().installModule(orders).build();builder.build()
Finish a complete graph as a lazy bag. Throws: DI_BAG_CLASSIFIER_REQUIRED.
const bag = DiBag.createBuilder().register({ greeting: () => 'hello' }).build();
await bag.close();builder.buildAndStart(keys, options?)
Create a fresh bag and acquire selected services before returning it. Throws: DI_BAG_STARTUP_FAILED, DI_BAG_STARTUP_CANCELLED, DI_BAG_INVALID_STARTUP, DI_BAG_INVALID_TOKEN, DI_BAG_CLASSIFIER_REQUIRED.
const bag = await DiBag.createBuilder()
.register({ db: async () => ({ ping: () => true }) })
.buildAndStart(['db'], { timeoutMs: 5_000 });Bag
bag.resolve(token)
Resolve a named or typed-token service, acquiring it lazily when needed. Throws: DI_BAG_CLOSING, DI_BAG_CLOSED, DI_BAG_INVALID_TOKEN, DI_BAG_MISSING_REGISTRATION, DI_BAG_MISSING_DEPENDENCY, DI_BAG_CYCLE, DI_BAG_LIFETIME_DEPENDENCY, DI_BAG_INVALID_DEPENDENCY_ACCESS, DI_BAG_STRUCTURAL_THENABLE, DI_BAG_INVALID_CLASSIFIER_RESULT, DI_BAG_INVALID_METADATA, DI_BAG_PLUGIN_VALIDATION.
const bag = DiBag.createBuilder().register({ greeting: () => 'hello' }).build();
const greeting: string = bag.resolve('greeting');bag.resolveAll(token)
Resolve every contribution for a typed token in declaration and installation order. Throws: DI_BAG_CLOSING, DI_BAG_CLOSED, DI_BAG_INVALID_TOKEN.
const toolsKey = Symbol('tools');
const tools = DiBag.token(toolsKey).of<string>();
const bag = DiBag.createBuilder().contribute(tools, () => 'search').contribute(tools, () => 'fetch').build();
const names: readonly string[] = bag.resolveAll(tools);bag.inspectAll(token)
Inspect every contribution for a token without running its factories. Throws: DI_BAG_INVALID_TOKEN.
const toolsKey = Symbol('tools');
const tools = DiBag.token(toolsKey).of<string>();
const bag = DiBag.createBuilder().contribute(tools, () => 'search').build();
const labels = bag.inspectAll(tools).map(snapshot => snapshot.label);bag.inspect(token)
Inspect static metadata and copied acquisition state without resolving a service. Throws: DI_BAG_INVALID_TOKEN, DI_BAG_MISSING_REGISTRATION, DI_BAG_CYCLE.
const bag = DiBag.createBuilder().register({ greeting: () => 'hello' }).build();
const acquired = bag.inspect('greeting').acquisitions.length;bag.inspectGraph()
Describe every binding this bag can resolve and the dependency edges observed so far.
const bag = DiBag.createBuilder().register({ greeting: () => 'hello' }).build();
const labels = bag.inspectGraph().bindings.map(binding => binding.label);bag.createScope()
Create a tracked child with the same graph and fresh scoped acquisitions. Throws: DI_BAG_INVALID_SCOPE, DI_BAG_INVALID_TOKEN, DI_BAG_CLOSING, DI_BAG_CLOSED, DI_BAG_INVALID_REGISTRATION, DI_BAG_CLASSIFIER_REQUIRED.
const app = DiBag.createBuilder().register({ requestId: () => Math.random() }).build();
const request = app.createScope();
const id: number = request.resolve('requestId');
await request.close();bag.fork(keys, overrides)
Create an independent bag with selected replacements, the way tests substitute dependencies. Throws: DI_BAG_CLOSING, DI_BAG_CLOSED, DI_BAG_INVALID_OVERRIDE, DI_BAG_INVALID_TOKEN, DI_BAG_INVALID_REGISTRATION, DI_BAG_CLASSIFIER_REQUIRED.
type Clock = { now(): number };
const app = DiBag.createBuilder().register({ clock: (): Clock => ({ now: () => Date.now() }) }).build();
const test = app.fork(['clock'], { clock: (): Clock => ({ now: () => 0 }) });
await test.close();bag.close(options?)
Close this bag, drain in-flight work, and dispose owned resources once. Throws: DI_BAG_CLEANUP_FAILED, DI_BAG_CLOSE_FAILED, DI_BAG_CLOSE_TIMEOUT, DI_BAG_CLOSE_ABORTED, DI_BAG_INVALID_CLOSE.
const bag = DiBag.createBuilder().register({ value: () => 1 }).build();
await bag.close({ timeoutMs: 10_000, signal: 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.fromPlugin([], { apiVersion: 2 }, { acquisitionMode: 'raw', validate: (value): value is string => typeof value === 'string' });
} catch (error) {
if (error instanceof DiBagPluginValidationError) console.error(error.phase, error.reason);
}DiBagCleanupError
One or more disposers failed during close(); every cleanup was still attempted. Code: DI_BAG_CLEANUP_FAILED.
import { DiBag, DiBagCleanupError } from 'di-bag';
const bag = DiBag.createBuilder().register({ value: () => 1 }).build();
await bag.close().catch((error: unknown) => {
if (error instanceof DiBagCleanupError) for (const failure of error.failures) console.error(failure.label, failure.error);
});DiBagStartupError
buildAndStart failed to acquire a selected service; the new bag has already released its resources. Code: DI_BAG_STARTUP_FAILED.
import { DiBag, DiBagStartupError } from 'di-bag';
const builder = DiBag.createBuilder().register({ db: async (): Promise<number> => { throw new Error('offline'); } });
try {
await builder.buildAndStart(['db']);
} catch (error) {
if (error instanceof DiBagStartupError) console.error(error.cause, error.cleanupFailures);
}DiBagStartupCancelledError
buildAndStart stopped waiting on abort or timeout; cleanupPromise settles when the partial bag is released. Code: DI_BAG_STARTUP_CANCELLED.
import { DiBag, DiBagStartupCancelledError } from 'di-bag';
const builder = DiBag.createBuilder().register({ db: () => new Promise<number>(() => {}) });
try {
await builder.buildAndStart(['db'], { timeoutMs: 1_000 });
} catch (error) {
if (error instanceof DiBagStartupCancelledError) await error.cleanupPromise;
}DiBagCloseCancelledError
A close({ timeoutMs, signal }) wait stopped before cleanup finished; cleanup keeps running. Code: DI_BAG_CLOSE_TIMEOUT, DI_BAG_CLOSE_ABORTED.
import { DiBag, DiBagCloseCancelledError } from 'di-bag';
const bag = DiBag.createBuilder().register({ value: () => 1 }).build();
try {
await bag.close({ timeoutMs: 5_000 });
} catch (error) {
if (error instanceof DiBagCloseCancelledError) console.error(error.details.pending);
throw error;
}