@resolid/di 0.6.0 → 0.7.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
@@ -1,17 +1,15 @@
1
- # Resolid: DI Container Package
1
+ # TypeScript Dependency Injection Container
2
2
 
3
3
  ![GitHub License](https://img.shields.io/github/license/resolid/framework)
4
4
  ![NPM Version](https://img.shields.io/npm/v/%40resolid/di)
5
5
 
6
6
  <b>[Documentation](https://www.resolid.tech/docs/di)</b> | [Framework Bundle](https://github.com/resolid/framework)
7
7
 
8
- ## TypeScript Dependency Injection Container
9
-
10
8
  A lightweight, fully-typed Dependency Injection (DI) container for TypeScript.
11
9
  Supports singleton & transient scopes, lazy resolution, optional dependencies, and disposable resources. Fully functional with async factories
12
10
  and avoids circular dependency issues.
13
11
 
14
- ### Features
12
+ ## Features
15
13
 
16
14
  - Fully typed with TypeScript, no any.
17
15
  - Supports singleton and transient scopes.
@@ -19,10 +17,9 @@ and avoids circular dependency issues.
19
17
  - Optional dependency resolution.
20
18
  - Detects circular dependencies.
21
19
  - Handles disposable instances with dispose() support.
22
- - Fully async-compatible with inject() and injectAsync() functions.
23
20
  - Context-aware injection with InjectionContext.
24
21
 
25
- ### Installation
22
+ ## Installation
26
23
 
27
24
  ```shell
28
25
  pnpm add @resolid/di
@@ -34,7 +31,9 @@ yarn add @resolid/di
34
31
  bun add @resolid/di
35
32
  ```
36
33
 
37
- ### Basic Usage
34
+ ## Usage
35
+
36
+ ### Basic
38
37
 
39
38
  ```typescript
40
39
  import { Container, inject } from "@resolid/di";
@@ -95,65 +94,13 @@ container.add({
95
94
  });
96
95
  ```
97
96
 
98
- ### Asynchronous Dependencies
99
-
100
- ```typescript
101
- import { Container, injectAsync } from "@resolid/di";
102
-
103
- class AsyncDatabaseService {
104
- async connect(): Promise<void> {
105
- await new Promise(resolve => setTimeout(resolve, 100));
106
- console.log("Database connected");
107
- }
108
- }
109
-
110
- class AsyncUserService {
111
- private db : AsyncDatabaseService
112
-
113
- constructor(db: AsyncDatabaseService) {
114
- this.db = db;
115
- }
116
-
117
- async getUser(id: number): Promise<string> {
118
- return `User ${id}`;
119
- }
120
- }
121
-
122
- const container = new Container();
123
-
124
- container.add({
125
- token: AsyncDatabaseService,
126
- factory: async () => {
127
- const db = new AsyncDatabaseService();
128
- await db.connect();
129
- return db;
130
- },
131
- async: true,
132
- });
133
-
134
- container.add({
135
- token: AsyncUserService,
136
- factory: async () => {
137
- const db = await injectAsync(AsyncDatabaseService);
138
- return new AsyncUserService(db)
139
- },
140
- async: true,
141
- });
142
-
143
- // Resolve async services
144
- const userService = await container.getAsync(AsyncUserService);
145
- const user = await userService.getUser(1);
146
-
147
- console.log(user); // Output: User 1
148
- ```
149
-
150
97
  ### Optional Dependencies
151
98
 
152
99
  ```typescript
153
100
  import { Container, inject } from "@resolid/di";
154
101
 
155
102
  class AnalyticsService {
156
- private logger?: LogService
103
+ private logger?: LogService;
157
104
 
158
105
  constructor(logger?: LogService) {
159
106
  this.logger = logger;
@@ -186,7 +133,7 @@ analytics.track("page_view"); // Output: Analytics (no logger): page_view
186
133
  import { Container, inject } from "@resolid/di";
187
134
 
188
135
  class ReportService {
189
- private getLogger : () => LogService;
136
+ private getLogger: () => LogService;
190
137
 
191
138
  constructor(private getLogger: () => LogService) {
192
139
  this.getLogger = getLogger;
@@ -255,11 +202,10 @@ const container = new Container();
255
202
 
256
203
  container.add({
257
204
  token: DatabaseConnection,
258
- factory: async () => new DatabaseConnection(),
259
- async: true,
205
+ factory: () => new DatabaseConnection(),
260
206
  });
261
207
 
262
- const db = await container.getAsync(DatabaseConnection);
208
+ const db = container.get(DatabaseConnection);
263
209
 
264
210
  // Dispose all singleton instances
265
211
  await container.dispose(); // Output: Database connection closed
@@ -0,0 +1,71 @@
1
+ //#region src/shared/index.d.ts
2
+ type Token<T = unknown> = symbol | (new (...args: any[]) => T) | {
3
+ prototype: T;
4
+ name: string;
5
+ };
6
+ interface Disposable {
7
+ dispose: () => Promise<void> | void;
8
+ }
9
+ interface Resolver {
10
+ get: (<T>(token: Token<T>) => T) & (<T>(token: Token<T>, options: {
11
+ optional: true;
12
+ }) => T | undefined) & (<T>(token: Token<T>, options: {
13
+ lazy: true;
14
+ }) => () => T) & (<T>(token: Token<T>, options: {
15
+ lazy: true;
16
+ optional: true;
17
+ }) => () => T | undefined) & (<T>(token: Token<T>, options: {
18
+ lazy?: false;
19
+ optional?: boolean;
20
+ }) => T | undefined) & (<T>(token: Token<T>, options?: {
21
+ lazy?: boolean;
22
+ optional?: boolean;
23
+ }) => T | undefined | (() => T | undefined));
24
+ }
25
+ //#endregion
26
+ //#region src/container/index.d.ts
27
+ type Scope = "singleton" | "transient";
28
+ type Provider<T = unknown> = {
29
+ token: Token<T>;
30
+ factory: () => T;
31
+ scope?: Scope;
32
+ };
33
+ declare class Container implements Resolver, Disposable {
34
+ private readonly _providers;
35
+ private readonly _singletons;
36
+ private readonly _constructing;
37
+ private _checkCircularDependency;
38
+ private _resolve;
39
+ add(provider: Provider): void;
40
+ get<T>(token: Token<T>): T;
41
+ get<T>(token: Token<T>, options: {
42
+ optional: true;
43
+ }): T | undefined;
44
+ get<T>(token: Token<T>, options: {
45
+ lazy: true;
46
+ }): () => T;
47
+ get<T>(token: Token<T>, options: {
48
+ lazy: true;
49
+ optional: true;
50
+ }): () => T | undefined;
51
+ get<T>(token: Token<T>, options: {
52
+ lazy?: false;
53
+ optional?: boolean;
54
+ }): T | undefined;
55
+ dispose(): Promise<void>;
56
+ }
57
+ //#endregion
58
+ //#region src/inject/index.d.ts
59
+ declare function inject<T>(token: Token<T>): T;
60
+ declare function inject<T>(token: Token<T>, options: {
61
+ optional: true;
62
+ }): T | undefined;
63
+ declare function inject<T>(token: Token<T>, options: {
64
+ lazy: true;
65
+ }): () => T;
66
+ declare function inject<T>(token: Token<T>, options: {
67
+ lazy: true;
68
+ optional: true;
69
+ }): () => T | undefined;
70
+ //#endregion
71
+ export { Container, Provider, Scope, type Token, inject };
package/dist/index.mjs ADDED
@@ -0,0 +1 @@
1
+ var e=class extends Error{constructor(){super(`inject() must be called within a injection context`)}},t=class t{static contextStack=[];resolver;constructor(e){this.resolver=e}static current(){let t=this.contextStack[this.contextStack.length-1];if(t===void 0)throw new e;return t}run(e){t.contextStack.push(this);try{return e(this.resolver)}finally{t.contextStack.pop()}}};function n(e){return typeof e==`symbol`?e.description??String(e):e.name}var r=class{_providers=new Map;_singletons=new Map;_constructing=[];_checkCircularDependency(e){if(this._constructing.includes(e))throw Error(`Circular dependency detected ${[...this._constructing,e].map(n).join(` -> `)}`)}_resolve(e,r){this._checkCircularDependency(e);let i=this._providers.get(e);if(i===void 0){if(!r)throw Error(`No provider found for ${n(e)}`);return}let a=i.scope!==`transient`;if(a&&this._singletons.has(e))return this._singletons.get(e);this._constructing.push(e);try{let n=new t(this).run(()=>i.factory());return a&&this._singletons.set(e,n),n}finally{this._constructing.pop()}}add(e){this._providers.set(e.token,e)}get(e,t){return t?.lazy??!1?()=>this.get(e,{...t,lazy:!1}):this._resolve(e,t?.optional??!1)}async dispose(){let e=0,t=``;for(let[r,i]of this._singletons)if(typeof i.dispose==`function`)try{await i.dispose()}catch(i){e++,t+=`${n(r)}: ${i instanceof Error?i.message:i}; `}if(this._singletons.clear(),e>0)throw Error(`Failed to dispose ${e} provider(s):\n${t.slice(0,-2)}`)}};function i(n,r){try{return t.current().run(e=>e.get(n,r))}catch(t){if(t instanceof e&&r?.optional==1)return;throw t}}export{r as Container,i as inject};
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "@resolid/di",
3
- "version": "0.6.0",
3
+ "version": "0.7.0",
4
4
  "private": false,
5
- "description": "The Resolid DI Container package.",
5
+ "description": "TypeScript Dependency Injection Container",
6
6
  "keywords": [
7
7
  "resolid",
8
8
  "container",
@@ -26,20 +26,18 @@
26
26
  "type": "module",
27
27
  "exports": {
28
28
  ".": {
29
- "types": "./dist/index.d.ts",
30
- "import": "./dist/index.js"
29
+ "types": "./dist/index.d.mts",
30
+ "import": "./dist/index.mjs"
31
31
  }
32
32
  },
33
- "main": "./dist/index.js",
34
- "types": "./dist/index.d.ts",
33
+ "main": "./dist/index.mjs",
34
+ "types": "./dist/index.d.mts",
35
35
  "files": [
36
36
  "dist"
37
37
  ],
38
38
  "devDependencies": {
39
- "@vitest/coverage-v8": "^4.0.3",
40
- "tsdown": "^0.15.10",
41
- "typescript": "^5.9.3",
42
- "vitest": "^4.0.3"
39
+ "tsdown": "^0.16.8",
40
+ "typescript": "^5.9.3"
43
41
  },
44
42
  "engines": {
45
43
  "node": "^20.19.0 || ^22.13.0 || >=24"
package/dist/index.d.ts DELETED
@@ -1,119 +0,0 @@
1
- //#region src/shared/index.d.ts
2
- type Class<T> = new (...args: any[]) => T;
3
- type Token<T = unknown> = symbol | Class<T>;
4
- interface Disposable {
5
- dispose: () => Promise<void> | void;
6
- }
7
- interface Resolver {
8
- get: (<T>(token: Token<T>) => T) & (<T>(token: Token<T>, options: {
9
- optional: true;
10
- }) => T | undefined) & (<T>(token: Token<T>, options: {
11
- lazy: true;
12
- }) => () => T) & (<T>(token: Token<T>, options: {
13
- lazy: true;
14
- optional: true;
15
- }) => () => T | undefined) & (<T>(token: Token<T>, options: {
16
- lazy?: false;
17
- optional?: boolean;
18
- }) => T | undefined) & (<T>(token: Token<T>, options?: {
19
- lazy?: boolean;
20
- optional?: boolean;
21
- }) => T | undefined | (() => T | undefined));
22
- getAsync: (<T>(token: Token<T>) => Promise<T>) & (<T>(token: Token<T>, options: {
23
- optional: true;
24
- }) => Promise<T | undefined>) & (<T>(token: Token<T>, options: {
25
- lazy: true;
26
- }) => () => Promise<T>) & (<T>(token: Token<T>, options: {
27
- lazy: true;
28
- optional: true;
29
- }) => () => Promise<T | undefined>) & (<T>(token: Token<T>, options: {
30
- lazy?: false;
31
- optional?: boolean;
32
- }) => Promise<T | undefined>) & (<T>(token: Token<T>, options?: {
33
- lazy?: boolean;
34
- optional?: boolean;
35
- }) => Promise<T | undefined> | (() => Promise<T | undefined>));
36
- }
37
- //#endregion
38
- //#region src/container/index.d.ts
39
- type Scope = "singleton" | "transient";
40
- type SyncProvider<T = unknown> = {
41
- token: Token<T>;
42
- factory: (resolver: Resolver) => T;
43
- async?: false;
44
- scope?: Scope;
45
- };
46
- type AsyncProvider<T = unknown> = {
47
- token: Token<T>;
48
- factory: (resolver: Resolver) => Promise<T>;
49
- async?: true;
50
- scope?: Scope;
51
- };
52
- type Provider<T = unknown> = SyncProvider<T> | AsyncProvider<T>;
53
- declare class Container implements Resolver, Disposable {
54
- private readonly _providers;
55
- private readonly _singletons;
56
- private readonly _constructing;
57
- private _checkCircularDependency;
58
- private _resolveProvide;
59
- private _resolve;
60
- private _resolveAsync;
61
- add(provider: Provider): void;
62
- get<T>(token: Token<T>): T;
63
- get<T>(token: Token<T>, options: {
64
- optional: true;
65
- }): T | undefined;
66
- get<T>(token: Token<T>, options: {
67
- lazy: true;
68
- }): () => T;
69
- get<T>(token: Token<T>, options: {
70
- lazy: true;
71
- optional: true;
72
- }): () => T | undefined;
73
- get<T>(token: Token<T>, options: {
74
- lazy?: false;
75
- optional?: boolean;
76
- }): T | undefined;
77
- getAsync<T>(token: Token<T>): Promise<T>;
78
- getAsync<T>(token: Token<T>, options: {
79
- optional: true;
80
- }): Promise<T | undefined>;
81
- getAsync<T>(token: Token<T>, options: {
82
- lazy: true;
83
- }): () => Promise<T>;
84
- getAsync<T>(token: Token<T>, options: {
85
- lazy: true;
86
- optional: true;
87
- }): () => Promise<T | undefined>;
88
- getAsync<T>(token: Token<T>, options: {
89
- lazy?: false;
90
- optional?: boolean;
91
- }): Promise<T | undefined>;
92
- dispose(): Promise<void>;
93
- }
94
- //#endregion
95
- //#region src/inject/index.d.ts
96
- declare function inject<T>(token: Token<T>): T;
97
- declare function inject<T>(token: Token<T>, options: {
98
- optional: true;
99
- }): T | undefined;
100
- declare function inject<T>(token: Token<T>, options: {
101
- lazy: true;
102
- }): () => T;
103
- declare function inject<T>(token: Token<T>, options: {
104
- lazy: true;
105
- optional: true;
106
- }): () => T | undefined;
107
- declare function injectAsync<T>(token: Token<T>): Promise<T>;
108
- declare function injectAsync<T>(token: Token<T>, options: {
109
- optional: true;
110
- }): Promise<T | undefined>;
111
- declare function injectAsync<T>(token: Token<T>, options: {
112
- lazy: true;
113
- }): () => Promise<T>;
114
- declare function injectAsync<T>(token: Token<T>, options: {
115
- lazy: true;
116
- optional: true;
117
- }): () => Promise<T | undefined>;
118
- //#endregion
119
- export { Container, Provider, type Resolver, Scope, type Token, inject, injectAsync };
package/dist/index.js DELETED
@@ -1 +0,0 @@
1
- var e=class extends Error{constructor(){super(`inject() / injectAsync() must be called within a injection context`)}},t=class t{static contextStack=[];resolver;constructor(e){this.resolver=e}static current(){let t=this.contextStack[this.contextStack.length-1];if(t===void 0)throw new e;return t}run(e){t.contextStack.push(this);try{return e(this.resolver)}finally{t.contextStack.pop()}}async runAsync(e){t.contextStack.push(this);try{return await e(this.resolver)}finally{t.contextStack.pop()}}};function n(e){return typeof e==`symbol`?e.description??String(e):e.name}var r=class{_providers=new Map;_singletons=new Map;_constructing=[];_checkCircularDependency(e){if(this._constructing.includes(e))throw Error(`Circular dependency detected ${[...this._constructing,e].map(n).join(` -> `)}`)}_resolveProvide(e,t){let r=this._providers.get(e);if(r===void 0&&!t)throw Error(`No provider found for ${n(e)}`);return r}_resolve(e,r){this._checkCircularDependency(e);let i=this._resolveProvide(e,r);if(i===void 0)return;if(i.async)throw Error(`Provider for ${n(e)} is async, please use injectAsync()`);let a=i.scope!==`transient`;if(a&&this._singletons.has(e))return this._singletons.get(e);this._constructing.push(e);try{let n=new t(this).run(()=>i.factory(this));return a&&this._singletons.set(e,n),n}finally{this._constructing.pop()}}async _resolveAsync(e,n){this._checkCircularDependency(e);let r=this._resolveProvide(e,n);if(r===void 0)return;let i=r.scope!==`transient`;if(i&&this._singletons.has(e))return this._singletons.get(e);this._constructing.push(e);try{let n=await new t(this).runAsync(async()=>await r.factory(this));return i&&this._singletons.set(e,n),n}finally{this._constructing.pop()}}add(e){this._providers.set(e.token,e)}get(e,t){return t?.lazy??!1?()=>this.get(e,{...t,lazy:!1}):this._resolve(e,t?.optional??!1)}getAsync(e,t){return t?.lazy??!1?()=>this.getAsync(e,{...t,lazy:!1}):this._resolveAsync(e,t?.optional??!1)}async dispose(){let e=0,t=``;for(let[r,i]of this._singletons)if(typeof i.dispose==`function`)try{await i.dispose()}catch(i){e++,t+=`${n(r)}: ${i instanceof Error?i.message:i}; `}if(this._singletons.clear(),e>0)throw Error(`Failed to dispose ${e} provider(s):\n${t.slice(0,-2)}`)}};function i(n,r){try{return t.current().run(e=>e.get(n,r))}catch(t){if(t instanceof e&&r?.optional==1)return;throw t}}function a(n,r){try{return r?.lazy?t.current().run(e=>e.getAsync(n,{...r,lazy:!0})):t.current().runAsync(e=>e.getAsync(n,{...r,lazy:!1}))}catch(t){return t instanceof e&&r?.optional===!0?r?.lazy?()=>Promise.resolve(void 0):Promise.resolve(void 0):Promise.reject(t)}}export{r as Container,i as inject,a as injectAsync};