@loopback/express 1.2.5 → 1.4.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.
package/CHANGELOG.md CHANGED
@@ -3,6 +3,46 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
+ ## [1.4.1](https://github.com/strongloop/loopback-next/compare/@loopback/express@1.4.0...@loopback/express@1.4.1) (2020-08-27)
7
+
8
+ **Note:** Version bump only for package @loopback/express
9
+
10
+
11
+
12
+
13
+
14
+ # [1.4.0](https://github.com/strongloop/loopback-next/compare/@loopback/express@1.3.0...@loopback/express@1.4.0) (2020-08-19)
15
+
16
+
17
+ ### Features
18
+
19
+ * **express:** add middleware view to watch registered middleware ([205d948](https://github.com/strongloop/loopback-next/commit/205d948cb91cf48d187ce247ee5e77b1204be35e))
20
+ * **rest:** add the ability to validate sorted middleware groups ([227dbf8](https://github.com/strongloop/loopback-next/commit/227dbf8045990536ac1437ea4a7ae1f1a1e571bb))
21
+ * **rest:** optimize middleware sequence to reuse middleware binding keys ([0041a24](https://github.com/strongloop/loopback-next/commit/0041a246df89f7dbff179ed7c5e08a65ec5bcbda))
22
+
23
+
24
+
25
+
26
+
27
+ # [1.3.0](https://github.com/strongloop/loopback-next/compare/@loopback/express@1.2.6...@loopback/express@1.3.0) (2020-08-05)
28
+
29
+
30
+ ### Features
31
+
32
+ * **express:** sort middleware by group dependencies and ordered groups ([5582f06](https://github.com/strongloop/loopback-next/commit/5582f069834666a6d6a9d8d2f2d66fa1a9a5f7d3))
33
+
34
+
35
+
36
+
37
+
38
+ ## [1.2.6](https://github.com/strongloop/loopback-next/compare/@loopback/express@1.2.5...@loopback/express@1.2.6) (2020-07-20)
39
+
40
+ **Note:** Version bump only for package @loopback/express
41
+
42
+
43
+
44
+
45
+
6
46
  ## [1.2.5](https://github.com/strongloop/loopback-next/compare/@loopback/express@1.2.4...@loopback/express@1.2.5) (2020-06-30)
7
47
 
8
48
  **Note:** Version bump only for package @loopback/express
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Sort the groups by their relative order
3
+ * @param orderedGroups - A list of arrays - each of which represents a partial
4
+ * order of groups.
5
+ */
6
+ export declare function sortListOfGroups(...orderedGroups: string[][]): string[];
@@ -0,0 +1,37 @@
1
+ "use strict";
2
+ // Copyright IBM Corp. 2020. All Rights Reserved.
3
+ // Node module: @loopback/express
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
+ exports.sortListOfGroups = void 0;
8
+ const tslib_1 = require("tslib");
9
+ const debug_1 = tslib_1.__importDefault(require("debug"));
10
+ const toposort_1 = tslib_1.__importDefault(require("toposort"));
11
+ const debug = debug_1.default('loopback:middleware');
12
+ /**
13
+ * Sort the groups by their relative order
14
+ * @param orderedGroups - A list of arrays - each of which represents a partial
15
+ * order of groups.
16
+ */
17
+ function sortListOfGroups(...orderedGroups) {
18
+ if (debug.enabled) {
19
+ debug('Dependency graph: %s', orderedGroups.map(edge => edge.join('->')).join(', '));
20
+ }
21
+ const graph = [];
22
+ for (const groups of orderedGroups) {
23
+ if (groups.length >= 2) {
24
+ groups.reduce((prev, group) => {
25
+ if (typeof prev === 'string') {
26
+ graph.push([prev, group]);
27
+ }
28
+ return group;
29
+ }, undefined);
30
+ }
31
+ }
32
+ const sorted = toposort_1.default(graph);
33
+ debug('Sorted groups: %s', sorted.join('->'));
34
+ return sorted;
35
+ }
36
+ exports.sortListOfGroups = sortListOfGroups;
37
+ //# sourceMappingURL=group-sorter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"group-sorter.js","sourceRoot":"","sources":["../src/group-sorter.ts"],"names":[],"mappings":";AAAA,iDAAiD;AACjD,iCAAiC;AACjC,+CAA+C;AAC/C,gEAAgE;;;;AAEhE,0DAAiC;AACjC,gEAAgC;AAChC,MAAM,KAAK,GAAG,eAAY,CAAC,qBAAqB,CAAC,CAAC;AAClD;;;;GAIG;AACH,SAAgB,gBAAgB,CAAC,GAAG,aAAyB;IAC3D,IAAI,KAAK,CAAC,OAAO,EAAE;QACjB,KAAK,CACH,sBAAsB,EACtB,aAAa,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CACtD,CAAC;KACH;IACD,MAAM,KAAK,GAAuB,EAAE,CAAC;IACrC,KAAK,MAAM,MAAM,IAAI,aAAa,EAAE;QAClC,IAAI,MAAM,CAAC,MAAM,IAAI,CAAC,EAAE;YACtB,MAAM,CAAC,MAAM,CAAC,CAAC,IAAwB,EAAE,KAAK,EAAE,EAAE;gBAChD,IAAI,OAAO,IAAI,KAAK,QAAQ,EAAE;oBAC5B,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC,CAAC;iBAC3B;gBACD,OAAO,KAAK,CAAC;YACf,CAAC,EAAE,SAAS,CAAC,CAAC;SACf;KACF;IACD,MAAM,MAAM,GAAG,kBAAQ,CAAC,KAAK,CAAC,CAAC;IAC/B,KAAK,CAAC,mBAAmB,EAAE,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;IAC9C,OAAO,MAAM,CAAC;AAChB,CAAC;AArBD,4CAqBC"}
package/dist/index.d.ts CHANGED
@@ -17,6 +17,7 @@
17
17
  */
18
18
  export * from './express.application';
19
19
  export * from './express.server';
20
+ export * from './group-sorter';
20
21
  export * from './keys';
21
22
  export * from './middleware';
22
23
  export * from './middleware-interceptor';
package/dist/index.js CHANGED
@@ -24,6 +24,7 @@ const tslib_1 = require("tslib");
24
24
  */
25
25
  tslib_1.__exportStar(require("./express.application"), exports);
26
26
  tslib_1.__exportStar(require("./express.server"), exports);
27
+ tslib_1.__exportStar(require("./group-sorter"), exports);
27
28
  tslib_1.__exportStar(require("./keys"), exports);
28
29
  tslib_1.__exportStar(require("./middleware"), exports);
29
30
  tslib_1.__exportStar(require("./middleware-interceptor"), exports);
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAAA,iDAAiD;AACjD,iCAAiC;AACjC,+CAA+C;AAC/C,gEAAgE;;;AAEhE;;;;;;;;;;;;;;;;GAgBG;AACH,gEAAsC;AACtC,2DAAiC;AACjC,iDAAuB;AACvB,uDAA6B;AAC7B,mEAAyC;AACzC,gEAAsC;AACtC,oEAA0C;AAC1C,iFAAuD;AACvD,kDAAwB"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAAA,iDAAiD;AACjD,iCAAiC;AACjC,+CAA+C;AAC/C,gEAAgE;;;AAEhE;;;;;;;;;;;;;;;;GAgBG;AACH,gEAAsC;AACtC,2DAAiC;AACjC,yDAA+B;AAC/B,iDAAuB;AACvB,uDAA6B;AAC7B,mEAAyC;AACzC,gEAAsC;AACtC,oEAA0C;AAC1C,iFAAuD;AACvD,kDAAwB"}
package/dist/keys.js CHANGED
@@ -6,6 +6,7 @@
6
6
  Object.defineProperty(exports, "__esModule", { value: true });
7
7
  exports.DEFAULT_MIDDLEWARE_GROUP = exports.MIDDLEWARE_INTERCEPTOR_NAMESPACE = exports.GLOBAL_MIDDLEWARE_INTERCEPTOR_NAMESPACE = exports.MIDDLEWARE_NAMESPACE = exports.MiddlewareBindings = void 0;
8
8
  const core_1 = require("@loopback/core");
9
+ const types_1 = require("./types");
9
10
  var MiddlewareBindings;
10
11
  (function (MiddlewareBindings) {
11
12
  /**
@@ -28,5 +29,5 @@ exports.MIDDLEWARE_INTERCEPTOR_NAMESPACE = 'globalInterceptors.middleware';
28
29
  /**
29
30
  * Default order group name for Express middleware based global interceptors
30
31
  */
31
- exports.DEFAULT_MIDDLEWARE_GROUP = 'middleware';
32
+ exports.DEFAULT_MIDDLEWARE_GROUP = types_1.MiddlewareGroups.DEFAULT;
32
33
  //# sourceMappingURL=keys.js.map
package/dist/keys.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"keys.js","sourceRoot":"","sources":["../src/keys.ts"],"names":[],"mappings":";AAAA,iDAAiD;AACjD,iCAAiC;AACjC,+CAA+C;AAC/C,gEAAgE;;;AAEhE,yCAA0C;AAG1C,IAAiB,kBAAkB,CAOlC;AAPD,WAAiB,kBAAkB;IACjC;;OAEG;IACU,0BAAO,GAAG,iBAAU,CAAC,MAAM,CACtC,yBAAyB,CAC1B,CAAC;AACJ,CAAC,EAPgB,kBAAkB,GAAlB,0BAAkB,KAAlB,0BAAkB,QAOlC;AAED;;GAEG;AACU,QAAA,oBAAoB,GAAG,YAAY,CAAC;AAEjD;;GAEG;AACU,QAAA,uCAAuC,GAClD,+BAA+B,CAAC;AAElC;;GAEG;AACU,QAAA,gCAAgC,GAAG,+BAA+B,CAAC;AAEhF;;GAEG;AACU,QAAA,wBAAwB,GAAG,YAAY,CAAC"}
1
+ {"version":3,"file":"keys.js","sourceRoot":"","sources":["../src/keys.ts"],"names":[],"mappings":";AAAA,iDAAiD;AACjD,iCAAiC;AACjC,+CAA+C;AAC/C,gEAAgE;;;AAEhE,yCAA0C;AAC1C,mCAA4D;AAE5D,IAAiB,kBAAkB,CAOlC;AAPD,WAAiB,kBAAkB;IACjC;;OAEG;IACU,0BAAO,GAAG,iBAAU,CAAC,MAAM,CACtC,yBAAyB,CAC1B,CAAC;AACJ,CAAC,EAPgB,kBAAkB,GAAlB,0BAAkB,KAAlB,0BAAkB,QAOlC;AAED;;GAEG;AACU,QAAA,oBAAoB,GAAG,YAAY,CAAC;AAEjD;;GAEG;AACU,QAAA,uCAAuC,GAClD,+BAA+B,CAAC;AAElC;;GAEG;AACU,QAAA,gCAAgC,GAAG,+BAA+B,CAAC;AAEhF;;GAEG;AACU,QAAA,wBAAwB,GAAG,wBAAgB,CAAC,OAAO,CAAC"}
@@ -1,4 +1,4 @@
1
- import { Binding, BindingTemplate, Constructor, Context, InvocationResult, Provider, ValueOrPromise } from '@loopback/core';
1
+ import { Binding, BindingTemplate, Constructor, Context, ContextView, InvocationResult, Provider, ValueOrPromise } from '@loopback/core';
2
2
  import { ExpressMiddlewareFactory, ExpressRequestHandler, InvokeMiddlewareOptions, Middleware, MiddlewareBindingOptions, MiddlewareContext } from './types';
3
3
  /**
4
4
  * An adapter function to create a LoopBack middleware that invokes the list
@@ -63,6 +63,23 @@ export declare function createMiddlewareBinding(middlewareProviderClass: Constru
63
63
  * @param options - Options to invoke the middleware chain
64
64
  */
65
65
  export declare function invokeMiddleware(middlewareCtx: MiddlewareContext, options?: InvokeMiddlewareOptions): ValueOrPromise<InvocationResult>;
66
+ /**
67
+ * Watch middleware binding keys for the given context and sort them by
68
+ * group
69
+ * @param ctx - Context object
70
+ * @param options - Middleware options
71
+ */
72
+ export declare class MiddlewareView extends ContextView {
73
+ private options;
74
+ private keys;
75
+ constructor(ctx: Context, options?: InvokeMiddlewareOptions);
76
+ refresh(): void;
77
+ /**
78
+ * A list of binding keys sorted by group for registered middleware
79
+ */
80
+ get middlewareBindingKeys(): string[];
81
+ private buildMiddlewareKeys;
82
+ }
66
83
  /**
67
84
  * Invoke a list of Express middleware handler functions
68
85
  *
@@ -4,10 +4,11 @@
4
4
  // This file is licensed under the MIT License.
5
5
  // License text available at https://opensource.org/licenses/MIT
6
6
  Object.defineProperty(exports, "__esModule", { value: true });
7
- exports.toExpressMiddleware = exports.invokeExpressMiddleware = exports.invokeMiddleware = exports.createMiddlewareBinding = exports.registerMiddleware = exports.asMiddleware = exports.registerExpressMiddleware = exports.createMiddleware = exports.toMiddleware = void 0;
7
+ exports.toExpressMiddleware = exports.invokeExpressMiddleware = exports.MiddlewareView = exports.invokeMiddleware = exports.createMiddlewareBinding = exports.registerMiddleware = exports.asMiddleware = exports.registerExpressMiddleware = exports.createMiddleware = exports.toMiddleware = void 0;
8
8
  const tslib_1 = require("tslib");
9
9
  const core_1 = require("@loopback/core");
10
10
  const debug_1 = tslib_1.__importDefault(require("debug"));
11
+ const group_sorter_1 = require("./group-sorter");
11
12
  const keys_1 = require("./keys");
12
13
  const middleware_interceptor_1 = require("./middleware-interceptor");
13
14
  const types_1 = require("./types");
@@ -84,6 +85,18 @@ function asMiddleware(options = {}) {
84
85
  binding
85
86
  .apply(core_1.extensionFor((_a = options.chain) !== null && _a !== void 0 ? _a : types_1.DEFAULT_MIDDLEWARE_CHAIN))
86
87
  .tag({ group: (_b = options.group) !== null && _b !== void 0 ? _b : keys_1.DEFAULT_MIDDLEWARE_GROUP });
88
+ const groupsBefore = options.upstreamGroups;
89
+ if (groupsBefore != null) {
90
+ binding.tag({
91
+ upstreamGroups: typeof groupsBefore === 'string' ? [groupsBefore] : groupsBefore,
92
+ });
93
+ }
94
+ const groupsAfter = options.downstreamGroups;
95
+ if (groupsAfter != null) {
96
+ binding.tag({
97
+ downstreamGroups: typeof groupsAfter === 'string' ? [groupsAfter] : groupsAfter,
98
+ });
99
+ }
87
100
  };
88
101
  }
89
102
  exports.asMiddleware = asMiddleware;
@@ -134,16 +147,75 @@ exports.createMiddlewareBinding = createMiddlewareBinding;
134
147
  */
135
148
  function invokeMiddleware(middlewareCtx, options) {
136
149
  debug('Invoke middleware chain for %s %s with options', middlewareCtx.request.method, middlewareCtx.request.originalUrl, options);
137
- const { chain = types_1.DEFAULT_MIDDLEWARE_CHAIN, orderedGroups } = options !== null && options !== void 0 ? options : {};
138
- // Find extensions for the given extension point binding
139
- const filter = core_1.extensionFilter(chain);
140
- if (debug.enabled) {
141
- debug('Middleware for extension point "%s":', chain, middlewareCtx.find(filter).map(b => b.key));
150
+ let keys = options === null || options === void 0 ? void 0 : options.middlewareList;
151
+ if (keys == null) {
152
+ const view = new MiddlewareView(middlewareCtx, options);
153
+ keys = view.middlewareBindingKeys;
154
+ view.close();
142
155
  }
143
- const mwChain = new types_1.MiddlewareChain(middlewareCtx, filter, core_1.compareBindingsByTag('group', orderedGroups));
156
+ const mwChain = new types_1.MiddlewareChain(middlewareCtx, keys);
144
157
  return mwChain.invokeInterceptors(options === null || options === void 0 ? void 0 : options.next);
145
158
  }
146
159
  exports.invokeMiddleware = invokeMiddleware;
160
+ /**
161
+ * Watch middleware binding keys for the given context and sort them by
162
+ * group
163
+ * @param ctx - Context object
164
+ * @param options - Middleware options
165
+ */
166
+ class MiddlewareView extends core_1.ContextView {
167
+ constructor(ctx, options) {
168
+ var _a;
169
+ // Find extensions for the given extension point binding
170
+ const filter = core_1.extensionFilter((_a = options === null || options === void 0 ? void 0 : options.chain) !== null && _a !== void 0 ? _a : types_1.DEFAULT_MIDDLEWARE_CHAIN);
171
+ super(ctx, filter);
172
+ this.options = {
173
+ chain: types_1.DEFAULT_MIDDLEWARE_CHAIN,
174
+ orderedGroups: [],
175
+ ...options,
176
+ };
177
+ this.buildMiddlewareKeys();
178
+ this.open();
179
+ }
180
+ refresh() {
181
+ super.refresh();
182
+ this.buildMiddlewareKeys();
183
+ }
184
+ /**
185
+ * A list of binding keys sorted by group for registered middleware
186
+ */
187
+ get middlewareBindingKeys() {
188
+ return this.keys;
189
+ }
190
+ buildMiddlewareKeys() {
191
+ var _a;
192
+ const middlewareBindings = this.bindings;
193
+ if (debug.enabled) {
194
+ debug('Middleware for extension point "%s":', this.options.chain, middlewareBindings.map(b => b.key));
195
+ }
196
+ // Calculate orders from middleware dependencies
197
+ const ordersFromDependencies = [];
198
+ middlewareBindings.forEach(b => {
199
+ var _a, _b, _c;
200
+ const group = (_a = b.tagMap.group) !== null && _a !== void 0 ? _a : keys_1.DEFAULT_MIDDLEWARE_GROUP;
201
+ const groupsBefore = (_b = b.tagMap.upstreamGroups) !== null && _b !== void 0 ? _b : [];
202
+ groupsBefore.forEach(d => ordersFromDependencies.push([d, group]));
203
+ const groupsAfter = (_c = b.tagMap.downstreamGroups) !== null && _c !== void 0 ? _c : [];
204
+ groupsAfter.forEach(d => ordersFromDependencies.push([group, d]));
205
+ });
206
+ const order = group_sorter_1.sortListOfGroups(...ordersFromDependencies, this.options.orderedGroups);
207
+ /**
208
+ * Validate sorted groups
209
+ */
210
+ if (typeof ((_a = this.options) === null || _a === void 0 ? void 0 : _a.validate) === 'function') {
211
+ this.options.validate(order);
212
+ }
213
+ this.keys = middlewareBindings
214
+ .sort(core_1.compareBindingsByTag('group', order))
215
+ .map(b => b.key);
216
+ }
217
+ }
218
+ exports.MiddlewareView = MiddlewareView;
147
219
  /**
148
220
  * Invoke a list of Express middleware handler functions
149
221
  *
@@ -1 +1 @@
1
- {"version":3,"file":"middleware.js","sourceRoot":"","sources":["../src/middleware.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"middleware.js","sourceRoot":"","sources":["../src/middleware.ts"],"names":[],"mappings":"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"}
package/dist/types.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { BindingAddress, Context, GenericInterceptor, GenericInterceptorChain, InvocationContext, Next, ValueOrPromise } from '@loopback/core';
1
+ import { BindingAddress, Context, GenericInterceptor, GenericInterceptorChain, GenericInterceptorOrKey, InvocationContext, Next, ValueOrPromise } from '@loopback/core';
2
2
  import { Request, RequestHandler, Response } from 'express';
3
3
  export { Request, Response, Router, RouterOptions } from 'express';
4
4
  /**
@@ -20,6 +20,10 @@ export declare type ExpressRequestHandler = RequestHandler;
20
20
  export declare class MiddlewareContext extends Context implements HandlerContext {
21
21
  readonly request: Request;
22
22
  readonly response: Response;
23
+ /**
24
+ * A flag to tell if the response is finished.
25
+ */
26
+ responseFinished: boolean;
23
27
  /**
24
28
  * Constructor for `MiddlewareContext`
25
29
  * @param request - Express request object
@@ -33,7 +37,28 @@ export declare class MiddlewareContext extends Context implements HandlerContext
33
37
  /**
34
38
  * Interface LoopBack 4 middleware to be executed within sequence of actions.
35
39
  * A middleware for LoopBack is basically a generic interceptor that uses
36
- * `RequestContext`.
40
+ * `MiddlewareContext`.
41
+ *
42
+ * @remarks
43
+ *
44
+ * The middleware function is responsible for processing HTTP requests and
45
+ * responses. It typically includes the following logic.
46
+ *
47
+ * 1. Process the request with one of the following outcome
48
+ * - Reject the request by throwing an error if request is invalid, such as
49
+ * validation or authentication failures
50
+ * - Produce a response by itself, such as from the cache
51
+ * - Proceed by calling `await next()` to invoke downstream middleware. When
52
+ * `await next()` returns, it goes to step 2. If an error thrown from
53
+ * `await next()`, step 3 handles the error.
54
+ *
55
+ * 2. Process the response with one the following outcome
56
+ * - Reject the response by throwing an error
57
+ * - Replace the response with its own value
58
+ * - Return the response to upstream middleware
59
+ *
60
+ * 3. Catch the error thrown from `await next()`. If the `catch` block does not
61
+ * exist, the error will be bubbled up to upstream middleware
37
62
  *
38
63
  * The signature of a middleware function is described at
39
64
  * {@link https://loopback.io/doc/en/lb4/apidocs.express.middleware.html | Middleware}.
@@ -64,6 +89,10 @@ export interface Middleware extends GenericInterceptor<MiddlewareContext> {
64
89
  */
65
90
  export declare class MiddlewareChain extends GenericInterceptorChain<MiddlewareContext> {
66
91
  }
92
+ /**
93
+ * A middleware function or binding key
94
+ */
95
+ export declare type MiddlewareOrKey = GenericInterceptorOrKey<MiddlewareContext>;
67
96
  /**
68
97
  * Default extension point name for middleware
69
98
  */
@@ -78,9 +107,23 @@ export interface InvokeMiddlewareOptions {
78
107
  */
79
108
  chain?: string;
80
109
  /**
81
- * An array of group names to denote the order of execution
110
+ * An array of group names to denote the order of execution, such as
111
+ * `['cors', 'caching', 'rate-limiting']`.
82
112
  */
83
113
  orderedGroups?: string[];
114
+ /**
115
+ * An optional function to validate the sorted groups before invoking the
116
+ * middleware chain
117
+ */
118
+ validate?: (groups: string[]) => void;
119
+ /**
120
+ * Pre-built middleware list. If set, the list will be used to create the
121
+ * middleware chain without discovering again within the context.
122
+ */
123
+ middlewareList?: MiddlewareOrKey[];
124
+ /**
125
+ * Optional next handler
126
+ */
84
127
  next?: Next;
85
128
  }
86
129
  /**
@@ -173,6 +216,22 @@ export interface MiddlewareBindingOptions extends BaseMiddlewareBindingOptions<M
173
216
  * Name of the middleware extension point. Default to `DEFAULT_MIDDLEWARE_CHAIN`.
174
217
  */
175
218
  chain?: string;
219
+ /**
220
+ * An array of group names for upstream middleware in the cascading order.
221
+ *
222
+ * For example, the `invokeMethod` depends on `parseParams` for request
223
+ * processing. The `upstreamGroups` for `invokeMethod` should be
224
+ * `['parseParams']`. The order of groups in the array does not matter.
225
+ */
226
+ upstreamGroups?: string | string[];
227
+ /**
228
+ * An array of group names for downstream middleware in the cascading order.
229
+ *
230
+ * For example, the `sendResponse` depends on `invokeMethod` for response
231
+ * processing. The `downstreamGroups` for `sendResponse` should be
232
+ * `['invokeMethod']`. The order of groups in the array does not matter.
233
+ */
234
+ downstreamGroups?: string | string[];
176
235
  }
177
236
  /**
178
237
  * Interface for an express middleware factory
@@ -185,3 +244,21 @@ export interface ExpressMiddlewareFactory<C> {
185
244
  * A symbol to store `MiddlewareContext` on the request object
186
245
  */
187
246
  export declare const MIDDLEWARE_CONTEXT: unique symbol;
247
+ /**
248
+ * Constants for middleware groups
249
+ */
250
+ export declare namespace MiddlewareGroups {
251
+ /**
252
+ * Enforce CORS
253
+ */
254
+ const CORS = "cors";
255
+ /**
256
+ * Server OpenAPI specs
257
+ */
258
+ const API_SPEC = "apiSpec";
259
+ /**
260
+ * Default middleware group
261
+ */
262
+ const MIDDLEWARE = "middleware";
263
+ const DEFAULT = "middleware";
264
+ }
package/dist/types.js CHANGED
@@ -4,7 +4,7 @@
4
4
  // This file is licensed under the MIT License.
5
5
  // License text available at https://opensource.org/licenses/MIT
6
6
  Object.defineProperty(exports, "__esModule", { value: true });
7
- exports.MIDDLEWARE_CONTEXT = exports.DEFAULT_MIDDLEWARE_CHAIN = exports.MiddlewareChain = exports.MiddlewareContext = void 0;
7
+ exports.MiddlewareGroups = exports.MIDDLEWARE_CONTEXT = exports.DEFAULT_MIDDLEWARE_CHAIN = exports.MiddlewareChain = exports.MiddlewareContext = exports.Router = void 0;
8
8
  const tslib_1 = require("tslib");
9
9
  const core_1 = require("@loopback/core");
10
10
  const on_finished_1 = tslib_1.__importDefault(require("on-finished"));
@@ -27,8 +27,13 @@ class MiddlewareContext extends core_1.Context {
27
27
  super(parent, name);
28
28
  this.request = request;
29
29
  this.response = response;
30
+ /**
31
+ * A flag to tell if the response is finished.
32
+ */
33
+ this.responseFinished = false;
30
34
  this.setupBindings();
31
35
  on_finished_1.default(this.response, () => {
36
+ this.responseFinished = true;
32
37
  // Close the request context when the http response is finished so that
33
38
  // it can be recycled by GC
34
39
  this.emit('close');
@@ -55,4 +60,23 @@ exports.DEFAULT_MIDDLEWARE_CHAIN = 'middlewareChain.default';
55
60
  * A symbol to store `MiddlewareContext` on the request object
56
61
  */
57
62
  exports.MIDDLEWARE_CONTEXT = Symbol('loopback.middleware.context');
63
+ /**
64
+ * Constants for middleware groups
65
+ */
66
+ var MiddlewareGroups;
67
+ (function (MiddlewareGroups) {
68
+ /**
69
+ * Enforce CORS
70
+ */
71
+ MiddlewareGroups.CORS = 'cors';
72
+ /**
73
+ * Server OpenAPI specs
74
+ */
75
+ MiddlewareGroups.API_SPEC = 'apiSpec';
76
+ /**
77
+ * Default middleware group
78
+ */
79
+ MiddlewareGroups.MIDDLEWARE = 'middleware';
80
+ MiddlewareGroups.DEFAULT = MiddlewareGroups.MIDDLEWARE;
81
+ })(MiddlewareGroups = exports.MiddlewareGroups || (exports.MiddlewareGroups = {}));
58
82
  //# sourceMappingURL=types.js.map
package/dist/types.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":";AAAA,iDAAiD;AACjD,iCAAiC;AACjC,+CAA+C;AAC/C,gEAAgE;;;;AAEhE,yCAQwB;AAExB,sEAAqC;AACrC,iCAA0C;AAE1C,mCAAiE;AAAtC,iGAAA,MAAM,OAAA;AAgBjC;;;GAGG;AACH,MAAa,iBAAkB,SAAQ,cAAO;IAC5C;;;;;;OAMG;IACH,YACkB,OAAgB,EAChB,QAAkB,EAClC,MAAgB,EAChB,IAAa;QAEb,KAAK,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;QALJ,YAAO,GAAP,OAAO,CAAS;QAChB,aAAQ,GAAR,QAAQ,CAAU;QAKlC,IAAI,CAAC,aAAa,EAAE,CAAC;QACrB,qBAAU,CAAC,IAAI,CAAC,QAAQ,EAAE,GAAG,EAAE;YAC7B,uEAAuE;YACvE,2BAA2B;YAC3B,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;YACnB,IAAI,CAAC,KAAK,EAAE,CAAC;QACf,CAAC,CAAC,CAAC;IACL,CAAC;IAES,aAAa;QACrB,IAAI,CAAC,IAAI,CAAC,yBAAkB,CAAC,OAAO,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,CAAC;IACxD,CAAC;CACF;AA3BD,8CA2BC;AA8BD;;;GAGG;AACH,MAAa,eAAgB,SAAQ,8BAEpC;CAAG;AAFJ,0CAEI;AAEJ;;GAEG;AACU,QAAA,wBAAwB,GAAG,yBAAyB,CAAC;AAqIlE;;GAEG;AACU,QAAA,kBAAkB,GAAG,MAAM,CAAC,6BAA6B,CAAC,CAAC"}
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":";AAAA,iDAAiD;AACjD,iCAAiC;AACjC,+CAA+C;AAC/C,gEAAgE;;;;AAEhE,yCASwB;AAExB,sEAAqC;AACrC,iCAA0C;AAE1C,mCAAiE;AAAtC,iGAAA,MAAM,OAAA;AAgBjC;;;GAGG;AACH,MAAa,iBAAkB,SAAQ,cAAO;IAM5C;;;;;;OAMG;IACH,YACkB,OAAgB,EAChB,QAAkB,EAClC,MAAgB,EAChB,IAAa;QAEb,KAAK,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;QALJ,YAAO,GAAP,OAAO,CAAS;QAChB,aAAQ,GAAR,QAAQ,CAAU;QAdpC;;WAEG;QACH,qBAAgB,GAAG,KAAK,CAAC;QAgBvB,IAAI,CAAC,aAAa,EAAE,CAAC;QACrB,qBAAU,CAAC,IAAI,CAAC,QAAQ,EAAE,GAAG,EAAE;YAC7B,IAAI,CAAC,gBAAgB,GAAG,IAAI,CAAC;YAC7B,uEAAuE;YACvE,2BAA2B;YAC3B,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;YACnB,IAAI,CAAC,KAAK,EAAE,CAAC;QACf,CAAC,CAAC,CAAC;IACL,CAAC;IAES,aAAa;QACrB,IAAI,CAAC,IAAI,CAAC,yBAAkB,CAAC,OAAO,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,CAAC;IACxD,CAAC;CACF;AAjCD,8CAiCC;AAmDD;;;GAGG;AACH,MAAa,eAAgB,SAAQ,8BAEpC;CAAG;AAFJ,0CAEI;AAOJ;;GAEG;AACU,QAAA,wBAAwB,GAAG,yBAAyB,CAAC;AAwKlE;;GAEG;AACU,QAAA,kBAAkB,GAAG,MAAM,CAAC,6BAA6B,CAAC,CAAC;AAExE;;GAEG;AACH,IAAiB,gBAAgB,CAgBhC;AAhBD,WAAiB,gBAAgB;IAC/B;;OAEG;IACU,qBAAI,GAAG,MAAM,CAAC;IAE3B;;OAEG;IACU,yBAAQ,GAAG,SAAS,CAAC;IAElC;;OAEG;IACU,2BAAU,GAAG,YAAY,CAAC;IAC1B,wBAAO,GAAG,iBAAA,UAAU,CAAC;AACpC,CAAC,EAhBgB,gBAAgB,GAAhB,wBAAgB,KAAhB,wBAAgB,QAgBhC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@loopback/express",
3
- "version": "1.2.5",
3
+ "version": "1.4.1",
4
4
  "description": "Integrate with Express and expose middleware infrastructure for sequence and interceptors",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -37,28 +37,30 @@
37
37
  "!*/__tests__"
38
38
  ],
39
39
  "dependencies": {
40
- "@loopback/core": "^2.9.1",
41
- "@loopback/http-server": "^2.1.9",
40
+ "@loopback/core": "^2.9.5",
41
+ "@loopback/http-server": "^2.2.0",
42
42
  "@types/body-parser": "^1.19.0",
43
- "@types/express": "^4.17.6",
44
- "@types/express-serve-static-core": "^4.17.8",
45
- "@types/http-errors": "^1.6.3",
43
+ "@types/express": "^4.17.7",
44
+ "@types/express-serve-static-core": "^4.17.9",
45
+ "@types/http-errors": "^1.8.0",
46
46
  "body-parser": "^1.19.0",
47
47
  "debug": "^4.1.1",
48
48
  "express": "^4.17.1",
49
49
  "http-errors": "^1.8.0",
50
50
  "on-finished": "^2.3.0",
51
- "tslib": "^2.0.0"
51
+ "toposort": "^2.0.2",
52
+ "tslib": "^2.0.1"
52
53
  },
53
54
  "devDependencies": {
54
- "@loopback/build": "^6.1.0",
55
- "@loopback/testlab": "^3.2.0",
55
+ "@loopback/build": "^6.2.2",
56
+ "@loopback/testlab": "^3.2.4",
56
57
  "@types/debug": "^4.1.5",
57
- "@types/node": "^10.17.26",
58
+ "@types/node": "^10.17.28",
58
59
  "@types/on-finished": "^2.3.1",
60
+ "@types/toposort": "^2.0.3",
59
61
  "http-errors": "^1.8.0",
60
62
  "source-map-support": "^0.5.19",
61
- "typescript": "~3.9.5"
63
+ "typescript": "~4.0.2"
62
64
  },
63
- "gitHead": "b89db3d3b8be6a36e63e91c2331d217fda7538de"
65
+ "gitHead": "a3f54273814de63819e0d8bc86509f8a737800bb"
64
66
  }
@@ -0,0 +1,35 @@
1
+ // Copyright IBM Corp. 2020. All Rights Reserved.
2
+ // Node module: @loopback/express
3
+ // This file is licensed under the MIT License.
4
+ // License text available at https://opensource.org/licenses/MIT
5
+
6
+ import debugFactory from 'debug';
7
+ import toposort from 'toposort';
8
+ const debug = debugFactory('loopback:middleware');
9
+ /**
10
+ * Sort the groups by their relative order
11
+ * @param orderedGroups - A list of arrays - each of which represents a partial
12
+ * order of groups.
13
+ */
14
+ export function sortListOfGroups(...orderedGroups: string[][]) {
15
+ if (debug.enabled) {
16
+ debug(
17
+ 'Dependency graph: %s',
18
+ orderedGroups.map(edge => edge.join('->')).join(', '),
19
+ );
20
+ }
21
+ const graph: [string, string][] = [];
22
+ for (const groups of orderedGroups) {
23
+ if (groups.length >= 2) {
24
+ groups.reduce((prev: string | undefined, group) => {
25
+ if (typeof prev === 'string') {
26
+ graph.push([prev, group]);
27
+ }
28
+ return group;
29
+ }, undefined);
30
+ }
31
+ }
32
+ const sorted = toposort(graph);
33
+ debug('Sorted groups: %s', sorted.join('->'));
34
+ return sorted;
35
+ }
package/src/index.ts CHANGED
@@ -22,6 +22,7 @@
22
22
  */
23
23
  export * from './express.application';
24
24
  export * from './express.server';
25
+ export * from './group-sorter';
25
26
  export * from './keys';
26
27
  export * from './middleware';
27
28
  export * from './middleware-interceptor';
package/src/keys.ts CHANGED
@@ -4,7 +4,7 @@
4
4
  // License text available at https://opensource.org/licenses/MIT
5
5
 
6
6
  import {BindingKey} from '@loopback/core';
7
- import {MiddlewareContext} from './types';
7
+ import {MiddlewareContext, MiddlewareGroups} from './types';
8
8
 
9
9
  export namespace MiddlewareBindings {
10
10
  /**
@@ -34,4 +34,4 @@ export const MIDDLEWARE_INTERCEPTOR_NAMESPACE = 'globalInterceptors.middleware';
34
34
  /**
35
35
  * Default order group name for Express middleware based global interceptors
36
36
  */
37
- export const DEFAULT_MIDDLEWARE_GROUP = 'middleware';
37
+ export const DEFAULT_MIDDLEWARE_GROUP = MiddlewareGroups.DEFAULT;
package/src/middleware.ts CHANGED
@@ -11,6 +11,7 @@ import {
11
11
  compareBindingsByTag,
12
12
  Constructor,
13
13
  Context,
14
+ ContextView,
14
15
  createBindingFromClass,
15
16
  extensionFilter,
16
17
  extensionFor,
@@ -21,6 +22,7 @@ import {
21
22
  ValueOrPromise,
22
23
  } from '@loopback/core';
23
24
  import debugFactory from 'debug';
25
+ import {sortListOfGroups} from './group-sorter';
24
26
  import {DEFAULT_MIDDLEWARE_GROUP, MIDDLEWARE_NAMESPACE} from './keys';
25
27
  import {
26
28
  createInterceptor,
@@ -133,6 +135,20 @@ export function asMiddleware(
133
135
  binding
134
136
  .apply(extensionFor(options.chain ?? DEFAULT_MIDDLEWARE_CHAIN))
135
137
  .tag({group: options.group ?? DEFAULT_MIDDLEWARE_GROUP});
138
+ const groupsBefore = options.upstreamGroups;
139
+ if (groupsBefore != null) {
140
+ binding.tag({
141
+ upstreamGroups:
142
+ typeof groupsBefore === 'string' ? [groupsBefore] : groupsBefore,
143
+ });
144
+ }
145
+ const groupsAfter = options.downstreamGroups;
146
+ if (groupsAfter != null) {
147
+ binding.tag({
148
+ downstreamGroups:
149
+ typeof groupsAfter === 'string' ? [groupsAfter] : groupsAfter,
150
+ });
151
+ }
136
152
  };
137
153
  }
138
154
 
@@ -199,24 +215,89 @@ export function invokeMiddleware(
199
215
  middlewareCtx.request.originalUrl,
200
216
  options,
201
217
  );
202
- const {chain = DEFAULT_MIDDLEWARE_CHAIN, orderedGroups} = options ?? {};
203
- // Find extensions for the given extension point binding
204
- const filter = extensionFilter(chain);
205
- if (debug.enabled) {
206
- debug(
207
- 'Middleware for extension point "%s":',
208
- chain,
209
- middlewareCtx.find(filter).map(b => b.key),
210
- );
218
+ let keys = options?.middlewareList;
219
+ if (keys == null) {
220
+ const view = new MiddlewareView(middlewareCtx, options);
221
+ keys = view.middlewareBindingKeys;
222
+ view.close();
211
223
  }
212
- const mwChain = new MiddlewareChain(
213
- middlewareCtx,
214
- filter,
215
- compareBindingsByTag('group', orderedGroups),
216
- );
224
+ const mwChain = new MiddlewareChain(middlewareCtx, keys);
217
225
  return mwChain.invokeInterceptors(options?.next);
218
226
  }
219
227
 
228
+ /**
229
+ * Watch middleware binding keys for the given context and sort them by
230
+ * group
231
+ * @param ctx - Context object
232
+ * @param options - Middleware options
233
+ */
234
+ export class MiddlewareView extends ContextView {
235
+ private options: InvokeMiddlewareOptions;
236
+ private keys: string[];
237
+
238
+ constructor(ctx: Context, options?: InvokeMiddlewareOptions) {
239
+ // Find extensions for the given extension point binding
240
+ const filter = extensionFilter(options?.chain ?? DEFAULT_MIDDLEWARE_CHAIN);
241
+ super(ctx, filter);
242
+ this.options = {
243
+ chain: DEFAULT_MIDDLEWARE_CHAIN,
244
+ orderedGroups: [],
245
+ ...options,
246
+ };
247
+ this.buildMiddlewareKeys();
248
+ this.open();
249
+ }
250
+
251
+ refresh() {
252
+ super.refresh();
253
+ this.buildMiddlewareKeys();
254
+ }
255
+
256
+ /**
257
+ * A list of binding keys sorted by group for registered middleware
258
+ */
259
+ get middlewareBindingKeys() {
260
+ return this.keys;
261
+ }
262
+
263
+ private buildMiddlewareKeys() {
264
+ const middlewareBindings = this.bindings;
265
+ if (debug.enabled) {
266
+ debug(
267
+ 'Middleware for extension point "%s":',
268
+ this.options.chain,
269
+ middlewareBindings.map(b => b.key),
270
+ );
271
+ }
272
+
273
+ // Calculate orders from middleware dependencies
274
+ const ordersFromDependencies: string[][] = [];
275
+ middlewareBindings.forEach(b => {
276
+ const group: string = b.tagMap.group ?? DEFAULT_MIDDLEWARE_GROUP;
277
+ const groupsBefore: string[] = b.tagMap.upstreamGroups ?? [];
278
+ groupsBefore.forEach(d => ordersFromDependencies.push([d, group]));
279
+ const groupsAfter: string[] = b.tagMap.downstreamGroups ?? [];
280
+ groupsAfter.forEach(d => ordersFromDependencies.push([group, d]));
281
+ });
282
+
283
+ const order = sortListOfGroups(
284
+ ...ordersFromDependencies,
285
+ this.options.orderedGroups!,
286
+ );
287
+
288
+ /**
289
+ * Validate sorted groups
290
+ */
291
+ if (typeof this.options?.validate === 'function') {
292
+ this.options.validate(order);
293
+ }
294
+
295
+ this.keys = middlewareBindings
296
+ .sort(compareBindingsByTag('group', order))
297
+ .map(b => b.key);
298
+ }
299
+ }
300
+
220
301
  /**
221
302
  * Invoke a list of Express middleware handler functions
222
303
  *
package/src/types.ts CHANGED
@@ -8,6 +8,7 @@ import {
8
8
  Context,
9
9
  GenericInterceptor,
10
10
  GenericInterceptorChain,
11
+ GenericInterceptorOrKey,
11
12
  InvocationContext,
12
13
  Next,
13
14
  ValueOrPromise,
@@ -37,6 +38,11 @@ export type ExpressRequestHandler = RequestHandler;
37
38
  * context (request, response, etc.).
38
39
  */
39
40
  export class MiddlewareContext extends Context implements HandlerContext {
41
+ /**
42
+ * A flag to tell if the response is finished.
43
+ */
44
+ responseFinished = false;
45
+
40
46
  /**
41
47
  * Constructor for `MiddlewareContext`
42
48
  * @param request - Express request object
@@ -53,6 +59,7 @@ export class MiddlewareContext extends Context implements HandlerContext {
53
59
  super(parent, name);
54
60
  this.setupBindings();
55
61
  onFinished(this.response, () => {
62
+ this.responseFinished = true;
56
63
  // Close the request context when the http response is finished so that
57
64
  // it can be recycled by GC
58
65
  this.emit('close');
@@ -68,7 +75,28 @@ export class MiddlewareContext extends Context implements HandlerContext {
68
75
  /**
69
76
  * Interface LoopBack 4 middleware to be executed within sequence of actions.
70
77
  * A middleware for LoopBack is basically a generic interceptor that uses
71
- * `RequestContext`.
78
+ * `MiddlewareContext`.
79
+ *
80
+ * @remarks
81
+ *
82
+ * The middleware function is responsible for processing HTTP requests and
83
+ * responses. It typically includes the following logic.
84
+ *
85
+ * 1. Process the request with one of the following outcome
86
+ * - Reject the request by throwing an error if request is invalid, such as
87
+ * validation or authentication failures
88
+ * - Produce a response by itself, such as from the cache
89
+ * - Proceed by calling `await next()` to invoke downstream middleware. When
90
+ * `await next()` returns, it goes to step 2. If an error thrown from
91
+ * `await next()`, step 3 handles the error.
92
+ *
93
+ * 2. Process the response with one the following outcome
94
+ * - Reject the response by throwing an error
95
+ * - Replace the response with its own value
96
+ * - Return the response to upstream middleware
97
+ *
98
+ * 3. Catch the error thrown from `await next()`. If the `catch` block does not
99
+ * exist, the error will be bubbled up to upstream middleware
72
100
  *
73
101
  * The signature of a middleware function is described at
74
102
  * {@link https://loopback.io/doc/en/lb4/apidocs.express.middleware.html | Middleware}.
@@ -101,6 +129,11 @@ export class MiddlewareChain extends GenericInterceptorChain<
101
129
  MiddlewareContext
102
130
  > {}
103
131
 
132
+ /**
133
+ * A middleware function or binding key
134
+ */
135
+ export type MiddlewareOrKey = GenericInterceptorOrKey<MiddlewareContext>;
136
+
104
137
  /**
105
138
  * Default extension point name for middleware
106
139
  */
@@ -115,11 +148,28 @@ export interface InvokeMiddlewareOptions {
115
148
  * from the binding
116
149
  */
117
150
  chain?: string;
151
+
118
152
  /**
119
- * An array of group names to denote the order of execution
153
+ * An array of group names to denote the order of execution, such as
154
+ * `['cors', 'caching', 'rate-limiting']`.
120
155
  */
121
156
  orderedGroups?: string[];
122
157
 
158
+ /**
159
+ * An optional function to validate the sorted groups before invoking the
160
+ * middleware chain
161
+ */
162
+ validate?: (groups: string[]) => void;
163
+
164
+ /**
165
+ * Pre-built middleware list. If set, the list will be used to create the
166
+ * middleware chain without discovering again within the context.
167
+ */
168
+ middlewareList?: MiddlewareOrKey[];
169
+
170
+ /**
171
+ * Optional next handler
172
+ */
123
173
  next?: Next;
124
174
  }
125
175
 
@@ -227,6 +277,24 @@ export interface MiddlewareBindingOptions
227
277
  * Name of the middleware extension point. Default to `DEFAULT_MIDDLEWARE_CHAIN`.
228
278
  */
229
279
  chain?: string;
280
+
281
+ /**
282
+ * An array of group names for upstream middleware in the cascading order.
283
+ *
284
+ * For example, the `invokeMethod` depends on `parseParams` for request
285
+ * processing. The `upstreamGroups` for `invokeMethod` should be
286
+ * `['parseParams']`. The order of groups in the array does not matter.
287
+ */
288
+ upstreamGroups?: string | string[];
289
+
290
+ /**
291
+ * An array of group names for downstream middleware in the cascading order.
292
+ *
293
+ * For example, the `sendResponse` depends on `invokeMethod` for response
294
+ * processing. The `downstreamGroups` for `sendResponse` should be
295
+ * `['invokeMethod']`. The order of groups in the array does not matter.
296
+ */
297
+ downstreamGroups?: string | string[];
230
298
  }
231
299
 
232
300
  /**
@@ -241,3 +309,24 @@ export interface ExpressMiddlewareFactory<C> {
241
309
  * A symbol to store `MiddlewareContext` on the request object
242
310
  */
243
311
  export const MIDDLEWARE_CONTEXT = Symbol('loopback.middleware.context');
312
+
313
+ /**
314
+ * Constants for middleware groups
315
+ */
316
+ export namespace MiddlewareGroups {
317
+ /**
318
+ * Enforce CORS
319
+ */
320
+ export const CORS = 'cors';
321
+
322
+ /**
323
+ * Server OpenAPI specs
324
+ */
325
+ export const API_SPEC = 'apiSpec';
326
+
327
+ /**
328
+ * Default middleware group
329
+ */
330
+ export const MIDDLEWARE = 'middleware';
331
+ export const DEFAULT = MIDDLEWARE;
332
+ }