@opra/core 0.14.0 → 0.16.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.
Files changed (105) hide show
  1. package/cjs/adapter/adapter.js +89 -293
  2. package/cjs/adapter/http/express-adapter.js +27 -0
  3. package/cjs/adapter/http/http-adapter.js +467 -0
  4. package/cjs/adapter/http/http-request-context.host.js +28 -0
  5. package/cjs/adapter/http/http-request.host.js +14 -0
  6. package/cjs/adapter/http/http-response.host.js +14 -0
  7. package/cjs/adapter/internal/metadata.resource.js +27 -0
  8. package/cjs/adapter/request-context.host.js +30 -0
  9. package/cjs/adapter/request.host.js +19 -0
  10. package/cjs/adapter/response.host.js +20 -0
  11. package/cjs/augmentation/resource.augmentation.js +26 -0
  12. package/cjs/index.js +7 -8
  13. package/esm/adapter/adapter.js +97 -297
  14. package/esm/adapter/http/express-adapter.js +27 -0
  15. package/esm/adapter/http/http-adapter.js +467 -0
  16. package/esm/adapter/http/http-request-context.host.js +28 -0
  17. package/esm/adapter/http/http-request.host.js +14 -0
  18. package/esm/adapter/http/http-response.host.js +14 -0
  19. package/esm/adapter/interfaces/request-context.interface.js +2 -0
  20. package/esm/adapter/interfaces/request.interface.js +2 -0
  21. package/esm/adapter/interfaces/response.interface.js +2 -0
  22. package/esm/adapter/internal/metadata.resource.js +27 -0
  23. package/esm/adapter/request-context.host.js +30 -0
  24. package/esm/adapter/request.host.js +19 -0
  25. package/esm/adapter/response.host.js +20 -0
  26. package/esm/augmentation/resource.augmentation.js +26 -0
  27. package/esm/index.js +13 -11
  28. package/esm/types.js +2 -1
  29. package/i18n/en/error.json +2 -2
  30. package/package.json +11 -8
  31. package/types/adapter/adapter.d.ts +63 -0
  32. package/types/adapter/http/express-adapter.d.ts +11 -0
  33. package/types/adapter/http/http-adapter.d.ts +37 -0
  34. package/types/adapter/http/http-request-context.host.d.ts +16 -0
  35. package/types/adapter/http/http-request.host.d.ts +7 -0
  36. package/types/adapter/http/http-response.host.d.ts +7 -0
  37. package/{esm → types/adapter}/interfaces/logger.interface.d.ts +1 -0
  38. package/types/adapter/interfaces/request-context.interface.d.ts +25 -0
  39. package/types/adapter/interfaces/request.interface.d.ts +13 -0
  40. package/types/adapter/interfaces/response.interface.d.ts +22 -0
  41. package/types/adapter/internal/metadata.resource.d.ts +7 -0
  42. package/types/adapter/request-context.host.d.ts +19 -0
  43. package/types/adapter/request.host.d.ts +26 -0
  44. package/types/adapter/response.host.d.ts +20 -0
  45. package/types/augmentation/resource.augmentation.d.ts +33 -0
  46. package/types/index.d.ts +10 -0
  47. package/cjs/adapter/classes/execution-context.host.js +0 -16
  48. package/cjs/adapter/classes/express-request-wrapper.host.js +0 -36
  49. package/cjs/adapter/classes/express-response-wrapper.host.js +0 -55
  50. package/cjs/adapter/classes/http-execution-context.host.js +0 -28
  51. package/cjs/adapter/classes/metadata.resource.js +0 -22
  52. package/cjs/adapter/express-adapter.js +0 -26
  53. package/cjs/adapter/http-adapter.js +0 -443
  54. package/cjs/adapter/request-contexts/batch-request-context.js +0 -11
  55. package/cjs/adapter/request-contexts/request-context.js +0 -25
  56. package/cjs/adapter/request-contexts/single-request-context.js +0 -14
  57. package/cjs/services/data-service.js +0 -9
  58. package/cjs/services/json-singleton-service.js +0 -96
  59. package/cjs/utils/create-i18n.js +0 -21
  60. package/cjs/utils/get-caller-file.util.js +0 -24
  61. package/esm/adapter/adapter.d.ts +0 -38
  62. package/esm/adapter/classes/execution-context.host.d.ts +0 -10
  63. package/esm/adapter/classes/execution-context.host.js +0 -12
  64. package/esm/adapter/classes/express-request-wrapper.host.d.ts +0 -19
  65. package/esm/adapter/classes/express-request-wrapper.host.js +0 -32
  66. package/esm/adapter/classes/express-response-wrapper.host.d.ts +0 -22
  67. package/esm/adapter/classes/express-response-wrapper.host.js +0 -51
  68. package/esm/adapter/classes/http-execution-context.host.d.ts +0 -13
  69. package/esm/adapter/classes/http-execution-context.host.js +0 -24
  70. package/esm/adapter/classes/metadata.resource.d.ts +0 -8
  71. package/esm/adapter/classes/metadata.resource.js +0 -19
  72. package/esm/adapter/express-adapter.d.ts +0 -11
  73. package/esm/adapter/express-adapter.js +0 -21
  74. package/esm/adapter/http-adapter.d.ts +0 -37
  75. package/esm/adapter/http-adapter.js +0 -439
  76. package/esm/adapter/request-contexts/batch-request-context.d.ts +0 -7
  77. package/esm/adapter/request-contexts/batch-request-context.js +0 -7
  78. package/esm/adapter/request-contexts/request-context.d.ts +0 -22
  79. package/esm/adapter/request-contexts/request-context.js +0 -21
  80. package/esm/adapter/request-contexts/single-request-context.d.ts +0 -10
  81. package/esm/adapter/request-contexts/single-request-context.js +0 -10
  82. package/esm/enums/issue-severity.enum.d.ts +0 -1
  83. package/esm/enums/issue-severity.enum.js +0 -1
  84. package/esm/index.d.ts +0 -11
  85. package/esm/interfaces/execution-context.interface.d.ts +0 -47
  86. package/esm/interfaces/execution-context.interface.js +0 -1
  87. package/esm/interfaces/i18n-options.interface.d.ts +0 -28
  88. package/esm/interfaces/i18n-options.interface.js +0 -1
  89. package/esm/interfaces/logger.interface.js +0 -1
  90. package/esm/interfaces/resource.interface.d.ts +0 -23
  91. package/esm/interfaces/resource.interface.js +0 -1
  92. package/esm/services/data-service.d.ts +0 -2
  93. package/esm/services/data-service.js +0 -5
  94. package/esm/services/json-singleton-service.d.ts +0 -39
  95. package/esm/services/json-singleton-service.js +0 -91
  96. package/esm/utils/create-i18n.d.ts +0 -3
  97. package/esm/utils/create-i18n.js +0 -16
  98. package/esm/utils/get-caller-file.util.d.ts +0 -1
  99. package/esm/utils/get-caller-file.util.js +0 -20
  100. /package/cjs/{interfaces → adapter/interfaces}/logger.interface.js +0 -0
  101. /package/cjs/{enums/issue-severity.enum.js → adapter/interfaces/request-context.interface.js} +0 -0
  102. /package/cjs/{interfaces/execution-context.interface.js → adapter/interfaces/request.interface.js} +0 -0
  103. /package/cjs/{interfaces/i18n-options.interface.js → adapter/interfaces/response.interface.js} +0 -0
  104. /package/{cjs/interfaces/resource.interface.js → esm/adapter/interfaces/logger.interface.js} +0 -0
  105. /package/{esm → types}/types.d.ts +0 -0
@@ -1,318 +1,118 @@
1
- import { Task } from 'power-tasks';
2
- import { AsyncEventEmitter } from 'strict-typed-events';
3
- import { CollectionCountQuery, CollectionResourceInfo, CollectionSearchQuery, ComplexType, FailedDependencyError, ForbiddenError, HttpHeaderCodes, I18n, InternalServerError, ResourceNotFoundError, ResponsiveMap, SingletonResourceInfo, translate, wrapException } from '@opra/common';
4
- import { createI18n } from '../utils/create-i18n.js';
5
- import { MetadataResource } from './classes/metadata.resource.js';
6
- import { BatchRequestContext } from './request-contexts/batch-request-context.js';
7
- import { SingleRequestContext } from './request-contexts/single-request-context.js';
8
- const noOp = () => void 0;
9
- export class OpraAdapter {
10
- constructor(document) {
11
- this.document = document;
12
- this._internalResources = new ResponsiveMap();
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.OpraAdapter = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const path_1 = tslib_1.__importDefault(require("path"));
6
+ const strict_typed_events_1 = require("strict-typed-events");
7
+ const common_1 = require("@opra/common");
8
+ const metadata_resource_js_1 = require("./internal/metadata.resource.js");
9
+ /**
10
+ * @class OpraAdapter
11
+ */
12
+ class OpraAdapter extends strict_typed_events_1.AsyncEventEmitter {
13
+ constructor(api) {
14
+ super();
15
+ this.api = api;
13
16
  }
14
- async close() {
15
- const promises = [];
16
- for (const r of this.document.resources.values()) {
17
- if (r.instance) {
18
- const shutDown = r.instance.shutDown;
19
- if (shutDown)
20
- promises.push((async () => shutDown.call(r.instance))());
21
- }
22
- }
23
- await Promise.allSettled(promises);
24
- }
25
- async handler(executionContext) {
26
- let requestContext;
27
- let failed = false;
28
- try {
29
- if (this.userContextResolver)
30
- executionContext.userContext = this.userContextResolver(executionContext);
31
- requestContext = await this.parse(executionContext);
32
- const task = this._requestContextToTask(executionContext, requestContext);
33
- await task.toPromise().catch(noOp);
34
- await this.sendResponse(executionContext, requestContext);
35
- }
36
- catch (e) {
37
- failed = true;
38
- const error = wrapException(e);
39
- await this.sendError(executionContext, error);
40
- if (this.logger)
41
- try {
42
- this.logger.error(e);
43
- }
44
- catch {
45
- // noop
46
- }
47
- }
48
- finally {
49
- if (executionContext instanceof AsyncEventEmitter) {
50
- await executionContext
51
- .emitAsyncSerial('finish', {
52
- context: executionContext,
53
- failed
54
- }).catch();
55
- }
56
- }
57
- }
58
- _requestContextToTask(executionContext, requestContext) {
59
- if (requestContext instanceof BatchRequestContext) {
60
- const children = requestContext.queries.map(q => this._requestContextToTask(executionContext, q));
61
- return new Task(children, { bail: true });
62
- }
63
- else if (requestContext instanceof SingleRequestContext) {
64
- const { query } = requestContext;
65
- const task = new Task(async () => {
66
- if (query.resource) {
67
- const { resource } = query;
68
- // call pre_xxx method
69
- const fn = resource.metadata['pre_' + query.method];
70
- if (fn && typeof fn === 'function') {
71
- await fn(requestContext);
72
- }
73
- await this._executeResourceQuery(this.document, resource, requestContext);
74
- // todo execute sub property queries
75
- return;
76
- }
77
- throw new InternalServerError('Not implemented yet');
78
- }, {
79
- id: requestContext.contentId,
80
- exclusive: query.operation !== 'read'
81
- });
82
- task.on('finish', () => {
83
- if (task.error) {
84
- if (task.failedDependencies)
85
- requestContext.errors.push(new FailedDependencyError());
86
- else
87
- requestContext.errors.push(wrapException(task.error));
88
- if (this.logger)
89
- try {
90
- this.logger.error(task.error);
91
- }
92
- catch {
93
- // noop
94
- }
95
- return;
96
- }
97
- });
98
- return task;
99
- }
100
- /* istanbul ignore next */
101
- throw new TypeError('Invalid request context instance');
102
- }
103
- async _init(options) {
17
+ /**
18
+ * Initializes the adapter
19
+ * @param options
20
+ * @protected
21
+ */
22
+ async init(options) {
104
23
  this.logger = options?.logger;
105
- if (options?.i18n instanceof I18n)
24
+ if (options?.i18n instanceof common_1.I18n)
106
25
  this.i18n = options.i18n;
107
26
  else if (typeof options?.i18n === 'function')
108
27
  this.i18n = await options.i18n();
109
28
  else
110
- this.i18n = await createI18n(options?.i18n);
111
- this.i18n = this.i18n || I18n.defaultInstance;
29
+ this.i18n = await this._createI18n(options?.i18n);
30
+ this.i18n = this.i18n || common_1.I18n.defaultInstance;
112
31
  if (!this.i18n.isInitialized)
113
32
  await this.i18n.init();
114
- this.userContextResolver = options?.userContext;
115
- const metadataResource = new MetadataResource();
116
- const metadataResourceInfo = new SingletonResourceInfo(this.document, '$metadata', this.document.getComplexDataType('object'), {
117
- kind: 'SingletonResource',
118
- type: 'object',
119
- instance: metadataResource,
120
- get: {
121
- handler: metadataResource.get.bind(metadataResource)
122
- }
33
+ if (options?.onRequest)
34
+ this.on('request', options.onRequest);
35
+ this._internalDoc = await common_1.DocumentFactory.createDocument({
36
+ version: common_1.OpraSchema.SpecVersion,
37
+ info: {
38
+ version: common_1.OpraSchema.SpecVersion,
39
+ title: 'Internal resources',
40
+ },
41
+ references: { 'api': this.api },
42
+ resources: [new metadata_resource_js_1.MetadataResource(this.api)]
123
43
  });
124
- this._internalResources.set(metadataResourceInfo.name, metadataResourceInfo);
125
- metadataResource.init(metadataResourceInfo);
126
- for (const r of this.document.resources.values()) {
127
- if (r.instance) {
128
- const init = r.instance.init;
129
- if (init)
130
- await init.call(r.instance, r);
131
- }
44
+ const promises = [];
45
+ for (const r of this.api.resources.values()) {
46
+ const onInit = r.onInit;
47
+ if (onInit)
48
+ promises.push((async () => onInit.call(r.controller, r))());
132
49
  }
50
+ await Promise.all(promises);
133
51
  }
134
- async _executeResourceQuery(document, resource, context) {
135
- if (resource instanceof CollectionResourceInfo) {
136
- const { query } = context;
137
- if (query instanceof CollectionSearchQuery) {
138
- const promises = [];
139
- let search;
140
- promises.push(this._executeCollectionResource(document, resource, context)
141
- .then(v => search = v));
142
- if (query.count && resource.metadata.count) {
143
- const ctx = {
144
- query: new CollectionCountQuery(query.resource, { filter: query.filter }),
145
- resultPath: ''
146
- };
147
- Object.setPrototypeOf(ctx, context);
148
- promises.push(this._executeCollectionResource(document, resource, ctx)
149
- .then(r => {
150
- context.responseHeaders[HttpHeaderCodes.X_Opra_Count] = r;
151
- }));
152
- }
153
- await Promise.all(promises);
154
- context.response = search;
155
- return;
156
- }
157
- context.response = await this._executeCollectionResource(document, resource, context);
158
- return;
159
- }
160
- else if (resource instanceof SingletonResourceInfo) {
161
- context.response = await this._executeSingletonResource(document, resource, context);
162
- return;
52
+ /**
53
+ * Calls shutDown hook for all resources
54
+ */
55
+ async close() {
56
+ const promises = [];
57
+ for (const r of this.api.resources.values()) {
58
+ const onShutdown = r.onShutdown;
59
+ if (onShutdown)
60
+ promises.push((async () => onShutdown.call(r.controller, r))());
163
61
  }
164
- throw new Error(`Executing "${resource.kind}" has not been implemented yet`);
62
+ await Promise.allSettled(promises);
165
63
  }
166
- async _executeCollectionResource(document, resource, context) {
167
- const method = context.query.method;
168
- const handler = resource.getHandler(method);
169
- if (!handler)
170
- throw new ForbiddenError({
171
- message: translate('RESOLVER_FORBIDDEN', { method }, `The resource endpoint does not accept '{{method}}' operations`),
172
- severity: 'error',
173
- code: 'RESOLVER_FORBIDDEN'
174
- });
175
- let result;
176
- switch (method) {
177
- case 'create': {
178
- const query = context.query;
179
- result = await handler(context, query.data, query);
180
- result = Array.isArray(result) ? result[0] : result;
181
- if (result)
182
- context.status = 201;
183
- context.responseHeaders[HttpHeaderCodes.X_Opra_DataType] = resource.dataType.name;
184
- return result;
185
- }
186
- case 'count': {
187
- const query = context.query;
188
- result = await handler(context, query);
189
- return result;
190
- }
191
- case 'get': {
192
- const query = context.query;
193
- result = await handler(context, query.keyValue, query);
194
- result = Array.isArray(result) ? result[0] : result;
195
- if (!result)
196
- throw new ResourceNotFoundError(resource.name, query.keyValue);
197
- const v = await this._pathWalkThrough(query, query.dataType, result, resource.name);
198
- if (v.value === undefined)
199
- throw new ResourceNotFoundError(v.path);
200
- if (v.dataType)
201
- context.responseHeaders[HttpHeaderCodes.X_Opra_DataType] = v.dataType.name;
202
- return v.value;
203
- }
204
- case 'search': {
205
- const query = context.query;
206
- result = await handler(context, query);
207
- const items = Array.isArray(result) ? result : (context.response ? [result] : []);
208
- context.responseHeaders[HttpHeaderCodes.X_Opra_DataType] = resource.dataType.name;
209
- return items;
210
- }
211
- case 'update': {
212
- const query = context.query;
213
- result = await handler(context, query.keyValue, query.data, query);
214
- result = Array.isArray(result) ? result[0] : result;
215
- if (!result)
216
- throw new ResourceNotFoundError(resource.name, query.keyValue);
217
- context.responseHeaders[HttpHeaderCodes.X_Opra_DataType] = resource.dataType.name;
218
- return result;
219
- }
220
- case 'delete':
221
- case 'deleteMany':
222
- case 'updateMany': {
223
- switch (method) {
224
- case 'delete': {
225
- const query = context.query;
226
- result = await handler(context, query.keyValue, query);
227
- break;
228
- }
229
- case 'deleteMany': {
230
- const query = context.query;
231
- result = await handler(context, query);
232
- break;
233
- }
234
- case 'updateMany': {
235
- const query = context.query;
236
- result = await handler(context, query.data, query);
237
- break;
238
- }
239
- }
240
- let affected;
241
- if (typeof result === 'number')
242
- affected = result;
243
- if (typeof result === 'boolean')
244
- affected = result ? 1 : 0;
245
- if (typeof result === 'object')
246
- affected = result.affectedRows || result.affected;
247
- return {
248
- operation: context.query.method,
64
+ async executeRequest(context) {
65
+ await this.emitAsync('request', context);
66
+ const { request, response } = context;
67
+ const { resource, operation } = request;
68
+ if (resource instanceof common_1.Collection || resource instanceof common_1.Singleton) {
69
+ const endpoint = resource.operations[operation];
70
+ if (!endpoint?.handler)
71
+ throw new common_1.ForbiddenError({
72
+ message: (0, common_1.translate)('RESOLVER_FORBIDDEN', { operation }, `The resource endpoint does not accept '{{operation}}' operations`),
73
+ severity: 'error',
74
+ code: 'RESOLVER_FORBIDDEN'
75
+ });
76
+ const value = await endpoint.handler(context);
77
+ // if (value == null)
78
+ if (value != null)
79
+ response.value = value;
80
+ const { crud, many } = request;
81
+ if (crud === 'delete' || (crud === 'update' && many)) {
82
+ let affected = 0;
83
+ if (typeof response.value === 'number')
84
+ affected = response.value;
85
+ if (typeof response.value === 'boolean')
86
+ affected = response.value ? 1 : 0;
87
+ if (typeof response.value === 'object')
88
+ affected = response.value.affectedRows || response.value.affected;
89
+ response.value = {
90
+ operation: request.operation,
249
91
  affected
250
92
  };
251
93
  }
252
- }
253
- }
254
- async _executeSingletonResource(document, resource, context) {
255
- const method = context.query.method;
256
- const resolverInfo = resource.metadata[method];
257
- if (!(resolverInfo && resolverInfo.handler))
258
- throw new ForbiddenError({
259
- message: translate('RESOLVER_FORBIDDEN', { method }, `The resource endpoint does not accept '{{method}}' operations`),
260
- severity: 'error',
261
- code: 'RESOLVER_FORBIDDEN'
262
- });
263
- let result = await resolverInfo.handler(context);
264
- switch (method) {
265
- case 'get': {
266
- const query = context.query;
267
- result = await resolverInfo.handler(context, query);
268
- result = Array.isArray(result) ? result[0] : result;
269
- if (!result)
270
- throw new ResourceNotFoundError(resource.name);
271
- const v = await this._pathWalkThrough(query, query.dataType, result, resource.name);
272
- if (v.value === undefined)
273
- throw new ResourceNotFoundError(v.path);
274
- if (v.dataType)
275
- context.responseHeaders[HttpHeaderCodes.X_Opra_DataType] = v.dataType.name;
276
- return v.value;
277
- }
278
- }
279
- if (!result)
280
- return;
281
- result = Array.isArray(result) ? result[0] : result;
282
- let dataType = resource.dataType;
283
- if (context.resultPath) {
284
- const pathArray = context.resultPath.split('.');
285
- for (const field of pathArray) {
286
- const prop = dataType instanceof ComplexType ? dataType.fields.get(field) : undefined;
287
- dataType = prop && prop.type ? this.document.types.get(prop.type) : undefined;
288
- result = result && typeof result === 'object' && result[field];
94
+ else if (response.value != null) {
95
+ if (!request.many)
96
+ response.value = Array.isArray(response.value) ? response.value[0] : response.value;
97
+ else
98
+ response.value = Array.isArray(response.value) ? response.value : [response.value];
289
99
  }
100
+ if ((request.operation === 'get' || request.operation === 'update') && response.value == null)
101
+ throw new common_1.ResourceNotFoundError(resource.name, request.args.key);
290
102
  }
291
- if (method === 'create')
292
- context.status = 201;
293
- context.responseHeaders[HttpHeaderCodes.X_Opra_DataType] = resource.dataType.name;
294
- return result;
295
103
  }
296
- async _pathWalkThrough(query, dataType, value, parentPath) {
297
- const { child } = query;
298
- if (!child)
299
- return { value, dataType, path: parentPath };
300
- // Make a case in sensitive lookup
301
- const fieldNameLower = child.fieldName.toLowerCase();
302
- const path = parentPath + (parentPath ? '.' : '') + child.fieldName;
303
- for (const key of Object.keys(value)) {
304
- if (key.toLowerCase() === fieldNameLower) {
305
- let v = value[key];
306
- if (v == null)
307
- return { path };
308
- if (child.child && child.dataType instanceof ComplexType) {
309
- if (Array.isArray(v))
310
- v = v[0];
311
- return this._pathWalkThrough(child, child.dataType, v, path);
312
- }
313
- return { value: v, dataType: child.dataType, path };
314
- }
315
- }
316
- return { path };
104
+ async _createI18n(options) {
105
+ const opts = {
106
+ ...options,
107
+ };
108
+ delete opts.resourceDirs;
109
+ const instance = common_1.I18n.createInstance(opts);
110
+ await instance.init();
111
+ await instance.loadResourceDir(path_1.default.resolve((0, common_1.getStackFileName)(), '../../../i18n'));
112
+ if (options?.resourceDirs)
113
+ for (const dir of options.resourceDirs)
114
+ await instance.loadResourceDir(dir);
115
+ return instance;
317
116
  }
318
117
  }
118
+ exports.OpraAdapter = OpraAdapter;
@@ -0,0 +1,27 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.OpraExpressAdapter = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const body_parser_1 = tslib_1.__importDefault(require("body-parser"));
6
+ const common_1 = require("@opra/common");
7
+ const http_adapter_js_1 = require("./http-adapter.js");
8
+ const noOp = () => void 0;
9
+ class OpraExpressAdapter extends http_adapter_js_1.OpraHttpAdapter {
10
+ constructor() {
11
+ super(...arguments);
12
+ this.platform = 'express';
13
+ }
14
+ static async create(app, document, options) {
15
+ const adapter = new OpraExpressAdapter(document);
16
+ await adapter.init(options);
17
+ const prefix = '/' + (0, common_1.normalizePath)(options?.prefix, true);
18
+ app.use(prefix, body_parser_1.default.json());
19
+ app.use(prefix, (req, res, next) => {
20
+ req.end = noOp;
21
+ req.send = noOp;
22
+ adapter.handler(req, res).catch(e => next(e));
23
+ });
24
+ return adapter;
25
+ }
26
+ }
27
+ exports.OpraExpressAdapter = OpraExpressAdapter;