@nestia/fetcher 14.0.1 → 14.0.2

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/lib/AesPkcs5.d.ts +19 -2
  2. package/lib/AesPkcs5.js +21 -4
  3. package/lib/AesPkcs5.js.map +1 -1
  4. package/lib/AesPkcs5.mjs +2 -2
  5. package/lib/AesPkcs5.mjs.map +1 -1
  6. package/lib/EncryptedFetcher.d.ts +34 -3
  7. package/lib/EncryptedFetcher.js +44 -54
  8. package/lib/EncryptedFetcher.js.map +1 -1
  9. package/lib/EncryptedFetcher.mjs +30 -25
  10. package/lib/EncryptedFetcher.mjs.map +1 -1
  11. package/lib/FormDataInput.d.ts +21 -8
  12. package/lib/IConnection.d.ts +49 -14
  13. package/lib/IEncryptionPassword.d.ts +18 -2
  14. package/lib/IFetchEvent.d.ts +25 -0
  15. package/lib/IFetchEvent.js +0 -18
  16. package/lib/IFetchEvent.js.map +1 -1
  17. package/lib/IFetchRoute.d.ts +19 -0
  18. package/lib/IPropagation.d.ts +32 -10
  19. package/lib/NestiaSimulator.d.ts +37 -0
  20. package/lib/NestiaSimulator.js +62 -13
  21. package/lib/NestiaSimulator.js.map +1 -1
  22. package/lib/NestiaSimulator.mjs +36 -8
  23. package/lib/NestiaSimulator.mjs.map +1 -1
  24. package/lib/PathParameter.d.ts +8 -0
  25. package/lib/PathParameter.js +8 -0
  26. package/lib/PathParameter.js.map +1 -1
  27. package/lib/PathParameter.mjs.map +1 -1
  28. package/lib/PlainFetcher.d.ts +36 -5
  29. package/lib/PlainFetcher.js +6 -2
  30. package/lib/PlainFetcher.js.map +1 -1
  31. package/lib/PlainFetcher.mjs.map +1 -1
  32. package/lib/internal/FetcherBase.js +31 -7
  33. package/lib/internal/FetcherBase.js.map +1 -1
  34. package/lib/internal/FetcherBase.mjs +11 -5
  35. package/lib/internal/FetcherBase.mjs.map +1 -1
  36. package/lib/internal/is_binary_response_content_type.d.ts +12 -0
  37. package/lib/internal/is_binary_response_content_type.js +12 -0
  38. package/lib/internal/is_binary_response_content_type.js.map +1 -1
  39. package/lib/internal/is_binary_response_content_type.mjs +12 -0
  40. package/lib/internal/is_binary_response_content_type.mjs.map +1 -1
  41. package/package.json +4 -3
  42. package/src/AesPkcs5.ts +21 -4
  43. package/src/EncryptedFetcher.ts +77 -57
  44. package/src/FormDataInput.ts +26 -10
  45. package/src/IConnection.ts +54 -18
  46. package/src/IEncryptionPassword.ts +18 -2
  47. package/src/IFetchEvent.ts +32 -19
  48. package/src/IFetchRoute.ts +19 -0
  49. package/src/IPropagation.ts +32 -10
  50. package/src/NestiaSimulator.ts +66 -14
  51. package/src/PathParameter.ts +8 -0
  52. package/src/PlainFetcher.ts +50 -5
  53. package/src/internal/FetcherBase.ts +44 -6
  54. package/src/internal/is_binary_response_content_type.ts +12 -0
package/lib/AesPkcs5.d.ts CHANGED
@@ -1,12 +1,21 @@
1
1
  /**
2
- * Utility class for the AES-128/256 encryption.
2
+ * Utilities for AES-CBC encryption.
3
3
  *
4
- * - AES-128/256
4
+ * - AES-128/192/256
5
5
  * - CBC mode
6
6
  * - PKCS#5 Padding
7
7
  * - Base64 Encoding
8
8
  *
9
+ * The key and the initializer vector are strings that Node reads as UTF-8
10
+ * bytes. The variant is chosen by the key's byte length: 16, 24, and 32 bytes
11
+ * select AES-128, AES-192, and AES-256, and any other length is refused by the
12
+ * cipher.
13
+ *
9
14
  * @author Jeongho Nam - https://github.com/samchon
15
+ * @evidence contracts/common.md#principled-implementation The functions call Node's `createCipheriv` and `createDecipheriv` with AES in CBC mode, whose default padding is PKCS#5/PKCS#7, and exchange base64 text; the variant is derived from the key's UTF-8 byte length, which is the length Node actually reads, so a key of 16, 24, or 32 bytes selects AES-128, AES-192, or AES-256 and any other length is refused by the cipher. CBC carries no authentication tag, so the format offers confidentiality without integrity; the mode and encoding are fixed by the wire format `@nestia/core`'s encrypted decorators use, so this namespace cannot change them alone.
16
+ * @evidence contracts/common.md#clear-and-simple-design Two functions with the same three inputs and no state; the variant selection is one expression in each because the two directions share nothing else.
17
+ * @evidence contracts/common.md#prohibited-implementation-shortcuts The primitives are the platform's crypto module with no custom cipher, padding, or key derivation; nothing is hardcoded to a key or an initializer vector.
18
+ * @evidence contracts/common.md#meaningful-documentation The comment names the algorithm, the mode, the padding, and the encoding, and states how the key length selects the variant.
10
19
  */
11
20
  export declare namespace AesPkcs5 {
12
21
  /**
@@ -16,6 +25,10 @@ export declare namespace AesPkcs5 {
16
25
  * @param key Key value of the encryption.
17
26
  * @param iv Initializer Vector for the encryption
18
27
  * @returns Encrypted data
28
+ * @evidence contracts/common.md#principled-implementation The plain text is read as UTF-8, encrypted in CBC mode with the key and the initializer vector, and emitted as base64 by concatenating the update and final outputs, so the result decrypts to the same text with the same key and vector.
29
+ * @evidence contracts/common.md#clear-and-simple-design A single expression over one cipher object, with the cipher name derived from the key in the line above.
30
+ * @evidence contracts/common.md#prohibited-implementation-shortcuts The key and the vector come only from the caller, and a key of unsupported size makes Node throw instead of being padded or truncated.
31
+ * @evidence contracts/common.md#meaningful-documentation The parameters and the return value are documented, and the namespace comment states the byte-length rule for the key.
19
32
  */
20
33
  function encrypt(data: string, key: string, iv: string): string;
21
34
  /**
@@ -25,6 +38,10 @@ export declare namespace AesPkcs5 {
25
38
  * @param key Key value of the decryption.
26
39
  * @param iv Initializer Vector for the decryption
27
40
  * @returns Decrypted data.
41
+ * @evidence contracts/common.md#principled-implementation The base64 text is decrypted in CBC mode with the key and the initializer vector, and the update and final outputs are concatenated as UTF-8, so `decrypt(encrypt(x))` returns `x`; wrong keys or corrupted text make the final block check throw rather than return garbage silently in the usual case, although CBC without a tag cannot detect every alteration.
42
+ * @evidence contracts/common.md#clear-and-simple-design A single expression over one decipher object, mirroring `encrypt`.
43
+ * @evidence contracts/common.md#prohibited-implementation-shortcuts It uses the platform's decipher and does not catch its errors, so a padding or key failure reaches the caller.
44
+ * @evidence contracts/common.md#meaningful-documentation The parameters and the return value are documented, and the namespace comment states the byte-length rule for the key.
28
45
  */
29
46
  function decrypt(data: string, key: string, iv: string): string;
30
47
  }
package/lib/AesPkcs5.js CHANGED
@@ -6,14 +6,23 @@ Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.AesPkcs5 = void 0;
7
7
  const crypto_1 = __importDefault(require("crypto"));
8
8
  /**
9
- * Utility class for the AES-128/256 encryption.
9
+ * Utilities for AES-CBC encryption.
10
10
  *
11
- * - AES-128/256
11
+ * - AES-128/192/256
12
12
  * - CBC mode
13
13
  * - PKCS#5 Padding
14
14
  * - Base64 Encoding
15
15
  *
16
+ * The key and the initializer vector are strings that Node reads as UTF-8
17
+ * bytes. The variant is chosen by the key's byte length: 16, 24, and 32 bytes
18
+ * select AES-128, AES-192, and AES-256, and any other length is refused by the
19
+ * cipher.
20
+ *
16
21
  * @author Jeongho Nam - https://github.com/samchon
22
+ * @evidence contracts/common.md#principled-implementation The functions call Node's `createCipheriv` and `createDecipheriv` with AES in CBC mode, whose default padding is PKCS#5/PKCS#7, and exchange base64 text; the variant is derived from the key's UTF-8 byte length, which is the length Node actually reads, so a key of 16, 24, or 32 bytes selects AES-128, AES-192, or AES-256 and any other length is refused by the cipher. CBC carries no authentication tag, so the format offers confidentiality without integrity; the mode and encoding are fixed by the wire format `@nestia/core`'s encrypted decorators use, so this namespace cannot change them alone.
23
+ * @evidence contracts/common.md#clear-and-simple-design Two functions with the same three inputs and no state; the variant selection is one expression in each because the two directions share nothing else.
24
+ * @evidence contracts/common.md#prohibited-implementation-shortcuts The primitives are the platform's crypto module with no custom cipher, padding, or key derivation; nothing is hardcoded to a key or an initializer vector.
25
+ * @evidence contracts/common.md#meaningful-documentation The comment names the algorithm, the mode, the padding, and the encoding, and states how the key length selects the variant.
17
26
  */
18
27
  var AesPkcs5;
19
28
  (function (AesPkcs5) {
@@ -24,9 +33,13 @@ var AesPkcs5;
24
33
  * @param key Key value of the encryption.
25
34
  * @param iv Initializer Vector for the encryption
26
35
  * @returns Encrypted data
36
+ * @evidence contracts/common.md#principled-implementation The plain text is read as UTF-8, encrypted in CBC mode with the key and the initializer vector, and emitted as base64 by concatenating the update and final outputs, so the result decrypts to the same text with the same key and vector.
37
+ * @evidence contracts/common.md#clear-and-simple-design A single expression over one cipher object, with the cipher name derived from the key in the line above.
38
+ * @evidence contracts/common.md#prohibited-implementation-shortcuts The key and the vector come only from the caller, and a key of unsupported size makes Node throw instead of being padded or truncated.
39
+ * @evidence contracts/common.md#meaningful-documentation The parameters and the return value are documented, and the namespace comment states the byte-length rule for the key.
27
40
  */
28
41
  function encrypt(data, key, iv) {
29
- const bytes = key.length * 8;
42
+ const bytes = Buffer.byteLength(key, "utf8") * 8;
30
43
  const cipher = crypto_1.default.createCipheriv(`AES-${bytes}-CBC`, key, iv);
31
44
  return cipher.update(data, "utf8", "base64") + cipher.final("base64");
32
45
  }
@@ -38,9 +51,13 @@ var AesPkcs5;
38
51
  * @param key Key value of the decryption.
39
52
  * @param iv Initializer Vector for the decryption
40
53
  * @returns Decrypted data.
54
+ * @evidence contracts/common.md#principled-implementation The base64 text is decrypted in CBC mode with the key and the initializer vector, and the update and final outputs are concatenated as UTF-8, so `decrypt(encrypt(x))` returns `x`; wrong keys or corrupted text make the final block check throw rather than return garbage silently in the usual case, although CBC without a tag cannot detect every alteration.
55
+ * @evidence contracts/common.md#clear-and-simple-design A single expression over one decipher object, mirroring `encrypt`.
56
+ * @evidence contracts/common.md#prohibited-implementation-shortcuts It uses the platform's decipher and does not catch its errors, so a padding or key failure reaches the caller.
57
+ * @evidence contracts/common.md#meaningful-documentation The parameters and the return value are documented, and the namespace comment states the byte-length rule for the key.
41
58
  */
42
59
  function decrypt(data, key, iv) {
43
- const bytes = key.length * 8;
60
+ const bytes = Buffer.byteLength(key, "utf8") * 8;
44
61
  const decipher = crypto_1.default.createDecipheriv(`AES-${bytes}-CBC`, key, iv);
45
62
  return decipher.update(data, "base64", "utf8") + decipher.final("utf8");
46
63
  }
@@ -1 +1 @@
1
- {"version":3,"file":"AesPkcs5.js","sourceRoot":"","sources":["../src/AesPkcs5.ts"],"names":[],"mappings":";;;;;;AAAA,oDAA4B;AAE5B;;;;;;;;;GASG;AACH,IAAiB,QAAQ,CA4BxB;AA5BD,WAAiB,QAAQ;IACvB;;;;;;;OAOG;IACH,SAAgB,OAAO,CAAC,IAAY,EAAE,GAAW,EAAE,EAAU;QAC3D,MAAM,KAAK,GAAW,GAAG,CAAC,MAAM,GAAG,CAAC,CAAC;QACrC,MAAM,MAAM,GAAG,gBAAM,CAAC,cAAc,CAAC,OAAO,KAAK,MAAM,EAAE,GAAG,EAAE,EAAE,CAAC,CAAC;QAClE,OAAO,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,CAAC,GAAG,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;IACxE,CAAC;IAJe,SAAA,OAAO,UAItB,CAAA;IAED;;;;;;;OAOG;IACH,SAAgB,OAAO,CAAC,IAAY,EAAE,GAAW,EAAE,EAAU;QAC3D,MAAM,KAAK,GAAW,GAAG,CAAC,MAAM,GAAG,CAAC,CAAC;QACrC,MAAM,QAAQ,GAAG,gBAAM,CAAC,gBAAgB,CAAC,OAAO,KAAK,MAAM,EAAE,GAAG,EAAE,EAAE,CAAC,CAAC;QACtE,OAAO,QAAQ,CAAC,MAAM,CAAC,IAAI,EAAE,QAAQ,EAAE,MAAM,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;IAC1E,CAAC;IAJe,SAAA,OAAO,UAItB,CAAA;AACH,CAAC,EA5BgB,QAAQ,aAAR,QAAQ,GAAR,QAAQ,QA4BxB"}
1
+ {"version":3,"file":"AesPkcs5.js","sourceRoot":"","sources":["../src/AesPkcs5.ts"],"names":[],"mappings":";;;;;;AAAA,oDAA4B;AAE5B;;;;;;;;;;;;;;;;;;GAkBG;AACH,IAAiB,QAAQ,CAoCxB;AApCD,WAAiB,QAAQ;IACvB;;;;;;;;;;;OAWG;IACH,SAAgB,OAAO,CAAC,IAAY,EAAE,GAAW,EAAE,EAAU;QAC3D,MAAM,KAAK,GAAW,MAAM,CAAC,UAAU,CAAC,GAAG,EAAE,MAAM,CAAC,GAAG,CAAC,CAAC;QACzD,MAAM,MAAM,GAAG,gBAAM,CAAC,cAAc,CAAC,OAAO,KAAK,MAAM,EAAE,GAAG,EAAE,EAAE,CAAC,CAAC;QAClE,OAAO,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,CAAC,GAAG,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;IACxE,CAAC;IAJe,SAAA,OAAO,UAItB,CAAA;IAED;;;;;;;;;;;OAWG;IACH,SAAgB,OAAO,CAAC,IAAY,EAAE,GAAW,EAAE,EAAU;QAC3D,MAAM,KAAK,GAAW,MAAM,CAAC,UAAU,CAAC,GAAG,EAAE,MAAM,CAAC,GAAG,CAAC,CAAC;QACzD,MAAM,QAAQ,GAAG,gBAAM,CAAC,gBAAgB,CAAC,OAAO,KAAK,MAAM,EAAE,GAAG,EAAE,EAAE,CAAC,CAAC;QACtE,OAAO,QAAQ,CAAC,MAAM,CAAC,IAAI,EAAE,QAAQ,EAAE,MAAM,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;IAC1E,CAAC;IAJe,SAAA,OAAO,UAItB,CAAA;AACH,CAAC,EApCgB,QAAQ,aAAR,QAAQ,GAAR,QAAQ,QAoCxB"}
package/lib/AesPkcs5.mjs CHANGED
@@ -3,13 +3,13 @@ import crypto from "crypto";
3
3
  let AesPkcs5;
4
4
  (function(_AesPkcs) {
5
5
  function encrypt(data, key, iv) {
6
- const bytes = key.length * 8;
6
+ const bytes = Buffer.byteLength(key, "utf8") * 8;
7
7
  const cipher = crypto.createCipheriv(`AES-${bytes}-CBC`, key, iv);
8
8
  return cipher.update(data, "utf8", "base64") + cipher.final("base64");
9
9
  }
10
10
  _AesPkcs.encrypt = encrypt;
11
11
  function decrypt(data, key, iv) {
12
- const bytes = key.length * 8;
12
+ const bytes = Buffer.byteLength(key, "utf8") * 8;
13
13
  const decipher = crypto.createDecipheriv(`AES-${bytes}-CBC`, key, iv);
14
14
  return decipher.update(data, "base64", "utf8") + decipher.final("utf8");
15
15
  }
@@ -1 +1 @@
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"}
1
+ {"version":3,"file":"AesPkcs5.mjs","names":[],"sources":["../src/AesPkcs5.ts"],"sourcesContent":["import crypto from \"crypto\";\n\n/**\n * Utilities for AES-CBC encryption.\n *\n * - AES-128/192/256\n * - CBC mode\n * - PKCS#5 Padding\n * - Base64 Encoding\n *\n * The key and the initializer vector are strings that Node reads as UTF-8\n * bytes. The variant is chosen by the key's byte length: 16, 24, and 32 bytes\n * select AES-128, AES-192, and AES-256, and any other length is refused by the\n * cipher.\n *\n * @author Jeongho Nam - https://github.com/samchon\n * @evidence contracts/common.md#principled-implementation The functions call Node's `createCipheriv` and `createDecipheriv` with AES in CBC mode, whose default padding is PKCS#5/PKCS#7, and exchange base64 text; the variant is derived from the key's UTF-8 byte length, which is the length Node actually reads, so a key of 16, 24, or 32 bytes selects AES-128, AES-192, or AES-256 and any other length is refused by the cipher. CBC carries no authentication tag, so the format offers confidentiality without integrity; the mode and encoding are fixed by the wire format `@nestia/core`'s encrypted decorators use, so this namespace cannot change them alone.\n * @evidence contracts/common.md#clear-and-simple-design Two functions with the same three inputs and no state; the variant selection is one expression in each because the two directions share nothing else.\n * @evidence contracts/common.md#prohibited-implementation-shortcuts The primitives are the platform's crypto module with no custom cipher, padding, or key derivation; nothing is hardcoded to a key or an initializer vector.\n * @evidence contracts/common.md#meaningful-documentation The comment names the algorithm, the mode, the padding, and the encoding, and states how the key length selects the variant.\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 * @evidence contracts/common.md#principled-implementation The plain text is read as UTF-8, encrypted in CBC mode with the key and the initializer vector, and emitted as base64 by concatenating the update and final outputs, so the result decrypts to the same text with the same key and vector.\n * @evidence contracts/common.md#clear-and-simple-design A single expression over one cipher object, with the cipher name derived from the key in the line above.\n * @evidence contracts/common.md#prohibited-implementation-shortcuts The key and the vector come only from the caller, and a key of unsupported size makes Node throw instead of being padded or truncated.\n * @evidence contracts/common.md#meaningful-documentation The parameters and the return value are documented, and the namespace comment states the byte-length rule for the key.\n */\n export function encrypt(data: string, key: string, iv: string): string {\n const bytes: number = Buffer.byteLength(key, \"utf8\") * 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 * @evidence contracts/common.md#principled-implementation The base64 text is decrypted in CBC mode with the key and the initializer vector, and the update and final outputs are concatenated as UTF-8, so `decrypt(encrypt(x))` returns `x`; wrong keys or corrupted text make the final block check throw rather than return garbage silently in the usual case, although CBC without a tag cannot detect every alteration.\n * @evidence contracts/common.md#clear-and-simple-design A single expression over one decipher object, mirroring `encrypt`.\n * @evidence contracts/common.md#prohibited-implementation-shortcuts It uses the platform's decipher and does not catch its errors, so a padding or key failure reaches the caller.\n * @evidence contracts/common.md#meaningful-documentation The parameters and the return value are documented, and the namespace comment states the byte-length rule for the key.\n */\n export function decrypt(data: string, key: string, iv: string): string {\n const bytes: number = Buffer.byteLength(key, \"utf8\") * 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":";;AAqBO,IAAA;;CAaE,SAAS,QAAQ,MAAc,KAAa,IAAoB;EACrE,MAAM,QAAgB,OAAO,WAAW,KAAK,MAAM,IAAI;EACvD,MAAM,SAAS,OAAO,eAAe,OAAO,MAAM,OAAO,KAAK,EAAE;EAChE,OAAO,OAAO,OAAO,MAAM,QAAQ,QAAQ,IAAI,OAAO,MAAM,QAAQ;CACtE;;CAcO,SAAS,QAAQ,MAAc,KAAa,IAAoB;EACrE,MAAM,QAAgB,OAAO,WAAW,KAAK,MAAM,IAAI;EACvD,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"}
@@ -1,6 +1,5 @@
1
1
  import { IConnection } from "./IConnection";
2
2
  import { IFetchRoute } from "./IFetchRoute";
3
- import { IPropagation } from "./IPropagation";
4
3
  /**
5
4
  * Utility class for `fetch` functions used in `@nestia/sdk` with encryption.
6
5
  *
@@ -16,6 +15,10 @@ import { IPropagation } from "./IPropagation";
16
15
  * {@link PlainFetcher} class would be used instead.
17
16
  *
18
17
  * @author Jeongho Nam - https://github.com/samchon
18
+ * @evidence contracts/common.md#principled-implementation A per-call codec encrypts the request body only when the route declares its request encrypted and decrypts the response body only when the route declares its response encrypted; the password is read from the connection, directly or through a closure that receives the headers, the body text, and the direction, where the body is the serialized plain text when encoding and the received cipher text when decoding, as on the server, and the request pipeline itself is shared with `PlainFetcher`.
19
+ * @evidence contracts/common.md#clear-and-simple-design Two public operations, `fetch` and `propagate`, share one private `codec` builder, so the encryption policy exists once and the transport lives in `FetcherBase`.
20
+ * @evidence contracts/common.md#prohibited-implementation-shortcuts No route, header, or key is special-cased: encryption follows the route metadata that `@nestia/sdk` generates, and a missing password is a thrown error rather than a silent plain request.
21
+ * @evidence contracts/common.md#meaningful-documentation The namespace prose says when the generated SDK uses this fetcher instead of `PlainFetcher`.
19
22
  */
20
23
  export declare namespace EncryptedFetcher {
21
24
  /**
@@ -24,6 +27,10 @@ export declare namespace EncryptedFetcher {
24
27
  * @param connection Connection information for the remote HTTP server
25
28
  * @param route Route information about the target API
26
29
  * @returns Nothing because of `HEAD` method
30
+ * @evidence contracts/common.md#principled-implementation The overloads narrow the route method to the argument list and return type that method allows; the implementation builds the codec, which throws before any request when an encrypted route has no password, and delegates to `FetcherBase.request`, which returns the body on success and throws `HttpError` otherwise.
31
+ * @evidence contracts/common.md#clear-and-simple-design The implementation is one delegation, and the overloads exist only for the type-level split between `HEAD`, `GET`, and the body methods.
32
+ * @evidence contracts/common.md#prohibited-implementation-shortcuts The function gates on the route's declared encryption and adds no branch for a particular path or password.
33
+ * @evidence contracts/common.md#meaningful-documentation Each overload documents its parameters and return value.
27
34
  */
28
35
  function fetch(connection: IConnection, route: IFetchRoute<"HEAD">): Promise<void>;
29
36
  /**
@@ -42,6 +49,30 @@ export declare namespace EncryptedFetcher {
42
49
  * @returns Response body data from the remote API
43
50
  */
44
51
  function fetch<Input, Output>(connection: IConnection, route: IFetchRoute<"POST" | "PUT" | "PATCH" | "DELETE">, input?: Input, stringify?: (input: Input) => string): Promise<Output>;
45
- function propagate<Output extends IPropagation<any, any>>(connection: IConnection, route: IFetchRoute<"GET" | "HEAD">): Promise<Output>;
46
- function propagate<Input, Output extends IPropagation<any, any>>(connection: IConnection, route: IFetchRoute<"DELETE" | "GET" | "HEAD" | "PATCH" | "POST" | "PUT">, input?: Input, stringify?: (input: Input) => string): Promise<Output>;
52
+ /**
53
+ * Fetch function that returns every response as an {@link IPropagation},
54
+ * encrypting and decrypting the bodies the route declares encrypted.
55
+ *
56
+ * An HTTP failure status is returned as a failed branch instead of being
57
+ * thrown. A missing encryption password and a transport failure still throw.
58
+ * The output must describe numeric status branches with boolean success and
59
+ * string or string-array headers; its data type remains caller-owned.
60
+ *
61
+ * @evidence contracts/common.md#principled-implementation It builds the same codec as `fetch` and delegates to `FetcherBase.propagate`. Its structural branch constraint accepts numeric literal successes, numeric range failures and unknown-status fallback without instantiating a status map with any, while requiring boolean success and actual numeric status/header shapes.
62
+ * @evidence contracts/common.md#clear-and-simple-design The implementation is one delegation to the shared pipeline, and the overloads only split the argument types by method.
63
+ * @evidence contracts/common.md#prohibited-implementation-shortcuts The function adds no status-specific branch: the success flag comes from the status rules in `FetcherBase`.
64
+ * @evidence contracts/common.md#meaningful-documentation The comment states what the returned union carries and which failures still throw.
65
+ */
66
+ function propagate<Output extends {
67
+ success: boolean;
68
+ status: number;
69
+ headers: Record<string, string | string[]>;
70
+ data: unknown;
71
+ }>(connection: IConnection, route: IFetchRoute<"GET" | "HEAD">): Promise<Output>;
72
+ function propagate<Input, Output extends {
73
+ success: boolean;
74
+ status: number;
75
+ headers: Record<string, string | string[]>;
76
+ data: unknown;
77
+ }>(connection: IConnection, route: IFetchRoute<"DELETE" | "GET" | "HEAD" | "PATCH" | "POST" | "PUT">, input?: Input, stringify?: (input: Input) => string): Promise<Output>;
47
78
  }
@@ -27,72 +27,62 @@ const FetcherBase_1 = require("./internal/FetcherBase");
27
27
  * {@link PlainFetcher} class would be used instead.
28
28
  *
29
29
  * @author Jeongho Nam - https://github.com/samchon
30
+ * @evidence contracts/common.md#principled-implementation A per-call codec encrypts the request body only when the route declares its request encrypted and decrypts the response body only when the route declares its response encrypted; the password is read from the connection, directly or through a closure that receives the headers, the body text, and the direction, where the body is the serialized plain text when encoding and the received cipher text when decoding, as on the server, and the request pipeline itself is shared with `PlainFetcher`.
31
+ * @evidence contracts/common.md#clear-and-simple-design Two public operations, `fetch` and `propagate`, share one private `codec` builder, so the encryption policy exists once and the transport lives in `FetcherBase`.
32
+ * @evidence contracts/common.md#prohibited-implementation-shortcuts No route, header, or key is special-cased: encryption follows the route metadata that `@nestia/sdk` generates, and a missing password is a thrown error rather than a silent plain request.
33
+ * @evidence contracts/common.md#meaningful-documentation The namespace prose says when the generated SDK uses this fetcher instead of `PlainFetcher`.
30
34
  */
31
35
  var EncryptedFetcher;
32
36
  (function (EncryptedFetcher) {
33
37
  function fetch(connection, route, input, stringify) {
34
38
  return __awaiter(this, void 0, void 0, function* () {
35
- var _a, _b, _c, _d;
36
- if ((((_a = route.request) === null || _a === void 0 ? void 0 : _a.encrypted) === true || ((_b = route.response) === null || _b === void 0 ? void 0 : _b.encrypted)) &&
37
- connection.encryption === undefined)
38
- throw new Error("Error on EncryptedFetcher.fetch(): the encryption password has not been configured.");
39
- const closure = typeof connection.encryption === "function"
40
- ? (direction) => (headers, body) => connection.encryption({
41
- headers,
42
- body,
43
- direction,
44
- })
45
- : () => () => connection.encryption;
46
- return FetcherBase_1.FetcherBase.request({
47
- className: "EncryptedFetcher",
48
- encode: ((_c = route.request) === null || _c === void 0 ? void 0 : _c.encrypted) === true
49
- ? (input, headers) => {
50
- const p = closure("encode")(headers, input);
51
- return AesPkcs5_1.AesPkcs5.encrypt((stringify !== null && stringify !== void 0 ? stringify : JSON.stringify)(input), p.key, p.iv);
52
- }
53
- : (input) => input,
54
- decode: ((_d = route.response) === null || _d === void 0 ? void 0 : _d.encrypted) === true
55
- ? (input, headers) => {
56
- const p = closure("decode")(headers, input);
57
- const s = AesPkcs5_1.AesPkcs5.decrypt(input, p.key, p.iv);
58
- return s.length ? JSON.parse(s) : s;
59
- }
60
- : (input) => input,
61
- })(connection, route, input, stringify);
39
+ return FetcherBase_1.FetcherBase.request(codec({ method: "fetch", connection, route, stringify }))(connection, route, input, stringify);
62
40
  });
63
41
  }
64
42
  EncryptedFetcher.fetch = fetch;
65
43
  function propagate(connection, route, input, stringify) {
66
44
  return __awaiter(this, void 0, void 0, function* () {
67
- var _a, _b, _c, _d;
68
- if ((((_a = route.request) === null || _a === void 0 ? void 0 : _a.encrypted) === true || ((_b = route.response) === null || _b === void 0 ? void 0 : _b.encrypted)) &&
69
- connection.encryption === undefined)
70
- throw new Error("Error on EncryptedFetcher.propagate(): the encryption password has not been configured.");
71
- const closure = typeof connection.encryption === "function"
72
- ? (direction) => (headers, body) => connection.encryption({
73
- headers,
74
- body,
75
- direction,
76
- })
77
- : () => () => connection.encryption;
78
- return FetcherBase_1.FetcherBase.propagate({
79
- className: "EncryptedFetcher",
80
- encode: ((_c = route.request) === null || _c === void 0 ? void 0 : _c.encrypted) === true
81
- ? (input, headers) => {
82
- const p = closure("encode")(headers, input);
83
- return AesPkcs5_1.AesPkcs5.encrypt((stringify !== null && stringify !== void 0 ? stringify : JSON.stringify)(input), p.key, p.iv);
84
- }
85
- : (input) => input,
86
- decode: ((_d = route.response) === null || _d === void 0 ? void 0 : _d.encrypted) === true
87
- ? (input, headers) => {
88
- const p = closure("decode")(headers, input);
89
- const s = AesPkcs5_1.AesPkcs5.decrypt(input, p.key, p.iv);
90
- return s.length ? JSON.parse(s) : s;
91
- }
92
- : (input) => input,
93
- })(connection, route, input, stringify);
45
+ return FetcherBase_1.FetcherBase.propagate(codec({ method: "propagate", connection, route, stringify }))(connection, route, input, stringify);
94
46
  });
95
47
  }
96
48
  EncryptedFetcher.propagate = propagate;
49
+ /**
50
+ * Builds the body codec of one call: encrypt the request body when the route
51
+ * declares it encrypted, and decrypt the response body likewise.
52
+ *
53
+ * Refuses the call before any request when the route needs a password and the
54
+ * connection has none.
55
+ */
56
+ const codec = (props) => {
57
+ var _a, _b, _c, _d;
58
+ const { connection, route, stringify } = props;
59
+ if ((((_a = route.request) === null || _a === void 0 ? void 0 : _a.encrypted) === true || ((_b = route.response) === null || _b === void 0 ? void 0 : _b.encrypted)) &&
60
+ connection.encryption === undefined)
61
+ throw new Error(`Error on EncryptedFetcher.${props.method}(): the encryption password has not been configured.`);
62
+ const closure = typeof connection.encryption === "function"
63
+ ? (direction) => (headers, body) => connection.encryption({
64
+ headers,
65
+ body,
66
+ direction,
67
+ })
68
+ : () => () => connection.encryption;
69
+ return {
70
+ className: "EncryptedFetcher",
71
+ encode: ((_c = route.request) === null || _c === void 0 ? void 0 : _c.encrypted) === true
72
+ ? (input, headers) => {
73
+ const text = (stringify !== null && stringify !== void 0 ? stringify : JSON.stringify)(input);
74
+ const p = closure("encode")(headers, text);
75
+ return AesPkcs5_1.AesPkcs5.encrypt(text, p.key, p.iv);
76
+ }
77
+ : (input) => input,
78
+ decode: ((_d = route.response) === null || _d === void 0 ? void 0 : _d.encrypted) === true
79
+ ? (input, headers) => {
80
+ const p = closure("decode")(headers, input);
81
+ const s = AesPkcs5_1.AesPkcs5.decrypt(input, p.key, p.iv);
82
+ return s.length ? JSON.parse(s) : s;
83
+ }
84
+ : (input) => input,
85
+ };
86
+ };
97
87
  })(EncryptedFetcher || (exports.EncryptedFetcher = EncryptedFetcher = {}));
98
88
  //# sourceMappingURL=EncryptedFetcher.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"EncryptedFetcher.js","sourceRoot":"","sources":["../src/EncryptedFetcher.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"EncryptedFetcher.js","sourceRoot":"","sources":["../src/EncryptedFetcher.ts"],"names":[],"mappings":"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"}
@@ -4,47 +4,52 @@ import { FetcherBase } from "./internal/FetcherBase.mjs";
4
4
  let EncryptedFetcher;
5
5
  (function(_EncryptedFetcher) {
6
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);
7
+ return FetcherBase.request(codec({
8
+ method: "fetch",
9
+ connection,
10
+ route,
11
+ stringify
12
+ }))(connection, route, input, stringify);
25
13
  }
26
14
  _EncryptedFetcher.fetch = fetch;
27
15
  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.");
16
+ return FetcherBase.propagate(codec({
17
+ method: "propagate",
18
+ connection,
19
+ route,
20
+ stringify
21
+ }))(connection, route, input, stringify);
22
+ }
23
+ _EncryptedFetcher.propagate = propagate;
24
+ /**
25
+ * Builds the body codec of one call: encrypt the request body when the route
26
+ * declares it encrypted, and decrypt the response body likewise.
27
+ *
28
+ * Refuses the call before any request when the route needs a password and the
29
+ * connection has none.
30
+ */
31
+ const codec = (props) => {
32
+ const { connection, route, stringify } = props;
33
+ if ((route.request?.encrypted === true || route.response?.encrypted) && connection.encryption === void 0) throw new Error(`Error on EncryptedFetcher.${props.method}(): the encryption password has not been configured.`);
29
34
  const closure = typeof connection.encryption === "function" ? (direction) => (headers, body) => connection.encryption({
30
35
  headers,
31
36
  body,
32
37
  direction
33
38
  }) : () => () => connection.encryption;
34
- return FetcherBase.propagate({
39
+ return {
35
40
  className: "EncryptedFetcher",
36
41
  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);
42
+ const text = (stringify ?? JSON.stringify)(input);
43
+ const p = closure("encode")(headers, text);
44
+ return AesPkcs5.encrypt(text, p.key, p.iv);
39
45
  } : (input) => input,
40
46
  decode: route.response?.encrypted === true ? (input, headers) => {
41
47
  const p = closure("decode")(headers, input);
42
48
  const s = AesPkcs5.decrypt(input, p.key, p.iv);
43
49
  return s.length ? JSON.parse(s) : s;
44
50
  } : (input) => input
45
- })(connection, route, input, stringify);
46
- }
47
- _EncryptedFetcher.propagate = propagate;
51
+ };
52
+ };
48
53
  })(EncryptedFetcher || (EncryptedFetcher = {}));
49
54
  //#endregion
50
55
  export { EncryptedFetcher };
@@ -1 +1 @@
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"}
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 * @evidence contracts/common.md#principled-implementation A per-call codec encrypts the request body only when the route declares its request encrypted and decrypts the response body only when the route declares its response encrypted; the password is read from the connection, directly or through a closure that receives the headers, the body text, and the direction, where the body is the serialized plain text when encoding and the received cipher text when decoding, as on the server, and the request pipeline itself is shared with `PlainFetcher`.\n * @evidence contracts/common.md#clear-and-simple-design Two public operations, `fetch` and `propagate`, share one private `codec` builder, so the encryption policy exists once and the transport lives in `FetcherBase`.\n * @evidence contracts/common.md#prohibited-implementation-shortcuts No route, header, or key is special-cased: encryption follows the route metadata that `@nestia/sdk` generates, and a missing password is a thrown error rather than a silent plain request.\n * @evidence contracts/common.md#meaningful-documentation The namespace prose says when the generated SDK uses this fetcher instead of `PlainFetcher`.\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 * @evidence contracts/common.md#principled-implementation The overloads narrow the route method to the argument list and return type that method allows; the implementation builds the codec, which throws before any request when an encrypted route has no password, and delegates to `FetcherBase.request`, which returns the body on success and throws `HttpError` otherwise.\n * @evidence contracts/common.md#clear-and-simple-design The implementation is one delegation, and the overloads exist only for the type-level split between `HEAD`, `GET`, and the body methods.\n * @evidence contracts/common.md#prohibited-implementation-shortcuts The function gates on the route's declared encryption and adds no branch for a particular path or password.\n * @evidence contracts/common.md#meaningful-documentation Each overload documents its parameters and return value.\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 return FetcherBase.request(\n codec({ method: \"fetch\", connection, route, stringify }),\n )(connection, route, input, stringify);\n }\n\n /**\n * Fetch function that returns every response as an {@link IPropagation},\n * encrypting and decrypting the bodies the route declares encrypted.\n *\n * An HTTP failure status is returned as a failed branch instead of being\n * thrown. A missing encryption password and a transport failure still throw.\n * The output must describe numeric status branches with boolean success and\n * string or string-array headers; its data type remains caller-owned.\n *\n * @evidence contracts/common.md#principled-implementation It builds the same codec as `fetch` and delegates to `FetcherBase.propagate`. Its structural branch constraint accepts numeric literal successes, numeric range failures and unknown-status fallback without instantiating a status map with any, while requiring boolean success and actual numeric status/header shapes.\n * @evidence contracts/common.md#clear-and-simple-design The implementation is one delegation to the shared pipeline, and the overloads only split the argument types by method.\n * @evidence contracts/common.md#prohibited-implementation-shortcuts The function adds no status-specific branch: the success flag comes from the status rules in `FetcherBase`.\n * @evidence contracts/common.md#meaningful-documentation The comment states what the returned union carries and which failures still throw.\n */\n export function propagate<\n Output extends {\n success: boolean;\n status: number;\n headers: Record<string, string | string[]>;\n data: unknown;\n },\n >(\n connection: IConnection,\n route: IFetchRoute<\"GET\" | \"HEAD\">,\n ): Promise<Output>;\n\n export function propagate<\n Input,\n Output extends {\n success: boolean;\n status: number;\n headers: Record<string, string | string[]>;\n data: unknown;\n },\n >(\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<\n Input,\n Output extends {\n success: boolean;\n status: number;\n headers: Record<string, string | string[]>;\n data: unknown;\n },\n >(\n connection: IConnection,\n route: IFetchRoute<\"DELETE\" | \"GET\" | \"HEAD\" | \"PATCH\" | \"POST\" | \"PUT\">,\n input?: Input,\n stringify?: (input: Input) => string,\n ): Promise<Output> {\n return FetcherBase.propagate(\n codec({ method: \"propagate\", connection, route, stringify }),\n )(connection, route, input, stringify) as Promise<Output>;\n }\n\n /**\n * Builds the body codec of one call: encrypt the request body when the route\n * declares it encrypted, and decrypt the response body likewise.\n *\n * Refuses the call before any request when the route needs a password and the\n * connection has none.\n */\n const codec = <Input>(props: {\n method: \"fetch\" | \"propagate\";\n connection: IConnection;\n route: IFetchRoute<\"DELETE\" | \"GET\" | \"HEAD\" | \"PATCH\" | \"POST\" | \"PUT\">;\n stringify: ((input: Input) => string) | undefined;\n }): FetcherBase.IProps => {\n const { connection, route, stringify } = props;\n if (\n (route.request?.encrypted === true || route.response?.encrypted) &&\n connection.encryption === undefined\n )\n throw new Error(\n `Error on EncryptedFetcher.${props.method}(): 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 return {\n className: \"EncryptedFetcher\",\n encode:\n route.request?.encrypted === true\n ? (input, headers) => {\n const text: string = (stringify ?? JSON.stringify)(input);\n const p: IEncryptionPassword = closure(\"encode\")(headers, text);\n return AesPkcs5.encrypt(text, p.key, p.iv);\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 };\n };\n}\n"],"mappings":";;;AA2BO,IAAA;;CA2CE,eAAe,MACpB,YACA,OACA,OACA,WACiB;EACjB,OAAO,YAAY,QACjB,MAAM;GAAE,QAAQ;GAAS;GAAY;GAAO;EAAU,CAAC,CACzD,CAAC,CAAC,YAAY,OAAO,OAAO,SAAS;CACvC;;CA2CO,eAAe,UASpB,YACA,OACA,OACA,WACiB;EACjB,OAAO,YAAY,UACjB,MAAM;GAAE,QAAQ;GAAa;GAAY;GAAO;EAAU,CAAC,CAC7D,CAAC,CAAC,YAAY,OAAO,OAAO,SAAS;CACvC;;;;;;;;;CASA,MAAM,SAAgB,UAKI;EACxB,MAAM,EAAE,YAAY,OAAO,cAAc;EACzC,KACG,MAAM,SAAS,cAAc,QAAQ,MAAM,UAAU,cACtD,WAAW,eAAe,KAAA,GAE1B,MAAM,IAAI,MACR,6BAA6B,MAAM,OAAO,qDAC5C;EACF,MAAM,UACJ,OAAO,WAAW,eAAe,cAC5B,eAEG,SACA,SAEC,WAAW,WAA2C;GACrD;GACA;GACA;EACF,CAAC,gBACO,WAAW;EAC7B,OAAO;GACL,WAAW;GACX,QACE,MAAM,SAAS,cAAc,QACxB,OAAO,YAAY;IAClB,MAAM,QAAgB,aAAa,KAAK,UAAA,CAAW,KAAK;IACxD,MAAM,IAAyB,QAAQ,QAAQ,CAAC,CAAC,SAAS,IAAI;IAC9D,OAAO,SAAS,QAAQ,MAAM,EAAE,KAAK,EAAE,EAAE;GAC3C,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;CACF;GACD,qBAAA,mBAAA,CAAA,EAAD"}
@@ -17,15 +17,18 @@
17
17
  * array of `File` class, it converts it to an array of union type of `File` and
18
18
  * {@link FormDataInput.IFileProps} type too.
19
19
  *
20
- * Before | After ----------|------------------------ `boolean` | `boolean`
21
- * `bigint` | `bigint` `number` | `number` `string` | `string` `File` | `File \|
22
- * IFileProps`
20
+ * Atomic fields retain their original types; file fields accept either a File
21
+ * or the React Native file descriptor.
23
22
  *
24
23
  * @author Jeongho Nam - https://github.com/samchon
25
24
  * @template T Target object type.
25
+ * @evidence contracts/common.md#principled-implementation The mapped type delegates each field to a distributive value conversion, so file alternatives and file-array alternatives accept React Native descriptors even when optional or mixed with another field type. Arrays and functions at the outer body level become never because a form body is an object of named fields.
26
+ * @evidence contracts/common.md#clear-and-simple-design One conditional over the object type, delegating the per-value rule to `FormDataInput.Value`.
27
+ * @evidence contracts/common.md#prohibited-implementation-shortcuts It is a type and adds no runtime behavior.
28
+ * @evidence contracts/common.md#meaningful-documentation The comment explains the React Native motive, which fields are converted, and the platform limit of descriptors.
26
29
  */
27
30
  export type FormDataInput<T extends object> = T extends Array<any> ? never : T extends Function ? never : {
28
- [P in keyof T]: T[P] extends Array<infer U> ? FormDataInput.Value<U>[] : FormDataInput.Value<T[P]>;
31
+ [P in keyof T]: FormDataInput.Value<T[P]>;
29
32
  };
30
33
  export declare namespace FormDataInput {
31
34
  /**
@@ -36,8 +39,13 @@ export declare namespace FormDataInput {
36
39
  * If the original value type is a `File` class, `Value<T>` converts it to an
37
40
  * union type of `File` and {@link IFileProps} type which is a structured data
38
41
  * for the URI file location in the React Native environment.
42
+ *
43
+ * @evidence contracts/common.md#principled-implementation Distribution examines each optional or union alternative separately; mutable arrays convert their immediate File elements, File becomes File or its descriptor, and other scalar alternatives retain their types.
44
+ * @evidence contracts/common.md#clear-and-simple-design One value conversion owns scalar and array handling, so the outer mapped type applies one rule to every field.
45
+ * @evidence contracts/common.md#prohibited-implementation-shortcuts It is a type and adds no runtime behavior.
46
+ * @evidence contracts/common.md#meaningful-documentation The comment states the conversion for `File` values.
39
47
  */
40
- type Value<T> = T extends File ? T | IFileProps : T;
48
+ type Value<T> = T extends Array<infer U> ? (U extends File ? U | IFileProps : U)[] : T extends File ? T | IFileProps : T;
41
49
  /**
42
50
  * Properties of a file.
43
51
  *
@@ -45,19 +53,24 @@ export declare namespace FormDataInput {
45
53
  * `File` class instance in the `FormData` request.
46
54
  *
47
55
  * Just put the {@link uri URI address} of the local file system with the
48
- * file's {@link name} and {@link type}. It would be casted to the `File` class
49
- * instance automatically in the `FormData` request.
56
+ * file's {@link name} and {@link type}. React Native's FormData implementation
57
+ * consumes this descriptor; the fetcher does not construct a File instance.
50
58
  *
51
59
  * Note that, this `IFileProps` type works only in the React Native
52
60
  * environment. If you are developing a Web or NodeJS application, you have to
53
61
  * utilize the `File` class instance directly.
62
+ *
63
+ * @evidence contracts/common.md#principled-implementation React Native has no `File` class, so a file is described by the local URI, the file name, and the content type, which is the shape its FormData implementation accepts.
64
+ * @evidence contracts/common.md#clear-and-simple-design A three-field record with no behavior.
65
+ * @evidence contracts/common.md#prohibited-implementation-shortcuts It is a type and adds no runtime behavior.
66
+ * @evidence contracts/common.md#meaningful-documentation The comment states the platform limit and each field is documented.
54
67
  */
55
68
  interface IFileProps {
56
69
  /**
57
70
  * URI address of the file.
58
71
  *
59
72
  * In the React Native, the URI address in the local file system can replace
60
- * the `File` class instance. If
73
+ * the `File` class instance.
61
74
  *
62
75
  * @format uri
63
76
  */