diff --git a/.node-version b/.node-version index e8416a1..b52936b 100644 --- a/.node-version +++ b/.node-version @@ -1 +1 @@ -v24.13.0 +v26.2.0 diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..5835d6f --- /dev/null +++ b/.prettierrc @@ -0,0 +1,6 @@ +{ + "tabWidth": 2, + "semi": true, + "singleQuote": false, + "trailingComma": "all" +} diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 0000000..1d7ac85 --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,3 @@ +{ + "recommendations": ["dbaeumer.vscode-eslint", "esbenp.prettier-vscode"] +} diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..873e72d --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,14 @@ +{ + "[typescript]": { + "editor.formatOnSave": true, + "editor.defaultFormatter": "esbenp.prettier-vscode" + }, + "[javascript]": { + "editor.formatOnSave": true, + "editor.defaultFormatter": "esbenp.prettier-vscode" + }, + "[json]": { + "editor.formatOnSave": true, + "editor.defaultFormatter": "esbenp.prettier-vscode" + } +} diff --git a/crates/yaejuyang-supertonic/src/main.rs b/crates/yaejuyang-supertonic/src/main.rs index a9b7927..10e7054 100644 --- a/crates/yaejuyang-supertonic/src/main.rs +++ b/crates/yaejuyang-supertonic/src/main.rs @@ -5,7 +5,7 @@ use std::{collections::HashMap, path::PathBuf}; pub mod api; pub mod tts; -use tts::{TtsOpts, TtsPool, load_text_to_speech, load_voice_style}; +use tts::{TtsOpts, TtsPool, load_text_to_speech}; use crate::tts::engine::load_voice_style_map; @@ -18,7 +18,7 @@ async fn main() -> Result<(), Box> { std::env::var("SUPERTONIC_MODEL_DIR").unwrap_or_else(|_| "./assets".to_string()); let voice_style_path = std::env::var("SUPERTONIC_VOICE_STYLE") .unwrap_or_else(|_| format!("F1={model_dir}/voice_styles/F1.json")); - let lang = std::env::var("SUPERTONIC_LANG").unwrap_or_else(|_| "en".to_string()); + let _lang = std::env::var("SUPERTONIC_LANG").unwrap_or_else(|_| "en".to_string()); let total_step: usize = std::env::var("SUPERTONIC_TOTAL_STEP") .ok() .and_then(|v| v.parse().ok()) diff --git a/eslint.config.js b/eslint.config.js index e69de29..c14a559 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -0,0 +1,18 @@ +// @ts-check + +import js from "@eslint/js"; +import { defineConfig } from "eslint/config"; +import tseslint from "typescript-eslint"; + +export default defineConfig({ + extends: [js.configs.recommended, tseslint.configs.recommended], + basePath: "packages", + files: ["**/*.ts"], + linterOptions: { + reportUnusedDisableDirectives: "off", // Or use false / 0 + }, + rules: { + "@typescript-eslint/no-namespace": "off", + "@typescript-eslint/no-explicit-any": "off", + }, +}); diff --git a/package-lock.json b/package-lock.json index 48552cd..9832af5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,6 +12,7 @@ "@snazzah/davey": "^0.1.9", "discord.js": "^14.25.1", "dotenv": "^17.2.3", + "extensionless": "^2.0.6", "fastify": "^5.7.1", "libsodium-wrappers": "^0.8.0", "opusscript": "^0.0.8", @@ -22,9 +23,13 @@ "typescript": "^5.9.3" }, "devDependencies": { + "@eslint/js": "^10.0.1", "@types/ffprobe-static": "^2.0.3", "@types/node": "25.0.9", - "@types/pg": "^8.16.0" + "@types/pg": "^8.16.0", + "eslint": "^10.4.0", + "prettier": "^3.8.3", + "typescript-eslint": "^8.59.4" } }, "node_modules/@chevrotain/cst-dts-gen": { @@ -72,22 +77,6 @@ "integrity": "sha512-hBzuU5+JjB2cqNZyszkDHZgOSrUUT8V3dhgRl8Q9Gp6dAj/H5+KILGjbhDpc3Iy9qmqlm/akuOI2ut9VUtzJxQ==", "license": "Apache-2.0" }, - "node_modules/@derhuerst/http-basic": { - "version": "8.2.4", - "resolved": "https://registry.npmjs.org/@derhuerst/http-basic/-/http-basic-8.2.4.tgz", - "integrity": "sha512-F9rL9k9Xjf5blCz8HsJRO4diy111cayL2vkY2XE4r4t3n0yPXVYy3KD3nJ1qbrSn9743UWSXH4IwuCa/HWlGFw==", - "license": "MIT", - "optional": true, - "dependencies": { - "caseless": "^0.12.0", - "concat-stream": "^2.0.0", - "http-response-object": "^3.0.1", - "parse-cache-control": "^1.0.1" - }, - "engines": { - "node": ">=6.0.0" - } - }, "node_modules/@discordjs/builders": { "version": "1.13.1", "resolved": "https://registry.npmjs.org/@discordjs/builders/-/builders-1.13.1.tgz", @@ -351,6 +340,173 @@ "tslib": "^2.4.0" } }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz", + "integrity": "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.12.2", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz", + "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/config-array": { + "version": "0.23.5", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.23.5.tgz", + "integrity": "sha512-Y3kKLvC1dvTOT+oGlqNQ1XLqK6D1HU2YXPc52NmAlJZbMMWDzGYXMiPRJ8TYD39muD/OTjlZmNJ4ib7dvSrMBA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/object-schema": "^3.0.5", + "debug": "^4.3.1", + "minimatch": "^10.2.4" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "node_modules/@eslint/config-array/node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/@eslint/config-array/node_modules/brace-expansion": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz", + "integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/@eslint/config-array/node_modules/minimatch": { + "version": "10.2.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", + "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.5" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@eslint/config-helpers": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.6.0.tgz", + "integrity": "sha512-ii6Bw9jJ2zi2cWA2Z+9/QZ/+3DX6kwaV5Q986D/CdP3Lap3w/pgQZ373FV7byY/i7L4IRH/G43I5dz1ClsCbpA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^1.2.1" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "node_modules/@eslint/core": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-1.2.1.tgz", + "integrity": "sha512-MwcE1P+AZ4C6DWlpin/OmOA54mmIZ/+xZuJiQd4SyB29oAJjN30UW9wkKNptW2ctp4cEsvhlLY/CsQ1uoHDloQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "node_modules/@eslint/js": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-10.0.1.tgz", + "integrity": "sha512-zeR9k5pd4gxjZ0abRoIaxdc7I3nDktoXZk2qOv9gCNWx3mVwEn32VRhyLaRsDiJjTs0xq/T8mfPtyuXu7GWBcA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "eslint": "^10.0.0" + }, + "peerDependenciesMeta": { + "eslint": { + "optional": true + } + } + }, + "node_modules/@eslint/object-schema": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-3.0.5.tgz", + "integrity": "sha512-vqTaUEgxzm+YDSdElad6PiRoX4t8VGDjCtt05zn4nU810UIx/uNEV7/lZJ6KwFThKZOzOxzXy48da+No7HZaMw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "node_modules/@eslint/plugin-kit": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.7.1.tgz", + "integrity": "sha512-rZAP3aVgB9ds9KOeUSL+zZ21hPmo8dh6fnIFwRQj5EAZl9gzR7wxYbYXYysAM8CTqGmUGyp2S4kUdV17MnGuWQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^1.2.1", + "levn": "^0.4.1" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, "node_modules/@fastify/ajv-compiler": { "version": "4.0.5", "resolved": "https://registry.npmjs.org/@fastify/ajv-compiler/-/ajv-compiler-4.0.5.tgz", @@ -474,6 +630,72 @@ "hono": "^4" } }, + "node_modules/@humanfs/core": { + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.2.tgz", + "integrity": "sha512-UhXNm+CFMWcbChXywFwkmhqjs3PRCmcSa/hfBgLIb7oQ5HNb1wS0icWsGtSAUNgefHeI+eBrA8I1fxmbHsGdvA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanfs/types": "^0.15.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node": { + "version": "0.16.8", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.8.tgz", + "integrity": "sha512-gE1eQNZ3R++kTzFUpdGlpmy8kDZD/MLyHqDwqjkVQI0JMdI1D51sy1H958PNXYkM2rAac7e5/CnIKZrHtPh3BQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanfs/core": "^0.19.2", + "@humanfs/types": "^0.15.0", + "@humanwhocodes/retry": "^0.4.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/types": { + "version": "0.15.0", + "resolved": "https://registry.npmjs.org/@humanfs/types/-/types-0.15.0.tgz", + "integrity": "sha512-ZZ1w0aoQkwuUuC7Yf+7sdeaNfqQiiLcSRbfI08oAxqLtpXQr9AIVX7Ay7HLDuiLYAaFPu8oBYNq/QIi9URHJ3Q==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/retry": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", + "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, "node_modules/@mrleebo/prisma-ast": { "version": "0.13.1", "resolved": "https://registry.npmjs.org/@mrleebo/prisma-ast/-/prisma-ast-0.13.1.tgz", @@ -983,6 +1205,20 @@ "tslib": "^2.4.0" } }, + "node_modules/@types/esrecurse": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@types/esrecurse/-/esrecurse-4.3.1.tgz", + "integrity": "sha512-xJBAbDifo5hpffDBuHl0Y8ywswbiAp/Wi7Y/GtAgSlZyIABppyurxVueOPE8LUQOxdlgi6Zqce7uoEpqNTeiUw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/estree": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz", + "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/ffprobe-static": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/@types/ffprobe-static/-/ffprobe-static-2.0.3.tgz", @@ -990,6 +1226,13 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/node": { "version": "25.0.9", "resolved": "https://registry.npmjs.org/@types/node/-/node-25.0.9.tgz", @@ -1030,6 +1273,276 @@ "@types/node": "*" } }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "8.59.4", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.59.4.tgz", + "integrity": "sha512-PegsU+XfyJJNjd4+u/k6f9yTyp0lEXXiPopUNobZcIAUJFGICFLN+sP0Rb3JehVmiij1Ph0dFGYqODoRo/2+6A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/regexpp": "^4.12.2", + "@typescript-eslint/scope-manager": "8.59.4", + "@typescript-eslint/type-utils": "8.59.4", + "@typescript-eslint/utils": "8.59.4", + "@typescript-eslint/visitor-keys": "8.59.4", + "ignore": "^7.0.5", + "natural-compare": "^1.4.0", + "ts-api-utils": "^2.5.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^8.59.4", + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", + "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "8.59.4", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.59.4.tgz", + "integrity": "sha512-zORHqO/tuhxY1zWuTvMUqddRxpiFJ72xVfcNoWpqdLjs6lfPbuQBJuW4pk+49/uBMy7Ssr4bzgjiKmmDB1UbZQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@typescript-eslint/scope-manager": "8.59.4", + "@typescript-eslint/types": "8.59.4", + "@typescript-eslint/typescript-estree": "8.59.4", + "@typescript-eslint/visitor-keys": "8.59.4", + "debug": "^4.4.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/project-service": { + "version": "8.59.4", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.59.4.tgz", + "integrity": "sha512-Ly00Vu4oAacfDeHp2Zg85ioNG6l8HG+tN1D7J+xTHSxu9y0awYKJ2zH1rFBn8ZSfuGK+7FxK3Cgl3uAz0aZZLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/tsconfig-utils": "^8.59.4", + "@typescript-eslint/types": "^8.59.4", + "debug": "^4.4.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "8.59.4", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.59.4.tgz", + "integrity": "sha512-mUeR/3H1WrTAddJrwut8OoPjfauaztMQmRwV5fQTUyNVJCLiUXXe4lGEyYIL2oFDpP7UtgbGJXCt72wT0z2S3Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.59.4", + "@typescript-eslint/visitor-keys": "8.59.4" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/tsconfig-utils": { + "version": "8.59.4", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.59.4.tgz", + "integrity": "sha512-DLCpnKgD4alVxTBSKulK+gU1KCqOgUXfDRDXh2mZgzokQKa/70ax93I2uVO3m/LLvIAtWZIFoiifudmIqAxpMA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "8.59.4", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.59.4.tgz", + "integrity": "sha512-uonTuPAAKr9XaBGqJ3LjYTh72zy5DyGesljO9gtmk/eFW0W1fRHjnwVYKB35Lm8d5Q5CluEW3gPHjTvZTmgrfA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.59.4", + "@typescript-eslint/typescript-estree": "8.59.4", + "@typescript-eslint/utils": "8.59.4", + "debug": "^4.4.3", + "ts-api-utils": "^2.5.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/types": { + "version": "8.59.4", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.59.4.tgz", + "integrity": "sha512-F1o7WJcCq+bc8dwcO/YsSEOudAH8RDtaOhM6wcAQhcUsFhnWQl81JKy48q1hoxAU0qrzM89+31GYh1515Zde3Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "8.59.4", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.59.4.tgz", + "integrity": "sha512-F+RuOmcDXo4+TPdfd/TCLS3m2nw8gE9XXyZLrA3JBfaA5tz9TtdkyD3YJFmPxulyc2cKbEok/CvFE3MgSLWnag==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/project-service": "8.59.4", + "@typescript-eslint/tsconfig-utils": "8.59.4", + "@typescript-eslint/types": "8.59.4", + "@typescript-eslint/visitor-keys": "8.59.4", + "debug": "^4.4.3", + "minimatch": "^10.2.2", + "semver": "^7.7.3", + "tinyglobby": "^0.2.15", + "ts-api-utils": "^2.5.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz", + "integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { + "version": "10.2.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", + "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.5" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "8.59.4", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.59.4.tgz", + "integrity": "sha512-cYXeNAUsG4lJo5dbc1FcKm+JwIWrj1/UpTORsC6tGMjEZ81DYcvIr9/ueikhMa/Y/gDQYGp+YX9/xQrXje5BJw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.9.1", + "@typescript-eslint/scope-manager": "8.59.4", + "@typescript-eslint/types": "8.59.4", + "@typescript-eslint/typescript-estree": "8.59.4" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "8.59.4", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.59.4.tgz", + "integrity": "sha512-U3gxVaDVnuZKhSspW/MzMxE1kq7zOdc072FcSNoqA1I9p8HyKbBFfEHoWckBAMgNMph4MamwS5iTVzFmrnt8TQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.59.4", + "eslint-visitor-keys": "^5.0.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, "node_modules/@vladfrangu/async_event_emitter": { "version": "2.4.7", "resolved": "https://registry.npmjs.org/@vladfrangu/async_event_emitter/-/async_event_emitter-2.4.7.tgz", @@ -1052,6 +1565,30 @@ "integrity": "sha512-2BjRTZxTPvheOvGbBslFSYOUkr+SjPtOnrLP33f+VIWLzezQpZcqVg7ja3L4dBXmzzgwT+a029jRx5PCi3JuiA==", "license": "MIT" }, + "node_modules/acorn": { + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", + "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", + "dev": true, + "license": "MIT", + "peer": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, "node_modules/agent-base": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", @@ -1170,13 +1707,6 @@ "concat-map": "0.0.1" } }, - "node_modules/buffer-from": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", - "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", - "license": "MIT", - "optional": true - }, "node_modules/c12": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/c12/-/c12-3.1.0.tgz", @@ -1217,13 +1747,6 @@ "url": "https://dotenvx.com" } }, - "node_modules/caseless": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", - "integrity": "sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==", - "license": "Apache-2.0", - "optional": true - }, "node_modules/chevrotain": { "version": "10.5.0", "resolved": "https://registry.npmjs.org/chevrotain/-/chevrotain-10.5.0.tgz", @@ -1292,22 +1815,6 @@ "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", "license": "MIT" }, - "node_modules/concat-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-2.0.0.tgz", - "integrity": "sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==", - "engines": [ - "node >= 6.0" - ], - "license": "MIT", - "optional": true, - "dependencies": { - "buffer-from": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^3.0.2", - "typedarray": "^0.0.6" - } - }, "node_modules/confbox": { "version": "0.2.4", "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.2.4.tgz", @@ -1379,6 +1886,13 @@ } } }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true, + "license": "MIT" + }, "node_modules/deepmerge-ts": { "version": "7.1.5", "resolved": "https://registry.npmjs.org/deepmerge-ts/-/deepmerge-ts-7.1.5.tgz", @@ -1506,14 +2020,233 @@ "node": ">=14" } }, - "node_modules/env-paths": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", - "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, "license": "MIT", - "optional": true, "engines": { - "node": ">=6" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-10.4.0.tgz", + "integrity": "sha512-loXy6bWOoP3EP6JA7jo6p5jMpBJmHmsNZM5SFRHLdh1MGOPurMnNBj4ZlAbaqUAaQWbCr7jHV4P7gzAyryZWkQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.8.0", + "@eslint-community/regexpp": "^4.12.2", + "@eslint/config-array": "^0.23.5", + "@eslint/config-helpers": "^0.6.0", + "@eslint/core": "^1.2.1", + "@eslint/plugin-kit": "^0.7.1", + "@humanfs/node": "^0.16.6", + "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.4.2", + "@types/estree": "^1.0.6", + "ajv": "^6.14.0", + "cross-spawn": "^7.0.6", + "debug": "^4.3.2", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^9.1.2", + "eslint-visitor-keys": "^5.0.1", + "espree": "^11.2.0", + "esquery": "^1.7.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^8.0.0", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "minimatch": "^10.2.4", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } + } + }, + "node_modules/eslint-scope": { + "version": "9.1.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-9.1.2.tgz", + "integrity": "sha512-xS90H51cKw0jltxmvmHy2Iai1LIqrfbw57b79w/J7MfvDfkIkFZ+kj6zC3BjtUwh150HsSSdxXZcsuv72miDFQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "@types/esrecurse": "^4.3.1", + "@types/estree": "^1.0.8", + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz", + "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/node_modules/ajv": { + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.15.0.tgz", + "integrity": "sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/eslint/node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/eslint/node_modules/brace-expansion": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz", + "integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/eslint/node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "license": "MIT" + }, + "node_modules/eslint/node_modules/minimatch": { + "version": "10.2.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", + "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.5" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/espree": { + "version": "11.2.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-11.2.0.tgz", + "integrity": "sha512-7p3DrVEIopW1B1avAGLuCSh1jubc01H2JHc8B4qqGblmg5gI9yumBgACjWo4JlIc04ufug4xJ3SQI8HkS/Rgzw==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.16.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^5.0.1" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esquery": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz", + "integrity": "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" } }, "node_modules/exsolve": { @@ -1522,6 +2255,12 @@ "integrity": "sha512-LmDxfWXwcTArk8fUEnOfSZpHOJ6zOMUJKOtFLFqJLoKJetuQG874Uc7/Kki7zFLzYybmZhp1M7+98pfMqeX8yA==", "license": "MIT" }, + "node_modules/extensionless": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/extensionless/-/extensionless-2.0.6.tgz", + "integrity": "sha512-Kri4UehTAnQzYpM2gySya2nsCBEChxstgtKtOqR1nqZpF0HYM+BZXaeua9uUhE4Z0CE4kfZp+0JFhKL3SsuKFA==", + "license": "MIT" + }, "node_modules/fast-check": { "version": "3.23.2", "resolved": "https://registry.npmjs.org/fast-check/-/fast-check-3.23.2.tgz", @@ -1556,6 +2295,13 @@ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", "license": "MIT" }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true, + "license": "MIT" + }, "node_modules/fast-json-stringify": { "version": "6.3.0", "resolved": "https://registry.npmjs.org/fast-json-stringify/-/fast-json-stringify-6.3.0.tgz", @@ -1580,6 +2326,13 @@ "rfdc": "^1.2.0" } }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true, + "license": "MIT" + }, "node_modules/fast-querystring": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/fast-querystring/-/fast-querystring-1.1.2.tgz", @@ -1647,6 +2400,37 @@ "reusify": "^1.0.4" } }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/file-entry-cache": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "flat-cache": "^4.0.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, "node_modules/find-my-way": { "version": "9.4.0", "resolved": "https://registry.npmjs.org/find-my-way/-/find-my-way-9.4.0.tgz", @@ -1661,6 +2445,44 @@ "node": ">=20" } }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat-cache": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.4" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/flatted": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.2.tgz", + "integrity": "sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==", + "dev": true, + "license": "ISC" + }, "node_modules/foreground-child": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", @@ -1793,6 +2615,19 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, "node_modules/graceful-fs": { "version": "4.2.11", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", @@ -1826,23 +2661,6 @@ "node": ">=16.9.0" } }, - "node_modules/http-response-object": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/http-response-object/-/http-response-object-3.0.2.tgz", - "integrity": "sha512-bqX0XTF6fnXSQcEJ2Iuyr75yVakyjIDCqroJQ/aHfSdlM743Cwqoi2nDYMzLGWUcuTWGWy8AAvOKXTfiv6q9RA==", - "license": "MIT", - "optional": true, - "dependencies": { - "@types/node": "^10.0.3" - } - }, - "node_modules/http-response-object/node_modules/@types/node": { - "version": "10.17.60", - "resolved": "https://registry.npmjs.org/@types/node/-/node-10.17.60.tgz", - "integrity": "sha512-F0KIgDJfy2nA3zMLmWGKxcH2ZVEtCZXHHdOQs2gSaQ27+lNeEfGxzkIw90aXswATX7AZ33tahPbzy6KAfUreVw==", - "license": "MIT", - "optional": true - }, "node_modules/http-status-codes": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/http-status-codes/-/http-status-codes-2.3.0.tgz", @@ -1878,6 +2696,26 @@ "url": "https://opencollective.com/express" } }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, "node_modules/inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", @@ -1904,6 +2742,16 @@ "node": ">= 10" } }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/is-fullwidth-code-point": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", @@ -1913,6 +2761,19 @@ "node": ">=8" } }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/is-property": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz", @@ -1934,6 +2795,13 @@ "jiti": "lib/jiti-cli.mjs" } }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true, + "license": "MIT" + }, "node_modules/json-schema-ref-resolver": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/json-schema-ref-resolver/-/json-schema-ref-resolver-3.0.0.tgz", @@ -1959,6 +2827,37 @@ "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", "license": "MIT" }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, "node_modules/libsodium": { "version": "0.8.2", "resolved": "https://registry.npmjs.org/libsodium/-/libsodium-0.8.2.tgz", @@ -2020,6 +2919,22 @@ "node": ">=10" } }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/lodash": { "version": "4.17.23", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.23.tgz", @@ -2179,6 +3094,13 @@ "node": ">=8.0.0" } }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true, + "license": "MIT" + }, "node_modules/node-addon-api": { "version": "8.5.0", "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-8.5.0.tgz", @@ -2278,6 +3200,24 @@ "wrappy": "1" } }, + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, "node_modules/opusscript": { "version": "0.0.8", "resolved": "https://registry.npmjs.org/opusscript/-/opusscript-0.0.8.tgz", @@ -2285,11 +3225,47 @@ "license": "MIT", "peer": true }, - "node_modules/parse-cache-control": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parse-cache-control/-/parse-cache-control-1.0.1.tgz", - "integrity": "sha512-60zvsJReQPX5/QP0Kzfd/VrpjScIQ7SHBW6bFCYfEP+fp0Eppr1SHhIO5nd1PjZtvclzSzES9D/p5nFJurwfWg==", - "optional": true + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } }, "node_modules/path-is-absolute": { "version": "1.0.1", @@ -2420,6 +3396,20 @@ "split2": "^4.1.0" } }, + "node_modules/picomatch": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/pino": { "version": "10.3.1", "resolved": "https://registry.npmjs.org/pino/-/pino-10.3.1.tgz", @@ -2538,6 +3528,32 @@ "node": ">=0.10.0" } }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/prettier": { + "version": "3.8.3", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.8.3.tgz", + "integrity": "sha512-7igPTM53cGHMW8xWuVTydi2KO233VFiTNyF5hLJqpilHfmn8C8gPf+PS7dUT64YcXFbiMGZxS9pCSxL/Dxm/Jw==", + "dev": true, + "license": "MIT", + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, "node_modules/prism-media": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/prism-media/-/prism-media-1.3.5.tgz", @@ -2614,16 +3630,6 @@ ], "license": "MIT" }, - "node_modules/progress": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", - "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", - "license": "MIT", - "optional": true, - "engines": { - "node": ">=0.4.0" - } - }, "node_modules/proper-lockfile": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/proper-lockfile/-/proper-lockfile-4.1.2.tgz", @@ -2635,6 +3641,16 @@ "signal-exit": "^3.0.2" } }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/pure-rand": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.1.0.tgz", @@ -3039,6 +4055,23 @@ "node": ">=18" } }, + "node_modules/tinyglobby": { + "version": "0.2.16", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.16.tgz", + "integrity": "sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.4" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, "node_modules/toad-cache": { "version": "3.7.0", "resolved": "https://registry.npmjs.org/toad-cache/-/toad-cache-3.7.0.tgz", @@ -3054,6 +4087,19 @@ "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", "license": "MIT" }, + "node_modules/ts-api-utils": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.5.0.tgz", + "integrity": "sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.12" + }, + "peerDependencies": { + "typescript": ">=4.8.4" + } + }, "node_modules/ts-mixer": { "version": "6.0.4", "resolved": "https://registry.npmjs.org/ts-mixer/-/ts-mixer-6.0.4.tgz", @@ -3066,12 +4112,18 @@ "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", "license": "0BSD" }, - "node_modules/typedarray": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", - "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==", + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, "license": "MIT", - "optional": true + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } }, "node_modules/typescript": { "version": "5.9.3", @@ -3087,6 +4139,30 @@ "node": ">=14.17" } }, + "node_modules/typescript-eslint": { + "version": "8.59.4", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.59.4.tgz", + "integrity": "sha512-Rw6+44QNFaXtgHSjPy+Kw8hrJniMYzR85E9yLmOLcfZ91/rz+JXQbDTCmc6ccxMPY6K6PgAq26f0JCBfR7LIPQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/eslint-plugin": "8.59.4", + "@typescript-eslint/parser": "8.59.4", + "@typescript-eslint/typescript-estree": "8.59.4", + "@typescript-eslint/utils": "8.59.4" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, "node_modules/undici": { "version": "6.21.3", "resolved": "https://registry.npmjs.org/undici/-/undici-6.21.3.tgz", @@ -3102,6 +4178,16 @@ "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", "license": "MIT" }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", @@ -3162,6 +4248,16 @@ "string-width": "^1.0.2 || 2 || 3 || 4" } }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", @@ -3204,6 +4300,19 @@ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", "license": "ISC" }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/zeptomatch": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/zeptomatch/-/zeptomatch-2.1.0.tgz", diff --git a/package.json b/package.json index bcb5cd4..3daff1e 100644 --- a/package.json +++ b/package.json @@ -1,15 +1,19 @@ { "main": "dist", + "type": "module", "scripts": { "prisma:push": "prisma db push", - "format:prisma": "prisma format", - "format": "npm run format:prisma", + "prisma:format": "prisma format", + "check": "npm run check:tsc && npm run check:eslint && npm run check:format", + "check:eslint": "eslint", + "check:tsc": "./node_modules/typescript/bin/tsc --noEmit", + "check:format": "prettier --check .", + "fix:format": "prettier --write .", "build:prisma": "prisma generate", - "build:tsc": "tsc", + "build:tsc": "./node_modules/typescript/bin/tsc", "build": "npm run build:prisma && npm run build:tsc", - "start": "prisma migrate deploy && node .", - "start:no-db": "npm run build:tsc && node .", - "dev": "npm run build && npm run start" + "start": "prisma migrate deploy && node --import=extensionless/register .", + "dev": "npm run build && node --import=extensionless/register ." }, "dependencies": { "@discordjs/opus": "^0.10.0", @@ -19,18 +23,23 @@ "@snazzah/davey": "^0.1.9", "discord.js": "^14.25.1", "dotenv": "^17.2.3", + "extensionless": "^2.0.6", "fastify": "^5.7.1", "libsodium-wrappers": "^0.8.0", "opusscript": "^0.0.8", "pg": "^8.17.1", "play-dl": "^1.9.7", "prism-media": "^1.3.5", - "prisma": "^7.2.0", - "typescript": "^5.9.3" + "prisma": "^7.2.0" }, "devDependencies": { + "@eslint/js": "^10.0.1", "@types/ffprobe-static": "^2.0.3", "@types/node": "25.0.9", - "@types/pg": "^8.16.0" + "@types/pg": "^8.16.0", + "eslint": "^10.4.0", + "prettier": "^3.8.3", + "typescript-eslint": "^8.59.4", + "typescript": "^5.9.3" } } diff --git a/packages/bot/admin.ts b/packages/bot/admin.ts index 51dab91..9d3142b 100644 --- a/packages/bot/admin.ts +++ b/packages/bot/admin.ts @@ -1,7 +1,7 @@ import { setUserCanTypecast } from "./db"; -export const AdminUsers = [ "858173387775148073", "367946917197381644" ]; +export const AdminUsers = ["858173387775148073", "367946917197381644"]; -AdminUsers.forEach(userid => { - setUserCanTypecast(userid, true); +AdminUsers.forEach((userid) => { + setUserCanTypecast(userid, true); }); diff --git a/packages/bot/command.ts b/packages/bot/command.ts index 7f149fc..54063fb 100644 --- a/packages/bot/command.ts +++ b/packages/bot/command.ts @@ -1,41 +1,56 @@ -import { ChatInputCommandInteraction, SlashCommandBuilder, SlashCommandOptionsOnlyBuilder, SlashCommandSubcommandBuilder } from "discord.js"; +import { + ChatInputCommandInteraction, + SlashCommandBuilder, + type SlashCommandOptionsOnlyBuilder, + SlashCommandSubcommandBuilder, +} from "discord.js"; import { join } from "node:path"; -import { requireDirectorySync } from "../utils/requireDirectory"; +import { requireDirectory } from "../utils/requireDirectory"; import { AdminUsers } from "./admin"; -export type DiscordCommandData = SlashCommandBuilder | SlashCommandOptionsOnlyBuilder | SlashCommandSubcommandBuilder -export type DiscordCommandExecute = (interaction: ChatInputCommandInteraction) => Promise +export type DiscordCommandData = + | SlashCommandBuilder + | SlashCommandOptionsOnlyBuilder + | SlashCommandSubcommandBuilder; +export type DiscordCommandExecute = ( + interaction: ChatInputCommandInteraction, +) => Promise; export interface DiscordCommand { - data: DiscordCommandData - execute: DiscordCommandExecute + data: DiscordCommandData; + execute: DiscordCommandExecute; } export function defineCommand( - data: DiscordCommandData, - execute: DiscordCommandExecute, - isAdminCommand=false, + data: DiscordCommandData, + execute: DiscordCommandExecute, + isAdminCommand = false, ): DiscordCommand { - if (isAdminCommand) { - return { - data: data, - execute: async (interaction: ChatInputCommandInteraction): Promise => { - if (AdminUsers.includes(interaction.user.id)) { - execute(interaction); - } else { - interaction.reply("당신은 어드민이 아닙니다"); - } - } - } - } + if (isAdminCommand) { return { - data: data, - execute: execute, - } + data: data, + execute: async ( + interaction: ChatInputCommandInteraction, + ): Promise => { + if (AdminUsers.includes(interaction.user.id)) { + execute(interaction); + } else { + interaction.reply("당신은 어드민이 아닙니다"); + } + }, + }; + } + return { + data: data, + execute: execute, + }; } -const commandDirectory = join(__dirname, "commands"); -export const defineCommands = requireDirectorySync(commandDirectory); +const commandDirectory = join(import.meta.dirname, "commands"); +export const defineCommands = + await requireDirectory(commandDirectory); export const commandExecuteNameHashMap: { - [key: string]: DiscordCommandExecute -} = Object.fromEntries(defineCommands.map(command => [command.data.name, command.execute])); + [key: string]: DiscordCommandExecute; +} = Object.fromEntries( + defineCommands.map((command) => [command.data.name, command.execute]), +); diff --git a/packages/bot/commands/getReadChannels.ts b/packages/bot/commands/getReadChannels.ts index a7f8fba..3ad9508 100644 --- a/packages/bot/commands/getReadChannels.ts +++ b/packages/bot/commands/getReadChannels.ts @@ -1,28 +1,35 @@ -import { ChatInputCommandInteraction, MessageFlags, SlashCommandBuilder } from "discord.js"; -import { defineCommand } from "../command"; -import { getGuildProfile } from "../db"; +import { + ChatInputCommandInteraction, + MessageFlags, + SlashCommandBuilder, +} from "discord.js"; +import { defineCommand } from "../command.js"; +import { getGuildProfile } from "../db.js"; export default defineCommand( - new SlashCommandBuilder() - .setName("읽는채널") - .setDescription("예주가 읽어주는 채널들을 말해줘요"), - async (interaction: ChatInputCommandInteraction): Promise => { - await interaction.deferReply({ - flags: [MessageFlags.Ephemeral] - }); + new SlashCommandBuilder() + .setName("읽는채널") + .setDescription("예주가 읽어주는 채널들을 말해줘요"), + async (interaction: ChatInputCommandInteraction): Promise => { + await interaction.deferReply({ + flags: [MessageFlags.Ephemeral], + }); - const guildId = interaction.guildId; + const guildId = interaction.guildId; - if (guildId == null) - return await interaction.editReply("알수없는 서버에요!"); + if (guildId == null) + return await interaction.editReply("알수없는 서버에요!"); - try { - const guildProfile = await getGuildProfile(guildId); - const readChannel = guildProfile.readChannel; + try { + const guildProfile = await getGuildProfile(guildId); + const readChannel = guildProfile.readChannel; - await interaction.editReply(readChannel.map(channel => `<#${channel}>`).join("\n") || "아무 채널도 읽지 않아요!"); - } catch { - await interaction.editReply("서버 프로필을 가져오는데 문제가 생겼어요"); - } + await interaction.editReply( + readChannel.map((channel: any) => `<#${channel}>`).join("\n") || + "아무 채널도 읽지 않아요!", + ); + } catch { + await interaction.editReply("서버 프로필을 가져오는데 문제가 생겼어요"); } -) \ No newline at end of file + }, +); diff --git a/packages/bot/commands/getStatus.ts b/packages/bot/commands/getStatus.ts index 047d6b4..4a8f5b7 100644 --- a/packages/bot/commands/getStatus.ts +++ b/packages/bot/commands/getStatus.ts @@ -1,22 +1,26 @@ -import { AttachmentBuilder, ChatInputCommandInteraction, MessageFlags, SlashCommandBuilder } from "discord.js"; +import { + AttachmentBuilder, + ChatInputCommandInteraction, + SlashCommandBuilder, +} from "discord.js"; import { defineCommand } from "../command"; import { OutputHandler } from "../../utils/outputHandler"; export default defineCommand( - new SlashCommandBuilder() - .setName("상태") - .setDescription("예주의 상태를 확인해요"), - async (interaction: ChatInputCommandInteraction): Promise => { - if (interaction.guild == null) - return interaction.reply("올바르지 않은 서버에요"); - - const buffer = Buffer.from(await OutputHandler.getErrorLog(), 'utf-8'); - const attachment = new AttachmentBuilder(buffer, { name: 'result.txt' }); + new SlashCommandBuilder() + .setName("상태") + .setDescription("예주의 상태를 확인해요"), + async (interaction: ChatInputCommandInteraction): Promise => { + if (interaction.guild == null) + return interaction.reply("올바르지 않은 서버에요"); - await interaction.reply({ - content: "제 상태에요", - files: [attachment] - }); - }, - true -) \ No newline at end of file + const buffer = Buffer.from(await OutputHandler.getErrorLog(), "utf-8"); + const attachment = new AttachmentBuilder(buffer, { name: "result.txt" }); + + await interaction.reply({ + content: "제 상태에요", + files: [attachment], + }); + }, + true, +); diff --git a/packages/bot/commands/joinVoiceChannel.ts b/packages/bot/commands/joinVoiceChannel.ts index 089d467..1667f68 100644 --- a/packages/bot/commands/joinVoiceChannel.ts +++ b/packages/bot/commands/joinVoiceChannel.ts @@ -1,48 +1,56 @@ -import { Channel, ChannelType, ChatInputCommandInteraction, GuildMember, MessageFlags, SlashCommandBuilder } from "discord.js"; +import { + type Channel, + ChannelType, + ChatInputCommandInteraction, + GuildMember, + MessageFlags, + SlashCommandBuilder, +} from "discord.js"; import { defineCommand } from "../command"; import { joinVoiceChannel } from "@discordjs/voice"; export default defineCommand( - new SlashCommandBuilder() - .setName("등장") - .setDescription("예주가 등장해요") - .addChannelOption(option => - option - .setName("channel") - .setDescription("예주가 등장할 채널이에요") - .addChannelTypes(ChannelType.GuildVoice) - ), - async (interaction: ChatInputCommandInteraction): Promise => { - await interaction.deferReply({ - flags: [MessageFlags.Ephemeral] - }); + new SlashCommandBuilder() + .setName("등장") + .setDescription("예주가 등장해요") + .addChannelOption((option) => + option + .setName("channel") + .setDescription("예주가 등장할 채널이에요") + .addChannelTypes(ChannelType.GuildVoice), + ), + async (interaction: ChatInputCommandInteraction): Promise => { + await interaction.deferReply({ + flags: [MessageFlags.Ephemeral], + }); - const member = interaction.member as GuildMember | null; - if (member == null) - return; - - if (interaction.guild == null) - return interaction.reply({ - content: "올바르지 않은 서버에요", - ephemeral: true, - }); + const member = interaction.member as GuildMember | null; + if (member == null) return; - const channel = interaction.options.getChannel("channel") as Channel || member.voice.channel; + if (interaction.guild == null) + return interaction.reply({ + content: "올바르지 않은 서버에요", + ephemeral: true, + }); - if (channel == null) - return interaction.reply({ - content: "통화방에 들어가거나 채널을 지정해주세요!", - ephemeral: true, - }); + const channel = + (interaction.options.getChannel("channel") as Channel) || + member.voice.channel; - joinVoiceChannel({ - channelId: channel.id, - guildId: interaction.guild.id, - adapterCreator: interaction.guild.voiceAdapterCreator, - selfDeaf: false, - selfMute: false, - }); + if (channel == null) + return interaction.reply({ + content: "통화방에 들어가거나 채널을 지정해주세요!", + ephemeral: true, + }); - await interaction.editReply("등장했어요!"); - } -) \ No newline at end of file + joinVoiceChannel({ + channelId: channel.id, + guildId: interaction.guild.id, + adapterCreator: interaction.guild.voiceAdapterCreator, + selfDeaf: false, + selfMute: false, + }); + + await interaction.editReply("등장했어요!"); + }, +); diff --git a/packages/bot/commands/leaveVoiceChannel.ts b/packages/bot/commands/leaveVoiceChannel.ts index 9ca6a44..8a098fa 100644 --- a/packages/bot/commands/leaveVoiceChannel.ts +++ b/packages/bot/commands/leaveVoiceChannel.ts @@ -1,25 +1,27 @@ -import { ChatInputCommandInteraction, MessageFlags, SlashCommandBuilder } from "discord.js"; -import { defineCommand, DiscordCommand } from "../command"; +import { + ChatInputCommandInteraction, + MessageFlags, + SlashCommandBuilder, +} from "discord.js"; +import { defineCommand } from "../command"; import { getVoiceConnection } from "@discordjs/voice"; export default defineCommand( - new SlashCommandBuilder() - .setName("퇴장") - .setDescription("예주가 퇴장해요"), - async (interaction: ChatInputCommandInteraction): Promise => { - await interaction.deferReply({ - flags: [MessageFlags.Ephemeral] - }); + new SlashCommandBuilder().setName("퇴장").setDescription("예주가 퇴장해요"), + async (interaction: ChatInputCommandInteraction): Promise => { + await interaction.deferReply({ + flags: [MessageFlags.Ephemeral], + }); - if (interaction.guild == null) - return interaction.editReply("올바르지 않은 서버에요"); + if (interaction.guild == null) + return interaction.editReply("올바르지 않은 서버에요"); - const connection = getVoiceConnection(interaction.guild.id); - if (!connection) - return interaction.editReply("예주는 통화방에 존제하지 않아요"); + const connection = getVoiceConnection(interaction.guild.id); + if (!connection) + return interaction.editReply("예주는 통화방에 존제하지 않아요"); - connection.disconnect(); + connection.disconnect(); - await interaction.editReply("퇴장했어요!"); - } -) \ No newline at end of file + await interaction.editReply("퇴장했어요!"); + }, +); diff --git a/packages/bot/commands/playVoice.ts b/packages/bot/commands/playVoice.ts index d93f6b8..81d2c57 100644 --- a/packages/bot/commands/playVoice.ts +++ b/packages/bot/commands/playVoice.ts @@ -1,38 +1,42 @@ -import { ChatInputCommandInteraction, MessageFlags, SlashCommandSubcommandBuilder } from "discord.js"; +import { + ChatInputCommandInteraction, + MessageFlags, + SlashCommandSubcommandBuilder, +} from "discord.js"; import { defineCommand } from "../command"; import { playVoice } from "../tts"; import { getUserProfile } from "../db"; export default defineCommand( - new SlashCommandSubcommandBuilder() - .setName("말") - .setDescription("구구가가") - .addStringOption(option => - option - .setName("content") - .setDescription("예주가 말해준데요") - .setRequired(true) - ), - async (interaction: ChatInputCommandInteraction): Promise => { - await interaction.deferReply({ - flags: [MessageFlags.Ephemeral] - }); + new SlashCommandSubcommandBuilder() + .setName("말") + .setDescription("구구가가") + .addStringOption((option) => + option + .setName("content") + .setDescription("예주가 말해준데요") + .setRequired(true), + ), + async (interaction: ChatInputCommandInteraction): Promise => { + await interaction.deferReply({ + flags: [MessageFlags.Ephemeral], + }); - if (interaction.guild == null) - return await interaction.editReply("올바르지 않은 서버에요"); + if (interaction.guild == null) + return await interaction.editReply("올바르지 않은 서버에요"); - try { - const userProfile = await getUserProfile(interaction.user.id); - await playVoice( - interaction.guild, - userProfile, - userProfile.voice, - interaction.options.getString("content") as string - ); + try { + const userProfile = await getUserProfile(interaction.user.id); + await playVoice( + interaction.guild, + userProfile, + userProfile.voice, + interaction.options.getString("content") as string, + ); - await interaction.editReply("말했어요!"); - } catch { - await interaction.editReply("오늘따라 말이 꼬이네요 ㅜ.ㅜ"); - } + await interaction.editReply("말했어요!"); + } catch { + await interaction.editReply("오늘따라 말이 꼬이네요 ㅜ.ㅜ"); } -) \ No newline at end of file + }, +); diff --git a/packages/bot/commands/readChannel.ts b/packages/bot/commands/readChannel.ts index 92ef62b..3aa742f 100644 --- a/packages/bot/commands/readChannel.ts +++ b/packages/bot/commands/readChannel.ts @@ -1,36 +1,42 @@ -import { Channel, ChannelType, ChatInputCommandInteraction, MessageFlags, SlashCommandSubcommandBuilder } from "discord.js"; -import { defineCommand, DiscordCommand } from "../command"; +import { + type Channel, + ChannelType, + ChatInputCommandInteraction, + MessageFlags, + SlashCommandSubcommandBuilder, +} from "discord.js"; +import { defineCommand } from "../command"; import { insertGuildReadChannel } from "../db"; export default defineCommand( - new SlashCommandSubcommandBuilder() - .setName("읽어") - .setDescription("예주가 해당 채널을 읽어줘요") - .addChannelOption(option => - option - .setName("channel") - .addChannelTypes(ChannelType.GuildText, ChannelType.GuildVoice) - .setDescription("예주가 읽을 채널이에요") - .setRequired(true) - ), - async (interaction: ChatInputCommandInteraction): Promise => { - await interaction.deferReply({ - flags: [MessageFlags.Ephemeral] - }); + new SlashCommandSubcommandBuilder() + .setName("읽어") + .setDescription("예주가 해당 채널을 읽어줘요") + .addChannelOption((option) => + option + .setName("channel") + .addChannelTypes(ChannelType.GuildText, ChannelType.GuildVoice) + .setDescription("예주가 읽을 채널이에요") + .setRequired(true), + ), + async (interaction: ChatInputCommandInteraction): Promise => { + await interaction.deferReply({ + flags: [MessageFlags.Ephemeral], + }); - const channel = interaction.options.getChannel("channel") as Channel; - const guildId = interaction.guildId; + const channel = interaction.options.getChannel("channel") as Channel; + const guildId = interaction.guildId; - if (guildId == null) - return await interaction.editReply("알수없는 서버에요!"); + if (guildId == null) + return await interaction.editReply("알수없는 서버에요!"); - try { - await insertGuildReadChannel(guildId, channel.id); - } catch { - return await interaction.editReply("읽는대 너무 어려워요.."); - } + try { + await insertGuildReadChannel(guildId, channel.id); + } catch { + return await interaction.editReply("읽는대 너무 어려워요.."); + } - await interaction.editReply("예주가 이제 이 채널을 읽어요!"); - }, - true -) \ No newline at end of file + await interaction.editReply("예주가 이제 이 채널을 읽어요!"); + }, + true, +); diff --git a/packages/bot/commands/setNya.ts b/packages/bot/commands/setNya.ts index e1f2049..b5b30c4 100644 --- a/packages/bot/commands/setNya.ts +++ b/packages/bot/commands/setNya.ts @@ -1,19 +1,21 @@ -import { ChatInputCommandInteraction, GuildMember, MessageFlags, SlashCommandBuilder } from "discord.js"; +import { + ChatInputCommandInteraction, + MessageFlags, + SlashCommandBuilder, +} from "discord.js"; import { defineCommand } from "../command"; import { getUserProfile, setUserNya } from "../db"; export default defineCommand( - new SlashCommandBuilder() - .setName("냥") - .setDescription("???"), - async (interaction: ChatInputCommandInteraction): Promise => { - await interaction.deferReply({ - flags: [MessageFlags.Ephemeral] - }); + new SlashCommandBuilder().setName("냥").setDescription("???"), + async (interaction: ChatInputCommandInteraction): Promise => { + await interaction.deferReply({ + flags: [MessageFlags.Ephemeral], + }); - const profile = await getUserProfile(interaction.user.id); - await setUserNya(interaction.user.id, !profile.nya); + const profile = await getUserProfile(interaction.user.id); + await setUserNya(interaction.user.id, !profile.nya); - await interaction.editReply(profile.nya ? "냐앙..." : "냐앙!!"); - } -) \ No newline at end of file + await interaction.editReply(profile.nya ? "냐앙..." : "냐앙!!"); + }, +); diff --git a/packages/bot/commands/setSupertonicStyle.ts b/packages/bot/commands/setSupertonicStyle.ts index 31cb061..3c4230a 100644 --- a/packages/bot/commands/setSupertonicStyle.ts +++ b/packages/bot/commands/setSupertonicStyle.ts @@ -1,29 +1,31 @@ -import { ChatInputCommandInteraction, MessageFlags, SlashCommandSubcommandBuilder } from "discord.js"; +import { + ChatInputCommandInteraction, + MessageFlags, + SlashCommandSubcommandBuilder, +} from "discord.js"; import { defineCommand } from "../command"; import { setUserSupertonicStyle } from "../db"; import { SUPERTONIC_STYLE_LIST } from "../../env"; export default defineCommand( - new SlashCommandSubcommandBuilder() - .setName("슈퍼토닉목소리") - .setDescription("예주의 슈퍼토닉 목소리 스타일을 설정해요") - .addStringOption(option => - option - .setName("style") - .setDescription("사용할수 있는 목소리들이에요") - .setRequired(true) - .addChoices( - ...SUPERTONIC_STYLE_LIST - ) - ), - async (interaction: ChatInputCommandInteraction): Promise => { - await interaction.deferReply({ - flags: [MessageFlags.Ephemeral] - }); + new SlashCommandSubcommandBuilder() + .setName("슈퍼토닉목소리") + .setDescription("예주의 슈퍼토닉 목소리 스타일을 설정해요") + .addStringOption((option) => + option + .setName("style") + .setDescription("사용할수 있는 목소리들이에요") + .setRequired(true) + .addChoices(...SUPERTONIC_STYLE_LIST), + ), + async (interaction: ChatInputCommandInteraction): Promise => { + await interaction.deferReply({ + flags: [MessageFlags.Ephemeral], + }); - const style = interaction.options.getString("style") as string; - await setUserSupertonicStyle(interaction.user.id, style); + const style = interaction.options.getString("style") as string; + await setUserSupertonicStyle(interaction.user.id, style); - await interaction.editReply("예주의 목소리 스타일을 변경했어요!"); - } -) \ No newline at end of file + await interaction.editReply("예주의 목소리 스타일을 변경했어요!"); + }, +); diff --git a/packages/bot/commands/setVoice.ts b/packages/bot/commands/setVoice.ts index f56536d..d01aa37 100644 --- a/packages/bot/commands/setVoice.ts +++ b/packages/bot/commands/setVoice.ts @@ -1,31 +1,35 @@ -import { ChatInputCommandInteraction, MessageFlags, SlashCommandSubcommandBuilder } from "discord.js"; +import { + ChatInputCommandInteraction, + MessageFlags, + SlashCommandSubcommandBuilder, +} from "discord.js"; import { defineCommand } from "../command"; import { Voice } from "../../db/generated/prisma/enums"; import { setUserVoice } from "../db"; export default defineCommand( - new SlashCommandSubcommandBuilder() - .setName("목소리") - .setDescription("예주의 목소리를 설정해요") - .addStringOption(option => - option - .setName("voice") - .setDescription("사용할수 있는 목소리들이에요") - .setRequired(true) - .addChoices( - { name: "TypeCast", value: "TypeCast" }, - { name: "Papago", value: "Papago" }, - { name: "Supertonic", value: "Supertonic" } - ) + new SlashCommandSubcommandBuilder() + .setName("목소리") + .setDescription("예주의 목소리를 설정해요") + .addStringOption((option) => + option + .setName("voice") + .setDescription("사용할수 있는 목소리들이에요") + .setRequired(true) + .addChoices( + { name: "TypeCast", value: "TypeCast" }, + { name: "Papago", value: "Papago" }, + { name: "Supertonic", value: "Supertonic" }, ), - async (interaction: ChatInputCommandInteraction): Promise => { - await interaction.deferReply({ - flags: [MessageFlags.Ephemeral] - }); + ), + async (interaction: ChatInputCommandInteraction): Promise => { + await interaction.deferReply({ + flags: [MessageFlags.Ephemeral], + }); - const voice = interaction.options.getString("voice") as Voice; - await setUserVoice(interaction.user.id, voice); + const voice = interaction.options.getString("voice") as Voice; + await setUserVoice(interaction.user.id, voice); - await interaction.editReply("예주의 목소리를 변경했어요!"); - } -) \ No newline at end of file + await interaction.editReply("예주의 목소리를 변경했어요!"); + }, +); diff --git a/packages/bot/commands/skipCurrent.ts b/packages/bot/commands/skipCurrent.ts index 93a44aa..7234a18 100644 --- a/packages/bot/commands/skipCurrent.ts +++ b/packages/bot/commands/skipCurrent.ts @@ -1,25 +1,29 @@ -import { ChatInputCommandInteraction, MessageFlags, SlashCommandBuilder } from "discord.js"; +import { + ChatInputCommandInteraction, + MessageFlags, + SlashCommandBuilder, +} from "discord.js"; import { defineCommand } from "../command"; import { skipCurrentVoice } from "../tts"; export default defineCommand( - new SlashCommandBuilder() - .setName("스킵") - .setDescription("실행중인 보이스를 건너뜁니다"), - async (interaction: ChatInputCommandInteraction): Promise => { - await interaction.deferReply({ - flags: [MessageFlags.Ephemeral] - }); + new SlashCommandBuilder() + .setName("스킵") + .setDescription("실행중인 보이스를 건너뜁니다"), + async (interaction: ChatInputCommandInteraction): Promise => { + await interaction.deferReply({ + flags: [MessageFlags.Ephemeral], + }); - if (!interaction.guild) { - await interaction.editReply("서버에서만 사용할 수 있어요"); - return; - } - - if (await skipCurrentVoice(interaction.guild)) { - await interaction.editReply("스킵 되었어요"); - } else { - await interaction.editReply("실행중인 보이스가 없어요"); - } + if (!interaction.guild) { + await interaction.editReply("서버에서만 사용할 수 있어요"); + return; } -) \ No newline at end of file + + if (await skipCurrentVoice(interaction.guild)) { + await interaction.editReply("스킵 되었어요"); + } else { + await interaction.editReply("실행중인 보이스가 없어요"); + } + }, +); diff --git a/packages/bot/commands/unreadChannel.ts b/packages/bot/commands/unreadChannel.ts index 20da2e3..9f82a3d 100644 --- a/packages/bot/commands/unreadChannel.ts +++ b/packages/bot/commands/unreadChannel.ts @@ -1,36 +1,42 @@ -import { Channel, ChannelType, ChatInputCommandInteraction, MessageFlags, SlashCommandSubcommandBuilder } from "discord.js"; -import { defineCommand, DiscordCommand } from "../command"; -import { insertGuildReadChannel, removeGuildReadChannel } from "../db"; +import { + type Channel, + ChannelType, + ChatInputCommandInteraction, + MessageFlags, + SlashCommandSubcommandBuilder, +} from "discord.js"; +import { defineCommand } from "../command"; +import { removeGuildReadChannel } from "../db"; export default defineCommand( - new SlashCommandSubcommandBuilder() - .setName("읽지마") - .setDescription("예주가 해당 채널을 더이상 읽지 않아요") - .addChannelOption(option => - option - .setName("channel") - .addChannelTypes(ChannelType.GuildText, ChannelType.GuildVoice) - .setDescription("예주가 더이상 읽지 않을 채널이에요") - .setRequired(true) - ), - async (interaction: ChatInputCommandInteraction): Promise => { - await interaction.deferReply({ - flags: [MessageFlags.Ephemeral] - }); + new SlashCommandSubcommandBuilder() + .setName("읽지마") + .setDescription("예주가 해당 채널을 더이상 읽지 않아요") + .addChannelOption((option) => + option + .setName("channel") + .addChannelTypes(ChannelType.GuildText, ChannelType.GuildVoice) + .setDescription("예주가 더이상 읽지 않을 채널이에요") + .setRequired(true), + ), + async (interaction: ChatInputCommandInteraction): Promise => { + await interaction.deferReply({ + flags: [MessageFlags.Ephemeral], + }); - const channel = interaction.options.getChannel("channel") as Channel; - const guildId = interaction.guildId; + const channel = interaction.options.getChannel("channel") as Channel; + const guildId = interaction.guildId; - if (guildId == null) - return await interaction.editReply("알수없는 서버에요!"); + if (guildId == null) + return await interaction.editReply("알수없는 서버에요!"); - try { - await removeGuildReadChannel(guildId, channel.id); - } catch { - return await interaction.editReply("읽지 않는것을 실패했어요 ?ㄴ"); - } + try { + await removeGuildReadChannel(guildId, channel.id); + } catch { + return await interaction.editReply("읽지 않는것을 실패했어요 ?ㄴ"); + } - await interaction.editReply(`예주가 <#${channel.id}> 채널을 읽지않아요!`); - }, - true -) \ No newline at end of file + await interaction.editReply(`예주가 <#${channel.id}> 채널을 읽지않아요!`); + }, + true, +); diff --git a/packages/bot/db.ts b/packages/bot/db.ts index 8487932..afdd7ca 100644 --- a/packages/bot/db.ts +++ b/packages/bot/db.ts @@ -1,149 +1,176 @@ import { prisma } from "../db/prisma"; -import { DiscordUserProfile, DiscordGuildProfile, Voice } from "../db/generated/prisma/client"; +import { + type DiscordUserProfile, + type DiscordGuildProfile, + Voice, +} from "../db/generated/prisma/client"; export function getUserProfile(userId: string): Promise { - return prisma.discordUserProfile.upsert({ - where: { - userId: userId - }, - update: {}, - create: { - userId: userId, - }, - }); + return prisma.discordUserProfile.upsert({ + where: { + userId: userId, + }, + update: {}, + create: { + userId: userId, + }, + }); } -export async function setUserProfile(userId: string, profile: DiscordUserProfile): Promise { - await prisma.discordUserProfile.upsert({ - where: { - userId: userId - }, - update: { - voice: profile.voice, - nya: profile.nya - }, - create: { - userId: userId, - } - }); +export async function setUserProfile( + userId: string, + profile: DiscordUserProfile, +): Promise { + await prisma.discordUserProfile.upsert({ + where: { + userId: userId, + }, + update: { + voice: profile.voice, + nya: profile.nya, + }, + create: { + userId: userId, + }, + }); } export async function setUserNya(userId: string, nya: boolean): Promise { - await prisma.discordUserProfile.upsert({ - where: { - userId: userId - }, - update: { - nya: nya - }, - create: { - userId: userId, - } - }); + await prisma.discordUserProfile.upsert({ + where: { + userId: userId, + }, + update: { + nya: nya, + }, + create: { + userId: userId, + }, + }); } -export async function setUserSupertonicStyle(userId: string, style: string): Promise { - await prisma.discordUserProfile.upsert({ - where: { - userId: userId - }, - update: { - userSupertonicStyle: style - }, - create: { - userId: userId, - userSupertonicStyle: style - } - }); +export async function setUserSupertonicStyle( + userId: string, + style: string, +): Promise { + await prisma.discordUserProfile.upsert({ + where: { + userId: userId, + }, + update: { + userSupertonicStyle: style, + }, + create: { + userId: userId, + userSupertonicStyle: style, + }, + }); } -export async function setUserVoice(userId: string, voice: Voice): Promise { - await prisma.discordUserProfile.upsert({ - where: { - userId: userId - }, - update: { - voice: voice - }, - create: { - userId: userId, - voice: voice - } - }); +export async function setUserVoice( + userId: string, + voice: Voice, +): Promise { + await prisma.discordUserProfile.upsert({ + where: { + userId: userId, + }, + update: { + voice: voice, + }, + create: { + userId: userId, + voice: voice, + }, + }); } -export async function setUserCanTypecast(userId: string, canTypecast: boolean): Promise { - await prisma.discordUserProfile.upsert({ - where: { - userId: userId - }, - update: { - canTypecast: canTypecast - }, - create: { - userId: userId, - } - }); +export async function setUserCanTypecast( + userId: string, + canTypecast: boolean, +): Promise { + await prisma.discordUserProfile.upsert({ + where: { + userId: userId, + }, + update: { + canTypecast: canTypecast, + }, + create: { + userId: userId, + }, + }); } export function getGuildProfile(guildId: string): Promise { - return prisma.discordGuildProfile.upsert({ - where: { guildId: guildId }, - update: {}, - create: { - guildId: guildId, - }, + return prisma.discordGuildProfile.upsert({ + where: { guildId: guildId }, + update: {}, + create: { + guildId: guildId, + }, + }); +} + +export async function hasGuildReadChannel( + guildId: string, + channelId: string, +): Promise { + return ( + (await prisma.discordGuildProfile.findFirst({ + where: { + guildId: guildId, + readChannel: { has: channelId }, + }, + })) != null + ); +} + +export async function removeGuildReadChannel( + guildId: string, + channelId: string, +): Promise { + const guildProfile = await prisma.discordGuildProfile.findUnique({ + where: { + guildId: guildId, + }, + }); + + if (guildProfile) { + await prisma.discordGuildProfile.update({ + where: { + guildId: guildId, + }, + data: { + readChannel: guildProfile.readChannel.filter( + (channel) => channel != channelId, + ), + }, }); + } } -export async function hasGuildReadChannel(guildId: string, channelId: string): Promise { - return ( - await prisma.discordGuildProfile.findFirst({ - where: { - guildId: guildId, - readChannel: { has: channelId } - } - }) - ) != null; -} - -export async function removeGuildReadChannel(guildId: string, channelId: string): Promise { - const guildProfile = await prisma.discordGuildProfile.findUnique({ - where: { - guildId: guildId - }, - }); - - if (guildProfile) { - await prisma.discordGuildProfile.update({ - where: { - guildId: guildId - }, - data: { - readChannel: guildProfile.readChannel.filter(channel => channel != channelId), - }, - }); - } -} - -export async function insertGuildReadChannel(guildId: string, channelId: string): Promise { - await prisma.discordGuildProfile.upsert({ - where: { - guildId: guildId, - NOT: { - readChannel: { - has: channelId, - }, - } - }, - update: { - readChannel: { - push: channelId, - }, - }, - create: { - guildId: guildId, - readChannel: [channelId], - }, - }); +export async function insertGuildReadChannel( + guildId: string, + channelId: string, +): Promise { + await prisma.discordGuildProfile.upsert({ + where: { + guildId: guildId, + NOT: { + readChannel: { + has: channelId, + }, + }, + }, + update: { + readChannel: { + push: channelId, + }, + }, + create: { + guildId: guildId, + readChannel: [channelId], + }, + }); } diff --git a/packages/bot/event.ts b/packages/bot/event.ts index 31adae1..3d67571 100644 --- a/packages/bot/event.ts +++ b/packages/bot/event.ts @@ -1,21 +1,22 @@ -import { ClientEvents } from "discord.js"; +import { type ClientEvents } from "discord.js"; import { join } from "node:path"; -import { requireDirectorySync } from "../utils/requireDirectory"; +import { requireDirectory } from "../utils/requireDirectory"; export interface DiscordEvent { - event: Event - callback: (...args: ClientEvents[Event]) => Promise + event: Event; + callback: (...args: ClientEvents[Event]) => Promise; } export function defineEvent( - event: Event, - callback: (...args: ClientEvents[Event]) => Promise + event: Event, + callback: (...args: ClientEvents[Event]) => Promise, ): DiscordEvent { - return { - event: event, - callback: callback, - } + return { + event: event, + callback: callback, + }; } -export const eventDirectory = join(__dirname, "events"); -export const eventMap = requireDirectorySync>(eventDirectory); +export const eventDirectory = join(import.meta.dirname, "events"); +export const eventMap = + await requireDirectory>(eventDirectory); diff --git a/packages/bot/events/readChannel.ts b/packages/bot/events/readChannel.ts index e271cd6..4d4edbe 100644 --- a/packages/bot/events/readChannel.ts +++ b/packages/bot/events/readChannel.ts @@ -1,72 +1,69 @@ import { getOrCreateVoiceConnection } from "../util"; import { getUserProfile, hasGuildReadChannel } from "../db"; import { defineEvent } from "../event"; -import { playVoice, PlayVoiceOptions } from "../tts"; +import { playVoice, type PlayVoiceOptions } from "../tts"; import { Voice } from "../../db/generated/prisma/enums"; import { SUPERTONIC_DEFAULT_VOICE } from "../../env"; export default defineEvent("messageCreate", async (message) => { - if (message.author.bot) - return; + if (message.author.bot) return; - const guild = message.guild; - if (guild == null) - return; + const guild = message.guild; + if (guild == null) return; - const hasChannel = await hasGuildReadChannel(guild.id, message.channelId); - if (!hasChannel) - return; + const hasChannel = await hasGuildReadChannel(guild.id, message.channelId); + if (!hasChannel) return; - const profile = await getUserProfile(message.author.id); + const profile = await getUserProfile(message.author.id); - let content = message.cleanContent; - let voice: Voice | null = null - let options: PlayVoiceOptions = {}; - let matched: RegExpMatchArray | null = null; - if (content.startsWith("$t ")) { - voice = "TypeCast"; - } else if (content.startsWith("$p ")) { - voice = "Papago"; - } else if (matched = content.match(/^\$s(\S*) /)) { - voice = "Supertonic"; - if (matched[1].length) { - options.supertonicStyleId = matched[1] - } - } else if (content.match(/^\$\s/)) { - return; + let content = message.cleanContent; + let voice: Voice | null = null; + const options: PlayVoiceOptions = {}; + // eslint-disable-next-line no-useless-assignment + let matched: RegExpMatchArray | null = null; + if (content.startsWith("$t ")) { + voice = "TypeCast"; + } else if (content.startsWith("$p ")) { + voice = "Papago"; + } else if ((matched = content.match(/^\$s(\S*) /))) { + voice = "Supertonic"; + if (matched[1]?.length) { + options.supertonicStyleId = matched[1]; + } + } else if (content.match(/^\$\s/)) { + return; + } + + if (profile.userSupertonicStyle.length) { + options.supertonicStyleId ??= profile.userSupertonicStyle; + } + options.supertonicStyleId ??= SUPERTONIC_DEFAULT_VOICE; + + if (voice) { + content = content.replace(/^\$\S+\s+/, ""); + } else { + voice = profile.voice; + } + + try { + if (!(await getOrCreateVoiceConnection(guild))) return; + + if (!guild.members.me?.voice.channel) return; + + if (message.content === "") { + content = + message.attachments.size > 0 + ? `${message.attachments.size} 개의 첨부파일` + : "알수없는 메시지"; + + if (message.attachments.size == 1 && Math.random() < 0.05) { + content = "어이, 유저. 일개의 첨부파일이 뭘 할 수 있지?"; + } } - if (profile.userSupertonicStyle.length) { - options.supertonicStyleId ??= profile.userSupertonicStyle; - } - options.supertonicStyleId ??= SUPERTONIC_DEFAULT_VOICE; - - if (voice) { - content = content.replace(/^\$\S+\s+/, "") - } else { - voice = profile.voice; - } - - try { - if (!await getOrCreateVoiceConnection(guild)) - return; - - if (!guild.members.me?.voice.channel) - return; - - if (message.content === "") { - content = message.attachments.size > 0 - ? `${message.attachments.size} 개의 첨부파일` - : "알수없는 메시지" - - if (message.attachments.size == 1 && Math.random() < 0.05) { - content = "어이, 유저. 일개의 첨부파일이 뭘 할 수 있지?" - } - } - - await playVoice(guild, profile, voice, content, options); - } catch(err) { - message.reply("말이 꼬이네요 ㅜ.ㅜ"); - console.log("playVoice failed. ", err); - } -}) \ No newline at end of file + await playVoice(guild, profile, voice, content, options); + } catch (err) { + message.reply("말이 꼬이네요 ㅜ.ㅜ"); + console.log("playVoice failed. ", err); + } +}); diff --git a/packages/bot/events/useCommand.ts b/packages/bot/events/useCommand.ts index 1b511c6..00b71d2 100644 --- a/packages/bot/events/useCommand.ts +++ b/packages/bot/events/useCommand.ts @@ -1,11 +1,10 @@ -import { commandExecuteNameHashMap, DiscordCommand, DiscordCommandExecute } from "../command"; +import { commandExecuteNameHashMap } from "../command"; import { defineEvent } from "../event"; export default defineEvent("interactionCreate", async (interaction) => { - if (!interaction.isChatInputCommand()) - return; + if (!interaction.isChatInputCommand()) return; - if (commandExecuteNameHashMap[interaction.commandName]) { - await commandExecuteNameHashMap[interaction.commandName](interaction); - } -}) + if (commandExecuteNameHashMap[interaction.commandName]) { + await commandExecuteNameHashMap[interaction.commandName]?.(interaction); + } +}); diff --git a/packages/bot/index.ts b/packages/bot/index.ts index e91c55b..0fb1509 100644 --- a/packages/bot/index.ts +++ b/packages/bot/index.ts @@ -1,5 +1,5 @@ import { Client, Events, GatewayIntentBits, REST, Routes } from "discord.js"; -import { commandExecuteNameHashMap, defineCommands, DiscordCommand } from "./command"; +import { commandExecuteNameHashMap, defineCommands } from "./command"; import { eventMap } from "./event"; import { OutputHandler } from "../utils/outputHandler"; import { APPLICATION_ID } from "../env"; @@ -8,93 +8,92 @@ import { join } from "node:path"; import { cwd } from "node:process"; export class DiscordBot { - rest: REST; - client: Client; + rest: REST; + client: Client; - constructor(token: string) { - this.client = new Client({ - intents: [ - GatewayIntentBits.Guilds, - GatewayIntentBits.GuildMessages, - GatewayIntentBits.GuildVoiceStates, - GatewayIntentBits.MessageContent, - ], - }); - this.rest = new REST({ version: "10" }).setToken(token); - } + constructor(token: string) { + this.client = new Client({ + intents: [ + GatewayIntentBits.Guilds, + GatewayIntentBits.GuildMessages, + GatewayIntentBits.GuildVoiceStates, + GatewayIntentBits.MessageContent, + ], + }); + this.rest = new REST({ version: "10" }).setToken(token); + } - public async registerCommands(): Promise { - try { - if (!this.client.isReady()) { - await this.client.once(Events.ClientReady, () => {}); - } + public async registerCommands(): Promise { + try { + if (!this.client.isReady()) { + await this.client.once(Events.ClientReady, () => {}); + } - const commandsCachePath = join(cwd(), "cache", "commands"); - await mkdir(commandsCachePath, { - recursive: true - }); - let commandsCache: { [key: string]: string } - try { - commandsCache = JSON.parse( - await readFile(join(commandsCachePath, "list.json"), "utf-8") - ); - } catch { - commandsCache = {}; - } - - for (const [command, id] of Object.entries(commandsCache)) { - if (commandExecuteNameHashMap[command]) { - continue; - } - console.log(`sync(delete) command: ${command}(${id})`); - await this.deleteCommand(id); - } - - await this.rest.put( - Routes.applicationCommands(APPLICATION_ID), - { - body: defineCommands.map((command) => command.data.toJSON()), - } - ); - - await writeFile( - join(commandsCachePath, "list.json"), - JSON.stringify( - ( - (await this.rest.get(Routes.applicationCommands(APPLICATION_ID))) as { - name: string, - id: string - }[] - ).reduce>((acc, cur) => { - acc[cur.name] = cur.id; - return acc; - }, {}), - null, - 2 - ) - ); - } catch(err) { - OutputHandler.errorLog("[Command Register Error]", err); - } - } - - public async deleteCommand(commandId: string): Promise { - if (!this.client.isReady()) - throw new Error("Client is not ready"); - - await this.rest.delete( - Routes.applicationCommand(this.client.application.id, commandId) + const commandsCachePath = join(cwd(), "cache", "commands"); + await mkdir(commandsCachePath, { + recursive: true, + }); + let commandsCache: { [key: string]: string }; + try { + commandsCache = JSON.parse( + await readFile(join(commandsCachePath, "list.json"), "utf-8"), ); - } + } catch { + commandsCache = {}; + } - public registerEvents() { - try { - for (let index = 0; index < eventMap.length; index++) { - const event = eventMap[index]; - this.client.on(event.event, event.callback); - } - } catch(err) { - OutputHandler.errorLog("[Event Register Error]", err); + for (const [command, id] of Object.entries(commandsCache)) { + if (commandExecuteNameHashMap[command]) { + continue; } + console.log(`sync(delete) command: ${command}(${id})`); + await this.deleteCommand(id); + } + + await this.rest.put(Routes.applicationCommands(APPLICATION_ID), { + body: defineCommands.map((command) => command.data.toJSON()), + }); + + await writeFile( + join(commandsCachePath, "list.json"), + JSON.stringify( + ( + (await this.rest.get( + Routes.applicationCommands(APPLICATION_ID), + )) as { + name: string; + id: string; + }[] + ).reduce>((acc, cur) => { + acc[cur.name] = cur.id; + return acc; + }, {}), + null, + 2, + ), + ); + } catch (err) { + OutputHandler.errorLog("[Command Register Error]", err); } + } + + public async deleteCommand(commandId: string): Promise { + if (!this.client.isReady()) throw new Error("Client is not ready"); + + await this.rest.delete( + Routes.applicationCommand(this.client.application.id, commandId), + ); + } + + public registerEvents() { + try { + for (let index = 0; index < eventMap.length; index++) { + const event = eventMap[index]; + if (!event) continue; + this.client.on(event.event, event.callback); + } + } catch (err) { + OutputHandler.errorLog("[Event Register Error]", err); + } + } } diff --git a/packages/bot/music.ts b/packages/bot/music.ts index 9cf103a..46c5f59 100644 --- a/packages/bot/music.ts +++ b/packages/bot/music.ts @@ -1,81 +1,83 @@ -import { AudioPlayerStatus, AudioResource, createAudioPlayer, createAudioResource, VoiceConnection } from "@discordjs/voice"; -import { stream as createStream } from "play-dl"; +import { + AudioPlayerStatus, + AudioResource, + createAudioPlayer, + createAudioResource, + VoiceConnection, +} from "@discordjs/voice"; import { Guild } from "discord.js"; import { getOrCreateVoiceConnection } from "./util"; import { OutputHandler } from "../utils/outputHandler"; import play from "play-dl"; namespace InitPlayDl { - let initialized = false; - export async function init() { - if (initialized) - return; + let initialized = false; + export async function init() { + if (initialized) return; - await play.getFreeClientID(); - initialized = true; - } + await play.getFreeClientID(); + initialized = true; + } } class MusicQueue { - private connection: VoiceConnection; - private list: AudioResource[]; - constructor(connection: VoiceConnection) { - this.connection = connection; - this.list = []; - } - public static fromConnection(connection: VoiceConnection): MusicQueue { - return (connection as any).queue ??= new MusicQueue(connection); - } - private play() { - if (!this.list[0]) return; - const player = createAudioPlayer(); - this.connection.subscribe(player); - player.once(AudioPlayerStatus.Idle, this.next.bind(this)); - player.play(this.list[0]); - } - public enqueue(resource: AudioResource) { - this.list.push(resource); - if (this.list.length == 1) { - this.play(); - return; - } - } - public next() { - this.list.shift(); - this.play(); + private connection: VoiceConnection; + private list: AudioResource[]; + constructor(connection: VoiceConnection) { + this.connection = connection; + this.list = []; + } + public static fromConnection(connection: VoiceConnection): MusicQueue { + return ((connection as any).queue ??= new MusicQueue(connection)); + } + private play() { + if (!this.list[0]) return; + const player = createAudioPlayer(); + this.connection.subscribe(player); + player.once(AudioPlayerStatus.Idle, this.next.bind(this)); + player.play(this.list[0]); + } + public enqueue(resource: AudioResource) { + this.list.push(resource); + if (this.list.length == 1) { + this.play(); + return; } + } + public next() { + this.list.shift(); + this.play(); + } } // TODO: 토큰 설정하고 플레이돼게 만들기 export async function playMusic(guild: Guild, url: string) { - try { - const connection = await getOrCreateVoiceConnection(guild); - if (!connection) - throw new Error("Yaeju is not joined VoiceChat"); + try { + const connection = await getOrCreateVoiceConnection(guild); + if (!connection) throw new Error("Yaeju is not joined VoiceChat"); - await InitPlayDl.init(); + await InitPlayDl.init(); - const validation = play.yt_validate(url); + const validation = play.yt_validate(url); - if (validation !== "video" && validation !== "playlist") - throw new Error("Invalid YouTube URL: " + validation); + if (validation !== "video" && validation !== "playlist") + throw new Error("Invalid YouTube URL: " + validation); - const stream = await play.stream(url); - MusicQueue.fromConnection(connection).enqueue( - createAudioResource(stream.stream, { - inputType: stream.type - }) - ); - } catch(err) { - OutputHandler.errorLog("[PlayMusic Error]", err); - throw err; - } + const stream = await play.stream(url); + MusicQueue.fromConnection(connection).enqueue( + createAudioResource(stream.stream, { + inputType: stream.type, + }), + ); + } catch (err) { + OutputHandler.errorLog("[PlayMusic Error]", err); + throw err; + } } export async function skipMusic(guild: Guild) { - const connection = await getOrCreateVoiceConnection(guild); - if (!connection) - throw new Error("Yaeju is not joined VoiceChat"); + const connection = await getOrCreateVoiceConnection(guild); + if (!connection) throw new Error("Yaeju is not joined VoiceChat"); - MusicQueue.fromConnection(connection).next(); + MusicQueue.fromConnection(connection).next(); } diff --git a/packages/bot/tts.ts b/packages/bot/tts.ts index 0f65d5a..1e55912 100644 --- a/packages/bot/tts.ts +++ b/packages/bot/tts.ts @@ -1,130 +1,145 @@ -import { AudioPlayer, AudioPlayerStatus, AudioResource, createAudioPlayer, VoiceConnection } from "@discordjs/voice"; +import { + AudioPlayer, + AudioPlayerStatus, + AudioResource, + createAudioPlayer, + VoiceConnection, +} from "@discordjs/voice"; import { Voice } from "../db/generated/prisma/enums"; import TTSTypecastModel from "../tts/typecast"; import TTSPapagoModel from "../tts/papago"; import { Guild } from "discord.js"; import { getOrCreateVoiceConnection } from "./util"; import TTSModelBase from "../tts"; -import { DiscordUserProfile } from "../db/generated/prisma/client"; +import { type DiscordUserProfile } from "../db/generated/prisma/client"; import { nyaize } from "../utils/nyaize"; import { OutputHandler } from "../utils/outputHandler"; import TTSSupertonicModel from "../tts/supertonic"; class VoiceQueue { - private connection: VoiceConnection; - private list: AudioResource[]; - private currentPlayer?: AudioPlayer; - constructor(connection: VoiceConnection) { - this.connection = connection; - this.list = []; - } - public static fromConnection(connection: VoiceConnection): VoiceQueue { - return (connection as any).queue ??= new VoiceQueue(connection); - } - private play() { - if (!this.list[0]) return; - const player = this.currentPlayer = createAudioPlayer(); - this.connection.subscribe(player); - player.once(AudioPlayerStatus.Idle, this.next.bind(this)); - player.play(this.list[0]); - } - public enqueue(resource: AudioResource) { - this.list.push(resource); - if (this.list.length == 1) { - this.play(); - return; - } - } - public next() { - this.currentPlayer?.removeAllListeners(AudioPlayerStatus.Idle); - this.list.shift(); - this.play(); - } - public hasNext(): boolean { - return !!this.list[0]; + private connection: VoiceConnection; + private list: AudioResource[]; + private currentPlayer?: AudioPlayer; + constructor(connection: VoiceConnection) { + this.connection = connection; + this.list = []; + } + public static fromConnection(connection: VoiceConnection): VoiceQueue { + return ((connection as any).queue ??= new VoiceQueue(connection)); + } + private play() { + if (!this.list[0]) return; + const player = (this.currentPlayer = createAudioPlayer()); + this.connection.subscribe(player); + player.once(AudioPlayerStatus.Idle, this.next.bind(this)); + player.play(this.list[0]); + } + public enqueue(resource: AudioResource) { + this.list.push(resource); + if (this.list.length == 1) { + this.play(); + return; } + } + public next() { + this.currentPlayer?.removeAllListeners(AudioPlayerStatus.Idle); + this.list.shift(); + this.play(); + } + public hasNext(): boolean { + return !!this.list[0]; + } } export type PlayVoiceOptions = { - supertonicStyleId?: string, + supertonicStyleId?: string; }; export async function playVoice( - guild: Guild, - profile: DiscordUserProfile, - voice: Voice, - text: string, - options?: PlayVoiceOptions, + guild: Guild, + profile: DiscordUserProfile, + voice: Voice, + text: string, + options?: PlayVoiceOptions, ) { - if (profile.nya) - text = nyaize(text); + if (profile.nya) text = nyaize(text); - try { - let connection = await getOrCreateVoiceConnection(guild); - if (!connection) - throw new Error("Yaeju is not joined VoiceChat"); - - if (voice == "TypeCast" && !profile.canTypecast) { - throw new Error(`the user ${profile.userId} is can't use typecast voice`); - } - - let voiceBufferList: Buffer[] - if (voice == "TypeCast") { - const content = TTSTypecastModel.instance.ttsify(text); + try { + const connection = await getOrCreateVoiceConnection(guild); + if (!connection) throw new Error("Yaeju is not joined VoiceChat"); - if (!content.length) - throw new Error("Empty content"); - - voiceBufferList = await Promise.all(content.split("\n").map( - (content) => TTSTypecastModel.instance.getMemcachedVoice( - TTSTypecastModel.instance.createRequestId(content) - ) - )); - } else if (voice == "Supertonic") { - const content = TTSSupertonicModel.instance.ttsify(text); - - if (!content.length) - throw new Error("Empty content"); - - voiceBufferList = await Promise.all(content.split("\n").map( - (content) => TTSSupertonicModel.instance.getMemcachedVoice( - TTSSupertonicModel.instance.createRequestId(content, options?.supertonicStyleId) - ) - )); - } else if (voice == "Papago") { - const content = TTSPapagoModel.instance.ttsify(text); - if (!content.length) - throw new Error("Empty content"); - - voiceBufferList = await Promise.all(content.split("\n").map( - (content) => TTSPapagoModel.instance.getMemcachedVoice( - TTSPapagoModel.instance.createRequestId(content) - ) - )); - } else { - throw new Error(`Unknown voice type: ${voice}`); - } - - for (const voiceBuffer of voiceBufferList) { - VoiceQueue.fromConnection(connection).enqueue( - TTSModelBase.bufferToAudioResource(voiceBuffer) - ); - } - } catch(err) { - OutputHandler.errorLog("[PlayVoice Error]", err); - throw new Error(err as any); + if (voice == "TypeCast" && !profile.canTypecast) { + throw new Error(`the user ${profile.userId} is can't use typecast voice`); } + + let voiceBufferList: Buffer[]; + if (voice == "TypeCast") { + const content = TTSTypecastModel.instance.ttsify(text); + + if (!content.length) throw new Error("Empty content"); + + voiceBufferList = await Promise.all( + content + .split("\n") + .map((content) => + TTSTypecastModel.instance.getMemcachedVoice( + TTSTypecastModel.instance.createRequestId(content), + ), + ), + ); + } else if (voice == "Supertonic") { + const content = TTSSupertonicModel.instance.ttsify(text); + + if (!content.length) throw new Error("Empty content"); + + voiceBufferList = await Promise.all( + content + .split("\n") + .map((content) => + TTSSupertonicModel.instance.getMemcachedVoice( + TTSSupertonicModel.instance.createRequestId( + content, + options?.supertonicStyleId, + ), + ), + ), + ); + } else if (voice == "Papago") { + const content = TTSPapagoModel.instance.ttsify(text); + if (!content.length) throw new Error("Empty content"); + + voiceBufferList = await Promise.all( + content + .split("\n") + .map((content) => + TTSPapagoModel.instance.getMemcachedVoice( + TTSPapagoModel.instance.createRequestId(content), + ), + ), + ); + } else { + throw new Error(`Unknown voice type: ${voice}`); + } + + for (const voiceBuffer of voiceBufferList) { + VoiceQueue.fromConnection(connection).enqueue( + TTSModelBase.bufferToAudioResource(voiceBuffer), + ); + } + } catch (err) { + OutputHandler.errorLog("[PlayVoice Error]", err); + throw new Error(err as any, { cause: err }); + } } export async function skipCurrentVoice(guild: Guild): Promise { - let connection = await getOrCreateVoiceConnection(guild); - if (!connection) - throw new Error("YaejuNyang is not joined VoiceChat"); + const connection = await getOrCreateVoiceConnection(guild); + if (!connection) throw new Error("YaejuNyang is not joined VoiceChat"); - const vqueue = VoiceQueue.fromConnection(connection); - if (vqueue.hasNext()) { - vqueue.next(); - return true; - } - return false; + const vqueue = VoiceQueue.fromConnection(connection); + if (vqueue.hasNext()) { + vqueue.next(); + return true; + } + return false; } diff --git a/packages/bot/util.ts b/packages/bot/util.ts index 970e46d..b1f846d 100644 --- a/packages/bot/util.ts +++ b/packages/bot/util.ts @@ -1,25 +1,30 @@ -import { getVoiceConnection as defaultGetVoiceConnection, EndBehaviorType, joinVoiceChannel, VoiceConnection } from "@discordjs/voice"; +import { + getVoiceConnection as defaultGetVoiceConnection, + joinVoiceChannel, + VoiceConnection, +} from "@discordjs/voice"; import { Guild } from "discord.js"; -export async function getOrCreateVoiceConnection(guild: Guild): Promise { - let connection = defaultGetVoiceConnection(guild.id); +export async function getOrCreateVoiceConnection( + guild: Guild, +): Promise { + let connection = defaultGetVoiceConnection(guild.id); - if (!connection) { - if (!guild.members.me?.voice.channel) - return; - - const channel = guild.members.me.voice.channel; - - connection = joinVoiceChannel({ - channelId: channel.id, - guildId: channel.guild.id, - adapterCreator: channel.guild.voiceAdapterCreator, - selfDeaf: false, - selfMute: false, - }); - - await new Promise((resolve) => setTimeout(resolve, 2000)); - } + if (!connection) { + if (!guild.members.me?.voice.channel) return; - return connection; + const channel = guild.members.me.voice.channel; + + connection = joinVoiceChannel({ + channelId: channel.id, + guildId: channel.guild.id, + adapterCreator: channel.guild.voiceAdapterCreator, + selfDeaf: false, + selfMute: false, + }); + + await new Promise((resolve) => setTimeout(resolve, 2000)); + } + + return connection; } diff --git a/packages/db/generated/prisma/client.ts b/packages/db/generated/prisma/client.ts index a585700..c766dea 100644 --- a/packages/db/generated/prisma/client.ts +++ b/packages/db/generated/prisma/client.ts @@ -12,6 +12,8 @@ import * as process from 'node:process' import * as path from 'node:path' +import { fileURLToPath } from 'node:url' +globalThis['__dirname'] = path.dirname(fileURLToPath(import.meta.url)) import * as runtime from "@prisma/client/runtime/client" import * as $Enums from "./enums" diff --git a/packages/db/generated/prisma/internal/class.ts b/packages/db/generated/prisma/internal/class.ts index b53ef69..09f0acb 100644 --- a/packages/db/generated/prisma/internal/class.ts +++ b/packages/db/generated/prisma/internal/class.ts @@ -37,10 +37,10 @@ async function decodeBase64AsWasm(wasmBase64: string): Promise await import("@prisma/client/runtime/query_compiler_fast_bg.postgresql.js"), + getRuntime: async () => await import("@prisma/client/runtime/query_compiler_fast_bg.postgresql.mjs"), getQueryCompilerWasmModule: async () => { - const { wasm } = await import("@prisma/client/runtime/query_compiler_fast_bg.postgresql.wasm-base64.js") + const { wasm } = await import("@prisma/client/runtime/query_compiler_fast_bg.postgresql.wasm-base64.mjs") return await decodeBase64AsWasm(wasm) }, diff --git a/packages/env.ts b/packages/env.ts index 6981863..00bd1d7 100644 --- a/packages/env.ts +++ b/packages/env.ts @@ -1,19 +1,20 @@ -import { config } from "dotenv" +import { config } from "dotenv"; config({ quiet: true }); export const DISCORD_TOKEN = process.env.DISCORD_TOKEN as string; export const APPLICATION_ID = process.env.APPLICATION_ID as string; export const GUILD_ID = process.env.GUILD_ID as string; -export const TYPECAST_TOKENS = (process.env.TYPECAST_TOKEN as string).split(","); +export const TYPECAST_TOKENS = (process.env.TYPECAST_TOKEN as string).split( + ",", +); export const DATABASE_URL = process.env.DATABASE_URL as string; -export const SUPERTONIC_DEFAULT_VOICE = (process.env.SUPERTONIC_DEFAULT_VOICE as string | undefined) ?? "Q1"; -export const SUPERTONIC_STYLE_LIST: { name: string, value: string }[] = (()=>{ - const defaultValue = [ - { name: "여성1", value: "F1" }, - ]; - try { - return JSON.parse(process.env.SUPERTONIC_STYLE_LIST ?? "null") as any ?? defaultValue - } catch {} - return defaultValue; +export const SUPERTONIC_DEFAULT_VOICE = + (process.env.SUPERTONIC_DEFAULT_VOICE as string | undefined) ?? "Q1"; +export const SUPERTONIC_STYLE_LIST: { name: string; value: string }[] = (() => { + return ( + (JSON.parse(process.env.SUPERTONIC_STYLE_LIST ?? "null") as any) ?? [ + { name: "여성1", value: "F1" }, + ] + ); })(); diff --git a/packages/index.ts b/packages/index.ts index 2b08196..d84c619 100644 --- a/packages/index.ts +++ b/packages/index.ts @@ -5,15 +5,19 @@ import { APPLICATION_ID, DISCORD_TOKEN } from "./env"; export const bot = new DiscordBot(DISCORD_TOKEN); bot.client.once("clientReady", async (client) => { - await bot.registerCommands(); - await bot.registerEvents(); + await bot.registerCommands(); + await bot.registerEvents(); - console.log( - "registerCommands: \n| " + - ( - (await client.rest.get(Routes.applicationCommands(APPLICATION_ID))) as any[] - ).map(info => `name: ${info.name} id: ${info.id}`).join("\n| ") - ); + console.log( + "registerCommands: \n| " + + ( + (await client.rest.get( + Routes.applicationCommands(APPLICATION_ID), + )) as any[] + ) + .map((info) => `name: ${info.name} id: ${info.id}`) + .join("\n| "), + ); }); -bot.client.login(DISCORD_TOKEN); \ No newline at end of file +bot.client.login(DISCORD_TOKEN); diff --git a/packages/tts/index.ts b/packages/tts/index.ts index 5aa7223..a3256ef 100644 --- a/packages/tts/index.ts +++ b/packages/tts/index.ts @@ -1,6 +1,10 @@ -import { writeFile, mkdir, stat, readFile } from "fs/promises"; +import { writeFile, mkdir, readFile } from "fs/promises"; import { dirname } from "path"; -import { AudioResource, createAudioResource, StreamType } from "@discordjs/voice"; +import { + AudioResource, + createAudioResource, + StreamType, +} from "@discordjs/voice"; import { Readable } from "stream"; import { createHash } from "node:crypto"; import { join } from "node:path"; @@ -8,88 +12,84 @@ import { existsSync } from "node:fs"; import { saferKorean } from "../utils/saferKorean"; export abstract class TTSModelBase { - public ttsify(input: string): string { - return saferKorean( - input.replace(/:[^:]+:/g, (text: string): string => (TTSModelBase.EMOJI_MAP[text] ?? "이모지")) - ); + public ttsify(input: string): string { + return saferKorean( + input.replace( + /:[^:]+:/g, + (text: string): string => TTSModelBase.EMOJI_MAP[text] ?? "이모지", + ), + ); + } + public abstract createRequestId(text: string): RequestId; + public abstract getVoiceBuffer(id: RequestId): Promise; + public abstract getVoicePath(id: RequestId): string; + + /** + * id로 부터 음성을 생성하여 캐시 파일에 저장합니다 + * 생성된 음성을 반환합니다 + */ + public async createVoice(id: RequestId, audioPath?: string): Promise { + const voiceBuffer = await this.getVoiceBuffer(id); + audioPath ??= this.getVoicePath(id); + const buffer = Buffer.from(voiceBuffer); + + await mkdir(dirname(audioPath), { recursive: true }); + await writeFile(audioPath, buffer); + + return buffer; + } + /** + * id로 부터 파일에 캐싱된 음성을 얻거나 없는 경우 생성합니다 + */ + public async getVoice(id: RequestId, audioPath?: string): Promise { + audioPath ??= this.getVoicePath(id); + + if (existsSync(audioPath)) { + const buffer = await readFile(audioPath); + return buffer; } - public abstract createRequestId(text: string): RequestId - public abstract getVoiceBuffer(id: RequestId): Promise - public abstract getVoicePath(id: RequestId): string - - /** - * id로 부터 음성을 생성하여 캐시 파일에 저장합니다 - * 생성된 음성을 반환합니다 - */ - public async createVoice(id: RequestId, audioPath?: string): Promise { - const voiceBuffer = await this.getVoiceBuffer(id); - audioPath ??= this.getVoicePath(id); - const buffer = Buffer.from(voiceBuffer); - await mkdir(dirname(audioPath), { recursive: true }); - await writeFile(audioPath, buffer); + return this.createVoice(id, audioPath); + } + /** + * id로 부터 메모리에 캐싱된 음성을 얻거나, 파일에 캐싱된 + * 음성을 얻거나, 없는 경우 생성합니다 + */ + protected abstract cachedVoice: Map>; + public async getMemcachedVoice(id: RequestId): Promise { + const path = this.getVoicePath(id); - return buffer; + const cached = this.cachedVoice.get(path); + if (cached) { + return cached; } - /** - * id로 부터 파일에 캐싱된 음성을 얻거나 없는 경우 생성합니다 - */ - public async getVoice(id: RequestId, audioPath?: string): Promise { - audioPath ??= this.getVoicePath(id); - if (existsSync(audioPath)) { - const buffer = await readFile(audioPath); - return buffer; - } - - return this.createVoice(id, audioPath); - } - /** - * id로 부터 메모리에 캐싱된 음성을 얻거나, 파일에 캐싱된 - * 음성을 얻거나, 없는 경우 생성합니다 - */ - protected abstract cachedVoice: Map> - public async getMemcachedVoice(id: RequestId): Promise { - const path = this.getVoicePath(id); - - const cached = this.cachedVoice.get(path); - if (cached) { - return cached; - } - - const waitter = this.getVoice(id); - this.cachedVoice.set(path, waitter); - setTimeout( - () => this.cachedVoice.delete(path), - TTSModelBase.MemCacheTTL - ); - return await waitter; - } + const waitter = this.getVoice(id); + this.cachedVoice.set(path, waitter); + setTimeout(() => this.cachedVoice.delete(path), TTSModelBase.MemCacheTTL); + return await waitter; + } } export namespace TTSModelBase { - export const EMOJI_MAP: { [key: string]: string } = { - ":heart:": "하트", - ":huck:": "헉헉!", - ":star:": "초롱초롱!" - } - export const AudioCachePath = join( - process.cwd(), - "cache", - "audio", - ); - export function bufferToAudioResource(buf: Buffer): AudioResource { - const stream = Readable.from(buf); - const resource = createAudioResource(stream, { - inlineVolume: true, - inputType: StreamType.Arbitrary, - }); + export const EMOJI_MAP: { [key: string]: string } = { + ":heart:": "하트", + ":huck:": "헉헉!", + ":star:": "초롱초롱!", + }; + export const AudioCachePath = join(process.cwd(), "cache", "audio"); + export function bufferToAudioResource(buf: Buffer): AudioResource { + const stream = Readable.from(buf); + const resource = createAudioResource(stream, { + inlineVolume: true, + inputType: StreamType.Arbitrary, + }); - resource.volume?.setVolume(0.3); - return resource; - } - export function hashAudioFile(audio: string, suffix: string = ""): string { - return createHash("md5").update(audio).digest("hex") + suffix + ".mp3"; - } - export const MemCacheTTL = 60 * 60 * 1000 + resource.volume?.setVolume(0.3); + return resource; + } + export function hashAudioFile(audio: string, suffix: string = ""): string { + return createHash("md5").update(audio).digest("hex") + suffix + ".mp3"; + } + export const MemCacheTTL = 60 * 60 * 1000; } export default TTSModelBase; diff --git a/packages/tts/papago.ts b/packages/tts/papago.ts index 92803ca..4f977f7 100644 --- a/packages/tts/papago.ts +++ b/packages/tts/papago.ts @@ -4,101 +4,116 @@ import fetch from "../utils/fetch"; import TTSModelBase from "."; export class TTSPapagoModel extends TTSModelBase { - protected cachedVoice: Map> - constructor() { - super() - this.cachedVoice = new Map(); - } - ttsify(input: string): string { - return super.ttsify(input) - } - public getVoicePath(id: TTSPapagoModel.RequestId): string { - const audioFileName = TTSModelBase.hashAudioFile(id.text, `.${id.speaker}.${id.speed.replace(/\-/g, "_")}`); - const audioPath = join( - TTSPapagoModel.PapagoAudioCachePath, - audioFileName - ); - return audioPath; - } - async getVoiceBuffer(id: TTSPapagoModel.RequestId, voiceId?: string): Promise { - voiceId ??= await TTSPapagoModel.getVoiceId(id) - const response = await fetch(`https://papago.naver.com/apis/tts/${voiceId}`); - return await response.arrayBuffer(); - } - createRequestId(text: string, speaker?: string, speed?: string): TTSPapagoModel.RequestId { - return { - text, - speed: speed ?? "-1", - speaker: speaker ?? "kyuri", - }; - } + protected cachedVoice: Map>; + constructor() { + super(); + this.cachedVoice = new Map(); + } + ttsify(input: string): string { + return super.ttsify(input); + } + public getVoicePath(id: TTSPapagoModel.RequestId): string { + const audioFileName = TTSModelBase.hashAudioFile( + id.text, + `.${id.speaker}.${id.speed.replace(/-/g, "_")}`, + ); + const audioPath = join(TTSPapagoModel.PapagoAudioCachePath, audioFileName); + return audioPath; + } + async getVoiceBuffer( + id: TTSPapagoModel.RequestId, + voiceId?: string, + ): Promise { + voiceId ??= await TTSPapagoModel.getVoiceId(id); + const response = await fetch( + `https://papago.naver.com/apis/tts/${voiceId}`, + ); + return await response.arrayBuffer(); + } + createRequestId( + text: string, + speaker?: string, + speed?: string, + ): TTSPapagoModel.RequestId { + return { + text, + speed: speed ?? "-1", + speaker: speaker ?? "kyuri", + }; + } } export namespace TTSPapagoModel { - export const instance = new TTSPapagoModel(); - export type RequestId = { - speaker: string; - speed: string; - text: string; + export const instance = new TTSPapagoModel(); + export type RequestId = { + speaker: string; + speed: string; + text: string; + }; + export const GenerateTokenKey = "v1.9.3_3bdf0438a8"; + export function hmacMD5(key: string, plaintext: string) { + const hmac = createHmac("md5", key); + const data = hmac.update(plaintext); + + return data.digest("base64"); + } + export function generateToken(time: number) { + const e = "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (e) => { + const t = ((time + 16 * Math.random()) % 16) | 0; + return ( + (time = Math.floor(time / 16)), + ("x" === e ? t : (3 & t) | 8).toString(16) + ); + }); + + const plain = `${e}\n${"https://papago.naver.com/apis/tts/makeID"}\n${time}`; + + return `PPG ${e}:${hmacMD5(GenerateTokenKey, plain)}`; + } + export async function getVoiceId(id: RequestId): Promise { + const input = { + alpha: "0", + pitch: "0", + speaker: id.speaker, + speed: id.speed, + text: id.text, }; - export const GenerateTokenKey = "v1.9.3_3bdf0438a8"; - export function hmacMD5(key: string, plaintext: string) { - const hmac = createHmac("md5", key); - const data = hmac.update(plaintext); - return data.digest("base64"); + const time = new Date().getTime(); + const token = TTSPapagoModel.generateToken(time); + + const reqbody = new URLSearchParams(Object.entries(input)).toString(); + const response = await fetch("https://papago.naver.com/apis/tts/makeID", { + headers: { + "User-Agent": + "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:121.0) Gecko/20100101 Firefox/121.0", + Accept: "application/json", + "Accept-Language": "en", + "Sec-GPC": "1", + "Sec-Fetch-Dest": "empty", + "Sec-Fetch-Mode": "no-cors", + "Sec-Fetch-Site": "same-origin", + "Content-Type": "application/x-www-form-urlencoded; charset=UTF-8", + Authorization: token, + Timestamp: time.toString(), + Pragma: "no-cache", + "Cache-Control": "no-cache", + }, + referrer: "https://papago.naver.com/", + body: reqbody, + method: "POST", + }); + + if (!response.ok) { + throw new Error( + `TTS makeID request failed: ${response.status}: ${await response.text()}`, + ); } - export function generateToken(time: number) { - const e = "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (e) => { - var t = (time + 16 * Math.random()) % 16 | 0; - return (time = Math.floor(time / 16)), ("x" === e ? t : (3 & t) | 8).toString(16); - }); - const plain = `${e}\n${"https://papago.naver.com/apis/tts/makeID"}\n${time}`; - - return `PPG ${e}:${hmacMD5(GenerateTokenKey, plain)}`; - } - export async function getVoiceId(id: RequestId): Promise { - const input = { - alpha: "0", - pitch: "0", - speaker: id.speaker, - speed: id.speed, - text: id.text, - }; - - const time = new Date().getTime(); - const token = TTSPapagoModel.generateToken(time); - - const reqbody = new URLSearchParams(Object.entries(input)).toString(); - const response = await fetch("https://papago.naver.com/apis/tts/makeID", { - headers: { - "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:121.0) Gecko/20100101 Firefox/121.0", - Accept: "application/json", - "Accept-Language": "en", - "Sec-GPC": "1", - "Sec-Fetch-Dest": "empty", - "Sec-Fetch-Mode": "no-cors", - "Sec-Fetch-Site": "same-origin", - "Content-Type": "application/x-www-form-urlencoded; charset=UTF-8", - Authorization: token, - Timestamp: time.toString(), - Pragma: "no-cache", - "Cache-Control": "no-cache", - }, - referrer: "https://papago.naver.com/", - body: reqbody, - method: "POST", - }); - - if (!response.ok) { - throw new Error(`TTS makeID request failed: ${response.status}: ${await response.text()}`); - } - - return ((await response.json()) as any).id; - } - export const PapagoAudioCachePath = join( - TTSModelBase.AudioCachePath, - "papago" - ); + return ((await response.json()) as any).id; + } + export const PapagoAudioCachePath = join( + TTSModelBase.AudioCachePath, + "papago", + ); } export default TTSPapagoModel; diff --git a/packages/tts/supertonic.ts b/packages/tts/supertonic.ts index 28a8663..9430e67 100644 --- a/packages/tts/supertonic.ts +++ b/packages/tts/supertonic.ts @@ -3,60 +3,67 @@ import fetch from "../utils/fetch"; import TTSModelBase from "."; export class TTSSupertonicModel extends TTSModelBase { - protected override cachedVoice: Map> - constructor() { - super() - this.cachedVoice = new Map(); - } - override ttsify(input: string): string { - return super.ttsify(input); - } - private async getSupertonicResponse(voiceId: TTSSupertonicModel.RequestId) { - const payload = { - text: voiceId.text, - lang: "ko", - style_id: voiceId.styleId - }; + protected override cachedVoice: Map>; + constructor() { + super(); + this.cachedVoice = new Map(); + } + override ttsify(input: string): string { + return super.ttsify(input); + } + private async getSupertonicResponse(voiceId: TTSSupertonicModel.RequestId) { + const payload = { + text: voiceId.text, + lang: "ko", + style_id: voiceId.styleId, + }; - if (!process.env.SUPERTONIC_API_URL) { - throw Error("process.env.SUPERTONIC_API_URL not set"); - } + if (!process.env.SUPERTONIC_API_URL) { + throw Error("process.env.SUPERTONIC_API_URL not set"); + } - return await fetch(process.env.SUPERTONIC_API_URL, { - method: "POST", - headers: { - "Content-Type": "application/json" - }, - body: JSON.stringify(payload) - }); - } - async getVoiceBuffer(voiceId: TTSSupertonicModel.RequestId): Promise { - let response: Response | undefined; + return await fetch(process.env.SUPERTONIC_API_URL, { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify(payload), + }); + } + async getVoiceBuffer( + voiceId: TTSSupertonicModel.RequestId, + ): Promise { + const response: Response | undefined = (await this.getSupertonicResponse( + voiceId, + )) as Response; + if (response.ok) return await response.arrayBuffer(); - response = await this.getSupertonicResponse(voiceId) as Response; - if (response.ok) - return await response.arrayBuffer(); - - throw new Error(`invalid supertonic response ${await response.text()}`); - } - public getVoicePath(id: TTSSupertonicModel.RequestId): string { - const audioFileName = TTSModelBase.hashAudioFile(id.text + id.styleId); - const audioPath = join( - TTSSupertonicModel.SupertonicAudioCachePath, - audioFileName - ); - return audioPath; - } - public createRequestId(text: string, styleId?: string): TTSSupertonicModel.RequestId { - return { - text, - styleId: styleId ?? "F1" - }; - } + throw new Error(`invalid supertonic response ${await response.text()}`); + } + public getVoicePath(id: TTSSupertonicModel.RequestId): string { + const audioFileName = TTSModelBase.hashAudioFile(id.text + id.styleId); + const audioPath = join( + TTSSupertonicModel.SupertonicAudioCachePath, + audioFileName, + ); + return audioPath; + } + public createRequestId( + text: string, + styleId?: string, + ): TTSSupertonicModel.RequestId { + return { + text, + styleId: styleId ?? "F1", + }; + } } export namespace TTSSupertonicModel { - export const instance = new TTSSupertonicModel(); - export type RequestId = { text: string, styleId: string }; - export const SupertonicAudioCachePath = join(TTSModelBase.AudioCachePath, "supertonic"); + export const instance = new TTSSupertonicModel(); + export type RequestId = { text: string; styleId: string }; + export const SupertonicAudioCachePath = join( + TTSModelBase.AudioCachePath, + "supertonic", + ); } export default TTSSupertonicModel; diff --git a/packages/tts/typecast.ts b/packages/tts/typecast.ts index d7307b8..668b607 100644 --- a/packages/tts/typecast.ts +++ b/packages/tts/typecast.ts @@ -6,89 +6,108 @@ import { readFileSync, writeFileSync } from "fs"; import { cwd } from "process"; export class TTSTypecastModel extends TTSModelBase { - protected cachedVoice: Map> - private lastUseApiKeyPath: string - constructor() { - super() - this.cachedVoice = new Map(); - this.lastUseApiKeyPath = join(cwd(), "cache", "typecast", "lastUseApiToken"); - } - ttsify(input: string): string { - return super.ttsify( - input - .replace(/ㅜㅜ/g, "눙물") - .replace(/빵/g, "빵 크크") - .replace(/[\?]+ *ㄴ/g, "물음표ㄴ") - ) - } - private async getTypecastResponse(apiKey: string, voiceId: TTSTypecastModel.RequestId) { - const payload = { - text: voiceId.text, - model: "ssfm-v21", - voice_id: voiceId.voiceId, - language: "kor", - prompt: { - emotion_preset: "happy", // Options: normal, happy, sad, angry, tonemid, toneup - emotion_intensity: 1 // Range: 0.0 to 2.0 - }, - output: { - volume: 45, // Range: 0 to 200 - audio_pitch: 1, // Range: -12 to +12 semitones - audio_tempo: 1, // Range: 0.5x to 2.0x - audio_format: "mp3" // Options: wav, mp3 - }, - seed: 22 // For reproducible results - }; + protected cachedVoice: Map>; + private lastUseApiKeyPath: string; + constructor() { + super(); + this.cachedVoice = new Map(); + this.lastUseApiKeyPath = join( + cwd(), + "cache", + "typecast", + "lastUseApiToken", + ); + } + ttsify(input: string): string { + return super.ttsify( + input + .replace(/ㅜㅜ/g, "눙물") + .replace(/빵/g, "빵 크크") + .replace(/[?]+ *ㄴ/g, "물음표ㄴ"), + ); + } + private async getTypecastResponse( + apiKey: string, + voiceId: TTSTypecastModel.RequestId, + ) { + const payload = { + text: voiceId.text, + model: "ssfm-v21", + voice_id: voiceId.voiceId, + language: "kor", + prompt: { + emotion_preset: "happy", // Options: normal, happy, sad, angry, tonemid, toneup + emotion_intensity: 1, // Range: 0.0 to 2.0 + }, + output: { + volume: 45, // Range: 0 to 200 + audio_pitch: 1, // Range: -12 to +12 semitones + audio_tempo: 1, // Range: 0.5x to 2.0x + audio_format: "mp3", // Options: wav, mp3 + }, + seed: 22, // For reproducible results + }; - return await fetch(TTSTypecastModel.TypecastApiUrl, { - method: "POST", - headers: { - "X-API-KEY": apiKey, - "Content-Type": "application/json" - }, - body: JSON.stringify(payload) - }); - } - async getVoiceBuffer(voiceId: TTSTypecastModel.RequestId): Promise { - let response: Response | undefined; + return await fetch(TTSTypecastModel.TypecastApiUrl, { + method: "POST", + headers: { + "X-API-KEY": apiKey, + "Content-Type": "application/json", + }, + body: JSON.stringify(payload), + }); + } + async getVoiceBuffer( + voiceId: TTSTypecastModel.RequestId, + ): Promise { + let response: Response | undefined; - for (let i = 0; i < TYPECAST_TOKENS.length; i++) { - response = await this.getTypecastResponse(readFileSync(this.lastUseApiKeyPath, "utf-8"), voiceId) as Response; + for (let i = 0; i < TYPECAST_TOKENS.length; i++) { + response = (await this.getTypecastResponse( + readFileSync(this.lastUseApiKeyPath, "utf-8"), + voiceId, + )) as Response; - if (response.ok) - return await response.arrayBuffer();; + if (response.ok) return await response.arrayBuffer(); - if (response.status === 402) { - writeFileSync(this.lastUseApiKeyPath, TYPECAST_TOKENS[i]); - } else { - throw new Error(`TTS makeID request failed: ${response.status}: ${await response.text()}`); - } - - } - - throw new Error("Typecast Api use all credit"); - } - public getVoicePath(id: TTSTypecastModel.RequestId): string { - const audioFileName = TTSModelBase.hashAudioFile(id.text); - const audioPath = join( - TTSTypecastModel.TypecastAudioCachePath, - id.voiceId, - audioFileName + if (response.status === 402) { + writeFileSync(this.lastUseApiKeyPath, TYPECAST_TOKENS[i]!); + } else { + throw new Error( + `TTS makeID request failed: ${response.status}: ${await response.text()}`, ); - return audioPath; - } - public createRequestId(text: string, voiceId?: string): TTSTypecastModel.RequestId { - return { - text, - voiceId: voiceId ?? TTSTypecastModel.DefaultVoiceId, - }; + } } + + throw new Error("Typecast Api use all credit"); + } + public getVoicePath(id: TTSTypecastModel.RequestId): string { + const audioFileName = TTSModelBase.hashAudioFile(id.text); + const audioPath = join( + TTSTypecastModel.TypecastAudioCachePath, + id.voiceId, + audioFileName, + ); + return audioPath; + } + public createRequestId( + text: string, + voiceId?: string, + ): TTSTypecastModel.RequestId { + return { + text, + voiceId: voiceId ?? TTSTypecastModel.DefaultVoiceId, + }; + } } export namespace TTSTypecastModel { - export const instance = new TTSTypecastModel(); - export type RequestId = { text: string, voiceId: string }; - export const TypecastAudioCachePath = join(TTSModelBase.AudioCachePath, "typecast"); - export const TypecastApiUrl = "https://api.typecast.ai/v1/text-to-speech"; - export const DefaultVoiceId = "tc_6731b292d944a485bc406efb"; + export const instance = new TTSTypecastModel(); + export type RequestId = { text: string; voiceId: string }; + export const TypecastAudioCachePath = join( + TTSModelBase.AudioCachePath, + "typecast", + ); + export const TypecastApiUrl = "https://api.typecast.ai/v1/text-to-speech"; + export const DefaultVoiceId = "tc_6731b292d944a485bc406efb"; } export default TTSTypecastModel; diff --git a/packages/utils/callingNumberKorean.ts b/packages/utils/callingNumberKorean.ts index 75adf31..33c36cf 100644 --- a/packages/utils/callingNumberKorean.ts +++ b/packages/utils/callingNumberKorean.ts @@ -1,29 +1,28 @@ export default class CallingNumberKorean { - // 개, 살 이 붙는 경우 발음법 - static SecondDigit = [ - "", "열", "스물", "서른", "마흔", "쉰", - "예순", "일흔", "여든", "아흔", - ] - static FirstDigit = [ - "", "한", "두", "세", "네", "다섯", - "여섯", "일곱", "여덟", "아홉", "열", - ] - static canConvert(num: number): boolean { - return num < 100 && num >= 0 && Number.isInteger(num) + // 개, 살 이 붙는 경우 발음법 + // prettier-ignore + static SecondDigit = [ + "", "열", "스물", "서른", "마흔", "쉰", + "예순", "일흔", "여든", "아흔", + ]; + // prettier-ignore + static FirstDigit = [ + "", "한", "두", "세", "네", "다섯", + "여섯", "일곱", "여덟", "아홉", "열", + ]; + static canConvert(num: number): boolean { + return num < 100 && num >= 0 && Number.isInteger(num); + } + static convert(num: number): string { + const firstDigit = num % 10; + const secondDigit = Math.floor(num / 10); + + let result = this.SecondDigit[secondDigit]! + this.FirstDigit[firstDigit]!; + + if (!result.length) { + result = "영"; } - static convert(num: number): string { - const firstDigit = num % 10; - const secondDigit = Math.floor(num / 10); - let result = ( - this.SecondDigit[secondDigit] - + this.FirstDigit[firstDigit] - ); - - if (!result.length) { - result = "영" - } - - return result; - } + return result; + } } diff --git a/packages/utils/emoji-descriptions.json b/packages/utils/emoji-descriptions.json new file mode 100644 index 0000000..a5906c2 --- /dev/null +++ b/packages/utils/emoji-descriptions.json @@ -0,0 +1,1318 @@ +{ + "😀": "웃는 얼굴", + "😃": "큰 눈을 가진 웃는 얼굴", + "😄": "웃는 눈으로 웃는 얼굴", + "😁": "웃는 눈으로 환하게 웃는 얼굴", + "😆": "찡그린 얼굴", + "😅": "땀으로 웃는 얼굴", + "🤣": "바닥에 구르며 웃다", + "😂": "기쁨의 눈물을 흘리며 얼굴을 맞대다", + "🙂": "살짝 웃는 얼굴", + "🙃": "거꾸로 된 얼굴", + "🫠": "녹는 얼굴", + "😉": "윙크하는 얼굴", + "😊": "웃는 눈으로 미소 짓는 얼굴", + "😇": "후광으로 미소 짓는 얼굴", + "🥰": "애정하는 얼굴", + "😍": "하트 눈으로 웃는 얼굴", + "🤩": "눈에 별이 빛나는 얼굴", + "😘": "키스를 날리는 얼굴", + "😗": "키스하는 얼굴", + "😚": "눈을 감고 키스하는 얼굴", + "😙": "웃는 눈으로 키스하는 얼굴", + "🥲": "눈물 흘리며 웃는 얼굴", + "😋": "음식을 음미하는 얼굴", + "😛": "얼굴에 혀를 내밀다", + "😜": "혀로 윙크하는 얼굴", + "🤪": "엉뚱한 얼굴", + "😝": "혀를 내밀고 눈을 가늘게 뜨고 있는 얼굴", + "🤑": "돈 입 얼굴", + "🤗": "두 손을 벌리고 웃는 얼굴", + "🤭": "입에 손을 대고 얼굴", + "🫢": "눈을 뜨고 입에 손을 대고 있는 얼굴", + "🫣": "엿보는 눈으로 얼굴", + "🤫": "조용히 하는 얼굴", + "🤔": "생각하는 얼굴", + "🫡": "경례하는 얼굴", + "🤐": "지퍼 입 얼굴", + "🤨": "눈썹을 치켜올린 얼굴", + "😐": "중립 얼굴", + "😑": "무표정한 얼굴", + "😶": "입 없는 얼굴", + "🫥": "점선 얼굴", + "😏": "웃는 얼굴", + "😒": "재미없는 얼굴", + "🙄": "눈을 굴리는 얼굴", + "😬": "찡그린 얼굴", + "🤥": "거짓말하는 얼굴", + "🫨": "흔들리는 얼굴", + "😌": "안도한 얼굴", + "😔": "잠겨있는 얼굴", + "😪": "졸린 얼굴", + "🤤": "침 흘리는 얼굴", + "😴": "잠자는 얼굴", + "😷": "의료용 마스크를 쓴 얼굴", + "🤒": "온도계를 들고 있는 얼굴", + "🤕": "머리에 붕대를 감은 얼굴", + "🤢": "메스꺼운 얼굴", + "🤮": "구토하는 얼굴", + "🤧": "재채기하는 얼굴", + "🥵": "뜨거운 얼굴", + "🥶": "차가운 얼굴", + "🥴": "멍한 얼굴", + "😵": "눈을 가린 얼굴", + "🤯": "폭발하는 머리", + "🤠": "카우보이 모자 얼굴", + "🥳": "파티하는 얼굴", + "🥸": "위장한 얼굴", + "😎": "선글라스를 끼고 웃는 얼굴", + "🤓": "괴상한 얼굴", + "🧐": "단안경을 쓴 얼굴", + "😕": "혼란스러운 얼굴", + "🫤": "대각선 입으로 얼굴", + "😟": "걱정스러운 얼굴", + "🙁": "살짝 찡그린 얼굴", + "😮": "입을 벌린 얼굴", + "😯": "숨죽인 얼굴", + "😲": "놀란 얼굴", + "😳": "붉어진 얼굴", + "🥺": "애원하는 얼굴", + "🥹": "눈물을 참는 얼굴", + "😦": "입을 벌리고 찡그린 얼굴", + "😧": "괴로운 얼굴", + "😨": "두려운 얼굴", + "😰": "땀에 젖은 불안한 얼굴", + "😥": "슬프지만 안심하는 얼굴", + "😢": "우는 얼굴", + "😭": "크게 우는 얼굴", + "😱": "두려움에 비명을 지르는 얼굴", + "😖": "당황한 얼굴", + "😣": "인내하는 얼굴", + "😞": "실망한 얼굴", + "😓": "땀에 젖은 얼굴", + "😩": "지친 얼굴", + "😫": "피곤한 얼굴", + "🥱": "하품하는 얼굴", + "😤": "코에서 김이 나는 얼굴", + "😡": "분노한 얼굴", + "😠": "화난 얼굴", + "🤬": "입에 상징이 있는 얼굴", + "😈": "뿔이 달린 웃는 얼굴", + "👿": "뿔이 달린 화난 얼굴", + "💀": "두개골", + "💩": "똥 더미", + "🤡": "광대 얼굴", + "👹": "오우거", + "👺": "고블린", + "👻": "귀신", + "👽": "외계인", + "👾": "외계 괴물", + "🤖": "로봇", + "😺": "웃는 고양이", + "😸": "웃는 눈으로 웃는 고양이", + "😹": "기쁨의 눈물을 흘리는 고양이", + "😻": "하트눈을 가지고 웃는 고양이", + "😼": "쓴웃음을 짓는 고양이", + "😽": "키스를 날리는 고양이", + "🙀": "지친 고양이", + "😿": "우는 고양이", + "😾": "삐죽삐죽 고양이", + "🙈": "눈에 보이지 않는 원숭이", + "🙉": "악의를 듣지 못하는 원숭이", + "🙊": "말을 하지 못하는 원숭이", + "💌": "연애 편지", + "💘": "화살표가 있는 하트", + "💝": "리본이 달린 하트", + "💖": "반짝이는 하트", + "💗": "성장하는 하트", + "💓": "성뛰는 하트", + "💞": "회전하는 하트", + "💕": "두개의 하트", + "💟": "하트 장식", + "💔": "깨진 하트", + "🩷": "핑크 하트", + "🧡": "오렌지 하트", + "💛": "노란 하트", + "💚": "녹색 하트", + "💙": "푸른 하트", + "🩵": "하늘색 하트", + "💜": "보라색 하트", + "🤎": "갈색 하트", + "🖤": "흑색 하트", + "🩶": "회색 하트", + "🤍": "흰색 하트", + "💋": "키스마크", + "💯": "백점 포인트", + "💢": "분노의 상징", + "💥": "충돌", + "💫": "어지러운", + "💦": "땀방울", + "💨": "돌진해 가다", + "💬": "말풍선", + "💭": "생각 풍선", + "💤": "ZZZ", + "👋": "손을 흔들며", + "🤚": "손등을 들었다", + "🖐": "손가락이 벌어진 손", + "✋": "손을 들었다", + "🖖": "벌컨의 경례", + "🫱": "오른쪽 손", + "🫲": "왼쪽 손", + "🫳": "손바닥을 아래로 한 손", + "🫴": "손바닥을 위로 한 손", + "🫷": "왼쪽으로 미는 손", + "🫸": "오른쪽으로 미는 손", + "👌": "알았어 손", + "🤌": "끼인 손가락", + "🤏": "꼬집는 손", + "✌": "승리의 손", + "🤞": "교차 손가락", + "🫰": "검지와 엄지손가락을 교차한 손", + "🤟": "사랑해 몸짓", + "🤘": "뿔의 표시", + "🤙": "나한테 손 좀 불러줘", + "👈": "왼쪽을 가리키는 백핸드 인덱스", + "👉": "오른쪽을 가리키는 백핸드 인덱스", + "👆": "위쪽을 가리키는 백핸드 인덱스", + "🖕": "가운데 손가락", + "👇": "아래쪽을 가리키는 백핸드 인덱스", + "☝": "위를 가리키는 지수", + "🫵": "뷰어를 가리키는 인덱스", + "👍": "엄지손가락", + "👎": "거절하다", + "✊": "주먹을 들었다", + "👊": "다가오는 주먹", + "🤛": "왼쪽을 향한 주먹", + "🤜": "오른쪽을 향한 주먹", + "👏": "박수를 치다", + "🙌": "손을 들다", + "🫶": "심장 손", + "👐": "손을 벌리다", + "🤲": "손바닥을 함께 위로", + "🤝": "악수", + "🙏": "접힌 손", + "✍": "손으로 쓰는", + "💅": "매니큐어", + "🤳": "셀카", + "💪": "굴곡된 팔뚝", + "🦾": "기계 팔", + "🦿": "기계 다리", + "🦵": "다리", + "🦶": "발", + "👂": "귀", + "🦻": "보청기를 착용한 귀", + "👃": "코", + "🧠": "뇌", + "🫀": "해부학적 심장", + "🫁": "폐", + "🦷": "치아", + "🦴": "뼈", + "👀": "눈", + "👅": "혀", + "👄": "입", + "🫦": "입술을 깨물다", + "👶": "아기", + "🧒": "어린이", + "👦": "소년", + "👧": "소녀", + "🧑": "사람", + "👱": "사람: 금발머리", + "👨": "남성", + "🧔": "사람: 수염", + "👩": "여성", + "🧓": "노인", + "👴": "늙은 남자", + "👵": "늙은 여자", + "🙍": "눈살을 찌푸리는 사람", + "🙎": "삐죽삐죽한 사람", + "🙅": "아니오라고 몸짓하는 사람", + "🙆": "괜찮다는 몸짓을 하는 사람", + "💁": "손을 기울이는 사람", + "🙋": "손을 드는 사람", + "🧏": "청각 장애인", + "🙇": "절하는 사람", + "🤦": "얼굴을 찡그린 사람", + "🤷": "어깨를 으쓱하는 사람", + "👮": "경찰관", + "🕵": "형사", + "💂": "경비원", + "🥷": "닌자", + "👷": "건설 노동자", + "🫅": "왕관을 쓴 사람", + "🤴": "왕자", + "👸": "공주", + "👳": "터번을 쓴 사람", + "👲": "해골모자를 쓴 사람", + "🧕": "머리스카프를 쓴 여자", + "🤵": "턱시도 입은 사람", + "👰": "베일을 쓴 사람", + "🤰": "임산부", + "🫃": "임부", + "🫄": "임신한 사람", + "🤱": "모유 수유", + "👼": "아기천사", + "🎅": "산타클로스", + "🤶": "미세스 클로스", + "🦸": "슈퍼히어로", + "🦹": "슈퍼빌런", + "🧙": "마술사", + "🧚": "요정", + "🧛": "뱀파이어", + "🧜": "인어", + "🧝": "꼬마 요정", + "🧞": "요정", + "🧟": "좀비", + "🧌": "트롤", + "💆": "마사지 받는 사람", + "💇": "머리 자르는 사람", + "🚶": "걷는 사람", + "🧍": "서있는 사람", + "🧎": "무릎을 꿇고 있는 사람", + "🏃": "달리는 사람", + "💃": "춤추는 여자", + "🕺": "춤추는 남자", + "🕴": "공중에 떠 있는 정장 입은 사람", + "👯": "토끼 귀를 가진 사람들", + "🧖": "증기 방에 있는 남자", + "🧗": "등산하는 사람", + "🤺": "펜싱하는 사람", + "🏇": "경마", + "🏂": "스노보더", + "🏌": "골프를 치는 사람", + "🏄": "서핑하는 사람", + "🚣": "노 젓는 배", + "🏊": "수영하는 사람", + "⛹": "공을 튀는 사람", + "🏋": "역기를 드는 사람", + "🚴": "자전거를 타는 사람", + "🚵": "산악 자전거를 타는 사람", + "🤸": "수레바퀴를 돌리는 사람", + "🤼": "레슬링하는 사람들", + "🤽": "수구를 하는 사람", + "🤾": "핸드볼을 하는 사람", + "🤹": "저글링하는 사람", + "🧘": "연꽃 자세의 사람", + "🛀": "목욕하는 사람", + "🛌": "침대에 있는 사람", + "👭": "손을 잡고 있는 여성들", + "👫": "여자와 남자가 손을 잡고", + "👬": "손을 잡고 있는 남자들", + "💏": "키스", + "👤": "실루엣의 흉상", + "👥": "실루엣의 흉상들", + "🫂": "포옹하는 사람들", + "👪": "가족", + "💑": "마음으로 커플", + "👣": "발자취", + "🦰": "빨강 머리", + "🦱": "곱슬 머리", + "🦳": "흰머리", + "🦲": "대머리", + "🐵": "원숭이 얼굴", + "🐒": "원숭이", + "🦍": "고릴라", + "🦧": "오랑우탄", + "🐶": "강아지 얼굴", + "🐕": "개", + "🦮": "안내견", + "🐩": "푸들", + "🐺": "늑대", + "🦊": "여우", + "🦝": "너구리", + "🐱": "고양이", + "🐈": "고양이", + "🦁": "사자", + "🐯": "호랑이 얼굴", + "🐅": "호랑이", + "🐆": "표범", + "🐴": "말 얼굴", + "🫎": "무스", + "🫏": "당나귀", + "🐎": "말", + "🦄": "유니콘", + "🦓": "얼룩말", + "🦌": "사슴", + "🦬": "들소", + "🐮": "소의 얼굴", + "🐂": "소", + "🐃": "물소", + "🐄": "젖소", + "🐷": "돼지 얼굴", + "🐖": "돼지", + "🐗": "멧돼지", + "🐽": "돼지코", + "🐏": "양", + "🐑": "암양", + "🐐": "염소", + "🐪": "낙타", + "🐫": "혹이 두 개인 낙타", + "🦙": "라마", + "🦒": "기린", + "🐘": "코끼리", + "🦣": "매머드", + "🦏": "코뿔소", + "🦛": "하마", + "🐭": "쥐 얼굴", + "🐁": "쥐", + "🐀": "검은 쥐", + "🐹": "햄스터", + "🐰": "토끼", + "🐇": "토끼", + "🐿": "다람쥐", + "🦫": "비버", + "🦔": "고슴도치", + "🦇": "박쥐", + "🐻": "곰", + "🐨": "코알라", + "🐼": "판다", + "🦥": "나무늘보", + "🦦": "수달", + "🦨": "스컹크", + "🦘": "캥거루", + "🦡": "오소리", + "🐾": "발자국", + "🦃": "칠면조", + "🐔": "치킨", + "🐓": "수탉", + "🐣": "부화한 병아리", + "🐤": "아기 병아리", + "🐥": "아기 병아리", + "🐦": "새", + "🐧": "펭귄", + "🦅": "독수리", + "🦆": "오리", + "🦢": "백조", + "🦉": "올빼미", + "🦤": "도도새", + "🪶": "깃털", + "🦩": "플라밍고", + "🦚": "공작새", + "🦜": "앵무새", + "🪽": "날개", + "🪿": "거위", + "🐸": "개구리", + "🐊": "악어", + "🐢": "거북이", + "🦎": "도마뱀", + "🐍": "뱀", + "🐲": "용 얼굴", + "🐉": "용", + "🦕": "공룡", + "🦖": "티라노", + "🐳": "내뿜는 고래", + "🐋": "고래", + "🐬": "돌고래", + "🦭": "물개", + "🐟": "물고기", + "🐠": "열대어", + "🐡": "복어", + "🦈": "상어", + "🐙": "문어", + "🐚": "소라", + "🪸": "산호", + "🪼": "해파리", + "🐌": "달팽이", + "🦋": "나비", + "🐛": "벌레", + "🐜": "개미", + "🐝": "꿀벌", + "🪲": "딱정벌레", + "🐞": "무당벌레", + "🦗": "귀뚜라미", + "🪳": "바퀴벌레", + "🕸": "거미줄", + "🕷": "거미", + "🦂": "전갈", + "🦟": "모기", + "🪰": "파리", + "🪱": "지렁이", + "🦠": "미생물", + "💐": "꽃다발", + "🌸": "벚꽃", + "💮": "흰 꽃", + "🪷": "연꽃", + "🌹": "장미", + "🥀": "시든 꽃", + "🌺": "히비스커스", + "🌻": "해바라기", + "🌼": "꽃을 피우다", + "🌷": "튤립", + "🪻": "히아신스", + "🌱": "모종", + "🪴": "화분에 심은 식물", + "🌲": "상록수", + "🌳": "낙엽수", + "🌴": "야자수", + "🌵": "선인장", + "🌾": "벼", + "🌿": "약초", + "🍀": "네잎클로버", + "🍁": "단풍잎", + "🍂": "낙엽", + "🍃": "바람에 흩날리는 나뭇잎", + "🪹": "빈둥지", + "🪺": "알을 품다", + "🍄": "버섯", + "🍇": "포도", + "🍈": "메론", + "🍉": "수박", + "🍊": "귤", + "🍋": "레몬", + "🍌": "바나나", + "🍍": "파인애플", + "🥭": "망고", + "🍎": "빨간 사과", + "🍏": "청사과", + "🍐": "먹는 배", + "🍑": "복숭아", + "🍒": "체리", + "🍓": "딸기", + "🫐": "블루베리", + "🥝": "키위", + "🍅": "토마토", + "🫒": "올리브", + "🥥": "코코넛", + "🥑": "아보카도", + "🍆": "가지", + "🥔": "감자", + "🥕": "당근", + "🌽": "옥수수", + "🫑": "피망", + "🥒": "오이", + "🥬": "배추", + "🥦": "브로콜리", + "🧄": "마늘", + "🧅": "양파", + "🥜": "땅콩", + "🫘": "콩류", + "🌰": "밤송이", + "🫚": "생강 뿌리", + "🫛": "완두콩", + "🍞": "빵", + "🥐": "크루아상", + "🥖": "바게트 빵", + "🫓": "납작한 빵", + "🥨": "프레첼", + "🥞": "팬케이크", + "🥯": "베이글", + "🧇": "와플", + "🧀": "치즈", + "🍖": "뼈 있는 고기", + "🍗": "양다리 고기", + "🥩": "살코기", + "🥓": "베이컨", + "🍔": "햄버거", + "🍟": "감자튀김", + "🍕": "피자", + "🌭": "핫도그", + "🥪": "샌드위치", + "🌮": "타코", + "🌯": "부리토", + "🫔": "타말", + "🥙": "속이 꽉 찬 납작빵", + "🧆": "파라펠", + "🥚": "달걀", + "🍳": "요리", + "🥘": "얕은 냄비의 음식", + "🍲": "냄비의 음식", + "🫕": "퐁듀", + "🥣": "숟가락으로 그릇에 담다", + "🥗": "그린 샐러드", + "🍿": "팝콘", + "🧈": "버터", + "🧂": "소금", + "🥫": "통조림", + "🍱": "도시락통", + "🍘": "쌀 과자", + "🍙": "주먹밥", + "🍚": "지은 밥", + "🍛": "카레라이스", + "🍜": "김이 모락모락 나는 그릇", + "🍝": "스파게티", + "🍠": "군고구마", + "🍢": "꼬지", + "🍣": "스시", + "🍤": "새우튀김", + "🍥": "어묵", + "🥮": "월병", + "🍡": "당고", + "🥟": "만두", + "🥠": "포춘 쿠키", + "🥡": "테이크아웃 박스", + "🦀": "게", + "🦞": "바다가재", + "🦐": "새우", + "🦑": "오징어", + "🦪": "굴", + "🍦": "소프트 아이스크림", + "🍧": "빙수", + "🍨": "아이스크림", + "🍩": "도넛", + "🍪": "쿠키", + "🎂": "생일 케이크", + "🍰": "조각 케이크", + "🧁": "컵케이크", + "🥧": "파이", + "🍫": "초콜릿", + "🍬": "사탕", + "🍭": "막대사탕", + "🍮": "푸딩", + "🍯": "꿀단지", + "🍼": "젖병", + "🥛": "우유 한 잔", + "☕": "뜨거운 음료", + "🫖": "찻주전자", + "🍵": "손이 없는 찻잔", + "🍶": "사케", + "🍾": "코르크 마개가 달린 병", + "🍷": "포도주 잔", + "🍸": "칵테일 잔", + "🍹": "열대 음료", + "🍺": "맥주 머그잔", + "🍻": "건배하는 맥주잔", + "🥂": "건배하는 샴페인잔", + "🥃": "텀블러 글라스", + "🫗": "쏟아지는 액체", + "🥤": "빨대가 든 컵", + "🧋": "버블티", + "🧃": "팩 음료수", + "🧉": "코코넛 음료수", + "🧊": "얼음", + "🥢": "젓가락", + "🍴": "포크 나이프", + "🥄": "숟가락", + "🔪": "식칼", + "🫙": "항아리", + "🏺": "암포라", + "🌍": "유럽-아프리카를 보여주는 지구본", + "🌎": "아메리카 대륙을 보여주는 지구본", + "🌏": "아시아-호주를 보여주는 지구본", + "🌐": "자오선이 있는 지구본", + "🗾": "일본의 지도", + "🧭": "나침반", + "⛰": "산", + "🌋": "화산", + "🗻": "후지산", + "🏕": "캠핑", + "🏖": "파라솔이 있는 해변", + "🏜": "사막", + "🏝": "무인도", + "🏞": "국립공원", + "🏟": "경기장", + "🏛": "고전적인 건축물", + "🏗": "건축 공사", + "🧱": "벽돌", + "🪨": "바위", + "🪵": "나무", + "🛖": "움막", + "🏘": "집들", + "🏚": "폐가", + "🏠": "집", + "🏡": "집", + "🏢": "사옥", + "🏣": "일본우체국", + "🏤": "우체국", + "🏥": "병원", + "🏦": "은행", + "🏨": "호텔", + "🏩": "러브호텔", + "🏪": "편의점", + "🏫": "학교", + "🏬": "백화점", + "🏭": "공장", + "🏯": "왜성", + "🏰": "성", + "💒": "웨딩", + "🗼": "도쿄 타워", + "🗽": "자유의 여신상", + "⛪": "교회", + "🕌": "모스크", + "🛕": "힌두교 사원", + "🕍": "유대교 회당", + "⛩": "신사", + "🕋": "카바", + "⛲": "분수", + "⛺": "텐트", + "🌁": "안개가 자욱한", + "🌃": "별이 빛나는 밤", + "🏙": "도시의 풍경", + "🌄": "산을 넘는 해돋이", + "🌅": "해돋이", + "🌆": "해질녘의 도시 풍경", + "🌇": "석양", + "🌉": "밤에 다리를 놓음", + "♨": "온천", + "🎠": "회전목마", + "🛝": "놀이터 미끄럼틀", + "🎡": "관람차", + "🎢": "롤러코스터", + "💈": "이발소", + "🎪": "서커스 텐트", + "🚂": "기관차", + "🚃": "전철", + "🚄": "고속 열차", + "🚅": "신칸센", + "🚆": "기차", + "🚇": "메트로", + "🚈": "경전철", + "🚉": "역", + "🚊": "전차", + "🚝": "모노레일", + "🚞": "산악 철도", + "🚋": "전차", + "🚌": "버스", + "🚍": "오는 버스", + "🚎": "트롤리버스", + "🚐": "미니버스", + "🚑": "구급차", + "🚒": "소방차", + "🚓": "경찰차", + "🚔": "마주 오는 경찰차", + "🚕": "택시", + "🚖": "마주 오는 택시", + "🚗": "자동차", + "🚘": "마주 오는 자동차", + "🚙": "스포츠 유틸리티 차량", + "🛻": "소형 오픈 트럭", + "🚚": "배달차", + "🚛": "관절식 화물차", + "🚜": "트랙터", + "🏎": "경주용 자동차", + "🏍": "오토바이", + "🛵": "모터 스쿠터", + "🦽": "수동 휠체어", + "🦼": "전동 휠체어", + "🛺": "자동 인력거", + "🚲": "자전거", + "🛴": "킥 스쿠터", + "🛹": "스케이트보드", + "🛼": "롤러스케이트", + "🚏": "버스 정류장", + "🛣": "고속도로", + "🛤": "철도 선로", + "🛢": "오일 드럼", + "⛽": "주유소 연료 펌프", + "🛞": "바퀴", + "🚨": "경차등", + "🚥": "수평 신호등", + "🚦": "수직 신호등", + "🛑": "정지표지", + "🚧": "시공 표지판", + "⚓": "닻", + "🛟": "고리 부표", + "⛵": "범선", + "🛶": "카누", + "🚤": "쾌속정", + "🛳": "여객선", + "⛴": "페리", + "🛥": "모터보트", + "🚢": "배", + "✈": "비행기", + "🛩": "소형 비행기", + "🛫": "비행기 출발", + "🛬": "비행기 도착", + "🪂": "낙하산", + "💺": "좌석", + "🚁": "헬리콥터", + "🚟": "현수 철도", + "🚠": "케이블카", + "🚡": "공중 전차로", + "🛰": "위성", + "🚀": "로켓", + "🛸": "UFO", + "🛎": "종소리", + "🧳": "짐", + "⌛": "모래시계", + "⏳": "모래시계 미완성", + "⌚": "시계", + "⏰": "자명종", + "⏱": "스톱워치", + "⏲": "타이머 시계", + "🕰": "맨틀피스 시계", + "🕛": "12시", + "🕧": "12시 30분", + "🕐": "1시", + "🕜": "1시 30분", + "🕑": "2시", + "🕝": "2시 30분", + "🕒": "3시", + "🕞": "3시 30분", + "🕓": "4시", + "🕟": "4시 30분", + "🕔": "5시", + "🕠": "5시 30분", + "🕕": "6시", + "🕡": "6시 30분", + "🕖": "7시", + "🕢": "7시 30분", + "🕗": "8시", + "🕣": "8시 30분", + "🕘": "9시", + "🕤": "9시 30분", + "🕙": "10시", + "🕥": "10시 30분", + "🕚": "11시", + "🕦": "11시 30분", + "🌑": "어두운 달", + "🌒": "초승달", + "🌓": "상현달", + "🌔": "상현달과 보름달 사이", + "🌕": "보름달", + "🌖": "보름달과 하현달 사이", + "🌗": "하현달", + "🌘": "그믐달", + "🌙": "초승달", + "🌚": "어두운 달 얼굴", + "🌛": "초승달 얼굴", + "🌜": "그믐달 얼굴", + "🌡": "온도계", + "☀": "태양", + "🌝": "보름달 얼굴", + "🌞": "태양 얼굴", + "🪐": "고리형 행성", + "⭐": "별", + "🌟": "빛나는 별", + "🌠": "별똥별", + "🌌": "은하수", + "☁": "구름", + "⛅": "구름 뒤의 태양", + "⛈": "번개와 비를 동반한 구름", + "🌤": "작은 구름 뒤의 태양", + "🌥": "큰 구름 뒤의 태양", + "🌦": "비구름 뒤의 태양", + "🌧": "비로 구름이 끼다", + "🌨": "눈구름", + "🌩": "번개가 치는 구름", + "🌪": "토네이도", + "🌫": "안개", + "🌬": "풍면", + "🌀": "태풍", + "🌈": "무지개", + "🌂": "닫힌 우산", + "☂": "우산", + "☔": "빗방울이 떨어지는 우산", + "⛱": "파라솔", + "⚡": "번개", + "❄": "눈송이", + "☃": "눈 내리는 눈사람", + "⛄": "눈사람", + "☄": "혜성", + "🔥": "불", + "💧": "물방울", + "🌊": "파도", + "🎃": "호박 랜턴", + "🎄": "크리스마스트리", + "🎆": "불꽃놀이", + "🎇": "반짝반짝 빛나는", + "🧨": "폭죽", + "✨": "반짝임", + "🎈": "풍선", + "🎉": "폭죽", + "🎊": "색종이 공", + "🎋": "타나바타 나무", + "🎍": "대나무 장식", + "🎎": "일본 인형", + "🎏": "카프 스트리머", + "🎐": "풍등", + "🎑": "달 구경식", + "🧧": "빨간 봉투", + "🎀": "리본", + "🎁": "선물", + "🎗": "리마인더 리본", + "🎟": "입장권", + "🎫": "티켓", + "🎖": "무공훈장", + "🏆": "트로피", + "🏅": "스포츠 메달", + "🥇": "1등 메달", + "🥈": "2등 메달", + "🥉": "3등 메달", + "⚽": "축구공", + "⚾": "야구공", + "🥎": "소프트볼", + "🏀": "농구공", + "🏐": "배구공", + "🏈": "미식축구공", + "🏉": "럭비공", + "🎾": "테니스", + "🥏": "원반", + "🎳": "볼링", + "🏏": "크리켓 경기", + "🏑": "필드하키", + "🏒": "아이스 하키", + "🥍": "라크로스", + "🏓": "탁구", + "🏸": "배드민턴", + "🥊": "권투 글러브", + "🥋": "도복", + "🥅": "골망", + "⛳": "골프 필드", + "⛸": "스케이트 신발", + "🎣": "낚싯대", + "🤿": "잠수 마스크", + "🎽": "런닝 셔츠", + "🎿": "스키", + "🛷": "썰매", + "🥌": "컬링 스톤", + "🎯": "양궁 과녁", + "🪀": "요요", + "🪁": "연", + "🔫": "물총", + "🎱": "포켓볼", + "🔮": "수정구슬", + "🪄": "요술 지팡이", + "🎮": "비디오 게임", + "🕹": "조이스틱", + "🎰": "슬롯머신", + "🎲": "주사위", + "🧩": "퍼즐 조각", + "🧸": "인형", + "🪅": "피냐타", + "🪩": "미러볼", + "🪆": "중첩 인형", + "♠": "스페이드 문양", + "♥": "하트 문양", + "♦": "다이아몬드 문양", + "♣": "클럽 문양", + "♟": "체스", + "🃏": "조커", + "🀄": "마작 적룡", + "🎴": "화투", + "🎭": "공연 예술", + "🖼": "액자에 넣어진 그림", + "🎨": "팔레트", + "🧵": "실", + "🪡": "바느질 바늘", + "🧶": "털실", + "🪢": "매듭을 짓다", + "👓": "안경", + "🕶": "선글라스", + "🥽": "고글", + "🥼": "실험실 코트", + "🦺": "안전조끼", + "👔": "넥타이", + "👕": "티셔츠", + "👖": "청바지", + "🧣": "목도리", + "🧤": "장갑", + "🧥": "외투", + "🧦": "양말", + "👗": "드레스", + "👘": "기모노", + "🥻": "사리 옷", + "🩱": "원피스 수영복", + "🩲": "팬티", + "🩳": "반바지", + "👙": "비키니", + "👚": "여성 옷", + "🪭": "접이식 부채", + "👛": "지갑", + "👜": "핸드백", + "👝": "클러치백", + "🛍": "쇼핑백", + "🎒": "백팩", + "🩴": "굵은 샌들", + "👞": "구두", + "👟": "러닝화", + "🥾": "등산화", + "🥿": "납작한 구두", + "👠": "굽이 높은 구두", + "👡": "여자 샌들", + "🩰": "발레 슈즈", + "👢": "여성 부츠", + "🪮": "헤어 픽", + "👑": "왕관", + "👒": "여성 모자", + "🎩": "중절모", + "🎓": "졸업모", + "🧢": "캡모자", + "🪖": "군모", + "⛑": "구조대원의 헬멧", + "📿": "기도 구슬", + "💄": "립스틱", + "💍": "반지", + "💎": "보석", + "🔇": "음소거", + "🔈": "스피커 저음량", + "🔉": "스피커 미디엄 볼륨", + "🔊": "스피커 고음량", + "📢": "확성기", + "📣": "메가폰", + "📯": "호른", + "🔔": "종", + "🔕": "종을 울리면 안된다.", + "🎼": "높은음자리표", + "🎵": "음표", + "🎶": "음표", + "🎙": "스튜디오 마이크", + "🎚": "수평 슬라이더", + "🎛": "조절 손잡이", + "🎤": "마이크", + "🎧": "헤드폰", + "📻": "라디오", + "🎷": "색소폰", + "🪗": "아코디언", + "🎸": "기타", + "🎹": "키보드", + "🎺": "트럼펫", + "🎻": "바이올린", + "🪕": "밴조", + "🥁": "북을 치다", + "🪘": "긴 북", + "🪇": "마라카스", + "🪈": "피리", + "📱": "휴대전화", + "📲": "화살이 달린 휴대전화", + "☎": "전화", + "📞": "전화 수신기", + "📟": "호출기", + "📠": "팩스기", + "🔋": "건전지", + "🪫": "배터리 부족", + "🔌": "전기 플러그", + "💻": "노트북", + "🖥": "데스크톱 컴퓨터", + "🖨": "프린터", + "⌨": "키보드", + "🖱": "컴퓨터 마우스", + "🖲": "트랙볼", + "💽": "컴퓨터 디스크", + "💾": "플로피 디스크", + "💿": "광디스크", + "📀": "dvd", + "🧮": "주판", + "🎥": "영화 카메라", + "🎞": "필름", + "📽": "필름 프로젝터", + "🎬": "슬레이트", + "📺": "텔레비전", + "📷": "카메라", + "📸": "플래시가 달린 카메라", + "📹": "비디오 카메라", + "📼": "비디오 카세트", + "🔍": "돋보기", + "🔎": "오른쪽으로 기울어진 돋보기", + "🕯": "양초", + "💡": "전구", + "🔦": "손전등", + "🏮": "붉은 색 종이 등불", + "🪔": "디야등", + "📔": "장식 표지가 달린 수첩", + "📕": "책", + "📖": "오픈북", + "📗": "초록색 책", + "📘": "하늘색 책", + "📙": "주황색 책", + "📚": "책들", + "📓": "수첩", + "📒": "장부", + "📃": "종이", + "📜": "두루마리", + "📄": "페이지가 위로 향하게", + "📰": "신문지", + "🗞": "두루마리 신문", + "📑": "책갈피 탭", + "🔖": "북마크", + "🏷": "라벨", + "💰": "돈가방", + "🪙": "동전", + "💴": "엔화 지폐", + "💵": "달러 지폐", + "💶": "유로 지폐", + "💷": "파운드 지폐", + "💸": "날개 달린 돈", + "💳": "신용카드", + "🧾": "영수증", + "💹": "엔에 따라 증가하는 차트", + "✉": "우편 봉투", + "📧": "전자우편", + "📨": "착신 봉투", + "📩": "화살표 봉투", + "📤": "보낸 편지함 트레이", + "📥": "받은 편지함 트레이", + "📦": "상자", + "📫": "깃발을 올린 닫힌 편지함", + "📪": "깃발을 내린 닫힌 편지함", + "📬": "깃발을 올린 열린 편지함", + "📭": "깃발을 내린 열린 편지함", + "📮": "우체통", + "🗳": "투표용지가 붙은 투표함", + "✏": "연필", + "✒": "펜촉", + "🖋": "만년필", + "🖊": "펜", + "🖌": "붓", + "🖍": "크레파스", + "📝": "메모", + "💼": "서류 가방", + "📁": "파일 폴더", + "📂": "열린 파일 폴더", + "🗂": "카드 인덱스 분할 폴더", + "📅": "달력", + "📆": "찢어진 달력", + "🗒": "메모장", + "🗓": "달력", + "📇": "카드 인덱스", + "📈": "차트 증가", + "📉": "차트 감소", + "📊": "막대 차트", + "📋": "클립보드", + "📌": "압정", + "📍": "둥근 푸쉬핀", + "📎": "종이 클립", + "🖇": "연결된 종이 클립", + "📏": "자", + "📐": "삼각자", + "✂": "가위", + "🗃": "카드 파일 박스", + "🗄": "파일 캐비닛", + "🗑": "쓰레기통", + "🔒": "자물쇠", + "🔓": "잠금 해제된", + "🔏": "펜으로 잠근", + "🔐": "열쇠로 잠근", + "🔑": "열쇠", + "🗝": "옛날 열쇠", + "🔨": "망치", + "🪓": "도끼", + "⛏": "곡갱이", + "⚒": "망치와 곡괭이", + "🛠": "망치와 렌치", + "🗡": "단검", + "⚔": "엇갈린 칼", + "💣": "폭탄", + "🪃": "부메랑", + "🏹": "활과 화살", + "🛡": "방패", + "🪚": "목공톱", + "🔧": "렌치", + "🪛": "드라이버", + "🔩": "볼트와 너트", + "⚙": "기어", + "🗜": "집게를 끼우다", + "⚖": "저울", + "🦯": "흰 지팡이", + "🔗": "링크", + "⛓": "쇠사슬", + "🪝": "갈고리", + "🧰": "공구함", + "🧲": "자석", + "🪜": "사다리", + "⚗": "원형 비커", + "🧪": "시험관", + "🧫": "페트리 접시", + "🧬": "DNA", + "🔬": "현미경", + "🔭": "망원경", + "📡": "위성 안테나", + "💉": "주사기", + "🩸": "피 한 방울", + "💊": "알약", + "🩹": "반창고", + "🩼": "목발", + "🩺": "청진기", + "🩻": "엑스레이", + "🚪": "문", + "🛗": "엘레베이터", + "🪞": "거울", + "🪟": "창문", + "🛏": "침대", + "🛋": "소파와 램프", + "🪑": "의자", + "🚽": "변기", + "🪠": "변기 플런저", + "🚿": "샤워", + "🛁": "욕조", + "🪤": "쥐 트랩", + "🪒": "면도기", + "🧴": "로션 병", + "🧷": "안전핀", + "🧹": "빗자루", + "🧺": "바구니", + "🧻": "두루마리 휴지", + "🪣": "양동이", + "🧼": "비누", + "🫧": "비눗방울", + "🪥": "칫솔", + "🧽": "스펀지", + "🧯": "소화기", + "🛒": "장바구니 카트", + "🚬": "담배", + "⚰": "관", + "🪦": "머릿돌", + "⚱": "장례식장", + "🧿": "나자르 본주우", + "🪬": "함사", + "🗿": "모아이 석상", + "🪧": "플래카드", + "🪪": "신분증", + "🏧": "ATM 표지", + "🚮": "쓰레기를 버림 표지", + "🚰": "음용수 표지", + "♿": "휠체어 기호", + "🚹": "남자 화장실", + "🚺": "여자 화장실", + "🚻": "화장실", + "🚼": "아기 기호", + "🚾": "화장실 표지", + "🛂": "여권 관리 표지", + "🛃": "세관 표지", + "🛄": "수하물 찾는 곳 표지", + "🛅": "남은 짐 표지", + "⚠": "경고문", + "🚸": "어린이 보호", + "⛔": "출입 금지", + "🚫": "금지 표지", + "🚳": "자전거 금지 표지", + "🚭": "흡연금지", + "🚯": "쓰레기 투기 금지", + "🚱": "식수 금지", + "🚷": "횡단 금지", + "📵": "휴대전화 금지", + "🔞": "18세 미만 금지", + "☢": "방사성의", + "☣": "생물학적 유해성", + "⬆": "위 방향 화살표", + "↗": "오른쪽 위의 화살표", + "➡": "오른쪽 화살표", + "↘": "오른쪽 아래 화살표", + "⬇": "아래쪽 화살표", + "↙": "왼쪽 아래 화살표", + "⬅": "왼쪽 화살표", + "↖": "왼쪽 위 화살표", + "↕": "위아래 화살표", + "↔": "좌우 화살표", + "↩": "오른쪽 화살표 곡선 왼쪽", + "↪": "왼쪽 화살표 곡선 오른쪽", + "🔃": "시계방향 세로 화살표", + "🔄": "반시계 방향 화살표", + "🔙": "뒤로 화살표", + "🔚": "끝 화살표", + "🔛": "ON! 화살표", + "🔜": "SOON 화살표", + "🔝": "상단 화살표", + "🛐": "예배 장소", + "⚛": "원자 기호", + "🕉": "om", + "✡": "다윗의 별", + "☸": "법륜", + "☯": "음양", + "✝": "라틴 십자가", + "☦": "정통 십자가", + "☪": "별과 초승달", + "☮": "평화의 상징", + "🕎": "메노라", + "🔯": "6개의 꼭짓점이 있는 별", + "🪯": "칸다", + "♈": "양자리", + "♉": "황소자리", + "♊": "쌍둥이자리", + "♋": "게자리", + "♌": "사자자리", + "♍": "처녀자리", + "♎": "천칭자리", + "♏": "전갈자리", + "♐": "궁수자리", + "♑": "염소자리", + "♒": "물병자리", + "♓": "물고기자리", + "⛎": "뱀주인자리", + "🔀": "셔플 버튼", + "🔁": "반복 버튼", + "🔂": "단일 버튼 반복", + "▶": "재생 버튼", + "⏩": "속전버튼", + "⏭": "다음 트랙 버튼", + "⏯": "재생 또는 일시 중지 버튼", + "◀": "역버튼", + "⏪": "빠른 역방향 버튼", + "⏮": "마지막 트랙 버튼", + "🔼": "위쪽 버튼", + "⏫": "위쪽 빠른 버튼", + "🔽": "아래쪽 버튼", + "⏬": "아래쪽 속단 버튼", + "⏸": "일시 중지 버튼", + "⏹": "정지 버튼", + "⏺": "녹음 버튼", + "⏏": "배출 버튼", + "🎦": "영화", + "🔅": "희미한 불빛 버튼", + "🔆": "밝은 불빛 버튼", + "📶": "안테나 바", + "🛜": "무선", + "📳": "진동 모드", + "📴": "휴대전화 꺼짐", + "♀": "여성 부호", + "♂": "남성 부호", + "⚧": "트랜스젠더 기호", + "✖": "곱셈", + "➕": "덧셈", + "➖": "뺄셈", + "➗": "나눗셋", + "🟰": "등호", + "♾": "무한대", + "‼": "이중 느낌표", + "⁉": "느낌표 물음표", + "❓": "빨간 물음표", + "❔": "흰 물음표", + "❕": "흰 느낌표", + "❗": "붉은 느낌표", + "〰": "물결치는 대쉬", + "💱": "환전", + "💲": "무거운 달러 표시", + "⚕": "의학적 기호", + "♻": "재활용 기호", + "⚜": "플뢰르 드 리", + "🔱": "삼지창 엠블럼", + "📛": "이름배지", + "🔰": "초급을 뜻하는 일본어 기호", + "⭕": "속이 빈 붉은 원", + "✅": "체크 표시 버튼", + "☑": "체크박스가 있는 체크박스", + "✔": "체크 마크", + "❌": "엑스 표시", + "❎": "엑스 표시 버튼", + "➰": "꼬불꼬불한 고리", + "➿": "더블 컬 루프", + "〽": "부품 교대 표시", + "✳": "여덟 스포크의 별표", + "✴": "여덟 포인트의 별", + "❇": "반짝반짝 빛나다", + "©": "저작권", + "®": "등록표시", + "™": "상표", + "🔟": "키캡: 10", + "🔠": "라틴 대문자 입력", + "🔡": "라틴 소문자 입력", + "🔢": "입력 번호", + "🔣": "입력 기호", + "🔤": "라틴 문자 입력", + "🅰": "A버튼(혈액형)", + "🆎": "AB버튼(혈액형)", + "🅱": "B버튼(혈액형)", + "🆑": "CL버튼", + "🆒": "COOL 버튼", + "🆓": "FREE 버튼", + "ℹ": "정보", + "🆔": "아이디버튼", + "Ⓜ": "동그라미가 쳐진 M", + "🆕": "NEW 버튼", + "🆖": "NG버튼", + "🅾": "O버튼(혈액형)", + "🆗": "OK버튼", + "🅿": "P버튼", + "🆘": "SOS버튼", + "🆙": "UP! 버튼", + "🆚": "VS 버튼", + "🔴": "붉은 원", + "🟠": "오렌지색 원", + "🟡": "노란색 원", + "🟢": "초록색 원", + "🔵": "파란색 원", + "⚫": "검은색 원", + "🏳": "백기", + "🕯️": "양초", + "✉️": "편지", + "✂️": "가위", + "🗺️": "세계지도" +} diff --git a/packages/utils/fetch.ts b/packages/utils/fetch.ts index 1e93ac2..3d56057 100644 --- a/packages/utils/fetch.ts +++ b/packages/utils/fetch.ts @@ -1,12 +1,16 @@ -export default async function(url: URL | RequestInfo, request: RequestInit={}, time: number=5000): Promise { - const controller = new AbortController(); - const timeout = setTimeout(() => controller.abort(), time); +export default async function ( + url: URL | RequestInfo, + request: RequestInit = {}, + time: number = 5000, +): Promise { + const controller = new AbortController(); + const timeout = setTimeout(() => controller.abort(), time); - request.signal ??= controller.signal; + request.signal ??= controller.signal; - try { - return await fetch(url, request);; - } finally { - clearTimeout(timeout); - } -} \ No newline at end of file + try { + return await fetch(url, request); + } finally { + clearTimeout(timeout); + } +} diff --git a/packages/utils/floatKorean.ts b/packages/utils/floatKorean.ts index 971a3be..dcd64d5 100644 --- a/packages/utils/floatKorean.ts +++ b/packages/utils/floatKorean.ts @@ -1,16 +1,14 @@ export default class FloatKorean { - static Digits = [ - "영", "일", "이", "삼", "사", "오", "육", "칠", "팔", "구" - ]; - static convert(num: string): string { - const buf = new Array(num.length); - for (let idx = 0; idx < num.length; idx++) { - if (num[idx] == ".") { - buf[idx] = "쩜"; - } else { - buf[idx] = this.Digits[+num[idx]]; - } - } - return buf.join(""); + static Digits = ["영", "일", "이", "삼", "사", "오", "육", "칠", "팔", "구"]; + static convert(num: string): string { + const buf = new Array(num.length); + for (let idx = 0; idx < num.length; idx++) { + if (num[idx] == ".") { + buf[idx] = "쩜"; + } else { + buf[idx] = this.Digits[+(num[idx] ?? "0")]; + } } -} \ No newline at end of file + return buf.join(""); + } +} diff --git a/packages/utils/integerKorean.ts b/packages/utils/integerKorean.ts index 2cbee34..b782f57 100644 --- a/packages/utils/integerKorean.ts +++ b/packages/utils/integerKorean.ts @@ -1,110 +1,141 @@ export default class IntegerKorean { - static DigitName = [ "영", "일", "이", "삼", "사", "오", "육", "칠", "팔", "구" ]; - static DigitModifier = ["", "십", "백", "천"]; - static Unit = [ "", "만", "억", "조", "경", "해", "자", "양", "구", "간", "정", "재", "극", "항하사", "아승기", "나유타", "불가사의", "무량대수" ]; + static DigitName = [ + "영", + "일", + "이", + "삼", + "사", + "오", + "육", + "칠", + "팔", + "구", + ]; + static DigitModifier = ["", "십", "백", "천"]; + static Unit = [ + "", + "만", + "억", + "조", + "경", + "해", + "자", + "양", + "구", + "간", + "정", + "재", + "극", + "항하사", + "아승기", + "나유타", + "불가사의", + "무량대수", + ]; - private static stringifyKDigits( - first: number, second: number, third: number, forth: number - ): string { - const buf = []; + private static stringifyKDigits( + first: number, + second: number, + third: number, + forth: number, + ): string { + const buf = []; - if (forth) { - if (forth >= 2) buf.push(this.DigitName[forth]); - buf.push(this.DigitModifier[3]); - } - if (third) { - if (third >= 2) buf.push(this.DigitName[third]); - buf.push(this.DigitModifier[2]); - } - if (second) { - if (second >= 2) buf.push(this.DigitName[second]); - buf.push(this.DigitModifier[1]); - } - if (first || (!forth && !third && !second)) { - buf.push(this.DigitName[first]); - } - - return buf.join(""); + if (forth) { + if (forth >= 2) buf.push(this.DigitName[forth]); + buf.push(this.DigitModifier[3]); } - private static parseKDigitsFromNumber(num: number): string { - const first = num % 10; - const second = Math.floor(num / 10) % 10; - const third = Math.floor(num / 100) % 10; - const forth = Math.floor(num / 1000) % 10; - - return this.stringifyKDigits(first, second, third, forth); + if (third) { + if (third >= 2) buf.push(this.DigitName[third]); + buf.push(this.DigitModifier[2]); } - private static parseKDigitsFromString(num: string, offset: number): string { - const first = +num[offset]; - const second = offset >= 1 ? +num[offset - 1] : 0; - const third = offset >= 2 ? +num[offset - 2] : 0; - const forth = offset >= 3 ? +num[offset - 3] : 0; - - return this.stringifyKDigits(first, second, third, forth); + if (second) { + if (second >= 2) buf.push(this.DigitName[second]); + buf.push(this.DigitModifier[1]); + } + if (first || (!forth && !third && !second)) { + buf.push(this.DigitName[first]); } - static convertFromString(num: string): string { - num = num.replace(/,/g, ""); - let isNegative = false; - if (num.startsWith("-")) { - num = num.slice(1, -1); - isNegative = true; - } - if (num == "0") { - return isNegative ? "마이너스영" : "영"; - } + return buf.join(""); + } + private static parseKDigitsFromNumber(num: number): string { + const first = num % 10; + const second = Math.floor(num / 10) % 10; + const third = Math.floor(num / 100) % 10; + const forth = Math.floor(num / 1000) % 10; - const unitStack = []; - let offset = num.length - 1; - while (offset >= 0) { - unitStack.push(this.parseKDigitsFromString(num, offset)); - offset -= 4; - } + return this.stringifyKDigits(first, second, third, forth); + } + private static parseKDigitsFromString(num: string, offset: number): string { + const first = +num[offset]!; + const second = offset >= 1 ? +num[offset - 1]! : 0; + const third = offset >= 2 ? +num[offset - 2]! : 0; + const forth = offset >= 3 ? +num[offset - 3]! : 0; - const buf = []; - if (isNegative) buf.push("마이너스"); - for (let i = unitStack.length - 1; i >= 0; i--) { - const currUnit = this.Unit[i]; - let currKDigits = unitStack[i]; + return this.stringifyKDigits(first, second, third, forth); + } - if (currKDigits == "영") continue; - if (i == 1 && currKDigits == "일") - currKDigits = ""; - - buf.push(currKDigits + currUnit); - } - - return buf.join(""); + static convertFromString(num: string): string { + num = num.replace(/,/g, ""); + let isNegative = false; + if (num.startsWith("-")) { + num = num.slice(1, -1); + isNegative = true; } - static convertFromNumber(num: number): string { - let isNegative = false; - if (num < 0) { - isNegative = true; - num *= -1; - } - if (num == 0) { - return "영"; - } - - const unitStack = []; - while (num) { - unitStack.push(this.parseKDigitsFromNumber(num)); - num = Math.floor(num / 10000); - } - - const buf = []; - if (isNegative) buf.push("마이너스"); - for (let i = unitStack.length - 1; i >= 0; i--) { - const currUnit = this.Unit[i]; - let currKDigits = unitStack[i]; - - if (currKDigits == "영") continue; - if (i == 1 && currKDigits == "일") - currKDigits = ""; - - buf.push(currKDigits + currUnit); - } - - return buf.join(""); + if (num == "0") { + return isNegative ? "마이너스영" : "영"; } + + const unitStack = []; + let offset = num.length - 1; + while (offset >= 0) { + unitStack.push(this.parseKDigitsFromString(num, offset)); + offset -= 4; + } + + const buf = []; + if (isNegative) buf.push("마이너스"); + for (let i = unitStack.length - 1; i >= 0; i--) { + const currUnit = this.Unit[i] ?? ""; + let currKDigits = unitStack[i] ?? ""; + + if (currKDigits == "영") continue; + if (i == 1 && currKDigits == "일") currKDigits = ""; + + buf.push(currKDigits + currUnit); + } + + return buf.join(""); + } + static convertFromNumber(num: number): string { + let isNegative = false; + if (num < 0) { + isNegative = true; + num *= -1; + } + if (num == 0) { + return "영"; + } + + const unitStack = []; + while (num) { + unitStack.push(this.parseKDigitsFromNumber(num)); + num = Math.floor(num / 10000); + } + + const buf = []; + if (isNegative) buf.push("마이너스"); + for (let i = unitStack.length - 1; i >= 0; i--) { + const currUnit = this.Unit[i] ?? ""; + let currKDigits = unitStack[i] ?? ""; + + if (currKDigits == "영") continue; + if (i == 1 && currKDigits == "일") currKDigits = ""; + + buf.push(currKDigits + currUnit); + } + + return buf.join(""); + } } diff --git a/packages/utils/nyaize.ts b/packages/utils/nyaize.ts index 1a200bb..1ffea07 100644 --- a/packages/utils/nyaize.ts +++ b/packages/utils/nyaize.ts @@ -1,137 +1,173 @@ export const nyaWords = { - '나': "냐", - '낙': "냑", - '낚': "냒", - '낛': "냓", - '난': "냔", - '낝': "냕", - '낞': "냖", - '낟': "냗", - '날': "냘", - '낡': "냙", - '낢': "냚", - '낣': "냛", - '낤': "냜", - '낥': "냝", - '낦': "냞", - '낧': "냟", - '남': "냠", - '납': "냡", - '낪': "냢", - '낫': "냣", - '났': "냤", - '낭': "냥", - '낮': "냦", - '낯': "냧", - '낰': "냨", - '낱': "냩", - '낲': "냪", - '낳': "냫", + 나: "냐", + 낙: "냑", + 낚: "냒", + 낛: "냓", + 난: "냔", + 낝: "냕", + 낞: "냖", + 낟: "냗", + 날: "냘", + 낡: "냙", + 낢: "냚", + 낣: "냛", + 낤: "냜", + 낥: "냝", + 낦: "냞", + 낧: "냟", + 남: "냠", + 납: "냡", + 낪: "냢", + 낫: "냣", + 났: "냤", + 낭: "냥", + 낮: "냦", + 낯: "냧", + 낰: "냨", + 낱: "냩", + 낲: "냪", + 낳: "냫", }; export const nyaWords2 = { - '내': "냥", - '넹': "냥", - '넴': "냥", - '넵': "냥", - '냐': "냥", - '님': "냥", - '니': "냥", - '다': "다냥", - '까': "까냥", - '네': "네냥", - '야': "야냥", - '꺼': "꺼냥", - '래': "래냥", - '해': "해냥", - '지': "지냥", - '라': "라냥", - '요': "요냥", - '가': "가냥", - '데': "데냥", - '돼': "돼냥", - '줘': "줘냥", - '마': "마냥", - '와': "와냥", - '어': "어냥", - '자': "자냥", - '죠': "죠냥", - '서': "서냥", - '게': "게냥", + 내: "냥", + 넹: "냥", + 넴: "냥", + 넵: "냥", + 냐: "냥", + 님: "냥", + 니: "냥", + 다: "다냥", + 까: "까냥", + 네: "네냥", + 야: "야냥", + 꺼: "꺼냥", + 래: "래냥", + 해: "해냥", + 지: "지냥", + 라: "라냥", + 요: "요냥", + 가: "가냥", + 데: "데냥", + 돼: "돼냥", + 줘: "줘냥", + 마: "마냥", + 와: "와냥", + 어: "어냥", + 자: "자냥", + 죠: "죠냥", + 서: "서냥", + 게: "게냥", }; function replacePunctuation(input: string): string { - return input.replace(/(^|\s)([?!,.;~^@()]+)/g, (match, p1, p2) => { - const firstChar = p2[0]; - let transformed; + return input.replace(/(^|\s)([?!,.;~^@()]+)/g, (match, p1, p2) => { + const firstChar = p2[0]; + let transformed; - if (firstChar === '?') transformed = '냥?'; - else if (firstChar === '!') transformed = '냥!'; - else if (firstChar === ',') transformed = '냥,'; - else if (firstChar === '.') transformed = '냥.'; - else if (firstChar === ';') transformed = '냥;'; - else if (firstChar === '~') transformed = '냥~'; - else if (firstChar === '^') transformed = '냥^'; - else if (firstChar === '@') transformed = '냥@'; - else if (firstChar === '(') transformed = '냥('; - else if (firstChar === ')') transformed = '냥)'; - else transformed = p2; + if (firstChar === "?") transformed = "냥?"; + else if (firstChar === "!") transformed = "냥!"; + else if (firstChar === ",") transformed = "냥,"; + else if (firstChar === ".") transformed = "냥."; + else if (firstChar === ";") transformed = "냥;"; + else if (firstChar === "~") transformed = "냥~"; + else if (firstChar === "^") transformed = "냥^"; + else if (firstChar === "@") transformed = "냥@"; + else if (firstChar === "(") transformed = "냥("; + else if (firstChar === ")") transformed = "냥)"; + else transformed = p2; - return p1 + transformed + p2.slice(1); - }); + return p1 + transformed + p2.slice(1); + }); } function addNyangAtMWord(sentence: string): string { - return sentence.split(' ').map((word) => { - const match = word.match(/^([가-힣]+)([?!,.;~^@()]*)$/); + return sentence + .split(" ") + .map((word) => { + const match = word.match(/^([가-힣]+)([?!,.;~^@()]*)$/); - if (!match) return word; + if (!match) return word; - const baseWord = match[1]; - const punctuation = match[2]; + const baseWord = match[1] ?? ""; + const punctuation = match[2] ?? ""; - const lastChar = baseWord[baseWord.length - 1]; - const charCode = lastChar.charCodeAt(0); + const lastChar = baseWord[baseWord.length - 1] ?? ""; + const charCode = lastChar.charCodeAt(0); - if (charCode >= 0xAC00 && charCode <= 0xD7A3) { - const baseCode = charCode - 0xAC00; - const jongseong = baseCode % 28; + if (charCode >= 0xac00 && charCode <= 0xd7a3) { + const baseCode = charCode - 0xac00; + const jongseong = baseCode % 28; - if (jongseong === 16) { - return baseWord + "냥" + punctuation; - } + if (jongseong === 16) { + return baseWord + "냥" + punctuation; } + } - return word; - }).join(' '); + return word; + }) + .join(" "); } export function nyaize(text: string): string { - for (let key in nyaWords2) { - text = text.replaceAll(key + ".", nyaWords2[key as keyof typeof nyaWords2] + "."); // yeah I gotta optimize these - text = text.replaceAll(key + ",", nyaWords2[key as keyof typeof nyaWords2] + ","); - text = text.replaceAll(key + "?", nyaWords2[key as keyof typeof nyaWords2] + "?"); - text = text.replaceAll(key + "!", nyaWords2[key as keyof typeof nyaWords2] + "!"); - text = text.replaceAll(key + ";", nyaWords2[key as keyof typeof nyaWords2] + ";"); - text = text.replaceAll(key + "~", nyaWords2[key as keyof typeof nyaWords2] + "~"); - text = text.replaceAll(key + "^", nyaWords2[key as keyof typeof nyaWords2] + "^"); - text = text.replaceAll(key + "@", nyaWords2[key as keyof typeof nyaWords2] + "@"); - text = text.replaceAll(key + "(", nyaWords2[key as keyof typeof nyaWords2] + "("); - text = text.replaceAll(key + ")", nyaWords2[key as keyof typeof nyaWords2] + ")"); - text = text.replaceAll(key + " ", nyaWords2[key as keyof typeof nyaWords2] + " "); + for (const key in nyaWords2) { + text = text.replaceAll( + key + ".", + nyaWords2[key as keyof typeof nyaWords2] + ".", + ); // yeah I gotta optimize these + text = text.replaceAll( + key + ",", + nyaWords2[key as keyof typeof nyaWords2] + ",", + ); + text = text.replaceAll( + key + "?", + nyaWords2[key as keyof typeof nyaWords2] + "?", + ); + text = text.replaceAll( + key + "!", + nyaWords2[key as keyof typeof nyaWords2] + "!", + ); + text = text.replaceAll( + key + ";", + nyaWords2[key as keyof typeof nyaWords2] + ";", + ); + text = text.replaceAll( + key + "~", + nyaWords2[key as keyof typeof nyaWords2] + "~", + ); + text = text.replaceAll( + key + "^", + nyaWords2[key as keyof typeof nyaWords2] + "^", + ); + text = text.replaceAll( + key + "@", + nyaWords2[key as keyof typeof nyaWords2] + "@", + ); + text = text.replaceAll( + key + "(", + nyaWords2[key as keyof typeof nyaWords2] + "(", + ); + text = text.replaceAll( + key + ")", + nyaWords2[key as keyof typeof nyaWords2] + ")", + ); + text = text.replaceAll( + key + " ", + nyaWords2[key as keyof typeof nyaWords2] + " ", + ); - if (text.endsWith(key)) { - text = text.slice(0, -1) + nyaWords2[key as keyof typeof nyaWords2]; - } + if (text.endsWith(key)) { + text = text.slice(0, -1) + nyaWords2[key as keyof typeof nyaWords2]; } + } - for (let key in nyaWords) { - text = text.replaceAll(key, nyaWords[key as keyof typeof nyaWords]); - } + for (const key in nyaWords) { + text = text.replaceAll(key, nyaWords[key as keyof typeof nyaWords]); + } - text = replacePunctuation(text); + text = replacePunctuation(text); - text = addNyangAtMWord(text); + text = addNyangAtMWord(text); - return text; -} \ No newline at end of file + return text; +} diff --git a/packages/utils/outputHandler.ts b/packages/utils/outputHandler.ts index 3c22776..413390c 100644 --- a/packages/utils/outputHandler.ts +++ b/packages/utils/outputHandler.ts @@ -1,42 +1,37 @@ import { dirname, join } from "path"; -import { mkdir, open, readFile } from "fs/promises" +import { mkdir, open, readFile } from "fs/promises"; export namespace OutputHandler { - export const LogCachePath = join( - process.cwd(), - "cache", - "log", - ); - export const ErrorLogPath = join(LogCachePath, "error.log" ); - export function getErrorOutput(...args: any[]) { - const timestamp = new Date().toISOString(); + export const LogCachePath = join(process.cwd(), "cache", "log"); + export const ErrorLogPath = join(LogCachePath, "error.log"); + export function getErrorOutput(...args: any[]) { + const timestamp = new Date().toISOString(); - const message = args - .map(arg => { - if (arg instanceof Error) { - return `${arg.name}: ${arg.message}\n${arg.stack}`; - } - if (typeof arg === 'object') { - return JSON.stringify(arg); - } - return String(arg); - }) - .join(' '); + const message = args + .map((arg) => { + if (arg instanceof Error) { + return `${arg.name}: ${arg.message}\n${arg.stack}`; + } + if (typeof arg === "object") { + return JSON.stringify(arg); + } + return String(arg); + }) + .join(" "); + return `[${timestamp}] ${message}`; + } + export async function errorLog(...args: any[]): Promise { + const output = getErrorOutput(...args); + console.log(output); - return `[${timestamp}] ${message}`; - } - export async function errorLog(...args: any[]): Promise { - const output = getErrorOutput(...args); - console.log(output); + await mkdir(dirname(ErrorLogPath), { recursive: true }); - await mkdir(dirname(ErrorLogPath), { recursive: true }); - - const fileHandle = await open(ErrorLogPath, "a"); - fileHandle.write(output + "\n"); - fileHandle.close(); - } - export async function getErrorLog(): Promise { - return (await readFile(ErrorLogPath)).toString(); - } + const fileHandle = await open(ErrorLogPath, "a"); + fileHandle.write(output + "\n"); + fileHandle.close(); + } + export async function getErrorLog(): Promise { + return (await readFile(ErrorLogPath)).toString(); + } } diff --git a/packages/utils/phoneNumberKorean.ts b/packages/utils/phoneNumberKorean.ts index 3040113..f7c8613 100644 --- a/packages/utils/phoneNumberKorean.ts +++ b/packages/utils/phoneNumberKorean.ts @@ -1,15 +1,17 @@ export default class PhoneNumberKorean { - static DigitName = [ "공", "일", "이", "삼", "사", "오", "육", "칠", "팔", "구" ]; - static Dash = " "; - - static convert(phone: string): string { - return phone.replace(/[\d\- \+]/g, (char: string) => { - if (char == "-") return PhoneNumberKorean.Dash; - if (char == " ") return " "; - if (char == "+") return "플러스"; - return PhoneNumberKorean.DigitName[ - parseInt(char) as number - ] ?? ""; - }) - } + // prettier-ignore + static DigitName = [ + "공", "일", "이", "삼", "사", + "오", "육", "칠", "팔", "구", + ]; + static Dash = " "; + + static convert(phone: string): string { + return phone.replace(/[\d\- +]/g, (char: string) => { + if (char == "-") return PhoneNumberKorean.Dash; + if (char == " ") return " "; + if (char == "+") return "플러스"; + return PhoneNumberKorean.DigitName[parseInt(char) as number] ?? ""; + }); + } } diff --git a/packages/utils/requireDirectory.ts b/packages/utils/requireDirectory.ts index 8009d90..d22a7c4 100644 --- a/packages/utils/requireDirectory.ts +++ b/packages/utils/requireDirectory.ts @@ -1,13 +1,13 @@ -import { readdirSync } from "fs"; import { readdir } from "fs/promises"; import { join } from "path"; export async function requireDirectory(directory: string): Promise { - const requireFiles = (await readdir(directory)).filter(file => file.endsWith(".js")); - return requireFiles.map(file => require(join(directory, file)).default as T); -} - -export function requireDirectorySync(directory: string): T[] { - const requireFiles = readdirSync(directory).filter(file => file.endsWith(".js")); - return requireFiles.map(file => require(join(directory, file)).default as T).filter(x=>x); + const requireFiles = (await readdir(directory)).filter((file) => + file.endsWith(".js"), + ); + return await Promise.all( + requireFiles.map( + async (file) => (await import(join(directory, file))).default as T, + ), + ); } diff --git a/packages/utils/saferKorean.ts b/packages/utils/saferKorean.ts index f88650d..fc36308 100644 --- a/packages/utils/saferKorean.ts +++ b/packages/utils/saferKorean.ts @@ -1,388 +1,393 @@ -import CallingNumberKorean from "./callingNumberKorean"; -import FloatKorean from "./floatKorean"; -import IntegerKorean from "./integerKorean"; -import PhoneNumberKorean from "./phoneNumberKorean"; +import CallingNumberKorean from "./callingNumberKorean.js"; +import FloatKorean from "./floatKorean.js"; +import IntegerKorean from "./integerKorean.js"; +import PhoneNumberKorean from "./phoneNumberKorean.js"; +import EmojiDescriptions from "./emoji-descriptions.json" with { type: "json" }; export const IsolatedSymbolMap = { - "?": "물음표", - "!": "느낌표", - "'": "쿼트", - "\"": "더블쿼트", -} + "?": "물음표", + "!": "느낌표", + "'": "쿼트", + '"': "더블쿼트", +}; export const SymbolMap = { - "%": "퍼센트", - "$": "달러", - "^": "캐럿", - "&": "엔드", - "*": "스타", - "#": "샵", - "@": "엣", - ".": "쩜", - "-": "마이너스", - "+": "플러스", - "_": "언더바", - "=": "이퀄", - "/": "슬래쉬", - "~": "물결표", - "\\": "역슬래쉬", - "♡": "하트 ", - "|": "", - ">": "", - "<": "", - ":": "콜론", - ";": "세미콜론" + "%": "퍼센트", + $: "달러", + "^": "캐럿", + "&": "엔드", + "*": "스타", + "#": "샵", + "@": "엣", + ".": "쩜", + "-": "마이너스", + "+": "플러스", + _: "언더바", + "=": "이퀄", + "/": "슬래쉬", + "~": "물결표", + "\\": "역슬래쉬", + "♡": "하트 ", + "|": "", + ">": "", + "<": "", + ":": "콜론", + ";": "세미콜론", }; export const VersionPostfix = { - "a": "알파", - "b": "베타", + a: "알파", + b: "베타", }; export const LangPrefixes = { - "typescript": "타입스크립트", - "javascript": "자바스크립트", - "java": "자바", - "kotlin": "코틀린", - "rust": "러스트", - "lua": "루아", - "json": "제이슨", - "yaml": "야믈", - "yml": "야믈", - "toml": "토믈", - "xml": "엑스엠엘", - "julia": "줄리아", - "matlab": "매트랩", - "erlang": "얼랭", - "elxir": "엘릭서", - "zig": "지그", - "txt": "텍스트", - "vim": "빔", - "perl": "펄", - "php": "피에이치피", - "lisp": "리스프", - "postscript": "포스트스크립트", - "ghostscript": "고스트스크립트", - "fortran": "포트란", - "algol": "알골", - "scala": "스칼라", - "haskell": "하스켈", - "basic": "베이직", - - "cpp": "씨플플", - "c++": "씨플플", - "csharp": "씨샵", - "cs": "씨샵", - "c#": "씨샵", - "c": "씨", - "h": "헤더", + typescript: "타입스크립트", + javascript: "자바스크립트", + java: "자바", + kotlin: "코틀린", + rust: "러스트", + lua: "루아", + json: "제이슨", + yaml: "야믈", + yml: "야믈", + toml: "토믈", + xml: "엑스엠엘", + julia: "줄리아", + matlab: "매트랩", + erlang: "얼랭", + elxir: "엘릭서", + zig: "지그", + txt: "텍스트", + vim: "빔", + perl: "펄", + php: "피에이치피", + lisp: "리스프", + postscript: "포스트스크립트", + ghostscript: "고스트스크립트", + fortran: "포트란", + algol: "알골", + scala: "스칼라", + haskell: "하스켈", + basic: "베이직", - "d": "디", - "awk": "에이더블류케이", - "pl": "펄", - "pwsh": "파워쉘", - "powershell": "파워쉘", - "cmd": "씨엠디", - "sh": "쉘", - "ps1": "파워셀", - "bat": "배치파일", - "bash": "베시스크립트", - "tex": "텍", - "dart": "다트", - "go": "고랭", - "python": "파이썬", - "swift": "스위프트", - "css": "씨에스에스", - "html": "에이치티엠엘", + cpp: "씨플플", + "c++": "씨플플", + csharp: "씨샵", + cs: "씨샵", + "c#": "씨샵", + c: "씨", + h: "헤더", - "latex": "레이텍", - "md": "마크다운", - "markdown": "마크다운", + d: "디", + awk: "에이더블류케이", + pl: "펄", + pwsh: "파워쉘", + powershell: "파워쉘", + cmd: "씨엠디", + sh: "쉘", + ps1: "파워셀", + bat: "배치파일", + bash: "베시스크립트", + tex: "텍", + dart: "다트", + go: "고랭", + python: "파이썬", + swift: "스위프트", + css: "씨에스에스", + html: "에이치티엠엘", - "py": "파이썬", - "hs": "하스켈", - "rs": "러스트", - "kt": "코틀린", - "js": "자스", - "ts": "타스", - "tsx": "리액트 타입스크립트", - "jsx": "리액트 자바스크립트", - "an": "에이엔", - "parlance": "팔렌스", + latex: "레이텍", + md: "마크다운", + markdown: "마크다운", + + py: "파이썬", + hs: "하스켈", + rs: "러스트", + kt: "코틀린", + js: "자스", + ts: "타스", + tsx: "리액트 타입스크립트", + jsx: "리액트 자바스크립트", + an: "에이엔", + parlance: "팔렌스", }; -export const LangPrefixMaxLength = (()=>{ - let max = 0; - for (const key in LangPrefixes) { - max = Math.max(key.length, max); - } - return max; +export const LangPrefixMaxLength = (() => { + let max = 0; + for (const key in LangPrefixes) { + max = Math.max(key.length, max); + } + return max; })(); export const ChoseongMap = { - "ㄱ": "기역", - "ㄴ": "니은", - "ㄷ": "디귿", - "ㄹ": "리을", - "ㅁ": "미음", - "ㅂ": "비읍", - "ㅅ": "시옷", - "ㅇ": "이응", - "ㅈ": "지읒", - "ㅊ": "치읓", - "ㅋ": "키읔", - "ㅌ": "티읕", - "ㅍ": "피읖", - "ㅎ": "히읗", - "ㄲ": "쌍기역", - "ㄸ": "쌍디귿", - "ㅃ": "쌍비읍", - "ㅆ": "쌍시옷", - "ㅉ": "쌍지읒", + ㄱ: "기역", + ㄴ: "니은", + ㄷ: "디귿", + ㄹ: "리을", + ㅁ: "미음", + ㅂ: "비읍", + ㅅ: "시옷", + ㅇ: "이응", + ㅈ: "지읒", + ㅊ: "치읓", + ㅋ: "키읔", + ㅌ: "티읕", + ㅍ: "피읖", + ㅎ: "히읗", + ㄲ: "쌍기역", + ㄸ: "쌍디귿", + ㅃ: "쌍비읍", + ㅆ: "쌍시옷", + ㅉ: "쌍지읒", }; export const SIPrefix = { - "k": "킬로", - "ki": "키비", - "m": "메가", - "mi": "메비", - "g": "기가", - "gi": "기비", - "t": "테라", - "ti": "테비", - "p": "페타", - "pi": "페비", - "e": "엑사", - "ei": "엑시", - "z": "제타", - "zi": "제비", - "y": "요타", - "yi": "요비", + k: "킬로", + ki: "키비", + m: "메가", + mi: "메비", + g: "기가", + gi: "기비", + t: "테라", + ti: "테비", + p: "페타", + pi: "페비", + e: "엑사", + ei: "엑시", + z: "제타", + zi: "제비", + y: "요타", + yi: "요비", }; export const LiterPrefix = { - "m": "밀리", - "": "", + m: "밀리", + "": "", }; export const MeterPrefix = { - "m": "밀리", - "c": "센치", - "": "", - "k": "킬로", + m: "밀리", + c: "센치", + "": "", + k: "킬로", }; export const GIFMap = { - "tenor.com/view/majo-no-tabitabi-the-journey-of-elaina-elaina-windy-hair-gif-19187698": "화난 일레이나", - "tenor.com/view/majo-no-tabitabi-the-journey-of-elaina-elaina-sparkle-amazed-gif-18827847": "일레이나 반짝반짝!", - "images-ext-1.discordapp.net/external/C3xPFuUxs16jY25AR3NvsIDezaOtib9wozhLBWejZk4/https/media.tenor.com/bUd8mk4ufwsAAAPo/anime-disappointment.mp4": "일레이나 절래절래", - "images-ext-1.discordapp.net/external/SXv4qgpy2r1Gx-dNxhcfJle6AXDaH_SToRjEBYYaup0/https/media.tenor.com/nDDxJc4FDwEAAAPo/cute.mp4": "일레이나 끄덕", - "tenor.com/view/majo-no-tabitabi-the-journey-of-elaina-elaina-what-gif-19011602": "당황한 일레이나", - "images-ext-1.discordapp.net/external/2R41WcvNJwYMD69UKls2cDa_hEL-rzCRCFvOi2DDOVo/https/media.tenor.com/sU3RCOixDbgAAAPo/majo-no-tabitabi-the-journey-of-elaina.mp4": "일레이나 손짓", + "tenor.com/view/majo-no-tabitabi-the-journey-of-elaina-elaina-windy-hair-gif-19187698": + "화난 일레이나", + "tenor.com/view/majo-no-tabitabi-the-journey-of-elaina-elaina-sparkle-amazed-gif-18827847": + "일레이나 반짝반짝!", + "images-ext-1.discordapp.net/external/C3xPFuUxs16jY25AR3NvsIDezaOtib9wozhLBWejZk4/https/media.tenor.com/bUd8mk4ufwsAAAPo/anime-disappointment.mp4": + "일레이나 절래절래", + "images-ext-1.discordapp.net/external/SXv4qgpy2r1Gx-dNxhcfJle6AXDaH_SToRjEBYYaup0/https/media.tenor.com/nDDxJc4FDwEAAAPo/cute.mp4": + "일레이나 끄덕", + "tenor.com/view/majo-no-tabitabi-the-journey-of-elaina-elaina-what-gif-19011602": + "당황한 일레이나", + "images-ext-1.discordapp.net/external/2R41WcvNJwYMD69UKls2cDa_hEL-rzCRCFvOi2DDOVo/https/media.tenor.com/sU3RCOixDbgAAAPo/majo-no-tabitabi-the-journey-of-elaina.mp4": + "일레이나 손짓", }; -export const UnicodeEmojis = { - "㎢": "제곱킬로미터", - "㎡": "제곱미터", - "↑": "위쪽 화살표", "↓": "아래쪽 화살표", - "←": "왼쪽 화살표", "→": "오른쪽 화살표", - "↔": "좌우 화살표", - "↖": "왼쪽 위 화살표", "↗": "오른쪽 위 화살표", - "↘": "오른쪽 아래 화살표", "↙": "왼쪽 아래 화살표", - "🎀": "리본", "🐱": "고양이", "✨": "반짝임", "🍞": "빵", - "🧸": "인형", "🍓": "딸기", "🌸": "벚꽃", "🍰": "조각 케이크", - "🐾": "발자국", "👑": "왕관", "🦄": "유니콘", "🐰": "토끼", - "🦊": "여우", "🐻": "곰", "🐼": "판다", "🐥": "아기 병아리", - "🦋": "나비", "🌹": "장미", "🌷": "튤립", "🍀": "네잎클로버", - "🍁": "단풍잎", "🌙": "초승달", "⭐": "별", "🌈": "무지개", - "🌋": "화산", "🌊": "파도", "🔮": "수정구슬", "🍬": "사탕", - "🍭": "막대사탕", "🍫": "초콜릿", "🍩": "도넛", "🍪": "쿠키", - "🍨": "아이스크림", "🥞": "팬케이크", "🍎": "빨간 사과", - "🍒": "체리", "🍑": "복숭아", "🍇": "포도", "🧁": "컵케이크", - "🍋": "레몬", "🍌": "바나나", "🥑": "아보카도", "🥕": "당근", - "🍕": "피자", "🍔": "햄버거", "🍟": "감자튀김", "🍿": "팝콘", - "🧂": "소금", "🎈": "풍선", "🎉": "폭죽", "🎬": "슬레이트", - "🎁": "선물", "🎫": "티켓", "🏆": "트로피", "🎨": "팔레트", - "🎤": "마이크", "📱": "휴대전화", "🎼": "높은음자리표", - "🎸": "기타", "🎧": "헤드폰", "🎹": "키보드", "💻": "노트북", - "⌚": "시계", "📷": "카메라", "🔍": "돋보기", "💡": "전구", - "🕯️": "양초", "📜": "두루마리", "🔑": "열쇠", "🔒": "자물쇠", - "🔔": "종", "📣": "메가폰", "📦": "상자", "✉️": "편지", - "📌": "압정", "✂️": "가위", "🩹": "반창고", "🧬": "DNA", - "🧪": "시험관", "🔭": "망원경", "🚀": "로켓", "🛸": "UFO", - "🚲": "자전거", "🛹": "스케이트보드", "⚓": "닻", "⛺": "텐트", - "🧭": "나침반", "🗺️": "세계지도", "🏡": "집", "🏰": "성", - "🎡": "관람차", "🎠": "회전목마", "⛲": "분수", "💎": "보석", - "🪞": "거울", "💄": "립스틱", -} -export const UnicodeEmojisRegex = new RegExp( - "(" + - Object.keys(UnicodeEmojis).join(")|(") - + ")", "g" +export const UnicodeSymbols = { + "㎢": "제곱킬로미터", + "㎡": "제곱미터", + "↑": "위쪽 화살표", + "↓": "아래쪽 화살표", + "←": "왼쪽 화살표", + "→": "오른쪽 화살표", + "↔": "좌우 화살표", + "↖": "왼쪽 위 화살표", + "↗": "오른쪽 위 화살표", + "↘": "오른쪽 아래 화살표", + "↙": "왼쪽 아래 화살표", +}; +export const UnicodeSymbolsRegex = new RegExp( + "[" + Object.keys(UnicodeSymbols).join() + "]", + "gu", ); export function processDots(input: string): string { - return input.replace(/[\.,]+$/, "") - .replace(/[\.,]{2,}/g, "") - .replace(/[\.,]\s/g, " "); + return input + .replace(/[.,]+$/, "") + .replace(/[.,]{2,}/g, "") + .replace(/[.,]\s/g, " "); } export function saferKorean(input: string): string { - return processDots(input.normalize() + " ") - // Process isolated symbols - .replace(/^[\?\!\'\"]+ $/, (total)=>( - [...total].map(element => IsolatedSymbolMap[ - element as keyof typeof IsolatedSymbolMap - ]).join("") - )) - .replace(/\s\|\|\s/g, " 오얼 ") - .replace(/\s\&\&\s/g, " 엔드 ") + return ( + processDots(input.normalize() + " ") + // Process isolated symbols + .replace(/^[?!'"]+ $/, (total) => + [...total] + .map( + (element) => + IsolatedSymbolMap[element as keyof typeof IsolatedSymbolMap], + ) + .join(""), + ) + .replace(/\s\|\|\s/g, " 오얼 ") + .replace(/\s&&\s/g, " 엔드 ") - // Process codeblock - .replace(/\`\`\`([\s\S]*?)\`\`\`/g, (_, content: string)=>{ - const code = content.substring(0, LangPrefixMaxLength).toLowerCase(); - let lang = ""; - for (const [key, value] of Object.entries(LangPrefixes)) { - if (code.startsWith(key + "\n")) { - lang = value + " "; - break; - } - } - return lang + "코드블럭"; - }) + // Process codeblock + .replace(/```([\s\S]*?)```/g, (_, content: string) => { + const code = content.substring(0, LangPrefixMaxLength).toLowerCase(); + let lang = ""; + for (const [key, value] of Object.entries(LangPrefixes)) { + if (code.startsWith(key + "\n")) { + lang = value + " "; + break; + } + } + return lang + "코드블럭"; + }) - // Process link - .replace(/[hH][tT]{2}[pP][sS]?:\/\/(\S+)/g, (_, url: string) => { - const mapped = GIFMap[url as keyof typeof GIFMap] as (string | undefined); - if (mapped) return mapped; + // Process link + .replace(/[hH][tT]{2}[pP][sS]?:\/\/(\S+)/g, (_, url: string) => { + const mapped = GIFMap[url as keyof typeof GIFMap] as string | undefined; + if (mapped) return mapped; - if (url.startsWith("tenor.com/view")) { - return "움짤!"; - } - return "링크"; - }) + if (url.startsWith("tenor.com/view")) { + return "움짤!"; + } + return "링크"; + }) - // Process koreans - .replace(/[아ㅏ]{3,}/g, "아아아") - .replace(/ㄹㅇ/g, (content: string) => { - return "리얼".repeat( - Math.min(Math.floor(content.length / 2), 2) + // Process koreans + .replace(/[아ㅏ]{3,}/g, "아아아") + .replace(/ㄹㅇ/g, (content: string) => { + return "리얼".repeat(Math.min(Math.floor(content.length / 2), 2)); + }) + .replace(/(ㅇㄴ)+/g, (content: string) => { + return "아니".repeat(Math.min(Math.floor(content.length / 2), 2)); + }) + .replace(/(ㅇㅎ)+/g, (content: string) => { + return "아하".repeat(Math.min(Math.floor(content.length / 2), 2)); + }) + .replace(/(ㅇㅋ)+/g, (content: string) => { + return "오키".repeat(Math.min(Math.floor(content.length / 2), 2)); + }) + .replace(/(ㅊㅋ)+/g, (content: string) => { + return "추카".repeat(Math.min(Math.floor(content.length / 2), 2)); + }) + .replace(/ㄱ+/g, (content: string) => { + if (content.length == 2) { + return "고고"; + } else if (content.length == 3) { + return "고고고"; + } + return content; + }) + .replace(/ㅋ{2,}/g, (content) => "크".repeat(content.length)) + .replace(/ㅌ{2,}/g, "틔틔") + .replace(/ㄷ{2,}/g, "덜덜") + .replace(/ㄴ{2,}/g, "노노") + .replace(/ㅇ{2,}/g, "응응") + .replace(/ㅊ{2,}/g, "추추") + .replace(/ㅠ{2,}/g, "유유") + .replace(/ㅜ{2,}/g, "우우") + .replace( + /[ㄱ-ㅎㄲㄸㅃㅆㅉ]/g, + (char: string) => ChoseongMap[char as keyof typeof ChoseongMap], + ) + + // Process number, unit + .replace( + /(\+\d+[\s-]+)?([\d-]+)/g, + (_, prefix: string | undefined, phone: string) => { + const all = (prefix ?? "") + phone; + if (!phone.includes("-")) return all; + return PhoneNumberKorean.convert(all); + }, + ) + .replace( + /([\d,]+)([kKMmgGtTpPeEzZyY][iI]?)[bB]/g, + (_, num: string, mod: string) => { + // 10kib => 십키비바이트 + num = IntegerKorean.convertFromString(num); + mod = SIPrefix[mod.toLowerCase() as keyof typeof SIPrefix]; + return `${num} ${mod}바이트 `; + }, + ) + .replace(/([\d,]+)([m]?)[lL]\s/g, (_, num: string, mod: string) => { + // 10l => 십리터 + num = IntegerKorean.convertFromString(num); + mod = LiterPrefix[mod as keyof typeof LiterPrefix]; + return `${num} ${mod}리터 `; + }) + .replace(/([\d,]+)([mck]?)m\s/g, (_, num: string, mod: string) => { + // 10m => 십미터 + num = IntegerKorean.convertFromString(num); + mod = MeterPrefix[mod as keyof typeof MeterPrefix]; + return `${num} ${mod}미터 `; + }) + .replace( + /([\d.]+)\s*([개살시평명])/g, + (_, num: string, postfix: string) => { + // 10명 => 열명 + if (num.includes(".")) { + return num + postfix; + } + const intNum = parseInt(num); + if (CallingNumberKorean.canConvert(intNum)) { + return CallingNumberKorean.convert(intNum) + postfix; + } else { + return IntegerKorean.convertFromString(num) + postfix; + } + }, + ) + .replace(/[\d,]+/g, (num: string) => { + // 1,000 원 => 천원 + if (!num.includes(",")) return num; + return IntegerKorean.convertFromString(num); + }) + .replace( + /(v?)([\d.]+)([ab]?)/g, + (_, suffix: string, num: string, postfix: string) => { + const dotCount = [...num.matchAll(/\./g)].length; + const hasNoSuffix = suffix == ""; + + if (hasNoSuffix && dotCount == 0) { + // 일반 숫자는 인트로 읽음 + return IntegerKorean.convertFromString(num) + postfix; + } else if (hasNoSuffix && dotCount == 1) { + // 소수는 . 앞은 인트로, 뒤는 플로트로 읽음 + const [intPart, floatPart] = num.split(/\./); + return ( + IntegerKorean.convertFromString(intPart ?? "") + + "쩜" + + FloatKorean.convert(floatPart ?? "") + + postfix ); - }) - .replace(/(ㅇㄴ)+/g, (content: string) => { - return "아니".repeat( - Math.min(Math.floor(content.length / 2), 2) + } else if ((suffix == "v" || postfix.length) && dotCount > 1) { + // 버전표기는 버전을 붙여서 + return ( + "버전" + + FloatKorean.convert(num) + + (VersionPostfix[postfix as keyof typeof VersionPostfix] ?? "") ); - }) - .replace(/(ㅇㅎ)+/g, (content: string) => { - return "아하".repeat( - Math.min(Math.floor(content.length / 2), 2) - ); - }) - .replace(/(ㅇㅋ)+/g, (content: string) => { - return "오키".repeat( - Math.min(Math.floor(content.length / 2), 2) - ); - }) - .replace(/(ㅊㅋ)+/g, (content: string) => { - return "추카".repeat( - Math.min(Math.floor(content.length / 2), 2) - ); - }) - .replace(/ㄱ+/g, (content: string) => { - if (content.length == 2) { - return "고고"; - } else if (content.length == 3) { - return "고고고"; - } - return content; - }) - .replace(/ㅋ{2,}/g, (content) => "크".repeat(content.length)) - .replace(/ㅌ{2,}/g, "틔틔") - .replace(/ㄷ{2,}/g, "덜덜") - .replace(/ㄴ{2,}/g, "노노") - .replace(/ㅇ{2,}/g, "응응") - .replace(/ㅊ{2,}/g, "추추") - .replace(/ㅠ{2,}/g, "유유") - .replace(/ㅜ{2,}/g, "우우") - .replace(/[ㄱ-ㅎㄲㄸㅃㅆㅉ]/g, (char: string) => ChoseongMap[char as keyof typeof ChoseongMap]) + } else { + // 모든 경우에 속하지 않으면 영일이삼사 형태로 읽음 + // (예: 111.111.111.111 ip address) + return FloatKorean.convert(num) + postfix; + } + }, + ) - // Process number, unit - .replace(/(\+\d+[\s\-]+)?([\d\-]+)/g, (_, prefix: string | undefined, phone: string) => { - const all = (prefix ?? "") + phone; - if (!phone.includes("-")) return all; - return PhoneNumberKorean.convert(all); - }) - .replace(/([\d,]+)([kKMmgGtTpPeEzZyY][iI]?)[bB]/g, (_, num: string, mod: string) => { - // 10kib => 십키비바이트 - num = IntegerKorean.convertFromString(num); - mod = SIPrefix[mod.toLowerCase() as keyof typeof SIPrefix]; - return `${num} ${mod}바이트 `; - }) - .replace(/([\d,]+)([m]?)[lL]\s/g, (_, num: string, mod: string) => { - // 10l => 십리터 - num = IntegerKorean.convertFromString(num); - mod = LiterPrefix[mod as keyof typeof LiterPrefix]; - return `${num} ${mod}리터 `; - }) - .replace(/([\d,]+)([mck]?)m\s/g, (_, num: string, mod: string) => { - // 10m => 십미터 - num = IntegerKorean.convertFromString(num); - mod = MeterPrefix[mod as keyof typeof MeterPrefix]; - return `${num} ${mod}미터 `; - }) - .replace(/([\d\.]+)\s*([개살시평명])/g, (_, num: string, postfix: string)=>{ - // 10명 => 열명 - if (num.includes(".")) { - return num + postfix; - } - const intNum = parseInt(num) - if (CallingNumberKorean.canConvert(intNum)) { - return CallingNumberKorean.convert(intNum) + postfix; - } else { - return IntegerKorean.convertFromString(num) + postfix; - } - }) - .replace(/[\d,]+/g, (num: string) => { - // 1,000 원 => 천원 - if (!num.includes(",")) return num; - return IntegerKorean.convertFromString(num); - }) - .replace(/(v?)([\d\.]+)([ab]?)/g, (_, suffix: string, num: string, postfix: string) => { - const dotCount = [...num.matchAll(/\./g)].length; - const hasNoSuffix = suffix == ""; + // Process symbol + .replace( + /[%^&*#@.\-+_=/\\♡$|:;><]/g, + (t) => SymbolMap[t as keyof typeof SymbolMap], + ) + .replace(/([?!]+)/g, (_, content: string): string => content[0] ?? "") + .replace(/[ \t\f\r]+/g, " ") - if (hasNoSuffix && dotCount == 0) { - // 일반 숫자는 인트로 읽음 - return IntegerKorean.convertFromString(num) + postfix; - } else if (hasNoSuffix && dotCount == 1) { - // 소수는 . 앞은 인트로, 뒤는 플로트로 읽음 - const [intPart, floatPart] = num.split(/\./); - return ( - IntegerKorean.convertFromString(intPart) - + "쩜" - + FloatKorean.convert(floatPart) - + postfix - ) - } else if ((suffix == "v" || postfix.length) && dotCount > 1) { - // 버전표기는 버전을 붙여서 - return ( - "버전" - + FloatKorean.convert(num) - + (VersionPostfix[ - postfix as keyof typeof VersionPostfix - ] ?? "") - ); - } else { - // 모든 경우에 속하지 않으면 영일이삼사 형태로 읽음 - // (예: 111.111.111.111 ip address) - return FloatKorean.convert(num) + postfix; - } - }) - - // Process symbol - .replace(/[\%\^\&\*\#\@\.\-\+\_\=\/\\♡\$\|\:\;\>\<]/g, (t) => ( - SymbolMap[t as keyof typeof SymbolMap] - )) - .replace(/([\?\!]+)/g, (_, content: string) => content[0]) - .replace(/[ \t\f\r]+/g, " ") - - // Process emoji - .replace(UnicodeEmojisRegex, (content: string) => (UnicodeEmojis[content as keyof typeof UnicodeEmojis] ?? content)) - .replace(/\p{Emoji}/u, " 이모지 ") - .trim() + // Process emoji + .replace( + UnicodeSymbolsRegex, + (content: string) => + UnicodeSymbols[content as keyof typeof UnicodeSymbols] ?? content, + ) + .replace(/\p{Extended_Pictographic}/gu, (content: string) => { + return ( + EmojiDescriptions[content as keyof typeof EmojiDescriptions] ?? + content + ); + }) + .replace(/\p{Emoji}/u, " 이모지 ") + .trim() + ); } diff --git a/prisma.config.ts b/prisma.config.ts index 9144291..831a20f 100644 --- a/prisma.config.ts +++ b/prisma.config.ts @@ -4,11 +4,11 @@ import "dotenv/config"; import { defineConfig } from "prisma/config"; export default defineConfig({ - schema: "prisma/schema.prisma", - migrations: { - path: "prisma/migrations", - }, - datasource: { - url: process.env["DATABASE_URL"], - }, + schema: "prisma/schema.prisma", + migrations: { + path: "prisma/migrations", + }, + datasource: { + url: process.env["DATABASE_URL"], + }, }); diff --git a/tsconfig.json b/tsconfig.json index 75e4641..231caa2 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,14 +1,33 @@ { + "include": ["packages/**/*.ts", "packages/**/*.json"], "compilerOptions": { - "target": "ES6", - "module": "commonjs", - "lib": [ "ES2021", "DOM", "DOM.Iterable" ], - "moduleResolution": "node", - "outDir": "dist", + "outDir": "./dist", + "rootDir": "./packages", + + "resolveJsonModule": true, + "module": "esnext", + "target": "es2023", + "moduleDetection": "force", + "moduleResolution": "bundler", + "composite": true, // For incremental build + "esModuleInterop": true, + "skipLibCheck": true, + "verbatimModuleSyntax": true, + "isolatedModules": true, + "noUncheckedSideEffectImports": true, + "types": [], + + // Other Outputs + "declaration": true, // .d.ts + "declarationMap": true, // source map + "sourceMap": true, + + // Stricter Typechecking Options "strict": true, - "exactOptionalPropertyTypes": false, - "allowSyntheticDefaultImports": true, - "skipLibCheck": true + "noUncheckedIndexedAccess": true, + "exactOptionalPropertyTypes": true }, - "include": ["./packages/**/*"] -} \ No newline at end of file + "buildOptions": { + "incremental": true + } +} diff --git a/tsconfig.tsbuildinfo b/tsconfig.tsbuildinfo new file mode 100644 index 0000000..90726c9 --- /dev/null +++ b/tsconfig.tsbuildinfo @@ -0,0 +1 @@ +{"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.dom.d.ts","./node_modules/typescript/lib/lib.dom.iterable.d.ts","./node_modules/typescript/lib/lib.dom.asynciterable.d.ts","./node_modules/typescript/lib/lib.webworker.importscripts.d.ts","./node_modules/typescript/lib/lib.scripthost.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.arraybuffer.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.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.disposable.d.ts","./node_modules/typescript/lib/lib.esnext.float16.d.ts","./node_modules/typescript/lib/lib.decorators.d.ts","./node_modules/typescript/lib/lib.decorators.legacy.d.ts","./node_modules/typescript/lib/lib.es2023.full.d.ts","./node_modules/@types/node/compatibility/iterators.d.ts","./node_modules/@types/node/globals.typedarray.d.ts","./node_modules/@types/node/buffer.buffer.d.ts","./node_modules/@types/node/globals.d.ts","./node_modules/@types/node/web-globals/abortcontroller.d.ts","./node_modules/@types/node/web-globals/blob.d.ts","./node_modules/@types/node/web-globals/console.d.ts","./node_modules/@types/node/web-globals/crypto.d.ts","./node_modules/@types/node/web-globals/domexception.d.ts","./node_modules/@types/node/web-globals/encoding.d.ts","./node_modules/@types/node/web-globals/events.d.ts","./node_modules/undici-types/utility.d.ts","./node_modules/undici-types/header.d.ts","./node_modules/undici-types/readable.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-stats.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/h2c-client.d.ts","./node_modules/undici-types/agent.d.ts","./node_modules/undici-types/mock-interceptor.d.ts","./node_modules/undici-types/mock-call-history.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/snapshot-agent.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/cache-interceptor.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/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/web-globals/fetch.d.ts","./node_modules/@types/node/web-globals/importmeta.d.ts","./node_modules/@types/node/web-globals/messaging.d.ts","./node_modules/@types/node/web-globals/navigator.d.ts","./node_modules/@types/node/web-globals/performance.d.ts","./node_modules/@types/node/web-globals/storage.d.ts","./node_modules/@types/node/web-globals/streams.d.ts","./node_modules/@types/node/web-globals/timers.d.ts","./node_modules/@types/node/web-globals/url.d.ts","./node_modules/@types/node/assert.d.ts","./node_modules/@types/node/assert/strict.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/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/inspector.generated.d.ts","./node_modules/@types/node/inspector/promises.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/path/posix.d.ts","./node_modules/@types/node/path/win32.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/quic.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/sqlite.d.ts","./node_modules/@types/node/stream.d.ts","./node_modules/@types/node/stream/consumers.d.ts","./node_modules/@types/node/stream/promises.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/test/reporters.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/util/types.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/index.d.ts","./node_modules/dotenv/lib/main.d.ts","./packages/env.ts","./node_modules/@sapphire/shapeshift/dist/esm/index.d.mts","./node_modules/discord-api-types/globals.d.ts","./node_modules/discord-api-types/rest/common.d.ts","./node_modules/discord-api-types/payloads/common.d.ts","./node_modules/discord-api-types/utils/internals.d.ts","./node_modules/discord-api-types/payloads/v10/permissions.d.ts","./node_modules/discord-api-types/payloads/v10/user.d.ts","./node_modules/discord-api-types/payloads/v10/emoji.d.ts","./node_modules/discord-api-types/payloads/v10/channel.d.ts","./node_modules/discord-api-types/payloads/v10/gateway.d.ts","./node_modules/discord-api-types/payloads/v10/oauth2.d.ts","./node_modules/discord-api-types/payloads/v10/sticker.d.ts","./node_modules/discord-api-types/payloads/v10/guild.d.ts","./node_modules/discord-api-types/payloads/v10/poll.d.ts","./node_modules/discord-api-types/payloads/v10/message.d.ts","./node_modules/discord-api-types/payloads/v10/monetization.d.ts","./node_modules/discord-api-types/payloads/v10/_interactions/responses.d.ts","./node_modules/discord-api-types/payloads/v10/_interactions/base.d.ts","./node_modules/discord-api-types/payloads/v10/_interactions/_applicationCommands/_chatInput/shared.d.ts","./node_modules/discord-api-types/payloads/v10/_interactions/_applicationCommands/_chatInput/base.d.ts","./node_modules/discord-api-types/payloads/v10/_interactions/_applicationCommands/_chatInput/attachment.d.ts","./node_modules/discord-api-types/payloads/v10/_interactions/_applicationCommands/_chatInput/boolean.d.ts","./node_modules/discord-api-types/payloads/v10/_interactions/_applicationCommands/_chatInput/channel.d.ts","./node_modules/discord-api-types/payloads/v10/_interactions/_applicationCommands/_chatInput/integer.d.ts","./node_modules/discord-api-types/payloads/v10/_interactions/_applicationCommands/_chatInput/mentionable.d.ts","./node_modules/discord-api-types/payloads/v10/_interactions/_applicationCommands/_chatInput/number.d.ts","./node_modules/discord-api-types/payloads/v10/_interactions/_applicationCommands/_chatInput/role.d.ts","./node_modules/discord-api-types/payloads/v10/_interactions/_applicationCommands/_chatInput/string.d.ts","./node_modules/discord-api-types/payloads/v10/_interactions/_applicationCommands/_chatInput/subcommand.d.ts","./node_modules/discord-api-types/payloads/v10/_interactions/_applicationCommands/_chatInput/subcommandGroup.d.ts","./node_modules/discord-api-types/payloads/v10/_interactions/_applicationCommands/_chatInput/user.d.ts","./node_modules/discord-api-types/payloads/v10/_interactions/_applicationCommands/internals.d.ts","./node_modules/discord-api-types/payloads/v10/_interactions/_applicationCommands/chatInput.d.ts","./node_modules/discord-api-types/payloads/v10/_interactions/_applicationCommands/contextMenu.d.ts","./node_modules/discord-api-types/payloads/v10/_interactions/_applicationCommands/entryPoint.d.ts","./node_modules/discord-api-types/payloads/v10/_interactions/_applicationCommands/permissions.d.ts","./node_modules/discord-api-types/payloads/v10/_interactions/applicationCommands.d.ts","./node_modules/discord-api-types/payloads/v10/_interactions/autocomplete.d.ts","./node_modules/discord-api-types/payloads/v10/_interactions/messageComponents.d.ts","./node_modules/discord-api-types/payloads/v10/_interactions/modalSubmit.d.ts","./node_modules/discord-api-types/payloads/v10/_interactions/ping.d.ts","./node_modules/discord-api-types/payloads/v10/interactions.d.ts","./node_modules/discord-api-types/payloads/v10/teams.d.ts","./node_modules/discord-api-types/payloads/v10/webhook.d.ts","./node_modules/discord-api-types/payloads/v10/application.d.ts","./node_modules/discord-api-types/payloads/v10/autoModeration.d.ts","./node_modules/discord-api-types/payloads/v10/guildScheduledEvent.d.ts","./node_modules/discord-api-types/payloads/v10/stageInstance.d.ts","./node_modules/discord-api-types/payloads/v10/auditLog.d.ts","./node_modules/discord-api-types/payloads/v10/invite.d.ts","./node_modules/discord-api-types/payloads/v10/soundboard.d.ts","./node_modules/discord-api-types/_generated_/rest/v10/interfaces.d.ts","./node_modules/discord-api-types/rest/v10/application.d.ts","./node_modules/discord-api-types/rest/v10/auditLog.d.ts","./node_modules/discord-api-types/rest/v10/autoModeration.d.ts","./node_modules/discord-api-types/rest/v10/poll.d.ts","./node_modules/discord-api-types/rest/v10/channel.d.ts","./node_modules/discord-api-types/rest/v10/emoji.d.ts","./node_modules/discord-api-types/rest/v10/gateway.d.ts","./node_modules/discord-api-types/rest/v10/guild.d.ts","./node_modules/discord-api-types/rest/v10/guildScheduledEvent.d.ts","./node_modules/discord-api-types/rest/v10/webhook.d.ts","./node_modules/discord-api-types/rest/v10/interactions.d.ts","./node_modules/discord-api-types/rest/v10/invite.d.ts","./node_modules/discord-api-types/rest/v10/monetization.d.ts","./node_modules/discord-api-types/rest/v10/oauth2.d.ts","./node_modules/discord-api-types/rest/v10/soundboard.d.ts","./node_modules/discord-api-types/rest/v10/stageInstance.d.ts","./node_modules/discord-api-types/rest/v10/sticker.d.ts","./node_modules/discord-api-types/rest/v10/template.d.ts","./node_modules/discord-api-types/rest/v10/user.d.ts","./node_modules/discord-api-types/rest/v10/voice.d.ts","./node_modules/discord-api-types/rest/v10/index.d.ts","./node_modules/discord-api-types/payloads/v10/template.d.ts","./node_modules/discord-api-types/payloads/v10/voice.d.ts","./node_modules/discord-api-types/payloads/v10/index.d.ts","./node_modules/discord-api-types/gateway/common.d.ts","./node_modules/discord-api-types/gateway/v10.d.ts","./node_modules/discord-api-types/rpc/common.d.ts","./node_modules/discord-api-types/rpc/v10.d.ts","./node_modules/discord-api-types/utils/v10.d.ts","./node_modules/discord-api-types/v10.d.ts","./node_modules/@discordjs/formatters/dist/index.d.mts","./node_modules/@discordjs/util/dist/index.d.mts","./node_modules/@discordjs/builders/dist/index.d.mts","./node_modules/@discordjs/collection/dist/index.d.ts","./node_modules/@discordjs/rest/node_modules/@discordjs/collection/dist/index.d.mts","./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/undici/index.d.ts","./node_modules/@vladfrangu/async_event_emitter/dist/index.d.mts","./node_modules/@discordjs/rest/dist/web.d.mts","./node_modules/@discordjs/ws/node_modules/@discordjs/collection/dist/index.d.mts","./node_modules/@sapphire/async-queue/dist/esm/index.d.mts","./node_modules/@discordjs/ws/dist/index.d.mts","./node_modules/discord.js/typings/rawDataTypes.d.mts","./node_modules/@sapphire/snowflake/dist/esm/index.d.mts","./node_modules/discord.js/typings/index.d.mts","./packages/utils/requireDirectory.ts","./node_modules/@prisma/client-runtime-utils/dist/index.d.ts","./node_modules/@prisma/client/runtime/client.d.ts","./packages/db/generated/prisma/enums.ts","./packages/db/generated/prisma/models/DiscordUserProfile.ts","./packages/db/generated/prisma/models/DiscordGuildProfile.ts","./packages/db/generated/prisma/commonInputTypes.ts","./packages/db/generated/prisma/models.ts","./packages/db/generated/prisma/internal/prismaNamespace.ts","./packages/db/generated/prisma/internal/class.ts","./packages/db/generated/prisma/client.ts","./node_modules/@prisma/debug/dist/index.d.mts","./node_modules/@prisma/driver-adapter-utils/dist/index.d.mts","./node_modules/pg-types/index.d.ts","./node_modules/pg-protocol/dist/messages.d.ts","./node_modules/pg-protocol/dist/serializer.d.ts","./node_modules/pg-protocol/dist/parser.d.ts","./node_modules/pg-protocol/dist/index.d.ts","./node_modules/@types/pg/lib/type-overrides.d.ts","./node_modules/@types/pg/index.d.ts","./node_modules/@types/pg/index.d.mts","./node_modules/@prisma/adapter-pg/dist/index.d.mts","./packages/utils/outputHandler.ts","./packages/db/prisma.ts","./packages/bot/db.ts","./packages/bot/admin.ts","./packages/bot/command.ts","./packages/bot/event.ts","./packages/bot/index.ts","./packages/index.ts","./node_modules/prism-media/typings/opus.d.ts","./node_modules/prism-media/typings/vorbis.d.ts","./node_modules/prism-media/typings/index.d.ts","./node_modules/discord-api-types/voice/v8.d.ts","./node_modules/@types/ws/index.d.mts","./node_modules/@discordjs/voice/dist/index.d.mts","./packages/bot/util.ts","./node_modules/play-audio/dist/index.d.ts","./node_modules/play-dl/dist/index.d.ts","./packages/bot/music.ts","./packages/utils/fetch.ts","./packages/utils/callingNumberKorean.ts","./packages/utils/floatKorean.ts","./packages/utils/integerKorean.ts","./packages/utils/phoneNumberKorean.ts","./packages/utils/emoji-descriptions.json","./packages/utils/saferKorean.ts","./packages/tts/index.ts","./packages/tts/typecast.ts","./packages/tts/papago.ts","./packages/utils/nyaize.ts","./packages/tts/supertonic.ts","./packages/bot/tts.ts","./packages/bot/commands/getReadChannels.ts","./packages/bot/commands/getStatus.ts","./packages/bot/commands/joinVoiceChannel.ts","./packages/bot/commands/leaveVoiceChannel.ts","./packages/bot/commands/playVoice.ts","./packages/bot/commands/readChannel.ts","./packages/bot/commands/setNya.ts","./packages/bot/commands/setSupertonicStyle.ts","./packages/bot/commands/setVoice.ts","./packages/bot/commands/skipCurrent.ts","./packages/bot/commands/unreadChannel.ts","./packages/bot/events/readChannel.ts","./packages/bot/events/useCommand.ts","./node_modules/@prisma/client/runtime/index-browser.d.ts","./packages/db/generated/prisma/internal/prismaNamespaceBrowser.ts","./packages/db/generated/prisma/browser.ts"],"fileIdsList":[[72,134,142,146,149,151,152,153,165,193,274,275,276],[72,134,142,146,149,151,152,153,165],[72,134,142,146,149,151,152,153,165,182,194],[72,134,142,146,149,151,152,153,165,170,173,182,274,276,279,317,318],[72,134,142,146,149,151,152,153,165,274],[72,134,142,145,146,149,151,152,153,165,170,274,358,359,360],[72,134,142,146,149,151,152,153,165,274,276,279,318,319,321],[72,134,142,146,149,151,152,153,165,338,346],[72,134,142,146,149,151,152,153,165,327],[72,134,142,146,149,151,152,153,165,337],[72,134,142,146,149,151,152,153,165,183],[72,131,132,134,142,146,149,151,152,153,165],[72,133,134,142,146,149,151,152,153,165],[134,142,146,149,151,152,153,165],[72,134,142,146,149,151,152,153,165,173],[72,134,135,140,142,145,146,149,151,152,153,155,165,170,182],[72,134,135,136,142,145,146,149,151,152,153,165],[72,134,137,142,146,149,151,152,153,165,183],[72,134,138,139,142,146,149,151,152,153,156,165],[72,134,139,142,146,149,151,152,153,165,170,179],[72,134,140,142,145,146,149,151,152,153,155,165],[72,133,134,141,142,146,149,151,152,153,165],[72,134,142,143,146,149,151,152,153,165],[72,134,142,144,145,146,149,151,152,153,165],[72,133,134,142,145,146,149,151,152,153,165],[72,134,142,145,146,147,149,151,152,153,165,170,182],[72,134,142,145,146,147,149,151,152,153,165,170,173],[72,121,134,142,145,146,148,149,151,152,153,155,165,170,182],[72,134,142,145,146,148,149,151,152,153,155,165,170,179,182],[72,134,142,146,148,149,150,151,152,153,165,170,179,182],[70,71,72,73,74,75,76,77,78,79,80,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,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189],[72,134,142,145,146,149,151,152,153,165],[72,134,142,146,149,151,153,165],[72,134,142,146,149,151,152,153,154,165,182],[72,134,142,145,146,149,151,152,153,155,165,170],[72,134,142,146,149,151,152,153,156,165],[72,134,142,146,149,151,152,153,157,165],[72,134,142,145,146,149,151,152,153,160,165],[72,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,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189],[72,134,142,146,149,151,152,153,162,165],[72,134,142,146,149,151,152,153,163,165],[72,134,139,142,146,149,151,152,153,155,165,173],[72,134,142,145,146,149,151,152,153,165,166],[72,134,142,146,149,151,152,153,165,167,183,186],[72,134,142,145,146,149,151,152,153,165,170,172,173],[72,134,142,146,149,151,152,153,165,171,173],[72,134,142,146,149,151,152,153,165,173,183],[72,134,142,146,149,151,152,153,165,174],[72,131,134,142,146,149,151,152,153,165,170,176],[72,134,142,146,149,151,152,153,165,170,175],[72,134,142,145,146,149,151,152,153,165,177,178],[72,134,142,146,149,151,152,153,165,177,178],[72,134,139,142,146,149,151,152,153,155,165,170,179],[72,134,142,146,149,151,152,153,165,180],[72,134,142,146,149,151,152,153,155,165,181],[72,134,142,146,148,149,151,152,153,163,165,182],[72,134,142,146,149,151,152,153,165,183,184],[72,134,139,142,146,149,151,152,153,165,184],[72,134,142,146,149,151,152,153,165,170,185],[72,134,142,146,149,151,152,153,154,165,186],[72,134,142,146,149,151,152,153,165,187],[72,134,137,142,146,149,151,152,153,165],[72,134,139,142,146,149,151,152,153,165],[72,121,134,142,146,149,151,152,153,165],[72,134,142,146,149,151,152,153,165,182],[72,134,142,146,149,151,152,153,165,188],[72,134,142,146,149,151,152,153,160,165],[72,134,142,146,149,151,152,153,165,178],[72,121,134,142,145,146,147,149,151,152,153,160,165,170,173,182,185,186,188],[72,134,142,146,149,151,152,153,165,170,189],[72,134,142,146,149,151,152,153,165,345],[72,134,142,145,146,149,151,152,153,165,170,179,190,339,340,343,344,345],[72,134,142,145,146,148,149,150,151,152,153,155,165,170,179,182,189,190],[72,134,142,146,149,151,152,153,165,194,265],[72,134,142,146,149,151,152,153,165,194,265,268,269],[72,134,142,146,149,151,152,153,165,195],[72,134,142,146,149,151,152,153,165,194,211,212],[72,134,142,146,149,151,152,153,165,211,274],[72,134,142,146,149,151,152,153,165,211,212],[72,134,142,146,149,151,152,153,165,194,201,211,212],[72,134,142,146,149,151,152,153,165,209,211,212],[72,134,142,146,149,151,152,153,165,209,211,212,225],[72,134,142,146,149,151,152,153,165,209,211,212,221],[72,134,142,146,149,151,152,153,165,210,211,212,213,214,215,216,217,218,219,220,221,222,223,224,229,268],[72,134,142,146,149,151,152,153,165,194,207,210,224,229],[72,134,142,146,149,151,152,153,165,210,224,229],[72,134,142,146,149,151,152,153,165,194,229],[72,134,142,146,149,151,152,153,165,194],[72,134,142,146,149,151,152,153,165,194,209,210,224,225,226,227,228,274],[72,134,142,146,149,151,152,153,165,268],[72,134,142,146,149,151,152,153,165,194,199,201,205,207,208,209,274],[72,134,142,146,149,151,152,153,165,194,207,210,234],[72,134,142,146,149,151,152,153,165,207,268],[72,134,142,146,149,151,152,153,165,209,210],[72,134,142,146,149,151,152,153,165,207,229,274],[72,134,142,146,149,151,152,153,165,194,196,199,203,205,234,235,236],[72,134,142,146,149,151,152,153,165,194,198,199,201,204,205,234,236,238,239,240],[72,134,142,146,149,151,152,153,165,194,197,199,205],[72,134,142,146,149,151,152,153,165,194,197,198,199],[72,134,142,146,149,151,152,153,165,194,199,200,201],[72,134,142,146,149,151,152,153,165,194,195,198,199,200,202,203,204],[72,134,142,146,149,151,152,153,165,194,199,205],[72,134,142,146,149,151,152,153,165,196,198,199,200,201,202,203,204,205,206,207,208,234,235,236,237,238,239,240,241,242,243,266,267],[72,134,142,146,149,151,152,153,165,209,210,229,230,231,232,233],[72,134,142,146,149,151,152,153,165,199,201,205,237,239],[72,134,142,146,149,151,152,153,165,194,198,199,200,201,204,206,234,237],[72,134,142,146,149,151,152,153,165,200],[72,134,142,146,149,151,152,153,165,194,199],[72,134,142,146,149,151,152,153,165,194,205],[72,134,142,146,149,151,152,153,165,194,195,199,265],[72,134,142,146,149,151,152,153,165,194,268],[72,134,142,146,149,151,152,153,165,197,237],[72,134,142,146,149,151,152,153,165,194,241],[72,134,142,146,149,151,152,153,165,194,197,248,268],[72,134,142,146,149,151,152,153,165,194,195,197,249,268],[72,134,142,146,149,151,152,153,165,194,197,274],[72,134,142,146,149,151,152,153,165,195,244,245,246,247,248,249,250,251,252,253,254,255,256,257,258,259,260,261,262,263,264],[72,134,142,146,149,151,152,153,165,194,197,254,268],[72,134,142,146,149,151,152,153,165,194,274],[72,134,142,146,149,151,152,153,165,197,268],[72,134,142,146,149,151,152,153,165,194,197,248,249,268],[72,134,142,146,149,151,152,153,165,271,274],[72,134,142,146,149,151,152,153,165,194,197,265,268,270,272,273],[72,134,135,142,145,146,149,151,152,153,165,170,188,274,275,276,277,278,319,322,323,324],[72,134,142,146,149,151,152,153,165,274,325],[72,134,142,146,149,151,152,153,165,182,190],[72,134,142,146,149,151,152,153,165,190,340,341,342],[72,134,142,146,149,151,152,153,165,190],[72,134,142,146,149,151,152,153,165,170,190,340],[72,134,142,146,149,151,152,153,165,170],[72,134,142,146,149,151,152,153,165,170,363],[72,134,135,142,146,149,151,152,153,165,170,356,357],[72,87,90,93,94,134,142,146,149,151,152,153,165,182],[72,90,134,142,146,149,151,152,153,165,170,182],[72,90,94,134,142,146,149,151,152,153,165,182],[72,84,134,142,146,149,151,152,153,165],[72,88,134,142,146,149,151,152,153,165],[72,86,87,90,134,142,146,149,151,152,153,165,182],[72,134,142,146,149,151,152,153,155,165,179],[72,84,134,142,146,149,151,152,153,165,190],[72,86,90,134,142,146,149,151,152,153,155,165,182],[72,81,82,83,85,89,134,142,145,146,149,151,152,153,165,170,182],[72,90,98,106,134,142,146,149,151,152,153,165],[72,82,88,134,142,146,149,151,152,153,165],[72,90,115,116,134,142,146,149,151,152,153,165],[72,82,85,90,134,142,146,149,151,152,153,165,173,182,190],[72,90,134,142,146,149,151,152,153,165],[72,86,90,134,142,146,149,151,152,153,165,182],[72,81,134,142,146,149,151,152,153,165],[72,84,85,86,88,89,90,91,92,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,116,117,118,119,120,134,142,146,149,151,152,153,165],[72,90,108,111,134,142,146,149,151,152,153,165],[72,90,98,99,100,134,142,146,149,151,152,153,165],[72,88,90,99,101,134,142,146,149,151,152,153,165],[72,89,134,142,146,149,151,152,153,165],[72,82,84,90,134,142,146,149,151,152,153,165],[72,90,94,99,101,134,142,146,149,151,152,153,165],[72,94,134,142,146,149,151,152,153,165],[72,88,90,93,134,142,146,149,151,152,153,165,182],[72,82,86,90,98,134,142,146,149,151,152,153,165],[72,90,108,134,142,146,149,151,152,153,165],[72,101,134,142,146,149,151,152,153,165],[72,84,90,115,134,142,146,149,151,152,153,165,173,188,190],[72,134,142,146,149,151,152,153,165,316],[72,134,142,146,149,151,152,153,165,182,288,292],[72,134,142,146,149,151,152,153,165,170,182,288],[72,134,142,146,149,151,152,153,165,283],[72,134,142,146,149,151,152,153,165,179,182,285,288],[72,134,142,146,149,151,152,153,165,190,283],[72,134,142,146,149,151,152,153,155,165,182,285,288],[72,134,142,145,146,149,151,152,153,165,170,182,280,281,284,287],[72,134,142,146,149,151,152,153,165,288,295],[72,134,142,146,149,151,152,153,165,280,286],[72,134,142,146,149,151,152,153,165,288,309,310],[72,134,142,146,149,151,152,153,165,173,182,190,284,288],[72,134,142,146,149,151,152,153,165,190,309],[72,134,142,146,149,151,152,153,165,190,282,283],[72,134,142,146,149,151,152,153,165,288],[72,134,142,146,149,151,152,153,165,282,283,284,285,286,287,288,289,290,292,293,294,295,296,297,298,299,300,301,302,303,304,305,306,307,308,310,311,312,313,314,315],[72,134,142,146,149,151,152,153,165,288,303],[72,134,142,146,149,151,152,153,165,288,295,296],[72,134,142,146,149,151,152,153,165,286,288,296,297],[72,134,142,146,149,151,152,153,165,287],[72,134,142,146,149,151,152,153,165,280,283,288],[72,134,142,146,149,151,152,153,165,288,292,296,297],[72,134,142,146,149,151,152,153,165,292],[72,134,142,146,149,151,152,153,165,182,286,288,291],[72,134,142,146,149,151,152,153,165,280,285,288,295],[72,134,142,146,149,151,152,153,165,188,190,283,288,309],[72,134,142,146,149,151,152,153,165,350],[72,134,142,146,149,151,152,153,157,165,325,326,351],[72,134,142,146,149,151,152,153,165,325,350,352],[72,134,142,146,149,151,152,153,165,325,348,352],[72,134,142,146,149,151,152,153,165,325,352,361],[72,134,142,146,149,151,152,153,165,325,350,352,378],[72,134,142,146,149,151,152,153,165,192,325,350,352],[72,134,142,146,149,151,152,153,165,325,329,350,352],[72,134,142,146,149,151,152,153,165,325,352,378],[72,134,142,146,149,151,152,153,165,336,349],[72,134,142,146,149,151,152,153,157,165,325,326],[72,134,142,146,149,151,152,153,165,192,329,350,353,362,378],[72,134,142,146,149,151,152,153,165,352,353],[72,134,142,146,147,149,151,152,153,157,161,165,192,325,348,352,353],[72,134,142,146,149,151,152,153,165,325,348,361,362,364],[72,134,142,146,149,151,152,153,165,325,329,336,348,361,362,373,374,375,376,377],[72,134,142,146,149,151,152,153,165,325,361],[72,134,142,146,149,151,152,153,165,329,393],[72,134,142,146,149,151,152,153,157,161,165,182,328,329,334,335],[72,134,142,146,149,151,152,153,165,328,329,334],[72,134,142,146,149,151,152,153,165,328,334],[72,134,142,146,149,151,152,153,165,328,333,335],[72,134,142,146,149,151,152,153,165,333,334,392],[72,134,142,146,149,151,152,153,165,330,331,332],[72,134,142,146,149,151,152,153,165,192,336,346,347,348],[72,134,142,146,149,151,152,153,165,191],[72,134,142,146,149,151,152,153,165,192,325,354],[72,134,139,142,146,147,149,151,152,153,157,165,170,361,372],[72,134,139,142,146,149,151,152,153,157,165,366,373],[72,134,142,146,149,151,152,153,157,165,366,373],[72,134,142,146,149,151,152,153,157,161,165,192,366,373],[72,134,142,146,147,149,151,152,153,157,165],[72,134,142,146,149,151,152,153,165,367,368,369,370,371]],"fileInfos":[{"version":"c430d44666289dae81f30fa7b2edebf186ecc91a2d4c71266ea6ae76388792e1","affectsGlobalScope":true,"impliedFormat":1},{"version":"45b7ab580deca34ae9729e97c13cfd999df04416a79116c3bfb483804f85ded4","impliedFormat":1},{"version":"3facaf05f0c5fc569c5649dd359892c98a85557e3e0c847964caeb67076f4d75","impliedFormat":1},{"version":"e44bb8bbac7f10ecc786703fe0a6a4b952189f908707980ba8f3c8975a760962","impliedFormat":1},{"version":"5e1c4c362065a6b95ff952c0eab010f04dcd2c3494e813b493ecfd4fcb9fc0d8","impliedFormat":1},{"version":"68d73b4a11549f9c0b7d352d10e91e5dca8faa3322bfb77b661839c42b1ddec7","impliedFormat":1},{"version":"5efce4fc3c29ea84e8928f97adec086e3dc876365e0982cc8479a07954a3efd4","impliedFormat":1},{"version":"feecb1be483ed332fad555aff858affd90a48ab19ba7272ee084704eb7167569","impliedFormat":1},{"version":"ee7bad0c15b58988daa84371e0b89d313b762ab83cb5b31b8a2d1162e8eb41c2","impliedFormat":1},{"version":"27bdc30a0e32783366a5abeda841bc22757c1797de8681bbe81fbc735eeb1c10","impliedFormat":1},{"version":"080941d9f9ff9307f7e27a83bcd888b7c8270716c39af943532438932ec1d0b9","affectsGlobalScope":true,"impliedFormat":1},{"version":"2e80ee7a49e8ac312cc11b77f1475804bee36b3b2bc896bead8b6e1266befb43","affectsGlobalScope":true,"impliedFormat":1},{"version":"d7a3c8b952931daebdfc7a2897c53c0a1c73624593fa070e46bd537e64dcd20a","affectsGlobalScope":true,"impliedFormat":1},{"version":"80e18897e5884b6723488d4f5652167e7bb5024f946743134ecc4aa4ee731f89","affectsGlobalScope":true,"impliedFormat":1},{"version":"cd034f499c6cdca722b60c04b5b1b78e058487a7085a8e0d6fb50809947ee573","affectsGlobalScope":true,"impliedFormat":1},{"version":"c57796738e7f83dbc4b8e65132f11a377649c00dd3eee333f672b8f0a6bea671","affectsGlobalScope":true,"impliedFormat":1},{"version":"dc2df20b1bcdc8c2d34af4926e2c3ab15ffe1160a63e58b7e09833f616efff44","affectsGlobalScope":true,"impliedFormat":1},{"version":"515d0b7b9bea2e31ea4ec968e9edd2c39d3eebf4a2d5cbd04e88639819ae3b71","affectsGlobalScope":true,"impliedFormat":1},{"version":"0559b1f683ac7505ae451f9a96ce4c3c92bdc71411651ca6ddb0e88baaaad6a3","affectsGlobalScope":true,"impliedFormat":1},{"version":"0dc1e7ceda9b8b9b455c3a2d67b0412feab00bd2f66656cd8850e8831b08b537","affectsGlobalScope":true,"impliedFormat":1},{"version":"ce691fb9e5c64efb9547083e4a34091bcbe5bdb41027e310ebba8f7d96a98671","affectsGlobalScope":true,"impliedFormat":1},{"version":"8d697a2a929a5fcb38b7a65594020fcef05ec1630804a33748829c5ff53640d0","affectsGlobalScope":true,"impliedFormat":1},{"version":"4ff2a353abf8a80ee399af572debb8faab2d33ad38c4b4474cff7f26e7653b8d","affectsGlobalScope":true,"impliedFormat":1},{"version":"fb0f136d372979348d59b3f5020b4cdb81b5504192b1cacff5d1fbba29378aa1","affectsGlobalScope":true,"impliedFormat":1},{"version":"d15bea3d62cbbdb9797079416b8ac375ae99162a7fba5de2c6c505446486ac0a","affectsGlobalScope":true,"impliedFormat":1},{"version":"68d18b664c9d32a7336a70235958b8997ebc1c3b8505f4f1ae2b7e7753b87618","affectsGlobalScope":true,"impliedFormat":1},{"version":"eb3d66c8327153d8fa7dd03f9c58d351107fe824c79e9b56b462935176cdf12a","affectsGlobalScope":true,"impliedFormat":1},{"version":"38f0219c9e23c915ef9790ab1d680440d95419ad264816fa15009a8851e79119","affectsGlobalScope":true,"impliedFormat":1},{"version":"69ab18c3b76cd9b1be3d188eaf8bba06112ebbe2f47f6c322b5105a6fbc45a2e","affectsGlobalScope":true,"impliedFormat":1},{"version":"a680117f487a4d2f30ea46f1b4b7f58bef1480456e18ba53ee85c2746eeca012","affectsGlobalScope":true,"impliedFormat":1},{"version":"2f11ff796926e0832f9ae148008138ad583bd181899ab7dd768a2666700b1893","affectsGlobalScope":true,"impliedFormat":1},{"version":"4de680d5bb41c17f7f68e0419412ca23c98d5749dcaaea1896172f06435891fc","affectsGlobalScope":true,"impliedFormat":1},{"version":"954296b30da6d508a104a3a0b5d96b76495c709785c1d11610908e63481ee667","affectsGlobalScope":true,"impliedFormat":1},{"version":"ac9538681b19688c8eae65811b329d3744af679e0bdfa5d842d0e32524c73e1c","affectsGlobalScope":true,"impliedFormat":1},{"version":"0a969edff4bd52585473d24995c5ef223f6652d6ef46193309b3921d65dd4376","affectsGlobalScope":true,"impliedFormat":1},{"version":"9e9fbd7030c440b33d021da145d3232984c8bb7916f277e8ffd3dc2e3eae2bdb","affectsGlobalScope":true,"impliedFormat":1},{"version":"811ec78f7fefcabbda4bfa93b3eb67d9ae166ef95f9bff989d964061cbf81a0c","affectsGlobalScope":true,"impliedFormat":1},{"version":"717937616a17072082152a2ef351cb51f98802fb4b2fdabd32399843875974ca","affectsGlobalScope":true,"impliedFormat":1},{"version":"d7e7d9b7b50e5f22c915b525acc5a49a7a6584cf8f62d0569e557c5cfc4b2ac2","affectsGlobalScope":true,"impliedFormat":1},{"version":"71c37f4c9543f31dfced6c7840e068c5a5aacb7b89111a4364b1d5276b852557","affectsGlobalScope":true,"impliedFormat":1},{"version":"576711e016cf4f1804676043e6a0a5414252560eb57de9faceee34d79798c850","affectsGlobalScope":true,"impliedFormat":1},{"version":"89c1b1281ba7b8a96efc676b11b264de7a8374c5ea1e6617f11880a13fc56dc6","affectsGlobalScope":true,"impliedFormat":1},{"version":"74f7fa2d027d5b33eb0471c8e82a6c87216223181ec31247c357a3e8e2fddc5b","affectsGlobalScope":true,"impliedFormat":1},{"version":"d6d7ae4d1f1f3772e2a3cde568ed08991a8ae34a080ff1151af28b7f798e22ca","affectsGlobalScope":true,"impliedFormat":1},{"version":"063600664504610fe3e99b717a1223f8b1900087fab0b4cad1496a114744f8df","affectsGlobalScope":true,"impliedFormat":1},{"version":"934019d7e3c81950f9a8426d093458b65d5aff2c7c1511233c0fd5b941e608ab","affectsGlobalScope":true,"impliedFormat":1},{"version":"52ada8e0b6e0482b728070b7639ee42e83a9b1c22d205992756fe020fd9f4a47","affectsGlobalScope":true,"impliedFormat":1},{"version":"3bdefe1bfd4d6dee0e26f928f93ccc128f1b64d5d501ff4a8cf3c6371200e5e6","affectsGlobalScope":true,"impliedFormat":1},{"version":"59fb2c069260b4ba00b5643b907ef5d5341b167e7d1dbf58dfd895658bda2867","affectsGlobalScope":true,"impliedFormat":1},{"version":"639e512c0dfc3fad96a84caad71b8834d66329a1f28dc95e3946c9b58176c73a","affectsGlobalScope":true,"impliedFormat":1},{"version":"368af93f74c9c932edd84c58883e736c9e3d53cec1fe24c0b0ff451f529ceab1","affectsGlobalScope":true,"impliedFormat":1},{"version":"af3dd424cf267428f30ccfc376f47a2c0114546b55c44d8c0f1d57d841e28d74","affectsGlobalScope":true,"impliedFormat":1},{"version":"995c005ab91a498455ea8dfb63aa9f83fa2ea793c3d8aa344be4a1678d06d399","affectsGlobalScope":true,"impliedFormat":1},{"version":"959d36cddf5e7d572a65045b876f2956c973a586da58e5d26cde519184fd9b8a","affectsGlobalScope":true,"impliedFormat":1},{"version":"965f36eae237dd74e6cca203a43e9ca801ce38824ead814728a2807b1910117d","affectsGlobalScope":true,"impliedFormat":1},{"version":"3925a6c820dcb1a06506c90b1577db1fdbf7705d65b62b99dce4be75c637e26b","affectsGlobalScope":true,"impliedFormat":1},{"version":"0a3d63ef2b853447ec4f749d3f368ce642264246e02911fcb1590d8c161b8005","affectsGlobalScope":true,"impliedFormat":1},{"version":"8cdf8847677ac7d20486e54dd3fcf09eda95812ac8ace44b4418da1bbbab6eb8","affectsGlobalScope":true,"impliedFormat":1},{"version":"8444af78980e3b20b49324f4a16ba35024fef3ee069a0eb67616ea6ca821c47a","affectsGlobalScope":true,"impliedFormat":1},{"version":"3287d9d085fbd618c3971944b65b4be57859f5415f495b33a6adc994edd2f004","affectsGlobalScope":true,"impliedFormat":1},{"version":"b4b67b1a91182421f5df999988c690f14d813b9850b40acd06ed44691f6727ad","affectsGlobalScope":true,"impliedFormat":1},{"version":"df83c2a6c73228b625b0beb6669c7ee2a09c914637e2d35170723ad49c0f5cd4","affectsGlobalScope":true,"impliedFormat":1},{"version":"436aaf437562f276ec2ddbee2f2cdedac7664c1e4c1d2c36839ddd582eeb3d0a","affectsGlobalScope":true,"impliedFormat":1},{"version":"8e3c06ea092138bf9fa5e874a1fdbc9d54805d074bee1de31b99a11e2fec239d","affectsGlobalScope":true,"impliedFormat":1},{"version":"51ad4c928303041605b4d7ae32e0c1ee387d43a24cd6f1ebf4a2699e1076d4fa","affectsGlobalScope":true,"impliedFormat":1},{"version":"196cb558a13d4533a5163286f30b0509ce0210e4b316c56c38d4c0fd2fb38405","affectsGlobalScope":true,"impliedFormat":1},{"version":"8e7f8264d0fb4c5339605a15daadb037bf238c10b654bb3eee14208f860a32ea","affectsGlobalScope":true,"impliedFormat":1},{"version":"782dec38049b92d4e85c1585fbea5474a219c6984a35b004963b00beb1aab538","affectsGlobalScope":true,"impliedFormat":1},{"version":"785921608325fa246b450f05b238f4b3ed659f1099af278ce9ebbc9416a13f1d","impliedFormat":1},{"version":"d153a11543fd884b596587ccd97aebbeed950b26933ee000f94009f1ab142848","affectsGlobalScope":true,"impliedFormat":1},{"version":"0ccdaa19852d25ecd84eec365c3bfa16e7859cadecf6e9ca6d0dbbbee439743f","affectsGlobalScope":true,"impliedFormat":1},{"version":"438b41419b1df9f1fbe33b5e1b18f5853432be205991d1b19f5b7f351675541e","affectsGlobalScope":true,"impliedFormat":1},{"version":"096116f8fedc1765d5bd6ef360c257b4a9048e5415054b3bf3c41b07f8951b0b","affectsGlobalScope":true,"impliedFormat":1},{"version":"e5e01375c9e124a83b52ee4b3244ed1a4d214a6cfb54ac73e164a823a4a7860a","affectsGlobalScope":true,"impliedFormat":1},{"version":"f90ae2bbce1505e67f2f6502392e318f5714bae82d2d969185c4a6cecc8af2fc","affectsGlobalScope":true,"impliedFormat":1},{"version":"4b58e207b93a8f1c88bbf2a95ddc686ac83962b13830fe8ad3f404ffc7051fb4","affectsGlobalScope":true,"impliedFormat":1},{"version":"1fefabcb2b06736a66d2904074d56268753654805e829989a46a0161cd8412c5","affectsGlobalScope":true,"impliedFormat":1},{"version":"9798340ffb0d067d69b1ae5b32faa17ab31b82466a3fc00d8f2f2df0c8554aaa","affectsGlobalScope":true,"impliedFormat":1},{"version":"c18a99f01eb788d849ad032b31cafd49de0b19e083fe775370834c5675d7df8e","affectsGlobalScope":true,"impliedFormat":1},{"version":"5247874c2a23b9a62d178ae84f2db6a1d54e6c9a2e7e057e178cc5eea13757fc","affectsGlobalScope":true,"impliedFormat":1},{"version":"cdcf9ea426ad970f96ac930cd176d5c69c6c24eebd9fc580e1572d6c6a88f62c","impliedFormat":1},{"version":"23cd712e2ce083d68afe69224587438e5914b457b8acf87073c22494d706a3d0","impliedFormat":1},{"version":"487b694c3de27ddf4ad107d4007ad304d29effccf9800c8ae23c2093638d906a","impliedFormat":1},{"version":"3a80bc85f38526ca3b08007ee80712e7bb0601df178b23fbf0bf87036fce40ce","impliedFormat":1},{"version":"ccf4552357ce3c159ef75f0f0114e80401702228f1898bdc9402214c9499e8c0","impliedFormat":1},{"version":"c6fd2c5a395f2432786c9cb8deb870b9b0e8ff7e22c029954fabdd692bff6195","impliedFormat":1},{"version":"68834d631c8838c715f225509cfc3927913b9cc7a4870460b5b60c8dbdb99baf","impliedFormat":1},{"version":"2931540c47ee0ff8a62860e61782eb17b155615db61e36986e54645ec67f67c2","impliedFormat":1},{"version":"ccab02f3920fc75c01174c47fcf67882a11daf16baf9e81701d0a94636e94556","impliedFormat":1},{"version":"f6faf5f74e4c4cc309a6c6a6c4da02dbb840be5d3e92905a23dcd7b2b0bd1986","impliedFormat":1},{"version":"ea6bc8de8b59f90a7a3960005fd01988f98fd0784e14bc6922dde2e93305ec7d","impliedFormat":1},{"version":"36107995674b29284a115e21a0618c4c2751b32a8766dd4cb3ba740308b16d59","impliedFormat":1},{"version":"914a0ae30d96d71915fc519ccb4efbf2b62c0ddfb3a3fc6129151076bc01dc60","impliedFormat":1},{"version":"33e981bf6376e939f99bd7f89abec757c64897d33c005036b9a10d9587d80187","impliedFormat":1},{"version":"7fd1b31fd35876b0aa650811c25ec2c97a3c6387e5473eb18004bed86cdd76b6","impliedFormat":1},{"version":"b41767d372275c154c7ea6c9d5449d9a741b8ce080f640155cc88ba1763e35b3","impliedFormat":1},{"version":"3bacf516d686d08682751a3bd2519ea3b8041a164bfb4f1d35728993e70a2426","impliedFormat":1},{"version":"7fb266686238369442bd1719bc0d7edd0199da4fb8540354e1ff7f16669b4323","impliedFormat":1},{"version":"0a60a292b89ca7218b8616f78e5bbd1c96b87e048849469cccb4355e98af959a","impliedFormat":1},{"version":"0b6e25234b4eec6ed96ab138d96eb70b135690d7dd01f3dd8a8ab291c35a683a","impliedFormat":1},{"version":"9666f2f84b985b62400d2e5ab0adae9ff44de9b2a34803c2c5bd3c8325b17dc0","impliedFormat":1},{"version":"40cd35c95e9cf22cfa5bd84e96408b6fcbca55295f4ff822390abb11afbc3dca","impliedFormat":1},{"version":"b1616b8959bf557feb16369c6124a97a0e74ed6f49d1df73bb4b9ddf68acf3f3","impliedFormat":1},{"version":"5b03a034c72146b61573aab280f295b015b9168470f2df05f6080a2122f9b4df","impliedFormat":1},{"version":"40b463c6766ca1b689bfcc46d26b5e295954f32ad43e37ee6953c0a677e4ae2b","impliedFormat":1},{"version":"249b9cab7f5d628b71308c7d9bb0a808b50b091e640ba3ed6e2d0516f4a8d91d","impliedFormat":1},{"version":"80aae6afc67faa5ac0b32b5b8bc8cc9f7fa299cff15cf09cc2e11fd28c6ae29e","impliedFormat":1},{"version":"f473cd2288991ff3221165dcf73cd5d24da30391f87e85b3dd4d0450c787a391","impliedFormat":1},{"version":"499e5b055a5aba1e1998f7311a6c441a369831c70905cc565ceac93c28083d53","impliedFormat":1},{"version":"54c3e2371e3d016469ad959697fd257e5621e16296fa67082c2575d0bf8eced0","impliedFormat":1},{"version":"beb8233b2c220cfa0feea31fbe9218d89fa02faa81ef744be8dce5acb89bb1fd","impliedFormat":1},{"version":"c183b931b68ad184bc8e8372bf663f3d33304772fb482f29fb91b3c391031f3e","impliedFormat":1},{"version":"5d0375ca7310efb77e3ef18d068d53784faf62705e0ad04569597ae0e755c401","impliedFormat":1},{"version":"59af37caec41ecf7b2e76059c9672a49e682c1a2aa6f9d7dc78878f53aa284d6","impliedFormat":1},{"version":"addf417b9eb3f938fddf8d81e96393a165e4be0d4a8b6402292f9c634b1cb00d","impliedFormat":1},{"version":"48cc3ec153b50985fb95153258a710782b25975b10dd4ac8a4f3920632d10790","impliedFormat":1},{"version":"adf27937dba6af9f08a68c5b1d3fce0ca7d4b960c57e6d6c844e7d1a8e53adae","impliedFormat":1},{"version":"e1528ca65ac90f6fa0e4a247eb656b4263c470bb22d9033e466463e13395e599","impliedFormat":1},{"version":"2e85db9e6fd73cfa3d7f28e0ab6b55417ea18931423bd47b409a96e4a169e8e6","impliedFormat":1},{"version":"c46e079fe54c76f95c67fb89081b3e399da2c7d109e7dca8e4b58d83e332e605","impliedFormat":1},{"version":"866078923a56d026e39243b4392e282c1c63159723996fa89243140e1388a98d","impliedFormat":1},{"version":"dd0109710de4cd93e245121ab86d8c66d20f3ead80074b68e9c3e349c4f53342","affectsGlobalScope":true,"impliedFormat":1},{"version":"b3275d55fac10b799c9546804126239baf020d220136163f763b55a74e50e750","affectsGlobalScope":true,"impliedFormat":1},{"version":"fa68a0a3b7cb32c00e39ee3cd31f8f15b80cac97dce51b6ee7fc14a1e8deb30b","affectsGlobalScope":true,"impliedFormat":1},{"version":"1cf059eaf468efcc649f8cf6075d3cb98e9a35a0fe9c44419ec3d2f5428d7123","affectsGlobalScope":true,"impliedFormat":1},{"version":"6c36e755bced82df7fb6ce8169265d0a7bb046ab4e2cb6d0da0cb72b22033e89","affectsGlobalScope":true,"impliedFormat":1},{"version":"e7721c4f69f93c91360c26a0a84ee885997d748237ef78ef665b153e622b36c1","affectsGlobalScope":true,"impliedFormat":1},{"version":"7a93de4ff8a63bafe62ba86b89af1df0ccb5e40bb85b0c67d6bbcfdcf96bf3d4","affectsGlobalScope":true,"impliedFormat":1},{"version":"90e85f9bc549dfe2b5749b45fe734144e96cd5d04b38eae244028794e142a77e","affectsGlobalScope":true,"impliedFormat":1},{"version":"e0a5deeb610b2a50a6350bd23df6490036a1773a8a71d70f2f9549ab009e67ee","affectsGlobalScope":true,"impliedFormat":1},{"version":"435b3711465425770ed2ee2f1cf00ce071835265e0851a7dc4600ab4b007550e","impliedFormat":1},{"version":"7e49f52a159435fc8df4de9dc377ef5860732ca2dc9efec1640531d3cf5da7a3","impliedFormat":1},{"version":"dd4bde4bdc2e5394aed6855e98cf135dfdf5dd6468cad842e03116d31bbcc9bc","impliedFormat":1},{"version":"4d4e879009a84a47c05350b8dca823036ba3a29a3038efed1be76c9f81e45edf","affectsGlobalScope":true,"impliedFormat":1},{"version":"cf83d90d5faf27b994c2e79af02e32b555dbfe42cd9bd1571445f2168d1f4e2d","impliedFormat":1},{"version":"9ba13b47cb450a438e3076c4a3f6afb9dc85e17eae50f26d4b2d72c0688c9251","impliedFormat":1},{"version":"b64cd4401633ea4ecadfd700ddc8323a13b63b106ac7127c1d2726f32424622c","impliedFormat":1},{"version":"37c6e5fe5715814412b43cc9b50b24c67a63c4e04e753e0d1305970d65417a60","impliedFormat":1},{"version":"0e28335ac43f4d94dd2fe6d9e6fa6813570640839addd10d309d7985f33a6308","impliedFormat":1},{"version":"ee0e4946247f842c6dd483cbb60a5e6b484fee07996e3a7bc7343dfb68a04c5d","impliedFormat":1},{"version":"ef051f42b7e0ef5ca04552f54c4552eac84099d64b6c5ad0ef4033574b6035b8","impliedFormat":1},{"version":"853a43154f1d01b0173d9cbd74063507ece57170bad7a3b68f3fa1229ad0a92f","impliedFormat":1},{"version":"56231e3c39a031bfb0afb797690b20ed4537670c93c0318b72d5180833d98b72","impliedFormat":1},{"version":"5cc7c39031bfd8b00ad58f32143d59eb6ffc24f5d41a20931269011dccd36c5e","impliedFormat":1},{"version":"b0b69c61b0f0ec8ca15db4c8c41f6e77f4cacb784d42bca948f42dea33e8757e","affectsGlobalScope":true,"impliedFormat":1},{"version":"f96a48183254c00d24575401f1a761b4ce4927d927407e7862a83e06ce5d6964","impliedFormat":1},{"version":"cc25940cfb27aa538e60d465f98bb5068d4d7d33131861ace43f04fe6947d68f","impliedFormat":1},{"version":"ac86245c2f31335bfd52cbe7fc760f9fc4f165387875869a478a6d9616a95e72","impliedFormat":1},{"version":"01ff95aa1443e3f7248974e5a771f513cb2ac158c8898f470a1792f817bee497","impliedFormat":1},{"version":"9d96a7ce809392ff2cb99691acf7c62e632fe56897356ba013b689277aca3619","impliedFormat":1},{"version":"42a05d8f239f74587d4926aba8cc54792eed8e8a442c7adc9b38b516642aadfe","impliedFormat":1},{"version":"5d21b58d60383cc6ab9ad3d3e265d7d25af24a2c9b506247e0e50b0a884920be","impliedFormat":1},{"version":"101f482fd48cb4c7c0468dcc6d62c843d842977aea6235644b1edd05e81fbf22","impliedFormat":1},{"version":"ae6757460f37078884b1571a3de3ebaf724d827d7e1d53626c02b3c2a408ac63","affectsGlobalScope":true,"impliedFormat":1},{"version":"27c0a08e343c6a0ae17bd13ba6d44a9758236dc904cd5e4b43456996cd51f520","impliedFormat":1},{"version":"3ef397f12387eff17f550bc484ea7c27d21d43816bbe609d495107f44b97e933","impliedFormat":1},{"version":"1023282e2ba810bc07905d3668349fbd37a26411f0c8f94a70ef3c05fe523fcf","impliedFormat":1},{"version":"b214ebcf76c51b115453f69729ee8aa7b7f8eccdae2a922b568a45c2d7ff52f7","impliedFormat":1},{"version":"429c9cdfa7d126255779efd7e6d9057ced2d69c81859bbab32073bad52e9ba76","impliedFormat":1},{"version":"6f80e51ba310608cd71bcdc09a171d7bbfb3b316048601c9ec215ce16a8dcfbc","impliedFormat":1},{"version":"63054ef6518309c85b11e00537eef47a2b8609c6c20630b1a1daaecf86ea0d28","affectsGlobalScope":true,"impliedFormat":1},{"version":"7f2c62938251b45715fd2a9887060ec4fbc8724727029d1cbce373747252bdd7","impliedFormat":1},{"version":"e3ace08b6bbd84655d41e244677b474fd995923ffef7149ddb68af8848b60b05","impliedFormat":1},{"version":"132580b0e86c48fab152bab850fc57a4b74fe915c8958d2ccb052b809a44b61c","impliedFormat":1},{"version":"af4ab0aa8908fc9a655bb833d3bc28e117c4f0e1038c5a891546158beb25accb","impliedFormat":1},{"version":"69c9a5a9392e8564bd81116e1ed93b13205201fb44cb35a7fde8c9f9e21c4b23","impliedFormat":1},{"version":"5f8fc37f8434691ffac1bfd8fc2634647da2c0e84253ab5d2dd19a7718915b35","impliedFormat":1},{"version":"5981c2340fd8b076cae8efbae818d42c11ffc615994cb060b1cd390795f1be2b","impliedFormat":1},{"version":"2ca2bca6845a7234eff5c3d192727a068fca72ac565f3c819c6b04ccc83dadc0","impliedFormat":1},{"version":"ed4f674fc8c0c993cc7e145069ac44129e03519b910c62be206a0cc777bdc60b","affectsGlobalScope":true,"impliedFormat":1},{"version":"0250da3eb85c99624f974e77ef355cdf86f43980251bc371475c2b397ba55bcd","impliedFormat":1},{"version":"f1c93e046fb3d9b7f8249629f4b63dc068dd839b824dd0aa39a5e68476dc9420","impliedFormat":1},{"version":"3d3a5f27ffbc06c885dd4d5f9ee20de61faf877fe2c3a7051c4825903d9a7fdc","impliedFormat":1},{"version":"12806f9f085598ef930edaf2467a5fa1789a878fba077cd27e85dc5851e11834","impliedFormat":1},{"version":"17d06eb5709839c7ce719f0c38ada6f308fb433f2cd6d8c87b35856e07400950","impliedFormat":1},{"version":"a43fe41c33d0a192a0ecaf9b92e87bef3709c9972e6d53c42c49251ccb962d69","impliedFormat":1},{"version":"a177959203c017fad3ecc4f3d96c8757a840957a4959a3ae00dab9d35961ca6c","affectsGlobalScope":true,"impliedFormat":1},{"version":"6fc727ccf9b36e257ff982ea0badeffbfc2c151802f741bddff00c6af3b784cf","impliedFormat":1},{"version":"2a00d005e3af99cd1cfa75220e60c61b04bfb6be7ca7453bfe2ef6cca37cc03c","impliedFormat":1},{"version":"4844a4c9b4b1e812b257676ed8a80b3f3be0e29bf05e742cc2ea9c3c6865e6c6","impliedFormat":1},{"version":"064878a60367e0407c42fb7ba02a2ea4d83257357dc20088e549bd4d89433e9c","impliedFormat":1},{"version":"14d4bd22d1b05824971b98f7e91b2484c90f1a684805c330476641417c3d9735","impliedFormat":1},{"version":"586eaf66bace2e731cee0ddfbfac326ad74a83c1acfeac4afb2db85ad23226c7","impliedFormat":1},{"version":"b484ec11ba00e3a2235562a41898d55372ccabe607986c6fa4f4aba72093749f","impliedFormat":1},{"version":"d1a14d87cedcf4f0b8173720d6eb29cc02878bf2b6dabf9c9d9cee742f275368","impliedFormat":1},{"version":"e60efae9fe48a2955f66bf4cbf0f082516185b877daf50d9c5e2a009660a7714","impliedFormat":1},{"version":"041a7781b9127ab568d2cdcce62c58fdea7c7407f40b8c50045d7866a2727130","impliedFormat":1},{"version":"b37f83e7deea729aa9ce5593f78905afb45b7532fdff63041d374f60059e7852","impliedFormat":1},{"version":"e1cb68f3ef3a8dd7b2a9dfb3de482ed6c0f1586ba0db4e7d73c1d2147b6ffc51","impliedFormat":1},{"version":"55cdbeebe76a1fa18bbd7e7bf73350a2173926bd3085bb050cf5a5397025ee4e","impliedFormat":1},{"version":"a1936fd7bdd911dc2fea8bb2edf2e1eb8765c657a9c0a50de2a7cf4e2fa0ca8d","impliedFormat":1},{"version":"313ec01d2b2f3d2862fc67bd954ad797ffb1ca0bcf8d1e1d79aa30a06380357b","signature":"0cccb133d36e65457462b7a8a55bf6f97b16dec17c9199b65962383945ae1038"},{"version":"a2999ed84296a880c3f8ad4adc1c8c07617007cc55a2b6ad362b142a6fed9af0","impliedFormat":99},{"version":"a9f672caa1c5631a744b05f69c28b4cfdbcb35e16112b2596ad88c90d1491e90","impliedFormat":1},{"version":"f9888ec2127d5aaa039ca5443e94759e7057764063cf27e30eef0faba52c4ef6","impliedFormat":1},{"version":"ae61f4cafd678c65fc25d195118406ee9cca1df911e2ee1c616f2527c791a153","impliedFormat":1},{"version":"8d1722eddb71400b416698dc4a4faf61d38f3c5fb191412b487877c12f92f5ce","impliedFormat":1},{"version":"c364fe92add5ec24793043aae128dcb964fd000af2a05085d045b45c13014b63","impliedFormat":1},{"version":"bdc055f76f35b8b071844601d37cd1f1787126346788b415ed75f7b6fb9b84e2","impliedFormat":1},{"version":"133fd0be181d7c21fceeccfa59efeaabf949502d37288326ffe6b0383fdc0b9e","impliedFormat":1},{"version":"61ace3cc50d78c7864bac5b0cd652b20715f2bc1a90fbaec1827cb2643590087","impliedFormat":1},{"version":"f564f2be9c3e6807d1f69a7522605c22cc0a1809bf684f70f052cf5efcd463f6","impliedFormat":1},{"version":"15eee289dbd1369243ce767502b3d48bbdf50638249df90555c0f02f5b2107b6","impliedFormat":1},{"version":"633f7c4ebb6f3a3ab186b8ad44af6b7a41355b19e4125bd378899a50ba5991e7","impliedFormat":1},{"version":"038fe6ed477da8cfc9746d1e2f574a5051307f166fb83ad325c5b6aa370a0811","impliedFormat":1},{"version":"1adef29addf928c7a07a4de2e12298ad25a01af6d9e939de273d550b474f1dca","impliedFormat":1},{"version":"36ff4895f786673222511005ad3cf0d36ed6bab7b03640898caaaae3a99e9c0f","impliedFormat":1},{"version":"5d303981e2efe7b75e303b9ba0b149f7dc52b60a716a37366d15c0ee7e06d71c","impliedFormat":1},{"version":"5f6517251db8f8c9c6492fb269c3127b02acdeee3aef6f75e90f2bf8965cf79f","impliedFormat":1},{"version":"49c5f7d0baf3065c8533f8cc867f2185cd5766f5a6d9f876867ead2e17c88244","impliedFormat":1},{"version":"68a75a201a5d1322df462c133020e5c639809c94a6698e3cc46363e56c174b30","impliedFormat":1},{"version":"265a4739974854eb43539419a6c86faab7e161b1133496c62f8bf2725542244d","impliedFormat":1},{"version":"a1accfaac01020c1b866634f7a698fdd48612caccf305e16844665cb7ecac399","impliedFormat":1},{"version":"ec00441ae4838b8d595acfe3b2750750e4335802b06ba1f41fb03453c2c776b5","impliedFormat":1},{"version":"5752c1bf2c41f766377630b830fc866283247f5821a356f70a6e51d632d4b49b","impliedFormat":1},{"version":"8dca1366d02c160f39732f1a9ff2a882489903cd034e9c9c3f0ebfe9fa90f7ae","impliedFormat":1},{"version":"c548226db013facd262a4bc1c32c44a13eda7e1181aa6f569f9bdd531c95b0d5","impliedFormat":1},{"version":"37db669ef9a2ff2244d8e13aa8df9237fe3135c24facf0d62e7751fd49832386","impliedFormat":1},{"version":"543f461070fbd4b33bd05a5ba3e472e1297bb1db3c3101092bd69bfdddd3b1a1","impliedFormat":1},{"version":"929540ba3980906ac3f38e8567233028233c1c7e4d8921c1ce19604357028600","impliedFormat":1},{"version":"de5512f2a4c42776474808db1a6a8f12374be84392eeb649ad306330df41d0b3","impliedFormat":1},{"version":"7e96277aa5d7be6cddfdb78db26d986e00308e63f0202fb0d9fa39c826597dd4","impliedFormat":1},{"version":"3745762b83e5d49983a4183821125e3417473f992c4bf1d6dd6cea91efb4166f","impliedFormat":1},{"version":"ade458e350eda79fa74ecb678d9c51cc17f592d56df3e47286ef28e71401a989","impliedFormat":1},{"version":"109518ae11f199a3394af0e00818a0d1bf7334ccfabfaff8e87af0a026b67db1","impliedFormat":1},{"version":"88fb7ebc82e961435064d22022821b5708fe2b7c6fa5f09f368a0e7b3cc21a70","impliedFormat":1},{"version":"279c2ed4f57d90587de8e8dbadcb58b2ce2a0f6f7466ca07de24ad2cd50b6d12","impliedFormat":1},{"version":"02a711dc0da3eb73b8fae269ea1603cceaf395a7288429fdd724986974fa0ffa","impliedFormat":1},{"version":"0a5b8eb24e1c79ffbe5442926ff08d56d2ed195ae69523b9adadd1b6cacb5f91","impliedFormat":1},{"version":"caebb18abe3f02c23f8a16a2ee40ca5e26ac77f952cb15c660ff653cbad52144","impliedFormat":1},{"version":"319b53c7e5d6d0bb6427ab5eb8aac6dc8b364f68c5e425ef5ff25e293f567484","impliedFormat":1},{"version":"b7de25aa47d2893eaa5fe482685ac89fc589e12897d0ac046b22683f17497b5b","impliedFormat":1},{"version":"4359e8ba73374bb7d25c0d4181c4e9f9fd6174569897788596feac600ec69639","impliedFormat":1},{"version":"104d0506a41de5235716c9b11b8cf97398bfa14fbdf608561174b8789c23489c","impliedFormat":1},{"version":"f6d629d05333c4f6097d2e8d43622930677cfaa0d04451c19d149da012fb024e","impliedFormat":1},{"version":"230240e9143de90f6cb27b26529a77c1aaf2eaf78bf28d5b94b791754f24d320","impliedFormat":1},{"version":"74560f96bd420008b43a89d4b54dbae7188562ae30afdf20021c021eff2bbb94","impliedFormat":1},{"version":"8b3449c5b0bcc0f9b82cfd611179453444576ddc31ecfa0df32b94bba23c7962","impliedFormat":1},{"version":"28f13eae6f1c2e2c0b09b3a5899695881f586050cdc5e26ea70b84d53e245c53","impliedFormat":1},{"version":"eeb3eeaf375fc738138043ecc239471c0d4c1798e5426dadda6a370a6e430313","impliedFormat":1},{"version":"4190e5f9ae257a623994a52c1e41eb90c0b73d3a95cc35b792f0ef115782caba","impliedFormat":1},{"version":"f863533aa9746d2029dc17ec6200ad1b06f367adac0564e76c984f355378e8f8","impliedFormat":1},{"version":"566d39c79490d7d3c0edf99528a27ddbc190a43187de9c6903c539310dc32876","impliedFormat":1},{"version":"3203964312514a45f8f6ad1cd5e7fae5751313d104e7d7dd8e82f3213472f432","impliedFormat":1},{"version":"37ff372218ab7f5fe97a3ea0d889aa22a9cf856f49f3c071b285bb57f61126a4","impliedFormat":1},{"version":"0697562c198ad4887ee0c6ba87c81674415bf08622c38761a35ce284f79000a6","impliedFormat":1},{"version":"ec298fb58b43841e59e9cf74e5c4da1b28211549a111361d1b814b6cf9846ba3","impliedFormat":1},{"version":"21fc53b388d2ee497278c39cfda8fce06b48f179e44ec3d014c7d6385a8e9e2d","impliedFormat":1},{"version":"bec15f035f57b6db0ad6ad593bff48a145ee0a6aadded34447349dcfc36267c5","impliedFormat":1},{"version":"98d0e09ba964ffb04107d0b9abc217f53b2107824dc3332a4f67957feb0878db","impliedFormat":1},{"version":"b29fca8647befa819e15522effef648208d240212705b5eea931107f252c2cd4","impliedFormat":1},{"version":"3a2e2407e85251df154db1fc74a600ee6b64632b98b46f4dba4023ae0dc42c0a","impliedFormat":1},{"version":"db217a869e18a1e7e45fd2b0f5ce41a687a52b751cbf84edf66b992df3d41a22","impliedFormat":1},{"version":"b98b383a150d6f95df39eb6db6c652d5d1b8e51abc898b05a4bb4a467bd32000","impliedFormat":1},{"version":"32f947815fa11a2af64ff01f4ab8c34c898b4a0051b61007459df0d1e7c8d2c8","impliedFormat":1},{"version":"a6f12f14dbcb3fa60e7972637c7fd2ebee8ecaa320f88e4a6ea0233c77037d98","impliedFormat":1},{"version":"9514002fe1f72b3049408f9d6ca86e3cb961652f0e537d46f9b5452d577bb8cd","impliedFormat":1},{"version":"6b90bcd53023e3ed63fa38bf47045aec93f590f7457c62709041a74848590d82","impliedFormat":1},{"version":"54de542e1f492d0b9df28493e771f7967c46d0285024f053ac6d81b8e24c05cb","impliedFormat":1},{"version":"a5b04b41838cd784ac841c351ae2c626a45a4d6b7981f805f41c0f332f632e0b","impliedFormat":1},{"version":"cb11accd47db05613ce31bcf360fe9357c25df8d50c3a7f0e953b7569ea4bee5","impliedFormat":1},{"version":"9ce0e26737e23ee8b9c977edad1a4307c919eb1fec28941721afc77c9c49a814","impliedFormat":1},{"version":"04b32b104bf086362811496726a9a761a93ef1051e56107582433eeabb05216c","impliedFormat":1},{"version":"2d2c3ff10f8825d2a7120cb43addefb64111be064e35d270f662cde6ad1c8d5c","impliedFormat":1},{"version":"21dce3fdf75688a2ad305a1153c9f411aa27b5bf4ad7bf98b45e32d64b220fbb","impliedFormat":1},{"version":"89aff3abdf4a99430d0e8e90c51ee4ca7c63aa5add8c159f922f7e7e7864ca55","impliedFormat":1},{"version":"c1357329ff6857755d94e0625fda309fefa76e24304118c52d573bc4bbb54092","impliedFormat":1},{"version":"822bc7a5681e3decc4d781e99d313cdad6187449ea754c2e90f1980213303509","impliedFormat":1},{"version":"4130c2c57580e214060025c2df1460313ba070d3d6906c957755f10fc3b23ae0","impliedFormat":1},{"version":"56080e0d1944a5ae8a9464d9104d19c338716304b96aea91dafee4367de89229","impliedFormat":1},{"version":"f437b699c7942638c9772c53521e47989460c3b5a567f80a3797e0bf8ac93157","impliedFormat":1},{"version":"fcce9ec2fbf189ef75e386c47bebdb6a519725245922560a24f2b334413201cc","impliedFormat":1},{"version":"6ad25e6b2c1ea3f01d8a85537bda0dd2735b3367f54e2996368f1d2204e2e2ee","impliedFormat":1},{"version":"f86e6fa947feda6a87db6dac122d7308b6060687adfec373e7a4f62249a42cf9","impliedFormat":1},{"version":"2926b069186ed76252b428712b29db87a2518f8c4eef4b842622c5b5ab1996b5","impliedFormat":99},{"version":"dac51fba7e524be93b215ae0b429a8050ce969e61336a91beabb4094337781cc","impliedFormat":99},{"version":"1a36d2cde07d7b737b665bf6cf7824b6abcbb439f146ad1b4676cfe607936008","impliedFormat":99},{"version":"66181539572954871da89df777217a2feb17640a32c993fd25f9a004ff4bd1b2","impliedFormat":1},{"version":"8e20b585eed1b1afb844f5d1f32e726b1ef00a829ac408ca118b7bfbadd2dea9","impliedFormat":99},{"version":"5929864ce17fba74232584d90cb721a89b7ad277220627cc97054ba15a98ea8f","impliedFormat":1},{"version":"763fe0f42b3d79b440a9b6e51e9ba3f3f91352469c1e4b3b67bfa4ff6352f3f4","impliedFormat":1},{"version":"25c8056edf4314820382a5fdb4bb7816999acdcb929c8f75e3f39473b87e85bc","impliedFormat":1},{"version":"c464d66b20788266e5353b48dc4aa6bc0dc4a707276df1e7152ab0c9ae21fad8","impliedFormat":1},{"version":"78d0d27c130d35c60b5e5566c9f1e5be77caf39804636bc1a40133919a949f21","impliedFormat":1},{"version":"c6fd2c5a395f2432786c9cb8deb870b9b0e8ff7e22c029954fabdd692bff6195","impliedFormat":1},{"version":"1d6e127068ea8e104a912e42fc0a110e2aa5a66a356a917a163e8cf9a65e4a75","impliedFormat":1},{"version":"5ded6427296cdf3b9542de4471d2aa8d3983671d4cac0f4bf9c637208d1ced43","impliedFormat":1},{"version":"7f182617db458e98fc18dfb272d40aa2fff3a353c44a89b2c0ccb3937709bfb5","impliedFormat":1},{"version":"cadc8aced301244057c4e7e73fbcae534b0f5b12a37b150d80e5a45aa4bebcbd","impliedFormat":1},{"version":"385aab901643aa54e1c36f5ef3107913b10d1b5bb8cbcd933d4263b80a0d7f20","impliedFormat":1},{"version":"9670d44354bab9d9982eca21945686b5c24a3f893db73c0dae0fd74217a4c219","impliedFormat":1},{"version":"0b8a9268adaf4da35e7fa830c8981cfa22adbbe5b3f6f5ab91f6658899e657a7","impliedFormat":1},{"version":"11396ed8a44c02ab9798b7dca436009f866e8dae3c9c25e8c1fbc396880bf1bb","impliedFormat":1},{"version":"ba7bc87d01492633cb5a0e5da8a4a42a1c86270e7b3d2dea5d156828a84e4882","impliedFormat":1},{"version":"4893a895ea92c85345017a04ed427cbd6a1710453338df26881a6019432febdd","impliedFormat":1},{"version":"c21dc52e277bcfc75fac0436ccb75c204f9e1b3fa5e12729670910639f27343e","impliedFormat":1},{"version":"13f6f39e12b1518c6650bbb220c8985999020fe0f21d818e28f512b7771d00f9","impliedFormat":1},{"version":"9b5369969f6e7175740bf51223112ff209f94ba43ecd3bb09eefff9fd675624a","impliedFormat":1},{"version":"4fe9e626e7164748e8769bbf74b538e09607f07ed17c2f20af8d680ee49fc1da","impliedFormat":1},{"version":"24515859bc0b836719105bb6cc3d68255042a9f02a6022b3187948b204946bd2","impliedFormat":1},{"version":"ea0148f897b45a76544ae179784c95af1bd6721b8610af9ffa467a518a086a43","impliedFormat":1},{"version":"24c6a117721e606c9984335f71711877293a9651e44f59f3d21c1ea0856f9cc9","impliedFormat":1},{"version":"aa5524e0509c2168c9493604acf51ef97d2027f03f3b38da097802d3aa719dc8","impliedFormat":1},{"version":"405822be75ad3e4d162e07439bac80c6bcc6dbae1929e179cf467ec0b9ee4e2e","impliedFormat":1},{"version":"0db18c6e78ea846316c012478888f33c11ffadab9efd1cc8bcc12daded7a60b6","impliedFormat":1},{"version":"d072cb61b086eeae042c45d85ea553a03b123d3e27dbac911faa1a695f5d6752","impliedFormat":1},{"version":"bd0532fd6556073727d28da0edfd1736417a3f9f394877b6d5ef6ad88fba1d1a","impliedFormat":1},{"version":"89167d696a849fce5ca508032aabfe901c0868f833a8625d5a9c6e861ef935d2","impliedFormat":1},{"version":"615ba88d0128ed16bf83ef8ccbb6aff05c3ee2db1cc0f89ab50a4939bfc1943f","impliedFormat":1},{"version":"a4d551dbf8746780194d550c88f26cf937caf8d56f102969a110cfaed4b06656","impliedFormat":1},{"version":"8bd86b8e8f6a6aa6c49b71e14c4ffe1211a0e97c80f08d2c8cc98838006e4b88","impliedFormat":1},{"version":"317e63deeb21ac07f3992f5b50cdca8338f10acd4fbb7257ebf56735bf52ab00","impliedFormat":1},{"version":"4732aec92b20fb28c5fe9ad99521fb59974289ed1e45aecb282616202184064f","impliedFormat":1},{"version":"2e85db9e6fd73cfa3d7f28e0ab6b55417ea18931423bd47b409a96e4a169e8e6","impliedFormat":1},{"version":"c46e079fe54c76f95c67fb89081b3e399da2c7d109e7dca8e4b58d83e332e605","impliedFormat":1},{"version":"bf67d53d168abc1298888693338cb82854bdb2e69ef83f8a0092093c2d562107","impliedFormat":1},{"version":"299fd0c281633d8dbfbe5f44c5f2850fe37392da6fd3b9cca3cb4e10cda16432","impliedFormat":1},{"version":"dbe5bb36003e3c3bd02d229662eab6a3ad56d2d11af3bb736d9e8e90f611d347","impliedFormat":99},{"version":"124ab25ec1f90d0485d3392e72f4b4fc52a3e2a6301d74eaa6ede41727094d10","impliedFormat":99},{"version":"8e20b585eed1b1afb844f5d1f32e726b1ef00a829ac408ca118b7bfbadd2dea9","impliedFormat":99},{"version":"3aa236d922d6447e405f662a789dfe99052a33e971eeda03c34982d636ae119a","impliedFormat":99},{"version":"367546a9101d0516f076a316b42788d4100c4b11ba8ab524e43da449ce427f1a","impliedFormat":99},{"version":"d2fd65eea869f39cbdcc782ecd32932e1b327eba90d5bba4c0d0742e1f362bbf","impliedFormat":99},{"version":"bc640c39437752657c123ec9810ac440d3d3e5bf707a53e55218f56120f7db2f","impliedFormat":99},{"version":"e4e2582cff4273be37fb0ce2d9a8a63938cef16fd55718f6c1fe267e6150d079","impliedFormat":99},{"version":"e30d684087e1cb885668978c97df64e6f8c94e036b2bc4b6c281801f3db2dca6","signature":"88e9e8204ef4a66a53cff2939f1567a3fe66ca199edc742b3418bfcccb17c737"},{"version":"85ee2513d76319ac025a0b70dfef3fbf52ca8fc96de4a0200e26f58c1c72ebfa","impliedFormat":1},{"version":"f79f4ad5f9763423a3194163b0121105b15eea7dfe00aae027ab8027e3b262ee","impliedFormat":1},{"version":"352dbd4f43da59969a875b8ef269edeebd7084ad30eabc30fed3f54152c92079","signature":"336683810b9d68bcd23b2988b6d78d20df83db1a3705225a7697509d614feeaa"},{"version":"539d31ca534e541aa10204db203408a72567a19420dd7e7f845ac4043f342625","signature":"b7f5d57447bab671293b6ced9bfe11edc73d8ee688607016cdd9dc06db7bf4b2"},{"version":"ab24d3bbf9e701bf863fd8e8b5b583c273b7727c2b87e72bab8952dfcda8424d","signature":"f779ea33c720ddcfcd064f6a9a37b22c67e9a88caa10d4824f18986c7caaec95"},{"version":"82db9876a2508876c9097d3a8fd6266d40e5ee3c31c7d618bfbfd964508a547f","signature":"979c537c181fdd4c1ef0ad313bca6a5e6cda84dc4dec244a1d8a443806ea4e8e"},{"version":"2f20ff383f5e2ecb28eff9b7480e50ff3fdf7ad0dc65624a0e4f5f059f68fc7c","signature":"7e7ce94b627d814286b35d542d0af30f670be7aa6bb20a49862afc2dce36b991"},{"version":"ae27442de24a0fc46536a24eacd24923bf4c0e9b53617058fb95794f4c6e7f9d","signature":"fb113ea024b096ad70150a60310c92f2d25d69622d0da6f0e7dcaa28a135431b"},{"version":"4f07a948e1278132b36ef0bc9f583fe3d70068268093bae8f848b73a7d2d3a36","signature":"de405f75279759c535d5e6d5833d8ff74401dd180cc7545f09a5ec3499f5358b"},{"version":"633e51786c336c0e9bf8294337465a7aac0d5f6ba4e26f33e0f9607e548e2bd3","signature":"b241541217a87382b744a6049a284b3640248ed6e52e139b117cbdfe5032173b"},{"version":"8aa255453712ce8df1652947a0406769415ee7113c54551310e36cf6ab9db99c","impliedFormat":99},{"version":"6ef2ac05da44657a9a2fc1cd47198d2474a0f25d8ea02dcbb9c4a1e45a96dd9d","impliedFormat":99},{"version":"f60e3e3060207ac982da13363181fd7ee4beecc19a7c569f0d6bb034331066c2","impliedFormat":1},{"version":"17230b34bb564a3a2e36f9d3985372ccab4ad1722df2c43f7c5c2b553f68e5db","impliedFormat":1},{"version":"6e5c9272f6b3783be7bdddaf207cccdb8e033be3d14c5beacc03ae9d27d50929","impliedFormat":1},{"version":"21ac4cf3f8d8c6e1201cb31f600be708c9a37867fc5c73b7ccf80560fae591c8","impliedFormat":1},{"version":"0dfe35191a04e8f9dc7caeb9f52f2ee07402736563d12cbccd15fb5f31ac877f","impliedFormat":1},{"version":"798367363a3274220cbed839b883fe2f52ba7197b25e8cb2ac59c1e1fd8af6b7","impliedFormat":1},{"version":"3fa6aece30a44b769633651b07e0a0859e8194dfbbcfe0d7cda561bc521a5d7c","impliedFormat":1},{"version":"5aea76ab98173f2c230b1f78dc010da403da622c105c468ace9fe24e3b77883c","impliedFormat":99},{"version":"69f56d8b9be27fa77055e144ab75843b2c342f7e287d61770abfab9510e723da","impliedFormat":99},{"version":"142b50512c9d8cf126937c3a6bca2918e5511ce6eee6d8b2ecd3e03e2c36417a","signature":"f187c2eccb07f567f9864709a3245495b2b1139288c0f6eedb85a3f8b3a4d4b5"},{"version":"f3edef52ac440bb27714591ddc53aa2c70d212f0631228b72fe2bb05a264a8cd","signature":"4eeda7093ec1bb5c378566425b3ee2e0b66e96607f24817a6761c63022fb9ced"},{"version":"667d1c8037cbc4f7d361befefc442c78ee6c7f20eac3917ba0c5d218727f644e","signature":"35db1c3051224855ece8b4e9559fd9060b58807beffbee76bd84fc02161ca196"},{"version":"f2887579b54203864637ce79749433835e6c98616792f2038172dd779f41b4ec","signature":"76f94d429f01f6ada25d1afba2908262e845a70a5902bafee04fc91495981af0"},{"version":"3d82a4da044bf0e9cb6d1e4c3fe93c440553923c7c83577d98952a4b29f8b732","signature":"38fc02e0837e2e12ab8496dca1cf1fc9f95f045666a465f024d0b08fb0848655"},{"version":"72c6241cb4045ae40667c778dd8b15b8a2663dc5a4d47abf1a19ec6765b9c11d","signature":"2c2e42bc82b8fe6f97883adeb50494596723925293a021ee2162ca53ed36277f"},{"version":"59f9fbb0464e61a839f7418a3394ea0b3fdb57f09d15b505f0026061c58631de","signature":"49fe27f4f78aceaeeb0bc59abaf704f3b900ca27fe6bbfeeca44f4c6138aa9f6"},{"version":"f5310f50699c11ac2e16b6362ffc00d772010877039578ff6667b5a6e2117972","signature":"29f43632af5c9110b3913fdee3d082eabc9ab8badb7381b77923f4280cd6bd62"},{"version":"b13715eda434461367e0dc0d7374f313631e02b3fd5b03a299ac6b223312b802","impliedFormat":1},{"version":"c6196a8bb53c3bca8185c25a4319c7a60a3614b7b0475621c561e89860a4df59","impliedFormat":1},{"version":"7001d2b80d3339eb8e77d65f1c67451f732744877a1e96c4a992638fcce81842","impliedFormat":1},{"version":"f765178b420f129672a69f3545d932156475d38544a78246b5ae8d8fbe5d6eec","impliedFormat":1},{"version":"316f1486e15cbf7896425f0a16dfe12d447dd57cfb3244b8b119c77df870858f","impliedFormat":99},{"version":"aa73163e6501ac8db1d89ec74c5b5abafa8a79d7ebf8b09e3b319b054ded24fe","impliedFormat":99},{"version":"e7769b3b59ff701e10d6dcd878c85a5cbff2a62b0b46daa72c064553376faac5","signature":"4975e2d76d839c19f954a5d16134a7237988242eef53989057287ecdc6c5456e"},{"version":"d5b42c5ba7cd1e608b0e2575e5f979c8659637817460c4a86056a01f709455ab","impliedFormat":1},{"version":"7e7886e141f32ca1988c2cf4229f6548bd4633e8ad76aee82df301520eac1bd0","impliedFormat":1},{"version":"f744d7712f1e42df50fd3fab633fc9cbb7a0860bd6387fb0ac4d20cf1251582f","signature":"fa977e006cb03df84913e02933da6fbff01947692105d25725838497ab6af0cf"},{"version":"53745ef619640cb9085a63f3c0073a617cda112ab9234c2b532f56c86f321211","signature":"d92e17be3e016d3152317003a5afda6888c6f92e135f157e663061d815ed4fec"},{"version":"cd2982762d8724855909ed89eb5833e79e4f1f5b53a37453215587ac9c1a4fc6","signature":"546b396859131c79d5c3672c4dadc07f58e16eacfc0b449030e9ecf72518057f"},{"version":"a21b5e7e6051c3cad575d96e65c721ccb9b9c71b14236ddae12c30b9bd8037e0","signature":"00e3809722eaf0200dd0f2b1d7691f570b9fedc06a0357355eeea40488783c8d"},{"version":"f07bcb2e747512d9ab459a706744c0139248b110c53561e61d6968f83ed723c3","signature":"2c172b8dba477ed644763d763730feb966a67c291f58bdb725ed97122d00223a"},{"version":"4373450e31d81fc7a0e18bcf1ecd82ec62ac0dd935044572f9e53927bb51ed98","signature":"237d5ea54498195185d96f32eb6e2a445cf5afcede86eb236f57ddccf36b84ea"},"f83b492bc7a300aaa2db31f34c239347c0cd5ececeeb9380c99ccc79e9d12a2e",{"version":"533dd952ea55efc1ca1483731e8ca5c5a37ec635d527de558ae77157dfb2e9e9","signature":"3540d3799bca5c5c9184582b335345c569b1d3b528f8b8631bf965b50b6f7470"},{"version":"37ff5e81fb7b3feb7286fb700d0326cd5cb102554efe1d54272d504d7515ee96","signature":"756422fda43fe3ed7e7e80138fc22c752de583dead9282801d33edc24c4f98e7"},{"version":"4e17a2c56b1c461ab6ccbe599dd63e2eb886e75e5197975d9d7b2093f704bc4c","signature":"b6d4b061e2b92923b14c2f897dec134468702486aa0a68794cf5b8a331dc0a2b"},{"version":"0c951e45711298e8302f580956713ef9cf3cb3c7f1b17a61d39530cf6d3a5527","signature":"8244f468465ddedc3948410f37f08da4b09d0fce7226d3e73f1b28f1034c33bb"},{"version":"ae36a3b47fcdd10a14c21390e5ef262df2ab82f7124cd657c9b9d66f89245a55","signature":"18c1f9490f694e6154edf3724d3a46f44dfe05c8bc007a4e714bbc6d934d25fa"},{"version":"f76c05acf85eec5b9834958cd800f3cdeb352680245ecf722268001534541ca5","signature":"9ce9dc6faeed85f449d580f7027fa1fb5575463f0c4c41c9da9d4afe8609d3d3"},{"version":"e401594c28a64cfe9d70e1d59f89cc9688771bea62fa077ac3ad75273680f85a","signature":"9a689128ffa7489c30c262156d3f83114d59d39e43846716b0dd8fe156c305cb"},{"version":"101283fc055dcc7e693ad962ae92e1c28fd5d1c0573294ecbd0e4ea8c66ca7b4","signature":"05e00976d525947b701270a30845efe7bcf685f234d46dca71308b2fe2c367a8"},{"version":"110e818f9558e25d7f854d39cb0e6341dbf0cbecf20bf45c1cfe8bb317c45d43","signature":"093954ee9a15e837d90abae28e3f171adaf6e3e4e5e37b960df3a959bc6e7887"},{"version":"d959a355566fe16b47fa97e74ece8c3793533407f8b432ad3cf6704d63ee83ae","signature":"093954ee9a15e837d90abae28e3f171adaf6e3e4e5e37b960df3a959bc6e7887"},{"version":"c02ad401a5ab49764c20b04666c262a38021d30079395b559eb6d7d6ec5284de","signature":"093954ee9a15e837d90abae28e3f171adaf6e3e4e5e37b960df3a959bc6e7887"},{"version":"57e6eaf8838012c5ec1e95a5fd228b775a57bac2199a303ec631790bd9e69c11","signature":"093954ee9a15e837d90abae28e3f171adaf6e3e4e5e37b960df3a959bc6e7887"},{"version":"0058b76a077b40f4ade71ba1bb40d733abcacf1cf1334d9dc0e59a7e8e1eda63","signature":"093954ee9a15e837d90abae28e3f171adaf6e3e4e5e37b960df3a959bc6e7887"},{"version":"424e793ff8f976d9f4f8c5c8593c6a0e8d9ab9451504c00f2a2724967db80aa1","signature":"093954ee9a15e837d90abae28e3f171adaf6e3e4e5e37b960df3a959bc6e7887"},{"version":"9978a554c0956034752dfa86755ac7d42e3b2d1a37eaff0f2aaf787cf7e1ded4","signature":"093954ee9a15e837d90abae28e3f171adaf6e3e4e5e37b960df3a959bc6e7887"},{"version":"fa5fce5a1ded71ac044f0518b1a1f077c254500b2bea6a3d1cd9a32745ba8c4d","signature":"093954ee9a15e837d90abae28e3f171adaf6e3e4e5e37b960df3a959bc6e7887"},{"version":"7bb134bde8f3b9419d1497295642e678cefe74a65f9704a0c649a018d51a11e4","signature":"093954ee9a15e837d90abae28e3f171adaf6e3e4e5e37b960df3a959bc6e7887"},{"version":"4d10ef114834dc519780f6a3ca7a617b369bb80e48389d419de14646fb048a57","signature":"093954ee9a15e837d90abae28e3f171adaf6e3e4e5e37b960df3a959bc6e7887"},{"version":"2aee6591664c8fb4238deb6c6ac165e792366af67c0bcae10e9fb355527acdf4","signature":"7d0b5fef8505d33eb8ebe4eb99b40e984f06fc001b48c32e273584926b7b3968"},{"version":"226746834952997e89a7c3a1c5e0c33d45f5956084a84ded5b16323d8cec6815","signature":"55cec8d91a26d6b86c178cfcbae6c67b64167c7c621e7b629f600c6bc4e0d973"},{"version":"19de5922e44d6a0a9320cbdd1d0ef95fc1efdd6792feecdc39b679f69c242be6","impliedFormat":1},{"version":"4ff00b2905a1384d8fe1e4eb676767b5596b6ac59cac400ace8296a48fbf80f7","signature":"d70c7dc22bbc1568f042333186a053ed8e6a316f40be8fb527f69dfc6ecb54ba"},{"version":"c84c9ca5a6b62e72d9c29409fdc6b4430f57db0743d828fd5699eab983d41eb0","signature":"34729fd55d1442f88a844a50c6976c0f07e7cd17e7febe4cc639b4ff6f13a416"}],"root":[192,326,[329,336],[348,355],362,[365,391],393,394],"options":{"composite":true,"declaration":true,"declarationMap":true,"esModuleInterop":true,"exactOptionalPropertyTypes":true,"module":99,"noUncheckedIndexedAccess":true,"noUncheckedSideEffectImports":true,"outDir":"./dist","rootDir":"./packages","skipLibCheck":true,"sourceMap":true,"strict":true,"target":10,"verbatimModuleSyntax":true},"referencedMap":[[277,1],[278,2],[275,3],[319,4],[279,2],[276,5],[361,6],[322,7],[320,2],[347,8],[327,2],[328,9],[392,9],[337,2],[338,10],[321,2],[193,11],[324,2],[131,12],[132,12],[133,13],[72,14],[134,15],[135,16],[136,17],[70,2],[137,18],[138,19],[139,20],[140,21],[141,22],[142,23],[143,23],[144,24],[145,25],[146,26],[147,27],[73,2],[71,2],[148,28],[149,29],[150,30],[190,31],[151,32],[152,33],[153,32],[154,34],[155,35],[156,36],[157,37],[158,37],[159,37],[160,38],[161,39],[162,40],[163,41],[164,42],[165,43],[166,43],[167,44],[168,2],[169,2],[170,45],[171,46],[172,45],[173,47],[174,48],[175,49],[176,50],[177,51],[178,52],[179,53],[180,54],[181,55],[182,56],[183,57],[184,58],[185,59],[186,60],[187,61],[74,32],[75,2],[76,62],[77,63],[78,2],[79,11],[80,2],[122,64],[123,65],[124,66],[125,66],[126,67],[127,2],[128,15],[129,68],[130,65],[188,69],[189,70],[346,71],[345,72],[344,71],[360,73],[318,2],[244,74],[269,2],[270,75],[194,2],[196,76],[213,77],[212,78],[214,79],[215,80],[216,81],[217,77],[218,81],[219,77],[211,5],[220,79],[221,82],[222,83],[223,77],[225,84],[226,85],[227,86],[224,87],[228,88],[229,89],[230,90],[210,91],[231,92],[232,93],[233,94],[209,95],[237,96],[241,97],[238,88],[201,98],[200,99],[202,100],[205,101],[239,102],[268,103],[234,104],[242,105],[207,106],[208,88],[203,2],[198,88],[206,107],[243,108],[240,109],[204,108],[235,108],[266,110],[199,109],[267,109],[236,111],[195,2],[245,112],[246,113],[247,111],[249,114],[250,111],[251,90],[252,115],[253,116],[265,117],[255,118],[256,111],[257,119],[258,111],[248,119],[259,111],[260,111],[261,90],[262,120],[263,111],[264,111],[254,121],[271,88],[272,122],[197,2],[273,90],[274,123],[359,88],[325,124],[323,125],[191,126],[343,127],[340,128],[342,129],[341,2],[339,2],[363,130],[364,131],[358,132],[356,130],[357,130],[67,2],[68,2],[13,2],[11,2],[12,2],[17,2],[16,2],[2,2],[18,2],[19,2],[20,2],[21,2],[22,2],[23,2],[24,2],[25,2],[3,2],[26,2],[27,2],[4,2],[28,2],[32,2],[29,2],[30,2],[31,2],[33,2],[34,2],[35,2],[5,2],[36,2],[37,2],[38,2],[39,2],[6,2],[43,2],[40,2],[41,2],[42,2],[44,2],[7,2],[45,2],[50,2],[51,2],[46,2],[47,2],[48,2],[49,2],[8,2],[55,2],[52,2],[53,2],[54,2],[56,2],[9,2],[57,2],[58,2],[59,2],[61,2],[60,2],[62,2],[63,2],[10,2],[69,2],[64,2],[1,2],[65,2],[66,2],[15,2],[14,2],[98,133],[110,134],[96,135],[111,130],[120,136],[87,137],[88,138],[86,139],[119,128],[114,140],[118,141],[90,142],[107,143],[89,144],[117,145],[84,146],[85,140],[91,147],[92,2],[97,148],[95,147],[82,149],[121,150],[112,151],[101,152],[100,147],[102,153],[105,154],[99,155],[103,156],[115,128],[93,157],[94,158],[106,159],[83,130],[109,160],[108,147],[104,161],[113,2],[81,2],[116,162],[317,163],[295,164],[305,165],[294,164],[315,166],[286,167],[285,139],[314,128],[308,168],[313,169],[288,170],[302,171],[287,172],[311,173],[283,174],[282,128],[312,175],[284,176],[289,177],[290,2],[293,177],[280,2],[316,178],[306,179],[297,180],[298,181],[300,182],[296,183],[299,184],[309,128],[291,185],[292,186],[301,187],[281,130],[304,179],[303,177],[307,2],[310,188],[351,189],[352,190],[379,191],[380,192],[381,193],[382,193],[383,194],[384,191],[385,191],[386,195],[387,196],[388,197],[389,191],[350,198],[353,199],[390,200],[391,201],[354,202],[365,203],[378,204],[362,205],[394,206],[336,207],[332,208],[329,2],[335,209],[334,210],[393,211],[333,212],[331,208],[330,208],[349,213],[192,214],[355,215],[373,216],[375,217],[377,218],[374,219],[367,2],[371,2],[366,2],[368,2],[369,2],[376,2],[348,220],[370,2],[326,220],[372,221]],"latestChangedDtsFile":"./dist/db/generated/prisma/browser.d.ts","version":"5.9.3"} \ No newline at end of file