@opra/core 1.0.0-alpha.6 → 1.0.0-alpha.8

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 (34) hide show
  1. package/cjs/augmentation/18n.augmentation.js +1 -1
  2. package/cjs/http/express-adapter.js +7 -12
  3. package/cjs/http/http-context.js +5 -3
  4. package/cjs/http/impl/http-handler.js +54 -41
  5. package/cjs/http/impl/http-incoming.host.js +3 -3
  6. package/cjs/http/impl/http-outgoing.host.js +2 -2
  7. package/cjs/http/impl/multipart-reader.js +4 -10
  8. package/cjs/http/impl/node-incoming-message.host.js +5 -3
  9. package/cjs/http/interfaces/node-incoming-message.interface.js +3 -2
  10. package/cjs/http/utils/body-reader.js +6 -4
  11. package/cjs/http/utils/common.js +2 -1
  12. package/cjs/index.js +3 -3
  13. package/cjs/platform-adapter.js +1 -1
  14. package/esm/augmentation/18n.augmentation.js +1 -1
  15. package/esm/http/express-adapter.js +7 -12
  16. package/esm/http/http-context.js +5 -3
  17. package/esm/http/impl/http-handler.js +54 -41
  18. package/esm/http/impl/http-incoming.host.js +3 -3
  19. package/esm/http/impl/http-outgoing.host.js +2 -2
  20. package/esm/http/impl/multipart-reader.js +4 -10
  21. package/esm/http/impl/node-incoming-message.host.js +5 -3
  22. package/esm/http/interfaces/node-incoming-message.interface.js +3 -2
  23. package/esm/http/utils/body-reader.js +6 -4
  24. package/esm/http/utils/common.js +2 -1
  25. package/esm/index.js +3 -3
  26. package/esm/platform-adapter.js +1 -1
  27. package/package.json +10 -4
  28. package/types/augmentation/18n.augmentation.d.ts +1 -1
  29. package/types/execution-context.d.ts +1 -1
  30. package/types/http/express-adapter.d.ts +1 -1
  31. package/types/http/impl/node-incoming-message.host.d.ts +1 -1
  32. package/types/http/utils/body-reader.d.ts +1 -1
  33. package/types/index.d.ts +3 -3
  34. package/types/platform-adapter.d.ts +1 -1
@@ -1,6 +1,6 @@
1
- import { vg } from 'valgen';
2
1
  import typeIs from '@browsery/type-is';
3
2
  import { BadRequestError, InternalServerError, NotAcceptableError, } from '@opra/common';
3
+ import { vg } from 'valgen';
4
4
  import { kAssetCache } from '../constants.js';
5
5
  import { ExecutionContext } from '../execution-context.js';
6
6
  import { MultipartReader } from './impl/multipart-reader.js';
@@ -74,10 +74,11 @@ export class HttpContext extends ExecutionContext {
74
74
  }
75
75
  /** Check required fields */
76
76
  for (const field of multipartFields) {
77
- if (field.required && !fieldsFound.get(field))
77
+ if (field.required && !fieldsFound.get(field)) {
78
78
  throw new BadRequestError({
79
79
  message: `Multipart field (${field.fieldName}) is required`,
80
80
  });
81
+ }
81
82
  }
82
83
  }
83
84
  return this._body;
@@ -85,8 +86,9 @@ export class HttpContext extends ExecutionContext {
85
86
  this._body = await this.request.readBody({ limit: operation.requestBody?.maxContentSize });
86
87
  if (this._body != null) {
87
88
  // Convert Buffer to string if media is text
88
- if (Buffer.isBuffer(this._body) && request.is(['json', 'xml', 'txt', 'text']))
89
+ if (Buffer.isBuffer(this._body) && request.is(['json', 'xml', 'txt', 'text'])) {
89
90
  this._body = this._body.toString('utf-8');
91
+ }
90
92
  // Transform text to Object if media is JSON
91
93
  if (typeof this._body === 'string' && request.is(['json']))
92
94
  this._body = JSON.parse(this._body);
@@ -1,10 +1,10 @@
1
+ import * as process from 'node:process';
2
+ import typeIs from '@browsery/type-is';
3
+ import { BadRequestError, HttpHeaderCodes, HttpStatusCode, InternalServerError, isBlob, isReadableStream, IssueSeverity, MethodNotAllowedError, MimeTypes, OperationResult, OpraException, OpraSchema, translate, } from '@opra/common';
1
4
  import { parse as parseContentType } from 'content-type';
2
5
  import { splitString } from 'fast-tokenizer';
3
- import * as process from 'node:process';
4
6
  import { asMutable } from 'ts-gems';
5
7
  import { toArray, ValidationError, vg } from 'valgen';
6
- import typeIs from '@browsery/type-is';
7
- import { BadRequestError, HttpHeaderCodes, HttpStatusCode, InternalServerError, isBlob, isReadableStream, IssueSeverity, MethodNotAllowedError, MimeTypes, OperationResult, OpraException, OpraSchema, translate, } from '@opra/common';
8
8
  import { kAssetCache } from '../../constants.js';
9
9
  import { wrapException } from '../utils/wrap-exception.js';
10
10
  /**
@@ -60,7 +60,8 @@ export class HttpHandler {
60
60
  else
61
61
  await this._executeRequest(context);
62
62
  }
63
- catch (e) {
63
+ catch (error) {
64
+ let e = error;
64
65
  if (e instanceof ValidationError) {
65
66
  e = new InternalServerError({
66
67
  message: translate('error:RESPONSE_VALIDATION,', 'Response validation failed'),
@@ -72,11 +73,10 @@ export class HttpHandler {
72
73
  e = wrapException(e);
73
74
  response.status(e.statusCode || e.status || HttpStatusCode.INTERNAL_SERVER_ERROR);
74
75
  response.contentType(MimeTypes.opra_response_json);
75
- await this._sendResponse(context, new OperationResult({ errors: [e] })).finally(() => {
76
+ await this._sendResponse(context, new OperationResult({ errors: [e.toJSON()] })).finally(() => {
76
77
  if (!response.finished)
77
78
  response.end();
78
79
  });
79
- // if (!outgoing.writableEnded) await this._sendErrorResponse(context.response, [error]);
80
80
  }
81
81
  finally {
82
82
  await context.emitAsync('finish');
@@ -109,10 +109,10 @@ export class HttpHandler {
109
109
  */
110
110
  async _parseParameters(context) {
111
111
  const { operation, request } = context;
112
- let prmName = '';
112
+ let key = '';
113
113
  try {
114
114
  const onFail = (issue) => {
115
- issue.location = prmName;
115
+ issue.location = key;
116
116
  return issue;
117
117
  };
118
118
  /** prepare decoders */
@@ -127,9 +127,9 @@ export class HttpHandler {
127
127
  const paramsLeft = new Set([...operation.parameters, ...operation.owner.parameters]);
128
128
  /** parse cookie parameters */
129
129
  if (request.cookies) {
130
- for (prmName of Object.keys(request.cookies)) {
131
- const oprPrm = operation.findParameter(prmName, 'cookie');
132
- const cntPrm = operation.owner.findParameter(prmName, 'cookie');
130
+ for (key of Object.keys(request.cookies)) {
131
+ const oprPrm = operation.findParameter(key, 'cookie');
132
+ const cntPrm = operation.owner.findParameter(key, 'cookie');
133
133
  const prm = oprPrm || cntPrm;
134
134
  if (!prm)
135
135
  continue;
@@ -138,16 +138,17 @@ export class HttpHandler {
138
138
  if (cntPrm)
139
139
  paramsLeft.delete(cntPrm);
140
140
  const decode = getDecoder(prm);
141
- const v = decode(request.cookies[prmName], { coerce: true, label: prmName, onFail });
141
+ const v = decode(request.cookies[key], { coerce: true, label: key, onFail });
142
+ const prmName = typeof prm.name === 'string' ? prm.name : key;
142
143
  if (v !== undefined)
143
144
  context.cookies[prmName] = v;
144
145
  }
145
146
  }
146
147
  /** parse headers */
147
148
  if (request.headers) {
148
- for (prmName of Object.keys(request.headers)) {
149
- const oprPrm = operation.findParameter(prmName, 'header');
150
- const cntPrm = operation.owner.findParameter(prmName, 'header');
149
+ for (key of Object.keys(request.headers)) {
150
+ const oprPrm = operation.findParameter(key, 'header');
151
+ const cntPrm = operation.owner.findParameter(key, 'header');
151
152
  const prm = oprPrm || cntPrm;
152
153
  if (!prm)
153
154
  continue;
@@ -156,16 +157,17 @@ export class HttpHandler {
156
157
  if (cntPrm)
157
158
  paramsLeft.delete(cntPrm);
158
159
  const decode = getDecoder(prm);
159
- const v = decode(request.headers[prmName], { coerce: true, label: prmName, onFail });
160
+ const v = decode(request.headers[key], { coerce: true, label: key, onFail });
161
+ const prmName = typeof prm.name === 'string' ? prm.name : key;
160
162
  if (v !== undefined)
161
163
  context.headers[prmName] = v;
162
164
  }
163
165
  }
164
166
  /** parse path parameters */
165
167
  if (request.params) {
166
- for (prmName of Object.keys(request.params)) {
167
- const oprPrm = operation.findParameter(prmName, 'path');
168
- const cntPrm = operation.owner.findParameter(prmName, 'path');
168
+ for (key of Object.keys(request.params)) {
169
+ const oprPrm = operation.findParameter(key, 'path');
170
+ const cntPrm = operation.owner.findParameter(key, 'path');
169
171
  const prm = oprPrm || cntPrm;
170
172
  if (!prm)
171
173
  continue;
@@ -174,17 +176,17 @@ export class HttpHandler {
174
176
  if (cntPrm)
175
177
  paramsLeft.delete(cntPrm);
176
178
  const decode = getDecoder(prm);
177
- const v = decode(request.params[prmName], { coerce: true, label: prmName, onFail });
179
+ const v = decode(request.params[key], { coerce: true, label: key, onFail });
178
180
  if (v !== undefined)
179
- context.pathParams[prmName] = v;
181
+ context.pathParams[key] = v;
180
182
  }
181
183
  }
182
184
  /** parse query parameters */
183
185
  const url = new URL(request.originalUrl || request.url || '/', 'http://tempuri.org');
184
186
  const { searchParams } = url;
185
- for (prmName of searchParams.keys()) {
186
- const oprPrm = operation.findParameter(prmName, 'query');
187
- const cntPrm = operation.owner.findParameter(prmName, 'query');
187
+ for (key of searchParams.keys()) {
188
+ const oprPrm = operation.findParameter(key, 'query');
189
+ const cntPrm = operation.owner.findParameter(key, 'query');
188
190
  const prm = oprPrm || cntPrm;
189
191
  if (!prm)
190
192
  continue;
@@ -193,15 +195,16 @@ export class HttpHandler {
193
195
  if (cntPrm)
194
196
  paramsLeft.delete(cntPrm);
195
197
  const decode = getDecoder(prm);
196
- let values = searchParams?.getAll(prmName);
198
+ let values = searchParams?.getAll(key);
199
+ const prmName = typeof prm.name === 'string' ? prm.name : key;
197
200
  if (values?.length && prm.isArray) {
198
201
  values = values.map(v => splitString(v, { delimiters: prm.arraySeparator, quotes: true })).flat();
199
- values = values.map(v => decode(v, { coerce: true, label: prmName, onFail }));
202
+ values = values.map(v => decode(v, { coerce: true, label: key, onFail }));
200
203
  if (values.length)
201
204
  context.queryParams[prmName] = values;
202
205
  }
203
206
  else {
204
- const v = decode(values[0], { coerce: true, label: prmName, onFail });
207
+ const v = decode(values[0], { coerce: true, label: key, onFail });
205
208
  if (values.length)
206
209
  context.queryParams[prmName] = v;
207
210
  }
@@ -216,8 +219,8 @@ export class HttpHandler {
216
219
  }
217
220
  catch (e) {
218
221
  if (e instanceof ValidationError) {
219
- e = new BadRequestError({
220
- message: `Invalid parameter (${prmName}) value. ` + e.message,
222
+ throw new BadRequestError({
223
+ message: `Invalid parameter (${key}) value. ` + e.message,
221
224
  code: 'REQUEST_VALIDATION',
222
225
  details: e.issues,
223
226
  }, e);
@@ -237,9 +240,8 @@ export class HttpHandler {
237
240
  let contentType = request.header('content-type');
238
241
  if (contentType) {
239
242
  contentType = parseContentType(contentType).type;
240
- mediaType = operation.requestBody.content.find(mc => {
241
- return (mc.contentType && typeIs.is(contentType, Array.isArray(mc.contentType) ? mc.contentType : [mc.contentType]));
242
- });
243
+ mediaType = operation.requestBody.content.find(mc => mc.contentType &&
244
+ typeIs.is(contentType, Array.isArray(mc.contentType) ? mc.contentType : [mc.contentType]));
243
245
  }
244
246
  if (!mediaType) {
245
247
  const contentTypes = operation.requestBody.content.map(mc => mc.contentType).flat();
@@ -258,11 +260,12 @@ export class HttpHandler {
258
260
  throw new MethodNotAllowedError();
259
261
  const responseValue = await context.operationHandler.call(context.controllerInstance, context);
260
262
  const { response } = context;
261
- if (!response.writableEnded)
263
+ if (!response.writableEnded) {
262
264
  await this._sendResponse(context, responseValue).finally(() => {
263
265
  if (!response.writableEnded)
264
266
  response.end();
265
267
  });
268
+ }
266
269
  }
267
270
  /**
268
271
  *
@@ -388,8 +391,9 @@ export class HttpHandler {
388
391
  /** Filter available HttpOperationResponse instances according to status code. */
389
392
  const filteredResponses = operation.responses.filter(r => r.statusCode.find(sc => sc.start <= statusCode && sc.end >= statusCode));
390
393
  /** Throw InternalServerError if controller returns non-configured status code */
391
- if (!filteredResponses.length && statusCode < 400)
394
+ if (!filteredResponses.length && statusCode < 400) {
392
395
  throw new InternalServerError(`No responses defined for status code ${statusCode} in operation "${operation.name}"`);
396
+ }
393
397
  /** We search for content-type in filtered HttpOperationResponse array */
394
398
  if (filteredResponses.length) {
395
399
  /** If no response returned, and content-type has not been set (No response wants to be returned by operation) */
@@ -402,8 +406,9 @@ export class HttpHandler {
402
406
  if (contentType) {
403
407
  // Find HttpEndpointResponse instance according to content-type header
404
408
  operationResponse = filteredResponses.find(r => typeIs.is(contentType, toArray(r.contentType)));
405
- if (!operationResponse)
409
+ if (!operationResponse) {
406
410
  throw new InternalServerError(`Operation didn't configured to return "${contentType}" content`);
411
+ }
407
412
  }
408
413
  else {
409
414
  /** Select first HttpOperationResponse if content-type header has not been set */
@@ -435,23 +440,26 @@ export class HttpHandler {
435
440
  case 'Entity.Get':
436
441
  case 'Entity.FindMany':
437
442
  case 'Entity.Update': {
438
- if (!(body instanceof OperationResult))
443
+ if (!(body instanceof OperationResult)) {
439
444
  body = new OperationResult({
440
445
  payload: body,
441
446
  });
447
+ }
442
448
  if ((composition === 'Entity.Create' || composition === 'Entity.Update') &&
443
449
  composition &&
444
- body.affected == null)
450
+ body.affected == null) {
445
451
  body.affected = 1;
452
+ }
446
453
  break;
447
454
  }
448
455
  case 'Entity.Delete':
449
456
  case 'Entity.DeleteMany':
450
457
  case 'Entity.UpdateMany': {
451
- if (!(body instanceof OperationResult))
458
+ if (!(body instanceof OperationResult)) {
452
459
  body = new OperationResult({
453
460
  affected: body,
454
461
  });
462
+ }
455
463
  body.affected =
456
464
  typeof body.affected === 'number'
457
465
  ? body.affected
@@ -462,15 +470,19 @@ export class HttpHandler {
462
470
  : undefined;
463
471
  break;
464
472
  }
473
+ default:
474
+ break;
465
475
  }
466
476
  }
467
- if (responseArgs.contentType && responseArgs.contentType !== parsedContentType?.type)
477
+ if (responseArgs.contentType && responseArgs.contentType !== parsedContentType?.type) {
468
478
  response.setHeader('content-type', responseArgs.contentType);
479
+ }
469
480
  if (responseArgs.contentType &&
470
481
  body != null &&
471
482
  !(body instanceof OperationResult) &&
472
- typeIs.is(responseArgs.contentType, [MimeTypes.opra_response_json]))
483
+ typeIs.is(responseArgs.contentType, [MimeTypes.opra_response_json])) {
473
484
  body = new OperationResult({ payload: body });
485
+ }
474
486
  if (hasBody)
475
487
  responseArgs.body = body;
476
488
  return responseArgs;
@@ -483,12 +495,13 @@ export class HttpHandler {
483
495
  const { searchParams } = url;
484
496
  const documentId = searchParams.get('id');
485
497
  const doc = documentId ? document.findDocument(documentId) : document;
486
- if (!doc)
498
+ if (!doc) {
487
499
  return this.sendErrorResponse(response, [
488
500
  new BadRequestError({
489
501
  message: `Document with given id [${documentId}] does not exists`,
490
502
  }),
491
503
  ]);
504
+ }
492
505
  /** Check if response cache exists */
493
506
  let responseBody = this[kAssetCache].get(doc, `$schema`);
494
507
  /** Create response if response cache does not exists */
@@ -2,10 +2,10 @@
2
2
  Some parts of this file contains codes from open source express library
3
3
  https://github.com/expressjs
4
4
  */
5
+ import typeIs from '@browsery/type-is';
5
6
  import accepts from 'accepts';
6
7
  import fresh from 'fresh';
7
8
  import parseRange from 'range-parser';
8
- import typeIs from '@browsery/type-is';
9
9
  import { BodyReader } from '../utils/body-reader.js';
10
10
  export class HttpIncomingHost {
11
11
  get protocol() {
@@ -37,11 +37,11 @@ export class HttpIncomingHost {
37
37
  get fresh() {
38
38
  const method = this.method;
39
39
  // GET or HEAD for weak freshness validation only
40
- if ('GET' !== method && 'HEAD' !== method)
40
+ if (method !== 'GET' && method !== 'HEAD')
41
41
  return false;
42
42
  const status = this.res?.statusCode;
43
43
  // 2xx or 304 as per rfc2616 14.26
44
- if ((status >= 200 && status < 300) || 304 === status) {
44
+ if ((status >= 200 && status < 300) || status === 304) {
45
45
  return fresh(this.headers, {
46
46
  etag: this.res.getHeader('ETag'),
47
47
  'last-modified': this.res.getHeader('Last-Modified'),
@@ -2,6 +2,7 @@
2
2
  Some parts of this file contains codes from open source express library
3
3
  https://github.com/expressjs
4
4
  */
5
+ import { HttpStatusCode } from '@opra/common';
5
6
  import contentDisposition from 'content-disposition';
6
7
  import contentType from 'content-type';
7
8
  import cookie from 'cookie';
@@ -11,7 +12,6 @@ import mime from 'mime-types';
11
12
  import path from 'path';
12
13
  import { toString } from 'putil-varhelpers';
13
14
  import vary from 'vary';
14
- import { HttpStatusCode } from '@opra/common';
15
15
  const charsetRegExp = /;\s*charset\s*=/;
16
16
  export class HttpOutgoingHost {
17
17
  attachment(filename) {
@@ -160,7 +160,7 @@ export class HttpOutgoingHost {
160
160
  if (req?.fresh)
161
161
  this.statusCode = 304;
162
162
  // strip irrelevant headers
163
- if (204 === this.statusCode || 304 === this.statusCode) {
163
+ if (this.statusCode === 204 || this.statusCode === 304) {
164
164
  this.removeHeader('Content-Type');
165
165
  this.removeHeader('Content-Length');
166
166
  this.removeHeader('Transfer-Encoding');
@@ -12,9 +12,7 @@ export class MultipartReader extends EventEmitter {
12
12
  this._incoming = incoming;
13
13
  const form = (this._form = formidable({
14
14
  ...options,
15
- filter: (part) => {
16
- return !this._cancelled && (!options?.filter || options.filter(part));
17
- },
15
+ filter: (part) => !this._cancelled && (!options?.filter || options.filter(part)),
18
16
  }));
19
17
  form.once('error', () => {
20
18
  this._cancelled = true;
@@ -69,7 +67,7 @@ export class MultipartReader extends EventEmitter {
69
67
  }
70
68
  resume() {
71
69
  if (!this._started)
72
- this._form.parse(this._incoming, () => void 0);
70
+ this._form.parse(this._incoming, () => undefined);
73
71
  if (this._form.req)
74
72
  this._form.resume();
75
73
  }
@@ -88,12 +86,8 @@ export class MultipartReader extends EventEmitter {
88
86
  return resolve();
89
87
  file._writeStream.once('close', resolve);
90
88
  })
91
- .then(() => {
92
- return fs.unlink(file.filepath);
93
- })
94
- .then(() => {
95
- return 0;
96
- }));
89
+ .then(() => fs.unlink(file.filepath))
90
+ .then(() => 0));
97
91
  });
98
92
  return Promise.allSettled(promises);
99
93
  }
@@ -1,5 +1,5 @@
1
- import { Duplex, Readable } from 'stream';
2
1
  import { isAsyncIterable, isIterable } from '@opra/common';
2
+ import { Duplex, Readable } from 'stream';
3
3
  import { convertToHeaders, convertToHeadersDistinct } from '../utils/convert-to-headers.js';
4
4
  import { convertToRawHeaders } from '../utils/convert-to-raw-headers.js';
5
5
  export const CRLF = Buffer.from('\r\n');
@@ -33,10 +33,12 @@ export class NodeIncomingMessageHost extends Duplex {
33
33
  else
34
34
  this.body = Buffer.from(JSON.stringify(init.body), 'utf-8');
35
35
  }
36
- if (init.headers)
36
+ if (init.headers) {
37
37
  this.rawHeaders = Array.isArray(init.headers) ? init.headers : convertToRawHeaders(init.headers);
38
- if (init.trailers)
38
+ }
39
+ if (init.trailers) {
39
40
  this.rawTrailers = Array.isArray(init.trailers) ? init.trailers : convertToRawHeaders(init.trailers);
41
+ }
40
42
  this.ip = init.ip || '';
41
43
  this.ips = init.ips || (this.ip ? [this.ip] : []);
42
44
  if (this.body && !this.headers['content-length'])
@@ -1,6 +1,6 @@
1
- import { Readable } from 'stream';
2
1
  import { HTTPParser } from '@browsery/http-parser';
3
2
  import { isAsyncIterable, isIterable } from '@opra/common';
3
+ import { Readable } from 'stream';
4
4
  import { CRLF, kHttpParser, NodeIncomingMessageHost } from '../impl/node-incoming-message.host.js';
5
5
  import { concatReadable } from '../utils/concat-readable.js';
6
6
  /**
@@ -14,8 +14,9 @@ export var NodeIncomingMessage;
14
14
  * @param iterable
15
15
  */
16
16
  function from(iterable) {
17
- if (typeof iterable === 'object' && !(isIterable(iterable) || isAsyncIterable(iterable)))
17
+ if (typeof iterable === 'object' && !(isIterable(iterable) || isAsyncIterable(iterable))) {
18
18
  return new NodeIncomingMessageHost(iterable);
19
+ }
19
20
  const msg = new NodeIncomingMessageHost();
20
21
  const parser = (msg[kHttpParser] = new HTTPParser(HTTPParser.REQUEST));
21
22
  let bodyChunks;
@@ -1,3 +1,5 @@
1
+ import typeIs from '@browsery/type-is';
2
+ import { BadRequestError, InternalServerError, OpraHttpError } from '@opra/common';
1
3
  import { Base64Decode } from 'base64-stream';
2
4
  import byteParser from 'bytes';
3
5
  import { parse as parseContentType } from 'content-type';
@@ -5,8 +7,6 @@ import { EventEmitter } from 'events';
5
7
  import iconv from 'iconv-lite';
6
8
  import { Writable } from 'stream';
7
9
  import * as zlib from 'zlib';
8
- import typeIs from '@browsery/type-is';
9
- import { BadRequestError, InternalServerError, OpraHttpError } from '@opra/common';
10
10
  /**
11
11
  *
12
12
  * @class BodyReader
@@ -29,11 +29,12 @@ export class BodyReader extends EventEmitter {
29
29
  }
30
30
  async read() {
31
31
  /* istanbul ignore next */
32
- if (this._completed)
32
+ if (this._completed) {
33
33
  throw new InternalServerError({
34
34
  message: 'Stream already read',
35
35
  code: 'STREAM_ALREADY_READ',
36
36
  });
37
+ }
37
38
  if (!this.req.readable) {
38
39
  throw new InternalServerError({
39
40
  message: 'Stream is not readable',
@@ -57,8 +58,9 @@ export class BodyReader extends EventEmitter {
57
58
  * http://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.3
58
59
  */
59
60
  const contentLength = parseInt(this.req.headers['content-length'] || '0', 10);
60
- if (this.req.headers['transfer-encoding'] === undefined && !(contentLength && !isNaN(contentLength)))
61
+ if (this.req.headers['transfer-encoding'] === undefined && !(contentLength && !isNaN(contentLength))) {
61
62
  return this.onEnd();
63
+ }
62
64
  // check the length and limit options.
63
65
  // note: we intentionally leave the stream paused,
64
66
  // so users should handle the stream themselves.
@@ -55,6 +55,7 @@ export const validateHeaderValue = hideStackFrames((name, value) => {
55
55
  }
56
56
  });
57
57
  export function validateString(value, name) {
58
- if (typeof value !== 'string')
58
+ if (typeof value !== 'string') {
59
59
  throw new TypeError(`Invalid ${name ? name + ' ' : ''}argument. Value must be a string`);
60
+ }
60
61
  }
package/esm/index.js CHANGED
@@ -6,20 +6,20 @@ 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 './platform-adapter.js';
10
- export * from './type-guards.js';
11
9
  export * from './helpers/logger.js';
12
10
  export * from './helpers/service-base.js';
13
11
  export * from './http/express-adapter.js';
14
12
  export * from './http/http-adapter.js';
15
13
  export * from './http/http-context.js';
14
+ export * from './http/impl/multipart-reader.js';
16
15
  export * from './http/interfaces/http-incoming.interface.js';
17
16
  export * from './http/interfaces/http-outgoing.interface.js';
18
17
  export * from './http/interfaces/node-incoming-message.interface.js';
19
18
  export * from './http/interfaces/node-outgoing-message.interface.js';
20
- export * from './http/impl/multipart-reader.js';
21
19
  export * from './http/utils/wrap-exception.js';
22
20
  export * from './interfaces/logger.interface.js';
21
+ export * from './platform-adapter.js';
22
+ export * from './type-guards.js';
23
23
  export var classes;
24
24
  (function (classes) {
25
25
  classes.HttpIncomingHost = HttpIncomingHost_.HttpIncomingHost;
@@ -1,6 +1,6 @@
1
1
  import './augmentation/18n.augmentation.js';
2
- import { AsyncEventEmitter } from 'strict-typed-events';
3
2
  import { I18n } from '@opra/common';
3
+ import { AsyncEventEmitter } from 'strict-typed-events';
4
4
  import { kAssetCache } from './constants.js';
5
5
  import { Logger } from './helpers/logger.js';
6
6
  import { AssetCache } from './http/impl/asset-cache.js';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@opra/core",
3
- "version": "1.0.0-alpha.6",
3
+ "version": "1.0.0-alpha.8",
4
4
  "description": "Opra schema package",
5
5
  "author": "Panates",
6
6
  "license": "MIT",
@@ -19,8 +19,9 @@
19
19
  "_copy_pkg_files": "cp README.md package.json ../../LICENSE ../../build/core && cp ../../package.cjs.json ../../build/core/cjs/package.json",
20
20
  "_copyi18n": "cp -R i18n ../../build/core/i18n",
21
21
  "lint": "eslint . --max-warnings=0",
22
- "check": "madge --circular src/**",
22
+ "lint:fix": "eslint . --max-warnings=0 --fix",
23
23
  "format": "prettier . --write --log-level=warn",
24
+ "check": "madge --circular src/**",
24
25
  "test": "jest --passWithNoTests",
25
26
  "cover": "jest --passWithNoTests --collect-coverage",
26
27
  "clean": "npm run clean:src && npm run clean:test && npm run clean:dist && npm run clean:cover",
@@ -30,8 +31,9 @@
30
31
  "clean:cover": "rimraf ../../coverage/client"
31
32
  },
32
33
  "dependencies": {
34
+ "@browsery/http-parser": "^0.5.8",
33
35
  "@browsery/type-is": "^1.6.18-r2",
34
- "@opra/common": "^1.0.0-alpha.6",
36
+ "@opra/common": "^1.0.0-alpha.8",
35
37
  "@types/formidable": "^3.4.5",
36
38
  "accepts": "^1.3.8",
37
39
  "base64-stream": "^1.0.0",
@@ -44,12 +46,15 @@
44
46
  "encodeurl": "^2.0.0",
45
47
  "formidable": "^3.5.1",
46
48
  "fresh": "^0.5.2",
49
+ "iconv-lite": "^0.6.3",
47
50
  "mime-types": "^2.1.35",
48
51
  "power-tasks": "^1.7.3",
49
52
  "putil-isplainobject": "^1.1.5",
53
+ "putil-merge": "^3.12.1",
50
54
  "putil-varhelpers": "^1.6.5",
51
55
  "range-parser": "^1.2.1",
52
56
  "raw-body": "^2.5.2",
57
+ "reflect-metadata": "^0.2.2",
53
58
  "strict-typed-events": "^2.3.3",
54
59
  "vary": "^1.1.2"
55
60
  },
@@ -76,8 +81,9 @@
76
81
  "cookie-parser": "^1.4.6",
77
82
  "crypto-browserify": "^3.12.0",
78
83
  "express": "^4.19.2",
79
- "fastify": "^4.28.0",
84
+ "fastify": "^4.28.1",
80
85
  "path-browserify": "^1.0.1",
86
+ "supertest": "^7.0.0",
81
87
  "ts-gems": "^3.4.0"
82
88
  },
83
89
  "type": "module",
@@ -1,4 +1,3 @@
1
- import { FallbackLng, LanguageResource } from '@opra/common';
2
1
  declare module '@opra/common' {
3
2
  interface I18n {
4
3
  loadResourceDir(dirnames: string | string[], deep?: boolean, overwrite?: boolean): Promise<void>;
@@ -35,3 +34,4 @@ declare module '@opra/common' {
35
34
  }
36
35
  }
37
36
  }
37
+ export {};
@@ -1,5 +1,5 @@
1
- import { AsyncEventEmitter } from 'strict-typed-events';
2
1
  import { ApiDocument, OpraSchema } from '@opra/common';
2
+ import { AsyncEventEmitter } from 'strict-typed-events';
3
3
  /**
4
4
  * @namespace ExecutionContext
5
5
  */
@@ -1,5 +1,5 @@
1
- import { Application } from 'express';
2
1
  import { ApiDocument, HttpController } from '@opra/common';
2
+ import { Application } from 'express';
3
3
  import { HttpAdapter } from './http-adapter.js';
4
4
  export declare class ExpressAdapter extends HttpAdapter {
5
5
  readonly app: Application;
@@ -2,9 +2,9 @@
2
2
  /// <reference types="node" />
3
3
  /// <reference types="node" />
4
4
  /// <reference types="node" />
5
+ import { HTTPParserJS } from '@browsery/http-parser';
5
6
  import { IncomingHttpHeaders } from 'http';
6
7
  import { Duplex, Readable } from 'stream';
7
- import { HTTPParserJS } from '@browsery/http-parser';
8
8
  import type { NodeIncomingMessage } from '../interfaces/node-incoming-message.interface';
9
9
  export declare const CRLF: Buffer;
10
10
  export declare const kHeaders: unique symbol;
@@ -1,8 +1,8 @@
1
1
  /// <reference types="node" />
2
2
  /// <reference types="node" />
3
3
  /// <reference types="node" />
4
- import { EventEmitter } from 'events';
5
4
  import nodeStream from 'node:stream';
5
+ import { EventEmitter } from 'events';
6
6
  import type { HttpIncoming } from '../interfaces/http-incoming.interface.js';
7
7
  /**
8
8
  *
package/types/index.d.ts CHANGED
@@ -6,20 +6,20 @@ 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 './platform-adapter.js';
10
- export * from './type-guards.js';
11
9
  export * from './helpers/logger.js';
12
10
  export * from './helpers/service-base.js';
13
11
  export * from './http/express-adapter.js';
14
12
  export * from './http/http-adapter.js';
15
13
  export * from './http/http-context.js';
14
+ export * from './http/impl/multipart-reader.js';
16
15
  export * from './http/interfaces/http-incoming.interface.js';
17
16
  export * from './http/interfaces/http-outgoing.interface.js';
18
17
  export * from './http/interfaces/node-incoming-message.interface.js';
19
18
  export * from './http/interfaces/node-outgoing-message.interface.js';
20
- export * from './http/impl/multipart-reader.js';
21
19
  export * from './http/utils/wrap-exception.js';
22
20
  export * from './interfaces/logger.interface.js';
21
+ export * from './platform-adapter.js';
22
+ export * from './type-guards.js';
23
23
  export declare namespace classes {
24
24
  export import HttpIncomingHost = HttpIncomingHost_.HttpIncomingHost;
25
25
  export import HttpOutgoingHost = HttpOutgoingHost_.HttpOutgoingHost;