shelving 1.168.0 → 1.168.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/endpoint/Endpoint.d.ts +5 -12
- package/endpoint/Endpoint.js +2 -69
- package/package.json +1 -1
- package/util/http.d.ts +22 -0
- package/util/http.js +59 -0
package/endpoint/Endpoint.d.ts
CHANGED
|
@@ -1,15 +1,8 @@
|
|
|
1
1
|
import { type Schema } from "../schema/Schema.js";
|
|
2
2
|
import type { AnyCaller } from "../util/function.js";
|
|
3
|
+
import { type RequestMethod, type RequestOptions } from "../util/http.js";
|
|
3
4
|
import type { URLString } from "../util/url.js";
|
|
4
5
|
import type { EndpointCallback, EndpointHandler } from "./util.js";
|
|
5
|
-
/** HTTP request methods. */
|
|
6
|
-
export type EndpointMethod = EndpointBodyMethod | EndpointHeadMethod;
|
|
7
|
-
/** HTTP request methods that have no body. */
|
|
8
|
-
export type EndpointHeadMethod = "HEAD" | "GET";
|
|
9
|
-
/** HTTP request methods that have a body. */
|
|
10
|
-
export type EndpointBodyMethod = "POST" | "PUT" | "PATCH" | "DELETE";
|
|
11
|
-
/** Configurable options for endpoint. */
|
|
12
|
-
export type EndpointOptions = Omit<RequestInit, "method" | "body">;
|
|
13
6
|
/**
|
|
14
7
|
* An abstract API resource definition, used to specify types for e.g. serverless functions.
|
|
15
8
|
*
|
|
@@ -20,14 +13,14 @@ export type EndpointOptions = Omit<RequestInit, "method" | "body">;
|
|
|
20
13
|
*/
|
|
21
14
|
export declare class Endpoint<P, R> {
|
|
22
15
|
/** Endpoint method. */
|
|
23
|
-
readonly method:
|
|
16
|
+
readonly method: RequestMethod;
|
|
24
17
|
/** Endpoint URL, possibly including placeholders e.g. `https://api.mysite.com/users/{id}` */
|
|
25
18
|
readonly url: URLString;
|
|
26
19
|
/** Payload schema. */
|
|
27
20
|
readonly payload: Schema<P>;
|
|
28
21
|
/** Result schema. */
|
|
29
22
|
readonly result: Schema<R>;
|
|
30
|
-
constructor(method:
|
|
23
|
+
constructor(method: RequestMethod, url: URLString, payload: Schema<P>, result: Schema<R>);
|
|
31
24
|
/**
|
|
32
25
|
* Return an `EndpointHandler` for this endpoint.
|
|
33
26
|
*
|
|
@@ -54,7 +47,7 @@ export declare class Endpoint<P, R> {
|
|
|
54
47
|
*
|
|
55
48
|
* @throws Feedback if the payload is invalid.
|
|
56
49
|
*/
|
|
57
|
-
request(payload: P, options?:
|
|
50
|
+
request(payload: P, options?: RequestOptions, caller?: AnyCaller): Request;
|
|
58
51
|
/**
|
|
59
52
|
* Validate an HTTP `Response` against this endpoint.
|
|
60
53
|
* @throws ResponseError if the response status is not ok (200-299)
|
|
@@ -70,7 +63,7 @@ export declare class Endpoint<P, R> {
|
|
|
70
63
|
* @throws ResponseError if the response status is not ok (200-299)
|
|
71
64
|
* @throws ResponseError if the response content is invalid.
|
|
72
65
|
*/
|
|
73
|
-
fetch(payload: P, options?:
|
|
66
|
+
fetch(payload: P, options?: RequestOptions, caller?: AnyCaller): Promise<R>;
|
|
74
67
|
/** Convert to string, e.g. `GET https://a.com/user/{id}` */
|
|
75
68
|
toString(): string;
|
|
76
69
|
}
|
package/endpoint/Endpoint.js
CHANGED
|
@@ -2,9 +2,8 @@ import { ResponseError } from "../error/ResponseError.js";
|
|
|
2
2
|
import { UNDEFINED } from "../schema/Schema.js";
|
|
3
3
|
import { assertDictionary } from "../util/dictionary.js";
|
|
4
4
|
import { getMessage } from "../util/error.js";
|
|
5
|
-
import { getResponse, getResponseContent } from "../util/http.js";
|
|
5
|
+
import { getRequest, getResponse, getResponseContent } from "../util/http.js";
|
|
6
6
|
import { getPlaceholders, renderTemplate } from "../util/template.js";
|
|
7
|
-
import { omitURIParams, withURIParams } from "../util/uri.js";
|
|
8
7
|
import { getValid } from "../util/validate.js";
|
|
9
8
|
/**
|
|
10
9
|
* An abstract API resource definition, used to specify types for e.g. serverless functions.
|
|
@@ -77,7 +76,7 @@ export class Endpoint {
|
|
|
77
76
|
* @throws Feedback if the payload is invalid.
|
|
78
77
|
*/
|
|
79
78
|
request(payload, options = {}, caller = this.request) {
|
|
80
|
-
return
|
|
79
|
+
return getRequest(this.method, this.url, this.payload.validate(payload), options, caller);
|
|
81
80
|
}
|
|
82
81
|
/**
|
|
83
82
|
* Validate an HTTP `Response` against this endpoint.
|
|
@@ -127,69 +126,3 @@ export function PATCH(url, payload = UNDEFINED, result = UNDEFINED) {
|
|
|
127
126
|
export function DELETE(url, payload = UNDEFINED, result = UNDEFINED) {
|
|
128
127
|
return new Endpoint("DELETE", url, payload, result);
|
|
129
128
|
}
|
|
130
|
-
/**
|
|
131
|
-
* Create a `Request` instance for a method/url and payload.
|
|
132
|
-
*
|
|
133
|
-
* - If `{placeholders}` are set in the URL, they are replaced by values from payload (will throw if `payload` is not a dictionary object).
|
|
134
|
-
* - If the method is `HEAD` or `GET`, the payload is sent as `?query` parameters in the URL.
|
|
135
|
-
* - If the method is anything else, the payload is sent in the body (either as JSON, string, or `FormData`).
|
|
136
|
-
*
|
|
137
|
-
* @throws ValueError if this is a `HEAD` or `GET` request but `body` is not a dictionary object.
|
|
138
|
-
* @throws ValueError if `{placeholders}` are set in the URL but `body` is not a dictionary object.
|
|
139
|
-
*/
|
|
140
|
-
function createRequest(method, url, payload, options = {}, caller = createRequest) {
|
|
141
|
-
// This is a head request, so ensure the payload is a dictionary object.
|
|
142
|
-
if (method === "GET" || method === "HEAD") {
|
|
143
|
-
assertDictionary(payload, caller);
|
|
144
|
-
return createHeadRequest(method, url, payload, options, caller);
|
|
145
|
-
}
|
|
146
|
-
// This is a normal body request.
|
|
147
|
-
return createBodyRequest(method, url, payload, options, caller);
|
|
148
|
-
}
|
|
149
|
-
/**
|
|
150
|
-
* Create a body-less request to a URL.
|
|
151
|
-
* - Any `{placeholders}` in the URL will be rendered with values from `params`, and won't be set in `?query` parameters in the URL.
|
|
152
|
-
*/
|
|
153
|
-
function createHeadRequest(method, url, params, options = {}, caller = createHeadRequest) {
|
|
154
|
-
const placeholders = getPlaceholders(url);
|
|
155
|
-
// URL has `{placeholders}` to render, so rendere those to the URL and add all other params as `?query` params.
|
|
156
|
-
if (placeholders.length) {
|
|
157
|
-
const rendered = omitURIParams(withURIParams(renderTemplate(url, params, caller), params, caller), ...placeholders);
|
|
158
|
-
return new Request(rendered, { ...options, method });
|
|
159
|
-
}
|
|
160
|
-
// URL has no `{placeholders}`, so add all payload params to the URL.
|
|
161
|
-
return new Request(withURIParams(url, params, caller), { ...options, method });
|
|
162
|
-
}
|
|
163
|
-
/**
|
|
164
|
-
* Create a body request to a URL.
|
|
165
|
-
* - Any `{placeholders}` in the URL will be rendered with values from `data`, and won't be set in the request body.
|
|
166
|
-
* - The payload is sent in the body (either as JSON, string, or `FormData`).
|
|
167
|
-
*
|
|
168
|
-
* @throws ValueError if `{placeholders}` are set in the URL but `body` is not a dictionary object.
|
|
169
|
-
*/
|
|
170
|
-
function createBodyRequest(method, url, body, options = {}, caller = createBodyRequest) {
|
|
171
|
-
const placeholders = getPlaceholders(url);
|
|
172
|
-
// If `{placeholders}` are set in the URL then body must be a dictionary object and is sent as JSON.
|
|
173
|
-
if (placeholders.length) {
|
|
174
|
-
assertDictionary(body, caller);
|
|
175
|
-
return createJSONRequest(method, url, body, options);
|
|
176
|
-
}
|
|
177
|
-
// `FormData` instances pass through unaltered and will set their own `Content-Type` with complex boundary information.
|
|
178
|
-
if (body instanceof FormData)
|
|
179
|
-
return createFormDataRequest(method, url, body, options);
|
|
180
|
-
if (typeof body === "string")
|
|
181
|
-
return createTextRequest(method, url, body, options);
|
|
182
|
-
return createJSONRequest(method, url, body, options); // JSON is the default.
|
|
183
|
-
}
|
|
184
|
-
/** Create a `FormData` request to a URL. */
|
|
185
|
-
function createFormDataRequest(method, url, body, options = {}) {
|
|
186
|
-
return new Request(url, { ...options, method, body });
|
|
187
|
-
}
|
|
188
|
-
/** Create a plain text request to a URL. */
|
|
189
|
-
function createTextRequest(method, url, body, { headers, ...options } = {}) {
|
|
190
|
-
return new Request(url, { ...options, headers: { ...headers, "Content-Type": "text/plain" }, method, body });
|
|
191
|
-
}
|
|
192
|
-
/** Create a JSON request to a URL. */
|
|
193
|
-
function createJSONRequest(method, url, body, { headers, ...options } = {}) {
|
|
194
|
-
return new Request(url, { ...options, headers: { ...headers, "Content-Type": "application/json" }, method, body: JSON.stringify(body) });
|
|
195
|
-
}
|
package/package.json
CHANGED
package/util/http.d.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { RequestError } from "../error/RequestError.js";
|
|
2
2
|
import { ResponseError } from "../error/ResponseError.js";
|
|
3
|
+
import { type ImmutableDictionary } from "./dictionary.js";
|
|
3
4
|
import type { AnyCaller } from "./function.js";
|
|
5
|
+
import type { URLString } from "./url.js";
|
|
4
6
|
/** A handler function takes a `Request` and returns a `Response` (possibly asynchronously). */
|
|
5
7
|
export type RequestHandler = (request: Request) => Response | Promise<Response>;
|
|
6
8
|
export declare function _getMessageJSON(message: Request | Response, MessageError: typeof RequestError | typeof ResponseError, caller: AnyCaller): Promise<unknown>;
|
|
@@ -49,3 +51,23 @@ export declare function getResponse(value: unknown): Response;
|
|
|
49
51
|
* @param debug If `true` include the error message in the response (for debugging), or `false` to return generic error codes (for security).
|
|
50
52
|
*/
|
|
51
53
|
export declare function getErrorResponse(reason: unknown, debug?: boolean): Response;
|
|
54
|
+
/** HTTP request methods. */
|
|
55
|
+
export type RequestMethod = RequestBodyMethod | RequestHeadMethod;
|
|
56
|
+
/** HTTP request methods that have no body. */
|
|
57
|
+
export type RequestHeadMethod = "HEAD" | "GET";
|
|
58
|
+
/** HTTP request methods that have a body. */
|
|
59
|
+
export type RequestBodyMethod = "POST" | "PUT" | "PATCH" | "DELETE";
|
|
60
|
+
/** Configurable options for endpoint. */
|
|
61
|
+
export type RequestOptions = Omit<RequestInit, "method" | "body">;
|
|
62
|
+
/**
|
|
63
|
+
* Create a `Request` instance for a method/url and payload.
|
|
64
|
+
*
|
|
65
|
+
* - If `{placeholders}` are set in the URL, they are replaced by values from payload (will throw if `payload` is not a dictionary object).
|
|
66
|
+
* - If the method is `HEAD` or `GET`, the payload is sent as `?query` parameters in the URL.
|
|
67
|
+
* - If the method is anything else, the payload is sent in the body (plain text string, `FormData` object, or JSON for any other).
|
|
68
|
+
*
|
|
69
|
+
* @throws ValueError if this is a `HEAD` or `GET` request but `body` is not a dictionary object.
|
|
70
|
+
* @throws ValueError if `{placeholders}` are set in the URL but `body` is not a dictionary object.
|
|
71
|
+
*/
|
|
72
|
+
export declare function getRequest(method: RequestHeadMethod, url: URLString, payload: ImmutableDictionary<unknown>, options?: RequestOptions, caller?: AnyCaller): Request;
|
|
73
|
+
export declare function getRequest(method: RequestMethod, url: URLString, payload: unknown, options?: RequestOptions, caller?: AnyCaller): Request;
|
package/util/http.js
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import { RequestError } from "../error/RequestError.js";
|
|
2
2
|
import { ResponseError } from "../error/ResponseError.js";
|
|
3
3
|
import { Feedback } from "../feedback/Feedback.js";
|
|
4
|
+
import { assertDictionary } from "./dictionary.js";
|
|
4
5
|
import { isError } from "./error.js";
|
|
6
|
+
import { getPlaceholders, renderTemplate } from "./template.js";
|
|
7
|
+
import { omitURIParams, withURIParams } from "./uri.js";
|
|
5
8
|
export async function _getMessageJSON(message, MessageError, caller) {
|
|
6
9
|
const trimmed = (await message.text()).trim();
|
|
7
10
|
if (!trimmed.length)
|
|
@@ -112,3 +115,59 @@ export function getErrorResponse(reason, debug = false) {
|
|
|
112
115
|
// Otherwise return a generic error message with no details.
|
|
113
116
|
return new Response(undefined, { status });
|
|
114
117
|
}
|
|
118
|
+
export function getRequest(method, url, payload, options = {}, caller = getRequest) {
|
|
119
|
+
// This is a head request, so ensure the payload is a dictionary object.
|
|
120
|
+
if (method === "GET" || method === "HEAD") {
|
|
121
|
+
assertDictionary(payload, caller);
|
|
122
|
+
return getHeadRequest(method, url, payload, options, caller);
|
|
123
|
+
}
|
|
124
|
+
// This is a normal body request.
|
|
125
|
+
return getBodyRequest(method, url, payload, options, caller);
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Create a body-less request to a URL.
|
|
129
|
+
* - Any `{placeholders}` in the URL will be rendered with values from `params`, and won't be set in `?query` parameters in the URL.
|
|
130
|
+
*/
|
|
131
|
+
function getHeadRequest(method, url, params, options = {}, caller = getHeadRequest) {
|
|
132
|
+
const placeholders = getPlaceholders(url);
|
|
133
|
+
// URL has `{placeholders}` to render, so rendere those to the URL and add all other params as `?query` params.
|
|
134
|
+
if (placeholders.length) {
|
|
135
|
+
const rendered = omitURIParams(withURIParams(renderTemplate(url, params, caller), params, caller), ...placeholders);
|
|
136
|
+
return new Request(rendered, { ...options, method });
|
|
137
|
+
}
|
|
138
|
+
// URL has no `{placeholders}`, so add all payload params to the URL.
|
|
139
|
+
return new Request(withURIParams(url, params, caller), { ...options, method });
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Create a body request to a URL.
|
|
143
|
+
* - Any `{placeholders}` in the URL will be rendered with values from `data`, and won't be set in the request body.
|
|
144
|
+
* - The payload is sent in the body (either as JSON, string, or `FormData`).
|
|
145
|
+
*
|
|
146
|
+
* @throws ValueError if `{placeholders}` are set in the URL but `body` is not a dictionary object.
|
|
147
|
+
*/
|
|
148
|
+
function getBodyRequest(method, url, body, options = {}, caller = getBodyRequest) {
|
|
149
|
+
const placeholders = getPlaceholders(url);
|
|
150
|
+
// If `{placeholders}` are set in the URL then body must be a dictionary object and is sent as JSON.
|
|
151
|
+
if (placeholders.length) {
|
|
152
|
+
assertDictionary(body, caller);
|
|
153
|
+
return getJSONRequest(method, renderTemplate(url, body, caller), body, options);
|
|
154
|
+
}
|
|
155
|
+
// `FormData` instances pass through unaltered and will set their own `Content-Type` with complex boundary information.
|
|
156
|
+
if (body instanceof FormData)
|
|
157
|
+
return getFormDataRequest(method, url, body, options);
|
|
158
|
+
if (typeof body === "string")
|
|
159
|
+
return getTextRequest(method, url, body, options);
|
|
160
|
+
return getJSONRequest(method, url, body, options); // JSON is the default.
|
|
161
|
+
}
|
|
162
|
+
/** Create a `FormData` request to a URL. */
|
|
163
|
+
function getFormDataRequest(method, url, body, options = {}) {
|
|
164
|
+
return new Request(url, { ...options, method, body });
|
|
165
|
+
}
|
|
166
|
+
/** Create a plain text request to a URL. */
|
|
167
|
+
function getTextRequest(method, url, body, { headers, ...options } = {}) {
|
|
168
|
+
return new Request(url, { ...options, headers: { ...headers, "Content-Type": "text/plain" }, method, body });
|
|
169
|
+
}
|
|
170
|
+
/** Create a JSON request to a URL. */
|
|
171
|
+
function getJSONRequest(method, url, body, { headers, ...options } = {}) {
|
|
172
|
+
return new Request(url, { ...options, headers: { ...headers, "Content-Type": "application/json" }, method, body: JSON.stringify(body) });
|
|
173
|
+
}
|