@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.
- package/lib/AesPkcs5.d.ts +19 -2
- package/lib/AesPkcs5.js +21 -4
- package/lib/AesPkcs5.js.map +1 -1
- package/lib/AesPkcs5.mjs +2 -2
- package/lib/AesPkcs5.mjs.map +1 -1
- package/lib/EncryptedFetcher.d.ts +34 -3
- package/lib/EncryptedFetcher.js +44 -54
- package/lib/EncryptedFetcher.js.map +1 -1
- package/lib/EncryptedFetcher.mjs +30 -25
- package/lib/EncryptedFetcher.mjs.map +1 -1
- package/lib/FormDataInput.d.ts +21 -8
- package/lib/IConnection.d.ts +49 -14
- package/lib/IEncryptionPassword.d.ts +18 -2
- package/lib/IFetchEvent.d.ts +25 -0
- package/lib/IFetchEvent.js +0 -18
- package/lib/IFetchEvent.js.map +1 -1
- package/lib/IFetchRoute.d.ts +19 -0
- package/lib/IPropagation.d.ts +32 -10
- package/lib/NestiaSimulator.d.ts +37 -0
- package/lib/NestiaSimulator.js +62 -13
- package/lib/NestiaSimulator.js.map +1 -1
- package/lib/NestiaSimulator.mjs +36 -8
- package/lib/NestiaSimulator.mjs.map +1 -1
- package/lib/PathParameter.d.ts +8 -0
- package/lib/PathParameter.js +8 -0
- package/lib/PathParameter.js.map +1 -1
- package/lib/PathParameter.mjs.map +1 -1
- package/lib/PlainFetcher.d.ts +36 -5
- package/lib/PlainFetcher.js +6 -2
- package/lib/PlainFetcher.js.map +1 -1
- package/lib/PlainFetcher.mjs.map +1 -1
- package/lib/internal/FetcherBase.js +31 -7
- package/lib/internal/FetcherBase.js.map +1 -1
- package/lib/internal/FetcherBase.mjs +11 -5
- package/lib/internal/FetcherBase.mjs.map +1 -1
- package/lib/internal/is_binary_response_content_type.d.ts +12 -0
- package/lib/internal/is_binary_response_content_type.js +12 -0
- package/lib/internal/is_binary_response_content_type.js.map +1 -1
- package/lib/internal/is_binary_response_content_type.mjs +12 -0
- package/lib/internal/is_binary_response_content_type.mjs.map +1 -1
- package/package.json +4 -3
- package/src/AesPkcs5.ts +21 -4
- package/src/EncryptedFetcher.ts +77 -57
- package/src/FormDataInput.ts +26 -10
- package/src/IConnection.ts +54 -18
- package/src/IEncryptionPassword.ts +18 -2
- package/src/IFetchEvent.ts +32 -19
- package/src/IFetchRoute.ts +19 -0
- package/src/IPropagation.ts +32 -10
- package/src/NestiaSimulator.ts +66 -14
- package/src/PathParameter.ts +8 -0
- package/src/PlainFetcher.ts +50 -5
- package/src/internal/FetcherBase.ts +44 -6
- package/src/internal/is_binary_response_content_type.ts +12 -0
package/src/AesPkcs5.ts
CHANGED
|
@@ -1,14 +1,23 @@
|
|
|
1
1
|
import crypto from "crypto";
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
*
|
|
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
|
|
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
|
|
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
|
}
|
package/src/EncryptedFetcher.ts
CHANGED
|
@@ -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
|
-
|
|
70
|
-
(
|
|
71
|
-
|
|
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
|
-
|
|
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<
|
|
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<
|
|
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
|
-
|
|
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
|
|
159
|
-
|
|
160
|
-
|
|
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
|
-
}
|
|
175
|
-
}
|
|
194
|
+
};
|
|
195
|
+
};
|
|
176
196
|
}
|
package/src/FormDataInput.ts
CHANGED
|
@@ -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
|
-
*
|
|
21
|
-
*
|
|
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]
|
|
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> =
|
|
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}.
|
|
57
|
-
*
|
|
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.
|
|
84
|
+
* the `File` class instance.
|
|
69
85
|
*
|
|
70
86
|
* @format uri
|
|
71
87
|
*/
|
package/src/IConnection.ts
CHANGED
|
@@ -5,16 +5,16 @@ import { IFetchEvent } from "./IFetchEvent";
|
|
|
5
5
|
/**
|
|
6
6
|
* Connection information.
|
|
7
7
|
*
|
|
8
|
-
* `IConnection` is an interface
|
|
9
|
-
* the remote HTTP server. You can target the remote HTTP server by
|
|
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
|
|
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
|
-
/**
|
|
27
|
-
|
|
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
|
-
*
|
|
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
|
|
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
|
|
88
|
-
*
|
|
89
|
-
*
|
|
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
|
|
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
|
-
?
|
|
235
|
-
|
|
236
|
-
|
|
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
|
-
/**
|
|
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;
|
package/src/IFetchEvent.ts
CHANGED
|
@@ -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
|
-
// }
|
package/src/IFetchRoute.ts
CHANGED
|
@@ -39,15 +39,34 @@ export interface IFetchRoute<
|
|
|
39
39
|
* If you've forgotten to configuring this `parseQuery` property about the
|
|
40
40
|
* `application/x-www-form-urlencoded` typed response body data, then only the
|
|
41
41
|
* `URLSearchParams` typed instance would be returned instead.
|
|
42
|
+
*
|
|
43
|
+
* @evidence contracts/common.md#principled-implementation A response of type `application/x-www-form-urlencoded` is parsed with the route's function when there is one and is returned as `URLSearchParams` otherwise.
|
|
44
|
+
* @evidence contracts/common.md#clear-and-simple-design One optional method with one argument.
|
|
45
|
+
* @evidence contracts/common.md#prohibited-implementation-shortcuts The parser is route data supplied by the generated SDK, with no default guess about the shape.
|
|
46
|
+
* @evidence contracts/common.md#meaningful-documentation The comment states when it is called and what happens without it.
|
|
42
47
|
*/
|
|
43
48
|
parseQuery?(input: URLSearchParams): any;
|
|
44
49
|
}
|
|
50
|
+
/**
|
|
51
|
+
* Support types of {@link IFetchRoute}: the metadata of a request or response
|
|
52
|
+
* body.
|
|
53
|
+
*
|
|
54
|
+
* @evidence contracts/common.md#principled-implementation The conditional member types encode HTTP: only `DELETE`, `POST`, `PUT`, and `PATCH` requests may have a body, and `HEAD` responses have none, so a route that contradicts its method does not compile.
|
|
55
|
+
* @evidence contracts/common.md#clear-and-simple-design A record of the route facts the pipeline needs (method, path, optional template, bodies, status, query parser), with the nested body metadata in the namespace.
|
|
56
|
+
* @evidence contracts/common.md#prohibited-implementation-shortcuts It is a type and adds no runtime behavior.
|
|
57
|
+
* @evidence contracts/common.md#meaningful-documentation Each member documents its meaning, including that the template exists since version 3.2.2.
|
|
58
|
+
*/
|
|
45
59
|
export namespace IFetchRoute {
|
|
46
60
|
/**
|
|
47
61
|
* Metadata of body.
|
|
48
62
|
*
|
|
49
63
|
* Describes how content-type being used in body, and whether encrypted or
|
|
50
64
|
* not.
|
|
65
|
+
*
|
|
66
|
+
* @evidence contracts/common.md#principled-implementation A body is described by its content type, which selects the encoding, and by whether it is encrypted, which selects the fetcher; the union keeps the common types visible while allowing any other string.
|
|
67
|
+
* @evidence contracts/common.md#clear-and-simple-design Two fields, one of them optional.
|
|
68
|
+
* @evidence contracts/common.md#prohibited-implementation-shortcuts It is a type and adds no runtime behavior.
|
|
69
|
+
* @evidence contracts/common.md#meaningful-documentation The comment states that it describes the content type and the encryption.
|
|
51
70
|
*/
|
|
52
71
|
export interface IBody {
|
|
53
72
|
type:
|