@autometa/http 1.4.20 → 2.0.0-rc.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/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 +30 -30
- 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 -335
- 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/dist/index.js
CHANGED
|
@@ -1,1054 +1,1442 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
-
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
-
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
-
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
-
var __export = (target, all) => {
|
|
9
|
-
for (var name in all)
|
|
10
|
-
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
-
};
|
|
12
|
-
var __copyProps = (to, from, except, desc) => {
|
|
13
|
-
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
-
for (let key of __getOwnPropNames(from))
|
|
15
|
-
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
-
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
-
}
|
|
18
|
-
return to;
|
|
19
|
-
};
|
|
20
|
-
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
-
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
-
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
-
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
-
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
-
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
-
mod
|
|
27
|
-
));
|
|
28
|
-
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
-
var __decorateClass = (decorators, target, key, kind) => {
|
|
30
|
-
var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
|
|
31
|
-
for (var i = decorators.length - 1, decorator; i >= 0; i--)
|
|
32
|
-
if (decorator = decorators[i])
|
|
33
|
-
result = (kind ? decorator(target, key, result) : decorator(result)) || result;
|
|
34
|
-
if (kind && result)
|
|
35
|
-
__defProp(target, key, result);
|
|
36
|
-
return result;
|
|
37
|
-
};
|
|
38
|
-
var __accessCheck = (obj, member, msg) => {
|
|
39
|
-
if (!member.has(obj))
|
|
40
|
-
throw TypeError("Cannot " + msg);
|
|
41
|
-
};
|
|
42
|
-
var __privateGet = (obj, member, getter) => {
|
|
43
|
-
__accessCheck(obj, member, "read from private field");
|
|
44
|
-
return getter ? getter.call(obj) : member.get(obj);
|
|
45
|
-
};
|
|
46
|
-
var __privateAdd = (obj, member, value) => {
|
|
47
|
-
if (member.has(obj))
|
|
48
|
-
throw TypeError("Cannot add the same private member more than once");
|
|
49
|
-
member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
|
|
50
|
-
};
|
|
51
|
-
var __privateSet = (obj, member, value, setter) => {
|
|
52
|
-
__accessCheck(obj, member, "write to private field");
|
|
53
|
-
setter ? setter.call(obj, value) : member.set(obj, value);
|
|
54
|
-
return value;
|
|
55
|
-
};
|
|
56
|
-
var __privateMethod = (obj, member, method) => {
|
|
57
|
-
__accessCheck(obj, member, "access private method");
|
|
58
|
-
return method;
|
|
59
|
-
};
|
|
1
|
+
import { AutomationError } from '@autometa/errors';
|
|
2
|
+
import { highlight } from 'cli-highlight';
|
|
3
|
+
import { EnsureError } from '@autometa/assertions';
|
|
60
4
|
|
|
61
|
-
// src/
|
|
62
|
-
var src_exports = {};
|
|
63
|
-
__export(src_exports, {
|
|
64
|
-
AnySchema: () => AnySchema,
|
|
65
|
-
AxiosClient: () => AxiosClient,
|
|
66
|
-
BooleanSchema: () => BooleanSchema,
|
|
67
|
-
EmptySchema: () => EmptySchema,
|
|
68
|
-
HTTP: () => HTTP,
|
|
69
|
-
HTTPClient: () => HTTPClient,
|
|
70
|
-
HTTPRequest: () => HTTPRequest,
|
|
71
|
-
HTTPRequestBuilder: () => HTTPRequestBuilder,
|
|
72
|
-
HTTPResponse: () => HTTPResponse,
|
|
73
|
-
HTTPResponseBuilder: () => HTTPResponseBuilder,
|
|
74
|
-
JSONSchema: () => JSONSchema,
|
|
75
|
-
NullSchema: () => NullSchema,
|
|
76
|
-
NumberSchema: () => NumberSchema,
|
|
77
|
-
StringSchema: () => StringSchema,
|
|
78
|
-
UndefinedSchema: () => UndefinedSchema,
|
|
79
|
-
defaultClient: () => defaultClient
|
|
80
|
-
});
|
|
81
|
-
module.exports = __toCommonJS(src_exports);
|
|
5
|
+
// src/http.ts
|
|
82
6
|
|
|
83
|
-
// src/
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
const newResponse = new _HTTPResponse();
|
|
90
|
-
Object.assign(newResponse, response);
|
|
91
|
-
return response;
|
|
92
|
-
}
|
|
93
|
-
decompose(transformFnOrVal) {
|
|
94
|
-
const value = getDecompositionValue(this.data, transformFnOrVal);
|
|
95
|
-
return new HTTPResponseBuilder().status(this.status).statusText(this.statusText).headers(this.headers).request(this.request).data(value).build();
|
|
7
|
+
// src/fetch-transport.ts
|
|
8
|
+
function createFetchTransport(fetchImpl = globalThis.fetch) {
|
|
9
|
+
if (!fetchImpl) {
|
|
10
|
+
throw new Error(
|
|
11
|
+
"No fetch implementation available. Provide one via createFetchTransport(fetchImpl)."
|
|
12
|
+
);
|
|
96
13
|
}
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
14
|
+
return {
|
|
15
|
+
async send(request, options = {}) {
|
|
16
|
+
const {
|
|
17
|
+
headers: optionHeaders,
|
|
18
|
+
body: optionBody,
|
|
19
|
+
signal: optionSignal,
|
|
20
|
+
streamResponse,
|
|
21
|
+
...restOptions
|
|
22
|
+
} = options;
|
|
23
|
+
const headers = mergeHeaders(request.headers, optionHeaders);
|
|
24
|
+
const body = optionBody ?? createBody(request, headers);
|
|
25
|
+
const url = request.fullUrl ?? "";
|
|
26
|
+
const fetchOptions = {
|
|
27
|
+
...restOptions,
|
|
28
|
+
method: request.method ?? "GET",
|
|
29
|
+
headers,
|
|
30
|
+
body
|
|
31
|
+
};
|
|
32
|
+
if (optionSignal !== void 0) {
|
|
33
|
+
fetchOptions.signal = optionSignal;
|
|
34
|
+
}
|
|
35
|
+
const response = await fetchImpl(url, fetchOptions);
|
|
36
|
+
const data = streamResponse ? response.body ?? null : await readBody(response);
|
|
37
|
+
return {
|
|
38
|
+
status: response.status,
|
|
39
|
+
statusText: response.statusText,
|
|
40
|
+
headers: headersToRecord(response.headers),
|
|
41
|
+
data
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
};
|
|
100
45
|
}
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
status(code) {
|
|
113
|
-
__privateGet(this, _response).status = code;
|
|
114
|
-
return this;
|
|
46
|
+
function mergeHeaders(base, additional) {
|
|
47
|
+
const next = { ...base };
|
|
48
|
+
if (!additional) {
|
|
49
|
+
return next;
|
|
50
|
+
}
|
|
51
|
+
for (const [key, value] of Object.entries(additional)) {
|
|
52
|
+
if (value === void 0 || value === null) {
|
|
53
|
+
delete next[key];
|
|
54
|
+
} else {
|
|
55
|
+
next[key] = String(value);
|
|
56
|
+
}
|
|
115
57
|
}
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
58
|
+
return next;
|
|
59
|
+
}
|
|
60
|
+
function createBody(request, headers) {
|
|
61
|
+
const { data } = request;
|
|
62
|
+
if (data === void 0 || data === null) {
|
|
63
|
+
return void 0;
|
|
119
64
|
}
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
return this;
|
|
65
|
+
if (isBodyInit(data)) {
|
|
66
|
+
return data;
|
|
123
67
|
}
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
return this;
|
|
68
|
+
if (typeof data === "string") {
|
|
69
|
+
return data;
|
|
127
70
|
}
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
71
|
+
if (typeof data === "object") {
|
|
72
|
+
if (!hasHeader(headers, "content-type")) {
|
|
73
|
+
headers["content-type"] = "application/json";
|
|
74
|
+
}
|
|
75
|
+
return JSON.stringify(data);
|
|
131
76
|
}
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
77
|
+
return String(data);
|
|
78
|
+
}
|
|
79
|
+
async function readBody(response) {
|
|
80
|
+
if (response.status === 204 || response.status === 205) {
|
|
81
|
+
return null;
|
|
135
82
|
}
|
|
136
|
-
|
|
137
|
-
|
|
83
|
+
const text = await response.text();
|
|
84
|
+
return text.length === 0 ? null : text;
|
|
85
|
+
}
|
|
86
|
+
function headersToRecord(headers) {
|
|
87
|
+
const record = {};
|
|
88
|
+
headers.forEach((value, key) => {
|
|
89
|
+
record[key] = value;
|
|
90
|
+
});
|
|
91
|
+
return record;
|
|
92
|
+
}
|
|
93
|
+
function hasHeader(headers, name) {
|
|
94
|
+
const lower = name.toLowerCase();
|
|
95
|
+
return Object.keys(headers).some((key) => key.toLowerCase() === lower);
|
|
96
|
+
}
|
|
97
|
+
function isBodyInit(value) {
|
|
98
|
+
if (typeof value === "string") {
|
|
99
|
+
return true;
|
|
138
100
|
}
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
var HTTPResponseBuilder = _HTTPResponseBuilder;
|
|
142
|
-
|
|
143
|
-
// src/axios-client.ts
|
|
144
|
-
var import_axios = __toESM(require("axios"), 1);
|
|
145
|
-
|
|
146
|
-
// src/http-client.ts
|
|
147
|
-
var defaultClient;
|
|
148
|
-
var HTTPClient = class {
|
|
149
|
-
static Use(client) {
|
|
150
|
-
if (client) {
|
|
151
|
-
defaultClient = client;
|
|
152
|
-
}
|
|
153
|
-
return function(target) {
|
|
154
|
-
defaultClient = target;
|
|
155
|
-
};
|
|
101
|
+
if (typeof ArrayBuffer !== "undefined" && value instanceof ArrayBuffer) {
|
|
102
|
+
return true;
|
|
156
103
|
}
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
// src/axios-client.ts
|
|
160
|
-
var AxiosClient = class extends HTTPClient {
|
|
161
|
-
async request(request, options) {
|
|
162
|
-
const { baseUrl, route, params, headers, method, data } = request;
|
|
163
|
-
const url = [baseUrl, ...route].join("/");
|
|
164
|
-
const axiosRequest = {
|
|
165
|
-
url,
|
|
166
|
-
params,
|
|
167
|
-
headers,
|
|
168
|
-
method,
|
|
169
|
-
data,
|
|
170
|
-
validateStatus: function(status) {
|
|
171
|
-
return status >= 0 && status < 600;
|
|
172
|
-
},
|
|
173
|
-
...options
|
|
174
|
-
};
|
|
175
|
-
const response = await (0, import_axios.default)(axiosRequest);
|
|
176
|
-
return HTTPResponseBuilder.create().status(response.status).statusText(response.statusText).data(response.data).headers(response.headers).request(request).build();
|
|
104
|
+
if (typeof ArrayBuffer !== "undefined" && ArrayBuffer.isView(value)) {
|
|
105
|
+
return true;
|
|
177
106
|
}
|
|
178
|
-
|
|
179
|
-
AxiosClient = __decorateClass([
|
|
180
|
-
HTTPClient.Use()
|
|
181
|
-
], AxiosClient);
|
|
182
|
-
|
|
183
|
-
// src/http.ts
|
|
184
|
-
var import_injection = require("@autometa/injection");
|
|
185
|
-
|
|
186
|
-
// src/default-client-factory.ts
|
|
187
|
-
function defaultClientFactory() {
|
|
188
|
-
const type = defaultClient;
|
|
189
|
-
return new type();
|
|
107
|
+
return false;
|
|
190
108
|
}
|
|
191
109
|
|
|
192
110
|
// src/http-request.ts
|
|
193
|
-
var import_url_join_ts = require("url-join-ts");
|
|
194
111
|
var HTTPRequest = class _HTTPRequest {
|
|
195
112
|
constructor(config) {
|
|
113
|
+
/**
|
|
114
|
+
* Normalised header collection that will be sent with the request.
|
|
115
|
+
*/
|
|
196
116
|
this.headers = {};
|
|
197
117
|
this.params = {};
|
|
198
118
|
this.route = [];
|
|
199
|
-
|
|
119
|
+
this.queryOptions = {};
|
|
120
|
+
if (config) {
|
|
121
|
+
Object.assign(this, config);
|
|
122
|
+
if (config.queryOptions) {
|
|
123
|
+
this.queryOptions = { ...config.queryOptions };
|
|
124
|
+
}
|
|
125
|
+
}
|
|
200
126
|
}
|
|
201
127
|
/**
|
|
202
|
-
*
|
|
203
|
-
* routes, and query parameters.
|
|
204
|
-
*
|
|
205
|
-
* ```ts
|
|
206
|
-
* console.log(request.fullUrl())// https://example.com/foo?bar=baz?array=1,2,3
|
|
207
|
-
* ```
|
|
208
|
-
*
|
|
209
|
-
* Note characters may be converted to escape codes. I.e (space => %20) and (comma => %2C)
|
|
210
|
-
*
|
|
211
|
-
* N.B this getter estimates what the url will be. The actual value
|
|
212
|
-
* might be different depending on your underlying HTTPClient and
|
|
213
|
-
* configuration. For example, query parameters might
|
|
214
|
-
* use different array formats.
|
|
128
|
+
* Full request URL derived from {@link baseUrl}, {@link route} and {@link params}.
|
|
215
129
|
*/
|
|
216
130
|
get fullUrl() {
|
|
217
|
-
return (
|
|
131
|
+
return buildFullUrl(
|
|
132
|
+
this.baseUrl,
|
|
133
|
+
this.route,
|
|
134
|
+
this.params,
|
|
135
|
+
this.queryOptions
|
|
136
|
+
);
|
|
218
137
|
}
|
|
219
138
|
/**
|
|
220
|
-
*
|
|
139
|
+
* Creates a deep copy of an existing request instance.
|
|
221
140
|
*/
|
|
222
141
|
static derive(original) {
|
|
223
142
|
const request = new _HTTPRequest();
|
|
224
143
|
request.headers = { ...original.headers };
|
|
225
|
-
request.params =
|
|
144
|
+
request.params = cloneParams(original.params);
|
|
226
145
|
request.baseUrl = original.baseUrl;
|
|
227
146
|
request.route = [...original.route];
|
|
228
147
|
request.method = original.method;
|
|
229
148
|
request.data = original.data;
|
|
149
|
+
request.queryOptions = { ...original.queryOptions };
|
|
230
150
|
return request;
|
|
231
151
|
}
|
|
232
152
|
};
|
|
233
|
-
var
|
|
234
|
-
var _HTTPRequestBuilder = class _HTTPRequestBuilder {
|
|
153
|
+
var HTTPRequestBuilder = class _HTTPRequestBuilder {
|
|
235
154
|
constructor(request = () => new HTTPRequest()) {
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
if (typeof request === "function") {
|
|
240
|
-
__privateSet(this, _request, request());
|
|
241
|
-
return;
|
|
242
|
-
}
|
|
243
|
-
__privateSet(this, _request, request);
|
|
155
|
+
this.dynamicHeaders = /* @__PURE__ */ new Map();
|
|
156
|
+
this.requestInstance = typeof request === "function" ? request() : request;
|
|
157
|
+
this.queryOptions = { ...this.requestInstance.queryOptions };
|
|
244
158
|
}
|
|
159
|
+
/**
|
|
160
|
+
* Initializes a new builder for the default {@link HTTPRequest} type.
|
|
161
|
+
*/
|
|
245
162
|
static create() {
|
|
246
163
|
return new _HTTPRequestBuilder();
|
|
247
164
|
}
|
|
165
|
+
/**
|
|
166
|
+
* Exposes the underlying request without defensive cloning.
|
|
167
|
+
*/
|
|
248
168
|
get request() {
|
|
249
|
-
return
|
|
169
|
+
return this.requestInstance;
|
|
250
170
|
}
|
|
251
|
-
|
|
252
|
-
|
|
171
|
+
/**
|
|
172
|
+
* Resolves asynchronous header factories into concrete header values on demand.
|
|
173
|
+
*/
|
|
174
|
+
async resolveDynamicHeaders(request = this.requestInstance) {
|
|
175
|
+
for (const [name, factory] of this.dynamicHeaders) {
|
|
176
|
+
if (!factory) {
|
|
177
|
+
continue;
|
|
178
|
+
}
|
|
253
179
|
try {
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
180
|
+
const result = await factory();
|
|
181
|
+
if (result === void 0 || result === null) {
|
|
182
|
+
delete request.headers[name];
|
|
183
|
+
continue;
|
|
184
|
+
}
|
|
185
|
+
request.headers[name] = String(result);
|
|
186
|
+
} catch (error) {
|
|
187
|
+
const cause = error;
|
|
188
|
+
throw new Error(
|
|
189
|
+
`Failed to resolve dynamic header "${name}": ${cause.message}`
|
|
190
|
+
);
|
|
262
191
|
}
|
|
263
192
|
}
|
|
264
193
|
return this;
|
|
265
194
|
}
|
|
195
|
+
/**
|
|
196
|
+
* Sets the root URL (protocol, host and optional base path).
|
|
197
|
+
*/
|
|
266
198
|
url(url) {
|
|
267
|
-
|
|
199
|
+
this.requestInstance.baseUrl = url;
|
|
268
200
|
return this;
|
|
269
201
|
}
|
|
270
|
-
|
|
271
|
-
|
|
202
|
+
/**
|
|
203
|
+
* Appends one or more path segments to the current request route.
|
|
204
|
+
*/
|
|
205
|
+
route(...segments) {
|
|
206
|
+
const mapped = segments.map((segment) => String(segment)).filter((segment) => segment.length > 0);
|
|
207
|
+
this.requestInstance.route.push(...mapped);
|
|
272
208
|
return this;
|
|
273
209
|
}
|
|
210
|
+
/**
|
|
211
|
+
* Adds or removes a query parameter value.
|
|
212
|
+
*/
|
|
274
213
|
param(name, value) {
|
|
214
|
+
if (value === void 0) {
|
|
215
|
+
delete this.requestInstance.params[name];
|
|
216
|
+
return this;
|
|
217
|
+
}
|
|
275
218
|
if (Array.isArray(value)) {
|
|
276
|
-
const
|
|
277
|
-
|
|
219
|
+
const list = value.filter(
|
|
220
|
+
(item) => item !== void 0 && item !== null
|
|
221
|
+
);
|
|
222
|
+
const existing = this.requestInstance.params[name];
|
|
223
|
+
if (Array.isArray(existing)) {
|
|
224
|
+
this.requestInstance.params[name] = [
|
|
225
|
+
...existing,
|
|
226
|
+
...list
|
|
227
|
+
];
|
|
228
|
+
} else {
|
|
229
|
+
this.requestInstance.params[name] = list;
|
|
230
|
+
}
|
|
278
231
|
return this;
|
|
279
232
|
}
|
|
280
|
-
if (
|
|
281
|
-
|
|
233
|
+
if (value && typeof value === "object") {
|
|
234
|
+
const dictEntries = Object.entries(
|
|
235
|
+
value
|
|
236
|
+
).reduce((acc, [key, paramValue]) => {
|
|
237
|
+
if (paramValue !== void 0) {
|
|
238
|
+
acc[key] = paramValue;
|
|
239
|
+
}
|
|
240
|
+
return acc;
|
|
241
|
+
}, {});
|
|
242
|
+
const existing = this.requestInstance.params[name];
|
|
243
|
+
if (isPlainObject(existing)) {
|
|
244
|
+
this.requestInstance.params[name] = {
|
|
245
|
+
...existing,
|
|
246
|
+
...dictEntries
|
|
247
|
+
};
|
|
248
|
+
} else {
|
|
249
|
+
this.requestInstance.params[name] = dictEntries;
|
|
250
|
+
}
|
|
282
251
|
return this;
|
|
283
252
|
}
|
|
284
|
-
|
|
253
|
+
this.requestInstance.params[name] = value;
|
|
285
254
|
return this;
|
|
286
255
|
}
|
|
256
|
+
/**
|
|
257
|
+
* Merges a dictionary of query parameters into the request.
|
|
258
|
+
*/
|
|
287
259
|
params(dict) {
|
|
288
|
-
|
|
260
|
+
for (const [key, value] of Object.entries(dict)) {
|
|
261
|
+
this.param(key, value);
|
|
262
|
+
}
|
|
263
|
+
return this;
|
|
264
|
+
}
|
|
265
|
+
queryFormat(options) {
|
|
266
|
+
if (options.arrayFormat !== void 0) {
|
|
267
|
+
this.queryOptions.arrayFormat = options.arrayFormat;
|
|
268
|
+
}
|
|
269
|
+
if (options.objectFormat !== void 0) {
|
|
270
|
+
this.queryOptions.objectFormat = options.objectFormat;
|
|
271
|
+
}
|
|
272
|
+
if (Object.prototype.hasOwnProperty.call(options, "serializer")) {
|
|
273
|
+
const serializer = options.serializer ?? void 0;
|
|
274
|
+
if (serializer) {
|
|
275
|
+
this.queryOptions.serializer = serializer;
|
|
276
|
+
} else {
|
|
277
|
+
delete this.queryOptions.serializer;
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
this.requestInstance.queryOptions = { ...this.queryOptions };
|
|
289
281
|
return this;
|
|
290
282
|
}
|
|
283
|
+
/**
|
|
284
|
+
* Sets the request body payload. Passing `undefined` removes the body.
|
|
285
|
+
*/
|
|
291
286
|
data(data) {
|
|
292
|
-
|
|
287
|
+
if (data === void 0) {
|
|
288
|
+
delete this.requestInstance.data;
|
|
289
|
+
return this;
|
|
290
|
+
}
|
|
291
|
+
this.requestInstance.data = data;
|
|
293
292
|
return this;
|
|
294
293
|
}
|
|
295
|
-
|
|
294
|
+
/**
|
|
295
|
+
* Sets a single header using direct values or a lazy factory.
|
|
296
|
+
*/
|
|
297
|
+
header(name, value) {
|
|
296
298
|
if (typeof value === "function") {
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
}
|
|
300
|
-
__privateGet(this, _dynamicHeaders).set(name, value);
|
|
299
|
+
this.dynamicHeaders.set(name, value);
|
|
300
|
+
delete this.requestInstance.headers[name];
|
|
301
301
|
return this;
|
|
302
302
|
}
|
|
303
|
-
|
|
304
|
-
|
|
303
|
+
this.dynamicHeaders.delete(name);
|
|
304
|
+
if (value === void 0 || value === null) {
|
|
305
|
+
delete this.requestInstance.headers[name];
|
|
306
|
+
return this;
|
|
307
|
+
}
|
|
308
|
+
if (Array.isArray(value)) {
|
|
309
|
+
const filtered = value.filter(
|
|
310
|
+
(item) => item !== void 0 && item !== null
|
|
311
|
+
);
|
|
312
|
+
this.requestInstance.headers[name] = filtered.map(String).join(",");
|
|
313
|
+
return this;
|
|
305
314
|
}
|
|
306
|
-
|
|
307
|
-
__privateGet(this, _request).headers[name] = val;
|
|
315
|
+
this.requestInstance.headers[name] = String(value);
|
|
308
316
|
return this;
|
|
309
317
|
}
|
|
318
|
+
/**
|
|
319
|
+
* Replaces or merges multiple headers in one call.
|
|
320
|
+
*/
|
|
310
321
|
headers(dict) {
|
|
311
|
-
|
|
322
|
+
for (const [key, value] of Object.entries(dict)) {
|
|
323
|
+
this.header(key, value);
|
|
324
|
+
}
|
|
312
325
|
return this;
|
|
313
326
|
}
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
327
|
+
/**
|
|
328
|
+
* Stores the HTTP verb ensuring consistent casing.
|
|
329
|
+
*/
|
|
317
330
|
method(method) {
|
|
318
|
-
|
|
331
|
+
this.requestInstance.method = method.toUpperCase();
|
|
319
332
|
return this;
|
|
320
333
|
}
|
|
334
|
+
/**
|
|
335
|
+
* Returns a copy-on-write builder pointing at the same request state.
|
|
336
|
+
*/
|
|
321
337
|
derive() {
|
|
322
|
-
|
|
323
|
-
const request = HTTPRequest.derive(__privateGet(this, _request));
|
|
324
|
-
return __privateMethod(_a = new _HTTPRequestBuilder(request), _setDynamicHeaders, setDynamicHeaders_fn).call(_a, __privateGet(this, _dynamicHeaders));
|
|
338
|
+
return this.clone();
|
|
325
339
|
}
|
|
340
|
+
/**
|
|
341
|
+
* Produces a deep copy of the builder and the underlying request.
|
|
342
|
+
*/
|
|
343
|
+
clone() {
|
|
344
|
+
const request = HTTPRequest.derive(this.requestInstance);
|
|
345
|
+
const builder = new _HTTPRequestBuilder(request);
|
|
346
|
+
builder.dynamicHeaders = new Map(this.dynamicHeaders);
|
|
347
|
+
builder.queryOptions = { ...this.queryOptions };
|
|
348
|
+
builder.requestInstance.queryOptions = { ...this.queryOptions };
|
|
349
|
+
return builder;
|
|
350
|
+
}
|
|
351
|
+
/**
|
|
352
|
+
* Returns the current request without resolving header factories.
|
|
353
|
+
*/
|
|
326
354
|
build() {
|
|
327
|
-
|
|
355
|
+
this.requestInstance.queryOptions = { ...this.queryOptions };
|
|
356
|
+
return this.requestInstance;
|
|
328
357
|
}
|
|
358
|
+
/**
|
|
359
|
+
* Resolves lazy headers before returning the request.
|
|
360
|
+
*/
|
|
329
361
|
async buildAsync() {
|
|
330
362
|
await this.resolveDynamicHeaders();
|
|
331
|
-
|
|
363
|
+
this.requestInstance.queryOptions = { ...this.queryOptions };
|
|
364
|
+
return this.requestInstance;
|
|
365
|
+
}
|
|
366
|
+
};
|
|
367
|
+
function cloneParams(params) {
|
|
368
|
+
return Object.entries(params).reduce((acc, [key, value]) => {
|
|
369
|
+
if (Array.isArray(value)) {
|
|
370
|
+
acc[key] = [...value];
|
|
371
|
+
return acc;
|
|
372
|
+
}
|
|
373
|
+
if (isPlainObject(value)) {
|
|
374
|
+
acc[key] = { ...value };
|
|
375
|
+
return acc;
|
|
376
|
+
}
|
|
377
|
+
acc[key] = value;
|
|
378
|
+
return acc;
|
|
379
|
+
}, {});
|
|
380
|
+
}
|
|
381
|
+
function buildFullUrl(baseUrl, route, params, options) {
|
|
382
|
+
const pathSegments = route.map((segment) => String(segment)).filter((segment) => segment.length > 0).map((segment) => segment.replace(/^\/+|\/+$/g, ""));
|
|
383
|
+
const query = buildQueryString(params, options);
|
|
384
|
+
if (baseUrl && /^https?:\/\//i.test(baseUrl)) {
|
|
385
|
+
const url = new URL(baseUrl);
|
|
386
|
+
if (pathSegments.length > 0) {
|
|
387
|
+
const basePath = url.pathname.replace(/^\/+|\/+$/g, "");
|
|
388
|
+
const combined = [basePath, ...pathSegments].filter(Boolean).join("/");
|
|
389
|
+
url.pathname = `/${combined}`;
|
|
390
|
+
}
|
|
391
|
+
url.search = query ? `?${query}` : "";
|
|
392
|
+
return url.toString();
|
|
393
|
+
}
|
|
394
|
+
const trimmedBase = (baseUrl ?? "").replace(/\/+$/g, "");
|
|
395
|
+
const joinedPath = pathSegments.join("/");
|
|
396
|
+
const path = [trimmedBase, joinedPath].filter((segment) => segment && segment.length > 0).join(trimmedBase && joinedPath ? "/" : "");
|
|
397
|
+
if (!path && !query) {
|
|
398
|
+
return "";
|
|
399
|
+
}
|
|
400
|
+
if (!path) {
|
|
401
|
+
return query ? `?${query}` : "";
|
|
402
|
+
}
|
|
403
|
+
return query ? `${path}?${query}` : path;
|
|
404
|
+
}
|
|
405
|
+
function buildQueryString(params, options) {
|
|
406
|
+
if (!params || Object.keys(params).length === 0) {
|
|
407
|
+
return "";
|
|
408
|
+
}
|
|
409
|
+
const serializer = options?.serializer ?? void 0;
|
|
410
|
+
if (serializer) {
|
|
411
|
+
return serializer(params);
|
|
412
|
+
}
|
|
413
|
+
const arrayFormat = options?.arrayFormat ?? "repeat";
|
|
414
|
+
const objectFormat = options?.objectFormat ?? "brackets";
|
|
415
|
+
const search = new URLSearchParams();
|
|
416
|
+
const append = (key, value) => {
|
|
417
|
+
if (value === void 0 || value === null) {
|
|
418
|
+
return;
|
|
419
|
+
}
|
|
420
|
+
search.append(key, String(value));
|
|
421
|
+
};
|
|
422
|
+
const encode = (key, value) => {
|
|
423
|
+
if (value === void 0 || value === null) {
|
|
424
|
+
return;
|
|
425
|
+
}
|
|
426
|
+
if (Array.isArray(value)) {
|
|
427
|
+
const filtered = value.filter(
|
|
428
|
+
(item) => item !== void 0 && item !== null
|
|
429
|
+
);
|
|
430
|
+
if (filtered.length === 0) {
|
|
431
|
+
return;
|
|
432
|
+
}
|
|
433
|
+
switch (arrayFormat) {
|
|
434
|
+
case "json": {
|
|
435
|
+
append(key, JSON.stringify(filtered));
|
|
436
|
+
return;
|
|
437
|
+
}
|
|
438
|
+
case "comma": {
|
|
439
|
+
const joined = filtered.map((item) => String(item)).join(",");
|
|
440
|
+
append(key, joined);
|
|
441
|
+
return;
|
|
442
|
+
}
|
|
443
|
+
case "indices": {
|
|
444
|
+
filtered.forEach((item, index) => {
|
|
445
|
+
encode(`${key}[${index}]`, item);
|
|
446
|
+
});
|
|
447
|
+
return;
|
|
448
|
+
}
|
|
449
|
+
case "brackets": {
|
|
450
|
+
filtered.forEach((item) => {
|
|
451
|
+
encode(`${key}[]`, item);
|
|
452
|
+
});
|
|
453
|
+
return;
|
|
454
|
+
}
|
|
455
|
+
default: {
|
|
456
|
+
filtered.forEach((item) => {
|
|
457
|
+
encode(key, item);
|
|
458
|
+
});
|
|
459
|
+
return;
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
if (isPlainObject(value)) {
|
|
464
|
+
if (objectFormat === "json") {
|
|
465
|
+
append(key, JSON.stringify(value));
|
|
466
|
+
return;
|
|
467
|
+
}
|
|
468
|
+
for (const [childKey, childValue] of Object.entries(
|
|
469
|
+
value
|
|
470
|
+
)) {
|
|
471
|
+
const nextKey = objectFormat === "dot" ? `${key}.${childKey}` : `${key}[${childKey}]`;
|
|
472
|
+
encode(nextKey, childValue);
|
|
473
|
+
}
|
|
474
|
+
return;
|
|
475
|
+
}
|
|
476
|
+
append(key, value);
|
|
477
|
+
};
|
|
478
|
+
for (const [key, value] of Object.entries(params)) {
|
|
479
|
+
encode(key, value);
|
|
480
|
+
}
|
|
481
|
+
return search.toString();
|
|
482
|
+
}
|
|
483
|
+
function isPlainObject(value) {
|
|
484
|
+
if (value === null || typeof value !== "object") {
|
|
485
|
+
return false;
|
|
486
|
+
}
|
|
487
|
+
const proto = Object.getPrototypeOf(value);
|
|
488
|
+
return proto === Object.prototype || proto === null;
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
// src/http-response.ts
|
|
492
|
+
var HTTPResponse = class _HTTPResponse {
|
|
493
|
+
constructor() {
|
|
494
|
+
/**
|
|
495
|
+
* Human-readable text accompanying {@link status}.
|
|
496
|
+
*/
|
|
497
|
+
this.statusText = "";
|
|
498
|
+
/**
|
|
499
|
+
* Normalised response headers keyed by lowercase header names.
|
|
500
|
+
*/
|
|
501
|
+
this.headers = {};
|
|
502
|
+
}
|
|
503
|
+
/**
|
|
504
|
+
* Creates a shallow clone from an existing response instance.
|
|
505
|
+
*/
|
|
506
|
+
static fromRaw(response) {
|
|
507
|
+
const next = new _HTTPResponse();
|
|
508
|
+
next.status = response.status;
|
|
509
|
+
next.statusText = response.statusText;
|
|
510
|
+
next.data = response.data;
|
|
511
|
+
next.headers = { ...response.headers };
|
|
512
|
+
next.request = response.request;
|
|
513
|
+
return next;
|
|
514
|
+
}
|
|
515
|
+
/**
|
|
516
|
+
* Returns a new response with identical metadata but a transformed payload.
|
|
517
|
+
*
|
|
518
|
+
* @example
|
|
519
|
+
* const productResponse = await http.route("products", 1).get<Product>()
|
|
520
|
+
* const simplified = productResponse.mapData((product) => product.name)
|
|
521
|
+
*/
|
|
522
|
+
mapData(transform) {
|
|
523
|
+
const nextValue = typeof transform === "function" ? transform(this.data) : transform;
|
|
524
|
+
return new HTTPResponseBuilder().status(this.status).statusText(this.statusText).headers(this.headers).request(this.request).data(nextValue).build();
|
|
332
525
|
}
|
|
333
526
|
};
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
527
|
+
var HTTPResponseBuilder = class _HTTPResponseBuilder {
|
|
528
|
+
constructor() {
|
|
529
|
+
this.response = new HTTPResponse();
|
|
530
|
+
}
|
|
531
|
+
/**
|
|
532
|
+
* Creates a new empty builder.
|
|
533
|
+
*/
|
|
534
|
+
static create() {
|
|
535
|
+
return new _HTTPResponseBuilder();
|
|
536
|
+
}
|
|
537
|
+
/**
|
|
538
|
+
* Produces a new builder seeded with the current response state.
|
|
539
|
+
*/
|
|
540
|
+
derive() {
|
|
541
|
+
return _HTTPResponseBuilder.create().status(this.response.status).statusText(this.response.statusText).headers({ ...this.response.headers }).request(this.response.request).data(this.response.data);
|
|
542
|
+
}
|
|
543
|
+
/**
|
|
544
|
+
* Sets the response status code.
|
|
545
|
+
*/
|
|
546
|
+
status(code) {
|
|
547
|
+
this.response.status = code;
|
|
548
|
+
return this;
|
|
549
|
+
}
|
|
550
|
+
/**
|
|
551
|
+
* Sets the textual status message.
|
|
552
|
+
*/
|
|
553
|
+
statusText(text) {
|
|
554
|
+
this.response.statusText = text;
|
|
555
|
+
return this;
|
|
556
|
+
}
|
|
557
|
+
/**
|
|
558
|
+
* Attaches the response payload.
|
|
559
|
+
*/
|
|
560
|
+
data(data) {
|
|
561
|
+
this.response.data = data;
|
|
562
|
+
return this;
|
|
563
|
+
}
|
|
564
|
+
/**
|
|
565
|
+
* Replaces the entire headers collection.
|
|
566
|
+
*/
|
|
567
|
+
headers(dict) {
|
|
568
|
+
this.response.headers = { ...dict };
|
|
569
|
+
return this;
|
|
570
|
+
}
|
|
571
|
+
/**
|
|
572
|
+
* Sets or overrides a single response header.
|
|
573
|
+
*/
|
|
574
|
+
header(name, value) {
|
|
575
|
+
this.response.headers[name] = value;
|
|
576
|
+
return this;
|
|
577
|
+
}
|
|
578
|
+
/**
|
|
579
|
+
* References the originating request.
|
|
580
|
+
*/
|
|
581
|
+
request(request) {
|
|
582
|
+
this.response.request = request;
|
|
583
|
+
return this;
|
|
584
|
+
}
|
|
585
|
+
/**
|
|
586
|
+
* Builds the immutable {@link HTTPResponse} instance.
|
|
587
|
+
*/
|
|
588
|
+
build() {
|
|
589
|
+
return this.response;
|
|
590
|
+
}
|
|
340
591
|
};
|
|
341
|
-
var HTTPRequestBuilder = _HTTPRequestBuilder;
|
|
342
592
|
|
|
343
593
|
// src/schema.map.ts
|
|
344
|
-
var
|
|
345
|
-
var _SchemaMap = class _SchemaMap {
|
|
594
|
+
var SchemaMap = class _SchemaMap {
|
|
346
595
|
constructor(map) {
|
|
347
|
-
__privateAdd(this, _map, void 0);
|
|
348
596
|
if (map instanceof _SchemaMap) {
|
|
349
|
-
|
|
597
|
+
this.map = new Map(map.map);
|
|
350
598
|
return;
|
|
351
599
|
}
|
|
352
|
-
|
|
600
|
+
this.map = map ? new Map(map) : /* @__PURE__ */ new Map();
|
|
353
601
|
}
|
|
354
602
|
derive() {
|
|
355
|
-
return new _SchemaMap(
|
|
603
|
+
return new _SchemaMap(this.map);
|
|
356
604
|
}
|
|
357
605
|
registerStatus(parser, ...codes) {
|
|
358
606
|
codes.forEach((code) => {
|
|
359
|
-
|
|
360
|
-
const msg = `Status code ${code} is already registered with a parser`;
|
|
361
|
-
throw new Error(msg);
|
|
362
|
-
}
|
|
363
|
-
__privateGet(this, _map).set(code, parser);
|
|
607
|
+
this.map.set(code, parser);
|
|
364
608
|
});
|
|
365
609
|
}
|
|
366
610
|
registerRange(parser, from, to) {
|
|
367
|
-
for (let
|
|
368
|
-
|
|
369
|
-
throw new Error(`Status code ${i} is already registered with a parser`);
|
|
370
|
-
}
|
|
371
|
-
__privateGet(this, _map).set(i, parser);
|
|
611
|
+
for (let code = from; code <= to; code++) {
|
|
612
|
+
this.map.set(code, parser);
|
|
372
613
|
}
|
|
373
614
|
}
|
|
374
615
|
validate(status, data, requireSchema) {
|
|
375
616
|
const parser = this.getParser(status, requireSchema);
|
|
617
|
+
if (!parser) {
|
|
618
|
+
return data;
|
|
619
|
+
}
|
|
620
|
+
if (typeof parser === "function") {
|
|
621
|
+
return parser(data);
|
|
622
|
+
}
|
|
376
623
|
if ("parse" in parser) {
|
|
377
624
|
return parser.parse(data);
|
|
378
625
|
}
|
|
379
626
|
if ("validate" in parser) {
|
|
380
627
|
return parser.validate(data);
|
|
381
628
|
}
|
|
382
|
-
|
|
383
|
-
return parser(data);
|
|
384
|
-
} catch (e) {
|
|
385
|
-
const msg = `Failed to schema parse response data for status code ${status} with data:
|
|
386
|
-
|
|
387
|
-
${JSON.stringify(data, null, 2)}}`;
|
|
388
|
-
throw new Error(msg);
|
|
389
|
-
}
|
|
629
|
+
return data;
|
|
390
630
|
}
|
|
391
631
|
getParser(status, requireSchema) {
|
|
392
|
-
const parser =
|
|
632
|
+
const parser = this.map.get(status);
|
|
393
633
|
if (!parser && requireSchema) {
|
|
394
|
-
|
|
395
|
-
|
|
634
|
+
throw new Error(
|
|
635
|
+
`No schema parser registered for status code ${status} while requireSchema is true.`
|
|
636
|
+
);
|
|
396
637
|
}
|
|
397
|
-
|
|
398
|
-
return parser;
|
|
399
|
-
}
|
|
400
|
-
return (data) => data;
|
|
638
|
+
return parser ?? null;
|
|
401
639
|
}
|
|
402
640
|
toObject() {
|
|
403
|
-
return Object.fromEntries(
|
|
641
|
+
return Object.fromEntries(this.map);
|
|
404
642
|
}
|
|
405
643
|
};
|
|
406
|
-
_map = new WeakMap();
|
|
407
|
-
var SchemaMap = _SchemaMap;
|
|
408
644
|
|
|
409
645
|
// src/request-meta.config.ts
|
|
410
646
|
var MetaConfig = class {
|
|
411
|
-
constructor() {
|
|
647
|
+
constructor(init) {
|
|
648
|
+
this.schemas = new SchemaMap();
|
|
649
|
+
this.requireSchema = false;
|
|
650
|
+
this.allowPlainText = false;
|
|
412
651
|
this.onSend = [];
|
|
413
652
|
this.onReceive = [];
|
|
653
|
+
this.throwOnServerError = false;
|
|
414
654
|
this.options = {};
|
|
655
|
+
this.streamResponse = false;
|
|
656
|
+
this.timeoutMs = void 0;
|
|
657
|
+
Object.assign(this, init);
|
|
415
658
|
}
|
|
416
659
|
};
|
|
417
|
-
var
|
|
418
|
-
var _MetaConfigBuilder = class _MetaConfigBuilder {
|
|
660
|
+
var MetaConfigBuilder = class _MetaConfigBuilder {
|
|
419
661
|
constructor() {
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
662
|
+
this.schemaMapValue = new SchemaMap();
|
|
663
|
+
this.requireSchemaValue = false;
|
|
664
|
+
this.allowPlainTextValue = false;
|
|
665
|
+
this.onBeforeSendHooks = [];
|
|
666
|
+
this.onAfterReceiveHooks = [];
|
|
667
|
+
this.throwOnServerErrorValue = false;
|
|
668
|
+
this.optionsValue = {};
|
|
669
|
+
this.streamResponseValue = false;
|
|
670
|
+
}
|
|
671
|
+
merge(builder) {
|
|
672
|
+
this.schemaMapValue = builder.schemaMapValue.derive();
|
|
673
|
+
this.requireSchemaValue = builder.requireSchemaValue;
|
|
674
|
+
this.allowPlainTextValue = builder.allowPlainTextValue;
|
|
675
|
+
this.onBeforeSendHooks = [...builder.onBeforeSendHooks];
|
|
676
|
+
this.onAfterReceiveHooks = [...builder.onAfterReceiveHooks];
|
|
677
|
+
this.throwOnServerErrorValue = builder.throwOnServerErrorValue;
|
|
678
|
+
this.optionsValue = { ...builder.optionsValue };
|
|
679
|
+
this.retryOptionsValue = builder.retryOptionsValue ? { ...builder.retryOptionsValue } : void 0;
|
|
680
|
+
this.streamResponseValue = builder.streamResponseValue;
|
|
681
|
+
this.timeoutMsValue = builder.timeoutMsValue;
|
|
432
682
|
return this;
|
|
433
683
|
}
|
|
434
684
|
schemaMap(map) {
|
|
435
|
-
|
|
685
|
+
this.schemaMapValue = map;
|
|
436
686
|
return this;
|
|
437
687
|
}
|
|
438
688
|
schema(parser, ...args) {
|
|
439
689
|
args.forEach((arg) => {
|
|
440
690
|
if (typeof arg === "number") {
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
691
|
+
this.schemaMapValue.registerStatus(parser, arg);
|
|
692
|
+
return;
|
|
693
|
+
}
|
|
694
|
+
if (Array.isArray(arg)) {
|
|
695
|
+
this.schemaMapValue.registerStatus(parser, ...arg);
|
|
696
|
+
return;
|
|
446
697
|
}
|
|
698
|
+
this.schemaMapValue.registerRange(parser, arg.from, arg.to);
|
|
447
699
|
});
|
|
448
700
|
return this;
|
|
449
701
|
}
|
|
450
702
|
requireSchema(value) {
|
|
451
|
-
|
|
703
|
+
this.requireSchemaValue = value;
|
|
452
704
|
return this;
|
|
453
705
|
}
|
|
454
706
|
allowPlainText(value) {
|
|
455
|
-
|
|
707
|
+
this.allowPlainTextValue = value;
|
|
456
708
|
return this;
|
|
457
709
|
}
|
|
458
710
|
onBeforeSend(description, hook) {
|
|
459
|
-
|
|
711
|
+
this.onBeforeSendHooks.push([description, hook]);
|
|
712
|
+
return this;
|
|
713
|
+
}
|
|
714
|
+
onReceiveResponse(description, hook) {
|
|
715
|
+
this.onAfterReceiveHooks.push([description, hook]);
|
|
460
716
|
return this;
|
|
461
717
|
}
|
|
462
718
|
throwOnServerError(value) {
|
|
463
|
-
|
|
719
|
+
this.throwOnServerErrorValue = value;
|
|
464
720
|
return this;
|
|
465
721
|
}
|
|
466
|
-
|
|
467
|
-
|
|
722
|
+
options(options) {
|
|
723
|
+
this.optionsValue = mergeOptions(this.optionsValue, options);
|
|
468
724
|
return this;
|
|
469
725
|
}
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
config.requireSchema = __privateGet(this, _requireSchema);
|
|
474
|
-
config.allowPlainText = __privateGet(this, _allowPlainText);
|
|
475
|
-
config.onSend = __privateGet(this, _onBeforeSend);
|
|
476
|
-
config.onReceive = __privateGet(this, _onAfterSend);
|
|
477
|
-
config.options = __privateGet(this, _options);
|
|
478
|
-
config.throwOnServerError = __privateGet(this, _throwOnServerError);
|
|
479
|
-
return config;
|
|
726
|
+
retry(options) {
|
|
727
|
+
this.retryOptionsValue = options ? { ...options } : void 0;
|
|
728
|
+
return this;
|
|
480
729
|
}
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
return
|
|
730
|
+
streamResponse(value) {
|
|
731
|
+
this.streamResponseValue = value;
|
|
732
|
+
return this;
|
|
484
733
|
}
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
_options = new WeakMap();
|
|
493
|
-
_setOnSend = new WeakSet();
|
|
494
|
-
setOnSend_fn = function(hooks) {
|
|
495
|
-
__privateSet(this, _onBeforeSend, [...hooks]);
|
|
496
|
-
return this;
|
|
497
|
-
};
|
|
498
|
-
_setOnReceive = new WeakSet();
|
|
499
|
-
setOnReceive_fn = function(hooks) {
|
|
500
|
-
__privateSet(this, _onAfterSend, [...hooks]);
|
|
501
|
-
return this;
|
|
502
|
-
};
|
|
503
|
-
var MetaConfigBuilder = _MetaConfigBuilder;
|
|
504
|
-
|
|
505
|
-
// src/transform-response.ts
|
|
506
|
-
var import_errors = require("@autometa/errors");
|
|
507
|
-
var import_assert_is_json = __toESM(require("@stdlib/assert-is-json"), 1);
|
|
508
|
-
var import_cli_highlight = require("cli-highlight");
|
|
509
|
-
function transformResponse(allowPlainText, data) {
|
|
510
|
-
if (data === null) {
|
|
511
|
-
return null;
|
|
734
|
+
timeout(duration) {
|
|
735
|
+
if (typeof duration === "number" && duration > 0) {
|
|
736
|
+
this.timeoutMsValue = duration;
|
|
737
|
+
} else {
|
|
738
|
+
this.timeoutMsValue = void 0;
|
|
739
|
+
}
|
|
740
|
+
return this;
|
|
512
741
|
}
|
|
513
|
-
|
|
514
|
-
|
|
742
|
+
build() {
|
|
743
|
+
const retry = this.retryOptionsValue ? { ...this.retryOptionsValue } : void 0;
|
|
744
|
+
return new MetaConfig({
|
|
745
|
+
schemas: this.schemaMapValue.derive(),
|
|
746
|
+
requireSchema: this.requireSchemaValue,
|
|
747
|
+
allowPlainText: this.allowPlainTextValue,
|
|
748
|
+
onSend: [...this.onBeforeSendHooks],
|
|
749
|
+
onReceive: [...this.onAfterReceiveHooks],
|
|
750
|
+
options: { ...this.optionsValue },
|
|
751
|
+
throwOnServerError: this.throwOnServerErrorValue,
|
|
752
|
+
...retry ? { retry } : {},
|
|
753
|
+
streamResponse: this.streamResponseValue,
|
|
754
|
+
timeoutMs: this.timeoutMsValue
|
|
755
|
+
});
|
|
515
756
|
}
|
|
516
|
-
|
|
517
|
-
return
|
|
757
|
+
derive() {
|
|
758
|
+
return new _MetaConfigBuilder().schemaMap(this.schemaMapValue.derive()).requireSchema(this.requireSchemaValue).allowPlainText(this.allowPlainTextValue).throwOnServerError(this.throwOnServerErrorValue).options(this.optionsValue).retry(this.retryOptionsValue ?? null).streamResponse(this.streamResponseValue).timeout(this.timeoutMsValue).setOnBeforeSend(this.onBeforeSendHooks).setOnAfterReceive(this.onAfterReceiveHooks);
|
|
518
759
|
}
|
|
519
|
-
|
|
520
|
-
|
|
760
|
+
setOnBeforeSend(hooks) {
|
|
761
|
+
this.onBeforeSendHooks = [...hooks];
|
|
762
|
+
return this;
|
|
521
763
|
}
|
|
522
|
-
|
|
523
|
-
|
|
764
|
+
setOnAfterReceive(hooks) {
|
|
765
|
+
this.onAfterReceiveHooks = [...hooks];
|
|
766
|
+
return this;
|
|
524
767
|
}
|
|
525
|
-
|
|
526
|
-
|
|
768
|
+
};
|
|
769
|
+
function mergeOptions(target, updates) {
|
|
770
|
+
const next = { ...target };
|
|
771
|
+
for (const [key, value] of Object.entries(updates)) {
|
|
772
|
+
if (value === void 0) {
|
|
773
|
+
delete next[key];
|
|
774
|
+
} else {
|
|
775
|
+
next[key] = value;
|
|
776
|
+
}
|
|
777
|
+
}
|
|
778
|
+
return next;
|
|
779
|
+
}
|
|
780
|
+
function transformResponse(allowPlainText, data) {
|
|
781
|
+
if (data === null || data === void 0) {
|
|
782
|
+
return data;
|
|
783
|
+
}
|
|
784
|
+
if (typeof data === "string") {
|
|
785
|
+
const trimmed = data.trim();
|
|
786
|
+
if (trimmed.length === 0) {
|
|
787
|
+
return void 0;
|
|
788
|
+
}
|
|
789
|
+
if (trimmed.toLowerCase() === "undefined") {
|
|
790
|
+
return void 0;
|
|
791
|
+
}
|
|
792
|
+
}
|
|
793
|
+
const primitive = normalizePrimitive(data);
|
|
794
|
+
if (primitive !== void 0) {
|
|
795
|
+
return primitive;
|
|
527
796
|
}
|
|
528
797
|
if (typeof data === "object") {
|
|
529
798
|
return data;
|
|
530
799
|
}
|
|
531
800
|
if (allowPlainText) {
|
|
532
|
-
return data;
|
|
801
|
+
return String(data);
|
|
533
802
|
}
|
|
534
|
-
const
|
|
535
|
-
const response = (0, import_cli_highlight.highlight)(dataStr, { language: "html" });
|
|
803
|
+
const rendered = typeof data === "string" ? data : String(data);
|
|
536
804
|
const message = [
|
|
537
|
-
|
|
538
|
-
|
|
805
|
+
"Could not parse response as JSON and plain text responses are disabled.",
|
|
806
|
+
"Call 'allowPlainText(true)' or 'sharedAllowPlainText(true)' to permit plain text responses.",
|
|
539
807
|
"",
|
|
540
|
-
|
|
541
|
-
];
|
|
542
|
-
throw new
|
|
808
|
+
highlight(rendered, { language: "html" })
|
|
809
|
+
].join("\n");
|
|
810
|
+
throw new AutomationError(message);
|
|
811
|
+
}
|
|
812
|
+
function normalizePrimitive(data) {
|
|
813
|
+
if (typeof data === "string") {
|
|
814
|
+
const parsed = tryParseJson(data);
|
|
815
|
+
if (parsed !== void 0) {
|
|
816
|
+
return parsed;
|
|
817
|
+
}
|
|
818
|
+
const lowered = data.toLowerCase();
|
|
819
|
+
if (lowered === "true" || lowered === "false") {
|
|
820
|
+
return lowered === "true";
|
|
821
|
+
}
|
|
822
|
+
if (/^(?:\d+|\d*\.\d+)$/.test(data)) {
|
|
823
|
+
return Number(data);
|
|
824
|
+
}
|
|
825
|
+
return void 0;
|
|
826
|
+
}
|
|
827
|
+
if (isArrayBufferLike(data)) {
|
|
828
|
+
const text = bufferToString(data);
|
|
829
|
+
return normalizePrimitive(text) ?? text;
|
|
830
|
+
}
|
|
831
|
+
if (typeof data === "boolean" || typeof data === "number") {
|
|
832
|
+
return data;
|
|
833
|
+
}
|
|
834
|
+
return void 0;
|
|
835
|
+
}
|
|
836
|
+
function tryParseJson(value) {
|
|
837
|
+
try {
|
|
838
|
+
return JSON.parse(value);
|
|
839
|
+
} catch {
|
|
840
|
+
return void 0;
|
|
841
|
+
}
|
|
842
|
+
}
|
|
843
|
+
function isArrayBufferLike(value) {
|
|
844
|
+
return typeof ArrayBuffer !== "undefined" && value instanceof ArrayBuffer || typeof ArrayBuffer !== "undefined" && ArrayBuffer.isView(value);
|
|
845
|
+
}
|
|
846
|
+
function bufferToString(value) {
|
|
847
|
+
const view = value instanceof ArrayBuffer ? new Uint8Array(value) : new Uint8Array(value.buffer, value.byteOffset, value.byteLength);
|
|
848
|
+
if (typeof TextDecoder !== "undefined") {
|
|
849
|
+
return new TextDecoder().decode(view);
|
|
850
|
+
}
|
|
851
|
+
let output = "";
|
|
852
|
+
for (let i = 0; i < view.length; i++) {
|
|
853
|
+
const code = view[i];
|
|
854
|
+
if (code === void 0) {
|
|
855
|
+
continue;
|
|
856
|
+
}
|
|
857
|
+
output += String.fromCharCode(code);
|
|
858
|
+
}
|
|
859
|
+
return output;
|
|
543
860
|
}
|
|
544
861
|
|
|
545
862
|
// src/http.ts
|
|
546
|
-
var
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
863
|
+
var HTTPError = class extends AutomationError {
|
|
864
|
+
constructor(message, request, response, cause) {
|
|
865
|
+
super(message, {
|
|
866
|
+
cause: cause instanceof Error ? cause : void 0
|
|
867
|
+
});
|
|
868
|
+
this.request = request;
|
|
869
|
+
this.response = response;
|
|
870
|
+
this.originalError = cause;
|
|
871
|
+
this.name = this.constructor.name;
|
|
872
|
+
Object.setPrototypeOf(this, new.target.prototype);
|
|
873
|
+
}
|
|
874
|
+
};
|
|
875
|
+
var HTTPTransportError = class extends HTTPError {
|
|
876
|
+
constructor(request, cause) {
|
|
877
|
+
super("Failed to execute HTTP request", request, void 0, cause);
|
|
878
|
+
this.name = "HTTPTransportError";
|
|
879
|
+
}
|
|
880
|
+
};
|
|
881
|
+
var HTTPSchemaValidationError = class extends HTTPError {
|
|
882
|
+
constructor(request, response, cause) {
|
|
883
|
+
super("Response schema validation failed", request, response, cause);
|
|
884
|
+
this.name = "HTTPSchemaValidationError";
|
|
885
|
+
}
|
|
886
|
+
};
|
|
887
|
+
var HTTPServerError = class extends HTTPError {
|
|
888
|
+
constructor(request, response) {
|
|
889
|
+
super(`Server responded with status ${response.status}`, request, response);
|
|
890
|
+
this.name = "HTTPServerError";
|
|
891
|
+
}
|
|
892
|
+
};
|
|
893
|
+
var _HTTP = class _HTTP {
|
|
894
|
+
constructor(transport, builder, meta, sharedPlugins, scopedPlugins) {
|
|
895
|
+
this.transport = transport;
|
|
896
|
+
this.builder = builder;
|
|
897
|
+
this.meta = meta;
|
|
898
|
+
this.sharedPlugins = sharedPlugins;
|
|
899
|
+
this.scopedPlugins = scopedPlugins;
|
|
900
|
+
}
|
|
901
|
+
/**
|
|
902
|
+
* Factory helper that prepares an {@link HTTP} instance with shared state.
|
|
903
|
+
*/
|
|
904
|
+
static create(options = {}) {
|
|
905
|
+
const transport = options.transport ?? createFetchTransport();
|
|
906
|
+
const plugins = [..._HTTP.sharedPlugins, ...options.plugins ?? []];
|
|
907
|
+
return new _HTTP(
|
|
908
|
+
transport,
|
|
909
|
+
HTTPRequestBuilder.create(),
|
|
910
|
+
new MetaConfigBuilder(),
|
|
911
|
+
plugins,
|
|
912
|
+
[]
|
|
913
|
+
);
|
|
914
|
+
}
|
|
915
|
+
/**
|
|
916
|
+
* Registers a plugin applied to every client created via {@link HTTP.create}.
|
|
917
|
+
*/
|
|
918
|
+
static registerSharedPlugin(plugin) {
|
|
919
|
+
this.sharedPlugins = [...this.sharedPlugins, plugin];
|
|
920
|
+
}
|
|
921
|
+
/**
|
|
922
|
+
* Replaces the shared plugin registry used by {@link HTTP.create}.
|
|
923
|
+
*/
|
|
924
|
+
static setSharedPlugins(plugins) {
|
|
925
|
+
this.sharedPlugins = [...plugins];
|
|
926
|
+
}
|
|
927
|
+
/**
|
|
928
|
+
* Returns a copy of the currently registered shared plugins.
|
|
929
|
+
*/
|
|
930
|
+
static getSharedPlugins() {
|
|
931
|
+
return [...this.sharedPlugins];
|
|
932
|
+
}
|
|
933
|
+
/**
|
|
934
|
+
* Registers a plugin that runs for every request executed by this instance and its clones.
|
|
935
|
+
*/
|
|
936
|
+
use(plugin) {
|
|
937
|
+
this.sharedPlugins.push(plugin);
|
|
938
|
+
return this;
|
|
939
|
+
}
|
|
940
|
+
/**
|
|
941
|
+
* Returns a scoped clone with an additional plugin applied only to that clone.
|
|
942
|
+
*/
|
|
943
|
+
plugin(plugin) {
|
|
944
|
+
return this.derive(({ plugins }) => {
|
|
945
|
+
plugins.push(plugin);
|
|
946
|
+
});
|
|
947
|
+
}
|
|
948
|
+
/**
|
|
949
|
+
* Mutates the current instance to use a different transport implementation.
|
|
950
|
+
*/
|
|
951
|
+
useTransport(transport) {
|
|
952
|
+
this.transport = transport;
|
|
953
|
+
return this;
|
|
954
|
+
}
|
|
955
|
+
/**
|
|
956
|
+
* Produces a new client with an alternate transport without changing the original instance.
|
|
957
|
+
*/
|
|
958
|
+
withTransport(transport) {
|
|
959
|
+
return this.derive(void 0, { transport });
|
|
960
|
+
}
|
|
961
|
+
/**
|
|
962
|
+
* Sets the base URL shared by subsequent requests.
|
|
578
963
|
*/
|
|
579
964
|
url(url) {
|
|
580
|
-
|
|
965
|
+
this.builder.url(url);
|
|
581
966
|
return this;
|
|
582
967
|
}
|
|
968
|
+
/**
|
|
969
|
+
* Applies additional transport specific options to every request executed by this instance.
|
|
970
|
+
*/
|
|
583
971
|
sharedOptions(options) {
|
|
584
|
-
|
|
972
|
+
this.meta.options(options);
|
|
585
973
|
return this;
|
|
586
974
|
}
|
|
587
975
|
/**
|
|
588
|
-
*
|
|
589
|
-
* matching any response status code. If set to false, a schema will still be used for validation
|
|
590
|
-
* if defined, or the unadulterated original body will be returned if no schema matches.
|
|
591
|
-
*
|
|
592
|
-
* @param required Whether or not a schema is required for all responses.
|
|
593
|
-
* @returns This instance of HTTP.
|
|
976
|
+
* Returns a derived client with extra transport options applied only to that clone.
|
|
594
977
|
*/
|
|
595
|
-
|
|
596
|
-
|
|
978
|
+
withOptions(options) {
|
|
979
|
+
return this.derive(({ meta }) => {
|
|
980
|
+
meta.options(options);
|
|
981
|
+
});
|
|
982
|
+
}
|
|
983
|
+
/**
|
|
984
|
+
* Registers an {@link AbortSignal} that will be forwarded to every request issued by this instance.
|
|
985
|
+
*/
|
|
986
|
+
sharedAbortSignal(signal) {
|
|
987
|
+
this.meta.options({ signal: signal ?? void 0 });
|
|
597
988
|
return this;
|
|
598
989
|
}
|
|
599
990
|
/**
|
|
600
|
-
*
|
|
601
|
-
* responses. If set to false, plain text responses will throw an serialization error.
|
|
602
|
-
*
|
|
603
|
-
* Useful when an endpoint returns a HTML or plain text response. If the plain text
|
|
604
|
-
* is the value of `true` or `false`, or a number, it will be parsed into the
|
|
605
|
-
* appropriate type.
|
|
606
|
-
*
|
|
607
|
-
* This method is a shared chain method, and will return the same instance of HTTP.
|
|
608
|
-
*
|
|
609
|
-
* @param allow Whether or not plain text responses are allowed.
|
|
610
|
-
* @returns This instance of HTTP.
|
|
991
|
+
* Returns a derived client configured with the provided {@link AbortSignal}.
|
|
611
992
|
*/
|
|
612
|
-
|
|
613
|
-
|
|
993
|
+
abortSignal(signal) {
|
|
994
|
+
return this.derive(({ meta }) => {
|
|
995
|
+
meta.options({ signal: signal ?? void 0 });
|
|
996
|
+
});
|
|
997
|
+
}
|
|
998
|
+
/**
|
|
999
|
+
* Configures automatic retries for this instance and all derived clients.
|
|
1000
|
+
*/
|
|
1001
|
+
sharedRetry(options) {
|
|
1002
|
+
this.meta.retry(options);
|
|
614
1003
|
return this;
|
|
615
1004
|
}
|
|
616
1005
|
/**
|
|
617
|
-
*
|
|
618
|
-
* responses. If set to false, plain text responses will throw an serialization error.
|
|
619
|
-
*
|
|
620
|
-
* Useful when an endpoint returns a HTML or plain text response. If the plain text
|
|
621
|
-
* is the value of `true` or `false`, or a number, it will be parsed into the
|
|
622
|
-
* appropriate type.
|
|
623
|
-
*
|
|
624
|
-
* This method is a request chain method, and will return a new instance of HTTP.
|
|
625
|
-
*
|
|
626
|
-
* @param allow Whether or not plain text responses are allowed.
|
|
627
|
-
* @returns A new child instance of HTTP derived from this one.
|
|
1006
|
+
* Returns a derived client with custom retry behaviour.
|
|
628
1007
|
*/
|
|
629
|
-
|
|
630
|
-
return
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
__privateGet(this, _metaConfig).derive().allowPlainText(allow)
|
|
634
|
-
);
|
|
1008
|
+
retry(options) {
|
|
1009
|
+
return this.derive(({ meta }) => {
|
|
1010
|
+
meta.retry(options);
|
|
1011
|
+
});
|
|
635
1012
|
}
|
|
636
1013
|
/**
|
|
637
|
-
*
|
|
638
|
-
* to this method will append the route to the existing route, such as `/product/1`.
|
|
639
|
-
*
|
|
640
|
-
* Numbers will be converted to strings automatically. Routes can be defined one
|
|
641
|
-
* at a time or as a spread argument.
|
|
642
|
-
*
|
|
643
|
-
* ```ts
|
|
644
|
-
* constructor(http: HTTP) {
|
|
645
|
-
* super(http);
|
|
646
|
-
* this.http.sharedRoute("user", id).get();
|
|
647
|
-
* }
|
|
648
|
-
*
|
|
649
|
-
* // or
|
|
650
|
-
*
|
|
651
|
-
* constructor(http: HTTP) {
|
|
652
|
-
* super(http);
|
|
653
|
-
* this.http
|
|
654
|
-
* .sharedRoute("user")
|
|
655
|
-
* .sharedRoute(id)
|
|
656
|
-
* .get();
|
|
657
|
-
* }
|
|
658
|
-
* ```
|
|
659
|
-
*
|
|
660
|
-
* This method is a shared chain method, and will return the same instance of HTTP. All
|
|
661
|
-
* child clients will inherit the routes defined by this method. Useful to configure
|
|
662
|
-
* in the constructor body.
|
|
663
|
-
*
|
|
664
|
-
* @param route A route or spread list of routes to append to the request.
|
|
665
|
-
* @returns This instance of HTTP.
|
|
1014
|
+
* Forces subsequent requests to return raw response streams without parsing.
|
|
666
1015
|
*/
|
|
667
|
-
|
|
668
|
-
|
|
1016
|
+
sharedStreamResponse(enabled) {
|
|
1017
|
+
this.meta.streamResponse(enabled);
|
|
669
1018
|
return this;
|
|
670
1019
|
}
|
|
671
1020
|
/**
|
|
672
|
-
*
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
* ```ts
|
|
679
|
-
* getUser(id: number) {
|
|
680
|
-
* return this.http.route("user", id).get();
|
|
681
|
-
* }
|
|
682
|
-
*
|
|
683
|
-
* // or
|
|
684
|
-
*
|
|
685
|
-
* getUser(id: number) {
|
|
686
|
-
* return this.http
|
|
687
|
-
* .route("user")
|
|
688
|
-
* .route(id)
|
|
689
|
-
* .get();
|
|
690
|
-
* }
|
|
691
|
-
* ```
|
|
692
|
-
*
|
|
693
|
-
* This method is a request chain method, and will return a new instance of HTTP, inheriting
|
|
694
|
-
* any routes previously defined and appending the new route. Useful to configure
|
|
695
|
-
* in class methods as part of finalizing a request.
|
|
696
|
-
*
|
|
697
|
-
* @param route A route or spread list of routes to append to the request.
|
|
698
|
-
* @returns A new child instance of HTTP derived from this one.
|
|
699
|
-
*/
|
|
700
|
-
route(...route) {
|
|
701
|
-
const mapped = route.map((r) => String(r));
|
|
702
|
-
return HTTP.create(
|
|
703
|
-
this.client,
|
|
704
|
-
__privateGet(this, _request2).derive().route(...mapped),
|
|
705
|
-
__privateGet(this, _metaConfig).derive()
|
|
706
|
-
);
|
|
1021
|
+
* Returns a derived client configured for streaming responses.
|
|
1022
|
+
*/
|
|
1023
|
+
streamResponse(enabled) {
|
|
1024
|
+
return this.derive(({ meta }) => {
|
|
1025
|
+
meta.streamResponse(enabled);
|
|
1026
|
+
});
|
|
707
1027
|
}
|
|
708
|
-
|
|
709
|
-
|
|
1028
|
+
/**
|
|
1029
|
+
* Convenience helper that returns a clone configured for streaming responses.
|
|
1030
|
+
*/
|
|
1031
|
+
asStream() {
|
|
1032
|
+
return this.streamResponse(true);
|
|
1033
|
+
}
|
|
1034
|
+
/**
|
|
1035
|
+
* Executes a GET request while preserving the raw response stream.
|
|
1036
|
+
*/
|
|
1037
|
+
stream(options) {
|
|
1038
|
+
return this.streamResponse(true).get(options);
|
|
1039
|
+
}
|
|
1040
|
+
/**
|
|
1041
|
+
* Sets a shared timeout (in milliseconds) applied to every request from this instance.
|
|
1042
|
+
*/
|
|
1043
|
+
sharedTimeout(duration) {
|
|
1044
|
+
this.meta.timeout(duration);
|
|
710
1045
|
return this;
|
|
711
1046
|
}
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
1047
|
+
/**
|
|
1048
|
+
* Returns a derived client with a per-request timeout in milliseconds.
|
|
1049
|
+
*/
|
|
1050
|
+
timeout(duration) {
|
|
1051
|
+
return this.derive(({ meta }) => {
|
|
1052
|
+
meta.timeout(duration);
|
|
1053
|
+
});
|
|
718
1054
|
}
|
|
719
|
-
|
|
720
|
-
|
|
1055
|
+
/**
|
|
1056
|
+
* Configures whether schema validation is required before resolving a response.
|
|
1057
|
+
*/
|
|
1058
|
+
requireSchema(required) {
|
|
1059
|
+
this.meta.requireSchema(required);
|
|
721
1060
|
return this;
|
|
722
1061
|
}
|
|
723
1062
|
/**
|
|
724
|
-
*
|
|
725
|
-
* immediately before the request is sent. This hook can be used to analyze or
|
|
726
|
-
* log the request state.
|
|
727
|
-
*
|
|
728
|
-
* ```ts
|
|
729
|
-
*
|
|
730
|
-
* \@Fixture(INJECTION_SCOPE.TRANSIENT)
|
|
731
|
-
* export class UserController extends BaseController {
|
|
732
|
-
* constructor(private readonly http: HTTP) {
|
|
733
|
-
* super(http);
|
|
734
|
-
* this.http
|
|
735
|
-
* .sharedRoute("user")
|
|
736
|
-
* .sharedOnSend("log request",
|
|
737
|
-
* (request) => console.log(JSON.stringify(request, null, 2))
|
|
738
|
-
* );
|
|
739
|
-
* }
|
|
740
|
-
* }
|
|
741
|
-
* ```
|
|
742
|
-
*
|
|
743
|
-
* This method is a shared chain method, and will return the same instance of HTTP. All
|
|
744
|
-
* child clients will inherit the onSend hooks defined by this method. Useful to configure
|
|
745
|
-
* in the constructor body.
|
|
746
|
-
*
|
|
747
|
-
* @param description A description of the hook, used for debugging.
|
|
748
|
-
* @param hook The hook to execute.
|
|
749
|
-
* @returns This instance of HTTP.
|
|
1063
|
+
* Returns a clone with overridden plain text handling mode.
|
|
750
1064
|
*/
|
|
751
|
-
|
|
752
|
-
|
|
1065
|
+
allowPlainText(allow) {
|
|
1066
|
+
return this.derive(({ meta }) => {
|
|
1067
|
+
meta.allowPlainText(allow);
|
|
1068
|
+
});
|
|
1069
|
+
}
|
|
1070
|
+
/**
|
|
1071
|
+
* Sets plain text handling for the current instance and all future requests.
|
|
1072
|
+
*/
|
|
1073
|
+
sharedAllowPlainText(allow) {
|
|
1074
|
+
this.meta.allowPlainText(allow);
|
|
753
1075
|
return this;
|
|
754
1076
|
}
|
|
755
1077
|
/**
|
|
756
|
-
*
|
|
757
|
-
* immediately after the response is received. This hook can be used to analyze or
|
|
758
|
-
* log the response state.
|
|
759
|
-
*
|
|
760
|
-
* ```ts
|
|
761
|
-
*
|
|
762
|
-
* \@Fixture(INJECTION_SCOPE.TRANSIENT)
|
|
763
|
-
* export class UserController extends BaseController {
|
|
764
|
-
* constructor(private readonly http: HTTP) {
|
|
765
|
-
* super(http);
|
|
766
|
-
* this.http
|
|
767
|
-
* .sharedRoute("user")
|
|
768
|
-
* .sharedOnReceive("log response",
|
|
769
|
-
* (response) => console.log(JSON.stringify(response, null, 2))
|
|
770
|
-
* );
|
|
771
|
-
* }
|
|
772
|
-
* }
|
|
773
|
-
* ```
|
|
774
|
-
*
|
|
775
|
-
* This method is a shared chain method, and will return the same instance of HTTP. All
|
|
776
|
-
* child clients will inherit the onReceive hooks defined by this method. Useful to configure
|
|
777
|
-
* in the constructor body.
|
|
778
|
-
*
|
|
779
|
-
* @param description A description of the hook, used for debugging.
|
|
780
|
-
* @param hook The hook to execute.
|
|
781
|
-
* @returns This instance of HTTP.
|
|
1078
|
+
* Adds path segments that will be included in every request.
|
|
782
1079
|
*/
|
|
783
|
-
|
|
784
|
-
|
|
1080
|
+
sharedRoute(...segments) {
|
|
1081
|
+
this.builder.route(...segments);
|
|
785
1082
|
return this;
|
|
786
1083
|
}
|
|
787
1084
|
/**
|
|
788
|
-
*
|
|
789
|
-
* parameters are key-value pairs which are appended to the request url, such as
|
|
790
|
-
* `https://api.example.com?name=John&age=30`.
|
|
791
|
-
*
|
|
792
|
-
* This method is a shared chain method, and will return the same instance of HTTP. All
|
|
793
|
-
* child clients will inherit the query string parameters defined by this method. Useful to configure
|
|
794
|
-
* in the constructor body.
|
|
1085
|
+
* Executes a request using the provided method.
|
|
795
1086
|
*
|
|
796
|
-
*
|
|
797
|
-
*
|
|
798
|
-
*
|
|
799
|
-
* this.http
|
|
800
|
-
* .sharedParams({ 'is-test': "true" })
|
|
801
|
-
* ```
|
|
802
|
-
* @param name The name of the query string parameter.
|
|
803
|
-
* @param value The value of the query string parameter.
|
|
804
|
-
* @returns This instance of HTTP.
|
|
1087
|
+
* Use this when the verb is dynamic (e.g. provided by a parameter). It
|
|
1088
|
+
* behaves like calling {@link get}/{@link post}/{@link patch}, respecting any
|
|
1089
|
+
* route/headers/body configured earlier in the chain.
|
|
805
1090
|
*/
|
|
806
|
-
|
|
807
|
-
|
|
1091
|
+
fetchWith(method, options) {
|
|
1092
|
+
const normalized = String(method).trim().toUpperCase();
|
|
1093
|
+
return this.execute(normalized, options);
|
|
1094
|
+
}
|
|
1095
|
+
/**
|
|
1096
|
+
* Returns a clone with additional path segments.
|
|
1097
|
+
*/
|
|
1098
|
+
route(...segments) {
|
|
1099
|
+
return this.derive(({ builder }) => {
|
|
1100
|
+
builder.route(...segments);
|
|
1101
|
+
});
|
|
1102
|
+
}
|
|
1103
|
+
/**
|
|
1104
|
+
* Applies query serialization preferences to all future requests originating from this instance.
|
|
1105
|
+
*/
|
|
1106
|
+
sharedQueryFormat(options) {
|
|
1107
|
+
this.builder.queryFormat(options);
|
|
808
1108
|
return this;
|
|
809
1109
|
}
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
);
|
|
1110
|
+
/**
|
|
1111
|
+
* Returns a derived client with custom query serialization that does not affect the source instance.
|
|
1112
|
+
*/
|
|
1113
|
+
queryFormat(options) {
|
|
1114
|
+
return this.derive(({ builder }) => {
|
|
1115
|
+
builder.queryFormat(options);
|
|
1116
|
+
});
|
|
1117
|
+
}
|
|
1118
|
+
sharedSchema(parser, ...args) {
|
|
1119
|
+
this.meta.schema(parser, ...args);
|
|
1120
|
+
return this;
|
|
1121
|
+
}
|
|
1122
|
+
schema(parser, ...args) {
|
|
1123
|
+
return this.derive(({ meta }) => {
|
|
1124
|
+
meta.schema(parser, ...args);
|
|
1125
|
+
});
|
|
1126
|
+
}
|
|
1127
|
+
sharedParam(name, value, ...rest) {
|
|
1128
|
+
this.builder.param(name, toParamValue(value, rest));
|
|
1129
|
+
return this;
|
|
1130
|
+
}
|
|
1131
|
+
param(name, value, ...rest) {
|
|
1132
|
+
return this.derive(({ builder }) => {
|
|
1133
|
+
builder.param(name, toParamValue(value, rest));
|
|
1134
|
+
});
|
|
817
1135
|
}
|
|
818
1136
|
/**
|
|
819
|
-
*
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
*
|
|
827
|
-
* ```ts
|
|
828
|
-
* getUser(id: number) {
|
|
829
|
-
* return this.http
|
|
830
|
-
* .route(id)
|
|
831
|
-
* .param({ name: "John", age: "30" })
|
|
832
|
-
*
|
|
833
|
-
* @param name The name of the query string parameter.
|
|
834
|
-
* @param value The value of the query string parameter.
|
|
835
|
-
* @returns This instance of HTTP.
|
|
1137
|
+
* Merges multiple shared query parameters into the instance.
|
|
1138
|
+
*/
|
|
1139
|
+
sharedParams(dict) {
|
|
1140
|
+
this.builder.params(dict);
|
|
1141
|
+
return this;
|
|
1142
|
+
}
|
|
1143
|
+
/**
|
|
1144
|
+
* Derives a client with additional query parameters applied together.
|
|
836
1145
|
*/
|
|
837
1146
|
params(dict) {
|
|
838
|
-
return
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
__privateGet(this, _metaConfig).derive()
|
|
842
|
-
);
|
|
1147
|
+
return this.derive(({ builder }) => {
|
|
1148
|
+
builder.params(dict);
|
|
1149
|
+
});
|
|
843
1150
|
}
|
|
844
1151
|
/**
|
|
845
|
-
*
|
|
846
|
-
* and can be any type. If the data payload is an object, it will be serialized to JSON.
|
|
847
|
-
*
|
|
848
|
-
* This method is a shared chain method, and will return the same instance of HTTP. All
|
|
849
|
-
* child clients will inherit the data payload defined by this method. Useful to configure
|
|
850
|
-
* in the constructor body.
|
|
851
|
-
*
|
|
852
|
-
* @param data The data payload to attach to the request.
|
|
853
|
-
* @returns This instance of HTTP.
|
|
1152
|
+
* Sets a shared request body used by every request from this instance.
|
|
854
1153
|
*/
|
|
855
1154
|
sharedData(data) {
|
|
856
|
-
|
|
1155
|
+
this.builder.data(data);
|
|
857
1156
|
return this;
|
|
858
1157
|
}
|
|
859
1158
|
/**
|
|
860
|
-
*
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
*
|
|
869
|
-
* in the constructor body.
|
|
870
|
-
*
|
|
871
|
-
* @param name The name of the header.
|
|
872
|
-
* @param value The value of the header.
|
|
1159
|
+
* Derives a client with a one-off request body.
|
|
1160
|
+
*/
|
|
1161
|
+
data(data) {
|
|
1162
|
+
return this.derive(({ builder }) => {
|
|
1163
|
+
builder.data(data);
|
|
1164
|
+
});
|
|
1165
|
+
}
|
|
1166
|
+
/**
|
|
1167
|
+
* Registers a header that will be resolved for every request on this instance.
|
|
873
1168
|
*/
|
|
874
1169
|
sharedHeader(name, value) {
|
|
875
|
-
|
|
1170
|
+
this.builder.header(name, value);
|
|
876
1171
|
return this;
|
|
877
1172
|
}
|
|
1173
|
+
/**
|
|
1174
|
+
* Returns a clone with a header applied only to the resulting client.
|
|
1175
|
+
*/
|
|
878
1176
|
header(name, value) {
|
|
879
|
-
return
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
__privateGet(this, _metaConfig).derive()
|
|
883
|
-
);
|
|
1177
|
+
return this.derive(({ builder }) => {
|
|
1178
|
+
builder.header(name, value);
|
|
1179
|
+
});
|
|
884
1180
|
}
|
|
885
1181
|
/**
|
|
886
|
-
*
|
|
887
|
-
* and can be any type. If the data payload is an object, it will be serialized to JSON.
|
|
888
|
-
*
|
|
889
|
-
* This method is a request chain method, and will return a new instance of HTTP, inheriting
|
|
890
|
-
* any data payload previously defined and appending the new payload. Useful to configure
|
|
891
|
-
* in class methods as part of finalizing a request.
|
|
892
|
-
*
|
|
893
|
-
* @param data The data payload to attach to the request.
|
|
894
|
-
* @returns A new child instance of HTTP derived from this one.
|
|
1182
|
+
* Registers multiple shared headers for every downstream request.
|
|
895
1183
|
*/
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
return
|
|
899
|
-
this.client,
|
|
900
|
-
__privateGet(this, _request2).derive().data(data),
|
|
901
|
-
__privateGet(this, _metaConfig).derive()
|
|
902
|
-
);
|
|
1184
|
+
sharedHeaders(dict) {
|
|
1185
|
+
this.builder.headers(dict);
|
|
1186
|
+
return this;
|
|
903
1187
|
}
|
|
904
1188
|
/**
|
|
905
|
-
*
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
*
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
*
|
|
921
|
-
* (request) => console.log(JSON.stringify(request, null, 2)
|
|
922
|
-
* )
|
|
923
|
-
* .get();
|
|
924
|
-
* }
|
|
925
|
-
* ```
|
|
926
|
-
*
|
|
927
|
-
* This method is a request chain method, and will return a new instance of HTTP, inheriting
|
|
928
|
-
* any onSend hooks previously defined and appending the new hook. Useful to configure
|
|
929
|
-
* in class methods as part of finalizing a request.
|
|
930
|
-
*
|
|
931
|
-
* @param description A description of the hook, used for debugging.
|
|
932
|
-
* @param hook The hook to execute.
|
|
933
|
-
* @returns A new child instance of HTTP derived from this one.
|
|
1189
|
+
* Returns a derived client with additional headers.
|
|
1190
|
+
*/
|
|
1191
|
+
headers(dict) {
|
|
1192
|
+
return this.derive(({ builder }) => {
|
|
1193
|
+
builder.headers(dict);
|
|
1194
|
+
});
|
|
1195
|
+
}
|
|
1196
|
+
/**
|
|
1197
|
+
* Registers a request hook that runs before every execution on this instance.
|
|
1198
|
+
*/
|
|
1199
|
+
sharedOnSend(description, hook) {
|
|
1200
|
+
this.meta.onBeforeSend(description, hook);
|
|
1201
|
+
return this;
|
|
1202
|
+
}
|
|
1203
|
+
/**
|
|
1204
|
+
* Returns a clone with a request hook used only for that clone.
|
|
934
1205
|
*/
|
|
935
1206
|
onSend(description, hook) {
|
|
936
|
-
return
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
__privateGet(this, _metaConfig).derive().onBeforeSend(description, hook)
|
|
940
|
-
);
|
|
1207
|
+
return this.derive(({ meta }) => {
|
|
1208
|
+
meta.onBeforeSend(description, hook);
|
|
1209
|
+
});
|
|
941
1210
|
}
|
|
942
1211
|
/**
|
|
943
|
-
*
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
*
|
|
951
|
-
* constructor(private readonly http: HTTP) {
|
|
952
|
-
* super(http);
|
|
953
|
-
* }
|
|
954
|
-
*
|
|
955
|
-
* getUser(id: number) {
|
|
956
|
-
* return this.http
|
|
957
|
-
* .route(id)
|
|
958
|
-
* .onReceive("log response",
|
|
959
|
-
* (response) => console.log(JSON.stringify(response, null, 2)
|
|
960
|
-
* )
|
|
961
|
-
* .get();
|
|
962
|
-
* }
|
|
963
|
-
* ```
|
|
964
|
-
*
|
|
965
|
-
* This method is a request chain method, and will return a new instance of HTTP, inheriting
|
|
966
|
-
* any onReceive hooks previously defined and appending the new hook. Useful to configure
|
|
967
|
-
* in class methods as part of finalizing a request.
|
|
968
|
-
*
|
|
969
|
-
* @param description A description of the hook, used for debugging.
|
|
970
|
-
* @param hook The hook to execute.
|
|
971
|
-
* @returns A new child instance of HTTP derived from this one.
|
|
1212
|
+
* Registers a response hook executed after every transport response.
|
|
1213
|
+
*/
|
|
1214
|
+
sharedOnReceive(description, hook) {
|
|
1215
|
+
this.meta.onReceiveResponse(description, hook);
|
|
1216
|
+
return this;
|
|
1217
|
+
}
|
|
1218
|
+
/**
|
|
1219
|
+
* Returns a derived client with a response hook limited to that client.
|
|
972
1220
|
*/
|
|
973
1221
|
onReceive(description, hook) {
|
|
974
|
-
return
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
__privateGet(this, _metaConfig).derive().onReceiveResponse(description, hook)
|
|
978
|
-
);
|
|
1222
|
+
return this.derive(({ meta }) => {
|
|
1223
|
+
meta.onReceiveResponse(description, hook);
|
|
1224
|
+
});
|
|
979
1225
|
}
|
|
980
1226
|
/**
|
|
981
|
-
*
|
|
982
|
-
*
|
|
983
|
-
* @param options Additional options to pass to the underlying http client, such
|
|
984
|
-
* as e.g Axios configuration values.
|
|
985
|
-
* @returns A promise which resolves to the response.
|
|
1227
|
+
* Configures whether server errors (>=500) throw by default for every request.
|
|
986
1228
|
*/
|
|
987
|
-
|
|
988
|
-
|
|
1229
|
+
sharedThrowOnServerError(value) {
|
|
1230
|
+
this.meta.throwOnServerError(value);
|
|
1231
|
+
return this;
|
|
989
1232
|
}
|
|
990
1233
|
/**
|
|
991
|
-
*
|
|
992
|
-
*
|
|
993
|
-
* @param data The data payload to attach to the request.
|
|
994
|
-
* @param options Additional options to pass to the underlying http client, such
|
|
995
|
-
* as e.g Axios configuration values.
|
|
996
|
-
* @returns A promise which resolves to the response.
|
|
1234
|
+
* Returns a derived client with custom server error behaviour.
|
|
997
1235
|
*/
|
|
998
|
-
|
|
999
|
-
return
|
|
1236
|
+
throwOnServerError(value) {
|
|
1237
|
+
return this.derive(({ meta }) => {
|
|
1238
|
+
meta.throwOnServerError(value);
|
|
1239
|
+
});
|
|
1000
1240
|
}
|
|
1001
1241
|
/**
|
|
1002
|
-
* Executes
|
|
1003
|
-
*
|
|
1004
|
-
* @param options Additional options to pass to the underlying http client, such
|
|
1005
|
-
* as e.g Axios configuration values.
|
|
1006
|
-
* @returns A promise which resolves to the response.
|
|
1007
|
-
* as e.g Axios configuration values.
|
|
1242
|
+
* Executes a GET request using the current configuration.
|
|
1008
1243
|
*/
|
|
1009
|
-
|
|
1010
|
-
return
|
|
1244
|
+
get(options) {
|
|
1245
|
+
return this.execute("GET", options);
|
|
1011
1246
|
}
|
|
1012
1247
|
/**
|
|
1013
|
-
* Executes
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1248
|
+
* Executes a POST request using the current configuration.
|
|
1249
|
+
*/
|
|
1250
|
+
post(options) {
|
|
1251
|
+
return this.execute("POST", options);
|
|
1252
|
+
}
|
|
1253
|
+
/**
|
|
1254
|
+
* Executes a PUT request using the current configuration.
|
|
1018
1255
|
*/
|
|
1019
1256
|
put(options) {
|
|
1020
|
-
return
|
|
1257
|
+
return this.execute("PUT", options);
|
|
1021
1258
|
}
|
|
1022
1259
|
/**
|
|
1023
|
-
* Executes
|
|
1024
|
-
*
|
|
1025
|
-
* @param options Additional options to pass to the underlying http client, such
|
|
1026
|
-
* as e.g Axios configuration values.
|
|
1027
|
-
* @returns A promise which resolves to the response.
|
|
1260
|
+
* Executes a PATCH request using the current configuration.
|
|
1028
1261
|
*/
|
|
1029
1262
|
patch(options) {
|
|
1030
|
-
return
|
|
1263
|
+
return this.execute("PATCH", options);
|
|
1031
1264
|
}
|
|
1265
|
+
/**
|
|
1266
|
+
* Executes a DELETE request using the current configuration.
|
|
1267
|
+
*/
|
|
1268
|
+
delete(options) {
|
|
1269
|
+
return this.execute("DELETE", options);
|
|
1270
|
+
}
|
|
1271
|
+
/**
|
|
1272
|
+
* Executes a HEAD request using the current configuration.
|
|
1273
|
+
*/
|
|
1032
1274
|
head(options) {
|
|
1033
|
-
return
|
|
1275
|
+
return this.execute("HEAD", options);
|
|
1034
1276
|
}
|
|
1277
|
+
/**
|
|
1278
|
+
* Executes an OPTIONS request using the current configuration.
|
|
1279
|
+
*/
|
|
1035
1280
|
options(options) {
|
|
1036
|
-
return
|
|
1281
|
+
return this.execute("OPTIONS", options);
|
|
1037
1282
|
}
|
|
1283
|
+
/**
|
|
1284
|
+
* Executes a TRACE request using the current configuration.
|
|
1285
|
+
*/
|
|
1038
1286
|
trace(options) {
|
|
1039
|
-
return
|
|
1287
|
+
return this.execute("TRACE", options);
|
|
1040
1288
|
}
|
|
1289
|
+
/**
|
|
1290
|
+
* Executes a CONNECT request using the current configuration.
|
|
1291
|
+
*/
|
|
1041
1292
|
connect(options) {
|
|
1042
|
-
return
|
|
1293
|
+
return this.execute("CONNECT", options);
|
|
1294
|
+
}
|
|
1295
|
+
async execute(method, options) {
|
|
1296
|
+
const baseBuilder = this.builder.clone().method(method);
|
|
1297
|
+
const meta = this.meta.derive().build();
|
|
1298
|
+
const baseOptions = mergeOptions2(meta.options, options);
|
|
1299
|
+
if (meta.streamResponse) {
|
|
1300
|
+
baseOptions.streamResponse = true;
|
|
1301
|
+
}
|
|
1302
|
+
const retryPolicy = meta.retry;
|
|
1303
|
+
const maxRetries = retryPolicy?.attempts ?? 0;
|
|
1304
|
+
for (let retriesUsed = 0; ; ) {
|
|
1305
|
+
const attemptBuilder = baseBuilder.clone();
|
|
1306
|
+
await attemptBuilder.resolveDynamicHeaders();
|
|
1307
|
+
const request = attemptBuilder.build();
|
|
1308
|
+
request.method = method;
|
|
1309
|
+
const attemptOptions = {
|
|
1310
|
+
...baseOptions
|
|
1311
|
+
};
|
|
1312
|
+
let timeoutController;
|
|
1313
|
+
let timeoutHandle;
|
|
1314
|
+
let combinedSignal;
|
|
1315
|
+
if (typeof meta.timeoutMs === "number" && meta.timeoutMs > 0) {
|
|
1316
|
+
const setup = createTimeoutController(meta.timeoutMs);
|
|
1317
|
+
timeoutController = setup.controller;
|
|
1318
|
+
timeoutHandle = setup.timer;
|
|
1319
|
+
}
|
|
1320
|
+
const existingSignal = attemptOptions.signal;
|
|
1321
|
+
const signals = [];
|
|
1322
|
+
if (existingSignal) {
|
|
1323
|
+
signals.push(existingSignal);
|
|
1324
|
+
}
|
|
1325
|
+
if (timeoutController) {
|
|
1326
|
+
signals.push(timeoutController.signal);
|
|
1327
|
+
}
|
|
1328
|
+
if (signals.length === 1) {
|
|
1329
|
+
const singleSignal = signals[0];
|
|
1330
|
+
if (singleSignal) {
|
|
1331
|
+
attemptOptions.signal = singleSignal;
|
|
1332
|
+
} else if ("signal" in attemptOptions) {
|
|
1333
|
+
delete attemptOptions.signal;
|
|
1334
|
+
}
|
|
1335
|
+
} else if (signals.length > 1) {
|
|
1336
|
+
combinedSignal = combineAbortSignals(signals);
|
|
1337
|
+
attemptOptions.signal = combinedSignal.signal;
|
|
1338
|
+
} else if ("signal" in attemptOptions) {
|
|
1339
|
+
delete attemptOptions.signal;
|
|
1340
|
+
}
|
|
1341
|
+
const activeSignal = attemptOptions.signal;
|
|
1342
|
+
if (activeSignal?.aborted) {
|
|
1343
|
+
const reason = activeSignal.reason ?? createAbortError();
|
|
1344
|
+
throw new HTTPTransportError(request, reason);
|
|
1345
|
+
}
|
|
1346
|
+
const requestContext = {
|
|
1347
|
+
request,
|
|
1348
|
+
options: attemptOptions
|
|
1349
|
+
};
|
|
1350
|
+
let response;
|
|
1351
|
+
try {
|
|
1352
|
+
await this.runRequestPlugins(requestContext);
|
|
1353
|
+
await this.runOnSendHooks(meta, request);
|
|
1354
|
+
const raw = await this.transport.send(request, attemptOptions).catch((cause) => {
|
|
1355
|
+
throw new HTTPTransportError(request, cause);
|
|
1356
|
+
});
|
|
1357
|
+
response = this.buildResponse(raw, request);
|
|
1358
|
+
const isStreaming = meta.streamResponse;
|
|
1359
|
+
if (!isStreaming) {
|
|
1360
|
+
response.data = transformResponse(meta.allowPlainText, response.data);
|
|
1361
|
+
}
|
|
1362
|
+
if (meta.throwOnServerError && response.status >= 500) {
|
|
1363
|
+
throw new HTTPServerError(request, response);
|
|
1364
|
+
}
|
|
1365
|
+
await this.runOnReceiveHooks(meta, response);
|
|
1366
|
+
let validated;
|
|
1367
|
+
if (meta.streamResponse) {
|
|
1368
|
+
validated = response;
|
|
1369
|
+
} else {
|
|
1370
|
+
try {
|
|
1371
|
+
validated = this.validateResponse(response, meta);
|
|
1372
|
+
} catch (cause) {
|
|
1373
|
+
throw new HTTPSchemaValidationError(request, response, cause);
|
|
1374
|
+
}
|
|
1375
|
+
}
|
|
1376
|
+
await this.runResponsePlugins({
|
|
1377
|
+
request,
|
|
1378
|
+
response: validated,
|
|
1379
|
+
options: attemptOptions
|
|
1380
|
+
});
|
|
1381
|
+
return validated;
|
|
1382
|
+
} catch (thrown) {
|
|
1383
|
+
const normalized = thrown instanceof HTTPError ? thrown : thrown;
|
|
1384
|
+
const retryAttempt = retriesUsed + 1;
|
|
1385
|
+
const policy = retryPolicy;
|
|
1386
|
+
const canRetry = policy !== void 0 && retryAttempt <= maxRetries && await this.shouldRetryRequest(
|
|
1387
|
+
normalized,
|
|
1388
|
+
policy,
|
|
1389
|
+
retryAttempt,
|
|
1390
|
+
request
|
|
1391
|
+
);
|
|
1392
|
+
if (!canRetry) {
|
|
1393
|
+
await this.runErrorPlugins({
|
|
1394
|
+
request,
|
|
1395
|
+
options: attemptOptions,
|
|
1396
|
+
error: normalized
|
|
1397
|
+
});
|
|
1398
|
+
throw normalized;
|
|
1399
|
+
}
|
|
1400
|
+
retriesUsed += 1;
|
|
1401
|
+
await this.delayRetry(retryAttempt, policy);
|
|
1402
|
+
} finally {
|
|
1403
|
+
if (timeoutHandle !== void 0) {
|
|
1404
|
+
clearTimeout(timeoutHandle);
|
|
1405
|
+
}
|
|
1406
|
+
combinedSignal?.dispose();
|
|
1407
|
+
}
|
|
1408
|
+
}
|
|
1409
|
+
}
|
|
1410
|
+
async runRequestPlugins(context) {
|
|
1411
|
+
for (const plugin of this.plugins()) {
|
|
1412
|
+
if (plugin.onRequest) {
|
|
1413
|
+
await plugin.onRequest(context);
|
|
1414
|
+
}
|
|
1415
|
+
}
|
|
1416
|
+
}
|
|
1417
|
+
async runResponsePlugins(context) {
|
|
1418
|
+
for (const plugin of this.plugins()) {
|
|
1419
|
+
if (plugin.onResponse) {
|
|
1420
|
+
await plugin.onResponse(context);
|
|
1421
|
+
}
|
|
1422
|
+
}
|
|
1423
|
+
}
|
|
1424
|
+
async runErrorPlugins(context) {
|
|
1425
|
+
for (const plugin of this.plugins()) {
|
|
1426
|
+
if (plugin.onError) {
|
|
1427
|
+
await plugin.onError(context);
|
|
1428
|
+
}
|
|
1429
|
+
}
|
|
1043
1430
|
}
|
|
1044
1431
|
async runOnSendHooks(meta, request) {
|
|
1045
1432
|
for (const [description, hook] of meta.onSend) {
|
|
1046
1433
|
try {
|
|
1047
1434
|
await hook(request);
|
|
1048
|
-
} catch (
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1435
|
+
} catch (error) {
|
|
1436
|
+
throw new AutomationError(
|
|
1437
|
+
`An error occurred in onSend hook "${description}"`,
|
|
1438
|
+
{ cause: error }
|
|
1439
|
+
);
|
|
1052
1440
|
}
|
|
1053
1441
|
}
|
|
1054
1442
|
}
|
|
@@ -1056,111 +1444,605 @@ var HTTP = class {
|
|
|
1056
1444
|
for (const [description, hook] of meta.onReceive) {
|
|
1057
1445
|
try {
|
|
1058
1446
|
await hook(response);
|
|
1059
|
-
} catch (
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1447
|
+
} catch (error) {
|
|
1448
|
+
throw new AutomationError(
|
|
1449
|
+
`An error occurred in onReceive hook "${description}"`,
|
|
1450
|
+
{ cause: error }
|
|
1451
|
+
);
|
|
1063
1452
|
}
|
|
1064
1453
|
}
|
|
1065
1454
|
}
|
|
1455
|
+
validateResponse(response, meta) {
|
|
1456
|
+
const validated = meta.schemas.validate(
|
|
1457
|
+
response.status,
|
|
1458
|
+
response.data,
|
|
1459
|
+
meta.requireSchema
|
|
1460
|
+
);
|
|
1461
|
+
response.data = validated;
|
|
1462
|
+
return response;
|
|
1463
|
+
}
|
|
1464
|
+
buildResponse(raw, request) {
|
|
1465
|
+
return HTTPResponseBuilder.create().status(raw.status).statusText(raw.statusText).headers(normalizeHeaders(raw.headers)).data(raw.data).request(request).build();
|
|
1466
|
+
}
|
|
1467
|
+
plugins() {
|
|
1468
|
+
return [...this.sharedPlugins, ...this.scopedPlugins];
|
|
1469
|
+
}
|
|
1470
|
+
async shouldRetryRequest(error, policy, attempt, request) {
|
|
1471
|
+
if (attempt > policy.attempts) {
|
|
1472
|
+
return false;
|
|
1473
|
+
}
|
|
1474
|
+
const response = error instanceof HTTPError ? error.response : void 0;
|
|
1475
|
+
if (policy.retryOn) {
|
|
1476
|
+
const retryContext = response ? { error, attempt, request, response } : { error, attempt, request };
|
|
1477
|
+
return await policy.retryOn(retryContext);
|
|
1478
|
+
}
|
|
1479
|
+
if (error instanceof HTTPTransportError) {
|
|
1480
|
+
return true;
|
|
1481
|
+
}
|
|
1482
|
+
if (response && response.status >= 500) {
|
|
1483
|
+
return true;
|
|
1484
|
+
}
|
|
1485
|
+
return false;
|
|
1486
|
+
}
|
|
1487
|
+
async delayRetry(attempt, policy) {
|
|
1488
|
+
const { delay } = policy;
|
|
1489
|
+
let duration;
|
|
1490
|
+
if (typeof delay === "function") {
|
|
1491
|
+
duration = await delay(attempt);
|
|
1492
|
+
} else if (typeof delay === "number") {
|
|
1493
|
+
duration = delay * attempt;
|
|
1494
|
+
} else {
|
|
1495
|
+
duration = attempt * 100;
|
|
1496
|
+
}
|
|
1497
|
+
if (!duration || duration <= 0) {
|
|
1498
|
+
return;
|
|
1499
|
+
}
|
|
1500
|
+
await new Promise((resolve) => setTimeout(resolve, duration));
|
|
1501
|
+
}
|
|
1502
|
+
derive(mutate, overrides) {
|
|
1503
|
+
const builder = this.builder.clone();
|
|
1504
|
+
const meta = this.meta.derive();
|
|
1505
|
+
const plugins = [...this.scopedPlugins];
|
|
1506
|
+
if (mutate) {
|
|
1507
|
+
mutate({ builder, meta, plugins });
|
|
1508
|
+
}
|
|
1509
|
+
return new _HTTP(
|
|
1510
|
+
overrides?.transport ?? this.transport,
|
|
1511
|
+
builder,
|
|
1512
|
+
meta,
|
|
1513
|
+
this.sharedPlugins,
|
|
1514
|
+
plugins
|
|
1515
|
+
);
|
|
1516
|
+
}
|
|
1066
1517
|
};
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
const
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
}
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
const
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1518
|
+
_HTTP.sharedPlugins = [];
|
|
1519
|
+
var HTTP = _HTTP;
|
|
1520
|
+
function mergeOptions2(base, overrides) {
|
|
1521
|
+
if (!overrides) {
|
|
1522
|
+
return { ...base };
|
|
1523
|
+
}
|
|
1524
|
+
const merged = { ...base };
|
|
1525
|
+
for (const [key, value] of Object.entries(overrides)) {
|
|
1526
|
+
if (value === void 0) {
|
|
1527
|
+
delete merged[key];
|
|
1528
|
+
} else {
|
|
1529
|
+
merged[key] = value;
|
|
1530
|
+
}
|
|
1531
|
+
}
|
|
1532
|
+
return merged;
|
|
1533
|
+
}
|
|
1534
|
+
function normalizeHeaders(headers) {
|
|
1535
|
+
const next = {};
|
|
1536
|
+
for (const [key, value] of Object.entries(headers)) {
|
|
1537
|
+
next[key] = Array.isArray(value) ? value.join(",") : String(value);
|
|
1538
|
+
}
|
|
1539
|
+
return next;
|
|
1540
|
+
}
|
|
1541
|
+
function toParamValue(value, rest) {
|
|
1542
|
+
if (rest.length > 0) {
|
|
1543
|
+
return [value, ...rest];
|
|
1544
|
+
}
|
|
1545
|
+
return value;
|
|
1546
|
+
}
|
|
1547
|
+
function createTimeoutController(timeoutMs) {
|
|
1548
|
+
const controller = new AbortController();
|
|
1549
|
+
const timer = setTimeout(() => {
|
|
1550
|
+
controller.abort(createAbortError(`Request timed out after ${timeoutMs} ms`));
|
|
1551
|
+
}, timeoutMs);
|
|
1552
|
+
maybeUnrefTimer(timer);
|
|
1553
|
+
return { controller, timer };
|
|
1554
|
+
}
|
|
1555
|
+
function combineAbortSignals(signals) {
|
|
1556
|
+
const controller = new AbortController();
|
|
1557
|
+
const aborted = signals.find((signal) => signal.aborted);
|
|
1558
|
+
if (aborted) {
|
|
1559
|
+
controller.abort(aborted.reason ?? createAbortError());
|
|
1560
|
+
return { signal: controller.signal, dispose: () => void 0 };
|
|
1561
|
+
}
|
|
1562
|
+
const listeners = [];
|
|
1563
|
+
for (const signal of signals) {
|
|
1564
|
+
const listener = () => {
|
|
1565
|
+
controller.abort(signal.reason ?? createAbortError());
|
|
1566
|
+
};
|
|
1567
|
+
signal.addEventListener("abort", listener, { once: true });
|
|
1568
|
+
listeners.push({ signal, listener });
|
|
1569
|
+
}
|
|
1570
|
+
const dispose = () => {
|
|
1571
|
+
for (const { signal, listener } of listeners) {
|
|
1572
|
+
signal.removeEventListener("abort", listener);
|
|
1573
|
+
}
|
|
1574
|
+
};
|
|
1575
|
+
controller.signal.addEventListener("abort", dispose, { once: true });
|
|
1576
|
+
return { signal: controller.signal, dispose };
|
|
1577
|
+
}
|
|
1578
|
+
function maybeUnrefTimer(timer) {
|
|
1579
|
+
if (typeof timer === "object" && timer !== null) {
|
|
1580
|
+
const maybeTimer = timer;
|
|
1581
|
+
if (typeof maybeTimer.unref === "function") {
|
|
1582
|
+
maybeTimer.unref();
|
|
1583
|
+
}
|
|
1584
|
+
}
|
|
1585
|
+
}
|
|
1586
|
+
function createAbortError(message = "The operation was aborted.") {
|
|
1587
|
+
const error = new Error(message);
|
|
1588
|
+
error.name = "AbortError";
|
|
1589
|
+
return error;
|
|
1590
|
+
}
|
|
1095
1591
|
|
|
1096
1592
|
// src/default-schema.ts
|
|
1097
|
-
var import_assert_is_json2 = __toESM(require("@stdlib/assert-is-json"), 1);
|
|
1098
1593
|
function AnySchema(data) {
|
|
1099
1594
|
return data;
|
|
1100
1595
|
}
|
|
1101
1596
|
function EmptySchema(data) {
|
|
1102
1597
|
if (data !== null && data !== void 0 && data !== "null") {
|
|
1103
|
-
throw new Error(`Expected null but
|
|
1598
|
+
throw new Error(`Expected null but received ${describeValue(data)}`);
|
|
1104
1599
|
}
|
|
1105
1600
|
return data === "null" ? null : data;
|
|
1106
1601
|
}
|
|
1107
1602
|
function NullSchema(data) {
|
|
1108
1603
|
if (data !== null && data !== "null") {
|
|
1109
|
-
throw new Error(`Expected null but
|
|
1604
|
+
throw new Error(`Expected null but received ${describeValue(data)}`);
|
|
1110
1605
|
}
|
|
1111
1606
|
return null;
|
|
1112
1607
|
}
|
|
1113
1608
|
function UndefinedSchema(data) {
|
|
1114
1609
|
if (data !== void 0) {
|
|
1115
|
-
throw new Error(`Expected undefined but
|
|
1610
|
+
throw new Error(`Expected undefined but received ${describeValue(data)}`);
|
|
1116
1611
|
}
|
|
1117
1612
|
return void 0;
|
|
1118
1613
|
}
|
|
1119
1614
|
function BooleanSchema(data) {
|
|
1120
|
-
if (
|
|
1121
|
-
|
|
1615
|
+
if (typeof data === "boolean" || typeof data === "string" && ["true", "false"].includes(data)) {
|
|
1616
|
+
return typeof data === "boolean" ? data : data === "true";
|
|
1122
1617
|
}
|
|
1123
|
-
|
|
1618
|
+
throw new Error(`Expected boolean but received ${describeValue(data)}`);
|
|
1124
1619
|
}
|
|
1125
1620
|
function NumberSchema(data) {
|
|
1126
|
-
if (
|
|
1127
|
-
|
|
1621
|
+
if (typeof data === "number") {
|
|
1622
|
+
return data;
|
|
1623
|
+
}
|
|
1624
|
+
if (typeof data === "string" && /^(?:\d+|\d*\.\d+)$/.test(data)) {
|
|
1625
|
+
return Number(data);
|
|
1128
1626
|
}
|
|
1129
|
-
|
|
1627
|
+
throw new Error(`Expected number but received ${describeValue(data)}`);
|
|
1130
1628
|
}
|
|
1131
1629
|
function StringSchema(data) {
|
|
1132
|
-
if (typeof data
|
|
1133
|
-
|
|
1630
|
+
if (typeof data === "string") {
|
|
1631
|
+
return data;
|
|
1134
1632
|
}
|
|
1135
|
-
|
|
1633
|
+
throw new Error(`Expected string but received ${describeValue(data)}`);
|
|
1136
1634
|
}
|
|
1137
1635
|
function JSONSchema(data) {
|
|
1138
|
-
if (typeof data === "object") {
|
|
1636
|
+
if (typeof data === "object" && data !== null) {
|
|
1139
1637
|
return data;
|
|
1140
1638
|
}
|
|
1141
|
-
if (
|
|
1142
|
-
|
|
1639
|
+
if (typeof data === "string") {
|
|
1640
|
+
const parsed = tryParseJson2(data);
|
|
1641
|
+
if (parsed !== void 0) {
|
|
1642
|
+
return parsed;
|
|
1643
|
+
}
|
|
1644
|
+
}
|
|
1645
|
+
throw new Error(`Expected JSON but received ${describeValue(data)}`);
|
|
1646
|
+
}
|
|
1647
|
+
function describeValue(value) {
|
|
1648
|
+
return `<${typeof value}> ${String(value)}`;
|
|
1649
|
+
}
|
|
1650
|
+
function tryParseJson2(value) {
|
|
1651
|
+
try {
|
|
1652
|
+
return JSON.parse(value);
|
|
1653
|
+
} catch {
|
|
1654
|
+
return void 0;
|
|
1655
|
+
}
|
|
1656
|
+
}
|
|
1657
|
+
|
|
1658
|
+
// src/plugins.ts
|
|
1659
|
+
function createLoggingPlugin(sink) {
|
|
1660
|
+
return {
|
|
1661
|
+
name: "http-logging",
|
|
1662
|
+
async onRequest(context) {
|
|
1663
|
+
await sink({
|
|
1664
|
+
type: "request",
|
|
1665
|
+
timestamp: Date.now(),
|
|
1666
|
+
request: context.request,
|
|
1667
|
+
options: context.options
|
|
1668
|
+
});
|
|
1669
|
+
},
|
|
1670
|
+
async onResponse(context) {
|
|
1671
|
+
await sink({
|
|
1672
|
+
type: "response",
|
|
1673
|
+
timestamp: Date.now(),
|
|
1674
|
+
request: context.request,
|
|
1675
|
+
response: context.response,
|
|
1676
|
+
options: context.options
|
|
1677
|
+
});
|
|
1678
|
+
},
|
|
1679
|
+
async onError(context) {
|
|
1680
|
+
await sink({
|
|
1681
|
+
type: "error",
|
|
1682
|
+
timestamp: Date.now(),
|
|
1683
|
+
request: context.request,
|
|
1684
|
+
error: context.error,
|
|
1685
|
+
options: context.options
|
|
1686
|
+
});
|
|
1687
|
+
}
|
|
1688
|
+
};
|
|
1689
|
+
}
|
|
1690
|
+
|
|
1691
|
+
// src/axios-transport.ts
|
|
1692
|
+
function createAxiosTransport(axios) {
|
|
1693
|
+
if (!axios || typeof axios.request !== "function") {
|
|
1694
|
+
throw new Error("Axios transport requires an axios-like client instance.");
|
|
1695
|
+
}
|
|
1696
|
+
return {
|
|
1697
|
+
async send(request, options = {}) {
|
|
1698
|
+
const {
|
|
1699
|
+
headers: optionHeaders,
|
|
1700
|
+
streamResponse,
|
|
1701
|
+
...restOptions
|
|
1702
|
+
} = options;
|
|
1703
|
+
const config = {
|
|
1704
|
+
url: request.fullUrl ?? "",
|
|
1705
|
+
method: request.method ?? "GET",
|
|
1706
|
+
headers: { ...request.headers },
|
|
1707
|
+
data: request.data,
|
|
1708
|
+
validateStatus: () => true,
|
|
1709
|
+
...restOptions
|
|
1710
|
+
};
|
|
1711
|
+
if (optionHeaders) {
|
|
1712
|
+
config.headers = mergeHeaders2(config.headers ?? {}, optionHeaders);
|
|
1713
|
+
}
|
|
1714
|
+
if (streamResponse) {
|
|
1715
|
+
config.responseType = config.responseType ?? "stream";
|
|
1716
|
+
}
|
|
1717
|
+
const response = await axios.request(config);
|
|
1718
|
+
return {
|
|
1719
|
+
status: response.status,
|
|
1720
|
+
statusText: response.statusText,
|
|
1721
|
+
headers: response.headers ?? {},
|
|
1722
|
+
data: response.data
|
|
1723
|
+
};
|
|
1724
|
+
}
|
|
1725
|
+
};
|
|
1726
|
+
}
|
|
1727
|
+
function mergeHeaders2(base, overrides) {
|
|
1728
|
+
const next = {};
|
|
1729
|
+
for (const [key, value] of Object.entries(base)) {
|
|
1730
|
+
if (value === void 0 || value === null) {
|
|
1731
|
+
continue;
|
|
1732
|
+
}
|
|
1733
|
+
next[key] = String(value);
|
|
1734
|
+
}
|
|
1735
|
+
for (const [key, value] of Object.entries(overrides)) {
|
|
1736
|
+
if (value === void 0 || value === null) {
|
|
1737
|
+
delete next[key];
|
|
1738
|
+
continue;
|
|
1739
|
+
}
|
|
1740
|
+
next[key] = String(value);
|
|
1741
|
+
}
|
|
1742
|
+
return next;
|
|
1743
|
+
}
|
|
1744
|
+
function ensureHttp(response, options = {}) {
|
|
1745
|
+
return new HttpEnsureChainImpl(response, {
|
|
1746
|
+
...options.label ? { label: options.label } : {},
|
|
1747
|
+
negated: Boolean(options.negated)
|
|
1748
|
+
});
|
|
1749
|
+
}
|
|
1750
|
+
var HttpEnsureChainImpl = class _HttpEnsureChainImpl {
|
|
1751
|
+
constructor(value, state) {
|
|
1752
|
+
this.value = value;
|
|
1753
|
+
this.label = state.label;
|
|
1754
|
+
this.negated = state.negated;
|
|
1755
|
+
this.normalized = normalizeResponse(value);
|
|
1756
|
+
}
|
|
1757
|
+
get not() {
|
|
1758
|
+
return new _HttpEnsureChainImpl(this.value, {
|
|
1759
|
+
...this.label ? { label: this.label } : {},
|
|
1760
|
+
negated: !this.negated
|
|
1761
|
+
});
|
|
1762
|
+
}
|
|
1763
|
+
toHaveStatus(expectation) {
|
|
1764
|
+
const { pass, description } = matchesStatus(
|
|
1765
|
+
this.normalized.status,
|
|
1766
|
+
expectation
|
|
1767
|
+
);
|
|
1768
|
+
if (shouldFail(pass, this.negated)) {
|
|
1769
|
+
const baseMessage = this.negated ? `Expected response status not to be ${description}` : `Expected response status to be ${description}`;
|
|
1770
|
+
this.fail({
|
|
1771
|
+
matcher: "toHaveStatus",
|
|
1772
|
+
message: baseMessage,
|
|
1773
|
+
actual: this.normalized.status,
|
|
1774
|
+
expected: description
|
|
1775
|
+
});
|
|
1776
|
+
}
|
|
1777
|
+
return this;
|
|
1778
|
+
}
|
|
1779
|
+
toHaveHeader(name, expectation) {
|
|
1780
|
+
const key = name.toLowerCase();
|
|
1781
|
+
const actual = this.normalized.headers[key];
|
|
1782
|
+
if (expectation === void 0) {
|
|
1783
|
+
const pass2 = actual !== void 0;
|
|
1784
|
+
if (shouldFail(pass2, this.negated)) {
|
|
1785
|
+
const baseMessage = this.negated ? `Expected response not to include header ${name}` : `Expected response to include header ${name}`;
|
|
1786
|
+
this.fail({
|
|
1787
|
+
matcher: "toHaveHeader",
|
|
1788
|
+
message: baseMessage,
|
|
1789
|
+
actual: actual ?? "<missing>",
|
|
1790
|
+
expected: name
|
|
1791
|
+
});
|
|
1792
|
+
}
|
|
1793
|
+
return this;
|
|
1794
|
+
}
|
|
1795
|
+
const pass = matchHeaderValue(actual, expectation);
|
|
1796
|
+
if (shouldFail(pass, this.negated)) {
|
|
1797
|
+
const baseMessage = this.negated ? `Expected header ${name} not to match` : `Expected header ${name} to match`;
|
|
1798
|
+
this.fail({
|
|
1799
|
+
matcher: "toHaveHeader",
|
|
1800
|
+
message: baseMessage,
|
|
1801
|
+
actual: actual ?? "<missing>",
|
|
1802
|
+
expected: expectation
|
|
1803
|
+
});
|
|
1804
|
+
}
|
|
1805
|
+
return this;
|
|
1806
|
+
}
|
|
1807
|
+
toBeCacheable(expectation = {}) {
|
|
1808
|
+
const cacheControl = this.normalized.headers["cache-control"];
|
|
1809
|
+
if (!cacheControl) {
|
|
1810
|
+
if (!this.negated) {
|
|
1811
|
+
this.fail({
|
|
1812
|
+
matcher: "toBeCacheable",
|
|
1813
|
+
message: "Expected Cache-Control header to be present",
|
|
1814
|
+
actual: "<missing>"
|
|
1815
|
+
});
|
|
1816
|
+
}
|
|
1817
|
+
return this;
|
|
1818
|
+
}
|
|
1819
|
+
const directives = parseCacheControl(cacheControl);
|
|
1820
|
+
const impliedCacheable = !("no-store" in directives || "no-cache" in directives);
|
|
1821
|
+
const expectationPass = evaluateCacheExpectations(directives, expectation);
|
|
1822
|
+
const pass = impliedCacheable && expectationPass;
|
|
1823
|
+
if (shouldFail(pass, this.negated)) {
|
|
1824
|
+
const baseMessage = this.negated ? "Expected response not to advertise cacheable directives" : "Expected response to advertise cacheable directives";
|
|
1825
|
+
this.fail({
|
|
1826
|
+
matcher: "toBeCacheable",
|
|
1827
|
+
message: baseMessage,
|
|
1828
|
+
actual: directives,
|
|
1829
|
+
expected: expectation
|
|
1830
|
+
});
|
|
1831
|
+
}
|
|
1832
|
+
return this;
|
|
1833
|
+
}
|
|
1834
|
+
toHaveCorrelationId(headerName = "x-correlation-id") {
|
|
1835
|
+
const key = headerName.toLowerCase();
|
|
1836
|
+
const value = this.normalized.headers[key];
|
|
1837
|
+
const pass = typeof value === "string" && value.trim().length > 0;
|
|
1838
|
+
if (shouldFail(pass, this.negated)) {
|
|
1839
|
+
const baseMessage = this.negated ? `Expected header ${headerName} to be missing or empty` : `Expected header ${headerName} to be present`;
|
|
1840
|
+
this.fail({
|
|
1841
|
+
matcher: "toHaveCorrelationId",
|
|
1842
|
+
message: baseMessage,
|
|
1843
|
+
actual: value ?? "<missing>",
|
|
1844
|
+
expected: headerName
|
|
1845
|
+
});
|
|
1846
|
+
}
|
|
1847
|
+
return this;
|
|
1848
|
+
}
|
|
1849
|
+
fail(details) {
|
|
1850
|
+
const merged = {
|
|
1851
|
+
...details,
|
|
1852
|
+
...this.label ? { receivedLabel: this.label } : {}
|
|
1853
|
+
};
|
|
1854
|
+
throw new EnsureError(merged);
|
|
1855
|
+
}
|
|
1856
|
+
};
|
|
1857
|
+
function shouldFail(pass, negated) {
|
|
1858
|
+
return negated ? pass : !pass;
|
|
1859
|
+
}
|
|
1860
|
+
function normalizeResponse(response) {
|
|
1861
|
+
return {
|
|
1862
|
+
status: response.status,
|
|
1863
|
+
statusText: response.statusText ?? "",
|
|
1864
|
+
headers: normalizeHeaders2(response.headers),
|
|
1865
|
+
original: response.raw ?? response
|
|
1866
|
+
};
|
|
1867
|
+
}
|
|
1868
|
+
function normalizeHeaders2(source) {
|
|
1869
|
+
const result = {};
|
|
1870
|
+
if (typeof source.get === "function") {
|
|
1871
|
+
const entries = source.entries;
|
|
1872
|
+
const iterator = entries ? entries.call(source) : source[Symbol.iterator]?.call(source);
|
|
1873
|
+
if (iterator) {
|
|
1874
|
+
for (const [name, value] of iterator) {
|
|
1875
|
+
result[String(name).toLowerCase()] = String(value);
|
|
1876
|
+
}
|
|
1877
|
+
return result;
|
|
1878
|
+
}
|
|
1879
|
+
}
|
|
1880
|
+
for (const [key, value] of Object.entries(
|
|
1881
|
+
source
|
|
1882
|
+
)) {
|
|
1883
|
+
result[String(key).toLowerCase()] = String(value);
|
|
1143
1884
|
}
|
|
1144
|
-
const result = JSON.parse(data);
|
|
1145
1885
|
return result;
|
|
1146
1886
|
}
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1887
|
+
function matchesStatus(status, expectation) {
|
|
1888
|
+
if (typeof expectation === "number") {
|
|
1889
|
+
return {
|
|
1890
|
+
pass: status === expectation,
|
|
1891
|
+
description: expectation.toString()
|
|
1892
|
+
};
|
|
1893
|
+
}
|
|
1894
|
+
if (typeof expectation === "string") {
|
|
1895
|
+
const digit = Number.parseInt(expectation.charAt(0), 10);
|
|
1896
|
+
if (!Number.isNaN(digit) && expectation.endsWith("xx")) {
|
|
1897
|
+
const min = digit * 100;
|
|
1898
|
+
return {
|
|
1899
|
+
pass: status >= min && status < min + 100,
|
|
1900
|
+
description: `${digit}xx`
|
|
1901
|
+
};
|
|
1902
|
+
}
|
|
1903
|
+
return {
|
|
1904
|
+
pass: status.toString() === expectation,
|
|
1905
|
+
description: expectation
|
|
1906
|
+
};
|
|
1907
|
+
}
|
|
1908
|
+
if (Array.isArray(expectation)) {
|
|
1909
|
+
const [min, max] = expectation;
|
|
1910
|
+
return {
|
|
1911
|
+
pass: status >= min && status <= max,
|
|
1912
|
+
description: `[${min}, ${max}]`
|
|
1913
|
+
};
|
|
1914
|
+
}
|
|
1915
|
+
if (typeof expectation === "function") {
|
|
1916
|
+
return {
|
|
1917
|
+
pass: expectation(status),
|
|
1918
|
+
description: "predicate(status)"
|
|
1919
|
+
};
|
|
1920
|
+
}
|
|
1921
|
+
const range = expectation;
|
|
1922
|
+
return {
|
|
1923
|
+
pass: status >= range.min && status <= range.max,
|
|
1924
|
+
description: `[${range.min}, ${range.max}]`
|
|
1925
|
+
};
|
|
1926
|
+
}
|
|
1927
|
+
function matchHeaderValue(actual, expected) {
|
|
1928
|
+
if (actual === void 0) {
|
|
1929
|
+
return false;
|
|
1930
|
+
}
|
|
1931
|
+
if (typeof expected === "string") {
|
|
1932
|
+
return actual === expected;
|
|
1933
|
+
}
|
|
1934
|
+
if (expected instanceof RegExp) {
|
|
1935
|
+
return expected.test(actual);
|
|
1936
|
+
}
|
|
1937
|
+
if (Array.isArray(expected)) {
|
|
1938
|
+
const list = expected;
|
|
1939
|
+
const actualParts = actual.split(",").map((part) => part.trim()).filter(Boolean);
|
|
1940
|
+
return list.every((value) => actualParts.includes(value));
|
|
1941
|
+
}
|
|
1942
|
+
return expected(actual);
|
|
1943
|
+
}
|
|
1944
|
+
function parseCacheControl(value) {
|
|
1945
|
+
const directives = {};
|
|
1946
|
+
for (const segment of value.split(",")) {
|
|
1947
|
+
const trimmed = segment.trim();
|
|
1948
|
+
if (!trimmed) {
|
|
1949
|
+
continue;
|
|
1950
|
+
}
|
|
1951
|
+
const [rawName, rawParameter] = trimmed.split("=", 2);
|
|
1952
|
+
const name = rawName?.trim();
|
|
1953
|
+
if (!name) {
|
|
1954
|
+
continue;
|
|
1955
|
+
}
|
|
1956
|
+
if (rawParameter === void 0) {
|
|
1957
|
+
directives[name.toLowerCase()] = true;
|
|
1958
|
+
continue;
|
|
1959
|
+
}
|
|
1960
|
+
const parameter = rawParameter.trim().replace(/^"|"$/g, "");
|
|
1961
|
+
directives[name.toLowerCase()] = parameter;
|
|
1962
|
+
}
|
|
1963
|
+
return directives;
|
|
1964
|
+
}
|
|
1965
|
+
function evaluateCacheExpectations(directives, expectation) {
|
|
1966
|
+
if (expectation.cacheability) {
|
|
1967
|
+
if (!(expectation.cacheability in directives)) {
|
|
1968
|
+
return false;
|
|
1969
|
+
}
|
|
1970
|
+
}
|
|
1971
|
+
if (expectation.maxAge !== void 0) {
|
|
1972
|
+
const raw = directives["max-age"];
|
|
1973
|
+
if (typeof raw !== "string") {
|
|
1974
|
+
return false;
|
|
1975
|
+
}
|
|
1976
|
+
const parsed = Number.parseInt(raw, 10);
|
|
1977
|
+
if (Number.isNaN(parsed)) {
|
|
1978
|
+
return false;
|
|
1979
|
+
}
|
|
1980
|
+
if (typeof expectation.maxAge === "number") {
|
|
1981
|
+
if (parsed !== expectation.maxAge) {
|
|
1982
|
+
return false;
|
|
1983
|
+
}
|
|
1984
|
+
} else {
|
|
1985
|
+
const { min, max } = expectation.maxAge;
|
|
1986
|
+
if (min !== void 0 && parsed < min) {
|
|
1987
|
+
return false;
|
|
1988
|
+
}
|
|
1989
|
+
if (max !== void 0 && parsed > max) {
|
|
1990
|
+
return false;
|
|
1991
|
+
}
|
|
1992
|
+
}
|
|
1993
|
+
}
|
|
1994
|
+
if (expectation.sMaxAge !== void 0) {
|
|
1995
|
+
const raw = directives["s-maxage"];
|
|
1996
|
+
if (typeof raw !== "string") {
|
|
1997
|
+
return false;
|
|
1998
|
+
}
|
|
1999
|
+
const parsed = Number.parseInt(raw, 10);
|
|
2000
|
+
if (Number.isNaN(parsed) || parsed !== expectation.sMaxAge) {
|
|
2001
|
+
return false;
|
|
2002
|
+
}
|
|
2003
|
+
}
|
|
2004
|
+
if (expectation.revalidate) {
|
|
2005
|
+
if (!("must-revalidate" in directives || "proxy-revalidate" in directives)) {
|
|
2006
|
+
return false;
|
|
2007
|
+
}
|
|
2008
|
+
}
|
|
2009
|
+
if (expectation.immutable && !("immutable" in directives)) {
|
|
2010
|
+
return false;
|
|
2011
|
+
}
|
|
2012
|
+
return true;
|
|
2013
|
+
}
|
|
2014
|
+
|
|
2015
|
+
// src/assertions/http-assertions-plugin.ts
|
|
2016
|
+
var httpAssertionsPlugin = () => ({ isNot }) => (_world) => {
|
|
2017
|
+
const facet = (response, options) => {
|
|
2018
|
+
return ensureHttp(response, {
|
|
2019
|
+
...options?.label ? { label: options.label } : {},
|
|
2020
|
+
negated: isNot
|
|
2021
|
+
});
|
|
2022
|
+
};
|
|
2023
|
+
return facet;
|
|
2024
|
+
};
|
|
2025
|
+
|
|
2026
|
+
// src/assertions/http-adapters.ts
|
|
2027
|
+
function fromHttpResponse(response) {
|
|
2028
|
+
return {
|
|
2029
|
+
status: response.status,
|
|
2030
|
+
statusText: response.statusText ?? "",
|
|
2031
|
+
headers: response.headers,
|
|
2032
|
+
data: response.data,
|
|
2033
|
+
raw: response
|
|
2034
|
+
};
|
|
2035
|
+
}
|
|
2036
|
+
function fromFetchResponse(response, data) {
|
|
2037
|
+
return {
|
|
2038
|
+
status: response.status,
|
|
2039
|
+
statusText: response.statusText ?? "",
|
|
2040
|
+
headers: response.headers,
|
|
2041
|
+
data,
|
|
2042
|
+
raw: response
|
|
2043
|
+
};
|
|
2044
|
+
}
|
|
2045
|
+
|
|
2046
|
+
export { AnySchema, BooleanSchema, EmptySchema, HTTP, HTTPError, HTTPRequest, HTTPRequestBuilder, HTTPResponse, HTTPResponseBuilder, HTTPSchemaValidationError, HTTPTransportError, JSONSchema, MetaConfig, MetaConfigBuilder, NullSchema, NumberSchema, SchemaMap, StringSchema, UndefinedSchema, createAxiosTransport, createFetchTransport, createLoggingPlugin, ensureHttp, fromFetchResponse, fromHttpResponse, httpAssertionsPlugin, transformResponse };
|
|
2047
|
+
//# sourceMappingURL=out.js.map
|
|
1166
2048
|
//# sourceMappingURL=index.js.map
|