@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.
- package/lib/AesPkcs5.js.map +1 -1
- package/lib/EncryptedFetcher.js +6 -6
- package/lib/EncryptedFetcher.js.map +1 -1
- package/lib/HttpError.js.map +1 -1
- package/lib/IConnection.js +0 -1
- package/lib/IConnection.js.map +1 -1
- package/lib/PlainFetcher.d.ts +1 -1
- package/lib/PlainFetcher.js +2 -4
- package/lib/PlainFetcher.js.map +1 -1
- package/lib/internal/FetcherBase.d.ts +1 -1
- package/lib/internal/FetcherBase.js +2 -4
- package/lib/internal/FetcherBase.js.map +1 -1
- package/lib/internal/Singleton.js.map +1 -1
- package/package.json +2 -4
- package/src/AesPkcs5.ts +34 -34
- package/src/EncryptedFetcher.ts +143 -162
- package/src/HttpError.ts +85 -85
- package/src/IConnection.ts +237 -238
- package/src/IEncryptionPassword.ts +29 -29
- package/src/IPropagation.ts +102 -103
- package/src/IRandomGenerator.ts +38 -38
- package/src/PlainFetcher.ts +106 -122
- package/src/Primitive.ts +135 -135
- package/src/Resolved.ts +116 -116
- package/src/index.ts +7 -7
- package/src/internal/FetcherBase.ts +180 -192
- package/src/internal/IFetchRoute.ts +46 -46
- package/src/internal/Singleton.ts +20 -20
|
@@ -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
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
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
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
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
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
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
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
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
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
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
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
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
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
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
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
123
|
+
// DO FETCH
|
|
124
|
+
const response: Response = await (
|
|
125
|
+
connection.fetch ?? (await polyfill.get())
|
|
126
|
+
)(url.href, init);
|
|
134
127
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
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
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
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
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
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
|
-
|
|
200
|
+
headers: Headers,
|
|
213
201
|
): Record<string, string | string[]> => {
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
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
|
-
|
|
7
|
+
Method extends "HEAD" | "GET" | "POST" | "PUT" | "PATCH" | "DELETE",
|
|
8
8
|
> {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
9
|
+
/**
|
|
10
|
+
* Method of the HTTP request.
|
|
11
|
+
*/
|
|
12
|
+
method: Method;
|
|
13
13
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
14
|
+
/**
|
|
15
|
+
* Path of the HTTP request.
|
|
16
|
+
*/
|
|
17
|
+
path: string;
|
|
18
18
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
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
|
-
|
|
28
|
-
|
|
29
|
-
|
|
26
|
+
/**
|
|
27
|
+
* Response body data info.
|
|
28
|
+
*/
|
|
29
|
+
response: Method extends "HEAD" ? null : IFetchRoute.IBody;
|
|
30
30
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
31
|
+
/**
|
|
32
|
+
* When special status code being used.
|
|
33
|
+
*/
|
|
34
|
+
status: number | null;
|
|
35
35
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
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
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
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
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
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 = {};
|