@nestia/fetcher 2.4.2 → 2.4.3

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.
@@ -1,224 +1,212 @@
1
1
  import import2 from "import2";
2
+
3
+ import { HttpError } from "../HttpError";
2
4
  import { IConnection } from "../IConnection";
5
+ import { IPropagation } from "../IPropagation";
3
6
  import { Primitive } from "../Primitive";
4
7
  import { IFetchRoute } from "./IFetchRoute";
5
8
  import { Singleton } from "./Singleton";
6
- import { HttpError } from "../HttpError";
7
- import { IPropagation } from "../IPropagation";
8
9
 
9
10
  export namespace FetcherBase {
10
- export interface IProps {
11
- className: string;
12
- encode: (
13
- input: any,
14
- headers: Record<string, IConnection.HeaderValue | undefined>,
15
- ) => string;
16
- decode: (
17
- input: string,
18
- headers: Record<string, IConnection.HeaderValue | undefined>,
19
- ) => any;
20
- }
11
+ export interface IProps {
12
+ className: string;
13
+ encode: (
14
+ input: any,
15
+ headers: Record<string, IConnection.HeaderValue | undefined>,
16
+ ) => string;
17
+ decode: (
18
+ input: string,
19
+ headers: Record<string, IConnection.HeaderValue | undefined>,
20
+ ) => any;
21
+ }
21
22
 
22
- export const fetch =
23
- (props: IProps) =>
24
- async <Input, Output>(
25
- connection: IConnection,
26
- route: IFetchRoute<
27
- "DELETE" | "GET" | "HEAD" | "PATCH" | "POST" | "PUT"
28
- >,
29
- input?: Input,
30
- stringify?: (input: Input) => string,
31
- ): Promise<Primitive<Output>> => {
32
- const result = await _Propagate("fetch")(props)(
33
- connection,
34
- route,
35
- input,
36
- stringify,
37
- );
38
- if ((result as any).success === false)
39
- throw new HttpError(
40
- route.method,
41
- route.path,
42
- result.status as any as number,
43
- result.headers,
44
- result.data as string,
45
- );
46
- return result.data as Primitive<Output>;
47
- };
23
+ export const fetch =
24
+ (props: IProps) =>
25
+ async <Input, Output>(
26
+ connection: IConnection,
27
+ route: IFetchRoute<"DELETE" | "GET" | "HEAD" | "PATCH" | "POST" | "PUT">,
28
+ input?: Input,
29
+ stringify?: (input: Input) => string,
30
+ ): Promise<Primitive<Output>> => {
31
+ const result = await _Propagate("fetch")(props)(
32
+ connection,
33
+ route,
34
+ input,
35
+ stringify,
36
+ );
37
+ if ((result as any).success === false)
38
+ throw new HttpError(
39
+ route.method,
40
+ route.path,
41
+ result.status as any as number,
42
+ result.headers,
43
+ result.data as string,
44
+ );
45
+ return result.data as Primitive<Output>;
46
+ };
48
47
 
49
- export const propagate =
50
- (props: IProps) =>
51
- async <Input>(
52
- connection: IConnection,
53
- route: IFetchRoute<
54
- "DELETE" | "GET" | "HEAD" | "PATCH" | "POST" | "PUT"
55
- >,
56
- input?: Input,
57
- stringify?: (input: Input) => string,
58
- ): Promise<IPropagation<any, any>> =>
59
- _Propagate("propagate")(props)(connection, route, input, stringify);
48
+ export const propagate =
49
+ (props: IProps) =>
50
+ async <Input>(
51
+ connection: IConnection,
52
+ route: IFetchRoute<"DELETE" | "GET" | "HEAD" | "PATCH" | "POST" | "PUT">,
53
+ input?: Input,
54
+ stringify?: (input: Input) => string,
55
+ ): Promise<IPropagation<any, any>> =>
56
+ _Propagate("propagate")(props)(connection, route, input, stringify);
60
57
 
61
- /**
62
- * @internal
63
- */
64
- const _Propagate =
65
- (method: string) =>
66
- (props: IProps) =>
67
- async <Input>(
68
- connection: IConnection,
69
- route: IFetchRoute<
70
- "DELETE" | "GET" | "HEAD" | "PATCH" | "POST" | "PUT"
71
- >,
72
- input?: Input,
73
- stringify?: (input: Input) => string,
74
- ): Promise<IPropagation<any, any>> => {
75
- //----
76
- // REQUEST MESSSAGE
77
- //----
78
- // METHOD & HEADERS
79
- const headers: Record<string, IConnection.HeaderValue | undefined> =
80
- {
81
- ...(connection.headers ?? {}),
82
- };
83
- if (input !== undefined) {
84
- if (route.request?.type === undefined)
85
- throw new Error(
86
- `Error on ${props.className}.fetch(): no content-type being configured.`,
87
- );
88
- headers["Content-Type"] = route.request.type;
89
- }
58
+ /**
59
+ * @internal
60
+ */
61
+ const _Propagate =
62
+ (method: string) =>
63
+ (props: IProps) =>
64
+ async <Input>(
65
+ connection: IConnection,
66
+ route: IFetchRoute<"DELETE" | "GET" | "HEAD" | "PATCH" | "POST" | "PUT">,
67
+ input?: Input,
68
+ stringify?: (input: Input) => string,
69
+ ): Promise<IPropagation<any, any>> => {
70
+ //----
71
+ // REQUEST MESSSAGE
72
+ //----
73
+ // METHOD & HEADERS
74
+ const headers: Record<string, IConnection.HeaderValue | undefined> = {
75
+ ...(connection.headers ?? {}),
76
+ };
77
+ if (input !== undefined) {
78
+ if (route.request?.type === undefined)
79
+ throw new Error(
80
+ `Error on ${props.className}.fetch(): no content-type being configured.`,
81
+ );
82
+ headers["Content-Type"] = route.request.type;
83
+ }
90
84
 
91
- // INIT REQUEST DATA
92
- const init: RequestInit = {
93
- ...(connection.options ?? {}),
94
- method: route.method,
95
- headers: (() => {
96
- const output: [string, string][] = [];
97
- for (const [key, value] of Object.entries(headers))
98
- if (value === undefined) continue;
99
- else if (Array.isArray(value))
100
- for (const v of value)
101
- output.push([key, String(v)]);
102
- else output.push([key, String(value)]);
103
- return output;
104
- })(),
105
- };
85
+ // INIT REQUEST DATA
86
+ const init: RequestInit = {
87
+ ...(connection.options ?? {}),
88
+ method: route.method,
89
+ headers: (() => {
90
+ const output: [string, string][] = [];
91
+ for (const [key, value] of Object.entries(headers))
92
+ if (value === undefined) continue;
93
+ else if (Array.isArray(value))
94
+ for (const v of value) output.push([key, String(v)]);
95
+ else output.push([key, String(value)]);
96
+ return output;
97
+ })(),
98
+ };
106
99
 
107
- // CONSTRUCT BODY DATA
108
- if (input !== undefined)
109
- init.body = props.encode(
110
- // BODY TRANSFORM
111
- route.request?.type === "application/x-www-form-urlencoded"
112
- ? request_query_body(input)
113
- : route.request?.type !== "text/plain"
114
- ? (stringify ?? JSON.stringify)(input)
115
- : input,
116
- headers,
117
- );
100
+ // CONSTRUCT BODY DATA
101
+ if (input !== undefined)
102
+ init.body = props.encode(
103
+ // BODY TRANSFORM
104
+ route.request?.type === "application/x-www-form-urlencoded"
105
+ ? request_query_body(input)
106
+ : route.request?.type !== "text/plain"
107
+ ? (stringify ?? JSON.stringify)(input)
108
+ : input,
109
+ headers,
110
+ );
118
111
 
119
- //----
120
- // RESPONSE MESSSAGE
121
- //----
122
- // URL SPECIFICATION
123
- const path: string =
124
- connection.host[connection.host.length - 1] !== "/" &&
125
- route.path[0] !== "/"
126
- ? `/${route.path}`
127
- : route.path;
128
- const url: URL = new URL(`${connection.host}${path}`);
112
+ //----
113
+ // RESPONSE MESSSAGE
114
+ //----
115
+ // URL SPECIFICATION
116
+ const path: string =
117
+ connection.host[connection.host.length - 1] !== "/" &&
118
+ route.path[0] !== "/"
119
+ ? `/${route.path}`
120
+ : route.path;
121
+ const url: URL = new URL(`${connection.host}${path}`);
129
122
 
130
- // DO FETCH
131
- const response: Response = await (
132
- connection.fetch ?? (await polyfill.get())
133
- )(url.href, init);
123
+ // DO FETCH
124
+ const response: Response = await (
125
+ connection.fetch ?? (await polyfill.get())
126
+ )(url.href, init);
134
127
 
135
- // CONSTRUCT RESULT DATA
136
- const result: IPropagation<any, any> = {
137
- success:
138
- response.status === 200 ||
139
- response.status === 201 ||
140
- response.status == route.status,
141
- status: response.status,
142
- headers: response_headers_to_object(response.headers),
143
- data: undefined!,
144
- } as any;
145
- if ((result as any).success === false) {
146
- // WHEN FAILED
147
- result.data = await response.text();
148
- const type = response.headers.get("content-type");
149
- if (
150
- method !== "fetch" &&
151
- type &&
152
- type.indexOf("application/json") !== -1
153
- )
154
- try {
155
- result.data = JSON.parse(result.data);
156
- } catch {}
157
- } else {
158
- // WHEN SUCCESS
159
- if (route.method === "HEAD") result.data = undefined!;
160
- else if (route.response?.type === "application/json") {
161
- const text: string = await response.text();
162
- result.data = text.length ? JSON.parse(text) : undefined;
163
- } else if (
164
- route.response?.type === "application/x-www-form-urlencoded"
165
- ) {
166
- const query: URLSearchParams = new URLSearchParams(
167
- await response.text(),
168
- );
169
- result.data = route.parseQuery
170
- ? route.parseQuery(query)
171
- : query;
172
- } else
173
- result.data = props.decode(
174
- await response.text(),
175
- result.headers,
176
- );
177
- }
178
- return result;
179
- };
128
+ // CONSTRUCT RESULT DATA
129
+ const result: IPropagation<any, any> = {
130
+ success:
131
+ response.status === 200 ||
132
+ response.status === 201 ||
133
+ response.status == route.status,
134
+ status: response.status,
135
+ headers: response_headers_to_object(response.headers),
136
+ data: undefined!,
137
+ } as any;
138
+ if ((result as any).success === false) {
139
+ // WHEN FAILED
140
+ result.data = await response.text();
141
+ const type = response.headers.get("content-type");
142
+ if (
143
+ method !== "fetch" &&
144
+ type &&
145
+ type.indexOf("application/json") !== -1
146
+ )
147
+ try {
148
+ result.data = JSON.parse(result.data);
149
+ } catch {}
150
+ } else {
151
+ // WHEN SUCCESS
152
+ if (route.method === "HEAD") result.data = undefined!;
153
+ else if (route.response?.type === "application/json") {
154
+ const text: string = await response.text();
155
+ result.data = text.length ? JSON.parse(text) : undefined;
156
+ } else if (
157
+ route.response?.type === "application/x-www-form-urlencoded"
158
+ ) {
159
+ const query: URLSearchParams = new URLSearchParams(
160
+ await response.text(),
161
+ );
162
+ result.data = route.parseQuery ? route.parseQuery(query) : query;
163
+ } else
164
+ result.data = props.decode(await response.text(), result.headers);
165
+ }
166
+ return result;
167
+ };
180
168
  }
181
169
 
182
170
  /**
183
171
  * @internal
184
172
  */
185
173
  const polyfill = new Singleton(async (): Promise<typeof fetch> => {
186
- if (
187
- typeof global === "object" &&
188
- typeof global.process === "object" &&
189
- typeof global.process.versions === "object" &&
190
- typeof global.process.versions.node !== undefined
191
- ) {
192
- global.fetch ??= ((await import2("node-fetch")) as any).default;
193
- return (global as any).fetch;
194
- }
195
- return window.fetch;
174
+ if (
175
+ typeof global === "object" &&
176
+ typeof global.process === "object" &&
177
+ typeof global.process.versions === "object" &&
178
+ typeof global.process.versions.node !== undefined
179
+ ) {
180
+ global.fetch ??= ((await import2("node-fetch")) as any).default;
181
+ return (global as any).fetch;
182
+ }
183
+ return window.fetch;
196
184
  });
197
185
 
198
186
  const request_query_body = (input: any): URLSearchParams => {
199
- const q: URLSearchParams = new URLSearchParams();
200
- for (const [key, value] of Object.entries(input))
201
- if (value === undefined) continue;
202
- else if (Array.isArray(value))
203
- value.forEach((elem) => q.append(key, String(elem)));
204
- else q.set(key, String(value));
205
- return q;
187
+ const q: URLSearchParams = new URLSearchParams();
188
+ for (const [key, value] of Object.entries(input))
189
+ if (value === undefined) continue;
190
+ else if (Array.isArray(value))
191
+ value.forEach((elem) => q.append(key, String(elem)));
192
+ else q.set(key, String(value));
193
+ return q;
206
194
  };
207
195
 
208
196
  /**
209
197
  * @internal
210
198
  */
211
199
  const response_headers_to_object = (
212
- headers: Headers,
200
+ headers: Headers,
213
201
  ): Record<string, string | string[]> => {
214
- const output: Record<string, string | string[]> = {};
215
- headers.forEach((value, key) => {
216
- if (key === "set-cookie") {
217
- output[key] ??= [];
218
- (output[key] as string[]).push(
219
- ...value.split(";").map((str) => str.trim()),
220
- );
221
- } else output[key] = value;
222
- });
223
- return output;
202
+ const output: Record<string, string | string[]> = {};
203
+ headers.forEach((value, key) => {
204
+ if (key === "set-cookie") {
205
+ output[key] ??= [];
206
+ (output[key] as string[]).push(
207
+ ...value.split(";").map((str) => str.trim()),
208
+ );
209
+ } else output[key] = value;
210
+ });
211
+ return output;
224
212
  };
@@ -4,58 +4,58 @@
4
4
  * @author Jeongho Nam - https://github.com/samchon
5
5
  */
6
6
  export interface IFetchRoute<
7
- Method extends "HEAD" | "GET" | "POST" | "PUT" | "PATCH" | "DELETE",
7
+ Method extends "HEAD" | "GET" | "POST" | "PUT" | "PATCH" | "DELETE",
8
8
  > {
9
- /**
10
- * Method of the HTTP request.
11
- */
12
- method: Method;
9
+ /**
10
+ * Method of the HTTP request.
11
+ */
12
+ method: Method;
13
13
 
14
- /**
15
- * Path of the HTTP request.
16
- */
17
- path: string;
14
+ /**
15
+ * Path of the HTTP request.
16
+ */
17
+ path: string;
18
18
 
19
- /**
20
- * Request body data info.
21
- */
22
- request: Method extends "DELETE" | "POST" | "PUT" | "PATCH"
23
- ? IFetchRoute.IBody | null
24
- : null;
19
+ /**
20
+ * Request body data info.
21
+ */
22
+ request: Method extends "DELETE" | "POST" | "PUT" | "PATCH"
23
+ ? IFetchRoute.IBody | null
24
+ : null;
25
25
 
26
- /**
27
- * Response body data info.
28
- */
29
- response: Method extends "HEAD" ? null : IFetchRoute.IBody;
26
+ /**
27
+ * Response body data info.
28
+ */
29
+ response: Method extends "HEAD" ? null : IFetchRoute.IBody;
30
30
 
31
- /**
32
- * When special status code being used.
33
- */
34
- status: number | null;
31
+ /**
32
+ * When special status code being used.
33
+ */
34
+ status: number | null;
35
35
 
36
- /**
37
- * Parser of the query string.
38
- *
39
- * If content type of response body is `application/x-www-form-urlencoded`,
40
- * then this `parseQuery` function would be called.
41
- *
42
- * If you've forgotten to configuring this `parseQuery` property about the
43
- * `application/x-www-form-urlencoded` typed response body data, then
44
- * only the `URLSearchParams` typed instance would be returned instead.
45
- */
46
- parseQuery?(input: URLSearchParams): any;
36
+ /**
37
+ * Parser of the query string.
38
+ *
39
+ * If content type of response body is `application/x-www-form-urlencoded`,
40
+ * then this `parseQuery` function would be called.
41
+ *
42
+ * If you've forgotten to configuring this `parseQuery` property about the
43
+ * `application/x-www-form-urlencoded` typed response body data, then
44
+ * only the `URLSearchParams` typed instance would be returned instead.
45
+ */
46
+ parseQuery?(input: URLSearchParams): any;
47
47
  }
48
48
  export namespace IFetchRoute {
49
- /**
50
- * Metadata of body.
51
- *
52
- * Describes how content-type being used in body, and whether encrypted or not.
53
- */
54
- export interface IBody {
55
- type:
56
- | "application/json"
57
- | "application/x-www-form-urlencoded"
58
- | "text/plain";
59
- encrypted?: boolean;
60
- }
49
+ /**
50
+ * Metadata of body.
51
+ *
52
+ * Describes how content-type being used in body, and whether encrypted or not.
53
+ */
54
+ export interface IBody {
55
+ type:
56
+ | "application/json"
57
+ | "application/x-www-form-urlencoded"
58
+ | "text/plain";
59
+ encrypted?: boolean;
60
+ }
61
61
  }
@@ -1,20 +1,20 @@
1
- /**
2
- * @internal
3
- */
4
- export class Singleton<T> {
5
- private value_: T | object;
6
-
7
- public constructor(private readonly closure_: () => T) {
8
- this.value_ = NOT_MOUNTED_YET;
9
- }
10
-
11
- public get(): T {
12
- if (this.value_ === NOT_MOUNTED_YET) this.value_ = this.closure_();
13
- return this.value_ as T;
14
- }
15
- }
16
-
17
- /**
18
- * @internal
19
- */
20
- const NOT_MOUNTED_YET = {};
1
+ /**
2
+ * @internal
3
+ */
4
+ export class Singleton<T> {
5
+ private value_: T | object;
6
+
7
+ public constructor(private readonly closure_: () => T) {
8
+ this.value_ = NOT_MOUNTED_YET;
9
+ }
10
+
11
+ public get(): T {
12
+ if (this.value_ === NOT_MOUNTED_YET) this.value_ = this.closure_();
13
+ return this.value_ as T;
14
+ }
15
+ }
16
+
17
+ /**
18
+ * @internal
19
+ */
20
+ const NOT_MOUNTED_YET = {};