All checks were successful
CI / build-lint-test (pull_request) Successful in 34s
- @Service(uid) n'a plus de parametre generique : un decorateur de propriete legacy ne peut de toute facon pas changer le type du champ qu'il decore, seule son annotation compte. Le generique n'apportait rien. - Nouvelle constante exportee services = ServiceRepository.getInstance(), instance prete a l'emploi : services.get<T>(uid), services.has(uid), services.register(uid, Classe). C'est desormais le point d'entree recommande plutot que ServiceRepository.getInstance() directement. - ServiceRepository.resolve() renomme en get() pour matcher services.get<T>(uid). - resetForTesting() vide la map en place (Map.clear()) au lieu de jeter l'instance singleton, pour que la constante services capturee au chargement du module reste valide apres un reset dans les tests. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
99 lines
3.4 KiB
TypeScript
99 lines
3.4 KiB
TypeScript
import type { ServiceConstructor, ServiceUid } from './types';
|
|
|
|
const LOG_PREFIX = '[lightweight-sdk:service-registry]';
|
|
|
|
/**
|
|
* Central singleton registry that holds the single shared instance of every
|
|
* service registered via {@link RegisterService}, keyed by the uid the
|
|
* developer assigned at registration time. Prefer the ready-to-use
|
|
* {@link services} constant over calling {@link ServiceRepository.getInstance}
|
|
* directly.
|
|
*/
|
|
export class ServiceRepository {
|
|
private static instance: ServiceRepository | undefined;
|
|
|
|
private instances = new Map<ServiceUid, unknown>();
|
|
|
|
/** Private to enforce the singleton pattern — use {@link ServiceRepository.getInstance} or {@link services}. */
|
|
private constructor() {}
|
|
|
|
/**
|
|
* Returns the single shared instance of the repository, creating it on first access.
|
|
* @returns the singleton {@link ServiceRepository}
|
|
*/
|
|
public static getInstance(): ServiceRepository {
|
|
if (ServiceRepository.instance === undefined) {
|
|
ServiceRepository.instance = new ServiceRepository();
|
|
}
|
|
return ServiceRepository.instance;
|
|
}
|
|
|
|
/**
|
|
* Clears every registered service. The singleton instance itself is kept
|
|
* (not discarded), so the {@link services} constant stays valid across
|
|
* resets. Intended for resetting state between test cases — application
|
|
* code should not call this.
|
|
*/
|
|
public static resetForTesting(): void {
|
|
ServiceRepository.getInstance().instances.clear();
|
|
}
|
|
|
|
/**
|
|
* Instantiates `target` (its constructor must take no arguments) and
|
|
* registers the resulting instance under `uid`. Called by
|
|
* {@link RegisterService} — most callers should use that decorator
|
|
* instead of calling this method directly.
|
|
* @param uid the unique identifier the caller assigns to this service
|
|
* @param target the service class to instantiate and register
|
|
* @throws {Error} if `uid` is already registered
|
|
*/
|
|
public register<T>(uid: ServiceUid, target: ServiceConstructor<T>): void {
|
|
if (this.instances.has(uid)) {
|
|
throw new Error(`Service uid "${uid}" is already registered.`);
|
|
}
|
|
|
|
const instance = new target();
|
|
this.instances.set(uid, instance);
|
|
|
|
console.debug(`${LOG_PREFIX} Registered service "${uid}" (${target.name}).`);
|
|
}
|
|
|
|
/**
|
|
* Resolves the instance registered under `uid`.
|
|
* @param uid the service's unique identifier, as passed to {@link RegisterService}
|
|
* @returns the instance registered under `uid`
|
|
* @throws {Error} if no service is registered under `uid`
|
|
* @example
|
|
* ```ts
|
|
* const logger = services.get<LoggerService>(LOGGER_UID);
|
|
* ```
|
|
*/
|
|
public get<T>(uid: ServiceUid): T {
|
|
if (!this.instances.has(uid)) {
|
|
throw new Error(`No service registered with uid "${uid}". Did you forget @RegisterService?`);
|
|
}
|
|
return this.instances.get(uid) as T;
|
|
}
|
|
|
|
/**
|
|
* Checks whether a service is currently registered under `uid`.
|
|
* @param uid the unique identifier to check
|
|
* @returns `true` if a service is registered under `uid`
|
|
*/
|
|
public has(uid: ServiceUid): boolean {
|
|
return this.instances.has(uid);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Ready-to-use singleton instance of {@link ServiceRepository}. Import this
|
|
* wherever a service needs to be registered or resolved, instead of calling
|
|
* {@link ServiceRepository.getInstance} directly.
|
|
* @example
|
|
* ```ts
|
|
* import { services } from '@lightweight-sdk/service-registry';
|
|
*
|
|
* const logger = services.get<LoggerService>(LOGGER_UID);
|
|
* ```
|
|
*/
|
|
export const services = ServiceRepository.getInstance();
|