@js-injection/service-provider 1.0.0-alpha.1
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/LICENSE.md +15 -0
- package/README.md +72 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +7 -0
- package/dist/legacy.cjs +2 -0
- package/dist/legacy.cjs.map +7 -0
- package/dist/types/src/errors.d.ts +31 -0
- package/dist/types/src/errors.d.ts.map +1 -0
- package/dist/types/src/index.d.ts +5 -0
- package/dist/types/src/index.d.ts.map +1 -0
- package/dist/types/src/registry.d.ts +8 -0
- package/dist/types/src/registry.d.ts.map +1 -0
- package/dist/types/src/service-collection.d.ts +145 -0
- package/dist/types/src/service-collection.d.ts.map +1 -0
- package/dist/types/src/service-provider.d.ts +102 -0
- package/dist/types/src/service-provider.d.ts.map +1 -0
- package/dist/types/src/types.d.ts +168 -0
- package/dist/types/src/types.d.ts.map +1 -0
- package/docs/CHEATSHEET.md +179 -0
- package/package.json +50 -0
- package/src/errors.ts +49 -0
- package/src/index.ts +4 -0
- package/src/registry.ts +17 -0
- package/src/service-collection.ts +385 -0
- package/src/service-provider.ts +314 -0
- package/src/types.ts +225 -0
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import type { Class, IServiceProvider, MapToInstance, ServiceDefinitionMap, TypeMap } from './types.js';
|
|
2
|
+
export declare class ServiceProvider<T extends TypeMap> implements IServiceProvider<T> {
|
|
3
|
+
readonly name: string;
|
|
4
|
+
private definitions;
|
|
5
|
+
private parent?;
|
|
6
|
+
private readonly isAsyncScope;
|
|
7
|
+
private instances;
|
|
8
|
+
private resolving;
|
|
9
|
+
private path;
|
|
10
|
+
private disposed;
|
|
11
|
+
/**
|
|
12
|
+
* @param name The name of the service provider.
|
|
13
|
+
* @param definitions The service definitions map.
|
|
14
|
+
* @param parent The parent service provider, if any.
|
|
15
|
+
* @param isAsyncScope Whether this is an asynchronous scope.
|
|
16
|
+
*/
|
|
17
|
+
constructor(name: string, definitions: ServiceDefinitionMap, parent?: ServiceProvider<T> | undefined, isAsyncScope?: boolean);
|
|
18
|
+
/**
|
|
19
|
+
* Creates a new scoped ServiceProvider.
|
|
20
|
+
* @param name The name of the scope.
|
|
21
|
+
* @example
|
|
22
|
+
* const scope = di.createScope('request-scope');
|
|
23
|
+
*/
|
|
24
|
+
createScope(name?: string): IServiceProvider<T>;
|
|
25
|
+
/**
|
|
26
|
+
* Creates a new scoped ServiceProvider that supports asynchronous disposal.
|
|
27
|
+
* @param name The name of the scope.
|
|
28
|
+
* @example
|
|
29
|
+
* const scope = di.createAsyncScope('worker-scope');
|
|
30
|
+
*/
|
|
31
|
+
createAsyncScope(name?: string): IServiceProvider<T>;
|
|
32
|
+
/**
|
|
33
|
+
* Resolves a service by its key.
|
|
34
|
+
* @param key The key to resolve.
|
|
35
|
+
* @example
|
|
36
|
+
* const logger = di.getService('Logger');
|
|
37
|
+
*/
|
|
38
|
+
getService<K extends keyof T & (string | symbol)>(key: K): MapToInstance<T[K]>;
|
|
39
|
+
/**
|
|
40
|
+
* Resolves a service by its Class type.
|
|
41
|
+
* @param type The class type to resolve.
|
|
42
|
+
* @example
|
|
43
|
+
* const logger = di.getService(Logger);
|
|
44
|
+
*/
|
|
45
|
+
getService<C extends Class>(type: C): InstanceType<C>;
|
|
46
|
+
/**
|
|
47
|
+
* Synchronously disposes of all resolved services.
|
|
48
|
+
* Throws AsyncDisposalError if this is an async scope or if any service disposal returns a Promise.
|
|
49
|
+
*/
|
|
50
|
+
dispose(): void;
|
|
51
|
+
/**
|
|
52
|
+
* Asynchronously disposes of all resolved services.
|
|
53
|
+
*/
|
|
54
|
+
disposeAsync(): Promise<void>;
|
|
55
|
+
/**
|
|
56
|
+
* Resolves a singleton value.
|
|
57
|
+
* @param uid The unique ID of the value.
|
|
58
|
+
* @param value The value to resolve.
|
|
59
|
+
*/
|
|
60
|
+
private resolveValue;
|
|
61
|
+
/**
|
|
62
|
+
* Resolves an array of services.
|
|
63
|
+
* @param uid The unique ID of the service array.
|
|
64
|
+
* @param types The class types to resolve for the array.
|
|
65
|
+
*/
|
|
66
|
+
private resolveArray;
|
|
67
|
+
/**
|
|
68
|
+
* Resolves a service via a factory.
|
|
69
|
+
* @param uid The unique ID of the service.
|
|
70
|
+
* @param factory The factory to use for resolution.
|
|
71
|
+
*/
|
|
72
|
+
private resolveFactory;
|
|
73
|
+
/**
|
|
74
|
+
* Resolves a service via a function.
|
|
75
|
+
* @param uid The unique ID of the service.
|
|
76
|
+
* @param func The function to use for resolution.
|
|
77
|
+
* @param params The arguments for the function.
|
|
78
|
+
*/
|
|
79
|
+
private resolveFunc;
|
|
80
|
+
/**
|
|
81
|
+
* Resolves a service via its class type.
|
|
82
|
+
* @param uid The unique ID of the service.
|
|
83
|
+
* @param type The class type to instantiate.
|
|
84
|
+
* @param params The constructor arguments.
|
|
85
|
+
*/
|
|
86
|
+
private resolveClass;
|
|
87
|
+
/**
|
|
88
|
+
* Creates a proxy for the type map to allow resolving services by property access.
|
|
89
|
+
*/
|
|
90
|
+
private createContainerProxy;
|
|
91
|
+
/**
|
|
92
|
+
* Gets the key for a given key or class type.
|
|
93
|
+
* @param keyOrType The key or class type.
|
|
94
|
+
*/
|
|
95
|
+
private getKey;
|
|
96
|
+
/**
|
|
97
|
+
* Attempts to get the key for a given key or class type without throwing.
|
|
98
|
+
* @param keyOrType The key or class type.
|
|
99
|
+
*/
|
|
100
|
+
private tryGetKey;
|
|
101
|
+
}
|
|
102
|
+
//# sourceMappingURL=service-provider.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"service-provider.d.ts","sourceRoot":"","sources":["../../../src/service-provider.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EACV,KAAK,EAGL,gBAAgB,EAChB,aAAa,EAEb,oBAAoB,EACpB,OAAO,EACR,MAAM,YAAY,CAAC;AAEpB,qBAAa,eAAe,CAAC,CAAC,SAAS,OAAO,CAAE,YAAW,gBAAgB,CAAC,CAAC,CAAC;aAa1D,IAAI,EAAE,MAAM;IAC5B,OAAO,CAAC,WAAW;IACnB,OAAO,CAAC,MAAM,CAAC;IACf,OAAO,CAAC,QAAQ,CAAC,YAAY;IAf/B,OAAO,CAAC,SAAS,CAAmC;IACpD,OAAO,CAAC,SAAS,CAA8B;IAC/C,OAAO,CAAC,IAAI,CAA2B;IACvC,OAAO,CAAC,QAAQ,CAAS;IAEzB;;;;;OAKG;gBAEe,IAAI,EAAE,MAAM,EACpB,WAAW,EAAE,oBAAoB,EACjC,MAAM,CAAC,EAAE,eAAe,CAAC,CAAC,CAAC,YAAA,EAClB,YAAY,GAAE,OAAe;IAGhD;;;;;OAKG;IACH,WAAW,CAAC,IAAI,GAAE,MAAgB,GAAG,gBAAgB,CAAC,CAAC,CAAC;IAKxD;;;;;OAKG;IACH,gBAAgB,CAAC,IAAI,GAAE,MAAsB,GAAG,gBAAgB,CAAC,CAAC,CAAC;IAKnE;;;;;OAKG;IACH,UAAU,CAAC,CAAC,SAAS,MAAM,CAAC,GAAG,CAAC,MAAM,GAAG,MAAM,CAAC,EAAE,GAAG,EAAE,CAAC,GAAG,aAAa,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAE9E;;;;;OAKG;IACH,UAAU,CAAC,CAAC,SAAS,KAAK,EAAE,IAAI,EAAE,CAAC,GAAG,YAAY,CAAC,CAAC,CAAC;IAmDrD;;;OAGG;IACH,OAAO,IAAI,IAAI;IAyBf;;OAEG;IACG,YAAY,IAAI,OAAO,CAAC,IAAI,CAAC;IAoBnC;;;;OAIG;IACH,OAAO,CAAC,YAAY;IAKpB;;;;OAIG;IACH,OAAO,CAAC,YAAY;IAUpB;;;;OAIG;IACH,OAAO,CAAC,cAAc;IAOtB;;;;;OAKG;IACH,OAAO,CAAC,WAAW;IAanB;;;;;OAKG;IACH,OAAO,CAAC,YAAY;IAapB;;OAEG;IACH,OAAO,CAAC,oBAAoB;IAS5B;;;OAGG;IACH,OAAO,CAAC,MAAM;IAYd;;;OAGG;IACH,OAAO,CAAC,SAAS;CA8BlB"}
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
export type Class = abstract new (...args: any[]) => any;
|
|
2
|
+
export type ConcreteClass<T extends Class = Class> = new (...args: any[]) => InstanceType<T>;
|
|
3
|
+
/**
|
|
4
|
+
* Maps a value from a TypeMap to its resolved instance type.
|
|
5
|
+
*/
|
|
6
|
+
export type MapToInstance<T> = T extends abstract new (...args: any[]) => infer R ? R : (T extends (infer C)[] ? (C extends abstract new (...args: any[]) => infer R ? R[] : T) : T);
|
|
7
|
+
/**
|
|
8
|
+
* Represents an object that can be disposed synchronously.
|
|
9
|
+
*/
|
|
10
|
+
export interface IDisposable {
|
|
11
|
+
dispose(): void;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Represents an object that can be disposed asynchronously.
|
|
15
|
+
*/
|
|
16
|
+
export interface IAsyncDisposable {
|
|
17
|
+
disposeAsync(): Promise<void>;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Interface for the ServiceProvider to avoid circular dependencies in types.
|
|
21
|
+
*/
|
|
22
|
+
export interface IServiceProvider<T extends TypeMap> {
|
|
23
|
+
/**
|
|
24
|
+
* The name of the service provider.
|
|
25
|
+
*/
|
|
26
|
+
readonly name: string;
|
|
27
|
+
/**
|
|
28
|
+
* Synchronously disposes of all resolved services.
|
|
29
|
+
* Throws if any service requires asynchronous disposal.
|
|
30
|
+
*/
|
|
31
|
+
dispose(): void;
|
|
32
|
+
/**
|
|
33
|
+
* Asynchronously disposes of all resolved services.
|
|
34
|
+
*/
|
|
35
|
+
disposeAsync(): Promise<void>;
|
|
36
|
+
/**
|
|
37
|
+
* Creates a new scoped ServiceProvider.
|
|
38
|
+
*/
|
|
39
|
+
createScope(): IServiceProvider<T>;
|
|
40
|
+
/**
|
|
41
|
+
* Creates a new scoped ServiceProvider that supports asynchronous disposal.
|
|
42
|
+
*/
|
|
43
|
+
createAsyncScope(): IServiceProvider<T>;
|
|
44
|
+
/**
|
|
45
|
+
* Resolves a service by its key.
|
|
46
|
+
*/
|
|
47
|
+
getService<K extends keyof T & (string | symbol)>(key: K): MapToInstance<T[K]>;
|
|
48
|
+
/**
|
|
49
|
+
* Resolves a service by its Class type.
|
|
50
|
+
*/
|
|
51
|
+
getService<C extends Class>(type: C): InstanceType<C>;
|
|
52
|
+
/**
|
|
53
|
+
* Resolves a service by its key or its Class type.
|
|
54
|
+
*/
|
|
55
|
+
getService<K extends keyof T & (string | symbol)>(keyOrType: K | T[K]): MapToInstance<T[K]>;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Maps all keys in a TypeMap to their resolved instance types.
|
|
59
|
+
*/
|
|
60
|
+
export type ResolvedTypeMap<T extends TypeMap> = {
|
|
61
|
+
[K in keyof T & (string | symbol)]: MapToInstance<T[K]>;
|
|
62
|
+
};
|
|
63
|
+
/**
|
|
64
|
+
* A factory function that creates an instance of a service.
|
|
65
|
+
*/
|
|
66
|
+
export type ImplementationFactory<T extends TypeMap, R> = (container: ResolvedTypeMap<T>, sp: IServiceProvider<T>) => R;
|
|
67
|
+
/**
|
|
68
|
+
* Resolves the arguments for a given function.
|
|
69
|
+
* Each argument can be its actual type or a key from the DI container.
|
|
70
|
+
*/
|
|
71
|
+
export type FuncArgs<F extends (...args: any[]) => any, T extends TypeMap> = Parameters<F> extends infer P ? (P extends any[] ? {
|
|
72
|
+
[K in keyof P]: P[K] | FilterKeys<T, P[K]> | (P[K] extends abstract new (...args: any[]) => infer R ? FilterKeys<T, abstract new (...args: any[]) => R> : never);
|
|
73
|
+
} : never) : never;
|
|
74
|
+
/**
|
|
75
|
+
* Resolves the constructor arguments for a given class.
|
|
76
|
+
* Each argument can be its actual type or a key from the DI container.
|
|
77
|
+
*/
|
|
78
|
+
export type ClassArgs<T extends Class, Keys extends (string | symbol)> = ConstructorParameters<T> extends infer P ? (P extends any[] ? {
|
|
79
|
+
[K in keyof P]: P[K] | Keys | Class;
|
|
80
|
+
} : never) : never;
|
|
81
|
+
/**
|
|
82
|
+
* Represents the lifetime of a service.
|
|
83
|
+
*/
|
|
84
|
+
export type ServiceLifetime = 'singleton' | 'scoped';
|
|
85
|
+
/**
|
|
86
|
+
* A registration for a service created via a function.
|
|
87
|
+
*/
|
|
88
|
+
export type FunctionRegistration<F extends (...args: any[]) => any, L extends ServiceLifetime, T extends TypeMap> = {
|
|
89
|
+
kind: 'function';
|
|
90
|
+
lifetime: L;
|
|
91
|
+
func: F;
|
|
92
|
+
params: FuncArgs<F, T>;
|
|
93
|
+
};
|
|
94
|
+
/**
|
|
95
|
+
* A registration for a service created via a factory.
|
|
96
|
+
*/
|
|
97
|
+
export type FactoryRegistration<C extends Class, L extends ServiceLifetime> = {
|
|
98
|
+
kind: 'service';
|
|
99
|
+
lifetime: L;
|
|
100
|
+
type: C;
|
|
101
|
+
factory: ImplementationFactory<any, any>;
|
|
102
|
+
};
|
|
103
|
+
/**
|
|
104
|
+
* A registration for a service created via constructor injection.
|
|
105
|
+
*/
|
|
106
|
+
export type ClassRegistration<C extends Class, L extends ServiceLifetime, Keys extends (string | symbol)> = {
|
|
107
|
+
kind: 'service';
|
|
108
|
+
lifetime: L;
|
|
109
|
+
type: C;
|
|
110
|
+
params: ClassArgs<C, Keys>;
|
|
111
|
+
};
|
|
112
|
+
/**
|
|
113
|
+
* A registration for an array of services (multiple implementations).
|
|
114
|
+
*/
|
|
115
|
+
export type ArrayRegistration<C extends Class[], L extends ServiceLifetime> = {
|
|
116
|
+
kind: 'service';
|
|
117
|
+
lifetime: L;
|
|
118
|
+
type: C;
|
|
119
|
+
};
|
|
120
|
+
/**
|
|
121
|
+
* A registration for a constant value.
|
|
122
|
+
*/
|
|
123
|
+
export type ValueRegistration<V, L extends ServiceLifetime> = {
|
|
124
|
+
kind: 'value';
|
|
125
|
+
lifetime: L;
|
|
126
|
+
value: V;
|
|
127
|
+
};
|
|
128
|
+
/**
|
|
129
|
+
* Internal runtime definition for a service or value.
|
|
130
|
+
*/
|
|
131
|
+
export interface ServiceDefinition<T> {
|
|
132
|
+
kind: 'service' | 'value' | 'function';
|
|
133
|
+
lifetime: 'singleton' | 'scoped';
|
|
134
|
+
uid: string | symbol;
|
|
135
|
+
type?: T;
|
|
136
|
+
params?: any[];
|
|
137
|
+
factory?: T extends Class ? ImplementationFactory<any, any> : never;
|
|
138
|
+
value?: T;
|
|
139
|
+
func?: (...args: any[]) => any;
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* A record of all registered services and their internal definitions.
|
|
143
|
+
*/
|
|
144
|
+
export type ServiceDefinitionMap = Record<string | symbol, ServiceDefinition<any>>;
|
|
145
|
+
/**
|
|
146
|
+
* A record mapping string keys to Class constructors, arrays of constructors, or values.
|
|
147
|
+
*/
|
|
148
|
+
export type TypeMap = Record<string | symbol, any>;
|
|
149
|
+
/**
|
|
150
|
+
* Filters keys of M that match type V.
|
|
151
|
+
*/
|
|
152
|
+
export type FilterKeys<M, V> = {
|
|
153
|
+
[K in keyof M]: M[K] extends V ? K : never;
|
|
154
|
+
}[keyof M] & (string | symbol);
|
|
155
|
+
/**
|
|
156
|
+
* Filters out keys from TypeMap that are primarily used for service registration.
|
|
157
|
+
*/
|
|
158
|
+
export type FilterValueKeys<M extends TypeMap> = {
|
|
159
|
+
[K in keyof M]: M[K] extends abstract new (...args: any[]) => any ? never : (M[K] extends (abstract new (...args: any[]) => any)[] ? never : (M[K] extends (...args: any[]) => any ? never : K));
|
|
160
|
+
}[keyof M] & (string | symbol);
|
|
161
|
+
/**
|
|
162
|
+
* Filters keys of M that are functions, promises, or async functions.
|
|
163
|
+
* Excludes class constructors and promises that resolve to class constructors.
|
|
164
|
+
*/
|
|
165
|
+
export type FilterFuncKeys<M extends TypeMap> = {
|
|
166
|
+
[K in keyof M]: (M[K] extends ((...args: any[]) => any) | Promise<any> ? ((M[K] extends Promise<infer R> ? R : M[K]) extends abstract new (...args: any[]) => any ? never : K) : never);
|
|
167
|
+
}[keyof M] & (string | symbol);
|
|
168
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/types.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
# DI Service Provider Cheatsheet
|
|
2
|
+
|
|
3
|
+
Quick reference for the Type-Safe Dependency Injection container.
|
|
4
|
+
|
|
5
|
+
## Table of Contents
|
|
6
|
+
1. [Setup & TypeMap](#1-setup--typemap)
|
|
7
|
+
2. [Class Registration](#2-class-registration)
|
|
8
|
+
3. [Factory Registration](#3-factory-registration)
|
|
9
|
+
4. [Function Registration](#4-function-registration)
|
|
10
|
+
5. [Array Registration](#5-array-registration)
|
|
11
|
+
6. [Value Registration](#6-value-registration)
|
|
12
|
+
7. [Service Retrieval](#7-service-retrieval)
|
|
13
|
+
8. [Scopes](#8-scopes)
|
|
14
|
+
9. [Disposal](#9-disposal)
|
|
15
|
+
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
## 1. Setup & TypeMap
|
|
19
|
+
The `TypeMap` defines the contract of your container.
|
|
20
|
+
|
|
21
|
+
```typescript
|
|
22
|
+
import { ServiceCollection } from '@js-injection/service-provider';
|
|
23
|
+
|
|
24
|
+
interface MyMap {
|
|
25
|
+
logger: ILogger;
|
|
26
|
+
database: Database;
|
|
27
|
+
userService: UserService;
|
|
28
|
+
apiClient: ApiClient;
|
|
29
|
+
config: Config;
|
|
30
|
+
plugins: Plugin[];
|
|
31
|
+
generateId: () => string;
|
|
32
|
+
}
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
---
|
|
36
|
+
|
|
37
|
+
## 2. Class Registration
|
|
38
|
+
Standard constructor injection. Dependencies are automatically resolved.
|
|
39
|
+
|
|
40
|
+
```typescript
|
|
41
|
+
class Logger implements ILogger {
|
|
42
|
+
log(msg: string) { console.log(msg); }
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
class UserService {
|
|
46
|
+
constructor(public logger: ILogger) {}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
class ApiClient {
|
|
50
|
+
constructor(public logger: ILogger) {}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const sp = new ServiceCollection<MyMap>()
|
|
54
|
+
// Singleton (one instance global)
|
|
55
|
+
.addSingletonClass(Logger)
|
|
56
|
+
.addSingletonClass(UserService, Logger)
|
|
57
|
+
// Scoped (one instance per scope)
|
|
58
|
+
.addScopedClass(ApiClient)
|
|
59
|
+
// Build the service provider
|
|
60
|
+
.build();
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
---
|
|
64
|
+
|
|
65
|
+
## 3. Factory Registration
|
|
66
|
+
Use factories for complex logic or when you need access to the container.
|
|
67
|
+
|
|
68
|
+
```typescript
|
|
69
|
+
class Database {
|
|
70
|
+
constructor(public connectionString: string) {}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const sp = new ServiceCollection<MyMap>()
|
|
74
|
+
// Singleton Factory
|
|
75
|
+
.addSingletonFactory(Database, (container) => {
|
|
76
|
+
const connectionString = container.config.dbUrl;
|
|
77
|
+
return new Database(connectionString);
|
|
78
|
+
})
|
|
79
|
+
// Scoped Factory
|
|
80
|
+
.addScopedFactory('apiClient', (container, sp) => {
|
|
81
|
+
return new ApiClient(container.logger);
|
|
82
|
+
})
|
|
83
|
+
// Build the service provider
|
|
84
|
+
.build();
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
---
|
|
88
|
+
|
|
89
|
+
## 4. Function Registration
|
|
90
|
+
Register a function as a service. Arguments are auto-injected.
|
|
91
|
+
|
|
92
|
+
```typescript
|
|
93
|
+
function createIdGenerator(prefix: string) {
|
|
94
|
+
return () => `${prefix}-${Math.random()}`;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// Arguments can be literal values or other services
|
|
98
|
+
const sp = new ServiceCollection<MyMap>()
|
|
99
|
+
.addSingletonFunc('generateId', createIdGenerator, 'APP')
|
|
100
|
+
// Build the service provider
|
|
101
|
+
.build();
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
---
|
|
105
|
+
|
|
106
|
+
## 5. Array Registration
|
|
107
|
+
Map multiple implementations to a single key. Returns an array.
|
|
108
|
+
|
|
109
|
+
```typescript
|
|
110
|
+
interface Plugin { name: string; }
|
|
111
|
+
class AuthPlugin implements Plugin { name = 'auth'; }
|
|
112
|
+
class LogPlugin implements Plugin { name = 'log'; }
|
|
113
|
+
|
|
114
|
+
// Individual classes are auto-registered if needed
|
|
115
|
+
const sp = new ServiceCollection<MyMap>()
|
|
116
|
+
.addSingletonArray('plugins', AuthPlugin, LogPlugin)
|
|
117
|
+
// Build the service provider
|
|
118
|
+
.build();
|
|
119
|
+
|
|
120
|
+
// Retrieval
|
|
121
|
+
const plugins = sp.getService('plugins'); // [AuthPlugin, LogPlugin]
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
---
|
|
125
|
+
|
|
126
|
+
## 6. Value Registration
|
|
127
|
+
Register constant values or configuration objects.
|
|
128
|
+
|
|
129
|
+
```typescript
|
|
130
|
+
interface Config { dbUrl: string; }
|
|
131
|
+
const config: Config = { dbUrl: 'postgres://localhost:5432' };
|
|
132
|
+
|
|
133
|
+
const sp = new ServiceCollection<MyMap>()
|
|
134
|
+
.addSingletonValue('config', config)
|
|
135
|
+
// Build the service provider
|
|
136
|
+
.build();
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
---
|
|
140
|
+
|
|
141
|
+
## 7. Service Retrieval
|
|
142
|
+
Resolve services from the built provider.
|
|
143
|
+
|
|
144
|
+
```typescript
|
|
145
|
+
const sp = new ServiceCollection<MyMap>().build();
|
|
146
|
+
|
|
147
|
+
// 1. Resolve by Class (returns Implementation type)
|
|
148
|
+
const userService = sp.getService(UserService);
|
|
149
|
+
|
|
150
|
+
// 2. Resolve by Key (returns Interface type from TypeMap)
|
|
151
|
+
const logger = sp.getService('logger');
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
---
|
|
155
|
+
|
|
156
|
+
## 8. Scopes
|
|
157
|
+
Create child containers for unit-of-work isolation.
|
|
158
|
+
|
|
159
|
+
```typescript
|
|
160
|
+
// Standard Scope
|
|
161
|
+
const scope = sp.createScope();
|
|
162
|
+
const scopedSvc = scope.getService(ApiClient);
|
|
163
|
+
|
|
164
|
+
// Async Scope (supports async disposal)
|
|
165
|
+
const asyncScope = sp.createAsyncScope();
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
---
|
|
169
|
+
|
|
170
|
+
## 9. Disposal
|
|
171
|
+
Clean up resources for `IDisposable` and `IAsyncDisposable` services.
|
|
172
|
+
|
|
173
|
+
```typescript
|
|
174
|
+
// Synchronous (throws if any service is async)
|
|
175
|
+
sp.dispose();
|
|
176
|
+
|
|
177
|
+
// Asynchronous (recommended)
|
|
178
|
+
await sp.disposeAsync();
|
|
179
|
+
```
|
package/package.json
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@js-injection/service-provider",
|
|
3
|
+
"version": "1.0.0-alpha.1",
|
|
4
|
+
"description": "",
|
|
5
|
+
"license": "ISC",
|
|
6
|
+
"author": "",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "git+https://gitlab.com/js-injection/service-provider.git"
|
|
10
|
+
},
|
|
11
|
+
"bugs": {
|
|
12
|
+
"url": "https://gitlab.com/js-injection/service-provider/issues"
|
|
13
|
+
},
|
|
14
|
+
"homepage": "https://gitlab.com/js-injection/service-provider#readme",
|
|
15
|
+
"engines": {
|
|
16
|
+
"node": ">=22"
|
|
17
|
+
},
|
|
18
|
+
"type": "module",
|
|
19
|
+
"types": "./dist/types/src/index.d.ts",
|
|
20
|
+
"main": "./dist/legacy.cjs",
|
|
21
|
+
"imports": {
|
|
22
|
+
"#src/*": "./out/src/*"
|
|
23
|
+
},
|
|
24
|
+
"exports": {
|
|
25
|
+
"import": "./dist/index.js",
|
|
26
|
+
"types": "./dist/types/src/index.d.ts",
|
|
27
|
+
"require": "./dist/legacy.cjs"
|
|
28
|
+
},
|
|
29
|
+
"devDependencies": {
|
|
30
|
+
"@types/mocha": "10.0.10",
|
|
31
|
+
"@types/node": "22.19.15",
|
|
32
|
+
"@types/source-map-support": "0.5.10",
|
|
33
|
+
"c8": "11.0.0",
|
|
34
|
+
"esbuild": "0.27.4",
|
|
35
|
+
"js-build-tasks": "1.0.0-rc.18",
|
|
36
|
+
"mocha": "11.7.5",
|
|
37
|
+
"mocha-ui-esm": "1.0.0-beta.17",
|
|
38
|
+
"rimraf": "6.1.3",
|
|
39
|
+
"source-map-support": "0.5.21",
|
|
40
|
+
"typescript": "5.9.3"
|
|
41
|
+
},
|
|
42
|
+
"scripts": {
|
|
43
|
+
"compile": "task",
|
|
44
|
+
"bundle": "task",
|
|
45
|
+
"test": "task",
|
|
46
|
+
"coverage": "task",
|
|
47
|
+
"start": "task compile bundle:smoke && node ./dist/test/smoke/async-func.js",
|
|
48
|
+
"prepublishOnly": "task"
|
|
49
|
+
}
|
|
50
|
+
}
|
package/src/errors.ts
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Thrown when a circular dependency is detected.
|
|
3
|
+
*/
|
|
4
|
+
export class CircularDependencyError extends Error {
|
|
5
|
+
constructor(path: string) {
|
|
6
|
+
super(`Circular dependency detected: ${path}`);
|
|
7
|
+
this.name = 'CircularDependencyError';
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Thrown when a service cannot be resolved.
|
|
13
|
+
*/
|
|
14
|
+
export class ServiceNotFoundError extends Error {
|
|
15
|
+
constructor(identifier: string) {
|
|
16
|
+
super(`Service not registered for identifier: ${identifier}`);
|
|
17
|
+
this.name = 'ServiceNotFoundError';
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Thrown when a registration is invalid.
|
|
23
|
+
*/
|
|
24
|
+
export class RegistrationError extends Error {
|
|
25
|
+
constructor(message: string) {
|
|
26
|
+
super(message);
|
|
27
|
+
this.name = 'RegistrationError';
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Thrown when a service provider has been disposed.
|
|
33
|
+
*/
|
|
34
|
+
export class ServiceProviderDisposedError extends Error {
|
|
35
|
+
constructor() {
|
|
36
|
+
super('ServiceProvider has been disposed and cannot resolve services.');
|
|
37
|
+
this.name = 'ServiceProviderDisposedError';
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Thrown when a synchronous dispose is called on a scope containing services that require asynchronous disposal.
|
|
43
|
+
*/
|
|
44
|
+
export class AsyncDisposalError extends Error {
|
|
45
|
+
constructor(serviceKey: string) {
|
|
46
|
+
super(`Service "${serviceKey}" requires asynchronous disposal but a synchronous dispose was called. Please use await disposeAsync() instead.`);
|
|
47
|
+
this.name = 'AsyncDisposalError';
|
|
48
|
+
}
|
|
49
|
+
}
|
package/src/index.ts
ADDED
package/src/registry.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { Class } from './types.js';
|
|
2
|
+
|
|
3
|
+
const classKeyRegistry = new WeakMap<Class, symbol>();
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Resolves a stable Symbol for a given class constructor.
|
|
7
|
+
* This is used to ensure that DI resolution remains stable even after minification.
|
|
8
|
+
* @param type The class constructor.
|
|
9
|
+
*/
|
|
10
|
+
export function getStableKey(type: Class): symbol {
|
|
11
|
+
let key = classKeyRegistry.get(type);
|
|
12
|
+
if (!key) {
|
|
13
|
+
key = Symbol(type.name || 'AnonymousClass');
|
|
14
|
+
classKeyRegistry.set(type, key);
|
|
15
|
+
}
|
|
16
|
+
return key;
|
|
17
|
+
}
|