@rest-vir/run-service 0.18.1 → 0.19.1
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/handle-request/endpoint-handler.d.ts +10 -1
- package/dist/handle-request/endpoint-handler.js +22 -1
- package/dist/handle-request/handle-route.d.ts +19 -6
- package/dist/handle-request/handle-route.js +25 -9
- package/dist/handle-request/pre-handler.d.ts +16 -10
- package/dist/handle-request/pre-handler.js +23 -20
- package/dist/handle-request/run-post-hook.d.ts +17 -0
- package/dist/handle-request/run-post-hook.js +59 -0
- package/dist/start-service/attach-service.d.ts +1 -0
- package/dist/start-service/attach-service.js +88 -9
- package/dist/start-service/start-service.d.ts +1 -0
- package/dist/test/test-service.d.ts +3 -0
- package/package.json +10 -10
|
@@ -65,6 +65,15 @@ export type EndpointHandlerParams = {
|
|
|
65
65
|
* @package [`@rest-vir/run-service`](https://www.npmjs.com/package/@rest-vir/run-service)
|
|
66
66
|
*/
|
|
67
67
|
export type EndpointHandler = (params: Readonly<EndpointHandlerParams>) => MaybePromise<HandledOutput>;
|
|
68
|
+
/**
|
|
69
|
+
* Handle the output of a handler without sending the response. Similar to
|
|
70
|
+
* {@link handleHandlerOutput} but this one does not send the response.
|
|
71
|
+
*
|
|
72
|
+
* @category Internal
|
|
73
|
+
* @category Package : @rest-vir/run-service
|
|
74
|
+
* @package [`@rest-vir/run-service`](https://www.npmjs.com/package/@rest-vir/run-service)
|
|
75
|
+
*/
|
|
76
|
+
export declare function handleHandlerOutputWithoutSending(result: Readonly<HandledOutput>, response: ServerResponse): undefined | HandledOutput;
|
|
68
77
|
/**
|
|
69
78
|
* Handle the output of a handler. Setting headers, sending the response, etc.
|
|
70
79
|
*
|
|
@@ -72,4 +81,4 @@ export type EndpointHandler = (params: Readonly<EndpointHandlerParams>) => Maybe
|
|
|
72
81
|
* @category Package : @rest-vir/run-service
|
|
73
82
|
* @package [`@rest-vir/run-service`](https://www.npmjs.com/package/@rest-vir/run-service)
|
|
74
83
|
*/
|
|
75
|
-
export declare function
|
|
84
|
+
export declare function handleHandlerOutput(result: Readonly<HandledOutput>, response: ServerResponse): undefined | FastifyReply;
|
|
@@ -1,5 +1,26 @@
|
|
|
1
1
|
/* node:coverage disable: this file is just types */
|
|
2
2
|
import { setResponseHeaders } from '../util/headers.js';
|
|
3
|
+
/**
|
|
4
|
+
* Handle the output of a handler without sending the response. Similar to
|
|
5
|
+
* {@link handleHandlerOutput} but this one does not send the response.
|
|
6
|
+
*
|
|
7
|
+
* @category Internal
|
|
8
|
+
* @category Package : @rest-vir/run-service
|
|
9
|
+
* @package [`@rest-vir/run-service`](https://www.npmjs.com/package/@rest-vir/run-service)
|
|
10
|
+
*/
|
|
11
|
+
export function handleHandlerOutputWithoutSending(result, response) {
|
|
12
|
+
if (result?.headers) {
|
|
13
|
+
setResponseHeaders(response, result.headers);
|
|
14
|
+
}
|
|
15
|
+
if (result?.statusCode) {
|
|
16
|
+
response.statusCode = result.statusCode;
|
|
17
|
+
return {
|
|
18
|
+
body: result.body,
|
|
19
|
+
statusCode: result.statusCode,
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
return undefined;
|
|
23
|
+
}
|
|
3
24
|
/**
|
|
4
25
|
* Handle the output of a handler. Setting headers, sending the response, etc.
|
|
5
26
|
*
|
|
@@ -7,7 +28,7 @@ import { setResponseHeaders } from '../util/headers.js';
|
|
|
7
28
|
* @category Package : @rest-vir/run-service
|
|
8
29
|
* @package [`@rest-vir/run-service`](https://www.npmjs.com/package/@rest-vir/run-service)
|
|
9
30
|
*/
|
|
10
|
-
export function
|
|
31
|
+
export function handleHandlerOutput(result, response) {
|
|
11
32
|
if (result?.headers) {
|
|
12
33
|
setResponseHeaders(response, result.headers);
|
|
13
34
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { type
|
|
1
|
+
import { type SelectFrom } from '@augment-vir/common';
|
|
2
|
+
import { type GenericServiceImplementation, type ImplementedEndpoint, type ImplementedWebSocket, type PostHook, type RunningServerInfo, type ServerRequest, type ServerResponse } from '@rest-vir/implement-service';
|
|
2
3
|
import { type WebSocket as WsWebSocket } from 'ws';
|
|
3
4
|
import { type HandleRouteOptions } from './endpoint-handler.js';
|
|
4
5
|
/**
|
|
@@ -8,8 +9,20 @@ import { type HandleRouteOptions } from './endpoint-handler.js';
|
|
|
8
9
|
* @category Package : @rest-vir/run-service
|
|
9
10
|
* @package [`@rest-vir/run-service`](https://www.npmjs.com/package/@rest-vir/run-service)
|
|
10
11
|
*/
|
|
11
|
-
export declare function handleRoute(
|
|
12
|
-
/** Endpoint requests won't have a `WebSocket`. */
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
12
|
+
export declare function handleRoute({ webSocket, request, response, route, attachId, server, options, postHook, service, }: {
|
|
13
|
+
/** Endpoint requests won't have a `WebSocket`. */ webSocket: WsWebSocket | undefined;
|
|
14
|
+
request: ServerRequest /** `WebSocket` requests won't have a response. */;
|
|
15
|
+
response: ServerResponse | undefined;
|
|
16
|
+
route: Readonly<ImplementedEndpoint | ImplementedWebSocket>;
|
|
17
|
+
attachId: string;
|
|
18
|
+
server: Readonly<RunningServerInfo>;
|
|
19
|
+
options: Readonly<Pick<HandleRouteOptions, 'throwErrorsForExternalHandling'>>;
|
|
20
|
+
postHook: PostHook | undefined;
|
|
21
|
+
service: Readonly<SelectFrom<GenericServiceImplementation, {
|
|
22
|
+
webSockets: true;
|
|
23
|
+
endpoints: true;
|
|
24
|
+
serviceName: true;
|
|
25
|
+
serviceOrigin: true;
|
|
26
|
+
requiredClientOrigin: true;
|
|
27
|
+
}>>;
|
|
28
|
+
}): Promise<void>;
|
|
@@ -2,9 +2,10 @@ import { assert, check } from '@augment-vir/assert';
|
|
|
2
2
|
import { ensureError, HttpStatus } from '@augment-vir/common';
|
|
3
3
|
import { RestVirHandlerError, } from '@rest-vir/implement-service';
|
|
4
4
|
import cluster from 'node:cluster';
|
|
5
|
-
import {
|
|
5
|
+
import { handleHandlerOutput, handleHandlerOutputWithoutSending, } from './endpoint-handler.js';
|
|
6
6
|
import { handleEndpointRequest } from './handle-endpoint.js';
|
|
7
7
|
import { handleWebSocketRequest } from './handle-web-socket.js';
|
|
8
|
+
import { runPostHook } from './run-post-hook.js';
|
|
8
9
|
/**
|
|
9
10
|
* Handles a WebSocket or Endpoint request.
|
|
10
11
|
*
|
|
@@ -12,11 +13,7 @@ import { handleWebSocketRequest } from './handle-web-socket.js';
|
|
|
12
13
|
* @category Package : @rest-vir/run-service
|
|
13
14
|
* @package [`@rest-vir/run-service`](https://www.npmjs.com/package/@rest-vir/run-service)
|
|
14
15
|
*/
|
|
15
|
-
export async function handleRoute(
|
|
16
|
-
/** Endpoint requests won't have a `WebSocket`. */
|
|
17
|
-
webSocket, request,
|
|
18
|
-
/** `WebSocket` requests won't have a response. */
|
|
19
|
-
response, route, attachId, server, options = {}) {
|
|
16
|
+
export async function handleRoute({ webSocket, request, response, route, attachId, server, options = {}, postHook, service, }) {
|
|
20
17
|
try {
|
|
21
18
|
const workerPid = cluster.isPrimary ? '' : process.pid;
|
|
22
19
|
const webSocketMarker = route.isWebSocket ? '(ws)' : '';
|
|
@@ -36,9 +33,28 @@ response, route, attachId, server, options = {}) {
|
|
|
36
33
|
attachId,
|
|
37
34
|
server,
|
|
38
35
|
});
|
|
39
|
-
const
|
|
40
|
-
|
|
41
|
-
|
|
36
|
+
const endpointResult = handleHandlerOutputWithoutSending(result, response);
|
|
37
|
+
const postHookResult = (postHook &&
|
|
38
|
+
endpointResult?.statusCode &&
|
|
39
|
+
(await runPostHook({
|
|
40
|
+
attachId,
|
|
41
|
+
originalBody: endpointResult.body,
|
|
42
|
+
originalStatus: endpointResult.statusCode,
|
|
43
|
+
postHook,
|
|
44
|
+
request,
|
|
45
|
+
response,
|
|
46
|
+
server,
|
|
47
|
+
service,
|
|
48
|
+
}))) ||
|
|
49
|
+
endpointResult;
|
|
50
|
+
if (postHookResult) {
|
|
51
|
+
if (postHookResult.body == undefined) {
|
|
52
|
+
postHookResult.headers = {
|
|
53
|
+
...postHookResult.headers,
|
|
54
|
+
'content-type': undefined,
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
return handleHandlerOutput(postHookResult, response);
|
|
42
58
|
}
|
|
43
59
|
}
|
|
44
60
|
else if (route.isWebSocket) {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { type SelectFrom } from '@augment-vir/common';
|
|
2
2
|
import { type GenericServiceImplementation, type RunningServerInfo, type ServerRequest, type ServerResponse } from '@rest-vir/implement-service';
|
|
3
|
-
import { type
|
|
3
|
+
import { type HandledOutput } from './endpoint-handler.js';
|
|
4
4
|
/**
|
|
5
5
|
* Handles a request before it gets to the actual route handlers.
|
|
6
6
|
*
|
|
@@ -8,12 +8,18 @@ import { type FastifyReply } from 'fastify';
|
|
|
8
8
|
* @category Package : @rest-vir/run-service
|
|
9
9
|
* @package [`@rest-vir/run-service`](https://www.npmjs.com/package/@rest-vir/run-service)
|
|
10
10
|
*/
|
|
11
|
-
export declare function preHandler(request
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
11
|
+
export declare function preHandler({ request, response, service, server, attachId, }: {
|
|
12
|
+
request: ServerRequest;
|
|
13
|
+
response: ServerResponse;
|
|
14
|
+
service: Readonly<SelectFrom<GenericServiceImplementation, {
|
|
15
|
+
webSockets: true;
|
|
16
|
+
endpoints: true;
|
|
17
|
+
serviceName: true;
|
|
18
|
+
createContext: true;
|
|
19
|
+
serviceOrigin: true;
|
|
20
|
+
requiredClientOrigin: true;
|
|
21
|
+
logger: true;
|
|
22
|
+
}>>;
|
|
23
|
+
server: Readonly<RunningServerInfo>;
|
|
24
|
+
attachId: string;
|
|
25
|
+
}): Promise<Readonly<HandledOutput>>;
|
|
@@ -3,7 +3,7 @@ import { ensureErrorAndPrependMessage, extractErrorMessage, HttpStatus, stringif
|
|
|
3
3
|
import { isFormDataShape, matchUrlToService, restVirServiceNameHeader, } from '@rest-vir/define-service';
|
|
4
4
|
import { HttpMethod, RestVirHandlerError, } from '@rest-vir/implement-service';
|
|
5
5
|
import { assertValidShape, isValidShape } from 'object-shape-tester';
|
|
6
|
-
import {
|
|
6
|
+
import { handleHandlerOutputWithoutSending } from './endpoint-handler.js';
|
|
7
7
|
import { handleCors } from './handle-cors.js';
|
|
8
8
|
import { handleRequestMethod } from './handle-request-method.js';
|
|
9
9
|
import { handleSearchParams } from './handle-search-params.js';
|
|
@@ -14,7 +14,7 @@ import { handleSearchParams } from './handle-search-params.js';
|
|
|
14
14
|
* @category Package : @rest-vir/run-service
|
|
15
15
|
* @package [`@rest-vir/run-service`](https://www.npmjs.com/package/@rest-vir/run-service)
|
|
16
16
|
*/
|
|
17
|
-
export async function preHandler(request, response, service, server, attachId) {
|
|
17
|
+
export async function preHandler({ request, response, service, server, attachId, }) {
|
|
18
18
|
response.header(restVirServiceNameHeader, service.serviceName);
|
|
19
19
|
const pathMatch = matchUrlToService(service, request.originalUrl);
|
|
20
20
|
if (!pathMatch) {
|
|
@@ -41,15 +41,16 @@ export async function preHandler(request, response, service, server, attachId) {
|
|
|
41
41
|
: undefined;
|
|
42
42
|
if (protocolShapeError) {
|
|
43
43
|
service.logger.error(new RestVirHandlerError(route, extractErrorMessage(ensureErrorAndPrependMessage(protocolShapeError, `WebSocket protocols rejected (${stringify(protocols)}):`))));
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
44
|
+
return {
|
|
45
|
+
statusCode: HttpStatus.BadRequest,
|
|
46
|
+
body: 'Invalid protocol.',
|
|
47
|
+
};
|
|
47
48
|
}
|
|
48
|
-
const subHandlerResponse =
|
|
49
|
+
const subHandlerResponse = handleHandlerOutputWithoutSending(await handleCors({
|
|
49
50
|
request,
|
|
50
51
|
route,
|
|
51
52
|
}), response) ||
|
|
52
|
-
|
|
53
|
+
handleHandlerOutputWithoutSending(handleRequestMethod({
|
|
53
54
|
request,
|
|
54
55
|
route,
|
|
55
56
|
}), response);
|
|
@@ -59,15 +60,17 @@ export async function preHandler(request, response, service, server, attachId) {
|
|
|
59
60
|
const requestData = wrapInTry(() => extractRequestData(request.body, request.headers, route));
|
|
60
61
|
if (requestData instanceof Error) {
|
|
61
62
|
service.logger.error(new RestVirHandlerError(route, `Rejected request body from '${request.originalUrl}': ${stringify(requestData)}`));
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
63
|
+
return {
|
|
64
|
+
statusCode: HttpStatus.BadRequest,
|
|
65
|
+
body: 'Invalid body.',
|
|
66
|
+
};
|
|
65
67
|
}
|
|
66
68
|
const searchParams = handleSearchParams({ request, route });
|
|
67
69
|
if (!('data' in searchParams)) {
|
|
68
|
-
return
|
|
70
|
+
return handleHandlerOutputWithoutSending(searchParams, response);
|
|
69
71
|
}
|
|
70
72
|
const contextParams = {
|
|
73
|
+
pathParams: request.params,
|
|
71
74
|
method: assertWrap.isEnumValue(request.method.toUpperCase(), HttpMethod),
|
|
72
75
|
request,
|
|
73
76
|
requestData,
|
|
@@ -81,23 +84,23 @@ export async function preHandler(request, response, service, server, attachId) {
|
|
|
81
84
|
};
|
|
82
85
|
try {
|
|
83
86
|
const contextOutput = await service.createContext?.(contextParams);
|
|
84
|
-
if (contextOutput?.reject) {
|
|
85
|
-
service.logger.error(new RestVirHandlerError(route, `Context creation rejected: '${request.originalUrl}'`));
|
|
86
|
-
return handleHandlerResult({
|
|
87
|
-
body: contextOutput.reject.responseErrorMessage,
|
|
88
|
-
statusCode: contextOutput.reject.statusCode,
|
|
89
|
-
headers: contextOutput.reject.headers,
|
|
90
|
-
}, response);
|
|
91
|
-
}
|
|
92
87
|
if (!request.restVirContext) {
|
|
93
88
|
request.restVirContext = {};
|
|
94
89
|
}
|
|
95
90
|
request.restVirContext[attachId] = {
|
|
96
|
-
context: contextOutput?.context,
|
|
91
|
+
context: contextOutput?.reject ? undefined : contextOutput?.context,
|
|
97
92
|
requestData,
|
|
98
93
|
protocols,
|
|
99
94
|
searchParams: searchParams.data,
|
|
100
95
|
};
|
|
96
|
+
if (contextOutput?.reject) {
|
|
97
|
+
service.logger.error(new RestVirHandlerError(route, `Context creation rejected: '${request.originalUrl}'`));
|
|
98
|
+
return handleHandlerOutputWithoutSending({
|
|
99
|
+
body: contextOutput.reject.responseErrorMessage,
|
|
100
|
+
statusCode: contextOutput.reject.statusCode,
|
|
101
|
+
headers: contextOutput.reject.headers,
|
|
102
|
+
}, response);
|
|
103
|
+
}
|
|
101
104
|
return undefined;
|
|
102
105
|
}
|
|
103
106
|
catch (error) {
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { type HttpStatus, type SelectFrom } from '@augment-vir/common';
|
|
2
|
+
import { type GenericServiceImplementation, type PostHook, type RunningServerInfo } from '@rest-vir/implement-service';
|
|
3
|
+
import { type EndpointHandlerParams, type HandledOutput } from './endpoint-handler.js';
|
|
4
|
+
export declare function runPostHook(this: void, { request, response, attachId, server, postHook, service, originalBody, originalStatus, }: Readonly<Omit<EndpointHandlerParams, 'route'> & {
|
|
5
|
+
attachId: string;
|
|
6
|
+
server: Readonly<RunningServerInfo>;
|
|
7
|
+
postHook: PostHook;
|
|
8
|
+
originalBody: unknown;
|
|
9
|
+
originalStatus: HttpStatus;
|
|
10
|
+
service: Readonly<SelectFrom<GenericServiceImplementation, {
|
|
11
|
+
webSockets: true;
|
|
12
|
+
endpoints: true;
|
|
13
|
+
serviceName: true;
|
|
14
|
+
serviceOrigin: true;
|
|
15
|
+
requiredClientOrigin: true;
|
|
16
|
+
}>>;
|
|
17
|
+
}>): Promise<HandledOutput>;
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { assert, assertWrap } from '@augment-vir/assert';
|
|
2
|
+
import { matchUrlToService } from '@rest-vir/define-service';
|
|
3
|
+
import { HttpMethod, } from '@rest-vir/implement-service';
|
|
4
|
+
export async function runPostHook({ request, response, attachId, server, postHook, service, originalBody, originalStatus, }) {
|
|
5
|
+
const restVirContext = request.restVirContext?.[attachId];
|
|
6
|
+
assert.isDefined(restVirContext, 'restVirContext is not defined');
|
|
7
|
+
const context = restVirContext.context;
|
|
8
|
+
const requestData = restVirContext.requestData;
|
|
9
|
+
const searchParams = restVirContext.searchParams;
|
|
10
|
+
const pathMatch = matchUrlToService(service, request.originalUrl);
|
|
11
|
+
/* node:coverage ignore next 10 */
|
|
12
|
+
if (!pathMatch) {
|
|
13
|
+
return undefined;
|
|
14
|
+
}
|
|
15
|
+
const endpointDefinition = pathMatch.endpointPath
|
|
16
|
+
? service.endpoints[pathMatch.endpointPath]
|
|
17
|
+
: undefined;
|
|
18
|
+
const webSocketDefinition = request.ws && pathMatch.webSocketPath
|
|
19
|
+
? service.webSockets[pathMatch.webSocketPath]
|
|
20
|
+
: undefined;
|
|
21
|
+
const postHookParams = {
|
|
22
|
+
pathParams: request.params,
|
|
23
|
+
method: assertWrap.isEnumValue(request.method.toUpperCase(), HttpMethod),
|
|
24
|
+
request,
|
|
25
|
+
requestData,
|
|
26
|
+
requestHeaders: request.headers,
|
|
27
|
+
response,
|
|
28
|
+
service,
|
|
29
|
+
endpointDefinition,
|
|
30
|
+
webSocketDefinition,
|
|
31
|
+
context,
|
|
32
|
+
searchParams,
|
|
33
|
+
server,
|
|
34
|
+
originalResponseData: originalBody,
|
|
35
|
+
originalStatus,
|
|
36
|
+
};
|
|
37
|
+
const result = await postHook(postHookParams);
|
|
38
|
+
if (result) {
|
|
39
|
+
return {
|
|
40
|
+
body: 'responseErrorMessage' in result
|
|
41
|
+
? result.responseErrorMessage
|
|
42
|
+
: 'responseData' in result
|
|
43
|
+
? result.responseData
|
|
44
|
+
: originalBody,
|
|
45
|
+
statusCode: 'statusCode' in result ? result.statusCode : originalStatus,
|
|
46
|
+
headers: {
|
|
47
|
+
...result.headers,
|
|
48
|
+
...(result.dataType
|
|
49
|
+
? {
|
|
50
|
+
'content-type': result.dataType,
|
|
51
|
+
}
|
|
52
|
+
: {}),
|
|
53
|
+
},
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
else {
|
|
57
|
+
return undefined;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
@@ -42,6 +42,7 @@ export declare function attachService(server: Readonly<FastifyInstance>, service
|
|
|
42
42
|
serviceOrigin: true;
|
|
43
43
|
requiredClientOrigin: true;
|
|
44
44
|
logger: true;
|
|
45
|
+
postHook: true;
|
|
45
46
|
}>>, options?: Readonly<HandleRouteOptions>): Promise<void>;
|
|
46
47
|
/**
|
|
47
48
|
* Merge the running Fastify server's port with the given service's origin port (if it has a port).
|
|
@@ -1,10 +1,12 @@
|
|
|
1
|
-
import { check } from '@augment-vir/assert';
|
|
1
|
+
import { assert, check } from '@augment-vir/assert';
|
|
2
2
|
import { ensureError, extractErrorMessage, getEnumValues, getObjectTypedKeys, HttpMethod, HttpStatus, randomString, } from '@augment-vir/common';
|
|
3
3
|
import fastifyWs from '@fastify/websocket';
|
|
4
4
|
import { RestVirHandlerError, } from '@rest-vir/implement-service';
|
|
5
5
|
import { buildUrl, parseUrl } from 'url-vir';
|
|
6
|
+
import { handleHandlerOutput } from '../handle-request/endpoint-handler.js';
|
|
6
7
|
import { handleRoute } from '../handle-request/handle-route.js';
|
|
7
8
|
import { preHandler } from '../handle-request/pre-handler.js';
|
|
9
|
+
import { runPostHook } from '../handle-request/run-post-hook.js';
|
|
8
10
|
const endpointFastifyMethods = getEnumValues(HttpMethod).filter((value) => {
|
|
9
11
|
return (
|
|
10
12
|
/** Fastify doesn't support the CONNECT method. */
|
|
@@ -51,18 +53,45 @@ export async function attachService(server, service, options = {}) {
|
|
|
51
53
|
},
|
|
52
54
|
});
|
|
53
55
|
}
|
|
56
|
+
const postHook = service.postHook;
|
|
54
57
|
server.addHook('preValidation', async (request, response) => {
|
|
55
58
|
try {
|
|
56
|
-
|
|
59
|
+
const preHandlerResult = await preHandler({
|
|
60
|
+
request,
|
|
61
|
+
response,
|
|
62
|
+
service,
|
|
63
|
+
server: extractRunningServerInfo(service, server),
|
|
64
|
+
attachId,
|
|
65
|
+
});
|
|
66
|
+
if (preHandlerResult?.statusCode && postHook) {
|
|
67
|
+
const postHookResult = await runPostHook({
|
|
68
|
+
attachId,
|
|
69
|
+
originalBody: preHandlerResult.body,
|
|
70
|
+
originalStatus: preHandlerResult.statusCode,
|
|
71
|
+
postHook,
|
|
72
|
+
request,
|
|
73
|
+
response,
|
|
74
|
+
server: extractRunningServerInfo(service, server),
|
|
75
|
+
service,
|
|
76
|
+
});
|
|
77
|
+
if (postHookResult) {
|
|
78
|
+
return handleHandlerOutput(postHookResult, response);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
return handleHandlerOutput(preHandlerResult, response);
|
|
57
82
|
}
|
|
58
83
|
catch (error) {
|
|
59
84
|
service.logger.error(ensureError(error));
|
|
60
85
|
if (options.throwErrorsForExternalHandling) {
|
|
61
86
|
throw error;
|
|
87
|
+
/* node:coverage ignore next 5 */
|
|
88
|
+
}
|
|
89
|
+
else if (response.sent) {
|
|
90
|
+
assert.never("Error encountered but response was already sent so there's nothing we can do about it.");
|
|
62
91
|
}
|
|
63
|
-
else
|
|
92
|
+
else {
|
|
64
93
|
response.statusCode = HttpStatus.InternalServerError;
|
|
65
|
-
response.send();
|
|
94
|
+
return response.send();
|
|
66
95
|
}
|
|
67
96
|
}
|
|
68
97
|
});
|
|
@@ -78,17 +107,47 @@ export async function attachService(server, service, options = {}) {
|
|
|
78
107
|
method: endpointFastifyMethods,
|
|
79
108
|
url: path,
|
|
80
109
|
handler(request, response) {
|
|
81
|
-
return handleRoute(
|
|
110
|
+
return handleRoute({
|
|
111
|
+
webSocket: undefined,
|
|
112
|
+
request,
|
|
113
|
+
response,
|
|
114
|
+
route: endpoint,
|
|
115
|
+
attachId,
|
|
116
|
+
server: extractRunningServerInfo(service, server),
|
|
117
|
+
options,
|
|
118
|
+
postHook,
|
|
119
|
+
service,
|
|
120
|
+
});
|
|
82
121
|
},
|
|
83
122
|
});
|
|
84
123
|
server.route({
|
|
85
124
|
method: HttpMethod.Get,
|
|
86
125
|
url: path,
|
|
87
126
|
handler(request, response) {
|
|
88
|
-
return handleRoute(
|
|
127
|
+
return handleRoute({
|
|
128
|
+
webSocket: undefined,
|
|
129
|
+
request,
|
|
130
|
+
response,
|
|
131
|
+
route: endpoint,
|
|
132
|
+
attachId,
|
|
133
|
+
server: extractRunningServerInfo(service, server),
|
|
134
|
+
options,
|
|
135
|
+
postHook,
|
|
136
|
+
service,
|
|
137
|
+
});
|
|
89
138
|
},
|
|
90
139
|
wsHandler(webSocket, request) {
|
|
91
|
-
return handleRoute(
|
|
140
|
+
return handleRoute({
|
|
141
|
+
webSocket,
|
|
142
|
+
request,
|
|
143
|
+
response: undefined,
|
|
144
|
+
route: webSocketDefinition,
|
|
145
|
+
attachId,
|
|
146
|
+
server: extractRunningServerInfo(service, server),
|
|
147
|
+
options,
|
|
148
|
+
postHook,
|
|
149
|
+
service,
|
|
150
|
+
});
|
|
92
151
|
},
|
|
93
152
|
});
|
|
94
153
|
}
|
|
@@ -100,7 +159,17 @@ export async function attachService(server, service, options = {}) {
|
|
|
100
159
|
],
|
|
101
160
|
url: path,
|
|
102
161
|
handler(request, response) {
|
|
103
|
-
return handleRoute(
|
|
162
|
+
return handleRoute({
|
|
163
|
+
webSocket: undefined,
|
|
164
|
+
request,
|
|
165
|
+
response,
|
|
166
|
+
route: endpoint,
|
|
167
|
+
attachId,
|
|
168
|
+
server: extractRunningServerInfo(service, server),
|
|
169
|
+
options,
|
|
170
|
+
postHook,
|
|
171
|
+
service,
|
|
172
|
+
});
|
|
104
173
|
},
|
|
105
174
|
});
|
|
106
175
|
}
|
|
@@ -112,7 +181,17 @@ export async function attachService(server, service, options = {}) {
|
|
|
112
181
|
return response.status(HttpStatus.NotFound).send();
|
|
113
182
|
},
|
|
114
183
|
wsHandler(webSocket, request) {
|
|
115
|
-
return handleRoute(
|
|
184
|
+
return handleRoute({
|
|
185
|
+
webSocket,
|
|
186
|
+
request,
|
|
187
|
+
response: undefined,
|
|
188
|
+
route: webSocketDefinition,
|
|
189
|
+
attachId,
|
|
190
|
+
server: extractRunningServerInfo(service, server),
|
|
191
|
+
options,
|
|
192
|
+
postHook,
|
|
193
|
+
service,
|
|
194
|
+
});
|
|
116
195
|
},
|
|
117
196
|
});
|
|
118
197
|
}
|
|
@@ -69,4 +69,5 @@ export declare function startService(service: Readonly<SelectFrom<GenericService
|
|
|
69
69
|
serviceOrigin: true;
|
|
70
70
|
requiredClientOrigin: true;
|
|
71
71
|
logger: true;
|
|
72
|
+
postHook: true;
|
|
72
73
|
}>>, userOptions?: Readonly<StartServiceUserOptions>, fastifyPlugins?: Readonly<FastifyPlugins>): Promise<StartServiceOutput>;
|
|
@@ -284,6 +284,7 @@ export declare function testService<const Service extends Readonly<SelectFrom<Ge
|
|
|
284
284
|
serviceOrigin: true;
|
|
285
285
|
requiredClientOrigin: true;
|
|
286
286
|
logger: true;
|
|
287
|
+
postHook: true;
|
|
287
288
|
}>>>(service: Readonly<Service>, testServiceOptions?: Readonly<Omit<PartialWithUndefined<StartServiceUserOptions>, 'workerCount' | 'preventWorkerRespawn' | ''>>): Promise<{
|
|
288
289
|
/** Kill the server being tested. This should always be called after your tests are finished. */
|
|
289
290
|
kill(this: void): Promise<void>;
|
|
@@ -313,6 +314,7 @@ export declare function testExistingServer<const Service extends Readonly<Select
|
|
|
313
314
|
serviceOrigin: true;
|
|
314
315
|
requiredClientOrigin: true;
|
|
315
316
|
logger: true;
|
|
317
|
+
postHook: true;
|
|
316
318
|
}>>>(server: Readonly<FastifyInstance>, service: Readonly<Service>, options?: Readonly<HandleRouteOptions & Omit<PartialWithUndefined<StartServiceOptions>, 'workerCount' | 'preventWorkerRespawn'>>): Promise<{
|
|
317
319
|
/** Send a request to the service. */
|
|
318
320
|
fetchEndpoint: FetchTestService<Service>;
|
|
@@ -353,6 +355,7 @@ export declare function describeService<const Service extends Readonly<SelectFro
|
|
|
353
355
|
serviceOrigin: true;
|
|
354
356
|
requiredClientOrigin: true;
|
|
355
357
|
logger: true;
|
|
358
|
+
postHook: true;
|
|
356
359
|
}>>>({ service, options, }: {
|
|
357
360
|
/** The service to startup and send requests to. */
|
|
358
361
|
service: Readonly<Service>;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rest-vir/run-service",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.19.1",
|
|
4
4
|
"description": "Run a service defined by @rest-vir/define-service and implemented by @rest-vir/implement-service.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"rest",
|
|
@@ -39,25 +39,25 @@
|
|
|
39
39
|
"test:update": "npm test update"
|
|
40
40
|
},
|
|
41
41
|
"dependencies": {
|
|
42
|
-
"@augment-vir/assert": "^31.
|
|
43
|
-
"@augment-vir/common": "^31.
|
|
44
|
-
"@augment-vir/node": "^31.
|
|
42
|
+
"@augment-vir/assert": "^31.19.1",
|
|
43
|
+
"@augment-vir/common": "^31.19.1",
|
|
44
|
+
"@augment-vir/node": "^31.19.1",
|
|
45
45
|
"@fastify/websocket": "^11.0.2",
|
|
46
|
-
"@rest-vir/define-service": "^0.
|
|
47
|
-
"@rest-vir/implement-service": "^0.
|
|
46
|
+
"@rest-vir/define-service": "^0.19.1",
|
|
47
|
+
"@rest-vir/implement-service": "^0.19.1",
|
|
48
48
|
"cluster-vir": "^0.1.0",
|
|
49
49
|
"date-vir": "^7.3.1",
|
|
50
|
-
"fastify": "^5.3.
|
|
50
|
+
"fastify": "^5.3.3",
|
|
51
51
|
"light-my-request": "^6.6.0",
|
|
52
52
|
"portfinder": "^1.0.37",
|
|
53
|
-
"type-fest": "^4.
|
|
53
|
+
"type-fest": "^4.41.0",
|
|
54
54
|
"url-vir": "^2.1.3"
|
|
55
55
|
},
|
|
56
56
|
"devDependencies": {
|
|
57
|
-
"@augment-vir/test": "^31.
|
|
57
|
+
"@augment-vir/test": "^31.19.1",
|
|
58
58
|
"@fastify/multipart": "^9.0.3",
|
|
59
59
|
"@types/connect": "^3.4.38",
|
|
60
|
-
"@types/node": "^22.15.
|
|
60
|
+
"@types/node": "^22.15.18",
|
|
61
61
|
"@types/ws": "^8.18.1",
|
|
62
62
|
"c8": "^10.1.3",
|
|
63
63
|
"istanbul-smart-text-reporter": "^1.1.5",
|