@utiliread/http 1.17.7 → 1.18.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.vscode/settings.json +2 -2
- package/dist/cjs/event-aggregator.js +29 -29
- package/dist/cjs/event-aggregator.js.map +1 -1
- package/dist/cjs/events.js +18 -18
- package/dist/cjs/events.js.map +1 -1
- package/dist/cjs/helpers.js +15 -15
- package/dist/cjs/http-builder.js +191 -191
- package/dist/cjs/http-builder.js.map +1 -1
- package/dist/cjs/http-error.js +29 -29
- package/dist/cjs/http-error.js.map +1 -1
- package/dist/cjs/http-response.js +50 -50
- package/dist/cjs/http-response.js.map +1 -1
- package/dist/cjs/http.js +95 -95
- package/dist/cjs/http.js.map +1 -1
- package/dist/cjs/http.spec.js +68 -68
- package/dist/cjs/http.spec.js.map +1 -1
- package/dist/cjs/index.js +37 -37
- package/dist/cjs/json/index.js +73 -73
- package/dist/cjs/jsonpatch/index.js +14 -14
- package/dist/cjs/mapping.js +36 -36
- package/dist/cjs/mapping.js.map +1 -1
- package/dist/cjs/msgpack/index.js +34 -34
- package/dist/cjs/msgpack/index.js.map +1 -1
- package/dist/cjs/pagination.js +2 -2
- package/dist/cjs/problem-details.js +2 -2
- package/dist/cjs/query-string.js +67 -65
- package/dist/cjs/query-string.js.map +1 -1
- package/dist/cjs/query-string.spec.js +51 -51
- package/dist/cjs/status-codes.js +70 -70
- package/dist/cjs/timeout-error.js +13 -13
- package/dist/esm/event-aggregator.d.ts +11 -11
- package/dist/esm/event-aggregator.js +25 -25
- package/dist/esm/event-aggregator.js.map +1 -1
- package/dist/esm/events.d.ts +11 -11
- package/dist/esm/events.js +14 -14
- package/dist/esm/events.js.map +1 -1
- package/dist/esm/helpers.d.ts +5 -5
- package/dist/esm/helpers.js +9 -9
- package/dist/esm/http-builder.d.ts +60 -60
- package/dist/esm/http-builder.js +186 -186
- package/dist/esm/http-builder.js.map +1 -1
- package/dist/esm/http-error.d.ts +10 -10
- package/dist/esm/http-error.js +25 -25
- package/dist/esm/http-error.js.map +1 -1
- package/dist/esm/http-response.d.ts +18 -18
- package/dist/esm/http-response.js +45 -45
- package/dist/esm/http-response.js.map +1 -1
- package/dist/esm/http.d.ts +33 -33
- package/dist/esm/http.js +91 -91
- package/dist/esm/http.js.map +1 -1
- package/dist/esm/http.spec.d.ts +1 -1
- package/dist/esm/http.spec.js +66 -66
- package/dist/esm/http.spec.js.map +1 -1
- package/dist/esm/index.d.ts +15 -15
- package/dist/esm/index.js +10 -10
- package/dist/esm/json/index.d.ts +15 -15
- package/dist/esm/json/index.js +71 -71
- package/dist/esm/jsonpatch/index.d.ts +9 -9
- package/dist/esm/jsonpatch/index.js +12 -12
- package/dist/esm/mapping.d.ts +9 -9
- package/dist/esm/mapping.js +31 -31
- package/dist/esm/mapping.js.map +1 -1
- package/dist/esm/msgpack/index.d.ts +8 -8
- package/dist/esm/msgpack/index.js +32 -32
- package/dist/esm/msgpack/index.js.map +1 -1
- package/dist/esm/pagination.d.ts +23 -23
- package/dist/esm/pagination.js +1 -1
- package/dist/esm/problem-details.d.ts +7 -7
- package/dist/esm/problem-details.js +1 -1
- package/dist/esm/query-string.d.ts +6 -6
- package/dist/esm/query-string.js +63 -61
- package/dist/esm/query-string.js.map +1 -1
- package/dist/esm/query-string.spec.d.ts +1 -1
- package/dist/esm/query-string.spec.js +49 -49
- package/dist/esm/status-codes.d.ts +67 -67
- package/dist/esm/status-codes.js +67 -67
- package/dist/esm/timeout-error.d.ts +3 -3
- package/dist/esm/timeout-error.js +9 -9
- package/package.json +15 -17
- package/plugins/json/node_modules/@utiliread/http/package.json +4 -0
- package/plugins/json/src/index.ts +124 -122
- package/plugins/jsonpatch/node_modules/@utiliread/http/package.json +4 -0
- package/plugins/jsonpatch/src/index.ts +31 -31
- package/plugins/msgpack/node_modules/@utiliread/http/package.json +4 -0
- package/plugins/msgpack/src/index.ts +67 -60
- package/src/event-aggregator.ts +21 -21
- package/src/events.ts +39 -39
- package/src/helpers.ts +6 -6
- package/src/http-builder.ts +252 -218
- package/src/http-error.ts +30 -23
- package/src/http-response.ts +1 -1
- package/src/http.spec.ts +1 -1
- package/src/http.ts +8 -5
- package/src/index.ts +1 -1
- package/src/mapping.ts +40 -32
- package/src/pagination.ts +16 -16
- package/src/problem-details.ts +6 -6
- package/src/query-string.spec.ts +59 -43
- package/src/query-string.ts +56 -58
- package/src/status-codes.ts +67 -67
- package/src/timeout-error.ts +7 -7
- package/tsconfig.cjs.json +1 -0
- package/tsconfig.json +1 -1
- package/web-test-runner.config.mjs +7 -0
- package/karma.config.js +0 -29
- package/src/mapping.d.ts +0 -9
- package/src/mapping.js +0 -32
- package/src/mapping.js.map +0 -1
package/src/http-builder.ts
CHANGED
|
@@ -1,255 +1,289 @@
|
|
|
1
|
-
import { Fetch } from
|
|
2
|
-
import { HttpResponse, HttpResponseOfT } from
|
|
3
|
-
import { TimeoutError } from
|
|
4
|
-
import { EventAggregator } from
|
|
5
|
-
import { Http } from
|
|
1
|
+
import { Fetch } from "./http";
|
|
2
|
+
import { HttpResponse, HttpResponseOfT } from "./http-response";
|
|
3
|
+
import { TimeoutError } from "./timeout-error";
|
|
4
|
+
import { EventAggregator } from "./event-aggregator";
|
|
5
|
+
import { Http } from "./http";
|
|
6
6
|
|
|
7
7
|
export class HttpBuilder {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
const init: RequestInit = {
|
|
42
|
-
method: this.message.method,
|
|
43
|
-
body: this.message.content,
|
|
44
|
-
headers: this.message.headers,
|
|
45
|
-
mode: this.message.mode
|
|
46
|
-
};
|
|
47
|
-
|
|
48
|
-
if (abortSignal || this.options.timeout) {
|
|
49
|
-
var outerController = new AbortController();
|
|
50
|
-
if (abortSignal) {
|
|
51
|
-
abortSignal.addEventListener("abort", () => {
|
|
52
|
-
outerController.abort();
|
|
53
|
-
});
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
init.signal = outerController.signal;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
const fetchResponsePromise = this.options.fetch(this.message.url, init);
|
|
60
|
-
let fetchResponse: Response;
|
|
61
|
-
|
|
62
|
-
if (this.options.timeout) {
|
|
63
|
-
fetchResponse = await Promise.race([
|
|
64
|
-
fetchResponsePromise,
|
|
65
|
-
new Promise<Response>((_, reject) => setTimeout(() => {
|
|
66
|
-
outerController.abort();
|
|
67
|
-
reject(new TimeoutError());
|
|
68
|
-
}, this.options.timeout))
|
|
69
|
-
]);
|
|
70
|
-
}
|
|
71
|
-
else {
|
|
72
|
-
fetchResponse = await fetchResponsePromise;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
const httpResponse = new HttpResponse(fetchResponse);
|
|
76
|
-
|
|
77
|
-
if (this._ensureSuccessStatusCode) {
|
|
78
|
-
httpResponse.ensureSuccessfulStatusCode();
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
await this._onSent.publish(httpResponse, this.message);
|
|
82
|
-
await this.http._onSent.publish(httpResponse, this.message);
|
|
83
|
-
|
|
84
|
-
return httpResponse;
|
|
8
|
+
private _ensureSuccessStatusCode = true;
|
|
9
|
+
private _onSend = new EventAggregator<[Message]>();
|
|
10
|
+
private _onSent = new EventAggregator<[HttpResponse, Message]>();
|
|
11
|
+
|
|
12
|
+
constructor(
|
|
13
|
+
public message: Message,
|
|
14
|
+
public options: RequestOptions,
|
|
15
|
+
/** @internal */ public http: Http,
|
|
16
|
+
) {}
|
|
17
|
+
|
|
18
|
+
onSend(callback: (request: Message) => void | Promise<void>) {
|
|
19
|
+
this._onSend.subscribe(callback);
|
|
20
|
+
return this;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
onSent(
|
|
24
|
+
callback: (
|
|
25
|
+
response: HttpResponse,
|
|
26
|
+
request: Message,
|
|
27
|
+
) => void | Promise<void>,
|
|
28
|
+
) {
|
|
29
|
+
this._onSent.subscribe(callback);
|
|
30
|
+
return this;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
useHandler<T>(handler: (response: HttpResponse) => Promise<T>) {
|
|
34
|
+
return new HttpBuilderOfT<T>(this, handler);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
async send(abortSignal?: AbortSignal) {
|
|
38
|
+
if (this.message.contentType) {
|
|
39
|
+
this.message.headers.set("Content-Type", this.message.contentType);
|
|
85
40
|
}
|
|
86
41
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
return this;
|
|
109
|
-
}
|
|
42
|
+
// Resolve the final url and assign it to the message
|
|
43
|
+
// This makes the final url apper in onSend, onSent, and on Received handlers
|
|
44
|
+
this.message.url = this.getUrl();
|
|
45
|
+
|
|
46
|
+
await this._onSend.publish(this.message);
|
|
47
|
+
await this.http._onSend.publish(this.message);
|
|
48
|
+
|
|
49
|
+
const init: RequestInit = {
|
|
50
|
+
method: this.message.method,
|
|
51
|
+
body: this.message.content,
|
|
52
|
+
headers: this.message.headers,
|
|
53
|
+
mode: this.message.mode,
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
if (abortSignal || this.options.timeout) {
|
|
57
|
+
var outerController = new AbortController();
|
|
58
|
+
if (abortSignal) {
|
|
59
|
+
abortSignal.addEventListener("abort", () => {
|
|
60
|
+
outerController.abort();
|
|
61
|
+
});
|
|
62
|
+
}
|
|
110
63
|
|
|
111
|
-
|
|
112
|
-
this.message.mode = mode;
|
|
113
|
-
return this;
|
|
64
|
+
init.signal = outerController.signal;
|
|
114
65
|
}
|
|
115
66
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
67
|
+
const fetchResponsePromise = this.options.fetch(this.message.url, init);
|
|
68
|
+
let fetchResponse: Response;
|
|
69
|
+
|
|
70
|
+
if (this.options.timeout) {
|
|
71
|
+
fetchResponse = await Promise.race([
|
|
72
|
+
fetchResponsePromise,
|
|
73
|
+
new Promise<Response>((_, reject) =>
|
|
74
|
+
setTimeout(() => {
|
|
75
|
+
outerController.abort();
|
|
76
|
+
reject(new TimeoutError());
|
|
77
|
+
}, this.options.timeout),
|
|
78
|
+
),
|
|
79
|
+
]);
|
|
80
|
+
} else {
|
|
81
|
+
fetchResponse = await fetchResponsePromise;
|
|
119
82
|
}
|
|
120
83
|
|
|
121
|
-
|
|
84
|
+
const httpResponse = new HttpResponse(fetchResponse);
|
|
122
85
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
this.message.contentType = contentType;
|
|
126
|
-
return this;
|
|
86
|
+
if (this._ensureSuccessStatusCode) {
|
|
87
|
+
httpResponse.ensureSuccessfulStatusCode();
|
|
127
88
|
}
|
|
128
89
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
this.message.contentType = undefined;
|
|
132
|
-
return this;
|
|
133
|
-
}
|
|
90
|
+
await this._onSent.publish(httpResponse, this.message);
|
|
91
|
+
await this.http._onSent.publish(httpResponse, this.message);
|
|
134
92
|
|
|
135
|
-
|
|
93
|
+
return httpResponse;
|
|
94
|
+
}
|
|
136
95
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
96
|
+
getUrl() {
|
|
97
|
+
let baseUrl = this.options.baseUrl;
|
|
98
|
+
if (!baseUrl) {
|
|
99
|
+
return this.message.url;
|
|
140
100
|
}
|
|
141
101
|
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
expectString() {
|
|
145
|
-
return this.useHandler(response => {
|
|
146
|
-
return response.rawResponse.text();
|
|
147
|
-
});
|
|
102
|
+
if (baseUrl.endsWith("/")) {
|
|
103
|
+
baseUrl = baseUrl.substr(0, baseUrl.length - 1);
|
|
148
104
|
}
|
|
149
105
|
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
106
|
+
if (this.message.url.startsWith("/")) {
|
|
107
|
+
return baseUrl + this.message.url;
|
|
108
|
+
} else {
|
|
109
|
+
return baseUrl + "/" + this.message.url;
|
|
154
110
|
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
ensureSuccessStatusCode(ensureSuccessStatusCode?: boolean) {
|
|
114
|
+
this._ensureSuccessStatusCode =
|
|
115
|
+
ensureSuccessStatusCode === false ? false : true;
|
|
116
|
+
|
|
117
|
+
return this;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
useCors(mode: RequestMode) {
|
|
121
|
+
this.message.mode = mode;
|
|
122
|
+
return this;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
useTimeout(timeout: number | null) {
|
|
126
|
+
this.options.timeout = timeout || undefined;
|
|
127
|
+
return this;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// Content Extensions
|
|
131
|
+
|
|
132
|
+
with(content: any, contentType?: string) {
|
|
133
|
+
this.message.content = content;
|
|
134
|
+
this.message.contentType = contentType;
|
|
135
|
+
return this;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
withForm(content: FormData) {
|
|
139
|
+
this.message.content = content;
|
|
140
|
+
this.message.contentType = undefined;
|
|
141
|
+
return this;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// Modifier Extensions
|
|
145
|
+
|
|
146
|
+
addHeader(name: string, value: string) {
|
|
147
|
+
this.message.headers.append(name, value);
|
|
148
|
+
return this;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// Expect Extensions
|
|
152
|
+
|
|
153
|
+
expectString() {
|
|
154
|
+
return this.useHandler((response) => {
|
|
155
|
+
return response.rawResponse.text();
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
expectBinary() {
|
|
160
|
+
return this.useHandler((response) => {
|
|
161
|
+
return response.rawResponse.arrayBuffer();
|
|
162
|
+
});
|
|
163
|
+
}
|
|
155
164
|
}
|
|
156
165
|
|
|
157
166
|
export class HttpBuilderOfT<T> extends HttpBuilder {
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
167
|
+
private _onReceived = new EventAggregator<[HttpResponseOfT<T>, Message, T]>();
|
|
168
|
+
|
|
169
|
+
constructor(
|
|
170
|
+
private inner: HttpBuilder,
|
|
171
|
+
private handler: (response: HttpResponse) => Promise<T>,
|
|
172
|
+
) {
|
|
173
|
+
super(inner.message, inner.options, inner.http);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
onSend(callback: (request: Message) => void | Promise<void>) {
|
|
177
|
+
this.inner.onSend(callback);
|
|
178
|
+
return this;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
onSent(
|
|
182
|
+
callback: (
|
|
183
|
+
response: HttpResponse,
|
|
184
|
+
request: Message,
|
|
185
|
+
) => void | Promise<void>,
|
|
186
|
+
) {
|
|
187
|
+
this.inner.onSent(callback);
|
|
188
|
+
return this;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
ensureSuccessStatusCode(ensureSuccessStatusCode?: boolean) {
|
|
192
|
+
this.inner.ensureSuccessStatusCode(ensureSuccessStatusCode);
|
|
193
|
+
return this;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
useCors(mode: RequestMode) {
|
|
197
|
+
this.inner.useCors(mode);
|
|
198
|
+
return this;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
useTimeout(timeout: number) {
|
|
202
|
+
this.inner.useTimeout(timeout);
|
|
203
|
+
return this;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
allowEmptyResponse() {
|
|
207
|
+
if (this._onReceived.any) {
|
|
208
|
+
throw new Error(
|
|
209
|
+
"onReceived() must be called after allowEmptyResponse() because the callback type changes",
|
|
210
|
+
);
|
|
172
211
|
}
|
|
173
212
|
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
return
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
const request = this.message;
|
|
220
|
-
const value = await response.receive();
|
|
221
|
-
|
|
222
|
-
await this._onReceived.publish(response, request, value);
|
|
223
|
-
await this.http._onReceived.publish(response, request, value);
|
|
224
|
-
|
|
225
|
-
return value;
|
|
226
|
-
}
|
|
213
|
+
return new HttpBuilderOfT<T | null>(this.inner, (response) => {
|
|
214
|
+
if (response.statusCode === 204) {
|
|
215
|
+
return Promise.resolve(null);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
return this.handler(response);
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
onReceived(
|
|
223
|
+
callback: (
|
|
224
|
+
response: HttpResponseOfT<T>,
|
|
225
|
+
request: Message,
|
|
226
|
+
value: T,
|
|
227
|
+
) => void | Promise<void>,
|
|
228
|
+
) {
|
|
229
|
+
this._onReceived.subscribe(callback);
|
|
230
|
+
return this;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
send(abortSignal?: AbortSignal) {
|
|
234
|
+
const responsePromise = this.inner
|
|
235
|
+
.send(abortSignal)
|
|
236
|
+
.then((x) => new HttpResponseOfT<T>(x.rawResponse, this.handler));
|
|
237
|
+
|
|
238
|
+
return asSendPromise(responsePromise, () =>
|
|
239
|
+
responsePromise.then((response) => this.handleReceive(response)),
|
|
240
|
+
);
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
transfer(abortSignal?: AbortSignal) {
|
|
244
|
+
return this.send(abortSignal).then((response) =>
|
|
245
|
+
this.handleReceive(response),
|
|
246
|
+
);
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
private async handleReceive(response: HttpResponseOfT<T>) {
|
|
250
|
+
const request = this.message;
|
|
251
|
+
const value = await response.receive();
|
|
252
|
+
|
|
253
|
+
await this._onReceived.publish(response, request, value);
|
|
254
|
+
await this.http._onReceived.publish(response, request, value);
|
|
255
|
+
|
|
256
|
+
return value;
|
|
257
|
+
}
|
|
227
258
|
}
|
|
228
259
|
|
|
229
260
|
export type HttpMethod = "HEAD" | "POST" | "GET" | "PUT" | "PATCH" | "DELETE";
|
|
230
261
|
|
|
231
262
|
export interface Message {
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
263
|
+
method: HttpMethod;
|
|
264
|
+
url: string;
|
|
265
|
+
headers: Headers;
|
|
266
|
+
content?: any;
|
|
267
|
+
contentType?: string;
|
|
268
|
+
mode?: RequestMode;
|
|
269
|
+
properties: { [key: string]: any };
|
|
239
270
|
}
|
|
240
271
|
|
|
241
272
|
export interface RequestOptions {
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
273
|
+
fetch: Fetch;
|
|
274
|
+
timeout?: number;
|
|
275
|
+
baseUrl?: string;
|
|
245
276
|
}
|
|
246
277
|
|
|
247
278
|
export interface SendPromise<T> extends Promise<HttpResponseOfT<T>> {
|
|
248
|
-
|
|
279
|
+
thenReceive(): Promise<T>;
|
|
249
280
|
}
|
|
250
281
|
|
|
251
|
-
function asSendPromise<T>(
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
282
|
+
function asSendPromise<T>(
|
|
283
|
+
responsePromise: Promise<HttpResponse>,
|
|
284
|
+
thenReceive: () => Promise<T>,
|
|
285
|
+
): SendPromise<T> {
|
|
286
|
+
const sendPromise = responsePromise as SendPromise<T>;
|
|
287
|
+
sendPromise.thenReceive = thenReceive;
|
|
288
|
+
return sendPromise;
|
|
289
|
+
}
|
package/src/http-error.ts
CHANGED
|
@@ -1,31 +1,38 @@
|
|
|
1
|
-
import { HttpResponse } from
|
|
2
|
-
import { ProblemDetails } from
|
|
1
|
+
import { HttpResponse } from "./http-response";
|
|
2
|
+
import { ProblemDetails } from "./problem-details";
|
|
3
3
|
|
|
4
4
|
export class HttpError extends Error {
|
|
5
|
-
|
|
5
|
+
private detailsPromise?: Promise<ProblemDetails>;
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
constructor(public statusCode: number, private response: HttpResponse | undefined = undefined) {
|
|
13
|
-
super(`The response was not successful: ${statusCode}`);
|
|
14
|
-
this.name = 'HttpError';
|
|
7
|
+
get hasDetails() {
|
|
8
|
+
const contentType = this.response?.rawResponse?.headers.get("Content-Type");
|
|
9
|
+
return contentType?.includes("application/problem+json");
|
|
10
|
+
}
|
|
15
11
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
12
|
+
constructor(
|
|
13
|
+
public statusCode: number,
|
|
14
|
+
private response: HttpResponse | undefined = undefined,
|
|
15
|
+
) {
|
|
16
|
+
super(`The response was not successful: ${statusCode}`);
|
|
17
|
+
this.name = "HttpError";
|
|
20
18
|
|
|
21
|
-
|
|
22
|
-
|
|
19
|
+
// Set the prototype explicitly to allow for "... instanceof HttpError",
|
|
20
|
+
// see https://github.com/Microsoft/TypeScript-wiki/blob/master/Breaking-Changes.md#extending-built-ins-like-error-array-and-map-may-no-longer-work
|
|
21
|
+
Object.setPrototypeOf(this, HttpError.prototype);
|
|
22
|
+
}
|
|
23
23
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
return this.detailsPromise.then(details => <TDetails>details);
|
|
27
|
-
}
|
|
24
|
+
details<TDetails = ProblemDetails>() {
|
|
25
|
+
const rawResponse = this.response?.rawResponse;
|
|
28
26
|
|
|
29
|
-
|
|
27
|
+
if (rawResponse && this.hasDetails) {
|
|
28
|
+
this.detailsPromise ??= rawResponse
|
|
29
|
+
.json()
|
|
30
|
+
.then((details) => <ProblemDetails>details);
|
|
31
|
+
return this.detailsPromise.then((details) => <TDetails>details);
|
|
30
32
|
}
|
|
31
|
-
|
|
33
|
+
|
|
34
|
+
return Promise.reject(
|
|
35
|
+
new Error("There are no problem details in the response"),
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
}
|
package/src/http-response.ts
CHANGED
|
@@ -47,7 +47,7 @@ export class HttpResponse {
|
|
|
47
47
|
export class HttpResponseOfT<T> extends HttpResponse {
|
|
48
48
|
constructor(
|
|
49
49
|
rawResponse: Response,
|
|
50
|
-
private handler: (response: HttpResponse) => Promise<T
|
|
50
|
+
private handler: (response: HttpResponse) => Promise<T>,
|
|
51
51
|
) {
|
|
52
52
|
super(rawResponse);
|
|
53
53
|
}
|
package/src/http.spec.ts
CHANGED
package/src/http.ts
CHANGED
|
@@ -6,7 +6,7 @@ import { QueryString } from "./query-string";
|
|
|
6
6
|
|
|
7
7
|
export type Fetch = (
|
|
8
8
|
input: RequestInfo,
|
|
9
|
-
init?: RequestInit
|
|
9
|
+
init?: RequestInit,
|
|
10
10
|
) => Promise<Response>;
|
|
11
11
|
|
|
12
12
|
export class Http {
|
|
@@ -81,7 +81,7 @@ export class Http {
|
|
|
81
81
|
timeout: this.options.timeout,
|
|
82
82
|
baseUrl: this.options.baseUrl,
|
|
83
83
|
},
|
|
84
|
-
this
|
|
84
|
+
this,
|
|
85
85
|
);
|
|
86
86
|
return builder;
|
|
87
87
|
}
|
|
@@ -115,7 +115,10 @@ export class Http {
|
|
|
115
115
|
}
|
|
116
116
|
|
|
117
117
|
onSent(
|
|
118
|
-
callback: (
|
|
118
|
+
callback: (
|
|
119
|
+
response: HttpResponse,
|
|
120
|
+
request: Message,
|
|
121
|
+
) => void | Promise<void>,
|
|
119
122
|
): Subscription {
|
|
120
123
|
return this._onSent.subscribe(callback);
|
|
121
124
|
}
|
|
@@ -124,8 +127,8 @@ export class Http {
|
|
|
124
127
|
callback: (
|
|
125
128
|
response: HttpResponseOfT<any>,
|
|
126
129
|
request: Message,
|
|
127
|
-
value: any
|
|
128
|
-
) => void | Promise<void
|
|
130
|
+
value: any,
|
|
131
|
+
) => void | Promise<void>,
|
|
129
132
|
): Subscription {
|
|
130
133
|
return this._onReceived.subscribe(callback);
|
|
131
134
|
}
|
package/src/index.ts
CHANGED