di-bag 0.1.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 (73) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +280 -0
  3. package/dist/acquisition-context.d.ts +42 -0
  4. package/dist/acquisition-context.js +19 -0
  5. package/dist/acquisition-family.d.ts +32 -0
  6. package/dist/acquisition-family.js +135 -0
  7. package/dist/acquisition-mode.d.ts +34 -0
  8. package/dist/acquisition-mode.js +35 -0
  9. package/dist/acquisition.d.ts +44 -0
  10. package/dist/acquisition.js +395 -0
  11. package/dist/alias-types.d.ts +22 -0
  12. package/dist/alias-types.js +2 -0
  13. package/dist/aliases.d.ts +4 -0
  14. package/dist/aliases.js +24 -0
  15. package/dist/composition.d.ts +41 -0
  16. package/dist/composition.js +45 -0
  17. package/dist/contribution-types.d.ts +57 -0
  18. package/dist/contribution-types.js +2 -0
  19. package/dist/contributions.d.ts +3 -0
  20. package/dist/contributions.js +11 -0
  21. package/dist/dependency-references.d.ts +52 -0
  22. package/dist/dependency-references.js +53 -0
  23. package/dist/di-bag.d.ts +247 -0
  24. package/dist/di-bag.js +211 -0
  25. package/dist/errors.d.ts +66 -0
  26. package/dist/errors.js +89 -0
  27. package/dist/index.d.ts +25 -0
  28. package/dist/index.js +10 -0
  29. package/dist/inspection.d.ts +36 -0
  30. package/dist/inspection.js +2 -0
  31. package/dist/lifetime-types.d.ts +214 -0
  32. package/dist/lifetime-types.js +2 -0
  33. package/dist/lifetime.d.ts +42 -0
  34. package/dist/lifetime.js +31 -0
  35. package/dist/module-types.d.ts +182 -0
  36. package/dist/module-types.js +2 -0
  37. package/dist/module.d.ts +45 -0
  38. package/dist/module.js +118 -0
  39. package/dist/node.d.ts +4 -0
  40. package/dist/node.js +22 -0
  41. package/dist/observers.d.ts +71 -0
  42. package/dist/observers.js +58 -0
  43. package/dist/persistent-map.d.ts +29 -0
  44. package/dist/persistent-map.js +146 -0
  45. package/dist/persistent-sequence.d.ts +9 -0
  46. package/dist/persistent-sequence.js +20 -0
  47. package/dist/plugins.d.ts +32 -0
  48. package/dist/plugins.js +82 -0
  49. package/dist/provider-execution.d.ts +59 -0
  50. package/dist/provider-execution.js +271 -0
  51. package/dist/provider-operations.d.ts +59 -0
  52. package/dist/provider-operations.js +38 -0
  53. package/dist/provider.d.ts +199 -0
  54. package/dist/provider.js +158 -0
  55. package/dist/registration.d.ts +32 -0
  56. package/dist/registration.js +45 -0
  57. package/dist/replacement-types.d.ts +30 -0
  58. package/dist/replacement-types.js +2 -0
  59. package/dist/runtime.d.ts +90 -0
  60. package/dist/runtime.js +428 -0
  61. package/dist/scope-selection.d.ts +6 -0
  62. package/dist/scope-selection.js +62 -0
  63. package/dist/scope-types.d.ts +58 -0
  64. package/dist/scope-types.js +2 -0
  65. package/dist/startup.d.ts +14 -0
  66. package/dist/startup.js +141 -0
  67. package/dist/token-types.d.ts +67 -0
  68. package/dist/token-types.js +2 -0
  69. package/dist/tokens.d.ts +47 -0
  70. package/dist/tokens.js +69 -0
  71. package/dist/types.d.ts +174 -0
  72. package/dist/types.js +2 -0
  73. package/package.json +64 -0
@@ -0,0 +1,90 @@
1
+ import { ScopeAcquisitions } from './acquisition';
2
+ import { normalize } from './registration';
3
+ import type { Registration, Registrations } from './registration';
4
+ import type { RegistrationSnapshot } from './inspection';
5
+ import type { RuntimeContext } from './acquisition-mode';
6
+ export type BindingId = symbol;
7
+ export type BindingKey = string | symbol;
8
+ export type BindingRef = {
9
+ readonly kind: 'private';
10
+ readonly id: BindingId;
11
+ } | {
12
+ readonly kind: 'public';
13
+ readonly key: BindingKey;
14
+ };
15
+ export interface BindingDescription {
16
+ readonly id: BindingId;
17
+ readonly label: string;
18
+ readonly registration: Registration;
19
+ readonly localNames: ReadonlyMap<BindingKey, BindingRef>;
20
+ }
21
+ export interface GraphDescription {
22
+ readonly bindings: ReadonlyMap<BindingId, BindingDescription>;
23
+ readonly publicSlots: ReadonlyMap<BindingKey, BindingId>;
24
+ readonly contributions?: ReadonlyMap<symbol, readonly BindingId[]>;
25
+ }
26
+ type Normalized = Readonly<ReturnType<typeof normalize>>;
27
+ /** Immutable descriptions and path-copied lookup storage. Retained maps never escape. */
28
+ export declare class BindingGraph {
29
+ #private;
30
+ constructor(description?: GraphDescription);
31
+ /** Share only storage roots. Caches never retain ancestor wrappers or old arrays. */
32
+ private copy;
33
+ private entry;
34
+ private slot;
35
+ private addBinding;
36
+ contributionBindings(key: symbol): readonly BindingId[];
37
+ withContribution(key: symbol, registration: Registration): BindingGraph;
38
+ hasPublic(key: BindingKey): boolean;
39
+ hasBinding(id: BindingId): boolean;
40
+ /** Immutable graphs need explicit-mode validation only once; configured forks are O(1). */
41
+ preflight(context: RuntimeContext): void;
42
+ publicBinding(key: BindingKey): BindingId;
43
+ private requirePublicBinding;
44
+ findDependency(from: BindingId, localName: BindingKey): BindingId | undefined;
45
+ dependency(from: BindingId, localName: BindingKey): BindingId;
46
+ registration(id: BindingId): Normalized;
47
+ private requireRegistration;
48
+ label(id: BindingId): string;
49
+ withPublicRegistrations(registrations: Registrations): BindingGraph;
50
+ /** Replace ordered slots and prune only unreferenced public replacement history. */
51
+ withPublicBindings(entries: readonly (readonly [BindingKey, Registration])[]): BindingGraph;
52
+ withPublicBinding(key: BindingKey, registration: Registration): BindingGraph;
53
+ private releaseLexical;
54
+ /** No recursion or graph-wide scan, even when losing a snapshot unlocks a chain. */
55
+ private prune;
56
+ /**
57
+ * Snapshot every retained binding for sealing into a module. Public bindings
58
+ * come first in declaration order, then contributions in their group order,
59
+ * then any privately retained binding. Nothing beyond the graph's own storage
60
+ * is kept to produce this order.
61
+ */
62
+ describe(): GraphDescription;
63
+ /** Install disjoint public slots atomically, retaining lexical private refs. */
64
+ withInstallation(description: GraphDescription): BindingGraph;
65
+ }
66
+ /** Each runtime owns its acquisitions; immutable descriptions remain reusable. */
67
+ export declare class BagRuntime {
68
+ private readonly graph;
69
+ private readonly context;
70
+ private detach;
71
+ private readonly parentAcquisitions?;
72
+ private readonly acquisitions;
73
+ private readonly children;
74
+ private closing;
75
+ private state;
76
+ constructor(graph: BindingGraph, context: RuntimeContext, detach?: (() => void) | undefined, parentAcquisitions?: ScopeAcquisitions | undefined, shared?: readonly BindingId[]);
77
+ resolve(key: BindingKey): unknown;
78
+ resolveAll(key: symbol): readonly unknown[];
79
+ inspectAll(key: symbol): readonly RegistrationSnapshot<object, readonly unknown[]>[];
80
+ acquire(key: BindingKey): Promise<void>;
81
+ isTransient(key: BindingKey): boolean;
82
+ inspect(key: BindingKey): RegistrationSnapshot<object, readonly unknown[]>;
83
+ private inspectBinding;
84
+ assertOpen(): void;
85
+ scope(graph?: BindingGraph, shared?: readonly BindingId[]): BagRuntime;
86
+ close(cause?: unknown): Promise<void>;
87
+ private observeScope;
88
+ private finishClose;
89
+ }
90
+ export {};
@@ -0,0 +1,428 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.BagRuntime = exports.BindingGraph = void 0;
4
+ const errors_1 = require("./errors");
5
+ const acquisition_1 = require("./acquisition");
6
+ const persistent_map_1 = require("./persistent-map");
7
+ const persistent_sequence_1 = require("./persistent-sequence");
8
+ const errors_2 = require("./errors");
9
+ const registration_1 = require("./registration");
10
+ const acquisition_mode_1 = require("./acquisition-mode");
11
+ const emptyContributions = Object.freeze([]);
12
+ const emptyNames = new Map();
13
+ /** Immutable descriptions and path-copied lookup storage. Retained maps never escape. */
14
+ class BindingGraph {
15
+ #bindings = new persistent_map_1.PersistentMap();
16
+ #publicSlots = new persistent_map_1.PersistentMap();
17
+ #publicReferences = new persistent_map_1.PersistentMap();
18
+ #lexicalUsers = new persistent_map_1.PersistentMap();
19
+ #privateReferences = new persistent_map_1.PersistentMap();
20
+ #contributed = new persistent_map_1.PersistentMap();
21
+ // Only former public bindings are candidates; constructor-only private
22
+ // registrations remain available to whole-graph preflight.
23
+ #obsolete = new persistent_map_1.PersistentMap();
24
+ #contributions = new persistent_map_1.PersistentMap();
25
+ // First public registration order by key, for module snapshots. Keys are
26
+ // never unregistered, so this retains nothing a graph would otherwise drop.
27
+ #publicOrder;
28
+ #bindingCache = new Map();
29
+ #registrationCache = new Map();
30
+ #publicCache = new Map();
31
+ #contributionCache = new Map();
32
+ #explicitlyClassified = false;
33
+ constructor(description = { bindings: new Map(), publicSlots: new Map() }) {
34
+ const lexicalSnapshots = new Map();
35
+ for (const [id, binding] of description.bindings) {
36
+ let lexical = lexicalSnapshots.get(binding.localNames);
37
+ if (!lexical) {
38
+ const snapshot = new Map();
39
+ const privateIds = [];
40
+ for (const [name, ref] of binding.localNames) {
41
+ const copiedRef = Object.freeze({ ...ref });
42
+ snapshot.set(name, copiedRef);
43
+ if (copiedRef.kind === 'private')
44
+ privateIds.push(copiedRef.id);
45
+ }
46
+ lexical = { id: Symbol('lexical'), names: snapshot, privateIds };
47
+ lexicalSnapshots.set(binding.localNames, lexical);
48
+ for (const target of privateIds)
49
+ this.#privateReferences = this.#privateReferences.set(target, (this.#privateReferences.get(target) ?? 0) + 1);
50
+ }
51
+ this.#lexicalUsers = this.#lexicalUsers.set(lexical.id, (this.#lexicalUsers.get(lexical.id) ?? 0) + 1);
52
+ this.#bindings = this.#bindings.set(id, {
53
+ lexical,
54
+ description: Object.freeze({ id: binding.id, label: binding.label, registration: binding.registration, localNames: lexical.names }),
55
+ normalized: Object.freeze((0, registration_1.normalize)(binding.registration)),
56
+ });
57
+ }
58
+ for (const [key, id] of description.publicSlots) {
59
+ this.#publicOrder = (0, persistent_sequence_1.append)(this.#publicOrder, { values: [key] });
60
+ this.#publicSlots = this.#publicSlots.set(key, id);
61
+ this.#publicReferences = this.#publicReferences.set(id, (this.#publicReferences.get(id) ?? 0) + 1);
62
+ }
63
+ for (const [key, ids] of description.contributions ?? []) {
64
+ const snapshot = Object.freeze([...ids]);
65
+ this.#contributions = this.#contributions.set(key, { values: snapshot });
66
+ for (const id of snapshot)
67
+ this.#contributed = this.#contributed.set(id, true);
68
+ }
69
+ }
70
+ /** Share only storage roots. Caches never retain ancestor wrappers or old arrays. */
71
+ copy() {
72
+ const graph = new BindingGraph();
73
+ graph.#bindings = this.#bindings;
74
+ graph.#publicSlots = this.#publicSlots;
75
+ graph.#publicReferences = this.#publicReferences;
76
+ graph.#lexicalUsers = this.#lexicalUsers;
77
+ graph.#privateReferences = this.#privateReferences;
78
+ graph.#contributed = this.#contributed;
79
+ graph.#obsolete = this.#obsolete;
80
+ graph.#contributions = this.#contributions;
81
+ graph.#publicOrder = this.#publicOrder;
82
+ return graph;
83
+ }
84
+ entry(id) {
85
+ const entry = this.#bindings.get(id);
86
+ if (entry) {
87
+ this.#bindingCache.set(id, entry.description);
88
+ this.#registrationCache.set(id, entry.normalized);
89
+ }
90
+ return entry;
91
+ }
92
+ slot(key) {
93
+ const id = this.#publicSlots.get(key);
94
+ if (id !== undefined)
95
+ this.#publicCache.set(key, id);
96
+ return id;
97
+ }
98
+ addBinding(label, registration) {
99
+ const id = Symbol(label);
100
+ this.#bindings = this.#bindings.set(id, {
101
+ description: Object.freeze({ id, label, registration, localNames: emptyNames }),
102
+ normalized: Object.freeze((0, registration_1.normalize)(registration)),
103
+ });
104
+ return id;
105
+ }
106
+ contributionBindings(key) {
107
+ let ids = this.#contributionCache.get(key);
108
+ if (!ids) {
109
+ const sequence = this.#contributions.get(key);
110
+ if (!sequence)
111
+ return emptyContributions;
112
+ ids = (0, persistent_sequence_1.materialize)(sequence);
113
+ this.#contributionCache.set(key, ids);
114
+ }
115
+ return ids;
116
+ }
117
+ withContribution(key, registration) {
118
+ const graph = this.copy();
119
+ const id = graph.addBinding(`contribution:${String(key)}`, registration);
120
+ graph.#contributions = graph.#contributions.set(key, (0, persistent_sequence_1.append)(graph.#contributions.get(key), { values: [id] }));
121
+ graph.#contributed = graph.#contributed.set(id, true);
122
+ return graph;
123
+ }
124
+ hasPublic(key) { return this.#publicCache.has(key) || this.#publicSlots.has(key); }
125
+ hasBinding(id) { return this.#bindings.has(id); }
126
+ /** Immutable graphs need explicit-mode validation only once; configured forks are O(1). */
127
+ preflight(context) {
128
+ if (context.isNativePromise || this.#explicitlyClassified)
129
+ return;
130
+ for (const [, { normalized: description }] of this.#bindings) {
131
+ (0, acquisition_mode_1.requireClassificationCapability)([description.acquisitionMode, ...description.operations.flatMap(operation => 'acquisitionMode' in operation ? [operation.acquisitionMode] : [])], context);
132
+ }
133
+ this.#explicitlyClassified = true;
134
+ }
135
+ publicBinding(key) {
136
+ return this.#publicCache.get(key) ?? this.requirePublicBinding(key);
137
+ }
138
+ requirePublicBinding(key) {
139
+ const id = this.slot(key);
140
+ if (id === undefined)
141
+ throw (0, errors_1.libraryError)('DI_BAG_MISSING_REGISTRATION', `Service ${JSON.stringify(String(key))} is not registered.`, { operation: 'resolve', key });
142
+ return id;
143
+ }
144
+ findDependency(from, localName) {
145
+ const ref = (this.#bindingCache.get(from) ?? this.entry(from)?.description)?.localNames.get(localName);
146
+ if (ref?.kind === 'private')
147
+ return ref.id;
148
+ const key = ref?.key ?? localName;
149
+ return this.#publicCache.get(key) ?? this.slot(key);
150
+ }
151
+ dependency(from, localName) {
152
+ const target = this.findDependency(from, localName);
153
+ if (target === undefined)
154
+ throw (0, errors_1.libraryError)('DI_BAG_MISSING_DEPENDENCY', `Cannot resolve ${JSON.stringify(this.label(from))}: dependency ${JSON.stringify(String(localName))} is not registered.`, { operation: 'resolve', consumer: this.label(from), dependency: localName, path: Object.freeze([this.label(from), String(localName)]) });
155
+ return target;
156
+ }
157
+ registration(id) {
158
+ return this.#registrationCache.get(id) ?? this.requireRegistration(id);
159
+ }
160
+ requireRegistration(id) {
161
+ const registration = this.entry(id)?.normalized;
162
+ if (!registration)
163
+ throw (0, errors_1.libraryError)('DI_BAG_MISSING_REGISTRATION', `Service ${JSON.stringify(this.label(id))} is not registered.`, { bindingId: id });
164
+ return registration;
165
+ }
166
+ label(id) { return (this.#bindingCache.get(id) ?? this.entry(id)?.description)?.label ?? String(id); }
167
+ withPublicRegistrations(registrations) {
168
+ return this.withPublicBindings(Object.keys(registrations).map(key => [key, registrations[key]]));
169
+ }
170
+ /** Replace ordered slots and prune only unreferenced public replacement history. */
171
+ withPublicBindings(entries) {
172
+ if (entries.length === 0)
173
+ return this;
174
+ const graph = this.copy();
175
+ for (const [key, registration] of entries) {
176
+ const previous = graph.#publicSlots.get(key);
177
+ if (previous === undefined)
178
+ graph.#publicOrder = (0, persistent_sequence_1.append)(graph.#publicOrder, { values: [key] });
179
+ const id = graph.addBinding(String(key), registration);
180
+ graph.#publicSlots = graph.#publicSlots.set(key, id);
181
+ graph.#publicReferences = graph.#publicReferences.set(id, 1);
182
+ if (previous !== undefined) {
183
+ const remaining = (graph.#publicReferences.get(previous) ?? 1) - 1;
184
+ if (remaining)
185
+ graph.#publicReferences = graph.#publicReferences.set(previous, remaining);
186
+ else {
187
+ graph.#publicReferences = graph.#publicReferences.delete(previous);
188
+ graph.#obsolete = graph.#obsolete.set(previous, true);
189
+ graph.prune([previous]);
190
+ }
191
+ }
192
+ }
193
+ return graph;
194
+ }
195
+ withPublicBinding(key, registration) {
196
+ return this.withPublicBindings([[key, registration]]);
197
+ }
198
+ releaseLexical(entry, pending) {
199
+ const lexical = entry.lexical;
200
+ if (!lexical)
201
+ return;
202
+ const remaining = this.#lexicalUsers.get(lexical.id) - 1;
203
+ if (remaining) {
204
+ this.#lexicalUsers = this.#lexicalUsers.set(lexical.id, remaining);
205
+ return;
206
+ }
207
+ this.#lexicalUsers = this.#lexicalUsers.delete(lexical.id);
208
+ for (const id of lexical.privateIds) {
209
+ const count = this.#privateReferences.get(id) - 1;
210
+ if (count)
211
+ this.#privateReferences = this.#privateReferences.set(id, count);
212
+ else {
213
+ this.#privateReferences = this.#privateReferences.delete(id);
214
+ pending.push(id);
215
+ }
216
+ }
217
+ }
218
+ /** No recursion or graph-wide scan, even when losing a snapshot unlocks a chain. */
219
+ prune(pending) {
220
+ while (pending.length) {
221
+ const id = pending.pop();
222
+ if (!this.#obsolete.has(id) || this.#publicReferences.has(id) || this.#privateReferences.has(id) || this.#contributed.has(id))
223
+ continue;
224
+ const entry = this.#bindings.get(id);
225
+ this.#bindings = this.#bindings.delete(id);
226
+ this.#obsolete = this.#obsolete.delete(id);
227
+ if (entry)
228
+ this.releaseLexical(entry, pending);
229
+ }
230
+ }
231
+ /**
232
+ * Snapshot every retained binding for sealing into a module. Public bindings
233
+ * come first in declaration order, then contributions in their group order,
234
+ * then any privately retained binding. Nothing beyond the graph's own storage
235
+ * is kept to produce this order.
236
+ */
237
+ describe() {
238
+ const bindings = new Map();
239
+ const seen = new Set();
240
+ const take = (id) => {
241
+ if (bindings.has(id))
242
+ return;
243
+ const entry = this.#bindings.get(id);
244
+ if (entry)
245
+ bindings.set(id, entry.description);
246
+ };
247
+ if (this.#publicOrder)
248
+ for (const key of (0, persistent_sequence_1.materialize)(this.#publicOrder)) {
249
+ if (seen.has(key))
250
+ continue;
251
+ seen.add(key);
252
+ const id = this.#publicSlots.get(key);
253
+ if (id !== undefined)
254
+ take(id);
255
+ }
256
+ const contributions = new Map();
257
+ for (const [key, sequence] of this.#contributions) {
258
+ const ids = (0, persistent_sequence_1.materialize)(sequence);
259
+ contributions.set(key, ids);
260
+ for (const id of ids)
261
+ take(id);
262
+ }
263
+ for (const [id] of this.#bindings)
264
+ take(id);
265
+ const publicSlots = new Map();
266
+ for (const [key, id] of this.#publicSlots)
267
+ publicSlots.set(key, id);
268
+ return { bindings, publicSlots, contributions };
269
+ }
270
+ /** Install disjoint public slots atomically, retaining lexical private refs. */
271
+ withInstallation(description) {
272
+ for (const key of description.publicSlots.keys()) {
273
+ if (this.hasPublic(key))
274
+ throw (0, errors_1.libraryError)('DI_BAG_DUPLICATE_REGISTRATION', `duplicate registration: ${String(key)}`, { operation: 'installModule', key });
275
+ }
276
+ const installation = new BindingGraph(description);
277
+ const graph = this.copy();
278
+ const pending = [];
279
+ // Publish all incoming protection before releasing overwritten descriptions.
280
+ for (const [id, count] of installation.#lexicalUsers)
281
+ graph.#lexicalUsers = graph.#lexicalUsers.set(id, count);
282
+ for (const [id, count] of installation.#privateReferences)
283
+ graph.#privateReferences = graph.#privateReferences.set(id, (graph.#privateReferences.get(id) ?? 0) + count);
284
+ for (const [id] of installation.#contributed)
285
+ graph.#contributed = graph.#contributed.set(id, true);
286
+ for (const [key, id] of installation.#publicSlots) {
287
+ graph.#publicOrder = (0, persistent_sequence_1.append)(graph.#publicOrder, { values: [key] });
288
+ graph.#publicSlots = graph.#publicSlots.set(key, id);
289
+ }
290
+ for (const [id, count] of installation.#publicReferences)
291
+ graph.#publicReferences = graph.#publicReferences.set(id, (graph.#publicReferences.get(id) ?? 0) + count);
292
+ for (const [id, entry] of installation.#bindings) {
293
+ const previous = graph.#bindings.get(id);
294
+ if (previous)
295
+ graph.releaseLexical(previous, pending);
296
+ graph.#bindings = graph.#bindings.set(id, entry);
297
+ graph.#obsolete = graph.#obsolete.delete(id);
298
+ }
299
+ for (const [key, sequence] of installation.#contributions)
300
+ graph.#contributions = graph.#contributions.set(key, (0, persistent_sequence_1.append)(graph.#contributions.get(key), sequence));
301
+ graph.prune(pending);
302
+ return graph;
303
+ }
304
+ }
305
+ exports.BindingGraph = BindingGraph;
306
+ /** Each runtime owns its acquisitions; immutable descriptions remain reusable. */
307
+ class BagRuntime {
308
+ graph;
309
+ context;
310
+ detach;
311
+ parentAcquisitions;
312
+ acquisitions;
313
+ children = new Set();
314
+ closing;
315
+ state = 'open';
316
+ constructor(graph, context, detach = undefined, parentAcquisitions, shared = []) {
317
+ this.graph = graph;
318
+ this.context = context;
319
+ this.detach = detach;
320
+ this.parentAcquisitions = parentAcquisitions;
321
+ graph.preflight(context);
322
+ this.acquisitions = new acquisition_1.ScopeAcquisitions(graph, context, parentAcquisitions, shared);
323
+ this.observeScope('scope-opened');
324
+ }
325
+ resolve(key) {
326
+ return this.acquisitions.resolve(key);
327
+ }
328
+ resolveAll(key) { return this.acquisitions.resolveAll(key); }
329
+ inspectAll(key) {
330
+ return Object.freeze(this.graph.contributionBindings(key).map(bindingId => this.inspectBinding(bindingId)));
331
+ }
332
+ acquire(key) {
333
+ return this.acquisitions.acquire(key);
334
+ }
335
+ isTransient(key) {
336
+ return this.acquisitions.isTransient(this.graph.publicBinding(key));
337
+ }
338
+ inspect(key) {
339
+ return this.inspectBinding(this.graph.publicBinding(key));
340
+ }
341
+ inspectBinding(bindingId) {
342
+ return Object.freeze({
343
+ bindingId,
344
+ label: this.graph.label(bindingId),
345
+ ...this.acquisitions.inspectDescription(bindingId),
346
+ acquisitions: this.acquisitions.inspect(bindingId),
347
+ });
348
+ }
349
+ assertOpen() {
350
+ if (this.state !== 'open')
351
+ throw (0, errors_1.libraryError)(this.state === 'closing' ? 'DI_BAG_CLOSING' : 'DI_BAG_CLOSED', `bag is ${this.state}`, { state: this.state });
352
+ this.acquisitions.assertOpen();
353
+ }
354
+ scope(graph = this.graph, shared = []) {
355
+ this.assertOpen();
356
+ let child;
357
+ child = new BagRuntime(graph, this.context, () => { this.children.delete(child); }, this.acquisitions, shared);
358
+ this.children.add(child);
359
+ return child;
360
+ }
361
+ close(cause) {
362
+ if (this.closing)
363
+ return this.closing;
364
+ let fulfill;
365
+ let reject;
366
+ const closing = new Promise((resolve, fail) => { fulfill = resolve; reject = fail; });
367
+ // Publish before recursively closing children or starting local cleanup.
368
+ this.closing = closing;
369
+ this.state = 'closing';
370
+ this.observeScope('scope-closing');
371
+ const childClosing = [...this.children].map(child => {
372
+ try {
373
+ return child.close(cause);
374
+ }
375
+ catch (error) {
376
+ return Promise.reject(error);
377
+ }
378
+ });
379
+ const childResults = Promise.allSettled(childClosing);
380
+ let localClosing;
381
+ try {
382
+ localClosing = this.acquisitions.close(childClosing.length > 0 ? childResults.then(() => undefined) : undefined, cause);
383
+ }
384
+ catch (error) {
385
+ localClosing = Promise.reject(error);
386
+ }
387
+ void this.finishClose(childResults, localClosing).then(() => { this.state = 'closed'; fulfill(); this.observeScope('scope-closed'); }, error => { this.state = 'closed'; reject(error); this.observeScope('scope-close-failed', error); });
388
+ const detach = this.detach;
389
+ this.detach = undefined;
390
+ if (detach)
391
+ void closing.then(() => { detach(); }, () => { detach(); });
392
+ return closing;
393
+ }
394
+ observeScope(kind, error) {
395
+ if (!this.context.observers)
396
+ return;
397
+ const fields = {
398
+ scopeId: this.acquisitions.ownerId,
399
+ ...(this.parentAcquisitions ? { parentScopeId: this.parentAcquisitions.ownerId } : {}),
400
+ };
401
+ this.context.observers.emit(kind === 'scope-close-failed' ? { ...fields, kind, error } : { ...fields, kind });
402
+ }
403
+ async finishClose(childResults, localClosing) {
404
+ const [children, local] = await Promise.all([
405
+ childResults,
406
+ localClosing.then(() => ({ status: 'fulfilled', value: undefined }), reason => ({ status: 'rejected', reason })),
407
+ ]);
408
+ const failures = [];
409
+ const unexpected = [];
410
+ for (const result of [...children, local]) {
411
+ if (result.status === 'fulfilled')
412
+ continue;
413
+ if (result.reason instanceof errors_2.DiBagCleanupError)
414
+ failures.push(...result.reason.failures);
415
+ else
416
+ unexpected.push(result.reason);
417
+ }
418
+ if (unexpected.length > 0) {
419
+ const errors = failures.length > 0
420
+ ? [new errors_2.DiBagCleanupError(failures), ...unexpected]
421
+ : unexpected;
422
+ throw (0, errors_1.diagnostic)(new AggregateError(errors, `Failed to close ${errors.length} runtime operation(s)`), 'DI_BAG_CLOSE_FAILED', { operation: 'close', failedOperations: errors.length });
423
+ }
424
+ if (failures.length > 0)
425
+ throw new errors_2.DiBagCleanupError(failures);
426
+ }
427
+ }
428
+ exports.BagRuntime = BagRuntime;
@@ -0,0 +1,6 @@
1
+ import type { BindingGraph, BindingKey, BindingId } from './runtime';
2
+ /** Validate both selections before reading any override value or building a graph. */
3
+ export declare function selectScope(graph: BindingGraph, args: readonly unknown[], isTransient: (key: BindingKey) => boolean): {
4
+ readonly graph: BindingGraph;
5
+ readonly shared: readonly BindingId[];
6
+ };
@@ -0,0 +1,62 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.selectScope = selectScope;
4
+ const errors_1 = require("./errors");
5
+ const registration_1 = require("./registration");
6
+ const tokens_1 = require("./tokens");
7
+ function snapshot(selection) {
8
+ if (!Array.isArray(selection))
9
+ throw (0, errors_1.libraryError)('DI_BAG_INVALID_SCOPE', 'createScope requires a selected key array', { operation: 'createScope' });
10
+ const values = [];
11
+ const length = selection.length;
12
+ for (let index = 0; index < length; index++)
13
+ values.push(selection[index]);
14
+ return values.map(value => typeof value === 'string' ? value : (0, tokens_1.readTokenKey)(value));
15
+ }
16
+ /** Validate both selections before reading any override value or building a graph. */
17
+ function selectScope(graph, args, isTransient) {
18
+ if (args.length === 0)
19
+ return { graph, shared: [] };
20
+ if (args.length > 3)
21
+ throw (0, errors_1.libraryError)('DI_BAG_INVALID_SCOPE', 'createScope accepts sharing options or selected keys, overrides and optional sharing options', { operation: 'createScope' });
22
+ const hasOverrides = args.length >= 2;
23
+ const selected = hasOverrides ? snapshot(args[0]) : [];
24
+ const overrides = hasOverrides ? args[1] : undefined;
25
+ if (hasOverrides && (typeof overrides !== 'object' || overrides === null || Array.isArray(overrides))) {
26
+ throw (0, errors_1.libraryError)('DI_BAG_INVALID_SCOPE', 'createScope requires an override object', { operation: 'createScope' });
27
+ }
28
+ const options = hasOverrides ? args[2] : args[0];
29
+ let shareKeys = [];
30
+ if (options !== undefined || !hasOverrides) {
31
+ if (typeof options !== 'object' || options === null || Array.isArray(options) ||
32
+ ![Object.prototype, null].includes(Object.getPrototypeOf(options)) ||
33
+ Reflect.ownKeys(options).some(key => key !== 'share') || !Object.hasOwn(options, 'share')) {
34
+ throw (0, errors_1.libraryError)('DI_BAG_INVALID_SCOPE', 'createScope options require only an own share selection', { operation: 'createScope' });
35
+ }
36
+ shareKeys = snapshot(Reflect.get(options, 'share'));
37
+ }
38
+ for (const key of [...selected, ...shareKeys]) {
39
+ if (!graph.hasPublic(key))
40
+ throw (0, errors_1.libraryError)('DI_BAG_INVALID_SCOPE', `createScope accepts existing names or typed tokens only: ${String(key)}`, { operation: 'createScope' });
41
+ }
42
+ const selectedSet = new Set(selected);
43
+ const shared = [...new Set(shareKeys)].map(key => {
44
+ if (selectedSet.has(key))
45
+ throw (0, errors_1.libraryError)('DI_BAG_INVALID_SCOPE', `createScope cannot share and override the same token: ${String(key)}`, { operation: 'createScope' });
46
+ const id = graph.publicBinding(key);
47
+ if (isTransient(key))
48
+ throw (0, errors_1.libraryError)('DI_BAG_INVALID_SCOPE', `createScope cannot share transient providers: ${String(key)}`, { operation: 'createScope' });
49
+ return id;
50
+ });
51
+ for (const key of selectedSet) {
52
+ if (!Object.hasOwn(overrides, key))
53
+ throw (0, errors_1.libraryError)('DI_BAG_INVALID_SCOPE', `missing createScope override: ${String(key)}`, { operation: 'createScope' });
54
+ }
55
+ const bindings = [];
56
+ for (const key of selectedSet) {
57
+ const registration = Reflect.get(overrides, key);
58
+ (0, registration_1.normalize)(registration);
59
+ bindings.push([key, registration]);
60
+ }
61
+ return { graph: graph.withPublicBindings(bindings), shared };
62
+ }
@@ -0,0 +1,58 @@
1
+ import type { CanonicalLifetime } from './lifetime-types';
2
+ import type { Provider, ProviderGraphContract, ProviderFactory, ProviderRegistrationMetadata, ProviderAcquisitionMetadata, ProviderAcquiredValue } from './provider';
3
+ import type { Registration, Registrations } from './registration';
4
+ import type { SelectionKey } from './token-types';
5
+ import type { Selection, Unsatisfied } from './types';
6
+ type Transients<R extends Registrations, S extends readonly unknown[]> = {
7
+ [K in SelectionKey<S[number]> & keyof R]: 'transient' extends CanonicalLifetime<R, K> ? K : never;
8
+ }[SelectionKey<S[number]> & keyof R];
9
+ /** CheckDependencyCompatibility options for borrowing selected non-transient parent acquisitions in a child scope. */
10
+ export type ScopeOptions<R extends Registrations, S extends readonly unknown[]> = {
11
+ /** Existing names or tokens to resolve through the parent's acquisition and ownership context. */
12
+ readonly share: S & Selection<R, S, 'createScope share'> & ([
13
+ Transients<R, S>
14
+ ] extends [never] ? unknown : Unsatisfied<'createScope cannot share transient providers', {
15
+ tokens: Transients<R, S>;
16
+ }>);
17
+ };
18
+ /** Reject a child-scope key selected for both replacement and parent sharing. */
19
+ export type DisjointScopeSelection<K extends readonly unknown[], S extends readonly unknown[]> = [
20
+ SelectionKey<K[number]> & SelectionKey<S[number]>
21
+ ] extends [never] ? unknown : Unsatisfied<'createScope cannot share and override the same token', {
22
+ tokens: SelectionKey<K[number]> & SelectionKey<S[number]>;
23
+ }>;
24
+ type SharedKeys<R extends Registrations> = {
25
+ [K in keyof R]: ProviderGraphContract<R[K]> extends {
26
+ readonly sharedAlias: unknown;
27
+ } ? K : never;
28
+ }[keyof R];
29
+ type Unshared<V extends Registration> = ProviderGraphContract<V> extends {
30
+ readonly sharedAlias: {
31
+ readonly original: infer O extends Registration;
32
+ };
33
+ } ? O : V;
34
+ /** Remove parent-sharing routes when creating a fresh scope or independent fork. */
35
+ export type UnsharedAliases<R extends Registrations> = [SharedKeys<R>] extends [never] ? R : Omit<R, SharedKeys<R>> & {
36
+ [K in SharedKeys<R>]: Unshared<R[K]>;
37
+ };
38
+ type AliasKeys<R extends Registrations, S extends readonly unknown[]> = {
39
+ [K in SelectionKey<S[number]> & keyof R]: ProviderGraphContract<R[K]> extends {
40
+ readonly alias: PropertyKey;
41
+ } ? K : never;
42
+ }[SelectionKey<S[number]> & keyof R];
43
+ type SharedAlias<R extends Registrations, Parent extends Registrations, K extends keyof R> = Provider<ProviderFactory<R[K]>, ProviderRegistrationMetadata<R[K]> & object, ProviderAcquisitionMetadata<R[K]>, ProviderGraphContract<R[K]> & {
44
+ readonly sharedAlias: {
45
+ readonly registrations: Parent;
46
+ readonly source: K;
47
+ readonly original: R[K];
48
+ };
49
+ }, ProviderAcquiredValue<R[K]>>;
50
+ /** Named mapping keeps reflected package declarations inside this checked generic boundary. */
51
+ export type SharedAliasProviders<R extends Registrations, Parent extends Registrations, S extends readonly unknown[]> = [
52
+ AliasKeys<R, S>
53
+ ] extends [never] ? R : Omit<R, AliasKeys<R, S>> & {
54
+ [K in AliasKeys<R, S>]: SharedAlias<R, Parent, K>;
55
+ };
56
+ /** The registration map visible in a child after clearing and applying selected sharing routes. */
57
+ export type ScopedAliases<R extends Registrations, Parent extends Registrations, S extends readonly unknown[]> = SharedAliasProviders<UnsharedAliases<R>, Parent, S>;
58
+ export {};
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,14 @@
1
+ import { BagRuntime } from './runtime';
2
+ import type { BindingGraph } from './runtime';
3
+ import type { RuntimeContext } from './acquisition-mode';
4
+ /** Controls eager acquisition performed by {@link Builder.buildAndStart}. */
5
+ export interface StartupOptions {
6
+ /** An external signal that promptly cancels the startup wait and begins cleanup. */
7
+ readonly signal?: AbortSignal;
8
+ /** A finite positive deadline in milliseconds. */
9
+ readonly timeoutMs?: number;
10
+ /** Start together (`parallel`, default), in tuple order (`sequential`), or with a positive safe integer bound on selected readiness waits. Dependency fanout is not bounded. */
11
+ readonly startupOrder?: 'parallel' | 'sequential' | number;
12
+ }
13
+ /** One startup transaction; never assimilate an exposed service to establish readiness. */
14
+ export declare function startRuntime(graph: BindingGraph, context: RuntimeContext, keys: readonly unknown[], options?: StartupOptions): Promise<BagRuntime>;