inwire 1.0.1 → 1.0.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 +6 -6
- package/dist/{index.d.ts → index.d.mts} +175 -145
- package/dist/index.mjs +4 -0
- package/package.json +8 -7
- package/dist/index.js +0 -23
package/README.md
CHANGED
|
@@ -2,18 +2,18 @@
|
|
|
2
2
|
|
|
3
3
|
Zero-ceremony dependency injection for TypeScript. Full inference, no decorators, no tokens. Built-in introspection for AI tooling and debugging.
|
|
4
4
|
|
|
5
|
+
[](https://www.npmjs.com/package/inwire)
|
|
6
|
+
[](https://github.com/axelhamil/inwire/actions)
|
|
5
7
|
[](https://bundlephobia.com/package/inwire)
|
|
6
|
-
[](https://github.com)
|
|
8
|
-
[](https://github.com)
|
|
9
|
-
[](./vitest.config.ts)
|
|
8
|
+
[](https://www.npmjs.com/package/inwire)
|
|
10
9
|
[](https://www.typescriptlang.org/)
|
|
11
|
-
[](https://github.com/axelhamil/inwire/blob/main/LICENSE)
|
|
11
|
+
[](https://www.npmjs.com/package/inwire)
|
|
12
12
|
|
|
13
13
|
## Install
|
|
14
14
|
|
|
15
15
|
```bash
|
|
16
|
-
npm
|
|
16
|
+
npm i inwire
|
|
17
17
|
```
|
|
18
18
|
|
|
19
19
|
## Quick Start
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
//#region src/domain/types.d.ts
|
|
1
2
|
/**
|
|
2
3
|
* A factory function that receives the container and returns an instance.
|
|
3
4
|
*
|
|
@@ -6,7 +7,7 @@
|
|
|
6
7
|
* const factory: Factory<MyService> = (c) => new MyService(c.db);
|
|
7
8
|
* ```
|
|
8
9
|
*/
|
|
9
|
-
type Factory<T =
|
|
10
|
+
type Factory<T = unknown> = (container: any) => T;
|
|
10
11
|
/**
|
|
11
12
|
* An object of factory functions — the definition of a container.
|
|
12
13
|
* Each key maps to a factory that produces the dependency.
|
|
@@ -31,15 +32,13 @@ type DepsDefinition = Record<string, Factory>;
|
|
|
31
32
|
* // { logger: LoggerService }
|
|
32
33
|
* ```
|
|
33
34
|
*/
|
|
34
|
-
type ResolvedDeps<T extends DepsDefinition> = {
|
|
35
|
-
readonly [K in keyof T]: ReturnType<T[K]>;
|
|
36
|
-
};
|
|
35
|
+
type ResolvedDeps<T extends DepsDefinition> = { readonly [K in keyof T]: ReturnType<T[K]> };
|
|
37
36
|
/**
|
|
38
37
|
* Options for creating a scoped container.
|
|
39
38
|
*/
|
|
40
39
|
interface ScopeOptions {
|
|
41
|
-
|
|
42
|
-
|
|
40
|
+
/** Optional name for the scope, useful for debugging and introspection. */
|
|
41
|
+
name?: string;
|
|
43
42
|
}
|
|
44
43
|
/**
|
|
45
44
|
* Full container type exposed to the user.
|
|
@@ -54,134 +53,135 @@ interface ScopeOptions {
|
|
|
54
53
|
* container.inspect(); // ContainerGraph
|
|
55
54
|
* ```
|
|
56
55
|
*/
|
|
57
|
-
type Container<T extends Record<string,
|
|
56
|
+
type Container<T extends Record<string, unknown> = Record<string, unknown>> = T & IContainer<T>;
|
|
58
57
|
/**
|
|
59
58
|
* Container methods interface. Defines the API available on every container.
|
|
60
59
|
*/
|
|
61
|
-
interface IContainer<T extends Record<string,
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
60
|
+
interface IContainer<T extends Record<string, unknown> = Record<string, unknown>> {
|
|
61
|
+
/**
|
|
62
|
+
* Creates a child container with additional dependencies.
|
|
63
|
+
* Child inherits all parent singletons and can add/override deps.
|
|
64
|
+
*
|
|
65
|
+
* @example
|
|
66
|
+
* ```typescript
|
|
67
|
+
* const request = app.scope({
|
|
68
|
+
* requestId: () => crypto.randomUUID(),
|
|
69
|
+
* currentUser: () => extractUser(req),
|
|
70
|
+
* });
|
|
71
|
+
* request.requestId; // scoped singleton
|
|
72
|
+
* request.logger; // inherited from parent
|
|
73
|
+
* ```
|
|
74
|
+
*/
|
|
75
|
+
scope<E extends DepsDefinition>(extra: E, options?: ScopeOptions): Container<Omit<T, keyof ResolvedDeps<E>> & ResolvedDeps<E>>;
|
|
76
|
+
/**
|
|
77
|
+
* Returns a new container with additional dependencies.
|
|
78
|
+
* Existing singletons are shared. The original container is not modified.
|
|
79
|
+
*
|
|
80
|
+
* @example
|
|
81
|
+
* ```typescript
|
|
82
|
+
* const appWithAuth = app.extend(authDeps);
|
|
83
|
+
* ```
|
|
84
|
+
*/
|
|
85
|
+
extend<E extends DepsDefinition>(extra: E): Container<Omit<T, keyof ResolvedDeps<E>> & ResolvedDeps<E>>;
|
|
86
|
+
/**
|
|
87
|
+
* Pre-resolves dependencies (warm-up).
|
|
88
|
+
* Call with specific keys to resolve only those, or without arguments to resolve all.
|
|
89
|
+
*
|
|
90
|
+
* @example
|
|
91
|
+
* ```typescript
|
|
92
|
+
* await container.preload('db', 'cache'); // specific deps
|
|
93
|
+
* await container.preload(); // all deps
|
|
94
|
+
* ```
|
|
95
|
+
*/
|
|
96
|
+
preload(...keys: (keyof T)[]): Promise<void>;
|
|
97
|
+
/**
|
|
98
|
+
* Returns the full dependency graph as a serializable JSON object.
|
|
99
|
+
* Useful for AI analysis of the architecture.
|
|
100
|
+
*
|
|
101
|
+
* @example
|
|
102
|
+
* ```typescript
|
|
103
|
+
* container.inspect();
|
|
104
|
+
* // { providers: { logger: { key: 'logger', resolved: true, deps: [], scope: 'singleton' } } }
|
|
105
|
+
* ```
|
|
106
|
+
*/
|
|
107
|
+
inspect(): ContainerGraph;
|
|
108
|
+
/**
|
|
109
|
+
* Returns detailed information about a specific provider.
|
|
110
|
+
*
|
|
111
|
+
* @example
|
|
112
|
+
* ```typescript
|
|
113
|
+
* container.describe('userService');
|
|
114
|
+
* // { key: 'userService', resolved: true, deps: ['userRepo', 'logger'], scope: 'singleton' }
|
|
115
|
+
* ```
|
|
116
|
+
*/
|
|
117
|
+
describe(key: keyof T): ProviderInfo;
|
|
118
|
+
/**
|
|
119
|
+
* Returns container health status and warnings.
|
|
120
|
+
*
|
|
121
|
+
* @example
|
|
122
|
+
* ```typescript
|
|
123
|
+
* container.health();
|
|
124
|
+
* // { totalProviders: 12, resolved: ['db', 'logger'], unresolved: ['cache'], warnings: [] }
|
|
125
|
+
* ```
|
|
126
|
+
*/
|
|
127
|
+
health(): ContainerHealth;
|
|
128
|
+
/**
|
|
129
|
+
* Invalidates cached singletons, forcing re-creation on next access.
|
|
130
|
+
* Does not affect parent scopes.
|
|
131
|
+
*
|
|
132
|
+
* @example
|
|
133
|
+
* ```typescript
|
|
134
|
+
* container.reset('db'); // reset one
|
|
135
|
+
* container.reset('db', 'cache'); // reset multiple
|
|
136
|
+
* ```
|
|
137
|
+
*/
|
|
138
|
+
reset(...keys: (keyof T)[]): void;
|
|
139
|
+
/**
|
|
140
|
+
* Disposes the container. Calls `onDestroy()` on all resolved instances
|
|
141
|
+
* that implement it, in reverse resolution order.
|
|
142
|
+
*
|
|
143
|
+
* @example
|
|
144
|
+
* ```typescript
|
|
145
|
+
* await container.dispose();
|
|
146
|
+
* ```
|
|
147
|
+
*/
|
|
148
|
+
dispose(): Promise<void>;
|
|
150
149
|
}
|
|
151
150
|
/**
|
|
152
151
|
* Full dependency graph of the container.
|
|
153
152
|
*/
|
|
154
153
|
interface ContainerGraph {
|
|
155
|
-
|
|
156
|
-
|
|
154
|
+
name?: string;
|
|
155
|
+
providers: Record<string, ProviderInfo>;
|
|
157
156
|
}
|
|
158
157
|
/**
|
|
159
158
|
* Detailed information about a single provider/dependency.
|
|
160
159
|
*/
|
|
161
160
|
interface ProviderInfo {
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
161
|
+
key: string;
|
|
162
|
+
resolved: boolean;
|
|
163
|
+
deps: string[];
|
|
164
|
+
scope: 'singleton' | 'transient';
|
|
166
165
|
}
|
|
167
166
|
/**
|
|
168
167
|
* Container health status with warnings.
|
|
169
168
|
*/
|
|
170
169
|
interface ContainerHealth {
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
170
|
+
totalProviders: number;
|
|
171
|
+
resolved: string[];
|
|
172
|
+
unresolved: string[];
|
|
173
|
+
warnings: ContainerWarning[];
|
|
175
174
|
}
|
|
176
175
|
/**
|
|
177
176
|
* A warning detected by the container's runtime analysis.
|
|
178
177
|
*/
|
|
179
178
|
interface ContainerWarning {
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
179
|
+
type: 'scope_mismatch';
|
|
180
|
+
message: string;
|
|
181
|
+
details: Record<string, unknown>;
|
|
183
182
|
}
|
|
184
|
-
|
|
183
|
+
//#endregion
|
|
184
|
+
//#region src/domain/errors.d.ts
|
|
185
185
|
/**
|
|
186
186
|
* Base class for all container errors.
|
|
187
187
|
* Every error includes a human-readable `hint` and structured `details`
|
|
@@ -199,9 +199,9 @@ interface ContainerWarning {
|
|
|
199
199
|
* ```
|
|
200
200
|
*/
|
|
201
201
|
declare abstract class ContainerError extends Error {
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
202
|
+
abstract readonly hint: string;
|
|
203
|
+
abstract readonly details: Record<string, unknown>;
|
|
204
|
+
constructor(message: string);
|
|
205
205
|
}
|
|
206
206
|
/**
|
|
207
207
|
* Thrown when a non-function value is passed in the deps definition.
|
|
@@ -214,9 +214,12 @@ declare abstract class ContainerError extends Error {
|
|
|
214
214
|
* ```
|
|
215
215
|
*/
|
|
216
216
|
declare class ContainerConfigError extends ContainerError {
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
217
|
+
readonly hint: string;
|
|
218
|
+
readonly details: {
|
|
219
|
+
key: string;
|
|
220
|
+
actualType: string;
|
|
221
|
+
};
|
|
222
|
+
constructor(key: string, actualType: string);
|
|
220
223
|
}
|
|
221
224
|
/**
|
|
222
225
|
* Thrown when a reserved container method name is used as a dependency key.
|
|
@@ -229,9 +232,12 @@ declare class ContainerConfigError extends ContainerError {
|
|
|
229
232
|
* ```
|
|
230
233
|
*/
|
|
231
234
|
declare class ReservedKeyError extends ContainerError {
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
+
readonly hint: string;
|
|
236
|
+
readonly details: {
|
|
237
|
+
key: string;
|
|
238
|
+
reserved: string[];
|
|
239
|
+
};
|
|
240
|
+
constructor(key: string, reserved: readonly string[]);
|
|
235
241
|
}
|
|
236
242
|
/**
|
|
237
243
|
* Thrown when a dependency cannot be found during resolution.
|
|
@@ -245,9 +251,14 @@ declare class ReservedKeyError extends ContainerError {
|
|
|
245
251
|
* ```
|
|
246
252
|
*/
|
|
247
253
|
declare class ProviderNotFoundError extends ContainerError {
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
254
|
+
readonly hint: string;
|
|
255
|
+
readonly details: {
|
|
256
|
+
key: string;
|
|
257
|
+
chain: string[];
|
|
258
|
+
registered: string[];
|
|
259
|
+
suggestion: string | undefined;
|
|
260
|
+
};
|
|
261
|
+
constructor(key: string, chain: string[], registered: string[], suggestion?: string);
|
|
251
262
|
}
|
|
252
263
|
/**
|
|
253
264
|
* Thrown when a circular dependency is detected.
|
|
@@ -259,9 +270,13 @@ declare class ProviderNotFoundError extends ContainerError {
|
|
|
259
270
|
* ```
|
|
260
271
|
*/
|
|
261
272
|
declare class CircularDependencyError extends ContainerError {
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
273
|
+
readonly hint: string;
|
|
274
|
+
readonly details: {
|
|
275
|
+
key: string;
|
|
276
|
+
chain: string[];
|
|
277
|
+
cycle: string;
|
|
278
|
+
};
|
|
279
|
+
constructor(key: string, chain: string[]);
|
|
265
280
|
}
|
|
266
281
|
/**
|
|
267
282
|
* Thrown when a factory function returns `undefined`.
|
|
@@ -274,9 +289,12 @@ declare class CircularDependencyError extends ContainerError {
|
|
|
274
289
|
* ```
|
|
275
290
|
*/
|
|
276
291
|
declare class UndefinedReturnError extends ContainerError {
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
292
|
+
readonly hint: string;
|
|
293
|
+
readonly details: {
|
|
294
|
+
key: string;
|
|
295
|
+
chain: string[];
|
|
296
|
+
};
|
|
297
|
+
constructor(key: string, chain: string[]);
|
|
280
298
|
}
|
|
281
299
|
/**
|
|
282
300
|
* Thrown when a factory function throws an error during resolution.
|
|
@@ -289,10 +307,14 @@ declare class UndefinedReturnError extends ContainerError {
|
|
|
289
307
|
* ```
|
|
290
308
|
*/
|
|
291
309
|
declare class FactoryError extends ContainerError {
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
310
|
+
readonly hint: string;
|
|
311
|
+
readonly details: {
|
|
312
|
+
key: string;
|
|
313
|
+
chain: string[];
|
|
314
|
+
originalError: string;
|
|
315
|
+
};
|
|
316
|
+
readonly originalError: unknown;
|
|
317
|
+
constructor(key: string, chain: string[], originalError: unknown);
|
|
296
318
|
}
|
|
297
319
|
/**
|
|
298
320
|
* Warning emitted when a singleton depends on a transient dependency.
|
|
@@ -303,14 +325,18 @@ declare class FactoryError extends ContainerError {
|
|
|
303
325
|
* // ScopeMismatchWarning: Singleton 'userService' depends on transient 'requestId'.
|
|
304
326
|
* ```
|
|
305
327
|
*/
|
|
306
|
-
declare class ScopeMismatchWarning {
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
328
|
+
declare class ScopeMismatchWarning implements ContainerWarning {
|
|
329
|
+
readonly type: "scope_mismatch";
|
|
330
|
+
readonly message: string;
|
|
331
|
+
readonly hint: string;
|
|
332
|
+
readonly details: {
|
|
333
|
+
singleton: string;
|
|
334
|
+
transient: string;
|
|
335
|
+
};
|
|
336
|
+
constructor(singletonKey: string, transientKey: string);
|
|
312
337
|
}
|
|
313
|
-
|
|
338
|
+
//#endregion
|
|
339
|
+
//#region src/application/create-container.d.ts
|
|
314
340
|
/**
|
|
315
341
|
* Creates a dependency injection container from an object of factory functions.
|
|
316
342
|
* Each factory receives the container and returns an instance.
|
|
@@ -329,7 +355,8 @@ declare class ScopeMismatchWarning {
|
|
|
329
355
|
* ```
|
|
330
356
|
*/
|
|
331
357
|
declare function createContainer<T extends DepsDefinition>(defs: T): Container<ResolvedDeps<T>>;
|
|
332
|
-
|
|
358
|
+
//#endregion
|
|
359
|
+
//#region src/infrastructure/transient.d.ts
|
|
333
360
|
/**
|
|
334
361
|
* Wraps a factory function to produce a new instance on every access,
|
|
335
362
|
* instead of the default singleton behavior.
|
|
@@ -348,7 +375,8 @@ declare function createContainer<T extends DepsDefinition>(defs: T): Container<R
|
|
|
348
375
|
* ```
|
|
349
376
|
*/
|
|
350
377
|
declare function transient<T>(factory: Factory<T>): Factory<T>;
|
|
351
|
-
|
|
378
|
+
//#endregion
|
|
379
|
+
//#region src/domain/lifecycle.d.ts
|
|
352
380
|
/**
|
|
353
381
|
* Implement this interface (or just add an `onInit` method) to run
|
|
354
382
|
* initialization logic when the dependency is first resolved.
|
|
@@ -361,7 +389,7 @@ declare function transient<T>(factory: Factory<T>): Factory<T>;
|
|
|
361
389
|
* ```
|
|
362
390
|
*/
|
|
363
391
|
interface OnInit {
|
|
364
|
-
|
|
392
|
+
onInit(): void | Promise<void>;
|
|
365
393
|
}
|
|
366
394
|
/**
|
|
367
395
|
* Implement this interface (or just add an `onDestroy` method) to run
|
|
@@ -375,13 +403,15 @@ interface OnInit {
|
|
|
375
403
|
* ```
|
|
376
404
|
*/
|
|
377
405
|
interface OnDestroy {
|
|
378
|
-
|
|
406
|
+
onDestroy(): void | Promise<void>;
|
|
379
407
|
}
|
|
380
|
-
|
|
408
|
+
//#endregion
|
|
409
|
+
//#region src/domain/validation.d.ts
|
|
381
410
|
/**
|
|
382
411
|
* Detects duplicate keys across multiple modules (spread objects).
|
|
383
412
|
* Returns an array of keys that appear in more than one source.
|
|
384
413
|
*/
|
|
385
414
|
declare function detectDuplicateKeys(...modules: Record<string, unknown>[]): string[];
|
|
386
|
-
|
|
415
|
+
//#endregion
|
|
387
416
|
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 };
|
|
417
|
+
//# sourceMappingURL=index.d.mts.map
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
function e(e){return typeof e==`object`&&!!e&&`onInit`in e&&typeof e.onInit==`function`}function t(e){return typeof e==`object`&&!!e&&`onDestroy`in e&&typeof e.onDestroy==`function`}const n=[`scope`,`extend`,`preload`,`reset`,`inspect`,`describe`,`health`,`dispose`,`toString`];var r=class extends Error{constructor(e){super(e),this.name=this.constructor.name}},i=class extends r{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}}},a=class extends r{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]}}},o=class extends r{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}`),this.hint=r?`Did you mean '${r}'? Or add '${e}' to your container:\n createContainer({\n ...existing,\n ${e}: (c) => new Your${e[0].toUpperCase()}${e.slice(1)}(/* deps */),\n });`:`Add '${e}' to your container:\n createContainer({\n ...existing,\n ${e}: (c) => new Your${e[0].toUpperCase()}${e.slice(1)}(/* deps */),\n });`,this.details={key:e,chain:t,registered:n,suggestion:r}}},s=class extends r{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
|
+
`),this.details={key:e,chain:t,cycle:n}}},c=class extends r{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}}},l=class extends r{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}},u=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}}},d=class{validateConfig(e){for(let[t,r]of Object.entries(e)){if(n.includes(t))throw new a(t,n);if(typeof r!=`function`)throw new i(t,typeof r)}}suggestKey(e,t){let n,r=1/0;for(let i of t){let t=p(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 f(...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 p(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 m=Symbol.for(`inwire:transient`);function h(e){let t=(t=>e(t));return t[m]=!0,t}function g(e){return typeof e==`function`&&m in e&&e[m]===!0}var _=class{factories;cache;resolving=new Set;depGraph=new Map;warnings=[];validator=new d;parent;name;constructor(e,t,n,r){this.factories=e,this.cache=t??new Map,this.parent=n,this.name=r}getName(){return this.name}resolve(t,n=[]){let r=this.factories.get(t);if(r&&!g(r)&&this.cache.has(t))return this.cache.get(t);if(!r){if(this.parent)return this.parent.resolve(t,n);let e=this.getAllRegisteredKeys();throw new o(t,n,e,this.validator.suggestKey(t,e))}if(this.resolving.has(t))throw new s(t,[...n]);this.resolving.add(t);let i=[...n,t];try{let n=[],a=r(this.createTrackingProxy(t,n,i));if(a===void 0)throw new c(t,i);if(this.depGraph.set(t,n),!g(r))for(let e of n){let n=this.getFactory(e);n&&g(n)&&this.warnings.push(new u(t,e))}if(g(r)||this.cache.set(t,a),e(a)){let e=a.onInit();e instanceof Promise&&e.catch(()=>{})}return a}catch(e){throw e instanceof s||e instanceof o||e instanceof c||e instanceof l?e:new l(t,i,e)}finally{this.resolving.delete(t)}}isResolved(e){return this.cache.has(e)}getDepGraph(){return new Map(this.depGraph)}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]}createTrackingProxy(e,t,n){return new Proxy({},{get:(e,r)=>{if(typeof r==`symbol`)return;let i=r;return t.push(i),this.resolve(i,n)}})}getFactory(e){return this.factories.get(e)??this.parent?.getFactory(e)}},v=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:g(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:g(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 y(e,t,n){let r=new Map;for(let[e,n]of Object.entries(t))r.set(e,n);return S(new _(r,new Map,e,n?.name))}const b=new d;function x(e){b.validateConfig(e);let t=new Map;for(let[n,r]of Object.entries(e))t.set(n,r);return S(new _(t))}function S(e){let n=new v(e),r={scope:(t,n)=>y(e,t,n),extend:t=>{b.validateConfig(t);let n=new Map(e.getFactories());for(let[e,r]of Object.entries(t))n.set(e,r);return S(new _(n,new Map(e.getCache())))},preload:async(...t)=>{let n=t.length>0?t:[...e.getFactories().keys()];for(let t of n)e.resolve(t)},reset:(...t)=>{let n=e.getCache();for(let e of t)n.delete(e)},inspect:()=>n.inspect(),describe:e=>n.describe(e),health:()=>n.health(),toString:()=>n.toString(),dispose:async()=>{let n=e.getCache(),r=[...n.entries()].reverse();for(let[,e]of r)t(e)&&await e.onDestroy();n.clear()}};return new Proxy({},{get(t,i){if(typeof i==`symbol`)return i===Symbol.toPrimitive||i===Symbol.toStringTag?()=>n.toString():void 0;let a=i;return a in r?r[a]:e.resolve(a)},has(t,n){if(typeof n==`symbol`)return!1;let i=n;return i in r||e.getFactories().has(i)||e.getAllRegisteredKeys().includes(i)},ownKeys(){return[...e.getAllRegisteredKeys(),...Object.keys(r)]},getOwnPropertyDescriptor(t,n){if(typeof n==`symbol`)return;let i=n;if(i in r||e.getFactories().has(i)||e.getAllRegisteredKeys().includes(i))return{configurable:!0,enumerable:!(i in r),writable:!1}}})}export{s as CircularDependencyError,i as ContainerConfigError,r as ContainerError,l as FactoryError,o as ProviderNotFoundError,a as ReservedKeyError,u as ScopeMismatchWarning,c as UndefinedReturnError,x as createContainer,f as detectDuplicateKeys,h as transient};
|
|
4
|
+
//# sourceMappingURL=index.mjs.map
|
package/package.json
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "inwire",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.3",
|
|
4
4
|
"description": "Zero-ceremony dependency injection for TypeScript. Full inference, no decorators, no tokens. Built-in introspection for AI tooling.",
|
|
5
5
|
"type": "module",
|
|
6
|
-
"main": "./dist/index.
|
|
7
|
-
"types": "./dist/index.d.
|
|
6
|
+
"main": "./dist/index.mjs",
|
|
7
|
+
"types": "./dist/index.d.mts",
|
|
8
8
|
"exports": {
|
|
9
9
|
".": {
|
|
10
|
-
"types": "./dist/index.d.
|
|
11
|
-
"default": "./dist/index.
|
|
10
|
+
"types": "./dist/index.d.mts",
|
|
11
|
+
"default": "./dist/index.mjs"
|
|
12
12
|
}
|
|
13
13
|
},
|
|
14
14
|
"files": [
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
"!dist/*.map"
|
|
17
17
|
],
|
|
18
18
|
"scripts": {
|
|
19
|
-
"build": "
|
|
19
|
+
"build": "tsdown",
|
|
20
20
|
"test": "vitest run",
|
|
21
21
|
"test:watch": "vitest",
|
|
22
22
|
"test:coverage": "vitest run --coverage",
|
|
@@ -48,8 +48,9 @@
|
|
|
48
48
|
"@semantic-release/changelog": "^6.0.0",
|
|
49
49
|
"@semantic-release/git": "^10.0.0",
|
|
50
50
|
"@vitest/coverage-v8": "^3.2.4",
|
|
51
|
+
"publint": "^0.3.17",
|
|
51
52
|
"semantic-release": "^24.0.0",
|
|
52
|
-
"
|
|
53
|
+
"tsdown": "^0.20.3",
|
|
53
54
|
"typescript": "^5.4.0",
|
|
54
55
|
"vitest": "^3.0.0"
|
|
55
56
|
}
|
package/dist/index.js
DELETED
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
function b(s){return s!==null&&typeof s=="object"&&"onInit"in s&&typeof s.onInit=="function"}function S(s){return s!==null&&typeof s=="object"&&"onDestroy"in s&&typeof s.onDestroy=="function"}var D=["scope","extend","preload","reset","inspect","describe","health","dispose","toString"];var d=class extends Error{constructor(e){super(e),this.name=this.constructor.name;}},m=class extends d{hint;details;constructor(e,t){super(`'${e}' must be a factory function, got ${t}.`),this.hint=`Wrap it: ${e}: () => ${JSON.stringify(e===e?`<your ${t} value>`:e)}`,this.details={key:e,actualType:t};}},v=class extends d{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]};}},g=class extends d{hint;details;constructor(e,t,o,r){let n=t.length>0?`
|
|
2
|
-
|
|
3
|
-
Resolution chain: ${[...t,`${e} (not found)`].join(" -> ")}`:"",i=`
|
|
4
|
-
Registered keys: [${o.join(", ")}]`,a=r?`
|
|
5
|
-
|
|
6
|
-
Did you mean '${r}'?`:"";super(`Cannot resolve '${t[0]??e}': dependency '${e}' not found.${n}${i}${a}`),this.hint=r?`Did you mean '${r}'? Or add '${e}' to your container:
|
|
7
|
-
createContainer({
|
|
8
|
-
...existing,
|
|
9
|
-
${e}: (c) => new Your${e[0].toUpperCase()}${e.slice(1)}(/* deps */),
|
|
10
|
-
});`:`Add '${e}' to your container:
|
|
11
|
-
createContainer({
|
|
12
|
-
...existing,
|
|
13
|
-
${e}: (c) => new Your${e[0].toUpperCase()}${e.slice(1)}(/* deps */),
|
|
14
|
-
});`,this.details={key:e,chain:t,registered:o,suggestion:r};}},f=class extends d{hint;details;constructor(e,t){let o=[...t,e].join(" -> ");super(`Circular dependency detected while resolving '${t[0]}'.
|
|
15
|
-
|
|
16
|
-
Cycle: ${o}`),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(`
|
|
17
|
-
`),this.details={key:e,chain:t,cycle:o};}},u=class extends d{hint;details;constructor(e,t){let o=t.length>1?`
|
|
18
|
-
|
|
19
|
-
Resolution chain: ${t.join(" -> ")}`:"";super(`Factory '${e}' returned undefined.${o}`),this.hint="Your factory function returned undefined. Did you forget a return statement?",this.details={key:e,chain:t};}},h=class extends d{hint;details;originalError;constructor(e,t,o){let r=o instanceof Error?o.message:String(o),n=t.length>1?`
|
|
20
|
-
|
|
21
|
-
Resolution chain: ${[...t.slice(0,-1),`${e} (factory threw)`].join(" -> ")}`:"";super(`Factory '${e}' threw an error: "${r}"${n}`),this.hint=`Check the factory function for '${e}'. The error occurred during instantiation.`,this.details={key:e,chain:t,originalError:r},this.originalError=o;}},x=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(`
|
|
22
|
-
`),this.details={singleton:e,transient:t};}};var y=class{validateConfig(e){for(let[t,o]of Object.entries(e)){if(D.includes(t))throw new v(t,D);if(typeof o!="function")throw new m(t,typeof o)}}suggestKey(e,t){let o,r=1/0;for(let a of t){let c=M(e,a);c<r&&(r=c,o=a);}if(!o)return;let n=Math.max(e.length,o.length);return 1-r/n>=.5?o:void 0}};function E(...s){let e=new Map,t=[];for(let o of s)for(let r of Object.keys(o)){let n=(e.get(r)??0)+1;e.set(r,n),n===2&&t.push(r);}return t}function M(s,e){let t=s.length,o=e.length;if(t===0)return o;if(o===0)return t;let r=new Array(o+1),n=new Array(o+1);for(let i=0;i<=o;i++)r[i]=i;for(let i=1;i<=t;i++){n[0]=i;for(let a=1;a<=o;a++){let c=s[i-1]===e[a-1]?0:1;n[a]=Math.min(r[a]+1,n[a-1]+1,r[a-1]+c);}[r,n]=[n,r];}return r[o]}var C=Symbol.for("inwire:transient");function j(s){let e=(t=>s(t));return e[C]=true,e}function p(s){return typeof s=="function"&&C in s&&s[C]===true}var l=class{factories;cache;resolving=new Set;depGraph=new Map;warnings=[];validator=new y;parent;name;constructor(e,t,o,r){this.factories=e,this.cache=t??new Map,this.parent=o,this.name=r;}getName(){return this.name}resolve(e,t=[]){let o=this.factories.get(e);if(o&&!p(o)&&this.cache.has(e))return this.cache.get(e);if(!o){if(this.parent)return this.parent.resolve(e,t);let n=this.getAllRegisteredKeys(),i=this.validator.suggestKey(e,n);throw new g(e,t,n,i)}if(this.resolving.has(e))throw new f(e,[...t]);this.resolving.add(e);let r=[...t,e];try{let n=[],i=this.createTrackingProxy(e,n,r),a=o(i);if(a===void 0)throw new u(e,r);if(this.depGraph.set(e,n),!p(o))for(let c of n){let $=this.getFactory(c);$&&p($)&&this.warnings.push(new x(e,c));}if(p(o)||this.cache.set(e,a),b(a)){let c=a.onInit();c instanceof Promise&&c.catch(()=>{});}return a}catch(n){throw n instanceof f||n instanceof g||n instanceof u||n instanceof h?n:new h(e,r,n)}finally{this.resolving.delete(e);}}isResolved(e){return this.cache.has(e)}getDepGraph(){return new Map(this.depGraph)}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]}createTrackingProxy(e,t,o){return new Proxy({},{get:(r,n)=>{if(typeof n=="symbol")return;let i=n;return t.push(i),this.resolve(i,o)}})}getFactory(e){return this.factories.get(e)??this.parent?.getFactory(e)}};var w=class{constructor(e){this.resolver=e;}inspect(){let e={};for(let[o,r]of this.resolver.getFactories())e[o]={key:o,resolved:this.resolver.isResolved(o),deps:this.resolver.getDepGraph().get(o)??[],scope:p(r)?"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:p(t)?"transient":"singleton"}:{key:e,resolved:false,deps:[],scope:"singleton"}}health(){let e=[...this.resolver.getFactories().keys()],t=this.resolver.getResolvedKeys(),o=new Set(t),r=this.resolver.getWarnings().map(n=>({type:n.type,message:n.message,details:n.details}));return {totalProviders:e.length,resolved:t,unresolved:e.filter(n=>!o.has(n)),warnings:r}}toString(){let e=[];for(let[r]of this.resolver.getFactories()){let n=this.resolver.isResolved(r),i=this.resolver.getDepGraph().get(r),a=i&&i.length>0?` -> [${i.join(", ")}]`:"",c=n?"(resolved)":"(pending)";e.push(`${r}${a} ${c}`);}let t=this.resolver.getName();return `${t?`Scope(${t})`:"Container"} { ${e.join(", ")} }`}};function T(s,e,t){let o=new Map;for(let[n,i]of Object.entries(e))o.set(n,i);let r=new l(o,new Map,s,t?.name);return R(r)}var F=new y;function I(s){F.validateConfig(s);let e=new Map;for(let[o,r]of Object.entries(s))e.set(o,r);let t=new l(e);return R(t)}function R(s){let e=new w(s),t={scope:(r,n)=>T(s,r,n),extend:r=>{F.validateConfig(r);let n=new Map(s.getFactories());for(let[a,c]of Object.entries(r))n.set(a,c);let i=new l(n,new Map(s.getCache()));return R(i)},preload:async(...r)=>{let n=r.length>0?r:[...s.getFactories().keys()];for(let i of n)s.resolve(i);},reset:(...r)=>{let n=s.getCache();for(let i of r)n.delete(i);},inspect:()=>e.inspect(),describe:r=>e.describe(r),health:()=>e.health(),toString:()=>e.toString(),dispose:async()=>{let r=s.getCache(),n=[...r.entries()].reverse();for(let[,i]of n)S(i)&&await i.onDestroy();r.clear();}};return new Proxy({},{get(r,n){if(typeof n=="symbol")return n===Symbol.toPrimitive||n===Symbol.toStringTag?()=>e.toString():void 0;let i=n;return i in t?t[i]:s.resolve(i)},has(r,n){if(typeof n=="symbol")return false;let i=n;return i in t||s.getFactories().has(i)||s.getAllRegisteredKeys().includes(i)},ownKeys(){return [...s.getAllRegisteredKeys(),...Object.keys(t)]},getOwnPropertyDescriptor(r,n){if(typeof n=="symbol")return;let i=n;if(i in t||s.getFactories().has(i)||s.getAllRegisteredKeys().includes(i))return {configurable:true,enumerable:!(i in t),writable:false}}})}export{f as CircularDependencyError,m as ContainerConfigError,d as ContainerError,h as FactoryError,g as ProviderNotFoundError,v as ReservedKeyError,x as ScopeMismatchWarning,u as UndefinedReturnError,I as createContainer,E as detectDuplicateKeys,j as transient};//# sourceMappingURL=index.js.map
|
|
23
|
-
//# sourceMappingURL=index.js.map
|