di-bag 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/AGENTS.md +149 -0
  2. package/README.md +59 -46
  3. package/dist/acquisition-context.d.ts +8 -2
  4. package/dist/acquisition-mode.d.ts +9 -4
  5. package/dist/acquisition-mode.js +25 -7
  6. package/dist/acquisition.d.ts +2 -0
  7. package/dist/acquisition.js +9 -0
  8. package/dist/alias-types.d.ts +12 -3
  9. package/dist/composition-report.d.ts +3 -2
  10. package/dist/composition.d.ts +8 -2
  11. package/dist/contribution-types.d.ts +26 -18
  12. package/dist/dependency-references.d.ts +16 -4
  13. package/dist/di-bag.d.ts +320 -41
  14. package/dist/di-bag.js +86 -14
  15. package/dist/errors.d.ts +115 -8
  16. package/dist/errors.js +113 -12
  17. package/dist/index.d.ts +5 -5
  18. package/dist/index.js +2 -1
  19. package/dist/inspection.d.ts +21 -5
  20. package/dist/lifetime-types.d.ts +281 -180
  21. package/dist/lifetime.d.ts +4 -1
  22. package/dist/module-types.d.ts +87 -30
  23. package/dist/module.d.ts +18 -4
  24. package/dist/module.js +31 -7
  25. package/dist/observers.d.ts +25 -6
  26. package/dist/plugins.d.ts +17 -4
  27. package/dist/provider.d.ts +41 -10
  28. package/dist/provider.js +1 -0
  29. package/dist/registration.d.ts +8 -2
  30. package/dist/registration.js +4 -1
  31. package/dist/runtime.d.ts +12 -3
  32. package/dist/runtime.js +25 -8
  33. package/dist/scope-types.d.ts +20 -5
  34. package/dist/startup.d.ts +19 -1
  35. package/dist/startup.js +72 -11
  36. package/dist/token-types.d.ts +26 -8
  37. package/dist/tokens.d.ts +10 -2
  38. package/dist/tokens.js +2 -0
  39. package/dist/types.d.ts +68 -22
  40. package/docs/agent/api-card.md +337 -0
  41. package/docs/agent/errors.md +1042 -0
  42. package/docs/agent/recipes.md +290 -0
  43. package/package.json +11 -5
@@ -0,0 +1,337 @@
1
+ <!-- Generated by npm run docs:generate from JSDoc in src/ and tools/docs/api-card-tasks.json. Do not edit. -->
2
+
3
+ # API card {#api-card}
4
+
5
+ Every runtime call of `di-bag`: its summary, the codes it can raise, and an example that type-checks.
6
+ An example without an import line uses `import { DiBag } from 'di-bag';`. The rules are in
7
+ [AGENTS.md](../../AGENTS.md); each code links to its section in [errors.md](errors.md).
8
+
9
+ ## One way per task {#one-way-per-task}
10
+
11
+ | Task | Call |
12
+ | --- | --- |
13
+ | Start a graph | [`DiBag.createBuilder()`](#dibag-createbuilder) |
14
+ | Register a service | [`builder.register(more)`](#builder-register) |
15
+ | Attach cleanup | [`DiBag.withDisposal(create, dispose)`](#dibag-withdisposal) |
16
+ | Choose a lifetime | [`DiBag.withLifetime(registration, lifetime)`](#dibag-withlifetime) |
17
+ | Seal a module | [`builder.buildModule(keys, options?)`](#builder-buildmodule) |
18
+ | Install a module | [`builder.installModule(module)`](#builder-installmodule) |
19
+ | Check the graph on its own line | [`builder.verifyGraph()`](#builder-verifygraph) |
20
+ | Build a bag | [`builder.build()`](#builder-build) |
21
+ | Replace for a test | [`bag.fork(keys, overrides)`](#bag-fork) |
22
+ | Open a scope | [`bag.createScope()`](#bag-createscope) |
23
+ | Resolve | [`bag.resolve(token)`](#bag-resolve) |
24
+ | Close | [`bag.close(options?)`](#bag-close) |
25
+
26
+ ## DiBag facade {#dibag-facade}
27
+
28
+ ### `DiBag.withConfiguration(options)` {#dibag-withconfiguration}
29
+ Return a facade with inherited runtime settings and appended observers. Throws: [`DI_BAG_INVALID_CONFIGURATION`](errors.md#di-bag-invalid-configuration).
30
+ ```ts
31
+ const Observed = DiBag.withConfiguration({
32
+ observers: [{ onEvent: event => console.log(event.kind), onError: failure => console.error(failure.error) }],
33
+ });
34
+ ```
35
+
36
+ ### `DiBag.fromFactory(callback, options)` {#dibag-fromfactory}
37
+ Describe a named-dependency factory with an explicit acquisition mode or the acquisition's abort signal. Throws: [`DI_BAG_INVALID_FACTORY`](errors.md#di-bag-invalid-factory), [`DI_BAG_INVALID_ACQUISITION_MODE`](errors.md#di-bag-invalid-acquisition-mode).
38
+ ```ts
39
+ type Query = { then(done: (rows: string[]) => void): void };
40
+ const query = DiBag.fromFactory((): Query => ({ then: done => done([]) }), { acquisitionMode: 'raw' });
41
+ ```
42
+
43
+ ### `DiBag.token(key)` {#dibag-token}
44
+ Create a typed token from a unique symbol; `.of<Service>()` fixes its service type. Throws: [`DI_BAG_INVALID_TOKEN`](errors.md#di-bag-invalid-token).
45
+ ```ts
46
+ const clockKey = Symbol('clock');
47
+ const clock = DiBag.token(clockKey).of<{ now(): number }>();
48
+ ```
49
+
50
+ ### `DiBag.optional(token)` {#dibag-optional}
51
+ Create a positional dependency that yields `undefined` only when the token is unregistered. Throws: [`DI_BAG_INVALID_TOKEN`](errors.md#di-bag-invalid-token).
52
+ ```ts
53
+ const clockKey = Symbol('clock');
54
+ const clock = DiBag.token(clockKey).of<{ now(): number }>();
55
+ const stamp = DiBag.fromFunction([DiBag.optional(clock)], source => source?.now() ?? 0);
56
+ ```
57
+
58
+ ### `DiBag.lazy(token)` {#dibag-lazy}
59
+ Create a positional dependency supplied as a function that resolves the token when called. Throws: [`DI_BAG_INVALID_TOKEN`](errors.md#di-bag-invalid-token).
60
+ ```ts
61
+ const clockKey = Symbol('clock');
62
+ const clock = DiBag.token(clockKey).of<{ now(): number }>();
63
+ const stamp = DiBag.fromFunction([DiBag.lazy(clock)], getClock => () => getClock().now());
64
+ ```
65
+
66
+ ### `DiBag.all(token)` {#dibag-all}
67
+ Create a positional dependency containing every contribution to a collection token, in order. Throws: [`DI_BAG_INVALID_TOKEN`](errors.md#di-bag-invalid-token).
68
+ ```ts
69
+ const toolsKey = Symbol('tools');
70
+ const tools = DiBag.token(toolsKey).of<string>();
71
+ const menu = DiBag.fromFunction([DiBag.all(tools)], names => names.join(', '));
72
+ ```
73
+
74
+ ### `DiBag.fromPlugin(dependencies, plugin, options)` {#dibag-fromplugin}
75
+ Validate an unknown plugin descriptor now and its acquired output at acquisition. Throws: [`DI_BAG_INVALID_TOKEN`](errors.md#di-bag-invalid-token), [`DI_BAG_INVALID_PLUGIN_OPTIONS`](errors.md#di-bag-invalid-plugin-options), [`DI_BAG_PLUGIN_VALIDATION`](errors.md#di-bag-plugin-validation).
76
+ ```ts
77
+ declare const descriptor: unknown;
78
+ const greeter = DiBag.fromPlugin([], descriptor, {
79
+ acquisitionMode: 'raw',
80
+ validate: (value): value is () => string => typeof value === 'function',
81
+ });
82
+ ```
83
+
84
+ ### `DiBag.fromFunction(tokens, callback, ...modeOptions)` {#dibag-fromfunction}
85
+ Adapt a positional function whose parameters receive the listed tokens' services. Throws: [`DI_BAG_INVALID_TOKEN`](errors.md#di-bag-invalid-token), [`DI_BAG_INVALID_FUNCTION`](errors.md#di-bag-invalid-function), [`DI_BAG_INVALID_ACQUISITION_MODE`](errors.md#di-bag-invalid-acquisition-mode).
86
+ ```ts
87
+ const clockKey = Symbol('clock');
88
+ const clock = DiBag.token(clockKey).of<{ now(): number }>();
89
+ const stamp = DiBag.fromFunction([clock], source => new Date(source.now()).toISOString());
90
+ ```
91
+
92
+ ### `DiBag.fromClass(tokens, constructor, ...modeOptions)` {#dibag-fromclass}
93
+ Adapt a class whose constructor parameters receive the listed tokens' services. Throws: [`DI_BAG_INVALID_TOKEN`](errors.md#di-bag-invalid-token), [`DI_BAG_INVALID_CONSTRUCTOR`](errors.md#di-bag-invalid-constructor), [`DI_BAG_INVALID_ACQUISITION_MODE`](errors.md#di-bag-invalid-acquisition-mode).
94
+ ```ts
95
+ class Greeter { constructor(readonly greeting: string) {} }
96
+ const greetingKey = Symbol('greeting');
97
+ const greeter = DiBag.fromClass([DiBag.token(greetingKey).of<string>()], Greeter);
98
+ ```
99
+
100
+ ### `DiBag.createBuilder()` {#dibag-createbuilder}
101
+ Begin an empty immutable graph; `build` creates its owning bag, `buildModule` seals a reusable module.
102
+ ```ts
103
+ const bag = DiBag.createBuilder().register({ greeting: () => 'hello' }).build();
104
+ ```
105
+
106
+ ### `DiBag.withDisposal(create, dispose)` {#dibag-withdisposal}
107
+ Make the bag own a factory's value and run `dispose` on it when the bag closes. Throws: [`DI_BAG_INVALID_REGISTRATION`](errors.md#di-bag-invalid-registration).
108
+ ```ts
109
+ const bag = DiBag.createBuilder()
110
+ .register({ controller: DiBag.withDisposal(() => new AbortController(), controller => controller.abort()) })
111
+ .build();
112
+ await bag.close();
113
+ ```
114
+
115
+ ### `DiBag.withLifetime(registration, lifetime)` {#dibag-withlifetime}
116
+ Select `root`, `scoped` (the default), or `transient` caching for a registration. Throws: [`DI_BAG_INVALID_LIFETIME`](errors.md#di-bag-invalid-lifetime), [`DI_BAG_INVALID_REGISTRATION`](errors.md#di-bag-invalid-registration).
117
+ ```ts
118
+ const bag = DiBag.createBuilder()
119
+ .register({ cache: DiBag.withLifetime(() => new Map<string, string>(), 'root') })
120
+ .build();
121
+ ```
122
+
123
+ ### `DiBag.withMetadata(registration, options)` {#dibag-withmetadata}
124
+ Attach static registration metadata, or per-acquisition metadata in direct or awaited mode. Throws: [`DI_BAG_INVALID_METADATA`](errors.md#di-bag-invalid-metadata), [`DI_BAG_DUPLICATE_METADATA`](errors.md#di-bag-duplicate-metadata), [`DI_BAG_INVALID_REGISTRATION`](errors.md#di-bag-invalid-registration).
125
+ ```ts
126
+ const greeting = DiBag.withMetadata(() => 'hello', { static: { owner: 'greeting' } });
127
+ ```
128
+
129
+ ### `DiBag.transformService(registration, options)` {#dibag-transformservice}
130
+ Transform the exposed service while retaining dependencies, metadata, lifetime, and existing ownership. Throws: [`DI_BAG_INVALID_TRANSFORM`](errors.md#di-bag-invalid-transform), [`DI_BAG_INVALID_ACQUISITION_MODE`](errors.md#di-bag-invalid-acquisition-mode), [`DI_BAG_INVALID_REGISTRATION`](errors.md#di-bag-invalid-registration).
131
+ ```ts
132
+ const shout = DiBag.transformService(() => 'hello', { mode: 'direct', transform: text => text.toUpperCase() });
133
+ ```
134
+
135
+ ## Builder {#builder}
136
+
137
+ ### `builder.register(more)` {#builder-register}
138
+ Add new string-named registrations. Throws: [`DI_BAG_INVALID_REGISTRATION`](errors.md#di-bag-invalid-registration), [`DI_BAG_DUPLICATE_REGISTRATION`](errors.md#di-bag-duplicate-registration), [`DI_BAG_INVALID_TOKEN`](errors.md#di-bag-invalid-token).
139
+ ```ts
140
+ type Clock = { now(): number };
141
+ const builder = DiBag.createBuilder()
142
+ .register({ clock: (): Clock => ({ now: () => Date.now() }) })
143
+ .register({ stamp: ({ clock }: { clock: Clock }) => clock.now() });
144
+ ```
145
+
146
+ ### `builder.alias(destination, target)` {#builder-alias}
147
+ Add another lookup name or token for an existing service. Throws: [`DI_BAG_INVALID_TOKEN`](errors.md#di-bag-invalid-token), [`DI_BAG_DUPLICATE_REGISTRATION`](errors.md#di-bag-duplicate-registration), [`DI_BAG_INVALID_ALIAS`](errors.md#di-bag-invalid-alias).
148
+ ```ts
149
+ const builder = DiBag.createBuilder().register({ clock: () => Date.now() }).alias('now', 'clock');
150
+ ```
151
+
152
+ ### `builder.contribute(token, registration)` {#builder-contribute}
153
+ Append a provider to a typed-token collection. Throws: [`DI_BAG_INVALID_TOKEN`](errors.md#di-bag-invalid-token), [`DI_BAG_INVALID_REGISTRATION`](errors.md#di-bag-invalid-registration).
154
+ ```ts
155
+ const toolsKey = Symbol('tools');
156
+ const tools = DiBag.token(toolsKey).of<string>();
157
+ const builder = DiBag.createBuilder().contribute(tools, () => 'search').contribute(tools, () => 'fetch');
158
+ ```
159
+
160
+ ### `builder.replace(key, registration)` {#builder-replace}
161
+ Replace an existing string-named registration with a dependency-free factory. Throws: [`DI_BAG_INVALID_REPLACEMENT`](errors.md#di-bag-invalid-replacement), [`DI_BAG_INVALID_REGISTRATION`](errors.md#di-bag-invalid-registration), [`DI_BAG_INVALID_TOKEN`](errors.md#di-bag-invalid-token).
162
+ ```ts
163
+ const builder = DiBag.createBuilder().register({ clock: () => Date.now() }).replace('clock', () => 0);
164
+ ```
165
+
166
+ ### `builder.installModule(module)` {#builder-installmodule}
167
+ Install a sealed module, allocating fresh private bindings for this installation. Throws: [`DI_BAG_INVALID_MODULE`](errors.md#di-bag-invalid-module), [`DI_BAG_DUPLICATE_REGISTRATION`](errors.md#di-bag-duplicate-registration).
168
+ ```ts
169
+ const greeting = DiBag.createBuilder()
170
+ .register({ greet: ({ name }: { name: string }) => `hello, ${name}` })
171
+ .buildModule(['greet']);
172
+ const bag = DiBag.createBuilder().installModule(greeting).register({ name: () => 'Ada' }).build();
173
+ ```
174
+
175
+ ### `builder.verifyGraph()` {#builder-verifygraph}
176
+ Report at the type level why this graph would not build; the runtime call does nothing.
177
+ ```ts
178
+ const builder = DiBag.createBuilder().register({ greeting: () => 'hello' });
179
+ builder.verifyGraph() satisfies void;
180
+ ```
181
+
182
+ ### `builder.buildModule(keys, options?)` {#builder-buildmodule}
183
+ Seal this graph as a reusable module and select its public names and typed tokens. Throws: [`DI_BAG_INVALID_EXPORT`](errors.md#di-bag-invalid-export), [`DI_BAG_INVALID_TOKEN`](errors.md#di-bag-invalid-token).
184
+ ```ts
185
+ const orders = DiBag.createBuilder()
186
+ .register({ repository: () => new Map<string, number>() })
187
+ .register({ placeOrder: ({ repository }: { repository: Map<string, number> }) => (id: string) => repository.set(id, 1) })
188
+ .buildModule(['placeOrder'], { label: 'orders' });
189
+ // Errors and inspectGraph() name the private binding 'orders/repository'.
190
+ const app = DiBag.createBuilder().installModule(orders).build();
191
+ ```
192
+
193
+ ### `builder.build()` {#builder-build}
194
+ Finish a complete graph as a lazy bag. Throws: [`DI_BAG_CLASSIFIER_REQUIRED`](errors.md#di-bag-classifier-required).
195
+ ```ts
196
+ const bag = DiBag.createBuilder().register({ greeting: () => 'hello' }).build();
197
+ await bag.close();
198
+ ```
199
+
200
+ ### `builder.buildAndStart(keys, options?)` {#builder-buildandstart}
201
+ Create a fresh bag and acquire selected services before returning it. Throws: [`DI_BAG_STARTUP_FAILED`](errors.md#di-bag-startup-failed), [`DI_BAG_STARTUP_CANCELLED`](errors.md#di-bag-startup-cancelled), [`DI_BAG_INVALID_STARTUP`](errors.md#di-bag-invalid-startup), [`DI_BAG_INVALID_TOKEN`](errors.md#di-bag-invalid-token), [`DI_BAG_CLASSIFIER_REQUIRED`](errors.md#di-bag-classifier-required).
202
+ ```ts
203
+ const bag = await DiBag.createBuilder()
204
+ .register({ db: async () => ({ ping: () => true }) })
205
+ .buildAndStart(['db'], { timeoutMs: 5_000 });
206
+ ```
207
+
208
+ ## Bag {#bag}
209
+
210
+ ### `bag.resolve(token)` {#bag-resolve}
211
+ Resolve a named or typed-token service, acquiring it lazily when needed. Throws: [`DI_BAG_CLOSING`](errors.md#di-bag-closing), [`DI_BAG_CLOSED`](errors.md#di-bag-closed), [`DI_BAG_INVALID_TOKEN`](errors.md#di-bag-invalid-token), [`DI_BAG_MISSING_REGISTRATION`](errors.md#di-bag-missing-registration), [`DI_BAG_MISSING_DEPENDENCY`](errors.md#di-bag-missing-dependency), [`DI_BAG_CYCLE`](errors.md#di-bag-cycle), [`DI_BAG_LIFETIME_DEPENDENCY`](errors.md#di-bag-lifetime-dependency), [`DI_BAG_INVALID_DEPENDENCY_ACCESS`](errors.md#di-bag-invalid-dependency-access), [`DI_BAG_STRUCTURAL_THENABLE`](errors.md#di-bag-structural-thenable), [`DI_BAG_INVALID_CLASSIFIER_RESULT`](errors.md#di-bag-invalid-classifier-result), [`DI_BAG_INVALID_METADATA`](errors.md#di-bag-invalid-metadata), [`DI_BAG_PLUGIN_VALIDATION`](errors.md#di-bag-plugin-validation).
212
+ ```ts
213
+ const bag = DiBag.createBuilder().register({ greeting: () => 'hello' }).build();
214
+ const greeting: string = bag.resolve('greeting');
215
+ ```
216
+
217
+ ### `bag.resolveAll(token)` {#bag-resolveall}
218
+ Resolve every contribution for a typed token in declaration and installation order. Throws: [`DI_BAG_CLOSING`](errors.md#di-bag-closing), [`DI_BAG_CLOSED`](errors.md#di-bag-closed), [`DI_BAG_INVALID_TOKEN`](errors.md#di-bag-invalid-token).
219
+ ```ts
220
+ const toolsKey = Symbol('tools');
221
+ const tools = DiBag.token(toolsKey).of<string>();
222
+ const bag = DiBag.createBuilder().contribute(tools, () => 'search').contribute(tools, () => 'fetch').build();
223
+ const names: readonly string[] = bag.resolveAll(tools);
224
+ ```
225
+
226
+ ### `bag.inspectAll(token)` {#bag-inspectall}
227
+ Inspect every contribution for a token without running its factories. Throws: [`DI_BAG_INVALID_TOKEN`](errors.md#di-bag-invalid-token).
228
+ ```ts
229
+ const toolsKey = Symbol('tools');
230
+ const tools = DiBag.token(toolsKey).of<string>();
231
+ const bag = DiBag.createBuilder().contribute(tools, () => 'search').build();
232
+ const labels = bag.inspectAll(tools).map(snapshot => snapshot.label);
233
+ ```
234
+
235
+ ### `bag.inspect(token)` {#bag-inspect}
236
+ Inspect static metadata and copied acquisition state without resolving a service. Throws: [`DI_BAG_INVALID_TOKEN`](errors.md#di-bag-invalid-token), [`DI_BAG_MISSING_REGISTRATION`](errors.md#di-bag-missing-registration), [`DI_BAG_CYCLE`](errors.md#di-bag-cycle).
237
+ ```ts
238
+ const bag = DiBag.createBuilder().register({ greeting: () => 'hello' }).build();
239
+ const acquired = bag.inspect('greeting').acquisitions.length;
240
+ ```
241
+
242
+ ### `bag.inspectGraph()` {#bag-inspectgraph}
243
+ Describe every binding this bag can resolve and the dependency edges observed so far.
244
+ ```ts
245
+ const bag = DiBag.createBuilder().register({ greeting: () => 'hello' }).build();
246
+ const labels = bag.inspectGraph().bindings.map(binding => binding.label);
247
+ ```
248
+
249
+ ### `bag.createScope()` {#bag-createscope}
250
+ Create a tracked child with the same graph and fresh scoped acquisitions. Throws: [`DI_BAG_INVALID_SCOPE`](errors.md#di-bag-invalid-scope), [`DI_BAG_INVALID_TOKEN`](errors.md#di-bag-invalid-token), [`DI_BAG_CLOSING`](errors.md#di-bag-closing), [`DI_BAG_CLOSED`](errors.md#di-bag-closed), [`DI_BAG_INVALID_REGISTRATION`](errors.md#di-bag-invalid-registration), [`DI_BAG_CLASSIFIER_REQUIRED`](errors.md#di-bag-classifier-required).
251
+ ```ts
252
+ const app = DiBag.createBuilder().register({ requestId: () => Math.random() }).build();
253
+ const request = app.createScope();
254
+ const id: number = request.resolve('requestId');
255
+ await request.close();
256
+ ```
257
+
258
+ ### `bag.fork(keys, overrides)` {#bag-fork}
259
+ Create an independent bag with selected replacements, the way tests substitute dependencies. Throws: [`DI_BAG_CLOSING`](errors.md#di-bag-closing), [`DI_BAG_CLOSED`](errors.md#di-bag-closed), [`DI_BAG_INVALID_OVERRIDE`](errors.md#di-bag-invalid-override), [`DI_BAG_INVALID_TOKEN`](errors.md#di-bag-invalid-token), [`DI_BAG_INVALID_REGISTRATION`](errors.md#di-bag-invalid-registration), [`DI_BAG_CLASSIFIER_REQUIRED`](errors.md#di-bag-classifier-required).
260
+ ```ts
261
+ type Clock = { now(): number };
262
+ const app = DiBag.createBuilder().register({ clock: (): Clock => ({ now: () => Date.now() }) }).build();
263
+ const test = app.fork(['clock'], { clock: (): Clock => ({ now: () => 0 }) });
264
+ await test.close();
265
+ ```
266
+
267
+ ### `bag.close(options?)` {#bag-close}
268
+ Close this bag, drain in-flight work, and dispose owned resources once. Throws: [`DI_BAG_CLEANUP_FAILED`](errors.md#di-bag-cleanup-failed), [`DI_BAG_CLOSE_FAILED`](errors.md#di-bag-close-failed), [`DI_BAG_CLOSE_TIMEOUT`](errors.md#di-bag-close-timeout), [`DI_BAG_CLOSE_ABORTED`](errors.md#di-bag-close-aborted), [`DI_BAG_INVALID_CLOSE`](errors.md#di-bag-invalid-close).
269
+ ```ts
270
+ const bag = DiBag.createBuilder().register({ value: () => 1 }).build();
271
+ await bag.close({ timeoutMs: 10_000, signal: AbortSignal.timeout(15_000) });
272
+ ```
273
+
274
+ ## Errors {#errors}
275
+
276
+ ### `DiBagPluginValidationError` {#dibagpluginvalidationerror}
277
+ A plugin descriptor or its acquired output failed validation at the checked plugin boundary. Code: [`DI_BAG_PLUGIN_VALIDATION`](errors.md#di-bag-plugin-validation).
278
+ ```ts
279
+ import { DiBag, DiBagPluginValidationError } from 'di-bag';
280
+
281
+ try {
282
+ DiBag.fromPlugin([], { apiVersion: 2 }, { acquisitionMode: 'raw', validate: (value): value is string => typeof value === 'string' });
283
+ } catch (error) {
284
+ if (error instanceof DiBagPluginValidationError) console.error(error.phase, error.reason);
285
+ }
286
+ ```
287
+
288
+ ### `DiBagCleanupError` {#dibagcleanuperror}
289
+ One or more disposers failed during `close()`; every cleanup was still attempted. Code: [`DI_BAG_CLEANUP_FAILED`](errors.md#di-bag-cleanup-failed).
290
+ ```ts
291
+ import { DiBag, DiBagCleanupError } from 'di-bag';
292
+
293
+ const bag = DiBag.createBuilder().register({ value: () => 1 }).build();
294
+ await bag.close().catch((error: unknown) => {
295
+ if (error instanceof DiBagCleanupError) for (const failure of error.failures) console.error(failure.label, failure.error);
296
+ });
297
+ ```
298
+
299
+ ### `DiBagStartupError` {#dibagstartuperror}
300
+ `buildAndStart` failed to acquire a selected service; the new bag has already released its resources. Code: [`DI_BAG_STARTUP_FAILED`](errors.md#di-bag-startup-failed).
301
+ ```ts
302
+ import { DiBag, DiBagStartupError } from 'di-bag';
303
+
304
+ const builder = DiBag.createBuilder().register({ db: async (): Promise<number> => { throw new Error('offline'); } });
305
+ try {
306
+ await builder.buildAndStart(['db']);
307
+ } catch (error) {
308
+ if (error instanceof DiBagStartupError) console.error(error.cause, error.cleanupFailures);
309
+ }
310
+ ```
311
+
312
+ ### `DiBagStartupCancelledError` {#dibagstartupcancellederror}
313
+ `buildAndStart` stopped waiting on abort or timeout; `cleanupPromise` settles when the partial bag is released. Code: [`DI_BAG_STARTUP_CANCELLED`](errors.md#di-bag-startup-cancelled).
314
+ ```ts
315
+ import { DiBag, DiBagStartupCancelledError } from 'di-bag';
316
+
317
+ const builder = DiBag.createBuilder().register({ db: () => new Promise<number>(() => {}) });
318
+ try {
319
+ await builder.buildAndStart(['db'], { timeoutMs: 1_000 });
320
+ } catch (error) {
321
+ if (error instanceof DiBagStartupCancelledError) await error.cleanupPromise;
322
+ }
323
+ ```
324
+
325
+ ### `DiBagCloseCancelledError` {#dibagclosecancellederror}
326
+ A `close({ timeoutMs, signal })` wait stopped before cleanup finished; cleanup keeps running. Code: [`DI_BAG_CLOSE_TIMEOUT`](errors.md#di-bag-close-timeout), [`DI_BAG_CLOSE_ABORTED`](errors.md#di-bag-close-aborted).
327
+ ```ts
328
+ import { DiBag, DiBagCloseCancelledError } from 'di-bag';
329
+
330
+ const bag = DiBag.createBuilder().register({ value: () => 1 }).build();
331
+ try {
332
+ await bag.close({ timeoutMs: 5_000 });
333
+ } catch (error) {
334
+ if (error instanceof DiBagCloseCancelledError) console.error(error.details.pending);
335
+ throw error;
336
+ }
337
+ ```