@opra/core 1.0.0-alpha.3 → 1.0.0-alpha.31

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 (63) hide show
  1. package/cjs/augmentation/18n.augmentation.js +1 -1
  2. package/cjs/constants.js +1 -2
  3. package/cjs/execution-context.js +1 -1
  4. package/cjs/http/express-adapter.js +25 -34
  5. package/cjs/http/http-adapter.js +2 -5
  6. package/cjs/http/http-context.js +20 -32
  7. package/cjs/http/{impl/http-handler.js → http-handler.js} +249 -213
  8. package/cjs/http/impl/http-incoming.host.js +3 -3
  9. package/cjs/http/impl/http-outgoing.host.js +2 -2
  10. package/cjs/http/impl/multipart-reader.js +141 -50
  11. package/cjs/http/impl/node-incoming-message.host.js +5 -3
  12. package/cjs/http/interfaces/node-incoming-message.interface.js +3 -2
  13. package/cjs/http/utils/body-reader.js +6 -5
  14. package/cjs/http/utils/common.js +6 -5
  15. package/cjs/http/utils/concat-readable.js +1 -2
  16. package/cjs/http/utils/convert-to-headers.js +2 -3
  17. package/cjs/http/utils/convert-to-raw-headers.js +1 -2
  18. package/cjs/http/utils/match-known-fields.js +2 -2
  19. package/cjs/http/utils/wrap-exception.js +1 -2
  20. package/cjs/index.js +4 -4
  21. package/cjs/platform-adapter.js +1 -4
  22. package/cjs/type-guards.js +4 -5
  23. package/esm/augmentation/18n.augmentation.js +1 -1
  24. package/esm/constants.js +0 -1
  25. package/esm/execution-context.js +1 -1
  26. package/esm/http/express-adapter.js +25 -34
  27. package/esm/http/http-adapter.js +2 -5
  28. package/esm/http/http-context.js +21 -33
  29. package/esm/http/{impl/http-handler.js → http-handler.js} +243 -207
  30. package/esm/http/impl/http-incoming.host.js +3 -3
  31. package/esm/http/impl/http-outgoing.host.js +2 -2
  32. package/esm/http/impl/multipart-reader.js +142 -51
  33. package/esm/http/impl/node-incoming-message.host.js +5 -3
  34. package/esm/http/interfaces/node-incoming-message.interface.js +3 -2
  35. package/esm/http/utils/body-reader.js +6 -5
  36. package/esm/http/utils/common.js +2 -1
  37. package/esm/index.js +4 -4
  38. package/esm/platform-adapter.js +1 -4
  39. package/package.json +21 -14
  40. package/types/augmentation/18n.augmentation.d.ts +1 -1
  41. package/types/constants.d.ts +0 -1
  42. package/types/execution-context.d.ts +2 -3
  43. package/types/http/express-adapter.d.ts +1 -1
  44. package/types/http/http-adapter.d.ts +35 -8
  45. package/types/http/http-context.d.ts +4 -4
  46. package/types/http/{impl/http-handler.d.ts → http-handler.d.ts} +11 -9
  47. package/types/http/impl/http-incoming.host.d.ts +1 -2
  48. package/types/http/impl/http-outgoing.host.d.ts +1 -1
  49. package/types/http/impl/multipart-reader.d.ts +38 -20
  50. package/types/http/impl/node-incoming-message.host.d.ts +2 -6
  51. package/types/http/impl/node-outgoing-message.host.d.ts +4 -7
  52. package/types/http/interfaces/http-incoming.interface.d.ts +1 -2
  53. package/types/http/interfaces/http-outgoing.interface.d.ts +1 -1
  54. package/types/http/interfaces/node-incoming-message.interface.d.ts +0 -2
  55. package/types/http/interfaces/node-outgoing-message.interface.d.ts +0 -2
  56. package/types/http/utils/body-reader.d.ts +2 -5
  57. package/types/http/utils/concat-readable.d.ts +0 -1
  58. package/types/http/utils/convert-to-raw-headers.d.ts +0 -1
  59. package/types/index.d.ts +4 -4
  60. package/types/platform-adapter.d.ts +1 -5
  61. package/cjs/helpers/logger.js +0 -35
  62. package/esm/helpers/logger.js +0 -31
  63. package/types/helpers/logger.d.ts +0 -14
@@ -1,9 +1,9 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const tslib_1 = require("tslib");
4
+ const common_1 = require("@opra/common");
4
5
  const fs_1 = tslib_1.__importDefault(require("fs"));
5
6
  const path_1 = tslib_1.__importDefault(require("path"));
6
- const common_1 = require("@opra/common");
7
7
  common_1.I18n.load = async function (options) {
8
8
  const opts = {
9
9
  ...options,
package/cjs/constants.js CHANGED
@@ -1,5 +1,4 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.kAssetCache = exports.kHandler = void 0;
4
- exports.kHandler = Symbol.for('kHandler');
3
+ exports.kAssetCache = void 0;
5
4
  exports.kAssetCache = Symbol.for('kAssetCache');
@@ -8,10 +8,10 @@ const strict_typed_events_1 = require("strict-typed-events");
8
8
  class ExecutionContext extends strict_typed_events_1.AsyncEventEmitter {
9
9
  constructor(init) {
10
10
  super();
11
+ this.errors = [];
11
12
  this.document = init.document;
12
13
  this.protocol = init.protocol;
13
14
  this.platform = init.platform;
14
- this.platformArgs = init.platformArgs;
15
15
  }
16
16
  addListener(event, listener) {
17
17
  return super.addListener(event, listener);
@@ -2,14 +2,14 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.ExpressAdapter = void 0;
4
4
  const tslib_1 = require("tslib");
5
+ const common_1 = require("@opra/common");
5
6
  const express_1 = require("express");
6
7
  const nodePath = tslib_1.__importStar(require("path"));
7
- const common_1 = require("@opra/common");
8
- const constants_js_1 = require("../constants.js");
9
8
  const http_adapter_js_1 = require("./http-adapter.js");
10
9
  const http_context_js_1 = require("./http-context.js");
11
10
  const http_incoming_interface_js_1 = require("./interfaces/http-incoming.interface.js");
12
11
  const http_outgoing_interface_js_1 = require("./interfaces/http-outgoing.interface.js");
12
+ const wrap_exception_1 = require("./utils/wrap-exception");
13
13
  class ExpressAdapter extends http_adapter_js_1.HttpAdapter {
14
14
  constructor(app, document, options) {
15
15
  super(document, options);
@@ -35,13 +35,15 @@ class ExpressAdapter extends http_adapter_js_1.HttpAdapter {
35
35
  }
36
36
  if (resource.onShutdown) {
37
37
  const instance = this._controllerInstances.get(resource) || resource.instance;
38
- if (instance)
38
+ if (instance) {
39
39
  try {
40
40
  await resource.onShutdown.call(instance, resource);
41
41
  }
42
42
  catch (e) {
43
- this.logger.error(e);
43
+ if (this.listenerCount('error'))
44
+ this.emit('error', (0, wrap_exception_1.wrapException)(e));
44
45
  }
46
+ }
45
47
  }
46
48
  };
47
49
  for (const c of this.api.controllers.values())
@@ -62,17 +64,12 @@ class ExpressAdapter extends http_adapter_js_1.HttpAdapter {
62
64
  }
63
65
  else
64
66
  this.app.use(router);
65
- const createContext = (_req, _res, args) => {
67
+ const createContext = async (_req, _res, args) => {
66
68
  const request = http_incoming_interface_js_1.HttpIncoming.from(_req);
67
69
  const response = http_outgoing_interface_js_1.HttpOutgoing.from(_res);
68
- const platformArgs = {
69
- request: _req,
70
- response: _res,
71
- };
72
- return new http_context_js_1.HttpContext({
70
+ const ctx = new http_context_js_1.HttpContext({
73
71
  adapter: this,
74
72
  platform: this.platform,
75
- platformArgs,
76
73
  request,
77
74
  response,
78
75
  controller: args?.controller,
@@ -80,18 +77,14 @@ class ExpressAdapter extends http_adapter_js_1.HttpAdapter {
80
77
  operation: args?.operation,
81
78
  operationHandler: args?.operationHandler,
82
79
  });
80
+ await this.emitAsync('createContext', ctx);
81
+ return ctx;
83
82
  };
84
83
  /** Add an endpoint that returns document schema */
85
- router.get('*', (_req, _res, next) => {
86
- if (_req.url.includes('/$schema')) {
87
- const url = (_req.url.includes('?') ? _req.url.substring(0, _req.url.indexOf('?')) : _req.url).toLowerCase();
88
- if (url === '/$schema') {
89
- const context = createContext(_req, _res);
90
- this[constants_js_1.kHandler].sendDocumentSchema(context).catch(next);
91
- return;
92
- }
93
- }
94
- next();
84
+ router.get('/\\$schema', (_req, _res, next) => {
85
+ createContext(_req, _res)
86
+ .then(ctx => this.handler.sendDocumentSchema(ctx).catch(next))
87
+ .catch(next);
95
88
  });
96
89
  /** Add operation endpoints */
97
90
  if (this.api.controllers.size) {
@@ -105,19 +98,18 @@ class ExpressAdapter extends http_adapter_js_1.HttpAdapter {
105
98
  continue;
106
99
  /** Define router callback */
107
100
  router[operation.method.toLowerCase()](routePath, (_req, _res, _next) => {
108
- const context = createContext(_req, _res, {
101
+ createContext(_req, _res, {
109
102
  controller,
110
103
  controllerInstance,
111
104
  operation,
112
105
  operationHandler,
113
- });
114
- this[constants_js_1.kHandler]
115
- .handleRequest(context)
106
+ })
107
+ .then(ctx => this.handler.handleRequest(ctx))
116
108
  .then(() => {
117
109
  if (!_res.headersSent)
118
110
  _next();
119
111
  })
120
- .catch((e) => this.logger.fatal(e));
112
+ .catch((e) => this.emit('error', e));
121
113
  });
122
114
  }
123
115
  if (controller.controllers.size) {
@@ -130,18 +122,17 @@ class ExpressAdapter extends http_adapter_js_1.HttpAdapter {
130
122
  }
131
123
  /** Add an endpoint that returns 404 error at last */
132
124
  router.use('*', (_req, _res, next) => {
133
- const res = http_outgoing_interface_js_1.HttpOutgoing.from(_res);
134
- // const url = new URL(_req.originalUrl, '')
135
- this[constants_js_1.kHandler]
136
- .sendErrorResponse(res, [
137
- new common_1.NotFoundError({
138
- message: `No endpoint found for [${_req.method}]${_req.baseUrl}`,
125
+ createContext(_req, _res)
126
+ .then(ctx => {
127
+ ctx.errors.push(new common_1.NotFoundError({
128
+ message: `No endpoint found at [${_req.method}]${_req.baseUrl}`,
139
129
  details: {
140
130
  path: _req.baseUrl,
141
131
  method: _req.method,
142
132
  },
143
- }),
144
- ])
133
+ }));
134
+ this.handler.sendResponse(ctx).catch(next);
135
+ })
145
136
  .catch(next);
146
137
  });
147
138
  }
@@ -2,9 +2,8 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.HttpAdapter = void 0;
4
4
  const common_1 = require("@opra/common");
5
- const constants_js_1 = require("../constants.js");
6
5
  const platform_adapter_js_1 = require("../platform-adapter.js");
7
- const http_handler_js_1 = require("./impl/http-handler.js");
6
+ const http_handler_js_1 = require("./http-handler.js");
8
7
  /**
9
8
  *
10
9
  * @class HttpAdapter
@@ -15,10 +14,8 @@ class HttpAdapter extends platform_adapter_js_1.PlatformAdapter {
15
14
  this.protocol = 'http';
16
15
  if (!(document.api instanceof common_1.HttpApi))
17
16
  throw new TypeError(`The document does not expose an HTTP Api`);
18
- this[constants_js_1.kHandler] = new http_handler_js_1.HttpHandler(this);
17
+ this.handler = new http_handler_js_1.HttpHandler(this);
19
18
  this.interceptors = [...(options?.interceptors || [])];
20
- if (options?.onRequest)
21
- this.on('request', options.onRequest);
22
19
  }
23
20
  get api() {
24
21
  return this.document.api;
@@ -2,9 +2,9 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.HttpContext = void 0;
4
4
  const tslib_1 = require("tslib");
5
- const valgen_1 = require("valgen");
6
5
  const type_is_1 = tslib_1.__importDefault(require("@browsery/type-is"));
7
6
  const common_1 = require("@opra/common");
7
+ const valgen_1 = require("valgen");
8
8
  const constants_js_1 = require("../constants.js");
9
9
  const execution_context_js_1 = require("../execution-context.js");
10
10
  const multipart_reader_js_1 = require("./impl/multipart-reader.js");
@@ -29,6 +29,10 @@ class HttpContext extends execution_context_js_1.ExecutionContext {
29
29
  this.pathParams = init.pathParams || {};
30
30
  this.queryParams = init.queryParams || {};
31
31
  this._body = init.body;
32
+ this.on('finish', () => {
33
+ if (this._multipartReader)
34
+ this._multipartReader.purge().catch(() => undefined);
35
+ });
32
36
  }
33
37
  get isMultipart() {
34
38
  return !!this.request.is('multipart');
@@ -38,21 +42,21 @@ class HttpContext extends execution_context_js_1.ExecutionContext {
38
42
  throw new common_1.InternalServerError('Request content is not a multipart content');
39
43
  if (this._multipartReader)
40
44
  return this._multipartReader;
41
- const { request, mediaType } = this;
45
+ const { mediaType } = this;
42
46
  if (mediaType?.contentType) {
43
47
  const arr = Array.isArray(mediaType.contentType) ? mediaType.contentType : [mediaType.contentType];
44
48
  const contentType = arr.find(ct => type_is_1.default.is(ct, ['multipart']));
45
49
  if (!contentType)
46
50
  throw new common_1.NotAcceptableError('This endpoint does not accept multipart requests');
47
51
  }
48
- const reader = new multipart_reader_js_1.MultipartReader(request, {
49
- maxFields: mediaType?.maxFields,
50
- maxFieldsSize: mediaType?.maxFieldsSize,
51
- maxFiles: mediaType?.maxFiles,
52
- maxFileSize: mediaType?.maxFileSize,
53
- maxTotalFileSize: mediaType?.maxTotalFileSize,
54
- minFileSize: mediaType?.minFileSize,
55
- });
52
+ const reader = new multipart_reader_js_1.MultipartReader(this, {
53
+ limits: {
54
+ fields: mediaType?.maxFields,
55
+ fieldSize: mediaType?.maxFieldsSize,
56
+ files: mediaType?.maxFiles,
57
+ fileSize: mediaType?.maxFileSize,
58
+ },
59
+ }, mediaType);
56
60
  this._multipartReader = reader;
57
61
  return reader;
58
62
  }
@@ -65,32 +69,15 @@ class HttpContext extends execution_context_js_1.ExecutionContext {
65
69
  /** Retrieve all fields */
66
70
  const parts = await reader.getAll();
67
71
  /** Filter fields according to configuration */
68
- this._body = [];
69
- const multipartFields = mediaType?.multipartFields;
70
- if (mediaType && multipartFields?.length) {
71
- const fieldsFound = new Map();
72
- for (const item of parts) {
73
- const field = mediaType.findMultipartField(item.fieldName, item.type);
74
- if (field) {
75
- fieldsFound.set(field, true);
76
- this._body.push(item);
77
- }
78
- }
79
- /** Check required fields */
80
- for (const field of multipartFields) {
81
- if (field.required && !fieldsFound.get(field))
82
- throw new common_1.BadRequestError({
83
- message: `Multipart field (${field.fieldName}) is required`,
84
- });
85
- }
86
- }
72
+ this._body = [...parts];
87
73
  return this._body;
88
74
  }
89
- this._body = await this.request.readBody({ limit: operation.requestBody?.maxContentSize });
75
+ this._body = await this.request.readBody({ limit: operation?.requestBody?.maxContentSize });
90
76
  if (this._body != null) {
91
77
  // Convert Buffer to string if media is text
92
- if (Buffer.isBuffer(this._body) && request.is(['json', 'xml', 'txt', 'text']))
78
+ if (Buffer.isBuffer(this._body) && request.is(['json', 'xml', 'txt', 'text'])) {
93
79
  this._body = this._body.toString('utf-8');
80
+ }
94
81
  // Transform text to Object if media is JSON
95
82
  if (typeof this._body === 'string' && request.is(['json']))
96
83
  this._body = JSON.parse(this._body);
@@ -102,8 +89,9 @@ class HttpContext extends execution_context_js_1.ExecutionContext {
102
89
  if (!decode) {
103
90
  decode =
104
91
  mediaType.type?.generateCodec('decode', {
105
- partial: operation.requestBody?.partial,
92
+ partial: operation?.requestBody?.partial,
106
93
  projection: '*',
94
+ ignoreReadonlyFields: true,
107
95
  }) || valgen_1.vg.isAny();
108
96
  this.adapter[constants_js_1.kAssetCache].set(mediaType, 'decode', decode);
109
97
  }