@lakutata/entrypoint 3.0.0-beta.0 → 3.0.0-beta.2
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/README.md +3 -3
- package/dist/cjs/adapter/express/Responders.d.ts +21 -3
- package/dist/cjs/adapter/express/Responders.js +16 -7
- package/dist/cjs/adapter/fastify/Responders.d.ts +21 -3
- package/dist/cjs/adapter/fastify/Responders.js +15 -6
- package/dist/cjs/lib/Envelope.d.ts +13 -0
- package/dist/cjs/lib/Envelope.js +32 -0
- package/dist/esm/adapter/express/Responders.js +16 -7
- package/dist/esm/adapter/fastify/Responders.js +15 -6
- package/dist/esm/lib/Envelope.js +28 -0
- package/dist/types/adapter/express/Responders.d.ts +21 -3
- package/dist/types/adapter/fastify/Responders.d.ts +21 -3
- package/dist/types/lib/Envelope.d.ts +13 -0
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -111,7 +111,7 @@ import {buildEntrypoints, Controller, type ActionInvocation, type Next} from 'la
|
|
|
111
111
|
import {GET, UseMiddlewares} from 'lakutata/decorator/ctrl'
|
|
112
112
|
|
|
113
113
|
//Runs around the action: before its input validation and its access control
|
|
114
|
-
async function
|
|
114
|
+
async function timing(invocation: ActionInvocation, next: Next): Promise<unknown> {
|
|
115
115
|
const started: number = Date.now()
|
|
116
116
|
try {
|
|
117
117
|
return await next()
|
|
@@ -120,7 +120,7 @@ async function Timing(invocation: ActionInvocation, next: Next): Promise<unknown
|
|
|
120
120
|
}
|
|
121
121
|
}
|
|
122
122
|
|
|
123
|
-
@UseMiddlewares(
|
|
123
|
+
@UseMiddlewares(timing)
|
|
124
124
|
class HealthController extends Controller {
|
|
125
125
|
@GET('/health')
|
|
126
126
|
public async health(): Promise<string> {
|
|
@@ -132,7 +132,7 @@ Application.run(() => ({
|
|
|
132
132
|
id: 'health.app',
|
|
133
133
|
name: 'Health',
|
|
134
134
|
components: {
|
|
135
|
-
entrypoint: buildEntrypoints({controllers: [HealthController], middlewares: [
|
|
135
|
+
entrypoint: buildEntrypoints({controllers: [HealthController], middlewares: [timing]})
|
|
136
136
|
}
|
|
137
137
|
}))
|
|
138
138
|
```
|
|
@@ -49,12 +49,23 @@ export type JSONEnvelopeOptions = {
|
|
|
49
49
|
* @default `errorStatus`
|
|
50
50
|
*/
|
|
51
51
|
actionErrorStatus?: number;
|
|
52
|
+
/**
|
|
53
|
+
* The extra fields of the envelope, such as the `timestamp`, `nonce` or `requestId` of the envelopes of the
|
|
54
|
+
* applications of lakutata 2.x: called for each response (a result or an error), its fields added after `errno`,
|
|
55
|
+
* `errMsg` and `data`, which they never replace. When it throws, a result is answered as an error, and an error
|
|
56
|
+
* without the extra fields.
|
|
57
|
+
* @param request The express request being answered.
|
|
58
|
+
* @returns The fields to add to the envelope.
|
|
59
|
+
* @default no extra field
|
|
60
|
+
*/
|
|
61
|
+
extra?: (request: Request) => Record<string, unknown>;
|
|
52
62
|
};
|
|
53
63
|
/**
|
|
54
64
|
* Create a responder for {@link expressHTTPEntrypoint} that wraps every response in the envelope
|
|
55
65
|
* `{errno, errMsg, data}`: a result as `{errno: null, errMsg: null, data}` (`data` is `null` when the action returned
|
|
56
66
|
* nothing, and its status is not turned into 204), an error as `{errno, errMsg, data: null}` with the error's status,
|
|
57
|
-
* the `errorStatus` option, or for the errors of the actions the `actionErrorStatus` option
|
|
67
|
+
* the `errorStatus` option, or for the errors of the actions the `actionErrorStatus` option; the `extra` option adds
|
|
68
|
+
* fields to both.
|
|
58
69
|
* @param options The envelope options.
|
|
59
70
|
* @returns The responder, to give to the `respond` option of `expressHTTPEntrypoint`.
|
|
60
71
|
* @example
|
|
@@ -79,8 +90,15 @@ export type JSONEnvelopeOptions = {
|
|
|
79
90
|
* entrypoint: buildEntrypoints({
|
|
80
91
|
* controllers: [PingController],
|
|
81
92
|
* //The errors of the actions answered with 200 too (as the applications of lakutata 2.x), the routes not
|
|
82
|
-
* //found and the errors of the express middlewares with their status (404, 401…)
|
|
83
|
-
*
|
|
93
|
+
* //found and the errors of the express middlewares with their status (404, 401…); every envelope with
|
|
94
|
+
* //its timestamp ({"errno": null, "errMsg": null, "data": "pong", "timestamp": 1767225600000})
|
|
95
|
+
* http: expressHTTPEntrypoint({
|
|
96
|
+
* port: 8080,
|
|
97
|
+
* respond: jsonEnvelope({
|
|
98
|
+
* actionErrorStatus: 200,
|
|
99
|
+
* extra: (): Record<string, unknown> => ({timestamp: Date.now()})
|
|
100
|
+
* })
|
|
101
|
+
* })
|
|
84
102
|
* })
|
|
85
103
|
* }
|
|
86
104
|
* })
|
|
@@ -5,6 +5,7 @@ exports.jsonEnvelope = jsonEnvelope;
|
|
|
5
5
|
const HTTPErrors_js_1 = require("../../lib/HTTPErrors.js");
|
|
6
6
|
Object.defineProperty(exports, "describeError", { enumerable: true, get: function () { return HTTPErrors_js_1.describeError; } });
|
|
7
7
|
Object.defineProperty(exports, "statusOfError", { enumerable: true, get: function () { return HTTPErrors_js_1.statusOfError; } });
|
|
8
|
+
const Envelope_js_1 = require("../../lib/Envelope.js");
|
|
8
9
|
/**
|
|
9
10
|
* The default responder of {@link expressHTTPEntrypoint}: a result is sent as JSON, no result as an empty 204 (or the
|
|
10
11
|
* status set by a `StatusResult`); an error is sent as `{errno, errMsg}` (see `describeError`) with its HTTP status
|
|
@@ -26,7 +27,8 @@ exports.JSONResponder = {
|
|
|
26
27
|
* Create a responder for {@link expressHTTPEntrypoint} that wraps every response in the envelope
|
|
27
28
|
* `{errno, errMsg, data}`: a result as `{errno: null, errMsg: null, data}` (`data` is `null` when the action returned
|
|
28
29
|
* nothing, and its status is not turned into 204), an error as `{errno, errMsg, data: null}` with the error's status,
|
|
29
|
-
* the `errorStatus` option, or for the errors of the actions the `actionErrorStatus` option
|
|
30
|
+
* the `errorStatus` option, or for the errors of the actions the `actionErrorStatus` option; the `extra` option adds
|
|
31
|
+
* fields to both.
|
|
30
32
|
* @param options The envelope options.
|
|
31
33
|
* @returns The responder, to give to the `respond` option of `expressHTTPEntrypoint`.
|
|
32
34
|
* @example
|
|
@@ -51,8 +53,15 @@ exports.JSONResponder = {
|
|
|
51
53
|
* entrypoint: buildEntrypoints({
|
|
52
54
|
* controllers: [PingController],
|
|
53
55
|
* //The errors of the actions answered with 200 too (as the applications of lakutata 2.x), the routes not
|
|
54
|
-
* //found and the errors of the express middlewares with their status (404, 401…)
|
|
55
|
-
*
|
|
56
|
+
* //found and the errors of the express middlewares with their status (404, 401…); every envelope with
|
|
57
|
+
* //its timestamp ({"errno": null, "errMsg": null, "data": "pong", "timestamp": 1767225600000})
|
|
58
|
+
* http: expressHTTPEntrypoint({
|
|
59
|
+
* port: 8080,
|
|
60
|
+
* respond: jsonEnvelope({
|
|
61
|
+
* actionErrorStatus: 200,
|
|
62
|
+
* extra: (): Record<string, unknown> => ({timestamp: Date.now()})
|
|
63
|
+
* })
|
|
64
|
+
* })
|
|
56
65
|
* })
|
|
57
66
|
* }
|
|
58
67
|
* })
|
|
@@ -60,12 +69,12 @@ exports.JSONResponder = {
|
|
|
60
69
|
*/
|
|
61
70
|
function jsonEnvelope(options = {}) {
|
|
62
71
|
return {
|
|
63
|
-
success: (response, data) => {
|
|
64
|
-
response.json({ errno: null, errMsg: null, data: data === undefined ? null : data });
|
|
72
|
+
success: (response, data, request) => {
|
|
73
|
+
response.json((0, Envelope_js_1.envelopeOf)({ errno: null, errMsg: null, data: data === undefined ? null : data }, options.extra?.(request)));
|
|
65
74
|
},
|
|
66
|
-
error: (response, error,
|
|
75
|
+
error: (response, error, request, origin) => {
|
|
67
76
|
const status = origin === 'action' ? options.actionErrorStatus ?? options.errorStatus : options.errorStatus;
|
|
68
|
-
response.status(status ?? (0, HTTPErrors_js_1.statusOfError)(error)).json({ ...(0, HTTPErrors_js_1.describeError)(error), data: null });
|
|
77
|
+
response.status(status ?? (0, HTTPErrors_js_1.statusOfError)(error)).json((0, Envelope_js_1.envelopeOf)({ ...(0, HTTPErrors_js_1.describeError)(error), data: null }, (0, Envelope_js_1.errorExtraOf)(options.extra, request)));
|
|
69
78
|
}
|
|
70
79
|
};
|
|
71
80
|
}
|
|
@@ -48,12 +48,23 @@ export type FastifyJSONEnvelopeOptions = {
|
|
|
48
48
|
* @default `errorStatus`
|
|
49
49
|
*/
|
|
50
50
|
actionErrorStatus?: number;
|
|
51
|
+
/**
|
|
52
|
+
* The extra fields of the envelope, such as the `timestamp`, `nonce` or `requestId` of the envelopes of the
|
|
53
|
+
* applications of lakutata 2.x: called for each response (a result or an error), its fields added after `errno`,
|
|
54
|
+
* `errMsg` and `data`, which they never replace. When it throws, a result is answered as an error, and an error
|
|
55
|
+
* without the extra fields.
|
|
56
|
+
* @param request The fastify request being answered.
|
|
57
|
+
* @returns The fields to add to the envelope.
|
|
58
|
+
* @default no extra field
|
|
59
|
+
*/
|
|
60
|
+
extra?: (request: FastifyRequest) => Record<string, unknown>;
|
|
51
61
|
};
|
|
52
62
|
/**
|
|
53
63
|
* Create a responder for {@link fastifyHTTPEntrypoint} that wraps every response in the envelope
|
|
54
64
|
* `{errno, errMsg, data}`: a result as `{errno: null, errMsg: null, data}` (`data` is `null` when the action returned
|
|
55
65
|
* nothing, and its status is not turned into 204), an error as `{errno, errMsg, data: null}` with the error's status,
|
|
56
|
-
* the `errorStatus` option, or for the errors of the actions the `actionErrorStatus` option
|
|
66
|
+
* the `errorStatus` option, or for the errors of the actions the `actionErrorStatus` option; the `extra` option adds
|
|
67
|
+
* fields to both.
|
|
57
68
|
* @param options The envelope options.
|
|
58
69
|
* @returns The responder, to give to the `respond` option of `fastifyHTTPEntrypoint`.
|
|
59
70
|
* @example
|
|
@@ -78,8 +89,15 @@ export type FastifyJSONEnvelopeOptions = {
|
|
|
78
89
|
* entrypoint: buildEntrypoints({
|
|
79
90
|
* controllers: [PingController],
|
|
80
91
|
* //The errors of the actions answered with 200 too (as the applications of lakutata 2.x), the routes not
|
|
81
|
-
* //found and the errors of the hooks with their status (404, 401…)
|
|
82
|
-
*
|
|
92
|
+
* //found and the errors of the hooks with their status (404, 401…); every envelope with
|
|
93
|
+
* //its timestamp ({"errno": null, "errMsg": null, "data": "pong", "timestamp": 1767225600000})
|
|
94
|
+
* http: fastifyHTTPEntrypoint({
|
|
95
|
+
* port: 8080,
|
|
96
|
+
* respond: fastifyJSONEnvelope({
|
|
97
|
+
* actionErrorStatus: 200,
|
|
98
|
+
* extra: (): Record<string, unknown> => ({timestamp: Date.now()})
|
|
99
|
+
* })
|
|
100
|
+
* })
|
|
83
101
|
* })
|
|
84
102
|
* }
|
|
85
103
|
* })
|
|
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.FastifyJSONResponder = void 0;
|
|
4
4
|
exports.fastifyJSONEnvelope = fastifyJSONEnvelope;
|
|
5
5
|
const HTTPErrors_js_1 = require("../../lib/HTTPErrors.js");
|
|
6
|
+
const Envelope_js_1 = require("../../lib/Envelope.js");
|
|
6
7
|
/**
|
|
7
8
|
* Send JSON
|
|
8
9
|
* @param reply
|
|
@@ -33,7 +34,8 @@ exports.FastifyJSONResponder = {
|
|
|
33
34
|
* Create a responder for {@link fastifyHTTPEntrypoint} that wraps every response in the envelope
|
|
34
35
|
* `{errno, errMsg, data}`: a result as `{errno: null, errMsg: null, data}` (`data` is `null` when the action returned
|
|
35
36
|
* nothing, and its status is not turned into 204), an error as `{errno, errMsg, data: null}` with the error's status,
|
|
36
|
-
* the `errorStatus` option, or for the errors of the actions the `actionErrorStatus` option
|
|
37
|
+
* the `errorStatus` option, or for the errors of the actions the `actionErrorStatus` option; the `extra` option adds
|
|
38
|
+
* fields to both.
|
|
37
39
|
* @param options The envelope options.
|
|
38
40
|
* @returns The responder, to give to the `respond` option of `fastifyHTTPEntrypoint`.
|
|
39
41
|
* @example
|
|
@@ -58,8 +60,15 @@ exports.FastifyJSONResponder = {
|
|
|
58
60
|
* entrypoint: buildEntrypoints({
|
|
59
61
|
* controllers: [PingController],
|
|
60
62
|
* //The errors of the actions answered with 200 too (as the applications of lakutata 2.x), the routes not
|
|
61
|
-
* //found and the errors of the hooks with their status (404, 401…)
|
|
62
|
-
*
|
|
63
|
+
* //found and the errors of the hooks with their status (404, 401…); every envelope with
|
|
64
|
+
* //its timestamp ({"errno": null, "errMsg": null, "data": "pong", "timestamp": 1767225600000})
|
|
65
|
+
* http: fastifyHTTPEntrypoint({
|
|
66
|
+
* port: 8080,
|
|
67
|
+
* respond: fastifyJSONEnvelope({
|
|
68
|
+
* actionErrorStatus: 200,
|
|
69
|
+
* extra: (): Record<string, unknown> => ({timestamp: Date.now()})
|
|
70
|
+
* })
|
|
71
|
+
* })
|
|
63
72
|
* })
|
|
64
73
|
* }
|
|
65
74
|
* })
|
|
@@ -67,11 +76,11 @@ exports.FastifyJSONResponder = {
|
|
|
67
76
|
*/
|
|
68
77
|
function fastifyJSONEnvelope(options = {}) {
|
|
69
78
|
return {
|
|
70
|
-
success: (reply, data) => sendJSON(reply, { errno: null, errMsg: null, data: data === undefined ? null : data }),
|
|
71
|
-
error: (reply, error,
|
|
79
|
+
success: (reply, data, request) => sendJSON(reply, (0, Envelope_js_1.envelopeOf)({ errno: null, errMsg: null, data: data === undefined ? null : data }, options.extra?.(request))),
|
|
80
|
+
error: (reply, error, request, origin) => {
|
|
72
81
|
const status = origin === 'action' ? options.actionErrorStatus ?? options.errorStatus : options.errorStatus;
|
|
73
82
|
reply.code(status ?? (0, HTTPErrors_js_1.statusOfError)(error));
|
|
74
|
-
sendJSON(reply, { ...(0, HTTPErrors_js_1.describeError)(error), data: null });
|
|
83
|
+
sendJSON(reply, (0, Envelope_js_1.envelopeOf)({ ...(0, HTTPErrors_js_1.describeError)(error), data: null }, (0, Envelope_js_1.errorExtraOf)(options.extra, request)));
|
|
75
84
|
}
|
|
76
85
|
};
|
|
77
86
|
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The envelope of a response: its fields, then the extra fields not named as one of them (an extra field never
|
|
3
|
+
* replaces errno, errMsg or data)
|
|
4
|
+
* @param fields The fields of the envelope.
|
|
5
|
+
* @param extra The extra fields, ignored when they are not an object.
|
|
6
|
+
*/
|
|
7
|
+
export declare function envelopeOf(fields: Record<string, unknown>, extra: unknown): Record<string, unknown>;
|
|
8
|
+
/**
|
|
9
|
+
* The extra fields of an error response: none when the function giving them fails, an error being answered anyway
|
|
10
|
+
* @param extra The function giving the extra fields.
|
|
11
|
+
* @param request The request answered.
|
|
12
|
+
*/
|
|
13
|
+
export declare function errorExtraOf<Request>(extra: ((request: Request) => Record<string, unknown>) | undefined, request: Request): unknown;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.envelopeOf = envelopeOf;
|
|
4
|
+
exports.errorExtraOf = errorExtraOf;
|
|
5
|
+
/**
|
|
6
|
+
* The envelope of a response: its fields, then the extra fields not named as one of them (an extra field never
|
|
7
|
+
* replaces errno, errMsg or data)
|
|
8
|
+
* @param fields The fields of the envelope.
|
|
9
|
+
* @param extra The extra fields, ignored when they are not an object.
|
|
10
|
+
*/
|
|
11
|
+
function envelopeOf(fields, extra) {
|
|
12
|
+
const envelope = { ...fields };
|
|
13
|
+
if (extra === null || typeof extra !== 'object')
|
|
14
|
+
return envelope;
|
|
15
|
+
for (const [key, value] of Object.entries(extra))
|
|
16
|
+
if (!Object.prototype.hasOwnProperty.call(envelope, key))
|
|
17
|
+
envelope[key] = value;
|
|
18
|
+
return envelope;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* The extra fields of an error response: none when the function giving them fails, an error being answered anyway
|
|
22
|
+
* @param extra The function giving the extra fields.
|
|
23
|
+
* @param request The request answered.
|
|
24
|
+
*/
|
|
25
|
+
function errorExtraOf(extra, request) {
|
|
26
|
+
try {
|
|
27
|
+
return extra?.(request);
|
|
28
|
+
}
|
|
29
|
+
catch {
|
|
30
|
+
return undefined;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { describeError, statusOfError } from '../../lib/HTTPErrors.js';
|
|
2
|
+
import { envelopeOf, errorExtraOf } from '../../lib/Envelope.js';
|
|
2
3
|
export { describeError, statusOfError };
|
|
3
4
|
/**
|
|
4
5
|
* The default responder of {@link expressHTTPEntrypoint}: a result is sent as JSON, no result as an empty 204 (or the
|
|
@@ -21,7 +22,8 @@ export const JSONResponder = {
|
|
|
21
22
|
* Create a responder for {@link expressHTTPEntrypoint} that wraps every response in the envelope
|
|
22
23
|
* `{errno, errMsg, data}`: a result as `{errno: null, errMsg: null, data}` (`data` is `null` when the action returned
|
|
23
24
|
* nothing, and its status is not turned into 204), an error as `{errno, errMsg, data: null}` with the error's status,
|
|
24
|
-
* the `errorStatus` option, or for the errors of the actions the `actionErrorStatus` option
|
|
25
|
+
* the `errorStatus` option, or for the errors of the actions the `actionErrorStatus` option; the `extra` option adds
|
|
26
|
+
* fields to both.
|
|
25
27
|
* @param options The envelope options.
|
|
26
28
|
* @returns The responder, to give to the `respond` option of `expressHTTPEntrypoint`.
|
|
27
29
|
* @example
|
|
@@ -46,8 +48,15 @@ export const JSONResponder = {
|
|
|
46
48
|
* entrypoint: buildEntrypoints({
|
|
47
49
|
* controllers: [PingController],
|
|
48
50
|
* //The errors of the actions answered with 200 too (as the applications of lakutata 2.x), the routes not
|
|
49
|
-
* //found and the errors of the express middlewares with their status (404, 401…)
|
|
50
|
-
*
|
|
51
|
+
* //found and the errors of the express middlewares with their status (404, 401…); every envelope with
|
|
52
|
+
* //its timestamp ({"errno": null, "errMsg": null, "data": "pong", "timestamp": 1767225600000})
|
|
53
|
+
* http: expressHTTPEntrypoint({
|
|
54
|
+
* port: 8080,
|
|
55
|
+
* respond: jsonEnvelope({
|
|
56
|
+
* actionErrorStatus: 200,
|
|
57
|
+
* extra: (): Record<string, unknown> => ({timestamp: Date.now()})
|
|
58
|
+
* })
|
|
59
|
+
* })
|
|
51
60
|
* })
|
|
52
61
|
* }
|
|
53
62
|
* })
|
|
@@ -55,12 +64,12 @@ export const JSONResponder = {
|
|
|
55
64
|
*/
|
|
56
65
|
export function jsonEnvelope(options = {}) {
|
|
57
66
|
return {
|
|
58
|
-
success: (response, data) => {
|
|
59
|
-
response.json({ errno: null, errMsg: null, data: data === undefined ? null : data });
|
|
67
|
+
success: (response, data, request) => {
|
|
68
|
+
response.json(envelopeOf({ errno: null, errMsg: null, data: data === undefined ? null : data }, options.extra?.(request)));
|
|
60
69
|
},
|
|
61
|
-
error: (response, error,
|
|
70
|
+
error: (response, error, request, origin) => {
|
|
62
71
|
const status = origin === 'action' ? options.actionErrorStatus ?? options.errorStatus : options.errorStatus;
|
|
63
|
-
response.status(status ?? statusOfError(error)).json({ ...describeError(error), data: null });
|
|
72
|
+
response.status(status ?? statusOfError(error)).json(envelopeOf({ ...describeError(error), data: null }, errorExtraOf(options.extra, request)));
|
|
64
73
|
}
|
|
65
74
|
};
|
|
66
75
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { describeError, statusOfError } from '../../lib/HTTPErrors.js';
|
|
2
|
+
import { envelopeOf, errorExtraOf } from '../../lib/Envelope.js';
|
|
2
3
|
/**
|
|
3
4
|
* Send JSON
|
|
4
5
|
* @param reply
|
|
@@ -29,7 +30,8 @@ export const FastifyJSONResponder = {
|
|
|
29
30
|
* Create a responder for {@link fastifyHTTPEntrypoint} that wraps every response in the envelope
|
|
30
31
|
* `{errno, errMsg, data}`: a result as `{errno: null, errMsg: null, data}` (`data` is `null` when the action returned
|
|
31
32
|
* nothing, and its status is not turned into 204), an error as `{errno, errMsg, data: null}` with the error's status,
|
|
32
|
-
* the `errorStatus` option, or for the errors of the actions the `actionErrorStatus` option
|
|
33
|
+
* the `errorStatus` option, or for the errors of the actions the `actionErrorStatus` option; the `extra` option adds
|
|
34
|
+
* fields to both.
|
|
33
35
|
* @param options The envelope options.
|
|
34
36
|
* @returns The responder, to give to the `respond` option of `fastifyHTTPEntrypoint`.
|
|
35
37
|
* @example
|
|
@@ -54,8 +56,15 @@ export const FastifyJSONResponder = {
|
|
|
54
56
|
* entrypoint: buildEntrypoints({
|
|
55
57
|
* controllers: [PingController],
|
|
56
58
|
* //The errors of the actions answered with 200 too (as the applications of lakutata 2.x), the routes not
|
|
57
|
-
* //found and the errors of the hooks with their status (404, 401…)
|
|
58
|
-
*
|
|
59
|
+
* //found and the errors of the hooks with their status (404, 401…); every envelope with
|
|
60
|
+
* //its timestamp ({"errno": null, "errMsg": null, "data": "pong", "timestamp": 1767225600000})
|
|
61
|
+
* http: fastifyHTTPEntrypoint({
|
|
62
|
+
* port: 8080,
|
|
63
|
+
* respond: fastifyJSONEnvelope({
|
|
64
|
+
* actionErrorStatus: 200,
|
|
65
|
+
* extra: (): Record<string, unknown> => ({timestamp: Date.now()})
|
|
66
|
+
* })
|
|
67
|
+
* })
|
|
59
68
|
* })
|
|
60
69
|
* }
|
|
61
70
|
* })
|
|
@@ -63,11 +72,11 @@ export const FastifyJSONResponder = {
|
|
|
63
72
|
*/
|
|
64
73
|
export function fastifyJSONEnvelope(options = {}) {
|
|
65
74
|
return {
|
|
66
|
-
success: (reply, data) => sendJSON(reply, { errno: null, errMsg: null, data: data === undefined ? null : data }),
|
|
67
|
-
error: (reply, error,
|
|
75
|
+
success: (reply, data, request) => sendJSON(reply, envelopeOf({ errno: null, errMsg: null, data: data === undefined ? null : data }, options.extra?.(request))),
|
|
76
|
+
error: (reply, error, request, origin) => {
|
|
68
77
|
const status = origin === 'action' ? options.actionErrorStatus ?? options.errorStatus : options.errorStatus;
|
|
69
78
|
reply.code(status ?? statusOfError(error));
|
|
70
|
-
sendJSON(reply, { ...describeError(error), data: null });
|
|
79
|
+
sendJSON(reply, envelopeOf({ ...describeError(error), data: null }, errorExtraOf(options.extra, request)));
|
|
71
80
|
}
|
|
72
81
|
};
|
|
73
82
|
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The envelope of a response: its fields, then the extra fields not named as one of them (an extra field never
|
|
3
|
+
* replaces errno, errMsg or data)
|
|
4
|
+
* @param fields The fields of the envelope.
|
|
5
|
+
* @param extra The extra fields, ignored when they are not an object.
|
|
6
|
+
*/
|
|
7
|
+
export function envelopeOf(fields, extra) {
|
|
8
|
+
const envelope = { ...fields };
|
|
9
|
+
if (extra === null || typeof extra !== 'object')
|
|
10
|
+
return envelope;
|
|
11
|
+
for (const [key, value] of Object.entries(extra))
|
|
12
|
+
if (!Object.prototype.hasOwnProperty.call(envelope, key))
|
|
13
|
+
envelope[key] = value;
|
|
14
|
+
return envelope;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* The extra fields of an error response: none when the function giving them fails, an error being answered anyway
|
|
18
|
+
* @param extra The function giving the extra fields.
|
|
19
|
+
* @param request The request answered.
|
|
20
|
+
*/
|
|
21
|
+
export function errorExtraOf(extra, request) {
|
|
22
|
+
try {
|
|
23
|
+
return extra?.(request);
|
|
24
|
+
}
|
|
25
|
+
catch {
|
|
26
|
+
return undefined;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
@@ -49,12 +49,23 @@ export type JSONEnvelopeOptions = {
|
|
|
49
49
|
* @default `errorStatus`
|
|
50
50
|
*/
|
|
51
51
|
actionErrorStatus?: number;
|
|
52
|
+
/**
|
|
53
|
+
* The extra fields of the envelope, such as the `timestamp`, `nonce` or `requestId` of the envelopes of the
|
|
54
|
+
* applications of lakutata 2.x: called for each response (a result or an error), its fields added after `errno`,
|
|
55
|
+
* `errMsg` and `data`, which they never replace. When it throws, a result is answered as an error, and an error
|
|
56
|
+
* without the extra fields.
|
|
57
|
+
* @param request The express request being answered.
|
|
58
|
+
* @returns The fields to add to the envelope.
|
|
59
|
+
* @default no extra field
|
|
60
|
+
*/
|
|
61
|
+
extra?: (request: Request) => Record<string, unknown>;
|
|
52
62
|
};
|
|
53
63
|
/**
|
|
54
64
|
* Create a responder for {@link expressHTTPEntrypoint} that wraps every response in the envelope
|
|
55
65
|
* `{errno, errMsg, data}`: a result as `{errno: null, errMsg: null, data}` (`data` is `null` when the action returned
|
|
56
66
|
* nothing, and its status is not turned into 204), an error as `{errno, errMsg, data: null}` with the error's status,
|
|
57
|
-
* the `errorStatus` option, or for the errors of the actions the `actionErrorStatus` option
|
|
67
|
+
* the `errorStatus` option, or for the errors of the actions the `actionErrorStatus` option; the `extra` option adds
|
|
68
|
+
* fields to both.
|
|
58
69
|
* @param options The envelope options.
|
|
59
70
|
* @returns The responder, to give to the `respond` option of `expressHTTPEntrypoint`.
|
|
60
71
|
* @example
|
|
@@ -79,8 +90,15 @@ export type JSONEnvelopeOptions = {
|
|
|
79
90
|
* entrypoint: buildEntrypoints({
|
|
80
91
|
* controllers: [PingController],
|
|
81
92
|
* //The errors of the actions answered with 200 too (as the applications of lakutata 2.x), the routes not
|
|
82
|
-
* //found and the errors of the express middlewares with their status (404, 401…)
|
|
83
|
-
*
|
|
93
|
+
* //found and the errors of the express middlewares with their status (404, 401…); every envelope with
|
|
94
|
+
* //its timestamp ({"errno": null, "errMsg": null, "data": "pong", "timestamp": 1767225600000})
|
|
95
|
+
* http: expressHTTPEntrypoint({
|
|
96
|
+
* port: 8080,
|
|
97
|
+
* respond: jsonEnvelope({
|
|
98
|
+
* actionErrorStatus: 200,
|
|
99
|
+
* extra: (): Record<string, unknown> => ({timestamp: Date.now()})
|
|
100
|
+
* })
|
|
101
|
+
* })
|
|
84
102
|
* })
|
|
85
103
|
* }
|
|
86
104
|
* })
|
|
@@ -48,12 +48,23 @@ export type FastifyJSONEnvelopeOptions = {
|
|
|
48
48
|
* @default `errorStatus`
|
|
49
49
|
*/
|
|
50
50
|
actionErrorStatus?: number;
|
|
51
|
+
/**
|
|
52
|
+
* The extra fields of the envelope, such as the `timestamp`, `nonce` or `requestId` of the envelopes of the
|
|
53
|
+
* applications of lakutata 2.x: called for each response (a result or an error), its fields added after `errno`,
|
|
54
|
+
* `errMsg` and `data`, which they never replace. When it throws, a result is answered as an error, and an error
|
|
55
|
+
* without the extra fields.
|
|
56
|
+
* @param request The fastify request being answered.
|
|
57
|
+
* @returns The fields to add to the envelope.
|
|
58
|
+
* @default no extra field
|
|
59
|
+
*/
|
|
60
|
+
extra?: (request: FastifyRequest) => Record<string, unknown>;
|
|
51
61
|
};
|
|
52
62
|
/**
|
|
53
63
|
* Create a responder for {@link fastifyHTTPEntrypoint} that wraps every response in the envelope
|
|
54
64
|
* `{errno, errMsg, data}`: a result as `{errno: null, errMsg: null, data}` (`data` is `null` when the action returned
|
|
55
65
|
* nothing, and its status is not turned into 204), an error as `{errno, errMsg, data: null}` with the error's status,
|
|
56
|
-
* the `errorStatus` option, or for the errors of the actions the `actionErrorStatus` option
|
|
66
|
+
* the `errorStatus` option, or for the errors of the actions the `actionErrorStatus` option; the `extra` option adds
|
|
67
|
+
* fields to both.
|
|
57
68
|
* @param options The envelope options.
|
|
58
69
|
* @returns The responder, to give to the `respond` option of `fastifyHTTPEntrypoint`.
|
|
59
70
|
* @example
|
|
@@ -78,8 +89,15 @@ export type FastifyJSONEnvelopeOptions = {
|
|
|
78
89
|
* entrypoint: buildEntrypoints({
|
|
79
90
|
* controllers: [PingController],
|
|
80
91
|
* //The errors of the actions answered with 200 too (as the applications of lakutata 2.x), the routes not
|
|
81
|
-
* //found and the errors of the hooks with their status (404, 401…)
|
|
82
|
-
*
|
|
92
|
+
* //found and the errors of the hooks with their status (404, 401…); every envelope with
|
|
93
|
+
* //its timestamp ({"errno": null, "errMsg": null, "data": "pong", "timestamp": 1767225600000})
|
|
94
|
+
* http: fastifyHTTPEntrypoint({
|
|
95
|
+
* port: 8080,
|
|
96
|
+
* respond: fastifyJSONEnvelope({
|
|
97
|
+
* actionErrorStatus: 200,
|
|
98
|
+
* extra: (): Record<string, unknown> => ({timestamp: Date.now()})
|
|
99
|
+
* })
|
|
100
|
+
* })
|
|
83
101
|
* })
|
|
84
102
|
* }
|
|
85
103
|
* })
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The envelope of a response: its fields, then the extra fields not named as one of them (an extra field never
|
|
3
|
+
* replaces errno, errMsg or data)
|
|
4
|
+
* @param fields The fields of the envelope.
|
|
5
|
+
* @param extra The extra fields, ignored when they are not an object.
|
|
6
|
+
*/
|
|
7
|
+
export declare function envelopeOf(fields: Record<string, unknown>, extra: unknown): Record<string, unknown>;
|
|
8
|
+
/**
|
|
9
|
+
* The extra fields of an error response: none when the function giving them fails, an error being answered anyway
|
|
10
|
+
* @param extra The function giving the extra fields.
|
|
11
|
+
* @param request The request answered.
|
|
12
|
+
*/
|
|
13
|
+
export declare function errorExtraOf<Request>(extra: ((request: Request) => Record<string, unknown>) | undefined, request: Request): unknown;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lakutata/entrypoint",
|
|
3
|
-
"version": "3.0.0-beta.
|
|
3
|
+
"version": "3.0.0-beta.2",
|
|
4
4
|
"description": "The entrypoint component of the lakutata framework: controllers and the dispatch of the CLI, HTTP and service actions.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/cjs/exports/Entrypoint.js",
|
|
@@ -110,8 +110,8 @@
|
|
|
110
110
|
},
|
|
111
111
|
"peerDependencies": {
|
|
112
112
|
"@inquirer/prompts": "^7.0.0 || ^8.0.0",
|
|
113
|
-
"@lakutata/core": "3.0.0-beta.
|
|
114
|
-
"@lakutata/utils": "3.0.0-beta.
|
|
113
|
+
"@lakutata/core": "3.0.0-beta.2",
|
|
114
|
+
"@lakutata/utils": "3.0.0-beta.2",
|
|
115
115
|
"commander": "^12.0.0 || ^13.0.0 || ^14.0.0 || ^15.0.0",
|
|
116
116
|
"express": "^4.21.0 || ^5.0.0",
|
|
117
117
|
"fastify": "^5.0.0",
|