docs: enhance type definitions and decorators with detailed JSDoc comments
This commit is contained in:
@@ -1,8 +1,33 @@
|
||||
import type { ServiceIdentifier } from './types.js';
|
||||
|
||||
/**
|
||||
* Metadata key for storing parameter type information for dependency injection
|
||||
* @internal
|
||||
*/
|
||||
export const DI_PARAM_TYPES = 'di:paramtypes';
|
||||
|
||||
/**
|
||||
* Metadata key for marking classes as injectable
|
||||
* @internal
|
||||
*/
|
||||
export const DI_INJECTABLE = 'di:injectable';
|
||||
|
||||
/**
|
||||
* Decorator for constructor parameters that specifies which service identifier to use for injection
|
||||
*
|
||||
* @template T - The type of the service to be injected
|
||||
* @param serviceType - The service identifier for the dependency to inject
|
||||
* @returns A parameter decorator function that associates the parameter with the service identifier
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* class MyService {
|
||||
* constructor(
|
||||
* @inject(loggerIdentifier) private logger: ILogger
|
||||
* ) {}
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
export function inject<T>(serviceType: ServiceIdentifier<T>): ParameterDecorator {
|
||||
// eslint-disable-next-line @typescript-eslint/no-wrapper-object-types
|
||||
return (target: Object, _: string | symbol | undefined, parameterIndex: number) => {
|
||||
@@ -10,6 +35,19 @@ export function inject<T>(serviceType: ServiceIdentifier<T>): ParameterDecorator
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Decorator that marks a class as injectable, allowing the container to create instances with dependencies
|
||||
*
|
||||
* @returns A class decorator function that marks the class as injectable
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* @injectable()
|
||||
* class MyService {
|
||||
* constructor() {}
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
export function injectable(): ClassDecorator {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
|
||||
return (target: Function) => {
|
||||
|
||||
@@ -9,10 +9,38 @@ import type {
|
||||
ServiceLifetime,
|
||||
} from './types.js';
|
||||
|
||||
/**
|
||||
* A collection of service descriptors that can be used to build a service provider.
|
||||
*
|
||||
* This class is used to register services with different lifetimes and build a service provider
|
||||
* that can resolve those services at runtime.
|
||||
*
|
||||
* @implements {IServiceCollection}
|
||||
*/
|
||||
export class ServiceCollection implements IServiceCollection {
|
||||
/**
|
||||
* Internal map of service descriptors, keyed by service identifiers
|
||||
*/
|
||||
private readonly _descriptors: Map<ServiceIdentifier<unknown>, ServiceDescriptor<unknown>> =
|
||||
new Map();
|
||||
|
||||
/**
|
||||
* Registers a singleton service with the collection.
|
||||
*
|
||||
* Singleton services are created once and shared by all consumers.
|
||||
*
|
||||
* @template T - The type of the service to register
|
||||
* @param serviceType - The service identifier
|
||||
* @param implementationOrFactory - The implementation class or factory function
|
||||
* @returns The service collection instance for method chaining
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* services.addSingleton(userServiceIdentifier, UserService);
|
||||
* // or with a factory:
|
||||
* services.addSingleton(userServiceIdentifier, (provider) => new UserService(provider.getService(loggerIdentifier)));
|
||||
* ```
|
||||
*/
|
||||
public addSingleton<T>(
|
||||
serviceType: ServiceIdentifier<T>,
|
||||
implementationOrFactory: ServiceFactory<T> | Constructor<T>,
|
||||
@@ -20,6 +48,24 @@ export class ServiceCollection implements IServiceCollection {
|
||||
return this.add(serviceType, implementationOrFactory, 'singleton');
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a scoped service with the collection.
|
||||
*
|
||||
* Scoped services are created once per scope. This is useful for services that should be
|
||||
* shared within a request but not across requests.
|
||||
*
|
||||
* @template T - The type of the service to register
|
||||
* @param serviceType - The service identifier
|
||||
* @param implementationOrFactory - The implementation class or factory function
|
||||
* @returns The service collection instance for method chaining
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* services.addScoped(userServiceIdentifier, UserService);
|
||||
* // or with a factory:
|
||||
* services.addScoped(userServiceIdentifier, (provider) => new UserService(provider.getService(loggerIdentifier)));
|
||||
* ```
|
||||
*/
|
||||
public addScoped<T>(
|
||||
serviceType: ServiceIdentifier<T>,
|
||||
implementationOrFactory: ServiceFactory<T> | Constructor<T>,
|
||||
@@ -27,6 +73,23 @@ export class ServiceCollection implements IServiceCollection {
|
||||
return this.add(serviceType, implementationOrFactory, 'scoped');
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a transient service with the collection.
|
||||
*
|
||||
* Transient services are created each time they are requested.
|
||||
*
|
||||
* @template T - The type of the service to register
|
||||
* @param serviceType - The service identifier
|
||||
* @param implementationOrFactory - The implementation class or factory function
|
||||
* @returns The service collection instance for method chaining
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* services.addTransient(userServiceIdentifier, UserService);
|
||||
* // or with a factory:
|
||||
* services.addTransient(userServiceIdentifier, (provider) => new UserService(provider.getService(loggerIdentifier)));
|
||||
* ```
|
||||
*/
|
||||
public addTransient<T>(
|
||||
serviceType: ServiceIdentifier<T>,
|
||||
implementationOrFactory: ServiceFactory<T> | Constructor<T>,
|
||||
@@ -34,10 +97,24 @@ export class ServiceCollection implements IServiceCollection {
|
||||
return this.add(serviceType, implementationOrFactory, 'transient');
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a service provider from the registered services.
|
||||
*
|
||||
* @returns A new service provider that can resolve the registered services
|
||||
*/
|
||||
public build(): IServiceProvider {
|
||||
return new ServiceProvider(this._descriptors);
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal method to add a service descriptor to the collection.
|
||||
*
|
||||
* @template T - The type of the service to register
|
||||
* @param serviceType - The service identifier
|
||||
* @param implementationOrFactory - The implementation class or factory function
|
||||
* @param lifetime - The service lifetime
|
||||
* @returns The service collection instance for method chaining
|
||||
*/
|
||||
private add<T>(
|
||||
serviceType: ServiceIdentifier<T>,
|
||||
implementationOrFactory: ServiceFactory<T> | Constructor<T>,
|
||||
@@ -65,6 +142,12 @@ export class ServiceCollection implements IServiceCollection {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a function is a constructor
|
||||
*
|
||||
* @param func - The function to check
|
||||
* @returns True if the function is a constructor, false otherwise
|
||||
*/
|
||||
private isConstructor(func: unknown): func is Constructor<unknown> {
|
||||
return typeof func === 'function' &&
|
||||
!!func.prototype &&
|
||||
|
||||
@@ -7,23 +7,66 @@ import type {
|
||||
Constructor,
|
||||
} from './types.js';
|
||||
|
||||
/**
|
||||
* Represents a scope for scoped services.
|
||||
*
|
||||
* A service scope provides access to scoped services that are created once per scope.
|
||||
*
|
||||
* @implements {IServiceScope}
|
||||
*/
|
||||
export class ServiceScope implements IServiceScope {
|
||||
/**
|
||||
* The service provider associated with this scope
|
||||
*/
|
||||
public readonly serviceProvider: IServiceProvider;
|
||||
|
||||
/**
|
||||
* Creates a new service scope
|
||||
*
|
||||
* @param serviceProvider - The service provider for this scope
|
||||
*/
|
||||
constructor(serviceProvider: IServiceProvider) {
|
||||
this.serviceProvider = serviceProvider;
|
||||
}
|
||||
|
||||
/**
|
||||
* Disposes the scope and clears any scoped service instances
|
||||
*/
|
||||
dispose(): void {
|
||||
this.serviceProvider.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A provider that can resolve registered services by their service identifier.
|
||||
*
|
||||
* The service provider is responsible for creating and managing service instances
|
||||
* according to their registered lifetime.
|
||||
*
|
||||
* @implements {IServiceProvider}
|
||||
*/
|
||||
export class ServiceProvider implements IServiceProvider {
|
||||
/**
|
||||
* Map of service descriptors by service identifier
|
||||
*/
|
||||
private readonly _descriptors: Map<ServiceIdentifier<unknown>, ServiceDescriptor<unknown>>;
|
||||
|
||||
/**
|
||||
* Map of singleton service instances by service identifier
|
||||
*/
|
||||
private readonly _singletonInstances: Map<ServiceIdentifier<unknown>, unknown> = new Map();
|
||||
|
||||
/**
|
||||
* Map of scoped service instances by service identifier
|
||||
*/
|
||||
private readonly _scopedInstances: Map<ServiceIdentifier<unknown>, unknown> = new Map();
|
||||
|
||||
/**
|
||||
* Creates a new service provider
|
||||
*
|
||||
* @param descriptors - Map of service descriptors
|
||||
* @param parent - Optional parent service provider to inherit singleton instances from
|
||||
*/
|
||||
constructor(
|
||||
descriptors: Map<ServiceIdentifier<unknown>, ServiceDescriptor<unknown>>,
|
||||
parent?: ServiceProvider,
|
||||
@@ -36,6 +79,7 @@ export class ServiceProvider implements IServiceProvider {
|
||||
});
|
||||
}
|
||||
|
||||
// Pre-resolve singleton services
|
||||
for (const descriptor of descriptors.values()) {
|
||||
if (descriptor.lifetime === 'singleton') {
|
||||
this.resolveService(descriptor);
|
||||
@@ -43,6 +87,19 @@ export class ServiceProvider implements IServiceProvider {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a service instance by its service identifier
|
||||
*
|
||||
* @template T - The type of the service to resolve
|
||||
* @param serviceType - The service identifier of the service to resolve
|
||||
* @returns The resolved service instance
|
||||
* @throws Error if the service is not registered
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* const userService = serviceProvider.getService(userServiceIdentifier);
|
||||
* ```
|
||||
*/
|
||||
getService<T>(serviceType: ServiceIdentifier<T>): T {
|
||||
const descriptor = this._descriptors.get(serviceType);
|
||||
|
||||
@@ -53,15 +110,39 @@ export class ServiceProvider implements IServiceProvider {
|
||||
return this.resolveService(descriptor as ServiceDescriptor<T>);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new scope for scoped services
|
||||
*
|
||||
* @returns A new service scope
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* const scope = serviceProvider.createScope();
|
||||
* const scopedService = scope.serviceProvider.getService(serviceIdentifier);
|
||||
* // ... use scoped service
|
||||
* scope.dispose();
|
||||
* ```
|
||||
*/
|
||||
createScope(): IServiceScope {
|
||||
const scopedProvider = new ServiceProvider(this._descriptors, this);
|
||||
return new ServiceScope(scopedProvider);
|
||||
}
|
||||
|
||||
/**
|
||||
* Disposes the service provider and clears any scoped service instances
|
||||
*/
|
||||
dispose(): void {
|
||||
this._scopedInstances.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves a service instance from its descriptor
|
||||
*
|
||||
* @template T - The type of the service to resolve
|
||||
* @param descriptor - The service descriptor
|
||||
* @returns The resolved service instance
|
||||
* @throws Error if the service lifetime is unknown
|
||||
*/
|
||||
private resolveService<T>(descriptor: ServiceDescriptor<T>): T {
|
||||
const { serviceType, implementationType, lifetime, factory } = descriptor;
|
||||
|
||||
@@ -89,6 +170,13 @@ export class ServiceProvider implements IServiceProvider {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an instance of a service class and resolves its dependencies
|
||||
*
|
||||
* @template T - The type of the service to create
|
||||
* @param ctor - The constructor of the service class
|
||||
* @returns A new instance of the service class with its dependencies resolved
|
||||
*/
|
||||
private createInstance<T>(ctor: Constructor<T>): T {
|
||||
const paramTypes = Reflect.getMetadata('design:paramtypes', ctor) ?? [];
|
||||
|
||||
|
||||
+149
@@ -1,40 +1,189 @@
|
||||
/**
|
||||
* Represents the possible lifetimes for registered services
|
||||
*
|
||||
* - singleton: Created once and shared by all consumers
|
||||
* - scoped: Created once per scope
|
||||
* - transient: Created each time they are requested
|
||||
*/
|
||||
export type ServiceLifetime = 'singleton' | 'scoped' | 'transient';
|
||||
|
||||
/**
|
||||
* Represents a constructor function for a class
|
||||
*
|
||||
* @template T - The type of object the constructor creates
|
||||
*/
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
export type Constructor<T> = new (...args: any[]) => T;
|
||||
|
||||
/**
|
||||
* A unique identifier for a service type
|
||||
*
|
||||
* @template T - The type of the service
|
||||
*/
|
||||
export type ServiceIdentifier<T = unknown> = symbol & { __brand: T };
|
||||
|
||||
/**
|
||||
* A factory function that creates a service instance
|
||||
*
|
||||
* @template T - The type of the service to create
|
||||
*/
|
||||
export type ServiceFactory<T> = (provider: IServiceProvider) => T;
|
||||
|
||||
/**
|
||||
* Descriptor for a registered service
|
||||
*
|
||||
* @template T - The type of the service
|
||||
*/
|
||||
export type ServiceDescriptor<T = unknown> = {
|
||||
/**
|
||||
* The service identifier
|
||||
*/
|
||||
serviceType: ServiceIdentifier<T>;
|
||||
|
||||
/**
|
||||
* The implementation class constructor
|
||||
*/
|
||||
implementationType: Constructor<T>;
|
||||
|
||||
/**
|
||||
* The service lifetime
|
||||
*/
|
||||
lifetime: ServiceLifetime;
|
||||
|
||||
/**
|
||||
* Optional factory function to create the service instance
|
||||
*/
|
||||
factory?: ServiceFactory<T>;
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates a typed service identifier
|
||||
*
|
||||
* @template T - The type of the service
|
||||
* @returns A unique identifier for the service type
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* interface IUserService {
|
||||
* getUserById(id: string): Promise<User>;
|
||||
* }
|
||||
*
|
||||
* const userServiceIdentifier = createServiceIdentifier<IUserService>();
|
||||
* ```
|
||||
*/
|
||||
export function createServiceIdentifier<T>(): ServiceIdentifier<T> {
|
||||
return Symbol() as ServiceIdentifier<T>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Interface for a collection of service descriptors that can be used to build a service provider
|
||||
*/
|
||||
export interface IServiceCollection {
|
||||
/**
|
||||
* Registers a singleton service with the collection
|
||||
*
|
||||
* @template T - The type of the service
|
||||
* @param serviceType - The service identifier
|
||||
* @param implementationType - The implementation class
|
||||
* @returns The service collection for method chaining
|
||||
*/
|
||||
addSingleton<T>(serviceType: ServiceIdentifier<T>, implementationType: Constructor<T>): IServiceCollection;
|
||||
|
||||
/**
|
||||
* Registers a singleton service with a factory function
|
||||
*
|
||||
* @template T - The type of the service
|
||||
* @param serviceType - The service identifier
|
||||
* @param factory - A factory function that creates the service instance
|
||||
* @returns The service collection for method chaining
|
||||
*/
|
||||
addSingleton<T>(serviceType: ServiceIdentifier<T>, factory: ServiceFactory<T>): IServiceCollection;
|
||||
|
||||
/**
|
||||
* Registers a scoped service with the collection
|
||||
*
|
||||
* @template T - The type of the service
|
||||
* @param serviceType - The service identifier
|
||||
* @param implementationType - The implementation class
|
||||
* @returns The service collection for method chaining
|
||||
*/
|
||||
addScoped<T>(serviceType: ServiceIdentifier<T>, implementationType: Constructor<T>): IServiceCollection;
|
||||
|
||||
/**
|
||||
* Registers a scoped service with a factory function
|
||||
*
|
||||
* @template T - The type of the service
|
||||
* @param serviceType - The service identifier
|
||||
* @param factory - A factory function that creates the service instance
|
||||
* @returns The service collection for method chaining
|
||||
*/
|
||||
addScoped<T>(serviceType: ServiceIdentifier<T>, factory: ServiceFactory<T>): IServiceCollection;
|
||||
|
||||
/**
|
||||
* Registers a transient service with the collection
|
||||
*
|
||||
* @template T - The type of the service
|
||||
* @param serviceType - The service identifier
|
||||
* @param implementationType - The implementation class
|
||||
* @returns The service collection for method chaining
|
||||
*/
|
||||
addTransient<T>(serviceType: ServiceIdentifier<T>, implementationType: Constructor<T>): IServiceCollection;
|
||||
|
||||
/**
|
||||
* Registers a transient service with a factory function
|
||||
*
|
||||
* @template T - The type of the service
|
||||
* @param serviceType - The service identifier
|
||||
* @param factory - A factory function that creates the service instance
|
||||
* @returns The service collection for method chaining
|
||||
*/
|
||||
addTransient<T>(serviceType: ServiceIdentifier<T>, factory: ServiceFactory<T>): IServiceCollection;
|
||||
|
||||
/**
|
||||
* Builds a service provider from the registered services
|
||||
*
|
||||
* @returns A new service provider instance
|
||||
*/
|
||||
build(): IServiceProvider;
|
||||
}
|
||||
|
||||
/**
|
||||
* Interface for a provider that can resolve services by their service identifier
|
||||
*/
|
||||
export interface IServiceProvider {
|
||||
/**
|
||||
* Gets a service instance by its service identifier
|
||||
*
|
||||
* @template T - The type of the service to resolve
|
||||
* @param serviceType - The service identifier
|
||||
* @returns The resolved service instance
|
||||
*/
|
||||
getService<T>(serviceType: ServiceIdentifier<T>): T;
|
||||
|
||||
/**
|
||||
* Creates a new scope for scoped services
|
||||
*
|
||||
* @returns A new service scope
|
||||
*/
|
||||
createScope(): IServiceScope;
|
||||
|
||||
/**
|
||||
* Disposes the service provider and clears any scoped service instances
|
||||
*/
|
||||
dispose(): void;
|
||||
}
|
||||
|
||||
/**
|
||||
* Interface for a scope that provides access to scoped services
|
||||
*/
|
||||
export interface IServiceScope {
|
||||
/**
|
||||
* The service provider for this scope
|
||||
*/
|
||||
serviceProvider: IServiceProvider;
|
||||
|
||||
/**
|
||||
* Disposes the scope and clears any scoped service instances
|
||||
*/
|
||||
dispose(): void;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user