inwire 1.0.2 → 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 CHANGED
@@ -13,7 +13,7 @@ Zero-ceremony dependency injection for TypeScript. Full inference, no decorators
13
13
  ## Install
14
14
 
15
15
  ```bash
16
- npm/pnpm i inwire
16
+ npm i inwire
17
17
  ```
18
18
 
19
19
  ## Quick Start
package/dist/index.d.mts CHANGED
@@ -7,7 +7,7 @@
7
7
  * const factory: Factory<MyService> = (c) => new MyService(c.db);
8
8
  * ```
9
9
  */
10
- type Factory<T = any> = (container: any) => T;
10
+ type Factory<T = unknown> = (container: any) => T;
11
11
  /**
12
12
  * An object of factory functions — the definition of a container.
13
13
  * Each key maps to a factory that produces the dependency.
@@ -53,11 +53,11 @@ interface ScopeOptions {
53
53
  * container.inspect(); // ContainerGraph
54
54
  * ```
55
55
  */
56
- type Container<T extends Record<string, any> = Record<string, any>> = T & IContainer<T>;
56
+ type Container<T extends Record<string, unknown> = Record<string, unknown>> = T & IContainer<T>;
57
57
  /**
58
58
  * Container methods interface. Defines the API available on every container.
59
59
  */
60
- interface IContainer<T extends Record<string, any> = Record<string, any>> {
60
+ interface IContainer<T extends Record<string, unknown> = Record<string, unknown>> {
61
61
  /**
62
62
  * Creates a child container with additional dependencies.
63
63
  * Child inherits all parent singletons and can add/override deps.
@@ -72,7 +72,7 @@ interface IContainer<T extends Record<string, any> = Record<string, any>> {
72
72
  * request.logger; // inherited from parent
73
73
  * ```
74
74
  */
75
- scope<E extends DepsDefinition>(extra: E, options?: ScopeOptions): Container<T & ResolvedDeps<E>>;
75
+ scope<E extends DepsDefinition>(extra: E, options?: ScopeOptions): Container<Omit<T, keyof ResolvedDeps<E>> & ResolvedDeps<E>>;
76
76
  /**
77
77
  * Returns a new container with additional dependencies.
78
78
  * Existing singletons are shared. The original container is not modified.
@@ -82,7 +82,7 @@ interface IContainer<T extends Record<string, any> = Record<string, any>> {
82
82
  * const appWithAuth = app.extend(authDeps);
83
83
  * ```
84
84
  */
85
- extend<E extends DepsDefinition>(extra: E): Container<T & ResolvedDeps<E>>;
85
+ extend<E extends DepsDefinition>(extra: E): Container<Omit<T, keyof ResolvedDeps<E>> & ResolvedDeps<E>>;
86
86
  /**
87
87
  * Pre-resolves dependencies (warm-up).
88
88
  * Call with specific keys to resolve only those, or without arguments to resolve all.
@@ -176,7 +176,7 @@ interface ContainerHealth {
176
176
  * A warning detected by the container's runtime analysis.
177
177
  */
178
178
  interface ContainerWarning {
179
- type: 'scope_mismatch' | 'duplicate_key';
179
+ type: 'scope_mismatch';
180
180
  message: string;
181
181
  details: Record<string, unknown>;
182
182
  }
@@ -215,7 +215,10 @@ declare abstract class ContainerError extends Error {
215
215
  */
216
216
  declare class ContainerConfigError extends ContainerError {
217
217
  readonly hint: string;
218
- readonly details: Record<string, unknown>;
218
+ readonly details: {
219
+ key: string;
220
+ actualType: string;
221
+ };
219
222
  constructor(key: string, actualType: string);
220
223
  }
221
224
  /**
@@ -230,7 +233,10 @@ declare class ContainerConfigError extends ContainerError {
230
233
  */
231
234
  declare class ReservedKeyError extends ContainerError {
232
235
  readonly hint: string;
233
- readonly details: Record<string, unknown>;
236
+ readonly details: {
237
+ key: string;
238
+ reserved: string[];
239
+ };
234
240
  constructor(key: string, reserved: readonly string[]);
235
241
  }
236
242
  /**
@@ -246,7 +252,12 @@ declare class ReservedKeyError extends ContainerError {
246
252
  */
247
253
  declare class ProviderNotFoundError extends ContainerError {
248
254
  readonly hint: string;
249
- readonly details: Record<string, unknown>;
255
+ readonly details: {
256
+ key: string;
257
+ chain: string[];
258
+ registered: string[];
259
+ suggestion: string | undefined;
260
+ };
250
261
  constructor(key: string, chain: string[], registered: string[], suggestion?: string);
251
262
  }
252
263
  /**
@@ -260,7 +271,11 @@ declare class ProviderNotFoundError extends ContainerError {
260
271
  */
261
272
  declare class CircularDependencyError extends ContainerError {
262
273
  readonly hint: string;
263
- readonly details: Record<string, unknown>;
274
+ readonly details: {
275
+ key: string;
276
+ chain: string[];
277
+ cycle: string;
278
+ };
264
279
  constructor(key: string, chain: string[]);
265
280
  }
266
281
  /**
@@ -275,7 +290,10 @@ declare class CircularDependencyError extends ContainerError {
275
290
  */
276
291
  declare class UndefinedReturnError extends ContainerError {
277
292
  readonly hint: string;
278
- readonly details: Record<string, unknown>;
293
+ readonly details: {
294
+ key: string;
295
+ chain: string[];
296
+ };
279
297
  constructor(key: string, chain: string[]);
280
298
  }
281
299
  /**
@@ -290,7 +308,11 @@ declare class UndefinedReturnError extends ContainerError {
290
308
  */
291
309
  declare class FactoryError extends ContainerError {
292
310
  readonly hint: string;
293
- readonly details: Record<string, unknown>;
311
+ readonly details: {
312
+ key: string;
313
+ chain: string[];
314
+ originalError: string;
315
+ };
294
316
  readonly originalError: unknown;
295
317
  constructor(key: string, chain: string[], originalError: unknown);
296
318
  }
@@ -307,7 +329,10 @@ declare class ScopeMismatchWarning implements ContainerWarning {
307
329
  readonly type: "scope_mismatch";
308
330
  readonly message: string;
309
331
  readonly hint: string;
310
- readonly details: Record<string, unknown>;
332
+ readonly details: {
333
+ singleton: string;
334
+ transient: string;
335
+ };
311
336
  constructor(singletonKey: string, transientKey: string);
312
337
  }
313
338
  //#endregion
package/dist/index.mjs CHANGED
@@ -1,4 +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}: () => ${JSON.stringify(e===e?`<your ${t} value>`:e)}`,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(`
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
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
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
4
  //# sourceMappingURL=index.mjs.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "inwire",
3
- "version": "1.0.2",
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
6
  "main": "./dist/index.mjs",