@opra/core 0.22.0 → 0.23.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 (118) hide show
  1. package/cjs/adapter/execution-context.host.js +48 -0
  2. package/cjs/adapter/http/express-adapter.host.js +24 -0
  3. package/cjs/adapter/http/express-adapter.js +12 -45
  4. package/cjs/adapter/http/helpers/concat-readable.js +20 -0
  5. package/cjs/adapter/http/helpers/multipart-helper.js +96 -0
  6. package/cjs/adapter/http/helpers/query-parsers.js +16 -0
  7. package/cjs/adapter/http/http-adapter-base.js +127 -0
  8. package/cjs/adapter/http/http-adapter.host.js +57 -0
  9. package/cjs/adapter/http/http-adapter.js +11 -133
  10. package/cjs/adapter/http/{impl/http-server-request.js → http-server-request.js} +11 -5
  11. package/cjs/adapter/http/{impl/http-server-response.js → http-server-response.js} +4 -4
  12. package/cjs/adapter/http/impl/http-incoming-message.host.js +148 -0
  13. package/cjs/adapter/http/impl/{http-outgoing-message-host.js → http-outgoing-message.host.js} +26 -38
  14. package/cjs/adapter/http/request-handlers/entity-request-handler.js +378 -0
  15. package/cjs/adapter/http/request-handlers/request-handler-base.js +27 -0
  16. package/cjs/adapter/http/request-handlers/storage-request-handler.js +134 -0
  17. package/cjs/adapter/operation-context.js +16 -0
  18. package/cjs/adapter/platform-adapter.host.js +107 -0
  19. package/cjs/adapter/request.host.js +1 -2
  20. package/cjs/adapter/request.js +2 -0
  21. package/cjs/adapter/response.js +2 -0
  22. package/cjs/adapter/services/logger.js +36 -0
  23. package/cjs/augmentation/collection.augmentation.js +2 -0
  24. package/cjs/augmentation/singleton.augmentation.js +2 -0
  25. package/cjs/augmentation/storage.augmentation.js +2 -0
  26. package/cjs/index.js +15 -9
  27. package/esm/adapter/execution-context.host.js +44 -0
  28. package/esm/adapter/http/express-adapter.host.js +20 -0
  29. package/esm/adapter/http/express-adapter.js +11 -20
  30. package/esm/adapter/http/helpers/concat-readable.js +16 -0
  31. package/esm/adapter/http/helpers/multipart-helper.js +91 -0
  32. package/esm/adapter/http/helpers/query-parsers.js +12 -0
  33. package/esm/adapter/http/http-adapter-base.js +123 -0
  34. package/esm/adapter/http/http-adapter.host.js +52 -0
  35. package/esm/adapter/http/http-adapter.js +11 -132
  36. package/esm/adapter/http/{impl/http-server-request.js → http-server-request.js} +12 -6
  37. package/esm/adapter/http/{impl/http-server-response.js → http-server-response.js} +4 -4
  38. package/esm/adapter/http/impl/http-incoming-message.host.js +144 -0
  39. package/esm/adapter/http/impl/{http-outgoing-message-host.js → http-outgoing-message.host.js} +25 -36
  40. package/esm/adapter/http/request-handlers/entity-request-handler.js +373 -0
  41. package/esm/adapter/http/request-handlers/request-handler-base.js +23 -0
  42. package/esm/adapter/http/request-handlers/storage-request-handler.js +129 -0
  43. package/esm/adapter/operation-context.js +13 -0
  44. package/esm/adapter/platform-adapter.host.js +102 -0
  45. package/esm/adapter/request.host.js +1 -2
  46. package/esm/adapter/request.js +1 -0
  47. package/esm/adapter/response.js +1 -0
  48. package/esm/adapter/services/logger.js +32 -0
  49. package/esm/augmentation/collection.augmentation.js +1 -0
  50. package/esm/augmentation/singleton.augmentation.js +1 -0
  51. package/esm/augmentation/storage.augmentation.js +1 -0
  52. package/esm/index.js +15 -9
  53. package/i18n/en/error.json +5 -2
  54. package/package.json +3 -2
  55. package/types/adapter/execution-context.d.ts +31 -0
  56. package/types/adapter/execution-context.host.d.ts +27 -0
  57. package/types/adapter/http/express-adapter.d.ts +12 -8
  58. package/types/adapter/http/express-adapter.host.d.ts +11 -0
  59. package/types/adapter/http/helpers/concat-readable.d.ts +3 -0
  60. package/types/adapter/http/helpers/multipart-helper.d.ts +25 -0
  61. package/types/adapter/http/helpers/query-parsers.d.ts +1 -0
  62. package/types/adapter/http/http-adapter-base.d.ts +23 -0
  63. package/types/adapter/http/http-adapter.d.ts +13 -29
  64. package/types/adapter/http/http-adapter.host.d.ts +18 -0
  65. package/types/adapter/http/{impl/http-server-request.d.ts → http-server-request.d.ts} +7 -6
  66. package/types/adapter/http/{impl/http-server-response.d.ts → http-server-response.d.ts} +2 -2
  67. package/types/adapter/http/impl/{http-incoming-message-host.d.ts → http-incoming-message.host.d.ts} +16 -12
  68. package/types/adapter/http/impl/{http-outgoing-message-host.d.ts → http-outgoing-message.host.d.ts} +12 -16
  69. package/types/adapter/http/request-handlers/entity-request-handler.d.ts +24 -0
  70. package/types/adapter/http/request-handlers/request-handler-base.d.ts +15 -0
  71. package/types/adapter/http/request-handlers/storage-request-handler.d.ts +23 -0
  72. package/types/adapter/interfaces/logger.interface.d.ts +7 -6
  73. package/types/adapter/interfaces/request-handler.interface.d.ts +4 -0
  74. package/types/adapter/operation-context.d.ts +11 -0
  75. package/types/adapter/{adapter.d.ts → platform-adapter.d.ts} +18 -28
  76. package/types/adapter/platform-adapter.host.d.ts +31 -0
  77. package/types/adapter/request.d.ts +11 -0
  78. package/types/adapter/request.host.d.ts +12 -21
  79. package/types/adapter/{interfaces/response.interface.d.ts → response.d.ts} +2 -2
  80. package/types/adapter/response.host.d.ts +2 -2
  81. package/types/adapter/services/logger.d.ts +14 -0
  82. package/types/augmentation/collection.augmentation.d.ts +112 -0
  83. package/types/augmentation/singleton.augmentation.d.ts +64 -0
  84. package/types/augmentation/storage.augmentation.d.ts +39 -0
  85. package/types/index.d.ts +15 -9
  86. package/cjs/adapter/adapter.js +0 -118
  87. package/cjs/adapter/http/impl/http-incoming-message-host.js +0 -127
  88. package/cjs/adapter/http/request-parsers/parse-collection-request.js +0 -165
  89. package/cjs/adapter/http/request-parsers/parse-request.js +0 -24
  90. package/cjs/adapter/http/request-parsers/parse-singleton-request.js +0 -96
  91. package/cjs/adapter/internal/metadata.resource.js +0 -26
  92. package/cjs/adapter/request-context.host.js +0 -44
  93. package/cjs/shared/collection-resource-base.js +0 -20
  94. package/esm/adapter/adapter.js +0 -113
  95. package/esm/adapter/http/impl/http-incoming-message-host.js +0 -122
  96. package/esm/adapter/http/request-parsers/parse-collection-request.js +0 -161
  97. package/esm/adapter/http/request-parsers/parse-request.js +0 -20
  98. package/esm/adapter/http/request-parsers/parse-singleton-request.js +0 -92
  99. package/esm/adapter/internal/metadata.resource.js +0 -23
  100. package/esm/adapter/request-context.host.js +0 -40
  101. package/esm/shared/collection-resource-base.js +0 -16
  102. package/types/adapter/http/request-parsers/parse-collection-request.d.ts +0 -4
  103. package/types/adapter/http/request-parsers/parse-request.d.ts +0 -4
  104. package/types/adapter/http/request-parsers/parse-singleton-request.d.ts +0 -4
  105. package/types/adapter/interfaces/request-context.interface.d.ts +0 -31
  106. package/types/adapter/interfaces/request.interface.d.ts +0 -15
  107. package/types/adapter/internal/metadata.resource.d.ts +0 -7
  108. package/types/adapter/request-context.host.d.ts +0 -22
  109. package/types/shared/collection-resource-base.d.ts +0 -11
  110. /package/cjs/adapter/{interfaces/request-context.interface.js → execution-context.js} +0 -0
  111. /package/cjs/adapter/http/{request-parsers/batch-request-parser.js → request-handlers/parse-batch-request.js} +0 -0
  112. /package/cjs/adapter/interfaces/{request.interface.js → request-handler.interface.js} +0 -0
  113. /package/cjs/adapter/{interfaces/response.interface.js → platform-adapter.js} +0 -0
  114. /package/esm/adapter/{interfaces/request-context.interface.js → execution-context.js} +0 -0
  115. /package/esm/adapter/http/{request-parsers/batch-request-parser.js → request-handlers/parse-batch-request.js} +0 -0
  116. /package/esm/adapter/interfaces/{request.interface.js → request-handler.interface.js} +0 -0
  117. /package/esm/adapter/{interfaces/response.interface.js → platform-adapter.js} +0 -0
  118. /package/types/adapter/http/{request-parsers/batch-request-parser.d.ts → request-handlers/parse-batch-request.d.ts} +0 -0
@@ -0,0 +1,48 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ExecutionContextHost = void 0;
4
+ const strict_typed_events_1 = require("strict-typed-events");
5
+ class ExecutionContextHost extends strict_typed_events_1.AsyncEventEmitter {
6
+ constructor(api, platform, protocol) {
7
+ super();
8
+ this.api = api;
9
+ this.platform = platform;
10
+ this.errors = [];
11
+ this.executionScope = {};
12
+ this.ws = protocol.ws;
13
+ this.rpc = protocol.rpc;
14
+ if (protocol.http) {
15
+ this.protocol = 'http';
16
+ this.http = {
17
+ platform,
18
+ incoming: protocol.http.incoming,
19
+ outgoing: protocol.http.outgoing,
20
+ switchToContext: () => this
21
+ };
22
+ }
23
+ else if (protocol.ws) {
24
+ this.protocol = 'ws';
25
+ this.ws = protocol.ws;
26
+ }
27
+ else if (protocol.rpc) {
28
+ this.protocol = 'rpc';
29
+ this.rpc = protocol.rpc;
30
+ }
31
+ }
32
+ switchToHttp() {
33
+ if (this.http)
34
+ return this.http;
35
+ throw new TypeError('Not executing in an "Http" context');
36
+ }
37
+ switchToWs() {
38
+ if (this.ws)
39
+ return this.ws;
40
+ throw new TypeError('Not executing in an "WebSocket" context');
41
+ }
42
+ switchToRpc() {
43
+ if (this.rpc)
44
+ return this.rpc;
45
+ throw new TypeError('Not executing in an "RPC" context');
46
+ }
47
+ }
48
+ exports.ExecutionContextHost = ExecutionContextHost;
@@ -0,0 +1,24 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ExpressAdapterHost = void 0;
4
+ const common_1 = require("@opra/common");
5
+ const http_adapter_base_js_1 = require("./http-adapter-base.js");
6
+ const http_server_request_js_1 = require("./http-server-request.js");
7
+ const http_server_response_js_1 = require("./http-server-response.js");
8
+ class ExpressAdapterHost extends http_adapter_base_js_1.HttpAdapterBase {
9
+ constructor(app, api, options) {
10
+ super(api, options);
11
+ this._platform = 'express';
12
+ const basePath = new common_1.OpraURLPath(options?.basePath);
13
+ app.use(basePath.toString(), (_req, _res) => {
14
+ const req = http_server_request_js_1.HttpServerRequest.from(_req);
15
+ const res = http_server_response_js_1.HttpServerResponse.from(_res);
16
+ this.handleIncoming(req, res)
17
+ .catch(() => void 0);
18
+ });
19
+ }
20
+ get app() {
21
+ return this._app;
22
+ }
23
+ }
24
+ exports.ExpressAdapterHost = ExpressAdapterHost;
@@ -1,49 +1,16 @@
1
1
  "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
- Object.defineProperty(o, "default", { enumerable: true, value: v });
15
- }) : function(o, v) {
16
- o["default"] = v;
17
- });
18
- var __importStar = (this && this.__importStar) || function (mod) {
19
- if (mod && mod.__esModule) return mod;
20
- var result = {};
21
- if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
- __setModuleDefault(result, mod);
23
- return result;
24
- };
25
2
  Object.defineProperty(exports, "__esModule", { value: true });
26
- exports.OpraExpressAdapter = void 0;
27
- const common_1 = require("@opra/common");
28
- const http_adapter_js_1 = require("./http-adapter.js");
29
- const http_server_request_js_1 = require("./impl/http-server-request.js");
30
- const http_server_response_js_1 = require("./impl/http-server-response.js");
31
- class OpraExpressAdapter extends http_adapter_js_1.OpraHttpAdapter {
32
- constructor() {
33
- super(...arguments);
34
- this.platform = 'express';
35
- }
36
- static async create(app, document, options) {
37
- const express = await Promise.resolve().then(() => __importStar(require('express')));
38
- const adapter = new OpraExpressAdapter(document);
39
- await adapter.init(options);
40
- const prefix = '/' + (0, common_1.normalizePath)(options?.prefix, true);
41
- app.use(prefix, express.json());
42
- app.use(prefix, (req, res, next) => {
43
- adapter.handler(http_server_request_js_1.HttpServerRequest.create(req), http_server_response_js_1.HttpServerResponse.create(res))
44
- .catch(e => next(e));
45
- });
3
+ exports.ExpressAdapter = void 0;
4
+ const express_adapter_host_js_1 = require("./express-adapter.host.js");
5
+ /**
6
+ * @namespace
7
+ */
8
+ var ExpressAdapter;
9
+ (function (ExpressAdapter) {
10
+ async function create(app, api, options) {
11
+ const adapter = new express_adapter_host_js_1.ExpressAdapterHost(app, api, options);
12
+ await adapter.init();
46
13
  return adapter;
47
14
  }
48
- }
49
- exports.OpraExpressAdapter = OpraExpressAdapter;
15
+ ExpressAdapter.create = create;
16
+ })(ExpressAdapter || (exports.ExpressAdapter = ExpressAdapter = {}));
@@ -0,0 +1,20 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.concatReadable = void 0;
4
+ const stream_1 = require("stream");
5
+ function concatReadable(...streams) {
6
+ const out = new stream_1.PassThrough();
7
+ const pipeNext = () => {
8
+ const nextStream = streams.shift();
9
+ if (nextStream) {
10
+ nextStream.pipe(out, { end: false });
11
+ nextStream.once('end', () => pipeNext());
12
+ }
13
+ else {
14
+ out.end();
15
+ }
16
+ };
17
+ pipeNext();
18
+ return out;
19
+ }
20
+ exports.concatReadable = concatReadable;
@@ -0,0 +1,96 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.MultipartIterator = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const events_1 = require("events");
6
+ const formidable_1 = tslib_1.__importDefault(require("formidable"));
7
+ const promises_1 = tslib_1.__importDefault(require("fs/promises"));
8
+ const noOp = () => void 0;
9
+ class MultipartIterator extends events_1.EventEmitter {
10
+ constructor(incoming, options) {
11
+ super();
12
+ this._cancelled = false;
13
+ this._items = [];
14
+ this._stack = [];
15
+ this.setMaxListeners(1000);
16
+ const form = this._form = (0, formidable_1.default)({
17
+ ...options,
18
+ filter: (part) => {
19
+ return !this._cancelled && (!options?.filter || options.filter(part));
20
+ }
21
+ });
22
+ form.once('error', () => {
23
+ this._cancelled = true;
24
+ if (this.listenerCount('error') > 0)
25
+ this.emit('error');
26
+ });
27
+ form.on('field', (field, value) => {
28
+ const item = { field, value };
29
+ this._items.push(item);
30
+ this._stack.push(item);
31
+ this.emit('item', item);
32
+ });
33
+ form.on('file', (field, file) => {
34
+ const item = { field, file };
35
+ this._items.push(item);
36
+ this._stack.push(item);
37
+ this.emit('item', item);
38
+ });
39
+ form.parse(incoming).catch(noOp);
40
+ }
41
+ get items() {
42
+ return this._items;
43
+ }
44
+ getNext() {
45
+ if (this._form.ended)
46
+ return Promise.resolve(undefined);
47
+ this.resume();
48
+ return new Promise((resolve, reject) => {
49
+ if (this._stack.length)
50
+ return resolve(this._stack.shift());
51
+ this.once('item', () => resolve(this._stack.shift()));
52
+ this.once('error', (e) => reject(e));
53
+ });
54
+ }
55
+ getAll() {
56
+ if (this._form.ended)
57
+ return Promise.resolve([...this._items]);
58
+ this.resume();
59
+ return new Promise((resolve, reject) => {
60
+ this._form.once('error', reject);
61
+ this._form.once('end', () => {
62
+ resolve([...this._items]);
63
+ });
64
+ });
65
+ }
66
+ cancel() {
67
+ this._cancelled = true;
68
+ this.resume();
69
+ }
70
+ resume() {
71
+ this._form.resume();
72
+ }
73
+ pause() {
74
+ this._form.pause();
75
+ }
76
+ async deleteFiles() {
77
+ const promises = [];
78
+ this._items
79
+ .forEach(item => {
80
+ if (!item.file)
81
+ return;
82
+ const file = item.file;
83
+ promises.push(new Promise(resolve => {
84
+ if (file._writeStream.closed)
85
+ return resolve();
86
+ file._writeStream.once('close', resolve);
87
+ }).then(() => {
88
+ return promises_1.default.unlink(file.filepath);
89
+ }).then(() => {
90
+ return 0;
91
+ }));
92
+ });
93
+ return Promise.allSettled(promises);
94
+ }
95
+ }
96
+ exports.MultipartIterator = MultipartIterator;
@@ -0,0 +1,16 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.parseArrayParam = void 0;
4
+ const fast_tokenizer_1 = require("fast-tokenizer");
5
+ function parseArrayParam(v) {
6
+ if (!v)
7
+ return;
8
+ return (0, fast_tokenizer_1.splitString)(v, {
9
+ delimiters: ',',
10
+ quotes: true,
11
+ brackets: true,
12
+ keepBrackets: true,
13
+ keepQuotes: true
14
+ }).map(x => x.trim());
15
+ }
16
+ exports.parseArrayParam = parseArrayParam;
@@ -0,0 +1,127 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.HttpAdapterBase = void 0;
4
+ const common_1 = require("@opra/common");
5
+ const execution_context_host_js_1 = require("../execution-context.host.js");
6
+ const platform_adapter_host_js_1 = require("../platform-adapter.host.js");
7
+ const entity_request_handler_js_1 = require("./request-handlers/entity-request-handler.js");
8
+ const storage_request_handler_js_1 = require("./request-handlers/storage-request-handler.js");
9
+ /**
10
+ *
11
+ * @class HttpAdapterBase
12
+ */
13
+ class HttpAdapterBase extends platform_adapter_host_js_1.PlatformAdapterHost {
14
+ constructor() {
15
+ super(...arguments);
16
+ this._protocol = 'http';
17
+ this._requestHandlers = [
18
+ new entity_request_handler_js_1.EntityRequestHandler(this),
19
+ new storage_request_handler_js_1.StorageRequestHandler(this)
20
+ ];
21
+ }
22
+ /**
23
+ * Main http request handler
24
+ * @param incoming
25
+ * @param outgoing
26
+ * @protected
27
+ */
28
+ async handleIncoming(incoming, outgoing) {
29
+ const context = new execution_context_host_js_1.ExecutionContextHost(this.api, this.platform, { http: { incoming, outgoing } });
30
+ try {
31
+ /* istanbul ignore next */
32
+ if (!this._initialized)
33
+ throw new common_1.InternalServerError(`${Object.getPrototypeOf(this).constructor.name} has not been initialized yet`);
34
+ outgoing.setHeader(common_1.HttpHeaderCodes.X_Opra_Version, common_1.OpraSchema.SpecVersion);
35
+ // Expose headers if cors enabled
36
+ if (outgoing.getHeader(common_1.HttpHeaderCodes.Access_Control_Allow_Origin)) {
37
+ // Expose X-Opra-* headers
38
+ outgoing.appendHeader(common_1.HttpHeaderCodes.Access_Control_Expose_Headers, Object.values(common_1.HttpHeaderCodes)
39
+ .filter(k => k.toLowerCase().startsWith('x-opra-')));
40
+ }
41
+ await this.processRequest(context);
42
+ }
43
+ catch (error) {
44
+ context.errors.push((0, common_1.wrapException)(error));
45
+ }
46
+ // If no response returned to the client we send an error
47
+ if (!outgoing.writableEnded) {
48
+ if (!context.errors.length)
49
+ context.errors.push(new common_1.BadRequestError(`Server can not process this request`));
50
+ await this.handleError(context);
51
+ }
52
+ }
53
+ async processRequest(context) {
54
+ try {
55
+ const { incoming, outgoing } = context.switchToHttp();
56
+ if (incoming.method === 'GET' &&
57
+ incoming.parsedUrl.path.length &&
58
+ incoming.parsedUrl.path[0].resource === '$metadata') {
59
+ outgoing.setHeader('content-type', 'application/json');
60
+ outgoing.end(JSON.stringify(this.api.exportSchema({ webSafe: true })));
61
+ return;
62
+ }
63
+ const { parsedUrl } = incoming;
64
+ if (!parsedUrl.path.length) {
65
+ // Batch
66
+ if (incoming.headers['content-type'] === 'multipart/mixed') {
67
+ // todo
68
+ }
69
+ throw new common_1.BadRequestError();
70
+ }
71
+ // Iterate through request handlers until one of them sends response (end outgoing stream)
72
+ for (const requestHandler of this._requestHandlers) {
73
+ await requestHandler.processRequest(context);
74
+ if (outgoing.writableEnded)
75
+ return;
76
+ if (context.errors.length) {
77
+ await this.handleError(context);
78
+ return;
79
+ }
80
+ }
81
+ }
82
+ finally {
83
+ await context.emitAsync('finish');
84
+ }
85
+ }
86
+ async handleError(context) {
87
+ const { errors } = context;
88
+ const { outgoing } = context.switchToHttp();
89
+ if (outgoing.headersSent) {
90
+ outgoing.end();
91
+ return;
92
+ }
93
+ errors.forEach(x => {
94
+ // todo. implement a better log mechanism
95
+ if (x instanceof common_1.OpraException)
96
+ this._logger.fatal(x);
97
+ else
98
+ this._logger.error(x);
99
+ });
100
+ const wrappedErrors = errors.map(common_1.wrapException);
101
+ // Sort errors from fatal to info
102
+ wrappedErrors.sort((a, b) => {
103
+ const i = common_1.IssueSeverity.Keys.indexOf(a.issue.severity) - common_1.IssueSeverity.Keys.indexOf(b.issue.severity);
104
+ if (i === 0)
105
+ return b.status - a.status;
106
+ return i;
107
+ });
108
+ let status = outgoing.statusCode || 0;
109
+ if (!status || status < Number(common_1.HttpStatusCodes.BAD_REQUEST)) {
110
+ status = wrappedErrors[0].status;
111
+ if (status < Number(common_1.HttpStatusCodes.BAD_REQUEST))
112
+ status = common_1.HttpStatusCodes.INTERNAL_SERVER_ERROR;
113
+ }
114
+ outgoing.statusCode = status;
115
+ const body = this._i18n.deep({
116
+ errors: wrappedErrors.map(x => x.issue)
117
+ });
118
+ outgoing.setHeader(common_1.HttpHeaderCodes.Content_Type, 'application/json; charset=utf-8');
119
+ outgoing.setHeader(common_1.HttpHeaderCodes.Cache_Control, 'no-cache');
120
+ outgoing.setHeader(common_1.HttpHeaderCodes.Pragma, 'no-cache');
121
+ outgoing.setHeader(common_1.HttpHeaderCodes.Expires, '-1');
122
+ outgoing.setHeader(common_1.HttpHeaderCodes.X_Opra_Version, common_1.OpraSchema.SpecVersion);
123
+ outgoing.send(JSON.stringify(body));
124
+ outgoing.end();
125
+ }
126
+ }
127
+ exports.HttpAdapterBase = HttpAdapterBase;
@@ -0,0 +1,57 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.HttpAdapterHost = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const http_1 = tslib_1.__importDefault(require("http"));
6
+ const common_1 = require("@opra/common");
7
+ const http_adapter_base_js_1 = require("./http-adapter-base.js");
8
+ const http_server_request_js_1 = require("./http-server-request.js");
9
+ const http_server_response_js_1 = require("./http-server-response.js");
10
+ /**
11
+ * @class HttpAdapterHost
12
+ */
13
+ class HttpAdapterHost extends http_adapter_base_js_1.HttpAdapterBase {
14
+ constructor(api, options) {
15
+ super(api, options);
16
+ this._platform = 'http';
17
+ this._basePath = new common_1.OpraURLPath(options?.basePath);
18
+ this._server = http_1.default.createServer((incomingMessage, serverResponse) => this._serverListener(incomingMessage, serverResponse));
19
+ }
20
+ get basePath() {
21
+ return this._basePath;
22
+ }
23
+ get server() {
24
+ return this._server;
25
+ }
26
+ _serverListener(incomingMessage, serverResponse) {
27
+ const originalUrl = incomingMessage.url;
28
+ const parsedUrl = new common_1.OpraURL(originalUrl);
29
+ const relativePath = common_1.OpraURLPath.relative(parsedUrl.path, this.basePath);
30
+ if (!relativePath) {
31
+ serverResponse.statusCode = common_1.HttpStatusCodes.NOT_FOUND;
32
+ serverResponse.statusMessage = common_1.HttpStatusMessages[common_1.HttpStatusCodes.NOT_FOUND];
33
+ serverResponse.end();
34
+ return;
35
+ }
36
+ parsedUrl.path = relativePath;
37
+ incomingMessage.originalUrl = originalUrl;
38
+ incomingMessage.baseUrl = this.basePath.toString();
39
+ incomingMessage.parsedUrl = parsedUrl;
40
+ const req = http_server_request_js_1.HttpServerRequest.from(incomingMessage);
41
+ const res = http_server_response_js_1.HttpServerResponse.from(serverResponse);
42
+ this.handleIncoming(req, res)
43
+ .catch((e) => this._logger.fatal(e));
44
+ }
45
+ async close() {
46
+ await super.close();
47
+ if (this.server.listening)
48
+ await new Promise((resolve, reject) => {
49
+ this.server.close((err) => {
50
+ if (err)
51
+ return reject(err);
52
+ resolve();
53
+ });
54
+ });
55
+ }
56
+ }
57
+ exports.HttpAdapterHost = HttpAdapterHost;
@@ -1,138 +1,16 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.OpraHttpAdapter = void 0;
4
- const power_tasks_1 = require("power-tasks");
5
- const common_1 = require("@opra/common");
6
- const adapter_js_1 = require("../adapter.js");
7
- const request_context_host_js_1 = require("../request-context.host.js");
8
- const response_host_js_1 = require("../response.host.js");
9
- const parse_request_js_1 = require("./request-parsers/parse-request.js");
3
+ exports.HttpAdapter = void 0;
4
+ const http_adapter_host_js_1 = require("./http-adapter.host.js");
10
5
  /**
11
- *
12
- * @class OpraHttpAdapter
6
+ * @namespace HttpAdapter
13
7
  */
14
- class OpraHttpAdapter extends adapter_js_1.OpraAdapter {
15
- /**
16
- * Main http request handler
17
- * @param incoming
18
- * @param outgoing
19
- * @protected
20
- */
21
- async handler(incoming, outgoing) {
22
- try {
23
- try {
24
- const request = await (0, parse_request_js_1.parseRequest)(this._apiRoot, incoming);
25
- const task = this.createRequestTask(request, outgoing);
26
- await task.toPromise();
27
- }
28
- catch (e) {
29
- if (e instanceof common_1.OpraException)
30
- throw e;
31
- throw new common_1.BadRequestError(e);
32
- }
33
- }
34
- catch (error) {
35
- await this.handleError(incoming, outgoing, [error]);
36
- }
8
+ var HttpAdapter;
9
+ (function (HttpAdapter) {
10
+ async function create(api, options) {
11
+ const adapter = new http_adapter_host_js_1.HttpAdapterHost(api, options);
12
+ await adapter.init();
13
+ return adapter;
37
14
  }
38
- createRequestTask(request, outgoing) {
39
- return new power_tasks_1.Task(async () => {
40
- const response = new response_host_js_1.ResponseHost({ http: outgoing });
41
- const context = new request_context_host_js_1.RequestContextHost('http', this.platform, this.api, request, response);
42
- return this.executeRequest(context);
43
- }, {
44
- id: request.contentId,
45
- exclusive: request.crud !== 'read'
46
- });
47
- }
48
- async executeRequest(context) {
49
- const { request, response } = context;
50
- await super.executeRequest(context)
51
- .catch(e => {
52
- response.errors = response.errors || [];
53
- response.errors.push(e);
54
- });
55
- const outgoing = response.switchToHttp();
56
- if (response.errors?.length) {
57
- const errors = response.errors.map(e => (0, common_1.wrapException)(e));
58
- await this.handleError(request.switchToHttp(), outgoing, errors);
59
- return;
60
- }
61
- if (request.crud === 'create') {
62
- if (!response.value)
63
- throw new common_1.InternalServerError();
64
- // todo validate
65
- outgoing.statusCode = 201;
66
- }
67
- if (request.resource instanceof common_1.Collection &&
68
- request.crud === 'read' && request.many && request.args.count >= 0) {
69
- outgoing.setHeader(common_1.HttpHeaderCodes.X_Total_Count, String(response.count));
70
- }
71
- outgoing.statusCode = outgoing.statusCode || common_1.HttpStatusCodes.OK;
72
- outgoing.setHeader(common_1.HttpHeaderCodes.Cache_Control, 'no-cache');
73
- outgoing.setHeader(common_1.HttpHeaderCodes.Pragma, 'no-cache');
74
- outgoing.setHeader(common_1.HttpHeaderCodes.Expires, '-1');
75
- outgoing.setHeader(common_1.HttpHeaderCodes.X_Opra_Version, common_1.OpraSchema.SpecVersion);
76
- // Expose headers if cors enabled
77
- if (outgoing.getHeader(common_1.HttpHeaderCodes.Access_Control_Allow_Origin)) {
78
- // Expose X-Total-Count header
79
- outgoing.appendHeader(common_1.HttpHeaderCodes.Access_Control_Expose_Headers, [common_1.HttpHeaderCodes.X_Total_Count]);
80
- // Expose X-Opra-* headers
81
- outgoing.appendHeader(common_1.HttpHeaderCodes.Access_Control_Expose_Headers, Object.values(common_1.HttpHeaderCodes)
82
- .filter(k => k.toLowerCase().startsWith('x-opra-')));
83
- }
84
- if (response.value) {
85
- if (typeof response.value === 'object') {
86
- if ((0, common_1.isReadable)(response.value) || Buffer.isBuffer(response.value))
87
- outgoing.send(response.value);
88
- else {
89
- const body = this.i18n.deep(response.value);
90
- outgoing.setHeader(common_1.HttpHeaderCodes.Content_Type, 'application/json; charset=utf-8');
91
- outgoing.send(JSON.stringify(body));
92
- }
93
- }
94
- else
95
- outgoing.send(JSON.stringify(response.value));
96
- }
97
- outgoing.end();
98
- }
99
- async handleError(incoming, outgoing, errors) {
100
- errors.forEach(e => {
101
- this.log((e instanceof common_1.OpraException) ? 'error' : 'fatal', incoming, e); // todo. implement a better logger
102
- });
103
- errors = errors.map(common_1.wrapException);
104
- let status = outgoing.statusCode || 0;
105
- // Sort errors from fatal to info
106
- errors.sort((a, b) => {
107
- const i = common_1.IssueSeverity.Keys.indexOf(a.issue.severity) - common_1.IssueSeverity.Keys.indexOf(b.issue.severity);
108
- if (i === 0)
109
- return b.status - a.status;
110
- return i;
111
- });
112
- if (!status || status < Number(common_1.HttpStatusCodes.BAD_REQUEST)) {
113
- status = errors[0].status;
114
- if (status < Number(common_1.HttpStatusCodes.BAD_REQUEST))
115
- status = common_1.HttpStatusCodes.INTERNAL_SERVER_ERROR;
116
- }
117
- const body = this.i18n.deep({
118
- errors: errors.map(e => e.issue)
119
- });
120
- outgoing.statusCode = status;
121
- outgoing.setHeader(common_1.HttpHeaderCodes.Content_Type, 'application/json; charset=utf-8');
122
- outgoing.setHeader(common_1.HttpHeaderCodes.Cache_Control, 'no-cache');
123
- outgoing.setHeader(common_1.HttpHeaderCodes.Pragma, 'no-cache');
124
- outgoing.setHeader(common_1.HttpHeaderCodes.Expires, '-1');
125
- outgoing.setHeader(common_1.HttpHeaderCodes.X_Opra_Version, common_1.OpraSchema.SpecVersion);
126
- outgoing.send(JSON.stringify(body));
127
- outgoing.end();
128
- }
129
- log(logType, incoming, message, ...optionalParams) {
130
- const logFn = logType === 'fatal'
131
- ? this.logger?.fatal || this.logger?.error
132
- : this.logger?.[logType];
133
- if (!logFn)
134
- return;
135
- logFn.apply(this.logger, [String(message), ...optionalParams]);
136
- }
137
- }
138
- exports.OpraHttpAdapter = OpraHttpAdapter;
15
+ HttpAdapter.create = create;
16
+ })(HttpAdapter || (exports.HttpAdapter = HttpAdapter = {}));
@@ -11,21 +11,27 @@ const fresh_1 = tslib_1.__importDefault(require("fresh"));
11
11
  const range_parser_1 = tslib_1.__importDefault(require("range-parser"));
12
12
  const type_is_1 = tslib_1.__importDefault(require("type-is"));
13
13
  const common_1 = require("@opra/common");
14
- const http_incoming_message_host_js_1 = require("./http-incoming-message-host.js");
14
+ const http_incoming_message_host_js_1 = require("./impl/http-incoming-message.host.js");
15
15
  function isHttpIncomingMessage(v) {
16
16
  return v && Array.isArray(v.rawHeaders) && (0, common_1.isReadable)(v);
17
17
  }
18
18
  var HttpServerRequest;
19
19
  (function (HttpServerRequest) {
20
- function create(instance) {
20
+ function from(instance) {
21
21
  if (!isHttpIncomingMessage(instance))
22
- instance = http_incoming_message_host_js_1.HttpIncomingMessageHost.create(instance);
22
+ instance = http_incoming_message_host_js_1.HttpIncomingMessageHost.from(instance);
23
23
  (0, common_1.mergePrototype)(instance, HttpServerRequestHost.prototype);
24
- return instance;
24
+ const req = instance;
25
+ req.baseUrl = req.baseUrl || '';
26
+ req.parsedUrl = req.parsedUrl || new common_1.OpraURL(req.url);
27
+ return req;
25
28
  }
26
- HttpServerRequest.create = create;
29
+ HttpServerRequest.from = from;
27
30
  })(HttpServerRequest || (exports.HttpServerRequest = HttpServerRequest = {}));
28
31
  class HttpServerRequestHost {
32
+ constructor() {
33
+ this.basePath = '/';
34
+ }
29
35
  get protocol() {
30
36
  const proto = this.header('X-Forwarded-Proto') || 'http';
31
37
  const index = proto.indexOf(',');
@@ -16,20 +16,20 @@ const path_1 = tslib_1.__importDefault(require("path"));
16
16
  const putil_varhelpers_1 = require("putil-varhelpers");
17
17
  const vary_1 = tslib_1.__importDefault(require("vary"));
18
18
  const common_1 = require("@opra/common");
19
- const http_outgoing_message_host_js_1 = require("./http-outgoing-message-host.js");
19
+ const http_outgoing_message_host_js_1 = require("./impl/http-outgoing-message.host.js");
20
20
  const charsetRegExp = /;\s*charset\s*=/;
21
21
  function isHttpIncomingMessage(v) {
22
22
  return v && typeof v.getHeaders === 'function' && (0, common_1.isStream)(v);
23
23
  }
24
24
  var HttpServerResponse;
25
25
  (function (HttpServerResponse) {
26
- function create(instance) {
26
+ function from(instance) {
27
27
  if (!isHttpIncomingMessage(instance))
28
- instance = http_outgoing_message_host_js_1.HttpOutgoingMessageHost.create(instance);
28
+ instance = new http_outgoing_message_host_js_1.HttpOutgoingMessageHost(instance);
29
29
  (0, common_1.mergePrototype)(instance, HttpServerResponseHost.prototype);
30
30
  return instance;
31
31
  }
32
- HttpServerResponse.create = create;
32
+ HttpServerResponse.from = from;
33
33
  })(HttpServerResponse || (exports.HttpServerResponse = HttpServerResponse = {}));
34
34
  class HttpServerResponseHost {
35
35
  attachment(filename) {