@nattyjs/core 0.0.1-beta.5 → 0.0.1-beta.50

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/dist/index.cjs CHANGED
@@ -27,6 +27,12 @@ function route(path) {
27
27
  const CONTROLLER = "controller";
28
28
  const INVALID_VALUE = "INVALID_VALUE";
29
29
  const BLANK = "";
30
+ const DENY_BY_DEFAULT = {
31
+ type: "https://cheatsheetseries.owasp.org/cheatsheets/Authorization_Cheat_Sheet.html#deny-by-default",
32
+ title: "Deny-by-default",
33
+ description: `Zero Trust Architecture (NIST SP 800-207) \u2014 Core principle is "never trust, always verify," which translates to deny-by-default behavior. OWASP Secure Defaults Principle \u2014 \u201CSecurity should be a default setting, and everything should be locked down unless explicitly permitted.\u201D`,
34
+ solution: `Please implement proper authentication and authorization checks. If this API needs to be accessed anonymously, add the @anonymous() decorator above the HTTP action.`
35
+ };
30
36
 
31
37
  function get(path) {
32
38
  return function(target, propertyKey, descriptor) {
@@ -60,10 +66,13 @@ const nattyContainer = new class {
60
66
  this.container = /* @__PURE__ */ new Map();
61
67
  this.containerState = /* @__PURE__ */ new Map();
62
68
  }
63
- setup(config, routes, types) {
69
+ get types() {
70
+ return common.commonContainer.types;
71
+ }
72
+ setup(config, routes, resolver) {
64
73
  this.config = config;
65
74
  this.routes = routes;
66
- this.types = types;
75
+ this.resolver = resolver;
67
76
  }
68
77
  getTypes() {
69
78
  return StaticContainer.types;
@@ -113,34 +122,91 @@ const nattyContainer = new class {
113
122
  }
114
123
  }();
115
124
 
125
+ function startWebSchedules(schedules) {
126
+ if (schedules && Array.isArray(schedules)) {
127
+ for (const schedule of schedules)
128
+ startWebSchedule(schedule);
129
+ }
130
+ }
131
+ async function startWebSchedule(config) {
132
+ if (config) {
133
+ const interval = setInterval(async () => {
134
+ try {
135
+ clearInterval(interval);
136
+ await config.scheduleFunction();
137
+ startWebSchedule(config);
138
+ } catch (ex) {
139
+ startWebSchedule(config);
140
+ }
141
+ }, config.interval);
142
+ }
143
+ }
144
+
116
145
  function init(config, appConfig) {
117
146
  common.commonContainer.setupConfig(config);
118
147
  common.commonContainer.setEnvTsDefinition(appConfig.envTsDefinition);
119
- nattyContainer.setup(config, appConfig.routes, appConfig.types);
120
- return initializeModule(config);
148
+ nattyContainer.setup(config, appConfig.routes, appConfig.resolver);
149
+ callLifeCycleEvents(config, true);
150
+ const result = initializeModule(config);
151
+ callLifeCycleEvents(config);
152
+ startWebSchedules(config.webSchedules);
153
+ return result;
121
154
  }
122
155
  function initializeModule(config) {
123
156
  if (config.app) {
124
157
  return config.app.init(config);
125
158
  }
126
159
  }
160
+ async function callLifeCycleEvents(config, isPreInit = false) {
161
+ if (config.lifeCycle) {
162
+ if (config.lifeCycle.preInit && isPreInit) {
163
+ const preInit = config.lifeCycle.preInit();
164
+ if (preInit) {
165
+ if (preInit.cors) {
166
+ if (!config.cors) {
167
+ const jObject = { origin: [] };
168
+ config.cors = jObject;
169
+ } else if (!config.cors.origin)
170
+ config.cors.origin = [];
171
+ if (preInit.cors.origin)
172
+ preInit.cors.origin.forEach((t) => {
173
+ config.cors?.origin.push(t);
174
+ });
175
+ if (preInit.cors.methods)
176
+ config.cors.methods = preInit.cors.methods;
177
+ if (preInit.cors.optionsSuccessStatus)
178
+ config.cors.optionsSuccessStatus = preInit.cors.optionsSuccessStatus;
179
+ if (preInit.cors.preflightContinue)
180
+ config.cors.preflightContinue = preInit.cors.preflightContinue;
181
+ }
182
+ }
183
+ }
184
+ if (config.lifeCycle.onStart) {
185
+ config.lifeCycle.onStart();
186
+ }
187
+ }
188
+ }
127
189
 
128
- function getPreResponseBody(body) {
190
+ function getPreResponseBody(body, isBuffer = false) {
129
191
  let bodyInfo;
130
192
  if (body) {
131
- if (common.isObject(body) || Array.isArray(body))
132
- bodyInfo = { json: body };
133
- const typeText = typeof body;
134
- switch (typeText) {
135
- case "string":
136
- bodyInfo = { string: body };
137
- break;
138
- case "number":
139
- bodyInfo = { number: body };
140
- break;
141
- case "boolean":
142
- bodyInfo = { boolean: body };
143
- break;
193
+ if (isBuffer)
194
+ bodyInfo = { buffer: body };
195
+ else {
196
+ if (common.isObject(body) || Array.isArray(body))
197
+ bodyInfo = { json: body };
198
+ const typeText = typeof body;
199
+ switch (typeText) {
200
+ case "string":
201
+ bodyInfo = { string: body };
202
+ break;
203
+ case "number":
204
+ bodyInfo = { number: body };
205
+ break;
206
+ case "boolean":
207
+ bodyInfo = { boolean: body };
208
+ break;
209
+ }
144
210
  }
145
211
  }
146
212
  return bodyInfo;
@@ -154,6 +220,7 @@ class HttpResponse {
154
220
  }
155
221
  setValues(responseInit) {
156
222
  if (responseInit) {
223
+ this._isBuffer = responseInit.isBuffer;
157
224
  if (responseInit.headers)
158
225
  for (const [key, value] of Object.entries(responseInit.headers))
159
226
  this.headers.append(key, value);
@@ -185,7 +252,7 @@ class HttpResponse {
185
252
  return this._body;
186
253
  }
187
254
  set body(value) {
188
- this._body = getPreResponseBody(value);
255
+ this._body = getPreResponseBody(value, this._isBuffer);
189
256
  }
190
257
  write(responseInit) {
191
258
  this.setValues(responseInit);
@@ -207,6 +274,10 @@ var RequestPipeline = /* @__PURE__ */ ((RequestPipeline2) => {
207
274
  return RequestPipeline2;
208
275
  })(RequestPipeline || {});
209
276
 
277
+ function sanitizeSpecialCodes(value) {
278
+ return value;
279
+ }
280
+
210
281
  function isBoolean(value) {
211
282
  return typeof value === "boolean" || value === "1" || value === "true" || value === "0" || value === "false";
212
283
  }
@@ -267,7 +338,7 @@ function toInt(value, radix = 0) {
267
338
  }
268
339
  function toString(value) {
269
340
  if (isNotBlank(value))
270
- return String(value);
341
+ return sanitizeSpecialCodes(String(value));
271
342
  return value;
272
343
  }
273
344
  function whitelist(value, chars) {
@@ -348,6 +419,7 @@ class BaseResult {
348
419
  }
349
420
 
350
421
  function getResponseBodyObject(body, props) {
422
+ const sensitiveProps = common.commonContainer.nattyConfig?.secure?.sensitiveProps;
351
423
  if (body instanceof common.List)
352
424
  return getResponseBodyObject(body.values, body.props);
353
425
  if (Array.isArray(body)) {
@@ -361,7 +433,8 @@ function getResponseBodyObject(body, props) {
361
433
  const keys = Object.keys(body);
362
434
  const getterProps = props ? Object.keys(props).map((key) => props[key]) : [];
363
435
  for (const key of [...keys, ...getterProps])
364
- jObject[key] = getResponseBodyObject(body[key]);
436
+ if (!sensitiveProps || sensitiveProps.filter((t) => t == key.toLowerCase()).length == 0)
437
+ jObject[key] = getResponseBodyObject(body[key]);
365
438
  return jObject;
366
439
  }
367
440
  return body;
@@ -388,7 +461,7 @@ class BaseResponse {
388
461
  }
389
462
 
390
463
  function getTypedErrorMessage(type, value) {
391
- const message = common.commonContainer.nattyConfig.modelBinding.errorMessage.typed[type];
464
+ const message = common.commonContainer.nattyConfig?.modelBinding?.errorMessage?.typed ? common.commonContainer.nattyConfig?.modelBinding?.errorMessage?.typed[type] : "";
392
465
  return parseMessage(message, [value]);
393
466
  }
394
467
  function parseMessage(message, value) {
@@ -455,7 +528,7 @@ const entityContainer = new class {
455
528
  }
456
529
  getPropertyValidators(entityName, propName) {
457
530
  const entityInfo = this.entityConfig[entityName];
458
- const propertyInfo = entityInfo.properties[propName];
531
+ const propertyInfo = entityInfo ? entityInfo.properties[propName] : void 0;
459
532
  return propertyInfo ? propertyInfo.validators : {};
460
533
  }
461
534
  getProperties(entityName) {
@@ -464,6 +537,32 @@ const entityContainer = new class {
464
537
  }
465
538
  }();
466
539
 
540
+ class ForbiddenAccessException extends HttpException {
541
+ constructor(data) {
542
+ super({
543
+ body: data,
544
+ status: 403
545
+ });
546
+ }
547
+ }
548
+
549
+ class UnauthorizedAccessException extends HttpException {
550
+ constructor(data) {
551
+ super({
552
+ body: data,
553
+ status: 401
554
+ });
555
+ }
556
+ }
557
+
558
+ function CreateProblemDetail(modelName, detail) {
559
+ return {
560
+ type: "https://tools.ietf.org/html/rfc7231#section-6.5.1",
561
+ title: `The specified ${modelName} model props are invalid.`,
562
+ detail
563
+ };
564
+ }
565
+
467
566
  class ParameterTypeConverter extends BaseResponse {
468
567
  constructor() {
469
568
  super(...arguments);
@@ -524,12 +623,14 @@ class ParameterTypeConverter extends BaseResponse {
524
623
  } else {
525
624
  if (this.isArrayType(property.type) && Array.isArray(value)) {
526
625
  let arrayValue = body[property.name] = [];
527
- let arrayInvalidProps = invalidProps[property.name] = [];
528
626
  for (const item of value) {
529
627
  const sanitizeValue = this.sanitizer[property.type.toLowerCase()] ? this.sanitizer[property.type.toLowerCase()](item) : item;
530
- if (sanitizeValue === INVALID_VALUE)
628
+ if (sanitizeValue === INVALID_VALUE) {
629
+ let arrayInvalidProps = invalidProps[property.name];
630
+ if (!arrayInvalidProps)
631
+ arrayInvalidProps = invalidProps[property.name] = [];
531
632
  arrayInvalidProps.push(property);
532
- else
633
+ } else
533
634
  arrayValue.push(sanitizeValue);
534
635
  }
535
636
  } else
@@ -550,14 +651,17 @@ class ParameterTypeConverter extends BaseResponse {
550
651
  for (const parameterInfo of methodInfo.parameters) {
551
652
  const value = jObject[parameterInfo.name];
552
653
  if (value !== void 0) {
553
- jObject[parameterInfo.name] = SANITIZERS[parameterInfo.type](value);
654
+ const sanitizedValue = SANITIZERS[parameterInfo.type](value);
655
+ if (sanitizedValue === null && sanitizedValue != value)
656
+ throw new HttpBadRequestException(CreateProblemDetail(parameterInfo.type, [{ [parameterInfo.name]: `The supplied data type must be "${parameterInfo.type}"` }]));
657
+ jObject[parameterInfo.name] = sanitizedValue;
554
658
  }
555
659
  }
556
660
  return jObject;
557
661
  }
558
662
  convertToInstance(entityName, data) {
559
663
  const typesInfo = this.types[entityName];
560
- const target = this.getClassTarget(typesInfo.path) || entityContainer.getTarget(entityName);
664
+ const target = this.getClassTarget(typesInfo.path, entityName) || entityContainer.getTarget(entityName);
561
665
  let instance = null;
562
666
  if (target) {
563
667
  instance = new target();
@@ -566,11 +670,10 @@ class ParameterTypeConverter extends BaseResponse {
566
670
  instance = data;
567
671
  return instance;
568
672
  }
569
- getClassTarget(resolver) {
570
- if (resolver) {
571
- const classInfo = resolver();
572
- const name = Object.keys(classInfo)[0];
573
- return classInfo[name];
673
+ getClassTarget(path, entityName) {
674
+ if (path) {
675
+ const classInfo = nattyContainer.resolver(path);
676
+ return classInfo[entityName];
574
677
  }
575
678
  return void 0;
576
679
  }
@@ -668,15 +771,6 @@ class RouteParser extends ParameterTypeConverter {
668
771
  }
669
772
  }
670
773
 
671
- class UnauthorizedAccessException extends HttpException {
672
- constructor(data) {
673
- super({
674
- body: data,
675
- status: 401
676
- });
677
- }
678
- }
679
-
680
774
  const decoratorStateContainer = new class {
681
775
  constructor() {
682
776
  this.controllerConfig = {};
@@ -722,15 +816,6 @@ var DecoratorType = /* @__PURE__ */ ((DecoratorType2) => {
722
816
  return DecoratorType2;
723
817
  })(DecoratorType || {});
724
818
 
725
- class ForbiddenAccessException extends HttpException {
726
- constructor(data) {
727
- super({
728
- body: data,
729
- status: 403
730
- });
731
- }
732
- }
733
-
734
819
  class AbstractExecutionContext {
735
820
  constructor(context, routeInfo) {
736
821
  this.context = context;
@@ -760,14 +845,6 @@ class ActionExecutingContext extends AbstractExecutionContext {
760
845
  }
761
846
  }
762
847
 
763
- function CreateProblemDetail(modelName, detail) {
764
- return {
765
- type: "https://tools.ietf.org/html/rfc7231#section-6.5.1",
766
- title: `The specified ${modelName} model props are invalid.`,
767
- detail
768
- };
769
- }
770
-
771
848
  function runValidators(entityName, value) {
772
849
  const typeInfo = nattyContainer.types[entityName];
773
850
  let errors = {};
@@ -829,6 +906,8 @@ class ModelBindingContext extends ParameterTypeConverter {
829
906
  else
830
907
  this.data = body;
831
908
  this.instance = this.convertToInstance(this.type, this.data);
909
+ if (!this.isValid)
910
+ throw new HttpBadRequestException(CreateProblemDetail(this.type, this.errors));
832
911
  }
833
912
  get isValid() {
834
913
  const errors = runValidators(this.type, this.instance);
@@ -845,6 +924,17 @@ class ActionExecutedContext extends AbstractExecutionContext {
845
924
  super(httpContext, routeInfo);
846
925
  this.content = content;
847
926
  }
927
+ get response() {
928
+ return this.context.response;
929
+ }
930
+ }
931
+
932
+ class AuthorizationContext extends AbstractExecutionContext {
933
+ constructor(models, context, routeInfo, config) {
934
+ super(context, routeInfo);
935
+ this.models = models;
936
+ this.config = config;
937
+ }
848
938
  }
849
939
 
850
940
  class RequestProcessor extends RouteParser {
@@ -857,9 +947,6 @@ class RequestProcessor extends RouteParser {
857
947
  case RequestPipeline.onAuthentication:
858
948
  await this.onAuthentication();
859
949
  break;
860
- case RequestPipeline.onAuthorization:
861
- await this.onAuthorization();
862
- break;
863
950
  }
864
951
  }
865
952
  resolveFilter(instance) {
@@ -880,26 +967,34 @@ class RequestProcessor extends RouteParser {
880
967
  const authentication = this.getAuthenticationClass();
881
968
  const authenticationFilter = authentication ? this.resolveFilter(authentication) : void 0;
882
969
  const anonymousInfo = decoratorStateContainer.getInfo(this.routeInfo.controller.name, this.routeInfo.methodInfo.name, DecoratorType.anonymous);
970
+ if (!anonymousInfo.controllerConfig && !anonymousInfo.methodConfig && !authenticationFilter)
971
+ throw new UnauthorizedAccessException(DENY_BY_DEFAULT);
883
972
  if (authenticationFilter) {
884
973
  const result = await authenticationFilter.onAuthentication(this.httpContext);
885
974
  this.httpContext.user = result;
886
975
  if (!result.isAuthenticate && !anonymousInfo.controllerConfig && !anonymousInfo.methodConfig)
887
976
  throw new UnauthorizedAccessException(authenticationFilter.onFailedResponse());
888
- await this.onAuthorization();
889
977
  }
890
978
  }
891
- async onAuthorization() {
979
+ async onAuthorization(methodParameters) {
892
980
  const authorization = common.commonContainer.globalConfig.authorization;
893
981
  const authorizationFilter = authorization ? this.resolveFilter(authorization) : void 0;
894
982
  const authorizeConfig = decoratorStateContainer.getInfo(this.routeInfo.controller.name, this.routeInfo.methodInfo.name, DecoratorType.authorize);
895
983
  const authenticationOnly = decoratorStateContainer.getInfo(this.routeInfo.controller.name, this.routeInfo.methodInfo.name, DecoratorType.authenticationOnly);
896
- if (this.httpContext.user?.isAuthenticate && authorizationFilter && (!authenticationOnly.controllerConfig && !authenticationOnly.methodConfig)) {
897
- const result = await authorizationFilter.onAuthorization(this.httpContext, authorizeConfig.methodConfig || authorizeConfig.controllerConfig);
984
+ if (this.httpContext.user?.isAuthenticate && authorizationFilter && (authorizeConfig.controllerConfig || authorizeConfig.methodConfig) && (!authenticationOnly.controllerConfig && !authenticationOnly.methodConfig)) {
985
+ const authorizationContext = new AuthorizationContext(
986
+ methodParameters.filter((t) => t instanceof ModelBindingContext),
987
+ this.httpContext,
988
+ this.routeInfo,
989
+ authorizeConfig.methodConfig || authorizeConfig.controllerConfig
990
+ );
991
+ const result = await authorizationFilter.onAuthorization(authorizationContext);
898
992
  if (!result)
899
993
  throw new ForbiddenAccessException(authorizationFilter.onFailedAuthorization());
900
994
  }
901
995
  }
902
996
  async onActionExecuting(methodParameters) {
997
+ await this.onAuthorization(methodParameters);
903
998
  let actionFilters = common.commonContainer.globalConfig.actionFilters || [];
904
999
  const actionFiltersConfig = decoratorStateContainer.getInfo(this.routeInfo.controller.name, this.routeInfo.methodInfo.name, DecoratorType.useFilter);
905
1000
  actionFilters = [...actionFilters, ...actionFiltersConfig.controllerConfig?.actionFilters || [], ...actionFiltersConfig.methodConfig?.actionFilters || []];
@@ -1205,6 +1300,16 @@ function badRequest(value, response) {
1205
1300
  return new BadRequestResult(value || common.BLANK, response);
1206
1301
  }
1207
1302
 
1303
+ class FileResult extends BaseResult {
1304
+ constructor(value, response) {
1305
+ super({ ...{ isBuffer: true, body: value }, ...{ status: HttpStatusCode.success } }, response);
1306
+ this.value = value;
1307
+ }
1308
+ }
1309
+ function fileResult(value, response) {
1310
+ return new FileResult(value || common.BLANK, response);
1311
+ }
1312
+
1208
1313
  function base(params, type, additionaConfig) {
1209
1314
  decoratorStateContainer.register(params, type, additionaConfig);
1210
1315
  }
@@ -1231,12 +1336,31 @@ function authenticationOnly() {
1231
1336
  };
1232
1337
  }
1233
1338
 
1339
+ function setEnvInfo(envTsDefinition, envValueInfo) {
1340
+ if (envTsDefinition && envValueInfo) {
1341
+ common.commonContainer.setEnvTsDefinition(envTsDefinition);
1342
+ Object.keys(envValueInfo).forEach((key) => process.env[key] = envValueInfo[key]);
1343
+ }
1344
+ }
1345
+
1346
+ function authorize(permission) {
1347
+ return function(target, propertyKey, descriptor) {
1348
+ base({
1349
+ target,
1350
+ propertyKey,
1351
+ descriptor
1352
+ }, DecoratorType.authorize, permission);
1353
+ };
1354
+ }
1355
+
1234
1356
  exports.$request = $request;
1235
1357
  exports.AbstractModelState = AbstractModelState;
1236
1358
  exports.BadRequestResult = BadRequestResult;
1237
1359
  exports.BaseController = BaseController;
1360
+ exports.CreateProblemDetail = CreateProblemDetail;
1238
1361
  exports.CreatedResult = CreatedResult;
1239
1362
  exports.Delete = Delete;
1363
+ exports.FileResult = FileResult;
1240
1364
  exports.ForbiddenAccessException = ForbiddenAccessException;
1241
1365
  exports.ForbiddenAccessInfoResult = ForbiddenAccessInfoResult;
1242
1366
  exports.HttpBadRequestException = HttpBadRequestException;
@@ -1245,6 +1369,7 @@ exports.HttpException = HttpException;
1245
1369
  exports.HttpHandler = HttpHandler;
1246
1370
  exports.HttpNotFoundException = HttpNotFoundException;
1247
1371
  exports.HttpResponse = HttpResponse;
1372
+ exports.HttpStatusCode = HttpStatusCode;
1248
1373
  exports.ModelBindingContext = ModelBindingContext;
1249
1374
  exports.NoContentResult = NoContentResult;
1250
1375
  exports.NotFoundResult = NotFoundResult;
@@ -1253,10 +1378,12 @@ exports.RunOn = RunOn;
1253
1378
  exports.UnauthorizedAccessException = UnauthorizedAccessException;
1254
1379
  exports.anonymous = anonymous;
1255
1380
  exports.authenticationOnly = authenticationOnly;
1381
+ exports.authorize = authorize;
1256
1382
  exports.badRequest = badRequest;
1257
1383
  exports.created = created;
1258
1384
  exports.defineNattyConfig = defineNattyConfig;
1259
1385
  exports.entityContainer = entityContainer;
1386
+ exports.fileResult = fileResult;
1260
1387
  exports.filter = filter;
1261
1388
  exports.forbiddenAccessInfo = forbiddenAccessInfo;
1262
1389
  exports.get = get;
@@ -1269,4 +1396,5 @@ exports.post = post;
1269
1396
  exports.put = put;
1270
1397
  exports.registerDecorator = registerDecorator;
1271
1398
  exports.route = route;
1399
+ exports.setEnvInfo = setEnvInfo;
1272
1400
  exports.useFilter = useFilter;
package/dist/index.d.ts CHANGED
@@ -53,12 +53,70 @@ declare function route(path: string): (target: any, propertyKey?: string, parame
53
53
 
54
54
  declare function get(path: string): (target: any, propertyKey?: string, descriptor?: any) => void;
55
55
 
56
- declare function post(path: string): (target: any, propertyKey?: string, descriptor?: any) => void;
56
+ declare function post(path?: string): (target: any, propertyKey?: string, descriptor?: any) => void;
57
57
 
58
- declare function put(path: string): (target: any, propertyKey?: string, descriptor?: any) => void;
58
+ declare function put(path?: string): (target: any, propertyKey?: string, descriptor?: any) => void;
59
59
 
60
60
  declare function Delete(): (target: any, propertyKey?: string, descriptor?: any) => void;
61
61
 
62
+ interface RouteConfig {
63
+ controller: Function;
64
+ parameters: Array<{
65
+ name: string;
66
+ type: string;
67
+ }>;
68
+ get: {
69
+ [key: string]: {
70
+ name: string;
71
+ parameters: Array<{
72
+ name: string;
73
+ type: string;
74
+ }>;
75
+ returnType: string;
76
+ };
77
+ };
78
+ post: {
79
+ [key: string]: {
80
+ name: string;
81
+ parameters: Array<{
82
+ name: string;
83
+ type: string;
84
+ }>;
85
+ returnType: string;
86
+ };
87
+ };
88
+ put: {
89
+ [key: string]: {
90
+ name: string;
91
+ parameters: Array<{
92
+ name: string;
93
+ type: string;
94
+ }>;
95
+ returnType: string;
96
+ };
97
+ };
98
+ delete: {
99
+ [key: string]: {
100
+ name: string;
101
+ parameters: Array<{
102
+ name: string;
103
+ type: string;
104
+ }>;
105
+ returnType: string;
106
+ };
107
+ };
108
+ }
109
+
110
+ declare function init(config: NattyConfig, appConfig: {
111
+ routes: {
112
+ [key: string]: RouteConfig;
113
+ };
114
+ envTsDefinition: {
115
+ [key: string]: string;
116
+ };
117
+ resolver: (path: string) => {};
118
+ }): any;
119
+
62
120
  interface DecoratorInfo {
63
121
  httpMethod: string;
64
122
  route: string;
@@ -81,6 +139,7 @@ type HttpRequestBodyInfo = {
81
139
  number?: number | number[];
82
140
  boolean?: boolean | boolean[];
83
141
  FormData?: FormData;
142
+ buffer?: any;
84
143
  ArrayBuffer?: ArrayBuffer;
85
144
  Blob?: Blob;
86
145
  json?: {
@@ -120,6 +179,7 @@ interface HttpResponseInit {
120
179
  headers?: HeadersInit;
121
180
  cookies?: Cookie[];
122
181
  enableContentNegotiation?: boolean;
182
+ isBuffer?: boolean;
123
183
  }
124
184
 
125
185
  interface ProblemDetail {
@@ -190,64 +250,6 @@ interface IHttpResult {
190
250
  getResponse(): HttpResponseInit;
191
251
  }
192
252
 
193
- interface RouteConfig {
194
- controller: Function;
195
- parameters: Array<{
196
- name: string;
197
- type: string;
198
- }>;
199
- get: {
200
- [key: string]: {
201
- name: string;
202
- parameters: Array<{
203
- name: string;
204
- type: string;
205
- }>;
206
- returnType: string;
207
- };
208
- };
209
- post: {
210
- [key: string]: {
211
- name: string;
212
- parameters: Array<{
213
- name: string;
214
- type: string;
215
- }>;
216
- returnType: string;
217
- };
218
- };
219
- put: {
220
- [key: string]: {
221
- name: string;
222
- parameters: Array<{
223
- name: string;
224
- type: string;
225
- }>;
226
- returnType: string;
227
- };
228
- };
229
- delete: {
230
- [key: string]: {
231
- name: string;
232
- parameters: Array<{
233
- name: string;
234
- type: string;
235
- }>;
236
- returnType: string;
237
- };
238
- };
239
- }
240
-
241
- declare function init(config: NattyConfig, appConfig: {
242
- routes: {
243
- [key: string]: RouteConfig;
244
- };
245
- envTsDefinition: {
246
- [key: string]: string;
247
- };
248
- types?: TypesInfo;
249
- }): any;
250
-
251
253
  declare class HttpRequest {
252
254
  private httpRequest;
253
255
  constructor(http: HttpRequestInit);
@@ -264,6 +266,7 @@ declare class HttpResponse {
264
266
  private _headers;
265
267
  private _body;
266
268
  private _status;
269
+ private _isBuffer;
267
270
  constructor(response?: HttpResponseInit);
268
271
  private setValues;
269
272
  addCookie(cookie: Cookie): void;
@@ -341,9 +344,9 @@ declare abstract class ParameterTypeConverter extends BaseResponse {
341
344
  };
342
345
  get types(): TypesInfo;
343
346
  getObjectTypeInfo(typeName: string): {
344
- path?: any;
345
- props?: TypeInfo[];
346
- values?: any[];
347
+ path?: string | any | Function;
348
+ props?: Array<TypeInfo>;
349
+ values?: Array<any>;
347
350
  };
348
351
  isArrayType(typeName: string): boolean;
349
352
  getTypeName(typeName: string): string;
@@ -435,6 +438,12 @@ declare class BadRequestResult extends BaseResult implements IHttpResult {
435
438
  }
436
439
  declare function badRequest(value?: ExceptionTypeInfo, response?: Pick<HttpResponseInit, "headers" | "cookies">): BadRequestResult;
437
440
 
441
+ declare class FileResult extends BaseResult {
442
+ value: any;
443
+ constructor(value: any, response?: Pick<HttpResponseInit, "headers" | "cookies">);
444
+ }
445
+ declare function fileResult(value?: any, response?: Pick<HttpResponseInit, "headers" | "cookies">): FileResult;
446
+
438
447
  declare class HttpException {
439
448
  private httpResponse;
440
449
  constructor(response: HttpResponseInit);
@@ -463,4 +472,27 @@ declare function anonymous(): (target: any, propertyKey?: string, descriptor?: a
463
472
 
464
473
  declare function authenticationOnly(): (target: any, propertyKey?: string, descriptor?: any) => void;
465
474
 
466
- export { $request, AbstractModelState, BadRequestResult, BaseController, BuildOptions, ClassTypeInfo, CreatedResult, Delete, ForbiddenAccessException, ForbiddenAccessInfoResult, HttpBadRequestException, HttpContext, HttpException, HttpHandler, HttpModule, HttpNotFoundException, HttpResponse, MethodInfo$1 as MethodInfo, ModelBindingContext, NoContentResult, NotFoundResult, OkResult, ParameterInfo, RunOn, TypeInfo$1 as TypeInfo, UnauthorizedAccessException, anonymous, authenticationOnly, badRequest, created, defineNattyConfig, entityContainer, filter, forbiddenAccessInfo, get, init, injectable, noContent, notFound, ok, post, put, registerDecorator, route, useFilter };
475
+ declare function setEnvInfo(envTsDefinition: {
476
+ [key: string]: string;
477
+ }, envValueInfo: {
478
+ [key: string]: any;
479
+ }): void;
480
+
481
+ declare enum HttpStatusCode {
482
+ success = 200,
483
+ created = 201,
484
+ noContent = 204,
485
+ notFound = 404,
486
+ unAuthorized = 401,
487
+ forbiddenAccess = 403,
488
+ badRequest = 400,
489
+ serverError = 500
490
+ }
491
+
492
+ declare function authorize(permission: {
493
+ [key: string]: any;
494
+ }): (target: any, propertyKey?: string, descriptor?: any) => void;
495
+
496
+ declare function CreateProblemDetail(modelName: string, detail: any): ProblemDetail;
497
+
498
+ export { $request, AbstractModelState, BadRequestResult, BaseController, BuildOptions, ClassTypeInfo, CreateProblemDetail, CreatedResult, Delete, FileResult, ForbiddenAccessException, ForbiddenAccessInfoResult, HttpBadRequestException, HttpContext, HttpException, HttpHandler, HttpModule, HttpNotFoundException, HttpResponse, HttpStatusCode, MethodInfo$1 as MethodInfo, ModelBindingContext, NoContentResult, NotFoundResult, OkResult, ParameterInfo, RunOn, TypeInfo$1 as TypeInfo, UnauthorizedAccessException, anonymous, authenticationOnly, authorize, badRequest, created, defineNattyConfig, entityContainer, fileResult, filter, forbiddenAccessInfo, get, init, injectable, noContent, notFound, ok, post, put, registerDecorator, route, setEnvInfo, useFilter };
package/dist/index.mjs CHANGED
@@ -25,6 +25,12 @@ function route(path) {
25
25
  const CONTROLLER = "controller";
26
26
  const INVALID_VALUE = "INVALID_VALUE";
27
27
  const BLANK = "";
28
+ const DENY_BY_DEFAULT = {
29
+ type: "https://cheatsheetseries.owasp.org/cheatsheets/Authorization_Cheat_Sheet.html#deny-by-default",
30
+ title: "Deny-by-default",
31
+ description: `Zero Trust Architecture (NIST SP 800-207) \u2014 Core principle is "never trust, always verify," which translates to deny-by-default behavior. OWASP Secure Defaults Principle \u2014 \u201CSecurity should be a default setting, and everything should be locked down unless explicitly permitted.\u201D`,
32
+ solution: `Please implement proper authentication and authorization checks. If this API needs to be accessed anonymously, add the @anonymous() decorator above the HTTP action.`
33
+ };
28
34
 
29
35
  function get(path) {
30
36
  return function(target, propertyKey, descriptor) {
@@ -58,10 +64,13 @@ const nattyContainer = new class {
58
64
  this.container = /* @__PURE__ */ new Map();
59
65
  this.containerState = /* @__PURE__ */ new Map();
60
66
  }
61
- setup(config, routes, types) {
67
+ get types() {
68
+ return commonContainer.types;
69
+ }
70
+ setup(config, routes, resolver) {
62
71
  this.config = config;
63
72
  this.routes = routes;
64
- this.types = types;
73
+ this.resolver = resolver;
65
74
  }
66
75
  getTypes() {
67
76
  return StaticContainer.types;
@@ -111,34 +120,91 @@ const nattyContainer = new class {
111
120
  }
112
121
  }();
113
122
 
123
+ function startWebSchedules(schedules) {
124
+ if (schedules && Array.isArray(schedules)) {
125
+ for (const schedule of schedules)
126
+ startWebSchedule(schedule);
127
+ }
128
+ }
129
+ async function startWebSchedule(config) {
130
+ if (config) {
131
+ const interval = setInterval(async () => {
132
+ try {
133
+ clearInterval(interval);
134
+ await config.scheduleFunction();
135
+ startWebSchedule(config);
136
+ } catch (ex) {
137
+ startWebSchedule(config);
138
+ }
139
+ }, config.interval);
140
+ }
141
+ }
142
+
114
143
  function init(config, appConfig) {
115
144
  commonContainer.setupConfig(config);
116
145
  commonContainer.setEnvTsDefinition(appConfig.envTsDefinition);
117
- nattyContainer.setup(config, appConfig.routes, appConfig.types);
118
- return initializeModule(config);
146
+ nattyContainer.setup(config, appConfig.routes, appConfig.resolver);
147
+ callLifeCycleEvents(config, true);
148
+ const result = initializeModule(config);
149
+ callLifeCycleEvents(config);
150
+ startWebSchedules(config.webSchedules);
151
+ return result;
119
152
  }
120
153
  function initializeModule(config) {
121
154
  if (config.app) {
122
155
  return config.app.init(config);
123
156
  }
124
157
  }
158
+ async function callLifeCycleEvents(config, isPreInit = false) {
159
+ if (config.lifeCycle) {
160
+ if (config.lifeCycle.preInit && isPreInit) {
161
+ const preInit = config.lifeCycle.preInit();
162
+ if (preInit) {
163
+ if (preInit.cors) {
164
+ if (!config.cors) {
165
+ const jObject = { origin: [] };
166
+ config.cors = jObject;
167
+ } else if (!config.cors.origin)
168
+ config.cors.origin = [];
169
+ if (preInit.cors.origin)
170
+ preInit.cors.origin.forEach((t) => {
171
+ config.cors?.origin.push(t);
172
+ });
173
+ if (preInit.cors.methods)
174
+ config.cors.methods = preInit.cors.methods;
175
+ if (preInit.cors.optionsSuccessStatus)
176
+ config.cors.optionsSuccessStatus = preInit.cors.optionsSuccessStatus;
177
+ if (preInit.cors.preflightContinue)
178
+ config.cors.preflightContinue = preInit.cors.preflightContinue;
179
+ }
180
+ }
181
+ }
182
+ if (config.lifeCycle.onStart) {
183
+ config.lifeCycle.onStart();
184
+ }
185
+ }
186
+ }
125
187
 
126
- function getPreResponseBody(body) {
188
+ function getPreResponseBody(body, isBuffer = false) {
127
189
  let bodyInfo;
128
190
  if (body) {
129
- if (isObject(body) || Array.isArray(body))
130
- bodyInfo = { json: body };
131
- const typeText = typeof body;
132
- switch (typeText) {
133
- case "string":
134
- bodyInfo = { string: body };
135
- break;
136
- case "number":
137
- bodyInfo = { number: body };
138
- break;
139
- case "boolean":
140
- bodyInfo = { boolean: body };
141
- break;
191
+ if (isBuffer)
192
+ bodyInfo = { buffer: body };
193
+ else {
194
+ if (isObject(body) || Array.isArray(body))
195
+ bodyInfo = { json: body };
196
+ const typeText = typeof body;
197
+ switch (typeText) {
198
+ case "string":
199
+ bodyInfo = { string: body };
200
+ break;
201
+ case "number":
202
+ bodyInfo = { number: body };
203
+ break;
204
+ case "boolean":
205
+ bodyInfo = { boolean: body };
206
+ break;
207
+ }
142
208
  }
143
209
  }
144
210
  return bodyInfo;
@@ -152,6 +218,7 @@ class HttpResponse {
152
218
  }
153
219
  setValues(responseInit) {
154
220
  if (responseInit) {
221
+ this._isBuffer = responseInit.isBuffer;
155
222
  if (responseInit.headers)
156
223
  for (const [key, value] of Object.entries(responseInit.headers))
157
224
  this.headers.append(key, value);
@@ -183,7 +250,7 @@ class HttpResponse {
183
250
  return this._body;
184
251
  }
185
252
  set body(value) {
186
- this._body = getPreResponseBody(value);
253
+ this._body = getPreResponseBody(value, this._isBuffer);
187
254
  }
188
255
  write(responseInit) {
189
256
  this.setValues(responseInit);
@@ -205,6 +272,10 @@ var RequestPipeline = /* @__PURE__ */ ((RequestPipeline2) => {
205
272
  return RequestPipeline2;
206
273
  })(RequestPipeline || {});
207
274
 
275
+ function sanitizeSpecialCodes(value) {
276
+ return value;
277
+ }
278
+
208
279
  function isBoolean(value) {
209
280
  return typeof value === "boolean" || value === "1" || value === "true" || value === "0" || value === "false";
210
281
  }
@@ -265,7 +336,7 @@ function toInt(value, radix = 0) {
265
336
  }
266
337
  function toString(value) {
267
338
  if (isNotBlank(value))
268
- return String(value);
339
+ return sanitizeSpecialCodes(String(value));
269
340
  return value;
270
341
  }
271
342
  function whitelist(value, chars) {
@@ -346,6 +417,7 @@ class BaseResult {
346
417
  }
347
418
 
348
419
  function getResponseBodyObject(body, props) {
420
+ const sensitiveProps = commonContainer.nattyConfig?.secure?.sensitiveProps;
349
421
  if (body instanceof List)
350
422
  return getResponseBodyObject(body.values, body.props);
351
423
  if (Array.isArray(body)) {
@@ -359,7 +431,8 @@ function getResponseBodyObject(body, props) {
359
431
  const keys = Object.keys(body);
360
432
  const getterProps = props ? Object.keys(props).map((key) => props[key]) : [];
361
433
  for (const key of [...keys, ...getterProps])
362
- jObject[key] = getResponseBodyObject(body[key]);
434
+ if (!sensitiveProps || sensitiveProps.filter((t) => t == key.toLowerCase()).length == 0)
435
+ jObject[key] = getResponseBodyObject(body[key]);
363
436
  return jObject;
364
437
  }
365
438
  return body;
@@ -386,7 +459,7 @@ class BaseResponse {
386
459
  }
387
460
 
388
461
  function getTypedErrorMessage(type, value) {
389
- const message = commonContainer.nattyConfig.modelBinding.errorMessage.typed[type];
462
+ const message = commonContainer.nattyConfig?.modelBinding?.errorMessage?.typed ? commonContainer.nattyConfig?.modelBinding?.errorMessage?.typed[type] : "";
390
463
  return parseMessage(message, [value]);
391
464
  }
392
465
  function parseMessage(message, value) {
@@ -453,7 +526,7 @@ const entityContainer = new class {
453
526
  }
454
527
  getPropertyValidators(entityName, propName) {
455
528
  const entityInfo = this.entityConfig[entityName];
456
- const propertyInfo = entityInfo.properties[propName];
529
+ const propertyInfo = entityInfo ? entityInfo.properties[propName] : void 0;
457
530
  return propertyInfo ? propertyInfo.validators : {};
458
531
  }
459
532
  getProperties(entityName) {
@@ -462,6 +535,32 @@ const entityContainer = new class {
462
535
  }
463
536
  }();
464
537
 
538
+ class ForbiddenAccessException extends HttpException {
539
+ constructor(data) {
540
+ super({
541
+ body: data,
542
+ status: 403
543
+ });
544
+ }
545
+ }
546
+
547
+ class UnauthorizedAccessException extends HttpException {
548
+ constructor(data) {
549
+ super({
550
+ body: data,
551
+ status: 401
552
+ });
553
+ }
554
+ }
555
+
556
+ function CreateProblemDetail(modelName, detail) {
557
+ return {
558
+ type: "https://tools.ietf.org/html/rfc7231#section-6.5.1",
559
+ title: `The specified ${modelName} model props are invalid.`,
560
+ detail
561
+ };
562
+ }
563
+
465
564
  class ParameterTypeConverter extends BaseResponse {
466
565
  constructor() {
467
566
  super(...arguments);
@@ -522,12 +621,14 @@ class ParameterTypeConverter extends BaseResponse {
522
621
  } else {
523
622
  if (this.isArrayType(property.type) && Array.isArray(value)) {
524
623
  let arrayValue = body[property.name] = [];
525
- let arrayInvalidProps = invalidProps[property.name] = [];
526
624
  for (const item of value) {
527
625
  const sanitizeValue = this.sanitizer[property.type.toLowerCase()] ? this.sanitizer[property.type.toLowerCase()](item) : item;
528
- if (sanitizeValue === INVALID_VALUE)
626
+ if (sanitizeValue === INVALID_VALUE) {
627
+ let arrayInvalidProps = invalidProps[property.name];
628
+ if (!arrayInvalidProps)
629
+ arrayInvalidProps = invalidProps[property.name] = [];
529
630
  arrayInvalidProps.push(property);
530
- else
631
+ } else
531
632
  arrayValue.push(sanitizeValue);
532
633
  }
533
634
  } else
@@ -548,14 +649,17 @@ class ParameterTypeConverter extends BaseResponse {
548
649
  for (const parameterInfo of methodInfo.parameters) {
549
650
  const value = jObject[parameterInfo.name];
550
651
  if (value !== void 0) {
551
- jObject[parameterInfo.name] = SANITIZERS[parameterInfo.type](value);
652
+ const sanitizedValue = SANITIZERS[parameterInfo.type](value);
653
+ if (sanitizedValue === null && sanitizedValue != value)
654
+ throw new HttpBadRequestException(CreateProblemDetail(parameterInfo.type, [{ [parameterInfo.name]: `The supplied data type must be "${parameterInfo.type}"` }]));
655
+ jObject[parameterInfo.name] = sanitizedValue;
552
656
  }
553
657
  }
554
658
  return jObject;
555
659
  }
556
660
  convertToInstance(entityName, data) {
557
661
  const typesInfo = this.types[entityName];
558
- const target = this.getClassTarget(typesInfo.path) || entityContainer.getTarget(entityName);
662
+ const target = this.getClassTarget(typesInfo.path, entityName) || entityContainer.getTarget(entityName);
559
663
  let instance = null;
560
664
  if (target) {
561
665
  instance = new target();
@@ -564,11 +668,10 @@ class ParameterTypeConverter extends BaseResponse {
564
668
  instance = data;
565
669
  return instance;
566
670
  }
567
- getClassTarget(resolver) {
568
- if (resolver) {
569
- const classInfo = resolver();
570
- const name = Object.keys(classInfo)[0];
571
- return classInfo[name];
671
+ getClassTarget(path, entityName) {
672
+ if (path) {
673
+ const classInfo = nattyContainer.resolver(path);
674
+ return classInfo[entityName];
572
675
  }
573
676
  return void 0;
574
677
  }
@@ -666,15 +769,6 @@ class RouteParser extends ParameterTypeConverter {
666
769
  }
667
770
  }
668
771
 
669
- class UnauthorizedAccessException extends HttpException {
670
- constructor(data) {
671
- super({
672
- body: data,
673
- status: 401
674
- });
675
- }
676
- }
677
-
678
772
  const decoratorStateContainer = new class {
679
773
  constructor() {
680
774
  this.controllerConfig = {};
@@ -720,15 +814,6 @@ var DecoratorType = /* @__PURE__ */ ((DecoratorType2) => {
720
814
  return DecoratorType2;
721
815
  })(DecoratorType || {});
722
816
 
723
- class ForbiddenAccessException extends HttpException {
724
- constructor(data) {
725
- super({
726
- body: data,
727
- status: 403
728
- });
729
- }
730
- }
731
-
732
817
  class AbstractExecutionContext {
733
818
  constructor(context, routeInfo) {
734
819
  this.context = context;
@@ -758,14 +843,6 @@ class ActionExecutingContext extends AbstractExecutionContext {
758
843
  }
759
844
  }
760
845
 
761
- function CreateProblemDetail(modelName, detail) {
762
- return {
763
- type: "https://tools.ietf.org/html/rfc7231#section-6.5.1",
764
- title: `The specified ${modelName} model props are invalid.`,
765
- detail
766
- };
767
- }
768
-
769
846
  function runValidators(entityName, value) {
770
847
  const typeInfo = nattyContainer.types[entityName];
771
848
  let errors = {};
@@ -827,6 +904,8 @@ class ModelBindingContext extends ParameterTypeConverter {
827
904
  else
828
905
  this.data = body;
829
906
  this.instance = this.convertToInstance(this.type, this.data);
907
+ if (!this.isValid)
908
+ throw new HttpBadRequestException(CreateProblemDetail(this.type, this.errors));
830
909
  }
831
910
  get isValid() {
832
911
  const errors = runValidators(this.type, this.instance);
@@ -843,6 +922,17 @@ class ActionExecutedContext extends AbstractExecutionContext {
843
922
  super(httpContext, routeInfo);
844
923
  this.content = content;
845
924
  }
925
+ get response() {
926
+ return this.context.response;
927
+ }
928
+ }
929
+
930
+ class AuthorizationContext extends AbstractExecutionContext {
931
+ constructor(models, context, routeInfo, config) {
932
+ super(context, routeInfo);
933
+ this.models = models;
934
+ this.config = config;
935
+ }
846
936
  }
847
937
 
848
938
  class RequestProcessor extends RouteParser {
@@ -855,9 +945,6 @@ class RequestProcessor extends RouteParser {
855
945
  case RequestPipeline.onAuthentication:
856
946
  await this.onAuthentication();
857
947
  break;
858
- case RequestPipeline.onAuthorization:
859
- await this.onAuthorization();
860
- break;
861
948
  }
862
949
  }
863
950
  resolveFilter(instance) {
@@ -878,26 +965,34 @@ class RequestProcessor extends RouteParser {
878
965
  const authentication = this.getAuthenticationClass();
879
966
  const authenticationFilter = authentication ? this.resolveFilter(authentication) : void 0;
880
967
  const anonymousInfo = decoratorStateContainer.getInfo(this.routeInfo.controller.name, this.routeInfo.methodInfo.name, DecoratorType.anonymous);
968
+ if (!anonymousInfo.controllerConfig && !anonymousInfo.methodConfig && !authenticationFilter)
969
+ throw new UnauthorizedAccessException(DENY_BY_DEFAULT);
881
970
  if (authenticationFilter) {
882
971
  const result = await authenticationFilter.onAuthentication(this.httpContext);
883
972
  this.httpContext.user = result;
884
973
  if (!result.isAuthenticate && !anonymousInfo.controllerConfig && !anonymousInfo.methodConfig)
885
974
  throw new UnauthorizedAccessException(authenticationFilter.onFailedResponse());
886
- await this.onAuthorization();
887
975
  }
888
976
  }
889
- async onAuthorization() {
977
+ async onAuthorization(methodParameters) {
890
978
  const authorization = commonContainer.globalConfig.authorization;
891
979
  const authorizationFilter = authorization ? this.resolveFilter(authorization) : void 0;
892
980
  const authorizeConfig = decoratorStateContainer.getInfo(this.routeInfo.controller.name, this.routeInfo.methodInfo.name, DecoratorType.authorize);
893
981
  const authenticationOnly = decoratorStateContainer.getInfo(this.routeInfo.controller.name, this.routeInfo.methodInfo.name, DecoratorType.authenticationOnly);
894
- if (this.httpContext.user?.isAuthenticate && authorizationFilter && (!authenticationOnly.controllerConfig && !authenticationOnly.methodConfig)) {
895
- const result = await authorizationFilter.onAuthorization(this.httpContext, authorizeConfig.methodConfig || authorizeConfig.controllerConfig);
982
+ if (this.httpContext.user?.isAuthenticate && authorizationFilter && (authorizeConfig.controllerConfig || authorizeConfig.methodConfig) && (!authenticationOnly.controllerConfig && !authenticationOnly.methodConfig)) {
983
+ const authorizationContext = new AuthorizationContext(
984
+ methodParameters.filter((t) => t instanceof ModelBindingContext),
985
+ this.httpContext,
986
+ this.routeInfo,
987
+ authorizeConfig.methodConfig || authorizeConfig.controllerConfig
988
+ );
989
+ const result = await authorizationFilter.onAuthorization(authorizationContext);
896
990
  if (!result)
897
991
  throw new ForbiddenAccessException(authorizationFilter.onFailedAuthorization());
898
992
  }
899
993
  }
900
994
  async onActionExecuting(methodParameters) {
995
+ await this.onAuthorization(methodParameters);
901
996
  let actionFilters = commonContainer.globalConfig.actionFilters || [];
902
997
  const actionFiltersConfig = decoratorStateContainer.getInfo(this.routeInfo.controller.name, this.routeInfo.methodInfo.name, DecoratorType.useFilter);
903
998
  actionFilters = [...actionFilters, ...actionFiltersConfig.controllerConfig?.actionFilters || [], ...actionFiltersConfig.methodConfig?.actionFilters || []];
@@ -1203,6 +1298,16 @@ function badRequest(value, response) {
1203
1298
  return new BadRequestResult(value || BLANK$1, response);
1204
1299
  }
1205
1300
 
1301
+ class FileResult extends BaseResult {
1302
+ constructor(value, response) {
1303
+ super({ ...{ isBuffer: true, body: value }, ...{ status: HttpStatusCode.success } }, response);
1304
+ this.value = value;
1305
+ }
1306
+ }
1307
+ function fileResult(value, response) {
1308
+ return new FileResult(value || BLANK$1, response);
1309
+ }
1310
+
1206
1311
  function base(params, type, additionaConfig) {
1207
1312
  decoratorStateContainer.register(params, type, additionaConfig);
1208
1313
  }
@@ -1229,4 +1334,21 @@ function authenticationOnly() {
1229
1334
  };
1230
1335
  }
1231
1336
 
1232
- export { $request, AbstractModelState, BadRequestResult, BaseController, CreatedResult, Delete, ForbiddenAccessException, ForbiddenAccessInfoResult, HttpBadRequestException, HttpContext, HttpException, HttpHandler, HttpNotFoundException, HttpResponse, ModelBindingContext, NoContentResult, NotFoundResult, OkResult, RunOn, UnauthorizedAccessException, anonymous, authenticationOnly, badRequest, created, defineNattyConfig, entityContainer, filter, forbiddenAccessInfo, get, init, injectable, noContent, notFound, ok, post, put, registerDecorator, route, useFilter };
1337
+ function setEnvInfo(envTsDefinition, envValueInfo) {
1338
+ if (envTsDefinition && envValueInfo) {
1339
+ commonContainer.setEnvTsDefinition(envTsDefinition);
1340
+ Object.keys(envValueInfo).forEach((key) => process.env[key] = envValueInfo[key]);
1341
+ }
1342
+ }
1343
+
1344
+ function authorize(permission) {
1345
+ return function(target, propertyKey, descriptor) {
1346
+ base({
1347
+ target,
1348
+ propertyKey,
1349
+ descriptor
1350
+ }, DecoratorType.authorize, permission);
1351
+ };
1352
+ }
1353
+
1354
+ export { $request, AbstractModelState, BadRequestResult, BaseController, CreateProblemDetail, CreatedResult, Delete, FileResult, ForbiddenAccessException, ForbiddenAccessInfoResult, HttpBadRequestException, HttpContext, HttpException, HttpHandler, HttpNotFoundException, HttpResponse, HttpStatusCode, ModelBindingContext, NoContentResult, NotFoundResult, OkResult, RunOn, UnauthorizedAccessException, anonymous, authenticationOnly, authorize, badRequest, created, defineNattyConfig, entityContainer, fileResult, filter, forbiddenAccessInfo, get, init, injectable, noContent, notFound, ok, post, put, registerDecorator, route, setEnvInfo, useFilter };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nattyjs/core",
3
- "version": "0.0.1-beta.5",
3
+ "version": "0.0.1-beta.50",
4
4
  "description": "",
5
5
  "keywords": [],
6
6
  "author": "ajayojha",
@@ -15,9 +15,10 @@
15
15
  "build": "unbuild"
16
16
  },
17
17
  "dependencies": {
18
+ "reflect-metadata": "0.2.2",
18
19
  "tsyringe": "^4.7.0",
19
20
  "path-to-regexp": "6.2.1",
20
- "@nattyjs/common": "0.0.1-beta.5"
21
+ "@nattyjs/common": "0.0.1-beta.50"
21
22
  },
22
23
  "devDependencies": {
23
24
  "unbuild": "1.2.1"