inwire 2.1.2 → 2.1.3
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/README.md +21 -12
- package/dist/index.d.mts +176 -176
- package/dist/index.mjs +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -106,7 +106,8 @@ app.db; // safe to use, fully initialized
|
|
|
106
106
|
- **Smart errors** — 7 error types, each with `hint`, `details`, and fuzzy matching ("did you mean `userService`?"). Designed for both humans and LLMs to parse.
|
|
107
107
|
- **Built-in introspection** — `inspect()` returns a serializable JSON graph. Feed it to an LLM, render it in a dashboard, or use `health()` to catch scope mismatches at runtime.
|
|
108
108
|
- **Runtime agnostic** — pure ES2022. No decorators, no `reflect-metadata`, no compiler plugins. Works in Node.js, Deno, Bun, Cloudflare Workers, Vercel Edge, and browsers.
|
|
109
|
-
- **
|
|
109
|
+
- **Clean internals** — Clean Architecture, SOLID, single-responsibility files. Open any file, understand it, change it without fear.
|
|
110
|
+
- **Tiny** — ~4 KB gzip, zero dependencies.
|
|
110
111
|
|
|
111
112
|
## Features
|
|
112
113
|
|
|
@@ -346,23 +347,31 @@ detectDuplicateKeys(authModule, userModule);
|
|
|
346
347
|
|
|
347
348
|
## Architecture
|
|
348
349
|
|
|
350
|
+
Clean Architecture / SOLID internals. The dependency rule is enforced: `domain/` has zero imports from other layers.
|
|
351
|
+
|
|
349
352
|
```
|
|
350
353
|
src/
|
|
351
|
-
index.ts # barrel
|
|
352
|
-
domain/
|
|
353
|
-
types.ts # interfaces,
|
|
354
|
-
errors.ts # 7 error classes +
|
|
355
|
-
lifecycle.ts # OnInit / OnDestroy
|
|
354
|
+
index.ts # public barrel — only file consumers import
|
|
355
|
+
domain/ # pure contracts — no framework deps
|
|
356
|
+
types.ts # interfaces (IResolver, ICycleDetector, IDependencyTracker, IValidator)
|
|
357
|
+
errors.ts # 7 error classes + 2 warning types, each with hint + details
|
|
358
|
+
lifecycle.ts # OnInit / OnDestroy (duck-typed)
|
|
356
359
|
validation.ts # Validator, detectDuplicateKeys, Levenshtein
|
|
357
|
-
infrastructure/
|
|
358
|
-
resolver.ts #
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
360
|
+
infrastructure/ # low-level mechanisms — depends on domain/ only
|
|
361
|
+
resolver.ts # lazy resolution, singleton cache, parent chain
|
|
362
|
+
cycle-detector.ts # circular dependency detection
|
|
363
|
+
dependency-tracker.ts # tracking Proxy + dependency graph builder
|
|
364
|
+
transient.ts # transient() marker (Symbol-based)
|
|
365
|
+
application/ # use cases + orchestration — depends on domain/ + infrastructure/
|
|
366
|
+
container-builder.ts # fluent builder + container() factory
|
|
367
|
+
container-proxy.ts # Proxy construction, scope/extend/reset
|
|
368
|
+
preloader.ts # topological sort (Kahn) + parallel onInit
|
|
369
|
+
disposer.ts # reverse-order onDestroy + cleanup
|
|
363
370
|
introspection.ts # inspect, describe, health, toString
|
|
364
371
|
```
|
|
365
372
|
|
|
373
|
+
Each file has a single responsibility. The Resolver receives its collaborators (`CycleDetector`, `DependencyTracker`) via constructor injection — no internal `new`, no hidden coupling. `Preloader`, `Disposer`, and `Introspection` depend on the `IResolver` interface, not the concrete class.
|
|
374
|
+
|
|
366
375
|
## LLM / AI Integration
|
|
367
376
|
|
|
368
377
|
This package ships with [llms.txt](https://llmstxt.org/) files for AI-assisted development:
|
package/dist/index.d.mts
CHANGED
|
@@ -1,3 +1,179 @@
|
|
|
1
|
+
//#region src/domain/errors.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* Base class for all container errors.
|
|
4
|
+
* Every error includes a human-readable `hint` and structured `details`
|
|
5
|
+
* so that AI tools and developers can auto-correct issues.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```typescript
|
|
9
|
+
* try { container.userService; }
|
|
10
|
+
* catch (e) {
|
|
11
|
+
* if (e instanceof ContainerError) {
|
|
12
|
+
* console.log(e.hint); // actionable fix
|
|
13
|
+
* console.log(e.details); // structured context
|
|
14
|
+
* }
|
|
15
|
+
* }
|
|
16
|
+
* ```
|
|
17
|
+
*/
|
|
18
|
+
declare abstract class ContainerError extends Error {
|
|
19
|
+
abstract readonly hint: string;
|
|
20
|
+
abstract readonly details: Record<string, unknown>;
|
|
21
|
+
constructor(message: string);
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Thrown when a non-function value is passed in the deps definition.
|
|
25
|
+
*
|
|
26
|
+
* @example
|
|
27
|
+
* ```typescript
|
|
28
|
+
* container().add('apiKey', 'sk-123');
|
|
29
|
+
* // ContainerConfigError: 'apiKey' must be a factory function, got string.
|
|
30
|
+
* // hint: "Wrap it: apiKey: () => 'sk-123'"
|
|
31
|
+
* ```
|
|
32
|
+
*/
|
|
33
|
+
declare class ContainerConfigError extends ContainerError {
|
|
34
|
+
readonly hint: string;
|
|
35
|
+
readonly details: {
|
|
36
|
+
key: string;
|
|
37
|
+
actualType: string;
|
|
38
|
+
};
|
|
39
|
+
constructor(key: string, actualType: string);
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Thrown when a reserved container method name is used as a dependency key.
|
|
43
|
+
*
|
|
44
|
+
* @example
|
|
45
|
+
* ```typescript
|
|
46
|
+
* container().add('inspect', () => 'foo');
|
|
47
|
+
* // ReservedKeyError: 'inspect' is a reserved container method.
|
|
48
|
+
* // hint: "Rename this dependency, e.g. 'inspectService' or 'dataInspector'."
|
|
49
|
+
* ```
|
|
50
|
+
*/
|
|
51
|
+
declare class ReservedKeyError extends ContainerError {
|
|
52
|
+
readonly hint: string;
|
|
53
|
+
readonly details: {
|
|
54
|
+
key: string;
|
|
55
|
+
reserved: string[];
|
|
56
|
+
};
|
|
57
|
+
constructor(key: string, reserved: readonly string[]);
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Thrown when a dependency cannot be found during resolution.
|
|
61
|
+
* Includes fuzzy suggestion if a similar key exists.
|
|
62
|
+
*
|
|
63
|
+
* @example
|
|
64
|
+
* ```typescript
|
|
65
|
+
* container.userService;
|
|
66
|
+
* // ProviderNotFoundError: Cannot resolve 'userService': dependency 'userRepo' not found.
|
|
67
|
+
* // hint: "Did you mean 'userRepository'?"
|
|
68
|
+
* ```
|
|
69
|
+
*/
|
|
70
|
+
declare class ProviderNotFoundError extends ContainerError {
|
|
71
|
+
readonly hint: string;
|
|
72
|
+
readonly details: {
|
|
73
|
+
key: string;
|
|
74
|
+
chain: string[];
|
|
75
|
+
registered: string[];
|
|
76
|
+
suggestion: string | undefined;
|
|
77
|
+
};
|
|
78
|
+
constructor(key: string, chain: string[], registered: string[], suggestion?: string);
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Thrown when a circular dependency is detected.
|
|
82
|
+
*
|
|
83
|
+
* @example
|
|
84
|
+
* ```typescript
|
|
85
|
+
* // CircularDependencyError: Circular dependency detected while resolving 'authService'.
|
|
86
|
+
* // Cycle: authService -> userService -> authService
|
|
87
|
+
* ```
|
|
88
|
+
*/
|
|
89
|
+
declare class CircularDependencyError extends ContainerError {
|
|
90
|
+
readonly hint: string;
|
|
91
|
+
readonly details: {
|
|
92
|
+
key: string;
|
|
93
|
+
chain: string[];
|
|
94
|
+
cycle: string;
|
|
95
|
+
};
|
|
96
|
+
constructor(key: string, chain: string[]);
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Thrown when a factory function returns `undefined`.
|
|
100
|
+
*
|
|
101
|
+
* @example
|
|
102
|
+
* ```typescript
|
|
103
|
+
* container.db;
|
|
104
|
+
* // UndefinedReturnError: Factory 'db' returned undefined.
|
|
105
|
+
* // hint: "Did you forget a return statement?"
|
|
106
|
+
* ```
|
|
107
|
+
*/
|
|
108
|
+
declare class UndefinedReturnError extends ContainerError {
|
|
109
|
+
readonly hint: string;
|
|
110
|
+
readonly details: {
|
|
111
|
+
key: string;
|
|
112
|
+
chain: string[];
|
|
113
|
+
};
|
|
114
|
+
constructor(key: string, chain: string[]);
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Thrown when a factory function throws an error during resolution.
|
|
118
|
+
* Wraps the original error with resolution context.
|
|
119
|
+
*
|
|
120
|
+
* @example
|
|
121
|
+
* ```typescript
|
|
122
|
+
* container.db;
|
|
123
|
+
* // FactoryError: Factory 'db' threw an error: "Connection refused"
|
|
124
|
+
* ```
|
|
125
|
+
*/
|
|
126
|
+
declare class FactoryError extends ContainerError {
|
|
127
|
+
readonly hint: string;
|
|
128
|
+
readonly details: {
|
|
129
|
+
key: string;
|
|
130
|
+
chain: string[];
|
|
131
|
+
originalError: string;
|
|
132
|
+
};
|
|
133
|
+
readonly originalError: unknown;
|
|
134
|
+
constructor(key: string, chain: string[], originalError: unknown);
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Warning emitted when a singleton depends on a transient dependency.
|
|
138
|
+
* The transient value gets frozen inside the singleton — almost always a bug.
|
|
139
|
+
*
|
|
140
|
+
* @example
|
|
141
|
+
* ```typescript
|
|
142
|
+
* // ScopeMismatchWarning: Singleton 'userService' depends on transient 'requestId'.
|
|
143
|
+
* ```
|
|
144
|
+
*/
|
|
145
|
+
declare class ScopeMismatchWarning implements ContainerWarning {
|
|
146
|
+
readonly type: "scope_mismatch";
|
|
147
|
+
readonly message: string;
|
|
148
|
+
readonly hint: string;
|
|
149
|
+
readonly details: {
|
|
150
|
+
singleton: string;
|
|
151
|
+
transient: string;
|
|
152
|
+
};
|
|
153
|
+
constructor(singletonKey: string, transientKey: string);
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Warning emitted when an async `onInit()` rejects during lazy resolution.
|
|
157
|
+
* The error is collected (not thrown) because lazy access is fire-and-forget.
|
|
158
|
+
* Use `preload()` to await and surface async init errors.
|
|
159
|
+
*
|
|
160
|
+
* @example
|
|
161
|
+
* ```typescript
|
|
162
|
+
* // health().warnings may include:
|
|
163
|
+
* // { type: 'async_init_error', message: "onInit() for 'db' rejected: connection refused" }
|
|
164
|
+
* ```
|
|
165
|
+
*/
|
|
166
|
+
declare class AsyncInitErrorWarning implements ContainerWarning {
|
|
167
|
+
readonly type: "async_init_error";
|
|
168
|
+
readonly message: string;
|
|
169
|
+
readonly hint: string;
|
|
170
|
+
readonly details: {
|
|
171
|
+
key: string;
|
|
172
|
+
error: string;
|
|
173
|
+
};
|
|
174
|
+
constructor(key: string, error: unknown);
|
|
175
|
+
}
|
|
176
|
+
//#endregion
|
|
1
177
|
//#region src/domain/types.d.ts
|
|
2
178
|
/**
|
|
3
179
|
* A factory function that receives the container and returns an instance.
|
|
@@ -237,182 +413,6 @@ declare class ContainerBuilder<TContract extends Record<string, any> = Record<st
|
|
|
237
413
|
*/
|
|
238
414
|
declare function container<T extends Record<string, any> = Record<string, unknown>>(): ContainerBuilder<T>;
|
|
239
415
|
//#endregion
|
|
240
|
-
//#region src/domain/errors.d.ts
|
|
241
|
-
/**
|
|
242
|
-
* Base class for all container errors.
|
|
243
|
-
* Every error includes a human-readable `hint` and structured `details`
|
|
244
|
-
* so that AI tools and developers can auto-correct issues.
|
|
245
|
-
*
|
|
246
|
-
* @example
|
|
247
|
-
* ```typescript
|
|
248
|
-
* try { container.userService; }
|
|
249
|
-
* catch (e) {
|
|
250
|
-
* if (e instanceof ContainerError) {
|
|
251
|
-
* console.log(e.hint); // actionable fix
|
|
252
|
-
* console.log(e.details); // structured context
|
|
253
|
-
* }
|
|
254
|
-
* }
|
|
255
|
-
* ```
|
|
256
|
-
*/
|
|
257
|
-
declare abstract class ContainerError extends Error {
|
|
258
|
-
abstract readonly hint: string;
|
|
259
|
-
abstract readonly details: Record<string, unknown>;
|
|
260
|
-
constructor(message: string);
|
|
261
|
-
}
|
|
262
|
-
/**
|
|
263
|
-
* Thrown when a non-function value is passed in the deps definition.
|
|
264
|
-
*
|
|
265
|
-
* @example
|
|
266
|
-
* ```typescript
|
|
267
|
-
* container().add('apiKey', 'sk-123');
|
|
268
|
-
* // ContainerConfigError: 'apiKey' must be a factory function, got string.
|
|
269
|
-
* // hint: "Wrap it: apiKey: () => 'sk-123'"
|
|
270
|
-
* ```
|
|
271
|
-
*/
|
|
272
|
-
declare class ContainerConfigError extends ContainerError {
|
|
273
|
-
readonly hint: string;
|
|
274
|
-
readonly details: {
|
|
275
|
-
key: string;
|
|
276
|
-
actualType: string;
|
|
277
|
-
};
|
|
278
|
-
constructor(key: string, actualType: string);
|
|
279
|
-
}
|
|
280
|
-
/**
|
|
281
|
-
* Thrown when a reserved container method name is used as a dependency key.
|
|
282
|
-
*
|
|
283
|
-
* @example
|
|
284
|
-
* ```typescript
|
|
285
|
-
* container().add('inspect', () => 'foo');
|
|
286
|
-
* // ReservedKeyError: 'inspect' is a reserved container method.
|
|
287
|
-
* // hint: "Rename this dependency, e.g. 'inspectService' or 'dataInspector'."
|
|
288
|
-
* ```
|
|
289
|
-
*/
|
|
290
|
-
declare class ReservedKeyError extends ContainerError {
|
|
291
|
-
readonly hint: string;
|
|
292
|
-
readonly details: {
|
|
293
|
-
key: string;
|
|
294
|
-
reserved: string[];
|
|
295
|
-
};
|
|
296
|
-
constructor(key: string, reserved: readonly string[]);
|
|
297
|
-
}
|
|
298
|
-
/**
|
|
299
|
-
* Thrown when a dependency cannot be found during resolution.
|
|
300
|
-
* Includes fuzzy suggestion if a similar key exists.
|
|
301
|
-
*
|
|
302
|
-
* @example
|
|
303
|
-
* ```typescript
|
|
304
|
-
* container.userService;
|
|
305
|
-
* // ProviderNotFoundError: Cannot resolve 'userService': dependency 'userRepo' not found.
|
|
306
|
-
* // hint: "Did you mean 'userRepository'?"
|
|
307
|
-
* ```
|
|
308
|
-
*/
|
|
309
|
-
declare class ProviderNotFoundError extends ContainerError {
|
|
310
|
-
readonly hint: string;
|
|
311
|
-
readonly details: {
|
|
312
|
-
key: string;
|
|
313
|
-
chain: string[];
|
|
314
|
-
registered: string[];
|
|
315
|
-
suggestion: string | undefined;
|
|
316
|
-
};
|
|
317
|
-
constructor(key: string, chain: string[], registered: string[], suggestion?: string);
|
|
318
|
-
}
|
|
319
|
-
/**
|
|
320
|
-
* Thrown when a circular dependency is detected.
|
|
321
|
-
*
|
|
322
|
-
* @example
|
|
323
|
-
* ```typescript
|
|
324
|
-
* // CircularDependencyError: Circular dependency detected while resolving 'authService'.
|
|
325
|
-
* // Cycle: authService -> userService -> authService
|
|
326
|
-
* ```
|
|
327
|
-
*/
|
|
328
|
-
declare class CircularDependencyError extends ContainerError {
|
|
329
|
-
readonly hint: string;
|
|
330
|
-
readonly details: {
|
|
331
|
-
key: string;
|
|
332
|
-
chain: string[];
|
|
333
|
-
cycle: string;
|
|
334
|
-
};
|
|
335
|
-
constructor(key: string, chain: string[]);
|
|
336
|
-
}
|
|
337
|
-
/**
|
|
338
|
-
* Thrown when a factory function returns `undefined`.
|
|
339
|
-
*
|
|
340
|
-
* @example
|
|
341
|
-
* ```typescript
|
|
342
|
-
* container.db;
|
|
343
|
-
* // UndefinedReturnError: Factory 'db' returned undefined.
|
|
344
|
-
* // hint: "Did you forget a return statement?"
|
|
345
|
-
* ```
|
|
346
|
-
*/
|
|
347
|
-
declare class UndefinedReturnError extends ContainerError {
|
|
348
|
-
readonly hint: string;
|
|
349
|
-
readonly details: {
|
|
350
|
-
key: string;
|
|
351
|
-
chain: string[];
|
|
352
|
-
};
|
|
353
|
-
constructor(key: string, chain: string[]);
|
|
354
|
-
}
|
|
355
|
-
/**
|
|
356
|
-
* Thrown when a factory function throws an error during resolution.
|
|
357
|
-
* Wraps the original error with resolution context.
|
|
358
|
-
*
|
|
359
|
-
* @example
|
|
360
|
-
* ```typescript
|
|
361
|
-
* container.db;
|
|
362
|
-
* // FactoryError: Factory 'db' threw an error: "Connection refused"
|
|
363
|
-
* ```
|
|
364
|
-
*/
|
|
365
|
-
declare class FactoryError extends ContainerError {
|
|
366
|
-
readonly hint: string;
|
|
367
|
-
readonly details: {
|
|
368
|
-
key: string;
|
|
369
|
-
chain: string[];
|
|
370
|
-
originalError: string;
|
|
371
|
-
};
|
|
372
|
-
readonly originalError: unknown;
|
|
373
|
-
constructor(key: string, chain: string[], originalError: unknown);
|
|
374
|
-
}
|
|
375
|
-
/**
|
|
376
|
-
* Warning emitted when a singleton depends on a transient dependency.
|
|
377
|
-
* The transient value gets frozen inside the singleton — almost always a bug.
|
|
378
|
-
*
|
|
379
|
-
* @example
|
|
380
|
-
* ```typescript
|
|
381
|
-
* // ScopeMismatchWarning: Singleton 'userService' depends on transient 'requestId'.
|
|
382
|
-
* ```
|
|
383
|
-
*/
|
|
384
|
-
declare class ScopeMismatchWarning implements ContainerWarning {
|
|
385
|
-
readonly type: "scope_mismatch";
|
|
386
|
-
readonly message: string;
|
|
387
|
-
readonly hint: string;
|
|
388
|
-
readonly details: {
|
|
389
|
-
singleton: string;
|
|
390
|
-
transient: string;
|
|
391
|
-
};
|
|
392
|
-
constructor(singletonKey: string, transientKey: string);
|
|
393
|
-
}
|
|
394
|
-
/**
|
|
395
|
-
* Warning emitted when an async `onInit()` rejects during lazy resolution.
|
|
396
|
-
* The error is collected (not thrown) because lazy access is fire-and-forget.
|
|
397
|
-
* Use `preload()` to await and surface async init errors.
|
|
398
|
-
*
|
|
399
|
-
* @example
|
|
400
|
-
* ```typescript
|
|
401
|
-
* // health().warnings may include:
|
|
402
|
-
* // { type: 'async_init_error', message: "onInit() for 'db' rejected: connection refused" }
|
|
403
|
-
* ```
|
|
404
|
-
*/
|
|
405
|
-
declare class AsyncInitErrorWarning implements ContainerWarning {
|
|
406
|
-
readonly type: "async_init_error";
|
|
407
|
-
readonly message: string;
|
|
408
|
-
readonly hint: string;
|
|
409
|
-
readonly details: {
|
|
410
|
-
key: string;
|
|
411
|
-
error: string;
|
|
412
|
-
};
|
|
413
|
-
constructor(key: string, error: unknown);
|
|
414
|
-
}
|
|
415
|
-
//#endregion
|
|
416
416
|
//#region src/domain/lifecycle.d.ts
|
|
417
417
|
/**
|
|
418
418
|
* Implement this interface (or just add an `onInit` method) to run
|
package/dist/index.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
var e=class extends Error{constructor(e){super(e),this.name=this.constructor.name}},t=class extends e{hint;details;constructor(e,t){super(`'${e}' must be a factory function, got ${t}.`),this.hint=`Wrap it: ${e}: () => <your ${t} value>`,this.details={key:e,actualType:t}}},n=class extends e{hint;details;constructor(e,t){super(`'${e}' is a reserved container method.`),this.hint=`Rename this dependency, e.g. '${e}Service' or 'my${e[0].toUpperCase()}${e.slice(1)}'.`,this.details={key:e,reserved:[...t]}}},r=class extends e{hint;details;constructor(e,t,n,r){let i=t.length>0?`\n\nResolution chain: ${[...t,`${e} (not found)`].join(` -> `)}`:``,a=`\nRegistered keys: [${n.join(`, `)}]`,o=r?`\n\nDid you mean '${r}'?`:``;super(`Cannot resolve '${t[0]??e}': dependency '${e}' not found.${i}${a}${o}`);let s=`.add('${e}', (c) => new ${e[0].toUpperCase()}${e.slice(1)}(/* deps */))`;this.hint=r?`Did you mean '${r}'? Or add '${e}' to your container:\n container()${s}`:`Add '${e}' to your container:\n container()${s}`,this.details={key:e,chain:t,registered:n,suggestion:r}}},i=class extends e{hint;details;constructor(e,t){let n=[...t,e].join(` -> `);super(`Circular dependency detected while resolving '${t[0]}'.\n\nCycle: ${n}`),this.hint=[`To fix:`,` 1. Extract shared logic into a new dependency both can use`,` 2. Restructure so one doesn't depend on the other`,` 3. Use a mediator/event pattern to decouple them`].join(`
|
|
2
2
|
`),this.details={key:e,chain:t,cycle:n}}},a=class extends e{hint;details;constructor(e,t){let n=t.length>1?`\n\nResolution chain: ${t.join(` -> `)}`:``;super(`Factory '${e}' returned undefined.${n}`),this.hint=`Your factory function returned undefined. Did you forget a return statement?`,this.details={key:e,chain:t}}},o=class extends e{hint;details;originalError;constructor(e,t,n){let r=n instanceof Error?n.message:String(n),i=t.length>1?`\n\nResolution chain: ${[...t.slice(0,-1),`${e} (factory threw)`].join(` -> `)}`:``;super(`Factory '${e}' threw an error: "${r}"${i}`),this.hint=`Check the factory function for '${e}'. The error occurred during instantiation.`,this.details={key:e,chain:t,originalError:r},this.originalError=n}},s=class{type=`scope_mismatch`;message;hint;details;constructor(e,t){this.message=`Singleton '${e}' depends on transient '${t}'.`,this.hint=[`The transient value was resolved once and is now frozen inside the singleton.`,`This is almost always a bug.`,``,`To fix:`,` 1. Make '${e}' transient too: transient((c) => new ${e[0].toUpperCase()}${e.slice(1)}(c.${t}))`,` 2. Make '${t}' singleton if it doesn't need to change`,` 3. Inject a factory instead: ${t}Factory: () => () => <your value>`].join(`
|
|
3
|
-
`),this.details={singleton:e,transient:t}}},c=class{type=`async_init_error`;message;hint;details;constructor(e,t){let n=t instanceof Error?t.message:String(t);this.message=`onInit() for '${e}' rejected: ${n}`,this.hint=`Use preload('${e}') to await and handle async init errors.`,this.details={key:e,error:n}}};const l=[`scope`,`extend`,`module`,`preload`,`reset`,`inspect`,`describe`,`health`,`dispose`,`toString`];
|
|
3
|
+
`),this.details={singleton:e,transient:t}}},c=class{type=`async_init_error`;message;hint;details;constructor(e,t){let n=t instanceof Error?t.message:String(t);this.message=`onInit() for '${e}' rejected: ${n}`,this.hint=`Use preload('${e}') to await and handle async init errors.`,this.details={key:e,error:n}}};const l=[`scope`,`extend`,`module`,`preload`,`reset`,`inspect`,`describe`,`health`,`dispose`,`toString`];var u=class{resolving=new Set;enter(e){this.resolving.add(e)}leave(e){this.resolving.delete(e)}isResolving(e){return this.resolving.has(e)}},d=class{depGraph=new Map;createTrackingProxy(e,t,n){return new Proxy({},{get:(r,i)=>{if(typeof i==`symbol`)return;let a=i;return e.push(a),n(a,t)}})}getDepGraph(){return new Map(this.depGraph)}recordDeps(e,t){this.depGraph.set(e,t)}clearDepGraph(...e){for(let t of e)this.depGraph.delete(t)}clearAllDepGraph(){this.depGraph.clear()}};function f(e){return typeof e==`object`&&!!e&&`onInit`in e&&typeof e.onInit==`function`}function p(e){return typeof e==`object`&&!!e&&`onDestroy`in e&&typeof e.onDestroy==`function`}var m=class{validateConfig(e){for(let[r,i]of Object.entries(e)){if(l.includes(r))throw new n(r,l);if(typeof i!=`function`)throw new t(r,typeof i)}}suggestKey(e,t){let n,r=1/0;for(let i of t){let t=g(e,i);t<r&&(r=t,n=i)}if(!n)return;let i=Math.max(e.length,n.length);return 1-r/i>=.5?n:void 0}};function h(...e){let t=new Map,n=[];for(let r of e)for(let e of Object.keys(r)){let r=(t.get(e)??0)+1;t.set(e,r),r===2&&n.push(e)}return n}function g(e,t){let n=e.length,r=t.length;if(n===0)return r;if(r===0)return n;let i=Array(r+1),a=Array(r+1);for(let e=0;e<=r;e++)i[e]=e;for(let o=1;o<=n;o++){a[0]=o;for(let n=1;n<=r;n++){let r=e[o-1]===t[n-1]?0:1;a[n]=Math.min(i[n]+1,a[n-1]+1,i[n-1]+r)}[i,a]=[a,i]}return i[r]}const _=Symbol.for(`inwire:transient`);function v(e){let t=(t=>e(t));return t[_]=!0,t}function y(e){return typeof e==`function`&&_ in e&&e[_]===!0}var b=class{factories;cache;warnings=[];validator=new m;initCalled;deferOnInit=!1;parent;name;cycleDetector;dependencyTracker;constructor(e){this.factories=e.factories,this.cache=e.cache??new Map,this.parent=e.parent,this.name=e.name,this.initCalled=e.initCalled?new Set(e.initCalled):new Set,this.cycleDetector=e.cycleDetector,this.dependencyTracker=e.dependencyTracker}getName(){return this.name}resolve(e,t=[]){let n=this.factories.get(e);if(n&&!y(n)&&this.cache.has(e))return this.cache.get(e);if(!n){if(this.parent)return this.parent.resolve(e,t);let n=this.getAllRegisteredKeys();throw new r(e,t,n,this.validator.suggestKey(e,n))}if(this.cycleDetector.isResolving(e))throw new i(e,[...t]);this.cycleDetector.enter(e);let l=[...t,e];try{let t=[],r=n(this.dependencyTracker.createTrackingProxy(t,l,(e,t)=>this.resolve(e,t)));if(r===void 0)throw new a(e,l);if(this.dependencyTracker.recordDeps(e,t),!y(n))for(let n of t){let t=this.getFactory(n);t&&y(t)&&this.warnings.push(new s(e,n))}if(y(n)||this.cache.set(e,r),!this.deferOnInit&&!this.initCalled.has(e)&&f(r)){this.initCalled.add(e);let t=r.onInit();t instanceof Promise&&t.catch(t=>{this.warnings.push(new c(e,t))})}return r}catch(t){throw t instanceof i||t instanceof r||t instanceof a||t instanceof o?t:new o(e,l,t)}finally{this.cycleDetector.leave(e)}}isResolved(e){return this.cache.has(e)}getDepGraph(){return this.dependencyTracker.getDepGraph()}getResolvedKeys(){return[...this.cache.keys()]}getFactories(){return this.factories}getCache(){return this.cache}getWarnings(){return[...this.warnings]}getAllRegisteredKeys(){let e=new Set(this.factories.keys());if(this.parent)for(let t of this.parent.getAllRegisteredKeys())e.add(t);return[...e]}setDeferOnInit(e){this.deferOnInit=e}async callOnInit(e){if(this.initCalled.has(e)||!this.cache.has(e))return;let t=this.cache.get(e);f(t)&&await t.onInit(),this.initCalled.add(e)}clearInitState(...e){for(let t of e)this.initCalled.delete(t)}clearAllInitState(){this.initCalled.clear()}clearDepGraph(...e){this.dependencyTracker.clearDepGraph(...e)}clearAllDepGraph(){this.dependencyTracker.clearAllDepGraph()}clearWarnings(){this.warnings.length=0}clearWarningsForKeys(...e){let t=new Set(e),n=this.warnings.filter(e=>e.type===`async_init_error`?!t.has(e.details.key):e.type===`scope_mismatch`?!t.has(e.details.singleton)&&!t.has(e.details.transient):!0);this.warnings.length=0,this.warnings.push(...n)}getInitCalled(){return this.initCalled}getFactory(e){return this.factories.get(e)??this.parent?.getFactory(e)}},x=class{constructor(e){this.resolver=e}async dispose(){let e=this.resolver.getCache(),t=[...e.entries()].reverse(),n=[];for(let[,e]of t)if(p(e))try{await e.onDestroy()}catch(e){n.push(e)}if(e.clear(),this.resolver.clearAllInitState(),this.resolver.clearAllDepGraph(),this.resolver.clearWarnings(),n.length===1)throw n[0];if(n.length>1)throw AggregateError(n,`dispose() encountered ${n.length} errors`)}},S=class{constructor(e){this.resolver=e}inspect(){let e={};for(let[t,n]of this.resolver.getFactories())e[t]={key:t,resolved:this.resolver.isResolved(t),deps:this.resolver.getDepGraph().get(t)??[],scope:y(n)?`transient`:`singleton`};let t=this.resolver.getName();return t?{name:t,providers:e}:{providers:e}}describe(e){let t=this.resolver.getFactories().get(e);return t?{key:e,resolved:this.resolver.isResolved(e),deps:this.resolver.getDepGraph().get(e)??[],scope:y(t)?`transient`:`singleton`}:{key:e,resolved:!1,deps:[],scope:`singleton`}}health(){let e=[...this.resolver.getFactories().keys()],t=this.resolver.getResolvedKeys(),n=new Set(t),r=this.resolver.getWarnings().map(e=>({type:e.type,message:e.message,details:e.details}));return{totalProviders:e.length,resolved:t,unresolved:e.filter(e=>!n.has(e)),warnings:r}}toString(){let e=[];for(let[t]of this.resolver.getFactories()){let n=this.resolver.isResolved(t),r=this.resolver.getDepGraph().get(t),i=r&&r.length>0?` -> [${r.join(`, `)}]`:``,a=n?`(resolved)`:`(pending)`;e.push(`${t}${i} ${a}`)}let t=this.resolver.getName();return`${t?`Scope(${t})`:`Container`} { ${e.join(`, `)} }`}};function C(e,t){let n=new Map,r=new Map;for(let e of t)n.set(e,0);for(let i of t){let a=e.get(i)??[];for(let e of a)if(t.has(e)){n.set(i,(n.get(i)??0)+1);let t=r.get(e)??[];t.push(i),r.set(e,t)}}let i=[],a=[...t].filter(e=>n.get(e)===0);for(;a.length>0;){i.push(a);let e=[];for(let t of a)for(let i of r.get(t)??[]){let t=(n.get(i)??1)-1;n.set(i,t),t===0&&e.push(i)}a=e}if(i.reduce((e,t)=>e+t.length,0)<t.size){let e=new Set(i.flat()),n=[...t].filter(t=>!e.has(t));throw Error(`Incomplete topological sort: [${n.join(`, `)}] could not be ordered. This may indicate a cycle in the dependency graph.`)}return i}var w=class{constructor(e){this.resolver=e}async preload(...e){let t=e.length>0?e:[...this.resolver.getFactories().keys()],n=new Set(this.resolver.getCache().keys());this.resolver.setDeferOnInit(!0);try{for(let e of t)this.resolver.resolve(e)}catch(e){let t=this.resolver.getCache();for(let e of t.keys())n.has(e)||t.delete(e);throw e}finally{this.resolver.setDeferOnInit(!1)}let r=this.resolver.getDepGraph(),i=new Set,a=e=>{if(!i.has(e)){i.add(e);for(let t of r.get(e)??[])a(t)}};for(let e of t)a(e);let o=C(r,i),s=[];for(let e of o){let t=await Promise.allSettled(e.map(e=>this.resolver.callOnInit(e)));for(let e of t)e.status===`rejected`&&s.push(e.reason)}if(s.length===1)throw s[0];if(s.length>1)throw AggregateError(s,`preload() encountered ${s.length} onInit errors`)}};const T=new m;function E(e,t){let n=new S(e),r=new w(e),i=new x(e),a={scope:(n,r)=>{T.validateConfig(n);let i=new Map;for(let[e,t]of Object.entries(n))i.set(e,t);return E(new b({factories:i,parent:e,name:r?.name,cycleDetector:new u,dependencyTracker:new d}),t)},extend:n=>{T.validateConfig(n);let r=new Map(e.getFactories());for(let[e,t]of Object.entries(n))r.set(e,t);return E(new b({factories:r,cache:new Map(e.getCache()),initCalled:e.getInitCalled(),cycleDetector:new u,dependencyTracker:new d}),t)},module:e=>{if(!t)throw Error(`module() is not available`);let n=e(t());return a.extend(n._toRecord())},preload:(...e)=>r.preload(...e),reset:(...t)=>{let n=e.getCache();if(t.length===0)n.clear(),e.clearAllInitState(),e.clearAllDepGraph(),e.clearWarnings();else{for(let e of t)n.delete(e);e.clearInitState(...t),e.clearDepGraph(...t),e.clearWarningsForKeys(...t)}},inspect:()=>n.inspect(),describe:e=>n.describe(e),health:()=>n.health(),toString:()=>n.toString(),dispose:()=>i.dispose()};return new Proxy({},{get(t,r){if(typeof r==`symbol`)return r===Symbol.toPrimitive||r===Symbol.toStringTag?()=>n.toString():void 0;let i=r;return i in a?a[i]:e.resolve(i)},has(t,n){if(typeof n==`symbol`)return!1;let r=n;return r in a||e.getFactories().has(r)||e.getAllRegisteredKeys().includes(r)},ownKeys(){return[...e.getAllRegisteredKeys(),...Object.keys(a)]},getOwnPropertyDescriptor(t,n){if(typeof n==`symbol`)return;let r=n;if(r in a||e.getFactories().has(r)||e.getAllRegisteredKeys().includes(r))return{configurable:!0,enumerable:!(r in a),writable:!1}}})}var D=class e{factories=new Map;add(e,t){return this.validateKey(e),typeof t==`function`?this.factories.set(e,t):this.factories.set(e,()=>t),this}addTransient(e,t){return this.validateKey(e),this.factories.set(e,v(t)),this}addModule(e){return e(this)}_toRecord(){return Object.fromEntries(this.factories)}build(){return E(new b({factories:new Map(this.factories),cycleDetector:new u,dependencyTracker:new d}),()=>new e)}validateKey(e){if(l.includes(e))throw new n(e,l)}};function O(){return new D}export{c as AsyncInitErrorWarning,i as CircularDependencyError,D as ContainerBuilder,t as ContainerConfigError,e as ContainerError,o as FactoryError,r as ProviderNotFoundError,n as ReservedKeyError,s as ScopeMismatchWarning,a as UndefinedReturnError,O as container,h as detectDuplicateKeys,v as transient};
|
|
4
4
|
//# sourceMappingURL=index.mjs.map
|
package/package.json
CHANGED