@opra/core 1.0.0-alpha.9 → 1.0.0-beta.2
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/cjs/augmentation/18n.augmentation.js +13 -13
- package/cjs/constants.js +1 -2
- package/cjs/execution-context.js +1 -0
- package/cjs/http/express-adapter.js +22 -20
- package/cjs/http/http-adapter.js +2 -5
- package/cjs/http/http-context.js +16 -31
- package/cjs/http/{impl/http-handler.js → http-handler.js} +194 -169
- package/cjs/http/impl/http-outgoing.host.js +2 -2
- package/cjs/http/impl/multipart-reader.js +141 -44
- package/cjs/http/utils/body-reader.js +0 -1
- package/cjs/http/utils/common.js +4 -4
- package/cjs/http/utils/concat-readable.js +1 -2
- package/cjs/http/utils/convert-to-headers.js +2 -3
- package/cjs/http/utils/convert-to-raw-headers.js +1 -2
- package/cjs/http/utils/match-known-fields.js +2 -2
- package/cjs/http/utils/wrap-exception.js +1 -2
- package/cjs/index.js +1 -1
- package/cjs/platform-adapter.js +0 -3
- package/cjs/type-guards.js +4 -5
- package/esm/augmentation/18n.augmentation.js +2 -2
- package/esm/constants.js +0 -1
- package/esm/execution-context.js +1 -0
- package/esm/http/express-adapter.js +22 -20
- package/esm/http/http-adapter.js +2 -5
- package/esm/http/http-context.js +17 -32
- package/esm/http/{impl/http-handler.js → http-handler.js} +195 -170
- package/esm/http/impl/http-outgoing.host.js +1 -1
- package/esm/http/impl/multipart-reader.js +142 -45
- package/esm/http/utils/body-reader.js +0 -1
- package/esm/index.js +1 -1
- package/esm/package.json +3 -0
- package/esm/platform-adapter.js +0 -3
- package/package.json +35 -63
- package/types/augmentation/http-controller.augmentation.d.ts +1 -2
- package/types/constants.d.ts +0 -1
- package/types/execution-context.d.ts +2 -1
- package/types/helpers/service-base.d.ts +1 -1
- package/types/http/express-adapter.d.ts +1 -1
- package/types/http/http-adapter.d.ts +35 -8
- package/types/http/http-context.d.ts +4 -4
- package/types/http/{impl/http-handler.d.ts → http-handler.d.ts} +11 -9
- package/types/http/impl/http-incoming.host.d.ts +1 -2
- package/types/http/impl/http-outgoing.host.d.ts +1 -1
- package/types/http/impl/multipart-reader.d.ts +38 -20
- package/types/http/impl/node-incoming-message.host.d.ts +3 -7
- package/types/http/impl/node-outgoing-message.host.d.ts +5 -8
- package/types/http/interfaces/http-incoming.interface.d.ts +2 -3
- package/types/http/interfaces/http-outgoing.interface.d.ts +2 -2
- package/types/http/interfaces/node-incoming-message.interface.d.ts +0 -2
- package/types/http/interfaces/node-outgoing-message.interface.d.ts +1 -3
- package/types/http/utils/body-reader.d.ts +1 -4
- package/types/http/utils/concat-readable.d.ts +0 -1
- package/types/http/utils/convert-to-raw-headers.d.ts +1 -2
- package/types/index.d.cts +28 -0
- package/types/index.d.ts +1 -1
- package/types/platform-adapter.d.ts +0 -4
- package/cjs/helpers/logger.js +0 -35
- package/esm/helpers/logger.js +0 -31
- package/i18n/i18n/en/error.json +0 -21
- package/types/helpers/logger.d.ts +0 -14
|
@@ -1,55 +1,155 @@
|
|
|
1
|
+
import { randomFillSync } from 'node:crypto';
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
import os from 'node:os';
|
|
4
|
+
import nodePath from 'node:path';
|
|
5
|
+
import typeIs from '@browsery/type-is';
|
|
6
|
+
import { BadRequestError } from '@opra/common';
|
|
7
|
+
import busboy from 'busboy';
|
|
1
8
|
import { EventEmitter } from 'events';
|
|
2
|
-
import
|
|
3
|
-
import
|
|
9
|
+
import fsPromise from 'fs/promises';
|
|
10
|
+
import { isNotNullish } from 'valgen';
|
|
4
11
|
export class MultipartReader extends EventEmitter {
|
|
5
|
-
constructor(
|
|
12
|
+
constructor(context, options, mediaType) {
|
|
6
13
|
super();
|
|
14
|
+
this.context = context;
|
|
15
|
+
this.mediaType = mediaType;
|
|
7
16
|
this._started = false;
|
|
17
|
+
this._finished = false;
|
|
8
18
|
this._cancelled = false;
|
|
9
19
|
this._items = [];
|
|
10
20
|
this._stack = [];
|
|
11
21
|
this.setMaxListeners(1000);
|
|
12
|
-
this.
|
|
13
|
-
const
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
form.once('error', () => {
|
|
22
|
+
this.tempDirectory = options?.tempDirectory || os.tmpdir();
|
|
23
|
+
const { request } = context;
|
|
24
|
+
const form = busboy({ headers: request.headers });
|
|
25
|
+
this._form = form;
|
|
26
|
+
form.once('error', (e) => {
|
|
18
27
|
this._cancelled = true;
|
|
28
|
+
this._finished = true;
|
|
19
29
|
if (this.listenerCount('error') > 0)
|
|
20
|
-
this.emit('error');
|
|
30
|
+
this.emit('error', e);
|
|
21
31
|
});
|
|
22
|
-
form.on('
|
|
23
|
-
|
|
32
|
+
form.on('close', () => {
|
|
33
|
+
this._finished = true;
|
|
34
|
+
});
|
|
35
|
+
form.on('field', (field, value, info) => {
|
|
36
|
+
const item = {
|
|
37
|
+
kind: 'field',
|
|
38
|
+
field,
|
|
39
|
+
value,
|
|
40
|
+
mimeType: info.mimeType,
|
|
41
|
+
encoding: info.encoding,
|
|
42
|
+
};
|
|
24
43
|
this._items.push(item);
|
|
25
44
|
this._stack.push(item);
|
|
26
45
|
this.emit('field', item);
|
|
27
46
|
this.emit('item', item);
|
|
28
47
|
});
|
|
29
|
-
form.on('file', (
|
|
30
|
-
const
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
48
|
+
form.on('file', (field, file, info) => {
|
|
49
|
+
const saveTo = nodePath.join(this.tempDirectory, `opra-${generateFileName()}`);
|
|
50
|
+
file.pipe(fs.createWriteStream(saveTo));
|
|
51
|
+
file.once('end', () => {
|
|
52
|
+
const item = {
|
|
53
|
+
kind: 'file',
|
|
54
|
+
field,
|
|
55
|
+
storedPath: saveTo,
|
|
56
|
+
filename: info.filename,
|
|
57
|
+
mimeType: info.mimeType,
|
|
58
|
+
encoding: info.encoding,
|
|
59
|
+
};
|
|
60
|
+
this._items.push(item);
|
|
61
|
+
this._stack.push(item);
|
|
62
|
+
this.emit('file', item);
|
|
63
|
+
this.emit('item', item);
|
|
64
|
+
});
|
|
35
65
|
});
|
|
36
66
|
}
|
|
37
67
|
get items() {
|
|
38
68
|
return this._items;
|
|
39
69
|
}
|
|
40
|
-
getNext() {
|
|
41
|
-
|
|
70
|
+
async getNext() {
|
|
71
|
+
let item = this._stack.shift();
|
|
72
|
+
if (!item && !this._finished) {
|
|
42
73
|
this.resume();
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
74
|
+
item = await new Promise((resolve, reject) => {
|
|
75
|
+
let resolved = false;
|
|
76
|
+
if (this._stack.length)
|
|
77
|
+
return resolve(this._stack.shift());
|
|
78
|
+
if (this._form.ended)
|
|
79
|
+
return resolve(undefined);
|
|
80
|
+
this._form.once('close', () => {
|
|
81
|
+
if (resolved)
|
|
82
|
+
return;
|
|
83
|
+
resolved = true;
|
|
84
|
+
resolve(this._stack.shift());
|
|
85
|
+
});
|
|
86
|
+
this.once('item', () => {
|
|
87
|
+
this.pause();
|
|
88
|
+
if (resolved)
|
|
89
|
+
return;
|
|
90
|
+
resolved = true;
|
|
91
|
+
resolve(this._stack.shift());
|
|
92
|
+
});
|
|
93
|
+
this.once('error', e => reject(e));
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
if (item && this.mediaType) {
|
|
97
|
+
const field = this.mediaType.findMultipartField(item.field);
|
|
98
|
+
if (!field)
|
|
99
|
+
throw new BadRequestError(`Unknown multipart field (${item.field})`);
|
|
100
|
+
if (item.kind === 'field') {
|
|
101
|
+
const decode = field.generateCodec('decode', { ignoreReadonlyFields: true, projection: '*' });
|
|
102
|
+
item.value = decode(item.value, {
|
|
103
|
+
onFail: issue => `Multipart field (${item.field}) validation failed: ` + issue.message,
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
else if (item.kind === 'file') {
|
|
107
|
+
if (field.contentType) {
|
|
108
|
+
const arr = Array.isArray(field.contentType) ? field.contentType : [field.contentType];
|
|
109
|
+
if (!(item.mimeType && arr.find(ct => typeIs.is(item.mimeType, [ct])))) {
|
|
110
|
+
throw new BadRequestError(`Multipart field (${item.field}) do not accept this content type`);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
/** if all items received we check for required items */
|
|
116
|
+
if (this._finished && this.mediaType && this.mediaType.multipartFields?.length > 0) {
|
|
117
|
+
const fieldsLeft = new Set(this.mediaType.multipartFields);
|
|
118
|
+
for (const x of this._items) {
|
|
119
|
+
const field = this.mediaType.findMultipartField(x.field);
|
|
120
|
+
if (field)
|
|
121
|
+
fieldsLeft.delete(field);
|
|
122
|
+
}
|
|
123
|
+
let issues;
|
|
124
|
+
for (const field of fieldsLeft) {
|
|
125
|
+
if (!field.required)
|
|
126
|
+
continue;
|
|
127
|
+
try {
|
|
128
|
+
isNotNullish(null, { onFail: () => `Multi part field "${String(field.fieldName)}" is required` });
|
|
129
|
+
}
|
|
130
|
+
catch (e) {
|
|
131
|
+
if (!issues) {
|
|
132
|
+
issues = e.issues;
|
|
133
|
+
this.context.errors.push(e);
|
|
134
|
+
}
|
|
135
|
+
else
|
|
136
|
+
issues.push(...e.issues);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
if (this.context.errors.length)
|
|
140
|
+
throw this.context.errors[0];
|
|
141
|
+
}
|
|
142
|
+
return item;
|
|
143
|
+
}
|
|
144
|
+
async getAll() {
|
|
145
|
+
const items = [...this._items];
|
|
146
|
+
let item;
|
|
147
|
+
while (!this._cancelled && (item = await this.getNext())) {
|
|
148
|
+
items.push(item);
|
|
149
|
+
}
|
|
150
|
+
return items;
|
|
51
151
|
}
|
|
52
|
-
|
|
152
|
+
getAll_() {
|
|
53
153
|
if (this._form.ended)
|
|
54
154
|
return Promise.resolve([...this._items]);
|
|
55
155
|
this.resume();
|
|
@@ -66,29 +166,26 @@ export class MultipartReader extends EventEmitter {
|
|
|
66
166
|
this.resume();
|
|
67
167
|
}
|
|
68
168
|
resume() {
|
|
69
|
-
if (!this._started)
|
|
70
|
-
this.
|
|
71
|
-
|
|
72
|
-
|
|
169
|
+
if (!this._started) {
|
|
170
|
+
this._started = true;
|
|
171
|
+
this.context.request.pipe(this._form);
|
|
172
|
+
}
|
|
173
|
+
this.context.request.resume();
|
|
73
174
|
}
|
|
74
175
|
pause() {
|
|
75
|
-
|
|
76
|
-
this._form.pause();
|
|
176
|
+
this.context.request.pause();
|
|
77
177
|
}
|
|
78
|
-
async
|
|
178
|
+
async purge() {
|
|
79
179
|
const promises = [];
|
|
80
180
|
this._items.forEach(item => {
|
|
81
|
-
if (
|
|
181
|
+
if (item.kind !== 'file')
|
|
82
182
|
return;
|
|
83
|
-
|
|
84
|
-
promises.push(new Promise(resolve => {
|
|
85
|
-
if (file._writeStream.closed)
|
|
86
|
-
return resolve();
|
|
87
|
-
file._writeStream.once('close', resolve);
|
|
88
|
-
})
|
|
89
|
-
.then(() => fs.unlink(file.filepath))
|
|
90
|
-
.then(() => 0));
|
|
183
|
+
promises.push(fsPromise.unlink(item.storedPath));
|
|
91
184
|
});
|
|
92
185
|
return Promise.allSettled(promises);
|
|
93
186
|
}
|
|
94
187
|
}
|
|
188
|
+
function generateFileName() {
|
|
189
|
+
const buf = Buffer.alloc(10);
|
|
190
|
+
return new Date().toISOString().substring(0, 10).replace(/-/g, '') + randomFillSync(buf).toString('hex');
|
|
191
|
+
}
|
package/esm/index.js
CHANGED
|
@@ -6,11 +6,11 @@ import * as HttpOutgoingHost_ from './http/impl/http-outgoing.host.js';
|
|
|
6
6
|
import * as NodeIncomingMessageHost_ from './http/impl/node-incoming-message.host.js';
|
|
7
7
|
import * as NodeOutgoingMessageHost_ from './http/impl/node-outgoing-message.host.js';
|
|
8
8
|
export * from './execution-context.js';
|
|
9
|
-
export * from './helpers/logger.js';
|
|
10
9
|
export * from './helpers/service-base.js';
|
|
11
10
|
export * from './http/express-adapter.js';
|
|
12
11
|
export * from './http/http-adapter.js';
|
|
13
12
|
export * from './http/http-context.js';
|
|
13
|
+
export * from './http/http-handler.js';
|
|
14
14
|
export * from './http/impl/multipart-reader.js';
|
|
15
15
|
export * from './http/interfaces/http-incoming.interface.js';
|
|
16
16
|
export * from './http/interfaces/http-outgoing.interface.js';
|
package/esm/package.json
ADDED
package/esm/platform-adapter.js
CHANGED
|
@@ -2,7 +2,6 @@ import './augmentation/18n.augmentation.js';
|
|
|
2
2
|
import { I18n } from '@opra/common';
|
|
3
3
|
import { AsyncEventEmitter } from 'strict-typed-events';
|
|
4
4
|
import { kAssetCache } from './constants.js';
|
|
5
|
-
import { Logger } from './helpers/logger.js';
|
|
6
5
|
import { AssetCache } from './http/impl/asset-cache.js';
|
|
7
6
|
/**
|
|
8
7
|
* @class PlatformAdapter
|
|
@@ -12,8 +11,6 @@ export class PlatformAdapter extends AsyncEventEmitter {
|
|
|
12
11
|
super();
|
|
13
12
|
this[kAssetCache] = new AssetCache();
|
|
14
13
|
this.document = document;
|
|
15
|
-
this.logger =
|
|
16
|
-
options?.logger && options.logger instanceof Logger ? options.logger : new Logger({ instance: options?.logger });
|
|
17
14
|
this.i18n = options?.i18n || I18n.defaultInstance;
|
|
18
15
|
}
|
|
19
16
|
}
|
package/package.json
CHANGED
|
@@ -1,42 +1,17 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@opra/core",
|
|
3
|
-
"version": "1.0.0-
|
|
3
|
+
"version": "1.0.0-beta.2",
|
|
4
4
|
"description": "Opra schema package",
|
|
5
5
|
"author": "Panates",
|
|
6
6
|
"license": "MIT",
|
|
7
|
-
"repository": {
|
|
8
|
-
"type": "git",
|
|
9
|
-
"url": "https://github.com/panates/opra.git",
|
|
10
|
-
"directory": "packages/core"
|
|
11
|
-
},
|
|
12
|
-
"scripts": {
|
|
13
|
-
"compile": "tsc",
|
|
14
|
-
"prebuild": "npm run lint && npm run clean",
|
|
15
|
-
"build": "npm run build:cjs && npm run build:esm",
|
|
16
|
-
"build:cjs": "tsc -b tsconfig-build-cjs.json",
|
|
17
|
-
"build:esm": "tsc -b tsconfig-build-esm.json",
|
|
18
|
-
"postbuild": "npm run _copyi18n && npm run _copy_pkg_files",
|
|
19
|
-
"_copy_pkg_files": "cp README.md package.json ../../LICENSE ../../build/core && cp ../../package.cjs.json ../../build/core/cjs/package.json",
|
|
20
|
-
"_copyi18n": "cp -R i18n ../../build/core/i18n",
|
|
21
|
-
"lint": "eslint . --max-warnings=0",
|
|
22
|
-
"lint:fix": "eslint . --max-warnings=0 --fix",
|
|
23
|
-
"format": "prettier . --write --log-level=warn",
|
|
24
|
-
"check": "madge --circular src/**",
|
|
25
|
-
"test": "jest --passWithNoTests",
|
|
26
|
-
"cover": "jest --passWithNoTests --collect-coverage",
|
|
27
|
-
"clean": "npm run clean:src && npm run clean:test && npm run clean:dist && npm run clean:cover",
|
|
28
|
-
"clean:src": "ts-cleanup -s src --all",
|
|
29
|
-
"clean:test": "ts-cleanup -s test --all",
|
|
30
|
-
"clean:dist": "rimraf ../../build/client",
|
|
31
|
-
"clean:cover": "rimraf ../../coverage/client"
|
|
32
|
-
},
|
|
33
7
|
"dependencies": {
|
|
34
|
-
"@browsery/
|
|
35
|
-
"@browsery/
|
|
36
|
-
"@
|
|
37
|
-
"@
|
|
8
|
+
"@browsery/antlr4": "^4.13.3-r1",
|
|
9
|
+
"@browsery/http-parser": "^0.5.9-r1",
|
|
10
|
+
"@browsery/type-is": "^1.6.18-r5",
|
|
11
|
+
"@opra/common": "^1.0.0-beta.2",
|
|
38
12
|
"accepts": "^1.3.8",
|
|
39
13
|
"base64-stream": "^1.0.0",
|
|
14
|
+
"busboy": "^1.6.0",
|
|
40
15
|
"bytes": "^3.1.2",
|
|
41
16
|
"content-disposition": "^0.5.4",
|
|
42
17
|
"content-type": "^1.0.5",
|
|
@@ -44,58 +19,55 @@
|
|
|
44
19
|
"cookie-signature": "^1.2.1",
|
|
45
20
|
"cppzst": "^2.0.12",
|
|
46
21
|
"encodeurl": "^2.0.0",
|
|
47
|
-
"
|
|
22
|
+
"fast-tokenizer": "^1.7.0",
|
|
48
23
|
"fresh": "^0.5.2",
|
|
49
24
|
"iconv-lite": "^0.6.3",
|
|
50
25
|
"mime-types": "^2.1.35",
|
|
51
|
-
"power-tasks": "^1.
|
|
26
|
+
"power-tasks": "^1.11.0",
|
|
52
27
|
"putil-isplainobject": "^1.1.5",
|
|
53
|
-
"putil-merge": "^3.
|
|
28
|
+
"putil-merge": "^3.13.0",
|
|
54
29
|
"putil-varhelpers": "^1.6.5",
|
|
55
30
|
"range-parser": "^1.2.1",
|
|
56
|
-
"raw-body": "^
|
|
31
|
+
"raw-body": "^3.0.0",
|
|
57
32
|
"reflect-metadata": "^0.2.2",
|
|
58
|
-
"strict-typed-events": "^2.
|
|
33
|
+
"strict-typed-events": "^2.8.0",
|
|
34
|
+
"super-fast-md5": "^1.0.3",
|
|
35
|
+
"tslib": "^2.7.0",
|
|
36
|
+
"valgen": "^5.9.0",
|
|
59
37
|
"vary": "^1.1.2"
|
|
60
38
|
},
|
|
61
39
|
"optionalDependencies": {
|
|
62
40
|
"express": "^4.x.x || ^5.x.x",
|
|
63
41
|
"fastify": "^4.x.x"
|
|
64
42
|
},
|
|
65
|
-
"devDependencies": {
|
|
66
|
-
"@faker-js/faker": "^8.4.1",
|
|
67
|
-
"@types/accepts": "^1.3.7",
|
|
68
|
-
"@types/base64-stream": "^1.0.5",
|
|
69
|
-
"@types/bytes": "^3.1.4",
|
|
70
|
-
"@types/content-disposition": "^0.5.8",
|
|
71
|
-
"@types/content-type": "^1.1.8",
|
|
72
|
-
"@types/cookie": "^0.6.0",
|
|
73
|
-
"@types/cookie-parser": "^1.4.7",
|
|
74
|
-
"@types/cookie-signature": "^1.1.2",
|
|
75
|
-
"@types/encodeurl": "^1.0.2",
|
|
76
|
-
"@types/express": "^4.17.21",
|
|
77
|
-
"@types/fresh": "^0.5.2",
|
|
78
|
-
"@types/mime-types": "^2.1.4",
|
|
79
|
-
"@types/range-parser": "^1.2.7",
|
|
80
|
-
"@types/vary": "^1.1.3",
|
|
81
|
-
"cookie-parser": "^1.4.6",
|
|
82
|
-
"crypto-browserify": "^3.12.0",
|
|
83
|
-
"express": "^4.19.2",
|
|
84
|
-
"fastify": "^4.28.1",
|
|
85
|
-
"path-browserify": "^1.0.1",
|
|
86
|
-
"supertest": "^7.0.0",
|
|
87
|
-
"ts-gems": "^3.4.0"
|
|
88
|
-
},
|
|
89
43
|
"type": "module",
|
|
90
|
-
"
|
|
44
|
+
"exports": {
|
|
45
|
+
".": {
|
|
46
|
+
"import": {
|
|
47
|
+
"types": "./types/index.d.ts",
|
|
48
|
+
"default": "./esm/index.js"
|
|
49
|
+
},
|
|
50
|
+
"require": {
|
|
51
|
+
"types": "./types/index.d.cts",
|
|
52
|
+
"default": "./cjs/index.js"
|
|
53
|
+
},
|
|
54
|
+
"default": "./esm/index.js"
|
|
55
|
+
},
|
|
56
|
+
"./package.json": "./package.json"
|
|
57
|
+
},
|
|
91
58
|
"main": "./cjs/index.js",
|
|
59
|
+
"module": "./esm/index.js",
|
|
92
60
|
"types": "./types/index.d.ts",
|
|
61
|
+
"repository": {
|
|
62
|
+
"type": "git",
|
|
63
|
+
"url": "https://github.com/panates/opra.git",
|
|
64
|
+
"directory": "packages/core"
|
|
65
|
+
},
|
|
93
66
|
"engines": {
|
|
94
67
|
"node": ">=16.0",
|
|
95
68
|
"npm": ">=7.0.0"
|
|
96
69
|
},
|
|
97
70
|
"files": [
|
|
98
|
-
"bin/",
|
|
99
71
|
"cjs/",
|
|
100
72
|
"esm/",
|
|
101
73
|
"i18n/",
|
|
@@ -113,4 +85,4 @@
|
|
|
113
85
|
"swagger",
|
|
114
86
|
"raml"
|
|
115
87
|
]
|
|
116
|
-
}
|
|
88
|
+
}
|
package/types/constants.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ApiDocument, OpraSchema } from '@opra/common';
|
|
1
|
+
import { ApiDocument, OpraHttpError, OpraSchema } from '@opra/common';
|
|
2
2
|
import { AsyncEventEmitter } from 'strict-typed-events';
|
|
3
3
|
/**
|
|
4
4
|
* @namespace ExecutionContext
|
|
@@ -18,6 +18,7 @@ export declare abstract class ExecutionContext extends AsyncEventEmitter {
|
|
|
18
18
|
readonly document: ApiDocument;
|
|
19
19
|
readonly protocol: OpraSchema.Protocol;
|
|
20
20
|
readonly platform: string;
|
|
21
|
+
errors: OpraHttpError[];
|
|
21
22
|
protected constructor(init: ExecutionContext.Initiator);
|
|
22
23
|
addListener(event: 'finish', listener: ExecutionContext.OnFinishListener): this;
|
|
23
24
|
removeListener(event: 'finish', listener: ExecutionContext.OnFinishListener): this;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ApiDocument, HttpController } from '@opra/common';
|
|
2
|
-
import { Application } from 'express';
|
|
2
|
+
import { type Application } from 'express';
|
|
3
3
|
import { HttpAdapter } from './http-adapter.js';
|
|
4
4
|
export declare class ExpressAdapter extends HttpAdapter {
|
|
5
5
|
readonly app: Application;
|
|
@@ -1,27 +1,54 @@
|
|
|
1
1
|
import { ApiDocument, HttpApi, OpraSchema } from '@opra/common';
|
|
2
|
-
import { kHandler } from '../constants.js';
|
|
3
2
|
import { PlatformAdapter } from '../platform-adapter.js';
|
|
4
3
|
import { HttpContext } from './http-context.js';
|
|
5
|
-
import { HttpHandler } from './
|
|
4
|
+
import { HttpHandler } from './http-handler.js';
|
|
6
5
|
export declare namespace HttpAdapter {
|
|
6
|
+
type NextCallback = () => Promise<void>;
|
|
7
7
|
/**
|
|
8
|
-
* @type
|
|
8
|
+
* @type InterceptorFunction
|
|
9
9
|
*/
|
|
10
|
-
type
|
|
10
|
+
type InterceptorFunction = IHttpInterceptor['intercept'];
|
|
11
|
+
/**
|
|
12
|
+
* @interface IHttpInterceptor
|
|
13
|
+
*/
|
|
14
|
+
type IHttpInterceptor = {
|
|
15
|
+
intercept(context: HttpContext, next: NextCallback): Promise<void>;
|
|
16
|
+
};
|
|
11
17
|
interface Options extends PlatformAdapter.Options {
|
|
12
18
|
basePath?: string;
|
|
13
|
-
interceptors?:
|
|
14
|
-
onRequest?: (ctx: HttpContext) => void | Promise<void>;
|
|
19
|
+
interceptors?: (InterceptorFunction | IHttpInterceptor)[];
|
|
15
20
|
}
|
|
21
|
+
type EventFunction = (context: HttpContext) => void | Promise<void>;
|
|
22
|
+
interface Events {
|
|
23
|
+
createContext: EventFunction;
|
|
24
|
+
error: EventFunction;
|
|
25
|
+
request: EventFunction;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
export interface HttpAdapter {
|
|
29
|
+
addListener<Event extends keyof HttpAdapter.Events>(event: Event, listener: HttpAdapter.Events[Event]): this;
|
|
30
|
+
addListener(event: string | symbol, listener: (...args: any[]) => void): this;
|
|
31
|
+
on<Event extends keyof HttpAdapter.Events>(event: Event, listener: HttpAdapter.Events[Event]): this;
|
|
32
|
+
on(event: string | symbol, listener: (...args: any[]) => void): this;
|
|
33
|
+
once<Event extends keyof HttpAdapter.Events>(event: Event, listener: HttpAdapter.Events[Event]): this;
|
|
34
|
+
once(event: string | symbol, listener: (...args: any[]) => void): this;
|
|
35
|
+
removeListener<Event extends keyof HttpAdapter.Events>(event: Event, listener: HttpAdapter.Events[Event]): this;
|
|
36
|
+
removeListener(event: string | symbol, listener: (...args: any[]) => void): this;
|
|
37
|
+
off<Event extends keyof HttpAdapter.Events>(event: Event, listener: HttpAdapter.Events[Event]): this;
|
|
38
|
+
off(event: string | symbol, listener: (...args: any[]) => void): this;
|
|
39
|
+
prependListener<Event extends keyof HttpAdapter.Events>(event: Event, listener: HttpAdapter.Events[Event]): this;
|
|
40
|
+
prependListener(event: string | symbol, listener: (...args: any[]) => void): this;
|
|
41
|
+
prependOnceListener<Event extends keyof HttpAdapter.Events>(event: Event, listener: HttpAdapter.Events[Event]): this;
|
|
42
|
+
prependOnceListener(event: string | symbol, listener: (...args: any[]) => void): this;
|
|
16
43
|
}
|
|
17
44
|
/**
|
|
18
45
|
*
|
|
19
46
|
* @class HttpAdapter
|
|
20
47
|
*/
|
|
21
48
|
export declare abstract class HttpAdapter extends PlatformAdapter {
|
|
22
|
-
|
|
49
|
+
readonly handler: HttpHandler;
|
|
23
50
|
readonly protocol: OpraSchema.Protocol;
|
|
24
|
-
interceptors: HttpAdapter.
|
|
51
|
+
interceptors: (HttpAdapter.InterceptorFunction | HttpAdapter.IHttpInterceptor)[];
|
|
25
52
|
protected constructor(document: ApiDocument, options?: HttpAdapter.Options);
|
|
26
53
|
get api(): HttpApi;
|
|
27
54
|
}
|
|
@@ -2,8 +2,8 @@ import { HttpController, HttpMediaType, HttpOperation, OpraSchema } from '@opra/
|
|
|
2
2
|
import { ExecutionContext } from '../execution-context.js';
|
|
3
3
|
import type { HttpAdapter } from './http-adapter';
|
|
4
4
|
import { MultipartReader } from './impl/multipart-reader.js';
|
|
5
|
-
import type { HttpIncoming } from './interfaces/http-incoming.interface';
|
|
6
|
-
import type { HttpOutgoing } from './interfaces/http-outgoing.interface';
|
|
5
|
+
import type { HttpIncoming } from './interfaces/http-incoming.interface.js';
|
|
6
|
+
import type { HttpOutgoing } from './interfaces/http-outgoing.interface.js';
|
|
7
7
|
export declare namespace HttpContext {
|
|
8
8
|
interface Initiator extends Omit<ExecutionContext.Initiator, 'document' | 'protocol'> {
|
|
9
9
|
adapter: HttpAdapter;
|
|
@@ -26,9 +26,9 @@ export declare class HttpContext extends ExecutionContext {
|
|
|
26
26
|
protected _multipartReader?: MultipartReader;
|
|
27
27
|
readonly protocol: OpraSchema.Protocol;
|
|
28
28
|
readonly adapter: HttpAdapter;
|
|
29
|
-
readonly controller
|
|
29
|
+
readonly controller?: HttpController;
|
|
30
30
|
readonly controllerInstance?: any;
|
|
31
|
-
readonly operation
|
|
31
|
+
readonly operation?: HttpOperation;
|
|
32
32
|
readonly operationHandler?: Function;
|
|
33
33
|
readonly request: HttpIncoming;
|
|
34
34
|
readonly response: HttpOutgoing;
|
|
@@ -1,9 +1,8 @@
|
|
|
1
|
-
import { HttpOperationResponse } from '@opra/common';
|
|
2
|
-
import { kAssetCache } from '
|
|
3
|
-
import type { HttpAdapter } from '
|
|
4
|
-
import { HttpContext } from '
|
|
5
|
-
import {
|
|
6
|
-
import { AssetCache } from './asset-cache.js';
|
|
1
|
+
import { HttpOperationResponse, OpraException, OpraHttpError } from '@opra/common';
|
|
2
|
+
import { kAssetCache } from '../constants.js';
|
|
3
|
+
import type { HttpAdapter } from './http-adapter.js';
|
|
4
|
+
import { HttpContext } from './http-context.js';
|
|
5
|
+
import { AssetCache } from './impl/asset-cache.js';
|
|
7
6
|
/**
|
|
8
7
|
* @namespace
|
|
9
8
|
*/
|
|
@@ -16,6 +15,7 @@ export declare namespace HttpHandler {
|
|
|
16
15
|
contentType?: string;
|
|
17
16
|
operationResponse?: HttpOperationResponse;
|
|
18
17
|
body?: any;
|
|
18
|
+
projection?: string[] | '*';
|
|
19
19
|
}
|
|
20
20
|
}
|
|
21
21
|
/**
|
|
@@ -24,6 +24,7 @@ export declare namespace HttpHandler {
|
|
|
24
24
|
export declare class HttpHandler {
|
|
25
25
|
readonly adapter: HttpAdapter;
|
|
26
26
|
protected [kAssetCache]: AssetCache;
|
|
27
|
+
onError?: (context: HttpContext, error: OpraException) => void | Promise<void>;
|
|
27
28
|
constructor(adapter: HttpAdapter);
|
|
28
29
|
/**
|
|
29
30
|
* Main http request handler
|
|
@@ -60,7 +61,9 @@ export declare class HttpHandler {
|
|
|
60
61
|
* @param responseValue
|
|
61
62
|
* @protected
|
|
62
63
|
*/
|
|
63
|
-
|
|
64
|
+
sendResponse(context: HttpContext, responseValue?: any): Promise<void>;
|
|
65
|
+
protected _sendErrorResponse(context: HttpContext): Promise<void>;
|
|
66
|
+
sendDocumentSchema(context: HttpContext): Promise<void>;
|
|
64
67
|
/**
|
|
65
68
|
*
|
|
66
69
|
* @param context
|
|
@@ -68,6 +71,5 @@ export declare class HttpHandler {
|
|
|
68
71
|
* @protected
|
|
69
72
|
*/
|
|
70
73
|
protected _determineResponseArgs(context: HttpContext, body: any): HttpHandler.ResponseArgs;
|
|
71
|
-
|
|
72
|
-
sendErrorResponse(response: HttpOutgoing, errors: any[]): Promise<void>;
|
|
74
|
+
protected _wrapExceptions(exceptions: any[]): OpraHttpError[];
|
|
73
75
|
}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
/// <reference types="node" />
|
|
2
1
|
import parseRange from 'range-parser';
|
|
3
|
-
import type { HttpIncoming } from '../interfaces/http-incoming.interface';
|
|
2
|
+
import type { HttpIncoming } from '../interfaces/http-incoming.interface.js';
|
|
4
3
|
import { BodyReader } from '../utils/body-reader.js';
|
|
5
4
|
export interface HttpIncomingHost extends HttpIncoming {
|
|
6
5
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { CookieOptions, HttpOutgoing } from '../interfaces/http-outgoing.interface';
|
|
1
|
+
import type { CookieOptions, HttpOutgoing } from '../interfaces/http-outgoing.interface.js';
|
|
2
2
|
export interface HttpOutgoingHost extends HttpOutgoing {
|
|
3
3
|
}
|
|
4
4
|
export declare class HttpOutgoingHost {
|