@nattyjs/core 0.0.1-beta.7 → 0.0.1-beta.70

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.mjs CHANGED
@@ -1,6 +1,7 @@
1
- import { commonContainer, isObject, List, BLANK as BLANK$1, RIGHT_SLASH } from '@nattyjs/common';
2
- import { container, injectable as injectable$1 } from 'tsyringe';
1
+ import { commonContainer, isObject, List, BLANK as BLANK$1 } from '@nattyjs/common';
3
2
  import { match } from 'path-to-regexp';
3
+ import path from 'node:path';
4
+ import 'reflect-metadata';
4
5
 
5
6
  function defineNattyConfig(config) {
6
7
  return config;
@@ -25,6 +26,12 @@ function route(path) {
25
26
  const CONTROLLER = "controller";
26
27
  const INVALID_VALUE = "INVALID_VALUE";
27
28
  const BLANK = "";
29
+ const DENY_BY_DEFAULT = {
30
+ type: "https://cheatsheetseries.owasp.org/cheatsheets/Authorization_Cheat_Sheet.html#deny-by-default",
31
+ title: "Deny-by-default",
32
+ 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`,
33
+ solution: `Please implement proper authentication and authorization checks. If this API needs to be accessed anonymously, add the @anonymous() decorator above the HTTP action.`
34
+ };
28
35
 
29
36
  function get(path) {
30
37
  return function(target, propertyKey, descriptor) {
@@ -46,6 +53,7 @@ function Delete() {
46
53
  };
47
54
  }
48
55
 
56
+ const HTTP_METHODS = ["get", "post", "put", "delete"];
49
57
  const _StaticContainer = class {
50
58
  setup(types) {
51
59
  _StaticContainer.types = types;
@@ -57,11 +65,101 @@ const nattyContainer = new class {
57
65
  constructor() {
58
66
  this.container = /* @__PURE__ */ new Map();
59
67
  this.containerState = /* @__PURE__ */ new Map();
68
+ this.compiledRoutes = {};
69
+ }
70
+ get types() {
71
+ return commonContainer.types;
60
72
  }
61
- setup(config, routes, types) {
73
+ setup(config, routes, resolver) {
62
74
  this.config = config;
75
+ this.resolver = resolver;
76
+ this.applyRouteSnapshot(routes);
77
+ }
78
+ replaceRoutes(manifest) {
79
+ if (manifest.resolver)
80
+ this.resolver = manifest.resolver;
81
+ this.applyRouteSnapshot(manifest.routes);
82
+ }
83
+ applyRouteSnapshot(routes) {
63
84
  this.routes = routes;
64
- this.types = types;
85
+ this.compiledRoutes = this.createCompiledRoutes(routes);
86
+ }
87
+ createCompiledRoutes(routes) {
88
+ const compiledRoutes = {};
89
+ let declarationOrder = 0;
90
+ for (const method of HTTP_METHODS)
91
+ compiledRoutes[method] = [];
92
+ for (const [rootPath, routeConfig] of Object.entries(routes)) {
93
+ for (const method of HTTP_METHODS) {
94
+ const childRoutes = routeConfig[method];
95
+ if (!childRoutes)
96
+ continue;
97
+ for (const [childRoutePath, routeInfo] of Object.entries(childRoutes)) {
98
+ const childPath = this.normalizeChildPath(childRoutePath);
99
+ const configuredRoutePath = `${rootPath}${childPath}`;
100
+ compiledRoutes[method].push({
101
+ declarationOrder: declarationOrder++,
102
+ requestMethod: method,
103
+ configuredRoutePath,
104
+ matcher: match(configuredRoutePath, { decode: decodeURIComponent }),
105
+ routeConfig,
106
+ methodInfo: routeInfo,
107
+ specificity: this.getRouteSpecificity(configuredRoutePath)
108
+ });
109
+ }
110
+ }
111
+ }
112
+ const sortedCompiledRoutes = {};
113
+ for (const method of HTTP_METHODS) {
114
+ sortedCompiledRoutes[method] = compiledRoutes[method].sort((routeA, routeB) => this.compareCompiledRoutes(routeA, routeB)).map(({ declarationOrder: declarationOrder2, specificity, ...route }) => route);
115
+ }
116
+ return sortedCompiledRoutes;
117
+ }
118
+ normalizeChildPath(childRoutePath) {
119
+ if (childRoutePath.indexOf("/") === 0)
120
+ return childRoutePath === "/" ? BLANK : childRoutePath;
121
+ return `/${childRoutePath}`;
122
+ }
123
+ getRouteSpecificity(routePath) {
124
+ const segments = routePath.split("/").filter(Boolean);
125
+ const segmentKinds = segments.map((segment) => this.getSegmentKind(segment));
126
+ return {
127
+ segments,
128
+ segmentKinds,
129
+ staticSegmentCount: segmentKinds.filter((kind) => kind === 2).length,
130
+ dynamicSegmentCount: segmentKinds.filter((kind) => kind === 1).length,
131
+ literalLength: segments.filter((segment) => !segment.startsWith(":")).reduce((length, segment) => length + segment.length, 0)
132
+ };
133
+ }
134
+ getSegmentKind(segment) {
135
+ if (!segment)
136
+ return 0;
137
+ if (segment.startsWith(":"))
138
+ return 1;
139
+ return 2;
140
+ }
141
+ compareCompiledRoutes(routeA, routeB) {
142
+ const bySegmentSpecificity = this.compareSegmentSpecificity(routeA.specificity, routeB.specificity);
143
+ if (bySegmentSpecificity !== 0)
144
+ return bySegmentSpecificity;
145
+ if (routeA.specificity.staticSegmentCount !== routeB.specificity.staticSegmentCount)
146
+ return routeB.specificity.staticSegmentCount - routeA.specificity.staticSegmentCount;
147
+ if (routeA.specificity.dynamicSegmentCount !== routeB.specificity.dynamicSegmentCount)
148
+ return routeA.specificity.dynamicSegmentCount - routeB.specificity.dynamicSegmentCount;
149
+ if (routeA.specificity.literalLength !== routeB.specificity.literalLength)
150
+ return routeB.specificity.literalLength - routeA.specificity.literalLength;
151
+ return routeA.declarationOrder - routeB.declarationOrder;
152
+ }
153
+ compareSegmentSpecificity(routeA, routeB) {
154
+ const maxSharedSegments = Math.min(routeA.segmentKinds.length, routeB.segmentKinds.length);
155
+ for (let index = 0; index < maxSharedSegments; index++) {
156
+ if (routeA.segmentKinds[index] !== routeB.segmentKinds[index])
157
+ return routeB.segmentKinds[index] - routeA.segmentKinds[index];
158
+ }
159
+ return 0;
160
+ }
161
+ getCompiledRoutes(method) {
162
+ return this.compiledRoutes[method] || [];
65
163
  }
66
164
  getTypes() {
67
165
  return StaticContainer.types;
@@ -111,57 +209,133 @@ const nattyContainer = new class {
111
209
  }
112
210
  }();
113
211
 
212
+ function startWebSchedules(schedules) {
213
+ if (schedules && Array.isArray(schedules)) {
214
+ for (const schedule of schedules)
215
+ startWebSchedule(schedule);
216
+ }
217
+ }
218
+ async function startWebSchedule(config) {
219
+ if (config) {
220
+ const interval = setInterval(async () => {
221
+ try {
222
+ clearInterval(interval);
223
+ await config.scheduleFunction();
224
+ startWebSchedule(config);
225
+ } catch (ex) {
226
+ startWebSchedule(config);
227
+ }
228
+ }, config.interval);
229
+ }
230
+ }
231
+
114
232
  function init(config, appConfig) {
115
233
  commonContainer.setupConfig(config);
116
234
  commonContainer.setEnvTsDefinition(appConfig.envTsDefinition);
117
- nattyContainer.setup(config, appConfig.routes, appConfig.types);
118
- return initializeModule(config);
235
+ setupLegacyTypes(appConfig.types);
236
+ nattyContainer.setup(config, appConfig.routes, appConfig.resolver || defaultResolver);
237
+ callLifeCycleEvents(config, true);
238
+ const result = initializeModule(config);
239
+ callLifeCycleEvents(config);
240
+ startWebSchedules(config.webSchedules);
241
+ return result;
242
+ }
243
+ function setupLegacyTypes(types) {
244
+ if (types) {
245
+ commonContainer.types = {};
246
+ for (const [name, type] of Object.entries(types))
247
+ commonContainer.registerType({ name, ...type });
248
+ }
249
+ }
250
+ function defaultResolver(path) {
251
+ if (typeof path === "function")
252
+ return path();
253
+ if (typeof path === "string") {
254
+ const normalizedPath = path.startsWith("/") ? `.${path}` : path;
255
+ return require(normalizedPath);
256
+ }
257
+ return {};
119
258
  }
120
259
  function initializeModule(config) {
121
260
  if (config.app) {
122
261
  return config.app.init(config);
123
262
  }
124
263
  }
125
-
126
- function getPreResponseBody(body) {
127
- let bodyInfo;
128
- 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;
264
+ async function callLifeCycleEvents(config, isPreInit = false) {
265
+ if (config.lifeCycle) {
266
+ if (config.lifeCycle.preInit && isPreInit) {
267
+ const preInit = config.lifeCycle.preInit();
268
+ if (preInit) {
269
+ if (preInit.cors) {
270
+ if (!config.cors) {
271
+ const jObject = { origin: [] };
272
+ config.cors = jObject;
273
+ } else if (!config.cors.origin)
274
+ config.cors.origin = [];
275
+ if (preInit.cors.origin)
276
+ preInit.cors.origin.forEach((t) => {
277
+ config.cors?.origin.push(t);
278
+ });
279
+ if (preInit.cors.methods)
280
+ config.cors.methods = preInit.cors.methods;
281
+ if (preInit.cors.optionsSuccessStatus)
282
+ config.cors.optionsSuccessStatus = preInit.cors.optionsSuccessStatus;
283
+ if (preInit.cors.preflightContinue)
284
+ config.cors.preflightContinue = preInit.cors.preflightContinue;
285
+ }
286
+ }
142
287
  }
288
+ if (config.lifeCycle.onStart) {
289
+ config.lifeCycle.onStart();
290
+ }
291
+ }
292
+ }
293
+
294
+ function getPreResponseBody(body, isBuffer = false) {
295
+ if (body === void 0 || body === null)
296
+ return void 0;
297
+ if (isBuffer)
298
+ return { buffer: body };
299
+ if (isObject(body) || Array.isArray(body))
300
+ return { json: body };
301
+ switch (typeof body) {
302
+ case "string":
303
+ return { string: body };
304
+ case "number":
305
+ return { number: body };
306
+ case "boolean":
307
+ return { boolean: body };
308
+ default:
309
+ return { json: body };
143
310
  }
144
- return bodyInfo;
145
311
  }
146
312
 
147
313
  class HttpResponse {
148
314
  constructor(response) {
149
- this._cookies = new Array();
315
+ this._cookies = [];
150
316
  this._headers = new Headers();
317
+ this._isBuffer = false;
151
318
  this.setValues(response);
152
319
  }
153
320
  setValues(responseInit) {
154
- if (responseInit) {
155
- if (responseInit.headers)
156
- for (const [key, value] of Object.entries(responseInit.headers))
157
- this.headers.append(key, value);
158
- if (responseInit.cookies)
159
- for (const cookie of responseInit.cookies)
160
- this.addCookie(cookie);
161
- if (responseInit.status)
162
- this.status = responseInit.status;
163
- if (responseInit.body)
164
- this.body = responseInit.body;
321
+ if (!responseInit)
322
+ return;
323
+ if ("isBuffer" in responseInit)
324
+ this._isBuffer = !!responseInit.isBuffer;
325
+ if (responseInit.headers) {
326
+ for (const [key, value] of Object.entries(responseInit.headers)) {
327
+ this.headers.append(key, String(value));
328
+ }
329
+ }
330
+ if (responseInit.cookies) {
331
+ for (const cookie of responseInit.cookies)
332
+ this.addCookie(cookie);
333
+ }
334
+ if ("status" in responseInit && responseInit.status !== void 0) {
335
+ this.status = responseInit.status;
336
+ }
337
+ if ("body" in responseInit) {
338
+ this.body = responseInit.body;
165
339
  }
166
340
  }
167
341
  addCookie(cookie) {
@@ -171,7 +345,7 @@ class HttpResponse {
171
345
  return this._cookies;
172
346
  }
173
347
  get status() {
174
- return this._status;
348
+ return this._status ?? 200;
175
349
  }
176
350
  set status(value) {
177
351
  this._status = value;
@@ -183,22 +357,62 @@ class HttpResponse {
183
357
  return this._body;
184
358
  }
185
359
  set body(value) {
186
- this._body = getPreResponseBody(value);
360
+ this._body = getPreResponseBody(value, this._isBuffer);
187
361
  }
188
362
  write(responseInit) {
189
363
  this.setValues(responseInit);
190
364
  }
191
365
  }
192
366
 
193
- class HttpException {
194
- constructor(response) {
367
+ class HttpException extends Error {
368
+ constructor(response, message) {
369
+ const bodyMsg = response?.body?.message;
370
+ const msg = message ?? (typeof bodyMsg === "string" && bodyMsg.trim() ? bodyMsg : `HTTP ${response.status ?? 500}`);
371
+ super(msg);
372
+ this.name = this.constructor.name;
195
373
  this.httpResponse = new HttpResponse(response);
374
+ const anyErr = Error;
375
+ if (typeof anyErr.captureStackTrace === "function") {
376
+ anyErr.captureStackTrace(this, this.constructor);
377
+ }
196
378
  }
197
379
  getResponse() {
198
380
  return this.httpResponse;
199
381
  }
200
382
  }
201
383
 
384
+ class HttpRequest {
385
+ constructor(http) {
386
+ this.httpRequest = http;
387
+ }
388
+ get cookies() {
389
+ return this.httpRequest.cookies;
390
+ }
391
+ get url() {
392
+ return this.httpRequest.url;
393
+ }
394
+ get method() {
395
+ return this.httpRequest.method;
396
+ }
397
+ get headers() {
398
+ return this.httpRequest.headers;
399
+ }
400
+ get user() {
401
+ return null;
402
+ }
403
+ get body() {
404
+ return this.httpRequest.body;
405
+ }
406
+ }
407
+
408
+ class HttpContext {
409
+ constructor(request, context) {
410
+ this.request = new HttpRequest(request);
411
+ this.response = new HttpResponse();
412
+ this.context = context;
413
+ }
414
+ }
415
+
202
416
  var RequestPipeline = /* @__PURE__ */ ((RequestPipeline2) => {
203
417
  RequestPipeline2[RequestPipeline2["onAuthentication"] = 0] = "onAuthentication";
204
418
  RequestPipeline2[RequestPipeline2["onAuthorization"] = 1] = "onAuthorization";
@@ -325,20 +539,24 @@ class HttpNotFoundException extends HttpException {
325
539
  var HttpStatusCode = /* @__PURE__ */ ((HttpStatusCode2) => {
326
540
  HttpStatusCode2[HttpStatusCode2["success"] = 200] = "success";
327
541
  HttpStatusCode2[HttpStatusCode2["created"] = 201] = "created";
542
+ HttpStatusCode2[HttpStatusCode2["accepted"] = 202] = "accepted";
328
543
  HttpStatusCode2[HttpStatusCode2["noContent"] = 204] = "noContent";
329
- HttpStatusCode2[HttpStatusCode2["notFound"] = 404] = "notFound";
544
+ HttpStatusCode2[HttpStatusCode2["movedPermanently"] = 301] = "movedPermanently";
545
+ HttpStatusCode2[HttpStatusCode2["found"] = 302] = "found";
546
+ HttpStatusCode2[HttpStatusCode2["badRequest"] = 400] = "badRequest";
330
547
  HttpStatusCode2[HttpStatusCode2["unAuthorized"] = 401] = "unAuthorized";
331
548
  HttpStatusCode2[HttpStatusCode2["forbiddenAccess"] = 403] = "forbiddenAccess";
332
- HttpStatusCode2[HttpStatusCode2["badRequest"] = 400] = "badRequest";
549
+ HttpStatusCode2[HttpStatusCode2["notFound"] = 404] = "notFound";
550
+ HttpStatusCode2[HttpStatusCode2["conflict"] = 409] = "conflict";
551
+ HttpStatusCode2[HttpStatusCode2["unprocessableEntity"] = 422] = "unprocessableEntity";
552
+ HttpStatusCode2[HttpStatusCode2["tooManyRequests"] = 429] = "tooManyRequests";
333
553
  HttpStatusCode2[HttpStatusCode2["serverError"] = 500] = "serverError";
334
554
  return HttpStatusCode2;
335
555
  })(HttpStatusCode || {});
336
556
 
337
557
  class BaseResult {
338
- constructor(response, responseHeaders) {
339
- this.response = response;
340
- if (responseHeaders)
341
- this.response = { ...this.response, ...responseHeaders };
558
+ constructor(response, extras) {
559
+ this.response = extras ? { ...response, ...extras } : response;
342
560
  }
343
561
  getResponse() {
344
562
  return this.response;
@@ -346,6 +564,7 @@ class BaseResult {
346
564
  }
347
565
 
348
566
  function getResponseBodyObject(body, props) {
567
+ const sensitiveProps = commonContainer.nattyConfig?.secure?.sensitiveProps;
349
568
  if (body instanceof List)
350
569
  return getResponseBodyObject(body.values, body.props);
351
570
  if (Array.isArray(body)) {
@@ -359,7 +578,8 @@ function getResponseBodyObject(body, props) {
359
578
  const keys = Object.keys(body);
360
579
  const getterProps = props ? Object.keys(props).map((key) => props[key]) : [];
361
580
  for (const key of [...keys, ...getterProps])
362
- jObject[key] = getResponseBodyObject(body[key]);
581
+ if (!sensitiveProps || sensitiveProps.filter((t) => t == key.toLowerCase()).length == 0)
582
+ jObject[key] = getResponseBodyObject(body[key]);
363
583
  return jObject;
364
584
  }
365
585
  return body;
@@ -383,10 +603,19 @@ class BaseResponse {
383
603
  notFound() {
384
604
  throw new HttpNotFoundException({});
385
605
  }
606
+ normalizeHttpResponse(result) {
607
+ if (result instanceof HttpResponse)
608
+ return result;
609
+ if (result instanceof HttpException)
610
+ return result.getResponse();
611
+ if (result && typeof result === "object" && ("status" in result || "body" in result || "headers" in result || "cookies" in result || "isBuffer" in result))
612
+ return new HttpResponse(result);
613
+ return result;
614
+ }
386
615
  }
387
616
 
388
617
  function getTypedErrorMessage(type, value) {
389
- const message = commonContainer.nattyConfig.modelBinding.errorMessage.typed[type];
618
+ const message = commonContainer.nattyConfig?.modelBinding?.errorMessage?.typed ? commonContainer.nattyConfig?.modelBinding?.errorMessage?.typed[type] : "";
390
619
  return parseMessage(message, [value]);
391
620
  }
392
621
  function parseMessage(message, value) {
@@ -453,7 +682,7 @@ const entityContainer = new class {
453
682
  }
454
683
  getPropertyValidators(entityName, propName) {
455
684
  const entityInfo = this.entityConfig[entityName];
456
- const propertyInfo = entityInfo.properties[propName];
685
+ const propertyInfo = entityInfo ? entityInfo.properties[propName] : void 0;
457
686
  return propertyInfo ? propertyInfo.validators : {};
458
687
  }
459
688
  getProperties(entityName) {
@@ -462,6 +691,115 @@ const entityContainer = new class {
462
691
  }
463
692
  }();
464
693
 
694
+ class ForbiddenAccessException extends HttpException {
695
+ constructor(data) {
696
+ super({
697
+ body: data,
698
+ status: 403
699
+ });
700
+ }
701
+ }
702
+
703
+ class UnauthorizedAccessException extends HttpException {
704
+ constructor(data) {
705
+ super({
706
+ body: data,
707
+ status: 401
708
+ });
709
+ }
710
+ }
711
+
712
+ class AcceptedException extends HttpException {
713
+ constructor(data) {
714
+ super({ body: data, status: HttpStatusCode.accepted });
715
+ }
716
+ }
717
+
718
+ class HttpConflictException extends HttpException {
719
+ constructor(data) {
720
+ super({ body: data, status: HttpStatusCode.conflict });
721
+ }
722
+ }
723
+
724
+ class HttpUnprocessableEntityException extends HttpException {
725
+ constructor(data) {
726
+ super({ body: data, status: HttpStatusCode.unprocessableEntity });
727
+ }
728
+ }
729
+
730
+ function formatRetryAfter(value) {
731
+ if (typeof value === "number")
732
+ return String(Math.max(0, Math.floor(value)));
733
+ if (value instanceof Date)
734
+ return value.toUTCString();
735
+ return String(value);
736
+ }
737
+ class TooManyRequestsException extends HttpException {
738
+ constructor(data, retryAfter) {
739
+ super({
740
+ body: data,
741
+ status: HttpStatusCode.tooManyRequests,
742
+ headers: retryAfter !== void 0 ? { "Retry-After": formatRetryAfter(retryAfter) } : void 0
743
+ });
744
+ }
745
+ }
746
+
747
+ class RedirectException extends HttpException {
748
+ constructor(location, data) {
749
+ super({
750
+ body: data,
751
+ status: HttpStatusCode.found,
752
+ headers: { Location: location }
753
+ });
754
+ }
755
+ }
756
+
757
+ class RedirectPermanentException extends HttpException {
758
+ constructor(location, data) {
759
+ super({
760
+ body: data,
761
+ status: HttpStatusCode.movedPermanently,
762
+ headers: { Location: location }
763
+ });
764
+ }
765
+ }
766
+
767
+ class ProblemDetailsException extends HttpException {
768
+ constructor(problem, status, headers) {
769
+ const resolvedStatus = status ?? problem.status ?? 500;
770
+ super({
771
+ status: resolvedStatus,
772
+ body: { ...problem, status: resolvedStatus },
773
+ headers: {
774
+ "Content-Type": "application/problem+json; charset=utf-8",
775
+ ...headers ?? {}
776
+ }
777
+ });
778
+ }
779
+ }
780
+ class ValidationProblemDetailsException extends ProblemDetailsException {
781
+ constructor(errors, detail, status = 400) {
782
+ super(
783
+ {
784
+ type: "https://tools.ietf.org/html/rfc7231#section-6.5.1",
785
+ title: "One or more validation errors occurred.",
786
+ status,
787
+ detail,
788
+ errors
789
+ },
790
+ status
791
+ );
792
+ }
793
+ }
794
+
795
+ function CreateProblemDetail(modelName, detail) {
796
+ return {
797
+ type: "https://tools.ietf.org/html/rfc7231#section-6.5.1",
798
+ title: `The specified ${modelName} model props are invalid.`,
799
+ detail
800
+ };
801
+ }
802
+
465
803
  class ParameterTypeConverter extends BaseResponse {
466
804
  constructor() {
467
805
  super(...arguments);
@@ -522,12 +860,14 @@ class ParameterTypeConverter extends BaseResponse {
522
860
  } else {
523
861
  if (this.isArrayType(property.type) && Array.isArray(value)) {
524
862
  let arrayValue = body[property.name] = [];
525
- let arrayInvalidProps = invalidProps[property.name] = [];
526
863
  for (const item of value) {
527
864
  const sanitizeValue = this.sanitizer[property.type.toLowerCase()] ? this.sanitizer[property.type.toLowerCase()](item) : item;
528
- if (sanitizeValue === INVALID_VALUE)
865
+ if (sanitizeValue === INVALID_VALUE) {
866
+ let arrayInvalidProps = invalidProps[property.name];
867
+ if (!arrayInvalidProps)
868
+ arrayInvalidProps = invalidProps[property.name] = [];
529
869
  arrayInvalidProps.push(property);
530
- else
870
+ } else
531
871
  arrayValue.push(sanitizeValue);
532
872
  }
533
873
  } else
@@ -548,14 +888,17 @@ class ParameterTypeConverter extends BaseResponse {
548
888
  for (const parameterInfo of methodInfo.parameters) {
549
889
  const value = jObject[parameterInfo.name];
550
890
  if (value !== void 0) {
551
- jObject[parameterInfo.name] = SANITIZERS[parameterInfo.type](value);
891
+ const sanitizedValue = SANITIZERS[parameterInfo.type](value);
892
+ if (sanitizedValue === null && sanitizedValue != value)
893
+ throw new HttpBadRequestException(CreateProblemDetail(parameterInfo.type, [{ [parameterInfo.name]: `The supplied data type must be "${parameterInfo.type}"` }]));
894
+ jObject[parameterInfo.name] = sanitizedValue;
552
895
  }
553
896
  }
554
897
  return jObject;
555
898
  }
556
899
  convertToInstance(entityName, data) {
557
900
  const typesInfo = this.types[entityName];
558
- const target = this.getClassTarget(typesInfo.path) || entityContainer.getTarget(entityName);
901
+ const target = this.getClassTarget(typesInfo.path, entityName) || entityContainer.getTarget(entityName);
559
902
  let instance = null;
560
903
  if (target) {
561
904
  instance = new target();
@@ -564,11 +907,10 @@ class ParameterTypeConverter extends BaseResponse {
564
907
  instance = data;
565
908
  return instance;
566
909
  }
567
- getClassTarget(resolver) {
568
- if (resolver) {
569
- const classInfo = resolver();
570
- const name = Object.keys(classInfo)[0];
571
- return classInfo[name];
910
+ getClassTarget(path, entityName) {
911
+ if (path) {
912
+ const classInfo = nattyContainer.resolver(path);
913
+ return classInfo[entityName];
572
914
  }
573
915
  return void 0;
574
916
  }
@@ -604,8 +946,8 @@ class RouteParser extends ParameterTypeConverter {
604
946
  get httpMethod() {
605
947
  return this.httpContext.request.method.toLowerCase();
606
948
  }
607
- get appRoutes() {
608
- return nattyContainer.routes;
949
+ get compiledRoutes() {
950
+ return nattyContainer.getCompiledRoutes(this.httpMethod);
609
951
  }
610
952
  init() {
611
953
  const isMatched = this.matchRoute();
@@ -618,47 +960,46 @@ class RouteParser extends ParameterTypeConverter {
618
960
  return controllerInfo[name];
619
961
  }
620
962
  matchRoute() {
621
- let isMatched = false;
622
- const requestPathname = this.getRequestPathname();
623
- for (const [key, value] of Object.entries(this.appRoutes)) {
624
- const rootPath = key;
625
- const routeConfig = value;
626
- const childRoutes = routeConfig[this.httpMethod];
627
- if (childRoutes) {
628
- for (const [key2, value2] of Object.entries(childRoutes)) {
629
- const childPath = key2.indexOf(RIGHT_SLASH) == 0 ? key2 === RIGHT_SLASH ? BLANK$1 : key2 : `/${key2}`;
630
- const methodInfo = value2;
631
- const configuredRoutePath = `${rootPath}${childPath}`;
632
- const routeMatch = match(`${rootPath}${childPath}`, { decode: decodeURIComponent });
633
- const matched = routeMatch(requestPathname);
634
- if (matched) {
635
- isMatched = true;
636
- this.routeInfo = {
637
- path: `${commonContainer.nattyConfig.api.rootPath}/${requestPathname}`,
638
- configuredRoutePath: `${commonContainer.nattyConfig.api.rootPath}/${configuredRoutePath}`,
639
- controller: this.getController(routeConfig),
640
- parameters: routeConfig.parameters,
641
- methodInfo,
642
- params: this.convert(methodInfo, matched.params),
643
- queryParams: this.getQueryParams()
644
- };
645
- break;
646
- }
647
- }
963
+ const requestUrl = this.getRequestUrl();
964
+ const requestPathname = this.getRequestPathname(requestUrl);
965
+ for (const route of this.compiledRoutes) {
966
+ const matched = route.matcher(requestPathname);
967
+ if (matched) {
968
+ this.routeInfo = {
969
+ path: `${commonContainer.nattyConfig.api.rootPath}/${requestPathname}`,
970
+ configuredRoutePath: `${commonContainer.nattyConfig.api.rootPath}/${route.configuredRoutePath}`,
971
+ controller: this.getController(route.routeConfig),
972
+ parameters: route.routeConfig.parameters,
973
+ methodInfo: route.methodInfo,
974
+ params: this.convert(route.methodInfo, matched.params),
975
+ queryParams: this.getQueryParams(requestUrl)
976
+ };
977
+ return true;
648
978
  }
649
979
  }
650
- return isMatched;
651
- }
652
- getRequestPathname() {
653
- const apiRootPath = commonContainer.nattyConfig.api.rootPath;
654
- const url = new URL(this.httpContext.request.url);
655
- let splitUrl = url.pathname.split(`${apiRootPath}/`);
656
- if (splitUrl.length > 1)
657
- return splitUrl[1];
658
- return url.pathname;
659
- }
660
- getQueryParams() {
661
- const url = new URL(this.httpContext.request.url);
980
+ return false;
981
+ }
982
+ getRequestUrl() {
983
+ if (!this.parsedRequestUrl)
984
+ this.parsedRequestUrl = new URL(this.httpContext.request.url);
985
+ return this.parsedRequestUrl;
986
+ }
987
+ getRequestPathname(url) {
988
+ const apiRootPath = String(commonContainer.nattyConfig.api.rootPath || "").replace(/^\/+|\/+$/g, "");
989
+ const normalizedPathname = url.pathname.replace(/^\/+/, "");
990
+ if (!apiRootPath) {
991
+ return normalizedPathname;
992
+ }
993
+ if (normalizedPathname === apiRootPath) {
994
+ return "";
995
+ }
996
+ const apiPrefix = `${apiRootPath}/`;
997
+ if (normalizedPathname.startsWith(apiPrefix)) {
998
+ return normalizedPathname.slice(apiPrefix.length);
999
+ }
1000
+ return normalizedPathname;
1001
+ }
1002
+ getQueryParams(url) {
662
1003
  const queryParams = {};
663
1004
  for (const param of url.searchParams.keys())
664
1005
  queryParams[param] = url.searchParams.get(param);
@@ -666,20 +1007,13 @@ class RouteParser extends ParameterTypeConverter {
666
1007
  }
667
1008
  }
668
1009
 
669
- class UnauthorizedAccessException extends HttpException {
670
- constructor(data) {
671
- super({
672
- body: data,
673
- status: 401
674
- });
675
- }
676
- }
677
-
678
1010
  const decoratorStateContainer = new class {
679
1011
  constructor() {
680
1012
  this.controllerConfig = {};
1013
+ this.controllerSources = /* @__PURE__ */ new Map();
1014
+ this.sourceControllers = /* @__PURE__ */ new Map();
681
1015
  }
682
- register(params, type, additionalConfig) {
1016
+ register(params, type, additionalConfig, sourceFile) {
683
1017
  const name = params.target.name || params.target.constructor.name;
684
1018
  let controllerInfo = this.controllerConfig[name] || null;
685
1019
  let controllerMethodInfo = null;
@@ -692,6 +1026,9 @@ const decoratorStateContainer = new class {
692
1026
  controllerMethodInfo[type] = additionalConfig;
693
1027
  } else
694
1028
  controllerInfo.config[type] = additionalConfig;
1029
+ if (sourceFile) {
1030
+ this.trackControllerSource(name, sourceFile);
1031
+ }
695
1032
  }
696
1033
  getInfo(controllerName, methodName, type) {
697
1034
  const controllerInfo = this.controllerConfig[controllerName];
@@ -704,6 +1041,55 @@ const decoratorStateContainer = new class {
704
1041
  methodConfig = methodInfo[type];
705
1042
  return { controllerConfig, methodConfig };
706
1043
  }
1044
+ clearFromSource(sourceFile) {
1045
+ const normalizedSourceFile = this.normalizeFilePath(sourceFile);
1046
+ const controllerNames = Array.from(
1047
+ this.sourceControllers.get(normalizedSourceFile) || []
1048
+ );
1049
+ this.clearControllers(controllerNames);
1050
+ this.sourceControllers.delete(normalizedSourceFile);
1051
+ return controllerNames;
1052
+ }
1053
+ clearControllers(controllerNames) {
1054
+ const removedControllers = [];
1055
+ for (const controllerName of controllerNames) {
1056
+ const previousSource = this.controllerSources.get(controllerName);
1057
+ delete this.controllerConfig[controllerName];
1058
+ this.controllerSources.delete(controllerName);
1059
+ removedControllers.push(controllerName);
1060
+ if (previousSource) {
1061
+ const controllers = this.sourceControllers.get(previousSource);
1062
+ controllers?.delete(controllerName);
1063
+ if (controllers && controllers.size === 0) {
1064
+ this.sourceControllers.delete(previousSource);
1065
+ }
1066
+ }
1067
+ }
1068
+ return removedControllers;
1069
+ }
1070
+ reset() {
1071
+ this.controllerConfig = {};
1072
+ this.controllerSources.clear();
1073
+ this.sourceControllers.clear();
1074
+ }
1075
+ trackControllerSource(controllerName, sourceFile) {
1076
+ const normalizedSourceFile = this.normalizeFilePath(sourceFile);
1077
+ const previousSource = this.controllerSources.get(controllerName);
1078
+ if (previousSource && previousSource !== normalizedSourceFile) {
1079
+ this.sourceControllers.get(previousSource)?.delete(controllerName);
1080
+ if (this.sourceControllers.get(previousSource)?.size === 0) {
1081
+ this.sourceControllers.delete(previousSource);
1082
+ }
1083
+ }
1084
+ this.controllerSources.set(controllerName, normalizedSourceFile);
1085
+ if (!this.sourceControllers.has(normalizedSourceFile)) {
1086
+ this.sourceControllers.set(normalizedSourceFile, /* @__PURE__ */ new Set());
1087
+ }
1088
+ this.sourceControllers.get(normalizedSourceFile)?.add(controllerName);
1089
+ }
1090
+ normalizeFilePath(filePath) {
1091
+ return path.resolve(filePath).replace(/\\/g, "/");
1092
+ }
707
1093
  }();
708
1094
 
709
1095
  var DecoratorType = /* @__PURE__ */ ((DecoratorType2) => {
@@ -720,22 +1106,13 @@ var DecoratorType = /* @__PURE__ */ ((DecoratorType2) => {
720
1106
  return DecoratorType2;
721
1107
  })(DecoratorType || {});
722
1108
 
723
- class ForbiddenAccessException extends HttpException {
724
- constructor(data) {
725
- super({
726
- body: data,
727
- status: 403
728
- });
729
- }
730
- }
731
-
732
1109
  class AbstractExecutionContext {
733
1110
  constructor(context, routeInfo) {
734
1111
  this.context = context;
735
1112
  this.routeInfo = routeInfo;
736
1113
  }
737
1114
  resolveService(instance) {
738
- return container.resolve(instance);
1115
+ return this.context.services.get(instance);
739
1116
  }
740
1117
  get request() {
741
1118
  return this.context.request;
@@ -758,14 +1135,6 @@ class ActionExecutingContext extends AbstractExecutionContext {
758
1135
  }
759
1136
  }
760
1137
 
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
1138
  function runValidators(entityName, value) {
770
1139
  const typeInfo = nattyContainer.types[entityName];
771
1140
  let errors = {};
@@ -788,7 +1157,7 @@ function runValidators(entityName, value) {
788
1157
  errors[propertyInfo.name] = arrayPropErrors.length > 0 ? arrayPropErrors : null;
789
1158
  } else
790
1159
  errors[propertyInfo.name] = runValidators(childEntityName, propValue);
791
- if (Array.isArray(errors[propertyInfo.name]) && errors[propertyInfo.name].length == 0 || Object.keys(errors[propertyInfo.name]).length == 0)
1160
+ if (Array.isArray(errors[propertyInfo.name]) && errors[propertyInfo.name].length == 0 || Object.keys(errors[propertyInfo.name] ?? {}).length == 0)
792
1161
  errors[propertyInfo.name] = null;
793
1162
  }
794
1163
  } else {
@@ -813,11 +1182,12 @@ function getTypeName(typeName) {
813
1182
  }
814
1183
 
815
1184
  class ModelBindingContext extends ParameterTypeConverter {
816
- constructor(type, typeInfo, data) {
1185
+ constructor(type, typeInfo, data, throwOnValidationError = false) {
817
1186
  super();
818
1187
  this.type = type;
819
1188
  this.typeInfo = typeInfo;
820
1189
  this.data = data;
1190
+ this.throwOnValidationError = throwOnValidationError;
821
1191
  this.serialize();
822
1192
  }
823
1193
  serialize() {
@@ -827,6 +1197,8 @@ class ModelBindingContext extends ParameterTypeConverter {
827
1197
  else
828
1198
  this.data = body;
829
1199
  this.instance = this.convertToInstance(this.type, this.data);
1200
+ if (this.throwOnValidationError && !this.isValid)
1201
+ throw new HttpBadRequestException(CreateProblemDetail(this.type, this.errors));
830
1202
  }
831
1203
  get isValid() {
832
1204
  const errors = runValidators(this.type, this.instance);
@@ -843,6 +1215,17 @@ class ActionExecutedContext extends AbstractExecutionContext {
843
1215
  super(httpContext, routeInfo);
844
1216
  this.content = content;
845
1217
  }
1218
+ get response() {
1219
+ return this.context.response;
1220
+ }
1221
+ }
1222
+
1223
+ class AuthorizationContext extends AbstractExecutionContext {
1224
+ constructor(models, context, routeInfo, config) {
1225
+ super(context, routeInfo);
1226
+ this.models = models;
1227
+ this.config = config;
1228
+ }
846
1229
  }
847
1230
 
848
1231
  class RequestProcessor extends RouteParser {
@@ -855,13 +1238,10 @@ class RequestProcessor extends RouteParser {
855
1238
  case RequestPipeline.onAuthentication:
856
1239
  await this.onAuthentication();
857
1240
  break;
858
- case RequestPipeline.onAuthorization:
859
- await this.onAuthorization();
860
- break;
861
1241
  }
862
1242
  }
863
1243
  resolveFilter(instance) {
864
- return container.resolve(instance);
1244
+ return this.httpContext.services.get(instance);
865
1245
  }
866
1246
  getAuthenticationClass() {
867
1247
  let authentication = void 0;
@@ -878,26 +1258,37 @@ class RequestProcessor extends RouteParser {
878
1258
  const authentication = this.getAuthenticationClass();
879
1259
  const authenticationFilter = authentication ? this.resolveFilter(authentication) : void 0;
880
1260
  const anonymousInfo = decoratorStateContainer.getInfo(this.routeInfo.controller.name, this.routeInfo.methodInfo.name, DecoratorType.anonymous);
1261
+ if (!authenticationFilter) {
1262
+ if (commonContainer.nattyConfig?.secure?.denyByDefault && !anonymousInfo.controllerConfig && !anonymousInfo.methodConfig)
1263
+ throw new UnauthorizedAccessException(DENY_BY_DEFAULT);
1264
+ return;
1265
+ }
881
1266
  if (authenticationFilter) {
882
1267
  const result = await authenticationFilter.onAuthentication(this.httpContext);
883
1268
  this.httpContext.user = result;
884
1269
  if (!result.isAuthenticate && !anonymousInfo.controllerConfig && !anonymousInfo.methodConfig)
885
1270
  throw new UnauthorizedAccessException(authenticationFilter.onFailedResponse());
886
- await this.onAuthorization();
887
1271
  }
888
1272
  }
889
- async onAuthorization() {
1273
+ async onAuthorization(methodParameters) {
890
1274
  const authorization = commonContainer.globalConfig.authorization;
891
1275
  const authorizationFilter = authorization ? this.resolveFilter(authorization) : void 0;
892
1276
  const authorizeConfig = decoratorStateContainer.getInfo(this.routeInfo.controller.name, this.routeInfo.methodInfo.name, DecoratorType.authorize);
893
1277
  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);
1278
+ if (this.httpContext.user?.isAuthenticate && authorizationFilter && (authorizeConfig.controllerConfig || authorizeConfig.methodConfig) && (!authenticationOnly.controllerConfig && !authenticationOnly.methodConfig)) {
1279
+ const authorizationContext = new AuthorizationContext(
1280
+ methodParameters.filter((t) => t instanceof ModelBindingContext),
1281
+ this.httpContext,
1282
+ this.routeInfo,
1283
+ authorizeConfig.methodConfig || authorizeConfig.controllerConfig
1284
+ );
1285
+ const result = await authorizationFilter.onAuthorization(authorizationContext, authorizationContext.config);
896
1286
  if (!result)
897
1287
  throw new ForbiddenAccessException(authorizationFilter.onFailedAuthorization());
898
1288
  }
899
1289
  }
900
1290
  async onActionExecuting(methodParameters) {
1291
+ await this.onAuthorization(methodParameters);
901
1292
  let actionFilters = commonContainer.globalConfig.actionFilters || [];
902
1293
  const actionFiltersConfig = decoratorStateContainer.getInfo(this.routeInfo.controller.name, this.routeInfo.methodInfo.name, DecoratorType.useFilter);
903
1294
  actionFilters = [...actionFilters, ...actionFiltersConfig.controllerConfig?.actionFilters || [], ...actionFiltersConfig.methodConfig?.actionFilters || []];
@@ -927,9 +1318,227 @@ class RequestProcessor extends RouteParser {
927
1318
  }
928
1319
  }
929
1320
 
1321
+ const PARAM_TOKENS_KEY = Symbol.for("natty:di:paramtokens");
1322
+ function getParamTokens(Cls) {
1323
+ return Reflect.getMetadata(PARAM_TOKENS_KEY, Cls);
1324
+ }
1325
+
1326
+ function isClassToken(x) {
1327
+ return typeof x === "function";
1328
+ }
1329
+
1330
+ var Lifetime = /* @__PURE__ */ ((Lifetime2) => {
1331
+ Lifetime2["Singleton"] = "singleton";
1332
+ Lifetime2["Scoped"] = "scoped";
1333
+ Lifetime2["Transient"] = "transient";
1334
+ return Lifetime2;
1335
+ })(Lifetime || {});
1336
+
1337
+ function isDisposable(x) {
1338
+ return x && typeof x.dispose === "function";
1339
+ }
1340
+
1341
+ class NattyScope {
1342
+ constructor(root) {
1343
+ this.root = root;
1344
+ this.scoped = /* @__PURE__ */ new Map();
1345
+ this.disposables = [];
1346
+ }
1347
+ createScope() {
1348
+ return new NattyScope(this.root);
1349
+ }
1350
+ set(token, value) {
1351
+ this.scoped.set(token, value);
1352
+ if (isDisposable(value))
1353
+ this.disposables.push(value);
1354
+ }
1355
+ tryGet(token) {
1356
+ try {
1357
+ return this.get(token);
1358
+ } catch {
1359
+ return void 0;
1360
+ }
1361
+ }
1362
+ get(token) {
1363
+ if (this.scoped.has(token))
1364
+ return this.scoped.get(token);
1365
+ const desc = this.root._getDescriptor(token);
1366
+ if (!desc) {
1367
+ if (typeof token === "function")
1368
+ return this.root.construct(token, this);
1369
+ throw new Error(`DI: No registration for token: ${String(token)}`);
1370
+ }
1371
+ if (desc.lifetime === Lifetime.Singleton) {
1372
+ return this.root.get(token);
1373
+ }
1374
+ if (desc.lifetime === Lifetime.Scoped) {
1375
+ const created = desc.useFactory ? desc.useFactory(this) : this.root.construct(desc.useClass, this);
1376
+ this.scoped.set(token, created);
1377
+ if (isDisposable(created))
1378
+ this.disposables.push(created);
1379
+ return created;
1380
+ }
1381
+ return desc.useFactory ? desc.useFactory(this) : this.root.construct(desc.useClass, this);
1382
+ }
1383
+ async dispose() {
1384
+ for (let i = this.disposables.length - 1; i >= 0; i--) {
1385
+ const d = this.disposables[i];
1386
+ await d.dispose();
1387
+ }
1388
+ this.disposables.length = 0;
1389
+ this.scoped.clear();
1390
+ }
1391
+ }
1392
+
1393
+ function normalizeFilePath$1(filePath) {
1394
+ return path.resolve(filePath).replace(/\\/g, "/");
1395
+ }
1396
+ class NattyContainer {
1397
+ constructor() {
1398
+ this.regs = /* @__PURE__ */ new Map();
1399
+ this.singletons = /* @__PURE__ */ new Map();
1400
+ this.tokenSources = /* @__PURE__ */ new Map();
1401
+ this.sourceTokens = /* @__PURE__ */ new Map();
1402
+ this.metadataKeySources = /* @__PURE__ */ new Map();
1403
+ this.sourceMetadataKeys = /* @__PURE__ */ new Map();
1404
+ }
1405
+ register(token, desc, sourceFile) {
1406
+ this.regs.set(token, desc);
1407
+ if (sourceFile) {
1408
+ this.trackTokenSource(token, sourceFile);
1409
+ }
1410
+ }
1411
+ addTransient(token, useClass, useFactory, sourceFile) {
1412
+ this.register(
1413
+ token,
1414
+ { lifetime: Lifetime.Transient, useClass: useClass ?? token, useFactory },
1415
+ sourceFile
1416
+ );
1417
+ }
1418
+ addScoped(token, useClass, useFactory, sourceFile) {
1419
+ this.register(
1420
+ token,
1421
+ { lifetime: Lifetime.Scoped, useClass: useClass ?? token, useFactory },
1422
+ sourceFile
1423
+ );
1424
+ }
1425
+ addSingleton(token, useClass, useFactory, sourceFile) {
1426
+ this.register(
1427
+ token,
1428
+ { lifetime: Lifetime.Singleton, useClass: useClass ?? token, useFactory },
1429
+ sourceFile
1430
+ );
1431
+ }
1432
+ addInstance(token, value, sourceFile) {
1433
+ this.register(token, { lifetime: Lifetime.Singleton, useValue: value }, sourceFile);
1434
+ this.singletons.set(token, value);
1435
+ }
1436
+ createScope() {
1437
+ return new NattyScope(this);
1438
+ }
1439
+ set(_token, _value) {
1440
+ throw new Error("Use scope.set(token, value) for per-request instances.");
1441
+ }
1442
+ tryGet(token) {
1443
+ try {
1444
+ return this.get(token);
1445
+ } catch {
1446
+ return void 0;
1447
+ }
1448
+ }
1449
+ get(token) {
1450
+ const desc = this.regs.get(token);
1451
+ if (!desc) {
1452
+ if (isClassToken(token))
1453
+ return this.construct(token, this);
1454
+ throw new Error(`DI: No registration for token: ${String(token)}`);
1455
+ }
1456
+ if (desc.lifetime === Lifetime.Singleton) {
1457
+ if (desc.useValue !== void 0)
1458
+ return desc.useValue;
1459
+ if (this.singletons.has(token))
1460
+ return this.singletons.get(token);
1461
+ const created = desc.useFactory ? desc.useFactory(this) : this.construct(desc.useClass, this);
1462
+ this.singletons.set(token, created);
1463
+ return created;
1464
+ }
1465
+ throw new Error(
1466
+ `DI: Tried to resolve ${desc.lifetime} service from root container. Use httpContext.services.get(...)`
1467
+ );
1468
+ }
1469
+ construct(Cls, sp) {
1470
+ const paramTypes = Reflect.getMetadata("design:paramtypes", Cls) || [];
1471
+ const overrideTokens = getParamTokens(Cls) || [];
1472
+ const args = paramTypes.map((t, i) => sp.get(overrideTokens[i] ?? t));
1473
+ return new Cls(...args);
1474
+ }
1475
+ _getDescriptor(token) {
1476
+ return this.regs.get(token);
1477
+ }
1478
+ trackMetadataKey(sourceFile, key) {
1479
+ const normalizedSourceFile = normalizeFilePath$1(sourceFile);
1480
+ const previousSource = this.metadataKeySources.get(key);
1481
+ if (previousSource && previousSource !== normalizedSourceFile) {
1482
+ this.sourceMetadataKeys.get(previousSource)?.delete(key);
1483
+ if (this.sourceMetadataKeys.get(previousSource)?.size === 0) {
1484
+ this.sourceMetadataKeys.delete(previousSource);
1485
+ }
1486
+ }
1487
+ this.metadataKeySources.set(key, normalizedSourceFile);
1488
+ if (!this.sourceMetadataKeys.has(normalizedSourceFile)) {
1489
+ this.sourceMetadataKeys.set(normalizedSourceFile, /* @__PURE__ */ new Set());
1490
+ }
1491
+ this.sourceMetadataKeys.get(normalizedSourceFile)?.add(key);
1492
+ }
1493
+ clearRegistrationsFromSource(sourceFile) {
1494
+ const normalizedSourceFile = normalizeFilePath$1(sourceFile);
1495
+ const tokens = Array.from(this.sourceTokens.get(normalizedSourceFile) || []);
1496
+ const metadataKeys = Array.from(
1497
+ this.sourceMetadataKeys.get(normalizedSourceFile) || []
1498
+ );
1499
+ for (const token of tokens) {
1500
+ this.regs.delete(token);
1501
+ this.singletons.delete(token);
1502
+ this.tokenSources.delete(token);
1503
+ }
1504
+ for (const metadataKey of metadataKeys) {
1505
+ this.metadataKeySources.delete(metadataKey);
1506
+ }
1507
+ this.sourceTokens.delete(normalizedSourceFile);
1508
+ this.sourceMetadataKeys.delete(normalizedSourceFile);
1509
+ return {
1510
+ metadataKeys,
1511
+ tokens
1512
+ };
1513
+ }
1514
+ trackTokenSource(token, sourceFile) {
1515
+ const normalizedSourceFile = normalizeFilePath$1(sourceFile);
1516
+ const previousSource = this.tokenSources.get(token);
1517
+ if (previousSource && previousSource !== normalizedSourceFile) {
1518
+ this.sourceTokens.get(previousSource)?.delete(token);
1519
+ if (this.sourceTokens.get(previousSource)?.size === 0) {
1520
+ this.sourceTokens.delete(previousSource);
1521
+ }
1522
+ }
1523
+ this.tokenSources.set(token, normalizedSourceFile);
1524
+ if (!this.sourceTokens.has(normalizedSourceFile)) {
1525
+ this.sourceTokens.set(normalizedSourceFile, /* @__PURE__ */ new Set());
1526
+ }
1527
+ this.sourceTokens.get(normalizedSourceFile)?.add(token);
1528
+ }
1529
+ }
1530
+
1531
+ const nattyServiceResolver = new NattyContainer();
1532
+
930
1533
  class Resolver extends RequestProcessor {
931
1534
  constructor(httpContext) {
932
1535
  super(httpContext);
1536
+ this.registerDependency();
1537
+ }
1538
+ registerDependency() {
1539
+ this.httpContext.services = nattyServiceResolver.createScope();
1540
+ commonContainer.setMetadata(HttpContext.name, HttpContext, "services");
1541
+ this.httpContext.services.set(HttpContext, this.httpContext);
933
1542
  }
934
1543
  getControllerInstance() {
935
1544
  const controller = this.routeInfo.controller;
@@ -937,18 +1546,27 @@ class Resolver extends RequestProcessor {
937
1546
  const instance = new controller(...parameters);
938
1547
  return instance;
939
1548
  }
940
- resolveClass(classConstruct) {
941
- return container.resolve(classConstruct);
1549
+ resolveClass(token) {
1550
+ return this.httpContext.services.get(token);
942
1551
  }
943
1552
  resolveConstructorParameters() {
944
1553
  let parameters = [];
945
1554
  for (const parameter of this.routeInfo.parameters) {
946
- const classConstruct = this.getClass(parameter.type);
947
- if (classConstruct)
948
- parameters.push(this.resolveClass(classConstruct));
1555
+ const token = this.getConstructorParameterToken(parameter);
1556
+ if (token)
1557
+ parameters.push(this.resolveClass(token));
949
1558
  }
950
1559
  return parameters;
951
1560
  }
1561
+ getConstructorParameterToken(parameter) {
1562
+ if (parameter.tokenKey) {
1563
+ return Symbol.for(parameter.tokenKey);
1564
+ }
1565
+ if (parameter.type) {
1566
+ return this.getClass(parameter.type);
1567
+ }
1568
+ return void 0;
1569
+ }
952
1570
  getMethodParameters() {
953
1571
  const parameters = new Array();
954
1572
  for (const parameter of this.routeInfo.methodInfo.parameters) {
@@ -960,7 +1578,7 @@ class Resolver extends RequestProcessor {
960
1578
  else if (queryParams[parameter.name] !== void 0)
961
1579
  parameters.push(queryParams[parameter.name]);
962
1580
  else if (typeInfo && this.httpContext.request.body.json) {
963
- const context = new ModelBindingContext(parameter.type, typeInfo.props, this.httpContext.request.body.json);
1581
+ const context = new ModelBindingContext(parameter.type, typeInfo.props, this.httpContext.request.body.json, true);
964
1582
  parameters.push(context);
965
1583
  }
966
1584
  }
@@ -1013,14 +1631,14 @@ class Resolver extends RequestProcessor {
1013
1631
  if (onException) {
1014
1632
  const instance = this.resolveClass(onException);
1015
1633
  if (instance.onException)
1016
- result = instance.onException({
1634
+ result = this.normalizeHttpResponse(instance.onException({
1017
1635
  error: ex,
1018
1636
  request: this.httpContext.request,
1019
1637
  routeInfo: this.routeInfo
1020
- });
1638
+ }));
1021
1639
  } else
1022
1640
  result = new HttpException({
1023
- body: getPreResponseBody({ message: ex.message, stack: ex.stack }),
1641
+ body: { message: ex.message, stack: ex.stack },
1024
1642
  status: HttpStatusCode.serverError
1025
1643
  }).getResponse();
1026
1644
  }
@@ -1048,14 +1666,17 @@ class RequestHandler extends Resolver {
1048
1666
  if (onException) {
1049
1667
  const instance = this.resolveClass(onException);
1050
1668
  if (instance.onException)
1051
- return instance.onException({
1669
+ return this.normalizeHttpResponse(instance.onException({
1052
1670
  error: ex,
1053
1671
  request: this.httpContext.request,
1054
1672
  routeInfo: this.routeInfo
1055
- });
1673
+ }));
1056
1674
  } else
1057
1675
  return new HttpException({
1058
- body: ex,
1676
+ body: {
1677
+ message: ex?.message,
1678
+ stack: ex?.stack
1679
+ },
1059
1680
  status: HttpStatusCode.serverError
1060
1681
  });
1061
1682
  }
@@ -1068,46 +1689,59 @@ class HttpHandler {
1068
1689
  async processRequest(httpContext) {
1069
1690
  const requestProcessor = new RequestHandler(httpContext);
1070
1691
  const result = await requestProcessor.onRequest();
1071
- return result;
1692
+ if (result instanceof HttpResponse) {
1693
+ return result;
1694
+ }
1695
+ if (result instanceof HttpException) {
1696
+ return result.getResponse();
1697
+ }
1698
+ return new HttpResponse({ body: result });
1072
1699
  }
1073
1700
  }
1074
1701
 
1075
- class HttpRequest {
1076
- constructor(http) {
1077
- this.httpRequest = http;
1078
- }
1079
- get cookies() {
1080
- return this.httpRequest.cookies;
1081
- }
1082
- get url() {
1083
- return this.httpRequest.url;
1084
- }
1085
- get method() {
1086
- return this.httpRequest.method;
1087
- }
1088
- get headers() {
1089
- return this.httpRequest.headers;
1090
- }
1091
- get user() {
1092
- return null;
1093
- }
1094
- get body() {
1095
- return this.httpRequest.body;
1096
- }
1702
+ const STACK_PATH_REGEX = /\bat (?:(?:.+?) \()?(.+):(\d+):(\d+)\)?$/;
1703
+ function normalizeFilePath(filePath) {
1704
+ return path.resolve(filePath).replace(/\\/g, "/");
1097
1705
  }
1098
-
1099
- class HttpContext {
1100
- constructor(request, context) {
1101
- this.request = new HttpRequest(request);
1102
- this.response = new HttpResponse();
1103
- this.context = context;
1706
+ function isInternalRegistrationFrame(filePath) {
1707
+ const normalizedPath = normalizeFilePath(filePath);
1708
+ return normalizedPath.includes("/packages/core/decorators/") || normalizedPath.includes("/packages/core/functions/") || normalizedPath.includes("/packages/core/domain/di/") || normalizedPath.includes("/packages/core/const/") || normalizedPath.includes("/packages/core/dist/") || normalizedPath.includes("/node_modules/@nattyjs/core/dist/");
1709
+ }
1710
+ function getRegistrationSourceFile() {
1711
+ const stack = new Error().stack;
1712
+ return getRegistrationSourceFileFromStack(stack);
1713
+ }
1714
+ function getRegistrationSourceFileFromStack(stack) {
1715
+ if (!stack) {
1716
+ return void 0;
1104
1717
  }
1718
+ const lines = stack.split("\n").slice(1);
1719
+ for (const line of lines) {
1720
+ const match = line.trim().match(STACK_PATH_REGEX);
1721
+ if (!match) {
1722
+ continue;
1723
+ }
1724
+ const filePath = normalizeFilePath(match[1]);
1725
+ if (!isInternalRegistrationFrame(filePath)) {
1726
+ return filePath;
1727
+ }
1728
+ }
1729
+ return void 0;
1105
1730
  }
1106
1731
 
1107
1732
  function injectable(options = {}) {
1108
1733
  return (targetConstructor) => {
1109
- injectable$1()(targetConstructor);
1734
+ const lt = options.lifetime ?? Lifetime.Transient;
1735
+ const sourceFile = getRegistrationSourceFile();
1736
+ if (lt === Lifetime.Singleton)
1737
+ nattyServiceResolver.addSingleton(targetConstructor, void 0, void 0, sourceFile);
1738
+ else if (lt === Lifetime.Scoped)
1739
+ nattyServiceResolver.addScoped(targetConstructor, void 0, void 0, sourceFile);
1740
+ else
1741
+ nattyServiceResolver.addTransient(targetConstructor, void 0, void 0, sourceFile);
1110
1742
  commonContainer.setMetadata(targetConstructor.name, targetConstructor, "services");
1743
+ if (sourceFile)
1744
+ nattyServiceResolver.trackMetadataKey(sourceFile, targetConstructor.name);
1111
1745
  };
1112
1746
  }
1113
1747
 
@@ -1119,7 +1753,7 @@ function $request(request) {
1119
1753
 
1120
1754
  function filter(config) {
1121
1755
  return (targetConstructor) => {
1122
- injectable$1()(targetConstructor);
1756
+ injectable({ lifetime: Lifetime.Transient })(targetConstructor);
1123
1757
  };
1124
1758
  }
1125
1759
 
@@ -1148,63 +1782,257 @@ class BaseController {
1148
1782
  }
1149
1783
 
1150
1784
  class OkResult extends BaseResult {
1151
- constructor(value, response) {
1152
- super({ ...{ body: getResponseBodyObject(value) }, ...{ status: HttpStatusCode.success } }, response);
1153
- this.value = value;
1785
+ constructor(value, extras) {
1786
+ super(
1787
+ {
1788
+ status: HttpStatusCode.success,
1789
+ body: getResponseBodyObject(value ?? BLANK$1)
1790
+ },
1791
+ extras
1792
+ );
1154
1793
  }
1155
1794
  }
1156
- function ok(value, response) {
1157
- return new OkResult(value || BLANK$1, response);
1795
+ function ok(value, extras) {
1796
+ return new OkResult(value, extras);
1158
1797
  }
1159
1798
 
1160
- class NotFoundResult extends BaseResult {
1161
- constructor(response) {
1162
- super({ ...{ body: BLANK$1 }, ...{ status: HttpStatusCode.notFound } }, response);
1799
+ class CreatedResult extends BaseResult {
1800
+ constructor(value, extras) {
1801
+ super({ status: HttpStatusCode.created, body: getResponseBodyObject(value ?? BLANK$1) }, extras);
1163
1802
  }
1164
1803
  }
1165
- function notFound(response) {
1166
- return new NotFoundResult(response);
1804
+ function created(value, extras) {
1805
+ return new CreatedResult(value, extras);
1806
+ }
1807
+ function createdWith(location, value, extras) {
1808
+ const merged = {
1809
+ ...extras ?? {},
1810
+ headers: { ...extras?.headers ?? {}, Location: location }
1811
+ };
1812
+ return new CreatedResult(value, merged);
1813
+ }
1814
+ function createdAt(location, value, extras) {
1815
+ return createdWith(location, value, extras);
1816
+ }
1817
+
1818
+ class AcceptedResult extends BaseResult {
1819
+ constructor(value, extras) {
1820
+ super({ status: HttpStatusCode.accepted, body: getResponseBodyObject(value ?? BLANK$1) }, extras);
1821
+ this.value = value;
1822
+ }
1823
+ }
1824
+ function accepted(value, extras) {
1825
+ return new AcceptedResult(value, extras);
1167
1826
  }
1168
1827
 
1169
1828
  class NoContentResult extends BaseResult {
1170
- constructor(response) {
1171
- super({ ...{ body: BLANK$1 }, ...{ status: HttpStatusCode.noContent } }, response);
1829
+ constructor(extras) {
1830
+ super(
1831
+ {
1832
+ status: HttpStatusCode.noContent,
1833
+ body: void 0
1834
+ },
1835
+ extras
1836
+ );
1837
+ }
1838
+ }
1839
+ function noContent(extras) {
1840
+ return new NoContentResult(extras);
1841
+ }
1842
+
1843
+ class BadRequestResult extends BaseResult {
1844
+ constructor(value, extras) {
1845
+ super({ status: HttpStatusCode.badRequest, body: getResponseBodyObject(value ?? BLANK$1) }, extras);
1846
+ this.value = value;
1172
1847
  }
1173
1848
  }
1174
- function noContent(response) {
1175
- return new NoContentResult(response);
1849
+ function badRequest(value, extras) {
1850
+ return new BadRequestResult(value, extras);
1851
+ }
1852
+
1853
+ class UnAuthorizedResult extends BaseResult {
1854
+ constructor(value, extras) {
1855
+ super({ status: HttpStatusCode.unAuthorized, body: getResponseBodyObject(value ?? BLANK$1) }, extras);
1856
+ this.value = value;
1857
+ }
1858
+ }
1859
+ function unAuthorized(value, extras) {
1860
+ return new UnAuthorizedResult(value, extras);
1176
1861
  }
1177
1862
 
1178
1863
  class ForbiddenAccessInfoResult extends BaseResult {
1179
- constructor(value, response) {
1180
- super({ ...{ body: value }, ...{ status: HttpStatusCode.forbiddenAccess } }, response);
1864
+ constructor(value, extras) {
1865
+ super(
1866
+ {
1867
+ status: HttpStatusCode.forbiddenAccess,
1868
+ body: getResponseBodyObject(value ?? BLANK$1)
1869
+ },
1870
+ extras
1871
+ );
1181
1872
  }
1182
1873
  }
1183
- function forbiddenAccessInfo(value, response) {
1184
- return new ForbiddenAccessInfoResult(value, response);
1874
+ function forbiddenAccessInfo(value, extras) {
1875
+ return new ForbiddenAccessInfoResult(value, extras);
1876
+ }
1877
+ function forbiddenAccess(value, extras) {
1878
+ return new ForbiddenAccessInfoResult(value, extras);
1185
1879
  }
1186
1880
 
1187
- class CreatedResult extends BaseResult {
1188
- constructor(response) {
1189
- super({ ...{ body: BLANK$1 }, ...{ status: HttpStatusCode.created } }, response);
1881
+ class NotFoundResult extends BaseResult {
1882
+ constructor(value, extras) {
1883
+ super(
1884
+ {
1885
+ status: HttpStatusCode.notFound,
1886
+ body: getResponseBodyObject(value ?? BLANK$1)
1887
+ },
1888
+ extras
1889
+ );
1190
1890
  }
1191
1891
  }
1192
- function created(response) {
1193
- return new CreatedResult(response);
1892
+ function notFound(extras) {
1893
+ return new NotFoundResult(BLANK$1, extras);
1894
+ }
1895
+ function notFoundWith(value, extras) {
1896
+ return new NotFoundResult(value, extras);
1194
1897
  }
1195
1898
 
1196
- class BadRequestResult extends BaseResult {
1197
- constructor(value, response) {
1198
- super({ ...{ body: value }, ...{ status: HttpStatusCode.badRequest } }, response);
1899
+ class ConflictResult extends BaseResult {
1900
+ constructor(value, extras) {
1901
+ super({ status: HttpStatusCode.conflict, body: getResponseBodyObject(value ?? BLANK$1) }, extras);
1199
1902
  this.value = value;
1200
1903
  }
1201
1904
  }
1202
- function badRequest(value, response) {
1203
- return new BadRequestResult(value || BLANK$1, response);
1905
+ function conflict(value, extras) {
1906
+ return new ConflictResult(value, extras);
1204
1907
  }
1205
1908
 
1909
+ class UnprocessableEntityResult extends BaseResult {
1910
+ constructor(value, extras) {
1911
+ super({ status: HttpStatusCode.unprocessableEntity, body: getResponseBodyObject(value ?? BLANK$1) }, extras);
1912
+ this.value = value;
1913
+ }
1914
+ }
1915
+ function unprocessableEntity(value, extras) {
1916
+ return new UnprocessableEntityResult(value, extras);
1917
+ }
1918
+
1919
+ class TooManyRequestsResult extends BaseResult {
1920
+ constructor(value, retryAfterSeconds, extras) {
1921
+ const merged = {
1922
+ ...extras ?? {},
1923
+ headers: {
1924
+ ...extras?.headers ?? {},
1925
+ ...retryAfterSeconds != null ? { "Retry-After": String(retryAfterSeconds) } : {}
1926
+ }
1927
+ };
1928
+ super({ status: HttpStatusCode.tooManyRequests, body: getResponseBodyObject(value ?? BLANK$1) }, merged);
1929
+ this.value = value;
1930
+ }
1931
+ }
1932
+ function tooManyRequests(value, retryAfterSeconds, extras) {
1933
+ return new TooManyRequestsResult(value, retryAfterSeconds, extras);
1934
+ }
1935
+
1936
+ class RedirectResult extends BaseResult {
1937
+ constructor(location, permanent = false, extras) {
1938
+ const merged = {
1939
+ ...extras ?? {},
1940
+ headers: { ...extras?.headers ?? {}, Location: location }
1941
+ };
1942
+ super({ status: permanent ? HttpStatusCode.movedPermanently : HttpStatusCode.found, body: void 0 }, merged);
1943
+ }
1944
+ }
1945
+ function redirect(location, extras) {
1946
+ return new RedirectResult(location, false, extras);
1947
+ }
1948
+ function redirectPermanent(location, extras) {
1949
+ return new RedirectResult(location, true, extras);
1950
+ }
1951
+
1952
+ class ProblemResult extends BaseResult {
1953
+ constructor(problem2, extras) {
1954
+ const status = problem2.status ?? HttpStatusCode.serverError;
1955
+ const merged = {
1956
+ ...extras ?? {},
1957
+ headers: { ...extras?.headers ?? {}, "Content-Type": "application/problem+json; charset=utf-8" }
1958
+ };
1959
+ super({ status, body: { ...problem2, status } }, merged);
1960
+ }
1961
+ }
1962
+ function problem(problem2, extras) {
1963
+ return new ProblemResult(problem2, extras);
1964
+ }
1965
+ function validationProblem(errors, detail, extras) {
1966
+ return new ProblemResult(
1967
+ {
1968
+ type: "https://tools.ietf.org/html/rfc7231#section-6.5.1",
1969
+ title: "One or more validation errors occurred.",
1970
+ status: HttpStatusCode.badRequest,
1971
+ detail,
1972
+ errors
1973
+ },
1974
+ extras
1975
+ );
1976
+ }
1977
+
1978
+ class FileResult extends BaseResult {
1979
+ constructor(buffer, extras) {
1980
+ super({ status: HttpStatusCode.success, body: buffer, isBuffer: true }, extras);
1981
+ }
1982
+ }
1983
+ function file(buffer, extras) {
1984
+ return new FileResult(buffer, extras);
1985
+ }
1986
+
1987
+ class StatusCodeResult extends BaseResult {
1988
+ constructor(status, value, extras) {
1989
+ super(
1990
+ {
1991
+ status,
1992
+ body: getResponseBodyObject(value ?? BLANK$1)
1993
+ },
1994
+ extras
1995
+ );
1996
+ }
1997
+ }
1998
+ function statusCode(status, value, extras) {
1999
+ return new StatusCodeResult(status, value, extras);
2000
+ }
2001
+
2002
+ const Results = {
2003
+ // 2xx
2004
+ ok: (value, extras) => ok(value, extras),
2005
+ created: (value, extras) => created(value, extras),
2006
+ createdWith: (location, value, extras) => createdWith(location, value, extras),
2007
+ createdAt: (location, value, extras) => createdAt(location, value, extras),
2008
+ accepted: (value, extras) => accepted(value, extras),
2009
+ noContent: (extras) => noContent(extras),
2010
+ file: (buffer, extras) => file(buffer, extras),
2011
+ statusCode: (status, value, extras) => statusCode(status, value, extras),
2012
+ // 4xx
2013
+ badRequest: (value, extras) => badRequest(value, extras),
2014
+ notFound: (extras) => notFound(extras),
2015
+ notFoundWith: (value, extras) => notFoundWith(value, extras),
2016
+ unAuthorized: (value, extras) => unAuthorized(value, extras),
2017
+ forbid: (value, extras) => forbiddenAccess(value, extras),
2018
+ conflict: (value, extras) => conflict(value, extras),
2019
+ unprocessableEntity: (value, extras) => unprocessableEntity(value, extras),
2020
+ tooManyRequests: (value, retryAfterSeconds, extras) => tooManyRequests(value, retryAfterSeconds, extras),
2021
+ // 3xx
2022
+ redirect: (location, extras) => redirect(location, extras),
2023
+ redirectPermanent: (location, extras) => redirectPermanent(location, extras),
2024
+ // problem
2025
+ problem: (p, extras) => problem(p, extras),
2026
+ validationProblem: (errors, detail, extras) => validationProblem(errors, detail, extras)
2027
+ };
2028
+
1206
2029
  function base(params, type, additionaConfig) {
1207
- decoratorStateContainer.register(params, type, additionaConfig);
2030
+ decoratorStateContainer.register(
2031
+ params,
2032
+ type,
2033
+ additionaConfig,
2034
+ getRegistrationSourceFile()
2035
+ );
1208
2036
  }
1209
2037
 
1210
2038
  function useFilter(config) {
@@ -1229,4 +2057,121 @@ function authenticationOnly() {
1229
2057
  };
1230
2058
  }
1231
2059
 
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 };
2060
+ function onException(exceptionFilter) {
2061
+ return function(target, propertyKey, descriptor) {
2062
+ base({ target, propertyKey, descriptor }, DecoratorType.onException, exceptionFilter);
2063
+ };
2064
+ }
2065
+
2066
+ function setEnvInfo(envTsDefinition, envValueInfo) {
2067
+ if (envTsDefinition && envValueInfo) {
2068
+ commonContainer.setEnvTsDefinition(envTsDefinition);
2069
+ Object.keys(envValueInfo).forEach((key) => {
2070
+ if (envValueInfo[key])
2071
+ process.env[key] = envValueInfo[key];
2072
+ });
2073
+ }
2074
+ }
2075
+
2076
+ function authorize(permission) {
2077
+ return function(target, propertyKey, descriptor) {
2078
+ base({
2079
+ target,
2080
+ propertyKey,
2081
+ descriptor
2082
+ }, DecoratorType.authorize, permission);
2083
+ };
2084
+ }
2085
+
2086
+ function scoped(token) {
2087
+ return injectable({ lifetime: Lifetime.Scoped, token });
2088
+ }
2089
+
2090
+ function transient(token) {
2091
+ return injectable({ lifetime: Lifetime.Transient, token });
2092
+ }
2093
+
2094
+ function singleton(token) {
2095
+ return injectable({ lifetime: Lifetime.Singleton, token });
2096
+ }
2097
+
2098
+ function getTokenTypeName(token) {
2099
+ if (typeof token !== "symbol") {
2100
+ return void 0;
2101
+ }
2102
+ const key = Symbol.keyFor(token);
2103
+ if (!key) {
2104
+ return void 0;
2105
+ }
2106
+ const separatorIndex = key.lastIndexOf("#");
2107
+ if (separatorIndex < 0 || separatorIndex === key.length - 1) {
2108
+ return void 0;
2109
+ }
2110
+ return key.slice(separatorIndex + 1);
2111
+ }
2112
+
2113
+ function registerLifetime(targetConstructor, sourceFile, lifetime) {
2114
+ if (lifetime === Lifetime.Singleton) {
2115
+ nattyServiceResolver.addSingleton(targetConstructor, void 0, void 0, sourceFile);
2116
+ return;
2117
+ }
2118
+ if (lifetime === Lifetime.Scoped) {
2119
+ nattyServiceResolver.addScoped(targetConstructor, void 0, void 0, sourceFile);
2120
+ return;
2121
+ }
2122
+ if (lifetime === Lifetime.Transient) {
2123
+ nattyServiceResolver.addTransient(targetConstructor, void 0, void 0, sourceFile);
2124
+ }
2125
+ }
2126
+ function registerAliasTypeName(token, sourceFile) {
2127
+ const typeName = getTokenTypeName(token);
2128
+ if (typeName) {
2129
+ commonContainer.setMetadata(typeName, token, "services");
2130
+ if (sourceFile)
2131
+ nattyServiceResolver.trackMetadataKey(sourceFile, typeName);
2132
+ }
2133
+ }
2134
+ function registerDiToken(targetConstructor, token, options = {}) {
2135
+ const sourceFile = getRegistrationSourceFile();
2136
+ commonContainer.setMetadata(targetConstructor.name, targetConstructor, "services");
2137
+ if (sourceFile)
2138
+ nattyServiceResolver.trackMetadataKey(sourceFile, targetConstructor.name);
2139
+ registerAliasTypeName(token, sourceFile);
2140
+ registerLifetime(targetConstructor, sourceFile, options.lifetime);
2141
+ nattyServiceResolver.addTransient(
2142
+ token,
2143
+ void 0,
2144
+ (serviceProvider) => serviceProvider.get(targetConstructor),
2145
+ sourceFile
2146
+ );
2147
+ }
2148
+
2149
+ function di(token, options = {}) {
2150
+ return (targetConstructor) => {
2151
+ registerDiToken(targetConstructor, token, options);
2152
+ };
2153
+ }
2154
+
2155
+ function createServiceScope() {
2156
+ return nattyServiceResolver.createScope();
2157
+ }
2158
+
2159
+ function clearHotReloadRegistrations(files) {
2160
+ for (const filePath of files || []) {
2161
+ const result = nattyServiceResolver.clearRegistrationsFromSource(filePath);
2162
+ decoratorStateContainer.clearFromSource(filePath);
2163
+ for (const metadataKey of result.metadataKeys) {
2164
+ commonContainer.deleteMetadataValue(metadataKey, "services");
2165
+ }
2166
+ }
2167
+ }
2168
+
2169
+ function clearHotReloadControllerMetadata(controllerNames) {
2170
+ decoratorStateContainer.clearControllers(controllerNames || []);
2171
+ }
2172
+
2173
+ function replaceRoutes(manifest) {
2174
+ nattyContainer.replaceRoutes(manifest);
2175
+ }
2176
+
2177
+ export { $request, AbstractModelState, AcceptedException, AcceptedResult, BadRequestResult, BaseController, BaseResult, ConflictResult, CreateProblemDetail, CreatedResult, Delete, FileResult, ForbiddenAccessException, ForbiddenAccessInfoResult, HttpBadRequestException, HttpConflictException, HttpContext, HttpException, HttpHandler, HttpNotFoundException, HttpResponse, HttpStatusCode, HttpUnprocessableEntityException, Lifetime, ModelBindingContext, NoContentResult, NotFoundResult, OkResult, ProblemDetailsException, ProblemResult, RedirectException, RedirectPermanentException, RedirectResult, Results, RunOn, TooManyRequestsException, TooManyRequestsResult, UnAuthorizedResult, UnauthorizedAccessException, UnprocessableEntityResult, ValidationProblemDetailsException, accepted, anonymous, authenticationOnly, authorize, badRequest, clearHotReloadControllerMetadata, clearHotReloadRegistrations, conflict, createServiceScope, created, createdAt, createdWith, defineNattyConfig, di, entityContainer, file, filter, forbiddenAccess, forbiddenAccessInfo, get, init, injectable, noContent, notFound, notFoundWith, ok, onException, post, problem, put, redirect, redirectPermanent, registerDecorator, replaceRoutes, route, scoped, setEnvInfo, singleton, tooManyRequests, transient, unAuthorized, unprocessableEntity, useFilter, validationProblem };