@opra/core 1.0.0-alpha.8 → 1.0.0-beta.1

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 (60) hide show
  1. package/cjs/augmentation/18n.augmentation.js +13 -13
  2. package/cjs/constants.js +1 -2
  3. package/cjs/execution-context.js +1 -0
  4. package/cjs/http/express-adapter.js +22 -21
  5. package/cjs/http/http-adapter.js +2 -5
  6. package/cjs/http/http-context.js +16 -31
  7. package/cjs/http/{impl/http-handler.js → http-handler.js} +194 -169
  8. package/cjs/http/impl/http-outgoing.host.js +2 -2
  9. package/cjs/http/impl/multipart-reader.js +141 -44
  10. package/cjs/http/utils/body-reader.js +0 -1
  11. package/cjs/http/utils/common.js +4 -4
  12. package/cjs/http/utils/concat-readable.js +1 -2
  13. package/cjs/http/utils/convert-to-headers.js +2 -3
  14. package/cjs/http/utils/convert-to-raw-headers.js +1 -2
  15. package/cjs/http/utils/match-known-fields.js +2 -2
  16. package/cjs/http/utils/wrap-exception.js +1 -2
  17. package/cjs/index.js +1 -1
  18. package/cjs/platform-adapter.js +0 -3
  19. package/cjs/type-guards.js +4 -5
  20. package/esm/augmentation/18n.augmentation.js +2 -2
  21. package/esm/constants.js +0 -1
  22. package/esm/execution-context.js +1 -0
  23. package/esm/http/express-adapter.js +22 -21
  24. package/esm/http/http-adapter.js +2 -5
  25. package/esm/http/http-context.js +17 -32
  26. package/esm/http/{impl/http-handler.js → http-handler.js} +195 -170
  27. package/esm/http/impl/http-outgoing.host.js +1 -1
  28. package/esm/http/impl/multipart-reader.js +142 -45
  29. package/esm/http/utils/body-reader.js +0 -1
  30. package/esm/index.js +1 -1
  31. package/esm/package.json +3 -0
  32. package/esm/platform-adapter.js +0 -3
  33. package/package.json +35 -63
  34. package/types/augmentation/http-controller.augmentation.d.ts +1 -2
  35. package/types/constants.d.ts +0 -1
  36. package/types/execution-context.d.ts +2 -1
  37. package/types/helpers/service-base.d.ts +1 -1
  38. package/types/http/express-adapter.d.ts +1 -1
  39. package/types/http/http-adapter.d.ts +35 -8
  40. package/types/http/http-context.d.ts +4 -4
  41. package/types/http/{impl/http-handler.d.ts → http-handler.d.ts} +11 -9
  42. package/types/http/impl/http-incoming.host.d.ts +1 -2
  43. package/types/http/impl/http-outgoing.host.d.ts +1 -1
  44. package/types/http/impl/multipart-reader.d.ts +38 -20
  45. package/types/http/impl/node-incoming-message.host.d.ts +3 -7
  46. package/types/http/impl/node-outgoing-message.host.d.ts +5 -8
  47. package/types/http/interfaces/http-incoming.interface.d.ts +2 -3
  48. package/types/http/interfaces/http-outgoing.interface.d.ts +2 -2
  49. package/types/http/interfaces/node-incoming-message.interface.d.ts +0 -2
  50. package/types/http/interfaces/node-outgoing-message.interface.d.ts +1 -3
  51. package/types/http/utils/body-reader.d.ts +1 -4
  52. package/types/http/utils/concat-readable.d.ts +0 -1
  53. package/types/http/utils/convert-to-raw-headers.d.ts +1 -2
  54. package/types/index.d.cts +28 -0
  55. package/types/index.d.ts +1 -1
  56. package/types/platform-adapter.d.ts +0 -4
  57. package/cjs/helpers/logger.js +0 -35
  58. package/esm/helpers/logger.js +0 -31
  59. package/i18n/i18n/en/error.json +0 -21
  60. package/types/helpers/logger.d.ts +0 -14
@@ -1,12 +1,13 @@
1
1
  import * as process from 'node:process';
2
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';
3
+ import { BadRequestError, HttpHeaderCodes, HttpStatusCode, InternalServerError, isBlob, isReadableStream, IssueSeverity, MethodNotAllowedError, MimeTypes, OperationResult, OpraException, OpraSchema, safeJsonStringify, } from '@opra/common';
4
4
  import { parse as parseContentType } from 'content-type';
5
5
  import { splitString } from 'fast-tokenizer';
6
+ import { md5 } from 'super-fast-md5';
6
7
  import { asMutable } from 'ts-gems';
7
8
  import { toArray, ValidationError, vg } from 'valgen';
8
- import { kAssetCache } from '../../constants.js';
9
- import { wrapException } from '../utils/wrap-exception.js';
9
+ import { kAssetCache } from '../constants.js';
10
+ import { wrapException } from './utils/wrap-exception.js';
10
11
  /**
11
12
  * @class HttpHandler
12
13
  */
@@ -38,7 +39,7 @@ export class HttpHandler {
38
39
  throw e;
39
40
  if (e instanceof ValidationError) {
40
41
  throw new BadRequestError({
41
- message: translate('error:RESPONSE_VALIDATION,', 'Response validation failed'),
42
+ message: 'Response validation failed',
42
43
  code: 'RESPONSE_VALIDATION',
43
44
  details: e.issues,
44
45
  }, e);
@@ -48,11 +49,14 @@ export class HttpHandler {
48
49
  await this.adapter.emitAsync('request', context);
49
50
  // Call interceptors than execute request
50
51
  if (this.adapter.interceptors) {
52
+ const interceptors = this.adapter.interceptors;
51
53
  let i = 0;
52
54
  const next = async () => {
53
- const interceptor = this.adapter.interceptors[i++];
54
- if (interceptor)
55
+ const interceptor = interceptors[i++];
56
+ if (typeof interceptor === 'function')
55
57
  await interceptor(context, next);
58
+ else if (typeof interceptor?.intercept === 'function')
59
+ await interceptor.intercept(context, next);
56
60
  await this._executeRequest(context);
57
61
  };
58
62
  await next();
@@ -64,19 +68,17 @@ export class HttpHandler {
64
68
  let e = error;
65
69
  if (e instanceof ValidationError) {
66
70
  e = new InternalServerError({
67
- message: translate('error:RESPONSE_VALIDATION,', 'Response validation failed'),
71
+ message: 'Response validation failed',
68
72
  code: 'RESPONSE_VALIDATION',
69
73
  details: e.issues,
70
74
  }, e);
71
75
  }
72
76
  else
73
77
  e = wrapException(e);
74
- response.status(e.statusCode || e.status || HttpStatusCode.INTERNAL_SERVER_ERROR);
75
- response.contentType(MimeTypes.opra_response_json);
76
- await this._sendResponse(context, new OperationResult({ errors: [e.toJSON()] })).finally(() => {
77
- if (!response.finished)
78
- response.end();
79
- });
78
+ if (this.onError)
79
+ await this.onError(context, error);
80
+ context.errors.push(e);
81
+ await this.sendResponse(context);
80
82
  }
81
83
  finally {
82
84
  await context.emitAsync('finish');
@@ -89,7 +91,7 @@ export class HttpHandler {
89
91
  async parseRequest(context) {
90
92
  await this._parseParameters(context);
91
93
  await this._parseContentType(context);
92
- if (context.operation.requestBody?.immediateFetch)
94
+ if (context.operation?.requestBody?.immediateFetch)
93
95
  await context.getBody();
94
96
  /** Set default status code as the first status code between 200 and 299 */
95
97
  if (context.operation) {
@@ -109,6 +111,8 @@ export class HttpHandler {
109
111
  */
110
112
  async _parseParameters(context) {
111
113
  const { operation, request } = context;
114
+ if (!operation)
115
+ return;
112
116
  let key = '';
113
117
  try {
114
118
  const onFail = (issue) => {
@@ -210,6 +214,7 @@ export class HttpHandler {
210
214
  }
211
215
  }
212
216
  for (const prm of paramsLeft) {
217
+ key = String(prm.name);
213
218
  // Throw error for required parameters
214
219
  if (prm.required) {
215
220
  const decode = getDecoder(prm);
@@ -235,6 +240,8 @@ export class HttpHandler {
235
240
  */
236
241
  async _parseContentType(context) {
237
242
  const { request, operation } = context;
243
+ if (!operation)
244
+ return;
238
245
  if (operation.requestBody?.content.length) {
239
246
  let mediaType;
240
247
  let contentType = request.header('content-type');
@@ -261,7 +268,7 @@ export class HttpHandler {
261
268
  const responseValue = await context.operationHandler.call(context.controllerInstance, context);
262
269
  const { response } = context;
263
270
  if (!response.writableEnded) {
264
- await this._sendResponse(context, responseValue).finally(() => {
271
+ await this.sendResponse(context, responseValue).finally(() => {
265
272
  if (!response.writableEnded)
266
273
  response.end();
267
274
  });
@@ -273,94 +280,182 @@ export class HttpHandler {
273
280
  * @param responseValue
274
281
  * @protected
275
282
  */
276
- async _sendResponse(context, responseValue) {
283
+ async sendResponse(context, responseValue) {
284
+ if (context.errors.length)
285
+ return this._sendErrorResponse(context);
277
286
  const { response } = context;
278
287
  const { document } = this.adapter;
279
- const responseArgs = this._determineResponseArgs(context, responseValue);
280
- const { operationResponse, statusCode } = responseArgs;
281
- let { contentType, body } = responseArgs;
282
- const operationResultType = document.node.getDataType(OperationResult);
283
- let operationResultEncoder = this[kAssetCache].get(operationResultType, 'encode');
284
- if (!operationResultEncoder) {
285
- operationResultEncoder = operationResultType.generateCodec('encode', { ignoreWriteonlyFields: true });
286
- this[kAssetCache].set(operationResultType, 'encode', operationResultEncoder);
287
- }
288
- /** Validate response */
289
- if (operationResponse?.type) {
290
- if (!(body == null && statusCode === HttpStatusCode.NO_CONTENT)) {
291
- /** Generate encoder */
292
- let encode = this[kAssetCache].get(operationResponse, 'encode');
293
- if (!encode) {
294
- encode = operationResponse.type.generateCodec('encode', {
295
- partial: operationResponse.partial,
296
- projection: '*',
297
- ignoreWriteonlyFields: true,
298
- });
299
- if (operationResponse) {
300
- if (operationResponse.isArray)
301
- encode = vg.isArray(encode);
302
- this[kAssetCache].set(operationResponse, 'encode', encode);
288
+ try {
289
+ const responseArgs = this._determineResponseArgs(context, responseValue);
290
+ const { operationResponse, statusCode } = responseArgs;
291
+ let { contentType, body } = responseArgs;
292
+ const operationResultType = document.node.getDataType(OperationResult);
293
+ let operationResultEncoder = this[kAssetCache].get(operationResultType, 'encode');
294
+ if (!operationResultEncoder) {
295
+ operationResultEncoder = operationResultType.generateCodec('encode', {
296
+ ignoreWriteonlyFields: true,
297
+ ignoreHiddenFields: true,
298
+ });
299
+ this[kAssetCache].set(operationResultType, 'encode', operationResultEncoder);
300
+ }
301
+ /** Validate response */
302
+ if (operationResponse?.type) {
303
+ if (!(body == null && statusCode === HttpStatusCode.NO_CONTENT)) {
304
+ /** Generate encoder */
305
+ const projection = responseArgs.projection || '*';
306
+ const assetKey = md5(String(projection));
307
+ let encode = this[kAssetCache].get(operationResponse, 'encode:' + assetKey);
308
+ if (!encode) {
309
+ encode = operationResponse.type.generateCodec('encode', {
310
+ partial: operationResponse.partial,
311
+ projection,
312
+ ignoreWriteonlyFields: true,
313
+ ignoreHiddenFields: true,
314
+ onFail: issue => `Response body validation failed: ` + issue.message,
315
+ });
316
+ if (operationResponse) {
317
+ if (operationResponse.isArray)
318
+ encode = vg.isArray(encode);
319
+ this[kAssetCache].set(operationResponse, 'encode:' + assetKey, encode);
320
+ }
321
+ }
322
+ /** Encode body */
323
+ if (operationResponse.type.extendsFrom(operationResultType)) {
324
+ if (body instanceof OperationResult)
325
+ body = encode(body);
326
+ else {
327
+ body.payload = encode(body.payload);
328
+ body = operationResultEncoder(body);
329
+ }
303
330
  }
304
- }
305
- /** Encode body */
306
- if (operationResponse.type.extendsFrom(operationResultType)) {
307
- if (body instanceof OperationResult)
308
- body = encode(body);
309
331
  else {
310
- body.payload = encode(body.payload);
311
- body = operationResultEncoder(body);
332
+ if (body instanceof OperationResult &&
333
+ contentType &&
334
+ typeIs.is(contentType, [MimeTypes.opra_response_json])) {
335
+ body.payload = encode(body.payload);
336
+ body = operationResultEncoder(body);
337
+ }
338
+ else {
339
+ body = encode(body);
340
+ }
312
341
  }
313
- }
314
- else {
315
342
  if (body instanceof OperationResult &&
316
- contentType &&
317
- typeIs.is(contentType, [MimeTypes.opra_response_json])) {
318
- body.payload = encode(body.payload);
319
- body = operationResultEncoder(body);
343
+ operationResponse.type &&
344
+ operationResponse.type !== document.node.getDataType(OperationResult)) {
345
+ body.type = operationResponse.type.name ? operationResponse.type.name : '#embedded';
320
346
  }
321
- else
322
- body = encode(body);
323
- }
324
- if (body instanceof OperationResult && operationResponse.type) {
325
- body.type = operationResponse.type.name ? operationResponse.type.name : '#embedded';
326
347
  }
327
348
  }
328
- }
329
- else if (body != null) {
330
- if (body instanceof OperationResult) {
331
- body = operationResultEncoder(body);
332
- contentType = MimeTypes.opra_response_json;
333
- }
334
- else if (Buffer.isBuffer(body))
335
- contentType = MimeTypes.binary;
336
- else if (typeof body === 'object') {
337
- contentType = contentType || MimeTypes.json;
338
- if (typeof body.toJSON === 'function')
339
- body = body.toJSON();
349
+ else if (body != null) {
350
+ if (body instanceof OperationResult) {
351
+ body = operationResultEncoder(body);
352
+ contentType = MimeTypes.opra_response_json;
353
+ }
354
+ else if (Buffer.isBuffer(body))
355
+ contentType = MimeTypes.binary;
356
+ else if (typeof body === 'object') {
357
+ contentType = contentType || MimeTypes.json;
358
+ if (typeof body.toJSON === 'function')
359
+ body = body.toJSON();
360
+ }
361
+ else {
362
+ contentType = contentType || MimeTypes.text;
363
+ body = String(body);
364
+ }
340
365
  }
341
- else {
342
- contentType = contentType || MimeTypes.text;
343
- body = String(body);
366
+ /** Set content-type header value if not set */
367
+ if (contentType && contentType !== responseArgs.contentType)
368
+ response.setHeader('content-type', contentType);
369
+ response.status(statusCode);
370
+ if (body == null) {
371
+ response.end();
372
+ return;
344
373
  }
374
+ let x;
375
+ if (Buffer.isBuffer(body) || isReadableStream(body))
376
+ x = body;
377
+ else if (isBlob(body))
378
+ x = body.stream();
379
+ else if (typeof body === 'object')
380
+ x = JSON.stringify(body);
381
+ else
382
+ x = String(body);
383
+ response.end(x);
384
+ }
385
+ catch (error) {
386
+ context.errors.push(error);
387
+ return this._sendErrorResponse(context);
345
388
  }
346
- /** Set content-type header value if not set */
347
- if (contentType && contentType !== responseArgs.contentType)
348
- response.setHeader('content-type', contentType);
349
- response.status(statusCode);
350
- if (body == null) {
389
+ }
390
+ async _sendErrorResponse(context) {
391
+ context.errors = this._wrapExceptions(context.errors);
392
+ try {
393
+ await this.adapter.emitAsync('error', context);
394
+ context.errors = this._wrapExceptions(context.errors);
395
+ }
396
+ catch (e) {
397
+ context.errors = this._wrapExceptions([e, ...context.errors]);
398
+ }
399
+ const { response, errors } = context;
400
+ if (response.headersSent) {
351
401
  response.end();
352
402
  return;
353
403
  }
354
- let x;
355
- if (Buffer.isBuffer(body) || isReadableStream(body))
356
- x = body;
357
- else if (isBlob(body))
358
- x = body.stream();
359
- else if (typeof body === 'object')
360
- x = JSON.stringify(body);
361
- else
362
- x = String(body);
363
- response.end(x);
404
+ let status = response.statusCode || 0;
405
+ if (!status || status < Number(HttpStatusCode.BAD_REQUEST)) {
406
+ status = errors[0].status;
407
+ if (status < Number(HttpStatusCode.BAD_REQUEST))
408
+ status = HttpStatusCode.INTERNAL_SERVER_ERROR;
409
+ }
410
+ response.statusCode = status;
411
+ const { document } = this.adapter;
412
+ const dt = document.node.getComplexType('OperationResult');
413
+ let encode = this[kAssetCache].get(dt, 'encode');
414
+ if (!encode) {
415
+ encode = dt.generateCodec('encode', { ignoreWriteonlyFields: true });
416
+ this[kAssetCache].set(dt, 'encode', encode);
417
+ }
418
+ // const { i18n } = this.adapter;
419
+ const bodyObject = new OperationResult({
420
+ errors: errors.map(x => {
421
+ const o = x.toJSON();
422
+ if (!(process.env.NODE_ENV === 'dev' || process.env.NODE_ENV === 'development'))
423
+ delete o.stack;
424
+ return o; // i18n.deep(o);
425
+ }),
426
+ });
427
+ const body = encode(bodyObject);
428
+ response.setHeader(HttpHeaderCodes.Content_Type, MimeTypes.opra_response_json + '; charset=utf-8');
429
+ response.setHeader(HttpHeaderCodes.Cache_Control, 'no-cache');
430
+ response.setHeader(HttpHeaderCodes.Pragma, 'no-cache');
431
+ response.setHeader(HttpHeaderCodes.Expires, '-1');
432
+ response.setHeader(HttpHeaderCodes.X_Opra_Version, OpraSchema.SpecVersion);
433
+ response.send(safeJsonStringify(body));
434
+ response.end();
435
+ }
436
+ async sendDocumentSchema(context) {
437
+ const { request, response } = context;
438
+ const { document } = this.adapter;
439
+ response.setHeader('content-type', MimeTypes.json);
440
+ const url = new URL(request.originalUrl || request.url || '/', 'http://tempuri.org');
441
+ const { searchParams } = url;
442
+ const documentId = searchParams.get('id');
443
+ const doc = documentId ? document.findDocument(documentId) : document;
444
+ if (!doc) {
445
+ context.errors.push(new BadRequestError({
446
+ message: `Document with given id [${documentId}] does not exists`,
447
+ }));
448
+ return this.sendResponse(context);
449
+ }
450
+ /** Check if response cache exists */
451
+ let responseBody = this[kAssetCache].get(doc, `$schema`);
452
+ /** Create response if response cache does not exists */
453
+ if (!responseBody) {
454
+ const schema = doc.export();
455
+ responseBody = JSON.stringify(schema);
456
+ this[kAssetCache].set(doc, `$schema`, responseBody);
457
+ }
458
+ response.end(responseBody);
364
459
  }
365
460
  /**
366
461
  *
@@ -387,7 +482,7 @@ export class HttpHandler {
387
482
  let responseArgs = this[kAssetCache].get(response, cacheKey);
388
483
  if (!responseArgs) {
389
484
  responseArgs = { statusCode, contentType };
390
- if (operation.responses.length) {
485
+ if (operation?.responses.length) {
391
486
  /** Filter available HttpOperationResponse instances according to status code. */
392
487
  const filteredResponses = operation.responses.filter(r => r.statusCode.find(sc => sc.start <= statusCode && sc.end >= statusCode));
393
488
  /** Throw InternalServerError if controller returns non-configured status code */
@@ -419,6 +514,8 @@ export class HttpHandler {
419
514
  : operationResponse.contentType);
420
515
  if (typeof ct === 'string')
421
516
  responseArgs.contentType = contentType = ct;
517
+ else if (operationResponse.type)
518
+ responseArgs.contentType = MimeTypes.opra_response_json;
422
519
  }
423
520
  }
424
521
  }
@@ -430,6 +527,10 @@ export class HttpHandler {
430
527
  }
431
528
  if (!hasBody)
432
529
  delete responseArgs.contentType;
530
+ if (operation?.composition?.startsWith('Entity.')) {
531
+ if (context.queryParams.projection)
532
+ responseArgs.projection = context.queryParams.projection;
533
+ }
433
534
  this[kAssetCache].set(response, cacheKey, { ...responseArgs });
434
535
  }
435
536
  /** Fix response value according to composition */
@@ -487,56 +588,10 @@ export class HttpHandler {
487
588
  responseArgs.body = body;
488
589
  return responseArgs;
489
590
  }
490
- async sendDocumentSchema(context) {
491
- const { request, response } = context;
492
- const { document } = this.adapter;
493
- response.setHeader('content-type', MimeTypes.json);
494
- const url = new URL(request.originalUrl || request.url || '/', 'http://tempuri.org');
495
- const { searchParams } = url;
496
- const documentId = searchParams.get('id');
497
- const doc = documentId ? document.findDocument(documentId) : document;
498
- if (!doc) {
499
- return this.sendErrorResponse(response, [
500
- new BadRequestError({
501
- message: `Document with given id [${documentId}] does not exists`,
502
- }),
503
- ]);
504
- }
505
- /** Check if response cache exists */
506
- let responseBody = this[kAssetCache].get(doc, `$schema`);
507
- /** Create response if response cache does not exists */
508
- if (!responseBody) {
509
- const schema = doc.export();
510
- responseBody = JSON.stringify(schema);
511
- this[kAssetCache].set(doc, `$schema`, responseBody);
512
- }
513
- response.end(responseBody);
514
- }
515
- async sendErrorResponse(response, errors) {
516
- if (response.headersSent) {
517
- response.end();
518
- return;
519
- }
520
- if (!errors.length)
521
- errors.push(wrapException({ status: response.statusCode || 500 }));
522
- const { logger } = this.adapter;
523
- errors.forEach(x => {
524
- if (x instanceof OpraException) {
525
- switch (x.severity) {
526
- case 'fatal':
527
- logger.fatal(x);
528
- break;
529
- case 'warning':
530
- logger.warn(x);
531
- break;
532
- default:
533
- logger.error(x);
534
- }
535
- }
536
- else
537
- logger.fatal(x);
538
- });
539
- const wrappedErrors = errors.map(wrapException);
591
+ _wrapExceptions(exceptions) {
592
+ const wrappedErrors = exceptions.map(wrapException);
593
+ if (!wrappedErrors.length)
594
+ wrappedErrors.push(new InternalServerError());
540
595
  // Sort errors from fatal to info
541
596
  wrappedErrors.sort((a, b) => {
542
597
  const i = IssueSeverity.Keys.indexOf(a.severity) - IssueSeverity.Keys.indexOf(b.severity);
@@ -544,36 +599,6 @@ export class HttpHandler {
544
599
  return b.status - a.status;
545
600
  return i;
546
601
  });
547
- let status = response.statusCode || 0;
548
- if (!status || status < Number(HttpStatusCode.BAD_REQUEST)) {
549
- status = wrappedErrors[0].status;
550
- if (status < Number(HttpStatusCode.BAD_REQUEST))
551
- status = HttpStatusCode.INTERNAL_SERVER_ERROR;
552
- }
553
- response.statusCode = status;
554
- const { document } = this.adapter;
555
- const dt = document.node.getComplexType('OperationResult');
556
- let encode = this[kAssetCache].get(dt, 'encode');
557
- if (!encode) {
558
- encode = dt.generateCodec('encode', { ignoreWriteonlyFields: true });
559
- this[kAssetCache].set(dt, 'encode', encode);
560
- }
561
- const { i18n } = this.adapter;
562
- const bodyObject = new OperationResult({
563
- errors: wrappedErrors.map(x => {
564
- const o = x.toJSON();
565
- if (!(process.env.NODE_ENV === 'dev' || process.env.NODE_ENV === 'development'))
566
- delete o.stack;
567
- return i18n.deep(o);
568
- }),
569
- });
570
- const body = encode(bodyObject);
571
- response.setHeader(HttpHeaderCodes.Content_Type, MimeTypes.opra_response_json + '; charset=utf-8');
572
- response.setHeader(HttpHeaderCodes.Cache_Control, 'no-cache');
573
- response.setHeader(HttpHeaderCodes.Pragma, 'no-cache');
574
- response.setHeader(HttpHeaderCodes.Expires, '-1');
575
- response.setHeader(HttpHeaderCodes.X_Opra_Version, OpraSchema.SpecVersion);
576
- response.send(JSON.stringify(body));
577
- response.end();
602
+ return wrappedErrors;
578
603
  }
579
604
  }
@@ -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 path from 'node:path';
5
6
  import { HttpStatusCode } from '@opra/common';
6
7
  import contentDisposition from 'content-disposition';
7
8
  import contentType from 'content-type';
@@ -9,7 +10,6 @@ import cookie from 'cookie';
9
10
  import cookieSignature from 'cookie-signature';
10
11
  import encodeUrl from 'encodeurl';
11
12
  import mime from 'mime-types';
12
- import path from 'path';
13
13
  import { toString } from 'putil-varhelpers';
14
14
  import vary from 'vary';
15
15
  const charsetRegExp = /;\s*charset\s*=/;