@opra/http 1.26.2 → 1.26.4

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/README.md CHANGED
@@ -1,3 +1,26 @@
1
- # @opra/core
1
+ # @opra/http
2
2
 
3
- OPRA schema package.
3
+ [![NPM Version][npm-image]][npm-url]
4
+ [![NPM Downloads][downloads-image]][downloads-url]
5
+ [![CI Tests][ci-test-image]][ci-test-url]
6
+ [![Test Coverage][coveralls-image]][coveralls-url]
7
+
8
+
9
+ ## Support
10
+ You can report bugs and discuss features on the [GitHub issues](https://github.com/panates/opra/issues) page.
11
+
12
+ ## Node Compatibility
13
+ - node >= 20.x
14
+
15
+
16
+ ## License
17
+ Available under [MIT](LICENSE) license.
18
+
19
+ [npm-image]: https://img.shields.io/npm/v/@opra/http
20
+ [npm-url]: https://npmjs.org/package/@opra/http
21
+ [downloads-image]: https://img.shields.io/npm/dm/@opra/http.svg
22
+ [downloads-url]: https://npmjs.org/package/@opra/http
23
+ [ci-test-image]: https://github.com/panates/opra/actions/workflows/test.yml/badge.svg
24
+ [ci-test-url]: https://github.com/panates/opra/actions/workflows/test.yml
25
+ [coveralls-image]: https://coveralls.io/repos/github/panates/opra/badge.svg?branch=main
26
+ [coveralls-url]: https://coveralls.io/github/panates/opra?branch=main
@@ -1,12 +1,27 @@
1
1
  import { ApiDocument, HttpController } from '@opra/common';
2
2
  import { type Application } from 'express';
3
3
  import { HttpAdapter } from './http-adapter.js';
4
+ /**
5
+ * ExpressAdapter is a platform adapter for the Express.js framework.
6
+ * It integrates Opra with Express applications and routers.
7
+ */
4
8
  export declare class ExpressAdapter extends HttpAdapter {
5
9
  readonly app: Application;
6
10
  protected _controllerInstances: Map<HttpController, any>;
7
11
  constructor(app: Application, document: ApiDocument, options?: HttpAdapter.Options);
8
12
  get platform(): string;
13
+ /**
14
+ * Closes the adapter and performs cleanup.
15
+ *
16
+ * @returns A promise that resolves when the adapter is closed.
17
+ */
9
18
  close(): Promise<void>;
19
+ /**
20
+ * Retrieves a controller instance by its path.
21
+ *
22
+ * @param controllerPath - The path of the controller.
23
+ * @returns The controller instance, or undefined if not found.
24
+ */
10
25
  getControllerInstance<T>(controllerPath: string): T | undefined;
11
26
  protected _initRouter(): void;
12
27
  protected _createControllers(controller: HttpController): void;
@@ -5,6 +5,10 @@ import { HttpAdapter } from './http-adapter.js';
5
5
  import { HttpContext } from './http-context.js';
6
6
  import { HttpIncoming } from './interfaces/http-incoming.interface.js';
7
7
  import { HttpOutgoing } from './interfaces/http-outgoing.interface.js';
8
+ /**
9
+ * ExpressAdapter is a platform adapter for the Express.js framework.
10
+ * It integrates Opra with Express applications and routers.
11
+ */
8
12
  export class ExpressAdapter extends HttpAdapter {
9
13
  app;
10
14
  _controllerInstances = new Map();
@@ -21,6 +25,11 @@ export class ExpressAdapter extends HttpAdapter {
21
25
  get platform() {
22
26
  return 'express';
23
27
  }
28
+ /**
29
+ * Closes the adapter and performs cleanup.
30
+ *
31
+ * @returns A promise that resolves when the adapter is closed.
32
+ */
24
33
  async close() {
25
34
  const processInstance = async (controller) => {
26
35
  if (controller.controllers.size) {
@@ -35,6 +44,12 @@ export class ExpressAdapter extends HttpAdapter {
35
44
  await processInstance(c);
36
45
  this._controllerInstances.clear();
37
46
  }
47
+ /**
48
+ * Retrieves a controller instance by its path.
49
+ *
50
+ * @param controllerPath - The path of the controller.
51
+ * @returns The controller instance, or undefined if not found.
52
+ */
38
53
  getControllerInstance(controllerPath) {
39
54
  const controller = this.api.findController(controllerPath);
40
55
  return controller && this._controllerInstances.get(controller);
@@ -58,13 +73,13 @@ export class ExpressAdapter extends HttpAdapter {
58
73
  await this.emitAsync('createContext', ctx);
59
74
  return ctx;
60
75
  };
61
- /** Add an endpoint that returns document schema */
76
+ /* Add an endpoint that returns document schema */
62
77
  router.get('/\\$schema', (_req, _res, next) => {
63
78
  createContext(_req, _res)
64
79
  .then(ctx => this.handler.sendDocumentSchema(ctx).catch(next))
65
80
  .catch(next);
66
81
  });
67
- /** Add operation endpoints */
82
+ /* Add operation endpoints */
68
83
  if (this.api.controllers.size) {
69
84
  const processResource = (controller, currentPath) => {
70
85
  currentPath = nodePath.posix.join(currentPath, controller.path);
@@ -74,7 +89,7 @@ export class ExpressAdapter extends HttpAdapter {
74
89
  const operationHandler = controllerInstance[operation.name];
75
90
  if (!operationHandler)
76
91
  continue;
77
- /** Define router callback */
92
+ /* Define router callback */
78
93
  router[operation.method.toLowerCase()](routePath, (_req, _res, _next) => {
79
94
  createContext(_req, _res, {
80
95
  controller,
@@ -98,7 +113,7 @@ export class ExpressAdapter extends HttpAdapter {
98
113
  for (const c of this.api.controllers.values())
99
114
  processResource(c, '/');
100
115
  }
101
- /** Add an endpoint that returns 404 error at last */
116
+ /* Add an endpoint that returns 404 error at last */
102
117
  router.use('/{*splat}', (_req, _res, next) => {
103
118
  createContext(_req, _res)
104
119
  .then(ctx => {
package/http-adapter.d.ts CHANGED
@@ -4,8 +4,10 @@ import { EventMap } from 'node-events-async';
4
4
  import { HttpContext } from './http-context.js';
5
5
  import { HttpHandler } from './http-handler.js';
6
6
  /**
7
+ * HttpAdapter is the base class for all HTTP platform adapters.
8
+ * It provides core functionality for handling HTTP requests and managing interceptors.
7
9
  *
8
- * @class HttpAdapter
10
+ * @abstract
9
11
  */
10
12
  export declare abstract class HttpAdapter<T extends HttpAdapter.Events = HttpAdapter.Events> extends PlatformAdapter<EventMap<T>> {
11
13
  readonly handler: HttpHandler;
@@ -19,11 +21,11 @@ export declare abstract class HttpAdapter<T extends HttpAdapter.Events = HttpAda
19
21
  export declare namespace HttpAdapter {
20
22
  type NextCallback = () => Promise<void>;
21
23
  /**
22
- * @type InterceptorFunction
24
+ * The interceptor function signature.
23
25
  */
24
26
  type InterceptorFunction = IHttpInterceptor['intercept'];
25
27
  /**
26
- * @interface IHttpInterceptor
28
+ * Interface for HTTP interceptors.
27
29
  */
28
30
  type IHttpInterceptor = {
29
31
  intercept(context: HttpContext, next: NextCallback): Promise<void>;
package/http-adapter.js CHANGED
@@ -1,8 +1,10 @@
1
1
  import { PlatformAdapter } from '@opra/core';
2
2
  import { HttpHandler } from './http-handler.js';
3
3
  /**
4
+ * HttpAdapter is the base class for all HTTP platform adapters.
5
+ * It provides core functionality for handling HTTP requests and managing interceptors.
4
6
  *
5
- * @class HttpAdapter
7
+ * @abstract
6
8
  */
7
9
  export class HttpAdapter extends PlatformAdapter {
8
10
  handler;
package/http-context.d.ts CHANGED
@@ -21,8 +21,26 @@ export declare class HttpContext extends ExecutionContext {
21
21
  readonly queryParams: Record<string, any>;
22
22
  errors: Error[];
23
23
  constructor(init: HttpContext.Initiator);
24
+ /**
25
+ * Checks if the request is a multipart request.
26
+ */
24
27
  get isMultipart(): boolean;
28
+ /**
29
+ * Retrieves the multipart reader for the current context.
30
+ *
31
+ * @returns A promise that resolves to the multipart reader.
32
+ * @throws {@link InternalServerError} If the request content is not multipart.
33
+ * @throws {@link NotAcceptableError} If the endpoint does not accept multipart requests.
34
+ */
25
35
  getMultipartReader(): Promise<MultipartReader>;
36
+ /**
37
+ * Retrieves and parses the request body.
38
+ *
39
+ * @param args - Optional arguments for body retrieval.
40
+ * @param args.toFile - Whether to save the body to a file.
41
+ * @returns A promise that resolves to the parsed body.
42
+ * @throws {@link BadRequestError} If the body cannot be parsed or validated.
43
+ */
26
44
  getBody<T>(args?: {
27
45
  toFile: boolean | string;
28
46
  }): Promise<T>;
package/http-context.js CHANGED
@@ -44,9 +44,19 @@ export class HttpContext extends ExecutionContext {
44
44
  this._multipartReader.purge().catch(() => undefined);
45
45
  });
46
46
  }
47
+ /**
48
+ * Checks if the request is a multipart request.
49
+ */
47
50
  get isMultipart() {
48
51
  return !!this.request.is('multipart');
49
52
  }
53
+ /**
54
+ * Retrieves the multipart reader for the current context.
55
+ *
56
+ * @returns A promise that resolves to the multipart reader.
57
+ * @throws {@link InternalServerError} If the request content is not multipart.
58
+ * @throws {@link NotAcceptableError} If the endpoint does not accept multipart requests.
59
+ */
50
60
  async getMultipartReader() {
51
61
  if (!this.isMultipart)
52
62
  throw new InternalServerError('Request content is not a multipart content');
@@ -72,6 +82,14 @@ export class HttpContext extends ExecutionContext {
72
82
  this._multipartReader = reader;
73
83
  return reader;
74
84
  }
85
+ /**
86
+ * Retrieves and parses the request body.
87
+ *
88
+ * @param args - Optional arguments for body retrieval.
89
+ * @param args.toFile - Whether to save the body to a file.
90
+ * @returns A promise that resolves to the parsed body.
91
+ * @throws {@link BadRequestError} If the body cannot be parsed or validated.
92
+ */
75
93
  async getBody(args) {
76
94
  if (this._body !== undefined)
77
95
  return this._body;
@@ -79,9 +97,9 @@ export class HttpContext extends ExecutionContext {
79
97
  const { request, __oprDef, mediaType } = this;
80
98
  if (this.isMultipart) {
81
99
  const reader = await this.getMultipartReader();
82
- /** Retrieve all fields */
100
+ /* Retrieve all fields */
83
101
  const parts = await reader.getAll();
84
- /** Filter fields according to configuration */
102
+ /* Filter fields according to configuration */
85
103
  this._body = [...parts];
86
104
  return this._body;
87
105
  }
package/http-handler.d.ts CHANGED
@@ -18,7 +18,8 @@ export declare namespace HttpHandler {
18
18
  }
19
19
  }
20
20
  /**
21
- * @class HttpHandler
21
+ * HttpHandler is responsible for processing incoming HTTP requests.
22
+ * It handles request parsing, interceptor execution, and response generation.
22
23
  */
23
24
  export declare class HttpHandler {
24
25
  readonly adapter: HttpAdapter;
@@ -26,25 +27,35 @@ export declare class HttpHandler {
26
27
  onError?: (context: HttpContext, error: OpraException) => void | Promise<void>;
27
28
  constructor(adapter: HttpAdapter);
28
29
  /**
29
- * Main http request handler
30
- * @param context
30
+ * Main HTTP request handler.
31
+ *
32
+ * @param context - The HTTP execution context.
33
+ * @returns A promise that resolves when the request is handled.
31
34
  * @protected
32
35
  */
33
36
  handleRequest(context: HttpContext): Promise<void>;
34
37
  /**
38
+ * Parses the HTTP request, including parameters and content type.
35
39
  *
36
- * @param context
40
+ * @param context - The HTTP execution context.
41
+ * @returns A promise that resolves when the request is parsed.
37
42
  */
38
43
  parseRequest(context: HttpContext): Promise<void>;
39
44
  /**
45
+ * Parses various HTTP parameters (cookies, headers, path, query).
40
46
  *
41
- * @param context
47
+ * @param context - The HTTP execution context.
48
+ * @returns A promise that resolves when parameters are parsed.
49
+ * @throws {@link BadRequestError} If parameter validation fails.
42
50
  * @protected
43
51
  */
44
52
  protected _parseParameters(context: HttpContext): Promise<void>;
45
53
  /**
54
+ * Parses and validates the request content type.
46
55
  *
47
- * @param context
56
+ * @param context - The HTTP execution context.
57
+ * @returns A promise that resolves when content type is parsed.
58
+ * @throws {@link BadRequestError} If the content type is invalid or missing.
48
59
  * @protected
49
60
  */
50
61
  protected _parseContentType(context: HttpContext): Promise<void>;
@@ -55,18 +66,28 @@ export declare class HttpHandler {
55
66
  */
56
67
  protected _executeRequest(context: HttpContext): Promise<any>;
57
68
  /**
69
+ * Sends an HTTP response back to the client.
58
70
  *
59
- * @param context
60
- * @param responseValue
61
- * @protected
71
+ * @param context - The HTTP execution context.
72
+ * @param responseValue - The value to be sent in the response body.
73
+ * @returns A promise that resolves when the response is sent.
62
74
  */
63
75
  sendResponse(context: HttpContext, responseValue?: any): Promise<void>;
64
76
  protected _sendErrorResponse(context: HttpContext): Promise<void>;
77
+ /**
78
+ * Sends the document schema as a JSON response.
79
+ *
80
+ * @param context - The HTTP execution context.
81
+ * @returns A promise that resolves when the schema is sent.
82
+ */
65
83
  sendDocumentSchema(context: HttpContext): Promise<void>;
66
84
  /**
85
+ * Determines the response arguments (status code, content type, etc.) for a given response value.
67
86
  *
68
- * @param context
69
- * @param body
87
+ * @param context - The HTTP execution context.
88
+ * @param body - The response body.
89
+ * @returns The determined response arguments.
90
+ * @throws {@link InternalServerError} If response configuration is missing or invalid.
70
91
  * @protected
71
92
  */
72
93
  protected _determineResponseArgs(context: HttpContext, body: any): HttpHandler.ResponseArgs;
package/http-handler.js CHANGED
@@ -9,7 +9,8 @@ import { asMutable } from 'ts-gems';
9
9
  import { toArray, ValidationError, vg, } from 'valgen';
10
10
  import { wrapException } from './utils/wrap-exception.js';
11
11
  /**
12
- * @class HttpHandler
12
+ * HttpHandler is responsible for processing incoming HTTP requests.
13
+ * It handles request parsing, interceptor execution, and response generation.
13
14
  */
14
15
  export class HttpHandler {
15
16
  adapter;
@@ -20,8 +21,10 @@ export class HttpHandler {
20
21
  this[kAssetCache] = adapter[kAssetCache];
21
22
  }
22
23
  /**
23
- * Main http request handler
24
- * @param context
24
+ * Main HTTP request handler.
25
+ *
26
+ * @param context - The HTTP execution context.
27
+ * @returns A promise that resolves when the request is handled.
25
28
  * @protected
26
29
  */
27
30
  async handleRequest(context) {
@@ -88,15 +91,17 @@ export class HttpHandler {
88
91
  }
89
92
  }
90
93
  /**
94
+ * Parses the HTTP request, including parameters and content type.
91
95
  *
92
- * @param context
96
+ * @param context - The HTTP execution context.
97
+ * @returns A promise that resolves when the request is parsed.
93
98
  */
94
99
  async parseRequest(context) {
95
100
  await this._parseParameters(context);
96
101
  await this._parseContentType(context);
97
102
  if (context.__oprDef?.requestBody?.immediateFetch)
98
103
  await context.getBody();
99
- /** Set default status code as the first status code between 200 and 299 */
104
+ /* Set default status code as the first status code between 200 and 299 */
100
105
  if (context.__oprDef) {
101
106
  for (const r of context.__oprDef.responses) {
102
107
  const st = r.statusCode.find(sc => sc.start <= 299 && sc.end >= 200);
@@ -108,8 +113,11 @@ export class HttpHandler {
108
113
  }
109
114
  }
110
115
  /**
116
+ * Parses various HTTP parameters (cookies, headers, path, query).
111
117
  *
112
- * @param context
118
+ * @param context - The HTTP execution context.
119
+ * @returns A promise that resolves when parameters are parsed.
120
+ * @throws {@link BadRequestError} If parameter validation fails.
113
121
  * @protected
114
122
  */
115
123
  async _parseParameters(context) {
@@ -122,7 +130,7 @@ export class HttpHandler {
122
130
  issue.location = key;
123
131
  return issue;
124
132
  };
125
- /** prepare decoders */
133
+ /* prepare decoders */
126
134
  const getDecoder = (prm) => {
127
135
  let decode = this[kAssetCache].get(prm, 'decode');
128
136
  if (!decode) {
@@ -138,7 +146,7 @@ export class HttpHandler {
138
146
  ...__oprDef.parameters,
139
147
  ...__oprDef.owner.parameters,
140
148
  ]);
141
- /** parse cookie parameters */
149
+ /* parse cookie parameters */
142
150
  if (request.cookies) {
143
151
  for (key of Object.keys(request.cookies)) {
144
152
  const oprPrm = __oprDef.findParameter(key, 'cookie');
@@ -161,7 +169,7 @@ export class HttpHandler {
161
169
  context.cookies[prmName] = v;
162
170
  }
163
171
  }
164
- /** parse headers */
172
+ /* parse headers */
165
173
  if (request.headers) {
166
174
  for (key of Object.keys(request.headers)) {
167
175
  const oprPrm = __oprDef.findParameter(key, 'header');
@@ -184,7 +192,7 @@ export class HttpHandler {
184
192
  context.headers[prmName] = v;
185
193
  }
186
194
  }
187
- /** parse path parameters */
195
+ /* parse path parameters */
188
196
  if (request.params) {
189
197
  for (key of Object.keys(request.params)) {
190
198
  const oprPrm = __oprDef.findParameter(key, 'path');
@@ -206,7 +214,7 @@ export class HttpHandler {
206
214
  context.pathParams[key] = v;
207
215
  }
208
216
  }
209
- /** parse query parameters */
217
+ /* parse query parameters */
210
218
  const url = new URL(request.originalUrl || request.url || '/', 'http://tempuri.org');
211
219
  const { searchParams } = url;
212
220
  for (key of searchParams.keys()) {
@@ -267,8 +275,11 @@ export class HttpHandler {
267
275
  }
268
276
  }
269
277
  /**
278
+ * Parses and validates the request content type.
270
279
  *
271
- * @param context
280
+ * @param context - The HTTP execution context.
281
+ * @returns A promise that resolves when content type is parsed.
282
+ * @throws {@link BadRequestError} If the content type is invalid or missing.
272
283
  * @protected
273
284
  */
274
285
  async _parseContentType(context) {
@@ -318,10 +329,11 @@ export class HttpHandler {
318
329
  }
319
330
  }
320
331
  /**
332
+ * Sends an HTTP response back to the client.
321
333
  *
322
- * @param context
323
- * @param responseValue
324
- * @protected
334
+ * @param context - The HTTP execution context.
335
+ * @param responseValue - The value to be sent in the response body.
336
+ * @returns A promise that resolves when the response is sent.
325
337
  */
326
338
  async sendResponse(context, responseValue) {
327
339
  if (context.errors.length)
@@ -341,11 +353,11 @@ export class HttpHandler {
341
353
  });
342
354
  this[kAssetCache].set(operationResultType, 'encode', operationResultEncoder);
343
355
  }
344
- /** Validate response */
356
+ /* Validate response */
345
357
  if (operationResponse?.type) {
346
358
  if (!(body == null &&
347
359
  statusCode === HttpStatusCode.NO_CONTENT)) {
348
- /** Generate encoder */
360
+ /* Generate encoder */
349
361
  const projection = responseArgs.projection || '*';
350
362
  const assetKey = md5(String(projection));
351
363
  let encode = this[kAssetCache].get(operationResponse, 'encode:' + assetKey);
@@ -363,7 +375,7 @@ export class HttpHandler {
363
375
  this[kAssetCache].set(operationResponse, 'encode:' + assetKey, encode);
364
376
  }
365
377
  }
366
- /** Encode body */
378
+ /* Encode body */
367
379
  if (operationResponse.type.extendsFrom(operationResultType)) {
368
380
  if (body instanceof OperationResult)
369
381
  body = encode(body);
@@ -410,7 +422,7 @@ export class HttpHandler {
410
422
  body = String(body);
411
423
  }
412
424
  }
413
- /** Set content-type header value if not set */
425
+ /* Set content-type header value if not set */
414
426
  if (contentType && contentType !== responseArgs.contentType)
415
427
  response.setHeader('content-type', contentType);
416
428
  response.status(statusCode);
@@ -497,6 +509,12 @@ export class HttpHandler {
497
509
  response.send(safeJsonStringify(body));
498
510
  response.end();
499
511
  }
512
+ /**
513
+ * Sends the document schema as a JSON response.
514
+ *
515
+ * @param context - The HTTP execution context.
516
+ * @returns A promise that resolves when the schema is sent.
517
+ */
500
518
  async sendDocumentSchema(context) {
501
519
  const { request, response } = context;
502
520
  const { document } = this.adapter;
@@ -511,9 +529,9 @@ export class HttpHandler {
511
529
  }));
512
530
  return this.sendResponse(context);
513
531
  }
514
- /** Check if response cache exists */
532
+ /* Check if response cache exists */
515
533
  let responseBody = this[kAssetCache].get(doc, `$schema`);
516
- /** Create response if response cache does not exists */
534
+ /* Create response if response cache does not exists */
517
535
  if (!responseBody) {
518
536
  const schema = doc.export({
519
537
  scope: this.adapter.scope,
@@ -524,9 +542,12 @@ export class HttpHandler {
524
542
  response.end(responseBody);
525
543
  }
526
544
  /**
545
+ * Determines the response arguments (status code, content type, etc.) for a given response value.
527
546
  *
528
- * @param context
529
- * @param body
547
+ * @param context - The HTTP execution context.
548
+ * @param body - The response body.
549
+ * @returns The determined response arguments.
550
+ * @throws {@link InternalServerError} If response configuration is missing or invalid.
530
551
  * @protected
531
552
  */
532
553
  _determineResponseArgs(context, body) {
@@ -535,12 +556,12 @@ export class HttpHandler {
535
556
  const statusCode = !hasBody && response.statusCode === HttpStatusCode.OK
536
557
  ? HttpStatusCode.NO_CONTENT
537
558
  : response.statusCode;
538
- /** Parse content-type header */
559
+ /* Parse content-type header */
539
560
  const parsedContentType = hasBody && response.hasHeader('content-type')
540
561
  ? parseContentType(response)
541
562
  : undefined;
542
563
  let contentType = parsedContentType?.type;
543
- /** Estimate content type if not defined */
564
+ /* Estimate content type if not defined */
544
565
  if (hasBody && !contentType) {
545
566
  if (body instanceof OperationResult)
546
567
  contentType = MimeTypes.opra_response_json;
@@ -553,21 +574,21 @@ export class HttpHandler {
553
574
  if (!responseArgs) {
554
575
  responseArgs = { statusCode, contentType };
555
576
  if (__oprDef?.responses.length) {
556
- /** Filter available HttpOperationResponse instances according to status code. */
577
+ /* Filter available HttpOperationResponse instances according to status code. */
557
578
  const filteredResponses = __oprDef.responses.filter(r => r.statusCode.find(sc => sc.start <= statusCode && sc.end >= statusCode));
558
- /** Throw InternalServerError if controller returns non-configured status code */
579
+ /* Throw InternalServerError if controller returns non-configured status code */
559
580
  if (!filteredResponses.length && statusCode < 400) {
560
581
  throw new InternalServerError(`No responses defined for status code ${statusCode} in operation "${__oprDef.name}"`);
561
582
  }
562
- /** We search for content-type in filtered HttpOperationResponse array */
583
+ /* We search for content-type in filtered HttpOperationResponse array */
563
584
  if (filteredResponses.length) {
564
- /** If no response returned, and content-type has not been set (No response wants to be returned by operation) */
585
+ /* If no response returned, and content-type has not been set (No response wants to be returned by operation) */
565
586
  if (!hasBody) {
566
- /** Find HttpOperationResponse with no content-type */
587
+ /* Find HttpOperationResponse with no content-type */
567
588
  operationResponse = filteredResponses.find(r => !r.contentType);
568
589
  }
569
590
  if (!operationResponse) {
570
- /** Find HttpOperationResponse according to content-type */
591
+ /* Find HttpOperationResponse according to content-type */
571
592
  if (contentType) {
572
593
  // Find HttpEndpointResponse instance according to content-type header
573
594
  operationResponse = filteredResponses.find(r => typeIs.is(contentType, toArray(r.contentType)));
@@ -576,7 +597,7 @@ export class HttpHandler {
576
597
  }
577
598
  }
578
599
  else {
579
- /** Select first HttpOperationResponse if content-type header has not been set */
600
+ /* Select first HttpOperationResponse if content-type header has not been set */
580
601
  operationResponse = filteredResponses[0];
581
602
  if (operationResponse.contentType) {
582
603
  const ct = typeIs.normalize(Array.isArray(operationResponse.contentType)
@@ -603,7 +624,7 @@ export class HttpHandler {
603
624
  }
604
625
  this[kAssetCache].set(response, cacheKey, { ...responseArgs });
605
626
  }
606
- /** Fix response value according to composition */
627
+ /* Fix response value according to composition */
607
628
  const composition = operationResponse?.owner.composition;
608
629
  if (composition && body != null) {
609
630
  switch (composition) {
@@ -1,3 +1,7 @@
1
+ /**
2
+ * LocalFile represents a file stored on the local file system.
3
+ * It provides utility methods for reading the file content and managing its lifecycle.
4
+ */
1
5
  export declare class LocalFile {
2
6
  private _autoDelete;
3
7
  readonly storedPath: string;
@@ -7,6 +11,11 @@ export declare class LocalFile {
7
11
  constructor(storedPath: string, options?: LocalFile.Options);
8
12
  text(): Promise<string>;
9
13
  buffer(): Promise<Buffer>;
14
+ /**
15
+ * Deletes the local file.
16
+ *
17
+ * @returns A promise that resolves when the file is deleted.
18
+ */
10
19
  delete(): Promise<void>;
11
20
  get size(): number;
12
21
  get autoDelete(): boolean;
@@ -6,6 +6,10 @@ import { uid } from 'uid';
6
6
  const registry = new FinalizationRegistry((storedPath) => {
7
7
  fs.unlink(storedPath, () => undefined);
8
8
  });
9
+ /**
10
+ * LocalFile represents a file stored on the local file system.
11
+ * It provides utility methods for reading the file content and managing its lifecycle.
12
+ */
9
13
  export class LocalFile {
10
14
  _autoDelete = false;
11
15
  storedPath;
@@ -25,6 +29,11 @@ export class LocalFile {
25
29
  async buffer() {
26
30
  return fsAsync.readFile(this.storedPath);
27
31
  }
32
+ /**
33
+ * Deletes the local file.
34
+ *
35
+ * @returns A promise that resolves when the file is deleted.
36
+ */
28
37
  async delete() {
29
38
  if (fs.existsSync(this.storedPath)) {
30
39
  try {
@@ -4,6 +4,10 @@ import { EventEmitter } from 'events';
4
4
  import type { StrictOmit } from 'ts-gems';
5
5
  import type { HttpContext } from '../http-context.js';
6
6
  import { LocalFile } from './local-file.js';
7
+ /**
8
+ * MultipartReader is responsible for parsing multipart/form-data requests.
9
+ * It uses busboy to stream incoming parts and can handle both fields and files.
10
+ */
7
11
  export declare class MultipartReader extends EventEmitter {
8
12
  protected context: HttpContext;
9
13
  protected mediaType?: HttpMediaType | undefined;
@@ -17,12 +21,28 @@ export declare class MultipartReader extends EventEmitter {
17
21
  scope?: string;
18
22
  constructor(context: HttpContext, options?: MultipartReader.Options, mediaType?: HttpMediaType | undefined);
19
23
  get items(): MultipartReader.Item[];
24
+ /**
25
+ * Retrieves the next item (field or file) from the multipart stream.
26
+ *
27
+ * @returns A promise that resolves to the next item, or undefined if no more items are available.
28
+ * @throws {@link BadRequestError} If a field is unknown or validation fails.
29
+ */
20
30
  getNext(): Promise<MultipartReader.Item | undefined>;
31
+ /**
32
+ * Retrieves all items from the multipart stream.
33
+ *
34
+ * @returns A promise that resolves to an array of all items.
35
+ */
21
36
  getAll(): Promise<MultipartReader.Item[]>;
22
37
  getAll_(): Promise<MultipartReader.Item[]>;
23
38
  cancel(): void;
24
39
  resume(): void;
25
40
  pause(): void;
41
+ /**
42
+ * Purges all temporary files created by the reader.
43
+ *
44
+ * @returns A promise that resolves when all files are purged.
45
+ */
26
46
  purge(): Promise<PromiseSettledResult<any>[]>;
27
47
  }
28
48
  export declare class MultipartFile extends LocalFile {
@@ -7,6 +7,10 @@ import { EventEmitter } from 'events';
7
7
  import fsPromise from 'fs/promises';
8
8
  import { isNotNullish } from 'valgen';
9
9
  import { LocalFile } from './local-file.js';
10
+ /**
11
+ * MultipartReader is responsible for parsing multipart/form-data requests.
12
+ * It uses busboy to stream incoming parts and can handle both fields and files.
13
+ */
10
14
  export class MultipartReader extends EventEmitter {
11
15
  context;
12
16
  mediaType;
@@ -70,6 +74,12 @@ export class MultipartReader extends EventEmitter {
70
74
  get items() {
71
75
  return this._items;
72
76
  }
77
+ /**
78
+ * Retrieves the next item (field or file) from the multipart stream.
79
+ *
80
+ * @returns A promise that resolves to the next item, or undefined if no more items are available.
81
+ * @throws {@link BadRequestError} If a field is unknown or validation fails.
82
+ */
73
83
  async getNext() {
74
84
  let item = this._stack.shift();
75
85
  if (!item && !this._finished) {
@@ -122,7 +132,7 @@ export class MultipartReader extends EventEmitter {
122
132
  }
123
133
  }
124
134
  }
125
- /** if all items received we check for required items */
135
+ /* if all items received we check for required items */
126
136
  if (this._finished &&
127
137
  this.mediaType &&
128
138
  this.mediaType.multipartFields?.length > 0) {
@@ -155,6 +165,11 @@ export class MultipartReader extends EventEmitter {
155
165
  }
156
166
  return item;
157
167
  }
168
+ /**
169
+ * Retrieves all items from the multipart stream.
170
+ *
171
+ * @returns A promise that resolves to an array of all items.
172
+ */
158
173
  async getAll() {
159
174
  const items = [...this._items];
160
175
  let item;
@@ -189,6 +204,11 @@ export class MultipartReader extends EventEmitter {
189
204
  pause() {
190
205
  this.context.request.pause();
191
206
  }
207
+ /**
208
+ * Purges all temporary files created by the reader.
209
+ *
210
+ * @returns A promise that resolves when all files are purged.
211
+ */
192
212
  async purge() {
193
213
  const promises = [];
194
214
  this._items.forEach(item => {
@@ -3,7 +3,8 @@ import { BodyReader } from '../utils/body-reader.js';
3
3
  import type { HttpOutgoing } from './http-outgoing.interface.js';
4
4
  import { NodeIncomingMessage } from './node-incoming-message.interface.js';
5
5
  /**
6
- * @interface HttpIncoming
6
+ * HttpIncoming represents an incoming HTTP request.
7
+ * It extends NodeIncomingMessage with additional functionality for handling HTTP requests.
7
8
  */
8
9
  export interface HttpIncoming extends NodeIncomingMessage {
9
10
  res: HttpOutgoing;
@@ -180,14 +181,22 @@ export interface HttpIncoming extends NodeIncomingMessage {
180
181
  */
181
182
  range(size: number, options?: RangeParserOptions): RangeParserRanges | RangeParserResult | undefined;
182
183
  /**
183
- * Receives the body
184
- * @param options
184
+ * Receives and parses the request body.
185
+ *
186
+ * @param options - Optional reader settings.
187
+ * @returns A promise that resolves to the body content.
185
188
  */
186
189
  readBody(options?: BodyReader.Options): Promise<string | Buffer | undefined>;
187
190
  }
188
191
  /**
189
- * @namespace HttpIncoming
192
+ * Utility functions for HttpIncoming.
190
193
  */
191
194
  export declare namespace HttpIncoming {
195
+ /**
196
+ * Creates an HttpIncoming instance from various sources.
197
+ *
198
+ * @param instance - The source instance.
199
+ * @returns The HttpIncoming instance.
200
+ */
192
201
  function from(instance: HttpIncoming | NodeIncomingMessage.Initiator | string | Iterable<any> | AsyncIterable<any>): HttpIncoming;
193
202
  }
@@ -3,10 +3,16 @@ import { HttpIncomingHost } from '../impl/http-incoming.host.js';
3
3
  import { isHttpIncoming, isNodeIncomingMessage } from '../type-guards.js';
4
4
  import { NodeIncomingMessage } from './node-incoming-message.interface.js';
5
5
  /**
6
- * @namespace HttpIncoming
6
+ * Utility functions for HttpIncoming.
7
7
  */
8
8
  export var HttpIncoming;
9
9
  (function (HttpIncoming) {
10
+ /**
11
+ * Creates an HttpIncoming instance from various sources.
12
+ *
13
+ * @param instance - The source instance.
14
+ * @returns The HttpIncoming instance.
15
+ */
10
16
  function from(instance) {
11
17
  if (isHttpIncoming(instance))
12
18
  return instance;
@@ -1,6 +1,10 @@
1
1
  import { type StrictOmit } from 'ts-gems';
2
2
  import type { HttpIncoming } from './http-incoming.interface.js';
3
3
  import { NodeOutgoingMessage } from './node-outgoing-message.interface.js';
4
+ /**
5
+ * HttpOutgoing represents an outgoing HTTP response.
6
+ * It extends NodeOutgoingMessage with additional functionality for handling HTTP responses.
7
+ */
4
8
  export interface HttpOutgoing extends StrictOmit<NodeOutgoingMessage, 'req' | 'appendHeader' | 'setHeader'> {
5
9
  req: HttpIncoming;
6
10
  readonly finished?: boolean;
@@ -10,7 +14,6 @@ export interface HttpOutgoing extends StrictOmit<NodeOutgoingMessage, 'req' | 'a
10
14
  * Set _Content-Disposition_ header to _attachment_ with optional `filename`.
11
15
  */
12
16
  attachment(filename?: string): this;
13
- /** Clear cookie `name`. */
14
17
  clearCookie(name: string, options?: CookieOptions): this;
15
18
  /**
16
19
  * Set cookie `name` to `val`, with the given `options`.
@@ -106,7 +109,7 @@ export interface HttpOutgoing extends StrictOmit<NodeOutgoingMessage, 'req' | 'a
106
109
  */
107
110
  status(code: number): this;
108
111
  /**
109
- * Send given HTTP status code.
112
+ * Send the given HTTP status code.
110
113
  *
111
114
  * Sets the response status to `statusCode` and the body of the
112
115
  * response to the standard description from node's http.STATUS_CODES
@@ -137,8 +140,14 @@ export interface CookieOptions {
137
140
  sameSite?: boolean | 'lax' | 'strict' | 'none';
138
141
  }
139
142
  /**
140
- * @namespace HttpIncoming
143
+ * Utility functions for HttpOutgoing.
141
144
  */
142
145
  export declare namespace HttpOutgoing {
146
+ /**
147
+ * Creates an HttpOutgoing instance from various sources.
148
+ *
149
+ * @param instance - The source instance.
150
+ * @returns The HttpOutgoing instance.
151
+ */
143
152
  function from(instance: HttpOutgoing | NodeOutgoingMessage.Initiator): HttpOutgoing;
144
153
  }
@@ -3,10 +3,16 @@ import { HttpOutgoingHost } from '../impl/http-outgoing.host.js';
3
3
  import { isHttpOutgoing, isNodeOutgoingMessage } from '../type-guards.js';
4
4
  import { NodeOutgoingMessage } from './node-outgoing-message.interface.js';
5
5
  /**
6
- * @namespace HttpIncoming
6
+ * Utility functions for HttpOutgoing.
7
7
  */
8
8
  export var HttpOutgoing;
9
9
  (function (HttpOutgoing) {
10
+ /**
11
+ * Creates an HttpOutgoing instance from various sources.
12
+ *
13
+ * @param instance - The source instance.
14
+ * @returns The HttpOutgoing instance.
15
+ */
10
16
  function from(instance) {
11
17
  if (isHttpOutgoing(instance))
12
18
  return instance;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@opra/http",
3
- "version": "1.26.2",
3
+ "version": "1.26.4",
4
4
  "description": "Opra Http Server Adapter",
5
5
  "author": "Panates",
6
6
  "license": "MIT",
@@ -38,8 +38,8 @@
38
38
  "yaml": "^2.8.3"
39
39
  },
40
40
  "peerDependencies": {
41
- "@opra/common": "^1.26.2",
42
- "@opra/core": "^1.26.2"
41
+ "@opra/common": "^1.26.4",
42
+ "@opra/core": "^1.26.4"
43
43
  },
44
44
  "optionalDependencies": {
45
45
  "express": "^4.0.0 || ^5.0.0",
package/type-guards.d.ts CHANGED
@@ -2,7 +2,31 @@ import type { HttpIncoming } from './interfaces/http-incoming.interface.js';
2
2
  import type { HttpOutgoing } from './interfaces/http-outgoing.interface.js';
3
3
  import type { NodeIncomingMessage } from './interfaces/node-incoming-message.interface.js';
4
4
  import type { NodeOutgoingMessage } from './interfaces/node-outgoing-message.interface.js';
5
+ /**
6
+ * Checks if the given value is a NodeIncomingMessage.
7
+ *
8
+ * @param v - The value to check.
9
+ * @returns True if the value is a NodeIncomingMessage, false otherwise.
10
+ */
5
11
  export declare function isNodeIncomingMessage(v: any): v is NodeIncomingMessage;
12
+ /**
13
+ * Checks if the given value is an HttpIncoming instance.
14
+ *
15
+ * @param v - The value to check.
16
+ * @returns True if the value is an HttpIncoming instance, false otherwise.
17
+ */
6
18
  export declare function isHttpIncoming(v: any): v is HttpIncoming;
19
+ /**
20
+ * Checks if the given value is a NodeOutgoingMessage.
21
+ *
22
+ * @param v - The value to check.
23
+ * @returns True if the value is a NodeOutgoingMessage, false otherwise.
24
+ */
7
25
  export declare function isNodeOutgoingMessage(v: any): v is NodeOutgoingMessage;
26
+ /**
27
+ * Checks if the given value is an HttpOutgoing instance.
28
+ *
29
+ * @param v - The value to check.
30
+ * @returns True if the value is an HttpOutgoing instance, false otherwise.
31
+ */
8
32
  export declare function isHttpOutgoing(v: any): v is HttpOutgoing;
package/type-guards.js CHANGED
@@ -1,19 +1,43 @@
1
1
  import { isReadable, isStream } from '@opra/common';
2
+ /**
3
+ * Checks if the given value is a NodeIncomingMessage.
4
+ *
5
+ * @param v - The value to check.
6
+ * @returns True if the value is a NodeIncomingMessage, false otherwise.
7
+ */
2
8
  export function isNodeIncomingMessage(v) {
3
9
  return (v &&
4
10
  typeof v.method === 'string' &&
5
11
  Array.isArray(v.rawHeaders) &&
6
12
  isReadable(v));
7
13
  }
14
+ /**
15
+ * Checks if the given value is an HttpIncoming instance.
16
+ *
17
+ * @param v - The value to check.
18
+ * @returns True if the value is an HttpIncoming instance, false otherwise.
19
+ */
8
20
  export function isHttpIncoming(v) {
9
21
  return (isNodeIncomingMessage(v) &&
10
22
  typeof v.header === 'function' &&
11
23
  typeof v.acceptsLanguages === 'function' &&
12
24
  typeof v.readBody === 'function');
13
25
  }
26
+ /**
27
+ * Checks if the given value is a NodeOutgoingMessage.
28
+ *
29
+ * @param v - The value to check.
30
+ * @returns True if the value is a NodeOutgoingMessage, false otherwise.
31
+ */
14
32
  export function isNodeOutgoingMessage(v) {
15
33
  return v && typeof v.getHeaders === 'function' && isStream(v);
16
34
  }
35
+ /**
36
+ * Checks if the given value is an HttpOutgoing instance.
37
+ *
38
+ * @param v - The value to check.
39
+ * @returns True if the value is an HttpOutgoing instance, false otherwise.
40
+ */
17
41
  export function isHttpOutgoing(v) {
18
42
  return (isNodeOutgoingMessage(v) &&
19
43
  typeof v.clearCookie === 'function' &&
@@ -15,8 +15,8 @@ export declare namespace BodyReader {
15
15
  }
16
16
  type Callback = (...args: any[]) => any;
17
17
  /**
18
- *
19
- * @class BodyReader
18
+ * BodyReader is responsible for reading and parsing the request body.
19
+ * It supports various content encodings and can save the body to a file.
20
20
  */
21
21
  export declare class BodyReader extends EventEmitter {
22
22
  readonly req: HttpIncoming;
@@ -32,11 +32,25 @@ export declare class BodyReader extends EventEmitter {
32
32
  protected onData: Callback;
33
33
  protected onEnd: Callback;
34
34
  constructor(req: HttpIncoming, options?: BodyReader.Options);
35
+ /**
36
+ * Reads the request body.
37
+ *
38
+ * @returns A promise that resolves to the body content (string, Buffer, or LocalFile).
39
+ * @throws {@link InternalServerError} If the stream is already read or not readable.
40
+ * @throws {@link OpraHttpError} If the content size exceeds the limit.
41
+ */
35
42
  read(): Promise<string | Buffer<ArrayBufferLike> | undefined>;
36
43
  protected _onEnd(error: any): void;
37
44
  protected _cleanup(): void;
38
45
  protected _onAborted(): void;
39
46
  protected _onData(chunk: Buffer | string): void;
47
+ /**
48
+ * Static method to read the request body.
49
+ *
50
+ * @param req - The incoming HTTP request.
51
+ * @param options - Optional reader settings.
52
+ * @returns A promise that resolves to the body content.
53
+ */
40
54
  static read(req: HttpIncoming, options?: BodyReader.Options): Promise<string | Buffer | undefined>;
41
55
  protected static encoderPipeline(req: HttpIncoming): nodeStream.Readable;
42
56
  }
@@ -11,8 +11,8 @@ import { Writable } from 'stream';
11
11
  import * as zlib from 'zlib';
12
12
  import { LocalFile } from '../impl/local-file.js';
13
13
  /**
14
- *
15
- * @class BodyReader
14
+ * BodyReader is responsible for reading and parsing the request body.
15
+ * It supports various content encodings and can save the body to a file.
16
16
  */
17
17
  export class BodyReader extends EventEmitter {
18
18
  req;
@@ -53,6 +53,13 @@ export class BodyReader extends EventEmitter {
53
53
  if (this._file)
54
54
  this._file.autoDelete = true;
55
55
  }
56
+ /**
57
+ * Reads the request body.
58
+ *
59
+ * @returns A promise that resolves to the body content (string, Buffer, or LocalFile).
60
+ * @throws {@link InternalServerError} If the stream is already read or not readable.
61
+ * @throws {@link OpraHttpError} If the content size exceeds the limit.
62
+ */
56
63
  async read() {
57
64
  /* istanbul ignore next */
58
65
  if (this._completed) {
@@ -209,6 +216,13 @@ export class BodyReader extends EventEmitter {
209
216
  this._buffer.push(chunk);
210
217
  }
211
218
  }
219
+ /**
220
+ * Static method to read the request body.
221
+ *
222
+ * @param req - The incoming HTTP request.
223
+ * @param options - Optional reader settings.
224
+ * @returns A promise that resolves to the body content.
225
+ */
212
226
  static async read(req, options) {
213
227
  const bodyReady = new BodyReader(req, options);
214
228
  return bodyReady.read();
package/utils/common.d.ts CHANGED
@@ -1,17 +1,25 @@
1
1
  /**
2
- * Verifies that the given val is a valid HTTP token
3
- * per the rules defined in RFC 7230
4
- * See https://tools.ietf.org/html/rfc7230#section-3.2.6
2
+ * Verifies that the given val is a valid HTTP token per the rules defined in RFC 7230.
3
+ * See {@link https://tools.ietf.org/html/rfc7230#section-3.2.6}
5
4
  *
6
- * https://github.com/nodejs/node/blob/main/lib/_http_common.js
5
+ * @param val - The value to check.
6
+ * @returns True if the value is a valid HTTP token, false otherwise.
7
7
  */
8
8
  export declare function checkIsHttpToken(val: any): boolean;
9
9
  /**
10
- * This function removes unnecessary frames from Node.js core errors.
10
+ * Hides internal Node.js stack frames for a given function.
11
11
  *
12
- * https://github.com/nodejs/node/blob/main/lib/internal/errors.js
12
+ * @param fn - The function to hide stack frames for.
13
+ * @returns The function with hidden stack frames.
13
14
  */
14
15
  export declare function hideStackFrames(fn: Function): Function;
15
16
  export declare const validateHeaderName: Function;
16
17
  export declare const validateHeaderValue: Function;
18
+ /**
19
+ * Validates if the given value is a string.
20
+ *
21
+ * @param value - The value to validate.
22
+ * @param name - The name of the argument being validated.
23
+ * @throws {@link TypeError} If the value is not a string.
24
+ */
17
25
  export declare function validateString(value: any, name?: string): void;
package/utils/common.js CHANGED
@@ -5,11 +5,11 @@
5
5
  const tokenRegExp = /^[\^_`a-zA-Z\-0-9!#$%&'*+.|~]+$/;
6
6
  const nodeInternalPrefix = '__node_internal_';
7
7
  /**
8
- * Verifies that the given val is a valid HTTP token
9
- * per the rules defined in RFC 7230
10
- * See https://tools.ietf.org/html/rfc7230#section-3.2.6
8
+ * Verifies that the given val is a valid HTTP token per the rules defined in RFC 7230.
9
+ * See {@link https://tools.ietf.org/html/rfc7230#section-3.2.6}
11
10
  *
12
- * https://github.com/nodejs/node/blob/main/lib/_http_common.js
11
+ * @param val - The value to check.
12
+ * @returns True if the value is a valid HTTP token, false otherwise.
13
13
  */
14
14
  export function checkIsHttpToken(val) {
15
15
  return typeof val === 'string' && tokenRegExp.exec(val) !== null;
@@ -28,9 +28,10 @@ function checkInvalidHeaderChar(val) {
28
28
  return typeof val === 'string' && headerCharRegex.exec(val) !== null;
29
29
  }
30
30
  /**
31
- * This function removes unnecessary frames from Node.js core errors.
31
+ * Hides internal Node.js stack frames for a given function.
32
32
  *
33
- * https://github.com/nodejs/node/blob/main/lib/internal/errors.js
33
+ * @param fn - The function to hide stack frames for.
34
+ * @returns The function with hidden stack frames.
34
35
  */
35
36
  export function hideStackFrames(fn) {
36
37
  // We rename the functions that will be hidden to cut off the stacktrace
@@ -54,6 +55,13 @@ export const validateHeaderValue = hideStackFrames((name, value) => {
54
55
  throw new TypeError(`Invalid character in header content ["${name}"]`);
55
56
  }
56
57
  });
58
+ /**
59
+ * Validates if the given value is a string.
60
+ *
61
+ * @param value - The value to validate.
62
+ * @param name - The name of the argument being validated.
63
+ * @throws {@link TypeError} If the value is not a string.
64
+ */
57
65
  export function validateString(value, name) {
58
66
  if (typeof value !== 'string') {
59
67
  throw new TypeError(`Invalid ${name ? name + ' ' : ''}argument. Value must be a string`);
@@ -1,2 +1,8 @@
1
1
  import { OpraHttpError } from '@opra/common';
2
+ /**
3
+ * Wraps an error into an OpraHttpError.
4
+ *
5
+ * @param error - The error to wrap.
6
+ * @returns The wrapped OpraHttpError.
7
+ */
2
8
  export declare function wrapException(error: any): OpraHttpError;
@@ -1,4 +1,10 @@
1
1
  import { BadRequestError, FailedDependencyError, ForbiddenError, InternalServerError, MethodNotAllowedError, NotAcceptableError, NotFoundError, OpraHttpError, UnauthorizedError, UnprocessableEntityError, } from '@opra/common';
2
+ /**
3
+ * Wraps an error into an OpraHttpError.
4
+ *
5
+ * @param error - The error to wrap.
6
+ * @returns The wrapped OpraHttpError.
7
+ */
2
8
  export function wrapException(error) {
3
9
  if (error instanceof OpraHttpError)
4
10
  return error;