shelving 1.144.0 → 1.145.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/api/Endpoint.d.ts +3 -15
- package/api/Endpoint.js +9 -30
- package/api/util.d.ts +0 -14
- package/api/util.js +0 -30
- package/package.json +1 -1
- 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,14 @@ 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
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
// Throw a `ValueError` if the result is not valid, which indicates an internal error in the callback implementation.
|
|
68
|
-
const result = getValid(returned, this, ValueError, this.handle);
|
|
69
|
-
// Return a new `Response` with a 200 status and the validated result data.
|
|
70
|
-
return Response.json(result);
|
|
45
|
+
return getResponse(await callback(payload, request));
|
|
46
|
+
}
|
|
47
|
+
/** Convert to string, e.g. `GET /user/{id}` */
|
|
48
|
+
toString() {
|
|
49
|
+
return `${this.method} ${this.path}`;
|
|
71
50
|
}
|
|
72
51
|
}
|
|
73
52
|
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,8 +1,6 @@
|
|
|
1
1
|
import { NotFoundError, RequestError } from "../error/RequestError.js";
|
|
2
|
-
import { Feedback } from "../feedback/Feedback.js";
|
|
3
2
|
import { isData } from "../util/data.js";
|
|
4
3
|
import { getDictionary } from "../util/dictionary.js";
|
|
5
|
-
import { isError } from "../util/error.js";
|
|
6
4
|
import { getRequestContent } from "../util/http.js";
|
|
7
5
|
import { matchTemplate } from "../util/template.js";
|
|
8
6
|
import { getURL } from "../util/url.js";
|
|
@@ -52,31 +50,3 @@ async function handleEndpoint(endpoint, callback, params, request) {
|
|
|
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/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
|
+
}
|