@decohere/http 0.0.1-dev.20261105.97 → 0.0.1-dev.20261106.132
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/es6/src/routing/services/route-registry.js +5 -0
- package/package.json +9 -14
- package/commonjs/src/app/app.js +0 -166
- package/commonjs/src/app/index.js +0 -19
- package/commonjs/src/app/module.js +0 -17
- package/commonjs/src/auth/ability.js +0 -54
- package/commonjs/src/auth/auth.config.js +0 -23
- package/commonjs/src/auth/auth.extensions.js +0 -106
- package/commonjs/src/auth/auth.types.js +0 -65
- package/commonjs/src/auth/casl-authorization.service.js +0 -36
- package/commonjs/src/auth/index.js +0 -23
- package/commonjs/src/auth/principal-provider.js +0 -19
- package/commonjs/src/auth/safe-outbound-url.js +0 -102
- package/commonjs/src/auth/token-service.js +0 -151
- package/commonjs/src/builder/builder.js +0 -95
- package/commonjs/src/builder/index.js +0 -18
- package/commonjs/src/const.js +0 -86
- package/commonjs/src/health/health.controller.js +0 -75
- package/commonjs/src/health/health.extensions.js +0 -50
- package/commonjs/src/health/index.js +0 -21
- package/commonjs/src/health/readiness.service.js +0 -67
- package/commonjs/src/health/readiness.types.js +0 -7
- package/commonjs/src/hosting/hosted-service-host.js +0 -88
- package/commonjs/src/hosting/hosted-service.js +0 -11
- package/commonjs/src/hosting/index.js +0 -19
- package/commonjs/src/http/http.compression.js +0 -27
- package/commonjs/src/http/http.extensions.js +0 -269
- package/commonjs/src/http/http.options.js +0 -26
- package/commonjs/src/http/http.options.store.js +0 -20
- package/commonjs/src/http/http.security.js +0 -27
- package/commonjs/src/http/http.static.js +0 -248
- package/commonjs/src/http/index.js +0 -22
- package/commonjs/src/index.js +0 -27
- package/commonjs/src/openapi/index.js +0 -45
- package/commonjs/src/openapi/openapi.generator.js +0 -103
- package/commonjs/src/openapi/openapi.types.js +0 -4
- package/commonjs/src/openapi/request-schema.js +0 -187
- package/commonjs/src/openapi/request-validator.js +0 -85
- package/commonjs/src/providers/fastify.provider.js +0 -391
- package/commonjs/src/providers/index.js +0 -18
- package/commonjs/src/routing/decorators/argument.decorator.js +0 -49
- package/commonjs/src/routing/decorators/auth.decorator.js +0 -70
- package/commonjs/src/routing/decorators/controller.decorator.js +0 -16
- package/commonjs/src/routing/decorators/field.decorator.js +0 -25
- package/commonjs/src/routing/decorators/index.js +0 -24
- package/commonjs/src/routing/decorators/request.decorator.js +0 -30
- package/commonjs/src/routing/decorators/response.decorator.js +0 -34
- package/commonjs/src/routing/decorators/route.decorator.js +0 -67
- package/commonjs/src/routing/filters/exception-filter-host.js +0 -145
- package/commonjs/src/routing/filters/exception-filter.js +0 -28
- package/commonjs/src/routing/http.types.js +0 -99
- package/commonjs/src/routing/index.js +0 -25
- package/commonjs/src/routing/middleware/http-access-log.middleware.js +0 -47
- package/commonjs/src/routing/middleware/middleware.js +0 -20
- package/commonjs/src/routing/middleware/request-id.js +0 -30
- package/commonjs/src/routing/path.util.js +0 -51
- package/commonjs/src/routing/pipes/index.js +0 -18
- package/commonjs/src/routing/pipes/pipes.js +0 -145
- package/commonjs/src/routing/services/index.js +0 -18
- package/commonjs/src/routing/services/route-argument-resolver.js +0 -77
- package/commonjs/src/routing/services/route-registry.js +0 -194
- package/commonjs/src/routing/services/router.js +0 -158
- package/commonjs/src/sse/index.js +0 -26
- package/commonjs/src/sse/sse.decorator.js +0 -42
- package/commonjs/src/sse/sse.encoder.js +0 -73
- package/commonjs/src/sse/sse.types.js +0 -36
- package/commonjs/src/sse/sse.wire.js +0 -40
- package/commonjs/src/testing/boot-module.js +0 -38
- package/commonjs/src/testing/index.js +0 -20
- package/commonjs/src/testing/sse-reader.js +0 -98
|
@@ -1,145 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", {
|
|
3
|
-
value: true
|
|
4
|
-
});
|
|
5
|
-
function _export(target, all) {
|
|
6
|
-
for(var name in all)Object.defineProperty(target, name, {
|
|
7
|
-
enumerable: true,
|
|
8
|
-
get: Object.getOwnPropertyDescriptor(all, name).get
|
|
9
|
-
});
|
|
10
|
-
}
|
|
11
|
-
_export(exports, {
|
|
12
|
-
get BooleanPipe () {
|
|
13
|
-
return BooleanPipe;
|
|
14
|
-
},
|
|
15
|
-
get DatePipe () {
|
|
16
|
-
return DatePipe;
|
|
17
|
-
},
|
|
18
|
-
get JsonPipe () {
|
|
19
|
-
return JsonPipe;
|
|
20
|
-
},
|
|
21
|
-
get NumberPipe () {
|
|
22
|
-
return NumberPipe;
|
|
23
|
-
},
|
|
24
|
-
get Pipe () {
|
|
25
|
-
return Pipe;
|
|
26
|
-
}
|
|
27
|
-
});
|
|
28
|
-
const _reflection = require("@decohere/core/reflection");
|
|
29
|
-
const _httptypes = require("../http.types.js");
|
|
30
|
-
const invalidBooleanIssue = {
|
|
31
|
-
path: "/value",
|
|
32
|
-
message: "Invalid boolean",
|
|
33
|
-
keyword: "type"
|
|
34
|
-
};
|
|
35
|
-
const invalidNumberIssue = {
|
|
36
|
-
path: "/value",
|
|
37
|
-
message: "Invalid number",
|
|
38
|
-
keyword: "type"
|
|
39
|
-
};
|
|
40
|
-
const invalidDateIssue = {
|
|
41
|
-
path: "/value",
|
|
42
|
-
message: "Invalid date",
|
|
43
|
-
keyword: "format"
|
|
44
|
-
};
|
|
45
|
-
class Pipe {
|
|
46
|
-
}
|
|
47
|
-
class BooleanPipe extends Pipe {
|
|
48
|
-
transform(value, _context) {
|
|
49
|
-
if (typeof value === "boolean") {
|
|
50
|
-
return value;
|
|
51
|
-
}
|
|
52
|
-
const normalized = typeof value === "string" ? value.trim().toLowerCase() : value;
|
|
53
|
-
switch(normalized){
|
|
54
|
-
case "true":
|
|
55
|
-
case "1":
|
|
56
|
-
case 1:
|
|
57
|
-
return true;
|
|
58
|
-
case "false":
|
|
59
|
-
case "0":
|
|
60
|
-
case 0:
|
|
61
|
-
return false;
|
|
62
|
-
default:
|
|
63
|
-
throw new _httptypes.ValidationError("Validation failed", [
|
|
64
|
-
invalidBooleanIssue
|
|
65
|
-
]);
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
class DatePipe extends Pipe {
|
|
70
|
-
transform(value, _context) {
|
|
71
|
-
if (value instanceof Date) {
|
|
72
|
-
if (Number.isNaN(value.getTime())) {
|
|
73
|
-
throw new _httptypes.ValidationError("Validation failed", [
|
|
74
|
-
invalidDateIssue
|
|
75
|
-
]);
|
|
76
|
-
}
|
|
77
|
-
return value;
|
|
78
|
-
}
|
|
79
|
-
if (typeof value === "string") {
|
|
80
|
-
const trimmed = value.trim();
|
|
81
|
-
if (trimmed === "") {
|
|
82
|
-
throw new _httptypes.ValidationError("Validation failed", [
|
|
83
|
-
invalidDateIssue
|
|
84
|
-
]);
|
|
85
|
-
}
|
|
86
|
-
if (/^-?\d+(\.\d+)?$/.test(trimmed)) {
|
|
87
|
-
return new Date(Number(trimmed));
|
|
88
|
-
}
|
|
89
|
-
const parsed = new Date(trimmed);
|
|
90
|
-
if (Number.isNaN(parsed.getTime())) {
|
|
91
|
-
throw new _httptypes.ValidationError("Validation failed", [
|
|
92
|
-
invalidDateIssue
|
|
93
|
-
]);
|
|
94
|
-
}
|
|
95
|
-
return parsed;
|
|
96
|
-
}
|
|
97
|
-
if (typeof value === "number" && !Number.isNaN(value)) {
|
|
98
|
-
return new Date(value);
|
|
99
|
-
}
|
|
100
|
-
throw new _httptypes.ValidationError("Validation failed", [
|
|
101
|
-
invalidDateIssue
|
|
102
|
-
]);
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
class JsonPipe extends Pipe {
|
|
106
|
-
transform(value, context) {
|
|
107
|
-
try {
|
|
108
|
-
const obj = JSON.parse(value, (fieldKey, fieldValue)=>{
|
|
109
|
-
const val = this.reviver(context?.fieldTypes, fieldKey, fieldValue);
|
|
110
|
-
return val;
|
|
111
|
-
});
|
|
112
|
-
return obj;
|
|
113
|
-
} catch (err) {
|
|
114
|
-
throw new _httptypes.TransformError(`Error parsing json ${err}`);
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
reviver(fieldTypes, fieldKey, value) {
|
|
118
|
-
if (!fieldKey) {
|
|
119
|
-
return value;
|
|
120
|
-
}
|
|
121
|
-
if (!fieldTypes) {
|
|
122
|
-
return value;
|
|
123
|
-
}
|
|
124
|
-
const fieldType = fieldTypes[fieldKey];
|
|
125
|
-
if (fieldType == null) {
|
|
126
|
-
return value;
|
|
127
|
-
}
|
|
128
|
-
if (!(0, _reflection.isDateType)(fieldType)) {
|
|
129
|
-
return value;
|
|
130
|
-
}
|
|
131
|
-
return new Date(value.toString());
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
class NumberPipe extends Pipe {
|
|
135
|
-
/** Finite numbers only. Blank or non-numeric input is a 400, not `undefined`. */ transform(value, _context) {
|
|
136
|
-
const text = typeof value === "number" ? null : String(value).trim();
|
|
137
|
-
const number = text == null ? value : text === "" ? Number.NaN : Number(text);
|
|
138
|
-
if (!Number.isFinite(number)) {
|
|
139
|
-
throw new _httptypes.ValidationError("Validation failed", [
|
|
140
|
-
invalidNumberIssue
|
|
141
|
-
]);
|
|
142
|
-
}
|
|
143
|
-
return number;
|
|
144
|
-
}
|
|
145
|
-
}
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", {
|
|
3
|
-
value: true
|
|
4
|
-
});
|
|
5
|
-
_export_star(require("./router.js"), exports);
|
|
6
|
-
function _export_star(from, to) {
|
|
7
|
-
Object.keys(from).forEach(function(k) {
|
|
8
|
-
if (k !== "default" && !Object.prototype.hasOwnProperty.call(to, k)) {
|
|
9
|
-
Object.defineProperty(to, k, {
|
|
10
|
-
enumerable: true,
|
|
11
|
-
get: function() {
|
|
12
|
-
return from[k];
|
|
13
|
-
}
|
|
14
|
-
});
|
|
15
|
-
}
|
|
16
|
-
});
|
|
17
|
-
return from;
|
|
18
|
-
}
|
|
@@ -1,77 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", {
|
|
3
|
-
value: true
|
|
4
|
-
});
|
|
5
|
-
Object.defineProperty(exports, "RouteArgumentResolve", {
|
|
6
|
-
enumerable: true,
|
|
7
|
-
get: function() {
|
|
8
|
-
return RouteArgumentResolve;
|
|
9
|
-
}
|
|
10
|
-
});
|
|
11
|
-
const _reflection = require("@decohere/core/reflection");
|
|
12
|
-
class RouteArgumentResolve {
|
|
13
|
-
resolveArguments(httpRequest, routeArgs, principalProvider) {
|
|
14
|
-
const argsValues = [];
|
|
15
|
-
for (const [argIdx, routeArg] of routeArgs.entries()){
|
|
16
|
-
const val = this.getValue(httpRequest, routeArg, principalProvider);
|
|
17
|
-
argsValues[argIdx] = val;
|
|
18
|
-
}
|
|
19
|
-
return argsValues;
|
|
20
|
-
}
|
|
21
|
-
getValue(httpRequest, routeArg, principalProvider) {
|
|
22
|
-
switch(routeArg.argumentLocation){
|
|
23
|
-
case "principal":
|
|
24
|
-
return principalProvider?.currentPrincipal;
|
|
25
|
-
case "signal":
|
|
26
|
-
return httpRequest.signal;
|
|
27
|
-
case "lastEventId":
|
|
28
|
-
return httpRequest.headers["last-event-id"];
|
|
29
|
-
}
|
|
30
|
-
const value = (0, _reflection.isPrimitiveOrDateType)(routeArg.argumentType) ? this.mapPrimitive(httpRequest, routeArg) : this.mapObject(httpRequest, routeArg);
|
|
31
|
-
return value;
|
|
32
|
-
}
|
|
33
|
-
mapPrimitive(httpRequest, routeArg) {
|
|
34
|
-
const { argumentLocation, mappingKey, pipes } = routeArg;
|
|
35
|
-
const value = httpRequest[argumentLocation][mappingKey];
|
|
36
|
-
const tranformedValue = this.transform(value, pipes, routeArg.fieldMap);
|
|
37
|
-
return tranformedValue;
|
|
38
|
-
}
|
|
39
|
-
mapObject(httpRequest, routeArg) {
|
|
40
|
-
const { argumentType } = routeArg;
|
|
41
|
-
const inst = new argumentType();
|
|
42
|
-
const { fieldMap } = routeArg;
|
|
43
|
-
for (const [fieldKey, meta] of fieldMap.entries()){
|
|
44
|
-
const { fieldLocation, mappingKey, pipes } = meta;
|
|
45
|
-
const value = httpRequest[fieldLocation][mappingKey];
|
|
46
|
-
const transformedValue = this.transform(value, pipes, fieldMap);
|
|
47
|
-
inst[fieldKey] = transformedValue;
|
|
48
|
-
}
|
|
49
|
-
return inst;
|
|
50
|
-
}
|
|
51
|
-
transform(value, pipes, fieldMap) {
|
|
52
|
-
let transformed = value;
|
|
53
|
-
if (value == null) {
|
|
54
|
-
return value;
|
|
55
|
-
}
|
|
56
|
-
const context = this.pipeContext(fieldMap);
|
|
57
|
-
for (const pipeConstructor of pipes ?? []){
|
|
58
|
-
const pipe = new pipeConstructor();
|
|
59
|
-
transformed = pipe.transform(transformed, context);
|
|
60
|
-
}
|
|
61
|
-
return transformed;
|
|
62
|
-
}
|
|
63
|
-
pipeContext(fieldMap) {
|
|
64
|
-
if (fieldMap == null || fieldMap.size === 0) {
|
|
65
|
-
return undefined;
|
|
66
|
-
}
|
|
67
|
-
const fieldTypes = {};
|
|
68
|
-
for (const [key, meta] of fieldMap.entries()){
|
|
69
|
-
if (meta.fieldType != null) {
|
|
70
|
-
fieldTypes[key] = meta.fieldType;
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
return Object.keys(fieldTypes).length > 0 ? {
|
|
74
|
-
fieldTypes
|
|
75
|
-
} : undefined;
|
|
76
|
-
}
|
|
77
|
-
}
|
|
@@ -1,194 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", {
|
|
3
|
-
value: true
|
|
4
|
-
});
|
|
5
|
-
Object.defineProperty(exports, "RouteRegistry", {
|
|
6
|
-
enumerable: true,
|
|
7
|
-
get: function() {
|
|
8
|
-
return RouteRegistry;
|
|
9
|
-
}
|
|
10
|
-
});
|
|
11
|
-
const _decorators = require("@decohere/crucible/decorators");
|
|
12
|
-
const _reflection = require("@decohere/core/reflection");
|
|
13
|
-
const _const = require("../../const.js");
|
|
14
|
-
const _httptypes = require("../http.types.js");
|
|
15
|
-
const _pathutil = require("../path.util.js");
|
|
16
|
-
const _pipes = require("../pipes/pipes.js");
|
|
17
|
-
function _ts_decorate(decorators, target, key, desc) {
|
|
18
|
-
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
19
|
-
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
20
|
-
else for(var i = decorators.length - 1; i >= 0; i--)if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
21
|
-
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
22
|
-
}
|
|
23
|
-
function _ts_metadata(k, v) {
|
|
24
|
-
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
25
|
-
}
|
|
26
|
-
function _ts_param(paramIndex, decorator) {
|
|
27
|
-
return function(target, key) {
|
|
28
|
-
decorator(target, key, paramIndex);
|
|
29
|
-
};
|
|
30
|
-
}
|
|
31
|
-
class RouteRegistry {
|
|
32
|
-
routeMap = new Map();
|
|
33
|
-
httpOptions;
|
|
34
|
-
constructor(httpOptions){
|
|
35
|
-
this.httpOptions = httpOptions;
|
|
36
|
-
}
|
|
37
|
-
addController(controller) {
|
|
38
|
-
const funcs = (0, _reflection.getFunctions)(controller);
|
|
39
|
-
for (const func of funcs){
|
|
40
|
-
this.addControllerRoute(controller, func);
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
get routes() {
|
|
44
|
-
return this.routeMap.values();
|
|
45
|
-
}
|
|
46
|
-
getRoute(key) {
|
|
47
|
-
return this.routeMap.get(key);
|
|
48
|
-
}
|
|
49
|
-
addControllerRoute(controller, prop) {
|
|
50
|
-
const func = prop.value;
|
|
51
|
-
const handler = func.name;
|
|
52
|
-
const route = (0, _reflection.getMeta)(_const.MetadataKeys.route, func);
|
|
53
|
-
if (route == null) {
|
|
54
|
-
return;
|
|
55
|
-
}
|
|
56
|
-
const globalPrefix = this.takesGlobalPrefix(controller) ? (0, _pathutil.normalizePrefix)(this.httpOptions?.globalPrefix) : "";
|
|
57
|
-
const controllerPrefix = this.getControllerPrefix(controller);
|
|
58
|
-
const path = (0, _pathutil.applyGlobalPrefix)(globalPrefix, (0, _pathutil.joinRoutePath)(controllerPrefix, route.path));
|
|
59
|
-
const { method } = route;
|
|
60
|
-
const key = `${method}|${path}`;
|
|
61
|
-
const existing = this.routeMap.get(key);
|
|
62
|
-
if (existing != null) {
|
|
63
|
-
// Last one used to win silently, leaving the other handler unreachable.
|
|
64
|
-
throw new _httptypes.RouteConfigurationError(`Duplicate route ${method.toUpperCase()} ${path}: ${existing.controller.name}.${existing.handler} and ${controller.name}.${handler}`);
|
|
65
|
-
}
|
|
66
|
-
const routeArgs = this.getRouteArguments(controller, func);
|
|
67
|
-
const httpCode = (0, _reflection.getMeta)(_const.MetadataKeys.httpCode, func);
|
|
68
|
-
const headers = (0, _reflection.getMeta)(_const.MetadataKeys.headers, func);
|
|
69
|
-
const sse = (0, _reflection.getMeta)(_const.MetadataKeys.sse, func);
|
|
70
|
-
this.routeMap.set(key, {
|
|
71
|
-
key,
|
|
72
|
-
controller,
|
|
73
|
-
handler,
|
|
74
|
-
path,
|
|
75
|
-
method,
|
|
76
|
-
routeArgs,
|
|
77
|
-
httpCode,
|
|
78
|
-
headers,
|
|
79
|
-
sse
|
|
80
|
-
});
|
|
81
|
-
}
|
|
82
|
-
/** `@Controller({ globalPrefix: false })` opts out, unless the app put it back under. */ takesGlobalPrefix(controller) {
|
|
83
|
-
const meta = (0, _reflection.getMeta)(_const.MetadataKeys.controller, controller.prototype);
|
|
84
|
-
return meta?.globalPrefix !== false || this.httpOptions?.underGlobalPrefix.has(controller) === true;
|
|
85
|
-
}
|
|
86
|
-
getControllerPrefix(controller) {
|
|
87
|
-
const meta = (0, _reflection.getMeta)(_const.MetadataKeys.controller, controller.prototype);
|
|
88
|
-
if (meta?.prefix) {
|
|
89
|
-
return (0, _pathutil.normalizePrefix)(meta.prefix);
|
|
90
|
-
}
|
|
91
|
-
const controllerName = controller.name.toLowerCase().replace("controller", "");
|
|
92
|
-
return controllerName !== "" ? `/${controllerName}` : "";
|
|
93
|
-
}
|
|
94
|
-
getRouteArguments(controller, handler) {
|
|
95
|
-
const types = (0, _reflection.getFunctionArgs)(controller.prototype, handler) ?? [];
|
|
96
|
-
const args = [];
|
|
97
|
-
for (const [argumentIndex, argumentType] of types.entries()){
|
|
98
|
-
const argMeta = (0, _reflection.getArgumentMeta)(_const.MetadataKeys.parameter, handler, argumentIndex);
|
|
99
|
-
if (argMeta == null && !(0, _reflection.isConstructable)(argumentType)) {
|
|
100
|
-
continue;
|
|
101
|
-
}
|
|
102
|
-
args.push(this.getRouteArgument(argumentType, argumentIndex, handler));
|
|
103
|
-
}
|
|
104
|
-
return args;
|
|
105
|
-
}
|
|
106
|
-
getRouteArgument(argumentType, argumentIndex, handler) {
|
|
107
|
-
const argMeta = (0, _reflection.getArgumentMeta)(_const.MetadataKeys.parameter, handler, argumentIndex);
|
|
108
|
-
// @CurrentUser() / interface params are Object (or other non-DTO types) — no field map.
|
|
109
|
-
if (isInjectedPart(argMeta?.argumentLocation) || !(0, _reflection.isConstructable)(argumentType)) {
|
|
110
|
-
return {
|
|
111
|
-
...argMeta,
|
|
112
|
-
pipes: [],
|
|
113
|
-
argumentIndex,
|
|
114
|
-
argumentType: (0, _reflection.isConstructable)(argumentType) ? argumentType : Object,
|
|
115
|
-
fieldMap: new Map()
|
|
116
|
-
};
|
|
117
|
-
}
|
|
118
|
-
const pipes = argMeta != null ? this.getDefaultPipes(argumentType) : [];
|
|
119
|
-
const fieldMap = this.getFields(argumentType, argumentIndex, argMeta);
|
|
120
|
-
const routeArg = {
|
|
121
|
-
...argMeta,
|
|
122
|
-
pipes,
|
|
123
|
-
argumentIndex,
|
|
124
|
-
argumentType,
|
|
125
|
-
fieldMap
|
|
126
|
-
};
|
|
127
|
-
return routeArg;
|
|
128
|
-
}
|
|
129
|
-
getFields(argumentType, argumentIndex, argMeta) {
|
|
130
|
-
const inst = new argumentType();
|
|
131
|
-
const fields = (0, _reflection.getFields)(inst);
|
|
132
|
-
const fieldMap = (0, _reflection.getMeta)(_const.MetadataKeys.fieldMap, argumentType.prototype) ?? new Map();
|
|
133
|
-
for (const field of fields){
|
|
134
|
-
const fieldKey = field.key;
|
|
135
|
-
const fieldMeta = fieldMap.get(field.key);
|
|
136
|
-
if (fieldMeta && fieldMeta.fieldLocation == null && argMeta?.argumentLocation == null) {
|
|
137
|
-
throw new _httptypes.RouteConfigurationError(`field ${fieldMeta.fieldKey} must specify a location because location was not specified for the route argument at idx ${argumentIndex}`);
|
|
138
|
-
}
|
|
139
|
-
const enriched = this.enrichField(field, fieldMeta, argMeta);
|
|
140
|
-
fieldMap.set(fieldKey, enriched);
|
|
141
|
-
}
|
|
142
|
-
return fieldMap;
|
|
143
|
-
}
|
|
144
|
-
enrichField(field, fieldMeta, argMeta) {
|
|
145
|
-
const fieldLocation = fieldMeta?.fieldLocation ?? argMeta.argumentLocation;
|
|
146
|
-
const pipes = this.getDefaultPipes(fieldMeta?.fieldType);
|
|
147
|
-
return {
|
|
148
|
-
fieldKey: field.key,
|
|
149
|
-
mappingKey: field.key,
|
|
150
|
-
...fieldMeta,
|
|
151
|
-
fieldLocation,
|
|
152
|
-
pipes
|
|
153
|
-
};
|
|
154
|
-
}
|
|
155
|
-
getDefaultPipes(argumentType) {
|
|
156
|
-
if (argumentType == null) {
|
|
157
|
-
return [];
|
|
158
|
-
}
|
|
159
|
-
let pipe;
|
|
160
|
-
if (!(0, _reflection.isPrimitiveOrDateType)(argumentType)) {
|
|
161
|
-
pipe = _pipes.JsonPipe;
|
|
162
|
-
return [
|
|
163
|
-
pipe
|
|
164
|
-
];
|
|
165
|
-
}
|
|
166
|
-
switch(argumentType.name){
|
|
167
|
-
case "Number":
|
|
168
|
-
pipe = _pipes.NumberPipe;
|
|
169
|
-
break;
|
|
170
|
-
case "Boolean":
|
|
171
|
-
pipe = _pipes.BooleanPipe;
|
|
172
|
-
break;
|
|
173
|
-
case "Date":
|
|
174
|
-
pipe = _pipes.DatePipe;
|
|
175
|
-
break;
|
|
176
|
-
default:
|
|
177
|
-
return [];
|
|
178
|
-
}
|
|
179
|
-
return [
|
|
180
|
-
pipe
|
|
181
|
-
];
|
|
182
|
-
}
|
|
183
|
-
}
|
|
184
|
-
RouteRegistry = _ts_decorate([
|
|
185
|
-
(0, _decorators.injectable)(),
|
|
186
|
-
_ts_param(0, (0, _decorators.inject)(_const.HttpOptionsToken)),
|
|
187
|
-
_ts_metadata("design:type", Function),
|
|
188
|
-
_ts_metadata("design:paramtypes", [
|
|
189
|
-
typeof HttpOptions === "undefined" ? Object : HttpOptions
|
|
190
|
-
])
|
|
191
|
-
], RouteRegistry);
|
|
192
|
-
function isInjectedPart(location) {
|
|
193
|
-
return location != null && _httptypes.InjectedParts.includes(location);
|
|
194
|
-
}
|
|
@@ -1,158 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", {
|
|
3
|
-
value: true
|
|
4
|
-
});
|
|
5
|
-
Object.defineProperty(exports, "Router", {
|
|
6
|
-
enumerable: true,
|
|
7
|
-
get: function() {
|
|
8
|
-
return Router;
|
|
9
|
-
}
|
|
10
|
-
});
|
|
11
|
-
const _crucible = require("@decohere/crucible");
|
|
12
|
-
const _decorators = require("@decohere/crucible/decorators");
|
|
13
|
-
const _scribe = require("@decohere/scribe");
|
|
14
|
-
const _const = require("../../const.js");
|
|
15
|
-
const _requestvalidator = require("../../openapi/request-validator.js");
|
|
16
|
-
const _ssetypes = require("../../sse/sse.types.js");
|
|
17
|
-
const _httptypes = require("../http.types.js");
|
|
18
|
-
const _authdecorator = require("../decorators/auth.decorator.js");
|
|
19
|
-
const _middleware = require("../middleware/middleware.js");
|
|
20
|
-
const _routeargumentresolver = require("./route-argument-resolver.js");
|
|
21
|
-
const _routeregistry = require("./route-registry.js");
|
|
22
|
-
function _ts_decorate(decorators, target, key, desc) {
|
|
23
|
-
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
24
|
-
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
25
|
-
else for(var i = decorators.length - 1; i >= 0; i--)if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
26
|
-
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
27
|
-
}
|
|
28
|
-
function _ts_metadata(k, v) {
|
|
29
|
-
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
30
|
-
}
|
|
31
|
-
function _ts_param(paramIndex, decorator) {
|
|
32
|
-
return function(target, key) {
|
|
33
|
-
decorator(target, key, paramIndex);
|
|
34
|
-
};
|
|
35
|
-
}
|
|
36
|
-
class Router {
|
|
37
|
-
argResolver = new _routeargumentresolver.RouteArgumentResolve();
|
|
38
|
-
lifetimeScope;
|
|
39
|
-
routeRegistry;
|
|
40
|
-
authService;
|
|
41
|
-
httpOptions;
|
|
42
|
-
logger = _scribe.scribe.logger(Router.name);
|
|
43
|
-
constructor(routeRegistry, lifetimeScope, authService, httpOptions){
|
|
44
|
-
this.routeRegistry = routeRegistry;
|
|
45
|
-
this.lifetimeScope = lifetimeScope;
|
|
46
|
-
this.authService = authService;
|
|
47
|
-
this.httpOptions = httpOptions;
|
|
48
|
-
this.assertAuthConfiguredForProtectedRoutes();
|
|
49
|
-
}
|
|
50
|
-
/**
|
|
51
|
-
* Without an authorization service nothing checks tokens, so a route marked @Authorize
|
|
52
|
-
* would be served to anyone. Refuse to start instead of failing open.
|
|
53
|
-
*/ assertAuthConfiguredForProtectedRoutes() {
|
|
54
|
-
if (this.authService != null) {
|
|
55
|
-
return;
|
|
56
|
-
}
|
|
57
|
-
const protectedRoutes = [
|
|
58
|
-
...this.routeRegistry.routes
|
|
59
|
-
].filter(({ controller, handler })=>(0, _authdecorator.getAuthorizeOptions)(controller.prototype, controller.prototype[handler])).map(({ controller, handler })=>`${controller.name}.${String(handler)}`);
|
|
60
|
-
if (protectedRoutes.length > 0) {
|
|
61
|
-
throw new _httptypes.RoutingError(`Routes use @Authorize but auth is not configured, so they would be open to anyone: ${protectedRoutes.join(", ")}. Add .withAuth() to the builder, or remove @Authorize.`);
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
async route(routeKey, httpRequest) {
|
|
65
|
-
const routeConfig = this.routeRegistry.getRoute(routeKey);
|
|
66
|
-
if (!routeConfig) {
|
|
67
|
-
throw new _httptypes.RoutingError(`Route was not configured for key ${routeKey}`);
|
|
68
|
-
}
|
|
69
|
-
const { controller, handler, routeArgs } = routeConfig;
|
|
70
|
-
// Unnamed: the request id comes from the client, so it is not unique.
|
|
71
|
-
// Disposed before we respond, so async cleanup (e.g. rolling back a DB transaction)
|
|
72
|
-
// finishes first. An SSE route is the exception: its scope lives as long as the stream.
|
|
73
|
-
const scope = this.lifetimeScope.beginScope();
|
|
74
|
-
let scopeHandedOff = false;
|
|
75
|
-
// Runs inside the middleware chain so rejections (401/403/400) reach middleware such as
|
|
76
|
-
// the access log. Auth comes first so anonymous callers learn nothing about the body.
|
|
77
|
-
const handle = async ()=>{
|
|
78
|
-
const principal = await this.authorize(routeConfig, httpRequest);
|
|
79
|
-
if (this.httpOptions?.openapi?.validateRequests !== false && this.httpOptions?.openapi) {
|
|
80
|
-
(0, _requestvalidator.validateRequest)(routeConfig, httpRequest);
|
|
81
|
-
}
|
|
82
|
-
const provider = scope.resolve(_const.IPrincipalProviderToken);
|
|
83
|
-
provider.setPrincipal(principal);
|
|
84
|
-
const controllerInstance = scope.resolve(controller);
|
|
85
|
-
const func = controllerInstance[handler];
|
|
86
|
-
const argValues = this.argResolver.resolveArguments(httpRequest, routeArgs, provider);
|
|
87
|
-
try {
|
|
88
|
-
const result = await func.apply(controllerInstance, argValues);
|
|
89
|
-
if (routeConfig.sse == null) {
|
|
90
|
-
return result;
|
|
91
|
-
}
|
|
92
|
-
if (typeof result?.[Symbol.asyncIterator] !== "function") {
|
|
93
|
-
throw new _httptypes.RoutingError(`${controller.name}.${String(handler)} is marked @Sse() so it must return an AsyncIterable (an async generator method works)`);
|
|
94
|
-
}
|
|
95
|
-
scopeHandedOff = true;
|
|
96
|
-
return new _ssetypes.SseResponse(result, ()=>scope[Symbol.asyncDispose]());
|
|
97
|
-
} catch (error) {
|
|
98
|
-
this.logger.error(error.message, error.stack);
|
|
99
|
-
throw error;
|
|
100
|
-
}
|
|
101
|
-
};
|
|
102
|
-
try {
|
|
103
|
-
const middlewares = this.resolveMiddlewares(scope);
|
|
104
|
-
if (middlewares.length === 0) {
|
|
105
|
-
return await handle();
|
|
106
|
-
}
|
|
107
|
-
return await (0, _middleware.composeMiddleware)(middlewares, handle, {
|
|
108
|
-
request: httpRequest,
|
|
109
|
-
route: routeConfig
|
|
110
|
-
});
|
|
111
|
-
} catch (error) {
|
|
112
|
-
// A middleware failed after the handler returned: nobody will consume the stream.
|
|
113
|
-
scopeHandedOff = false;
|
|
114
|
-
throw error;
|
|
115
|
-
} finally{
|
|
116
|
-
if (!scopeHandedOff) {
|
|
117
|
-
await scope[Symbol.asyncDispose]();
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
resolveMiddlewares(scope) {
|
|
122
|
-
const types = this.httpOptions?.middlewareTypes ?? [];
|
|
123
|
-
return types.map((type)=>scope.resolve(type));
|
|
124
|
-
}
|
|
125
|
-
async authorize(routeConfig, httpRequest) {
|
|
126
|
-
// Optional DI may resolve as null when auth is not registered.
|
|
127
|
-
if (this.authService == null) {
|
|
128
|
-
return;
|
|
129
|
-
}
|
|
130
|
-
const { controller, handler } = routeConfig;
|
|
131
|
-
const handlerFn = controller.prototype[handler];
|
|
132
|
-
if ((0, _authdecorator.isAllowAnonymous)(controller.prototype, handlerFn)) {
|
|
133
|
-
return;
|
|
134
|
-
}
|
|
135
|
-
const authOptions = (0, _authdecorator.getAuthorizeOptions)(controller.prototype, handlerFn);
|
|
136
|
-
if (authOptions === undefined) {
|
|
137
|
-
// Auth is enabled but the route has no @Authorize — require a valid
|
|
138
|
-
// token (fail closed) without checking a specific ability.
|
|
139
|
-
return this.authService.authenticateRequest(httpRequest);
|
|
140
|
-
}
|
|
141
|
-
return this.authService.authorizeRequest(httpRequest, authOptions.action, authOptions.subject);
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
Router = _ts_decorate([
|
|
145
|
-
(0, _decorators.injectable)(),
|
|
146
|
-
_ts_param(0, (0, _decorators.inject)(_const.RouteRegistryToken)),
|
|
147
|
-
_ts_param(1, (0, _decorators.inject)(_crucible.LifetimeScopeToken)),
|
|
148
|
-
_ts_param(2, (0, _decorators.inject)(_const.AuthorizationServiceToken)),
|
|
149
|
-
_ts_param(2, (0, _decorators.optional)()),
|
|
150
|
-
_ts_param(3, (0, _decorators.inject)(_const.HttpOptionsToken)),
|
|
151
|
-
_ts_metadata("design:type", Function),
|
|
152
|
-
_ts_metadata("design:paramtypes", [
|
|
153
|
-
typeof _routeregistry.RouteRegistry === "undefined" ? Object : _routeregistry.RouteRegistry,
|
|
154
|
-
typeof LifetimeScope === "undefined" ? Object : LifetimeScope,
|
|
155
|
-
typeof IAuthorizationService === "undefined" ? Object : IAuthorizationService,
|
|
156
|
-
typeof HttpOptions === "undefined" ? Object : HttpOptions
|
|
157
|
-
])
|
|
158
|
-
], Router);
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", {
|
|
3
|
-
value: true
|
|
4
|
-
});
|
|
5
|
-
function _export(target, all) {
|
|
6
|
-
for(var name in all)Object.defineProperty(target, name, {
|
|
7
|
-
enumerable: true,
|
|
8
|
-
get: Object.getOwnPropertyDescriptor(all, name).get
|
|
9
|
-
});
|
|
10
|
-
}
|
|
11
|
-
_export(exports, {
|
|
12
|
-
get LastEventId () {
|
|
13
|
-
return _ssedecorator.LastEventId;
|
|
14
|
-
},
|
|
15
|
-
get Signal () {
|
|
16
|
-
return _ssedecorator.Signal;
|
|
17
|
-
},
|
|
18
|
-
get Sse () {
|
|
19
|
-
return _ssedecorator.Sse;
|
|
20
|
-
},
|
|
21
|
-
get SseResponse () {
|
|
22
|
-
return _ssetypes.SseResponse;
|
|
23
|
-
}
|
|
24
|
-
});
|
|
25
|
-
const _ssedecorator = require("./sse.decorator.js");
|
|
26
|
-
const _ssetypes = require("./sse.types.js");
|
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", {
|
|
3
|
-
value: true
|
|
4
|
-
});
|
|
5
|
-
function _export(target, all) {
|
|
6
|
-
for(var name in all)Object.defineProperty(target, name, {
|
|
7
|
-
enumerable: true,
|
|
8
|
-
get: Object.getOwnPropertyDescriptor(all, name).get
|
|
9
|
-
});
|
|
10
|
-
}
|
|
11
|
-
_export(exports, {
|
|
12
|
-
get LastEventId () {
|
|
13
|
-
return LastEventId;
|
|
14
|
-
},
|
|
15
|
-
get Signal () {
|
|
16
|
-
return Signal;
|
|
17
|
-
},
|
|
18
|
-
get Sse () {
|
|
19
|
-
return Sse;
|
|
20
|
-
}
|
|
21
|
-
});
|
|
22
|
-
const _reflection = require("@decohere/core/reflection");
|
|
23
|
-
const _const = require("../const.js");
|
|
24
|
-
function Sse(options = {}) {
|
|
25
|
-
return (_target, _methodName, descriptor)=>{
|
|
26
|
-
(0, _reflection.defineMeta)(_const.MetadataKeys.sse, options, descriptor.value);
|
|
27
|
-
};
|
|
28
|
-
}
|
|
29
|
-
function injected(location) {
|
|
30
|
-
return ()=>(target, propertyKey, parameterIndex)=>{
|
|
31
|
-
const fn = target[propertyKey];
|
|
32
|
-
const meta = {
|
|
33
|
-
argumentIndex: parameterIndex,
|
|
34
|
-
argumentLocation: location,
|
|
35
|
-
mappingKey: undefined,
|
|
36
|
-
pipes: []
|
|
37
|
-
};
|
|
38
|
-
(0, _reflection.defineArgumentMeta)(_const.MetadataKeys.parameter, meta, fn, parameterIndex);
|
|
39
|
-
};
|
|
40
|
-
}
|
|
41
|
-
const Signal = injected("signal");
|
|
42
|
-
const LastEventId = injected("lastEventId");
|