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>
61 lines
2 KiB
TypeScript
61 lines
2 KiB
TypeScript
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;
|
||
},
|
||
});
|
||
};
|
||
}
|