lightweight-sdk/packages/common/service-registry/src/service.decorator.ts
Nicolas 544614add7
All checks were successful
CI / build-lint-test (pull_request) Successful in 34s
Simplifier : retirer le generique de @Service, ajouter services
- @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>
2026-09-18 14:31:42 +02:00

61 lines
2 KiB
TypeScript
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { services } from './service-repository';
import type { ServiceUid } from './types';
const LOG_PREFIX = '[lightweight-sdk:service-registry]';
/**
* Property decorator that injects the instance registered under `uid`
* (via {@link RegisterService}) into the decorated class member. No generic
* type parameter is needed here: a legacy property decorator cannot change
* the type of the field it decorates, so the field's own type annotation
* (`private readonly logger!: LoggerService;`) is what types the member —
* annotate it as usual.
*
* Resolution is lazy and memoized: the service is looked up in
* {@link services} the first time the property is read, not at decoration
* time — this avoids depending on module evaluation order between a service
* and its consumers. After the first read, the property is replaced by the
* resolved value on the instance, so later accesses skip the repository
* lookup entirely.
* @param uid the unique identifier passed to {@link RegisterService} for the target service
* @returns a property decorator
* @example
* ```ts
* class ReportGenerator {
* ⁣@Service(LOGGER_UID)
* private readonly logger!: LoggerService;
*
* public run(): void {
* this.logger.log('generating report');
* }
* }
* ```
*/
export function Service(
uid: ServiceUid,
): (prototype: object, propertyKey: string | symbol) => void {
return (prototype: object, propertyKey: string | symbol): void => {
Object.defineProperty(prototype, propertyKey, {
configurable: true,
enumerable: true,
get(this: object): unknown {
let resolved: unknown;
try {
resolved = services.get(uid);
} catch (error) {
console.error(
`${LOG_PREFIX} Failed to inject service "${uid}" into member "${String(propertyKey)}".`,
error,
);
throw error;
}
Object.defineProperty(this, propertyKey, {
value: resolved,
enumerable: true,
configurable: true,
});
return resolved;
},
});
};
}