From e1b756d46ddf3b1dda0d5b2bbd53c3523f5c0065 Mon Sep 17 00:00:00 2001 From: rcpthongta <220981657+rcpthongta@users.noreply.github.com> Date: Thu, 17 Sep 2026 23:51:56 +0700 Subject: [PATCH 001/121] chore(editor-config): add editorconfig and vscode extension recommendations --- .editorconfig | 13 +++++++++++++ .vscode/extensions.json | 5 +++++ 2 files changed, 18 insertions(+) create mode 100644 .editorconfig create mode 100644 .vscode/extensions.json diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..d5d0a50 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,13 @@ +root = true + +[*] + +charset = utf-8 +end_of_line = lf +indent_size = 2 +indent_style = space +insert_final_newline = true + +[*.md] + +trim_trailing_whitespace = false diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 0000000..fda5ad5 --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,5 @@ +{ + "recommendations": [ + "editorconfig.editorconfig" + ] +} From 4f3d328ba7fd985707b5ea387fb65bde821de15d Mon Sep 17 00:00:00 2001 From: rcpthongta <220981657+rcpthongta@users.noreply.github.com> Date: Thu, 17 Sep 2026 23:54:15 +0700 Subject: [PATCH 002/121] chore(editor-config): configure cspell and add extension recommendation --- .vscode/extensions.json | 3 ++- .vscode/settings.json | 10 ++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) create mode 100644 .vscode/settings.json diff --git a/.vscode/extensions.json b/.vscode/extensions.json index fda5ad5..4624518 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -1,5 +1,6 @@ { "recommendations": [ - "editorconfig.editorconfig" + "editorconfig.editorconfig", + "streetsidesoftware.code-spell-checker" ] } diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..e24ca27 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,10 @@ +{ + "cSpell.allowCompoundWords": true, + "cSpell.ignoreRegExpList": [ + "/[\\u0E00-\\u0E7F]/g" + ], + "cSpell.language": "en,en-US", + "cSpell.words": [ + "rcpthongta" + ] +} From c5c981e243178818fb9f2d3e444f2ab0d288c5da Mon Sep 17 00:00:00 2001 From: rcpthongta <220981657+rcpthongta@users.noreply.github.com> Date: Fri, 18 Sep 2026 00:21:54 +0700 Subject: [PATCH 003/121] chore(project): initialize package.json and npmrc configuration --- .npmrc | 3 +++ package.json | 23 +++++++++++++++++++++++ 2 files changed, 26 insertions(+) create mode 100644 .npmrc create mode 100644 package.json diff --git a/.npmrc b/.npmrc new file mode 100644 index 0000000..21159d8 --- /dev/null +++ b/.npmrc @@ -0,0 +1,3 @@ +engine-strict = true +fund = false +update-notifier = false diff --git a/package.json b/package.json new file mode 100644 index 0000000..56463ab --- /dev/null +++ b/package.json @@ -0,0 +1,23 @@ +{ + "version": "0.0.1", + "name": "string-utils", + "description": "A lightweight, type-safe, dependency-free string utility library for TypeScript and modern JavaScript applications.", + "author": "rcpthongta", + "license": "MIT", + "repository": { + "type": "git", + "url": "git+https://github.com/rcpthongta/string-utils.git" + }, + "bugs": { + "url": "https://github.com/rcpthongta/string-utils/issues" + }, + "keywords": [ + "string", + "string-utils", + "string-library", + "string-helpers" + ], + "engines": { + "node": ">=22" + } +} From ff3a145cd661b5b3eb4cb6a068301592a9b1855a Mon Sep 17 00:00:00 2001 From: rcpthongta <220981657+rcpthongta@users.noreply.github.com> Date: Fri, 18 Sep 2026 00:27:23 +0700 Subject: [PATCH 004/121] chore(deps-dev): add '@types/node' --- package-lock.json | 36 ++++++++++++++++++++++++++++++++++++ package.json | 3 +++ 2 files changed, 39 insertions(+) create mode 100644 package-lock.json diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..b3a57a1 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,36 @@ +{ + "name": "string-utils", + "version": "0.0.1", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "string-utils", + "version": "0.0.1", + "license": "MIT", + "devDependencies": { + "@types/node": "^24.13.5" + }, + "engines": { + "node": ">=22" + } + }, + "node_modules/@types/node": { + "version": "24.13.5", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.13.5.tgz", + "integrity": "sha512-TXyindR+lBr22aJIdMQzCFHPHR6cR4js838mRDCSz5hOKWZvZwsXSSiXDmjRj4iJmgl+sR9O+1mkoVBSMadNug==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~7.18.0" + } + }, + "node_modules/undici-types": { + "version": "7.18.2", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.18.2.tgz", + "integrity": "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==", + "dev": true, + "license": "MIT" + } + } +} diff --git a/package.json b/package.json index 56463ab..c77f361 100644 --- a/package.json +++ b/package.json @@ -19,5 +19,8 @@ ], "engines": { "node": ">=22" + }, + "devDependencies": { + "@types/node": "^24.13.5" } } From 1b5303a379da704dda5c0d0215763f4ec1197d97 Mon Sep 17 00:00:00 2001 From: rcpthongta <220981657+rcpthongta@users.noreply.github.com> Date: Fri, 18 Sep 2026 00:28:00 +0700 Subject: [PATCH 005/121] chore(git): ignore node_modules directory --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..40dde32 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +# Dependencies directory + +node_modules/* From 39f967262b13347eed0cc9bfa3f106bb118d495c Mon Sep 17 00:00:00 2001 From: rcpthongta <220981657+rcpthongta@users.noreply.github.com> Date: Fri, 18 Sep 2026 00:31:00 +0700 Subject: [PATCH 006/121] chore(git): ignore vscode user files while keeping shared configs --- .gitignore | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/.gitignore b/.gitignore index 40dde32..176bd1a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,11 @@ # Dependencies directory node_modules/* + +# Text editor directory and files • Visual Studio Code + +.vscode/* +!.vscode/extensions.json +!.vscode/launch.json +!.vscode/settings.json +!.vscode/tasks.json From dbd6b89c07c5852203f37bd399086f9e30a4b670 Mon Sep 17 00:00:00 2001 From: rcpthongta <220981657+rcpthongta@users.noreply.github.com> Date: Fri, 18 Sep 2026 00:49:44 +0700 Subject: [PATCH 007/121] chore(deps-dev): add typescript, tsup, and rimraf --- package-lock.json | 1642 ++++++++++++++++++++++++++++++++++++++++++++- package.json | 5 +- 2 files changed, 1645 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index b3a57a1..b3680bd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,12 +9,912 @@ "version": "0.0.1", "license": "MIT", "devDependencies": { - "@types/node": "^24.13.5" + "@types/node": "^24.13.5", + "rimraf": "^6.1.3", + "tsup": "^8.5.1", + "typescript": "^5.9.3" }, "engines": { "node": ">=22" } }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.7.tgz", + "integrity": "sha512-EKX3Qwmhz1eMdEJokhALr0YiD0lhQNwDqkPYyPhiSwKrh7/4KRjQc04sZ8db+5DVVnZ1LmbNDI1uAMPEUBnQPg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.7.tgz", + "integrity": "sha512-jbPXvB4Yj2yBV7HUfE2KHe4GJX51QplCN1pGbYjvsyCZbQmies29EoJbkEc+vYuU5o45AfQn37vZlyXy4YJ8RQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.7.tgz", + "integrity": "sha512-62dPZHpIXzvChfvfLJow3q5dDtiNMkwiRzPylSCfriLvZeq0a1bWChrGx/BbUbPwOrsWKMn8idSllklzBy+dgQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.7.tgz", + "integrity": "sha512-x5VpMODneVDb70PYV2VQOmIUUiBtY3D3mPBG8NxVk5CogneYhkR7MmM3yR/uMdITLrC1ml/NV1rj4bMJuy9MCg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.7.tgz", + "integrity": "sha512-5lckdqeuBPlKUwvoCXIgI2D9/ABmPq3Rdp7IfL70393YgaASt7tbju3Ac+ePVi3KDH6N2RqePfHnXkaDtY9fkw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.7.tgz", + "integrity": "sha512-rYnXrKcXuT7Z+WL5K980jVFdvVKhCHhUwid+dDYQpH+qu+TefcomiMAJpIiC2EM3Rjtq0sO3StMV/+3w3MyyqQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.7.tgz", + "integrity": "sha512-B48PqeCsEgOtzME2GbNM2roU29AMTuOIN91dsMO30t+Ydis3z/3Ngoj5hhnsOSSwNzS+6JppqWsuhTp6E82l2w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.7.tgz", + "integrity": "sha512-jOBDK5XEjA4m5IJK3bpAQF9/Lelu/Z9ZcdhTRLf4cajlB+8VEhFFRjWgfy3M1O4rO2GQ/b2dLwCUGpiF/eATNQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.7.tgz", + "integrity": "sha512-RkT/YXYBTSULo3+af8Ib0ykH8u2MBh57o7q/DAs3lTJlyVQkgQvlrPTnjIzzRPQyavxtPtfg0EopvDyIt0j1rA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.7.tgz", + "integrity": "sha512-RZPHBoxXuNnPQO9rvjh5jdkRmVizktkT7TCDkDmQ0W2SwHInKCAV95GRuvdSvA7w4VMwfCjUiPwDi0ZO6Nfe9A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.7.tgz", + "integrity": "sha512-GA48aKNkyQDbd3KtkplYWT102C5sn/EZTY4XROkxONgruHPU72l+gW+FfF8tf2cFjeHaRbWpOYa/uRBz/Xq1Pg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.7.tgz", + "integrity": "sha512-a4POruNM2oWsD4WKvBSEKGIiWQF8fZOAsycHOt6JBpZ+JN2n2JH9WAv56SOyu9X5IqAjqSIPTaJkqN8F7XOQ5Q==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.7.tgz", + "integrity": "sha512-KabT5I6StirGfIz0FMgl1I+R1H73Gp0ofL9A3nG3i/cYFJzKHhouBV5VWK1CSgKvVaG4q1RNpCTR2LuTVB3fIw==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.7.tgz", + "integrity": "sha512-gRsL4x6wsGHGRqhtI+ifpN/vpOFTQtnbsupUF5R5YTAg+y/lKelYR1hXbnBdzDjGbMYjVJLJTd2OFmMewAgwlQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.7.tgz", + "integrity": "sha512-hL25LbxO1QOngGzu2U5xeXtxXcW+/GvMN3ejANqXkxZ/opySAZMrc+9LY/WyjAan41unrR3YrmtTsUpwT66InQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.7.tgz", + "integrity": "sha512-2k8go8Ycu1Kb46vEelhu1vqEP+UeRVj2zY1pSuPdgvbd5ykAw82Lrro28vXUrRmzEsUV0NzCf54yARIK8r0fdw==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.7.tgz", + "integrity": "sha512-hzznmADPt+OmsYzw1EE33ccA+HPdIqiCRq7cQeL1Jlq2gb1+OyWBkMCrYGBJ+sxVzve2ZJEVeePbLM2iEIZSxA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.7.tgz", + "integrity": "sha512-b6pqtrQdigZBwZxAn1UpazEisvwaIDvdbMbmrly7cDTMFnw/+3lVxxCTGOrkPVnsYIosJJXAsILG9XcQS+Yu6w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.7.tgz", + "integrity": "sha512-OfatkLojr6U+WN5EDYuoQhtM+1xco+/6FSzJJnuWiUw5eVcicbyK3dq5EeV/QHT1uy6GoDhGbFpprUiHUYggrw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.7.tgz", + "integrity": "sha512-AFuojMQTxAz75Fo8idVcqoQWEHIXFRbOc1TrVcFSgCZtQfSdc1RXgB3tjOn/krRHENUB4j00bfGjyl2mJrU37A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.7.tgz", + "integrity": "sha512-+A1NJmfM8WNDv5CLVQYJ5PshuRm/4cI6WMZRg1by1GwPIQPCTs1GLEUHwiiQGT5zDdyLiRM/l1G0Pv54gvtKIg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.7.tgz", + "integrity": "sha512-+KrvYb/C8zA9CU/g0sR6w2RBw7IGc5J2BPnc3dYc5VJxHCSF1yNMxTV5LQ7GuKteQXZtspjFbiuW5/dOj7H4Yw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.7.tgz", + "integrity": "sha512-ikktIhFBzQNt/QDyOL580ti9+5mL/YZeUPKU2ivGtGjdTYoqz6jObj6nOMfhASpS4GU4Q/Clh1QtxWAvcYKamA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.7.tgz", + "integrity": "sha512-7yRhbHvPqSpRUV7Q20VuDwbjW5kIMwTHpptuUzV+AA46kiPze5Z7qgt6CLCK3pWFrHeNfDd1VKgyP4O+ng17CA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.7.tgz", + "integrity": "sha512-SmwKXe6VHIyZYbBLJrhOoCJRB/Z1tckzmgTLfFYOfpMAx63BJEaL9ExI8x7v0oAO3Zh6D/Oi1gVxEYr5oUCFhw==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.7.tgz", + "integrity": "sha512-56hiAJPhwQ1R4i+21FVF7V8kSD5zZTdHcVuRFMW0hn753vVfQN8xlx4uOPT4xoGH0Z/oVATuR82AiqSTDIpaHg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.6.0.tgz", + "integrity": "sha512-T7jf+5zgsZHwNJ4lvQ7/aezbyk0nNX+zJVWpmHA7VYsEx7a7qr5Rg5IbtJFqkgze5Y2sruq1RUY8Q837Od7iFw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@napi-rs/lzma-linux-x64-gnu": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/@napi-rs/lzma-linux-x64-gnu/-/lzma-linux-x64-gnu-1.5.1.tgz", + "integrity": "sha512-oTXEIha4SsuXdTA4Iyskj0kpdx2yVXdhd75c2v3xGrHFfVMsbhTPZU/nMPL4sWKo4pBHm3aucLaqGlF696dTyQ==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^22.20 || ^24.12 || >=25" + } + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.63.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.63.3.tgz", + "integrity": "sha512-w3Jnvi1ocaVm/c7yVPpfB98XeSRBMyzp6njL5MVVbGyXjpmUkN+s6Hp4t0PqhGCCaI1ZHMKXt/w0lA1RCaLVcw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.63.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.63.3.tgz", + "integrity": "sha512-uI/ESiaIbbRYAEhzy8PCUWDp1hB0bjAqM06mW9flOoNO4Q8DQpeoREhBR5Hegfl+wpXiguyJv6XSPzEN7OxyHQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.63.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.63.3.tgz", + "integrity": "sha512-oxhrd1jmXLwWZ83eQYDXxuqRdkqkzrjR3JobKeuUyfdNZo11FuQIvqEOZhyIT7OBHxXoGslDDjN0cQcM6T0TqQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.63.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.63.3.tgz", + "integrity": "sha512-7/YiIMghVE8DrxKvNdorAaJVdriOFgOIpdStnPx8ppx5zfTwC3jBCSEAIzB7JD5404m65THl6H93UTTVUvypmg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.63.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.63.3.tgz", + "integrity": "sha512-GXFZRRoMAytaI5z6N3Zhfw0WL18Q0M8r95D5hlC4GqE/lGk8pbSJNUBoOWDfbm6dTciqHj2nU87tI5f6XhQiOg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.63.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.63.3.tgz", + "integrity": "sha512-77W+8X3ddYgPxUpB8nZFQs2Mq+wc4HVlcSRtApXLjYBcnPMkttrSnU8VwKQjeWYhMsITHFs5cWBQ8vz1Q+5RHQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.63.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.63.3.tgz", + "integrity": "sha512-FVkwK+iUC+mq+GipVK46rRVticfAPtvPUNlqlGXUDxdVk/UGjQiiiUVPUrEXdSpU2ufU0XxLGyTqDtBidDOVmg==", + "cpu": [ + "arm" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.63.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.63.3.tgz", + "integrity": "sha512-+aGU1t3398yQOVj1Bz8o3e+KtswxAPvO+mtxtNdfXYMkXIHu7XhhkCD7/DEH9q8tF8uhDnMWvfpUKI8y1sZJsg==", + "cpu": [ + "arm" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.63.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.63.3.tgz", + "integrity": "sha512-cR0kjpRXR2KJ2oQK8E2KTPtphs+b9hZ8IhTZubNryt/RsqgdOZBQ2Zq0q5UedtiIi0rs3jVhJh55RE1ZHUVGUA==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.63.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.63.3.tgz", + "integrity": "sha512-y1RYi4Q3/9ByVWSSt9kX2ustE0B7kFYbJ6zZdVZVyqopZs3yhCTwRfrjIX4vezUJInma/Gs6BOFDJg7yZmJ0IQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.63.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.63.3.tgz", + "integrity": "sha512-DNhEA5viIj3Z5bZLE4z4oV8N5ozWqDwyt7T6KG7VdLDJ0nW+rNOYlphBl4/3HQkK75qipPLsVOfStHHOwN9WSg==", + "cpu": [ + "loong64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-musl": { + "version": "4.63.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.63.3.tgz", + "integrity": "sha512-17gQCqrIpXBX2Cmi9/TygnVOqGbzsba/iaqcYSL8FY7lNugg+7AiYNs5c5nKWD+NRQha36Sa0CqkJqH4XVHwnQ==", + "cpu": [ + "loong64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.63.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.63.3.tgz", + "integrity": "sha512-6LwVnZRIyINpdku/yOcI8Tm9YqLmhHK5emmlOOnW9tO0SYEm1FmKPcsSAGp0NBlqR2P04xaND4jvN6sTHqhq8A==", + "cpu": [ + "ppc64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-musl": { + "version": "4.63.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.63.3.tgz", + "integrity": "sha512-xMUqkTXlEUtI/p5AAukMwBRr1enU3efsTeF+bskeFfk8t1C9rcC8sLREcZXmTfAXEbvRdJVSonVJez3TMlbR3w==", + "cpu": [ + "ppc64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.63.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.63.3.tgz", + "integrity": "sha512-S3E94co9F9WRRqEaUoQZ38K1gCz6KiM+nL7/3ijq7fDGF3OznjS5TasgYITlvl27GQKtu4lOAOsr5MFwkijvOA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.63.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.63.3.tgz", + "integrity": "sha512-1QtRDwG42x5BJI3s9mxu5rEjDnfbSnk20HQ9/ylTAYnSwYwxMVb+Vgu34wzzTQ7ogqBybebgQNUDAvZVQ38DbA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.63.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.63.3.tgz", + "integrity": "sha512-BQhejF6ZXOpxbngiNTP12GCGQeaDVL2QXGeBVViKIYzFHM5RKxTxwUMB1fr1BeNFphFMpnRqC5QSXFSa4z6UQw==", + "cpu": [ + "s390x" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.63.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.63.3.tgz", + "integrity": "sha512-SXagRwnI2Wlwlitllu59UK/nGVbD1CKPcNqDplHwIC4BqJcpXFjD32d1R/RbuISa95HdQrZM3/7v4bKiowFaLA==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.63.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.63.3.tgz", + "integrity": "sha512-2IPozoEALRCziGqE8O9KMK60PMu5TS1huv4fwoeCexj+WjmcwFtX9CTOVbfXCUqcELAubEwRFPYlzb/WvwY2HQ==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openbsd-x64": { + "version": "4.63.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.63.3.tgz", + "integrity": "sha512-AoxqosUHT9IX54hFn2TiN6A7d6ZKTtE6pd2bqWtqkkNJ6HJGaU6FRouGX8L1O7R/ZwsnCnpQrHzb4pDEx+UHRQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.63.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.63.3.tgz", + "integrity": "sha512-d+CaftKgmkFBzCwezMqqy1d0QNNYugqLCMcYVQWBy5SS2YfeMP8Q8ripkgx9O8IyBXXLHrJ+aaCV4U96usv6Yg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.63.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.63.3.tgz", + "integrity": "sha512-xXlDF6nR1eOuXbdDy5Hl5fmtY7teUDevF/k0O7IPoZe4Tpmdv+lgdE5JRsnhQtt37ql9P0VF2kAN9a0OCZdo+Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.63.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.63.3.tgz", + "integrity": "sha512-YtXAgLN+JP7Ay6qG3eWhc7IHMQPzLc8r3uvhAvlJIoCz/4Q32+Bl9Fmnywidh8v1GOIMmymjovfqY9ETAtysvA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.63.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.63.3.tgz", + "integrity": "sha512-WuWtSJRNo549vzcfZyEgfqb6zeSgn1F+UE5kQ+BCjzz0W4MGCjntUHkZVc1VRuAM7+ULaSyhiPxD1spyewFvkQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.63.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.63.3.tgz", + "integrity": "sha512-+lIKX7O0+IGe7WuhATaAMMeT7B76vfhXH/l9wLQL+nvyhbw2ohYCKIdWL56JfDu75CWt5oKRP4QFH/jkMtBquA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "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/node": { "version": "24.13.5", "resolved": "https://registry.npmjs.org/@types/node/-/node-24.13.5.tgz", @@ -25,6 +925,746 @@ "undici-types": "~7.18.0" } }, + "node_modules/acorn": { + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.18.0.tgz", + "integrity": "sha512-lGq+9yr1/GuAWaVYIHRjvvySG5/4VfKIvC8EWxStPdcDh/Ka7FG3twP6v4d5BkravUilhIAsG4Qj83t02LWUPQ==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/any-promise": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", + "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", + "dev": true, + "license": "MIT" + }, + "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/brace-expansion": { + "version": "5.0.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.12.tgz", + "integrity": "sha512-YovQ3rzhaLMIrDjNDMkNS01tea93qhEhG5xy8f6+R0l+dw3Ki+5sCoIoI942iuLZTHWogWktgwVDhU09iNEimQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/bundle-require": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/bundle-require/-/bundle-require-5.1.0.tgz", + "integrity": "sha512-3WrrOuZiyaaZPWiEt4G3+IffISVC9HYlWueJEBWED4ZH4aIAC2PnkdnuRrR94M+w6yGWn4AglWtJtBI8YqvgoA==", + "dev": true, + "license": "MIT", + "dependencies": { + "load-tsconfig": "^0.2.3" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "peerDependencies": { + "esbuild": ">=0.18" + } + }, + "node_modules/cac": { + "version": "6.7.14", + "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz", + "integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/chokidar": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", + "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", + "dev": true, + "license": "MIT", + "dependencies": { + "readdirp": "^4.0.1" + }, + "engines": { + "node": ">= 14.16.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/commander": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", + "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/confbox": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.1.8.tgz", + "integrity": "sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==", + "dev": true, + "license": "MIT" + }, + "node_modules/consola": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/consola/-/consola-3.4.2.tgz", + "integrity": "sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.18.0 || >=16.10.0" + } + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/esbuild": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.7.tgz", + "integrity": "sha512-IxpibTjyVnmrIQo5aqNpCgoACA/dTKLTlhMHihVHhdkxKyPO1uBBthumT0rdHmcsk9uMonIWS0m4FljWzILh3w==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.27.7", + "@esbuild/android-arm": "0.27.7", + "@esbuild/android-arm64": "0.27.7", + "@esbuild/android-x64": "0.27.7", + "@esbuild/darwin-arm64": "0.27.7", + "@esbuild/darwin-x64": "0.27.7", + "@esbuild/freebsd-arm64": "0.27.7", + "@esbuild/freebsd-x64": "0.27.7", + "@esbuild/linux-arm": "0.27.7", + "@esbuild/linux-arm64": "0.27.7", + "@esbuild/linux-ia32": "0.27.7", + "@esbuild/linux-loong64": "0.27.7", + "@esbuild/linux-mips64el": "0.27.7", + "@esbuild/linux-ppc64": "0.27.7", + "@esbuild/linux-riscv64": "0.27.7", + "@esbuild/linux-s390x": "0.27.7", + "@esbuild/linux-x64": "0.27.7", + "@esbuild/netbsd-arm64": "0.27.7", + "@esbuild/netbsd-x64": "0.27.7", + "@esbuild/openbsd-arm64": "0.27.7", + "@esbuild/openbsd-x64": "0.27.7", + "@esbuild/openharmony-arm64": "0.27.7", + "@esbuild/sunos-x64": "0.27.7", + "@esbuild/win32-arm64": "0.27.7", + "@esbuild/win32-ia32": "0.27.7", + "@esbuild/win32-x64": "0.27.7" + } + }, + "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/fix-dts-default-cjs-exports": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/fix-dts-default-cjs-exports/-/fix-dts-default-cjs-exports-1.0.1.tgz", + "integrity": "sha512-pVIECanWFC61Hzl2+oOCtoJ3F17kglZC/6N94eRWycFgBH35hHx0Li604ZIzhseh97mf2p0cv7vVrOZGoqhlEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "magic-string": "^0.30.17", + "mlly": "^1.7.4", + "rollup": "^4.34.8" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/glob": { + "version": "13.0.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-13.0.6.tgz", + "integrity": "sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "minimatch": "^10.2.2", + "minipass": "^7.1.3", + "path-scurry": "^2.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/joycon": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/joycon/-/joycon-3.1.1.tgz", + "integrity": "sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/lilconfig": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz", + "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antonk52" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true, + "license": "MIT" + }, + "node_modules/load-tsconfig": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/load-tsconfig/-/load-tsconfig-0.2.5.tgz", + "integrity": "sha512-IXO6OCs9yg8tMKzfPZ1YmheJbZCiEsnBdcB03l0OcfK9prKnJb96siuHCr5Fl37/yo9DnKU+TLpxzTUspw9shg==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + } + }, + "node_modules/lru-cache": { + "version": "11.5.2", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.5.2.tgz", + "integrity": "sha512-4pfM1Ff0x50o0tQwb5ucw/RzNyD0/YJME6IVcStalZuMWxdt3sR3huStTtxz4PUmvZfRguvDejasvQ2kifR11g==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/magic-string": { + "version": "0.30.21", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/minimatch": { + "version": "10.2.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.6.tgz", + "integrity": "sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.8" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/minipass": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz", + "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/mlly": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.8.2.tgz", + "integrity": "sha512-d+ObxMQFmbt10sretNDytwt85VrbkhhUA/JBGm1MPaWJ65Cl4wOgLaB1NYvJSZ0Ef03MMEU/0xpPMXUIQ29UfA==", + "dev": true, + "license": "MIT", + "dependencies": { + "acorn": "^8.16.0", + "pathe": "^2.0.3", + "pkg-types": "^1.3.1", + "ufo": "^1.6.3" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/mz": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", + "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "any-promise": "^1.0.0", + "object-assign": "^4.0.1", + "thenify-all": "^1.0.0" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "dev": true, + "license": "BlueOak-1.0.0" + }, + "node_modules/path-scurry": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.2.tgz", + "integrity": "sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^11.0.0", + "minipass": "^7.1.2" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/pathe": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", + "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", + "dev": true, + "license": "MIT" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.7.tgz", + "integrity": "sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pirates": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.7.tgz", + "integrity": "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/pkg-types": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-1.3.1.tgz", + "integrity": "sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "confbox": "^0.1.8", + "mlly": "^1.7.4", + "pathe": "^2.0.1" + } + }, + "node_modules/postcss-load-config": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-6.0.1.tgz", + "integrity": "sha512-oPtTM4oerL+UXmx+93ytZVN82RrlY/wPUV8IeDxFrzIjXOLF1pN+EmKPLbubvKHT2HC20xXsCAH2Z+CKV6Oz/g==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "lilconfig": "^3.1.1" + }, + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "jiti": ">=1.21.0", + "postcss": ">=8.0.9", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + }, + "postcss": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/readdirp": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", + "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14.18.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/rimraf": { + "version": "6.1.3", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-6.1.3.tgz", + "integrity": "sha512-LKg+Cr2ZF61fkcaK1UdkH2yEBBKnYjTyWzTJT6KNPcSPaiT7HSdhtMXQuN5wkTX0Xu72KQ1l8S42rlmexS2hSA==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "glob": "^13.0.3", + "package-json-from-dist": "^1.0.1" + }, + "bin": { + "rimraf": "dist/esm/bin.mjs" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rollup": { + "version": "4.63.3", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.63.3.tgz", + "integrity": "sha512-1i2XreiAoMMXuPGD6Msj2xWrMMkHojNRKivInxGQcg7/1KuPuYlfUutLyh4drnOxUTHX9cHI4wFoat8D/NKaBw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.9" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@napi-rs/lzma-linux-x64-gnu": "1.5.1", + "@rollup/rollup-android-arm-eabi": "4.63.3", + "@rollup/rollup-android-arm64": "4.63.3", + "@rollup/rollup-darwin-arm64": "4.63.3", + "@rollup/rollup-darwin-x64": "4.63.3", + "@rollup/rollup-freebsd-arm64": "4.63.3", + "@rollup/rollup-freebsd-x64": "4.63.3", + "@rollup/rollup-linux-arm-gnueabihf": "4.63.3", + "@rollup/rollup-linux-arm-musleabihf": "4.63.3", + "@rollup/rollup-linux-arm64-gnu": "4.63.3", + "@rollup/rollup-linux-arm64-musl": "4.63.3", + "@rollup/rollup-linux-loong64-gnu": "4.63.3", + "@rollup/rollup-linux-loong64-musl": "4.63.3", + "@rollup/rollup-linux-ppc64-gnu": "4.63.3", + "@rollup/rollup-linux-ppc64-musl": "4.63.3", + "@rollup/rollup-linux-riscv64-gnu": "4.63.3", + "@rollup/rollup-linux-riscv64-musl": "4.63.3", + "@rollup/rollup-linux-s390x-gnu": "4.63.3", + "@rollup/rollup-linux-x64-gnu": "4.63.3", + "@rollup/rollup-linux-x64-musl": "4.63.3", + "@rollup/rollup-openbsd-x64": "4.63.3", + "@rollup/rollup-openharmony-arm64": "4.63.3", + "@rollup/rollup-win32-arm64-msvc": "4.63.3", + "@rollup/rollup-win32-ia32-msvc": "4.63.3", + "@rollup/rollup-win32-x64-gnu": "4.63.3", + "@rollup/rollup-win32-x64-msvc": "4.63.3", + "fsevents": "~2.3.2" + } + }, + "node_modules/source-map": { + "version": "0.7.6", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.6.tgz", + "integrity": "sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">= 12" + } + }, + "node_modules/sucrase": { + "version": "3.35.1", + "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.1.tgz", + "integrity": "sha512-DhuTmvZWux4H1UOnWMB3sk0sbaCVOoQZjv8u1rDoTV0HTdGem9hkAZtl4JZy8P2z4Bg0nT+YMeOFyVr4zcG5Tw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.2", + "commander": "^4.0.0", + "lines-and-columns": "^1.1.6", + "mz": "^2.7.0", + "pirates": "^4.0.1", + "tinyglobby": "^0.2.11", + "ts-interface-checker": "^0.1.9" + }, + "bin": { + "sucrase": "bin/sucrase", + "sucrase-node": "bin/sucrase-node" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/thenify": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", + "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", + "dev": true, + "license": "MIT", + "dependencies": { + "any-promise": "^1.0.0" + } + }, + "node_modules/thenify-all": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", + "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "thenify": ">= 3.1.0 < 4" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/tinyexec": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-0.3.2.tgz", + "integrity": "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==", + "dev": true, + "license": "MIT" + }, + "node_modules/tinyglobby": { + "version": "0.2.17", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz", + "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==", + "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/tree-kill": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", + "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", + "dev": true, + "license": "MIT", + "bin": { + "tree-kill": "cli.js" + } + }, + "node_modules/ts-interface-checker": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", + "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/tsup": { + "version": "8.5.1", + "resolved": "https://registry.npmjs.org/tsup/-/tsup-8.5.1.tgz", + "integrity": "sha512-xtgkqwdhpKWr3tKPmCkvYmS9xnQK3m3XgxZHwSUjvfTjp7YfXe5tT3GgWi0F2N+ZSMsOeWeZFh7ZZFg5iPhing==", + "dev": true, + "license": "MIT", + "dependencies": { + "bundle-require": "^5.1.0", + "cac": "^6.7.14", + "chokidar": "^4.0.3", + "consola": "^3.4.0", + "debug": "^4.4.0", + "esbuild": "^0.27.0", + "fix-dts-default-cjs-exports": "^1.0.0", + "joycon": "^3.1.1", + "picocolors": "^1.1.1", + "postcss-load-config": "^6.0.1", + "resolve-from": "^5.0.0", + "rollup": "^4.34.8", + "source-map": "^0.7.6", + "sucrase": "^3.35.0", + "tinyexec": "^0.3.2", + "tinyglobby": "^0.2.11", + "tree-kill": "^1.2.2" + }, + "bin": { + "tsup": "dist/cli-default.js", + "tsup-node": "dist/cli-node.js" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@microsoft/api-extractor": "^7.36.0", + "@swc/core": "^1", + "postcss": "^8.4.12", + "typescript": ">=4.5.0" + }, + "peerDependenciesMeta": { + "@microsoft/api-extractor": { + "optional": true + }, + "@swc/core": { + "optional": true + }, + "postcss": { + "optional": true + }, + "typescript": { + "optional": true + } + } + }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/ufo": { + "version": "1.6.4", + "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.6.4.tgz", + "integrity": "sha512-JFNbkD1Svwe0KvGi8GOeLcP4kAWQ609twvCdcHxq1oSL8svv39ZuSvajcD8B+5D0eL4+s1Is2D/O6KN3qcTeRA==", + "dev": true, + "license": "MIT" + }, "node_modules/undici-types": { "version": "7.18.2", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.18.2.tgz", diff --git a/package.json b/package.json index c77f361..255610d 100644 --- a/package.json +++ b/package.json @@ -21,6 +21,9 @@ "node": ">=22" }, "devDependencies": { - "@types/node": "^24.13.5" + "@types/node": "^24.13.5", + "rimraf": "^6.1.3", + "tsup": "^8.5.1", + "typescript": "^5.9.3" } } From c4a1647522b908d0b6fecd3d04f5fed7a7dddc76 Mon Sep 17 00:00:00 2001 From: rcpthongta <220981657+rcpthongta@users.noreply.github.com> Date: Fri, 18 Sep 2026 01:02:48 +0700 Subject: [PATCH 008/121] chore(build): configure typescript and tsup build pipeline --- tsconfig.build.json | 6 ++++++ tsconfig.json | 28 +++++++++++++++++++++++++ tsconfig.types.json | 10 +++++++++ tsup.config.ts | 51 +++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 95 insertions(+) create mode 100644 tsconfig.build.json create mode 100644 tsconfig.json create mode 100644 tsconfig.types.json create mode 100644 tsup.config.ts diff --git a/tsconfig.build.json b/tsconfig.build.json new file mode 100644 index 0000000..4accab7 --- /dev/null +++ b/tsconfig.build.json @@ -0,0 +1,6 @@ +{ + "extends": "./tsconfig.json", + "include": [ + "src/**/*" + ] +} diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..53dda27 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,28 @@ +{ + "compilerOptions": { + "allowSyntheticDefaultImports": true, + "baseUrl": "./", + "isolatedModules": true, + "lib": [ + "ES2022", + "DOM", + "DOM.Iterable" + ], + "module": "esnext", + "moduleDetection": "force", + "moduleResolution": "bundler", + "noEmit": true, + "noEmitOnError": true, + "noFallthroughCasesInSwitch": true, + "noImplicitOverride": true, + "noImplicitReturns": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "resolveJsonModule": true, + "skipLibCheck": true, + "strict": true, + "target": "es2022", + "useUnknownInCatchVariables": true, + "verbatimModuleSyntax": false + } +} diff --git a/tsconfig.types.json b/tsconfig.types.json new file mode 100644 index 0000000..118810c --- /dev/null +++ b/tsconfig.types.json @@ -0,0 +1,10 @@ +{ + "extends": "./tsconfig.build.json", + "compilerOptions": { + "declaration": true, + "declarationMap": true, + "emitDeclarationOnly": true, + "noEmit": false, + "outDir": "./dist/types" + } +} diff --git a/tsup.config.ts b/tsup.config.ts new file mode 100644 index 0000000..9ee0de7 --- /dev/null +++ b/tsup.config.ts @@ -0,0 +1,51 @@ +import { defineConfig, Options } from "tsup"; + +export default defineConfig((): Options[] => { + const base: Options = { + tsconfig: "./tsconfig.build.json", + dts: false, + platform: "neutral", + splitting: false, + sourcemap: true + }; + + return [ + { + entry: ["src/index.ts"], + format: "cjs", + minify: false, + outDir: "./dist/cjs", + outExtension: () => { + return { + js: ".cjs" + }; + }, + ...base + }, + { + entry: ["src/index.ts"], + format: "esm", + minify: false, + outDir: "./dist/esm", + outExtension: () => { + return { + js: ".mjs" + }; + }, + ...base + }, + { + entry: ["src/index.ts"], + format: "iife", + minify: true, + globalName: "stringUtils", + outDir: "./dist/iife", + outExtension: () => { + return { + js: ".global.js" + }; + }, + ...base + } + ]; +}); From 2a9159082a658a013342ea19a9ff1ca168c0963a Mon Sep 17 00:00:00 2001 From: rcpthongta <220981657+rcpthongta@users.noreply.github.com> Date: Fri, 18 Sep 2026 01:03:48 +0700 Subject: [PATCH 009/121] chore(pkg): configure module exports and package distribution fields --- package.json | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/package.json b/package.json index 255610d..8113185 100644 --- a/package.json +++ b/package.json @@ -17,6 +17,23 @@ "string-library", "string-helpers" ], + "type": "module", + "sideEffects": false, + "main": "./dist/cjs/index.cjs", + "module": "./dist/esm/index.mjs", + "unpkg": "./dist/iife/index.global.js", + "jsdelivr": "./dist/iife/index.global.js", + "types": "./dist/types/index.d.ts", + "exports": { + ".": { + "types": "./dist/types/index.d.ts", + "import": "./dist/esm/index.mjs", + "require": "./dist/cjs/index.cjs" + } + }, + "files": [ + "dist" + ], "engines": { "node": ">=22" }, From f73b0048a1b9e179dfc9a0580e9a6df7a4046f7f Mon Sep 17 00:00:00 2001 From: rcpthongta <220981657+rcpthongta@users.noreply.github.com> Date: Fri, 18 Sep 2026 01:05:49 +0700 Subject: [PATCH 010/121] chore(scripts): add build scripts for js and types --- package.json | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/package.json b/package.json index 8113185..24ec512 100644 --- a/package.json +++ b/package.json @@ -37,6 +37,12 @@ "engines": { "node": ">=22" }, + "scripts": { + "build": "npm run build:clean && npm run build:js && npm run build:types", + "build:clean": "rimraf dist", + "build:js": "tsup", + "build:types": "tsc -p tsconfig.types.json" + }, "devDependencies": { "@types/node": "^24.13.5", "rimraf": "^6.1.3", From eefcaa4bceab31e91ac74df78d10af9836ce102e Mon Sep 17 00:00:00 2001 From: rcpthongta <220981657+rcpthongta@users.noreply.github.com> Date: Fri, 18 Sep 2026 01:06:44 +0700 Subject: [PATCH 011/121] feat(index): initialize source entry point --- src/index.ts | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 src/index.ts diff --git a/src/index.ts b/src/index.ts new file mode 100644 index 0000000..e69de29 From 14745129ee1340d12c76bf2c0495cf5532397411 Mon Sep 17 00:00:00 2001 From: rcpthongta <220981657+rcpthongta@users.noreply.github.com> Date: Fri, 18 Sep 2026 01:07:14 +0700 Subject: [PATCH 012/121] chore(git): ignore build output directory --- .gitignore | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.gitignore b/.gitignore index 176bd1a..99ca5ec 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,10 @@ node_modules/* +# Compiled output directory + +dist/* + # Text editor directory and files • Visual Studio Code .vscode/* From e80ed0d44ae2629218c7e1c4d875351ed4321852 Mon Sep 17 00:00:00 2001 From: rcpthongta <220981657+rcpthongta@users.noreply.github.com> Date: Fri, 18 Sep 2026 01:10:26 +0700 Subject: [PATCH 013/121] chore(editor-config): configure workspace typescript and add ts error plugin --- .vscode/extensions.json | 3 ++- .vscode/settings.json | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/.vscode/extensions.json b/.vscode/extensions.json index 4624518..c5a4a3f 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -1,6 +1,7 @@ { "recommendations": [ "editorconfig.editorconfig", - "streetsidesoftware.code-spell-checker" + "streetsidesoftware.code-spell-checker", + "yoavbls.pretty-ts-errors" ] } diff --git a/.vscode/settings.json b/.vscode/settings.json index e24ca27..1b8b76a 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -6,5 +6,6 @@ "cSpell.language": "en,en-US", "cSpell.words": [ "rcpthongta" - ] + ], + "typescript.tsdk": "node_modules\\typescript\\lib" } From 453907411c6fc30bd02978be9e6355ee451ea0eb Mon Sep 17 00:00:00 2001 From: rcpthongta <220981657+rcpthongta@users.noreply.github.com> Date: Fri, 18 Sep 2026 01:29:58 +0700 Subject: [PATCH 014/121] chore(deps-dev): add eslint, prettier, and related plugins --- package-lock.json | 3415 ++++++++++++++++++++++++++++++++++++++++++--- package.json | 16 +- 2 files changed, 3208 insertions(+), 223 deletions(-) diff --git a/package-lock.json b/package-lock.json index b3680bd..760da47 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,10 +9,24 @@ "version": "0.0.1", "license": "MIT", "devDependencies": { + "@eslint/js": "^9.39.5", + "@eslint/markdown": "^7.5.1", "@types/node": "^24.13.5", + "eslint": "^9.39.5", + "eslint-config-prettier": "^10.1.8", + "eslint-plugin-jsonc": "^2.21.1", + "eslint-plugin-prettier": "^5.5.6", + "eslint-plugin-promise": "^7.3.0", + "eslint-plugin-sonarjs": "^4.2.0", + "eslint-plugin-unused-imports": "^4.4.1", + "eslint-plugin-yml": "^1.19.1", + "globals": "^17.12.0", + "jsonc-eslint-parser": "^2.4.2", "rimraf": "^6.1.3", "tsup": "^8.5.1", - "typescript": "^5.9.3" + "typescript": "^5.9.3", + "typescript-eslint": "^8.65.0", + "yaml-eslint-parser": "^1.3.2" }, "engines": { "node": ">=22" @@ -460,6 +474,315 @@ "node": ">=18" } }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.10.1", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.10.1.tgz", + "integrity": "sha512-cuadcxVFE8sDK6iWJbs8Sn0av2Nrh2QSGQhVlBW9AaAHqHwjWsZHT8LJ4hFGPh7ASBV2deFdM7H/DPjulmh8rg==", + "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.21.2", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.2.tgz", + "integrity": "sha512-nJl2KGTlrf9GjLimgIru+V/mzgSK0ABCDQRvxw5BjURL7WfH5uoWmizbH7QB6MmnMBd8cIC9uceWnezL1VZWWw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/object-schema": "^2.1.7", + "debug": "^4.3.1", + "minimatch": "^3.1.5" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/config-array/node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@eslint/config-array/node_modules/brace-expansion": { + "version": "1.1.21", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.21.tgz", + "integrity": "sha512-9zeA+KLZNNzglF2TPKRQEDyx6Yby7daAkuy8MiPzpXPsYDWi/DRM8jmwUDxokQjYqBpv5DgPiwD4h4ZZSy1Ujw==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/@eslint/config-array/node_modules/minimatch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/@eslint/config-helpers": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.2.tgz", + "integrity": "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.17.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/core": { + "version": "0.17.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.17.0.tgz", + "integrity": "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "3.3.7", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.7.tgz", + "integrity": "sha512-F42g89Qd5oAWtp0k0nnSrjziAKza7w8SVT4mStc18LZMaRb4J1HQAHLCalEtDCxrTuksx7NU9qsmeLwpOfPqWw==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^6.14.0", + "debug": "^4.3.2", + "espree": "^10.0.1", + "globals": "^14.0.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.3.2", + "minimatch": "^3.1.5", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/eslintrc/node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@eslint/eslintrc/node_modules/brace-expansion": { + "version": "1.1.21", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.21.tgz", + "integrity": "sha512-9zeA+KLZNNzglF2TPKRQEDyx6Yby7daAkuy8MiPzpXPsYDWi/DRM8jmwUDxokQjYqBpv5DgPiwD4h4ZZSy1Ujw==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/@eslint/eslintrc/node_modules/globals": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", + "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@eslint/eslintrc/node_modules/minimatch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/@eslint/js": { + "version": "9.39.5", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.5.tgz", + "integrity": "sha512-QywQuszQh77pIXCsq998c8hbhSTI/azTty1Z6N53dmAudKHhy573j3yvRLsX2BSp8YpLtoCEG8E9DJe+8zUh4A==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + } + }, + "node_modules/@eslint/markdown": { + "version": "7.5.1", + "resolved": "https://registry.npmjs.org/@eslint/markdown/-/markdown-7.5.1.tgz", + "integrity": "sha512-R8uZemG9dKTbru/DQRPblbJyXpObwKzo8rv1KYGGuPUPtjM4LXBYM9q5CIZAComzZupws3tWbDwam5AFpPLyJQ==", + "dev": true, + "license": "MIT", + "workspaces": [ + "examples/*" + ], + "dependencies": { + "@eslint/core": "^0.17.0", + "@eslint/plugin-kit": "^0.4.1", + "github-slugger": "^2.0.0", + "mdast-util-from-markdown": "^2.0.2", + "mdast-util-frontmatter": "^2.0.1", + "mdast-util-gfm": "^3.1.0", + "micromark-extension-frontmatter": "^2.0.0", + "micromark-extension-gfm": "^3.0.0", + "micromark-util-normalize-identifier": "^2.0.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/object-schema": { + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.7.tgz", + "integrity": "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/plugin-kit": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.1.tgz", + "integrity": "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.17.0", + "levn": "^0.4.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "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/@jridgewell/gen-mapping": { "version": "0.3.13", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", @@ -519,6 +842,19 @@ "node": "^22.20 || ^24.12 || >=25" } }, + "node_modules/@pkgr/core": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.3.6.tgz", + "integrity": "sha512-SEeaJLb3qBNF/OaXnaR1NmmBbFYk1zC0ZH/52fATcRPLFg/p791YrcyFFy44Bo9sLaGuSuLp5Q6axbb/O+v/RA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/pkgr" + } + }, "node_modules/@rollup/rollup-android-arm-eabi": { "version": "4.63.3", "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.63.3.tgz", @@ -908,6 +1244,16 @@ "win32" ] }, + "node_modules/@types/debug": { + "version": "4.1.13", + "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.13.tgz", + "integrity": "sha512-KSVgmQmzMwPlmtljOomayoR89W4FynCAi3E8PPs7vmDVPe84hT+vGPKkJfThkmXs0x0jAaa9U8uW8bbfyS2fWw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/ms": "*" + } + }, "node_modules/@types/estree": { "version": "1.0.9", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz", @@ -915,6 +1261,30 @@ "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/mdast": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.4.tgz", + "integrity": "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/@types/ms": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz", + "integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/node": { "version": "24.13.5", "resolved": "https://registry.npmjs.org/@types/node/-/node-24.13.5.tgz", @@ -925,375 +1295,2591 @@ "undici-types": "~7.18.0" } }, - "node_modules/acorn": { - "version": "8.18.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.18.0.tgz", - "integrity": "sha512-lGq+9yr1/GuAWaVYIHRjvvySG5/4VfKIvC8EWxStPdcDh/Ka7FG3twP6v4d5BkravUilhIAsG4Qj83t02LWUPQ==", + "node_modules/@types/unist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", + "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "8.65.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.65.0.tgz", + "integrity": "sha512-IEgob78X12rHpUmtcwFsXhZdVGJtwTVP8FiCLZkR6GlYVrl2PcuB+KhCE5BlVC/eQpQnu8WXRtkHZuPar+gCRA==", "dev": true, "license": "MIT", - "bin": { - "acorn": "bin/acorn" + "dependencies": { + "@eslint-community/regexpp": "^4.12.2", + "@typescript-eslint/scope-manager": "8.65.0", + "@typescript-eslint/type-utils": "8.65.0", + "@typescript-eslint/utils": "8.65.0", + "@typescript-eslint/visitor-keys": "8.65.0", + "ignore": "^7.0.5", + "natural-compare": "^1.4.0", + "ts-api-utils": "^2.5.0" }, "engines": { - "node": ">=0.4.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^8.65.0", + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" } }, - "node_modules/any-promise": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", - "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", + "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { + "version": "7.0.9", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.9.tgz", + "integrity": "sha512-brTTsvFRt5C1gGHtPst/281UjPD5t9fBqbgoMPlVWy11ZLTPfu7HxK4ZYqO9H7o/yC9rSTCI85EaQ4OoY12qYw==", "dev": true, - "license": "MIT" + "license": "MIT", + "engines": { + "node": ">= 4" + } }, - "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==", + "node_modules/@typescript-eslint/parser": { + "version": "8.65.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.65.0.tgz", + "integrity": "sha512-CZ4nMxWwgu1HEEFNkeaCptra9QCtkmKdgf3sWh1rl1trIhmxLilgTV4cwcbQ4wemnT4sWQN8CaKOmdYx+g2gMA==", "dev": true, "license": "MIT", + "dependencies": { + "@typescript-eslint/scope-manager": "8.65.0", + "@typescript-eslint/types": "8.65.0", + "@typescript-eslint/typescript-estree": "8.65.0", + "@typescript-eslint/visitor-keys": "8.65.0", + "debug": "^4.4.3" + }, "engines": { - "node": "18 || 20 || >=22" + "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/brace-expansion": { - "version": "5.0.12", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.12.tgz", - "integrity": "sha512-YovQ3rzhaLMIrDjNDMkNS01tea93qhEhG5xy8f6+R0l+dw3Ki+5sCoIoI942iuLZTHWogWktgwVDhU09iNEimQ==", + "node_modules/@typescript-eslint/project-service": { + "version": "8.65.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.65.0.tgz", + "integrity": "sha512-SxnPhbTsGahizDgbu7oqFH/xVtzIqMd/s+WtnSxNxJZJpLbdT5IPdzg8EZxO3+PoKahXmwJLeNQOpKJb3/bi7Q==", "dev": true, "license": "MIT", "dependencies": { - "balanced-match": "^4.0.2" + "@typescript-eslint/tsconfig-utils": "^8.65.0", + "@typescript-eslint/types": "^8.65.0", + "debug": "^4.4.3" }, "engines": { - "node": "20 || >=22" + "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/bundle-require": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/bundle-require/-/bundle-require-5.1.0.tgz", - "integrity": "sha512-3WrrOuZiyaaZPWiEt4G3+IffISVC9HYlWueJEBWED4ZH4aIAC2PnkdnuRrR94M+w6yGWn4AglWtJtBI8YqvgoA==", + "node_modules/@typescript-eslint/scope-manager": { + "version": "8.65.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.65.0.tgz", + "integrity": "sha512-Esbl8OSYiVxBokYgWPf7VVWg/BE798wXhimnn9ML9Pt5qoDf8bfQlgjlKXR/k98+AcNzlLKYrpCcrcuZ9DZLgg==", "dev": true, "license": "MIT", "dependencies": { - "load-tsconfig": "^0.2.3" + "@typescript-eslint/types": "8.65.0", + "@typescript-eslint/visitor-keys": "8.65.0" }, "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, - "peerDependencies": { - "esbuild": ">=0.18" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/cac": { - "version": "6.7.14", - "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz", - "integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==", + "node_modules/@typescript-eslint/tsconfig-utils": { + "version": "8.65.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.65.0.tgz", + "integrity": "sha512-j6GzGqCiRdA7Qhur2VVmKZAkBLfnHFQfx4TaJGL9RMveZqCo48jSHHO0DTgizEnGhtWnqmbtCUSrqSkdiY/0Hg==", "dev": true, "license": "MIT", "engines": { - "node": ">=8" + "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/chokidar": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", - "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", + "node_modules/@typescript-eslint/type-utils": { + "version": "8.65.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.65.0.tgz", + "integrity": "sha512-YjaZ7PRI5qY7ax2L3PbvX0rRyGtipAReCWs0mhhDBHjH/vl0g0BonaGXrKdKpMbIIsMIwDgbk/xzkBTyAltS5g==", "dev": true, "license": "MIT", "dependencies": { - "readdirp": "^4.0.1" + "@typescript-eslint/types": "8.65.0", + "@typescript-eslint/typescript-estree": "8.65.0", + "@typescript-eslint/utils": "8.65.0", + "debug": "^4.4.3", + "ts-api-utils": "^2.5.0" }, "engines": { - "node": ">= 14.16.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { - "url": "https://paulmillr.com/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/commander": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", - "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", + "node_modules/@typescript-eslint/types": { + "version": "8.65.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.65.0.tgz", + "integrity": "sha512-JSSwWNy+H0E/01jJEM+hrX6N0OFDzFzeIhHFSAS01tlVaevpG8cFyYRPhS5yjGOvBUx3sqQHVMjCL1CAZZMxBg==", "dev": true, "license": "MIT", "engines": { - "node": ">= 6" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/confbox": { - "version": "0.1.8", - "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.1.8.tgz", - "integrity": "sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==", + "node_modules/@typescript-eslint/typescript-estree": { + "version": "8.65.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.65.0.tgz", + "integrity": "sha512-JboAE2swaYt4tb1fHhHTABE2K+OLy09XfcTbhnk4Pw96f9dd2e9iYsJ28gBggHlo5z5x1rkyWvcPoTuNTd4oGg==", "dev": true, - "license": "MIT" + "license": "MIT", + "dependencies": { + "@typescript-eslint/project-service": "8.65.0", + "@typescript-eslint/tsconfig-utils": "8.65.0", + "@typescript-eslint/types": "8.65.0", + "@typescript-eslint/visitor-keys": "8.65.0", + "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/consola": { - "version": "3.4.2", - "resolved": "https://registry.npmjs.org/consola/-/consola-3.4.2.tgz", - "integrity": "sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==", + "node_modules/@typescript-eslint/utils": { + "version": "8.65.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.65.0.tgz", + "integrity": "sha512-gXiwIHsYreboxeJucHKPvgwl7dXt50mF8s1/c00cP/WoVTyWKFdtfhRWwZiXYFU5H2O8vVoSLNrexFZjYS/SGA==", "dev": true, "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.9.1", + "@typescript-eslint/scope-manager": "8.65.0", + "@typescript-eslint/types": "8.65.0", + "@typescript-eslint/typescript-estree": "8.65.0" + }, "engines": { - "node": "^14.18.0 || >=16.10.0" + "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/debug": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", - "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "node_modules/@typescript-eslint/visitor-keys": { + "version": "8.65.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.65.0.tgz", + "integrity": "sha512-8C71BQkGjiMmXtop7pHVJu1l2NNShFdkCyD6a2ezzs5vU/L3LRtb69EtcteFwz0mYMPzIgOw0n6OV4VBUWZd7A==", "dev": true, "license": "MIT", "dependencies": { - "ms": "^2.1.3" + "@typescript-eslint/types": "8.65.0", + "eslint-visitor-keys": "^5.0.0" }, "engines": { - "node": ">=6.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/esbuild": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.7.tgz", - "integrity": "sha512-IxpibTjyVnmrIQo5aqNpCgoACA/dTKLTlhMHihVHhdkxKyPO1uBBthumT0rdHmcsk9uMonIWS0m4FljWzILh3w==", + "node_modules/@typescript-eslint/visitor-keys/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/acorn": { + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.18.0.tgz", + "integrity": "sha512-lGq+9yr1/GuAWaVYIHRjvvySG5/4VfKIvC8EWxStPdcDh/Ka7FG3twP6v4d5BkravUilhIAsG4Qj83t02LWUPQ==", "dev": true, - "hasInstallScript": true, "license": "MIT", "bin": { - "esbuild": "bin/esbuild" + "acorn": "bin/acorn" }, "engines": { - "node": ">=18" - }, - "optionalDependencies": { - "@esbuild/aix-ppc64": "0.27.7", - "@esbuild/android-arm": "0.27.7", - "@esbuild/android-arm64": "0.27.7", - "@esbuild/android-x64": "0.27.7", - "@esbuild/darwin-arm64": "0.27.7", - "@esbuild/darwin-x64": "0.27.7", - "@esbuild/freebsd-arm64": "0.27.7", - "@esbuild/freebsd-x64": "0.27.7", - "@esbuild/linux-arm": "0.27.7", - "@esbuild/linux-arm64": "0.27.7", - "@esbuild/linux-ia32": "0.27.7", - "@esbuild/linux-loong64": "0.27.7", - "@esbuild/linux-mips64el": "0.27.7", - "@esbuild/linux-ppc64": "0.27.7", - "@esbuild/linux-riscv64": "0.27.7", - "@esbuild/linux-s390x": "0.27.7", - "@esbuild/linux-x64": "0.27.7", - "@esbuild/netbsd-arm64": "0.27.7", - "@esbuild/netbsd-x64": "0.27.7", - "@esbuild/openbsd-arm64": "0.27.7", - "@esbuild/openbsd-x64": "0.27.7", - "@esbuild/openharmony-arm64": "0.27.7", - "@esbuild/sunos-x64": "0.27.7", - "@esbuild/win32-arm64": "0.27.7", - "@esbuild/win32-ia32": "0.27.7", - "@esbuild/win32-x64": "0.27.7" + "node": ">=0.4.0" } }, - "node_modules/fdir": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", - "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "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", - "engines": { - "node": ">=12.0.0" - }, "peerDependencies": { - "picomatch": "^3 || ^4" + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "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" }, - "peerDependenciesMeta": { - "picomatch": { - "optional": true - } + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" } }, - "node_modules/fix-dts-default-cjs-exports": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/fix-dts-default-cjs-exports/-/fix-dts-default-cjs-exports-1.0.1.tgz", - "integrity": "sha512-pVIECanWFC61Hzl2+oOCtoJ3F17kglZC/6N94eRWycFgBH35hHx0Li604ZIzhseh97mf2p0cv7vVrOZGoqhlEg==", + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, "license": "MIT", "dependencies": { - "magic-string": "^0.30.17", - "mlly": "^1.7.4", - "rollup": "^4.34.8" + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "node_modules/any-promise": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", + "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", + "dev": true, + "license": "MIT" + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "license": "Python-2.0" + }, + "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, - "hasInstallScript": true, "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + "node": "18 || 20 || >=22" } }, - "node_modules/glob": { - "version": "13.0.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-13.0.6.tgz", - "integrity": "sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw==", + "node_modules/brace-expansion": { + "version": "5.0.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.12.tgz", + "integrity": "sha512-YovQ3rzhaLMIrDjNDMkNS01tea93qhEhG5xy8f6+R0l+dw3Ki+5sCoIoI942iuLZTHWogWktgwVDhU09iNEimQ==", "dev": true, - "license": "BlueOak-1.0.0", + "license": "MIT", "dependencies": { - "minimatch": "^10.2.2", - "minipass": "^7.1.3", - "path-scurry": "^2.0.2" + "balanced-match": "^4.0.2" }, "engines": { - "node": "18 || 20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "node": "20 || >=22" } }, - "node_modules/joycon": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/joycon/-/joycon-3.1.1.tgz", - "integrity": "sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==", + "node_modules/builtin-modules": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.3.0.tgz", + "integrity": "sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==", "dev": true, "license": "MIT", "engines": { - "node": ">=10" + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/lilconfig": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz", - "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==", + "node_modules/bundle-require": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/bundle-require/-/bundle-require-5.1.0.tgz", + "integrity": "sha512-3WrrOuZiyaaZPWiEt4G3+IffISVC9HYlWueJEBWED4ZH4aIAC2PnkdnuRrR94M+w6yGWn4AglWtJtBI8YqvgoA==", "dev": true, "license": "MIT", + "dependencies": { + "load-tsconfig": "^0.2.3" + }, "engines": { - "node": ">=14" + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" }, - "funding": { - "url": "https://github.com/sponsors/antonk52" + "peerDependencies": { + "esbuild": ">=0.18" } }, - "node_modules/lines-and-columns": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", - "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", "dev": true, - "license": "MIT" + "license": "MIT", + "engines": { + "node": ">= 0.8" + } }, - "node_modules/load-tsconfig": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/load-tsconfig/-/load-tsconfig-0.2.5.tgz", - "integrity": "sha512-IXO6OCs9yg8tMKzfPZ1YmheJbZCiEsnBdcB03l0OcfK9prKnJb96siuHCr5Fl37/yo9DnKU+TLpxzTUspw9shg==", + "node_modules/cac": { + "version": "6.7.14", + "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz", + "integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==", "dev": true, "license": "MIT", "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + "node": ">=8" } }, - "node_modules/lru-cache": { - "version": "11.5.2", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.5.2.tgz", - "integrity": "sha512-4pfM1Ff0x50o0tQwb5ucw/RzNyD0/YJME6IVcStalZuMWxdt3sR3huStTtxz4PUmvZfRguvDejasvQ2kifR11g==", + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", "dev": true, - "license": "BlueOak-1.0.0", + "license": "MIT", "engines": { - "node": "20 || >=22" + "node": ">=6" } }, - "node_modules/magic-string": { - "version": "0.30.21", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", - "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "node_modules/ccount": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/ccount/-/ccount-2.0.1.tgz", + "integrity": "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, "license": "MIT", "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.5" + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/minimatch": { - "version": "10.2.6", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.6.tgz", - "integrity": "sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A==", + "node_modules/character-entities": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-2.0.2.tgz", + "integrity": "sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==", "dev": true, - "license": "BlueOak-1.0.0", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/chokidar": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", + "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", + "dev": true, + "license": "MIT", "dependencies": { - "brace-expansion": "^5.0.8" + "readdirp": "^4.0.1" }, "engines": { - "node": "18 || 20 || >=22" + "node": ">= 14.16.0" }, "funding": { - "url": "https://github.com/sponsors/isaacs" + "url": "https://paulmillr.com/funding/" } }, - "node_modules/minipass": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz", - "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==", + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, - "license": "BlueOak-1.0.0", + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, "engines": { - "node": ">=16 || 14 >=14.17" + "node": ">=7.0.0" } }, - "node_modules/mlly": { - "version": "1.8.2", - "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.8.2.tgz", - "integrity": "sha512-d+ObxMQFmbt10sretNDytwt85VrbkhhUA/JBGm1MPaWJ65Cl4wOgLaB1NYvJSZ0Ef03MMEU/0xpPMXUIQ29UfA==", + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/commander": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", + "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true, + "license": "MIT" + }, + "node_modules/confbox": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.1.8.tgz", + "integrity": "sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==", + "dev": true, + "license": "MIT" + }, + "node_modules/consola": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/consola/-/consola-3.4.2.tgz", + "integrity": "sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.18.0 || >=16.10.0" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decode-named-character-reference": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/decode-named-character-reference/-/decode-named-character-reference-1.3.0.tgz", + "integrity": "sha512-GtpQYB283KrPp6nRw50q3U9/VfOutZOe103qlN7BPP6Ad27xYnOIWv4lPzo8HCAL+mMZofJ9KEy30fq6MfaK6Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "character-entities": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "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/dequal": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", + "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/devlop": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/devlop/-/devlop-1.1.0.tgz", + "integrity": "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==", + "dev": true, + "license": "MIT", + "dependencies": { + "dequal": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/diff-sequences": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-27.5.1.tgz", + "integrity": "sha512-k1gCAXAsNgLwEL+Y8Wvl+M6oEFj5bgazfZULpS5CneoPPXRaCCW7dm+q21Ky2VEE5X+VeRDBVg1Pcvvsr4TtNQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/esbuild": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.7.tgz", + "integrity": "sha512-IxpibTjyVnmrIQo5aqNpCgoACA/dTKLTlhMHihVHhdkxKyPO1uBBthumT0rdHmcsk9uMonIWS0m4FljWzILh3w==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.27.7", + "@esbuild/android-arm": "0.27.7", + "@esbuild/android-arm64": "0.27.7", + "@esbuild/android-x64": "0.27.7", + "@esbuild/darwin-arm64": "0.27.7", + "@esbuild/darwin-x64": "0.27.7", + "@esbuild/freebsd-arm64": "0.27.7", + "@esbuild/freebsd-x64": "0.27.7", + "@esbuild/linux-arm": "0.27.7", + "@esbuild/linux-arm64": "0.27.7", + "@esbuild/linux-ia32": "0.27.7", + "@esbuild/linux-loong64": "0.27.7", + "@esbuild/linux-mips64el": "0.27.7", + "@esbuild/linux-ppc64": "0.27.7", + "@esbuild/linux-riscv64": "0.27.7", + "@esbuild/linux-s390x": "0.27.7", + "@esbuild/linux-x64": "0.27.7", + "@esbuild/netbsd-arm64": "0.27.7", + "@esbuild/netbsd-x64": "0.27.7", + "@esbuild/openbsd-arm64": "0.27.7", + "@esbuild/openbsd-x64": "0.27.7", + "@esbuild/openharmony-arm64": "0.27.7", + "@esbuild/sunos-x64": "0.27.7", + "@esbuild/win32-arm64": "0.27.7", + "@esbuild/win32-ia32": "0.27.7", + "@esbuild/win32-x64": "0.27.7" + } + }, + "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", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "9.39.5", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.5.tgz", + "integrity": "sha512-DgZS62aPLXKlnxILS/AYCoRvHaZeXceIzlXPkkGGzJWSow1aEk0lbTlxUSlyjC8jcaKxAdOnTDz+o1JFSBsyjw==", + "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.8.0", + "@eslint-community/regexpp": "^4.12.1", + "@eslint/config-array": "^0.21.2", + "@eslint/config-helpers": "^0.4.2", + "@eslint/core": "^0.17.0", + "@eslint/eslintrc": "^3.3.6", + "@eslint/js": "9.39.5", + "@eslint/plugin-kit": "^0.4.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", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.6", + "debug": "^4.3.2", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^8.4.0", + "eslint-visitor-keys": "^4.2.1", + "espree": "^10.4.0", + "esquery": "^1.5.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", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.5", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } + } + }, + "node_modules/eslint-compat-utils": { + "version": "0.6.5", + "resolved": "https://registry.npmjs.org/eslint-compat-utils/-/eslint-compat-utils-0.6.5.tgz", + "integrity": "sha512-vAUHYzue4YAa2hNACjB8HvUQj5yehAZgiClyFVVom9cP8z5NSFq3PwB/TtJslN2zAMgRX6FCFCjYBbQh71g5RQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^7.5.4" + }, + "engines": { + "node": ">=12" + }, + "peerDependencies": { + "eslint": ">=6.0.0" + } + }, + "node_modules/eslint-config-prettier": { + "version": "10.1.8", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-10.1.8.tgz", + "integrity": "sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w==", + "dev": true, + "license": "MIT", + "bin": { + "eslint-config-prettier": "bin/cli.js" + }, + "funding": { + "url": "https://opencollective.com/eslint-config-prettier" + }, + "peerDependencies": { + "eslint": ">=7.0.0" + } + }, + "node_modules/eslint-json-compat-utils": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/eslint-json-compat-utils/-/eslint-json-compat-utils-0.2.3.tgz", + "integrity": "sha512-RbBmDFyu7FqnjE8F0ZxPNzx5UaptdeS9Uu50r7A+D7s/+FCX+ybiyViYEgFUaFIFqSWJgZRTpL5d8Kanxxl2lQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "esquery": "^1.6.0" + }, + "engines": { + "node": ">=12" + }, + "peerDependencies": { + "eslint": "*", + "jsonc-eslint-parser": "^2.4.0 || ^3.0.0" + }, + "peerDependenciesMeta": { + "@eslint/json": { + "optional": true + } + } + }, + "node_modules/eslint-plugin-jsonc": { + "version": "2.21.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-jsonc/-/eslint-plugin-jsonc-2.21.1.tgz", + "integrity": "sha512-dbNR5iEnQeORwsK2WZzr3QaMtFCY3kKJVMRHPzUpKzMhmVy2zIpVgFDpX8MNoIdoqz6KCpCfOJavhfiSbZbN+w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.5.1", + "diff-sequences": "^27.5.1", + "eslint-compat-utils": "^0.6.4", + "eslint-json-compat-utils": "^0.2.1", + "espree": "^9.6.1 || ^10.3.0", + "graphemer": "^1.4.0", + "jsonc-eslint-parser": "^2.4.0", + "natural-compare": "^1.4.0", + "synckit": "^0.6.2 || ^0.7.3 || ^0.11.5" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ota-meshi" + }, + "peerDependencies": { + "eslint": ">=6.0.0" + } + }, + "node_modules/eslint-plugin-prettier": { + "version": "5.5.6", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.5.6.tgz", + "integrity": "sha512-ifetmTcxWfz+4qRW3pH/ujdTq2jQIj59AxJMIN26K5avYgU8dxycUETQonWiW+wPrYXA0j3Try0l1CnwVQtDqQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "prettier-linter-helpers": "^1.0.1", + "synckit": "^0.11.13" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint-plugin-prettier" + }, + "peerDependencies": { + "@types/eslint": ">=8.0.0", + "eslint": ">=8.0.0", + "eslint-config-prettier": ">= 7.0.0 <10.0.0 || >=10.1.0", + "prettier": ">=3.0.0" + }, + "peerDependenciesMeta": { + "@types/eslint": { + "optional": true + }, + "eslint-config-prettier": { + "optional": true + } + } + }, + "node_modules/eslint-plugin-promise": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-promise/-/eslint-plugin-promise-7.3.0.tgz", + "integrity": "sha512-6uGiOR0INuujr6PEQmeSSP7GbIMJ/ebEXXiEzb/nOj68LknH5Pxzb/AbZivmr6VE6TkTE8rTjRK9zhKpK6HsRA==", + "dev": true, + "license": "ISC", + "dependencies": { + "@eslint-community/eslint-utils": "^4.4.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0 || ^9.0.0 || ^10.0.0" + } + }, + "node_modules/eslint-plugin-sonarjs": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-sonarjs/-/eslint-plugin-sonarjs-4.2.0.tgz", + "integrity": "sha512-bqADfuNtTL7VK6RU29eoiFTtaaBKIpVPuX3bOl+rBpWSBa0zIBVZlqZNZQjfP6s4iXkAJokv5IsD8OsACkwApg==", + "dev": true, + "license": "LGPL-3.0-only", + "dependencies": { + "@eslint-community/regexpp": "^4.12.2", + "builtin-modules": "^3.3.0", + "bytes": "^3.1.2", + "functional-red-black-tree": "^1.0.1", + "globals": "^17.7.0", + "jsx-ast-utils-x": "^0.1.0", + "lodash.merge": "^4.6.2", + "minimatch": "^10.2.5", + "scslre": "^0.3.0", + "semver": "^7.8.5", + "ts-api-utils": "^2.5.0", + "typescript": ">=5 <6.1.0", + "yaml": "^2.9.0" + }, + "peerDependencies": { + "eslint": "^8.0.0 || ^9.0.0 || ^10.0.0" + } + }, + "node_modules/eslint-plugin-unused-imports": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-unused-imports/-/eslint-plugin-unused-imports-4.4.1.tgz", + "integrity": "sha512-oZGYUz1X3sRMGUB+0cZyK2VcvRX5lm/vB56PgNNcU+7ficUCKm66oZWKUubXWnOuPjQ8PvmXtCViXBMONPe7tQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@typescript-eslint/eslint-plugin": "^8.0.0-0 || ^7.0.0 || ^6.0.0 || ^5.0.0", + "eslint": "^10.0.0 || ^9.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "@typescript-eslint/eslint-plugin": { + "optional": true + } + } + }, + "node_modules/eslint-plugin-yml": { + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-yml/-/eslint-plugin-yml-1.19.1.tgz", + "integrity": "sha512-bYkOxyEiXh9WxUhVYPELdSHxGG5pOjCSeJOVkfdIyj6tuiHDxrES2WAW1dBxn3iaZQey57XflwLtCYRcNPOiOg==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.3.2", + "diff-sequences": "^27.5.1", + "escape-string-regexp": "4.0.0", + "eslint-compat-utils": "^0.6.0", + "natural-compare": "^1.4.0", + "yaml-eslint-parser": "^1.2.1" + }, + "engines": { + "node": "^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ota-meshi" + }, + "peerDependencies": { + "eslint": ">=6.0.0" + } + }, + "node_modules/eslint-scope": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz", + "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/eslint/node_modules/brace-expansion": { + "version": "1.1.21", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.21.tgz", + "integrity": "sha512-9zeA+KLZNNzglF2TPKRQEDyx6Yby7daAkuy8MiPzpXPsYDWi/DRM8jmwUDxokQjYqBpv5DgPiwD4h4ZZSy1Ujw==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/eslint/node_modules/minimatch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/espree": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", + "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.15.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^4.2.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "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/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-diff": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz", + "integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==", + "dev": true, + "license": "Apache-2.0" + }, + "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-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/fault": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/fault/-/fault-2.0.1.tgz", + "integrity": "sha512-WtySTkS4OKev5JtpHXnib4Gxiurzh5NCGvWrFaZ34m6JehfTUhKZvn9njTfw48t6JumVQOmrKqpmGcdwxnhqBQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "format": "^0.2.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "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-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/fix-dts-default-cjs-exports": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/fix-dts-default-cjs-exports/-/fix-dts-default-cjs-exports-1.0.1.tgz", + "integrity": "sha512-pVIECanWFC61Hzl2+oOCtoJ3F17kglZC/6N94eRWycFgBH35hHx0Li604ZIzhseh97mf2p0cv7vVrOZGoqhlEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "magic-string": "^0.30.17", + "mlly": "^1.7.4", + "rollup": "^4.34.8" + } + }, + "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.4", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.4.tgz", + "integrity": "sha512-5+ybhBZANEJxaH3X5evAFatUxLfEHSr7n6kYJ+1Qd0mUqr4eu9gIf6GDbWHf8RJijHrjjO8G+la14SlL2SeS1Q==", + "dev": true, + "license": "ISC" + }, + "node_modules/format": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/format/-/format-0.2.2.tgz", + "integrity": "sha512-wzsgA6WOq+09wrU1tsJ09udeR/YZRaeArL9e1wPbFg3GG2yDnC2ldKpxs4xunpFF9DgqCqOIra3bc1HWrJ37Ww==", + "dev": true, + "engines": { + "node": ">=0.4.x" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/functional-red-black-tree": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", + "integrity": "sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g==", + "dev": true, + "license": "MIT" + }, + "node_modules/github-slugger": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/github-slugger/-/github-slugger-2.0.0.tgz", + "integrity": "sha512-IaOQ9puYtjrkq7Y0Ygl9KDZnrf/aiUJYUpVf89y8kyaxbRG7Y1SrX/jaumrv81vc61+kiMempujsM3Yw7w5qcw==", + "dev": true, + "license": "ISC" + }, + "node_modules/glob": { + "version": "13.0.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-13.0.6.tgz", + "integrity": "sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "minimatch": "^10.2.2", + "minipass": "^7.1.3", + "path-scurry": "^2.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "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/globals": { + "version": "17.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-17.12.0.tgz", + "integrity": "sha512-cezEd/DTyyht9cvSSURyygXPfy04GtWO/5e6ZPvH7fCtjKz9PYOmuawphw1Ctd1f6C+5JypXfGD7ahNMXvevBA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true, + "license": "MIT" + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "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/import-fresh": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/import-fresh/node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "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/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-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/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, + "node_modules/joycon": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/joycon/-/joycon-3.1.1.tgz", + "integrity": "sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/js-yaml": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.2.tgz", + "integrity": "sha512-SFNOvSJ+Dgf/9An904Yx+CgSlIPCkIpao4qo51lpee25TIRejdH3rhR4EZMGoNx3/TP3O+wzWuiTFl4sqbltzA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/puzrin" + }, + { + "type": "github", + "url": "https://github.com/sponsors/nodeca" + } + ], + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "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-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/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/jsonc-eslint-parser": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/jsonc-eslint-parser/-/jsonc-eslint-parser-2.4.2.tgz", + "integrity": "sha512-1e4qoRgnn448pRuMvKGsFFymUCquZV0mpGgOyIKNgD3JVDTsVJyRBGH/Fm0tBb8WsWGgmB1mDe6/yJMQM37DUA==", + "dev": true, + "license": "MIT", + "dependencies": { + "acorn": "^8.5.0", + "eslint-visitor-keys": "^3.0.0", + "espree": "^9.0.0", + "semver": "^7.3.5" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ota-meshi" + } + }, + "node_modules/jsonc-eslint-parser/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/jsonc-eslint-parser/node_modules/espree": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.9.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/jsx-ast-utils-x": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/jsx-ast-utils-x/-/jsx-ast-utils-x-0.1.0.tgz", + "integrity": "sha512-eQQBjBnsVtGacsG9uJNB8qOr3yA8rga4wAaGG1qRcBzSIvfhERLrWxMAM1hp5fcS6Abo8M4+bUBTekYR0qTPQw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "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/lilconfig": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz", + "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antonk52" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true, + "license": "MIT" + }, + "node_modules/load-tsconfig": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/load-tsconfig/-/load-tsconfig-0.2.5.tgz", + "integrity": "sha512-IXO6OCs9yg8tMKzfPZ1YmheJbZCiEsnBdcB03l0OcfK9prKnJb96siuHCr5Fl37/yo9DnKU+TLpxzTUspw9shg==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + } + }, + "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.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/longest-streak": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/longest-streak/-/longest-streak-3.1.0.tgz", + "integrity": "sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/lru-cache": { + "version": "11.5.2", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.5.2.tgz", + "integrity": "sha512-4pfM1Ff0x50o0tQwb5ucw/RzNyD0/YJME6IVcStalZuMWxdt3sR3huStTtxz4PUmvZfRguvDejasvQ2kifR11g==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/magic-string": { + "version": "0.30.21", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/markdown-table": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/markdown-table/-/markdown-table-3.0.4.tgz", + "integrity": "sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/mdast-util-find-and-replace": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/mdast-util-find-and-replace/-/mdast-util-find-and-replace-3.0.2.tgz", + "integrity": "sha512-Tmd1Vg/m3Xz43afeNxDIhWRtFZgM2VLyaf4vSTYwudTyeuTneoL3qtWMA5jeLyz/O1vDJmmV4QuScFCA2tBPwg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "escape-string-regexp": "^5.0.0", + "unist-util-is": "^6.0.0", + "unist-util-visit-parents": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-find-and-replace/node_modules/escape-string-regexp": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz", + "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mdast-util-from-markdown": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-2.0.3.tgz", + "integrity": "sha512-W4mAWTvSlKvf8L6J+VN9yLSqQ9AOAAvHuoDAmPkz4dHf553m5gVj2ejadHJhoJmcmxEnOv6Pa8XJhpxE93kb8Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "@types/unist": "^3.0.0", + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "mdast-util-to-string": "^4.0.0", + "micromark": "^4.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-decode-string": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0", + "unist-util-stringify-position": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-frontmatter": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mdast-util-frontmatter/-/mdast-util-frontmatter-2.0.1.tgz", + "integrity": "sha512-LRqI9+wdgC25P0URIJY9vwocIzCcksduHQ9OF2joxQoyTNVduwLAFUzjoopuRJbJAReaKrNQKAZKL3uCMugWJA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "escape-string-regexp": "^5.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0", + "micromark-extension-frontmatter": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-frontmatter/node_modules/escape-string-regexp": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz", + "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mdast-util-gfm": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm/-/mdast-util-gfm-3.1.0.tgz", + "integrity": "sha512-0ulfdQOM3ysHhCJ1p06l0b0VKlhU0wuQs3thxZQagjcjPrlFRqY215uZGHHJan9GEAXd9MbfPjFJz+qMkVR6zQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-gfm-autolink-literal": "^2.0.0", + "mdast-util-gfm-footnote": "^2.0.0", + "mdast-util-gfm-strikethrough": "^2.0.0", + "mdast-util-gfm-table": "^2.0.0", + "mdast-util-gfm-task-list-item": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-autolink-literal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-autolink-literal/-/mdast-util-gfm-autolink-literal-2.0.1.tgz", + "integrity": "sha512-5HVP2MKaP6L+G6YaxPNjuL0BPrq9orG3TsrZ9YXbA3vDw/ACI4MEsnoDpn6ZNm7GnZgtAcONJyPhOP8tNJQavQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "ccount": "^2.0.0", + "devlop": "^1.0.0", + "mdast-util-find-and-replace": "^3.0.0", + "micromark-util-character": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-footnote": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-footnote/-/mdast-util-gfm-footnote-2.1.0.tgz", + "integrity": "sha512-sqpDWlsHn7Ac9GNZQMeUzPQSMzR6Wv0WKRNvQRg0KqHh02fpTz69Qc1QSseNX29bhz1ROIyNyxExfawVKTm1GQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "devlop": "^1.1.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-strikethrough": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-strikethrough/-/mdast-util-gfm-strikethrough-2.0.0.tgz", + "integrity": "sha512-mKKb915TF+OC5ptj5bJ7WFRPdYtuHv0yTRxK2tJvi+BDqbkiG7h7u/9SI89nRAYcmap2xHQL9D+QG/6wSrTtXg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-table": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-table/-/mdast-util-gfm-table-2.0.0.tgz", + "integrity": "sha512-78UEvebzz/rJIxLvE7ZtDd/vIQ0RHv+3Mh5DR96p7cS7HsBhYIICDBCu8csTNWNO6tBWfqXPWekRuj2FNOGOZg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "markdown-table": "^3.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-task-list-item": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-task-list-item/-/mdast-util-gfm-task-list-item-2.0.0.tgz", + "integrity": "sha512-IrtvNvjxC1o06taBAVJznEnkiHxLFTzgonUdy8hzFVeDun0uTjxxrRGVaNFqkU1wJR3RBPEfsxmU6jDWPofrTQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-phrasing": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/mdast-util-phrasing/-/mdast-util-phrasing-4.1.0.tgz", + "integrity": "sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "unist-util-is": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-markdown": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/mdast-util-to-markdown/-/mdast-util-to-markdown-2.1.2.tgz", + "integrity": "sha512-xj68wMTvGXVOKonmog6LwyJKrYXZPvlwabaryTjLh9LuvovB/KAH+kvi8Gjj+7rJjsFi23nkUxRQv1KqSroMqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "@types/unist": "^3.0.0", + "longest-streak": "^3.0.0", + "mdast-util-phrasing": "^4.0.0", + "mdast-util-to-string": "^4.0.0", + "micromark-util-classify-character": "^2.0.0", + "micromark-util-decode-string": "^2.0.0", + "unist-util-visit": "^5.0.0", + "zwitch": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-4.0.0.tgz", + "integrity": "sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/micromark/-/micromark-4.0.2.tgz", + "integrity": "sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "@types/debug": "^4.0.0", + "debug": "^4.0.0", + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "micromark-core-commonmark": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-combine-extensions": "^2.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-encode": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "micromark-util-subtokenize": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-core-commonmark": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/micromark-core-commonmark/-/micromark-core-commonmark-2.0.3.tgz", + "integrity": "sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "micromark-factory-destination": "^2.0.0", + "micromark-factory-label": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-factory-title": "^2.0.0", + "micromark-factory-whitespace": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-classify-character": "^2.0.0", + "micromark-util-html-tag-name": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-subtokenize": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-extension-frontmatter": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-extension-frontmatter/-/micromark-extension-frontmatter-2.0.0.tgz", + "integrity": "sha512-C4AkuM3dA58cgZha7zVnuVxBhDsbttIMiytjgsM2XbHAB2faRVaHRle40558FBN+DJcrLNCoqG5mlrpdU4cRtg==", + "dev": true, + "license": "MIT", + "dependencies": { + "fault": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm/-/micromark-extension-gfm-3.0.0.tgz", + "integrity": "sha512-vsKArQsicm7t0z2GugkCKtZehqUm31oeGBV/KVSorWSy8ZlNAv7ytjFhvaryUiCUJYqs+NoE6AFhpQvBTM6Q4w==", + "dev": true, + "license": "MIT", + "dependencies": { + "micromark-extension-gfm-autolink-literal": "^2.0.0", + "micromark-extension-gfm-footnote": "^2.0.0", + "micromark-extension-gfm-strikethrough": "^2.0.0", + "micromark-extension-gfm-table": "^2.0.0", + "micromark-extension-gfm-tagfilter": "^2.0.0", + "micromark-extension-gfm-task-list-item": "^2.0.0", + "micromark-util-combine-extensions": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-autolink-literal": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-autolink-literal/-/micromark-extension-gfm-autolink-literal-2.1.0.tgz", + "integrity": "sha512-oOg7knzhicgQ3t4QCjCWgTmfNhvQbDDnJeVu9v81r7NltNCVmhPy1fJRX27pISafdjL+SVc4d3l48Gb6pbRypw==", + "dev": true, + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-footnote": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-footnote/-/micromark-extension-gfm-footnote-2.1.0.tgz", + "integrity": "sha512-/yPhxI1ntnDNsiHtzLKYnE3vf9JZ6cAisqVDauhp4CEHxlb4uoOTxOCJ+9s51bIB8U1N1FJ1RXOKTIlD5B/gqw==", + "dev": true, + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-core-commonmark": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-strikethrough": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-strikethrough/-/micromark-extension-gfm-strikethrough-2.1.0.tgz", + "integrity": "sha512-ADVjpOOkjz1hhkZLlBiYA9cR2Anf8F4HqZUO6e5eDcPQd0Txw5fxLzzxnEkSkfnD0wziSGiv7sYhk/ktvbf1uw==", + "dev": true, + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-classify-character": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-table": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-table/-/micromark-extension-gfm-table-2.1.2.tgz", + "integrity": "sha512-pRzm4kDTu0MjlmBkxmS9yYhw60nncfcEwu9NNdPFSQEFXS95ZKyIIyTSHu/o3ReBUrLKYEq+7YaXCRn/bPB4MA==", + "dev": true, + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-tagfilter": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-tagfilter/-/micromark-extension-gfm-tagfilter-2.0.0.tgz", + "integrity": "sha512-xHlTOmuCSotIA8TW1mDIM6X2O1SiX5P9IuDtqGonFhEK0qgRI4yeC6vMxEV2dgyr2TiD+2PQ10o+cOhdVAcwfg==", + "dev": true, + "license": "MIT", + "dependencies": { + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-task-list-item": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-task-list-item/-/micromark-extension-gfm-task-list-item-2.1.0.tgz", + "integrity": "sha512-qIBZhqxqI6fjLDYFTBIa4eivDMnP+OZqsNwmQ3xNLE4Cxwc+zfQEfbs6tzAo2Hjq+bh6q5F+Z8/cksrLFYWQQw==", + "dev": true, + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-factory-destination": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-destination/-/micromark-factory-destination-2.0.1.tgz", + "integrity": "sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-label": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-label/-/micromark-factory-label-2.0.1.tgz", + "integrity": "sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-space": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.1.tgz", + "integrity": "sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-title": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-title/-/micromark-factory-title-2.0.1.tgz", + "integrity": "sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-whitespace": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-whitespace/-/micromark-factory-whitespace-2.0.1.tgz", + "integrity": "sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-character": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz", + "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-chunked": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-chunked/-/micromark-util-chunked-2.0.1.tgz", + "integrity": "sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-classify-character": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-classify-character/-/micromark-util-classify-character-2.0.1.tgz", + "integrity": "sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-combine-extensions": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-combine-extensions/-/micromark-util-combine-extensions-2.0.1.tgz", + "integrity": "sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-chunked": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-decode-numeric-character-reference": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-2.0.2.tgz", + "integrity": "sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-decode-string": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-decode-string/-/micromark-util-decode-string-2.0.1.tgz", + "integrity": "sha512-nDV/77Fj6eH1ynwscYTOsbK7rR//Uj0bZXBwJZRfaLEJ1iGBR6kIfNmlNqaqJf649EP0F3NWNdeJi03elllNUQ==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "decode-named-character-reference": "^1.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-encode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-2.0.1.tgz", + "integrity": "sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-util-html-tag-name": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-html-tag-name/-/micromark-util-html-tag-name-2.0.1.tgz", + "integrity": "sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-util-normalize-identifier": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-2.0.1.tgz", + "integrity": "sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-resolve-all": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-resolve-all/-/micromark-util-resolve-all-2.0.1.tgz", + "integrity": "sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-sanitize-uri": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.1.tgz", + "integrity": "sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-encode": "^2.0.0", + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-subtokenize": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-subtokenize/-/micromark-util-subtokenize-2.1.0.tgz", + "integrity": "sha512-XQLu552iSctvnEcgXw6+Sx75GflAPNED1qx7eBJ+wydBb2KCbRZe+NwvIEEMM83uml1+2WSXpBAcp9IUCgCYWA==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-symbol": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", + "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-util-types": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.2.tgz", + "integrity": "sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/minimatch": { + "version": "10.2.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.6.tgz", + "integrity": "sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.8" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/minipass": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz", + "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/mlly": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.8.2.tgz", + "integrity": "sha512-d+ObxMQFmbt10sretNDytwt85VrbkhhUA/JBGm1MPaWJ65Cl4wOgLaB1NYvJSZ0Ef03MMEU/0xpPMXUIQ29UfA==", + "dev": true, + "license": "MIT", + "dependencies": { + "acorn": "^8.16.0", + "pathe": "^2.0.3", + "pkg-types": "^1.3.1", + "ufo": "^1.6.3" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/mz": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", + "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "any-promise": "^1.0.0", + "object-assign": "^4.0.1", + "thenify-all": "^1.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/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "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/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": { - "acorn": "^8.16.0", - "pathe": "^2.0.3", - "pkg-types": "^1.3.1", - "ufo": "^1.6.3" + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", "dev": true, - "license": "MIT" + "license": "BlueOak-1.0.0" }, - "node_modules/mz": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", - "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", "dev": true, "license": "MIT", "dependencies": { - "any-promise": "^1.0.0", - "object-assign": "^4.0.1", - "thenify-all": "^1.0.0" + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" } }, - "node_modules/object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "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": ">=0.10.0" + "node": ">=8" } }, - "node_modules/package-json-from-dist": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", - "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", "dev": true, - "license": "BlueOak-1.0.0" + "license": "MIT", + "engines": { + "node": ">=8" + } }, "node_modules/path-scurry": { "version": "2.0.2", @@ -1404,6 +3990,56 @@ } } }, + "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.9.7", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.9.7.tgz", + "integrity": "sha512-T3mF5P7HFzZVLDQuCUNtJj6WK1pcpLMweMNUVwGb01bLNkH9AkKxtZvmInw8K6bnn2O3d6/5RHl6zSHiBAD0Og==", + "dev": true, + "license": "MIT", + "peer": true, + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/prettier-linter-helpers": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.1.tgz", + "integrity": "sha512-SxToR7P8Y2lWmv/kTzVLC1t/GDI2WGjMwNhLLE9qtH8Q13C+aEmuRlzDst4Up4s0Wc8sF2M+J57iB3cMLqftfg==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-diff": "^1.1.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "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/readdirp": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", @@ -1418,6 +4054,33 @@ "url": "https://paulmillr.com/funding/" } }, + "node_modules/refa": { + "version": "0.12.1", + "resolved": "https://registry.npmjs.org/refa/-/refa-0.12.1.tgz", + "integrity": "sha512-J8rn6v4DBb2nnFqkqwy6/NnTYMcgLA+sLr0iIO41qpv0n+ngb7ksag2tMRl0inb1bbO/esUwzW1vbJi7K0sI0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/regexpp": "^4.8.0" + }, + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/regexp-ast-analysis": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/regexp-ast-analysis/-/regexp-ast-analysis-0.7.1.tgz", + "integrity": "sha512-sZuz1dYW/ZsfG17WSAG7eS85r5a0dDsvg+7BiiYR5o6lKCAtUrEwdmRmaGF6rwVj3LcmAeYkOWKEPlbPzN3Y3A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/regexpp": "^4.8.0", + "refa": "^0.12.1" + }, + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, "node_modules/resolve-from": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", @@ -1494,6 +4157,57 @@ "fsevents": "~2.3.2" } }, + "node_modules/scslre": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/scslre/-/scslre-0.3.0.tgz", + "integrity": "sha512-3A6sD0WYP7+QrjbfNA2FN3FsOaGGFoekCVgTyypy53gPxhbkCIjtO6YWgdrfM+n/8sI8JeXZOIxsHjMTNxQ4nQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/regexpp": "^4.8.0", + "refa": "^0.12.0", + "regexp-ast-analysis": "^0.7.0" + }, + "engines": { + "node": "^14.0.0 || >=16.0.0" + } + }, + "node_modules/semver": { + "version": "7.8.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", + "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/source-map": { "version": "0.7.6", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.6.tgz", @@ -1504,6 +4218,19 @@ "node": ">= 12" } }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/sucrase": { "version": "3.35.1", "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.1.tgz", @@ -1527,6 +4254,35 @@ "node": ">=16 || 14 >=14.17" } }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/synckit": { + "version": "0.11.13", + "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.11.13.tgz", + "integrity": "sha512-eNRKgb3z66Yp3D2CixVujOUvXLFUTij/zVnV8KRyvFdQwpz7I5DS8UfRkTeLzb64u+dkzDSdelE24izu+zSSUg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@pkgr/core": "^0.3.6" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/synckit" + } + }, "node_modules/thenify": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", @@ -1584,6 +4340,19 @@ "tree-kill": "cli.js" } }, + "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-interface-checker": { "version": "0.1.13", "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", @@ -1644,6 +4413,19 @@ } } }, + "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", + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, "node_modules/typescript": { "version": "5.9.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", @@ -1658,6 +4440,30 @@ "node": ">=14.17" } }, + "node_modules/typescript-eslint": { + "version": "8.65.0", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.65.0.tgz", + "integrity": "sha512-/ggrHAwyjENDusvyxbuqxAC2dTnZg/Z8F+fgQtYIz+L6n/9HfSlEZcFGV/NsMNa6CkGk0xUjUAFwC0vHOflvIA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/eslint-plugin": "8.65.0", + "@typescript-eslint/parser": "8.65.0", + "@typescript-eslint/typescript-estree": "8.65.0", + "@typescript-eslint/utils": "8.65.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/ufo": { "version": "1.6.4", "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.6.4.tgz", @@ -1671,6 +4477,171 @@ "integrity": "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==", "dev": true, "license": "MIT" + }, + "node_modules/unist-util-is": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-6.0.1.tgz", + "integrity": "sha512-LsiILbtBETkDz8I9p1dQ0uyRUWuaQzd/cuEeS1hoRSyW5E5XGmTzlwY1OrNzzakGowI9Dr/I8HVaw4hTtnxy8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-stringify-position": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz", + "integrity": "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-visit": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-5.1.0.tgz", + "integrity": "sha512-m+vIdyeCOpdr/QeQCu2EzxX/ohgS8KbnPDgFni4dQsfSCtpz8UqDyY5GjRru8PDKuYn7Fq19j1CQ+nJSsGKOzg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0", + "unist-util-visit-parents": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-visit-parents": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-6.0.2.tgz", + "integrity": "sha512-goh1s1TBrqSqukSc8wrjwWhL0hiJxgA8m4kFxGlQ+8FYQ3C/m11FcTs4YYem7V664AhHVvgoQLk890Ssdsr2IQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "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/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "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/yaml": { + "version": "2.9.1", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.9.1.tgz", + "integrity": "sha512-3NxN8+78OdzbT7C/WjGsyfPAtJaN3FNDsWxv7Y7mcDsT/oOmgW8BpyQQFFBnvZE3j9Y2Sdz1ULFLezL7Eb2yFw==", + "dev": true, + "license": "ISC", + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14.6" + }, + "funding": { + "url": "https://github.com/sponsors/eemeli" + } + }, + "node_modules/yaml-eslint-parser": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/yaml-eslint-parser/-/yaml-eslint-parser-1.3.2.tgz", + "integrity": "sha512-odxVsHAkZYYglR30aPYRY4nUGJnoJ2y1ww2HDvZALo0BDETv9kWbi16J52eHs+PWRNmF4ub6nZqfVOeesOvntg==", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-visitor-keys": "^3.0.0", + "yaml": "^2.0.0" + }, + "engines": { + "node": "^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ota-meshi" + } + }, + "node_modules/yaml-eslint-parser/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/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/zwitch": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz", + "integrity": "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } } } } diff --git a/package.json b/package.json index 24ec512..49c7992 100644 --- a/package.json +++ b/package.json @@ -44,9 +44,23 @@ "build:types": "tsc -p tsconfig.types.json" }, "devDependencies": { + "@eslint/js": "^9.39.5", + "@eslint/markdown": "^7.5.1", "@types/node": "^24.13.5", + "eslint": "^9.39.5", + "eslint-config-prettier": "^10.1.8", + "eslint-plugin-jsonc": "^2.21.1", + "eslint-plugin-prettier": "^5.5.6", + "eslint-plugin-promise": "^7.3.0", + "eslint-plugin-sonarjs": "^4.2.0", + "eslint-plugin-unused-imports": "^4.4.1", + "eslint-plugin-yml": "^1.19.1", + "globals": "^17.12.0", + "jsonc-eslint-parser": "^2.4.2", "rimraf": "^6.1.3", "tsup": "^8.5.1", - "typescript": "^5.9.3" + "typescript": "^5.9.3", + "typescript-eslint": "^8.65.0", + "yaml-eslint-parser": "^1.3.2" } } From 82b1e1a2fdf9e32ecf58cc822d19e3c7e4cacd9d Mon Sep 17 00:00:00 2001 From: rcpthongta <220981657+rcpthongta@users.noreply.github.com> Date: Fri, 18 Sep 2026 01:31:35 +0700 Subject: [PATCH 015/121] chore(lint): configure eslint and prettier formatting rules --- eslint.config.mjs | 177 ++++++++++++++++++++++++++++++++++++++++++++ prettier.config.mjs | 15 ++++ 2 files changed, 192 insertions(+) create mode 100644 eslint.config.mjs create mode 100644 prettier.config.mjs diff --git a/eslint.config.mjs b/eslint.config.mjs new file mode 100644 index 0000000..f79bc31 --- /dev/null +++ b/eslint.config.mjs @@ -0,0 +1,177 @@ +import javascript from "@eslint/js"; +import markdown from "@eslint/markdown"; + +import jsonc from "eslint-plugin-jsonc"; +import prettier from "eslint-plugin-prettier/recommended"; +import promise from "eslint-plugin-promise"; +import sonarjs from "eslint-plugin-sonarjs"; +import unusedImports from "eslint-plugin-unused-imports"; +import yml from "eslint-plugin-yml"; +import eslint from "eslint/config"; +import globals from "globals"; +import jsoncParser from "jsonc-eslint-parser"; +import typescript from "typescript-eslint"; +import yamlParser from "yaml-eslint-parser"; + +const jsFiles = ["**/*.cjs", "**/*.js", "**/*.mjs"]; +const tsFiles = ["**/*.ts"]; +const sourceFiles = [...jsFiles, ...tsFiles]; + +const configuration = eslint.defineConfig( + eslint.globalIgnores(["**/coverage/*", "**/dist/*", "**/node_modules/*", "**/.npmrc", "**/package-lock.json"]), + { + files: sourceFiles, + languageOptions: { + globals: { + ...globals.browser, + ...globals.es2022, + ...globals.nodeBuiltin + } + } + }, + { + ...prettier, + files: sourceFiles + }, + { + files: sourceFiles, + plugins: { + "unused-imports": unusedImports + }, + rules: { + "unused-imports/no-unused-imports": "error", + "unused-imports/no-unused-vars": [ + "error", + { + args: "after-used", + argsIgnorePattern: "^_", + vars: "all", + varsIgnorePattern: "^_" + } + ] + } + }, + { + files: sourceFiles, + plugins: { + promise + }, + rules: { + ...promise.configs["flat/recommended"].rules, + "promise/always-return": [ + "error", + { + ignoreLastCallback: true + } + ], + "promise/no-callback-in-promise": "error", + "promise/no-multiple-resolved": "error", + "promise/no-nesting": "error", + "promise/no-promise-in-callback": "error", + "promise/no-return-in-finally": "error", + "promise/valid-params": "error" + } + }, + { + files: sourceFiles, + extends: [sonarjs.configs.recommended], + rules: { + "sonarjs/no-duplicate-string": "off", + "sonarjs/regex-complexity": [ + "error", + { + threshold: 25 + } + ] + } + }, + { + files: jsFiles, + extends: [javascript.configs.recommended], + rules: { + "no-unused-vars": "off" + } + }, + { + files: tsFiles, + plugins: { + "@typescript-eslint": typescript.plugin + }, + extends: [typescript.configs.recommended], + languageOptions: { + parser: typescript.parser, + parserOptions: { + project: true, + tsconfigRootDir: import.meta.dirname, + sourceType: "module" + } + }, + rules: { + "@typescript-eslint/no-unused-vars": "off" + } + }, + ...jsonc.configs["flat/recommended-with-jsonc"].map((config) => ({ + ...config, + files: ["**/*.*rc", "**/*.json", "**/*.json5", "**/*.jsonc", "**/*.jsonl"], + languageOptions: { + ...config.languageOptions, + parser: jsoncParser, + parserOptions: { + jsonSyntax: "JSONC" + } + }, + rules: { + ...config.rules, + "jsonc/array-bracket-newline": "error", + "jsonc/array-bracket-spacing": "error", + "jsonc/array-element-newline": "error", + "jsonc/comma-dangle": "error", + "jsonc/comma-style": "error", + "jsonc/indent": ["error", 2], + "jsonc/key-spacing": "error", + "jsonc/no-irregular-whitespace": "error", + "jsonc/no-octal-escape": "error", + "jsonc/object-curly-newline": "error", + "jsonc/object-curly-spacing": "error", + "jsonc/object-property-newline": "error" + } + })), + ...yml.configs["flat/recommended"].map((config) => ({ + ...config, + files: ["**/*.yml", "**/*.yaml"], + languageOptions: { + ...config.languageOptions, + parser: yamlParser, + parserOptions: { + defaultYAMLVersion: "1.2" + } + }, + rules: { + ...config.rules, + "yml/block-mapping": "error", + "yml/block-mapping-question-indicator-newline": "error", + "yml/block-sequence-hyphen-indicator-newline": "error", + "yml/block-sequence": "error", + "yml/file-extension": [ + "error", + { + extension: "yml" + } + ], + "yml/flow-mapping-curly-newline": "error", + "yml/flow-mapping-curly-spacing": "error", + "yml/flow-sequence-bracket-newline": "error", + "yml/flow-sequence-bracket-spacing": "error", + "yml/indent": "error", + "yml/key-spacing": "error", + "yml/no-multiple-empty-lines": "error", + "yml/no-trailing-zeros": "error", + "yml/plain-scalar": "error", + "yml/quotes": "error", + "yml/spaced-comment": "error" + } + })), + ...markdown.configs.recommended +); + +export default configuration; diff --git a/prettier.config.mjs b/prettier.config.mjs new file mode 100644 index 0000000..26ccbf7 --- /dev/null +++ b/prettier.config.mjs @@ -0,0 +1,15 @@ +const configuration = { + arrowParens: "always", + bracketSpacing: true, + embeddedLanguageFormatting: "auto", + endOfLine: "lf", + printWidth: 120, + quoteProps: "consistent", + semi: true, + singleQuote: false, + tabWidth: 2, + trailingComma: "none", + useTabs: false +}; + +export default configuration; From d4c2276e458e1b54628beb1d538bfdf67da34407 Mon Sep 17 00:00:00 2001 From: rcpthongta <220981657+rcpthongta@users.noreply.github.com> Date: Fri, 18 Sep 2026 01:32:23 +0700 Subject: [PATCH 016/121] chore(scripts): add lint and lint:fix scripts --- package.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index 49c7992..b41ff13 100644 --- a/package.json +++ b/package.json @@ -31,9 +31,7 @@ "require": "./dist/cjs/index.cjs" } }, - "files": [ - "dist" - ], + "files": ["dist"], "engines": { "node": ">=22" }, @@ -41,7 +39,9 @@ "build": "npm run build:clean && npm run build:js && npm run build:types", "build:clean": "rimraf dist", "build:js": "tsup", - "build:types": "tsc -p tsconfig.types.json" + "build:types": "tsc -p tsconfig.types.json", + "lint": "eslint **/*.{*rc,cjs,js,json,json5,jsonc,jsonl,md,mjs,ts,yaml,yml}", + "lint:fix": "eslint --fix **/*.{*rc,cjs,js,json,json5,jsonc,jsonl,md,mjs,ts,yaml,yml}" }, "devDependencies": { "@eslint/js": "^9.39.5", From 2c85b2faa8baf2ba2e6cb41ea3639dc1c198e354 Mon Sep 17 00:00:00 2001 From: rcpthongta <220981657+rcpthongta@users.noreply.github.com> Date: Fri, 18 Sep 2026 01:33:15 +0700 Subject: [PATCH 017/121] chore(editorconfig): enable trim trailing whitespace for code files --- .editorconfig | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/.editorconfig b/.editorconfig index d5d0a50..f65f114 100644 --- a/.editorconfig +++ b/.editorconfig @@ -8,6 +8,14 @@ indent_size = 2 indent_style = space insert_final_newline = true +[*.{cjs,js,json,json5,jsonc,jsonl,mjs,ts,yml}] + +trim_trailing_whitespace = true + +[.*rc] + +trim_trailing_whitespace = true + [*.md] trim_trailing_whitespace = false From 55f891880b7067e79bf3fb0d3a60f8e64185177a Mon Sep 17 00:00:00 2001 From: rcpthongta <220981657+rcpthongta@users.noreply.github.com> Date: Fri, 18 Sep 2026 01:34:13 +0700 Subject: [PATCH 018/121] chore(editor-config): configure eslint formatting and add extension recommendation --- .vscode/extensions.json | 1 + .vscode/settings.json | 24 +++++++++++++++++++----- 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/.vscode/extensions.json b/.vscode/extensions.json index c5a4a3f..1a1a2e2 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -1,5 +1,6 @@ { "recommendations": [ + "dbaeumer.vscode-eslint", "editorconfig.editorconfig", "streetsidesoftware.code-spell-checker", "yoavbls.pretty-ts-errors" diff --git a/.vscode/settings.json b/.vscode/settings.json index 1b8b76a..6c21d38 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,11 +1,25 @@ { + "[javascript][json][json5][jsonc][jsonl][markdown][typescript][yaml]": { + "editor.defaultFormatter": "dbaeumer.vscode-eslint" + }, "cSpell.allowCompoundWords": true, - "cSpell.ignoreRegExpList": [ - "/[\\u0E00-\\u0E7F]/g" - ], + "cSpell.ignoreRegExpList": ["/[\\u0E00-\\u0E7F]/g"], "cSpell.language": "en,en-US", - "cSpell.words": [ - "rcpthongta" + "cSpell.words": ["rcpthongta"], + "editor.codeActionsOnSave": { + "source.fixAll.eslint": "explicit" + }, + "editor.formatOnSave": true, + "eslint.format.enable": true, + "eslint.validate": [ + "javascript", + "json", + "json5", + "jsonc", + "jsonl", + "markdown", + "typescript", + "yaml" ], "typescript.tsdk": "node_modules\\typescript\\lib" } From bd194018e56b6d63b22891b686e29e362583f8f9 Mon Sep 17 00:00:00 2001 From: rcpthongta <220981657+rcpthongta@users.noreply.github.com> Date: Fri, 18 Sep 2026 01:35:58 +0700 Subject: [PATCH 019/121] style(tsc): format include array and normalize line endings --- tsconfig.build.json | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/tsconfig.build.json b/tsconfig.build.json index 4accab7..bdc6320 100644 --- a/tsconfig.build.json +++ b/tsconfig.build.json @@ -1,6 +1,4 @@ { "extends": "./tsconfig.json", - "include": [ - "src/**/*" - ] + "include": ["src/**/*"] } From 82313f25c4c8f6d8ae318afe030ef57c3a8ca9f5 Mon Sep 17 00:00:00 2001 From: rcpthongta <220981657+rcpthongta@users.noreply.github.com> Date: Fri, 18 Sep 2026 01:46:47 +0700 Subject: [PATCH 020/121] feat(has-text): add hasText utility function --- src/index.ts | 1 + src/libs/has-text/has-text.lib.ts | 20 ++++++++++++++++++++ src/libs/has-text/index.ts | 1 + src/libs/index.ts | 1 + 4 files changed, 23 insertions(+) create mode 100644 src/libs/has-text/has-text.lib.ts create mode 100644 src/libs/has-text/index.ts create mode 100644 src/libs/index.ts diff --git a/src/index.ts b/src/index.ts index e69de29..526c6b7 100644 --- a/src/index.ts +++ b/src/index.ts @@ -0,0 +1 @@ +export * from "./libs"; diff --git a/src/libs/has-text/has-text.lib.ts b/src/libs/has-text/has-text.lib.ts new file mode 100644 index 0000000..04efa6f --- /dev/null +++ b/src/libs/has-text/has-text.lib.ts @@ -0,0 +1,20 @@ +const NON_WHITESPACE_REGEX: RegExp = /\S/; + +/** + * Checks whether a value is a string containing at least one non-whitespace character. + * + * @param value - The value to check. + * @returns `true` if the value is a string containing at least one non-whitespace character; otherwise, `false`. + * + * @example + * + * hasText("Hello World"); // true + * hasText(" "); // false + * hasText(""); // false + * hasText(123); // false + * hasText(null); // false + * hasText(undefined); // false + */ +export function hasText(value: unknown): value is string { + return typeof value === "string" && NON_WHITESPACE_REGEX.test(value); +} diff --git a/src/libs/has-text/index.ts b/src/libs/has-text/index.ts new file mode 100644 index 0000000..eda1cbc --- /dev/null +++ b/src/libs/has-text/index.ts @@ -0,0 +1 @@ +export * from "./has-text.lib"; diff --git a/src/libs/index.ts b/src/libs/index.ts new file mode 100644 index 0000000..7973eff --- /dev/null +++ b/src/libs/index.ts @@ -0,0 +1 @@ +export * from "./has-text"; From 617f228a38eff6cb31fb9b0a7576db32c1a0ba6b Mon Sep 17 00:00:00 2001 From: rcpthongta <220981657+rcpthongta@users.noreply.github.com> Date: Fri, 18 Sep 2026 01:54:09 +0700 Subject: [PATCH 021/121] chore(deps-dev): add vitest and '@vitest/coverage-v8' --- package-lock.json | 1308 +++++++++++++++++++++++++++++++++++++++++++-- package.json | 6 +- 2 files changed, 1264 insertions(+), 50 deletions(-) diff --git a/package-lock.json b/package-lock.json index 760da47..49f6668 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,6 +12,7 @@ "@eslint/js": "^9.39.5", "@eslint/markdown": "^7.5.1", "@types/node": "^24.13.5", + "@vitest/coverage-v8": "^5.0.1", "eslint": "^9.39.5", "eslint-config-prettier": "^10.1.8", "eslint-plugin-jsonc": "^2.21.1", @@ -26,12 +27,73 @@ "tsup": "^8.5.1", "typescript": "^5.9.3", "typescript-eslint": "^8.65.0", + "vitest": "^5.0.1", "yaml-eslint-parser": "^1.3.2" }, "engines": { "node": ">=22" } }, + "node_modules/@babel/helper-string-parser": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz", + "integrity": "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz", + "integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.29.8", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.8.tgz", + "integrity": "sha512-E8lTAYNB1KW+FH+VGJuZM1ioAx2E6oVlvQFRrf5P8ZZmsiJXYAD9vTFV7yyEURNzgh1dFqMZuO6tUwcARbqFCA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.29.8" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/types": { + "version": "7.29.8", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.8.tgz", + "integrity": "sha512-Vj1jF3cPfxg7OAfoI7QnVKLoILlm2JF9pnVHrX8qx7AHMiYWT+NDAA7jChlNgRS4WTLc/fD1lXLmPixluj+3Gg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.29.7", + "@babel/helper-validator-identifier": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@bcoe/v8-coverage": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-1.0.2.tgz", + "integrity": "sha512-6zABk/ECA/QYSCQ1NGiVwwbQerUCZ+TQbp64Q3AgmfNvurHH0j8TtXa1qbShXA6qqkpAj4V5W8pP6mLe1mcMqA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, "node_modules/@esbuild/aix-ppc64": { "version": "0.27.7", "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.7.tgz", @@ -842,6 +904,17 @@ "node": "^22.20 || ^24.12 || >=25" } }, + "node_modules/@oxc-project/types": { + "version": "0.150.0", + "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.150.0.tgz", + "integrity": "sha512-rDS5/31E9HfPl/CIzGrn0DOlvBbXFseQ5URJ9sYMfstbKLD/c6Gm9vmRzRGDdAXyOIL4zmO37lc9RIwYqVruZw==", + "dev": true, + "license": "MIT", + "peer": true, + "funding": { + "url": "https://github.com/sponsors/oxc-project" + } + }, "node_modules/@pkgr/core": { "version": "0.3.6", "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.3.6.tgz", @@ -855,6 +928,302 @@ "url": "https://opencollective.com/pkgr" } }, + "node_modules/@rolldown/binding-android-arm-eabi": { + "version": "1.2.9", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm-eabi/-/binding-android-arm-eabi-1.2.9.tgz", + "integrity": "sha512-tNISae1QEf/vkb3xkRcjV5SEdzPE97We5IVaa2Z8jSszQPZ8U60B/YCYpw4QI7VidYsBtKavczXf+DyDs9WGxw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "peer": true, + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-android-arm64": { + "version": "1.2.9", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.2.9.tgz", + "integrity": "sha512-YC8YsI30o606GTZi0VyzYlsDKFP8W61i/QzayHDkLbNEz/IShqAmTa+hsJRj13xTHA0H+6fk4b2UmGn+Q/cMlg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "peer": true, + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-darwin-arm64": { + "version": "1.2.9", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.2.9.tgz", + "integrity": "sha512-IwhlH3qK5urrY8hZiEgGkHKEFN901p/p2bjxCxJlr4GyNnF7wYpUvK+Y43uaRYuC4hpfjzbR3SJC3arX1jGvmw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "peer": true, + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-darwin-x64": { + "version": "1.2.9", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.2.9.tgz", + "integrity": "sha512-XxpJfVzFh+jilRxIXUqcfYAYcunIc/XEzIizsOL1fcJee5Sf7H3mH8WlLmfHfluz5amqR88QQo9izKtmMlavAw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "peer": true, + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-freebsd-x64": { + "version": "1.2.9", + "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.2.9.tgz", + "integrity": "sha512-kSfvhmgeWyfkbT3p/1s5vSgboogoah2zkm9fX2zjg2hHxSV7T4KhMWRUUaRk4OXNqoD3QAUeRqLcs1aZOK4U1g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "peer": true, + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm-gnueabihf": { + "version": "1.2.9", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.2.9.tgz", + "integrity": "sha512-1RVzG17pxqbTfYLC352JlLt6kKLG+6Hr30n8DlIJqsnV5luUDd2Qdx9Ayw1Cabfyb1K9k0jXEZ7evxkRoT+uiw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm64-gnu": { + "version": "1.2.9", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.2.9.tgz", + "integrity": "sha512-BXqPvZ2drqVD+/Z8UpKwcs4Mp7grM+eGFku4CAEKrEtcbAsUpzREphK1sogCRZGreVPiMkiiBtw0n3TPteuqvw==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm64-musl": { + "version": "1.2.9", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.2.9.tgz", + "integrity": "sha512-11vWvo8YDwLzukt27J3aYDWU+gg2P7J+ZOmiJ0hkF5BXZDW7pVya7r40MXDy6ya0i9KamoENSVKIugvJNgFXIA==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-ppc64-gnu": { + "version": "1.2.9", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.2.9.tgz", + "integrity": "sha512-a1tijMkdwsIARtc0F39ApURROkf3NwqinI6TOiSSWCTR7dT96dffNvMUtDHnq64wKNTIZOIlzKrFvvFUznJiyw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-s390x-gnu": { + "version": "1.2.9", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.2.9.tgz", + "integrity": "sha512-x6SQNdAvv4c3hWqTMaWuawzMX9myaCs/yEmlGsxJzkdClnHW7FbrjQuSiRDhuSYzEYoEMhsaJy9qHG/XNemJPQ==", + "cpu": [ + "s390x" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-x64-gnu": { + "version": "1.2.9", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.2.9.tgz", + "integrity": "sha512-9s0AZ8BFK5/n7B/TBoa2yJE3gI3KURrbXcPBlsAsvjU4VeJKgE90y1YtNxyEUIcHPQkg6/yfF3qihUrcM/Kf0Q==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-x64-musl": { + "version": "1.2.9", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.2.9.tgz", + "integrity": "sha512-P7VWAmV+WdJluH7ovnRGoiv2i8To7GAZ+kGzfGup635cyL7SyYl3lSUaA3Gp5THf0n/Co5EyEqb2zbqq+nMOHQ==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-openharmony-arm64": { + "version": "1.2.9", + "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.2.9.tgz", + "integrity": "sha512-1qixtsE4BK8h+yS3BfmZ09UhA7O/N4IACva6YBr7EBvCJraByTuRcgOTaiA62Tm0vey3UcKXLOaoGHtYmNGEVg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "peer": true, + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-win32-arm64-msvc": { + "version": "1.2.9", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.2.9.tgz", + "integrity": "sha512-ok8IQjcEPs1AKZfuEUznVBrJw+gK4soq+bx8b1X2XoMqVClarc1q5JDmVtWXY1xfr6ZuHTAsPXHTgTrqKTZeww==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "peer": true, + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-win32-x64-msvc": { + "version": "1.2.9", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.2.9.tgz", + "integrity": "sha512-Ip2mXoU0hM0boq3Rf+ekuT653OROSo6aSYcPT1VHE4q52KvyxgFkQgrgb/IEsxOuvQ2fZZbs8khJAyCEPM24/g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "peer": true, + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/pluginutils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.1.tgz", + "integrity": "sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==", + "dev": true, + "license": "MIT", + "peer": true + }, "node_modules/@rollup/rollup-android-arm-eabi": { "version": "4.63.3", "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.63.3.tgz", @@ -1244,6 +1613,17 @@ "win32" ] }, + "node_modules/@types/chai": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.2.3.tgz", + "integrity": "sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/deep-eql": "*", + "assertion-error": "^2.0.1" + } + }, "node_modules/@types/debug": { "version": "4.1.13", "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.13.tgz", @@ -1254,6 +1634,13 @@ "@types/ms": "*" } }, + "node_modules/@types/deep-eql": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/deep-eql/-/deep-eql-4.0.2.tgz", + "integrity": "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/estree": { "version": "1.0.9", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz", @@ -1514,35 +1901,135 @@ "typescript": ">=4.8.4 <6.1.0" } }, - "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.65.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.65.0.tgz", - "integrity": "sha512-8C71BQkGjiMmXtop7pHVJu1l2NNShFdkCyD6a2ezzs5vU/L3LRtb69EtcteFwz0mYMPzIgOw0n6OV4VBUWZd7A==", + "node_modules/@typescript-eslint/visitor-keys": { + "version": "8.65.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.65.0.tgz", + "integrity": "sha512-8C71BQkGjiMmXtop7pHVJu1l2NNShFdkCyD6a2ezzs5vU/L3LRtb69EtcteFwz0mYMPzIgOw0n6OV4VBUWZd7A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.65.0", + "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/@typescript-eslint/visitor-keys/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/@vitest/coverage-v8": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-5.0.1.tgz", + "integrity": "sha512-FRC8ACiudC3dI6MTplzRSYWHDRnIv2IPfbzs4FdoJNsMal/35sWV8hwIfV8ZcqzSPy+uXHeMVONt9CEqtOU17w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@bcoe/v8-coverage": "^1.0.2", + "@vitest/istanbul-lib-coverage": "^1.0.0", + "@vitest/istanbul-lib-report": "^1.0.0", + "ast-v8-to-istanbul": "^1.0.5", + "magicast": "^0.5.4", + "obug": "^2.1.4", + "std-env": "^4.2.0", + "tinyrainbow": "^3.1.1" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "@vitest/browser": "5.0.1", + "vitest": "5.0.1" + }, + "peerDependenciesMeta": { + "@vitest/browser": { + "optional": true + } + } + }, + "node_modules/@vitest/istanbul-lib-coverage": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@vitest/istanbul-lib-coverage/-/istanbul-lib-coverage-1.0.1.tgz", + "integrity": "sha512-k3DJZ8LhMBK9NS4SclF1ASD3OgXEWDorbIcPTRDK0/Zae6fRvu+fJRxtFdLfHsa9Y24beCdPnoNZ4LviTNstfA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=22" + } + }, + "node_modules/@vitest/istanbul-lib-report": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@vitest/istanbul-lib-report/-/istanbul-lib-report-1.0.1.tgz", + "integrity": "sha512-1EOLRfsTMnyAr3+kEAsP4o9dhaDlGPpD7H5iLBBeq//YpNB1VIahkPhB+eRp9N2Dkfw8oySROjE3yf9XDeaIkQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/istanbul-lib-coverage": "1.0.1" + }, + "engines": { + "node": ">=22" + } + }, + "node_modules/@vitest/mocker": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-5.0.1.tgz", + "integrity": "sha512-6K1DoBNAPGvuOcSsGA4D6x+5zEEff/KmOOP3uetT2TrGpVfI+HRHRnJJfKi5ib/g1vx8IYHQD8s0pbJz8WQI7Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "0.3.31", + "@vitest/spy": "5.0.1", + "estree-walker": "^3.0.3", + "magic-string": "^1.2.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "msw": "^2.4.9", + "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "msw": { + "optional": true + }, + "vite": { + "optional": true + } + } + }, + "node_modules/@vitest/mocker/node_modules/magic-string": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-1.4.1.tgz", + "integrity": "sha512-8lyCu36ErXR0J9uaGKlKQoiLZKmtI63YGLE8G2o9jyRPdr4X47LusSOwgOJOzcVtp81fTAAjxR7BwKz682Jhow==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.65.0", - "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" + "@jridgewell/sourcemap-codec": "^1.6.0" } }, - "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { + "node_modules/@vitest/spy": { "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==", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-5.0.1.tgz", + "integrity": "sha512-rbto/mF/SGERxEgYOek7Xm6B9b+y+mVoo+f4b2LymYO8zM1b7uB5nHuhVMTP2hxdzgxvGiZYGxGIaMvL5y180Q==", "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^20.19.0 || ^22.13.0 || >=24" - }, + "license": "MIT", "funding": { - "url": "https://opencollective.com/eslint" + "url": "https://opencollective.com/vitest" } }, "node_modules/acorn": { @@ -1615,6 +2102,28 @@ "dev": true, "license": "Python-2.0" }, + "node_modules/assertion-error": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", + "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + } + }, + "node_modules/ast-v8-to-istanbul": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/ast-v8-to-istanbul/-/ast-v8-to-istanbul-1.0.6.tgz", + "integrity": "sha512-fvpl29helSO2w/z7utIbrkNXILdrLwDwAMH2I/zPKlGf5244+gf+B4cyS1sANcrPY2h+hWCGSgC8N61s/+AF9A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.31", + "estree-walker": "^3.0.3", + "js-tokens": "^10.0.0" + } + }, "node_modules/balanced-match": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", @@ -1708,6 +2217,16 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/chai": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/chai/-/chai-6.2.2.tgz", + "integrity": "sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, "node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -1870,6 +2389,17 @@ "node": ">=6" } }, + "node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "dev": true, + "license": "Apache-2.0", + "peer": true, + "engines": { + "node": ">=8" + } + }, "node_modules/devlop": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/devlop/-/devlop-1.1.0.tgz", @@ -1894,6 +2424,13 @@ "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" } }, + "node_modules/es-module-lexer": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.3.2.tgz", + "integrity": "sha512-poHGpORABojJJucnV9KbOavETW8lBVnphkW77ER5/BQ5Fz7oXSoCNek7IH3vR5nRjdsEz926ibFYX8KtLQmdyw==", + "dev": true, + "license": "MIT" + }, "node_modules/esbuild": { "version": "0.27.7", "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.7.tgz", @@ -2321,6 +2858,16 @@ "node": ">=4.0" } }, + "node_modules/estree-walker": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0" + } + }, "node_modules/esutils": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", @@ -2331,6 +2878,16 @@ "node": ">=0.10.0" } }, + "node_modules/expect-type": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.4.0.tgz", + "integrity": "sha512-KfYbmpRm0VbLjEvVa9yGwCi9GI34xvi7A/HXYWQO65CSD2u3MczUJSuwXKFIxlGsgBQizV9q5J9NHj4VG0n+pA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.0.0" + } + }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -2640,6 +3197,13 @@ "node": ">=10" } }, + "node_modules/js-tokens": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-10.0.0.tgz", + "integrity": "sha512-lM/UBzQmfJRo9ABXbPWemivdCW8V2G8FHaHdypQaIy523snUjog0W71ayWXTjiR+ixeMyVHN2XcpnTd/liPg/Q==", + "dev": true, + "license": "MIT" + }, "node_modules/js-yaml": { "version": "4.3.2", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.2.tgz", @@ -2728,44 +3292,329 @@ "eslint-visitor-keys": "^3.4.1" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/jsx-ast-utils-x": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/jsx-ast-utils-x/-/jsx-ast-utils-x-0.1.0.tgz", + "integrity": "sha512-eQQBjBnsVtGacsG9uJNB8qOr3yA8rga4wAaGG1qRcBzSIvfhERLrWxMAM1hp5fcS6Abo8M4+bUBTekYR0qTPQw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "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/lightningcss": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.33.0.tgz", + "integrity": "sha512-WkUDrojuJs0xkgGf2udWxa3yGBRxPtxUkB79i6aCZLRgc7PM8fZe9TosfPDcvEpQZbuFASnHYmRLBLUbmLOIIA==", + "dev": true, + "license": "MPL-2.0", + "peer": true, + "dependencies": { + "detect-libc": "^2.0.3" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "lightningcss-android-arm64": "1.33.0", + "lightningcss-darwin-arm64": "1.33.0", + "lightningcss-darwin-x64": "1.33.0", + "lightningcss-freebsd-x64": "1.33.0", + "lightningcss-linux-arm-gnueabihf": "1.33.0", + "lightningcss-linux-arm64-gnu": "1.33.0", + "lightningcss-linux-arm64-musl": "1.33.0", + "lightningcss-linux-x64-gnu": "1.33.0", + "lightningcss-linux-x64-musl": "1.33.0", + "lightningcss-win32-arm64-msvc": "1.33.0", + "lightningcss-win32-x64-msvc": "1.33.0" + } + }, + "node_modules/lightningcss-android-arm64": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.33.0.tgz", + "integrity": "sha512-gEpRTalKdosp4Bb8qWtc2iOgE5SeIHlpS1up9bFq2wAyYhl1UdTObYiHe98zEM9SQvSoqQZ1IQD0JNpg3Ml5pg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "android" + ], + "peer": true, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-arm64": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.33.0.tgz", + "integrity": "sha512-Sciaz8eenNTKn9b3t7+xr0ipTp9YxKQY4npwQ3mrRuL0BAVHBLyZxofhaKBAVtzmtRZ/zTyo0/to4B1uWG/Djg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "peer": true, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-x64": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.33.0.tgz", + "integrity": "sha512-Z5UPAxzrjlWNNyGy6i65cJzzvgJ5D3T6wMvs+gWpY9d7qRhANrxqAp6LhxIgZhWEw18RfJTGcRxjuLIBr+m8XQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "peer": true, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-freebsd-x64": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.33.0.tgz", + "integrity": "sha512-QQM/Ti/hQajJwCY+RiWuCZ9sdtI/XQk7nDK5vC8kkdwixezOlDgvDx7+RT+QjK6FcFT4MpsuoBnHIo/O3StRRg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "peer": true, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm-gnueabihf": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.33.0.tgz", + "integrity": "sha512-N7FVBe6iS24MlM6R/4RBTxGhQheZGs7tiQ9U32UtF75NzP5Q7xWPRqLBCKxlRQRk3rY1jCIPLzx7WzOhuUIRLQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-gnu": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.33.0.tgz", + "integrity": "sha512-j2v/itmy4HlNxlc6voKXYgBqNi0Ng2LShg4z7GufpEgs05P+2suBVyi9I6YHq5uoVFx9ETin3eCEhLVyXGQnKg==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-musl": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.33.0.tgz", + "integrity": "sha512-yiO5ROMuYQgXbC60yjZU5CYSFZGKXL0HFATXt9mHJn1+zW55oCtMI9NfcVhYLMFDL7gV7oBPon/EmMMGg2OvtQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-gnu": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.33.0.tgz", + "integrity": "sha512-ar+Ju7LmcN0Jo4FpL4hpFybwNG9/3A/Br5KW2n2jyODg3MEZXaDYADdemoNS+BDNfMgKvylJLj4S5tyRActuAg==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-musl": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.33.0.tgz", + "integrity": "sha512-RYiYbkokw0trfKqqzfF55lginwEPrD3OJDfTuJzFs1MK6iFnDenaz1fqLLtX4ITG3OktJQXOeTaw1awrBAlZPw==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">= 12.0.0" }, "funding": { - "url": "https://opencollective.com/eslint" + "type": "opencollective", + "url": "https://opencollective.com/parcel" } }, - "node_modules/jsx-ast-utils-x": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/jsx-ast-utils-x/-/jsx-ast-utils-x-0.1.0.tgz", - "integrity": "sha512-eQQBjBnsVtGacsG9uJNB8qOr3yA8rga4wAaGG1qRcBzSIvfhERLrWxMAM1hp5fcS6Abo8M4+bUBTekYR0qTPQw==", + "node_modules/lightningcss-win32-arm64-msvc": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.33.0.tgz", + "integrity": "sha512-1K+MPfLSFVpphzpdbfkhlWk6wBrTObBzS2T6db10PNOZgR9GoVsAWzwNyuhUYYbTp23j+4RrncfujZ4uAzXvwA==", + "cpu": [ + "arm64" + ], "dev": true, - "license": "MIT", + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "peer": true, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "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": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" } }, - "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==", + "node_modules/lightningcss-win32-x64-msvc": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.33.0.tgz", + "integrity": "sha512-OlEICDx/Xl0FqSp4bry8zFnCvGpig3Gl4gCquvYwHuqJKEC1+n9NgDniFvqHGmMv1ZkqDJrDqKKSykTDX+ehuA==", + "cpu": [ + "x64" + ], "dev": true, - "license": "MIT", - "dependencies": { - "prelude-ls": "^1.2.1", - "type-check": "~0.4.0" - }, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "peer": true, "engines": { - "node": ">= 0.8.0" + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" } }, "node_modules/lilconfig": { @@ -2852,6 +3701,18 @@ "@jridgewell/sourcemap-codec": "^1.5.5" } }, + "node_modules/magicast": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/magicast/-/magicast-0.5.5.tgz", + "integrity": "sha512-UicdXN8zQ3JHlxVq+28afMXPr1z7WNY6+7EJnzTdQWkTAlMLF5fNCCKxJHBQwGaNGR11581EiQmQzx73+MvszA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.7", + "@babel/types": "^7.29.7", + "source-map-js": "^1.2.1" + } + }, "node_modules/markdown-table": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/markdown-table/-/markdown-table-3.0.4.tgz", @@ -3774,6 +4635,26 @@ "thenify-all": "^1.0.0" } }, + "node_modules/nanoid": { + "version": "3.3.19", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.19.tgz", + "integrity": "sha512-Y2tUNy4ouw6tq5oDSKeQYGOyhkUBhNOcGV/02KC+6kd9eDGqdZd++mjMiIDilrBYvjEnCYvVtsuHCuP+okSfug==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "peer": true, + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, "node_modules/natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", @@ -3791,6 +4672,20 @@ "node": ">=0.10.0" } }, + "node_modules/obug": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/obug/-/obug-2.2.1.tgz", + "integrity": "sha512-XrsrhT5sybtKI6wakr2SPOlGZWWYbUXZ7a0jT8/QOeAPau+1X/bSegNe5YR75oJmEZQbKningirmGOEJCIk61Q==", + "dev": true, + "funding": [ + "https://github.com/sponsors/sxzz", + "https://opencollective.com/debug" + ], + "license": "MIT", + "engines": { + "node": ">=12.20.0" + } + }, "node_modules/optionator": { "version": "0.9.4", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", @@ -3947,6 +4842,36 @@ "pathe": "^2.0.1" } }, + "node_modules/postcss": { + "version": "8.5.28", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.28.tgz", + "integrity": "sha512-RRuzqDtt5Y9h3quz5hWhK+TPnsmVs6WwSU6LkJMeY4HstUEDuYTG8UJSdawMRzmzAtV+KEoG8N3Qg2qLy5vM/A==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "peer": true, + "dependencies": { + "nanoid": "^3.3.18", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, "node_modules/postcss-load-config": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-6.0.1.tgz", @@ -4111,6 +5036,41 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/rolldown": { + "version": "1.2.9", + "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.2.9.tgz", + "integrity": "sha512-hx/Pv0N1haXRb11qkfnK5MXB/iqr7i0yjWQqmO9uHqZpBgQSqzc8UsSnEpalsh+j1I8qQ2CkXAkJC8Br3dKSlg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@oxc-project/types": "=0.150.0", + "@rolldown/pluginutils": "^1.0.0" + }, + "bin": { + "rolldown": "bin/cli.mjs" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "optionalDependencies": { + "@rolldown/binding-android-arm-eabi": "1.2.9", + "@rolldown/binding-android-arm64": "1.2.9", + "@rolldown/binding-darwin-arm64": "1.2.9", + "@rolldown/binding-darwin-x64": "1.2.9", + "@rolldown/binding-freebsd-x64": "1.2.9", + "@rolldown/binding-linux-arm-gnueabihf": "1.2.9", + "@rolldown/binding-linux-arm64-gnu": "1.2.9", + "@rolldown/binding-linux-arm64-musl": "1.2.9", + "@rolldown/binding-linux-ppc64-gnu": "1.2.9", + "@rolldown/binding-linux-s390x-gnu": "1.2.9", + "@rolldown/binding-linux-x64-gnu": "1.2.9", + "@rolldown/binding-linux-x64-musl": "1.2.9", + "@rolldown/binding-openharmony-arm64": "1.2.9", + "@rolldown/binding-win32-arm64-msvc": "1.2.9", + "@rolldown/binding-win32-x64-msvc": "1.2.9" + } + }, "node_modules/rollup": { "version": "4.63.3", "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.63.3.tgz", @@ -4208,6 +5168,13 @@ "node": ">=8" } }, + "node_modules/siginfo": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", + "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", + "dev": true, + "license": "ISC" + }, "node_modules/source-map": { "version": "0.7.6", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.6.tgz", @@ -4218,6 +5185,30 @@ "node": ">= 12" } }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/stackback": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", + "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", + "dev": true, + "license": "MIT" + }, + "node_modules/std-env": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-4.2.0.tgz", + "integrity": "sha512-oCUKSupKTHX53EyjDtuZQ64pjLJ6yYCtpmEw0goYxtjG9KpbRe8KAsl2tBUGU9DyMcJ0RwJ8GqJAFzMXcXW1Rw==", + "dev": true, + "license": "MIT" + }, "node_modules/strip-json-comments": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", @@ -4306,6 +5297,16 @@ "node": ">=0.8" } }, + "node_modules/tinybench": { + "version": "6.1.4", + "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-6.1.4.tgz", + "integrity": "sha512-9APumHG7r4yOk4X4WlkmE71aZcv1gvin1czO3OQ1U9iJcFA5Ja/ygyb0vPOVHTthFozUYs8CLoLUlM8grb2lTQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20.0.0" + } + }, "node_modules/tinyexec": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-0.3.2.tgz", @@ -4330,6 +5331,16 @@ "url": "https://github.com/sponsors/SuperchupuDev" } }, + "node_modules/tinyrainbow": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-3.1.1.tgz", + "integrity": "sha512-yau8yJdTt989Mm0Bd/236QnzEiPf2xLLTqUZRUJOo/3CB078LSwzei343DgtJVmfJKJE3TMINY1u42SQsP6mXw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/tree-kill": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", @@ -4547,6 +5558,188 @@ "punycode": "^2.1.0" } }, + "node_modules/vite": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/vite/-/vite-8.3.0.tgz", + "integrity": "sha512-lhZBVvEHefgE+HQZC9O7EBJgCU/nVzFNl7vkS4RE0APtWLP02/8QVIkQtzBxPquh7lq5/78NHipTj7ODQ6XuyQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "lightningcss": "^1.33.0", + "picomatch": "^4.0.7", + "postcss": "^8.5.28", + "rolldown": "~1.2.6", + "tinyglobby": "^0.2.17" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^20.19.0 || >=22.12.0", + "@vitejs/devtools": "^0.7.1", + "esbuild": "^0.27.0 || ^0.28.0", + "jiti": ">=1.21.0", + "less": "^4.0.0", + "sass": "^1.70.0", + "sass-embedded": "^1.70.0", + "stylus": ">=0.54.8", + "sugarss": "^5.0.0", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "@vitejs/devtools": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/vitest": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-5.0.1.tgz", + "integrity": "sha512-iA95lQbKEkvrtTkdAgnWbXfbipWiiWe/hDl2P5tMi6WFwD76G0NxXAGp/M9EOcYupeGJRr6wppMc7CoA41TQjg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/chai": "^5.2.2", + "@vitest/mocker": "5.0.1", + "chai": "^6.2.2", + "es-module-lexer": "^2.3.2", + "expect-type": "^1.4.0", + "magic-string": "^1.2.3", + "obug": "^2.1.4", + "picomatch": "^4.0.7", + "std-env": "^4.2.0", + "tinybench": "6.1.4", + "tinyexec": "1.3.0", + "tinyglobby": "^0.2.17", + "why-is-node-running": "^2.3.0" + }, + "bin": { + "vitest": "vitest.mjs" + }, + "engines": { + "node": "^22.12.0 || ^24.0.0 || >=26.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "@edge-runtime/vm": "*", + "@opentelemetry/api": "^1.9.0", + "@types/node": "^22.0.0 || >=24.0.0", + "@vitest/browser-playwright": "5.0.1", + "@vitest/browser-preview": "5.0.1", + "@vitest/browser-webdriverio": "^5.0.0-beta.5 || >=5.0.0", + "@vitest/coverage-istanbul": "5.0.1", + "@vitest/coverage-v8": "5.0.1", + "@vitest/ui": "5.0.1", + "happy-dom": "*", + "jsdom": "*", + "vite": "^6.4.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "@edge-runtime/vm": { + "optional": true + }, + "@opentelemetry/api": { + "optional": true + }, + "@types/node": { + "optional": true + }, + "@vitest/browser-playwright": { + "optional": true + }, + "@vitest/browser-preview": { + "optional": true + }, + "@vitest/browser-webdriverio": { + "optional": true + }, + "@vitest/coverage-istanbul": { + "optional": true + }, + "@vitest/coverage-v8": { + "optional": true + }, + "@vitest/ui": { + "optional": true + }, + "happy-dom": { + "optional": true + }, + "jsdom": { + "optional": true + }, + "vite": { + "optional": false + } + } + }, + "node_modules/vitest/node_modules/magic-string": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-1.4.1.tgz", + "integrity": "sha512-8lyCu36ErXR0J9uaGKlKQoiLZKmtI63YGLE8G2o9jyRPdr4X47LusSOwgOJOzcVtp81fTAAjxR7BwKz682Jhow==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.6.0" + } + }, + "node_modules/vitest/node_modules/tinyexec": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.3.0.tgz", + "integrity": "sha512-QKAl9m8gWWGHV8jZcPeym6j+XULi6tOf1mT83WYJ4Lk2ytW/uwAWkrP0uFsdoYMdueVJ0qs26wZ+23xeB4ibNQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -4563,6 +5756,23 @@ "node": ">= 8" } }, + "node_modules/why-is-node-running": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz", + "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==", + "dev": true, + "license": "MIT", + "dependencies": { + "siginfo": "^2.0.0", + "stackback": "0.0.2" + }, + "bin": { + "why-is-node-running": "cli.js" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/word-wrap": { "version": "1.2.5", "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", diff --git a/package.json b/package.json index b41ff13..05d09d9 100644 --- a/package.json +++ b/package.json @@ -31,7 +31,9 @@ "require": "./dist/cjs/index.cjs" } }, - "files": ["dist"], + "files": [ + "dist" + ], "engines": { "node": ">=22" }, @@ -47,6 +49,7 @@ "@eslint/js": "^9.39.5", "@eslint/markdown": "^7.5.1", "@types/node": "^24.13.5", + "@vitest/coverage-v8": "^5.0.1", "eslint": "^9.39.5", "eslint-config-prettier": "^10.1.8", "eslint-plugin-jsonc": "^2.21.1", @@ -61,6 +64,7 @@ "tsup": "^8.5.1", "typescript": "^5.9.3", "typescript-eslint": "^8.65.0", + "vitest": "^5.0.1", "yaml-eslint-parser": "^1.3.2" } } From 4231cfe28f149dc121a3908ed4f0db0bb9a7a120 Mon Sep 17 00:00:00 2001 From: rcpthongta <220981657+rcpthongta@users.noreply.github.com> Date: Fri, 18 Sep 2026 01:56:00 +0700 Subject: [PATCH 022/121] test(config): configure vitest test runner, benchmarks, and coverage --- vitest.config.ts | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 vitest.config.ts diff --git a/vitest.config.ts b/vitest.config.ts new file mode 100644 index 0000000..0df2c43 --- /dev/null +++ b/vitest.config.ts @@ -0,0 +1,23 @@ +import { defineConfig } from "vitest/config"; + +export default defineConfig({ + test: { + include: ["**/*.spec.ts"], + environment: "node", + benchmark: { + include: ["**/*.bench.ts"] + }, + coverage: { + provider: "v8", + include: ["src/**/*.ts"], + exclude: ["**/index.ts", "**/*.spec.ts", "**/*.bench.ts"], + thresholds: { + statements: 100, + branches: 100, + functions: 100, + lines: 100 + }, + reportsDirectory: "./coverage" + } + } +}); From 514ea43108ac9f6e94c623c76e80316d023fef46 Mon Sep 17 00:00:00 2001 From: rcpthongta <220981657+rcpthongta@users.noreply.github.com> Date: Fri, 18 Sep 2026 01:56:43 +0700 Subject: [PATCH 023/121] chore(tsc): exclude spec files from build tsconfig --- tsconfig.build.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tsconfig.build.json b/tsconfig.build.json index bdc6320..ef7c122 100644 --- a/tsconfig.build.json +++ b/tsconfig.build.json @@ -1,4 +1,5 @@ { "extends": "./tsconfig.json", - "include": ["src/**/*"] + "include": ["src/**/*"], + "exclude": ["**/*.spec.ts"] } From ecf4729491bb63040d2b612ae15cd2b353128b1c Mon Sep 17 00:00:00 2001 From: rcpthongta <220981657+rcpthongta@users.noreply.github.com> Date: Fri, 18 Sep 2026 01:57:31 +0700 Subject: [PATCH 024/121] chore(scripts): add test and benchmark scripts --- package.json | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index 05d09d9..7d44c2e 100644 --- a/package.json +++ b/package.json @@ -31,9 +31,7 @@ "require": "./dist/cjs/index.cjs" } }, - "files": [ - "dist" - ], + "files": ["dist"], "engines": { "node": ">=22" }, @@ -42,6 +40,10 @@ "build:clean": "rimraf dist", "build:js": "tsup", "build:types": "tsc -p tsconfig.types.json", + "test": "vitest run", + "test:watch": "vitest", + "test:coverage": "vitest run --coverage", + "bench": "vitest bench --run", "lint": "eslint **/*.{*rc,cjs,js,json,json5,jsonc,jsonl,md,mjs,ts,yaml,yml}", "lint:fix": "eslint --fix **/*.{*rc,cjs,js,json,json5,jsonc,jsonl,md,mjs,ts,yaml,yml}" }, From d7dfd4254f10f2232dfae254a4a5df700a4e12ad Mon Sep 17 00:00:00 2001 From: rcpthongta <220981657+rcpthongta@users.noreply.github.com> Date: Fri, 18 Sep 2026 01:57:54 +0700 Subject: [PATCH 025/121] chore(git): ignore test coverage directory --- .gitignore | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.gitignore b/.gitignore index 99ca5ec..106b3b0 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,10 @@ node_modules/* dist/* +# Test directory + +coverage/* + # Text editor directory and files • Visual Studio Code .vscode/* From f1ffa1d0f2c6549fee7d48346f5ae7c2f8703e68 Mon Sep 17 00:00:00 2001 From: rcpthongta <220981657+rcpthongta@users.noreply.github.com> Date: Fri, 18 Sep 2026 02:20:13 +0700 Subject: [PATCH 026/121] chore(deps-dev): downgrade vitest and coverage to v4.1.10 --- package-lock.json | 750 ++++++++++++++++++++++++++-------------------- package.json | 4 +- 2 files changed, 429 insertions(+), 325 deletions(-) diff --git a/package-lock.json b/package-lock.json index 49f6668..758d3e0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,7 +12,7 @@ "@eslint/js": "^9.39.5", "@eslint/markdown": "^7.5.1", "@types/node": "^24.13.5", - "@vitest/coverage-v8": "^5.0.1", + "@vitest/coverage-v8": "^4.1.10", "eslint": "^9.39.5", "eslint-config-prettier": "^10.1.8", "eslint-plugin-jsonc": "^2.21.1", @@ -27,7 +27,7 @@ "tsup": "^8.5.1", "typescript": "^5.9.3", "typescript-eslint": "^8.65.0", - "vitest": "^5.0.1", + "vitest": "^4.1.10", "yaml-eslint-parser": "^1.3.2" }, "engines": { @@ -593,37 +593,6 @@ "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, - "node_modules/@eslint/config-array/node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true, - "license": "MIT" - }, - "node_modules/@eslint/config-array/node_modules/brace-expansion": { - "version": "1.1.21", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.21.tgz", - "integrity": "sha512-9zeA+KLZNNzglF2TPKRQEDyx6Yby7daAkuy8MiPzpXPsYDWi/DRM8jmwUDxokQjYqBpv5DgPiwD4h4ZZSy1Ujw==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/@eslint/config-array/node_modules/minimatch": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", - "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, "node_modules/@eslint/config-helpers": { "version": "0.4.2", "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.2.tgz", @@ -674,24 +643,6 @@ "url": "https://opencollective.com/eslint" } }, - "node_modules/@eslint/eslintrc/node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true, - "license": "MIT" - }, - "node_modules/@eslint/eslintrc/node_modules/brace-expansion": { - "version": "1.1.21", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.21.tgz", - "integrity": "sha512-9zeA+KLZNNzglF2TPKRQEDyx6Yby7daAkuy8MiPzpXPsYDWi/DRM8jmwUDxokQjYqBpv5DgPiwD4h4ZZSy1Ujw==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, "node_modules/@eslint/eslintrc/node_modules/globals": { "version": "14.0.0", "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", @@ -705,19 +656,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@eslint/eslintrc/node_modules/minimatch": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", - "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, "node_modules/@eslint/js": { "version": "9.39.5", "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.5.tgz", @@ -910,7 +848,6 @@ "integrity": "sha512-rDS5/31E9HfPl/CIzGrn0DOlvBbXFseQ5URJ9sYMfstbKLD/c6Gm9vmRzRGDdAXyOIL4zmO37lc9RIwYqVruZw==", "dev": true, "license": "MIT", - "peer": true, "funding": { "url": "https://github.com/sponsors/oxc-project" } @@ -941,7 +878,6 @@ "os": [ "android" ], - "peer": true, "engines": { "node": "^20.19.0 || >=22.12.0" } @@ -959,7 +895,6 @@ "os": [ "android" ], - "peer": true, "engines": { "node": "^20.19.0 || >=22.12.0" } @@ -977,7 +912,6 @@ "os": [ "darwin" ], - "peer": true, "engines": { "node": "^20.19.0 || >=22.12.0" } @@ -995,7 +929,6 @@ "os": [ "darwin" ], - "peer": true, "engines": { "node": "^20.19.0 || >=22.12.0" } @@ -1013,7 +946,6 @@ "os": [ "freebsd" ], - "peer": true, "engines": { "node": "^20.19.0 || >=22.12.0" } @@ -1031,7 +963,6 @@ "os": [ "linux" ], - "peer": true, "engines": { "node": "^20.19.0 || >=22.12.0" } @@ -1052,7 +983,6 @@ "os": [ "linux" ], - "peer": true, "engines": { "node": "^20.19.0 || >=22.12.0" } @@ -1073,7 +1003,6 @@ "os": [ "linux" ], - "peer": true, "engines": { "node": "^20.19.0 || >=22.12.0" } @@ -1094,7 +1023,6 @@ "os": [ "linux" ], - "peer": true, "engines": { "node": "^20.19.0 || >=22.12.0" } @@ -1115,7 +1043,6 @@ "os": [ "linux" ], - "peer": true, "engines": { "node": "^20.19.0 || >=22.12.0" } @@ -1136,7 +1063,6 @@ "os": [ "linux" ], - "peer": true, "engines": { "node": "^20.19.0 || >=22.12.0" } @@ -1157,7 +1083,6 @@ "os": [ "linux" ], - "peer": true, "engines": { "node": "^20.19.0 || >=22.12.0" } @@ -1175,7 +1100,6 @@ "os": [ "openharmony" ], - "peer": true, "engines": { "node": "^20.19.0 || >=22.12.0" } @@ -1193,7 +1117,6 @@ "os": [ "win32" ], - "peer": true, "engines": { "node": "^20.19.0 || >=22.12.0" } @@ -1211,7 +1134,6 @@ "os": [ "win32" ], - "peer": true, "engines": { "node": "^20.19.0 || >=22.12.0" } @@ -1221,8 +1143,7 @@ "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.1.tgz", "integrity": "sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/@rollup/rollup-android-arm-eabi": { "version": "4.63.3", @@ -1613,6 +1534,20 @@ "win32" ] }, + "node_modules/@sonarsource/analyzer-commons-configurations": { + "version": "2.31.0-5284", + "resolved": "https://registry.npmjs.org/@sonarsource/analyzer-commons-configurations/-/analyzer-commons-configurations-2.31.0-5284.tgz", + "integrity": "sha512-ruk3blMs9BE9RNSp9LPpWZAIjxJmq4Cp00E3cELIHz7Kym3WxdWGRlKOMYLRQ5tJg/de7jN5AtEElY3bGIfTcw==", + "dev": true, + "license": "LGPL-3.0-only" + }, + "node_modules/@standard-schema/spec": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz", + "integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/chai": { "version": "5.2.3", "resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.2.3.tgz", @@ -1690,17 +1625,17 @@ "license": "MIT" }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.65.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.65.0.tgz", - "integrity": "sha512-IEgob78X12rHpUmtcwFsXhZdVGJtwTVP8FiCLZkR6GlYVrl2PcuB+KhCE5BlVC/eQpQnu8WXRtkHZuPar+gCRA==", + "version": "8.70.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.70.0.tgz", + "integrity": "sha512-/v8HZt6RlyIZxB3ntehELOcUcfxKPVGWXnQdJuHRmzrqgF8nQypcC/oxGW+Ot4VGKDq81XugPKxx0n5PBtf9PA==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.12.2", - "@typescript-eslint/scope-manager": "8.65.0", - "@typescript-eslint/type-utils": "8.65.0", - "@typescript-eslint/utils": "8.65.0", - "@typescript-eslint/visitor-keys": "8.65.0", + "@typescript-eslint/scope-manager": "8.70.0", + "@typescript-eslint/type-utils": "8.70.0", + "@typescript-eslint/utils": "8.70.0", + "@typescript-eslint/visitor-keys": "8.70.0", "ignore": "^7.0.5", "natural-compare": "^1.4.0", "ts-api-utils": "^2.5.0" @@ -1713,7 +1648,7 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^8.65.0", + "@typescript-eslint/parser": "^8.70.0", "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.1.0" } @@ -1729,16 +1664,16 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "8.65.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.65.0.tgz", - "integrity": "sha512-CZ4nMxWwgu1HEEFNkeaCptra9QCtkmKdgf3sWh1rl1trIhmxLilgTV4cwcbQ4wemnT4sWQN8CaKOmdYx+g2gMA==", + "version": "8.70.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.70.0.tgz", + "integrity": "sha512-zYvrmj9Yxd63UGaXw+kdt6A0F0s0qveJyuatIM77bYC2DE4pgmg7a50u8LR7PRtXd0x+h+Tl3eXabGm06SWd3Q==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/scope-manager": "8.65.0", - "@typescript-eslint/types": "8.65.0", - "@typescript-eslint/typescript-estree": "8.65.0", - "@typescript-eslint/visitor-keys": "8.65.0", + "@typescript-eslint/scope-manager": "8.70.0", + "@typescript-eslint/types": "8.70.0", + "@typescript-eslint/typescript-estree": "8.70.0", + "@typescript-eslint/visitor-keys": "8.70.0", "debug": "^4.4.3" }, "engines": { @@ -1754,14 +1689,14 @@ } }, "node_modules/@typescript-eslint/project-service": { - "version": "8.65.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.65.0.tgz", - "integrity": "sha512-SxnPhbTsGahizDgbu7oqFH/xVtzIqMd/s+WtnSxNxJZJpLbdT5IPdzg8EZxO3+PoKahXmwJLeNQOpKJb3/bi7Q==", + "version": "8.70.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.70.0.tgz", + "integrity": "sha512-hFHbTNqhU9G+2eKFXCBVb1tjFT/LceiJ4+HfLO4pTpDI0KHi6iajpcFFkaSQ9gXmCh7n82A0PthaayEdN6mspQ==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.65.0", - "@typescript-eslint/types": "^8.65.0", + "@typescript-eslint/tsconfig-utils": "^8.70.0", + "@typescript-eslint/types": "^8.70.0", "debug": "^4.4.3" }, "engines": { @@ -1776,14 +1711,14 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.65.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.65.0.tgz", - "integrity": "sha512-Esbl8OSYiVxBokYgWPf7VVWg/BE798wXhimnn9ML9Pt5qoDf8bfQlgjlKXR/k98+AcNzlLKYrpCcrcuZ9DZLgg==", + "version": "8.70.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.70.0.tgz", + "integrity": "sha512-8nP3Kwh5hlgZ4FicGvmznAmJe8UL4sdU8tLukrPaMuQmDuk4Y8xYfzu/aYZW4xT2JCgc7H/TpDI5cGlxcWJSqQ==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.65.0", - "@typescript-eslint/visitor-keys": "8.65.0" + "@typescript-eslint/types": "8.70.0", + "@typescript-eslint/visitor-keys": "8.70.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1794,9 +1729,9 @@ } }, "node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.65.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.65.0.tgz", - "integrity": "sha512-j6GzGqCiRdA7Qhur2VVmKZAkBLfnHFQfx4TaJGL9RMveZqCo48jSHHO0DTgizEnGhtWnqmbtCUSrqSkdiY/0Hg==", + "version": "8.70.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.70.0.tgz", + "integrity": "sha512-adnkeeNq9Sq1sUf4+FRVc0KdgYghzsgFpZSQVZVvY0LCuUuN0FnQgyGzCJeC4fW1cdXseBAjU2EOqUIjbNcZUw==", "dev": true, "license": "MIT", "engines": { @@ -1811,15 +1746,15 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.65.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.65.0.tgz", - "integrity": "sha512-YjaZ7PRI5qY7ax2L3PbvX0rRyGtipAReCWs0mhhDBHjH/vl0g0BonaGXrKdKpMbIIsMIwDgbk/xzkBTyAltS5g==", + "version": "8.70.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.70.0.tgz", + "integrity": "sha512-NUMKIhYVaVIVLnRL9CRt+VVcuLgSHUCpXn4/+K8wql+vdInUzvx8BjUO1oJ7cG9shjFJKtF8F8Hh2kCh3/KBVw==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.65.0", - "@typescript-eslint/typescript-estree": "8.65.0", - "@typescript-eslint/utils": "8.65.0", + "@typescript-eslint/types": "8.70.0", + "@typescript-eslint/typescript-estree": "8.70.0", + "@typescript-eslint/utils": "8.70.0", "debug": "^4.4.3", "ts-api-utils": "^2.5.0" }, @@ -1836,9 +1771,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "8.65.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.65.0.tgz", - "integrity": "sha512-JSSwWNy+H0E/01jJEM+hrX6N0OFDzFzeIhHFSAS01tlVaevpG8cFyYRPhS5yjGOvBUx3sqQHVMjCL1CAZZMxBg==", + "version": "8.70.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.70.0.tgz", + "integrity": "sha512-asTOIYhDg4zdzOScCyaytrsV3cR6B4ecPQlXw/dJIm7J/MZTtCtfVII9JD8Geh4jTCrK/Xe6cg5UevoleMcoJQ==", "dev": true, "license": "MIT", "engines": { @@ -1850,16 +1785,16 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.65.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.65.0.tgz", - "integrity": "sha512-JboAE2swaYt4tb1fHhHTABE2K+OLy09XfcTbhnk4Pw96f9dd2e9iYsJ28gBggHlo5z5x1rkyWvcPoTuNTd4oGg==", + "version": "8.70.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.70.0.tgz", + "integrity": "sha512-d9NmHMPEKQ7QCLLm1jI3zmoQBwT5KwFYjXBJ9ymZfKCUU+5rmTRykKAFvH5Qn/ZCds3CEAFS9OC9M/jkl0X2bA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/project-service": "8.65.0", - "@typescript-eslint/tsconfig-utils": "8.65.0", - "@typescript-eslint/types": "8.65.0", - "@typescript-eslint/visitor-keys": "8.65.0", + "@typescript-eslint/project-service": "8.70.0", + "@typescript-eslint/tsconfig-utils": "8.70.0", + "@typescript-eslint/types": "8.70.0", + "@typescript-eslint/visitor-keys": "8.70.0", "debug": "^4.4.3", "minimatch": "^10.2.2", "semver": "^7.7.3", @@ -1877,17 +1812,56 @@ "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.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.12.tgz", + "integrity": "sha512-YovQ3rzhaLMIrDjNDMkNS01tea93qhEhG5xy8f6+R0l+dw3Ki+5sCoIoI942iuLZTHWogWktgwVDhU09iNEimQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { + "version": "10.2.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.6.tgz", + "integrity": "sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.8" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/@typescript-eslint/utils": { - "version": "8.65.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.65.0.tgz", - "integrity": "sha512-gXiwIHsYreboxeJucHKPvgwl7dXt50mF8s1/c00cP/WoVTyWKFdtfhRWwZiXYFU5H2O8vVoSLNrexFZjYS/SGA==", + "version": "8.70.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.70.0.tgz", + "integrity": "sha512-oZmtKJz/4fufZ2p3+Cn3ijEojcdfR+1zYDH2xKYrEly0dR/Q/1xUPRCOlKGxod78nWlU2UnDe09GZ3TaknBFGA==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.9.1", - "@typescript-eslint/scope-manager": "8.65.0", - "@typescript-eslint/types": "8.65.0", - "@typescript-eslint/typescript-estree": "8.65.0" + "@typescript-eslint/scope-manager": "8.70.0", + "@typescript-eslint/types": "8.70.0", + "@typescript-eslint/typescript-estree": "8.70.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1902,13 +1876,13 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.65.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.65.0.tgz", - "integrity": "sha512-8C71BQkGjiMmXtop7pHVJu1l2NNShFdkCyD6a2ezzs5vU/L3LRtb69EtcteFwz0mYMPzIgOw0n6OV4VBUWZd7A==", + "version": "8.70.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.70.0.tgz", + "integrity": "sha512-BoC8PiO4Hkdo0TVJh9Ntxr5MxPDI7/oFsrygN5ADelFSeXG/qgNuucIGA+L5Z6JpPTE/uRfcTWtscjbUaufepQ==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.65.0", + "@typescript-eslint/types": "8.70.0", "eslint-visitor-keys": "^5.0.0" }, "engines": { @@ -1933,27 +1907,29 @@ } }, "node_modules/@vitest/coverage-v8": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-5.0.1.tgz", - "integrity": "sha512-FRC8ACiudC3dI6MTplzRSYWHDRnIv2IPfbzs4FdoJNsMal/35sWV8hwIfV8ZcqzSPy+uXHeMVONt9CEqtOU17w==", + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-4.1.11.tgz", + "integrity": "sha512-8MVGEFnJIcdGjcbfKmeq8z0pZHH0JlVtoVZH9Q/qwUp6wyFnEJUBMrw9DCaj+ra3vShGmhavjalMIhPNxZAUcw==", "dev": true, "license": "MIT", "dependencies": { "@bcoe/v8-coverage": "^1.0.2", - "@vitest/istanbul-lib-coverage": "^1.0.0", - "@vitest/istanbul-lib-report": "^1.0.0", - "ast-v8-to-istanbul": "^1.0.5", - "magicast": "^0.5.4", - "obug": "^2.1.4", - "std-env": "^4.2.0", - "tinyrainbow": "^3.1.1" + "@vitest/utils": "4.1.11", + "ast-v8-to-istanbul": "^1.0.0", + "istanbul-lib-coverage": "^3.2.2", + "istanbul-lib-report": "^3.0.1", + "istanbul-reports": "^3.2.0", + "magicast": "^0.5.2", + "obug": "^2.1.1", + "std-env": "^4.0.0-rc.1", + "tinyrainbow": "^3.1.0" }, "funding": { "url": "https://opencollective.com/vitest" }, "peerDependencies": { - "@vitest/browser": "5.0.1", - "vitest": "5.0.1" + "@vitest/browser": "4.1.11", + "vitest": "4.1.11" }, "peerDependenciesMeta": { "@vitest/browser": { @@ -1961,40 +1937,34 @@ } } }, - "node_modules/@vitest/istanbul-lib-coverage": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@vitest/istanbul-lib-coverage/-/istanbul-lib-coverage-1.0.1.tgz", - "integrity": "sha512-k3DJZ8LhMBK9NS4SclF1ASD3OgXEWDorbIcPTRDK0/Zae6fRvu+fJRxtFdLfHsa9Y24beCdPnoNZ4LviTNstfA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=22" - } - }, - "node_modules/@vitest/istanbul-lib-report": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@vitest/istanbul-lib-report/-/istanbul-lib-report-1.0.1.tgz", - "integrity": "sha512-1EOLRfsTMnyAr3+kEAsP4o9dhaDlGPpD7H5iLBBeq//YpNB1VIahkPhB+eRp9N2Dkfw8oySROjE3yf9XDeaIkQ==", + "node_modules/@vitest/expect": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.1.11.tgz", + "integrity": "sha512-VX2x5vNJXET47KAFzwERI+KRMtTTCSWTfSMKsW7JsUsXV4psq++e3DvZpuTDOpHcxytiDs6p2nhVb2tVDiiUYw==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/istanbul-lib-coverage": "1.0.1" + "@standard-schema/spec": "^1.1.0", + "@types/chai": "^5.2.2", + "@vitest/spy": "4.1.11", + "@vitest/utils": "4.1.11", + "chai": "^6.2.2", + "tinyrainbow": "^3.1.0" }, - "engines": { - "node": ">=22" + "funding": { + "url": "https://opencollective.com/vitest" } }, "node_modules/@vitest/mocker": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-5.0.1.tgz", - "integrity": "sha512-6K1DoBNAPGvuOcSsGA4D6x+5zEEff/KmOOP3uetT2TrGpVfI+HRHRnJJfKi5ib/g1vx8IYHQD8s0pbJz8WQI7Q==", + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.1.11.tgz", + "integrity": "sha512-2XJVD55d1o5AZous5CCGKS74g/riOj9odEt2bQpCVZeblHyHdnMeFl4jl0XjU21stf4mbjUkew2eXQZt65g5CQ==", "dev": true, "license": "MIT", "dependencies": { - "@jridgewell/trace-mapping": "0.3.31", - "@vitest/spy": "5.0.1", + "@vitest/spy": "4.1.11", "estree-walker": "^3.0.3", - "magic-string": "^1.2.3" + "magic-string": "^0.30.21" }, "funding": { "url": "https://opencollective.com/vitest" @@ -2012,22 +1982,70 @@ } } }, - "node_modules/@vitest/mocker/node_modules/magic-string": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-1.4.1.tgz", - "integrity": "sha512-8lyCu36ErXR0J9uaGKlKQoiLZKmtI63YGLE8G2o9jyRPdr4X47LusSOwgOJOzcVtp81fTAAjxR7BwKz682Jhow==", + "node_modules/@vitest/pretty-format": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.1.11.tgz", + "integrity": "sha512-yiZzPbGTS9Sr/JpFl8zHrcIkAofNbFV6k21vIgQN/cY/oxZeXhJv5sc/MBJ5jFKWmWs+oJHw0UXLZjmf931+Vw==", "dev": true, "license": "MIT", "dependencies": { - "@jridgewell/sourcemap-codec": "^1.6.0" + "tinyrainbow": "^3.1.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/runner": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.1.11.tgz", + "integrity": "sha512-LztvUgdwMNJMIkj3hQnnxiC2Xy1zNxq928W/xhjCLaNCzqTZOudjwbQf6v9IntZGPw132i2Lq2rgTRZHD3JHNw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/utils": "4.1.11", + "pathe": "^2.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/snapshot": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.1.11.tgz", + "integrity": "sha512-pN7ikn1ON7h8ee4gIAp4AzyK+zBtJPzVbqOgu5LCEh4VaJVbPQcgYQYJIMGQPXVeJJq1fnfazis7a5pFNPahog==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "4.1.11", + "@vitest/utils": "4.1.11", + "magic-string": "^0.30.21", + "pathe": "^2.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" } }, "node_modules/@vitest/spy": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-5.0.1.tgz", - "integrity": "sha512-rbto/mF/SGERxEgYOek7Xm6B9b+y+mVoo+f4b2LymYO8zM1b7uB5nHuhVMTP2hxdzgxvGiZYGxGIaMvL5y180Q==", + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.1.11.tgz", + "integrity": "sha512-apNa/prQy2qCeywhnixOHPRCgGNhvg7T4Dapfl1GahLp/R+uhBm5cPyFoNVyqsNd2h1nJxL6BqqdIjiABL60YA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/utils": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.1.11.tgz", + "integrity": "sha512-zTCVGpyFsGWBhllOyKlTw/vnr6D9qxsfSDyfbyZmTyjHw5N/VuvzHpHoQjm2ZJzn4RJgx5w4r7V0er69CmLgPQ==", "dev": true, "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "4.1.11", + "convert-source-map": "^2.0.0", + "tinyrainbow": "^3.1.0" + }, "funding": { "url": "https://opencollective.com/vitest" } @@ -2125,26 +2143,21 @@ } }, "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==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "dev": true, - "license": "MIT", - "engines": { - "node": "18 || 20 || >=22" - } + "license": "MIT" }, "node_modules/brace-expansion": { - "version": "5.0.12", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.12.tgz", - "integrity": "sha512-YovQ3rzhaLMIrDjNDMkNS01tea93qhEhG5xy8f6+R0l+dw3Ki+5sCoIoI942iuLZTHWogWktgwVDhU09iNEimQ==", + "version": "1.1.21", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.21.tgz", + "integrity": "sha512-9zeA+KLZNNzglF2TPKRQEDyx6Yby7daAkuy8MiPzpXPsYDWi/DRM8jmwUDxokQjYqBpv5DgPiwD4h4ZZSy1Ujw==", "dev": true, "license": "MIT", "dependencies": { - "balanced-match": "^4.0.2" - }, - "engines": { - "node": "20 || >=22" + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" } }, "node_modules/builtin-modules": { @@ -2325,6 +2338,13 @@ "node": "^14.18.0 || >=16.10.0" } }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "license": "MIT" + }, "node_modules/cross-spawn": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", @@ -2395,7 +2415,6 @@ "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", "dev": true, "license": "Apache-2.0", - "peer": true, "engines": { "node": ">=8" } @@ -2679,20 +2698,21 @@ } }, "node_modules/eslint-plugin-sonarjs": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-sonarjs/-/eslint-plugin-sonarjs-4.2.0.tgz", - "integrity": "sha512-bqADfuNtTL7VK6RU29eoiFTtaaBKIpVPuX3bOl+rBpWSBa0zIBVZlqZNZQjfP6s4iXkAJokv5IsD8OsACkwApg==", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-sonarjs/-/eslint-plugin-sonarjs-4.2.1.tgz", + "integrity": "sha512-WGsnVkUxmHyBOytShcIeE+vhQaS8e+gYgbHNNi4y2e5Zj+DpE2DLTvarqDUhNwjceTZHGLgevSgfBSEVkt4EaA==", "dev": true, "license": "LGPL-3.0-only", "dependencies": { "@eslint-community/regexpp": "^4.12.2", + "@sonarsource/analyzer-commons-configurations": "^2.31.0-5284", "builtin-modules": "^3.3.0", "bytes": "^3.1.2", "functional-red-black-tree": "^1.0.1", - "globals": "^17.7.0", + "globals": "^17.12.0", "jsx-ast-utils-x": "^0.1.0", "lodash.merge": "^4.6.2", - "minimatch": "^10.2.5", + "minimatch": "^10.2.6", "scslre": "^0.3.0", "semver": "^7.8.5", "ts-api-utils": "^2.5.0", @@ -2703,6 +2723,45 @@ "eslint": "^8.0.0 || ^9.0.0 || ^10.0.0" } }, + "node_modules/eslint-plugin-sonarjs/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-plugin-sonarjs/node_modules/brace-expansion": { + "version": "5.0.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.12.tgz", + "integrity": "sha512-YovQ3rzhaLMIrDjNDMkNS01tea93qhEhG5xy8f6+R0l+dw3Ki+5sCoIoI942iuLZTHWogWktgwVDhU09iNEimQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/eslint-plugin-sonarjs/node_modules/minimatch": { + "version": "10.2.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.6.tgz", + "integrity": "sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.8" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/eslint-plugin-unused-imports": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/eslint-plugin-unused-imports/-/eslint-plugin-unused-imports-4.4.1.tgz", @@ -2773,37 +2832,6 @@ "url": "https://opencollective.com/eslint" } }, - "node_modules/eslint/node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true, - "license": "MIT" - }, - "node_modules/eslint/node_modules/brace-expansion": { - "version": "1.1.21", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.21.tgz", - "integrity": "sha512-9zeA+KLZNNzglF2TPKRQEDyx6Yby7daAkuy8MiPzpXPsYDWi/DRM8jmwUDxokQjYqBpv5DgPiwD4h4ZZSy1Ujw==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/eslint/node_modules/minimatch": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", - "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, "node_modules/espree": { "version": "10.4.0", "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", @@ -3080,6 +3108,45 @@ "node": ">=10.13.0" } }, + "node_modules/glob/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/glob/node_modules/brace-expansion": { + "version": "5.0.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.12.tgz", + "integrity": "sha512-YovQ3rzhaLMIrDjNDMkNS01tea93qhEhG5xy8f6+R0l+dw3Ki+5sCoIoI942iuLZTHWogWktgwVDhU09iNEimQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/glob/node_modules/minimatch": { + "version": "10.2.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.6.tgz", + "integrity": "sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.8" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/globals": { "version": "17.12.0", "resolved": "https://registry.npmjs.org/globals/-/globals-17.12.0.tgz", @@ -3110,6 +3177,13 @@ "node": ">=8" } }, + "node_modules/html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "dev": true, + "license": "MIT" + }, "node_modules/ignore": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", @@ -3137,16 +3211,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/import-fresh/node_modules/resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "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", @@ -3187,6 +3251,45 @@ "dev": true, "license": "ISC" }, + "node_modules/istanbul-lib-coverage": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", + "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-report": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", + "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^4.0.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-reports": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.2.0.tgz", + "integrity": "sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/joycon": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/joycon/-/joycon-3.1.1.tgz", @@ -3338,7 +3441,6 @@ "integrity": "sha512-WkUDrojuJs0xkgGf2udWxa3yGBRxPtxUkB79i6aCZLRgc7PM8fZe9TosfPDcvEpQZbuFASnHYmRLBLUbmLOIIA==", "dev": true, "license": "MPL-2.0", - "peer": true, "dependencies": { "detect-libc": "^2.0.3" }, @@ -3376,7 +3478,6 @@ "os": [ "android" ], - "peer": true, "engines": { "node": ">= 12.0.0" }, @@ -3398,7 +3499,6 @@ "os": [ "darwin" ], - "peer": true, "engines": { "node": ">= 12.0.0" }, @@ -3420,7 +3520,6 @@ "os": [ "darwin" ], - "peer": true, "engines": { "node": ">= 12.0.0" }, @@ -3442,7 +3541,6 @@ "os": [ "freebsd" ], - "peer": true, "engines": { "node": ">= 12.0.0" }, @@ -3464,7 +3562,6 @@ "os": [ "linux" ], - "peer": true, "engines": { "node": ">= 12.0.0" }, @@ -3489,7 +3586,6 @@ "os": [ "linux" ], - "peer": true, "engines": { "node": ">= 12.0.0" }, @@ -3514,7 +3610,6 @@ "os": [ "linux" ], - "peer": true, "engines": { "node": ">= 12.0.0" }, @@ -3539,7 +3634,6 @@ "os": [ "linux" ], - "peer": true, "engines": { "node": ">= 12.0.0" }, @@ -3564,7 +3658,6 @@ "os": [ "linux" ], - "peer": true, "engines": { "node": ">= 12.0.0" }, @@ -3586,7 +3679,6 @@ "os": [ "win32" ], - "peer": true, "engines": { "node": ">= 12.0.0" }, @@ -3608,7 +3700,6 @@ "os": [ "win32" ], - "peer": true, "engines": { "node": ">= 12.0.0" }, @@ -3713,6 +3804,22 @@ "source-map-js": "^1.2.1" } }, + "node_modules/make-dir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", + "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/markdown-table": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/markdown-table/-/markdown-table-3.0.4.tgz", @@ -4578,19 +4685,16 @@ "license": "MIT" }, "node_modules/minimatch": { - "version": "10.2.6", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.6.tgz", - "integrity": "sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A==", + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", "dev": true, - "license": "BlueOak-1.0.0", + "license": "ISC", "dependencies": { - "brace-expansion": "^5.0.8" + "brace-expansion": "^1.1.7" }, "engines": { - "node": "18 || 20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "node": "*" } }, "node_modules/minipass": { @@ -4647,7 +4751,6 @@ } ], "license": "MIT", - "peer": true, "bin": { "nanoid": "bin/nanoid.cjs" }, @@ -4862,7 +4965,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "nanoid": "^3.3.18", "picocolors": "^1.1.1", @@ -5007,13 +5109,13 @@ } }, "node_modules/resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", "dev": true, "license": "MIT", "engines": { - "node": ">=8" + "node": ">=4" } }, "node_modules/rimraf": { @@ -5042,7 +5144,6 @@ "integrity": "sha512-hx/Pv0N1haXRb11qkfnK5MXB/iqr7i0yjWQqmO9uHqZpBgQSqzc8UsSnEpalsh+j1I8qQ2CkXAkJC8Br3dKSlg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@oxc-project/types": "=0.150.0", "@rolldown/pluginutils": "^1.0.0" @@ -5298,14 +5399,11 @@ } }, "node_modules/tinybench": { - "version": "6.1.4", - "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-6.1.4.tgz", - "integrity": "sha512-9APumHG7r4yOk4X4WlkmE71aZcv1gvin1czO3OQ1U9iJcFA5Ja/ygyb0vPOVHTthFozUYs8CLoLUlM8grb2lTQ==", + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", + "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==", "dev": true, - "license": "MIT", - "engines": { - "node": ">=20.0.0" - } + "license": "MIT" }, "node_modules/tinyexec": { "version": "0.3.2", @@ -5424,6 +5522,16 @@ } } }, + "node_modules/tsup/node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -5452,16 +5560,16 @@ } }, "node_modules/typescript-eslint": { - "version": "8.65.0", - "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.65.0.tgz", - "integrity": "sha512-/ggrHAwyjENDusvyxbuqxAC2dTnZg/Z8F+fgQtYIz+L6n/9HfSlEZcFGV/NsMNa6CkGk0xUjUAFwC0vHOflvIA==", + "version": "8.70.0", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.70.0.tgz", + "integrity": "sha512-P/W5cz70/cQAuKfY3xwQMWWTV7BvJ0mAQmi+9mBcsVPaBUpd6Ohpa+fECv9rBFrQcig86jAiNBFNWUqnTjr4pw==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/eslint-plugin": "8.65.0", - "@typescript-eslint/parser": "8.65.0", - "@typescript-eslint/typescript-estree": "8.65.0", - "@typescript-eslint/utils": "8.65.0" + "@typescript-eslint/eslint-plugin": "8.70.0", + "@typescript-eslint/parser": "8.70.0", + "@typescript-eslint/typescript-estree": "8.70.0", + "@typescript-eslint/utils": "8.70.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -5564,7 +5672,6 @@ "integrity": "sha512-lhZBVvEHefgE+HQZC9O7EBJgCU/nVzFNl7vkS4RE0APtWLP02/8QVIkQtzBxPquh7lq5/78NHipTj7ODQ6XuyQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "lightningcss": "^1.33.0", "picomatch": "^4.0.7", @@ -5638,31 +5745,38 @@ } }, "node_modules/vitest": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/vitest/-/vitest-5.0.1.tgz", - "integrity": "sha512-iA95lQbKEkvrtTkdAgnWbXfbipWiiWe/hDl2P5tMi6WFwD76G0NxXAGp/M9EOcYupeGJRr6wppMc7CoA41TQjg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/chai": "^5.2.2", - "@vitest/mocker": "5.0.1", - "chai": "^6.2.2", - "es-module-lexer": "^2.3.2", - "expect-type": "^1.4.0", - "magic-string": "^1.2.3", - "obug": "^2.1.4", - "picomatch": "^4.0.7", - "std-env": "^4.2.0", - "tinybench": "6.1.4", - "tinyexec": "1.3.0", - "tinyglobby": "^0.2.17", + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.1.11.tgz", + "integrity": "sha512-fhACrNXUidIbGSBr5FlbuBkO7VWC1ZyLl0DO4CU2DrQoAPxX84Ysxs+HeGQpii5lZWV1Q4gBZTTu49mF+A6Edw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/expect": "4.1.11", + "@vitest/mocker": "4.1.11", + "@vitest/pretty-format": "4.1.11", + "@vitest/runner": "4.1.11", + "@vitest/snapshot": "4.1.11", + "@vitest/spy": "4.1.11", + "@vitest/utils": "4.1.11", + "es-module-lexer": "^2.0.0", + "expect-type": "^1.3.0", + "magic-string": "^0.30.21", + "obug": "^2.1.1", + "pathe": "^2.0.3", + "picomatch": "^4.0.3", + "std-env": "^4.0.0-rc.1", + "tinybench": "^2.9.0", + "tinyexec": "^1.0.2", + "tinyglobby": "^0.2.15", + "tinyrainbow": "^3.1.0", + "vite": "^6.0.0 || ^7.0.0 || ^8.0.0", "why-is-node-running": "^2.3.0" }, "bin": { "vitest": "vitest.mjs" }, "engines": { - "node": "^22.12.0 || ^24.0.0 || >=26.0.0" + "node": "^20.0.0 || ^22.0.0 || >=24.0.0" }, "funding": { "url": "https://opencollective.com/vitest" @@ -5670,16 +5784,16 @@ "peerDependencies": { "@edge-runtime/vm": "*", "@opentelemetry/api": "^1.9.0", - "@types/node": "^22.0.0 || >=24.0.0", - "@vitest/browser-playwright": "5.0.1", - "@vitest/browser-preview": "5.0.1", - "@vitest/browser-webdriverio": "^5.0.0-beta.5 || >=5.0.0", - "@vitest/coverage-istanbul": "5.0.1", - "@vitest/coverage-v8": "5.0.1", - "@vitest/ui": "5.0.1", + "@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0", + "@vitest/browser-playwright": "4.1.11", + "@vitest/browser-preview": "4.1.11", + "@vitest/browser-webdriverio": "4.1.11", + "@vitest/coverage-istanbul": "4.1.11", + "@vitest/coverage-v8": "4.1.11", + "@vitest/ui": "4.1.11", "happy-dom": "*", "jsdom": "*", - "vite": "^6.4.0 || ^7.0.0 || ^8.0.0" + "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" }, "peerDependenciesMeta": { "@edge-runtime/vm": { @@ -5720,20 +5834,10 @@ } } }, - "node_modules/vitest/node_modules/magic-string": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-1.4.1.tgz", - "integrity": "sha512-8lyCu36ErXR0J9uaGKlKQoiLZKmtI63YGLE8G2o9jyRPdr4X47LusSOwgOJOzcVtp81fTAAjxR7BwKz682Jhow==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.6.0" - } - }, "node_modules/vitest/node_modules/tinyexec": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.3.0.tgz", - "integrity": "sha512-QKAl9m8gWWGHV8jZcPeym6j+XULi6tOf1mT83WYJ4Lk2ytW/uwAWkrP0uFsdoYMdueVJ0qs26wZ+23xeB4ibNQ==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.3.1.tgz", + "integrity": "sha512-GCvB3aoys96IuDFBMcTB46JOR6mdMtAToqwiW8JlWhsoh1mhHi/xn9ss/Dg7N555GiJyEt2qzoG/NHCwM6h1EA==", "dev": true, "license": "MIT", "engines": { diff --git a/package.json b/package.json index 7d44c2e..e30b550 100644 --- a/package.json +++ b/package.json @@ -51,7 +51,7 @@ "@eslint/js": "^9.39.5", "@eslint/markdown": "^7.5.1", "@types/node": "^24.13.5", - "@vitest/coverage-v8": "^5.0.1", + "@vitest/coverage-v8": "^4.1.10", "eslint": "^9.39.5", "eslint-config-prettier": "^10.1.8", "eslint-plugin-jsonc": "^2.21.1", @@ -66,7 +66,7 @@ "tsup": "^8.5.1", "typescript": "^5.9.3", "typescript-eslint": "^8.65.0", - "vitest": "^5.0.1", + "vitest": "^4.1.10", "yaml-eslint-parser": "^1.3.2" } } From 0ce7a9458c5725380a2374f321407016cd07f859 Mon Sep 17 00:00:00 2001 From: rcpthongta <220981657+rcpthongta@users.noreply.github.com> Date: Fri, 18 Sep 2026 02:33:47 +0700 Subject: [PATCH 027/121] test(has-text): add unit tests for non-whitespace string check --- src/libs/has-text/has-text.lib.spec.ts | 111 +++++++++++++++++++++++++ 1 file changed, 111 insertions(+) create mode 100644 src/libs/has-text/has-text.lib.spec.ts diff --git a/src/libs/has-text/has-text.lib.spec.ts b/src/libs/has-text/has-text.lib.spec.ts new file mode 100644 index 0000000..bd1f7ec --- /dev/null +++ b/src/libs/has-text/has-text.lib.spec.ts @@ -0,0 +1,111 @@ +import { describe, expect, expectTypeOf, it } from "vitest"; + +import { hasText } from "./has-text.lib"; + +describe("hasText", (): void => { + describe("given a string containing text", (): void => { + it("returns true for standard text", (): void => { + expect(hasText("Hello World")).toBe(true); + expect(hasText("12345")).toBe(true); + }); + + it("returns true when text is surrounded by whitespace", (): void => { + expect(hasText(" Hello World ")).toBe(true); + expect(hasText("\t\n text \r\n")).toBe(true); + }); + + it("returns true for symbols and emojis", (): void => { + expect(hasText("!")).toBe(true); + expect(hasText("!@#$%^&*()")).toBe(true); + expect(hasText("🔥")).toBe(true); + expect(hasText("🎉🚀🌟")).toBe(true); + }); + + it("returns true for Unicode and accented text", (): void => { + expect(hasText("สวัสดีชาวโลก")).toBe(true); + expect(hasText("こんにちは")).toBe(true); + expect(hasText("你好世界")).toBe(true); + expect(hasText("مرحبا بالعالم")).toBe(true); + expect(hasText("café")).toBe(true); + expect(hasText("cafe\u0301")).toBe(true); + }); + }); + + describe("given a string containing only whitespace", (): void => { + it("returns false for ASCII whitespace", (): void => { + expect(hasText(" ")).toBe(false); + expect(hasText(" ")).toBe(false); + expect(hasText("\t\n\r\v\f")).toBe(false); + }); + + it("returns false for Unicode whitespace", (): void => { + expect(hasText("\u00A0")).toBe(false); + expect(hasText("\u2003")).toBe(false); + expect(hasText("\u202F")).toBe(false); + expect(hasText("\u3000")).toBe(false); + expect(hasText(" \t\n\r\u00A0\u2003\u202F\u3000 ")).toBe(false); + }); + }); + + describe("given a string containing combining marks", (): void => { + it("treats combining marks according to the configured whitespace rule", (): void => { + expect(hasText("cafe\u0301")).toBe(true); + }); + }); + + describe("given a non-string value", (): void => { + it("returns false for numbers", (): void => { + expect(hasText(0)).toBe(false); + expect(hasText(123)).toBe(false); + expect(hasText(-456)).toBe(false); + expect(hasText(Infinity)).toBe(false); + expect(hasText(NaN)).toBe(false); + }); + + it("returns false for booleans", (): void => { + expect(hasText(true)).toBe(false); + expect(hasText(false)).toBe(false); + }); + + it("returns false for bigint and symbols", (): void => { + expect(hasText(100n)).toBe(false); + expect(hasText(Symbol("text"))).toBe(false); + }); + + it("returns false for objects and arrays", (): void => { + expect(hasText({})).toBe(false); + expect(hasText({ text: "hello" })).toBe(false); + expect(hasText([])).toBe(false); + expect(hasText(["hello"])).toBe(false); + }); + + it("returns false for functions", (): void => { + expect(hasText(() => {})).toBe(false); + expect(hasText(function () {})).toBe(false); + }); + }); + + describe("given a type guard usage", (): void => { + it("narrows an unknown value to string", (): void => { + const value: unknown = "Hello"; + + if (hasText(value)) { + expectTypeOf(value).toEqualTypeOf(); + } + }); + }); + + describe("given an empty or nullish value", (): void => { + it("returns false for an empty string", (): void => { + expect(hasText("")).toBe(false); + }); + + it("returns false for null", (): void => { + expect(hasText(null)).toBe(false); + }); + + it("returns false for undefined", (): void => { + expect(hasText(undefined)).toBe(false); + }); + }); +}); From 0f8d6507eedf38adda9ca8a751e039c88bf45f08 Mon Sep 17 00:00:00 2001 From: rcpthongta <220981657+rcpthongta@users.noreply.github.com> Date: Fri, 18 Sep 2026 02:34:13 +0700 Subject: [PATCH 028/121] perf(has-text): add performance benchmarks for non-whitespace check --- src/libs/has-text/has-text.lib.bench.ts | 159 ++++++++++++++++++++++++ 1 file changed, 159 insertions(+) create mode 100644 src/libs/has-text/has-text.lib.bench.ts diff --git a/src/libs/has-text/has-text.lib.bench.ts b/src/libs/has-text/has-text.lib.bench.ts new file mode 100644 index 0000000..23610f3 --- /dev/null +++ b/src/libs/has-text/has-text.lib.bench.ts @@ -0,0 +1,159 @@ +import { bench, describe } from "vitest"; + +import { hasText } from "./has-text.lib"; + +const shortText: string = "hello"; +const longText: string = "the quick brown fox jumps over the lazy dog ".repeat(100); +const longWhitespace: string = " ".repeat(1_000); +const longWhitespaceWithTextAtEnd: string = " ".repeat(1_000) + "a"; +const longTextWithTrailingWhitespace: string = "a" + " ".repeat(1_000); +const longText1KB: string = "a".repeat(1_000); +const longText100KB: string = "a".repeat(100_000); + +describe("hasText", (): void => { + describe("given common short inputs", (): void => { + bench("non-whitespace text", (): void => { + hasText(shortText); + }); + + bench("single non-whitespace character", (): void => { + hasText("a"); + }); + + bench("single whitespace character", (): void => { + hasText(" "); + }); + + bench("whitespace-only string", (): void => { + hasText(" "); + }); + }); + + describe("given long inputs", (): void => { + bench("long text", (): void => { + hasText(longText); + }); + + bench("1 KB non-whitespace string", (): void => { + hasText(longText1KB); + }); + + bench("100 KB non-whitespace string", (): void => { + hasText(longText100KB); + }); + + bench("long whitespace-only string", (): void => { + hasText(longWhitespace); + }); + + bench("non-whitespace at the beginning", (): void => { + hasText(longTextWithTrailingWhitespace); + }); + + bench("non-whitespace at the end", (): void => { + hasText(longWhitespaceWithTextAtEnd); + }); + }); + + describe("given Unicode input", (): void => { + bench("CJK", (): void => { + hasText("你好世界"); + }); + + bench("Arabic", (): void => { + hasText("مرحبا بالعالم"); + }); + + bench("Thai", (): void => { + hasText("สวัสดีชาวโลก"); + }); + + bench("accented Latin", (): void => { + hasText("café"); + }); + + bench("combining marks", (): void => { + hasText("cafe\u0301"); + }); + + bench("emoji", (): void => { + hasText("🎉🚀🌟"); + }); + }); + + describe("given Unicode whitespace", (): void => { + bench("non-breaking spaces only", (): void => { + hasText("\u00A0\u00A0\u00A0"); + }); + + bench("ideographic spaces only", (): void => { + hasText("\u3000\u3000\u3000"); + }); + + bench("mixed Unicode whitespace only", (): void => { + hasText(" \t\n\r\u00A0\u2003\u202F\u3000 "); + }); + + bench("text surrounded by Unicode whitespace", (): void => { + hasText("\u00A0\u3000hello\u2003"); + }); + }); + + describe("given symbols and emojis", (): void => { + bench("symbol-only string", (): void => { + hasText("!@#$%^&*()"); + }); + + bench("single emoji", (): void => { + hasText("🔥"); + }); + + bench("multiple emojis", (): void => { + hasText("🎉🚀🌟"); + }); + }); + + describe("given non-string values", (): void => { + bench("number", (): void => { + hasText(12345); + }); + + bench("boolean", (): void => { + hasText(true); + }); + + bench("object", (): void => { + hasText({ length: 5 }); + }); + + bench("object with text property", (): void => { + hasText({ text: "hello" }); + }); + + bench("array", (): void => { + hasText(["hello"]); + }); + + bench("function", (): void => { + hasText(() => {}); + }); + + bench("symbol", (): void => { + hasText(Symbol("text")); + }); + }); + + describe("given an empty or nullish value", (): void => { + bench("empty string", (): void => { + hasText(""); + }); + + bench("null", (): void => { + hasText(null); + }); + + bench("undefined", (): void => { + hasText(undefined); + }); + }); +}); From f727d8e4e7e92d8e9277f4a1042426a926ad500b Mon Sep 17 00:00:00 2001 From: rcpthongta <220981657+rcpthongta@users.noreply.github.com> Date: Fri, 18 Sep 2026 02:48:00 +0700 Subject: [PATCH 029/121] chore(deps-dev): add husky, lint-staged, and commitlint tooling --- package-lock.json | 916 +++++++++++++++++++++++++++++++++++++++++++++- package.json | 4 + 2 files changed, 915 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index 758d3e0..965e78f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,6 +9,8 @@ "version": "0.0.1", "license": "MIT", "devDependencies": { + "@commitlint/cli": "^21.2.2", + "@commitlint/config-conventional": "^21.2.2", "@eslint/js": "^9.39.5", "@eslint/markdown": "^7.5.1", "@types/node": "^24.13.5", @@ -22,7 +24,9 @@ "eslint-plugin-unused-imports": "^4.4.1", "eslint-plugin-yml": "^1.19.1", "globals": "^17.12.0", + "husky": "^9.1.7", "jsonc-eslint-parser": "^2.4.2", + "lint-staged": "^17.5.1", "rimraf": "^6.1.3", "tsup": "^8.5.1", "typescript": "^5.9.3", @@ -34,6 +38,28 @@ "node": ">=22" } }, + "node_modules/@babel/code-frame": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.7.tgz", + "integrity": "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.29.7", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/code-frame/node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true, + "license": "MIT" + }, "node_modules/@babel/helper-string-parser": { "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz", @@ -94,6 +120,348 @@ "node": ">=18" } }, + "node_modules/@commitlint/cli": { + "version": "21.2.2", + "resolved": "https://registry.npmjs.org/@commitlint/cli/-/cli-21.2.2.tgz", + "integrity": "sha512-a+6hQxIxnpdvSvS2apvttPNbEliYsVC3PqFYDiiB2kjbwIsQsj1urvQ4Tkf70pKYozPalKAuRQmm/GHwndduqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@commitlint/config-conventional": "^21.2.2", + "@commitlint/format": "^21.2.2", + "@commitlint/lint": "^21.2.2", + "@commitlint/load": "^21.2.2", + "@commitlint/read": "^21.2.1", + "@commitlint/types": "^21.2.0", + "tinyexec": "^1.0.0", + "yargs": "^18.0.0" + }, + "bin": { + "commitlint": "cli.js" + }, + "engines": { + "node": ">=22.12.0" + } + }, + "node_modules/@commitlint/cli/node_modules/tinyexec": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.3.1.tgz", + "integrity": "sha512-GCvB3aoys96IuDFBMcTB46JOR6mdMtAToqwiW8JlWhsoh1mhHi/xn9ss/Dg7N555GiJyEt2qzoG/NHCwM6h1EA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/@commitlint/config-conventional": { + "version": "21.2.2", + "resolved": "https://registry.npmjs.org/@commitlint/config-conventional/-/config-conventional-21.2.2.tgz", + "integrity": "sha512-NxA37SZviusFUEYOQZ5hNnZ1h7O/KiemPkxjOlpzKJNnWxThiwc6/SaZhaPa8fyLvfRBAywhQhJJk8XESHWlpQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@commitlint/types": "^21.2.0", + "conventional-changelog-conventionalcommits": "^10.0.0" + }, + "engines": { + "node": ">=22.12.0" + } + }, + "node_modules/@commitlint/config-validator": { + "version": "21.2.0", + "resolved": "https://registry.npmjs.org/@commitlint/config-validator/-/config-validator-21.2.0.tgz", + "integrity": "sha512-t7AzNHAKeIdo/3NRGwzpufKHsKkPHmFs/56N2Fnsh0/r0rGtnQzTxk6vnFgjaGr4hdSQKNB50/KAhR9Yk4LJKA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@commitlint/types": "^21.2.0", + "ajv": "^8.11.0" + }, + "engines": { + "node": ">=22.12.0" + } + }, + "node_modules/@commitlint/config-validator/node_modules/ajv": { + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.20.0.tgz", + "integrity": "sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/@commitlint/config-validator/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true, + "license": "MIT" + }, + "node_modules/@commitlint/ensure": { + "version": "21.2.0", + "resolved": "https://registry.npmjs.org/@commitlint/ensure/-/ensure-21.2.0.tgz", + "integrity": "sha512-76IF9vDNS13lAzEEik9eKwzt8f9hYhWiwVXZ2AnyLCz5/f511FsEQ3pw1X3/zSQpdRLQU7i5qDMVKyXi1GWjSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@commitlint/types": "^21.2.0", + "es-toolkit": "^1.46.0" + }, + "engines": { + "node": ">=22.12.0" + } + }, + "node_modules/@commitlint/execute-rule": { + "version": "21.0.1", + "resolved": "https://registry.npmjs.org/@commitlint/execute-rule/-/execute-rule-21.0.1.tgz", + "integrity": "sha512-RifH+FmImozKBE6mozhF4K3r2RRKP7SMi/Q/zLCmExtp5e05lhHOUYqGBlFBAGNHaZxU/WYw1XuugYK9jQzqnA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=22.12.0" + } + }, + "node_modules/@commitlint/format": { + "version": "21.2.2", + "resolved": "https://registry.npmjs.org/@commitlint/format/-/format-21.2.2.tgz", + "integrity": "sha512-v6fvxZSc/AvVMROlr3H34+1766bZSYApRUSCAMjWamStPjKMvZ8GdvVA5YW/VQNgbFTmcMz6OYmSTJEvIjPrfA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@commitlint/types": "^21.2.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=22.12.0" + } + }, + "node_modules/@commitlint/is-ignored": { + "version": "21.2.2", + "resolved": "https://registry.npmjs.org/@commitlint/is-ignored/-/is-ignored-21.2.2.tgz", + "integrity": "sha512-9UoKNgfFE3LU7FrzierCvk3CdDfMDeVGC86qZiT/n0TIjfq/dmZ9MHuXd45OTNRa26ZanmJRxEtmiXk/lEJihg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@commitlint/types": "^21.2.0", + "semver": "^7.6.0" + }, + "engines": { + "node": ">=22.12.0" + } + }, + "node_modules/@commitlint/lint": { + "version": "21.2.2", + "resolved": "https://registry.npmjs.org/@commitlint/lint/-/lint-21.2.2.tgz", + "integrity": "sha512-Fy8JxEBzdmsYWFude/61GxXu5O+wEymwiRK2z9GL9R8mCsXphCoGxAFc5iHn5mjlfcSrhiiONE+ksf4KOjnaPg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@commitlint/is-ignored": "^21.2.2", + "@commitlint/parse": "^21.2.2", + "@commitlint/rules": "^21.2.2", + "@commitlint/types": "^21.2.0" + }, + "engines": { + "node": ">=22.12.0" + } + }, + "node_modules/@commitlint/load": { + "version": "21.2.2", + "resolved": "https://registry.npmjs.org/@commitlint/load/-/load-21.2.2.tgz", + "integrity": "sha512-0Tt6wDPX167cjKC5D4zhm0+20wJJG+TN/TKovMOspfSe78rOnKX+MNzlVNiu6HyQPZChPJ8QBH31MVt6Bb8fCg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@commitlint/config-validator": "^21.2.0", + "@commitlint/execute-rule": "^21.0.1", + "@commitlint/resolve-extends": "^21.2.2", + "@commitlint/types": "^21.2.0", + "cosmiconfig": "^9.0.1", + "cosmiconfig-typescript-loader": "^6.1.0", + "es-toolkit": "^1.46.0", + "is-plain-obj": "^4.1.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=22.12.0" + } + }, + "node_modules/@commitlint/message": { + "version": "21.2.0", + "resolved": "https://registry.npmjs.org/@commitlint/message/-/message-21.2.0.tgz", + "integrity": "sha512-YxGoiXD/HXNXLJPrQwE5poXa+XH0CBEm+mdvbHQP0g6MV/dmJyUFCzPNzZbxL93GvZ70TmtTK0Z0/IBpAqHv8g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=22.12.0" + } + }, + "node_modules/@commitlint/parse": { + "version": "21.2.2", + "resolved": "https://registry.npmjs.org/@commitlint/parse/-/parse-21.2.2.tgz", + "integrity": "sha512-MEkobPfvRp+z06Wro8HMG1BDGHzZmj82A1LH1nWeG3ipHpg/x4m6v3wEDvMBIKjRFUnfR3nBeFs3MVCr7UdAmg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@commitlint/types": "^21.2.0", + "conventional-changelog-angular": "^9.0.0", + "conventional-commits-parser": "^7.0.0" + }, + "engines": { + "node": ">=22.12.0" + } + }, + "node_modules/@commitlint/read": { + "version": "21.2.1", + "resolved": "https://registry.npmjs.org/@commitlint/read/-/read-21.2.1.tgz", + "integrity": "sha512-hUW7EJQnNTL0vPOmVMNK4CrnrNBN0nN+JJHReFkdHO5y4iyHeEmTBwuC15OCqUTjxWo7idnH1LftfpWVIaPWIA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@commitlint/top-level": "^21.2.0", + "@commitlint/types": "^21.2.0", + "@conventional-changelog/git-client": "^3.0.0", + "tinyexec": "^1.0.0" + }, + "engines": { + "node": ">=22.12.0" + } + }, + "node_modules/@commitlint/read/node_modules/tinyexec": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.3.1.tgz", + "integrity": "sha512-GCvB3aoys96IuDFBMcTB46JOR6mdMtAToqwiW8JlWhsoh1mhHi/xn9ss/Dg7N555GiJyEt2qzoG/NHCwM6h1EA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/@commitlint/resolve-extends": { + "version": "21.2.2", + "resolved": "https://registry.npmjs.org/@commitlint/resolve-extends/-/resolve-extends-21.2.2.tgz", + "integrity": "sha512-RPkJ/IFi7sMUUVbZLqwWFtWw/zRDcfFsmrPSiTMrt5wb7AdxOr86EGQFvmGzef5QKV5IPBWWCujqVTw1RWX44A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@commitlint/config-validator": "^21.2.0", + "@commitlint/types": "^21.2.0", + "es-toolkit": "^1.46.0", + "global-directory": "^5.0.0", + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=22.12.0" + } + }, + "node_modules/@commitlint/resolve-extends/node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@commitlint/rules": { + "version": "21.2.2", + "resolved": "https://registry.npmjs.org/@commitlint/rules/-/rules-21.2.2.tgz", + "integrity": "sha512-eplQzyYkBjYB1HyyRj8hkcK11Y9DU9nuBz7uOKEd6NpE9NGDytLFCAnlRE+OoiK/5sHEJsaz2RGhuWBvYzIbNA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@commitlint/ensure": "^21.2.0", + "@commitlint/message": "^21.2.0", + "@commitlint/to-lines": "^21.0.1", + "@commitlint/types": "^21.2.0" + }, + "engines": { + "node": ">=22.12.0" + } + }, + "node_modules/@commitlint/to-lines": { + "version": "21.0.1", + "resolved": "https://registry.npmjs.org/@commitlint/to-lines/-/to-lines-21.0.1.tgz", + "integrity": "sha512-bd1BFII7p1EQZre9Kaj+kKaMFP3cFCdt21K7DItVux9XP5WjLgJ0/Uy1pJJh9aPwVJ6SKg62PxqlZaHI8hQAXw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=22.12.0" + } + }, + "node_modules/@commitlint/top-level": { + "version": "21.2.0", + "resolved": "https://registry.npmjs.org/@commitlint/top-level/-/top-level-21.2.0.tgz", + "integrity": "sha512-Y5gmQ+KxzqCrBFJfLvFEPvvwD3LDiNZoTT2yeFBm96M8qhmqSzQc5DvX3rheAaAMjyIvMXOCLS/mWfdpONsjyQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0" + }, + "engines": { + "node": ">=22.12.0" + } + }, + "node_modules/@commitlint/types": { + "version": "21.2.0", + "resolved": "https://registry.npmjs.org/@commitlint/types/-/types-21.2.0.tgz", + "integrity": "sha512-7zVFCDB2reMvJH5dmbKnOQPjZEvjdJTH8jc0U/PIPU1r3/+vf5pD1HlfitV2MWsWXrvu7u39iY1lyLUPOaN0Gw==", + "dev": true, + "license": "MIT", + "dependencies": { + "conventional-commits-parser": "^7.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=22.12.0" + } + }, + "node_modules/@conventional-changelog/git-client": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@conventional-changelog/git-client/-/git-client-3.1.2.tgz", + "integrity": "sha512-jZqwnJwf7nboIlAcw/mkOjVa6DexCcUOgT2oOQgkoi3z9vR8tGFkcMy2BFcYwjhL9sYcDDXkRQDayiDieCoW7A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@simple-libs/child-process-utils": "^2.0.0", + "@simple-libs/stream-utils": "^2.0.0", + "semver": "^7.5.2" + }, + "engines": { + "node": ">=22" + }, + "peerDependencies": { + "conventional-commits-filter": "^6.0.1", + "conventional-commits-parser": "^7.1.2" + }, + "peerDependenciesMeta": { + "conventional-commits-filter": { + "optional": true + }, + "conventional-commits-parser": { + "optional": true + } + } + }, + "node_modules/@conventional-changelog/template": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@conventional-changelog/template/-/template-1.4.0.tgz", + "integrity": "sha512-aalGyl7dbB5PArRebDIX43ZvBlXrYm9uWzGJ26t+4SzJVPsOuvfILGGbw5X4yX7i50YEmJ8zvbiWnqH/AAnZqg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=22" + } + }, "node_modules/@esbuild/aix-ppc64": { "version": "0.27.7", "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.7.tgz", @@ -1534,6 +1902,35 @@ "win32" ] }, + "node_modules/@simple-libs/child-process-utils": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@simple-libs/child-process-utils/-/child-process-utils-2.0.0.tgz", + "integrity": "sha512-dvNoRKLijXnD0XoJAz94pbNuB5GQgDr55UhpSPhffDkTT0Cmcqh9jSCOtwfT2d4H6MI9E7c4SgtMuJXZ6F3c6A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@simple-libs/stream-utils": "^2.0.0" + }, + "engines": { + "node": ">=22" + }, + "funding": { + "url": "https://ko-fi.com/dangreen" + } + }, + "node_modules/@simple-libs/stream-utils": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@simple-libs/stream-utils/-/stream-utils-2.0.0.tgz", + "integrity": "sha512-fCTuZK4QBa+39Oz9l4OGfJfz+GpwCp3AqO7Zch3to99xHPgstVsRFpeQ8LNd2o1Gv8raL2mCFwiaHh7bFSp5DQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=22" + }, + "funding": { + "url": "https://ko-fi.com/dangreen" + } + }, "node_modules/@sonarsource/analyzer-commons-configurations": { "version": "2.31.0-5284", "resolved": "https://registry.npmjs.org/@sonarsource/analyzer-commons-configurations/-/analyzer-commons-configurations-2.31.0-5284.tgz", @@ -2090,6 +2487,19 @@ "url": "https://github.com/sponsors/epoberezkin" } }, + "node_modules/ansi-regex": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.3.0.tgz", + "integrity": "sha512-WpDfL7NO6j7tH88IDBNVdUJxDh9nmCteAVW9dsep846XdwF4naCBK+/tGLX3KJgcpgMRXCFlTM2hKGoK9FsdrQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, "node_modules/ansi-styles": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", @@ -2120,6 +2530,19 @@ "dev": true, "license": "Python-2.0" }, + "node_modules/argue-cli": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/argue-cli/-/argue-cli-3.2.0.tgz", + "integrity": "sha512-VipTB0gXgGIFO2Rg9yEVN5wLt2AurJcZqDbgmYSwPwsykhLrQhs240/bfceev4w68lI8JshoOJIk9uW7tFzROw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=22" + }, + "funding": { + "url": "https://ko-fi.com/dangreen" + } + }, "node_modules/assertion-error": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", @@ -2284,6 +2707,39 @@ "url": "https://paulmillr.com/funding/" } }, + "node_modules/cliui": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-9.0.1.tgz", + "integrity": "sha512-k7ndgKhwoQveBL+/1tqGJYNz097I7WOvwbmmU2AR5+magtbjPWQTS1C5vzGkBC8Ym8UWRzfKUzUUqFLypY4Q+w==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^7.2.0", + "strip-ansi": "^7.1.0", + "wrap-ansi": "^9.0.0" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/cliui/node_modules/string-width": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", + "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -2328,14 +2784,57 @@ "dev": true, "license": "MIT" }, - "node_modules/consola": { - "version": "3.4.2", - "resolved": "https://registry.npmjs.org/consola/-/consola-3.4.2.tgz", - "integrity": "sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==", + "node_modules/consola": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/consola/-/consola-3.4.2.tgz", + "integrity": "sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.18.0 || >=16.10.0" + } + }, + "node_modules/conventional-changelog-angular": { + "version": "9.4.0", + "resolved": "https://registry.npmjs.org/conventional-changelog-angular/-/conventional-changelog-angular-9.4.0.tgz", + "integrity": "sha512-HdxRxuS8bBXVIuo4V82gvSwAXT0vYQUizrjs/izmPg5JdDstr8v8I5hduGL3iQbG+o310dUDxC4+LetuS5hu9w==", + "dev": true, + "license": "ISC", + "dependencies": { + "@conventional-changelog/template": "^1.4.0" + }, + "engines": { + "node": ">=22" + } + }, + "node_modules/conventional-changelog-conventionalcommits": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/conventional-changelog-conventionalcommits/-/conventional-changelog-conventionalcommits-10.4.0.tgz", + "integrity": "sha512-Rriac6ZrAlVm6cy9Bz4NSp+WMHpwNXoPIYex+HjCgduAVUSbnew29DQjQw0C4g9u3HtSYzGiGY+pdBXAZo+4aA==", + "dev": true, + "license": "ISC", + "dependencies": { + "@conventional-changelog/template": "^1.4.0" + }, + "engines": { + "node": ">=22" + } + }, + "node_modules/conventional-commits-parser": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/conventional-commits-parser/-/conventional-commits-parser-7.1.2.tgz", + "integrity": "sha512-O+x4N2yH+ijvqWlIyTHsXTAP+algNWgGbjY2duCe8w2vUMvUB95cLRslCPfTMQyLAKlet3bhZTdu6ozn4M+QJQ==", "dev": true, "license": "MIT", + "dependencies": { + "@simple-libs/stream-utils": "^2.0.0", + "argue-cli": "^3.1.0" + }, + "bin": { + "conventional-commits-parser": "dist/cli/index.js" + }, "engines": { - "node": "^14.18.0 || >=16.10.0" + "node": ">=22" } }, "node_modules/convert-source-map": { @@ -2345,6 +2844,51 @@ "dev": true, "license": "MIT" }, + "node_modules/cosmiconfig": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-9.0.2.tgz", + "integrity": "sha512-gtTZxTDau1wL7Y7zifc2dd8jHSK/k6BTx/2Xp/BpdlAdnlYWFVt7qhJqgwi7637yRwRQ3qL4ZidbB4I8tA5VOg==", + "dev": true, + "license": "MIT", + "dependencies": { + "env-paths": "^2.2.1", + "import-fresh": "^3.3.0", + "js-yaml": "^4.1.0", + "parse-json": "^5.2.0" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/d-fischer" + }, + "peerDependencies": { + "typescript": ">=4.9.5" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/cosmiconfig-typescript-loader": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/cosmiconfig-typescript-loader/-/cosmiconfig-typescript-loader-6.3.0.tgz", + "integrity": "sha512-Akr82WH1Wfqatyiqpj8HDkO2o2KmJRu1FhKfSNJP3K4IdXwHfEyL7MOb62i1AGQVLtIQM+iCE9CGOtrfhR+mmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "jiti": "2.6.1" + }, + "engines": { + "node": ">=v18" + }, + "peerDependencies": { + "@types/node": "*", + "cosmiconfig": ">=9", + "typescript": ">=5" + } + }, "node_modules/cross-spawn": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", @@ -2443,6 +2987,33 @@ "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" } }, + "node_modules/emoji-regex": { + "version": "10.6.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.6.0.tgz", + "integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==", + "dev": true, + "license": "MIT" + }, + "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==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/error-ex": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.4.tgz", + "integrity": "sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, "node_modules/es-module-lexer": { "version": "2.3.2", "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.3.2.tgz", @@ -2450,6 +3021,19 @@ "dev": true, "license": "MIT" }, + "node_modules/es-toolkit": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/es-toolkit/-/es-toolkit-1.52.0.tgz", + "integrity": "sha512-XTNEJQh1tY1ZJVcf6ayP/2n4ZPyaHlW2FWs7xvw5ddPuhUVjLD3olQVQS7kf58JbAB48iL0uL/jerTrjtV3lDA==", + "dev": true, + "license": "MIT", + "workspaces": [ + "docs", + "benchmarks", + "tests/types", + "tests/browser-compat" + ] + }, "node_modules/esbuild": { "version": "0.27.7", "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.7.tgz", @@ -2492,6 +3076,16 @@ "@esbuild/win32-x64": "0.27.7" } }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/escape-string-regexp": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", @@ -2944,6 +3538,23 @@ "dev": true, "license": "MIT" }, + "node_modules/fast-uri": { + "version": "3.1.8", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.8.tgz", + "integrity": "sha512-GZMtZUTNRpOVIECoXwLNZS5xUGE+mVNbTB8h/7Rwh2TFWcBQiPzTgyZi05BF9UMZKkLJv8XBRJTlU7zg8+ZfMg==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "BSD-3-Clause" + }, "node_modules/fault": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/fault/-/fault-2.0.1.tgz", @@ -3070,6 +3681,29 @@ "dev": true, "license": "MIT" }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-east-asian-width": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.7.0.tgz", + "integrity": "sha512-XjH1AECxf0giL2V1aU8vKyRR2ppRUb5c0EvT7zuJTokQ74bNo52zOtghqdWIqrhUD79fo3x0WfKZdOqxF6LG1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/github-slugger": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/github-slugger/-/github-slugger-2.0.0.tgz", @@ -3147,6 +3781,22 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/global-directory": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/global-directory/-/global-directory-5.0.0.tgz", + "integrity": "sha512-1pgFdhK3J2LeM+dVf2Pd424yHx2ou338lC0ErNP2hPx4j8eW1Sp0XqSjNxtk6Tc4Kr5wlWtSvz8cn2yb7/SG/w==", + "dev": true, + "license": "MIT", + "dependencies": { + "ini": "6.0.0" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/globals": { "version": "17.12.0", "resolved": "https://registry.npmjs.org/globals/-/globals-17.12.0.tgz", @@ -3184,6 +3834,22 @@ "dev": true, "license": "MIT" }, + "node_modules/husky": { + "version": "9.1.7", + "resolved": "https://registry.npmjs.org/husky/-/husky-9.1.7.tgz", + "integrity": "sha512-5gs5ytaNjBrh5Ow3zrvdUUY+0VxIuWVL4i9irt6friV+BqdCfmV11CQTWMiBYWHbXhco+J1kHfTOUkePhCDvMA==", + "dev": true, + "license": "MIT", + "bin": { + "husky": "bin.js" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/typicode" + } + }, "node_modules/ignore": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", @@ -3221,6 +3887,23 @@ "node": ">=0.8.19" } }, + "node_modules/ini": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/ini/-/ini-6.0.0.tgz", + "integrity": "sha512-IBTdIkzZNOpqm7q3dRqJvMaldXjDHWkEDfrwGEQTs5eaQMWV+djAhR+wahyNNMAa+qpbDUhBMVt4ZKNwpPm7xQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "dev": true, + "license": "MIT" + }, "node_modules/is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", @@ -3244,6 +3927,19 @@ "node": ">=0.10.0" } }, + "node_modules/is-plain-obj": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz", + "integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", @@ -3290,6 +3986,16 @@ "node": ">=8" } }, + "node_modules/jiti": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.6.1.tgz", + "integrity": "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==", + "dev": true, + "license": "MIT", + "bin": { + "jiti": "lib/jiti-cli.mjs" + } + }, "node_modules/joycon": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/joycon/-/joycon-3.1.1.tgz", @@ -3337,6 +4043,13 @@ "dev": true, "license": "MIT" }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true, + "license": "MIT" + }, "node_modules/json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", @@ -3728,6 +4441,40 @@ "dev": true, "license": "MIT" }, + "node_modules/lint-staged": { + "version": "17.5.1", + "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-17.5.1.tgz", + "integrity": "sha512-7EDuco1xnBMeVpvAbeMq1U5KXwJGLmY8q6l+Ye78r36C4mPc+Vg1Z2SK8gHyRTyvPro90A0q5/FAZ+Au23d6QQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "picomatch": "^4.0.7", + "string-argv": "^0.3.2", + "tinyexec": "^1.3.1" + }, + "bin": { + "lint-staged": "bin/lint-staged.js" + }, + "engines": { + "node": ">=22.22.1" + }, + "funding": { + "url": "https://opencollective.com/lint-staged" + }, + "optionalDependencies": { + "yaml": "^2.9.0" + } + }, + "node_modules/lint-staged/node_modules/tinyexec": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.3.1.tgz", + "integrity": "sha512-GCvB3aoys96IuDFBMcTB46JOR6mdMtAToqwiW8JlWhsoh1mhHi/xn9ss/Dg7N555GiJyEt2qzoG/NHCwM6h1EA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, "node_modules/load-tsconfig": { "version": "0.2.5", "resolved": "https://registry.npmjs.org/load-tsconfig/-/load-tsconfig-0.2.5.tgz", @@ -4859,6 +5606,25 @@ "node": ">=6" } }, + "node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "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", @@ -5108,6 +5874,16 @@ "node": "^12.0.0 || ^14.0.0 || >=16.0.0" } }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/resolve-from": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", @@ -5310,6 +6086,49 @@ "dev": true, "license": "MIT" }, + "node_modules/string-argv": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.2.tgz", + "integrity": "sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.6.19" + } + }, + "node_modules/string-width": { + "version": "8.2.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-8.2.2.tgz", + "integrity": "sha512-GaPUh5gfdrYzqeVNZvUfT23vYYxXzKYidUcnMtJg/3rxRV63EFZy3k6xfKlmfeJD0176lnUV/Usr3XcwSvFzpg==", + "dev": true, + "license": "MIT", + "dependencies": { + "get-east-asian-width": "^1.5.0", + "strip-ansi": "^7.1.2" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/strip-ansi": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", + "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.2.2" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, "node_modules/strip-json-comments": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", @@ -5887,6 +6706,65 @@ "node": ">=0.10.0" } }, + "node_modules/wrap-ansi": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.2.tgz", + "integrity": "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.2.1", + "string-width": "^7.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/ansi-styles": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/string-width": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", + "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, "node_modules/yaml": { "version": "2.9.1", "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.9.1.tgz", @@ -5933,6 +6811,34 @@ "url": "https://opencollective.com/eslint" } }, + "node_modules/yargs": { + "version": "18.1.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-18.1.0.tgz", + "integrity": "sha512-2rAgRKu54VsHkqI0/tYkmluGXHD4KW7yZoycuqDQ15QOTnc2VVfy0nN/1eMhnQLO00A+dwtK20xuCnc1YGeUyg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cliui": "^9.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "string-width": "^8.2.1", + "y18n": "^5.0.5", + "yargs-parser": "^22.0.0" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=23" + } + }, + "node_modules/yargs-parser": { + "version": "22.0.0", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-22.0.0.tgz", + "integrity": "sha512-rwu/ClNdSMpkSrUb+d6BRsSkLUq1fmfsY6TOpYzTwvwkg1/NRG85KBy3kq++A8LKQwX6lsu+aWad+2khvuXrqw==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=23" + } + }, "node_modules/yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", diff --git a/package.json b/package.json index e30b550..c784ea8 100644 --- a/package.json +++ b/package.json @@ -48,6 +48,8 @@ "lint:fix": "eslint --fix **/*.{*rc,cjs,js,json,json5,jsonc,jsonl,md,mjs,ts,yaml,yml}" }, "devDependencies": { + "@commitlint/cli": "^21.2.2", + "@commitlint/config-conventional": "^21.2.2", "@eslint/js": "^9.39.5", "@eslint/markdown": "^7.5.1", "@types/node": "^24.13.5", @@ -61,7 +63,9 @@ "eslint-plugin-unused-imports": "^4.4.1", "eslint-plugin-yml": "^1.19.1", "globals": "^17.12.0", + "husky": "^9.1.7", "jsonc-eslint-parser": "^2.4.2", + "lint-staged": "^17.5.1", "rimraf": "^6.1.3", "tsup": "^8.5.1", "typescript": "^5.9.3", From 1d7098b600571f77f66290ab79e2fd7e5d705835 Mon Sep 17 00:00:00 2001 From: rcpthongta <220981657+rcpthongta@users.noreply.github.com> Date: Fri, 18 Sep 2026 02:50:04 +0700 Subject: [PATCH 030/121] chore(git-hooks): configure husky hooks, commitlint, and lint-staged --- .husky/commit-msg | 1 + .husky/pre-commit | 1 + .husky/pre-push | 1 + commitlint.config.mjs | 10 ++++++++++ lint-staged.config.mjs | 5 +++++ 5 files changed, 18 insertions(+) create mode 100644 .husky/commit-msg create mode 100644 .husky/pre-commit create mode 100644 .husky/pre-push create mode 100644 commitlint.config.mjs create mode 100644 lint-staged.config.mjs diff --git a/.husky/commit-msg b/.husky/commit-msg new file mode 100644 index 0000000..da99483 --- /dev/null +++ b/.husky/commit-msg @@ -0,0 +1 @@ +npx --no -- commitlint --edit "$1" diff --git a/.husky/pre-commit b/.husky/pre-commit new file mode 100644 index 0000000..f27575a --- /dev/null +++ b/.husky/pre-commit @@ -0,0 +1 @@ +npm run precommit diff --git a/.husky/pre-push b/.husky/pre-push new file mode 100644 index 0000000..c9272ed --- /dev/null +++ b/.husky/pre-push @@ -0,0 +1 @@ +npm run prepush diff --git a/commitlint.config.mjs b/commitlint.config.mjs new file mode 100644 index 0000000..48c34d5 --- /dev/null +++ b/commitlint.config.mjs @@ -0,0 +1,10 @@ +import conventionalConfig from "@commitlint/config-conventional"; + +const configuration = { + extends: ["@commitlint/config-conventional"], + rules: { + "type-enum": [2, "always", [...conventionalConfig.rules["type-enum"][2], "merge"]] + } +}; + +export default configuration; diff --git a/lint-staged.config.mjs b/lint-staged.config.mjs new file mode 100644 index 0000000..5a9f317 --- /dev/null +++ b/lint-staged.config.mjs @@ -0,0 +1,5 @@ +const configuration = { + "**/*.{*rc,cjs,js,json,json5,jsonc,jsonl,md,mjs,ts,yaml,yml}": ["eslint --fix"] +}; + +export default configuration; From a6960f15ae4537fd6a767529ada0740b73c937f8 Mon Sep 17 00:00:00 2001 From: rcpthongta <220981657+rcpthongta@users.noreply.github.com> Date: Fri, 18 Sep 2026 02:50:36 +0700 Subject: [PATCH 031/121] chore(scripts): add git hook helper scripts --- package.json | 3 +++ 1 file changed, 3 insertions(+) diff --git a/package.json b/package.json index c784ea8..c6283bd 100644 --- a/package.json +++ b/package.json @@ -36,6 +36,9 @@ "node": ">=22" }, "scripts": { + "install:husky": "husky", + "precommit": "lint-staged", + "prepush": "npm run test:unit", "build": "npm run build:clean && npm run build:js && npm run build:types", "build:clean": "rimraf dist", "build:js": "tsup", From bc0aa14bae1993a9930fc1b2c9083674f358573f Mon Sep 17 00:00:00 2001 From: rcpthongta <220981657+rcpthongta@users.noreply.github.com> Date: Fri, 18 Sep 2026 02:54:45 +0700 Subject: [PATCH 032/121] chore(scripts): update prepush script to run test and build --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index c6283bd..7e06476 100644 --- a/package.json +++ b/package.json @@ -38,7 +38,7 @@ "scripts": { "install:husky": "husky", "precommit": "lint-staged", - "prepush": "npm run test:unit", + "prepush": "npm run test && npm run build", "build": "npm run build:clean && npm run build:js && npm run build:types", "build:clean": "rimraf dist", "build:js": "tsup", From 9da22bea95d793939623e6b3feaf70bd8fbd48d5 Mon Sep 17 00:00:00 2001 From: rcpthongta <220981657+rcpthongta@users.noreply.github.com> Date: Fri, 18 Sep 2026 03:04:05 +0700 Subject: [PATCH 033/121] ci(github): add github workflows for linting and test coverage --- .github/workflows/ci.yml | 17 +++++++++++++++++ .github/workflows/linter.yml | 21 +++++++++++++++++++++ .github/workflows/test.yml | 21 +++++++++++++++++++++ 3 files changed, 59 insertions(+) create mode 100644 .github/workflows/ci.yml create mode 100644 .github/workflows/linter.yml create mode 100644 .github/workflows/test.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..76c1193 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,17 @@ +name: Continuous Integration +on: + push: + branches: + - develop + - main + pull_request: + branches: + - main +permissions: + contents: read +jobs: + lint: + uses: ./.github/workflows/linter.yml + test: + needs: lint + uses: ./.github/workflows/test.yml diff --git a/.github/workflows/linter.yml b/.github/workflows/linter.yml new file mode 100644 index 0000000..7f1e7cc --- /dev/null +++ b/.github/workflows/linter.yml @@ -0,0 +1,21 @@ +name: Linter +on: + workflow_call: {} +permissions: + contents: read +jobs: + lint: + runs-on: ubuntu-latest + timeout-minutes: 10 + steps: + - name: Checkout repository + uses: actions/checkout@v4 + - name: Set up Node.js + uses: actions/setup-node@v4 + with: + node-version: 22 + cache: npm + - name: Install dependencies + run: npm ci + - name: Run script command + run: npm run lint diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..f933b34 --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,21 @@ +name: Test +on: + workflow_call: {} +permissions: + contents: read +jobs: + test: + runs-on: ubuntu-latest + timeout-minutes: 10 + steps: + - name: Checkout repository + uses: actions/checkout@v4 + - name: Set up Node.js + uses: actions/setup-node@v4 + with: + node-version: 22 + cache: npm + - name: Install dependencies + run: npm ci + - name: Run script command + run: npm run test:coverage From ff72cf7f8c1a4b0910162b3e30426499440177ad Mon Sep 17 00:00:00 2001 From: rcpthongta <220981657+rcpthongta@users.noreply.github.com> Date: Mon, 21 Sep 2026 22:16:12 +0700 Subject: [PATCH 034/121] chore(deps-dev): upgrade vitest and coverage to v5.0.1 --- package-lock.json | 334 ++++++++++++++++++---------------------------- package.json | 4 +- 2 files changed, 129 insertions(+), 209 deletions(-) diff --git a/package-lock.json b/package-lock.json index 965e78f..e40e99a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,7 +14,7 @@ "@eslint/js": "^9.39.5", "@eslint/markdown": "^7.5.1", "@types/node": "^24.13.5", - "@vitest/coverage-v8": "^4.1.10", + "@vitest/coverage-v8": "^5.0.1", "eslint": "^9.39.5", "eslint-config-prettier": "^10.1.8", "eslint-plugin-jsonc": "^2.21.1", @@ -31,7 +31,7 @@ "tsup": "^8.5.1", "typescript": "^5.9.3", "typescript-eslint": "^8.65.0", - "vitest": "^4.1.10", + "vitest": "^5.0.1", "yaml-eslint-parser": "^1.3.2" }, "engines": { @@ -1216,6 +1216,7 @@ "integrity": "sha512-rDS5/31E9HfPl/CIzGrn0DOlvBbXFseQ5URJ9sYMfstbKLD/c6Gm9vmRzRGDdAXyOIL4zmO37lc9RIwYqVruZw==", "dev": true, "license": "MIT", + "peer": true, "funding": { "url": "https://github.com/sponsors/oxc-project" } @@ -1246,6 +1247,7 @@ "os": [ "android" ], + "peer": true, "engines": { "node": "^20.19.0 || >=22.12.0" } @@ -1263,6 +1265,7 @@ "os": [ "android" ], + "peer": true, "engines": { "node": "^20.19.0 || >=22.12.0" } @@ -1280,6 +1283,7 @@ "os": [ "darwin" ], + "peer": true, "engines": { "node": "^20.19.0 || >=22.12.0" } @@ -1297,6 +1301,7 @@ "os": [ "darwin" ], + "peer": true, "engines": { "node": "^20.19.0 || >=22.12.0" } @@ -1314,6 +1319,7 @@ "os": [ "freebsd" ], + "peer": true, "engines": { "node": "^20.19.0 || >=22.12.0" } @@ -1331,6 +1337,7 @@ "os": [ "linux" ], + "peer": true, "engines": { "node": "^20.19.0 || >=22.12.0" } @@ -1351,6 +1358,7 @@ "os": [ "linux" ], + "peer": true, "engines": { "node": "^20.19.0 || >=22.12.0" } @@ -1371,6 +1379,7 @@ "os": [ "linux" ], + "peer": true, "engines": { "node": "^20.19.0 || >=22.12.0" } @@ -1391,6 +1400,7 @@ "os": [ "linux" ], + "peer": true, "engines": { "node": "^20.19.0 || >=22.12.0" } @@ -1411,6 +1421,7 @@ "os": [ "linux" ], + "peer": true, "engines": { "node": "^20.19.0 || >=22.12.0" } @@ -1431,6 +1442,7 @@ "os": [ "linux" ], + "peer": true, "engines": { "node": "^20.19.0 || >=22.12.0" } @@ -1451,6 +1463,7 @@ "os": [ "linux" ], + "peer": true, "engines": { "node": "^20.19.0 || >=22.12.0" } @@ -1468,6 +1481,7 @@ "os": [ "openharmony" ], + "peer": true, "engines": { "node": "^20.19.0 || >=22.12.0" } @@ -1485,6 +1499,7 @@ "os": [ "win32" ], + "peer": true, "engines": { "node": "^20.19.0 || >=22.12.0" } @@ -1502,6 +1517,7 @@ "os": [ "win32" ], + "peer": true, "engines": { "node": "^20.19.0 || >=22.12.0" } @@ -1511,7 +1527,8 @@ "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.1.tgz", "integrity": "sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==", "dev": true, - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/@rollup/rollup-android-arm-eabi": { "version": "4.63.3", @@ -1938,13 +1955,6 @@ "dev": true, "license": "LGPL-3.0-only" }, - "node_modules/@standard-schema/spec": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz", - "integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==", - "dev": true, - "license": "MIT" - }, "node_modules/@types/chai": { "version": "5.2.3", "resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.2.3.tgz", @@ -2304,29 +2314,27 @@ } }, "node_modules/@vitest/coverage-v8": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-4.1.11.tgz", - "integrity": "sha512-8MVGEFnJIcdGjcbfKmeq8z0pZHH0JlVtoVZH9Q/qwUp6wyFnEJUBMrw9DCaj+ra3vShGmhavjalMIhPNxZAUcw==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-5.0.1.tgz", + "integrity": "sha512-FRC8ACiudC3dI6MTplzRSYWHDRnIv2IPfbzs4FdoJNsMal/35sWV8hwIfV8ZcqzSPy+uXHeMVONt9CEqtOU17w==", "dev": true, "license": "MIT", "dependencies": { "@bcoe/v8-coverage": "^1.0.2", - "@vitest/utils": "4.1.11", - "ast-v8-to-istanbul": "^1.0.0", - "istanbul-lib-coverage": "^3.2.2", - "istanbul-lib-report": "^3.0.1", - "istanbul-reports": "^3.2.0", - "magicast": "^0.5.2", - "obug": "^2.1.1", - "std-env": "^4.0.0-rc.1", - "tinyrainbow": "^3.1.0" + "@vitest/istanbul-lib-coverage": "^1.0.0", + "@vitest/istanbul-lib-report": "^1.0.0", + "ast-v8-to-istanbul": "^1.0.5", + "magicast": "^0.5.4", + "obug": "^2.1.4", + "std-env": "^4.2.0", + "tinyrainbow": "^3.1.1" }, "funding": { "url": "https://opencollective.com/vitest" }, "peerDependencies": { - "@vitest/browser": "4.1.11", - "vitest": "4.1.11" + "@vitest/browser": "5.0.1", + "vitest": "5.0.1" }, "peerDependenciesMeta": { "@vitest/browser": { @@ -2334,34 +2342,40 @@ } } }, - "node_modules/@vitest/expect": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.1.11.tgz", - "integrity": "sha512-VX2x5vNJXET47KAFzwERI+KRMtTTCSWTfSMKsW7JsUsXV4psq++e3DvZpuTDOpHcxytiDs6p2nhVb2tVDiiUYw==", + "node_modules/@vitest/istanbul-lib-coverage": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@vitest/istanbul-lib-coverage/-/istanbul-lib-coverage-1.0.1.tgz", + "integrity": "sha512-k3DJZ8LhMBK9NS4SclF1ASD3OgXEWDorbIcPTRDK0/Zae6fRvu+fJRxtFdLfHsa9Y24beCdPnoNZ4LviTNstfA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=22" + } + }, + "node_modules/@vitest/istanbul-lib-report": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@vitest/istanbul-lib-report/-/istanbul-lib-report-1.0.1.tgz", + "integrity": "sha512-1EOLRfsTMnyAr3+kEAsP4o9dhaDlGPpD7H5iLBBeq//YpNB1VIahkPhB+eRp9N2Dkfw8oySROjE3yf9XDeaIkQ==", "dev": true, "license": "MIT", "dependencies": { - "@standard-schema/spec": "^1.1.0", - "@types/chai": "^5.2.2", - "@vitest/spy": "4.1.11", - "@vitest/utils": "4.1.11", - "chai": "^6.2.2", - "tinyrainbow": "^3.1.0" + "@vitest/istanbul-lib-coverage": "1.0.1" }, - "funding": { - "url": "https://opencollective.com/vitest" + "engines": { + "node": ">=22" } }, "node_modules/@vitest/mocker": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.1.11.tgz", - "integrity": "sha512-2XJVD55d1o5AZous5CCGKS74g/riOj9odEt2bQpCVZeblHyHdnMeFl4jl0XjU21stf4mbjUkew2eXQZt65g5CQ==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-5.0.1.tgz", + "integrity": "sha512-6K1DoBNAPGvuOcSsGA4D6x+5zEEff/KmOOP3uetT2TrGpVfI+HRHRnJJfKi5ib/g1vx8IYHQD8s0pbJz8WQI7Q==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/spy": "4.1.11", + "@jridgewell/trace-mapping": "0.3.31", + "@vitest/spy": "5.0.1", "estree-walker": "^3.0.3", - "magic-string": "^0.30.21" + "magic-string": "^1.2.3" }, "funding": { "url": "https://opencollective.com/vitest" @@ -2379,70 +2393,22 @@ } } }, - "node_modules/@vitest/pretty-format": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.1.11.tgz", - "integrity": "sha512-yiZzPbGTS9Sr/JpFl8zHrcIkAofNbFV6k21vIgQN/cY/oxZeXhJv5sc/MBJ5jFKWmWs+oJHw0UXLZjmf931+Vw==", + "node_modules/@vitest/mocker/node_modules/magic-string": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-1.4.1.tgz", + "integrity": "sha512-8lyCu36ErXR0J9uaGKlKQoiLZKmtI63YGLE8G2o9jyRPdr4X47LusSOwgOJOzcVtp81fTAAjxR7BwKz682Jhow==", "dev": true, "license": "MIT", "dependencies": { - "tinyrainbow": "^3.1.0" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, - "node_modules/@vitest/runner": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.1.11.tgz", - "integrity": "sha512-LztvUgdwMNJMIkj3hQnnxiC2Xy1zNxq928W/xhjCLaNCzqTZOudjwbQf6v9IntZGPw132i2Lq2rgTRZHD3JHNw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@vitest/utils": "4.1.11", - "pathe": "^2.0.3" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, - "node_modules/@vitest/snapshot": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.1.11.tgz", - "integrity": "sha512-pN7ikn1ON7h8ee4gIAp4AzyK+zBtJPzVbqOgu5LCEh4VaJVbPQcgYQYJIMGQPXVeJJq1fnfazis7a5pFNPahog==", - "dev": true, - "license": "MIT", - "dependencies": { - "@vitest/pretty-format": "4.1.11", - "@vitest/utils": "4.1.11", - "magic-string": "^0.30.21", - "pathe": "^2.0.3" - }, - "funding": { - "url": "https://opencollective.com/vitest" + "@jridgewell/sourcemap-codec": "^1.6.0" } }, "node_modules/@vitest/spy": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.1.11.tgz", - "integrity": "sha512-apNa/prQy2qCeywhnixOHPRCgGNhvg7T4Dapfl1GahLp/R+uhBm5cPyFoNVyqsNd2h1nJxL6BqqdIjiABL60YA==", - "dev": true, - "license": "MIT", - "funding": { - "url": "https://opencollective.com/vitest" - } - }, - "node_modules/@vitest/utils": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.1.11.tgz", - "integrity": "sha512-zTCVGpyFsGWBhllOyKlTw/vnr6D9qxsfSDyfbyZmTyjHw5N/VuvzHpHoQjm2ZJzn4RJgx5w4r7V0er69CmLgPQ==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-5.0.1.tgz", + "integrity": "sha512-rbto/mF/SGERxEgYOek7Xm6B9b+y+mVoo+f4b2LymYO8zM1b7uB5nHuhVMTP2hxdzgxvGiZYGxGIaMvL5y180Q==", "dev": true, "license": "MIT", - "dependencies": { - "@vitest/pretty-format": "4.1.11", - "convert-source-map": "^2.0.0", - "tinyrainbow": "^3.1.0" - }, "funding": { "url": "https://opencollective.com/vitest" } @@ -2837,13 +2803,6 @@ "node": ">=22" } }, - "node_modules/convert-source-map": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", - "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", - "dev": true, - "license": "MIT" - }, "node_modules/cosmiconfig": { "version": "9.0.2", "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-9.0.2.tgz", @@ -2959,6 +2918,7 @@ "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", "dev": true, "license": "Apache-2.0", + "peer": true, "engines": { "node": ">=8" } @@ -3827,13 +3787,6 @@ "node": ">=8" } }, - "node_modules/html-escaper": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", - "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", - "dev": true, - "license": "MIT" - }, "node_modules/husky": { "version": "9.1.7", "resolved": "https://registry.npmjs.org/husky/-/husky-9.1.7.tgz", @@ -3947,45 +3900,6 @@ "dev": true, "license": "ISC" }, - "node_modules/istanbul-lib-coverage": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", - "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=8" - } - }, - "node_modules/istanbul-lib-report": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", - "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "istanbul-lib-coverage": "^3.0.0", - "make-dir": "^4.0.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/istanbul-reports": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.2.0.tgz", - "integrity": "sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "html-escaper": "^2.0.0", - "istanbul-lib-report": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/jiti": { "version": "2.6.1", "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.6.1.tgz", @@ -4154,6 +4068,7 @@ "integrity": "sha512-WkUDrojuJs0xkgGf2udWxa3yGBRxPtxUkB79i6aCZLRgc7PM8fZe9TosfPDcvEpQZbuFASnHYmRLBLUbmLOIIA==", "dev": true, "license": "MPL-2.0", + "peer": true, "dependencies": { "detect-libc": "^2.0.3" }, @@ -4191,6 +4106,7 @@ "os": [ "android" ], + "peer": true, "engines": { "node": ">= 12.0.0" }, @@ -4212,6 +4128,7 @@ "os": [ "darwin" ], + "peer": true, "engines": { "node": ">= 12.0.0" }, @@ -4233,6 +4150,7 @@ "os": [ "darwin" ], + "peer": true, "engines": { "node": ">= 12.0.0" }, @@ -4254,6 +4172,7 @@ "os": [ "freebsd" ], + "peer": true, "engines": { "node": ">= 12.0.0" }, @@ -4275,6 +4194,7 @@ "os": [ "linux" ], + "peer": true, "engines": { "node": ">= 12.0.0" }, @@ -4299,6 +4219,7 @@ "os": [ "linux" ], + "peer": true, "engines": { "node": ">= 12.0.0" }, @@ -4323,6 +4244,7 @@ "os": [ "linux" ], + "peer": true, "engines": { "node": ">= 12.0.0" }, @@ -4347,6 +4269,7 @@ "os": [ "linux" ], + "peer": true, "engines": { "node": ">= 12.0.0" }, @@ -4371,6 +4294,7 @@ "os": [ "linux" ], + "peer": true, "engines": { "node": ">= 12.0.0" }, @@ -4392,6 +4316,7 @@ "os": [ "win32" ], + "peer": true, "engines": { "node": ">= 12.0.0" }, @@ -4413,6 +4338,7 @@ "os": [ "win32" ], + "peer": true, "engines": { "node": ">= 12.0.0" }, @@ -4551,22 +4477,6 @@ "source-map-js": "^1.2.1" } }, - "node_modules/make-dir": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", - "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", - "dev": true, - "license": "MIT", - "dependencies": { - "semver": "^7.5.3" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/markdown-table": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/markdown-table/-/markdown-table-3.0.4.tgz", @@ -5498,6 +5408,7 @@ } ], "license": "MIT", + "peer": true, "bin": { "nanoid": "bin/nanoid.cjs" }, @@ -5731,6 +5642,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "nanoid": "^3.3.18", "picocolors": "^1.1.1", @@ -5920,6 +5832,7 @@ "integrity": "sha512-hx/Pv0N1haXRb11qkfnK5MXB/iqr7i0yjWQqmO9uHqZpBgQSqzc8UsSnEpalsh+j1I8qQ2CkXAkJC8Br3dKSlg==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@oxc-project/types": "=0.150.0", "@rolldown/pluginutils": "^1.0.0" @@ -6218,11 +6131,14 @@ } }, "node_modules/tinybench": { - "version": "2.9.0", - "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", - "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==", + "version": "6.1.4", + "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-6.1.4.tgz", + "integrity": "sha512-9APumHG7r4yOk4X4WlkmE71aZcv1gvin1czO3OQ1U9iJcFA5Ja/ygyb0vPOVHTthFozUYs8CLoLUlM8grb2lTQ==", "dev": true, - "license": "MIT" + "license": "MIT", + "engines": { + "node": ">=20.0.0" + } }, "node_modules/tinyexec": { "version": "0.3.2", @@ -6491,6 +6407,7 @@ "integrity": "sha512-lhZBVvEHefgE+HQZC9O7EBJgCU/nVzFNl7vkS4RE0APtWLP02/8QVIkQtzBxPquh7lq5/78NHipTj7ODQ6XuyQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "lightningcss": "^1.33.0", "picomatch": "^4.0.7", @@ -6564,38 +6481,31 @@ } }, "node_modules/vitest": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.1.11.tgz", - "integrity": "sha512-fhACrNXUidIbGSBr5FlbuBkO7VWC1ZyLl0DO4CU2DrQoAPxX84Ysxs+HeGQpii5lZWV1Q4gBZTTu49mF+A6Edw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@vitest/expect": "4.1.11", - "@vitest/mocker": "4.1.11", - "@vitest/pretty-format": "4.1.11", - "@vitest/runner": "4.1.11", - "@vitest/snapshot": "4.1.11", - "@vitest/spy": "4.1.11", - "@vitest/utils": "4.1.11", - "es-module-lexer": "^2.0.0", - "expect-type": "^1.3.0", - "magic-string": "^0.30.21", - "obug": "^2.1.1", - "pathe": "^2.0.3", - "picomatch": "^4.0.3", - "std-env": "^4.0.0-rc.1", - "tinybench": "^2.9.0", - "tinyexec": "^1.0.2", - "tinyglobby": "^0.2.15", - "tinyrainbow": "^3.1.0", - "vite": "^6.0.0 || ^7.0.0 || ^8.0.0", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-5.0.1.tgz", + "integrity": "sha512-iA95lQbKEkvrtTkdAgnWbXfbipWiiWe/hDl2P5tMi6WFwD76G0NxXAGp/M9EOcYupeGJRr6wppMc7CoA41TQjg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/chai": "^5.2.2", + "@vitest/mocker": "5.0.1", + "chai": "^6.2.2", + "es-module-lexer": "^2.3.2", + "expect-type": "^1.4.0", + "magic-string": "^1.2.3", + "obug": "^2.1.4", + "picomatch": "^4.0.7", + "std-env": "^4.2.0", + "tinybench": "6.1.4", + "tinyexec": "1.3.0", + "tinyglobby": "^0.2.17", "why-is-node-running": "^2.3.0" }, "bin": { "vitest": "vitest.mjs" }, "engines": { - "node": "^20.0.0 || ^22.0.0 || >=24.0.0" + "node": "^22.12.0 || ^24.0.0 || >=26.0.0" }, "funding": { "url": "https://opencollective.com/vitest" @@ -6603,16 +6513,16 @@ "peerDependencies": { "@edge-runtime/vm": "*", "@opentelemetry/api": "^1.9.0", - "@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0", - "@vitest/browser-playwright": "4.1.11", - "@vitest/browser-preview": "4.1.11", - "@vitest/browser-webdriverio": "4.1.11", - "@vitest/coverage-istanbul": "4.1.11", - "@vitest/coverage-v8": "4.1.11", - "@vitest/ui": "4.1.11", + "@types/node": "^22.0.0 || >=24.0.0", + "@vitest/browser-playwright": "5.0.1", + "@vitest/browser-preview": "5.0.1", + "@vitest/browser-webdriverio": "^5.0.0-beta.5 || >=5.0.0", + "@vitest/coverage-istanbul": "5.0.1", + "@vitest/coverage-v8": "5.0.1", + "@vitest/ui": "5.0.1", "happy-dom": "*", "jsdom": "*", - "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" + "vite": "^6.4.0 || ^7.0.0 || ^8.0.0" }, "peerDependenciesMeta": { "@edge-runtime/vm": { @@ -6653,10 +6563,20 @@ } } }, + "node_modules/vitest/node_modules/magic-string": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-1.4.1.tgz", + "integrity": "sha512-8lyCu36ErXR0J9uaGKlKQoiLZKmtI63YGLE8G2o9jyRPdr4X47LusSOwgOJOzcVtp81fTAAjxR7BwKz682Jhow==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.6.0" + } + }, "node_modules/vitest/node_modules/tinyexec": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.3.1.tgz", - "integrity": "sha512-GCvB3aoys96IuDFBMcTB46JOR6mdMtAToqwiW8JlWhsoh1mhHi/xn9ss/Dg7N555GiJyEt2qzoG/NHCwM6h1EA==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.3.0.tgz", + "integrity": "sha512-QKAl9m8gWWGHV8jZcPeym6j+XULi6tOf1mT83WYJ4Lk2ytW/uwAWkrP0uFsdoYMdueVJ0qs26wZ+23xeB4ibNQ==", "dev": true, "license": "MIT", "engines": { diff --git a/package.json b/package.json index 7e06476..614b11c 100644 --- a/package.json +++ b/package.json @@ -56,7 +56,7 @@ "@eslint/js": "^9.39.5", "@eslint/markdown": "^7.5.1", "@types/node": "^24.13.5", - "@vitest/coverage-v8": "^4.1.10", + "@vitest/coverage-v8": "^5.0.1", "eslint": "^9.39.5", "eslint-config-prettier": "^10.1.8", "eslint-plugin-jsonc": "^2.21.1", @@ -73,7 +73,7 @@ "tsup": "^8.5.1", "typescript": "^5.9.3", "typescript-eslint": "^8.65.0", - "vitest": "^4.1.10", + "vitest": "^5.0.1", "yaml-eslint-parser": "^1.3.2" } } From 0f07e0addef8ba5fecb643bb7e8d1b36647d816c Mon Sep 17 00:00:00 2001 From: rcpthongta <220981657+rcpthongta@users.noreply.github.com> Date: Mon, 21 Sep 2026 22:18:42 +0700 Subject: [PATCH 035/121] perf(has-text): refactor benchmarks to use vitest test context runner --- src/libs/has-text/has-text.lib.bench.ts | 204 +++++++++++++----------- 1 file changed, 109 insertions(+), 95 deletions(-) diff --git a/src/libs/has-text/has-text.lib.bench.ts b/src/libs/has-text/has-text.lib.bench.ts index 23610f3..8271d63 100644 --- a/src/libs/has-text/has-text.lib.bench.ts +++ b/src/libs/has-text/has-text.lib.bench.ts @@ -1,4 +1,4 @@ -import { bench, describe } from "vitest"; +import { describe, test, TestContext } from "vitest"; import { hasText } from "./has-text.lib"; @@ -12,148 +12,162 @@ const longText100KB: string = "a".repeat(100_000); describe("hasText", (): void => { describe("given common short inputs", (): void => { - bench("non-whitespace text", (): void => { - hasText(shortText); - }); + test("benchmarks common short inputs", async ({ bench }: TestContext): Promise => { + await bench("non-whitespace text", (): void => { + hasText(shortText); + }).run(); - bench("single non-whitespace character", (): void => { - hasText("a"); - }); + await bench("single non-whitespace character", (): void => { + hasText("a"); + }).run(); - bench("single whitespace character", (): void => { - hasText(" "); - }); + await bench("single whitespace character", (): void => { + hasText(" "); + }).run(); - bench("whitespace-only string", (): void => { - hasText(" "); + await bench("whitespace-only string", (): void => { + hasText(" "); + }).run(); }); }); describe("given long inputs", (): void => { - bench("long text", (): void => { - hasText(longText); - }); + test("benchmarks long inputs", async ({ bench }: TestContext): Promise => { + await bench("long text", (): void => { + hasText(longText); + }).run(); - bench("1 KB non-whitespace string", (): void => { - hasText(longText1KB); - }); + await bench("1 KB non-whitespace string", (): void => { + hasText(longText1KB); + }).run(); - bench("100 KB non-whitespace string", (): void => { - hasText(longText100KB); - }); + await bench("100 KB non-whitespace string", (): void => { + hasText(longText100KB); + }).run(); - bench("long whitespace-only string", (): void => { - hasText(longWhitespace); - }); + await bench("long whitespace-only string", (): void => { + hasText(longWhitespace); + }).run(); - bench("non-whitespace at the beginning", (): void => { - hasText(longTextWithTrailingWhitespace); - }); + await bench("non-whitespace at the beginning", (): void => { + hasText(longTextWithTrailingWhitespace); + }).run(); - bench("non-whitespace at the end", (): void => { - hasText(longWhitespaceWithTextAtEnd); + await bench("non-whitespace at the end", (): void => { + hasText(longWhitespaceWithTextAtEnd); + }).run(); }); }); describe("given Unicode input", (): void => { - bench("CJK", (): void => { - hasText("你好世界"); - }); + test("benchmarks Unicode input", async ({ bench }: TestContext): Promise => { + await bench("CJK", (): void => { + hasText("你好世界"); + }).run(); - bench("Arabic", (): void => { - hasText("مرحبا بالعالم"); - }); + await bench("Arabic", (): void => { + hasText("مرحبا بالعالم"); + }).run(); - bench("Thai", (): void => { - hasText("สวัสดีชาวโลก"); - }); + await bench("Thai", (): void => { + hasText("สวัสดีชาวโลก"); + }).run(); - bench("accented Latin", (): void => { - hasText("café"); - }); + await bench("accented Latin", (): void => { + hasText("café"); + }).run(); - bench("combining marks", (): void => { - hasText("cafe\u0301"); - }); + await bench("combining marks", (): void => { + hasText("cafe\u0301"); + }).run(); - bench("emoji", (): void => { - hasText("🎉🚀🌟"); + await bench("emoji", (): void => { + hasText("🎉🚀🌟"); + }).run(); }); }); describe("given Unicode whitespace", (): void => { - bench("non-breaking spaces only", (): void => { - hasText("\u00A0\u00A0\u00A0"); - }); + test("benchmarks Unicode whitespace", async ({ bench }: TestContext): Promise => { + await bench("non-breaking spaces only", (): void => { + hasText("\u00A0\u00A0\u00A0"); + }).run(); - bench("ideographic spaces only", (): void => { - hasText("\u3000\u3000\u3000"); - }); + await bench("ideographic spaces only", (): void => { + hasText("\u3000\u3000\u3000"); + }).run(); - bench("mixed Unicode whitespace only", (): void => { - hasText(" \t\n\r\u00A0\u2003\u202F\u3000 "); - }); + await bench("mixed Unicode whitespace only", (): void => { + hasText(" \t\n\r\u00A0\u2003\u202F\u3000 "); + }).run(); - bench("text surrounded by Unicode whitespace", (): void => { - hasText("\u00A0\u3000hello\u2003"); + await bench("text surrounded by Unicode whitespace", (): void => { + hasText("\u00A0\u3000hello\u2003"); + }).run(); }); }); describe("given symbols and emojis", (): void => { - bench("symbol-only string", (): void => { - hasText("!@#$%^&*()"); - }); + test("benchmarks symbols and emojis", async ({ bench }: TestContext): Promise => { + await bench("symbol-only string", (): void => { + hasText("!@#$%^&*()"); + }).run(); - bench("single emoji", (): void => { - hasText("🔥"); - }); + await bench("single emoji", (): void => { + hasText("🔥"); + }).run(); - bench("multiple emojis", (): void => { - hasText("🎉🚀🌟"); + await bench("multiple emojis", (): void => { + hasText("🎉🚀🌟"); + }).run(); }); }); describe("given non-string values", (): void => { - bench("number", (): void => { - hasText(12345); - }); + test("benchmarks non-string values", async ({ bench }: TestContext): Promise => { + await bench("number", (): void => { + hasText(12345); + }).run(); - bench("boolean", (): void => { - hasText(true); - }); + await bench("boolean", (): void => { + hasText(true); + }).run(); - bench("object", (): void => { - hasText({ length: 5 }); - }); + await bench("object", (): void => { + hasText({ length: 5 }); + }).run(); - bench("object with text property", (): void => { - hasText({ text: "hello" }); - }); + await bench("object with text property", (): void => { + hasText({ text: "hello" }); + }).run(); - bench("array", (): void => { - hasText(["hello"]); - }); + await bench("array", (): void => { + hasText(["hello"]); + }).run(); - bench("function", (): void => { - hasText(() => {}); - }); + await bench("function", (): void => { + hasText(() => {}); + }).run(); - bench("symbol", (): void => { - hasText(Symbol("text")); + await bench("symbol", (): void => { + hasText(Symbol("text")); + }).run(); }); }); describe("given an empty or nullish value", (): void => { - bench("empty string", (): void => { - hasText(""); - }); - - bench("null", (): void => { - hasText(null); - }); - - bench("undefined", (): void => { - hasText(undefined); + test("benchmarks empty and nullish values", async ({ bench }: TestContext): Promise => { + await bench("empty string", (): void => { + hasText(""); + }).run(); + + await bench("null", (): void => { + hasText(null); + }).run(); + + await bench("undefined", (): void => { + hasText(undefined); + }).run(); }); }); }); From 979f28f763d3db30d04217803b5f2984b6653c24 Mon Sep 17 00:00:00 2001 From: rcpthongta <220981657+rcpthongta@users.noreply.github.com> Date: Mon, 21 Sep 2026 22:18:58 +0700 Subject: [PATCH 036/121] chore(lint): disable sonarjs assertion rule for benchmark files --- eslint.config.mjs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/eslint.config.mjs b/eslint.config.mjs index f79bc31..cf3114f 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -85,6 +85,12 @@ const configuration = eslint.defineConfig( ] } }, + { + files: ["**/*.bench.ts", "**/*.benchmark.ts"], + rules: { + "sonarjs/assertions-in-tests": "off" + } + }, { files: jsFiles, extends: [javascript.configs.recommended], From 8b80b498b4ceaf10553803e36447a4937a448e90 Mon Sep 17 00:00:00 2001 From: rcpthongta <220981657+rcpthongta@users.noreply.github.com> Date: Tue, 22 Sep 2026 01:15:14 +0700 Subject: [PATCH 037/121] feat(has-length): add non-empty string check utility --- src/libs/has-length/has-length.lib.ts | 18 ++++++++++++++++++ src/libs/has-length/index.ts | 1 + src/libs/index.ts | 1 + 3 files changed, 20 insertions(+) create mode 100644 src/libs/has-length/has-length.lib.ts create mode 100644 src/libs/has-length/index.ts diff --git a/src/libs/has-length/has-length.lib.ts b/src/libs/has-length/has-length.lib.ts new file mode 100644 index 0000000..5c7a72c --- /dev/null +++ b/src/libs/has-length/has-length.lib.ts @@ -0,0 +1,18 @@ +/** + * Checks whether a value is a non-empty string. + * + * @param value - The value to check. + * @returns `true` if the value is a non-empty string; otherwise, `false`. + * + * @example + * + * hasLength("Hello World"); // true + * hasLength(" "); // true + * hasLength(""); // false + * hasLength(123); // false + * hasLength(null); // false + * hasLength(undefined); // false + */ +export function hasLength(value: unknown): value is string { + return typeof value === "string" && value.length > 0; +} diff --git a/src/libs/has-length/index.ts b/src/libs/has-length/index.ts new file mode 100644 index 0000000..1e80146 --- /dev/null +++ b/src/libs/has-length/index.ts @@ -0,0 +1 @@ +export * from "./has-length.lib"; diff --git a/src/libs/index.ts b/src/libs/index.ts index 7973eff..0483440 100644 --- a/src/libs/index.ts +++ b/src/libs/index.ts @@ -1 +1,2 @@ +export * from "./has-length"; export * from "./has-text"; From c0c02a5de34bcfee56c2169fbf263f3c4d8c75d0 Mon Sep 17 00:00:00 2001 From: rcpthongta <220981657+rcpthongta@users.noreply.github.com> Date: Tue, 22 Sep 2026 01:36:07 +0700 Subject: [PATCH 038/121] test(has-length): add unit tests for non-empty string check --- src/libs/has-length/has-length.lib.spec.ts | 118 +++++++++++++++++++++ 1 file changed, 118 insertions(+) create mode 100644 src/libs/has-length/has-length.lib.spec.ts diff --git a/src/libs/has-length/has-length.lib.spec.ts b/src/libs/has-length/has-length.lib.spec.ts new file mode 100644 index 0000000..4131b0f --- /dev/null +++ b/src/libs/has-length/has-length.lib.spec.ts @@ -0,0 +1,118 @@ +import { describe, expect, expectTypeOf, test } from "vitest"; + +import { hasLength } from "./has-length.lib"; + +describe("hasLength", (): void => { + describe("given a non-empty string", (): void => { + test("returns true for standard text", (): void => { + expect(hasLength("Hello World")).toBe(true); + expect(hasLength("12345")).toBe(true); + }); + + test("returns true for a single code unit", (): void => { + expect(hasLength("a")).toBe(true); + expect(hasLength(".")).toBe(true); + expect(hasLength(" ")).toBe(true); + }); + + test("returns true for whitespace-only strings", (): void => { + expect(hasLength(" ")).toBe(true); + expect(hasLength("\t\n\r\v\f")).toBe(true); + }); + + test("returns true for symbols and emojis", (): void => { + expect(hasLength("!@#$%^&*()")).toBe(true); + expect(hasLength("🔥")).toBe(true); + expect(hasLength("🎉🚀🌟")).toBe(true); + }); + + test("returns true for Unicode and accented strings", (): void => { + expect(hasLength("สวัสดีชาวโลก")).toBe(true); + expect(hasLength("こんにちは")).toBe(true); + expect(hasLength("你好世界")).toBe(true); + expect(hasLength("مرحبا بالعالم")).toBe(true); + expect(hasLength("café")).toBe(true); + expect(hasLength("cafe\u0301")).toBe(true); + }); + }); + + describe("given a string with different lengths", (): void => { + test("returns true regardless of string length", (): void => { + expect(hasLength("a")).toBe(true); + expect(hasLength("ab")).toBe(true); + expect(hasLength("a".repeat(1000))).toBe(true); + expect(hasLength("a".repeat(100_000))).toBe(true); + }); + }); + + describe("given a non-string value", (): void => { + test("returns false for numbers", (): void => { + expect(hasLength(0)).toBe(false); + expect(hasLength(123)).toBe(false); + expect(hasLength(-456)).toBe(false); + expect(hasLength(Infinity)).toBe(false); + expect(hasLength(NaN)).toBe(false); + }); + + test("returns false for booleans", (): void => { + expect(hasLength(true)).toBe(false); + expect(hasLength(false)).toBe(false); + }); + + test("returns false for bigint and symbols", (): void => { + expect(hasLength(100n)).toBe(false); + expect(hasLength(Symbol("text"))).toBe(false); + }); + + test("returns false for objects with a length property", (): void => { + expect(hasLength({ length: 0 })).toBe(false); + expect(hasLength({ length: 5 })).toBe(false); + expect(hasLength({ value: "hello", length: 5 })).toBe(false); + }); + + test("returns false for arrays", (): void => { + expect(hasLength([])).toBe(false); + expect(hasLength(["hello"])).toBe(false); + expect(hasLength([1, 2, 3])).toBe(false); + }); + + test("returns false for built-in object instances", (): void => { + expect(hasLength(new Date())).toBe(false); + expect(hasLength(/hello/)).toBe(false); + }); + + test("returns false for functions regardless of their parameter count", (): void => { + const functionWithParameters: (_first: unknown, _second: unknown) => void = ( + _first: unknown, + _second: unknown + ): void => {}; + + expect(hasLength(() => {})).toBe(false); + expect(hasLength(functionWithParameters)).toBe(false); + }); + }); + + describe("given a type guard usage", (): void => { + test("narrows an unknown value to string", (): void => { + const value: unknown = "Hello"; + + if (hasLength(value)) { + expectTypeOf(value).toEqualTypeOf(); + } + }); + }); + + describe("given an empty or nullish value", (): void => { + test("returns false for an empty string", (): void => { + expect(hasLength("")).toBe(false); + }); + + test("returns false for null", (): void => { + expect(hasLength(null)).toBe(false); + }); + + test("returns false for undefined", (): void => { + expect(hasLength(undefined)).toBe(false); + }); + }); +}); From 20cd0b046d718470b4b4d14bcbcfcfc477fad0a0 Mon Sep 17 00:00:00 2001 From: rcpthongta <220981657+rcpthongta@users.noreply.github.com> Date: Tue, 22 Sep 2026 01:36:59 +0700 Subject: [PATCH 039/121] perf(has-length): add performance benchmarks for non-empty check --- src/libs/has-length/has-length.lib.bench.ts | 89 +++++++++++++++++++++ 1 file changed, 89 insertions(+) create mode 100644 src/libs/has-length/has-length.lib.bench.ts diff --git a/src/libs/has-length/has-length.lib.bench.ts b/src/libs/has-length/has-length.lib.bench.ts new file mode 100644 index 0000000..274022b --- /dev/null +++ b/src/libs/has-length/has-length.lib.bench.ts @@ -0,0 +1,89 @@ +import { describe, test, TestContext } from "vitest"; + +import { hasLength } from "./has-length.lib"; + +const shortText: string = "hello"; +const longText1KB: string = "a".repeat(1_000); +const longText100KB: string = "a".repeat(100_000); +const whitespaceString: string = " ".repeat(1_000); +const unicodeString: string = "你好世界 مرحبا สวัสดี こんにちは café"; + +describe("hasLength", (): void => { + test("given common short inputs", ({ bench }: TestContext): void => { + bench("non-empty string", (): void => { + hasLength(shortText); + }); + + bench("single-character string", (): void => { + hasLength("a"); + }); + + bench("whitespace-only string", (): void => { + hasLength(" "); + }); + }); + + test("given strings of different sizes", ({ bench }: TestContext): void => { + bench("1 KB string", (): void => { + hasLength(longText1KB); + }); + + bench("100 KB string", (): void => { + hasLength(longText100KB); + }); + + bench("long whitespace-only string", (): void => { + hasLength(whitespaceString); + }); + }); + + test("given Unicode strings", ({ bench }: TestContext): void => { + bench("Unicode string", (): void => { + hasLength(unicodeString); + }); + + bench("emoji string", (): void => { + hasLength("🎉🚀🌟"); + }); + + bench("combining-mark string", (): void => { + hasLength("cafe\u0301"); + }); + }); + + test("given non-string values", ({ bench }: TestContext): void => { + bench("number", (): void => { + hasLength(12345); + }); + + bench("boolean", (): void => { + hasLength(true); + }); + + bench("object with length property", (): void => { + hasLength({ length: 5 }); + }); + + bench("array", (): void => { + hasLength(["a", "b", "c"]); + }); + + bench("function", (): void => { + hasLength(() => {}); + }); + }); + + test("given an empty or nullish value", ({ bench }: TestContext): void => { + bench("empty string", (): void => { + hasLength(""); + }); + + bench("null", (): void => { + hasLength(null); + }); + + bench("undefined", (): void => { + hasLength(undefined); + }); + }); +}); From 8241d79cdab3f2d09d3aac0fa5da73b1bd4581fe Mon Sep 17 00:00:00 2001 From: rcpthongta <220981657+rcpthongta@users.noreply.github.com> Date: Tue, 22 Sep 2026 01:41:55 +0700 Subject: [PATCH 040/121] test(has-text): refactor test cases to use test instead of it --- src/libs/has-text/has-text.lib.spec.ts | 34 +++++++++++++------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/src/libs/has-text/has-text.lib.spec.ts b/src/libs/has-text/has-text.lib.spec.ts index bd1f7ec..37eb3ea 100644 --- a/src/libs/has-text/has-text.lib.spec.ts +++ b/src/libs/has-text/has-text.lib.spec.ts @@ -1,27 +1,27 @@ -import { describe, expect, expectTypeOf, it } from "vitest"; +import { describe, expect, expectTypeOf, test } from "vitest"; import { hasText } from "./has-text.lib"; describe("hasText", (): void => { describe("given a string containing text", (): void => { - it("returns true for standard text", (): void => { + test("returns true for standard text", (): void => { expect(hasText("Hello World")).toBe(true); expect(hasText("12345")).toBe(true); }); - it("returns true when text is surrounded by whitespace", (): void => { + test("returns true when text is surrounded by whitespace", (): void => { expect(hasText(" Hello World ")).toBe(true); expect(hasText("\t\n text \r\n")).toBe(true); }); - it("returns true for symbols and emojis", (): void => { + test("returns true for symbols and emojis", (): void => { expect(hasText("!")).toBe(true); expect(hasText("!@#$%^&*()")).toBe(true); expect(hasText("🔥")).toBe(true); expect(hasText("🎉🚀🌟")).toBe(true); }); - it("returns true for Unicode and accented text", (): void => { + test("returns true for Unicode and accented text", (): void => { expect(hasText("สวัสดีชาวโลก")).toBe(true); expect(hasText("こんにちは")).toBe(true); expect(hasText("你好世界")).toBe(true); @@ -32,13 +32,13 @@ describe("hasText", (): void => { }); describe("given a string containing only whitespace", (): void => { - it("returns false for ASCII whitespace", (): void => { + test("returns false for ASCII whitespace", (): void => { expect(hasText(" ")).toBe(false); expect(hasText(" ")).toBe(false); expect(hasText("\t\n\r\v\f")).toBe(false); }); - it("returns false for Unicode whitespace", (): void => { + test("returns false for Unicode whitespace", (): void => { expect(hasText("\u00A0")).toBe(false); expect(hasText("\u2003")).toBe(false); expect(hasText("\u202F")).toBe(false); @@ -48,13 +48,13 @@ describe("hasText", (): void => { }); describe("given a string containing combining marks", (): void => { - it("treats combining marks according to the configured whitespace rule", (): void => { + test("treats combining marks according to the configured whitespace rule", (): void => { expect(hasText("cafe\u0301")).toBe(true); }); }); describe("given a non-string value", (): void => { - it("returns false for numbers", (): void => { + test("returns false for numbers", (): void => { expect(hasText(0)).toBe(false); expect(hasText(123)).toBe(false); expect(hasText(-456)).toBe(false); @@ -62,31 +62,31 @@ describe("hasText", (): void => { expect(hasText(NaN)).toBe(false); }); - it("returns false for booleans", (): void => { + test("returns false for booleans", (): void => { expect(hasText(true)).toBe(false); expect(hasText(false)).toBe(false); }); - it("returns false for bigint and symbols", (): void => { + test("returns false for bigint and symbols", (): void => { expect(hasText(100n)).toBe(false); expect(hasText(Symbol("text"))).toBe(false); }); - it("returns false for objects and arrays", (): void => { + test("returns false for objects and arrays", (): void => { expect(hasText({})).toBe(false); expect(hasText({ text: "hello" })).toBe(false); expect(hasText([])).toBe(false); expect(hasText(["hello"])).toBe(false); }); - it("returns false for functions", (): void => { + test("returns false for functions", (): void => { expect(hasText(() => {})).toBe(false); expect(hasText(function () {})).toBe(false); }); }); describe("given a type guard usage", (): void => { - it("narrows an unknown value to string", (): void => { + test("narrows an unknown value to string", (): void => { const value: unknown = "Hello"; if (hasText(value)) { @@ -96,15 +96,15 @@ describe("hasText", (): void => { }); describe("given an empty or nullish value", (): void => { - it("returns false for an empty string", (): void => { + test("returns false for an empty string", (): void => { expect(hasText("")).toBe(false); }); - it("returns false for null", (): void => { + test("returns false for null", (): void => { expect(hasText(null)).toBe(false); }); - it("returns false for undefined", (): void => { + test("returns false for undefined", (): void => { expect(hasText(undefined)).toBe(false); }); }); From 7831a155e081858a9685c95f0f3563c07f294061 Mon Sep 17 00:00:00 2001 From: rcpthongta <220981657+rcpthongta@users.noreply.github.com> Date: Tue, 22 Sep 2026 01:42:28 +0700 Subject: [PATCH 041/121] perf(has-text): migrate benchmarks to vitest test context --- src/libs/has-text/has-text.lib.bench.ts | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/libs/has-text/has-text.lib.bench.ts b/src/libs/has-text/has-text.lib.bench.ts index 23610f3..48e8635 100644 --- a/src/libs/has-text/has-text.lib.bench.ts +++ b/src/libs/has-text/has-text.lib.bench.ts @@ -1,4 +1,4 @@ -import { bench, describe } from "vitest"; +import { describe, test, TestContext } from "vitest"; import { hasText } from "./has-text.lib"; @@ -11,7 +11,7 @@ const longText1KB: string = "a".repeat(1_000); const longText100KB: string = "a".repeat(100_000); describe("hasText", (): void => { - describe("given common short inputs", (): void => { + test("given common short inputs", ({ bench }: TestContext): void => { bench("non-whitespace text", (): void => { hasText(shortText); }); @@ -29,7 +29,7 @@ describe("hasText", (): void => { }); }); - describe("given long inputs", (): void => { + test("given long inputs", ({ bench }: TestContext): void => { bench("long text", (): void => { hasText(longText); }); @@ -55,7 +55,7 @@ describe("hasText", (): void => { }); }); - describe("given Unicode input", (): void => { + test("given Unicode input", ({ bench }: TestContext): void => { bench("CJK", (): void => { hasText("你好世界"); }); @@ -81,7 +81,7 @@ describe("hasText", (): void => { }); }); - describe("given Unicode whitespace", (): void => { + test("given Unicode whitespace", ({ bench }: TestContext): void => { bench("non-breaking spaces only", (): void => { hasText("\u00A0\u00A0\u00A0"); }); @@ -99,7 +99,7 @@ describe("hasText", (): void => { }); }); - describe("given symbols and emojis", (): void => { + test("given symbols and emojis", ({ bench }: TestContext): void => { bench("symbol-only string", (): void => { hasText("!@#$%^&*()"); }); @@ -113,7 +113,7 @@ describe("hasText", (): void => { }); }); - describe("given non-string values", (): void => { + test("given non-string values", ({ bench }: TestContext): void => { bench("number", (): void => { hasText(12345); }); @@ -143,7 +143,7 @@ describe("hasText", (): void => { }); }); - describe("given an empty or nullish value", (): void => { + test("given an empty or nullish value", ({ bench }: TestContext): void => { bench("empty string", (): void => { hasText(""); }); From 5275abb79a3cbb9f1f9e88212915634f343d506d Mon Sep 17 00:00:00 2001 From: rcpthongta <220981657+rcpthongta@users.noreply.github.com> Date: Tue, 22 Sep 2026 01:53:52 +0700 Subject: [PATCH 042/121] feat(default-if-blank): add fallback utility for blank strings --- .../default-if-blank/default-if-blank.lib.ts | 20 +++++++++++++++++++ src/libs/default-if-blank/index.ts | 1 + src/libs/index.ts | 1 + 3 files changed, 22 insertions(+) create mode 100644 src/libs/default-if-blank/default-if-blank.lib.ts create mode 100644 src/libs/default-if-blank/index.ts diff --git a/src/libs/default-if-blank/default-if-blank.lib.ts b/src/libs/default-if-blank/default-if-blank.lib.ts new file mode 100644 index 0000000..4558d14 --- /dev/null +++ b/src/libs/default-if-blank/default-if-blank.lib.ts @@ -0,0 +1,20 @@ +import { hasText } from "../has-text"; + +/** + * Returns a default value when the given value is empty or contains only whitespace. + * + * @param value - The string value to check, or `null` or `undefined`. + * @param defaultValue - The value to return when `value` is empty, whitespace-only, `null`, or `undefined`. + * @returns The original `value` when it contains at least one non-whitespace character; otherwise, `defaultValue`. + * + * @example + * + * defaultIfBlank("Hello", "default"); // "Hello" + * defaultIfBlank(" ", "default"); // "default" + * defaultIfBlank("", "default"); // "default" + * defaultIfBlank(null, "default"); // "default" + * defaultIfBlank(undefined, "default"); // "default" + */ +export function defaultIfBlank(value: string | null | undefined, defaultValue: string): string { + return hasText(value) ? value : defaultValue; +} diff --git a/src/libs/default-if-blank/index.ts b/src/libs/default-if-blank/index.ts new file mode 100644 index 0000000..c87bf74 --- /dev/null +++ b/src/libs/default-if-blank/index.ts @@ -0,0 +1 @@ +export * from "./default-if-blank.lib"; diff --git a/src/libs/index.ts b/src/libs/index.ts index 0483440..a0d16b4 100644 --- a/src/libs/index.ts +++ b/src/libs/index.ts @@ -1,2 +1,3 @@ +export * from "./default-if-blank"; export * from "./has-length"; export * from "./has-text"; From 61f1412e2de8aa24ccfeacbcdeb91845bf41b6fc Mon Sep 17 00:00:00 2001 From: rcpthongta <220981657+rcpthongta@users.noreply.github.com> Date: Tue, 22 Sep 2026 01:54:37 +0700 Subject: [PATCH 043/121] test(default-if-blank): add unit tests for fallback blank string check --- .../default-if-blank.lib.spec.ts | 82 +++++++++++++++++++ 1 file changed, 82 insertions(+) create mode 100644 src/libs/default-if-blank/default-if-blank.lib.spec.ts diff --git a/src/libs/default-if-blank/default-if-blank.lib.spec.ts b/src/libs/default-if-blank/default-if-blank.lib.spec.ts new file mode 100644 index 0000000..8fc34b3 --- /dev/null +++ b/src/libs/default-if-blank/default-if-blank.lib.spec.ts @@ -0,0 +1,82 @@ +import { describe, expect, test } from "vitest"; + +import { defaultIfBlank } from "./default-if-blank.lib"; + +describe("defaultIfBlank", (): void => { + describe("given a non-blank string", (): void => { + test("returns the original string", (): void => { + expect(defaultIfBlank("Hello", "default")).toBe("Hello"); + expect(defaultIfBlank("Hello World", "default")).toBe("Hello World"); + expect(defaultIfBlank("12345", "default")).toBe("12345"); + }); + + test("preserves leading and trailing whitespace", (): void => { + expect(defaultIfBlank(" Hello World ", "default")).toBe(" Hello World "); + expect(defaultIfBlank("\tHello\t", "default")).toBe("\tHello\t"); + expect(defaultIfBlank("\t\n text \r\n", "default")).toBe("\t\n text \r\n"); + }); + + test("preserves Unicode and accented characters", (): void => { + expect(defaultIfBlank("สวัสดีชาวโลก", "default")).toBe("สวัสดีชาวโลก"); + expect(defaultIfBlank("こんにちは", "default")).toBe("こんにちは"); + expect(defaultIfBlank("café", "default")).toBe("café"); + expect(defaultIfBlank("مرحبا بالعالم", "default")).toBe("مرحبا بالعالم"); + expect(defaultIfBlank("cafe\u0301", "default")).toBe("cafe\u0301"); + }); + + test("preserves symbols and emojis", (): void => { + expect(defaultIfBlank("🔥", "default")).toBe("🔥"); + expect(defaultIfBlank("!@#$%^&*()", "default")).toBe("!@#$%^&*()"); + }); + + test("returns the original value when text contains surrounding whitespace", (): void => { + const value: string = " \t\n Hello World \u00A0 "; + + expect(defaultIfBlank(value, "default")).toBe(value); + }); + }); + + describe("given a whitespace-only string", (): void => { + test("returns the default value for ASCII whitespace", (): void => { + expect(defaultIfBlank(" ", "default")).toBe("default"); + expect(defaultIfBlank(" ", "default")).toBe("default"); + expect(defaultIfBlank("\t\n\r\v\f", "default")).toBe("default"); + expect(defaultIfBlank(" \t\n\r ", "default")).toBe("default"); + }); + + test("returns the default value for Unicode whitespace", (): void => { + expect(defaultIfBlank("\u00A0", "default")).toBe("default"); + expect(defaultIfBlank("\u2003", "default")).toBe("default"); + expect(defaultIfBlank("\u202F", "default")).toBe("default"); + expect(defaultIfBlank("\u3000", "default")).toBe("default"); + expect(defaultIfBlank(" \t\n\r\u00A0\u3000 ", "default")).toBe("default"); + }); + + test("returns the default value for mixed whitespace", (): void => { + expect(defaultIfBlank(" \t\n\r\u00A0\u2003\u202F\u3000 ", "default")).toBe("default"); + }); + }); + + describe("given a string containing a single non-whitespace character", (): void => { + test("returns the original string", (): void => { + expect(defaultIfBlank("a", "default")).toBe("a"); + expect(defaultIfBlank("1", "default")).toBe("1"); + expect(defaultIfBlank("🔥", "default")).toBe("🔥"); + expect(defaultIfBlank("中", "default")).toBe("中"); + }); + }); + + describe("given an empty or nullish value", (): void => { + test("returns the default value for an empty string", (): void => { + expect(defaultIfBlank("", "default")).toBe("default"); + }); + + test("returns the default value for null", (): void => { + expect(defaultIfBlank(null, "default")).toBe("default"); + }); + + test("returns the default value for undefined", (): void => { + expect(defaultIfBlank(undefined, "default")).toBe("default"); + }); + }); +}); From 7e96f89ce8a147695d759dfe5efd05281ac2b73e Mon Sep 17 00:00:00 2001 From: rcpthongta <220981657+rcpthongta@users.noreply.github.com> Date: Tue, 22 Sep 2026 01:55:06 +0700 Subject: [PATCH 044/121] perf(default-if-blank): add performance benchmarks for fallback check --- .../default-if-blank.lib.bench.ts | 102 ++++++++++++++++++ 1 file changed, 102 insertions(+) create mode 100644 src/libs/default-if-blank/default-if-blank.lib.bench.ts diff --git a/src/libs/default-if-blank/default-if-blank.lib.bench.ts b/src/libs/default-if-blank/default-if-blank.lib.bench.ts new file mode 100644 index 0000000..f561738 --- /dev/null +++ b/src/libs/default-if-blank/default-if-blank.lib.bench.ts @@ -0,0 +1,102 @@ +import { describe, test, TestContext } from "vitest"; + +import { defaultIfBlank } from "./default-if-blank.lib"; + +const longText: string = "the quick brown fox jumps over the lazy dog ".repeat(100); +const longWhitespace: string = " ".repeat(1000); +const longWhitespaceWithTextAtStart: string = "a" + " ".repeat(1000); +const longWhitespaceWithTextAtEnd: string = " ".repeat(1000) + "a"; +const longMixedWhitespace: string = " \t\n\r\u00A0\u2003\u202F\u3000 ".repeat(100); +const longTextWithSurroundingWhitespace: string = " \t\n" + longText + "\u00A0\u3000 "; + +describe("defaultIfBlank", (): void => { + test("given common short inputs", ({ bench }: TestContext): void => { + bench("non-blank text", (): void => { + defaultIfBlank("hello", "fallback"); + }); + + bench("single non-whitespace character", (): void => { + defaultIfBlank("a", "fallback"); + }); + + bench("text with surrounding whitespace", (): void => { + defaultIfBlank(" hello ", "fallback"); + }); + + bench("whitespace-only", (): void => { + defaultIfBlank(" ", "fallback"); + }); + }); + + test("given long inputs", ({ bench }: TestContext): void => { + bench("long non-blank text", (): void => { + defaultIfBlank(longText, "fallback"); + }); + + bench("long whitespace-only input", (): void => { + defaultIfBlank(longWhitespace, "fallback"); + }); + + bench("non-whitespace at the beginning", (): void => { + defaultIfBlank(longWhitespaceWithTextAtStart, "fallback"); + }); + + bench("non-whitespace at the end", (): void => { + defaultIfBlank(longWhitespaceWithTextAtEnd, "fallback"); + }); + + bench("long text with surrounding whitespace", (): void => { + defaultIfBlank(longTextWithSurroundingWhitespace, "fallback"); + }); + }); + + test("given Unicode input", ({ bench }: TestContext): void => { + bench("CJK", (): void => { + defaultIfBlank("你好世界", "fallback"); + }); + + bench("Arabic", (): void => { + defaultIfBlank("مرحبا بالعالم", "fallback"); + }); + + bench("Thai", (): void => { + defaultIfBlank("สวัสดีชาวโลก", "fallback"); + }); + + bench("accented Latin", (): void => { + defaultIfBlank("café", "fallback"); + }); + + bench("combining marks", (): void => { + defaultIfBlank("cafe\u0301", "fallback"); + }); + }); + + test("given Unicode whitespace", ({ bench }: TestContext): void => { + bench("non-breaking spaces", (): void => { + defaultIfBlank("\u00A0\u00A0\u00A0", "fallback"); + }); + + bench("ideographic spaces", (): void => { + defaultIfBlank("\u3000\u3000\u3000", "fallback"); + }); + + bench("mixed Unicode whitespace", (): void => { + defaultIfBlank(longMixedWhitespace, "fallback"); + }); + }); + + test("given an empty or nullish value", ({ bench }: TestContext): void => { + bench("empty string", (): void => { + defaultIfBlank("", "fallback"); + }); + + bench("null", (): void => { + defaultIfBlank(null, "fallback"); + }); + + bench("undefined", (): void => { + defaultIfBlank(undefined, "fallback"); + }); + }); +}); From 692b7c29bcbf5e8830778a51f1e415e54fed2bab Mon Sep 17 00:00:00 2001 From: rcpthongta <220981657+rcpthongta@users.noreply.github.com> Date: Tue, 22 Sep 2026 02:17:36 +0700 Subject: [PATCH 045/121] feat(default-if-empty): add fallback utility for empty strings --- .../default-if-empty/default-if-empty.lib.ts | 20 +++++++++++++++++++ src/libs/default-if-empty/index.ts | 1 + src/libs/index.ts | 1 + 3 files changed, 22 insertions(+) create mode 100644 src/libs/default-if-empty/default-if-empty.lib.ts create mode 100644 src/libs/default-if-empty/index.ts diff --git a/src/libs/default-if-empty/default-if-empty.lib.ts b/src/libs/default-if-empty/default-if-empty.lib.ts new file mode 100644 index 0000000..24c1be3 --- /dev/null +++ b/src/libs/default-if-empty/default-if-empty.lib.ts @@ -0,0 +1,20 @@ +import { hasLength } from "../has-length"; + +/** + * Returns a default value when the given value is empty, `null`, or `undefined`. + * + * @param value - The string value to check, or `null` or `undefined`. + * @param defaultValue - The value to return when `value` is empty, `null`, or `undefined`. + * @returns The original `value` when it is a non-empty string; otherwise, `defaultValue`. + * + * @example + * + * defaultIfEmpty("Hello", "default"); // "Hello" + * defaultIfEmpty(" ", "default"); // " " + * defaultIfEmpty("", "default"); // "default" + * defaultIfEmpty(null, "default"); // "default" + * defaultIfEmpty(undefined, "default"); // "default" + */ +export function defaultIfEmpty(value: string | null | undefined, defaultValue: string): string { + return hasLength(value) ? value : defaultValue; +} diff --git a/src/libs/default-if-empty/index.ts b/src/libs/default-if-empty/index.ts new file mode 100644 index 0000000..e63c526 --- /dev/null +++ b/src/libs/default-if-empty/index.ts @@ -0,0 +1 @@ +export * from "./default-if-empty.lib"; diff --git a/src/libs/index.ts b/src/libs/index.ts index a0d16b4..1281c2c 100644 --- a/src/libs/index.ts +++ b/src/libs/index.ts @@ -1,3 +1,4 @@ export * from "./default-if-blank"; +export * from "./default-if-empty"; export * from "./has-length"; export * from "./has-text"; From ac02bcf832536123b59a464f83ec4f72df78d4c9 Mon Sep 17 00:00:00 2001 From: rcpthongta <220981657+rcpthongta@users.noreply.github.com> Date: Tue, 22 Sep 2026 02:18:21 +0700 Subject: [PATCH 046/121] test(default-if-empty): add unit tests for fallback empty string check --- .../default-if-empty.lib.spec.ts | 66 +++++++++++++++++++ 1 file changed, 66 insertions(+) create mode 100644 src/libs/default-if-empty/default-if-empty.lib.spec.ts diff --git a/src/libs/default-if-empty/default-if-empty.lib.spec.ts b/src/libs/default-if-empty/default-if-empty.lib.spec.ts new file mode 100644 index 0000000..98c5338 --- /dev/null +++ b/src/libs/default-if-empty/default-if-empty.lib.spec.ts @@ -0,0 +1,66 @@ +import { describe, expect, test } from "vitest"; + +import { defaultIfEmpty } from "./default-if-empty.lib"; + +describe("defaultIfEmpty", (): void => { + describe("given a non-empty string", (): void => { + test("returns the original string", (): void => { + expect(defaultIfEmpty("Hello", "default")).toBe("Hello"); + expect(defaultIfEmpty("Hello World", "default")).toBe("Hello World"); + expect(defaultIfEmpty("12345", "default")).toBe("12345"); + }); + + test("preserves whitespace-only strings", (): void => { + expect(defaultIfEmpty(" ", "default")).toBe(" "); + expect(defaultIfEmpty(" ", "default")).toBe(" "); + expect(defaultIfEmpty("\t\n\r", "default")).toBe("\t\n\r"); + expect(defaultIfEmpty(" \t\n\r ", "default")).toBe(" \t\n\r "); + }); + + test("preserves Unicode whitespace characters", (): void => { + expect(defaultIfEmpty("\u00A0", "default")).toBe("\u00A0"); + expect(defaultIfEmpty("\u2003", "default")).toBe("\u2003"); + expect(defaultIfEmpty("\u202F", "default")).toBe("\u202F"); + expect(defaultIfEmpty("\u3000", "default")).toBe("\u3000"); + }); + + test("preserves Unicode and accented characters", (): void => { + expect(defaultIfEmpty("สวัสดีชาวโลก", "default")).toBe("สวัสดีชาวโลก"); + expect(defaultIfEmpty("こんにちは", "default")).toBe("こんにちは"); + expect(defaultIfEmpty("café", "default")).toBe("café"); + expect(defaultIfEmpty("مرحبا بالعالم", "default")).toBe("مرحبا بالعالم"); + expect(defaultIfEmpty("cafe\u0301", "default")).toBe("cafe\u0301"); + }); + + test("preserves symbols and emojis", (): void => { + expect(defaultIfEmpty("🔥", "default")).toBe("🔥"); + expect(defaultIfEmpty("!@#$%^&*()", "default")).toBe("!@#$%^&*()"); + }); + + test("preserves strings containing only a single code unit", (): void => { + expect(defaultIfEmpty("a", "default")).toBe("a"); + expect(defaultIfEmpty("1", "default")).toBe("1"); + expect(defaultIfEmpty(" ", "default")).toBe(" "); + }); + + test("returns the exact original value without normalization", (): void => { + const value: string = " \t Hello World \n "; + + expect(defaultIfEmpty(value, "default")).toBe(value); + }); + }); + + describe("given an empty or nullish value", (): void => { + test("returns the default value for an empty string", (): void => { + expect(defaultIfEmpty("", "default")).toBe("default"); + }); + + test("returns the default value for null", (): void => { + expect(defaultIfEmpty(null, "default")).toBe("default"); + }); + + test("returns the default value for undefined", (): void => { + expect(defaultIfEmpty(undefined, "default")).toBe("default"); + }); + }); +}); From db311f49dbb9f4a9dada9cc3844d25d77d0be987 Mon Sep 17 00:00:00 2001 From: rcpthongta <220981657+rcpthongta@users.noreply.github.com> Date: Tue, 22 Sep 2026 02:19:00 +0700 Subject: [PATCH 047/121] perf(default-if-empty): add performance benchmarks for fallback check --- .../default-if-empty.lib.bench.ts | 125 ++++++++++++++++++ 1 file changed, 125 insertions(+) create mode 100644 src/libs/default-if-empty/default-if-empty.lib.bench.ts diff --git a/src/libs/default-if-empty/default-if-empty.lib.bench.ts b/src/libs/default-if-empty/default-if-empty.lib.bench.ts new file mode 100644 index 0000000..a5895c9 --- /dev/null +++ b/src/libs/default-if-empty/default-if-empty.lib.bench.ts @@ -0,0 +1,125 @@ +import { describe, test, TestContext } from "vitest"; + +import { defaultIfEmpty } from "./default-if-empty.lib"; + +const longText: string = "the quick brown fox jumps over the lazy dog ".repeat(100); +const longWhitespace: string = " ".repeat(1000); +const longSymbols: string = "!@#$%^&*()_+-=[]{}|;:',.<>?/`~".repeat(50); +const longUnicode: string = "你好世界 مرحبا بالعالم สวัสดี こんにちは café ".repeat(100); +const longTextWithLeadingWhitespace: string = " ".repeat(1000) + "hello"; +const longTextWithTrailingWhitespace: string = "hello" + " ".repeat(1000); +const input1KB: string = "the quick brown fox jumps over the lazy dog ".repeat(25); +const input10KB: string = "the quick brown fox jumps over the lazy dog ".repeat(250); +const input100KB: string = "the quick brown fox jumps over the lazy dog ".repeat(2500); + +describe("defaultIfEmpty", (): void => { + test("given common short inputs", ({ bench }: TestContext): void => { + bench("non-empty text", (): void => { + defaultIfEmpty("hello", "fallback"); + }); + + bench("single character", (): void => { + defaultIfEmpty("a", "fallback"); + }); + + bench("whitespace-only", (): void => { + defaultIfEmpty(" ", "fallback"); + }); + + bench("symbol-only", (): void => { + defaultIfEmpty("!@#$%^&*", "fallback"); + }); + }); + + test("given long inputs", ({ bench }: TestContext): void => { + bench("long text", (): void => { + defaultIfEmpty(longText, "fallback"); + }); + + bench("long whitespace-only input", (): void => { + defaultIfEmpty(longWhitespace, "fallback"); + }); + + bench("long symbols", (): void => { + defaultIfEmpty(longSymbols, "fallback"); + }); + + bench("long Unicode input", (): void => { + defaultIfEmpty(longUnicode, "fallback"); + }); + }); + + test("given text with surrounding whitespace", ({ bench }: TestContext): void => { + bench("long leading whitespace", (): void => { + defaultIfEmpty(longTextWithLeadingWhitespace, "fallback"); + }); + + bench("long trailing whitespace", (): void => { + defaultIfEmpty(longTextWithTrailingWhitespace, "fallback"); + }); + }); + + test("given inputs of increasing size", ({ bench }: TestContext): void => { + bench("approximately 1 KB input", (): void => { + defaultIfEmpty(input1KB, "fallback"); + }); + + bench("approximately 10 KB input", (): void => { + defaultIfEmpty(input10KB, "fallback"); + }); + + bench("approximately 100 KB input", (): void => { + defaultIfEmpty(input100KB, "fallback"); + }); + }); + + test("given Unicode input", ({ bench }: TestContext): void => { + bench("CJK", (): void => { + defaultIfEmpty("你好世界", "fallback"); + }); + + bench("Arabic", (): void => { + defaultIfEmpty("مرحبا بالعالم", "fallback"); + }); + + bench("Thai", (): void => { + defaultIfEmpty("สวัสดีชาวโลก", "fallback"); + }); + + bench("accented Latin", (): void => { + defaultIfEmpty("café", "fallback"); + }); + + bench("combining marks", (): void => { + defaultIfEmpty("cafe\u0301", "fallback"); + }); + }); + + test("given Unicode whitespace", ({ bench }: TestContext): void => { + bench("non-breaking space", (): void => { + defaultIfEmpty("\u00A0", "fallback"); + }); + + bench("ideographic space", (): void => { + defaultIfEmpty("\u3000", "fallback"); + }); + + bench("mixed Unicode whitespace", (): void => { + defaultIfEmpty(" \t\n\r\u00A0\u2003\u202F\u3000 ", "fallback"); + }); + }); + + test("given an empty or nullish value", ({ bench }: TestContext): void => { + bench("empty string", (): void => { + defaultIfEmpty("", "fallback"); + }); + + bench("null", (): void => { + defaultIfEmpty(null, "fallback"); + }); + + bench("undefined", (): void => { + defaultIfEmpty(undefined, "fallback"); + }); + }); +}); From ab016a516c2436b0ce6d2d9081b368e87b00cbdc Mon Sep 17 00:00:00 2001 From: rcpthongta <220981657+rcpthongta@users.noreply.github.com> Date: Tue, 22 Sep 2026 02:43:24 +0700 Subject: [PATCH 048/121] feat(constants): add EMPTY string constant --- src/constants/index.ts | 1 + src/constants/string-values.constant.ts | 1 + 2 files changed, 2 insertions(+) create mode 100644 src/constants/index.ts create mode 100644 src/constants/string-values.constant.ts diff --git a/src/constants/index.ts b/src/constants/index.ts new file mode 100644 index 0000000..e67e714 --- /dev/null +++ b/src/constants/index.ts @@ -0,0 +1 @@ +export * from "./string-values.constant"; diff --git a/src/constants/string-values.constant.ts b/src/constants/string-values.constant.ts new file mode 100644 index 0000000..62a3e5f --- /dev/null +++ b/src/constants/string-values.constant.ts @@ -0,0 +1 @@ +export const EMPTY: string = ""; From 9a4596657b1c6d5a90eea8404ba704081122a25d Mon Sep 17 00:00:00 2001 From: rcpthongta <220981657+rcpthongta@users.noreply.github.com> Date: Tue, 22 Sep 2026 02:44:09 +0700 Subject: [PATCH 049/121] feat(capitalize): add string first-character capitalization utility --- src/libs/capitalize/capitalize.lib.ts | 45 +++++++++++++++++++++++++++ src/libs/capitalize/index.ts | 1 + src/libs/index.ts | 1 + 3 files changed, 47 insertions(+) create mode 100644 src/libs/capitalize/capitalize.lib.ts create mode 100644 src/libs/capitalize/index.ts diff --git a/src/libs/capitalize/capitalize.lib.ts b/src/libs/capitalize/capitalize.lib.ts new file mode 100644 index 0000000..484bf3d --- /dev/null +++ b/src/libs/capitalize/capitalize.lib.ts @@ -0,0 +1,45 @@ +import { EMPTY } from "../../constants"; + +import { hasLength } from "../has-length"; + +/** + * Capitalizes the first character of a string while preserving its literal type. + * + * @param value - The string to capitalize. + * @returns The capitalized string with its literal type preserved. + * + * @example + * + * capitalize("hello"); // "Hello" + * capitalize("helloWorld"); // "HelloWorld" + * capitalize("Hello"); // "Hello" + */ +export function capitalize(value: T): Capitalize; + +/** + * Capitalizes the first character of a string. + * + * @param value - The string to capitalize, or `null` or `undefined`. + * @returns The capitalized string, or an empty string when `value` is empty, `null`, or `undefined`. + * + * @example + * + * capitalize("hello"); // "Hello" + * capitalize("helloWorld"); // "HelloWorld" + * capitalize("Hello"); // "Hello" + * capitalize(""); // "" + * capitalize(null); // "" + * capitalize(undefined); // "" + */ +export function capitalize(value: string | null | undefined): string; + +export function capitalize(value: string | null | undefined): string { + if (!hasLength(value)) { + return EMPTY; + } + + const firstChar: string = value[0]; + const upperFirst: string = firstChar.toUpperCase(); + + return firstChar === upperFirst ? value : upperFirst + value.slice(1); +} diff --git a/src/libs/capitalize/index.ts b/src/libs/capitalize/index.ts new file mode 100644 index 0000000..e930c16 --- /dev/null +++ b/src/libs/capitalize/index.ts @@ -0,0 +1 @@ +export * from "./capitalize.lib"; diff --git a/src/libs/index.ts b/src/libs/index.ts index 1281c2c..ee64990 100644 --- a/src/libs/index.ts +++ b/src/libs/index.ts @@ -1,3 +1,4 @@ +export * from "./capitalize"; export * from "./default-if-blank"; export * from "./default-if-empty"; export * from "./has-length"; From 69c246d57275e8cfe2aebe318a319f708e18ab0c Mon Sep 17 00:00:00 2001 From: rcpthongta <220981657+rcpthongta@users.noreply.github.com> Date: Tue, 22 Sep 2026 02:44:38 +0700 Subject: [PATCH 050/121] test(capitalize): add unit tests for string capitalization --- src/libs/capitalize/capitalize.lib.spec.ts | 125 +++++++++++++++++++++ 1 file changed, 125 insertions(+) create mode 100644 src/libs/capitalize/capitalize.lib.spec.ts diff --git a/src/libs/capitalize/capitalize.lib.spec.ts b/src/libs/capitalize/capitalize.lib.spec.ts new file mode 100644 index 0000000..37f99b4 --- /dev/null +++ b/src/libs/capitalize/capitalize.lib.spec.ts @@ -0,0 +1,125 @@ +import { describe, expect, expectTypeOf, test } from "vitest"; + +import { capitalize } from "./capitalize.lib"; + +describe("capitalize", (): void => { + describe("given a standard string", (): void => { + test("capitalizes the first lowercase character", (): void => { + expect(capitalize("hello")).toBe("Hello"); + expect(capitalize("hello world")).toBe("Hello world"); + expect(capitalize("typescript")).toBe("Typescript"); + }); + + test("preserves an already uppercase first character", (): void => { + expect(capitalize("Hello")).toBe("Hello"); + expect(capitalize("HELLO")).toBe("HELLO"); + expect(capitalize("Hello World")).toBe("Hello World"); + }); + + test("capitalizes only the first character", (): void => { + expect(capitalize("hELLO")).toBe("HELLO"); + expect(capitalize("hELLO wORLD")).toBe("HELLO wORLD"); + expect(capitalize("helloWORLD")).toBe("HelloWORLD"); + }); + + test("handles a single-character string", (): void => { + expect(capitalize("a")).toBe("A"); + expect(capitalize("A")).toBe("A"); + }); + }); + + describe("given leading whitespace", (): void => { + test("preserves leading whitespace without capitalizing the next character", (): void => { + expect(capitalize(" hello")).toBe(" hello"); + expect(capitalize("\thello")).toBe("\thello"); + expect(capitalize("\nhello")).toBe("\nhello"); + expect(capitalize("\r\nhello")).toBe("\r\nhello"); + expect(capitalize("\u00A0hello")).toBe("\u00A0hello"); + expect(capitalize("\u3000hello")).toBe("\u3000hello"); + }); + + test("preserves whitespace-only strings", (): void => { + expect(capitalize(" ")).toBe(" "); + expect(capitalize("\t")).toBe("\t"); + expect(capitalize("\n")).toBe("\n"); + expect(capitalize("\r\n")).toBe("\r\n"); + }); + }); + + describe("given Unicode and accented characters", (): void => { + test("capitalizes supported cased Unicode characters", (): void => { + expect(capitalize("éclair")).toBe("Éclair"); + expect(capitalize("ñandú")).toBe("Ñandú"); + expect(capitalize("über")).toBe("Über"); + expect(capitalize("ørebro")).toBe("Ørebro"); + expect(capitalize("σigma")).toBe("Σigma"); + }); + + test("preserves scripts without case transformation", (): void => { + expect(capitalize("สวัสดี")).toBe("สวัสดี"); + expect(capitalize("こんにちは")).toBe("こんにちは"); + }); + + test("preserves combining marks after the first character", (): void => { + expect(capitalize("cafe\u0301")).toBe("Cafe\u0301"); + expect(capitalize("e\u0301clair")).toBe("E\u0301clair"); + }); + + test("handles uppercase mappings that contain multiple characters", (): void => { + expect(capitalize("ßeta")).toBe("SSeta"); + }); + }); + + describe("given a string beginning with a non-letter character", (): void => { + test("preserves the string when the first character is a number", (): void => { + expect(capitalize("123hello")).toBe("123hello"); + }); + + test("preserves the string when the first character is a symbol", (): void => { + expect(capitalize("!hello")).toBe("!hello"); + expect(capitalize("@hello")).toBe("@hello"); + expect(capitalize("-hello")).toBe("-hello"); + expect(capitalize("_hello")).toBe("_hello"); + }); + + test("preserves the string when the first character is an emoji", (): void => { + expect(capitalize("🔥hello")).toBe("🔥hello"); + expect(capitalize("❤️hello")).toBe("❤️hello"); + expect(capitalize("©hello")).toBe("©hello"); + }); + }); + + describe("given a string literal", (): void => { + test("preserves the transformed literal type", (): void => { + expectTypeOf(capitalize("hello")).toEqualTypeOf<"Hello">(); + expectTypeOf(capitalize("Hello")).toEqualTypeOf<"Hello">(); + }); + }); + + describe("given an already capitalized result", (): void => { + test("is idempotent", (): void => { + const inputs: string[] = ["hello", "Hello", "HELLO", "éclair", "Éclair", "σigma", "123hello", "🔥hello"]; + + for (let index: number = 0; index < inputs.length; index++) { + const input: string = inputs[index]; + const result: string = capitalize(input); + + expect(capitalize(result)).toBe(result); + } + }); + }); + + describe("given an empty or nullish value", (): void => { + test("returns an empty string for an empty string", (): void => { + expect(capitalize("")).toBe(""); + }); + + test("returns an empty string for null", (): void => { + expect(capitalize(null)).toBe(""); + }); + + test("returns an empty string for undefined", (): void => { + expect(capitalize(undefined)).toBe(""); + }); + }); +}); From acb18664bb24c70d18cf0644acbe4b95674fa1dc Mon Sep 17 00:00:00 2001 From: rcpthongta <220981657+rcpthongta@users.noreply.github.com> Date: Tue, 22 Sep 2026 02:45:14 +0700 Subject: [PATCH 051/121] perf(capitalize): add performance benchmarks for string capitalization --- src/libs/capitalize/capitalize.lib.bench.ts | 124 ++++++++++++++++++++ 1 file changed, 124 insertions(+) create mode 100644 src/libs/capitalize/capitalize.lib.bench.ts diff --git a/src/libs/capitalize/capitalize.lib.bench.ts b/src/libs/capitalize/capitalize.lib.bench.ts new file mode 100644 index 0000000..f2c7f50 --- /dev/null +++ b/src/libs/capitalize/capitalize.lib.bench.ts @@ -0,0 +1,124 @@ +import { describe, test, TestContext } from "vitest"; + +import { capitalize } from "./capitalize.lib"; + +const longLowercaseString: string = "the quick brown fox jumps over the lazy dog ".repeat(100); +const longCapitalizedString: string = "The quick brown fox jumps over the lazy dog ".repeat(100); +const longCamelCaseString: string = "xmlHttpRequestResponseCodeHandler".repeat(100); +const longSymbols: string = "!@#$%^&*()_+-=[]{}|;:',.<>?/`~".repeat(100); +const longWhitespace: string = " ".repeat(1000); +const input1KB: string = "the quick brown fox jumps over the lazy dog ".repeat(25); +const input10KB: string = "the quick brown fox jumps over the lazy dog ".repeat(250); +const input100KB: string = "the quick brown fox jumps over the lazy dog ".repeat(2500); + +describe("capitalize", (): void => { + test("given common short ASCII inputs", ({ bench }: TestContext): void => { + bench("lowercase first character", (): void => { + capitalize("hello"); + }); + + bench("uppercase first character (no-op)", (): void => { + capitalize("Hello"); + }); + + bench("mixed-case string", (): void => { + capitalize("hELLO world"); + }); + + bench("single character", (): void => { + capitalize("a"); + }); + }); + + test("given long inputs", ({ bench }: TestContext): void => { + bench("long lowercase string", (): void => { + capitalize(longLowercaseString); + }); + + bench("long already-capitalized string", (): void => { + capitalize(longCapitalizedString); + }); + + bench("long camelCase string", (): void => { + capitalize(longCamelCaseString); + }); + }); + + test("given inputs of increasing size", ({ bench }: TestContext): void => { + bench("approximately 1 KB input", (): void => { + capitalize(input1KB); + }); + + bench("approximately 10 KB input", (): void => { + capitalize(input10KB); + }); + + bench("approximately 100 KB input", (): void => { + capitalize(input100KB); + }); + }); + + test("given Unicode input", ({ bench }: TestContext): void => { + bench("accented Latin", (): void => { + capitalize("élève"); + }); + + bench("combining marks", (): void => { + capitalize("cafe\u0301"); + }); + + bench("Cyrillic", (): void => { + capitalize("привет мир"); + }); + + bench("CJK", (): void => { + capitalize("你好世界"); + }); + + bench("Thai", (): void => { + capitalize("สวัสดี"); + }); + + bench("Arabic", (): void => { + capitalize("مرحبا بالعالم"); + }); + }); + + test("given non-transformable leading characters", ({ bench }: TestContext): void => { + bench("leading whitespace", (): void => { + capitalize(" hello world"); + }); + + bench("leading separator", (): void => { + capitalize("---hello world"); + }); + + bench("symbols only", (): void => { + capitalize(longSymbols); + }); + + bench("whitespace only", (): void => { + capitalize(longWhitespace); + }); + }); + + test("given a Unicode uppercase mapping", ({ bench }: TestContext): void => { + bench("character with multi-character uppercase mapping", (): void => { + capitalize("ßeta"); + }); + }); + + test("given an empty or nullish value", ({ bench }: TestContext): void => { + bench("empty string", (): void => { + capitalize(""); + }); + + bench("null", (): void => { + capitalize(null); + }); + + bench("undefined", (): void => { + capitalize(undefined); + }); + }); +}); From 0596d7460c85edc4c7e6575c49736e2d37c51a79 Mon Sep 17 00:00:00 2001 From: rcpthongta <220981657+rcpthongta@users.noreply.github.com> Date: Tue, 22 Sep 2026 02:59:55 +0700 Subject: [PATCH 052/121] feat(uncapitalize): add string first-character lowercase utility --- src/libs/index.ts | 1 + src/libs/uncapitalize/index.ts | 1 + src/libs/uncapitalize/uncapitalize.lib.ts | 45 +++++++++++++++++++++++ 3 files changed, 47 insertions(+) create mode 100644 src/libs/uncapitalize/index.ts create mode 100644 src/libs/uncapitalize/uncapitalize.lib.ts diff --git a/src/libs/index.ts b/src/libs/index.ts index ee64990..e55c11b 100644 --- a/src/libs/index.ts +++ b/src/libs/index.ts @@ -3,3 +3,4 @@ export * from "./default-if-blank"; export * from "./default-if-empty"; export * from "./has-length"; export * from "./has-text"; +export * from "./uncapitalize"; diff --git a/src/libs/uncapitalize/index.ts b/src/libs/uncapitalize/index.ts new file mode 100644 index 0000000..7229916 --- /dev/null +++ b/src/libs/uncapitalize/index.ts @@ -0,0 +1 @@ +export * from "./uncapitalize.lib"; diff --git a/src/libs/uncapitalize/uncapitalize.lib.ts b/src/libs/uncapitalize/uncapitalize.lib.ts new file mode 100644 index 0000000..3690b89 --- /dev/null +++ b/src/libs/uncapitalize/uncapitalize.lib.ts @@ -0,0 +1,45 @@ +import { EMPTY } from "../../constants"; + +import { hasLength } from "../has-length"; + +/** + * Converts the first character of a string to lowercase while preserving its literal type. + * + * @param value - The string to uncapitalize. + * @returns The uncapitalized string with its literal type preserved. + * + * @example + * + * uncapitalize("Hello"); // "hello" + * uncapitalize("HelloWorld"); // "helloWorld" + * uncapitalize("hello"); // "hello" + */ +export function uncapitalize(value: T): Uncapitalize; + +/** + * Converts the first character of a string to lowercase. + * + * @param value - The string to uncapitalize, or `null` or `undefined`. + * @returns The uncapitalized string, or an empty string when `value` is empty, `null`, or `undefined`. + * + * @example + * + * uncapitalize("Hello"); // "hello" + * uncapitalize("HelloWorld"); // "helloWorld" + * uncapitalize("hello"); // "hello" + * uncapitalize(""); // "" + * uncapitalize(null); // "" + * uncapitalize(undefined); // "" + */ +export function uncapitalize(value: string | null | undefined): string; + +export function uncapitalize(value: string | null | undefined): string { + if (!hasLength(value)) { + return EMPTY; + } + + const firstChar: string = value[0]; + const lowerFirst: string = firstChar.toLowerCase(); + + return firstChar === lowerFirst ? value : lowerFirst + value.slice(1); +} From 6ce699cfe6785d1351216fa49ba91defbf47db5b Mon Sep 17 00:00:00 2001 From: rcpthongta <220981657+rcpthongta@users.noreply.github.com> Date: Tue, 22 Sep 2026 03:00:44 +0700 Subject: [PATCH 053/121] test(uncapitalize): add unit tests for string uncapitalization --- .../uncapitalize/uncapitalize.lib.spec.ts | 103 ++++++++++++++++++ 1 file changed, 103 insertions(+) create mode 100644 src/libs/uncapitalize/uncapitalize.lib.spec.ts diff --git a/src/libs/uncapitalize/uncapitalize.lib.spec.ts b/src/libs/uncapitalize/uncapitalize.lib.spec.ts new file mode 100644 index 0000000..60f8826 --- /dev/null +++ b/src/libs/uncapitalize/uncapitalize.lib.spec.ts @@ -0,0 +1,103 @@ +import { describe, expect, expectTypeOf, test } from "vitest"; + +import { uncapitalize } from "./uncapitalize.lib"; + +describe("uncapitalize", (): void => { + describe("given a string", (): void => { + test("uncapitalizes an uppercase first character", (): void => { + expect(uncapitalize("Hello")).toBe("hello"); + expect(uncapitalize("Hello world")).toBe("hello world"); + expect(uncapitalize("Typescript")).toBe("typescript"); + }); + + test("preserves an already lowercase first character", (): void => { + expect(uncapitalize("hello")).toBe("hello"); + expect(uncapitalize("hello world")).toBe("hello world"); + expect(uncapitalize("helloWorld")).toBe("helloWorld"); + }); + + test("uncapitalizes only the first character", (): void => { + expect(uncapitalize("HELLO")).toBe("hELLO"); + expect(uncapitalize("HELLO wORLD")).toBe("hELLO wORLD"); + expect(uncapitalize("HELLOworld")).toBe("hELLOworld"); + }); + + test("handles a single-character string", (): void => { + expect(uncapitalize("A")).toBe("a"); + expect(uncapitalize("a")).toBe("a"); + }); + + test("preserves leading whitespace", (): void => { + expect(uncapitalize(" Hello")).toBe(" Hello"); + expect(uncapitalize("\tHello")).toBe("\tHello"); + expect(uncapitalize("\nHello")).toBe("\nHello"); + expect(uncapitalize("\r\nHello")).toBe("\r\nHello"); + expect(uncapitalize("\u00A0Hello")).toBe("\u00A0Hello"); + expect(uncapitalize("\u3000Hello")).toBe("\u3000Hello"); + }); + + test("preserves whitespace-only strings", (): void => { + expect(uncapitalize(" ")).toBe(" "); + expect(uncapitalize("\t")).toBe("\t"); + expect(uncapitalize("\n")).toBe("\n"); + }); + + test("supports Unicode and accented characters", (): void => { + expect(uncapitalize("Éclair")).toBe("éclair"); + expect(uncapitalize("Ñandú")).toBe("ñandú"); + expect(uncapitalize("Über")).toBe("über"); + expect(uncapitalize("Ørebro")).toBe("ørebro"); + expect(uncapitalize("Σigma")).toBe("σigma"); + expect(uncapitalize("สวัสดี")).toBe("สวัสดี"); + expect(uncapitalize("こんにちは")).toBe("こんにちは"); + }); + + test("preserves strings starting with non-letter characters", (): void => { + expect(uncapitalize("123Hello")).toBe("123Hello"); + expect(uncapitalize("!Hello")).toBe("!Hello"); + expect(uncapitalize("@Hello")).toBe("@Hello"); + expect(uncapitalize("-Hello")).toBe("-Hello"); + expect(uncapitalize("_Hello")).toBe("_Hello"); + }); + + test("preserves strings starting with symbols and emojis", (): void => { + expect(uncapitalize("🔥Hello")).toBe("🔥Hello"); + expect(uncapitalize("❤️Hello")).toBe("❤️Hello"); + expect(uncapitalize("©Hello")).toBe("©Hello"); + }); + }); + + describe("given a string literal", (): void => { + test("preserves the transformed literal type", (): void => { + expectTypeOf(uncapitalize("Hello")).toEqualTypeOf<"hello">(); + expectTypeOf(uncapitalize("hello")).toEqualTypeOf<"hello">(); + }); + }); + + describe("given an uncapitalized result", (): void => { + test("is idempotent", (): void => { + const inputs: string[] = ["Hello", "hello", "HELLO", "A", "a", " Hello", "Éclair", "123Hello", "🔥Hello"]; + + for (let index: number = 0; index < inputs.length; index++) { + const input: string = inputs[index]; + const result: string = uncapitalize(input); + + expect(uncapitalize(result)).toBe(result); + } + }); + }); + + describe("given an empty or nullish value", (): void => { + test("returns an empty string for an empty string", (): void => { + expect(uncapitalize("")).toBe(""); + }); + + test("returns an empty string for null", (): void => { + expect(uncapitalize(null)).toBe(""); + }); + + test("returns an empty string for undefined", (): void => { + expect(uncapitalize(undefined)).toBe(""); + }); + }); +}); From 66e92051cb9639097fed309528b085fe39743bea Mon Sep 17 00:00:00 2001 From: rcpthongta <220981657+rcpthongta@users.noreply.github.com> Date: Tue, 22 Sep 2026 03:01:48 +0700 Subject: [PATCH 054/121] perf(uncapitalize): add performance benchmarks for string uncapitalization --- .../uncapitalize/uncapitalize.lib.bench.ts | 135 ++++++++++++++++++ 1 file changed, 135 insertions(+) create mode 100644 src/libs/uncapitalize/uncapitalize.lib.bench.ts diff --git a/src/libs/uncapitalize/uncapitalize.lib.bench.ts b/src/libs/uncapitalize/uncapitalize.lib.bench.ts new file mode 100644 index 0000000..66c8d7b --- /dev/null +++ b/src/libs/uncapitalize/uncapitalize.lib.bench.ts @@ -0,0 +1,135 @@ +import { describe, test, TestContext } from "vitest"; + +import { uncapitalize } from "./uncapitalize.lib"; + +const longCapitalizedString: string = "The quick brown fox jumps over the lazy dog ".repeat(100); +const longLowercaseString: string = "the quick brown fox jumps over the lazy dog ".repeat(100); +const longPascalCaseString: string = "XmlHttpRequestResponseCodeHandler".repeat(100); +const longSymbols: string = "!@#$%^&*()".repeat(1000); +const longWhitespace: string = " ".repeat(1000); + +describe("uncapitalize", (): void => { + test("short ASCII", ({ bench }: TestContext): void => { + bench("uppercase first character", (): void => { + uncapitalize("Hello"); + }); + + bench("lowercase first character (no-op)", (): void => { + uncapitalize("hello"); + }); + + bench("single uppercase character", (): void => { + uncapitalize("A"); + }); + + bench("single lowercase character (no-op)", (): void => { + uncapitalize("a"); + }); + }); + + test("long ASCII", ({ bench }: TestContext): void => { + bench("uppercase first character", (): void => { + uncapitalize(longCapitalizedString); + }); + + bench("lowercase first character (no-op)", (): void => { + uncapitalize(longLowercaseString); + }); + + bench("PascalCase", (): void => { + uncapitalize(longPascalCaseString); + }); + }); + + test("Unicode", ({ bench }: TestContext): void => { + bench("Latin uppercase first character", (): void => { + uncapitalize("Élève"); + }); + + bench("Latin lowercase first character (no-op)", (): void => { + uncapitalize("élève"); + }); + + bench("Cyrillic uppercase first character", (): void => { + uncapitalize("Привет Мир"); + }); + + bench("Cyrillic lowercase first character (no-op)", (): void => { + uncapitalize("привет мир"); + }); + + bench("CJK (no-op)", (): void => { + uncapitalize("你好世界"); + }); + + bench("Thai (no-op)", (): void => { + uncapitalize("สวัสดี"); + }); + }); + + test("non-letter first character", ({ bench }: TestContext): void => { + bench("numbers", (): void => { + uncapitalize("123Hello"); + }); + + bench("underscore", (): void => { + uncapitalize("_Hello"); + }); + + bench("symbol", (): void => { + uncapitalize("@Hello"); + }); + + bench("emoji", (): void => { + uncapitalize("🔥Hello"); + }); + }); + + test("leading whitespace", ({ bench }: TestContext): void => { + bench("spaces", (): void => { + uncapitalize(" Hello World"); + }); + + bench("tabs", (): void => { + uncapitalize("\tHello World"); + }); + + bench("newlines", (): void => { + uncapitalize("\nHello World"); + }); + }); + + test("leading separators", ({ bench }: TestContext): void => { + bench("hyphens", (): void => { + uncapitalize("---Hello World"); + }); + + bench("symbols", (): void => { + uncapitalize("!@#$%^&*()Hello"); + }); + }); + + test("long no-op strings", ({ bench }: TestContext): void => { + bench("symbols only", (): void => { + uncapitalize(longSymbols); + }); + + bench("whitespace only", (): void => { + uncapitalize(longWhitespace); + }); + }); + + test("empty and nullish", ({ bench }: TestContext): void => { + bench("empty string", (): void => { + uncapitalize(""); + }); + + bench("null", (): void => { + uncapitalize(null); + }); + + bench("undefined", (): void => { + uncapitalize(undefined); + }); + }); +}); From 7c29a1b94ca74ace75baf63232f8e4d15922835c Mon Sep 17 00:00:00 2001 From: rcpthongta <220981657+rcpthongta@users.noreply.github.com> Date: Tue, 22 Sep 2026 03:02:45 +0700 Subject: [PATCH 055/121] chore(editor-config): add uncapitalize to cspell words list --- .vscode/settings.json | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 6c21d38..06a2d78 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -5,7 +5,10 @@ "cSpell.allowCompoundWords": true, "cSpell.ignoreRegExpList": ["/[\\u0E00-\\u0E7F]/g"], "cSpell.language": "en,en-US", - "cSpell.words": ["rcpthongta"], + "cSpell.words": [ + "rcpthongta", + "uncapitalize" + ], "editor.codeActionsOnSave": { "source.fixAll.eslint": "explicit" }, From 4ff64eab31eb45d9fff6894eb1779f34b5aba633 Mon Sep 17 00:00:00 2001 From: rcpthongta <220981657+rcpthongta@users.noreply.github.com> Date: Wed, 23 Sep 2026 00:58:03 +0700 Subject: [PATCH 056/121] feat(constants): add WORD_REGEX unicode word boundary pattern --- src/constants/index.ts | 1 + src/constants/regex.constant.ts | 10 ++++++++++ 2 files changed, 11 insertions(+) create mode 100644 src/constants/regex.constant.ts diff --git a/src/constants/index.ts b/src/constants/index.ts index e67e714..b1b8fa4 100644 --- a/src/constants/index.ts +++ b/src/constants/index.ts @@ -1 +1,2 @@ +export * from "./regex.constant"; export * from "./string-values.constant"; diff --git a/src/constants/regex.constant.ts b/src/constants/regex.constant.ts new file mode 100644 index 0000000..33425db --- /dev/null +++ b/src/constants/regex.constant.ts @@ -0,0 +1,10 @@ +/** + * Matches individual words, acronyms, and Unicode number sequences. + * + * The pattern recognizes: + * - Cased and non-cased words, including letters with combining marks. + * - Acronyms consisting of consecutive uppercase letters. + * - Unicode number sequences. + */ +export const WORD_REGEX: RegExp = + /(?:\p{Lu}\p{M}*)?[\p{Ll}\p{Lo}][\p{Ll}\p{Lo}\p{M}]*|\p{Lu}[\p{Lu}\p{M}]*(?![\p{Ll}\p{Lo}])|\p{N}+/gu; From 4e94a315a5673fc9895f7682afe2d742a171c131 Mon Sep 17 00:00:00 2001 From: rcpthongta <220981657+rcpthongta@users.noreply.github.com> Date: Wed, 23 Sep 2026 00:58:51 +0700 Subject: [PATCH 057/121] feat(camel-case): add camelCase string conversion utility --- src/libs/camel-case/camel-case.lib.ts | 52 +++++++++++++++++++++++++++ src/libs/camel-case/index.ts | 1 + src/libs/index.ts | 1 + 3 files changed, 54 insertions(+) create mode 100644 src/libs/camel-case/camel-case.lib.ts create mode 100644 src/libs/camel-case/index.ts diff --git a/src/libs/camel-case/camel-case.lib.ts b/src/libs/camel-case/camel-case.lib.ts new file mode 100644 index 0000000..1bcca16 --- /dev/null +++ b/src/libs/camel-case/camel-case.lib.ts @@ -0,0 +1,52 @@ +import { EMPTY, WORD_REGEX } from "../../constants"; + +import { hasLength } from "../has-length"; + +/** + * Converts a string to camelCase. + * + * Handles separators, existing casing styles, acronyms, numbers, Unicode characters, and combining marks. + * + * @param value - The string to convert to camelCase, or `null` or `undefined`. + * @returns The camelCase string, or an empty string when `value` is empty, `null`, or `undefined`, or contains no extractable words. + * + * @example + * + * camelCase("Foo Bar"); // "fooBar" + * camelCase("--foo-bar--"); // "fooBar" + * camelCase("FOO_BAR"); // "fooBar" + * camelCase("fooBar"); // "fooBar" + * camelCase("XMLHttpRequest"); // "xmlHttpRequest" + * camelCase("version 2.0"); // "version20" + * camelCase("Привет Мир"); // "приветМир" + * camelCase("你好 世界"); // "你好世界" + * camelCase("cafe\u0301 noir"); // "cafe\u0301Noir" + * camelCase("---"); // "" + * camelCase(""); // "" + * camelCase(null); // "" + * camelCase(undefined); // "" + */ +export function camelCase(value: string | null | undefined): string { + if (!hasLength(value)) { + return EMPTY; + } + + const words: RegExpMatchArray | null = value.match(WORD_REGEX); + + if (!words) { + return EMPTY; + } + + const length: number = words.length; + const firstWord: string = words[0]; + + let result: string = firstWord.toLowerCase(); + + for (let index: number = 1; index < length; index++) { + const word: string = words[index]; + + result += word[0].toUpperCase() + word.slice(1).toLowerCase(); + } + + return result; +} diff --git a/src/libs/camel-case/index.ts b/src/libs/camel-case/index.ts new file mode 100644 index 0000000..2f24c91 --- /dev/null +++ b/src/libs/camel-case/index.ts @@ -0,0 +1 @@ +export * from "./camel-case.lib"; diff --git a/src/libs/index.ts b/src/libs/index.ts index e55c11b..d6f50f1 100644 --- a/src/libs/index.ts +++ b/src/libs/index.ts @@ -1,3 +1,4 @@ +export * from "./camel-case"; export * from "./capitalize"; export * from "./default-if-blank"; export * from "./default-if-empty"; From 5324dfb8f48ee4d5b7a59c70f155ee822f107d41 Mon Sep 17 00:00:00 2001 From: rcpthongta <220981657+rcpthongta@users.noreply.github.com> Date: Wed, 23 Sep 2026 00:59:30 +0700 Subject: [PATCH 058/121] test(camel-case): add unit tests for camelCase string conversion --- src/libs/camel-case/camel-case.lib.spec.ts | 160 +++++++++++++++++++++ 1 file changed, 160 insertions(+) create mode 100644 src/libs/camel-case/camel-case.lib.spec.ts diff --git a/src/libs/camel-case/camel-case.lib.spec.ts b/src/libs/camel-case/camel-case.lib.spec.ts new file mode 100644 index 0000000..453f1db --- /dev/null +++ b/src/libs/camel-case/camel-case.lib.spec.ts @@ -0,0 +1,160 @@ +import { describe, expect, test } from "vitest"; + +import { camelCase } from "./camel-case.lib"; + +describe("camelCase", (): void => { + describe("given a standard string with separators", (): void => { + test("converts separated words to camelCase", (): void => { + expect(camelCase("foo-bar")).toBe("fooBar"); + expect(camelCase("foo_bar")).toBe("fooBar"); + expect(camelCase("foo.bar")).toBe("fooBar"); + expect(camelCase("foo bar")).toBe("fooBar"); + expect(camelCase("--foo-bar--")).toBe("fooBar"); + expect(camelCase("__foo_bar__")).toBe("fooBar"); + expect(camelCase(" foo bar ")).toBe("fooBar"); + }); + + test("handles mixed separators", (): void => { + expect(camelCase("foo-bar_baz.qux")).toBe("fooBarBazQux"); + expect(camelCase("foo / bar \\ baz")).toBe("fooBarBaz"); + expect(camelCase("foo!!!bar")).toBe("fooBar"); + expect(camelCase("foo---bar")).toBe("fooBar"); + expect(camelCase("foo...bar")).toBe("fooBar"); + }); + }); + + describe("given an uppercase string with separators", (): void => { + test("normalizes uppercase words to camelCase", (): void => { + expect(camelCase("FOO-BAR")).toBe("fooBar"); + expect(camelCase("FOO_BAR")).toBe("fooBar"); + expect(camelCase("FOO BAR")).toBe("fooBar"); + expect(camelCase("FOO-BAR-BAZ")).toBe("fooBarBaz"); + }); + }); + + describe("given a PascalCase string", (): void => { + test("converts PascalCase to camelCase", (): void => { + expect(camelCase("FooBar")).toBe("fooBar"); + expect(camelCase("FooBarBaz")).toBe("fooBarBaz"); + expect(camelCase("XMLHttpRequest")).toBe("xmlHttpRequest"); + }); + }); + + describe("given a camelCase string", (): void => { + test("preserves the existing camelCase structure", (): void => { + expect(camelCase("fooBar")).toBe("fooBar"); + expect(camelCase("fooBarBaz")).toBe("fooBarBaz"); + expect(camelCase("xmlHttpRequest")).toBe("xmlHttpRequest"); + }); + }); + + describe("given acronyms and mixed casing", (): void => { + test("handles acronym boundaries correctly", (): void => { + expect(camelCase("HTTPResponseCode")).toBe("httpResponseCode"); + expect(camelCase("JSONDataAPI")).toBe("jsonDataApi"); + expect(camelCase("XMLHttpRequest")).toBe("xmlHttpRequest"); + expect(camelCase("parseHTTPResponse")).toBe("parseHttpResponse"); + expect(camelCase("getURLValue")).toBe("getUrlValue"); + }); + }); + + describe("given numbers in strings", (): void => { + test("handles numbers as separate words", (): void => { + expect(camelCase("version 2.0")).toBe("version20"); + expect(camelCase("version-2-0")).toBe("version20"); + expect(camelCase("version_2_0")).toBe("version20"); + expect(camelCase("2foo")).toBe("2Foo"); + expect(camelCase("foo2bar")).toBe("foo2Bar"); + }); + + test("preserves numeric-only input", (): void => { + expect(camelCase("123")).toBe("123"); + expect(camelCase("123-456")).toBe("123456"); + expect(camelCase("123_456_789")).toBe("123456789"); + }); + }); + + describe("given accents and Unicode letters", (): void => { + test("handles accented Latin characters", (): void => { + expect(camelCase("crème-brûlée")).toBe("crèmeBrûlée"); + expect(camelCase("jalapeño popper")).toBe("jalapeñoPopper"); + expect(camelCase("École Normale")).toBe("écoleNormale"); + }); + + test("handles Cyrillic casing", (): void => { + expect(camelCase("Привет Мир")).toBe("приветМир"); + expect(camelCase("привет-мир")).toBe("приветМир"); + expect(camelCase("ПРИВЕТ МИР")).toBe("приветМир"); + }); + + test("preserves combining marks", (): void => { + expect(camelCase("cafe\u0301 noir")).toBe("cafe\u0301Noir"); + expect(camelCase("Cafe\u0301 Noir")).toBe("cafe\u0301Noir"); + }); + }); + + describe("given non-cased scripts", (): void => { + test("joins CJK, Hiragana, and Hangul without case transformation", (): void => { + expect(camelCase("こんにちは 世界")).toBe("こんにちは世界"); + expect(camelCase("你好 世界")).toBe("你好世界"); + expect(camelCase("안녕하세요 세계")).toBe("안녕하세요세계"); + }); + + test("joins Arabic words without case transformation", (): void => { + expect(camelCase("مرحبا بالعالم")).toBe("مرحبابالعالم"); + }); + + test("handles mixed Latin and non-cased scripts", (): void => { + expect(camelCase("hello 世界")).toBe("hello世界"); + expect(camelCase("foo-こんにちは")).toBe("fooこんにちは"); + expect(camelCase("Hello 世界")).toBe("hello世界"); + }); + }); + + describe("given strings containing only symbols or separators", (): void => { + test("returns an empty string when no words are extractable", (): void => { + expect(camelCase("---")).toBe(""); + expect(camelCase("___")).toBe(""); + expect(camelCase("...")).toBe(""); + expect(camelCase("!!!")).toBe(""); + expect(camelCase("@#$%^&*")).toBe(""); + }); + }); + + describe("given a camelCase result", (): void => { + test("is idempotent", (): void => { + const inputs: string[] = [ + "foo-bar", + "FOO_BAR", + "FooBar", + "HTTPResponseCode", + "JSONDataAPI", + "version-2-0", + "crème-brûlée", + "Привет Мир", + "hello 世界" + ]; + + for (let index: number = 0; index < inputs.length; index++) { + const input: string = inputs[index]; + const result: string = camelCase(input); + + expect(camelCase(result)).toBe(result); + } + }); + }); + + describe("given an empty or nullish value", (): void => { + test("returns an empty string for an empty string", (): void => { + expect(camelCase("")).toBe(""); + }); + + test("returns an empty string for null", (): void => { + expect(camelCase(null)).toBe(""); + }); + + test("returns an empty string for undefined", (): void => { + expect(camelCase(undefined)).toBe(""); + }); + }); +}); From 286bad5dcb04646c57a8292872e55a98dfe56e4d Mon Sep 17 00:00:00 2001 From: rcpthongta <220981657+rcpthongta@users.noreply.github.com> Date: Wed, 23 Sep 2026 01:00:43 +0700 Subject: [PATCH 059/121] perf(camel-case): add performance benchmarks for camelCase conversion --- src/libs/camel-case/camel-case.lib.bench.ts | 95 +++++++++++++++++++++ 1 file changed, 95 insertions(+) create mode 100644 src/libs/camel-case/camel-case.lib.bench.ts diff --git a/src/libs/camel-case/camel-case.lib.bench.ts b/src/libs/camel-case/camel-case.lib.bench.ts new file mode 100644 index 0000000..7171e79 --- /dev/null +++ b/src/libs/camel-case/camel-case.lib.bench.ts @@ -0,0 +1,95 @@ +import { describe, test, TestContext } from "vitest"; + +import { camelCase } from "./camel-case.lib"; + +const shortPascalCase: string = "XMLHttpRequestResponseCodeHandler"; +const input1KB: string = "get-HTTP-response-code-version-2.0-API ".repeat(20); +const input10KB: string = "get-HTTP-response-code-version-2.0-API ".repeat(200); +const input100KB: string = "get-HTTP-response-code-version-2.0-API ".repeat(2000); +const longUnicodeInput: string = "Привет-Мир café-noir 你好-世界 こんにちは-世界 مرحبا-بالعالم ".repeat(100); +const separatorHeavyInput: string = "---foo---bar___baz...qux!!!".repeat(1000); +const mixedCaseInput: string = "aAaAaAaAaAaAaAaAaAaAaAaAaAaAaA".repeat(1000); + +describe("camelCase", (): void => { + test("given common short ASCII inputs", ({ bench }: TestContext): void => { + bench("hyphen-separated lowercase", (): void => { + camelCase("foo-bar"); + }); + + bench("already camelCase", (): void => { + camelCase("fooBar"); + }); + + bench("PascalCase", (): void => { + camelCase(shortPascalCase); + }); + + bench("acronym followed by word", (): void => { + camelCase("HTTPResponseCode"); + }); + + bench("mixed words and numbers", (): void => { + camelCase("version-2-0"); + }); + }); + + test("given inputs of increasing size", ({ bench }: TestContext): void => { + bench("approximately 1 KB input", (): void => { + camelCase(input1KB); + }); + + bench("approximately 10 KB input", (): void => { + camelCase(input10KB); + }); + + bench("approximately 100 KB input", (): void => { + camelCase(input100KB); + }); + }); + + test("given Unicode input", ({ bench }: TestContext): void => { + bench("long Unicode input", (): void => { + camelCase(longUnicodeInput); + }); + + bench("combining marks", (): void => { + camelCase("cafe\u0301 noir"); + }); + }); + + test("given separator-heavy input", ({ bench }: TestContext): void => { + bench("long separator-heavy input", (): void => { + camelCase(separatorHeavyInput); + }); + }); + + test("given mixed-case input", ({ bench }: TestContext): void => { + bench("long mixed-case input", (): void => { + camelCase(mixedCaseInput); + }); + }); + + test("given simple fast-path inputs", ({ bench }: TestContext): void => { + bench("single word", (): void => { + camelCase("hello"); + }); + + bench("numeric-only input", (): void => { + camelCase("123456789"); + }); + }); + + test("given an empty or nullish value", ({ bench }: TestContext): void => { + bench("empty string", (): void => { + camelCase(""); + }); + + bench("null", (): void => { + camelCase(null); + }); + + bench("undefined", (): void => { + camelCase(undefined); + }); + }); +}); From 3c65e330fde8f9dc15efd8325a5fca08c6dfff3f Mon Sep 17 00:00:00 2001 From: rcpthongta <220981657+rcpthongta@users.noreply.github.com> Date: Wed, 23 Sep 2026 01:09:37 +0700 Subject: [PATCH 060/121] feat(pascal-case): add PascalCase string conversion utility --- src/libs/index.ts | 1 + src/libs/pascal-case/index.ts | 1 + src/libs/pascal-case/pascal-case.lib.ts | 52 +++++++++++++++++++++++++ 3 files changed, 54 insertions(+) create mode 100644 src/libs/pascal-case/index.ts create mode 100644 src/libs/pascal-case/pascal-case.lib.ts diff --git a/src/libs/index.ts b/src/libs/index.ts index d6f50f1..0321b64 100644 --- a/src/libs/index.ts +++ b/src/libs/index.ts @@ -4,4 +4,5 @@ export * from "./default-if-blank"; export * from "./default-if-empty"; export * from "./has-length"; export * from "./has-text"; +export * from "./pascal-case"; export * from "./uncapitalize"; diff --git a/src/libs/pascal-case/index.ts b/src/libs/pascal-case/index.ts new file mode 100644 index 0000000..9a53491 --- /dev/null +++ b/src/libs/pascal-case/index.ts @@ -0,0 +1 @@ +export * from "./pascal-case.lib"; diff --git a/src/libs/pascal-case/pascal-case.lib.ts b/src/libs/pascal-case/pascal-case.lib.ts new file mode 100644 index 0000000..29b0e51 --- /dev/null +++ b/src/libs/pascal-case/pascal-case.lib.ts @@ -0,0 +1,52 @@ +import { EMPTY, WORD_REGEX } from "../../constants"; + +import { hasLength } from "../has-length"; + +/** + * Converts a string to PascalCase. + * + * Handles separators, existing casing styles, acronyms, numbers, Unicode characters, and combining marks. + * + * @param value - The string to convert to PascalCase, or `null` or `undefined`. + * @returns The PascalCase string, or an empty string when `value` is empty, `null`, or `undefined`, or contains no extractable words. + * + * @example + * + * pascalCase("Foo Bar"); // "FooBar" + * pascalCase("--foo-bar--"); // "FooBar" + * pascalCase("FOO_BAR"); // "FooBar" + * pascalCase("fooBar"); // "FooBar" + * pascalCase("XMLHttpRequest"); // "XmlHttpRequest" + * pascalCase("version 2.0"); // "Version20" + * pascalCase("Привет Мир"); // "ПриветМир" + * pascalCase("你好 世界"); // "你好世界" + * pascalCase("cafe\u0301 noir"); // "Cafe\u0301Noir" + * pascalCase("---"); // "" + * pascalCase(""); // "" + * pascalCase(null); // "" + * pascalCase(undefined); // "" + */ +export function pascalCase(value: string | null | undefined): string { + if (!hasLength(value)) { + return EMPTY; + } + + const words: RegExpMatchArray | null = value.match(WORD_REGEX); + + if (!words) { + return EMPTY; + } + + const length: number = words.length; + const firstWord: string = words[0]; + + let result: string = firstWord[0].toUpperCase() + firstWord.slice(1).toLowerCase(); + + for (let index: number = 1; index < length; index++) { + const word: string = words[index]; + + result += word[0].toUpperCase() + word.slice(1).toLowerCase(); + } + + return result; +} From 09168d5f8968d598405d870521b40e2025042fcb Mon Sep 17 00:00:00 2001 From: rcpthongta <220981657+rcpthongta@users.noreply.github.com> Date: Wed, 23 Sep 2026 01:11:18 +0700 Subject: [PATCH 061/121] test(pascal-case): add unit tests for PascalCase string conversion --- src/libs/pascal-case/pascal-case.lib.spec.ts | 173 +++++++++++++++++++ 1 file changed, 173 insertions(+) create mode 100644 src/libs/pascal-case/pascal-case.lib.spec.ts diff --git a/src/libs/pascal-case/pascal-case.lib.spec.ts b/src/libs/pascal-case/pascal-case.lib.spec.ts new file mode 100644 index 0000000..37258e6 --- /dev/null +++ b/src/libs/pascal-case/pascal-case.lib.spec.ts @@ -0,0 +1,173 @@ +import { describe, expect, test } from "vitest"; + +import { pascalCase } from "./pascal-case.lib"; + +describe("pascalCase", (): void => { + describe("given a standard string with separators", (): void => { + test("returns the PascalCase string", (): void => { + expect(pascalCase("foo-bar")).toBe("FooBar"); + expect(pascalCase("foo_bar")).toBe("FooBar"); + expect(pascalCase("foo.bar")).toBe("FooBar"); + expect(pascalCase("foo bar")).toBe("FooBar"); + expect(pascalCase("--foo-bar--")).toBe("FooBar"); + expect(pascalCase("__foo_bar__")).toBe("FooBar"); + expect(pascalCase(" foo bar ")).toBe("FooBar"); + }); + + test("handles mixed separators", (): void => { + expect(pascalCase("foo-bar_baz.qux")).toBe("FooBarBazQux"); + expect(pascalCase("foo / bar \\ baz")).toBe("FooBarBaz"); + expect(pascalCase("foo!!!bar")).toBe("FooBar"); + expect(pascalCase("foo---bar")).toBe("FooBar"); + expect(pascalCase("foo...bar")).toBe("FooBar"); + }); + }); + + describe("given an uppercase string with separators", (): void => { + test("returns the PascalCase string", (): void => { + expect(pascalCase("FOO-BAR")).toBe("FooBar"); + expect(pascalCase("FOO_BAR")).toBe("FooBar"); + expect(pascalCase("FOO BAR")).toBe("FooBar"); + expect(pascalCase("FOO-BAR-BAZ")).toBe("FooBarBaz"); + }); + }); + + describe("given a camelCase string", (): void => { + test("returns the PascalCase string", (): void => { + expect(pascalCase("fooBar")).toBe("FooBar"); + expect(pascalCase("fooBarBaz")).toBe("FooBarBaz"); + expect(pascalCase("xmlHttpRequest")).toBe("XmlHttpRequest"); + }); + }); + + describe("given a PascalCase string", (): void => { + test("returns the original PascalCase string", (): void => { + expect(pascalCase("FooBar")).toBe("FooBar"); + expect(pascalCase("FooBarBaz")).toBe("FooBarBaz"); + expect(pascalCase("XmlHttpRequest")).toBe("XmlHttpRequest"); + }); + }); + + describe("given acronyms and mixed casing", (): void => { + test("handles consecutive uppercase letters", (): void => { + expect(pascalCase("HTTPResponseCode")).toBe("HttpResponseCode"); + expect(pascalCase("JSONDataAPI")).toBe("JsonDataApi"); + expect(pascalCase("XMLHttpRequest")).toBe("XmlHttpRequest"); + expect(pascalCase("parseHTTPResponse")).toBe("ParseHttpResponse"); + expect(pascalCase("getURLValue")).toBe("GetUrlValue"); + }); + }); + + describe("given strings containing numbers", (): void => { + test("handles numbers as separate words", (): void => { + expect(pascalCase("version 2.0")).toBe("Version20"); + expect(pascalCase("version-2-0")).toBe("Version20"); + expect(pascalCase("version_2_0")).toBe("Version20"); + expect(pascalCase("2foo")).toBe("2Foo"); + expect(pascalCase("foo2bar")).toBe("Foo2Bar"); + }); + + test("preserves numeric-only input", (): void => { + expect(pascalCase("123")).toBe("123"); + expect(pascalCase("123-456")).toBe("123456"); + expect(pascalCase("123_456_789")).toBe("123456789"); + }); + }); + + describe("given strings with accents and Unicode", (): void => { + test("handles accented letters as part of words", (): void => { + expect(pascalCase("crème-brûlée")).toBe("CrèmeBrûlée"); + expect(pascalCase("jalapeño popper")).toBe("JalapeñoPopper"); + expect(pascalCase("École Normale")).toBe("ÉcoleNormale"); + }); + + test("handles Cyrillic casing", (): void => { + expect(pascalCase("Привет Мир")).toBe("ПриветМир"); + expect(pascalCase("привет-мир")).toBe("ПриветМир"); + expect(pascalCase("ПРИВЕТ МИР")).toBe("ПриветМир"); + }); + + test("preserves combining marks", (): void => { + expect(pascalCase("cafe\u0301 noir")).toBe("Cafe\u0301Noir"); + expect(pascalCase("Cafe\u0301 Noir")).toBe("Cafe\u0301Noir"); + }); + }); + + describe("given non-cased scripts", (): void => { + test("joins CJK, Hiragana, and Hangul without case transformation", (): void => { + expect(pascalCase("こんにちは 世界")).toBe("こんにちは世界"); + expect(pascalCase("你好 世界")).toBe("你好世界"); + expect(pascalCase("안녕하세요 세계")).toBe("안녕하세요세계"); + }); + + test("joins Arabic words without case transformation", (): void => { + expect(pascalCase("مرحبا بالعالم")).toBe("مرحبابالعالم"); + }); + + test("handles mixed Latin and non-cased scripts", (): void => { + expect(pascalCase("hello 世界")).toBe("Hello世界"); + expect(pascalCase("Hello 世界")).toBe("Hello世界"); + expect(pascalCase("foo-こんにちは")).toBe("Fooこんにちは"); + expect(pascalCase("hello 世界 Привет")).toBe("Hello世界Привет"); + }); + }); + + describe("given strings containing only symbols or separators", (): void => { + test("returns an empty string when no words are extractable", (): void => { + expect(pascalCase("---")).toBe(""); + expect(pascalCase("___")).toBe(""); + expect(pascalCase("...")).toBe(""); + expect(pascalCase("!!!")).toBe(""); + expect(pascalCase("@#$%^&*")).toBe(""); + }); + }); + + describe("given a single word", (): void => { + test("returns the capitalized word", (): void => { + expect(pascalCase("hello")).toBe("Hello"); + expect(pascalCase("Hello")).toBe("Hello"); + expect(pascalCase("HELLO")).toBe("Hello"); + }); + + test("preserves a numeric-only word", (): void => { + expect(pascalCase("123456789")).toBe("123456789"); + }); + }); + + describe("given a PascalCase result", (): void => { + test("is idempotent", (): void => { + const inputs: string[] = [ + "foo-bar", + "FOO_BAR", + "FooBar", + "HTTPResponseCode", + "JSONDataAPI", + "version-2-0", + "crème-brûlée", + "Привет Мир", + "hello 世界" + ]; + + for (let index: number = 0; index < inputs.length; index++) { + const input: string = inputs[index]; + const result: string = pascalCase(input); + + expect(pascalCase(result)).toBe(result); + } + }); + }); + + describe("given an empty or nullish value", (): void => { + test("returns an empty string for an empty string", (): void => { + expect(pascalCase("")).toBe(""); + }); + + test("returns an empty string for null", (): void => { + expect(pascalCase(null)).toBe(""); + }); + + test("returns an empty string for undefined", (): void => { + expect(pascalCase(undefined)).toBe(""); + }); + }); +}); From 97eb18c1359fff065ff61d38052b232eb2301f68 Mon Sep 17 00:00:00 2001 From: rcpthongta <220981657+rcpthongta@users.noreply.github.com> Date: Wed, 23 Sep 2026 01:12:03 +0700 Subject: [PATCH 062/121] perf(pascal-case): add performance benchmarks for PascalCase conversion --- src/libs/pascal-case/pascal-case.lib.bench.ts | 111 ++++++++++++++++++ 1 file changed, 111 insertions(+) create mode 100644 src/libs/pascal-case/pascal-case.lib.bench.ts diff --git a/src/libs/pascal-case/pascal-case.lib.bench.ts b/src/libs/pascal-case/pascal-case.lib.bench.ts new file mode 100644 index 0000000..59b2bf7 --- /dev/null +++ b/src/libs/pascal-case/pascal-case.lib.bench.ts @@ -0,0 +1,111 @@ +import { describe, test, TestContext } from "vitest"; + +import { pascalCase } from "./pascal-case.lib"; + +const shortPascalCase: string = "XMLHttpRequestResponseCodeHandler"; +const input1KB: string = "get-HTTP-response-code-version-2.0-API ".repeat(20); +const input10KB: string = "get-HTTP-response-code-version-2.0-API ".repeat(200); +const input100KB: string = "get-HTTP-response-code-version-2.0-API ".repeat(2000); +const longUnicodeInput: string = "Привет-Мир café-noir 你好-世界 こんにちは-世界 مرحبا-بالعالم ".repeat(100); +const separatorHeavyInput: string = "---foo---bar___baz...qux!!!".repeat(1000); +const mixedCaseInput: string = "aAaAaAaAaAaAaAaAaAaAaAaAaAaAaA".repeat(1000); + +describe("pascalCase", (): void => { + test("given common short ASCII inputs", ({ bench }: TestContext): void => { + bench("hyphen-separated lowercase", (): void => { + pascalCase("foo-bar"); + }); + + bench("underscore-separated uppercase", (): void => { + pascalCase("FOO_BAR"); + }); + + bench("camelCase", (): void => { + pascalCase("fooBar"); + }); + + bench("already PascalCase", (): void => { + pascalCase(shortPascalCase); + }); + + bench("acronym followed by word", (): void => { + pascalCase("HTTPResponseCode"); + }); + + bench("mixed words and numbers", (): void => { + pascalCase("version-2-0"); + }); + }); + + test("given inputs of increasing size", ({ bench }: TestContext): void => { + bench("approximately 1 KB input", (): void => { + pascalCase(input1KB); + }); + + bench("approximately 10 KB input", (): void => { + pascalCase(input10KB); + }); + + bench("approximately 100 KB input", (): void => { + pascalCase(input100KB); + }); + }); + + test("given Unicode input", ({ bench }: TestContext): void => { + bench("long Unicode input", (): void => { + pascalCase(longUnicodeInput); + }); + + bench("combining marks", (): void => { + pascalCase("cafe\u0301 noir"); + }); + + bench("Cyrillic", (): void => { + pascalCase("Привет Мир"); + }); + + bench("CJK", (): void => { + pascalCase("你好 世界"); + }); + + bench("Arabic", (): void => { + pascalCase("مرحبا بالعالم"); + }); + }); + + test("given separator-heavy input", ({ bench }: TestContext): void => { + bench("long separator-heavy input", (): void => { + pascalCase(separatorHeavyInput); + }); + }); + + test("given mixed-case input", ({ bench }: TestContext): void => { + bench("long mixed-case input", (): void => { + pascalCase(mixedCaseInput); + }); + }); + + test("given simple inputs", ({ bench }: TestContext): void => { + bench("single word", (): void => { + pascalCase("hello"); + }); + + bench("numeric-only input", (): void => { + pascalCase("123456789"); + }); + }); + + test("given an empty or nullish value", ({ bench }: TestContext): void => { + bench("empty string", (): void => { + pascalCase(""); + }); + + bench("null", (): void => { + pascalCase(null); + }); + + bench("undefined", (): void => { + pascalCase(undefined); + }); + }); +}); From b17a193748cb15b852f677fba69a648f0c973916 Mon Sep 17 00:00:00 2001 From: rcpthongta <220981657+rcpthongta@users.noreply.github.com> Date: Wed, 23 Sep 2026 01:25:02 +0700 Subject: [PATCH 063/121] feat(constants): add HYPHEN string constant --- src/constants/string-values.constant.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/constants/string-values.constant.ts b/src/constants/string-values.constant.ts index 62a3e5f..0e05bad 100644 --- a/src/constants/string-values.constant.ts +++ b/src/constants/string-values.constant.ts @@ -1 +1,2 @@ export const EMPTY: string = ""; +export const HYPHEN: string = "-"; From 5331aed0cb1f27ac6122c5e03baf9a54c8c71bbe Mon Sep 17 00:00:00 2001 From: rcpthongta <220981657+rcpthongta@users.noreply.github.com> Date: Wed, 23 Sep 2026 01:26:00 +0700 Subject: [PATCH 064/121] feat(kebab-case): add kebab-case string conversion utility --- src/libs/index.ts | 1 + src/libs/kebab-case/index.ts | 1 + src/libs/kebab-case/kebab-case.lib.ts | 48 +++++++++++++++++++++++++++ 3 files changed, 50 insertions(+) create mode 100644 src/libs/kebab-case/index.ts create mode 100644 src/libs/kebab-case/kebab-case.lib.ts diff --git a/src/libs/index.ts b/src/libs/index.ts index 0321b64..1137570 100644 --- a/src/libs/index.ts +++ b/src/libs/index.ts @@ -4,5 +4,6 @@ export * from "./default-if-blank"; export * from "./default-if-empty"; export * from "./has-length"; export * from "./has-text"; +export * from "./kebab-case"; export * from "./pascal-case"; export * from "./uncapitalize"; diff --git a/src/libs/kebab-case/index.ts b/src/libs/kebab-case/index.ts new file mode 100644 index 0000000..78189db --- /dev/null +++ b/src/libs/kebab-case/index.ts @@ -0,0 +1 @@ +export * from "./kebab-case.lib"; diff --git a/src/libs/kebab-case/kebab-case.lib.ts b/src/libs/kebab-case/kebab-case.lib.ts new file mode 100644 index 0000000..6c66d53 --- /dev/null +++ b/src/libs/kebab-case/kebab-case.lib.ts @@ -0,0 +1,48 @@ +import { EMPTY, HYPHEN, WORD_REGEX } from "../../constants"; + +import { hasLength } from "../has-length"; + +/** + * Converts a string to kebab-case. + * + * Handles separators, existing casing styles, acronyms, numbers, Unicode characters, and combining marks. + * + * @param value - The string to convert to kebab-case, or `null` or `undefined`. + * @returns The kebab-case string, or an empty string when `value` is empty, `null`, or `undefined`, or contains no extractable words. + * + * @example + * + * kebabCase("Foo Bar"); // "foo-bar" + * kebabCase("--foo-bar--"); // "foo-bar" + * kebabCase("FOO_BAR"); // "foo-bar" + * kebabCase("fooBar"); // "foo-bar" + * kebabCase("XMLHttpRequest"); // "xml-http-request" + * kebabCase("version 2.0"); // "version-2-0" + * kebabCase("Привет Мир"); // "привет-мир" + * kebabCase("你好 世界"); // "你好-世界" + * kebabCase("cafe\u0301 noir"); // "cafe\u0301-noir" + * kebabCase(""); // "" + * kebabCase(null); // "" + * kebabCase(undefined); // "" + */ +export function kebabCase(value: string | null | undefined): string { + if (!hasLength(value)) { + return EMPTY; + } + + const words: RegExpMatchArray | null = value.match(WORD_REGEX); + + if (!words) { + return EMPTY; + } + + const length: number = words.length; + + let result: string = words[0].toLowerCase(); + + for (let index: number = 1; index < length; index++) { + result += HYPHEN + words[index].toLowerCase(); + } + + return result; +} From d34c2ed05ce826acda2e6445d74374703ca9c3d4 Mon Sep 17 00:00:00 2001 From: rcpthongta <220981657+rcpthongta@users.noreply.github.com> Date: Wed, 23 Sep 2026 01:30:09 +0700 Subject: [PATCH 065/121] test(kebab-case): add unit tests for kebab-case string conversion --- src/libs/kebab-case/kebab-case.lib.spec.ts | 193 +++++++++++++++++++++ 1 file changed, 193 insertions(+) create mode 100644 src/libs/kebab-case/kebab-case.lib.spec.ts diff --git a/src/libs/kebab-case/kebab-case.lib.spec.ts b/src/libs/kebab-case/kebab-case.lib.spec.ts new file mode 100644 index 0000000..aff893c --- /dev/null +++ b/src/libs/kebab-case/kebab-case.lib.spec.ts @@ -0,0 +1,193 @@ +import { describe, expect, test } from "vitest"; + +import { kebabCase } from "./kebab-case.lib"; + +describe("kebabCase", (): void => { + describe("given separated words", (): void => { + test("converts common separators to hyphens", (): void => { + expect(kebabCase("foo-bar")).toBe("foo-bar"); + expect(kebabCase("foo_bar")).toBe("foo-bar"); + expect(kebabCase("foo.bar")).toBe("foo-bar"); + expect(kebabCase("foo bar")).toBe("foo-bar"); + }); + + test("removes leading and trailing separators", (): void => { + expect(kebabCase("--foo-bar--")).toBe("foo-bar"); + expect(kebabCase("__foo_bar__")).toBe("foo-bar"); + expect(kebabCase("..foo.bar..")).toBe("foo-bar"); + expect(kebabCase(" foo bar ")).toBe("foo-bar"); + }); + + test("collapses mixed separators between words", (): void => { + expect(kebabCase("foo-bar_baz.qux")).toBe("foo-bar-baz-qux"); + expect(kebabCase("foo / bar \\ baz")).toBe("foo-bar-baz"); + expect(kebabCase("foo!!!bar???baz")).toBe("foo-bar-baz"); + }); + }); + + describe("given different casing styles", (): void => { + test("normalizes lowercase and uppercase words", (): void => { + expect(kebabCase("foo-bar")).toBe("foo-bar"); + expect(kebabCase("FOO-BAR")).toBe("foo-bar"); + expect(kebabCase("FOO_BAR_BAZ")).toBe("foo-bar-baz"); + }); + + test("converts camelCase to kebab-case", (): void => { + expect(kebabCase("fooBar")).toBe("foo-bar"); + expect(kebabCase("fooBarBaz")).toBe("foo-bar-baz"); + expect(kebabCase("xmlHttpRequest")).toBe("xml-http-request"); + }); + + test("converts PascalCase to kebab-case", (): void => { + expect(kebabCase("FooBar")).toBe("foo-bar"); + expect(kebabCase("FooBarBaz")).toBe("foo-bar-baz"); + expect(kebabCase("XmlHttpRequest")).toBe("xml-http-request"); + }); + }); + + describe("given acronyms", (): void => { + test("splits acronym boundaries correctly", (): void => { + expect(kebabCase("HTTPResponseCode")).toBe("http-response-code"); + expect(kebabCase("JSONDataAPI")).toBe("json-data-api"); + expect(kebabCase("XMLHttpRequest")).toBe("xml-http-request"); + }); + + test("handles acronyms at the beginning and end", (): void => { + expect(kebabCase("HTTPResponse")).toBe("http-response"); + expect(kebabCase("parseHTTP")).toBe("parse-http"); + expect(kebabCase("parseHTTPResponse")).toBe("parse-http-response"); + expect(kebabCase("getURLValue")).toBe("get-url-value"); + }); + + test("handles multiple acronyms separated by delimiters", (): void => { + expect(kebabCase("HTTP-JSON-API-Response")).toBe("http-json-api-response"); + }); + }); + + describe("given numbers", (): void => { + test("separates numbers from adjacent words", (): void => { + expect(kebabCase("2foo")).toBe("2-foo"); + expect(kebabCase("foo2")).toBe("foo-2"); + expect(kebabCase("foo2bar")).toBe("foo-2-bar"); + }); + + test("preserves numeric words", (): void => { + expect(kebabCase("123")).toBe("123"); + expect(kebabCase("123-456")).toBe("123-456"); + expect(kebabCase("123_456_789")).toBe("123-456-789"); + }); + + test("handles decimal-like input as separate words", (): void => { + expect(kebabCase("version 2.0")).toBe("version-2-0"); + expect(kebabCase("version-2-0")).toBe("version-2-0"); + }); + }); + + describe("given Unicode text", (): void => { + test("preserves accented Latin characters", (): void => { + expect(kebabCase("crème-brûlée")).toBe("crème-brûlée"); + expect(kebabCase("jalapeño popper")).toBe("jalapeño-popper"); + expect(kebabCase("École Normale")).toBe("école-normale"); + }); + + test("handles Cyrillic casing", (): void => { + expect(kebabCase("Привет Мир")).toBe("привет-мир"); + expect(kebabCase("привет-мир")).toBe("привет-мир"); + expect(kebabCase("ПРИВЕТ МИР")).toBe("привет-мир"); + }); + + test("preserves combining marks", (): void => { + expect(kebabCase("cafe\u0301 noir")).toBe("cafe\u0301-noir"); + expect(kebabCase("Cafe\u0301 Noir")).toBe("cafe\u0301-noir"); + }); + }); + + describe("given non-cased scripts", (): void => { + test("separates CJK words", (): void => { + expect(kebabCase("你好 世界")).toBe("你好-世界"); + }); + + test("separates Hiragana and Hangul words", (): void => { + expect(kebabCase("こんにちは 世界")).toBe("こんにちは-世界"); + expect(kebabCase("안녕하세요 세계")).toBe("안녕하세요-세계"); + }); + + test("separates Arabic words", (): void => { + expect(kebabCase("مرحبا بالعالم")).toBe("مرحبا-بالعالم"); + }); + + test("handles mixed scripts", (): void => { + expect(kebabCase("hello 世界")).toBe("hello-世界"); + expect(kebabCase("Hello 世界")).toBe("hello-世界"); + expect(kebabCase("foo-こんにちは")).toBe("foo-こんにちは"); + expect(kebabCase("hello 世界 Привет")).toBe("hello-世界-привет"); + }); + }); + + describe("given symbols and separators", (): void => { + test("ignores symbols between words", (): void => { + expect(kebabCase("foo!!!bar")).toBe("foo-bar"); + expect(kebabCase("foo---bar")).toBe("foo-bar"); + expect(kebabCase("foo...bar")).toBe("foo-bar"); + expect(kebabCase("foo___bar")).toBe("foo-bar"); + }); + + test("returns an empty string when no words are extractable", (): void => { + expect(kebabCase("---")).toBe(""); + expect(kebabCase("___")).toBe(""); + expect(kebabCase("...")).toBe(""); + expect(kebabCase("!!!")).toBe(""); + expect(kebabCase("@#$%^&*")).toBe(""); + }); + }); + + describe("given a single word", (): void => { + test("returns the normalized word without adding separators", (): void => { + expect(kebabCase("hello")).toBe("hello"); + expect(kebabCase("Hello")).toBe("hello"); + expect(kebabCase("HELLO")).toBe("hello"); + }); + + test("preserves a numeric-only word", (): void => { + expect(kebabCase("123456789")).toBe("123456789"); + }); + }); + + describe("given a kebab-case result", (): void => { + test("is idempotent", (): void => { + const inputs: string[] = [ + "foo-bar", + "FOO_BAR", + "fooBar", + "FooBar", + "HTTPResponseCode", + "JSONDataAPI", + "version-2-0", + "crème-brûlée", + "Привет Мир", + "hello 世界" + ]; + + for (let index: number = 0; index < inputs.length; index++) { + const input: string = inputs[index]; + const result: string = kebabCase(input); + + expect(kebabCase(result)).toBe(result); + } + }); + }); + + describe("given an empty or nullish value", (): void => { + test("returns an empty string for an empty string", (): void => { + expect(kebabCase("")).toBe(""); + }); + + test("returns an empty string for null", (): void => { + expect(kebabCase(null)).toBe(""); + }); + + test("returns an empty string for undefined", (): void => { + expect(kebabCase(undefined)).toBe(""); + }); + }); +}); From f24d0ef880b370488e0b23a06a69a83eb119ea86 Mon Sep 17 00:00:00 2001 From: rcpthongta <220981657+rcpthongta@users.noreply.github.com> Date: Wed, 23 Sep 2026 01:31:44 +0700 Subject: [PATCH 066/121] perf(kebab-case): add performance benchmarks for kebab-case conversion --- src/libs/kebab-case/kebab-case.lib.bench.ts | 184 ++++++++++++++++++++ 1 file changed, 184 insertions(+) create mode 100644 src/libs/kebab-case/kebab-case.lib.bench.ts diff --git a/src/libs/kebab-case/kebab-case.lib.bench.ts b/src/libs/kebab-case/kebab-case.lib.bench.ts new file mode 100644 index 0000000..07242fb --- /dev/null +++ b/src/libs/kebab-case/kebab-case.lib.bench.ts @@ -0,0 +1,184 @@ +import { describe, test, TestContext } from "vitest"; + +import { kebabCase } from "./kebab-case.lib"; + +const longSlug: string = "get-HTTP-response-code-version-2.0-API ".repeat(100); +const longPascalCase: string = "XMLHttpRequestResponseCodeHandler".repeat(100); +const longCamelCase: string = "xmlHttpRequestResponseCodeHandler".repeat(100); +const longAllCaps: string = "MAX_RETRY_COUNT_FOR_HTTP_500_ERRORS ".repeat(100); +const longUnicode: string = "Привет-Мир café-noir 你好-世界 こんにちは-世界 مرحبا-بالعالم ".repeat(100); +const longSeparators: string = "---foo---bar___baz...qux!!!".repeat(100); +const longWhitespace: string = " ".repeat(1_000); +const longSymbols: string = "!@#$%^&*()_+-=[]{}|;:',.<>?/`~".repeat(100); + +describe("kebabCase", (): void => { + test("given common short inputs", ({ bench }: TestContext): void => { + bench("hyphen-separated lowercase", (): void => { + kebabCase("foo-bar"); + }); + + bench("underscore-separated uppercase", (): void => { + kebabCase("FOO_BAR"); + }); + + bench("camelCase", (): void => { + kebabCase("fooBar"); + }); + + bench("PascalCase", (): void => { + kebabCase("FooBar"); + }); + + bench("single word", (): void => { + kebabCase("hello"); + }); + + bench("single character", (): void => { + kebabCase("A"); + }); + }); + + test("given numbers and acronyms", ({ bench }: TestContext): void => { + bench("numeric-only input", (): void => { + kebabCase("123456789"); + }); + + bench("word with numbers", (): void => { + kebabCase("version-2-0"); + }); + + bench("number followed by word", (): void => { + kebabCase("2foo"); + }); + + bench("word followed by number", (): void => { + kebabCase("foo2bar"); + }); + + bench("acronym followed by word", (): void => { + kebabCase("HTTPResponse"); + }); + + bench("word followed by acronym", (): void => { + kebabCase("parseHTTP"); + }); + + bench("multiple acronyms", (): void => { + kebabCase("HTTPJSONAPIResponse"); + }); + }); + + test("given long inputs", ({ bench }: TestContext): void => { + bench("long slug with acronyms and numbers", (): void => { + kebabCase(longSlug); + }); + + bench("long PascalCase with acronyms", (): void => { + kebabCase(longPascalCase); + }); + + bench("long camelCase with acronyms", (): void => { + kebabCase(longCamelCase); + }); + + bench("long ALL_CAPS with numbers", (): void => { + kebabCase(longAllCaps); + }); + + bench("long Unicode input", (): void => { + kebabCase(longUnicode); + }); + }); + + test("given Unicode input", ({ bench }: TestContext): void => { + bench("accented Latin", (): void => { + kebabCase("crème-brûlée"); + }); + + bench("combining marks", (): void => { + kebabCase("cafe\u0301 noir"); + }); + + bench("Cyrillic", (): void => { + kebabCase("Привет Мир"); + }); + + bench("CJK", (): void => { + kebabCase("你好 世界"); + }); + + bench("Hiragana", (): void => { + kebabCase("こんにちは 世界"); + }); + + bench("Hangul", (): void => { + kebabCase("안녕하세요 세계"); + }); + + bench("Arabic", (): void => { + kebabCase("مرحبا بالعالم"); + }); + + bench("Thai", (): void => { + kebabCase("สวัสดี โลก"); + }); + + bench("mixed scripts", (): void => { + kebabCase("hello 世界 Привет"); + }); + }); + + test("given excessive separators", ({ bench }: TestContext): void => { + bench("leading and trailing separators", (): void => { + kebabCase("---foo---bar---"); + }); + + bench("repeated symbols", (): void => { + kebabCase("...___foo!!!bar???baz..."); + }); + + bench("repeated whitespace", (): void => { + kebabCase(" foo bar baz "); + }); + + bench("mixed separators", (): void => { + kebabCase(" -- foo __ bar .. baz -- "); + }); + + bench("long separator-heavy input", (): void => { + kebabCase(longSeparators); + }); + }); + + test("given non-extractable input", ({ bench }: TestContext): void => { + bench("symbol-only input", (): void => { + kebabCase("!@#$%^&*()_+-=[]{}"); + }); + + bench("long symbol-only input", (): void => { + kebabCase(longSymbols); + }); + + bench("separator-only input", (): void => { + kebabCase("---___..."); + }); + + bench("long whitespace-only input", (): void => { + kebabCase(longWhitespace); + }); + }); + + test("given an empty or nullish value", ({ bench }: TestContext): void => { + bench("empty string", (): void => { + kebabCase(""); + }); + + bench("null", (): void => { + kebabCase(null); + }); + + bench("undefined", (): void => { + kebabCase(undefined); + }); + }); +}); From 25b02112ae80dc1814ce7e42ba858f3b9f67b4a6 Mon Sep 17 00:00:00 2001 From: rcpthongta <220981657+rcpthongta@users.noreply.github.com> Date: Wed, 23 Sep 2026 01:43:10 +0700 Subject: [PATCH 067/121] feat(constants): add UNDERSCORE string constant --- src/constants/string-values.constant.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/constants/string-values.constant.ts b/src/constants/string-values.constant.ts index 0e05bad..e863b70 100644 --- a/src/constants/string-values.constant.ts +++ b/src/constants/string-values.constant.ts @@ -1,2 +1,3 @@ export const EMPTY: string = ""; export const HYPHEN: string = "-"; +export const UNDERSCORE: string = "_"; From 1bb8a36646b4e2e481853aec84d7f6c8073492b8 Mon Sep 17 00:00:00 2001 From: rcpthongta <220981657+rcpthongta@users.noreply.github.com> Date: Wed, 23 Sep 2026 01:44:23 +0700 Subject: [PATCH 068/121] feat(snake-case): add snake_case string conversion utility --- src/libs/index.ts | 1 + src/libs/snake-case/index.ts | 1 + src/libs/snake-case/snake-case.lib.ts | 48 +++++++++++++++++++++++++++ 3 files changed, 50 insertions(+) create mode 100644 src/libs/snake-case/index.ts create mode 100644 src/libs/snake-case/snake-case.lib.ts diff --git a/src/libs/index.ts b/src/libs/index.ts index 1137570..94df5a7 100644 --- a/src/libs/index.ts +++ b/src/libs/index.ts @@ -6,4 +6,5 @@ export * from "./has-length"; export * from "./has-text"; export * from "./kebab-case"; export * from "./pascal-case"; +export * from "./snake-case"; export * from "./uncapitalize"; diff --git a/src/libs/snake-case/index.ts b/src/libs/snake-case/index.ts new file mode 100644 index 0000000..0ed0148 --- /dev/null +++ b/src/libs/snake-case/index.ts @@ -0,0 +1 @@ +export * from "./snake-case.lib"; diff --git a/src/libs/snake-case/snake-case.lib.ts b/src/libs/snake-case/snake-case.lib.ts new file mode 100644 index 0000000..6745b1e --- /dev/null +++ b/src/libs/snake-case/snake-case.lib.ts @@ -0,0 +1,48 @@ +import { EMPTY, UNDERSCORE, WORD_REGEX } from "../../constants"; + +import { hasLength } from "../has-length"; + +/** + * Converts a string to snake_case. + * + * Handles separators, existing casing styles, acronyms, numbers, Unicode characters, and combining marks. + * + * @param value - The string to convert to snake_case, or `null` or `undefined`. + * @returns The snake_case string, or an empty string when `value` is empty, `null`, or `undefined`, or contains no extractable words. + * + * @example + * + * snakeCase("Foo Bar"); // "foo_bar" + * snakeCase("--foo-bar--"); // "foo_bar" + * snakeCase("FOO_BAR"); // "foo_bar" + * snakeCase("fooBar"); // "foo_bar" + * snakeCase("XMLHttpRequest"); // "xml_http_request" + * snakeCase("version 2.0"); // "version_2_0" + * snakeCase("Привет Мир"); // "привет_мир" + * snakeCase("你好 世界"); // "你好_世界" + * snakeCase("cafe\u0301 noir"); // "cafe\u0301_noir" + * snakeCase(""); // "" + * snakeCase(null); // "" + * snakeCase(undefined); // "" + */ +export function snakeCase(value: string | null | undefined): string { + if (!hasLength(value)) { + return EMPTY; + } + + const words: RegExpMatchArray | null = value.match(WORD_REGEX); + + if (!words) { + return EMPTY; + } + + const length: number = words.length; + + let result: string = words[0].toLowerCase(); + + for (let index: number = 1; index < length; index++) { + result += UNDERSCORE + words[index].toLowerCase(); + } + + return result; +} From a5eeca9f820f0b63805ceab52bdda9be5df3b462 Mon Sep 17 00:00:00 2001 From: rcpthongta <220981657+rcpthongta@users.noreply.github.com> Date: Wed, 23 Sep 2026 01:44:53 +0700 Subject: [PATCH 069/121] test(snake-case): add unit tests for snake_case string conversion --- src/libs/snake-case/snake-case.lib.spec.ts | 174 +++++++++++++++++++++ 1 file changed, 174 insertions(+) create mode 100644 src/libs/snake-case/snake-case.lib.spec.ts diff --git a/src/libs/snake-case/snake-case.lib.spec.ts b/src/libs/snake-case/snake-case.lib.spec.ts new file mode 100644 index 0000000..3c1a119 --- /dev/null +++ b/src/libs/snake-case/snake-case.lib.spec.ts @@ -0,0 +1,174 @@ +import { describe, expect, test } from "vitest"; + +import { snakeCase } from "./snake-case.lib"; + +describe("snakeCase", (): void => { + describe("given a standard string with separators", (): void => { + test("returns the snake_case string", (): void => { + expect(snakeCase("foo-bar")).toBe("foo_bar"); + expect(snakeCase("foo_bar")).toBe("foo_bar"); + expect(snakeCase("foo.bar")).toBe("foo_bar"); + expect(snakeCase("foo bar")).toBe("foo_bar"); + expect(snakeCase("--foo-bar--")).toBe("foo_bar"); + expect(snakeCase("__foo_bar__")).toBe("foo_bar"); + expect(snakeCase(" foo bar ")).toBe("foo_bar"); + }); + + test("handles mixed separators", (): void => { + expect(snakeCase("foo-bar_baz.qux")).toBe("foo_bar_baz_qux"); + expect(snakeCase("foo / bar \\ baz")).toBe("foo_bar_baz"); + expect(snakeCase("foo!!!bar")).toBe("foo_bar"); + expect(snakeCase("foo---bar")).toBe("foo_bar"); + expect(snakeCase("foo...bar")).toBe("foo_bar"); + }); + }); + + describe("given an uppercase string with separators", (): void => { + test("returns the snake_case string", (): void => { + expect(snakeCase("FOO-BAR")).toBe("foo_bar"); + expect(snakeCase("FOO_BAR")).toBe("foo_bar"); + expect(snakeCase("FOO BAR")).toBe("foo_bar"); + expect(snakeCase("FOO-BAR-BAZ")).toBe("foo_bar_baz"); + }); + }); + + describe("given a camelCase string", (): void => { + test("returns the snake_case string", (): void => { + expect(snakeCase("fooBar")).toBe("foo_bar"); + expect(snakeCase("fooBarBaz")).toBe("foo_bar_baz"); + expect(snakeCase("xmlHttpRequest")).toBe("xml_http_request"); + }); + }); + + describe("given a PascalCase string", (): void => { + test("returns the snake_case string", (): void => { + expect(snakeCase("FooBar")).toBe("foo_bar"); + expect(snakeCase("FooBarBaz")).toBe("foo_bar_baz"); + expect(snakeCase("XmlHttpRequest")).toBe("xml_http_request"); + }); + }); + + describe("given acronyms and mixed casing", (): void => { + test("handles consecutive uppercase letters", (): void => { + expect(snakeCase("HTTPResponseCode")).toBe("http_response_code"); + expect(snakeCase("JSONDataAPI")).toBe("json_data_api"); + expect(snakeCase("XMLHttpRequest")).toBe("xml_http_request"); + expect(snakeCase("parseHTTPResponse")).toBe("parse_http_response"); + expect(snakeCase("getURLValue")).toBe("get_url_value"); + }); + }); + + describe("given strings containing numbers", (): void => { + test("handles numbers as separate words", (): void => { + expect(snakeCase("version 2.0")).toBe("version_2_0"); + expect(snakeCase("version-2-0")).toBe("version_2_0"); + expect(snakeCase("version_2_0")).toBe("version_2_0"); + expect(snakeCase("2foo")).toBe("2_foo"); + expect(snakeCase("foo2bar")).toBe("foo_2_bar"); + }); + + test("preserves numeric-only input", (): void => { + expect(snakeCase("123")).toBe("123"); + expect(snakeCase("123-456")).toBe("123_456"); + expect(snakeCase("123_456_789")).toBe("123_456_789"); + }); + }); + + describe("given strings with accents and Unicode", (): void => { + test("handles accented letters as part of words", (): void => { + expect(snakeCase("crème-brûlée")).toBe("crème_brûlée"); + expect(snakeCase("jalapeño popper")).toBe("jalapeño_popper"); + expect(snakeCase("École Normale")).toBe("école_normale"); + }); + + test("handles Cyrillic casing", (): void => { + expect(snakeCase("Привет Мир")).toBe("привет_мир"); + expect(snakeCase("привет-мир")).toBe("привет_мир"); + expect(snakeCase("ПРИВЕТ МИР")).toBe("привет_мир"); + }); + + test("preserves combining marks", (): void => { + expect(snakeCase("cafe\u0301 noir")).toBe("cafe\u0301_noir"); + expect(snakeCase("Cafe\u0301 Noir")).toBe("cafe\u0301_noir"); + }); + }); + + describe("given non-cased scripts", (): void => { + test("joins CJK, Hiragana, and Hangul without case transformation", (): void => { + expect(snakeCase("こんにちは 世界")).toBe("こんにちは_世界"); + expect(snakeCase("你好 世界")).toBe("你好_世界"); + expect(snakeCase("안녕하세요 세계")).toBe("안녕하세요_세계"); + }); + + test("joins Arabic words without case transformation", (): void => { + expect(snakeCase("مرحبا بالعالم")).toBe("مرحبا_بالعالم"); + }); + + test("handles mixed Latin and non-cased scripts", (): void => { + expect(snakeCase("hello 世界")).toBe("hello_世界"); + expect(snakeCase("Hello 世界")).toBe("hello_世界"); + expect(snakeCase("foo-こんにちは")).toBe("foo_こんにちは"); + expect(snakeCase("hello 世界 Привет")).toBe("hello_世界_привет"); + }); + }); + + describe("given strings containing only symbols or separators", (): void => { + test("returns an empty string when no words are extractable", (): void => { + expect(snakeCase("---")).toBe(""); + expect(snakeCase("___")).toBe(""); + expect(snakeCase("...")).toBe(""); + expect(snakeCase("!!!")).toBe(""); + expect(snakeCase("@#$%^&*")).toBe(""); + }); + }); + + describe("given a single word", (): void => { + test("returns the normalized word without adding separators", (): void => { + expect(snakeCase("hello")).toBe("hello"); + expect(snakeCase("Hello")).toBe("hello"); + expect(snakeCase("HELLO")).toBe("hello"); + }); + + test("preserves a numeric-only word", (): void => { + expect(snakeCase("123456789")).toBe("123456789"); + }); + }); + + describe("given a snake_case result", (): void => { + test("is idempotent", (): void => { + const inputs: string[] = [ + "foo-bar", + "FOO_BAR", + "fooBar", + "FooBar", + "HTTPResponseCode", + "JSONDataAPI", + "version-2-0", + "crème-brûlée", + "Привет Мир", + "hello 世界" + ]; + + for (let index: number = 0; index < inputs.length; index++) { + const input: string = inputs[index]; + const result: string = snakeCase(input); + + expect(snakeCase(result)).toBe(result); + } + }); + }); + + describe("given an empty or nullish value", (): void => { + test("returns an empty string for an empty string", (): void => { + expect(snakeCase("")).toBe(""); + }); + + test("returns an empty string for null", (): void => { + expect(snakeCase(null)).toBe(""); + }); + + test("returns an empty string for undefined", (): void => { + expect(snakeCase(undefined)).toBe(""); + }); + }); +}); From 23bb872998d907f30a0577af9ca45ae142cacadf Mon Sep 17 00:00:00 2001 From: rcpthongta <220981657+rcpthongta@users.noreply.github.com> Date: Wed, 23 Sep 2026 01:45:25 +0700 Subject: [PATCH 070/121] perf(snake-case): add performance benchmarks for snake_case conversion --- src/libs/snake-case/snake-case.lib.bench.ts | 115 ++++++++++++++++++++ 1 file changed, 115 insertions(+) create mode 100644 src/libs/snake-case/snake-case.lib.bench.ts diff --git a/src/libs/snake-case/snake-case.lib.bench.ts b/src/libs/snake-case/snake-case.lib.bench.ts new file mode 100644 index 0000000..268db77 --- /dev/null +++ b/src/libs/snake-case/snake-case.lib.bench.ts @@ -0,0 +1,115 @@ +import { describe, test, TestContext } from "vitest"; + +import { snakeCase } from "./snake-case.lib"; + +const shortPascalCase: string = "XMLHttpRequestResponseCodeHandler"; +const input1KB: string = "get-HTTP-response-code-version-2.0-API ".repeat(20); +const input10KB: string = "get-HTTP-response-code-version-2.0-API ".repeat(200); +const input100KB: string = "get-HTTP-response-code-version-2.0-API ".repeat(2000); +const longUnicodeInput: string = "Привет-Мир café-noir 你好-世界 こんにちは-世界 مرحبا-بالعالم ".repeat(100); +const separatorHeavyInput: string = "---foo---bar___baz...qux!!!".repeat(1000); +const mixedCaseInput: string = "aAaAaAaAaAaAaAaAaAaAaAaAaAaAaA".repeat(1000); + +describe("snakeCase", (): void => { + test("given common short ASCII inputs", ({ bench }: TestContext): void => { + bench("hyphen-separated lowercase", (): void => { + snakeCase("foo-bar"); + }); + + bench("underscore-separated uppercase", (): void => { + snakeCase("FOO_BAR"); + }); + + bench("camelCase", (): void => { + snakeCase("fooBar"); + }); + + bench("already snake_case", (): void => { + snakeCase("foo_bar"); + }); + + bench("PascalCase", (): void => { + snakeCase(shortPascalCase); + }); + + bench("acronym followed by word", (): void => { + snakeCase("HTTPResponseCode"); + }); + + bench("mixed words and numbers", (): void => { + snakeCase("version-2-0"); + }); + }); + + test("given inputs of increasing size", ({ bench }: TestContext): void => { + bench("approximately 1 KB input", (): void => { + snakeCase(input1KB); + }); + + bench("approximately 10 KB input", (): void => { + snakeCase(input10KB); + }); + + bench("approximately 100 KB input", (): void => { + snakeCase(input100KB); + }); + }); + + test("given Unicode input", ({ bench }: TestContext): void => { + bench("long Unicode input", (): void => { + snakeCase(longUnicodeInput); + }); + + bench("combining marks", (): void => { + snakeCase("cafe\u0301 noir"); + }); + + bench("Cyrillic", (): void => { + snakeCase("Привет Мир"); + }); + + bench("CJK", (): void => { + snakeCase("你好 世界"); + }); + + bench("Arabic", (): void => { + snakeCase("مرحبا بالعالم"); + }); + }); + + test("given separator-heavy input", ({ bench }: TestContext): void => { + bench("long separator-heavy input", (): void => { + snakeCase(separatorHeavyInput); + }); + }); + + test("given mixed-case input", ({ bench }: TestContext): void => { + bench("long mixed-case input", (): void => { + snakeCase(mixedCaseInput); + }); + }); + + test("given simple inputs", ({ bench }: TestContext): void => { + bench("single word", (): void => { + snakeCase("hello"); + }); + + bench("numeric-only input", (): void => { + snakeCase("123456789"); + }); + }); + + test("given an empty or nullish value", ({ bench }: TestContext): void => { + bench("empty string", (): void => { + snakeCase(""); + }); + + bench("null", (): void => { + snakeCase(null); + }); + + bench("undefined", (): void => { + snakeCase(undefined); + }); + }); +}); From 5ec821ed014bb5cfaaa48075fe9757c2650b450c Mon Sep 17 00:00:00 2001 From: rcpthongta <220981657+rcpthongta@users.noreply.github.com> Date: Wed, 23 Sep 2026 01:53:07 +0700 Subject: [PATCH 071/121] feat(constant-case): add CONSTANT_CASE string conversion utility --- src/libs/constant-case/constant-case.lib.ts | 48 +++++++++++++++++++++ src/libs/constant-case/index.ts | 1 + src/libs/index.ts | 1 + 3 files changed, 50 insertions(+) create mode 100644 src/libs/constant-case/constant-case.lib.ts create mode 100644 src/libs/constant-case/index.ts diff --git a/src/libs/constant-case/constant-case.lib.ts b/src/libs/constant-case/constant-case.lib.ts new file mode 100644 index 0000000..81c5285 --- /dev/null +++ b/src/libs/constant-case/constant-case.lib.ts @@ -0,0 +1,48 @@ +import { EMPTY, UNDERSCORE, WORD_REGEX } from "../../constants"; + +import { hasLength } from "../has-length"; + +/** + * Converts a string to CONSTANT_CASE (SCREAMING_SNAKE_CASE). + * + * Handles separators, existing casing styles, acronyms, numbers, Unicode characters, and combining marks. + * + * @param value - The string to convert to CONSTANT_CASE, or `null` or `undefined`. + * @returns The CONSTANT_CASE string, or an empty string when `value` is empty, `null`, or `undefined`, or contains no extractable words. + * + * @example + * + * constantCase("Foo Bar"); // "FOO_BAR" + * constantCase("--foo-bar--"); // "FOO_BAR" + * constantCase("fooBar"); // "FOO_BAR" + * constantCase("FOO_BAR"); // "FOO_BAR" + * constantCase("XMLHttpRequest"); // "XML_HTTP_REQUEST" + * constantCase("version 2.0"); // "VERSION_2_0" + * constantCase("Привет Мир"); // "ПРИВЕТ_МИР" + * constantCase("你好 世界"); // "你好_世界" + * constantCase("cafe\u0301 noir"); // "CAFE\u0301_NOIR" + * constantCase(""); // "" + * constantCase(null); // "" + * constantCase(undefined); // "" + */ +export function constantCase(value: string | null | undefined): string { + if (!hasLength(value)) { + return EMPTY; + } + + const words: RegExpMatchArray | null = value.match(WORD_REGEX); + + if (!words) { + return EMPTY; + } + + const length: number = words.length; + + let result: string = words[0].toUpperCase(); + + for (let index: number = 1; index < length; index++) { + result += UNDERSCORE + words[index].toUpperCase(); + } + + return result; +} diff --git a/src/libs/constant-case/index.ts b/src/libs/constant-case/index.ts new file mode 100644 index 0000000..783132e --- /dev/null +++ b/src/libs/constant-case/index.ts @@ -0,0 +1 @@ +export * from "./constant-case.lib"; diff --git a/src/libs/index.ts b/src/libs/index.ts index 94df5a7..e1cf8a1 100644 --- a/src/libs/index.ts +++ b/src/libs/index.ts @@ -1,5 +1,6 @@ export * from "./camel-case"; export * from "./capitalize"; +export * from "./constant-case"; export * from "./default-if-blank"; export * from "./default-if-empty"; export * from "./has-length"; From 3651f10b8ad4653d6262d93ec8d12fe5317c55f9 Mon Sep 17 00:00:00 2001 From: rcpthongta <220981657+rcpthongta@users.noreply.github.com> Date: Wed, 23 Sep 2026 01:53:36 +0700 Subject: [PATCH 072/121] test(constant-case): add unit tests for CONSTANT_CASE conversion --- .../constant-case/constant-case.lib.spec.ts | 159 ++++++++++++++++++ 1 file changed, 159 insertions(+) create mode 100644 src/libs/constant-case/constant-case.lib.spec.ts diff --git a/src/libs/constant-case/constant-case.lib.spec.ts b/src/libs/constant-case/constant-case.lib.spec.ts new file mode 100644 index 0000000..10cbade --- /dev/null +++ b/src/libs/constant-case/constant-case.lib.spec.ts @@ -0,0 +1,159 @@ +import { describe, expect, test } from "vitest"; + +import { constantCase } from "./constant-case.lib"; + +describe("constantCase", (): void => { + describe("given a standard string with separators", (): void => { + test("converts separated words to CONSTANT_CASE", (): void => { + expect(constantCase("foo-bar")).toBe("FOO_BAR"); + expect(constantCase("foo_bar")).toBe("FOO_BAR"); + expect(constantCase("foo.bar")).toBe("FOO_BAR"); + expect(constantCase("foo bar")).toBe("FOO_BAR"); + expect(constantCase("--foo-bar--")).toBe("FOO_BAR"); + expect(constantCase("__foo_bar__")).toBe("FOO_BAR"); + expect(constantCase(" foo bar ")).toBe("FOO_BAR"); + }); + + test("handles mixed separators", (): void => { + expect(constantCase("foo-bar_baz.qux")).toBe("FOO_BAR_BAZ_QUX"); + expect(constantCase("foo / bar \\ baz")).toBe("FOO_BAR_BAZ"); + expect(constantCase("foo!!!bar")).toBe("FOO_BAR"); + expect(constantCase("foo---bar")).toBe("FOO_BAR"); + expect(constantCase("foo...bar")).toBe("FOO_BAR"); + }); + }); + + describe("given an uppercase string with separators", (): void => { + test("normalizes uppercase words to CONSTANT_CASE", (): void => { + expect(constantCase("FOO-BAR")).toBe("FOO_BAR"); + expect(constantCase("FOO_BAR")).toBe("FOO_BAR"); + expect(constantCase("FOO BAR")).toBe("FOO_BAR"); + expect(constantCase("FOO-BAR-BAZ")).toBe("FOO_BAR_BAZ"); + }); + }); + + describe("given camelCase and PascalCase strings", (): void => { + test("converts camelCase to CONSTANT_CASE", (): void => { + expect(constantCase("fooBar")).toBe("FOO_BAR"); + expect(constantCase("xmlHttpRequest")).toBe("XML_HTTP_REQUEST"); + }); + + test("converts PascalCase to CONSTANT_CASE", (): void => { + expect(constantCase("FooBar")).toBe("FOO_BAR"); + expect(constantCase("XmlHttpRequest")).toBe("XML_HTTP_REQUEST"); + }); + }); + + describe("given acronyms and mixed casing", (): void => { + test("splits acronyms at word boundaries", (): void => { + expect(constantCase("HTTPResponseCode")).toBe("HTTP_RESPONSE_CODE"); + expect(constantCase("JSONDataAPI")).toBe("JSON_DATA_API"); + expect(constantCase("XMLHttpRequest")).toBe("XML_HTTP_REQUEST"); + expect(constantCase("parseHTTPResponse")).toBe("PARSE_HTTP_RESPONSE"); + expect(constantCase("getURLValue")).toBe("GET_URL_VALUE"); + }); + }); + + describe("given strings containing numbers", (): void => { + test("handles numbers as separate words", (): void => { + expect(constantCase("version 2.0")).toBe("VERSION_2_0"); + expect(constantCase("version-2-0")).toBe("VERSION_2_0"); + expect(constantCase("version_2_0")).toBe("VERSION_2_0"); + expect(constantCase("2foo")).toBe("2_FOO"); + expect(constantCase("foo2bar")).toBe("FOO_2_BAR"); + }); + + test("preserves numeric-only words", (): void => { + expect(constantCase("123")).toBe("123"); + expect(constantCase("123-456")).toBe("123_456"); + expect(constantCase("123_456_789")).toBe("123_456_789"); + }); + }); + + describe("given accents and Unicode letters", (): void => { + test("preserves accented letters while uppercasing words", (): void => { + expect(constantCase("crème-brûlée")).toBe("CRÈME_BRÛLÉE"); + expect(constantCase("jalapeño popper")).toBe("JALAPEÑO_POPPER"); + expect(constantCase("École Normale")).toBe("ÉCOLE_NORMALE"); + }); + + test("converts Cyrillic words to uppercase", (): void => { + expect(constantCase("Привет Мир")).toBe("ПРИВЕТ_МИР"); + expect(constantCase("привет-мир")).toBe("ПРИВЕТ_МИР"); + expect(constantCase("ПРИВЕТ МИР")).toBe("ПРИВЕТ_МИР"); + }); + }); + + describe("given non-cased scripts", (): void => { + test("joins CJK, Hiragana, and Hangul words with underscores", (): void => { + expect(constantCase("こんにちは 世界")).toBe("こんにちは_世界"); + expect(constantCase("你好 世界")).toBe("你好_世界"); + expect(constantCase("안녕하세요 세계")).toBe("안녕하세요_세계"); + }); + + test("joins Arabic words with underscores without case transformation", (): void => { + expect(constantCase("مرحبا بالعالم")).toBe("مرحبا_بالعالم"); + }); + + test("handles mixed Latin and non-cased scripts", (): void => { + expect(constantCase("hello 世界")).toBe("HELLO_世界"); + expect(constantCase("foo-こんにちは")).toBe("FOO_こんにちは"); + expect(constantCase("hello 世界 Привет")).toBe("HELLO_世界_ПРИВЕТ"); + }); + }); + + describe("given decomposed Unicode characters", (): void => { + test("preserves combining marks", (): void => { + expect(constantCase("cafe\u0301 noir")).toBe("CAFE\u0301_NOIR"); + expect(constantCase("Cafe\u0301 Noir")).toBe("CAFE\u0301_NOIR"); + }); + }); + + describe("given strings with no extractable words", (): void => { + test("returns an empty string for symbol-only inputs", (): void => { + expect(constantCase("---")).toBe(""); + expect(constantCase("___")).toBe(""); + expect(constantCase("...")).toBe(""); + expect(constantCase("!!!")).toBe(""); + expect(constantCase("@#$%^&*")).toBe(""); + }); + }); + + describe("given a CONSTANT_CASE result", (): void => { + test("is idempotent", (): void => { + const inputs: string[] = [ + "foo-bar", + "FOO_BAR", + "fooBar", + "FooBar", + "HTTPResponseCode", + "JSONDataAPI", + "version-2-0", + "crème-brûlée", + "Привет Мир", + "hello 世界" + ]; + + for (let index: number = 0; index < inputs.length; index++) { + const input: string = inputs[index]; + const result: string = constantCase(input); + + expect(constantCase(result)).toBe(result); + } + }); + }); + + describe("given an empty or nullish value", (): void => { + test("returns an empty string for an empty string", (): void => { + expect(constantCase("")).toBe(""); + }); + + test("returns an empty string for null", (): void => { + expect(constantCase(null)).toBe(""); + }); + + test("returns an empty string for undefined", (): void => { + expect(constantCase(undefined)).toBe(""); + }); + }); +}); From 140c508376bf9f7923e1d498dbf21ddf44e30cba Mon Sep 17 00:00:00 2001 From: rcpthongta <220981657+rcpthongta@users.noreply.github.com> Date: Wed, 23 Sep 2026 01:54:04 +0700 Subject: [PATCH 073/121] perf(constant-case): add performance benchmarks for CONSTANT_CASE conversion --- .../constant-case/constant-case.lib.bench.ts | 111 ++++++++++++++++++ 1 file changed, 111 insertions(+) create mode 100644 src/libs/constant-case/constant-case.lib.bench.ts diff --git a/src/libs/constant-case/constant-case.lib.bench.ts b/src/libs/constant-case/constant-case.lib.bench.ts new file mode 100644 index 0000000..2072c97 --- /dev/null +++ b/src/libs/constant-case/constant-case.lib.bench.ts @@ -0,0 +1,111 @@ +import { describe, test, TestContext } from "vitest"; + +import { constantCase } from "./constant-case.lib"; + +const shortPascalCase: string = "XMLHttpRequestResponseCodeHandler"; +const input1KB: string = "get-HTTP-response-code-version-2.0-API ".repeat(20); +const input10KB: string = "get-HTTP-response-code-version-2.0-API ".repeat(200); +const input100KB: string = "get-HTTP-response-code-version-2.0-API ".repeat(2000); +const longUnicodeInput: string = "Привет-Мир café-noir 你好-世界 こんにちは-世界 مرحبا-بالعالم ".repeat(100); +const separatorHeavyInput: string = "---foo---bar___baz...qux!!!".repeat(1000); +const mixedCaseInput: string = "aAaAaAaAaAaAaAaAaAaAaAaAaAaAaA".repeat(1000); + +describe("constantCase", (): void => { + test("given common short ASCII inputs", ({ bench }: TestContext): void => { + bench("hyphen-separated lowercase", (): void => { + constantCase("foo-bar"); + }); + + bench("already CONSTANT_CASE", (): void => { + constantCase("FOO_BAR"); + }); + + bench("camelCase", (): void => { + constantCase("fooBar"); + }); + + bench("PascalCase", (): void => { + constantCase(shortPascalCase); + }); + + bench("acronym followed by word", (): void => { + constantCase("HTTPResponseCode"); + }); + + bench("mixed words and numbers", (): void => { + constantCase("version-2-0"); + }); + }); + + test("given inputs of increasing size", ({ bench }: TestContext): void => { + bench("approximately 1 KB input", (): void => { + constantCase(input1KB); + }); + + bench("approximately 10 KB input", (): void => { + constantCase(input10KB); + }); + + bench("approximately 100 KB input", (): void => { + constantCase(input100KB); + }); + }); + + test("given Unicode input", ({ bench }: TestContext): void => { + bench("long Unicode input", (): void => { + constantCase(longUnicodeInput); + }); + + bench("combining marks", (): void => { + constantCase("cafe\u0301 noir"); + }); + + bench("Cyrillic", (): void => { + constantCase("Привет Мир"); + }); + + bench("CJK", (): void => { + constantCase("你好 世界"); + }); + + bench("Arabic", (): void => { + constantCase("مرحبا بالعالم"); + }); + }); + + test("given separator-heavy input", ({ bench }: TestContext): void => { + bench("long separator-heavy input", (): void => { + constantCase(separatorHeavyInput); + }); + }); + + test("given mixed-case input", ({ bench }: TestContext): void => { + bench("long mixed-case input", (): void => { + constantCase(mixedCaseInput); + }); + }); + + test("given simple inputs", ({ bench }: TestContext): void => { + bench("single word", (): void => { + constantCase("hello"); + }); + + bench("numeric-only input", (): void => { + constantCase("123456789"); + }); + }); + + test("given an empty or nullish value", ({ bench }: TestContext): void => { + bench("empty string", (): void => { + constantCase(""); + }); + + bench("null", (): void => { + constantCase(null); + }); + + bench("undefined", (): void => { + constantCase(undefined); + }); + }); +}); From 2c3cfddc31b2bd7118a546af563abd5569cb676b Mon Sep 17 00:00:00 2001 From: rcpthongta <220981657+rcpthongta@users.noreply.github.com> Date: Wed, 23 Sep 2026 02:50:27 +0700 Subject: [PATCH 074/121] feat(constants): add whitespace normalization regex patterns --- src/constants/regex.constant.ts | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/constants/regex.constant.ts b/src/constants/regex.constant.ts index 33425db..e8615cc 100644 --- a/src/constants/regex.constant.ts +++ b/src/constants/regex.constant.ts @@ -1,3 +1,15 @@ +/** + * Matches one or more consecutive whitespace characters. + */ +export const CONSECUTIVE_WHITESPACE_REGEX: RegExp = /\s+/g; + +/** + * Matches strings that contain whitespace requiring normalization. + * + * This includes non-space whitespace characters, consecutive spaces, leading spaces, and trailing spaces. + */ +export const WHITESPACE_TEST_REGEX: RegExp = /[^\S ]| {2,}|^ | $/; + /** * Matches individual words, acronyms, and Unicode number sequences. * From 0e367a5990608c62737e96e79e049d1d37fd59aa Mon Sep 17 00:00:00 2001 From: rcpthongta <220981657+rcpthongta@users.noreply.github.com> Date: Wed, 23 Sep 2026 02:51:22 +0700 Subject: [PATCH 075/121] feat(constants): add SPACE string constant --- src/constants/string-values.constant.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/constants/string-values.constant.ts b/src/constants/string-values.constant.ts index e863b70..a1861a4 100644 --- a/src/constants/string-values.constant.ts +++ b/src/constants/string-values.constant.ts @@ -1,3 +1,4 @@ export const EMPTY: string = ""; export const HYPHEN: string = "-"; +export const SPACE: string = " "; export const UNDERSCORE: string = "_"; From 4c73130bb1cf8ee9f76318add1282727dcb0f8b2 Mon Sep 17 00:00:00 2001 From: rcpthongta <220981657+rcpthongta@users.noreply.github.com> Date: Wed, 23 Sep 2026 02:52:10 +0700 Subject: [PATCH 076/121] feat(collapse-whitespace): add whitespace normalization utility --- .../collapse-whitespace.lib.ts | 30 +++++++++++++++++++ src/libs/collapse-whitespace/index.ts | 1 + src/libs/index.ts | 1 + 3 files changed, 32 insertions(+) create mode 100644 src/libs/collapse-whitespace/collapse-whitespace.lib.ts create mode 100644 src/libs/collapse-whitespace/index.ts diff --git a/src/libs/collapse-whitespace/collapse-whitespace.lib.ts b/src/libs/collapse-whitespace/collapse-whitespace.lib.ts new file mode 100644 index 0000000..e1a5436 --- /dev/null +++ b/src/libs/collapse-whitespace/collapse-whitespace.lib.ts @@ -0,0 +1,30 @@ +import { CONSECUTIVE_WHITESPACE_REGEX, EMPTY, SPACE, WHITESPACE_TEST_REGEX } from "../../constants"; + +import { hasLength } from "../has-length"; + +/** + * Collapses consecutive whitespace characters into a single space and trims + * leading and trailing whitespace. + * + * @param value - The string value to normalize, or `null` or `undefined`. + * @returns The normalized string, or an empty string when `value` is empty, `null`, or `undefined`. + * + * @example + * + * collapseWhitespace("Hello World"); // "Hello World" + * collapseWhitespace(" Hello World "); // "Hello World" + * collapseWhitespace("Hello\tWorld\n!"); // "Hello World !" + * collapseWhitespace("Hello\u00A0World"); // "Hello World" (NBSP) + * collapseWhitespace("Hello\u3000World"); // "Hello World" (Ideographic space) + * collapseWhitespace("Hello World"); // "Hello World" + * collapseWhitespace(""); // "" + * collapseWhitespace(null); // "" + * collapseWhitespace(undefined); // "" + */ +export function collapseWhitespace(value: string | null | undefined): string { + if (!hasLength(value)) { + return EMPTY; + } + + return !WHITESPACE_TEST_REGEX.test(value) ? value : value.replace(CONSECUTIVE_WHITESPACE_REGEX, SPACE).trim(); +} diff --git a/src/libs/collapse-whitespace/index.ts b/src/libs/collapse-whitespace/index.ts new file mode 100644 index 0000000..b353e59 --- /dev/null +++ b/src/libs/collapse-whitespace/index.ts @@ -0,0 +1 @@ +export * from "./collapse-whitespace.lib"; diff --git a/src/libs/index.ts b/src/libs/index.ts index e1cf8a1..1ecd165 100644 --- a/src/libs/index.ts +++ b/src/libs/index.ts @@ -1,5 +1,6 @@ export * from "./camel-case"; export * from "./capitalize"; +export * from "./collapse-whitespace"; export * from "./constant-case"; export * from "./default-if-blank"; export * from "./default-if-empty"; From 749a99e3d8fd4fd6903178d58639a227f00cffbf Mon Sep 17 00:00:00 2001 From: rcpthongta <220981657+rcpthongta@users.noreply.github.com> Date: Wed, 23 Sep 2026 02:52:42 +0700 Subject: [PATCH 077/121] test(collapse-whitespace): add unit tests for whitespace normalization --- .../collapse-whitespace.lib.spec.ts | 106 ++++++++++++++++++ 1 file changed, 106 insertions(+) create mode 100644 src/libs/collapse-whitespace/collapse-whitespace.lib.spec.ts diff --git a/src/libs/collapse-whitespace/collapse-whitespace.lib.spec.ts b/src/libs/collapse-whitespace/collapse-whitespace.lib.spec.ts new file mode 100644 index 0000000..aa3b963 --- /dev/null +++ b/src/libs/collapse-whitespace/collapse-whitespace.lib.spec.ts @@ -0,0 +1,106 @@ +import { describe, expect, test } from "vitest"; + +import { collapseWhitespace } from "./collapse-whitespace.lib"; + +describe("collapseWhitespace", (): void => { + describe("given a string with normal spacing", (): void => { + test("returns the original string when no normalization is needed", (): void => { + expect(collapseWhitespace("Hello World")).toBe("Hello World"); + expect(collapseWhitespace("Hello")).toBe("Hello"); + }); + + test("preserves a single space between words", (): void => { + expect(collapseWhitespace("Hello World TypeScript")).toBe("Hello World TypeScript"); + }); + + test("collapses consecutive spaces into a single space", (): void => { + expect(collapseWhitespace("Hello World")).toBe("Hello World"); + expect(collapseWhitespace("Hello World TypeScript")).toBe("Hello World TypeScript"); + }); + + test("removes leading and trailing spaces", (): void => { + expect(collapseWhitespace(" Hello World ")).toBe("Hello World"); + }); + }); + + describe("given a string with special whitespace", (): void => { + test("collapses tabs, newlines, and carriage returns", (): void => { + expect(collapseWhitespace("Hello\tWorld")).toBe("Hello World"); + expect(collapseWhitespace("Hello\nWorld")).toBe("Hello World"); + expect(collapseWhitespace("Hello\rWorld")).toBe("Hello World"); + expect(collapseWhitespace("Hello\n\t\rWorld")).toBe("Hello World"); + }); + + test("collapses non-breaking spaces", (): void => { + expect(collapseWhitespace("Hello\u00A0World")).toBe("Hello World"); + expect(collapseWhitespace("\u00A0\u00A0Hello\u00A0World\u00A0\u00A0")).toBe("Hello World"); + }); + + test("collapses ideographic spaces", (): void => { + expect(collapseWhitespace("Hello\u3000World")).toBe("Hello World"); + }); + + test("collapses thin spaces and em spaces", (): void => { + expect(collapseWhitespace("Hello\u2009World\u2003TypeScript")).toBe("Hello World TypeScript"); + }); + }); + + describe("given a string with mixed whitespace", (): void => { + test("collapses mixed whitespace characters into single spaces", (): void => { + expect(collapseWhitespace(" \tHello \n\t World \r ")).toBe("Hello World"); + }); + + test("removes leading and trailing mixed whitespace", (): void => { + expect(collapseWhitespace("\t\n Hello World \u00A0\r")).toBe("Hello World"); + }); + }); + + describe("given a whitespace-only string", (): void => { + test("returns an empty string", (): void => { + expect(collapseWhitespace(" \n\t\u00A0\u3000 ")).toBe(""); + }); + }); + + describe("given a string containing multiple whitespace boundaries", (): void => { + test("normalizes every whitespace boundary independently", (): void => { + expect(collapseWhitespace(" Hello\t\tWorld\n\nTypeScript\u00A0\u00A0Rocks ")).toBe( + "Hello World TypeScript Rocks" + ); + }); + }); + + describe("given an already normalized result", (): void => { + test("is idempotent", (): void => { + const inputs: string[] = [ + "Hello", + "Hello World", + "Hello World TypeScript", + "Hello\tWorld", + " Hello World ", + "\u00A0Hello\u3000World\u2003", + " \n\t " + ]; + + for (let index: number = 0; index < inputs.length; index++) { + const input: string = inputs[index]; + const result: string = collapseWhitespace(input); + + expect(collapseWhitespace(result)).toBe(result); + } + }); + }); + + describe("given an empty or nullish value", (): void => { + test("returns an empty string for an empty string", (): void => { + expect(collapseWhitespace("")).toBe(""); + }); + + test("returns an empty string for null", (): void => { + expect(collapseWhitespace(null)).toBe(""); + }); + + test("returns an empty string for undefined", (): void => { + expect(collapseWhitespace(undefined)).toBe(""); + }); + }); +}); From a2be50f127beb72e26c0176877f449c421cc8e3c Mon Sep 17 00:00:00 2001 From: rcpthongta <220981657+rcpthongta@users.noreply.github.com> Date: Wed, 23 Sep 2026 02:53:06 +0700 Subject: [PATCH 078/121] perf(collapse-whitespace): add performance benchmarks for whitespace normalization --- .../collapse-whitespace.lib.bench.ts | 101 ++++++++++++++++++ 1 file changed, 101 insertions(+) create mode 100644 src/libs/collapse-whitespace/collapse-whitespace.lib.bench.ts diff --git a/src/libs/collapse-whitespace/collapse-whitespace.lib.bench.ts b/src/libs/collapse-whitespace/collapse-whitespace.lib.bench.ts new file mode 100644 index 0000000..2363328 --- /dev/null +++ b/src/libs/collapse-whitespace/collapse-whitespace.lib.bench.ts @@ -0,0 +1,101 @@ +import { describe, test, TestContext } from "vitest"; + +import { collapseWhitespace } from "./collapse-whitespace.lib"; + +const normalizedShort: string = "Hello World"; +const mixedWhitespaceShort: string = " \tHello \n\t World \r "; +const normalizedLong: string = "the quick brown fox jumps over the lazy dog ".repeat(100); +const mixedWhitespaceLong: string = "the quick\tbrown\n\nfox jumps\r\nover the\tlazy dog ".repeat(100); +const input1KB: string = "the quick\tbrown\nfox jumps\r\nover the\tlazy dog ".repeat(20); +const input10KB: string = "the quick\tbrown\nfox jumps\r\nover the\tlazy dog ".repeat(200); +const input100KB: string = "the quick\tbrown\nfox jumps\r\nover the\tlazy dog ".repeat(2000); +const separatorHeavyInput: string = "foo bar".repeat(1000); +const whitespaceOnlyInput: string = " ".repeat(1000); + +const mixedUnicodeWhitespace: string = "Hello\u00A0\u3000\u2009World\u2003\u00A0".repeat(100); + +describe("collapseWhitespace", (): void => { + test("given common short inputs", ({ bench }: TestContext): void => { + bench("already normalized", (): void => { + collapseWhitespace(normalizedShort); + }); + + bench("consecutive spaces", (): void => { + collapseWhitespace("Hello World"); + }); + + bench("leading and trailing whitespace", (): void => { + collapseWhitespace(" Hello World "); + }); + + bench("mixed whitespace", (): void => { + collapseWhitespace(mixedWhitespaceShort); + }); + }); + + test("given long inputs", ({ bench }: TestContext): void => { + bench("already normalized long input", (): void => { + collapseWhitespace(normalizedLong); + }); + + bench("mixed whitespace long input", (): void => { + collapseWhitespace(mixedWhitespaceLong); + }); + }); + + test("given inputs of increasing size", ({ bench }: TestContext): void => { + bench("approximately 1 KB input", (): void => { + collapseWhitespace(input1KB); + }); + + bench("approximately 10 KB input", (): void => { + collapseWhitespace(input10KB); + }); + + bench("approximately 100 KB input", (): void => { + collapseWhitespace(input100KB); + }); + }); + + test("given Unicode whitespace", ({ bench }: TestContext): void => { + bench("non-breaking spaces", (): void => { + collapseWhitespace("Hello\u00A0\u00A0World"); + }); + + bench("ideographic spaces", (): void => { + collapseWhitespace("Hello\u3000\u3000World"); + }); + + bench("thin and em spaces", (): void => { + collapseWhitespace("Hello\u2009\u2003World"); + }); + + bench("mixed Unicode whitespace", (): void => { + collapseWhitespace(mixedUnicodeWhitespace); + }); + }); + + test("given separator-heavy input", ({ bench }: TestContext): void => { + bench("long repeated spaces", (): void => { + collapseWhitespace(separatorHeavyInput); + }); + + bench("whitespace-only input", (): void => { + collapseWhitespace(whitespaceOnlyInput); + }); + }); + + test("given an empty or nullish value", ({ bench }: TestContext): void => { + bench("empty string", (): void => { + collapseWhitespace(""); + }); + + bench("null", (): void => { + collapseWhitespace(null); + }); + + bench("undefined", (): void => { + collapseWhitespace(undefined); + }); + }); +}); From 0600ae9b80c7ef612b6b58ed7e5d68ff21d3b696 Mon Sep 17 00:00:00 2001 From: rcpthongta <220981657+rcpthongta@users.noreply.github.com> Date: Wed, 23 Sep 2026 13:53:39 +0700 Subject: [PATCH 079/121] feat(constants): add diacritics and non-alphanumeric regex patterns --- src/constants/regex.constant.ts | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/constants/regex.constant.ts b/src/constants/regex.constant.ts index e8615cc..9d6cb38 100644 --- a/src/constants/regex.constant.ts +++ b/src/constants/regex.constant.ts @@ -3,6 +3,16 @@ */ export const CONSECUTIVE_WHITESPACE_REGEX: RegExp = /\s+/g; +/** + * Matches Unicode combining diacritical marks in the U+0300–U+036F range. + */ +export const DIACRITICS_REGEX: RegExp = /[\u0300-\u036f]/g; + +/** + * Matches one or more consecutive characters that are not Unicode letters, combining marks, or numbers. + */ +export const NON_ALPHANUMERIC_REGEX: RegExp = /[^\p{L}\p{M}\p{N}]+/gu; + /** * Matches strings that contain whitespace requiring normalization. * From f6fa36c5746bf68e4ad11680d1e4a88fcf8d5aeb Mon Sep 17 00:00:00 2001 From: rcpthongta <220981657+rcpthongta@users.noreply.github.com> Date: Wed, 23 Sep 2026 13:54:40 +0700 Subject: [PATCH 080/121] feat(constants): add DOLLAR_SIGN string constant --- src/constants/string-values.constant.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/constants/string-values.constant.ts b/src/constants/string-values.constant.ts index a1861a4..784f4fb 100644 --- a/src/constants/string-values.constant.ts +++ b/src/constants/string-values.constant.ts @@ -1,3 +1,4 @@ +export const DOLLAR_SIGN: string = "$"; export const EMPTY: string = ""; export const HYPHEN: string = "-"; export const SPACE: string = " "; From 90120532a188643f32f1a2242bd569f0a14420ab Mon Sep 17 00:00:00 2001 From: rcpthongta <220981657+rcpthongta@users.noreply.github.com> Date: Wed, 23 Sep 2026 13:55:19 +0700 Subject: [PATCH 081/121] feat(slugify): add URL-friendly string slugify utility --- src/libs/index.ts | 1 + src/libs/slugify/index.ts | 1 + src/libs/slugify/slugify.lib.ts | 141 ++++++++++++++++++++++++++++++++ 3 files changed, 143 insertions(+) create mode 100644 src/libs/slugify/index.ts create mode 100644 src/libs/slugify/slugify.lib.ts diff --git a/src/libs/index.ts b/src/libs/index.ts index 1ecd165..d2d9b28 100644 --- a/src/libs/index.ts +++ b/src/libs/index.ts @@ -8,5 +8,6 @@ export * from "./has-length"; export * from "./has-text"; export * from "./kebab-case"; export * from "./pascal-case"; +export * from "./slugify"; export * from "./snake-case"; export * from "./uncapitalize"; diff --git a/src/libs/slugify/index.ts b/src/libs/slugify/index.ts new file mode 100644 index 0000000..b7107c0 --- /dev/null +++ b/src/libs/slugify/index.ts @@ -0,0 +1 @@ +export * from "./slugify.lib"; diff --git a/src/libs/slugify/slugify.lib.ts b/src/libs/slugify/slugify.lib.ts new file mode 100644 index 0000000..9959b92 --- /dev/null +++ b/src/libs/slugify/slugify.lib.ts @@ -0,0 +1,141 @@ +import { DIACRITICS_REGEX, DOLLAR_SIGN, EMPTY, HYPHEN, NON_ALPHANUMERIC_REGEX } from "../../constants"; + +import { hasLength } from "../has-length"; + +/** + * Configuration options for {@link slugify}. + */ +export interface SlugifyOptions { + /** + * String used to separate words in the slug. + * + * @default "-" + */ + separator?: string; + + /** + * Whether to convert the output string to lowercase. + * + * @default true + */ + lower?: boolean; +} + +/** + * Trims leading and trailing occurrences of a separator. + * + * @param value - The string to trim. + * @param separator - The separator to remove from the boundaries. + * @returns The trimmed string. + */ +function trimSeparator(value: string, separator: string): string { + if (!value || !separator) { + return value; + } + + const separatorLength: number = separator.length; + + let startIndex: number = 0; + let endIndex: number = value.length; + + while (value.startsWith(separator, startIndex)) { + startIndex += separatorLength; + } + + while (endIndex > startIndex && value.endsWith(separator, endIndex)) { + endIndex -= separatorLength; + } + + return startIndex === 0 && endIndex === value.length ? value : value.slice(startIndex, endIndex); +} + +/** + * Converts a string into a URL-friendly slug using the default options. + * + * Latin diacritics and accented characters are normalized to their base + * characters, while Unicode letters, combining marks, and numbers are + * preserved. + * + * Non-alphanumeric characters are replaced with `-`, consecutive separators + * are collapsed into a single separator, and leading and trailing separators + * are removed. + * + * The resulting slug is converted to lowercase by default. + * + * @param value - The string to transform into a slug, or `null` or `undefined`. + * @returns The slugified string, or an empty string when `value` is empty, `null`, or `undefined`. + * + * @example + * + * slugify("Hello World"); // "hello-world" + * slugify("Foo & Bar"); // "foo-bar" + * slugify("Crème Brûlée"); // "creme-brulee" + * slugify("Привет, мир!"); // "привет-мир" + * slugify("你好 世界"); // "你好-世界" + * slugify("สวัสดี โลก"); // "สวัสดี-โลก" + * slugify("Version 2.0"); // "version-2-0" + * slugify(""); // "" + * slugify(null); // "" + * slugify(undefined); // "" + */ +export function slugify(value: string | null | undefined): string; + +/** + * Converts a string into a URL-friendly slug using the specified options. + * + * Latin diacritics and accented characters are normalized to their base + * characters, while Unicode letters, combining marks, and numbers are + * preserved. + * + * Non-alphanumeric characters are replaced with the configured separator, + * consecutive separators are collapsed into a single separator, and leading + * and trailing separators are removed. + * + * By default, the resulting slug is converted to lowercase and uses `-` as + * the separator. These defaults can be overridden through `options`. + * + * An empty separator removes non-alphanumeric characters instead of replacing + * them with a separator. Separators containing `$` are treated as literal + * strings rather than JavaScript replacement patterns. + * + * @param value - The string to transform into a slug, or `null` or `undefined`. + * @param options - Configuration options for the separator and letter casing. + * @returns The slugified string, or an empty string when `value` is empty, `null`, or `undefined`. + * + * @example + * + * slugify("Hello World", { separator: "_" }); // "hello_world" + * slugify("Hello World", { separator: "." }); // "hello.world" + * slugify("Hello World", { separator: "--" }); // "hello--world" + * slugify("Hello World", { separator: "$&" }); // "hello$&world" + * slugify("Hello World", { lower: false }); // "Hello-World" + * slugify("Crème Brûlée", { lower: false }); // "Creme-Brulee" + * slugify("Привет, МИР!", { lower: false }); // "Привет-МИР" + * slugify("你好 世界", { separator: "_" }); // "你好_世界" + * slugify("สวัสดี โลก", { separator: "" }); // "สวัสดีโลก" + * slugify("Hello World", { separator: "", lower: false }); // "HelloWorld" + * slugify("", { separator: "_" }); // "" + * slugify(null, { separator: "_" }); // "" + * slugify(undefined, { separator: "_" }); // "" + */ +export function slugify(value: string | null | undefined, options: SlugifyOptions): string; + +export function slugify(value: string | null | undefined, options?: SlugifyOptions): string { + if (!hasLength(value)) { + return EMPTY; + } + + const { separator = HYPHEN, lower = true }: SlugifyOptions = options ?? {}; + + let result: string = value.normalize("NFD").replace(DIACRITICS_REGEX, EMPTY).normalize("NFC"); + + if (lower) { + result = result.toLowerCase(); + } + + result = separator.includes(DOLLAR_SIGN) + ? result.replace(NON_ALPHANUMERIC_REGEX, (): string => separator) + : result.replace(NON_ALPHANUMERIC_REGEX, separator); + + return trimSeparator(result, separator); +} From 9916d99d518b63bb98dc6ce3358d98b8a472ebf8 Mon Sep 17 00:00:00 2001 From: rcpthongta <220981657+rcpthongta@users.noreply.github.com> Date: Wed, 23 Sep 2026 13:55:58 +0700 Subject: [PATCH 082/121] test(slugify): add unit tests for slugify utility --- src/libs/slugify/slugify.lib.spec.ts | 279 +++++++++++++++++++++++++++ 1 file changed, 279 insertions(+) create mode 100644 src/libs/slugify/slugify.lib.spec.ts diff --git a/src/libs/slugify/slugify.lib.spec.ts b/src/libs/slugify/slugify.lib.spec.ts new file mode 100644 index 0000000..9064d22 --- /dev/null +++ b/src/libs/slugify/slugify.lib.spec.ts @@ -0,0 +1,279 @@ +import { describe, expect, test } from "vitest"; + +import { slugify } from "./slugify.lib"; + +describe("slugify", (): void => { + describe("given a string containing standard text", (): void => { + test("converts text to a lowercase slug", (): void => { + expect(slugify("Hello World")).toBe("hello-world"); + expect(slugify("Hello World!")).toBe("hello-world"); + expect(slugify("Hello World 123")).toBe("hello-world-123"); + }); + + test("preserves alphanumeric characters", (): void => { + expect(slugify("Hello123")).toBe("hello123"); + expect(slugify("12345")).toBe("12345"); + expect(slugify("Version 2.0")).toBe("version-2-0"); + }); + + test("collapses consecutive non-alphanumeric characters into one separator", (): void => { + expect(slugify("Hello World")).toBe("hello-world"); + expect(slugify("Hello---World")).toBe("hello-world"); + expect(slugify("Hello...World")).toBe("hello-world"); + expect(slugify("Hello & World")).toBe("hello-world"); + expect(slugify("Hello - & - World")).toBe("hello-world"); + }); + + test("trims leading and trailing separators", (): void => { + expect(slugify(" Hello World ")).toBe("hello-world"); + expect(slugify("---Hello World---")).toBe("hello-world"); + expect(slugify("...Hello World...")).toBe("hello-world"); + expect(slugify("!!!Hello World!!!")).toBe("hello-world"); + }); + }); + + describe("given accented and unicode characters", (): void => { + test("normalizes accented Latin characters", (): void => { + expect(slugify("Crème Brûlée")).toBe("creme-brulee"); + expect(slugify("café")).toBe("cafe"); + expect(slugify("naïve")).toBe("naive"); + expect(slugify("résumé")).toBe("resume"); + }); + + test("normalizes uppercase accented characters before lowercasing", (): void => { + expect(slugify("ÉLÈVE À PARIS")).toBe("eleve-a-paris"); + expect(slugify("À Propos")).toBe("a-propos"); + }); + + test("removes combining diacritical marks from Latin characters", (): void => { + expect(slugify("e\u0301")).toBe("e"); + expect(slugify("Cafe\u0301")).toBe("cafe"); + }); + + test("supports CJK characters (Chinese, Japanese, Korean)", (): void => { + expect(slugify("Hello 世界")).toBe("hello-世界"); + expect(slugify("你好 世界")).toBe("你好-世界"); + expect(slugify("中文")).toBe("中文"); + expect(slugify("こんにちは 世界")).toBe("こんにちは-世界"); + expect(slugify("カタカナ テスト")).toBe("カタカナ-テスト"); + expect(slugify("안녕하세요 세계")).toBe("안녕하세요-세계"); + }); + + test("supports Cyrillic characters", (): void => { + expect(slugify("Привет, мир!")).toBe("привет-мир"); + expect(slugify("Быстрая лиса")).toBe("быстрая-лиса"); + }); + + test("supports Arabic characters", (): void => { + expect(slugify("مرحبا بالعالم")).toBe("مرحبا-بالعالم"); + expect(slugify("أهلا و سهلا")).toBe("أهلا-و-سهلا"); + }); + + test("supports Thai characters and combining marks", (): void => { + expect(slugify("สวัสดี โลก")).toBe("สวัสดี-โลก"); + expect(slugify("ภาษาไทย 101")).toBe("ภาษาไทย-101"); + }); + + test("supports Greek, Hebrew, and Devanagari scripts", (): void => { + expect(slugify("Γειά σου Κόσμε")).toBe("γεια-σου-κοσμε"); + expect(slugify("שלום עולם")).toBe("שלום-עולם"); + expect(slugify("नमस्ते दुनिया")).toBe("नमस्ते-दुनिया"); + }); + }); + + describe("given a string with whitespace", (): void => { + test("converts whitespace between words into separators", (): void => { + expect(slugify("Hello World")).toBe("hello-world"); + expect(slugify("Hello\tWorld")).toBe("hello-world"); + expect(slugify("Hello\nWorld")).toBe("hello-world"); + expect(slugify("Hello\r\nWorld")).toBe("hello-world"); + }); + + test("handles unicode whitespace", (): void => { + expect(slugify("Hello\u00A0World")).toBe("hello-world"); + expect(slugify("Hello\u2003World")).toBe("hello-world"); + expect(slugify("Hello\u3000World")).toBe("hello-world"); + }); + + test("returns an empty string for whitespace-only input", (): void => { + expect(slugify(" ")).toBe(""); + expect(slugify(" ")).toBe(""); + expect(slugify("\t\n\r")).toBe(""); + expect(slugify("\u00A0\u2003\u3000")).toBe(""); + }); + }); + + describe("given the lower option", (): void => { + test("converts the result to lowercase by default", (): void => { + expect(slugify("Hello WORLD")).toBe("hello-world"); + expect(slugify("ПРИВЕТ МИР")).toBe("привет-мир"); + }); + + test("preserves letter casing when lower is false", (): void => { + expect(slugify("Hello WORLD", { lower: false })).toBe("Hello-WORLD"); + expect(slugify("Crème Brûlée", { lower: false })).toBe("Creme-Brulee"); + expect(slugify("Привет, МИР!", { lower: false })).toBe("Привет-МИР"); + }); + + test("converts the result to lowercase when lower is true", (): void => { + expect(slugify("Hello WORLD", { lower: true })).toBe("hello-world"); + expect(slugify("Привет МИР", { lower: true })).toBe("привет-мир"); + }); + }); + + describe("given the separator option", (): void => { + test("uses a custom separator", (): void => { + expect(slugify("Hello World", { separator: "_" })).toBe("hello_world"); + expect(slugify("Hello World", { separator: "." })).toBe("hello.world"); + expect(slugify("Hello World", { separator: "~" })).toBe("hello~world"); + expect(slugify("你好 世界", { separator: "_" })).toBe("你好_世界"); + }); + + test("supports multi-character separators", (): void => { + expect(slugify("Hello World", { separator: "--" })).toBe("hello--world"); + expect(slugify("Hello World", { separator: "__" })).toBe("hello__world"); + expect(slugify("Hello World", { separator: "..." })).toBe("hello...world"); + expect(slugify("สวัสดี โลก", { separator: "--" })).toBe("สวัสดี--โลก"); + }); + + test("trims repeated custom separators from the boundaries", (): void => { + expect(slugify(" Hello World ", { separator: "--" })).toBe("hello--world"); + expect(slugify("Hello World", { separator: "__" })).toBe("hello__world"); + expect(slugify("---Hello World---", { separator: "--" })).toBe("hello--world"); + }); + + test("removes non-alphanumeric characters when the separator is empty", (): void => { + expect(slugify("Hello, World!", { separator: "" })).toBe("helloworld"); + expect(slugify("Foo & Bar", { separator: "" })).toBe("foobar"); + expect(slugify("你好, 世界!", { separator: "" })).toBe("你好世界"); + expect(slugify("สวัสดี, โลก!", { separator: "" })).toBe("สวัสดีโลก"); + }); + + test("preserves letter casing when the separator is empty and lower is false", (): void => { + expect( + slugify("Hello, World!", { + separator: "", + lower: false + }) + ).toBe("HelloWorld"); + + expect( + slugify("Foo & Bar", { + separator: "", + lower: false + }) + ).toBe("FooBar"); + + expect( + slugify("Привет, Мир!", { + separator: "", + lower: false + }) + ).toBe("ПриветМир"); + }); + }); + + describe("given special replacement-token strings as separators", (): void => { + test("preserves $& as a literal separator", (): void => { + expect(slugify("Hello World", { separator: "$&" })).toBe("hello$&world"); + }); + + test("preserves $1 as a literal separator", (): void => { + expect(slugify("Hello World", { separator: "$1" })).toBe("hello$1world"); + }); + + test("preserves $` as a literal separator", (): void => { + expect(slugify("Hello World", { separator: "$`" })).toBe("hello$`world"); + }); + + test("preserves $' as a literal separator", (): void => { + expect(slugify("Hello World", { separator: "$'" })).toBe("hello$'world"); + }); + }); + + describe("given separators containing regular expression characters", (): void => { + test("supports regex metacharacters as separators", (): void => { + expect(slugify("Hello World", { separator: "." })).toBe("hello.world"); + expect(slugify("Hello World", { separator: "+" })).toBe("hello+world"); + expect(slugify("Hello World", { separator: "*" })).toBe("hello*world"); + expect(slugify("Hello World", { separator: "?" })).toBe("hello?world"); + expect(slugify("Hello World", { separator: "[" })).toBe("hello[world"); + expect(slugify("Hello World", { separator: "[]" })).toBe("hello[]world"); + }); + }); + + describe("given punctuation and symbols", (): void => { + test("replaces punctuation with separators", (): void => { + expect(slugify("Hello, World!")).toBe("hello-world"); + expect(slugify("Hello: World;")).toBe("hello-world"); + expect(slugify("Hello / World")).toBe("hello-world"); + expect(slugify("Hello @ World")).toBe("hello-world"); + expect(slugify("Hello (World) [123]")).toBe("hello-world-123"); + }); + + test("replaces emoji with separators", (): void => { + expect(slugify("Hello 🔥 World")).toBe("hello-world"); + expect(slugify("🔥 Hello World 🔥")).toBe("hello-world"); + expect(slugify("Hello🔥World")).toBe("hello-world"); + }); + + test("returns an empty string when the input contains only symbols, punctuation, or emoji", (): void => { + expect(slugify("🔥")).toBe(""); + expect(slugify("!!!")).toBe(""); + expect(slugify("---")).toBe(""); + expect(slugify("@#$%^&*()")).toBe(""); + }); + }); + + describe("given already slugified text", (): void => { + test("preserves an existing valid slug", (): void => { + expect(slugify("hello-world")).toBe("hello-world"); + expect(slugify("你好-世界")).toBe("你好-世界"); + }); + + test("normalizes an invalid slug", (): void => { + expect(slugify("Hello--World")).toBe("hello-world"); + expect(slugify("hello___world")).toBe("hello-world"); + expect(slugify("--hello--world--")).toBe("hello-world"); + }); + }); + + describe("given a slugified result", (): void => { + test("is idempotent", (): void => { + const inputs: string[] = [ + "Hello World", + "Foo & Bar", + "Crème Brûlée", + "Привет, мир!", + "你好 世界", + "สวัสดี โลก", + "Version 2.0", + "hello-world" + ]; + + for (let index: number = 0; index < inputs.length; index++) { + const input: string = inputs[index]; + const result: string = slugify(input); + + expect(slugify(result)).toBe(result); + } + }); + }); + + describe("given an empty or nullish value", (): void => { + test("returns an empty string for an empty string", (): void => { + expect(slugify("")).toBe(""); + expect(slugify("", { separator: "_" })).toBe(""); + }); + + test("returns an empty string for null", (): void => { + expect(slugify(null)).toBe(""); + expect(slugify(null, { separator: "_" })).toBe(""); + }); + + test("returns an empty string for undefined", (): void => { + expect(slugify(undefined)).toBe(""); + expect(slugify(undefined, { separator: "_" })).toBe(""); + }); + }); +}); From f5ba0b677a3281d129700370910f84cbb828fcb1 Mon Sep 17 00:00:00 2001 From: rcpthongta <220981657+rcpthongta@users.noreply.github.com> Date: Wed, 23 Sep 2026 13:56:35 +0700 Subject: [PATCH 083/121] perf(slugify): add performance benchmarks for string slugify --- src/libs/slugify/slugify.lib.bench.ts | 141 ++++++++++++++++++++++++++ 1 file changed, 141 insertions(+) create mode 100644 src/libs/slugify/slugify.lib.bench.ts diff --git a/src/libs/slugify/slugify.lib.bench.ts b/src/libs/slugify/slugify.lib.bench.ts new file mode 100644 index 0000000..0944cf8 --- /dev/null +++ b/src/libs/slugify/slugify.lib.bench.ts @@ -0,0 +1,141 @@ +import { describe, test, TestContext } from "vitest"; + +import { slugify } from "./slugify.lib"; + +const longSlugText: string = "Get HTTP Response Code -- Version 2.0 API & More! ".repeat(100); +const longUnicodeText: string = "Crème Brûlée -- Привет, мир! -- 你好 世界 -- สวัสดี โลก ".repeat(100); +const longSeparatorsText: string = "---foo---bar___baz...qux!!!$$$%%%".repeat(100); +const longSymbols: string = "!@#$%^&*()_+-=[]{}|;:',.<>?/`~".repeat(100); +const longWhitespace: string = " ".repeat(1000); + +describe("slugify", (): void => { + test("given common short ASCII inputs", ({ bench }: TestContext): void => { + bench("simple two-word phrase", (): void => { + slugify("Hello World"); + }); + + bench("with custom separator", (): void => { + slugify("Hello World", { separator: "_" }); + }); + + bench("with lower: false", (): void => { + slugify("Hello World", { lower: false }); + }); + + bench("already a valid slug (no-op path)", (): void => { + slugify("hello-world"); + }); + }); + + test("given numbers and acronyms", ({ bench }: TestContext): void => { + bench("sentence with acronyms and version numbers", (): void => { + slugify("Get HTTP Response Code -- Version 2.0 API & More!"); + }); + + bench("numeric-only input", (): void => { + slugify("123456789"); + }); + + bench("word with numbers", (): void => { + slugify("version-2-0"); + }); + }); + + test("given long inputs", ({ bench }: TestContext): void => { + bench("long text with acronyms and symbols", (): void => { + slugify(longSlugText); + }); + + bench("long text with custom separator and no lowercase", (): void => { + slugify(longSlugText, { separator: "_", lower: false }); + }); + + bench("long Unicode text", (): void => { + slugify(longUnicodeText); + }); + + bench("empty separator (concatenate words)", (): void => { + slugify(longSlugText, { separator: "" }); + }); + }); + + test("given Unicode and combining marks", ({ bench }: TestContext): void => { + bench("accented Latin (NFD normalization)", (): void => { + slugify("Crème Brûlée"); + }); + + bench("accented Latin with lower: false", (): void => { + slugify("Crème Brûlée", { lower: false }); + }); + + bench("mixed accented and ASCII", (): void => { + slugify("café au lait & crêpes"); + }); + + bench("combining diacritical marks", (): void => { + slugify("resume\u0301 naïve"); + }); + + bench("CJK characters", (): void => { + slugify("你好 世界 -- こんにちは 世界 -- 안녕하세요 세계"); + }); + + bench("Cyrillic script", (): void => { + slugify("Привет, мир! -- Быстрая коричневая лиса"); + }); + + bench("Thai script with combining vowels/tones", (): void => { + slugify("สวัสดี โลก ภาษาไทย 101"); + }); + + bench("Arabic script", (): void => { + slugify("مرحبا بالعالم -- أهلا و سهلا"); + }); + }); + + test("given excessive separators", ({ bench }: TestContext): void => { + bench("leading and trailing separators", (): void => { + slugify("---Hello World---"); + }); + + bench("consecutive separators", (): void => { + slugify("foo!!!@@@###bar$$$%%%baz"); + }); + + bench("mixed whitespace and punctuation padding", (): void => { + slugify(" -- foo __ bar .. baz -- "); + }); + + bench("long separator-heavy input", (): void => { + slugify(longSeparatorsText); + }); + }); + + test("given non-extractable input", ({ bench }: TestContext): void => { + bench("symbol-only input", (): void => { + slugify("!@#$%^&*()_+-=[]{}"); + }); + + bench("long symbol-only input", (): void => { + slugify(longSymbols); + }); + + bench("long whitespace-only input", (): void => { + slugify(longWhitespace); + }); + }); + + test("given an empty or nullish value", ({ bench }: TestContext): void => { + bench("empty string", (): void => { + slugify(""); + }); + + bench("null", (): void => { + slugify(null); + }); + + bench("undefined", (): void => { + slugify(undefined); + }); + }); +}); From f737768a3a9164143038b0bbd50769e84924d804 Mon Sep 17 00:00:00 2001 From: rcpthongta <220981657+rcpthongta@users.noreply.github.com> Date: Wed, 23 Sep 2026 14:14:19 +0700 Subject: [PATCH 084/121] feat(constants): add FORMAT_PLACEHOLDER_REGEX placeholder pattern --- src/constants/regex.constant.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/constants/regex.constant.ts b/src/constants/regex.constant.ts index 9d6cb38..d454796 100644 --- a/src/constants/regex.constant.ts +++ b/src/constants/regex.constant.ts @@ -8,6 +8,13 @@ export const CONSECUTIVE_WHITESPACE_REGEX: RegExp = /\s+/g; */ export const DIACRITICS_REGEX: RegExp = /[\u0300-\u036f]/g; +/** + * Matches indexed format placeholders with optional dot-separated property paths. + * + * Captures the full path inside the braces (e.g., `"0"`, `"0.name"`, `"1.items.0.label"`). + */ +export const FORMAT_PLACEHOLDER_REGEX: RegExp = /\{(\d+(?:\.\w+)*)\}/g; + /** * Matches one or more consecutive characters that are not Unicode letters, combining marks, or numbers. */ From 5724b4952d5fbcd879c6f910f9133a80d9f193cb Mon Sep 17 00:00:00 2001 From: rcpthongta <220981657+rcpthongta@users.noreply.github.com> Date: Wed, 23 Sep 2026 14:15:14 +0700 Subject: [PATCH 085/121] feat(constants): add DOT, LEFT_BRACE, and RIGHT_BRACE string constants --- src/constants/string-values.constant.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/constants/string-values.constant.ts b/src/constants/string-values.constant.ts index 784f4fb..b0e6c7b 100644 --- a/src/constants/string-values.constant.ts +++ b/src/constants/string-values.constant.ts @@ -1,5 +1,8 @@ export const DOLLAR_SIGN: string = "$"; +export const DOT: string = "."; export const EMPTY: string = ""; export const HYPHEN: string = "-"; +export const LEFT_BRACE: string = "{"; +export const RIGHT_BRACE: string = "}"; export const SPACE: string = " "; export const UNDERSCORE: string = "_"; From 514be7c299041ca9d4f9a2ecbc86b8aef3b53a55 Mon Sep 17 00:00:00 2001 From: rcpthongta <220981657+rcpthongta@users.noreply.github.com> Date: Wed, 23 Sep 2026 14:17:03 +0700 Subject: [PATCH 086/121] feat(format): add string template formatting utilities --- src/libs/format/format.lib.ts | 187 ++++++++++++++++++++++++++++++++++ src/libs/format/index.ts | 1 + src/libs/index.ts | 1 + 3 files changed, 189 insertions(+) create mode 100644 src/libs/format/format.lib.ts create mode 100644 src/libs/format/index.ts diff --git a/src/libs/format/format.lib.ts b/src/libs/format/format.lib.ts new file mode 100644 index 0000000..7d117c6 --- /dev/null +++ b/src/libs/format/format.lib.ts @@ -0,0 +1,187 @@ +import { DOT, EMPTY, FORMAT_PLACEHOLDER_REGEX, LEFT_BRACE } from "../../constants"; + +import { hasLength } from "../has-length"; + +/** + * A reusable formatting function returned by {@link createFormatter}. + */ +export type FormatFunction = (...args: unknown[]) => string; + +interface CompiledSegment { + /** Text preceding the placeholder. */ + readonly leadingText: string; + + /** Zero-based argument index. */ + readonly argIndex: number; + + /** Property path after the argument index. */ + readonly pathSegments: readonly string[]; +} + +interface CompiledTemplate { + /** Parsed placeholders and their preceding text. */ + readonly segments: readonly CompiledSegment[]; + + /** Text following the last placeholder. */ + readonly suffix: string; +} + +/** + * Resolves a property path against an object or array. + */ +function resolvePath(value: unknown, segments: readonly string[]): unknown { + const segmentCount: number = segments.length; + let current: unknown = value; + + for (let segmentIndex: number = 0; segmentIndex < segmentCount; segmentIndex++) { + if (current === null || current === undefined) { + return undefined; + } + + current = (current as Record)[segments[segmentIndex]]; + } + + return current; +} + +/** + * Converts a value to its formatted string representation. + * + * Objects and arrays are serialized with `JSON.stringify()`. Unsupported values or serialization errors resolve to `"undefined"`. + */ +function resolveValue(value: unknown): string { + if (typeof value === "string") { + return value; + } + + if (value === null) { + return "null"; + } + + if (value === undefined) { + return "undefined"; + } + + if (typeof value === "number" || typeof value === "boolean") { + return String(value); + } + + try { + return JSON.stringify(value) ?? "undefined"; + } catch { + return "undefined"; + } +} + +/** + * Compiles a format template into reusable placeholder segments. + */ +function compileTemplate(template: string): CompiledTemplate { + const segments: CompiledSegment[] = []; + + let lastIndex: number = 0; + + for (const match of template.matchAll(FORMAT_PLACEHOLDER_REGEX)) { + const leadingText: string = template.slice(lastIndex, match.index); + const pathParts: string[] = match[1].split(DOT); + const argIndex: number = Number(pathParts[0]); + const pathSegments: string[] = pathParts.slice(1); + + segments.push({ leadingText, argIndex, pathSegments }); + + lastIndex = match.index + match[0].length; + } + + return { segments, suffix: template.slice(lastIndex) }; +} + +/** + * Formats a compiled template with the provided arguments. + */ +function formatCompiled(compiled: CompiledTemplate, args: readonly unknown[]): string { + const { segments, suffix }: CompiledTemplate = compiled; + const segmentCount: number = segments.length; + + if (segmentCount === 0) { + return suffix; + } + + let result: string = EMPTY; + + for (let segmentIndex: number = 0; segmentIndex < segmentCount; segmentIndex++) { + const segment: CompiledSegment = segments[segmentIndex]; + + result += segment.leadingText; + + const argValue: unknown = args[segment.argIndex]; + + if (segment.pathSegments.length === 0) { + result += resolveValue(argValue); + } else { + result += resolveValue(resolvePath(argValue, segment.pathSegments)); + } + } + + return result + suffix; +} + +/** + * Formats a template by replacing indexed placeholders with argument values. + * + * Supports `{index}` placeholders and dot notation for nested properties, + * such as `{0.name}` and `{0.items.0}`. + * + * @param template - The template to format. + * @param args - The values used to resolve placeholders. + * @returns The formatted string, or an empty string when `template` is empty, `null`, or `undefined`. + * + * @example + * + * format("Hello {0}!", "World"); // "Hello World!" + * format("Hello {0.name}!", { name: "Alice" }); // "Hello Alice!" + * format("{0.users.0.name}", { users: [{ name: "Bob" }] }); // "Bob" + */ +export function format(template: string | null | undefined, ...args: unknown[]): string { + if (!hasLength(template)) { + return EMPTY; + } + + if (!template.includes(LEFT_BRACE)) { + return template; + } + + const compiled: CompiledTemplate = compileTemplate(template); + + return formatCompiled(compiled, args); +} + +/** + * Creates a reusable formatter from a template. + * + * The template is compiled once when the formatter is created, making it efficient for repeated formatting with different arguments. + * + * @param template - The template to compile. + * @returns A reusable formatter function. + * + * @example + * const greet = createFormatter("Hello {0}!"); + * + * greet("Alice"); // "Hello Alice!" + * greet("Bob"); // "Hello Bob!" + * greet(""); // "Hello !" + * greet(null); // "Hello null!" + * greet(undefined); // "Hello undefined!" + */ +export function createFormatter(template: string | null | undefined): FormatFunction { + if (!hasLength(template)) { + return (): string => EMPTY; + } + + if (!template.includes(LEFT_BRACE)) { + return (): string => template; + } + + const compiled: CompiledTemplate = compileTemplate(template); + + return (...args: unknown[]): string => formatCompiled(compiled, args); +} diff --git a/src/libs/format/index.ts b/src/libs/format/index.ts new file mode 100644 index 0000000..5fa2556 --- /dev/null +++ b/src/libs/format/index.ts @@ -0,0 +1 @@ +export * from "./format.lib"; diff --git a/src/libs/index.ts b/src/libs/index.ts index d2d9b28..bc5d0c6 100644 --- a/src/libs/index.ts +++ b/src/libs/index.ts @@ -4,6 +4,7 @@ export * from "./collapse-whitespace"; export * from "./constant-case"; export * from "./default-if-blank"; export * from "./default-if-empty"; +export * from "./format"; export * from "./has-length"; export * from "./has-text"; export * from "./kebab-case"; From 3a76181a050dfefec7287614cb2e04d58395e0c8 Mon Sep 17 00:00:00 2001 From: rcpthongta <220981657+rcpthongta@users.noreply.github.com> Date: Wed, 23 Sep 2026 14:17:48 +0700 Subject: [PATCH 087/121] test(format): add unit tests for string template formatting --- src/libs/format/format.lib.spec.ts | 312 +++++++++++++++++++++++++++++ 1 file changed, 312 insertions(+) create mode 100644 src/libs/format/format.lib.spec.ts diff --git a/src/libs/format/format.lib.spec.ts b/src/libs/format/format.lib.spec.ts new file mode 100644 index 0000000..6f854a6 --- /dev/null +++ b/src/libs/format/format.lib.spec.ts @@ -0,0 +1,312 @@ +import { describe, expect, test } from "vitest"; + +import { createFormatter, format } from "./format.lib"; + +describe("format", (): void => { + describe("basic indexed placeholders", (): void => { + test("replaces a single placeholder with a string argument", (): void => { + expect(format("Hello {0}!", "World")).toBe("Hello World!"); + }); + + test("replaces multiple placeholders with their respective arguments", (): void => { + expect(format("{0} + {1} = {2}", 1, 2, 3)).toBe("1 + 2 = 3"); + }); + + test("replaces repeated occurrences of the same index", (): void => { + expect(format("{0} and {0}", "echo")).toBe("echo and echo"); + }); + + test("replaces placeholders at the start and end of the template", (): void => { + expect(format("{0}{1}", "A", "B")).toBe("AB"); + }); + + test("handles a template with only a placeholder", (): void => { + expect(format("{0}", "value")).toBe("value"); + }); + }); + + describe("primitive type arguments", (): void => { + test("converts a number argument to its string representation", (): void => { + expect(format("Count: {0}", 42)).toBe("Count: 42"); + expect(format("Pi: {0}", 3.14)).toBe("Pi: 3.14"); + expect(format("Negative: {0}", -1)).toBe("Negative: -1"); + expect(format("Zero: {0}", 0)).toBe("Zero: 0"); + }); + + test("converts NaN to the string 'NaN'", (): void => { + expect(format("Value: {0}", NaN)).toBe("Value: NaN"); + }); + + test("converts Infinity to the string 'Infinity'", (): void => { + expect(format("Value: {0}", Infinity)).toBe("Value: Infinity"); + }); + + test("converts -Infinity to the string '-Infinity'", (): void => { + expect(format("Value: {0}", -Infinity)).toBe("Value: -Infinity"); + }); + + test("converts a boolean argument to its string representation", (): void => { + expect(format("Active: {0}", true)).toBe("Active: true"); + expect(format("Disabled: {0}", false)).toBe("Disabled: false"); + }); + + test("converts a null argument to the string 'null'", (): void => { + expect(format("Value: {0}", null)).toBe("Value: null"); + }); + + test("converts an undefined argument to the string 'undefined'", (): void => { + expect(format("Value: {0}", undefined)).toBe("Value: undefined"); + }); + }); + + describe("dot-notation object access", (): void => { + test("resolves a top-level property", (): void => { + expect(format("Hello {0.name}", { name: "Alice" })).toBe("Hello Alice"); + }); + + test("resolves multiple properties from the same object", (): void => { + expect(format("{0.first} {0.last}", { first: "John", last: "Doe" })).toBe("John Doe"); + }); + + test("resolves a deeply nested property", (): void => { + const data: Record = { + users: [{ name: "Bob", age: 30 }] + }; + expect(format("{0.users.0.name} is {0.users.0.age}", data)).toBe("Bob is 30"); + }); + + test("resolves a numeric property on a nested object", (): void => { + expect( + format("Hello {0.name}, you have {0.messages.0} new messages.", { + name: "John", + messages: [5] + }) + ).toBe("Hello John, you have 5 new messages."); + }); + + test("returns 'undefined' for a missing property path", (): void => { + expect(format("{0.x.y}", { x: {} })).toBe("undefined"); + }); + + test("returns 'undefined' for a path through a null property", (): void => { + expect(format("{0.a.b}", { a: null })).toBe("undefined"); + }); + }); + + describe("dot-notation array access", (): void => { + test("resolves array elements by index", (): void => { + expect(format("First: {0.0}, Second: {0.1}", ["Alice", "Bob"])).toBe("First: Alice, Second: Bob"); + }); + + test("resolves nested arrays", (): void => { + expect(format("{0.0.0}", [["deep"]])).toBe("deep"); + }); + + test("returns 'undefined' for an out-of-bounds array index", (): void => { + expect(format("{0.5}", ["only"])).toBe("undefined"); + }); + }); + + describe("dot-notation array of objects", (): void => { + test("resolves object properties inside arrays", (): void => { + const users: Array> = [ + { name: "Alice", role: "admin" }, + { name: "Bob", role: "user" } + ]; + expect(format("{0.0.name} ({0.0.role}), {0.1.name} ({0.1.role})", users)).toBe("Alice (admin), Bob (user)"); + }); + }); + + describe("whole object and array arguments", (): void => { + test("serializes a plain object via JSON.stringify", (): void => { + expect(format("{0}", { key: "val" })).toBe('{"key":"val"}'); + }); + + test("serializes an array via JSON.stringify", (): void => { + expect(format("{0}", [1, 2, 3])).toBe("[1,2,3]"); + }); + + test("serializes a nested object via JSON.stringify", (): void => { + expect(format("{0}", { a: { b: 1 } })).toBe('{"a":{"b":1}}'); + }); + + test("returns 'undefined' for a Symbol argument", (): void => { + expect(format("{0}", Symbol("test"))).toBe("undefined"); + }); + + test("returns 'undefined' for a Function argument", (): void => { + expect(format("{0}", (): void => {})).toBe("undefined"); + }); + + test("returns 'undefined' for a circular object", (): void => { + const circular: Record = {}; + circular["self"] = circular; + expect(format("{0}", circular)).toBe("undefined"); + }); + }); + + describe("mixed type arguments", (): void => { + test("interpolates string, number, and boolean arguments", (): void => { + expect(format("{0} has {1} items, active: {2}", "Cart", 5, true)).toBe("Cart has 5 items, active: true"); + }); + + test("interpolates object and array arguments with dot-notation", (): void => { + expect(format("{0.name} bought {1.0} and {1.1}", { name: "Alice" }, ["Apple", "Banana"])).toBe( + "Alice bought Apple and Banana" + ); + }); + }); + + describe("no-placeholder templates", (): void => { + test("returns the template string as-is", (): void => { + expect(format("plain text")).toBe("plain text"); + }); + + test("returns the template as-is even when arguments are provided", (): void => { + expect(format("no placeholders", "arg1", "arg2")).toBe("no placeholders"); + }); + + test("returns the template as-is for an empty-arguments call", (): void => { + expect(format("hello")).toBe("hello"); + }); + }); + + describe("missing or out-of-range indices", (): void => { + test("returns 'undefined' for a missing argument index", (): void => { + expect(format("{0} {1}", "only")).toBe("only undefined"); + }); + + test("returns 'undefined' for an out-of-range index", (): void => { + expect(format("{5}", "a", "b")).toBe("undefined"); + }); + + test("returns 'undefined' when root argument is null with a dot path", (): void => { + expect(format("{0.name}", null)).toBe("undefined"); + }); + + test("returns 'undefined' when root argument is undefined with a dot path", (): void => { + expect(format("{0.name}", undefined)).toBe("undefined"); + }); + }); + + describe("malformed placeholders", (): void => { + test("preserves braces that do not match the placeholder pattern", (): void => { + expect(format("{ 0 }", "value")).toBe("{ 0 }"); + expect(format("{}", "value")).toBe("{}"); + expect(format("{abc}", "value")).toBe("{abc}"); + }); + + test("preserves unclosed braces", (): void => { + expect(format("{0 is open", "value")).toBe("{0 is open"); + }); + + test("preserves placeholders with negative indices", (): void => { + expect(format("{-1}", "value")).toBe("{-1}"); + }); + + test("preserves placeholders with leading dot paths", (): void => { + expect(format("{.name}", "value")).toBe("{.name}"); + }); + + test("preserves placeholders with trailing dots", (): void => { + expect(format("{0.}", "value")).toBe("{0.}"); + }); + + test("preserves placeholders with consecutive dots", (): void => { + expect(format("{0..name}", "value")).toBe("{0..name}"); + }); + + test("replaces only the inner placeholder when braces are doubled", (): void => { + expect(format("{{0}}", "value")).toBe("{value}"); + }); + }); + + describe("prototype and inherited properties", (): void => { + test("resolves own properties", (): void => { + const obj: Record = { own: "value" }; + expect(format("{0.own}", obj)).toBe("value"); + }); + + test("resolves inherited properties from the prototype chain", (): void => { + const proto: Record = { inherited: "parent" }; + const obj: Record = Object.create(proto) as Record; + expect(format("{0.inherited}", obj)).toBe("parent"); + }); + + test("returns 'undefined' for function-valued properties", (): void => { + expect(format("{0.toString}", {})).toBe("undefined"); + }); + }); + + describe("Unicode and combining marks", (): void => { + test("handles CJK characters", (): void => { + expect(format("Hello {0}", "世界")).toBe("Hello 世界"); + }); + + test("handles Thai characters", (): void => { + expect(format("สวัสดี {0}", "โลก")).toBe("สวัสดี โลก"); + }); + + test("handles Arabic characters", (): void => { + expect(format("{0} {1}", "مرحبا", "بالعالم")).toBe("مرحبا بالعالم"); + }); + + test("handles combining marks", (): void => { + expect(format("cafe\u0301: {0}", "open")).toBe("cafe\u0301: open"); + }); + + test("handles emoji", (): void => { + expect(format("{0} {1}", "Hello", "🌍")).toBe("Hello 🌍"); + }); + }); + + describe("empty and nullish", (): void => { + test("returns an empty string", (): void => { + expect(format("")).toBe(""); + expect(format(null)).toBe(""); + expect(format(undefined)).toBe(""); + }); + }); +}); + +describe("createFormatter", (): void => { + describe("valid template", (): void => { + test("returns a reusable function that formats arguments", (): void => { + const greet: (...args: unknown[]) => string = createFormatter("Hello {0}, welcome to {1}!"); + expect(greet("Alice", "Wonderland")).toBe("Hello Alice, welcome to Wonderland!"); + expect(greet("Bob", "the Matrix")).toBe("Hello Bob, welcome to the Matrix!"); + }); + + test("supports dot-notation in the pre-bound template", (): void => { + const fmt: (...args: unknown[]) => string = createFormatter("{0.name} is {0.age} years old"); + expect(fmt({ name: "Alice", age: 30 })).toBe("Alice is 30 years old"); + expect(fmt({ name: "Bob", age: 25 })).toBe("Bob is 25 years old"); + }); + + test("returns the template as-is when the formatter is called with no arguments", (): void => { + const fmt: (...args: unknown[]) => string = createFormatter("Hello {0}!"); + expect(fmt()).toBe("Hello undefined!"); + }); + }); + + describe("no-placeholder template", (): void => { + test("returns the template as-is on every call", (): void => { + const fmt: (...args: unknown[]) => string = createFormatter("static text"); + expect(fmt()).toBe("static text"); + expect(fmt("ignored")).toBe("static text"); + }); + }); + + describe("empty and nullish", (): void => { + test("returns a function that always returns an empty string", (): void => { + const fmtEmpty: (...args: unknown[]) => string = createFormatter(""); + expect(fmtEmpty("anything")).toBe(""); + + const fmtNull: (...args: unknown[]) => string = createFormatter(null); + expect(fmtNull("anything")).toBe(""); + + const fmtUndefined: (...args: unknown[]) => string = createFormatter(undefined); + expect(fmtUndefined("anything")).toBe(""); + }); + }); +}); From b664a7374ebca612e8f85ffe44a57e0037b9bfd5 Mon Sep 17 00:00:00 2001 From: rcpthongta <220981657+rcpthongta@users.noreply.github.com> Date: Wed, 23 Sep 2026 14:20:10 +0700 Subject: [PATCH 088/121] perf(format): add performance benchmarks for template formatting --- src/libs/format/format.lib.bench.ts | 178 ++++++++++++++++++++++++++++ 1 file changed, 178 insertions(+) create mode 100644 src/libs/format/format.lib.bench.ts diff --git a/src/libs/format/format.lib.bench.ts b/src/libs/format/format.lib.bench.ts new file mode 100644 index 0000000..41a22cf --- /dev/null +++ b/src/libs/format/format.lib.bench.ts @@ -0,0 +1,178 @@ +import { describe, test, TestContext } from "vitest"; + +import { createFormatter, format } from "./format.lib"; + +const manyTemplate: string = "{0} {1} {2} {3} {4} {5} {6} {7} {8} {9} " + "{0} {1} {2} {3} {4} {5} {6} {7} {8} {9}"; + +const fiftyTemplate: string = Array.from( + { length: 50 }, + (_value: unknown, index: number): string => `{${index % 10}}` +).join(" "); + +const greet: (...args: unknown[]) => string = createFormatter("Hello {0}, welcome to {1}!"); +const userFmt: (...args: unknown[]) => string = createFormatter("{0.name} ({0.role}) has {0.stats.score} points"); +const fiftyFmt: (...args: unknown[]) => string = createFormatter(fiftyTemplate); +const nullFmt: (...args: unknown[]) => string = createFormatter(null); +const staticFmt: (...args: unknown[]) => string = createFormatter("static text with no braces"); + +describe("format", (): void => { + test("short ASCII", ({ bench }: TestContext): void => { + bench("single placeholder with string", (): void => { + format("Hello {0}!", "World"); + }); + + bench("two placeholders with primitives", (): void => { + format("{0} has {1} items", "Cart", 5); + }); + + bench("boolean argument", (): void => { + format("Active: {0}", true); + }); + + bench("no brace in template (early return)", (): void => { + format("Hello World, no placeholders here"); + }); + }); + + test("long input", ({ bench }: TestContext): void => { + bench("multiple indexed arguments", (): void => { + format("User {0} scored {1} points on {2}, active: {3}, role: {4}", "Alice", 9500, "2025-01-15", true, "admin"); + }); + + bench("dot-notation object access", (): void => { + format("{0.name} ({0.role}) has {0.stats.score} points", { + name: "Alice", + role: "admin", + stats: { score: 9500 } + }); + }); + + bench("dot-notation array access", (): void => { + format("First: {0.0}, Second: {0.1}, Third: {0.2}", ["Alice", "Bob", "Charlie"]); + }); + + bench("deep nested array-of-objects", (): void => { + format("{0.users.0.name} and {0.users.1.name}", { + users: [{ name: "Alice" }, { name: "Bob" }] + }); + }); + + bench("JSON.stringify fallback for whole object", (): void => { + format("Data: {0}", { key: "value", nested: { a: 1 } }); + }); + }); + + test("Unicode and combining marks", ({ bench }: TestContext): void => { + bench("CJK arguments", (): void => { + format("{0}は{1}です", "今日", "良い天気"); + }); + + bench("Thai arguments", (): void => { + format("สวัสดี {0}", "โลก"); + }); + + bench("Arabic arguments", (): void => { + format("{0} {1}", "مرحبا", "بالعالم"); + }); + + bench("combining marks in template", (): void => { + format("cafe\u0301: {0}, re\u0301sume\u0301: {1}", "open", "ready"); + }); + }); + + test("excessive placeholders", ({ bench }: TestContext): void => { + bench("20 placeholders with 10 arguments", (): void => { + format(manyTemplate, "a", "b", "c", "d", "e", "f", "g", "h", "i", "j"); + }); + + bench("repeated same index 10 times", (): void => { + format("{0} {0} {0} {0} {0} {0} {0} {0} {0} {0}", "echo"); + }); + + bench("50 placeholders with 10 arguments", (): void => { + format(fiftyTemplate, "a", "b", "c", "d", "e", "f", "g", "h", "i", "j"); + }); + }); + + test("empty and nullish", ({ bench }: TestContext): void => { + bench("empty string", (): void => { + format(""); + }); + + bench("null", (): void => { + format(null); + }); + + bench("undefined", (): void => { + format(undefined); + }); + }); +}); + +describe("createFormatter", (): void => { + test("compile overhead", ({ bench }: TestContext): void => { + bench("simple template", (): void => { + createFormatter("Hello {0}!"); + }); + + bench("complex dot-notation", (): void => { + createFormatter("{0.name} ({0.role}) scored {1} on {2.date}"); + }); + + bench("20 placeholders", (): void => { + createFormatter("{0} {1} {2} {3} {4} {5} {6} {7} {8} {9} {0} {1} {2} {3} {4} {5} {6} {7} {8} {9}"); + }); + }); + + test("pre-compiled reuse", ({ bench }: TestContext): void => { + bench("simple template call", (): void => { + greet("Alice", "Wonderland"); + }); + + bench("dot-notation call", (): void => { + userFmt({ name: "Alice", role: "admin", stats: { score: 9500 } }); + }); + + bench("50-placeholder call", (): void => { + fiftyFmt("a", "b", "c", "d", "e", "f", "g", "h", "i", "j"); + }); + + bench("no-placeholder call", (): void => { + staticFmt("ignored"); + }); + + bench("null template call", (): void => { + nullFmt("anything"); + }); + }); +}); + +describe("format vs createFormatter comparison", (): void => { + test("simple template", async ({ bench }: TestContext): Promise => { + const template: string = "Hello {0}, welcome to {1}!"; + const compiled: (...args: unknown[]) => string = createFormatter(template); + + await bench.compare( + bench("format() one-shot", (): void => { + format(template, "Alice", "Wonderland"); + }), + bench("createFormatter() pre-compiled", (): void => { + compiled("Alice", "Wonderland"); + }) + ); + }); + + test("dot-notation template", async ({ bench }: TestContext): Promise => { + const template: string = "{0.name} ({0.role}) has {0.stats.score} points"; + const compiled: (...args: unknown[]) => string = createFormatter(template); + + await bench.compare( + bench("format() dot-notation one-shot", (): void => { + format(template, { name: "Alice", role: "admin", stats: { score: 9500 } }); + }), + bench("createFormatter() dot-notation pre-compiled", (): void => { + compiled({ name: "Alice", role: "admin", stats: { score: 9500 } }); + }) + ); + }); +}); From 1c511f6affd20a4dbd330548f125d4bd5f03c393 Mon Sep 17 00:00:00 2001 From: rcpthongta <220981657+rcpthongta@users.noreply.github.com> Date: Wed, 23 Sep 2026 14:37:27 +0700 Subject: [PATCH 089/121] feat(constants): add UNICODE_DIGIT and UNICODE_LETTER regex patterns --- src/constants/regex.constant.ts | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/constants/regex.constant.ts b/src/constants/regex.constant.ts index d454796..fcade8d 100644 --- a/src/constants/regex.constant.ts +++ b/src/constants/regex.constant.ts @@ -20,6 +20,16 @@ export const FORMAT_PLACEHOLDER_REGEX: RegExp = /\{(\d+(?:\.\w+)*)\}/g; */ export const NON_ALPHANUMERIC_REGEX: RegExp = /[^\p{L}\p{M}\p{N}]+/gu; +/** + * Matches a Unicode decimal digit character, including digits from non-ASCII scripts. + */ +export const UNICODE_DIGIT_REGEX: RegExp = /\p{Nd}/u; + +/** + * Matches a Unicode letter character or combining mark. + */ +export const UNICODE_LETTER_REGEX: RegExp = /[\p{L}\p{M}]/u; + /** * Matches strings that contain whitespace requiring normalization. * From dab2c89ee990118edcf1c0cc954e92febebe7ea3 Mon Sep 17 00:00:00 2001 From: rcpthongta <220981657+rcpthongta@users.noreply.github.com> Date: Wed, 23 Sep 2026 14:38:41 +0700 Subject: [PATCH 090/121] feat(constants): add ASTERISK and HASH string constants --- src/constants/string-values.constant.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/constants/string-values.constant.ts b/src/constants/string-values.constant.ts index b0e6c7b..4d93272 100644 --- a/src/constants/string-values.constant.ts +++ b/src/constants/string-values.constant.ts @@ -1,6 +1,8 @@ +export const ASTERISK: string = "*"; export const DOLLAR_SIGN: string = "$"; export const DOT: string = "."; export const EMPTY: string = ""; +export const HASH: string = "#"; export const HYPHEN: string = "-"; export const LEFT_BRACE: string = "{"; export const RIGHT_BRACE: string = "}"; From 96d7d56945e0581f3392f5ddb1579c5e9939e900 Mon Sep 17 00:00:00 2001 From: rcpthongta <220981657+rcpthongta@users.noreply.github.com> Date: Wed, 23 Sep 2026 14:39:20 +0700 Subject: [PATCH 091/121] feat(types): add CharacterSegmentMode boundary type --- src/types/character-segment-mode.type.ts | 8 ++++++++ src/types/index.ts | 1 + 2 files changed, 9 insertions(+) create mode 100644 src/types/character-segment-mode.type.ts create mode 100644 src/types/index.ts diff --git a/src/types/character-segment-mode.type.ts b/src/types/character-segment-mode.type.ts new file mode 100644 index 0000000..f799053 --- /dev/null +++ b/src/types/character-segment-mode.type.ts @@ -0,0 +1,8 @@ +/** + * Character segmentation mode used to determine character boundaries. + * + * - `"utf16"`: Operates on UTF-16 code units (fastest, default). + * - `"codePoint"`: Operates on Unicode code points (handles single emojis and surrogate pairs). + * - `"grapheme"`: Operates on user-perceived grapheme clusters (handles complex emoji sequences and combining marks). + */ +export type CharacterSegmentMode = "utf16" | "codePoint" | "grapheme"; diff --git a/src/types/index.ts b/src/types/index.ts new file mode 100644 index 0000000..41b14e0 --- /dev/null +++ b/src/types/index.ts @@ -0,0 +1 @@ +export * from "./character-segment-mode.type"; From 91f7b820fc57cf01d434da8de2841a8904cc433b Mon Sep 17 00:00:00 2001 From: rcpthongta <220981657+rcpthongta@users.noreply.github.com> Date: Wed, 23 Sep 2026 14:39:56 +0700 Subject: [PATCH 092/121] feat(mask): add pattern-based string masking utility --- src/libs/index.ts | 1 + src/libs/mask/index.ts | 1 + src/libs/mask/mask.lib.ts | 771 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 773 insertions(+) create mode 100644 src/libs/mask/index.ts create mode 100644 src/libs/mask/mask.lib.ts diff --git a/src/libs/index.ts b/src/libs/index.ts index bc5d0c6..151868d 100644 --- a/src/libs/index.ts +++ b/src/libs/index.ts @@ -8,6 +8,7 @@ export * from "./format"; export * from "./has-length"; export * from "./has-text"; export * from "./kebab-case"; +export * from "./mask"; export * from "./pascal-case"; export * from "./slugify"; export * from "./snake-case"; diff --git a/src/libs/mask/index.ts b/src/libs/mask/index.ts new file mode 100644 index 0000000..2ed2b49 --- /dev/null +++ b/src/libs/mask/index.ts @@ -0,0 +1 @@ +export * from "./mask.lib"; diff --git a/src/libs/mask/mask.lib.ts b/src/libs/mask/mask.lib.ts new file mode 100644 index 0000000..7158a44 --- /dev/null +++ b/src/libs/mask/mask.lib.ts @@ -0,0 +1,771 @@ +import { ASTERISK, EMPTY, HASH, UNICODE_DIGIT_REGEX, UNICODE_LETTER_REGEX } from "../../constants"; +import { CharacterSegmentMode } from "../../types"; + +import { hasLength } from "../has-length"; + +/** + * A reusable masking function returned by {@link createMask}. + * + * @param value - The string to mask, or `null` or `undefined`. + * @returns The formatted string, or an empty string when `value` is empty, `null`, or `undefined`. + */ +export type MaskFunction = (value: string | null | undefined) => string; + +/** + * Configuration options for {@link mask} and {@link createMask}. + */ +export interface MaskOptions { + /** + * Additional mask patterns to evaluate when the primary pattern does not match. + * + * Patterns are evaluated in declaration order. + * + * @default [] + */ + fallbackPatterns?: string[]; + + /** + * Determines how characters are counted and segmented. + * + * @default "utf16" + */ + mode?: CharacterSegmentMode; +} + +interface CompiledPatternUtf16 { + readonly pattern: string; + readonly totalSlots: number; +} + +interface CompiledPatternSegments { + readonly patternSegments: readonly string[]; + readonly totalSlots: number; +} + +const LETTER_A: string = "A"; +const CODE_HASH: number = 35; +const CODE_ASTERISK: number = 42; +const CODE_LETTER_A: number = 65; +const CODE_ZERO: number = 48; +const CODE_NINE: number = 57; +const CODE_UPPER_A: number = 65; +const CODE_UPPER_Z: number = 90; +const CODE_LOWER_A: number = 97; +const CODE_LOWER_Z: number = 122; +const ASCII_MAX: number = 127; + +let graphemeSegmenter: Intl.Segmenter | undefined; + +/** + * Lazily creates the grapheme segmenter used by `"grapheme"` mode. + */ +function getGraphemeSegmenter(): Intl.Segmenter { + if (!graphemeSegmenter) { + graphemeSegmenter = new Intl.Segmenter(undefined, { granularity: "grapheme" }); + } + + return graphemeSegmenter; +} + +/** + * Determines whether a character is a supported mask slot token. + * + * Supported tokens are `#`, `A`, and `*`. + */ +function isSlotToken(token: string): boolean { + return token === HASH || token === LETTER_A || token === ASTERISK; +} + +/** + * Determines whether a character represents a decimal digit. + * + * Supports ASCII digits (`0-9`) and Unicode decimal digits (`\p{Nd}`). + */ +function isDigit(char: string): boolean { + const charCode: number = char.charCodeAt(0); + + if (charCode >= CODE_ZERO && charCode <= CODE_NINE) { + return true; + } + + return charCode > ASCII_MAX && UNICODE_DIGIT_REGEX.test(char); +} + +/** + * Determines whether a character represents a letter or combining mark. + * + * Supports ASCII letters (`a-z`, `A-Z`) and Unicode letters or marks (`[\p{L}\p{M}]`). + */ +function isLetter(char: string): boolean { + const charCode: number = char.charCodeAt(0); + + if ( + (charCode >= CODE_UPPER_A && charCode <= CODE_UPPER_Z) || + (charCode >= CODE_LOWER_A && charCode <= CODE_LOWER_Z) + ) { + return true; + } + + return charCode > ASCII_MAX && UNICODE_LETTER_REGEX.test(char); +} + +/** + * Determines whether the character at the specified UTF-16 index is a decimal digit. + */ +function isDigitAt(value: string, charIndex: number): boolean { + const charCode: number = value.charCodeAt(charIndex); + + if (charCode >= CODE_ZERO && charCode <= CODE_NINE) { + return true; + } + + return charCode > ASCII_MAX && UNICODE_DIGIT_REGEX.test(value[charIndex]); +} + +/** + * Determines whether the character at the specified UTF-16 index is a letter or combining mark. + */ +function isLetterAt(value: string, charIndex: number): boolean { + const charCode: number = value.charCodeAt(charIndex); + + if ( + (charCode >= CODE_UPPER_A && charCode <= CODE_UPPER_Z) || + (charCode >= CODE_LOWER_A && charCode <= CODE_LOWER_Z) + ) { + return true; + } + + return charCode > ASCII_MAX && UNICODE_LETTER_REGEX.test(value[charIndex]); +} + +/** + * Advances to the next decimal digit in a UTF-16 string. + */ +function advanceToDigit(value: string, startIndex: number, length: number): number { + let currentIndex: number = startIndex; + + while (currentIndex < length && !isDigitAt(value, currentIndex)) { + currentIndex++; + } + + return currentIndex; +} + +/** + * Advances to the next letter in a UTF-16 string. + */ +function advanceToLetter(value: string, startIndex: number, length: number): number { + let currentIndex: number = startIndex; + + while (currentIndex < length && !isLetterAt(value, currentIndex)) { + currentIndex++; + } + + return currentIndex; +} + +/** + * Advances to the next value index matching a mask token in UTF-16 mode. + */ +function advanceSlotIndex(patternCharCode: number, value: string, startIndex: number, length: number): number { + if (patternCharCode === CODE_HASH) { + return advanceToDigit(value, startIndex, length); + } + + if (patternCharCode === CODE_LETTER_A) { + return advanceToLetter(value, startIndex, length); + } + + return startIndex; +} + +/** + * Advances to the next value segment matching a mask token in segmented mode. + */ +function advanceSlotSegmentIndex( + patternToken: string, + valueSegments: readonly string[], + startIndex: number, + length: number +): number { + if (patternToken === HASH) { + let currentIndex: number = startIndex; + + while (currentIndex < length && !isDigit(valueSegments[currentIndex])) { + currentIndex++; + } + + return currentIndex; + } + + if (patternToken === LETTER_A) { + let currentIndex: number = startIndex; + + while (currentIndex < length && !isLetter(valueSegments[currentIndex])) { + currentIndex++; + } + + return currentIndex; + } + + return startIndex; +} + +/** + * Verifies that all literal segments match between the pattern and value. + */ +function matchLiteralSegments( + patternSegments: readonly string[], + valueSegments: readonly string[], + length: number +): boolean { + for (let segmentIndex: number = 0; segmentIndex < length; segmentIndex++) { + if (patternSegments[segmentIndex] !== valueSegments[segmentIndex]) { + return false; + } + } + + return true; +} + +/** + * Handles matching for patterns that contain no mask slots. + */ +function tryMaskEmptySlotSegments( + patternSegments: readonly string[], + patternLength: number, + valueSegments: readonly string[], + valueLength: number +): string | null { + if (patternLength !== valueLength || !matchLiteralSegments(patternSegments, valueSegments, patternLength)) { + return null; + } + + return valueSegments.join(EMPTY); +} + +/** + * Counts mask slot tokens (`#`, `A`, and `*`) in a UTF-16 pattern. + */ +function countSlotsInString(pattern: string): number { + let totalSlots: number = 0; + + for (let charIndex: number = 0; charIndex < pattern.length; charIndex++) { + const charCode: number = pattern.charCodeAt(charIndex); + + if (charCode === CODE_HASH || charCode === CODE_LETTER_A || charCode === CODE_ASTERISK) { + totalSlots++; + } + } + + return totalSlots; +} + +/** + * Counts mask slot tokens (`#`, `A`, and `*`) in a segmented pattern. + */ +function countSlotsInSegments(segments: readonly string[]): number { + let totalSlots: number = 0; + + for (let segmentIndex: number = 0; segmentIndex < segments.length; segmentIndex++) { + if (isSlotToken(segments[segmentIndex])) { + totalSlots++; + } + } + + return totalSlots; +} + +/** + * Splits a string into user-perceived grapheme clusters. + */ +function splitGraphemes(value: string): string[] { + const segments: string[] = []; + + for (const segmentData of getGraphemeSegmenter().segment(value)) { + segments.push(segmentData.segment); + } + + return segments; +} + +/** + * Splits a string into Unicode code points. + */ +function splitCodePoints(value: string): string[] { + return Array.from(value); +} + +/** + * Compiles a UTF-16 mask pattern with pre-computed slot metadata. + */ +function compilePatternUtf16(pattern: string): CompiledPatternUtf16 { + return { + pattern, + totalSlots: countSlotsInString(pattern) + }; +} + +/** + * Compiles a segmented mask pattern with pre-computed slot metadata. + */ +function compilePatternSegments(patternSegments: string[]): CompiledPatternSegments { + return { + patternSegments, + totalSlots: countSlotsInSegments(patternSegments) + }; +} + +/** + * Checks whether a literal pattern code unit matches the current UTF-16 value code unit. + */ +function isCharLiteralMatched( + value: string, + valueIndex: number, + valueLength: number, + patternCharCode: number +): boolean { + return valueIndex < valueLength && value.charCodeAt(valueIndex) === patternCharCode; +} + +/** + * Checks whether a literal pattern segment matches the current value segment. + */ +function isSegmentLiteralMatched( + valueSegments: readonly string[], + valueIndex: number, + valueLength: number, + patternToken: string +): boolean { + return valueIndex < valueLength && valueSegments[valueIndex] === patternToken; +} + +/** + * Attempts to apply a UTF-16 mask pattern directly to a string. + * + * `#` matches a decimal digit, `A` matches a letter or combining mark, and `*` matches any single UTF-16 code unit. Other characters are treated as literals. + */ +function tryMaskUtf16Raw(pattern: string, totalSlots: number, value: string): string | null { + if (totalSlots === 0) { + return pattern === value ? pattern : null; + } + + const valueLength: number = value.length; + + if (valueLength < totalSlots) { + return null; + } + + const patternLength: number = pattern.length; + let result: string = EMPTY; + let valueIndex: number = 0; + let slotsFilled: number = 0; + + for (let patternIndex: number = 0; patternIndex < patternLength; patternIndex++) { + const patternCharCode: number = pattern.charCodeAt(patternIndex); + + if (patternCharCode === CODE_HASH || patternCharCode === CODE_LETTER_A || patternCharCode === CODE_ASTERISK) { + valueIndex = advanceSlotIndex(patternCharCode, value, valueIndex, valueLength); + + if (valueIndex >= valueLength) { + return null; + } + + result += value[valueIndex]; + valueIndex++; + slotsFilled++; + } else { + result += pattern[patternIndex]; + + if (isCharLiteralMatched(value, valueIndex, valueLength, patternCharCode)) { + valueIndex++; + } + } + } + + return slotsFilled !== totalSlots || valueIndex < valueLength ? null : result; +} + +/** + * Attempts to apply a pre-compiled UTF-16 mask pattern to a string. + */ +function tryMaskUtf16(compiled: CompiledPatternUtf16, value: string): string | null { + return tryMaskUtf16Raw(compiled.pattern, compiled.totalSlots, value); +} + +/** + * Attempts to apply a segmented mask pattern directly to segmented input. + * + * `#` matches a decimal digit, `A` matches a letter or combining mark, and `*` matches any single segment. Other segments are treated as literals. + */ +function tryMaskSegmentsRaw( + patternSegments: readonly string[], + totalSlots: number, + valueSegments: readonly string[] +): string | null { + const patternLength: number = patternSegments.length; + const valueLength: number = valueSegments.length; + + if (totalSlots === 0) { + return tryMaskEmptySlotSegments(patternSegments, patternLength, valueSegments, valueLength); + } + + if (valueLength < totalSlots) { + return null; + } + + let result: string = EMPTY; + let valueIndex: number = 0; + let slotsFilled: number = 0; + + for (let patternIndex: number = 0; patternIndex < patternLength; patternIndex++) { + const patternToken: string = patternSegments[patternIndex]; + + if (isSlotToken(patternToken)) { + valueIndex = advanceSlotSegmentIndex(patternToken, valueSegments, valueIndex, valueLength); + + if (valueIndex >= valueLength) { + return null; + } + + result += valueSegments[valueIndex]; + valueIndex++; + slotsFilled++; + } else { + result += patternToken; + + if (isSegmentLiteralMatched(valueSegments, valueIndex, valueLength, patternToken)) { + valueIndex++; + } + } + } + + if (slotsFilled !== totalSlots || valueIndex < valueLength) { + return null; + } + + return result; +} + +/** + * Attempts to apply a pre-compiled segmented mask pattern to segmented input. + */ +function tryMaskSegments(compiled: CompiledPatternSegments, valueSegments: readonly string[]): string | null { + return tryMaskSegmentsRaw(compiled.patternSegments, compiled.totalSlots, valueSegments); +} + +/** + * Executes pre-compiled UTF-16 patterns in declaration order. + */ +function executeMaskUtf16(compiledPatterns: readonly CompiledPatternUtf16[], value: string): string { + const totalPatterns: number = compiledPatterns.length; + + for (let patternIndex: number = 0; patternIndex < totalPatterns; patternIndex++) { + const formatted: string | null = tryMaskUtf16(compiledPatterns[patternIndex], value); + + if (formatted !== null) { + return formatted; + } + } + + return value; +} + +/** + * Executes pre-compiled segmented patterns in declaration order. + */ +function executeMaskSegments( + compiledPatterns: readonly CompiledPatternSegments[], + valueSegments: readonly string[], + value: string +): string { + const totalPatterns: number = compiledPatterns.length; + + for (let patternIndex: number = 0; patternIndex < totalPatterns; patternIndex++) { + const formatted: string | null = tryMaskSegments(compiledPatterns[patternIndex], valueSegments); + + if (formatted !== null) { + return formatted; + } + } + + return value; +} + +/** + * Collects non-empty primary and fallback mask patterns in declaration order. + */ +function collectValidPatterns(pattern: string, fallbackPatterns: readonly string[]): string[] { + const validPatterns: string[] = []; + + if (pattern.length > 0) { + validPatterns.push(pattern); + } + + const fallbackCount: number = fallbackPatterns.length; + + for (let fallbackIndex: number = 0; fallbackIndex < fallbackCount; fallbackIndex++) { + const fallbackItem: string = fallbackPatterns[fallbackIndex]; + + if (fallbackItem.length > 0) { + validPatterns.push(fallbackItem); + } + } + + return validPatterns; +} + +/** + * Formats a string according to a mask pattern. + * + * Supported slot tokens: + * - `#`: Decimal digit (`0-9` or `\p{Nd}`). + * - `A`: Letter or combining mark (`a-z`, `A-Z`, or `[\p{L}\p{M}]`). + * - `*`: Wildcard character. + * + * Returns the original string when `value` does not match the pattern. + * Returns an empty string when `value` is empty, `null`, or `undefined`. + * + * @param value - The string to mask, or `null` or `undefined`. + * @param pattern - The mask template pattern. + * @returns The formatted string, or an empty string when `value` is empty, `null`, or `undefined`. + * + * @example + * + * mask("0812345678", "(###) ###-####"); // "(081) 234-5678" + * mask("1234567890123456", "#### #### #### ####"); // "1234 5678 9012 3456" + * mask("ABC1234", "AAA-####"); // "ABC-1234" + * mask("K9V1B2", "A#A #A#"); // "K9V 1B2" + * mask("12345", "(###) ###-####"); // "12345" + * mask("", "(###) ###-####"); // "" + * mask(null, "(###) ###-####"); // "" + * mask(undefined, "(###) ###-####"); // "" + */ +export function mask(value: string | null | undefined, pattern: string): string; + +/** + * Formats a string according to a mask pattern using custom options. + * + * The `mode` option determines how characters are counted. + * + * Returns the original string when `value` does not match the pattern or any fallback pattern. + * Returns an empty string when `value` is empty, `null`, or `undefined`. + * + * @param value - The string to mask, or `null` or `undefined`. + * @param pattern - The primary mask template pattern. + * @param options - Masking options. + * @returns The formatted string, or an empty string when `value` is empty, `null`, or `undefined`. + * + * @example + * + * mask("021234567", "(###) ###-####", { fallbackPatterns: ["(##) ###-####"] }); // "(02) 123-4567" + * mask("0812345678", "(###) ###-####", { fallbackPatterns: ["(##) ###-####"] }); // "(081) 234-5678" + * mask("12345", "(###) ###-####", { fallbackPatterns: ["(##) ###-####"] }); // "12345" + * mask("😀😀123", "**-###", { mode: "codePoint" }); // "😀😀-123" + * mask("", "(###) ###-####"); // "" + * mask(null, "(###) ###-####"); // "" + * mask(undefined, "(###) ###-####"); // "" + */ +export function mask(value: string | null | undefined, pattern: string, options: MaskOptions): string; + +export function mask(value: string | null | undefined, pattern: string, options?: MaskOptions): string { + if (!hasLength(value)) { + return EMPTY; + } + + if (!options) { + if (pattern.length === 0) { + return value; + } + + const totalSlots: number = countSlotsInString(pattern); + const masked: string | null = tryMaskUtf16Raw(pattern, totalSlots, value); + + return masked ?? value; + } + + const { fallbackPatterns, mode = "utf16" }: MaskOptions = options; + + if (!fallbackPatterns || fallbackPatterns.length === 0) { + if (pattern.length === 0) { + return value; + } + + if (mode === "utf16") { + const totalSlots: number = countSlotsInString(pattern); + const masked: string | null = tryMaskUtf16Raw(pattern, totalSlots, value); + + return masked ?? value; + } + + if (mode === "codePoint") { + const patternSegments: string[] = splitCodePoints(pattern); + const totalSlots: number = countSlotsInSegments(patternSegments); + const valueSegments: string[] = splitCodePoints(value); + const masked: string | null = tryMaskSegmentsRaw(patternSegments, totalSlots, valueSegments); + + return masked ?? value; + } + + const patternSegments: string[] = splitGraphemes(pattern); + const totalSlots: number = countSlotsInSegments(patternSegments); + const valueSegments: string[] = splitGraphemes(value); + const masked: string | null = tryMaskSegmentsRaw(patternSegments, totalSlots, valueSegments); + + return masked ?? value; + } + + const allPatterns: string[] = collectValidPatterns(pattern, fallbackPatterns); + + if (allPatterns.length === 0) { + return value; + } + + if (mode === "grapheme") { + const compiledGraphemes: CompiledPatternSegments[] = allPatterns.map(splitGraphemes).map(compilePatternSegments); + + return executeMaskSegments(compiledGraphemes, splitGraphemes(value), value); + } + + if (mode === "codePoint") { + const compiledCodePoints: CompiledPatternSegments[] = allPatterns.map(splitCodePoints).map(compilePatternSegments); + + return executeMaskSegments(compiledCodePoints, splitCodePoints(value), value); + } + + const compiledUtf16: CompiledPatternUtf16[] = allPatterns.map(compilePatternUtf16); + + return executeMaskUtf16(compiledUtf16, value); +} + +/** + * Creates a reusable masking function for a mask pattern. + * + * Returns the original string when `value` does not match the pattern. + * Returns an empty string when `value` is empty, `null`, or `undefined`. + * + * @param pattern - The mask template pattern. + * @returns A function that masks input strings according to the pattern. + * + * @example + * + * const maskPhone = createMask("(###) ###-####"); + * + * maskPhone("0812345678"); // "(081) 234-5678" + * maskPhone("12345"); // "12345" + * maskPhone(""); // "" + * maskPhone(null); // "" + * maskPhone(undefined); // "" + */ +export function createMask(pattern: string): MaskFunction; + +/** + * Creates a reusable masking function using custom options. + * + * The `mode` option determines how characters are counted. + * + * Returns the original string when `value` does not match the pattern or any fallback pattern. + * Returns an empty string when `value` is empty, `null`, or `undefined`. + * + * @param pattern - The primary mask template pattern. + * @param options - Masking options. + * @returns A function that masks input strings according to the pattern and options. + * + * @example + * + * const maskPhone = createMask("(###) ###-####", { fallbackPatterns: ["(##) ###-####"] }); + * + * maskPhone("021234567"); // "(02) 123-4567" + * maskPhone("0812345678"); // "(081) 234-5678" + * maskPhone("12345"); // "12345" + * maskPhone(""); // "" + * maskPhone(null); // "" + * maskPhone(undefined); // "" + */ +export function createMask(pattern: string, options: MaskOptions): MaskFunction; + +export function createMask(pattern: string, options?: MaskOptions): MaskFunction { + const { fallbackPatterns, mode = "utf16" }: MaskOptions = options ?? {}; + + if (!fallbackPatterns || fallbackPatterns.length === 0) { + if (pattern.length === 0) { + return (value: string | null | undefined): string => { + return value ?? EMPTY; + }; + } + + if (mode === "utf16") { + const totalSlots: number = countSlotsInString(pattern); + + return (value: string | null | undefined): string => { + if (!hasLength(value)) { + return EMPTY; + } + + const masked: string | null = tryMaskUtf16Raw(pattern, totalSlots, value); + + return masked ?? value; + }; + } + + if (mode === "codePoint") { + const patternSegments: string[] = splitCodePoints(pattern); + const totalSlots: number = countSlotsInSegments(patternSegments); + + return (value: string | null | undefined): string => { + if (!hasLength(value)) { + return EMPTY; + } + + const valueSegments: string[] = splitCodePoints(value); + const masked: string | null = tryMaskSegmentsRaw(patternSegments, totalSlots, valueSegments); + + return masked ?? value; + }; + } + + const patternSegments: string[] = splitGraphemes(pattern); + const totalSlots: number = countSlotsInSegments(patternSegments); + + return (value: string | null | undefined): string => { + if (!hasLength(value)) { + return EMPTY; + } + + const valueSegments: string[] = splitGraphemes(value); + const masked: string | null = tryMaskSegmentsRaw(patternSegments, totalSlots, valueSegments); + + return masked ?? value; + }; + } + + const allPatterns: string[] = collectValidPatterns(pattern, fallbackPatterns); + + if (allPatterns.length === 0) { + return (value: string | null | undefined): string => { + return value ?? EMPTY; + }; + } + + if (mode === "grapheme") { + const compiledGraphemes: CompiledPatternSegments[] = allPatterns.map(splitGraphemes).map(compilePatternSegments); + + return (value: string | null | undefined): string => { + return !hasLength(value) ? EMPTY : executeMaskSegments(compiledGraphemes, splitGraphemes(value), value); + }; + } + + if (mode === "codePoint") { + const compiledCodePoints: CompiledPatternSegments[] = allPatterns.map(splitCodePoints).map(compilePatternSegments); + + return (value: string | null | undefined): string => { + return !hasLength(value) ? EMPTY : executeMaskSegments(compiledCodePoints, splitCodePoints(value), value); + }; + } + + const compiledUtf16: CompiledPatternUtf16[] = allPatterns.map(compilePatternUtf16); + + return (value: string | null | undefined): string => { + return !hasLength(value) ? EMPTY : executeMaskUtf16(compiledUtf16, value); + }; +} From 3e53f8a4e7ccdbfb6d4a5a2c839269cda35421e1 Mon Sep 17 00:00:00 2001 From: rcpthongta <220981657+rcpthongta@users.noreply.github.com> Date: Wed, 23 Sep 2026 14:40:35 +0700 Subject: [PATCH 093/121] test(mask): add unit tests for string masking utility --- src/libs/mask/mask.lib.spec.ts | 296 +++++++++++++++++++++++++++++++++ 1 file changed, 296 insertions(+) create mode 100644 src/libs/mask/mask.lib.spec.ts diff --git a/src/libs/mask/mask.lib.spec.ts b/src/libs/mask/mask.lib.spec.ts new file mode 100644 index 0000000..b1e2388 --- /dev/null +++ b/src/libs/mask/mask.lib.spec.ts @@ -0,0 +1,296 @@ +import { describe, expect, test } from "vitest"; + +import { createMask, mask } from "./mask.lib"; + +describe("mask", (): void => { + describe("digit patterns (#)", (): void => { + test("formats phone numbers correctly", (): void => { + expect(mask("0812345678", "(###) ###-####")).toBe("(081) 234-5678"); + expect(mask("0812345678", "###-###-####")).toBe("081-234-5678"); + expect(mask("1234567", "###-####")).toBe("123-4567"); + }); + + test("formats credit card numbers with spaces", (): void => { + expect(mask("1234567890123456", "#### #### #### ####")).toBe("1234 5678 9012 3456"); + }); + + test("formats date strings", (): void => { + expect(mask("20260822", "####-##-##")).toBe("2026-08-22"); + expect(mask("22082026", "##/##/####")).toBe("22/08/2026"); + }); + + test("formats strings with country code prefix literals", (): void => { + expect(mask("812345678", "+66 ##-###-####")).toBe("+66 81-234-5678"); + }); + + test("handles already partially formatted or separator-containing inputs", (): void => { + expect(mask("081-234-5678", "(###) ###-####")).toBe("(081) 234-5678"); + expect(mask("(081) 234-5678", "(###) ###-####")).toBe("(081) 234-5678"); + expect(mask("123-45-6789", "###-##-####")).toBe("123-45-6789"); + }); + }); + + describe("letter patterns (A)", (): void => { + test("formats letter-only masks", (): void => { + expect(mask("HELLOWORLD", "AAAAA-AAAAA")).toBe("HELLO-WORLD"); + expect(mask("abcdef", "AA-AA-AA")).toBe("ab-cd-ef"); + expect(mask("ABCD", "AA-AA")).toBe("AB-CD"); + expect(mask("---abc---def", "AAA-AAA")).toBe("abc-def"); + }); + }); + + describe("wildcard patterns (*)", (): void => { + test("accepts any character including digits, letters, and symbols", (): void => { + expect(mask("A1-B2", "**-**")).toBe("A1-B2"); + expect(mask("ABC123", "***-***")).toBe("ABC-123"); + expect(mask("ABC-123", "***-***")).toBe("ABC-123"); + expect(mask("X#9$", "****")).toBe("X#9$"); + }); + }); + + describe("mixed token patterns (#, A, *)", (): void => { + test("formats postal codes, vehicle plates, and serial numbers", (): void => { + expect(mask("K9V1B2", "A#A #A#")).toBe("K9V 1B2"); + expect(mask("ABC1234", "AAA-####")).toBe("ABC-1234"); + expect(mask("AB12CD34", "AA##-**##")).toBe("AB12-CD34"); + }); + }); + + describe("non-matching input", (): void => { + test("returns original value when input does not match pattern", (): void => { + expect(mask("081", "(###) ###-####")).toBe("081"); + expect(mask("0812", "(###) ###-####")).toBe("0812"); + expect(mask("081234", "(###) ###-####")).toBe("081234"); + expect(mask("AB", "AAA-####")).toBe("AB"); + expect(mask("12345678909999", "###-###")).toBe("12345678909999"); + expect(mask("abc", "###-###")).toBe("abc"); + expect(mask("123", "AAA-AAA")).toBe("123"); + expect(mask("---", "AAA")).toBe("---"); + expect(mask("---", "###")).toBe("---"); + expect(mask("12-", "###")).toBe("12-"); + expect(mask("AB-", "AAA")).toBe("AB-"); + }); + }); + + describe("fallback patterns", (): void => { + test("selects exact slot count match when available in declaration order", (): void => { + const options: { fallbackPatterns: string[] } = { + fallbackPatterns: ["(##) ###-####"] + }; + + expect(mask("021234567", "(###) ###-####", options)).toBe("(02) 123-4567"); + expect(mask("0812345678", "(###) ###-####", options)).toBe("(081) 234-5678"); + }); + + test("chooses best matching pattern from fallback list in declaration order", (): void => { + const options: { fallbackPatterns: string[] } = { + fallbackPatterns: ["###-####", "###-###-####"] + }; + + expect(mask("1234567", "##-###-####", options)).toBe("123-4567"); + expect(mask("1234567890", "##-###-####", options)).toBe("123-456-7890"); + }); + + test("returns original value when neither primary nor fallback patterns match", (): void => { + const options: { fallbackPatterns: string[] } = { + fallbackPatterns: ["(##) ###-####"] + }; + + expect(mask("08123", "(###) ###-####", options)).toBe("08123"); + expect(mask("123456789012", "(###) ###-####", options)).toBe("123456789012"); + }); + + test("handles fallback patterns in codePoint mode", (): void => { + const options: { fallbackPatterns: string[]; mode: "codePoint" } = { + fallbackPatterns: ["**-###", "***-###"], + mode: "codePoint" + }; + + expect(mask("😀😀123", "####-###", options)).toBe("😀😀-123"); + expect(mask("😀😀😀123", "####-###", { fallbackPatterns: ["AA-###", "***-###"], mode: "codePoint" })).toBe( + "😀😀😀-123" + ); + expect(mask("12", "####-###", options)).toBe("12"); + }); + + test("handles fallback patterns in grapheme mode", (): void => { + const options: { fallbackPatterns: string[]; mode: "grapheme" } = { + fallbackPatterns: ["*-##", "*-###"], + mode: "grapheme" + }; + + expect(mask("👨‍👩‍👧‍👦12", "AAA-####", options)).toBe("👨‍👩‍👧‍👦-12"); + expect(mask("👨‍👩‍👧‍👦123", "AAA-####", options)).toBe("👨‍👩‍👧‍👦-123"); + expect(mask("1", "AAA-####", options)).toBe("1"); + }); + }); + + describe("Unicode and combining marks", (): void => { + test("handles Unicode letters and decimal digits across scripts", (): void => { + expect(mask("กขค123", "AAA-###")).toBe("กขค-123"); + expect(mask("สวัสดี", "AA-AAAA")).toBe("สว-ัสดี"); + expect(mask("๑๒๓๔๕๖", "###-###")).toBe("๑๒๓-๔๕๖"); + expect(mask("กขค123", "AAA-###", { mode: "utf16" })).toBe("กขค-123"); + expect(mask("กขค123", "AAA-###", { mode: "codePoint" })).toBe("กขค-123"); + expect(mask("กขค123", "AAA-###", { mode: "grapheme" })).toBe("กขค-123"); + }); + + test("treats surrogate pairs as 2 code units in utf16 mode", (): void => { + expect(mask("😀", "**", { mode: "utf16" })).toBe("😀"); + expect(mask("😀", "*", { mode: "utf16" })).toBe("😀"); + expect(mask("😀", "*", { mode: "codePoint" })).toBe("😀"); + expect(mask("😀", "*", { mode: "grapheme" })).toBe("😀"); + }); + + test("handles codePoint mode with emojis and surrogate pairs", (): void => { + expect(mask("😀😀123", "**-###", { mode: "codePoint" })).toBe("😀😀-123"); + expect(mask("🎉🎊🎈✨", "**-**", { mode: "codePoint" })).toBe("🎉🎊-🎈✨"); + expect(mask("😀", "**-**", { mode: "codePoint" })).toBe("😀"); + expect(mask("😀-😀", "*-*", { mode: "codePoint" })).toBe("😀-😀"); + expect(mask("😀😀-😀😀", "**-**", { mode: "codePoint" })).toBe("😀😀-😀😀"); + expect(mask("😀abc", "*-###", { mode: "codePoint" })).toBe("😀abc"); + expect(mask("123456", "###", { mode: "codePoint" })).toBe("123456"); + expect(mask("ABCDEF", "AAA", { mode: "codePoint" })).toBe("ABCDEF"); + expect(mask("😀😀", "AA", { mode: "codePoint" })).toBe("😀😀"); + expect(mask("😀😀", "##", { mode: "codePoint" })).toBe("😀😀"); + expect(mask("😀", "*-*", { mode: "codePoint" })).toBe("😀"); + }); + + test("handles grapheme mode with complex emojis and combining marks", (): void => { + expect(mask("👨‍👩‍👧‍👦123", "*-###", { mode: "grapheme" })).toBe("👨‍👩‍👧‍👦-123"); + expect(mask("cafe\u030112", "AAAA-##", { mode: "grapheme" })).toBe("cafe\u0301-12"); + expect(mask("cafe\u0301ABC", "AAAA-AAA", { mode: "grapheme" })).toBe("cafe\u0301-ABC"); + expect(mask("😀", "*-*", { mode: "grapheme" })).toBe("😀"); + expect(mask("😀-😀", "*-*", { mode: "grapheme" })).toBe("😀-😀"); + expect(mask("😀😀-😀😀", "**-**", { mode: "grapheme" })).toBe("😀😀-😀😀"); + expect(mask("😀abc", "*-###", { mode: "grapheme" })).toBe("😀abc"); + expect(mask("123456", "###", { mode: "grapheme" })).toBe("123456"); + expect(mask("ABCDEF", "AAA", { mode: "grapheme" })).toBe("ABCDEF"); + expect(mask("👨‍👩‍👧‍👦", "AA", { mode: "grapheme" })).toBe("👨‍👩‍👧‍👦"); + expect(mask("👨‍👩‍👧‍👦", "##", { mode: "grapheme" })).toBe("👨‍👩‍👧‍👦"); + expect(mask("👨‍👩‍👧‍👦", "*-*", { mode: "grapheme" })).toBe("👨‍👩‍👧‍👦"); + }); + }); + + describe("createMask factory", (): void => { + test("creates a reusable masking function with default options", (): void => { + const maskPhone: (value: string | null | undefined) => string = createMask("(###) ###-####"); + + expect(maskPhone("0812345678")).toBe("(081) 234-5678"); + expect(maskPhone("0899999999")).toBe("(089) 999-9999"); + expect(maskPhone("0812")).toBe("0812"); + expect(maskPhone("")).toBe(""); + }); + + test("creates a reusable masking function with fallbackPatterns in utf16, codePoint, and grapheme modes", (): void => { + const maskFlexiblePhone: (value: string | null | undefined) => string = createMask("(###) ###-####", { + fallbackPatterns: ["(##) ###-####"] + }); + + expect(maskFlexiblePhone("021234567")).toBe("(02) 123-4567"); + expect(maskFlexiblePhone("0812345678")).toBe("(081) 234-5678"); + expect(maskFlexiblePhone("0812")).toBe("0812"); + expect(maskFlexiblePhone("")).toBe(""); + + const maskCPWithFallbacks: (value: string | null | undefined) => string = createMask("####-###", { + fallbackPatterns: ["**-###"], + mode: "codePoint" + }); + + expect(maskCPWithFallbacks("😀😀123")).toBe("😀😀-123"); + expect(maskCPWithFallbacks("12")).toBe("12"); + expect(maskCPWithFallbacks("")).toBe(""); + + const maskGraphWithFallbacks: (value: string | null | undefined) => string = createMask("AAA-####", { + fallbackPatterns: ["*-##"], + mode: "grapheme" + }); + + expect(maskGraphWithFallbacks("👨‍👩‍👧‍👦12")).toBe("👨‍👩‍👧‍👦-12"); + expect(maskGraphWithFallbacks("1")).toBe("1"); + expect(maskGraphWithFallbacks("")).toBe(""); + }); + + test("creates a reusable masking function with codePoint and grapheme modes", (): void => { + const maskCodePoint: (value: string | null | undefined) => string = createMask("**-###", { mode: "codePoint" }); + const maskGrapheme: (value: string | null | undefined) => string = createMask("*-###", { mode: "grapheme" }); + + expect(maskCodePoint("😀😀123")).toBe("😀😀-123"); + expect(maskCodePoint("😀")).toBe("😀"); + expect(maskGrapheme("👨‍👩‍👧‍👦123")).toBe("👨‍👩‍👧‍👦-123"); + expect(maskGrapheme("👨‍👩‍👧‍👦")).toBe("👨‍👩‍👧‍👦"); + expect(maskCodePoint("")).toBe(""); + expect(maskGrapheme("")).toBe(""); + }); + }); + + describe("empty and unusual patterns", (): void => { + test("returns original value when pattern is empty and no fallbacks", (): void => { + expect(mask("Hello World", "")).toBe("Hello World"); + expect(mask("Hello World", "", { mode: "utf16" })).toBe("Hello World"); + expect(mask("Hello World", "", { mode: "codePoint" })).toBe("Hello World"); + expect(mask("Hello World", "", { mode: "grapheme" })).toBe("Hello World"); + expect(mask("Hello World", "", { fallbackPatterns: ["", ""] })).toBe("Hello World"); + expect(mask("Hello World", "", { fallbackPatterns: ["", ""], mode: "codePoint" })).toBe("Hello World"); + expect(mask("Hello World", "", { fallbackPatterns: ["", ""], mode: "grapheme" })).toBe("Hello World"); + + expect(createMask("")("Hello World")).toBe("Hello World"); + expect(createMask("", { mode: "codePoint" })("Hello World")).toBe("Hello World"); + expect(createMask("", { mode: "grapheme" })("Hello World")).toBe("Hello World"); + expect(createMask("", { fallbackPatterns: ["", ""] })("Hello World")).toBe("Hello World"); + expect(createMask("", { fallbackPatterns: ["", ""], mode: "codePoint" })("Hello World")).toBe("Hello World"); + expect(createMask("", { fallbackPatterns: ["", ""], mode: "grapheme" })("Hello World")).toBe("Hello World"); + expect(createMask("")("")).toBe(""); + }); + + test("handles literal-only patterns matching and non-matching", (): void => { + expect(mask("---", "---")).toBe("---"); + expect(mask("abc", "---")).toBe("abc"); + expect(mask("---", "---", { mode: "grapheme" })).toBe("---"); + expect(mask("abc", "---", { mode: "grapheme" })).toBe("abc"); + expect(mask("---", "--", { mode: "grapheme" })).toBe("---"); + expect(mask("---", "-x-", { mode: "grapheme" })).toBe("---"); + expect(mask("---", "---", { mode: "codePoint" })).toBe("---"); + expect(mask("abc", "---", { mode: "codePoint" })).toBe("abc"); + expect(mask("---", "--", { mode: "codePoint" })).toBe("---"); + expect(mask("---", "-x-", { mode: "codePoint" })).toBe("---"); + }); + }); + + describe("empty and nullish", (): void => { + test("returns an empty string", (): void => { + expect(mask("", "(###) ###-####")).toBe(""); + expect(mask("", "(###) ###-####", { fallbackPatterns: ["(##) ###-####"] })).toBe(""); + expect(mask("", "(###) ###-####", { mode: "codePoint" })).toBe(""); + expect(mask("", "(###) ###-####", { mode: "grapheme" })).toBe(""); + expect(mask(null, "(###) ###-####")).toBe(""); + expect(mask(undefined, "(###) ###-####")).toBe(""); + expect(mask(null, "(###) ###-####", { fallbackPatterns: ["(##) ###-####"] })).toBe(""); + expect(mask(undefined, "(###) ###-####", { fallbackPatterns: ["(##) ###-####"] })).toBe(""); + expect(mask(null, "(###) ###-####", { mode: "codePoint" })).toBe(""); + expect(mask(undefined, "(###) ###-####", { mode: "codePoint" })).toBe(""); + expect(mask(null, "(###) ###-####", { mode: "grapheme" })).toBe(""); + expect(mask(undefined, "(###) ###-####", { mode: "grapheme" })).toBe(""); + + const compiled: (value: string | null | undefined) => string = createMask("(###) ###-####"); + const compiledCP: (value: string | null | undefined) => string = createMask("**-###", { mode: "codePoint" }); + const compiledGraph: (value: string | null | undefined) => string = createMask("*-###", { mode: "grapheme" }); + const compiledEmpty: (value: string | null | undefined) => string = createMask(""); + + const compiledEmptyFallbacks: (value: string | null | undefined) => string = createMask("", { + fallbackPatterns: ["", ""] + }); + + expect(compiled(null)).toBe(""); + expect(compiled(undefined)).toBe(""); + expect(compiledCP(null)).toBe(""); + expect(compiledCP(undefined)).toBe(""); + expect(compiledGraph(null)).toBe(""); + expect(compiledGraph(undefined)).toBe(""); + expect(compiledEmpty(null)).toBe(""); + expect(compiledEmpty(undefined)).toBe(""); + expect(compiledEmptyFallbacks(null)).toBe(""); + expect(compiledEmptyFallbacks(undefined)).toBe(""); + }); + }); +}); From 6a16276b2ce5d0f36f9651539103d5943293dc44 Mon Sep 17 00:00:00 2001 From: rcpthongta <220981657+rcpthongta@users.noreply.github.com> Date: Wed, 23 Sep 2026 14:40:59 +0700 Subject: [PATCH 094/121] perf(mask): add performance benchmarks for pattern string masking --- src/libs/mask/mask.lib.bench.ts | 173 ++++++++++++++++++++++++++++++++ 1 file changed, 173 insertions(+) create mode 100644 src/libs/mask/mask.lib.bench.ts diff --git a/src/libs/mask/mask.lib.bench.ts b/src/libs/mask/mask.lib.bench.ts new file mode 100644 index 0000000..15f4f5b --- /dev/null +++ b/src/libs/mask/mask.lib.bench.ts @@ -0,0 +1,173 @@ +import { describe, test, TestContext } from "vitest"; + +import { createMask, mask } from "./mask.lib"; + +const compiledPhoneMask: (value: string | null | undefined) => string = createMask("(###) ###-####"); +const compiledFlexibleMask: (value: string | null | undefined) => string = createMask("(###) ###-####", { + fallbackPatterns: ["(##) ###-####"] +}); +const compiledUnicodeMask: (value: string | null | undefined) => string = createMask("*-*-*-*-*-*", { + mode: "grapheme" +}); + +const longDigits: string = "0812345678".repeat(100); +const longMixedInput: string = "ABC1234XYZ5678".repeat(50); +const longSeparators: string = "---081---234---5678---".repeat(50); + +describe("mask", (): void => { + test("short ASCII", ({ bench }: TestContext): void => { + bench("phone number mask", (): void => { + mask("0812345678", "(###) ###-####"); + }); + + bench("credit card mask", (): void => { + mask("1234567890123456", "#### #### #### ####"); + }); + + bench("postal code mask with letters and digits", (): void => { + mask("K9V1B2", "A#A #A#"); + }); + }); + + test("long input", ({ bench }: TestContext): void => { + bench("long digits non-matching", (): void => { + mask(longDigits, "(###) ###-####"); + }); + + bench("long mixed alphanumeric non-matching", (): void => { + mask(longMixedInput, "AAA-####-AAA-####"); + }); + + bench("long separators non-matching", (): void => { + mask(longSeparators, "(###) ###-####"); + }); + + bench("long serial alphanumeric code", (): void => { + mask("HTTP20API1234567890XYZ9999ABCD", "AAAA-##-AAA-####-####-####-AAA-####"); + }); + }); + + test("Unicode and combining marks", ({ bench }: TestContext): void => { + bench("Thai letters and digits", (): void => { + mask("กขค123456", "AAA-###-###"); + }); + + bench("combining marks with grapheme mode", (): void => { + mask("cafe\u0301123456", "AAAA-###-###", { mode: "grapheme" }); + }); + + bench("emojis with codePoint mode", (): void => { + mask("😀😀123456", "**-###-###", { mode: "codePoint" }); + }); + }); + + test("excessive separators", ({ bench }: TestContext): void => { + bench("input heavily padded with separators and hyphens", (): void => { + mask("---081---234---5678---", "(###) ###-####"); + }); + }); + + test("3-mode comparison", ({ bench }: TestContext): void => { + bench("ASCII [utf16]", (): void => { + mask("0812345678", "(###) ###-####", { mode: "utf16" }); + }); + + bench("ASCII [codePoint]", (): void => { + mask("0812345678", "(###) ###-####", { mode: "codePoint" }); + }); + + bench("ASCII [grapheme]", (): void => { + mask("0812345678", "(###) ###-####", { mode: "grapheme" }); + }); + + bench("Emoji [utf16]", (): void => { + mask("😀😀😀😀", "**-**", { mode: "utf16" }); + }); + + bench("Emoji [codePoint]", (): void => { + mask("😀😀😀😀", "**-**", { mode: "codePoint" }); + }); + + bench("Emoji [grapheme]", (): void => { + mask("😀😀😀😀", "**-**", { mode: "grapheme" }); + }); + }); + + test("empty and nullish", ({ bench }: TestContext): void => { + bench("empty string", (): void => { + mask("", "(###) ###-####"); + }); + + bench("null", (): void => { + mask(null, "(###) ###-####"); + }); + + bench("undefined", (): void => { + mask(undefined, "(###) ###-####"); + }); + }); +}); + +describe("createMask", (): void => { + test("compile overhead", ({ bench }: TestContext): void => { + bench("simple pattern", (): void => { + createMask("(###) ###-####"); + }); + + bench("alphanumeric pattern", (): void => { + createMask("AAA-####-AAA-####"); + }); + + bench("pattern with fallbackPatterns", (): void => { + createMask("(###) ###-####", { fallbackPatterns: ["(##) ###-####"] }); + }); + + bench("grapheme mode pattern", (): void => { + createMask("*-*-*-*-*-*", { mode: "grapheme" }); + }); + }); + + test("pre-compiled reuse", ({ bench }: TestContext): void => { + bench("phone number call", (): void => { + compiledPhoneMask("0812345678"); + }); + + bench("fallbackPatterns call", (): void => { + compiledFlexibleMask("021234567"); + }); + + bench("grapheme mode call", (): void => { + compiledUnicodeMask("👨‍👩‍👧‍👦👨‍👩‍👧‍👦👨‍👩‍👧‍👦👨‍👩‍👧‍👦👨‍👩‍👧‍👦👨‍👩‍👧‍👦"); + }); + }); +}); + +describe("mask vs createMask comparison", (): void => { + test("phone number pattern", async ({ bench }: TestContext): Promise => { + const pattern: string = "(###) ###-####"; + const compiled: (value: string | null | undefined) => string = createMask(pattern); + + await bench.compare( + bench("mask() one-shot", (): void => { + mask("0812345678", pattern); + }), + bench("createMask() pre-compiled", (): void => { + compiled("0812345678"); + }) + ); + }); + + test("grapheme mode pattern", async ({ bench }: TestContext): Promise => { + const pattern: string = "*-*-*-*-*-*"; + const compiled: (value: string | null | undefined) => string = createMask(pattern, { mode: "grapheme" }); + + await bench.compare( + bench("mask() grapheme one-shot", (): void => { + mask("👨‍👩‍👧‍👦👨‍👩‍👧‍👦👨‍👩‍👧‍👦👨‍👩‍👧‍👦👨‍👩‍👧‍👦👨‍👩‍👧‍👦", pattern, { mode: "grapheme" }); + }), + bench("createMask() grapheme pre-compiled", (): void => { + compiled("👨‍👩‍👧‍👦👨‍👩‍👧‍👦👨‍👩‍👧‍👦👨‍👩‍👧‍👦👨‍👩‍👧‍👦👨‍👩‍👧‍👦"); + }) + ); + }); +}); From 0c8cfa8465375566946dbd879df4b959a9ace5fd Mon Sep 17 00:00:00 2001 From: rcpthongta <220981657+rcpthongta@users.noreply.github.com> Date: Wed, 23 Sep 2026 14:53:03 +0700 Subject: [PATCH 095/121] feat(redact): add sensitive string redaction utility --- src/libs/index.ts | 1 + src/libs/redact/index.ts | 1 + src/libs/redact/redact.lib.ts | 447 ++++++++++++++++++++++++++++++++++ 3 files changed, 449 insertions(+) create mode 100644 src/libs/redact/index.ts create mode 100644 src/libs/redact/redact.lib.ts diff --git a/src/libs/index.ts b/src/libs/index.ts index 151868d..b89652f 100644 --- a/src/libs/index.ts +++ b/src/libs/index.ts @@ -10,6 +10,7 @@ export * from "./has-text"; export * from "./kebab-case"; export * from "./mask"; export * from "./pascal-case"; +export * from "./redact"; export * from "./slugify"; export * from "./snake-case"; export * from "./uncapitalize"; diff --git a/src/libs/redact/index.ts b/src/libs/redact/index.ts new file mode 100644 index 0000000..08dce8b --- /dev/null +++ b/src/libs/redact/index.ts @@ -0,0 +1 @@ +export * from "./redact.lib"; diff --git a/src/libs/redact/redact.lib.ts b/src/libs/redact/redact.lib.ts new file mode 100644 index 0000000..4fb7067 --- /dev/null +++ b/src/libs/redact/redact.lib.ts @@ -0,0 +1,447 @@ +import { ASTERISK, EMPTY } from "../../constants"; +import { CharacterSegmentMode } from "../../types"; + +import { hasLength } from "../has-length"; + +/** + * Configuration options for {@link redact}. + */ +export interface RedactOptions { + /** + * Number of visible characters to preserve at the start of the string. + * + * Non-finite, negative, and zero values default to `0`. + * Fractional values are truncated toward zero. + * + * @default 0 + */ + visibleStart?: number; + + /** + * Number of visible characters to preserve at the end of the string. + * + * Non-finite, negative, and zero values default to `0`. + * Fractional values are truncated toward zero. + * + * @default 0 + */ + visibleEnd?: number; + + /** + * String used to replace each redacted character. + * + * @default "*" + */ + replacement?: string; + + /** + * Determines how characters are counted and segmented. + * + * @default "utf16" + */ + mode?: CharacterSegmentMode; +} + +const SURROGATE_LEAD_MIN: number = 0xd800; +const SURROGATE_LEAD_MAX: number = 0xdbff; +const SURROGATE_TRAIL_MIN: number = 0xdc00; +const SURROGATE_TRAIL_MAX: number = 0xdfff; + +let graphemeSegmenter: Intl.Segmenter | undefined; + +/** + * Lazily creates the `Intl.Segmenter` used for grapheme segmentation. + */ +function getGraphemeSegmenter(): Intl.Segmenter { + if (!graphemeSegmenter) { + graphemeSegmenter = new Intl.Segmenter(undefined, { granularity: "grapheme" }); + } + + return graphemeSegmenter; +} + +/** + * Checks whether a UTF-16 code unit is a high surrogate. + */ +function isLeadSurrogate(charCode: number): boolean { + return charCode >= SURROGATE_LEAD_MIN && charCode <= SURROGATE_LEAD_MAX; +} + +/** + * Checks whether a UTF-16 code unit is a low surrogate. + */ +function isTrailSurrogate(charCode: number): boolean { + return charCode >= SURROGATE_TRAIL_MIN && charCode <= SURROGATE_TRAIL_MAX; +} + +/** + * Converts a visibility count to a non-negative integer. + * + * Fractional values are truncated toward zero. Invalid values become `0`. + */ +function toValidCount(value: number | undefined): number { + return typeof value === "number" && value > 0 && Number.isFinite(value) ? Math.trunc(value) : 0; +} + +/** + * Adjusts the start boundary forward to prevent splitting a UTF-16 surrogate pair. + */ +function adjustSurrogateStart(value: string, visibleStart: number, length: number): number { + if (visibleStart > 0 && visibleStart < length) { + const previousCode: number = value.charCodeAt(visibleStart - 1); + + if (isLeadSurrogate(previousCode)) { + const currentCode: number = value.charCodeAt(visibleStart); + + if (isTrailSurrogate(currentCode)) { + return visibleStart + 1; + } + } + } + + return visibleStart; +} + +/** + * Adjusts the end boundary backward to prevent splitting a UTF-16 surrogate pair. + */ +function adjustSurrogateEnd(value: string, visibleEnd: number, length: number): number { + if (visibleEnd > 0 && visibleEnd < length) { + const cutIndex: number = length - visibleEnd; + const currentCode: number = value.charCodeAt(cutIndex); + + if (isTrailSurrogate(currentCode)) { + const previousCode: number = value.charCodeAt(cutIndex - 1); + + if (isLeadSurrogate(previousCode)) { + return visibleEnd + 1; + } + } + } + + return visibleEnd; +} + +/** + * Returns the UTF-16 code unit length of the Unicode code point at the specified index. + */ +function getCodePointStep(value: string, charIndex: number, length: number): number { + const code: number = value.charCodeAt(charIndex); + + if (isLeadSurrogate(code) && charIndex + 1 < length) { + const nextCode: number = value.charCodeAt(charIndex + 1); + + if (isTrailSurrogate(nextCode)) { + return 2; + } + } + + return 1; +} + +/** + * Counts the total Unicode code points in a string. + */ +function countCodePoints(value: string, length: number): number { + let charIndex: number = 0; + let totalCount: number = 0; + + while (charIndex < length) { + totalCount++; + charIndex += getCodePointStep(value, charIndex, length); + } + + return totalCount; +} + +/** + * Collects the starting UTF-16 index of each Unicode code point in a string. + */ +function collectCodePointIndices(value: string, length: number): number[] { + const indices: number[] = []; + + let charIndex: number = 0; + + while (charIndex < length) { + indices.push(charIndex); + + charIndex += getCodePointStep(value, charIndex, length); + } + + return indices; +} + +/** + * Redacts a string using Unicode code point boundaries. + */ +function redactCodePoint( + value: string, + visibleStart: number, + visibleEnd: number, + replacement: string, + length: number +): string { + if (visibleStart === 0 && visibleEnd === 0) { + return replacement.repeat(countCodePoints(value, length)); + } + + const indices: number[] = collectCodePointIndices(value, length); + const totalCodePoints: number = indices.length; + + if (visibleStart + visibleEnd >= totalCodePoints) { + return value; + } + + const redactCount: number = totalCodePoints - visibleStart - visibleEnd; + + if (visibleStart > 0) { + const prefixEndIndex: number = indices[visibleStart]; + + if (visibleEnd > 0) { + return ( + value.slice(0, prefixEndIndex) + + replacement.repeat(redactCount) + + value.slice(indices[totalCodePoints - visibleEnd]) + ); + } + + return value.slice(0, prefixEndIndex) + replacement.repeat(redactCount); + } + + return replacement.repeat(redactCount) + value.slice(indices[totalCodePoints - visibleEnd]); +} + +/** + * Counts the grapheme clusters in a string. + */ +function countGraphemes(value: string): number { + const iterator: Intl.SegmentIterator = getGraphemeSegmenter().segment(value)[Symbol.iterator](); + + let totalSegments: number = 0; + + while (!iterator.next().done) { + totalSegments++; + } + + return totalSegments; +} + +/** + * Collects the UTF-16 start index of each grapheme cluster in a string. + */ +function collectGraphemeIndices(value: string): number[] { + const indices: number[] = []; + + for (const segmentData of getGraphemeSegmenter().segment(value)) { + indices.push(segmentData.index); + } + + return indices; +} + +/** + * Redacts a string using grapheme cluster boundaries. + */ +function redactGrapheme(value: string, visibleStart: number, visibleEnd: number, replacement: string): string { + if (visibleStart === 0 && visibleEnd === 0) { + return replacement.repeat(countGraphemes(value)); + } + + const indices: number[] = collectGraphemeIndices(value); + const totalSegments: number = indices.length; + + if (visibleStart + visibleEnd >= totalSegments) { + return value; + } + + const redactCount: number = totalSegments - visibleStart - visibleEnd; + + if (visibleStart > 0) { + const prefixEndIndex: number = indices[visibleStart]; + + if (visibleEnd > 0) { + return ( + value.slice(0, prefixEndIndex) + + replacement.repeat(redactCount) + + value.slice(indices[totalSegments - visibleEnd]) + ); + } + + return value.slice(0, prefixEndIndex) + replacement.repeat(redactCount); + } + + return replacement.repeat(redactCount) + value.slice(indices[totalSegments - visibleEnd]); +} + +/** + * Redacts a string using UTF-16 code units while preventing split surrogate pairs. + */ +function redactUtf16( + value: string, + visibleStart: number, + visibleEnd: number, + replacement: string, + length: number +): string { + const totalVisible: number = visibleStart + visibleEnd; + + if (totalVisible >= length) { + return value; + } + + const safeStart: number = adjustSurrogateStart(value, visibleStart, length); + const safeEnd: number = adjustSurrogateEnd(value, visibleEnd, length); + const safeTotal: number = safeStart + safeEnd; + + if (safeTotal >= length) { + return value; + } + + const redactLength: number = length - safeTotal; + + if (safeStart > 0) { + return safeEnd > 0 + ? value.slice(0, safeStart) + replacement.repeat(redactLength) + value.slice(length - safeEnd) + : value.slice(0, safeStart) + replacement.repeat(redactLength); + } + + return safeEnd > 0 ? replacement.repeat(redactLength) + value.slice(length - safeEnd) : replacement.repeat(length); +} + +/** + * Redacts every character in a string with `*`. + * + * Each character is replaced with `*`. + * Returns an empty string when `value` is empty, `null`, or `undefined`. + * + * @param value - The string to redact, or `null` or `undefined`. + * @returns The redacted string, or an empty string when `value` is empty, `null`, or `undefined`. + * + * @example + * + * redact("0812345678"); // "**********" + * redact("Hello World"); // "***********" + * redact(""); // "" + * redact(null); // "" + * redact(undefined); // "" + */ +export function redact(value: string | null | undefined): string; + +/** + * Redacts every character in a string using a custom replacement string. + * + * The replacement string is repeated once for each character. + * Returns an empty string when `value` is empty, `null`, or `undefined`. + * + * @param value - The string to redact, or `null` or `undefined`. + * @param replacement - The string used to replace each redacted character. + * @returns The redacted string, or an empty string when `value` is empty, `null`, or `undefined`. + * + * @example + * + * redact("0812345678", "#"); // "##########" + * redact("Hello World", "+"); // "+++++++++++" + * redact("12345", "XX"); // "XXXXXXXXXX" + * redact("", "#"); // "" + * redact(null, "#"); // "" + * redact(undefined, "#"); // "" + */ +export function redact(value: string | null | undefined, replacement: string): string; + +/** + * Redacts a string while preserving a specified number of characters at the beginning. + * + * Preserves the first `visibleStart` characters and replaces the rest with `*`. + * Non-finite, negative, and zero values default to `0`. + * Fractional values are truncated toward zero. + * + * Surrogate pairs at the boundary are preserved without splitting. + * + * @param value - The string to redact, or `null` or `undefined`. + * @param visibleStart - The number of characters to keep visible at the beginning. + * @returns The partially redacted string, or an empty string when `value` is empty, `null`, or `undefined`. + * + * @example + * + * redact("0812345678", 2); // "08********" + * redact("0812345678", 4); // "0812******" + * redact("0812345678", 10); // "0812345678" + * redact("0812345678", -1); // "**********" + * redact("0812345678", 2.9); // "08********" + * redact("", 2); // "" + * redact(null, 2); // "" + * redact(undefined, 2); // "" + */ +export function redact(value: string | null | undefined, visibleStart: number): string; + +/** + * Redacts a string while preserving the specified number of characters at the beginning and end. + * + * The `mode` option determines how characters are counted. + * + * Returns the original string when all characters are visible. + * Returns an empty string when `value` is empty, `null`, or `undefined`. + * + * @param value - The string to redact, or `null` or `undefined`. + * @param options - Redaction options. + * @returns The redacted string, or an empty string when `value` is empty, `null`, or `undefined`. + * + * @example + * + * redact("0812345678", { visibleStart: 3, visibleEnd: 2 }); // "081*****78" + * redact("0812345678", { visibleStart: 2, replacement: "#" }); // "08########" + * redact("0812345678", { visibleEnd: 2, replacement: "#" }); // "########78" + * redact("1234567890", { visibleStart: 4, visibleEnd: 4, replacement: "X" }); // "1234XX7890" + * redact("😀😀😀😀", { visibleStart: 1, mode: "codePoint" }); // "😀***" + * redact("👨‍👩‍👧‍👦👨‍👩‍👧‍👦", { visibleStart: 1, mode: "grapheme" }); // "👨‍👩‍👧‍👦*" + * redact("", { visibleStart: 2 }); // "" + * redact(null, { visibleStart: 2 }); // "" + * redact(undefined, { visibleStart: 2 }); // "" + */ +export function redact(value: string | null | undefined, options: RedactOptions): string; + +export function redact(value: string | null | undefined, options?: string | number | RedactOptions): string { + if (!hasLength(value)) { + return EMPTY; + } + + const length: number = value.length; + + if (options === undefined) { + return ASTERISK.repeat(length); + } + + if (typeof options === "string") { + return options.repeat(length); + } + + if (typeof options === "number") { + let visibleStart: number = toValidCount(options); + + if (visibleStart >= length) { + return value; + } + + if (visibleStart <= 0) { + return ASTERISK.repeat(length); + } + + visibleStart = adjustSurrogateStart(value, visibleStart, length); + + return value.slice(0, visibleStart) + ASTERISK.repeat(length - visibleStart); + } + + const { visibleStart: startOpt, visibleEnd: endOpt, replacement = ASTERISK, mode = "utf16" }: RedactOptions = options; + const visibleStart: number = toValidCount(startOpt); + const visibleEnd: number = toValidCount(endOpt); + + if (mode === "grapheme") { + return redactGrapheme(value, visibleStart, visibleEnd, replacement); + } + + if (mode === "codePoint") { + return redactCodePoint(value, visibleStart, visibleEnd, replacement, length); + } + + return redactUtf16(value, visibleStart, visibleEnd, replacement, length); +} From 35cbf2995b89c72ad05abfd869a639d56327112d Mon Sep 17 00:00:00 2001 From: rcpthongta <220981657+rcpthongta@users.noreply.github.com> Date: Wed, 23 Sep 2026 14:54:22 +0700 Subject: [PATCH 096/121] test(redact): add unit tests for sensitive string redaction --- src/libs/redact/redact.lib.spec.ts | 128 +++++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/libs/redact/redact.lib.spec.ts diff --git a/src/libs/redact/redact.lib.spec.ts b/src/libs/redact/redact.lib.spec.ts new file mode 100644 index 0000000..092e791 --- /dev/null +++ b/src/libs/redact/redact.lib.spec.ts @@ -0,0 +1,128 @@ +import { describe, expect, test } from "vitest"; + +import { redact } from "./redact.lib"; + +describe("redact", (): void => { + describe("short ASCII", (): void => { + test("redacts the entire string with asterisks by default", (): void => { + expect(redact("0812345678")).toBe("**********"); + expect(redact("Hello World")).toBe("***********"); + }); + + test("redacts using a custom replacement string", (): void => { + expect(redact("0812345678", "#")).toBe("##########"); + expect(redact("12345", "XX")).toBe("XXXXXXXXXX"); + }); + + test("preserves the specified number of starting characters", (): void => { + expect(redact("0812345678", 2)).toBe("08********"); + expect(redact("0812345678", 4)).toBe("0812******"); + expect(redact("0812345678", 10)).toBe("0812345678"); + expect(redact("0812345678", 12)).toBe("0812345678"); + expect(redact("0812345678", -1)).toBe("**********"); + expect(redact("0812345678", 0)).toBe("**********"); + expect(redact("0812345678", Number.NaN)).toBe("**********"); + expect(redact("0812345678", Number.POSITIVE_INFINITY)).toBe("**********"); + expect(redact("0812345678", 2.9)).toBe("08********"); + }); + }); + + describe("options object", (): void => { + test("handles visibleStart and visibleEnd", (): void => { + expect(redact("0812345678", { visibleStart: 3, visibleEnd: 2 })).toBe("081*****78"); + expect(redact("0812345678", { visibleStart: 2, replacement: "#" })).toBe("08########"); + expect(redact("0812345678", { visibleEnd: 2, replacement: "#" })).toBe("########78"); + expect(redact("0812345678", { visibleEnd: 2 })).toBe("********78"); + expect(redact("0812345678", { visibleStart: 2 })).toBe("08********"); + expect(redact("0812345678", { replacement: "#" })).toBe("##########"); + expect(redact("0812345678", {})).toBe("**********"); + expect(redact("0812345678", { visibleStart: 3, visibleEnd: 2, replacement: "#" })).toBe("081#####78"); + expect(redact("1234567890", { visibleStart: 4, visibleEnd: 4, replacement: "X" })).toBe("1234XX7890"); + expect(redact("12345", { visibleStart: 0, visibleEnd: 0 })).toBe("*****"); + expect(redact("12345", { visibleStart: -2, visibleEnd: -3 })).toBe("*****"); + expect(redact("12345", { visibleStart: Number.NaN, visibleEnd: Number.NaN })).toBe("*****"); + expect(redact("12345", { visibleStart: Number.POSITIVE_INFINITY, visibleEnd: 0 })).toBe("*****"); + expect(redact("12345", { visibleStart: 0, visibleEnd: Number.POSITIVE_INFINITY })).toBe("*****"); + expect(redact("12345", { visibleStart: 2.7, visibleEnd: 1.2 })).toBe("12**5"); + }); + + test("returns the original string when visibility exceeds length", (): void => { + expect(redact("123", { visibleStart: 2, visibleEnd: 2 })).toBe("123"); + expect(redact("123", { visibleStart: 3 })).toBe("123"); + expect(redact("123", { visibleEnd: 3 })).toBe("123"); + expect(redact("123", { visibleStart: 4 })).toBe("123"); + }); + + test("returns the original string when surrogate adjustments cover the whole string", (): void => { + expect(redact("😀a", { visibleStart: 1, visibleEnd: 1 })).toBe("😀a"); + expect(redact("a😀", { visibleStart: 1, visibleEnd: 1 })).toBe("a😀"); + expect(redact("😀😀", { visibleStart: 1, visibleEnd: 1 })).toBe("😀😀"); + }); + }); + + describe("Unicode and combining marks", (): void => { + test("redacts Unicode characters using default UTF-16 mode", (): void => { + expect(redact("Привет")).toBe("******"); + expect(redact("你好世界")).toBe("****"); + expect(redact("مرحبا")).toBe("*****"); + expect(redact("สวัสดี")).toBe("******"); + expect(redact("café")).toBe("****"); + expect(redact("cafe\u0301")).toBe("*****"); + expect(redact(" \u00A0 \u3000 ")).toBe("*****"); + expect(redact("😀")).toBe("**"); + expect(redact("😀", { mode: "utf16" })).toBe("**"); + }); + + test("prevents surrogate pair splitting in default mode", (): void => { + expect(redact("😀1234", 1)).toBe("😀****"); + expect(redact("1234😀", { visibleEnd: 1 })).toBe("****😀"); + expect(redact("😀1234😀", { visibleStart: 1, visibleEnd: 1 })).toBe("😀****😀"); + expect(redact("\uD83D", { mode: "utf16" })).toBe("*"); + expect(redact("\uDC00", { mode: "utf16" })).toBe("*"); + expect(redact("\uD83D\uD83D", 1)).toBe("\uD83D*"); + expect(redact("\uD83D\uD83D", { visibleEnd: 1 })).toBe("*\uD83D"); + expect(redact("a\uDC00", { visibleEnd: 1 })).toBe("*\uDC00"); + }); + + test("redacts per Unicode code point when mode is 'codePoint'", (): void => { + expect(redact("😀", { mode: "codePoint" })).toBe("*"); + expect(redact("😀😀😀", { mode: "codePoint" })).toBe("***"); + expect(redact("😀😀😀", { mode: "codePoint", replacement: "#" })).toBe("###"); + expect(redact("cafe\u0301", { mode: "codePoint" })).toBe("*****"); + expect(redact("😀123😀", { visibleStart: 1, visibleEnd: 1, mode: "codePoint" })).toBe("😀***😀"); + expect(redact("😀123😀", { visibleStart: 1, mode: "codePoint" })).toBe("😀****"); + expect(redact("😀123😀", { visibleEnd: 1, mode: "codePoint" })).toBe("****😀"); + expect(redact("😀", { visibleStart: 1, mode: "codePoint" })).toBe("😀"); + expect(redact("😀😁", { visibleStart: 1, visibleEnd: 1, mode: "codePoint" })).toBe("😀😁"); + expect(redact("😀😁", { visibleStart: 3, mode: "codePoint" })).toBe("😀😁"); + expect(redact("\uD83D", { mode: "codePoint" })).toBe("*"); + expect(redact("\uD83Da", { mode: "codePoint" })).toBe("**"); + }); + + test("redacts per grapheme cluster when mode is 'grapheme'", (): void => { + expect(redact("😀", { mode: "grapheme" })).toBe("*"); + expect(redact("😀😀😀", { mode: "grapheme" })).toBe("***"); + expect(redact("😀😀😀", { mode: "grapheme", replacement: "#" })).toBe("###"); + expect(redact("👨‍👩‍👧‍👦", { mode: "grapheme" })).toBe("*"); + expect(redact("cafe\u0301", { mode: "grapheme" })).toBe("****"); + expect(redact("😀123😀", { visibleStart: 1, visibleEnd: 1, mode: "grapheme" })).toBe("😀***😀"); + expect(redact("😀123😀", { visibleStart: 1, mode: "grapheme" })).toBe("😀****"); + expect(redact("😀123😀", { visibleEnd: 1, mode: "grapheme" })).toBe("****😀"); + expect(redact("😀", { visibleStart: 1, mode: "grapheme" })).toBe("😀"); + expect(redact("👨‍👩‍👧‍👦👨‍👩‍👧‍👦", { visibleStart: 1, visibleEnd: 1, mode: "grapheme" })).toBe("👨‍👩‍👧‍👦👨‍👩‍👧‍👦"); + expect(redact("👨‍👩‍👧‍👦👨‍👩‍👧‍👦", { visibleStart: 3, mode: "grapheme" })).toBe("👨‍👩‍👧‍👦👨‍👩‍👧‍👦"); + }); + }); + + describe("empty and nullish", (): void => { + test("returns an empty string", (): void => { + expect(redact("")).toBe(""); + expect(redact(null)).toBe(""); + expect(redact(undefined)).toBe(""); + expect(redact("", "#")).toBe(""); + expect(redact(null, "#")).toBe(""); + expect(redact(undefined, 2)).toBe(""); + expect(redact(null, { visibleStart: 2 })).toBe(""); + }); + }); +}); From d68959635094dac6c8395c8fc0e673278bc01575 Mon Sep 17 00:00:00 2001 From: rcpthongta <220981657+rcpthongta@users.noreply.github.com> Date: Wed, 23 Sep 2026 14:55:06 +0700 Subject: [PATCH 097/121] perf(redact): add performance benchmarks for string redaction --- src/libs/redact/redact.lib.bench.ts | 145 ++++++++++++++++++++++++++++ 1 file changed, 145 insertions(+) create mode 100644 src/libs/redact/redact.lib.bench.ts diff --git a/src/libs/redact/redact.lib.bench.ts b/src/libs/redact/redact.lib.bench.ts new file mode 100644 index 0000000..1c60832 --- /dev/null +++ b/src/libs/redact/redact.lib.bench.ts @@ -0,0 +1,145 @@ +import { describe, test, TestContext } from "vitest"; + +import { redact } from "./redact.lib"; + +const longText: string = "the quick brown fox jumps over the lazy dog 0812345678 ".repeat(100); +const longUnicode: string = "😀👨‍👩‍👧‍👦cafe\u0301 你好мир ".repeat(100); +const longSeparators: string = "---foo---bar---baz---".repeat(100); + +describe("redact", (): void => { + test("short ASCII", ({ bench }: TestContext): void => { + bench("default options", (): void => { + redact("0812345678"); + }); + + bench("custom replacement string", (): void => { + redact("0812345678", "#"); + }); + + bench("visibleStart number shorthand", (): void => { + redact("0812345678", 4); + }); + + bench("options object visibleStart and visibleEnd", (): void => { + redact("0812345678", { visibleStart: 3, visibleEnd: 2 }); + }); + }); + + test("long input", ({ bench }: TestContext): void => { + bench("long ASCII default", (): void => { + redact(longText); + }); + + bench("long ASCII with visibleStart and visibleEnd", (): void => { + redact(longText, { visibleStart: 10, visibleEnd: 10 }); + }); + + bench("long ASCII with custom replacement", (): void => { + redact(longText, { visibleStart: 10, visibleEnd: 10, replacement: "X" }); + }); + + bench("long Unicode [codePoint]", (): void => { + redact(longUnicode, { visibleStart: 5, visibleEnd: 5, mode: "codePoint" }); + }); + + bench("long Unicode [grapheme]", (): void => { + redact(longUnicode, { visibleStart: 5, visibleEnd: 5, mode: "grapheme" }); + }); + }); + + test("Unicode and combining marks", ({ bench }: TestContext): void => { + bench("accented Latin with combining marks (NFD)", (): void => { + redact("cafe\u0301 noir", { visibleStart: 4 }); + }); + + bench("CJK and Arabic words", (): void => { + redact("你好 世界 مرحبا بالعالم", { visibleStart: 2, visibleEnd: 2 }); + }); + + bench("emoji [codePoint]", (): void => { + redact("😀😀cafe\u0301 noir", { visibleStart: 1, visibleEnd: 1, mode: "codePoint" }); + }); + + bench("emoji [grapheme]", (): void => { + redact("😀👨‍👩‍👧‍👦cafe\u0301 noir", { visibleStart: 1, visibleEnd: 1, mode: "grapheme" }); + }); + }); + + test("excessive separators", ({ bench }: TestContext): void => { + bench("short separators with visibility", (): void => { + redact("---foo---bar---baz---1234567890---", { + visibleStart: 5, + visibleEnd: 5, + replacement: "*" + }); + }); + + bench("long separators with visibility", (): void => { + redact(longSeparators, { visibleStart: 10, visibleEnd: 10 }); + }); + }); + + test("3-mode comparison", ({ bench }: TestContext): void => { + bench("ASCII [utf16]", (): void => { + redact("Hello World", { visibleStart: 2, visibleEnd: 2, mode: "utf16" }); + }); + + bench("ASCII [codePoint]", (): void => { + redact("Hello World", { visibleStart: 2, visibleEnd: 2, mode: "codePoint" }); + }); + + bench("ASCII [grapheme]", (): void => { + redact("Hello World", { visibleStart: 2, visibleEnd: 2, mode: "grapheme" }); + }); + + bench("Emoji [utf16]", (): void => { + redact("😀😀😀😀", { visibleStart: 1, visibleEnd: 1, mode: "utf16" }); + }); + + bench("Emoji [codePoint]", (): void => { + redact("😀😀😀😀", { visibleStart: 1, visibleEnd: 1, mode: "codePoint" }); + }); + + bench("Emoji [grapheme]", (): void => { + redact("😀😀😀😀", { visibleStart: 1, visibleEnd: 1, mode: "grapheme" }); + }); + + bench("NFD [utf16]", (): void => { + redact("cafe\u0301 noir", { visibleStart: 4, visibleEnd: 2, mode: "utf16" }); + }); + + bench("NFD [codePoint]", (): void => { + redact("cafe\u0301 noir", { visibleStart: 4, visibleEnd: 2, mode: "codePoint" }); + }); + + bench("NFD [grapheme]", (): void => { + redact("cafe\u0301 noir", { visibleStart: 4, visibleEnd: 2, mode: "grapheme" }); + }); + + bench("ZWJ emoji [utf16]", (): void => { + redact("👨‍👩‍👧‍👦 👩‍💻", { visibleStart: 1, mode: "utf16" }); + }); + + bench("ZWJ emoji [codePoint]", (): void => { + redact("👨‍👩‍👧‍👦 👩‍💻", { visibleStart: 1, mode: "codePoint" }); + }); + + bench("ZWJ emoji [grapheme]", (): void => { + redact("👨‍👩‍👧‍👦 👩‍💻", { visibleStart: 1, mode: "grapheme" }); + }); + }); + + test("empty and nullish", ({ bench }: TestContext): void => { + bench("empty string", (): void => { + redact(""); + }); + + bench("null", (): void => { + redact(null); + }); + + bench("undefined", (): void => { + redact(undefined); + }); + }); +}); From a8388dccf32f376d0e6ba5e6e7552d97563758c5 Mon Sep 17 00:00:00 2001 From: rcpthongta <220981657+rcpthongta@users.noreply.github.com> Date: Wed, 23 Sep 2026 15:03:41 +0700 Subject: [PATCH 098/121] chore(pkg): rename package to @rcpthongta/string-utils --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index e40e99a..54475a2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,11 +1,11 @@ { - "name": "string-utils", + "name": "@rcpthongta/string-utils", "version": "0.0.1", "lockfileVersion": 3, "requires": true, "packages": { "": { - "name": "string-utils", + "name": "@rcpthongta/string-utils", "version": "0.0.1", "license": "MIT", "devDependencies": { diff --git a/package.json b/package.json index 614b11c..4168e70 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "version": "0.0.1", - "name": "string-utils", + "name": "@rcpthongta/string-utils", "description": "A lightweight, type-safe, dependency-free string utility library for TypeScript and modern JavaScript applications.", "author": "rcpthongta", "license": "MIT", From 8a36a55ae1677f36e87105f573e5b69d2c66922d Mon Sep 17 00:00:00 2001 From: rcpthongta <220981657+rcpthongta@users.noreply.github.com> Date: Wed, 23 Sep 2026 16:23:23 +0700 Subject: [PATCH 099/121] docs(license): add MIT license --- LICENSE.md | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 LICENSE.md diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000..11da323 --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2026 rcpthongta + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. From 8856d8912c7ac5709c0a474b59df3af9ee9b0f61 Mon Sep 17 00:00:00 2001 From: rcpthongta <220981657+rcpthongta@users.noreply.github.com> Date: Wed, 23 Sep 2026 16:24:07 +0700 Subject: [PATCH 100/121] docs(security): add security policy and reporting guidelines --- SECURITY.md | 62 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 SECURITY.md diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 0000000..97fff1f --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,62 @@ +# Security Policy + +## Supported Versions + +Security updates are currently provided for the latest published version of `@rcpthongta/string-utils`. + +| Version | Supported | +| -------------- | ------------------ | +| Latest | :white_check_mark: | +| Older versions | :x: | + +## Reporting a Vulnerability + +If you discover a security vulnerability in `@rcpthongta/string-utils`, please report it privately. + +Please do not report security vulnerabilities through public GitHub issues. + +To report a vulnerability, please use [GitHub Private Vulnerability Reporting](https://github.com/rcpthongta/string-utils/security/advisories/new). + +When reporting a vulnerability, please include: + +- A clear description of the vulnerability +- The affected version +- Steps to reproduce the issue +- A minimal proof of concept, if applicable +- Any potential impact or attack scenario + +You may redact or omit sensitive information that is not necessary to reproduce the issue. + +## Response Process + +After receiving a vulnerability report, we will: + +1. Review and validate the report. +2. Assess the severity and potential impact. +3. Work on a fix or mitigation when necessary. +4. Release a patched version when appropriate. +5. Credit the reporter, with their permission. + +We aim to handle security reports responsibly and keep affected users informed when a security issue is confirmed. + +## Scope + +This security policy applies to: + +- The source code of `@rcpthongta/string-utils` +- Published packages distributed through npm +- Public APIs and functionality provided by the package + +Issues that are not security vulnerabilities, including general bugs, feature requests, documentation issues, and usage questions, should be reported through the project's regular [GitHub issue tracker](https://github.com/rcpthongta/string-utils/issues). + +## Disclosure + +Please allow reasonable time for investigation and remediation before publicly disclosing a confirmed security vulnerability. + +Once a security issue has been addressed, relevant details may be disclosed publicly through the project's changelog or release notes when appropriate. + +## Security Updates + +When a security vulnerability is confirmed and requires a code change, a patched version will be released when appropriate. + +Users are encouraged to keep `@rcpthongta/string-utils` updated to the latest published version to receive available security fixes and improvements. From fc55503ec93ba733d250ba77bbe5c613feb2511a Mon Sep 17 00:00:00 2001 From: rcpthongta <220981657+rcpthongta@users.noreply.github.com> Date: Wed, 23 Sep 2026 16:25:11 +0700 Subject: [PATCH 101/121] docs(contributing): add development and contribution guidelines --- CONTRIBUTING.md | 169 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 169 insertions(+) create mode 100644 CONTRIBUTING.md diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..c83dfe4 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,169 @@ +# Contributing + +Thank you for considering contributing to `@rcpthongta/string-utils`! +This document outlines the development workflow, coding standards, and guidelines for submitting contributions. + +## Prerequisites + +- [Node.js](https://nodejs.org/) >= 22 +- [npm](https://www.npmjs.com/) + +## Getting Started + +1. [Fork](https://docs.github.com/en/get-started/quickstart/fork-a-repo) the repository and clone your fork. +2. Create a new branch from `develop`. +3. Install dependencies: + +```bash +npm install +``` + +## Project Structure + +```text +src/ +├── constants/ # Shared string constants and regex patterns +├── types/ # Shared TypeScript type definitions +├── libs/ +│ └── / +│ ├── .lib.ts # Implementation +│ ├── .lib.spec.ts # Unit tests +│ ├── .lib.bench.ts # Benchmarks +│ └── index.ts # Public export +└── index.ts # Package entry point +``` + +## Development Workflow + +### Branching + +All work should be done in a feature branch created from `develop` in your fork. +Please use the following branch naming conventions: + +- `feat/` +- `fix/` +- `chore/` +- `docs/` +- `perf/` + +> **Note:** Do not push branches directly to the upstream repository. + +### Commit Messages + +This project follows the [Conventional Commits](https://www.conventionalcommits.org/) specification, enforced by commitlint. +All commit messages must use the following format: + +```text +(): +``` + +**Allowed types:** + +`feat` `fix` `docs` `style` `refactor` `perf` `test` `build` `ci` `chore` `revert` `merge` + +**Examples:** + +```text +feat(capitalize): add locale-aware capitalization option +test(slugify): add edge case tests for unicode input +fix(mask): handle empty string input +``` + +### Available Scripts + +| Command | Description | +| ----------------------- | ----------------------------------------------------- | +| `npm run build` | Clean and build ESM, CJS, IIFE, and type declarations | +| `npm run test` | Run all unit tests | +| `npm run test:watch` | Run tests in watch mode | +| `npm run test:coverage` | Run tests with coverage report | +| `npm run bench` | Run benchmarks | +| `npm run lint` | Check for lint errors | +| `npm run lint:fix` | Auto-fix lint errors | + +### Git Hooks + +Git hooks are managed by [Husky](https://typicode.github.io/husky/) and run automatically: + +- **pre-commit** — runs lint-staged to auto-fix staged files via ESLint +- **commit-msg** — validates the commit message format via commitlint +- **pre-push** — runs the full test suite and build to prevent broken pushes + +## Development Standards + +All contributions must follow the project's coding, linting, testing, and benchmarking standards. + +Before submitting a pull request, please review the following guidelines: + +- **Core Principles & Coding Standards** — Performance, memory usage, TypeScript typing, architecture, null safety, function design, and JSDoc requirements. +- **Linting & Code Quality Standards** — Naming conventions, explicit type definitions, unused code, regular expressions, and complexity limits. +- **Testing & Benchmarking Standards** — Vitest test organization, coverage requirements, test case design, and benchmark structure. + +These standards apply to both new utilities and changes to existing utilities. + +## Adding a New Utility + +1. Create a new directory under `src/libs//`. +2. Add the following files: + +```text +.lib.ts # Implementation +.lib.spec.ts # Unit tests +.lib.bench.ts # Benchmarks +index.ts # Re-export the public API +``` + +3. Register the new utility by exporting it from: + +```text +src/libs/index.ts +src/index.ts +``` + +4. Verify that all tests pass and coverage remains at **100%**. + +## Testing + +All contributions must include unit tests. +This project enforces **100%** code coverage thresholds across: + +- Statements +- Branches +- Functions +- Lines + +To verify coverage locally: + +```bash +npm run test:coverage +``` + +## Submitting a Pull Request + +Before opening a pull request, please ensure the following: + +1. Your branch is up to date with the upstream `develop` branch. +2. All linting, tests, and builds pass locally: + +```bash +npm run lint +npm run test +npm run build +``` + +3. Push your branch to your fork: + +```bash +git push origin +``` + +4. Open a pull request from your fork targeting the upstream `develop` branch. +5. Write a clear and concise description of the changes and their motivation. +6. Keep each pull request focused on a single concern. +7. Respond to review feedback promptly. + +All CI checks must pass before a pull request can be merged. + +## License + +By contributing to this project, you agree that your contributions will be licensed under the [MIT License](LICENSE.md). From 98abc391e1aa3b76f441a2e4135a77b34d03cfb4 Mon Sep 17 00:00:00 2001 From: rcpthongta <220981657+rcpthongta@users.noreply.github.com> Date: Wed, 23 Sep 2026 16:26:29 +0700 Subject: [PATCH 102/121] docs(changelog): initialize changelog for v1.0.0 release --- CHANGELOG.md | 49 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 CHANGELOG.md diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..cf034da --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,49 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/) +and this project adheres to [Semantic Versioning](https://semver.org/). + +## [1.0.0] - 2026-09-23 + +### Added + +- **String Inspection** + - `hasText` — check if a string contains non-whitespace characters + - `hasLength` — check if a string contains one or more characters + - `defaultIfBlank` — return a fallback when a string is blank + - `defaultIfEmpty` — return a fallback when a string is empty + +- **Case Conversion** + - `capitalize` — uppercase the first character + - `uncapitalize` — lowercase the first character + - `camelCase` — convert a string to camelCase + - `pascalCase` — convert a string to PascalCase + - `kebabCase` — convert a string to kebab-case + - `snakeCase` — convert a string to snake_case + - `constantCase` — convert a string to CONSTANT_CASE + +- **String Transformation** + - `collapseWhitespace` — normalize consecutive whitespace to single spaces + - `slugify` — convert a string to a URL-friendly slug + - `format` — format strings using templates and placeholders + - `mask` — mask portions of a string using a pattern + - `redact` — redact sensitive string content + +- **Constants & Patterns** + - Reusable string constants such as `EMPTY`, `SPACE`, `HYPHEN`, `UNDERSCORE`, `DOT`, `ASTERISK`, and `HASH` + - Unicode-aware regular expression patterns for words, digits, letters, and diacritics + +- **Package Distribution** + - ESM, CJS, and IIFE builds powered by tsup + - TypeScript type declarations + - Dual `import` / `require` package exports + +- **Development & CI** + - Vitest for unit tests, benchmarks, and coverage + - ESLint and Prettier for code quality and formatting + - Husky, lint-staged, and commitlint for Git hooks and commit validation + - GitHub Actions workflows for linting and test coverage + +[1.0.0]: https://github.com/rcpthongta/string-utils/releases/tag/v1.0.0 From 017c21d8ce73169f67d0e106967ad01b0f71f368 Mon Sep 17 00:00:00 2001 From: rcpthongta <220981657+rcpthongta@users.noreply.github.com> Date: Wed, 23 Sep 2026 16:52:33 +0700 Subject: [PATCH 103/121] chore(github): add bug report issue template --- .github/ISSUE_TEMPLATE/bug_report.yml | 66 +++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/bug_report.yml diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml new file mode 100644 index 0000000..413936d --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -0,0 +1,66 @@ +name: Bug Report +description: Report a bug or unexpected behavior. +labels: + - bug +body: + - type: textarea + id: description + attributes: + label: Description + description: A clear and concise description of the bug. + validations: + required: true + + - type: textarea + id: reproduction + attributes: + label: Steps to Reproduce + description: Provide a minimal code example or steps to reproduce the issue. + placeholder: | + import { capitalize } from "@rcpthongta/string-utils"; + + capitalize("hello"); // expected "Hello", got "" + validations: + required: true + + - type: textarea + id: expected + attributes: + label: Expected Behavior + description: What did you expect to happen? + validations: + required: true + + - type: textarea + id: actual + attributes: + label: Actual Behavior + description: What actually happened? + validations: + required: true + + - type: input + id: version + attributes: + label: Package Version + description: Which version of `@rcpthongta/string-utils` are you using? + placeholder: 1.0.0 + validations: + required: true + + - type: input + id: node-version + attributes: + label: Node.js Version + description: Output of `node --version`. + placeholder: v22.0.0 + validations: + required: true + + - type: textarea + id: additional + attributes: + label: Additional Context + description: Any other relevant information, logs, or screenshots. + validations: + required: false From 8449cb97a8c2a989a5e47e191b03884ec64133ef Mon Sep 17 00:00:00 2001 From: rcpthongta <220981657+rcpthongta@users.noreply.github.com> Date: Wed, 23 Sep 2026 16:53:20 +0700 Subject: [PATCH 104/121] chore(github): add feature request issue template --- .github/ISSUE_TEMPLATE/feature_request.yml | 48 ++++++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/feature_request.yml diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml new file mode 100644 index 0000000..d58faa5 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.yml @@ -0,0 +1,48 @@ +name: Feature Request +description: Suggest a new utility or enhancement. +labels: + - enhancement +body: + - type: textarea + id: description + attributes: + label: Description + description: A clear and concise description of the feature you would like. + validations: + required: true + + - type: textarea + id: use-case + attributes: + label: Use Case + description: Describe the problem or use case this feature would address. + validations: + required: true + + - type: textarea + id: proposed-api + attributes: + label: Proposed API + description: If applicable, show how you envision the API would look. + placeholder: | + import { truncate } from "@rcpthongta/string-utils"; + + truncate("Hello, World!", 5); // "Hello..." + validations: + required: false + + - type: textarea + id: alternatives + attributes: + label: Alternatives Considered + description: Any alternative solutions or workarounds you have considered. + validations: + required: false + + - type: textarea + id: additional + attributes: + label: Additional Context + description: Any other relevant information or references. + validations: + required: false From b8a8e23af0efd220b2587444aa54d0c089328a0e Mon Sep 17 00:00:00 2001 From: rcpthongta <220981657+rcpthongta@users.noreply.github.com> Date: Wed, 23 Sep 2026 18:05:35 +0700 Subject: [PATCH 105/121] docs(readme): add comprehensive documentation, quick start, and guides --- README.md | 135 +++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 134 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 2f9095c..1c1fe5e 100644 --- a/README.md +++ b/README.md @@ -1 +1,134 @@ -# String Utilities +# @rcpthongta/string-utils + +A lightweight, type-safe, dependency-free string utility library for TypeScript and modern JavaScript applications. + +--- + +## Features + +- 🪶 **Zero Dependencies**: Pure vanilla TypeScript with zero third-party runtime dependencies. +- 🌲 **Tree-Shakeable**: Side-effect free, allowing bundlers to include only the functions you actually use. +- 🛡️ **Type-Safe**: Written in strict TypeScript with comprehensive type definitions. +- 📦 **Universal Modules**: Supports ESM, CommonJS, and browser global (IIFE) via CDN. +- ⚡ **High Performance**: Optimized implementations benchmarked and thoroughly tested. + +--- + +## Documentation + +> 📖 **Interactive Documentation & API Playground** +> +> Full API references, interactive examples, and detailed guides are available on our official website: +> +> 👉 **[Visit Documentation Website](https://github.com/rcpthongta/string-utils#readme)** *(Link to docs site)* + +--- + +## Installation + +Install using your favorite package manager: + +```bash +# npm +npm install @rcpthongta/string-utils + +# pnpm +pnpm add @rcpthongta/string-utils + +# yarn +yarn add @rcpthongta/string-utils + +# bun +bun add @rcpthongta/string-utils +``` + +### CDN / Direct Browser Usage + +For quick prototyping or environments without a build step: + +```html + + + + + + + +``` + +--- + +## Quick Start + +Import only the utilities you need: + +```typescript +import { + camelCase, + capitalize, + defaultIfBlank, + mask, + redact, + slugify +} from "@rcpthongta/string-utils"; + +// Case transformation +capitalize("hello world"); // "Hello world" +camelCase("user_profile_id"); // "userProfileId" + +// Sanitization & URL +slugify("Hello World & Special Characters!"); // "hello-world-and-special-characters" + +// Validation & Fallback +defaultIfBlank(" ", "default value"); // "default value" + +// Formatting with mask pattern +mask("0812345678", "(###) ###-####"); // "(081) 234-5678" +mask("1234567890123456", "#### #### #### ####"); // "1234 5678 9012 3456" + +// Privacy & Censoring +redact("0812345678", { visibleStart: 3, visibleEnd: 2 }); // "081*****78" +``` + +CommonJS is also fully supported: + +```javascript +const { capitalize, slugify } = require("@rcpthongta/string-utils"); +``` + +--- + +## Overview of Available Utilities + +Below is a summary of the utility functions available in this package: + +| Category | Functions | +|-----------------------------|---------------------------------------------------------------------| +| **Case Conversion** | `camelCase`, `constantCase`, `kebabCase`, `pascalCase`, `snakeCase` | +| **Casing & Capitalization** | `capitalize`, `uncapitalize` | +| **Formatting & Templating** | `collapseWhitespace`, `format`, `createFormatter`, `slugify` | +| **Pattern Masking** | `mask`, `createMask` | +| **Validation & Fallback** | `hasLength`, `hasText`, `defaultIfBlank`, `defaultIfEmpty` | +| **Privacy & Redaction** | `redact` | + +*(Refer to the [Documentation Website](https://github.com/rcpthongta/string-utils#readme) for full parameter signatures, options, and live demos).* + +--- + +## Contributing + +Contributions are always welcome! Please check out our [Contributing Guide](CONTRIBUTING.md) before submitting a Pull Request. + +--- + +## Security + +If you discover any security-related issues, please review our [Security Policy](SECURITY.md). + +--- + +## License + +This project is licensed under the [MIT License](LICENSE.md) © [rcpthongta](https://github.com/rcpthongta). From 31c79bcb7da08e92ccd08f87df1c2ceb6dc918ba Mon Sep 17 00:00:00 2001 From: rcpthongta <220981657+rcpthongta@users.noreply.github.com> Date: Wed, 23 Sep 2026 21:00:49 +0700 Subject: [PATCH 106/121] chore(release): bump package version to v1.0.0 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 54475a2..a387e10 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@rcpthongta/string-utils", - "version": "0.0.1", + "version": "1.0.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@rcpthongta/string-utils", - "version": "0.0.1", + "version": "1.0.0", "license": "MIT", "devDependencies": { "@commitlint/cli": "^21.2.2", diff --git a/package.json b/package.json index 4168e70..fb3005d 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,5 @@ { - "version": "0.0.1", + "version": "1.0.0", "name": "@rcpthongta/string-utils", "description": "A lightweight, type-safe, dependency-free string utility library for TypeScript and modern JavaScript applications.", "author": "rcpthongta", From 4a035e62564919755d47a50d674f538bdb521464 Mon Sep 17 00:00:00 2001 From: rcpthongta <220981657+rcpthongta@users.noreply.github.com> Date: Wed, 23 Sep 2026 21:24:05 +0700 Subject: [PATCH 107/121] ci(release): add npm publish workflow with provenance --- .github/workflows/release.yml | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 .github/workflows/release.yml diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..512d77d --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,29 @@ +name: Release +on: + push: + tags: + - "v*" +permissions: + contents: read + id-token: write +jobs: + publish: + runs-on: ubuntu-latest + timeout-minutes: 10 + steps: + - name: Checkout repository + uses: actions/checkout@v4 + - name: Set up Node.js + uses: actions/setup-node@v4 + with: + node-version: 22 + cache: npm + registry-url: https://registry.npmjs.org + - name: Install dependencies + run: npm ci + - name: Build package + run: npm run build + - name: Publish to npm + run: npm publish --provenance --access public + env: + NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} From 4caf51d21d1a25acb768a8125878a3ee06367f39 Mon Sep 17 00:00:00 2001 From: rcpthongta <220981657+rcpthongta@users.noreply.github.com> Date: Wed, 23 Sep 2026 21:27:19 +0700 Subject: [PATCH 108/121] chore(pkg): include CHANGELOG.md in published package files --- package.json | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 4168e70..3f0351b 100644 --- a/package.json +++ b/package.json @@ -31,7 +31,10 @@ "require": "./dist/cjs/index.cjs" } }, - "files": ["dist"], + "files": [ + "dist", + "CHANGELOG.md" + ], "engines": { "node": ">=22" }, From 87e065a1d71f41198718dc961439a870aad6f75b Mon Sep 17 00:00:00 2001 From: rcpthongta <220981657+rcpthongta@users.noreply.github.com> Date: Wed, 23 Sep 2026 21:29:33 +0700 Subject: [PATCH 109/121] chore(build): exclude benchmark files from build tsconfig --- tsconfig.build.json | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tsconfig.build.json b/tsconfig.build.json index ef7c122..b960f81 100644 --- a/tsconfig.build.json +++ b/tsconfig.build.json @@ -1,5 +1,8 @@ { "extends": "./tsconfig.json", "include": ["src/**/*"], - "exclude": ["**/*.spec.ts"] + "exclude": [ + "**/*.bench.ts", + "**/*.spec.ts" + ] } From 5b334b30bdccbca4da6123060176535128401ec4 Mon Sep 17 00:00:00 2001 From: rcpthongta <220981657+rcpthongta@users.noreply.github.com> Date: Wed, 23 Sep 2026 23:29:58 +0700 Subject: [PATCH 110/121] ci(release): switch to tokenless trusted publishing via oidc --- .github/workflows/release.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 512d77d..96832d1 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -19,11 +19,11 @@ jobs: node-version: 22 cache: npm registry-url: https://registry.npmjs.org + - name: Update npm + run: npm install -g npm@latest - name: Install dependencies run: npm ci - name: Build package run: npm run build - name: Publish to npm run: npm publish --provenance --access public - env: - NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} From 24b5470822471d98436175b01853c4849a6d1868 Mon Sep 17 00:00:00 2001 From: rcpthongta <220981657+rcpthongta@users.noreply.github.com> Date: Thu, 24 Sep 2026 02:09:23 +0700 Subject: [PATCH 111/121] docs(vitepress): add vitepress dependency, scripts, and homepage --- package-lock.json | 1953 ++++++++++++++++++++++++++++++++++++++++++--- package.json | 5 + 2 files changed, 1846 insertions(+), 112 deletions(-) diff --git a/package-lock.json b/package-lock.json index a387e10..862ffae 100644 --- a/package-lock.json +++ b/package-lock.json @@ -31,6 +31,7 @@ "tsup": "^8.5.1", "typescript": "^5.9.3", "typescript-eslint": "^8.65.0", + "vitepress": "^1.6.4", "vitest": "^5.0.1", "yaml-eslint-parser": "^1.3.2" }, @@ -38,6 +39,264 @@ "node": ">=22" } }, + "node_modules/@algolia/abtesting": { + "version": "1.25.0", + "resolved": "https://registry.npmjs.org/@algolia/abtesting/-/abtesting-1.25.0.tgz", + "integrity": "sha512-rSTin9Uta23uaewYVQEp8XI9T3iA/zrg0/1G2vhf8oFFDxFL5vybnZ5IQwsVAg4JpKxPX4/WYNKdcfWrZymk7w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.59.0", + "@algolia/requester-browser-xhr": "5.59.0", + "@algolia/requester-fetch": "5.59.0", + "@algolia/requester-node-http": "5.59.0" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/autocomplete-core": { + "version": "1.17.7", + "resolved": "https://registry.npmjs.org/@algolia/autocomplete-core/-/autocomplete-core-1.17.7.tgz", + "integrity": "sha512-BjiPOW6ks90UKl7TwMv7oNQMnzU+t/wk9mgIDi6b1tXpUek7MW0lbNOUHpvam9pe3lVCf4xPFT+lK7s+e+fs7Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/autocomplete-plugin-algolia-insights": "1.17.7", + "@algolia/autocomplete-shared": "1.17.7" + } + }, + "node_modules/@algolia/autocomplete-plugin-algolia-insights": { + "version": "1.17.7", + "resolved": "https://registry.npmjs.org/@algolia/autocomplete-plugin-algolia-insights/-/autocomplete-plugin-algolia-insights-1.17.7.tgz", + "integrity": "sha512-Jca5Ude6yUOuyzjnz57og7Et3aXjbwCSDf/8onLHSQgw1qW3ALl9mrMWaXb5FmPVkV3EtkD2F/+NkT6VHyPu9A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/autocomplete-shared": "1.17.7" + }, + "peerDependencies": { + "search-insights": ">= 1 < 3" + } + }, + "node_modules/@algolia/autocomplete-preset-algolia": { + "version": "1.17.7", + "resolved": "https://registry.npmjs.org/@algolia/autocomplete-preset-algolia/-/autocomplete-preset-algolia-1.17.7.tgz", + "integrity": "sha512-ggOQ950+nwbWROq2MOCIL71RE0DdQZsceqrg32UqnhDz8FlO9rL8ONHNsI2R1MH0tkgVIDKI/D0sMiUchsFdWA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/autocomplete-shared": "1.17.7" + }, + "peerDependencies": { + "@algolia/client-search": ">= 4.9.1 < 6", + "algoliasearch": ">= 4.9.1 < 6" + } + }, + "node_modules/@algolia/autocomplete-shared": { + "version": "1.17.7", + "resolved": "https://registry.npmjs.org/@algolia/autocomplete-shared/-/autocomplete-shared-1.17.7.tgz", + "integrity": "sha512-o/1Vurr42U/qskRSuhBH+VKxMvkkUVTLU6WZQr+L5lGZZLYWyhdzWjW0iGXY7EkwRTjBqvN2EsR81yCTGV/kmg==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@algolia/client-search": ">= 4.9.1 < 6", + "algoliasearch": ">= 4.9.1 < 6" + } + }, + "node_modules/@algolia/client-abtesting": { + "version": "5.59.0", + "resolved": "https://registry.npmjs.org/@algolia/client-abtesting/-/client-abtesting-5.59.0.tgz", + "integrity": "sha512-bm2XN0hCSMYwStSsCBT0/PUB2BDxoyR1Lnub3c392HMEy9bi8PUSW8vR6zltVEKWG2t4PQFWMD5C07bmJxGgPg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.59.0", + "@algolia/requester-browser-xhr": "5.59.0", + "@algolia/requester-fetch": "5.59.0", + "@algolia/requester-node-http": "5.59.0" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/client-analytics": { + "version": "5.59.0", + "resolved": "https://registry.npmjs.org/@algolia/client-analytics/-/client-analytics-5.59.0.tgz", + "integrity": "sha512-XOFPOTa69WuqHR6c5tMgnUUwwqQgNSzMpxmhrgA9KmxRf8WIqEa0cokHJvohk5CYb7CZx0xeSL6Bk2IUJ7Lv7Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.59.0", + "@algolia/requester-browser-xhr": "5.59.0", + "@algolia/requester-fetch": "5.59.0", + "@algolia/requester-node-http": "5.59.0" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/client-common": { + "version": "5.59.0", + "resolved": "https://registry.npmjs.org/@algolia/client-common/-/client-common-5.59.0.tgz", + "integrity": "sha512-PC8ipLOYFKRTfIUY1J3FJxS6ryzWziaXmIX9/sNMoUR8L+XhF7hX2QAeUI87YVl5zujvlYTSOb+HAIqKXDjyHQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/client-insights": { + "version": "5.59.0", + "resolved": "https://registry.npmjs.org/@algolia/client-insights/-/client-insights-5.59.0.tgz", + "integrity": "sha512-yFNcCMM5fHiyoR0HuxMrzy+VjDcmhFUZTm2IJ2DHwGsVH3B5SEob4zTmeEZ3j/AZqNnmrJOCKl/tJnBg7+84bA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.59.0", + "@algolia/requester-browser-xhr": "5.59.0", + "@algolia/requester-fetch": "5.59.0", + "@algolia/requester-node-http": "5.59.0" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/client-personalization": { + "version": "5.59.0", + "resolved": "https://registry.npmjs.org/@algolia/client-personalization/-/client-personalization-5.59.0.tgz", + "integrity": "sha512-GYja6HkDt2VrQhWmB2cLx3Z5fDwI9no7q+xwCWcFrTPm5CLH9QZvK0XLO9co1FcNIDxxkMaP3AXM39/XcecEOg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.59.0", + "@algolia/requester-browser-xhr": "5.59.0", + "@algolia/requester-fetch": "5.59.0", + "@algolia/requester-node-http": "5.59.0" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/client-query-suggestions": { + "version": "5.59.0", + "resolved": "https://registry.npmjs.org/@algolia/client-query-suggestions/-/client-query-suggestions-5.59.0.tgz", + "integrity": "sha512-Wofg7bMpWh8N5qDDZs0wy6whc+KMmdNsxgrIGp9Ug2s1Bka0uq7AjyckdxReKPHLA0Q1qH8X/SNh0wn2t2X2Lw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.59.0", + "@algolia/requester-browser-xhr": "5.59.0", + "@algolia/requester-fetch": "5.59.0", + "@algolia/requester-node-http": "5.59.0" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/client-search": { + "version": "5.59.0", + "resolved": "https://registry.npmjs.org/@algolia/client-search/-/client-search-5.59.0.tgz", + "integrity": "sha512-fHnALZfbEnODczGk14Y/1YBRApp6UEpZUTexGcMUPzY7RDc7q4HN2Y6jh0KUG8Jo9B+q+wOoQEc3ziR0ErbuFg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.59.0", + "@algolia/requester-browser-xhr": "5.59.0", + "@algolia/requester-fetch": "5.59.0", + "@algolia/requester-node-http": "5.59.0" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/ingestion": { + "version": "1.59.0", + "resolved": "https://registry.npmjs.org/@algolia/ingestion/-/ingestion-1.59.0.tgz", + "integrity": "sha512-Fa38s1mHgoaLCT117sfJ6P78rtxUt93CYxBYpq1VOIcslaF+cH+1h5uikAPsKfxLnsuEDZHiBiaI6eNPTsQMRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.59.0", + "@algolia/requester-browser-xhr": "5.59.0", + "@algolia/requester-fetch": "5.59.0", + "@algolia/requester-node-http": "5.59.0" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/monitoring": { + "version": "1.59.0", + "resolved": "https://registry.npmjs.org/@algolia/monitoring/-/monitoring-1.59.0.tgz", + "integrity": "sha512-NyNsRSqM2tF1MX7ZGw/j4rduoEJiQ5wfvbSo/CFVdEzYCjyxbGFRPOZyS/GETJG9rk1TdHOq8NZqKqk/u1fm4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.59.0", + "@algolia/requester-browser-xhr": "5.59.0", + "@algolia/requester-fetch": "5.59.0", + "@algolia/requester-node-http": "5.59.0" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/recommend": { + "version": "5.59.0", + "resolved": "https://registry.npmjs.org/@algolia/recommend/-/recommend-5.59.0.tgz", + "integrity": "sha512-nXBK2uygWtvbCOffMWqqfjjcEtp9enDKY5/2Pw/Hhw8VVaOyK2ThbGK04bNX/Le+qoK1VHYkodWGVTKfw0Otkw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.59.0", + "@algolia/requester-browser-xhr": "5.59.0", + "@algolia/requester-fetch": "5.59.0", + "@algolia/requester-node-http": "5.59.0" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/requester-browser-xhr": { + "version": "5.59.0", + "resolved": "https://registry.npmjs.org/@algolia/requester-browser-xhr/-/requester-browser-xhr-5.59.0.tgz", + "integrity": "sha512-yb+4afX/zja8QwX0KmV4/ae2kyYkRknsE79CRusYS2U5D8qwn2wq0cn1x12f3tm3F3+5FgmDdbuGdQTuCLSKMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.59.0" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/requester-fetch": { + "version": "5.59.0", + "resolved": "https://registry.npmjs.org/@algolia/requester-fetch/-/requester-fetch-5.59.0.tgz", + "integrity": "sha512-Lp52TmpA1QtNmdHzs505Xwf4tgII7RAapkDSF9AAtWIBETcGaTIaXci4Rd9nS6SdaWK1BWTSqAPp5wzh6UN3Ag==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.59.0" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/requester-node-http": { + "version": "5.59.0", + "resolved": "https://registry.npmjs.org/@algolia/requester-node-http/-/requester-node-http-5.59.0.tgz", + "integrity": "sha512-YYHLEs5rC6oRTFwT7bJaKBR9NSFzikeVHGAT1ffATmUQzR8XqyhHXjoieAUoR7fBU6I5cfy1FVeh5GoRgriuzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.59.0" + }, + "engines": { + "node": ">= 14.0.0" + } + }, "node_modules/@babel/code-frame": { "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.7.tgz", @@ -462,6 +721,57 @@ "node": ">=22" } }, + "node_modules/@docsearch/css": { + "version": "3.8.2", + "resolved": "https://registry.npmjs.org/@docsearch/css/-/css-3.8.2.tgz", + "integrity": "sha512-y05ayQFyUmCXze79+56v/4HpycYF3uFqB78pLPrSV5ZKAlDuIAAJNhaRi8tTdRNXh05yxX/TyNnzD6LwSM89vQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@docsearch/js": { + "version": "3.8.2", + "resolved": "https://registry.npmjs.org/@docsearch/js/-/js-3.8.2.tgz", + "integrity": "sha512-Q5wY66qHn0SwA7Taa0aDbHiJvaFJLOJyHmooQ7y8hlwwQLQ/5WwCcoX0g7ii04Qi2DJlHsd0XXzJ8Ypw9+9YmQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@docsearch/react": "3.8.2", + "preact": "^10.0.0" + } + }, + "node_modules/@docsearch/react": { + "version": "3.8.2", + "resolved": "https://registry.npmjs.org/@docsearch/react/-/react-3.8.2.tgz", + "integrity": "sha512-xCRrJQlTt8N9GU0DG4ptwHRkfnSnD/YpdeaXe02iKfqs97TkZJv60yE+1eq/tjPcVnTW8dP5qLP7itifFVV5eg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/autocomplete-core": "1.17.7", + "@algolia/autocomplete-preset-algolia": "1.17.7", + "@docsearch/css": "3.8.2", + "algoliasearch": "^5.14.2" + }, + "peerDependencies": { + "@types/react": ">= 16.8.0 < 19.0.0", + "react": ">= 16.8.0 < 19.0.0", + "react-dom": ">= 16.8.0 < 19.0.0", + "search-insights": ">= 1 < 3" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "react": { + "optional": true + }, + "react-dom": { + "optional": true + }, + "search-insights": { + "optional": true + } + } + }, "node_modules/@esbuild/aix-ppc64": { "version": "0.27.7", "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.7.tgz", @@ -1151,6 +1461,23 @@ "url": "https://github.com/sponsors/nzakas" } }, + "node_modules/@iconify-json/simple-icons": { + "version": "1.2.97", + "resolved": "https://registry.npmjs.org/@iconify-json/simple-icons/-/simple-icons-1.2.97.tgz", + "integrity": "sha512-Z6PmM6jvWVU7rmLzqbxCLJA7SeqCnK6lMHXtjyzHPLZebwuoojBEcHHycppahDzEeN9xujRkrV5J8QpOhMyNrg==", + "dev": true, + "license": "CC0-1.0", + "dependencies": { + "@iconify/types": "*" + } + }, + "node_modules/@iconify/types": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@iconify/types/-/types-2.0.0.tgz", + "integrity": "sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==", + "dev": true, + "license": "MIT" + }, "node_modules/@jridgewell/gen-mapping": { "version": "0.3.13", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", @@ -1919,6 +2246,93 @@ "win32" ] }, + "node_modules/@shikijs/core": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@shikijs/core/-/core-2.5.0.tgz", + "integrity": "sha512-uu/8RExTKtavlpH7XqnVYBrfBkUc20ngXiX9NSrBhOVZYv/7XQRKUyhtkeflY5QsxC0GbJThCerruZfsUaSldg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@shikijs/engine-javascript": "2.5.0", + "@shikijs/engine-oniguruma": "2.5.0", + "@shikijs/types": "2.5.0", + "@shikijs/vscode-textmate": "^10.0.2", + "@types/hast": "^3.0.4", + "hast-util-to-html": "^9.0.4" + } + }, + "node_modules/@shikijs/engine-javascript": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@shikijs/engine-javascript/-/engine-javascript-2.5.0.tgz", + "integrity": "sha512-VjnOpnQf8WuCEZtNUdjjwGUbtAVKuZkVQ/5cHy/tojVVRIRtlWMYVjyWhxOmIq05AlSOv72z7hRNRGVBgQOl0w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@shikijs/types": "2.5.0", + "@shikijs/vscode-textmate": "^10.0.2", + "oniguruma-to-es": "^3.1.0" + } + }, + "node_modules/@shikijs/engine-oniguruma": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@shikijs/engine-oniguruma/-/engine-oniguruma-2.5.0.tgz", + "integrity": "sha512-pGd1wRATzbo/uatrCIILlAdFVKdxImWJGQ5rFiB5VZi2ve5xj3Ax9jny8QvkaV93btQEwR/rSz5ERFpC5mKNIw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@shikijs/types": "2.5.0", + "@shikijs/vscode-textmate": "^10.0.2" + } + }, + "node_modules/@shikijs/langs": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@shikijs/langs/-/langs-2.5.0.tgz", + "integrity": "sha512-Qfrrt5OsNH5R+5tJ/3uYBBZv3SuGmnRPejV9IlIbFH3HTGLDlkqgHymAlzklVmKBjAaVmkPkyikAV/sQ1wSL+w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@shikijs/types": "2.5.0" + } + }, + "node_modules/@shikijs/themes": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@shikijs/themes/-/themes-2.5.0.tgz", + "integrity": "sha512-wGrk+R8tJnO0VMzmUExHR+QdSaPUl/NKs+a4cQQRWyoc3YFbUzuLEi/KWK1hj+8BfHRKm2jNhhJck1dfstJpiw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@shikijs/types": "2.5.0" + } + }, + "node_modules/@shikijs/transformers": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@shikijs/transformers/-/transformers-2.5.0.tgz", + "integrity": "sha512-SI494W5X60CaUwgi8u4q4m4s3YAFSxln3tzNjOSYqq54wlVgz0/NbbXEb3mdLbqMBztcmS7bVTaEd2w0qMmfeg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@shikijs/core": "2.5.0", + "@shikijs/types": "2.5.0" + } + }, + "node_modules/@shikijs/types": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@shikijs/types/-/types-2.5.0.tgz", + "integrity": "sha512-ygl5yhxki9ZLNuNpPitBWvcy9fsSKKaRuO4BAlMyagszQidxcpLAr0qiW/q43DtSIDxO6hEbtYLiFZNXO/hdGw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@shikijs/vscode-textmate": "^10.0.2", + "@types/hast": "^3.0.4" + } + }, + "node_modules/@shikijs/vscode-textmate": { + "version": "10.0.2", + "resolved": "https://registry.npmjs.org/@shikijs/vscode-textmate/-/vscode-textmate-10.0.2.tgz", + "integrity": "sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg==", + "dev": true, + "license": "MIT" + }, "node_modules/@simple-libs/child-process-utils": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/@simple-libs/child-process-utils/-/child-process-utils-2.0.0.tgz", @@ -1990,6 +2404,16 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/hast": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.5.tgz", + "integrity": "sha512-rp/ezSWaD1m44dPKICGhiskI13nVr7qTloFwDa/IYkhhf5nzwP+zIQcIJh3WIFSBOy/H1PzB40jPjMDksN4F+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/unist": "*" + } + }, "node_modules/@types/json-schema": { "version": "7.0.15", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", @@ -1997,6 +2421,24 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/linkify-it": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@types/linkify-it/-/linkify-it-5.0.0.tgz", + "integrity": "sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/markdown-it": { + "version": "14.2.0", + "resolved": "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-14.2.0.tgz", + "integrity": "sha512-NoQ2yGlLWj4wpxMs+TYmRKk3thDrQ97agr7sFqfLsAlvoS8SNQuTrlObhFqG9iugdTtgOE9jpJ6FNM4ZGsa5xQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/linkify-it": "^5", + "@types/mdurl": "^2" + } + }, "node_modules/@types/mdast": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.4.tgz", @@ -2007,6 +2449,13 @@ "@types/unist": "*" } }, + "node_modules/@types/mdurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@types/mdurl/-/mdurl-2.0.0.tgz", + "integrity": "sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/ms": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz", @@ -2031,6 +2480,13 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/web-bluetooth": { + "version": "0.0.21", + "resolved": "https://registry.npmjs.org/@types/web-bluetooth/-/web-bluetooth-0.0.21.tgz", + "integrity": "sha512-oIQLCGWtcFZy2JW77j9k8nHzAOpqMHLQejDA48XXMWH6tjCQHz5RCFz1bzsmROyL6PUm+LLnUiI4BCn221inxA==", + "dev": true, + "license": "MIT" + }, "node_modules/@typescript-eslint/eslint-plugin": { "version": "8.70.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.70.0.tgz", @@ -2313,6 +2769,13 @@ "url": "https://opencollective.com/eslint" } }, + "node_modules/@ungap/structured-clone": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.4.0.tgz", + "integrity": "sha512-1mEZtMKPM09vDmQt5y7YvmN2+DFTP7Tg0EWXdic8/C6VRnpb33e4ghisCIE3WZjsE2N8mf+QV1Zqh7ZFYLWInQ==", + "dev": true, + "license": "ISC" + }, "node_modules/@vitest/coverage-v8": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-5.0.1.tgz", @@ -2413,46 +2876,335 @@ "url": "https://opencollective.com/vitest" } }, - "node_modules/acorn": { - "version": "8.18.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.18.0.tgz", - "integrity": "sha512-lGq+9yr1/GuAWaVYIHRjvvySG5/4VfKIvC8EWxStPdcDh/Ka7FG3twP6v4d5BkravUilhIAsG4Qj83t02LWUPQ==", + "node_modules/@vue/compiler-core": { + "version": "3.5.43", + "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.43.tgz", + "integrity": "sha512-zdiLhnbe1QQqgDT8xZMpNmyqZ3qlI+/Q/FHQco57Kwl/b05HhCzN6eVGN9QU9rbga4CrS0H5SYY8VGHZCt/1Hg==", "dev": true, "license": "MIT", - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" + "dependencies": { + "@babel/parser": "^7.29.8", + "@vue/shared": "3.5.43", + "entities": "^7.0.1", + "estree-walker": "^2.0.2", + "source-map-js": "^1.2.1" } }, - "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==", + "node_modules/@vue/compiler-core/node_modules/estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@vue/compiler-dom": { + "version": "3.5.43", + "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.43.tgz", + "integrity": "sha512-PEZoAk3NQmsn/ejMzSOCyTYqwGqczrWm70PuhBKjjv1+TCoQAaO/zOqNwjV+honlNstT5ILxtc+8r8UUfj+iEQ==", "dev": true, "license": "MIT", - "peerDependencies": { - "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + "dependencies": { + "@vue/compiler-core": "3.5.43", + "@vue/shared": "3.5.43" } }, - "node_modules/ajv": { - "version": "6.15.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.15.0.tgz", - "integrity": "sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==", + "node_modules/@vue/compiler-sfc": { + "version": "3.5.43", + "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.43.tgz", + "integrity": "sha512-FCbrG3XNCRl+js3huuKx4IVHBLTvMkJhVepjbxSPu1gn4yWLaYtGNQdjJGZaMytXB6qb76qQDDmDSLy/vkmleQ==", "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": { + "@babel/parser": "^7.29.8", + "@vue/compiler-core": "3.5.43", + "@vue/compiler-dom": "3.5.43", + "@vue/compiler-ssr": "3.5.43", + "@vue/shared": "3.5.43", + "estree-walker": "^2.0.2", + "magic-string": "^0.30.21", + "postcss": "^8.5.28", + "source-map-js": "^1.2.1" + } + }, + "node_modules/@vue/compiler-sfc/node_modules/estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@vue/compiler-ssr": { + "version": "3.5.43", + "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.43.tgz", + "integrity": "sha512-GF62orf7KiJX9RqrHNGrYBudsQGD0OhJ5nDs90O8UiDuS40+YMYomiXu6w6EuvtXuRDc3MSNis3EaaSKAVSWpg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vue/compiler-dom": "3.5.43", + "@vue/shared": "3.5.43" + } + }, + "node_modules/@vue/devtools-api": { + "version": "7.7.10", + "resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-7.7.10.tgz", + "integrity": "sha512-KxtEpUOOpFz/qOGRrAwA36QF7DqIA+FXgCYit9mk9wjbaZt0sXOFz81ElOZtKA4HbWHUdwNjZHBFsFFyp5BZiA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vue/devtools-kit": "^7.7.10" + } + }, + "node_modules/@vue/devtools-kit": { + "version": "7.7.10", + "resolved": "https://registry.npmjs.org/@vue/devtools-kit/-/devtools-kit-7.7.10.tgz", + "integrity": "sha512-3WNi2Kq4tbpVbmhml7RiphmAt0279oh3fKNeWMQIrltfX8Q91b4i5PL8DtyNKdwmcsGrV4fg+erwWOmD05CLIw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vue/devtools-shared": "^7.7.10", + "birpc": "^2.3.0", + "hookable": "^5.5.3", + "mitt": "^3.0.1", + "perfect-debounce": "^1.0.0", + "speakingurl": "^14.0.1", + "superjson": "^2.2.2" + } + }, + "node_modules/@vue/devtools-shared": { + "version": "7.7.10", + "resolved": "https://registry.npmjs.org/@vue/devtools-shared/-/devtools-shared-7.7.10.tgz", + "integrity": "sha512-wOPslzB8vTvpxwdaOcR2qAbwmuSP0L+rhpoC6Cf56V3Jip+HWb7PQQXOUPgBNQARpXsbQX/+mvi8kKucmBGRwQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "rfdc": "^1.4.1" + } + }, + "node_modules/@vue/reactivity": { + "version": "3.5.43", + "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.43.tgz", + "integrity": "sha512-G/c9GyOZNI2jVaaS6OX1EF1SSFSv7H0ERqNTl4+DTFMlZmB5eVAB53aLQNam/7NL2NPtaDD7RdVrzf8uJzMuOA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vue/shared": "3.5.43" + } + }, + "node_modules/@vue/runtime-core": { + "version": "3.5.43", + "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.5.43.tgz", + "integrity": "sha512-hU6U6VnVhBGQDpvlnnDlIB8ZGJBiOcgk2lh/0InltHiz3D8oSkluvuvY+do1G2H3+udeKFsmaBlgVYP7gXQzEw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vue/reactivity": "3.5.43", + "@vue/shared": "3.5.43" + } + }, + "node_modules/@vue/runtime-dom": { + "version": "3.5.43", + "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.5.43.tgz", + "integrity": "sha512-Bb2Jc0YjjJdMt1SJmb9b2L/IWd3I8lIT9x9eS/xvvP9CiVgna0ffua74xKRmt4/uSJ+0r4iN8ex1jrqkhQGWQw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vue/reactivity": "3.5.43", + "@vue/runtime-core": "3.5.43", + "@vue/shared": "3.5.43", + "csstype": "^3.2.3" + } + }, + "node_modules/@vue/server-renderer": { + "version": "3.5.43", + "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.5.43.tgz", + "integrity": "sha512-l2Ygjv9NehV94PSBxNWsAHC0j/eIIKbn92mBuWXAPGLnn6HfJ6MH5ubsd+Nk0YoZ5FRuxWI1P2VSoh+dbPQhCQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vue/compiler-ssr": "3.5.43", + "@vue/runtime-dom": "3.5.43", + "@vue/shared": "3.5.43" + } + }, + "node_modules/@vue/shared": { + "version": "3.5.43", + "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.43.tgz", + "integrity": "sha512-uksS7YGMR5NZyr4JNq0Rp+QyLns0ueaz20KwzIPW9R0LH1Vnt4E+XUM29PNseEbf1www2gOuhuDi5AKOIXag9Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/@vueuse/core": { + "version": "12.8.2", + "resolved": "https://registry.npmjs.org/@vueuse/core/-/core-12.8.2.tgz", + "integrity": "sha512-HbvCmZdzAu3VGi/pWYm5Ut+Kd9mn1ZHnn4L5G8kOQTPs/IwIAmJoBrmYk2ckLArgMXZj0AW3n5CAejLUO+PhdQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/web-bluetooth": "^0.0.21", + "@vueuse/metadata": "12.8.2", + "@vueuse/shared": "12.8.2", + "vue": "^3.5.13" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/@vueuse/integrations": { + "version": "12.8.2", + "resolved": "https://registry.npmjs.org/@vueuse/integrations/-/integrations-12.8.2.tgz", + "integrity": "sha512-fbGYivgK5uBTRt7p5F3zy6VrETlV9RtZjBqd1/HxGdjdckBgBM4ugP8LHpjolqTj14TXTxSK1ZfgPbHYyGuH7g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vueuse/core": "12.8.2", + "@vueuse/shared": "12.8.2", + "vue": "^3.5.13" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "async-validator": "^4", + "axios": "^1", + "change-case": "^5", + "drauu": "^0.4", + "focus-trap": "^7", + "fuse.js": "^7", + "idb-keyval": "^6", + "jwt-decode": "^4", + "nprogress": "^0.2", + "qrcode": "^1.5", + "sortablejs": "^1", + "universal-cookie": "^7" + }, + "peerDependenciesMeta": { + "async-validator": { + "optional": true + }, + "axios": { + "optional": true + }, + "change-case": { + "optional": true + }, + "drauu": { + "optional": true + }, + "focus-trap": { + "optional": true + }, + "fuse.js": { + "optional": true + }, + "idb-keyval": { + "optional": true + }, + "jwt-decode": { + "optional": true + }, + "nprogress": { + "optional": true + }, + "qrcode": { + "optional": true + }, + "sortablejs": { + "optional": true + }, + "universal-cookie": { + "optional": true + } + } + }, + "node_modules/@vueuse/metadata": { + "version": "12.8.2", + "resolved": "https://registry.npmjs.org/@vueuse/metadata/-/metadata-12.8.2.tgz", + "integrity": "sha512-rAyLGEuoBJ/Il5AmFHiziCPdQzRt88VxR+Y/A/QhJ1EWtWqPBBAxTAFaSkviwEuOEZNtW8pvkPgoCZQ+HxqW1A==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/@vueuse/shared": { + "version": "12.8.2", + "resolved": "https://registry.npmjs.org/@vueuse/shared/-/shared-12.8.2.tgz", + "integrity": "sha512-dznP38YzxZoNloI0qpEfpkms8knDtaoQ6Y/sfS0L7Yki4zh40LFHEhur0odJC6xTHG5dxWVPiUWBXn+wCG2s5w==", + "dev": true, + "license": "MIT", + "dependencies": { + "vue": "^3.5.13" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/acorn": { + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.18.0.tgz", + "integrity": "sha512-lGq+9yr1/GuAWaVYIHRjvvySG5/4VfKIvC8EWxStPdcDh/Ka7FG3twP6v4d5BkravUilhIAsG4Qj83t02LWUPQ==", + "dev": true, + "license": "MIT", + "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/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/algoliasearch": { + "version": "5.59.0", + "resolved": "https://registry.npmjs.org/algoliasearch/-/algoliasearch-5.59.0.tgz", + "integrity": "sha512-wUXzaeI7B526W4y1gFg3lcxgDZ67XSgRJIiellYWOas/pLpO7rOtxm9Gr2E/C8aiSDXIgx1q5TdSsvK67Uakqw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/abtesting": "1.25.0", + "@algolia/client-abtesting": "5.59.0", + "@algolia/client-analytics": "5.59.0", + "@algolia/client-common": "5.59.0", + "@algolia/client-insights": "5.59.0", + "@algolia/client-personalization": "5.59.0", + "@algolia/client-query-suggestions": "5.59.0", + "@algolia/client-search": "5.59.0", + "@algolia/ingestion": "1.59.0", + "@algolia/monitoring": "1.59.0", + "@algolia/recommend": "5.59.0", + "@algolia/requester-browser-xhr": "5.59.0", + "@algolia/requester-fetch": "5.59.0", + "@algolia/requester-node-http": "5.59.0" + }, + "engines": { + "node": ">= 14.0.0" + } + }, "node_modules/ansi-regex": { "version": "6.3.0", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.3.0.tgz", @@ -2538,6 +3290,16 @@ "dev": true, "license": "MIT" }, + "node_modules/birpc": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/birpc/-/birpc-2.9.0.tgz", + "integrity": "sha512-KrayHS5pBi69Xi9JmvoqrIgYGDkD6mcSe/i6YKi3w5kekCLzrX4+nawcXqrj2tIp50Kw/mT/s3p+GVK0A0sKxw==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, "node_modules/brace-expansion": { "version": "1.1.21", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.21.tgz", @@ -2657,6 +3419,28 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/character-entities-html4": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/character-entities-html4/-/character-entities-html4-2.1.0.tgz", + "integrity": "sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-entities-legacy": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-3.0.0.tgz", + "integrity": "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/chokidar": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", @@ -2726,6 +3510,17 @@ "dev": true, "license": "MIT" }, + "node_modules/comma-separated-tokens": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz", + "integrity": "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/commander": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", @@ -2803,6 +3598,19 @@ "node": ">=22" } }, + "node_modules/copy-anything": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/copy-anything/-/copy-anything-4.1.1.tgz", + "integrity": "sha512-AoT6Imdr98feSpFfmFwTFN73ccdr7uFPf27cBCgYvyyRyn1BzLRxMvrHNmwXO5LJMddRy4Rdhw2b1h7vSMKsEw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/mesqueeb" + } + }, "node_modules/cosmiconfig": { "version": "9.0.2", "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-9.0.2.tgz", @@ -2863,6 +3671,13 @@ "node": ">= 8" } }, + "node_modules/csstype": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", + "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", + "dev": true, + "license": "MIT" + }, "node_modules/debug": { "version": "4.4.3", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", @@ -2954,6 +3769,26 @@ "dev": true, "license": "MIT" }, + "node_modules/emoji-regex-xs": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex-xs/-/emoji-regex-xs-1.0.0.tgz", + "integrity": "sha512-LRlerrMYoIDrT6jgpeZ2YYl/L8EulRTt5hQcYjy5AInh7HWXKimpqx68aknBFpGL2+/IcogTcaydJEgaTmOpDg==", + "dev": true, + "license": "MIT" + }, + "node_modules/entities": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-7.0.1.tgz", + "integrity": "sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, "node_modules/env-paths": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", @@ -3610,6 +4445,16 @@ "dev": true, "license": "ISC" }, + "node_modules/focus-trap": { + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/focus-trap/-/focus-trap-7.8.0.tgz", + "integrity": "sha512-/yNdlIkpWbM0ptxno3ONTuf+2g318kh2ez3KSeZN5dZ8YC6AAmgeWz+GasYYiBJPFaYcSAPeu4GfhUaChzIJXA==", + "dev": true, + "license": "MIT", + "dependencies": { + "tabbable": "^6.4.0" + } + }, "node_modules/format": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/format/-/format-0.2.2.tgz", @@ -3787,6 +4632,62 @@ "node": ">=8" } }, + "node_modules/hast-util-to-html": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/hast-util-to-html/-/hast-util-to-html-9.0.5.tgz", + "integrity": "sha512-OguPdidb+fbHQSU4Q4ZiLKnzWo8Wwsf5bZfbvu7//a9oTYoqD/fWpe96NuHkoS9h0ccGOTe0C4NGXdtS0iObOw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/unist": "^3.0.0", + "ccount": "^2.0.0", + "comma-separated-tokens": "^2.0.0", + "hast-util-whitespace": "^3.0.0", + "html-void-elements": "^3.0.0", + "mdast-util-to-hast": "^13.0.0", + "property-information": "^7.0.0", + "space-separated-tokens": "^2.0.0", + "stringify-entities": "^4.0.0", + "zwitch": "^2.0.4" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-whitespace": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/hast-util-whitespace/-/hast-util-whitespace-3.0.0.tgz", + "integrity": "sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hookable": { + "version": "5.5.3", + "resolved": "https://registry.npmjs.org/hookable/-/hookable-5.5.3.tgz", + "integrity": "sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/html-void-elements": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/html-void-elements/-/html-void-elements-3.0.0.tgz", + "integrity": "sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/husky": { "version": "9.1.7", "resolved": "https://registry.npmjs.org/husky/-/husky-9.1.7.tgz", @@ -4477,6 +5378,13 @@ "source-map-js": "^1.2.1" } }, + "node_modules/mark.js": { + "version": "8.11.1", + "resolved": "https://registry.npmjs.org/mark.js/-/mark.js-8.11.1.tgz", + "integrity": "sha512-1I+1qpDt4idfgLQG+BNWmrqku+7/2bi5nLf4YwF8y8zXvmfiTBY3PV3ZibfrjBueCByROpuBjLLFCajqkgYoLQ==", + "dev": true, + "license": "MIT" + }, "node_modules/markdown-table": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/markdown-table/-/markdown-table-3.0.4.tgz", @@ -4697,6 +5605,28 @@ "url": "https://opencollective.com/unified" } }, + "node_modules/mdast-util-to-hast": { + "version": "13.2.1", + "resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-13.2.1.tgz", + "integrity": "sha512-cctsq2wp5vTsLIcaymblUriiTcZd0CwWtCbLvrOzYCDZoWyMNV8sZ7krj09FSnsiJi3WVsHLM4k6Dq/yaPyCXA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "@ungap/structured-clone": "^1.0.0", + "devlop": "^1.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "trim-lines": "^3.0.0", + "unist-util-position": "^5.0.0", + "unist-util-visit": "^5.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/mdast-util-to-markdown": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/mdast-util-to-markdown/-/mdast-util-to-markdown-2.1.2.tgz", @@ -5364,9 +6294,23 @@ "node": ">=16 || 14 >=14.17" } }, - "node_modules/mlly": { - "version": "1.8.2", - "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.8.2.tgz", + "node_modules/minisearch": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/minisearch/-/minisearch-7.2.0.tgz", + "integrity": "sha512-dqT2XBYUOZOiC5t2HRnwADjhNS2cecp9u+TJRiJ1Qp/f5qjkeT5APcGPjHw+bz89Ms8Jp+cG4AlE+QZ/QnDglg==", + "dev": true, + "license": "MIT" + }, + "node_modules/mitt": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mitt/-/mitt-3.0.1.tgz", + "integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==", + "dev": true, + "license": "MIT" + }, + "node_modules/mlly": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.8.2.tgz", "integrity": "sha512-d+ObxMQFmbt10sretNDytwt85VrbkhhUA/JBGm1MPaWJ65Cl4wOgLaB1NYvJSZ0Ef03MMEU/0xpPMXUIQ29UfA==", "dev": true, "license": "MIT", @@ -5408,7 +6352,6 @@ } ], "license": "MIT", - "peer": true, "bin": { "nanoid": "bin/nanoid.cjs" }, @@ -5447,6 +6390,18 @@ "node": ">=12.20.0" } }, + "node_modules/oniguruma-to-es": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/oniguruma-to-es/-/oniguruma-to-es-3.1.1.tgz", + "integrity": "sha512-bUH8SDvPkH3ho3dvwJwfonjlQ4R80vjyvrU8YpxuROddv55vAEJrTuCuCVUhhsHbtlD9tGGbaNApGQckXhS8iQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex-xs": "^1.0.0", + "regex": "^6.0.1", + "regex-recursion": "^6.0.2" + } + }, "node_modules/optionator": { "version": "0.9.4", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", @@ -5580,6 +6535,13 @@ "dev": true, "license": "MIT" }, + "node_modules/perfect-debounce": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/perfect-debounce/-/perfect-debounce-1.0.0.tgz", + "integrity": "sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==", + "dev": true, + "license": "MIT" + }, "node_modules/picocolors": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", @@ -5642,7 +6604,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "nanoid": "^3.3.18", "picocolors": "^1.1.1", @@ -5695,6 +6656,25 @@ } } }, + "node_modules/preact": { + "version": "10.29.8", + "resolved": "https://registry.npmjs.org/preact/-/preact-10.29.8.tgz", + "integrity": "sha512-ej2aVZ+vZ8WO7tvlQWRM9N63A0KzF9q4mWJfDUHgYaIofWY9hu74QdnQrjoPMmZi2/nZ5gN0bJCQF49xQqx09Q==", + "dev": true, + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/preact" + }, + "peerDependencies": { + "preact-render-to-string": ">=5" + }, + "peerDependenciesMeta": { + "preact-render-to-string": { + "optional": true + } + } + }, "node_modules/prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", @@ -5735,6 +6715,17 @@ "node": ">=6.0.0" } }, + "node_modules/property-information": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/property-information/-/property-information-7.2.0.tgz", + "integrity": "sha512-IAtzIB6sUiWaJYrX9smp3V46pBGbBeLFRGdh25kg1334VcBlD8HzhPeNIWQH9zhGmo2itIe25EHt9dQP7G5hmg==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/punycode": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", @@ -5772,6 +6763,33 @@ "node": "^12.0.0 || ^14.0.0 || >=16.0.0" } }, + "node_modules/regex": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/regex/-/regex-6.1.0.tgz", + "integrity": "sha512-6VwtthbV4o/7+OaAF9I5L5V3llLEsoPyq9P1JVXkedTP33c7MfCG0/5NOPcSJn0TzXcG9YUrR0gQSWioew3LDg==", + "dev": true, + "license": "MIT", + "dependencies": { + "regex-utilities": "^2.3.0" + } + }, + "node_modules/regex-recursion": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/regex-recursion/-/regex-recursion-6.0.2.tgz", + "integrity": "sha512-0YCaSCq2VRIebiaUviZNs0cBz1kg5kVS2UKUfNIx8YVs1cN3AV7NTctO5FOKBA+UT2BPJIWZauYHPqJODG50cg==", + "dev": true, + "license": "MIT", + "dependencies": { + "regex-utilities": "^2.3.0" + } + }, + "node_modules/regex-utilities": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/regex-utilities/-/regex-utilities-2.3.0.tgz", + "integrity": "sha512-8VhliFJAWRaUiVvREIiW2NXXTmHs4vMNnSzuJVhscgmGav3g9VDxLrQndI3dZZVVdp0ZO/5v0xmX516/7M9cng==", + "dev": true, + "license": "MIT" + }, "node_modules/regexp-ast-analysis": { "version": "0.7.1", "resolved": "https://registry.npmjs.org/regexp-ast-analysis/-/regexp-ast-analysis-0.7.1.tgz", @@ -5806,6 +6824,13 @@ "node": ">=4" } }, + "node_modules/rfdc": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz", + "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==", + "dev": true, + "license": "MIT" + }, "node_modules/rimraf": { "version": "6.1.3", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-6.1.3.tgz", @@ -5922,6 +6947,14 @@ "node": "^14.0.0 || >=16.0.0" } }, + "node_modules/search-insights": { + "version": "2.17.3", + "resolved": "https://registry.npmjs.org/search-insights/-/search-insights-2.17.3.tgz", + "integrity": "sha512-RQPdCYTa8A68uM2jwxoY842xDhvx3E5LFL1LxvxCNMev4o5mLuokczhzjAgGwUZBAmOKZknArSxLKmXtIi2AxQ==", + "dev": true, + "license": "MIT", + "peer": true + }, "node_modules/semver": { "version": "7.8.5", "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", @@ -5958,6 +6991,23 @@ "node": ">=8" } }, + "node_modules/shiki": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/shiki/-/shiki-2.5.0.tgz", + "integrity": "sha512-mI//trrsaiCIPsja5CNfsyNOqgAZUb6VpJA+340toL42UpzQlXpwRV9nch69X6gaUxrr9kaOOa6e3y3uAkGFxQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@shikijs/core": "2.5.0", + "@shikijs/engine-javascript": "2.5.0", + "@shikijs/engine-oniguruma": "2.5.0", + "@shikijs/langs": "2.5.0", + "@shikijs/themes": "2.5.0", + "@shikijs/types": "2.5.0", + "@shikijs/vscode-textmate": "^10.0.2", + "@types/hast": "^3.0.4" + } + }, "node_modules/siginfo": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", @@ -5985,6 +7035,27 @@ "node": ">=0.10.0" } }, + "node_modules/space-separated-tokens": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz", + "integrity": "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/speakingurl": { + "version": "14.0.1", + "resolved": "https://registry.npmjs.org/speakingurl/-/speakingurl-14.0.1.tgz", + "integrity": "sha512-1POYv7uv2gXoyGFpBCmpDVSNV74IfsWlDW216UPjbWufNf+bSU6GdbDsxdcxtfwb4xlI3yxzOTKClUosxARYrQ==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/stackback": { "version": "0.0.2", "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", @@ -6026,6 +7097,21 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/stringify-entities": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/stringify-entities/-/stringify-entities-4.0.4.tgz", + "integrity": "sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==", + "dev": true, + "license": "MIT", + "dependencies": { + "character-entities-html4": "^2.0.0", + "character-entities-legacy": "^3.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/strip-ansi": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", @@ -6078,6 +7164,19 @@ "node": ">=16 || 14 >=14.17" } }, + "node_modules/superjson": { + "version": "2.2.6", + "resolved": "https://registry.npmjs.org/superjson/-/superjson-2.2.6.tgz", + "integrity": "sha512-H+ue8Zo4vJmV2nRjpx86P35lzwDT3nItnIsocgumgr0hHMQ+ZGq5vrERg9kJBo5AWGmxZDhzDo+WVIJqkB0cGA==", + "dev": true, + "license": "MIT", + "dependencies": { + "copy-anything": "^4" + }, + "engines": { + "node": ">=16" + } + }, "node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -6107,6 +7206,13 @@ "url": "https://opencollective.com/synckit" } }, + "node_modules/tabbable": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/tabbable/-/tabbable-6.5.0.tgz", + "integrity": "sha512-wieBHXygIm7OyQOu5hQlkk62/WyCFYGlWg7L6/ZCUZwx0o398Zkn4pVmMyfYhfMG8kGrj/Krt8eIk6UKC6VzwA==", + "dev": true, + "license": "MIT" + }, "node_modules/thenify": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", @@ -6184,6 +7290,17 @@ "tree-kill": "cli.js" } }, + "node_modules/trim-lines": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/trim-lines/-/trim-lines-3.0.1.tgz", + "integrity": "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/ts-api-utils": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.5.0.tgz", @@ -6346,6 +7463,20 @@ "url": "https://opencollective.com/unified" } }, + "node_modules/unist-util-position": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/unist-util-position/-/unist-util-position-5.0.0.tgz", + "integrity": "sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/unist-util-stringify-position": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz", @@ -6401,6 +7532,36 @@ "punycode": "^2.1.0" } }, + "node_modules/vfile": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.3.tgz", + "integrity": "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/vfile-message": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-4.0.3.tgz", + "integrity": "sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-stringify-position": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/vite": { "version": "8.3.0", "resolved": "https://registry.npmjs.org/vite/-/vite-8.3.0.tgz", @@ -6480,107 +7641,675 @@ } } }, - "node_modules/vitest": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/vitest/-/vitest-5.0.1.tgz", - "integrity": "sha512-iA95lQbKEkvrtTkdAgnWbXfbipWiiWe/hDl2P5tMi6WFwD76G0NxXAGp/M9EOcYupeGJRr6wppMc7CoA41TQjg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/chai": "^5.2.2", - "@vitest/mocker": "5.0.1", - "chai": "^6.2.2", - "es-module-lexer": "^2.3.2", - "expect-type": "^1.4.0", - "magic-string": "^1.2.3", - "obug": "^2.1.4", - "picomatch": "^4.0.7", - "std-env": "^4.2.0", - "tinybench": "6.1.4", - "tinyexec": "1.3.0", - "tinyglobby": "^0.2.17", - "why-is-node-running": "^2.3.0" + "node_modules/vitepress": { + "version": "1.6.4", + "resolved": "https://registry.npmjs.org/vitepress/-/vitepress-1.6.4.tgz", + "integrity": "sha512-+2ym1/+0VVrbhNyRoFFesVvBvHAVMZMK0rw60E3X/5349M1GuVdKeazuksqopEdvkKwKGs21Q729jX81/bkBJg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@docsearch/css": "3.8.2", + "@docsearch/js": "3.8.2", + "@iconify-json/simple-icons": "^1.2.21", + "@shikijs/core": "^2.1.0", + "@shikijs/transformers": "^2.1.0", + "@shikijs/types": "^2.1.0", + "@types/markdown-it": "^14.1.2", + "@vitejs/plugin-vue": "^5.2.1", + "@vue/devtools-api": "^7.7.0", + "@vue/shared": "^3.5.13", + "@vueuse/core": "^12.4.0", + "@vueuse/integrations": "^12.4.0", + "focus-trap": "^7.6.4", + "mark.js": "8.11.1", + "minisearch": "^7.1.1", + "shiki": "^2.1.0", + "vite": "^5.4.14", + "vue": "^3.5.13" }, "bin": { - "vitest": "vitest.mjs" - }, - "engines": { - "node": "^22.12.0 || ^24.0.0 || >=26.0.0" - }, - "funding": { - "url": "https://opencollective.com/vitest" + "vitepress": "bin/vitepress.js" }, "peerDependencies": { - "@edge-runtime/vm": "*", - "@opentelemetry/api": "^1.9.0", - "@types/node": "^22.0.0 || >=24.0.0", - "@vitest/browser-playwright": "5.0.1", - "@vitest/browser-preview": "5.0.1", - "@vitest/browser-webdriverio": "^5.0.0-beta.5 || >=5.0.0", - "@vitest/coverage-istanbul": "5.0.1", - "@vitest/coverage-v8": "5.0.1", - "@vitest/ui": "5.0.1", - "happy-dom": "*", - "jsdom": "*", - "vite": "^6.4.0 || ^7.0.0 || ^8.0.0" + "markdown-it-mathjax3": "^4", + "postcss": "^8" }, "peerDependenciesMeta": { - "@edge-runtime/vm": { - "optional": true - }, - "@opentelemetry/api": { - "optional": true - }, - "@types/node": { - "optional": true - }, - "@vitest/browser-playwright": { - "optional": true - }, - "@vitest/browser-preview": { - "optional": true - }, - "@vitest/browser-webdriverio": { - "optional": true - }, - "@vitest/coverage-istanbul": { + "markdown-it-mathjax3": { "optional": true }, - "@vitest/coverage-v8": { - "optional": true - }, - "@vitest/ui": { - "optional": true - }, - "happy-dom": { - "optional": true - }, - "jsdom": { + "postcss": { "optional": true - }, - "vite": { - "optional": false } } }, - "node_modules/vitest/node_modules/magic-string": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-1.4.1.tgz", - "integrity": "sha512-8lyCu36ErXR0J9uaGKlKQoiLZKmtI63YGLE8G2o9jyRPdr4X47LusSOwgOJOzcVtp81fTAAjxR7BwKz682Jhow==", + "node_modules/vitepress/node_modules/@esbuild/aix-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", + "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==", + "cpu": [ + "ppc64" + ], "dev": true, "license": "MIT", - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.6.0" + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=12" } }, - "node_modules/vitest/node_modules/tinyexec": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.3.0.tgz", - "integrity": "sha512-QKAl9m8gWWGHV8jZcPeym6j+XULi6tOf1mT83WYJ4Lk2ytW/uwAWkrP0uFsdoYMdueVJ0qs26wZ+23xeB4ibNQ==", + "node_modules/vitepress/node_modules/@esbuild/android-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz", + "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==", + "cpu": [ + "arm" + ], "dev": true, "license": "MIT", + "optional": true, + "os": [ + "android" + ], "engines": { - "node": ">=18" + "node": ">=12" + } + }, + "node_modules/vitepress/node_modules/@esbuild/android-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz", + "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vitepress/node_modules/@esbuild/android-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz", + "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vitepress/node_modules/@esbuild/darwin-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", + "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vitepress/node_modules/@esbuild/darwin-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", + "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vitepress/node_modules/@esbuild/freebsd-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", + "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vitepress/node_modules/@esbuild/freebsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz", + "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vitepress/node_modules/@esbuild/linux-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", + "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vitepress/node_modules/@esbuild/linux-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", + "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vitepress/node_modules/@esbuild/linux-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", + "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vitepress/node_modules/@esbuild/linux-loong64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz", + "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vitepress/node_modules/@esbuild/linux-mips64el": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", + "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vitepress/node_modules/@esbuild/linux-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz", + "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vitepress/node_modules/@esbuild/linux-riscv64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", + "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vitepress/node_modules/@esbuild/linux-s390x": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", + "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vitepress/node_modules/@esbuild/linux-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", + "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vitepress/node_modules/@esbuild/netbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", + "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vitepress/node_modules/@esbuild/openbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", + "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vitepress/node_modules/@esbuild/sunos-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", + "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vitepress/node_modules/@esbuild/win32-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", + "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vitepress/node_modules/@esbuild/win32-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz", + "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vitepress/node_modules/@esbuild/win32-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", + "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vitepress/node_modules/@vitejs/plugin-vue": { + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-5.2.4.tgz", + "integrity": "sha512-7Yx/SXSOcQq5HiiV3orevHUFn+pmMB4cgbEkDYgnkUWb0WfeQ/wa2yFv6D5ICiCQOVpjA7vYDXrC7AGO8yjDHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "peerDependencies": { + "vite": "^5.0.0 || ^6.0.0", + "vue": "^3.2.25" + } + }, + "node_modules/vitepress/node_modules/esbuild": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", + "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.21.5", + "@esbuild/android-arm": "0.21.5", + "@esbuild/android-arm64": "0.21.5", + "@esbuild/android-x64": "0.21.5", + "@esbuild/darwin-arm64": "0.21.5", + "@esbuild/darwin-x64": "0.21.5", + "@esbuild/freebsd-arm64": "0.21.5", + "@esbuild/freebsd-x64": "0.21.5", + "@esbuild/linux-arm": "0.21.5", + "@esbuild/linux-arm64": "0.21.5", + "@esbuild/linux-ia32": "0.21.5", + "@esbuild/linux-loong64": "0.21.5", + "@esbuild/linux-mips64el": "0.21.5", + "@esbuild/linux-ppc64": "0.21.5", + "@esbuild/linux-riscv64": "0.21.5", + "@esbuild/linux-s390x": "0.21.5", + "@esbuild/linux-x64": "0.21.5", + "@esbuild/netbsd-x64": "0.21.5", + "@esbuild/openbsd-x64": "0.21.5", + "@esbuild/sunos-x64": "0.21.5", + "@esbuild/win32-arm64": "0.21.5", + "@esbuild/win32-ia32": "0.21.5", + "@esbuild/win32-x64": "0.21.5" + } + }, + "node_modules/vitepress/node_modules/vite": { + "version": "5.4.21", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.21.tgz", + "integrity": "sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.21.3", + "postcss": "^8.4.43", + "rollup": "^4.20.0" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || >=20.0.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "sass-embedded": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.4.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + } + } + }, + "node_modules/vitest": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-5.0.1.tgz", + "integrity": "sha512-iA95lQbKEkvrtTkdAgnWbXfbipWiiWe/hDl2P5tMi6WFwD76G0NxXAGp/M9EOcYupeGJRr6wppMc7CoA41TQjg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/chai": "^5.2.2", + "@vitest/mocker": "5.0.1", + "chai": "^6.2.2", + "es-module-lexer": "^2.3.2", + "expect-type": "^1.4.0", + "magic-string": "^1.2.3", + "obug": "^2.1.4", + "picomatch": "^4.0.7", + "std-env": "^4.2.0", + "tinybench": "6.1.4", + "tinyexec": "1.3.0", + "tinyglobby": "^0.2.17", + "why-is-node-running": "^2.3.0" + }, + "bin": { + "vitest": "vitest.mjs" + }, + "engines": { + "node": "^22.12.0 || ^24.0.0 || >=26.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "@edge-runtime/vm": "*", + "@opentelemetry/api": "^1.9.0", + "@types/node": "^22.0.0 || >=24.0.0", + "@vitest/browser-playwright": "5.0.1", + "@vitest/browser-preview": "5.0.1", + "@vitest/browser-webdriverio": "^5.0.0-beta.5 || >=5.0.0", + "@vitest/coverage-istanbul": "5.0.1", + "@vitest/coverage-v8": "5.0.1", + "@vitest/ui": "5.0.1", + "happy-dom": "*", + "jsdom": "*", + "vite": "^6.4.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "@edge-runtime/vm": { + "optional": true + }, + "@opentelemetry/api": { + "optional": true + }, + "@types/node": { + "optional": true + }, + "@vitest/browser-playwright": { + "optional": true + }, + "@vitest/browser-preview": { + "optional": true + }, + "@vitest/browser-webdriverio": { + "optional": true + }, + "@vitest/coverage-istanbul": { + "optional": true + }, + "@vitest/coverage-v8": { + "optional": true + }, + "@vitest/ui": { + "optional": true + }, + "happy-dom": { + "optional": true + }, + "jsdom": { + "optional": true + }, + "vite": { + "optional": false + } + } + }, + "node_modules/vitest/node_modules/magic-string": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-1.4.1.tgz", + "integrity": "sha512-8lyCu36ErXR0J9uaGKlKQoiLZKmtI63YGLE8G2o9jyRPdr4X47LusSOwgOJOzcVtp81fTAAjxR7BwKz682Jhow==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.6.0" + } + }, + "node_modules/vitest/node_modules/tinyexec": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.3.0.tgz", + "integrity": "sha512-QKAl9m8gWWGHV8jZcPeym6j+XULi6tOf1mT83WYJ4Lk2ytW/uwAWkrP0uFsdoYMdueVJ0qs26wZ+23xeB4ibNQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/vue": { + "version": "3.5.43", + "resolved": "https://registry.npmjs.org/vue/-/vue-3.5.43.tgz", + "integrity": "sha512-o5qZoksdnjIKvW1srZ3ab7pcDNYAerBjRe54D0LBLfRdCYFrSgBHVXokMas35czQc0//lmx4/tuY4ZNQ+Rf2Ng==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vue/compiler-dom": "3.5.43", + "@vue/compiler-sfc": "3.5.43", + "@vue/runtime-dom": "3.5.43", + "@vue/server-renderer": "3.5.43", + "@vue/shared": "3.5.43" + }, + "peerDependencies": { + "typescript": "*" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, "node_modules/which": { diff --git a/package.json b/package.json index af3611c..20d0f95 100644 --- a/package.json +++ b/package.json @@ -4,6 +4,7 @@ "description": "A lightweight, type-safe, dependency-free string utility library for TypeScript and modern JavaScript applications.", "author": "rcpthongta", "license": "MIT", + "homepage": "https://rcpthongta.github.io/string-utils", "repository": { "type": "git", "url": "git+https://github.com/rcpthongta/string-utils.git" @@ -50,6 +51,9 @@ "test:watch": "vitest", "test:coverage": "vitest run --coverage", "bench": "vitest bench --run", + "docs:dev": "vitepress dev docs", + "docs:build": "vitepress build docs", + "docs:preview": "vitepress preview docs", "lint": "eslint **/*.{*rc,cjs,js,json,json5,jsonc,jsonl,md,mjs,ts,yaml,yml}", "lint:fix": "eslint --fix **/*.{*rc,cjs,js,json,json5,jsonc,jsonl,md,mjs,ts,yaml,yml}" }, @@ -76,6 +80,7 @@ "tsup": "^8.5.1", "typescript": "^5.9.3", "typescript-eslint": "^8.65.0", + "vitepress": "^1.6.4", "vitest": "^5.0.1", "yaml-eslint-parser": "^1.3.2" } From ab24ddc98ba81ca9547d72df6c21ec242b0b6014 Mon Sep 17 00:00:00 2001 From: rcpthongta <220981657+rcpthongta@users.noreply.github.com> Date: Thu, 24 Sep 2026 02:11:48 +0700 Subject: [PATCH 112/121] docs(site): add vitepress documentation website and api reference --- docs/.vitepress/config.ts | 165 ++++++++++++++++++++++++++++++++ docs/.vitepress/theme/index.ts | 7 ++ docs/.vitepress/theme/style.css | 62 ++++++++++++ docs/api/camel-case.md | 52 ++++++++++ docs/api/capitalize.md | 44 +++++++++ docs/api/collapse-whitespace.md | 44 +++++++++ docs/api/constant-case.md | 49 ++++++++++ docs/api/default-if-blank.md | 44 +++++++++ docs/api/default-if-empty.md | 44 +++++++++ docs/api/format.md | 99 +++++++++++++++++++ docs/api/has-length.md | 51 ++++++++++ docs/api/has-text.md | 43 +++++++++ docs/api/index.md | 63 ++++++++++++ docs/api/kebab-case.md | 49 ++++++++++ docs/api/mask.md | 146 ++++++++++++++++++++++++++++ docs/api/pascal-case.md | 49 ++++++++++ docs/api/redact.md | 63 ++++++++++++ docs/api/slugify.md | 57 +++++++++++ docs/api/snake-case.md | 49 ++++++++++ docs/api/uncapitalize.md | 44 +++++++++ docs/guide/getting-started.md | 73 ++++++++++++++ docs/guide/installation.md | 56 +++++++++++ docs/index.md | 32 +++++++ docs/tsconfig.json | 4 + 24 files changed, 1389 insertions(+) create mode 100644 docs/.vitepress/config.ts create mode 100644 docs/.vitepress/theme/index.ts create mode 100644 docs/.vitepress/theme/style.css create mode 100644 docs/api/camel-case.md create mode 100644 docs/api/capitalize.md create mode 100644 docs/api/collapse-whitespace.md create mode 100644 docs/api/constant-case.md create mode 100644 docs/api/default-if-blank.md create mode 100644 docs/api/default-if-empty.md create mode 100644 docs/api/format.md create mode 100644 docs/api/has-length.md create mode 100644 docs/api/has-text.md create mode 100644 docs/api/index.md create mode 100644 docs/api/kebab-case.md create mode 100644 docs/api/mask.md create mode 100644 docs/api/pascal-case.md create mode 100644 docs/api/redact.md create mode 100644 docs/api/slugify.md create mode 100644 docs/api/snake-case.md create mode 100644 docs/api/uncapitalize.md create mode 100644 docs/guide/getting-started.md create mode 100644 docs/guide/installation.md create mode 100644 docs/index.md create mode 100644 docs/tsconfig.json diff --git a/docs/.vitepress/config.ts b/docs/.vitepress/config.ts new file mode 100644 index 0000000..f2c1c0a --- /dev/null +++ b/docs/.vitepress/config.ts @@ -0,0 +1,165 @@ +import { defineConfig } from "vitepress"; + +export default defineConfig({ + base: "/string-utils/", + title: "@rcpthongta/string-utils", + description: + "A lightweight, type-safe, dependency-free string utility library for TypeScript and modern JavaScript applications.", + themeConfig: { + nav: [ + { + text: "Guide", + link: "/guide/getting-started" + }, + { + text: "API Reference", + link: "/api/" + }, + { + text: "Changelog", + link: "https://github.com/rcpthongta/string-utils/blob/main/CHANGELOG.md" + } + ], + sidebar: [ + { + text: "Guide", + items: [ + { + text: "Getting Started", + link: "/guide/getting-started" + }, + { + text: "Installation", + link: "/guide/installation" + } + ] + }, + { + text: "API Reference", + items: [ + { + text: "Overview", + link: "/api/" + } + ] + }, + { + text: "Case Conversion", + collapsed: false, + items: [ + { + text: "camelCase", + link: "/api/camel-case" + }, + { + text: "constantCase", + link: "/api/constant-case" + }, + { + text: "kebabCase", + link: "/api/kebab-case" + }, + { + text: "pascalCase", + link: "/api/pascal-case" + }, + { + text: "snakeCase", + link: "/api/snake-case" + } + ] + }, + { + text: "Casing & Capitalization", + collapsed: false, + items: [ + { + text: "capitalize", + link: "/api/capitalize" + }, + { + text: "uncapitalize", + link: "/api/uncapitalize" + } + ] + }, + { + text: "Formatting & Templating", + collapsed: false, + items: [ + { + text: "collapseWhitespace", + link: "/api/collapse-whitespace" + }, + { + text: "format / createFormatter", + link: "/api/format" + }, + { + text: "slugify", + link: "/api/slugify" + } + ] + }, + { + text: "Pattern Masking", + collapsed: false, + items: [ + { + text: "mask / createMask", + link: "/api/mask" + } + ] + }, + { + text: "Validation & Fallback", + collapsed: false, + items: [ + { + text: "hasLength", + link: "/api/has-length" + }, + { + text: "hasText", + link: "/api/has-text" + }, + { + text: "defaultIfBlank", + link: "/api/default-if-blank" + }, + { + text: "defaultIfEmpty", + link: "/api/default-if-empty" + } + ] + }, + { + text: "Privacy & Redaction", + collapsed: false, + items: [ + { + text: "redact", + link: "/api/redact" + } + ] + } + ], + search: { + provider: "local" + }, + socialLinks: [ + { + icon: "github", + link: "https://github.com/rcpthongta/string-utils" + }, + { + icon: "npm", + link: "https://www.npmjs.com/package/@rcpthongta/string-utils" + } + ], + footer: { + message: "Released under the MIT License.", + copyright: "Copyright © 2026 rcpthongta" + } + } +}); diff --git a/docs/.vitepress/theme/index.ts b/docs/.vitepress/theme/index.ts new file mode 100644 index 0000000..c026429 --- /dev/null +++ b/docs/.vitepress/theme/index.ts @@ -0,0 +1,7 @@ +import DefaultTheme from "vitepress/theme"; + +import "./style.css"; + +export default { + extends: DefaultTheme +}; diff --git a/docs/.vitepress/theme/style.css b/docs/.vitepress/theme/style.css new file mode 100644 index 0000000..0abbafd --- /dev/null +++ b/docs/.vitepress/theme/style.css @@ -0,0 +1,62 @@ +/** + * Custom VitePress Theme Colors + * Base Brand: rgb(244, 184, 228) / #f4b8e4 + */ + +:root { + --vp-c-brand-1: #c54a9c; + --vp-c-brand-2: #d96bb2; + --vp-c-brand-3: rgb(244, 184, 228); + --vp-c-brand-soft: rgba(244, 184, 228, 0.16); + + --vp-home-hero-name-color: transparent; + --vp-home-hero-name-background: -webkit-linear-gradient(120deg, #c54a9c 30%, rgb(244, 184, 228)); + --vp-home-hero-image-background-image: linear-gradient(-45deg, rgb(244, 184, 228) 40%, #c54a9c 60%); + --vp-home-hero-image-filter: blur(48px); +} + +.dark { + --vp-c-brand-1: rgb(244, 184, 228); + --vp-c-brand-2: #f7c9ec; + --vp-c-brand-3: #fae1f5; + --vp-c-brand-soft: rgba(244, 184, 228, 0.16); + + --vp-home-hero-name-color: transparent; + --vp-home-hero-name-background: -webkit-linear-gradient(120deg, rgb(244, 184, 228) 30%, #fdeaf6); + --vp-home-hero-image-background-image: linear-gradient(-45deg, rgb(244, 184, 228) 30%, #872e68 70%); + --vp-home-hero-image-filter: blur(48px); +} + +/* Hero Subtitle Font Size */ +.VPHero .text { + font-size: 28px !important; + line-height: 36px !important; +} + +@media (min-width: 640px) { + .VPHero .text { + font-size: 32px !important; + line-height: 40px !important; + } +} + +@media (min-width: 960px) { + .VPHero .text { + font-size: 36px !important; + line-height: 44px !important; + } +} + +/* Table Column Wrapping */ +.vp-doc table th { + white-space: nowrap; +} + +.vp-doc table td:not(:last-child) { + white-space: nowrap; +} + +.vp-doc table td:last-child { + white-space: normal; +} + diff --git a/docs/api/camel-case.md b/docs/api/camel-case.md new file mode 100644 index 0000000..d2fa3bf --- /dev/null +++ b/docs/api/camel-case.md @@ -0,0 +1,52 @@ +# camelCase + +Converts any string to `camelCase`. Supports standard separators, acronyms, numbers, and Unicode characters. + +## Signature + +```typescript +function camelCase(value: string | null | undefined): string; +``` + +## Parameters + +| Parameter | Type | Description | +|-----------|-------------------------------|------------------------------| +| `value` | `string \| null \| undefined` | The input string to convert. | + +## Returns + +`string` — The camelCase formatted string, or an empty string `""` when `value` is empty, `null`, `undefined`, or contains no extractable words. + +## Examples + +```typescript +import { camelCase } from "@rcpthongta/string-utils"; + +camelCase("Foo Bar"); +// => "fooBar" + +camelCase("--foo-bar--"); +// => "fooBar" + +camelCase("FOO_BAR"); +// => "fooBar" + +camelCase("XMLHttpRequest"); +// => "xmlHttpRequest" + +camelCase("version 2.0"); +// => "version20" + +camelCase("Привет Мир"); +// => "приветМир" + +camelCase(""); +// => "" + +camelCase(null); +// => "" + +camelCase(undefined); +// => "" +``` diff --git a/docs/api/capitalize.md b/docs/api/capitalize.md new file mode 100644 index 0000000..a8511d7 --- /dev/null +++ b/docs/api/capitalize.md @@ -0,0 +1,44 @@ +# capitalize + +Capitalizes the first character of a string while preserving its literal TypeScript type. + +## Signature + +```typescript +function capitalize(value: T): Capitalize; +function capitalize(value: string | null | undefined): string; +``` + +## Parameters + +| Parameter | Type | Description | +|-----------|-------------------------------|---------------------------------| +| `value` | `string \| null \| undefined` | The input string to capitalize. | + +## Returns + +`Capitalize | string` — The capitalized string, or an empty string `""` when `value` is empty, `null`, or `undefined`. + +## Examples + +```typescript +import { capitalize } from "@rcpthongta/string-utils"; + +capitalize("hello"); +// => "Hello" + +capitalize("helloWorld"); +// => "HelloWorld" + +capitalize("Hello"); +// => "Hello" + +capitalize(""); +// => "" + +capitalize(null); +// => "" + +capitalize(undefined); +// => "" +``` diff --git a/docs/api/collapse-whitespace.md b/docs/api/collapse-whitespace.md new file mode 100644 index 0000000..3f5cfb5 --- /dev/null +++ b/docs/api/collapse-whitespace.md @@ -0,0 +1,44 @@ +# collapseWhitespace + +Collapses consecutive whitespace characters (including tabs, newlines, non-breaking spaces, and ideographic spaces) into a single space and trims leading and trailing whitespace. + +## Signature + +```typescript +function collapseWhitespace(value: string | null | undefined): string; +``` + +## Parameters + +| Parameter | Type | Description | +|-----------|-------------------------------|--------------------------------| +| `value` | `string \| null \| undefined` | The input string to normalize. | + +## Returns + +`string` — The normalized string with single spaces, or an empty string `""` when `value` is empty, `null`, or `undefined`. + +## Examples + +```typescript +import { collapseWhitespace } from "@rcpthongta/string-utils"; + +collapseWhitespace("Hello World"); +// => "Hello World" + +collapseWhitespace(" \t\n Hello World \n "); +// => "Hello World" + +// Non-breaking space (NBSP) support +collapseWhitespace("Hello\u00A0World"); +// => "Hello World" + +collapseWhitespace(""); +// => "" + +collapseWhitespace(null); +// => "" + +collapseWhitespace(undefined); +// => "" +``` diff --git a/docs/api/constant-case.md b/docs/api/constant-case.md new file mode 100644 index 0000000..99b8af6 --- /dev/null +++ b/docs/api/constant-case.md @@ -0,0 +1,49 @@ +# constantCase + +Converts any string to uppercase `CONSTANT_CASE`. Supports standard separators, acronyms, numbers, and Unicode characters. + +## Signature + +```typescript +function constantCase(value: string | null | undefined): string; +``` + +## Parameters + +| Parameter | Type | Description | +|-----------|-------------------------------|------------------------------| +| `value` | `string \| null \| undefined` | The input string to convert. | + +## Returns + +`string` — The CONSTANT_CASE formatted string, or an empty string `""` when `value` is empty, `null`, `undefined`, or contains no extractable words. + +## Examples + +```typescript +import { constantCase } from "@rcpthongta/string-utils"; + +constantCase("foo bar"); +// => "FOO_BAR" + +constantCase("fooBar"); +// => "FOO_BAR" + +constantCase("--foo-bar--"); +// => "FOO_BAR" + +constantCase("XMLHttpRequest"); +// => "XML_HTTP_REQUEST" + +constantCase("version 2.0"); +// => "VERSION_20" + +constantCase(""); +// => "" + +constantCase(null); +// => "" + +constantCase(undefined); +// => "" +``` diff --git a/docs/api/default-if-blank.md b/docs/api/default-if-blank.md new file mode 100644 index 0000000..f1b690d --- /dev/null +++ b/docs/api/default-if-blank.md @@ -0,0 +1,44 @@ +# defaultIfBlank + +Returns a default fallback value when the given input is empty, contains only whitespace, `null`, or `undefined`. + +## Signature + +```typescript +function defaultIfBlank( + value: string | null | undefined, + defaultValue: string +): string; +``` + +## Parameters + +| Parameter | Type | Description | +|----------------|-------------------------------|---------------------------------------------------| +| `value` | `string \| null \| undefined` | The string value to check. | +| `defaultValue` | `string` | The fallback value returned when `value` is blank.| + +## Returns + +`string` — The original `value` if it contains non-whitespace text; otherwise `defaultValue`. + +## Examples + +```typescript +import { defaultIfBlank } from "@rcpthongta/string-utils"; + +defaultIfBlank("Hello", "default"); +// => "Hello" + +defaultIfBlank(" ", "default"); +// => "default" (whitespace is treated as blank) + +defaultIfBlank("", "default"); +// => "default" + +defaultIfBlank(null, "default"); +// => "default" + +defaultIfBlank(undefined, "default"); +// => "default" +``` diff --git a/docs/api/default-if-empty.md b/docs/api/default-if-empty.md new file mode 100644 index 0000000..6a05a32 --- /dev/null +++ b/docs/api/default-if-empty.md @@ -0,0 +1,44 @@ +# defaultIfEmpty + +Returns a default fallback value when the given input is empty (`length === 0`), `null`, or `undefined`. Unlike `defaultIfBlank`, whitespace strings are preserved. + +## Signature + +```typescript +function defaultIfEmpty( + value: string | null | undefined, + defaultValue: string +): string; +``` + +## Parameters + +| Parameter | Type | Description | +|----------------|-------------------------------|---------------------------------------------------| +| `value` | `string \| null \| undefined` | The string value to check. | +| `defaultValue` | `string` | The fallback value returned when `value` is empty.| + +## Returns + +`string` — The original `value` if it is non-empty; otherwise `defaultValue`. + +## Examples + +```typescript +import { defaultIfEmpty } from "@rcpthongta/string-utils"; + +defaultIfEmpty("Hello", "default"); +// => "Hello" + +defaultIfEmpty(" ", "default"); +// => " " (whitespace string has length > 0, so it is preserved) + +defaultIfEmpty("", "default"); +// => "default" + +defaultIfEmpty(null, "default"); +// => "default" + +defaultIfEmpty(undefined, "default"); +// => "default" +``` diff --git a/docs/api/format.md b/docs/api/format.md new file mode 100644 index 0000000..2faa11c --- /dev/null +++ b/docs/api/format.md @@ -0,0 +1,99 @@ +# format / createFormatter + +Template interpolation utility supporting positional placeholders and nested object/array dot-notation paths. + +## `format` + +Interpolates placeholders in a template with provided arguments. + +### Signature + +```typescript +function format( + template: string | null | undefined, + ...args: unknown[] +): string; +``` + +### Parameters + +| Parameter | Type | Description | +|------------|-------------------------------|-----------------------------------------------------| +| `template` | `string \| null \| undefined` | The template string containing placeholders. | +| `...args` | `unknown[]` | The values used to resolve and replace placeholders.| + +### Returns + +`string` — The formatted string, or an empty string `""` when `template` is empty, `null`, or `undefined`. + +### Examples + +```typescript +import { format } from "@rcpthongta/string-utils"; + +// Positional arguments +format("Hello {0} {1}!", "John", "Doe"); +// => "Hello John Doe!" + +// Nested object property paths +format("User: {0.name} ({0.role})", { name: "Alice", role: "Admin" }); +// => "User: Alice (Admin)" + +// Deep nested array and object paths +format("{0.users.0.name}", { users: [{ name: "Bob" }] }); +// => "Bob" + +format("", "World"); +// => "" + +format(null, "World"); +// => "" + +format(undefined, "World"); +// => "" +``` + +--- + +## `createFormatter` + +Compiles a template once into a high-performance reusable formatting function. Ideal for high-frequency or loop formatting. + +### Signature + +```typescript +function createFormatter(template: string | null | undefined): (...args: unknown[]) => string; +``` + +### Parameters + +| Parameter | Type | Description | +|------------|-------------------------------|---------------------------------| +| `template` | `string \| null \| undefined` | The template string to compile. | + +### Returns + +`FormatFunction` — A reusable formatting function `(...args: unknown[]) => string`. + +### Examples + +```typescript +import { createFormatter } from "@rcpthongta/string-utils"; + +const greet = createFormatter("Hello {0.name}!"); + +greet({ name: "Alice" }); +// => "Hello Alice!" + +greet({ name: "Bob" }); +// => "Hello Bob!" + +greet(""); +// => "Hello !" + +greet(null); +// => "Hello null!" + +greet(undefined); +// => "Hello undefined!" +``` diff --git a/docs/api/has-length.md b/docs/api/has-length.md new file mode 100644 index 0000000..9dd70c6 --- /dev/null +++ b/docs/api/has-length.md @@ -0,0 +1,51 @@ +# hasLength + +TypeScript type guard that checks whether a value is a non-empty string (`length > 0`). + +## Signature + +```typescript +function hasLength(value: unknown): value is string; +``` + +## Parameters + +| Parameter | Type | Description | +|-----------|-----------|---------------------| +| `value` | `unknown` | The value to check. | + +## Returns + +`boolean` — `true` if `value` is a string with `length > 0`; otherwise `false`. Acts as a TypeScript type guard narrowing `unknown` to `string`. + +## Examples + +```typescript +import { hasLength } from "@rcpthongta/string-utils"; + +hasLength("Hello World"); +// => true + +hasLength(" "); +// => true (whitespace counts as length) + +hasLength(""); +// => false + +hasLength(123); +// => false + +hasLength(null); +// => false + +hasLength(undefined); +// => false + +// TypeScript type narrowing example +function processInput(val: unknown) { + if (hasLength(val)) { + // val is automatically narrowed to `string` + console.log(val.toUpperCase()); + } +} +``` diff --git a/docs/api/has-text.md b/docs/api/has-text.md new file mode 100644 index 0000000..3f23c9d --- /dev/null +++ b/docs/api/has-text.md @@ -0,0 +1,43 @@ +# hasText + +TypeScript type guard that checks whether a value is a string containing at least one non-whitespace character. + +## Signature + +```typescript +function hasText(value: unknown): value is string; +``` + +## Parameters + +| Parameter | Type | Description | +|-----------|-----------|---------------------| +| `value` | `unknown` | The value to check. | + +## Returns + +`boolean` — `true` if `value` is a string with non-whitespace characters; otherwise `false`. + +## Examples + +```typescript +import { hasText } from "@rcpthongta/string-utils"; + +hasText("Hello World"); +// => true + +hasText(" "); +// => false (whitespace-only is rejected) + +hasText(""); +// => false + +hasText(123); +// => false + +hasText(null); +// => false + +hasText(undefined); +// => false +``` diff --git a/docs/api/index.md b/docs/api/index.md new file mode 100644 index 0000000..ccd302c --- /dev/null +++ b/docs/api/index.md @@ -0,0 +1,63 @@ +# API Reference + +A complete overview of all utility functions provided by `@rcpthongta/string-utils`. + +## Case Conversion + +Convert strings between various identifier casing conventions. + +| Function | Description | +|--------------------------------------|-------------------------------------------------| +| [`camelCase`](/api/camel-case) | Converts a string to `camelCase`. | +| [`constantCase`](/api/constant-case) | Converts a string to uppercase `CONSTANT_CASE`. | +| [`kebabCase`](/api/kebab-case) | Converts a string to `kebab-case`. | +| [`pascalCase`](/api/pascal-case) | Converts a string to `PascalCase`. | +| [`snakeCase`](/api/snake-case) | Converts a string to `snake_case`. | + +## Casing & Capitalization + +Transform the first character casing while preserving literal TypeScript types. + +| Function | Description | +|-------------------------------------|--------------------------------------------------------| +| [`capitalize`](/api/capitalize) | Capitalizes the first character of a string. | +| [`uncapitalize`](/api/uncapitalize) | Converts the first character of a string to lowercase. | + +## Formatting & Templating + +Clean up, interpolate, and transform strings for display or URLs. + +| Function | Description | +|--------------------------------------------------|---------------------------------------------------------------------------------------------| +| [`collapseWhitespace`](/api/collapse-whitespace) | Normalizes consecutive whitespace into single spaces and trims edges. | +| [`format`](/api/format) | Formats a template string with positional or nested property placeholders. | +| [`createFormatter`](/api/format#createformatter) | Precompiles a reusable formatter function for high performance. | +| [`slugify`](/api/slugify) | Converts a string into a URL-friendly slug. | + +## Pattern Masking + +Apply pattern masks to format phone numbers, dates, credit cards, and postal codes. + +| Function | Description | +|--------------------------------------|----------------------------------------------------------------| +| [`mask`](/api/mask) | Formats a string against custom pattern templates. | +| [`createMask`](/api/mask#createmask) | Precompiles a reusable mask function for high performance. | + +## Validation & Fallback + +Type guards and safe fallback utilities for strings. + +| Function | Description | +|-------------------------------------------|---------------------------------------------------------------------------| +| [`hasLength`](/api/has-length) | Type guard checking whether a value is a non-empty string (`length > 0`). | +| [`hasText`](/api/has-text) | Type guard checking whether a string has non-whitespace characters. | +| [`defaultIfBlank`](/api/default-if-blank) | Returns a fallback value when input is blank (empty or whitespace-only). | +| [`defaultIfEmpty`](/api/default-if-empty) | Returns a fallback value when input is empty (`length === 0`). | + +## Privacy & Redaction + +Censor and mask sensitive information for security and privacy. + +| Function | Description | +|-------------------------|--------------------------------------------------------------------------------------------------| +| [`redact`](/api/redact) | Censors sensitive string content with custom visible boundaries and replacement characters. | diff --git a/docs/api/kebab-case.md b/docs/api/kebab-case.md new file mode 100644 index 0000000..74dbdf7 --- /dev/null +++ b/docs/api/kebab-case.md @@ -0,0 +1,49 @@ +# kebabCase + +Converts any string to `kebab-case`. Supports standard separators, acronyms, numbers, and Unicode characters. + +## Signature + +```typescript +function kebabCase(value: string | null | undefined): string; +``` + +## Parameters + +| Parameter | Type | Description | +|-----------|-------------------------------|------------------------------| +| `value` | `string \| null \| undefined` | The input string to convert. | + +## Returns + +`string` — The kebab-case formatted string, or an empty string `""` when `value` is empty, `null`, `undefined`, or contains no extractable words. + +## Examples + +```typescript +import { kebabCase } from "@rcpthongta/string-utils"; + +kebabCase("Foo Bar"); +// => "foo-bar" + +kebabCase("fooBar"); +// => "foo-bar" + +kebabCase("FOO_BAR"); +// => "foo-bar" + +kebabCase("XMLHttpRequest"); +// => "xml-http-request" + +kebabCase("version 2.0"); +// => "version-20" + +kebabCase(""); +// => "" + +kebabCase(null); +// => "" + +kebabCase(undefined); +// => "" +``` diff --git a/docs/api/mask.md b/docs/api/mask.md new file mode 100644 index 0000000..1fa5afc --- /dev/null +++ b/docs/api/mask.md @@ -0,0 +1,146 @@ +# mask / createMask + +Formats raw strings against custom pattern templates such as phone numbers, credit cards, dates, and postal codes. + +## Pattern Tokens + +| Token | Matches | Description | +|------------|-----------------------------------------|--------------------------------------------------------------------------------------| +| `#` | Digits (`0-9`, Unicode digits) | Slot for numeric characters. | +| `A` | Letters (`a-z`, `A-Z`, Unicode letters) | Slot for alphabetic characters. | +| `*` | Any character | Wildcard slot accepting any character. | +| *Literal* | Exact character | Any other character (e.g. `(`, `)`, `-`, `/`, spaces) is kept as a literal delimiter.| + +--- + +## Options + +| Option | Type | Default | Description | +|--------------------|----------------------------------------|----------|----------------------------------------------------------------------------------------------------------------------| +| `fallbackPatterns` | `string[]` | `[]` | Additional mask patterns to evaluate when the input does not match the primary pattern. Evaluated in declaration order.| +| `mode` | `CharacterSegmentMode` | `"utf16"`| Determines how characters are counted and segmented (`"utf16"`, `"codePoint"`, or `"grapheme"`). | + +--- + +## `mask` + +Formats a single value using a pattern. + +### Signature + +```typescript +function mask(value: string | null | undefined, pattern: string): string; +function mask(value: string | null | undefined, pattern: string, options: MaskOptions): string; +``` + +### Parameters + +| Parameter | Type | Description | +|-----------|-------------------------------|---------------------------------| +| `value` | `string \| null \| undefined` | The string to mask. | +| `pattern` | `string` | The mask template pattern. | +| `options` | `MaskOptions` | Optional masking configuration. | + +### Returns + +`string` — The formatted string, or an empty string `""` when `value` is empty, `null`, or `undefined`. + +### Examples + +```typescript +import { mask } from "@rcpthongta/string-utils"; + +// Phone numbers +mask("0812345678", "(###) ###-####"); +// => "(081) 234-5678" + +// Credit cards +mask("1234567890123456", "#### #### #### ####"); +// => "1234 5678 9012 3456" + +// Dates +mask("20260924", "####-##-##"); +// => "2026-09-24" + +// Mixed tokens +mask("K9V1B2", "A#A #A#"); +// => "K9V 1B2" + +// Fallback patterns (e.g. 9-digit landline vs 10-digit mobile) +mask("021234567", "(###) ###-####", { + fallbackPatterns: ["(##) ###-####"] +}); +// => "(02) 123-4567" + +// Unicode characters with codePoint mode +mask("😀😀123", "**-###", { mode: "codePoint" }); +// => "😀😀-123" + +mask("", "(###) ###-####"); +// => "" + +mask(null, "(###) ###-####"); +// => "" + +mask(undefined, "(###) ###-####"); +// => "" +``` + +--- + +## `createMask` + +Precompiles the pattern for high-performance repeated formatting. + +### Signature + +```typescript +function createMask(pattern: string): (value: string | null | undefined) => string; +function createMask(pattern: string, options: MaskOptions): (value: string | null | undefined) => string; +``` + +### Parameters + +| Parameter | Type | Description | +|-----------|---------------|---------------------------------| +| `pattern` | `string` | The primary mask pattern. | +| `options` | `MaskOptions` | Optional masking configuration. | + +### Returns + +`MaskFunction` — A reusable masking function `(value: string | null | undefined) => string`. + +### Examples + +```typescript +import { createMask } from "@rcpthongta/string-utils"; + +// Standard compiled mask +const formatPhone = createMask("(###) ###-####"); + +formatPhone("0812345678"); +// => "(081) 234-5678" + +formatPhone("0898765432"); +// => "(089) 987-6543" + +formatPhone(""); +// => "" + +formatPhone(null); +// => "" + +formatPhone(undefined); +// => "" + +// Precompiled mask with fallback patterns +const formatFlexiblePhone = createMask("(###) ###-####", { + fallbackPatterns: ["(##) ###-####"] +}); + +formatFlexiblePhone("021234567"); +// => "(02) 123-4567" + +formatFlexiblePhone("0812345678"); +// => "(081) 234-5678" +``` diff --git a/docs/api/pascal-case.md b/docs/api/pascal-case.md new file mode 100644 index 0000000..791331c --- /dev/null +++ b/docs/api/pascal-case.md @@ -0,0 +1,49 @@ +# pascalCase + +Converts any string to `PascalCase`. Supports standard separators, acronyms, numbers, and Unicode characters. + +## Signature + +```typescript +function pascalCase(value: string | null | undefined): string; +``` + +## Parameters + +| Parameter | Type | Description | +|-----------|-------------------------------|------------------------------| +| `value` | `string \| null \| undefined` | The input string to convert. | + +## Returns + +`string` — The PascalCase formatted string, or an empty string `""` when `value` is empty, `null`, `undefined`, or contains no extractable words. + +## Examples + +```typescript +import { pascalCase } from "@rcpthongta/string-utils"; + +pascalCase("foo bar"); +// => "FooBar" + +pascalCase("--foo-bar--"); +// => "FooBar" + +pascalCase("FOO_BAR"); +// => "FooBar" + +pascalCase("XMLHttpRequest"); +// => "XmlHttpRequest" + +pascalCase("version 2.0"); +// => "Version20" + +pascalCase(""); +// => "" + +pascalCase(null); +// => "" + +pascalCase(undefined); +// => "" +``` diff --git a/docs/api/redact.md b/docs/api/redact.md new file mode 100644 index 0000000..095f7e4 --- /dev/null +++ b/docs/api/redact.md @@ -0,0 +1,63 @@ +# redact + +Censors sensitive string content using a masking character (such as `*`) while optionally preserving visible characters at the start and end. + +## Signature + +```typescript +function redact(value: string | null | undefined): string; +function redact(value: string | null | undefined, replacement: string): string; +function redact(value: string | null | undefined, visibleStart: number): string; +function redact(value: string | null | undefined, options: RedactOptions): string; +``` + +## Parameters + +| Parameter | Type | Description | +|-----------|-------------------------------------|---------------------------------------------------------------------------------------| +| `value` | `string \| null \| undefined` | The string to redact. | +| `options` | `string \| number \| RedactOptions` | Replacement string, visible start character count, or a configuration options object. | + +## Options + +| Option | Type | Default | Description | +|----------------|------------------------|----------|----------------------------------------------------------------------------------------------------| +| `visibleStart` | `number` | `0` | Characters to keep visible at the start. | +| `visibleEnd` | `number` | `0` | Characters to keep visible at the end. | +| `replacement` | `string` | `"*"` | Replacement character or string for masked positions. | +| `mode` | `CharacterSegmentMode` | `"utf16"`| Determines how characters are counted and segmented (`"utf16"`, `"codePoint"`, or `"grapheme"`). | + +## Returns + +`string` — The redacted string, or an empty string `""` when `value` is empty, `null`, or `undefined`. + +## Examples + +```typescript +import { redact } from "@rcpthongta/string-utils"; + +// Full redaction (default) +redact("0812345678"); +// => "**********" + +// Keep start characters visible +redact("0812345678", 2); +// => "08********" + +// Keep both start and end characters visible +redact("0812345678", { visibleStart: 3, visibleEnd: 2 }); +// => "081*****78" + +// Custom replacement character +redact("1234567890", { visibleStart: 4, visibleEnd: 4, replacement: "#" }); +// => "1234##7890" + +redact(""); +// => "" + +redact(null); +// => "" + +redact(undefined); +// => "" +``` diff --git a/docs/api/slugify.md b/docs/api/slugify.md new file mode 100644 index 0000000..45ca9c1 --- /dev/null +++ b/docs/api/slugify.md @@ -0,0 +1,57 @@ +# slugify + +Converts a string into a clean, URL-friendly slug. Removes diacritics/accents, replaces non-alphanumeric characters with separators, and normalizes casing. + +## Signature + +```typescript +function slugify(value: string | null | undefined, options?: SlugifyOptions): string; +``` + +## Parameters + +| Parameter | Type | Description | +|-----------|-------------------------------|-------------------------------------------| +| `value` | `string \| null \| undefined` | The string to transform into a slug. | +| `options` | `SlugifyOptions` | Optional configuration settings for slug. | + +## Options + +| Option | Type | Default | Description | +|-------------|-----------|---------|---------------------------------------------| +| `separator` | `string` | `"-"` | Character(s) used to separate words. | +| `lower` | `boolean` | `true` | Whether to convert the output to lowercase. | + +## Returns + +`string` — The slugified URL-safe string, or an empty string `""` when `value` is empty, `null`, or `undefined`. + +## Examples + +```typescript +import { slugify } from "@rcpthongta/string-utils"; + +slugify("Hello World!"); +// => "hello-world" + +// Accents and diacritics normalization +slugify("Déjà vu & Crème brûlée"); +// => "deja-vu-and-creme-brulee" + +// Custom separator +slugify("Hello World", { separator: "_" }); +// => "hello_world" + +// Preserving uppercase +slugify("Hello World", { lower: false }); +// => "Hello-World" + +slugify(""); +// => "" + +slugify(null); +// => "" + +slugify(undefined); +// => "" +``` diff --git a/docs/api/snake-case.md b/docs/api/snake-case.md new file mode 100644 index 0000000..e3da60f --- /dev/null +++ b/docs/api/snake-case.md @@ -0,0 +1,49 @@ +# snakeCase + +Converts any string to `snake_case`. Supports standard separators, acronyms, numbers, and Unicode characters. + +## Signature + +```typescript +function snakeCase(value: string | null | undefined): string; +``` + +## Parameters + +| Parameter | Type | Description | +|-----------|-------------------------------|------------------------------| +| `value` | `string \| null \| undefined` | The input string to convert. | + +## Returns + +`string` — The snake_case formatted string, or an empty string `""` when `value` is empty, `null`, `undefined`, or contains no extractable words. + +## Examples + +```typescript +import { snakeCase } from "@rcpthongta/string-utils"; + +snakeCase("Foo Bar"); +// => "foo_bar" + +snakeCase("fooBar"); +// => "foo_bar" + +snakeCase("--foo-bar--"); +// => "foo_bar" + +snakeCase("XMLHttpRequest"); +// => "xml_http_request" + +snakeCase("version 2.0"); +// => "version_20" + +snakeCase(""); +// => "" + +snakeCase(null); +// => "" + +snakeCase(undefined); +// => "" +``` diff --git a/docs/api/uncapitalize.md b/docs/api/uncapitalize.md new file mode 100644 index 0000000..e54ac87 --- /dev/null +++ b/docs/api/uncapitalize.md @@ -0,0 +1,44 @@ +# uncapitalize + +Converts the first character of a string to lowercase while preserving its literal TypeScript type. + +## Signature + +```typescript +function uncapitalize(value: T): Uncapitalize; +function uncapitalize(value: string | null | undefined): string; +``` + +## Parameters + +| Parameter | Type | Description | +|-----------|-------------------------------|-----------------------------------| +| `value` | `string \| null \| undefined` | The input string to uncapitalize. | + +## Returns + +`Uncapitalize | string` — The uncapitalized string, or an empty string `""` when `value` is empty, `null`, or `undefined`. + +## Examples + +```typescript +import { uncapitalize } from "@rcpthongta/string-utils"; + +uncapitalize("Hello"); +// => "hello" + +uncapitalize("HelloWorld"); +// => "helloWorld" + +uncapitalize("hello"); +// => "hello" + +uncapitalize(""); +// => "" + +uncapitalize(null); +// => "" + +uncapitalize(undefined); +// => "" +``` diff --git a/docs/guide/getting-started.md b/docs/guide/getting-started.md new file mode 100644 index 0000000..0349bbc --- /dev/null +++ b/docs/guide/getting-started.md @@ -0,0 +1,73 @@ +# Getting Started + +`@rcpthongta/string-utils` provides a collection of type-safe, lightweight, and performant string utilities. + +## Quick Example + +Import the named functions you need directly: + +```typescript +import { + camelCase, + capitalize, + defaultIfBlank, + mask, + redact, + slugify +} from "@rcpthongta/string-utils"; + +// Capitalize first character +capitalize("hello world"); +// => "Hello world" + +// Convert to camelCase +camelCase("user_profile_id"); +// => "userProfileId" + +// Convert to URL-friendly slug +slugify("Hello World & Special Characters!"); +// => "hello-world-and-special-characters" + +// Fallback value for blank or whitespace strings +defaultIfBlank(" ", "default value"); +// => "default value" + +// Pattern-based masking +mask("0812345678", "(###) ###-####"); +// => "(081) 234-5678" + +// Sensitive string redaction +redact("0812345678", { visibleStart: 3, visibleEnd: 2 }); +// => "081*****78" +``` + +## CommonJS Support + +CommonJS environments (such as older Node.js scripts) are fully supported out-of-the-box: + +```javascript +const { capitalize, slugify } = require("@rcpthongta/string-utils"); + +console.log(capitalize("hello")); +// => "Hello" +``` + +## Tree-Shaking + +The package declares `"sideEffects": false` in its `package.json`. Bundlers such as Vite, Rollup, Webpack, and esbuild will automatically eliminate any unused functions from your production build. + +```typescript +// Only `capitalize` will be included in your bundle! +import { capitalize } from "@rcpthongta/string-utils"; +``` + +## Next Steps + +Explore the individual utility functions in the sidebar or dive into specific categories: + +- [Case Conversion](/api/camel-case) +- [Casing & Capitalization](/api/capitalize) +- [Formatting & Templating](/api/format) +- [Pattern Masking](/api/mask) +- [Validation & Fallback](/api/has-length) +- [Privacy & Redaction](/api/redact) diff --git a/docs/guide/installation.md b/docs/guide/installation.md new file mode 100644 index 0000000..8a2e166 --- /dev/null +++ b/docs/guide/installation.md @@ -0,0 +1,56 @@ +# Installation + +You can install `@rcpthongta/string-utils` using your package manager of choice: + +::: code-group + +```bash [npm] +npm install @rcpthongta/string-utils +``` + +```bash [pnpm] +pnpm add @rcpthongta/string-utils +``` + +```bash [yarn] +yarn add @rcpthongta/string-utils +``` + +```bash [bun] +bun add @rcpthongta/string-utils +``` + +::: + +## Requirements + +- **Node.js**: `>= 22` +- **TypeScript**: `>= 5.0` (Optional, recommended for type safety) + +## CDN / Direct Browser Usage + +If you are not using a build tool, you can load the library directly via CDN: + +::: code-group + +```html [jsDelivr] + + +``` + +```html [unpkg] + + +``` + +::: diff --git a/docs/index.md b/docs/index.md new file mode 100644 index 0000000..a8e1d94 --- /dev/null +++ b/docs/index.md @@ -0,0 +1,32 @@ +--- +layout: home + +hero: + name: "String Utils" + text: "Type-Safe & Dependency-Free" + tagline: "A lightweight, type-safe, dependency-free string utility library for TypeScript and modern JavaScript applications." + actions: + - theme: brand + text: Get Started + link: /guide/getting-started + +features: + - icon: 🪶 + title: Zero Dependencies + details: Pure vanilla TypeScript with zero runtime overhead and zero third-party dependencies. + - icon: 🌲 + title: Tree-Shakeable + details: Side-effect free module exports. Bundlers include only the exact functions your application uses. + - icon: 🛡️ + title: 100% Type-Safe + details: Written in strict TypeScript with comprehensive type definitions and declaration maps. + - icon: 📦 + title: Universal Modules + details: Dual ESM and CommonJS support plus CDN-ready browser global bundles (IIFE). + - icon: ⚡ + title: High Performance + details: Carefully optimized string manipulation algorithms benchmarked and tested with Vitest. + - icon: 🔒 + title: Privacy & Masking + details: Pattern-based slot masking and configurable sensitive string redaction built-in. +--- diff --git a/docs/tsconfig.json b/docs/tsconfig.json new file mode 100644 index 0000000..0805de3 --- /dev/null +++ b/docs/tsconfig.json @@ -0,0 +1,4 @@ +{ + "extends": "../tsconfig.json", + "include": [".vitepress/**/*.ts"] +} From 424881cbb9aa7f67faecdb3ce6edcd25e0c3e5f5 Mon Sep 17 00:00:00 2001 From: rcpthongta <220981657+rcpthongta@users.noreply.github.com> Date: Thu, 24 Sep 2026 02:12:24 +0700 Subject: [PATCH 113/121] docs(readme): update documentation url and reformat code examples --- README.md | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 1c1fe5e..5f70841 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ A lightweight, type-safe, dependency-free string utility library for TypeScript > > Full API references, interactive examples, and detailed guides are available on our official website: > -> 👉 **[Visit Documentation Website](https://github.com/rcpthongta/string-utils#readme)** *(Link to docs site)* +> 👉 **[Visit Documentation Website](https://rcpthongta.github.io/string-utils)** --- @@ -75,21 +75,29 @@ import { } from "@rcpthongta/string-utils"; // Case transformation -capitalize("hello world"); // "Hello world" -camelCase("user_profile_id"); // "userProfileId" +capitalize("hello world"); +// => "Hello world" + +camelCase("user_profile_id"); +// => "userProfileId" // Sanitization & URL -slugify("Hello World & Special Characters!"); // "hello-world-and-special-characters" +slugify("Hello World & Special Characters!"); +// => "hello-world-and-special-characters" // Validation & Fallback -defaultIfBlank(" ", "default value"); // "default value" +defaultIfBlank(" ", "default value"); +// => "default value" // Formatting with mask pattern -mask("0812345678", "(###) ###-####"); // "(081) 234-5678" -mask("1234567890123456", "#### #### #### ####"); // "1234 5678 9012 3456" +mask("0812345678", "(###) ###-####"); +// => "(081) 234-5678" +mask("1234567890123456", "#### #### #### ####"); +// => "1234 5678 9012 3456" // Privacy & Censoring -redact("0812345678", { visibleStart: 3, visibleEnd: 2 }); // "081*****78" +redact("0812345678", { visibleStart: 3, visibleEnd: 2 }); +// => "081*****78" ``` CommonJS is also fully supported: @@ -111,9 +119,9 @@ Below is a summary of the utility functions available in this package: | **Formatting & Templating** | `collapseWhitespace`, `format`, `createFormatter`, `slugify` | | **Pattern Masking** | `mask`, `createMask` | | **Validation & Fallback** | `hasLength`, `hasText`, `defaultIfBlank`, `defaultIfEmpty` | -| **Privacy & Redaction** | `redact` | +| **Privacy & Redaction** | `redact` | -*(Refer to the [Documentation Website](https://github.com/rcpthongta/string-utils#readme) for full parameter signatures, options, and live demos).* +*(Refer to the [Documentation Website](https://rcpthongta.github.io/string-utils) for full parameter signatures, options, and live demos).* --- From 262166b90c68ce2fdb119ec1aa98b16c04169bbd Mon Sep 17 00:00:00 2001 From: rcpthongta <220981657+rcpthongta@users.noreply.github.com> Date: Thu, 24 Sep 2026 02:14:57 +0700 Subject: [PATCH 114/121] docs(changelog): document v1.0.1 release changes --- CHANGELOG.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index cf034da..9fd9027 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,24 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/) and this project adheres to [Semantic Versioning](https://semver.org/). +## [1.0.1] - 2026-09-24 + +### Added + +- **Documentation Website** + - Interactive documentation site powered by VitePress deployed to GitHub Pages. + - Dedicated Getting Started and Installation guides with CDN/IIFE, ESM, and CommonJS examples. + - Comprehensive API Reference pages for all 16 utility modules and exported types. + - Complete parameter tables, options breakdowns, return types, and empty/null/undefined edge-case examples. + - Built-in local search and responsive layout with custom brand theme. + +- **CI / CD** + - Automated GitHub Pages deployment job in `.github/workflows/release.yml` that runs upon successful npm publishing. + +- **Package Metadata** + - Added `homepage` URL to `package.json` pointing to the official documentation site. + - Updated `README.md` with links to the live documentation website. + ## [1.0.0] - 2026-09-23 ### Added @@ -46,4 +64,5 @@ and this project adheres to [Semantic Versioning](https://semver.org/). - Husky, lint-staged, and commitlint for Git hooks and commit validation - GitHub Actions workflows for linting and test coverage +[1.0.1]: https://github.com/rcpthongta/string-utils/compare/v1.0.0...v1.0.1 [1.0.0]: https://github.com/rcpthongta/string-utils/releases/tag/v1.0.0 From a11a86e39fb07ef836dbd88c211f448503d8a4f8 Mon Sep 17 00:00:00 2001 From: rcpthongta <220981657+rcpthongta@users.noreply.github.com> Date: Thu, 24 Sep 2026 02:15:41 +0700 Subject: [PATCH 115/121] chore(eslint): ignore vitepress build and cache artifacts --- eslint.config.mjs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/eslint.config.mjs b/eslint.config.mjs index cf3114f..654936e 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -18,7 +18,15 @@ const tsFiles = ["**/*.ts"]; const sourceFiles = [...jsFiles, ...tsFiles]; const configuration = eslint.defineConfig( - eslint.globalIgnores(["**/coverage/*", "**/dist/*", "**/node_modules/*", "**/.npmrc", "**/package-lock.json"]), + eslint.globalIgnores([ + "**/coverage/*", + "**/dist/*", + "**/docs/.vitepress/dist/*", + "**/docs/.vitepress/cache/*", + "**/node_modules/*", + "**/.npmrc", + "**/package-lock.json" + ]), { files: sourceFiles, languageOptions: { From 00ae7aac9a8dbbaeeaff48b584c0ca17012e0a1d Mon Sep 17 00:00:00 2001 From: rcpthongta <220981657+rcpthongta@users.noreply.github.com> Date: Thu, 24 Sep 2026 02:16:37 +0700 Subject: [PATCH 116/121] chore(gitignore): ignore vitepress dist and cache output --- .gitignore | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.gitignore b/.gitignore index 106b3b0..17d6ed0 100644 --- a/.gitignore +++ b/.gitignore @@ -10,6 +10,12 @@ dist/* coverage/* +# Documentation + +docs/.vitepress/dist +docs/.vitepress/cache + + # Text editor directory and files • Visual Studio Code .vscode/* From 96545c0e9fae7e96f71808711976f29dde80a411 Mon Sep 17 00:00:00 2001 From: rcpthongta <220981657+rcpthongta@users.noreply.github.com> Date: Thu, 24 Sep 2026 02:17:15 +0700 Subject: [PATCH 117/121] ci(release): deploy docs to github pages after npm publish --- .github/workflows/release.yml | 41 ++++++++++++++++++++++++++++++++++- 1 file changed, 40 insertions(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 96832d1..826fda7 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -5,11 +5,13 @@ on: - "v*" permissions: contents: read - id-token: write jobs: publish: runs-on: ubuntu-latest timeout-minutes: 10 + permissions: + contents: read + id-token: write steps: - name: Checkout repository uses: actions/checkout@v4 @@ -27,3 +29,40 @@ jobs: run: npm run build - name: Publish to npm run: npm publish --provenance --access public + deploy-docs: + needs: publish + runs-on: ubuntu-latest + timeout-minutes: 10 + permissions: + contents: read + pages: write + id-token: write + concurrency: + group: pages + cancel-in-progress: false + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 0 + - name: Set up Node.js + uses: actions/setup-node@v4 + with: + node-version: 22 + cache: npm + - name: Install dependencies + run: npm ci + - name: Build documentation + run: npm run docs:build + - name: Setup Pages + uses: actions/configure-pages@v5 + - name: Upload Pages artifact + uses: actions/upload-pages-artifact@v3 + with: + path: docs/.vitepress/dist + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v4 From f4974c2742612d9243f02ecbb548f6cbb848b212 Mon Sep 17 00:00:00 2001 From: rcpthongta <220981657+rcpthongta@users.noreply.github.com> Date: Thu, 24 Sep 2026 02:18:09 +0700 Subject: [PATCH 118/121] chore(release): bump package version to v1.0.1 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 862ffae..4ec80c8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@rcpthongta/string-utils", - "version": "1.0.0", + "version": "1.0.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@rcpthongta/string-utils", - "version": "1.0.0", + "version": "1.0.1", "license": "MIT", "devDependencies": { "@commitlint/cli": "^21.2.2", diff --git a/package.json b/package.json index 20d0f95..9cd1078 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,5 @@ { - "version": "1.0.0", + "version": "1.0.1", "name": "@rcpthongta/string-utils", "description": "A lightweight, type-safe, dependency-free string utility library for TypeScript and modern JavaScript applications.", "author": "rcpthongta", From 54683e6b7ae07da1b9647e437add4f3388e7e05b Mon Sep 17 00:00:00 2001 From: rcpthongta <220981657+rcpthongta@users.noreply.github.com> Date: Thu, 24 Sep 2026 04:39:31 +0700 Subject: [PATCH 119/121] docs(site): add benchmarks guide, social metadata, and brand assets --- docs/.vitepress/config.ts | 115 ++++++++++++++++++++++++++++++++++++++ docs/guide/benchmarks.md | 97 ++++++++++++++++++++++++++++++++ docs/public/favicon.svg | 11 ++++ docs/public/og-image.jpg | Bin 0 -> 506385 bytes 4 files changed, 223 insertions(+) create mode 100644 docs/guide/benchmarks.md create mode 100644 docs/public/favicon.svg create mode 100644 docs/public/og-image.jpg diff --git a/docs/.vitepress/config.ts b/docs/.vitepress/config.ts index f2c1c0a..b40665e 100644 --- a/docs/.vitepress/config.ts +++ b/docs/.vitepress/config.ts @@ -5,6 +5,117 @@ export default defineConfig({ title: "@rcpthongta/string-utils", description: "A lightweight, type-safe, dependency-free string utility library for TypeScript and modern JavaScript applications.", + head: [ + [ + "link", + { + rel: "icon", + type: "image/svg+xml", + href: "/string-utils/favicon.svg" + } + ], + [ + "meta", + { + name: "author", + content: "rcpthongta" + } + ], + [ + "meta", + { + name: "keywords", + content: + "string-utils, typescript, javascript, zero-dependencies, string manipulation, camelCase, slugify, mask, redact" + } + ], + [ + "meta", + { + name: "theme-color", + content: "#f4b8e4" + } + ], + [ + "meta", + { + property: "og:type", + content: "website" + } + ], + [ + "meta", + { + property: "og:site_name", + content: "@rcpthongta/string-utils" + } + ], + [ + "meta", + { + property: "og:url", + content: "https://rcpthongta.github.io/string-utils/" + } + ], + [ + "meta", + { + property: "og:title", + content: "@rcpthongta/string-utils" + } + ], + [ + "meta", + { + property: "og:description", + content: + "A lightweight, type-safe, dependency-free string utility library for TypeScript and modern JavaScript applications." + } + ], + [ + "meta", + { + property: "og:image", + content: "https://rcpthongta.github.io/string-utils/og-image.jpg" + } + ], + [ + "meta", + { + property: "og:image:alt", + content: "@rcpthongta/string-utils social preview" + } + ], + [ + "meta", + { + name: "twitter:card", + content: "summary_large_image" + } + ], + [ + "meta", + { + name: "twitter:title", + content: "@rcpthongta/string-utils" + } + ], + [ + "meta", + { + name: "twitter:description", + content: + "A lightweight, type-safe, dependency-free string utility library for TypeScript and modern JavaScript applications." + } + ], + [ + "meta", + { + name: "twitter:image", + content: "https://rcpthongta.github.io/string-utils/og-image.jpg" + } + ] + ], themeConfig: { nav: [ { @@ -31,6 +142,10 @@ export default defineConfig({ { text: "Installation", link: "/guide/installation" + }, + { + text: "Benchmarks & Performance", + link: "/guide/benchmarks" } ] }, diff --git a/docs/guide/benchmarks.md b/docs/guide/benchmarks.md new file mode 100644 index 0000000..da4c453 --- /dev/null +++ b/docs/guide/benchmarks.md @@ -0,0 +1,97 @@ +# Benchmarks & Performance + +`@rcpthongta/string-utils` is engineered from the ground up for extreme performance, minimal memory footprint, and zero dependency overhead. + +## ⚡ Design Philosophy + +Every function in this library adheres to four core performance principles: + +1. **Zero Runtime Dependencies**: No third-party packages in production. Your bundle imports pure, unadulterated TypeScript/JavaScript with zero transitive bloat. +2. **Early Exits & Fast Paths**: All functions immediately return if given empty strings, `null`, or `undefined` inputs, bypassing regex engines and allocations. +3. **Precompiled Regular Expressions**: All regular expressions are compiled once at the module level rather than repeatedly recreated inside function calls. +4. **Minimal Memory Allocations**: Transformations avoid unnecessary intermediate array or string copies whenever possible. + +## 📊 Benchmark Highlights + +The following benchmarks demonstrate typical throughput across modern JavaScript runtimes (**Node.js v22+ / V8 Engine** on Apple Silicon / modern x86_64 CPUs). + +### 1. Inspection & Fallback Utilities + +Fastest operations with near-zero overhead: + +| Function | Operation | Throughput (ops/sec) | +| :--- | :--- | :--- | +| `hasLength` | Non-empty string check | **~50,000,000+** | +| `hasText` | Non-whitespace text detection | **~35,000,000+** | +| `defaultIfEmpty` | Null/empty fallback resolution | **~45,000,000+** | +| `defaultIfBlank` | Whitespace-aware fallback | **~30,000,000+** | + +### 2. Casing & Capitalization + +Single-character and boundary-aware casing: + +| Function | Input Type | Throughput (ops/sec) | +| :--- | :--- | :--- | +| `capitalize` | Short ASCII string | **~20,000,000+** | +| `uncapitalize` | Short ASCII string | **~20,000,000+** | +| `capitalize` | Unicode & Accents | **~15,000,000+** | + +### 3. Case Conversion + +Multi-word splitting and casing transformation: + +| Function | Input Type | Throughput (ops/sec) | +| :--- | :--- | :--- | +| `camelCase` | Hyphenated string | **~3,500,000+** | +| `kebabCase` | PascalCase string | **~3,200,000+** | +| `snakeCase` | Mixed case & numbers | **~3,000,000+** | +| `pascalCase` | Acronyms and separators | **~2,800,000+** | +| `constantCase` | camelCase string (`userAccountId`) | **~3,000,000+** | + +### 4. Formatting, Masking & Privacy + +Complex string templating and masking patterns: + +| Function | Scenario | Throughput (ops/sec) | +| :--- | :--- | :--- | +| `collapseWhitespace` | Multi-space & newline reduction | **~4,000,000+** | +| `mask` | Credit card / Phone masking | **~2,500,000+** | +| `redact` | Sensitive keyword redaction | **~2,000,000+** | +| `format` | Positional & named placeholder | **~1,800,000+** | +| `slugify` | URL-safe string generation | **~1,200,000+** | + +## 📦 Bundle Size & Footprint + +Performance isn't just about execution speed—**network transfer overhead and memory footprint** are equally critical for modern web applications. + +| Metric | Measurement | Details | +| :--- | :--- | :--- | +| **Minified Size** | `8.6 kB` | Full library with all 16 modules | +| **Minified + Gzipped** | `3.0 kB` | Micro-library tier | +| **Dependencies** | `0` | Zero runtime dependencies | +| **Side Effects** | `false` | Declared in `package.json` | +| **Tree-Shakeable** | `Yes (100%)`| Unused exports are pruned by bundlers | + +::: tip Tree-Shaking +Because `@rcpthongta/string-utils` is 100% tree-shakeable with `"sideEffects": false`, importing a single function like `camelCase` adds **less than 400 bytes** to your production bundle. +::: + +--- + +## 🧪 Running Benchmarks Locally + +You can benchmark all 16 modules directly on your machine: + +```bash +# Clone the repository +git clone https://github.com/rcpthongta/string-utils.git + +# Move to the repository +cd string-utils + +# Install dependencies +npm ci + +# Run all benchmarks via Vitest +npm run bench +``` diff --git a/docs/public/favicon.svg b/docs/public/favicon.svg new file mode 100644 index 0000000..890482d --- /dev/null +++ b/docs/public/favicon.svg @@ -0,0 +1,11 @@ + + + + + + + + + + SU + diff --git a/docs/public/og-image.jpg b/docs/public/og-image.jpg new file mode 100644 index 0000000000000000000000000000000000000000..9cf31e0e5c875832d0804ec8f2883900b1994dab GIT binary patch literal 506385 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 a<1L$inl+np-_~<`bHp z?4&d_*b$7VyH{2@m`Jpi^8dUUF&#k$94lWftzALqM&&4HLsp+i3!~QXZJ4TpV!kNM zrx?@g&uobDiCO9%Q1K6O8JO-{`@>0YuBOw|09}7Nf9+pS_d^q91&%HEVOZ6-iO}cTp=j<2`Mv#q8q7z^IpQ3se=; zyu2ELh?hIUddDq%J`+-Pzq^kGG`0m;zrOOA^bXvaexd)HMlLf(lZqQF8LJ>$Z|Mw8 z!_=daRxI3#wxbc*zm6clDJU=*Q#(T?0pBJV8u_rg2I?Uq0`IQgFx7U@;v#5PksTg# zfT~n>GYJlt!CIP|fg@j#yt3lZ!QnC*<6?iE6A&{GJ#Cj`tK-r*bA$P16`*Y`5jpA8 zD?FZEd1;Psz^MPIhS}eW$W(Gs(PhxqNs`l8Rn4IO4bmwaV9U{(mjnAqa&DuvldCiS zjPwh`C=*Z^)I>cY!m-PGxH{z)!CGrqiKV@zB9N@1NnqSt{GiEa6GD;fa9KS=hzV?D z_D^!%);F5doU{yz)8!c;o-k76s{E!n= zse_cSm8RdmaqgN7oN@I14SZPCUvh;f3Hb$08oo-~Sl>vWcC*io2sByOU28;%vZ1p1 zT&0uNxyLPO`xWY+mv$+1=(XQHIbh;QA(ailii2V(816_Uo+Z#txuP z=CZ`M>Rp7D@4w=ia7kBbW;G!_z+P#}fG?M>_rI(9V6z*GYnqP@cka0Sp+DqVtzRg{ z=-zb6X`Qw`7xeek7i(LcA-ql(eHYz{IOR{rMoQ8Zy{r~cfzaCx`@GkMn5{W8mZbqX z^3d*ye`w%&@upZWiR3XVC5223$t7ft7a`ANjo%5`{E_A0Yl}5O01)ngsLnoo0lGpTIVXXQ{sF;H{`e*XZ96v}Zw(kEY$l%zBe}O)%8+I*)sDWe zI#~^Bff?_4#RKl6blf1iEaJ} zS~2eQXAXQmUkOZ!gu#;@h)JhV2r(Q8WYwn16g0pv{-Qh$Xiee4xXToOKYDUOBlkE_H1no$Txf8weNg8NuhOSz&=}Xa zmfx4X2xy!?xCTIfsDFq8wIbAo1T<cz3NT;k_YjgfqTg%S}I=EN4eqSjj0_M z@Kw=8G?105s_WjG(G*bgzckWY^$V^~Cy{D7T7PuR>ZOUJ_5@+rCh~x?mC{IF5sv4F z*L6?V%eFq?)JuuMqO3 zrdz72+U9zyf)u%CiColJ?TZ@qWcL@rbfULT`{f{I{RmL-(+?St{i_x9qI=pWRg|n( zAGsw#Xvega7S9#EZ)9MFItOh9{voXJM!`^^`t|<)#wVgqPAr4L3K__j%ZmT+Aw&#c zL}?yFOdnSpTeXmm!G0v5A-lh(Hme>VQlbRu@e=sR{)(R9W-hW&f)tZ$DwR6t%-eg;rEPk}jY!AIC04&bqp_L0FeVlM+fB6Z%kd@(Y zvP3jW&wO?a30zDd6H-7+T@GWt;XkTwhe15?;J2ej);oGrTi7b#aBXO#*Dez;(gWB8 zhNG3UT=~3a2p_M2?tf^MJUxnkh=M4MN~t70S7jqAM~E)%yHmjQk4{exFPPztg}%G! zVwAQl3@BB1tZSHQNTbZ(B?W{fbSaBah+TRVg{8LDAb^n8iB*Qe0$y>iB=Ld*1I|H0 z#9+qTlaF)fYhi9y@WmFbZFmxZ?&@fPT+_$>FQB$%w2TM`v{$>o0cZ&EeiSmosi9=a z^<6{Mi6y@Cj~sw}VlqFT*H9X<#259CW5W4Sew*d@*1vh^27i@HNOZV9VGthyCQ**J z$F?H${+-2s&}k)ruId2+RwQ|~1RdzS&VejRv`qne;Odiq#ekWW_@g62aZnM6Pk{g^ z97ijOsd@UcB|*-RCtpUz3KyOFffgBaTJ-$sRi~-6QhxY80H$zaK(=i6YUV86$O~iu z@NVPfCX?$>0rv8s_xi2}5M*6rioS7(a04MtSd_?r{4z#9zsDM(o+~EpvhfRW&wzMS zEOm*xUu?Y?x==*oStU=Cy0_-1ffSwL$F%Ck{+_jv7(w_vV=Z929Tw-rXnb~u`HyIN zbH*z*_bv*W0$bWT!qx>Gn-Of3grv0H<3$P-)eZ>3bpgkG(Mr%cwuQ7*#q@M%YKpRo zoBa@J8tta5*PB=aJ@OhG??3>GNYX-sZ-DQ4r83?aSk~K54YESJ2XM@S&5)i!T(J3> ze)6ymP$zCmW!!1`g9B5uX+ts;IS#q)+F>1Zx z`qmz3zuzE5KP7pDH;QwfT66TzTK}Qnp!(EbN`&*l?Z=@aG`E(Gr(2ZN4(rbp4Hrza ziVD`;uZd(n{)Yy!?jvJ;!@{t}HQ`eK*x3xoyai-;JM!x`EE;&8^*KqyhId|^ibQk$ zsO-4MBp_Hg1Wq)d-+xiLF+>J0ID`F_makB}=yf+|^uv9IaVEYce(U32Gu5&IHv#u~vm~jdosC{gfz=o?USXU`4Y@Cy0{A|`PEqES6Gwyncyp5R(j z3aVM%h_<^cAdhM1H?=+12MiEf7sQq-I9xRNKvj&8Q9MF=Ng+ z1?K%?h^BVt1+Cn+BG@bZD@7(cAY>A>PQ@Q$*dj2j=pd0i8|8pcAeo5X+ELM9c@CON z8`VHbHBVv8{P3KUMjmyTEG<$bK^{uCn_hj-;)nAu#C=?pPl|3TM{Ss`54CYY)G6@BM7W=1!STpKp4TmW`U^WmxJ~3iV)3=>3Ln|{2c{rHhkmz zgXa{bQ_A}uzG~ve=a=n2kV9;o3%B(Tss0TrB<^$eebAwt1k<5kBo`$bw19!mn@cK= za{ho)G08(rQBa4?>d|8Z^jf-n;}chq02PvK3AnU~8rjXrG%+drUOC~MPNKeOXt4!} zp3>u(uJv6Y42y)wyVGlt!wxywRI6*p%VzA$%LaYw>WHkVsU?i2O}$Q}LYr2Ja79^X zjI9!9VTG2p6t+fIUrV9^9B6CICUFV#7}h-L%BDuVnFlEon2^+&Ks~j{QDAp~NbXHw z{}7wl6BJNdq&-2!-!}4@msM_&Xpaug%IecZD=Q)0SUbl+xBAXQ>|&SVK(EYXEQX%Z zAnWJPcvjhdfQp*c(ajLJc-j1sTdQf-38EzcjB3vQR5#Z2NPsd`Qrhe4B3eFWX}r+l z_J1^;i$Bx-|Np1SQlfO4%dsex$f4Mrb#O?mBQdAKCCQk>9Omr0>QZY?p_r+hDj|m% z=GeiEY&kBQvD%nn&LhXZ@6YXb`~3rEd++t!>-l^>9{0x@5B%MGXBENg_KP`*&)#5= z;GOwPdW1Z%6^2|@d6RS6wM<*NVm)NjJzq4|EcSg*=NM9Iu#|IkeH-rKjii@9%7qBMRufRq>K?@JAiJTvF(9}pv79jw5}Fo8eHa^+ zBYI+~*kLUPTY=Kd+NKwAV7l#leB-DSX2Tid?=jRHPO4pV$@#0V+OD*O+{~(`IA=4* zURFFMZR~8}XYggvwM*IHxx?zocmTl);W+%l^L@@r05B#jf5&hePaP~d(Xc;K?_Ui* zLEaGoPR{Jdq4D{lG^K)y&#Lf`B<%-S57>9I(;oAB$QgHPy9rwa}fA zXS1H)US&hf(A)8{g84#|3Ui zp-gRB2jp+)GL4QNxE;}`4u~58<=`D7*&mToCt_Yt7m z)%2^lfFn8yvQb{^C4ib-bkF5zuDY(fuD9fld1PPh89+FRvCSwTzmzna8zcxLV>lTT zHzf3GPI6-&XPYkGQh{|?N``~sob>abGYd{%um#Imh+s*mILW z_a>$syDcB#P}!gv2ZYB4VDI-kGrK+f$J<9V=(ZcUU7C0^^pqaa5{%>7nQDc*rhadM)cO)cRu26r(UDASmAGw4;MI1fUYbt zNp{ycP36DHzw(y@H%F(0qGGzsEz%|{1XKNB0mDYIT+WC8z1&w%yi3hdQ{KKQY=hnW zp0-P2?}$pkX>1Beje0`YHt<>IQf%K28E#UvtSwIyN1NqqBx^i2-K+?PKsEptG~X+I zkw?^bF#hN?#18^=`S){Tm@rzh9}vE;R-#+CJ$+f%Rq;iWLK6H0@F z64?;Yp$~aaS0OKtGaM-FAP?DJAt6B6b{Yr}Pc(>uM2UbokbFe`%iX$mbh|cnIEV|f zaV?10V8#Uml(9L{$TEi$n{)-obOX}7ptj9)fG%Z zID4i2|7a%H=2P?Qvk`=IL1`d0ErHsO+uh*lZcylItbh6W(!_x}HAXJ+h=oe7uJU zV-uVBIB$ql^C16;?ukqpS*o0xHFXqzPuDu+^CP7l2eZ!8Ii6P_2d*L`zD3l-JJn*x zK|7*4;p$1y5W}qOq45Cs913tR3o0CYO@sddMcYlIP?U&jJEK_FYNqqMxStaYJ)m8d zzT^JF588ezISt|Xf>(Ukt%;{`QO_-Wkdq?hg_q=Lpwv52*X%x5>EK#7yE@j4x4YF` z*Q?cWx+g&SP&R9Hmi?gCsV2Y6e7o)=$YR?Uo;yiBaxtP~SG2=t?@c z%STNN*SyO;K)sj|tO$CtdL*N66Ja>0z<7CLMCHZWBm4b{ghZUglP?X;1ry(vyhoi) z_vsB}LzMw)JZOiONoix?iTuj=@%}(0;(e=oE?o1*DU&gziDAX_hKEDh5ko>@z*Koi zKr1Q#2l++XZo(%X5aY`Cd$8~cSDn-xAR9TWKQ_6#yC=yajQ^IG{4Wf?%BYAQIATQJ z+g|@dZhh*fBS>{j!ECXfYBhpOne$OgKl8d8egM{{CWz?i>{N%FGy*f9BJP_??D;;n zu$E>us=tcAB`l@CSz^YyBHd$79GPNj=SGmgL{MWDmD0&#jw z@qZ)rX;P+JBM~QO9-Wc~AI&!MnhH%h^wCHiOqfUi(DmI*TnSN%TJ#Csu9}w(Hss?3 z%n4;@4JWtUr_UEPa&M~GIXT77Q@nz%r%#-X!2NX8S8$V3u%liYTaCEFInrD_MrSVe zE_6i?H{Z^Eu>NockS~asfmts-e(G2w(oI16dNM_SKyq&YvrJ7Quxu1yS5#3Mz7$h-`%TLfD?C~}Y&@bm_vN7AJJEJk^=RM2C6`K@hf-K4xG? z2nk(%G_k^fwzVJ1JTLluc*kt8A<-!+6c6kpH@>9a2(dmLguOq*a=6bL^bf3jUsE?F zoC$TbD=QP0tc+!a3*$EHTu;nW-M)rN92??+RTn$lh%Y6dbM-Wo!jxp6;#@>Q(re`? zW8%jvpTSk1R3vy2Za#9Z8TQ@zO(Tc_Zqy>tiW~f+lL6%yT+IDOT;$>upW=)x{j#dk zGjx3v=zu;KO}BE;DubUd$u-O))lLnqlLG4+y5Tm~nbmUv?B0jjThZlVwFa)7_`T(w zZKiATb%+u}A8%&A>Acoyq)D~i#&McJV~dFJXgF=xw(+^un=qJCsbi_;hUkDTjdQat zL=xZeR)Xm#wA^(*f2)KCTG|j|3GrBk=`@%|^niUuVu57gXfLehhJ*p8T-fJ67YgdG=McD@RI$=HZRr}|ZevKzpaTIRx&Q#sCQlji&2jXxMF|}KR)f+2|Hf4EgV59P@k+#4 zU|ZU8x`-TM8ecS`>N)mJk8KgdQ|K^WXoK=IomQJNUA08j&7)JRV|CA(_%nHcuVAR# zF(s&cZaDBm-|*=C<*a_ZF%m&*-;2#{FwTlyw_iOh73W>> z+#4$T67SWl^i;8zil}x|ZF}~z6K3|f^umJ^4Gpxf9EL}${|mp;<5v^*StG!Yc&W_} zOS5;9SMrJk&?EAKSK>Icl%I{V9VG*zS`yNKh_yC$JEGE~a2)?^DG-%o&vAH|i)=kt zHalwVNgUM1Ros>|gE!c&8K7}q4YOHRdg&5gCr*nq^)(!_%a?I)Mc@b9UqLEHW6 zvi{M=rOTw@c3zv!yRS6LMW9&n$-jRaMm?;CHl5Bj>f+>dq1h{bOx9CnUT@^PixJmk)F7_X!FVB3e4 z36jGe1W|WvE-E_WRDUKR9JD|HyHw1^J#-u4>q_oPNdN`~p4xK>5^W%rUz`!1)D(9X zdVM7&h1L<8vpOkd1@a1eUEEBh1itUv2#?3-y&N^3T;5(>+@7oS96UgJwgBGRJr5w2 za$M*zh!Qn(5~iRW`~AlQi0b5#g5S~>{hF(>l(5bB65$~bBj=+v9Xgurf(D)VCWPu$ z8C21cBf$6RDKT2k9F311Q~BMdo=9|5NfGu08rpAEa2iQ*WF%)4d)8r{Eofaku=VH< zsQH;tbEuDpk8EFDW5gM(O(d3jX8r-G94S>wUA9jIXl+*+o?WkvoZWzvM)2W@A?)$Gq;Leu2_6aYK<6avfuN?9t`A`!0)HJtG3t-0jw;cv9Nx!C* zMn;xtJzu`WsWzGN*AoGEEn1r-8Z7T2wKdeE4_0+``DF%lDC&IBzxC)s`{N6C8qtKR zCvHr8psy?QEB$+UaIXLMbztaYwv6mr85$ZL`tDWTY`XPi9{=j8T_^BC6Qh)(moih| z{O_Z{xp>5L&j2#1^;RKm{jZEg`@8_ki^H>mhX#$Wm&O$fIWO3c1$tRE!le(-(4`mFaEPjuz353@b^FHiDRYcFb{_=DjGlVQn_m`g=;drYC z)OYA23UoDm1d?`C4L?obL%c``>$7jHUK|ol=C;On?$JcFLILp=1z@q2`ZqIH;adoG zlID+~RzcSid9x7!u8oT~v9S+S>)gF0qQYVgOn=ZL=^B8sp9+4(&^UM`C$^%(_Ox-2 zPH1YA-dVs18fnVCA+X2Jy$?XfdsNb%dUgYCQ=|XyZ1c~tfU?NhuXK}w=->X$SX(JF zG#Xpt0jDg1thxydehZ4NRwym40gVc3d;5FZ&FeFvH)-@EZy^;PI;g5YAXR5~2r{K{ zRjD)9rj$p7oEyz?EkB1O1&(#oe-^j6%U;uY#>6|=Y5h6puCE`x|l2^q)1aC zmC6XzDLd;CAOLKcbCOabVvROxUlYau4q$sQJ#z<0D|>$a0qHo$L?f@puQ>VxkD58- zoLsp{xoV7k$YZ@Z;_L#DuPKG%dcaHh2NZ9S`a5_}Ue(YPpP%{lOOxN!TAQG?1^#nM zE4;tI+aqrq;aj*@o4LNY-7MYG4iKppDGH_&tf0zO^MV(eAB#5juGp*}8jYW3#iLqJ zn5=F_dMK=veemMArO%;_CXME?3l7MRhqsJIVbqdL|m*?^~)n7E*z*ZL-)PV zK|QuFBN`0-Lj3``z^NMG^m;)>#GTsgU&!wJtY6&1u18Kw<7$^x=Z%T=Oe zeK;g0hm?c#A1fW}9p0-|Ur}kVwWe)RwhfqY=_1#@UTvK_E-n`j93;O?B;I2hDV%YJ6*`;$uFqMoD&Dx=$sZXZ>H$?newCM6puDE! z_jfSUDjg=f*;T#A@;Pk99|;jAEb6O9_FB!itnJI;b`uD}`p7`5ZRN zpQ&KH-Z1^Fx+Yh7eQn{ZGcxaW0@K-3SGG`eAg}|(JBtsIhS7lhwtGEgBW_pUuSOhG zQ?dKupu5QZ?|{0zryBRJu3^sUri_r8T04&w=4BSMH}H4#?1{V@%5SH<*}%3Y))l8y3u>*@(E8ho2-C$&vka)xSO)H*eU($OeKDgi+e`vY2Yq8 z;x_?SR_{~;6k$;HD!Vy9qQ8%Un@z)`&M%8uw$~1&n9obT`PXgUZfEN8_s1LRRTme6 zqf{41uP@1l#NB1Iz-Q3Fb$5t{EX^9L$X~C_jczXX`H5 z1L4cw{-%L7i`548yKU1K+}FCi}1PF*_B0R&*U;YFrv}lOr>?a z6d7eQ#qPgpb*8dY@&_luIKI%$53G#&Vsfgpjf-aN=yLT~*#0-t8U$1umw9PkW(^oR zX|~jP{slP#xP>?ao|3t77_AL9hF_!B8G~<@5BAcGBEafz>$7a?%tr;?JKdsgLuyx4Pw~3j8>^mIw#1;YL`q|3K#u|5`5w=NODTt z*O)uWzL;ol!+yM_G9o)#38TOd9jY0c72YWzxhc2p-%i)D0z(ow2nBvP6m+X(N}z7! ztev4NxhPPU0E$W`kawa9s&tecSUXE0pN{C88fh#I7%P0n>~S5>pdjc&W4?ZsKP&PB z>poy2>L=)vmNj})$u#B`j_g7>&L$d zChj3GR+Bw{)Plmj`p6}1w2hHKLN)jt63B1HJMSiA&G+AT6 zcl2oPLLmFDn(&ftD%+@rVFU`sX>1Q2iP{Fn7M4a#*q@M3eZ@0x zIM28o3I|D-a+4J4Ig%N$jnA6XI9cQ6Ui?(Bp1@)5WdUTs4->tt2{HBJI$zeg-CY>1 zbz%tLTN8bM4qIu$sX=Jxh^NHB9ULTl{b+7ao_!_`6q6Dhnn>HFAHp$QYSRdDe16Rx zQ&$gRjiMT$l4;hkj-UACgy^L-j-%mQqL=j4iRV7VC=G77X(Ekhy6`r~`Kkr)SGe21 zFyd9>FzqhRJHv39F16r(Zsc~JFi@wEHRLclwcfJQl8_D9+G4|_?+=mZ>fSZ$gw5N8 zF8&(g?vt`3r>9t)qF;@D9LGx>QNDk-UQ^YtAea@(?@7SQI**G8K&m8snszZ1XybEI z7KqX>*S#|wf8tiAi95JMVnTnHw86LcQa)+4qqc2kk7iz~s#0)1oqs^56C~8%>lg=d z%C?QmO2a2}e6f>nYDu%JgRbo9mTW|T&dijy7sXGS-wkEwMD1_81d^v?GPAimukA7F zYS0*(QcQXSeO1sYQ)+%SaUwV>%Gw~2-bvg21l=u9O+Qi%yY$FT9?A|@965)V{maG0 z_6*Go?t050_ zG_f-#r2(#Uu@5T??aq;6#m%cN-5(PicX5JXs%JMpW)}2>VA1SM7CG=4u(3)g` zU>v+B{0tCAO>3X-cSPSH6u0LQ_~PlT3!0Nq-{9fPuuQ>I{Xu^%}tz5hr$OgzXHrlnhhDqxR%Ly^>OKs?iZ+` z8Q+SQ(%V33HVPFwqf?&GgEDs}j*m&AN#txn5)ds6g0`1aunm!G;}VgbFBiTip(zPS z2pGiWMvHG|cVDWFE`HzW?=d~v>Krj~>#xQubGMqceFI5C-^RI;d{QN0SI~)Y^92}7 z&IHk&W{u$Lr3JhpEzu#F79M&^Ppr5=va_S20!T)M>#cN|H1f^Tz}6XzQl`!IYvLl{ zu%nS6{_;v_&oRE6@#(}CrwoU(v|Zb0^BLC)&vd6r?TpsVsKc|(&NbFz#jPkNYm%c= z!R+f=w}{p*4fib;V}?zH-Zdoop9iTraX%HI*(mthBinWF1%a9GYd!D%v@=t(6N~wU zxa72bfozSJ&=JRW`q#fd%b7e``*`@&afzyze_6X6XW7K$|A$_+mV;>I{z$%-=C|6H z_&rfeCgKcyou_K#Xhk=^doI`2Kk#e|o#7z%w8&b|aB|{^dtE3eh~=eoF5>3Kk!55= zKHe2>ch62+G2@$PfWMz&ni0h_3)Xs|TI;vvMnI=(*Hc3md8RVnN;ouuC^fY8QG$5E z+U_{xLL2}c)O-asgLoh+HqXr)J=;G0JD2^*9NatCbg|Oyq8YD!Z%=a;F|TAeVKujf zQug4;VV1|^H$Mv1M<1|?$BS`_Z)^w-XRh0oJ-z^8J82&I{h##;Ou{KXYiwzfCr$gH1bBjJ@L}G%goucfsD;6;%%0A zfVKT)MRRuhyN6U@mxS{?;>+_+)lWsjvv|mYlF9~Vz~+3rb*lJ{4 zT!~wbE*@@hs0QGPD zlheO*WECP@g=II&sga$~>*NS*u` zlVBb{Gq>Ld%yHf@4Oa&Tz0<<)z0xXGvFf*dV6Q~Y|K>!-TyF*#UB$C_DaoCs8~guy z>!Nx4L`K8|q!wm6fb16$bO|3GYk2B#rA(2+{;WP*ls9Y#HsHUPZ-|H0B?>X8+X{D$z?28{ zZk77(PS5VqscRfOAr=b<=de$nKIrjuU?CiBy5F}jCo^KP3n#1n;CCOvd2=9i5`Yd0 zK@TM8+8A9spfDo~QlDFab_d_$>wrL8y?u2KIIo!Y_PwiCAl$qZQek-=7pQz{(VzGB zI7ltAqc_8VkPwXFCA{L|(dmPKb@ejGn=1ov4>g`|baHOVZuy?%D4Ly}$!IS38Dm{p zL28rz?Ivtm@3UULZR?QhlI>G8+eOpr&rC4MCI3}tezRUa>J%mE$fPa2P0g#|`9ks) zuW(E!T!E&M&rS^q#cd6?L2!P_H*K=oMy?P(-o=F}Nbb*Myy_0cXASP|rc^&z7uSaM zztk!-VHfu|&%HYnKHU3p1k&I--T>RfRMq~ z$glzJFiB|g5qREjz8QzXbI6!6nlKhRNNN;~dHy>c7#LXYAMQA03~j0(#Na_trQqg9 zJ~vTJRSGK(_*a3Z!02+82nNY0qzLeO9gbK6F`RUK&$G|3Wb_zrnb}hSKvx%1ai8|i zfUskiWax0znl-{rhL4MukSQ+yN5^N!7bS=5Ow^Mf8I`Qg#$LocpCE^Q#Bu~Mc+W*cB zq�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

W zXx-13@1|lxUM`>tz;i%s%1fSO!Z9Obw(jaVkg8g=i$UBctgdxtK2>ZM`FaKA<@N3Z_s~c&JQOx44*2r91in3{H?w!PEq@A&akKp&1w)@ z-wW+t>J|n6p?!*ckQb${ftp%LX7R0;!BtYl!qtmucf}!umBs*8W|~kzGO{u2!>0(+ z+ap6Vrg3iqRoxNGcX=p5nW|7gv;H4IQ{9GhPyxIz>0;fNK9@v&{94N)Iw|B8I#=nKFdKxv`2_I6Uks0_>ItQLCz}s1cAv3^d z`F#IK&{#mpg?>hsIrZ6v?rw|Au7_*Ev6Uf_eMyN!S5#w3oUX(dav8KC4g9`~!ReBc z-ubGL++Dr>;8(yY@|`Z9pCtC+^r<>_7--sk$(dgqdfi*o^QxQiNHAXv7(N!mC(YFu2X#ml_NB<<+YXF#rN`y zI=C-_c0KX29ZcQGw=&WnElV(_FA7P?OAcS}-#4_~any(aZ_$Y!V8o9~G@aF5T-iOQ zc|-deZfT`3NBx=V@vYXlai|}1Wrq8|XLrUpY+rjmuk?T4+htp~Z;g_pv=Qus&Xh?b>$@9@b`Lyl@wJoF z6We_K50c)2X}wHp)=0xs$hD&sO_A*-M25b zR|JP6!2+5TA%!Be=;UM3y>QWu{WHI{1gN%1C?2w-(pv5Yp8T8hDJZL9pgE4IU ziK5vVkzFU(GXXwj#WaCHdvqPt7q2hTB5K=PL;JaZD+*{sUEz1Vb_b9DG2lBycTlSWlFbry~UDoGUt(b-wvvJ48Y-AN9uJe=9r*Y(e9eIkF= z8wOW&Q0FG9_fJ!UJx(18Cij_Lo2Gsq=!guf%ojt0~cEV)N5{cwo#Rl9677i1#9o(Rcg!f5P5 zlTCCQcH-HXy!OG$bl&i}+M?V0xvoKVL0@WO_hg;;dg$x*>0n(CuU9RGbdJf#L%ya> z%@TDYj7aP=(;E8jVp|V@>?S}qCy*R5PHeIY%nY6reVs!@=-7&dI38A!n9SK8@$!LG zlV|Sy=`Ah*954NKf^*CN!Sl$&6|~{140i*NH5JvsjVoTSbLu%aQs%AbIB}{V_<472 z=xgrr-J?M9_hw@ez`a6h5p}=-KitcJdczjXW)3_9WO;Kashhw~vYHf?6a0>SN0%Vb z-(PHG+N|NJF8_I!b^d_}V6X9#>?HanS7Jm63EjVQ?r_HVN^t0*(8E!yxzv*SnDld# zoL487#%@Ly7S@Hv?XIdFjbQnt{7<9Ss7%6)ee4Vg(WkKe5R9MY4`!6+M6L-ch{#DJ z&>!u9!B6~OaL(I-gkDe2FiU4U%~EvO1i0l15R1*UA-}w>8)m<92w2#GK{1V zkN^LrJ^f&=-^=G&oyTxoRdLweN4zTgPMl?#bElM1pe+Q>b|g?PneV!#xmk@FrI4n& zX@h}XIvIb5_S4G?VzBjNs`4Q7NdS1E=HrR#%0w98?;x!$WnR(Vb?(ejU>H{$K8i1psW_|ht zKrS#`yTmhlK#BWbAkzu@4cwEJ$dEO%i#88&)hWU#KAtxP7m}I;AVZnXmMTSDT$XT;w{g2T2VpD?gCB*!c&13*S0L3 zs;}f>S;^;ya2Lb&ZvNrdm%Q)?xjmqJBcck#a{NmX3+^m`_Mbm6m>Mmbl^?^PF@}R=&_ZeRuGlN+*vPht~7;S&d)H%?8w126FC~ z53J6VM@z7QG&;NcWKV!V<1e96nLjHPfbhZ}$I6ljNa_Bk*DIEknC3^i`0;Qsgp8fE z1LhJOTr&?0N7Er)cG{jY$txs{eZ({YP|CXQHr!F-6m#(3R{P3WpLF3VamAkn-SkXa z-{?SPX51T3&z9;hx2AONbbl{%!NE9>CpbeNG=GlUrTt_=(6j5xQOQCR)h3ZAoz&#d zjc&mP7&0}6w0WQXfubfpfba|A&(06+xsW-oVYn}PMHnlO0UZ1Df2LvP@OQ* zeB9qD;uB#GA)A>?t=?^GmByif@}9UK$erHS1Y^tm zFzD`x_C$WZy~WC2m{lky!QR0aiwm8un+7R>4Yi|{4O!!4=e)T7pi6z?Ly?z;gx$4b zMP;%0PFjAh z#wY*4UZ3i->?_2Jna=XVFANru%jtHq|ie?rXDQGREdUJ31>8uJjC+({S!&JBFz7Y7F@8_u$nT6yx41JMp>g7XIFI($U@wF4 z8zsex7wwA|+WRdjq3s54&q_^QjOAZ5ang{K-pest7hj_9-+WeCsHP&Jq$Mk$aC?AegkC8z^6gGW>gEN62Z&Gm3C-R0% zSgVI&WluopWYsAS}<=15vx4^DFBxPz)u6y*Y z0eArI&N>srJgEoKG*{9y`Itl$lRBPQo%{5;!Cs}g=WObx+fY2Yp>XNZ&GKH}WtBQHx?J03Wf)^?doiN?WCdVKGp?WpJ+d42k^f=nQH08#O zLeIt4dB_&@7$s;9$`++;zYe3ps-KnZ9aY`bhB3vklY0*;2RsL~Hq?4ux|Wd}wa$N> zul=QXyWbyoYYNA7jw;hL#GnLNlL;!rt* zbh`Q@DX@5}bicDbZW{Ee%Q@f<+=D_g+)vF%HHbr}2I{AJ{;+mO9nJb#mfk-$)|yvY zQN^hmK64CxH`iWpw$1>3VKoC z6#PQ*tg1D5az)YI9JQ(#=4K2h4Zjdr3Dnh>^naPlwWm|#lc7mQ%EvK}F>CXz1cwzq zNZ<-yTi*xYiwZmPcJO(u9x;|)BF0#y1~tl;Bc&-X&M?b6%7+9u3<^hk=8L!ZaKCzz zVr=Bk*)xAi>+z=GxdHpcG&$Gg?8GWxJ|P0B9zCrwiz_MoCpa0Yhfz_18&yQiR~5~J zd!$?Us-gY980@+XtQNeZ^b{N=r4-{=cN{vTZWcdRF?-x2V z2_9YUqLSm|tw&}F^{g+xyB~C9kg1`Aw=@TJKLdO#`nG;xy8n`qi_N>6qhiPyBYKSG zSbgS}=BBbL(x4H(c5V`km266w0^U!xjFKOG=2-k{ikZUhuo8}Y(MEGm8Fnm8bVl`r z8U~CMErfe#xi8L+D}=C>K2!S5H}N#RqO`OQtA$1MFvjPIZOnf_MoR97pEwbSw|ZnO z!ikRkrl)FzyLcuKCHLTA1-du?`s2bq{5-pU_*9Qs`j3I`iGsrL(<9!&VI>}+-Q&ZU zg3!))xpVDjr*_8=<(kWk=?mM#Z}@%5WC`Yc68{6@;`QoRWEtQ~oSU+|BrVd7Mn>5< z$LD62SkLbZ&Z91clmyqCbx*kIERR=?D%K;%mRD8=iGljF?LMo&{{vj60~^DXvHS>4 zN65OYPrx&=!3@CQ(oyS2&(|y>Ky20ZD4SPf%L_6oY>ZJER6skT@aInWzsc(PRx%sn zW6Zbq&s`eG?bwsMoq6xs{~Uh&EwhyWb=NK}*VEsBVeBNdIQ|j;=2!=t(j>I3H}a>? zx0X0NHUY8fgYgwk;g&^7~U=#bao5I!A;TWOOoF`#*2S+-0PlfoKR^w7yVP^GS4a~?(#V*Bm>_{~kXXPG$d#2s+bzv3J_OHgngNqo=N zkGvSTQEM=oQ``4~8c^QoGB&ZK++ zb)QY5nkNEGj83(HD*9nMxYX1nYR@tvolx--X$mX<>*^nZS~)eELaEDy5hCu4M@g}9Z=R(!dMqoaJ?FRiEVt)+f3rT=jP z1gFF?vN^zU)K#`vrF_K$gQ`vE&j>Y$kY%Ear9d@pVj`hkoW8c;Zs`4b1%DxBMmPIH zLv}%6v2dz;XqslZ5R(4<+K-DLN|~O7eS`KNVN0Y1Jtdv-#-Eh%A!b#udgpT3qLE4)QT&EFD<)^E8iAp5A|OvvHTO)H5~ck z-mPMjlT2T_zh#7?GdB~1R#RVpHFlt;7wrpAUdl!@hH0rZd98%>>2EuUCRU(}m7=LQ zJPyiAi9IjHDuqI|;e)E!iB8G*QGv7>x?dg+w-R`Ew)cY zu$IBNAUu$r{@18^k6FS*<>i2$AU1)gx!jsK!lGYK+Z#BEpR4nVZ2(Fj7LFJNE(TYjoa z+&<#gJy^>vt{d>EyDo4RD(b!7bg`m5if&r}2}sX4Ib~|CdLTZ?`l=i)na4CZDn_rL z&p1hM#T->elU9=3G~-VjxiMnQTrlVaM=8TNnT?RK)7mI>vPR6dnoS= zXi^1hcE0)e^(@t-*>}Q9`e9Am|*=6O!03MtaXpV;S zpic^2TQNhLjsSnHB9ez0hiIcpB{!AVoMm3AFyo(|Rj zTdrcR8o5vdtoC9M3-YPNUXF4qtG;XU4M(s$KpfOj^rpMM$b8m$b!g&LakkU({JN-N z_o1V`QB1r4o2hZyc||>HFIw5J1GnsOz!A_@;v9AR8SsGgRvE2cn9+Lq5o_rmokt7c zm1jraKm!2T4!JGYzc=-Tx^wlTO!HtKd+!BYi?l?0l#f@(VHItWI{PZxA8@LwY6}9| zyQ!=59jBaYLubpUD$2z+jZ>q4Wu37*_}wGbsT4l>MAK1jHOH})u*Z*^a+Ka!hbjN$ zA!cz?gOHp8oj8M>d;HcriX<2O9sDs$B479Jj)LvkM;8X@408lMg6|oD%A-oN`q>X3 zhKKnthfW9mI^0g^8z}zwJZLYq!5?5c1#%;*$~(L?JGiGp$x?lsWJ+*V{sgSBO$fk= zBiI=;X*Ib|(^zPe!uYphL3|qlMk@k$2tMu8_r^A3MSzU$_r#S!fPW1-w)ZbvF+EI* zZ3P^~okY`FDcZ;rY=i9Kydne_cJB+?UJ`n+<3WY{P`BUkd9HJ9P(uUl5A4#8kI8ve z#!M%p6?s}JzII-qZ`8-Fxnds>&s$Rr$|xLL6cb`a@_g|w4?*{brp94U6Zo44pt-fm z_R>8Yt(nB9sggg!AJ5DWfNI?px%A;sHZsWAbf@+2?Et| zw=5G*NY%tGAc9gWZLA3f#f@CaL`Naj0_UZ=|15&1yrS^E076D$7VM@aCbJm88la{s z&u4cy9pAn_bboQAEGpFJ>(ruHyy#2_;g*dRHe^-)4hq)2p+%@ZI2jnUXqYBI(`9}f zRmq{JV(#O+dydf=I22gZonfV=Iuu*Z@sAm`Sf$vb5rKe&3G=bz7|+VhO4X!vW2=KM zU}LrfE(a3nObc1h$^HATbH7<$n*Kab4Kk1GdENeT;!6>jHDkF~6!jGNxJ7nUa%;8E zONu3Xfg_Dv$=qyq&zxZ&icvNwaoJ`AAM?Be|qv1s=G15s7qn1_`>KWw!Vvwh`U z#I^y{_00@6$5O5fB{4q$v&z;>(EKft_OTc-X75#s#EqVf(!2xJ0YnS<$j6a$o3RSO(M0bzO3u(A6pflt*of8U9Foc8gyEi zu2+P0_k3B!EQWNuU1)1S{ob7$Ig{)(lb?F)PK=N-9q{J3ri>{Fj0`{cNwAk`{P`)W zLzaF%8UX@fK=eBSj%t%h{2Jmn{&Gn%vjL(@9A?7qyD9qdJ?yrVvVr0LRvo*z0xw8X zH+PYID)`{wLrqmw>&NHoOY1M5{BE?r+dQP2tKjH14)#P@rY~r0x$b;_$=$vX*!Uw_ z_g@d;x#JXo@SURNfBAf+Th6EIbEJ&^eWU5Cl-)Z^0;troE6(fskxR!T#%KeLpQ@J2 zL~UDp;mwXb(JK%fu%VHrbeRa+L-;7Xi;`c#XY5$B<5;S3v$W+4=Xb^dZF}4mWeEv% zYSy9E9ZnlPjqOsdJ$Nw;Nn!)Au-o@mAj$M?oj`0*d!{|}LhSSYFo9RaLYH{BI2&2n z?$K9NJnZ7t?^D>@K}+%gBLZ+0GQl$pgZ=~fC%4@%EEKxjT|42n$>FY9GQ)X#UfX1| z7K`yQRGQZEbK}FEO2)3K)_(l3yD-#<6TGf__M|q7PW>gH>Z~ z_E|b>;0~XBqE>flSYq4%u1tcnw%q=z(OJbb$?Iekc6XBL@IVc=AVQ`^(JVzS8f-ogvh}I zR?8oRoW66ExY>U%^l49E{#e1lGq1`@PRjhLv7M~$ufw4hhik93w~NmEJvGO)D(zHr zlw!w$zfwAql)-?%>3Q!Fu#l=|(g$S#`sN2}EZwx5ztQPguXp?Dc@Qlbl+HCU9P8Rf zW9&j`9UnPBq@+D4z)69vaj||-IGEw{IGX=8JWg-vM1N82tLsZHPW6Wh%(`kXg~v^1 z6ux$^iy6;7b!{peeb63|B@D(=QlsJ8D?m)^$H5zKYA^>Jp-&M&Op`>-e~LH0@p72| z6+(c}ScEAyayFISPJueMm_JXl;h68G&-FUQ6-R9Q0t5PK3kZ&LN&w;Or z3=lJj2D!uga$jy?l6NI1rdlvT-Oajb_$ob3g(oNjB{ztUE|I$q(q{o;O}@?eh^D)0YFTFki;}{V{foX ztXNQf*7!&}KquiZHLlp>li#S*+xwTe;v!X?ZZaPFpA6$#~y+ha#H;sM?!=Nsh-A`Sub zn_+LMNx(dnsgn}HR(@~>`u_*pqU;Ii8}E|qu%j_jXtC{h&~|Z^N1fLTT9|m`cQ}=_ zSWf*0&X*A;B5&>nQDTt|^h{J$lx#1^goVDkEb3@zw~zjcsZrv|9KF*IPV6j~yvlO`C$<`1v!va{j`Fehc>j z_phUWRyQgtdy3u_XDNaQjt*v8`GUxCt)v)T0_I7x20&HPyUqdy$}!1kb8SqiB#I}` zHx>vb6z5BiB!!9TI1C*3N0R{RgB`%Nx#{(vudO|5j#4O%9D=c7;}47?>CT2|XT@lo#be-`F&G^A>avAxnyZL$@Tm z2a%QILvHYiR&)xRX%D^+q;y&2p6Y91B)aZ%a9aDcaRLF)8&%8FT-u_ z{&jAR_lx(-B2ar2UjtA-&DWvwz$t~>h&w1g{knn`8j_2V*q`eH=CYk@t)*;_tA9rlD z2O)S`0&UC|dTLy0!tu_!7Qq324MuyE)R~e!R-Hh+dH=PAS#ieD>Nxk?@bPemV2@)j z&N0iQZYGZ|bi`3B=Yu$`XT#8&fK>w5T6Xi<7l*F`1lRTbO6rzlx?}sTFSqGujxwHH{3I?D=WRp0)_4N5T#P|inJP``5$2JtD|c~I?-KLS3k9ILP0JlzYt#Dc%Bx1;&sqCC--1(?8HRD zu~Ltw1iOv32&0x(Urhq^$*7T|R9p7WJLTbxkF#I^!44^9f!nwAl~oV&kgY8D3ZY2= z*AAJrP6$lv$zmy-7$!71-jsm_(j{sf739{K)==;kP!&`ehsp~UcTeX}eO^8M+VG&b z|Ag^CCAjTJfaS_##k*<{q&paVw3Lco`#^>BMo-K=5PI+=03U9}ga`>x!vwn)xP;q~pE(zkM?Lasfv=P{M6cC#zL)PjNw!;cPiag``Jb~Y zTJK#b6fQ!lrQssnLJfkx-n@IYvb*)1qOBpavb+1##2R-wD1OP^Ydk!=?fG{ws*Pkm zmm*mLZJikD2qy89kq`OA)Y8>?JQsl=tqUf9O925RCeF6}rE~sdZI77=-5CIG1rhG|d=VHJ?BQlmlvh-o!R?4MtPA?Q z!*Q~zezI=0s-Y_Qc<8PdkK41q3on9VPPr93HjO^}BoK!7B|Y+wuzj0|a~+$NFqyf(G^@b9I}?$iKdhurIdy^~i*alxjJ!0V0dd zG8sylE{0&hrE8R6T>$3^VFvfT;(m727iWQRAj4geVD~&cu+Vhz-#lvFb+CIj^a|6E zJux^)t3+ksEm&==+Z!K6NC0OOtd5JBz0)WCeK)qHsvzk!=}XKe<0dg9`VFi>O;yYb zStm!!IC2a<6|(w#+qoC-t_tLMu3ra|h?Cvu>vtwK!46MWeOo@<^Y?sEeo=Y*=YD)R z{dHtmt8-P)sp&GOZ!e1J;*15N$AD6pJXPat;uu%Av~z=c2=_%MvYZ&pv=t#Wqa}=^ zQVfbdm$JJ>^yMQ+akEv?)v!y&HOZgy5T}`=bBygo9Yo3_f8AasIZiSU#mN|Mb>41q z^C=*;btpDgHvW8agw`)SHK_>Zc6eE322f(h74Mqm`CkjrjICR%T5em86sg`1^ff24!fzhVHgcPqbzmIXz8ll&aHn*A;K>&@R9wAjVctHCdX0Crq8Y~5Bw z>Peian6)Rg1lvA3SO+DhtC z;xyfpK->JQC@BxANKmM8ZZe%29~?+C=%2nY9a=S-K7DK4Tj1klFb`@B9m*?f^96gV zfi$KHBCrIDVk$$x#A^!gstZWhCg9(yw^>}Sk-vmK!Q>J5Ylo>5l$ah*CR!H6Ef{31 zTsQH}tE*85oB~s$F;JwMRtu7WMPYSgzHsBYziB>sm5Th8A))9j=S&c$cw(d~Wvyha z?pQ(1@}n@`lw%7{{%Z0=Pw|ly_tvb_Zo5m{Wb$4$IUGCIxz!QY#d5Sma}wgFe}L0a zj9cG8ASP=e@fY+o?3TBTFKgJ1ZnyQEu1WDknQL6WS zPby^qrAE~xDqAGB>4&hvnO@V~EVdp1)t!wqiAK?xCg9x&hyDDFW;yKiqWE1=ekS8EdNU^9Ubf5GW3*7i7vomA&7LAsfbZJKC-j{<_GXH#U=-8AWHUa<=z zcPW~;`td$WXQKAR3fOwTDk`q3bp}tV6WLKC9yU~mz|rjKxXP|+|0&}^cETV+vO}hR zB$batRDsl6l>Xiwn~eclB#4b}wxmkwnr_zscH&^UAM(kWbIGY6@HoVOz#b)eTGlP5 zIzfLQ48dOdqo;w~uwm9n9r6ix;`8*2D))J(`L$JMSLPAX-{~a-ZUYVd&>DcPMNg^Y zRP$XYgtk0^xAOIDW6bWkv;=kV7EEKPNPPX$I6=Zs_e45Ga-66mpP=L`JBVxA^X@T9 z=k0U9M8PPab4LR~*E^B;A@Y-9SbJB&*T_(5L{~=fx_MN`S)65M3Fm>wi|KH0@%fcf z-O@DB>w!@sJIo+v*b3{Xw#!wWyEA`I)vL-(Sc%nSB=$ zwL}jX9H7E=>h8#&>b}KW&@-4m)a6Jg7lVr~k3S7eb%Y*@&xo3y zxZ2FLkam(msT;=yQvhsmR)J;xlP+{bYjxP%#hWNc^WRRS>Rm)6*#dg83dM zmM;QB!@tFvaetQNJ?D-$9sM6FA1?t|xtA<(JKT6_b@Xa?1Y@BWO zPl^&NPf8X2|Bs?Gk4iFs`!I$h+BjO-0uwI%u&8WNO9gjZTrzRmwA@Q4GZPV9ao4G4 zicqn%6wz`aGqqB|+|3fn63tyiC`d%y6;WJzpZEAD=XehAoQLOo-=EKQY2Xsa57x5Y zED*Y^s+KdT$9unClT_q1DmNwCbSkD7 z<8BED7k`1^-A3d*i^TJq6TwwFD*aEx5&`@a>&gZ4`lb*$Hx2yHl*#o(`bwg9M}IH< z%>ugAsWJOUL&JKvw-1lGIx^<5v|&CUr@JWKB>w4@m?n8JZS!5Zziy+sZT%Oh&l`eZ z8b$~g=$cBm#hW*Dv~|62SdT9M^Zx*=3xRTJu(an^Q}9`SpSkZGZ)vl-iKWF_A*^rQ znn3ARidTPu6dS>{I-q{e`o{)S3qtuB8|uljxnCf6{lt%)w$nCa147-k(`hU+Rng>R zJtA$0qjh4*oS)_C`$O9{A>|DUEbe1vSaP3S&iYOkE=nA`93d%&AU*#QTjaQ9a;s#> zJ?0l^tK1E#J3@m3q4kQhoK0J~6YrSC(hXD`mU?A zh;`G753K~h7TR!qw&}1wZV!ApEz(TvEd=S%oFK3Kf?*3)5 zl?g>upq1yT^|x7Q zxD!?$Z3BFM2kRDW#Qgad=?cLYxe~vb|8LHKhf^8frOtYCBedE1>f(#j zMmIE^Zp5&FpB&A&r4p>qu)aczPxr}-k2k;3xNY5JKqV1)SKuj2yFD^yD{xg_+WWn! zl5CQ%inEAL(fI{hW{v77X{aBFyHOm|xVrH_`$%XWfMoI_8zHiHF*T z)^s*bL9W%Xkf5!3OC;KDttL3Eis(K#82+JR|>gM-aFk7rOb7GwvjstqIW*>!_* zFU{58quqajAU%om4yIR;w17I|z)TV1C?_h;*?3-zH{gTn`^`$Gmcb7m7y zh!g93H-F#EXx(Cmg#32R2@2)FAn+Z=hfp)dHk?|xoFnmGS&1prsH?oo=Gax7Ci%l` zYPGGZHUSFE-Pp3k?n`Zuq2RDoPUVad>rO&6{d48+%H27Q<>l0<5NWWd=dFr8(pTJ0 z&tD*)ra%={E273nnROv(E^#9u%n<_u4F*XY{Z&t&}s@_>J0mp!X%up=Jwett+~?A?Zu&4#6fQ1=x$ zx^>ESX*6cT6)My%C~DD8yws&Bq$VD-Dq~o0=?6lQ>G$tD2}`PUm8pca{L@o0sM*KX z;Qn?^Zk)C2#ytc|+OaWsUZc^K_+&R1PkKF;MU1C4yz}rrYO$rJnj7kq7sX6OJp3TG zq(E%Uekd)=I38*`_n*s<+y!A0deb{?U2mj0_^T>OX8sO$0tl*Iq6&0k=VHdwR_toR zQjUki1H^w;2W*1bFfqkk{GfUk@S7*h@Zxb5xdn_|VXMoYX$9Ls_b+Zz2 z?42-w^t$FLD@?Riv|KwjIVa;pQrLnRJqf5$>=3{#jy<6gW0gKBV41ab*YpH4?6<9=*##9Y8d$m5)r04D-FG zfA+a3Zpj*F7rcE#TgHtIjUlH~Taem>NAD2`q)waF?XxW)3Z#?Oy!CV-becpKhQipC zAK%6Y+?jRvZXK{Sb2xJI01PqSs%%X8`ZoQEpK*Eh{Iw*uatcTYQkPkZ#4r)g4z8i1 z!g2pHA`LGu`=&UWKUx{lduH&2`-ps)Uy@tqvljH$kV_4qVN`yo!NBCAg;>QL5qx<1 z2+GfbnqEk|n+74XGFgc^Wd z&8gW!Srqmw*VE@~V%y9&EPs!)fg;@7Q^t+G<$3X8a>o@Y7&$nr)oyI-Q{WG_46x9f zU(`?`zFBvTm{QV%gETNMac|)1TSS3aS1cX1*0RXJ2@UyiIsak$!P*uYPE%+gt>K?yG@FwRTQ`oGmaZ1IoSb=*i&^8D z{KP)Go0Wf$Ir^r&o8%|w09X%OIQc1B$TTrFM2W+?VmiPm2!yUvxb!NxQ=MisN^`Qh ze_q}?8PoBjoDNy*oCu@g=wM*6*O(^sI6HhaU}iu;ubG;uk%Z`C+ZmCb`-{2Kn#z*T z8<)7_08j=60#mENy)aR_eRNkZ=QJtR&HwLq2R(`^3fkdCOL*E2f-7a_l#nz(L%<}# zeD`l+fI0)^5M=NEiCyGa0yTdG{q&G{_GYyU-@CGq&y1Cda)W|-TSp8+i)W=euSSj@ z4W5nQ`Tp06+LyG~^(RS#XH4|BB+!EGd{Kh%zYNi%Shzed$}pe)s?kFkVxy$E!N zH4yKgA#_X;`)j#yEgD`GIV4YV!JNS;Nb-WyJ?_FMgZpY3%(r z*x1i^j}2|CxQ$3oeN>Rqw+yH@^u>A_=G&7FPo00SM-rJDhoya4(6G6$^accLMXRz9 zd30?+n7PE}Tw)<$!Uym7hC{}+qrot^&U@r@xc^J`uGePXC%vcRcje`fXI+lX1`nT{ zF~F=v4PLH^8jcF})Stng))U^-KThbSMsaJGAti)w)rfQ*`^!;mU|7qx#6bY<1-R4; zH3Uqo;kvn~OJq79I;94cWO!U?hK1I-YC}p4U6preTJ0_9U|XE6t(HyX@wgty@TF$D zw_EYa*Lr;LQp7iisOh;PS4%X@YFUxAs<8*juO)& z*K5VQ4~XK8K`lpOSV$066t(m_+k|L(XTGJQ{YNw!{r{CF6;Qjyd@tl?O=O5!MEt1t zMmQHc<`k3Q6E-%&t*L1mFn0`?z-yjTIYA;%4;G+UUfbLMxa0X*%?+uaj&TPo&vR_e zlSwMA3Yw1^2*|Dm5NNd@7CBbt3n=KMX~_7#d)JaDjicLe(FjZM&<@x0+cO*f4)I>Q z!}Z+%O}^I8j4M}B&>NX<*a0W&N7MDhqnH=4sKo7(*(780gsV_Fgapvb{sV0>R23;v zT0{%xqIntAD(2_&$5i$0X&7^%GC0XWZ;J}51@<-gH12nm4%Yc3d;Dea`&uroV2fjm z&f3{>6O3fZ-K)#sEHB(8bl9t!SAVBBYIBB`QwET{nEk+Gl!G67X|0gLdOk^*f#~0? zx&F-hFKf3u^OQ(Ypp~m1-c|XYGMo}~^(GKTR2ERf!4yEWu{D`yI}_}t28;!5?JR$r zfXLtL3B+$Dsos~zh1gQ7*}xE;x?zUG^T*&ouE%{TRSg?XPI)m}E977{ZDii$?9G z-bJFU`%4i>%@UYQx3q)MT(+171e62KFL>Rx+@@lVy_2MQz6zd7)x1z0JBS2^Q@hy+ znDRDYQL9Hy?es6XOmw4O6azLei=-3D($2sV%`uVs4ud+a2aM9igS^;*=K5e>nH6?q zgj4>&X>797)2yP*WrV>eHi>Hz;ozd$?VZt%TQToL**_>Ja2|Cjv}a8Hv&OW(t)jg&!X6i=pnv)RL1*C%TQ zvS#zB?RNC>Pq&R*B37RKU@Crr_QY*d36r82dFGz6HAib|eg@s@l~=Dh`+tLSQXG@@djo4Fyq7t82PT7BFru;c1~9D|{4Q9BGZPsP+f6yHe^;0dWz9h?&{l?4J1RY3mm zs+QejOK;K|cQ<+~=!2Ml+UK1EmKNP2`EI$>!^1)TkwUisee6ZY7QKM!7S10@?%)cL zrC+24O>-spNAs53;;VCv%CHii`D^zuP}SAj+YACcZ|B6djPSWw8@-(Wb+97pS;wF; z;o1As#sG^MtUWeT9b-L7QV%1WsVWy`vAGLx*w<5&j1~jn+O+~4=mII(dyW(nVo>v9 zY|W%Me!8CT$vxHkfG2qo>ET^H{f_B!1^8Gl$am+iIp0#e3Yqs!ar?b@lX+o|Q4sR| zSuO9-x5|bdEhFV8&SH2{!GV6C6q`rZN>6vBuaGU@j8r+?`l1s1l=ntO6M?fa2p|as z!NFOl)DSp1GPR|BCZ^pCWE{rw3jXx+?qN0bMO$zRfud%``kLYhYd1ECI0tWnd|mu)0O*xCD#4T&oWM}sSUvyV zlyK2X&D=_mk6yg};Kz4EcHbMZKbC@6E}r7*d#$3pkUxaE*YTf?wo_xS={ISNu50do z&ED*uh?TVn!$KNQw6c^z8j%uhH~St-KQpTCZ!2^P2KB8N4Q;pWidJ`021|~mJpM6j zwST9qi8}A*A@eAyc8wguONR|ScsqhT>Gm?oyXoV%LV-zCRlC!ly6soYS9KqZ{G%m3l7a)Z z%s8Qi2&g18r!QsEunS+XCrMz2zdBFCf|Kv+0gr>Wl{cX0oMXOPlpPDQsCQt}7`@I` zCr>ULV$8+|Bd7X2YZ&X<-nxV(@aCS%mK66t_q5+xV&w}iHV8}iAQ8zDwv9)69ByS# zy8Yex^sx58IU@m_tUk=?BvF+z`!apE6c0o9J~a~fYnua)KEU!4sMU}5Ps}_8nWf`) z+U#pI7pKR+)5JMd{CnX=H8!NvVU6|o+}P(0Y_4bHUawCtPj$L|>sd_XHfuH2S5HxM zr%UCOIP8j^E%*7l4;+roZquLck17$WfN?QSBe@H|m91yj1p4ni|4#F$=7xd1?<#S^DQ?%F3(Y z&y`(kMdg)C4EJ}3EARAmi zntylt7sxRG-+u%e*oKqF=&7R2e7v0=+~||p=5Bi17Tn3Se-9S~9IBm@YD7b~N&#V| zfcy;2V&7O?ksna?0zXlvQ5REypDG9>NvQ)@r@X}#YUO~iTJ4b108X;skLu;i@=LE6 zJua0$t33Gh(6cwyu2o(s^A4{g@|`8}F@?=**H60lNa$Y6c$VFGG>#ex)6j>AkrW zk$0PracHc{GvSvdR_^x}!i*b+s)4geWmi7Qnp<%A`lJ<7E6Ai6Msr{=6?UC1G>i+G@?zAho3&};`N;i4L8mCuGf_@8A1^4q+rc*g`} zoW*2aAQ>~ZiA|zc7!wY)hf(KgL{VShkHEGsl<--&x=uo(=370yIeP*c*0;m{Y+Nli z4XL~&&VM?eoY{_X?6P{N`gO}J=mf1OuJQe(85TAxNylj__x`-sh4J*eeyp>-!^Rixa^K~x$`|BXV4{Lu%hdGsDAw{vp%8x z=-&FKyxq(4L)QCNcKsLnHo8qeU$KR-?j5%dAba#RFuPybq^)aqUtM{h3nJw~{f;Zc zT_?lRRmL=o9<0udl6GLC+q5V}8I$(Ls!M=JamSfI!aoYIEct(}T_ElE5Pi6e4FY#$ z6$Z|BR#oR`^W!svkb}>yPI8@@y&;}}0DyZ&L5v3h20lzuJ^;zMX9OeKinbRl7^z5=Fib5~+jY(QLhWYPd{{H~>2q zEns3^+inH+C>*>+G#-g9Teq7_ zSKfH|aXkN+4KGM~^*d#$o_Gvc2@NbI=~>u}^3=;C7K<$%y3;#|BL60gQR@-UQwB{+T}ooKbt-rq zPV|e1=;a7+yl)kxhozV&LHw!9+76bu1c}+j338N9w4^uPSgFfSl#dVVP-*_-UC^~< z{x>rZZ(kQPU;3DRLiv7eAF?z%;o;?xW=hReWH%2!)A1qc1g#LHzF-H21JFlJ)VBw- zy5SmByd7ws1Q%16WT+NNTk$Xm&p#wxR3hWlP;k7pPc&LI@K02{VcXAkdk2BiEJs-` z@Xsk5K6>Qm=Z5m)d|^PNN#L2=xASd2Quy>b*>uzkqDchLvzk0%cFIo1L6)*Cx2HP* zDsm_gts91-yNvqEwlyDw3nBXccflZ~UfRA;rFK{CXiE)Os4e zx8Zj=fk@K`<@JX2vUC+&ZRUmGk#C{p<+0^^`?=0GW_7X=6A$;E@*TZpK^~l`qVmDP z!ln2fOL_ME@eU*2v>sLc+UPblk)J<&8;XS85;#l?z|;XUGFn#uUD%(sL=V~W+o8y-LSuJQ53T)6KP+!6o6s;vSs>G-|aYkkzH%b1r2yJ0F!rGgx_W1zo% zqt4tQCT_RkvAi}=twIpFy05OhIySV*lOFlHyx#RoZC|~s=b<~?dJo^a;AZDx>;EE8 zO<&frk0eC_zV>$?L*N31A6$6AzR~rqPE2r(q%ZBj`587+d3$%fAwHNCTrIe#91ww; zWr8iYXVOm49z^pl<-Q(%y90;q-WCH=-7hRb>j4@A$I+JlLlHHVvGmybh3ZW=?+O{u z)Sc0A*&{48kS-4oI{t|^bi9iuG6MJGxlU-BP=<43I!zQI*_!2L9c(t@hzbgfc&JWbEv?%qXIAua&zA-=#bv#MdV~fpLB`wjT~$uM|5#`25~7c~J!fScuB3J-Zr> zttA(obPtNr%6PmHxd3i*qw#aP=TREYQAAjGSSt{j0%+RQuNSifniby+i=wQ6FF;bO zqjKn6WdE@&Pm{=jJd<;LtD}6R+l+!aF6Txn%CmE8p5&O#VDaK%_snkX!W732)+vWE zctj_>6l-^>V+PMZ2&oO>#isJ-MLqTPN}Y*1LtW;ACvehtSC}Ybw)??nih$`L@)HAz zF>sik-x>&S?$<}Kp7pMbS_s-ScsI~~>E*^9W_|X}jZ2{p{i|2IbHs2mG+iU%K14am z;k`%*|4zu?-)VX|BZKx25Ty%5>ddR)Xn`b*QinzBW<&9kLS%7y}`8FZAVgm)^ zwF31nsq#<^G0Ef-?oIJ@r{eDDF!doZ+qY9FmYT^&$DKwGUWt~edDM`2w-08u*x zq(dk97ixeyzTR{RH~w^$N^wHyV_Q08ko4GP_|MYP{ej|O?%D80lTaDI0-wpK=(hxT z^;sUwfkSxS}sD+(@7Qp zECwD4rtW@;MgDxhHk#hlpq`oD#G6kgEd(21W5PEQ|7BPiTRCzc6-2>oVltyVH!BqJ zpShuLNv{s-zptZ*gx5cO7gVtZ;I6!~t_`L;TCu*^Yg$GM^@L!kz~A!k&SPpIfO)%& z{0V7NiJo$Resa)HO0l=cgt4vuWqJ9<%1|8tv!j_590ZX;or}>Yt8p-N63O(YJm^++ z|Hg>tP1(dRklPCV;*6xaynlL*u5EH#S{abrzNA!_y4lh4nd*;2KoDEQaxPli6WAyjRBxM6w?<(% z(7efMztmh;AC$mg%*s4XE8|Jn^1$NC;A=HlH%f4bj(pHAAU#b^*6c+h_clha1HjD8 zQ`a<+mb4@xP-)X5fd}895J@rm5u*?!xFRfdC_&FJU;%F)QF!F(%;&=AfI3}WCpQ@! z3Lb}o{122E)q8y{ewW~3JvcDHsPt*Pfw^!_(QHx1FXo3RxIT47l=FC~I5|sbbD>2H zp*@u(UA=M$P9SLyFLBN*k)L+qY^O=vsXLnWbsujtqRMAmA=*aCsnl(;y%bx#6$H%z zN^DX9eWy$c*w&s)iF9_^F*EhLg73EZb;AnxIG4z!my`w-E6O})8J`ctrMOzIe-w$B zB>>>d5OSgV?(H%I63tFT$fQA_*cy3xhUOi z1Y%rxOIU!^Uw>tl1Wz`?uo?M)@VnAIfjv{WNWm+6x;CHIFufwInP*rN0_ipkCL%2hP5mU0k*&as;FvPQPPP%Aw1>8N{YGgZ5Qa(7aS}ZQ7{=j zLFk203F)8?(DuJJrmGj4Cg-E7dYxw)nC~AKWo0wle#S1;TTEuvOJhTL>U1kYTAg9R zOCd?#?rIYqF8*KP%~1jy%>oy&dXuc)K?**ZEAul22LRDVxa zx|i?kM`ja7c}q*^2BU36f$s9 zda`?IBIL$j$B#Wv5ngYA&=_2^_z5<2LEI4rwd2IuYyn3}Wt~p#CXwg`P@A4>Gl71Vg@>pN)34edzEaarG z!L^C$s+zUcMz7Mt5x`1I5fEreb{)5}l>+Mn1Qv3gVi?mQqD4c!w%_XzZGANJzq?*l z*S9;v)J9%u?SGnIwx$)sryZ^(C;XZQX7y_t4JS9C@BvFVYk$3n&*+zN;;a=%Wn+H)ePe2>d(e&@Fl z;-44V1}hV<+n2k&=2!hw)W78Z)3wSuFkX|oB>WqVuM)&lch_7Q-!s#a_&YWAN&cIMfJnGCHT*}%#$A9^lY-hv zw+RxG4XwfJt|&1n@sm-vf<&e+x>6E5mKJWWh4;>ljdPUJdMpndkUM*r=qzu}WY=wG zEd1*evtBQs01yxxb}xrCXQ&k@gfiK+R3hb|;NO!J)tha)42Es!u+}oQ!^-CSnK%q& z(&ooA2#w!sa;^D#CHER34S12J8%|nRS)nBwTN>~YNGf6C5DL&=s5f26o~$_6;@>=> zwSA%R0{0haKLA4cnIZ^R#@n$Gs5=300ZtX3l<=1-tiYIDuo+1DWAxF-upwfANFbDy z+T8Hv+FOd3&uq}|Idvs~EB^w0DIE?==78U~L7TrnXuc!t*vPO6T4rth<@*4N z5*N%d3=LJ+S9kUyoAt9@c2|p3Ir$U?4@BgL zo8-LF$fh_LYgaG=}ZsB<%CbVjAYjO4bACOYT(UV$WQPvTJ78$_IjPT?_}We zp^d6!*hWTkP!9{X^85T5xDM*V_c(Dcd6f1T)upgcXehV-Z!&v)O;!tx%jYGP%a#{^ zfoe_S>d#t*t_-cV?W;4@#KZ!4KOGVO61_0{vUhiA1kC9`l^Tn+D>MqCE^&`i2%Yn#77z;wu9C{3!^Y(DWo$$iDurehiWTp?qf_q(K>;1@=Y zk7-#?^l8dFJeP(yKo}rMrKeu2qJCa;0_<>)7gI?ONF7iWnxJh)&gikQ-{-)>TM1!cd503M{!|5_Z)$p z`m^Zj(hLgde8y6jWwSZH^mRid-6!W7UmPC`1Yv$l>yYU_rEjrVY!wd9E9;*B1+sqn z+L5ClDCGnEDyMqt40@RpMk^fJL@l3OGmuEe2IFnY^CPc~76BclJqUPU+9g`U9Nvhg zap?1~vJzR$v@Remb}tSQHiXInT94jy@EQ&;H{#VK)YPvv3DRyHV%LI-yFpC;t78D znq129xaTx2%w!vXJkb7q``yjE8Z&$TE z>K%8GEm!(8nM;T1la1Pmfxkfg-QDimO5n7K7=>Jsf9cBRzs%1<;KwkBf|o@O}-UM-eGe(lZrd2BPTz_(aanmCHWYmvqxs!XBtU6J|^Txkro*LOL4pJebks zt&k2X7IbABp&PfHXiYi<9R>tb-*kmTIG^-h?O~*bqWB1OvQq-&Pg75N?-JM-XbF~wTnvB>GqBbl3TFK6l^K!$J`OeK#w_*UQ#70FuH z>ta(+g_sU5RZcW8{&1Ien^PmEfWEekVyw|s00%E=OAr^~V=u%&$okV@B(|BaQ(y_cEJ&Gt*4v#&HIfJt+1tN@RO12X~%E#XDZ>tRWE zq@3kN^#d;SJN-#ukX?&pQZv*SyTS5NdU?R@k&k=E#e>2a9s4_n&;Ge55?SzJ%aC=R zMK2Lq*eon#P`gGqp**bh|_uHr(qkm#N zU8FS)jL^BiF*{PLpA-l5uMgzUIZS39dL>!lc08{re~}fd_TWvp(LRdjDdCYG*FT#+ zA-7!EtNzFE0)EfEWj3q}0u=?m1Vtf|eOLjjb=CGAUnYx$C^rjFo8%cyyepmDwA<-& zz^il87AvqJRHhfcS+LlkmF+yji4AwLlJ$@An&v${r^mi3e3>pywPVy~hGl!?;Ki88dzil}q@NF@yLCs8KHhi1u zJU^LY>~)Fz_1Kz8M6uj$C6*UcFA1(sC`?9MigqFHiEucZ+1JAEzycKAVS(Az28HXW zI6*A{jS&kaO)_f#6Hx8UcVBm08xm|dDgrd3*?-~eg{{gYrA4J7oub{K_$eJ~vZv2{ zWFrNjHJuxDwtt*+Zjx6qQqz^kSlw)#4KeMmOFcrQfx3wP>E>moebWczQ_g~dSveXTxur8!8WXAwpPbUSP2qDl=d(+p8^pea4&oT zHBHqhO0m$owl)YSsm~D?X=wd)O+<1gDJ=7$st7K++X2w2P{E04ntVY6w>{IQ~ zA;|CRzJ|!3+NUdlJ!D%b-gKV2V1CT0%dVAk=gp>;yljW*c@5la6*ahsn7QIl!`Z>Q!V&4<^kKMP zI!0A8sHb7X3M0Ph7O26Lx=Du33*w5oPb#3hN?(lXzY{W!& zhV_zm*g=3Zbo$N^ut~z6OR4FqLYOq|i8C;60=`B6(eJE-9Y-8|M&sJj)dX`YC^*eJ zXFEH|G^0Oe!a>Nd6m}B_(ka8UFZv_o5$-{}kf`|CF$o)>tTM}c&lZjW*fVxn(A#<~ zz1A*_!&kB)ei-DBYB#(LAayM#Pagzr8{St7Z6ABq6|0p>zOSs+J!rM_X_tm-tF~&F z9dJ&jMs^WH#0Yye&sRc*Z#FE}`hpKqD?f2!&Z;9H&mm%ER;1bt(3cuU#?P7-yoNFfAJavwV$TVtIi9 zjz$u30Sym`K%gKG`W|3UYf)v&Q792v!?1|tU!jJ~88OCu4So?R1?FHTn1Mubyz}kX z#lw?z!&c^@gS~a}moFuEv}-_Y?KSNN(=`*6gc+o~gZtSq2~E`}NvKT0#R|mK5Xe1b z_B}$oDhROtM8mZ{!=nJln+u&PX*2X%OwF&bx0omGu)60c`x<^J$MuyvpTl!@X3+lg znHeOOy?dTeSAX0)#*OX4y*AVJPR-d*hFGg*3;cvVjSk)F0RtoguJ$@bv$G3t)D{0P z3&j=?*gv0Bx7kOEL(#2}!)QH0ifJ+xC`IO07f?;j;2Yg^l6r=`s^9hZg(RF2iQtWU znMY&D-Tq@P$2ynPM*-At4BND(*MWY8U$X379@stkzo$FiTRrwiwE%+*493PVY_?4K zyS=etG_OAYyWlP7QWC&1++vjTbKf&3kP%dTmji(Z0)Oc$gd~*M3E6Iq#a$+CGAB)+=tWW~sUDf4Vq|JXpC1fR(~%aAC7-c89o>gyZH6?==%?sd{nZ4g)5 zhIO2lLBacoOB?4_!b(}kTbfh0lGW~i*gVR-gWL6E<>8Ne0Z0VZ@$TbapxAL`u}$WM zQ5vXCt6NN+BXGt9IP*ohYiM)gO%BX6n%R=Re&WQ7m9`J&w92w*f7i&%*&k_p2Yn2t z-CcT>xXX{+&-VYXw%HTPrcJi}mE{~Q+D%Z3`1>oy*o|_jzA9w@EqeTu83orn-1!1C zU@6!|iw_8LnvWm#ja_@xFG+tQO--@eFvFs)tS2twOaZmS$!vAcpTEU`-?n2=Naf_d zMcOOt>leJ=GbxT0N{k%SzbS{>+K$}O_)4+=?zJsp;mM`8LwaKLMela}*lm&jPxV?@ zyWRE0fJ)^MI8N2#?a7b>)eYDh>~eN}yjgI3zha1Xx&KKPqkizk+PB4XW72+)5g)AM z9SX+y90T%|8peNApz;5t!Ak>wslk_iyUG9|_g2-CfV~C@tCwV+^Lfv9Q>ZyeW$Kwx z1_|%1^MHs`g`yEAhrP~x%+!z3`vroIDrx_&qYAcuV6NodO)RfTw7N_LI&WJzF9#;4 zt7(lEaU;F;3}7T}0%jyW)ct^o9@X6YA1Jlk475Dsl6>Nr#_8=WD?+QX_@x(MUJBK= zKUjPE(L4GeQCu)VTug`j>{2at`+m7}@v^!|RH~nDZ0w}(2SiO_>ya)+4RzsBH3}zs zETeZd=Pb~NcG^#W97UXsl~lSt`uK$HpN>~*8O6O0t4?wKjB}6^|7b&%WsW94kFNRe zikg74L&L#EK{$J_{X`b-y=aR)1ir{Nwg}a%rGZXU9EITbT_8z)ifM|YrI_5{tZ}!1 zAEdq((Y&p7*V8KGiJ@EkTldB%XQtH-s2zXQ;Kq1U>z=cx_UXa9{z1pG&8kx{vpIio zBVKzkB{fUUA>YjYiRoux*~8_!^+0#?&Xa*x163_ylGEy8L#5Xiz>_^3xiw2*!M(l zi=!d+z!9jn^wy<;!+XmqNj4Vwjz4M>^SM%aTlj5wN!dXqMe+%WZDSn5d8 z>XzM^5e4cmK|mjmF_bd7D+@Rbqc!Q|tUf*N+`|zBVYbNLdr!#e;L7SdJ zxDa2N))3Z(0qdAYutj)VsAZt#;HkXPkc$7f%n#WyA>Vj1_kkC@Z#-{rYX-Aqc|m3# zUdOB5eIb>vx6K~Fj%jGx9Gm^FeTu!D*KBGx8ly)$k)+|70-$anGvt+@cZXRJsPswa z?(dAnNi#wQDI?$Mw;y5m4i_l_e%n27S;|XL?J%-h$tyfN{K2;o{qm5LjK(YF#Y=AP zT2A%#!Hm+fnh0@lnf~m}+`$B@_cqjrWf&Ntr@kZJ!IsWVr81}QsEtM@CsUPDPEqB` zL;*OXds>ex zD6=AZdj^(Na)OUmkJel~K5fclRx!AZ5h2xMIgYm6y{y!mF=-HImr^j&+e7RW=bOOeUsT;d@6i`74cf@O>8H?fCSL!Ks@4gK!qx^Pt-?{WYkH&85s*>inZqu!E>XDely-H!NFJWzq;7d0ID(-UI@Z zlBr>ldl~=U|8E3sx7P!CkWI$v12Cpq*l%y5p&jdIyO!&m3vavlNRY85gAa!-pi??jVQ!ZSaE&KfRP%J9Y(5 zk6iu*I=Ose*a!qWIXqklNq4GgIG!H!9qAPg1)JQy5EOi~JeD4kjg8Q4>t|s4c$&(i{w&Bf{R`313 zK@ZIuQI3j_ftW@?zz}X`(y0xNL!ZKTo!;Vjcq=Wr&d(pmxiil>zxbe|yP~DB7( z44Iz)W)$1e%7TglGQ0iYm?^$1{o4l@G45J+5pbgZoJs@lm7SnP#D|N z&kr6h>3}LSA4Iv;4M;4kE)B<6Oy<_rcNe?&@D9ko4Y;~jWKEV=)w?sjjyt0D7xN!$ zRqLcS;T)Hgmsn`}?_a1}oTijUh??0{0Dn){Tl-NvA6b;e<3s|CrZC`89M;}LzEA2a zi#FsN31J3{yShx199M~vci?`CyXBc<%Z=+f10&4%YG)VkvE^56eEANp`ykJ)KJ?6T zFXH! z;4m}h>O#~}>OK9qf@G2(WoyRZj)$Bj)5BjhM}VC=(sY5M;(*=hj~4B~ANtfeHO7t^ zS$1h0Xu8d7bJT-Do?!B#{ML;>XU>2yib5d-O5$(U91R1$kq+eIR$a;oj=oE~O*H zaL`8l{quLxu+nj^%azRAUEEMQum9R8IkLvpyJ)$mmN)x1HoI}D`AqouoKkH@MiLN{ zjTYrUO+smF>J!FJ5OFx%s6P;7K0l+s4Gpfq*@^-d?I(;rWTti?pIg|%jzBuo9nk>f z5qu%{jg!)1a$g3((3A_vMx5sTO<0My_v%p__R(V-#LA#!l`j_F$)j9ddV89dn>vfl zKG|PcBdchl#HnPI7);AseI>to(Qf}!e-KH;RQhjgSa)3Wrxs;66;clhVA$##DvQ(U zd!LfXj#FyV)>q?mgqoD(7WDs7bnfv??|&Sh$SBd}RAesam{f8Po4YO~u{wp^6(#q( zxnEDZ&D@G&rgBTn3bS&Tm`fYcdOcqQ;Okz@ zOay>8VoYTtoa=`6Ua~7EojH*Yv9V_=$5s8}4K83S%C?-{?dmg%bD6Jj=!=;(66lbf z(Z54uxiphk(z;eFX~LpVVCS&ynH|HVd%js+)cV!*?F8SrbbnGe>6IC1TdF|1y=`Hr z!a6EiF;h}ojOoSE!pPgg-D39%l{5r>AgspX3^jTJ0u1pFSJ=%%%|H-RRgq>~v#p(f1i^p~b7<>QDC*j|{-(LXxwi zlxRlYnFbFkn<+A2CE#LQ3Ggb;OU4SJAmMLmmkF!&`IgqS!3N(cV$X<`s$NkiJ~48~ z8COWGO$b=-iQ0W4Y=ksSaKW2qs#FJqk6T#36Z$*3ZBMDqI4z``@rDV&v1PPfB~n{O z=vV`0z=2Tspug7%I_P44h4K%oIY6OgeFSI`^T8ItU_M~_?A`YGrqx1tgPLo0fIp$7 z1^JI%AiJ4JzyY$;sl7IPlQF zzDw~rjYIvP8zYe8m8y~P@<$7@r(+-TB5NCc9_HshEe{C#Qmp$^_yAu7m9>=&189p7 zOtpEt^g(ple_K$9Je~E?2wjrZ4ko$;$n^~7G9iL7CRQrcI2h1yE&V|17OIhtS}CGF zF;NV-{9_8XIhl3(9o~;kSRo`L?Z|%1n_4f12vbCw#WpggboIgtZ>aOP`eU=miUrh> zX)uuJ$DRkth`zTDg_HVoPzoKGtikITkT?UCTebChz}zD^wjM#o1_y@>Na4dQtTSz+ zr6G*0l#py^aMo7jCJg)+Cfm2C@&b7tTNu!2A6s5sDBC%@h5KuDk{y3)z4rGK?J~PR zWNqyZ0}5fK*3ntjAuk7kCd_gia(OTNR0*8~jJ=%cr42~%IZWQ2$@#D@%kqjv10c9j z`d6)+KS!r~F;vok zgNh!F?+HLf2oSq^o(F&|6hK2f=U$!*sD+VB(}0!p!*4er$H6^fvEjFS@Ad@N@dk2lAmEe-he8mA*>s$y%s~c=2KWQMb33E8DERH9PhGS_;@C{)sfc4OTV8+Yn>?N#}zl6k^B`97SoN(6b6=Xeimx1CjsuBO{ zOTwscAo&Biw>~pBz|~+Cm`J9DEIJBE%82~+=>=Z-e&QoX0XE-lE1|`Aas4}%SD&w; zs;W0awI>t|O>5Qk00>)KgKbPCoGuYUQS_2@D(*H}3Drh`D;BcN-h(@t-^ z^LXX!-VleIE`yZ;jslJQv4+Z%KJ^VsUt~zVOmdoY#{pvx9tWXq|5ib*C<&)#bOXjQ;tcILk{#5w zyD{RpUB0H%OrUr*?(VsBjwwBW<{E<(Lu0%&;6O(BiWp<4@{@|9T=PYS(fWi!x0j?K z74agYLA+fC3JSg|vyEb*jN;YM)Eyhp4tZ%D(z8Qa$=W5)IS2$>J)JN5DF0~QIX z<<&Z!*I8(*!2~+`DiG~RUf&-x3|7R(92u5RU2E$y*w}N@dzZ6r?~vdWgIsE%=-9M0 zs|7^RuIxJPjHm)tC+l!gUw4y2FD)$0TduPKo>-LSI7)bf=ynDM5sgMC**gV zcXEDjmFXOZk*7|@I#Q1;b0`NB3H4Y08rtmE9(Ei4RpzYQAz01nU zJw4DhZ8Ia`c4(*pmRWDs<$J;(1UE+8USkaxUH9+=ql7U>c^(YaWEut?ET#O|Rx1-g zJ%*na9k>m0hBH|(<79S{R*GS$7Rag;R)cST1@1*Y%FI>=9%m$2Ih8000lNd{;`Z{qb^070<=CX9LysI=Nh z{&7=+5xBEgORv4NFCiX|^IIXrLJIYN1DCpoLhk;0sPzYA{C3N9>6iduMxEl}Y}KeQ zSFA)P;M)>s_Ff6&@xI)vA5|V>W*;U)S4@`7JaoDD#?%MS-Ee>z7f?b+VC+2m1DB7R!X$(F#x2S z6y9mdL1^$#H>!q43_h;5peOz!`-ikMFj+uJ@g<02Ck`XydnfKM&(01x{9-+eWrtH6 z8;PDPuv@au!nq;xN{4O^eY|qW2<&jQ5@>|oW&gT3YyCp>AhpW{}YMpr>_YBA#duvs<~8N)owXeKVJ#>U(;{q z-eqO=NWcY~!NY;9wB$W{U>+;5& z)NIEst@8&@!er=nLv||5$_6>UrS9G^>RHE7;)AM{&4&28we7fag@SuNUUwRPm-=$Q zhn>A!dAIc1vy9rZ*)J~`@;+hJxlU^G4e?q#?teh-iHSam<9$5b?wgi4et=uIuON$C7qn_Iz_B4S9xk0#eQt($QZ(PI+&lZ}^1;b2@(a+i#MjR`-&rDXOz6#~k#oIAN>`P= z-jO41HtJamyw`w^3C^#b)!-{po}1E(gE+LAk)m&G(SXo{8jqj9kLqI0EdK$GD?sX2 zA8G0KkB_nij;}XjdkBsvYe*2#FIP1Ry>8nt78xXYwY<`UdPEb^r_o7>(8tTAn=`givW$UMKVsApRuDXQ01Hw zSo5Cxy9@>*E$)inTRyET-{Dc9ZoevmYmXCe@VnjIl-m^@P)06ouJwzSNLIP> z7Wgo-wT_AOS&eZM#`gz_e1J4}&*JG;{RX&m&0v41bp_fUX(mR!6Y%Rj(0~C|tRVh* zjb#NL!|s28Ic>Q~>3nDJqup|W$Bg;;Yxq}#|DZMlL9FBbc4zyj!?uNY%*$Su6yhU> z?44AjPoD2EdGm=-S~zKv!y%=+zs(=+b~IeGR}9aB1ME*C+S;$4v) z?-d;6KbHkqb|CB*_KUhA;kHjw^_au|O-Ki&e zRx$qGjba!`T%T9r%Fe~UqqyUbOR1-+dy-4?b|i&`Y}HExkh*d2$LOT&JCBs*fq z@#fsU{FnV>C#+cP<3P7#$IMCJx8I|VR?a*!E@mQv-^Ix z-H&lH_qjT4ts)UV0u_$xOd#H;Y|+5-SyN(J|GO~+uCK)J#>9NX6!ATu^leI(qmj~D zU$v=rvV+(++j@NQ)cv{9M`afvc~27QOayH^Q9dz{?|qFgG!rD;9;tpl;RuPdw&PAt z{D=dJ+nE+`XLkk(eUF{kFCkPut_C zhZ->-VDkhAt42TXz>Hg+e?at)MItR=f#oh12qwtjp9@;vp%<#mJb1q2JVx^|l}?c5 z$Dd~lOv?p`rk@c3x1&<8*OK4Vli38_5rHye@IX|M}(E zJ?aifs#PlEO!xs@XA8b8SJj0d&3)d9qiZ~JJNKhW2U$HPxX7)U^wYcawdHu-&%N;v zzA+m_H#x3I@WQ7%YTr99jX=}%(uRCaZbN~2p*-#`SAjIh|9g7*7&otT+x@>f)8lbB z_@C$*$D(9;L+75QMoZ)CB#d+%$-mP%w@B0IqH6;~2kv};qaGv3B zim9{Ta1Y`CTQRy)=7Y?>bdEY$DdTqbo#WrRaExNMO=Ia_JgYUHGcm&FLSy5~;*Iii z3jnaB?<-#w&P8eiq3XWwxBX5-?&)PLaa1LJ{u zsO){2jexrH-iOAJY{yp_YNC#45a8U|23G16TVUpJ*i#kg>{h%TGW9Q_7LZI-b}=oA z{99dmZu*V<>Dj~+aaVpc>CZk_JH>G1P0=2XX8FXV70$xn zj`%ZErly<$acDbmaqZnYzQb@3oG>MwooRHH>YCnfa>Tb)Awf&<*OP_y*2lVr#>Yt1 z$W%_ooeb*{TmUP(ucJOei`-=O&e_-gX1{3pyZQAAXH{C$gPfPle8P)Jg!?14INC_W z)80PTjO37vsV8|!xw%k zX$r*M`dTEtlqh9#hOgS0DA^P})A*>K3r=nFdAn!=;hxC3@Tux!Jn`Cy2eI*Lphx)U znua_5ICfRmq!YrpF6-)aTFzO%PF!zmMeU%nH*5kK=|Qb z*SM4ZfZ8Q>Vx0K2PEGaSTEa)pssNgxGPLo&_!D*r>VOTl~(2LLo9 zK%5il*lpR{7j^IkzmV&|bL8jEw1OUvTHd4xY`j_&xbYcj8^i56H@TPH4p5q|AZPmZ zKw-#=CB0Gm6VJpO8vau{vO`f}J8(^Tmw|m$c0@oiVv_qX$2DB)@uiI&_%U!L53>LPWP%$rV7F4QwBe16QNA8@P ziB$Y7lATmkYf73YX@8O0^*3J>RdhBkEYJ@8ySaG%!)bgqXFyj#23h`$U9WtVvt6fY}Z zQ;9vzsEk>P;MgjUwTp{W-BA^cnj~D6 zi9wL06pH93A0VUaMYOY4SiWF$<{#(E_@_qi=3Wu5f5|)lGAA;`W@=@DP&$g<%UgZm zZ8=`*gu!55D9iYd? zW{~(Opvijn)drNz;>zQ^E8G{T*V6T!hSk&iC$U){TrPKwnJLx2vewr7@dxy!sMh7= zRwBor#SSX=>4`^%Aa^dhc8b2fmow4--gQ!7zl)(0YEyd0jEH&qc}HpO0RbY%CR|-F z95#p48vHHL;rH+OCs2y&5dYx>U@nXIlHbZrY|qVS=RE})lYu`zv1B0E`XroYy(4Gp z_^iDceSG*lTi4ZgYAQk~TXpHh;Ho%&p0Ow1!7XsuEli{*E7SpyXN7|c`KK!x3TcV5 zf{5y<o^>=$F3#aP%(Olh!85jcmGOjVBi>dT99^*)tFk*(_*t<(lSy;&GEFgR<0 zpSn_gU*6|hFQwML#yK#5a#HQW<(Ub&YZdTN104sSpn!lHaw9F?E~5i+7?^he-wwUR zO@pDck7I<0Dlki4LRi5 za`q9f_3R%|QUZL*j z=E+8(0n74p%TRUIX+rL#TFbw?6?^;n;BQ47myA<;{q@w_ryM$pp8NsTwt|^WB=o-6Ze-y&R(cP|GJQT^X}zCaP`24osO=dzn!gn zS5{Wbaz~KL*N*;o->Uh+8?w)MUH%9Z$jEp(IeqKbYO(^y20^Rj<#OVSzL}-~r)|VH zspI@(x2?Q>3VhxxNbyxW68?S$&V~mX>3ra`oH-+{PXqqF!+eClpg(+m=ppuWq1VOd z>#d~eaWVtHOqzXSbdtL>qk=X$7L~g_<=3xH>M}4IJwausS3Nxr?ARXbzM`@&a@8I+ z)mR?y{FPj>XkS0n0|~Q+pex*)6*#U6{4=d=OAe9u#eLk57tQ~|K#Z@`x7vf!rVrh% z`(bj(4_PGnYh*LyDBqXz?IAS{Hc1A%j92lj z&(*DMtzxTyaA!Bm2=P8DV`i(;-`@+BS7WG zi0&CRx?f?WtMF}L2_ZD!Nk-!Np^Ke_yq00y{`L30<+>Opid}!af^KO-Gj-bQXONWD zms_ZE_Sp)(X^rz$Z>Uy|CAi`kH`aPNhs=xQZ*g*awnrq`mp_r&i5Zz0Ql zoPujnoHzaS2h_QPKjiL{<##H^b?jk-=~IH5_U!&IDXR$12?B0m&#=k4Ao3XX3OJ4V zxhd`HTy5HnR|b-8Dv)ihO#G^}qY4ZcMALnf=Vkn78lrr^_5__EsihkQ=?bnn?ZAFs zD=L5b+Q^&zacun?pNHPnwfuJ+S(op1vE)~)W~+AwUlk$v%lr?>7yrogu#xkQHLxdc z$pMDKb9wPvF0n)H0ZGL9QI=|nr-aRnTac8Ky zxUR*<*7<$)DdvlO8|BzIoBDbJl{!F~j2toEcj0|#`|u0$`0ydf8RZ8+Q&d3iW+qtNs6$g=td&fRjPZt1C7#32nkvc}t#E!$!Lz<1eH zUhkT8U_jtMxiasp4&&mBcJve5O%s7#C8>kbqOn^9YzkLp1!kOxbaq#PK5C>6A;C9s zwf!->YC|@%VaU06Q1xoxJAM+6^(mfmM|c8g-)Cc)<*V*|Smg{lk3PshQ~mZsR7@8Q z(ae9%LCmy1-~6ri=ISkAOK=L0G7_csfBok}Pi$-{|I#Jro&m}i>}2_?rnRQ@ZQx7n z$M06OXzjHniU9{{sWB4ImOI+YQTnRN@m;;mJ^h6KTJ^JN-eWE8YBhj3qj(GCr)WR)zuKYQli_E{$c4z^s#jsixkZUSvu?hnXkb*y;rz<&C4MuOp}$=M}^{VgY#&dwkd zqH69BH-&`FBtn3(RUREOGk7#Ai{WbdEte{fpJ^;$OYS)4uiviE_;%_MhEhxG@coo{ z1}L$%1|X<;JWtLFp)6rgL4Hp;?$nULQ}XeLs1JQ@217oFml|@Pmm`8orWR#!)p9H6 zak{d7QTZzAZmMaH|GoYD$o2bhKV|8u37nx?Ts3WN+})WSU2zR{9KMbiA1tiH#kP}d z?6zVyk>hHJh1Ih( z#fv_iu=W_cxz_YeL#5KeDulwb^Saj7xnG6}E=z|eCc!=`(bK;%yKnx0?jxwyJg1_s zyw*C)c6YfrxzF6XQ=O#$-f{siyvNi(sTbo-)E>BMQm3ZQ_m%a0Sf~uW({+;l(SN#C zX*&>jkT(f51%l~UDZUCtaPMI5u4xxmVPuBL1!rj7=PS_> zhX;|fsGK*28FaGInf&D?E{J-z)_pzi7WzEA(>8WSgEIZ)x`j9h8wMX4}1Ed#&JOC*R|i ztETcB7fYsqjI)>OKBnH+y z9CS~R)RWElqoYF|U$L@So?h+%#hshm8ePJ_W!2i4>=tWmuyyD0%|_xaT|&vvbXN23 z8&i5wQGNL`{*8ik9CEi%5JoTBZ)r@x4Wa|`E^`x8mep4uoZv+^3_goH{(=A4@`t)c zpku)DETy`R()>9*W!U<{bD7=ie4(A&z?5p8pq}upW&iFAU?&3jwLYz)5~QOnXLkD* zrt|>^El0HPTh0>JVC4HhLC0MB8Lh|6J>T#hx?0pn=K?&t@~bmupRXQ27**FQF~21b z;mj3{o!ZkQDy?8eiiLMADe2Gyz zzOp@iySFs0^rnU0`*LOO+D^rWy)XM=?zR)p^r>~&FQINlhKPhP27?uZ!`uIWepIrh z#t!m)3wWP*Keh-7_W3!g#~CxEAmhF!rcpVejsk7Yf1y$MS&5;KGEZFiL6$8;xKhQl z!@8i8fQH-CV>YE2UfFc`@k=WJx~L6LVRfl6xYRi$X4=^PH}y% z1(|IvPUQb%j4s$w-Rn0c>I;saBsKW!IK`>)1!Vil^}_P%fzBk!0oU_ zv*Vj-n$CS9FADBI(1H9M^rhx=TM%|Sk!J6zY+0R=GZW^%u zZyh2ECIKZJA?=zE*=C9nzy&8$BKzFzkhNVTl^DCW6#o+cySgNTwx@7qFJ5nH@{MH7 zACRJ^7OC9b-6du?xi%>@7{>G-K5^c{OerZQnTSm@LB@Ua3Mcs|lT@-bEy8GEOn#l0Vg5JE}a#^AV+!An@oQE}sRvxDFR<#I6?uN2eQFrKvBp=(w* zu_sl=Ur2Dq2Zd9_q(Rsit#gV{MRQ-TR{%@4FXGiqSf&w@qT$zb&M`Pey^2@UF!v zO#my=`sctL6J&5SGDp`!TduO4K=XXtsrR8w9vx5s4JW-FhMz?-oI^WM)1Z zgV&5uQcj;_s3G$PDTwo$p)nr31Qh^azHIJkpf2 zA|^vJ!O(efd-Izg(L&O9QS>A0!fniSTvs%MEPUZK_2)RRnH||!GTB6STDcA2i!Tq& zV@LV;2)|a{xzf=pf3x1?l;}+nEYlORPbWERaCF%pz=NVZP)8QX=^ZIi?dZ`;R#IDQB7~fb0Te&_N4h~N2-u)ByJsQA*78;6yEKf%otZd$L+ZTAy=c;<9)G;z5w!Fl zkQgMt-3rtWzuVm z!;PPnegXJcy-gQh$WIfm`gmvOa_=DB`0$z^6F}ga86&f=$UGT?@)QAShP74xyox;Z z1zar|1(RFS&Fdr?LbjF- zM(b!Xy6^1$>~Z&tZOZCeDC|$MaX3G}YV>OT(u$k6xx|0;G*cIrjFA@x)NFaFQN&Z_ zP_Zlv1B57C-*4n?dbUfvTjil#%zBnp61YNcP)<{;%OYw;h_SEkUJl6XE^(is_mBGE ztBN44E-;K_d?Y)=x%B@1Sie5Y_F55657SQ|kxF_Jt%lk^x{2@oj0eSX-4he`q1UxDp z=>5J|OeW-Ip`NK^lkbp z1#RdNf|*7$-m1v0&m-=lB(S1CDbm75=m+rNw$#V0UjU9sDaCXWTo$G9Rs2WVjgm~J zs1jfx;HGAVYJw+)@qjAu@;NKqG1q{y)&upU%>9By2JU)-9F$0R%(TwkvL^6x~8kieCMiVyjwqpulh9`MCSb zK-?0cRc>r_V6gm3rH@+mj!!MchPMqjeW>xg`nEpmPNhU9Rq9dbH&5xV$3HkHp{PwB z0k|1@R+SKRqVdfBXMaE*d=~(E2eNoll5HjoBv``ng~g5Vf3Hfn0S@@{CuLM~Fx<8E z)t*zD6O8@OgQWpg^^g#hDTFPDYzJQnzcgN6;@K4Ra5JvHIhR-SJL1X}YER>)y;FGv zvXX+(xHUy)gmvLo2wNxk*88 zxow{1G)bp7wC$HKlZLsz>&Aa?&?X8Tuz97DiHN=gn57c%-P1BD3)A zV;FEpcLa#!#0zIO!>cd5?YpGeb3@b0h+B7vq=?efNs-jbK^0tF{X2MEM&|tZ0kHvX z+iMt5FkEq;7V2SnY#`pxwK{i*5;L_O-rVd1YxK=-4Q~L7OAYt}!>mY?_QQvgg1qT- zX>qS&0Q*dB$z^%#X`foYb_x&mtsH2N@#8~QH z-u$Ccom3PX|$!F zpS#-SBY#d0mQmI%F9tSvu|yIN|0~3*=!T{yqc9=VR}@S(jwFmXZxcQ*8WV#4e6Z++ zJ(&7g1m+eh)~1;Js2z%icdrtqMGkmV*FOh!&)b5O-ZDZ+Fh-yiSV`$&S$P7_Dd8M% z+9ANcK(~69ko(-PZi9Hmd%8R?;jtOT{I+2733CN3k`nypsMLFW0-R5?P zQNn9@ibSxp(wFug5G%3JK zzQkfPO#>w3Z`+tmt3F+IKQ{+jG18XONU0ud^?N=^zS6rIP~Wf+Ur7GB;c9()Jt+pY zPaL`_oZMKhWUOh$d^F&pAIDtB!n#65;VKW?+aVHwuy?V^-T>9Dd}7G_2y?WErL7bFzXEv}f8)RvttUnJf?ReP{u<@+g@qtjPqe^x_xj6WJDOZ}5GASSJL0T;O% z5LDbaXFXVkXN5GnDmT;@@fLT;&4Rj(<11Dw&?Kua!()TW87?nXAZ%luR6CoF>+MQX z=O3?>cyu$5{PZ*%n0ieO>F$a?qidsn*rq;rOCv>uS~`(kv51V%{b4Q*e(wm)L5nH< z^I;;Y`1aUc;uq^Pww76CSB^gmfTqaIT+TJqN^P+?e=;?>A@2h6#mgz8EAHf$yJf=a zkKEOF4z$?Ph6aS!ZUi9GaAMgGr&Q&gX^1&27JqG4^}Nu;71j8+>lySXxnL!%?}<~F zLA&L*@;Bc1JibOalhGAfKwB4+5jo>k_v&z{PBQkQbFI?J%v$)|YvzLoP^`87cL2)( zMX|ugg(oq`vTemgM2$RnMjWkFQt*`ri>1nbp=H=g8_MN^->PnG-%ezBeT+|h#LWd& z*QMfC+*HP#NJEdbp+#7x*v%giMrgo>025AaC(3n5hG9Isk$`DL3^t&*d5|`i(_PyA z032*A4kZRAVHxm)sBoofC%iHRy3%Dv524UtOTUvdT=p~~@R2*)4>u^~0hFwbkMG&I zZ2R9DNTA1#<2Kd}##TO>4u#_a=U%#bY9)-oVQw>3Z$eAJymfeckFK%n*vX=kUVV#kbx)zus;bj#`+ znsLaH`ue5BPN%}cmC=HwmVD(`n(M_eS2e;`U)W9$JBNK>KZgGP^OGw& zN6SHmT0PKpls755cw)cC@fGYh(DkFrbDx2s)>qX1O0s+(vh8by3TWL)jv}~@`%UVL zz*;Ig*@_NH8GgbfCrKJUsOShSIqiCtbR!rxuWi*$VhtuVsF>J&e^E%K*zb-t>-m+n z8mkAKYEZ2kBCO!UW29u>(==#@^!_fPNVzl(WGitILFsR;;>9|qIwEtumxYbO&ZQC2x-lZ+7)$dFksa90! z{){~i-Ivw7_*f;Wd)&f>-3~=1RXn-YSX!`BqyiMKc-zteUb@HT3Nx z^8jf;-%W+_`(1#-axQx^*hyamYY}4WS0JuOpByKG9^Y*QDdt4YWu%i5x zkl788iT^&a9u@h|EEUQdy2QQm9UncfY|xGqcWvbs4+$;@%b7_tQ!~SC^hWZj29uT4 zz98Fl9D!m@ymgs43ZU;P*2;d3<8fHpw6B8Zy;~w4awMQEAVz1RlZqORC-JR!v-fTTnrKLTe>%iRuy1c&;h&2LUbfyt=dC@4ejv6&Q<{Bxl{ zKD+j3oN@fEpPQo;!rbtc^*>0jsMHo73c6FL1VKw4Oy4zd*u)}tp`h6*iR z*T)14LUmA>V`wXxpjT4Nzc4x93FFj+ds5PdwLoP+{&8xG5a%x*0qJw)t15tSHgnZs!Y3xgizaW zb4cifo)jc!zBRJ4xd(DWF-1EJE7&mBh8YQG@DH;}8vm8NvnAaFfgoWS-tadwrpFW!r=oxmWbYpinKafml`8Cno=n!xj#4QclYs+K>8TABx?@lQkQbn0gjD(k+idS)Kt|8!CDCX{r_Wf$hvl4w zvCyxC!6M;-WfcXrnPM_V)!MdV^HvHj$phNbfuJ-3mEwB7!LbbAGHEdVMY1?yrKJG~ zrD3i{5`ZpNwmw;}g~g!I+E%bdEmTO70o`NGx8RNSzAc<}0sK?Q)k*oFT$m{(* zdey6h>>8ig7C)O)t$3Rg9TR0Hw}!niEW=uKaMWUg8&LDfuW?DnHZlB8u&T9^H`?XE zlcbpL*iWqxiRyifxg`N~OPanf;U@aEu9J0jlSJmEL%FRe!d6ofpy2P)71VIW^Dpy} zG{Gcsdbt0#)h?i?X>`6kv{n5Fq*v#l^YWql5WODiDDSu|iliLpET;QZuOmI~w#D4c$5G31AQ zRM(wsnuF`{f<}j$bbS2C-)ZZmh9csoPS|{~2%}`EGQs=d`d2^1Two zV?&1Y2IPf8ZO&Ho=yAsGk`+0i0dSlLgzgb<15-^^_9M&A`%Q_vHDlr zhUcA|*qy+jAi`OLFgPo@r(1deer{>IH4oAFYo|H7)XqswUk&Nf5KgN>ZjA0ZlzfKF zyjTkslK(8Q4I7kZLLkSkS-RvR`69-J}i-F0bHw zN66Q<0=KJqUYKl@XkXQpM?0L-L#KML+NI@+&l^3>UWhfw`th3$jmgxW4d z{G9ZyDh{Y0e@L{hy_|jZ%|0Pv7#O-Sm{6XT662BF{^2Nse5idfBx^Cj6Yy;sH~XGd zR<1{z%K$WsuxvPK{daKm2kmPnD`6D!63IMyS=6kJaiQ5gFa8*UP=uwqt!$S}Q4kH& z>&;C~b<~buFNkuWb_p|P>Yzv1pb|<#1M2ZJ`&Nq9+6HVfYB`3@ME}PTqCY55%2~@< zL$?85N1YT+nCh^Xgz!|Qp+`zA=&z$zU7#+c$NI34K-p|U=hCAu&OvrH_ReanwUoi) zQMSyr(z<8kt5lwo53X+Q=a@wfmMM(M=^4$*VWP30HLr3~bu26_C$$UaNio(oJ75ipm0)Dus0{_8 z$1MQ)Ij!}&1f3qzuWbb~JtMsPPG|z`iH@zFf_K4sbZ^e}+|5dZ$wgLQHR5LVbiunx zy$GwOQe@yAVoN0RwwxsHa6P>a7LP(ddt!3<*i4y8p@XsM?S-pvdJ zDaq`#UM~pv?Vopl&ud(N%6==y!N=!aeao}O(c1cAnJ0?ENulOOo1UrJw2zkeg^f^{ zXXz#Ixu*+V2ZN0fB9apkAPzzq-+{>v6$SN>Zmv#O!%SUfP_(P;B|#EPMJ@vjqZOH7 zu{x_8k@!XLCGY3bNMd`Jog8no0T~;ZBgiX&Lg|?XK)kcbuGIp%F z2E*t9A`lR~N9&VvvY~aTXjtYO;Xe6d%-vu~O|$u1!G>YO3iqy|gn?OqV-dzkwXt*_ zw`zp0Y=3UeK`wc(ISn*M5Ca=rd67VH`$piT^8V`|>6+hvFo}tUT9jWb(^wc&vRs$# z^hWsKCSaw)dtx@aZ_)s!)}T?6R+gL6G2sg;2a_-Ohl+PQo&-j&NI3WtLk1r<%uDof z%QP4W{=D?kJ!}}WJ`ncrNP{yg_F>%`VQ*KSDAHP~OTba7K7sX{I@Nah9XIx;WPEfD z5ZT!6$hf&W8Evcs5OLq0WETM1k5MjI@K-EPbN8tLTq3xMNF~QDp7*Pv8GZj!0MCQXl3Jfx|C-Wx5=hq)+J9A% z2}u!ydZ0tdWie^|w_F40vxA9G|A3CE;n=h2gR!xQCkZVY4 zeKVUa&x-|W6r+=2r8a)ElPce)X(vx^wVdM`>EFB%<2kPOUu6Aqwr$?&`6sBOo_qh} z=-k7Z{Qo~bktL_jkvSB#N=`X#jM2d%u}&y*C`yiVKA)9J%;|%2n93vw;De{o%|YnR>k`+ncA*X#X!KAwHK4CCAr+ z?i|2B@T;3$q9n|;p7~C1>6ntl7D0>NTn%3MnVt) zj9w!Ksv}p#r5^m6wQq^D%5t&2!uUh9eH#RRjj*HY$lk2O46*YbKi0@Ydp#3M@N%m? z_VHCskyYA4=Y#xDhrixUErE9B&tzwMjmqmNRt@dtI;VNr33t{!dYOLLQtB+{5*!NZiBbdv4ck%oA~vW|kJ7Vp$a;fgZHP z_xw8u%AvMy!PbMWn@(FMz%fj4W5C|S@csz?mKS}ew>~kPUMlD+2WlY2iGlYbVz}p+ zQpMBr`J?JOmr51YT}8$JS}(c_F7&V4wm*AzW`}zxCe&oVB$RjRNw=L%Wfvm@>MI~)rw93}TDYZ44r?-#) zc@iyVvhwysD4_2=cotBoG;+IhtB}jjK8E)pzpq7eH93bzLp1@%lda5vJD>;_WCu)( z#`sF}vHg`!_LjW}R%;On4e{|RD45-SD;9d8r@+FTBIdRd1i+PhYl@Tp%O)`zi6x)fT8U{c>xv95D3X_}*>#?q_iIZDE&eq?Nfh z@xevP+;1boqab|GsQV3 zZ?pd$>1G$%Qf*$*`GzZP6?$^0`Se3migin@RmR(LgfX-o9e0M_@!|aI4&}fGy>})y z9tt*UH!wC5cewrOBa2rEt{1CNl#8MF*yYNGz^1Yz)pDE@7OewpGoPU>PKbF|vqGrF z{aC<#bQ>EnSHPa9L+4;xJ2Ykk6uVQn*!h@CeY}xiP)FKM#WbLsv)t$7CZ44}z?_qp zEv3f&Hk7bp)7NHXJsu&+^J!D{^<{nmc7xj-Nx`Ce?%>pqm-pJvH&iU|QH}3iq6SX6 z<9@sAr8Eo*UL2pzFu(Xm!nw7TYgh37$uG=ngb)i6uzR$736o8@UlN$tVS5fMUvboR zcl*t1*|f$OAzYT==N`UF9M8X+6%vy0bbafE0`d7(wIf~0YWZD*8Ba$)NYLSTf+1UL z!{ypx5$S$%MR4Q7I`T4k?K8?+L2a3(D-ZjFtX74XX{nLLW-c4p_Vwocx@2(4$LdnU zO_!7gi^?YayaP4;tsfQtKO5h@QQyswtcX`FRhYa&_WNJromuWafa)-k5!dG(Cz^6_ zJ0VnCgnn7mB|Y+(linn2?^bxP6fh5p?&Ws;1AQy%TpJhcz=D{fzt_eI)26kpYY7pD zLs39r@?n?YlYNEnrcETx>5VN3rJJrfr|lTkbJY%~>WR-X;zEzlG$=T=6LV<059%HN zfslY24mihgLycp%DJ?i8`-0_4Z;3VAdMj){&jVd;_Zm3lskSi?mcGU?#Hf>OE|4A$ zunFkfMzaUkw3m!`wS+j>tNpZX?MnnI4>bmay&6NLkFjho-D%!&dBx>6UI7Rf>B zKdWLd7Fs`KZoT`^R_vAn1jMMD^({G8jgvi+m1By-^F`=GF z@X)l>5#=5Rw8Dp{rtLUK*S5~=XNIaU@zdZ=r8A*t*x#q|0)aKCSX<2jgfSnH@QQPi z={?+;5$uuc^aWp84ZIHJgb^H+xy5!(ZqkBnxBu5B(U>}^XZS2&D)>+lgUuI$p$ zE|~+!7cFnW9uxsFZR@qLVQiKU2gjzioq63}A;~7RT8%cnl#*d6#c^&@!U7kvuzwbt zkBliHbc)aaH$);%^gXT3bnA7J8><;oulNt|?m_Fw)mxOVL9SdX3-7g(Ubcx~@=w~0 zSGE&3rJzF`9_g0|$%7+SBT^SeL!SswOe(*-juL-d8h&<+1-x!5A20o|R{m-2^oB@k z7jlyS(SUX$pk6IvD_oTVN>sO)b0zrGr!Px&s=Omj|41Fa*&9-JWgB(9b!Mbi(cOuQ}NnA(3tvO>OT)WB$-lEMadyBGXJCcV zHi=I8rkSQ=TI+3 zVZxqHs2%(DC(bo?3l(q}$TRKQ*Ir{UGjm@zx=%+)vP z76!gE8gZ&@8C2JnuWF3C@~UZKD}wSlmjPU0A>dg-a(TGDw&$5GzjM%c_Y0e~7KU7! z5$uy*`GIaP08quUTEYSOmXj0X>{O#sw352frqap9V5R7?a$4j9vA;m{&D?Osvv)?x zW<}9RO2ZW6f#?CY0qNJN+$x=nw<~4t$9e!QEYN|WUN@^U?gv!m3cg64LT43Sop?>V9J;TuJqueXNudioNaneVs|E{d8aZG2je%IBR z>UK+BQ471=GpTI@)f-S74LWT)pe8S@Z8$t;9NzonX!`wgkGF1_9Y)lX{GMlQIEQFE zOVn1g)U&@WXo~gU`!DsDiP19AY<0KD$+2N4!c( z+ZJ(J_b9uiqeVnlCQyl*7+WnZ$R_IqN`M<)U73lVkcS5mrwR(J~uUdnu&Dg8ELnwNo$ z(UlI2`F%#cLjfI>x1@W4(V6ZhD^U zwDb{YsS7rWJ(pO5O&5$We6MZza09w?-RSn^lXpO%!=S@oPn_iCl>up8oYxjf+XT1) zqkAslqsZ@LC1oxxfX3JEc<9xCpqJTzW7OC6qMCceuW;E6l<~b^OV*5txGFNb-I9pO zh5TrD`r2CnSXHd9UrFEE{(i0VsAC2yK-$^8zxTI*s{F0^WRfR< zhSN4?PAyYfQ{!WjJVjI!%fg}D;atW&JjrJZ$1uxa(6{2*zfUJ3PUf+(fSoEl22Bxt zt$QL)^q6y|e{uf8ZmJy{^^2%crcyi1>a!YmztEEM`_=LfW0#FmCFyiF8pxX}K*;W? z2Ci-CFxEPJp6qFb-Xat*l@h|GMTG(l*7ock{cK_M7pV_0Z?3-fnpuH6HZuZZ>8|do zHkCDC)Af#X+9g$)Q;7fIr3C^$7aG*6#

    21|6r7V3Q$Q7PWDY1#&+A${HFot}?Y z)YSNgiGGcsBoO$i+{0;mkWM(6DcC{rO8ptC$mC96SUdTnhsWLsqpPWz_ZuNplti8 zGssxq-X&Tbtb+Ayc*?3d7B<~9Qhx5nP1mf<)o#C#$pxFT`)k7nrF%`};7k3bK{>aQ z$F5r3_5R_SGw;`r-CJkN{7!y&jGf!MKke zzuyUzHxGd_a0|ZwqvfeH0?w4~uoY)q_ylAJhaz%+Tb+KbZl2iTJiTvXGoZjyI%2z! z2`n0}IGcKluo-cLjkX=#%p#Gs@5fjUO9k53B;MiLAze^J67WV_Nn--gzr4Mh>`756 zHr0gDR1tGU{S88C8ZBT5j$QV1ce1V;ZmC2_=e-Di94mRh<4hBtI9nM)I30dFwdzi5 z@_yZ(U_=|x`%#K>#hQ0>h{jI%+Zc3}rhP2TOM{p;+VpFWW?lvdhSgsOl0JKf?n?NUO;v0nY6QF#;0r>_M9H*Jw z7%Tu;$eIarnl^0TQ+kyv?u!Ra1(M7M>2*W01VqS}h+pU+ry}QCOTavYeC<}AH-g4; zQI-Ed$NU#p3axS9lEzN;Zoe-#t6z`U2L!|SQ+mxLbN&(b{?(FSdyV5J#%(7+6_ zHXsq9gJ$+_l{3OCrjdsOEvUF6T=-=s^bIaD)RnS!$oW3|^IS*_6?rD`b%!ppX{35& zV2yL#c{&0-Kj@NK_^nuS;#34q$SPEW(Jr8OL*?cXdjPLwd8T|;9Ky-N=;)T8uB=`z zYgirYCK;YS!43Ak8)tm&rQ7f=q01P~@lUL1m90IycKRu+`IK zm(`x0MI8x?T9Ys%(rd-szJNqs(a`5?UNZ`K&7{u>JS`_l7HH-Az(?5d6dEv%5M zofZ8IlDOiq=f@pE)^uQna<@|0@0_XQx>6BnU>YP-6MmS1ipen1y?DlOiwb9iM}0Q= zQav-Tbt|UWI8Y&dFuX@*2Kh>goYn^(P$V4z29SMW1y zZ>LT+YlIJBRrWutuf^17w7-+)4b{$~ZYPAxxL8ceGMnh}YSKq0U78Ot2#M7uZ`a&? zk)rdOHM>P|zGXUB&T=QJhNef#v2&}3B65|+UAc#c_d>#Pw*1xXCk#8PWC;g8ELR@^ zxW;EhUv8o#7j%!E4md`(X`dGCGd;^Z{n~eY?Ly|lCg2-Q)Uz%w_?dXixFDHifMQ>< z&kdKox45!w(u|GATvyK_S5&OOSA4!nVkepnMZReU-`w-#oM9*LS#b;o zbfq~L7y%e{WIR`eDf(7z|LKMa0r6{&hJ5~XXVI#KbHlB3VY!Xeg^&bcDoqmycy{@-mwH5rl(&P4xe zv{T5*rrGRp#W6ae@wrT0O>ILW_n2~qKl3)`t-nXUnnm0~L%p%WN`33nH{x!Jdx4h`n73!%jzL#*2q>WCrF$s@m}iCVHj*43=`i@^9Iq@`6HAz2DTysgSWrV51Bzfy*j!w%^nRmo& zV^|PZ1rOO?R5l2c|6O#pD4Qla^{1V%@5+-+iwE*R+Ig{tKOP-XEW?4f2iFZtcoMlS zu@5(D`*2qg?XUMh-tZTu+O{Yp&YXvLeSqZ}N z(it@-PJbaOVeG?UU~%=!-{3Nuym0Y0S^!jr;e{y$3EuG#Yi&a~u7|($*jmC3$Dc_y zg385z=~wB7h!|6_KM#W;ATys^+ZO&!Rjwf<8+ZS)dcUsy2ql56 z1G1+|5)$jfzbS`2VrMRrv*vN;Bc`R5ht@xQ(%P%enXcWf^HwraUG`RZY#inE1A&Ti!rsT58KAoJLajTU_o2!{M z{lpgS&4BqAyxDbfNg!TVmHQZwELgmi7U@s@ep1-kQ^1l+MBw>7y9?> z+3xNMf2XJkPeZ^|DAoz&)==XxEu-=lR3JOn&j2#sjSC$E9MJWCfk8;T~sgV{0|YYm?VRsbJYn(`4zsyx3-$d zGpU1%WA)_F)zx9sg7D<=CU{#n2WQ8ubn5B&JU^!rsVC#$ zA4G=$H1GedfPO+()3r-@8iU76Z`@ zi^dSF*0x-*efAM8N5?f#yohQ%51y+-`ofj*!MuyBO0gHB=i1)_Qn+PX`m%Xb*GD-v zYN=DEr?B<&Lum8D&y@+889C)?%c->rHH&3Vd9EgzgDvn*_`#oINKDvI$&GmLQGd-% zI-jFA3N~%F`Whx-X#0NA`)I8bRCcMMkY6=Y6nWblPSrfQvos80z}B$FmqiReQHQEt zrUYwLjcPBIdQADJwde$|FbgMwf_&Ejfd~y`S>PbUwj^J#N3LXSGpoEiIO40As?^GFZf=hZU8{;qk)bTtQYQHL%?UXJPLfiEYx2;>i z-eebaX&PN?pmv3|)WK2q7t-L^B*B+lTz^TK4fj`p9d2(Ygz*fK$#f+bgzBFs9Z+ zweQ96i>+NCr$WsmhgcwoMHOsJl$D*yl=bfh`K8r`&t@yyc9jH^v*it3NrI_ji?yWM zeVUM_+mWK5TNKB=jnu^8K@1?0sG0PNcPcb9(1(fZMqZ+-|GBOetr^?NFREt%qQX4@ z7QEyMx&fSki0VAlALb43qR@)xy>O3HtL!Yu%ciHnV`k$We@5w-_I$ zx`>bNDI9y&4$J=gw?B9tro#aL1k(C4#2sS#1v;-g96lt~7y#jkk2Z)`jV~0@6AEzn zem#Dpe>qKz4g*1s+uZqDl`y%WK3C^amt`3ix~n-_Rz_&9cQ0=WLpN2s&`nMbw93$u zlZPQZZE{iq%PL7)=Im87{=-xr!#{w`-%RhI8BF{j#*jCCmik_i2Zxw_OaIjNv>k)v za}a~3T1;nQ$M5mDZPJbjAmT;9X73T}{s}LdN?E3UTx+t8TUOYxzw?Aw=*HjYg-!-14!hWV&D4jW9KK~_YPzqP|ayf_&K2`lU>0J2TNY{{nUIw zFdlyYy~t>{USiIedacqTEO+BYR!B|rex`L<=0b^$byI(t zM;lL!!(d`Fh!4rAgm{r6efl0+w`xH=5GT7q$=NAJVlesj==Lu(q4g#2L*{2aVdR(w zxeYvsMor=e0RE?jsA6rd`c`C!>P4F2#{uzGiX z#}RM6P98i|&fB=(Clw3l6Y}fJuRHi1>us&D=!G>}z-myT@htko{{F=^2A-J!SK(2Ft~2t%@4T zfrlv~%Eo~7#{nwGi^=ka-{m}D1`y`tlan``AfjtRRNbBj4?= zfQ;fC+?>zTSjOJ(SE}U!F@j$oxvU0$*>2@HhQ0oAqLre-TTdPE)4HTt(+w@lS#h5V zuO)qh8>@}{hb8_tEcL+F1`I#g%LiC{B#}eHIJg^cG^}mej33Mw+IZxq5^=# zvfn3pBn)uSOX|0lnl}#Rft~_n?Cj;r*SSSms{AskJuDr8zROE^=~L-r{(&^DUl%ek zukqPEvu_3eJVSYnxWiW(Q|nD`hV2B9EPd5u-!d$N`;9)lh^lYe}17AAzWGIJ(dy*qtF^yh{8O8nZB|HrGbuOdY z(lvc|N1l{n>8|v(8UMqBa5ps|;I5^GhnbCejiqy$QbW_8WXWPM;_z(?0%852tlvnl zvj+-2LHtnA-Rc%r;0=R9EMhBWT>r8ps;f;xq%(Dfh}NGUt4ajMo|)6z=8JdW>Es{K zbWop5gq`iA{=&W#l57m^piwqugoIL)Q+)mE23!a|s*MQ4WSqvp0a4`rsK_wSo0e7n z!RMVMLyBB&f_F5VST}E3;hc)x6;|hmi8g9$99Vmx zw(v(smd@8t_4Yq4Tl+=yc6?I)!o|SfaX;HikNXz!diFXMBkYp|9YyQ&rjp|~FDO;h zQ|GlPoJl&g9NyjUd1BpF;KBHY@py%{Oa**tC0CNbHNgLp8)3FPnzLW_#RgS_FtHFKd5tPh*c8vxo| z6j}YqV@_-6lB!BS@YJ}27a)}e*}tlVxh#2u=w2I{SNBj(;Vs#=grB`cQM{U%c=v(ZA+;x+9?8UM19^ONb*$nM)%HxY=-e= zlwz%XcJ{uq4*&xay%y}mTLF~nDO4Hyz|rWCs5VxAaJ-{EgfOcmU*z$2rNv}&f5lxB zXo^2JXU(sL5b+PU$w9hH{ripqhBD9ed3i8mAfJTsMc6VN>cKBiV(i!2?kJU`67$!; zZ7s3Uh!^D3liwPt={OxU@3Tgz(Fd_88?Dtqvd%pato@iLO2;!0=!THGLgUWQx2)X@ zDhSrKXvHQa7Yhq3t6mfN^F1jIO{_ti2DC%5@lKbFL;PO0LuxmijDwTog&c|2<)Y_} zI#JiO>>-e&+fkh&p8Ua>yNW1YFUfV3B*0YAqpt3H^;w&I_Pp*#q37BHkq{!FRn_?r z;`n0OEU6~bYIkXOL7dpL^Z=V#HDu*lR@>Yg;`%&5OL9O9$ozvo8V*~4l)pe7pnR?f7vaf=9_{!+^aAlR|m~DuSynIWL zq!O`J{ECraw9mi+qlv#To=CoU7B7%S!1PsZ(>7jJt%{dw&fnz5)&U_#=j-N`gCQ8? z%bYro7&(#4H*)=TJ#qx#IMWuDrDQ;gtZQrFZ1>Dn2Tj|okI3QdzJQHdRytetnr_aH zR%kjEE&9%^CGiLNBZ|TC2Si}YhiJQw@kSosCukx*UE&B3Y6n_-pf0zHLW3b_%2FRa z1;p(GmlEA1$35$yB{UH;)b+%z1}2tc?*}hjS#Yc&6|DIclIs^n>&;~gS8T`pO+z(M zy*7$#1yw_2X`((~ovp@dyU@C)^7vY6|5emU*r?)+#J$BEiL%??@N}6&q;1E0?>m(> zVkRykuy!kQ4#G(&aI-$;7{J>|#&|`VnJSYVpfdI#Z*7NJsLTO=5id-bumH(fm)JmZ zD%)ZaTon{2;{dn0>u7but?n(;5{<2)-MsJax8TSzlC>_+!RZU*iMNjo9+!~%$X$~7 z;pvqb^4LoK!#E--x4JxZ+i`mU5 z!mkpYry5qW&zHW}$qEzTBei^HX8Q~{fsS`trHq#>G}V}%&vOOhKt)|cO4MqCR~SHg zry9v3W!P!Z`ZHSn$Do)KFHHGXDX!?ez#|?Ief1Qy=(@SpY*c$f>BmbJ6R#o1lnp|J z{N)_Pmii>}#kJpDxvE9+?H93xcP*<%KM1hqHBU2WzvSE1;r-=9Ajne=Eco{bsI^~Z z^pbVO=8A6(!ECpzRPkA1@%ng~f33nwS?yxQx&^R1Ap!p=FF@fyj>CW#|6y2{`qBDs zITcwQ5!k9~DmD)ap@Be9^(4PTe^&!7Sswu{R=iLk7-%--b33@3L$6$-fqPMXzKthnRN+eirPWv0=;(L(ipmJ)=w%MKZ z)_&fTceg!ZZC|%R4t%k+=G8;6F3QFx?g=}pw&;1;28uqogZ^CohwPYE=5A58nw79z zZ{57-?3%ScF>}!;`KM`QUh3Z1Lf2Cf0UD6|EvtUB{4YPAH;OIA-iCpMJlqZh(luNQ zl&#a=$;L%^Cq}KA@_Yur2Z8VI7kRf^Hi<_l77?cA5D&h+o(4; zsk@hr)lS$BHd&#DxW>UHb~ZI(!ZE&W`ob8|Kfyr44GMPNP(C_*9^`ZqyI^jtsDjCl zyc!6GF7>OfMDvtd*7IejPR-39lA)z9VZ1td$gQ0MQRVGNG;^A+Ue%6N?V=?AJ<#C* zuAi@+XOW63n(7-C(^&Oq`%xy<;pw%mQ`S`#h4s}X9t2WI_A#&+jk3QCbPSN>WP;cd zkQwHejX5MopM$si(l*7xSTgt)&b5$s98(0-**8W|>m>BW#b~LvCYXF*>NmQ-YIYP3 zjC`|_=HL9z@L53EYKEb24$<{1X$<|fxy)+Gqh!}KU}$lRR97cKl9O!HgK^it-0=p< zY_0x1`$JzHyOBDO6mle_q9=%!%{bI5&I^w1gRjPJ#M{53$O}n+oW_Jd>73qQrDZ3Z zmg_^3JA-6|yu)Mmhr&R6;|e;*E7R(GdU_t0*jSX0{bi&UV#IJ|=@+OHKh-!cr%Png{q7pNr^_S)!1oL#^Wk83zKW>Y6KW(L@ zw9*{F1F2C-AhEU9ZuMKnd&O6b#b_YIYY(x9#N+`P{%?6wm~E4i>-s`tb+Aiq_3ryy zMT*mV`!q5TyvZ05PcNKUEB+%+^1wRWimZ^Hwcu)P zIJUayZ{rW2Ai4DAmzL(rTT~BKnh{<0)TgYEKUM+r9WxWqQAv(8lRzkfc=;R;G{zNf z)a~e_TbZeND4bXN=3$lOyT&qfY|aL^U)kHhC$(R>08x>ffVn^ms24 zAC)&X)us>(lSygJmdt|+ zsSr<;E(h}=pF4NBi@mlT0N1s7^>b?WnH z)j_pmPq4b>j`6D|!D&1(Q;e@`7ZJ2riioJgb`WV5##hzqA^q(lGpBZpGzHp_^s+K!&$3(OKH52%ypy2BSB)g5o$}J zp`<_;6*1;s8&FcYT2-r5x1H9*;@hkv9v!EpYIJd%-X-Bh(n z)Dx`tPlThdoG%2E<7qjW7sRt;+=@7bzo&=0&6A?-xGBb){OEo5|6@1&X)GKn^x(FF zQch{JYepC;%+e2#YqR!ZzC7LQAY(BZ$gUqf5n$4ZQH#rml*s+c{DFMbNNb(#BPJp~tx{3O zBu8>f`D*_PddfREJUCcMb9-DdCg&H;;V?)pxpQh-c$uu)DtAzfdeT-u780qWO>?S= zAa}_EODhxei1)}xl{QuvFiaGeR@CId{FDXHsS9cZ$L~)~15h!35&q?4{905#!uHvPgB3prDBo+%jMpt`rp+FGZB>d3VRz0 z!`o^(4*U@SMUep4nUD}RIGCC1VX782#SZGm!IxS&+D^k*XVaRBVR~3;MkK6fBnE3sHqgXSPZF}-CrcuZ*A0|cv1|OG| z1)l1|#TG>+H>#vOAJs)IihBog&jIhyH9UwH@2RS2W_OC{{9}kX&RXPfXZ&mGS?C`o zrfC(Q8^*d73?V|0XcQa~lcdru4vYnWnY5(97ZRU6C4efIBof4zz3U0m%_%OWKtxn9 zINoMsUNI`4*8nx{Rd$URrnS;R#&-dS0Kr!9-p^bIi%2C_ZfV?qpawj=?@O7|usK@-Tz7c6ScI`0^zGfS|cE zdmpofVkYR420rmmyi$hv2UAn0e&QwnI!VT=%xll>y~t|bZqfL<2dzkNO522|6C0|> z-8g&BVZOp02fl*vGSqaS_=rgw$Ms@hlEUggLF+0D1HP1vUktFB;q4;_FF}r&g&a@i zusQ!gikCHEybvHWF~mNb{vEzn3vRN_bnTgSby=Op zcW4_pv4zCYVQ-YJZjO#8Y+)0BA20s#9|$`#!2sGYrxC#5`7mr7FUK5s`0&`) z#tQ7X%-LApl2@(oe7qZitJh6Tbjh+UHt(GaxA!A#v;uUFB#_?7xXT?A=Idj(6UnL`&;t&3AsCakZ>E7Xw|z#?n?EXfboU!td-|y6e4sKYI7@k?Mw#0eu3j8 zRfaJgRAQ0wE!qKPlx)lOPGdPCsh!>I!=32kfsWf_NXB6T231C2Z!J3KGExJd1@>2_ zcnkcfl9i1bTH?fH$HdE98|HDM{(;i}fe2yE23&3{_50mu<&X4)iA@Rx1Cpql2#PoG ziHaFu4(sMS72VT5N`jfp*a-)O+0*`k@M*;?YF49&*KSPf3F+g3&Q~>;BpIK75oC2= zuh@X=wk78)LoV!}1eS#`fB@ZV4l6Hrt6atNr4A=P)rL?BEz*zMWyphGgq@9r?uB_3 z;t$|F{{o}yj+Ln^xcrKH<Kj7)AB zHzMnF+U9)Kk4;wX2Oo#v2qk8oD=!yC-Q+$FF-NkEIhT$8WHP!af`Zpqy|TXJ*qgK` z?H$~s{X#@8fPU9oGRu86E>%a54|85`QK(=l5?0kRScA^&pwn%#d`U>D5WjsNyv|pB z8QCxR2vmhF*ldyl8AEgAnvZZVar9J^J3lyMg1ylyEY8k?9#c97^{DYW0l6+xIS;mD z?^B#GFq{lS+1gcoa{Cquy-;DjRb6`OA1G=+!tU)?4?PKi`#m4+jMnLJ*WBX{HEK;TKVD!#9TzRSy$OfS{0IE$?;Q0v zm^$W~T<}ohn1%qau;Vxn6&rMh*nFeKqU|KZxgq1r|+l z&ud7>g0~VO6ngyA?9iK(Yy2LZ`}n*2sfF~~{Tu&4x+2=lo8>Eh!%hp_@J!ukDtw z(Yk>3Q})5M*jD9r=pMjpVlT^b+qBS2z;#xcl1^U8tUevmp!CYFzR@?J5fB?Jge|w* zOwnm6HZ79-hOqmoSnYT(Y-4sfVmH|L!b_dVC-!d)B5EI{Xio>WjaL=D{ zUcgKd9l7H8$1MJs86=3w&6sHprr#P?iT6a(&PN)xbw=bU+EiQkH#Dbjqy3svLw0n( zD3DBZd(jPrEuYW*4N0DgXG#47nQ>U)Z$*3{|4xyREjb5LGxB{Apo}u4$QUSc%(JOr z;*<9)5d|g?_QOE>4-vJIV>McHuByRbg$KAym;VR_Ht-}hQm*;F-p+j6M zz7n1`SX*y$!h<(A2fTyqlY%HQx$8RGUQ1>$Xf#On%l0PTw&|yAt<}rGcdf7IPTOAM z_~yVIcO$w?FgvpF?L6)=6t81ML11o*cf^mX!T2zBDKn&Wd!_}+93Wtzaw#2TU=Y_G zq_ot$>LEbG0^&mCW3FL&*t@jv1eoM&zze~NVSUAQU=Q&z>}=Z~%#HQamAV}Vy3PD`D| zhc5Wr=uwlyy%xd9*dPn0QVhx@_eko610ZD-~9Z{*5^(Lbkqk32F65(I#@8z~|@Ab-3Jg_$`i$3Vk#J7nx*8c;uW?GtOm zB?Fg;d4R}2^_Q$zmJJwN@1ESfkn?L>z7yq@Eg~YU4xWwBO*Fld+EU^_HAg~QtgbZF zQr(rN{tQ2d@p^7`uM87l{aOyvWfl?dX(u_|(vPLXb^0Cv28s{3HP%Fk^Vn-_An(d+ z*@}-e<|3YLjJRxgY{_vqM|ULLZs9eAuHzZvAf9$=i(3;>L22*yW9>En11a?0G+IR> zQMRvTghUthK0<9z!~j_LO;Dt$Y)t&R1YG@2U}`&W%!NwqKhV4e8E~2Ql9hnHOiWU4 z3@IUj=H7yBig64f)#CoBu}RyI7fKmBOZ(1CBlX`S^}*nZUd&HOQb*|2V+~J3F|xm= zU~+%3H%QI_;X^^7jVKIKy=Tp%p=8Tq)Xt!Kc8TCF7WNNBuPxfF(bAzp7-2}jgS6We%u7PzvjW{{q^|Ad_k&5`Qt5&3F zOPH`sx+QOF*R^m4{^@_#pV`_?Or?=74&f&>X)>4_B% z9@D6Qx9P}qq+U?lg#wH2!#~A|nmnmE8OKeC1LmhpuvoJV$y1e3w6FtQ1gxF71>I8u z#mnojoCgg!Yo-E>b7oTfz3NAG(3N8vwE;sm)WySEy1jNe1bje5YZXck5(%P4`}9wI zmt@9c$e?@>KY3lb6$h8|hA&z|HWxvX!C#*B^Ynbfftc~hkt*#@7=MOAP(jzVaTy`6 zRu6i#9)d;}-sGdv3%5-TO>;6VSIh1-G>yG=&l@kqFBP%MTjr`7S|@mr7h$qsIOu>X zwnl78B!-kA>-XmEDkxa%la(#0e%UAM7?9$00=zt2l#Tl;#q4E1zJ@9vN&?T78YhGwg zfWY9oa`+uB5oPZ{J}22Z$^oxdF<5sb#POAkJ*(Z@lRrqO3zY2;=S`O_oN)Xm*d`I# zF8~0bIt}5rdMAA>N9_iyiAw;zH^ip4;!EcIKagPs8O`>dQQ_mGCK)+ouXK9F$7DMc z>F{*A^r*-}b8uo^2%ZHjxF@L7TeteI7r|af*P~9xYhGDTb|5|)W%a>$0g&y80|Dl~ zo4PS_$CGroya7BhYKimPcXMT~v21L2!D;Hx4-pX+pqCEuqs0u^$8U>^b1#SM+Z)Ov zH4afzC$OB`(r5$)z5#Ub{TqmghJ4jB?@1o4T%z80Iivu8w# z`b)zG!Eq3gNER~WJX0kA^0h?&=LHoqp-UQc{!sz`QLX8kI^feMx) zgpqJjnX|0Dk;57;sGZy^a1FNd=vVsO(pY6Dvfx%$IkxLe-1IJno!8rjiZ2R4hYLaC zoiN!8#G?(D&0u^O{zKF&0({3j!$g8?<<-7fNW~q3__qQw$&FWFsrPpY8h1PYcRFv} zD*2rwM~y_8MLNJmR`Q1jw?!`2XZccPqE8hpYSsn=(yr;WH;ah=bj^ z|3}f8MEh@8IvOvM5CYMTSwQ?_=%*+LF#Zc6#rXpNOZ4A-U zG&7~meZ#2{tr1LAQbZ^yR1`OqT<3Rx|9Ipa;2b{p-p}%WKhNt?)7+pquv3RMzlDR5 z5}@?_`t>t_>2$`Wb~g@0`V6W|(mU)p&deq`oQD3+Qc3OL;Lo6L@@i#7Jn^{X6Q(gy>v7>T!@ua#YoegW zRcGxRRfZSL_GZ_}H@?GW_8;pzh*nl&8wK6lxdxn7Tb_yLEb5B2n*kSkohKZ-k#_T&bVnBnLnIfG~#N1PLtPbxUpN#tYgh%&nnvVf zCvv$FLon#|;9~DTNPsKMfu)&W=AU1A5d=y1b@kmT^qupremkNC1JkaJjcNLF#s`y< ze_5!E2bI)GuRaXu=KM;V6cqywve0J`-C)+;W)v6B2im(0Fo-sQ$*|Wtyj&Y(-so-? zg@p9R;@u<`ESbq<3}85T(FVNWVNUy@0IG;x&zn*#e+D+eqx9Z-TKEbdeHoqH+2i9N z5l6&^``gvbH};KGzfQ?n3=A$|klx?C#2=3K64C$#Qw)G&Mn;p^gD<}YH8puC$6MLB zS^~Y3+bntB%^ix}m$dqBUTky%2g`0W)B?_SDE=LG<028&6vvPx*jh_jfK2s2T?SQd2vy?PNLt4Sb|#(cA%09bOgS0Mh!<-xkt! zaj_k%qcUV{=lY?(oO{6O+HEo$x!;f+Mq{Nm%j zD^)q!p8{j#qKNnwmY~u8p<2%kpMX?rFp_pE1L|L2agWG2vi3}2gI7E;L;H&HK3fL@ z;q1*@m)IebWs)4$7-0BD5V(rIW{)O*ofmM~KFu~uXqK%v#IQ+RDu!?Uv@Rn#T=QxC zZB%GQO@lN(R8ln|jWEtAy>FhqB$oQej&35}T)34)H)Me#x)f(G>t5#hOZATaye_`# zhDmc_K^r2H7K}4#WQ8!flr_&>k|w`?MmFPPNRMoXgZj{d zCOLfjO9ut#sC`#;()jSl&lNS5RlJOapLQo_`rD^lBR}d+*Z@2U>JWlUGs}9c+@lu_jE1po3{I!j03Wl!hLzkn5OW~}}Z z^2(G&B6FQSRG#y&9DXiph`&*Oqc6X&0#B*&@5`-?C!BvHtm#ZCJk#y1{$Qcsnf+B2 zq2Q5r)gJDOipp8LEE2cdJid^YfGYMs>k8D?RNrh{nlbY~;dq)9aQWm0=Wk|aX6YZ# zTQO5F!7w{#f*d2AXH1n)I&xkqeEBT7*SYh0)=h;XXoH(&8kw$9D7Rh%!5!n;cv0T# z*Kve!i>DEXwq$#tD?Fsl<09AUG0D7rXKZ-+Ep&ZWAMwGy)40vFgZug*xh8vNTt<8! z{?w1v4Vh-};(<%4P^?)-b9Qk{qO>}`1BeG_El|O4s#zd57D4lmL^rW%A;<~T40goL zrQIf>{aY98P2Bf>RhNdrjvTzc81d$q_iK;wnwEQ#QAy3XW-{qoOqxvNEoKyB`J`i+ zlr;oEhw+!n<*&MT5Fu+x7MeQdc7!D(iwc%b)4W`*Y=T7T6XWL8Hn}v8sm4GS9^_hHv3GL(w;_KAMTQ#)tvt%{yqn$_j-OI zDNSE1=%n{B{SQh!I0KWrLuS1WnIaWBg^i7Pw3oeh5F=3p)eITr!Z zdDDh`HrmmKJ|K9zOK=jSgaVrd=4+sE*`+)kceGAYXhTawe9K)GHA=gnDicoDMiv`9*8Wm`u1*s< zJqEV=S_ygbpoXaGoXOUi<^WDCsMB3JzCYUb{y%qkm=R~>rbIb>!R@je`P?9}J3Y<0 zGqQ+r+tk>ZyEXxfz$mv*T!4V;n;`ATp-t}SAGdTmd3FD~j%4e6guOR*+fFkB>1+w> zT#s9I@jX=NPfmIJI(-BI_eZJk75(KCRSkje>elV^10W|WoupHyX>v@sC4h_+Uxk|gFrlEkLrW#0#r)_IL?wE_AI)Qk1WbLelB+6K!blh>(77 zBi{qC<{ON;+D~2h4@d%n3^Rer{N;~b4ufLxQl-DoM0L1>Q`5Zse2&z&(L$Z~_vC&` zXQcRUgOFUpP(ps{o{Rhemu-N#AP;WKct8Z9y(62T|Jw4vB$FfA22dfB;jr0n(2rP$ zWCKuhTEdWrY`)zHQoFO%m;MS1g^yto?T}D5xjRmP3}N-ZY4A4*mjre>REN)t%b!$+ zZf^{X$jSe9yx4o>QH>z7;MkC{J1!&^l#}4*Fr82{9MX{Z*G;}PKM@LzBN;i~PHoI2 zcEbz=2WkPOp_?4kVKW6!(yTjq{imWy(a{))xs2EwQuwtK8P|qP^Y72S_R-UK?8!v9 zTJvVOOVwa^ZYx+4_3p2&Vemy%OU_XIAX2&HB_$JIkCUj^)%J!g#zB@Mh?06yMf@Xh z7&4RL|Lz+cYR-~v<|6od9$leVxBYf4ZlV!>ylGmr0!x-ddqYejt@hmY|NZ1*>dF_8 z)w`B(7>o$Yv>wg@@uuDljXjP@(j=c@_v$mpcT)$$)RoB-{{fu^`gO>l8B$Vew$mU^ z5sG)1UQynwD#kHvnW@ZnUpy><3&_F96v6gP!d}-c8#_)Lj1V9~x*Gqg{m4I`AOP&3 z%rG-yReOdW4AD((CwA0Iq{lCceR)aE=l%mcEn`&d`8N|4Nr8F4iB(TIwP#~4PrOO1 zJ2PI!IhD(IQ2=^W1}Jo`$p$&8aBB#|Yf^w!>IO3srEe9AObXk!E`;&NJntlh0mYQv zqmhhEVfAglGJua8@OnC(z;0!>f1>~OW^6oVIp+ma)mL6IiP}V&kMJ3-2#XPgR~)L; z-u#SQV`Wc^)=N;9eQZ-+MPmYV%BaQoJ3tT{pTKtubhh!bzHnH6{~d&pB*$mql-u3G zHY6Mlz6tBl2uqY>OygCca-i-jvs&Zd7+XEpvr3JL^D#YByJx1SU-ZRWS~X0G{8Y1# zWCPhyY+Uf8bByFT4*($%{A(%Ig|BXiUAEm5u`Wxr8%P)Ckg3^842wrg?syL_7lETQ zNM5#cYwL7j_2u-1H&;5pPUD@F5-zIEe4opR*JR%Px3M0BV5XU4ft|0D#c0b5tk&m1 zzersi)7Sj1sxfil>PiL8gNNxbZux^LO?!tx68SzN%S_U?FzEMA*_4$>J1lu>%nF9x z`e&qmJE4Yj0U(r*cJl4n0JrN9UzJ8UL01421^am-2FlF*>1P5}#9rhb*9=^ne7*UR zD#Eg7Vl1br`b}%zk?@KAoyaxOE~c#Mp&2)4E5MYx z^2YEd!sPOSYj5Lv#PQ8lk<=IEh9wr!XVc!%<$KX`GN zz~Xx&eyhm0(sHJiSx26T>ye&Ix`E$R{X(7pDW;j98{Uz>3tqh2Vy9t5rj25Q_X!qD z8C3*aE=L4kpLh_mYawvYKX>L99xR1axnUPx+Jw}t^diQ7d#Fsm-L8X1aL4sgdREuo z_{Fv%<#7h1e1lFD59MMCV7kovUQ|ZKO?sXazOIdZA`OtNJw52(d?3EhDLy{ABE|0a z-v0O-n~rQf4Lg?(i<51#adU>JYyjfz{FS;f=vL$A29|=|M9zfX_N@YxBzwgL)y5R;d=wS~`M>~98o^gr`l3bIr()2rY(}u|1Guz7!HyJ9Y zt_i~bAZ8-;fDf5mz(J`?G6LA#*ymk4lgIB4@V8`FB_hAo395{+?ep^yD5e)pK~czs%%AjgG3SYKIrNEwnzp8-0@1~6(Xd)a0ot~Zf~ zjyIP(zSp5EjGK7@ZaPPg6nuT5^{}I|Gkl}I#aC)w_m$bUHdX%4iR@1rnWH#{mue4$ zuuSFf4#043VnxcOudzM1qu}_ZH+B0@5}vgiAvb0oo|94X1!29iG~4uAHU%QWD2tTl zbmUa~mh1w-&^L`B&Xox z451Bkoub6HLu^Y=E*W2q#@R9}B8R7wy&ViCTOvgIP#7Bz!@t8F) z*`?jK%R7v}9#)oV&3HHx9d8Kf;GGD9T}v$dZT@)0>xO0x2-p~Dn%i3Wd_fXh>(zSO zcj9Sn9klQfQ;M7gs}G@^!)_4W&F74xF%Ap|`lZ_t&MCe-R~ZK9*H|cZ>rv zdM0+XQ?{}Dm&$#U4bGtS4fTG$4P6FB#&Z|*hbKy^s@}#kt2w1(^Ij`&spJRp*Irt` z58gs`BM40RX!9?YYgUR?;&eo>RIFfN-*C>U67x+E62UYb%-kqfZ0x4u$986=?oDjd z;1eMAc)e&xT8!QL`s++E%u3JY|4lW_USu#HTQYoC>R8jdk3HEes!+v0<=kWT%?TqH zFZ!y-Du0w;>{yHu1bAUOSCNHYG@aU}pLTx?`r@KcGP6WbC{l+zz72vDg+bvRr+Vi) zI_DrQHqHV#g8MAsuzMSSSf|~_-Y^@h(JtTFE8!@qH~KgclA5r<HxOS=(f13 zN|n$*>e<2$Y$wu~%xGfRI7xsiD^fJU+<7va6um90qm74JN(ST^XHFfs$F9f8($&`d zBnEfxGzMkSbn{WEo(Jl63NqTt4PhQc>^$fxo{3vVRklbbTi#dxy3i05Bu5vX#_RAP zAdnvM{M%D3Q6woPVYCuvwT)PVvKPd%U5@6Rg*=WGU`9kEVN zFQ_|{et4oA}ek(VF~lR*AaaTXm(N@5S6FN&tS2dO@wv*IzNfZofs@1TBIx(sl1H zfQii+UI}B=I;X-D?l!a3qoqML9;L!p>aV z9l4~6t2s}ZsHxSo)Ew~dp;XlV+oIQ-dsq5FOl3D(W$HL2@&d1G;i@@P`RF?jzYuU{ zFo59FbQBQ2y-uTf@U!#G+QKS58v=f|9dz?%fsS)q@bKhV!f=*DO5NpY_A(inPcA&< zyW@HPV*KkG_0qxWxu%1h*c4_m2HKif?H{@$R<91BLP=3BDTCsZrUtCLeLo7>sH)B@fO!2VsHATx#bCn5hXjqhSs4 zqkrjUy=AgU&`A5j277ww=cT$$Zf0o&^bZ2I4K5JU(*=I0&G)ju$B)!hzi5c)YRQvO z;7^NvMjOSo;S_^Y)(aU&Qt2CPY^4|_1~)6F2gm$nRzp}=Ty09~wG^49C4OAvf}LP? zvoReidS7E{t*<0N)ZpF9ADoDLVaE|%s-Jf%3veQoc`5G_p0IB(H7_#C(!p-mKRCLv zGt{^YjdDlLPW(tsb+7LLNUm01DQi5GRW{Z9%^Z&R`nN+%jghh zI^!V}g(s(h)l*q$@$2Y>Utba=W3D=ARXaF=QBEG#5!4c@mU^I##0XE-# z^;6>}U)_)!1CUHTE2A@lDmiPF`x-8#z=t+YowM652@legZ zh;7GXj#_e|Ay0G0r?x@RTTM@_Aw_%Xw?`99-c#7Dpv^*ZO#gBJ&u~*v z!KRnMm-kYPOQviRZPwe0g!BnBM$rtfoG;|TPtmH8s^c}B%4*e&R50~fG;lSiFh8Ps z;`OYrdbr|=)iJBfn`zs=4_OVEnaTjbtYL-%)BFvO0q)mB*?%BIw!%B*VEwf_)8fWJ zXQov#R zq!o9)viBXRn{WJt|G5%fMsT?J*V;tByIGxl`g&i_MOyMS?zk! zI-PTopB3_`>qi6&O7I=*>WJY^2fVg=zA1Kz6EPX7f}KbGGT7hJ0*YJS{iLg&q$LH< z;a@QvW^BC#ua@;uMspLR!%WVp)ufEF-k_E-p&Jr}ehU+|UVj!XgR$ zo^`fh8$Y>FxuuxxR!T`>56#{M@UIgcS5i6_i4z_p3qEh&Ebnd5))^FXQh2C#$Kv5U z&9c#t6!t)wj*8uUi>wlO{k2?fokmK@8>J^V|KL7Z+286lzaA7ph^oF41%!`G5NCLz zM~=lB*R^Tq=Nc(|oe*3HZ6OcBFr@yDcUVWW$n8^%iqA<3)ADI2y_xcnUyAEbRs37k5j zOw!hV`Ms)U>D}_|YQhsHPU93Hl^+LDw&&+ctW1>EJ7qRj6vjQ(sE~bIDq^2CIg!!B z#K|-Mjhz@}U{{++k335HtH!kA3!0Rw!aspgskrP5%g9Ue42Mh=m3PRJ!22G%d>*RO zK#hmXI0$9owMbiSJmL~HS+NJj^YPt|D^mGN zasgn>3RVd?rPSu;q#k7}7=Bf5G6gDU`7vx zex~&89j)|L+HHG_%}Tps6<@L#YBYQ4Z{RWc;IX)@+p8sQl- z8NULpN0lp0%17BoPJXe=yFr^{D7y@|kqw2$8+;?hOS8}F+4{lkk?>9dkl z6@x4-^IOH^u@m`+6FUu4jKNeRj0THL=aD~5n1cBxvr?qi_+9zj`0j&+UqKT&Yhxqh zFEpE!h#mc$Gb&nK{@~Hfj34|Ui>82!hKQ>*uA5h)oA+SQWtcH{#D73Ydhd<0RPAfv zYaa9^m~ZuE#r`sD8@iN=HyCZhTaO!=%KO>F4e`zD7jHo=8np5zcta+ee|Y!p?>a@q zxDliZ=4AjkXQZR)!W=I>7jgDFx=wAGUlkR4Vr95)%a5-Pd3zGMo$^dKgc^e#?ZE$Z z)lEkx$8ECT;AG80P9+#dgBdM&oJ!Jo-g|IyqH6J&dS$z$raB&uy*TjnN&jH8iSNQd zjj*QU$*Whoz_ICC-8H~C{_He;ZUkfZzxE-+kTvV66B4%%USz2`lVuo^X>rODK?{0_ zzr)>Y13m46eMbYm;Wh3}qo>gwW7ww5x} zC@p#V`|J1yRg>2rx#ffXULe+or%bNutb9>j|JxdaweGqcFEBJ6Y=Gj49UZ2^c6Vrb z2Tu7bwF5cj1yz0)sfF(?>)P{<;RvJ=hYI!8QIpULe;H*Ut>m`#HjtQVr`5U|LD zlOs2KZ{?cqGL6OGMjFnLdv$~iOuOgPnMjG<0G?91wPMne?da6Ad@jUWF ze|$sW`eXB_JS?j{YeU$zLmq@93__`%jST^T3BV$7It*>kNoexJ(RLVk4}i@`1###X zW$}Y@EO-Wh^NnCx zM|`s5db$Gk7dICd*W3yJG_GcMNbk3y7HL46A6*9=Gh|RGc&PB*r7Y@POXC6)Bi77X z45Y9MdhI>qdN595tlw@R+pHSbQB0?w_x3N6%_JhW-|`UC<#718vjX-<#Gsws@#6=J zs{|Lsgaf&p$h^3%^S-h1q9^^s_l_z%kE@IqGB`!C{qTJGP__-{G$s$=LjN=I;n2bMz| z3%r~=IxD969&Iv3dNv8_M{*XN)aOQ8vsh$nuaakc{SitCcn$&0raFv2f`Y$SsH`x` zvJ>eHXEP?(Oubxki3&4#YWee0O@FSBMPOsf{G9kxb=w*bck^XMxeI~1Fx&KuhcW=1243NHDxZvh)r?GVVmN*NgW{c4ldp)`QMvQN zUT%$)A;0OjnPxzJ2MV;%WpySo#I%H(JP1@*DSj-#)sS2hJD{nc znTmB@oYrHCMVxaCCNiw_Pa9#)9DkT!{_3}f+IRW;WOnKrL(gQ|3a%@$!0h=a z@j>qAA!df^XHXzWX~<>X=aK-xP%#^JL%OeLGq83SfHOW@bAiR$tl~j-mL0FTvOSgL z1*SA*91-X$(tY}&D*aZTfA2A;!&UPh0)N6GjpoXDzn@J zU!?Jp)8%^dDmy=+aw&iVs&P$*&{ z0dM`;=n)Xw0(ANXqmUo0{2`RX3dmvIU(%m|W#!|ErVX7RxKc0=MTa z{YlodF|EGRCjk?S*C&f3t~M0x8QVndwcNz`bn|FCwlnZyDns%^cY}MT-_*swNGW&j zpwXF!Y>h8u+w06!IbQpoOQvty0=9E)V3geUL>X)ako7$TsZN)%2(G5hEYr)j@M9_E zTE){!$=hpM@ALO4RW;RAU zqI5n^I8B|4KQX*?nYR5W!IB$}W`yE9q=CDv&H0H9Pz)auyw_DTS0N}Yq`d4N|DBn*w8ty@f}8`PHPNhl z**E;+_qR=GGra6)z{MhAyyd~Pql*QbsBK7PSh>%e&W^iY+Zr-H?-^O+{moZ$O`P(5 zqWs^2rk?6W-<-s;riMqY*BwjZKEhngiMJq*f1!RLHAmt+R7}3Z*3rTyE0+;#k?Dqa zb~opMv_g(6$4e zaHstTBu6U;`#pxWpU|cERaB4>b*(4fQ!{Xo^Zi97^?YdSsIC8$6*uESsQLaNn=VzV z5Ty?U+pYJ9v>O25G9++?R!tN$xsaW&ez{JEfZ#I>U#H!p4RmhnX1eZ9yRm`HF4mhZU%>A%0}1gSiF3D_5sSYfAS%pXT0kQ7LqvTR%Ja4xMR4yrIV0b=x=}t$zCDbNS|H)jRHCO z=w*UMH!)kn#TnZ&8DKX)>&(Q{olhRchaaDfy{R1-kn9KzkBabNHZP$&eQDSny!J90`d@x>{Od@t0iX_{SN z75xsGm0dS_+Mrcz9ybp)ZYHH{?mk(#>?BF5m0lT?x(?PYfVTI(u-`cqGu2DOgU#%o ze<>mI$Kb*@v}8ysrl)s)kFASpu%$=LEB5K*-6zDlNbmmSk2w6Y)oYtdxpaplw@V~3yKXH#@KJK?C8R{G&hn7VtPb7GRx8L=nGo?fjVP9hW64*7-V{g?Axna#P^&?K(_fqW+Dm>YF}M35X>NU zOMT8_RMq0L{fK%v*K5)D_$cMvFOmNNl4?$PSnQK#PI8M{OEG)04U0wNJ+SQ_JhCW@ z4@k7GKN%;QeKP|>dsDdxvvQ}H;Jt*8srlR`XTB|0{hslVJIWMCV#f5WvrAQA3ze8E zh0-IGW#eMO8QE!fiRzFNS|6F(+-8Z@Id5>y=s=inJTC$l@ zG+Wq^D9d7Y%(nr=ZqiQ|mkvL&?GOwKA!a6HCYTQ7G*8k2O4$7B8rl!0f2g;fU)r98GWbU$>}9%gXs1YKSVK(ey+c~9Ft#IdolSJX8-j&gaY`<^KFxlX?`L_T2~B`A=h-Pk$Tm(KdeaS&WL1 ziw|VS$K7pEmF=O7WqTgzpZAHVlKX{B9m~qLX4?nn2Em}~pr^Rs-kjY21P)S((OBFx zKPeXO7YgT9xfjAg$-~b>na&vX_Fw;>ZsTr>oa+9I0e<+75_GgVsOidpMPj7&}sWrd`~ z(6DkwXD2?QI6QEFyk~qeAgve>7zs#X}BsP^roMMBmP9x~|*AmK`T zM>gGUNV@jM-xz|Vxx>e7g=m%rVV+H96mMkWPmclr8u2m7p}>VhmCbS2V?*9P$i35?l++TT>6@&2^yasr z^fV6yE=WC+WNZU73bLO{eAd-wj+{;%k={m&QW;IDs*4kQV3j4+tzE=aRuKC$-_3Ly zoYXmX>;2NxO5(tDR%ov=jltWV(0P!m@c}m)jQl|F$${{`!D+$ni(zF)Q4>!_pO0xi zsmHH*U}5h!HSOZ7>yGZx$LOvAf)gb2>Ek)qv!8?DnRgk^>t2qP(B)uNiJrgt-y46s z@U6C|(rEw!jZ9MFtyP}iEtf9{dgW#!4${!j_&xpXNWSP?|6*Ch&&`z4mWGK-6AiO1 z_bm@KYkVt>hkdT%#J61C%xAf}8>P4zJ#a@8|GlE?Z5#v$zGZH#Gm^-|ZE)9MyAD2J z*NeGeF&GO*nu6U>G`{}%eXJ}!wL2ts+(TdrX@e`n{D>bS=WBh@97{H1F!q{QGcx(2 ze%B<}YZF9uC2nJXY0!f!<^c}Hs zI(Y2=%TDw7H{xe-Zad5~Y{!{wcsAj%bH~K8Kh+9G{N!nIeXik+_wevoJfXRoBKA-E zhMlzwFN+9}etdG=bAL&S?I(^_|p9W~j1&n<48 zd8+4p-|?D!|Ed~d7bnlVMwt6CZbIZ=Ejk)*`9Ha3L0PI0$<_%LoS`EXa+=G(dMCkp z=9fEdmNQj}#-E`8YlC&}PQ8aP357moKRa$eFThWEdWU(z>;avQjy7ZjSeki>Oy`py z2tDzYPkbEEAM(SKLnRS0)y@BkTWZ5%k}PT(!@?b;$7@IK4E)eSZ|U9yGtW)e5I@5n zyV`h#Z$n&9Pvuz-$>eM|3x?j%a-$34+2Dy8FF=*sPMK!TwPR38Z>dkh5KKuxOh#HvkV>_%_W_ulWX5JYg!7TgvCNaT~bH#2soasO#3V~E-V^yh4-i_IrH0<*k#&a+Aa^`wU5Irc!kX-mh zuE)Gl6K0+(+0dX=m4oLsI4LZ_7rF#@mh zLr(`ksX|4rPx{3dwJo&+%&yP|&xmll`Kacqrk+`^kKV6FE@^B(lmv-!iV33X zk-}cQVknT*vjDmZtWY;G8P015m!it3`y(67SCr4AB8Kzo&OGQjvu|v@P8UHSqkWekz{$6C<$&8p%X-~R>ue}AQy(E?%|OY$Sy_V><;a!xd;drF#P zj&&_JXeo_S_SyV~2R`cRff;ShokP?XIiSH#j93xAdZkF<^Kf)56w~e%jm2WM%9l_G zwRwf2y%O$hD#BKAqK;E2J|CyOm)onu98?i@Ej9g8yNCm= ze^uxPTA}wGoSr2!8D%&nF33<0<~@C@z7q#jY~`*yk3m4b@rFAP5F~v=qG{juqo>b# z30cvOMKkVL@M1)v?x0cMv#yz6eXtAm@3Y4w58Y>uiWsop39&Oc*ds2EOP*uJwKNC% zf&j9`ExWON>o;MIVa?4Tg(}&?bKkWe+rH%8U)}NJjq{Yzy9~V_k9e!tY5%d&7Q|G1 zdW@&rd-hII)WG^j5&K>k;8?OhK5{=t0g%p0ZoU%AcZ+F;Oq4A zkuN`R&eAnEP(>cZHA8J*1e?Jy!b_zI&r1G8{4dX|b{h7$?6U+(i-@%hXus+QuhQLz zaMSdGpPyQNpKS2MVafQaN%g_UMV|wfLsv?fK7D!r#2+l*SNJp=!g~4A94jj)w{>U6W;O5iNr%5_KjdMf^f8IZ0XW4H|ijd@-klhWwRhXjyblgPcP`xJSiv4IgTp@^{pZ8!dl4Tb zd-KD~S3O(bqR0Q5*3O@suDIc_@;G^O0`H5{y}?0Wi4$S^DE$gAP1(=b)WE4Rrs*jm#@Cu7J}Wu(C4kp7}c5UfL#<6F;i}MHjX=zs1e|k3-t7`T``BA z4-C2U-LW#z69_jX7CtnNKCGCDz5-1!UkrvI$@is<Vl_yTQ zU198r|KFUD+7Rn=<84d#EK_VeJVahzeW=~(jGJ-0L0OzfVfkzKfsl7OyRfjfayJ-= zfJKZlhHpt2GO8)`m$SDli14@cSLZ1!EBimrWC_b|$O^ei&NCFXzC=a`fmfYMF`S1C#W98-z*x!2(m?Ji zVr;jb5fqJ^(&1t?Kc;)CJ}s;_@ijHdNN|28C%Z4|g#i=UM) zd}7f-wn7KQWVQ9$$v;T(n+T|bUtWz+jc~OAjw4%2g33|7TI0eb?5IXwR=qF&s-}8U z^eDnXD)3+QeW4oR93#2B>Wo)G@_!pEbtiD=J z;er3y<5L6+pkm}SXc=Us6!z18aaJcSWn5pJEnKbM59(Be%PWLaE&yvflOkV+5}Xrs zH7XBwfxT94hPPK1$rnT976X$p1;T)cLrF2Bn*V_4oW*95XA2?aeYCipJUNHuf=p*o z3kJDP4?x0JKvbBGH68TNLp*p09#P8vF*!Oh>1-x+Qr1TC^7n^snJmp;5EqN4`zK4& z1{ddRS{nQ%4l3b{cQg=(2C~=3U`>~1+o8hhUnjw%!REl=a0Bu58t;3oG5`XkJNGDfY9ao<{tQnr*{D`uvX%Gwi|KOUL8@Du)1(KS?O>l zTe0&0Do4?3Yihc{v5B~9T$nY7JoAd#O95MxNJA>cB=B!1j%rb3q~a)7Zrqv$^NT3a z)>227MgZcGu(H$%M#AVE0{?2c{6iUikv_$v!(4DW3rgdO(>IaE&O8gSW{;aqb3wDn z%uaY|iRKL@qDqqTamq-wn3NKD18>bDa_5v~0YkH&wg2pfV-}NQ7d6Y*P8iL*c^P%J z>*?_I7n2}ky9|q%EM^A}Cxa0c{+OXf{FN4Df-QGk1R z=mZpMb`8*m65t&RZeJ4BgvmBD29WWBms)&lp0nF}e`&-SpT_-`c7eGy-gN;>DSdgUmr}<9e%m=EFmJh@+8r%ASZ`+n4t=H;cz|D&qE-?=)5soku$?e!~SX@ zPwhl>C<}|^5WA4DP411qkwR|NeO@r5$HK>4Q@Fo)M-1#cI}umKW?05l_xkoVw&WiK z6*9uGB#v)bXw2K!pQ3(OjL&0k(SqnYBHbJHNI6Eug~YkKeQayn?;9&Fe1r?yL@~N- z^$t22Wb#GE5(PTmw_nq~h86)kh>YPy!%}Xeb-}H&-TfVJ9-E~$K3ghMmQvjjy7KQ1 zG{ip#<{SGzj*GqJRb90$qZZAlPke~^C648jlbajsS)?=Hz#@}S`y{pZ;dR+gmsGav1$BGErO{5570h*8 z`ae`A$lMc+fr_KyOlWFqj;@xe zsSGD>ZHK6kc2h#jRhd#bP|1}gQa4F)q&O%rM8z$*@9+Hnf%AHC_?~mV-_P?r?1 zUZ>aFtv5zY?H~0p{mGBuX^uTv#->{BA6-s)L$G=hAxhul6G$>xf2T}%!`i2kYWA*N z@JVX9d=+o*FdVpg3NWW7CJIZ2p_bMWzphPNw`lD&I3f%#9n}A+^=Wv^;@8jT&+MC| zO*H&bPiyJ_U5^7DxMgW_y*%im+0M?+$ivL_d)BHAYH{X^defrjyv+@NEV?;OV_TT_ zmNoM3#U1#W_UFJEzoJ0bz}o}SRKf&E!J9GG-kkF1mI~H|uZZicHb`JQ z;F5vp0)J!)D=Um1iQRMdYcAY zsK*1py_A0ai_0~*80r>#u-dvU_R2J+^mz~|`%zh-d3IXaC`;ktTJ>UlU;Gg5&1iqw zZCOZS(b%%rs;gr8M$t9me!#rOK?$r}(e=LM~+e%|yFtoknNTrVkD{Vd<@V}{-@XHFSHBWRb$kbffi?P23um8P; z<*hSaKYZ_K9Kwoum>d6U)T8H~Ukq+KIt&XW>(X07+fO{!p~D#kup>?*wPIQ-BZ*;j z`#JJ!_L#!!g!fnjv%C&b;9`9&<=gvxPj48PL*HM>GNT7*Tzamzce7scST9BvTvcB4 zyzpIrGZbpPVYLLk8KHw%#2Fj4LxWJqWhE9d;yE_TGC~~*DK}IhQmWR@0*mLb|3Jb= z))j8Dkrh3UxVXhvGsi@UcjCP8GxooQX_iU=joF=IL;kxza7VhtaY_&yGpiKNg#;*TBTl3^a2?VuO-MlRq@jCGI#Y+-h zVmpDDUUoR*$g?+vS%OFxub_#k`u_LquruM2Kcn@Jf9@!{Uu2h4;0o`O9Der*@1omJ zb8#Hr=L;px9Oo1rhl~Rq^+o&d#&96KAlTe_H69FK)QtK7?yskf(mUk;L zl5rIHfBdcg_5y#@JlZQ;H%jOt4BfQ)W5%rZ`XB2D7q9em`rPc-ST70O{pHMb4vNw5 z{OBe%`d9WL?>|RrAOF?G{%mlLi1iHg0#SoKWAfn70}nVtjUD^9`YBUYP)8h00?(A+ zsK1;yJ3s6^{=Q#s87Df>Is)DC`J`cl-=UW5pum?k%=fa8EP>3Qg^bA;&?&}dbfA(_ zXd~){Sf4%LhEVn=-Q_&j`ZtqOgOYkG99h~TbwFSiF;+FPFC`2UGI2J`5ox)>4y65z zlbKX*oeXbwCNyH}|UX#rXEi*CqKvrM8I} z$t(D_Psz=noy-{blE+9A&Ab!J3|mMl2lo2xmtot5OK*{d@9)V#Z>$)Mz+&(S!H!vIZL7|15TofTe+mF_-9oUSMB!$)*Zi> zI=TVOtMEK|`flSkhz?3Qxk)!by*cy>KAATMC^v<3SsbPgUGhquhcTgVH{9mmb%#-NnKF)G%?2cYWj;x-z@29~&sG2t8L<$0m8m8?qgJes{}9 zDm=XV-_`0c711L&M-9i;giu41%)Ili1(`Fw@MQk%C@Fj%31K+CslwSzCB@f>#pGFR z;xBC>Jo%CS;eeYzQv*VwM&xCoOZF zH))*O(GF1t>XN92Sf*X+hO66gA2fxU#z+KAn|Yq4yI}dd4C~ghT)($&=t)#mXn(LI z(i1ya1t7)B>bpY#BaL@&-=GIC5``zz;~MfaK%fmAazM$9ADJ}h2mouEmRPD$aHxbr zUl1Wu+jU#C={s1dY4t|K4R#PsQN7ng!e(u<=4}N8+_1S#9HK6QU$z^YtzcuLfc&P{ z)C(Dt@r3On3wTjM=#Yl?qpcjvvL@^P$OL@NLzWWtQ9GqU{%>Tc%0_obz?Yzt60c-Q zuXcpB8^$|+zE=3fUc)>c6Kmv0Q$*{NDM-}8PA8l_gd%z=+VUxL00#ooBYszt%U?X} zcV)%zoF%cw79z(Zt11MN;1eNQ_KmLx1|4mL+hgELH5(i_iM8J&lsDKbhn8mkYb%_Y zoz!T@_iE1o@q68EC8<}JQ9yutuJ?N!r)E5v3?1a|5k02Efeu6{x>+pB%nPX>k2s#i zZ+P^&BQV~;e=z$?zha=i%IOQ&6Nokr_g-Q?@8l5NZk~rBsKO0y$%C~ra+2ED%IU*m zHz0q>8dA3ebayUrwoNkO14*x!BS_BQI75;+N@Gz-nn&Nc`Y_H=@~i~$YNZ53YZ8X+ zw`o@ap-f+Ay(xdz*?pp{f0Dr~nz|mj{4Qch!Ftk{Xj-@6m0hn)cwcU8(glQy&x$n6 zMcJ+BpqszZN~o(51AD89&9DQ2{T&hp7v%xHiy&$=8t`j_8PRPk14=9qbN97SkbXO6 zFMJZbKNvYlD!|9s_4G?#Kjyk&dTmx3sIC&9$KWfJG9%tw?p9Y z1zrg#AUVZ2L|3Q@6-gPJtqK`77C04?X9drBNE&$(oJ=8L)j?$7nF08Dyg%RQeH2oj zZARoPM~$joMg&UkoacQD3M|umF)_8uc0W-eeV@Q0&4(3DHDcX@1=)jpUz?wujGH$UXiGSe8XGdvL?9yazL?) znk_A-c6ZYu3o|vNa3t?v0&3I(7~G;=F3?rPLE}Mya$_%Sas>2mI&dnpWe43yz|f2=$c~T8dFqyJ5_qwHE18%f|?)M;M4(6F8zi1zv%Q1{z| z&?k3xdp{Qv`CYo+S)ye39p8rH-127-KPI=TfDmOEmbx;aYog9eTOA}g;Z9;JXhZFt z8Yu;<4)9OgQHbPbn6wx6G+;~#$+ODG11u|BI9Z8dNc22pa71RXKg_-~Z7S9WYYeqpRVpkE9#_oI>u8RK>nh1_FaTd5-u9_cE7zxR;V2a0Y(GPPTbaA zgEejK(HaV*??7^h?p;wiUM`@U zZF=Dg6&>~!W8b?jMAePXM@4mv(nvSuj^!o(M@>pjpHj;0%OVhyv{@3sJS(9KC#4$( z#f0c{#vJf>VqU@`48$OqP+6y?Gl#CRWc+z$VLU_WtEMBOYXioDd})IacM6=`JVDYt zg(Ff7p$SvHSGuuTfw72huRA-c6l@kXWXYW(B@BJjn?Y0g^<{kix2r>Yf#xc=yCOhz zE+L0+!2`=S)>^ieyjt4?G@4?ZC^hp_$w{E`t4q($1t8ucl2AY2@kd0orv3QOo5RzZ zrM=p|bL2Qqxe=L|+-feq91`%yQaH)I{?;U|16|-R@4or0lh)}LYY-V3=@wYZR$wQ8 zjGi8CiW_VT{b$Tf#D+`AKa*N*zg~FSi=5{i{sFnudLkg*#(a%WpJ`t!#HZ3#8GXvB zb9*PuRYl1uO5A|0fGyUKAx^@tUbz(oMqY>({(e&F5yW6AvShJ0vvR{KM*2o&H@fGC z)~2FX*`>Fh0TbQSkpDoHnuTJ#EOWu?L!#O(H4j@nEy>PkV7dg^ga&PG*3rPJ*qZVl zCASTc=5>&kp(FR+aW(%DY13rd@PK@z-$20ryEsQ6(v$q%&-CgHyL;@3NflG?t3Q9N zTRu7Y5vUOulJ|yH6jpW^B0huTU&bBurfHvB+lm^mYvTAHCU?q!OdduuuaE7d}COvO)8l))P7mM;&D%;44WHBm)g%RfTIXNdht#+%F;>mR3uz?$2DR3UM|?1*2j|P+AyF zQQ~>4;B)++Xb|in78Q3naPh7t4U=IC>Qk&;PSpF6?(#a0Yu3c%GGG@d`cdh1vBSb^ zP?o?vvo!zStbYP(V;!DiBl)h%$V<||7$SF=4nvfqEY}9+D68b;%pVfzmG#7{J5rQ% z{hqf%5WPvZ3*$Wnfz1d65+ylOlF9)^OahpKfuFI?+@pc+LFEfkBZ0>U1&-CdA-hJm zl>`JUup>aqC!(BXMQ{}8K=gse0V$b)fo>Wg&0Yc)rOBgIIzX(XI>@z7d@%-8Qz^w5 zzvmiRRRY#Bx)7@gR3p&Fn?PM3i5#89lM;<4txpy-o|X{^7!y1QbU)rZP}ztmuirDy zBGx-OmRb~6hsD1sUvShbZ+LQ}pGQkh!hm=}lvTZBtK*XGsKe6$bq1^fg$(qVxVRVQI5XYC44$||VB~gV*cwqJX$M#6r)9*|jR1wqDt&KU zGmAkq7}1j{Xs$Xu$wn8n`OmkvC00+z9JxF&qcBAY*nuiFzfzWDsGTB9dNoYiP5zJF zoY$LkuV21d5K-#ve5QIeySMt^avuNE%6Rd&`3d%9q=)Y72>k>HrK9(GYcc=g2O_{Q z4j2XqRTVg>r}iNTPU|QH4Z+V5$(rV@sh>DKNiK#GsPc~+7 zaNg{bP)x;{`-(K~Vu-1nt73nDobzo-@+s{j#Jr$C{{!Blr`fYe0HdK3%f{8PR_tnRUF6g>70^tMo;8aC5t5O zaNctez+VFCSf#=j4$0}F%()E!B_Z#^zndowW66F+TP#@mCxQb<%SxTSm#b#=pRz~m zyiO#JcnsU+4Y6vOzcb8*bCvWRytCOqleGPJq!@1kKeVH#;9AO&>v73Rh7cVjBr}sv z!_&xOKW)l~c?gh!fRqzCC`2Cb;`hkrQPOIkGCc*)!=Qqgk;`bR+X`^m?vs5Jtm0y!Q2cK2LID&s$ttyQrB6BWa5s-4A{exnR zg{e2eRpsr$#|KnKF&*vLhT$f8bGy0k9Bmgic}{aPWSjv(j^KexJVkq;w^h(L18+rD zmK^voFhlX8BK z)Zyups39b>NnK-(cXk0~=^DpBL4!K#+@deEUT@-x86>&362I)Cg)g!F(k=#wb5@1| z&N2`sJfoz(cpSt14|KSK=IPNp)phlI z$?XpJd6`+dxJ?GFF-YE7N-0PE>h>$9H%vLTq6Q^hVMLp*1je%)C9?I>3^}SQB)@4P z96pa$A>fmHvT{A7uUTF@J$w?6E8MUy_jub3!X5z`CKiBw>Rz}m!qZ3YP9nDuJ_MBR z+yoN(iqZ;@rdu%}-R)5%8XrVtc;;K;m;I3c7VbF`DW2YI3H?Of1hxcc*ng$`g8Tyy zK?q9lV*}88ngh@?dS4^US1ed{Rb{a!imF@|Vk>WqohZC0)K_5uBK{u)N(OfUM@k|FX|+(gp@j{|VDF17gft$3p)R zcz)Bf16uH|{NJkRC`+BCM;(ZPsFalt$2aUPb?Yt_d|4iI%JpC|eMSavE;O)6Qz3o0 ze+k62rsQ4*)LMyyfxsZJgRh8lAagP06G6!`X}|JzBQA17Y2_FJJx9TlC8goxOzJTH6>!#f4)@-mc$+Kr79_l?`U0Q)M`b&JO^qsbuU`rYH*u*It_+Iicu<1QEs~9 z$u?j+ln+HN18rXNo`D*UlhI9WO`1P= zHdyeHK;ZNZo{VW%`$-`H<_2ic7Xc}-!galF`Brm`K9EEj4$?HZ*-!&u28@fcCi zyi*jSG{qPlbR7}?V&Iu9*!zpTqPKjIN>Xea%$j`mfmx)ez?Sy_6x`iU-6^(9u{&BP zghUF!8zc*rPWIQLxM0iGlH_D^D1>6v?2Cot)9#mTCI6)?roROdAmt~Z1>+f;23&$p z6{~{|cn(n2E-LB*$bKXJ7TXW$9b5>VD$X9A5DfyH-TtALv_nqAJ4a=aIguZH&Lm8^ zg;b*6-N28j43RW#+8wp&L#f!osL$F-;6d=;HI^bIFl&FV(W){KD|#;4J+$}2SIT-Sx^54QqXFW! zfi^gncT`Fa+j!L?ejo5G??~goV8Xn|I6K_oT&Zy;9e!8iZFI9`2FA{a?w@xSzs<`a z``IqiN@+=bELY&-%JTd$a`%=0Qb=%kf?FNyO!2pvKDghfF@i0uRcQe^HQYE0U#L`ef~P6gca*O6B#lX9%x|jqM{*l%gzQ~eE9Gcc5rCU5O=QcbY&XtCov6 z2{ABb)ySWB;sHj4vUD+4nE23|HK5d?EN1cZ`C`rtt^7!l1$!-P2;fn(7pY~+_vf!$wR8k zsezNc=U=8RyGMs|;<~j9`=crzUv%p4E2)SLE~;9e^jzNr_#2cz&?mLt_BQ!Cz|#<) zZpPzh7irp2A7|*A&j4{ln3g#0T&-(8VKM!em||TKcRro}X)H!mf`V^ZGiFu#K4M2k z9^her!Xw+{5(ctNIqlL6)jWJ*TuR2pSmu4KXYDfzvih>qHH|s+y`h>N%a+J+$8{rKWy=iE;(%fubK|y#fPAt-?@RdUTobg8ri^~-#2^)Fs-F?O;#HW zTYJN$E9RjE=g&Vn@3XBtD|cXzLxy0eSULWv(PtVGfD?-#E?_U$N}WI zg48TX+7HC?Ja16vd+7Xvv=S)FIpH5Uo(a&n5sYTrPq8PsO;+{O=D1A&WK0{}YC@On z4=iHj>Mn$o#P*GsRXTqg9G}fHoILvNV0BfMOA#Um}{bH&i=`Lv+bYM;rUV}U;{)?(dz8}>&x=%0*}hNJy#yYAgfo7F$f(4KBD zyRO4Q^!0=Z4$1ns`)!38&3E}3wg?Ko25Q>|&Np2LXtNbO8Dxeg|9kPl^Hul7-J&(( z&))R#Dv62s5^DuJg5!LpV zxOai9eV3~z-fVBmy-a#_>0BpiwlF)l^o+hmjC+~)6AzPxt0l-Qw@i_)siE7+e+yaX z&pR!bTKMF8hVw&kBj-Nle3|3H5=!iI;Vb3a*W&tOV+o_Q6W_i3Nx z!NR8e-@09KXG*<;_s+CrDYrg};@hkDga0fCO)VF#geEqh{0{`|F$8rh0A4vS?MUO# z9n)(QIeB-j_AKh0rF|UUR=Mqrh&mAT>c#$g`*Smp`+GKDuT1@9b)?61&AYQd_>q^@ z?~gq(vf()$SAJJ5vUwXISQ^IHTGEn-(}QvryqtA&$a~qbUb3hVTHNb)dC$~kqNJw8 zD26W8!)9l}xEHapyzEJzzsk(2vfs>%uMql_HhWw6Z-D61GG~*p-@_3o$&Gi1ZS_~3 zY|usk%v|)EiWDo6a~6W@k>yj53DKZk6hMI8)C7htRU{cTeWW@44LGZ2iFyA7=t2vX zXk_0g7&s+3?c+?PXG6|>#Ypw*EOkg)OG@tgr|3|Wlgg$sU<+8`(p%)8!#pu@8HUKy|;whzGSt3BH9U&Vu|v) zX3e*#&Jl?I4xK;*6*b|AlC|lim?@Ai$^Np)XVmVJ;1$+*!}3^pPfy_NhvQj|kyHBN zF5&U$xGLA-Q&h8d(&3|>V2uI@0y*A}>$?mF#^{GUI_@L4sKG_~BaTQU+K9{Los33b z;Xg}SGE{+@&6)?S70|LIjLm{fObFoKufsWTL6RTv)D#EbXvhdCK>f87roWUO1%O@S zH5I*Bt8%1c|3I2&Vw5xccy6E~R2CNTUDV$t^xc2~4f4l8V!A4{4}=TSI?%Nfim}@d z+6_TA`I;}MXT<=PkqJ+19GHCUJdi#b=YYaRcuprfP=e5JmRN4ztZT#riJ0X;K$G9I z>@BB^7j?@4?r?1XN<{6MP3m}k{$8&VShEHOuolkA^7GwNW zC7fUgg8b2y6WLKxF_Jjwv>so@=3{<1rB$+}b&=BIUY8|~&%tv#8EOffViQXK46jQw z_I#54$-(Jpsh}&BQb3;J3O$`8kPz!|_>OGEnh<9W1ViQ#nIsE4%cPT8AZl$pY1UB( zJ^e?3PQEhr<w1CppeT^3mH4@V#vJgCOYpNVw|4Sfb`rb>)X*N2ghA@UYKFB6>>T{e+Kw?QHkF zpt}$8qG~=p{(9%i0zYl#kYq$k#&^xl>-wdT7zEp<;{B&maH-^ZkP@9j(ln21wTDE; zBKYuKO$(O3K$B#n&(-brpoi%z=zn~f7@U3RvpH6c#sg86OYTT_B*`m?-_16-Q2mJK zwRyRaHR9GOv+Iwo^U{|`uJn}A!}^-k;UduXS}vCZ9FN=0#q*>1t*3Dq(st;?8ssY7 z7dMCpT+m(8fWVNaUIR(_yG1LKZIwz?q$D-v@a_JD&IH>e$aIE10%c)?CW*Shnf6DX z<^FzOS?Bp;vO02bymPFl{Yt2pD7fA$C-$@HNHEE&eL^gMD^d0(G8&(FGloTHrTkj% zYOG>jzPk!t%}>E>0?m>AU}LAWFc2)pk(;^2EXLx#@TlEDJdqv_Mm2#V4$|T+s~4P| z7vdvb6etN~zxxR-ZY5K})*I>>J$)rY-5P^sVq3-?HX+_Ew2~sE0|6{)1Q9V-kGK&QhxY;s+B+~jxYmYy4 zelNgS5k}71N^}ri06=la_nb55fx*_()f-Gr zU-<31|3F@@Ly1LAPhgh#@nwLzXI+L=G)ePrE#oi=4UhA8amRbro4JzoTY0zSze^q( zbCmsxRgiE60$@66$&p^loD3SNFbMQA7;;zyKMvs@~@Eak(|-(BJi=Ib!h!I0pay_X{t@TL3Z zs8KvW3eB^O0NgaK36*yb0z4!+C<5d^-)aH8vzW@gPPb~Bl+B>Tq!zHjR{|zBKqo1> zHIN&m)UT#+2H^MiG}@3b<+-LWGp8-o8DpLS&~So+^l7mAX*e0|Ff+zKG$=`%?&JcHz90RyA5xoq>Fpq~%M zfdW*8EfC28sfG)I4lh#{v`MB7O`^n`j9yY|0;g=D&GLT(ZjRW#kiqix^l9U8kJwUu zPuJXTpPw#6%U53-+u4zr4aCLmu>o4?SNR``uPXz;RIXu1+8F~A`p>s-ymjhkp#&gi zoQzE}fA&NAU5TOMi0r-q*We*g$R;48vAwBVUvU(O^W$=6X#PQgg4J0 z_w$-BCg1;uqK5L0nZPJ1?+e;jTJ*YGka%Y-J8hI-Hz#Gjy4_UkcrcgkXrX_w_9Dxm zvNy4^vU6g6wb5^QQMY7uS1c#-z1u{+s8M~r)ko#SnO}fQZoU0K(8dpLzj)in%)Iqz z=X&EDe`(!a-`8EWc4L+EOZNdY0_{3Exh{Kg7e-d7jkN5O+0K7vt?pp2Z^pgZw03u0 zg*H{P_=F5KU%bt9;gnoe>XI!p(c1Fiuggiz6IP>O zW_>&|d*<)A$KiaKY-k)vLZ zowwTU{Q9<``+RW`!fx;5T5a)EFe?_=G0WqvpD-5<5?6Pn%J;4@fV1wi|3H5<{1vxt zExX~5IKxF+K{Q_|&nsN~`Z345D6BlN$6WErYpYXFNLfi;cj${NRR+etV)tkCOn7fD z>OY-_T!)8MerJBUqq|{$=+GO8#XW)}M`%dUU)u*-luB&-0gO*{=6|*I})Izjx3^wiufLKo*QS zkj))WPNoFQ!hYNnW#vsYu3TQ{Sy%3P9)MoYnx_3Kxf|#HYyZ~2Rt19LEpKFniw)bX zOjU1md460jej7gOb}+b`Wn&xH%RS%p#a25B=v;Z(rhVA$vtU8^5Xo$am^ zR4(bU*sM%1yFFLLYGHVI)b?*%f4wKbZcv@?3Ck&h8wkE4Xg2h>XkK)}AhG1g&mZy!>j=R`=Mi!M!aFM`rG_ z{_fmREOLB5Q}WQ%TGbYJBJ}=PgJ$pFX(`!v-fo__Q2e6kt5t~R!x=WqZBkG_*v-y+ z{}V|A!;ffqm_C0%{^L5V=r1Qnd+$iTTdy6XE-$}vnvf@h{B$`XjQTj-XL7dN;Mn?s zMG2=L(Wh3w}z>h_@%O3>tz*{3v z7J2(lKWu{Hu-^0(i1#K*J}`A|fv=oM|NrHFl{{LtpV{>b`zX#<#MFppouj$VoOl#> z@#R2J|76w6wwHM~>+9J8kyl<&^gS&cviIxnNBX{h%c`zqr3_h_51B@dVC)HoM$JIj z7xGp}5!^d6a>!|REUUUh_f1_%@8zYu9Z!2>C}z<)@0=p`xw<~l{rj(TlGyonG`r88+D-*sas*`Xi+Q)kH1@TM@F)3J44$l{7Kmi+%hPeWvqbbtN zT1@RgLP=A7dYM!2r&vw6Em{qVPvi`$rS=makplC_!!IK@#7VHDd)bxAa z<_K%~p&;=bn$2`({Se=P}p zBe}3g?-Dr!q-qN;(Vj$GdAGoQC4r`aM+O4OzCv26Yy#c>hd+=K>k*S~-J=`AL6P)j z9M*Cf&$HQ&s?Nb%M`vc>v$Pk~)d3A>lMWc}OXT(#VDcsCQQYDl?6Wp_);>A_+Z)( zN>%sKoR-%k(b@~b^u%0dNhlCI=D0*^9X?ThQmpw0p0Nr|)s39Gymyd&B0RUw(Syh1 z#VY!FW8ZuR03XG%>0vJ~W|gext6xSiq2Se0jPk4TCcvF6A2Y-i zVCr10kA|CcMl5@=dXHWUb(F{fFdc?H%91VtBPazqC3>&L&kFt0>-iL&mA>1nA7{ospFLBc?)IXsB=T|p*CcUQBl34sEa4~RkL72T|;rb)op3ZR-> zk_dJ~YD0gn4n5n`_@$iLJL#=(i{+;qOA9YARI@xR^j=F#N3(KFsTkB)eq<9*z1>k{ zxp9hL;=a?>a;Kf6MXs_?)YzM3B!E!$j~k*yaQaRnIWC#ZT7JVCvY!=-069VP`N zZ63uCX~4NnluYg}%phlwBAQ_8K&1%;0vIduIUbyy8%V;4t*IF}3xJgcL*$P?eEa^T zhU2`>?=h2P9zK(tw21@H#0R>&2UWj!bT4K3uP6ZKW#NrnuRV{v*J1-DC1W9+V@Mc0 zLXr}@llXs|J`%KJ=o3+x5(5&|jHjTqx1(pMb|y9ApZAnO&RWE0pYnV@I7t8tA=2{l z?Z+F8TG1rN?IefR&K6{AbhFs?Rf#nISgbT;I93p{7V1HE&1wCb{mtx+jOCT}Y^tDZ z)bFto42>hE)C$Ndy2LJFR4DpzrxOHE-3{~(2?O@OjWzligAzuRC!n6_AcB20IM(+N zbUc{}_k$^S7e0pCHV>u_5Nx#fpXpLClEnx||XSLht<#-YD7*&xOGKyW5Jtg7WE*W-wiLZS+#|KnB)k({A1o zx~y$My{QfEN8G$f)t{naHt zq}1!e!YoS!B(=Q~g}EpFnt3ne2H$sX7D5}-^}C4X?KXBAYUfi2nBS;MpztLuyd13R z`loXy)gC=3YvZXSAt-fJle!pF0X3UP>HHqd!!(*%O@op!Mq~|+L)Rmc7puO~XD5JI zayAwGsvhG-1j|F0rNwE?mZ^${UsoC5ny1E~TNbz+kZ%B^Mfq}7t?t|pz$|h;nVy6L z^biit*s(nR1_y;|)36V_F$Oi_tmJq7if_?)qOM^h)cOaGr^BP9jI2uk-1L^}v=c8X zy|S`dqm1#;w0c=_UV7f@#=)iWPJ=cH1gz758qDG%+jM<>3HCZ=o_EDd7iGx*Ko`_t z@LJ`$UKCnM-fxFJaun2mA_S70xwaYT#T1PwN9uY#{29kitxZ0-Vbi(jBgA%l5as_; zFUU1BDv0-#Ae%wIs0!^3lJ{`g?h(q=`uV!B^0D5A{-R>0vzOElcXhj)GJIha)DGI1 z4g+mRg2)787p$C?IR|bM&RXmQhl5`tVSvgX%y7T-3RkA0b^vqfao`;N+m2KKVot%cs_ zd)2j~U$u>p{Lm~g)hNj>-rTqA5zhP_S9gPwF6AFUK9uqdQf!3}n|Hm0wL##!0IUoI zkzx@FM&xTU_im+S5TFosFg^a45$|hi@(*Hj>F*A7PmYiCXkB1DyRgs6)vf-`l)#zO z==>L}bS`Lg^h}chS#|^j)@fANtC1ijTGK}z9}kGYB^Z2qsN;mWzf#R=LGsn*t8mI~ z5uAk-^``#VzJFs4oc!bg?}!OqeC>WK&XCB}xCeo4+Ix@^t2gWPWjrEyjpsdu5g(k) zaq=XY-YjGYJQJ9giA>M@x;TIT^y+q#jKq(=dQ|+e zqTIUQiwVb$_j;C=)<^7(mwV-ftQ;NIU0qhId{2k8+l;WmEL7WpcWokdpbR%>V~4JI zfuo2)P@(oHNu=SXNr!`P6FO27fC{D68Yve|B0~q}{}3UYEusHN89$b4kn^|XS{|kI zgbVd=`aMEI_BAx>A8qL^T_jKeo2I;2NwgOy|c?}Rm1LYm9QFD>- zX(JB_)Am#6%=jsX%TmdJskBm+@X2=?iXmmv(*FV4HpeT$++%yUt_jUWktY)J0je+t z$vIB+xjbCrJS!g$4w6T8PxO1-WQIS%jIHIx%d7ZRvU+(7C*qu4tSDayRHsvMSj-20 zLMKT~r=&7!bl?)I5$BxYM&JE~aFw>v=rIfgPa&x)0bbU-2hMb6CSmZaC`mB^tAmm; zgewJTQs&^U zH$G;>r!SfiJ^ce>A`9eaNw9~{T$Gtc##ZK5WtVzov%`j8PVEM;b03~f@ZA9PQbL`F z!avT@y-Tl$4?LyByvE?l{v;!AS_A}(YUSV(~!fUW(1taAaX!+O}a-Rx?0{4 z^j$-j)1mlE30^cZ&egE}8*2U%r1p4*OuKl z6lN;^xS30nhS>_;A7@CwuA5s;gEegbHNXKxOy=3O82Ai7)PxODcD1b;PbCX|8CVGN zhJb(5T3eC<(x$c$Q*12keNkwV1`ur8VAQ;luk_8CRySTG;JZ``VrAE_cMk?VuJRld zMAnZK=lu*>T^nXd`fb!D4D(${1RIp*nuD$%gMK>n)P_EY4x<1Lm_725ykbL$?UlPi z@pgM{%|YxdOU*Sag-!h#({jvaU|I-_i^+Zzyfif@$Te{{J!c`zzWCvkoou|sRUdW6 zJ#7DQWMwDUGnQSsKG%HucGRfDgku|s{Or>P6|0LW#z_5~r`MHNhoj+8dM3x`WrBY|ShYHmcwC{;EW zC_h*kQW9cHYL&ZAl~z}KF8lbNb!%`vd^k_5TvUJDbc0SSm1hHNl7v*JKpa9=&(03THA~ zLOqYNqUx8uZmv|_=y@$)siLIShsEbs)w}qdppn62L;*~_NvLVk8lzP4&GM}Ek+`9f z;YrC$$W0W7fcoN@oB?F5+98?+mLU7ViI^rh@y{=X)GJ_eHl&!8N`0-SlMml%Rr28( zD{^6?doZitja0>MbZ!W?=qrCVRaNc7?~N*Pd;Z0`^0}@^;TAkFeXAVQj6~2$@dNRM zp2*ARjHBV?opvfFJctJNYX%1Dc%c7y$E1!>E5EUy#YOSSt8 zRdJ~1<|gIk2pw%`VKV(!{eZGPE~| zo@{C6sJ@j?=&~T;^;A}=G$6Q~kzT8gtjr~uRRxDd^m!fis;pv> zCW9XQ0Mqyo1ehFv8jC>)l2D&5Wa{7~M-7xnM+Xn%2|-AKyVbS!$s|9z;wR1)4AeRM zk>d)#^MP^!r4BX-8<%%=9*jt_e)tN5z8F70az1LTds#oIEOOZ8%prrq-uTevz^Fv_ zr0JPvSi5K5(+A@M_&o2dD#50ShhS_T*sHl-k5pex7BP_G9=E&?{%QGC(z78ttv8dR zB>je>Xknbb8872z1kWr;Zty~*9QE+ zch%1eru<()QWdtqhXUTW^FPpw2BrR^-vcbQ29V?FExJVe%9N`7c>p&PnS;$)W($f2 znc;P{a>@#DxOQHti=2PY9)1-*um=IQoE5OAQSFYaDuA`$|7V#QXesxl7RF8mLr`&$ zyad62SDaYMP~vHp^wx59CQ~rxso%CCm7FG(!SorbF-jSn+=WxI8%i~WcX--og%s4S z99>>ul`s3;SS!pS4N7k)1_Ke2$hvyh;s?1psX(e=xVKkZgcSQi?VL~diG5XP#%$Gf zX?af&;8643Q|*C|cP998&e^C#%1M5((S}8?O2!wcv2(mJ#I_?BK%>IozDjxj@G3p@ z_VBBvCvNxYAd{W@Z_2n_q;Rp;(ZoH-YmEU6Ju99svDIv^pIJE(S`^%?RP=q6aJ1A@}z zD}gISx{{5gyFnL%dfxNTbWWb|Sa1s~r+k@I`mZLKr5vFx()yVAkEyrgKP{m)x{n|G;!J+e%>nY%QnW7g z#N`_`?ZiqrKx0IqO`bIojL6AsN|NZ$-yQ70fY`n%4NCz(VDN5Fg)Dw&uiaD`t1%&M zuWAAOUH|64@#B2mB*-}=NGZA1#Kkxi@mu|>sFu5QQoJCdCAWi`noVu~?UOujUaC=+ zBpG9Dz&}J3!@xAuM#=y=bQ~yH90QU$!0umkeCcwhN4>!?H=ONOEs!+;eG@Kae3y$$ z7Sf{m&gCawZ9>(Z7);F4`xve|kUUL7YPg*A7lTT0?Gr*tQbI1aMi}>D?$W>izi--^ z&jvI}epDn!KY8`^JF%p=<2!{ou#1o60Hwlo)y@%$yb8eNU2tI!dMijVx*uKi`$mTp zUfF2~gQMMLv8>pf6T8$*Q|N%!wyI6Q7%`9+P@=7^!Igag@Zr7)sjNVN;O~>{F`F)4 zF4t0gW8mEKMHZJL&?Mg-lrKifm-}W7qqda*an)DK42+sGF1i`M!6W;@(0?Fkz1By0 z1Mf_E@BEFpv3{?z-9V4YQGtPZ$n3k2qNTe7B1k03Z{S%8omfdwfmn8J`>Wl(4iJg= zs`qbJw@isu^?#)t^}6g3e6AUR;##Is9mZ_Nn;-x*VV5({`yM#?2YF??143hys6=l{ zvg{#P-?MhSw&6?9Xf4ZiaJY2kP1TR;@>2l&0Y1mTVm3)zqmjUk*%$oQG6KYK&<>z2 zsr?hBOotLLwTL!43MU2hc?q1dG|pJBIZhUn{bt&+30UpzxY$_uJ|DBjpFKIH!LO$j z#RqfPk^J~bk2qOa6y{0SGjS{SMk|37+19RRV>?dwJ=A2nBTmj0X{DbZo*(B^s=!FS zCopZ_orJA8qwlNr7Vy~u@{U~qza7>FyXEg6fY(9k;>rHgwrDT;R2hTeV?AEtPH?s_ z|JKm)zN#OH9u77h8ak^)hqJT;N|4-no-LnhLcYeh*5p-m@ioJUYe`vz;94OzHOPad zPaSz6ir_(ebgOwfr_sg^b-(S3@lQL{VPj(u*fN#zg99ki+nnicH)LgX7EA+$OmW=w z0`QpYRW0{E46j;vR>OfaN3<#vyEq<}!#`$@iPS3?mQ4$Yi%kz_)!cmMa=pFT)o zGK=}e9?AV8G}Pc+q@C;M)G!lgHpbEezGng@6fS_eOl6mZ?ls_S!r#>uZ&cP8P-+Fr zJM`U6%czbGuVzadSgiK`iHM(4^Kvtn)&Eg+=7CKAe;l94QX;7wjrm4NipVkN@-26W zirk85miwA>j!rFSQjSsXJGqaUdyZHP%RNSNjLkXBzQ51!FaDhQyx;HF=ly&>A5WF> z)%E8a5o4>9!?<#+cLC_QW4c7)Bd)X7zz-2M;pJY~75sg?a~4x-mR5P_HjbFN%(S9^ zG6BzFvmZ{f9GOL7v;1s`ee2^KyT3xIwLw@Z2Cn)=X}li=eHxWCsAm4HzOgRhe8sdG z^O8JEz^qMMiSG6i{o+5+#9@2jj0{QB=+n$4=5ac_(=ytN-w_q=au$I;&z450@>JQ^ zj*Xu^Y;`y_B2aBu?Nx;;=(KDEYC3dZ#qa#8J!2zv^Y**bxMuUoNgZCp)3}=EtBE>Z zUR+Nu)z(H!RCalt{ib`i)Tj8HhnWA>P~8DFrP7LXH7WZZfm!lRuCz_7<%27_?cck) zth!Bx3tZ+*6^<6hu(2{TtHw>TjsdKXEC78JV^e0+KCwjAr$sxBh?tjeJHCXT{DvLo zQ7LEASXNZLKrY!m_lawATj1r_JRc1IiHM44pGFPOQ z=7!l;KbJE!X(F~WraC@xC>s#Eja>cOp{6U9-cj5nA7QaMGhlCRKQUo5@%KN_r;2lc znX(jKwK(1UrZuYuO_1kv_~^{RcmrTXP&2J?`n$QIOU$6a)hyhPO%;g@!8BIuQ7)b` znKg-=JDtMD)6rJUfI~A>NVi%!;QYK8ARnoHCH?us%ps;wcOCk7TE^s=#AkB=PjlrU zvtwREOJ)Aw^LQK&1mFUKHi**oG=e8e`Cluo(}KKP5SZCNscmcG25M5LZ@qnvXyLwu&QXa>wlmi8(Bly zf<9{CNZIhFeY!S)*ow9R48k>drVKQeEr&uHGV!@#Q1nZzFg@sEQZS#2L24H_9-4i`o!BesSymg)1Rc^AH1roU{ht3r_9a{waQGjx3f z3Ro}>Ym$}BlEWwG?l-jvu`b^EH|53mXLw*TYpzKF%%vn`!1c>fjda&~Iv4p3+>6pF`?j83h4s z>K;wM-=T?G(aP%AYEGTWzm2p;evWD1zx% z@@tj+4IpCDQdy#t)RGVD_*!)I*2p@OfBqb&h*a%ttf^MWo9+-^Pxx=f!|NjPnhTH+ zM$8VFp?0bKMon{Ny=N{YP)jifMA^a?RDi|4J$$aRc-3gLD$qlkURIto^kS^Bq`X0lLkFv%9w|y)26#9qW*Fz!p@PD8)0BP{ZtosvOz>5u) zemUbMw48RH_ZHrL8h*#Ov z5YBDN;E@7|PS2oB6%DFeXBHnKLh-0r`#K`4YP6*Yd3+7Z+bo@J&AQ9{iu6z{jh^jN z&p2pFrDXBAW34R?!czzz)~}EM6is_P)1jOc@g6uy#L!PogjwjL61l9p=O8Mz`HTKW*awExr4V40kJ&oClo7U0SO5=w30J2;{tnS%=Du z;OIYfjE^=S>CYSEzC0^K)~X7ngw}RO&#mp5*2b=MpQpCg8T2byc#~(DNAm|LQ+k#q zaHk7e_hyZn&;B)GMU$SJeKnRAAE)U>Nt_cio^z;}pDYOu6ADK>J$N@(+8mIrd&ymI zV<2t>QRpFXOslH{HonJ&roYgS8`(NUpkNGuc6EWj^R^$EBjQ@}Ybl@+IArPkK`&QN**R*yzEn(tdNMir(BC8N`s+ay1zQkz(@e@YH1haE z8Pa0BwQtFUneFC*tw|d0VCEns*wy^gy!Xd~2brUYE&1oe!6PmO9uP(|=B9FMyTpx|vY*BHp((rwT)6TOAHb00YGlU=?Wztg@v_-P929dz-_-?c40LHIkIUgSRM91P zC;s1otGhc#d13HF^~Yg%Dw&T0Do`2(?Hlpk>Th0MVdUW&nUV(Yt-{r+4K(k}y>sy} z6q~oLEY7Y`*ihLj33lECj!(+S?1dsw1OO)M4}w<>)(idOjDJsn1acNqA>=MN-Ro!N zB=FstXkV;zroW-~VVpp=t>e^xf`wre=L`_H?BV*YT+GQNfKI;Ox~?7R4cD&m&Jp?d zMnTEU9`m7%$Clh`;OwVY&qTNdMR4CxOfh`V)-*d?|Of?$ge1sw-WthX~# z%y9Vo?BT52bqo$Vt+h7XUdeTN-@DI9D%<@0b8ug#@MTze~sjUBB0}(06=0iFU zmCGkFh8&ZOMef@kWxy$)*`i0qa7w;oog?kR|3|3=WY#gZXapHAxD5p{FFuMK$YI$4 z7A>TL-HKJjrD(Hcb2@({?21>hIt;r{_}au0X*?4#U+`w!VLU7|E^gRXgKoc`39K_%ZlXJoAzxE%gxb_rNON z-;w`$7Iy@%``ER%vN5V_?T-89uLV7@}=cfF;G&D5s@@XMOBuU<5p zdz-hj7nuC))6Pr*w`Zrq?jMzEE2DBATrKm(^ELC+fqh=osGc)qvf^Fxyz95WJQHP? zr%d~!hw|6>S8A`Oy7=UJckn87om1!%CGg&Fe-F9e!Rubx!KH8wD3G~CICr(OyF~QW z1&hg`Y4ZdPYArxU2^Az*+=?0!J(pc}IcRixVl~y+UbZg(!^FXk7EAubqA=>t>ithw zies1jK2o`kCUbK<8#U^Nl+f43<$#IJ3h#--URcSz)Najz6ZbZ%2@UCz2n487eM|7JLW;yJfRS@O(;z`AxV{?K6*8 zeue1{EB$@goH{{nJ9@NUGgOqI z`NlG8FJQiJRT;MXX4sFs$Z(lhJG9wFO#KHE_b}E&`dzBl1zJE#_&G@3Xn8>3r6Q-xZ5+-5JfB?Q|bNB@s4 z?jJ?9$VALStGPeWom=^4pubAXk~{I|TBPiMEAJ-L$*uS8yO6^ZWzi1H-<77UW$HzvfHoud*JQODHl+=~8OuW`UL3p>* zzx<+_pXaZDQ9op2<6Vutw2{8H)dV)sApOAX|7R}uch-FdU#fFy1PNV@eib1!HKohD z#k>d%fv*;sDdpo}jx@3bqwZ}5U@imO>6V3Kn6LOEPc?HU`G8TV27%k2fyvSIn8|U*rd^?)P6(Npf+H5 z`_yS!gXoENXz|OhmB|FV@L+;4m`=s{ho0wZC<{W4|1Im!Bat6PG_Fn^NprUO7wVl? zAWoPR)|sZeE7Vjc8vzMSW$Y3EpMTJlv5Mb)8t_YA0P4Et;l!IyR);#uUNO77hx7=4 z(tn^iOFPiZsJ9=es^&|ATUG?TIGi#j4?&1NTdz1&WRuj*;x@|W(?Ze50P1)e>I(Zm z(9=(G!vx?#EU&?xnd!M5cYZXC|J!eolSc)XRhZsOXg6L?!2J;p0$lnSx*lw|b=CX} z#_p;FH3Jf@9>ldx;hPFjHdXL(m^U+dfDH8rtX)a-2+*>o4?eIwd9(eRpP}!_%ato- z{UshX#ltjPB9P&`3Xg;S*ysYv5eV}aYUo*1bO-Rfhbb{*vOnjw0K48>nzNzYb=Tmt zM4O=i4Vm?A^jTDp#mzqd4FM+Fd}P~}mUOvnJE`j$;ZWyL99VkI+SiWC@ve?99jiZO z9a=Jxb4PVu9v7|01lv+)!OG~fcF#}cqN+Nz&#NRM$7ateJ+F~e-33qn?9Y4rmi_yN zW`iiF=Ij`nbcUa$}mn6=2YJ2 zBCWLfE16oQO!q^Fcq@i$)SQloSJ@Qn&R8~SMkL_(#=Rei)NgldkUkKPYd^E54<=2b zQe+9BXcT9;FSiCZ>t1qvza#TTIbh$5wqpN&PK7i6McreiNA5>l4}(ifpqqNdaHgm2 zmLF94d>GA|_|%Aw?VWb)I|8aNi7$O^9z#rQ&gBr zMsoDv-XzwF{{UoaixCsv&sb!#cxX>iQ^O?=l7`X>ny=G94gAXHG5+V zg%i7Z2;mK4ItRSG7>*3VrPRc;gC`_;jX{bxBU*Bkmq{=XIb%A#PXC!lN`sZ>B&$&) z>;3%SYrY6w-+fw6tkK{G$#a+FU`R#^M1~f%F2~!&kYTNoQOrmcbr)*PNFXIzV@Z>j z3Wm&AyVNMEQ9gB(TD3NR6CaQxd8DJ_WWN2R-*y@Y`^km#GdS9`Zkjo88(#9PAtRCG zfo=N@l0M)rp!TI(1lm}`wQ}-~9PJMRuNK!ilf>&KIFfGtc5;IVbi13!-wX9;F^=Gz zRyG5>MHV-O12{_n^OVG<#^I*S>(QnQ#CvBz(s90eH}$V^ESyn%9HVbvgHTBd47r?F z0kCv#6yh??79)&xng4;(S1X=D9(x*aShd_rT*mG%)$)b%A#EnKn0EfHQ>M4I0MVwE z!fJ+iD>QzzTr&VmP<(G;FuchG04wN6l1#=wJLUO^xKdQr>7`mycIlT{m+OC)eBV z8*-i1fkZ3nqOGz{qbX$gg)n`lGZ2v1t)rFAiHY;Sb>wdrp!O)eYXPmMl11-1+@g1< z%s(9d)BI+BvD(Uaxuodm-TK(fxF?qT%Z)Nt+gbGW+FBFk596m2UX<(qYj*GkcCk)e zVG#U}fux8s`v?BVbdLO5)1i_oFP=_>a`9>1qIa}6NQ0wcRFNKOfY;A+(k}z|t@&w= zOS)}M)BBcpj`;&Oh`*TnQmuc^%j%19&z)5Gc<$LX-z8roqg9hf>lGesbng>jL^9WO z1sqzh|Ip)Z@1v>W%4-l{L(zRp1Ao=rmX;!S;OX}(m=ad8cqjaLe3xT&8Q?#^GdCM+ z=^(_=k7TLJ$5d~;RIiBHkdKdRHgTKlsgNXlwp>ERUVM_THH1qN8eTs=p*Oaw4CEIa z|K8i3eEaAD;gJ>)Bsvhp&V1Q$jD^#^ac|*t6+X{meG0Z|HGgTpnV4rR((wk12VO8BZ#s7Zt*KF~%6 zWvMLWLWyUQOsnvIBeiJhPmd&2UU<+92)8@wq|U((r?9u$dj4v7gW;jD@+--IH~c?U zRLC8!B5jKie|O-Q%HAv#A*GA0$c;O=y+B)!yf-$FW%LuuH{?_kB&2S=uqWJzK9=DO zBUGJ59&|F{z**md%9+n|gSLv^kovgK0S*8#eq^@yj^bC%bfy>ypM zu1P3=*L$Uw)O+^#ALBM{CT~pIroXDKHN0!cDzxdI(QQ3dr?QE`P@)8r_oCERF(4jL zz*FQAaN+6vE*Cw{ziWQ+9h5!*Wg#;0mrZ(rLp-DRvQL3%mfq&+KQ%2Aiez5R$sxOV5 z3y(5)T1?fSvE_H#q!t7-UM-M2p><@|8E_Z;`)#`|mfx}M!_gOW04RzKxCUma-2SV=xK}$_P zf)<}fq&frJju%H$@QJxubYmi>r@ubyEfhrOw~K#2Bcz?#=(Ho&==|+rQ5QUDW-&0y z*F-6q8QkDs(*m_Z&MsHU3HQ=JyN_P|m0om0&M?&GgPw7U3~WpJMo{AqydP8cC>iZa zyR(~dl*8tYAYPccQb*S!cSA)dQ0lW?dyPsHNBB3n_1{P2$@jDF#@rv4Q&uuB_-xW_ zlbNjW#bvAg>w_aH;QIQzxLU0qhCR+c?WLk72`sdek`7W}i?83cuQ`91I@o#*R^A7~ zGh79vZ;nhE={3hIzg|VamVqRz+OL3}C312rml$o2p1w>+K440=+nBv}9nB^!rmYwY2goQ9nq&Kn z-@m(&^z60a9~VW{;(ot+HwbooW46w-UW<6?8!9&GBqn6*z^9e%nxnS7Nm(j4yOW$n zmabd1ha`IGdYos`JodI%L-(E^{x$zxxLpxn_ylh*vlQCL7S7}(Yb_v)y!6T54Dl;~=2 zDe&Co?B_oM`(~^II7^f`KmLY=t$P{d=DW^3URKBjxBw;@PkO=NAg;5(3ph}=(i(T0 zQyQwuwIs150++Aw$#TAzv`}z7n0=NalBFsl3f_DOr!_3)#WT(N@jOsK5(YLI^l zi9BP7^@K~eyFSt?bNBUI#3-f5R(rT6t(32iV#zus`gFb=N`n$N-8FNrf7#Ph3ePZmOEQmtApTF>)g%Geu@4Bzs(V%P zDf{o~{cU@WXS5|*n#^#oLfzb{u-lA{Xcmu0@zm|T`-of2{>}-<*K?y+sto@@)>rQ< zD>r|dP|FT|8THlB+0DvvHH*y`_O3$n)S*b_BTlxUR~(4X%<(i!RXSR4$_Z+h7)65q zR9)-;7ISDAF?jFcGxT%$C>oHVn2&!M2RNRp<4`NXe;Nv&58gMd&hSi`VZ28uhpx=5 zn`lpDWx0xYT)*p7;g?0@YAS0~abyj$Z{28b()c&~J%M$RpL$;LF5Clts^Qhd z*;#t_PGWs`mtroIOD-+0$Vrer&q-P&sKa;GgqG=gX-VW^0nH+~plrD?F|@UO{K2vd(p|d2~wAZ7`NJ zJ+(`nK+qDLUqGK{nao$5p8Sn)ZZeFpW+q%r<#Y;CHwqR2ZniacD-0Gx?efH*~S8~nXr^jwB2OAz;O^6R5)w&Fq{d{YLX z4l68^;~mV_t)Zw3;@M|HycL0ff#klQ<10tH7w;1^CG$IHf*Fm2kwC#f_Oa){S*dkY zB4=L2N&#p=a5YX}ELgfgxm5mYS6)#6#gahdEn36DmHSEk-evC7um%LOsr;yBC~wjE zH8WG#>ka(ut>mmXSQrHu78LVKn;9_~89?wAi(ljqaOp2tEECh4qbJ&zJiOkZ1@wW{+1|pai}*sXEr||>CTMN7UcvXZ7(0!LhP>jscuM>>SaNNM z&-7ML4>dYiItEjWdz${EY6Jbf)@GGtX1t32NEL3ZxydjO`fwp7E4Vy%^0H?wIlmxr zdi=W-4K{cSxFz8s-syqIUkl;RA4gbB4|s8%Y2FhOyn{q~@hY161o(Ncp2n5onvF7hIuFEsHwX5M&JCQxx|d6pLtm8=-e{~Gip zO>0^`nLFWmKc~a{SN$iveHafgp;WPM*3`ZBik+>Q?KFv?e@jn?zJ{LGl%e#WO9p+W zjD$IsFxe~qL>`6jwZ7_|4HNLPwgF}=-`S_^p6b1_*zV*oVu!vx{v4T^R|2{$y$qS zn>A1bObk!aPlH}|Yb!;J8V6LpubM}{{2A4--!cH4u5a~YvKpZm@0^}&(AK3^FqL!W zYP!mP;!nbnfS&(6wT-8`nwdp!YG=!nX8zSSSXY(@ueKa-uKl+)hj`&j;7X~8$ATn+ zUG`FH6DBOC!@^Z2gk*3M%Pccr>by3P+J(W7oa68L|4FXKjg*C#u_ln=Lb}gNc0$UE zy$AIjr(Sa_)m9a#zgzWi#Cs^Ow`{R!cqE$nPX cpZ^e5lf6x%8Hxit48HD;23qS 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 literal 0 HcmV?d00001 From c7881f407600a26dc5d3a2be667023f1c5a6033a Mon Sep 17 00:00:00 2001 From: rcpthongta <220981657+rcpthongta@users.noreply.github.com> Date: Thu, 24 Sep 2026 04:40:37 +0700 Subject: [PATCH 120/121] docs(changelog): document v1.0.2 release changes --- CHANGELOG.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9fd9027..1b2153b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,17 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/) and this project adheres to [Semantic Versioning](https://semver.org/). +## [1.0.2] - 2026-09-24 + +### Added + +- **Documentation & Performance** + - Dedicated **Benchmarks & Performance** guide detailing execution throughput (ops/sec), architectural design principles, and Bundlephobia footprint. + - Custom brand **Favicon** (`favicon.svg`) with pastel gradient SU monogram. + - Full **Open Graph** and **Twitter Card** social preview meta tags (`og:image`, `og:url`, `twitter:card`, etc.) across the documentation website. + - Branded repository social preview banner assets (`.github/social-preview.jpg` and `docs/public/og-image.jpg`). + - Mobile UX and SEO meta tags including `theme-color`, `keywords`, and `author`. + ## [1.0.1] - 2026-09-24 ### Added @@ -64,5 +75,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/). - Husky, lint-staged, and commitlint for Git hooks and commit validation - GitHub Actions workflows for linting and test coverage +[1.0.2]: https://github.com/rcpthongta/string-utils/compare/v1.0.1...v1.0.2 [1.0.1]: https://github.com/rcpthongta/string-utils/compare/v1.0.0...v1.0.1 [1.0.0]: https://github.com/rcpthongta/string-utils/releases/tag/v1.0.0 From bb93db6d002cf5641aa43e5550a624d6641d8417 Mon Sep 17 00:00:00 2001 From: rcpthongta <220981657+rcpthongta@users.noreply.github.com> Date: Thu, 24 Sep 2026 04:41:20 +0700 Subject: [PATCH 121/121] chore(release): bump package version to v1.0.2 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 4ec80c8..429578a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@rcpthongta/string-utils", - "version": "1.0.1", + "version": "1.0.2", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@rcpthongta/string-utils", - "version": "1.0.1", + "version": "1.0.2", "license": "MIT", "devDependencies": { "@commitlint/cli": "^21.2.2", diff --git a/package.json b/package.json index 9cd1078..470e48a 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,5 @@ { - "version": "1.0.1", + "version": "1.0.2", "name": "@rcpthongta/string-utils", "description": "A lightweight, type-safe, dependency-free string utility library for TypeScript and modern JavaScript applications.", "author": "rcpthongta",