@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.cjs +1204 -209
- package/dist/index.d.ts +307 -53
- package/dist/index.mjs +1156 -211
- package/package.json +3 -3
package/dist/index.mjs
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { commonContainer, isObject, List, BLANK as BLANK$1
|
|
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,
|
|
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.
|
|
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
|
-
|
|
118
|
-
|
|
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
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
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 =
|
|
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
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
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["
|
|
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["
|
|
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,
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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(
|
|
568
|
-
if (
|
|
569
|
-
const classInfo = resolver();
|
|
570
|
-
|
|
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
|
|
608
|
-
return nattyContainer.
|
|
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
|
-
|
|
622
|
-
const requestPathname = this.getRequestPathname();
|
|
623
|
-
for (const
|
|
624
|
-
const
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
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
|
|
651
|
-
}
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
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
|
|
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
|
|
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
|
|
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(
|
|
941
|
-
return
|
|
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
|
|
947
|
-
if (
|
|
948
|
-
parameters.push(this.resolveClass(
|
|
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:
|
|
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:
|
|
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
|
-
|
|
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
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
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
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
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
|
-
|
|
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
|
|
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,
|
|
1152
|
-
super(
|
|
1153
|
-
|
|
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,
|
|
1157
|
-
return new OkResult(value
|
|
1795
|
+
function ok(value, extras) {
|
|
1796
|
+
return new OkResult(value, extras);
|
|
1158
1797
|
}
|
|
1159
1798
|
|
|
1160
|
-
class
|
|
1161
|
-
constructor(
|
|
1162
|
-
super({
|
|
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
|
|
1166
|
-
return new
|
|
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(
|
|
1171
|
-
super(
|
|
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
|
|
1175
|
-
return new
|
|
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,
|
|
1180
|
-
super(
|
|
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,
|
|
1184
|
-
return new ForbiddenAccessInfoResult(value,
|
|
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
|
|
1188
|
-
constructor(
|
|
1189
|
-
super(
|
|
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
|
|
1193
|
-
return new
|
|
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
|
|
1197
|
-
constructor(value,
|
|
1198
|
-
super({
|
|
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
|
|
1203
|
-
return new
|
|
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(
|
|
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
|
-
|
|
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 };
|