@loopback/core
Advanced tools
Comparing version 2.9.5 to 2.10.0
@@ -6,2 +6,19 @@ # Change Log | ||
# [2.10.0](https://github.com/strongloop/loopback-next/compare/@loopback/core@2.9.5...@loopback/core@2.10.0) (2020-09-15) | ||
### Bug Fixes | ||
* improve handling of missing design-time type metadata ([95b6a2b](https://github.com/strongloop/loopback-next/commit/95b6a2b7ce64e614720df43b905f77a53a54e438)) | ||
### Features | ||
* add `app.onStart()` and `app.onStop()` helpers ([92daddd](https://github.com/strongloop/loopback-next/commit/92daddd8dfaf24c16e03ed3af66d491a8fd9503e)) | ||
* allow dynamic value provider classes and classes with [@inject](https://github.com/inject) to be booted ([7b85cdf](https://github.com/strongloop/loopback-next/commit/7b85cdf63730ef659a4ee799f05f02eea8a1e3e8)) | ||
## [2.9.5](https://github.com/strongloop/loopback-next/compare/@loopback/core@2.9.4...@loopback/core@2.9.5) (2020-08-27) | ||
@@ -8,0 +25,0 @@ |
/// <reference types="node" /> | ||
import { Binding, BindingFromClassOptions, Constructor, Context, Interceptor, InterceptorBindingOptions, JSONObject, Provider } from '@loopback/context'; | ||
import { Binding, BindingFromClassOptions, Constructor, Context, DynamicValueProviderClass, Interceptor, InterceptorBindingOptions, JSONObject, Provider, ValueOrPromise } from '@loopback/context'; | ||
import { Component } from './component'; | ||
@@ -152,2 +152,13 @@ import { LifeCycleObserver } from './lifecycle'; | ||
/** | ||
* Register a function to be called when the application starts. | ||
* | ||
* This is a shortcut for adding a binding for a LifeCycleObserver | ||
* implementing a `start()` method. | ||
* | ||
* @param fn The function to invoke, it can be synchronous (returning `void`) | ||
* or asynchronous (returning `Promise<void>`). | ||
* @returns The LifeCycleObserver binding created. | ||
*/ | ||
onStart(fn: () => ValueOrPromise<void>): Binding<LifeCycleObserver>; | ||
/** | ||
* Stop the application instance and all of its registered observers. The | ||
@@ -160,2 +171,13 @@ * application state is checked to ensure the integrity of `stop`. | ||
stop(): Promise<void>; | ||
/** | ||
* Register a function to be called when the application starts. | ||
* | ||
* This is a shortcut for adding a binding for a LifeCycleObserver | ||
* implementing a `start()` method. | ||
* | ||
* @param fn The function to invoke, it can be synchronous (returning `void`) | ||
* or asynchronous (returning `Promise<void>`). | ||
* @returns The LifeCycleObserver binding created. | ||
*/ | ||
onStop(fn: () => ValueOrPromise<void>): Binding<LifeCycleObserver>; | ||
private getLifeCycleObserverRegistry; | ||
@@ -208,3 +230,3 @@ /** | ||
* // Define a class to be bound via ctx.toClass() | ||
* @bind({scope: BindingScope.SINGLETON}) | ||
* @injectable({scope: BindingScope.SINGLETON}) | ||
* export class LogService { | ||
@@ -288,3 +310,3 @@ * log(msg: string) { | ||
export declare type ControllerClass<T = any> = Constructor<T>; | ||
export declare type ServiceOrProviderClass<T = any> = Constructor<T | Provider<T>>; | ||
export declare type ServiceOrProviderClass<T = any> = Constructor<T | Provider<T>> | DynamicValueProviderClass<T>; | ||
/** | ||
@@ -291,0 +313,0 @@ * Type description for `package.json` |
@@ -10,2 +10,3 @@ "use strict"; | ||
const context_1 = require("@loopback/context"); | ||
const unique_id_1 = require("@loopback/context/dist/unique-id"); | ||
const assert_1 = tslib_1.__importDefault(require("assert")); | ||
@@ -250,2 +251,22 @@ const debug_1 = tslib_1.__importDefault(require("debug")); | ||
/** | ||
* Register a function to be called when the application starts. | ||
* | ||
* This is a shortcut for adding a binding for a LifeCycleObserver | ||
* implementing a `start()` method. | ||
* | ||
* @param fn The function to invoke, it can be synchronous (returning `void`) | ||
* or asynchronous (returning `Promise<void>`). | ||
* @returns The LifeCycleObserver binding created. | ||
*/ | ||
onStart(fn) { | ||
const key = [ | ||
keys_1.CoreBindings.LIFE_CYCLE_OBSERVERS, | ||
fn.name || '<onStart>', | ||
unique_id_1.generateUniqueId(), | ||
].join('.'); | ||
return this.bind(key) | ||
.to({ start: fn }) | ||
.apply(lifecycle_1.asLifeCycleObserver); | ||
} | ||
/** | ||
* Stop the application instance and all of its registered observers. The | ||
@@ -274,2 +295,22 @@ * application state is checked to ensure the integrity of `stop`. | ||
} | ||
/** | ||
* Register a function to be called when the application starts. | ||
* | ||
* This is a shortcut for adding a binding for a LifeCycleObserver | ||
* implementing a `start()` method. | ||
* | ||
* @param fn The function to invoke, it can be synchronous (returning `void`) | ||
* or asynchronous (returning `Promise<void>`). | ||
* @returns The LifeCycleObserver binding created. | ||
*/ | ||
onStop(fn) { | ||
const key = [ | ||
keys_1.CoreBindings.LIFE_CYCLE_OBSERVERS, | ||
fn.name || '<onStop>', | ||
unique_id_1.generateUniqueId(), | ||
].join('.'); | ||
return this.bind(key) | ||
.to({ stop: fn }) | ||
.apply(lifecycle_1.asLifeCycleObserver); | ||
} | ||
async getLifeCycleObserverRegistry() { | ||
@@ -352,3 +393,3 @@ return this.get(keys_1.CoreBindings.LIFE_CYCLE_OBSERVER_REGISTRY); | ||
* // Define a class to be bound via ctx.toClass() | ||
* @bind({scope: BindingScope.SINGLETON}) | ||
* @injectable({scope: BindingScope.SINGLETON}) | ||
* export class LogService { | ||
@@ -355,0 +396,0 @@ * log(msg: string) { |
@@ -18,3 +18,3 @@ import { Binding, BoundValue, Constructor, Provider } from '@loopback/context'; | ||
/** | ||
* A component declares a set of artifacts so that they cane be contributed to | ||
* A component declares a set of artifacts so that they can be contributed to | ||
* an application as a group | ||
@@ -21,0 +21,0 @@ */ |
@@ -27,3 +27,3 @@ "use strict"; | ||
function extensionPoint(name, ...specs) { | ||
return context_1.bind({ tags: { [keys_1.CoreTags.EXTENSION_POINT]: name } }, ...specs); | ||
return context_1.injectable({ tags: { [keys_1.CoreTags.EXTENSION_POINT]: name } }, ...specs); | ||
} | ||
@@ -30,0 +30,0 @@ exports.extensionPoint = extensionPoint; |
@@ -49,3 +49,3 @@ "use strict"; | ||
function lifeCycleObserver(group = '', ...specs) { | ||
return context_1.bind(asLifeCycleObserver, { | ||
return context_1.injectable(asLifeCycleObserver, { | ||
tags: { | ||
@@ -52,0 +52,0 @@ [keys_1.CoreTags.LIFE_CYCLE_OBSERVER_GROUP]: group, |
@@ -1,2 +0,3 @@ | ||
import { Binding, BindingFilter, BindingFromClassOptions, BindingTemplate, Constructor, InjectionMetadata, Provider } from '@loopback/context'; | ||
import { Binding, BindingFilter, BindingFromClassOptions, BindingTemplate, InjectionMetadata } from '@loopback/context'; | ||
import { ServiceOrProviderClass } from './application'; | ||
/** | ||
@@ -57,3 +58,3 @@ * Representing an interface for services. In TypeScript, the `interface` does | ||
*/ | ||
export declare function createServiceBinding<S>(cls: Constructor<S | Provider<S>>, options?: ServiceOptions): Binding<S>; | ||
export declare function createServiceBinding<S>(cls: ServiceOrProviderClass<S>, options?: ServiceOptions): Binding<S>; | ||
/** | ||
@@ -60,0 +61,0 @@ * Create a binding template for a service interface |
@@ -43,6 +43,7 @@ "use strict"; | ||
return context_1.inject('', { decorator: '@service', ...metadata }, (ctx, injection, session) => { | ||
var _a; | ||
let serviceType = serviceInterface; | ||
if (!serviceType) { | ||
if (typeof injection.methodDescriptorOrParameterIndex === 'number') { | ||
serviceType = context_1.MetadataInspector.getDesignTypeForMethod(injection.target, injection.member).parameterTypes[injection.methodDescriptorOrParameterIndex]; | ||
serviceType = (_a = context_1.MetadataInspector.getDesignTypeForMethod(injection.target, injection.member)) === null || _a === void 0 ? void 0 : _a.parameterTypes[injection.methodDescriptorOrParameterIndex]; | ||
} | ||
@@ -53,2 +54,9 @@ else { | ||
} | ||
if (serviceType === undefined) { | ||
const targetName = context_1.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) { | ||
@@ -110,2 +118,12 @@ throw new Error('Service class cannot be inferred from design type. Use @service(ServiceClass).'); | ||
} | ||
if (!name && context_1.isDynamicValueProviderClass(cls)) { | ||
// Trim `Provider` from the default service name | ||
const templateFn = context_1.bindingTemplateFor(cls); | ||
const template = context_1.Binding.bind('template').apply(templateFn); | ||
if (template.tagMap[context_1.ContextTags.DYNAMIC_VALUE_PROVIDER] && | ||
!template.tagMap[context_1.ContextTags.NAME]) { | ||
// The class is a provider and no `name` tag is found | ||
name = cls.name.replace(/Provider$/, ''); | ||
} | ||
} | ||
const binding = context_1.createBindingFromClass(cls, { | ||
@@ -112,0 +130,0 @@ name, |
{ | ||
"name": "@loopback/core", | ||
"version": "2.9.5", | ||
"version": "2.10.0", | ||
"description": "Define and implement core constructs such as Application and Component", | ||
@@ -27,3 +27,3 @@ "main": "dist/index.js", | ||
"dependencies": { | ||
"@loopback/context": "^3.10.1", | ||
"@loopback/context": "^3.11.0", | ||
"debug": "^4.1.1", | ||
@@ -33,7 +33,7 @@ "tslib": "^2.0.1" | ||
"devDependencies": { | ||
"@loopback/build": "^6.2.2", | ||
"@loopback/eslint-config": "^9.0.2", | ||
"@loopback/testlab": "^3.2.4", | ||
"@loopback/build": "^6.2.3", | ||
"@loopback/eslint-config": "^10.0.0", | ||
"@loopback/testlab": "^3.2.5", | ||
"@types/debug": "^4.1.5", | ||
"@types/node": "^10.17.28" | ||
"@types/node": "^10.17.34" | ||
}, | ||
@@ -51,3 +51,3 @@ "files": [ | ||
}, | ||
"gitHead": "a3f54273814de63819e0d8bc86509f8a737800bb" | ||
"gitHead": "2b7d2ef44be0e3c19aee3316a2776d7fff6b0051" | ||
} |
@@ -13,2 +13,3 @@ // Copyright IBM Corp. 2017,2020. All Rights Reserved. | ||
createBindingFromClass, | ||
DynamicValueProviderClass, | ||
Interceptor, | ||
@@ -19,3 +20,5 @@ InterceptorBindingOptions, | ||
registerInterceptor, | ||
ValueOrPromise, | ||
} from '@loopback/context'; | ||
import {generateUniqueId} from '@loopback/context/dist/unique-id'; | ||
import assert from 'assert'; | ||
@@ -318,2 +321,24 @@ import debugFactory from 'debug'; | ||
/** | ||
* Register a function to be called when the application starts. | ||
* | ||
* This is a shortcut for adding a binding for a LifeCycleObserver | ||
* implementing a `start()` method. | ||
* | ||
* @param fn The function to invoke, it can be synchronous (returning `void`) | ||
* or asynchronous (returning `Promise<void>`). | ||
* @returns The LifeCycleObserver binding created. | ||
*/ | ||
public onStart(fn: () => ValueOrPromise<void>): Binding<LifeCycleObserver> { | ||
const key = [ | ||
CoreBindings.LIFE_CYCLE_OBSERVERS, | ||
fn.name || '<onStart>', | ||
generateUniqueId(), | ||
].join('.'); | ||
return this.bind<LifeCycleObserver>(key) | ||
.to({start: fn}) | ||
.apply(asLifeCycleObserver); | ||
} | ||
/** | ||
* Stop the application instance and all of its registered observers. The | ||
@@ -341,2 +366,23 @@ * application state is checked to ensure the integrity of `stop`. | ||
/** | ||
* Register a function to be called when the application starts. | ||
* | ||
* This is a shortcut for adding a binding for a LifeCycleObserver | ||
* implementing a `start()` method. | ||
* | ||
* @param fn The function to invoke, it can be synchronous (returning `void`) | ||
* or asynchronous (returning `Promise<void>`). | ||
* @returns The LifeCycleObserver binding created. | ||
*/ | ||
public onStop(fn: () => ValueOrPromise<void>): Binding<LifeCycleObserver> { | ||
const key = [ | ||
CoreBindings.LIFE_CYCLE_OBSERVERS, | ||
fn.name || '<onStop>', | ||
generateUniqueId(), | ||
].join('.'); | ||
return this.bind<LifeCycleObserver>(key) | ||
.to({stop: fn}) | ||
.apply(asLifeCycleObserver); | ||
} | ||
private async getLifeCycleObserverRegistry() { | ||
@@ -429,3 +475,3 @@ return this.get(CoreBindings.LIFE_CYCLE_OBSERVER_REGISTRY); | ||
* // Define a class to be bound via ctx.toClass() | ||
* @bind({scope: BindingScope.SINGLETON}) | ||
* @injectable({scope: BindingScope.SINGLETON}) | ||
* export class LogService { | ||
@@ -616,3 +662,5 @@ * log(msg: string) { | ||
// eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
export type ServiceOrProviderClass<T = any> = Constructor<T | Provider<T>>; | ||
export type ServiceOrProviderClass<T = any> = | ||
| Constructor<T | Provider<T>> | ||
| DynamicValueProviderClass<T>; | ||
@@ -619,0 +667,0 @@ /** |
@@ -36,3 +36,3 @@ // Copyright IBM Corp. 2017,2019. All Rights Reserved. | ||
/** | ||
* A component declares a set of artifacts so that they cane be contributed to | ||
* A component declares a set of artifacts so that they can be contributed to | ||
* an application as a group | ||
@@ -39,0 +39,0 @@ */ |
@@ -8,3 +8,2 @@ // Copyright IBM Corp. 2017,2020. All Rights Reserved. | ||
assertTargetType, | ||
bind, | ||
Binding, | ||
@@ -24,2 +23,3 @@ BindingFilter, | ||
inject, | ||
injectable, | ||
Injection, | ||
@@ -47,3 +47,3 @@ ResolutionSession, | ||
export function extensionPoint(name: string, ...specs: BindingSpec[]) { | ||
return bind({tags: {[CoreTags.EXTENSION_POINT]: name}}, ...specs); | ||
return injectable({tags: {[CoreTags.EXTENSION_POINT]: name}}, ...specs); | ||
} | ||
@@ -50,0 +50,0 @@ |
@@ -7,3 +7,2 @@ // Copyright IBM Corp. 2018,2020. All Rights Reserved. | ||
import { | ||
bind, | ||
Binding, | ||
@@ -14,2 +13,3 @@ BindingSpec, | ||
filterByTag, | ||
injectable, | ||
ValueOrPromise, | ||
@@ -78,3 +78,3 @@ } from '@loopback/context'; | ||
export function lifeCycleObserver(group = '', ...specs: BindingSpec[]) { | ||
return bind( | ||
return injectable( | ||
asLifeCycleObserver, | ||
@@ -81,0 +81,0 @@ { |
@@ -12,13 +12,14 @@ // Copyright IBM Corp. 2019,2020. All Rights Reserved. | ||
bindingTemplateFor, | ||
Constructor, | ||
ContextTags, | ||
ContextView, | ||
createBindingFromClass, | ||
DecoratorFactory, | ||
inject, | ||
InjectionMetadata, | ||
isDynamicValueProviderClass, | ||
isProviderClass, | ||
MetadataInspector, | ||
Provider, | ||
transformValueOrPromise, | ||
} from '@loopback/context'; | ||
import {ServiceOrProviderClass} from './application'; | ||
import {CoreTags} from './keys'; | ||
@@ -85,3 +86,3 @@ | ||
injection.member!, | ||
).parameterTypes[injection.methodDescriptorOrParameterIndex]; | ||
)?.parameterTypes[injection.methodDescriptorOrParameterIndex]; | ||
} else { | ||
@@ -94,2 +95,15 @@ serviceType = MetadataInspector.getDesignTypeForProperty( | ||
} | ||
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) { | ||
@@ -148,3 +162,3 @@ throw new Error( | ||
export function createServiceBinding<S>( | ||
cls: Constructor<S | Provider<S>>, | ||
cls: ServiceOrProviderClass<S>, | ||
options: ServiceOptions = {}, | ||
@@ -166,2 +180,14 @@ ): Binding<S> { | ||
} | ||
if (!name && isDynamicValueProviderClass(cls)) { | ||
// Trim `Provider` from the default service name | ||
const templateFn = bindingTemplateFor(cls); | ||
const template = Binding.bind<S>('template').apply(templateFn); | ||
if ( | ||
template.tagMap[ContextTags.DYNAMIC_VALUE_PROVIDER] && | ||
!template.tagMap[ContextTags.NAME] | ||
) { | ||
// The class is a provider and no `name` tag is found | ||
name = cls.name.replace(/Provider$/, ''); | ||
} | ||
} | ||
const binding = createBindingFromClass(cls, { | ||
@@ -168,0 +194,0 @@ name, |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
203585
3971
Updated@loopback/context@^3.11.0