@utiliread/http 1.10.2 → 1.13.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.
Files changed (54) hide show
  1. package/.vscode/settings.json +2 -2
  2. package/dist/cjs/events.js +19 -85
  3. package/dist/cjs/events.js.map +1 -1
  4. package/dist/cjs/http-builder.js.map +1 -1
  5. package/dist/cjs/http.js +2 -1
  6. package/dist/cjs/http.js.map +1 -1
  7. package/dist/cjs/index.js +3 -1
  8. package/dist/cjs/index.js.map +1 -1
  9. package/dist/cjs/msgpack.js +17 -0
  10. package/dist/cjs/msgpack.js.map +1 -1
  11. package/dist/esm/events.d.ts +4 -22
  12. package/dist/esm/events.js +17 -84
  13. package/dist/esm/events.js.map +1 -1
  14. package/dist/esm/http-builder.d.ts +3 -0
  15. package/dist/esm/http-builder.js.map +1 -1
  16. package/dist/esm/http.js +2 -1
  17. package/dist/esm/http.js.map +1 -1
  18. package/dist/esm/index.d.ts +2 -1
  19. package/dist/esm/index.js +1 -0
  20. package/dist/esm/index.js.map +1 -1
  21. package/dist/esm/msgpack.d.ts +1 -0
  22. package/dist/esm/msgpack.js +18 -1
  23. package/dist/esm/msgpack.js.map +1 -1
  24. package/karma.config.js +30 -30
  25. package/package.json +1 -1
  26. package/src/event-aggregator.ts +31 -31
  27. package/src/events.ts +35 -0
  28. package/src/helpers.ts +13 -13
  29. package/src/http-builder.ts +254 -253
  30. package/src/http-error.ts +25 -25
  31. package/src/http-response.ts +55 -55
  32. package/src/http.spec.ts +104 -104
  33. package/src/http.ts +121 -120
  34. package/src/index.ts +17 -16
  35. package/src/json.ts +138 -138
  36. package/src/mapping.ts +42 -42
  37. package/src/msgpack.ts +63 -48
  38. package/src/pagination.ts +25 -25
  39. package/src/problem-details.ts +7 -7
  40. package/src/query-string.spec.ts +62 -62
  41. package/src/query-string.ts +69 -69
  42. package/src/timeout-error.ts +10 -10
  43. package/tsconfig.cjs.json +8 -8
  44. package/tsconfig.json +13 -13
  45. package/dist/cjs/settings.js +0 -3
  46. package/dist/cjs/settings.js.map +0 -1
  47. package/dist/cjs/utils.js +0 -8
  48. package/dist/cjs/utils.js.map +0 -1
  49. package/dist/esm/settings.d.ts +0 -6
  50. package/dist/esm/settings.js +0 -2
  51. package/dist/esm/settings.js.map +0 -1
  52. package/dist/esm/utils.d.ts +0 -3
  53. package/dist/esm/utils.js +0 -4
  54. package/dist/esm/utils.js.map +0 -1
@@ -1,32 +1,32 @@
1
- type Callback<P extends any[] = any[]> = (...params: P) => void | Promise<void>;
2
-
3
- export class EventAggregator<P extends any[]> {
4
- private callbacks: Function[] = [];
5
-
6
- get any() {
7
- return this.callbacks.length > 0;
8
- }
9
-
10
- subscribe(callback: Callback<P>): Subscription {
11
- this.callbacks.push(callback);
12
- return {
13
- dispose: () => {
14
- const index = this.callbacks.indexOf(callback);
15
- if (index >= 0) {
16
- this.callbacks.splice(index, 1);
17
- }
18
- }
19
- }
20
- }
21
-
22
- async publish(...params: P) {
23
- const callbacks = this.callbacks.slice();
24
- for (const callback of callbacks) {
25
- await Promise.resolve(callback.apply(this, params));
26
- }
27
- }
28
- }
29
-
30
- export interface Subscription {
31
- dispose(): void;
1
+ type Callback<P extends any[] = any[]> = (...params: P) => void | Promise<void>;
2
+
3
+ export class EventAggregator<P extends any[]> {
4
+ private callbacks: Function[] = [];
5
+
6
+ get any() {
7
+ return this.callbacks.length > 0;
8
+ }
9
+
10
+ subscribe(callback: Callback<P>): Subscription {
11
+ this.callbacks.push(callback);
12
+ return {
13
+ dispose: () => {
14
+ const index = this.callbacks.indexOf(callback);
15
+ if (index >= 0) {
16
+ this.callbacks.splice(index, 1);
17
+ }
18
+ }
19
+ }
20
+ }
21
+
22
+ async publish(...params: P) {
23
+ const callbacks = this.callbacks.slice();
24
+ for (const callback of callbacks) {
25
+ await Promise.resolve(callback.apply(this, params));
26
+ }
27
+ }
28
+ }
29
+
30
+ export interface Subscription {
31
+ dispose(): void;
32
32
  }
package/src/events.ts ADDED
@@ -0,0 +1,35 @@
1
+ import { HttpBuilder, HttpBuilderOfT, Message } from "./http-builder";
2
+ import { HttpResponse } from "./http-response";
3
+
4
+ interface Events {
5
+ sent: (response: HttpResponse, request: Message) => void | Promise<void>;
6
+ }
7
+
8
+ interface EventsOfT<T> extends Events {
9
+ received: (
10
+ response: HttpResponse,
11
+ request: Message,
12
+ value: T
13
+ ) => void | Promise<void>;
14
+ }
15
+
16
+ export function events<B extends HttpBuilder, P extends any[]>(
17
+ action: (...params: P) => B,
18
+ configure: (...params: P) => Partial<Events>
19
+ ): (...params: P) => B;
20
+ export function events<B extends HttpBuilderOfT<T>, P extends any[], T>(
21
+ action: (...params: P) => B,
22
+ configure: (...params: P) => Partial<EventsOfT<T>>
23
+ ): (...params: P) => B {
24
+ return function (...params: P) {
25
+ const builder = action(...params);
26
+ const events = configure(...params);
27
+ if (events.sent) {
28
+ builder.onSent(events.sent);
29
+ }
30
+ if (builder instanceof HttpBuilderOfT && events.received) {
31
+ builder.onReceived(events.received);
32
+ }
33
+ return builder;
34
+ };
35
+ }
package/src/helpers.ts CHANGED
@@ -1,14 +1,14 @@
1
- import { HttpError } from './http-error';
2
- import { TimeoutError } from './timeout-error';
3
-
4
- export function isHttpError(error: Error): error is HttpError {
5
- return error.name === "HttpError";
6
- }
7
-
8
- export function isAbortError(error: Error): boolean {
9
- return error.name === "AbortError";
10
- }
11
-
12
- export function isTimeoutError(error: Error): error is TimeoutError {
13
- return error.name === "TimeoutError";
1
+ import { HttpError } from './http-error';
2
+ import { TimeoutError } from './timeout-error';
3
+
4
+ export function isHttpError(error: Error): error is HttpError {
5
+ return error.name === "HttpError";
6
+ }
7
+
8
+ export function isAbortError(error: Error): boolean {
9
+ return error.name === "AbortError";
10
+ }
11
+
12
+ export function isTimeoutError(error: Error): error is TimeoutError {
13
+ return error.name === "TimeoutError";
14
14
  }
@@ -1,254 +1,255 @@
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 '.';
6
-
7
- export class HttpBuilder {
8
- private _ensureSuccessStatusCode = true;
9
- private _onSend = new EventAggregator<[Message]>();
10
- private _onSent = new EventAggregator<[HttpResponse, Message]>();
11
-
12
- constructor(public message: Message, public options: RequestOptions, /** @internal */ public http: Http) {
13
- }
14
-
15
- onSend(callback: (request: Message) => void | Promise<void>) {
16
- this._onSend.subscribe(callback);
17
- return this;
18
- }
19
-
20
- onSent(callback: (response: HttpResponse, request: Message) => void | Promise<void>) {
21
- this._onSent.subscribe(callback);
22
- return this;
23
- }
24
-
25
- protected useHandler<T>(handler: (response: Response) => Promise<T>) {
26
- return new HttpBuilderOfT<T>(this, handler);
27
- }
28
-
29
- async send(abortSignal?: AbortSignal) {
30
- if (this.message.contentType) {
31
- this.message.headers.set('Content-Type', this.message.contentType);
32
- }
33
-
34
- // Resolve the final url and assign it to the message
35
- // This makes the final url apper in onSend, onSent, and on Received handlers
36
- this.message.url = this.getUrl();
37
-
38
- await this._onSend.publish(this.message);
39
- await this.http._onSend.publish(this.message);
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;
85
- }
86
-
87
- getUrl() {
88
- let baseUrl = this.options.baseUrl;
89
- if (!baseUrl) {
90
- return this.message.url;
91
- }
92
-
93
- if (baseUrl.endsWith('/')) {
94
- baseUrl = baseUrl.substr(0, baseUrl.length - 1);
95
- }
96
-
97
- if (this.message.url.startsWith('/')) {
98
- return baseUrl + this.message.url;
99
- }
100
- else {
101
- return baseUrl + '/' + this.message.url;
102
- }
103
- }
104
-
105
- ensureSuccessStatusCode(ensureSuccessStatusCode?: boolean) {
106
- this._ensureSuccessStatusCode = ensureSuccessStatusCode === false ? false : true;
107
-
108
- return this;
109
- }
110
-
111
- useCors(mode: RequestMode) {
112
- this.message.mode = mode;
113
- return this;
114
- }
115
-
116
- useTimeout(timeout: number | null) {
117
- this.options.timeout = timeout || undefined;
118
- return this;
119
- }
120
-
121
- // Content Extensions
122
-
123
- with(content: any, contentType?: string) {
124
- this.message.content = content;
125
- this.message.contentType = contentType;
126
- return this;
127
- }
128
-
129
- withForm(content: FormData) {
130
- this.message.content = content;
131
- this.message.contentType = undefined;
132
- return this;
133
- }
134
-
135
- // Modifier Extensions
136
-
137
- addHeader(name: string, value: string) {
138
- this.message.headers.append(name, value);
139
- return this;
140
- }
141
-
142
- // Expect Extensions
143
-
144
- expectString() {
145
- return this.useHandler(response => {
146
- return response.text();
147
- });
148
- }
149
-
150
- expectBinary() {
151
- return this.useHandler(response => {
152
- return response.arrayBuffer();
153
- });
154
- }
155
- }
156
-
157
- export class HttpBuilderOfT<T> extends HttpBuilder {
158
- private _onReceived = new EventAggregator<[HttpResponseOfT<T>, Message, T]>();
159
-
160
- constructor(private inner: HttpBuilder, private handler: (response: Response) => Promise<T>) {
161
- super(inner.message, inner.options, inner.http);
162
- }
163
-
164
- onSend(callback: (request: Message) => void | Promise<void>) {
165
- this.inner.onSend(callback);
166
- return this;
167
- }
168
-
169
- onSent(callback: (response: HttpResponse, request: Message) => void | Promise<void>) {
170
- this.inner.onSent(callback);
171
- return this;
172
- }
173
-
174
- ensureSuccessStatusCode(ensureSuccessStatusCode?: boolean) {
175
- this.inner.ensureSuccessStatusCode(ensureSuccessStatusCode);
176
- return this;
177
- }
178
-
179
- useCors(mode: RequestMode) {
180
- this.inner.useCors(mode);
181
- return this;
182
- }
183
-
184
- useTimeout(timeout: number) {
185
- this.inner.useTimeout(timeout);
186
- return this;
187
- }
188
-
189
- allowEmptyResponse() {
190
- if (this._onReceived.any) {
191
- throw new Error("onReceived() must be called after allowEmptyResponse() because the callback type changes");
192
- }
193
-
194
- return new HttpBuilderOfT<T | null>(this.inner, response => {
195
- if (response.status === 204) {
196
- return Promise.resolve(null);
197
- }
198
-
199
- return this.handler(response);
200
- });
201
- }
202
-
203
- onReceived(callback: (response: HttpResponseOfT<T>, request: Message, value: T) => void | Promise<void>) {
204
- this._onReceived.subscribe(callback);
205
- return this;
206
- }
207
-
208
- send(abortSignal?: AbortSignal) {
209
- const responsePromise = this.inner.send(abortSignal).then(x => new HttpResponseOfT<T>(x.rawResponse, this.handler));
210
-
211
- return asSendPromise(responsePromise, () => responsePromise.then(response => this.handleReceive(response)));
212
- }
213
-
214
- transfer(abortSignal?: AbortSignal) {
215
- return this.send(abortSignal).then(response => this.handleReceive(response));
216
- }
217
-
218
- private async handleReceive(response: HttpResponseOfT<T>) {
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
- }
227
- }
228
-
229
- export type HttpMethod = "HEAD" | "POST" | "GET" | "PUT" | "PATCH" | "DELETE";
230
-
231
- export interface Message {
232
- method: HttpMethod;
233
- url: string;
234
- headers: Headers;
235
- content?: any;
236
- contentType?: string;
237
- mode?: RequestMode;
238
- }
239
-
240
- export interface RequestOptions {
241
- fetch: Fetch,
242
- timeout?: number,
243
- baseUrl?: string,
244
- }
245
-
246
- export interface SendPromise<T> extends Promise<HttpResponseOfT<T>> {
247
- thenReceive(): Promise<T>;
248
- }
249
-
250
- function asSendPromise<T>(responsePromise: Promise<HttpResponse>, thenReceive: () => Promise<T>): SendPromise<T> {
251
- const sendPromise = responsePromise as SendPromise<T>;
252
- sendPromise.thenReceive = thenReceive;
253
- return sendPromise;
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 '.';
6
+
7
+ export class HttpBuilder {
8
+ private _ensureSuccessStatusCode = true;
9
+ private _onSend = new EventAggregator<[Message]>();
10
+ private _onSent = new EventAggregator<[HttpResponse, Message]>();
11
+
12
+ constructor(public message: Message, public options: RequestOptions, /** @internal */ public http: Http) {
13
+ }
14
+
15
+ onSend(callback: (request: Message) => void | Promise<void>) {
16
+ this._onSend.subscribe(callback);
17
+ return this;
18
+ }
19
+
20
+ onSent(callback: (response: HttpResponse, request: Message) => void | Promise<void>) {
21
+ this._onSent.subscribe(callback);
22
+ return this;
23
+ }
24
+
25
+ protected useHandler<T>(handler: (response: Response) => Promise<T>) {
26
+ return new HttpBuilderOfT<T>(this, handler);
27
+ }
28
+
29
+ async send(abortSignal?: AbortSignal) {
30
+ if (this.message.contentType) {
31
+ this.message.headers.set('Content-Type', this.message.contentType);
32
+ }
33
+
34
+ // Resolve the final url and assign it to the message
35
+ // This makes the final url apper in onSend, onSent, and on Received handlers
36
+ this.message.url = this.getUrl();
37
+
38
+ await this._onSend.publish(this.message);
39
+ await this.http._onSend.publish(this.message);
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;
85
+ }
86
+
87
+ getUrl() {
88
+ let baseUrl = this.options.baseUrl;
89
+ if (!baseUrl) {
90
+ return this.message.url;
91
+ }
92
+
93
+ if (baseUrl.endsWith('/')) {
94
+ baseUrl = baseUrl.substr(0, baseUrl.length - 1);
95
+ }
96
+
97
+ if (this.message.url.startsWith('/')) {
98
+ return baseUrl + this.message.url;
99
+ }
100
+ else {
101
+ return baseUrl + '/' + this.message.url;
102
+ }
103
+ }
104
+
105
+ ensureSuccessStatusCode(ensureSuccessStatusCode?: boolean) {
106
+ this._ensureSuccessStatusCode = ensureSuccessStatusCode === false ? false : true;
107
+
108
+ return this;
109
+ }
110
+
111
+ useCors(mode: RequestMode) {
112
+ this.message.mode = mode;
113
+ return this;
114
+ }
115
+
116
+ useTimeout(timeout: number | null) {
117
+ this.options.timeout = timeout || undefined;
118
+ return this;
119
+ }
120
+
121
+ // Content Extensions
122
+
123
+ with(content: any, contentType?: string) {
124
+ this.message.content = content;
125
+ this.message.contentType = contentType;
126
+ return this;
127
+ }
128
+
129
+ withForm(content: FormData) {
130
+ this.message.content = content;
131
+ this.message.contentType = undefined;
132
+ return this;
133
+ }
134
+
135
+ // Modifier Extensions
136
+
137
+ addHeader(name: string, value: string) {
138
+ this.message.headers.append(name, value);
139
+ return this;
140
+ }
141
+
142
+ // Expect Extensions
143
+
144
+ expectString() {
145
+ return this.useHandler(response => {
146
+ return response.text();
147
+ });
148
+ }
149
+
150
+ expectBinary() {
151
+ return this.useHandler(response => {
152
+ return response.arrayBuffer();
153
+ });
154
+ }
155
+ }
156
+
157
+ export class HttpBuilderOfT<T> extends HttpBuilder {
158
+ private _onReceived = new EventAggregator<[HttpResponseOfT<T>, Message, T]>();
159
+
160
+ constructor(private inner: HttpBuilder, private handler: (response: Response) => Promise<T>) {
161
+ super(inner.message, inner.options, inner.http);
162
+ }
163
+
164
+ onSend(callback: (request: Message) => void | Promise<void>) {
165
+ this.inner.onSend(callback);
166
+ return this;
167
+ }
168
+
169
+ onSent(callback: (response: HttpResponse, request: Message) => void | Promise<void>) {
170
+ this.inner.onSent(callback);
171
+ return this;
172
+ }
173
+
174
+ ensureSuccessStatusCode(ensureSuccessStatusCode?: boolean) {
175
+ this.inner.ensureSuccessStatusCode(ensureSuccessStatusCode);
176
+ return this;
177
+ }
178
+
179
+ useCors(mode: RequestMode) {
180
+ this.inner.useCors(mode);
181
+ return this;
182
+ }
183
+
184
+ useTimeout(timeout: number) {
185
+ this.inner.useTimeout(timeout);
186
+ return this;
187
+ }
188
+
189
+ allowEmptyResponse() {
190
+ if (this._onReceived.any) {
191
+ throw new Error("onReceived() must be called after allowEmptyResponse() because the callback type changes");
192
+ }
193
+
194
+ return new HttpBuilderOfT<T | null>(this.inner, response => {
195
+ if (response.status === 204) {
196
+ return Promise.resolve(null);
197
+ }
198
+
199
+ return this.handler(response);
200
+ });
201
+ }
202
+
203
+ onReceived(callback: (response: HttpResponseOfT<T>, request: Message, value: T) => void | Promise<void>) {
204
+ this._onReceived.subscribe(callback);
205
+ return this;
206
+ }
207
+
208
+ send(abortSignal?: AbortSignal) {
209
+ const responsePromise = this.inner.send(abortSignal).then(x => new HttpResponseOfT<T>(x.rawResponse, this.handler));
210
+
211
+ return asSendPromise(responsePromise, () => responsePromise.then(response => this.handleReceive(response)));
212
+ }
213
+
214
+ transfer(abortSignal?: AbortSignal) {
215
+ return this.send(abortSignal).then(response => this.handleReceive(response));
216
+ }
217
+
218
+ private async handleReceive(response: HttpResponseOfT<T>) {
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
+ }
227
+ }
228
+
229
+ export type HttpMethod = "HEAD" | "POST" | "GET" | "PUT" | "PATCH" | "DELETE";
230
+
231
+ export interface Message {
232
+ method: HttpMethod;
233
+ url: string;
234
+ headers: Headers;
235
+ content?: any;
236
+ contentType?: string;
237
+ mode?: RequestMode;
238
+ properties: {[key: string]: any};
239
+ }
240
+
241
+ export interface RequestOptions {
242
+ fetch: Fetch,
243
+ timeout?: number,
244
+ baseUrl?: string,
245
+ }
246
+
247
+ export interface SendPromise<T> extends Promise<HttpResponseOfT<T>> {
248
+ thenReceive(): Promise<T>;
249
+ }
250
+
251
+ function asSendPromise<T>(responsePromise: Promise<HttpResponse>, thenReceive: () => Promise<T>): SendPromise<T> {
252
+ const sendPromise = responsePromise as SendPromise<T>;
253
+ sendPromise.thenReceive = thenReceive;
254
+ return sendPromise;
254
255
  }