@loopback/core 4.0.0-alpha.8 → 4.0.1

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.
Files changed (179) hide show
  1. package/LICENSE +25 -0
  2. package/README.md +77 -2
  3. package/dist/application.d.ts +341 -0
  4. package/dist/application.js +554 -0
  5. package/dist/application.js.map +1 -0
  6. package/dist/component.d.ts +80 -0
  7. package/dist/component.js +59 -0
  8. package/dist/component.js.map +1 -0
  9. package/dist/extension-point.d.ts +121 -0
  10. package/dist/extension-point.js +227 -0
  11. package/dist/extension-point.js.map +1 -0
  12. package/dist/index.d.ts +21 -0
  13. package/dist/index.js +31 -0
  14. package/dist/index.js.map +1 -0
  15. package/dist/keys.d.ts +97 -0
  16. package/dist/keys.js +109 -0
  17. package/dist/keys.js.map +1 -0
  18. package/dist/lifecycle-registry.d.ts +91 -0
  19. package/dist/lifecycle-registry.js +191 -0
  20. package/dist/lifecycle-registry.js.map +1 -0
  21. package/dist/lifecycle.d.ts +47 -0
  22. package/dist/lifecycle.js +56 -0
  23. package/dist/lifecycle.js.map +1 -0
  24. package/dist/mixin-target.d.ts +60 -0
  25. package/{lib/internal-types.js → dist/mixin-target.js} +2 -3
  26. package/dist/mixin-target.js.map +1 -0
  27. package/dist/server.d.ts +16 -0
  28. package/{lib6/src/Component.js → dist/server.js} +2 -1
  29. package/dist/server.js.map +1 -0
  30. package/dist/service.d.ts +63 -0
  31. package/dist/service.js +151 -0
  32. package/dist/service.js.map +1 -0
  33. package/package.json +39 -37
  34. package/src/application.ts +719 -0
  35. package/src/component.ts +155 -0
  36. package/src/extension-point.ts +312 -0
  37. package/src/index.ts +29 -0
  38. package/src/keys.ts +144 -0
  39. package/src/lifecycle-registry.ts +268 -0
  40. package/src/lifecycle.ts +90 -0
  41. package/src/mixin-target.ts +69 -0
  42. package/src/server.ts +22 -0
  43. package/src/service.ts +211 -0
  44. package/index.d.ts +0 -6
  45. package/index.js +0 -9
  46. package/lib/Component.d.ts +0 -2
  47. package/lib/Component.js +0 -7
  48. package/lib/Sequence.d.ts +0 -14
  49. package/lib/Sequence.js +0 -58
  50. package/lib/application.d.ts +0 -52
  51. package/lib/application.js +0 -79
  52. package/lib/application.js.map +0 -1
  53. package/lib/component.js.map +0 -1
  54. package/lib/handlers/http.d.ts +0 -3
  55. package/lib/handlers/http.js +0 -13
  56. package/lib/handlers/http.js.map +0 -1
  57. package/lib/handlers/index.d.ts +0 -1
  58. package/lib/handlers/index.js +0 -11
  59. package/lib/handlers/index.js.map +0 -1
  60. package/lib/http-handler.d.ts +0 -16
  61. package/lib/http-handler.js +0 -62
  62. package/lib/http-handler.js.map +0 -1
  63. package/lib/index.d.ts +0 -16
  64. package/lib/index.js +0 -33
  65. package/lib/index.js.map +0 -1
  66. package/lib/internal-types.d.ts +0 -20
  67. package/lib/internal-types.js.map +0 -1
  68. package/lib/invoke.d.ts +0 -5
  69. package/lib/invoke.js +0 -30
  70. package/lib/parser.d.ts +0 -3
  71. package/lib/parser.js +0 -73
  72. package/lib/parser.js.map +0 -1
  73. package/lib/promisify.d.ts +0 -3
  74. package/lib/promisify.js +0 -34
  75. package/lib/promisify.js.map +0 -1
  76. package/lib/router/SwaggerRouter.d.ts +0 -39
  77. package/lib/router/SwaggerRouter.js +0 -205
  78. package/lib/router/metadata.d.ts +0 -12
  79. package/lib/router/metadata.js +0 -30
  80. package/lib/router/metadata.js.map +0 -1
  81. package/lib/router/routing-table.d.ts +0 -16
  82. package/lib/router/routing-table.js +0 -95
  83. package/lib/router/routing-table.js.map +0 -1
  84. package/lib/sequence.js.map +0 -1
  85. package/lib/server.d.ts +0 -23
  86. package/lib/server.js +0 -53
  87. package/lib/server.js.map +0 -1
  88. package/lib/src/Component.d.ts +0 -2
  89. package/lib/src/Component.js +0 -6
  90. package/lib/src/Sequence.d.ts +0 -6
  91. package/lib/src/Sequence.js +0 -26
  92. package/lib/src/application.d.ts +0 -27
  93. package/lib/src/application.js +0 -70
  94. package/lib/src/index.d.ts +0 -13
  95. package/lib/src/index.js +0 -29
  96. package/lib/src/invoke.d.ts +0 -5
  97. package/lib/src/invoke.js +0 -34
  98. package/lib/src/parser.d.ts +0 -3
  99. package/lib/src/parser.js +0 -72
  100. package/lib/src/promisify.d.ts +0 -3
  101. package/lib/src/promisify.js +0 -33
  102. package/lib/src/router/SwaggerRouter.d.ts +0 -53
  103. package/lib/src/router/SwaggerRouter.js +0 -101
  104. package/lib/src/router/metadata.d.ts +0 -13
  105. package/lib/src/router/metadata.js +0 -29
  106. package/lib/src/router/routing-table.d.ts +0 -13
  107. package/lib/src/router/routing-table.js +0 -83
  108. package/lib/src/server.d.ts +0 -17
  109. package/lib/src/server.js +0 -40
  110. package/lib/writer.d.ts +0 -4
  111. package/lib/writer.js +0 -24
  112. package/lib/writer.js.map +0 -1
  113. package/lib6/Component.d.ts +0 -2
  114. package/lib6/Component.js +0 -7
  115. package/lib6/Sequence.d.ts +0 -14
  116. package/lib6/Sequence.js +0 -68
  117. package/lib6/application.d.ts +0 -52
  118. package/lib6/application.js +0 -79
  119. package/lib6/application.js.map +0 -1
  120. package/lib6/component.js.map +0 -1
  121. package/lib6/handlers/http.d.ts +0 -3
  122. package/lib6/handlers/http.js +0 -21
  123. package/lib6/handlers/http.js.map +0 -1
  124. package/lib6/handlers/index.d.ts +0 -1
  125. package/lib6/handlers/index.js +0 -11
  126. package/lib6/handlers/index.js.map +0 -1
  127. package/lib6/http-handler.d.ts +0 -16
  128. package/lib6/http-handler.js +0 -72
  129. package/lib6/http-handler.js.map +0 -1
  130. package/lib6/index.d.ts +0 -16
  131. package/lib6/index.js +0 -33
  132. package/lib6/index.js.map +0 -1
  133. package/lib6/internal-types.d.ts +0 -20
  134. package/lib6/internal-types.js +0 -8
  135. package/lib6/internal-types.js.map +0 -1
  136. package/lib6/invoke.d.ts +0 -5
  137. package/lib6/invoke.js +0 -30
  138. package/lib6/parser.d.ts +0 -3
  139. package/lib6/parser.js +0 -83
  140. package/lib6/parser.js.map +0 -1
  141. package/lib6/promisify.d.ts +0 -3
  142. package/lib6/promisify.js +0 -34
  143. package/lib6/promisify.js.map +0 -1
  144. package/lib6/router/SwaggerRouter.d.ts +0 -39
  145. package/lib6/router/SwaggerRouter.js +0 -205
  146. package/lib6/router/metadata.d.ts +0 -12
  147. package/lib6/router/metadata.js +0 -30
  148. package/lib6/router/metadata.js.map +0 -1
  149. package/lib6/router/routing-table.d.ts +0 -16
  150. package/lib6/router/routing-table.js +0 -95
  151. package/lib6/router/routing-table.js.map +0 -1
  152. package/lib6/sequence.js.map +0 -1
  153. package/lib6/server.d.ts +0 -23
  154. package/lib6/server.js +0 -63
  155. package/lib6/server.js.map +0 -1
  156. package/lib6/src/Component.d.ts +0 -2
  157. package/lib6/src/Sequence.d.ts +0 -6
  158. package/lib6/src/Sequence.js +0 -36
  159. package/lib6/src/application.d.ts +0 -27
  160. package/lib6/src/application.js +0 -70
  161. package/lib6/src/index.d.ts +0 -13
  162. package/lib6/src/index.js +0 -29
  163. package/lib6/src/invoke.d.ts +0 -5
  164. package/lib6/src/invoke.js +0 -34
  165. package/lib6/src/parser.d.ts +0 -3
  166. package/lib6/src/parser.js +0 -82
  167. package/lib6/src/promisify.d.ts +0 -3
  168. package/lib6/src/promisify.js +0 -33
  169. package/lib6/src/router/SwaggerRouter.d.ts +0 -53
  170. package/lib6/src/router/SwaggerRouter.js +0 -101
  171. package/lib6/src/router/metadata.d.ts +0 -13
  172. package/lib6/src/router/metadata.js +0 -29
  173. package/lib6/src/router/routing-table.d.ts +0 -13
  174. package/lib6/src/router/routing-table.js +0 -83
  175. package/lib6/src/server.d.ts +0 -17
  176. package/lib6/src/server.js +0 -50
  177. package/lib6/writer.d.ts +0 -4
  178. package/lib6/writer.js +0 -24
  179. package/lib6/writer.js.map +0 -1
@@ -0,0 +1,268 @@
1
+ // Copyright IBM Corp. and LoopBack contributors 2018,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 {
7
+ Binding,
8
+ Context,
9
+ ContextView,
10
+ inject,
11
+ invokeMethod,
12
+ sortBindingsByPhase,
13
+ } from '@loopback/context';
14
+ import debugFactory from 'debug';
15
+ import {CoreBindings, CoreTags} from './keys';
16
+ import {LifeCycleObserver, lifeCycleObserverFilter} from './lifecycle';
17
+ const debug = debugFactory('loopback:core:lifecycle');
18
+
19
+ /**
20
+ * A group of life cycle observers
21
+ */
22
+ export type LifeCycleObserverGroup = {
23
+ /**
24
+ * Observer group name
25
+ */
26
+ group: string;
27
+ /**
28
+ * Bindings for observers within the group
29
+ */
30
+ bindings: Readonly<Binding<LifeCycleObserver>>[];
31
+ };
32
+
33
+ export type LifeCycleObserverOptions = {
34
+ /**
35
+ * Control the order of observer groups for notifications. For example,
36
+ * with `['datasource', 'server']`, the observers in `datasource` group are
37
+ * notified before those in `server` group during `start`. Please note that
38
+ * observers are notified in the reverse order during `stop`.
39
+ */
40
+ orderedGroups: string[];
41
+ /**
42
+ * Override and disable lifecycle observer groups. This setting applies to
43
+ * both ordered groups (i.e. those defined in `orderedGroups`) and unordered
44
+ * groups.
45
+ */
46
+ disabledGroups?: string[];
47
+ /**
48
+ * Notify observers of the same group in parallel, default to `true`
49
+ */
50
+ parallel?: boolean;
51
+ };
52
+
53
+ export const DEFAULT_ORDERED_GROUPS = ['server'];
54
+
55
+ /**
56
+ * A context-based registry for life cycle observers
57
+ */
58
+ export class LifeCycleObserverRegistry implements LifeCycleObserver {
59
+ constructor(
60
+ @inject.context()
61
+ protected readonly context: Context,
62
+ @inject.view(lifeCycleObserverFilter)
63
+ protected readonly observersView: ContextView<LifeCycleObserver>,
64
+ @inject(CoreBindings.LIFE_CYCLE_OBSERVER_OPTIONS, {optional: true})
65
+ protected readonly options: LifeCycleObserverOptions = {
66
+ parallel: true,
67
+ orderedGroups: DEFAULT_ORDERED_GROUPS,
68
+ },
69
+ ) {}
70
+
71
+ setOrderedGroups(groups: string[]) {
72
+ this.options.orderedGroups = groups;
73
+ }
74
+
75
+ /**
76
+ * Get observer groups ordered by the group
77
+ */
78
+ public getObserverGroupsByOrder(): LifeCycleObserverGroup[] {
79
+ const bindings = this.observersView.bindings;
80
+ const groups = this.sortObserverBindingsByGroup(bindings);
81
+ if (debug.enabled) {
82
+ debug(
83
+ 'Observer groups: %j',
84
+ groups.map(g => ({
85
+ group: g.group,
86
+ bindings: g.bindings.map(b => b.key),
87
+ })),
88
+ );
89
+ }
90
+ return groups;
91
+ }
92
+
93
+ /**
94
+ * Get the group for a given life cycle observer binding
95
+ * @param binding - Life cycle observer binding
96
+ */
97
+ protected getObserverGroup(
98
+ binding: Readonly<Binding<LifeCycleObserver>>,
99
+ ): string {
100
+ // First check if there is an explicit group name in the tag
101
+ let group = binding.tagMap[CoreTags.LIFE_CYCLE_OBSERVER_GROUP];
102
+ if (!group) {
103
+ // Fall back to a tag that matches one of the groups
104
+ group = this.options.orderedGroups.find(g => binding.tagMap[g] === g);
105
+ }
106
+ group = group || '';
107
+ debug(
108
+ 'Binding %s is configured with observer group %s',
109
+ binding.key,
110
+ group,
111
+ );
112
+ return group;
113
+ }
114
+
115
+ /**
116
+ * Sort the life cycle observer bindings so that we can start/stop them
117
+ * in the right order. By default, we can start other observers before servers
118
+ * and stop them in the reverse order
119
+ * @param bindings - Life cycle observer bindings
120
+ */
121
+ protected sortObserverBindingsByGroup(
122
+ bindings: Readonly<Binding<LifeCycleObserver>>[],
123
+ ) {
124
+ // Group bindings in a map
125
+ const groupMap: Map<string, Readonly<Binding<LifeCycleObserver>>[]> =
126
+ new Map();
127
+ sortBindingsByPhase(
128
+ bindings,
129
+ CoreTags.LIFE_CYCLE_OBSERVER_GROUP,
130
+ this.options.orderedGroups,
131
+ );
132
+ for (const binding of bindings) {
133
+ const group = this.getObserverGroup(binding);
134
+ let bindingsInGroup = groupMap.get(group);
135
+ if (bindingsInGroup == null) {
136
+ bindingsInGroup = [];
137
+ groupMap.set(group, bindingsInGroup);
138
+ }
139
+ bindingsInGroup.push(binding);
140
+ }
141
+ // Create an array for group entries
142
+ const groups: LifeCycleObserverGroup[] = [];
143
+ for (const [group, bindingsInGroup] of groupMap) {
144
+ groups.push({group, bindings: bindingsInGroup});
145
+ }
146
+ return groups;
147
+ }
148
+
149
+ /**
150
+ * Notify an observer group of the given event
151
+ * @param group - A group of bindings for life cycle observers
152
+ * @param event - Event name
153
+ */
154
+ protected async notifyObservers(
155
+ observers: LifeCycleObserver[],
156
+ bindings: Readonly<Binding<LifeCycleObserver>>[],
157
+ event: keyof LifeCycleObserver,
158
+ ) {
159
+ if (!this.options.parallel) {
160
+ let index = 0;
161
+ for (const observer of observers) {
162
+ debug(
163
+ 'Invoking %s observer for binding %s',
164
+ event,
165
+ bindings[index].key,
166
+ );
167
+ index++;
168
+ await this.invokeObserver(observer, event);
169
+ }
170
+ return;
171
+ }
172
+
173
+ // Parallel invocation
174
+ const notifiers = observers.map((observer, index) => {
175
+ debug('Invoking %s observer for binding %s', event, bindings[index].key);
176
+ return this.invokeObserver(observer, event);
177
+ });
178
+ await Promise.all(notifiers);
179
+ }
180
+
181
+ /**
182
+ * Invoke an observer for the given event
183
+ * @param observer - A life cycle observer
184
+ * @param event - Event name
185
+ */
186
+ protected async invokeObserver(
187
+ observer: LifeCycleObserver,
188
+ event: keyof LifeCycleObserver,
189
+ ) {
190
+ if (typeof observer[event] === 'function') {
191
+ // Supply `undefined` for legacy callback function expected by
192
+ // DataSource.stop()
193
+ await invokeMethod(observer, event, this.context, [undefined], {
194
+ skipInterceptors: true,
195
+ });
196
+ }
197
+ }
198
+
199
+ /**
200
+ * Emit events to the observer groups
201
+ * @param events - Event names
202
+ * @param groups - Observer groups
203
+ */
204
+ protected async notifyGroups(
205
+ events: (keyof LifeCycleObserver)[],
206
+ groups: LifeCycleObserverGroup[],
207
+ reverse = false,
208
+ ) {
209
+ const observers = await this.observersView.values();
210
+ const bindings = this.observersView.bindings;
211
+ const found = observers.some(observer =>
212
+ events.some(e => typeof observer[e] === 'function'),
213
+ );
214
+ if (!found) return;
215
+ if (reverse) {
216
+ // Do not reverse the original `groups` in place
217
+ groups = [...groups].reverse();
218
+ }
219
+ for (const group of groups) {
220
+ if (this.options.disabledGroups?.includes(group.group)) {
221
+ debug('Notification skipped (Group is disabled): %s', group.group);
222
+ continue;
223
+ }
224
+ const observersForGroup: LifeCycleObserver[] = [];
225
+ const bindingsInGroup = reverse
226
+ ? group.bindings.reverse()
227
+ : group.bindings;
228
+ for (const binding of bindingsInGroup) {
229
+ const index = bindings.indexOf(binding);
230
+ observersForGroup.push(observers[index]);
231
+ }
232
+
233
+ for (const event of events) {
234
+ debug('Beginning notification %s of %s...', event);
235
+ await this.notifyObservers(observersForGroup, group.bindings, event);
236
+ debug('Finished notification %s of %s', event);
237
+ }
238
+ }
239
+ }
240
+
241
+ /**
242
+ * Notify all life cycle observers by group of `init`
243
+ */
244
+ public async init(): Promise<void> {
245
+ debug('Initializing the %s...');
246
+ const groups = this.getObserverGroupsByOrder();
247
+ await this.notifyGroups(['init'], groups);
248
+ }
249
+
250
+ /**
251
+ * Notify all life cycle observers by group of `start`
252
+ */
253
+ public async start(): Promise<void> {
254
+ debug('Starting the %s...');
255
+ const groups = this.getObserverGroupsByOrder();
256
+ await this.notifyGroups(['start'], groups);
257
+ }
258
+
259
+ /**
260
+ * Notify all life cycle observers by group of `stop`
261
+ */
262
+ public async stop(): Promise<void> {
263
+ debug('Stopping the %s...');
264
+ const groups = this.getObserverGroupsByOrder();
265
+ // Stop in the reverse order
266
+ await this.notifyGroups(['stop'], groups, true);
267
+ }
268
+ }
@@ -0,0 +1,90 @@
1
+ // Copyright IBM Corp. and LoopBack contributors 2018,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 {
7
+ Binding,
8
+ BindingSpec,
9
+ BindingTagFilter,
10
+ Constructor,
11
+ filterByTag,
12
+ injectable,
13
+ ValueOrPromise,
14
+ } from '@loopback/context';
15
+ import {CoreTags} from './keys';
16
+
17
+ /**
18
+ * Observers to handle life cycle init/start/stop events
19
+ */
20
+ export interface LifeCycleObserver {
21
+ /**
22
+ * The method to be invoked during `init`. It will only be called at most once
23
+ * for a given application instance.
24
+ */
25
+ init?(...injectedArgs: unknown[]): ValueOrPromise<void>;
26
+ /**
27
+ * The method to be invoked during `start`
28
+ */
29
+ start?(...injectedArgs: unknown[]): ValueOrPromise<void>;
30
+ /**
31
+ * The method to be invoked during `stop`
32
+ */
33
+ stop?(...injectedArgs: unknown[]): ValueOrPromise<void>;
34
+ }
35
+
36
+ const lifeCycleMethods: (keyof LifeCycleObserver)[] = ['init', 'start', 'stop'];
37
+
38
+ /**
39
+ * Test if an object implements LifeCycleObserver
40
+ * @param obj - An object
41
+ */
42
+ export function isLifeCycleObserver(obj: object): obj is LifeCycleObserver {
43
+ const candidate = obj as Partial<LifeCycleObserver>;
44
+ return lifeCycleMethods.some(m => typeof candidate[m] === 'function');
45
+ }
46
+
47
+ /**
48
+ * Test if a class implements LifeCycleObserver
49
+ * @param ctor - A class
50
+ */
51
+ export function isLifeCycleObserverClass(
52
+ ctor: Constructor<unknown>,
53
+ ): ctor is Constructor<LifeCycleObserver> {
54
+ return ctor.prototype && isLifeCycleObserver(ctor.prototype);
55
+ }
56
+
57
+ /**
58
+ * A `BindingTemplate` function to configure the binding as life cycle observer
59
+ * by tagging it with `CoreTags.LIFE_CYCLE_OBSERVER`.
60
+ *
61
+ * @param binding - Binding object
62
+ */
63
+ export function asLifeCycleObserver<T = unknown>(binding: Binding<T>) {
64
+ return binding.tag(CoreTags.LIFE_CYCLE_OBSERVER);
65
+ }
66
+
67
+ /**
68
+ * Find all life cycle observer bindings. By default, a binding tagged with
69
+ * `CoreTags.LIFE_CYCLE_OBSERVER`. It's used as `BindingFilter`.
70
+ */
71
+ export const lifeCycleObserverFilter: BindingTagFilter = filterByTag(
72
+ CoreTags.LIFE_CYCLE_OBSERVER,
73
+ );
74
+
75
+ /**
76
+ * Sugar decorator to mark a class as life cycle observer
77
+ * @param group - Optional observer group name
78
+ * @param specs - Optional bindings specs
79
+ */
80
+ export function lifeCycleObserver(group = '', ...specs: BindingSpec[]) {
81
+ return injectable(
82
+ asLifeCycleObserver,
83
+ {
84
+ tags: {
85
+ [CoreTags.LIFE_CYCLE_OBSERVER_GROUP]: group,
86
+ },
87
+ },
88
+ ...specs,
89
+ );
90
+ }
@@ -0,0 +1,69 @@
1
+ // Copyright IBM Corp. and LoopBack contributors 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
+ * // eslint-disable-next-line @typescript-eslint/ban-ts-comment
30
+ * // @ts-ignore
31
+ * (this as unknown as {YourBaseClass}).protectedMember
32
+ * ```
33
+ *
34
+ * The directive `@ts-ignore` suppresses compiler error about accessing
35
+ * a protected member from outside. Unfortunately, it also disables other
36
+ * compile-time checks (e.g. to verify that a protected method was invoked
37
+ * with correct arguments, and so on). This is the same behavior you
38
+ * would get by using `Constructor<any>` instead of `MixinTarget<Application>`.
39
+ * The major improvement is that TypeScript can still infer the return
40
+ * type of the protected member, therefore `any` is NOT introduced to subsequent
41
+ * code.
42
+ *
43
+ * TypeScript also does not allow mixin class to overwrite a method inherited
44
+ * from a mapped type, see https://github.com/microsoft/TypeScript/issues/38496
45
+ * As a workaround, use `@ts-ignore` to disable the error.
46
+ *
47
+ * ```ts
48
+ * export function RepositoryMixin<T extends MixinTarget<Application>>(
49
+ * superClass: T,
50
+ * ) {
51
+ * return class extends superClass {
52
+ * // @ts-ignore
53
+ * public component<C extends Component = Component>(
54
+ * componentCtor: Constructor<C>,
55
+ * nameOrOptions?: string | BindingFromClassOptions,
56
+ * ) {
57
+ * const binding = super.component(componentCtor, nameOrOptions);
58
+ * // ...
59
+ * return binding;
60
+ * }
61
+ * }
62
+ * ```
63
+ */
64
+ export type MixinTarget<T extends object> = Constructor<{
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
+ }>;
package/src/server.ts ADDED
@@ -0,0 +1,22 @@
1
+ // Copyright IBM Corp. and LoopBack contributors 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 {LifeCycleObserver} from './lifecycle';
7
+
8
+ /**
9
+ * Defines the requirements to implement a Server for LoopBack applications:
10
+ * start() : Promise<void>
11
+ * stop() : Promise<void>
12
+ * It is recommended that each Server implementation creates its own child
13
+ * Context, which inherits from the parent Application context. This way,
14
+ * any Server-specific bindings will remain local to the Server instance,
15
+ * and will avoid polluting its parent module scope.
16
+ */
17
+ export interface Server extends LifeCycleObserver {
18
+ /**
19
+ * Tells whether the server is listening for connections or not
20
+ */
21
+ readonly listening: boolean;
22
+ }
package/src/service.ts ADDED
@@ -0,0 +1,211 @@
1
+ // Copyright IBM Corp. and LoopBack contributors 2019,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 {
7
+ Binding,
8
+ BindingFilter,
9
+ BindingFromClassOptions,
10
+ BindingTemplate,
11
+ bindingTemplateFor,
12
+ ContextTags,
13
+ ContextView,
14
+ createBindingFromClass,
15
+ DecoratorFactory,
16
+ inject,
17
+ InjectionMetadata,
18
+ isDynamicValueProviderClass,
19
+ isProviderClass,
20
+ MetadataInspector,
21
+ transformValueOrPromise,
22
+ } from '@loopback/context';
23
+ import {ServiceOrProviderClass} from './application';
24
+ import {CoreTags} from './keys';
25
+
26
+ /**
27
+ * Representing an interface for services. In TypeScript, the `interface` does
28
+ * not have reflections at runtime. We use a string, a symbol or a Function as
29
+ * the type for the service interface.
30
+ */
31
+ export type ServiceInterface = string | symbol | Function;
32
+
33
+ /**
34
+ * Options to register a service binding
35
+ */
36
+ export interface ServiceOptions extends BindingFromClassOptions {
37
+ interface?: ServiceInterface;
38
+ }
39
+
40
+ /**
41
+ * `@service` injects a service instance that matches the class or interface.
42
+ *
43
+ * @param serviceInterface - Interface for the service. It can be in one of the
44
+ * following forms:
45
+ *
46
+ * - A class, such as MyService
47
+ * - A string that identifies the interface, such as `'MyService'`
48
+ * - A symbol that identifies the interface, such as `Symbol('MyService')`
49
+ *
50
+ * If not provided, the value is inferred from the design:type of the parameter
51
+ * or property
52
+ *
53
+ * @example
54
+ * ```ts
55
+ *
56
+ * const ctx = new Context();
57
+ * ctx.bind('my-service').toClass(MyService);
58
+ * ctx.bind('logger').toClass(Logger);
59
+ *
60
+ * export class MyController {
61
+ * constructor(@service(MyService) private myService: MyService) {}
62
+ *
63
+ * @service()
64
+ * private logger: Logger;
65
+ * }
66
+ *
67
+ * ctx.bind('my-controller').toClass(MyController);
68
+ * await myController = ctx.get<MyController>('my-controller');
69
+ * ```
70
+ */
71
+ export function service(
72
+ serviceInterface?: ServiceInterface,
73
+ metadata?: InjectionMetadata,
74
+ ) {
75
+ return inject(
76
+ '',
77
+ {decorator: '@service', ...metadata},
78
+ (ctx, injection, session) => {
79
+ let serviceType = serviceInterface;
80
+ if (!serviceType) {
81
+ if (typeof injection.methodDescriptorOrParameterIndex === 'number') {
82
+ serviceType = MetadataInspector.getDesignTypeForMethod(
83
+ injection.target,
84
+ injection.member!,
85
+ )?.parameterTypes[injection.methodDescriptorOrParameterIndex];
86
+ } else {
87
+ serviceType = MetadataInspector.getDesignTypeForProperty(
88
+ injection.target,
89
+ injection.member!,
90
+ );
91
+ }
92
+ }
93
+ if (serviceType === undefined) {
94
+ const targetName = DecoratorFactory.getTargetName(
95
+ injection.target,
96
+ injection.member,
97
+ injection.methodDescriptorOrParameterIndex,
98
+ );
99
+ const msg =
100
+ `No design-time type metadata found while inspecting ${targetName}. ` +
101
+ 'You can either use `@service(ServiceClass)` or ensure `emitDecoratorMetadata` is enabled in your TypeScript configuration. ' +
102
+ 'Run `tsc --showConfig` to print the final TypeScript configuration of your project.';
103
+ throw new Error(msg);
104
+ }
105
+
106
+ if (serviceType === Object || serviceType === Array) {
107
+ throw new Error(
108
+ 'Service class cannot be inferred from design type. Use @service(ServiceClass).',
109
+ );
110
+ }
111
+ const view = new ContextView(ctx, filterByServiceInterface(serviceType));
112
+ const result = view.resolve({
113
+ optional: metadata?.optional,
114
+ asProxyWithInterceptors: metadata?.asProxyWithInterceptors,
115
+ session,
116
+ });
117
+
118
+ const serviceTypeName =
119
+ typeof serviceType === 'string'
120
+ ? serviceType
121
+ : typeof serviceType === 'symbol'
122
+ ? serviceType.toString()
123
+ : serviceType.name;
124
+ return transformValueOrPromise(result, values => {
125
+ if (values.length === 1) return values[0];
126
+ if (values.length >= 1) {
127
+ throw new Error(
128
+ `More than one bindings found for ${serviceTypeName}`,
129
+ );
130
+ } else {
131
+ if (metadata?.optional) {
132
+ return undefined;
133
+ }
134
+ throw new Error(
135
+ `No binding found for ${serviceTypeName}. Make sure a service ` +
136
+ `binding is created in context ${ctx.name} with serviceInterface (${serviceTypeName}).`,
137
+ );
138
+ }
139
+ });
140
+ },
141
+ );
142
+ }
143
+
144
+ /**
145
+ * Create a binding filter by service class
146
+ * @param serviceInterface - Service class matching the one used by `binding.toClass()`
147
+ * @param options - Options to control if subclasses should be skipped for matching
148
+ */
149
+ export function filterByServiceInterface(
150
+ serviceInterface: ServiceInterface,
151
+ ): BindingFilter {
152
+ return binding =>
153
+ binding.valueConstructor === serviceInterface ||
154
+ binding.tagMap[CoreTags.SERVICE_INTERFACE] === serviceInterface;
155
+ }
156
+
157
+ /**
158
+ * Create a service binding from a class or provider
159
+ * @param cls - Service class or provider
160
+ * @param options - Service options
161
+ */
162
+ export function createServiceBinding<S>(
163
+ cls: ServiceOrProviderClass<S>,
164
+ options: ServiceOptions = {},
165
+ ): Binding<S> {
166
+ let name = options.name;
167
+ if (!name && isProviderClass(cls)) {
168
+ // Trim `Provider` from the default service name
169
+ // This is needed to keep backward compatibility
170
+ const templateFn = bindingTemplateFor(cls);
171
+ const template = Binding.bind<S>('template').apply(templateFn);
172
+ if (
173
+ template.tagMap[ContextTags.PROVIDER] &&
174
+ !template.tagMap[ContextTags.NAME]
175
+ ) {
176
+ // The class is a provider and no `name` tag is found
177
+ name = cls.name.replace(/Provider$/, '');
178
+ }
179
+ }
180
+ if (!name && isDynamicValueProviderClass(cls)) {
181
+ // Trim `Provider` from the default service name
182
+ const templateFn = bindingTemplateFor(cls);
183
+ const template = Binding.bind<S>('template').apply(templateFn);
184
+ if (
185
+ template.tagMap[ContextTags.DYNAMIC_VALUE_PROVIDER] &&
186
+ !template.tagMap[ContextTags.NAME]
187
+ ) {
188
+ // The class is a provider and no `name` tag is found
189
+ name = cls.name.replace(/Provider$/, '');
190
+ }
191
+ }
192
+ const binding = createBindingFromClass(cls, {
193
+ name,
194
+ type: CoreTags.SERVICE,
195
+ ...options,
196
+ }).apply(asService(options.interface ?? cls));
197
+ return binding;
198
+ }
199
+
200
+ /**
201
+ * Create a binding template for a service interface
202
+ * @param serviceInterface - Service interface
203
+ */
204
+ export function asService(serviceInterface: ServiceInterface): BindingTemplate {
205
+ return function serviceTemplate(binding: Binding) {
206
+ binding.tag({
207
+ [ContextTags.TYPE]: CoreTags.SERVICE,
208
+ [CoreTags.SERVICE_INTERFACE]: serviceInterface,
209
+ });
210
+ };
211
+ }
package/index.d.ts DELETED
@@ -1,6 +0,0 @@
1
- // Copyright IBM Corp. 2017. 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
- export * from './lib';
package/index.js DELETED
@@ -1,9 +0,0 @@
1
- // Copyright IBM Corp. 2017. 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
- const nodeMajorVersion = +process.versions.node.split('.')[0];
7
- module.exports = nodeMajorVersion >= 7 ?
8
- require('./lib') :
9
- require('./lib6');
@@ -1,2 +0,0 @@
1
- export interface Component {
2
- }
package/lib/Component.js DELETED
@@ -1,7 +0,0 @@
1
- "use strict";
2
- // Copyright IBM Corp. 2017. All Rights Reserved.
3
- // Node module: @loopback/core
4
- // This file is licensed under the MIT License.
5
- // License text available at https://opensource.org/licenses/MIT
6
- Object.defineProperty(exports, "__esModule", { value: true });
7
- //# sourceMappingURL=component.js.map