@nestia/fetcher 12.0.0-rc.3 → 12.0.0-rc.4
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.mjs +21 -3
- package/lib/AesPkcs5.mjs.map +1 -1
- package/lib/EncryptedFetcher.mjs +52 -3
- package/lib/EncryptedFetcher.mjs.map +1 -1
- package/lib/FormDataInput.mjs +0 -3
- package/lib/HttpError.mjs +2 -2
- package/lib/IConnection.mjs +0 -3
- package/lib/IEncryptionPassword.mjs +0 -3
- package/lib/IFetchEvent.mjs +0 -3
- package/lib/IFetchRoute.mjs +0 -3
- package/lib/IPropagation.mjs +0 -3
- package/lib/NestiaSimulator.mjs +36 -3
- package/lib/NestiaSimulator.mjs.map +1 -1
- package/lib/PlainFetcher.mjs +27 -3
- package/lib/PlainFetcher.mjs.map +1 -1
- package/lib/index.mjs +4 -2
- package/lib/internal/FetcherBase.mjs +117 -3
- package/lib/internal/FetcherBase.mjs.map +1 -1
- package/lib/internal/is_binary_response_content_type.mjs +10 -3
- package/lib/internal/is_binary_response_content_type.mjs.map +1 -1
- package/package.json +5 -9
- package/lib/AesPkcs52.mjs +0 -53
- package/lib/AesPkcs52.mjs.map +0 -1
- package/lib/EncryptedFetcher2.mjs +0 -105
- package/lib/EncryptedFetcher2.mjs.map +0 -1
- package/lib/FormDataInput.mjs.map +0 -1
- package/lib/FormDataInput2.mjs +0 -6
- package/lib/FormDataInput2.mjs.map +0 -1
- package/lib/HttpError.mjs.map +0 -1
- package/lib/HttpError2.mjs +0 -16
- package/lib/HttpError2.mjs.map +0 -1
- package/lib/IConnection.mjs.map +0 -1
- package/lib/IConnection2.mjs +0 -6
- package/lib/IConnection2.mjs.map +0 -1
- package/lib/IEncryptionPassword.mjs.map +0 -1
- package/lib/IEncryptionPassword2.mjs +0 -6
- package/lib/IEncryptionPassword2.mjs.map +0 -1
- package/lib/IFetchEvent.mjs.map +0 -1
- package/lib/IFetchEvent2.mjs +0 -6
- package/lib/IFetchEvent2.mjs.map +0 -1
- package/lib/IFetchRoute.mjs.map +0 -1
- package/lib/IFetchRoute2.mjs +0 -6
- package/lib/IFetchRoute2.mjs.map +0 -1
- package/lib/IPropagation.mjs.map +0 -1
- package/lib/IPropagation2.mjs +0 -6
- package/lib/IPropagation2.mjs.map +0 -1
- package/lib/NestiaSimulator2.mjs +0 -49
- package/lib/NestiaSimulator2.mjs.map +0 -1
- package/lib/PlainFetcher2.mjs +0 -63
- package/lib/PlainFetcher2.mjs.map +0 -1
- package/lib/_virtual/AesPkcs5.mjs +0 -4
- package/lib/_virtual/AesPkcs5.mjs.map +0 -1
- package/lib/_virtual/EncryptedFetcher.mjs +0 -4
- package/lib/_virtual/EncryptedFetcher.mjs.map +0 -1
- package/lib/_virtual/FetcherBase.mjs +0 -4
- package/lib/_virtual/FetcherBase.mjs.map +0 -1
- package/lib/_virtual/FormDataInput.mjs +0 -4
- package/lib/_virtual/FormDataInput.mjs.map +0 -1
- package/lib/_virtual/HttpError.mjs +0 -4
- package/lib/_virtual/HttpError.mjs.map +0 -1
- package/lib/_virtual/IConnection.mjs +0 -4
- package/lib/_virtual/IConnection.mjs.map +0 -1
- package/lib/_virtual/IEncryptionPassword.mjs +0 -4
- package/lib/_virtual/IEncryptionPassword.mjs.map +0 -1
- package/lib/_virtual/IFetchEvent.mjs +0 -4
- package/lib/_virtual/IFetchEvent.mjs.map +0 -1
- package/lib/_virtual/IFetchRoute.mjs +0 -4
- package/lib/_virtual/IFetchRoute.mjs.map +0 -1
- package/lib/_virtual/IPropagation.mjs +0 -4
- package/lib/_virtual/IPropagation.mjs.map +0 -1
- package/lib/_virtual/NestiaSimulator.mjs +0 -4
- package/lib/_virtual/NestiaSimulator.mjs.map +0 -1
- package/lib/_virtual/PlainFetcher.mjs +0 -4
- package/lib/_virtual/PlainFetcher.mjs.map +0 -1
- package/lib/_virtual/_commonjsHelpers.mjs +0 -35
- package/lib/_virtual/_commonjsHelpers.mjs.map +0 -1
- package/lib/_virtual/_node_crypto.mjs +0 -7
- package/lib/_virtual/_node_crypto.mjs.map +0 -1
- package/lib/_virtual/index.mjs +0 -4
- package/lib/_virtual/index.mjs.map +0 -1
- package/lib/_virtual/is_binary_response_content_type.mjs +0 -4
- package/lib/_virtual/is_binary_response_content_type.mjs.map +0 -1
- package/lib/_virtual/utils.mjs +0 -7
- package/lib/_virtual/utils.mjs.map +0 -1
- package/lib/index.mjs.map +0 -1
- package/lib/index2.mjs +0 -53
- package/lib/index2.mjs.map +0 -1
- package/lib/internal/FetcherBase2.mjs +0 -203
- package/lib/internal/FetcherBase2.mjs.map +0 -1
- package/lib/internal/is_binary_response_content_type2.mjs +0 -18
- package/lib/internal/is_binary_response_content_type2.mjs.map +0 -1
package/lib/AesPkcs5.mjs
CHANGED
|
@@ -1,3 +1,21 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
import crypto from "crypto";
|
|
2
|
+
//#region src/AesPkcs5.ts
|
|
3
|
+
let AesPkcs5;
|
|
4
|
+
(function(_AesPkcs) {
|
|
5
|
+
function encrypt(data, key, iv) {
|
|
6
|
+
const bytes = key.length * 8;
|
|
7
|
+
const cipher = crypto.createCipheriv(`AES-${bytes}-CBC`, key, iv);
|
|
8
|
+
return cipher.update(data, "utf8", "base64") + cipher.final("base64");
|
|
9
|
+
}
|
|
10
|
+
_AesPkcs.encrypt = encrypt;
|
|
11
|
+
function decrypt(data, key, iv) {
|
|
12
|
+
const bytes = key.length * 8;
|
|
13
|
+
const decipher = crypto.createDecipheriv(`AES-${bytes}-CBC`, key, iv);
|
|
14
|
+
return decipher.update(data, "base64", "utf8") + decipher.final("utf8");
|
|
15
|
+
}
|
|
16
|
+
_AesPkcs.decrypt = decrypt;
|
|
17
|
+
})(AesPkcs5 || (AesPkcs5 = {}));
|
|
18
|
+
//#endregion
|
|
19
|
+
export { AesPkcs5 };
|
|
20
|
+
|
|
21
|
+
//# sourceMappingURL=AesPkcs5.mjs.map
|
package/lib/AesPkcs5.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AesPkcs5.mjs","sources":[],"sourcesContent":[
|
|
1
|
+
{"version":3,"file":"AesPkcs5.mjs","names":[],"sources":["../src/AesPkcs5.ts"],"sourcesContent":["import crypto from \"crypto\";\n\n/**\n * Utility class for the AES-128/256 encryption.\n *\n * - AES-128/256\n * - CBC mode\n * - PKCS#5 Padding\n * - Base64 Encoding\n *\n * @author Jeongho Nam - https://github.com/samchon\n */\nexport namespace AesPkcs5 {\n /**\n * Encrypt data\n *\n * @param data Target data\n * @param key Key value of the encryption.\n * @param iv Initializer Vector for the encryption\n * @returns Encrypted data\n */\n export function encrypt(data: string, key: string, iv: string): string {\n const bytes: number = key.length * 8;\n const cipher = crypto.createCipheriv(`AES-${bytes}-CBC`, key, iv);\n return cipher.update(data, \"utf8\", \"base64\") + cipher.final(\"base64\");\n }\n\n /**\n * Decrypt data.\n *\n * @param data Target data\n * @param key Key value of the decryption.\n * @param iv Initializer Vector for the decryption\n * @returns Decrypted data.\n */\n export function decrypt(data: string, key: string, iv: string): string {\n const bytes: number = key.length * 8;\n const decipher = crypto.createDecipheriv(`AES-${bytes}-CBC`, key, iv);\n return decipher.update(data, \"base64\", \"utf8\") + decipher.final(\"utf8\");\n }\n}\n"],"mappings":";;AAYO,IAAA;;CASE,SAAS,QAAQ,MAAc,KAAa,IAAoB;EACrE,MAAM,QAAgB,IAAI,SAAS;EACnC,MAAM,SAAS,OAAO,eAAe,OAAO,MAAM,OAAO,KAAK,EAAE;EAChE,OAAO,OAAO,OAAO,MAAM,QAAQ,QAAQ,IAAI,OAAO,MAAM,QAAQ;CACtE;;CAUO,SAAS,QAAQ,MAAc,KAAa,IAAoB;EACrE,MAAM,QAAgB,IAAI,SAAS;EACnC,MAAM,WAAW,OAAO,iBAAiB,OAAO,MAAM,OAAO,KAAK,EAAE;EACpE,OAAO,SAAS,OAAO,MAAM,UAAU,MAAM,IAAI,SAAS,MAAM,MAAM;CACxE;;GACD,aAAA,WAAA,CAAA,EAAD"}
|
package/lib/EncryptedFetcher.mjs
CHANGED
|
@@ -1,3 +1,52 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
//#
|
|
1
|
+
import { AesPkcs5 } from "./AesPkcs5.mjs";
|
|
2
|
+
import { FetcherBase } from "./internal/FetcherBase.mjs";
|
|
3
|
+
//#region src/EncryptedFetcher.ts
|
|
4
|
+
let EncryptedFetcher;
|
|
5
|
+
(function(_EncryptedFetcher) {
|
|
6
|
+
async function fetch(connection, route, input, stringify) {
|
|
7
|
+
if ((route.request?.encrypted === true || route.response?.encrypted) && connection.encryption === void 0) throw new Error("Error on EncryptedFetcher.fetch(): the encryption password has not been configured.");
|
|
8
|
+
const closure = typeof connection.encryption === "function" ? (direction) => (headers, body) => connection.encryption({
|
|
9
|
+
headers,
|
|
10
|
+
body,
|
|
11
|
+
direction
|
|
12
|
+
}) : () => () => connection.encryption;
|
|
13
|
+
return FetcherBase.request({
|
|
14
|
+
className: "EncryptedFetcher",
|
|
15
|
+
encode: route.request?.encrypted === true ? (input, headers) => {
|
|
16
|
+
const p = closure("encode")(headers, input);
|
|
17
|
+
return AesPkcs5.encrypt((stringify ?? JSON.stringify)(input), p.key, p.iv);
|
|
18
|
+
} : (input) => input,
|
|
19
|
+
decode: route.response?.encrypted === true ? (input, headers) => {
|
|
20
|
+
const p = closure("decode")(headers, input);
|
|
21
|
+
const s = AesPkcs5.decrypt(input, p.key, p.iv);
|
|
22
|
+
return s.length ? JSON.parse(s) : s;
|
|
23
|
+
} : (input) => input
|
|
24
|
+
})(connection, route, input, stringify);
|
|
25
|
+
}
|
|
26
|
+
_EncryptedFetcher.fetch = fetch;
|
|
27
|
+
async function propagate(connection, route, input, stringify) {
|
|
28
|
+
if ((route.request?.encrypted === true || route.response?.encrypted) && connection.encryption === void 0) throw new Error("Error on EncryptedFetcher.propagate(): the encryption password has not been configured.");
|
|
29
|
+
const closure = typeof connection.encryption === "function" ? (direction) => (headers, body) => connection.encryption({
|
|
30
|
+
headers,
|
|
31
|
+
body,
|
|
32
|
+
direction
|
|
33
|
+
}) : () => () => connection.encryption;
|
|
34
|
+
return FetcherBase.propagate({
|
|
35
|
+
className: "EncryptedFetcher",
|
|
36
|
+
encode: route.request?.encrypted === true ? (input, headers) => {
|
|
37
|
+
const p = closure("encode")(headers, input);
|
|
38
|
+
return AesPkcs5.encrypt((stringify ?? JSON.stringify)(input), p.key, p.iv);
|
|
39
|
+
} : (input) => input,
|
|
40
|
+
decode: route.response?.encrypted === true ? (input, headers) => {
|
|
41
|
+
const p = closure("decode")(headers, input);
|
|
42
|
+
const s = AesPkcs5.decrypt(input, p.key, p.iv);
|
|
43
|
+
return s.length ? JSON.parse(s) : s;
|
|
44
|
+
} : (input) => input
|
|
45
|
+
})(connection, route, input, stringify);
|
|
46
|
+
}
|
|
47
|
+
_EncryptedFetcher.propagate = propagate;
|
|
48
|
+
})(EncryptedFetcher || (EncryptedFetcher = {}));
|
|
49
|
+
//#endregion
|
|
50
|
+
export { EncryptedFetcher };
|
|
51
|
+
|
|
52
|
+
//# sourceMappingURL=EncryptedFetcher.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"EncryptedFetcher.mjs","sources":[],"sourcesContent":[],"
|
|
1
|
+
{"version":3,"file":"EncryptedFetcher.mjs","names":[],"sources":["../src/EncryptedFetcher.ts"],"sourcesContent":["import { AesPkcs5 } from \"./AesPkcs5\";\nimport { IConnection } from \"./IConnection\";\nimport { IEncryptionPassword } from \"./IEncryptionPassword\";\nimport { IFetchRoute } from \"./IFetchRoute\";\nimport { IPropagation } from \"./IPropagation\";\nimport { FetcherBase } from \"./internal/FetcherBase\";\n\n/**\n * Utility class for `fetch` functions used in `@nestia/sdk` with encryption.\n *\n * `EncryptedFetcher` is a utility class designed for SDK functions generated by\n * [`@nestia/sdk`](https://nestia.io/docs/sdk/sdk), interacting with the remote\n * HTTP API encrypted by AES-PKCS algorithm. In other words, this is a\n * collection of dedicated `fetch()` functions for `@nestia/sdk` with\n * encryption.\n *\n * For reference, `EncryptedFetcher` class being used only when target\n * controller method is encrypting body data by `@EncryptedRoute` or\n * `@EncryptedBody` decorators. If those decorators are not used,\n * {@link PlainFetcher} class would be used instead.\n *\n * @author Jeongho Nam - https://github.com/samchon\n */\nexport namespace EncryptedFetcher {\n /**\n * Fetch function only for `HEAD` method.\n *\n * @param connection Connection information for the remote HTTP server\n * @param route Route information about the target API\n * @returns Nothing because of `HEAD` method\n */\n export function fetch(\n connection: IConnection,\n route: IFetchRoute<\"HEAD\">,\n ): Promise<void>;\n\n /**\n * Fetch function only for `GET` method.\n *\n * @param connection Connection information for the remote HTTP server\n * @param route Route information about the target API\n * @returns Response body data from the remote API\n */\n export function fetch<Output>(\n connection: IConnection,\n route: IFetchRoute<\"GET\">,\n ): Promise<Output>;\n\n /**\n * Fetch function for the `POST`, `PUT`, `PATCH` and `DELETE` methods.\n *\n * @param connection Connection information for the remote HTTP server\n * @param route Route information about the target API\n * @returns Response body data from the remote API\n */\n export function fetch<Input, Output>(\n connection: IConnection,\n route: IFetchRoute<\"POST\" | \"PUT\" | \"PATCH\" | \"DELETE\">,\n input?: Input,\n stringify?: (input: Input) => string,\n ): Promise<Output>;\n\n export async function fetch<Input, Output>(\n connection: IConnection,\n route: IFetchRoute<\"DELETE\" | \"GET\" | \"HEAD\" | \"PATCH\" | \"POST\" | \"PUT\">,\n input?: Input,\n stringify?: (input: Input) => string,\n ): Promise<Output> {\n if (\n (route.request?.encrypted === true || route.response?.encrypted) &&\n connection.encryption === undefined\n )\n throw new Error(\n \"Error on EncryptedFetcher.fetch(): the encryption password has not been configured.\",\n );\n const closure =\n typeof connection.encryption === \"function\"\n ? (direction: \"encode\" | \"decode\") =>\n (\n headers: Record<string, IConnection.HeaderValue | undefined>,\n body: string,\n ) =>\n (connection.encryption as IEncryptionPassword.Closure)({\n headers,\n body,\n direction,\n })\n : () => () => connection.encryption as IEncryptionPassword;\n\n return FetcherBase.request({\n className: \"EncryptedFetcher\",\n encode:\n route.request?.encrypted === true\n ? (input, headers) => {\n const p: IEncryptionPassword = closure(\"encode\")(headers, input);\n return AesPkcs5.encrypt(\n (stringify ?? JSON.stringify)(input),\n p.key,\n p.iv,\n );\n }\n : (input) => input,\n decode:\n route.response?.encrypted === true\n ? (input, headers) => {\n const p: IEncryptionPassword = closure(\"decode\")(headers, input);\n const s: string = AesPkcs5.decrypt(input, p.key, p.iv);\n return s.length ? JSON.parse(s) : s;\n }\n : (input) => input,\n })(connection, route, input, stringify);\n }\n\n export function propagate<Output extends IPropagation<any, any>>(\n connection: IConnection,\n route: IFetchRoute<\"GET\" | \"HEAD\">,\n ): Promise<Output>;\n\n export function propagate<Input, Output extends IPropagation<any, any>>(\n connection: IConnection,\n route: IFetchRoute<\"DELETE\" | \"GET\" | \"HEAD\" | \"PATCH\" | \"POST\" | \"PUT\">,\n input?: Input,\n stringify?: (input: Input) => string,\n ): Promise<Output>;\n\n export async function propagate<Input, Output extends IPropagation<any, any>>(\n connection: IConnection,\n route: IFetchRoute<\"DELETE\" | \"GET\" | \"HEAD\" | \"PATCH\" | \"POST\" | \"PUT\">,\n input?: Input,\n stringify?: (input: Input) => string,\n ): Promise<Output> {\n if (\n (route.request?.encrypted === true || route.response?.encrypted) &&\n connection.encryption === undefined\n )\n throw new Error(\n \"Error on EncryptedFetcher.propagate(): the encryption password has not been configured.\",\n );\n const closure =\n typeof connection.encryption === \"function\"\n ? (direction: \"encode\" | \"decode\") =>\n (\n headers: Record<string, IConnection.HeaderValue | undefined>,\n body: string,\n ) =>\n (connection.encryption as IEncryptionPassword.Closure)({\n headers,\n body,\n direction,\n })\n : () => () => connection.encryption as IEncryptionPassword;\n\n return FetcherBase.propagate({\n className: \"EncryptedFetcher\",\n encode:\n route.request?.encrypted === true\n ? (input, headers) => {\n const p: IEncryptionPassword = closure(\"encode\")(headers, input);\n return AesPkcs5.encrypt(\n (stringify ?? JSON.stringify)(input),\n p.key,\n p.iv,\n );\n }\n : (input) => input,\n decode:\n route.response?.encrypted === true\n ? (input, headers) => {\n const p: IEncryptionPassword = closure(\"decode\")(headers, input);\n const s: string = AesPkcs5.decrypt(input, p.key, p.iv);\n return s.length ? JSON.parse(s) : s;\n }\n : (input) => input,\n })(connection, route, input, stringify) as Promise<Output>;\n }\n}\n"],"mappings":";;;AAuBO,IAAA;;CAuCE,eAAe,MACpB,YACA,OACA,OACA,WACiB;EACjB,KACG,MAAM,SAAS,cAAc,QAAQ,MAAM,UAAU,cACtD,WAAW,eAAe,KAAA,GAE1B,MAAM,IAAI,MACR,qFACF;EACF,MAAM,UACJ,OAAO,WAAW,eAAe,cAC5B,eAEG,SACA,SAEC,WAAW,WAA2C;GACrD;GACA;GACA;EACF,CAAC,gBACO,WAAW;EAE7B,OAAO,YAAY,QAAQ;GACzB,WAAW;GACX,QACE,MAAM,SAAS,cAAc,QACxB,OAAO,YAAY;IAClB,MAAM,IAAyB,QAAQ,QAAQ,CAAC,CAAC,SAAS,KAAK;IAC/D,OAAO,SAAS,SACb,aAAa,KAAK,UAAA,CAAW,KAAK,GACnC,EAAE,KACF,EAAE,EACJ;GACF,KACC,UAAU;GACjB,QACE,MAAM,UAAU,cAAc,QACzB,OAAO,YAAY;IAClB,MAAM,IAAyB,QAAQ,QAAQ,CAAC,CAAC,SAAS,KAAK;IAC/D,MAAM,IAAY,SAAS,QAAQ,OAAO,EAAE,KAAK,EAAE,EAAE;IACrD,OAAO,EAAE,SAAS,KAAK,MAAM,CAAC,IAAI;GACpC,KACC,UAAU;EACnB,CAAC,CAAC,CAAC,YAAY,OAAO,OAAO,SAAS;CACxC;;CAcO,eAAe,UACpB,YACA,OACA,OACA,WACiB;EACjB,KACG,MAAM,SAAS,cAAc,QAAQ,MAAM,UAAU,cACtD,WAAW,eAAe,KAAA,GAE1B,MAAM,IAAI,MACR,yFACF;EACF,MAAM,UACJ,OAAO,WAAW,eAAe,cAC5B,eAEG,SACA,SAEC,WAAW,WAA2C;GACrD;GACA;GACA;EACF,CAAC,gBACO,WAAW;EAE7B,OAAO,YAAY,UAAU;GAC3B,WAAW;GACX,QACE,MAAM,SAAS,cAAc,QACxB,OAAO,YAAY;IAClB,MAAM,IAAyB,QAAQ,QAAQ,CAAC,CAAC,SAAS,KAAK;IAC/D,OAAO,SAAS,SACb,aAAa,KAAK,UAAA,CAAW,KAAK,GACnC,EAAE,KACF,EAAE,EACJ;GACF,KACC,UAAU;GACjB,QACE,MAAM,UAAU,cAAc,QACzB,OAAO,YAAY;IAClB,MAAM,IAAyB,QAAQ,QAAQ,CAAC,CAAC,SAAS,KAAK;IAC/D,MAAM,IAAY,SAAS,QAAQ,OAAO,EAAE,KAAK,EAAE,EAAE;IACrD,OAAO,EAAE,SAAS,KAAK,MAAM,CAAC,IAAI;GACpC,KACC,UAAU;EACnB,CAAC,CAAC,CAAC,YAAY,OAAO,OAAO,SAAS;CACxC;;GACD,qBAAA,mBAAA,CAAA,EAAD"}
|
package/lib/FormDataInput.mjs
CHANGED
package/lib/HttpError.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
import { HttpError } from "@typia/utils";
|
|
2
|
+
export { HttpError };
|
package/lib/IConnection.mjs
CHANGED
package/lib/IFetchEvent.mjs
CHANGED
package/lib/IFetchRoute.mjs
CHANGED
package/lib/IPropagation.mjs
CHANGED
package/lib/NestiaSimulator.mjs
CHANGED
|
@@ -1,3 +1,36 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
import { HttpError } from "./HttpError.mjs";
|
|
2
|
+
//#region src/NestiaSimulator.ts
|
|
3
|
+
let NestiaSimulator;
|
|
4
|
+
(function(_NestiaSimulator) {
|
|
5
|
+
_NestiaSimulator.assert = (props) => {
|
|
6
|
+
return {
|
|
7
|
+
param: param(props),
|
|
8
|
+
query: query(props),
|
|
9
|
+
body: body(props)
|
|
10
|
+
};
|
|
11
|
+
};
|
|
12
|
+
const param = (props) => (name) => (task) => {
|
|
13
|
+
validate((exp) => `URL parameter "${name}" is not ${exp.expected} type.`)(props)(task);
|
|
14
|
+
};
|
|
15
|
+
const query = (props) => (task) => validate(() => "Request query parameters are not following the promised type.")(props)(task);
|
|
16
|
+
const body = (props) => (task) => validate(() => "Request body is not following the promised type.")(props)(task);
|
|
17
|
+
const validate = (message, path) => (props) => (task) => {
|
|
18
|
+
try {
|
|
19
|
+
task();
|
|
20
|
+
} catch (exp) {
|
|
21
|
+
if (isTypeGuardError(exp)) throw new HttpError(props.method, props.host + props.path, 400, { "Content-Type": props.contentType }, JSON.stringify({
|
|
22
|
+
method: exp.method,
|
|
23
|
+
path: path ?? exp.path,
|
|
24
|
+
expected: exp.expected,
|
|
25
|
+
value: exp.value,
|
|
26
|
+
message: message(exp)
|
|
27
|
+
}));
|
|
28
|
+
throw exp;
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
})(NestiaSimulator || (NestiaSimulator = {}));
|
|
32
|
+
const isTypeGuardError = (input) => "string" === typeof input.method && (void 0 === input.path || "string" === typeof input.path) && "string" === typeof input.expected && "string" === typeof input.name && "string" === typeof input.message && (void 0 === input.stack || "string" === typeof input.stack);
|
|
33
|
+
//#endregion
|
|
34
|
+
export { NestiaSimulator };
|
|
35
|
+
|
|
36
|
+
//# sourceMappingURL=NestiaSimulator.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"NestiaSimulator.mjs","sources":[],"sourcesContent":[
|
|
1
|
+
{"version":3,"file":"NestiaSimulator.mjs","names":[],"sources":["../src/NestiaSimulator.ts"],"sourcesContent":["import { HttpError } from \"./HttpError\";\n\nexport namespace NestiaSimulator {\n export interface IProps {\n host: string;\n path: string;\n method: \"GET\" | \"POST\" | \"PATCH\" | \"PUT\" | \"DELETE\";\n contentType: string;\n }\n\n export const assert = (props: IProps) => {\n return {\n param: param(props),\n query: query(props),\n body: body(props),\n };\n };\n const param =\n (props: IProps) =>\n (name: string) =>\n <T>(task: () => T): void => {\n validate((exp) => `URL parameter \"${name}\" is not ${exp.expected} type.`)(\n props,\n )(task);\n };\n\n const query =\n (props: IProps) =>\n <T>(task: () => T): void =>\n validate(\n () => \"Request query parameters are not following the promised type.\",\n )(props)(task);\n\n const body =\n (props: IProps) =>\n <T>(task: () => T): void =>\n validate(() => \"Request body is not following the promised type.\")(props)(\n task,\n );\n\n const validate =\n (message: (exp: TypeGuardError) => string, path?: string) =>\n (props: IProps) =>\n <T>(task: () => T): void => {\n try {\n task();\n } catch (exp) {\n if (isTypeGuardError(exp))\n throw new HttpError(\n props.method,\n props.host + props.path,\n 400,\n {\n \"Content-Type\": props.contentType,\n },\n JSON.stringify({\n method: exp.method,\n path: path ?? exp.path,\n expected: exp.expected,\n value: exp.value,\n message: message(exp),\n }),\n );\n throw exp;\n }\n };\n}\n\nconst isTypeGuardError = (input: any): input is TypeGuardError =>\n \"string\" === typeof input.method &&\n (undefined === input.path || \"string\" === typeof input.path) &&\n \"string\" === typeof input.expected &&\n \"string\" === typeof input.name &&\n \"string\" === typeof input.message &&\n (undefined === input.stack || \"string\" === typeof input.stack);\n\ninterface TypeGuardError extends Error {\n method: string;\n path: string | undefined;\n expected: string;\n value: any;\n}\n"],"mappings":";;AAEO,IAAA;;4BAQkB,UAAkB;EACvC,OAAO;GACL,OAAO,MAAM,KAAK;GAClB,OAAO,MAAM,KAAK;GAClB,MAAM,KAAK,KAAK;EAClB;CACF;CACA,MAAM,SACH,WACA,UACG,SAAwB;EAC1B,UAAU,QAAQ,kBAAkB,KAAK,WAAW,IAAI,SAAS,OAAO,CAAC,CACvE,KACF,CAAC,CAAC,IAAI;CACR;CAEF,MAAM,SACH,WACG,SACF,eACQ,+DACR,CAAC,CAAC,KAAK,CAAC,CAAC,IAAI;CAEjB,MAAM,QACH,WACG,SACF,eAAe,kDAAkD,CAAC,CAAC,KAAK,CAAC,CACvE,IACF;CAEJ,MAAM,YACH,SAA0C,UAC1C,WACG,SAAwB;EAC1B,IAAI;GACF,KAAK;EACP,SAAS,KAAK;GACZ,IAAI,iBAAiB,GAAG,GACtB,MAAM,IAAI,UACR,MAAM,QACN,MAAM,OAAO,MAAM,MACnB,KACA,EACE,gBAAgB,MAAM,YACxB,GACA,KAAK,UAAU;IACb,QAAQ,IAAI;IACZ,MAAM,QAAQ,IAAI;IAClB,UAAU,IAAI;IACd,OAAO,IAAI;IACX,SAAS,QAAQ,GAAG;GACtB,CAAC,CACH;GACF,MAAM;EACR;CACF;GACH,oBAAA,kBAAA,CAAA,EAAD;AAEA,MAAM,oBAAoB,UACxB,aAAa,OAAO,MAAM,WACzB,KAAA,MAAc,MAAM,QAAQ,aAAa,OAAO,MAAM,SACvD,aAAa,OAAO,MAAM,YAC1B,aAAa,OAAO,MAAM,QAC1B,aAAa,OAAO,MAAM,YACzB,KAAA,MAAc,MAAM,SAAS,aAAa,OAAO,MAAM"}
|
package/lib/PlainFetcher.mjs
CHANGED
|
@@ -1,3 +1,27 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
import { FetcherBase } from "./internal/FetcherBase.mjs";
|
|
2
|
+
//#region src/PlainFetcher.ts
|
|
3
|
+
let PlainFetcher;
|
|
4
|
+
(function(_PlainFetcher) {
|
|
5
|
+
async function fetch(connection, route, input, stringify) {
|
|
6
|
+
if (route.request?.encrypted === true || route.response?.encrypted === true) throw new Error("Error on PlainFetcher.fetch(): PlainFetcher doesn't have encryption ability. Use EncryptedFetcher instead.");
|
|
7
|
+
return FetcherBase.request({
|
|
8
|
+
className: "PlainFetcher",
|
|
9
|
+
encode: (input) => input,
|
|
10
|
+
decode: (input) => input
|
|
11
|
+
})(connection, route, input, stringify);
|
|
12
|
+
}
|
|
13
|
+
_PlainFetcher.fetch = fetch;
|
|
14
|
+
async function propagate(connection, route, input, stringify) {
|
|
15
|
+
if (route.request?.encrypted === true || route.response?.encrypted === true) throw new Error("Error on PlainFetcher.propagate(): PlainFetcher doesn't have encryption ability. Use EncryptedFetcher instead.");
|
|
16
|
+
return FetcherBase.propagate({
|
|
17
|
+
className: "PlainFetcher",
|
|
18
|
+
encode: (input) => input,
|
|
19
|
+
decode: (input) => input
|
|
20
|
+
})(connection, route, input, stringify);
|
|
21
|
+
}
|
|
22
|
+
_PlainFetcher.propagate = propagate;
|
|
23
|
+
})(PlainFetcher || (PlainFetcher = {}));
|
|
24
|
+
//#endregion
|
|
25
|
+
export { PlainFetcher };
|
|
26
|
+
|
|
27
|
+
//# sourceMappingURL=PlainFetcher.mjs.map
|
package/lib/PlainFetcher.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PlainFetcher.mjs","sources":[],"sourcesContent":[],"
|
|
1
|
+
{"version":3,"file":"PlainFetcher.mjs","names":[],"sources":["../src/PlainFetcher.ts"],"sourcesContent":["import { IConnection } from \"./IConnection\";\nimport { IFetchRoute } from \"./IFetchRoute\";\nimport { IPropagation } from \"./IPropagation\";\nimport { FetcherBase } from \"./internal/FetcherBase\";\n\n/**\n * Utility class for `fetch` functions used in `@nestia/sdk`.\n *\n * `PlainFetcher` is a utility class designed for SDK functions generated by\n * [`@nestia/sdk`](https://nestia.io/docs/sdk/sdk), interacting with the remote\n * HTTP sever API. In other words, this is a collection of dedicated `fetch()`\n * functions for `@nestia/sdk`.\n *\n * For reference, `PlainFetcher` class does not encrypt or decrypt the body data\n * at all. It just delivers plain data without any post processing. If you've\n * defined a controller method through `@EncryptedRoute` or `@EncryptedBody`\n * decorator, then {@liink EncryptedFetcher} class would be used instead.\n *\n * @author Jeongho Nam - https://github.com/samchon\n */\nexport namespace PlainFetcher {\n /**\n * Fetch function only for `HEAD` method.\n *\n * @param connection Connection information for the remote HTTP server\n * @param route Route information about the target API\n * @returns Nothing because of `HEAD` method\n */\n export function fetch(\n connection: IConnection,\n route: IFetchRoute<\"HEAD\">,\n ): Promise<void>;\n\n /**\n * Fetch function only for `GET` method.\n *\n * @param connection Connection information for the remote HTTP server\n * @param route Route information about the target API\n * @returns Response body data from the remote API\n */\n export function fetch<Output>(\n connection: IConnection,\n route: IFetchRoute<\"GET\">,\n ): Promise<Output>;\n\n /**\n * Fetch function for the `POST`, `PUT`, `PATCH` and `DELETE` methods.\n *\n * @param connection Connection information for the remote HTTP server\n * @param route Route information about the target API\n * @returns Response body data from the remote API\n */\n export function fetch<Input, Output>(\n connection: IConnection,\n route: IFetchRoute<\"POST\" | \"PUT\" | \"PATCH\" | \"DELETE\">,\n input?: Input,\n stringify?: (input: Input) => string,\n ): Promise<Output>;\n\n export async function fetch<Input, Output>(\n connection: IConnection,\n route: IFetchRoute<\"DELETE\" | \"GET\" | \"HEAD\" | \"PATCH\" | \"POST\" | \"PUT\">,\n input?: Input,\n stringify?: (input: Input) => string,\n ): Promise<Output> {\n if (route.request?.encrypted === true || route.response?.encrypted === true)\n throw new Error(\n \"Error on PlainFetcher.fetch(): PlainFetcher doesn't have encryption ability. Use EncryptedFetcher instead.\",\n );\n return FetcherBase.request({\n className: \"PlainFetcher\",\n encode: (input) => input,\n decode: (input) => input,\n })(connection, route, input, stringify);\n }\n\n export function propagate<Output extends IPropagation<any, any>>(\n connection: IConnection,\n route: IFetchRoute<\"GET\" | \"HEAD\">,\n ): Promise<Output>;\n\n export function propagate<Input, Output extends IPropagation<any, any>>(\n connection: IConnection,\n route: IFetchRoute<\"DELETE\" | \"GET\" | \"HEAD\" | \"PATCH\" | \"POST\" | \"PUT\">,\n input?: Input,\n stringify?: (input: Input) => string,\n ): Promise<Output>;\n\n export async function propagate<Input, Output extends IPropagation<any, any>>(\n connection: IConnection,\n route: IFetchRoute<\"DELETE\" | \"GET\" | \"HEAD\" | \"PATCH\" | \"POST\" | \"PUT\">,\n input?: Input,\n stringify?: (input: Input) => string,\n ): Promise<Output> {\n if (route.request?.encrypted === true || route.response?.encrypted === true)\n throw new Error(\n \"Error on PlainFetcher.propagate(): PlainFetcher doesn't have encryption ability. Use EncryptedFetcher instead.\",\n );\n return FetcherBase.propagate({\n className: \"PlainFetcher\",\n encode: (input) => input,\n decode: (input) => input,\n })(connection, route, input, stringify) as Promise<Output>;\n }\n}\n"],"mappings":";;AAoBO,IAAA;;CAuCE,eAAe,MACpB,YACA,OACA,OACA,WACiB;EACjB,IAAI,MAAM,SAAS,cAAc,QAAQ,MAAM,UAAU,cAAc,MACrE,MAAM,IAAI,MACR,4GACF;EACF,OAAO,YAAY,QAAQ;GACzB,WAAW;GACX,SAAS,UAAU;GACnB,SAAS,UAAU;EACrB,CAAC,CAAC,CAAC,YAAY,OAAO,OAAO,SAAS;CACxC;;CAcO,eAAe,UACpB,YACA,OACA,OACA,WACiB;EACjB,IAAI,MAAM,SAAS,cAAc,QAAQ,MAAM,UAAU,cAAc,MACrE,MAAM,IAAI,MACR,gHACF;EACF,OAAO,YAAY,UAAU;GAC3B,WAAW;GACX,SAAS,UAAU;GACnB,SAAS,UAAU;EACrB,CAAC,CAAC,CAAC,YAAY,OAAO,OAAO,SAAS;CACxC;;GACD,iBAAA,eAAA,CAAA,EAAD"}
|
package/lib/index.mjs
CHANGED
|
@@ -1,2 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
import { HttpError } from "./HttpError.mjs";
|
|
2
|
+
import { NestiaSimulator } from "./NestiaSimulator.mjs";
|
|
3
|
+
import { PlainFetcher } from "./PlainFetcher.mjs";
|
|
4
|
+
export { HttpError, NestiaSimulator, PlainFetcher };
|
|
@@ -1,3 +1,117 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
//#
|
|
1
|
+
import { HttpError } from "../HttpError.mjs";
|
|
2
|
+
import { is_binary_response_content_type } from "./is_binary_response_content_type.mjs";
|
|
3
|
+
//#region src/internal/FetcherBase.ts
|
|
4
|
+
let FetcherBase;
|
|
5
|
+
(function(_FetcherBase) {
|
|
6
|
+
_FetcherBase.request = (props) => async (connection, route, input, stringify) => {
|
|
7
|
+
const result = await _Propagate("fetch")(props)(connection, route, input, stringify);
|
|
8
|
+
if (result.success === false) throw new HttpError(route.method, route.path, result.status, result.headers, result.data);
|
|
9
|
+
return result.data;
|
|
10
|
+
};
|
|
11
|
+
_FetcherBase.propagate = (props) => async (connection, route, input, stringify) => _Propagate("propagate")(props)(connection, route, input, stringify);
|
|
12
|
+
/** @internal */
|
|
13
|
+
const _Propagate = (method) => (props) => async (connection, route, input, stringify) => {
|
|
14
|
+
const headers = { ...connection.headers ?? {} };
|
|
15
|
+
if (input !== void 0) {
|
|
16
|
+
if (route.request?.type === void 0) throw new Error(`Error on ${props.className}.fetch(): no content-type being configured.`);
|
|
17
|
+
else if (route.request.type !== "multipart/form-data") headers["Content-Type"] = route.request.type;
|
|
18
|
+
} else if (input === void 0 && headers["Content-Type"] !== void 0) delete headers["Content-Type"];
|
|
19
|
+
const init = {
|
|
20
|
+
...connection.options ?? {},
|
|
21
|
+
method: route.method,
|
|
22
|
+
headers: (() => {
|
|
23
|
+
const output = [];
|
|
24
|
+
for (const [key, value] of Object.entries(headers)) if (value === void 0) continue;
|
|
25
|
+
else if (Array.isArray(value)) for (const v of value) output.push([key, String(v)]);
|
|
26
|
+
else output.push([key, String(value)]);
|
|
27
|
+
return output;
|
|
28
|
+
})()
|
|
29
|
+
};
|
|
30
|
+
if (input !== void 0) init.body = props.encode(route.request?.type === "application/x-www-form-urlencoded" ? request_query_body(input) : route.request?.type === "multipart/form-data" ? request_form_data_body(input) : route.request?.type !== "text/plain" ? (stringify ?? JSON.stringify)(input) : input, headers);
|
|
31
|
+
const path = connection.host[connection.host.length - 1] !== "/" && route.path[0] !== "/" ? `/${route.path}` : route.path;
|
|
32
|
+
const url = new URL(`${connection.host}${path}`);
|
|
33
|
+
const event = {
|
|
34
|
+
route,
|
|
35
|
+
path,
|
|
36
|
+
status: null,
|
|
37
|
+
input,
|
|
38
|
+
output: void 0,
|
|
39
|
+
started_at: /* @__PURE__ */ new Date(),
|
|
40
|
+
respond_at: null,
|
|
41
|
+
completed_at: null
|
|
42
|
+
};
|
|
43
|
+
try {
|
|
44
|
+
const response = await (connection.fetch ?? fetch)(url.href, init);
|
|
45
|
+
event.respond_at = /* @__PURE__ */ new Date();
|
|
46
|
+
event.status = response.status;
|
|
47
|
+
const result = {
|
|
48
|
+
success: response.status === 200 || response.status === 201 || response.status === route.status,
|
|
49
|
+
status: response.status,
|
|
50
|
+
headers: response_headers_to_object(response.headers),
|
|
51
|
+
data: void 0
|
|
52
|
+
};
|
|
53
|
+
if (result.success === false) {
|
|
54
|
+
result.data = await response.text();
|
|
55
|
+
const type = response.headers.get("content-type");
|
|
56
|
+
if (method !== "fetch" && type && type.indexOf("application/json") !== -1) try {
|
|
57
|
+
result.data = JSON.parse(result.data);
|
|
58
|
+
} catch {}
|
|
59
|
+
} else if (route.method === "HEAD") result.data = void 0;
|
|
60
|
+
else if (route.response?.type === "application/json") {
|
|
61
|
+
const text = await response.text();
|
|
62
|
+
result.data = text.length ? JSON.parse(text) : void 0;
|
|
63
|
+
} else if (route.response?.type === "application/x-www-form-urlencoded") {
|
|
64
|
+
const query = new URLSearchParams(await response.text());
|
|
65
|
+
result.data = route.parseQuery ? route.parseQuery(query) : query;
|
|
66
|
+
} else if (is_binary_response_content_type(route.response?.type)) result.data = response.body ?? new ReadableStream();
|
|
67
|
+
else result.data = props.decode(await response.text(), result.headers);
|
|
68
|
+
event.output = result.data;
|
|
69
|
+
return result;
|
|
70
|
+
} catch (exp) {
|
|
71
|
+
throw exp;
|
|
72
|
+
} finally {
|
|
73
|
+
event.completed_at = /* @__PURE__ */ new Date();
|
|
74
|
+
if (connection.logger) try {
|
|
75
|
+
await connection.logger(event);
|
|
76
|
+
} catch {}
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
})(FetcherBase || (FetcherBase = {}));
|
|
80
|
+
/** @internal */
|
|
81
|
+
const request_query_body = (input) => {
|
|
82
|
+
const q = new URLSearchParams();
|
|
83
|
+
for (const [key, value] of Object.entries(input)) if (value === void 0) continue;
|
|
84
|
+
else if (Array.isArray(value)) value.forEach((elem) => q.append(key, String(elem)));
|
|
85
|
+
else q.set(key, String(value));
|
|
86
|
+
return q;
|
|
87
|
+
};
|
|
88
|
+
/** @internal */
|
|
89
|
+
const request_form_data_body = (input) => {
|
|
90
|
+
const encoded = new FormData();
|
|
91
|
+
const append = (key) => (value) => {
|
|
92
|
+
if (value === void 0) return;
|
|
93
|
+
else if (typeof File === "function" && value instanceof File) encoded.append(key, value, value.name);
|
|
94
|
+
else encoded.append(key, value);
|
|
95
|
+
};
|
|
96
|
+
for (const [key, value] of Object.entries(input)) if (Array.isArray(value)) value.map(append(key));
|
|
97
|
+
else append(key)(value);
|
|
98
|
+
return encoded;
|
|
99
|
+
};
|
|
100
|
+
/** @internal */
|
|
101
|
+
const response_headers_to_object = (headers) => {
|
|
102
|
+
const output = {};
|
|
103
|
+
const cookieHeaders = typeof headers.getSetCookie === "function" ? headers.getSetCookie() : void 0;
|
|
104
|
+
if (cookieHeaders?.length) output["set-cookie"] = cookieHeaders;
|
|
105
|
+
headers.forEach((value, key) => {
|
|
106
|
+
if (key.toLowerCase() === "set-cookie") {
|
|
107
|
+
if (cookieHeaders?.length) return;
|
|
108
|
+
output[key] ??= [];
|
|
109
|
+
output[key].push(value);
|
|
110
|
+
} else output[key] = value;
|
|
111
|
+
});
|
|
112
|
+
return output;
|
|
113
|
+
};
|
|
114
|
+
//#endregion
|
|
115
|
+
export { FetcherBase };
|
|
116
|
+
|
|
117
|
+
//# sourceMappingURL=FetcherBase.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FetcherBase.mjs","sources":[],"sourcesContent":[],"names":[],"mappings":";"}
|
|
1
|
+
{"version":3,"file":"FetcherBase.mjs","names":[],"sources":["../../src/internal/FetcherBase.ts"],"sourcesContent":["import { HttpError } from \"../HttpError\";\nimport { IConnection } from \"../IConnection\";\nimport { IFetchEvent } from \"../IFetchEvent\";\nimport { IFetchRoute } from \"../IFetchRoute\";\nimport { IPropagation } from \"../IPropagation\";\nimport { is_binary_response_content_type } from \"./is_binary_response_content_type\";\n\n/** @internal */\nexport namespace FetcherBase {\n export interface IProps {\n className: string;\n encode: (\n input: any,\n headers: Record<string, IConnection.HeaderValue | undefined>,\n ) => string;\n decode: (\n input: string,\n headers: Record<string, IConnection.HeaderValue | undefined>,\n ) => any;\n }\n\n export const request =\n (props: IProps) =>\n async <Input, Output>(\n connection: IConnection,\n route: IFetchRoute<\"DELETE\" | \"GET\" | \"HEAD\" | \"PATCH\" | \"POST\" | \"PUT\">,\n input?: Input,\n stringify?: (input: Input) => string,\n ): Promise<Output> => {\n const result = await _Propagate(\"fetch\")(props)(\n connection,\n route,\n input,\n stringify,\n );\n if ((result as any).success === false)\n throw new HttpError(\n route.method,\n route.path,\n result.status as any as number,\n result.headers,\n result.data as string,\n );\n return result.data as Output;\n };\n\n export const propagate =\n (props: IProps) =>\n async <Input>(\n connection: IConnection,\n route: IFetchRoute<\"DELETE\" | \"GET\" | \"HEAD\" | \"PATCH\" | \"POST\" | \"PUT\">,\n input?: Input,\n stringify?: (input: Input) => string,\n ): Promise<IPropagation<any, any>> =>\n _Propagate(\"propagate\")(props)(connection, route, input, stringify);\n\n /** @internal */\n const _Propagate =\n (method: string) =>\n (props: IProps) =>\n async <Input>(\n connection: IConnection,\n route: IFetchRoute<\"DELETE\" | \"GET\" | \"HEAD\" | \"PATCH\" | \"POST\" | \"PUT\">,\n input?: Input,\n stringify?: (input: Input) => string,\n ): Promise<IPropagation<any, any>> => {\n //----\n // REQUEST MESSAGE\n //----\n // METHOD & HEADERS\n const headers: Record<string, IConnection.HeaderValue | undefined> = {\n ...(connection.headers ?? {}),\n };\n if (input !== undefined) {\n if (route.request?.type === undefined)\n throw new Error(\n `Error on ${props.className}.fetch(): no content-type being configured.`,\n );\n else if (route.request.type !== \"multipart/form-data\")\n headers[\"Content-Type\"] = route.request.type;\n } else if (input === undefined && headers[\"Content-Type\"] !== undefined)\n delete headers[\"Content-Type\"];\n\n // INIT REQUEST DATA\n const init: RequestInit = {\n ...(connection.options ?? {}),\n method: route.method,\n headers: (() => {\n const output: [string, string][] = [];\n for (const [key, value] of Object.entries(headers))\n if (value === undefined) continue;\n else if (Array.isArray(value))\n for (const v of value) output.push([key, String(v)]);\n else output.push([key, String(value)]);\n return output;\n })(),\n };\n\n // CONSTRUCT BODY DATA\n if (input !== undefined)\n init.body = props.encode(\n // BODY TRANSFORM\n route.request?.type === \"application/x-www-form-urlencoded\"\n ? request_query_body(input)\n : route.request?.type === \"multipart/form-data\"\n ? request_form_data_body(input as any)\n : route.request?.type !== \"text/plain\"\n ? (stringify ?? JSON.stringify)(input)\n : input,\n headers,\n );\n\n //----\n // RESPONSE MESSAGE\n //----\n // URL SPECIFICATION\n const path: string =\n connection.host[connection.host.length - 1] !== \"/\" &&\n route.path[0] !== \"/\"\n ? `/${route.path}`\n : route.path;\n const url: URL = new URL(`${connection.host}${path}`);\n\n // DO FETCH\n const event: IFetchEvent = {\n route,\n path,\n status: null,\n input,\n output: undefined,\n started_at: new Date(),\n respond_at: null,\n completed_at: null!,\n };\n try {\n // TRY FETCH\n const response: Response = await (connection.fetch ?? fetch)(\n url.href,\n init,\n );\n event.respond_at = new Date();\n event.status = response.status;\n\n // CONSTRUCT RESULT DATA\n const result: IPropagation<any, any> = {\n success:\n response.status === 200 ||\n response.status === 201 ||\n response.status === route.status,\n status: response.status,\n headers: response_headers_to_object(response.headers),\n data: undefined!,\n } as any;\n if ((result as any).success === false) {\n // WHEN FAILED\n result.data = await response.text();\n const type = response.headers.get(\"content-type\");\n if (\n method !== \"fetch\" &&\n type &&\n type.indexOf(\"application/json\") !== -1\n )\n try {\n result.data = JSON.parse(result.data);\n } catch {}\n } else {\n // WHEN SUCCESS\n if (route.method === \"HEAD\") result.data = undefined!;\n else if (route.response?.type === \"application/json\") {\n const text: string = await response.text();\n result.data = text.length ? JSON.parse(text) : undefined;\n } else if (\n route.response?.type === \"application/x-www-form-urlencoded\"\n ) {\n const query: URLSearchParams = new URLSearchParams(\n await response.text(),\n );\n result.data = route.parseQuery ? route.parseQuery(query) : query;\n } else if (is_binary_response_content_type(route.response?.type))\n result.data = response.body ?? new ReadableStream<Uint8Array>();\n else\n result.data = props.decode(await response.text(), result.headers);\n }\n event.output = result.data;\n return result;\n } catch (exp) {\n throw exp;\n } finally {\n event.completed_at = new Date();\n if (connection.logger)\n try {\n await connection.logger(event);\n } catch {}\n }\n };\n}\n\n/** @internal */\nconst request_query_body = (input: any): URLSearchParams => {\n const q: URLSearchParams = new URLSearchParams();\n for (const [key, value] of Object.entries(input))\n if (value === undefined) continue;\n else if (Array.isArray(value))\n value.forEach((elem) => q.append(key, String(elem)));\n else q.set(key, String(value));\n return q;\n};\n\n/** @internal */\nconst request_form_data_body = (input: Record<string, any>): FormData => {\n const encoded: FormData = new FormData();\n const append = (key: string) => (value: any) => {\n if (value === undefined) return;\n else if (typeof File === \"function\" && value instanceof File)\n encoded.append(key, value, value.name);\n else encoded.append(key, value);\n };\n for (const [key, value] of Object.entries(input))\n if (Array.isArray(value)) value.map(append(key));\n else append(key)(value);\n return encoded;\n};\n\n/** @internal */\nconst response_headers_to_object = (\n headers: Headers,\n): Record<string, string | string[]> => {\n const output: Record<string, string | string[]> = {};\n const cookieHeaders: string[] | undefined =\n typeof (headers as any).getSetCookie === \"function\"\n ? (headers as any).getSetCookie()\n : undefined;\n if (cookieHeaders?.length) output[\"set-cookie\"] = cookieHeaders;\n\n headers.forEach((value, key) => {\n if (key.toLowerCase() === \"set-cookie\") {\n if (cookieHeaders?.length) return;\n output[key] ??= [];\n (output[key] as string[]).push(value);\n } else output[key] = value;\n });\n return output;\n};\n"],"mappings":"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"}
|
|
@@ -1,3 +1,10 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
//#region src/internal/is_binary_response_content_type.ts
|
|
2
|
+
const is_binary_response_content_type = (input) => {
|
|
3
|
+
if (typeof input !== "string") return false;
|
|
4
|
+
const value = input.split(";")[0].trim().toLowerCase();
|
|
5
|
+
return value.startsWith("image/") || value.startsWith("video/") || value.startsWith("audio/") || value === "application/octet-stream" || value === "application/pdf";
|
|
6
|
+
};
|
|
7
|
+
//#endregion
|
|
8
|
+
export { is_binary_response_content_type };
|
|
9
|
+
|
|
10
|
+
//# sourceMappingURL=is_binary_response_content_type.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"is_binary_response_content_type.mjs","
|
|
1
|
+
{"version":3,"file":"is_binary_response_content_type.mjs","names":[],"sources":["../../src/internal/is_binary_response_content_type.ts"],"sourcesContent":["export const is_binary_response_content_type = (\n input: string | null | undefined,\n): input is string => {\n if (typeof input !== \"string\") return false;\n\n const value: string = input.split(\";\")[0]!.trim().toLowerCase();\n return (\n value.startsWith(\"image/\") ||\n value.startsWith(\"video/\") ||\n value.startsWith(\"audio/\") ||\n value === \"application/octet-stream\" ||\n value === \"application/pdf\"\n );\n};\n"],"mappings":";AAAA,MAAa,mCACX,UACoB;CACpB,IAAI,OAAO,UAAU,UAAU,OAAO;CAEtC,MAAM,QAAgB,MAAM,MAAM,GAAG,CAAC,CAAC,EAAE,CAAE,KAAK,CAAC,CAAC,YAAY;CAC9D,OACE,MAAM,WAAW,QAAQ,KACzB,MAAM,WAAW,QAAQ,KACzB,MAAM,WAAW,QAAQ,KACzB,UAAU,8BACV,UAAU;AAEd"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nestia/fetcher",
|
|
3
|
-
"version": "12.0.0-rc.
|
|
3
|
+
"version": "12.0.0-rc.4",
|
|
4
4
|
"description": "Fetcher library of Nestia SDK",
|
|
5
5
|
"types": "lib/index.d.ts",
|
|
6
6
|
"main": "lib/index.js",
|
|
@@ -38,17 +38,13 @@
|
|
|
38
38
|
},
|
|
39
39
|
"homepage": "https://nestia.io",
|
|
40
40
|
"dependencies": {
|
|
41
|
-
"@typia/interface": "^13.0.
|
|
42
|
-
"@typia/utils": "^13.0.
|
|
41
|
+
"@typia/interface": "^13.0.1",
|
|
42
|
+
"@typia/utils": "^13.0.1"
|
|
43
43
|
},
|
|
44
44
|
"devDependencies": {
|
|
45
|
-
"@rollup/plugin-commonjs": "^29.0.0",
|
|
46
|
-
"@rollup/plugin-esm-shim": "^0.1.8",
|
|
47
|
-
"@rollup/plugin-node-resolve": "^16.0.3",
|
|
48
45
|
"@types/node": "^25.3.3",
|
|
49
46
|
"rimraf": "^6.1.3",
|
|
50
|
-
"
|
|
51
|
-
"rollup-plugin-node-externals": "^8.1.2"
|
|
47
|
+
"rolldown": "^1.1.5"
|
|
52
48
|
},
|
|
53
49
|
"files": [
|
|
54
50
|
"README.md",
|
|
@@ -61,7 +57,7 @@
|
|
|
61
57
|
"access": "public"
|
|
62
58
|
},
|
|
63
59
|
"scripts": {
|
|
64
|
-
"build": "rimraf lib && ttsc &&
|
|
60
|
+
"build": "rimraf lib && ttsc && rolldown -c && node ../../deploy/verify-package-exports.cjs --require-main --esm",
|
|
65
61
|
"dev": "rimraf lib && ttsc --watch"
|
|
66
62
|
}
|
|
67
63
|
}
|
package/lib/AesPkcs52.mjs
DELETED
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
import { __exports as AesPkcs5$1 } from './_virtual/AesPkcs5.mjs';
|
|
2
|
-
import require$$0 from './_virtual/_node_crypto.mjs';
|
|
3
|
-
|
|
4
|
-
var __importDefault = (AesPkcs5$1 && AesPkcs5$1.__importDefault) || function (mod) {
|
|
5
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
6
|
-
};
|
|
7
|
-
Object.defineProperty(AesPkcs5$1, "__esModule", { value: true });
|
|
8
|
-
var AesPkcs5_2 = AesPkcs5$1.AesPkcs5 = void 0;
|
|
9
|
-
const crypto_1 = __importDefault(require$$0);
|
|
10
|
-
/**
|
|
11
|
-
* Utility class for the AES-128/256 encryption.
|
|
12
|
-
*
|
|
13
|
-
* - AES-128/256
|
|
14
|
-
* - CBC mode
|
|
15
|
-
* - PKCS#5 Padding
|
|
16
|
-
* - Base64 Encoding
|
|
17
|
-
*
|
|
18
|
-
* @author Jeongho Nam - https://github.com/samchon
|
|
19
|
-
*/
|
|
20
|
-
var AesPkcs5;
|
|
21
|
-
(function (AesPkcs5) {
|
|
22
|
-
/**
|
|
23
|
-
* Encrypt data
|
|
24
|
-
*
|
|
25
|
-
* @param data Target data
|
|
26
|
-
* @param key Key value of the encryption.
|
|
27
|
-
* @param iv Initializer Vector for the encryption
|
|
28
|
-
* @returns Encrypted data
|
|
29
|
-
*/
|
|
30
|
-
function encrypt(data, key, iv) {
|
|
31
|
-
const bytes = key.length * 8;
|
|
32
|
-
const cipher = crypto_1.default.createCipheriv(`AES-${bytes}-CBC`, key, iv);
|
|
33
|
-
return cipher.update(data, "utf8", "base64") + cipher.final("base64");
|
|
34
|
-
}
|
|
35
|
-
AesPkcs5.encrypt = encrypt;
|
|
36
|
-
/**
|
|
37
|
-
* Decrypt data.
|
|
38
|
-
*
|
|
39
|
-
* @param data Target data
|
|
40
|
-
* @param key Key value of the decryption.
|
|
41
|
-
* @param iv Initializer Vector for the decryption
|
|
42
|
-
* @returns Decrypted data.
|
|
43
|
-
*/
|
|
44
|
-
function decrypt(data, key, iv) {
|
|
45
|
-
const bytes = key.length * 8;
|
|
46
|
-
const decipher = crypto_1.default.createDecipheriv(`AES-${bytes}-CBC`, key, iv);
|
|
47
|
-
return decipher.update(data, "base64", "utf8") + decipher.final("utf8");
|
|
48
|
-
}
|
|
49
|
-
AesPkcs5.decrypt = decrypt;
|
|
50
|
-
})(AesPkcs5 || (AesPkcs5_2 = AesPkcs5$1.AesPkcs5 = AesPkcs5 = {}));
|
|
51
|
-
|
|
52
|
-
export { AesPkcs5_2 as AesPkcs5, AesPkcs5$1 as default };
|
|
53
|
-
//# sourceMappingURL=AesPkcs52.mjs.map
|
package/lib/AesPkcs52.mjs.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"AesPkcs52.mjs","sources":["AesPkcs5.js"],"sourcesContent":["\"use strict\";\nvar __importDefault = (this && this.__importDefault) || function (mod) {\n return (mod && mod.__esModule) ? mod : { \"default\": mod };\n};\nObject.defineProperty(exports, \"__esModule\", { value: true });\nexports.AesPkcs5 = void 0;\nconst crypto_1 = __importDefault(require(\"crypto\"));\n/**\n * Utility class for the AES-128/256 encryption.\n *\n * - AES-128/256\n * - CBC mode\n * - PKCS#5 Padding\n * - Base64 Encoding\n *\n * @author Jeongho Nam - https://github.com/samchon\n */\nvar AesPkcs5;\n(function (AesPkcs5) {\n /**\n * Encrypt data\n *\n * @param data Target data\n * @param key Key value of the encryption.\n * @param iv Initializer Vector for the encryption\n * @returns Encrypted data\n */\n function encrypt(data, key, iv) {\n const bytes = key.length * 8;\n const cipher = crypto_1.default.createCipheriv(`AES-${bytes}-CBC`, key, iv);\n return cipher.update(data, \"utf8\", \"base64\") + cipher.final(\"base64\");\n }\n AesPkcs5.encrypt = encrypt;\n /**\n * Decrypt data.\n *\n * @param data Target data\n * @param key Key value of the decryption.\n * @param iv Initializer Vector for the decryption\n * @returns Decrypted data.\n */\n function decrypt(data, key, iv) {\n const bytes = key.length * 8;\n const decipher = crypto_1.default.createDecipheriv(`AES-${bytes}-CBC`, key, iv);\n return decipher.update(data, \"base64\", \"utf8\") + decipher.final(\"utf8\");\n }\n AesPkcs5.decrypt = decrypt;\n})(AesPkcs5 || (exports.AesPkcs5 = AesPkcs5 = {}));\n//# sourceMappingURL=AesPkcs5.js.map"],"names":["this","AesPkcs5_1"],"mappings":";;;AACA,IAAI,eAAe,GAAG,CAACA,UAAI,IAAIA,UAAI,CAAC,eAAe,KAAK,UAAU,GAAG,EAAE;AACvE,IAAI,OAAO,CAAC,GAAG,IAAI,GAAG,CAAC,UAAU,IAAI,GAAG,GAAG,EAAE,SAAS,EAAE,GAAG,EAAE;AAC7D,CAAC;AACD,MAAM,CAAC,cAAc,CAACC,UAAO,EAAE,YAAY,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC;AAC7D,IAAA,UAAA,GAAAA,UAAA,CAAA,QAAgB,GAAG;AACnB,MAAM,QAAQ,GAAG,eAAe,CAAC,UAAiB,CAAC;AACnD;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,IAAI,QAAQ;AACZ,CAAC,UAAU,QAAQ,EAAE;AACrB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,IAAI,SAAS,OAAO,CAAC,IAAI,EAAE,GAAG,EAAE,EAAE,EAAE;AACpC,QAAQ,MAAM,KAAK,GAAG,GAAG,CAAC,MAAM,GAAG,CAAC;AACpC,QAAQ,MAAM,MAAM,GAAG,QAAQ,CAAC,OAAO,CAAC,cAAc,CAAC,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,CAAC,EAAE,GAAG,EAAE,EAAE,CAAC;AACnF,QAAQ,OAAO,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,CAAC,GAAG,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC;AAC7E,IAAA;AACA,IAAI,QAAQ,CAAC,OAAO,GAAG,OAAO;AAC9B;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,IAAI,SAAS,OAAO,CAAC,IAAI,EAAE,GAAG,EAAE,EAAE,EAAE;AACpC,QAAQ,MAAM,KAAK,GAAG,GAAG,CAAC,MAAM,GAAG,CAAC;AACpC,QAAQ,MAAM,QAAQ,GAAG,QAAQ,CAAC,OAAO,CAAC,gBAAgB,CAAC,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,CAAC,EAAE,GAAG,EAAE,EAAE,CAAC;AACvF,QAAQ,OAAO,QAAQ,CAAC,MAAM,CAAC,IAAI,EAAE,QAAQ,EAAE,MAAM,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC,MAAM,CAAC;AAC/E,IAAA;AACA,IAAI,QAAQ,CAAC,OAAO,GAAG,OAAO;AAC9B,CAAC,EAAE,QAAQ,KAAK,UAAA,GAAAA,UAAA,CAAA,QAAgB,GAAG,QAAQ,GAAG,EAAE,CAAC,CAAC;;;;"}
|