@opra/nestjs 1.0.0-alpha.9 → 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.
@@ -1,8 +1,8 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- const external_exception_filter_1 = require("@nestjs/core/exceptions/external-exception-filter");
4
- const oldCatchMethod = external_exception_filter_1.ExternalExceptionFilter.prototype.catch;
5
- external_exception_filter_1.ExternalExceptionFilter.prototype.catch = function (exception, host) {
3
+ const external_exception_filter_js_1 = require("@nestjs/core/exceptions/external-exception-filter.js");
4
+ const oldCatchMethod = external_exception_filter_js_1.ExternalExceptionFilter.prototype.catch;
5
+ external_exception_filter_js_1.ExternalExceptionFilter.prototype.catch = function (exception, host) {
6
6
  const opraContext = host.getArgByIndex(3);
7
7
  // Prevents error logging for all Opra controllers
8
8
  if (opraContext && opraContext.request && opraContext.response)
package/cjs/constants.js CHANGED
@@ -1,4 +1,5 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.IS_PUBLIC_KEY = void 0;
4
- exports.IS_PUBLIC_KEY = 'isPublic';
3
+ exports.OPRA_HTTP_MODULE_OPTIONS = exports.IS_PUBLIC_KEY = void 0;
4
+ exports.IS_PUBLIC_KEY = 'opra:isPublic';
5
+ exports.OPRA_HTTP_MODULE_OPTIONS = 'OPRA_HTTP_MODULE_OPTIONS';
@@ -4,9 +4,12 @@ Object.defineProperty(exports, "__esModule", { value: true });
4
4
  exports.OpraHttpCoreModule = void 0;
5
5
  const tslib_1 = require("tslib");
6
6
  const common_1 = require("@nestjs/common");
7
+ const core_1 = require("@nestjs/core");
7
8
  const common_2 = require("@opra/common");
8
9
  const ts_gems_1 = require("ts-gems");
10
+ const constants_js_1 = require("./constants.js");
9
11
  const opra_nestjs_adapter_js_1 = require("./opra-nestjs-adapter.js");
12
+ const opra_exception_filter_js_1 = require("./services/opra-exception-filter.js");
10
13
  const opra_middleware_js_1 = require("./services/opra-middleware.js");
11
14
  let OpraHttpCoreModule = OpraHttpCoreModule_1 = class OpraHttpCoreModule {
12
15
  constructor(opraAdapter) {
@@ -23,16 +26,35 @@ let OpraHttpCoreModule = OpraHttpCoreModule_1 = class OpraHttpCoreModule {
23
26
  const token = init?.id || opra_nestjs_adapter_js_1.OpraNestAdapter;
24
27
  const providers = [
25
28
  ...(init?.providers || []),
29
+ {
30
+ provide: constants_js_1.OPRA_HTTP_MODULE_OPTIONS,
31
+ useValue: { ...options },
32
+ },
26
33
  {
27
34
  provide: opra_nestjs_adapter_js_1.OpraNestAdapter,
28
- useFactory: async () => {
35
+ inject: [core_1.ModuleRef],
36
+ useFactory: async (moduleRef) => {
29
37
  (0, ts_gems_1.asMutable)(opraAdapter).document = await common_2.ApiDocumentFactory.createDocument({
30
38
  ...init,
31
39
  api: { protocol: 'http', name: init.name, controllers: init.controllers },
32
40
  });
41
+ opraAdapter.interceptors.map(x => {
42
+ if ((0, common_2.isConstructor)(x)) {
43
+ return (ctx, next) => {
44
+ const interceptor = moduleRef.get(x);
45
+ if (typeof interceptor.intercept === 'function')
46
+ return interceptor.intercept(ctx, next());
47
+ };
48
+ }
49
+ return x;
50
+ });
33
51
  return opraAdapter;
34
52
  },
35
53
  },
54
+ {
55
+ provide: core_1.APP_FILTER,
56
+ useClass: opra_exception_filter_js_1.OpraExceptionFilter,
57
+ },
36
58
  ];
37
59
  if (token !== opra_nestjs_adapter_js_1.OpraNestAdapter) {
38
60
  providers.push({
@@ -1,22 +1,24 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.OpraNestAdapter = exports.kHandler = void 0;
3
+ exports.OpraNestAdapter = void 0;
4
4
  const tslib_1 = require("tslib");
5
+ const node_path_1 = tslib_1.__importDefault(require("node:path"));
5
6
  const common_1 = require("@nestjs/common");
7
+ const constants_js_1 = require("@nestjs/common/constants.js");
6
8
  const common_2 = require("@opra/common");
7
9
  const core_1 = require("@opra/core");
8
- const path_1 = tslib_1.__importDefault(require("path"));
9
10
  const ts_gems_1 = require("ts-gems");
10
- const public_decorator_1 = require("./decorators/public.decorator");
11
- const opra_exception_filter_js_1 = require("./services/opra-exception-filter.js");
12
- exports.kHandler = Symbol.for('kHandler');
11
+ const public_decorator_js_1 = require("./decorators/public.decorator.js");
13
12
  class OpraNestAdapter extends core_1.HttpAdapter {
14
13
  constructor(init, options) {
15
14
  super((function () {
16
15
  const document = new common_2.ApiDocument();
17
16
  document.api = new common_2.HttpApi(document);
18
17
  return document;
19
- })(), options);
18
+ })(), {
19
+ ...options,
20
+ interceptors: options?.interceptors,
21
+ });
20
22
  this.nestControllers = [];
21
23
  this.options = options;
22
24
  let basePath = options?.basePath || '/';
@@ -24,7 +26,7 @@ class OpraNestAdapter extends core_1.HttpAdapter {
24
26
  basePath = '/' + basePath;
25
27
  this._addRootController(basePath);
26
28
  if (init.controllers)
27
- init.controllers.forEach(c => this._addToNestControllers(c, basePath));
29
+ init.controllers.forEach(c => this._addToNestControllers(c, basePath, []));
28
30
  }
29
31
  async close() {
30
32
  //
@@ -33,7 +35,7 @@ class OpraNestAdapter extends core_1.HttpAdapter {
33
35
  const _this = this;
34
36
  let RootController = class RootController {
35
37
  schema(_req, next) {
36
- _this[exports.kHandler].sendDocumentSchema(_req.opraContext).catch(next);
38
+ _this.handler.sendDocumentSchema(_req.opraContext).catch(() => next());
37
39
  }
38
40
  };
39
41
  tslib_1.__decorate([
@@ -50,11 +52,11 @@ class OpraNestAdapter extends core_1.HttpAdapter {
50
52
  })
51
53
  ], RootController);
52
54
  if (this.options?.schemaRouteIsPublic) {
53
- (0, public_decorator_1.Public)()(RootController.prototype, 'schema', Object.getOwnPropertyDescriptor(RootController.prototype, 'schema'));
55
+ (0, public_decorator_js_1.Public)()(RootController.prototype, 'schema', Object.getOwnPropertyDescriptor(RootController.prototype, 'schema'));
54
56
  }
55
57
  this.nestControllers.push(RootController);
56
58
  }
57
- _addToNestControllers(sourceClass, currentPath) {
59
+ _addToNestControllers(sourceClass, currentPath, parentTree) {
58
60
  const metadata = Reflect.getMetadata(common_2.HTTP_CONTROLLER_METADATA, sourceClass);
59
61
  if (!metadata)
60
62
  return;
@@ -62,10 +64,16 @@ class OpraNestAdapter extends core_1.HttpAdapter {
62
64
  [sourceClass.name]: class extends sourceClass {
63
65
  },
64
66
  }[sourceClass.name];
65
- const newPath = metadata.path ? path_1.default.join(currentPath, metadata.path) : currentPath;
67
+ /** Copy metadata keys from source class to new one */
68
+ let metadataKeys;
69
+ OpraNestAdapter.copyDecoratorMetadataToChild(newClass, parentTree);
70
+ const newPath = metadata.path ? node_path_1.default.join(currentPath, metadata.path) : currentPath;
66
71
  const adapter = this;
67
- /** Inject exception filter */
68
- (0, common_1.UseFilters)(new opra_exception_filter_js_1.OpraExceptionFilter(adapter))(newClass);
72
+ // adapter.logger =
73
+ /** Disable default error handler. Errors will be handled by OpraExceptionFilter */
74
+ adapter.handler.onError = (context, error) => {
75
+ throw error;
76
+ };
69
77
  (0, common_1.Controller)()(newClass);
70
78
  this.nestControllers.push(newClass);
71
79
  if (metadata.operations) {
@@ -74,7 +82,8 @@ class OpraNestAdapter extends core_1.HttpAdapter {
74
82
  Object.defineProperty(newClass.prototype, k, {
75
83
  writable: true,
76
84
  /** NestJS handler method */
77
- async value(_req) {
85
+ async value(_req, _res) {
86
+ _res.statusCode = 200;
78
87
  const api = adapter.document.api;
79
88
  const controller = api.findController(sourceClass);
80
89
  const operation = controller?.operations.get(k);
@@ -89,18 +98,16 @@ class OpraNestAdapter extends core_1.HttpAdapter {
89
98
  });
90
99
  }
91
100
  /** Configure the HttpContext */
92
- context.adapter = adapter;
93
- context.document = adapter.document;
94
101
  context.operation = operation;
95
102
  context.controller = operation.owner;
96
103
  context.controllerInstance = this;
97
104
  context.operationHandler = operationHandler;
98
105
  /** Handle request */
99
- await adapter[exports.kHandler].handleRequest(context);
106
+ await adapter.handler.handleRequest(context);
100
107
  },
101
108
  });
102
109
  /** Copy metadata keys from source function to new one */
103
- const metadataKeys = Reflect.getOwnMetadataKeys(operationHandler);
110
+ metadataKeys = Reflect.getOwnMetadataKeys(operationHandler);
104
111
  const newFn = newClass.prototype[k];
105
112
  for (const key of metadataKeys) {
106
113
  const m = Reflect.getMetadata(key, operationHandler);
@@ -109,7 +116,7 @@ class OpraNestAdapter extends core_1.HttpAdapter {
109
116
  (0, common_1.Req)()(newClass.prototype, k, 0);
110
117
  (0, common_1.Res)()(newClass.prototype, k, 1);
111
118
  const descriptor = Object.getOwnPropertyDescriptor(newClass.prototype, k);
112
- const operationPath = newPath + (v.path || '');
119
+ const operationPath = v.mergePath ? newPath + (v.path || '') : node_path_1.default.posix.join(newPath, v.path || '');
113
120
  switch (v.method || 'GET') {
114
121
  case 'DELETE':
115
122
  /** Call @Delete decorator over new property */
@@ -152,7 +159,30 @@ class OpraNestAdapter extends core_1.HttpAdapter {
152
159
  for (const child of metadata.controllers) {
153
160
  if (!(0, common_2.isConstructor)(child))
154
161
  throw new TypeError('Controllers should be injectable a class');
155
- this._addToNestControllers(child, newPath);
162
+ this._addToNestControllers(child, newPath, [...parentTree, sourceClass]);
163
+ }
164
+ }
165
+ }
166
+ static copyDecoratorMetadataToChild(target, parentTree) {
167
+ for (const parent of parentTree) {
168
+ const metadataKeys = Reflect.getOwnMetadataKeys(parent);
169
+ for (const key of metadataKeys) {
170
+ if (typeof key === 'string' && key.startsWith('opra:') && !Reflect.hasOwnMetadata(key, target)) {
171
+ const metadata = Reflect.getMetadata(key, parent);
172
+ Reflect.defineMetadata(key, metadata, target);
173
+ continue;
174
+ }
175
+ if (key === constants_js_1.GUARDS_METADATA || key === constants_js_1.INTERCEPTORS_METADATA || key === constants_js_1.EXCEPTION_FILTERS_METADATA) {
176
+ const m1 = Reflect.getMetadata(key, target) || [];
177
+ const metadata = [...m1];
178
+ const m2 = Reflect.getOwnMetadata(key, parent) || [];
179
+ m2.forEach((t) => {
180
+ if (!metadata.includes(t)) {
181
+ metadata.push(t);
182
+ }
183
+ });
184
+ Reflect.defineMetadata(key, metadata, target);
185
+ }
156
186
  }
157
187
  }
158
188
  }
@@ -1,18 +1,26 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.OpraExceptionFilter = exports.kHandler = void 0;
4
- const core_1 = require("@opra/core");
5
- exports.kHandler = Symbol.for('kHandler');
6
- class OpraExceptionFilter {
7
- constructor(adapter) {
8
- this.adapter = adapter;
3
+ exports.OpraExceptionFilter = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const common_1 = require("@nestjs/common");
6
+ const core_1 = require("@nestjs/core");
7
+ const opra_nestjs_adapter_js_1 = require("../opra-nestjs-adapter.js");
8
+ let OpraExceptionFilter = class OpraExceptionFilter extends core_1.BaseExceptionFilter {
9
+ constructor(moduleRef) {
10
+ super();
11
+ this.moduleRef = moduleRef;
9
12
  }
10
13
  catch(exception, host) {
11
- const ctx = host.switchToHttp();
12
- const _res = ctx.getResponse();
13
- const error = (0, core_1.wrapException)(exception);
14
- const response = core_1.HttpOutgoing.from(_res);
15
- return this.adapter[exports.kHandler].sendErrorResponse(response, [error]);
14
+ const ctx = host.switchToHttp().getRequest().opraContext;
15
+ if (ctx) {
16
+ const adapter = this.moduleRef.get(opra_nestjs_adapter_js_1.OpraNestAdapter);
17
+ ctx.errors.push(exception);
18
+ return adapter.handler.sendResponse(ctx);
19
+ }
16
20
  }
17
- }
21
+ };
18
22
  exports.OpraExceptionFilter = OpraExceptionFilter;
23
+ exports.OpraExceptionFilter = OpraExceptionFilter = tslib_1.__decorate([
24
+ (0, common_1.Catch)(),
25
+ tslib_1.__metadata("design:paramtypes", [core_1.ModuleRef])
26
+ ], OpraExceptionFilter);
@@ -4,21 +4,33 @@ exports.OpraMiddleware = void 0;
4
4
  const tslib_1 = require("tslib");
5
5
  const common_1 = require("@nestjs/common");
6
6
  const core_1 = require("@opra/core");
7
+ const constants_js_1 = require("../constants.js");
8
+ const opra_nestjs_adapter_js_1 = require("../opra-nestjs-adapter.js");
7
9
  let OpraMiddleware = class OpraMiddleware {
10
+ constructor(opraAdapter, options) {
11
+ this.opraAdapter = opraAdapter;
12
+ this.options = options;
13
+ }
8
14
  use(req, res, next) {
9
15
  const request = core_1.HttpIncoming.from(req);
10
16
  const response = core_1.HttpOutgoing.from(res);
11
17
  /** Create the HttpContext */
12
- req.opraContext = new core_1.HttpContext({
13
- adapter: {},
18
+ const context = new core_1.HttpContext({
19
+ adapter: this.opraAdapter,
14
20
  platform: req.route ? 'express' : 'fastify',
15
21
  request,
16
22
  response,
17
23
  });
18
- next();
24
+ req.opraContext = context;
25
+ this.opraAdapter
26
+ .emitAsync('createContext', context)
27
+ .then(() => next())
28
+ .catch(next);
19
29
  }
20
30
  };
21
31
  exports.OpraMiddleware = OpraMiddleware;
22
32
  exports.OpraMiddleware = OpraMiddleware = tslib_1.__decorate([
23
- (0, common_1.Injectable)()
33
+ (0, common_1.Injectable)(),
34
+ tslib_1.__param(1, (0, common_1.Inject)(constants_js_1.OPRA_HTTP_MODULE_OPTIONS)),
35
+ tslib_1.__metadata("design:paramtypes", [opra_nestjs_adapter_js_1.OpraNestAdapter, Object])
24
36
  ], OpraMiddleware);
@@ -1,4 +1,4 @@
1
- import { ExternalExceptionFilter } from '@nestjs/core/exceptions/external-exception-filter';
1
+ import { ExternalExceptionFilter } from '@nestjs/core/exceptions/external-exception-filter.js';
2
2
  const oldCatchMethod = ExternalExceptionFilter.prototype.catch;
3
3
  ExternalExceptionFilter.prototype.catch = function (exception, host) {
4
4
  const opraContext = host.getArgByIndex(3);
package/esm/constants.js CHANGED
@@ -1 +1,2 @@
1
- export const IS_PUBLIC_KEY = 'isPublic';
1
+ export const IS_PUBLIC_KEY = 'opra:isPublic';
2
+ export const OPRA_HTTP_MODULE_OPTIONS = 'OPRA_HTTP_MODULE_OPTIONS';
@@ -1,9 +1,12 @@
1
1
  var OpraHttpCoreModule_1;
2
2
  import { __decorate, __metadata } from "tslib";
3
3
  import { Global, Module, RequestMethod, } from '@nestjs/common';
4
- import { ApiDocumentFactory } from '@opra/common';
4
+ import { APP_FILTER, ModuleRef } from '@nestjs/core';
5
+ import { ApiDocumentFactory, isConstructor } from '@opra/common';
5
6
  import { asMutable } from 'ts-gems';
7
+ import { OPRA_HTTP_MODULE_OPTIONS } from './constants.js';
6
8
  import { OpraNestAdapter } from './opra-nestjs-adapter.js';
9
+ import { OpraExceptionFilter } from './services/opra-exception-filter.js';
7
10
  import { OpraMiddleware } from './services/opra-middleware.js';
8
11
  let OpraHttpCoreModule = OpraHttpCoreModule_1 = class OpraHttpCoreModule {
9
12
  constructor(opraAdapter) {
@@ -20,16 +23,35 @@ let OpraHttpCoreModule = OpraHttpCoreModule_1 = class OpraHttpCoreModule {
20
23
  const token = init?.id || OpraNestAdapter;
21
24
  const providers = [
22
25
  ...(init?.providers || []),
26
+ {
27
+ provide: OPRA_HTTP_MODULE_OPTIONS,
28
+ useValue: { ...options },
29
+ },
23
30
  {
24
31
  provide: OpraNestAdapter,
25
- useFactory: async () => {
32
+ inject: [ModuleRef],
33
+ useFactory: async (moduleRef) => {
26
34
  asMutable(opraAdapter).document = await ApiDocumentFactory.createDocument({
27
35
  ...init,
28
36
  api: { protocol: 'http', name: init.name, controllers: init.controllers },
29
37
  });
38
+ opraAdapter.interceptors.map(x => {
39
+ if (isConstructor(x)) {
40
+ return (ctx, next) => {
41
+ const interceptor = moduleRef.get(x);
42
+ if (typeof interceptor.intercept === 'function')
43
+ return interceptor.intercept(ctx, next());
44
+ };
45
+ }
46
+ return x;
47
+ });
30
48
  return opraAdapter;
31
49
  },
32
50
  },
51
+ {
52
+ provide: APP_FILTER,
53
+ useClass: OpraExceptionFilter,
54
+ },
33
55
  ];
34
56
  if (token !== OpraNestAdapter) {
35
57
  providers.push({
@@ -1,19 +1,21 @@
1
1
  import { __decorate, __metadata, __param } from "tslib";
2
- import { Controller, Delete, Get, Head, Next, Options, Patch, Post, Put, Req, Res, Search, UseFilters, } from '@nestjs/common';
2
+ import nodePath from 'node:path';
3
+ import { Controller, Delete, Get, Head, Next, Options, Patch, Post, Put, Req, Res, Search, } from '@nestjs/common';
4
+ import { EXCEPTION_FILTERS_METADATA, GUARDS_METADATA, INTERCEPTORS_METADATA } from '@nestjs/common/constants.js';
3
5
  import { ApiDocument, HTTP_CONTROLLER_METADATA, HttpApi, isConstructor, NotFoundError, } from '@opra/common';
4
6
  import { HttpAdapter } from '@opra/core';
5
- import nodePath from 'path';
6
7
  import { asMutable } from 'ts-gems';
7
- import { Public } from './decorators/public.decorator';
8
- import { OpraExceptionFilter } from './services/opra-exception-filter.js';
9
- export const kHandler = Symbol.for('kHandler');
8
+ import { Public } from './decorators/public.decorator.js';
10
9
  export class OpraNestAdapter extends HttpAdapter {
11
10
  constructor(init, options) {
12
11
  super((function () {
13
12
  const document = new ApiDocument();
14
13
  document.api = new HttpApi(document);
15
14
  return document;
16
- })(), options);
15
+ })(), {
16
+ ...options,
17
+ interceptors: options?.interceptors,
18
+ });
17
19
  this.nestControllers = [];
18
20
  this.options = options;
19
21
  let basePath = options?.basePath || '/';
@@ -21,7 +23,7 @@ export class OpraNestAdapter extends HttpAdapter {
21
23
  basePath = '/' + basePath;
22
24
  this._addRootController(basePath);
23
25
  if (init.controllers)
24
- init.controllers.forEach(c => this._addToNestControllers(c, basePath));
26
+ init.controllers.forEach(c => this._addToNestControllers(c, basePath, []));
25
27
  }
26
28
  async close() {
27
29
  //
@@ -30,7 +32,7 @@ export class OpraNestAdapter extends HttpAdapter {
30
32
  const _this = this;
31
33
  let RootController = class RootController {
32
34
  schema(_req, next) {
33
- _this[kHandler].sendDocumentSchema(_req.opraContext).catch(next);
35
+ _this.handler.sendDocumentSchema(_req.opraContext).catch(() => next());
34
36
  }
35
37
  };
36
38
  __decorate([
@@ -51,7 +53,7 @@ export class OpraNestAdapter extends HttpAdapter {
51
53
  }
52
54
  this.nestControllers.push(RootController);
53
55
  }
54
- _addToNestControllers(sourceClass, currentPath) {
56
+ _addToNestControllers(sourceClass, currentPath, parentTree) {
55
57
  const metadata = Reflect.getMetadata(HTTP_CONTROLLER_METADATA, sourceClass);
56
58
  if (!metadata)
57
59
  return;
@@ -59,10 +61,16 @@ export class OpraNestAdapter extends HttpAdapter {
59
61
  [sourceClass.name]: class extends sourceClass {
60
62
  },
61
63
  }[sourceClass.name];
64
+ /** Copy metadata keys from source class to new one */
65
+ let metadataKeys;
66
+ OpraNestAdapter.copyDecoratorMetadataToChild(newClass, parentTree);
62
67
  const newPath = metadata.path ? nodePath.join(currentPath, metadata.path) : currentPath;
63
68
  const adapter = this;
64
- /** Inject exception filter */
65
- UseFilters(new OpraExceptionFilter(adapter))(newClass);
69
+ // adapter.logger =
70
+ /** Disable default error handler. Errors will be handled by OpraExceptionFilter */
71
+ adapter.handler.onError = (context, error) => {
72
+ throw error;
73
+ };
66
74
  Controller()(newClass);
67
75
  this.nestControllers.push(newClass);
68
76
  if (metadata.operations) {
@@ -71,7 +79,8 @@ export class OpraNestAdapter extends HttpAdapter {
71
79
  Object.defineProperty(newClass.prototype, k, {
72
80
  writable: true,
73
81
  /** NestJS handler method */
74
- async value(_req) {
82
+ async value(_req, _res) {
83
+ _res.statusCode = 200;
75
84
  const api = adapter.document.api;
76
85
  const controller = api.findController(sourceClass);
77
86
  const operation = controller?.operations.get(k);
@@ -86,18 +95,16 @@ export class OpraNestAdapter extends HttpAdapter {
86
95
  });
87
96
  }
88
97
  /** Configure the HttpContext */
89
- context.adapter = adapter;
90
- context.document = adapter.document;
91
98
  context.operation = operation;
92
99
  context.controller = operation.owner;
93
100
  context.controllerInstance = this;
94
101
  context.operationHandler = operationHandler;
95
102
  /** Handle request */
96
- await adapter[kHandler].handleRequest(context);
103
+ await adapter.handler.handleRequest(context);
97
104
  },
98
105
  });
99
106
  /** Copy metadata keys from source function to new one */
100
- const metadataKeys = Reflect.getOwnMetadataKeys(operationHandler);
107
+ metadataKeys = Reflect.getOwnMetadataKeys(operationHandler);
101
108
  const newFn = newClass.prototype[k];
102
109
  for (const key of metadataKeys) {
103
110
  const m = Reflect.getMetadata(key, operationHandler);
@@ -106,7 +113,7 @@ export class OpraNestAdapter extends HttpAdapter {
106
113
  Req()(newClass.prototype, k, 0);
107
114
  Res()(newClass.prototype, k, 1);
108
115
  const descriptor = Object.getOwnPropertyDescriptor(newClass.prototype, k);
109
- const operationPath = newPath + (v.path || '');
116
+ const operationPath = v.mergePath ? newPath + (v.path || '') : nodePath.posix.join(newPath, v.path || '');
110
117
  switch (v.method || 'GET') {
111
118
  case 'DELETE':
112
119
  /** Call @Delete decorator over new property */
@@ -149,7 +156,30 @@ export class OpraNestAdapter extends HttpAdapter {
149
156
  for (const child of metadata.controllers) {
150
157
  if (!isConstructor(child))
151
158
  throw new TypeError('Controllers should be injectable a class');
152
- this._addToNestControllers(child, newPath);
159
+ this._addToNestControllers(child, newPath, [...parentTree, sourceClass]);
160
+ }
161
+ }
162
+ }
163
+ static copyDecoratorMetadataToChild(target, parentTree) {
164
+ for (const parent of parentTree) {
165
+ const metadataKeys = Reflect.getOwnMetadataKeys(parent);
166
+ for (const key of metadataKeys) {
167
+ if (typeof key === 'string' && key.startsWith('opra:') && !Reflect.hasOwnMetadata(key, target)) {
168
+ const metadata = Reflect.getMetadata(key, parent);
169
+ Reflect.defineMetadata(key, metadata, target);
170
+ continue;
171
+ }
172
+ if (key === GUARDS_METADATA || key === INTERCEPTORS_METADATA || key === EXCEPTION_FILTERS_METADATA) {
173
+ const m1 = Reflect.getMetadata(key, target) || [];
174
+ const metadata = [...m1];
175
+ const m2 = Reflect.getOwnMetadata(key, parent) || [];
176
+ m2.forEach((t) => {
177
+ if (!metadata.includes(t)) {
178
+ metadata.push(t);
179
+ }
180
+ });
181
+ Reflect.defineMetadata(key, metadata, target);
182
+ }
153
183
  }
154
184
  }
155
185
  }
@@ -0,0 +1,3 @@
1
+ {
2
+ "type": "module"
3
+ }
@@ -1,14 +1,23 @@
1
- import { HttpOutgoing, wrapException } from '@opra/core';
2
- export const kHandler = Symbol.for('kHandler');
3
- export class OpraExceptionFilter {
4
- constructor(adapter) {
5
- this.adapter = adapter;
1
+ import { __decorate, __metadata } from "tslib";
2
+ import { Catch } from '@nestjs/common';
3
+ import { BaseExceptionFilter, ModuleRef } from '@nestjs/core';
4
+ import { OpraNestAdapter } from '../opra-nestjs-adapter.js';
5
+ let OpraExceptionFilter = class OpraExceptionFilter extends BaseExceptionFilter {
6
+ constructor(moduleRef) {
7
+ super();
8
+ this.moduleRef = moduleRef;
6
9
  }
7
10
  catch(exception, host) {
8
- const ctx = host.switchToHttp();
9
- const _res = ctx.getResponse();
10
- const error = wrapException(exception);
11
- const response = HttpOutgoing.from(_res);
12
- return this.adapter[kHandler].sendErrorResponse(response, [error]);
11
+ const ctx = host.switchToHttp().getRequest().opraContext;
12
+ if (ctx) {
13
+ const adapter = this.moduleRef.get(OpraNestAdapter);
14
+ ctx.errors.push(exception);
15
+ return adapter.handler.sendResponse(ctx);
16
+ }
13
17
  }
14
- }
18
+ };
19
+ OpraExceptionFilter = __decorate([
20
+ Catch(),
21
+ __metadata("design:paramtypes", [ModuleRef])
22
+ ], OpraExceptionFilter);
23
+ export { OpraExceptionFilter };
@@ -1,21 +1,33 @@
1
- import { __decorate } from "tslib";
2
- import { Injectable } from '@nestjs/common';
1
+ import { __decorate, __metadata, __param } from "tslib";
2
+ import { Inject, Injectable } from '@nestjs/common';
3
3
  import { HttpContext, HttpIncoming, HttpOutgoing } from '@opra/core';
4
+ import { OPRA_HTTP_MODULE_OPTIONS } from '../constants.js';
5
+ import { OpraNestAdapter } from '../opra-nestjs-adapter.js';
4
6
  let OpraMiddleware = class OpraMiddleware {
7
+ constructor(opraAdapter, options) {
8
+ this.opraAdapter = opraAdapter;
9
+ this.options = options;
10
+ }
5
11
  use(req, res, next) {
6
12
  const request = HttpIncoming.from(req);
7
13
  const response = HttpOutgoing.from(res);
8
14
  /** Create the HttpContext */
9
- req.opraContext = new HttpContext({
10
- adapter: {},
15
+ const context = new HttpContext({
16
+ adapter: this.opraAdapter,
11
17
  platform: req.route ? 'express' : 'fastify',
12
18
  request,
13
19
  response,
14
20
  });
15
- next();
21
+ req.opraContext = context;
22
+ this.opraAdapter
23
+ .emitAsync('createContext', context)
24
+ .then(() => next())
25
+ .catch(next);
16
26
  }
17
27
  };
18
28
  OpraMiddleware = __decorate([
19
- Injectable()
29
+ Injectable(),
30
+ __param(1, Inject(OPRA_HTTP_MODULE_OPTIONS)),
31
+ __metadata("design:paramtypes", [OpraNestAdapter, Object])
20
32
  ], OpraMiddleware);
21
33
  export { OpraMiddleware };
package/package.json CHANGED
@@ -1,55 +1,45 @@
1
1
  {
2
2
  "name": "@opra/nestjs",
3
- "version": "1.0.0-alpha.9",
3
+ "version": "1.0.0-beta.1",
4
4
  "description": "Opra NestJS module",
5
5
  "author": "Panates",
6
6
  "license": "MIT",
7
- "repository": {
8
- "type": "git",
9
- "url": "https://github.com/panates/opra.git",
10
- "directory": "packages/nestjs"
11
- },
12
- "scripts": {
13
- "compile": "tsc",
14
- "prebuild": "npm run lint && npm run clean",
15
- "build": "npm run build:cjs && npm run build:esm",
16
- "build:cjs": "tsc -b tsconfig-build-cjs.json",
17
- "build:esm": "tsc -b tsconfig-build-esm.json",
18
- "postbuild": "cp README.md package.json ../../LICENSE ../../build/nestjs && cp ../../package.cjs.json ../../build/nestjs/cjs/package.json",
19
- "lint": "eslint . --max-warnings=0",
20
- "format": "prettier . --write --log-level=warn",
21
- "test": "jest --passWithNoTests",
22
- "cover": "jest --passWithNoTests --collect-coverage",
23
- "clean": "npm run clean:src && npm run clean:test && npm run clean:dist && npm run clean:cover",
24
- "clean:src": "ts-cleanup -s src --all",
25
- "clean:test": "ts-cleanup -s test --all",
26
- "clean:dist": "rimraf ../../build/client",
27
- "clean:cover": "rimraf ../../coverage/client"
28
- },
29
7
  "dependencies": {
30
- "@opra/common": "^1.0.0-alpha.9",
31
- "@opra/core": "^1.0.0-alpha.9",
32
- "fast-tokenizer": "^1.3.0",
8
+ "@opra/common": "^1.0.0-beta.1",
9
+ "@opra/core": "^1.0.0-beta.1",
10
+ "fast-tokenizer": "^1.7.0",
33
11
  "lodash.head": "^4.0.1",
34
- "reflect-metadata": "^0.2.2"
12
+ "putil-promisify": "^1.10.1",
13
+ "reflect-metadata": "^0.2.2",
14
+ "tslib": "^2.7.0"
35
15
  },
36
16
  "peerDependencies": {
37
- "@nestjs/common": "^10.3.10",
38
- "@nestjs/core": "^10.3.10"
39
- },
40
- "devDependencies": {
41
- "@nestjs/platform-express": "^10.3.10",
42
- "@nestjs/testing": "^10.3.10",
43
- "@types/lodash.head": "^4.0.9",
44
- "filedirname": "^3.4.0",
45
- "rxjs": "^7.8.1",
46
- "supertest": "^7.0.0",
47
- "ts-gems": "^3.4.0"
17
+ "@nestjs/common": "^10.4.1",
18
+ "@nestjs/core": "^10.4.1"
48
19
  },
49
20
  "type": "module",
50
- "module": "./esm/index.js",
21
+ "exports": {
22
+ ".": {
23
+ "import": {
24
+ "types": "./types/index.d.ts",
25
+ "default": "./esm/index.js"
26
+ },
27
+ "require": {
28
+ "types": "./types/index.d.cts",
29
+ "default": "./cjs/index.js"
30
+ },
31
+ "default": "./esm/index.js"
32
+ },
33
+ "./package.json": "./package.json"
34
+ },
51
35
  "main": "./cjs/index.js",
36
+ "module": "./esm/index.js",
52
37
  "types": "./types/index.d.ts",
38
+ "repository": {
39
+ "type": "git",
40
+ "url": "https://github.com/panates/opra.git",
41
+ "directory": "packages/nestjs"
42
+ },
53
43
  "engines": {
54
44
  "node": ">=16.0",
55
45
  "npm": ">=7.0.0"
@@ -66,4 +56,4 @@
66
56
  "opra",
67
57
  "nestjs"
68
58
  ]
69
- }
59
+ }
@@ -1 +1,2 @@
1
- export declare const IS_PUBLIC_KEY = "isPublic";
1
+ export declare const IS_PUBLIC_KEY = "opra:isPublic";
2
+ export declare const OPRA_HTTP_MODULE_OPTIONS = "OPRA_HTTP_MODULE_OPTIONS";
@@ -0,0 +1,6 @@
1
+ import 'reflect-metadata';
2
+ import './augmentation/nestjs.augmentation.js';
3
+ export * from './constants.js';
4
+ export * from './decorators/public.decorator.js';
5
+ export * from './opra-http.module.js';
6
+ export * from './opra-nestjs-adapter.js';
@@ -1,5 +1,5 @@
1
- import { DynamicModule, MiddlewareConsumer, NestModule, OnModuleDestroy } from '@nestjs/common';
2
- import type { OpraHttpModule } from './opra-http.module';
1
+ import { type DynamicModule, type MiddlewareConsumer, type NestModule, type OnModuleDestroy } from '@nestjs/common';
2
+ import type { OpraHttpModule } from './opra-http.module.js';
3
3
  import { OpraNestAdapter } from './opra-nestjs-adapter.js';
4
4
  export declare class OpraHttpCoreModule implements OnModuleDestroy, NestModule {
5
5
  protected opraAdapter: OpraNestAdapter;
@@ -1,5 +1,6 @@
1
- import { DynamicModule } from '@nestjs/common';
1
+ import { type DynamicModule, type Type } from '@nestjs/common';
2
2
  import { ApiDocumentFactory } from '@opra/common';
3
+ import { HttpAdapter } from '@opra/core';
3
4
  export declare namespace OpraHttpModule {
4
5
  interface Initiator extends Pick<DynamicModule, 'imports' | 'providers' | 'exports' | 'controllers'>, Pick<ApiDocumentFactory.InitArguments, 'types' | 'references' | 'info'> {
5
6
  id?: any;
@@ -8,6 +9,7 @@ export declare namespace OpraHttpModule {
8
9
  interface Options {
9
10
  basePath?: string;
10
11
  schemaRouteIsPublic?: boolean;
12
+ interceptors?: (HttpAdapter.InterceptorFunction | HttpAdapter.IHttpInterceptor | Type<HttpAdapter.IHttpInterceptor>)[];
11
13
  }
12
14
  }
13
15
  export declare class OpraHttpModule {
@@ -1,12 +1,12 @@
1
- import { Type } from '@nestjs/common';
1
+ import { type Type } from '@nestjs/common';
2
2
  import { HttpAdapter } from '@opra/core';
3
- import type { OpraHttpModule } from './opra-http.module';
4
- export declare const kHandler: unique symbol;
3
+ import type { OpraHttpModule } from './opra-http.module.js';
5
4
  export declare class OpraNestAdapter extends HttpAdapter {
6
5
  readonly nestControllers: Type[];
7
6
  readonly options?: OpraHttpModule.Options;
8
7
  constructor(init: OpraHttpModule.Initiator, options?: OpraHttpModule.Options);
9
8
  close(): Promise<void>;
10
9
  protected _addRootController(basePath: string): void;
11
- protected _addToNestControllers(sourceClass: Type, currentPath: string): void;
10
+ protected _addToNestControllers(sourceClass: Type, currentPath: string, parentTree: Type[]): void;
11
+ static copyDecoratorMetadataToChild(target: Type, parentTree: Type[]): void;
12
12
  }
@@ -1,8 +1,7 @@
1
- import { ArgumentsHost, ExceptionFilter } from '@nestjs/common';
2
- import { OpraNestAdapter } from '../opra-nestjs-adapter.js';
3
- export declare const kHandler: unique symbol;
4
- export declare class OpraExceptionFilter implements ExceptionFilter {
5
- readonly adapter: OpraNestAdapter;
6
- constructor(adapter: OpraNestAdapter);
7
- catch(exception: any, host: ArgumentsHost): Promise<void>;
1
+ import { type ArgumentsHost } from '@nestjs/common';
2
+ import { BaseExceptionFilter, ModuleRef } from '@nestjs/core';
3
+ export declare class OpraExceptionFilter extends BaseExceptionFilter {
4
+ private moduleRef;
5
+ constructor(moduleRef: ModuleRef);
6
+ catch(exception: any, host: ArgumentsHost): Promise<void> | undefined;
8
7
  }
@@ -1,5 +1,10 @@
1
- import { NestMiddleware } from '@nestjs/common';
1
+ import { type NestMiddleware } from '@nestjs/common';
2
2
  import type { NextFunction, Request, Response } from 'express';
3
+ import type { OpraHttpModule } from '../opra-http.module.js';
4
+ import { OpraNestAdapter } from '../opra-nestjs-adapter.js';
3
5
  export declare class OpraMiddleware implements NestMiddleware {
6
+ protected opraAdapter: OpraNestAdapter;
7
+ protected options: OpraHttpModule.Options;
8
+ constructor(opraAdapter: OpraNestAdapter, options: OpraHttpModule.Options);
4
9
  use(req: Request, res: Response, next: NextFunction): void;
5
10
  }
@@ -1,17 +0,0 @@
1
- "use strict";
2
- // import { ApiDocumentFactory } from '@opra/common';
3
- // import { ExpressAdapter, HttpAdapter } from '@opra/core';
4
- // export interface OpraModuleOptions extends HttpAdapter.Options {
5
- // id?: any;
6
- // document?: Partial<ApiDocumentFactory.InitArguments>;
7
- //
8
- // /**
9
- // * @default true
10
- // */
11
- // useGlobalPrefix?: boolean;
12
- // }
13
- // export interface ExpressModuleOptions extends OpraModuleOptions, HttpAdapter.Options {}
14
- // type OpraModuleOptionsWithoutId = Omit<OpraModuleOptions, 'id'>;
15
- // export interface OpraModuleOptionsFactory {
16
- // createOptions(): Promise<OpraModuleOptionsWithoutId> | OpraModuleOptionsWithoutId;
17
- // }
@@ -1,17 +0,0 @@
1
- "use strict";
2
- // import { ApiDocumentFactory } from '@opra/common';
3
- // import { ExpressAdapter, HttpAdapter } from '@opra/core';
4
- // export interface OpraModuleOptions extends HttpAdapter.Options {
5
- // id?: any;
6
- // document?: Partial<ApiDocumentFactory.InitArguments>;
7
- //
8
- // /**
9
- // * @default true
10
- // */
11
- // useGlobalPrefix?: boolean;
12
- // }
13
- // export interface ExpressModuleOptions extends OpraModuleOptions, HttpAdapter.Options {}
14
- // type OpraModuleOptionsWithoutId = Omit<OpraModuleOptions, 'id'>;
15
- // export interface OpraModuleOptionsFactory {
16
- // createOptions(): Promise<OpraModuleOptionsWithoutId> | OpraModuleOptionsWithoutId;
17
- // }