From 92f00e3aa029fa1f616bd22f1fb947dcf1d15695 Mon Sep 17 00:00:00 2001 From: Stevan Freeborn <65925598+StevanFreeborn@users.noreply.github.com> Date: Mon, 12 May 2025 22:18:55 -0500 Subject: [PATCH] chore: npm run format --- .github/workflows/publish.yml | 2 +- .github/workflows/pull_request.yml | 5 +- README.md | 22 ++++---- src/decorators.ts | 8 +-- src/index.ts | 2 +- src/serviceCollection.ts | 37 ++++++------- src/serviceProvider.ts | 30 +++++------ src/types.ts | 83 ++++++++++++++++++------------ tests/serviceCollection.spec.ts | 58 +++++++++++---------- tests/serviceProvider.spec.ts | 26 +++++----- 10 files changed, 144 insertions(+), 129 deletions(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 218a9fb..daed014 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -23,7 +23,7 @@ jobs: - name: Build run: npm run build - name: Publish - env: + env: NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} GITHUB_TOKEN: ${{ secrets.ACTIONS_PAT }} run: npx semantic-release diff --git a/.github/workflows/pull_request.yml b/.github/workflows/pull_request.yml index a9d234e..e314d47 100644 --- a/.github/workflows/pull_request.yml +++ b/.github/workflows/pull_request.yml @@ -11,7 +11,7 @@ jobs: steps: - name: Check out repository uses: actions/checkout@v4 - - name: Setup node + - name: Setup node uses: actions/setup-node@v4 with: node-version: lts/* @@ -25,7 +25,7 @@ jobs: steps: - name: Check out repository uses: actions/checkout@v4 - - name: Setup node + - name: Setup node uses: actions/setup-node@v4 with: node-version: lts/* @@ -76,4 +76,3 @@ jobs: uses: codecov/codecov-action@v4 with: token: ${{ secrets.CODECOV_TOKEN }} - diff --git a/README.md b/README.md index 6ed2a9f..b449cd9 100644 --- a/README.md +++ b/README.md @@ -52,11 +52,11 @@ Add the following to your `tsconfig.json`: Here's a simple example of how to use netdi: ```typescript -import { - ServiceCollection, - createServiceIdentifier, - injectable, - inject +import { + ServiceCollection, + createServiceIdentifier, + injectable, + inject, } from '@stevanfreeborn/netdi'; // Define interfaces @@ -82,9 +82,7 @@ class GreetingService implements IGreetingService { @injectable() class Greeter implements IGreeter { - constructor( - @inject(greetingServiceIdentifier) private greetingService: IGreetingService - ) {} + constructor(@inject(greetingServiceIdentifier) private greetingService: IGreetingService) {} greet(name: string): string { return this.greetingService.createGreeting(name); @@ -146,14 +144,14 @@ services.addTransient(serviceIdentifier, Implementation); For complex service instantiation, you can use factory methods: ```typescript -services.addSingleton(serviceIdentifier, (provider) => { +services.addSingleton(serviceIdentifier, provider => { // Use the provider to get dependencies const dependency = provider.getService(dependencyIdentifier); - + // Create and configure your service instance const instance = new MyService(dependency); instance.configure(); - + return instance; }); ``` @@ -192,7 +190,7 @@ Specifies a dependency for a parameter: class MyService { constructor( @inject(loggerIdentifier) private logger: ILogger, - @inject(configIdentifier) private config: IConfig + @inject(configIdentifier) private config: IConfig, ) {} } ``` diff --git a/src/decorators.ts b/src/decorators.ts index 56c0c64..2eac770 100644 --- a/src/decorators.ts +++ b/src/decorators.ts @@ -14,11 +14,11 @@ 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 { @@ -37,9 +37,9 @@ export function inject(serviceType: ServiceIdentifier): 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() diff --git a/src/index.ts b/src/index.ts index cac7d08..ad54198 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,4 +1,4 @@ -import "reflect-metadata"; +import 'reflect-metadata'; export * from './serviceProvider.js'; export * from './types.js'; diff --git a/src/serviceCollection.ts b/src/serviceCollection.ts index 1753ed0..e6c21bd 100644 --- a/src/serviceCollection.ts +++ b/src/serviceCollection.ts @@ -11,10 +11,10 @@ import type { /** * 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 { @@ -26,14 +26,14 @@ export class ServiceCollection implements IServiceCollection { /** * 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); @@ -50,15 +50,15 @@ export class ServiceCollection implements IServiceCollection { /** * 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); @@ -75,14 +75,14 @@ export class ServiceCollection implements IServiceCollection { /** * 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); @@ -99,7 +99,7 @@ export class ServiceCollection implements IServiceCollection { /** * Builds a service provider from the registered services. - * + * * @returns A new service provider that can resolve the registered services */ public build(): IServiceProvider { @@ -108,7 +108,7 @@ export class ServiceCollection implements IServiceCollection { /** * 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 @@ -120,7 +120,10 @@ export class ServiceCollection implements IServiceCollection { implementationOrFactory: ServiceFactory | Constructor, lifetime: ServiceLifetime, ): IServiceCollection { - if (typeof implementationOrFactory === 'function' && this.isConstructor(implementationOrFactory) === false) { + if ( + typeof implementationOrFactory === 'function' && + this.isConstructor(implementationOrFactory) === false + ) { const descriptor: ServiceDescriptor = { serviceType, implementationType: Object as unknown as Constructor, @@ -144,13 +147,11 @@ export class ServiceCollection implements IServiceCollection { /** * 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 { - return typeof func === 'function' && - !!func.prototype && - func.prototype.constructor === func; + return typeof func === 'function' && !!func.prototype && func.prototype.constructor === func; } } diff --git a/src/serviceProvider.ts b/src/serviceProvider.ts index a6585b0..606f095 100644 --- a/src/serviceProvider.ts +++ b/src/serviceProvider.ts @@ -9,9 +9,9 @@ import type { /** * 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 { @@ -22,7 +22,7 @@ export class ServiceScope implements IServiceScope { /** * Creates a new service scope - * + * * @param serviceProvider - The service provider for this scope */ constructor(serviceProvider: IServiceProvider) { @@ -39,10 +39,10 @@ export class ServiceScope implements IServiceScope { /** * 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 { @@ -50,12 +50,12 @@ export class ServiceProvider implements IServiceProvider { * Map of service descriptors by service identifier */ private readonly _descriptors: Map, ServiceDescriptor>; - + /** * Map of singleton service instances by service identifier */ private readonly _singletonInstances: Map, unknown> = new Map(); - + /** * Map of scoped service instances by service identifier */ @@ -63,7 +63,7 @@ export class ServiceProvider implements IServiceProvider { /** * Creates a new service provider - * + * * @param descriptors - Map of service descriptors * @param parent - Optional parent service provider to inherit singleton instances from */ @@ -89,12 +89,12 @@ 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); @@ -102,7 +102,7 @@ export class ServiceProvider implements IServiceProvider { */ getService(serviceType: ServiceIdentifier): T { const descriptor = this._descriptors.get(serviceType); - + if (!descriptor) { throw new Error(`Service of type ${serviceType.toString()} is not registered.`); } @@ -112,9 +112,9 @@ export class ServiceProvider implements IServiceProvider { /** * Creates a new scope for scoped services - * + * * @returns A new service scope - * + * * @example * ```typescript * const scope = serviceProvider.createScope(); @@ -137,7 +137,7 @@ export class ServiceProvider implements IServiceProvider { /** * 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 @@ -172,7 +172,7 @@ 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 diff --git a/src/types.ts b/src/types.ts index b9279c5..b1371a5 100644 --- a/src/types.ts +++ b/src/types.ts @@ -1,6 +1,6 @@ /** * 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 @@ -9,7 +9,7 @@ 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 @@ -17,21 +17,21 @@ export type Constructor = new (...args: any[]) => T; /** * A unique identifier for a service type - * + * * @template T - The type of the service */ export type ServiceIdentifier = symbol & { __brand: T }; /** * A factory function that creates a service instance - * + * * @template T - The type of the service to create */ export type ServiceFactory = (provider: IServiceProvider) => T; /** * Descriptor for a registered service - * + * * @template T - The type of the service */ export type ServiceDescriptor = { @@ -39,17 +39,17 @@ export type ServiceDescriptor = { * The service identifier */ serviceType: ServiceIdentifier; - + /** * The implementation class constructor */ implementationType: Constructor; - + /** * The service lifetime */ lifetime: ServiceLifetime; - + /** * Optional factory function to create the service instance */ @@ -58,16 +58,16 @@ export type ServiceDescriptor = { /** * 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; * } - * + * * const userServiceIdentifier = createServiceIdentifier(); * ``` */ @@ -81,67 +81,82 @@ export function createServiceIdentifier(): ServiceIdentifier { 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(serviceType: ServiceIdentifier, implementationType: Constructor): IServiceCollection; - + addSingleton( + serviceType: ServiceIdentifier, + implementationType: Constructor, + ): 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(serviceType: ServiceIdentifier, factory: ServiceFactory): IServiceCollection; - + addSingleton( + serviceType: ServiceIdentifier, + factory: ServiceFactory, + ): 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(serviceType: ServiceIdentifier, implementationType: Constructor): IServiceCollection; - + addScoped( + serviceType: ServiceIdentifier, + implementationType: Constructor, + ): 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(serviceType: ServiceIdentifier, factory: ServiceFactory): 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(serviceType: ServiceIdentifier, implementationType: Constructor): IServiceCollection; - + addTransient( + serviceType: ServiceIdentifier, + implementationType: Constructor, + ): 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(serviceType: ServiceIdentifier, factory: ServiceFactory): IServiceCollection; - + addTransient( + serviceType: ServiceIdentifier, + factory: ServiceFactory, + ): IServiceCollection; + /** * Builds a service provider from the registered services - * + * * @returns A new service provider instance */ build(): IServiceProvider; @@ -153,20 +168,20 @@ export interface IServiceCollection { 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(serviceType: ServiceIdentifier): 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 */ @@ -181,7 +196,7 @@ export interface IServiceScope { * The service provider for this scope */ serviceProvider: IServiceProvider; - + /** * Disposes the scope and clears any scoped service instances */ diff --git a/tests/serviceCollection.spec.ts b/tests/serviceCollection.spec.ts index 5918d9c..39405df 100644 --- a/tests/serviceCollection.spec.ts +++ b/tests/serviceCollection.spec.ts @@ -5,103 +5,102 @@ describe('ServiceCollection', () => { it('should add a singleton service', () => { class TestService {} const serviceIdentifier = createServiceIdentifier(); - + const collection = new ServiceCollection(); collection.addSingleton(serviceIdentifier, TestService); - + const provider = collection.build(); const serviceInstance1 = provider.getService(serviceIdentifier); const serviceInstance2 = provider.getService(serviceIdentifier); - + expect(serviceInstance1).toBe(serviceInstance2); }); it('should add a scoped service', () => { class TestService {} const serviceIdentifier = createServiceIdentifier(); - + const collection = new ServiceCollection(); collection.addScoped(serviceIdentifier, TestService); - + const provider = collection.build(); const scope1 = provider.createScope(); const scope2 = provider.createScope(); - + const serviceInstance1 = scope1.serviceProvider.getService(serviceIdentifier); const serviceInstance2 = scope2.serviceProvider.getService(serviceIdentifier); - + expect(serviceInstance1).not.toBe(serviceInstance2); }); it('should add a transient service', () => { class TestService {} const serviceIdentifier = createServiceIdentifier(); - + const collection = new ServiceCollection(); collection.addTransient(serviceIdentifier, TestService); - + const provider = collection.build(); - + const serviceInstance1 = provider.getService(serviceIdentifier); const serviceInstance2 = provider.getService(serviceIdentifier); - + expect(serviceInstance1).not.toBe(serviceInstance2); }); it('should add a single service with a factory', () => { class TestService {} const serviceIdentifier = createServiceIdentifier(); - + const collection = new ServiceCollection(); collection.addSingleton(serviceIdentifier, () => new TestService()); - + const provider = collection.build(); const serviceInstance1 = provider.getService(serviceIdentifier); const serviceInstance2 = provider.getService(serviceIdentifier); - + expect(serviceInstance1).toBe(serviceInstance2); }); it('should add a scoped service with a factory', () => { class TestService {} const serviceIdentifier = createServiceIdentifier(); - + const collection = new ServiceCollection(); collection.addScoped(serviceIdentifier, () => new TestService()); - + const provider = collection.build(); const scope1 = provider.createScope(); const scope2 = provider.createScope(); - + const serviceInstance1 = scope1.serviceProvider.getService(serviceIdentifier); const serviceInstance2 = scope2.serviceProvider.getService(serviceIdentifier); - + expect(serviceInstance1).not.toBe(serviceInstance2); }); - it('should add a transient service with a factory', () => { class TestService {} const serviceIdentifier = createServiceIdentifier(); - + const collection = new ServiceCollection(); collection.addTransient(serviceIdentifier, () => new TestService()); - + const provider = collection.build(); - + const serviceInstance1 = provider.getService(serviceIdentifier); const serviceInstance2 = provider.getService(serviceIdentifier); - + expect(serviceInstance1).not.toBe(serviceInstance2); }); it('should add a service with a factory and dependencies', () => { - type IDependencyService = object + type IDependencyService = object; class DependencyService implements IDependencyService {} type ITestService = { dependency: IDependencyService; - } + }; class TestService implements ITestService { constructor(public dependency: DependencyService) {} } @@ -111,12 +110,15 @@ describe('ServiceCollection', () => { const collection = new ServiceCollection(); collection.addSingleton(dependencyServiceIdentifier, DependencyService); - collection.addSingleton(testServiceIdentifier, (provider) => new TestService(provider.getService(dependencyServiceIdentifier))); - + collection.addSingleton( + testServiceIdentifier, + provider => new TestService(provider.getService(dependencyServiceIdentifier)), + ); + const provider = collection.build(); const serviceInstance1 = provider.getService(testServiceIdentifier); const serviceInstance2 = provider.getService(testServiceIdentifier); - + expect(serviceInstance1).toBe(serviceInstance2); expect(serviceInstance1.dependency).toBe(serviceInstance2.dependency); }); diff --git a/tests/serviceProvider.spec.ts b/tests/serviceProvider.spec.ts index f827ea0..29c9d59 100644 --- a/tests/serviceProvider.spec.ts +++ b/tests/serviceProvider.spec.ts @@ -247,7 +247,7 @@ describe('ServiceProvider', () => { ); const userService = serviceProvider.getService(userServiceIdentifier); - + const user = userService.getUser(); expect(user).toBe('John Doe'); @@ -255,9 +255,9 @@ describe('ServiceProvider', () => { it('should throw an error when service has invalid lifetime', () => { type IService = object; - + const serviceIdentifier = createServiceIdentifier(); - + const serviceProvider = new ServiceProvider( new Map([ [ @@ -341,9 +341,9 @@ describe('ServiceProvider', () => { ); const initialService = serviceProvider.getService(serviceIdentifier); - + serviceProvider.dispose(); - + const newService = serviceProvider.getService(serviceIdentifier); expect(initialService).not.toBe(newService); @@ -380,9 +380,9 @@ describe('ServiceProvider', () => { const scope = serviceProvider.createScope(); const initialService = scope.serviceProvider.getService(serviceIdentifier); - + scope.dispose(); - + const newService = scope.serviceProvider.getService(serviceIdentifier); expect(initialService).not.toBe(newService); @@ -419,15 +419,15 @@ describe('ServiceProvider', () => { const scope = serviceProvider.createScope(); const initialService = scope.serviceProvider.getService(serviceIdentifier); - + serviceProvider.dispose(); - + const newService = scope.serviceProvider.getService(serviceIdentifier); expect(initialService).toBe(newService); }); - it("should not clear the scoped instances on dispose from another scope when one scope is disposed", () => { + it('should not clear the scoped instances on dispose from another scope when one scope is disposed', () => { type IService = { id: string; }; @@ -460,9 +460,9 @@ describe('ServiceProvider', () => { const initialService1 = scope1.serviceProvider.getService(serviceIdentifier); const initialService2 = scope2.serviceProvider.getService(serviceIdentifier); - + scope1.dispose(); - + const newService1 = scope1.serviceProvider.getService(serviceIdentifier); const newService2 = scope2.serviceProvider.getService(serviceIdentifier); @@ -501,7 +501,7 @@ describe('ServiceProvider', () => { ], ]), ); - + expect(() => { serviceProvider.getService(userServiceIdentifier); }).toThrowError();