@nestia/fetcher 14.0.0 → 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 (61) hide show
  1. package/README.md +1 -1
  2. package/lib/AesPkcs5.d.ts +19 -2
  3. package/lib/AesPkcs5.js +21 -4
  4. package/lib/AesPkcs5.js.map +1 -1
  5. package/lib/AesPkcs5.mjs +2 -2
  6. package/lib/AesPkcs5.mjs.map +1 -1
  7. package/lib/EncryptedFetcher.d.ts +34 -3
  8. package/lib/EncryptedFetcher.js +44 -54
  9. package/lib/EncryptedFetcher.js.map +1 -1
  10. package/lib/EncryptedFetcher.mjs +30 -25
  11. package/lib/EncryptedFetcher.mjs.map +1 -1
  12. package/lib/FormDataInput.d.ts +21 -8
  13. package/lib/IConnection.d.ts +49 -14
  14. package/lib/IEncryptionPassword.d.ts +18 -2
  15. package/lib/IFetchEvent.d.ts +25 -0
  16. package/lib/IFetchEvent.js +0 -18
  17. package/lib/IFetchEvent.js.map +1 -1
  18. package/lib/IFetchRoute.d.ts +19 -0
  19. package/lib/IPropagation.d.ts +34 -11
  20. package/lib/NestiaSimulator.d.ts +45 -2
  21. package/lib/NestiaSimulator.js +65 -14
  22. package/lib/NestiaSimulator.js.map +1 -1
  23. package/lib/NestiaSimulator.mjs +39 -9
  24. package/lib/NestiaSimulator.mjs.map +1 -1
  25. package/lib/PathParameter.d.ts +31 -0
  26. package/lib/PathParameter.js +41 -0
  27. package/lib/PathParameter.js.map +1 -0
  28. package/lib/PathParameter.mjs +13 -0
  29. package/lib/PathParameter.mjs.map +1 -0
  30. package/lib/PlainFetcher.d.ts +36 -5
  31. package/lib/PlainFetcher.js +6 -2
  32. package/lib/PlainFetcher.js.map +1 -1
  33. package/lib/PlainFetcher.mjs.map +1 -1
  34. package/lib/index.d.ts +1 -0
  35. package/lib/index.js +1 -0
  36. package/lib/index.js.map +1 -1
  37. package/lib/index.mjs +2 -1
  38. package/lib/internal/FetcherBase.js +35 -12
  39. package/lib/internal/FetcherBase.js.map +1 -1
  40. package/lib/internal/FetcherBase.mjs +14 -10
  41. package/lib/internal/FetcherBase.mjs.map +1 -1
  42. package/lib/internal/is_binary_response_content_type.d.ts +12 -0
  43. package/lib/internal/is_binary_response_content_type.js +12 -0
  44. package/lib/internal/is_binary_response_content_type.js.map +1 -1
  45. package/lib/internal/is_binary_response_content_type.mjs +12 -0
  46. package/lib/internal/is_binary_response_content_type.mjs.map +1 -1
  47. package/package.json +4 -3
  48. package/src/AesPkcs5.ts +21 -4
  49. package/src/EncryptedFetcher.ts +77 -57
  50. package/src/FormDataInput.ts +26 -10
  51. package/src/IConnection.ts +54 -18
  52. package/src/IEncryptionPassword.ts +18 -2
  53. package/src/IFetchEvent.ts +32 -19
  54. package/src/IFetchRoute.ts +19 -0
  55. package/src/IPropagation.ts +41 -17
  56. package/src/NestiaSimulator.ts +83 -17
  57. package/src/PathParameter.ts +40 -0
  58. package/src/PlainFetcher.ts +50 -5
  59. package/src/index.ts +1 -0
  60. package/src/internal/FetcherBase.ts +49 -10
  61. package/src/internal/is_binary_response_content_type.ts +12 -0
@@ -1,4 +1,16 @@
1
1
  //#region src/internal/is_binary_response_content_type.ts
2
+ /**
3
+ * Reports whether a response content type carries a binary body.
4
+ *
5
+ * The parameters of the type (`; charset=...`) and its case are ignored. Image,
6
+ * video, and audio types, `application/octet-stream`, and `application/pdf` are
7
+ * binary, and the fetcher returns their body as a stream instead of text.
8
+ *
9
+ * @evidence contracts/common.md#principled-implementation The media type is the text before the first `;`, trimmed and lower-cased as HTTP defines, and is matched against the binary families and two exact types, so a parameter or a case variant cannot change the decision.
10
+ * @evidence contracts/common.md#clear-and-simple-design One predicate with a type guard, so the caller can narrow the route's content type.
11
+ * @evidence contracts/common.md#prohibited-implementation-shortcuts The rule is a list of media-type families from the HTTP registry rather than a list of routes.
12
+ * @evidence contracts/common.md#meaningful-documentation The comment states the normalization and the binary types.
13
+ */
2
14
  const is_binary_response_content_type = (input) => {
3
15
  if (typeof input !== "string") return false;
4
16
  const value = input.split(";")[0].trim().toLowerCase();
@@ -1 +1 @@
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"}
1
+ {"version":3,"file":"is_binary_response_content_type.mjs","names":[],"sources":["../../src/internal/is_binary_response_content_type.ts"],"sourcesContent":["/**\n * Reports whether a response content type carries a binary body.\n *\n * The parameters of the type (`; charset=...`) and its case are ignored. Image,\n * video, and audio types, `application/octet-stream`, and `application/pdf` are\n * binary, and the fetcher returns their body as a stream instead of text.\n *\n * @evidence contracts/common.md#principled-implementation The media type is the text before the first `;`, trimmed and lower-cased as HTTP defines, and is matched against the binary families and two exact types, so a parameter or a case variant cannot change the decision.\n * @evidence contracts/common.md#clear-and-simple-design One predicate with a type guard, so the caller can narrow the route's content type.\n * @evidence contracts/common.md#prohibited-implementation-shortcuts The rule is a list of media-type families from the HTTP registry rather than a list of routes.\n * @evidence contracts/common.md#meaningful-documentation The comment states the normalization and the binary types.\n */\nexport 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":";;;;;;;;;;;;;AAYA,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": "14.0.0",
3
+ "version": "14.0.2",
4
4
  "description": "Fetcher library of Nestia SDK",
5
5
  "main": "lib/index.js",
6
6
  "exports": {
@@ -56,8 +56,9 @@
56
56
  "access": "public"
57
57
  },
58
58
  "scripts": {
59
- "build": "rimraf lib && ttsc && rolldown -c && node ../../deploy/verify-package-exports.cjs --require-main --esm",
60
- "dev": "rimraf lib && ttsc --watch"
59
+ "build": "rimraf lib && ttsc && rolldown -c",
60
+ "dev": "rimraf lib && ttsc --watch",
61
+ "evidence": "evidence"
61
62
  },
62
63
  "types": "lib/index.d.ts"
63
64
  }
package/src/AesPkcs5.ts CHANGED
@@ -1,14 +1,23 @@
1
1
  import crypto from "crypto";
2
2
 
3
3
  /**
4
- * Utility class for the AES-128/256 encryption.
4
+ * Utilities for AES-CBC encryption.
5
5
  *
6
- * - AES-128/256
6
+ * - AES-128/192/256
7
7
  * - CBC mode
8
8
  * - PKCS#5 Padding
9
9
  * - Base64 Encoding
10
10
  *
11
+ * The key and the initializer vector are strings that Node reads as UTF-8
12
+ * bytes. The variant is chosen by the key's byte length: 16, 24, and 32 bytes
13
+ * select AES-128, AES-192, and AES-256, and any other length is refused by the
14
+ * cipher.
15
+ *
11
16
  * @author Jeongho Nam - https://github.com/samchon
17
+ * @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.
18
+ * @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.
19
+ * @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.
20
+ * @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.
12
21
  */
13
22
  export namespace AesPkcs5 {
14
23
  /**
@@ -18,9 +27,13 @@ export namespace AesPkcs5 {
18
27
  * @param key Key value of the encryption.
19
28
  * @param iv Initializer Vector for the encryption
20
29
  * @returns Encrypted data
30
+ * @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.
31
+ * @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.
32
+ * @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.
33
+ * @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.
21
34
  */
22
35
  export function encrypt(data: string, key: string, iv: string): string {
23
- const bytes: number = key.length * 8;
36
+ const bytes: number = Buffer.byteLength(key, "utf8") * 8;
24
37
  const cipher = crypto.createCipheriv(`AES-${bytes}-CBC`, key, iv);
25
38
  return cipher.update(data, "utf8", "base64") + cipher.final("base64");
26
39
  }
@@ -32,9 +45,13 @@ export namespace AesPkcs5 {
32
45
  * @param key Key value of the decryption.
33
46
  * @param iv Initializer Vector for the decryption
34
47
  * @returns Decrypted data.
48
+ * @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.
49
+ * @evidence contracts/common.md#clear-and-simple-design A single expression over one decipher object, mirroring `encrypt`.
50
+ * @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.
51
+ * @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.
35
52
  */
36
53
  export function decrypt(data: string, key: string, iv: string): string {
37
- const bytes: number = key.length * 8;
54
+ const bytes: number = Buffer.byteLength(key, "utf8") * 8;
38
55
  const decipher = crypto.createDecipheriv(`AES-${bytes}-CBC`, key, iv);
39
56
  return decipher.update(data, "base64", "utf8") + decipher.final("utf8");
40
57
  }
@@ -20,6 +20,10 @@ import { FetcherBase } from "./internal/FetcherBase";
20
20
  * {@link PlainFetcher} class would be used instead.
21
21
  *
22
22
  * @author Jeongho Nam - https://github.com/samchon
23
+ * @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`.
24
+ * @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`.
25
+ * @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.
26
+ * @evidence contracts/common.md#meaningful-documentation The namespace prose says when the generated SDK uses this fetcher instead of `PlainFetcher`.
23
27
  */
24
28
  export namespace EncryptedFetcher {
25
29
  /**
@@ -28,6 +32,10 @@ export namespace EncryptedFetcher {
28
32
  * @param connection Connection information for the remote HTTP server
29
33
  * @param route Route information about the target API
30
34
  * @returns Nothing because of `HEAD` method
35
+ * @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.
36
+ * @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.
37
+ * @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.
38
+ * @evidence contracts/common.md#meaningful-documentation Each overload documents its parameters and return value.
31
39
  */
32
40
  export function fetch(
33
41
  connection: IConnection,
@@ -66,75 +74,91 @@ export namespace EncryptedFetcher {
66
74
  input?: Input,
67
75
  stringify?: (input: Input) => string,
68
76
  ): Promise<Output> {
69
- if (
70
- (route.request?.encrypted === true || route.response?.encrypted) &&
71
- connection.encryption === undefined
72
- )
73
- throw new Error(
74
- "Error on EncryptedFetcher.fetch(): the encryption password has not been configured.",
75
- );
76
- const closure =
77
- typeof connection.encryption === "function"
78
- ? (direction: "encode" | "decode") =>
79
- (
80
- headers: Record<string, IConnection.HeaderValue | undefined>,
81
- body: string,
82
- ) =>
83
- (connection.encryption as IEncryptionPassword.Closure)({
84
- headers,
85
- body,
86
- direction,
87
- })
88
- : () => () => connection.encryption as IEncryptionPassword;
89
-
90
- return FetcherBase.request({
91
- className: "EncryptedFetcher",
92
- encode:
93
- route.request?.encrypted === true
94
- ? (input, headers) => {
95
- const p: IEncryptionPassword = closure("encode")(headers, input);
96
- return AesPkcs5.encrypt(
97
- (stringify ?? JSON.stringify)(input),
98
- p.key,
99
- p.iv,
100
- );
101
- }
102
- : (input) => input,
103
- decode:
104
- route.response?.encrypted === true
105
- ? (input, headers) => {
106
- const p: IEncryptionPassword = closure("decode")(headers, input);
107
- const s: string = AesPkcs5.decrypt(input, p.key, p.iv);
108
- return s.length ? JSON.parse(s) : s;
109
- }
110
- : (input) => input,
111
- })(connection, route, input, stringify);
77
+ return FetcherBase.request(
78
+ codec({ method: "fetch", connection, route, stringify }),
79
+ )(connection, route, input, stringify);
112
80
  }
113
81
 
114
- export function propagate<Output extends IPropagation<any, any>>(
82
+ /**
83
+ * Fetch function that returns every response as an {@link IPropagation},
84
+ * encrypting and decrypting the bodies the route declares encrypted.
85
+ *
86
+ * An HTTP failure status is returned as a failed branch instead of being
87
+ * thrown. A missing encryption password and a transport failure still throw.
88
+ * The output must describe numeric status branches with boolean success and
89
+ * string or string-array headers; its data type remains caller-owned.
90
+ *
91
+ * @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.
92
+ * @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.
93
+ * @evidence contracts/common.md#prohibited-implementation-shortcuts The function adds no status-specific branch: the success flag comes from the status rules in `FetcherBase`.
94
+ * @evidence contracts/common.md#meaningful-documentation The comment states what the returned union carries and which failures still throw.
95
+ */
96
+ export function propagate<
97
+ Output extends {
98
+ success: boolean;
99
+ status: number;
100
+ headers: Record<string, string | string[]>;
101
+ data: unknown;
102
+ },
103
+ >(
115
104
  connection: IConnection,
116
105
  route: IFetchRoute<"GET" | "HEAD">,
117
106
  ): Promise<Output>;
118
107
 
119
- export function propagate<Input, Output extends IPropagation<any, any>>(
108
+ export function propagate<
109
+ Input,
110
+ Output extends {
111
+ success: boolean;
112
+ status: number;
113
+ headers: Record<string, string | string[]>;
114
+ data: unknown;
115
+ },
116
+ >(
120
117
  connection: IConnection,
121
118
  route: IFetchRoute<"DELETE" | "GET" | "HEAD" | "PATCH" | "POST" | "PUT">,
122
119
  input?: Input,
123
120
  stringify?: (input: Input) => string,
124
121
  ): Promise<Output>;
125
122
 
126
- export async function propagate<Input, Output extends IPropagation<any, any>>(
123
+ export async function propagate<
124
+ Input,
125
+ Output extends {
126
+ success: boolean;
127
+ status: number;
128
+ headers: Record<string, string | string[]>;
129
+ data: unknown;
130
+ },
131
+ >(
127
132
  connection: IConnection,
128
133
  route: IFetchRoute<"DELETE" | "GET" | "HEAD" | "PATCH" | "POST" | "PUT">,
129
134
  input?: Input,
130
135
  stringify?: (input: Input) => string,
131
136
  ): Promise<Output> {
137
+ return FetcherBase.propagate(
138
+ codec({ method: "propagate", connection, route, stringify }),
139
+ )(connection, route, input, stringify) as Promise<Output>;
140
+ }
141
+
142
+ /**
143
+ * Builds the body codec of one call: encrypt the request body when the route
144
+ * declares it encrypted, and decrypt the response body likewise.
145
+ *
146
+ * Refuses the call before any request when the route needs a password and the
147
+ * connection has none.
148
+ */
149
+ const codec = <Input>(props: {
150
+ method: "fetch" | "propagate";
151
+ connection: IConnection;
152
+ route: IFetchRoute<"DELETE" | "GET" | "HEAD" | "PATCH" | "POST" | "PUT">;
153
+ stringify: ((input: Input) => string) | undefined;
154
+ }): FetcherBase.IProps => {
155
+ const { connection, route, stringify } = props;
132
156
  if (
133
157
  (route.request?.encrypted === true || route.response?.encrypted) &&
134
158
  connection.encryption === undefined
135
159
  )
136
160
  throw new Error(
137
- "Error on EncryptedFetcher.propagate(): the encryption password has not been configured.",
161
+ `Error on EncryptedFetcher.${props.method}(): the encryption password has not been configured.`,
138
162
  );
139
163
  const closure =
140
164
  typeof connection.encryption === "function"
@@ -149,18 +173,14 @@ export namespace EncryptedFetcher {
149
173
  direction,
150
174
  })
151
175
  : () => () => connection.encryption as IEncryptionPassword;
152
-
153
- return FetcherBase.propagate({
176
+ return {
154
177
  className: "EncryptedFetcher",
155
178
  encode:
156
179
  route.request?.encrypted === true
157
180
  ? (input, headers) => {
158
- const p: IEncryptionPassword = closure("encode")(headers, input);
159
- return AesPkcs5.encrypt(
160
- (stringify ?? JSON.stringify)(input),
161
- p.key,
162
- p.iv,
163
- );
181
+ const text: string = (stringify ?? JSON.stringify)(input);
182
+ const p: IEncryptionPassword = closure("encode")(headers, text);
183
+ return AesPkcs5.encrypt(text, p.key, p.iv);
164
184
  }
165
185
  : (input) => input,
166
186
  decode:
@@ -171,6 +191,6 @@ export namespace EncryptedFetcher {
171
191
  return s.length ? JSON.parse(s) : s;
172
192
  }
173
193
  : (input) => input,
174
- })(connection, route, input, stringify) as Promise<Output>;
175
- }
194
+ };
195
+ };
176
196
  }
@@ -17,12 +17,15 @@
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> =
28
31
  T extends Array<any>
@@ -30,9 +33,7 @@ export type FormDataInput<T extends object> =
30
33
  : T extends Function
31
34
  ? never
32
35
  : {
33
- [P in keyof T]: T[P] extends Array<infer U>
34
- ? FormDataInput.Value<U>[]
35
- : FormDataInput.Value<T[P]>;
36
+ [P in keyof T]: FormDataInput.Value<T[P]>;
36
37
  };
37
38
  export namespace FormDataInput {
38
39
  /**
@@ -43,8 +44,18 @@ export namespace FormDataInput {
43
44
  * If the original value type is a `File` class, `Value<T>` converts it to an
44
45
  * union type of `File` and {@link IFileProps} type which is a structured data
45
46
  * for the URI file location in the React Native environment.
47
+ *
48
+ * @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.
49
+ * @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.
50
+ * @evidence contracts/common.md#prohibited-implementation-shortcuts It is a type and adds no runtime behavior.
51
+ * @evidence contracts/common.md#meaningful-documentation The comment states the conversion for `File` values.
46
52
  */
47
- export type Value<T> = T extends File ? T | IFileProps : T;
53
+ export type Value<T> =
54
+ T extends Array<infer U>
55
+ ? (U extends File ? U | IFileProps : U)[]
56
+ : T extends File
57
+ ? T | IFileProps
58
+ : T;
48
59
 
49
60
  /**
50
61
  * Properties of a file.
@@ -53,19 +64,24 @@ export namespace FormDataInput {
53
64
  * `File` class instance in the `FormData` request.
54
65
  *
55
66
  * Just put the {@link uri URI address} of the local file system with the
56
- * file's {@link name} and {@link type}. It would be casted to the `File` class
57
- * instance automatically in the `FormData` request.
67
+ * file's {@link name} and {@link type}. React Native's FormData implementation
68
+ * consumes this descriptor; the fetcher does not construct a File instance.
58
69
  *
59
70
  * Note that, this `IFileProps` type works only in the React Native
60
71
  * environment. If you are developing a Web or NodeJS application, you have to
61
72
  * utilize the `File` class instance directly.
73
+ *
74
+ * @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.
75
+ * @evidence contracts/common.md#clear-and-simple-design A three-field record with no behavior.
76
+ * @evidence contracts/common.md#prohibited-implementation-shortcuts It is a type and adds no runtime behavior.
77
+ * @evidence contracts/common.md#meaningful-documentation The comment states the platform limit and each field is documented.
62
78
  */
63
79
  export interface IFileProps {
64
80
  /**
65
81
  * URI address of the file.
66
82
  *
67
83
  * In the React Native, the URI address in the local file system can replace
68
- * the `File` class instance. If
84
+ * the `File` class instance.
69
85
  *
70
86
  * @format uri
71
87
  */
@@ -5,16 +5,16 @@ import { IFetchEvent } from "./IFetchEvent";
5
5
  /**
6
6
  * Connection information.
7
7
  *
8
- * `IConnection` is an interface ttype who represents connection information of
9
- * the remote HTTP server. You can target the remote HTTP server by wring the
8
+ * `IConnection` is an interface type that represents connection information of
9
+ * the remote HTTP server. You can target the remote HTTP server by writing the
10
10
  * {@link IConnection.host} variable down. Also, you can configure special header
11
11
  * values by specializing the {@link IConnection.headers} variable.
12
12
  *
13
13
  * If the remote HTTP server encrypts or decrypts its body data through the
14
- * AES-128/256 algorithm, specify the {@link IConnection.encryption} with
14
+ * AES-128/192/256 algorithm, specify the {@link IConnection.encryption} with
15
15
  * {@link IEncryptionPassword} or {@link IEncryptionPassword.Closure} variable.
16
16
  *
17
- * @author Jenogho Nam - https://github.com/samchon
17
+ * @author Jeongho Nam - https://github.com/samchon
18
18
  * @author Seungjun We - https://github.com/SeungjunWe
19
19
  */
20
20
  export interface IConnection<
@@ -23,8 +23,11 @@ export interface IConnection<
23
23
  /** Host address of the remote HTTP server. */
24
24
  host: string;
25
25
 
26
- /** Header values delivered to the remote HTTP server. */
27
- headers?: Record<string, IConnection.HeaderValue> &
26
+ /**
27
+ * Header values delivered to the remote HTTP server; undefined entries are
28
+ * omitted.
29
+ */
30
+ headers?: Record<string, IConnection.HeaderValue | undefined> &
28
31
  IConnection.Headerify<Headers>;
29
32
 
30
33
  /**
@@ -37,7 +40,7 @@ export interface IConnection<
37
40
  * By the way, to utilize this simulation mode, SDK library must be generated
38
41
  * with {@link INestiaConfig.simulate} option, too. Open `nestia.config.ts`
39
42
  * file, and configure {@link INestiaConfig.simulate} property to be `true`.
40
- * Them, newly generated SDK library would have a built-in mock-up data
43
+ * Then, newly generated SDK library would have a built-in mock-up data
41
44
  * generator.
42
45
  *
43
46
  * @default false
@@ -47,9 +50,17 @@ export interface IConnection<
47
50
  /**
48
51
  * Logger function.
49
52
  *
50
- * This function is called when the fetch event is completed.
53
+ * This function is called and awaited after transport and response
54
+ * processing, whether they succeeded or failed. Configuration, body encoding
55
+ * and URL errors that occur before transport do not produce an event. Logger
56
+ * errors are ignored; event input and output share the caller's payload
57
+ * references, so a logger should treat them as read-only.
51
58
  *
52
59
  * @param event Event information of the fetch event.
60
+ * @evidence contracts/common.md#principled-implementation The callback receives the event from the transport finally block after completed_at is set. respond_at and status stay null without a response, and output stays undefined if body processing throws. Its rejection is ignored, but its argument includes mutable payload references and its awaiting can delay completion.
61
+ * @evidence contracts/common.md#clear-and-simple-design One optional callback with a single event argument.
62
+ * @evidence contracts/common.md#prohibited-implementation-shortcuts The optional callback uses the connection's supported logging boundary; the pipeline catches its errors without replacing a transport or decoding failure.
63
+ * @evidence contracts/common.md#meaningful-documentation The comment states when the callback runs, which pretransport errors create no event, that it is awaited, and why shared payload references should be treated as read-only.
53
64
  */
54
65
  logger?: (event: IFetchEvent) => Promise<void>;
55
66
 
@@ -77,6 +88,15 @@ export interface IConnection<
77
88
  */
78
89
  fetch?: typeof fetch;
79
90
  }
91
+ /**
92
+ * Support types of {@link IConnection}: the fetch options, the allowed header
93
+ * values, and the header mapping.
94
+ *
95
+ * @evidence contracts/common.md#principled-implementation The merged connection interface and namespace share one public identity for addressing, transport settings and support types. Header entries accept HeaderValue or undefined omission, while Headerify retains name-specific constraints; the HeaderValue union continues to exclude null and objects.
96
+ * @evidence contracts/common.md#clear-and-simple-design The interface separates addressing, header input, simulation, observation, encryption and fetch options; three supporting types organize their representations without runtime members.
97
+ * @evidence contracts/common.md#prohibited-implementation-shortcuts It contains types only.
98
+ * @evidence contracts/common.md#meaningful-documentation The interface member comments explain addressing, omitted headers and optional transport settings; the logger separately documents completion, awaiting and ignored errors. Each support type has its own documentation.
99
+ */
80
100
  export namespace IConnection {
81
101
  /**
82
102
  * Additional options for the `fetch` function.
@@ -84,9 +104,14 @@ export namespace IConnection {
84
104
  * Almost same with {@link RequestInit} type of the {@link fetch} function, but
85
105
  * `body`, `headers` and `method` properties are omitted.
86
106
  *
87
- * The reason why defining duplicated definition of {@link RequestInit} is for
88
- * legacy NodeJS environments, which does not have the {@link fetch} function
89
- * type.
107
+ * The explicit option record exposes the supported subset independently of
108
+ * changes to {@link RequestInit}. DOM types are still required by the
109
+ * connection's custom fetch and abort signal members.
110
+ *
111
+ * @evidence contracts/common.md#principled-implementation The record exposes a subset of RequestInit options while omitting body, headers and method, whose construction belongs to the route pipeline; AbortSignal and the connection's fetch member still depend on DOM declarations.
112
+ * @evidence contracts/common.md#clear-and-simple-design A flat option record, with each member mirroring one field of the standard request options.
113
+ * @evidence contracts/common.md#prohibited-implementation-shortcuts It is a type; the fetcher spreads the options into the request and then sets the method and headers itself.
114
+ * @evidence contracts/common.md#meaningful-documentation The comment explains the supported subset and remaining DOM type dependency, and each member documents the standard field's meaning.
90
115
  */
91
116
  export interface IOptions {
92
117
  /**
@@ -161,17 +186,20 @@ export namespace IConnection {
161
186
  * Type of allowed header values.
162
187
  *
163
188
  * Only atomic or array of atomic values are allowed.
189
+ *
190
+ * @evidence contracts/common.md#principled-implementation A header value is a string, boolean, number, or bigint, or an array of the non-string atomic types or of strings, which the request pipeline stringifies, one header line per array element.
191
+ * @evidence contracts/common.md#clear-and-simple-design A single union of the allowed atomic and array values.
192
+ * @evidence contracts/common.md#prohibited-implementation-shortcuts It is a type and adds no runtime behavior.
193
+ * @evidence contracts/common.md#meaningful-documentation The comment states that only atomic values and arrays of them are allowed.
164
194
  */
165
195
  export type HeaderValue =
166
196
  | string
167
197
  | boolean
168
198
  | number
169
199
  | bigint
170
- | string
171
200
  | Array<boolean>
172
201
  | Array<number>
173
202
  | Array<bigint>
174
- | Array<number>
175
203
  | Array<string>;
176
204
 
177
205
  /**
@@ -182,7 +210,7 @@ export namespace IConnection {
182
210
  *
183
211
  * Below are list of prohibited in HTTP headers.
184
212
  *
185
- * 1. Value type one of {@link HeaderValue}
213
+ * 1. Value type is neither {@link HeaderValue} nor omitted `undefined`
186
214
  * 2. Key is "set-cookie", but value is not an Array type
187
215
  * 3. Key is one of them, but value is Array type
188
216
  *
@@ -204,12 +232,17 @@ export namespace IConnection {
204
232
  * - "retry-after"
205
233
  * - "server"
206
234
  * - "user-agent"
235
+ *
236
+ * @evidence contracts/common.md#principled-implementation Each key admits only HeaderValue or omitted undefined. Classifying the defined value domain preserves optional array set-cookie inputs; singleton headers reject any array member even in a scalar/array union. An undefined-only domain transmits nothing and stays permitted. Lowercase keys follow HTTP's case-insensitive names, and ordinary keys retain their type.
237
+ * @evidence contracts/common.md#clear-and-simple-design One mapped type that rejects an entry by turning it into `never`, with the rule spelled out in the comment.
238
+ * @evidence contracts/common.md#prohibited-implementation-shortcuts It is a type and adds no runtime behavior.
239
+ * @evidence contracts/common.md#meaningful-documentation The comment lists the prohibited cases and the singleton headers.
207
240
  */
208
241
  export type Headerify<T extends object | undefined> = {
209
242
  [P in keyof T]?: T[P] extends HeaderValue | undefined
210
243
  ? P extends string
211
244
  ? Lowercase<P> extends "set-cookie"
212
- ? T[P] extends Array<HeaderValue>
245
+ ? Exclude<T[P], undefined> extends Array<HeaderValue>
213
246
  ? T[P] | undefined
214
247
  : never
215
248
  : Lowercase<P> extends
@@ -231,9 +264,12 @@ export namespace IConnection {
231
264
  | "retry-after"
232
265
  | "server"
233
266
  | "user-agent"
234
- ? T[P] extends Array<HeaderValue>
235
- ? never
236
- : T[P] | undefined
267
+ ? Extract<
268
+ Exclude<T[P], undefined>,
269
+ Array<HeaderValue>
270
+ > extends never
271
+ ? T[P] | undefined
272
+ : never
237
273
  : T[P] | undefined
238
274
  : never
239
275
  : never;
@@ -4,11 +4,15 @@ import { IConnection } from "./IConnection";
4
4
  * Encryption password.
5
5
  *
6
6
  * `IEncryptionPassword` is a type of interface who represents encryption
7
- * password used by the {@link Fetcher} with AES-128/256 algorithm. If your
7
+ * password used by the {@link Fetcher} with AES-128/192/256 algorithm. If your
8
8
  * encryption password is not fixed but changes according to the input content,
9
9
  * you can utilize the {@link IEncryptionPassword.Closure} function type.
10
10
  *
11
11
  * @author Jeongho Nam - https://github.com/samchon
12
+ * @evidence contracts/common.md#principled-implementation A key and an initialization vector are the two inputs of AES-CBC, so the interface holds exactly them, as strings that `AesPkcs5` reads as UTF-8 bytes.
13
+ * @evidence contracts/common.md#clear-and-simple-design A two-field record whose namespace adds the closure form for passwords that depend on the message.
14
+ * @evidence contracts/common.md#prohibited-implementation-shortcuts It is a type and adds no runtime behavior.
15
+ * @evidence contracts/common.md#meaningful-documentation The comment says what the object represents and links the closure form.
12
16
  */
13
17
  export interface IEncryptionPassword {
14
18
  /** Secret key. */
@@ -24,6 +28,11 @@ export namespace IEncryptionPassword {
24
28
  * `IEncryptionPassword.Closure` is a type of closure function who are
25
29
  * returning the {@link IEncryptionPassword} object. It would be used when your
26
30
  * encryption password be changed according to the input content.
31
+ *
32
+ * @evidence contracts/common.md#principled-implementation A call signature from the message context to a password lets a server-driven scheme choose the key from the headers, the body, and the direction of each call.
33
+ * @evidence contracts/common.md#clear-and-simple-design A callable interface with one signature and no members.
34
+ * @evidence contracts/common.md#prohibited-implementation-shortcuts It is a type and adds no runtime behavior.
35
+ * @evidence contracts/common.md#meaningful-documentation The comment states when to use it and documents the parameter and the return value.
27
36
  */
28
37
  export interface Closure {
29
38
  /**
@@ -35,7 +44,14 @@ export namespace IEncryptionPassword {
35
44
  (props: IProps): IEncryptionPassword;
36
45
  }
37
46
 
38
- /** Properties for the closure. */
47
+ /**
48
+ * Properties for the closure.
49
+ *
50
+ * @evidence contracts/common.md#principled-implementation The record carries what a password closure can choose by: the request or response headers, the body text, and whether the body is being encoded or decoded.
51
+ * @evidence contracts/common.md#clear-and-simple-design A three-field record with no behavior.
52
+ * @evidence contracts/common.md#prohibited-implementation-shortcuts It is a type and adds no runtime behavior.
53
+ * @evidence contracts/common.md#meaningful-documentation The comment names it as the closure's properties; the direction values are self-descriptive.
54
+ */
39
55
  export interface IProps {
40
56
  headers: Record<string, IConnection.HeaderValue | undefined>;
41
57
  body: string;
@@ -1,31 +1,44 @@
1
- // import { IConnection } from "./IConnection";
2
1
  import { IFetchRoute } from "./IFetchRoute";
3
2
 
3
+ /**
4
+ * Event of one completed fetch, passed to {@link IConnection.logger}.
5
+ *
6
+ * `status` and `respond_at` are `null` when no response arrived, and
7
+ * `completed_at` is set after response processing or transport failure. Binary
8
+ * success streams are handed to the caller without reading their bytes, so this
9
+ * timestamp does not measure the duration of their consumption. `input` is the
10
+ * value the caller passed and `output` is the response data, the error body for
11
+ * a failed status, or `undefined` when the request threw. These values retain
12
+ * their original references. Errors before transport, such as encoding or URL
13
+ * construction failures, do not create a logged event.
14
+ *
15
+ * @evidence contracts/common.md#principled-implementation The event records the route, the request input, the observed status and output, and the three instants of the exchange, which is what a logger or a benchmark needs to compute latency and to group by endpoint.
16
+ * @evidence contracts/common.md#clear-and-simple-design A flat record; the two nullable members mark the states with no response.
17
+ * @evidence contracts/common.md#prohibited-implementation-shortcuts Every value is measured by the pipeline; none is defaulted to a plausible value.
18
+ * @evidence contracts/common.md#meaningful-documentation The comment states the states with no response and what input and output hold.
19
+ */
4
20
  export interface IFetchEvent {
21
+ /** Metadata used to execute the request. */
5
22
  route: IFetchRoute<"DELETE" | "GET" | "HEAD" | "PATCH" | "POST" | "PUT">;
23
+
24
+ /** Route path normalized to one leading slash. */
6
25
  path: string;
26
+
27
+ /** Received HTTP status, or null when transport produced no response. */
7
28
  status: number | null;
29
+
30
+ /** Original request value; no defensive copy is made. */
8
31
  input: any;
32
+
33
+ /** Decoded payload or transferred binary stream, absent on processing error. */
9
34
  output: any;
35
+
36
+ /** Start of the transport operation, after request preparation. */
10
37
  started_at: Date;
38
+
39
+ /** Response arrival, before response body processing; null without a response. */
11
40
  respond_at: Date | null;
41
+
42
+ /** End of response processing, before the logger is awaited. */
12
43
  completed_at: Date;
13
44
  }
14
- // export namespace IFetchEvent {
15
- // export interface IFunction {
16
- // (connection: IConnection, ...args: any[]): Promise<any>;
17
- // METADATA: {
18
- // method: "GET" | "POST" | "PUT" | "DELETE" | "PATCH" | "HEAD" | "OPTIONS";
19
- // path: string;
20
- // request: null | {
21
- // type: string;
22
- // encrypted: boolean;
23
- // };
24
- // response: null | {
25
- // type: string;
26
- // encrypted: boolean;
27
- // };
28
- // };
29
- // status: null | number;
30
- // }
31
- // }