@nestia/fetcher
Advanced tools
Comparing version 2.0.0-dev.20230906 to 2.0.0-dev.20230907
import { IConnection } from "./IConnection"; | ||
import { IPropagation } from "./IPropagation"; | ||
import { Primitive } from "./Primitive"; | ||
@@ -43,2 +44,4 @@ import { IFetchRoute } from "./internal/IFetchRoute"; | ||
function fetch<Input, Output>(connection: IConnection, route: IFetchRoute<"POST" | "PUT" | "PATCH" | "DELETE">, input?: Input, stringify?: (input: Input) => string): Promise<Primitive<Output>>; | ||
function propagate<Output extends IPropagation<number, any, boolean>>(connection: IConnection, route: IFetchRoute<"HEAD">): Promise<Output>; | ||
function propagate<Input, Output extends IPropagation<number, any, boolean>>(connection: IConnection, route: IFetchRoute<"HEAD">, input?: Input, stringify?: (input: Input) => string): Promise<Output>; | ||
} |
@@ -97,3 +97,42 @@ "use strict"; | ||
EncryptedFetcher.fetch = fetch; | ||
function propagate(connection, route, input, stringify) { | ||
var _a, _b, _c, _d; | ||
return __awaiter(this, void 0, void 0, function () { | ||
var closure; | ||
return __generator(this, function (_e) { | ||
if ((((_a = route.request) === null || _a === void 0 ? void 0 : _a.encrypted) === true || ((_b = route.response) === null || _b === void 0 ? void 0 : _b.encrypted)) && | ||
connection.encryption === undefined) | ||
throw new Error("Error on EncryptedFetcher.propagate(): the encryption password has not been configured."); | ||
closure = typeof connection.encryption === "function" | ||
? function (direction) { | ||
return function (headers, body) { | ||
return connection.encryption({ | ||
headers: headers, | ||
body: body, | ||
direction: direction, | ||
}); | ||
}; | ||
} | ||
: function () { return function () { return connection.encryption; }; }; | ||
return [2 /*return*/, FetcherBase_1.FetcherBase.propagate({ | ||
className: "EncryptedFetcher", | ||
encode: ((_c = route.request) === null || _c === void 0 ? void 0 : _c.encrypted) === true | ||
? function (input, headers) { | ||
var p = closure("encode")(headers, input); | ||
return AesPkcs5_1.AesPkcs5.encrypt(JSON.stringify(input), p.key, p.iv); | ||
} | ||
: function (input) { return input; }, | ||
decode: ((_d = route.response) === null || _d === void 0 ? void 0 : _d.encrypted) === true | ||
? function (input, headers) { | ||
var p = closure("decode")(headers, input); | ||
var str = AesPkcs5_1.AesPkcs5.decrypt(input, p.key, p.iv); | ||
return str.length ? JSON.parse(str) : str; | ||
} | ||
: function (input) { return input; }, | ||
})(connection, route, input, stringify)]; | ||
}); | ||
}); | ||
} | ||
EncryptedFetcher.propagate = propagate; | ||
})(EncryptedFetcher || (exports.EncryptedFetcher = EncryptedFetcher = {})); | ||
//# sourceMappingURL=EncryptedFetcher.js.map |
@@ -12,2 +12,3 @@ /** | ||
readonly status: number; | ||
readonly headers: Record<string, string | string[]>; | ||
/** | ||
@@ -21,3 +22,3 @@ * Initializer Constructor. | ||
*/ | ||
constructor(method: "GET" | "DELETE" | "POST" | "PUT" | "PATCH" | "HEAD", path: string, status: number, message: string); | ||
constructor(method: "GET" | "DELETE" | "POST" | "PUT" | "PATCH" | "HEAD", path: string, status: number, headers: Record<string, string | string[]>, message: string); | ||
/** | ||
@@ -46,4 +47,5 @@ * `HttpError` to JSON. | ||
status: number; | ||
headers: Record<string, string | string[]>; | ||
message: T; | ||
} | ||
} |
@@ -36,3 +36,3 @@ "use strict"; | ||
*/ | ||
function HttpError(method, path, status, message) { | ||
function HttpError(method, path, status, headers, message) { | ||
var _newTarget = this.constructor; | ||
@@ -43,2 +43,3 @@ var _this = _super.call(this, message) || this; | ||
_this.status = status; | ||
_this.headers = headers; | ||
/** | ||
@@ -81,2 +82,3 @@ * @internal | ||
status: this.status, | ||
headers: this.headers, | ||
message: this.body_, | ||
@@ -83,0 +85,0 @@ }; |
export * from "./IConnection"; | ||
export * from "./IEncryptionPassword"; | ||
export * from "./IPropagation"; | ||
export * from "./IRandomGenerator"; | ||
export * from "./HttpError"; | ||
export * from "./Primitive"; | ||
export * from "./HttpError"; | ||
export * from "./Resolved"; |
@@ -19,5 +19,7 @@ "use strict"; | ||
__exportStar(require("./IEncryptionPassword"), exports); | ||
__exportStar(require("./IPropagation"), exports); | ||
__exportStar(require("./IRandomGenerator"), exports); | ||
__exportStar(require("./HttpError"), exports); | ||
__exportStar(require("./Primitive"), exports); | ||
__exportStar(require("./HttpError"), exports); | ||
__exportStar(require("./Resolved"), exports); | ||
//# sourceMappingURL=index.js.map |
import { IConnection } from "../IConnection"; | ||
import { Primitive } from "../Primitive"; | ||
import { IFetchRoute } from "./IFetchRoute"; | ||
import { IPropagation } from "../IPropagation"; | ||
export declare namespace FetcherBase { | ||
@@ -11,2 +12,3 @@ interface IProps { | ||
const fetch: (props: IProps) => <Input, Output>(connection: IConnection, route: IFetchRoute<"DELETE" | "GET" | "HEAD" | "PATCH" | "POST" | "PUT">, input?: Input | undefined, stringify?: ((input: Input) => string) | undefined) => Promise<Primitive<Output>>; | ||
const propagate: (props: IProps) => <Input>(connection: IConnection, route: IFetchRoute<"DELETE" | "GET" | "HEAD" | "PATCH" | "POST" | "PUT">, input?: Input | undefined, stringify?: ((input: Input) => string) | undefined) => Promise<IPropagation<any, any, any>>; | ||
} |
@@ -98,101 +98,145 @@ "use strict"; | ||
return function (connection, route, input, stringify) { return __awaiter(_this, void 0, void 0, function () { | ||
var headers, init, path, url, response, _a, _b, text, _c, _d; | ||
var _e, _f, _g, _h, _j, _k, _l, _m; | ||
return __generator(this, function (_o) { | ||
switch (_o.label) { | ||
case 0: | ||
headers = __assign({}, ((_e = connection.headers) !== null && _e !== void 0 ? _e : {})); | ||
if (input !== undefined) { | ||
if (((_f = route.request) === null || _f === void 0 ? void 0 : _f.type) === undefined) | ||
throw new Error("Error on ".concat(props.className, ".fetch(): no content-type being configured.")); | ||
headers["Content-Type"] = route.request.type; | ||
} | ||
init = __assign(__assign({}, ((_g = connection.options) !== null && _g !== void 0 ? _g : {})), { method: route.method, headers: (function () { | ||
var e_1, _a, e_2, _b; | ||
var output = []; | ||
try { | ||
for (var _c = __values(Object.entries(headers)), _d = _c.next(); !_d.done; _d = _c.next()) { | ||
var _e = __read(_d.value, 2), key = _e[0], value = _e[1]; | ||
if (value === undefined) | ||
continue; | ||
else if (Array.isArray(value)) | ||
try { | ||
for (var value_1 = (e_2 = void 0, __values(value)), value_1_1 = value_1.next(); !value_1_1.done; value_1_1 = value_1.next()) { | ||
var v = value_1_1.value; | ||
output.push([key, String(v)]); | ||
} | ||
} | ||
catch (e_2_1) { e_2 = { error: e_2_1 }; } | ||
finally { | ||
var result; | ||
return __generator(this, function (_a) { | ||
switch (_a.label) { | ||
case 0: return [4 /*yield*/, _Propagate("fetch")(props)(connection, route, input, stringify)]; | ||
case 1: | ||
result = _a.sent(); | ||
if (result.success === false) | ||
throw new HttpError_1.HttpError(route.method, route.path, result.status, result.headers, result.data); | ||
return [2 /*return*/, result.data]; | ||
} | ||
}); | ||
}); }; | ||
}; | ||
FetcherBase.propagate = function (props) { | ||
return function (connection, route, input, stringify) { return __awaiter(_this, void 0, void 0, function () { return __generator(this, function (_a) { | ||
return [2 /*return*/, _Propagate("propagate")(props)(connection, route, input, stringify)]; | ||
}); }); }; | ||
}; | ||
/** | ||
* @internal | ||
*/ | ||
var _Propagate = function (method) { | ||
return function (props) { | ||
return function (connection, route, input, stringify) { return __awaiter(_this, void 0, void 0, function () { | ||
var headers, init, path, url, response, result, _a, type, text, _b, _c, _d; | ||
var _e, _f, _g, _h, _j; | ||
return __generator(this, function (_k) { | ||
switch (_k.label) { | ||
case 0: | ||
headers = __assign({}, ((_e = connection.headers) !== null && _e !== void 0 ? _e : {})); | ||
if (input !== undefined) { | ||
if (((_f = route.request) === null || _f === void 0 ? void 0 : _f.type) === undefined) | ||
throw new Error("Error on ".concat(props.className, ".fetch(): no content-type being configured.")); | ||
headers["Content-Type"] = route.request.type; | ||
} | ||
init = __assign(__assign({}, ((_g = connection.options) !== null && _g !== void 0 ? _g : {})), { method: route.method, headers: (function () { | ||
var e_1, _a, e_2, _b; | ||
var output = []; | ||
try { | ||
for (var _c = __values(Object.entries(headers)), _d = _c.next(); !_d.done; _d = _c.next()) { | ||
var _e = __read(_d.value, 2), key = _e[0], value = _e[1]; | ||
if (value === undefined) | ||
continue; | ||
else if (Array.isArray(value)) | ||
try { | ||
if (value_1_1 && !value_1_1.done && (_b = value_1.return)) _b.call(value_1); | ||
for (var value_1 = (e_2 = void 0, __values(value)), value_1_1 = value_1.next(); !value_1_1.done; value_1_1 = value_1.next()) { | ||
var v = value_1_1.value; | ||
output.push([key, String(v)]); | ||
} | ||
} | ||
finally { if (e_2) throw e_2.error; } | ||
} | ||
else | ||
output.push([key, String(value)]); | ||
catch (e_2_1) { e_2 = { error: e_2_1 }; } | ||
finally { | ||
try { | ||
if (value_1_1 && !value_1_1.done && (_b = value_1.return)) _b.call(value_1); | ||
} | ||
finally { if (e_2) throw e_2.error; } | ||
} | ||
else | ||
output.push([key, String(value)]); | ||
} | ||
} | ||
} | ||
catch (e_1_1) { e_1 = { error: e_1_1 }; } | ||
finally { | ||
try { | ||
if (_d && !_d.done && (_a = _c.return)) _a.call(_c); | ||
catch (e_1_1) { e_1 = { error: e_1_1 }; } | ||
finally { | ||
try { | ||
if (_d && !_d.done && (_a = _c.return)) _a.call(_c); | ||
} | ||
finally { if (e_1) throw e_1.error; } | ||
} | ||
finally { if (e_1) throw e_1.error; } | ||
return output; | ||
})() }); | ||
// CONSTRUCT BODY DATA | ||
if (input !== undefined) | ||
init.body = props.encode( | ||
// BODY TRANSFORM | ||
((_h = route.request) === null || _h === void 0 ? void 0 : _h.type) !== "text/plain" | ||
? (stringify !== null && stringify !== void 0 ? stringify : JSON.stringify)(input) | ||
: input, headers); | ||
path = connection.host[connection.host.length - 1] !== "/" && | ||
route.path[0] !== "/" | ||
? "/".concat(route.path) | ||
: route.path; | ||
url = new URL("".concat(connection.host).concat(path)); | ||
return [4 /*yield*/, polyfill.get()]; | ||
case 1: return [4 /*yield*/, (_k.sent())(url.href, init)]; | ||
case 2: | ||
response = _k.sent(); | ||
result = { | ||
success: response.status === 200 || | ||
response.status === 201 || | ||
response.status == route.status, | ||
status: route.status, | ||
headers: response_headers_to_object(response.headers), | ||
data: undefined, | ||
}; | ||
if (!(result.success === false)) return [3 /*break*/, 4]; | ||
// WHEN FAILED | ||
_a = result; | ||
return [4 /*yield*/, response.text()]; | ||
case 3: | ||
// WHEN FAILED | ||
_a.data = _k.sent(); | ||
type = response.headers.get("content-type"); | ||
if (method !== "fetch" && | ||
type && | ||
type.indexOf("application/json") !== -1) | ||
try { | ||
result.data = JSON.parse(result.data); | ||
} | ||
return output; | ||
})() }); | ||
// CONSTRUCT BODY DATA | ||
if (input !== undefined) | ||
init.body = props.encode( | ||
// BODY TRANSFORM | ||
((_h = route.request) === null || _h === void 0 ? void 0 : _h.type) !== "text/plain" | ||
? (stringify !== null && stringify !== void 0 ? stringify : JSON.stringify)(input) | ||
: input, headers); | ||
path = connection.host[connection.host.length - 1] !== "/" && | ||
route.path[0] !== "/" | ||
? "/".concat(route.path) | ||
: route.path; | ||
url = new URL("".concat(connection.host).concat(path)); | ||
return [4 /*yield*/, polyfill.get()]; | ||
case 1: return [4 /*yield*/, (_o.sent())(url.href, init)]; | ||
case 2: | ||
response = _o.sent(); | ||
if (!((route.status !== null && response.status !== route.status) || | ||
(route.status === null && | ||
response.status !== 200 && | ||
response.status !== 201))) return [3 /*break*/, 4]; | ||
_a = HttpError_1.HttpError.bind; | ||
_b = [void 0, route.method, | ||
path, | ||
response.status]; | ||
return [4 /*yield*/, response.text()]; | ||
case 3: throw new (_a.apply(HttpError_1.HttpError, _b.concat([_o.sent()])))(); | ||
case 4: | ||
if (((_j = route.response) === null || _j === void 0 ? void 0 : _j.type) && | ||
((_l = ((_k = response.headers.get("content-type")) !== null && _k !== void 0 ? _k : route.response.type)) === null || _l === void 0 ? void 0 : _l.indexOf(route.response.type)) === -1) | ||
throw new Error("Error on ".concat(props.className, ".fetch(): response type mismatched - expected ").concat(route.response.type, ", but ").concat(response.headers.get("content-type"), ".")); | ||
_o.label = 5; | ||
case 5: | ||
if (!(route.method === "HEAD")) return [3 /*break*/, 6]; | ||
return [2 /*return*/, undefined]; | ||
case 6: | ||
if (!(((_m = route.response) === null || _m === void 0 ? void 0 : _m.type) === "application/json")) return [3 /*break*/, 8]; | ||
return [4 /*yield*/, response.text()]; | ||
case 7: | ||
text = _o.sent(); | ||
return [2 /*return*/, text.length ? JSON.parse(text) : undefined]; | ||
case 8: | ||
_d = (_c = props).decode; | ||
return [4 /*yield*/, response.text()]; | ||
case 9: return [2 /*return*/, _d.apply(_c, [_o.sent(), response_headers_to_object(response.headers)])]; | ||
} | ||
}); | ||
}); }; | ||
catch (_l) { } | ||
return [3 /*break*/, 9]; | ||
case 4: | ||
if (!(route.method === "HEAD")) return [3 /*break*/, 5]; | ||
result.data = undefined; | ||
return [3 /*break*/, 9]; | ||
case 5: | ||
if (!(((_j = route.response) === null || _j === void 0 ? void 0 : _j.type) === "application/json")) return [3 /*break*/, 7]; | ||
return [4 /*yield*/, response.text()]; | ||
case 6: | ||
text = _k.sent(); | ||
result.data = text.length ? JSON.parse(text) : undefined; | ||
return [3 /*break*/, 9]; | ||
case 7: | ||
_b = result; | ||
_d = (_c = props).decode; | ||
return [4 /*yield*/, response.text()]; | ||
case 8: | ||
_b.data = _d.apply(_c, [_k.sent(), result.headers]); | ||
_k.label = 9; | ||
case 9: return [2 /*return*/, result]; | ||
} | ||
}); | ||
}); }; | ||
}; | ||
}; | ||
})(FetcherBase || (exports.FetcherBase = FetcherBase = {})); | ||
/** | ||
* @internal | ||
*/ | ||
var polyfill = new Singleton_1.Singleton(function () { return __awaiter(void 0, void 0, void 0, function () { | ||
var _a; | ||
return __generator(this, function (_b) { | ||
switch (_b.label) { | ||
var _a, _b; | ||
var _c; | ||
return __generator(this, function (_d) { | ||
switch (_d.label) { | ||
case 0: | ||
@@ -202,14 +246,24 @@ if (!(typeof global === "object" && | ||
typeof global.process.versions === "object" && | ||
typeof global.process.versions.node !== undefined)) return [3 /*break*/, 3]; | ||
if (!(global.fetch === undefined)) return [3 /*break*/, 2]; | ||
_a = global; | ||
typeof global.process.versions.node !== undefined)) return [3 /*break*/, 5]; | ||
if (!(global.fetch === undefined)) return [3 /*break*/, 4]; | ||
if (!((_c = global.fetch) !== null && _c !== void 0)) return [3 /*break*/, 1]; | ||
_a = _c; | ||
return [3 /*break*/, 3]; | ||
case 1: | ||
_b = global; | ||
return [4 /*yield*/, (0, import2_1.default)("node-fetch")]; | ||
case 1: | ||
_a.fetch = (_b.sent()).default; | ||
_b.label = 2; | ||
case 2: return [2 /*return*/, global.fetch]; | ||
case 3: return [2 /*return*/, window.fetch]; | ||
case 2: | ||
_a = (_b.fetch = (_d.sent()).default); | ||
_d.label = 3; | ||
case 3: | ||
_a; | ||
_d.label = 4; | ||
case 4: return [2 /*return*/, global.fetch]; | ||
case 5: return [2 /*return*/, window.fetch]; | ||
} | ||
}); | ||
}); }); | ||
/** | ||
* @internal | ||
*/ | ||
var response_headers_to_object = function (headers) { | ||
@@ -216,0 +270,0 @@ var output = {}; |
import { IConnection } from "./IConnection"; | ||
import { Primitive } from "./Primitive"; | ||
import { IFetchRoute } from "./internal/IFetchRoute"; | ||
import { IPropagation } from "./IPropagation"; | ||
/** | ||
@@ -44,2 +45,4 @@ * Utility class for `fetch` functions used in `@nestia/sdk`. | ||
function fetch<Input, Output>(connection: IConnection, route: IFetchRoute<"POST" | "PUT" | "PATCH" | "DELETE">, input?: Input, stringify?: (input: Input) => string): Promise<Primitive<Output>>; | ||
function propagate<Output extends IPropagation<number, any, boolean>>(connection: IConnection, route: IFetchRoute<"HEAD">): Promise<Output>; | ||
function propagate<Input, Output extends IPropagation<number, any, boolean>>(connection: IConnection, route: IFetchRoute<"HEAD">, input?: Input, stringify?: (input: Input) => string): Promise<Output>; | ||
} |
@@ -74,3 +74,19 @@ "use strict"; | ||
PlainFetcher.fetch = fetch; | ||
function propagate(connection, route, input, stringify) { | ||
var _a, _b; | ||
return __awaiter(this, void 0, void 0, function () { | ||
return __generator(this, function (_c) { | ||
if (((_a = route.request) === null || _a === void 0 ? void 0 : _a.encrypted) === true || | ||
((_b = route.response) === null || _b === void 0 ? void 0 : _b.encrypted) === true) | ||
throw new Error("Error on PlainFetcher.propagate(): PlainFetcher doesn't have encryption ability. Use EncryptedFetcher instead."); | ||
return [2 /*return*/, FetcherBase_1.FetcherBase.propagate({ | ||
className: "PlainFetcher", | ||
encode: function (input) { return input; }, | ||
decode: function (input) { return input; }, | ||
})(connection, route, input, stringify)]; | ||
}); | ||
}); | ||
} | ||
PlainFetcher.propagate = propagate; | ||
})(PlainFetcher || (exports.PlainFetcher = PlainFetcher = {})); | ||
//# sourceMappingURL=PlainFetcher.js.map |
/** | ||
* Primitive type. | ||
* Primitive type of JSON. | ||
* | ||
* `Primitive` is a type of TMP (Type Meta Programming) type who converts its argument as a | ||
* primitive type. | ||
* `Primitive<T>` is a TMP (Type Meta Programming) type which converts | ||
* its argument as a primitive type within framework JSON. | ||
* | ||
* If the target argument is a built-in class who returns its origin primitive type through | ||
* the `valueOf()` method like the `String` or `Number`, its return type would be the | ||
* `string` or `number`. | ||
* If the target argument is a built-in class which returns its origin primitive type | ||
* through the `valueOf()` method like the `String` or `Number`, its return type would | ||
* be the `string` or `number`. Otherwise, the built-in class does not have the | ||
* `valueOf()` method, the return type would be an empty object (`{}`). | ||
* | ||
* Otherwise, the target argument is a type of custom class, all of its custom method would | ||
* be erased and its prototype would be changed to the primitive `object`. Therefore, return | ||
* type of the TMP type finally be the primitive object. | ||
* Otherwise, the target argument is a type of custom class, all of its custom method | ||
* would be erased and its prototype would be changed to the primitive `object`. | ||
* Therefore, return type of the TMP type finally be the primitive object. | ||
* | ||
@@ -26,6 +27,7 @@ * In addition, if the target argument is a type of custom class and it has a special | ||
* `Class` with `toJSON()` | `Primitive<ReturnType<Class.toJSON>>` | ||
* Native Class | `{}` | ||
* Others | No change | ||
* | ||
* @template Instance Target argument type. | ||
* @author Jenogho Nam - https://github.com/samchon | ||
* @author Jeongho Nam - https://github.com/samchon | ||
* @author Kyungsu Kang - https://github.com/kakasoo | ||
@@ -36,5 +38,5 @@ * @author Michael - https://github.com/8471919 | ||
type Equal<X, Y> = X extends Y ? (Y extends X ? true : false) : false; | ||
type PrimitiveMain<Instance> = Instance extends [never] ? never : ValueOf<Instance> extends boolean | number | bigint | string ? ValueOf<Instance> : ValueOf<Instance> extends object ? Instance extends object ? Instance extends NativeClass ? {} : Instance extends IJsonable<infer Raw> ? ValueOf<Raw> extends object ? Raw extends object ? PrimitiveObject<Raw> : never : ValueOf<Raw> : PrimitiveObject<Instance> : never : ValueOf<Instance>; | ||
type PrimitiveMain<Instance> = Instance extends [never] ? never : ValueOf<Instance> extends bigint ? never : ValueOf<Instance> extends boolean | number | string ? ValueOf<Instance> : Instance extends Function ? never : ValueOf<Instance> extends object ? Instance extends object ? Instance extends NativeClass ? {} : Instance extends IJsonable<infer Raw> ? ValueOf<Raw> extends object ? Raw extends object ? PrimitiveObject<Raw> : never : ValueOf<Raw> : PrimitiveObject<Instance> : never : ValueOf<Instance>; | ||
type PrimitiveObject<Instance extends object> = Instance extends Array<infer T> ? IsTuple<Instance> extends true ? PrimitiveTuple<Instance> : PrimitiveMain<T>[] : { | ||
[P in keyof Instance]: Instance[P] extends Function ? never : PrimitiveMain<Instance[P]>; | ||
[P in keyof Instance]: PrimitiveMain<Instance[P]>; | ||
}; | ||
@@ -49,3 +51,3 @@ type PrimitiveTuple<T extends readonly any[]> = T extends [] ? [] : T extends [infer F] ? [PrimitiveMain<F>] : T extends [infer F, ...infer Rest extends readonly any[]] ? [PrimitiveMain<F>, ...PrimitiveTuple<Rest>] : T extends [(infer F)?] ? [PrimitiveMain<F>?] : T extends [(infer F)?, ...infer Rest extends readonly any[]] ? [PrimitiveMain<F>?, ...PrimitiveTuple<Rest>] : []; | ||
] ? false : T extends readonly any[] ? number extends T["length"] ? false : true : false; | ||
type IsValueOf<Instance, Object extends IValueOf<any>> = Instance extends Object ? Object extends IValueOf<infer Primitive> ? Instance extends Primitive ? false : true : false : false; | ||
type IsValueOf<Instance, Object extends IValueOf<any>> = Instance extends Object ? Object extends IValueOf<infer U> ? Instance extends U ? false : true : false : false; | ||
interface IValueOf<T> { | ||
@@ -52,0 +54,0 @@ valueOf(): T; |
{ | ||
"name": "@nestia/fetcher", | ||
"version": "2.0.0-dev.20230906", | ||
"version": "2.0.0-dev.20230907", | ||
"description": "Fetcher library of Nestia SDK", | ||
@@ -5,0 +5,0 @@ "main": "lib/index.js", |
import { AesPkcs5 } from "./AesPkcs5"; | ||
import { IConnection } from "./IConnection"; | ||
import { IEncryptionPassword } from "./IEncryptionPassword"; | ||
import { IPropagation } from "./IPropagation"; | ||
import { Primitive } from "./Primitive"; | ||
@@ -118,2 +119,77 @@ import { FetcherBase } from "./internal/FetcherBase"; | ||
} | ||
export function propagate< | ||
Output extends IPropagation<number, any, boolean>, | ||
>(connection: IConnection, route: IFetchRoute<"HEAD">): Promise<Output>; | ||
export function propagate< | ||
Input, | ||
Output extends IPropagation<number, any, boolean>, | ||
>( | ||
connection: IConnection, | ||
route: IFetchRoute<"HEAD">, | ||
input?: Input, | ||
stringify?: (input: Input) => string, | ||
): Promise<Output>; | ||
export async function propagate< | ||
Input, | ||
Output extends IPropagation<number, any, boolean>, | ||
>( | ||
connection: IConnection, | ||
route: IFetchRoute< | ||
"DELETE" | "GET" | "HEAD" | "PATCH" | "POST" | "PUT" | ||
>, | ||
input?: Input, | ||
stringify?: (input: Input) => string, | ||
): Promise<Output> { | ||
if ( | ||
(route.request?.encrypted === true || route.response?.encrypted) && | ||
connection.encryption === undefined | ||
) | ||
throw new Error( | ||
"Error on EncryptedFetcher.propagate(): the encryption password has not been configured.", | ||
); | ||
const closure = | ||
typeof connection.encryption === "function" | ||
? (direction: "encode" | "decode") => | ||
( | ||
headers: Record< | ||
string, | ||
IConnection.HeaderValue | undefined | ||
>, | ||
body: string, | ||
) => | ||
( | ||
connection.encryption as IEncryptionPassword.Closure | ||
)({ | ||
headers, | ||
body, | ||
direction, | ||
}) | ||
: () => () => connection.encryption as IEncryptionPassword; | ||
return FetcherBase.propagate({ | ||
className: "EncryptedFetcher", | ||
encode: | ||
route.request?.encrypted === true | ||
? (input, headers) => { | ||
const p = closure("encode")(headers, input); | ||
return AesPkcs5.encrypt( | ||
JSON.stringify(input), | ||
p.key, | ||
p.iv, | ||
); | ||
} | ||
: (input) => input, | ||
decode: | ||
route.response?.encrypted === true | ||
? (input, headers) => { | ||
const p = closure("decode")(headers, input); | ||
const str = AesPkcs5.decrypt(input, p.key, p.iv); | ||
return str.length ? JSON.parse(str) : str; | ||
} | ||
: (input) => input, | ||
})(connection, route, input, stringify) as Promise<Output>; | ||
} | ||
} |
@@ -32,2 +32,3 @@ /** | ||
public readonly status: number, | ||
public readonly headers: Record<string, string | string[]>, | ||
message: string, | ||
@@ -67,2 +68,3 @@ ) { | ||
status: this.status, | ||
headers: this.headers, | ||
message: this.body_, | ||
@@ -80,2 +82,3 @@ }; | ||
status: number; | ||
headers: Record<string, string | string[]>; | ||
message: T; | ||
@@ -82,0 +85,0 @@ } |
export * from "./IConnection"; | ||
export * from "./IEncryptionPassword"; | ||
export * from "./IPropagation"; | ||
export * from "./IRandomGenerator"; | ||
export * from "./HttpError"; | ||
export * from "./Primitive"; | ||
export * from "./HttpError"; | ||
export * from "./Resolved"; |
@@ -7,2 +7,3 @@ import import2 from "import2"; | ||
import { HttpError } from "../HttpError"; | ||
import { IPropagation } from "../IPropagation"; | ||
@@ -32,2 +33,45 @@ export namespace FetcherBase { | ||
): Promise<Primitive<Output>> => { | ||
const result = await _Propagate("fetch")(props)( | ||
connection, | ||
route, | ||
input, | ||
stringify, | ||
); | ||
if (result.success === false) | ||
throw new HttpError( | ||
route.method, | ||
route.path, | ||
result.status, | ||
result.headers, | ||
result.data as string, | ||
); | ||
return result.data as Primitive<Output>; | ||
}; | ||
export const propagate = | ||
(props: IProps) => | ||
async <Input>( | ||
connection: IConnection, | ||
route: IFetchRoute< | ||
"DELETE" | "GET" | "HEAD" | "PATCH" | "POST" | "PUT" | ||
>, | ||
input?: Input, | ||
stringify?: (input: Input) => string, | ||
): Promise<IPropagation<any, any, any>> => | ||
_Propagate("propagate")(props)(connection, route, input, stringify); | ||
/** | ||
* @internal | ||
*/ | ||
const _Propagate = | ||
(method: string) => | ||
(props: IProps) => | ||
async <Input>( | ||
connection: IConnection, | ||
route: IFetchRoute< | ||
"DELETE" | "GET" | "HEAD" | "PATCH" | "POST" | "PUT" | ||
>, | ||
input?: Input, | ||
stringify?: (input: Input) => string, | ||
): Promise<IPropagation<any, any, any>> => { | ||
//---- | ||
@@ -91,42 +135,43 @@ // REQUEST MESSSAGE | ||
// VALIDATE RESPONSE | ||
if ( | ||
(route.status !== null && response.status !== route.status) || | ||
(route.status === null && | ||
response.status !== 200 && | ||
response.status !== 201) | ||
) | ||
throw new HttpError( | ||
route.method, | ||
path, | ||
response.status, | ||
await response.text(), | ||
); | ||
else if ( | ||
route.response?.type && | ||
( | ||
response.headers.get("content-type") ?? route.response.type | ||
)?.indexOf(route.response.type) === -1 | ||
) | ||
throw new Error( | ||
`Error on ${ | ||
props.className | ||
}.fetch(): response type mismatched - expected ${ | ||
route.response.type | ||
}, but ${response.headers.get("content-type")}.`, | ||
); | ||
// RETURNS | ||
if (route.method === "HEAD") return undefined!; | ||
else if (route.response?.type === "application/json") { | ||
const text: string = await response.text(); | ||
return text.length ? JSON.parse(text) : undefined; | ||
// CONSTRUCT RESULT DATA | ||
const result: IPropagation<any, any, any> = { | ||
success: | ||
response.status === 200 || | ||
response.status === 201 || | ||
response.status == route.status, | ||
status: route.status, | ||
headers: response_headers_to_object(response.headers), | ||
data: undefined!, | ||
}; | ||
if (result.success === false) { | ||
// WHEN FAILED | ||
result.data = await response.text(); | ||
const type = response.headers.get("content-type"); | ||
if ( | ||
method !== "fetch" && | ||
type && | ||
type.indexOf("application/json") !== -1 | ||
) | ||
try { | ||
result.data = JSON.parse(result.data); | ||
} catch {} | ||
} else { | ||
// WHEN SUCCESS | ||
if (route.method === "HEAD") result.data = undefined!; | ||
else if (route.response?.type === "application/json") { | ||
const text: string = await response.text(); | ||
result.data = text.length ? JSON.parse(text) : undefined; | ||
} else | ||
result.data = props.decode( | ||
await response.text(), | ||
result.headers, | ||
); | ||
} | ||
return props.decode( | ||
await response.text(), | ||
response_headers_to_object(response.headers), | ||
) as Primitive<Output>; | ||
return result; | ||
}; | ||
} | ||
/** | ||
* @internal | ||
*/ | ||
const polyfill = new Singleton(async (): Promise<typeof fetch> => { | ||
@@ -140,3 +185,3 @@ if ( | ||
if (global.fetch === undefined) | ||
global.fetch = ((await import2("node-fetch")) as any).default; | ||
global.fetch ??= ((await import2("node-fetch")) as any).default; | ||
return (global as any).fetch; | ||
@@ -147,6 +192,9 @@ } | ||
/** | ||
* @internal | ||
*/ | ||
const response_headers_to_object = ( | ||
headers: Headers, | ||
): Record<string, IConnection.HeaderValue | undefined> => { | ||
const output: Record<string, IConnection.HeaderValue> = {}; | ||
): Record<string, string | string[]> => { | ||
const output: Record<string, string | string[]> = {}; | ||
headers.forEach((value, key) => { | ||
@@ -153,0 +201,0 @@ if (key === "set-cookie") { |
@@ -6,2 +6,3 @@ import { IConnection } from "./IConnection"; | ||
import { FetcherBase } from "./internal/FetcherBase"; | ||
import { IPropagation } from "./IPropagation"; | ||
@@ -83,2 +84,41 @@ /** | ||
} | ||
export function propagate< | ||
Output extends IPropagation<number, any, boolean>, | ||
>(connection: IConnection, route: IFetchRoute<"HEAD">): Promise<Output>; | ||
export function propagate< | ||
Input, | ||
Output extends IPropagation<number, any, boolean>, | ||
>( | ||
connection: IConnection, | ||
route: IFetchRoute<"HEAD">, | ||
input?: Input, | ||
stringify?: (input: Input) => string, | ||
): Promise<Output>; | ||
export async function propagate< | ||
Input, | ||
Output extends IPropagation<number, any, boolean>, | ||
>( | ||
connection: IConnection, | ||
route: IFetchRoute< | ||
"DELETE" | "GET" | "HEAD" | "PATCH" | "POST" | "PUT" | ||
>, | ||
input?: Input, | ||
stringify?: (input: Input) => string, | ||
): Promise<Output> { | ||
if ( | ||
route.request?.encrypted === true || | ||
route.response?.encrypted === true | ||
) | ||
throw new Error( | ||
"Error on PlainFetcher.propagate(): PlainFetcher doesn't have encryption ability. Use EncryptedFetcher instead.", | ||
); | ||
return FetcherBase.propagate({ | ||
className: "PlainFetcher", | ||
encode: (input) => input, | ||
decode: (input) => input, | ||
})(connection, route, input, stringify) as Promise<Output>; | ||
} | ||
} |
/** | ||
* Primitive type. | ||
* Primitive type of JSON. | ||
* | ||
* `Primitive` is a type of TMP (Type Meta Programming) type who converts its argument as a | ||
* primitive type. | ||
* `Primitive<T>` is a TMP (Type Meta Programming) type which converts | ||
* its argument as a primitive type within framework JSON. | ||
* | ||
* If the target argument is a built-in class who returns its origin primitive type through | ||
* the `valueOf()` method like the `String` or `Number`, its return type would be the | ||
* `string` or `number`. | ||
* If the target argument is a built-in class which returns its origin primitive type | ||
* through the `valueOf()` method like the `String` or `Number`, its return type would | ||
* be the `string` or `number`. Otherwise, the built-in class does not have the | ||
* `valueOf()` method, the return type would be an empty object (`{}`). | ||
* | ||
* Otherwise, the target argument is a type of custom class, all of its custom method would | ||
* be erased and its prototype would be changed to the primitive `object`. Therefore, return | ||
* type of the TMP type finally be the primitive object. | ||
* Otherwise, the target argument is a type of custom class, all of its custom method | ||
* would be erased and its prototype would be changed to the primitive `object`. | ||
* Therefore, return type of the TMP type finally be the primitive object. | ||
* | ||
@@ -26,6 +27,7 @@ * In addition, if the target argument is a type of custom class and it has a special | ||
* `Class` with `toJSON()` | `Primitive<ReturnType<Class.toJSON>>` | ||
* Native Class | `{}` | ||
* Others | No change | ||
* | ||
* @template Instance Target argument type. | ||
* @author Jenogho Nam - https://github.com/samchon | ||
* @author Jeongho Nam - https://github.com/samchon | ||
* @author Kyungsu Kang - https://github.com/kakasoo | ||
@@ -42,4 +44,8 @@ * @author Michael - https://github.com/8471919 | ||
? never // (special trick for jsonable | null) type | ||
: ValueOf<Instance> extends boolean | number | bigint | string | ||
: ValueOf<Instance> extends bigint | ||
? never | ||
: ValueOf<Instance> extends boolean | number | string | ||
? ValueOf<Instance> | ||
: Instance extends Function | ||
? never | ||
: ValueOf<Instance> extends object | ||
@@ -64,5 +70,3 @@ ? Instance extends object | ||
: { | ||
[P in keyof Instance]: Instance[P] extends Function | ||
? never | ||
: PrimitiveMain<Instance[P]>; | ||
[P in keyof Instance]: PrimitiveMain<Instance[P]>; | ||
}; | ||
@@ -121,4 +125,4 @@ | ||
type IsValueOf<Instance, Object extends IValueOf<any>> = Instance extends Object | ||
? Object extends IValueOf<infer Primitive> | ||
? Instance extends Primitive | ||
? Object extends IValueOf<infer U> | ||
? Instance extends U | ||
? false | ||
@@ -125,0 +129,0 @@ : true // not Primitive, but Object |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
123892
58
2514