@autometa/http 1.4.19 → 2.0.0-rc.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/README.md +107 -2
- package/dist/assertions/http-adapters.d.ts +35 -0
- package/dist/assertions/http-assertions-plugin.d.ts +16 -0
- package/dist/assertions/http-ensure.d.ts +42 -0
- package/dist/axios-transport.d.ts +22 -0
- package/dist/default-schema.d.ts +8 -0
- package/dist/fetch-transport.d.ts +21 -0
- package/dist/http-request.d.ts +109 -0
- package/dist/http-response.d.ts +77 -0
- package/dist/http.d.ts +300 -0
- package/dist/index.cjs +2076 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.ts +15 -1116
- package/dist/index.js +1727 -845
- package/dist/index.js.map +1 -1
- package/dist/plugins.d.ts +43 -0
- package/dist/request-meta.config.d.ts +56 -0
- package/dist/schema.map.d.ts +11 -0
- package/dist/transform-response.d.ts +1 -0
- package/dist/transport.d.ts +11 -0
- package/dist/types.d.ts +39 -0
- package/package.json +31 -31
- package/.eslintignore +0 -3
- package/.eslintrc.cjs +0 -4
- package/.turbo/turbo-lint$colon$fix.log +0 -4
- package/.turbo/turbo-prettify.log +0 -5
- package/.turbo/turbo-test.log +0 -120
- package/CHANGELOG.md +0 -326
- package/dist/esm/index.js +0 -1117
- package/dist/esm/index.js.map +0 -1
- package/dist/index.d.cts +0 -1116
- package/src/axios-client.ts +0 -35
- package/src/default-client-factory.axios.spec.ts +0 -9
- package/src/default-client-factory.other.spec.ts +0 -13
- package/src/default-client-factory.ts +0 -7
- package/src/default-schema.spec.ts +0 -74
- package/src/default-schema.ts +0 -127
- package/src/http-client.ts +0 -20
- package/src/http-request.spec.ts +0 -172
- package/src/http-request.ts +0 -201
- package/src/http-response.ts +0 -107
- package/src/http.spec.ts +0 -189
- package/src/http.ts +0 -907
- package/src/index.ts +0 -13
- package/src/node_modules/.vitest/deps/_metadata.json +0 -8
- package/src/node_modules/.vitest/deps/package.json +0 -3
- package/src/node_modules/.vitest/results.json +0 -1
- package/src/request-meta.config.spec.ts +0 -81
- package/src/request-meta.config.ts +0 -134
- package/src/request.config.ts +0 -34
- package/src/schema.map.spec.ts +0 -50
- package/src/schema.map.ts +0 -68
- package/src/transform-response.ts +0 -43
- package/src/types.ts +0 -37
- package/tsup.config.ts +0 -14
package/src/axios-client.ts
DELETED
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
import { HTTPRequest } from "./http-request";
|
|
2
|
-
import { HTTPResponse, HTTPResponseBuilder } from "./http-response";
|
|
3
|
-
import { HTTPAdditionalOptions, StatusCode } from "./types";
|
|
4
|
-
import axios, { AxiosRequestConfig } from "axios";
|
|
5
|
-
import { HTTPClient } from "./http-client";
|
|
6
|
-
|
|
7
|
-
@HTTPClient.Use()
|
|
8
|
-
export class AxiosClient extends HTTPClient {
|
|
9
|
-
async request<TRequestType, TResponseType>(
|
|
10
|
-
request: HTTPRequest<TRequestType>,
|
|
11
|
-
options: HTTPAdditionalOptions<AxiosRequestConfig>
|
|
12
|
-
): Promise<HTTPResponse<TResponseType>> {
|
|
13
|
-
const { baseUrl, route, params, headers, method, data } = request;
|
|
14
|
-
const url = [baseUrl, ...route].join("/");
|
|
15
|
-
const axiosRequest: AxiosRequestConfig = {
|
|
16
|
-
url,
|
|
17
|
-
params,
|
|
18
|
-
headers,
|
|
19
|
-
method,
|
|
20
|
-
data,
|
|
21
|
-
validateStatus: function (status) {
|
|
22
|
-
return status >= 0 && status < 600;
|
|
23
|
-
},
|
|
24
|
-
...options,
|
|
25
|
-
};
|
|
26
|
-
const response = await axios(axiosRequest);
|
|
27
|
-
return HTTPResponseBuilder.create()
|
|
28
|
-
.status(response.status as StatusCode)
|
|
29
|
-
.statusText(response.statusText)
|
|
30
|
-
.data(response.data)
|
|
31
|
-
.headers(response.headers as Record<string, string>)
|
|
32
|
-
.request(request)
|
|
33
|
-
.build() as HTTPResponse<TResponseType>;
|
|
34
|
-
}
|
|
35
|
-
}
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from "vitest";
|
|
2
|
-
import { defaultClientFactory } from "./default-client-factory";
|
|
3
|
-
import { AxiosClient } from "./axios-client";
|
|
4
|
-
describe("default client as Axios", () => {
|
|
5
|
-
it("should be an Axios client", () => {
|
|
6
|
-
const client = defaultClientFactory();
|
|
7
|
-
expect(client).instanceOf(AxiosClient);
|
|
8
|
-
});
|
|
9
|
-
});
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, vi } from "vitest";
|
|
2
|
-
import { defaultClientFactory } from "./default-client-factory";
|
|
3
|
-
import { HTTPClient } from "./http-client";
|
|
4
|
-
describe("default client as Axios", () => {
|
|
5
|
-
@HTTPClient.Use()
|
|
6
|
-
class TestHTTPClient extends HTTPClient {
|
|
7
|
-
request = vi.fn();
|
|
8
|
-
}
|
|
9
|
-
it("should be an Axios client", () => {
|
|
10
|
-
const client = defaultClientFactory();
|
|
11
|
-
expect(client).instanceOf(TestHTTPClient);
|
|
12
|
-
});
|
|
13
|
-
});
|
|
@@ -1,74 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from "vitest";
|
|
2
|
-
import {
|
|
3
|
-
AnySchema,
|
|
4
|
-
BooleanSchema,
|
|
5
|
-
NullSchema,
|
|
6
|
-
NumberSchema,
|
|
7
|
-
StringSchema,
|
|
8
|
-
UndefinedSchema,
|
|
9
|
-
} from "./default-schema";
|
|
10
|
-
describe("Default Schema Functions", () => {
|
|
11
|
-
describe("AnySchema", () => {
|
|
12
|
-
it("should return the data", () => {
|
|
13
|
-
expect(AnySchema(1)).toBe(1);
|
|
14
|
-
});
|
|
15
|
-
});
|
|
16
|
-
|
|
17
|
-
describe("StringSchema", () => {
|
|
18
|
-
it("should return the data", () => {
|
|
19
|
-
expect(StringSchema("1")).toBe("1");
|
|
20
|
-
});
|
|
21
|
-
});
|
|
22
|
-
|
|
23
|
-
describe("NumberSchema", () => {
|
|
24
|
-
it("should return the data", () => {
|
|
25
|
-
expect(NumberSchema(1)).toBe(1);
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
it("should return stringified data", () => {
|
|
29
|
-
expect(NumberSchema("1")).toBe(1);
|
|
30
|
-
});
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
describe("BooleanSchema", () => {
|
|
34
|
-
it("should return the data", () => {
|
|
35
|
-
expect(BooleanSchema(true)).toBe(true);
|
|
36
|
-
});
|
|
37
|
-
|
|
38
|
-
it("should return stringified data", () => {
|
|
39
|
-
expect(BooleanSchema("true")).toBe(true);
|
|
40
|
-
});
|
|
41
|
-
});
|
|
42
|
-
|
|
43
|
-
describe("NullSchema", () => {
|
|
44
|
-
it("should return the data", () => {
|
|
45
|
-
expect(NullSchema(null)).toBe(null);
|
|
46
|
-
});
|
|
47
|
-
|
|
48
|
-
it("should return the stringified data", () => {
|
|
49
|
-
expect(NullSchema("null")).toBe(null);
|
|
50
|
-
});
|
|
51
|
-
|
|
52
|
-
it("should throw if the data is not null", () => {
|
|
53
|
-
expect(() => NullSchema(1)).toThrow();
|
|
54
|
-
});
|
|
55
|
-
|
|
56
|
-
it("should throw if the data is not null", () => {
|
|
57
|
-
expect(() => NullSchema(undefined)).toThrow();
|
|
58
|
-
});
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
describe("UndefinedSchema", () => {
|
|
62
|
-
it("should return the data", () => {
|
|
63
|
-
expect(UndefinedSchema(undefined)).toBe(undefined);
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
it("should throw if the data is not undefined", () => {
|
|
67
|
-
expect(() => UndefinedSchema(1)).toThrow();
|
|
68
|
-
});
|
|
69
|
-
|
|
70
|
-
it("should throw if the data is not undefined", () => {
|
|
71
|
-
expect(() => UndefinedSchema(null)).toThrow();
|
|
72
|
-
});
|
|
73
|
-
});
|
|
74
|
-
});
|
package/src/default-schema.ts
DELETED
|
@@ -1,127 +0,0 @@
|
|
|
1
|
-
import isJson from "@stdlib/assert-is-json";
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Schema which does not care about data validation.
|
|
5
|
-
*
|
|
6
|
-
* Useful if `requireSchema` is set to true, but a specific
|
|
7
|
-
* endpoints response does not matter.
|
|
8
|
-
* @param data
|
|
9
|
-
* @returns
|
|
10
|
-
*/
|
|
11
|
-
export function AnySchema(data: unknown) {
|
|
12
|
-
return data;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* Schema which validates that a response is empty. This can mean
|
|
17
|
-
* the data payload was `null`, `undefined` or the string `'null'`.
|
|
18
|
-
*
|
|
19
|
-
* Useful if `requireSchema` is set to true, but a specific
|
|
20
|
-
* endpoints response should be null or not defined.
|
|
21
|
-
* @param data
|
|
22
|
-
* @returns
|
|
23
|
-
*/
|
|
24
|
-
export function EmptySchema(data: unknown) {
|
|
25
|
-
if (data !== null && data !== undefined && data !== "null") {
|
|
26
|
-
throw new Error(`Expected null but got <${typeof data}> for ${data}`);
|
|
27
|
-
}
|
|
28
|
-
return data === "null" ? null : data;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
/**
|
|
32
|
-
* Schema which validates a response was null.
|
|
33
|
-
*
|
|
34
|
-
* Useful if `requireSchema` is set to true, but a specific
|
|
35
|
-
* endpoints response should be null.
|
|
36
|
-
* @param data
|
|
37
|
-
* @returns
|
|
38
|
-
*/
|
|
39
|
-
export function NullSchema(data: unknown) {
|
|
40
|
-
if (data !== null && data !== "null") {
|
|
41
|
-
throw new Error(`Expected null but got <${typeof data}> for ${data}`);
|
|
42
|
-
}
|
|
43
|
-
return null;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
/**
|
|
47
|
-
* Schema which validates a response was undefined.
|
|
48
|
-
*
|
|
49
|
-
* Useful if `requireSchema` is set to true, but a specific
|
|
50
|
-
* endpoints response should be undefined.
|
|
51
|
-
*
|
|
52
|
-
* @param data
|
|
53
|
-
* @returns
|
|
54
|
-
*/
|
|
55
|
-
export function UndefinedSchema(data: unknown) {
|
|
56
|
-
if (data !== undefined) {
|
|
57
|
-
throw new Error(`Expected undefined but got <${typeof data}> for ${data}`);
|
|
58
|
-
}
|
|
59
|
-
return undefined;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
/**
|
|
63
|
-
* Schema which validates a response was a boolean, or a string of value
|
|
64
|
-
* `'true'` or `'false'`.
|
|
65
|
-
*
|
|
66
|
-
* Useful if `requireSchema` is set to true, but a specific
|
|
67
|
-
* endpoints response should be a boolean.
|
|
68
|
-
*
|
|
69
|
-
* @param data
|
|
70
|
-
* @returns
|
|
71
|
-
*/
|
|
72
|
-
export function BooleanSchema(data: unknown) {
|
|
73
|
-
if (
|
|
74
|
-
!(typeof data === "boolean") &&
|
|
75
|
-
["true", "false"].includes(String(data)) === false
|
|
76
|
-
) {
|
|
77
|
-
throw new Error(`Expected boolean but got <${typeof data}> for ${data}`);
|
|
78
|
-
}
|
|
79
|
-
return JSON.parse(data as string);
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
/**
|
|
83
|
-
* Schema which validates a response was a number, or a string of value
|
|
84
|
-
* of a number.
|
|
85
|
-
*
|
|
86
|
-
* Useful if `requireSchema` is set to true, but a specific
|
|
87
|
-
* endpoints response should be a number.
|
|
88
|
-
*
|
|
89
|
-
* @param data
|
|
90
|
-
* @returns
|
|
91
|
-
*/
|
|
92
|
-
export function NumberSchema(data: unknown) {
|
|
93
|
-
if (
|
|
94
|
-
!(typeof data === "number") &&
|
|
95
|
-
/^\d*\.?\d+$/.test(String(data)) === false
|
|
96
|
-
) {
|
|
97
|
-
throw new Error(`Expected number but got <${typeof data}> for ${data}`);
|
|
98
|
-
}
|
|
99
|
-
return JSON.parse(data as string);
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
/**
|
|
103
|
-
* Schema which validates a response was a string.
|
|
104
|
-
*
|
|
105
|
-
* Useful if `requireSchema` is set to true, but a specific
|
|
106
|
-
* endpoints response should be a string.
|
|
107
|
-
*
|
|
108
|
-
* @param data
|
|
109
|
-
* @returns
|
|
110
|
-
*/
|
|
111
|
-
export function StringSchema(data: unknown) {
|
|
112
|
-
if (typeof data !== "string") {
|
|
113
|
-
throw new Error(`Expected string but got <${typeof data}> for ${data}`);
|
|
114
|
-
}
|
|
115
|
-
return data;
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
export function JSONSchema<T = unknown>(data: unknown) {
|
|
119
|
-
if (typeof data === "object") {
|
|
120
|
-
return data as T;
|
|
121
|
-
}
|
|
122
|
-
if (!isJson(data)) {
|
|
123
|
-
throw new Error(`Expected JSON but got <${typeof data}> for ${data}`);
|
|
124
|
-
}
|
|
125
|
-
const result = JSON.parse(data);
|
|
126
|
-
return result;
|
|
127
|
-
}
|
package/src/http-client.ts
DELETED
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
import { HTTPRequest } from "./http-request";
|
|
2
|
-
import { HTTPResponse } from "./http-response";
|
|
3
|
-
import { HTTPAdditionalOptions } from "./types";
|
|
4
|
-
import { Class } from "@autometa/types";
|
|
5
|
-
export let defaultClient: Class<HTTPClient>;
|
|
6
|
-
export abstract class HTTPClient {
|
|
7
|
-
static Use(): (target: Class<HTTPClient>) => void;
|
|
8
|
-
static Use(client?: Class<HTTPClient>) {
|
|
9
|
-
if (client) {
|
|
10
|
-
defaultClient = client;
|
|
11
|
-
}
|
|
12
|
-
return function (target: Class<HTTPClient>) {
|
|
13
|
-
defaultClient = target;
|
|
14
|
-
};
|
|
15
|
-
}
|
|
16
|
-
abstract request<TRequestType, TResponseType>(
|
|
17
|
-
request: HTTPRequest<TRequestType>,
|
|
18
|
-
options?: HTTPAdditionalOptions<unknown>
|
|
19
|
-
): Promise<HTTPResponse<TResponseType>>;
|
|
20
|
-
}
|
package/src/http-request.spec.ts
DELETED
|
@@ -1,172 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from "vitest";
|
|
2
|
-
import { HTTPRequest, HTTPRequestBuilder } from "./http-request";
|
|
3
|
-
|
|
4
|
-
describe("HTTP Request", () => {
|
|
5
|
-
it("should derive a detailed request", () => {
|
|
6
|
-
const request = new HTTPRequest();
|
|
7
|
-
request.baseUrl = "https://example.com";
|
|
8
|
-
request.route.push("foo", "bar");
|
|
9
|
-
request.params = { foo: "bar" };
|
|
10
|
-
request.data = { foo: "bar" };
|
|
11
|
-
request.headers = { foo: "bar" };
|
|
12
|
-
request.method = "GET";
|
|
13
|
-
|
|
14
|
-
const derived = HTTPRequest.derive(request);
|
|
15
|
-
expect(derived).toEqual({
|
|
16
|
-
baseUrl: "https://example.com",
|
|
17
|
-
route: ["foo", "bar"],
|
|
18
|
-
params: { foo: "bar" },
|
|
19
|
-
data: { foo: "bar" },
|
|
20
|
-
headers: { foo: "bar" },
|
|
21
|
-
method: "GET",
|
|
22
|
-
});
|
|
23
|
-
});
|
|
24
|
-
|
|
25
|
-
describe("HTTPRequestBuilder", () => {
|
|
26
|
-
it("should build a request from a base", () => {
|
|
27
|
-
const request = new HTTPRequest();
|
|
28
|
-
request.baseUrl = "https://example.com";
|
|
29
|
-
request.route.push("foo", "bar");
|
|
30
|
-
request.params = { foo: "bar" };
|
|
31
|
-
request.data = { foo: "bar" };
|
|
32
|
-
request.headers = { foo: "bar" };
|
|
33
|
-
request.method = "GET";
|
|
34
|
-
const builder = new HTTPRequestBuilder(request);
|
|
35
|
-
expect(builder.request).toEqual(request);
|
|
36
|
-
});
|
|
37
|
-
|
|
38
|
-
it("should build a request from scratch", () => {
|
|
39
|
-
const builder = new HTTPRequestBuilder()
|
|
40
|
-
.url("https://example.com")
|
|
41
|
-
.route("foo", "bar")
|
|
42
|
-
.param("foo", "bar")
|
|
43
|
-
.data({ foo: "bar" })
|
|
44
|
-
.header("foo", "bar")
|
|
45
|
-
.method("GET");
|
|
46
|
-
expect(builder.request).toEqual({
|
|
47
|
-
baseUrl: "https://example.com",
|
|
48
|
-
route: ["foo", "bar"],
|
|
49
|
-
params: { foo: "bar" },
|
|
50
|
-
data: { foo: "bar" },
|
|
51
|
-
headers: { foo: "bar" },
|
|
52
|
-
method: "GET",
|
|
53
|
-
});
|
|
54
|
-
});
|
|
55
|
-
|
|
56
|
-
it("should derive a request builder", () => {
|
|
57
|
-
const builder = new HTTPRequestBuilder()
|
|
58
|
-
.url("https://example.com")
|
|
59
|
-
.route("foo", "bar")
|
|
60
|
-
.param("foo", "bar")
|
|
61
|
-
.data({ foo: "bar" })
|
|
62
|
-
.header("foo", "bar")
|
|
63
|
-
.method("GET");
|
|
64
|
-
const derived = builder.derive();
|
|
65
|
-
expect(derived.request).toEqual(builder.request);
|
|
66
|
-
expect(derived.request).not.toBe(builder.request);
|
|
67
|
-
});
|
|
68
|
-
|
|
69
|
-
describe("build", () => {
|
|
70
|
-
it("should build a request", () => {
|
|
71
|
-
const request = new HTTPRequestBuilder()
|
|
72
|
-
.url("https://example.com")
|
|
73
|
-
.route("foo", "bar")
|
|
74
|
-
.param("foo", "bar")
|
|
75
|
-
.data({ foo: "bar" })
|
|
76
|
-
.header("foo", "bar")
|
|
77
|
-
.method("GET")
|
|
78
|
-
.build();
|
|
79
|
-
expect(request).toEqual({
|
|
80
|
-
baseUrl: "https://example.com",
|
|
81
|
-
route: ["foo", "bar"],
|
|
82
|
-
params: { foo: "bar" },
|
|
83
|
-
data: { foo: "bar" },
|
|
84
|
-
headers: { foo: "bar" },
|
|
85
|
-
method: "GET",
|
|
86
|
-
});
|
|
87
|
-
});
|
|
88
|
-
});
|
|
89
|
-
|
|
90
|
-
describe("paramList", () => {
|
|
91
|
-
it("should build a request with a list of params", () => {
|
|
92
|
-
const request = new HTTPRequestBuilder()
|
|
93
|
-
.url("https://example.com")
|
|
94
|
-
.route("foo", "bar")
|
|
95
|
-
.param("foo", ["bar", "baz"])
|
|
96
|
-
.data({ foo: "bar" })
|
|
97
|
-
.header("foo", "bar")
|
|
98
|
-
.method("GET")
|
|
99
|
-
.build();
|
|
100
|
-
expect(request).toEqual({
|
|
101
|
-
baseUrl: "https://example.com",
|
|
102
|
-
route: ["foo", "bar"],
|
|
103
|
-
params: { foo: ["bar", "baz"] },
|
|
104
|
-
data: { foo: "bar" },
|
|
105
|
-
headers: { foo: "bar" },
|
|
106
|
-
method: "GET",
|
|
107
|
-
});
|
|
108
|
-
});
|
|
109
|
-
});
|
|
110
|
-
|
|
111
|
-
describe("fullUrl", () => {
|
|
112
|
-
it("should have a full url with routes", () => {
|
|
113
|
-
const request = new HTTPRequestBuilder()
|
|
114
|
-
.url("https://example.com")
|
|
115
|
-
.route("foo")
|
|
116
|
-
.build().fullUrl;
|
|
117
|
-
expect(request).toEqual("https://example.com/foo");
|
|
118
|
-
});
|
|
119
|
-
it("should have a full url with routes and params", () => {
|
|
120
|
-
const request = new HTTPRequestBuilder()
|
|
121
|
-
.url("https://example.com")
|
|
122
|
-
.route("foo")
|
|
123
|
-
.param("gru", "bar")
|
|
124
|
-
.build().fullUrl;
|
|
125
|
-
expect(request).toEqual("https://example.com/foo?gru=bar");
|
|
126
|
-
});
|
|
127
|
-
|
|
128
|
-
it("should have a full url with a route and a param list", () => {
|
|
129
|
-
const request = new HTTPRequestBuilder()
|
|
130
|
-
.url("https://example.com")
|
|
131
|
-
.route("foo")
|
|
132
|
-
.param("bob", ["bar", "baz"])
|
|
133
|
-
.build().fullUrl;
|
|
134
|
-
expect(request).toEqual("https://example.com/foo?bob=bar%2Cbaz");
|
|
135
|
-
});
|
|
136
|
-
});
|
|
137
|
-
});
|
|
138
|
-
|
|
139
|
-
describe("dynamic header", () => {
|
|
140
|
-
it("should resolve a synchronous dynamic header", async () => {
|
|
141
|
-
const builder = new HTTPRequestBuilder()
|
|
142
|
-
.url("https://example.com")
|
|
143
|
-
.route("foo", "bar")
|
|
144
|
-
.param("foo", "bar")
|
|
145
|
-
.data({ foo: "bar" })
|
|
146
|
-
.header("foo", "bar")
|
|
147
|
-
.header("dynamic", () => "foo")
|
|
148
|
-
.method("GET");
|
|
149
|
-
await builder.resolveDynamicHeaders();
|
|
150
|
-
expect(builder.request.headers).toEqual({
|
|
151
|
-
foo: "bar",
|
|
152
|
-
dynamic: "foo",
|
|
153
|
-
});
|
|
154
|
-
});
|
|
155
|
-
|
|
156
|
-
it("should resolve an asynchronous dynamic header", async () => {
|
|
157
|
-
const builder = new HTTPRequestBuilder()
|
|
158
|
-
.url("https://example.com")
|
|
159
|
-
.route("foo", "bar")
|
|
160
|
-
.param("foo", "bar")
|
|
161
|
-
.data({ foo: "bar" })
|
|
162
|
-
.header("foo", "bar")
|
|
163
|
-
.header("dynamic", async () => "foo")
|
|
164
|
-
.method("GET");
|
|
165
|
-
await builder.resolveDynamicHeaders();
|
|
166
|
-
expect(builder.request.headers).toEqual({
|
|
167
|
-
foo: "bar",
|
|
168
|
-
dynamic: "foo",
|
|
169
|
-
});
|
|
170
|
-
});
|
|
171
|
-
});
|
|
172
|
-
});
|
package/src/http-request.ts
DELETED
|
@@ -1,201 +0,0 @@
|
|
|
1
|
-
import { RequestConfig, RequestConfigBasic } from "./request.config";
|
|
2
|
-
import { HTTPMethod } from "./types";
|
|
3
|
-
import { urlJoinP } from "url-join-ts";
|
|
4
|
-
export class HTTPRequest<T = unknown> implements RequestConfig<T> {
|
|
5
|
-
headers: Record<string, string> = {};
|
|
6
|
-
params: Record<string, string | string[] | Record<string, unknown>> = {};
|
|
7
|
-
baseUrl?: string;
|
|
8
|
-
route: string[] = [];
|
|
9
|
-
method: HTTPMethod;
|
|
10
|
-
data: T;
|
|
11
|
-
|
|
12
|
-
constructor(config?: RequestConfigBasic) {
|
|
13
|
-
Object.assign(this, config);
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
/**
|
|
17
|
-
* Returns the full URL of the request, including the base url,
|
|
18
|
-
* routes, and query parameters.
|
|
19
|
-
*
|
|
20
|
-
* ```ts
|
|
21
|
-
* console.log(request.fullUrl())// https://example.com/foo?bar=baz?array=1,2,3
|
|
22
|
-
* ```
|
|
23
|
-
*
|
|
24
|
-
* Note characters may be converted to escape codes. I.e (space => %20) and (comma => %2C)
|
|
25
|
-
*
|
|
26
|
-
* N.B this getter estimates what the url will be. The actual value
|
|
27
|
-
* might be different depending on your underlying HTTPClient and
|
|
28
|
-
* configuration. For example, query parameters might
|
|
29
|
-
* use different array formats.
|
|
30
|
-
*/
|
|
31
|
-
get fullUrl() {
|
|
32
|
-
return urlJoinP(this.baseUrl, this.route, this.params);
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
/**
|
|
36
|
-
* Returns a new independent copy of the request.
|
|
37
|
-
*/
|
|
38
|
-
static derive(original: HTTPRequest<unknown>) {
|
|
39
|
-
const request = new HTTPRequest();
|
|
40
|
-
request.headers = { ...original.headers };
|
|
41
|
-
request.params = { ...original.params };
|
|
42
|
-
request.baseUrl = original.baseUrl;
|
|
43
|
-
request.route = [...original.route];
|
|
44
|
-
request.method = original.method;
|
|
45
|
-
request.data = original.data;
|
|
46
|
-
return request;
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
export class HTTPRequestBuilder<T extends HTTPRequest<unknown>> {
|
|
51
|
-
#request: T;
|
|
52
|
-
#dynamicHeaders = new Map<
|
|
53
|
-
string,
|
|
54
|
-
| (() => string | number | boolean | null)
|
|
55
|
-
| (() => Promise<string | number | boolean | null>)
|
|
56
|
-
>();
|
|
57
|
-
|
|
58
|
-
constructor(request: T | (() => T) = () => new HTTPRequest() as T) {
|
|
59
|
-
if (typeof request === "function") {
|
|
60
|
-
this.#request = request();
|
|
61
|
-
return;
|
|
62
|
-
}
|
|
63
|
-
this.#request = request;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
static create<T extends HTTPRequest<unknown>>() {
|
|
67
|
-
return new HTTPRequestBuilder<T>();
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
get request() {
|
|
71
|
-
return this.#request;
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
async resolveDynamicHeaders(
|
|
75
|
-
request: HTTPRequest<T> = this.#request as HTTPRequest<T>
|
|
76
|
-
) {
|
|
77
|
-
for (const [name, value] of this.#dynamicHeaders) {
|
|
78
|
-
try {
|
|
79
|
-
if (!request.headers) request.headers = {};
|
|
80
|
-
request.headers[name] = String(await value());
|
|
81
|
-
} catch (e) {
|
|
82
|
-
const cause = e as Error;
|
|
83
|
-
const msg = `Failed to resolve dynamic header "${name}":
|
|
84
|
-
${cause}`;
|
|
85
|
-
throw new Error(msg);
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
return this;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
url(url: string) {
|
|
92
|
-
this.#request.baseUrl = url;
|
|
93
|
-
return this;
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
route(...route: string[]) {
|
|
97
|
-
this.#request.route.push(...route);
|
|
98
|
-
return this;
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
param(
|
|
102
|
-
name: string,
|
|
103
|
-
value:
|
|
104
|
-
| string
|
|
105
|
-
| number
|
|
106
|
-
| boolean
|
|
107
|
-
| (string | number | boolean)[]
|
|
108
|
-
| Record<string, string | number | boolean>
|
|
109
|
-
) {
|
|
110
|
-
if (Array.isArray(value)) {
|
|
111
|
-
const asStr = value.flatMap(String);
|
|
112
|
-
this.#request.params[name] = asStr;
|
|
113
|
-
return this;
|
|
114
|
-
}
|
|
115
|
-
if (!Array.isArray(value) && typeof value === "object") {
|
|
116
|
-
this.#request.params[name] = value;
|
|
117
|
-
return this;
|
|
118
|
-
}
|
|
119
|
-
this.#request.params[name] = String(value);
|
|
120
|
-
return this;
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
params(dict: Record<string, unknown>) {
|
|
124
|
-
Object.assign(this.#request.params, dict);
|
|
125
|
-
return this;
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
data<T>(data: T) {
|
|
129
|
-
this.#request.data = data;
|
|
130
|
-
return this;
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
header(
|
|
134
|
-
name: string,
|
|
135
|
-
value:
|
|
136
|
-
| string
|
|
137
|
-
| number
|
|
138
|
-
| boolean
|
|
139
|
-
| null
|
|
140
|
-
| (string | number | boolean)[]
|
|
141
|
-
| (() => string | number | boolean | null)
|
|
142
|
-
| (() => Promise<string | number | boolean | null>),
|
|
143
|
-
onArray: (value: (string | number | boolean)[]) => string = (value) =>
|
|
144
|
-
value.join(",")
|
|
145
|
-
) {
|
|
146
|
-
if (typeof value === "function") {
|
|
147
|
-
if (this.#request.headers[name]) {
|
|
148
|
-
delete this.#request.headers[name];
|
|
149
|
-
}
|
|
150
|
-
this.#dynamicHeaders.set(name, value);
|
|
151
|
-
return this;
|
|
152
|
-
}
|
|
153
|
-
if (this.#dynamicHeaders.has(name)) {
|
|
154
|
-
this.#dynamicHeaders.delete(name);
|
|
155
|
-
}
|
|
156
|
-
const val = Array.isArray(value) ? onArray(value) : String(value);
|
|
157
|
-
this.#request.headers[name] = val;
|
|
158
|
-
return this;
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
headers(dict: Record<string, string>) {
|
|
162
|
-
Object.assign(this.#request.headers, dict);
|
|
163
|
-
return this;
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
get() {
|
|
167
|
-
return this.#request;
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
method(method: HTTPMethod) {
|
|
171
|
-
this.#request.method = method;
|
|
172
|
-
return this;
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
#setDynamicHeaders(
|
|
176
|
-
headers: Map<
|
|
177
|
-
string,
|
|
178
|
-
| (() => string | number | boolean | null)
|
|
179
|
-
| (() => Promise<string | number | boolean | null>)
|
|
180
|
-
>
|
|
181
|
-
) {
|
|
182
|
-
this.#dynamicHeaders = new Map(headers);
|
|
183
|
-
return this;
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
derive(): HTTPRequestBuilder<T> {
|
|
187
|
-
const request = HTTPRequest.derive(this.#request);
|
|
188
|
-
return new HTTPRequestBuilder(request).#setDynamicHeaders(
|
|
189
|
-
this.#dynamicHeaders
|
|
190
|
-
) as HTTPRequestBuilder<T>;
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
build(): HTTPRequest<T> {
|
|
194
|
-
return this.#request as HTTPRequest<T>;
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
async buildAsync(): Promise<HTTPRequest<T>> {
|
|
198
|
-
await this.resolveDynamicHeaders();
|
|
199
|
-
return this.#request as HTTPRequest<T>;
|
|
200
|
-
}
|
|
201
|
-
}
|