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.d.ts
ADDED
|
@@ -0,0 +1,387 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A factory function that receives the container and returns an instance.
|
|
3
|
+
*
|
|
4
|
+
* @example
|
|
5
|
+
* ```typescript
|
|
6
|
+
* const factory: Factory<MyService> = (c) => new MyService(c.db);
|
|
7
|
+
* ```
|
|
8
|
+
*/
|
|
9
|
+
type Factory<T = any> = (container: any) => T;
|
|
10
|
+
/**
|
|
11
|
+
* An object of factory functions — the definition of a container.
|
|
12
|
+
* Each key maps to a factory that produces the dependency.
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* ```typescript
|
|
16
|
+
* const deps: DepsDefinition = {
|
|
17
|
+
* logger: () => new LoggerService(),
|
|
18
|
+
* db: () => new Database(process.env.DB_URL!),
|
|
19
|
+
* userRepo: (c) => new PgUserRepo(c.db),
|
|
20
|
+
* };
|
|
21
|
+
* ```
|
|
22
|
+
*/
|
|
23
|
+
type DepsDefinition = Record<string, Factory>;
|
|
24
|
+
/**
|
|
25
|
+
* Extracts the resolved types from a deps definition.
|
|
26
|
+
* Maps each key to the return type of its factory function.
|
|
27
|
+
*
|
|
28
|
+
* @example
|
|
29
|
+
* ```typescript
|
|
30
|
+
* type Resolved = ResolvedDeps<{ logger: () => LoggerService }>;
|
|
31
|
+
* // { logger: LoggerService }
|
|
32
|
+
* ```
|
|
33
|
+
*/
|
|
34
|
+
type ResolvedDeps<T extends DepsDefinition> = {
|
|
35
|
+
readonly [K in keyof T]: ReturnType<T[K]>;
|
|
36
|
+
};
|
|
37
|
+
/**
|
|
38
|
+
* Options for creating a scoped container.
|
|
39
|
+
*/
|
|
40
|
+
interface ScopeOptions {
|
|
41
|
+
/** Optional name for the scope, useful for debugging and introspection. */
|
|
42
|
+
name?: string;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Full container type exposed to the user.
|
|
46
|
+
* Combines resolved dependencies with container methods.
|
|
47
|
+
*
|
|
48
|
+
* @example
|
|
49
|
+
* ```typescript
|
|
50
|
+
* const container: Container<{ logger: LoggerService }> = createContainer({
|
|
51
|
+
* logger: () => new LoggerService(),
|
|
52
|
+
* });
|
|
53
|
+
* container.logger; // LoggerService
|
|
54
|
+
* container.inspect(); // ContainerGraph
|
|
55
|
+
* ```
|
|
56
|
+
*/
|
|
57
|
+
type Container<T extends Record<string, any> = Record<string, any>> = T & IContainer<T>;
|
|
58
|
+
/**
|
|
59
|
+
* Container methods interface. Defines the API available on every container.
|
|
60
|
+
*/
|
|
61
|
+
interface IContainer<T extends Record<string, any> = Record<string, any>> {
|
|
62
|
+
/**
|
|
63
|
+
* Creates a child container with additional dependencies.
|
|
64
|
+
* Child inherits all parent singletons and can add/override deps.
|
|
65
|
+
*
|
|
66
|
+
* @example
|
|
67
|
+
* ```typescript
|
|
68
|
+
* const request = app.scope({
|
|
69
|
+
* requestId: () => crypto.randomUUID(),
|
|
70
|
+
* currentUser: () => extractUser(req),
|
|
71
|
+
* });
|
|
72
|
+
* request.requestId; // scoped singleton
|
|
73
|
+
* request.logger; // inherited from parent
|
|
74
|
+
* ```
|
|
75
|
+
*/
|
|
76
|
+
scope<E extends DepsDefinition>(extra: E, options?: ScopeOptions): Container<T & ResolvedDeps<E>>;
|
|
77
|
+
/**
|
|
78
|
+
* Returns a new container with additional dependencies.
|
|
79
|
+
* Existing singletons are shared. The original container is not modified.
|
|
80
|
+
*
|
|
81
|
+
* @example
|
|
82
|
+
* ```typescript
|
|
83
|
+
* const appWithAuth = app.extend(authDeps);
|
|
84
|
+
* ```
|
|
85
|
+
*/
|
|
86
|
+
extend<E extends DepsDefinition>(extra: E): Container<T & ResolvedDeps<E>>;
|
|
87
|
+
/**
|
|
88
|
+
* Pre-resolves dependencies (warm-up).
|
|
89
|
+
* Call with specific keys to resolve only those, or without arguments to resolve all.
|
|
90
|
+
*
|
|
91
|
+
* @example
|
|
92
|
+
* ```typescript
|
|
93
|
+
* await container.preload('db', 'cache'); // specific deps
|
|
94
|
+
* await container.preload(); // all deps
|
|
95
|
+
* ```
|
|
96
|
+
*/
|
|
97
|
+
preload(...keys: (keyof T)[]): Promise<void>;
|
|
98
|
+
/**
|
|
99
|
+
* Returns the full dependency graph as a serializable JSON object.
|
|
100
|
+
* Useful for AI analysis of the architecture.
|
|
101
|
+
*
|
|
102
|
+
* @example
|
|
103
|
+
* ```typescript
|
|
104
|
+
* container.inspect();
|
|
105
|
+
* // { providers: { logger: { key: 'logger', resolved: true, deps: [], scope: 'singleton' } } }
|
|
106
|
+
* ```
|
|
107
|
+
*/
|
|
108
|
+
inspect(): ContainerGraph;
|
|
109
|
+
/**
|
|
110
|
+
* Returns detailed information about a specific provider.
|
|
111
|
+
*
|
|
112
|
+
* @example
|
|
113
|
+
* ```typescript
|
|
114
|
+
* container.describe('userService');
|
|
115
|
+
* // { key: 'userService', resolved: true, deps: ['userRepo', 'logger'], scope: 'singleton' }
|
|
116
|
+
* ```
|
|
117
|
+
*/
|
|
118
|
+
describe(key: keyof T): ProviderInfo;
|
|
119
|
+
/**
|
|
120
|
+
* Returns container health status and warnings.
|
|
121
|
+
*
|
|
122
|
+
* @example
|
|
123
|
+
* ```typescript
|
|
124
|
+
* container.health();
|
|
125
|
+
* // { totalProviders: 12, resolved: ['db', 'logger'], unresolved: ['cache'], warnings: [] }
|
|
126
|
+
* ```
|
|
127
|
+
*/
|
|
128
|
+
health(): ContainerHealth;
|
|
129
|
+
/**
|
|
130
|
+
* Invalidates cached singletons, forcing re-creation on next access.
|
|
131
|
+
* Does not affect parent scopes.
|
|
132
|
+
*
|
|
133
|
+
* @example
|
|
134
|
+
* ```typescript
|
|
135
|
+
* container.reset('db'); // reset one
|
|
136
|
+
* container.reset('db', 'cache'); // reset multiple
|
|
137
|
+
* ```
|
|
138
|
+
*/
|
|
139
|
+
reset(...keys: (keyof T)[]): void;
|
|
140
|
+
/**
|
|
141
|
+
* Disposes the container. Calls `onDestroy()` on all resolved instances
|
|
142
|
+
* that implement it, in reverse resolution order.
|
|
143
|
+
*
|
|
144
|
+
* @example
|
|
145
|
+
* ```typescript
|
|
146
|
+
* await container.dispose();
|
|
147
|
+
* ```
|
|
148
|
+
*/
|
|
149
|
+
dispose(): Promise<void>;
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Full dependency graph of the container.
|
|
153
|
+
*/
|
|
154
|
+
interface ContainerGraph {
|
|
155
|
+
name?: string;
|
|
156
|
+
providers: Record<string, ProviderInfo>;
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* Detailed information about a single provider/dependency.
|
|
160
|
+
*/
|
|
161
|
+
interface ProviderInfo {
|
|
162
|
+
key: string;
|
|
163
|
+
resolved: boolean;
|
|
164
|
+
deps: string[];
|
|
165
|
+
scope: 'singleton' | 'transient';
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Container health status with warnings.
|
|
169
|
+
*/
|
|
170
|
+
interface ContainerHealth {
|
|
171
|
+
totalProviders: number;
|
|
172
|
+
resolved: string[];
|
|
173
|
+
unresolved: string[];
|
|
174
|
+
warnings: ContainerWarning[];
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* A warning detected by the container's runtime analysis.
|
|
178
|
+
*/
|
|
179
|
+
interface ContainerWarning {
|
|
180
|
+
type: 'scope_mismatch' | 'duplicate_key';
|
|
181
|
+
message: string;
|
|
182
|
+
details: Record<string, unknown>;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* Base class for all container errors.
|
|
187
|
+
* Every error includes a human-readable `hint` and structured `details`
|
|
188
|
+
* so that AI tools and developers can auto-correct issues.
|
|
189
|
+
*
|
|
190
|
+
* @example
|
|
191
|
+
* ```typescript
|
|
192
|
+
* try { container.userService; }
|
|
193
|
+
* catch (e) {
|
|
194
|
+
* if (e instanceof ContainerError) {
|
|
195
|
+
* console.log(e.hint); // actionable fix
|
|
196
|
+
* console.log(e.details); // structured context
|
|
197
|
+
* }
|
|
198
|
+
* }
|
|
199
|
+
* ```
|
|
200
|
+
*/
|
|
201
|
+
declare abstract class ContainerError extends Error {
|
|
202
|
+
abstract readonly hint: string;
|
|
203
|
+
abstract readonly details: Record<string, unknown>;
|
|
204
|
+
constructor(message: string);
|
|
205
|
+
}
|
|
206
|
+
/**
|
|
207
|
+
* Thrown when a non-function value is passed in the deps definition.
|
|
208
|
+
*
|
|
209
|
+
* @example
|
|
210
|
+
* ```typescript
|
|
211
|
+
* createContainer({ apiKey: 'sk-123' });
|
|
212
|
+
* // ContainerConfigError: 'apiKey' must be a factory function, got string.
|
|
213
|
+
* // hint: "Wrap it: apiKey: () => 'sk-123'"
|
|
214
|
+
* ```
|
|
215
|
+
*/
|
|
216
|
+
declare class ContainerConfigError extends ContainerError {
|
|
217
|
+
readonly hint: string;
|
|
218
|
+
readonly details: Record<string, unknown>;
|
|
219
|
+
constructor(key: string, actualType: string);
|
|
220
|
+
}
|
|
221
|
+
/**
|
|
222
|
+
* Thrown when a reserved container method name is used as a dependency key.
|
|
223
|
+
*
|
|
224
|
+
* @example
|
|
225
|
+
* ```typescript
|
|
226
|
+
* createContainer({ inspect: () => 'foo' });
|
|
227
|
+
* // ReservedKeyError: 'inspect' is a reserved container method.
|
|
228
|
+
* // hint: "Rename this dependency, e.g. 'inspectService' or 'dataInspector'."
|
|
229
|
+
* ```
|
|
230
|
+
*/
|
|
231
|
+
declare class ReservedKeyError extends ContainerError {
|
|
232
|
+
readonly hint: string;
|
|
233
|
+
readonly details: Record<string, unknown>;
|
|
234
|
+
constructor(key: string, reserved: readonly string[]);
|
|
235
|
+
}
|
|
236
|
+
/**
|
|
237
|
+
* Thrown when a dependency cannot be found during resolution.
|
|
238
|
+
* Includes fuzzy suggestion if a similar key exists.
|
|
239
|
+
*
|
|
240
|
+
* @example
|
|
241
|
+
* ```typescript
|
|
242
|
+
* container.userService;
|
|
243
|
+
* // ProviderNotFoundError: Cannot resolve 'userService': dependency 'userRepo' not found.
|
|
244
|
+
* // hint: "Did you mean 'userRepository'?"
|
|
245
|
+
* ```
|
|
246
|
+
*/
|
|
247
|
+
declare class ProviderNotFoundError extends ContainerError {
|
|
248
|
+
readonly hint: string;
|
|
249
|
+
readonly details: Record<string, unknown>;
|
|
250
|
+
constructor(key: string, chain: string[], registered: string[], suggestion?: string);
|
|
251
|
+
}
|
|
252
|
+
/**
|
|
253
|
+
* Thrown when a circular dependency is detected.
|
|
254
|
+
*
|
|
255
|
+
* @example
|
|
256
|
+
* ```typescript
|
|
257
|
+
* // CircularDependencyError: Circular dependency detected while resolving 'authService'.
|
|
258
|
+
* // Cycle: authService -> userService -> authService
|
|
259
|
+
* ```
|
|
260
|
+
*/
|
|
261
|
+
declare class CircularDependencyError extends ContainerError {
|
|
262
|
+
readonly hint: string;
|
|
263
|
+
readonly details: Record<string, unknown>;
|
|
264
|
+
constructor(key: string, chain: string[]);
|
|
265
|
+
}
|
|
266
|
+
/**
|
|
267
|
+
* Thrown when a factory function returns `undefined`.
|
|
268
|
+
*
|
|
269
|
+
* @example
|
|
270
|
+
* ```typescript
|
|
271
|
+
* container.db;
|
|
272
|
+
* // UndefinedReturnError: Factory 'db' returned undefined.
|
|
273
|
+
* // hint: "Did you forget a return statement?"
|
|
274
|
+
* ```
|
|
275
|
+
*/
|
|
276
|
+
declare class UndefinedReturnError extends ContainerError {
|
|
277
|
+
readonly hint: string;
|
|
278
|
+
readonly details: Record<string, unknown>;
|
|
279
|
+
constructor(key: string, chain: string[]);
|
|
280
|
+
}
|
|
281
|
+
/**
|
|
282
|
+
* Thrown when a factory function throws an error during resolution.
|
|
283
|
+
* Wraps the original error with resolution context.
|
|
284
|
+
*
|
|
285
|
+
* @example
|
|
286
|
+
* ```typescript
|
|
287
|
+
* container.db;
|
|
288
|
+
* // FactoryError: Factory 'db' threw an error: "Connection refused"
|
|
289
|
+
* ```
|
|
290
|
+
*/
|
|
291
|
+
declare class FactoryError extends ContainerError {
|
|
292
|
+
readonly hint: string;
|
|
293
|
+
readonly details: Record<string, unknown>;
|
|
294
|
+
readonly originalError: unknown;
|
|
295
|
+
constructor(key: string, chain: string[], originalError: unknown);
|
|
296
|
+
}
|
|
297
|
+
/**
|
|
298
|
+
* Warning emitted when a singleton depends on a transient dependency.
|
|
299
|
+
* The transient value gets frozen inside the singleton — almost always a bug.
|
|
300
|
+
*
|
|
301
|
+
* @example
|
|
302
|
+
* ```typescript
|
|
303
|
+
* // ScopeMismatchWarning: Singleton 'userService' depends on transient 'requestId'.
|
|
304
|
+
* ```
|
|
305
|
+
*/
|
|
306
|
+
declare class ScopeMismatchWarning {
|
|
307
|
+
readonly type: "scope_mismatch";
|
|
308
|
+
readonly message: string;
|
|
309
|
+
readonly hint: string;
|
|
310
|
+
readonly details: Record<string, unknown>;
|
|
311
|
+
constructor(singletonKey: string, transientKey: string);
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
/**
|
|
315
|
+
* Creates a dependency injection container from an object of factory functions.
|
|
316
|
+
* Each factory receives the container and returns an instance.
|
|
317
|
+
* Dependencies are resolved lazily and cached as singletons by default.
|
|
318
|
+
*
|
|
319
|
+
* @example
|
|
320
|
+
* ```typescript
|
|
321
|
+
* const container = createContainer({
|
|
322
|
+
* logger: () => new LoggerService(),
|
|
323
|
+
* db: () => new Database(process.env.DB_URL!),
|
|
324
|
+
* userRepo: (c): UserRepository => new PgUserRepo(c.db),
|
|
325
|
+
* userService: (c) => new UserService(c.userRepo, c.logger),
|
|
326
|
+
* });
|
|
327
|
+
*
|
|
328
|
+
* container.userService; // type: UserService — lazy, singleton, fully resolved
|
|
329
|
+
* ```
|
|
330
|
+
*/
|
|
331
|
+
declare function createContainer<T extends DepsDefinition>(defs: T): Container<ResolvedDeps<T>>;
|
|
332
|
+
|
|
333
|
+
/**
|
|
334
|
+
* Wraps a factory function to produce a new instance on every access,
|
|
335
|
+
* instead of the default singleton behavior.
|
|
336
|
+
*
|
|
337
|
+
* @example
|
|
338
|
+
* ```typescript
|
|
339
|
+
* import { createContainer, transient } from 'inwire';
|
|
340
|
+
*
|
|
341
|
+
* const container = createContainer({
|
|
342
|
+
* logger: () => new LoggerService(), // singleton (default)
|
|
343
|
+
* requestId: transient(() => crypto.randomUUID()), // new instance every access
|
|
344
|
+
* });
|
|
345
|
+
*
|
|
346
|
+
* container.requestId; // 'abc-123'
|
|
347
|
+
* container.requestId; // 'def-456' (different!)
|
|
348
|
+
* ```
|
|
349
|
+
*/
|
|
350
|
+
declare function transient<T>(factory: Factory<T>): Factory<T>;
|
|
351
|
+
|
|
352
|
+
/**
|
|
353
|
+
* Implement this interface (or just add an `onInit` method) to run
|
|
354
|
+
* initialization logic when the dependency is first resolved.
|
|
355
|
+
*
|
|
356
|
+
* @example
|
|
357
|
+
* ```typescript
|
|
358
|
+
* class Database implements OnInit {
|
|
359
|
+
* async onInit() { await this.connect(); }
|
|
360
|
+
* }
|
|
361
|
+
* ```
|
|
362
|
+
*/
|
|
363
|
+
interface OnInit {
|
|
364
|
+
onInit(): void | Promise<void>;
|
|
365
|
+
}
|
|
366
|
+
/**
|
|
367
|
+
* Implement this interface (or just add an `onDestroy` method) to run
|
|
368
|
+
* cleanup logic when `container.dispose()` is called.
|
|
369
|
+
*
|
|
370
|
+
* @example
|
|
371
|
+
* ```typescript
|
|
372
|
+
* class Database implements OnDestroy {
|
|
373
|
+
* async onDestroy() { await this.disconnect(); }
|
|
374
|
+
* }
|
|
375
|
+
* ```
|
|
376
|
+
*/
|
|
377
|
+
interface OnDestroy {
|
|
378
|
+
onDestroy(): void | Promise<void>;
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
/**
|
|
382
|
+
* Detects duplicate keys across multiple modules (spread objects).
|
|
383
|
+
* Returns an array of keys that appear in more than one source.
|
|
384
|
+
*/
|
|
385
|
+
declare function detectDuplicateKeys(...modules: Record<string, unknown>[]): string[];
|
|
386
|
+
|
|
387
|
+
export { CircularDependencyError, type Container, ContainerConfigError, ContainerError, type ContainerGraph, type ContainerHealth, type ContainerWarning, type DepsDefinition, type Factory, FactoryError, type IContainer, type OnDestroy, type OnInit, type ProviderInfo, ProviderNotFoundError, ReservedKeyError, type ResolvedDeps, ScopeMismatchWarning, type ScopeOptions, UndefinedReturnError, createContainer, detectDuplicateKeys, transient };
|