From 2ad41355a9e235b1446472c15d0b81d840106ff3 Mon Sep 17 00:00:00 2001 From: zhaoyingbo Date: Sun, 18 Aug 2024 15:35:42 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=AE=8C=E6=88=90=E7=AC=AC=E4=B8=80?= =?UTF-8?q?=E7=89=88=EF=BC=8C=E5=8A=A0=E5=85=A5netTool=E4=BB=A5=E5=8F=8Alo?= =?UTF-8?q?gger?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .devcontainer/devcontainer.json | 0 .gitignore | 58 +++++ .husky/commit-msg | 1 + .husky/pre-commit | 1 + .npmignore | 5 + .vscode/settings.json | 3 + bun.lockb | Bin 0 -> 108789 bytes commitlint.config.js | 1 + dist/index.d.ts | 3 + dist/index.js | 3 + dist/logger.d.ts | 5 + dist/logger.js | 42 ++++ dist/netTool.d.ts | 152 ++++++++++++ dist/netTool.js | 303 +++++++++++++++++++++++ eslint.config.js | 22 ++ package.json | 47 ++++ prettier.config.js | 6 + src/index.ts | 4 + src/logger.ts | 55 +++++ src/netTool.ts | 422 ++++++++++++++++++++++++++++++++ tsconfig.json | 24 ++ tsconfig.tsbuildinfo | 1 + 22 files changed, 1158 insertions(+) create mode 100644 .devcontainer/devcontainer.json create mode 100644 .gitignore create mode 100644 .husky/commit-msg create mode 100644 .husky/pre-commit create mode 100644 .npmignore create mode 100644 .vscode/settings.json create mode 100644 bun.lockb create mode 100644 commitlint.config.js create mode 100644 dist/index.d.ts create mode 100644 dist/index.js create mode 100644 dist/logger.d.ts create mode 100644 dist/logger.js create mode 100644 dist/netTool.d.ts create mode 100644 dist/netTool.js create mode 100644 eslint.config.js create mode 100644 package.json create mode 100644 prettier.config.js create mode 100644 src/index.ts create mode 100644 src/logger.ts create mode 100644 src/netTool.ts create mode 100644 tsconfig.json create mode 100644 tsconfig.tsbuildinfo diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 0000000..e69de29 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1e73511 --- /dev/null +++ b/.gitignore @@ -0,0 +1,58 @@ +# Logs +logs +*.log +npm-debug.log* + +# Runtime data +pids +*.pid +*.seed + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage + +# nyc test coverage +.nyc_output + +# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (http://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +node_modules +jspm_packages + +# Optional npm cache directory +.npm + +# Optional REPL history +.node_repl_history + +# 0x +profile-* + +# mac files +.DS_Store + +# vim swap files +*.swp + +# webstorm +.idea + +# vscode +# .vscode +*code-workspace + +# clinic +profile* +*clinic* +*flamegraph* diff --git a/.husky/commit-msg b/.husky/commit-msg new file mode 100644 index 0000000..0a4b97d --- /dev/null +++ b/.husky/commit-msg @@ -0,0 +1 @@ +npx --no -- commitlint --edit $1 diff --git a/.husky/pre-commit b/.husky/pre-commit new file mode 100644 index 0000000..af5adff --- /dev/null +++ b/.husky/pre-commit @@ -0,0 +1 @@ +lint-staged \ No newline at end of file diff --git a/.npmignore b/.npmignore new file mode 100644 index 0000000..61a0884 --- /dev/null +++ b/.npmignore @@ -0,0 +1,5 @@ +src +node_modules +.git +.vscode +.devcontainer \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..a9a33e7 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "cSpell.words": ["commitlint", "tseslint"] +} diff --git a/bun.lockb b/bun.lockb new file mode 100644 index 0000000000000000000000000000000000000000..54645f973542f32c885cbaddb59b7380b95945bb GIT binary patch literal 108789 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 literal 0 HcmV?d00001 diff --git a/commitlint.config.js b/commitlint.config.js new file mode 100644 index 0000000..fba73fb --- /dev/null +++ b/commitlint.config.js @@ -0,0 +1 @@ +export default { extends: ["@commitlint/config-conventional"] } diff --git a/dist/index.d.ts b/dist/index.d.ts new file mode 100644 index 0000000..adca41a --- /dev/null +++ b/dist/index.d.ts @@ -0,0 +1,3 @@ +import loggerIns from "./logger"; +import { NetTool, NetToolBase } from "./netTool"; +export { loggerIns, NetTool, NetToolBase }; diff --git a/dist/index.js b/dist/index.js new file mode 100644 index 0000000..adca41a --- /dev/null +++ b/dist/index.js @@ -0,0 +1,3 @@ +import loggerIns from "./logger"; +import { NetTool, NetToolBase } from "./netTool"; +export { loggerIns, NetTool, NetToolBase }; diff --git a/dist/logger.d.ts b/dist/logger.d.ts new file mode 100644 index 0000000..4558935 --- /dev/null +++ b/dist/logger.d.ts @@ -0,0 +1,5 @@ +import "winston-daily-rotate-file"; + +import winston from "winston"; +declare const loggerIns: winston.Logger; +export default loggerIns; diff --git a/dist/logger.js b/dist/logger.js new file mode 100644 index 0000000..1100e83 --- /dev/null +++ b/dist/logger.js @@ -0,0 +1,42 @@ +import "winston-daily-rotate-file"; + +import winston, { format } from "winston"; +const isProd = process.env.NODE_ENV === "production"; +const transports = [ + new winston.transports.Console({ + level: "info", + }), +]; +if (isProd) { + const config = { + datePattern: "YYYY-MM-DD", + zippedArchive: true, + maxSize: "20m", + maxFiles: "14d", + }; + transports.push(new winston.transports.DailyRotateFile({ + level: "info", + filename: "/home/work/log/egg-info-%DATE%.log", + ...config, + })); + transports.push(new winston.transports.DailyRotateFile({ + level: "debug", + filename: "/home/work/log/egg-debug-%DATE%.log", + ...config, + })); +} +const loggerIns = winston.createLogger({ + level: "silly", + format: format.combine(format.colorize({ + level: !isProd, + }), // 开发环境下输出彩色日志 + format.simple(), // 简单文本格式化 + format.timestamp({ format: "YYYY-MM-DD HH:mm:ss" }), format.printf(({ level, message, timestamp, requestId }) => { + const singleLineMessage = isProd + ? message.replace(/\n/g, " ") // 将换行符替换为空格 + : message; + return `${timestamp} [${level}]${requestId ? ` [RequestId: ${requestId}]` : ""}: ${singleLineMessage}`; + })), + transports, +}); +export default loggerIns; diff --git a/dist/netTool.d.ts b/dist/netTool.d.ts new file mode 100644 index 0000000..dd5a518 --- /dev/null +++ b/dist/netTool.d.ts @@ -0,0 +1,152 @@ +import { Logger } from "winston"; +interface NetRequestParams { + url: string; + method: string; + queryParams?: any; + payload?: any; + additionalHeaders?: any; +} +interface NetErrorDetail { + httpStatus: number; + code: number; + message: string; +} +export declare class NetError extends Error { + code: number; + message: string; + httpStatus: number; + constructor({ code, message, httpStatus }: NetErrorDetail); +} +/** + * 网络工具类,提供发送HTTP请求的方法。 + */ +declare class NetToolBase { + protected prefix: string; + protected headers: any; + protected getHeaders: () => any; + protected logger: Logger; + protected requestId: string; + /** + * 创建一个网络工具类实例。 + * + * @param {Object} params - 构造函数参数。 + * @param {string} [params.prefix] - URL前缀。 + * @param {any} [params.headers] - 默认请求头。 + * @param {Function} [params.getHeaders] - 获取请求头的方法。 + * @param {string} [params.requestId] - 请求ID。 + */ + constructor({ prefix, headers, getHeaders, requestId, }?: { + prefix?: string; + headers?: any; + getHeaders?: () => any; + requestId?: string; + }); + /** + * 记录响应详情并返回响应日志对象。 + * @param response - 响应对象。 + * @param method - 请求使用的HTTP方法。 + * @param headers - 请求头。 + * @param requestBody - 请求体。 + * @param responseBody - 响应体。 + * @returns 响应日志对象。 + */ + private logResponse; + /** + * 发送网络请求并返回一个解析为响应数据的Promise。 + * @param url - 要发送请求的URL。 + * @param method - 请求使用的HTTP方法。 + * @param queryParams - 要包含在URL中的查询参数。 + * @param payload - 请求的有效负载数据。 + * @param additionalHeaders - 要包含在请求中的附加头。 + * @returns 一个解析为响应数据的Promise。 + * @throws 如果网络响应不成功或存在解析错误,则抛出错误。 + */ + protected request({ url, method, queryParams, payload, additionalHeaders, }: NetRequestParams): Promise; + /** + * 发送GET请求并返回一个解析为响应数据的Promise。 + * + * @param url - 要发送请求的URL。 + * @param queryParams - 要包含在URL中的查询参数。 + * @param additionalHeaders - 要包含在请求中的附加头。 + * @returns 一个解析为响应数据的Promise。 + */ + protected get(url: string, queryParams?: any, additionalHeaders?: any): Promise; + /** + * 发送POST请求并返回一个解析为响应数据的Promise。 + * + * @param url - 要发送请求的URL。 + * @param payload - 请求的有效负载数据。 + * @param queryParams - 要包含在URL中的查询参数。 + * @param additionalHeaders - 要包含在请求中的附加头。 + * @returns 一个解析为响应数据的Promise。 + */ + protected post(url: string, payload?: any, queryParams?: any, additionalHeaders?: any): Promise; + /** + * 发送PUT请求并返回一个解析为响应数据的Promise。 + * + * @param url - 要发送请求的URL。 + * @param payload - 请求的有效负载数据。 + * @param queryParams - 要包含在URL中的查询参数。 + * @param additionalHeaders - 要包含在请求中的附加头。 + * @returns 一个解析为响应数据的Promise。 + */ + protected put(url: string, payload: any, queryParams?: any, additionalHeaders?: any): Promise; + /** + * 发送DELETE请求并返回一个解析为响应数据的Promise。 + * + * @param url - 要发送请求的URL。 + * @param payload - 请求的有效负载数据。 + * @param queryParams - 要包含在URL中的查询参数。 + * @param additionalHeaders - 要包含在请求中的附加头。 + * @returns 一个解析为响应数据的Promise。 + */ + protected del(url: string, payload: any, queryParams?: any, additionalHeaders?: any): Promise; + /** + * 发送PATCH请求并返回一个解析为响应数据的Promise。 + * + * @param url - 要发送请求的URL。 + * @param payload - 请求的有效负载数据。 + * @param queryParams - 要包含在URL中的查询参数。 + * @param additionalHeaders - 要包含在请求中的附加头。 + * @returns 一个解析为响应数据的Promise。 + */ + protected patch(url: string, payload: any, queryParams?: any, additionalHeaders?: any): Promise; +} +declare class NetTool extends NetToolBase { + request({ url, method, queryParams, payload, additionalHeaders, }: NetRequestParams): Promise; + get(url: string, queryParams?: any, additionalHeaders?: any): Promise; + post(url: string, payload?: any, queryParams?: any, additionalHeaders?: any): Promise; + put(url: string, payload: any, queryParams?: any, additionalHeaders?: any): Promise; + del(url: string, payload: any, queryParams?: any, additionalHeaders?: any): Promise; + patch(url: string, payload: any, queryParams?: any, additionalHeaders?: any): Promise; + /** + * 创建一个表示400 Bad Request的响应对象。 + * + * @param message - 错误消息。 + * @returns 一个表示400 Bad Request的响应对象。 + */ + badRequest(message: string): import("undici-types").Response; + /** + * 创建一个表示404 Not Found的响应对象。 + * + * @param message - 错误消息。 + * @returns 一个表示404 Not Found的响应对象。 + */ + notFound(message: string): import("undici-types").Response; + /** + * 创建一个表示500 Internal Server Error的响应对象。 + * + * @param message - 错误消息。 + * @param data - 错误数据。 + * @returns 一个表示500 Internal Server Error的响应对象。 + */ + serverError(message: string, data?: any): import("undici-types").Response; + /** + * 创建一个表示200 OK的响应对象。 + * + * @param data - 响应数据。 + * @returns 一个表示200 OK的响应对象。 + */ + ok(data?: any): import("undici-types").Response; +} +export { NetTool, NetToolBase }; diff --git a/dist/netTool.js b/dist/netTool.js new file mode 100644 index 0000000..63ec3ac --- /dev/null +++ b/dist/netTool.js @@ -0,0 +1,303 @@ +import loggerIns from "./logger"; +export class NetError extends Error { + code; + message; + httpStatus; + constructor({ code, message, httpStatus }) { + super(message); + this.code = code; + this.message = message; + this.httpStatus = httpStatus; + } +} +/** + * 网络工具类,提供发送HTTP请求的方法。 + */ +class NetToolBase { + prefix; + headers; + getHeaders; + logger; + requestId; + /** + * 创建一个网络工具类实例。 + * + * @param {Object} params - 构造函数参数。 + * @param {string} [params.prefix] - URL前缀。 + * @param {any} [params.headers] - 默认请求头。 + * @param {Function} [params.getHeaders] - 获取请求头的方法。 + * @param {string} [params.requestId] - 请求ID。 + */ + constructor({ prefix, headers, getHeaders, requestId, } = {}) { + this.prefix = prefix || ""; + this.headers = headers || {}; + this.getHeaders = getHeaders || (() => ({})); + this.requestId = requestId || ""; + this.logger = loggerIns.child({ requestId }); + } + /** + * 记录响应详情并返回响应日志对象。 + * @param response - 响应对象。 + * @param method - 请求使用的HTTP方法。 + * @param headers - 请求头。 + * @param requestBody - 请求体。 + * @param responseBody - 响应体。 + * @returns 响应日志对象。 + */ + logResponse(response, method, headers, requestBody, responseBody) { + const responseLog = { + ok: response.ok, + status: response.status, + statusText: response.statusText, + url: response.url, + method: method, + requestHeaders: headers, + responseHeaders: response.headers, + requestBody, + responseBody, + }; + this.logger.http(JSON.stringify(responseLog, null, 2)); + return responseLog; + } + /** + * 发送网络请求并返回一个解析为响应数据的Promise。 + * @param url - 要发送请求的URL。 + * @param method - 请求使用的HTTP方法。 + * @param queryParams - 要包含在URL中的查询参数。 + * @param payload - 请求的有效负载数据。 + * @param additionalHeaders - 要包含在请求中的附加头。 + * @returns 一个解析为响应数据的Promise。 + * @throws 如果网络响应不成功或存在解析错误,则抛出错误。 + */ + async request({ url, method, queryParams, payload, additionalHeaders, }) { + // 拼接完整的URL + let fullUrl = `${this.prefix}${url}`; + if (queryParams) { + if (typeof queryParams === "string") { + fullUrl = `${fullUrl}?${queryParams}`; + } + else { + const queryString = new URLSearchParams(queryParams).toString(); + if (queryString) + fullUrl = `${fullUrl}?${queryString}`; + } + } + // 设置请求头 + const headers = { + ...this.headers, + ...(await this.getHeaders()), + ...additionalHeaders, + }; + // 设置请求Header + if (!(payload instanceof FormData)) { + headers["Content-Type"] = "application/json"; + } + // 处理请求数据 + const body = payload instanceof FormData ? payload : JSON.stringify(payload); + // 发送请求 + const res = await fetch(fullUrl, { + method, + body, + headers, + }); + // 获取响应数据 + let resData = null; + let resText = ""; + try { + resText = await res.text(); + resData = JSON.parse(resText); + } + catch { + /* empty */ + } + // 记录响应 + this.logResponse(res, method, headers, payload, resData || resText); + if (!res.ok) { + if (resData?.message || resData?.msg) { + throw new NetError({ + httpStatus: res.status, + code: resData?.code, + message: resData?.message || resData?.msg, + }); + } + throw new NetError({ + httpStatus: res.status, + code: res.status, + message: resText || "网络响应异常", + }); + } + // http 错误码正常,但解析异常 + if (!resData) { + throw new NetError({ + httpStatus: res.status, + code: 1, + message: "解析响应数据异常", + }); + } + // 响应数据异常 + if ("code" in resData && resData.code !== 0) { + throw new NetError({ + httpStatus: res.status, + code: resData.code, + message: resData.message || resData.msg || "网络请求失败", + }); + } + return resData; + } + /** + * 发送GET请求并返回一个解析为响应数据的Promise。 + * + * @param url - 要发送请求的URL。 + * @param queryParams - 要包含在URL中的查询参数。 + * @param additionalHeaders - 要包含在请求中的附加头。 + * @returns 一个解析为响应数据的Promise。 + */ + get(url, queryParams, additionalHeaders) { + return this.request({ url, method: "get", queryParams, additionalHeaders }); + } + /** + * 发送POST请求并返回一个解析为响应数据的Promise。 + * + * @param url - 要发送请求的URL。 + * @param payload - 请求的有效负载数据。 + * @param queryParams - 要包含在URL中的查询参数。 + * @param additionalHeaders - 要包含在请求中的附加头。 + * @returns 一个解析为响应数据的Promise。 + */ + post(url, payload, queryParams, additionalHeaders) { + return this.request({ + url, + method: "post", + payload, + queryParams, + additionalHeaders, + }); + } + /** + * 发送PUT请求并返回一个解析为响应数据的Promise。 + * + * @param url - 要发送请求的URL。 + * @param payload - 请求的有效负载数据。 + * @param queryParams - 要包含在URL中的查询参数。 + * @param additionalHeaders - 要包含在请求中的附加头。 + * @returns 一个解析为响应数据的Promise。 + */ + put(url, payload, queryParams, additionalHeaders) { + return this.request({ + url, + method: "put", + payload, + queryParams, + additionalHeaders, + }); + } + /** + * 发送DELETE请求并返回一个解析为响应数据的Promise。 + * + * @param url - 要发送请求的URL。 + * @param payload - 请求的有效负载数据。 + * @param queryParams - 要包含在URL中的查询参数。 + * @param additionalHeaders - 要包含在请求中的附加头。 + * @returns 一个解析为响应数据的Promise。 + */ + del(url, payload, queryParams, additionalHeaders) { + return this.request({ + url, + method: "delete", + payload, + queryParams, + additionalHeaders, + }); + } + /** + * 发送PATCH请求并返回一个解析为响应数据的Promise。 + * + * @param url - 要发送请求的URL。 + * @param payload - 请求的有效负载数据。 + * @param queryParams - 要包含在URL中的查询参数。 + * @param additionalHeaders - 要包含在请求中的附加头。 + * @returns 一个解析为响应数据的Promise。 + */ + patch(url, payload, queryParams, additionalHeaders) { + return this.request({ + url, + method: "patch", + payload, + queryParams, + additionalHeaders, + }); + } +} +class NetTool extends NetToolBase { + request({ url, method, queryParams, payload, additionalHeaders, }) { + return super.request({ + url, + method, + queryParams, + payload, + additionalHeaders, + }); + } + get(url, queryParams, additionalHeaders) { + return super.get(url, queryParams, additionalHeaders); + } + post(url, payload, queryParams, additionalHeaders) { + return super.post(url, payload, queryParams, additionalHeaders); + } + put(url, payload, queryParams, additionalHeaders) { + return super.put(url, payload, queryParams, additionalHeaders); + } + del(url, payload, queryParams, additionalHeaders) { + return super.del(url, payload, queryParams, additionalHeaders); + } + patch(url, payload, queryParams, additionalHeaders) { + return super.patch(url, payload, queryParams, additionalHeaders); + } + /** + * 创建一个表示400 Bad Request的响应对象。 + * + * @param message - 错误消息。 + * @returns 一个表示400 Bad Request的响应对象。 + */ + badRequest(message) { + this.logger.error(`return a bad request response: ${message}`); + return Response.json({ code: 400, message, requestId: this.requestId }, { status: 400 }); + } + /** + * 创建一个表示404 Not Found的响应对象。 + * + * @param message - 错误消息。 + * @returns 一个表示404 Not Found的响应对象。 + */ + notFound(message) { + this.logger.error(`return a not found response: ${message}`); + return Response.json({ code: 404, message, requestId: this.requestId }, { status: 404 }); + } + /** + * 创建一个表示500 Internal Server Error的响应对象。 + * + * @param message - 错误消息。 + * @param data - 错误数据。 + * @returns 一个表示500 Internal Server Error的响应对象。 + */ + serverError(message, data) { + this.logger.error(`return a server error response: ${message}`); + return Response.json({ code: 500, message, data, requestId: this.requestId }, { status: 500 }); + } + /** + * 创建一个表示200 OK的响应对象。 + * + * @param data - 响应数据。 + * @returns 一个表示200 OK的响应对象。 + */ + ok(data) { + this.logger.info(`return a ok response: ${JSON.stringify(data)}`); + return Response.json({ + code: 0, + message: "success", + data, + requestId: this.requestId, + }); + } +} +export { NetTool, NetToolBase }; diff --git a/eslint.config.js b/eslint.config.js new file mode 100644 index 0000000..d871960 --- /dev/null +++ b/eslint.config.js @@ -0,0 +1,22 @@ +import pluginJs from "@eslint/js" +import simpleImportSort from "eslint-plugin-simple-import-sort" +import globals from "globals" +import tseslint from "typescript-eslint" + +export default [ + { files: ["**/*.{js,mjs,cjs,ts}"] }, + { languageOptions: { globals: globals.browser } }, + pluginJs.configs.recommended, + ...tseslint.configs.recommended, + { + plugins: { + "simple-import-sort": simpleImportSort, + }, + rules: { + "@typescript-eslint/no-explicit-any": "off", + "@typescript-eslint/no-namespace": "off", + "simple-import-sort/imports": "error", + "simple-import-sort/exports": "error", + }, + }, +] diff --git a/package.json b/package.json new file mode 100644 index 0000000..bb7a125 --- /dev/null +++ b/package.json @@ -0,0 +1,47 @@ +{ + "name": "egg-tools", + "version": "1.0.0", + "description": "Tools for Egg projects, including netTool and logger", + "type": "module", + "main": "dist/index.js", + "types": "dist/index.d.ts", + "scripts": { + "build": "tsc", + "lint": "eslint --fix ./src", + "prepare": "husky", + "prettier": "prettier --write ./src" + }, + "keywords": [ + "egg", + "tools", + "logger", + "netTool" + ], + "author": "RainSun ", + "license": "ISC", + "lint-staged": { + "src/*.{js,jsx,ts,tsx}": [ + "eslint --fix", + "prettier --write", + "git add" + ] + }, + "devDependencies": { + "@commitlint/cli": "^19.4.0", + "@commitlint/config-conventional": "^19.2.2", + "@eslint/js": "^9.9.0", + "@types/node": "^22.4.0", + "eslint": "^9.9.0", + "eslint-plugin-simple-import-sort": "^12.1.1", + "globals": "^15.9.0", + "husky": "^9.1.4", + "lint-staged": "^15.2.9", + "prettier": "^3.3.3", + "typescript": "^5.5.4", + "typescript-eslint": "^8.1.0" + }, + "dependencies": { + "winston": "^3.14.2", + "winston-daily-rotate-file": "^5.0.0" + } +} diff --git a/prettier.config.js b/prettier.config.js new file mode 100644 index 0000000..db67959 --- /dev/null +++ b/prettier.config.js @@ -0,0 +1,6 @@ +export default { + trailingComma: "es5", + tabWidth: 2, + semi: false, + singleQuote: false, +} diff --git a/src/index.ts b/src/index.ts new file mode 100644 index 0000000..df2a5bd --- /dev/null +++ b/src/index.ts @@ -0,0 +1,4 @@ +import loggerIns from "./logger" +import { NetTool, NetToolBase } from "./netTool" + +export { loggerIns, NetTool, NetToolBase } diff --git a/src/logger.ts b/src/logger.ts new file mode 100644 index 0000000..dc532d4 --- /dev/null +++ b/src/logger.ts @@ -0,0 +1,55 @@ +import "winston-daily-rotate-file" + +import winston, { format } from "winston" + +const isProd = process.env.NODE_ENV === "production" + +const transports: any[] = [ + new winston.transports.Console({ + level: "info", + }), +] + +if (isProd) { + const config = { + datePattern: "YYYY-MM-DD", + zippedArchive: true, + maxSize: "20m", + maxFiles: "14d", + } + + transports.push( + new winston.transports.DailyRotateFile({ + level: "info", + filename: "/home/work/log/egg-info-%DATE%.log", + ...config, + }) + ) + transports.push( + new winston.transports.DailyRotateFile({ + level: "debug", + filename: "/home/work/log/egg-debug-%DATE%.log", + ...config, + }) + ) +} + +const loggerIns = winston.createLogger({ + level: "silly", + format: format.combine( + format.colorize({ + level: !isProd, + }), // 开发环境下输出彩色日志 + format.simple(), // 简单文本格式化 + format.timestamp({ format: "YYYY-MM-DD HH:mm:ss" }), + format.printf(({ level, message, timestamp, requestId }) => { + const singleLineMessage = isProd + ? message.replace(/\n/g, " ") // 将换行符替换为空格 + : message + return `${timestamp} [${level}]${requestId ? ` [RequestId: ${requestId}]` : ""}: ${singleLineMessage}` + }) + ), + transports, +}) + +export default loggerIns diff --git a/src/netTool.ts b/src/netTool.ts new file mode 100644 index 0000000..60c5815 --- /dev/null +++ b/src/netTool.ts @@ -0,0 +1,422 @@ +import { Logger } from "winston" + +import loggerIns from "./logger" + +interface NetRequestParams { + url: string + method: string + queryParams?: any + payload?: any + additionalHeaders?: any +} + +interface NetErrorDetail { + httpStatus: number + code: number + message: string +} + +export class NetError extends Error { + public code: number + public message: string + public httpStatus: number + + constructor({ code, message, httpStatus }: NetErrorDetail) { + super(message) + this.code = code + this.message = message + this.httpStatus = httpStatus + } +} + +/** + * 网络工具类,提供发送HTTP请求的方法。 + */ +class NetToolBase { + protected prefix: string + protected headers: any + protected getHeaders: () => any + protected logger: Logger + protected requestId: string + + /** + * 创建一个网络工具类实例。 + * + * @param {Object} params - 构造函数参数。 + * @param {string} [params.prefix] - URL前缀。 + * @param {any} [params.headers] - 默认请求头。 + * @param {Function} [params.getHeaders] - 获取请求头的方法。 + * @param {string} [params.requestId] - 请求ID。 + */ + constructor({ + prefix, + headers, + getHeaders, + requestId, + }: { + prefix?: string + headers?: any + getHeaders?: () => any + requestId?: string + } = {}) { + this.prefix = prefix || "" + this.headers = headers || {} + this.getHeaders = getHeaders || (() => ({})) + this.requestId = requestId || "" + this.logger = loggerIns.child({ requestId }) + } + + /** + * 记录响应详情并返回响应日志对象。 + * @param response - 响应对象。 + * @param method - 请求使用的HTTP方法。 + * @param headers - 请求头。 + * @param requestBody - 请求体。 + * @param responseBody - 响应体。 + * @returns 响应日志对象。 + */ + private logResponse( + response: Response, + method: string, + headers: any, + requestBody: any, + responseBody: any + ) { + const responseLog = { + ok: response.ok, + status: response.status, + statusText: response.statusText, + url: response.url, + method: method, + requestHeaders: headers, + responseHeaders: response.headers, + requestBody, + responseBody, + } + this.logger.http(JSON.stringify(responseLog, null, 2)) + return responseLog + } + + /** + * 发送网络请求并返回一个解析为响应数据的Promise。 + * @param url - 要发送请求的URL。 + * @param method - 请求使用的HTTP方法。 + * @param queryParams - 要包含在URL中的查询参数。 + * @param payload - 请求的有效负载数据。 + * @param additionalHeaders - 要包含在请求中的附加头。 + * @returns 一个解析为响应数据的Promise。 + * @throws 如果网络响应不成功或存在解析错误,则抛出错误。 + */ + protected async request({ + url, + method, + queryParams, + payload, + additionalHeaders, + }: NetRequestParams): Promise { + // 拼接完整的URL + let fullUrl = `${this.prefix}${url}` + if (queryParams) { + if (typeof queryParams === "string") { + fullUrl = `${fullUrl}?${queryParams}` + } else { + const queryString = new URLSearchParams(queryParams).toString() + if (queryString) fullUrl = `${fullUrl}?${queryString}` + } + } + + // 设置请求头 + const headers = { + ...this.headers, + ...(await this.getHeaders()), + ...additionalHeaders, + } + // 设置请求Header + if (!(payload instanceof FormData)) { + headers["Content-Type"] = "application/json" + } + + // 处理请求数据 + const body = payload instanceof FormData ? payload : JSON.stringify(payload) + + // 发送请求 + const res = await fetch(fullUrl, { + method, + body, + headers, + }) + // 获取响应数据 + let resData: any = null + let resText: string = "" + + try { + resText = await res.text() + resData = JSON.parse(resText) + } catch { + /* empty */ + } + + // 记录响应 + this.logResponse(res, method, headers, payload, resData || resText) + if (!res.ok) { + if (resData?.message || resData?.msg) { + throw new NetError({ + httpStatus: res.status, + code: resData?.code, + message: resData?.message || resData?.msg, + }) + } + throw new NetError({ + httpStatus: res.status, + code: res.status, + message: resText || "网络响应异常", + }) + } + // http 错误码正常,但解析异常 + if (!resData) { + throw new NetError({ + httpStatus: res.status, + code: 1, + message: "解析响应数据异常", + }) + } + // 响应数据异常 + if ("code" in resData && resData.code !== 0) { + throw new NetError({ + httpStatus: res.status, + code: resData.code, + message: resData.message || resData.msg || "网络请求失败", + }) + } + return resData as T + } + + /** + * 发送GET请求并返回一个解析为响应数据的Promise。 + * + * @param url - 要发送请求的URL。 + * @param queryParams - 要包含在URL中的查询参数。 + * @param additionalHeaders - 要包含在请求中的附加头。 + * @returns 一个解析为响应数据的Promise。 + */ + protected get( + url: string, + queryParams?: any, + additionalHeaders?: any + ): Promise { + return this.request({ url, method: "get", queryParams, additionalHeaders }) + } + + /** + * 发送POST请求并返回一个解析为响应数据的Promise。 + * + * @param url - 要发送请求的URL。 + * @param payload - 请求的有效负载数据。 + * @param queryParams - 要包含在URL中的查询参数。 + * @param additionalHeaders - 要包含在请求中的附加头。 + * @returns 一个解析为响应数据的Promise。 + */ + protected post( + url: string, + payload?: any, + queryParams?: any, + additionalHeaders?: any + ): Promise { + return this.request({ + url, + method: "post", + payload, + queryParams, + additionalHeaders, + }) + } + + /** + * 发送PUT请求并返回一个解析为响应数据的Promise。 + * + * @param url - 要发送请求的URL。 + * @param payload - 请求的有效负载数据。 + * @param queryParams - 要包含在URL中的查询参数。 + * @param additionalHeaders - 要包含在请求中的附加头。 + * @returns 一个解析为响应数据的Promise。 + */ + protected put( + url: string, + payload: any, + queryParams?: any, + additionalHeaders?: any + ): Promise { + return this.request({ + url, + method: "put", + payload, + queryParams, + additionalHeaders, + }) + } + + /** + * 发送DELETE请求并返回一个解析为响应数据的Promise。 + * + * @param url - 要发送请求的URL。 + * @param payload - 请求的有效负载数据。 + * @param queryParams - 要包含在URL中的查询参数。 + * @param additionalHeaders - 要包含在请求中的附加头。 + * @returns 一个解析为响应数据的Promise。 + */ + protected del( + url: string, + payload: any, + queryParams?: any, + additionalHeaders?: any + ): Promise { + return this.request({ + url, + method: "delete", + payload, + queryParams, + additionalHeaders, + }) + } + + /** + * 发送PATCH请求并返回一个解析为响应数据的Promise。 + * + * @param url - 要发送请求的URL。 + * @param payload - 请求的有效负载数据。 + * @param queryParams - 要包含在URL中的查询参数。 + * @param additionalHeaders - 要包含在请求中的附加头。 + * @returns 一个解析为响应数据的Promise。 + */ + protected patch( + url: string, + payload: any, + queryParams?: any, + additionalHeaders?: any + ): Promise { + return this.request({ + url, + method: "patch", + payload, + queryParams, + additionalHeaders, + }) + } +} + +class NetTool extends NetToolBase { + public request({ + url, + method, + queryParams, + payload, + additionalHeaders, + }: NetRequestParams): Promise { + return super.request({ + url, + method, + queryParams, + payload, + additionalHeaders, + }) + } + public get( + url: string, + queryParams?: any, + additionalHeaders?: any + ): Promise { + return super.get(url, queryParams, additionalHeaders) + } + public post( + url: string, + payload?: any, + queryParams?: any, + additionalHeaders?: any + ): Promise { + return super.post(url, payload, queryParams, additionalHeaders) + } + public put( + url: string, + payload: any, + queryParams?: any, + additionalHeaders?: any + ): Promise { + return super.put(url, payload, queryParams, additionalHeaders) + } + public del( + url: string, + payload: any, + queryParams?: any, + additionalHeaders?: any + ): Promise { + return super.del(url, payload, queryParams, additionalHeaders) + } + public patch( + url: string, + payload: any, + queryParams?: any, + additionalHeaders?: any + ): Promise { + return super.patch(url, payload, queryParams, additionalHeaders) + } + /** + * 创建一个表示400 Bad Request的响应对象。 + * + * @param message - 错误消息。 + * @returns 一个表示400 Bad Request的响应对象。 + */ + badRequest(message: string) { + this.logger.error(`return a bad request response: ${message}`) + return Response.json( + { code: 400, message, requestId: this.requestId }, + { status: 400 } + ) + } + + /** + * 创建一个表示404 Not Found的响应对象。 + * + * @param message - 错误消息。 + * @returns 一个表示404 Not Found的响应对象。 + */ + notFound(message: string) { + this.logger.error(`return a not found response: ${message}`) + return Response.json( + { code: 404, message, requestId: this.requestId }, + { status: 404 } + ) + } + + /** + * 创建一个表示500 Internal Server Error的响应对象。 + * + * @param message - 错误消息。 + * @param data - 错误数据。 + * @returns 一个表示500 Internal Server Error的响应对象。 + */ + serverError(message: string, data?: any) { + this.logger.error(`return a server error response: ${message}`) + return Response.json( + { code: 500, message, data, requestId: this.requestId }, + { status: 500 } + ) + } + + /** + * 创建一个表示200 OK的响应对象。 + * + * @param data - 响应数据。 + * @returns 一个表示200 OK的响应对象。 + */ + ok(data?: any) { + this.logger.info(`return a ok response: ${JSON.stringify(data)}`) + return Response.json({ + code: 0, + message: "success", + data, + requestId: this.requestId, + }) + } +} + +export { NetTool, NetToolBase } diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..8f3c14c --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,24 @@ +{ + "compilerOptions": { + "lib": ["ESNext"], + "module": "esnext", + "target": "esnext", + "moduleResolution": "bundler", + "moduleDetection": "force", + "allowImportingTsExtensions": false, + "noEmit": false, + "composite": true, + "strict": true, + "downlevelIteration": true, + "skipLibCheck": true, + "jsx": "react-jsx", + "allowSyntheticDefaultImports": true, + "forceConsistentCasingInFileNames": true, + "allowJs": true, + "declaration": true, + "rootDir": "./src", // 设置 rootDir + "outDir": "./dist" + }, + "include": ["src"], + "exclude": ["node_modules", "**/*.test.ts"] +} diff --git a/tsconfig.tsbuildinfo b/tsconfig.tsbuildinfo new file mode 100644 index 0000000..95a1abd --- /dev/null +++ b/tsconfig.tsbuildinfo @@ -0,0 +1 @@ +{"program":{"fileNames":["./node_modules/typescript/lib/lib.es5.d.ts","./node_modules/typescript/lib/lib.es2015.d.ts","./node_modules/typescript/lib/lib.es2016.d.ts","./node_modules/typescript/lib/lib.es2017.d.ts","./node_modules/typescript/lib/lib.es2018.d.ts","./node_modules/typescript/lib/lib.es2019.d.ts","./node_modules/typescript/lib/lib.es2020.d.ts","./node_modules/typescript/lib/lib.es2021.d.ts","./node_modules/typescript/lib/lib.es2022.d.ts","./node_modules/typescript/lib/lib.es2023.d.ts","./node_modules/typescript/lib/lib.esnext.d.ts","./node_modules/typescript/lib/lib.es2015.core.d.ts","./node_modules/typescript/lib/lib.es2015.collection.d.ts","./node_modules/typescript/lib/lib.es2015.generator.d.ts","./node_modules/typescript/lib/lib.es2015.iterable.d.ts","./node_modules/typescript/lib/lib.es2015.promise.d.ts","./node_modules/typescript/lib/lib.es2015.proxy.d.ts","./node_modules/typescript/lib/lib.es2015.reflect.d.ts","./node_modules/typescript/lib/lib.es2015.symbol.d.ts","./node_modules/typescript/lib/lib.es2015.symbol.wellknown.d.ts","./node_modules/typescript/lib/lib.es2016.array.include.d.ts","./node_modules/typescript/lib/lib.es2016.intl.d.ts","./node_modules/typescript/lib/lib.es2017.date.d.ts","./node_modules/typescript/lib/lib.es2017.object.d.ts","./node_modules/typescript/lib/lib.es2017.sharedmemory.d.ts","./node_modules/typescript/lib/lib.es2017.string.d.ts","./node_modules/typescript/lib/lib.es2017.intl.d.ts","./node_modules/typescript/lib/lib.es2017.typedarrays.d.ts","./node_modules/typescript/lib/lib.es2018.asyncgenerator.d.ts","./node_modules/typescript/lib/lib.es2018.asynciterable.d.ts","./node_modules/typescript/lib/lib.es2018.intl.d.ts","./node_modules/typescript/lib/lib.es2018.promise.d.ts","./node_modules/typescript/lib/lib.es2018.regexp.d.ts","./node_modules/typescript/lib/lib.es2019.array.d.ts","./node_modules/typescript/lib/lib.es2019.object.d.ts","./node_modules/typescript/lib/lib.es2019.string.d.ts","./node_modules/typescript/lib/lib.es2019.symbol.d.ts","./node_modules/typescript/lib/lib.es2019.intl.d.ts","./node_modules/typescript/lib/lib.es2020.bigint.d.ts","./node_modules/typescript/lib/lib.es2020.date.d.ts","./node_modules/typescript/lib/lib.es2020.promise.d.ts","./node_modules/typescript/lib/lib.es2020.sharedmemory.d.ts","./node_modules/typescript/lib/lib.es2020.string.d.ts","./node_modules/typescript/lib/lib.es2020.symbol.wellknown.d.ts","./node_modules/typescript/lib/lib.es2020.intl.d.ts","./node_modules/typescript/lib/lib.es2020.number.d.ts","./node_modules/typescript/lib/lib.es2021.promise.d.ts","./node_modules/typescript/lib/lib.es2021.string.d.ts","./node_modules/typescript/lib/lib.es2021.weakref.d.ts","./node_modules/typescript/lib/lib.es2021.intl.d.ts","./node_modules/typescript/lib/lib.es2022.array.d.ts","./node_modules/typescript/lib/lib.es2022.error.d.ts","./node_modules/typescript/lib/lib.es2022.intl.d.ts","./node_modules/typescript/lib/lib.es2022.object.d.ts","./node_modules/typescript/lib/lib.es2022.sharedmemory.d.ts","./node_modules/typescript/lib/lib.es2022.string.d.ts","./node_modules/typescript/lib/lib.es2022.regexp.d.ts","./node_modules/typescript/lib/lib.es2023.array.d.ts","./node_modules/typescript/lib/lib.es2023.collection.d.ts","./node_modules/typescript/lib/lib.es2023.intl.d.ts","./node_modules/typescript/lib/lib.esnext.array.d.ts","./node_modules/typescript/lib/lib.esnext.collection.d.ts","./node_modules/typescript/lib/lib.esnext.intl.d.ts","./node_modules/typescript/lib/lib.esnext.disposable.d.ts","./node_modules/typescript/lib/lib.esnext.string.d.ts","./node_modules/typescript/lib/lib.esnext.promise.d.ts","./node_modules/typescript/lib/lib.esnext.decorators.d.ts","./node_modules/typescript/lib/lib.esnext.object.d.ts","./node_modules/typescript/lib/lib.esnext.regexp.d.ts","./node_modules/typescript/lib/lib.decorators.d.ts","./node_modules/typescript/lib/lib.decorators.legacy.d.ts","./node_modules/@types/node/assert.d.ts","./node_modules/@types/node/assert/strict.d.ts","./node_modules/undici-types/header.d.ts","./node_modules/undici-types/readable.d.ts","./node_modules/undici-types/file.d.ts","./node_modules/undici-types/fetch.d.ts","./node_modules/undici-types/formdata.d.ts","./node_modules/undici-types/connector.d.ts","./node_modules/undici-types/client.d.ts","./node_modules/undici-types/errors.d.ts","./node_modules/undici-types/dispatcher.d.ts","./node_modules/undici-types/global-dispatcher.d.ts","./node_modules/undici-types/global-origin.d.ts","./node_modules/undici-types/pool-stats.d.ts","./node_modules/undici-types/pool.d.ts","./node_modules/undici-types/handlers.d.ts","./node_modules/undici-types/balanced-pool.d.ts","./node_modules/undici-types/agent.d.ts","./node_modules/undici-types/mock-interceptor.d.ts","./node_modules/undici-types/mock-agent.d.ts","./node_modules/undici-types/mock-client.d.ts","./node_modules/undici-types/mock-pool.d.ts","./node_modules/undici-types/mock-errors.d.ts","./node_modules/undici-types/proxy-agent.d.ts","./node_modules/undici-types/env-http-proxy-agent.d.ts","./node_modules/undici-types/retry-handler.d.ts","./node_modules/undici-types/retry-agent.d.ts","./node_modules/undici-types/api.d.ts","./node_modules/undici-types/interceptors.d.ts","./node_modules/undici-types/util.d.ts","./node_modules/undici-types/cookies.d.ts","./node_modules/undici-types/patch.d.ts","./node_modules/undici-types/websocket.d.ts","./node_modules/undici-types/eventsource.d.ts","./node_modules/undici-types/filereader.d.ts","./node_modules/undici-types/diagnostics-channel.d.ts","./node_modules/undici-types/content-type.d.ts","./node_modules/undici-types/cache.d.ts","./node_modules/undici-types/index.d.ts","./node_modules/@types/node/globals.d.ts","./node_modules/@types/node/async_hooks.d.ts","./node_modules/@types/node/buffer.d.ts","./node_modules/@types/node/child_process.d.ts","./node_modules/@types/node/cluster.d.ts","./node_modules/@types/node/console.d.ts","./node_modules/@types/node/constants.d.ts","./node_modules/@types/node/crypto.d.ts","./node_modules/@types/node/dgram.d.ts","./node_modules/@types/node/diagnostics_channel.d.ts","./node_modules/@types/node/dns.d.ts","./node_modules/@types/node/dns/promises.d.ts","./node_modules/@types/node/domain.d.ts","./node_modules/@types/node/dom-events.d.ts","./node_modules/@types/node/events.d.ts","./node_modules/@types/node/fs.d.ts","./node_modules/@types/node/fs/promises.d.ts","./node_modules/@types/node/http.d.ts","./node_modules/@types/node/http2.d.ts","./node_modules/@types/node/https.d.ts","./node_modules/@types/node/inspector.d.ts","./node_modules/@types/node/module.d.ts","./node_modules/@types/node/net.d.ts","./node_modules/@types/node/os.d.ts","./node_modules/@types/node/path.d.ts","./node_modules/@types/node/perf_hooks.d.ts","./node_modules/@types/node/process.d.ts","./node_modules/@types/node/punycode.d.ts","./node_modules/@types/node/querystring.d.ts","./node_modules/@types/node/readline.d.ts","./node_modules/@types/node/readline/promises.d.ts","./node_modules/@types/node/repl.d.ts","./node_modules/@types/node/sea.d.ts","./node_modules/@types/node/stream.d.ts","./node_modules/@types/node/stream/promises.d.ts","./node_modules/@types/node/stream/consumers.d.ts","./node_modules/@types/node/stream/web.d.ts","./node_modules/@types/node/string_decoder.d.ts","./node_modules/@types/node/test.d.ts","./node_modules/@types/node/timers.d.ts","./node_modules/@types/node/timers/promises.d.ts","./node_modules/@types/node/tls.d.ts","./node_modules/@types/node/trace_events.d.ts","./node_modules/@types/node/tty.d.ts","./node_modules/@types/node/url.d.ts","./node_modules/@types/node/util.d.ts","./node_modules/@types/node/v8.d.ts","./node_modules/@types/node/vm.d.ts","./node_modules/@types/node/wasi.d.ts","./node_modules/@types/node/worker_threads.d.ts","./node_modules/@types/node/zlib.d.ts","./node_modules/@types/node/globals.global.d.ts","./node_modules/@types/node/index.d.ts","./node_modules/@types/triple-beam/index.d.ts","./node_modules/logform/index.d.ts","./node_modules/winston-transport/index.d.ts","./node_modules/winston-daily-rotate-file/index.d.ts","./node_modules/winston/lib/winston/config/index.d.ts","./node_modules/winston/lib/winston/transports/index.d.ts","./node_modules/winston/index.d.ts","./src/logger.ts","./src/nettool.ts","./src/index.ts","./node_modules/@types/conventional-commits-parser/index.d.ts"],"fileInfos":[{"version":"44e584d4f6444f58791784f1d530875970993129442a847597db702a073ca68c","affectsGlobalScope":true},"45b7ab580deca34ae9729e97c13cfd999df04416a79116c3bfb483804f85ded4","3facaf05f0c5fc569c5649dd359892c98a85557e3e0c847964caeb67076f4d75","9a68c0c07ae2fa71b44384a839b7b8d81662a236d4b9ac30916718f7510b1b2d","5e1c4c362065a6b95ff952c0eab010f04dcd2c3494e813b493ecfd4fcb9fc0d8","68d73b4a11549f9c0b7d352d10e91e5dca8faa3322bfb77b661839c42b1ddec7","5efce4fc3c29ea84e8928f97adec086e3dc876365e0982cc8479a07954a3efd4","feecb1be483ed332fad555aff858affd90a48ab19ba7272ee084704eb7167569","5514e54f17d6d74ecefedc73c504eadffdeda79c7ea205cf9febead32d45c4bc","27bdc30a0e32783366a5abeda841bc22757c1797de8681bbe81fbc735eeb1c10","17edc026abf73c5c2dd508652d63f68ec4efd9d4856e3469890d27598209feb5",{"version":"6920e1448680767498a0b77c6a00a8e77d14d62c3da8967b171f1ddffa3c18e4","affectsGlobalScope":true},{"version":"dc2df20b1bcdc8c2d34af4926e2c3ab15ffe1160a63e58b7e09833f616efff44","affectsGlobalScope":true},{"version":"4443e68b35f3332f753eacc66a04ac1d2053b8b035a0e0ac1d455392b5e243b3","affectsGlobalScope":true},{"version":"bc47685641087c015972a3f072480889f0d6c65515f12bd85222f49a98952ed7","affectsGlobalScope":true},{"version":"0dc1e7ceda9b8b9b455c3a2d67b0412feab00bd2f66656cd8850e8831b08b537","affectsGlobalScope":true},{"version":"ce691fb9e5c64efb9547083e4a34091bcbe5bdb41027e310ebba8f7d96a98671","affectsGlobalScope":true},{"version":"8d697a2a929a5fcb38b7a65594020fcef05ec1630804a33748829c5ff53640d0","affectsGlobalScope":true},{"version":"4ff2a353abf8a80ee399af572debb8faab2d33ad38c4b4474cff7f26e7653b8d","affectsGlobalScope":true},{"version":"93495ff27b8746f55d19fcbcdbaccc99fd95f19d057aed1bd2c0cafe1335fbf0","affectsGlobalScope":true},{"version":"6fc23bb8c3965964be8c597310a2878b53a0306edb71d4b5a4dfe760186bcc01","affectsGlobalScope":true},{"version":"ea011c76963fb15ef1cdd7ce6a6808b46322c527de2077b6cfdf23ae6f5f9ec7","affectsGlobalScope":true},{"version":"38f0219c9e23c915ef9790ab1d680440d95419ad264816fa15009a8851e79119","affectsGlobalScope":true},{"version":"bb42a7797d996412ecdc5b2787720de477103a0b2e53058569069a0e2bae6c7e","affectsGlobalScope":true},{"version":"4738f2420687fd85629c9efb470793bb753709c2379e5f85bc1815d875ceadcd","affectsGlobalScope":true},{"version":"2f11ff796926e0832f9ae148008138ad583bd181899ab7dd768a2666700b1893","affectsGlobalScope":true},{"version":"4de680d5bb41c17f7f68e0419412ca23c98d5749dcaaea1896172f06435891fc","affectsGlobalScope":true},{"version":"9fc46429fbe091ac5ad2608c657201eb68b6f1b8341bd6d670047d32ed0a88fa","affectsGlobalScope":true},{"version":"61c37c1de663cf4171e1192466e52c7a382afa58da01b1dc75058f032ddf0839","affectsGlobalScope":true},{"version":"b541a838a13f9234aba650a825393ffc2292dc0fc87681a5d81ef0c96d281e7a","affectsGlobalScope":true},{"version":"9e9fbd7030c440b33d021da145d3232984c8bb7916f277e8ffd3dc2e3eae2bdb","affectsGlobalScope":true},{"version":"811ec78f7fefcabbda4bfa93b3eb67d9ae166ef95f9bff989d964061cbf81a0c","affectsGlobalScope":true},{"version":"717937616a17072082152a2ef351cb51f98802fb4b2fdabd32399843875974ca","affectsGlobalScope":true},{"version":"d7e7d9b7b50e5f22c915b525acc5a49a7a6584cf8f62d0569e557c5cfc4b2ac2","affectsGlobalScope":true},{"version":"71c37f4c9543f31dfced6c7840e068c5a5aacb7b89111a4364b1d5276b852557","affectsGlobalScope":true},{"version":"576711e016cf4f1804676043e6a0a5414252560eb57de9faceee34d79798c850","affectsGlobalScope":true},{"version":"89c1b1281ba7b8a96efc676b11b264de7a8374c5ea1e6617f11880a13fc56dc6","affectsGlobalScope":true},{"version":"74f7fa2d027d5b33eb0471c8e82a6c87216223181ec31247c357a3e8e2fddc5b","affectsGlobalScope":true},{"version":"ae37d6ccd1560b0203ab88d46987393adaaa78c919e51acf32fb82c86502e98c","affectsGlobalScope":true},{"version":"063600664504610fe3e99b717a1223f8b1900087fab0b4cad1496a114744f8df","affectsGlobalScope":true},{"version":"934019d7e3c81950f9a8426d093458b65d5aff2c7c1511233c0fd5b941e608ab","affectsGlobalScope":true},{"version":"bf14a426dbbf1022d11bd08d6b8e709a2e9d246f0c6c1032f3b2edb9a902adbe","affectsGlobalScope":true},{"version":"5e07ed3809d48205d5b985642a59f2eba47c402374a7cf8006b686f79efadcbd","affectsGlobalScope":true},{"version":"2b72d528b2e2fe3c57889ca7baef5e13a56c957b946906d03767c642f386bbc3","affectsGlobalScope":true},{"version":"479553e3779be7d4f68e9f40cdb82d038e5ef7592010100410723ceced22a0f7","affectsGlobalScope":true},{"version":"368af93f74c9c932edd84c58883e736c9e3d53cec1fe24c0b0ff451f529ceab1","affectsGlobalScope":true},{"version":"af3dd424cf267428f30ccfc376f47a2c0114546b55c44d8c0f1d57d841e28d74","affectsGlobalScope":true},{"version":"995c005ab91a498455ea8dfb63aa9f83fa2ea793c3d8aa344be4a1678d06d399","affectsGlobalScope":true},{"version":"d3d7b04b45033f57351c8434f60b6be1ea71a2dfec2d0a0c3c83badbb0e3e693","affectsGlobalScope":true},{"version":"956d27abdea9652e8368ce029bb1e0b9174e9678a273529f426df4b3d90abd60","affectsGlobalScope":true},{"version":"4fa6ed14e98aa80b91f61b9805c653ee82af3502dc21c9da5268d3857772ca05","affectsGlobalScope":true},{"version":"e6633e05da3ff36e6da2ec170d0d03ccf33de50ca4dc6f5aeecb572cedd162fb","affectsGlobalScope":true},{"version":"d8670852241d4c6e03f2b89d67497a4bbefe29ecaa5a444e2c11a9b05e6fccc6","affectsGlobalScope":true},{"version":"8444af78980e3b20b49324f4a16ba35024fef3ee069a0eb67616ea6ca821c47a","affectsGlobalScope":true},{"version":"caccc56c72713969e1cfe5c3d44e5bab151544d9d2b373d7dbe5a1e4166652be","affectsGlobalScope":true},{"version":"3287d9d085fbd618c3971944b65b4be57859f5415f495b33a6adc994edd2f004","affectsGlobalScope":true},{"version":"b4b67b1a91182421f5df999988c690f14d813b9850b40acd06ed44691f6727ad","affectsGlobalScope":true},{"version":"9d540251809289a05349b70ab5f4b7b99f922af66ab3c39ba56a475dcf95d5ff","affectsGlobalScope":true},{"version":"436aaf437562f276ec2ddbee2f2cdedac7664c1e4c1d2c36839ddd582eeb3d0a","affectsGlobalScope":true},{"version":"8e3c06ea092138bf9fa5e874a1fdbc9d54805d074bee1de31b99a11e2fec239d","affectsGlobalScope":true},{"version":"0b11f3ca66aa33124202c80b70cd203219c3d4460cfc165e0707aa9ec710fc53","affectsGlobalScope":true},{"version":"6a3f5a0129cc80cf439ab71164334d649b47059a4f5afca90282362407d0c87f","affectsGlobalScope":true},{"version":"811c71eee4aa0ac5f7adf713323a5c41b0cf6c4e17367a34fbce379e12bbf0a4","affectsGlobalScope":true},{"version":"15b98a533864d324e5f57cd3cfc0579b231df58c1c0f6063ea0fcb13c3c74ff9","affectsGlobalScope":true},{"version":"0a6282c8827e4b9a95f4bf4f5c205673ada31b982f50572d27103df8ceb8013c","affectsGlobalScope":true},{"version":"ac77cb3e8c6d3565793eb90a8373ee8033146315a3dbead3bde8db5eaf5e5ec6","affectsGlobalScope":true},{"version":"d4b1d2c51d058fc21ec2629fff7a76249dec2e36e12960ea056e3ef89174080f","affectsGlobalScope":true},{"version":"2fef54945a13095fdb9b84f705f2b5994597640c46afeb2ce78352fab4cb3279","affectsGlobalScope":true},{"version":"56e4ed5aab5f5920980066a9409bfaf53e6d21d3f8d020c17e4de584d29600ad","affectsGlobalScope":true},{"version":"33358442698bb565130f52ba79bfd3d4d484ac85fe33f3cb1759c54d18201393","affectsGlobalScope":true},{"version":"782dec38049b92d4e85c1585fbea5474a219c6984a35b004963b00beb1aab538","affectsGlobalScope":true},"e142fda89ed689ea53d6f2c93693898464c7d29a0ae71c6dc8cdfe5a1d76c775","7394959e5a741b185456e1ef5d64599c36c60a323207450991e7a42e08911419","5929864ce17fba74232584d90cb721a89b7ad277220627cc97054ba15a98ea8f","24bd580b5743dc56402c440dc7f9a4f5d592ad7a419f25414d37a7bfe11e342b","25c8056edf4314820382a5fdb4bb7816999acdcb929c8f75e3f39473b87e85bc","c464d66b20788266e5353b48dc4aa6bc0dc4a707276df1e7152ab0c9ae21fad8","78d0d27c130d35c60b5e5566c9f1e5be77caf39804636bc1a40133919a949f21","c6fd2c5a395f2432786c9cb8deb870b9b0e8ff7e22c029954fabdd692bff6195","1d6e127068ea8e104a912e42fc0a110e2aa5a66a356a917a163e8cf9a65e4a75","5ded6427296cdf3b9542de4471d2aa8d3983671d4cac0f4bf9c637208d1ced43","6bdc71028db658243775263e93a7db2fd2abfce3ca569c3cca5aee6ed5eb186d","cadc8aced301244057c4e7e73fbcae534b0f5b12a37b150d80e5a45aa4bebcbd","385aab901643aa54e1c36f5ef3107913b10d1b5bb8cbcd933d4263b80a0d7f20","9670d44354bab9d9982eca21945686b5c24a3f893db73c0dae0fd74217a4c219","0b8a9268adaf4da35e7fa830c8981cfa22adbbe5b3f6f5ab91f6658899e657a7","11396ed8a44c02ab9798b7dca436009f866e8dae3c9c25e8c1fbc396880bf1bb","ba7bc87d01492633cb5a0e5da8a4a42a1c86270e7b3d2dea5d156828a84e4882","4893a895ea92c85345017a04ed427cbd6a1710453338df26881a6019432febdd","c21dc52e277bcfc75fac0436ccb75c204f9e1b3fa5e12729670910639f27343e","13f6f39e12b1518c6650bbb220c8985999020fe0f21d818e28f512b7771d00f9","9b5369969f6e7175740bf51223112ff209f94ba43ecd3bb09eefff9fd675624a","4fe9e626e7164748e8769bbf74b538e09607f07ed17c2f20af8d680ee49fc1da","24515859bc0b836719105bb6cc3d68255042a9f02a6022b3187948b204946bd2","ea0148f897b45a76544ae179784c95af1bd6721b8610af9ffa467a518a086a43","24c6a117721e606c9984335f71711877293a9651e44f59f3d21c1ea0856f9cc9","dd3273ead9fbde62a72949c97dbec2247ea08e0c6952e701a483d74ef92d6a17","405822be75ad3e4d162e07439bac80c6bcc6dbae1929e179cf467ec0b9ee4e2e","0db18c6e78ea846316c012478888f33c11ffadab9efd1cc8bcc12daded7a60b6","4d2b0eb911816f66abe4970898f97a2cfc902bcd743cbfa5017fad79f7ef90d8","bd0532fd6556073727d28da0edfd1736417a3f9f394877b6d5ef6ad88fba1d1a","89167d696a849fce5ca508032aabfe901c0868f833a8625d5a9c6e861ef935d2","e53a3c2a9f624d90f24bf4588aacd223e7bec1b9d0d479b68d2f4a9e6011147f","24b8685c62562f5d98615c5a0c1d05f297cf5065f15246edfe99e81ec4c0e011","93507c745e8f29090efb99399c3f77bec07db17acd75634249dc92f961573387","339dc5265ee5ed92e536a93a04c4ebbc2128f45eeec6ed29f379e0085283542c","4732aec92b20fb28c5fe9ad99521fb59974289ed1e45aecb282616202184064f","2e85db9e6fd73cfa3d7f28e0ab6b55417ea18931423bd47b409a96e4a169e8e6","c46e079fe54c76f95c67fb89081b3e399da2c7d109e7dca8e4b58d83e332e605","bf67d53d168abc1298888693338cb82854bdb2e69ef83f8a0092093c2d562107",{"version":"b0007d2025c46c15c1b67f107aea10349b79e436bcf5e6690554df3f7232de0f","affectsGlobalScope":true},"db3ec8993b7596a4ef47f309c7b25ee2505b519c13050424d9c34701e5973315",{"version":"5a38909344f43b30b74c90623f83f4412344e992f2ff158e3b6d3725af18dc02","affectsGlobalScope":true},"af49b066a76ce26673fe49d1885cc6b44153f1071ed2d952f2a90fccba1095c9","f22fd1dc2df53eaf5ce0ff9e0a3326fc66f880d6a652210d50563ae72625455f",{"version":"3ddbdb519e87a7827c4f0c4007013f3628ca0ebb9e2b018cf31e5b2f61c593f1","affectsGlobalScope":true},"a40826e8476694e90da94aa008283a7de50d1dafd37beada623863f1901cb7fb",{"version":"6d498d4fd8036ea02a4edcae10375854a0eb1df0496cf0b9d692577d3c0fd603","affectsGlobalScope":true},"24642567d3729bcc545bacb65ee7c0db423400c7f1ef757cab25d05650064f98","fd09b892597ab93e7f79745ce725a3aaf6dd005e8db20f0c63a5d10984cba328","a3be878ff1e1964ab2dc8e0a3b67087cf838731c7f3d8f603337e7b712fdd558","5433f7f77cd1fd53f45bd82445a4e437b2f6a72a32070e907530a4fea56c30c8","9be74296ee565af0c12d7071541fdd23260f53c3da7731fb6361f61150a791f6",{"version":"ee1ee365d88c4c6c0c0a5a5701d66ebc27ccd0bcfcfaa482c6e2e7fe7b98edf7","affectsGlobalScope":true},{"version":"f501a53b94ba382d9ba396a5c486969a3abc68309828fa67f916035f5d37fe2b","affectsGlobalScope":true},"aa658b5d765f630c312ac9202d110bbaf2b82d180376457f0a9d57b42629714a","312ac7cbd070107766a9886fd27f9faad997ef57d93fdfb4095df2c618ac8162","bcfcff784a59db3f323c25cea5ae99a903ca9292c060f2c7e470ea73aaf71b44","672ad3045f329e94002256f8ed460cfd06173a50c92cde41edaadfacffd16808","64da4965d1e0559e134d9c1621ae400279a216f87ed00c4cce4f2c7c78021712","ddbf3aac94f85dbb8e4d0360782e60020da75a0becfc0d3c69e437c645feb30f",{"version":"0166fce1204d520fdfd6b5febb3cda3deee438bcbf8ce9ffeb2b1bcde7155346","affectsGlobalScope":true},"d8b13eab85b532285031b06a971fa051bf0175d8fff68065a24a6da9c1c986cf","50c382ba1827988c59aa9cc9d046e386d55d70f762e9e352e95ee8cb7337cdb8","2178ab4b68402d1de2dda199d3e4a55f7200e3334f5a9727fbd9d16975cdf75f",{"version":"21d7e87f271e72d02f8d167edc902f90b04525edc7918f00f01dd0bd00599f7e","affectsGlobalScope":true},{"version":"1785f3ca422887c49159986725420bd5764117c8f1fe70985b5e36d278158e09","affectsGlobalScope":true},"a215554477f7629e3dcbc8cde104bec036b78673650272f5ffdc5a2cee399a0a","c3497fc242aabfedcd430b5932412f94f157b5906568e737f6a18cc77b36a954","cdc1de3b672f9ef03ff15c443aa1b631edca35b6ae6970a7da6400647ff74d95","139ad1dc93a503da85b7a0d5f615bddbae61ad796bc68fedd049150db67a1e26","bf01fdd3b93cf633b3f7420718457af19c57ab8cbfea49268df60bae2e84d627","15c5e91b5f08be34a78e3d976179bf5b7a9cc28dc0ef1ffebffeb3c7812a2dca","65b39cc6b610a4a4aecc321f6efb436f10c0509d686124795b4c36a5e915b89e","269929a24b2816343a178008ac9ae9248304d92a8ba8e233055e0ed6dbe6ef71","93452d394fdd1dc551ec62f5042366f011a00d342d36d50793b3529bfc9bd633","3c1f19c7abcda6b3a4cf9438a15c7307a080bd3b51dfd56b198d9f86baf19447","d3edb86744e2c19f2c1503849ac7594a5e06024f2451bacae032390f2e20314a",{"version":"52ff13ed3bf7d9763eb6b7ad0545681f9fa591258d1df778256469b516047edb","affectsGlobalScope":true},{"version":"8a3e61347b8f80aa5af532094498bceb0c0b257b25a6aa8ab4880fd6ed57c95a","affectsGlobalScope":true},"98e00f3613402504bc2a2c9a621800ab48e0a463d1eed062208a4ae98ad8f84c","950f6810f7c80e0cffefcf1bcc6ade3485c94394720e334c3c2be3c16b6922fb","5475df7cfc493a08483c9d7aa61cc04791aecba9d0a2efc213f23c4006d4d3cd","000720870b275764c65e9f28ac97cc9e4d9e4a36942d4750ca8603e416e9c57c",{"version":"54412c70bacb9ed547ed6caae8836f712a83ccf58d94466f3387447ec4e82dc3","affectsGlobalScope":true},{"version":"e74e7b0baa7a24f073080091427d36a75836d584b9393e6ac2b1daf1647fe65a","affectsGlobalScope":true},"4c48e931a72f6971b5add7fdb1136be1d617f124594e94595f7114af749395e0","478eb5c32250678a906d91e0529c70243fc4d75477a08f3da408e2615396f558","e686a88c9ee004c8ba12ffc9d674ca3192a4c50ed0ca6bd5b2825c289e2b2bfe",{"version":"98d547613610452ac9323fb9ec4eafc89acab77644d6e23105b3c94913f712b3","affectsGlobalScope":true},"4423fb3d6abe6eefb8d7f79eb2df9510824a216ec1c6feee46718c9b18e6d89f",{"version":"ab9b9a36e5284fd8d3bf2f7d5fcbc60052f25f27e4d20954782099282c60d23e","affectsGlobalScope":true},"a42be67ed1ddaec743582f41fc219db96a1b69719fccac6d1464321178d610fc","908217c4f2244ec402b73533ebfcc46d6dcd34fc1c807ff403d7f98702abb3bc","201ced2ca97d71fe47afdaebc656c2fa63ef2784256e4dfc9eb83930f7aac2c2","d8b8a5a6bf623239d5374ad4a7ff6f3b195ab5ee61293f59f1957e90d2a22809","60acaaf99f80c65b62f3daa650b47090acab36d50b79e5c9fce95c0a97a0d83a","35d283eca7dc0a0c7b099f5fbbf0678b87f3d837572cd5e539ba297ad9837e68","1c8384a195a2d931cf6e2b8f656acf558ca649a3f74922d86b95889f49a7f7c5","26ec2c615ee349154b9cdb180a9bbd2d3e28a2646242e936cf79c1a44847ade7",{"version":"4410c44435f7a78c2bdb83a2e7e47179f712bd03dfbf8e0afe887e4300211787","signature":"46b4ee8d5346de4f5cd02469a999f4d9ae32b925cc116277f9cc207d5f669c91"},{"version":"69f4d201a94a3b62650fc83ebefbe05372972b3c7a75e1387185dd172f083ab1","signature":"9bef903de43860083566d251583a747d00e4cf335fb1d3899d2319a00cfee6cf"},{"version":"75c40ec322fb8ad472d64bf8a0d956959a5da7153db26ea97e752ebf5e46123c","signature":"5be42ad7b45c1d26ee1f1029a17e9cccb7bce22afe9456d0e2f52f8e6025b0e3"},"f96f3c445afc7d65d4790386e37c5b57f095f285cc89b8315b209fe0c81837c1"],"root":[[171,173]],"options":{"allowImportingTsExtensions":false,"allowJs":true,"allowSyntheticDefaultImports":true,"composite":true,"declaration":true,"downlevelIteration":true,"jsx":4,"module":99,"outDir":"./dist","rootDir":"./src","skipLibCheck":true,"strict":true,"target":99},"fileIdsList":[[144,163],[72],[112],[113,118,147],[114,119,125,126,133,144,155],[114,115,125,133],[116,156],[117,118,126,134],[118,144,152],[119,121,125,133],[112,120],[121,122],[125],[123,125],[112,125],[125,126,127,144,155],[125,126,127,140,144,147],[110,113,160],[121,125,128,133,144,155],[125,126,128,129,133,144,152,155],[128,130,144,152,155],[72,73,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161,162],[125,131],[132,155,160],[121,125,133,144],[134],[135],[112,136],[72,73,112,113,114,115,116,117,118,119,120,121,122,123,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161],[138],[139],[125,140,141],[140,142,156,158],[113,125,144,145,146,147],[113,144,146],[144,145],[147],[148],[72,144],[125,150,151],[150,151],[118,133,144,152],[153],[133,154],[113,128,139,155],[118,156],[144,157],[132,158],[159],[113,118,125,127,136,144,155,158,160],[144,161],[164],[82,86,155],[82,144,155],[77],[79,82,152,155],[133,152],[163],[77,163],[79,82,133,155],[74,75,78,81,113,125,144,155],[82,89],[74,80],[82,103,104],[78,82,113,147,155,163],[113,163],[103,113,163],[76,77,163],[82],[76,77,78,79,80,81,82,83,84,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,104,105,106,107,108,109],[82,97],[82,89,90],[80,82,90,91],[81],[74,77,82],[82,86,90,91],[86],[80,82,85,155],[74,79,82,89],[113,144],[77,82,103,113,160,163],[166,169],[144,163,165],[144,163,165,166,168,169],[128,163,166],[171,172],[167,170],[170,171]],"referencedMap":[[174,1],[72,2],[73,2],[112,3],[113,4],[114,5],[115,6],[116,7],[117,8],[118,9],[119,10],[120,11],[121,12],[122,12],[124,13],[123,14],[125,15],[126,16],[127,17],[111,18],[128,19],[129,20],[130,21],[163,22],[131,23],[132,24],[133,25],[134,26],[135,27],[136,28],[137,29],[138,30],[139,31],[140,32],[141,32],[142,33],[144,34],[146,35],[145,36],[147,37],[148,38],[149,39],[150,40],[151,41],[152,42],[153,43],[154,44],[155,45],[156,46],[157,47],[158,48],[159,49],[160,50],[161,51],[165,52],[89,53],[99,54],[88,53],[109,55],[80,56],[79,57],[108,58],[102,59],[107,60],[82,61],[96,62],[81,63],[105,64],[77,65],[76,66],[106,67],[78,68],[83,69],[87,69],[110,70],[100,71],[91,72],[92,73],[94,74],[90,75],[93,76],[103,58],[85,77],[86,78],[95,79],[75,80],[98,71],[97,69],[104,81],[167,82],[166,83],[170,84],[168,58],[169,85],[173,86],[171,87],[172,88]],"latestChangedDtsFile":"./dist/index.d.ts"},"version":"5.5.4"} \ No newline at end of file