@thisisagile/easy-express 19.1.3 → 20.0.0
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/express/AuthError.d.ts +6 -3
- package/dist/express/AuthError.js +16 -0
- package/dist/express/AuthError.js.map +1 -0
- package/dist/express/CorrelationHandler.d.ts +4 -1
- package/dist/express/CorrelationHandler.js +10 -0
- package/dist/express/CorrelationHandler.js.map +1 -0
- package/dist/express/ErrorHandler.d.ts +4 -1
- package/dist/express/ErrorHandler.js +19 -0
- package/dist/express/ErrorHandler.js.map +1 -0
- package/dist/express/ExpressProvider.d.ts +17 -14
- package/dist/express/ExpressProvider.js +68 -0
- package/dist/express/ExpressProvider.js.map +1 -0
- package/dist/express/NotFoundHandler.d.ts +4 -1
- package/dist/express/NotFoundHandler.js +10 -0
- package/dist/express/NotFoundHandler.js.map +1 -0
- package/dist/express/RequestContextHandler.d.ts +4 -1
- package/dist/express/RequestContextHandler.js +7 -0
- package/dist/express/RequestContextHandler.js.map +1 -0
- package/dist/express/SecurityHandler.d.ts +13 -10
- package/dist/express/SecurityHandler.js +37 -0
- package/dist/express/SecurityHandler.js.map +1 -0
- package/dist/express/index.d.ts +8 -7
- package/dist/express/index.js +8 -0
- package/dist/index.d.ts +11 -2
- package/dist/index.js +9 -238
- package/dist/types/NamespaceContext.d.ts +8 -5
- package/dist/types/NamespaceContext.js +17 -0
- package/dist/types/NamespaceContext.js.map +1 -0
- package/dist/types/index.d.ts +2 -1
- package/dist/types/index.js +1 -0
- package/package.json +22 -20
- package/dist/chunk-33OQAPCM.mjs +0 -11
- package/dist/chunk-33OQAPCM.mjs.map +0 -1
- package/dist/chunk-6TBWUXPT.mjs +0 -1
- package/dist/chunk-6TBWUXPT.mjs.map +0 -1
- package/dist/chunk-C7NPSRIT.mjs +0 -8
- package/dist/chunk-C7NPSRIT.mjs.map +0 -1
- package/dist/chunk-GEFXEGJW.mjs +0 -94
- package/dist/chunk-GEFXEGJW.mjs.map +0 -1
- package/dist/chunk-N2UP7WFM.mjs +0 -38
- package/dist/chunk-N2UP7WFM.mjs.map +0 -1
- package/dist/chunk-RL5ICAHT.mjs +0 -19
- package/dist/chunk-RL5ICAHT.mjs.map +0 -1
- package/dist/chunk-T4G4HIZT.mjs +0 -43
- package/dist/chunk-T4G4HIZT.mjs.map +0 -1
- package/dist/chunk-U2UXWL6X.mjs +0 -18
- package/dist/chunk-U2UXWL6X.mjs.map +0 -1
- package/dist/chunk-UQNJLY2I.mjs +0 -11
- package/dist/chunk-UQNJLY2I.mjs.map +0 -1
- package/dist/chunk-YPBKY4KY.mjs +0 -1
- package/dist/chunk-YPBKY4KY.mjs.map +0 -1
- package/dist/express/AuthError.mjs +0 -11
- package/dist/express/AuthError.mjs.map +0 -1
- package/dist/express/CorrelationHandler.mjs +0 -7
- package/dist/express/CorrelationHandler.mjs.map +0 -1
- package/dist/express/ErrorHandler.mjs +0 -8
- package/dist/express/ErrorHandler.mjs.map +0 -1
- package/dist/express/ExpressProvider.mjs +0 -11
- package/dist/express/ExpressProvider.mjs.map +0 -1
- package/dist/express/NotFoundHandler.mjs +0 -7
- package/dist/express/NotFoundHandler.mjs.map +0 -1
- package/dist/express/RequestContextHandler.mjs +0 -7
- package/dist/express/RequestContextHandler.mjs.map +0 -1
- package/dist/express/SecurityHandler.mjs +0 -16
- package/dist/express/SecurityHandler.mjs.map +0 -1
- package/dist/express/index.mjs +0 -46
- package/dist/express/index.mjs.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/index.mjs +0 -51
- package/dist/index.mjs.map +0 -1
- package/dist/types/NamespaceContext.mjs +0 -7
- package/dist/types/NamespaceContext.mjs.map +0 -1
- package/dist/types/index.mjs +0 -8
- package/dist/types/index.mjs.map +0 -1
|
@@ -1,7 +1,10 @@
|
|
|
1
|
-
import { HttpStatus } from
|
|
1
|
+
import { HttpStatus } from "@thisisagile/easy";
|
|
2
|
+
//#region src/express/AuthError.d.ts
|
|
2
3
|
export declare class AuthError extends Error {
|
|
3
|
-
|
|
4
|
-
|
|
4
|
+
status: number;
|
|
5
|
+
constructor({ name, status }: HttpStatus);
|
|
5
6
|
}
|
|
6
7
|
export declare const authError: (status: HttpStatus) => AuthError;
|
|
7
8
|
export declare const isAuthError: (e?: unknown) => e is AuthError;
|
|
9
|
+
//#endregion
|
|
10
|
+
//# sourceMappingURL=AuthError.d.ts.map
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { isError } from "@thisisagile/easy";
|
|
2
|
+
//#region src/express/AuthError.ts
|
|
3
|
+
var AuthError = class extends Error {
|
|
4
|
+
status;
|
|
5
|
+
constructor({ name, status }) {
|
|
6
|
+
super(name);
|
|
7
|
+
this.name = "AuthenticationError";
|
|
8
|
+
this.status = status;
|
|
9
|
+
}
|
|
10
|
+
};
|
|
11
|
+
const authError = (status) => new AuthError(status);
|
|
12
|
+
const isAuthError = (e) => isError(e) && e.name === "AuthenticationError";
|
|
13
|
+
//#endregion
|
|
14
|
+
export { AuthError, authError, isAuthError };
|
|
15
|
+
|
|
16
|
+
//# sourceMappingURL=AuthError.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AuthError.js","names":[],"sources":["../../src/express/AuthError.ts"],"sourcesContent":["import { HttpStatus, isError } from '@thisisagile/easy';\n\nexport class AuthError extends Error {\n status: number;\n\n constructor({ name, status }: HttpStatus) {\n super(name);\n this.name = 'AuthenticationError';\n this.status = status;\n }\n}\n\nexport const authError = (status: HttpStatus): AuthError => new AuthError(status);\n\nexport const isAuthError = (e?: unknown): e is AuthError => isError(e) && e.name === 'AuthenticationError';\n"],"mappings":";;AAEA,IAAa,YAAb,cAA+B,MAAM;CACnC;CAEA,YAAY,EAAE,MAAM,UAAsB;EACxC,MAAM,IAAI;EACV,KAAK,OAAO;EACZ,KAAK,SAAS;CAChB;AACF;AAEA,MAAa,aAAa,WAAkC,IAAI,UAAU,MAAM;AAEhF,MAAa,eAAe,MAAgC,QAAQ,CAAC,KAAK,EAAE,SAAS"}
|
|
@@ -1,2 +1,5 @@
|
|
|
1
|
-
import express from
|
|
1
|
+
import express from "express";
|
|
2
|
+
//#region src/express/CorrelationHandler.d.ts
|
|
2
3
|
export declare const correlation: (req: express.Request, res: express.Response, next: express.NextFunction) => void;
|
|
4
|
+
//#endregion
|
|
5
|
+
//# sourceMappingURL=CorrelationHandler.d.ts.map
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { HttpHeader, ctx, toUuid } from "@thisisagile/easy";
|
|
2
|
+
//#region src/express/CorrelationHandler.ts
|
|
3
|
+
const correlation = (req, res, next) => {
|
|
4
|
+
res.setHeader(HttpHeader.Correlation, ctx.request.correlationId = req?.header(HttpHeader.Correlation) ?? toUuid());
|
|
5
|
+
next();
|
|
6
|
+
};
|
|
7
|
+
//#endregion
|
|
8
|
+
export { correlation };
|
|
9
|
+
|
|
10
|
+
//# sourceMappingURL=CorrelationHandler.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CorrelationHandler.js","names":[],"sources":["../../src/express/CorrelationHandler.ts"],"sourcesContent":["import express from 'express';\nimport { ctx, HttpHeader, toUuid } from '@thisisagile/easy';\n\nexport const correlation = (req: express.Request, res: express.Response, next: express.NextFunction): void => {\n res.setHeader(HttpHeader.Correlation, (ctx.request.correlationId = req?.header(HttpHeader.Correlation) ?? toUuid()));\n next();\n};\n"],"mappings":";;AAGA,MAAa,eAAe,KAAsB,KAAuB,SAAqC;CAC5G,IAAI,UAAU,WAAW,aAAc,IAAI,QAAQ,gBAAgB,KAAK,OAAO,WAAW,WAAW,KAAK,OAAO,CAAE;CACnH,KAAK;AACP"}
|
|
@@ -1,2 +1,5 @@
|
|
|
1
|
-
import express from
|
|
1
|
+
import express from "express";
|
|
2
|
+
//#region src/express/ErrorHandler.d.ts
|
|
2
3
|
export declare const error: (e: Error, req: express.Request, res: express.Response, _next: express.NextFunction) => void;
|
|
4
|
+
//#endregion
|
|
5
|
+
//# sourceMappingURL=ErrorHandler.d.ts.map
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { isAuthError } from "./AuthError.js";
|
|
2
|
+
import { HttpStatus, asString, choose, ctx, isDoesNotExist, isError, isException, isResponse, isResults, isText, rest, toHttpStatus, toResult, tryTo } from "@thisisagile/easy";
|
|
3
|
+
import { toOriginatedError } from "@thisisagile/easy-service";
|
|
4
|
+
//#region src/express/ErrorHandler.ts
|
|
5
|
+
const toResponse = (status, errors = []) => ({
|
|
6
|
+
status,
|
|
7
|
+
body: rest.toError(status, errors)
|
|
8
|
+
});
|
|
9
|
+
const toBody = ({ origin, options }) => {
|
|
10
|
+
return choose(origin).type(isAuthError, (ae) => toResponse(toHttpStatus(ae.status), [toResult(ae.message)])).type(isDoesNotExist, (e) => toResponse(options?.onNotFound ?? HttpStatus.NotFound, [toResult(e.reason ?? e.message)])).type(isError, (e) => toResponse(HttpStatus.InternalServerError, [toResult(e.message)])).type(isResults, (r) => toResponse(options?.onError ?? HttpStatus.BadRequest, r.results)).type(isResponse, (r) => toResponse(HttpStatus.InternalServerError, r.body.error?.errors)).type(isException, (e) => toResponse(options?.onError ?? HttpStatus.BadRequest, [toResult(e.reason ?? e.message)])).type(isText, (t) => toResponse(options?.onError ?? HttpStatus.BadRequest, [toResult(asString(t))])).else(() => toResponse(HttpStatus.InternalServerError, [toResult("Unknown error")]));
|
|
11
|
+
};
|
|
12
|
+
const error = (e, req, res, _next) => {
|
|
13
|
+
let response;
|
|
14
|
+
tryTo(() => toOriginatedError(e)).map((oe) => toBody(oe)).accept((r) => response = r).accept((r) => ctx.request.lastError = r.status.isServerError ? r.body.error?.errors[0]?.message : void 0).accept((r) => ctx.request.lastErrorStack = r.status.isServerError ? e.stack : void 0).recover(() => response).accept((r) => res.status(r.status.status).json(r.body));
|
|
15
|
+
};
|
|
16
|
+
//#endregion
|
|
17
|
+
export { error };
|
|
18
|
+
|
|
19
|
+
//# sourceMappingURL=ErrorHandler.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ErrorHandler.js","names":[],"sources":["../../src/express/ErrorHandler.ts"],"sourcesContent":["import express from 'express';\nimport { isAuthError } from './AuthError';\nimport {\n asString,\n choose,\n ctx,\n HttpStatus,\n isDoesNotExist,\n isError,\n isException,\n isResponse,\n isResults,\n isText,\n Response,\n rest,\n Result,\n toHttpStatus,\n toResult,\n tryTo,\n} from '@thisisagile/easy';\nimport { OriginatedError, toOriginatedError } from '@thisisagile/easy-service';\n\nconst toResponse = (status: HttpStatus, errors: Result[] = []): Response => ({\n status,\n body: rest.toError(status, errors),\n});\n\nconst toBody = ({ origin, options }: OriginatedError): Response => {\n return (\n choose(origin)\n .type(isAuthError, ae => toResponse(toHttpStatus(ae.status), [toResult(ae.message)]))\n .type(isDoesNotExist, e => toResponse(options?.onNotFound ?? HttpStatus.NotFound, [toResult(e.reason ?? e.message)]))\n // This service breaks with an error\n .type(isError, e => toResponse(HttpStatus.InternalServerError, [toResult(e.message)]))\n // This service fails\n .type(isResults, r => toResponse(options?.onError ?? HttpStatus.BadRequest, r.results))\n // Underlying service fails\n .type(isResponse, r => toResponse(HttpStatus.InternalServerError, r.body.error?.errors))\n .type(isException, e => toResponse(options?.onError ?? HttpStatus.BadRequest, [toResult(e.reason ?? e.message)]))\n // This service fails with a string\n .type(isText, t => toResponse(options?.onError ?? HttpStatus.BadRequest, [toResult(asString(t))]))\n .else(() => toResponse(HttpStatus.InternalServerError, [toResult('Unknown error')]))\n );\n};\n\nexport const error = (e: Error, req: express.Request, res: express.Response, _next: express.NextFunction): void => {\n let response: Response;\n tryTo(() => toOriginatedError(e))\n .map(oe => toBody(oe))\n .accept(r => (response = r))\n .accept(r => (ctx.request.lastError = r.status.isServerError ? r.body.error?.errors[0]?.message : undefined))\n .accept(r => (ctx.request.lastErrorStack = r.status.isServerError ? e.stack : undefined))\n .recover(() => response)\n .accept(r => res.status(r.status.status).json(r.body));\n};\n"],"mappings":";;;;AAsBA,MAAM,cAAc,QAAoB,SAAmB,CAAC,OAAiB;CAC3E;CACA,MAAM,KAAK,QAAQ,QAAQ,MAAM;AACnC;AAEA,MAAM,UAAU,EAAE,QAAQ,cAAyC;CACjE,OACE,OAAO,MAAM,CAAC,CACX,KAAK,cAAa,OAAM,WAAW,aAAa,GAAG,MAAM,GAAG,CAAC,SAAS,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC,CACpF,KAAK,iBAAgB,MAAK,WAAW,SAAS,cAAc,WAAW,UAAU,CAAC,SAAS,EAAE,UAAU,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC,CAEpH,KAAK,UAAS,MAAK,WAAW,WAAW,qBAAqB,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC,CAErF,KAAK,YAAW,MAAK,WAAW,SAAS,WAAW,WAAW,YAAY,EAAE,OAAO,CAAC,CAAC,CAEtF,KAAK,aAAY,MAAK,WAAW,WAAW,qBAAqB,EAAE,KAAK,OAAO,MAAM,CAAC,CAAC,CACvF,KAAK,cAAa,MAAK,WAAW,SAAS,WAAW,WAAW,YAAY,CAAC,SAAS,EAAE,UAAU,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC,CAEhH,KAAK,SAAQ,MAAK,WAAW,SAAS,WAAW,WAAW,YAAY,CAAC,SAAS,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CACjG,WAAW,WAAW,WAAW,qBAAqB,CAAC,SAAS,eAAe,CAAC,CAAC,CAAC;AAEzF;AAEA,MAAa,SAAS,GAAU,KAAsB,KAAuB,UAAsC;CACjH,IAAI;CACJ,YAAY,kBAAkB,CAAC,CAAC,CAAC,CAC9B,KAAI,OAAM,OAAO,EAAE,CAAC,CAAC,CACrB,QAAO,MAAM,WAAW,CAAE,CAAC,CAC3B,QAAO,MAAM,IAAI,QAAQ,YAAY,EAAE,OAAO,gBAAgB,EAAE,KAAK,OAAO,OAAO,EAAE,EAAE,UAAU,KAAA,CAAU,CAAC,CAC5G,QAAO,MAAM,IAAI,QAAQ,iBAAiB,EAAE,OAAO,gBAAgB,EAAE,QAAQ,KAAA,CAAU,CAAC,CACxF,cAAc,QAAQ,CAAC,CACvB,QAAO,MAAK,IAAI,OAAO,EAAE,OAAO,MAAM,CAAC,CAAC,KAAK,EAAE,IAAI,CAAC;AACzD"}
|
|
@@ -1,18 +1,21 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { AppProvider, Endpoint, Handler, Resource, RouteRequires, Service, VerbOptions } from "@thisisagile/easy-service";
|
|
2
|
+
import { Express, RequestHandler, Response } from "express";
|
|
3
|
+
//#region src/express/ExpressProvider.d.ts
|
|
3
4
|
export type ExpressVerb = 'get' | 'post' | 'put' | 'patch' | 'delete';
|
|
4
5
|
export declare class ExpressProvider implements AppProvider {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
6
|
+
protected app: Express;
|
|
7
|
+
constructor(app?: Express);
|
|
8
|
+
use: (handler: Handler) => void;
|
|
9
|
+
route: (service: Service, resource: Resource) => void;
|
|
10
|
+
listen: (port: number, message?: string) => void;
|
|
11
|
+
protected addSecurityMiddleware(requires: RouteRequires): RequestHandler[];
|
|
12
|
+
protected handle: (endpoint: Endpoint, options?: VerbOptions, requires?: RouteRequires) => RequestHandler;
|
|
13
|
+
protected toResponse(res: Response, result: unknown, options: Required<VerbOptions>): void;
|
|
14
|
+
protected json(res: Response, result: unknown, options: Required<VerbOptions>): void;
|
|
15
|
+
protected rawJson(res: Response, result: unknown, options: Required<VerbOptions>): void;
|
|
16
|
+
protected stream(res: Response, result: unknown): void;
|
|
17
|
+
protected text(res: Response, data: unknown): void;
|
|
17
18
|
}
|
|
18
19
|
export declare const service: (name: string) => Service;
|
|
20
|
+
//#endregion
|
|
21
|
+
//# sourceMappingURL=ExpressProvider.d.ts.map
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { checkLabCoat, checkScope, checkToken, checkUseCase } from "./SecurityHandler.js";
|
|
2
|
+
import { HttpStatus, isEmpty, rest, toList, toReq } from "@thisisagile/easy";
|
|
3
|
+
import { Service, routes, toOriginatedError, toVerbOptions } from "@thisisagile/easy-service";
|
|
4
|
+
import express from "express";
|
|
5
|
+
//#region src/express/ExpressProvider.ts
|
|
6
|
+
var ExpressProvider = class {
|
|
7
|
+
app;
|
|
8
|
+
constructor(app = express()) {
|
|
9
|
+
this.app = app;
|
|
10
|
+
this.app.set("trust proxy", [
|
|
11
|
+
"loopback",
|
|
12
|
+
"linklocal",
|
|
13
|
+
"uniquelocal"
|
|
14
|
+
]);
|
|
15
|
+
}
|
|
16
|
+
use = (handler) => {
|
|
17
|
+
this.app.use(handler);
|
|
18
|
+
};
|
|
19
|
+
route = (service, resource) => {
|
|
20
|
+
const { route, endpoints, middleware } = routes(resource);
|
|
21
|
+
const router = express.Router({ mergeParams: true });
|
|
22
|
+
if (!isEmpty(middleware)) router.all(route.route(service.name), middleware);
|
|
23
|
+
endpoints.forEach(({ endpoint, verb, requires, middleware }) => {
|
|
24
|
+
console.log(verb.verb.code, route.route(service.name));
|
|
25
|
+
router[verb.verb.toString()](route.route(service.name), ...this.addSecurityMiddleware(requires), ...middleware, this.handle(endpoint, verb.options, requires));
|
|
26
|
+
});
|
|
27
|
+
this.app.use(router);
|
|
28
|
+
};
|
|
29
|
+
listen = (port, message = `Service is listening on port ${port}.`) => {
|
|
30
|
+
this.app.listen(port, () => {
|
|
31
|
+
console.log(message);
|
|
32
|
+
});
|
|
33
|
+
};
|
|
34
|
+
addSecurityMiddleware(requires) {
|
|
35
|
+
const middleware = [];
|
|
36
|
+
if (requires.labCoat) middleware.push(checkLabCoat());
|
|
37
|
+
if (requires.token) middleware.push(checkToken());
|
|
38
|
+
if (requires.scope) middleware.push(checkScope(requires.scope));
|
|
39
|
+
if (requires.uc) middleware.push(checkUseCase(requires.uc));
|
|
40
|
+
return middleware;
|
|
41
|
+
}
|
|
42
|
+
handle = (endpoint, options, requires) => (req, res, next) => endpoint(toReq(req)).then((r) => this.toResponse(res, r, toVerbOptions(options))).catch((error) => next(toOriginatedError(error, options)));
|
|
43
|
+
toResponse(res, result, options) {
|
|
44
|
+
res.status(options.onOk.status);
|
|
45
|
+
res.type(options.type.code);
|
|
46
|
+
if (options.cache.enabled) res.setHeader(options.cache.name, options.cache.value());
|
|
47
|
+
(this[options.type.name] ?? this.json)(res, result, options);
|
|
48
|
+
}
|
|
49
|
+
json(res, result, options) {
|
|
50
|
+
if (HttpStatus.NoContent.equals(options.onOk)) res.send();
|
|
51
|
+
else res.json(rest.toData(options.onOk, toList(result), result?.total, result?.meta));
|
|
52
|
+
}
|
|
53
|
+
rawJson(res, result, options) {
|
|
54
|
+
if (HttpStatus.NoContent.equals(options.onOk)) res.send();
|
|
55
|
+
else res.json(result);
|
|
56
|
+
}
|
|
57
|
+
stream(res, result) {
|
|
58
|
+
res.end(result);
|
|
59
|
+
}
|
|
60
|
+
text(res, data) {
|
|
61
|
+
res.send(data);
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
const service = (name) => new Service(name, new ExpressProvider());
|
|
65
|
+
//#endregion
|
|
66
|
+
export { ExpressProvider, service };
|
|
67
|
+
|
|
68
|
+
//# sourceMappingURL=ExpressProvider.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ExpressProvider.js","names":[],"sources":["../../src/express/ExpressProvider.ts"],"sourcesContent":["import express, { Express, NextFunction, Request, RequestHandler, Response } from 'express';\nimport { checkLabCoat, checkScope, checkToken, checkUseCase } from './SecurityHandler';\nimport { HttpStatus, isEmpty, PageList, rest, toList, toReq } from '@thisisagile/easy';\nimport {\n AppProvider,\n Endpoint,\n Handler,\n Resource,\n Route,\n RouteRequires,\n routes,\n Service,\n toOriginatedError,\n toVerbOptions,\n VerbOptions,\n} from '@thisisagile/easy-service';\n\nexport type ExpressVerb = 'get' | 'post' | 'put' | 'patch' | 'delete';\n\nexport class ExpressProvider implements AppProvider {\n constructor(protected app: Express = express()) {\n this.app.set('trust proxy', ['loopback', 'linklocal', 'uniquelocal']);\n }\n\n use = (handler: Handler): void => {\n this.app.use(handler);\n };\n\n route = (service: Service, resource: Resource): void => {\n const { route, endpoints, middleware } = routes(resource);\n const router = express.Router({ mergeParams: true });\n if (!isEmpty(middleware)) router.all(route.route(service.name), middleware);\n\n endpoints.forEach(({ endpoint, verb, requires, middleware }: Route) => {\n console.log(verb.verb.code, route.route(service.name));\n router[verb.verb.toString() as ExpressVerb](\n route.route(service.name),\n ...this.addSecurityMiddleware(requires),\n ...middleware,\n this.handle(endpoint, verb.options, requires)\n );\n });\n\n this.app.use(router);\n };\n\n listen = (port: number, message = `Service is listening on port ${port}.`): void => {\n this.app.listen(port, () => {\n console.log(message);\n });\n };\n\n protected addSecurityMiddleware(requires: RouteRequires): RequestHandler[] {\n const middleware: RequestHandler[] = [];\n if (requires.labCoat) middleware.push(checkLabCoat());\n if (requires.token) middleware.push(checkToken());\n if (requires.scope) middleware.push(checkScope(requires.scope));\n if (requires.uc) middleware.push(checkUseCase(requires.uc));\n return middleware;\n }\n\n protected handle =\n (endpoint: Endpoint, options?: VerbOptions, requires?: RouteRequires): RequestHandler =>\n (req: Request, res: Response, next: NextFunction) =>\n endpoint(toReq(req))\n .then((r: any) => this.toResponse(res, r, toVerbOptions(options)))\n .catch(error => next(toOriginatedError(error, options)));\n\n protected toResponse(res: Response, result: unknown, options: Required<VerbOptions>): void {\n res.status(options.onOk.status);\n res.type(options.type.code);\n if (options.cache.enabled) res.setHeader(options.cache.name, options.cache.value());\n\n ((this as any)[options.type.name] ?? this.json)(res, result, options);\n }\n\n // Handling responses depending on content type\n\n protected json(res: Response, result: unknown, options: Required<VerbOptions>): void {\n if (HttpStatus.NoContent.equals(options.onOk)) {\n res.send();\n } else {\n res.json(rest.toData(options.onOk, toList<any>(result), (result as PageList<any>)?.total, (result as PageList<any>)?.meta));\n }\n }\n\n protected rawJson(res: Response, result: unknown, options: Required<VerbOptions>): void {\n if (HttpStatus.NoContent.equals(options.onOk)) {\n res.send();\n } else {\n res.json(result);\n }\n }\n\n protected stream(res: Response, result: unknown): void {\n res.end(result);\n }\n\n protected text(res: Response, data: unknown): void {\n res.send(data);\n }\n}\n\nexport const service = (name: string): Service => new Service(name, new ExpressProvider());\n"],"mappings":";;;;;AAmBA,IAAa,kBAAb,MAAoD;CAC5B;CAAtB,YAAY,MAAyB,QAAQ,GAAG;EAA1B,KAAA,MAAA;EACpB,KAAK,IAAI,IAAI,eAAe;GAAC;GAAY;GAAa;EAAa,CAAC;CACtE;CAEA,OAAO,YAA2B;EAChC,KAAK,IAAI,IAAI,OAAO;CACtB;CAEA,SAAS,SAAkB,aAA6B;EACtD,MAAM,EAAE,OAAO,WAAW,eAAe,OAAO,QAAQ;EACxD,MAAM,SAAS,QAAQ,OAAO,EAAE,aAAa,KAAK,CAAC;EACnD,IAAI,CAAC,QAAQ,UAAU,GAAG,OAAO,IAAI,MAAM,MAAM,QAAQ,IAAI,GAAG,UAAU;EAE1E,UAAU,SAAS,EAAE,UAAU,MAAM,UAAU,iBAAwB;GACrE,QAAQ,IAAI,KAAK,KAAK,MAAM,MAAM,MAAM,QAAQ,IAAI,CAAC;GACrD,OAAO,KAAK,KAAK,SAAS,EAAiB,CACzC,MAAM,MAAM,QAAQ,IAAI,GACxB,GAAG,KAAK,sBAAsB,QAAQ,GACtC,GAAG,YACH,KAAK,OAAO,UAAU,KAAK,SAAS,QAAQ,CAC9C;EACF,CAAC;EAED,KAAK,IAAI,IAAI,MAAM;CACrB;CAEA,UAAU,MAAc,UAAU,gCAAgC,KAAK,OAAa;EAClF,KAAK,IAAI,OAAO,YAAY;GAC1B,QAAQ,IAAI,OAAO;EACrB,CAAC;CACH;CAEA,sBAAgC,UAA2C;EACzE,MAAM,aAA+B,CAAC;EACtC,IAAI,SAAS,SAAS,WAAW,KAAK,aAAa,CAAC;EACpD,IAAI,SAAS,OAAO,WAAW,KAAK,WAAW,CAAC;EAChD,IAAI,SAAS,OAAO,WAAW,KAAK,WAAW,SAAS,KAAK,CAAC;EAC9D,IAAI,SAAS,IAAI,WAAW,KAAK,aAAa,SAAS,EAAE,CAAC;EAC1D,OAAO;CACT;CAEA,UACG,UAAoB,SAAuB,cAC3C,KAAc,KAAe,SAC5B,SAAS,MAAM,GAAG,CAAC,CAAC,CACjB,MAAM,MAAW,KAAK,WAAW,KAAK,GAAG,cAAc,OAAO,CAAC,CAAC,CAAC,CACjE,OAAM,UAAS,KAAK,kBAAkB,OAAO,OAAO,CAAC,CAAC;CAE7D,WAAqB,KAAe,QAAiB,SAAsC;EACzF,IAAI,OAAO,QAAQ,KAAK,MAAM;EAC9B,IAAI,KAAK,QAAQ,KAAK,IAAI;EAC1B,IAAI,QAAQ,MAAM,SAAS,IAAI,UAAU,QAAQ,MAAM,MAAM,QAAQ,MAAM,MAAM,CAAC;EAElF,CAAE,KAAa,QAAQ,KAAK,SAAS,KAAK,KAAA,CAAM,KAAK,QAAQ,OAAO;CACtE;CAIA,KAAe,KAAe,QAAiB,SAAsC;EACnF,IAAI,WAAW,UAAU,OAAO,QAAQ,IAAI,GAC1C,IAAI,KAAK;OAET,IAAI,KAAK,KAAK,OAAO,QAAQ,MAAM,OAAY,MAAM,GAAI,QAA0B,OAAQ,QAA0B,IAAI,CAAC;CAE9H;CAEA,QAAkB,KAAe,QAAiB,SAAsC;EACtF,IAAI,WAAW,UAAU,OAAO,QAAQ,IAAI,GAC1C,IAAI,KAAK;OAET,IAAI,KAAK,MAAM;CAEnB;CAEA,OAAiB,KAAe,QAAuB;EACrD,IAAI,IAAI,MAAM;CAChB;CAEA,KAAe,KAAe,MAAqB;EACjD,IAAI,KAAK,IAAI;CACf;AACF;AAEA,MAAa,WAAW,SAA0B,IAAI,QAAQ,MAAM,IAAI,gBAAgB,CAAC"}
|
|
@@ -1,2 +1,5 @@
|
|
|
1
|
-
import { NextFunction, Request, Response } from
|
|
1
|
+
import { NextFunction, Request, Response } from "express";
|
|
2
|
+
//#region src/express/NotFoundHandler.d.ts
|
|
2
3
|
export declare const notFound: (req: Request, res: Response, next: NextFunction) => void;
|
|
4
|
+
//#endregion
|
|
5
|
+
//# sourceMappingURL=NotFoundHandler.d.ts.map
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { Exception } from "@thisisagile/easy";
|
|
2
|
+
import { toOriginatedError } from "@thisisagile/easy-service";
|
|
3
|
+
//#region src/express/NotFoundHandler.ts
|
|
4
|
+
const notFound = (req, res, next) => {
|
|
5
|
+
next(toOriginatedError(Exception.DoesNotExist));
|
|
6
|
+
};
|
|
7
|
+
//#endregion
|
|
8
|
+
export { notFound };
|
|
9
|
+
|
|
10
|
+
//# sourceMappingURL=NotFoundHandler.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"NotFoundHandler.js","names":[],"sources":["../../src/express/NotFoundHandler.ts"],"sourcesContent":["import { NextFunction, Request, Response } from 'express';\nimport { Exception } from '@thisisagile/easy';\nimport { toOriginatedError } from '@thisisagile/easy-service';\n\nexport const notFound = (req: Request, res: Response, next: NextFunction): void => {\n next(toOriginatedError(Exception.DoesNotExist));\n};\n"],"mappings":";;;AAIA,MAAa,YAAY,KAAc,KAAe,SAA6B;CACjF,KAAK,kBAAkB,UAAU,YAAY,CAAC;AAChD"}
|
|
@@ -1,2 +1,5 @@
|
|
|
1
|
-
import express from
|
|
1
|
+
import express from "express";
|
|
2
|
+
//#region src/express/RequestContextHandler.d.ts
|
|
2
3
|
export declare const requestContext: (req: express.Request, res: express.Response, next: express.NextFunction) => void;
|
|
4
|
+
//#endregion
|
|
5
|
+
//# sourceMappingURL=RequestContextHandler.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"RequestContextHandler.js","names":[],"sources":["../../src/express/RequestContextHandler.ts"],"sourcesContent":["import express from 'express';\nimport { ctx } from '@thisisagile/easy';\n\nexport const requestContext = (req: express.Request, res: express.Response, next: express.NextFunction): void => ctx.request.create(() => next());\n"],"mappings":";;AAGA,MAAa,kBAAkB,KAAsB,KAAuB,SAAqC,IAAI,QAAQ,aAAa,KAAK,CAAC"}
|
|
@@ -1,19 +1,22 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
3
|
-
import
|
|
1
|
+
import { Scope, UseCase } from "@thisisagile/easy";
|
|
2
|
+
import { NextFunction, Request, RequestHandler, Response } from "express";
|
|
3
|
+
import { Algorithm } from "jsonwebtoken";
|
|
4
|
+
//#region src/express/SecurityHandler.d.ts
|
|
4
5
|
type EasySecretOrKeyProvider = (request: Request, rawJwtToken: any) => Promise<string | Buffer>;
|
|
5
6
|
export interface SecurityOptions {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
7
|
+
jwtStrategyOptions?: {
|
|
8
|
+
secretOrKey?: string | Buffer;
|
|
9
|
+
secretOrKeyProvider?: EasySecretOrKeyProvider;
|
|
10
|
+
issuer?: string;
|
|
11
|
+
audience?: string;
|
|
12
|
+
algorithms?: Algorithm[];
|
|
13
|
+
};
|
|
13
14
|
}
|
|
14
15
|
export declare const checkLabCoat: () => RequestHandler;
|
|
15
16
|
export declare const checkToken: () => RequestHandler;
|
|
16
17
|
export declare const checkScope: (scope: Scope) => RequestHandler;
|
|
17
18
|
export declare const checkUseCase: (uc: UseCase) => RequestHandler;
|
|
18
19
|
export declare const security: ({ jwtStrategyOptions }?: SecurityOptions) => ((req: Request, res: Response, next: NextFunction) => void);
|
|
20
|
+
//#endregion
|
|
19
21
|
export {};
|
|
22
|
+
//# sourceMappingURL=SecurityHandler.d.ts.map
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { authError } from "./AuthError.js";
|
|
2
|
+
import { Environment, HttpStatus, ctx, ifFalse } from "@thisisagile/easy";
|
|
3
|
+
import passport from "passport";
|
|
4
|
+
import { ExtractJwt, Strategy } from "passport-jwt";
|
|
5
|
+
//#region src/express/SecurityHandler.ts
|
|
6
|
+
const checkLabCoat = () => (req, res, next) => next(ifFalse([
|
|
7
|
+
Environment.Dev,
|
|
8
|
+
Environment.Tst,
|
|
9
|
+
Environment.Acc
|
|
10
|
+
].some((e) => e.equals(ctx.env.name)), authError(HttpStatus.Forbidden)));
|
|
11
|
+
const checkToken = () => passport.authenticate("jwt", {
|
|
12
|
+
session: false,
|
|
13
|
+
failWithError: true
|
|
14
|
+
});
|
|
15
|
+
const checkScope = (scope) => (req, res, next) => next(ifFalse(req.user?.scopes?.includes(scope.id), authError(HttpStatus.Forbidden)));
|
|
16
|
+
const checkUseCase = (uc) => (req, res, next) => next(ifFalse(req.user?.usecases?.includes(uc.id), authError(HttpStatus.Forbidden)));
|
|
17
|
+
const wrapSecretOrKeyProvider = (p) => p ? (request, rawJwtToken, done) => p(request, rawJwtToken).then((t) => done(null, t)).catch((e) => done(e)) : void 0;
|
|
18
|
+
const security = ({ jwtStrategyOptions } = {}) => {
|
|
19
|
+
jwtStrategyOptions ??= {};
|
|
20
|
+
if ("secretOrKeyProvider" in jwtStrategyOptions) jwtStrategyOptions.secretOrKeyProvider = wrapSecretOrKeyProvider(jwtStrategyOptions.secretOrKeyProvider);
|
|
21
|
+
else if (!("secretOrKey" in jwtStrategyOptions)) jwtStrategyOptions.secretOrKey = ctx.env.get("tokenPublicKey");
|
|
22
|
+
const strategy = new Strategy({
|
|
23
|
+
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
|
|
24
|
+
passReqToCallback: true,
|
|
25
|
+
...jwtStrategyOptions
|
|
26
|
+
}, (req, payload, done) => {
|
|
27
|
+
ctx.request.token = payload;
|
|
28
|
+
ctx.request.jwt = ExtractJwt.fromAuthHeaderAsBearerToken()(req) ?? "";
|
|
29
|
+
done(null, payload);
|
|
30
|
+
});
|
|
31
|
+
passport.use(strategy);
|
|
32
|
+
return passport.initialize();
|
|
33
|
+
};
|
|
34
|
+
//#endregion
|
|
35
|
+
export { checkLabCoat, checkScope, checkToken, checkUseCase, security };
|
|
36
|
+
|
|
37
|
+
//# sourceMappingURL=SecurityHandler.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SecurityHandler.js","names":["JwtStrategy"],"sources":["../../src/express/SecurityHandler.ts"],"sourcesContent":["import type { NextFunction, Request, RequestHandler, Response } from 'express';\nimport passport from 'passport';\nimport { ExtractJwt, Strategy as JwtStrategy } from 'passport-jwt';\nimport type { SecretOrKeyProvider, StrategyOptionsWithRequest } from 'passport-jwt';\nimport type { Algorithm } from 'jsonwebtoken';\nimport { authError } from './AuthError';\nimport { ctx, Environment, HttpStatus, ifFalse } from '@thisisagile/easy';\nimport type { Scope, UseCase } from '@thisisagile/easy';\n\ntype EasySecretOrKeyProvider = (request: Request, rawJwtToken: any) => Promise<string | Buffer>;\n\nexport interface SecurityOptions {\n /** Configuration for verifying JWTs */\n jwtStrategyOptions?: {\n /** The secret (symmetric) or PEM-encoded public key (asymmetric) for verifying the token's signature.\n * REQUIRED unless secretOrKeyProvider is provided. Defaults to JWT_PUBLIC_KEY from the system environment. */\n secretOrKey?: string | Buffer;\n\n /** Should return a secret (symmetric) or PEM-encoded public key (asymmetric) for the given key and request combination.\n * REQUIRED unless secretOrKey is provided. Note it is up to the implementer to decode rawJwtToken. */\n secretOrKeyProvider?: EasySecretOrKeyProvider;\n\n /** If defined, the token issuer (iss) will be verified against this value. */\n issuer?: string;\n\n /** If defined, the token audience (aud) will be verified against this value. */\n audience?: string;\n\n /** If defined, the token algorithm (alg) must be in this list. */\n algorithms?: Algorithm[];\n };\n}\n\nexport const checkLabCoat = (): RequestHandler => (req, res, next) =>\n next(ifFalse([Environment.Dev, Environment.Tst, Environment.Acc].some(e => e.equals(ctx.env.name)), authError(HttpStatus.Forbidden)));\n\nexport const checkToken = (): RequestHandler => passport.authenticate('jwt', { session: false, failWithError: true });\n\nexport const checkScope =\n (scope: Scope): RequestHandler =>\n (req, res, next) =>\n next(ifFalse((req.user as any)?.scopes?.includes(scope.id), authError(HttpStatus.Forbidden)));\n\nexport const checkUseCase =\n (uc: UseCase): RequestHandler =>\n (req, res, next) =>\n next(ifFalse((req.user as any)?.usecases?.includes(uc.id), authError(HttpStatus.Forbidden)));\n\nconst wrapSecretOrKeyProvider = (p?: EasySecretOrKeyProvider): SecretOrKeyProvider | undefined =>\n p\n ? (request, rawJwtToken, done) =>\n p(request, rawJwtToken)\n .then(t => done(null, t))\n .catch(e => done(e))\n : undefined;\n\nexport const security = ({ jwtStrategyOptions }: SecurityOptions = {}): ((req: Request, res: Response, next: NextFunction) => void) => {\n jwtStrategyOptions ??= {};\n if ('secretOrKeyProvider' in jwtStrategyOptions)\n (jwtStrategyOptions as any).secretOrKeyProvider = wrapSecretOrKeyProvider(jwtStrategyOptions.secretOrKeyProvider);\n else if (!('secretOrKey' in jwtStrategyOptions)) jwtStrategyOptions.secretOrKey = ctx.env.get('tokenPublicKey') as string;\n\n const strategy = new JwtStrategy(\n {\n jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),\n passReqToCallback: true,\n ...jwtStrategyOptions,\n } as StrategyOptionsWithRequest,\n (req: Request, payload: any, done: (err: any, user: any) => void) => {\n ctx.request.token = payload;\n ctx.request.jwt = ExtractJwt.fromAuthHeaderAsBearerToken()(req) ?? '';\n done(null, payload);\n }\n );\n\n passport.use(strategy);\n return passport.initialize();\n};\n"],"mappings":";;;;;AAiCA,MAAa,sBAAsC,KAAK,KAAK,SAC3D,KAAK,QAAQ;CAAC,YAAY;CAAK,YAAY;CAAK,YAAY;AAAG,CAAC,CAAC,MAAK,MAAK,EAAE,OAAO,IAAI,IAAI,IAAI,CAAC,GAAG,UAAU,WAAW,SAAS,CAAC,CAAC;AAEtI,MAAa,mBAAmC,SAAS,aAAa,OAAO;CAAE,SAAS;CAAO,eAAe;AAAK,CAAC;AAEpH,MAAa,cACV,WACA,KAAK,KAAK,SACT,KAAK,QAAS,IAAI,MAAc,QAAQ,SAAS,MAAM,EAAE,GAAG,UAAU,WAAW,SAAS,CAAC,CAAC;AAEhG,MAAa,gBACV,QACA,KAAK,KAAK,SACT,KAAK,QAAS,IAAI,MAAc,UAAU,SAAS,GAAG,EAAE,GAAG,UAAU,WAAW,SAAS,CAAC,CAAC;AAE/F,MAAM,2BAA2B,MAC/B,KACK,SAAS,aAAa,SACrB,EAAE,SAAS,WAAW,CAAC,CACpB,MAAK,MAAK,KAAK,MAAM,CAAC,CAAC,CAAC,CACxB,OAAM,MAAK,KAAK,CAAC,CAAC,IACvB,KAAA;AAEN,MAAa,YAAY,EAAE,uBAAwC,CAAC,MAAmE;CACrI,uBAAuB,CAAC;CACxB,IAAI,yBAAyB,oBAC3B,mBAA4B,sBAAsB,wBAAwB,mBAAmB,mBAAmB;MAC7G,IAAI,EAAE,iBAAiB,qBAAqB,mBAAmB,cAAc,IAAI,IAAI,IAAI,gBAAgB;CAE9G,MAAM,WAAW,IAAIA,SACnB;EACE,gBAAgB,WAAW,4BAA4B;EACvD,mBAAmB;EACnB,GAAG;CACL,IACC,KAAc,SAAc,SAAwC;EACnE,IAAI,QAAQ,QAAQ;EACpB,IAAI,QAAQ,MAAM,WAAW,4BAA4B,CAAC,CAAC,GAAG,KAAK;EACnE,KAAK,MAAM,OAAO;CACpB,CACF;CAEA,SAAS,IAAI,QAAQ;CACrB,OAAO,SAAS,WAAW;AAC7B"}
|
package/dist/express/index.d.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
1
|
+
import { AuthError, authError, isAuthError } from "./AuthError.js";
|
|
2
|
+
import { correlation } from "./CorrelationHandler.js";
|
|
3
|
+
import { error } from "./ErrorHandler.js";
|
|
4
|
+
import { ExpressProvider, ExpressVerb, service } from "./ExpressProvider.js";
|
|
5
|
+
import { notFound } from "./NotFoundHandler.js";
|
|
6
|
+
import { requestContext } from "./RequestContextHandler.js";
|
|
7
|
+
import { SecurityOptions, checkLabCoat, checkScope, checkToken, checkUseCase, security } from "./SecurityHandler.js";
|
|
8
|
+
export { AuthError, ExpressProvider, ExpressVerb, SecurityOptions, authError, checkLabCoat, checkScope, checkToken, checkUseCase, correlation, error, isAuthError, notFound, requestContext, security, service };
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { AuthError, authError, isAuthError } from "./AuthError.js";
|
|
2
|
+
import { correlation } from "./CorrelationHandler.js";
|
|
3
|
+
import { error } from "./ErrorHandler.js";
|
|
4
|
+
import { checkLabCoat, checkScope, checkToken, checkUseCase, security } from "./SecurityHandler.js";
|
|
5
|
+
import { ExpressProvider, service } from "./ExpressProvider.js";
|
|
6
|
+
import { notFound } from "./NotFoundHandler.js";
|
|
7
|
+
import { requestContext } from "./RequestContextHandler.js";
|
|
8
|
+
export { AuthError, ExpressProvider, authError, checkLabCoat, checkScope, checkToken, checkUseCase, correlation, error, isAuthError, notFound, requestContext, security, service };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,2 +1,11 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
import { AuthError, authError, isAuthError } from "./express/AuthError.js";
|
|
2
|
+
import { correlation } from "./express/CorrelationHandler.js";
|
|
3
|
+
import { error } from "./express/ErrorHandler.js";
|
|
4
|
+
import { ExpressProvider, ExpressVerb, service } from "./express/ExpressProvider.js";
|
|
5
|
+
import { notFound } from "./express/NotFoundHandler.js";
|
|
6
|
+
import { requestContext } from "./express/RequestContextHandler.js";
|
|
7
|
+
import { SecurityOptions, checkLabCoat, checkScope, checkToken, checkUseCase, security } from "./express/SecurityHandler.js";
|
|
8
|
+
import "./express/index.js";
|
|
9
|
+
import { NamespaceContext } from "./types/NamespaceContext.js";
|
|
10
|
+
import "./types/index.js";
|
|
11
|
+
export { AuthError, ExpressProvider, ExpressVerb, NamespaceContext, SecurityOptions, authError, checkLabCoat, checkScope, checkToken, checkUseCase, correlation, error, isAuthError, notFound, requestContext, security, service };
|