import { Binding , BindingFilter , BindingFromClassOptions , BindingTemplate , bindingTemplateFor , ContextTags , ContextView , createBindingFromClass , DecoratorFactory , inject , InjectionMetadata , isDynamicValueProviderClass , isProviderClass , MetadataInspector , transformValueOrPromise , } from '@loopback/context' ; import { ServiceOrProviderClass } from './application' ; import { CoreTags } from './keys' ; /**
 * Representing an interface for services. In TypeScript, the `interface` does
 * not have reflections at runtime. We use a string, a symbol or a Function as
 * the type for the service interface.
 */ export type ServiceInterface = string | symbol | Function ; /**
 * Options to register a service binding
 */ export interface ServiceOptions extends BindingFromClassOptions { interface ? : ServiceInterface ; } param serviceInterface example service export function service ( serviceInterface ? , metadata ? ) { return inject ( '' , { decorator : '@service' , ... metadata } , ( ctx , injection , session ) => { let serviceType = serviceInterface ; if ( ! serviceType ) { if ( typeof injection . methodDescriptorOrParameterIndex === 'number' ) { serviceType = MetadataInspector . getDesignTypeForMethod ( injection . target , injection . member ! ) ?. parameterTypes [ injection . methodDescriptorOrParameterIndex ] ; } else { serviceType = MetadataInspector . getDesignTypeForProperty ( injection . target , injection . member ! ) ; } } if ( serviceType === undefined ) { const targetName = DecoratorFactory . getTargetName ( injection . target , injection . member , injection . methodDescriptorOrParameterIndex ) ; const msg = `No design-time type metadata found while inspecting ${ targetName }. ` + 'You can either use `@service(ServiceClass)` or ensure `emitDecoratorMetadata` is enabled in your TypeScript configuration. ' + 'Run `tsc --showConfig` to print the final TypeScript configuration of your project.' ; throw new Error ( msg ) ; } if ( serviceType === Object || serviceType === Array ) { throw new Error ( 'Service class cannot be inferred from design type. Use @service(ServiceClass).' ) ; } const view = new ContextView ( ctx , filterByServiceInterface ( serviceType ) ) ; const result = view . resolve ( session ) ; const serviceTypeName = typeof serviceType === 'string' ? serviceType : typeof serviceType === 'symbol' ? serviceType . toString ( ) : serviceType . name ; return transformValueOrPromise ( result , values => { if ( values . length === 1 ) return values [ 0 ] ; if ( values . length >= 1 ) { throw new Error ( `More than one bindings found for ${ serviceTypeName }` ) ; } else { if ( metadata ?. optional ) { return undefined ; } throw new Error ( `No binding found for ${ serviceTypeName }. Make sure a service ` + `binding is created in context ${ ctx . name } with serviceInterface (${ serviceTypeName }).` ) ; } } ) ; } ) ; } param serviceInterface param options export function filterByServiceInterface ( serviceInterface ) { return binding => binding . valueConstructor === serviceInterface || binding . tagMap [ CoreTags . SERVICE_INTERFACE ] === serviceInterface ; } param cls param options export function createServiceBinding < S > ( cls , options = { } ) { let name = options . name ; if ( ! name && isProviderClass ( cls ) ) { const templateFn = bindingTemplateFor ( cls ) ; const template = Binding . bind < S > ( 'template' ) . apply ( templateFn ) ; if ( template . tagMap [ ContextTags . PROVIDER ] && ! template . tagMap [ ContextTags . NAME ] ) { name = cls . name . replace ( /Provider$/ , '' ) ; } } if ( ! name && isDynamicValueProviderClass ( cls ) ) { const templateFn = bindingTemplateFor ( cls ) ; const template = Binding . bind < S > ( 'template' ) . apply ( templateFn ) ; if ( template . tagMap [ ContextTags . DYNAMIC_VALUE_PROVIDER ] && ! template . tagMap [ ContextTags . NAME ] ) { name = cls . name . replace ( /Provider$/ , '' ) ; } } const binding = createBindingFromClass ( cls , { name , type : CoreTags . SERVICE , ... options , } ) . apply ( asService ( options . interface ?? cls ) ) ; return binding ; } param serviceInterface export function asService ( serviceInterface ) { return function serviceTemplate ( binding ) { binding . tag ( { [ ContextTags . TYPE ] : CoreTags . SERVICE , [ CoreTags . SERVICE_INTERFACE ] : serviceInterface , } ) ; } ; }