inwire 2.1.6 → 2.2.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/README.md CHANGED
@@ -218,46 +218,84 @@ extended.db; // new dependency
218
218
 
219
219
  ### Modules
220
220
 
221
- A module is a function `(builder) => builder` that chains `.add()` calls. `c` is fully typed in every factory.
221
+ Split a large container into reusable modules. Each module declares its prerequisites locally — no shared `AppDeps` interface, no manual generics.
222
222
 
223
- #### Pre-build: `addModule()` on the builder
223
+ #### `defineModule()` recommended pattern
224
+
225
+ `defineModule<Prerequisites>()(builder => builder.add(...))` infers the module's output from the chained `.add()` calls. Prerequisites are explicit and local to the module file.
224
226
 
225
227
  ```typescript
226
- import { container, ContainerBuilder } from 'inwire';
228
+ import { container, defineModule } from 'inwire';
227
229
 
228
- function dbModule<T extends { config: { dbUrl: string }; logger: Logger }>(
229
- b: ContainerBuilder<Record<string, unknown>, T>,
230
- ) {
231
- return b
232
- .add('db', (c) => new Database(c.config.dbUrl))
233
- .add('cache', (c) => new Redis(c.config.dbUrl));
234
- }
230
+ interface Logger { log: (msg: string) => void }
231
+
232
+ const dbModule = defineModule<{ logger: Logger }>()((b) =>
233
+ b
234
+ .add('db', (c) => new Database(c.logger))
235
+ .add('cache', (c) => new Redis(c.logger)),
236
+ );
237
+
238
+ const userModule = defineModule<{ db: Database; logger: Logger }>()((b) =>
239
+ b.add('userService', (c) => new UserService(c.db, c.logger)),
240
+ );
235
241
 
236
242
  const app = container()
237
- .add('config', { dbUrl: 'postgres://...', port: 3000 })
238
- .add('logger', () => new Logger())
243
+ .add('logger', (): Logger => new ConsoleLogger())
239
244
  .addModule(dbModule)
245
+ .addModule(userModule)
240
246
  .build();
241
247
  ```
242
248
 
243
- #### Post-build: `module()` on the container
249
+ Why this works:
250
+ - Each module declares only what it **needs** — no import of a global `AppDeps` interface.
251
+ - The output type is inferred from the `.add()` chain — no duplicated signatures.
252
+ - Order of `addModule()` is enforced at compile time: applying `userModule` before `dbModule` would fail the prerequisite check.
244
253
 
245
- Compose modules after `.build()` — same DX, applied to an existing container:
254
+ #### `.merge()` — fuse standalone builders
255
+
256
+ When a module has no prerequisites (or just bundles independent bindings), define it as a standalone builder and merge it:
246
257
 
247
258
  ```typescript
248
- const core = container()
249
- .add('config', { dbUrl: 'postgres://...' })
259
+ const dbModule = container()
260
+ .add('db', () => new Database())
261
+ .add('cache', (c) => new Redis(c.db));
262
+
263
+ const app = container()
250
264
  .add('logger', () => new Logger())
265
+ .merge(dbModule)
266
+ .add('api', (c) => new Api(c.db, c.logger))
251
267
  .build();
268
+ ```
252
269
 
253
- const withDb = core.module((b) => b
254
- .add('db', (c) => new Database(c.config.dbUrl))
255
- .add('cache', (c) => new Redis(c.config.dbUrl))
270
+ `.merge()` copies factories into the host builder. Cross-builder dependencies are resolved at build time. Duplicate keys override (last write wins). Reserved keys throw.
271
+
272
+ #### Anti-pattern (avoid)
273
+
274
+ Older code may show this manual generic pattern — it works but is verbose, couples the module to a global `AppDeps`, and forces you to redeclare every prerequisite by hand:
275
+
276
+ ```typescript
277
+ // Don't do this anymore — use defineModule() instead.
278
+ function dbModule<T extends { logger: Logger }>(
279
+ b: ContainerBuilder<AppDeps, T>,
280
+ ) {
281
+ return b.add('db', (c) => new Database(c.logger));
282
+ }
283
+ ```
284
+
285
+ #### Post-build: `module()` on the container
286
+
287
+ Compose post-build using the same builder DX:
288
+
289
+ ```typescript
290
+ const core = container().add('logger', () => new Logger()).build();
291
+
292
+ const withDb = core.module((b) =>
293
+ b.add('db', (c) => new Database(c.logger)),
256
294
  );
257
295
 
258
- // Chainable
259
- const full = withDb.module((b) => b
260
- .add('userService', (c) => new UserService(c.db, c.logger))
296
+ // Chainable — c accumulates previous bindings
297
+ const full = withDb.module((b) =>
298
+ b.add('userService', (c) => new UserService(c.db, c.logger)),
261
299
  );
262
300
  ```
263
301
 
@@ -388,6 +426,7 @@ Compatible with [Context7](https://context7.com/) and any tool that supports the
388
426
  | Export | Description |
389
427
  |---|---|
390
428
  | `container<T?>()` | Creates a new `ContainerBuilder`. Pass interface `T` for contract mode. |
429
+ | `defineModule<Deps>()(fn)` | Creates a typed, reusable module with locally-declared prerequisites |
391
430
  | `transient(factory)` | Marks a factory as transient (for scope/extend) |
392
431
  | `detectDuplicateKeys(...modules)` | Pre-spread validation — detects duplicate keys |
393
432
 
@@ -397,7 +436,8 @@ Compatible with [Context7](https://context7.com/) and any tool that supports the
397
436
  |---|---|
398
437
  | `.add(key, factory)` | Register a dependency (factory or instance) |
399
438
  | `.addTransient(key, factory)` | Register a transient dependency |
400
- | `.addModule(module)` | Apply a module `(builder) => builder` |
439
+ | `.addModule(module)` | Apply a module `(builder) => builder` (use with `defineModule()`) |
440
+ | `.merge(otherBuilder)` | Merge a standalone builder's factories into this one |
401
441
  | `.build()` | Build and return the container |
402
442
 
403
443
  ### Container Methods
@@ -421,6 +461,9 @@ Compatible with [Context7](https://context7.com/) and any tool that supports the
421
461
  | `Container<T>` | Full container type (resolved deps + methods) |
422
462
  | `ContainerBuilder<TContract, TBuilt>` | Fluent builder class (also used in `module()` callbacks) |
423
463
  | `IContainer<T>` | Container methods interface |
464
+ | `Module<TDeps, TBuilt>` | Type of a reusable module (returned by `defineModule()`) |
465
+ | `InferModuleDeps<M>` / `InferModuleBuilt<M>` | Extract a module's prerequisites or full output type |
466
+ | `Factory<T>` | Function type for raw factories (`(c: unknown) => T`) |
424
467
  | `OnInit` | Interface with `onInit(): void \| Promise<void>` |
425
468
  | `OnDestroy` | Interface with `onDestroy(): void \| Promise<void>` |
426
469
  | `ContainerGraph` | Return type of `inspect()` |
package/dist/index.d.mts CHANGED
@@ -251,7 +251,7 @@ interface IContainer<T extends Record<string, any> = Record<string, unknown>> {
251
251
  * );
252
252
  * ```
253
253
  */
254
- module<TNew extends Record<string, any>>(fn: (builder: ContainerBuilder<Record<string, unknown>, T>) => ContainerBuilder<Record<string, unknown>, TNew>): Container<TNew>;
254
+ module<TNew extends Record<string, any>>(fn: (builder: ContainerBuilder<Record<string, unknown>, T>) => ContainerBuilder<Record<string, unknown>, T & TNew>): Container<T & TNew>;
255
255
  /**
256
256
  * Pre-resolves dependencies (warm-up).
257
257
  * Call with specific keys to resolve only those, or without arguments to resolve all.
@@ -379,9 +379,24 @@ declare class ContainerBuilder<TContract extends Record<string, any> = Record<st
379
379
  * `c` in the module's factories is fully typed with all previously registered deps.
380
380
  */
381
381
  addModule<TNew extends Record<string, any>>(module: (builder: ContainerBuilder<TContract, TBuilt>) => ContainerBuilder<TContract, TNew>): ContainerBuilder<TContract, TNew>;
382
+ /**
383
+ * Merges a standalone builder into this one. All factories of `other` are copied.
384
+ * The accumulated type becomes `TBuilt & TOther`, so subsequent factories can
385
+ * consume keys from either side.
386
+ *
387
+ * Use this to compose builders that were defined independently:
388
+ * ```typescript
389
+ * const dbModule = container().add('db', () => new DB());
390
+ * const di = container().add('logger', () => new Logger()).merge(dbModule).build();
391
+ * ```
392
+ *
393
+ * Cross-builder dependencies are resolved at build time. Reserved keys throw.
394
+ * Duplicate keys override silently — same semantics as `.add()` over an existing key.
395
+ */
396
+ merge<TOther extends Record<string, unknown>>(other: ContainerBuilder<Record<string, unknown>, TOther>): ContainerBuilder<TContract, TBuilt & TOther>;
382
397
  /**
383
398
  * Returns the accumulated factories as a plain record.
384
- * @internal Used by `module()` on the container.
399
+ * @internal Used by `module()` on the container and `merge()` on builders.
385
400
  */
386
401
  _toRecord(): Record<string, Factory>;
387
402
  /**
@@ -413,6 +428,48 @@ declare class ContainerBuilder<TContract extends Record<string, any> = Record<st
413
428
  */
414
429
  declare function container<T extends Record<string, any> = Record<string, unknown>>(): ContainerBuilder<T>;
415
430
  //#endregion
431
+ //#region src/application/define-module.d.ts
432
+ /**
433
+ * A reusable module: a function that extends a builder with new bindings.
434
+ *
435
+ * `TDeps` are the bindings the module **expects already present** on the target builder.
436
+ * `TBuilt` is the full set of bindings present after the module is applied (`TDeps` + what the module adds).
437
+ *
438
+ * Use {@link defineModule} to build a `Module` with strong inference.
439
+ */
440
+ type Module<TDeps extends Record<string, any> = {}, TBuilt extends Record<string, any> = TDeps> = (builder: ContainerBuilder<Record<string, unknown>, TDeps>) => ContainerBuilder<Record<string, unknown>, TBuilt>;
441
+ /**
442
+ * Defines a reusable, strongly-typed module without importing the host's deps interface.
443
+ *
444
+ * Pattern: `defineModule<Prerequisites>()(builder => builder.add(...))`.
445
+ * Prerequisites are declared **locally**, not pulled from a global `AppDeps`.
446
+ * The output type is **inferred** from the chained `.add()` calls — no manual signature.
447
+ *
448
+ * @example
449
+ * ```typescript
450
+ * import { defineModule } from 'inwire';
451
+ *
452
+ * const billingModule = defineModule<{ eventBus: EventBus }>()((b) =>
453
+ * b.add('subscribeUseCase', (c) => new SubscribeUseCase(c.eventBus)),
454
+ * );
455
+ *
456
+ * const di = container()
457
+ * .add('eventBus', () => new EventBus())
458
+ * .addModule(billingModule)
459
+ * .build();
460
+ * ```
461
+ */
462
+ declare function defineModule<TDeps extends Record<string, any> = {}>(): <TBuilt extends Record<string, any>>(fn: (builder: ContainerBuilder<Record<string, unknown>, TDeps>) => ContainerBuilder<Record<string, unknown>, TBuilt>) => Module<TDeps, TBuilt>;
463
+ /**
464
+ * Extracts the prerequisite deps of a `Module`.
465
+ */
466
+ type InferModuleDeps<M> = M extends Module<infer D, any> ? D : never;
467
+ /**
468
+ * Extracts the full set of bindings present after a `Module` is applied
469
+ * (prerequisites + bindings the module adds).
470
+ */
471
+ type InferModuleBuilt<M> = M extends Module<any, infer B> ? B : never;
472
+ //#endregion
416
473
  //#region src/domain/lifecycle.d.ts
417
474
  /**
418
475
  * Implement this interface (or just add an `onInit` method) to run
@@ -470,5 +527,5 @@ declare function detectDuplicateKeys(...modules: Record<string, unknown>[]): str
470
527
  */
471
528
  declare function transient<T>(factory: Factory<T>): Factory<T>;
472
529
  //#endregion
473
- export { AsyncInitErrorWarning, CircularDependencyError, type Container, ContainerBuilder, ContainerConfigError, ContainerError, type ContainerGraph, type ContainerHealth, type ContainerWarning, FactoryError, type IContainer, type OnDestroy, type OnInit, type ProviderInfo, ProviderNotFoundError, ReservedKeyError, ScopeMismatchWarning, type ScopeOptions, UndefinedReturnError, container, detectDuplicateKeys, transient };
530
+ export { AsyncInitErrorWarning, CircularDependencyError, type Container, ContainerBuilder, ContainerConfigError, ContainerError, type ContainerGraph, type ContainerHealth, type ContainerWarning, type Factory, FactoryError, type IContainer, type InferModuleBuilt, type InferModuleDeps, type Module, type OnDestroy, type OnInit, type ProviderInfo, ProviderNotFoundError, ReservedKeyError, ScopeMismatchWarning, type ScopeOptions, UndefinedReturnError, container, defineModule, detectDuplicateKeys, transient };
474
531
  //# sourceMappingURL=index.d.mts.map
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`];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};
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)}merge(e){for(let[t,n]of Object.entries(e._toRecord()))this.validateKey(t),this.factories.set(t,n);return 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}function k(){return e=>e}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,k as defineModule,h as detectDuplicateKeys,v as transient};
4
4
  //# sourceMappingURL=index.mjs.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "inwire",
3
- "version": "2.1.6",
3
+ "version": "2.2.0",
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
6
  "main": "./dist/index.mjs",
@@ -55,15 +55,15 @@
55
55
  "packageManager": "pnpm@10.26.0",
56
56
  "license": "MIT",
57
57
  "devDependencies": {
58
- "@biomejs/biome": "^2.3.14",
59
- "@semantic-release/changelog": "^6.0.0",
60
- "@semantic-release/git": "^10.0.0",
61
- "@vitest/coverage-v8": "^3.2.4",
58
+ "@biomejs/biome": "^2.4.2",
59
+ "@semantic-release/changelog": "^6.0.3",
60
+ "@semantic-release/git": "^10.0.1",
61
+ "@vitest/coverage-v8": "^4.0.18",
62
62
  "publint": "^0.3.17",
63
- "semantic-release": "^24.0.0",
63
+ "semantic-release": "^25.0.3",
64
64
  "tsdown": "^0.20.3",
65
65
  "tsx": "^4.21.0",
66
- "typescript": "^5.4.0",
67
- "vitest": "^3.0.0"
66
+ "typescript": "^5.9.3",
67
+ "vitest": "^4.0.18"
68
68
  }
69
69
  }