shelving 1.144.0 → 1.145.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/api/Endpoint.d.ts +3 -15
- package/api/Endpoint.js +11 -30
- package/api/util.d.ts +0 -14
- package/api/util.js +4 -34
- package/package.json +1 -1
- package/util/ansi.d.ts +9 -18
- package/util/ansi.js +10 -20
- package/util/http.d.ts +21 -0
- package/util/http.js +46 -0
package/api/Endpoint.d.ts
CHANGED
|
@@ -11,7 +11,7 @@ export type EndpointMethod = "GET" | "POST" | "PUT" | "PATCH" | "DELETE";
|
|
|
11
11
|
* @param payload A `Validator` for the payload of the resource.
|
|
12
12
|
* @param result A `Validator` for the result of the resource.
|
|
13
13
|
*/
|
|
14
|
-
export declare class Endpoint<P, R>
|
|
14
|
+
export declare class Endpoint<P, R> {
|
|
15
15
|
/** Endpoint method. */
|
|
16
16
|
readonly method: EndpointMethod;
|
|
17
17
|
/** Endpoint path, e.g. `/patient/{id}` */
|
|
@@ -21,20 +21,6 @@ export declare class Endpoint<P, R> implements Validator<R> {
|
|
|
21
21
|
/** Result validator. */
|
|
22
22
|
readonly result: Validator<R>;
|
|
23
23
|
constructor(method: EndpointMethod, path: Path, payload: Validator<P>, result: Validator<R>);
|
|
24
|
-
/**
|
|
25
|
-
* Validate a payload for this resource.
|
|
26
|
-
*
|
|
27
|
-
* @returns The validated payload for this resource.
|
|
28
|
-
* @throws `Feedback` if the payload is invalid. `Feedback` instances can be reported safely back to the end client so they know how to fix their request.
|
|
29
|
-
*/
|
|
30
|
-
prepare(unsafePayload: unknown): P;
|
|
31
|
-
/**
|
|
32
|
-
* Validate a result for this resource.
|
|
33
|
-
*
|
|
34
|
-
* @returns The validated result for this resource.
|
|
35
|
-
* @throws `Feedback` if the value is invalid. `Feedback` instances can be reported safely back to the end client so they know how to fix their request.
|
|
36
|
-
*/
|
|
37
|
-
validate(unsafeResult: unknown): R;
|
|
38
24
|
/**
|
|
39
25
|
* Return an `EndpointHandler` for this endpoint.
|
|
40
26
|
*
|
|
@@ -49,6 +35,8 @@ export declare class Endpoint<P, R> implements Validator<R> {
|
|
|
49
35
|
* @param request The entire HTTP request that is being handled (payload was possibly extracted from this somehow).
|
|
50
36
|
*/
|
|
51
37
|
handle(callback: EndpointCallback<P, R>, unsafePayload: unknown, request: Request): Promise<Response>;
|
|
38
|
+
/** Convert to string, e.g. `GET /user/{id}` */
|
|
39
|
+
toString(): string;
|
|
52
40
|
}
|
|
53
41
|
/** Extract the payload type from a `Endpoint`. */
|
|
54
42
|
export type PayloadType<X extends Endpoint<unknown, unknown>> = X extends Endpoint<infer Y, unknown> ? Y : never;
|
package/api/Endpoint.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { UNDEFINED
|
|
1
|
+
import { getResponse } from "../util/http.js";
|
|
2
|
+
import { UNDEFINED } from "../util/validate.js";
|
|
3
3
|
/**
|
|
4
4
|
* An abstract API resource definition, used to specify types for e.g. serverless functions.
|
|
5
5
|
*
|
|
@@ -23,24 +23,6 @@ export class Endpoint {
|
|
|
23
23
|
this.payload = payload;
|
|
24
24
|
this.result = result;
|
|
25
25
|
}
|
|
26
|
-
/**
|
|
27
|
-
* Validate a payload for this resource.
|
|
28
|
-
*
|
|
29
|
-
* @returns The validated payload for this resource.
|
|
30
|
-
* @throws `Feedback` if the payload is invalid. `Feedback` instances can be reported safely back to the end client so they know how to fix their request.
|
|
31
|
-
*/
|
|
32
|
-
prepare(unsafePayload) {
|
|
33
|
-
return this.payload.validate(unsafePayload);
|
|
34
|
-
}
|
|
35
|
-
/**
|
|
36
|
-
* Validate a result for this resource.
|
|
37
|
-
*
|
|
38
|
-
* @returns The validated result for this resource.
|
|
39
|
-
* @throws `Feedback` if the value is invalid. `Feedback` instances can be reported safely back to the end client so they know how to fix their request.
|
|
40
|
-
*/
|
|
41
|
-
validate(unsafeResult) {
|
|
42
|
-
return this.result.validate(unsafeResult);
|
|
43
|
-
}
|
|
44
26
|
/**
|
|
45
27
|
* Return an `EndpointHandler` for this endpoint.
|
|
46
28
|
*
|
|
@@ -57,17 +39,16 @@ export class Endpoint {
|
|
|
57
39
|
* @param request The entire HTTP request that is being handled (payload was possibly extracted from this somehow).
|
|
58
40
|
*/
|
|
59
41
|
async handle(callback, unsafePayload, request) {
|
|
60
|
-
|
|
42
|
+
// Validate the payload against this endpoint's payload type.
|
|
43
|
+
const payload = this.payload.validate(unsafePayload);
|
|
61
44
|
// Call the callback with the validated payload to get the result.
|
|
62
|
-
const
|
|
63
|
-
//
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
// Return a new `Response` with a 200 status and the validated result data.
|
|
70
|
-
return Response.json(result);
|
|
45
|
+
const result = await callback(payload, request);
|
|
46
|
+
// Convert the result to a `Response` object.
|
|
47
|
+
return getResponse(result);
|
|
48
|
+
}
|
|
49
|
+
/** Convert to string, e.g. `GET /user/{id}` */
|
|
50
|
+
toString() {
|
|
51
|
+
return `${this.method} ${this.path}`;
|
|
71
52
|
}
|
|
72
53
|
}
|
|
73
54
|
export function GET(path, payload, result) {
|
package/api/util.d.ts
CHANGED
|
@@ -38,17 +38,3 @@ export type EndpointHandlers = ReadonlyArray<AnyEndpointHandler>;
|
|
|
38
38
|
* @throws `NotFoundError` if no handler matches the `Request`.
|
|
39
39
|
*/
|
|
40
40
|
export declare function handleEndpoints(request: Request, endpoints: EndpointHandlers): Promise<Response>;
|
|
41
|
-
/**
|
|
42
|
-
* Correctly interpret an error thrown from an endpoint and return the correct `Response`.
|
|
43
|
-
*
|
|
44
|
-
* Returns the correct `Response` based on the type of error thrown:
|
|
45
|
-
* - If `reason` is a `Response` instance, return it directly.
|
|
46
|
-
* - If `reason` is a `Feedback` instance, return a 400 response with the feedback's message as JSON, e.g. `{ message: "Invalid input" }`
|
|
47
|
-
* - If `reason` is an `RequestError` instance, return a response with the error's message and code (but only if `debug` is true so we don't leak error details to the client).
|
|
48
|
-
* - If `reason` is an `Error` instance, return a 500 response with the error's message (but only if `debug` is true so we don't leak error details to the client).
|
|
49
|
-
* - Anything else returns a 500 response.
|
|
50
|
-
*
|
|
51
|
-
* @param reason The error thrown from the endpoint.
|
|
52
|
-
* @param debug If `true` include the error message in the response (for debugging), or `false` to return generic error codes (for security).
|
|
53
|
-
*/
|
|
54
|
-
export declare function handleEndpointError(reason: unknown, debug?: boolean): Response;
|
package/api/util.js
CHANGED
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
import { NotFoundError, RequestError } from "../error/RequestError.js";
|
|
2
|
-
import { Feedback } from "../feedback/Feedback.js";
|
|
3
|
-
import { isData } from "../util/data.js";
|
|
4
2
|
import { getDictionary } from "../util/dictionary.js";
|
|
5
|
-
import { isError } from "../util/error.js";
|
|
6
3
|
import { getRequestContent } from "../util/http.js";
|
|
4
|
+
import { isPlainObject } from "../util/object.js";
|
|
7
5
|
import { matchTemplate } from "../util/template.js";
|
|
8
6
|
import { getURL } from "../util/url.js";
|
|
9
7
|
/**
|
|
@@ -46,37 +44,9 @@ async function handleEndpoint(endpoint, callback, params, request) {
|
|
|
46
44
|
// Extract a data object from the request body and validate it against the endpoint's payload type.
|
|
47
45
|
const content = await getRequestContent(request, handleEndpoints);
|
|
48
46
|
// If content is undefined, it means the request has no body, so params are the only payload.
|
|
49
|
-
// If the content is a
|
|
50
|
-
// If the content is
|
|
51
|
-
const payload = content === undefined ? params :
|
|
47
|
+
// - If the content is a plain object, merge if with the params.
|
|
48
|
+
// - If the content is anything else (e.g. string, number, array), set it as a single `content` property.
|
|
49
|
+
const payload = content === undefined ? params : isPlainObject(content) ? { ...content, ...params } : { content, ...params };
|
|
52
50
|
// Call `endpoint.handle()` with the payload and request.
|
|
53
51
|
return endpoint.handle(callback, payload, request);
|
|
54
52
|
}
|
|
55
|
-
/**
|
|
56
|
-
* Correctly interpret an error thrown from an endpoint and return the correct `Response`.
|
|
57
|
-
*
|
|
58
|
-
* Returns the correct `Response` based on the type of error thrown:
|
|
59
|
-
* - If `reason` is a `Response` instance, return it directly.
|
|
60
|
-
* - If `reason` is a `Feedback` instance, return a 400 response with the feedback's message as JSON, e.g. `{ message: "Invalid input" }`
|
|
61
|
-
* - If `reason` is an `RequestError` instance, return a response with the error's message and code (but only if `debug` is true so we don't leak error details to the client).
|
|
62
|
-
* - If `reason` is an `Error` instance, return a 500 response with the error's message (but only if `debug` is true so we don't leak error details to the client).
|
|
63
|
-
* - Anything else returns a 500 response.
|
|
64
|
-
*
|
|
65
|
-
* @param reason The error thrown from the endpoint.
|
|
66
|
-
* @param debug If `true` include the error message in the response (for debugging), or `false` to return generic error codes (for security).
|
|
67
|
-
*/
|
|
68
|
-
export function handleEndpointError(reason, debug = false) {
|
|
69
|
-
// Throw `Response` to do a custom response that is not logged.
|
|
70
|
-
if (reason instanceof Response)
|
|
71
|
-
return reason;
|
|
72
|
-
// Throw 'Feedback' to return `{ message: "etc" }` to the client, e.g. for input validation.
|
|
73
|
-
if (reason instanceof Feedback)
|
|
74
|
-
return Response.json(reason, { status: 422 }); // HTTP 422 Unprocessable Entity
|
|
75
|
-
// Throw `RequestError` to set a custom status code (e.g. `UnauthorizedError`).
|
|
76
|
-
const status = reason instanceof RequestError ? reason.code : 500;
|
|
77
|
-
// Throw `Error` to return `{ message: "etc" }` to the client (but only if `debug` is true so we don't leak error details to the client).
|
|
78
|
-
if (debug && isError(reason))
|
|
79
|
-
return Response.json(reason, { status });
|
|
80
|
-
// Otherwise return a generic error message.
|
|
81
|
-
return new Response(undefined, { status });
|
|
82
|
-
}
|
package/package.json
CHANGED
package/util/ansi.d.ts
CHANGED
|
@@ -1,21 +1,12 @@
|
|
|
1
|
-
export declare const
|
|
2
|
-
export declare const
|
|
3
|
-
export declare const
|
|
4
|
-
export declare const
|
|
5
|
-
export declare const
|
|
6
|
-
export declare const
|
|
7
|
-
export declare const
|
|
8
|
-
export declare const
|
|
9
|
-
export declare const
|
|
10
|
-
export declare const ANSI_FILL_DEFAULT = "\u001B[49m";
|
|
11
|
-
export declare const ANSI_FILL_BLACK = "\u001B[40m";
|
|
12
|
-
export declare const ANSI_FILL_RED = "\u001B[41m";
|
|
13
|
-
export declare const ANSI_FILL_GREEN = "\u001B[42m";
|
|
14
|
-
export declare const ANSI_FILL_YELLOW = "\u001B[43m";
|
|
15
|
-
export declare const ANSI_FILL_BLUE = "\u001B[44m";
|
|
16
|
-
export declare const ANSI_FILL_MAGENTA = "\u001B[45m";
|
|
17
|
-
export declare const ANSI_FILL_CYAN = "\u001B[46m";
|
|
18
|
-
export declare const ANSI_FILL_WHITE = "\u001B[47m";
|
|
1
|
+
export declare const ANSI_DEFAULT = "\u001B[39m";
|
|
2
|
+
export declare const ANSI_BLACK = "\u001B[30m";
|
|
3
|
+
export declare const ANSI_RED = "\u001B[31m";
|
|
4
|
+
export declare const ANSI_GREEN = "\u001B[32m";
|
|
5
|
+
export declare const ANSI_YELLOW = "\u001B[33m";
|
|
6
|
+
export declare const ANSI_BLUE = "\u001B[34m";
|
|
7
|
+
export declare const ANSI_MAGENTA = "\u001B[35m";
|
|
8
|
+
export declare const ANSI_CYAN = "\u001B[36m";
|
|
9
|
+
export declare const ANSI_WHITE = "\u001B[37m";
|
|
19
10
|
export declare const ANSI_BOLD = "\u001B[1m";
|
|
20
11
|
export declare const ANSI_ITALIC = "\u001B[3m";
|
|
21
12
|
export declare const ANSI_UNDERLINE = "\u001B[4m";
|
package/util/ansi.js
CHANGED
|
@@ -1,23 +1,13 @@
|
|
|
1
|
-
//
|
|
2
|
-
export const
|
|
3
|
-
export const
|
|
4
|
-
export const
|
|
5
|
-
export const
|
|
6
|
-
export const
|
|
7
|
-
export const
|
|
8
|
-
export const
|
|
9
|
-
export const
|
|
10
|
-
export const
|
|
11
|
-
// Background colors.
|
|
12
|
-
export const ANSI_FILL_DEFAULT = "\x1b[49m";
|
|
13
|
-
export const ANSI_FILL_BLACK = "\x1b[40m";
|
|
14
|
-
export const ANSI_FILL_RED = "\x1b[41m";
|
|
15
|
-
export const ANSI_FILL_GREEN = "\x1b[42m";
|
|
16
|
-
export const ANSI_FILL_YELLOW = "\x1b[43m";
|
|
17
|
-
export const ANSI_FILL_BLUE = "\x1b[44m";
|
|
18
|
-
export const ANSI_FILL_MAGENTA = "\x1b[45m";
|
|
19
|
-
export const ANSI_FILL_CYAN = "\x1b[46m";
|
|
20
|
-
export const ANSI_FILL_WHITE = "\x1b[47m";
|
|
1
|
+
// Colors.
|
|
2
|
+
export const ANSI_DEFAULT = "\x1b[39m";
|
|
3
|
+
export const ANSI_BLACK = "\x1b[30m";
|
|
4
|
+
export const ANSI_RED = "\x1b[31m";
|
|
5
|
+
export const ANSI_GREEN = "\x1b[32m";
|
|
6
|
+
export const ANSI_YELLOW = "\x1b[33m";
|
|
7
|
+
export const ANSI_BLUE = "\x1b[34m";
|
|
8
|
+
export const ANSI_MAGENTA = "\x1b[35m";
|
|
9
|
+
export const ANSI_CYAN = "\x1b[36m";
|
|
10
|
+
export const ANSI_WHITE = "\x1b[37m";
|
|
21
11
|
// Styles.
|
|
22
12
|
export const ANSI_BOLD = "\x1b[1m";
|
|
23
13
|
export const ANSI_ITALIC = "\x1b[3m";
|
package/util/http.d.ts
CHANGED
|
@@ -44,3 +44,24 @@ export declare function getRequestJSON(message: Request, caller?: AnyCaller): Pr
|
|
|
44
44
|
* @throws RequestError if the content is not valid JSON.
|
|
45
45
|
*/
|
|
46
46
|
export declare function getResponseJSON(message: Response, caller?: AnyCaller): Promise<unknown>;
|
|
47
|
+
/**
|
|
48
|
+
* Get an HTTP `Response` for an unknown value.
|
|
49
|
+
*
|
|
50
|
+
* @param value The value to convert to a `Response`.
|
|
51
|
+
* @returns A `Response` with a 2xx status, and response body as JSON (if it was set), or no body if `value` is `undefined`
|
|
52
|
+
*/
|
|
53
|
+
export declare function getResponse(value: unknown): Response;
|
|
54
|
+
/**
|
|
55
|
+
* Get an HTTP `Response` for an unknown error value.
|
|
56
|
+
*
|
|
57
|
+
* Returns the correct `Response` based on the type of error thrown:
|
|
58
|
+
* - If `reason` is a `Response` instance, return it directly.
|
|
59
|
+
* - If `reason` is a `Feedback` instance, return a 400 response with the feedback's message as JSON, e.g. `{ message: "Invalid input" }`
|
|
60
|
+
* - If `reason` is an `RequestError` instance, return a response with the error's message and code (but only if `debug` is true so we don't leak error details to the client).
|
|
61
|
+
* - If `reason` is an `Error` instance, return a 500 response with the error's message (but only if `debug` is true so we don't leak error details to the client).
|
|
62
|
+
* - Anything else returns a 500 response.
|
|
63
|
+
*
|
|
64
|
+
* @param reason The error value to convert to a `Response`.
|
|
65
|
+
* @param debug If `true` include the error message in the response (for debugging), or `false` to return generic error codes (for security).
|
|
66
|
+
*/
|
|
67
|
+
export declare function getErrorResponse(reason: unknown, debug?: boolean): Response;
|
package/util/http.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { RequestError } from "../error/RequestError.js";
|
|
2
2
|
import { ResponseError } from "../error/ResponseError.js";
|
|
3
|
+
import { Feedback } from "../feedback/Feedback.js";
|
|
3
4
|
import { getDictionary } from "./dictionary.js";
|
|
5
|
+
import { isError } from "./error.js";
|
|
4
6
|
export async function _getMessageJSON(message, MessageError, caller) {
|
|
5
7
|
const trimmed = (await message.text()).trim();
|
|
6
8
|
if (!trimmed.length)
|
|
@@ -76,3 +78,47 @@ export function getRequestJSON(message, caller = getRequestJSON) {
|
|
|
76
78
|
export function getResponseJSON(message, caller = getResponseJSON) {
|
|
77
79
|
return _getMessageJSON(message, ResponseError, caller);
|
|
78
80
|
}
|
|
81
|
+
/**
|
|
82
|
+
* Get an HTTP `Response` for an unknown value.
|
|
83
|
+
*
|
|
84
|
+
* @param value The value to convert to a `Response`.
|
|
85
|
+
* @returns A `Response` with a 2xx status, and response body as JSON (if it was set), or no body if `value` is `undefined`
|
|
86
|
+
*/
|
|
87
|
+
export function getResponse(value) {
|
|
88
|
+
// If it's already a `Response`, return it directly.
|
|
89
|
+
if (value instanceof Response)
|
|
90
|
+
return value;
|
|
91
|
+
// If result is undefined, return 204 No Content response.
|
|
92
|
+
if (value === undefined)
|
|
93
|
+
return new Response(undefined, { status: 204 });
|
|
94
|
+
// Return a new `Response` with a 2xx status and response body as JSON.
|
|
95
|
+
return Response.json(value, { status: 200 });
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Get an HTTP `Response` for an unknown error value.
|
|
99
|
+
*
|
|
100
|
+
* Returns the correct `Response` based on the type of error thrown:
|
|
101
|
+
* - If `reason` is a `Response` instance, return it directly.
|
|
102
|
+
* - If `reason` is a `Feedback` instance, return a 400 response with the feedback's message as JSON, e.g. `{ message: "Invalid input" }`
|
|
103
|
+
* - If `reason` is an `RequestError` instance, return a response with the error's message and code (but only if `debug` is true so we don't leak error details to the client).
|
|
104
|
+
* - If `reason` is an `Error` instance, return a 500 response with the error's message (but only if `debug` is true so we don't leak error details to the client).
|
|
105
|
+
* - Anything else returns a 500 response.
|
|
106
|
+
*
|
|
107
|
+
* @param reason The error value to convert to a `Response`.
|
|
108
|
+
* @param debug If `true` include the error message in the response (for debugging), or `false` to return generic error codes (for security).
|
|
109
|
+
*/
|
|
110
|
+
export function getErrorResponse(reason, debug = false) {
|
|
111
|
+
// If it's already a `Response`, return it directly.
|
|
112
|
+
if (reason instanceof Response)
|
|
113
|
+
return reason;
|
|
114
|
+
// Throw 'Feedback' to return `{ message: "etc" }` to the client, e.g. for input validation.
|
|
115
|
+
if (reason instanceof Feedback)
|
|
116
|
+
return Response.json(reason, { status: 422 }); // HTTP 422 Unprocessable Entity
|
|
117
|
+
// Throw `RequestError` to set a custom status code (e.g. `UnauthorizedError`).
|
|
118
|
+
const status = reason instanceof RequestError ? reason.code : 500;
|
|
119
|
+
// Throw `Error` to return `{ message: "etc" }` to the client (but only if `debug` is true so we don't leak error details to the client).
|
|
120
|
+
if (debug && isError(reason))
|
|
121
|
+
return Response.json(reason, { status });
|
|
122
|
+
// Otherwise return a generic error message with no details.
|
|
123
|
+
return new Response(undefined, { status });
|
|
124
|
+
}
|