@loopback/core 2.4.1 → 2.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +43 -0
- package/dist/application.d.ts +16 -9
- package/dist/application.js +40 -19
- package/dist/application.js.map +1 -1
- package/dist/component.js +1 -0
- package/dist/component.js.map +1 -1
- package/dist/extension-point.d.ts +2 -2
- package/dist/extension-point.js +4 -3
- package/dist/extension-point.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/keys.d.ts +2 -2
- package/dist/keys.js +1 -0
- package/dist/keys.js.map +1 -1
- package/dist/lifecycle-registry.js +141 -137
- package/dist/lifecycle-registry.js.map +1 -1
- package/dist/lifecycle.d.ts +2 -2
- package/dist/lifecycle.js +1 -0
- package/dist/lifecycle.js.map +1 -1
- package/dist/mixin-target.d.ts +59 -0
- package/{index.d.ts → dist/mixin-target.js} +3 -2
- package/dist/mixin-target.js.map +1 -0
- package/dist/service.d.ts +4 -5
- package/dist/service.js +2 -0
- package/dist/service.js.map +1 -1
- package/package.json +10 -11
- package/src/application.ts +52 -20
- package/src/extension-point.ts +5 -3
- package/src/index.ts +1 -0
- package/src/keys.ts +6 -6
- package/src/lifecycle.ts +2 -1
- package/src/mixin-target.ts +70 -0
- package/src/service.ts +5 -4
- package/index.js +0 -6
package/src/application.ts
CHANGED
|
@@ -5,16 +5,20 @@
|
|
|
5
5
|
|
|
6
6
|
import {
|
|
7
7
|
Binding,
|
|
8
|
+
BindingFromClassOptions,
|
|
8
9
|
BindingScope,
|
|
9
10
|
Constructor,
|
|
10
11
|
Context,
|
|
11
12
|
createBindingFromClass,
|
|
13
|
+
Interceptor,
|
|
14
|
+
InterceptorBindingOptions,
|
|
12
15
|
JSONObject,
|
|
13
16
|
Provider,
|
|
17
|
+
registerInterceptor,
|
|
14
18
|
} from '@loopback/context';
|
|
15
19
|
import assert from 'assert';
|
|
16
20
|
import debugFactory from 'debug';
|
|
17
|
-
import
|
|
21
|
+
import {once} from 'events';
|
|
18
22
|
import {Component, mountComponent} from './component';
|
|
19
23
|
import {CoreBindings, CoreTags} from './keys';
|
|
20
24
|
import {
|
|
@@ -145,13 +149,16 @@ export class Application extends Context implements LifeCycleObserver {
|
|
|
145
149
|
* app.controller(MyController).lock();
|
|
146
150
|
* ```
|
|
147
151
|
*/
|
|
148
|
-
controller<T>(
|
|
149
|
-
|
|
152
|
+
controller<T>(
|
|
153
|
+
controllerCtor: ControllerClass<T>,
|
|
154
|
+
nameOrOptions?: string | BindingFromClassOptions,
|
|
155
|
+
): Binding<T> {
|
|
156
|
+
this.debug('Adding controller %s', nameOrOptions ?? controllerCtor.name);
|
|
150
157
|
const binding = createBindingFromClass(controllerCtor, {
|
|
151
|
-
name,
|
|
152
158
|
namespace: CoreBindings.CONTROLLERS,
|
|
153
159
|
type: CoreTags.CONTROLLER,
|
|
154
160
|
defaultScope: BindingScope.TRANSIENT,
|
|
161
|
+
...toOptions(nameOrOptions),
|
|
155
162
|
});
|
|
156
163
|
this.add(binding);
|
|
157
164
|
return binding;
|
|
@@ -171,20 +178,20 @@ export class Application extends Context implements LifeCycleObserver {
|
|
|
171
178
|
* ```
|
|
172
179
|
*
|
|
173
180
|
* @param server - The server constructor.
|
|
174
|
-
* @param
|
|
181
|
+
* @param nameOrOptions - Optional override for name or options.
|
|
175
182
|
* @returns Binding for the server class
|
|
176
183
|
*
|
|
177
184
|
*/
|
|
178
185
|
public server<T extends Server>(
|
|
179
186
|
ctor: Constructor<T>,
|
|
180
|
-
|
|
187
|
+
nameOrOptions?: string | BindingFromClassOptions,
|
|
181
188
|
): Binding<T> {
|
|
182
|
-
this.debug('Adding server %s',
|
|
189
|
+
this.debug('Adding server %s', nameOrOptions ?? ctor.name);
|
|
183
190
|
const binding = createBindingFromClass(ctor, {
|
|
184
|
-
name,
|
|
185
191
|
namespace: CoreBindings.SERVERS,
|
|
186
192
|
type: CoreTags.SERVER,
|
|
187
193
|
defaultScope: BindingScope.SINGLETON,
|
|
194
|
+
...toOptions(nameOrOptions),
|
|
188
195
|
}).apply(asLifeCycleObserver);
|
|
189
196
|
this.add(binding);
|
|
190
197
|
return binding;
|
|
@@ -278,7 +285,7 @@ export class Application extends Context implements LifeCycleObserver {
|
|
|
278
285
|
}
|
|
279
286
|
|
|
280
287
|
protected async awaitState(state: string) {
|
|
281
|
-
await
|
|
288
|
+
await once(this, state);
|
|
282
289
|
}
|
|
283
290
|
|
|
284
291
|
/**
|
|
@@ -332,7 +339,8 @@ export class Application extends Context implements LifeCycleObserver {
|
|
|
332
339
|
* controllers, providers, and servers from the component.
|
|
333
340
|
*
|
|
334
341
|
* @param componentCtor - The component class to add.
|
|
335
|
-
* @param
|
|
342
|
+
* @param nameOrOptions - Optional component name or options, default to the
|
|
343
|
+
* class name
|
|
336
344
|
*
|
|
337
345
|
* @example
|
|
338
346
|
* ```ts
|
|
@@ -351,14 +359,14 @@ export class Application extends Context implements LifeCycleObserver {
|
|
|
351
359
|
*/
|
|
352
360
|
public component<T extends Component = Component>(
|
|
353
361
|
componentCtor: Constructor<T>,
|
|
354
|
-
|
|
362
|
+
nameOrOptions?: string | BindingFromClassOptions,
|
|
355
363
|
) {
|
|
356
|
-
this.debug('Adding component: %s',
|
|
364
|
+
this.debug('Adding component: %s', nameOrOptions ?? componentCtor.name);
|
|
357
365
|
const binding = createBindingFromClass(componentCtor, {
|
|
358
|
-
name,
|
|
359
366
|
namespace: CoreBindings.COMPONENTS,
|
|
360
367
|
type: CoreTags.COMPONENT,
|
|
361
368
|
defaultScope: BindingScope.SINGLETON,
|
|
369
|
+
...toOptions(nameOrOptions),
|
|
362
370
|
});
|
|
363
371
|
if (isLifeCycleObserverClass(componentCtor)) {
|
|
364
372
|
binding.apply(asLifeCycleObserver);
|
|
@@ -383,18 +391,18 @@ export class Application extends Context implements LifeCycleObserver {
|
|
|
383
391
|
/**
|
|
384
392
|
* Register a life cycle observer class
|
|
385
393
|
* @param ctor - A class implements LifeCycleObserver
|
|
386
|
-
* @param
|
|
394
|
+
* @param nameOrOptions - Optional name or options for the life cycle observer
|
|
387
395
|
*/
|
|
388
396
|
public lifeCycleObserver<T extends LifeCycleObserver>(
|
|
389
397
|
ctor: Constructor<T>,
|
|
390
|
-
|
|
398
|
+
nameOrOptions?: string | BindingFromClassOptions,
|
|
391
399
|
): Binding<T> {
|
|
392
|
-
this.debug('Adding life cycle observer %s',
|
|
400
|
+
this.debug('Adding life cycle observer %s', nameOrOptions ?? ctor.name);
|
|
393
401
|
const binding = createBindingFromClass(ctor, {
|
|
394
|
-
name,
|
|
395
402
|
namespace: CoreBindings.LIFE_CYCLE_OBSERVERS,
|
|
396
403
|
type: CoreTags.LIFE_CYCLE_OBSERVER,
|
|
397
404
|
defaultScope: BindingScope.SINGLETON,
|
|
405
|
+
...toOptions(nameOrOptions),
|
|
398
406
|
}).apply(asLifeCycleObserver);
|
|
399
407
|
this.add(binding);
|
|
400
408
|
return binding;
|
|
@@ -443,15 +451,28 @@ export class Application extends Context implements LifeCycleObserver {
|
|
|
443
451
|
* ```
|
|
444
452
|
*/
|
|
445
453
|
public service<S>(
|
|
446
|
-
cls: Constructor<S
|
|
447
|
-
|
|
454
|
+
cls: Constructor<S | Provider<S>>,
|
|
455
|
+
nameOrOptions?: string | ServiceOptions,
|
|
448
456
|
): Binding<S> {
|
|
449
|
-
const options =
|
|
457
|
+
const options = toOptions(nameOrOptions);
|
|
450
458
|
const binding = createServiceBinding(cls, options);
|
|
451
459
|
this.add(binding);
|
|
452
460
|
return binding;
|
|
453
461
|
}
|
|
454
462
|
|
|
463
|
+
/**
|
|
464
|
+
* Register an interceptor
|
|
465
|
+
* @param interceptor - An interceptor function or provider class
|
|
466
|
+
* @param nameOrOptions - Binding name or options
|
|
467
|
+
*/
|
|
468
|
+
public interceptor(
|
|
469
|
+
interceptor: Interceptor | Constructor<Provider<Interceptor>>,
|
|
470
|
+
nameOrOptions?: string | InterceptorBindingOptions,
|
|
471
|
+
) {
|
|
472
|
+
const options = toOptions(nameOrOptions);
|
|
473
|
+
return registerInterceptor(this, interceptor, options);
|
|
474
|
+
}
|
|
475
|
+
|
|
455
476
|
/**
|
|
456
477
|
* Set up signals that are captured to shutdown the application
|
|
457
478
|
*/
|
|
@@ -532,6 +553,17 @@ export class Application extends Context implements LifeCycleObserver {
|
|
|
532
553
|
}
|
|
533
554
|
}
|
|
534
555
|
|
|
556
|
+
/**
|
|
557
|
+
* Normalize name or options to `BindingFromClassOptions`
|
|
558
|
+
* @param nameOrOptions - Name or options for binding from class
|
|
559
|
+
*/
|
|
560
|
+
function toOptions(nameOrOptions?: string | BindingFromClassOptions) {
|
|
561
|
+
if (typeof nameOrOptions === 'string') {
|
|
562
|
+
return {name: nameOrOptions};
|
|
563
|
+
}
|
|
564
|
+
return nameOrOptions ?? {};
|
|
565
|
+
}
|
|
566
|
+
|
|
535
567
|
/**
|
|
536
568
|
* Options to set up application shutdown
|
|
537
569
|
*/
|
package/src/extension-point.ts
CHANGED
|
@@ -227,11 +227,13 @@ function inferExtensionPointName(
|
|
|
227
227
|
/**
|
|
228
228
|
* A factory function to create binding filter for extensions of a named
|
|
229
229
|
* extension point
|
|
230
|
-
* @param
|
|
230
|
+
* @param extensionPointNames - A list of names of extension points
|
|
231
231
|
*/
|
|
232
|
-
export function extensionFilter(
|
|
232
|
+
export function extensionFilter(
|
|
233
|
+
...extensionPointNames: string[]
|
|
234
|
+
): BindingFilter {
|
|
233
235
|
return filterByTag({
|
|
234
|
-
[CoreTags.EXTENSION_FOR]: includesTagValue(
|
|
236
|
+
[CoreTags.EXTENSION_FOR]: includesTagValue(...extensionPointNames),
|
|
235
237
|
});
|
|
236
238
|
}
|
|
237
239
|
|
package/src/index.ts
CHANGED
package/src/keys.ts
CHANGED
|
@@ -30,9 +30,9 @@ export namespace CoreBindings {
|
|
|
30
30
|
/**
|
|
31
31
|
* Binding key for application configuration
|
|
32
32
|
*/
|
|
33
|
-
export const APPLICATION_CONFIG = BindingKey.create<
|
|
34
|
-
|
|
35
|
-
);
|
|
33
|
+
export const APPLICATION_CONFIG: BindingKey<ApplicationConfig> = BindingKey.create<
|
|
34
|
+
ApplicationConfig
|
|
35
|
+
>('application.config');
|
|
36
36
|
|
|
37
37
|
/**
|
|
38
38
|
* Binding key for the content of `package.json`
|
|
@@ -60,9 +60,9 @@ export namespace CoreBindings {
|
|
|
60
60
|
* Binding key for the controller class resolved in the current request
|
|
61
61
|
* context
|
|
62
62
|
*/
|
|
63
|
-
export const CONTROLLER_CLASS = BindingKey.create<
|
|
64
|
-
|
|
65
|
-
);
|
|
63
|
+
export const CONTROLLER_CLASS: BindingKey<ControllerClass> = BindingKey.create<
|
|
64
|
+
ControllerClass
|
|
65
|
+
>('controller.current.ctor');
|
|
66
66
|
|
|
67
67
|
/**
|
|
68
68
|
* Binding key for the controller method resolved in the current request
|
package/src/lifecycle.ts
CHANGED
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
bind,
|
|
8
8
|
Binding,
|
|
9
9
|
BindingSpec,
|
|
10
|
+
BindingTagFilter,
|
|
10
11
|
Constructor,
|
|
11
12
|
filterByTag,
|
|
12
13
|
ValueOrPromise,
|
|
@@ -62,7 +63,7 @@ export function asLifeCycleObserver<T = unknown>(binding: Binding<T>) {
|
|
|
62
63
|
* Find all life cycle observer bindings. By default, a binding tagged with
|
|
63
64
|
* `CoreTags.LIFE_CYCLE_OBSERVER`. It's used as `BindingFilter`.
|
|
64
65
|
*/
|
|
65
|
-
export const lifeCycleObserverFilter = filterByTag(
|
|
66
|
+
export const lifeCycleObserverFilter: BindingTagFilter = filterByTag(
|
|
66
67
|
CoreTags.LIFE_CYCLE_OBSERVER,
|
|
67
68
|
);
|
|
68
69
|
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
// Copyright IBM Corp. 2017,2020. All Rights Reserved.
|
|
2
|
+
// Node module: @loopback/core
|
|
3
|
+
// This file is licensed under the MIT License.
|
|
4
|
+
// License text available at https://opensource.org/licenses/MIT
|
|
5
|
+
|
|
6
|
+
import {Constructor} from '@loopback/context';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* A replacement for `typeof Target` to be used in mixin class definitions.
|
|
10
|
+
* This is a workaround for TypeScript limitation described in
|
|
11
|
+
* - https://github.com/microsoft/TypeScript/issues/17293
|
|
12
|
+
* - https://github.com/microsoft/TypeScript/issues/17744
|
|
13
|
+
* - https://github.com/microsoft/TypeScript/issues/36060
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
*
|
|
17
|
+
* ```ts
|
|
18
|
+
* export function MyMixin<T extends MixinTarget<Application>>(superClass: T) {
|
|
19
|
+
* return class extends superClass {
|
|
20
|
+
* // contribute new class members
|
|
21
|
+
* }
|
|
22
|
+
* };
|
|
23
|
+
* ```
|
|
24
|
+
*
|
|
25
|
+
* TypeScript does not allow class mixins to access protected members from
|
|
26
|
+
* the base class. You can use the following approach as a workaround:
|
|
27
|
+
*
|
|
28
|
+
* ```ts
|
|
29
|
+
* // @ts-ignore
|
|
30
|
+
* (this as unknown as {YourBaseClass}).protectedMember
|
|
31
|
+
* ```
|
|
32
|
+
*
|
|
33
|
+
* The directive `@ts-ignore` suppresses compiler error about accessing
|
|
34
|
+
* a protected member from outside. Unfortunately, it also disables other
|
|
35
|
+
* compile-time checks (e.g. to verify that a protected method was invoked
|
|
36
|
+
* with correct arguments, and so on). This is the same behavior you
|
|
37
|
+
* would get by using `Constructor<any>` instead of `MixinTarget<Application>`.
|
|
38
|
+
* The major improvement is that TypeScript can still infer the return
|
|
39
|
+
* type of the protected member, therefore `any` is NOT introduced to subsequent
|
|
40
|
+
* code.
|
|
41
|
+
*
|
|
42
|
+
* TypeScript also does not allow mixin class to overwrite a method inherited
|
|
43
|
+
* from a mapped type, see https://github.com/microsoft/TypeScript/issues/38496
|
|
44
|
+
* As a workaround, use `@ts-ignore` to disable the error.
|
|
45
|
+
*
|
|
46
|
+
* ```ts
|
|
47
|
+
* export function RepositoryMixin<T extends MixinTarget<Application>>(
|
|
48
|
+
* superClass: T,
|
|
49
|
+
* ) {
|
|
50
|
+
* return class extends superClass {
|
|
51
|
+
* // @ts-ignore
|
|
52
|
+
* public component<C extends Component = Component>(
|
|
53
|
+
* componentCtor: Constructor<C>,
|
|
54
|
+
* nameOrOptions?: string | BindingFromClassOptions,
|
|
55
|
+
* ) {
|
|
56
|
+
* const binding = super.component(componentCtor, nameOrOptions);
|
|
57
|
+
* // ...
|
|
58
|
+
* return binding;
|
|
59
|
+
* }
|
|
60
|
+
* }
|
|
61
|
+
* ```
|
|
62
|
+
*/
|
|
63
|
+
export type MixinTarget<T extends object> = Constructor<
|
|
64
|
+
{
|
|
65
|
+
// Enumerate only public members to avoid the following compiler error:
|
|
66
|
+
// Property '(name)' of exported class expression
|
|
67
|
+
// may not be private or protected.ts(4094)
|
|
68
|
+
[P in keyof T]: T[P];
|
|
69
|
+
}
|
|
70
|
+
>;
|
package/src/service.ts
CHANGED
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
import {
|
|
7
7
|
Binding,
|
|
8
8
|
BindingFilter,
|
|
9
|
+
BindingFromClassOptions,
|
|
9
10
|
BindingTemplate,
|
|
10
11
|
bindingTemplateFor,
|
|
11
12
|
Constructor,
|
|
@@ -31,10 +32,9 @@ export type ServiceInterface = string | symbol | Function;
|
|
|
31
32
|
/**
|
|
32
33
|
* Options to register a service binding
|
|
33
34
|
*/
|
|
34
|
-
export
|
|
35
|
-
name?: string;
|
|
35
|
+
export interface ServiceOptions extends BindingFromClassOptions {
|
|
36
36
|
interface?: ServiceInterface;
|
|
37
|
-
}
|
|
37
|
+
}
|
|
38
38
|
|
|
39
39
|
/**
|
|
40
40
|
* `@service` injects a service instance that matches the class or interface.
|
|
@@ -142,7 +142,7 @@ export function filterByServiceInterface(
|
|
|
142
142
|
* @param options - Service options
|
|
143
143
|
*/
|
|
144
144
|
export function createServiceBinding<S>(
|
|
145
|
-
cls: Constructor<S
|
|
145
|
+
cls: Constructor<S | Provider<S>>,
|
|
146
146
|
options: ServiceOptions = {},
|
|
147
147
|
): Binding<S> {
|
|
148
148
|
let name = options.name;
|
|
@@ -162,6 +162,7 @@ export function createServiceBinding<S>(
|
|
|
162
162
|
const binding = createBindingFromClass(cls, {
|
|
163
163
|
name,
|
|
164
164
|
type: CoreTags.SERVICE,
|
|
165
|
+
...options,
|
|
165
166
|
}).apply(asService(options.interface ?? cls));
|
|
166
167
|
return binding;
|
|
167
168
|
}
|