composed-di 0.2.9 → 0.3.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 +182 -141
- package/dist/errors.d.ts +17 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +26 -0
- package/dist/errors.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/serviceFactory.d.ts +3 -2
- package/dist/serviceFactory.d.ts.map +1 -1
- package/dist/serviceFactory.js +22 -7
- package/dist/serviceFactory.js.map +1 -1
- package/dist/serviceFactoryWrapper.d.ts +2 -0
- package/dist/serviceFactoryWrapper.d.ts.map +1 -0
- package/dist/serviceFactoryWrapper.js +16 -0
- package/dist/serviceFactoryWrapper.js.map +1 -0
- package/dist/serviceKey.d.ts +84 -0
- package/dist/serviceKey.d.ts.map +1 -1
- package/dist/serviceKey.js +83 -2
- package/dist/serviceKey.js.map +1 -1
- package/dist/serviceModule.d.ts +25 -4
- package/dist/serviceModule.d.ts.map +1 -1
- package/dist/serviceModule.js +106 -15
- package/dist/serviceModule.js.map +1 -1
- package/dist/serviceSelector.d.ts +64 -0
- package/dist/serviceSelector.d.ts.map +1 -0
- package/dist/serviceSelector.js +69 -0
- package/dist/serviceSelector.js.map +1 -0
- package/dist/test-service-selector.d.ts +2 -0
- package/dist/test-service-selector.d.ts.map +1 -0
- package/dist/test-service-selector.js +110 -0
- package/dist/test-service-selector.js.map +1 -0
- package/dist/utils.d.ts +33 -6
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +100 -6
- package/dist/utils.js.map +1 -1
- package/package.json +45 -41
- package/src/errors.ts +23 -0
- package/src/index.ts +2 -0
- package/src/serviceFactory.ts +104 -83
- package/src/serviceKey.ts +95 -8
- package/src/serviceModule.ts +223 -123
- package/src/serviceScope.ts +7 -7
- package/src/serviceSelector.ts +68 -0
- package/src/utils.ts +131 -6
- package/dist/main.d.ts +0 -2
- package/dist/main.d.ts.map +0 -1
- package/dist/main.js +0 -15
- package/dist/main.js.map +0 -1
- package/dist/plugin/index.d.ts +0 -66
- package/dist/plugin/index.d.ts.map +0 -1
- package/dist/plugin/index.js +0 -397
- package/dist/plugin/index.js.map +0 -1
- package/dist/serviceProvider.d.ts +0 -5
- package/dist/serviceProvider.d.ts.map +0 -1
- package/dist/serviceProvider.js +0 -3
- package/dist/serviceProvider.js.map +0 -1
- package/dist/test-from.d.ts +0 -2
- package/dist/test-from.d.ts.map +0 -1
- package/dist/test-from.js +0 -68
- package/dist/test-from.js.map +0 -1
package/README.md
CHANGED
|
@@ -1,141 +1,182 @@
|
|
|
1
|
-
# lazy-di
|
|
2
|
-
|
|
3
|
-
A
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
-
|
|
8
|
-
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
-
|
|
12
|
-
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
const
|
|
62
|
-
provides:
|
|
63
|
-
dependsOn: [],
|
|
64
|
-
initialize: () => {
|
|
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
|
-
|
|
1
|
+
# lazy-di
|
|
2
|
+
|
|
3
|
+
A lightweight, lazy, and typesafe dependency injection library for TypeScript.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **Lazy Initialization**: Services are only created when they are actually needed.
|
|
8
|
+
- **Type-Safe**: Full TypeScript support with typed keys and dependency resolution.
|
|
9
|
+
- **Circular Dependency Detection**: Validates your dependency graph at module creation.
|
|
10
|
+
- **Flexible Scoping**: Support for singletons, transient (one-shot) services, and custom scopes.
|
|
11
|
+
- **Runtime Selection**: Dynamically choose between multiple implementations of the same interface.
|
|
12
|
+
- **Async Support**: Native support for asynchronous service initialization.
|
|
13
|
+
- **Visualization**: Built-in support for generating Mermaid and DOT diagrams of your dependency graph.
|
|
14
|
+
|
|
15
|
+
## Installation
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
npm install lazy-di
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Quick Start
|
|
22
|
+
|
|
23
|
+
### 1. Define your Service Keys
|
|
24
|
+
|
|
25
|
+
Service keys are typed tokens that identify your services. They ensure type safety when injecting and retrieving services.
|
|
26
|
+
|
|
27
|
+
```typescript
|
|
28
|
+
import { ServiceKey } from 'lazy-di';
|
|
29
|
+
|
|
30
|
+
interface Database {
|
|
31
|
+
query: (sql: string) => Promise<any>;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export const DatabaseKey = new ServiceKey<Database>('Database');
|
|
35
|
+
|
|
36
|
+
interface UserService {
|
|
37
|
+
getUser: (id: string) => Promise<any>;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export const UserServiceKey = new ServiceKey<UserService>('UserService');
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
### 2. Create Service Factories
|
|
44
|
+
|
|
45
|
+
Factories define how services are created and what they depend on. `lazy-di` supports both singleton (created once) and one-shot (created every time) services.
|
|
46
|
+
|
|
47
|
+
```typescript
|
|
48
|
+
import { ServiceFactory } from 'lazy-di';
|
|
49
|
+
import { DatabaseKey, UserServiceKey } from './keys';
|
|
50
|
+
|
|
51
|
+
const databaseFactory = ServiceFactory.singleton({
|
|
52
|
+
provides: DatabaseKey,
|
|
53
|
+
initialize: () => {
|
|
54
|
+
console.log('Initializing Database...');
|
|
55
|
+
return {
|
|
56
|
+
query: async (sql) => ({ id: '1', name: 'John Doe' }),
|
|
57
|
+
};
|
|
58
|
+
},
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
const userServiceFactory = ServiceFactory.singleton({
|
|
62
|
+
provides: UserServiceKey,
|
|
63
|
+
dependsOn: [DatabaseKey],
|
|
64
|
+
initialize: (db) => {
|
|
65
|
+
// db is automatically typed as Database
|
|
66
|
+
return {
|
|
67
|
+
getUser: (id) => db.query(`SELECT * FROM users WHERE id = ${id}`),
|
|
68
|
+
};
|
|
69
|
+
},
|
|
70
|
+
});
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
### 3. Create a Service Module and Get Services
|
|
74
|
+
|
|
75
|
+
A `ServiceModule` aggregates factories and manages their lifecycle.
|
|
76
|
+
|
|
77
|
+
```typescript
|
|
78
|
+
import { ServiceModule } from 'lazy-di';
|
|
79
|
+
|
|
80
|
+
const module = ServiceModule.from([
|
|
81
|
+
databaseFactory,
|
|
82
|
+
userServiceFactory
|
|
83
|
+
]);
|
|
84
|
+
|
|
85
|
+
// At this point, no services have been initialized.
|
|
86
|
+
|
|
87
|
+
// This will initialize Database and then UserService lazily.
|
|
88
|
+
const userService = await module.get(UserServiceKey);
|
|
89
|
+
const user = await userService.getUser('1');
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
## Public API
|
|
93
|
+
|
|
94
|
+
### `ServiceKey<T>`
|
|
95
|
+
|
|
96
|
+
A unique identifier for a service of type `T`.
|
|
97
|
+
|
|
98
|
+
```typescript
|
|
99
|
+
const MyKey = new ServiceKey<MyInterface>('MyService');
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
### `ServiceFactory`
|
|
103
|
+
|
|
104
|
+
#### `ServiceFactory.singleton(options)`
|
|
105
|
+
Creates a factory for a service that is instantiated only once.
|
|
106
|
+
|
|
107
|
+
- `provides`: The `ServiceKey` this factory satisfies.
|
|
108
|
+
- `dependsOn`: (Optional) An array of `ServiceKey`s this service depends on.
|
|
109
|
+
- `initialize`: A function that creates the service instance. It receives the resolved dependencies as arguments. Can return a Promise.
|
|
110
|
+
- `dispose`: (Optional) A function called when the service is disposed.
|
|
111
|
+
- `scope`: (Optional) A `ServiceScope` for grouping services.
|
|
112
|
+
|
|
113
|
+
#### `ServiceFactory.oneShot(options)`
|
|
114
|
+
Creates a factory for a service that is instantiated every time it is requested.
|
|
115
|
+
|
|
116
|
+
### `ServiceModule`
|
|
117
|
+
|
|
118
|
+
#### `ServiceModule.from(entries)`
|
|
119
|
+
Creates a module from an array of factories or other `ServiceModule` instances. It automatically detects circular dependencies and missing dependencies.
|
|
120
|
+
|
|
121
|
+
#### `module.get(key)`
|
|
122
|
+
Retrieves a service instance. Returns a `Promise<T>`.
|
|
123
|
+
|
|
124
|
+
#### `module.dispose(scope?)`
|
|
125
|
+
Disposes of services. If a `scope` is provided, only services in that scope are disposed.
|
|
126
|
+
|
|
127
|
+
### `ServiceSelectorKey<T>` and `ServiceSelector<T>`
|
|
128
|
+
|
|
129
|
+
Useful for choosing between multiple implementations of the same interface at runtime.
|
|
130
|
+
|
|
131
|
+
```typescript
|
|
132
|
+
const LoggerSelectorKey = new ServiceSelectorKey<Logger>([
|
|
133
|
+
ConsoleLoggerKey,
|
|
134
|
+
FileLoggerKey,
|
|
135
|
+
]);
|
|
136
|
+
|
|
137
|
+
const AppFactory = ServiceFactory.singleton({
|
|
138
|
+
provides: AppKey,
|
|
139
|
+
dependsOn: [LoggerSelectorKey],
|
|
140
|
+
initialize: (loggerSelector) => {
|
|
141
|
+
return {
|
|
142
|
+
run: async (useFile: boolean) => {
|
|
143
|
+
const logger = await loggerSelector.get(
|
|
144
|
+
useFile ? FileLoggerKey : ConsoleLoggerKey
|
|
145
|
+
);
|
|
146
|
+
logger.log('Running...');
|
|
147
|
+
}
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
});
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
### `ServiceScope`
|
|
154
|
+
|
|
155
|
+
Used to group services for collective disposal.
|
|
156
|
+
|
|
157
|
+
```typescript
|
|
158
|
+
const MyScope = new ServiceScope('MyScope');
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
## Visualization
|
|
162
|
+
|
|
163
|
+
Visualize your dependency graph using Mermaid or DOT format.
|
|
164
|
+
|
|
165
|
+
```typescript
|
|
166
|
+
import { printMermaidGraph, printDotGraph } from 'lazy-di';
|
|
167
|
+
|
|
168
|
+
// Outputs a Mermaid diagram string
|
|
169
|
+
printMermaidGraph(module);
|
|
170
|
+
|
|
171
|
+
// Outputs a DOT diagram string
|
|
172
|
+
printDotGraph(module);
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
## Error Handling
|
|
176
|
+
|
|
177
|
+
- `ServiceModuleInitError`: Thrown during `ServiceModule.from()` if the dependency graph is invalid (circular or missing dependencies).
|
|
178
|
+
- `ServiceFactoryNotFoundError`: Thrown by `module.get()` if a requested key is not registered.
|
|
179
|
+
|
|
180
|
+
## License
|
|
181
|
+
|
|
182
|
+
MIT
|
package/dist/errors.d.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Error thrown when there is an issue during the initialization or configuration of a ServiceModule.
|
|
3
|
+
* This can include circular dependencies or missing dependencies that are detected during module creation.
|
|
4
|
+
*/
|
|
5
|
+
export declare class ServiceModuleInitError extends Error {
|
|
6
|
+
name: string;
|
|
7
|
+
constructor(message: string);
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Error thrown when a requested service cannot be found within the ServiceModule.
|
|
11
|
+
* This typically occurs when no factory has been registered for the given ServiceKey.
|
|
12
|
+
*/
|
|
13
|
+
export declare class ServiceFactoryNotFoundError extends Error {
|
|
14
|
+
name: string;
|
|
15
|
+
constructor(message: string);
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=errors.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,qBAAa,sBAAuB,SAAQ,KAAK;IAC/C,IAAI,SAA4B;gBAEpB,OAAO,EAAE,MAAM;CAG5B;AAED;;;GAGG;AACH,qBAAa,2BAA4B,SAAQ,KAAK;IACpD,IAAI,SAAiC;gBAEzB,OAAO,EAAE,MAAM;CAG5B"}
|
package/dist/errors.js
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ServiceFactoryNotFoundError = exports.ServiceModuleInitError = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Error thrown when there is an issue during the initialization or configuration of a ServiceModule.
|
|
6
|
+
* This can include circular dependencies or missing dependencies that are detected during module creation.
|
|
7
|
+
*/
|
|
8
|
+
class ServiceModuleInitError extends Error {
|
|
9
|
+
constructor(message) {
|
|
10
|
+
super(message);
|
|
11
|
+
this.name = 'ServiceModuleInitError';
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
exports.ServiceModuleInitError = ServiceModuleInitError;
|
|
15
|
+
/**
|
|
16
|
+
* Error thrown when a requested service cannot be found within the ServiceModule.
|
|
17
|
+
* This typically occurs when no factory has been registered for the given ServiceKey.
|
|
18
|
+
*/
|
|
19
|
+
class ServiceFactoryNotFoundError extends Error {
|
|
20
|
+
constructor(message) {
|
|
21
|
+
super(message);
|
|
22
|
+
this.name = 'ServiceFactoryNotFoundError';
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
exports.ServiceFactoryNotFoundError = ServiceFactoryNotFoundError;
|
|
26
|
+
//# sourceMappingURL=errors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":";;;AAAA;;;GAGG;AACH,MAAa,sBAAuB,SAAQ,KAAK;IAG/C,YAAY,OAAe;QACzB,KAAK,CAAC,OAAO,CAAC,CAAC;QAHjB,SAAI,GAAG,wBAAwB,CAAC;IAIhC,CAAC;CACF;AAND,wDAMC;AAED;;;GAGG;AACH,MAAa,2BAA4B,SAAQ,KAAK;IAGpD,YAAY,OAAe;QACzB,KAAK,CAAC,OAAO,CAAC,CAAC;QAHjB,SAAI,GAAG,6BAA6B,CAAC;IAIrC,CAAC;CACF;AAND,kEAMC"}
|
package/dist/index.d.ts
CHANGED
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,cAAc,CAAC;AAC7B,cAAc,iBAAiB,CAAC;AAChC,cAAc,kBAAkB,CAAC;AACjC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,SAAS,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,cAAc,CAAC;AAC7B,cAAc,iBAAiB,CAAC;AAChC,cAAc,kBAAkB,CAAC;AACjC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,mBAAmB,CAAC;AAClC,cAAc,UAAU,CAAC;AACzB,cAAc,SAAS,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -18,5 +18,7 @@ __exportStar(require("./serviceKey"), exports);
|
|
|
18
18
|
__exportStar(require("./serviceModule"), exports);
|
|
19
19
|
__exportStar(require("./serviceFactory"), exports);
|
|
20
20
|
__exportStar(require("./serviceScope"), exports);
|
|
21
|
+
__exportStar(require("./serviceSelector"), exports);
|
|
22
|
+
__exportStar(require("./errors"), exports);
|
|
21
23
|
__exportStar(require("./utils"), exports);
|
|
22
24
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,+CAA6B;AAC7B,kDAAgC;AAChC,mDAAiC;AACjC,iDAA+B;AAC/B,0CAAwB"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,+CAA6B;AAC7B,kDAAgC;AAChC,mDAAiC;AACjC,iDAA+B;AAC/B,oDAAkC;AAClC,2CAAyB;AACzB,0CAAwB"}
|
package/dist/serviceFactory.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { ServiceKey } from './serviceKey';
|
|
1
|
+
import { ServiceKey, ServiceSelectorKey } from './serviceKey';
|
|
2
2
|
import { ServiceScope } from './serviceScope';
|
|
3
|
-
|
|
3
|
+
import { ServiceSelector } from './serviceSelector';
|
|
4
|
+
type ServiceType<T> = T extends ServiceSelectorKey<infer U> ? ServiceSelector<U> : T extends ServiceKey<infer U> ? U : never;
|
|
4
5
|
type DependencyTypes<T extends readonly ServiceKey<unknown>[]> = {
|
|
5
6
|
[K in keyof T]: ServiceType<T[K]>;
|
|
6
7
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"serviceFactory.d.ts","sourceRoot":"","sources":["../src/serviceFactory.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"serviceFactory.d.ts","sourceRoot":"","sources":["../src/serviceFactory.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAC9D,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAGpD,KAAK,WAAW,CAAC,CAAC,IAChB,CAAC,SAAS,kBAAkB,CAAC,MAAM,CAAC,CAAC,GACjC,eAAe,CAAC,CAAC,CAAC,GAClB,CAAC,SAAS,UAAU,CAAC,MAAM,CAAC,CAAC,GAC3B,CAAC,GACD,KAAK,CAAC;AAGd,KAAK,eAAe,CAAC,CAAC,SAAS,SAAS,UAAU,CAAC,OAAO,CAAC,EAAE,IAAI;KAC9D,CAAC,IAAI,MAAM,CAAC,GAAG,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;CAClC,CAAC;AAEF,8BAAsB,cAAc,CAClC,KAAK,CAAC,CAAC,EACP,KAAK,CAAC,CAAC,SAAS,SAAS,UAAU,CAAC,OAAO,CAAC,EAAE,GAAG,EAAE;IAEnD,QAAQ,CAAC,QAAQ,EAAE,UAAU,CAAC,CAAC,CAAC,CAAC;IACjC,QAAQ,CAAC,SAAS,EAAE,CAAC,CAAC;IACtB,QAAQ,CAAC,KAAK,CAAC,EAAE,YAAY,CAAC;IAC9B,QAAQ,CAAC,UAAU,EAAE,CAAC,GAAG,YAAY,EAAE,eAAe,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;IAC7E,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IAE9B;;;OAGG;IACH,MAAM,CAAC,SAAS,CACd,KAAK,CAAC,CAAC,EACP,KAAK,CAAC,CAAC,SAAS,SAAS,UAAU,CAAC,OAAO,CAAC,EAAE,GAAG,EAAE,EACnD,EACA,KAAK,EACL,QAAQ,EACR,SAA8B,EAC9B,UAAU,EACV,OAAkB,GACnB,EAAE;QACD,KAAK,CAAC,EAAE,YAAY,CAAC;QACrB,QAAQ,EAAE,UAAU,CAAC,CAAC,CAAC,CAAC;QACxB,SAAS,CAAC,EAAE,CAAC,CAAC;QACd,UAAU,EAAE,CAAC,GAAG,YAAY,EAAE,eAAe,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;QACpE,OAAO,CAAC,EAAE,CAAC,QAAQ,EAAE,CAAC,KAAK,IAAI,CAAC;KACjC,GAAG,cAAc,CAAC,CAAC,EAAE,CAAC,CAAC;IAsCxB;;;OAGG;IACH,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,SAAS,SAAS,UAAU,CAAC,OAAO,CAAC,EAAE,GAAG,EAAE,EAAE,EAC3E,QAAQ,EACR,SAAS,EACT,UAAU,GACX,EAAE;QACD,QAAQ,EAAE,UAAU,CAAC,CAAC,CAAC,CAAC;QACxB,SAAS,EAAE,CAAC,CAAC;QACb,UAAU,EAAE,CAAC,GAAG,YAAY,EAAE,eAAe,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;KACrE,GAAG,cAAc,CAAC,CAAC,EAAE,CAAC,CAAC;CAOzB"}
|
package/dist/serviceFactory.js
CHANGED
|
@@ -16,24 +16,39 @@ class ServiceFactory {
|
|
|
16
16
|
* and used throughout the scope lifecycle.
|
|
17
17
|
*/
|
|
18
18
|
static singleton({ scope, provides, dependsOn = [], initialize, dispose = () => { }, }) {
|
|
19
|
-
let
|
|
19
|
+
let promisedInstance;
|
|
20
|
+
let resolvedInstance;
|
|
20
21
|
return {
|
|
21
22
|
scope,
|
|
22
23
|
provides,
|
|
23
24
|
dependsOn,
|
|
24
25
|
initialize(...dependencies) {
|
|
25
26
|
return __awaiter(this, void 0, void 0, function* () {
|
|
26
|
-
if (
|
|
27
|
-
|
|
27
|
+
if (resolvedInstance !== undefined) {
|
|
28
|
+
return resolvedInstance;
|
|
28
29
|
}
|
|
29
|
-
|
|
30
|
+
if (promisedInstance !== undefined) {
|
|
31
|
+
return promisedInstance;
|
|
32
|
+
}
|
|
33
|
+
// Store the reference to the promise so that concurrent requests can wait for it
|
|
34
|
+
promisedInstance = (() => __awaiter(this, void 0, void 0, function* () {
|
|
35
|
+
try {
|
|
36
|
+
resolvedInstance = yield initialize(...dependencies);
|
|
37
|
+
return resolvedInstance;
|
|
38
|
+
}
|
|
39
|
+
finally {
|
|
40
|
+
promisedInstance = undefined;
|
|
41
|
+
}
|
|
42
|
+
}))();
|
|
43
|
+
return promisedInstance;
|
|
30
44
|
});
|
|
31
45
|
},
|
|
32
46
|
dispose() {
|
|
33
|
-
if (
|
|
34
|
-
dispose(
|
|
35
|
-
|
|
47
|
+
if (resolvedInstance !== undefined) {
|
|
48
|
+
dispose(resolvedInstance);
|
|
49
|
+
resolvedInstance = undefined;
|
|
36
50
|
}
|
|
51
|
+
promisedInstance = undefined;
|
|
37
52
|
},
|
|
38
53
|
};
|
|
39
54
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"serviceFactory.js","sourceRoot":"","sources":["../src/serviceFactory.ts"],"names":[],"mappings":";;;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"serviceFactory.js","sourceRoot":"","sources":["../src/serviceFactory.ts"],"names":[],"mappings":";;;;;;;;;;;;AAiBA,MAAsB,cAAc;IAUlC;;;OAGG;IACH,MAAM,CAAC,SAAS,CAGd,EACA,KAAK,EACL,QAAQ,EACR,SAAS,GAAG,EAAkB,EAC9B,UAAU,EACV,OAAO,GAAG,GAAG,EAAE,GAAE,CAAC,GAOnB;QACC,IAAI,gBAAwC,CAAC;QAC7C,IAAI,gBAA+B,CAAC;QAEpC,OAAO;YACL,KAAK;YACL,QAAQ;YACR,SAAS;YACH,UAAU,CAAC,GAAG,YAAgC;;oBAClD,IAAI,gBAAgB,KAAK,SAAS,EAAE,CAAC;wBACnC,OAAO,gBAAgB,CAAC;oBAC1B,CAAC;oBAED,IAAI,gBAAgB,KAAK,SAAS,EAAE,CAAC;wBACnC,OAAO,gBAAgB,CAAC;oBAC1B,CAAC;oBAED,iFAAiF;oBACjF,gBAAgB,GAAG,CAAC,GAAS,EAAE;wBAC7B,IAAI,CAAC;4BACH,gBAAgB,GAAG,MAAM,UAAU,CAAC,GAAG,YAAY,CAAC,CAAC;4BACrD,OAAO,gBAAgB,CAAC;wBAC1B,CAAC;gCAAS,CAAC;4BACT,gBAAgB,GAAG,SAAS,CAAC;wBAC/B,CAAC;oBACH,CAAC,CAAA,CAAC,EAAE,CAAC;oBACL,OAAO,gBAAgB,CAAC;gBAC1B,CAAC;aAAA;YACD,OAAO;gBACL,IAAI,gBAAgB,KAAK,SAAS,EAAE,CAAC;oBACnC,OAAO,CAAC,gBAAgB,CAAC,CAAC;oBAC1B,gBAAgB,GAAG,SAAS,CAAC;gBAC/B,CAAC;gBACD,gBAAgB,GAAG,SAAS,CAAC;YAC/B,CAAC;SACF,CAAC;IACJ,CAAC;IAED;;;OAGG;IACH,MAAM,CAAC,OAAO,CAA+D,EAC3E,QAAQ,EACR,SAAS,EACT,UAAU,GAKX;QACC,OAAO;YACL,QAAQ;YACR,SAAS;YACT,UAAU;SACX,CAAC;IACJ,CAAC;CACF;AAtFD,wCAsFC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"serviceFactoryWrapper.d.ts","sourceRoot":"","sources":["../src/serviceFactoryWrapper.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const serviceKey_1 = require("./serviceKey");
|
|
4
|
+
class ServiceResolver {
|
|
5
|
+
constructor(serviceModule) {
|
|
6
|
+
this.serviceModule = serviceModule;
|
|
7
|
+
}
|
|
8
|
+
get(key) {
|
|
9
|
+
// TODO: Implement service factory wrapper logic
|
|
10
|
+
return this.serviceModule.get(key);
|
|
11
|
+
}
|
|
12
|
+
static from(keys) {
|
|
13
|
+
return new serviceKey_1.ServiceKey(`ServiceResolver[${keys}]`);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
//# sourceMappingURL=serviceFactoryWrapper.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"serviceFactoryWrapper.js","sourceRoot":"","sources":["../src/serviceFactoryWrapper.ts"],"names":[],"mappings":";;AAAA,6CAA0C;AAG1C,MAAM,eAAe;IACnB,YAAqB,aAA4B;QAA5B,kBAAa,GAAb,aAAa,CAAe;IAAG,CAAC;IAErD,GAAG,CAAC,GAAkB;QACpB,gDAAgD;QAChD,OAAO,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IACrC,CAAC;IAED,MAAM,CAAC,IAAI,CAAI,IAAqB;QAClC,OAAO,IAAI,uBAAU,CAAC,mBAAmB,IAAI,GAAG,CAAC,CAAA;IACnD,CAAC;CACF"}
|
package/dist/serviceKey.d.ts
CHANGED
|
@@ -1,6 +1,90 @@
|
|
|
1
|
+
import { ServiceSelector } from './serviceSelector';
|
|
2
|
+
/**
|
|
3
|
+
* A typed token used to identify and retrieve a service from a ServiceModule.
|
|
4
|
+
*
|
|
5
|
+
* ServiceKey acts as a unique identifier for a service type, allowing type-safe
|
|
6
|
+
* dependency injection. Each key has a unique symbol to ensure identity comparison
|
|
7
|
+
* works correctly even if two keys have the same name.
|
|
8
|
+
*
|
|
9
|
+
* @template T The type of service this key identifies.
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* ```ts
|
|
13
|
+
* interface Logger {
|
|
14
|
+
* log: (msg: string) => void;
|
|
15
|
+
* }
|
|
16
|
+
*
|
|
17
|
+
* const LoggerKey = new ServiceKey<Logger>('Logger');
|
|
18
|
+
*
|
|
19
|
+
* // Use with ServiceFactory and ServiceModule
|
|
20
|
+
* const loggerFactory = ServiceFactory.singleton({
|
|
21
|
+
* provides: LoggerKey,
|
|
22
|
+
* dependsOn: [],
|
|
23
|
+
* initialize: () => console,
|
|
24
|
+
* });
|
|
25
|
+
*
|
|
26
|
+
* const module = ServiceModule.from([loggerFactory]);
|
|
27
|
+
* const logger = await module.get(LoggerKey);
|
|
28
|
+
* ```
|
|
29
|
+
*/
|
|
1
30
|
export declare class ServiceKey<T> {
|
|
2
31
|
readonly name: string;
|
|
32
|
+
/**
|
|
33
|
+
* A unique symbol that identifies this service key.
|
|
34
|
+
* Used internally for identity comparison between keys.
|
|
35
|
+
*/
|
|
3
36
|
readonly symbol: symbol;
|
|
37
|
+
/**
|
|
38
|
+
* Creates a new ServiceKey with the given name.
|
|
39
|
+
*
|
|
40
|
+
* @param name A human-readable name for the service, used in error messages and debugging.
|
|
41
|
+
*/
|
|
4
42
|
constructor(name: string);
|
|
5
43
|
}
|
|
44
|
+
/**
|
|
45
|
+
* A specialized ServiceKey that groups multiple ServiceKeys of the same type,
|
|
46
|
+
* allowing a service to depend on a selector that can retrieve any of the grouped services.
|
|
47
|
+
*
|
|
48
|
+
* When used in a factory's `dependsOn` array, the factory's `initialize` callback
|
|
49
|
+
* receives a `ServiceSelector<T>` instance instead of a direct service instance.
|
|
50
|
+
* This enables runtime selection between multiple implementations of the same interface.
|
|
51
|
+
*
|
|
52
|
+
* @template T The common type shared by all service keys in this selector.
|
|
53
|
+
*
|
|
54
|
+
* @example
|
|
55
|
+
* ```ts
|
|
56
|
+
* interface Logger {
|
|
57
|
+
* log: (msg: string) => void;
|
|
58
|
+
* }
|
|
59
|
+
*
|
|
60
|
+
* const ConsoleLoggerKey = new ServiceKey<Logger>('ConsoleLogger');
|
|
61
|
+
* const FileLoggerKey = new ServiceKey<Logger>('FileLogger');
|
|
62
|
+
*
|
|
63
|
+
* // Group multiple logger implementations under one selector
|
|
64
|
+
* const LoggerSelectorKey = new ServiceSelectorKey<Logger>([
|
|
65
|
+
* ConsoleLoggerKey,
|
|
66
|
+
* FileLoggerKey,
|
|
67
|
+
* ]);
|
|
68
|
+
*
|
|
69
|
+
* // Use in a factory's dependsOn array
|
|
70
|
+
* const appFactory = ServiceFactory.singleton({
|
|
71
|
+
* provides: AppKey,
|
|
72
|
+
* dependsOn: [LoggerSelectorKey] as const,
|
|
73
|
+
* initialize: (loggerSelector: ServiceSelector<Logger>) => {
|
|
74
|
+
* // loggerSelector.get(ConsoleLoggerKey) or loggerSelector.get(FileLoggerKey)
|
|
75
|
+
* return new App(loggerSelector);
|
|
76
|
+
* },
|
|
77
|
+
* });
|
|
78
|
+
* ```
|
|
79
|
+
*/
|
|
80
|
+
export declare class ServiceSelectorKey<T> extends ServiceKey<ServiceSelector<T>> {
|
|
81
|
+
readonly values: ServiceKey<T>[];
|
|
82
|
+
/**
|
|
83
|
+
* Creates a new ServiceSelectorKey that groups the provided service keys.
|
|
84
|
+
*
|
|
85
|
+
* @param values An array of ServiceKeys that this selector can provide access to.
|
|
86
|
+
* All keys must be registered in the ServiceModule for dependency validation to pass.
|
|
87
|
+
*/
|
|
88
|
+
constructor(values: ServiceKey<T>[]);
|
|
89
|
+
}
|
|
6
90
|
//# sourceMappingURL=serviceKey.d.ts.map
|
package/dist/serviceKey.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"serviceKey.d.ts","sourceRoot":"","sources":["../src/serviceKey.ts"],"names":[],"mappings":"AACA,qBAAa,UAAU,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"serviceKey.d.ts","sourceRoot":"","sources":["../src/serviceKey.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAEpD;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,qBAAa,UAAU,CAAC,CAAC;aAYK,IAAI,EAAE,MAAM;IAXxC;;;OAGG;IACH,SAAgB,MAAM,EAAE,MAAM,CAAC;IAE/B;;;;OAIG;gBACyB,IAAI,EAAE,MAAM;CAGzC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AACH,qBAAa,kBAAkB,CAAC,CAAC,CAAE,SAAQ,UAAU,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC;IAO3D,QAAQ,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC,CAAC,EAAE;IAN5C;;;;;OAKG;gBACkB,MAAM,EAAE,UAAU,CAAC,CAAC,CAAC,EAAE;CAG7C"}
|