inwire 1.0.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.
- package/LICENSE +21 -0
- package/README.md +465 -0
- package/dist/index.cjs +590 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +387 -0
- package/dist/index.d.ts +387 -0
- package/dist/index.js +553 -0
- package/dist/index.js.map +1 -0
- package/llms-full.txt +523 -0
- package/llms.txt +53 -0
- package/package.json +64 -0
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,590 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index.ts
|
|
21
|
+
var index_exports = {};
|
|
22
|
+
__export(index_exports, {
|
|
23
|
+
CircularDependencyError: () => CircularDependencyError,
|
|
24
|
+
ContainerConfigError: () => ContainerConfigError,
|
|
25
|
+
ContainerError: () => ContainerError,
|
|
26
|
+
FactoryError: () => FactoryError,
|
|
27
|
+
ProviderNotFoundError: () => ProviderNotFoundError,
|
|
28
|
+
ReservedKeyError: () => ReservedKeyError,
|
|
29
|
+
ScopeMismatchWarning: () => ScopeMismatchWarning,
|
|
30
|
+
UndefinedReturnError: () => UndefinedReturnError,
|
|
31
|
+
createContainer: () => createContainer,
|
|
32
|
+
detectDuplicateKeys: () => detectDuplicateKeys,
|
|
33
|
+
transient: () => transient
|
|
34
|
+
});
|
|
35
|
+
module.exports = __toCommonJS(index_exports);
|
|
36
|
+
|
|
37
|
+
// src/domain/lifecycle.ts
|
|
38
|
+
function hasOnInit(value) {
|
|
39
|
+
return value !== null && typeof value === "object" && "onInit" in value && typeof value.onInit === "function";
|
|
40
|
+
}
|
|
41
|
+
function hasOnDestroy(value) {
|
|
42
|
+
return value !== null && typeof value === "object" && "onDestroy" in value && typeof value.onDestroy === "function";
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// src/domain/types.ts
|
|
46
|
+
var RESERVED_KEYS = [
|
|
47
|
+
"scope",
|
|
48
|
+
"extend",
|
|
49
|
+
"preload",
|
|
50
|
+
"reset",
|
|
51
|
+
"inspect",
|
|
52
|
+
"describe",
|
|
53
|
+
"health",
|
|
54
|
+
"dispose",
|
|
55
|
+
"toString"
|
|
56
|
+
];
|
|
57
|
+
|
|
58
|
+
// src/domain/errors.ts
|
|
59
|
+
var ContainerError = class extends Error {
|
|
60
|
+
constructor(message) {
|
|
61
|
+
super(message);
|
|
62
|
+
this.name = this.constructor.name;
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
var ContainerConfigError = class extends ContainerError {
|
|
66
|
+
hint;
|
|
67
|
+
details;
|
|
68
|
+
constructor(key, actualType) {
|
|
69
|
+
super(`'${key}' must be a factory function, got ${actualType}.`);
|
|
70
|
+
this.hint = `Wrap it: ${key}: () => ${JSON.stringify(key === key ? `<your ${actualType} value>` : key)}`;
|
|
71
|
+
this.details = { key, actualType };
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
var ReservedKeyError = class extends ContainerError {
|
|
75
|
+
hint;
|
|
76
|
+
details;
|
|
77
|
+
constructor(key, reserved) {
|
|
78
|
+
super(`'${key}' is a reserved container method.`);
|
|
79
|
+
this.hint = `Rename this dependency, e.g. '${key}Service' or 'my${key[0].toUpperCase()}${key.slice(1)}'.`;
|
|
80
|
+
this.details = { key, reserved: [...reserved] };
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
var ProviderNotFoundError = class extends ContainerError {
|
|
84
|
+
hint;
|
|
85
|
+
details;
|
|
86
|
+
constructor(key, chain, registered, suggestion) {
|
|
87
|
+
const chainStr = chain.length > 0 ? `
|
|
88
|
+
|
|
89
|
+
Resolution chain: ${[...chain, `${key} (not found)`].join(" -> ")}` : "";
|
|
90
|
+
const registeredStr = `
|
|
91
|
+
Registered keys: [${registered.join(", ")}]`;
|
|
92
|
+
const suggestionStr = suggestion ? `
|
|
93
|
+
|
|
94
|
+
Did you mean '${suggestion}'?` : "";
|
|
95
|
+
super(
|
|
96
|
+
`Cannot resolve '${chain[0] ?? key}': dependency '${key}' not found.${chainStr}${registeredStr}${suggestionStr}`
|
|
97
|
+
);
|
|
98
|
+
this.hint = suggestion ? `Did you mean '${suggestion}'? Or add '${key}' to your container:
|
|
99
|
+
createContainer({
|
|
100
|
+
...existing,
|
|
101
|
+
${key}: (c) => new Your${key[0].toUpperCase()}${key.slice(1)}(/* deps */),
|
|
102
|
+
});` : `Add '${key}' to your container:
|
|
103
|
+
createContainer({
|
|
104
|
+
...existing,
|
|
105
|
+
${key}: (c) => new Your${key[0].toUpperCase()}${key.slice(1)}(/* deps */),
|
|
106
|
+
});`;
|
|
107
|
+
this.details = { key, chain, registered, suggestion };
|
|
108
|
+
}
|
|
109
|
+
};
|
|
110
|
+
var CircularDependencyError = class extends ContainerError {
|
|
111
|
+
hint;
|
|
112
|
+
details;
|
|
113
|
+
constructor(key, chain) {
|
|
114
|
+
const cycle = [...chain, key].join(" -> ");
|
|
115
|
+
super(
|
|
116
|
+
`Circular dependency detected while resolving '${chain[0]}'.
|
|
117
|
+
|
|
118
|
+
Cycle: ${cycle}`
|
|
119
|
+
);
|
|
120
|
+
this.hint = [
|
|
121
|
+
"To fix:",
|
|
122
|
+
" 1. Extract shared logic into a new dependency both can use",
|
|
123
|
+
" 2. Restructure so one doesn't depend on the other",
|
|
124
|
+
" 3. Use a mediator/event pattern to decouple them"
|
|
125
|
+
].join("\n");
|
|
126
|
+
this.details = { key, chain, cycle };
|
|
127
|
+
}
|
|
128
|
+
};
|
|
129
|
+
var UndefinedReturnError = class extends ContainerError {
|
|
130
|
+
hint;
|
|
131
|
+
details;
|
|
132
|
+
constructor(key, chain) {
|
|
133
|
+
const chainStr = chain.length > 1 ? `
|
|
134
|
+
|
|
135
|
+
Resolution chain: ${chain.join(" -> ")}` : "";
|
|
136
|
+
super(`Factory '${key}' returned undefined.${chainStr}`);
|
|
137
|
+
this.hint = "Your factory function returned undefined. Did you forget a return statement?";
|
|
138
|
+
this.details = { key, chain };
|
|
139
|
+
}
|
|
140
|
+
};
|
|
141
|
+
var FactoryError = class extends ContainerError {
|
|
142
|
+
hint;
|
|
143
|
+
details;
|
|
144
|
+
originalError;
|
|
145
|
+
constructor(key, chain, originalError) {
|
|
146
|
+
const origMessage = originalError instanceof Error ? originalError.message : String(originalError);
|
|
147
|
+
const chainStr = chain.length > 1 ? `
|
|
148
|
+
|
|
149
|
+
Resolution chain: ${[...chain.slice(0, -1), `${key} (factory threw)`].join(" -> ")}` : "";
|
|
150
|
+
super(`Factory '${key}' threw an error: "${origMessage}"${chainStr}`);
|
|
151
|
+
this.hint = `Check the factory function for '${key}'. The error occurred during instantiation.`;
|
|
152
|
+
this.details = { key, chain, originalError: origMessage };
|
|
153
|
+
this.originalError = originalError;
|
|
154
|
+
}
|
|
155
|
+
};
|
|
156
|
+
var ScopeMismatchWarning = class {
|
|
157
|
+
type = "scope_mismatch";
|
|
158
|
+
message;
|
|
159
|
+
hint;
|
|
160
|
+
details;
|
|
161
|
+
constructor(singletonKey, transientKey) {
|
|
162
|
+
this.message = `Singleton '${singletonKey}' depends on transient '${transientKey}'.`;
|
|
163
|
+
this.hint = [
|
|
164
|
+
"The transient value was resolved once and is now frozen inside the singleton.",
|
|
165
|
+
"This is almost always a bug.",
|
|
166
|
+
"",
|
|
167
|
+
"To fix:",
|
|
168
|
+
` 1. Make '${singletonKey}' transient too: transient((c) => new ${singletonKey[0].toUpperCase()}${singletonKey.slice(1)}(c.${transientKey}))`,
|
|
169
|
+
` 2. Make '${transientKey}' singleton if it doesn't need to change`,
|
|
170
|
+
` 3. Inject a factory instead: ${transientKey}Factory: () => () => <your value>`
|
|
171
|
+
].join("\n");
|
|
172
|
+
this.details = { singleton: singletonKey, transient: transientKey };
|
|
173
|
+
}
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
// src/domain/validation.ts
|
|
177
|
+
var Validator = class {
|
|
178
|
+
/**
|
|
179
|
+
* Validates that all values in the config are factory functions
|
|
180
|
+
* and that no reserved keys are used.
|
|
181
|
+
*/
|
|
182
|
+
validateConfig(config) {
|
|
183
|
+
for (const [key, value] of Object.entries(config)) {
|
|
184
|
+
if (RESERVED_KEYS.includes(key)) {
|
|
185
|
+
throw new ReservedKeyError(key, RESERVED_KEYS);
|
|
186
|
+
}
|
|
187
|
+
if (typeof value !== "function") {
|
|
188
|
+
throw new ContainerConfigError(key, typeof value);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* Finds the closest registered key to a missing key using Levenshtein distance.
|
|
194
|
+
* Returns `undefined` if no close match is found (threshold: 3).
|
|
195
|
+
*
|
|
196
|
+
* @example
|
|
197
|
+
* ```typescript
|
|
198
|
+
* validator.suggestKey('userRepo', ['userRepository', 'logger', 'db']);
|
|
199
|
+
* // 'userRepository'
|
|
200
|
+
* ```
|
|
201
|
+
*/
|
|
202
|
+
suggestKey(key, registered) {
|
|
203
|
+
let bestMatch;
|
|
204
|
+
let bestDistance = Infinity;
|
|
205
|
+
for (const candidate of registered) {
|
|
206
|
+
const distance = levenshtein(key, candidate);
|
|
207
|
+
if (distance < bestDistance) {
|
|
208
|
+
bestDistance = distance;
|
|
209
|
+
bestMatch = candidate;
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
if (!bestMatch) return void 0;
|
|
213
|
+
const maxLen = Math.max(key.length, bestMatch.length);
|
|
214
|
+
const similarity = 1 - bestDistance / maxLen;
|
|
215
|
+
return similarity >= 0.5 ? bestMatch : void 0;
|
|
216
|
+
}
|
|
217
|
+
};
|
|
218
|
+
function detectDuplicateKeys(...modules) {
|
|
219
|
+
const seen = /* @__PURE__ */ new Map();
|
|
220
|
+
const duplicates = [];
|
|
221
|
+
for (const mod of modules) {
|
|
222
|
+
for (const key of Object.keys(mod)) {
|
|
223
|
+
const count = (seen.get(key) ?? 0) + 1;
|
|
224
|
+
seen.set(key, count);
|
|
225
|
+
if (count === 2) {
|
|
226
|
+
duplicates.push(key);
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
return duplicates;
|
|
231
|
+
}
|
|
232
|
+
function levenshtein(a, b) {
|
|
233
|
+
const la = a.length;
|
|
234
|
+
const lb = b.length;
|
|
235
|
+
if (la === 0) return lb;
|
|
236
|
+
if (lb === 0) return la;
|
|
237
|
+
let prev = new Array(lb + 1);
|
|
238
|
+
let curr = new Array(lb + 1);
|
|
239
|
+
for (let j = 0; j <= lb; j++) prev[j] = j;
|
|
240
|
+
for (let i = 1; i <= la; i++) {
|
|
241
|
+
curr[0] = i;
|
|
242
|
+
for (let j = 1; j <= lb; j++) {
|
|
243
|
+
const cost = a[i - 1] === b[j - 1] ? 0 : 1;
|
|
244
|
+
curr[j] = Math.min(
|
|
245
|
+
prev[j] + 1,
|
|
246
|
+
// deletion
|
|
247
|
+
curr[j - 1] + 1,
|
|
248
|
+
// insertion
|
|
249
|
+
prev[j - 1] + cost
|
|
250
|
+
// substitution
|
|
251
|
+
);
|
|
252
|
+
}
|
|
253
|
+
[prev, curr] = [curr, prev];
|
|
254
|
+
}
|
|
255
|
+
return prev[lb];
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
// src/infrastructure/transient.ts
|
|
259
|
+
var TRANSIENT_MARKER = /* @__PURE__ */ Symbol.for("inwire:transient");
|
|
260
|
+
function transient(factory) {
|
|
261
|
+
const wrapper = ((container) => factory(container));
|
|
262
|
+
wrapper[TRANSIENT_MARKER] = true;
|
|
263
|
+
return wrapper;
|
|
264
|
+
}
|
|
265
|
+
function isTransient(factory) {
|
|
266
|
+
return typeof factory === "function" && TRANSIENT_MARKER in factory && factory[TRANSIENT_MARKER] === true;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
// src/infrastructure/proxy-handler.ts
|
|
270
|
+
var Resolver = class {
|
|
271
|
+
factories;
|
|
272
|
+
cache;
|
|
273
|
+
resolving = /* @__PURE__ */ new Set();
|
|
274
|
+
depGraph = /* @__PURE__ */ new Map();
|
|
275
|
+
warnings = [];
|
|
276
|
+
validator = new Validator();
|
|
277
|
+
/** Parent resolver for scoped containers */
|
|
278
|
+
parent;
|
|
279
|
+
/** Optional name for debugging/introspection */
|
|
280
|
+
name;
|
|
281
|
+
constructor(factories, cache, parent, name) {
|
|
282
|
+
this.factories = factories;
|
|
283
|
+
this.cache = cache ?? /* @__PURE__ */ new Map();
|
|
284
|
+
this.parent = parent;
|
|
285
|
+
this.name = name;
|
|
286
|
+
}
|
|
287
|
+
getName() {
|
|
288
|
+
return this.name;
|
|
289
|
+
}
|
|
290
|
+
/**
|
|
291
|
+
* Resolves a dependency by key.
|
|
292
|
+
* - Returns cached singleton if available
|
|
293
|
+
* - Detects circular dependencies
|
|
294
|
+
* - Tracks dependencies accessed by each factory
|
|
295
|
+
* - Calls `onInit()` on newly created instances
|
|
296
|
+
* - Emits scope mismatch warnings
|
|
297
|
+
*/
|
|
298
|
+
resolve(key, chain = []) {
|
|
299
|
+
const factory = this.factories.get(key);
|
|
300
|
+
if (factory && !isTransient(factory) && this.cache.has(key)) {
|
|
301
|
+
return this.cache.get(key);
|
|
302
|
+
}
|
|
303
|
+
if (!factory) {
|
|
304
|
+
if (this.parent) {
|
|
305
|
+
return this.parent.resolve(key, chain);
|
|
306
|
+
}
|
|
307
|
+
const allKeys = this.getAllRegisteredKeys();
|
|
308
|
+
const suggestion = this.validator.suggestKey(key, allKeys);
|
|
309
|
+
throw new ProviderNotFoundError(key, chain, allKeys, suggestion);
|
|
310
|
+
}
|
|
311
|
+
if (this.resolving.has(key)) {
|
|
312
|
+
throw new CircularDependencyError(key, [...chain]);
|
|
313
|
+
}
|
|
314
|
+
this.resolving.add(key);
|
|
315
|
+
const currentChain = [...chain, key];
|
|
316
|
+
try {
|
|
317
|
+
const deps = [];
|
|
318
|
+
const trackingProxy = this.createTrackingProxy(key, deps, currentChain);
|
|
319
|
+
const instance = factory(trackingProxy);
|
|
320
|
+
if (instance === void 0) {
|
|
321
|
+
throw new UndefinedReturnError(key, currentChain);
|
|
322
|
+
}
|
|
323
|
+
this.depGraph.set(key, deps);
|
|
324
|
+
if (!isTransient(factory)) {
|
|
325
|
+
for (const dep of deps) {
|
|
326
|
+
const depFactory = this.getFactory(dep);
|
|
327
|
+
if (depFactory && isTransient(depFactory)) {
|
|
328
|
+
this.warnings.push(new ScopeMismatchWarning(key, dep));
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
if (!isTransient(factory)) {
|
|
333
|
+
this.cache.set(key, instance);
|
|
334
|
+
}
|
|
335
|
+
if (hasOnInit(instance)) {
|
|
336
|
+
const initResult = instance.onInit();
|
|
337
|
+
if (initResult instanceof Promise) {
|
|
338
|
+
initResult.catch(() => {
|
|
339
|
+
});
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
return instance;
|
|
343
|
+
} catch (error) {
|
|
344
|
+
if (error instanceof CircularDependencyError || error instanceof ProviderNotFoundError || error instanceof UndefinedReturnError || error instanceof FactoryError) {
|
|
345
|
+
throw error;
|
|
346
|
+
}
|
|
347
|
+
throw new FactoryError(key, currentChain, error);
|
|
348
|
+
} finally {
|
|
349
|
+
this.resolving.delete(key);
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
isResolved(key) {
|
|
353
|
+
return this.cache.has(key);
|
|
354
|
+
}
|
|
355
|
+
getDepGraph() {
|
|
356
|
+
return new Map(this.depGraph);
|
|
357
|
+
}
|
|
358
|
+
getResolvedKeys() {
|
|
359
|
+
return [...this.cache.keys()];
|
|
360
|
+
}
|
|
361
|
+
getFactories() {
|
|
362
|
+
return this.factories;
|
|
363
|
+
}
|
|
364
|
+
getCache() {
|
|
365
|
+
return this.cache;
|
|
366
|
+
}
|
|
367
|
+
getWarnings() {
|
|
368
|
+
return [...this.warnings];
|
|
369
|
+
}
|
|
370
|
+
getAllRegisteredKeys() {
|
|
371
|
+
const keys = new Set(this.factories.keys());
|
|
372
|
+
if (this.parent) {
|
|
373
|
+
for (const key of this.parent.getAllRegisteredKeys()) {
|
|
374
|
+
keys.add(key);
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
return [...keys];
|
|
378
|
+
}
|
|
379
|
+
/**
|
|
380
|
+
* Creates a Proxy that records which keys a factory accesses.
|
|
381
|
+
* This builds the dependency graph automatically.
|
|
382
|
+
*/
|
|
383
|
+
createTrackingProxy(factoryKey, deps, chain) {
|
|
384
|
+
return new Proxy(
|
|
385
|
+
{},
|
|
386
|
+
{
|
|
387
|
+
get: (_target, prop) => {
|
|
388
|
+
if (typeof prop === "symbol") return void 0;
|
|
389
|
+
const depKey = prop;
|
|
390
|
+
deps.push(depKey);
|
|
391
|
+
return this.resolve(depKey, chain);
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
);
|
|
395
|
+
}
|
|
396
|
+
/** Look up a factory in this resolver or its parent chain. */
|
|
397
|
+
getFactory(key) {
|
|
398
|
+
return this.factories.get(key) ?? this.parent?.getFactory(key);
|
|
399
|
+
}
|
|
400
|
+
};
|
|
401
|
+
|
|
402
|
+
// src/application/introspection.ts
|
|
403
|
+
var Introspection = class {
|
|
404
|
+
constructor(resolver) {
|
|
405
|
+
this.resolver = resolver;
|
|
406
|
+
}
|
|
407
|
+
/**
|
|
408
|
+
* Returns the full dependency graph as a serializable JSON object.
|
|
409
|
+
*/
|
|
410
|
+
inspect() {
|
|
411
|
+
const providers = {};
|
|
412
|
+
for (const [key, factory] of this.resolver.getFactories()) {
|
|
413
|
+
providers[key] = {
|
|
414
|
+
key,
|
|
415
|
+
resolved: this.resolver.isResolved(key),
|
|
416
|
+
deps: this.resolver.getDepGraph().get(key) ?? [],
|
|
417
|
+
scope: isTransient(factory) ? "transient" : "singleton"
|
|
418
|
+
};
|
|
419
|
+
}
|
|
420
|
+
const name = this.resolver.getName();
|
|
421
|
+
return name ? { name, providers } : { providers };
|
|
422
|
+
}
|
|
423
|
+
/**
|
|
424
|
+
* Returns detailed information about a specific provider.
|
|
425
|
+
*/
|
|
426
|
+
describe(key) {
|
|
427
|
+
const factory = this.resolver.getFactories().get(key);
|
|
428
|
+
if (!factory) {
|
|
429
|
+
return { key, resolved: false, deps: [], scope: "singleton" };
|
|
430
|
+
}
|
|
431
|
+
return {
|
|
432
|
+
key,
|
|
433
|
+
resolved: this.resolver.isResolved(key),
|
|
434
|
+
deps: this.resolver.getDepGraph().get(key) ?? [],
|
|
435
|
+
scope: isTransient(factory) ? "transient" : "singleton"
|
|
436
|
+
};
|
|
437
|
+
}
|
|
438
|
+
/**
|
|
439
|
+
* Returns container health status with warnings.
|
|
440
|
+
*/
|
|
441
|
+
health() {
|
|
442
|
+
const allKeys = [...this.resolver.getFactories().keys()];
|
|
443
|
+
const resolvedKeys = this.resolver.getResolvedKeys();
|
|
444
|
+
const resolvedSet = new Set(resolvedKeys);
|
|
445
|
+
const warnings = this.resolver.getWarnings().map((w) => ({
|
|
446
|
+
type: w.type,
|
|
447
|
+
message: w.message,
|
|
448
|
+
details: w.details
|
|
449
|
+
}));
|
|
450
|
+
return {
|
|
451
|
+
totalProviders: allKeys.length,
|
|
452
|
+
resolved: resolvedKeys,
|
|
453
|
+
unresolved: allKeys.filter((k) => !resolvedSet.has(k)),
|
|
454
|
+
warnings
|
|
455
|
+
};
|
|
456
|
+
}
|
|
457
|
+
/**
|
|
458
|
+
* Returns a human-readable representation of the container.
|
|
459
|
+
*/
|
|
460
|
+
toString() {
|
|
461
|
+
const parts = [];
|
|
462
|
+
for (const [key] of this.resolver.getFactories()) {
|
|
463
|
+
const resolved = this.resolver.isResolved(key);
|
|
464
|
+
const deps = this.resolver.getDepGraph().get(key);
|
|
465
|
+
const depsStr = deps && deps.length > 0 ? ` -> [${deps.join(", ")}]` : "";
|
|
466
|
+
const status = resolved ? "(resolved)" : "(pending)";
|
|
467
|
+
parts.push(`${key}${depsStr} ${status}`);
|
|
468
|
+
}
|
|
469
|
+
const name = this.resolver.getName();
|
|
470
|
+
const label = name ? `Scope(${name})` : "Container";
|
|
471
|
+
return `${label} { ${parts.join(", ")} }`;
|
|
472
|
+
}
|
|
473
|
+
};
|
|
474
|
+
|
|
475
|
+
// src/application/scope.ts
|
|
476
|
+
function createScope(parentResolver, extra, options) {
|
|
477
|
+
const childFactories = /* @__PURE__ */ new Map();
|
|
478
|
+
for (const [key, factory] of Object.entries(extra)) {
|
|
479
|
+
childFactories.set(key, factory);
|
|
480
|
+
}
|
|
481
|
+
const childResolver = new Resolver(childFactories, /* @__PURE__ */ new Map(), parentResolver, options?.name);
|
|
482
|
+
return buildContainerProxy(childResolver);
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
// src/application/create-container.ts
|
|
486
|
+
var validator = new Validator();
|
|
487
|
+
function createContainer(defs) {
|
|
488
|
+
validator.validateConfig(defs);
|
|
489
|
+
const factories = /* @__PURE__ */ new Map();
|
|
490
|
+
for (const [key, factory] of Object.entries(defs)) {
|
|
491
|
+
factories.set(key, factory);
|
|
492
|
+
}
|
|
493
|
+
const resolver = new Resolver(factories);
|
|
494
|
+
return buildContainerProxy(resolver);
|
|
495
|
+
}
|
|
496
|
+
function buildContainerProxy(resolver) {
|
|
497
|
+
const introspection = new Introspection(resolver);
|
|
498
|
+
const methods = {
|
|
499
|
+
scope: (extra, options) => createScope(resolver, extra, options),
|
|
500
|
+
extend: (extra) => {
|
|
501
|
+
validator.validateConfig(extra);
|
|
502
|
+
const merged = new Map(resolver.getFactories());
|
|
503
|
+
for (const [key, factory] of Object.entries(extra)) {
|
|
504
|
+
merged.set(key, factory);
|
|
505
|
+
}
|
|
506
|
+
const newResolver = new Resolver(merged, new Map(resolver.getCache()));
|
|
507
|
+
return buildContainerProxy(newResolver);
|
|
508
|
+
},
|
|
509
|
+
preload: async (...keys) => {
|
|
510
|
+
const toResolve = keys.length > 0 ? keys : [...resolver.getFactories().keys()];
|
|
511
|
+
for (const key of toResolve) {
|
|
512
|
+
resolver.resolve(key);
|
|
513
|
+
}
|
|
514
|
+
},
|
|
515
|
+
reset: (...keys) => {
|
|
516
|
+
const cache = resolver.getCache();
|
|
517
|
+
for (const key of keys) {
|
|
518
|
+
cache.delete(key);
|
|
519
|
+
}
|
|
520
|
+
},
|
|
521
|
+
inspect: () => introspection.inspect(),
|
|
522
|
+
describe: (key) => introspection.describe(key),
|
|
523
|
+
health: () => introspection.health(),
|
|
524
|
+
toString: () => introspection.toString(),
|
|
525
|
+
dispose: async () => {
|
|
526
|
+
const cache = resolver.getCache();
|
|
527
|
+
const entries = [...cache.entries()].reverse();
|
|
528
|
+
for (const [, instance] of entries) {
|
|
529
|
+
if (hasOnDestroy(instance)) {
|
|
530
|
+
await instance.onDestroy();
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
cache.clear();
|
|
534
|
+
}
|
|
535
|
+
};
|
|
536
|
+
const proxy = new Proxy(
|
|
537
|
+
{},
|
|
538
|
+
{
|
|
539
|
+
get(_target, prop) {
|
|
540
|
+
if (typeof prop === "symbol") {
|
|
541
|
+
if (prop === Symbol.toPrimitive || prop === Symbol.toStringTag) {
|
|
542
|
+
return () => introspection.toString();
|
|
543
|
+
}
|
|
544
|
+
return void 0;
|
|
545
|
+
}
|
|
546
|
+
const key = prop;
|
|
547
|
+
if (key in methods) {
|
|
548
|
+
return methods[key];
|
|
549
|
+
}
|
|
550
|
+
return resolver.resolve(key);
|
|
551
|
+
},
|
|
552
|
+
has(_target, prop) {
|
|
553
|
+
if (typeof prop === "symbol") return false;
|
|
554
|
+
const key = prop;
|
|
555
|
+
return key in methods || resolver.getFactories().has(key) || resolver.getAllRegisteredKeys().includes(key);
|
|
556
|
+
},
|
|
557
|
+
ownKeys() {
|
|
558
|
+
return [...resolver.getAllRegisteredKeys(), ...Object.keys(methods)];
|
|
559
|
+
},
|
|
560
|
+
getOwnPropertyDescriptor(_target, prop) {
|
|
561
|
+
if (typeof prop === "symbol") return void 0;
|
|
562
|
+
const key = prop;
|
|
563
|
+
if (key in methods || resolver.getFactories().has(key) || resolver.getAllRegisteredKeys().includes(key)) {
|
|
564
|
+
return {
|
|
565
|
+
configurable: true,
|
|
566
|
+
enumerable: key in methods ? false : true,
|
|
567
|
+
writable: false
|
|
568
|
+
};
|
|
569
|
+
}
|
|
570
|
+
return void 0;
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
);
|
|
574
|
+
return proxy;
|
|
575
|
+
}
|
|
576
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
577
|
+
0 && (module.exports = {
|
|
578
|
+
CircularDependencyError,
|
|
579
|
+
ContainerConfigError,
|
|
580
|
+
ContainerError,
|
|
581
|
+
FactoryError,
|
|
582
|
+
ProviderNotFoundError,
|
|
583
|
+
ReservedKeyError,
|
|
584
|
+
ScopeMismatchWarning,
|
|
585
|
+
UndefinedReturnError,
|
|
586
|
+
createContainer,
|
|
587
|
+
detectDuplicateKeys,
|
|
588
|
+
transient
|
|
589
|
+
});
|
|
590
|
+
//# sourceMappingURL=index.cjs.map
|