chore: npm run format

This commit is contained in:
Stevan Freeborn
2025-05-12 22:23:31 -05:00
parent 586ee228ac
commit 92f00e3aa0
10 changed files with 144 additions and 129 deletions
+1 -1
View File
@@ -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
+2 -3
View File
@@ -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 }}
+10 -12
View File
@@ -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,
) {}
}
```
+4 -4
View File
@@ -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<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()
+1 -1
View File
@@ -1,4 +1,4 @@
import "reflect-metadata";
import 'reflect-metadata';
export * from './serviceProvider.js';
export * from './types.js';
+19 -18
View File
@@ -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<T> | Constructor<T>,
lifetime: ServiceLifetime,
): IServiceCollection {
if (typeof implementationOrFactory === 'function' && this.isConstructor(implementationOrFactory) === false) {
if (
typeof implementationOrFactory === 'function' &&
this.isConstructor(implementationOrFactory) === false
) {
const descriptor: ServiceDescriptor<T> = {
serviceType,
implementationType: Object as unknown as Constructor<T>,
@@ -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<unknown> {
return typeof func === 'function' &&
!!func.prototype &&
func.prototype.constructor === func;
return typeof func === 'function' && !!func.prototype && func.prototype.constructor === func;
}
}
+15 -15
View File
@@ -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<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
*/
@@ -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<T>(serviceType: ServiceIdentifier<T>): 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
+49 -34
View File
@@ -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<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> = {
@@ -39,17 +39,17 @@ 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
*/
@@ -58,16 +58,16 @@ export type ServiceDescriptor<T = unknown> = {
/**
* 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>();
* ```
*/
@@ -81,67 +81,82 @@ export function createServiceIdentifier<T>(): ServiceIdentifier<T> {
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;
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;
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;
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;
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;
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;
@@ -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<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
*/
@@ -181,7 +196,7 @@ export interface IServiceScope {
* The service provider for this scope
*/
serviceProvider: IServiceProvider;
/**
* Disposes the scope and clears any scoped service instances
*/
+30 -28
View File
@@ -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);
});
+13 -13
View File
@@ -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<IService>();
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();